File indexing completed on 2025-01-05 04:26:01

0001 /****************************************************************************************
0002  * Copyright (c) 2012 Matěj Laitl <matej@laitl.cz>                                      *
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 "IpodPlaylist.h"
0018 
0019 #include "IpodCollection.h"
0020 #include "IpodMeta.h"
0021 #include "IpodPlaylistProvider.h"
0022 #include "core/playlists/PlaylistProvider.h"
0023 #include "core/support/Debug.h"
0024 #include "core-impl/collections/support/MemoryMeta.h"
0025 #include "core-impl/playlists/providers/user/UserPlaylistProvider.h"
0026 
0027 #include <gpod/itdb.h>
0028 
0029 
0030 IpodPlaylist::IpodPlaylist( Itdb_Playlist *ipodPlaylist, IpodCollection *collection )
0031     : m_playlist( ipodPlaylist )
0032     , m_coll( collection )
0033     , m_type( Normal )
0034 {
0035     Q_ASSERT( m_playlist && collection );
0036     for( GList *members = m_playlist->members; members; members = members->next )
0037     {
0038         Itdb_Track *itdbTrack = (Itdb_Track *) members->data;
0039         Q_ASSERT( itdbTrack );
0040         Meta::TrackPtr track = IpodMeta::Track::fromIpodTrack( itdbTrack );
0041         Q_ASSERT( track );
0042         track = collection->trackForUidUrl( track->uidUrl() ); // get MemoryMeta proxy track
0043         Q_ASSERT( track );
0044         m_tracks << track;
0045     }
0046 }
0047 
0048 IpodPlaylist::IpodPlaylist( const Meta::TrackList &tracks, const QString &name,
0049                             IpodCollection *collection, Type type )
0050     : m_coll( collection )
0051     , m_type( type )
0052 {
0053     m_playlist = itdb_playlist_new( name.toUtf8(), false /* Smart playlist */ );
0054     Q_ASSERT( m_playlist );
0055 
0056     if( m_type != Normal )
0057     {
0058         m_tracks = tracks;
0059         return; // m_playlist holds just the name in this case
0060     }
0061 
0062     int position = 0;
0063     int finalPosition = 0;
0064     foreach( Meta::TrackPtr track, tracks )
0065     {
0066         if( track->collection() == collection ) // track from associated collection
0067         {
0068             addIpodTrack( track, position );
0069             position++;
0070         }
0071         else
0072             m_tracksToCopy << TrackPosition( track, finalPosition );
0073         finalPosition++;  // yes increment every time, tracks are inserted in order so this is correct
0074     }
0075 
0076     if( !m_tracksToCopy.isEmpty() )
0077         scheduleCopyAndInsert();
0078 }
0079 
0080 IpodPlaylist::~IpodPlaylist()
0081 {
0082     itdb_playlist_free( m_playlist );
0083 }
0084 
0085 QUrl
0086 IpodPlaylist::uidUrl() const
0087 {
0088     // integer reading is atomic, no lock needed
0089     QString collId = m_coll ? m_coll->collectionId() : "removedipodcollection:/";
0090     return QUrl( QStringLiteral( "%1/playlists/%2" ).arg( collId ).arg( m_playlist->id ) );
0091 }
0092 
0093 QString
0094 IpodPlaylist::name() const
0095 {
0096     QReadLocker locker( &m_playlistLock );
0097     return QString::fromUtf8( m_playlist->name );
0098 }
0099 
0100 void
0101 IpodPlaylist::setName( const QString &name )
0102 {
0103     QWriteLocker locker( &m_playlistLock );
0104     g_free( m_playlist->name );
0105     m_playlist->name = g_strdup( name.toUtf8() );
0106 }
0107 
0108 Playlists::PlaylistProvider*
0109 IpodPlaylist::provider() const
0110 {
0111     return m_coll ? m_coll->playlistProvider() : nullptr;
0112 }
0113 
0114 int
0115 IpodPlaylist::trackCount() const
0116 {
0117     return m_tracks.count();
0118 }
0119 
0120 Meta::TrackList
0121 IpodPlaylist::tracks()
0122 {
0123     return m_tracks;
0124 }
0125 
0126 void
0127 IpodPlaylist::addTrack(const Meta::TrackPtr &track, int position )
0128 {
0129     if( m_type != Normal || !m_coll || !m_coll->isWritable() )
0130         return;
0131 
0132     if( position < 0 || position > m_tracks.count() )
0133         position = m_tracks.count();
0134 
0135     if( track->collection() == m_coll.data() ) // track from associated collection
0136         addIpodTrack( track, position );
0137     else
0138     {
0139         m_tracksToCopy << TrackPosition( track, position );
0140         scheduleCopyAndInsert();
0141     }
0142 }
0143 
0144 void
0145 IpodPlaylist::removeTrack( int position )
0146 {
0147     // we should fail only if position is incorrect, prevent infinite loops in
0148     // IpodPlaylistProvider::removeTrackFromPlaylists()
0149     if( position < 0 || position >= m_tracks.count() )
0150         return;
0151 
0152     Meta::TrackPtr removedTrack = m_tracks.takeAt( position );
0153     if( m_type == Stale || m_type == Orphaned )
0154     {
0155         notifyObserversTrackRemoved( position );
0156         return; // do not fire following machinery for special playlists
0157     }
0158 
0159     AmarokSharedPointer<MemoryMeta::Track> proxyTrack = AmarokSharedPointer<MemoryMeta::Track>::dynamicCast( removedTrack );
0160     if( !proxyTrack )
0161     {
0162         error() << __PRETTY_FUNCTION__ << "track" << removedTrack.data() << "from m_track was not MemoryMeta track!";
0163         return;
0164     }
0165 
0166     AmarokSharedPointer<IpodMeta::Track> ipodTrack = AmarokSharedPointer<IpodMeta::Track>::dynamicCast( proxyTrack->originalTrack() );
0167     if( !proxyTrack )
0168     {
0169         error() << __PRETTY_FUNCTION__ << "originalTrack of the proxyTrack was not IpodMeta track!";
0170         return;
0171     }
0172 
0173     {
0174         // notify observers _without_ the lock held
0175         QWriteLocker locker( &m_playlistLock );
0176         itdb_playlist_remove_track( m_playlist, ipodTrack->itdbTrack() );
0177     }
0178     notifyObserversTrackRemoved( position );
0179 }
0180 
0181 Itdb_Playlist*
0182 IpodPlaylist::itdbPlaylist()
0183 {
0184     return m_playlist;
0185 }
0186 
0187 TrackPositionList
0188 IpodPlaylist::takeTracksToCopy()
0189 {
0190     TrackPositionList tracksToCopy = m_tracksToCopy;
0191     m_tracksToCopy.clear();
0192     return tracksToCopy;
0193 }
0194 
0195 void
0196 IpodPlaylist::scheduleCopyAndInsert()
0197 {
0198     Playlists::PlaylistProvider *prov = provider();
0199     if( !prov )
0200         return;  // we can do nothing
0201     static_cast<IpodPlaylistProvider *>( prov )->scheduleCopyAndInsertToPlaylist(
0202         AmarokSharedPointer<IpodPlaylist>( this ) );
0203 }
0204 
0205 void
0206 IpodPlaylist::addIpodTrack( Meta::TrackPtr track, int position )
0207 {
0208     Q_ASSERT( position >= 0 && position <= m_tracks.count() );
0209 
0210     Meta::TrackPtr proxyTrack = Meta::TrackPtr();
0211     AmarokSharedPointer<MemoryMeta::Track> memoryTrack = AmarokSharedPointer<MemoryMeta::Track>::dynamicCast( track );
0212     if( memoryTrack )
0213     {
0214         track = memoryTrack->originalTrack();  // iPod track is usually hidden below MemoryMeta proxy
0215         proxyTrack = track;
0216     }
0217     AmarokSharedPointer<IpodMeta::Track> ipodTrack = AmarokSharedPointer<IpodMeta::Track>::dynamicCast( track );
0218     if( !ipodTrack )
0219     {
0220         error() << __PRETTY_FUNCTION__ << "Could not get IpodMeta::Track out of supplied track."
0221                 << ( memoryTrack ? "(but cast to MemoryMeta::Track succeeded)"
0222                                  : "(cast to MemoryMeta::Track failed too)" );
0223         return;
0224     }
0225 
0226     if( !proxyTrack)  // we got IpodTrack directly, expose its MemoryMeta proxy
0227         proxyTrack = m_coll ? m_coll->trackForUidUrl( ipodTrack->uidUrl() ) : Meta::TrackPtr();
0228     if( !proxyTrack )
0229     {
0230         error() << __PRETTY_FUNCTION__ << "was passed IpodMeta::Track but we could not find"
0231                 << "MemoryMeta::Track proxy for it.";
0232         return;
0233     }
0234 
0235     Itdb_Track *itdbTrack = ipodTrack->itdbTrack();
0236     /* There is following code in libgpod's itdb_playlist_add_track():
0237      *     g_return_if_fail (pl->itdb);
0238      *     track->itdb = pl->itdb;
0239      * Just fool libgpod by setting itdb to assumed value
0240      */
0241     Itdb_iTunesDB *save = m_playlist->itdb;
0242     m_playlist->itdb = itdbTrack->itdb;
0243     itdb_playlist_add_track( m_playlist, itdbTrack, -1 );
0244     m_playlist->itdb = save;
0245 
0246     m_tracks.insert( position, proxyTrack );
0247     notifyObserversTrackAdded( proxyTrack, position );
0248 }