mirror of
https://github.com/hyperion-project/hyperion.ng.git
synced 2023-10-10 13:36:59 +02:00
481 lines
14 KiB
C++
481 lines
14 KiB
C++
// Qt includes
|
|
#include <QJsonDocument>
|
|
#include <QJsonObject>
|
|
#include <QJsonArray>
|
|
|
|
#include <kodivideochecker/KODIVideoChecker.h>
|
|
|
|
|
|
KODIVideoChecker* KODIVideoChecker::_kodichecker = nullptr;
|
|
|
|
KODIVideoChecker* KODIVideoChecker::initInstance(const QString & address, uint16_t port, bool grabVideo, bool grabPhoto, bool grabAudio, bool grabMenu, bool grabPause, bool grabScreensaver, bool enable3DDetection)
|
|
{
|
|
if ( KODIVideoChecker::_kodichecker != nullptr )
|
|
throw std::runtime_error("KODIVideoChecker::initInstance can be called only one time");
|
|
KODIVideoChecker::_kodichecker = new KODIVideoChecker(address, port, grabVideo, grabPhoto, grabAudio, grabMenu, grabPause, grabScreensaver, enable3DDetection);
|
|
|
|
return KODIVideoChecker::_kodichecker;
|
|
}
|
|
|
|
KODIVideoChecker* KODIVideoChecker::getInstance()
|
|
{
|
|
return KODIVideoChecker::_kodichecker;
|
|
}
|
|
|
|
|
|
// Request player example:
|
|
// {"jsonrpc":"2.0","method":"Player.GetActivePlayers", "id":666}
|
|
// {"id":666,"jsonrpc":"2.0","result":[{"playerid":1,"type":"video"}]}
|
|
|
|
// Request playing item example:
|
|
// {"id":667,"jsonrpc":"2.0","method":"Player.GetItem","params":{"playerid":1,"properties":["file"]}}
|
|
// {"id":667,"jsonrpc":"2.0","result":{"item":{"file":"smb://xbmc:xbmc@192.168.53.12/video/Movies/Avatar (2009)/Avatar.mkv","label":"Avatar","type":"unknown"}}}
|
|
|
|
// Request if screensaver is on
|
|
// {"id":668,"jsonrpc":"2.0","method":"XBMC.GetInfoBooleans","params":{"booleans":["System.ScreenSaverActive"]}}
|
|
// {"id":668,"jsonrpc":"2.0","result":{"System.ScreenSaverActive":false}}
|
|
|
|
// Request stereoscopicmode example:
|
|
// {"jsonrpc":"2.0","method":"GUI.GetProperties","params":{"properties":["stereoscopicmode"]},"id":669}
|
|
// {"id":669,"jsonrpc":"2.0","result":{"stereoscopicmode":{"label":"Nebeneinander","mode":"split_vertical"}}}
|
|
|
|
KODIVideoChecker::KODIVideoChecker(const QString & address, uint16_t port, bool grabVideo, bool grabPhoto, bool grabAudio, bool grabMenu, bool grabPause, bool grabScreensaver, bool enable3DDetection)
|
|
: QObject()
|
|
, _address(address)
|
|
, _port(port)
|
|
, _activePlayerRequest(R"({"jsonrpc":"2.0","method":"Player.GetActivePlayers", "id":666})")
|
|
, _currentPlayingItemRequest(R"({"id":667,"jsonrpc":"2.0","method":"Player.GetItem","params":{"playerid":%1,"properties":["file"]}})")
|
|
, _checkScreensaverRequest(R"({"id":668,"jsonrpc":"2.0","method":"XBMC.GetInfoBooleans","params":{"booleans":["System.ScreenSaverActive"]}})")
|
|
, _getStereoscopicMode(R"({"jsonrpc":"2.0","method":"GUI.GetProperties","params":{"properties":["stereoscopicmode"]},"id":669})")
|
|
, _getKodiVersion(R"({"jsonrpc":"2.0","method":"Application.GetProperties","params":{"properties":["version"]},"id":670})")
|
|
, _getCurrentPlaybackState(R"({"id":671,"jsonrpc":"2.0","method":"Player.GetProperties","params":{"playerid":%1,"properties":["speed"]}})")
|
|
, _socket()
|
|
, _grabVideo(grabVideo)
|
|
, _grabPhoto(grabPhoto)
|
|
, _grabAudio(grabAudio)
|
|
, _grabMenu(grabMenu)
|
|
, _grabPause(grabPause)
|
|
, _grabScreensaver(grabScreensaver)
|
|
, _enable3DDetection(enable3DDetection)
|
|
, _previousGrabbingMode(GRABBINGMODE_INVALID)
|
|
, _previousVideoMode(VIDEO_2D)
|
|
, _currentPlaybackState(false)
|
|
, _currentPlayerID(0)
|
|
, _kodiVersion(0)
|
|
, _log(Logger::getInstance("KODI"))
|
|
, _active(false)
|
|
{
|
|
// setup socket
|
|
connect(&_socket, SIGNAL(readyRead()), this, SLOT(receiveReply()));
|
|
connect(&_socket, SIGNAL(disconnected()), this, SLOT(disconnected()));
|
|
connect(&_socket, SIGNAL(connected()), this, SLOT(connected()));
|
|
connect(&_socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(connectionError(QAbstractSocket::SocketError)));
|
|
}
|
|
|
|
KODIVideoChecker::~KODIVideoChecker()
|
|
{
|
|
stop();
|
|
}
|
|
|
|
|
|
void KODIVideoChecker::setConfig(const QString & address, uint16_t port, bool grabVideo, bool grabPhoto, bool grabAudio, bool grabMenu, bool grabPause, bool grabScreensaver, bool enable3DDetection)
|
|
{
|
|
_address = address;
|
|
_port = port;
|
|
_grabVideo = grabVideo;
|
|
_grabPhoto = grabPhoto;
|
|
_grabAudio = grabAudio;
|
|
_grabMenu = grabMenu;
|
|
_grabPause = grabPause;
|
|
_grabScreensaver = grabScreensaver;
|
|
_enable3DDetection = enable3DDetection;
|
|
_previousGrabbingMode = GRABBINGMODE_INVALID;
|
|
_previousVideoMode = VIDEO_2D;
|
|
_kodiVersion = 0;
|
|
|
|
// restart if active
|
|
if (_active)
|
|
{
|
|
stop();
|
|
QTimer::singleShot(2000, this, SLOT(()));
|
|
}
|
|
}
|
|
|
|
|
|
void KODIVideoChecker::start()
|
|
{
|
|
Info(_log, "started");
|
|
_active = true;
|
|
reconnect();
|
|
}
|
|
|
|
void KODIVideoChecker::stop()
|
|
{
|
|
Info(_log, "stopped");
|
|
_active = false;
|
|
_socket.close();
|
|
}
|
|
|
|
void KODIVideoChecker::receiveReply()
|
|
{
|
|
QJsonParseError error;
|
|
QByteArray qtreply = _socket.readAll();
|
|
QJsonDocument doc = QJsonDocument::fromJson(qtreply, &error);
|
|
|
|
if (error.error == QJsonParseError::NoError)
|
|
{
|
|
if (doc.isObject())
|
|
{
|
|
Debug(_log, "message: %s", doc.toJson(QJsonDocument::Compact).constData());
|
|
|
|
// Reply
|
|
if (doc.object().contains("id"))
|
|
{
|
|
int id = doc.object()["id"].toInt();
|
|
switch (id)
|
|
{
|
|
case 666:
|
|
{
|
|
if (doc.object()["result"].isArray())
|
|
{
|
|
QJsonArray resultArray = doc.object()["result"].toArray();
|
|
|
|
if (!resultArray.isEmpty())
|
|
{
|
|
// always start a new video in 2D mode
|
|
emit videoMode(VIDEO_2D);
|
|
|
|
QString type = resultArray[0].toObject()["type"].toString();
|
|
int prevCurrentPlayerID = _currentPlayerID;
|
|
_currentPlayerID = resultArray[0].toObject()["playerid"].toInt();
|
|
|
|
// set initial player state
|
|
if (prevCurrentPlayerID == 0 && _currentPlayerID != 0)
|
|
{
|
|
_socket.write(_getCurrentPlaybackState.arg(_currentPlayerID).toUtf8());
|
|
return;
|
|
}
|
|
if (type == "video")
|
|
{
|
|
if (_currentPlaybackState)
|
|
{
|
|
// video is playing
|
|
setGrabbingMode(_grabVideo ? GRABBINGMODE_VIDEO : GRABBINGMODE_OFF);
|
|
}
|
|
else
|
|
{
|
|
setGrabbingMode(_grabPause ? GRABBINGMODE_PAUSE : GRABBINGMODE_OFF);
|
|
}
|
|
|
|
// request info of the playing item
|
|
_socket.write(_currentPlayingItemRequest.arg(_currentPlayerID).toUtf8());
|
|
}
|
|
else if (type == "picture")
|
|
{
|
|
if (_currentPlaybackState)
|
|
{
|
|
// picture is playing
|
|
setGrabbingMode(_grabPhoto ? GRABBINGMODE_PHOTO : GRABBINGMODE_OFF);
|
|
}
|
|
else
|
|
{
|
|
setGrabbingMode(_grabPause ? GRABBINGMODE_PAUSE : GRABBINGMODE_OFF);
|
|
}
|
|
}
|
|
else if (type == "audio")
|
|
{
|
|
// audio is playing
|
|
if (_currentPlaybackState)
|
|
{
|
|
// audio is playing
|
|
setGrabbingMode(_grabAudio ? GRABBINGMODE_AUDIO : GRABBINGMODE_OFF);
|
|
}
|
|
else
|
|
{
|
|
setGrabbingMode(_grabPause ? GRABBINGMODE_PAUSE : GRABBINGMODE_OFF);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
// Nothing is playing.
|
|
setGrabbingMode(_grabMenu ? GRABBINGMODE_MENU : GRABBINGMODE_OFF);
|
|
}
|
|
else
|
|
{
|
|
setGrabbingMode(_grabMenu ? GRABBINGMODE_MENU : GRABBINGMODE_OFF);
|
|
}
|
|
break;
|
|
}
|
|
case 667:
|
|
{
|
|
if (_kodiVersion >= 13)
|
|
{
|
|
// check of active stereoscopicmode
|
|
_socket.write(_getStereoscopicMode.toUtf8());
|
|
}
|
|
else
|
|
{
|
|
if (doc.object()["result"].toObject()["item"].toObject().contains("file"))
|
|
{
|
|
|
|
QString filename = doc.object()["result"].toObject()["item"].toObject()["file"].toString();
|
|
if (filename.contains("3DSBS", Qt::CaseInsensitive) || filename.contains("HSBS", Qt::CaseInsensitive))
|
|
setVideoMode(VIDEO_3DSBS);
|
|
else if (filename.contains("3DTAB", Qt::CaseInsensitive) || filename.contains("HTAB", Qt::CaseInsensitive))
|
|
setVideoMode(VIDEO_3DTAB);
|
|
else
|
|
setVideoMode(VIDEO_2D);
|
|
}
|
|
else
|
|
{
|
|
setGrabbingMode(_grabMenu ? GRABBINGMODE_MENU : GRABBINGMODE_OFF);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case 668:
|
|
{
|
|
if (doc.object()["result"].toObject().contains("System.ScreenSaverActive"))
|
|
{
|
|
// result of System.ScreenSaverActive
|
|
if (doc.object()["result"].toObject()["System.ScreenSaverActive"].toBool())
|
|
setGrabbingMode(_grabScreensaver ? GRABBINGMODE_SCREENSAVER : GRABBINGMODE_OFF);
|
|
else
|
|
_socket.write(_activePlayerRequest.toUtf8());
|
|
|
|
// check here kodi version
|
|
if (_socket.state() == QTcpSocket::ConnectedState)
|
|
{
|
|
if (_kodiVersion == 0)
|
|
{
|
|
_socket.write(_getKodiVersion.toUtf8());
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
setGrabbingMode(_grabMenu ? GRABBINGMODE_MENU : GRABBINGMODE_OFF);
|
|
}
|
|
break;
|
|
}
|
|
case 669:
|
|
{
|
|
if (doc.object()["result"].toObject()["stereoscopicmode"].toObject().contains("mode"))
|
|
{
|
|
// result of stereoscopicmode
|
|
QString mode = doc.object()["result"].toObject()["stereoscopicmode"].toObject()["mode"].toString();
|
|
if (mode == "split_vertical")
|
|
setVideoMode(VIDEO_3DSBS);
|
|
else if (mode == "split_horizontal")
|
|
setVideoMode(VIDEO_3DTAB);
|
|
}
|
|
else
|
|
{
|
|
setGrabbingMode(_grabMenu ? GRABBINGMODE_MENU : GRABBINGMODE_OFF);
|
|
}
|
|
break;
|
|
}
|
|
case 670:
|
|
{
|
|
if (doc.object()["result"].toObject()["version"].toObject().contains("major"))
|
|
{
|
|
// kodi major version
|
|
_kodiVersion = doc.object()["result"].toObject()["version"].toObject()["major"].toInt();
|
|
}
|
|
else
|
|
{
|
|
setGrabbingMode(_grabMenu ? GRABBINGMODE_MENU : GRABBINGMODE_OFF);
|
|
}
|
|
break;
|
|
}
|
|
case 671:
|
|
{
|
|
if (doc.object()["result"].toObject().contains("speed"))
|
|
{
|
|
// result of Player.PlayPause
|
|
_currentPlaybackState = static_cast<bool>(doc.object()["result"].toObject()["speed"].toInt());
|
|
}
|
|
else
|
|
{
|
|
setGrabbingMode(_grabMenu ? GRABBINGMODE_MENU : GRABBINGMODE_OFF);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Notification
|
|
else if (doc.object().contains("method"))
|
|
{
|
|
QString method = doc.object()["method"].toString();
|
|
if (method == "Player.OnPlay")
|
|
{
|
|
if (doc.object()["params"].toObject()["data"].toObject()["player"].toObject().contains("speed"))
|
|
_currentPlaybackState = static_cast<bool>(doc.object()["params"].toObject()["data"].toObject()["player"].toObject()["speed"].toInt());
|
|
|
|
// send a request for the current player state
|
|
_socket.write(_activePlayerRequest.toUtf8());
|
|
return;
|
|
}
|
|
else if (method == "Player.OnStop")
|
|
{
|
|
// the player has stopped
|
|
setGrabbingMode(_grabMenu ? GRABBINGMODE_MENU : GRABBINGMODE_OFF);
|
|
setVideoMode(VIDEO_2D);
|
|
}
|
|
else if (method == "Player.OnPause")
|
|
{
|
|
if (doc.object()["params"].toObject()["data"].toObject()["player"].toObject().contains("speed"))
|
|
_currentPlaybackState = static_cast<bool>(doc.object()["params"].toObject()["data"].toObject()["player"].toObject()["speed"].toInt());
|
|
|
|
// player at pause
|
|
setGrabbingMode(_grabPause ? GRABBINGMODE_PAUSE : GRABBINGMODE_OFF);
|
|
}
|
|
else if (method == "GUI.OnScreensaverActivated")
|
|
setGrabbingMode(_grabScreensaver ? GRABBINGMODE_SCREENSAVER : GRABBINGMODE_OFF);
|
|
else if (method == "GUI.OnScreensaverDeactivated")
|
|
_socket.write(_activePlayerRequest.toUtf8());
|
|
else if (method == "Playlist.OnAdd" &&
|
|
(doc.object()["params"]
|
|
.toObject()["data"]
|
|
.toObject()["item"]
|
|
.toObject()["type"]) == QString("picture"))
|
|
{
|
|
// picture viewer is playing
|
|
setGrabbingMode(_grabPhoto ? GRABBINGMODE_PHOTO : GRABBINGMODE_OFF);
|
|
return;
|
|
}
|
|
else if (method == "Input.OnInputFinished")
|
|
{
|
|
// This Event is fired when Kodi Login
|
|
_socket.write(_activePlayerRequest.toUtf8());
|
|
_socket.write(_checkScreensaverRequest.toUtf8());
|
|
if (_currentPlayerID != 0)
|
|
_socket.write(_getCurrentPlaybackState.arg(_currentPlayerID).toUtf8());
|
|
}
|
|
}
|
|
}
|
|
else
|
|
Debug(_log, "Incomplete data");
|
|
}
|
|
}
|
|
|
|
void KODIVideoChecker::connected()
|
|
{
|
|
Info(_log, "Connected");
|
|
|
|
// send a request for the current player state
|
|
_socket.write(_activePlayerRequest.toUtf8());
|
|
_socket.write(_checkScreensaverRequest.toUtf8());
|
|
}
|
|
|
|
void KODIVideoChecker::disconnected()
|
|
{
|
|
Info(_log, "Disconnected");
|
|
_previousGrabbingMode = GRABBINGMODE_INVALID;
|
|
_previousVideoMode = VIDEO_2D;
|
|
_kodiVersion = 0;
|
|
reconnect();
|
|
}
|
|
|
|
void KODIVideoChecker::reconnect()
|
|
{
|
|
if (_socket.state() == QTcpSocket::ConnectedState || ! _active )
|
|
{
|
|
return;
|
|
}
|
|
Debug(_log, "try reconnect");
|
|
|
|
// try to connect
|
|
switch (_socket.state())
|
|
{
|
|
case QTcpSocket::ConnectingState:
|
|
// Somehow when KODI restarts we get stuck in connecting state
|
|
// If we get here we tried to connect already for 5 seconds. abort and try again in 1 second.
|
|
_socket.reset();
|
|
_socket.waitForDisconnected(50);
|
|
QTimer::singleShot(1000, this, SLOT(reconnect()));
|
|
break;
|
|
case QTcpSocket::UnconnectedState:
|
|
_socket.connectToHost(_address, _port);
|
|
QTimer::singleShot(10000, this, SLOT(reconnect()));
|
|
break;
|
|
default:
|
|
QTimer::singleShot(10000, this, SLOT(reconnect()));
|
|
break;
|
|
}
|
|
}
|
|
|
|
void KODIVideoChecker::connectionError(QAbstractSocket::SocketError error)
|
|
{
|
|
Error(_log,"Connection error (%s)", _socket.errorString().toStdString().c_str());
|
|
|
|
// close the socket
|
|
_socket.close();
|
|
}
|
|
|
|
void KODIVideoChecker::setGrabbingMode(GrabbingMode newGrabbingMode)
|
|
{
|
|
if (newGrabbingMode == _previousGrabbingMode)
|
|
{
|
|
// no change
|
|
return;
|
|
}
|
|
|
|
switch (newGrabbingMode)
|
|
{
|
|
case GRABBINGMODE_VIDEO:
|
|
Info(_log, "switching to VIDEO mode");
|
|
break;
|
|
case GRABBINGMODE_PHOTO:
|
|
Info(_log, "switching to PHOTO mode");
|
|
break;
|
|
case GRABBINGMODE_AUDIO:
|
|
Info(_log, "switching to AUDIO mode");
|
|
break;
|
|
case GRABBINGMODE_MENU:
|
|
Info(_log, "switching to MENU mode");
|
|
break;
|
|
case GRABBINGMODE_PAUSE:
|
|
Info(_log, "switching to PAUSE mode");
|
|
break;
|
|
case GRABBINGMODE_OFF:
|
|
Info(_log, "switching to OFF mode");
|
|
break;
|
|
case GRABBINGMODE_SCREENSAVER:
|
|
Info(_log, "switching to SCREENSAVER mode");
|
|
break;
|
|
default:
|
|
Warning(_log, "switching to INVALID mode");
|
|
break;
|
|
}
|
|
|
|
emit grabbingMode(newGrabbingMode);
|
|
_previousGrabbingMode = newGrabbingMode;
|
|
}
|
|
|
|
void KODIVideoChecker::setVideoMode(VideoMode newVideoMode)
|
|
{
|
|
if (newVideoMode == _previousVideoMode)
|
|
{
|
|
// no change
|
|
return;
|
|
}
|
|
|
|
switch (newVideoMode)
|
|
{
|
|
case VIDEO_2D:
|
|
Info(_log, "KODICHECK INFO: switching to 2D mode");
|
|
break;
|
|
case VIDEO_3DSBS:
|
|
Info(_log, "KODICHECK INFO: switching to 3D SBS mode");
|
|
break;
|
|
case VIDEO_3DTAB:
|
|
Info(_log, "KODICHECK INFO: switching to 3D TAB mode");
|
|
break;
|
|
}
|
|
|
|
emit videoMode(newVideoMode);
|
|
_previousVideoMode = newVideoMode;
|
|
}
|