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/GUISettings.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"
33 #include "libhts/htsmsg.h"
34 #include "libhts/htsmsg_binary.h"
37 using namespace XFILE;
52 CHTSPDirectorySession* session;
59 STimedOut(DWORD idle) : m_idle(idle)
61 m_time = XbmcThreads::SystemClockMillis();
63 bool operator()(SSession& data)
65 return data.refs == 0 && (m_time - data.last) > m_idle;
71 typedef std::vector<SSession> SSessions;
74 static SSessions g_sessions;
75 static CCriticalSection g_section;
78 CHTSPDirectorySession::CHTSPDirectorySession() : CThread("CHTSPDirectorySession")
82 CHTSPDirectorySession::~CHTSPDirectorySession()
87 CHTSPDirectorySession* CHTSPDirectorySession::Acquire(const CURL& url)
89 CSingleLock lock(g_section);
91 for(SSessions::iterator it = g_sessions.begin(); it != g_sessions.end(); it++)
93 if(it->hostname == url.GetHostName()
94 && it->port == url.GetPort()
95 && it->username == url.GetUserName()
96 && it->password == url.GetPassWord())
104 CHTSPDirectorySession* session = new CHTSPDirectorySession();
105 if(session->Open(url))
108 data.hostname = url.GetHostName();
109 data.port = url.GetPort();
110 data.username = url.GetUserName();
111 data.password = url.GetPassWord();
112 data.session = session;
115 g_sessions.push_back(data);
123 void CHTSPDirectorySession::Release(CHTSPDirectorySession* &session)
128 CSingleLock lock(g_section);
129 for(SSessions::iterator it = g_sessions.begin(); it != g_sessions.end(); it++)
131 if(it->session == session)
134 it->last = XbmcThreads::SystemClockMillis();
138 CLog::Log(LOGERROR, "CHTSPDirectorySession::Release - release of invalid session");
142 void CHTSPDirectorySession::CheckIdle(DWORD idle)
144 CSingleLock lock(g_section);
145 STimedOut timeout(idle);
147 for(SSessions::iterator it = g_sessions.begin(); it != g_sessions.end();)
151 CLog::Log(LOGINFO, "CheckIdle - Closing session to htsp://%s:%i", it->hostname.c_str(), it->port);
153 it = g_sessions.erase(it);
160 bool CHTSPDirectorySession::Open(const CURL& url)
162 if(!m_session.Connect(url.GetHostName(), url.GetPort()))
165 if(m_session.GetProtocol() < 2)
167 CLog::Log(LOGERROR, "CHTSPDirectory::GetDirectory - incompatible protocol version %d", m_session.GetProtocol());
171 if(!url.GetUserName().IsEmpty())
172 m_session.Auth(url.GetUserName(), url.GetPassWord());
174 if(!m_session.SendEnableAsync())
179 m_started.WaitMSec(30000);
183 void CHTSPDirectorySession::Close()
191 htsmsg_t* CHTSPDirectorySession::ReadResult(htsmsg_t* m)
193 CSingleLock lock(m_section);
194 unsigned seq (m_session.AddSequence());
196 SMessage &message(m_queue[seq]);
197 message.event = new CEvent();
201 htsmsg_add_u32(m, "seq", seq);
202 if(!m_session.SendMessage(m))
208 if(!message.event->WaitMSec(2000))
209 CLog::Log(LOGERROR, "CHTSPDirectorySession::ReadResult - Timeout waiting for response");
213 delete message.event;
220 bool CHTSPDirectorySession::GetEvent(SEvent& event, uint32_t id)
228 SEvents::iterator it = m_events.find(id);
229 if(it != m_events.end())
235 htsmsg_t *msg = htsmsg_create_map();
236 htsmsg_add_str(msg, "method", "getEvent");
237 htsmsg_add_u32(msg, "eventId", id);
238 if((msg = ReadResult(msg)) == NULL)
240 CLog::Log(LOGDEBUG, "CHTSPSession::GetEvent - failed to get event %u", id);
243 if(!CHTSPSession::ParseEvent(msg, id, event))
246 m_events[id] = event;
250 void CHTSPDirectorySession::Process()
252 CLog::Log(LOGDEBUG, "CHTSPDirectorySession::Process() - Starting");
258 if((msg = m_session.ReadMessage()) == NULL)
262 if(htsmsg_get_u32(msg, "seq", &seq) == 0)
264 CSingleLock lock(m_section);
265 SMessages::iterator it = m_queue.find(seq);
266 if(it != m_queue.end())
268 it->second.msg = msg;
269 it->second.event->Set();
275 if((method = htsmsg_get_str(msg, "method")) == NULL)
281 if (strstr(method, "channelAdd"))
282 CHTSPSession::ParseChannelUpdate(msg, m_channels);
283 else if(strstr(method, "channelUpdate"))
284 CHTSPSession::ParseChannelUpdate(msg, m_channels);
285 else if(strstr(method, "channelRemove"))
286 CHTSPSession::ParseChannelRemove(msg, m_channels);
287 if (strstr(method, "tagAdd"))
288 CHTSPSession::ParseTagUpdate(msg, m_tags);
289 else if(strstr(method, "tagUpdate"))
290 CHTSPSession::ParseTagUpdate(msg, m_tags);
291 else if(strstr(method, "tagRemove"))
292 CHTSPSession::ParseTagRemove(msg, m_tags);
293 else if(strstr(method, "initialSyncCompleted"))
300 CLog::Log(LOGDEBUG, "CHTSPDirectorySession::Process() - Exiting");
303 SChannels CHTSPDirectorySession::GetChannels()
305 return GetChannels(0);
308 SChannels CHTSPDirectorySession::GetChannels(int tag)
310 CSingleLock lock(m_section);
314 STags::iterator it = m_tags.find(tag);
315 if(it == m_tags.end())
320 return GetChannels(it->second);
323 SChannels CHTSPDirectorySession::GetChannels(STag& tag)
325 CSingleLock lock(m_section);
328 std::vector<int>::iterator it;
329 for(it = tag.channels.begin(); it != tag.channels.end(); it++)
331 SChannels::iterator it2 = m_channels.find(*it);
332 if(it2 == m_channels.end())
334 CLog::Log(LOGERROR, "CHTSPDirectorySession::GetChannels - tag points to unknown channel %d", *it);
337 channels[*it] = it2->second;
343 STags CHTSPDirectorySession::GetTags()
345 CSingleLock lock(m_section);
349 CHTSPDirectory::CHTSPDirectory(void)
354 CHTSPDirectory::~CHTSPDirectory(void)
356 CHTSPDirectorySession::Release(m_session);
360 bool CHTSPDirectory::GetChannels(const CURL &base, CFileItemList &items)
362 SChannels channels = m_session->GetChannels();
363 return GetChannels(base, items, channels, 0);
366 bool CHTSPDirectory::GetChannels( const CURL &base
367 , CFileItemList &items
375 for(SChannels::iterator it = channels.begin(); it != channels.end(); it++)
377 if(!m_session->GetEvent(event, it->second.event))
380 CFileItemPtr item(new CFileItem("", true));
383 item->SetPath(url.Get());
384 CHTSPSession::ParseItem(it->second, tag, event, *item);
385 item->m_bIsFolder = false;
386 item->SetLabel(item->m_strTitle);
387 item->m_strTitle.Format("%d", it->second.num);
392 items.AddSortMethod(SORT_METHOD_TRACKNUM, 554, LABEL_MASKS("%K[ - %B]", "%Z", "%L", ""));
394 if (g_guiSettings.GetBool("filelists.ignorethewhensorting"))
395 items.AddSortMethod(SORT_METHOD_ALBUM_IGNORE_THE, 558, LABEL_MASKS("%B", "%Z", "%L", ""));
397 items.AddSortMethod(SORT_METHOD_ALBUM, 558, LABEL_MASKS("%B", "%Z", "%L", ""));
399 if (g_guiSettings.GetBool("filelists.ignorethewhensorting"))
400 items.AddSortMethod(SORT_METHOD_LABEL_IGNORE_THE, 551, LABEL_MASKS("%Z", "%B", "%L", ""));
402 items.AddSortMethod(SORT_METHOD_LABEL, 551, LABEL_MASKS("%Z", "%B", "%L", ""));
404 items.SetContent("livetv");
406 return !channels.empty();
410 bool CHTSPDirectory::GetTag(const CURL &base, CFileItemList &items)
414 int id = atoi(url.GetFileName().Mid(5));
416 SChannels channels = m_session->GetChannels(id);
420 return GetChannels(base, items, channels, id);
424 bool CHTSPDirectory::GetDirectory(const CStdString& strPath, CFileItemList &items)
428 CHTSPDirectorySession::Release(m_session);
429 if(!(m_session = CHTSPDirectorySession::Acquire(url)))
433 if(url.GetFileName().IsEmpty())
437 item.reset(new CFileItem("", true));
438 url.SetFileName("tags/0/");
439 item->SetPath(url.Get());
440 item->SetLabel(g_localizeStrings.Get(22018));
441 item->SetLabelPreformated(true);
444 STags tags = m_session->GetTags();
445 CStdString filename, label;
446 for(STags::iterator it = tags.begin(); it != tags.end(); it++)
448 filename.Format("tags/%d/", it->second.id);
449 label.Format("Tag: %s", it->second.name);
451 item.reset(new CFileItem("", true));
452 url.SetFileName(filename);
453 item->SetPath(url.Get());
454 item->SetLabel(label);
455 item->SetLabelPreformated(true);
456 item->SetArt("thumb", it->second.icon);
462 else if(url.GetFileName().Left(5) == "tags/")
463 return GetTag(url, items);