diff options
author | chriszero <zerov83@gmail.com> | 2015-02-05 21:32:55 +0100 |
---|---|---|
committer | chriszero <zerov83@gmail.com> | 2015-02-05 21:32:55 +0100 |
commit | 312c13f4f6b299a976787542869f96b1e66549cf (patch) | |
tree | 7ee2732eaec79d50e0c9718960fd58e2436f10f6 | |
parent | 1ecdb2a5d9e05e0d30dae89e3d675218ca4c255e (diff) | |
download | vdr-plugin-plex-312c13f4f6b299a976787542869f96b1e66549cf.tar.gz vdr-plugin-plex-312c13f4f6b299a976787542869f96b1e66549cf.tar.bz2 |
Plex remote support.
Control via Plexapps (Android, IOS...)
-rw-r--r-- | ControlServer.cpp | 23 | ||||
-rw-r--r-- | ControlServer.h | 10 | ||||
-rw-r--r-- | PVideo.cpp | 1 | ||||
-rw-r--r-- | PVideo.h | 1 | ||||
-rw-r--r-- | PlexHTTPRequestHandler.cpp | 298 | ||||
-rw-r--r-- | PlexHTTPRequestHandler.h | 3 | ||||
-rw-r--r-- | PlexReqHandlerFactory.cpp | 17 | ||||
-rw-r--r-- | Plexservice.h | 2 | ||||
-rw-r--r-- | SubscriptionManager.cpp | 279 | ||||
-rw-r--r-- | SubscriptionManager.h | 69 | ||||
-rw-r--r-- | hlsPlayer.cpp | 6 | ||||
-rw-r--r-- | hlsPlayer.h | 2 | ||||
-rw-r--r-- | hlsPlayerControl.cpp | 19 | ||||
-rw-r--r-- | hlsPlayerControl.h | 4 | ||||
-rw-r--r-- | plex.cpp | 4 |
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; @@ -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(); @@ -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); }; @@ -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); } } |