File indexing completed on 2024-05-19 04:49:59

0001 /****************************************************************************************
0002  * Copyright (c) 2008 Nikolaj Hald Nielsen <nhn@kde.org>                                *
0003  *                                                                                      *
0004  * This program is free software; you can redistribute it and/or modify it under        *
0005  * the terms of the GNU General Public License as published by the Free Software        *
0006  * Foundation; either version 2 of the License, or (at your option) any later           *
0007  * version.                                                                             *
0008  *                                                                                      *
0009  * This program is distributed in the hope that it will be useful, but WITHOUT ANY      *
0010  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A      *
0011  * PARTICULAR PURPOSE. See the GNU General Public License for more details.             *
0012  *                                                                                      *
0013  * You should have received a copy of the GNU General Public License along with         *
0014  * this program.  If not, see <http://www.gnu.org/licenses/>.                           *
0015  ****************************************************************************************/
0016 
0017 #include "SqlPlaylist.h"
0018 
0019 #include <core/storage/SqlStorage.h>
0020 #include "core/support/Debug.h"
0021 #include "core-impl/storage/StorageManager.h"
0022 #include "core-impl/meta/proxy/MetaProxy.h"
0023 #include "core-impl/meta/stream/Stream.h"
0024 #include "core-impl/meta/timecode/TimecodeMeta.h"
0025 #include "playlistmanager/PlaylistManager.h"
0026 #include "playlistmanager/sql/SqlPlaylistGroup.h"
0027 #include "playlistmanager/sql/SqlUserPlaylistProvider.h"
0028 
0029 #include <typeinfo>
0030 
0031 namespace Playlists {
0032 
0033 SqlPlaylist::SqlPlaylist( const QString & name, const Meta::TrackList
0034         &tracks, SqlPlaylistGroupPtr parent, PlaylistProvider *provider,
0035         const QString &urlId )
0036     : m_dbId( -1 )
0037     , m_parent( parent )
0038     , m_tracks( tracks )
0039     , m_provider( provider)
0040     , m_name( name )
0041     , m_urlId( urlId )
0042     , m_tracksLoaded( true )
0043 {
0044     saveToDb();
0045 }
0046 
0047 SqlPlaylist::SqlPlaylist( const QStringList & resultRow,
0048                                 SqlPlaylistGroupPtr parent,
0049                                 PlaylistProvider *provider )
0050     : m_parent( parent )
0051     , m_provider( provider)
0052     , m_tracksLoaded( false )
0053 {
0054     m_dbId = resultRow[0].toInt();
0055     m_name = resultRow[2];
0056     m_urlId = resultRow[3];
0057 }
0058 
0059 
0060 SqlPlaylist::~SqlPlaylist()
0061 {
0062 }
0063 
0064 QUrl
0065 SqlPlaylist::uidUrl() const
0066 {
0067     return QUrl( QStringLiteral( "amarok-sqlplaylistuid://%1").arg( m_dbId ) );
0068 }
0069 
0070 QStringList
0071 SqlPlaylist::groups()
0072 {
0073     QStringList groups;
0074     if( m_parent && !m_parent->name().isNull() )
0075         groups << m_parent->name();
0076     return groups;
0077 }
0078 
0079 void
0080 SqlPlaylist::setGroups( const QStringList &groups )
0081 {
0082     SqlUserPlaylistProvider *userPlaylistProvider =
0083             dynamic_cast<SqlUserPlaylistProvider *>( m_provider );
0084     if( !userPlaylistProvider )
0085     {
0086         error() << "Provider could not be cast to SqlUserPlaylistProvider";
0087         return;
0088     }
0089 
0090     if( groups.isEmpty() )
0091         m_parent = SqlPlaylistGroupPtr();
0092     else
0093         m_parent = userPlaylistProvider->group( groups.first() );
0094 
0095     saveToDb();
0096 }
0097 
0098 bool
0099 SqlPlaylist::saveToDb( bool tracks )
0100 {
0101     int parentId = -1;
0102     if( m_parent )
0103         parentId = m_parent->id();
0104 
0105     auto sql = StorageManager::instance()->sqlStorage();
0106 
0107     //figure out if we have a urlId and if this id is already in the db, if so, update it instead of creating a new one.
0108     if( !m_urlId.isEmpty() )
0109     {
0110         debug() << "Checking " << m_urlId << " against db";
0111 
0112         //check if urlId exists
0113         QString query = QStringLiteral("SELECT id from playlists WHERE urlid='%1'");
0114         query = query.arg( sql->escape( m_urlId ) );
0115         QStringList result = sql->query( query );
0116 
0117         if( !result.isEmpty() )
0118         {
0119             //set this id to the already existing one
0120             m_dbId =  result.at( 0 ).toInt();
0121             debug() << "Got existing playlist with id " << m_dbId;
0122         }
0123     }
0124 
0125     if( m_dbId != -1 )
0126     {
0127         //update existing
0128         QString query = QStringLiteral("UPDATE playlists SET parent_id=%1, name='%2' WHERE id=%3;");
0129         query = query.arg( QString::number( parentId ),
0130                       sql->escape( m_name ),
0131                       QString::number( m_dbId ) );
0132         StorageManager::instance()->sqlStorage()->query( query );
0133 
0134         if( tracks )
0135         {
0136             //delete existing tracks and insert all
0137             query = QStringLiteral("DELETE FROM playlist_tracks where playlist_id=%1;");
0138             query = query.arg( QString::number( m_dbId ) );
0139             StorageManager::instance()->sqlStorage()->query( query );
0140             saveTracks();
0141         }
0142     }
0143     else
0144     {
0145         //insert new
0146         QString query = "INSERT INTO playlists ( parent_id, name, urlid ) "
0147                         "VALUES ( %1, '%2', '%3' );";
0148         query = query.arg( QString::number( parentId ),
0149                       sql->escape( m_name ),
0150                       sql->escape( m_urlId ) );
0151         m_dbId = StorageManager::instance()->sqlStorage()->insert( query, QStringLiteral("playlists") );
0152         if( tracks )
0153             saveTracks();
0154     }
0155 
0156     //HACK! if this has just been added from the collection scanner, the list is full of "dirty"
0157     //tracks that might not all have been properly trackForUrl'ed, so clear the track list so we
0158     //reload if we ever need them!
0159     if( !m_urlId.isEmpty() )
0160     {
0161         m_tracks.clear();
0162         m_tracksLoaded = false;
0163     }
0164 
0165     //clean the cache
0166     if( m_parent )
0167         m_parent->clear();
0168 
0169     return true;
0170 }
0171 
0172 void
0173 SqlPlaylist::saveTracks()
0174 {
0175     int trackNum = 1;
0176     auto sql = StorageManager::instance()->sqlStorage();
0177 
0178     foreach( Meta::TrackPtr trackPtr, m_tracks )
0179     {
0180         if( trackPtr )
0181         {
0182             // keep this in sync with SqlTrack::updatePlaylistsToDb()!
0183             debug() << "saving track with url " << trackPtr->uidUrl();
0184             QString query = "INSERT INTO playlist_tracks ( playlist_id, track_num, url, title, "
0185                             "album, artist, length, uniqueid ) VALUES ( %1, %2, '%3', '%4', '%5', "
0186                             "'%6', %7, '%8' );";
0187             query = query.arg( QString::number( m_dbId ), QString::number( trackNum ),
0188                         sql->escape( trackPtr->uidUrl() ),
0189                         sql->escape( trackPtr->prettyName() ),
0190                         trackPtr->album() ? sql->escape( trackPtr->album()->prettyName() ) : QLatin1String(""),
0191                         trackPtr->artist()? sql->escape( trackPtr->artist()->prettyName() ) : QLatin1String(""),
0192                         QString::number( trackPtr->length() ),
0193                         sql->escape( trackPtr->uidUrl() ) );
0194             sql->insert( query, QStringLiteral("playlist_tracks") );
0195 
0196             trackNum++;
0197         }
0198     }
0199 }
0200 
0201 int
0202 SqlPlaylist::trackCount() const
0203 {
0204     if( m_tracksLoaded )
0205         return m_tracks.count();
0206     else
0207         return -1;
0208 }
0209 
0210 Meta::TrackList
0211 SqlPlaylist::tracks()
0212 {
0213     return m_tracks;
0214 }
0215 
0216 void
0217 SqlPlaylist::triggerTrackLoad()
0218 {
0219     if( !m_tracksLoaded )
0220         loadTracks();
0221     notifyObserversTracksLoaded();
0222 }
0223 
0224 void
0225 SqlPlaylist::addTrack(const Meta::TrackPtr &track, int position )
0226 {
0227     if( !m_tracksLoaded )
0228         loadTracks();
0229 
0230     if( position < 0 )
0231         position = m_tracks.count();
0232     else
0233         position = qMin( position, m_tracks.count() );
0234     m_tracks.insert( position, track );
0235     saveToDb( true );
0236     notifyObserversTrackAdded( track, position );
0237 }
0238 
0239 void
0240 SqlPlaylist::removeTrack( int position )
0241 {
0242     if( !m_tracksLoaded )
0243         loadTracks();
0244 
0245     if( position < 0 || position >= m_tracks.size() )
0246         return;
0247     Meta::TrackPtr track = m_tracks.takeAt( position );
0248     saveToDb( true );
0249     notifyObserversTrackRemoved( position );
0250 }
0251 
0252 void
0253 SqlPlaylist::loadTracks()
0254 {
0255     QString query = "SELECT playlist_id, track_num, url, title, album, artist, length FROM "
0256                     "playlist_tracks WHERE playlist_id=%1 ORDER BY track_num";
0257     query = query.arg( QString::number( m_dbId ) );
0258 
0259     QStringList result = StorageManager::instance()->sqlStorage()->query( query );
0260 
0261     int resultRows = result.count() / 7;
0262 
0263     for( int i = 0; i < resultRows; i++ )
0264     {
0265         QStringList row = result.mid( i*7, 7 );
0266         QUrl url = QUrl( row[2] );
0267 
0268         MetaProxy::TrackPtr proxyTrack( new MetaProxy::Track( url ) );
0269 
0270         proxyTrack->setTitle( row[3] );
0271         proxyTrack->setAlbum( row[4] );
0272         proxyTrack->setArtist( row[5] );
0273         m_tracks << Meta::TrackPtr( proxyTrack.data() );
0274     }
0275 
0276     m_tracksLoaded = true;
0277 }
0278 
0279 void
0280 SqlPlaylist::setName( const QString &name )
0281 {
0282     m_name = name;
0283     saveToDb( false ); //no need to resave all tracks
0284 }
0285 
0286 void
0287 SqlPlaylist::removeFromDb()
0288 {
0289     QString query = QStringLiteral("DELETE FROM playlist_tracks WHERE playlist_id=%1");
0290     query = query.arg( QString::number( m_dbId ) );
0291     StorageManager::instance()->sqlStorage()->query( query );
0292 
0293     query = QStringLiteral("DELETE FROM playlists WHERE id=%1");
0294     query = query.arg( QString::number( m_dbId ) );
0295     StorageManager::instance()->sqlStorage()->query( query );
0296 }
0297 
0298 } //namespace Playlists