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 }