From 1c7650d20baf243b102442d29d2edbf6411d2522 Mon Sep 17 00:00:00 2001 From: Thilo Kogge Date: Sun, 13 Nov 2022 22:22:42 +0100 Subject: [PATCH 1/8] Added setting for episode image actions --- qml/components/EpisodeImage.qml | 35 ++++++-- qml/components/QueueHandlerPython.qml | 10 +++ qml/harbour-podqast.qml | 10 ++- qml/pages/Settings.qml | 30 +++++++ translations/harbour-podqast-de.ts | 118 ++++++++++++++++---------- translations/harbour-podqast-es.ts | 116 +++++++++++++++---------- translations/harbour-podqast-fr.ts | 116 +++++++++++++++---------- translations/harbour-podqast-sv.ts | 116 +++++++++++++++---------- translations/harbour-podqast-zh_CN.ts | 116 +++++++++++++++---------- translations/harbour-podqast.ts | 116 +++++++++++++++---------- 10 files changed, 516 insertions(+), 267 deletions(-) diff --git a/qml/components/EpisodeImage.qml b/qml/components/EpisodeImage.qml index 926dbf3..0c693ad 100644 --- a/qml/components/EpisodeImage.qml +++ b/qml/components/EpisodeImage.qml @@ -15,12 +15,35 @@ Thumbnail { MouseArea { anchors.fill: parent onClicked: { - if (model.id === playerHandler.firstid) { - playerHandler.playpause() - } else { - queuehandler.queueInsertTop(model.id, function () { - playerHandler.play() - }) + switch (episodeImageClickActionConf.value) { + case 0: + queuehandler.playAndSetPositionInQueue(model.id) + break + case 1: + pageStack.push(Qt.resolvedUrl("../pages/PodpostList.qml"), { + "url": model.podcast_url + }) + break + case 2: + pageStack.push(Qt.resolvedUrl("../pages/PostDescription.qml"), { + "title": model.title, + "detail": model.detail, + "length": model.length, + "date": model.date, + "duration": model.duration, + "href": model.link + }) + break + case 3: + queuehandler.queueInsertNext(model.id) + break + case 4: + queuehandler.queueInsertBottom(model.id) + break + default: + console.error( + "unexpected config value for episodeImageClickActionConf: " + + episodeImageClickActionConf.value) } } } diff --git a/qml/components/QueueHandlerPython.qml b/qml/components/QueueHandlerPython.qml index bdbd3cf..a9ee02c 100644 --- a/qml/components/QueueHandlerPython.qml +++ b/qml/components/QueueHandlerPython.qml @@ -107,6 +107,16 @@ Python { function () {}) } + function playAndSetPositionInQueue(episodeid) { + if (episodeid === playerHandler.firstid && !playerHandler.isPlaying) { + playerHandler.play() + } else { + queueInsertTop(episodeid, function () { + playerHandler.play() + }) + } + } + onError: { console.log('python error: ' + traceback) } diff --git a/qml/harbour-podqast.qml b/qml/harbour-podqast.qml index 851b2b4..84f2315 100644 --- a/qml/harbour-podqast.qml +++ b/qml/harbour-podqast.qml @@ -265,6 +265,12 @@ ApplicationWindow { defaultValue: true } + ConfigurationValue { + id: episodeImageClickActionConf + key: "/apps/ControlPanel/podqast/episodeImageClickAction" + defaultValue: 0 + } + function refresh() { connmanWifi.getProperties() } @@ -317,7 +323,7 @@ ApplicationWindow { feedparserhandler.refreshPodcasts() } - getProperties() + connmanWifi.getProperties() } DBusAdaptor { @@ -522,7 +528,7 @@ ApplicationWindow { artUrl: playerHandler.playicon } - loopStatus: Mpris.None + loopStatus: Mpris.LoopNone shuffle: false onPauseRequested: playerHandler.pause() onPlayPauseRequested: playerHandler.playpause() diff --git a/qml/pages/Settings.qml b/qml/pages/Settings.qml index 555b361..e694569 100644 --- a/qml/pages/Settings.qml +++ b/qml/pages/Settings.qml @@ -34,6 +34,34 @@ Dialog { color: Theme.highlightColor } + ComboBox { + anchors { + left: parent.left + right: parent.right + margins: Theme.paddingMedium + } + id: imageClickAction + width: parent.width + label: qsTr("When touching episode image...") + menu: ContextMenu { + MenuItem { + text: qsTr("Play Episode") + } + MenuItem { + text: qsTr("Show Podcast Episodes") + } + MenuItem { + text: qsTr("Show Episode Info") + } + MenuItem { + text: qsTr("Add To Queue Top") + } + MenuItem { + text: qsTr("Add To Queue Bottom") + } + } + } + ComboBox { anchors { left: parent.left @@ -365,6 +393,7 @@ Dialog { autoPlayQueue.checked = autoPlayNextInQueue.value // downLimit.value = downLimitConf.value // refreshTime = refreshTimeConf.value + imageClickAction.currentIndex = episodeImageClickActionConf.value } onAccepted: { // useGpodderConf.value = useGpodder.checked @@ -388,5 +417,6 @@ Dialog { markListenedEndThresholdConf.value = listenedThreshold.value // downLimitConf.value = downLimit.value // refreshTimeConf.value = refreshTime + episodeImageClickActionConf.value = imageClickAction.currentIndex } } diff --git a/translations/harbour-podqast-de.ts b/translations/harbour-podqast-de.ts index d5c97f5..2fd4409 100644 --- a/translations/harbour-podqast-de.ts +++ b/translations/harbour-podqast-de.ts @@ -761,154 +761,184 @@ + When touching episode image... + Beim tippen auf ein Episodenbild... + + + + Show Podcast Episodes + Zeige andere Episoden des Podcasts + + + Move new post to Neuen Beitrag - - + + Inbox in den Eingang - - + + Archive in das Archiv - + Automatic post limit Höchstgrenze automatisierter Beiträge - + Refresh time Aktualisierungs-Intervall - + Audio playrate Abspielgeschwindigkeit - + off aus - + Time left to ignore for listened-mark - + Download/Streaming Laden/Stream - + Development Entwicklung - + Experimental features Experimentelle Funktionen - - + + Top of Playlist an den Anfang der Playlist + + + Play Episode + Spiele die Episode + - + Show Episode Info + Zeige die Episodenbeschreibung + + + + Add To Queue Top + An den Anfang der Playlist + + + + Add To Queue Bottom + An das Ende der Playlist + + + + Bottom of Playlist ans Ende der Playlist - + All - + Alle - + s - + Sleep timer Schlummer-Uhr - + min min - + External Audio Fremde Audiodateien - + Allow external audio Erlaube fremde Audiodateien - + Will take data from Hole Daten von - + Move external audio to Externe Audiodateien - + Download Playlist Posts Herunterladen von Playlist-Beiträgen - + Download on Mobile Bei Mobilfunk laden - + Keep Favorites downloaded Favoriten speichern - + Will save data in Speicher Daten in - + System System - + Audio viewable in system Audio im System sichbar - + Auto-play next episode in queue - + Force refresh of old episodes @@ -1021,50 +1051,50 @@ harbour-podqast - + New posts available Neue Beiträge verfügbar - + Click to view updates Klicken um Aktualisierungen anzusehen - + New Posts are available. Click to view. Neue Beiträge verfügbar. Klicke zum Betrachten. - - + + PodQast message PodQast-Nachricht - + New PodQast message Neue PodQast-Nachricht - + Podcasts imported Podcasts importiert - - + + Podcasts imported from OPML Podcasts vom OPML importiert - + Error Fehler - - + + Audio File not existing Audiodatei existiert nicht diff --git a/translations/harbour-podqast-es.ts b/translations/harbour-podqast-es.ts index 1837e03..e7eb02b 100644 --- a/translations/harbour-podqast-es.ts +++ b/translations/harbour-podqast-es.ts @@ -761,152 +761,182 @@ + When touching episode image... + + + + + Show Podcast Episodes + + + + Move new post to Mover nueva publicación a - - + + Inbox Entradas - - + + Archive Almacén - + Automatic post limit Límite de publicaciones automáticas - + Refresh time Tiempo de actualización - + Audio playrate Velocidad de reproducción - + off apagado - + Time left to ignore for listened-mark - + Download/Streaming Descarga/Transmisión - + Development Desarrollo - + Experimental features Funciones experimentales - - + + Top of Playlist Al principio de la lista + + + Play Episode + + - + Show Episode Info + + + + + Add To Queue Top + + + + + Add To Queue Bottom + + + + + Bottom of Playlist Al final de la lista - + All - + s - + Sleep timer - + min - + External Audio Audio externo - + Allow external audio Permitir audio externo - + Will take data from Cogerá datos de - + Move external audio to Mover audio externo a - + Download Playlist Posts Descargar publicaciones de la lista - + Download on Mobile Descargar en el móvil - + Keep Favorites downloaded Conservar favoritos descargados - + Will save data in Guardará los datos en - + System Sistema - + Audio viewable in system Audio visible en el sistema - + Auto-play next episode in queue - + Force refresh of old episodes @@ -1019,50 +1049,50 @@ harbour-podqast - + New posts available Nuevas publicaciones disponibles - + Click to view updates Haz clic para ver actualizaciones - + New Posts are available. Click to view. Hay nuevas publicaciones. Haz clic para verlas. - - + + PodQast message Mensaje de PodQast - + New PodQast message Nuevo mensaje de PodQast - + Podcasts imported podcasts importados - - + + Podcasts imported from OPML podcasts importados desde OPML - + Error Error - - + + Audio File not existing El archivo de audio no existe diff --git a/translations/harbour-podqast-fr.ts b/translations/harbour-podqast-fr.ts index 92810dc..fc9a287 100644 --- a/translations/harbour-podqast-fr.ts +++ b/translations/harbour-podqast-fr.ts @@ -762,154 +762,184 @@ + When touching episode image... + + + + + Show Podcast Episodes + + + + Move new post to Transférer les nouveaux articles - - + + Inbox Boîte aux lettres - - + + Archive Archives - + Automatic post limit Limite automatique du nombre d'articles - + Refresh time Temps de rafraîchissement - + Audio playrate Vitesse de lecture - + off Désactivé - + Time left to ignore for listened-mark - + Download/Streaming Téléchargement/Streaming - + Development Développement - + Experimental features Fonctionnalités expérimentales - - + + Top of Playlist En début de Playlist + + + Play Episode + + - + Show Episode Info + + + + + Add To Queue Top + + + + + Add To Queue Bottom + + + + + Bottom of Playlist En fin de la Playlist - + All - + s - + Sleep timer Minuterie avant coupure - + min - + External Audio Fichiers Audios Externes - + Allow external audio Autoriser les fichiers externes - + Will take data from Prendra les données depuis - + Move external audio to Transférer les fichiers externes - + Download Playlist Posts Télécharger les Playlists d'Articles - + Download on Mobile Télécharger hors Wifi - + Keep Favorites downloaded Conserver les favoris déjà téléchargés - + Will save data in Sauvegardera les données dans - + System Système - + Audio viewable in system Audio visible dans le système - + Auto-play next episode in queue - + Force refresh of old episodes @@ -1022,50 +1052,50 @@ harbour-podqast - + New posts available Nouveaux articles disponibles - + Click to view updates Cliquez pour voir les mises à jour - + New Posts are available. Click to view. De nouveaux articles sont diponibles. Cliquez pour voir. - - + + PodQast message Message PodQast - + New PodQast message Nouveau message PodQast - + Podcasts imported Podcasts importés - - + + Podcasts imported from OPML Podcasts importés depuis OPML - + Error Erreur - - + + Audio File not existing Le fichier audio n'existe pas diff --git a/translations/harbour-podqast-sv.ts b/translations/harbour-podqast-sv.ts index 36e2a48..7e0186d 100644 --- a/translations/harbour-podqast-sv.ts +++ b/translations/harbour-podqast-sv.ts @@ -761,154 +761,184 @@ + When touching episode image... + + + + + Show Podcast Episodes + + + + Move new post to Flytta ny post till - - + + Inbox Inkorg - - + + Archive Arkiv - + Automatic post limit Automatisk postbegränsning - + Refresh time Uppdateringstid - + Audio playrate Spelningsfrekvens - + off av - + Time left to ignore for listened-mark - + Download/Streaming Nerladdning/Strömmning - + Development Utveckling - + Experimental features Experimentella funktioner - - + + Top of Playlist Först i spelningslistan + + + Play Episode + + - + Show Episode Info + + + + + Add To Queue Top + + + + + Add To Queue Bottom + + + + + Bottom of Playlist Sist i spelningslistan - + All - + s - + Sleep timer Insomningsur - + min min - + External Audio Externt ljud - + Allow external audio Tillåt externt ljud - + Will take data from Hämtar data från - + Move external audio to Flytta externt ljud till - + Download Playlist Posts Ladda ner spelningslistans poster - + Download on Mobile Ladda ner på mobilanslutning - + Keep Favorites downloaded Behåll favoriter nerladdade - + Will save data in Sparar data i - + System System - + Audio viewable in system Ljud som visas i systemet - + Auto-play next episode in queue - + Force refresh of old episodes @@ -1021,50 +1051,50 @@ harbour-podqast - + New posts available Nya poster tillgängliga - + Click to view updates Tryck för att visa uppdateringar - + New Posts are available. Click to view. Det finns nya poster. Tryck för att visa. - - + + PodQast message PodQast-meddelande - + New PodQast message Nytt PodQast-meddelande - + Podcasts imported poddar importerade - - + + Podcasts imported from OPML poddar importerade från OPML - + Error Fel - - + + Audio File not existing Ljudfilen finns inte diff --git a/translations/harbour-podqast-zh_CN.ts b/translations/harbour-podqast-zh_CN.ts index 0f9ece3..e513614 100644 --- a/translations/harbour-podqast-zh_CN.ts +++ b/translations/harbour-podqast-zh_CN.ts @@ -761,152 +761,182 @@ + When touching episode image... + + + + + Show Podcast Episodes + + + + Move new post to 移动新内容到 - - + + Inbox 收件箱 - - + + Archive 存档 - + Automatic post limit 自动限制内容 - + Refresh time 刷新时间 - + Audio playrate 音频播放比率 - + off 关闭 - + Time left to ignore for listened-mark - + Download/Streaming 下载流媒体 - + Development 开发 - + Experimental features 实验功能 - - + + Top of Playlist 播放列表顶部 + + + Play Episode + + - + Show Episode Info + + + + + Add To Queue Top + + + + + Add To Queue Bottom + + + + + Bottom of Playlist 播放列表底部 - + All - + s - + Sleep timer 睡眠计时器 - + min 分钟 - + External Audio 外部音频 - + Allow external audio 允许外部音频 - + Will take data from 将会获取数据自 - + Move external audio to 移动外部音频到 - + Download Playlist Posts 下载播放列表内容 - + Download on Mobile 通过移动网络下载 - + Keep Favorites downloaded 保留下载的收藏 - + Will save data in 将会保存数据到 - + System 系统 - + Audio viewable in system 音频在系统可见 - + Auto-play next episode in queue - + Force refresh of old episodes @@ -1019,50 +1049,50 @@ harbour-podqast - + New posts available 有可获取的新内容 - + Click to view updates 点击以查看更新 - + New Posts are available. Click to view. 有可用新内容,点击以查看。 - - + + PodQast message PodQast 消息 - + New PodQast message 新 PodQast 消息 - + Podcasts imported 播客已导入 - - + + Podcasts imported from OPML 已从 OPML 导入播客 - + Error 错误 - - + + Audio File not existing 音频文件不存在 diff --git a/translations/harbour-podqast.ts b/translations/harbour-podqast.ts index 4a59c7a..948c445 100644 --- a/translations/harbour-podqast.ts +++ b/translations/harbour-podqast.ts @@ -761,154 +761,184 @@ + When touching episode image... + + + + + Show Podcast Episodes + + + + Move new post to Move new post to - - + + Inbox Inbox - - + + Archive Archive - + Automatic post limit Automatic post limit - + Refresh time Refresh time - + Audio playrate Audio playrate - + off off - + Time left to ignore for listened-mark - + Download/Streaming Download/Streaming - + Development Development - + Experimental features Experimental features - - + + Top of Playlist Top of Playlist + + + Play Episode + + - + Show Episode Info + + + + + Add To Queue Top + + + + + Add To Queue Bottom + + + + + Bottom of Playlist Bottom of Playlist - + All - + s - + Sleep timer Sleep timer - + min min - + External Audio External Audio - + Allow external audio Allow external audio - + Will take data from Will take data from - + Move external audio to Move external audio to - + Download Playlist Posts Download Playlist Posts - + Download on Mobile Download on Mobile - + Keep Favorites downloaded Keep Favorites downloaded - + Will save data in Will save data in - + System System - + Audio viewable in system Audio viewable in system - + Auto-play next episode in queue - + Force refresh of old episodes @@ -1021,50 +1051,50 @@ harbour-podqast - + New posts available New posts available - + Click to view updates Click to view updates - + New Posts are available. Click to view. New Posts are available. Click to view. - - + + PodQast message PodQast message - + New PodQast message New PodQast message - + Podcasts imported Podcasts imported - - + + Podcasts imported from OPML Podcasts imported from OPML - + Error Error - - + + Audio File not existing Audio File not existing -- GitLab From ce2db8e1704b1c16dd6d8663309409ba0b26522f Mon Sep 17 00:00:00 2001 From: Thilo Kogge Date: Sun, 13 Nov 2022 23:40:12 +0100 Subject: [PATCH 2/8] PostDescription shows now episode image, repurposing logo_url and logo_path for the actual logos of the episodes. --- python/podcast/feedutils.py | 4 +-- python/podcast/podcast.py | 2 +- python/podcast/podpost.py | 43 +++++++++++---------------- qml/components/EpisodeImage.qml | 5 ++-- qml/components/PlayDockedPanel.qml | 3 +- qml/components/PlayerHandler.qml | 2 +- qml/components/PostListItem.qml | 5 ++-- qml/pages/PostDescription.qml | 11 +++++++ qml/pages/Queue.qml | 4 +-- translations/harbour-podqast-de.ts | 12 ++++---- translations/harbour-podqast-es.ts | 12 ++++---- translations/harbour-podqast-fr.ts | 12 ++++---- translations/harbour-podqast-sv.ts | 12 ++++---- translations/harbour-podqast-zh_CN.ts | 12 ++++---- translations/harbour-podqast.ts | 12 ++++---- 15 files changed, 77 insertions(+), 74 deletions(-) diff --git a/python/podcast/feedutils.py b/python/podcast/feedutils.py index 8962442..1744c5a 100644 --- a/python/podcast/feedutils.py +++ b/python/podcast/feedutils.py @@ -31,8 +31,6 @@ def fetch_feed(published, url) -> FeedParserDict: else: agent = util.user_agent feed: FeedParserDict = feedparser.parse(url, agent=agent, modified=time.gmtime(published)) - if feed.status == 304: - raise NotModified() if feed.bozo != 0: exc: Exception = feed.bozo_exception if type(exc) != feedparser.CharacterEncodingOverride: @@ -40,6 +38,8 @@ def fetch_feed(published, url) -> FeedParserDict: "Podcast init: error in parsing feed %s", str(type(exc)), exc_info=exc ) raise FeedFetchingError(exc.message if hasattr(exc, 'message') else "message_missing") + if feed.status == 304: + raise NotModified() if "itunes_new-feed-url" in feed.feed and feed.feed["itunes_new-feed-url"] != url: raise NewFeedUrlError(feed.feed["itunes_new-feed-url"]) logger.info( diff --git a/python/podcast/podcast.py b/python/podcast/podcast.py index 56cf1ce..745735e 100644 --- a/python/podcast/podcast.py +++ b/python/podcast/podcast.py @@ -191,7 +191,7 @@ class Podcast(BaseModel): from podcast.podpost import Podpost return [post.id for post in self.episodes.select(Podpost.id).order_by(Podpost.published)] - def logo(self): + def logo(self) -> str: if self.logo_path: image = self.logo_path else: diff --git a/python/podcast/podpost.py b/python/podcast/podpost.py index 72cb6fd..fdfe3ee 100644 --- a/python/podcast/podpost.py +++ b/python/podcast/podpost.py @@ -22,7 +22,7 @@ from urllib.parse import urlparse import html2text from email.utils import mktime_tz, parsedate_tz from calendar import timegm -from typing import List, Iterator, Optional, Dict +from typing import List, Iterator, Optional, Dict, Union from podcast.factory import BaseFactory import pyotherside @@ -42,15 +42,15 @@ class Podpost(BaseModel): One podcast post/episode. Most fields are persisted in the database """ - guid: str = CharField(index=True) + guid: Union[str,TextField] = CharField(index=True) id: int = AutoField(primary_key=True) # - author: str = CharField(default="") + author: Union[str,TextField] = CharField(default="") duration: int = IntegerField(null=True, help_text="in ms") favorite: bool = BooleanField(default=False) - file_path: str = TextField(null=True) + file_path: Union[str,TextField] = TextField(null=True) # podcast file url - href: TextField = TextField(default="", index=True) - htmlpart: str = TextField(null=True) + href: Union[str,TextField] = TextField(default="", index=True) + htmlpart: Union[str,TextField] = TextField(null=True) # when we entered this post into our db insert_date = FloatField(default=lambda: datetime.datetime.now().timestamp()) # if the post comes from the external folder @@ -58,9 +58,8 @@ class Podpost(BaseModel): # filesize length: int = IntegerField(default=0) listened: bool = BooleanField(default=False, null=False, help_text="Has been played already") - link: str = TextField(default="") - logo_path: str = TextField(null=True) - logo_url: str = TextField(null=True) + link: Union[str,TextField] = TextField(default="") + logo_url: Union[str,TextField] = TextField(null=True) # download percentage percentage: float = FloatField(default=0) plainpart: TextField = TextField(default="") @@ -70,9 +69,9 @@ class Podpost(BaseModel): published = DateTimeField() # play_state state: int = IntegerField(default=STOP) - title: TextField = TextField() + title: Union[str,TextField] = TextField() # mimetype - type: str = TextField(null=True) + type: Union[str,TextField] = TextField(null=True) def __init__(self, *args, **kwargs): """ @@ -94,11 +93,11 @@ class Podpost(BaseModel): assert logo_url post = cls() post.podcast = podcast - post.logo_url = logo_url - if logo_url and len(logo_url) > 0 and logo_url[0] == "/": - post.logo_path = logo_url - else: - post.logo_path = None + if 'image' in entry.keys(): + image_url: str = entry["image"].href + if image_url and isinstance(image_url,str) and image_url.startswith("http"): + post.logo_url = image_url + if 'published_parsed' in entry.keys(): post.published = timegm(entry['published_parsed']) else: @@ -213,8 +212,6 @@ class Podpost(BaseModel): inqueue = QueueFactory().get_queue().is_in_queue(self) - image = self.get_image_descriptor() - section = util.format_full_date(self.published) date = self.published asection = util.format_full_date(self.insert_date) @@ -235,7 +232,8 @@ class Podpost(BaseModel): "title": self.title, "podcast_url": self.podcast.url if not self.isaudio else "", "audio_url": self.href, - "logo_url": image, + "podcast_logo": self.podcast.logo(), + "episode_logo": self.logo_url, "link": self.link, "description": self.plainpart, "detail": self.htmlpart, @@ -256,13 +254,6 @@ class Podpost(BaseModel): "podcastTitle": self.podcast.title if not self.isaudio else "" } - def get_image_descriptor(self): - if self.logo_path != None: - image = self.logo_path - else: - image = self.logo_url - return image - def download_audio(self): """ Download the audio stuff diff --git a/qml/components/EpisodeImage.qml b/qml/components/EpisodeImage.qml index 0c693ad..f24ce90 100644 --- a/qml/components/EpisodeImage.qml +++ b/qml/components/EpisodeImage.qml @@ -31,7 +31,8 @@ Thumbnail { "length": model.length, "date": model.date, "duration": model.duration, - "href": model.link + "href": model.link, + "episode_logo": model.episode_logo }) break case 3: @@ -51,7 +52,7 @@ Thumbnail { height: Theme.iconSizeLarge sourceSize.width: Theme.iconSizeLarge sourceSize.height: Theme.iconSizeLarge - source: logo_url == "" ? "../../images/podcast.png" : logo_url + source: model.podcast_logo === "" ? "../../images/podcast.png" : model.podcast_logo Rectangle { id: dlstatus visible: showDownladingState diff --git a/qml/components/PlayDockedPanel.qml b/qml/components/PlayDockedPanel.qml index e336570..6f0c755 100644 --- a/qml/components/PlayDockedPanel.qml +++ b/qml/components/PlayDockedPanel.qml @@ -61,7 +61,8 @@ DockedPanel { "length": data.length, "date": data.date, "duration": data.duration, - "href": data.link + "href": data.link, + "episode_logo": data.episode_logo }) } } diff --git a/qml/components/PlayerHandler.qml b/qml/components/PlayerHandler.qml index 578e185..96ef6d6 100644 --- a/qml/components/PlayerHandler.qml +++ b/qml/components/PlayerHandler.qml @@ -103,7 +103,7 @@ Python { firsttitle = data.title firstPodcastTitle = data.podcastTitle chapters = chapterlist - playicon = data.logo_url + playicon = data.podcast_logo playtext = data.title durationFromDb = data.duration positionFromDb = data.position diff --git a/qml/components/PostListItem.qml b/qml/components/PostListItem.qml index fd763c1..8881ac8 100644 --- a/qml/components/PostListItem.qml +++ b/qml/components/PostListItem.qml @@ -11,7 +11,7 @@ ListItem { property double duration: model.duration property var postId: model.id property string link: model.link - property string logo_url: model.logo_url + property string logo_url: model.podcast_logo property double position: model.position property string description: model.description property bool favorite: model.favorite @@ -30,7 +30,8 @@ ListItem { "length": length, "date": date, "duration": duration, - "href": link + "href": link, + "episode_logo": model.episode_logo }) clip: true property bool isfavorite: favorite diff --git a/qml/pages/PostDescription.qml b/qml/pages/PostDescription.qml index ad06a99..64cd470 100644 --- a/qml/pages/PostDescription.qml +++ b/qml/pages/PostDescription.qml @@ -11,6 +11,7 @@ Page { property string length property string duration property string date + property string episode_logo // The effective value will be restricted by ApplicationWindow.allowedOrientations allowedOrientations: Orientation.All @@ -73,6 +74,16 @@ Page { font.pixelSize: Theme.fontSizeSmall color: Theme.highlightColor } + Image { + id: episode_image + anchors.horizontalCenter: parent.horizontalCenter + visible: episode_logo + width: Math.min(sourceSize.width, parent.width) + height: width + source: episode_logo + asynchronous: true + } + Label { id: detailcolumn width: parent.width diff --git a/qml/pages/Queue.qml b/qml/pages/Queue.qml index 6c64c22..641fd9d 100644 --- a/qml/pages/Queue.qml +++ b/qml/pages/Queue.qml @@ -18,12 +18,10 @@ Item { queuePostModel.clear() for (var i = 0; i < data.length; i++) { queuePostModel.append(data[i]) - if (i === 0) { - playerHandler.playicon = data[i].logo_url - } } if (i > 0) { playeropen = true + playerHandler.playicon = data[0].logo_url } else { playeropen = false } diff --git a/translations/harbour-podqast-de.ts b/translations/harbour-podqast-de.ts index 2fd4409..d19e9a6 100644 --- a/translations/harbour-podqast-de.ts +++ b/translations/harbour-podqast-de.ts @@ -680,7 +680,7 @@ PostDescription - + Open in browser Im Browser öffnen @@ -688,17 +688,17 @@ PostListItem - + playing - + listened - + remaining @@ -729,12 +729,12 @@ Queue - + No posts - + Pull down to Discover new podcasts or get posts from Inbox or Library diff --git a/translations/harbour-podqast-es.ts b/translations/harbour-podqast-es.ts index e7eb02b..73432c8 100644 --- a/translations/harbour-podqast-es.ts +++ b/translations/harbour-podqast-es.ts @@ -680,7 +680,7 @@ PostDescription - + Open in browser Abrir en el navegador @@ -688,17 +688,17 @@ PostListItem - + playing - + listened - + remaining @@ -729,12 +729,12 @@ Queue - + No posts - + Pull down to Discover new podcasts or get posts from Inbox or Library diff --git a/translations/harbour-podqast-fr.ts b/translations/harbour-podqast-fr.ts index fc9a287..963e9d5 100644 --- a/translations/harbour-podqast-fr.ts +++ b/translations/harbour-podqast-fr.ts @@ -681,7 +681,7 @@ PostDescription - + Open in browser Ouvrir dans le navigateur @@ -689,17 +689,17 @@ PostListItem - + playing - + listened - + remaining @@ -730,12 +730,12 @@ Queue - + No posts - + Pull down to Discover new podcasts or get posts from Inbox or Library diff --git a/translations/harbour-podqast-sv.ts b/translations/harbour-podqast-sv.ts index 7e0186d..4874cb8 100644 --- a/translations/harbour-podqast-sv.ts +++ b/translations/harbour-podqast-sv.ts @@ -680,7 +680,7 @@ PostDescription - + Open in browser Öppna i webbläsaren @@ -688,17 +688,17 @@ PostListItem - + playing - + listened - + remaining @@ -729,12 +729,12 @@ Queue - + No posts - + Pull down to Discover new podcasts or get posts from Inbox or Library diff --git a/translations/harbour-podqast-zh_CN.ts b/translations/harbour-podqast-zh_CN.ts index e513614..20e1050 100644 --- a/translations/harbour-podqast-zh_CN.ts +++ b/translations/harbour-podqast-zh_CN.ts @@ -680,7 +680,7 @@ PostDescription - + Open in browser 用浏览器打开 @@ -688,17 +688,17 @@ PostListItem - + playing - + listened - + remaining @@ -729,12 +729,12 @@ Queue - + No posts - + Pull down to Discover new podcasts or get posts from Inbox or Library diff --git a/translations/harbour-podqast.ts b/translations/harbour-podqast.ts index 948c445..a665e40 100644 --- a/translations/harbour-podqast.ts +++ b/translations/harbour-podqast.ts @@ -680,7 +680,7 @@ PostDescription - + Open in browser Open in browser @@ -688,17 +688,17 @@ PostListItem - + playing - + listened - + remaining @@ -729,12 +729,12 @@ Queue - + No posts - + Pull down to Discover new podcasts or get posts from Inbox or Library -- GitLab From 69d0c3b47fadd8aa78ad6485ebe25af3c465fca5 Mon Sep 17 00:00:00 2001 From: Thilo Kogge Date: Mon, 14 Nov 2022 00:52:41 +0100 Subject: [PATCH 3/8] downloading image when downloading audio, showing episode image on player page, on cover when episode is playing replaced fast forward/backward icons added fast forward to cover actions --- python/podcast/data_migration.py | 2 +- python/podcast/podcast.py | 2 +- python/podcast/podpost.py | 45 +++++++++++++++++---------- python/podcast/queue.py | 5 +-- qml/components/PlayDockedPanel.qml | 3 +- qml/components/PlayerHandler.qml | 8 +++-- qml/components/QueueHandlerPython.qml | 3 +- qml/cover/CoverPage.qml | 8 ++++- qml/harbour-podqast.qml | 2 +- qml/pages/Player.qml | 12 ++++--- qml/pages/Queue.qml | 2 +- translations/harbour-podqast-de.ts | 12 +++---- translations/harbour-podqast-es.ts | 12 +++---- translations/harbour-podqast-fr.ts | 12 +++---- translations/harbour-podqast-sv.ts | 12 +++---- translations/harbour-podqast-zh_CN.ts | 12 +++---- translations/harbour-podqast.ts | 12 +++---- 17 files changed, 94 insertions(+), 70 deletions(-) diff --git a/python/podcast/data_migration.py b/python/podcast/data_migration.py index 84aae24..d7f39ba 100644 --- a/python/podcast/data_migration.py +++ b/python/podcast/data_migration.py @@ -360,7 +360,7 @@ def migrate_podcast_v0_v1(podcast_dict: dict, strict=True) -> Tuple[Podcast, Dic logger.info("Found %d entries in old format", old_entry_count) for entry in feed.entries: try: - post = Podpost.from_feed_entry(podcast, entry, podcast_dict['url'], image) + post = Podpost.from_feed_entry(podcast, entry, podcast_dict['url']) except Exception as e: logger.exception("could not load post from podcast %s", podcast_dict['url']) if strict: diff --git a/python/podcast/podcast.py b/python/podcast/podcast.py index 745735e..7f2e25d 100644 --- a/python/podcast/podcast.py +++ b/python/podcast/podcast.py @@ -169,7 +169,7 @@ class Podcast(BaseModel): else: continue try: - post = Podpost.from_feed_entry(self, entry, self.url, image) + post = Podpost.from_feed_entry(self, entry, self.url) episode_count += 1 new_posts.append(post) except Exception as e: diff --git a/python/podcast/podpost.py b/python/podcast/podpost.py index fdfe3ee..c043a1d 100644 --- a/python/podcast/podpost.py +++ b/python/podcast/podpost.py @@ -43,10 +43,10 @@ class Podpost(BaseModel): Most fields are persisted in the database """ guid: Union[str,TextField] = CharField(index=True) - id: int = AutoField(primary_key=True) # + id: Union[int,AutoField] = AutoField(primary_key=True) # author: Union[str,TextField] = CharField(default="") - duration: int = IntegerField(null=True, help_text="in ms") - favorite: bool = BooleanField(default=False) + duration: Union[int,IntegerField] = IntegerField(null=True, help_text="in ms") + favorite: Union[bool,BooleanField] = BooleanField(default=False) file_path: Union[str,TextField] = TextField(null=True) # podcast file url href: Union[str,TextField] = TextField(default="", index=True) @@ -56,19 +56,20 @@ class Podpost(BaseModel): # if the post comes from the external folder isaudio: bool = BooleanField(default=False, help_text="Is episode from external folder source") # filesize - length: int = IntegerField(default=0) + length: Union[int,IntegerField] = IntegerField(default=0) listened: bool = BooleanField(default=False, null=False, help_text="Has been played already") link: Union[str,TextField] = TextField(default="") - logo_url: Union[str,TextField] = TextField(null=True) + logo_path: Union[str,TextField] = TextField(null=True) + logo_url: Union[str,TextField] = TextField(null=True) # download percentage percentage: float = FloatField(default=0) plainpart: TextField = TextField(default="") - position: int = IntegerField(default=0, help_text="in ms") - podcast = ForeignKeyField(Podcast, null=True, backref='episodes', lazy_load=True, on_delete='CASCADE') + position: Union[int,IntegerField] = IntegerField(default=0, help_text="in ms") + podcast: Union[Podcast,ForeignKeyField] = ForeignKeyField(Podcast, null=True, backref='episodes', lazy_load=True, on_delete='CASCADE') # when the post was published according to feed published = DateTimeField() # play_state - state: int = IntegerField(default=STOP) + state: Union[int,IntegerField] = IntegerField(default=STOP) title: Union[str,TextField] = TextField() # mimetype type: Union[str,TextField] = TextField(null=True) @@ -84,18 +85,17 @@ class Podpost(BaseModel): super().__init__(*args, **kwargs) @classmethod - def from_feed_entry(cls, podcast, entry, podurl, logo_url) -> 'Podpost': + def from_feed_entry(cls, podcast, entry, podurl) -> 'Podpost': """ Creates a podpost from a feed entry @rtype: Podpost """ assert podurl - assert logo_url post = cls() post.podcast = podcast if 'image' in entry.keys(): image_url: str = entry["image"].href - if image_url and isinstance(image_url,str) and image_url.startswith("http"): + if image_url and isinstance(image_url,str) and image_url.startswith("http") and image_url != podcast.logo_url: post.logo_url = image_url if 'published_parsed' in entry.keys(): @@ -233,7 +233,7 @@ class Podpost(BaseModel): "podcast_url": self.podcast.url if not self.isaudio else "", "audio_url": self.href, "podcast_logo": self.podcast.logo(), - "episode_logo": self.logo_url, + "episode_logo": self.get_episode_logo_path_or_url(), "link": self.link, "description": self.plainpart, "detail": self.htmlpart, @@ -254,6 +254,18 @@ class Podpost(BaseModel): "podcastTitle": self.podcast.title if not self.isaudio else "" } + def get_episode_logo_path_or_url(self): + if self.logo_path != None: + image = self.logo_path + else: + image = self.logo_url + return image + + def download(self): + yield from self.download_audio() + self.download_icon() + PodpostFactory().persist(self) + def download_audio(self): """ Download the audio stuff @@ -304,9 +316,7 @@ class Podpost(BaseModel): persist_log(LogType.Exception, what="episode download", title=self.title, exception=e) self.file_path = file_path - PodpostFactory().persist(self) - # TODO unify with podcasts download icon def download_icon(self): """ Download icon @@ -327,8 +337,6 @@ class Podpost(BaseModel): logo_path = os.path.join(iconpath, name) try: util.dl_from_url(self.logo_url, logo_path) - # for i in dl_from_url_progress(self.logo_url, logo_path): - # yield i except: logo_path = "" @@ -444,6 +452,9 @@ class Podpost(BaseModel): util.delete_file(self.file_path) self.percentage = 0 self.file_path = None + if self.logo_path: + util.delete_file(self.logo_path) + self.logo_path = None PodpostFactory().persist(self) def toggle_fav(self, do_download=False): @@ -472,7 +483,7 @@ class Podpost(BaseModel): if self.favorite and do_download: logger.info("need to download file") - for perc in self.download_audio(): + for perc in self.download(): pyotherside.send("downloading", self.id, perc) if self.file_path: diff --git a/python/podcast/queue.py b/python/podcast/queue.py index 3335156..8f40922 100644 --- a/python/podcast/queue.py +++ b/python/podcast/queue.py @@ -210,10 +210,11 @@ class Queue: if podpost in self.podposts: post = PodpostFactory().get_podpost(podpost) - for perc in post.download_audio(): + for perc in post.download(): yield perc + post.download_icon() if podpost == self.podposts[0] and post.file_path: - pyotherside.send("firstDownloaded", post.file_path) + pyotherside.send("firstDownloaded", post.file_path, post.logo_path) def download_all(self): """ diff --git a/qml/components/PlayDockedPanel.qml b/qml/components/PlayDockedPanel.qml index 6f0c755..d9427ce 100644 --- a/qml/components/PlayDockedPanel.qml +++ b/qml/components/PlayDockedPanel.qml @@ -43,8 +43,7 @@ DockedPanel { } IconButton { id: playpanelicon - icon.source: playerHandler.playicon - === "" ? "../../images/podcast.png" : playerHandler.playicon + icon.source: playerHandler.podcast_logo_or_fallback icon.width: Theme.iconSizeMedium icon.height: Theme.iconSizeMedium icon.color: undefined diff --git a/qml/components/PlayerHandler.qml b/qml/components/PlayerHandler.qml index 96ef6d6..79e27f7 100644 --- a/qml/components/PlayerHandler.qml +++ b/qml/components/PlayerHandler.qml @@ -14,7 +14,10 @@ Python { property real positionFromDb: 0 property bool isPlaying: false - property string playicon: "../../images/podcast.png" + property string podcast_logo: "../../images/podcast.png" + property string podcast_logo_or_fallback: podcast_logo ? podcast_logo : "../../images/podcast.png" + property string episode_logo: "" + property string episode_logo_or_fallback: episode_logo ? episode_logo : podcast_logo property string playtext: "" property string firstid: "" property string lastPlayedId: "" @@ -103,7 +106,8 @@ Python { firsttitle = data.title firstPodcastTitle = data.podcastTitle chapters = chapterlist - playicon = data.podcast_logo + podcast_logo = data.podcast_logo + episode_logo = data.episode_logo playtext = data.title durationFromDb = data.duration positionFromDb = data.position diff --git a/qml/components/QueueHandlerPython.qml b/qml/components/QueueHandlerPython.qml index a9ee02c..b390e5a 100644 --- a/qml/components/QueueHandlerPython.qml +++ b/qml/components/QueueHandlerPython.qml @@ -15,7 +15,7 @@ Python { signal createList(var data, string moved) signal downloading(string dlid, int percent) signal needDownload(string podpost) - signal firstDownloaded(string filepath) + signal firstDownloaded(string filepath, string episode_logo_path) signal episodeChapters(var chapters) Component.onCompleted: { @@ -149,6 +149,7 @@ Python { + filepath + "/" + playerHandler.seekPos) //magic needed to replace the source and seek to same position mediaplayer.source = "file://" + filepath + playerHandler.episode_logo = episode_logo_path mediaplayer.seek(position) mediaplayer.play() if (!mediaplayer.seekable) { diff --git a/qml/cover/CoverPage.qml b/qml/cover/CoverPage.qml index 5f92308..1464d71 100644 --- a/qml/cover/CoverPage.qml +++ b/qml/cover/CoverPage.qml @@ -4,7 +4,7 @@ import Sailfish.Silica 1.0 CoverBackground { Image { id: coverImage - source: playerHandler.playicon === "" ? "../../images/podcast.png" : playerHandler.playicon + source: playerHandler.isPlaying ? playerHandler.episode_logo_or_fallback : playerHandler.podcast_logo_or_fallback width: parent.height height: parent.height anchors.horizontalCenter: parent.horizontalCenter @@ -70,5 +70,11 @@ CoverBackground { playerHandler.playpause() } } + CoverAction { + iconSource: "image://theme/icon-m-media-forward" + onTriggered: { + playerHandler.fast_forward() + } + } } } diff --git a/qml/harbour-podqast.qml b/qml/harbour-podqast.qml index 84f2315..80762cc 100644 --- a/qml/harbour-podqast.qml +++ b/qml/harbour-podqast.qml @@ -525,7 +525,7 @@ ApplicationWindow { duration: playerHandler.duration title: playerHandler.firsttitle contributingArtist: playerHandler.firstPodcastTitle - artUrl: playerHandler.playicon + artUrl: playerHandler.episode_logo_or_fallback } loopStatus: Mpris.LoopNone diff --git a/qml/pages/Player.qml b/qml/pages/Player.qml index c2ff9b5..4a8a923 100644 --- a/qml/pages/Player.qml +++ b/qml/pages/Player.qml @@ -1,5 +1,6 @@ import QtQuick 2.0 import Sailfish.Silica 1.0 +import Nemo.Thumbnailer 1.0 import "../lib/hints" import "../components/hints" import "../components" @@ -129,12 +130,13 @@ Page { width: page.width height: page.width - Image { + Thumbnail { id: podimage width: parent.width height: parent.width - source: playerHandler.playicon - === "" ? "../../images/podcast.png" : playerHandler.playicon + source: playerHandler.episode_logo_or_fallback + sourceSize.width: width + sourceSize.height: height MouseArea { anchors.fill: parent onClicked: { @@ -272,7 +274,7 @@ Page { height: Theme.itemSizeLarge IconButton { - icon.source: "image://theme/icon-m-previous" + icon.source: "image://theme/icon-m-media-rewind" onClicked: { playerHandler.fast_backward() } @@ -285,7 +287,7 @@ Page { } } IconButton { - icon.source: "image://theme/icon-m-next" + icon.source: "image://theme/icon-m-media-forward" onClicked: { playerHandler.fast_forward() diff --git a/qml/pages/Queue.qml b/qml/pages/Queue.qml index 641fd9d..907d01a 100644 --- a/qml/pages/Queue.qml +++ b/qml/pages/Queue.qml @@ -21,7 +21,7 @@ Item { } if (i > 0) { playeropen = true - playerHandler.playicon = data[0].logo_url + playerHandler.podcast_logo = data[0].podcast_logo } else { playeropen = false } diff --git a/translations/harbour-podqast-de.ts b/translations/harbour-podqast-de.ts index d19e9a6..4e78cda 100644 --- a/translations/harbour-podqast-de.ts +++ b/translations/harbour-podqast-de.ts @@ -509,32 +509,32 @@ Player - + Auto-play next episode in queue - + Stop after each episode - + Audio playrate Abspielgeschwindigkeit - + Sleep timer Schlummer-Uhr - + running... läuft... - + chapters Kapitel diff --git a/translations/harbour-podqast-es.ts b/translations/harbour-podqast-es.ts index 73432c8..8c627ba 100644 --- a/translations/harbour-podqast-es.ts +++ b/translations/harbour-podqast-es.ts @@ -509,32 +509,32 @@ Player - + Auto-play next episode in queue - + Stop after each episode - + Audio playrate Velocidad de reproducción - + Sleep timer - + running... - + chapters capítulos diff --git a/translations/harbour-podqast-fr.ts b/translations/harbour-podqast-fr.ts index 963e9d5..3ed09d0 100644 --- a/translations/harbour-podqast-fr.ts +++ b/translations/harbour-podqast-fr.ts @@ -510,32 +510,32 @@ Player - + Auto-play next episode in queue - + Stop after each episode - + Audio playrate Vitesse de lecture - + Sleep timer Minuterie avant coupure - + running... activée... - + chapters chapitres diff --git a/translations/harbour-podqast-sv.ts b/translations/harbour-podqast-sv.ts index 4874cb8..4b23e89 100644 --- a/translations/harbour-podqast-sv.ts +++ b/translations/harbour-podqast-sv.ts @@ -509,32 +509,32 @@ Player - + Auto-play next episode in queue - + Stop after each episode - + Audio playrate Spelningsfrekvens - + Sleep timer Insomningsur - + running... körs... - + chapters avsnitt diff --git a/translations/harbour-podqast-zh_CN.ts b/translations/harbour-podqast-zh_CN.ts index 20e1050..16970eb 100644 --- a/translations/harbour-podqast-zh_CN.ts +++ b/translations/harbour-podqast-zh_CN.ts @@ -509,32 +509,32 @@ Player - + Auto-play next episode in queue - + Stop after each episode - + Audio playrate 音频播放率 - + Sleep timer 睡眠计时器 - + running... 运行中…… - + chapters 章节 diff --git a/translations/harbour-podqast.ts b/translations/harbour-podqast.ts index a665e40..d12f2a5 100644 --- a/translations/harbour-podqast.ts +++ b/translations/harbour-podqast.ts @@ -509,32 +509,32 @@ Player - + Auto-play next episode in queue - + Stop after each episode - + Audio playrate Audio playrate - + Sleep timer Sleep timer - + running... running... - + chapters chapters -- GitLab From bab59afa2388ef7a5be469e59a6f6023f7d07202 Mon Sep 17 00:00:00 2001 From: Thilo Kogge Date: Mon, 14 Nov 2022 01:08:27 +0100 Subject: [PATCH 4/8] fixed tests --- python/podcast/data_migration.py | 4 ---- python/podcast/podcast.py | 2 -- python/podcast/podpost.py | 13 ++++++++----- python/podcast/queue.py | 1 - test/__init__.py | 2 +- test/test_favorites.py | 2 +- test/test_feedutils.py | 2 +- test/test_podcast.py | 17 +++++++++-------- test/test_podpost.py | 4 ++-- test/test_queue.py | 8 ++++---- 10 files changed, 26 insertions(+), 29 deletions(-) diff --git a/python/podcast/data_migration.py b/python/podcast/data_migration.py index d7f39ba..3097409 100644 --- a/python/podcast/data_migration.py +++ b/python/podcast/data_migration.py @@ -344,10 +344,6 @@ def migrate_all_podposts_from_v0(post_hash_to_id): def migrate_podcast_v0_v1(podcast_dict: dict, strict=True) -> Tuple[Podcast, Dict[str, Podpost]]: logger.info("migrating podcast '%s' to new format version 1 strictmode: %s", podcast_dict['title'], strict) try: - if podcast_dict['logo_path']: - image = podcast_dict['logo_path'] - else: - image = podcast_dict['logo_url'] podcast: Podcast = Podcast(**podcast_dict) PodcastFactory().persist(podcast) entry_ids_new_to_old = [] diff --git a/python/podcast/podcast.py b/python/podcast/podcast.py index 7f2e25d..bc22efa 100644 --- a/python/podcast/podcast.py +++ b/python/podcast/podcast.py @@ -138,8 +138,6 @@ class Podcast(BaseModel): logger.debug("fetching episodes for %s full=%s limit=%d", self.title, str(not break_on_first_existing_episode), limit) - image = self.logo() - known_guids = set() known_hrefs = set() from podcast.podpost import Podpost diff --git a/python/podcast/podpost.py b/python/podcast/podpost.py index c043a1d..cc47b9f 100644 --- a/python/podcast/podpost.py +++ b/python/podcast/podpost.py @@ -232,7 +232,7 @@ class Podpost(BaseModel): "title": self.title, "podcast_url": self.podcast.url if not self.isaudio else "", "audio_url": self.href, - "podcast_logo": self.podcast.logo(), + "podcast_logo": self.podcast.logo() if not self.isaudio else "", "episode_logo": self.get_episode_logo_path_or_url(), "link": self.link, "description": self.plainpart, @@ -262,11 +262,14 @@ class Podpost(BaseModel): return image def download(self): - yield from self.download_audio() - self.download_icon() + yield from self.__download_audio() + try: + self.__download_icon() + except: + logger.exception("exception during downloading logo") PodpostFactory().persist(self) - def download_audio(self): + def __download_audio(self) -> Iterator[int]: """ Download the audio stuff """ @@ -317,7 +320,7 @@ class Podpost(BaseModel): self.file_path = file_path - def download_icon(self): + def __download_icon(self): """ Download icon """ diff --git a/python/podcast/queue.py b/python/podcast/queue.py index 8f40922..0776197 100644 --- a/python/podcast/queue.py +++ b/python/podcast/queue.py @@ -212,7 +212,6 @@ class Queue: post = PodpostFactory().get_podpost(podpost) for perc in post.download(): yield perc - post.download_icon() if podpost == self.podposts[0] and post.file_path: pyotherside.send("firstDownloaded", post.file_path, post.logo_path) diff --git a/test/__init__.py b/test/__init__.py index 3d7db0d..8e82258 100644 --- a/test/__init__.py +++ b/test/__init__.py @@ -40,7 +40,7 @@ def setup_inbox_with_2_posts(): inbox.insert(entry2.id) return entry1, entry2 -@httprettified +@httprettified(allow_net_connect=False) def setup_and_get_2_posts(): logger.info("setup with dummy feed") HTTPretty.register_uri(HTTPretty.GET, 'https://freakshow.fm/feed/opus/', diff --git a/test/test_favorites.py b/test/test_favorites.py index c20e399..ae9e30b 100644 --- a/test/test_favorites.py +++ b/test/test_favorites.py @@ -6,7 +6,7 @@ from podcast.util import ilen from . import xml_headers, read_testdata -@httprettified +@httprettified(allow_net_connect=False) def test_queue_favorites(): """ Test listing of podposts diff --git a/test/test_feedutils.py b/test/test_feedutils.py index ac4293d..3def309 100644 --- a/test/test_feedutils.py +++ b/test/test_feedutils.py @@ -4,7 +4,7 @@ from podcast.feedutils import fetch_feed, NewFeedUrlError from test import read_testdata, xml_headers -@httpretty.activate +@httpretty.activate(allow_net_connect=False) def test_new_feed_url(): url_f = "https://fakefeed.com/" httpretty.HTTPretty.register_uri(httpretty.HTTPretty.GET, url_f, body=read_testdata('testdata/new-feed-url-feed.rss'), diff --git a/test/test_podcast.py b/test/test_podcast.py index fe5dd70..9531750 100644 --- a/test/test_podcast.py +++ b/test/test_podcast.py @@ -95,11 +95,12 @@ def test_feed_entry(): assert data != None assert type(data) == dict assert 'id' in data.keys() - assert 'logo_url' in data.keys() + assert 'podcast_logo' in data.keys() + assert 'episode_logo' in data.keys() assert 'description' in data.keys() assert type(data['id']) == POST_ID_TYPE - assert len(data['logo_url']) > 0 or data['logo_url'] == "" - assert type(data['logo_url']) == str + assert len(data['podcast_logo']) > 0 or data['podcast_logo'] == "" + assert type(data['podcast_logo']) == str assert podcast.get_entry(data['id']) != None assert data == podcast.get_entry(data['id']).get_data() @@ -137,7 +138,7 @@ def test_import_nohref(): Podcast.create_from_url(url_99) -@httpretty.activate +@httpretty.activate(allow_net_connect=False) def test_preview(): invoked = 0 @@ -159,7 +160,7 @@ def test_preview(): assert invoked == 1 -@httpretty.activate +@httpretty.activate(allow_net_connect=False) def test_feed_no_changes(): global invoked invoked = 0 @@ -248,7 +249,7 @@ def refreshable_podcast_fixture(request) -> Tuple[Podcast, List[Podpost]]: -@httpretty.activate +@httpretty.activate(allow_net_connect=False) def test_pagination_stops(): url_f1 = "https://fakefeed.com/" url_f2 = "https://fakefeed.com/page2" @@ -276,7 +277,7 @@ def test_pagination_stops(): assert 1 == len(episodes) -@httpretty.activate +@httpretty.activate(allow_net_connect=False) def test_pagination(): url_f = "https://fakefeed.com/page" HTTPretty.register_uri(HTTPretty.GET, url_f, body=read_testdata('testdata/fakefeed2.xml'), @@ -289,7 +290,7 @@ def test_pagination(): assert 2 == podcast.count_episodes() -@httpretty.activate +@httpretty.activate(allow_net_connect=False) def test_new_feed_url(): url_f = "https://fakefeed.com/" HTTPretty.register_uri(HTTPretty.GET, url_f, body=read_testdata('testdata/new-feed-url-feed.rss'), diff --git a/test/test_podpost.py b/test/test_podpost.py index 3b8f89f..a796828 100644 --- a/test/test_podpost.py +++ b/test/test_podpost.py @@ -17,7 +17,7 @@ from podcast.podpost import Podpost, PodpostFactory logger = logging.getLogger(__name__) -@httpretty.activate +@httpretty.activate(allow_net_connect=False) def test_podpost_save(): """ Test podpost saving @@ -39,7 +39,7 @@ def test_podpost_save(): assert n.get_data()["listened"] -@httpretty.activate +@httpretty.activate(allow_net_connect=False) def test_podpost_save2(): """ Test podpost saving diff --git a/test/test_queue.py b/test/test_queue.py index c196110..f69d529 100644 --- a/test/test_queue.py +++ b/test/test_queue.py @@ -44,7 +44,7 @@ def test_queue_save(): -@httpretty.activate +@httpretty.activate(allow_net_connect=False) def test_insert_top(): HTTPretty.register_uri(HTTPretty.GET, 'https://freakshow.fm/feed/opus/', body=read_testdata('testdata/freakshow.rss'), adding_headers=xml_headers) @@ -72,7 +72,7 @@ def test_insert_top(): q.save() -@httpretty.activate +@httpretty.activate(allow_net_connect=False) def test_insert_next(): HTTPretty.register_uri(HTTPretty.GET, 'https://freakshow.fm/feed/opus/', body=read_testdata('testdata/freakshow.rss'), adding_headers=xml_headers) @@ -100,7 +100,7 @@ def test_insert_next(): q.save() assert QueueData.get_queue() == [e.id, e2.id] -@httpretty.activate +@httpretty.activate(allow_net_connect=False) def test_get_podposts(): """ Test listing of podposts @@ -129,7 +129,7 @@ def test_get_podposts(): assert os.path.exists(refreshed_post.file_path) assert refreshed_post.percentage == 100 -@httpretty.activate +@httpretty.activate(allow_net_connect=False) def test_queue_favorites(): """ Test listing of podposts -- GitLab From d914c17cb45e375ea2d6d77fc8452c1298bf7c88 Mon Sep 17 00:00:00 2001 From: Thilo Kogge Date: Mon, 14 Nov 2022 01:17:32 +0100 Subject: [PATCH 5/8] disallowing net access for tests globally --- test/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/__init__.py b/test/__init__.py index 8e82258..fa52cfb 100644 --- a/test/__init__.py +++ b/test/__init__.py @@ -13,6 +13,8 @@ from test.conftest import cleanup_podcast logger = logging.getLogger(__name__) +HTTPretty.allow_net_connect = False + def mock_pyotherside(): module_name = "pyotherside" module = types.ModuleType(module_name) -- GitLab From 048b7a12de008ec602106de7896987712a6e0ba5 Mon Sep 17 00:00:00 2001 From: Thilo Kogge Date: Mon, 14 Nov 2022 02:17:10 +0100 Subject: [PATCH 6/8] clearing obsolete data and updating podcast logo if outdated --- python/podcast/data_migration.py | 9 +++++++- python/podcast/podcast.py | 37 ++++++++++++++++++-------------- 2 files changed, 29 insertions(+), 17 deletions(-) diff --git a/python/podcast/data_migration.py b/python/podcast/data_migration.py index 3097409..13de0da 100644 --- a/python/podcast/data_migration.py +++ b/python/podcast/data_migration.py @@ -24,7 +24,7 @@ from podcast.util import chunks logger = logging.getLogger(__name__) -target_data_version = 5 +target_data_version = 6 def needs_migration(): @@ -125,6 +125,7 @@ def run_migrations(strict=True): raise set_versionnumber(3) if current_version < 4 and start_version > 1: + # duration is now in ms db.execute_sql("UPDATE podpost set duration = duration * 1000;") set_versionnumber(4) if current_version < 5 and start_version > 1: @@ -141,6 +142,12 @@ def run_migrations(strict=True): queue.sanitize() queue.save() set_versionnumber(5) + if current_version < 6 and start_version > 1: + # the fields were filled with the podcast logo path, we need that for the episode logo now + logger.info("clearing podpost logo data") + db.execute_sql("UPDATE podpost set logo_url = null;") + db.execute_sql("UPDATE podpost set logo_path = null;") + set_versionnumber(6) db.execute_sql('VACUUM "main"') pyotherside.send("migrationDone") diff --git a/python/podcast/podcast.py b/python/podcast/podcast.py index bc22efa..4dca1ab 100644 --- a/python/podcast/podcast.py +++ b/python/podcast/podcast.py @@ -5,6 +5,7 @@ Podcast import sys from typing import List, Dict, Optional, Iterator, Tuple +from feedparser import FeedParserDict from peewee import CharField, TextField, BooleanField, IntegerField, ModelSelect, DoesNotExist, FloatField, \ IntegrityError from podcast.persistent_log import persist_log, LogType @@ -61,7 +62,7 @@ class Podcast(BaseModel): super().__init__(*args, **kwargs) @classmethod - def create_from_url(cls, url, preview=False, num_preview_items=3) -> Optional[Tuple['Podcast',List['Podpost']]]: + def create_from_url(cls, url, preview=False, num_preview_items=3) -> Optional[Tuple['Podcast', List['Podpost']]]: """ @rtype: Tuple[Podcast,List[Podpost]] @@ -75,6 +76,7 @@ class Podcast(BaseModel): podcast.subscribed = not preview logger.info("initializing podcast %s", url) podcast.url = url + feed: FeedParserDict if feedCache.exists(url): feed = feedCache.get(url) else: @@ -98,7 +100,7 @@ class Podcast(BaseModel): return @classmethod - def podcast_from_feed(cls, feed, podcast, preview, url, num_preview_items=3) -> List['Podpost']: + def podcast_from_feed(cls, feed: FeedParserDict, podcast: 'Podcast', preview: bool, url: str, num_preview_items: int = 3) -> List['Podpost']: try: podcast.title = feed.feed.title except: @@ -112,14 +114,6 @@ class Podcast(BaseModel): podcast.description = feed.feed.description except: podcast.description = "" - try: - podcast.logo_url = feed.feed.image.href - logger.debug("podcast logo_url: %s", podcast.logo_url) - except: - podcast.logo_url = "../../images/podcast.png" - logger.exception("podcast logo_url error") - podcast.logo_path = None - podcast.download_icon() podcast.__set_data_from_feed(feed) if preview: episodes = podcast.__process_episodes(feed, limit=num_preview_items, supress_limit_notification=True, @@ -129,6 +123,19 @@ class Podcast(BaseModel): episodes = podcast.__process_episodes(feed) return episodes + def __set_logo_from_feed(self, feed: FeedParserDict): + try: + if not feed or "image" not in feed.feed or not feed.feed.image.href or feed.feed.image.href == self.logo_url: + return + self.logo_url = feed.feed.image.href + logger.debug("podcast logo_url: %s", self.logo_url) + if self.logo_path: + util.delete_file(self.logo_path) + self.download_icon() + except: + self.logo_url = "../../images/podcast.png" + logger.exception("podcast logo_url error") + def __process_episodes(self, feed, limit=0, supress_limit_notification=False, do_not_store=False, break_on_first_existing_episode=True) -> List['Podpost']: """ @@ -198,6 +205,7 @@ class Podcast(BaseModel): def __set_data_from_feed(self, feed): logger.debug("setting instance values from feed") + self.__set_logo_from_feed(feed) self.published = self.__get_times_from_feeds(feed) self._set_alt_feeds(feed) @@ -210,7 +218,7 @@ class Podcast(BaseModel): else: return 0 - def feedinfo(self) -> Dict[str,object]: + def feedinfo(self) -> Dict[str, object]: """ return feed information """ @@ -313,8 +321,7 @@ class Podcast(BaseModel): else: self.logo_path = "" - - def refresh(self, moveto, limit=0, full_refresh=False) -> Iterator[Tuple[int,'Podpost']]: + def refresh(self, moveto, limit=0, full_refresh=False) -> Iterator[Tuple[int, 'Podpost']]: """ Refresh podcast and return list of new entries @type moveto: int the page the podcast should be moved to (see FeedParser#movePost) @@ -378,7 +385,7 @@ class Podcast(BaseModel): self.playrate = params["playrate"] PodcastFactory().persist(self) - def get_params(self) -> Dict[str,object]: + def get_params(self) -> Dict[str, object]: """ Return a set of parameters """ @@ -410,8 +417,6 @@ class Podcast(BaseModel): return self.episodes.count() - - class PodcastFactory(BaseFactory): """ Helping Factory -- GitLab From 285e54012b47d7bedb5f3f430b97d599f1b9e2dd Mon Sep 17 00:00:00 2001 From: Thilo Kogge Date: Tue, 22 Nov 2022 20:21:48 +0100 Subject: [PATCH 7/8] reload podcast logo if changed --- python/podcast/feedutils.py | 2 +- python/podcast/podcast.py | 23 +++++++++++------------ qml/pages/Player.qml | 2 +- 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/python/podcast/feedutils.py b/python/podcast/feedutils.py index 1744c5a..1f55323 100644 --- a/python/podcast/feedutils.py +++ b/python/podcast/feedutils.py @@ -35,7 +35,7 @@ def fetch_feed(published, url) -> FeedParserDict: exc: Exception = feed.bozo_exception if type(exc) != feedparser.CharacterEncodingOverride: logger.exception( - "Podcast init: error in parsing feed %s", str(type(exc)), exc_info=exc + "Podcast init: error in parsing feed '%s' %s", url, str(type(exc)), exc_info=exc ) raise FeedFetchingError(exc.message if hasattr(exc, 'message') else "message_missing") if feed.status == 304: diff --git a/python/podcast/podcast.py b/python/podcast/podcast.py index 4dca1ab..ae573fd 100644 --- a/python/podcast/podcast.py +++ b/python/podcast/podcast.py @@ -125,13 +125,18 @@ class Podcast(BaseModel): def __set_logo_from_feed(self, feed: FeedParserDict): try: - if not feed or "image" not in feed.feed or not feed.feed.image.href or feed.feed.image.href == self.logo_url: - return + if not feed or "image" not in feed.feed or not feed.feed.image.href: + return #not there + if self.logo_path and os.path.exists(self.logo_path) and feed.feed.image.href == self.logo_url: + return #already downloaded self.logo_url = feed.feed.image.href - logger.debug("podcast logo_url: %s", self.logo_url) + logger.debug("podcast new logo_url: %s", self.logo_url) + old_path = self.logo_path if self.logo_path: - util.delete_file(self.logo_path) - self.download_icon() + self.logo_path = None + self.__download_icon() + if old_path: + util.delete_file(old_path) except: self.logo_url = "../../images/podcast.png" logger.exception("podcast logo_url error") @@ -286,17 +291,11 @@ class Podcast(BaseModel): else: return None - # todo handle download icon during init - # TODO unify with podpost download icon - def download_icon(self): + def __download_icon(self): """ Download icon """ - if self.logo_path: - if os.path.exists(self.logo_path): - return - iconpath = Constants().iconpath rdot = self.logo_url.rfind(".") extexcl = self.logo_url.rfind("?") diff --git a/qml/pages/Player.qml b/qml/pages/Player.qml index 4a8a923..61df1b8 100644 --- a/qml/pages/Player.qml +++ b/qml/pages/Player.qml @@ -130,7 +130,7 @@ Page { width: page.width height: page.width - Thumbnail { + Image { id: podimage width: parent.width height: parent.width -- GitLab From ab862a270877dc9b171926b6bb16725dd40e2396 Mon Sep 17 00:00:00 2001 From: Thilo Kogge Date: Tue, 22 Nov 2022 20:43:03 +0100 Subject: [PATCH 8/8] fixed playbackpositionoverwrite of next episode --- qml/components/PlayerHandler.qml | 19 +++++++++++++++---- qml/components/QueueHandlerPython.qml | 6 ------ qml/harbour-podqast.qml | 5 +---- translations/harbour-podqast-de.ts | 6 +++--- translations/harbour-podqast-es.ts | 6 +++--- translations/harbour-podqast-fr.ts | 6 +++--- translations/harbour-podqast-sv.ts | 6 +++--- translations/harbour-podqast-zh_CN.ts | 6 +++--- translations/harbour-podqast.ts | 6 +++--- 9 files changed, 34 insertions(+), 32 deletions(-) diff --git a/qml/components/PlayerHandler.qml b/qml/components/PlayerHandler.qml index 79e27f7..6ce8a9a 100644 --- a/qml/components/PlayerHandler.qml +++ b/qml/components/PlayerHandler.qml @@ -58,7 +58,9 @@ Python { } onPlaying: { - console.info("Playing audio_url: " + audio_url + " @ rate " + playrate) + console.info("Playing audio_url: " + audio_url + " @ rate " + playrate + + "@ position " + position) + positionFromDb = position if (audio_url != "") { if (mediaplayer.source != audio_url) { mediaplayer.source = audio_url @@ -86,13 +88,13 @@ Python { if (mediaplayer.position > pauseRewindTime) positionToSave = mediaplayer.position - pauseRewindTime isPlaying = false - queuehandler.updatePlayingPosition(firstid, positionToSave) + queuehandler.updatePlayingPosition(lastPlayedId, positionToSave) } onStopping: { mediaplayer.pause() isPlaying = false - queuehandler.updatePlayingPosition(firstid, mediaplayer.position) + queuehandler.updatePlayingPosition(lastPlayedId, mediaplayer.position) } onDownloading: { @@ -107,7 +109,10 @@ Python { firstPodcastTitle = data.podcastTitle chapters = chapterlist podcast_logo = data.podcast_logo - episode_logo = data.episode_logo + if (data.episode_logo) + episode_logo = data.episode_logo + else + episode_logo = "" playtext = data.title durationFromDb = data.duration positionFromDb = data.position @@ -222,4 +227,10 @@ Python { } seek(posi) } + + function interval_task() { + if (lastPlayedId) + queuehandler.updatePlayingPosition(lastPlayedId, + mediaplayer.position) + } } diff --git a/qml/components/QueueHandlerPython.qml b/qml/components/QueueHandlerPython.qml index b390e5a..8f7cdba 100644 --- a/qml/components/QueueHandlerPython.qml +++ b/qml/components/QueueHandlerPython.qml @@ -73,12 +73,6 @@ Python { call("QueueHandler.instance.get_first_entry", function () {}) } function updatePlayingPosition(id, position) { - if (id === undefined) { - id = playerHandler.firstid - } - if (position === undefined) { - position = playerHandler.position - } call("QueueHandler.instance.update_position", [id, position], function () { if (id === playerHandler.firstid) diff --git a/qml/harbour-podqast.qml b/qml/harbour-podqast.qml index 80762cc..12424d7 100644 --- a/qml/harbour-podqast.qml +++ b/qml/harbour-podqast.qml @@ -71,7 +71,7 @@ ApplicationWindow { repeat: true onTriggered: { console.log("Minuteman") - queuehandler.updatePlayingPosition() + playerHandler.interval_task() } } @@ -456,9 +456,6 @@ ApplicationWindow { id: loghandler } - // PodqastAudioPlayer { - // id: mediaplayer - // } PlayerHandler { id: playerHandler onAudioNotExist: { diff --git a/translations/harbour-podqast-de.ts b/translations/harbour-podqast-de.ts index 4e78cda..c0e4996 100644 --- a/translations/harbour-podqast-de.ts +++ b/translations/harbour-podqast-de.ts @@ -1088,13 +1088,13 @@ Podcasts vom OPML importiert - + Error Fehler - - + + Audio File not existing Audiodatei existiert nicht diff --git a/translations/harbour-podqast-es.ts b/translations/harbour-podqast-es.ts index 8c627ba..c80c633 100644 --- a/translations/harbour-podqast-es.ts +++ b/translations/harbour-podqast-es.ts @@ -1086,13 +1086,13 @@ podcasts importados desde OPML - + Error Error - - + + Audio File not existing El archivo de audio no existe diff --git a/translations/harbour-podqast-fr.ts b/translations/harbour-podqast-fr.ts index 3ed09d0..0f66804 100644 --- a/translations/harbour-podqast-fr.ts +++ b/translations/harbour-podqast-fr.ts @@ -1089,13 +1089,13 @@ Podcasts importés depuis OPML - + Error Erreur - - + + Audio File not existing Le fichier audio n'existe pas diff --git a/translations/harbour-podqast-sv.ts b/translations/harbour-podqast-sv.ts index 4b23e89..7b652c3 100644 --- a/translations/harbour-podqast-sv.ts +++ b/translations/harbour-podqast-sv.ts @@ -1088,13 +1088,13 @@ poddar importerade från OPML - + Error Fel - - + + Audio File not existing Ljudfilen finns inte diff --git a/translations/harbour-podqast-zh_CN.ts b/translations/harbour-podqast-zh_CN.ts index 16970eb..5b494c2 100644 --- a/translations/harbour-podqast-zh_CN.ts +++ b/translations/harbour-podqast-zh_CN.ts @@ -1086,13 +1086,13 @@ 已从 OPML 导入播客 - + Error 错误 - - + + Audio File not existing 音频文件不存在 diff --git a/translations/harbour-podqast.ts b/translations/harbour-podqast.ts index d12f2a5..90be444 100644 --- a/translations/harbour-podqast.ts +++ b/translations/harbour-podqast.ts @@ -1088,13 +1088,13 @@ Podcasts imported from OPML - + Error Error - - + + Audio File not existing Audio File not existing -- GitLab