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/>.
22 #include "GUIUserMessages.h"
23 #include "Application.h"
24 #include "GUIDialogSubtitles.h"
25 #include "addons/AddonManager.h"
26 #include "cores/IPlayer.h"
27 #include "dialogs/GUIDialogKaiToast.h"
28 #include "filesystem/AddonsDirectory.h"
29 #include "filesystem/File.h"
30 #include "filesystem/PluginDirectory.h"
31 #include "filesystem/SpecialProtocol.h"
32 #include "guilib/GUIImage.h"
33 #include "settings/MediaSettings.h"
34 #include "settings/Settings.h"
35 #include "settings/VideoSettings.h"
36 #include "settings/lib/Setting.h"
37 #include "utils/JobManager.h"
38 #include "utils/LangCodeExpander.h"
39 #include "utils/log.h"
40 #include "utils/StringUtils.h"
41 #include "utils/URIUtils.h"
44 #include "video/VideoDatabase.h"
46 using namespace ADDON;
47 using namespace XFILE;
49 #define CONTROL_NAMELABEL 100
50 #define CONTROL_NAMELOGO 110
51 #define CONTROL_SUBLIST 120
52 #define CONTROL_SUBSEXIST 130
53 #define CONTROL_SUBSTATUS 140
54 #define CONTROL_SERVICELIST 150
56 /*! \brief simple job to retrieve a directory and store a string (language)
58 class CSubtitlesJob: public CJob
61 CSubtitlesJob(const CURL &url, const std::string &language) : m_url(url), m_language(language)
63 m_items = new CFileItemList;
65 virtual ~CSubtitlesJob()
71 CDirectory::GetDirectory(m_url.Get(), *m_items);
74 virtual bool operator==(const CJob *job) const
76 if (strcmp(job->GetType(),GetType()) == 0)
78 const CSubtitlesJob* rjob = dynamic_cast<const CSubtitlesJob*>(job);
81 return m_url.Get() == rjob->m_url.Get() &&
82 m_language == rjob->m_language;
87 const CFileItemList *GetItems() const { return m_items; }
88 const CURL &GetURL() const { return m_url; }
89 const std::string &GetLanguage() const { return m_language; }
92 CFileItemList *m_items;
93 std::string m_language;
96 CGUIDialogSubtitles::CGUIDialogSubtitles(void)
97 : CGUIDialog(WINDOW_DIALOG_SUBTITLES, "DialogSubtitles.xml")
99 m_loadType = KEEP_IN_MEMORY;
100 m_subtitles = new CFileItemList;
101 m_serviceItems = new CFileItemList;
102 m_pausedOnRun = false;
103 m_updateSubsList = false;
106 CGUIDialogSubtitles::~CGUIDialogSubtitles(void)
110 delete m_serviceItems;
113 bool CGUIDialogSubtitles::OnMessage(CGUIMessage& message)
115 if (message.GetMessage() == GUI_MSG_CLICKED)
117 int iControl = message.GetSenderId();
119 if (iControl == CONTROL_SUBLIST)
121 CGUIMessage msg(GUI_MSG_ITEM_SELECTED, GetID(), CONTROL_SUBLIST);
124 int item = msg.GetParam1();
125 if (item >= 0 && item < m_subtitles->Size())
126 Download(*m_subtitles->Get(item));
129 else if (iControl == CONTROL_SERVICELIST)
131 CGUIMessage msg(GUI_MSG_ITEM_SELECTED, GetID(), CONTROL_SERVICELIST);
134 int item = msg.GetParam1();
135 if (item >= 0 && item < m_serviceItems->Size() &&
136 SetService(m_serviceItems->Get(item)->GetProperty("Addon.ID").asString()))
142 else if (message.GetMessage() == GUI_MSG_WINDOW_DEINIT)
144 // Resume the video if the user has requested it
145 if (g_application.m_pPlayer->IsPaused() && m_pausedOnRun)
146 g_application.m_pPlayer->Pause();
148 CGUIDialog::OnMessage(message);
154 return CGUIDialog::OnMessage(message);
157 void CGUIDialogSubtitles::OnInitWindow()
159 // Pause the video if the user has requested it
160 m_pausedOnRun = false;
161 if (CSettings::Get().GetBool("subtitles.pauseonsearch") && !g_application.m_pPlayer->IsPaused())
163 g_application.m_pPlayer->Pause();
164 m_pausedOnRun = true;
168 CGUIWindow::OnInitWindow();
172 void CGUIDialogSubtitles::Process(unsigned int currentTime, CDirtyRegionList &dirtyregions)
176 // take copies of our variables to ensure we don't hold the lock for long.
180 CSingleLock lock(m_section);
182 subs.Assign(*m_subtitles);
184 SET_CONTROL_LABEL(CONTROL_SUBSTATUS, status);
186 if (m_updateSubsList)
188 CGUIMessage message(GUI_MSG_LABEL_BIND, GetID(), CONTROL_SUBLIST, 0, 0, &subs);
190 m_updateSubsList = false;
193 int control = GetFocusedControlID();
197 CGUIMessage msg(GUI_MSG_SETFOCUS, GetID(), m_subtitles->IsEmpty() ?
198 CONTROL_SERVICELIST : CONTROL_SUBLIST);
201 // subs list is focused but we have no subs
202 else if (control == CONTROL_SUBLIST && m_subtitles->IsEmpty())
204 CGUIMessage msg(GUI_MSG_SETFOCUS, GetID(), CONTROL_SERVICELIST);
208 CGUIDialog::Process(currentTime, dirtyregions);
211 void CGUIDialogSubtitles::FillServices()
216 ADDON::CAddonMgr::Get().GetAddons(ADDON_SUBTITLE_MODULE, addons, true);
220 UpdateStatus(NO_SERVICES);
224 std::string defaultService;
225 const CFileItem &item = g_application.CurrentFileItem();
226 if (item.GetVideoContentType() == VIDEODB_CONTENT_TVSHOWS ||
227 item.GetVideoContentType() == VIDEODB_CONTENT_EPISODES)
228 // Set default service for tv shows
229 defaultService = CSettings::Get().GetString("subtitles.tv");
231 // Set default service for filemode and movies
232 defaultService = CSettings::Get().GetString("subtitles.movie");
234 std::string service = addons.front()->ID();
235 for (VECADDONS::const_iterator addonIt = addons.begin(); addonIt != addons.end(); addonIt++)
237 CFileItemPtr item(CAddonsDirectory::FileItemFromAddon(*addonIt, "plugin://", false));
238 m_serviceItems->Add(item);
239 if ((*addonIt)->ID() == defaultService)
240 service = (*addonIt)->ID();
243 // Bind our services to the UI
244 CGUIMessage msg(GUI_MSG_LABEL_BIND, GetID(), CONTROL_SERVICELIST, 0, 0, m_serviceItems);
250 bool CGUIDialogSubtitles::SetService(const std::string &service)
252 if (service != m_currentService)
254 m_currentService = service;
255 CLog::Log(LOGDEBUG, "New Service [%s] ", m_currentService.c_str());
257 CFileItemPtr currentService = GetService();
258 // highlight this item in the skin
259 for (int i = 0; i < m_serviceItems->Size(); i++)
261 CFileItemPtr pItem = m_serviceItems->Get(i);
262 pItem->Select(pItem == currentService);
265 SET_CONTROL_LABEL(CONTROL_NAMELABEL, currentService->GetLabel());
267 CGUIImage* image = (CGUIImage*)GetControl(CONTROL_NAMELOGO);
270 std::string icon = URIUtils::AddFileToFolder(currentService->GetProperty("Addon.Path").asString(), "logo.png");
271 image->SetFileName(icon);
273 if (g_application.m_pPlayer->GetSubtitleCount() == 0)
274 SET_CONTROL_HIDDEN(CONTROL_SUBSEXIST);
276 SET_CONTROL_VISIBLE(CONTROL_SUBSEXIST);
283 const CFileItemPtr CGUIDialogSubtitles::GetService() const
285 for (int i = 0; i < m_serviceItems->Size(); i++)
287 if (m_serviceItems->Get(i)->GetProperty("Addon.ID") == m_currentService)
288 return m_serviceItems->Get(i);
290 return CFileItemPtr();
293 void CGUIDialogSubtitles::Search()
295 if (m_currentService.empty())
296 return; // no services available
298 UpdateStatus(SEARCHING);
301 CURL url("plugin://" + m_currentService + "/");
302 url.SetOption("action", "search");
304 const CSetting *setting = CSettings::Get().GetSetting("subtitles.languages");
306 url.SetOption("languages", setting->ToString());
308 AddJob(new CSubtitlesJob(url, ""));
311 void CGUIDialogSubtitles::OnJobComplete(unsigned int jobID, bool success, CJob *job)
313 const CURL &url = ((CSubtitlesJob *)job)->GetURL();
314 const CFileItemList *items = ((CSubtitlesJob *)job)->GetItems();
315 const std::string &language = ((CSubtitlesJob *)job)->GetLanguage();
316 if (url.GetOption("action") == "search")
317 OnSearchComplete(items);
319 OnDownloadComplete(items, language);
320 CJobQueue::OnJobComplete(jobID, success, job);
323 void CGUIDialogSubtitles::OnSearchComplete(const CFileItemList *items)
325 CSingleLock lock(m_section);
326 m_subtitles->Assign(*items);
327 UpdateStatus(SEARCH_COMPLETE);
328 m_updateSubsList = true;
332 void CGUIDialogSubtitles::UpdateStatus(STATUS status)
334 CSingleLock lock(m_section);
339 label = g_localizeStrings.Get(24114);
342 label = g_localizeStrings.Get(24107);
344 case SEARCH_COMPLETE:
345 if (!m_subtitles->IsEmpty())
346 label = StringUtils::Format(g_localizeStrings.Get(24108).c_str(), m_subtitles->Size());
348 label = g_localizeStrings.Get(24109);
351 label = g_localizeStrings.Get(24110);
356 if (label != m_status)
363 void CGUIDialogSubtitles::Download(const CFileItem &subtitle)
365 UpdateStatus(DOWNLOADING);
367 // subtitle URL should be of the form plugin://<addonid>/?param=foo¶m=bar
368 // we just append (if not already present) the action=download parameter.
369 CURL url(subtitle.GetAsUrl());
370 if (url.GetOption("action").empty())
371 url.SetOption("action", "download");
373 AddJob(new CSubtitlesJob(url, subtitle.GetLabel()));
376 void CGUIDialogSubtitles::OnDownloadComplete(const CFileItemList *items, const std::string &language)
378 if (items->IsEmpty())
380 CFileItemPtr service = GetService();
382 CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Error, service->GetLabel(), g_localizeStrings.Get(24113));
383 UpdateStatus(SEARCH_COMPLETE);
387 CStdString strFileName;
388 CStdString strDestPath;
389 if (g_application.CurrentFileItem().IsStack())
391 for (int i = 0; i < items->Size(); i++)
393 // check for all stack items and match to given subs, item [0] == CD1, item [1] == CD2
394 // CLog::Log(LOGDEBUG, "Stack Subs [%s} Found", vecItems[i]->GetLabel().c_str());
397 else if (StringUtils::StartsWith(g_application.CurrentFile(), "http://"))
399 strFileName = "TemporarySubs";
400 strDestPath = "special://temp/";
404 strFileName = URIUtils::GetFileName(g_application.CurrentFile());
405 if (CSettings::Get().GetBool("subtitles.savetomoviefolder"))
407 strDestPath = URIUtils::GetDirectory(g_application.CurrentFile());
408 if (!CUtil::SupportsWriteFileOperations(strDestPath))
411 if (strDestPath.empty())
413 if (CSpecialProtocol::TranslatePath("special://subtitles").empty())
414 strDestPath = "special://temp";
416 strDestPath = "special://subtitles";
419 // Extract the language and appropriate extension
420 CStdString strSubLang;
421 g_LangCodeExpander.ConvertToTwoCharCode(strSubLang, language);
422 CStdString strUrl = items->Get(0)->GetPath();
423 CStdString strSubExt = URIUtils::GetExtension(strUrl);
425 // construct subtitle path
426 URIUtils::RemoveExtension(strFileName);
427 CStdString strSubName = StringUtils::Format("%s.%s%s", strFileName.c_str(), strSubLang.c_str(), strSubExt.c_str());
428 CStdString strSubPath = URIUtils::AddFileToFolder(strDestPath, strSubName);
430 // and copy the file across
431 CFile::Cache(strUrl, strSubPath);
432 SetSubtitles(strSubPath);
437 void CGUIDialogSubtitles::ClearSubtitles()
439 CGUIMessage msg(GUI_MSG_LABEL_RESET, GetID(), CONTROL_SUBLIST);
441 CSingleLock lock(m_section);
442 m_subtitles->Clear();
445 void CGUIDialogSubtitles::ClearServices()
447 CGUIMessage msg(GUI_MSG_LABEL_RESET, GetID(), CONTROL_SERVICELIST);
449 m_serviceItems->Clear();
450 m_currentService.clear();
453 void CGUIDialogSubtitles::SetSubtitles(const std::string &subtitle)
455 if (g_application.m_pPlayer)
457 int nStream = g_application.m_pPlayer->AddSubtitle(subtitle);
460 g_application.m_pPlayer->SetSubtitle(nStream);
461 g_application.m_pPlayer->SetSubtitleVisible(true);
462 CMediaSettings::Get().GetCurrentVideoSettings().m_SubtitleDelay = 0.0f;
463 g_application.m_pPlayer->SetSubTitleDelay(0);