PageRenderTime 56ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/xbmc/music/LastFmManager.cpp

https://github.com/weitao2012/android-1
C++ | 1013 lines | 858 code | 100 blank | 55 comment | 122 complexity | 9d458f433a727b833d4b521738b4455d MD5 | raw file
Possible License(s): GPL-2.0, AGPL-1.0
  1. /*
  2. * Copyright (C) 2005-2008 Team XBMC
  3. * http://www.xbmc.org
  4. *
  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)
  8. * any later version.
  9. *
  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.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with XBMC; see the file COPYING. If not, write to
  17. * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
  18. * http://www.gnu.org/copyleft/gpl.html
  19. *
  20. */
  21. #include "threads/SystemClock.h"
  22. #include "LastFmManager.h"
  23. #include "Album.h"
  24. #include "Artist.h"
  25. #include "Application.h"
  26. #include "ApplicationMessenger.h"
  27. #include "PlayListPlayer.h"
  28. #include "playlists/PlayListFactory.h"
  29. #include "utils/md5.h"
  30. #include "filesystem/File.h"
  31. #include "filesystem/CurlFile.h"
  32. #include "GUIInfoManager.h"
  33. #include "MusicDatabase.h"
  34. #include "music/tags/MusicInfoTag.h"
  35. #include "URL.h"
  36. #include "guilib/GUIWindowManager.h"
  37. #include "dialogs/GUIDialogKaiToast.h"
  38. #include "dialogs/GUIDialogProgress.h"
  39. #include "dialogs/GUIDialogYesNo.h"
  40. #include "dialogs/GUIDialogOK.h"
  41. #include "settings/GUISettings.h"
  42. #include "GUIUserMessages.h"
  43. #include "playlists/PlayList.h"
  44. #include "utils/Crc32.h"
  45. #include "settings/AdvancedSettings.h"
  46. #include "settings/Settings.h"
  47. #include "utils/StringUtils.h"
  48. #include "guilib/LocalizeStrings.h"
  49. #include "utils/XBMCTinyXML.h"
  50. #include "utils/TimeUtils.h"
  51. #include "threads/SingleLock.h"
  52. #include "utils/log.h"
  53. #include "utils/URIUtils.h"
  54. #include <sstream>
  55. using namespace std;
  56. using namespace MUSIC_INFO;
  57. using namespace PLAYLIST;
  58. using namespace XFILE;
  59. #define XBMC_LASTFM_VERSION "0.2"
  60. #ifdef _DEBUG
  61. #define XBMC_LASTFM_ID "tst"
  62. #else
  63. #define XBMC_LASTFM_ID "xbmc"
  64. #endif
  65. #define XBMC_LASTFM_MINTRACKS 3
  66. CLastFmManager* CLastFmManager::m_pInstance=NULL;
  67. CLastFmManager::CLastFmManager() : CThread("CLastFmManager")
  68. {
  69. m_RadioTrackQueue = new CPlayList;
  70. }
  71. CLastFmManager::~CLastFmManager()
  72. {
  73. StopRadio(true);
  74. StopThread();
  75. CLog::Log(LOGINFO,"lastfm destroyed");
  76. delete m_RadioTrackQueue;
  77. }
  78. void CLastFmManager::RemoveInstance()
  79. {
  80. if (m_pInstance)
  81. {
  82. delete m_pInstance;
  83. m_pInstance=NULL;
  84. }
  85. }
  86. CLastFmManager* CLastFmManager::GetInstance()
  87. {
  88. if (!m_pInstance)
  89. m_pInstance=new CLastFmManager;
  90. return m_pInstance;
  91. }
  92. void CLastFmManager::Parameter(const CStdString& key, const CStdString& data, CStdString& value)
  93. {
  94. value = "";
  95. vector<CStdString> params;
  96. StringUtils::SplitString(data, "\n", params);
  97. for (int i = 0; i < (int)params.size(); i++)
  98. {
  99. CStdString tmp = params[i];
  100. if (int pos = tmp.Find(key) >= 0)
  101. {
  102. tmp.Delete(pos - 1, key.GetLength() + 1);
  103. value = tmp;
  104. break;
  105. }
  106. }
  107. CLog::Log(LOGDEBUG, "Parameter %s -> %s", key.c_str(), value.c_str());
  108. }
  109. bool CLastFmManager::RadioHandShake()
  110. {
  111. if (!m_RadioSession.IsEmpty()) return true; //already signed in
  112. if (dlgProgress)
  113. {
  114. dlgProgress->SetLine(2, 15251);//Connecting to Last.fm..
  115. dlgProgress->Progress();
  116. }
  117. m_RadioSession = "";
  118. CCurlFile http;
  119. CStdString html;
  120. CStdString strPassword = g_guiSettings.GetString("scrobbler.lastfmpass");
  121. CStdString strUserName = g_guiSettings.GetString("scrobbler.lastfmusername");
  122. if (strUserName.IsEmpty() || strPassword.IsEmpty())
  123. {
  124. CLog::Log(LOGERROR, "Last.fm stream selected but no username or password set.");
  125. return false;
  126. }
  127. CStdString passwordmd5(strPassword);
  128. passwordmd5.ToLower();
  129. CStdString url;
  130. CURL::Encode(strUserName);
  131. url.Format("http://ws.audioscrobbler.com/radio/handshake.php?version=%s&platform=%s&username=%s&passwordmd5=%s&debug=%i&partner=%s", XBMC_LASTFM_VERSION, XBMC_LASTFM_ID, strUserName, passwordmd5, 0, "");
  132. if (!http.Get(url, html))
  133. {
  134. CLog::Log(LOGERROR, "Connect to Last.fm radio failed.");
  135. return false;
  136. }
  137. //CLog::Log(LOGDEBUG, "Handshake: %s", html.c_str());
  138. Parameter("session", html, m_RadioSession);
  139. Parameter("base_url", html, m_RadioBaseUrl);
  140. Parameter("base_path", html, m_RadioBasePath);
  141. Parameter("subscriber", html, m_RadioSubscriber);
  142. Parameter("banned", html, m_RadioBanned);
  143. if (m_RadioSession.CompareNoCase("failed") == 0)
  144. {
  145. CLog::Log(LOGERROR, "Last.fm return failed response, possible bad username or password?");
  146. m_RadioSession = "";
  147. }
  148. return !m_RadioSession.IsEmpty();
  149. }
  150. void CLastFmManager::InitProgressDialog(const CStdString& strUrl)
  151. {
  152. if (m_RadioSession.IsEmpty())
  153. {
  154. dlgProgress = (CGUIDialogProgress*)g_windowManager.GetWindow(WINDOW_DIALOG_PROGRESS);
  155. if (dlgProgress)
  156. {
  157. dlgProgress->SetHeading(15200);
  158. dlgProgress->SetLine(0, 259);
  159. CStdString strUrlDec = strUrl;
  160. CURL::Decode(strUrlDec);
  161. dlgProgress->SetLine(1, strUrlDec);
  162. dlgProgress->SetLine(2, "");
  163. if (!dlgProgress->IsDialogRunning())
  164. dlgProgress->StartModal();
  165. }
  166. }
  167. }
  168. void CLastFmManager::UpdateProgressDialog(const int iStringID)
  169. {
  170. if (dlgProgress)
  171. {
  172. dlgProgress->SetLine(2, iStringID);
  173. dlgProgress->Progress();
  174. }
  175. }
  176. void CLastFmManager::CloseProgressDialog()
  177. {
  178. if (dlgProgress)
  179. {
  180. dlgProgress->Close();
  181. dlgProgress = NULL;
  182. }
  183. }
  184. bool CLastFmManager::ChangeStation(const CURL& stationUrl)
  185. {
  186. unsigned int start = XbmcThreads::SystemClockMillis();
  187. InitProgressDialog(stationUrl.Get());
  188. StopRadio(false);
  189. if (!RadioHandShake())
  190. {
  191. CloseProgressDialog();
  192. CGUIDialogOK::ShowAndGetInput(15200, 15206, 0, 0);
  193. return false;
  194. }
  195. UpdateProgressDialog(15252); // Selecting station...
  196. CCurlFile http;
  197. CStdString url;
  198. CStdString html;
  199. url.Format("http://" + m_RadioBaseUrl + m_RadioBasePath + "/adjust.php?session=%s&url=%s&debug=%i", m_RadioSession, stationUrl.Get().c_str(), 0);
  200. if (!http.Get(url, html))
  201. {
  202. CLog::Log(LOGERROR, "Connect to Last.fm to change station failed.");
  203. CloseProgressDialog();
  204. return false;
  205. }
  206. //CLog::Log(LOGDEBUG, "ChangeStation: %s", html.c_str());
  207. CStdString strErrorCode;
  208. Parameter("error", html, strErrorCode);
  209. if (strErrorCode != "")
  210. {
  211. CLog::Log(LOGERROR, "Last.fm returned an error (%s) response for change station request.", strErrorCode.c_str());
  212. CloseProgressDialog();
  213. return false;
  214. }
  215. UpdateProgressDialog(261); //Waiting for start....
  216. g_playlistPlayer.ClearPlaylist(PLAYLIST_MUSIC);
  217. RequestRadioTracks();
  218. CacheTrackThumb(XBMC_LASTFM_MINTRACKS);
  219. AddToPlaylist(XBMC_LASTFM_MINTRACKS);
  220. Create(); //start thread
  221. m_hWorkerEvent.Set(); //kickstart the thread
  222. CSingleLock lock(m_lockPlaylist);
  223. CPlayList& playlist = g_playlistPlayer.GetPlaylist(PLAYLIST_MUSIC);
  224. if ((int)playlist.size())
  225. {
  226. g_application.m_strPlayListFile = stationUrl.Get(); //needed to highlight the playing item
  227. g_playlistPlayer.SetCurrentPlaylist(PLAYLIST_MUSIC);
  228. g_playlistPlayer.Play(0);
  229. CLog::Log(LOGDEBUG, "%s: Done (time: %i ms)", __FUNCTION__, (int)(XbmcThreads::SystemClockMillis() - start));
  230. CloseProgressDialog();
  231. return true;
  232. }
  233. CloseProgressDialog();
  234. return false;
  235. }
  236. bool CLastFmManager::RequestRadioTracks()
  237. {
  238. unsigned int start = XbmcThreads::SystemClockMillis();
  239. CStdString url;
  240. CStdString html;
  241. url.Format("http://" + m_RadioBaseUrl + m_RadioBasePath + "/xspf.php?sk=%s&discovery=0&desktop=", m_RadioSession);
  242. {
  243. CCurlFile http;
  244. if (!http.Get(url, html))
  245. {
  246. m_RadioSession.empty();
  247. CLog::Log(LOGERROR, "LastFmManager: Connect to Last.fm to request tracks failed.");
  248. return false;
  249. }
  250. }
  251. //CLog::Log(LOGDEBUG, "RequestRadioTracks: %s", html.c_str());
  252. //parse playlist
  253. CXBMCTinyXML xmlDoc;
  254. xmlDoc.Parse(html);
  255. if (xmlDoc.Error())
  256. {
  257. m_RadioSession.empty();
  258. CLog::Log(LOGERROR, "LastFmManager: Unable to parse tracklist Error: %s", xmlDoc.ErrorDesc());
  259. return false;
  260. }
  261. TiXmlElement* pRootElement = xmlDoc.RootElement();
  262. if (!pRootElement )
  263. {
  264. CLog::Log(LOGWARNING, "LastFmManager: No more tracks received");
  265. m_RadioSession.empty();
  266. return false;
  267. }
  268. TiXmlElement* pBodyElement = pRootElement->FirstChildElement("trackList");
  269. if (!pBodyElement )
  270. {
  271. CLog::Log(LOGWARNING, "LastFmManager: No more tracks received, no tracklist");
  272. m_RadioSession.empty();
  273. return false;
  274. }
  275. TiXmlElement* pTrackElement = pBodyElement->FirstChildElement("track");
  276. if (!pTrackElement)
  277. {
  278. CLog::Log(LOGWARNING, "LastFmManager: No more tracks received, empty tracklist");
  279. m_RadioSession.empty();
  280. return false;
  281. }
  282. while (pTrackElement)
  283. {
  284. CFileItemPtr newItem(new CFileItem);
  285. TiXmlElement* pElement = pTrackElement->FirstChildElement("location");
  286. if (pElement)
  287. {
  288. TiXmlNode* child = pElement->FirstChild();
  289. if (child)
  290. {
  291. CStdString url = child->Value();
  292. url.Replace("http:", "lastfm:");
  293. newItem->SetPath(url);
  294. }
  295. }
  296. pElement = pTrackElement->FirstChildElement("title");
  297. if (pElement)
  298. {
  299. TiXmlNode* child = pElement->FirstChild();
  300. if (child)
  301. {
  302. newItem->SetLabel(child->Value());
  303. newItem->GetMusicInfoTag()->SetTitle(child->Value());
  304. }
  305. }
  306. pElement = pTrackElement->FirstChildElement("creator");
  307. if (pElement)
  308. {
  309. TiXmlNode* child = pElement->FirstChild();
  310. if (child)
  311. {
  312. newItem->GetMusicInfoTag()->SetArtist(child->Value());
  313. }
  314. }
  315. pElement = pTrackElement->FirstChildElement("album");
  316. if (pElement)
  317. {
  318. TiXmlNode* child = pElement->FirstChild();
  319. if (child)
  320. {
  321. newItem->GetMusicInfoTag()->SetAlbum(child->Value());
  322. }
  323. }
  324. pElement = pTrackElement->FirstChildElement("duration");
  325. if (pElement)
  326. {
  327. TiXmlNode* child = pElement->FirstChild();
  328. if (child)
  329. {
  330. int iDuration = atoi(child->Value())/1000;
  331. newItem->GetMusicInfoTag()->SetDuration(iDuration);
  332. }
  333. }
  334. newItem->FillInDefaultIcon();
  335. pElement = pTrackElement->FirstChildElement("image");
  336. if (pElement)
  337. {
  338. TiXmlNode* child = pElement->FirstChild();
  339. if (child)
  340. {
  341. CStdString coverUrl = child->Value();
  342. if ((coverUrl != "") && (coverUrl.Find("noimage") == -1) && (coverUrl.Right(1) != "/"))
  343. {
  344. newItem->SetThumbnailImage(coverUrl);
  345. }
  346. }
  347. }
  348. //trackauth is needed for validating the track when scrobbling
  349. pElement = pTrackElement->FirstChildElement("lastfm:trackauth");
  350. if (pElement)
  351. {
  352. TiXmlNode* child = pElement->FirstChild();
  353. if (child)
  354. {
  355. CStdString trackAuth = child->Value();
  356. //abuse comment field for the track authcode
  357. newItem->GetMusicInfoTag()->SetComment(trackAuth);
  358. }
  359. }
  360. {
  361. CSingleLock lock(m_lockCache);
  362. m_RadioTrackQueue->Add(newItem);
  363. }
  364. pTrackElement = pTrackElement->NextSiblingElement();
  365. }
  366. //end parse
  367. CSingleLock lock(m_lockCache);
  368. int iNrCachedTracks = m_RadioTrackQueue->size();
  369. CLog::Log(LOGDEBUG, "%s: Done (time: %i ms)", __FUNCTION__, (int)(XbmcThreads::SystemClockMillis() - start));
  370. return iNrCachedTracks > 0;
  371. }
  372. void CLastFmManager::CacheTrackThumb(const int nrInitialTracksToAdd)
  373. {
  374. unsigned int start = XbmcThreads::SystemClockMillis();
  375. CSingleLock lock(m_lockCache);
  376. int iNrCachedTracks = m_RadioTrackQueue->size();
  377. CCurlFile http;
  378. for (int i = 0; i < nrInitialTracksToAdd && i < iNrCachedTracks; i++)
  379. {
  380. CFileItemPtr item = (*m_RadioTrackQueue)[i];
  381. if (!item->GetMusicInfoTag()->Loaded())
  382. {
  383. if (!item->HasThumbnail())
  384. {
  385. item->SetThumbnailImage("DefaultAlbumCover.png");
  386. }
  387. item->GetMusicInfoTag()->SetLoaded();
  388. }
  389. }
  390. CLog::Log(LOGDEBUG, "%s: Done (time: %i ms)", __FUNCTION__, (int)(XbmcThreads::SystemClockMillis() - start));
  391. }
  392. void CLastFmManager::AddToPlaylist(const int nrTracks)
  393. {
  394. CPlayList& playlist = g_playlistPlayer.GetPlaylist(PLAYLIST_MUSIC);
  395. for (int i = 0; i < nrTracks; i++)
  396. {
  397. int iNrCachedTracks = m_RadioTrackQueue->size();
  398. if (iNrCachedTracks > 0)
  399. {
  400. CFileItemPtr item = (*m_RadioTrackQueue)[0];
  401. if (item->GetMusicInfoTag()->Loaded())
  402. {
  403. CMusicDatabase database;
  404. database.Open();
  405. database.SetPropertiesForFileItem(*item);
  406. CSingleLock lock(m_lockCache);
  407. m_RadioTrackQueue->Remove(0);
  408. CSingleLock lock2(m_lockPlaylist);
  409. playlist.Add(item);
  410. }
  411. else
  412. {
  413. break;
  414. }
  415. }
  416. }
  417. }
  418. void CLastFmManager::OnSongChange(CFileItem& newSong)
  419. {
  420. if (IsRadioEnabled())
  421. {
  422. if (!newSong.IsLastFM())
  423. {
  424. StopRadio(true);
  425. }
  426. else
  427. {
  428. unsigned int start = XbmcThreads::SystemClockMillis();
  429. ReapSongs();
  430. MovePlaying();
  431. Update();
  432. SendUpdateMessage();
  433. CLog::Log(LOGDEBUG, "%s: Done (time: %i ms)", __FUNCTION__, (int)(XbmcThreads::SystemClockMillis() - start));
  434. }
  435. }
  436. m_CurrentSong.IsLoved = false;
  437. m_CurrentSong.IsBanned = false;
  438. m_CurrentSong.CurrentSong = &newSong;
  439. }
  440. void CLastFmManager::Update()
  441. {
  442. int iNrTracks = 0;
  443. {
  444. CSingleLock lock(m_lockPlaylist);
  445. CPlayList& playlist = g_playlistPlayer.GetPlaylist(PLAYLIST_MUSIC);
  446. iNrTracks = playlist.size();
  447. }
  448. if (iNrTracks < XBMC_LASTFM_MINTRACKS)
  449. {
  450. AddToPlaylist(XBMC_LASTFM_MINTRACKS - iNrTracks);
  451. int iNrCachedTracks = 0;
  452. {
  453. CSingleLock lock(m_lockCache);
  454. iNrCachedTracks = m_RadioTrackQueue->size();
  455. }
  456. if (iNrCachedTracks == 0)
  457. {
  458. //get more tracks
  459. if (IsRunning())
  460. {
  461. m_hWorkerEvent.Set();
  462. }
  463. }
  464. }
  465. }
  466. bool CLastFmManager::ReapSongs()
  467. {
  468. CSingleLock lock(m_lockPlaylist);
  469. CPlayList& playlist = g_playlistPlayer.GetPlaylist(PLAYLIST_MUSIC);
  470. // reap any played songs
  471. int iCurrentSong = g_playlistPlayer.GetCurrentSong();
  472. int i=0;
  473. while (i < playlist.size())
  474. {
  475. if (i < iCurrentSong)
  476. {
  477. playlist.Remove(i);
  478. iCurrentSong--;
  479. }
  480. else
  481. i++;
  482. }
  483. g_playlistPlayer.SetCurrentSong(iCurrentSong);
  484. return true;
  485. }
  486. bool CLastFmManager::MovePlaying()
  487. {
  488. CSingleLock lock(m_lockPlaylist);
  489. // move current song to the top if its not there
  490. int iCurrentSong = g_playlistPlayer.GetCurrentSong();
  491. if (iCurrentSong > 0)
  492. {
  493. CLog::Log(LOGINFO,"LastFmManager: Moving currently playing song from %i to 0", iCurrentSong);
  494. CPlayList &playlist = g_playlistPlayer.GetPlaylist(PLAYLIST_MUSIC);
  495. CPlayList playlistTemp;
  496. playlistTemp.Add(playlist[iCurrentSong]);
  497. playlist.Remove(iCurrentSong);
  498. for (int i=0; i<playlist.size(); i++)
  499. playlistTemp.Add(playlist[i]);
  500. playlist.Clear();
  501. for (int i=0; i<playlistTemp.size(); i++)
  502. playlist.Add(playlistTemp[i]);
  503. }
  504. g_playlistPlayer.SetCurrentSong(0);
  505. return true;
  506. }
  507. void CLastFmManager::SendUpdateMessage()
  508. {
  509. CGUIMessage msg(GUI_MSG_PLAYLIST_CHANGED, 0, 0);
  510. g_windowManager.SendThreadMessage(msg);
  511. }
  512. void CLastFmManager::OnStartup()
  513. {
  514. SetPriority( GetNormalPriority() );
  515. }
  516. void CLastFmManager::Process()
  517. {
  518. bool bLastShuffleState = g_playlistPlayer.IsShuffled(PLAYLIST_MUSIC);
  519. PLAYLIST::REPEAT_STATE LastRepeatState = g_playlistPlayer.GetRepeat(PLAYLIST_MUSIC);
  520. g_playlistPlayer.SetShuffle(PLAYLIST_MUSIC, false);
  521. g_playlistPlayer.SetRepeat(PLAYLIST_MUSIC, PLAYLIST::REPEAT_NONE);
  522. while (!m_bStop)
  523. {
  524. AbortableWait(m_hWorkerEvent);
  525. if (m_bStop)
  526. break;
  527. int iNrCachedTracks = m_RadioTrackQueue->size();
  528. if (iNrCachedTracks == 0)
  529. {
  530. RequestRadioTracks();
  531. }
  532. CSingleLock lock(m_lockCache);
  533. iNrCachedTracks = m_RadioTrackQueue->size();
  534. CacheTrackThumb(iNrCachedTracks);
  535. }
  536. g_playlistPlayer.SetShuffle(PLAYLIST_MUSIC, bLastShuffleState);
  537. g_playlistPlayer.SetRepeat(PLAYLIST_MUSIC, LastRepeatState);
  538. CLog::Log(LOGINFO,"LastFM thread terminated");
  539. }
  540. void CLastFmManager::StopRadio(bool bKillSession /*= true*/)
  541. {
  542. if (bKillSession)
  543. {
  544. m_RadioSession = "";
  545. }
  546. if (IsRunning())
  547. {
  548. m_bStop = true;
  549. m_hWorkerEvent.Set();
  550. StopThread();
  551. }
  552. m_CurrentSong.CurrentSong = NULL;
  553. m_RadioTrackQueue->Clear();
  554. {
  555. CSingleLock lock(m_lockPlaylist);
  556. //all last.fm tracks are now invalid, remove them from the playlist
  557. CPlayList &playlist = g_playlistPlayer.GetPlaylist(PLAYLIST_MUSIC);
  558. for (int i = playlist.size() - 1; i >= 0; i--)
  559. {
  560. if (playlist[i]->IsLastFM())
  561. {
  562. playlist.Remove(i);
  563. }
  564. }
  565. }
  566. if (!bKillSession)
  567. {
  568. SendUpdateMessage();
  569. }
  570. }
  571. void CLastFmManager::CreateMD5Hash(const CStdString& bufferToHash, CStdString& hash)
  572. {
  573. hash = XBMC::XBMC_MD5::GetMD5(bufferToHash);
  574. hash.ToLower();
  575. }
  576. /*
  577. <?xml version="1.0" encoding="UTF-8"?>
  578. <methodCall>
  579. <methodName>method</methodName>
  580. <params>
  581. <param><value><string>user</string></value></param>
  582. <param><value><string>challenge</string></value></param>
  583. <param><value><string>auth</string></value></param>
  584. <param><value><string>artist</string></value></param>
  585. <param><value><string>title</string></value></param>
  586. </params>
  587. </methodCall>
  588. */
  589. bool CLastFmManager::CallXmlRpc(const CStdString& action, const CStdString& artist, const CStdString& title)
  590. {
  591. CStdString strUserName = g_guiSettings.GetString("scrobbler.lastfmusername");
  592. CStdString strPassword = g_guiSettings.GetString("scrobbler.lastfmpass");
  593. if (strUserName.IsEmpty() || strPassword.IsEmpty())
  594. {
  595. CLog::Log(LOGERROR, "Last.fm CallXmlRpc no username or password set.");
  596. return false;
  597. }
  598. if (artist.IsEmpty())
  599. {
  600. CLog::Log(LOGERROR, "Last.fm CallXmlRpc no artistname provided.");
  601. return false;
  602. }
  603. if (title.IsEmpty())
  604. {
  605. CLog::Log(LOGERROR, "Last.fm CallXmlRpc no tracktitle provided.");
  606. return false;
  607. }
  608. char ti[20];
  609. time_t rawtime;
  610. time ( &rawtime );
  611. struct tm *now = gmtime(&rawtime);
  612. strftime(ti, sizeof(ti), "%Y-%m-%d %H:%M:%S", now);
  613. CStdString strChallenge = ti;
  614. CStdString strAuth(strPassword);
  615. strAuth.ToLower();
  616. strAuth.append(strChallenge);
  617. CreateMD5Hash(strAuth, strAuth);
  618. //create request xml
  619. CXBMCTinyXML doc;
  620. TiXmlDeclaration * decl = new TiXmlDeclaration( "1.0", "UTF-8", "" );
  621. doc.LinkEndChild( decl );
  622. TiXmlElement * elMethodCall = new TiXmlElement( "methodCall" );
  623. doc.LinkEndChild( elMethodCall );
  624. TiXmlElement * elMethodName = new TiXmlElement( "methodName" );
  625. elMethodCall->LinkEndChild( elMethodName );
  626. TiXmlText * txtAction = new TiXmlText( action );
  627. elMethodName->LinkEndChild( txtAction );
  628. TiXmlElement * elParams = new TiXmlElement( "params" );
  629. elMethodCall->LinkEndChild( elParams );
  630. TiXmlElement * elParam = new TiXmlElement( "param" );
  631. elParams->LinkEndChild( elParam );
  632. TiXmlElement * elValue = new TiXmlElement( "value" );
  633. elParam->LinkEndChild( elValue );
  634. TiXmlElement * elString = new TiXmlElement( "string" );
  635. elValue->LinkEndChild( elString );
  636. TiXmlText * txtParam = new TiXmlText( strUserName );
  637. elString->LinkEndChild( txtParam );
  638. elParam = new TiXmlElement( "param" );
  639. elParams->LinkEndChild( elParam );
  640. elValue = new TiXmlElement( "value" );
  641. elParam->LinkEndChild( elValue );
  642. elString = new TiXmlElement( "string" );
  643. elValue->LinkEndChild( elString );
  644. txtParam = new TiXmlText( strChallenge );
  645. elString->LinkEndChild( txtParam );
  646. elParam = new TiXmlElement( "param" );
  647. elParams->LinkEndChild( elParam );
  648. elValue = new TiXmlElement( "value" );
  649. elParam->LinkEndChild( elValue );
  650. elString = new TiXmlElement( "string" );
  651. elValue->LinkEndChild( elString );
  652. txtParam = new TiXmlText( strAuth );
  653. elString->LinkEndChild( txtParam );
  654. elParam = new TiXmlElement( "param" );
  655. elParams->LinkEndChild( elParam );
  656. elValue = new TiXmlElement( "value" );
  657. elParam->LinkEndChild( elValue );
  658. elString = new TiXmlElement( "string" );
  659. elValue->LinkEndChild( elString );
  660. txtParam = new TiXmlText( artist );
  661. elString->LinkEndChild( txtParam );
  662. elParam = new TiXmlElement( "param" );
  663. elParams->LinkEndChild( elParam );
  664. elValue = new TiXmlElement( "value" );
  665. elParam->LinkEndChild( elValue );
  666. elString = new TiXmlElement( "string" );
  667. elValue->LinkEndChild( elString );
  668. txtParam = new TiXmlText( title );
  669. elString->LinkEndChild( txtParam );
  670. CStdString strBody;
  671. strBody << doc;
  672. CCurlFile http;
  673. CStdString html;
  674. CStdString url = "http://ws.audioscrobbler.com/1.0/rw/xmlrpc.php";
  675. http.SetMimeType("text/xml");
  676. if (!http.Post(url, strBody, html))
  677. {
  678. CLog::Log(LOGERROR, "Last.fm action %s failed.", action.c_str());
  679. return false;
  680. }
  681. if (html.Find("fault") >= 0)
  682. {
  683. CLog::Log(LOGERROR, "Last.fm return failed response: %s", html.c_str());
  684. return false;
  685. }
  686. return true;
  687. }
  688. bool CLastFmManager::Love(bool askConfirmation)
  689. {
  690. if (IsLastFmEnabled() && CanLove())
  691. {
  692. const CMusicInfoTag* infoTag = g_infoManager.GetCurrentSongTag();
  693. if (infoTag)
  694. {
  695. CStdString strTitle = infoTag->GetTitle();
  696. CStdString strArtist = StringUtils::Join(infoTag->GetArtist(), g_advancedSettings.m_musicItemSeparator);
  697. CStdString strInfo;
  698. strInfo.Format("%s - %s", strArtist, strTitle);
  699. if (!askConfirmation || CGUIDialogYesNo::ShowAndGetInput(g_localizeStrings.Get(15200), g_localizeStrings.Get(15287), strInfo, ""))
  700. {
  701. CStdString strMessage;
  702. if (Love(*infoTag))
  703. {
  704. strMessage.Format(g_localizeStrings.Get(15289), strTitle);
  705. CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info, g_localizeStrings.Get(15200), strMessage, 7000, false);
  706. return true;
  707. }
  708. else
  709. {
  710. strMessage.Format(g_localizeStrings.Get(15290), strTitle);
  711. CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Error, g_localizeStrings.Get(15200), strMessage, 7000, false);
  712. return false;
  713. }
  714. }
  715. }
  716. }
  717. return false;
  718. }
  719. bool CLastFmManager::Ban(bool askConfirmation)
  720. {
  721. if (IsLastFmEnabled() && IsRadioEnabled() && CanBan())
  722. {
  723. const CMusicInfoTag* infoTag = g_infoManager.GetCurrentSongTag();
  724. if (infoTag)
  725. {
  726. CStdString strTitle = infoTag->GetTitle();
  727. CStdString strArtist = StringUtils::Join(infoTag->GetArtist(), g_advancedSettings.m_musicItemSeparator);
  728. CStdString strInfo;
  729. strInfo.Format("%s - %s", strArtist, strTitle);
  730. if (!askConfirmation || CGUIDialogYesNo::ShowAndGetInput(g_localizeStrings.Get(15200), g_localizeStrings.Get(15288), strInfo, ""))
  731. {
  732. CStdString strMessage;
  733. if (Ban(*infoTag))
  734. {
  735. strMessage.Format(g_localizeStrings.Get(15291), strTitle);
  736. CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info, g_localizeStrings.Get(15200), strMessage, 7000, false);
  737. return true;
  738. }
  739. else
  740. {
  741. strMessage.Format(g_localizeStrings.Get(15292), strTitle);
  742. CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Warning, g_localizeStrings.Get(15200), strMessage, 7000, false);
  743. return false;
  744. }
  745. }
  746. }
  747. }
  748. return false;
  749. }
  750. bool CLastFmManager::Love(const CMusicInfoTag& musicinfotag)
  751. {
  752. if (!IsLastFmEnabled())
  753. {
  754. CLog::Log(LOGERROR, "LastFmManager Love, lastfm is not enabled.");
  755. return false;
  756. }
  757. CStdString strTitle = musicinfotag.GetTitle();
  758. CStdString strArtist = StringUtils::Join(musicinfotag.GetArtist(), g_advancedSettings.m_musicItemSeparator);
  759. CStdString strFilePath;
  760. if (m_CurrentSong.CurrentSong && !m_CurrentSong.CurrentSong->IsLastFM())
  761. {
  762. //path to update the rating for
  763. strFilePath = m_CurrentSong.CurrentSong->GetPath();
  764. }
  765. if (CallXmlRpc("loveTrack",strArtist, strTitle))
  766. {
  767. m_CurrentSong.IsLoved = true;
  768. //update the rating to 5, we loved it.
  769. CMusicInfoTag newTag(musicinfotag);
  770. newTag.SetRating('5');
  771. g_infoManager.SetCurrentSongTag(newTag);
  772. //try updating the rating in the database if it's a local file.
  773. CMusicDatabase musicdatabase;
  774. if (musicdatabase.Open())
  775. {
  776. CSong song;
  777. //update if the song exists in our database and there is no rating yet.
  778. if (musicdatabase.GetSongByFileName(strFilePath, song) && song.rating == '0')
  779. {
  780. musicdatabase.SetSongRating(strFilePath, '5');
  781. }
  782. musicdatabase.Close();
  783. }
  784. return true;
  785. }
  786. return false;
  787. }
  788. bool CLastFmManager::Ban(const CMusicInfoTag& musicinfotag)
  789. {
  790. if (!IsRadioEnabled())
  791. {
  792. CLog::Log(LOGERROR, "LastFmManager Ban, radio is not active");
  793. return false;
  794. }
  795. if (CallXmlRpc("banTrack", StringUtils::Join(musicinfotag.GetArtist(), g_advancedSettings.m_musicItemSeparator), musicinfotag.GetTitle()))
  796. {
  797. //we banned this track so skip to the next track
  798. CApplicationMessenger::Get().ExecBuiltIn("playercontrol(next)");
  799. m_CurrentSong.IsBanned = true;
  800. return true;
  801. }
  802. return false;
  803. }
  804. bool CLastFmManager::Unlove(const CMusicInfoTag& musicinfotag, bool askConfirmation /*= true*/)
  805. {
  806. if (!IsLastFmEnabled())
  807. {
  808. CLog::Log(LOGERROR, "LastFmManager Unlove, lasfm is not enabled");
  809. return false;
  810. }
  811. CStdString strTitle = musicinfotag.GetTitle();
  812. CStdString strArtist = StringUtils::Join(musicinfotag.GetArtist(), g_advancedSettings.m_musicItemSeparator);
  813. if (strArtist.IsEmpty())
  814. {
  815. CLog::Log(LOGERROR, "Last.fm Unlove no artistname provided.");
  816. return false;
  817. }
  818. if (strTitle.IsEmpty())
  819. {
  820. CLog::Log(LOGERROR, "Last.fm Unlove no tracktitle provided.");
  821. return false;
  822. }
  823. CStdString strInfo;
  824. strInfo.Format("%s - %s", strArtist, strTitle);
  825. if (!askConfirmation || CGUIDialogYesNo::ShowAndGetInput(g_localizeStrings.Get(15200), g_localizeStrings.Get(15297), strInfo, ""))
  826. {
  827. if (CallXmlRpc("unLoveTrack", strArtist, strTitle))
  828. {
  829. //update our local rating, now this is tricky because we only have an artist and title
  830. //and don't know if it was a local or radio song.
  831. //So we're going to try to get it from the database and check if the rating is 5,
  832. //if it is we can assume this was the song we loved before.
  833. CMusicDatabase musicdatabase;
  834. if (musicdatabase.Open())
  835. {
  836. long songid = musicdatabase.GetSongByArtistAndAlbumAndTitle(strArtist, "%", strTitle);
  837. if (songid > 0)
  838. {
  839. CSong song;
  840. musicdatabase.GetSongById(songid, song);
  841. if (song.rating == '5')
  842. {
  843. //reset the rating
  844. musicdatabase.SetSongRating(song.strFileName, '0');
  845. }
  846. }
  847. musicdatabase.Close();
  848. }
  849. return true;
  850. }
  851. }
  852. return false;
  853. }
  854. bool CLastFmManager::Unban(const CMusicInfoTag& musicinfotag, bool askConfirmation /*= true*/)
  855. {
  856. if (!IsLastFmEnabled())
  857. {
  858. CLog::Log(LOGERROR, "LastFmManager Unban, lasfm is not enabled");
  859. return false;
  860. }
  861. CStdString strTitle = musicinfotag.GetTitle();
  862. CStdString strArtist = StringUtils::Join(musicinfotag.GetArtist(), g_advancedSettings.m_musicItemSeparator);
  863. if (strArtist.IsEmpty())
  864. {
  865. CLog::Log(LOGERROR, "Last.fm Unban no artistname provided.");
  866. return false;
  867. }
  868. if (strTitle.IsEmpty())
  869. {
  870. CLog::Log(LOGERROR, "Last.fm Unban no tracktitle provided.");
  871. return false;
  872. }
  873. CStdString strInfo;
  874. strInfo.Format("%s - %s", strArtist, strTitle);
  875. if (!askConfirmation || CGUIDialogYesNo::ShowAndGetInput(g_localizeStrings.Get(15200), g_localizeStrings.Get(15298), strInfo, ""))
  876. {
  877. return CallXmlRpc("unBanTrack", strArtist, strTitle);
  878. }
  879. return false;
  880. }
  881. bool CLastFmManager::IsLastFmEnabled()
  882. {
  883. return (
  884. !g_guiSettings.GetString("scrobbler.lastfmusername").IsEmpty() &&
  885. !g_guiSettings.GetString("scrobbler.lastfmpass").IsEmpty()
  886. );
  887. }
  888. bool CLastFmManager::CanLove()
  889. {
  890. return (
  891. m_CurrentSong.CurrentSong &&
  892. !m_CurrentSong.IsLoved &&
  893. IsLastFmEnabled() &&
  894. (m_CurrentSong.CurrentSong->IsLastFM() ||
  895. (
  896. m_CurrentSong.CurrentSong->HasMusicInfoTag() &&
  897. m_CurrentSong.CurrentSong->GetMusicInfoTag()->Loaded() &&
  898. !m_CurrentSong.CurrentSong->GetMusicInfoTag()->GetArtist().empty() &&
  899. !m_CurrentSong.CurrentSong->GetMusicInfoTag()->GetTitle().IsEmpty()
  900. ))
  901. );
  902. }
  903. bool CLastFmManager::CanBan()
  904. {
  905. return (m_CurrentSong.CurrentSong && !m_CurrentSong.IsBanned && m_CurrentSong.CurrentSong->IsLastFM());
  906. }
  907. bool CLastFmManager::CanScrobble(const CFileItem &fileitem)
  908. {
  909. return (
  910. (!fileitem.IsInternetStream() && g_guiSettings.GetBool("scrobbler.lastfmsubmit")) ||
  911. (fileitem.IsLastFM() && g_guiSettings.GetBool("scrobbler.lastfmsubmitradio"))
  912. );
  913. }