/thirdparty/liblastfm2/src/scrobble/ScrobbleCache.cpp

http://github.com/tomahawk-player/tomahawk · C++ · 163 lines · 106 code · 32 blank · 25 comment · 15 complexity · e093ec2aa30991ee3ae431c9cfde5e0e MD5 · raw file

  1. /*
  2. Copyright 2009 Last.fm Ltd.
  3. - Primarily authored by Max Howell, Jono Cole and Doug Mansell
  4. This file is part of liblastfm.
  5. liblastfm 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 3 of the License, or
  8. (at your option) any later version.
  9. liblastfm is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with liblastfm. If not, see <http://www.gnu.org/licenses/>.
  15. */
  16. #include "ScrobbleCache.h"
  17. #include "ScrobblePoint.h"
  18. #include <lastfm/misc.h>
  19. #include <QCoreApplication>
  20. #include <QFile>
  21. #include <QDomElement>
  22. #include <QDomDocument>
  23. #if LASTFM_VERSION >= 0x00010000
  24. using lastfm::ScrobbleCache;
  25. #endif
  26. ScrobbleCache::ScrobbleCache( const QString& username )
  27. {
  28. Q_ASSERT( username.length() );
  29. m_path = lastfm::dir::runtimeData().filePath( username + "_subs_cache.xml" );
  30. m_username = username;
  31. QDomDocument xml;
  32. read( xml );
  33. }
  34. bool
  35. ScrobbleCache::isValid( const Track& track, Invalidity* v )
  36. {
  37. #define TEST( test, x ) \
  38. if (test) { \
  39. if (v) *v = x; \
  40. return false; \
  41. }
  42. TEST( track.duration() < ScrobblePoint::kScrobbleMinLength, TooShort );
  43. TEST( !track.timestamp().isValid(), NoTimestamp );
  44. // actual spam prevention is something like 12 hours, but we are only
  45. // trying to weed out obviously bad data, server side criteria for
  46. // "the future" may change, so we should let the server decide, not us
  47. TEST( track.timestamp() > QDateTime::currentDateTime().addMonths( 1 ), FromTheFuture );
  48. TEST( track.timestamp() < QDateTime::fromString( "2003-01-01", Qt::ISODate ), FromTheDistantPast );
  49. // Check if any required fields are empty
  50. TEST( track.artist().isNull(), ArtistNameMissing );
  51. TEST( track.title().isEmpty(), TrackNameMissing );
  52. TEST( (QStringList() << "unknown artist"
  53. << "unknown"
  54. << "[unknown]"
  55. << "[unknown artist]").contains( track.artist().name().toLower() ),
  56. ArtistInvalid );
  57. return true;
  58. }
  59. void
  60. ScrobbleCache::read( QDomDocument& xml )
  61. {
  62. m_tracks.clear();
  63. QFile file( m_path );
  64. file.open( QFile::Text | QFile::ReadOnly );
  65. QTextStream stream( &file );
  66. stream.setCodec( "UTF-8" );
  67. xml.setContent( stream.readAll() );
  68. for (QDomNode n = xml.documentElement().firstChild(); !n.isNull(); n = n.nextSibling())
  69. if (n.nodeName() == "track")
  70. m_tracks += Track( n.toElement() );
  71. }
  72. void
  73. ScrobbleCache::write()
  74. {
  75. if (m_tracks.isEmpty())
  76. {
  77. QFile::remove( m_path );
  78. }
  79. else {
  80. QDomDocument xml;
  81. QDomElement e = xml.createElement( "submissions" );
  82. e.setAttribute( "product", QCoreApplication::applicationName() );
  83. e.setAttribute( "version", "2" );
  84. foreach (Track i, m_tracks)
  85. e.appendChild( i.toDomElement( xml ) );
  86. xml.appendChild( e );
  87. QFile file( m_path );
  88. file.open( QIODevice::WriteOnly | QIODevice::Text );
  89. QTextStream stream( &file );
  90. stream.setCodec( "UTF-8" );
  91. stream << "<?xml version='1.0' encoding='utf-8'?>\n";
  92. stream << xml.toString( 2 );
  93. file.close();
  94. }
  95. }
  96. void
  97. ScrobbleCache::add( const QList<Track>& tracks )
  98. {
  99. foreach (const Track& track, tracks)
  100. {
  101. ScrobbleCache::Invalidity invalidity;
  102. if ( !isValid( track, &invalidity ) )
  103. {
  104. qWarning() << invalidity;
  105. }
  106. else if (track.isNull())
  107. qDebug() << "Will not cache an empty track";
  108. else
  109. m_tracks += track;
  110. }
  111. write();
  112. }
  113. int
  114. ScrobbleCache::remove( const QList<Track>& toremove )
  115. {
  116. QMutableListIterator<Track> i( m_tracks );
  117. while (i.hasNext()) {
  118. Track t = i.next();
  119. for (int x = 0; x < toremove.count(); ++x)
  120. if (toremove[x] == t)
  121. i.remove();
  122. }
  123. write();
  124. // yes we return # remaining, rather # removed, but this is an internal
  125. // function and the behaviour is documented so it's alright imo --mxcl
  126. return m_tracks.count();
  127. }