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

0001 /****************************************************************************************
0002  * Copyright (c) 2011 Bart Cerneels <bart.cerneels@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 "PlaylistFile.h"
0018 
0019 #include "core/support/Debug.h"
0020 #include "core-impl/playlists/types/file/PlaylistFileLoaderJob.h"
0021 #include "playlistmanager/file/PlaylistFileProvider.h"
0022 #include "playlistmanager/PlaylistManager.h"
0023 
0024 #include <QUrl>
0025 #include <QDir>
0026 
0027 #include <ThreadWeaver/Queue>
0028 
0029 using namespace Playlists;
0030 
0031 PlaylistFile::PlaylistFile( const QUrl &url, PlaylistProvider *provider )
0032              : Playlist()
0033              , m_provider( provider )
0034              , m_url( url )
0035              , m_tracksLoaded( false )
0036              , m_name( m_url.fileName() )
0037              , m_relativePaths( false )
0038              , m_loadingDone( 0 )
0039 {
0040 }
0041 
0042 void
0043 PlaylistFile::saveLater()
0044 {
0045     PlaylistFileProvider *fileProvider = qobject_cast<PlaylistFileProvider *>( m_provider );
0046     if( !fileProvider )
0047         return;
0048 
0049     fileProvider->saveLater( PlaylistFilePtr( this ) );
0050 }
0051 
0052 void
0053 PlaylistFile::triggerTrackLoad()
0054 {
0055     if( m_tracksLoaded )
0056     {
0057         notifyObserversTracksLoaded();
0058         return;
0059     }
0060     PlaylistFileLoaderJob *worker = new PlaylistFileLoaderJob( PlaylistFilePtr( this ) );
0061     ThreadWeaver::Queue::instance()->enqueue( QSharedPointer<ThreadWeaver::Job>(worker) );
0062     if ( !isLoadingAsync() )
0063         m_loadingDone.acquire(); // after loading is finished worker will release semapore
0064 }
0065 
0066 bool
0067 PlaylistFile::isWritable() const
0068 {
0069     if( m_url.isEmpty() )
0070         return false;
0071 
0072     return QFileInfo( m_url.path() ).isWritable();
0073 }
0074 
0075 int
0076 PlaylistFile::trackCount() const
0077 {
0078     if( m_tracksLoaded )
0079         return m_tracks.count();
0080     else
0081         return -1;
0082 }
0083 
0084 void
0085 PlaylistFile::addTrack( const Meta::TrackPtr &track, int position )
0086 {
0087     if( !track ) // playlists might contain invalid tracks. see BUG: 303056
0088         return;
0089 
0090     int trackPos = position < 0 ? m_tracks.count() : position;
0091     if( trackPos > m_tracks.count() )
0092         trackPos = m_tracks.count();
0093     m_tracks.insert( trackPos, track );
0094     // set in case no track was in the playlist before
0095     m_tracksLoaded = true;
0096 
0097     notifyObserversTrackAdded( track, trackPos );
0098 
0099     if( !m_url.isEmpty() )
0100         saveLater();
0101 }
0102 
0103 void
0104 PlaylistFile::removeTrack( int position )
0105 {
0106     if( position < 0 || position >= m_tracks.count() )
0107         return;
0108 
0109     m_tracks.removeAt( position );
0110 
0111     notifyObserversTrackRemoved( position );
0112 
0113     if( !m_url.isEmpty() )
0114         saveLater();
0115 }
0116 
0117 bool
0118 PlaylistFile::save( bool relative )
0119 {
0120     m_relativePaths = relative;
0121     QMutexLocker locker( &m_saveLock );
0122 
0123     //if the location is a directory append the name of this playlist.
0124     if( m_url.fileName().isNull() )
0125     {    m_url = m_url.adjusted(QUrl::RemoveFilename);
0126          m_url.setPath(m_url.path() + name());
0127     }
0128     QFile file( m_url.path() );
0129 
0130     if( !file.open( QIODevice::WriteOnly ) )
0131     {
0132         warning() << QStringLiteral( "Cannot write playlist (%1)." ).arg( file.fileName() )
0133                   << file.errorString();
0134         return false;
0135     }
0136 
0137     savePlaylist( file );
0138     file.close();
0139     return true;
0140 }
0141 
0142 void
0143 PlaylistFile::setName( const QString &name )
0144 {
0145     //can't save to a new file if we don't know where.
0146     if( !m_url.isEmpty() && !name.isEmpty() )
0147     {
0148         QString exten = QStringLiteral( ".%1" ).arg(extension());
0149         m_url = m_url.adjusted(QUrl::RemoveFilename);
0150         m_url.setPath(m_url.path() + name + ( name.endsWith( exten, Qt::CaseInsensitive ) ? QLatin1String("") : exten ));
0151     }
0152 }
0153 
0154 void
0155 PlaylistFile::addProxyTrack( const Meta::TrackPtr &proxyTrack )
0156 {
0157     m_tracks << proxyTrack;
0158     notifyObserversTrackAdded( m_tracks.last(), m_tracks.size() - 1 );
0159 }
0160 
0161 QUrl
0162 PlaylistFile::getAbsolutePath( const QUrl &url )
0163 {
0164     QUrl absUrl = url;
0165 
0166     if( url.scheme().isEmpty() )
0167         absUrl.setScheme( QStringLiteral( "file" ) );
0168 
0169     if( !absUrl.isLocalFile() )
0170         return url;
0171 
0172     if( !url.path().startsWith( QLatin1Char('/') ) )
0173     {
0174         m_relativePaths = true;
0175         // example: url = QUrl( "file://../tunes/tune.ogg" )
0176         absUrl = m_url.adjusted(QUrl::RemoveFilename); // file:///playlists/
0177         absUrl = absUrl.adjusted(QUrl::StripTrailingSlash);
0178         absUrl.setPath( absUrl.path() + QLatin1Char('/') + url.path() );
0179         absUrl.setPath( QDir::cleanPath(absUrl.path()) ); // file:///playlists/tunes/tune.ogg
0180     }
0181     return absUrl;
0182 }
0183 
0184 QString
0185 PlaylistFile::trackLocation( const Meta::TrackPtr &track ) const
0186 {
0187     QUrl path = track->playableUrl();
0188     if( path.isEmpty() )
0189         return track->uidUrl();
0190 
0191     if( !m_relativePaths || m_url.isEmpty() || !path.isLocalFile() || !m_url.isLocalFile() )
0192         return path.toEncoded();
0193 
0194     QDir playlistDir( m_url.adjusted(QUrl::RemoveFilename).path() );
0195     return QUrl::toPercentEncoding( playlistDir.relativeFilePath( path.path() ), "/" );
0196 }