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