File indexing completed on 2024-05-19 04:50:00

0001 /****************************************************************************************
0002  * Copyright (c) 2008 Nikolaj Hald Nielsen <nhn@kde.org>                                *
0003  * Copyright (c) 2008 Bart Cerneels <bart.cerneels@kde.org>                             *
0004  *                                                                                      *
0005  * This program is free software; you can redistribute it and/or modify it under        *
0006  * the terms of the GNU General Public License as published by the Free Software        *
0007  * Foundation; either version 2 of the License, or (at your option) any later           *
0008  * version.                                                                             *
0009  *                                                                                      *
0010  * This program is distributed in the hope that it will be useful, but WITHOUT ANY      *
0011  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A      *
0012  * PARTICULAR PURPOSE. See the GNU General Public License for more details.             *
0013  *                                                                                      *
0014  * You should have received a copy of the GNU General Public License along with         *
0015  * this program.  If not, see <http://www.gnu.org/licenses/>.                           *
0016  ****************************************************************************************/
0017 
0018 #include "SqlUserPlaylistProvider.h"
0019 
0020 #include "SvgHandler.h"
0021 #include "browsers/playlistbrowser/UserPlaylistModel.h"
0022 #include <core/storage/SqlStorage.h>
0023 #include "core/playlists/PlaylistFormat.h"
0024 #include "core/support/Amarok.h"
0025 #include "core/support/Debug.h"
0026 #include "core-impl/storage/StorageManager.h"
0027 #include "core-impl/playlists/types/file/m3u/M3UPlaylist.h"
0028 #include "core-impl/playlists/types/file/pls/PLSPlaylist.h"
0029 #include "core-impl/playlists/types/file/xspf/XSPFPlaylist.h"
0030 #include "core-impl/playlists/types/file/PlaylistFileSupport.h"
0031 
0032 #include <QAction>
0033 #include <QIcon>
0034 #include <QInputDialog>
0035 #include <QLabel>
0036 #include <QMap>
0037 #include <QUrl>
0038 
0039 #include <KLocalizedString>
0040 #include <KMessageBox>
0041 #include <KConfigGroup>
0042 
0043 static const int USERPLAYLIST_DB_VERSION = 3;
0044 // a database updater has been added in checkTables(). Use that when updating db version
0045 static const QString key(QStringLiteral("AMAROK_USERPLAYLIST"));
0046 
0047 namespace Playlists {
0048 
0049 SqlUserPlaylistProvider::SqlUserPlaylistProvider( bool debug )
0050     : UserPlaylistProvider()
0051     , m_debug( debug )
0052 {
0053     checkTables();
0054     m_root = Playlists::SqlPlaylistGroupPtr( new Playlists::SqlPlaylistGroup( QString(),
0055             Playlists::SqlPlaylistGroupPtr(), this ) );
0056 }
0057 
0058 SqlUserPlaylistProvider::~SqlUserPlaylistProvider()
0059 {
0060 }
0061 
0062 int
0063 SqlUserPlaylistProvider::playlistCount() const
0064 {
0065     return m_root->childSqlPlaylists().count();
0066 }
0067 
0068 Playlists::PlaylistList
0069 SqlUserPlaylistProvider::playlists()
0070 {
0071     Playlists::PlaylistList playlists;
0072     foreach( Playlists::SqlPlaylistPtr sqlPlaylist, m_root->allChildPlaylists() )
0073     {
0074         playlists << Playlists::PlaylistPtr::staticCast( sqlPlaylist );
0075     }
0076     return playlists;
0077 }
0078 
0079 void
0080 SqlUserPlaylistProvider::renamePlaylist(PlaylistPtr playlist, const QString &newName )
0081 {
0082     playlist->setName( newName.trimmed() );
0083 }
0084 
0085 bool
0086 SqlUserPlaylistProvider::isWritable()
0087 {
0088     return true;
0089 }
0090 
0091 bool
0092 SqlUserPlaylistProvider::deletePlaylists( const Playlists::PlaylistList &playlistList )
0093 {
0094     Playlists::SqlPlaylistList sqlPlaylists;
0095     foreach( Playlists::PlaylistPtr playlist, playlistList )
0096     {
0097         Playlists::SqlPlaylistPtr sqlPlaylist =
0098             Playlists::SqlPlaylistPtr::dynamicCast( playlist );
0099         if( !sqlPlaylist.isNull() )
0100             sqlPlaylists << sqlPlaylist;
0101     }
0102     return deleteSqlPlaylists( sqlPlaylists );
0103 }
0104 
0105 bool
0106 SqlUserPlaylistProvider::deleteSqlPlaylists( Playlists::SqlPlaylistList playlistList )
0107 {
0108     //this delete is not confirmed, has to be done by the slot connected to the delete action.
0109     foreach( Playlists::SqlPlaylistPtr sqlPlaylist, playlistList )
0110     {
0111         if( sqlPlaylist )
0112         {
0113             debug() << "deleting " << sqlPlaylist->name();
0114             m_root->m_childPlaylists.removeAll( sqlPlaylist );
0115             Q_EMIT playlistRemoved( Playlists::PlaylistPtr::dynamicCast( sqlPlaylist ) );
0116             sqlPlaylist->removeFromDb();
0117         }
0118     }
0119 
0120     return true;
0121 }
0122 
0123 Playlists::PlaylistPtr
0124 SqlUserPlaylistProvider::save( const Meta::TrackList &tracks )
0125 {
0126     DEBUG_BLOCK
0127     QString name = QLocale().toString( QDateTime::currentDateTime(), QLocale::LongFormat );
0128     return save( tracks, name );
0129 }
0130 
0131 Playlists::PlaylistPtr
0132 SqlUserPlaylistProvider::save( const Meta::TrackList &tracks, const QString& name )
0133 {
0134     DEBUG_BLOCK
0135     debug() << "saving " << tracks.count() << " tracks to db with name" << name;
0136     Playlists::SqlPlaylistPtr sqlPlaylist = Playlists::SqlPlaylistPtr(
0137             new Playlists::SqlPlaylist( name, tracks,
0138                 Playlists::SqlPlaylistGroupPtr(),
0139                 this )
0140             );
0141     m_root->m_childPlaylists << sqlPlaylist;
0142     Playlists::PlaylistPtr playlist( sqlPlaylist.data() );
0143 
0144     Q_EMIT playlistAdded( playlist );
0145     return playlist; // assumes insertion in db was successful!
0146 }
0147 
0148 void
0149 SqlUserPlaylistProvider::reloadFromDb()
0150 {
0151     DEBUG_BLOCK;
0152     m_root->clear();
0153     Q_EMIT updated();
0154 }
0155 
0156 Playlists::SqlPlaylistGroupPtr
0157 SqlUserPlaylistProvider::group( const QString &name )
0158 {
0159     DEBUG_BLOCK
0160     Playlists::SqlPlaylistGroupPtr group;
0161 
0162     if( name.isEmpty() )
0163         return m_root;
0164 
0165     //clear the root first to force a reload.
0166     m_root->clear();
0167 
0168     foreach( const Playlists::SqlPlaylistGroupPtr &group, m_root->allChildGroups() )
0169     {
0170         debug() << group->name();
0171         if( group->name() == name )
0172         {
0173             debug() << "match";
0174             return group;
0175         }
0176     }
0177 
0178     debug() << "Creating a new group " << name;
0179     group = new Playlists::SqlPlaylistGroup( name, m_root, this );
0180     group->save();
0181 
0182     return group;
0183 }
0184 
0185 void
0186 SqlUserPlaylistProvider::createTables()
0187 {
0188     DEBUG_BLOCK
0189 
0190     auto sqlStorage = StorageManager::instance()->sqlStorage();
0191     if( !sqlStorage )
0192     {
0193         debug() << "No SQL Storage available!";
0194         return;
0195     }
0196     sqlStorage->query( QString( "CREATE TABLE playlist_groups ("
0197             " id " + sqlStorage->idType() +
0198             ", parent_id INTEGER"
0199             ", name " + sqlStorage->textColumnType() +
0200             ", description " + sqlStorage->textColumnType() + " ) ENGINE = MyISAM;" ) );
0201     sqlStorage->query( QStringLiteral("CREATE INDEX parent_podchannel ON playlist_groups( parent_id );") );
0202 
0203 
0204     sqlStorage->query( QString( "CREATE TABLE playlists ("
0205             " id " + sqlStorage->idType() +
0206             ", parent_id INTEGER"
0207             ", name " + sqlStorage->textColumnType() +
0208             ", urlid " + sqlStorage->exactTextColumnType() + " ) ENGINE = MyISAM;" ) );
0209     sqlStorage->query( QStringLiteral("CREATE INDEX parent_playlist ON playlists( parent_id );") );
0210 
0211     sqlStorage->query( QString( "CREATE TABLE playlist_tracks ("
0212             " id " + sqlStorage->idType() +
0213             ", playlist_id INTEGER "
0214             ", track_num INTEGER "
0215             ", url " + sqlStorage->exactTextColumnType() +
0216             ", title " + sqlStorage->textColumnType() +
0217             ", album " + sqlStorage->textColumnType() +
0218             ", artist " + sqlStorage->textColumnType() +
0219             ", length INTEGER "
0220             ", uniqueid " + sqlStorage->textColumnType(128) + ") ENGINE = MyISAM;" ) );
0221 
0222     sqlStorage->query( QStringLiteral("CREATE INDEX parent_playlist_tracks ON playlist_tracks( playlist_id );") );
0223     sqlStorage->query( QStringLiteral("CREATE INDEX playlist_tracks_uniqueid ON playlist_tracks( uniqueid );") );
0224 }
0225 
0226 void
0227 SqlUserPlaylistProvider::deleteTables()
0228 {
0229     DEBUG_BLOCK
0230 
0231     auto sqlStorage = StorageManager::instance()->sqlStorage();
0232 
0233     if( !sqlStorage )
0234     {
0235         debug() << "No SQL Storage available!";
0236         return;
0237     }
0238 
0239     sqlStorage->query( QStringLiteral("DROP INDEX parent_podchannel ON playlist_groups;") );
0240     sqlStorage->query( QStringLiteral("DROP INDEX parent_playlist ON playlists;") );
0241     sqlStorage->query( QStringLiteral("DROP INDEX parent_playlist_tracks ON playlist_tracks;") );
0242     sqlStorage->query( QStringLiteral("DROP INDEX playlist_tracks_uniqueid ON playlist_tracks;") );
0243 
0244     sqlStorage->query( QStringLiteral("DROP TABLE IF EXISTS playlist_groups;") );
0245     sqlStorage->query( QStringLiteral("DROP TABLE IF EXISTS playlists;") );
0246     sqlStorage->query( QStringLiteral("DROP TABLE IF EXISTS playlist_tracks;") );
0247 
0248 }
0249 
0250 void
0251 SqlUserPlaylistProvider::checkTables()
0252 {
0253     DEBUG_BLOCK
0254 
0255     auto sqlStorage = StorageManager::instance()->sqlStorage();
0256     QStringList values;
0257 
0258     //Prevents amarok from crashing on bad DB
0259     if ( !sqlStorage )
0260         return;
0261 
0262     values = sqlStorage->query( QStringLiteral("SELECT version FROM admin WHERE component = '%1';").arg(sqlStorage->escape( key ) ) );
0263     
0264     if( values.isEmpty() )
0265     {
0266         //debug() << "creating Playlist Tables";
0267         createTables();
0268 
0269         sqlStorage->query( "INSERT INTO admin(component,version) "
0270                 "VALUES('" + key + "'," + QString::number( USERPLAYLIST_DB_VERSION ) + ");" );
0271     }
0272     else
0273     {
0274         int dbVersion = values.at( 0 ).toInt();
0275         switch ( dbVersion )
0276         {
0277             case 2:
0278                 upgradeVersion2to3();
0279                 sqlStorage->query( "UPDATE admin SET version = '" + QString::number( USERPLAYLIST_DB_VERSION )  + "' WHERE component = '" + key + "';" );
0280             case 3: // current version
0281                break;
0282             default:
0283                 KMessageBox::error(
0284                     nullptr, // QWidget *parent
0285                     i18n( "Version %1 of playlist database schema encountered, however this "
0286                         "Amarok version only supports version %2 (and previous versions "
0287                         "starting with %2). Playlists saved in the Amarok Database probably "
0288                         "will not work and any write operations with them may result in losing "
0289                         "them. Perhaps you have started an older version of Amarok with a "
0290                         "database written by newer version?", dbVersion, USERPLAYLIST_DB_VERSION ),
0291                     i18nc( "the user's 'database version' is newer and unsupported by this software version",
0292                            "Future version of Playlist Database?" ) );
0293          }
0294      }
0295  }
0296 
0297 void
0298 SqlUserPlaylistProvider::upgradeVersion2to3()
0299 {
0300     DEBUG_BLOCK
0301     auto sqlStorage = StorageManager::instance()->sqlStorage();
0302     sqlStorage->query( QStringLiteral("ALTER TABLE playlists DROP COLUMN description") );
0303 }
0304 
0305 Playlists::SqlPlaylistList
0306 SqlUserPlaylistProvider::toSqlPlaylists( Playlists::PlaylistList playlists )
0307 {
0308     Playlists::SqlPlaylistList sqlPlaylists;
0309     foreach( Playlists::PlaylistPtr playlist, playlists )
0310     {
0311         Playlists::SqlPlaylistPtr sqlPlaylist =
0312             Playlists::SqlPlaylistPtr::dynamicCast( playlist );
0313         if( !sqlPlaylist.isNull() )
0314             sqlPlaylists << sqlPlaylist;
0315     }
0316     return sqlPlaylists;
0317 }
0318 
0319 } //namespace Playlists
0320