git.iko.soy/maciek134/podphoenix
on main help sign in
Backported clean up settings patch (rev 41)
commitd839119eebe7b1ab7a14c4cc9719cc043f744a4e authorNekhelesh Ramananthan <krnekhelesh@gmail.com> dateTue 29 Mar 2016, 13:19 (11 years ago) parent2fde668
5 files changed+243−11
app/podbird.qml+23−2 app/ui/SearchPage.qml+2−4 app/ui/SettingsPage.qml+189 backend/modules/Podbird/filemanager.cpp+18−5 backend/modules/Podbird/filemanager.h+11
app/podbird.qml+23−2
@@ -127,14 +127,35 @@ MainView {
127127
128128 SingleDownload {
129129 id: imageDownloader
130− property string feed;
130+
131+ property string feed
132+ property var queue: []
133+ property int queueLength: 0
134+
131135 onFinished: {
132136 var db = Podcasts.init();
133137 var finalLocation = fileManager.saveDownload(path);
134138 db.transaction(function (tx) {
135− tx.executeSql("UPDATE Podcast SET image=? WHERE feed=?", [finalLocation, feed]);
139+ tx.executeSql("UPDATE Podcast SET image=? WHERE feed=?", [finalLocation, imageDownloader.feed]);
140+ queue.shift();
141+ queueLength--
142+ if (queue.length > 0) {
143+ imageDownloader.feed = queue[0][0];
144+ download(queue[0][1]);
145+ } else {
146+ feed = "";
147+ }
136148 });
137149 }
150+
151+ function addDownload(feed, url) {
152+ queue.push([feed, url]);
153+ queueLength++
154+ if (queue.length == 1) {
155+ imageDownloader.feed = feed;
156+ download(url);
157+ }
158+ }
138159 }
139160
140161 Component {
app/ui/SearchPage.qml+2−4
@@ -276,8 +276,7 @@ Page {
276276 onClicked: {
277277 if (!model.subscribed) {
278278 Podcasts.subscribe(model.artist, model.name, model.feed, model.image);
279− imageDownloader.feed = model.feed;
280− imageDownloader.download(model.image);
279+ imageDownloader.addDownload(model.feed, model.image)
281280 } else {
282281 var db = Podcasts.init();
283282 db.transaction(function (tx) {
@@ -440,8 +439,7 @@ Page {
440439
441440 if(name != "") {
442441 Podcasts.subscribe(artist, name, feed, image);
443− imageDownloader.feed = feed;
444− imageDownloader.download(image);
442+ imageDownloader.addDownload(feed, image)
445443 tabs.selectedTabIndex = 2;
446444 } else {
447445 PopupUtils.open(subscribeFailedDialog);
app/ui/SettingsPage.qml+189
@@ -19,7 +19,9 @@
1919import QtQuick 2.4
2020import Ubuntu.Components 1.3
2121import Ubuntu.Components.Popups 1.3
22+import QtQuick.LocalStorage 2.0
2223import "../components"
24+import "../podcasts.js" as Podcasts
2325
2426Page {
2527 id: settingsPage
@@ -210,6 +212,103 @@ Page {
210212 onClicked: mainStack.push(Qt.resolvedUrl("../settings/DownloadSetting.qml"))
211213 }
212214
215+ ListItem {
216+ id: refreshArtListItem
217+
218+ property int numberOfPodcasts: 0
219+ property int queueLength: imageDownloader.queueLength
220+
221+ ListItemLayout {
222+ id: refreshArt
223+ title.text: i18n.tr("Refresh podcast artwork")
224+ title.color: podbird.appTheme.baseText
225+ summary.text: i18n.tr("Update all podcasts artwork and fix missing ones (this only works with podcasts added via iTunes® search)")
226+ summary.color: podbird.appTheme.baseSubText
227+ summary.maximumLineCount: 3
228+ ProgressionSlot{}
229+ }
230+
231+ CustomProgressBar {
232+ id: progressBar
233+ anchors { top: refreshArt.bottom; left: parent.left; right: parent.right; leftMargin: units.gu(2); rightMargin: units.gu(2) }
234+ progress: (refreshArtListItem.numberOfPodcasts - refreshArtListItem.queueLength) * (100 / refreshArtListItem.numberOfPodcasts)
235+ height: progress > 0 && progress < 100 ? units.dp(5) : 0
236+ }
237+
238+ divider.visible: false
239+ height: refreshArt.height + progressBar.height
240+ onClicked: {
241+ var db = Podcasts.init()
242+ db.transaction(function (tx) {
243+ var rs = tx.executeSql("SELECT rowid, * FROM Podcast ORDER BY name ASC");
244+ refreshArtListItem.numberOfPodcasts = rs.rows.length
245+ for (var i=0; i < rs.rows.length; i++) {
246+ var podcast = rs.rows.item(i);
247+ settingsPage.getPodcastCoverArt(podcast.name, podcast.feed, podcast.image)
248+ }
249+ });
250+ }
251+ }
252+
253+ HeaderListItem {
254+ title.text: i18n.tr("Storage Settings")
255+ }
256+
257+ ListItem {
258+ id: orphanListItem
259+
260+ property int fileCount: 0
261+ property int linkCount: 0
262+
263+ ListItemLayout {
264+ id: orphanLayout
265+ title.text: i18n.tr("Delete orphaned files and links")
266+ title.color: podbird.appTheme.baseText
267+ summary.text: i18n.tr("Free space by removing orphaned downloaded files and links")
268+ summary.color: podbird.appTheme.baseSubText
269+ ProgressionSlot {}
270+ }
271+
272+ CustomProgressBar {
273+ id: orphanProgressBar
274+ progress: 200
275+ anchors { top: orphanLayout.bottom; left: parent.left; right: parent.right; leftMargin: units.gu(2); rightMargin: units.gu(2) }
276+ height: indeterminateProgress ? units.dp(5) : 0
277+ }
278+
279+ divider.visible: false
280+ height: orphanLayout.height + orphanProgressBar.height
281+ onClicked: {
282+ orphanProgressBar.indeterminateProgress = true
283+ settingsPage.removeOrphans()
284+ orphanProgressBar.indeterminateProgress = false
285+ var popup = PopupUtils.open(cleanUpDialog, settingsPage);
286+ popup.orphanCount = orphanListItem.fileCount + orphanListItem.linkCount
287+ }
288+ }
289+
290+ Component {
291+ id: cleanUpDialog
292+ Dialog {
293+ id: dialogInternal
294+
295+ property int orphanCount: 0
296+
297+ title: orphanCount > 0 ? i18n.tr("Removed Orphaned files and links") : i18n.tr("No Orphans found!")
298+ text: orphanCount > 0 ? i18n.tr("All orphaned files have been deleted to recover disk space. Orphaned links \
299+pointing at invalid files have also been cleaned up.")
300+ : i18n.tr("No orphaned files have been found to recover disk space. Podbird database is clean.")
301+
302+ Button {
303+ text: i18n.tr("Close")
304+ color: podbird.appTheme.positiveActionButton
305+ onClicked: {
306+ PopupUtils.close(dialogInternal)
307+ }
308+ }
309+ }
310+ }
311+
213312 HeaderListItem {
214313 // TRANSLATORS: Shortened form of "Miscellaneous" which is shown to denote other setting options
215314 // that doesn't fit into any other category.
@@ -238,6 +337,96 @@ Page {
238337 }
239338 }
240339 }
340+
341+ function getPodcastCoverArt(name, feedUrl, image) {
342+ var coverArt
343+ var url = "https://itunes.apple.com/search?term=" + name + "&media=podcast&entity=podcast"
344+ var xhr = new XMLHttpRequest;
345+ xhr.open("GET", url);
346+ xhr.onreadystatechange = function() {
347+ if (xhr.readyState === XMLHttpRequest.DONE) {
348+ var json = JSON.parse(xhr.responseText);
349+ var db = Podcasts.init();
350+
351+ for(var i in json.results) {
352+ if (name == json.results[i].trackName) {
353+ if (feedUrl == json.results[i].feedUrl) {
354+ fileManager.deleteFile(image);
355+ coverArt = json.results[i].artworkUrl600
356+ if (coverArt) {
357+ imageDownloader.addDownload(feedUrl, coverArt)
358+ }
359+ }
360+ }
361+ }
362+ }
363+ }
364+ xhr.send();
365+ }
366+
367+ function removeOrphans() {
368+ var filelist = fileManager.getDownloadedEpisodes()
369+
370+ orphanListItem.fileCount = 0
371+ orphanListItem.linkCount = 0
372+
373+ var db = Podcasts.init()
374+ db.transaction(function (tx) {
375+ var rs = tx.executeSql("SELECT rowid, * FROM Podcast ORDER BY name ASC");
376+
377+ // Go through every downloaded file and check if they are orphaned
378+ for (var l = 0; l < filelist.length; l++) {
379+ var isOrphanFile = true
380+
381+ // Go through every podcast
382+ for (var i=0; i < rs.rows.length; i++) {
383+ var podcast = rs.rows.item(i);
384+ var rs2 = tx.executeSql("SELECT rowid, * FROM Episode WHERE podcast=?", [podcast.rowid]);
385+
386+ // Go through every episode in the podcast
387+ for (var j=0; j < rs2.rows.length; j++) {
388+ var episode = rs2.rows.item(j);
389+ if (episode.downloadedfile) {
390+ if(fileManager.podcastDirectory + "/" + filelist[l] === episode.downloadedfile) {
391+ isOrphanFile = false
392+ }
393+ }
394+ }
395+ }
396+
397+ if (isOrphanFile) {
398+ orphanListItem.fileCount++
399+ fileManager.deleteFile(fileManager.podcastDirectory + "/" + filelist[l])
400+ console.log("[LOG]: Removed Orphan File: " + fileManager.podcastDirectory + "/" + filelist[l])
401+ }
402+ }
403+
404+ // Go through every podcast in the db and find orphaned links
405+ for (i=0; i < rs.rows.length; i++) {
406+ var isOrphanLink = true
407+ podcast = rs.rows.item(i);
408+ rs2 = tx.executeSql("SELECT rowid, * FROM Episode WHERE podcast=?", [podcast.rowid]);
409+
410+ // Go through every episode in the podcast
411+ for (j=0; j < rs2.rows.length; j++) {
412+ episode = rs2.rows.item(j);
413+ if (episode.downloadedfile) {
414+ for (l = 0; l < filelist.length; l++) {
415+ if(episode.downloadedfile === fileManager.podcastDirectory + "/" + filelist[l]) {
416+ isOrphanLink = false
417+ }
418+ }
419+
420+ if (isOrphanLink) {
421+ orphanListItem.linkCount++
422+ tx.executeSql("UPDATE Episode SET downloadedfile = NULL WHERE guid = ?", [episode.guid]);
423+ console.log("[LOG]: Removed Orphan Link: " + episode.downloadedfile)
424+ }
425+ }
426+ }
427+ }
428+ });
429+ }
241430}
242431
243432
backend/modules/Podbird/filemanager.cpp+18−5
@@ -24,7 +24,8 @@
2424#include "filemanager.h"
2525
2626FileManager::FileManager(QObject *parent):
27− QObject(parent)
27+ QObject(parent),
28+ m_podcastDir(QStandardPaths::writableLocation(QStandardPaths::DataLocation) + QDir::separator() + "podcasts")
2829{
2930 // Remove any downloads that haven't been collected previously (because we were closed before they finished)
3031 QString downloadManagerPath = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QDir::separator() + "ubuntu-download-manager" + QDir::separator() + "com.mikeasoft.podbird";
@@ -38,6 +39,11 @@ FileManager::~FileManager() {
3839
3940}
4041
42+QString FileManager::podcastDirectory() const
43+{
44+ return m_podcastDir;
45+}
46+
4147void FileManager::deleteFile(QString path) {
4248 QFile file(path);
4349 if (file.exists()) {
@@ -46,17 +52,16 @@ void FileManager::deleteFile(QString path) {
4652}
4753
4854QString FileManager::saveDownload(QString origPath) {
49− QString destination = QStandardPaths::writableLocation(QStandardPaths::DataLocation) + QDir::separator() + "podcasts";
50− QDir destDir(destination);
55+ QDir destDir(m_podcastDir);
5156 if(!destDir.exists()) {
52− destDir.mkpath(destination);
57+ destDir.mkpath(m_podcastDir);
5358 }
5459 QFileInfo fi(origPath);
5560 QFile *destFile;
5661 QString filePath;
5762 int attempts = 0;
5863 do {
59− filePath = destination + QDir::separator() + fi.fileName();
64+ filePath = m_podcastDir + QDir::separator() + fi.fileName();
6065 if (attempts > 0) {
6166 filePath += "." + QString::number(attempts);
6267 }
@@ -66,3 +71,11 @@ QString FileManager::saveDownload(QString origPath) {
6671 QFile::rename(origPath, filePath);
6772 return filePath;
6873}
74+
75+QStringList FileManager::getDownloadedEpisodes() {
76+ QDir destDir(m_podcastDir);
77+ QStringList filters;
78+ filters << "*.mp3" << "*.mp3.*" << "*.m4a" << "*.m4a.*" << "*.ogg" << "*.ogg.*" << "*.oga" << "*.oga.*" << "*.wma";
79+ destDir.setNameFilters(filters);
80+ return destDir.entryList();
81+}
backend/modules/Podbird/filemanager.h+11
@@ -25,13 +25,24 @@ class FileManager : public QObject
2525{
2626 Q_OBJECT
2727
28+ // READONLY Property to return the podcast directory path
29+ Q_PROPERTY( QString podcastDirectory
30+ READ podcastDirectory
31+ CONSTANT)
32+
2833public:
2934 explicit FileManager(QObject *parent = 0);
3035 ~FileManager();
3136
37+ QString podcastDirectory() const;
38+
3239public:
3340 Q_INVOKABLE void deleteFile(QString path);
3441 Q_INVOKABLE QString saveDownload(QString path);
42+ Q_INVOKABLE QStringList getDownloadedEpisodes();
43+
44+private:
45+ QString m_podcastDir;
3546};
3647
3748#endif // FILEMANAGER_H