rendered paste bodyIndex: xbmc/lib/libscrobbler/scrobbler.h===================================================================--- xbmc/lib/libscrobbler/scrobbler.h (revision 16881)+++ xbmc/lib/libscrobbler/scrobbler.h (working copy)@@ -20,6 +20,9 @@ #ifndef _SCROBBLER_H #define _SCROBBLER_H +#include <vector>+using namespace std;+ namespace MUSIC_INFO { class CMusicInfoTag;@@ -59,6 +62,24 @@ }; +typedef struct SubmissionJournalEntry_s {+ CStdString strArtist;+ CStdString strAlbum;+ CStdString strTitle;+ CStdString strLength;+ CStdString strStartTime;+ CStdString strMusicBrainzID;+ SubmissionJournalEntry_s() {};+ SubmissionJournalEntry_s(const struct SubmissionJournalEntry_s& j) {+ strArtist = j.strArtist;+ strAlbum = j.strAlbum;+ strTitle = j.strTitle;+ strLength = j.strLength;+ strStartTime = j.strStartTime;+ strMusicBrainzID = j.strMusicBrainzID;+ };+} SubmissionJournalEntry_t;+ /** Audioscrobbler client class. $Id$@@ -134,13 +155,6 @@ */ void SetUsername(const CStdString& strUser); - /**- Clears the internal submission cache.-- @see setCache()- */- void ClearCache();- void SetSubmitSong(bool bSubmit); bool ShouldSubmit(); void SetSongStartTime();@@ -150,19 +164,6 @@ CStdString GetFilesCached(); void SetSecsTillSubmit(int iSecs); CStdString GetSubmitState();-protected:-- /**- Sets the contents of the submission cache, to be called from - loadCache().-- @param cache The contents of the cache to set.- @param numentries The number of entries in the cache.- @see loadCache()- @see clearCache()- */- void SetCache(const CStdString& strCache, int iNumEntries);- private: /**@@ -194,23 +195,21 @@ /** Override this to save the cache - called from the destructor. - @param cache The contents of the cache to save.- @param numentries The number of entries in the cache - save this too.- @see loadCache()+ @see LoadJournal() @retval 1 You saved some cache. @retval 0 You didn't save cache, or can't. */- virtual int SaveCache(const CStdString& strCache, int iNumEntries);+ virtual int SaveJournal(); /** This is called when you should load the cache. Use setCache. - @see saveCache()- @see setCache()+ @see SaveJournal() @retval 1 You loaded some cache. @retval 0 There was no cache to load, or you can't. */ virtual int LoadCache();+ virtual int LoadJournal(); virtual void GenSessionKey(); @@ -230,6 +229,7 @@ #endif CStdString GetTempFileName();+ CStdString GetJournalFileName(); CStdString m_strUserName; CStdString m_strPassword; // MD5 hash@@ -265,8 +265,9 @@ int m_iSecsTillSubmit; bool m_bShouldSubmit; bool m_bReHandShaking;+ + vector<SubmissionJournalEntry_t> m_vecSubmissionJournal; - static CScrobbler* m_pInstance; }; Index: xbmc/lib/libscrobbler/scrobbler.cpp===================================================================--- xbmc/lib/libscrobbler/scrobbler.cpp (revision 16881)+++ xbmc/lib/libscrobbler/scrobbler.cpp (working copy)@@ -29,6 +29,9 @@ #include "Application.h" #include "MusicInfoTag.h" #include "FileSystem/File.h"+#include "tinyXML/tinyxml.h"+#include "XMLUtils.h"+#include "RegExp.h" using namespace std; using namespace MUSIC_INFO;@@ -38,6 +41,7 @@ #define MINLENGTH 30 #define MAXLENGTH 10800 #define HS_FAIL_WAIT 100000+#define MAXSUBMISSIONS 50 //#define CLIENT_ID "tst" #define CLIENT_ID "xbm"@@ -118,15 +122,14 @@ SetPassword(strPassword); SetUsername(strUserName); - ClearCache();- LoadCache();+ LoadJournal(); DoHandshake(); } void CScrobbler::Term() { m_bReadyToSubmit=false;- SaveCache(m_strPostString.c_str(), m_iSongNum);+ SaveJournal(); } void CScrobbler::SetPassword(const CStdString& strPass)@@ -162,18 +165,6 @@ m_strHsString = "http://post.audioscrobbler.com/?hs=true&p=1.1&c=" + m_strClientId + "&v=" + m_strClientVer + "&u=" + m_strUserName; } -void CScrobbler::ClearCache()-{- m_iSongNum = 0;- m_strPostString = "";-}--void CScrobbler::SetCache(const CStdString& strCache, int iNumEntries)-{- m_strPostString = strCache;- m_iSongNum = iNumEntries;-}- int CScrobbler::AddSong(const CMusicInfoTag& tag) { if ((!g_guiSettings.GetBool("lastfm.enable") && !g_guiSettings.GetBool("lastfm.recordtoprofile")) || !g_guiSettings.GetBool("network.enableinternet"))@@ -195,39 +186,71 @@ struct tm *today = gmtime(&m_SongStartTime); strftime(ti, sizeof(ti), "%Y-%m-%d %H:%M:%S", today); - CStdString a, b, t;+ SubmissionJournalEntry_t entry; // our tags are stored as UTF-8, so no conversion needed- a = tag.GetArtist();- b = tag.GetAlbum();- t = tag.GetTitle();- CStdString i=ti;- CStdString m=tag.GetMusicBrainzTrackID();- CUtil::URLEncode(a);- CUtil::URLEncode(b);- CUtil::URLEncode(t);- CUtil::URLEncode(i);- CUtil::URLEncode(m);+ entry.strArtist = tag.GetArtist();+ entry.strAlbum = tag.GetAlbum();+ entry.strTitle = tag.GetTitle();+ entry.strStartTime = ti;+ entry.strLength.Format("%d",tag.GetDuration());+ entry.strMusicBrainzID = tag.GetMusicBrainzTrackID();+ m_vecSubmissionJournal.push_back(entry);+ SaveJournal(); - CStdString strSubmitStr;- strSubmitStr.Format("a[%i]=%s&t[%i]=%s&b[%i]=%s&m[%i]=%s&l[%i]=%i&i[%i]=%s&", m_iSongNum, a.c_str(), m_iSongNum, t.c_str(), m_iSongNum, b.c_str(), m_iSongNum, m.c_str(), m_iSongNum, tag.GetDuration(), m_iSongNum, i.c_str());-- if(m_strPostString.find(ti) != m_strPostString.npos)+ while ((unsigned)m_iSongNum < m_vecSubmissionJournal.size() && m_iSongNum < MAXSUBMISSIONS) {- // we have already tried to add a song at this time stamp- // I have no idea how this could happen but apparently it does so- // we stop it now+ entry = m_vecSubmissionJournal[m_iSongNum];+ CStdString strStartTime = entry.strStartTime;+ CUtil::URLEncode(entry.strArtist); + CUtil::URLEncode(entry.strTitle);+ CUtil::URLEncode(entry.strAlbum);+ CUtil::URLEncode(entry.strMusicBrainzID);+ CUtil::URLEncode(entry.strLength);+ CUtil::URLEncode(entry.strStartTime);+ CStdString strSubmitStr;+ strSubmitStr.Format("a[%i]=%s&t[%i]=%s&b[%i]=%s&m[%i]=%s&l[%i]=%s&i[%i]=%s&", + m_iSongNum, + entry.strArtist, + m_iSongNum, + entry.strTitle,+ m_iSongNum,+ entry.strAlbum,+ m_iSongNum, + entry.strMusicBrainzID,+ m_iSongNum, + entry.strLength,+ m_iSongNum, + entry.strStartTime); - StatusUpdate(S_NOT_SUBMITTING,strSubmitStr);- StatusUpdate(S_NOT_SUBMITTING,m_strPostString);+ if(m_strPostString.find(strStartTime) != m_strPostString.npos)+ {+ // we have already tried to add a song at this time stamp+ // I have no idea how this could happen but apparently it does so+ // we stop it now - StatusUpdate(S_NOT_SUBMITTING,"Submission error, duplicate subbmission time found");- return 3;- }+ StatusUpdate(S_NOT_SUBMITTING,strSubmitStr);+ StatusUpdate(S_NOT_SUBMITTING,m_strPostString);+ StatusUpdate(S_NOT_SUBMITTING,"Submission error, duplicate subbmission time found");+ + vector<SubmissionJournalEntry_t>::iterator it = m_vecSubmissionJournal.begin();+ for (int i = 0; i <m_iSongNum; i++, it++)+ ;+ m_vecSubmissionJournal.erase(it);+ SaveJournal(); - m_strPostString += strSubmitStr;- m_iSongNum++;+ return 3;+ } - SaveCache(m_strPostString.c_str(), m_iSongNum);+ // FIXME For some reason CStdString won't grow past 1036 chars...+ if(m_strPostString.length() + strSubmitStr.length() > 975) {+ printf("%d %s\n", m_strPostString.length(), m_strPostString.c_str());+ printf("%d %s\n", strSubmitStr.length(), strSubmitStr.c_str());+ break;+ }+ + m_strPostString += strSubmitStr;+ m_iSongNum++;+ } time_t now; time (&now);@@ -349,10 +372,8 @@ if (stricmp("OK", response) == 0) { StatusUpdate(S_SUBMIT_SUCCESS,"Submission succeeded.");- StatusUpdate(S_DEBUG,m_strPostString.c_str()); - ClearCache(); char *inttext = strtok(NULL, seps); if (inttext && (stricmp("INTERVAL", inttext) == 0)) {@@ -361,6 +382,14 @@ strBuf.Format("Submit interval set to %i seconds.", m_Interval); StatusUpdate(S_SUBMIT_INTERVAL, strBuf); }+ + // Remove successfully submitted songs from journal and clear POST data+ printf("before: %d %d\n", m_iSongNum, m_vecSubmissionJournal.size());+ while (--m_iSongNum)+ m_vecSubmissionJournal.erase(m_vecSubmissionJournal.begin());+ printf("after: %d %d\n", m_iSongNum, m_vecSubmissionJournal.size());+ m_strPostString = "";+ SaveJournal(); } else if (stricmp("BADPASS", response) == 0) {@@ -432,43 +461,117 @@ int CScrobbler::LoadCache() {- StatusUpdate(S_SUBMITTING,"load cache");-- int iNumEntries=0;- CStdString strCache;- CFile file; if (file.Open(GetTempFileName())) { CArchive ar(&file, CArchive::load);- ar >> iNumEntries;- ar >> strCache;+ ar >> m_iSongNum;+ ar >> m_strPostString; ar.Close(); file.Close();- SetCache(strCache, iNumEntries); return 1; } return 0; } -int CScrobbler::SaveCache(const CStdString& strCache, int iNumEntries)-{- if (iNumEntries<=0)+int CScrobbler::LoadJournal() {+ SubmissionJournalEntry_t entry;+ m_vecSubmissionJournal.clear();+ + if (LoadCache()) + {+ // load old style cache+ CLog::Log(LOGDEBUG, "Audioscrobbler: Found old submission cache, converting to submission journal.");+ for (int i = 0; i < m_iSongNum; i++)+ {+ CStdString strRegEx;+ strRegEx.Format("a\\[%i\\]=(.*?)&t\\[%i\\]=(.*?)&b\\[%i\\]=(.*?)&m\\[%i\\]=(.*?)&l\\[%i\\]=(.*?)&i\\[%i\\]=(.*?)&", i, i, i, i, i, i);+ CRegExp re;+ if (!re.RegComp(strRegEx.c_str()))+ break;+ if (re.RegFind(m_strPostString) < 0) {+ continue;+ }+ entry.strArtist = re.GetReplaceString("\\1");+ entry.strTitle = re.GetReplaceString("\\2");+ entry.strAlbum = re.GetReplaceString("\\3");+ entry.strMusicBrainzID = re.GetReplaceString("\\4");+ entry.strLength = re.GetReplaceString("\\5");+ entry.strStartTime = re.GetReplaceString("\\6");+ CUtil::UrlDecode(entry.strArtist);+ CUtil::UrlDecode(entry.strTitle);+ CUtil::UrlDecode(entry.strAlbum);+ CUtil::UrlDecode(entry.strMusicBrainzID);+ CUtil::UrlDecode(entry.strLength);+ CUtil::UrlDecode(entry.strStartTime);+ m_vecSubmissionJournal.push_back(entry);+ }+ m_iSongNum = 0;+ m_strPostString = "";+ ::DeleteFile(GetTempFileName());+ CLog::Log(LOGDEBUG, "Audioscrobbler: Added %d entries from old cache file (%s) to journal.", m_vecSubmissionJournal.size(), GetTempFileName().c_str());+ }++ TiXmlDocument xmlDoc;+ CStdString JournalFileName = GetJournalFileName();+ if (!xmlDoc.LoadFile(JournalFileName))+ {+ CLog::Log(LOGDEBUG, "Audioscrobbler: %s, Line %d (%s)", JournalFileName.c_str(), xmlDoc.ErrorRow(), xmlDoc.ErrorDesc()); return 0;+ } - CFile file;- if (file.OpenForWrite(GetTempFileName(), true, true)) // overwrite always+ TiXmlElement *pRoot = xmlDoc.RootElement();+ if (strcmpi(pRoot->Value(), "asjournal") != 0) {- CArchive ar(&file, CArchive::store);- ar << iNumEntries;- ar << strCache;- ar.Close();- file.Close();- return 1;+ CLog::Log(LOGDEBUG, "Audioscrobbler: %s missing <asjournal>", JournalFileName.c_str());+ return 0; }- return 0;++ TiXmlNode *pNode = pRoot->FirstChild("entry");+ while (pNode)+ {+ XMLUtils::GetString(pNode, "artist", entry.strArtist);+ XMLUtils::GetString(pNode, "album", entry.strAlbum);+ XMLUtils::GetString(pNode, "title", entry.strTitle);+ XMLUtils::GetString(pNode, "length", entry.strLength);+ XMLUtils::GetString(pNode, "starttime", entry.strStartTime);+ XMLUtils::GetString(pNode, "musicbrainzid", entry.strMusicBrainzID);+ m_vecSubmissionJournal.push_back(entry);+ pNode = pNode->NextSibling("entry");+ }++ CLog::Log(LOGDEBUG, "Audioscrobbler: Journal loaded with %d entries.", m_vecSubmissionJournal.size());+ return (m_vecSubmissionJournal.size() > 0) ? 1 : 0; } +int CScrobbler::SaveJournal()+{+ TiXmlDocument xmlDoc;+ TiXmlDeclaration decl("1.0", "utf-8", "yes");+ xmlDoc.InsertEndChild(decl);+ TiXmlElement xmlRootElement("asjournal");+ TiXmlNode *pRoot = xmlDoc.InsertEndChild(xmlRootElement);+ if (!pRoot)+ return 0;+ vector<SubmissionJournalEntry_t>::iterator it = m_vecSubmissionJournal.begin();+ for (; it != m_vecSubmissionJournal.end(); it++)+ {+ TiXmlElement entryNode("entry");+ TiXmlNode *pNode = pRoot->InsertEndChild(entryNode);+ if (!pNode)+ return 0;+ XMLUtils::SetString(pNode, "artist", it->strArtist);+ XMLUtils::SetString(pNode, "album", it->strAlbum);+ XMLUtils::SetString(pNode, "title", it->strTitle);+ XMLUtils::SetString(pNode, "length", it->strLength);+ XMLUtils::SetString(pNode, "starttime", it->strStartTime);+ XMLUtils::SetString(pNode, "musicbrainzid", it->strMusicBrainzID);+ }++ CStdString FileName = GetJournalFileName();+ return (xmlDoc.SaveFile(FileName)) ? 1 : 0;+}+ void CScrobbler::StatusUpdate(ScrobbleStatus status, const CStdString& strText) { switch (status)@@ -541,8 +644,6 @@ if (m_bCloseThread) break; - StatusUpdate(S_DEBUG,"...");- CHTTP http; CStdString strHtml; bool bSuccess;@@ -566,7 +667,6 @@ LPSTR lphtml=strHtml.GetBuffer(); HandleSubmit(lphtml); strHtml.ReleaseBuffer();- ::DeleteFile(GetTempFileName()); if (m_bReHandShaking) continue; } }@@ -579,7 +679,6 @@ StatusUpdate(S_CONNECT_ERROR,"Could not connect to server."); } - // OK, if this was a handshake, it failed since m_bReadyToSubmit isn't true. Submissions get cached. while (!m_bReadyToSubmit) {@@ -634,7 +733,7 @@ return strCachedFiles; CStdString strFormat=g_localizeStrings.Get(15210); // Cached %i Songs- strCachedFiles.Format(strFormat.c_str(), m_iSongNum);+ strCachedFiles.Format(strFormat.c_str(), m_vecSubmissionJournal.size()); return strCachedFiles; }@@ -683,3 +782,10 @@ CStdString strFileName = g_settings.GetProfileUserDataFolder(); return CUtil::AddFileToFolder(strFileName, "Scrobbler.tmp"); }++CStdString CScrobbler::GetJournalFileName()+{+ CStdString strFileName = g_settings.GetProfileUserDataFolder();+ return CUtil::AddFileToFolder(strFileName, "Scrobbler.xml");+}+