2 * Copyright (C) 2005-2013 Team XBMC
5 * This Program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
10 * This Program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with XBMC; see the file COPYING. If not, see
17 * <http://www.gnu.org/licenses/>.
21 #include "threads/SystemClock.h"
22 #include "HTSPDirectory.h"
25 #include "settings/Settings.h"
26 #include "guilib/LocalizeStrings.h"
27 #include "cores/dvdplayer/DVDInputStreams/DVDInputStreamHTSP.h"
28 #include "threads/SingleLock.h"
29 #include "utils/log.h"
30 #include "utils/TimeUtils.h"
31 #include "utils/StringUtils.h"
34 #include "libhts/htsmsg.h"
35 #include "libhts/htsmsg_binary.h"
38 using namespace XFILE;
55 CHTSPDirectorySession* session;
62 STimedOut(DWORD idle) : m_idle(idle)
64 m_time = XbmcThreads::SystemClockMillis();
66 bool operator()(SSession& data)
68 return data.refs == 0 && (m_time - data.last) > m_idle;
74 typedef std::vector<SSession> SSessions;
77 static SSessions g_sessions;
78 static CCriticalSection g_section;
81 CHTSPDirectorySession::CHTSPDirectorySession() : CThread("HTSPDirectorySession")
85 CHTSPDirectorySession::~CHTSPDirectorySession()
90 CHTSPDirectorySession* CHTSPDirectorySession::Acquire(const CURL& url)
92 CSingleLock lock(g_section);
94 for(SSessions::iterator it = g_sessions.begin(); it != g_sessions.end(); it++)
96 if(it->hostname == url.GetHostName()
97 && it->port == url.GetPort()
98 && it->username == url.GetUserName()
99 && it->password == url.GetPassWord())
107 CHTSPDirectorySession* session = new CHTSPDirectorySession();
108 if(session->Open(url))
111 data.hostname = url.GetHostName();
112 data.port = url.GetPort();
113 data.username = url.GetUserName();
114 data.password = url.GetPassWord();
115 data.session = session;
118 g_sessions.push_back(data);
126 void CHTSPDirectorySession::Release(CHTSPDirectorySession* &session)
131 CSingleLock lock(g_section);
132 for(SSessions::iterator it = g_sessions.begin(); it != g_sessions.end(); it++)
134 if(it->session == session)
137 it->last = XbmcThreads::SystemClockMillis();
141 CLog::Log(LOGERROR, "CHTSPDirectorySession::Release - release of invalid session");
145 void CHTSPDirectorySession::CheckIdle(DWORD idle)
147 CSingleLock lock(g_section);
148 STimedOut timeout(idle);
150 for(SSessions::iterator it = g_sessions.begin(); it != g_sessions.end();)
154 CLog::Log(LOGINFO, "CheckIdle - Closing session to htsp://%s:%i", it->hostname.c_str(), it->port);
156 it = g_sessions.erase(it);
163 bool CHTSPDirectorySession::Open(const CURL& url)
165 if(!m_session.Connect(url.GetHostName(), url.GetPort()))
168 if(m_session.GetProtocol() < 2)
170 CLog::Log(LOGERROR, "CHTSPDirectory::GetDirectory - incompatible protocol version %d", m_session.GetProtocol());
174 if(!url.GetUserName().IsEmpty())
175 m_session.Auth(url.GetUserName(), url.GetPassWord());
177 if(!m_session.SendEnableAsync())
182 m_started.WaitMSec(30000);
186 void CHTSPDirectorySession::Close()
194 htsmsg_t* CHTSPDirectorySession::ReadResult(htsmsg_t* m)
196 CSingleLock lock(m_section);
197 unsigned seq (m_session.AddSequence());
199 SMessage &message(m_queue[seq]);
200 message.event = new CEvent();
204 htsmsg_add_u32(m, "seq", seq);
205 if(!m_session.SendMessage(m))
211 if(!message.event->WaitMSec(2000))
212 CLog::Log(LOGERROR, "CHTSPDirectorySession::ReadResult - Timeout waiting for response");
216 delete message.event;
223 bool CHTSPDirectorySession::GetEvent(SEvent& event, uint32_t id)
231 SEvents::iterator it = m_events.find(id);
232 if(it != m_events.end())
238 htsmsg_t *msg = htsmsg_create_map();
239 htsmsg_add_str(msg, "method", "getEvent");
240 htsmsg_add_u32(msg, "eventId", id);
241 if((msg = ReadResult(msg)) == NULL)
243 CLog::Log(LOGDEBUG, "CHTSPSession::GetEvent - failed to get event %u", id);
246 if(!CHTSPSession::ParseEvent(msg, id, event))
249 m_events[id] = event;
253 void CHTSPDirectorySession::Process()
255 CLog::Log(LOGDEBUG, "CHTSPDirectorySession::Process() - Starting");
261 if((msg = m_session.ReadMessage()) == NULL)
265 if(htsmsg_get_u32(msg, "seq", &seq) == 0)
267 CSingleLock lock(m_section);
268 SMessages::iterator it = m_queue.find(seq);
269 if(it != m_queue.end())
271 it->second.msg = msg;
272 it->second.event->Set();
278 if((method = htsmsg_get_str(msg, "method")) == NULL)
284 if (strstr(method, "channelAdd"))
285 CHTSPSession::ParseChannelUpdate(msg, m_channels);
286 else if(strstr(method, "channelUpdate"))
287 CHTSPSession::ParseChannelUpdate(msg, m_channels);
288 else if(strstr(method, "channelRemove"))
289 CHTSPSession::ParseChannelRemove(msg, m_channels);
290 if (strstr(method, "tagAdd"))
291 CHTSPSession::ParseTagUpdate(msg, m_tags);
292 else if(strstr(method, "tagUpdate"))
293 CHTSPSession::ParseTagUpdate(msg, m_tags);
294 else if(strstr(method, "tagRemove"))
295 CHTSPSession::ParseTagRemove(msg, m_tags);
296 else if(strstr(method, "initialSyncCompleted"))
303 CLog::Log(LOGDEBUG, "CHTSPDirectorySession::Process() - Exiting");
306 SChannels CHTSPDirectorySession::GetChannels()
308 return GetChannels(0);
311 SChannels CHTSPDirectorySession::GetChannels(int tag)
313 CSingleLock lock(m_section);
317 STags::iterator it = m_tags.find(tag);
318 if(it == m_tags.end())
323 return GetChannels(it->second);
326 SChannels CHTSPDirectorySession::GetChannels(STag& tag)
328 CSingleLock lock(m_section);
331 std::vector<int>::iterator it;
332 for(it = tag.channels.begin(); it != tag.channels.end(); it++)
334 SChannels::iterator it2 = m_channels.find(*it);
335 if(it2 == m_channels.end())
337 CLog::Log(LOGERROR, "CHTSPDirectorySession::GetChannels - tag points to unknown channel %d", *it);
340 channels[*it] = it2->second;
346 STags CHTSPDirectorySession::GetTags()
348 CSingleLock lock(m_section);
352 CHTSPDirectory::CHTSPDirectory(void)
357 CHTSPDirectory::~CHTSPDirectory(void)
359 CHTSPDirectorySession::Release(m_session);
363 bool CHTSPDirectory::GetChannels(const CURL &base, CFileItemList &items)
365 SChannels channels = m_session->GetChannels();
366 return GetChannels(base, items, channels, 0);
369 bool CHTSPDirectory::GetChannels( const CURL &base
370 , CFileItemList &items
378 for(SChannels::iterator it = channels.begin(); it != channels.end(); it++)
380 if(!m_session->GetEvent(event, it->second.event))
383 CFileItemPtr item(new CFileItem("", true));
386 item->SetPath(url.Get());
387 CHTSPSession::ParseItem(it->second, tag, event, *item);
388 item->m_bIsFolder = false;
389 item->SetLabel(item->m_strTitle);
390 item->m_strTitle = StringUtils::Format("%d", it->second.num);
395 items.AddSortMethod(SortByTrackNumber, 554, LABEL_MASKS("%K[ - %B]", "%Z", "%L", ""));
396 items.AddSortMethod(SortByAlbum, 558, LABEL_MASKS("%B", "%Z", "%L", ""), CSettings::Get().GetBool("filelists.ignorethewhensorting") ? SortAttributeIgnoreArticle : SortAttributeNone);
397 items.AddSortMethod(SortByLabel, 551, LABEL_MASKS("%Z", "%B", "%L", ""), CSettings::Get().GetBool("filelists.ignorethewhensorting") ? SortAttributeIgnoreArticle : SortAttributeNone);
399 items.SetContent("livetv");
401 return !channels.empty();
405 bool CHTSPDirectory::GetTag(const CURL &base, CFileItemList &items)
409 int id = atoi(url.GetFileName().Mid(5));
411 SChannels channels = m_session->GetChannels(id);
415 return GetChannels(base, items, channels, id);
419 bool CHTSPDirectory::GetDirectory(const CStdString& strPath, CFileItemList &items)
423 CHTSPDirectorySession::Release(m_session);
424 if(!(m_session = CHTSPDirectorySession::Acquire(url)))
428 if(url.GetFileName().IsEmpty())
432 item.reset(new CFileItem("", true));
433 url.SetFileName("tags/0/");
434 item->SetPath(url.Get());
435 item->SetLabel(g_localizeStrings.Get(22018));
436 item->SetLabelPreformated(true);
439 STags tags = m_session->GetTags();
440 CStdString filename, label;
441 for(STags::iterator it = tags.begin(); it != tags.end(); it++)
443 filename = StringUtils::Format("tags/%d/", it->second.id);
444 label = StringUtils::Format("Tag: %s", it->second.name.c_str());
446 item.reset(new CFileItem("", true));
447 url.SetFileName(filename);
448 item->SetPath(url.Get());
449 item->SetLabel(label);
450 item->SetLabelPreformated(true);
451 item->SetArt("thumb", it->second.icon);
457 else if(url.GetFileName().Left(5) == "tags/")
458 return GetTag(url, items);