1 /* 2 * Cantata 3 * 4 * Copyright (c) 2011-2020 Craig Drummond <craig.p.drummond@gmail.com> 5 * 6 * ---- 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation; either version 2 of the License, or 11 * (at your option) any later version. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 * General Public License for more details. 17 * 18 * You should have received a copy of the GNU General Public License 19 * along with this program; see the file COPYING. If not, write to 20 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 21 * Boston, MA 02110-1301, USA. 22 */ 23 24 #ifndef PODCAST_SERVICE_H 25 #define PODCAST_SERVICE_H 26 27 #include "onlineservice.h" 28 #include "models/actionmodel.h" 29 #include "models/proxymodel.h" 30 #include "mpd-interface/song.h" 31 #include <QLatin1String> 32 #include <QList> 33 #include <QDateTime> 34 #include <QSet> 35 #include <QUrl> 36 37 class QTimer; 38 class NetworkJob; 39 40 class PodcastService : public ActionModel, public OnlineService 41 { 42 Q_OBJECT 43 44 public: 45 struct Item 46 { nameItem47 Item(const QString &n=QString(), const QUrl &u=QUrl()) : name(n), url(u) { } ~ItemItem48 virtual ~Item() { } isPodcastItem49 virtual bool isPodcast() const { return false; } 50 QString name; 51 QString descr; 52 QUrl url; 53 }; 54 55 struct Podcast; 56 struct Episode : public Item 57 { 58 enum DownloadState { 59 NotDownloading = -1, 60 QueuedForDownload = -2 61 }; 62 63 Episode(const QDateTime &d=QDateTime(), const QString &n=QString(), const QUrl &u=QUrl(), Podcast *p=nullptr) ItemEpisode64 : Item(n, u), played(false), duration(0), publishedDate(d), parent(p), downloadProg(NotDownloading) { } ~EpisodeEpisode65 ~Episode() override { } 66 Song toSong() const; 67 bool played; 68 int duration; 69 QDateTime publishedDate; 70 Podcast *parent; 71 QString localFile; 72 int downloadProg; 73 }; 74 75 struct Podcast : public Item 76 { 77 Podcast(const QString &f=QString()); ~PodcastPodcast78 ~Podcast() override { qDeleteAll(episodes); } isPodcastPodcast79 bool isPodcast() const override { return true; } 80 bool load(); 81 bool save() const; 82 void add(Episode *ep); 83 void add(QList<Episode *> &eps); 84 Episode * getEpisode(const QUrl &epUrl) const; 85 void setUnplayedCount(); 86 void removeFiles(); 87 const Song & coverSong(); 88 89 QList<Episode *> episodes; 90 int unplayedCount; 91 QString fileName; 92 QString imageFile; 93 QUrl imageUrl; 94 Song song; 95 }; 96 97 class Proxy : public ProxyModel 98 { 99 public: 100 Proxy(QObject *parent); 101 102 void showUnplayedOnly(bool on); 103 104 private: 105 bool lessThan(const QModelIndex &left, const QModelIndex &right) const override; 106 bool filterAcceptsPodcast(const Podcast *pod) const; 107 bool filterAcceptsEpisode(const Episode *item) const; 108 bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override; 109 110 private: 111 bool unplayedOnly; 112 }; 113 114 static const QLatin1String constName; 115 116 static PodcastService * self(); 117 118 PodcastService(); ~PodcastService()119 ~PodcastService() override { cancelAll(); } 120 121 Song & fixPath(Song &song) const; 122 QString name() const override; 123 QString title() const override; 124 QString descr() const override; 125 bool episodeCover(const Song &s, QImage &img, QString &imgFilename) const; 126 QString episodeDescr(const Song &s) const; 127 int rowCount(const QModelIndex &index = QModelIndex()) const override; 128 int columnCount(const QModelIndex &parent = QModelIndex()) const override { Q_UNUSED(parent) return 1; } 129 bool hasChildren(const QModelIndex &parent = QModelIndex()) const override; 130 QModelIndex parent(const QModelIndex &index) const override; 131 QModelIndex index(int row, int col, const QModelIndex &parent) const override; 132 QVariant data(const QModelIndex &, int) const override; 133 Qt::ItemFlags flags(const QModelIndex &index) const override; 134 QMimeData * mimeData(const QModelIndexList &indexes) const override; 135 QStringList filenames(const QModelIndexList &indexes, bool allowPlaylists=false) const; 136 QList<Song> songs(const QModelIndexList &indexes, bool allowPlaylists=false) const; 137 podcastCount()138 int podcastCount() const { return podcasts.count(); } 139 void clear(); 140 void configure(QWidget *p); subscribedToUrl(const QUrl & url)141 bool subscribedToUrl(const QUrl &url) { return nullptr!=getPodcast(url); } 142 void unSubscribe(Podcast *podcast); 143 void refresh(const QModelIndexList &list); 144 void refreshAll(); 145 void refreshSubscription(Podcast *item); 146 bool processingUrl(const QUrl &url) const; 147 void addUrl(const QUrl &url, bool isNew=true); 148 static bool isPodcastFile(const QString &file); iconPath()149 static const QString & iconPath() { return iconFile; } 150 static QUrl fixUrl(const QString &url); 151 static QUrl fixUrl(const QUrl &orig); isUrlOk(const QUrl & u)152 static bool isUrlOk(const QUrl &u) { return QLatin1String("http")==u.scheme() || QLatin1String("https")==u.scheme(); } 153 isDownloading()154 bool isDownloading() const { return nullptr!=downloadJob; } 155 void cancelAllDownloads(); 156 void downloadPodcasts(Podcast *pod, const QList<Episode *> &episodes); 157 void deleteDownloadedPodcasts(Podcast *pod, const QList<Episode *> &episodes); 158 void setPodcastsAsListened(Podcast *pod, const QList<Episode *> &episodes, bool listened); cancelAllJobs()159 void cancelAllJobs() { cancelAll(); cancelAllDownloads(); } 160 Podcast * getPodcast(const QUrl &url) const; 161 void cancelAll(); 162 void startRssUpdateTimer(); 163 void stopRssUpdateTimer(); 164 bool exportSubscriptions(const QString &name); 165 166 Q_SIGNALS: 167 void error(const QString &msg); 168 void newError(const QString &msg); 169 170 private: 171 bool downloadingEpisode(const QUrl &url) const; 172 void downloadEpisode(const Podcast *podcast, const QUrl &episode); 173 void cancelDownloads(const QList<Episode *> episodes); 174 void cancelDownload(const QUrl &url); 175 void cancelDownload(); 176 void doNextDownload(); 177 void updateEpisode(const QUrl &rssUrl, const QUrl &url, int pc); 178 void clearPartialDownloads(); 179 180 private Q_SLOTS: 181 void loadAll(); 182 void rssJobFinished(); 183 void updateRss(); 184 void currentMpdSong(const Song &s); 185 void downloadJobFinished(); 186 void downloadReadyRead(); 187 void downloadPercent(int pc); 188 189 private: 190 struct DownloadEntry 191 { urlDownloadEntry192 DownloadEntry(const QUrl &u=QUrl(), const QUrl &r=QUrl(), const QString &d=QString()) : url(u), rssUrl(r), dest(d) { } 193 bool operator==(const DownloadEntry &o) const { return o.url==url; } 194 QUrl url; 195 QUrl rssUrl; 196 QString dest; 197 Episode *ep; 198 }; 199 200 QList<Podcast *> podcasts; 201 QList<NetworkJob *> rssJobs; 202 NetworkJob * downloadJob; 203 QList<DownloadEntry> toDownload; 204 QTimer *rssUpdateTimer; 205 QDateTime lastRssUpdate; 206 QTimer *deleteTimer; 207 QDateTime lastDelete; 208 QSet<QUrl> updateUrls; 209 static QString iconFile; 210 }; 211 212 #endif 213 214