summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorchriszero <zerov83@gmail.com>2015-02-05 21:32:55 +0100
committerchriszero <zerov83@gmail.com>2015-02-05 21:32:55 +0100
commit312c13f4f6b299a976787542869f96b1e66549cf (patch)
tree7ee2732eaec79d50e0c9718960fd58e2436f10f6
parent1ecdb2a5d9e05e0d30dae89e3d675218ca4c255e (diff)
downloadvdr-plugin-plex-312c13f4f6b299a976787542869f96b1e66549cf.tar.gz
vdr-plugin-plex-312c13f4f6b299a976787542869f96b1e66549cf.tar.bz2
Plex remote support.
Control via Plexapps (Android, IOS...)
-rw-r--r--ControlServer.cpp23
-rw-r--r--ControlServer.h10
-rw-r--r--PVideo.cpp1
-rw-r--r--PVideo.h1
-rw-r--r--PlexHTTPRequestHandler.cpp298
-rw-r--r--PlexHTTPRequestHandler.h3
-rw-r--r--PlexReqHandlerFactory.cpp17
-rw-r--r--Plexservice.h2
-rw-r--r--SubscriptionManager.cpp279
-rw-r--r--SubscriptionManager.h69
-rw-r--r--hlsPlayer.cpp6
-rw-r--r--hlsPlayer.h2
-rw-r--r--hlsPlayerControl.cpp19
-rw-r--r--hlsPlayerControl.h4
-rw-r--r--plex.cpp4
15 files changed, 524 insertions, 214 deletions
diff --git a/ControlServer.cpp b/ControlServer.cpp
index c727e20..31363ae 100644
--- a/ControlServer.cpp
+++ b/ControlServer.cpp
@@ -3,20 +3,29 @@
namespace plexclient
{
-ControlServer::ControlServer() {
+ControlServer::ControlServer()
+{
m_pSvs = new Poco::Net::ServerSocket(3200);;
m_pSrv = new Poco::Net::HTTPServer(new PlexReqHandlerFactory, *m_pSvs, new Poco::Net::HTTPServerParams);
}
-
-void ControlServer::Start() {
- // start the HTTPServer
- m_pSrv->start();
-}
-void ControlServer::Stop() {
+void ControlServer::Stop()
+{
+ Cancel(1);
// Stop the HTTPServer
m_pSrv->stop();
}
+void ControlServer::Action()
+{
+ isyslog("[plex] Starting Controlserver...");
+ // start the HTTPServer
+ m_pSrv->start();
+ while(Running()) {
+ SubscriptionManager::GetInstance().Notify();
+ cCondWait::SleepMs(1000);
+ }
+}
+
}
diff --git a/ControlServer.h b/ControlServer.h
index f349533..8546fa7 100644
--- a/ControlServer.h
+++ b/ControlServer.h
@@ -10,10 +10,12 @@
#include "PlexHTTPRequestHandler.h"
#include "PlexReqHandlerFactory.h"
+#include <vdr/thread.h>
+
namespace plexclient
{
-class ControlServer
+class ControlServer : public cThread
{
public:
@@ -21,12 +23,14 @@ public:
static ControlServer instance;
return instance;
}
- void Start();
void Stop();
+protected:
+ void Action();
+
private:
ControlServer();
-
+
Poco::Net::ServerSocket *m_pSvs;
Poco::Net::HTTPServer *m_pSrv;
diff --git a/PVideo.cpp b/PVideo.cpp
index f893859..f05c220 100644
--- a/PVideo.cpp
+++ b/PVideo.cpp
@@ -5,6 +5,7 @@ namespace plexclient
Video::Video(Poco::XML::Node* pNode, PlexServer* Server)
{
+ m_iMyPlayOffset = 0;
m_pServer = Server;
NodeIterator it(pNode, Poco::XML::NodeFilter::SHOW_ALL);
Poco::XML::Node* pChildNode = it.nextNode();
diff --git a/PVideo.h b/PVideo.h
index fb4b5a5..101e5d1 100644
--- a/PVideo.h
+++ b/PVideo.h
@@ -61,6 +61,7 @@ public:
std::string m_sCollection;
Media m_Media;
PlexServer* m_pServer;
+ int m_iMyPlayOffset;
};
}
diff --git a/PlexHTTPRequestHandler.cpp b/PlexHTTPRequestHandler.cpp
index d6c8ad3..7ba12f1 100644
--- a/PlexHTTPRequestHandler.cpp
+++ b/PlexHTTPRequestHandler.cpp
@@ -3,12 +3,15 @@
#include <vdr/keys.h>
#include <unistd.h>
+#include "hlsPlayerControl.h"
+
namespace plexclient
{
-void PlexHTTPRequestHandler::AddHeaders(Poco::Net::HTTPServerResponse& response, Poco::Net::HTTPServerRequest& request) {
+void PlexHTTPRequestHandler::AddHeaders(Poco::Net::HTTPServerResponse& response, Poco::Net::HTTPServerRequest& request)
+{
if(request.getMethod() == Poco::Net::HTTPRequest::HTTP_OPTIONS) {
- response.setContentType("text/plain");
+ response.setContentType("text/plain");
response.add("X-Plex-Client-Identifier", Config::GetInstance().GetUUID());
response.add("Connection", "Close");
@@ -16,12 +19,12 @@ void PlexHTTPRequestHandler::AddHeaders(Poco::Net::HTTPServerResponse& response,
response.add("Access-Control-Allow-Origin", "*");
response.add("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PUT, HEAD");
response.add("Access-Control-Allow-Headers", "x-plex-version, x-plex-platform-version, "
- "x-plex-username, x-plex-client-identifier, "
- "x-plex-target-client-identifier, x-plex-device-name, "
- "x-plex-platform, x-plex-product, accept, x-plex-device");
+ "x-plex-username, x-plex-client-identifier, "
+ "x-plex-target-client-identifier, x-plex-device-name, "
+ "x-plex-platform, x-plex-product, accept, x-plex-device");
} else {
- response.setContentType("application/x-www-form-urlencoded");
-
+ response.setContentType("application/x-www-form-urlencoded");
+
response.add("Access-Control-Allow-Origin", "*");
response.add("X-Plex-Version", VERSION);
response.add("X-Plex-Client-Identifier", Config::GetInstance().GetUUID());
@@ -36,7 +39,8 @@ void PlexHTTPRequestHandler::AddHeaders(Poco::Net::HTTPServerResponse& response,
//header.MessageHeader(header);
}
-std::map<std::string, std::string> PlexHTTPRequestHandler::ParseQuery(std::string query) {
+std::map<std::string, std::string> PlexHTTPRequestHandler::ParseQuery(std::string query)
+{
std::map<std::string, std::string> querymap;
Poco::StringTokenizer queryTokens(query, "&");
for(Poco::StringTokenizer::Iterator token = queryTokens.begin() ; token != queryTokens.end(); ++token) {
@@ -46,137 +50,213 @@ std::map<std::string, std::string> PlexHTTPRequestHandler::ParseQuery(std::strin
return querymap;
}
-std::string PlexHTTPRequestHandler::GetOKMsg() {
+std::string PlexHTTPRequestHandler::GetOKMsg()
+{
return "<?xml version=\"1.0\" encoding=\"utf-8\"?> <Response code=\"200\" status=\"OK\" />";
}
-void PlexHTTPRequestHandler::handleRequest(Poco::Net::HTTPServerRequest& request, Poco::Net::HTTPServerResponse& response){
+void PlexHTTPRequestHandler::handleRequest(Poco::Net::HTTPServerRequest& request, Poco::Net::HTTPServerResponse& response)
+{
+ UpdateCommandId(request);
+
AddHeaders(response, request);
- std::ostream& ostr = response.send();
- ostr << GetOKMsg();
+ response.send() << GetOKMsg();
}
-void SubscribeRequestHandler::handleRequest(Poco::Net::HTTPServerRequest& request, Poco::Net::HTTPServerResponse& response){
- // parse query
+void PlexHTTPRequestHandler::UpdateCommandId(Poco::Net::HTTPServerRequest& request)
+{
Poco::URI uri(request.getURI());
std::map<std::string, std::string> query = ParseQuery(uri.getQuery()); // port=32400&commandID=0&protocol=http
- std::string path = uri.getPath(); // /player/timeline/subscribe
-
- std::string uuid = request.get("X-Plex-Client-Identifier");
- int port = atoi(query["port"].c_str());
- int command = atoi(query["commandID"].c_str());
- SubscriptionManager::GetInstance().AddSubscriber(Subscriber(query["protocol"], request.getHost(), port, uuid, command));
-
- AddHeaders(response, request);
-
- std::ostream& ostr = response.send();
- ostr << GetOKMsg();
- response.setStatus(Poco::Net::HTTPResponse::HTTP_REASON_OK);
-}
-
-void ResourceRequestHandler::handleRequest(Poco::Net::HTTPServerRequest& request, Poco::Net::HTTPServerResponse& response){
- AddHeaders(response, request);
-
- std::ostream& ostr = response.send();
- ostr << "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
- "<MediaContainer>"
- "<Player title=\"" << Config::GetInstance().GetHostname() << "\""
- " protocol=\"plex\""
- " protocolVersion=\"1\""
- " protocolCapabilities=\"navigation,playback,timeline\""
- " machineIdentifier=\"" << Config::GetInstance().GetUUID() << "\""
- " product=\"PlexVDR\""
- " platform=\"Linux\""
- " platformVersion=\"" << VERSION << "\""
- " deviceClass=\"HTPC\""
- "/> </MediaContainer>";
-
- response.setStatus(Poco::Net::HTTPResponse::HTTP_REASON_OK);
- dsyslog("[plex]Resources Response sent...");
+ std::string uuid = request.get("X-Plex-Client-Identifier", "");
+ if (uuid.length() != 0) {
+ std::string command = query["commandID"];
+ SubscriptionManager::GetInstance().UpdateSubscriber(uuid, command);
+ }
}
-void PlayerRequestHandler::handleRequest(Poco::Net::HTTPServerRequest& request, Poco::Net::HTTPServerResponse& response){
+void SubscribeRequestHandler::handleRequest(Poco::Net::HTTPServerRequest& request, Poco::Net::HTTPServerResponse& response)
+{
+ UpdateCommandId(request);
Poco::URI uri(request.getURI());
- //Poco::StringTokenizer pathTokens(uri, "/");
std::map<std::string, std::string> query = ParseQuery(uri.getQuery());
-
+
if(query.find("wait") != query.end() && atoi(query["wait"].c_str()) == 1) {
usleep(900 * 1000);
}
-
+
if(request.getMethod() == Poco::Net::HTTPRequest::HTTP_OPTIONS) {
AddHeaders(response, request);
- //response.setStatus(Poco::Net::HTTPResponse::HTTP_REASON_OK);
- std::ostream& ostr = response.send(); // Stream must not be empty!
- ostr << " ";
+ response.send() << " "; // Stream must not be empty!
response.setStatus(Poco::Net::HTTPResponse::HTTP_REASON_OK);
- dsyslog("[plex]OPTION Reply send");
return;
- }
-
- if(request.getURI().find("/poll")!= std::string::npos) {
+ }
+
+ // parse query
+ if(request.getURI().find("/subscribe")!= std::string::npos) {
+ Subscribe(request);
+ AddHeaders(response, request);
+ response.send() << GetOKMsg();
+ response.setStatus(Poco::Net::HTTPResponse::HTTP_REASON_OK);
+ } else if(request.getURI().find("/unsubscribe")!= std::string::npos) {
+ Unsubscribe(request);
+ AddHeaders(response, request);
+ response.send() << GetOKMsg();
+ response.setStatus(Poco::Net::HTTPResponse::HTTP_REASON_OK);
+ } else if(request.getURI().find("/poll")!= std::string::npos) {
response.add("X-Plex-Client-Identifier",Config::GetInstance().GetUUID());
response.add("Access-Control-Expose-Headers","X-Plex-Client-Identifier");
response.add("Access-Control-Allow-Origin","*");
response.setContentType("text/xml");
-
- std::ostream& ostr = response.send();
- ostr << SubscriptionManager::GetInstance().GetMsg(query["commandID"]);
- response.setStatus(Poco::Net::HTTPResponse::HTTP_REASON_OK);
-
- }
- else if(request.getURI().find("/playback/playMedia")!= std::string::npos) {
- //std::cout << "playMedia_1" << std::endl;
- AddHeaders(response, request);
-
- //std::cout << "playMedia_2" << std::endl;
- std::string protocol = query["protocol"];
- std::string address = query["address"];
- std::string port = query["port"];
- std::string key = query["key"];
- //std::cout << "playMedia_3" << std::endl;
-
- std::string fullUrl = protocol + "://" + address + ":" + port + key; // Metainfo
- std::cout << "FullUrl: " << fullUrl << std::endl;
-
- MediaContainer *pCont = Plexservice::GetMediaContainer(fullUrl);
- std::string filePath = pCont->m_vVideos[0].m_Media.m_sPartKey;
- Poco::URI fileuri(fullUrl);
- fileuri.setPath(filePath);
-
- // MUSS im Maintread des Plugins/VDR gestartet werden
- ActionManager::GetInstance().AddAction(fileuri.toString());
-
- std::ostream& ostr = response.send();
- ostr << GetOKMsg();
+ response.send() << SubscriptionManager::GetInstance().GetMsg(query["commandID"]);
response.setStatus(Poco::Net::HTTPResponse::HTTP_REASON_OK);
- SubscriptionManager::GetInstance().Notify();
}
- else if(request.getURI().find("/navigation")!= std::string::npos) {
- if(request.getURI().find("/moveUp")!= std::string::npos) {
- cRemote::Put(kUp);
- }
- else if(request.getURI().find("/moveDown")!= std::string::npos) {
- cRemote::Put(kDown);
- }
- else if(request.getURI().find("/moveLeft")!= std::string::npos) {
- cRemote::Put(kLeft);
- }
- else if(request.getURI().find("/moveRight")!= std::string::npos) {
- cRemote::Put(kRight);
- }
- else if(request.getURI().find("/select")!= std::string::npos) {
- cRemote::Put(kOk);
+}
+
+void SubscribeRequestHandler::Subscribe(Poco::Net::HTTPServerRequest& request)
+{
+ Poco::URI uri(request.getURI());
+ std::map<std::string, std::string> query = ParseQuery(uri.getQuery()); // port=32400&commandID=0&protocol=http
+
+ std::string uuid = request.get("X-Plex-Client-Identifier", "");
+ if(uuid.length() != 0) {
+ int port = atoi(query["port"].c_str());
+ std::string command = query["commandID"];
+
+ SubscriptionManager::GetInstance().AddSubscriber(Subscriber(query["protocol"], request.clientAddress().host().toString(), port, uuid, command));
+ }
+}
+
+void SubscribeRequestHandler::Unsubscribe(Poco::Net::HTTPServerRequest& request)
+{
+ std::string uuid = request.get("X-Plex-Client-Identifier", "");
+ if(uuid.length() != 0) {
+ SubscriptionManager::GetInstance().RemoveSubscriber(uuid);
+ }
+}
+
+void ResourceRequestHandler::handleRequest(Poco::Net::HTTPServerRequest& request, Poco::Net::HTTPServerResponse& response)
+{
+ UpdateCommandId(request);
+ AddHeaders(response, request);
+
+ std::ostream& ostr = response.send();
+ ostr << "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
+ "<MediaContainer>"
+ "<Player title=\"" << Config::GetInstance().GetHostname() << "\""
+ " protocol=\"plex\""
+ " protocolVersion=\"1\""
+ " protocolCapabilities=\"navigation,playback,timeline\""
+ " machineIdentifier=\"" << Config::GetInstance().GetUUID() << "\""
+ " product=\"PlexVDR\""
+ " platform=\"Linux\""
+ " platformVersion=\"" << VERSION << "\""
+ " deviceClass=\"HTPC\""
+ "/> </MediaContainer>";
+
+ response.setStatus(Poco::Net::HTTPResponse::HTTP_REASON_OK);
+
+ //dsyslog("[plex]Resources Response sent...");
+}
+
+void PlayerRequestHandler::handleRequest(Poco::Net::HTTPServerRequest& request, Poco::Net::HTTPServerResponse& response)
+{
+ try {
+ UpdateCommandId(request);
+
+ Poco::URI uri(request.getURI());
+ std::map<std::string, std::string> query = ParseQuery(uri.getQuery());
+
+ if(query.find("wait") != query.end() && atoi(query["wait"].c_str()) == 1) {
+ usleep(900 * 1000);
}
- else if(request.getURI().find("/home")!= std::string::npos) {
- cRemote::Put(kMenu);
+
+ if(request.getMethod() == Poco::Net::HTTPRequest::HTTP_OPTIONS) {
+ AddHeaders(response, request);
+ response.send() << " "; // Stream must not be empty!
+ return;
}
- else if(request.getURI().find("/back")!= std::string::npos) {
- cRemote::Put(kBack);
+
+ if(request.getURI().find("/poll")!= std::string::npos) {
+ response.add("X-Plex-Client-Identifier",Config::GetInstance().GetUUID());
+ response.add("Access-Control-Expose-Headers","X-Plex-Client-Identifier");
+ response.add("Access-Control-Allow-Origin","*");
+ response.setContentType("text/xml");
+ std::ostream& ostr = response.send();
+ ostr << SubscriptionManager::GetInstance().GetMsg(query["commandID"]);
+
+ } else if(request.getURI().find("/playback/playMedia")!= std::string::npos) {
+ AddHeaders(response, request);
+
+ std::string protocol = query["protocol"];
+ std::string address = query["address"];
+ std::string port = query["port"];
+ std::string key = query["key"];
+
+ std::string fullUrl = protocol + "://" + address + ":" + port + key; // Metainfo
+
+ MediaContainer *pCont = Plexservice::GetMediaContainer(fullUrl);
+
+ // MUSS im Maintread des Plugins/VDR gestartet werden
+ if(query.find("offset") != query.end()) {
+ pCont->m_vVideos[0].m_iMyPlayOffset = atoi(query["offset"].c_str()) / 1000;
+ }
+ ActionManager::GetInstance().AddAction(&pCont->m_vVideos[0]); // MemoryLeak?
+
+ response.send() << GetOKMsg();
+
+ SubscriptionManager::GetInstance().Notify();
+
+ } else if(request.getURI().find("/navigation")!= std::string::npos) {
+ if(request.getURI().find("/moveUp")!= std::string::npos) {
+ cRemote::Put(kUp);
+ } else if(request.getURI().find("/moveDown")!= std::string::npos) {
+ cRemote::Put(kDown);
+ } else if(request.getURI().find("/moveLeft")!= std::string::npos) {
+ cRemote::Put(kLeft);
+ } else if(request.getURI().find("/moveRight")!= std::string::npos) {
+ cRemote::Put(kRight);
+ } else if(request.getURI().find("/select")!= std::string::npos) {
+ cRemote::Put(kOk);
+ } else if(request.getURI().find("/home")!= std::string::npos) {
+ cRemote::Put(kMenu);
+ } else if(request.getURI().find("/back")!= std::string::npos) {
+ cRemote::Put(kBack);
+ }
+
+ response.send() << GetOKMsg();
+ } else if(request.getURI().find("/playback")!= std::string::npos) {
+ if(request.getURI().find("/seekTo")!= std::string::npos) {
+ if(query.find("offset") != query.end()) {
+ int offset = atoi(query["offset"].c_str()) / 1000;
+ cHlsPlayerControl* control = dynamic_cast<cHlsPlayerControl*>(cControl::Control(true));
+ if(control) {
+ isyslog("[plex] Seeking to %d", offset);
+ control->SeekTo(offset);
+ }
+ }
+ } else if(request.getURI().find("/play")!= std::string::npos) {
+ cRemote::Put(kPlay);
+ } else if(request.getURI().find("/pause")!= std::string::npos) {
+ cRemote::Put(kPause);
+ } else if(request.getURI().find("/stop")!= std::string::npos) {
+ cRemote::Put(kStop);
+ } else if(request.getURI().find("/stepForward")!= std::string::npos) {
+ cRemote::Put(kFastFwd);
+ } else if(request.getURI().find("/stepBack")!= std::string::npos) {
+ cRemote::Put(kFastRew);
+ } else if(request.getURI().find("/skipNext")!= std::string::npos) {
+ cRemote::Put(kFastFwd);
+ } else if(request.getURI().find("/skipPrevious")!= std::string::npos) {
+ cRemote::Put(kFastRew);
+ }
+ SubscriptionManager::GetInstance().Notify();
+ response.send() << GetOKMsg();
}
+ } catch (Poco::Exception& e) {
+ std::cerr << e.displayText() << std::endl;
}
-
+
}
}
diff --git a/PlexHTTPRequestHandler.h b/PlexHTTPRequestHandler.h
index 52837fe..2787e18 100644
--- a/PlexHTTPRequestHandler.h
+++ b/PlexHTTPRequestHandler.h
@@ -36,12 +36,15 @@ protected:
std::string GetOKMsg();
void AddHeaders(Poco::Net::HTTPServerResponse& response, Poco::Net::HTTPServerRequest& request);
std::map<std::string, std::string> ParseQuery(std::string query);
+ void UpdateCommandId(Poco::Net::HTTPServerRequest& request);
};
class SubscribeRequestHandler : public PlexHTTPRequestHandler
{
public:
virtual void handleRequest(Poco::Net::HTTPServerRequest& request, Poco::Net::HTTPServerResponse& response);
+ void Subscribe(Poco::Net::HTTPServerRequest& request);
+ void Unsubscribe(Poco::Net::HTTPServerRequest& request);
};
class ResourceRequestHandler : public PlexHTTPRequestHandler
diff --git a/PlexReqHandlerFactory.cpp b/PlexReqHandlerFactory.cpp
index 1b68bcb..ec33a12 100644
--- a/PlexReqHandlerFactory.cpp
+++ b/PlexReqHandlerFactory.cpp
@@ -12,21 +12,22 @@ PlexReqHandlerFactory::~PlexReqHandlerFactory()
}
Poco::Net::HTTPRequestHandler* PlexReqHandlerFactory::createRequestHandler(const Poco::Net::HTTPServerRequest& request)
-{
+{
if(request.getMethod() == Poco::Net::HTTPRequest::HTTP_GET) {
std::cout << "GET Request: " << request.getURI() << " from: " << request.clientAddress().toString() << std::endl;
- }
- else if(request.getMethod() == Poco::Net::HTTPRequest::HTTP_OPTIONS) {
+ } else if(request.getMethod() == Poco::Net::HTTPRequest::HTTP_OPTIONS) {
std::cout << "OPTIONS Request: " << request.getURI() << " from: " << request.clientAddress().toString() << std::endl;
- }
- else if(request.getMethod() == Poco::Net::HTTPRequest::HTTP_HEAD) {
+ } else if(request.getMethod() == Poco::Net::HTTPRequest::HTTP_HEAD) {
std::cout << "HEAD Request: " << request.getURI() << " from: " << request.clientAddress().toString() << std::endl;
}
-
- if(request.getURI().find("/subscribe")!= std::string::npos) return new SubscribeRequestHandler();
+ else {
+ std::cout << "??? Request: " << request.getURI() << " from: " << request.clientAddress().toString() << std::endl;
+ }
+
+ if(request.getURI().find("/player/timeline")!= std::string::npos) return new SubscribeRequestHandler();
else if(request.getURI().find("/resources")!= std::string::npos) return new ResourceRequestHandler();
else if(request.getURI().find("/player")!= std::string::npos) return new PlayerRequestHandler();
-
+
return new PlexHTTPRequestHandler();
}
diff --git a/Plexservice.h b/Plexservice.h
index 6bd3c33..3daa7cf 100644
--- a/Plexservice.h
+++ b/Plexservice.h
@@ -62,8 +62,6 @@ public:
static std::string GetUniversalTranscodeUrl(Video* video, int offset = 0, PlexServer* server = 0);
static MediaContainer* GetMediaContainer(std::string fullUrl);
-
-protected:
static std::string encode(std::string message);
private:
diff --git a/SubscriptionManager.cpp b/SubscriptionManager.cpp
index c4589ef..165018d 100644
--- a/SubscriptionManager.cpp
+++ b/SubscriptionManager.cpp
@@ -9,110 +9,275 @@
#include <Poco/Net/HTTPResponse.h>
#include <Poco/Net/MessageHeader.h>
+#include "Plexservice.h"
+
namespace plexclient
{
SubscriptionManager::SubscriptionManager()
{
-
+ m_pStatus = new cSubscriberStatus();
+ m_bStoppedSent = true;
}
-void SubscriptionManager::Notify() {
- //mutex.lock();
-
- //TODO: Implement
+void SubscriptionManager::Notify()
+{
+ //dsyslog("[plex]: '%s'", __FUNCTION__);
+ Cleanup();
+ m_myLock.Lock(&m_myMutex);
for(std::map<std::string, Subscriber>::iterator it = m_mSubcribers.begin() ; it != m_mSubcribers.end(); ++it) {
Subscriber subs = it->second;
- subs.SendUpdate(GetMsg(std::string(itoa(subs.m_iCommandId))), false);
+ subs.SendUpdate(GetMsg(subs.m_iCommandId), false);
}
-
- //mutex.unlock();
+ NotifyServer();
}
-void SubscriptionManager::AddSubscriber(Subscriber subs) {
- //mutex.lock();
+void SubscriptionManager::NotifyServer()
+{
+ if(m_bStoppedSent && m_pStatus->PlayerStopped) return;
+ try {
+ int current, total, speed;
+ bool play, forward;
+ Video *pVid = NULL;
+ if(!m_pStatus->PlayerStopped && m_pStatus->pControl) {
+ m_pStatus->pControl->GetIndex(current, total);
+ current = current / m_pStatus->pControl->FramesPerSecond() * 1000;
+ total = total / m_pStatus->pControl->FramesPerSecond() * 1000;
+ m_pStatus->pControl->GetReplayMode(play, forward, speed);
+ pVid = m_pStatus->pVideo;
+ }
+ else {
+ return;
+ }
+
+ std::stringstream uri;
+ uri << "/:/timeline";
+ uri << "?containerKey=" << Plexservice::encode(pVid ? pVid->m_sKey : "/library/metadata/900000");
+ uri << "&key=" << Plexservice::encode(pVid ? pVid->m_sKey : "/library/metadata/900000");
+ uri << "&ratingKey=" << (pVid ? pVid->m_iRatingKey : 900000);
+ uri << "&state=";
+ if (m_pStatus->PlayerStopped) uri << "stopped";
+ else if(!play) uri << "paused";
+ else uri << "playing";
+ uri << "&time=" << current;
+ uri << "&duration=" << total;
+
+ Poco::Net::HTTPRequest Request(Poco::Net::HTTPRequest::HTTP_GET, uri.str(), Poco::Net::HTTPMessage::HTTP_1_1);
+
+ Request.add("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.52 Safari/537.17");
+ Request.add("X-Plex-Client-Identifier", Config::GetInstance().GetUUID());
+ Request.add("X-Plex-Device", "PC");
+ Request.add("X-Plex-Device-Name", Config::GetInstance().GetHostname());
+ Request.add("X-Plex-Language", Config::GetInstance().GetLanguage());
+ Request.add("X-Plex-Model", "Linux");
+ Request.add("X-Plex-Platform", "VDR");
+ Request.add("X-Plex-Product", "plex-vdr");
+ Request.add("X-Plex-Provides", "player");
+ Request.add("X-Plex-Version", "0.0.1a");
+
+ Poco::Net::HTTPClientSession session(pVid->m_pServer->GetIpAdress(), pVid->m_pServer->GetPort());
+
+ session.sendRequest(Request);
+ Poco::Net::HTTPResponse response;
+ session.receiveResponse(response);
+
+ if(m_pStatus->PlayerStopped) {
+ m_bStoppedSent = true;
+ }
+ else {
+ m_bStoppedSent = false;
+ }
+ } catch (Poco::Exception& e) {
+ std::cout << e.displayText() << std::endl;
+ }
+
+}
+
+void SubscriptionManager::AddSubscriber(Subscriber subs)
+{
+ m_myLock.Lock(&m_myMutex);
m_mSubcribers[subs.GetUuid()] = subs;
- std::cout << "AddSubscriber: " << subs.to_string() << std::endl;
- //mutex.unlock();
}
-void SubscriptionManager::RemoveSubscriber(std::string uuid) {
- //mutex.lock();
+void SubscriptionManager::RemoveSubscriber(std::string uuid)
+{
+ m_myLock.Lock(&m_myMutex);
if(m_mSubcribers.find(uuid) != m_mSubcribers.end()) {
m_mSubcribers.erase(uuid);
}
- //mutex.unlock();
}
-std::string SubscriptionManager::GetMsg(std::string commandId) {
- int time = 0;//PlayerGetCurrentPosition();
-
- //bool paused = PlayerPaused;
-
+void SubscriptionManager::UpdateSubscriber(std::string uuid, std::string commandId)
+{
+ m_myLock.Lock(&m_myMutex);
+ if(m_mSubcribers.find(uuid) != m_mSubcribers.end()) {
+ m_mSubcribers[uuid].m_iCommandId = commandId;
+ //dsyslog("[plex]: '%s'", __FUNCTION__);
+ }
+}
+
+std::string SubscriptionManager::GetMsg(std::string commandId)
+{
+ //dsyslog("[plex]: '%s'", __FUNCTION__);
std::stringstream msg;
msg << "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
- "<MediaContainer commandID=\"" << commandId << "\""
- " location=\"navigation\">";
-
- msg << "<Timeline location=\"navigation\" state=\"";
- //if(paused) msg << "paused";
- //else if (paused) msg << "paused";
- msg << "stopped";
- msg << "\" time=\"" << time << "\" type=\"video\" />";
+ "<MediaContainer commandID=\"" << commandId << "\" location=\"navigation\">";
+
msg << "<Timeline location=\"navigation\" state=\"stopped\" time=\"0\" type=\"photo\" />";
msg << "<Timeline location=\"navigation\" state=\"stopped\" time=\"0\" type=\"music\" />";
-
+
+ msg << GetTimelineXml();
+
msg << "</MediaContainer>";
return msg.str();
}
-Subscriber::Subscriber(std::string protocol, std::string host, int port, std::string uuid, int commandId) {
+std::string SubscriptionManager::GetTimelineXml()
+{
+ int current, total, speed;
+ bool play, forward;
+ Video *pVid = NULL;
+ if(!m_pStatus->PlayerStopped && m_pStatus->pControl) {
+ m_pStatus->pControl->GetIndex(current, total);
+ current = current / m_pStatus->pControl->FramesPerSecond() * 1000;
+ total = total / m_pStatus->pControl->FramesPerSecond() * 1000;
+ m_pStatus->pControl->GetReplayMode(play, forward, speed);
+ pVid = m_pStatus->pVideo;
+ }
+
+ std::stringstream msg;
+
+ msg << "<Timeline location=\"navigation\" state=\"";
+ if (m_pStatus->PlayerStopped) msg << "stopped";
+ else if(!play) msg << "paused";
+ else msg << "playing";
+
+ msg << "\" time=\"" << current << "\" type=\"video\"";
+
+ msg << " duration=\"" << total << "\"";
+ msg << " seekRange=\"0-" << total << "\"";
+ msg << " controllable=\"true\"";
+ msg << " machineIdentifier=\"" << (pVid ? pVid->m_pServer->GetUuid() : "") << "\"";
+ msg << " protocol=\"http\"";
+ msg << " address=\"" << (pVid ? pVid->m_pServer->GetIpAdress() : "") << "\"";
+ msg << " port=\"" << (pVid ? pVid->m_pServer->GetPort() : 0) << "\"";
+ msg << " guid=\"" << Config::GetInstance().GetUUID() << "\"";
+ msg << " containerKey=\"" << (pVid ? pVid->m_sKey : "/library/metadata/900000") << "\"";
+ msg << " key=\"" << (pVid ? pVid->m_sKey : "/library/metadata/900000") << "\"";
+ msg << " ratingKey=\"" << (pVid ? pVid->m_iRatingKey : 900000) << "\"";
+ msg << " volume=\"" << m_pStatus->Volume << "\"";
+ msg << " shuffle=\"false\"";
+
+ msg << "/>";
+ return msg.str();
+}
+
+void SubscriptionManager::Cleanup()
+{
+ m_myLock.Lock(&m_myMutex);
+ for(std::map<std::string, Subscriber>::iterator it = m_mSubcribers.begin() ; it != m_mSubcribers.end(); ++it) {
+ Subscriber subs = it->second;
+ if(subs.m_iAge > 30) {
+ m_mSubcribers.erase(it);
+ }
+ }
+}
+
+Subscriber::Subscriber(std::string protocol, std::string host, int port, std::string uuid, std::string commandId)
+{
m_sProtocol = protocol;
- m_sHost = host;
+ int pos = host.find(":");
+ m_sHost = host.substr(0,pos-1);
m_iPort = port;
m_sUuid = uuid;
m_iCommandId = commandId;
+ m_iAge = 0;
}
-void Subscriber::SendUpdate(std::string msg, bool isNav) {
+void Subscriber::SendUpdate(std::string msg, bool isNav)
+{
+ //dsyslog("[plex]: '%s'", __FUNCTION__);
++m_iAge;
-
- Poco::Net::HTTPRequest *pRequest = new Poco::Net::HTTPRequest(Poco::Net::HTTPRequest::HTTP_POST,
- "/:/timeline", Poco::Net::HTTPMessage::HTTP_1_1);
-
- pRequest->add("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.52 Safari/537.17");
-
- pRequest->add("X-Plex-Client-Capabilities", "protocols=shoutcast,http-video;videoDecoders=h264{profile:high&resolution:1080&level:51};audioDecoders=mp3,aac");
- pRequest->add("X-Plex-Client-Identifier", Config::GetInstance().GetUUID());
- pRequest->add("X-Plex-Device", "PC");
- pRequest->add("X-Plex-Device-Name", Config::GetInstance().GetHostname());
- pRequest->add("X-Plex-Language", Config::GetInstance().GetLanguage());
- pRequest->add("X-Plex-Model", "Linux");
- pRequest->add("X-Plex-Platform", "VDR");
- pRequest->add("X-Plex-Product", "plex-vdr");
- pRequest->add("X-Plex-Provides", "player");
- pRequest->add("X-Plex-Version", "0.0.1a");
-
- Poco::Net::HTTPClientSession* session = new Poco::Net::HTTPClientSession(m_sHost, m_iPort);
- std::ostream& oustr = session->sendRequest(*pRequest);
- oustr << msg;
+ try {
+ Poco::Net::HTTPRequest Request(Poco::Net::HTTPRequest::HTTP_POST,
+ "/:/timeline", Poco::Net::HTTPMessage::HTTP_1_1);
+
+ Request.add("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.52 Safari/537.17");
+
+ Request.add("X-Plex-Client-Identifier", Config::GetInstance().GetUUID());
+ Request.add("X-Plex-Device", "PC");
+ Request.add("X-Plex-Device-Name", Config::GetInstance().GetHostname());
+ Request.add("X-Plex-Language", Config::GetInstance().GetLanguage());
+ Request.add("X-Plex-Model", "Linux");
+ Request.add("X-Plex-Platform", "VDR");
+ Request.add("X-Plex-Product", "plex-vdr");
+ Request.add("X-Plex-Provides", "player");
+ Request.add("X-Plex-Version", "0.0.1a");
+
+ Poco::Net::HTTPClientSession session(m_sHost, m_iPort);
+
+ Request.setContentLength(msg.length());
+ session.sendRequest(Request) << msg;
+
+ Poco::Net::HTTPResponse response;
+ session.receiveResponse(response);
+
+ } catch (Poco::Exception& e) {
+ std::cout << e.displayText() << std::endl;
+ }
}
ActionManager::ActionManager() {}
-void ActionManager::AddAction(std::string file) {
- m_sAction = file;
+void ActionManager::AddAction(Video* video)
+{
+ m_myLock.Lock(&m_myMutex);
+ m_Action = video;
m_isAction = true;
}
-std::string ActionManager::GetAction() {
+Video* ActionManager::GetAction()
+{
+ m_myLock.Lock(&m_myMutex);
m_isAction = false;
- return m_sAction;
+ return m_Action;
}
-bool ActionManager::IsAction() {
+bool ActionManager::IsAction()
+{
+ //m_myLock.Lock(&m_myMutex);
return m_isAction;
}
+cSubscriberStatus::cSubscriberStatus()
+{
+ PlayerStopped = true;
+ pControl = NULL;
+}
+
+void cSubscriberStatus::ChannelSwitch(const cDevice* Device, int ChannelNumber, bool LiveView)
+{
+ dsyslog("[plex]: '%s'", __FUNCTION__);
+}
+
+void cSubscriberStatus::Replaying(const cControl* DvbPlayerControl, const char* Name, const char* FileName, bool On)
+{
+ //dsyslog("[plex]: '%s'", __FUNCTION__);
+ PlayerStopped = !On;
+ pControl = const_cast<cControl*>(DvbPlayerControl);
+ cHlsPlayerControl* hlsControl = dynamic_cast<cHlsPlayerControl*>(pControl);
+ if(hlsControl) {
+ pVideo = hlsControl->m_pVideo;
+ } else {
+ pVideo = NULL;
+ }
+
+ SubscriptionManager::GetInstance().Notify();
+}
+
+void cSubscriberStatus::SetVolume(int Volume, bool Absolute)
+{
+ dsyslog("[plex]: '%s'", __FUNCTION__);
}
+} // Namespace
diff --git a/SubscriptionManager.h b/SubscriptionManager.h
index 9d68e3c..ec87d9b 100644
--- a/SubscriptionManager.h
+++ b/SubscriptionManager.h
@@ -2,30 +2,51 @@
#define SUBSCRIPTIONMANAGER_H
#include <vdr/tools.h>
+#include <vdr/status.h>
#include <string>
#include <iostream>
#include <map>
//#include <mutex>
#include <sstream>
+#include "hlsPlayerControl.h"
+#include "PVideo.h"
namespace plexclient
{
+class cSubscriberStatus : public cStatus
+{
+protected:
+ virtual void ChannelSwitch(const cDevice *Device, int ChannelNumber, bool LiveView);
+ virtual void Replaying(const cControl *DvbPlayerControl, const char *Name, const char *FileName, bool On);
+ virtual void SetVolume(int Volume, bool Absolute);
+
+public:
+ cSubscriberStatus();
+ bool PlayerPaused;
+ bool PlayerStopped;
+ cControl* pControl;
+ Video* pVideo;
+ int Volume;
+
+};
+
class Subscriber
{
- public:
- int m_iCommandId;
+ friend class SubscriptionManager;
+public:
+ std::string m_iCommandId;
Subscriber() {};
- Subscriber(std::string protocol, std::string host, int port, std::string uuid, int commandId);
-
+ Subscriber(std::string protocol, std::string host, int port, std::string uuid, std::string commandId);
+
std::string GetUuid() {
return m_sUuid;
}
-
+
void SendUpdate(std::string msg, bool isNav);
-
+
virtual std::string to_string() {
- return "Subscriber-> Host: " + m_sHost + "; Port: " + std::string(itoa(m_iPort)) + "; Uuid:" + m_sUuid + "; CmdID:" + std::string(itoa(m_iCommandId));
+ return "Subscriber-> Host: " + m_sHost + "; Port: " + std::string(itoa(m_iPort)) + "; Uuid:" + m_sUuid + "; CmdID:" + m_iCommandId;
}
private:
@@ -33,11 +54,11 @@ private:
std::string m_sHost;
int m_iPort;
std::string m_sUuid;
-
+
int m_iAge;
};
-
-
+
+
class SubscriptionManager
{
public:
@@ -47,29 +68,39 @@ public:
}
void AddSubscriber(Subscriber subs);
void RemoveSubscriber(std::string uuid);
+ void UpdateSubscriber(std::string uuid, std::string commandId);
std::string GetMsg(std::string commandId);
void Notify();
-
- private:
+
+private:
SubscriptionManager();
- //std::mutex mutex;
+ cMutexLock m_myLock;
+ cMutex m_myMutex;
std::map<std::string, Subscriber> m_mSubcribers;
+ cSubscriberStatus* m_pStatus;
+ bool m_bStoppedSent;
+
+ void NotifyServer();
+ void Cleanup();
+ std::string GetTimelineXml();
};
class ActionManager
{
- public:
+public:
static ActionManager& GetInstance() {
static ActionManager instance;
return instance;
}
- void AddAction(std::string file);
- std::string GetAction();
+ void AddAction(Video* video);
+ Video* GetAction();
bool IsAction();
-
- private:
+
+private:
+ cMutexLock m_myLock;
+ cMutex m_myMutex;
ActionManager();
- std::string m_sAction;
+ Video* m_Action;
bool m_isAction;
};
diff --git a/hlsPlayer.cpp b/hlsPlayer.cpp
index 92ce5c6..a6c0e6b 100644
--- a/hlsPlayer.cpp
+++ b/hlsPlayer.cpp
@@ -341,11 +341,12 @@ void cHlsSegmentLoader::Ping(void)
//--- cHlsPlayer
-cHlsPlayer::cHlsPlayer(std::string startm3u8, plexclient::Video* Video)
+cHlsPlayer::cHlsPlayer(std::string startm3u8, plexclient::Video* Video, int offset)
{
+ dsyslog("[plex]: '%s'", __FUNCTION__);
m_pSegmentLoader = new cHlsSegmentLoader(startm3u8);
m_pVideo = Video;
- m_timeOffset = 0;
+ m_timeOffset = offset;
m_jumpOffset = 0;
m_tTimeSum = 0;
m_doJump = false;
@@ -356,6 +357,7 @@ cHlsPlayer::cHlsPlayer(std::string startm3u8, plexclient::Video* Video)
cHlsPlayer::~cHlsPlayer()
{
+ dsyslog("[plex]: '%s'", __FUNCTION__);
delete m_pSegmentLoader;
m_pSegmentLoader = NULL;
Detach();
diff --git a/hlsPlayer.h b/hlsPlayer.h
index c188fc2..12bea70 100644
--- a/hlsPlayer.h
+++ b/hlsPlayer.h
@@ -106,7 +106,7 @@ protected:
public:
//static cMutex s_mutex;
- cHlsPlayer(std::string startm3u8, plexclient::Video* Video);
+ cHlsPlayer(std::string startm3u8, plexclient::Video* Video, int offset = 0);
~cHlsPlayer();
virtual bool GetIndex(int &Current, int &Total, bool SnapToIFrame = false);
diff --git a/hlsPlayerControl.cpp b/hlsPlayerControl.cpp
index 168c60e..17bbc12 100644
--- a/hlsPlayerControl.cpp
+++ b/hlsPlayerControl.cpp
@@ -12,20 +12,26 @@ cControl* cHlsPlayerControl::Create(plexclient::Video* Video)
{
if(!Video->m_pServer)
return NULL;
+
+ // Stop already playing stream
+ cHlsPlayerControl* c = dynamic_cast<cHlsPlayerControl*>(cControl::Control(true));
+ if(c) {
+ c->Stop();
+ }
// get Metadata
std::string uri = Video->m_pServer->GetUri() + Video->m_sKey;
plexclient::MediaContainer *pmcontainer = plexclient::Plexservice::GetMediaContainer(uri);
- std::string transcodeUri = plexclient::Plexservice::GetUniversalTranscodeUrl(&pmcontainer->m_vVideos[0]);
-
- cHlsPlayerControl* playerControl = new cHlsPlayerControl(new cHlsPlayer(transcodeUri, &pmcontainer->m_vVideos[0]), pmcontainer);
+ std::string transcodeUri = plexclient::Plexservice::GetUniversalTranscodeUrl(&pmcontainer->m_vVideos[0], Video->m_iMyPlayOffset);
+ cHlsPlayerControl* playerControl = new cHlsPlayerControl(new cHlsPlayer(transcodeUri, &pmcontainer->m_vVideos[0], Video->m_iMyPlayOffset), pmcontainer);
playerControl->m_title = pmcontainer->m_vVideos[0].m_sTitle;
return playerControl;
}
cHlsPlayerControl::cHlsPlayerControl(cHlsPlayer* Player, plexclient::MediaContainer* Container) :cControl(Player)
{
+ dsyslog("[plex]: '%s'", __FUNCTION__);
player = Player;
m_pVideo = &Container->m_vVideos[0];
//m_title = title;
@@ -192,6 +198,13 @@ void cHlsPlayerControl::Stop(void)
player->Stop();
}
+void cHlsPlayerControl::SeekTo(int offset)
+{
+ if (player) {
+ player->JumpTo(offset);
+ }
+}
+
void cHlsPlayerControl::ShowMode(void)
{
//dsyslog("[plex]: '%s'\n", __FUNCTION__);
diff --git a/hlsPlayerControl.h b/hlsPlayerControl.h
index 2f7347b..9a3fc60 100644
--- a/hlsPlayerControl.h
+++ b/hlsPlayerControl.h
@@ -13,7 +13,6 @@ class cHlsPlayerControl : public cControl
private:
static volatile int active;
plexclient::MediaContainer* m_pMediaContainer;
- plexclient::Video* m_pVideo;
cHlsPlayer* player;
std::string m_title;
@@ -32,6 +31,8 @@ protected:
//void ShowMode();
public:
+ plexclient::Video* m_pVideo;
+
static cControl* Create(plexclient::Video* Video);
cHlsPlayerControl(cHlsPlayer* Player, plexclient::MediaContainer* Container);
virtual ~cHlsPlayerControl();
@@ -47,6 +48,7 @@ public:
void Pause(void);
void Play(void);
void Stop(void);
+ void SeekTo(int offset);
};
diff --git a/plex.cpp b/plex.cpp
index 9ef5c4d..0c269dd 100644
--- a/plex.cpp
+++ b/plex.cpp
@@ -337,8 +337,8 @@ void cMyPlugin::MainThreadHook(void)
// dsyslog("[plex]%s:\n", __FUNCTION__);
// Start Tasks, e.g. Play Video
if(plexclient::ActionManager::GetInstance().IsAction()) {
- std::string file = plexclient::ActionManager::GetInstance().GetAction();
- //PlayFile(file, NULL);
+ plexclient::Video* video = plexclient::ActionManager::GetInstance().GetAction();
+ PlayFile(video);
}
}