File indexing completed on 2024-05-05 04:48:17

0001 /****************************************************************************************
0002  * Copyright (c) 2007-2009 Bart Cerneels <bart.cerneels@kde.org>                        *
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 #ifndef PODCASTMETA_H
0018 #define PODCASTMETA_H
0019 
0020 #include "core/amarokcore_export.h"
0021 #include "core/meta/Meta.h"
0022 #include "core/playlists/Playlist.h"
0023 #include "core/support/Amarok.h"
0024 
0025 #include <QDateTime>
0026 #include <QSharedData>
0027 #include <QString>
0028 #include <QStringList>
0029 #include <QTextStream>
0030 #include <QUrl>
0031 
0032 #include <KLocalizedString>
0033 namespace Collections 
0034 {
0035 class QueryMaker;
0036 }
0037 namespace Podcasts
0038 {
0039 class PodcastEpisode;
0040 class PodcastChannel;
0041 
0042 class PodcastArtist;
0043 class PodcastAlbum;
0044 class PodcastComposer;
0045 class PodcastGenre;
0046 class PodcastYear;
0047 
0048 typedef AmarokSharedPointer<PodcastEpisode> PodcastEpisodePtr;
0049 typedef AmarokSharedPointer<PodcastChannel> PodcastChannelPtr;
0050 
0051 typedef QList<PodcastEpisodePtr> PodcastEpisodeList;
0052 typedef QList<PodcastChannelPtr> PodcastChannelList;
0053 
0054 class AMAROKCORE_EXPORT PodcastMetaCommon
0055 {
0056     public:
0057         PodcastMetaCommon() {}
0058         virtual ~PodcastMetaCommon() {}
0059 
0060         virtual QString title() const { return m_title;}
0061         virtual QString description() const { return m_description; }
0062         virtual QStringList keywords() const { return m_keywords; }
0063         virtual QString subtitle() const { return m_subtitle; }
0064         virtual QString summary() const { return m_summary; }
0065         virtual QString author() const { return m_author; }
0066 
0067         virtual void setTitle( const QString &title ) { m_title = title; }
0068         virtual void setDescription( const QString &description ) { m_description = description; }
0069         virtual void setKeywords( const QStringList &keywords ) { m_keywords = keywords; }
0070         virtual void addKeyword( const QString &keyword ) { m_keywords << keyword; }
0071         virtual void setSubtitle( const QString &subtitle ) { m_subtitle = subtitle; }
0072         virtual void setSummary( const QString &summary ) { m_summary = summary; }
0073         virtual void setAuthor( const QString &author ) { m_author = author; }
0074 
0075     protected:
0076         QString m_title; //the title
0077         QString m_description; //a longer description, with HTML markup
0078         QStringList m_keywords; // TODO: save to DB
0079         QString m_subtitle; //a short description
0080         QString m_summary;
0081         QString m_author; // TODO: save to DB
0082 };
0083 
0084 class AMAROKCORE_EXPORT PodcastEpisode : public PodcastMetaCommon, public Meta::Track
0085 {
0086     public:
0087         PodcastEpisode();
0088         explicit PodcastEpisode( const PodcastChannelPtr &channel );
0089         PodcastEpisode( const PodcastEpisodePtr &episode, const PodcastChannelPtr &channel );
0090 
0091         ~PodcastEpisode() override {}
0092 
0093         // Meta::Base methods
0094         QString name() const override { return m_title; }
0095 
0096         // Meta::Track Methods
0097         QUrl playableUrl() const override { return m_localUrl.isEmpty() ? m_url : m_localUrl; }
0098         QString prettyUrl() const override { return playableUrl().toDisplayString(); }
0099         QString uidUrl() const override { return m_url.url(); }
0100         QString notPlayableReason() const override;
0101 
0102         Meta::AlbumPtr album() const override { return m_albumPtr; }
0103         Meta::ArtistPtr artist() const override { return m_artistPtr; }
0104         Meta::ComposerPtr composer() const override { return m_composerPtr; }
0105         Meta::GenrePtr genre() const override { return m_genrePtr; }
0106         Meta::YearPtr year() const override { return m_yearPtr; }
0107 
0108         qreal bpm() const override { return -1.0; }
0109 
0110         QString comment() const override { return QString(); }
0111         virtual void setComment( const QString &newComment ) { Q_UNUSED( newComment ); }
0112         qint64 length() const override { return m_duration * 1000; }
0113         int filesize() const override { return m_fileSize; }
0114         int sampleRate() const override { return 0; }
0115         int bitrate() const override { return 0; }
0116         int trackNumber() const override { return m_sequenceNumber; }
0117         virtual void setTrackNumber( int newTrackNumber ) { Q_UNUSED( newTrackNumber ); }
0118         int discNumber() const override { return 0; }
0119         virtual void setDiscNumber( int newDiscNumber ) { Q_UNUSED( newDiscNumber ); }
0120         virtual QString mimeType() const { return m_mimeType; }
0121 
0122         QString type() const override
0123         {
0124             const QString fileName = playableUrl().fileName();
0125             return Amarok::extension( fileName );
0126         }
0127 
0128         virtual void addMatchTo( Collections::QueryMaker* qm ) { Q_UNUSED( qm ); }
0129         bool inCollection() const override { return false; }
0130         QString cachedLyrics() const override { return QString(); }
0131         void setCachedLyrics( const QString &lyrics ) override { Q_UNUSED( lyrics ); }
0132 
0133         bool operator==( const Meta::Track &track ) const override;
0134 
0135         //PodcastMetaCommon methods
0136         void setTitle( const QString &title ) override { m_title = title; }
0137 
0138         //PodcastEpisode methods
0139         virtual QUrl localUrl() const { return m_localUrl; }
0140         virtual QDateTime pubDate() const { return m_pubDate; }
0141         virtual int duration() const { return m_duration; }
0142         virtual QString guid() const { return m_guid; }
0143         virtual bool isNew() const { return m_isNew; }
0144         virtual int sequenceNumber() const { return m_sequenceNumber; }
0145         virtual PodcastChannelPtr channel() const { return m_channel; }
0146 
0147         virtual void setLocalUrl( const QUrl &url ) { m_localUrl = url; }
0148         virtual void setFilesize( int fileSize ) { m_fileSize = fileSize; }
0149         virtual void setMimeType( const QString &mimeType ) { m_mimeType = mimeType; }
0150         virtual void setUidUrl( const QUrl &url ) { m_url = url; }
0151         virtual void setPubDate( const QDateTime &pubDate ) { m_pubDate = pubDate; }
0152         virtual void setDuration( int duration ) { m_duration = duration; }
0153         virtual void setGuid( const QString &guid ) { m_guid = guid; }
0154         virtual void setNew( bool isNew ) { m_isNew = isNew; }
0155         virtual void setSequenceNumber( int sequenceNumber ) { m_sequenceNumber = sequenceNumber; }
0156         virtual void setChannel( const PodcastChannelPtr &channel ) { m_channel = channel; }
0157 
0158     protected:
0159         PodcastChannelPtr m_channel;
0160 
0161         QString m_guid; //the GUID from the podcast feed
0162         QUrl m_url; //remote url of the file
0163         QUrl m_localUrl; //the localUrl, only valid if downloaded
0164         QString m_mimeType; //the mimetype of the enclosure
0165         QDateTime m_pubDate; //the pubDate from the feed
0166         int m_duration; //the playlength in seconds
0167         int m_fileSize; //the size tag from the enclosure
0168         int m_sequenceNumber; //number of the episode
0169         bool m_isNew; //listened to or not?
0170 
0171         //data members
0172         Meta::AlbumPtr m_albumPtr;
0173         Meta::ArtistPtr m_artistPtr;
0174         Meta::ComposerPtr m_composerPtr;
0175         Meta::GenrePtr m_genrePtr;
0176         Meta::YearPtr m_yearPtr;
0177 };
0178 
0179 class AMAROKCORE_EXPORT PodcastChannel : public PodcastMetaCommon, public Playlists::Playlist
0180 {
0181     public:
0182 
0183         enum FetchType
0184         {
0185             DownloadWhenAvailable = 0,
0186             StreamOrDownloadOnDemand
0187         };
0188 
0189         PodcastChannel()
0190             : PodcastMetaCommon()
0191             , Playlist()
0192             , m_image()
0193             , m_subscribeDate()
0194             , m_copyright()
0195             , m_autoScan( false )
0196             , m_fetchType( DownloadWhenAvailable )
0197             , m_purge( false )
0198             , m_purgeCount( 0 )
0199         { }
0200 
0201         explicit PodcastChannel(const PodcastChannelPtr &channel );
0202         ~PodcastChannel() override {}
0203 
0204         //Playlist virtual methods
0205         QUrl uidUrl() const override { return m_url; }
0206         QString name() const override { return title(); }
0207 
0208         int trackCount() const override { return m_episodes.count(); }
0209         Meta::TrackList tracks() override;
0210         void addTrack( const Meta::TrackPtr &track, int position = -1 ) override;
0211 
0212         //PodcastMetaCommon methods
0213         // override this since it's ambiguous in PodcastMetaCommon and Playlist
0214         QString description() const override { return m_description; }
0215 
0216         //PodcastChannel methods
0217         virtual QUrl url() const { return m_url; }
0218         virtual QUrl webLink() const { return m_webLink; }
0219         virtual bool hasImage() const { return !m_image.isNull(); }
0220         virtual QUrl imageUrl() const { return m_imageUrl; }
0221         virtual QImage image() const { return m_image; }
0222         virtual QString copyright() const { return m_copyright; }
0223         virtual QStringList labels() const { return m_labels; }
0224         virtual QDate subscribeDate() const { return m_subscribeDate; }
0225 
0226         virtual void setUrl( const QUrl &url ) { m_url = url; }
0227         virtual void setWebLink( const QUrl &link ) { m_webLink = link; }
0228         // TODO: inform all albums with this channel of the changed image
0229         virtual void setImage( const QImage &image ) { m_image = image; }
0230         virtual void setImageUrl( const QUrl &imageUrl ) { m_imageUrl = imageUrl; }
0231         virtual void setCopyright( const QString &copyright ) { m_copyright = copyright; }
0232         virtual void setLabels( const QStringList &labels ) { m_labels = labels; }
0233         virtual void addLabel( const QString &label ) { m_labels << label; }
0234         virtual void setSubscribeDate( const QDate &date ) { m_subscribeDate = date; }
0235 
0236         virtual Podcasts::PodcastEpisodePtr addEpisode( const PodcastEpisodePtr &episode );
0237         virtual PodcastEpisodeList episodes() const { return m_episodes; }
0238 
0239         bool load( QTextStream &stream ) { Q_UNUSED( stream ); return false; }
0240 
0241         //PodcastChannel Settings
0242         QUrl saveLocation() const { return m_directory; }
0243         bool autoScan() const { return m_autoScan; }
0244         FetchType fetchType() const { return m_fetchType; }
0245         bool hasPurge() const { return m_purge; }
0246         int purgeCount() const { return m_purgeCount; }
0247 
0248         void setSaveLocation( const QUrl &url ) { m_directory = url; }
0249         void setAutoScan( bool autoScan ) { m_autoScan = autoScan; }
0250         void setFetchType( FetchType fetchType ) { m_fetchType = fetchType; }
0251         void setPurge( bool purge ) { m_purge = purge; }
0252         void setPurgeCount( int purgeCount ) { m_purgeCount = purgeCount; }
0253 
0254     protected:
0255         QUrl m_url;
0256         QUrl m_webLink;
0257         QImage m_image;
0258         QUrl m_imageUrl;
0259         QStringList m_labels;
0260         QDate m_subscribeDate;
0261         QString m_copyright;
0262         QUrl m_directory; //the local directory to save the files in.
0263         bool m_autoScan; //should this channel be checked automatically?
0264         PodcastChannel::FetchType m_fetchType; //'download when available' or 'stream or download on demand'
0265         bool m_purge; //remove old episodes?
0266         int m_purgeCount; //how many episodes do we keep on disk?
0267         PodcastEpisodeList m_episodes;
0268 };
0269 
0270 // internal helper classes
0271 
0272 class AMAROKCORE_EXPORT PodcastArtist : public Meta::Artist
0273 {
0274 public:
0275     explicit PodcastArtist( PodcastEpisode *episode )
0276         : Meta::Artist()
0277         , episode( episode )
0278     {}
0279 
0280     Meta::TrackList tracks() override
0281     {
0282         return Meta::TrackList();
0283     }
0284 
0285     Meta::AlbumList albums()
0286     {
0287         return Meta::AlbumList();
0288     }
0289 
0290     QString name() const override
0291     {
0292         QString author;
0293         if( episode && episode->channel() )
0294             author = episode->channel()->author();
0295 
0296         return author;
0297     }
0298 
0299     bool operator==( const Meta::Artist &other ) const override
0300     {
0301         return name() == other.name();
0302     }
0303 
0304     PodcastEpisode const *episode;
0305 };
0306 
0307 class AMAROKCORE_EXPORT PodcastAlbum : public Meta::Album
0308 {
0309 public:
0310     explicit PodcastAlbum( PodcastEpisode *episode )
0311         : Meta::Album()
0312         , episode( episode )
0313     {}
0314 
0315     /* Its all a little bit stupid.
0316        When the channel image (and also the album image) changes the album get's no indication.
0317        Also the CoverCache is not in amarokcorelib but in amaroklib.
0318        Why the PodcastAlbum is the only one with a concrete implementation in amarokcorelib is another question.
0319 
0320        virtual ~PodcastAlbum()
0321        { CoverCache::invalidateAlbum( Meta::AlbumPtr(this) ); }
0322     */
0323 
0324     bool isCompilation() const override
0325     {
0326         return false;
0327     }
0328 
0329     bool hasAlbumArtist() const override
0330     {
0331         return false;
0332     }
0333 
0334     Meta::ArtistPtr albumArtist() const override
0335     {
0336         return Meta::ArtistPtr();
0337     }
0338 
0339     Meta::TrackList tracks() override
0340     {
0341         return Meta::TrackList();
0342     }
0343 
0344     QString name() const override
0345     {
0346         if( episode != nullptr )
0347         {
0348             const QString albumName = episode->channel()->title();
0349             return albumName;
0350         }
0351         else
0352             return QString();
0353     }
0354 
0355     QImage image( int size ) const override
0356     {
0357         // This is a little stupid. If Channel::setImage is called we don't Q_EMIT a MetaDataChanged or invalidate the cache
0358         QImage image = episode->channel()->image();
0359         return image.scaledToHeight( size );
0360     }
0361 
0362     bool operator==( const Meta::Album &other ) const override
0363     {
0364         return name() == other.name();
0365     }
0366 
0367     PodcastEpisode const *episode;
0368 };
0369 
0370 class AMAROKCORE_EXPORT PodcastGenre : public Meta::Genre
0371 {
0372 public:
0373     explicit PodcastGenre( PodcastEpisode *episode )
0374         : Meta::Genre()
0375         , episode( episode )
0376     {}
0377 
0378     Meta::TrackList tracks() override
0379     {
0380         return Meta::TrackList();
0381     }
0382 
0383     QString name() const override
0384     {
0385         const QString genreName = i18n( "Podcast" );
0386         return genreName;
0387     }
0388 
0389     bool operator==( const Meta::Genre &other ) const override
0390     {
0391         return name() == other.name();
0392     }
0393 
0394     PodcastEpisode const *episode;
0395 };
0396 
0397 class AMAROKCORE_EXPORT PodcastComposer : public Meta::Composer
0398 {
0399 public:
0400     explicit PodcastComposer( PodcastEpisode *episode )
0401         : Meta::Composer()
0402         , episode( episode )
0403     {}
0404 
0405     Meta::TrackList tracks() override
0406     {
0407         return Meta::TrackList();
0408     }
0409 
0410     QString name() const override
0411     {
0412         if( episode != nullptr )
0413         {
0414             const QString composer = episode->channel()->author();
0415             return composer;
0416         }
0417         else
0418             return QString();
0419 
0420      }
0421 
0422     bool operator==( const Meta::Composer &other ) const override
0423     {
0424         return name() == other.name();
0425     }
0426 
0427     PodcastEpisode const *episode;
0428 };
0429 
0430 class AMAROKCORE_EXPORT PodcastYear : public Meta::Year
0431 {
0432 public:
0433     explicit PodcastYear( PodcastEpisode *episode )
0434         : Meta::Year()
0435         , episode( episode )
0436     {}
0437 
0438     Meta::TrackList tracks() override
0439     {
0440         return Meta::TrackList();
0441     }
0442 
0443     QString name() const override
0444     {
0445         if( episode != nullptr )
0446         {
0447             const QString year = episode->pubDate().toString( QStringLiteral("yyyy") );
0448             return year;
0449         }
0450         else
0451             return QString();
0452     }
0453 
0454     bool operator==( const Meta::Year &other ) const override
0455     {
0456         return name() == other.name();
0457     }
0458 
0459     PodcastEpisode const *episode;
0460 };
0461 
0462 } //namespace Podcasts
0463 
0464 Q_DECLARE_METATYPE( Podcasts::PodcastMetaCommon* )
0465 Q_DECLARE_METATYPE( Podcasts::PodcastEpisodePtr )
0466 Q_DECLARE_METATYPE( Podcasts::PodcastEpisodeList )
0467 Q_DECLARE_METATYPE( Podcasts::PodcastChannelPtr )
0468 Q_DECLARE_METATYPE( Podcasts::PodcastChannelList )
0469 
0470 #endif