1 /**************************************************************************************** 2 * Copyright (c) 2007-2009 Bart Cerneels <bart.cerneels@kde.org> * 3 * * 4 * This program is free software; you can redistribute it and/or modify it under * 5 * the terms of the GNU General Public License as published by the Free Software * 6 * Foundation; either version 2 of the License, or (at your option) any later * 7 * version. * 8 * * 9 * This program is distributed in the hope that it will be useful, but WITHOUT ANY * 10 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * 11 * PARTICULAR PURPOSE. See the GNU General Public License for more details. * 12 * * 13 * You should have received a copy of the GNU General Public License along with * 14 * this program. If not, see <http://www.gnu.org/licenses/>. * 15 ****************************************************************************************/ 16 17 #ifndef PODCASTMETA_H 18 #define PODCASTMETA_H 19 20 #include "core/amarokcore_export.h" 21 #include "core/meta/Meta.h" 22 #include "core/playlists/Playlist.h" 23 #include "core/support/Amarok.h" 24 25 #include <QDateTime> 26 #include <QSharedData> 27 #include <QString> 28 #include <QStringList> 29 #include <QTextStream> 30 #include <QUrl> 31 32 #include <KLocalizedString> 33 namespace Collections 34 { 35 class QueryMaker; 36 } 37 namespace Podcasts 38 { 39 class PodcastEpisode; 40 class PodcastChannel; 41 42 class PodcastArtist; 43 class PodcastAlbum; 44 class PodcastComposer; 45 class PodcastGenre; 46 class PodcastYear; 47 48 typedef AmarokSharedPointer<PodcastEpisode> PodcastEpisodePtr; 49 typedef AmarokSharedPointer<PodcastChannel> PodcastChannelPtr; 50 51 typedef QList<PodcastEpisodePtr> PodcastEpisodeList; 52 typedef QList<PodcastChannelPtr> PodcastChannelList; 53 54 class AMAROKCORE_EXPORT PodcastMetaCommon 55 { 56 public: PodcastMetaCommon()57 PodcastMetaCommon() {} ~PodcastMetaCommon()58 virtual ~PodcastMetaCommon() {} 59 title()60 virtual QString title() const { return m_title;} description()61 virtual QString description() const { return m_description; } keywords()62 virtual QStringList keywords() const { return m_keywords; } subtitle()63 virtual QString subtitle() const { return m_subtitle; } summary()64 virtual QString summary() const { return m_summary; } author()65 virtual QString author() const { return m_author; } 66 setTitle(const QString & title)67 virtual void setTitle( const QString &title ) { m_title = title; } setDescription(const QString & description)68 virtual void setDescription( const QString &description ) { m_description = description; } setKeywords(const QStringList & keywords)69 virtual void setKeywords( const QStringList &keywords ) { m_keywords = keywords; } addKeyword(const QString & keyword)70 virtual void addKeyword( const QString &keyword ) { m_keywords << keyword; } setSubtitle(const QString & subtitle)71 virtual void setSubtitle( const QString &subtitle ) { m_subtitle = subtitle; } setSummary(const QString & summary)72 virtual void setSummary( const QString &summary ) { m_summary = summary; } setAuthor(const QString & author)73 virtual void setAuthor( const QString &author ) { m_author = author; } 74 75 protected: 76 QString m_title; //the title 77 QString m_description; //a longer description, with HTML markup 78 QStringList m_keywords; // TODO: save to DB 79 QString m_subtitle; //a short description 80 QString m_summary; 81 QString m_author; // TODO: save to DB 82 }; 83 84 class AMAROKCORE_EXPORT PodcastEpisode : public PodcastMetaCommon, public Meta::Track 85 { 86 public: 87 PodcastEpisode(); 88 explicit PodcastEpisode( const PodcastChannelPtr &channel ); 89 PodcastEpisode( const PodcastEpisodePtr &episode, const PodcastChannelPtr &channel ); 90 ~PodcastEpisode()91 ~PodcastEpisode() override {} 92 93 // Meta::Base methods name()94 QString name() const override { return m_title; } 95 96 // Meta::Track Methods playableUrl()97 QUrl playableUrl() const override { return m_localUrl.isEmpty() ? m_url : m_localUrl; } prettyUrl()98 QString prettyUrl() const override { return playableUrl().toDisplayString(); } uidUrl()99 QString uidUrl() const override { return m_url.url(); } 100 QString notPlayableReason() const override; 101 album()102 Meta::AlbumPtr album() const override { return m_albumPtr; } artist()103 Meta::ArtistPtr artist() const override { return m_artistPtr; } composer()104 Meta::ComposerPtr composer() const override { return m_composerPtr; } genre()105 Meta::GenrePtr genre() const override { return m_genrePtr; } year()106 Meta::YearPtr year() const override { return m_yearPtr; } 107 bpm()108 qreal bpm() const override { return -1.0; } 109 comment()110 QString comment() const override { return QString(); } setComment(const QString & newComment)111 virtual void setComment( const QString &newComment ) { Q_UNUSED( newComment ); } length()112 qint64 length() const override { return m_duration * 1000; } filesize()113 int filesize() const override { return m_fileSize; } sampleRate()114 int sampleRate() const override { return 0; } bitrate()115 int bitrate() const override { return 0; } trackNumber()116 int trackNumber() const override { return m_sequenceNumber; } setTrackNumber(int newTrackNumber)117 virtual void setTrackNumber( int newTrackNumber ) { Q_UNUSED( newTrackNumber ); } discNumber()118 int discNumber() const override { return 0; } setDiscNumber(int newDiscNumber)119 virtual void setDiscNumber( int newDiscNumber ) { Q_UNUSED( newDiscNumber ); } mimeType()120 virtual QString mimeType() const { return m_mimeType; } 121 type()122 QString type() const override 123 { 124 const QString fileName = playableUrl().fileName(); 125 return Amarok::extension( fileName ); 126 } 127 addMatchTo(Collections::QueryMaker * qm)128 virtual void addMatchTo( Collections::QueryMaker* qm ) { Q_UNUSED( qm ); } inCollection()129 bool inCollection() const override { return false; } cachedLyrics()130 QString cachedLyrics() const override { return QString(); } setCachedLyrics(const QString & lyrics)131 void setCachedLyrics( const QString &lyrics ) override { Q_UNUSED( lyrics ); } 132 133 bool operator==( const Meta::Track &track ) const override; 134 135 //PodcastMetaCommon methods setTitle(const QString & title)136 void setTitle( const QString &title ) override { m_title = title; } 137 138 //PodcastEpisode methods localUrl()139 virtual QUrl localUrl() const { return m_localUrl; } pubDate()140 virtual QDateTime pubDate() const { return m_pubDate; } duration()141 virtual int duration() const { return m_duration; } guid()142 virtual QString guid() const { return m_guid; } isNew()143 virtual bool isNew() const { return m_isNew; } sequenceNumber()144 virtual int sequenceNumber() const { return m_sequenceNumber; } channel()145 virtual PodcastChannelPtr channel() const { return m_channel; } 146 setLocalUrl(const QUrl & url)147 virtual void setLocalUrl( const QUrl &url ) { m_localUrl = url; } setFilesize(int fileSize)148 virtual void setFilesize( int fileSize ) { m_fileSize = fileSize; } setMimeType(const QString & mimeType)149 virtual void setMimeType( const QString &mimeType ) { m_mimeType = mimeType; } setUidUrl(const QUrl & url)150 virtual void setUidUrl( const QUrl &url ) { m_url = url; } setPubDate(const QDateTime & pubDate)151 virtual void setPubDate( const QDateTime &pubDate ) { m_pubDate = pubDate; } setDuration(int duration)152 virtual void setDuration( int duration ) { m_duration = duration; } setGuid(const QString & guid)153 virtual void setGuid( const QString &guid ) { m_guid = guid; } setNew(bool isNew)154 virtual void setNew( bool isNew ) { m_isNew = isNew; } setSequenceNumber(int sequenceNumber)155 virtual void setSequenceNumber( int sequenceNumber ) { m_sequenceNumber = sequenceNumber; } setChannel(const PodcastChannelPtr & channel)156 virtual void setChannel( const PodcastChannelPtr &channel ) { m_channel = channel; } 157 158 protected: 159 PodcastChannelPtr m_channel; 160 161 QString m_guid; //the GUID from the podcast feed 162 QUrl m_url; //remote url of the file 163 QUrl m_localUrl; //the localUrl, only valid if downloaded 164 QString m_mimeType; //the mimetype of the enclosure 165 QDateTime m_pubDate; //the pubDate from the feed 166 int m_duration; //the playlength in seconds 167 int m_fileSize; //the size tag from the enclosure 168 int m_sequenceNumber; //number of the episode 169 bool m_isNew; //listened to or not? 170 171 //data members 172 Meta::AlbumPtr m_albumPtr; 173 Meta::ArtistPtr m_artistPtr; 174 Meta::ComposerPtr m_composerPtr; 175 Meta::GenrePtr m_genrePtr; 176 Meta::YearPtr m_yearPtr; 177 }; 178 179 class AMAROKCORE_EXPORT PodcastChannel : public PodcastMetaCommon, public Playlists::Playlist 180 { 181 public: 182 183 enum FetchType 184 { 185 DownloadWhenAvailable = 0, 186 StreamOrDownloadOnDemand 187 }; 188 PodcastChannel()189 PodcastChannel() 190 : PodcastMetaCommon() 191 , Playlist() 192 , m_image() 193 , m_subscribeDate() 194 , m_copyright() 195 , m_autoScan( false ) 196 , m_fetchType( DownloadWhenAvailable ) 197 , m_purge( false ) 198 , m_purgeCount( 0 ) 199 { } 200 201 explicit PodcastChannel(const PodcastChannelPtr &channel ); ~PodcastChannel()202 ~PodcastChannel() override {} 203 204 //Playlist virtual methods uidUrl()205 QUrl uidUrl() const override { return m_url; } name()206 QString name() const override { return title(); } 207 trackCount()208 int trackCount() const override { return m_episodes.count(); } 209 Meta::TrackList tracks() override; 210 void addTrack( const Meta::TrackPtr &track, int position = -1 ) override; 211 212 //PodcastMetaCommon methods 213 // override this since it's ambiguous in PodcastMetaCommon and Playlist description()214 QString description() const override { return m_description; } 215 216 //PodcastChannel methods url()217 virtual QUrl url() const { return m_url; } webLink()218 virtual QUrl webLink() const { return m_webLink; } hasImage()219 virtual bool hasImage() const { return !m_image.isNull(); } imageUrl()220 virtual QUrl imageUrl() const { return m_imageUrl; } image()221 virtual QImage image() const { return m_image; } copyright()222 virtual QString copyright() const { return m_copyright; } labels()223 virtual QStringList labels() const { return m_labels; } subscribeDate()224 virtual QDate subscribeDate() const { return m_subscribeDate; } 225 setUrl(const QUrl & url)226 virtual void setUrl( const QUrl &url ) { m_url = url; } setWebLink(const QUrl & link)227 virtual void setWebLink( const QUrl &link ) { m_webLink = link; } 228 // TODO: inform all albums with this channel of the changed image setImage(const QImage & image)229 virtual void setImage( const QImage &image ) { m_image = image; } setImageUrl(const QUrl & imageUrl)230 virtual void setImageUrl( const QUrl &imageUrl ) { m_imageUrl = imageUrl; } setCopyright(const QString & copyright)231 virtual void setCopyright( const QString ©right ) { m_copyright = copyright; } setLabels(const QStringList & labels)232 virtual void setLabels( const QStringList &labels ) { m_labels = labels; } addLabel(const QString & label)233 virtual void addLabel( const QString &label ) { m_labels << label; } setSubscribeDate(const QDate & date)234 virtual void setSubscribeDate( const QDate &date ) { m_subscribeDate = date; } 235 236 virtual Podcasts::PodcastEpisodePtr addEpisode( const PodcastEpisodePtr &episode ); episodes()237 virtual PodcastEpisodeList episodes() const { return m_episodes; } 238 load(QTextStream & stream)239 bool load( QTextStream &stream ) { Q_UNUSED( stream ); return false; } 240 241 //PodcastChannel Settings saveLocation()242 QUrl saveLocation() const { return m_directory; } autoScan()243 bool autoScan() const { return m_autoScan; } fetchType()244 FetchType fetchType() const { return m_fetchType; } hasPurge()245 bool hasPurge() const { return m_purge; } purgeCount()246 int purgeCount() const { return m_purgeCount; } 247 setSaveLocation(const QUrl & url)248 void setSaveLocation( const QUrl &url ) { m_directory = url; } setAutoScan(bool autoScan)249 void setAutoScan( bool autoScan ) { m_autoScan = autoScan; } setFetchType(FetchType fetchType)250 void setFetchType( FetchType fetchType ) { m_fetchType = fetchType; } setPurge(bool purge)251 void setPurge( bool purge ) { m_purge = purge; } setPurgeCount(int purgeCount)252 void setPurgeCount( int purgeCount ) { m_purgeCount = purgeCount; } 253 254 protected: 255 QUrl m_url; 256 QUrl m_webLink; 257 QImage m_image; 258 QUrl m_imageUrl; 259 QStringList m_labels; 260 QDate m_subscribeDate; 261 QString m_copyright; 262 QUrl m_directory; //the local directory to save the files in. 263 bool m_autoScan; //should this channel be checked automatically? 264 PodcastChannel::FetchType m_fetchType; //'download when available' or 'stream or download on demand' 265 bool m_purge; //remove old episodes? 266 int m_purgeCount; //how many episodes do we keep on disk? 267 PodcastEpisodeList m_episodes; 268 }; 269 270 // internal helper classes 271 272 class AMAROKCORE_EXPORT PodcastArtist : public Meta::Artist 273 { 274 public: PodcastArtist(PodcastEpisode * episode)275 explicit PodcastArtist( PodcastEpisode *episode ) 276 : Meta::Artist() 277 , episode( episode ) 278 {} 279 tracks()280 Meta::TrackList tracks() override 281 { 282 return Meta::TrackList(); 283 } 284 albums()285 Meta::AlbumList albums() 286 { 287 return Meta::AlbumList(); 288 } 289 name()290 QString name() const override 291 { 292 QString author; 293 if( episode && episode->channel() ) 294 author = episode->channel()->author(); 295 296 return author; 297 } 298 299 bool operator==( const Meta::Artist &other ) const override 300 { 301 return name() == other.name(); 302 } 303 304 PodcastEpisode const *episode; 305 }; 306 307 class AMAROKCORE_EXPORT PodcastAlbum : public Meta::Album 308 { 309 public: PodcastAlbum(PodcastEpisode * episode)310 explicit PodcastAlbum( PodcastEpisode *episode ) 311 : Meta::Album() 312 , episode( episode ) 313 {} 314 315 /* Its all a little bit stupid. 316 When the channel image (and also the album image) changes the album get's no indication. 317 Also the CoverCache is not in amarokcorelib but in amaroklib. 318 Why the PodcastAlbum is the only one with a concrete implementation in amarokcorelib is another question. 319 320 virtual ~PodcastAlbum() 321 { CoverCache::invalidateAlbum( Meta::AlbumPtr(this) ); } 322 */ 323 isCompilation()324 bool isCompilation() const override 325 { 326 return false; 327 } 328 hasAlbumArtist()329 bool hasAlbumArtist() const override 330 { 331 return false; 332 } 333 albumArtist()334 Meta::ArtistPtr albumArtist() const override 335 { 336 return Meta::ArtistPtr(); 337 } 338 tracks()339 Meta::TrackList tracks() override 340 { 341 return Meta::TrackList(); 342 } 343 name()344 QString name() const override 345 { 346 if( episode != 0 ) 347 { 348 const QString albumName = episode->channel()->title(); 349 return albumName; 350 } 351 else 352 return QString(); 353 } 354 image(int size)355 QImage image( int size ) const override 356 { 357 // This is a little stupid. If Channel::setImage is called we don't Q_EMIT a MetaDataChanged or invalidate the cache 358 QImage image = episode->channel()->image(); 359 return image.scaledToHeight( size ); 360 } 361 362 bool operator==( const Meta::Album &other ) const override 363 { 364 return name() == other.name(); 365 } 366 367 PodcastEpisode const *episode; 368 }; 369 370 class AMAROKCORE_EXPORT PodcastGenre : public Meta::Genre 371 { 372 public: PodcastGenre(PodcastEpisode * episode)373 explicit PodcastGenre( PodcastEpisode *episode ) 374 : Meta::Genre() 375 , episode( episode ) 376 {} 377 tracks()378 Meta::TrackList tracks() override 379 { 380 return Meta::TrackList(); 381 } 382 name()383 QString name() const override 384 { 385 const QString genreName = i18n( "Podcast" ); 386 return genreName; 387 } 388 389 bool operator==( const Meta::Genre &other ) const override 390 { 391 return name() == other.name(); 392 } 393 394 PodcastEpisode const *episode; 395 }; 396 397 class AMAROKCORE_EXPORT PodcastComposer : public Meta::Composer 398 { 399 public: PodcastComposer(PodcastEpisode * episode)400 explicit PodcastComposer( PodcastEpisode *episode ) 401 : Meta::Composer() 402 , episode( episode ) 403 {} 404 tracks()405 Meta::TrackList tracks() override 406 { 407 return Meta::TrackList(); 408 } 409 name()410 QString name() const override 411 { 412 if( episode != 0 ) 413 { 414 const QString composer = episode->channel()->author(); 415 return composer; 416 } 417 else 418 return QString(); 419 420 } 421 422 bool operator==( const Meta::Composer &other ) const override 423 { 424 return name() == other.name(); 425 } 426 427 PodcastEpisode const *episode; 428 }; 429 430 class AMAROKCORE_EXPORT PodcastYear : public Meta::Year 431 { 432 public: PodcastYear(PodcastEpisode * episode)433 explicit PodcastYear( PodcastEpisode *episode ) 434 : Meta::Year() 435 , episode( episode ) 436 {} 437 tracks()438 Meta::TrackList tracks() override 439 { 440 return Meta::TrackList(); 441 } 442 name()443 QString name() const override 444 { 445 if( episode != 0 ) 446 { 447 const QString year = episode->pubDate().toString( QStringLiteral("yyyy") ); 448 return year; 449 } 450 else 451 return QString(); 452 } 453 454 bool operator==( const Meta::Year &other ) const override 455 { 456 return name() == other.name(); 457 } 458 459 PodcastEpisode const *episode; 460 }; 461 462 } //namespace Podcasts 463 464 Q_DECLARE_METATYPE( Podcasts::PodcastMetaCommon* ) 465 Q_DECLARE_METATYPE( Podcasts::PodcastEpisodePtr ) 466 Q_DECLARE_METATYPE( Podcasts::PodcastEpisodeList ) 467 Q_DECLARE_METATYPE( Podcasts::PodcastChannelPtr ) 468 Q_DECLARE_METATYPE( Podcasts::PodcastChannelList ) 469 470 #endif 471