mirror of
https://bitbucket.org/smil3y/kde-extraapps.git
synced 2025-02-24 02:42:52 +00:00
389 lines
12 KiB
C++
389 lines
12 KiB
C++
/**
|
|
* Copyright (C) 2002-2004 Scott Wheeler <wheeler@kde.org>
|
|
* Copyright (C) 2008, 2013 Michael Pyne <mpyne@kde.org>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it under
|
|
* the terms of the GNU General Public License as published by the Free Software
|
|
* Foundation; either version 2 of the License, or (at your option) any later
|
|
* version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
|
* PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along with
|
|
* this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "cache.h"
|
|
#include "juk-exception.h"
|
|
|
|
#include <kstandarddirs.h>
|
|
#include <ksavefile.h>
|
|
#include <kmessagebox.h>
|
|
#include <kconfig.h>
|
|
#include <klocale.h>
|
|
#include <kdebug.h>
|
|
#include <ktoggleaction.h>
|
|
|
|
#include <QDir>
|
|
#include <QBuffer>
|
|
|
|
#include "tag.h"
|
|
#include "searchplaylist.h"
|
|
#include "historyplaylist.h"
|
|
#include "upcomingplaylist.h"
|
|
#include "folderplaylist.h"
|
|
#include "playlistcollection.h"
|
|
#include "actioncollection.h"
|
|
|
|
using namespace ActionCollection;
|
|
|
|
const int Cache::playlistListCacheVersion = 3;
|
|
const int Cache::playlistItemsCacheVersion = 2;
|
|
|
|
enum PlaylistType
|
|
{
|
|
Normal = 0,
|
|
Search = 1,
|
|
History = 2,
|
|
Upcoming = 3,
|
|
Folder = 4
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// public methods
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
Cache *Cache::instance()
|
|
{
|
|
static Cache cache;
|
|
return &cache;
|
|
}
|
|
|
|
void Cache::loadPlaylists(PlaylistCollection *collection) // static
|
|
{
|
|
QString playlistsFile = KGlobal::dirs()->saveLocation("appdata") + "playlists";
|
|
|
|
QFile f(playlistsFile);
|
|
|
|
if(!f.open(QIODevice::ReadOnly))
|
|
return;
|
|
|
|
QDataStream fs(&f);
|
|
int dataStreamVersion = QDataStream::Qt_3_3;
|
|
|
|
qint32 version;
|
|
fs >> version;
|
|
kDebug() << "Playlists file is version" << version;
|
|
|
|
switch(version) {
|
|
case 3:
|
|
dataStreamVersion = QDataStream::Qt_4_3;
|
|
// Fall-through
|
|
|
|
case 1:
|
|
case 2:
|
|
{
|
|
// Our checksum is only for the values after the version and checksum so
|
|
// we want to get a byte array with just the checksummed data.
|
|
|
|
QByteArray data;
|
|
quint16 checksum;
|
|
fs >> checksum >> data;
|
|
|
|
if(checksum != qChecksum(data.data(), data.size()))
|
|
return;
|
|
|
|
// If we chose the wrong QDataStream version we may have to back out
|
|
// and try again, so get ready by noting the playlists we have made.
|
|
|
|
QList<Playlist *> createdPlaylists;
|
|
bool errorOccurred = true;
|
|
|
|
while(errorOccurred) {
|
|
// Create a new stream just based on the data.
|
|
|
|
QDataStream s(&data, QIODevice::ReadOnly);
|
|
s.setVersion(dataStreamVersion);
|
|
|
|
try { // Failure due to wrong version can be indicated by an exception
|
|
|
|
while(!s.atEnd()) {
|
|
|
|
qint32 playlistType;
|
|
s >> playlistType;
|
|
|
|
Playlist *playlist = 0;
|
|
|
|
switch(playlistType) {
|
|
case Search:
|
|
{
|
|
SearchPlaylist *p = new SearchPlaylist(collection);
|
|
createdPlaylists.append(p);
|
|
s >> *p;
|
|
playlist = p;
|
|
break;
|
|
}
|
|
case History:
|
|
{
|
|
action<KToggleAction>("showHistory")->setChecked(true);
|
|
collection->setHistoryPlaylistEnabled(true);
|
|
s >> *collection->historyPlaylist();
|
|
playlist = collection->historyPlaylist();
|
|
break;
|
|
}
|
|
case Upcoming:
|
|
{
|
|
/*
|
|
collection->setUpcomingPlaylistEnabled(true);
|
|
Playlist *p = collection->upcomingPlaylist();
|
|
action<KToggleAction>("saveUpcomingTracks")->setChecked(true);
|
|
s >> *p;
|
|
playlist = p;
|
|
*/
|
|
break;
|
|
}
|
|
case Folder:
|
|
{
|
|
FolderPlaylist *p = new FolderPlaylist(collection);
|
|
createdPlaylists.append(p);
|
|
s >> *p;
|
|
playlist = p;
|
|
break;
|
|
}
|
|
default:
|
|
Playlist *p = new Playlist(collection, true);
|
|
createdPlaylists.append(p);
|
|
s >> *p;
|
|
|
|
// We may have already read this playlist from the folder
|
|
// scanner, if an .m3u playlist
|
|
if(collection->containsPlaylistFile(p->fileName())) {
|
|
delete p;
|
|
p = 0;
|
|
}
|
|
|
|
playlist = p;
|
|
break;
|
|
} // switch
|
|
|
|
if(version >= 2) {
|
|
qint32 sortColumn;
|
|
s >> sortColumn;
|
|
if(playlist)
|
|
playlist->setSorting(sortColumn);
|
|
}
|
|
|
|
} // while !s.atEnd()
|
|
|
|
// Must be ok if we got this far, break out of loop.
|
|
errorOccurred = false;
|
|
|
|
} // try
|
|
catch(BICStreamException &) {
|
|
kError() << "Exception loading playlists - binary incompatible stream.";
|
|
|
|
// Delete created playlists which probably have junk now.
|
|
foreach(Playlist *p, createdPlaylists)
|
|
delete p;
|
|
createdPlaylists.clear();
|
|
|
|
if(dataStreamVersion == QDataStream::Qt_3_3) {
|
|
kError() << "Attempting other binary protocol - Qt 4.3";
|
|
dataStreamVersion = QDataStream::Qt_4_3;
|
|
|
|
break; // escape from while(!s.atEnd()) to try again
|
|
}
|
|
#if QT_VERSION >= 0x040400
|
|
// Unlikely, but maybe user had Qt 4.4 with KDE 4.0.0?
|
|
else if(dataStreamVersion == QDataStream::Qt_4_3) {
|
|
kError() << "Attempting other binary protocol - Qt 4.4";
|
|
dataStreamVersion = QDataStream::Qt_4_4;
|
|
|
|
break;
|
|
}
|
|
#endif
|
|
// We tried 3.3 first, if 4.3/4.4 doesn't work who knows...
|
|
kError() << "Unable to recover, no playlists will be loaded.";
|
|
return;
|
|
} // catch
|
|
} // while dataStreamVersion != -1
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
// Because the original version of the playlist cache did not contain a
|
|
// version number, we want to revert to the beginning of the file before
|
|
// reading the data.
|
|
|
|
f.reset();
|
|
|
|
while(!fs.atEnd()) {
|
|
Playlist *p = new Playlist(collection);
|
|
fs >> *p;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
f.close();
|
|
}
|
|
|
|
void Cache::savePlaylists(const PlaylistList &playlists)
|
|
{
|
|
QString dirName = KGlobal::dirs()->saveLocation("appdata");
|
|
QString playlistsFile = dirName + "playlists";
|
|
KSaveFile f(playlistsFile);
|
|
|
|
if(!f.open(QIODevice::WriteOnly)) {
|
|
kError() << "Error saving collection:" << f.errorString();
|
|
return;
|
|
}
|
|
|
|
QByteArray data;
|
|
QDataStream s(&data, QIODevice::WriteOnly);
|
|
s.setVersion(QDataStream::Qt_4_3);
|
|
|
|
for(PlaylistList::ConstIterator it = playlists.begin(); it != playlists.end(); ++it) {
|
|
if(*it) {
|
|
if(dynamic_cast<HistoryPlaylist *>(*it)) {
|
|
s << qint32(History)
|
|
<< *static_cast<HistoryPlaylist *>(*it);
|
|
}
|
|
else if(dynamic_cast<SearchPlaylist *>(*it)) {
|
|
s << qint32(Search)
|
|
<< *static_cast<SearchPlaylist *>(*it);
|
|
}
|
|
else if(dynamic_cast<UpcomingPlaylist *>(*it)) {
|
|
if(!action<KToggleAction>("saveUpcomingTracks")->isChecked())
|
|
continue;
|
|
s << qint32(Upcoming)
|
|
<< *static_cast<UpcomingPlaylist *>(*it);
|
|
}
|
|
else if(dynamic_cast<FolderPlaylist *>(*it)) {
|
|
s << qint32(Folder)
|
|
<< *static_cast<FolderPlaylist *>(*it);
|
|
}
|
|
else {
|
|
s << qint32(Normal)
|
|
<< *(*it);
|
|
}
|
|
s << qint32((*it)->sortColumn());
|
|
}
|
|
}
|
|
|
|
QDataStream fs(&f);
|
|
fs << qint32(playlistListCacheVersion);
|
|
fs << qChecksum(data.data(), data.size());
|
|
|
|
fs << data;
|
|
f.close();
|
|
|
|
if(!f.finalize())
|
|
kError() << "Error saving collection:" << f.errorString();
|
|
}
|
|
|
|
bool Cache::cacheFileExists() // static
|
|
{
|
|
return QFile::exists(KGlobal::dirs()->saveLocation("appdata") + "cache");
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// private methods
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
Cache::Cache()
|
|
{
|
|
|
|
}
|
|
|
|
bool Cache::prepareToLoadCachedItems()
|
|
{
|
|
QString cacheFileName = KGlobal::dirs()->saveLocation("appdata") + "cache";
|
|
|
|
m_loadFile.setFileName(cacheFileName);
|
|
if(!m_loadFile.open(QIODevice::ReadOnly))
|
|
return false;
|
|
|
|
m_loadDataStream.setDevice(&m_loadFile);
|
|
|
|
int dataStreamVersion = CacheDataStream::Qt_3_3;
|
|
|
|
qint32 version;
|
|
m_loadDataStream >> version;
|
|
|
|
switch(version) {
|
|
case 2:
|
|
dataStreamVersion = CacheDataStream::Qt_4_3;
|
|
|
|
// Other than that we're compatible with cache v1, so fallthrough
|
|
// to setCacheVersion
|
|
|
|
case 1: {
|
|
m_loadDataStream.setCacheVersion(1);
|
|
m_loadDataStream.setVersion(dataStreamVersion);
|
|
|
|
qint32 checksum;
|
|
m_loadDataStream >> checksum
|
|
>> m_loadFileBuffer.buffer();
|
|
|
|
m_loadFileBuffer.open(QIODevice::ReadOnly);
|
|
m_loadDataStream.setDevice(&m_loadFileBuffer);
|
|
|
|
qint32 checksumExpected = qChecksum(
|
|
m_loadFileBuffer.data(), m_loadFileBuffer.size());
|
|
if(m_loadDataStream.status() != CacheDataStream::Ok ||
|
|
checksum != checksumExpected)
|
|
{
|
|
kError() << "Music cache checksum expected to get" << checksumExpected <<
|
|
"actually was" << checksum;
|
|
KMessageBox::sorry(0, i18n("The music data cache has been corrupted. JuK "
|
|
"needs to rescan it now. This may take some time."));
|
|
return false;
|
|
}
|
|
|
|
break;
|
|
}
|
|
default: {
|
|
m_loadDataStream.device()->reset();
|
|
m_loadDataStream.setCacheVersion(0);
|
|
|
|
// This cache is so old that this is just a wild guess here that 3.3
|
|
// is compatible.
|
|
m_loadDataStream.setVersion(CacheDataStream::Qt_3_3);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
FileHandle Cache::loadNextCachedItem()
|
|
{
|
|
if(!m_loadFile.isOpen() || !m_loadDataStream.device()) {
|
|
kWarning() << "Already completed reading cache file.";
|
|
return FileHandle::null();
|
|
}
|
|
|
|
if(m_loadDataStream.status() == QDataStream::ReadCorruptData) {
|
|
kError() << "Attempted to read file handle from corrupt cache file.";
|
|
return FileHandle::null();
|
|
}
|
|
|
|
if(!m_loadDataStream.atEnd()) {
|
|
QString fileName;
|
|
m_loadDataStream >> fileName;
|
|
fileName.squeeze();
|
|
|
|
return FileHandle(fileName, m_loadDataStream);
|
|
}
|
|
else {
|
|
m_loadDataStream.setDevice(0);
|
|
m_loadFile.close();
|
|
|
|
return FileHandle::null();
|
|
}
|
|
}
|
|
|
|
// vim: set et sw=4 tw=0 sta:
|