1 /****************************************************************************************
2  * Copyright (c) 2007 Maximilian Kossick <maximilian.kossick@googlemail.com>            *
3  * Copyright (c) 2007 Ian Monroe <ian@monroe.nu>                                        *
4  * Copyright (c) 2008 Mark Kretschmann <kretschmann@kde.org>                            *
5  *                                                                                      *
6  * This program is free software; you can redistribute it and/or modify it under        *
7  * the terms of the GNU General Public License as published by the Free Software        *
8  * Foundation; either version 2 of the License, or (at your option) any later           *
9  * version.                                                                             *
10  *                                                                                      *
11  * This program is distributed in the hope that it will be useful, but WITHOUT ANY      *
12  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A      *
13  * PARTICULAR PURPOSE. See the GNU General Public License for more details.             *
14  *                                                                                      *
15  * You should have received a copy of the GNU General Public License along with         *
16  * this program.  If not, see <http://www.gnu.org/licenses/>.                           *
17  ****************************************************************************************/
18 
19 #include "core/meta/Meta.h"
20 
21 #include "core/collections/Collection.h"
22 #include "core/collections/QueryMaker.h"
23 #include "core/meta/Observer.h"
24 #include "core/meta/Statistics.h"
25 #include "core/meta/TrackEditor.h"
26 #include "core/support/Amarok.h"
27 #include "core/support/Debug.h"
28 
29 #include <QImage>
30 #include <QNetworkConfigurationManager>
31 
32 #include <KLocalizedString>
33 
34 using namespace Meta;
35 
36 //Meta::Track
37 
38 QString
prettyName() const39 Meta::Track::prettyName() const
40 {
41     if( !name().isEmpty() )
42         return name();
43     return prettyUrl();
44 }
45 
46 bool
inCollection() const47 Meta::Track::inCollection() const
48 {
49     return false;
50 }
51 
52 Collections::Collection*
collection() const53 Meta::Track::collection() const
54 {
55     return 0;
56 }
57 
58 Meta::LabelList
labels() const59 Meta::Track::labels() const
60 {
61     return Meta::LabelList();
62 }
63 
64 QString
cachedLyrics() const65 Meta::Track::cachedLyrics() const
66 {
67     return QString();
68 }
69 
70 void
setCachedLyrics(const QString & lyrics)71 Meta::Track::setCachedLyrics( const QString &lyrics )
72 {
73     Q_UNUSED( lyrics )
74 }
75 
76 void
addLabel(const QString & label)77 Meta::Track::addLabel( const QString &label )
78 {
79     Q_UNUSED( label )
80 }
81 
82 void
addLabel(const Meta::LabelPtr & label)83 Meta::Track::addLabel( const Meta::LabelPtr &label )
84 {
85     Q_UNUSED( label )
86 }
87 
88 void
removeLabel(const Meta::LabelPtr & label)89 Meta::Track::removeLabel( const Meta::LabelPtr &label )
90 {
91     Q_UNUSED( label )
92 }
93 
94 QDateTime
createDate() const95 Meta::Track::createDate() const
96 {
97     return QDateTime();
98 }
99 
100 QDateTime
modifyDate() const101 Meta::Track::modifyDate() const
102 {
103     return QDateTime();
104 }
105 
106 qreal
replayGain(Meta::ReplayGainTag mode) const107 Meta::Track::replayGain( Meta::ReplayGainTag mode ) const
108 {
109     Q_UNUSED( mode )
110     return 0.0;
111 }
112 
113 void
prepareToPlay()114 Meta::Track::prepareToPlay()
115 {
116 }
117 
118 void
finishedPlaying(double playedFraction)119 Meta::Track::finishedPlaying( double playedFraction )
120 {
121     qint64 len = length();
122     bool updatePlayCount;
123     if( len <= 30 * 1000 )
124         updatePlayCount = ( playedFraction >= 1.0 );
125     else
126         // at least half the song or at least 5 minutes played
127         updatePlayCount = ( playedFraction >= 0.5 || ( playedFraction * len ) >= 5 * 60 * 1000 );
128 
129     StatisticsPtr stats = statistics();
130     stats->beginUpdate();
131     // we should update score even if updatePlayCount is false to record skips
132     stats->setScore( Amarok::computeScore( stats->score(), stats->playCount(), playedFraction ) );
133     if( updatePlayCount )
134     {
135         stats->setPlayCount( stats->playCount() + 1 );
136         if( !stats->firstPlayed().isValid() )
137             stats->setFirstPlayed( QDateTime::currentDateTime() );
138         stats->setLastPlayed( QDateTime::currentDateTime() );
139     }
140     stats->endUpdate();
141 }
142 
143 void
notifyObservers() const144 Meta::Track::notifyObservers() const
145 {
146     notifyObserversHelper<Track, Observer>( this );
147 }
148 
149 bool
operator ==(const Meta::Track & track) const150 Meta::Track::operator==( const Meta::Track &track ) const
151 {
152     return dynamic_cast<const void*>( this ) == dynamic_cast<const void*>( &track );
153 }
154 
155 bool
lessThan(const Meta::TrackPtr & left,const Meta::TrackPtr & right)156 Meta::Track::lessThan( const Meta::TrackPtr& left, const Meta::TrackPtr& right )
157 {
158     if( !left || !right ) // These should never be 0, but it can apparently happen (https://bugs.kde.org/show_bug.cgi?id=181187)
159         return false;
160 
161     if( left->album() && right->album() )
162         if( left->album()->name() == right->album()->name() )
163         {
164             if( left->discNumber() < right->discNumber() )
165                 return true;
166             else if( left->discNumber() > right->discNumber() )
167                 return false;
168 
169             if( left->trackNumber() < right->trackNumber() )
170                 return true;
171             if( left->trackNumber() > right->trackNumber() )
172                 return false;
173         }
174 
175     if( left->artist() && right->artist() )
176     {
177         int compare = QString::localeAwareCompare( left->artist()->prettyName(), right->artist()->prettyName() );
178         if ( compare < 0 )
179             return true;
180         else if ( compare > 0 )
181             return false;
182     }
183 
184     if( left->album() && right->album() )
185     {
186         int compare = QString::localeAwareCompare( left->album()->prettyName(), right->album()->prettyName() );
187         if ( compare < 0 )
188             return true;
189         else if ( compare > 0 )
190             return false;
191     }
192 
193     return QString::localeAwareCompare( left->prettyName(), right->prettyName() ) < 0;
194 }
195 
196 bool
isPlayable() const197 Track::isPlayable() const
198 {
199     return notPlayableReason().isEmpty();
200 }
201 
202 QString
networkNotPlayableReason() const203 Track::networkNotPlayableReason() const
204 {
205     QNetworkConfigurationManager mgr;
206     if( !mgr.isOnline() )
207         return i18n( "No network connection" );
208 
209     return QString();
210 }
211 
212 QString
localFileNotPlayableReason(const QString & path) const213 Track::localFileNotPlayableReason( const QString &path ) const
214 {
215     QFileInfo trackFileInfo = QFileInfo( path );
216     if( !trackFileInfo.exists() )
217         return i18n( "File does not exist" );
218     if( !trackFileInfo.isFile() )
219         return i18n( "Not a file" );
220     if( !trackFileInfo.isReadable() )
221         return i18n( "No read permissions" );
222     return QString();
223 }
224 
225 TrackEditorPtr
editor()226 Track::editor()
227 {
228     return TrackEditorPtr();
229 }
230 
231 StatisticsPtr
statistics()232 Track::statistics()
233 {
234     // return dummy implementation
235     return StatisticsPtr( new Statistics() );
236 }
237 
238 ConstStatisticsPtr
statistics() const239 Track::statistics() const
240 {
241     StatisticsPtr statistics = const_cast<Track *>( this )->statistics();
242     return ConstStatisticsPtr( statistics.data() );
243 }
244 
245 
246 //Meta::Artist
247 
248 QString
prettyName() const249 Meta::Artist::prettyName() const
250 {
251     if( !name().isEmpty() )
252         return name();
253     return i18n("Unknown Artist");
254 }
255 
256 void
notifyObservers() const257 Meta::Artist::notifyObservers() const
258 {
259     m_sortableName.clear(); // name() may have changed, recompute sortableName next time
260     notifyObserversHelper<Artist, Observer>( this );
261 }
262 
263 bool
operator ==(const Meta::Artist & artist) const264 Meta::Artist::operator==( const Meta::Artist &artist ) const
265 {
266     return dynamic_cast<const void*>( this ) == dynamic_cast<const  void*>( &artist );
267 }
268 
269 QString
sortableName() const270 Meta::Artist::sortableName() const
271 {
272     if( !m_sortableName.isEmpty() )
273         return m_sortableName;
274 
275     const QString &n = name();
276     if( n.startsWith( QLatin1String("the "), Qt::CaseInsensitive ) )
277     {
278         QStringRef article = n.leftRef( 3 );
279         QStringRef subject = n.midRef( 4 );
280         m_sortableName = QStringLiteral( "%1, %2" ).arg( subject.toString(), article.toString() );
281     }
282     else if( n.startsWith( QLatin1String("dj "), Qt::CaseInsensitive ) )
283     {
284         QStringRef article = n.leftRef( 2 );
285         QStringRef subject = n.midRef( 3 );
286         m_sortableName = QStringLiteral( "%1, %2" ).arg( subject.toString(), article.toString() );
287     }
288     else
289         m_sortableName = n;
290     return m_sortableName;
291 }
292 
293 //Meta::Album
294 
295 QString
prettyName() const296 Meta::Album::prettyName() const
297 {
298     if( !name().isEmpty() )
299         return name();
300     return i18n("Unknown Album");
301 }
302 
303 void
notifyObservers() const304 Meta::Album::notifyObservers() const
305 {
306     notifyObserversHelper<Album, Observer>( this );
307 }
308 
309 /*
310  * This is the base class's image() function, which returns just an null image.
311  * Retrieval of the cover for the actual album is done by subclasses.
312  */
313 QImage
image(int size) const314 Meta::Album::image( int size ) const
315 {
316     Q_UNUSED( size );
317     return QImage();
318 }
319 
320 bool
operator ==(const Meta::Album & album) const321 Meta::Album::operator==( const Meta::Album &album ) const
322 {
323     return dynamic_cast<const void*>( this ) == dynamic_cast<const void*>( &album );
324 }
325 
326 //Meta::Genre
327 
328 QString
prettyName() const329 Meta::Genre::prettyName() const
330 {
331     if( !name().isEmpty() )
332         return name();
333     return i18n("Unknown Genre");
334 }
335 
336 void
notifyObservers() const337 Meta::Genre::notifyObservers() const
338 {
339     notifyObserversHelper<Genre, Observer>( this );
340 }
341 
342 bool
operator ==(const Meta::Genre & genre) const343 Meta::Genre::operator==( const Meta::Genre &genre ) const
344 {
345     return dynamic_cast<const void*>( this ) == dynamic_cast<const void*>( &genre );
346 }
347 
348 //Meta::Composer
349 
350 QString
prettyName() const351 Meta::Composer::prettyName() const
352 {
353     if( !name().isEmpty() )
354         return name();
355     return i18n("Unknown Composer");
356 }
357 
358 void
notifyObservers() const359 Meta::Composer::notifyObservers() const
360 {
361     notifyObserversHelper<Composer, Observer>( this );
362 }
363 
364 bool
operator ==(const Meta::Composer & composer) const365 Meta::Composer::operator==( const Meta::Composer &composer ) const
366 {
367     return dynamic_cast<const void*>( this ) == dynamic_cast<const  void*>( &composer );
368 }
369 
370 //Meta::Year
371 
372 void
notifyObservers() const373 Meta::Year::notifyObservers() const
374 {
375     notifyObserversHelper<Year, Observer>( this );
376 }
377 
378 bool
operator ==(const Meta::Year & year) const379 Meta::Year::operator==( const Meta::Year &year ) const
380 {
381     return dynamic_cast<const void*>( this ) == dynamic_cast<const  void*>( &year );
382 }
383