From af0641301b9371af1956506a3a9d043fa5b535e4 Mon Sep 17 00:00:00 2001 From: Thilo Kogge Date: Sat, 30 Jan 2021 16:37:03 +0100 Subject: [PATCH 01/18] moved the insert_date from podpost into archive --- python/podcast/archive.py | 39 +- python/podcast/data_migration.py | 28 +- qml/components/ArchiveHandler.py | 5 +- test/test_archive.py | 23 +- test/test_migration_to_v1.py | 3 +- test/test_podpost.py | 10 +- test/test_queue.py | 10 +- test/testdata/freakshow.rss | 5392 +----------------------------- 8 files changed, 86 insertions(+), 5424 deletions(-) diff --git a/python/podcast/archive.py b/python/podcast/archive.py index 0e2d649..ba5fa93 100644 --- a/python/podcast/archive.py +++ b/python/podcast/archive.py @@ -1,9 +1,10 @@ """ Archive of listened poposts """ - +import datetime import sys import time +from typing import List, Any sys.path.append("../") @@ -13,17 +14,30 @@ from podcast.factory import Factory archivename = "the_archive" +class ArchiveEntry: + insert_date: float + podpost: str + + @classmethod + def of_podpost(cls, postid: str): + archive_entry = ArchiveEntry() + archive_entry.podpost = postid + archive_entry.insert_date = time.time() + return archive_entry + + class Archive: """ The podcast Archive. It has a list of podposts """ + _archive_entries: list[ArchiveEntry] def __init__(self): """ Initialization """ - self.podposts = [] # Id list of posposts + self._archive_entries = [] # Id list of posposts def save(self): """ @@ -38,28 +52,27 @@ class Archive: Insert an podost """ - if podpost not in self.podposts: - self.podposts.insert(0, podpost) - post = Factory().get_podpost(podpost) - if post.insert_date == None: - post.insert_date = time.time() - post.save() + if podpost not in [e.podpost for e in self._archive_entries]: + self._archive_entries.insert(0, ArchiveEntry.of_podpost(podpost)) self.save() - def get_podposts(self): + def get_podposts(self, sorted_by_date=False): """ get the list of podposts """ - - for podpost in self.podposts: - yield podpost + if not sorted_by_date: + for archive_entry in self._archive_entries: + yield archive_entry.podpost + else: + for archive_entry in sorted(self._archive_entries, key=lambda e: e.insert_date, reverse=True): + yield archive_entry.podpost def remove_podpost(self, podpost): """ Remove a podpost from archive """ - self.podposts.remove(podpost) + self._archive_entries = [entry for entry in self._archive_entries if entry.podpost != podpost] class ArchiveFactory(metaclass=Singleton): diff --git a/python/podcast/data_migration.py b/python/podcast/data_migration.py index a7bcdbc..7fd6ccc 100644 --- a/python/podcast/data_migration.py +++ b/python/podcast/data_migration.py @@ -1,5 +1,7 @@ import logging import os +from typing import List + import pyotherside from podcast.factory import Factory @@ -41,10 +43,10 @@ def run_migrations(): migrate_podpost_v0_v1(Factory().get_podpost(entry_id)) i += 1 pyotherside.send("migrationProgress", i) + migrate_archive_v0_v1() set_versionnumber(1) pyotherside.send("migrationDone") - def get_versionnumber() -> int: versionfilepath = get_versionfile_path() if os.path.isfile(versionfilepath): @@ -64,6 +66,30 @@ def get_versionfile_path(): return os.path.join(Factory().data_home, "dataversion") +def migrate_archive_v0_v1(): + """ + migrates the id list to ArchiveEntry + """ + from podcast.archive import ArchiveFactory, ArchiveEntry, Archive + import datetime + archive: Archive = ArchiveFactory().get_archive() + if hasattr(archive, "podposts") and len(archive.podposts) > 0 and type(archive.podposts) is not ArchiveEntry: + old_archive: List[str] = archive.podposts + archive.__archive_entries = [] + for postid in old_archive: + archive_entry = ArchiveEntry() + post = Factory().get_podpost(postid) + if not post: + continue + if hasattr(post, "insert_date") and post.insert_date: + archive_entry.insert_date = post.insert_date + else: + archive_entry.insert_date = datetime.datetime.now() + archive_entry.podpost = postid + archive.__archive_entries.append(post) + del archive.podposts + + def migrate_podpost_v0_v1(self: Podpost): if hasattr(self, "entry"): if not hasattr(self, "isaudio"): diff --git a/qml/components/ArchiveHandler.py b/qml/components/ArchiveHandler.py index f488394..4a48d88 100644 --- a/qml/components/ArchiveHandler.py +++ b/qml/components/ArchiveHandler.py @@ -19,8 +19,8 @@ def get_archive_posts(podurl=None): entries = [] archive = ArchiveFactory().get_archive() - for post in archive.get_podposts(): - entry = Factory().get_podpost(post) + for archive_entry in archive.get_podposts(sorted_by_date=True): + entry = Factory().get_podpost(archive_entry.podpost) if podurl: if entry.podurl == podurl: entries.append(entry.get_data()) @@ -30,7 +30,6 @@ def get_archive_posts(podurl=None): else: pyotherside.send("ArchiveHandler: We have a none object") - entries.sort(key=lambda r: r["adate"], reverse=True) pyotherside.send("createArchiveList", entries) diff --git a/test/test_archive.py b/test/test_archive.py index e3d187d..a951a22 100644 --- a/test/test_archive.py +++ b/test/test_archive.py @@ -4,7 +4,6 @@ test the archive import sys -import httpretty from httpretty import HTTPretty, httprettified from test.test_podcast import read_testdata, xml_headers @@ -15,7 +14,6 @@ from podcast.archive import ArchiveFactory, Archive from podcast.factory import Factory from podcast.podcast import Podcast from podcast.podpost import Podpost -import podcast def test_create_archive(): """ @@ -43,18 +41,19 @@ def test_insert(): HTTPretty.register_uri(HTTPretty.GET, 'https://freakshow.fm/feed/opus/', body=read_testdata('testdata/freakshow.rss'), adding_headers=xml_headers) - a = ArchiveFactory().get_archive() - p = Podcast('https://freakshow.fm/feed/opus/') - e = p.get_entry(p.entry_ids_old_to_new[0]) - e.save() - a.insert(e.id) + archive = ArchiveFactory().get_archive() + podcast = Podcast('https://freakshow.fm/feed/opus/') + entry1 = podcast.get_entry(podcast.entry_ids_old_to_new[0]) + entry1.save() + archive.insert(entry1.id) - e2 = p.get_entry(p.entry_ids_old_to_new[1]) - e2.save() - a.insert(e2.id) + entry2 = podcast.get_entry(podcast.entry_ids_old_to_new[1]) + entry2.save() + archive.insert(entry2.id) - assert e.id in a.podposts - assert e2.id in a.podposts + result_posts = list(archive.get_podposts(sorted_by_date=True)) + assert entry1.id == result_posts[1] # older + assert entry2.id == result_posts[0] # newer def test_get_archives(): """ diff --git a/test/test_migration_to_v1.py b/test/test_migration_to_v1.py index 4f9e587..c694f0c 100644 --- a/test/test_migration_to_v1.py +++ b/test/test_migration_to_v1.py @@ -14,6 +14,7 @@ from podcast.queue import QueueFactory sys.path.append("../python") + def test_migration(): with tempfile.TemporaryDirectory() as tmpdir: copy_tree(os.path.join(os.path.dirname(__file__), "testdata/migrationtests_v1/"), tmpdir) @@ -23,7 +24,7 @@ def test_migration(): assert os.path.exists(os.path.join(tmpdir, "dataversion")) podcasts = list(PodcastListFactory().get_podcast_list().get_podcasts()) assert len(podcasts) == 1 - podcast:Podcast = PodcastFactory().get_podcast(podcasts[0]) + podcast: Podcast = PodcastFactory().get_podcast(podcasts[0]) assert podcast != None assert len(podcast.entry_ids_old_to_new) == 22 assert len(list(podcast.get_entries())) == 22 diff --git a/test/test_podpost.py b/test/test_podpost.py index 2d40f3c..b138568 100644 --- a/test/test_podpost.py +++ b/test/test_podpost.py @@ -3,17 +3,25 @@ Podpost tests """ import sys + +import httpretty +from httpretty import HTTPretty + +from test.test_podcast import read_testdata, xml_headers + sys.path.append("../python") from podcast.factory import Factory from podcast.podcast import Podcast from podcast.podpost import Podpost +@httpretty.activate def test_podpost_save(): """ Test podpost saving """ - + HTTPretty.register_uri(HTTPretty.GET, 'https://freakshow.fm/feed/opus/', + body=read_testdata('testdata/freakshow.rss'), adding_headers=xml_headers) f1 = Factory() p = Podcast('https://freakshow.fm/feed/opus/') diff --git a/test/test_queue.py b/test/test_queue.py index 7634336..0927ba5 100644 --- a/test/test_queue.py +++ b/test/test_queue.py @@ -4,6 +4,11 @@ test the queue import sys +import httpretty +from httpretty import HTTPretty + +from test.test_podcast import read_testdata, xml_headers + sys.path.append("../python") from podcast.queue import QueueFactory @@ -34,11 +39,12 @@ def test_queue_save(): q = QueueFactory().get_queue() q.save() - +@httpretty.activate def test_insert_top(): """ """ - + HTTPretty.register_uri(HTTPretty.GET, 'https://freakshow.fm/feed/opus/', + body=read_testdata('testdata/freakshow.rss'), adding_headers=xml_headers) q = QueueFactory().get_queue() l1 = len(q.podposts) p = Podcast('https://freakshow.fm/feed/opus/') diff --git a/test/testdata/freakshow.rss b/test/testdata/freakshow.rss index c81a8fc..e2b9ac7 100644 --- a/test/testdata/freakshow.rss +++ b/test/testdata/freakshow.rss @@ -56,5397 +56,7 @@ no no - - FS255 Bitcoin Recycling - https://freakshow.fm/fs255-bitcoin-recycling - Thu, 24 Dec 2020 18:02:10 +0000 - podlove-2020-12-24t16:44:11+00:00-2df73540be4775e - - - - 03:56:56 - Metaebene Personal Media - Tim Pritlove - Podcast Clients — 2020 Technik-Rückblick — LiDAR — iPhone 12 — M1 — OpenZFS — NAS — Mac Apps — Bildschirme - 255 - full - Das Jahr geht (endlich) zu Ende und wir füllen die Bits eines Bytes bis auf letzte Stelle. Ausgabe Nummer Zweihundertfünfundfünzig versucht Euch zu Weihnachten noch mal das Herz zu fluten. - Feedback zur Sendung?
-Schreibe uns einen Kommentar

- -

Diese Sendung soll nie aufhören?
-Unterstütze die Metaebene mit einer Spende -

- - -

Podcast Clients — 2020 Technik-Rückblick — LiDAR — iPhone 12 — M1 — OpenZFS — NAS — Mac Apps — Bildschirme

- -

Das Jahr geht (endlich) zu Ende und wir füllen die Bits eines Bytes bis auf letzte Stelle. Ausgabe Nummer Zweihundertfünfundfünzig versucht Euch zu Weihnachten noch mal das Herz zu fluten.

- -

Dauer: 3:56:56

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-On Air -
- -avatar - - -Tim Pritlove - - - -Bitcoin Icon - - - - -Amazon Wishlist Icon - - - - -Liberapay Icon - - - - -SEPA-Überweisung via Online-Banking-Software Icon - - - - -Paypal Icon - - -
- -avatar - - -Denis Ahrens - - - -Amazon Wishlist Icon - - - - -Bitcoin Icon - - -
- -avatar - - -roddi - - - -Amazon Wishlist Icon - - - - -Bitcoin Icon - - -
-Support -
- -avatar - - -Rainer - - - -Bitcoin Icon - - -
- -avatar - - -Studio Link On Air - -
- - - - - -Shownotes: - - - - - - - -
-]]>
- - - - - - - - - - - - - - - - - - - - - - - - - - - - 2 - - Tim Pritlove - http://tim.pritlove.org/ - - - Denis Ahrens - - - roddi - -
- - FS254 Chat Roulette Business Edition - https://freakshow.fm/fs254-chat-roulette-business-edition - Thu, 10 Dec 2020 02:43:45 +0000 - podlove-2020-12-10t01:23:08+00:00-036a06860a74b9b - - - - 03:30:42 - Metaebene Personal Media - Tim Pritlove - Talking Heads — Konten und Banken — M1 und Wintel — Internet Tips — pretix — venueless — pretalx — Virtuelle Events — Videotechnik — Mikrofone und Headsets — Licht — Ethernet - 254 - full - Heute dreht sich alles um Events. Wir begrüßen rami als Gast in der Sendung, der das Projekt pretix losgetreten hat, dass ein Ticketing-System für Veranstaltungen aller Art ist. Wir sprechen über Online-Events und in was von der Pandemie-Zeit übrig bleiben wird, wenn wir uns wieder in der Realität treffen können. Dazu gibt es noch eine Reihe von Tips für Technik zur Durchführung von Audio- und Videosessions. - Feedback zur Sendung?
-Schreibe uns einen Kommentar

- -

Diese Sendung soll nie aufhören?
-Unterstütze die Metaebene mit einer Spende -

- - -

Talking Heads — Konten und Banken — M1 und Wintel — Internet Tips — pretix — venueless — pretalx — Virtuelle Events — Videotechnik — Mikrofone und Headsets — Licht — Ethernet

- -

Heute dreht sich alles um Events. Wir begrüßen rami als Gast in der Sendung, der das Projekt pretix losgetreten hat, dass ein Ticketing-System für Veranstaltungen aller Art ist. Wir sprechen über Online-Events und in was von der Pandemie-Zeit übrig bleiben wird, wenn wir uns wieder in der Realität treffen können. Dazu gibt es noch eine Reihe von Tips für Technik zur Durchführung von Audio- und Videosessions.

- -

Dauer: 3:30:42

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-On Air -
- -avatar - - -Tim Pritlove - - - -Bitcoin Icon - - - - -Amazon Wishlist Icon - - - - -Liberapay Icon - - - - -SEPA-Überweisung via Online-Banking-Software Icon - - - - -Paypal Icon - - -
- -avatar - - -Letty - - - -Lettys Wunschliste Icon - - -
- -avatar - - -hukl - - - -Thomann Wishlist Icon - - - - -Amazon Wishlist Icon - - - - -Bitcoin Icon - - -
- -avatar - - -rami - -
-Support -
- -avatar - - -Rainer - - - -Bitcoin Icon - - -
- -avatar - - -Studio Link On Air - -
- - - - - -Shownotes: - - - - - - - -
-]]>
- - - - - - - - - - - - - - - - - - - - - - - - 2 - - Tim Pritlove - http://tim.pritlove.org/ - - - Letty - - - hukl - - - rami - -
- - FS253 Zero Latency Computing - https://freakshow.fm/fs253-zero-latency-computing - Thu, 26 Nov 2020 23:00:32 +0000 - podlove-2020-11-26t22:43:23+00:00-130a4cec453a37f - - - - 03:41:53 - Metaebene Personal Media - Tim Pritlove - Ultraschall — M1 Macs — Big Sur - 253 - full - Nach zwei Wochen und vor allem nachdem manche von uns bereits ein paar der neuen M1 Macs erhalten habe dreht sich hier ein weiteres Mal alles um die neuen Computer. Wir teilen erste Erfahrungen und Einschätzungen und sind überhaupt sehr aufgeregt. Außerdem ist Ralf zu Gast und berichtet von den Fortschritten beim Ultraschall-Projekt. - Feedback zur Sendung?
-Schreibe uns einen Kommentar

- -

Diese Sendung soll nie aufhören?
-Unterstütze die Metaebene mit einer Spende -

- - -

Ultraschall — M1 Macs — Big Sur

- -

Nach zwei Wochen und vor allem nachdem manche von uns bereits ein paar der neuen M1 Macs erhalten habe dreht sich hier ein weiteres Mal alles um die neuen Computer. Wir teilen erste Erfahrungen und Einschätzungen und sind überhaupt sehr aufgeregt. Außerdem ist Ralf zu Gast und berichtet von den Fortschritten beim Ultraschall-Projekt.

- -

Dauer: 3:41:53

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-On Air -
- -avatar - - -Tim Pritlove - - - -Bitcoin Icon - - - - -Amazon Wishlist Icon - - - - -Liberapay Icon - - - - -SEPA-Überweisung via Online-Banking-Software Icon - - - - -Paypal Icon - - -
- -avatar - - -Dominik Wagner - -
- -avatar - - -Denis Ahrens - - - -Amazon Wishlist Icon - - - - -Bitcoin Icon - - -
- -avatar - - -Ralf Stockmann - -
-Support -
- -avatar - - -Rainer - - - -Bitcoin Icon - - -
- -avatar - - -Studio Link On Air - -
- - - - - -Shownotes: - - - - - - - -
-]]>
- - - - - - - - - - - - - - - - - - - - - - - - - - 2 - - Tim Pritlove - http://tim.pritlove.org/ - - - Dominik Wagner - - - Denis Ahrens - - - Ralf Stockmann - -
- - FS252 Keine grobe Feinheit - https://freakshow.fm/fs252-keine-grobe-feinheit - Thu, 12 Nov 2020 20:36:35 +0000 - podlove-2020-11-12t13:43:01+00:00-6312fbdf2ec0387 - - - - 03:50:53 - Metaebene Personal Media - Tim Pritlove - Kontowechsel — Pausenende — Elektrourlaub — Tesla Battery Day — iPhone 12 — Apple Watch — iOS 14 und AirPods Pro — Augmented Reality — Big Sur — ARM-Macs — 5G — Audio auf dem Mac — Audiointerfaces — Küchenkram - 252 - full - We are back. Nach einer längeren, im Kern ungeplanten aber trotzdem notwenidgen Verschnaufpause ist die Freak Show wieder am Start, denn wir mussten dringend mal wieder mehr über Macs reden. Wir gehen die neue Produkten von Apple der letzten Woche durch und natürlich legen wir einen Schwerpunkt auf die neuen ARM-Macs, die nun endlich vorgestellt worden. Dazu ein wenig Corona-Küchenkram rund um Waffeleisen und scharfe Messer. - Feedback zur Sendung?
-Schreibe uns einen Kommentar

- -

Diese Sendung soll nie aufhören?
-Unterstütze die Metaebene mit einer Spende -

- - -

Kontowechsel — Pausenende — Elektrourlaub — Tesla Battery Day — iPhone 12 — Apple Watch — iOS 14 und AirPods Pro — Augmented Reality — Big Sur — ARM-Macs — 5G — Audio auf dem Mac — Audiointerfaces — Küchenkram

- -

We are back. Nach einer längeren, im Kern ungeplanten aber trotzdem notwenidgen Verschnaufpause ist die Freak Show wieder am Start, denn wir mussten dringend mal wieder mehr über Macs reden. Wir gehen die neue Produkten von Apple der letzten Woche durch und natürlich legen wir einen Schwerpunkt auf die neuen ARM-Macs, die nun endlich vorgestellt worden. Dazu ein wenig Corona-Küchenkram rund um Waffeleisen und scharfe Messer.

- -

Dauer: 3:50:53

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-On Air -
- -avatar - - -Tim Pritlove - - - -Bitcoin Icon - - - - -Amazon Wishlist Icon - - - - -Liberapay Icon - - - - -SEPA-Überweisung via Online-Banking-Software Icon - - - - -Paypal Icon - - -
- -avatar - - -hukl - - - -Thomann Wishlist Icon - - - - -Amazon Wishlist Icon - - - - -Bitcoin Icon - - -
- -avatar - - -Denis Ahrens - - - -Amazon Wishlist Icon - - - - -Bitcoin Icon - - -
- -avatar - - -roddi - - - -Amazon Wishlist Icon - - - - -Bitcoin Icon - - -
- -avatar - - -Dominik Wagner - -
-Support -
- -avatar - - -Rainer - - - -Bitcoin Icon - - -
- -avatar - - -Studio Link On Air - -
- - - - - -Shownotes: - - - - - - - -
-]]>
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - 2 - - Tim Pritlove - http://tim.pritlove.org/ - - - hukl - - - Denis Ahrens - - - roddi - - - Dominik Wagner - -
- - FS251 Bürgermeisterladesäulen - https://freakshow.fm/fs251-buergermeisterladesaeulen - Sat, 18 Jul 2020 09:49:43 +0000 - podlove-2020-07-18t09:29:01+00:00-e6a090d7c680aed - - - - 03:19:11 - Metaebene Personal Media - Tim Pritlove - Eine Spezialausgabe der Freak Show mit dem Schwerpunkt Elektromobilität - 251 - full - Wir haben schon oft darüber gesprochen, aber so richtig Ahnung haben wir ja eh nicht. Deswegen haben wir (Roddi, Denis, Tim) uns den Philipp vom cleaneletric Podcast eingeladen um mal ganz ausführlich über den Stand der Dinge in Sachen Elektromobilität in Deutschland und dem Rest der Welt zu sprechen. Dabei versuchen wir zu ergründen, woran es noch hapert und wo vielleicht der Umstieg für viele schon näher liegt, als diese selbst noch glauben. - Feedback zur Sendung?
-Schreibe uns einen Kommentar

- -

Diese Sendung soll nie aufhören?
-Unterstütze die Metaebene mit einer Spende -

- - -

Eine Spezialausgabe der Freak Show mit dem Schwerpunkt Elektromobilität

- -

Wir haben schon oft darüber gesprochen, aber so richtig Ahnung haben wir ja eh nicht. Deswegen haben wir (Roddi, Denis, Tim) uns den Philipp vom cleaneletric Podcast eingeladen um mal ganz ausführlich über den Stand der Dinge in Sachen Elektromobilität in Deutschland und dem Rest der Welt zu sprechen. Dabei versuchen wir zu ergründen, woran es noch hapert und wo vielleicht der Umstieg für viele schon näher liegt, als diese selbst noch glauben.

- -

Dauer: 3:19:11

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-On Air -
- -avatar - - -Tim Pritlove - - - -Bitcoin Icon - - - - -Amazon Wishlist Icon - - - - -Liberapay Icon - - - - -SEPA-Überweisung via Online-Banking-Software Icon - - - - -Paypal Icon - - -
- -avatar - - -Denis Ahrens - - - -Amazon Wishlist Icon - - - - -Bitcoin Icon - - -
- -avatar - - -roddi - - - -Amazon Wishlist Icon - - - - -Bitcoin Icon - - -
- -avatar - - -Philipp Hellwig - -
-Support -
- -avatar - - -Rainer - - - -Bitcoin Icon - - -
- -avatar - - -Studio Link On Air - -
- - - - - -Shownotes: - - - - - - - -
-]]>
- - - - - - - - - - - - - - - - - - - - - - - 2 - - Tim Pritlove - http://tim.pritlove.org/ - - - Denis Ahrens - - - roddi - - - Philipp Hellwig - -
- - FS250 Die Ente bleibt draußen - https://freakshow.fm/fs250-die-ente-bleibt-draussen - Thu, 02 Jul 2020 22:46:38 +0000 - podlove-2020-07-02t21:40:53+00:00-6b9d9aaa486e6bf - - - - 03:37:40 - Metaebene Personal Media - Tim Pritlove - Die 250. Sendung der Freak Show über die WWDC 2020 und andere Dinge - 250 - full - Nun sind schon über 12 Jahre vergangen seitdem die Freak Show (damals noch unter anderem Namen) ans Netz gegangen ist. Wir bedanken uns für die jahrelange Treue unserer Hörerinnen und Hörer, die mit ihrem Feedback immer dazu beigetragen haben, dass uns die Lust am Projekt nie vergangen ist. - -Heute sind wir dann auch standesgemäß vollzählig angetreten und dazu haben wir uns auch noch den Dominik dazugehört, um die Akustik für Euch komplett unübersichtlich zu gestalten. - Feedback zur Sendung?
-Schreibe uns einen Kommentar

- -

Diese Sendung soll nie aufhören?
-Unterstütze die Metaebene mit einer Spende -

- - -

Die 250. Sendung der Freak Show über die WWDC 2020 und andere Dinge

- -

Nun sind schon über 12 Jahre vergangen seitdem die Freak Show (damals noch unter anderem Namen) ans Netz gegangen ist. Wir bedanken uns für die jahrelange Treue unserer Hörerinnen und Hörer, die mit ihrem Feedback immer dazu beigetragen haben, dass uns die Lust am Projekt nie vergangen ist.
-
-Heute sind wir dann auch standesgemäß vollzählig angetreten und dazu haben wir uns auch noch den Dominik dazugehört, um die Akustik für Euch komplett unübersichtlich zu gestalten.

- -

Dauer: 3:37:40

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-On Air -
- -avatar - - -Tim Pritlove - - - -Bitcoin Icon - - - - -Amazon Wishlist Icon - - - - -Liberapay Icon - - - - -SEPA-Überweisung via Online-Banking-Software Icon - - - - -Paypal Icon - - -
- -avatar - - -Dominik Wagner - -
- -avatar - - -hukl - - - -Thomann Wishlist Icon - - - - -Amazon Wishlist Icon - - - - -Bitcoin Icon - - -
- -avatar - - -Clemens Schrimpe - -
- -avatar - - -Letty - - - -Lettys Wunschliste Icon - - -
- -avatar - - -Tala - - - -Amazon Wishlist Icon - - -
- -avatar - - -roddi - - - -Amazon Wishlist Icon - - - - -Bitcoin Icon - - -
- -avatar - - -Denis Ahrens - - - -Amazon Wishlist Icon - - - - -Bitcoin Icon - - -
-Support -
- -avatar - - -Rainer - - - -Bitcoin Icon - - -
- -avatar - - -Studio Link On Air - -
- - - - - -Shownotes: - - - - - - - -
-]]>
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 2 - - Tim Pritlove - http://tim.pritlove.org/ - - - Dominik Wagner - - - hukl - - - Clemens Schrimpe - - - Letty - - - Tala - - - roddi - - - Denis Ahrens - -
- - FS249 Hemmungsloser Hinterhoftoilettensex - https://freakshow.fm/fs249-hemmungsloser-hinterhoftoilettensex - Mon, 27 Apr 2020 10:05:37 +0000 - podlove-2020-04-27t09:29:08+00:00-0afb684ad7267f3 - - - - 04:05:36 - Metaebene Personal Media - Tim Pritlove - Die Corona-Situation — Masken und Zauberwürfel — Gesundheitstechnologie — Differential Privacy — macOS auf ARM — Programmiersprachen - 249 - full - Heute begrüßen wir in unserer Runde den Menschen, der jetzt schon seit Jahren auf die eine oder andere Art und Weise zu hören war, nur noch nicht selbst: Rainer, der seinerzeit die Produktion der Previouslys von David übernommen hatte nimmt Platz in unserer Runde. Inhaltlich dreht sich natürlich viel um Das Unvermeidliche Thema ™, aber wir versuchen noch das eine oder andere sinnvolle zur Debatte hinzuzufügen. Und wir mussten mal wieder über Macs reden. Viel Spaß dabei. - Feedback zur Sendung?
-Schreibe uns einen Kommentar

- -

Diese Sendung soll nie aufhören?
-Unterstütze die Metaebene mit einer Spende -

- - -

Die Corona-Situation — Masken und Zauberwürfel — Gesundheitstechnologie — Differential Privacy — macOS auf ARM — Programmiersprachen

- -

Heute begrüßen wir in unserer Runde den Menschen, der jetzt schon seit Jahren auf die eine oder andere Art und Weise zu hören war, nur noch nicht selbst: Rainer, der seinerzeit die Produktion der Previouslys von David übernommen hatte nimmt Platz in unserer Runde. Inhaltlich dreht sich natürlich viel um Das Unvermeidliche Thema ™, aber wir versuchen noch das eine oder andere sinnvolle zur Debatte hinzuzufügen. Und wir mussten mal wieder über Macs reden. Viel Spaß dabei.

- -

Dauer: 4:05:36

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-On Air -
- -avatar - - -Tim Pritlove - - - -Bitcoin Icon - - - - -Amazon Wishlist Icon - - - - -Liberapay Icon - - - - -SEPA-Überweisung via Online-Banking-Software Icon - - - - -Paypal Icon - - -
- -avatar - - -Letty - - - -Lettys Wunschliste Icon - - -
- -avatar - - -Denis Ahrens - - - -Amazon Wishlist Icon - - - - -Bitcoin Icon - - -
- -avatar - - -Clemens Schrimpe - -
- -avatar - - -Rainer - - - -Bitcoin Icon - - -
-Support -
- -avatar - - -Rainer -
Previously -
- - -Bitcoin Icon - - -
- -avatar - - -Studio Link On Air - -
- - - - - -Shownotes: - - - - - - - -
-]]>
- - - - - - - - - - - - - - - - - - - - - - - 2 - - Tim Pritlove - http://tim.pritlove.org/ - - - Letty - - - Denis Ahrens - - - Clemens Schrimpe - - - Rainer - -
- - FS248 Public Static Final - https://freakshow.fm/fs248-public-static-final - Tue, 31 Mar 2020 07:57:09 +0000 - podlove-2020-03-30t23:22:33+00:00-0d806fba61c9b50 - - - - 03:23:01 - Metaebene Personal Media - Tim Pritlove - Die erste Freak Show, die komplett remote stattfindet - 248 - full - Nun ist es es also soweit: der Coronavirus zwingt auch die Freak Show in den Zwangsabstand und so wird das erste mal nicht live und lebendig in der Metaebene aufgenommen, sondern wir finden uns alle digital zusammen. Klappt trotzdem irgendwie und wir bieten seit langem mal wieder ein volles Team. Die Themen liegen natürlich auf der Hand, aber wir reden auch mal wieder über Macs. Viel Spaß. - Feedback zur Sendung?
-Schreibe uns einen Kommentar

- -

Diese Sendung soll nie aufhören?
-Unterstütze die Metaebene mit einer Spende -

- - -

Die erste Freak Show, die komplett remote stattfindet

- -

Nun ist es es also soweit: der Coronavirus zwingt auch die Freak Show in den Zwangsabstand und so wird das erste mal nicht live und lebendig in der Metaebene aufgenommen, sondern wir finden uns alle digital zusammen. Klappt trotzdem irgendwie und wir bieten seit langem mal wieder ein volles Team. Die Themen liegen natürlich auf der Hand, aber wir reden auch mal wieder über Macs. Viel Spaß.

- -

Dauer: 3:23:01

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-On Air -
- -avatar - - -Tim Pritlove - - - -Bitcoin Icon - - - - -Amazon Wishlist Icon - - - - -Liberapay Icon - - - - -SEPA-Überweisung via Online-Banking-Software Icon - - - - -Paypal Icon - - -
- -avatar - - -Letty - - - -Lettys Wunschliste Icon - - -
- -avatar - - -hukl - - - -Thomann Wishlist Icon - - - - -Amazon Wishlist Icon - - - - -Bitcoin Icon - - -
- -avatar - - -Tala - - - -Amazon Wishlist Icon - - -
- -avatar - - -roddi - - - -Amazon Wishlist Icon - - - - -Bitcoin Icon - - -
- -avatar - - -Clemens Schrimpe - -
-Support -
- -avatar - - -Rainer - - - -Bitcoin Icon - - -
- -avatar - - -Studio Link On Air - -
- - - - - -Shownotes: - - - - - - - -
-]]>
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - 2 - - Tim Pritlove - http://tim.pritlove.org/ - - - Letty - - - hukl - - - Tala - - - roddi - - - Clemens Schrimpe - -
- - FS247 Pulp-Fiction-Koffermoment - https://freakshow.fm/fs247-pulp-fiction-koffermoment - Thu, 27 Feb 2020 21:32:03 +0000 - podlove-2020-02-27t19:11:42+00:00-44e5ad9cc461a0e - - - - 04:02:47 - Metaebene Personal Media - Tim Pritlove - Umziehen — Podlove Web Player — Podlove Publisher — WordPress — Podlove Radiator — Küchengeräte - 247 - full - Heute finden wir uns mit einer ganzen Reihe an Wiedergängern aus den letzten Jahren zusammen. Wir begrüßen Dennis Morhardt, Alexander Heimbuch und Eric Teubert in unserer Runde und reden viel über Podlove und Podcasting, aber auch darüber, wie man sich richtig die Hände wäscht und ohne welchen Küchengeräte ein Leben im 21. Jahrhundert unvorstellbar ist. - Feedback zur Sendung?
-Schreibe uns einen Kommentar

- -

Diese Sendung soll nie aufhören?
-Unterstütze die Metaebene mit einer Spende -

- - -

Umziehen — Podlove Web Player — Podlove Publisher — WordPress — Podlove Radiator — Küchengeräte

- -

Heute finden wir uns mit einer ganzen Reihe an Wiedergängern aus den letzten Jahren zusammen. Wir begrüßen Dennis Morhardt, Alexander Heimbuch und Eric Teubert in unserer Runde und reden viel über Podlove und Podcasting, aber auch darüber, wie man sich richtig die Hände wäscht und ohne welchen Küchengeräte ein Leben im 21. Jahrhundert unvorstellbar ist.

- -

Dauer: 4:02:47

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-On Air -
- -avatar - - -Tim Pritlove - - - -Bitcoin Icon - - - - -Amazon Wishlist Icon - - - - -Liberapay Icon - - - - -SEPA-Überweisung via Online-Banking-Software Icon - - - - -Paypal Icon - - -
- -avatar - - -roddi - - - -Amazon Wishlist Icon - - - - -Bitcoin Icon - - -
- -avatar - - -Eric Teubert - -
- -avatar - - -Dennis Morhardt - -
- -avatar - - -Alexander Heimbuch - - - -Amazon Wishlist Icon - - -
-Support -
- -avatar - - -Rainer -
Previously on Freak Show -
- - -Bitcoin Icon - - -
- -avatar - - -Studio Link On Air - -
- - - - - -Shownotes: - - - - - - - -
-]]>
- - - - - - - - - - - - - - - - - - - - - - - - - 2 - - Tim Pritlove - http://tim.pritlove.org/ - - - roddi - - - Eric Teubert - - - Dennis Morhardt - - - Alexander Heimbuch - -
- - FS246 Raumzeitfenster - https://freakshow.fm/fs246-raumzeitfenster - Thu, 30 Jan 2020 13:10:47 +0000 - podlove-2020-01-30t10:52:26+00:00-666de9cd437c1b9 - - - - 04:02:58 - Metaebene Personal Media - Tim Pritlove - Monty Python und Humor — Coronavirus — Living the Future — Raumzeit — Space — Tesla und die Automobilindustrie — Sozialraum Auto — Promotion — Billie Eilish - 246 - full - Eine kleine Runde mit Gast. Tala ist und Gregor ist mal wieder dazugestossen. Wenig überraschend gehen wir diverse Dinge stark auf der Metaebene an (im doppelten Wortsinne). Wir diskutieren Humor, Viren, Texteditoren, das Leben in der Zukunft und warum das mit der Abkehr vom Auto noch eine Weile dauern wird. - Feedback zur Sendung?
-Schreibe uns einen Kommentar

- -

Diese Sendung soll nie aufhören?
-Unterstütze die Metaebene mit einer Spende -

- - -

Monty Python und Humor — Coronavirus — Living the Future — Raumzeit — Space — Tesla und die Automobilindustrie — Sozialraum Auto — Promotion — Billie Eilish

- -

Eine kleine Runde mit Gast. Tala ist und Gregor ist mal wieder dazugestossen. Wenig überraschend gehen wir diverse Dinge stark auf der Metaebene an (im doppelten Wortsinne). Wir diskutieren Humor, Viren, Texteditoren, das Leben in der Zukunft und warum das mit der Abkehr vom Auto noch eine Weile dauern wird.

- -

Dauer: 4:02:58

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-On Air -
- -avatar - - -Tim Pritlove - - - -Bitcoin Icon - - - - -Amazon Wishlist Icon - - - - -Liberapay Icon - - - - -SEPA-Überweisung via Online-Banking-Software Icon - - - - -Paypal Icon - - -
- -avatar - - -Tala - - - -Amazon Wishlist Icon - - -
- -avatar - - -Gregor Sedlag - -
-Support -
- -avatar - - -Rainer - - - -Bitcoin Icon - - -
- -avatar - - -Studio Link On Air - -
- - - - - -Shownotes: - - - - - - - -
-]]>
- - - - - - - - - - - - - - - - - - - - - 2 - - Tim Pritlove - http://tim.pritlove.org/ - - - Tala - - - Gregor Sedlag - -
- + FS245 Lazy Letty https://freakshow.fm/fs245-lazy-letty Fri, 10 Jan 2020 19:33:54 +0000 -- GitLab From 885688a8b21d6828e2c0bc93ddd5a64a3818cc02 Mon Sep 17 00:00:00 2001 From: Thilo Kogge Date: Sat, 30 Jan 2021 16:39:20 +0100 Subject: [PATCH 02/18] fixed linter errors --- python/podcast/archive.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/python/podcast/archive.py b/python/podcast/archive.py index ba5fa93..a7346e7 100644 --- a/python/podcast/archive.py +++ b/python/podcast/archive.py @@ -1,10 +1,8 @@ """ Archive of listened poposts """ -import datetime import sys import time -from typing import List, Any sys.path.append("../") -- GitLab From 2d0567c372f81becee5f68967fc9a9d51bf03fd9 Mon Sep 17 00:00:00 2001 From: Thilo Kogge Date: Sat, 30 Jan 2021 16:42:14 +0100 Subject: [PATCH 03/18] fixed 3.8 interoperatibility --- python/podcast/archive.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/python/podcast/archive.py b/python/podcast/archive.py index a7346e7..f160ecd 100644 --- a/python/podcast/archive.py +++ b/python/podcast/archive.py @@ -3,6 +3,7 @@ Archive of listened poposts """ import sys import time +from typing import List sys.path.append("../") @@ -28,7 +29,7 @@ class Archive: """ The podcast Archive. It has a list of podposts """ - _archive_entries: list[ArchiveEntry] + _archive_entries: List[ArchiveEntry] def __init__(self): """ -- GitLab From cae5472dec77c69e736943d141e66e274e328708 Mon Sep 17 00:00:00 2001 From: Thilo Kogge Date: Mon, 1 Feb 2021 21:10:23 +0100 Subject: [PATCH 04/18] fixed tests, small refactorings, sorting by Podpost::insert_date again --- pytest.ini | 5 +++++ python/podcast/archive.py | 12 ++++++++++-- python/podcast/data_migration.py | 22 ++++++++++++---------- python/podcast/podpost.py | 22 ++++++++-------------- qml/components/ArchiveHandler.py | 20 +++++--------------- test/__init__.py | 5 +++++ test/test_archive.py | 11 +++++++---- test/test_migration_to_v1.py | 3 +++ test/test_queue.py | 11 +++++++---- 9 files changed, 62 insertions(+), 49 deletions(-) create mode 100644 pytest.ini diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 0000000..a861829 --- /dev/null +++ b/pytest.ini @@ -0,0 +1,5 @@ +[pytest] +log_cli = 1 +log_cli_level = Info +testpaths = + test \ No newline at end of file diff --git a/python/podcast/archive.py b/python/podcast/archive.py index f160ecd..1f7f7dc 100644 --- a/python/podcast/archive.py +++ b/python/podcast/archive.py @@ -3,18 +3,19 @@ Archive of listened poposts """ import sys import time -from typing import List +from typing import List, Iterator sys.path.append("../") from podcast.singleton import Singleton from podcast.factory import Factory +from podcast.podpost import Podpost archivename = "the_archive" class ArchiveEntry: - insert_date: float + insert_date: float # unused yet podpost: str @classmethod @@ -66,6 +67,13 @@ class Archive: for archive_entry in sorted(self._archive_entries, key=lambda e: e.insert_date, reverse=True): yield archive_entry.podpost + def get_podpost_objects(self, url_filter=None) -> Iterator[Podpost]: + for id in self.get_podposts(): + entry: Podpost = Factory().get_podpost(id) + if entry: + if not url_filter or (url_filter and entry.podurl == url_filter): + yield entry + def remove_podpost(self, podpost): """ Remove a podpost from archive diff --git a/python/podcast/data_migration.py b/python/podcast/data_migration.py index 7fd6ccc..5cbe69d 100644 --- a/python/podcast/data_migration.py +++ b/python/podcast/data_migration.py @@ -1,3 +1,4 @@ +import datetime import logging import os from typing import List @@ -75,7 +76,7 @@ def migrate_archive_v0_v1(): archive: Archive = ArchiveFactory().get_archive() if hasattr(archive, "podposts") and len(archive.podposts) > 0 and type(archive.podposts) is not ArchiveEntry: old_archive: List[str] = archive.podposts - archive.__archive_entries = [] + archive._archive_entries = [] for postid in old_archive: archive_entry = ArchiveEntry() post = Factory().get_podpost(postid) @@ -86,19 +87,20 @@ def migrate_archive_v0_v1(): else: archive_entry.insert_date = datetime.datetime.now() archive_entry.podpost = postid - archive.__archive_entries.append(post) + archive._archive_entries.append(post) del archive.podposts def migrate_podpost_v0_v1(self: Podpost): - if hasattr(self, "entry"): - if not hasattr(self, "isaudio"): - self.isaudio = False - if not hasattr(self, "length"): - self.length = 0 - if not self.isaudio: - self.init(self.entry, self.logo_url, self.podurl) - self.save() + if not hasattr(self, "insert_date") or self.insert_date is None: + self.insert_date = datetime.datetime.now().timestamp() + if not hasattr(self, "isaudio"): + self.isaudio = False + if not hasattr(self, "length"): + self.length = 0 + if hasattr(self, "entry") and not self.isaudio: + self.init(self.entry, self.logo_url, self.podurl) + self.save() def migrate_podcast_v0_v1(self: Podcast): diff --git a/python/podcast/podpost.py b/python/podcast/podpost.py index d59b89d..e263fad 100644 --- a/python/podcast/podpost.py +++ b/python/podcast/podpost.py @@ -1,7 +1,7 @@ """ Podpost: a single podcast post """ - +import datetime import sys sys.path.append("../") @@ -41,7 +41,7 @@ class Podpost: title: str percentage: float position: int - isaudio: bool + isaudio: bool # if the post comes from the external folder id: str state: int favorite: bool @@ -51,13 +51,12 @@ class Podpost: href: str duration: Optional[int] length: int - published: Optional[float] - insert_date: object + published: Optional[float] # when the post was published according to feed + insert_date: float # when we entered this post into our db link: Optional[str] type: str - podurl: str + podurl: str # the feed url chapters: List - version: int def __init__(self, entry, podurl, logo_url, data=None, isaudio=False): """ @@ -75,6 +74,7 @@ class Podpost: self.length = 0 self.type = "" self.href = "" + self.insert_date = datetime.datetime.now().timestamp() if isaudio: self.init_file_entry(data, logo_url, podurl) @@ -93,7 +93,6 @@ class Podpost: self.id = data["id"] self.length = int(data["length"]) self.published = 0 - self.insert_date = None self.link = None self.type = data["mime"] self.podurl = podurl @@ -107,7 +106,6 @@ class Podpost: if len(logo_url) > 0: if logo_url[0] == "/": self.logo_path = logo_url - self.insert_date = None try: self.published = timegm(entry.published_parsed) except: @@ -262,12 +260,8 @@ class Podpost: section = format_full_date(self.published) date = self.published fdate = s_to_year(date) - if self.insert_date: - asection = format_full_date(self.insert_date) - adate = self.insert_date - else: - asection = "" - adate = 0 + asection = format_full_date(self.insert_date) + adate = self.insert_date logger.debug("adate, asection %s, %s", adate, asection) logger.debug("date, section %s, %s", date, section) if self.duration: diff --git a/qml/components/ArchiveHandler.py b/qml/components/ArchiveHandler.py index 4a48d88..bc3edf3 100644 --- a/qml/components/ArchiveHandler.py +++ b/qml/components/ArchiveHandler.py @@ -9,28 +9,18 @@ sys.path.append("/usr/share/harbour-podqast/python") from podcast.archive import ArchiveFactory from podcast.factory import Factory +import logging +logger = logging.getLogger(__name__) def get_archive_posts(podurl=None): """ Return a list of all archive posts """ - entries = [] - archive = ArchiveFactory().get_archive() - - for archive_entry in archive.get_podposts(sorted_by_date=True): - entry = Factory().get_podpost(archive_entry.podpost) - if podurl: - if entry.podurl == podurl: - entries.append(entry.get_data()) - else: - if entry: - entries.append(entry.get_data()) - else: - pyotherside.send("ArchiveHandler: We have a none object") - - pyotherside.send("createArchiveList", entries) + posts = ArchiveFactory().get_archive().get_podpost_objects(url_filter=podurl) + sorted_posts = sorted(posts, key=lambda p: p.insert_date, reverse=True) + pyotherside.send("createArchiveList", [post.get_data() for post in sorted_posts]) def get_archive_pod_data(): diff --git a/test/__init__.py b/test/__init__.py index 30af4ca..c174ad0 100644 --- a/test/__init__.py +++ b/test/__init__.py @@ -1,3 +1,5 @@ +import os +import tempfile import types import sys from unittest.mock import Mock @@ -9,3 +11,6 @@ def mock_pyotherside(): module.send = Mock(name=module_name+'.send') mock_pyotherside() + +os.environ["PODQAST_HOME"] = tempfile.gettempdir() +print("tempdir: %s"%os.environ["PODQAST_HOME"] ) diff --git a/test/test_archive.py b/test/test_archive.py index a951a22..a1b958e 100644 --- a/test/test_archive.py +++ b/test/test_archive.py @@ -5,7 +5,6 @@ test the archive import sys from httpretty import HTTPretty, httprettified - from test.test_podcast import read_testdata, xml_headers sys.path.append("../python") @@ -51,9 +50,13 @@ def test_insert(): entry2.save() archive.insert(entry2.id) - result_posts = list(archive.get_podposts(sorted_by_date=True)) - assert entry1.id == result_posts[1] # older - assert entry2.id == result_posts[0] # newer + result_posts = list(archive.get_podposts()) + assert entry1.id in result_posts + assert entry2.id in result_posts + object_ids = [a.id for a in archive.get_podpost_objects('https://freakshow.fm/feed/opus/')] + assert entry1.id in object_ids + assert entry2.id in object_ids + assert 0 == len(list(archive.get_podpost_objects(url_filter='https://i.am.not.corrects/speex'))) def test_get_archives(): """ diff --git a/test/test_migration_to_v1.py b/test/test_migration_to_v1.py index c694f0c..c8fc258 100644 --- a/test/test_migration_to_v1.py +++ b/test/test_migration_to_v1.py @@ -27,6 +27,9 @@ def test_migration(): podcast: Podcast = PodcastFactory().get_podcast(podcasts[0]) assert podcast != None assert len(podcast.entry_ids_old_to_new) == 22 + for post in (Factory().get_podpost(post) for post in podcast.entry_ids_old_to_new for podcast in + map(PodcastFactory().get_podcast, podcasts)): + assert post.insert_date is not None assert len(list(podcast.get_entries())) == 22 del os.environ["PODQAST_HOME"] resetSingletons() diff --git a/test/test_queue.py b/test/test_queue.py index 0927ba5..f2e166d 100644 --- a/test/test_queue.py +++ b/test/test_queue.py @@ -14,9 +14,8 @@ sys.path.append("../python") from podcast.queue import QueueFactory from podcast.factory import Factory from podcast.queue import Queue -from podcast.podcast import Podcast +from podcast.podcast import Podcast, PodcastFactory from podcast.podpost import Podpost -import podcast def test_create_queue(): @@ -70,10 +69,14 @@ def test_get_podposts(): """ queue = QueueFactory().get_queue() - queue.podposts = ['85140fc8e16ba62517877ef4857a85f5bbf83e6a236c659a73489f093e51f154', - 'ce656a3888a282fe9df727e0729fa7d375704e4bd6c0d50e9c0fe7ddafa232ec'] + podcast = PodcastFactory().get_podcast('https://freakshow.fm/feed/opus/') + entry = podcast.entry_ids_old_to_new[1] + entry2 = podcast.entry_ids_old_to_new[0] + queue.insert_top(entry) + queue.insert_top(entry2) postids = list(queue.get_podposts()) assert len(postids) == 2 for post in postids: assert type(post) == str assert type(Factory().get_podpost(post)) == Podpost + assert post in [entry,entry2] -- GitLab From 6d79cdef668bd638cc913fd9608a52e660ff7c7a Mon Sep 17 00:00:00 2001 From: Thilo Kogge Date: Mon, 1 Feb 2021 21:20:06 +0100 Subject: [PATCH 05/18] removed adate completly, sorting everything by insert_date now --- python/podcast/podpost.py | 4 ---- qml/components/FavoriteHandler.py | 4 +--- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/python/podcast/podpost.py b/python/podcast/podpost.py index e263fad..39beaa2 100644 --- a/python/podcast/podpost.py +++ b/python/podcast/podpost.py @@ -261,9 +261,6 @@ class Podpost: date = self.published fdate = s_to_year(date) asection = format_full_date(self.insert_date) - adate = self.insert_date - logger.debug("adate, asection %s, %s", adate, asection) - logger.debug("date, section %s, %s", date, section) if self.duration: duration = s_to_hr(self.duration) else: @@ -294,7 +291,6 @@ class Podpost: "date": date, "fdate": fdate, "section": section, - "adate": adate, "asection": asection, "length": self.length, "duration": duration, diff --git a/qml/components/FavoriteHandler.py b/qml/components/FavoriteHandler.py index e19a9a9..9ea0aab 100644 --- a/qml/components/FavoriteHandler.py +++ b/qml/components/FavoriteHandler.py @@ -79,10 +79,8 @@ def get_favorites(podurl=None): posts = get_queue_favorites(podurl) posts = append_archive_favorites(posts, podurl=podurl) postsdata = [] - for post in posts: + for post in sorted(posts, key=lambda p: p.insert_date, reverse=True): postsdata.append(post.get_data()) - - postsdata.sort(key=lambda r: r["adate"], reverse=True) pyotherside.send("favoriteList", postsdata) -- GitLab From e9e9e3f4bac77004376ade6556a455b343b0368e Mon Sep 17 00:00:00 2001 From: Thilo Kogge Date: Mon, 1 Feb 2021 22:06:44 +0100 Subject: [PATCH 06/18] moved favorite logic to main package, added tests, fixed tests, stabilized tests --- python/podcast/archive.py | 7 ++- python/podcast/favorite.py | 80 ++++++++++++++++++++++++++++ qml/components/FavoriteHandler.py | 87 +++---------------------------- test/__init__.py | 14 ++++- test/test_archive.py | 36 ++++++++----- test/test_favorites.py | 35 +++++++++++++ test/test_queue.py | 28 +++++++++- 7 files changed, 188 insertions(+), 99 deletions(-) create mode 100644 python/podcast/favorite.py create mode 100644 test/test_favorites.py diff --git a/python/podcast/archive.py b/python/podcast/archive.py index 1f7f7dc..9318ec5 100644 --- a/python/podcast/archive.py +++ b/python/podcast/archive.py @@ -20,6 +20,8 @@ class ArchiveEntry: @classmethod def of_podpost(cls, postid: str): + if not type(postid) == str: + raise ValueError("did not get the id of a post") archive_entry = ArchiveEntry() archive_entry.podpost = postid archive_entry.insert_date = time.time() @@ -47,11 +49,12 @@ class Archive: store = Factory().get_store() store.store(archivename, self) - def insert(self, podpost): + def insert(self, podpost: str): """ Insert an podost """ - + if type(podpost) != str: + raise ValueError("please supply the id of the post") if podpost not in [e.podpost for e in self._archive_entries]: self._archive_entries.insert(0, ArchiveEntry.of_podpost(podpost)) self.save() diff --git a/python/podcast/favorite.py b/python/podcast/favorite.py new file mode 100644 index 0000000..6117218 --- /dev/null +++ b/python/podcast/favorite.py @@ -0,0 +1,80 @@ +from podcast.factory import Factory + + +def get_queue_favorites(podurl=None): + """ + Return a list of all favorites posts from queue + """ + + from podcast.queue import QueueFactory + + posts = [] + + queue = QueueFactory().get_queue() + for post in queue.get_podposts(): + p = Factory().get_podpost(post) + try: + if p.favorite: + if podurl: + if p.podurl == podurl: + posts.append(p) + else: + posts.append(p) + except: + pass + return posts + + +def append_archive_favorites(posts, podurl=None): + """ + append archive favorites to posts list + """ + + from podcast.archive import ArchiveFactory + + archive = ArchiveFactory().get_archive() + + for post in archive.get_podposts(): + p = Factory().get_podpost(post) + try: + if p.favorite: + if p not in posts: + if podurl: + if p.podurl == podurl: + posts.append(p) + else: + posts.append(p) + except: + pass + + return posts + + +def get_favorites(podurl=None): + """ + Test + """ + + posts = get_queue_favorites(podurl) + posts = append_archive_favorites(posts, podurl=podurl) + yield from sorted(posts, key=lambda p: p.insert_date, reverse=True) + + +def get_favorite_podcast_stats(): + """ + """ + + entries = {} + + posts = get_queue_favorites() + posts = append_archive_favorites(posts) + for p in posts: + if p.podurl in entries: + entries[p.podurl]["count"] += 1 + else: + if p.logo_url: + logo_url = p.logo_url + else: + logo_url = "../../images/podcast.png" + entries[p.podurl] = {"count": 1, "logo_url": logo_url} + return entries diff --git a/qml/components/FavoriteHandler.py b/qml/components/FavoriteHandler.py index 9ea0aab..98dfd6b 100644 --- a/qml/components/FavoriteHandler.py +++ b/qml/components/FavoriteHandler.py @@ -6,6 +6,7 @@ import threading import sys sys.path.append("/usr/share/harbour-podqast/python") +import podcast.favorite as favorite from podcast.factory import Factory @@ -22,87 +23,11 @@ def toggle_favorite(podpost, do_download=False): pyotherside.send("favoriteToggled", podpost, post.toggle_fav(do_download)) -def get_queue_favorites(podurl=None): - """ - Return a list of all favorites posts from queue - """ - - from podcast.queue import QueueFactory - - posts = [] - - queue = QueueFactory().get_queue() - for post in queue.get_podposts(): - p = Factory().get_podpost(post) - try: - if p.favorite: - if podurl: - if p.podurl == podurl: - posts.append(p) - else: - posts.append(p) - except: - pass - return posts - - -def append_archive_favorites(posts, podurl=None): - """ - append archive favorites to posts list - """ - - from podcast.archive import ArchiveFactory - - archive = ArchiveFactory().get_archive() - - for post in archive.get_podposts(): - p = Factory().get_podpost(post) - try: - if p.favorite: - if p not in posts: - if podurl: - if p.podurl == podurl: - posts.append(p) - else: - posts.append(p) - except: - pass - - return posts - - -def get_favorites(podurl=None): - """ - Test - """ - - posts = get_queue_favorites(podurl) - posts = append_archive_favorites(posts, podurl=podurl) - postsdata = [] - for post in sorted(posts, key=lambda p: p.insert_date, reverse=True): - postsdata.append(post.get_data()) - pyotherside.send("favoriteList", postsdata) - +def send_favorites(podurl=None): + pyotherside.send("favoriteList", [p.get_data() for p in favorite.get_favorites(podurl)]) def get_fav_pod_data(): - """ - """ - - entries = {} - - posts = get_queue_favorites() - posts = append_archive_favorites(posts) - for p in posts: - if p.podurl in entries: - entries[p.podurl]["count"] += 1 - else: - if p.logo_url: - logo_url = p.logo_url - else: - logo_url = "../../images/podcast.png" - entries[p.podurl] = {"count": 1, "logo_url": logo_url} - - pyotherside.send("archivePodList", entries) + pyotherside.send("archivePodList", favorite.get_favorite_podcast_stats()) class FavoriteHandler: @@ -125,10 +50,10 @@ class FavoriteHandler: return if podurl: self.bgthread2 = threading.Thread( - target=get_favorites, args=[podurl] + target=send_favorites, args=[podurl] ) else: - self.bgthread2 = threading.Thread(target=get_favorites) + self.bgthread2 = threading.Thread(target=send_favorites) self.bgthread2.start() def getfavpoddata(self): diff --git a/test/__init__.py b/test/__init__.py index c174ad0..2de8407 100644 --- a/test/__init__.py +++ b/test/__init__.py @@ -4,13 +4,23 @@ import types import sys from unittest.mock import Mock + def mock_pyotherside(): module_name = "pyotherside" module = types.ModuleType(module_name) sys.modules[module_name] = module - module.send = Mock(name=module_name+'.send') + module.send = Mock(name=module_name + '.send') + mock_pyotherside() os.environ["PODQAST_HOME"] = tempfile.gettempdir() -print("tempdir: %s"%os.environ["PODQAST_HOME"] ) +print("tempdir: %s" % os.environ["PODQAST_HOME"]) + + +def cleanup_archive(): + from podcast.archive import ArchiveFactory + archive = ArchiveFactory().get_archive() + for post in archive.get_podposts(): + archive.remove_podpost(post) + archive.save() diff --git a/test/test_archive.py b/test/test_archive.py index a1b958e..2c8e7a8 100644 --- a/test/test_archive.py +++ b/test/test_archive.py @@ -5,6 +5,8 @@ test the archive import sys from httpretty import HTTPretty, httprettified + +from . import cleanup_archive from test.test_podcast import read_testdata, xml_headers sys.path.append("../python") @@ -14,6 +16,7 @@ from podcast.factory import Factory from podcast.podcast import Podcast from podcast.podpost import Podpost + def test_create_archive(): """ test if archive is created @@ -25,6 +28,7 @@ def test_create_archive(): a2 = ArchiveFactory().get_archive() assert a1 == a2 + def test_archive_save(): """ does the archive save itself? @@ -33,41 +37,47 @@ def test_archive_save(): a = ArchiveFactory().get_archive() a.save() -@httprettified + def test_insert(): """ """ + + archive = ArchiveFactory().get_archive() + entry1, entry2 = setup_archive_with_2_posts(archive) + + result_posts = list(archive.get_podposts()) + assert entry1.id in result_posts + assert entry2.id in result_posts + object_ids = [a.id for a in archive.get_podpost_objects('https://freakshow.fm/feed/opus/')] + assert entry1.id in object_ids + assert entry2.id in object_ids + assert 0 == len(list(archive.get_podpost_objects(url_filter='https://i.am.not.corrects/speex'))) + + +@httprettified +def setup_archive_with_2_posts(archive): HTTPretty.register_uri(HTTPretty.GET, 'https://freakshow.fm/feed/opus/', body=read_testdata('testdata/freakshow.rss'), adding_headers=xml_headers) - - archive = ArchiveFactory().get_archive() podcast = Podcast('https://freakshow.fm/feed/opus/') entry1 = podcast.get_entry(podcast.entry_ids_old_to_new[0]) entry1.save() archive.insert(entry1.id) - entry2 = podcast.get_entry(podcast.entry_ids_old_to_new[1]) entry2.save() archive.insert(entry2.id) + return entry1, entry2 - result_posts = list(archive.get_podposts()) - assert entry1.id in result_posts - assert entry2.id in result_posts - object_ids = [a.id for a in archive.get_podpost_objects('https://freakshow.fm/feed/opus/')] - assert entry1.id in object_ids - assert entry2.id in object_ids - assert 0 == len(list(archive.get_podpost_objects(url_filter='https://i.am.not.corrects/speex'))) def test_get_archives(): """ Test listing of podposts """ - + cleanup_archive() a = ArchiveFactory().get_archive() + setup_archive_with_2_posts(a) count = 0 for post in a.get_podposts(): assert type(post) == str assert type(Factory().get_podpost(post)) == Podpost count += 1 - # make sure to clean ~/.local/share/harbour-podqast if this fails assert count == 2 diff --git a/test/test_favorites.py b/test/test_favorites.py new file mode 100644 index 0000000..c41e8fc --- /dev/null +++ b/test/test_favorites.py @@ -0,0 +1,35 @@ +from httpretty import HTTPretty, httprettified + +from podcast.archive import ArchiveFactory +from podcast.factory import Factory +from . import cleanup_archive +from test.test_podcast import read_testdata, xml_headers + + +@httprettified +def test_queue_favorites(): + """ + Test listing of podposts + """ + HTTPretty.register_uri(HTTPretty.GET, 'https://freakshow.fm/feed/opus/', + body=read_testdata('testdata/freakshow.rss'), adding_headers=xml_headers) + + from podcast import favorite + from podcast.podcast import PodcastFactory + cleanup_archive() + + url = 'https://freakshow.fm/feed/opus/' + podcast = PodcastFactory().get_podcast(url) + fav_posts = [Factory().get_podpost(podcast.entry_ids_old_to_new[i]) for i in range(0, 10, 2)] + assert len(fav_posts) == 5 + archive = ArchiveFactory().get_archive() + assert len(list(archive.get_podposts())) == 0 + for post in fav_posts: + post.favorite = True + post.save() + archive.insert(post.id) + archive.save() + assert len(favorite.get_queue_favorites()) == 0 + assert len(list(favorite.get_favorites())) == 5 + assert len(favorite.get_favorite_podcast_stats()) == 1 + assert favorite.get_favorite_podcast_stats()[url]['count'] == 5 diff --git a/test/test_queue.py b/test/test_queue.py index f2e166d..7043775 100644 --- a/test/test_queue.py +++ b/test/test_queue.py @@ -38,6 +38,7 @@ def test_queue_save(): q = QueueFactory().get_queue() q.save() + @httpretty.activate def test_insert_top(): """ @@ -79,4 +80,29 @@ def test_get_podposts(): for post in postids: assert type(post) == str assert type(Factory().get_podpost(post)) == Podpost - assert post in [entry,entry2] + assert post in [entry, entry2] + + +def test_queue_favorites(): + """ + Test listing of podposts + """ + from podcast import favorite + + queue = QueueFactory().get_queue() + url = 'https://freakshow.fm/feed/opus/' + podcast = PodcastFactory().get_podcast(url) + for post in (Factory().get_podpost(podcast.entry_ids_old_to_new[i]) for i in range(0, 10, 2)): + post.favorite = True + post.save() + for id in podcast.entry_ids_old_to_new: + queue.insert_bottom(id) + assert len(queue.podposts) == 20 + assert len(favorite.get_queue_favorites('https://wrong.url')) == 0 + assert [fav.id for fav in favorite.get_queue_favorites()] == [podcast.entry_ids_old_to_new[i] for i in + range(0, 10, 2)] + assert len(favorite.get_queue_favorites(url)) == 5 + for id in podcast.entry_ids_old_to_new: + queue.remove(id) + assert len(queue.podposts) == 0 + assert len(favorite.get_queue_favorites()) == 0 -- GitLab From c1fd61102cf6bde258d8d8a9a652cef1aaaba523 Mon Sep 17 00:00:00 2001 From: Thilo Kogge Date: Wed, 3 Feb 2021 16:30:00 +0100 Subject: [PATCH 07/18] create separate factory for podposts introduced basefactory to prevent code duplication --- python/podcast/archive.py | 4 +- python/podcast/data_migration.py | 8 ++-- python/podcast/factory.py | 71 ++++--------------------------- python/podcast/favorite.py | 20 ++++----- python/podcast/inbox.py | 4 +- python/podcast/podcast.py | 48 ++++----------------- python/podcast/podpost.py | 38 ++++++++++++++--- python/podcast/queue.py | 22 +++++----- qml/components/ArchiveHandler.py | 4 +- qml/components/ExternalHandler.py | 6 +-- qml/components/FavoriteHandler.py | 7 ++- qml/components/InboxHandler.py | 6 ++- qml/components/QueueHandler.py | 12 +++--- test/__init__.py | 7 +++ test/test_archive.py | 4 +- test/test_factory.py | 59 +------------------------ test/test_favorites.py | 6 ++- test/test_migration_to_v1.py | 3 +- test/test_queue.py | 6 +-- 19 files changed, 121 insertions(+), 214 deletions(-) diff --git a/python/podcast/archive.py b/python/podcast/archive.py index 9318ec5..6cb950c 100644 --- a/python/podcast/archive.py +++ b/python/podcast/archive.py @@ -9,7 +9,7 @@ sys.path.append("../") from podcast.singleton import Singleton from podcast.factory import Factory -from podcast.podpost import Podpost +from podcast.podpost import Podpost, PodpostFactory archivename = "the_archive" @@ -72,7 +72,7 @@ class Archive: def get_podpost_objects(self, url_filter=None) -> Iterator[Podpost]: for id in self.get_podposts(): - entry: Podpost = Factory().get_podpost(id) + entry: Podpost = PodpostFactory().get_podpost(id) if entry: if not url_filter or (url_filter and entry.podurl == url_filter): yield entry diff --git a/python/podcast/data_migration.py b/python/podcast/data_migration.py index 5cbe69d..8239696 100644 --- a/python/podcast/data_migration.py +++ b/python/podcast/data_migration.py @@ -8,7 +8,7 @@ import pyotherside from podcast.factory import Factory from podcast.podcast import PodcastFactory, Podcast from podcast.podcastlist import PodcastListFactory -from podcast.podpost import Podpost +from podcast.podpost import Podpost, PodpostFactory logger = logging.getLogger(__name__) @@ -41,7 +41,7 @@ def run_migrations(): if podcast != None: migrate_podcast_v0_v1(podcast) for entry_id in podcast.entry_ids_old_to_new: - migrate_podpost_v0_v1(Factory().get_podpost(entry_id)) + migrate_podpost_v0_v1(PodpostFactory().get_podpost(entry_id)) i += 1 pyotherside.send("migrationProgress", i) migrate_archive_v0_v1() @@ -79,7 +79,7 @@ def migrate_archive_v0_v1(): archive._archive_entries = [] for postid in old_archive: archive_entry = ArchiveEntry() - post = Factory().get_podpost(postid) + post = PodpostFactory().get_podpost(postid) if not post: continue if hasattr(post, "insert_date") and post.insert_date: @@ -118,7 +118,7 @@ def migrate_podcast_v0_v1(self: Podcast): logger.info("Found %d entries in old format", old_entry_count) for entry in self.feed.entries: post = Podpost(entry, self.url, image) - p2 = Factory().get_podpost(post.id) + p2 = PodpostFactory().get_podpost(post.id) if p2: post = p2 post.save() diff --git a/python/podcast/factory.py b/python/podcast/factory.py index 15d7dca..450c440 100644 --- a/python/podcast/factory.py +++ b/python/podcast/factory.py @@ -16,25 +16,19 @@ from podcast.store import Store from podcast import util -import feedparser import pyotherside - -class Factory(metaclass=Singleton): - """ - Factory for - """ +class BaseFactory(metaclass=Singleton): data_home: str + store: Store def __init__(self, progname="harbour-podqast"): """ Initialization - feedcache: cache for feeds store: store for feeds """ self.queue = None - self.feedcache = Cache() self.object = None self._set_paths(progname) @@ -53,7 +47,6 @@ class Factory(metaclass=Singleton): util.make_directory(self.afilepath) self.store = Store(storepath) - self.podpostcache = Cache(limit=500) def _set_paths(self, progname): home = os.path.expanduser("~") @@ -71,6 +64,13 @@ class Factory(metaclass=Singleton): self.cache_home = os.path.join(xdg_cache_home, progname) +class Factory(BaseFactory): + """ + Factory for + """ + def __init__(self): + super().__init__() + def init_from_qml(self, object): """ Get QML object @@ -113,59 +113,6 @@ class Factory(metaclass=Singleton): except: return None - def get_feed(self, index): - """ - get a feed object by index - index: index of feed - """ - - feed = self.feedcache.get(index) - if not feed: - feed = self.store.get(index) - if not feed: - feed = feedparser.parse(index) - if feed.bozo != 0: - pyotherside.send("get_feed: error in feed parsing") - return None - self.feedcache.store(index, feed) - - return feed - - def safe_feed(self, index): - """ - Safe feed to disk - """ - - feed = self.get_feed(index) - if feed: - self.store.store(index, feed) - - def delete_feed(self, index): - """ - Delete file from disk - """ - - self.store.delete(index) - - def get_podpost(self, index): - """ - Get a podpost from cache or store - """ - - podpost = self.podpostcache.get(index) - if not podpost: - podpost = self.store.get(index) - if podpost: - self.podpostcache.store(index, podpost) - else: - return None - - return podpost - - def delete_podpost(self, index): - self.podpostcache.delete(index) - self.store.delete(index) - def get_store(self): """ get the store diff --git a/python/podcast/favorite.py b/python/podcast/favorite.py index 6117218..db481cd 100644 --- a/python/podcast/favorite.py +++ b/python/podcast/favorite.py @@ -1,4 +1,4 @@ -from podcast.factory import Factory +from podcast.podpost import PodpostFactory def get_queue_favorites(podurl=None): @@ -12,16 +12,14 @@ def get_queue_favorites(podurl=None): queue = QueueFactory().get_queue() for post in queue.get_podposts(): - p = Factory().get_podpost(post) - try: - if p.favorite: - if podurl: - if p.podurl == podurl: - posts.append(p) - else: + p = PodpostFactory().get_podpost(post) + if p.favorite: + if podurl: + if p.podurl == podurl: posts.append(p) - except: - pass + else: + posts.append(p) + return posts @@ -35,7 +33,7 @@ def append_archive_favorites(posts, podurl=None): archive = ArchiveFactory().get_archive() for post in archive.get_podposts(): - p = Factory().get_podpost(post) + p = PodpostFactory().get_podpost(post) try: if p.favorite: if p not in posts: diff --git a/python/podcast/inbox.py b/python/podcast/inbox.py index 41cf20f..aad9af7 100644 --- a/python/podcast/inbox.py +++ b/python/podcast/inbox.py @@ -5,6 +5,8 @@ The inbox queue. This also uses a Factory for instancing import sys import time +from podcast.podpost import PodpostFactory + sys.path.append("../") from podcast.singleton import Singleton @@ -44,7 +46,7 @@ class Inbox: if podpost not in self.podposts: self.podposts.insert(0, podpost) - post = Factory().get_podpost(podpost) + post = PodpostFactory().get_podpost(podpost) if post.insert_date == None: post.insert_date = time.time() post.save() diff --git a/python/podcast/podcast.py b/python/podcast/podcast.py index 424d634..1c495a1 100644 --- a/python/podcast/podcast.py +++ b/python/podcast/podcast.py @@ -7,14 +7,14 @@ from typing import List, Dict, Optional, Generator from feedparser import FeedParserDict -from podcast.factory import Factory +from podcast.factory import Factory, BaseFactory sys.path.append("/usr/share/podqast/python") from podcast.singleton import Singleton from podcast.cache import Cache from podcast.store import Store -from podcast.podpost import Podpost +from podcast.podpost import Podpost, PodpostFactory from podcast import util from email.utils import mktime_tz, parsedate_tz from calendar import timegm @@ -230,9 +230,8 @@ class Podcast: """ Get an entry from podcast by id """ - from podcast.factory import Factory - entry = Factory().get_podpost(id) + entry = PodpostFactory().get_podpost(id) return entry def get_entries(self) -> Generator[dict, None, None]: @@ -242,7 +241,7 @@ class Podcast: from podcast.factory import Factory logger.debug("Podcast get_entries started.") for id in self.entry_ids_old_to_new: - yield Factory().get_podpost(id).get_data() + yield PodpostFactory().get_podpost(id).get_data() def get_latest_entry(self) -> Optional[Podpost]: """ @@ -386,45 +385,16 @@ class Podcast: } -class PodcastFactory(metaclass=Singleton): +class PodcastFactory(BaseFactory): """ Helping Factory """ store: Store podcastcache: Cache - def __init__(self, progname="harbour-podqast"): - """ - Initialization - """ - - self.podcastcache = Cache(limit=50) - home = os.path.expanduser("~") - xdg_data_home = os.environ.get( - "XDG_DATA_HOME", os.path.join(home, ".local", "share") - ) - xdg_config_home = os.environ.get( - "XDG_CONFIG_HOME", os.path.join(home, ".config") - ) - xdg_cache_home = os.path.join( - "XDG_CACHE_HOME", os.path.join(home, ".cache") - ) - - self.data_home = os.path.join(xdg_data_home, progname) - self.config_home = os.path.join(xdg_config_home, progname) - self.cache_home = os.path.join(xdg_cache_home, progname) - - if "PODQAST_HOME" in os.environ: - home = os.environ["PODQAST_HOME"] - self.data_home = self.config_home = self.cache_home = home - - self.iconpath = os.path.join(self.data_home, "icons") - storepath = os.path.join(self.data_home, "store") - util.make_directory(self.data_home) - util.make_directory(storepath) - util.make_directory(self.iconpath) - - self.store = Store(storepath) + def __init__(self): + super().__init__() + self.podcastcache = Cache(50) def get_podcast(self, url, preview: bool = False) -> Optional[Podcast]: """ @@ -454,6 +424,6 @@ class PodcastFactory(metaclass=Singleton): to_delete = self.get_podcast(url) if to_delete != None: for post in to_delete.entry_ids_old_to_new: - Factory().delete_podpost(post) + PodpostFactory().delete_podpost(post) self.podcastcache.delete(url) self.store.delete(url) diff --git a/python/podcast/podpost.py b/python/podcast/podpost.py index 39beaa2..b012c3b 100644 --- a/python/podcast/podpost.py +++ b/python/podcast/podpost.py @@ -4,6 +4,8 @@ Podpost: a single podcast post import datetime import sys +from podcast.cache import Cache + sys.path.append("../") import hashlib @@ -14,11 +16,10 @@ from email.utils import mktime_tz, parsedate_tz from calendar import timegm from typing import Optional, List -from podcast.factory import Factory +from podcast.factory import Factory, BaseFactory from podcast.util import s_to_hr, tx_to_s, dl_from_url, dl_from_url_progress from podcast.util import delete_file, format_full_date, s_to_year, make_symlink from podcast.util import get_audio_meta, get_audio_tech, down_audio_icon -from podcast.queue import QueueFactory import pyotherside import time import re @@ -310,9 +311,7 @@ class Podpost: logger.info("storing podpost %s", self.id) if not self.logo_path: self.download_icon() - store = Factory().get_store() - store.store(self.id, self) - logger.debug("...done") + PodpostFactory().persist(self.id, self) def download_audio(self): """ @@ -497,6 +496,7 @@ class Podpost: toggle favorite flag (and create one if it not exists) return: bool if favorite """ + from podcast.queue import QueueFactory try: self.favorite = not self.favorite @@ -572,3 +572,31 @@ class Podpost: return favorite +class PodpostFactory(BaseFactory): + + def __init__(self): + super().__init__() + self.podpostcache = Cache(limit=500) + + def get_podpost(self, index): + """ + Get a podpost from cache or store + """ + + podpost = self.podpostcache.get(index) + if not podpost: + podpost = self.store.get(index) + if podpost: + self.podpostcache.store(index, podpost) + else: + return None + + return podpost + + def delete_podpost(self, index): + self.podpostcache.delete(index) + self.store.delete(index) + + def persist(self, id, self1): + self.store.store(id,self1) + self.podpostcache.store(id,self1) diff --git a/python/podcast/queue.py b/python/podcast/queue.py index 51272ac..7454a09 100644 --- a/python/podcast/queue.py +++ b/python/podcast/queue.py @@ -8,6 +8,8 @@ import os import pyotherside import time +from podcast.podpost import PodpostFactory + sys.path.append("../") from podcast.singleton import Singleton @@ -57,7 +59,7 @@ class Queue: self.top = None else: podpost = self.podposts[0] - self.top = Factory().get_podpost(podpost) + self.top = PodpostFactory().get_podpost(podpost) return self.top @@ -81,7 +83,7 @@ class Queue: self.podposts.remove(podpost) self.podposts.insert(0, podpost) - post = Factory().get_podpost(podpost) + post = PodpostFactory().get_podpost(podpost) position = post.get_position / 1000 duration = post.duration if duration == 0 or duration - position < 60: @@ -110,7 +112,7 @@ class Queue: self.podposts.remove(podpost) self.podposts.insert(1, podpost) - post = Factory().get_podpost(podpost) + post = PodpostFactory().get_podpost(podpost) position = post.get_position / 1000 duration = post.duration if duration == 0 or duration - position < 60: @@ -136,7 +138,7 @@ class Queue: self.podposts.remove(podpost) self.podposts.append(podpost) - post = Factory().get_podpost(podpost) + post = PodpostFactory().get_podpost(podpost) position = post.get_position / 1000 duration = post.duration if duration == 0 or duration - position < 60: @@ -156,16 +158,16 @@ class Queue: """ if podpost in self.podposts: - post = Factory().get_podpost(podpost) + post = PodpostFactory().get_podpost(podpost) post.delete_file() post.state = 0 post.save() if podpost == self.podposts[0] and len(self.podposts) > 1: - post1 = Factory().get_podpost(self.podposts[1]) + post1 = PodpostFactory().get_podpost(self.podposts[1]) pyotherside.send("setpos", post1.position) self.podposts.remove(podpost) else: - Factory().get_podpost(podpost).save() + PodpostFactory().get_podpost(podpost).save() self.save() @@ -175,7 +177,7 @@ class Queue: """ if podpost in self.podposts: - post = Factory().get_podpost(podpost) + post = PodpostFactory().get_podpost(podpost) for perc in post.download_audio(): yield perc @@ -188,7 +190,7 @@ class Queue: """ for podpost in self.podposts: - post = Factory().get_podpost(podpost) + post = PodpostFactory().get_podpost(podpost) try: if post.file_path: if os.path.isfile(post.file_path): @@ -204,7 +206,7 @@ class Queue: """ return ( - Factory() + PodpostFactory() .get_podpost(self.podposts[0]) .get_hr_time(position=position) ) diff --git a/qml/components/ArchiveHandler.py b/qml/components/ArchiveHandler.py index bc3edf3..c9301fa 100644 --- a/qml/components/ArchiveHandler.py +++ b/qml/components/ArchiveHandler.py @@ -5,6 +5,8 @@ import pyotherside import threading import sys +from podcast.podpost import PodpostFactory + sys.path.append("/usr/share/harbour-podqast/python") from podcast.archive import ArchiveFactory @@ -32,7 +34,7 @@ def get_archive_pod_data(): archive = ArchiveFactory().get_archive() for post in archive.get_podposts(): - entry = Factory().get_podpost(post) + entry = PodpostFactory().get_podpost(post) if entry.podurl in entries: entries[entry.podurl]["count"] += 1 else: diff --git a/qml/components/ExternalHandler.py b/qml/components/ExternalHandler.py index b4be120..02406a3 100644 --- a/qml/components/ExternalHandler.py +++ b/qml/components/ExternalHandler.py @@ -15,7 +15,7 @@ from podcast.archive import ArchiveFactory from podcast.queue import QueueFactory from podcast.factory import Factory -from podcast.podpost import Podpost +from podcast.podpost import Podpost, PodpostFactory import inotify.adapters import mutagen @@ -170,7 +170,7 @@ def get_external_posts(): for post in external.get_podposts(): pyotherside.send("podposts loop") - entry = Factory().get_podpost(post) + entry = PodpostFactory().get_podpost(post) entries.append(entry.get_data()) pyotherside.send("after py external") # entries.sort(key = lambda r: r["edate"], reverse=True) @@ -186,7 +186,7 @@ def get_external_pod_data(): external = ExternalFactory().get_external() for post in external.get_podposts(): - entry = Factory().get_podpost(post) + entry = PodpostFactory().get_podpost(post) if entry.podurl in entries: entries[entry.podurl]["count"] += 1 else: diff --git a/qml/components/FavoriteHandler.py b/qml/components/FavoriteHandler.py index 98dfd6b..2f686e9 100644 --- a/qml/components/FavoriteHandler.py +++ b/qml/components/FavoriteHandler.py @@ -5,18 +5,17 @@ import pyotherside import threading import sys +from podcast.podpost import PodpostFactory + sys.path.append("/usr/share/harbour-podqast/python") import podcast.favorite as favorite -from podcast.factory import Factory - - def toggle_favorite(podpost, do_download=False): """ Return a list of all archive posts """ - post = Factory().get_podpost(podpost) + post = PodpostFactory().get_podpost(podpost) if not post: return diff --git a/qml/components/InboxHandler.py b/qml/components/InboxHandler.py index f0038ca..a315be6 100644 --- a/qml/components/InboxHandler.py +++ b/qml/components/InboxHandler.py @@ -5,6 +5,8 @@ import pyotherside import threading import sys +from podcast.podpost import PodpostFactory + sys.path.append("/usr/share/harbour-podqast/python") from podcast.inbox import InboxFactory @@ -23,7 +25,7 @@ def get_inbox_posts(podurl=None): pyotherside.send("len Inbox %d" % len(inbox.podposts)) for post in inbox.get_podposts(): - entry = Factory().get_podpost(post) + entry = PodpostFactory().get_podpost(post) if podurl: if entry.podurl == podurl: entries.append(entry.get_data()) @@ -43,7 +45,7 @@ def get_inbox_pod_data(): inbox = InboxFactory().get_inbox() for post in inbox.get_podposts(): - entry = Factory().get_podpost(post) + entry = PodpostFactory().get_podpost(post) if entry.podurl in entries: entries[entry.podurl]["count"] += 1 else: diff --git a/qml/components/QueueHandler.py b/qml/components/QueueHandler.py index 2c88455..59e10e7 100644 --- a/qml/components/QueueHandler.py +++ b/qml/components/QueueHandler.py @@ -5,6 +5,8 @@ import pyotherside import threading import sys +from podcast.podpost import PodpostFactory + sys.path.append("/usr/share/harbour-podqast/python") from podcast.queue import QueueFactory @@ -21,7 +23,7 @@ def get_queue_posts(moved=""): queue = QueueFactory().get_queue() for post in queue.get_podposts(): - entry = Factory().get_podpost(post) + entry = PodpostFactory().get_podpost(post) edata = entry.get_data() edata["moved"] = edata["id"] == moved entries.append(edata) @@ -43,7 +45,7 @@ def get_first_entry(): if len(queue.podposts) < 1: return None pyotherside.send( - "firstentry", Factory().get_podpost(queue.podposts[0]).get_data() + "firstentry", PodpostFactory().get_podpost(queue.podposts[0]).get_data() ) @@ -214,7 +216,7 @@ def get_episode_chapters(id): id: id of the episode """ - entry = Factory().get_podpost(id) + entry = PodpostFactory().get_podpost(id) chapters = entry.get_chapters() pyotherside.send("episodeChapters", chapters) @@ -225,7 +227,7 @@ def send_first_episode_chapters(id): (necessary when chapters of the currently playing episode are selected/deselected) """ - entry = Factory().get_podpost(id) + entry = PodpostFactory().get_podpost(id) edata = entry.get_data() chapters = entry.get_chapters() pyotherside.send("setFirst", edata, chapters) @@ -237,7 +239,7 @@ def toggle_chapter(episodeid, chapterid): episodeid: id of the episode chapterid: chapter number """ - entry = Factory().get_podpost(episodeid) + entry = PodpostFactory().get_podpost(episodeid) entry.toggle_chapter(chapterid) diff --git a/test/__init__.py b/test/__init__.py index 2de8407..24706ea 100644 --- a/test/__init__.py +++ b/test/__init__.py @@ -24,3 +24,10 @@ def cleanup_archive(): for post in archive.get_podposts(): archive.remove_podpost(post) archive.save() + +def cleanup_queue(): + from podcast.queue import QueueFactory + queue = QueueFactory().get_queue() + for post in queue.get_podposts(): + queue.remove(post) + queue.save() diff --git a/test/test_archive.py b/test/test_archive.py index 2c8e7a8..b74fc3a 100644 --- a/test/test_archive.py +++ b/test/test_archive.py @@ -14,7 +14,7 @@ sys.path.append("../python") from podcast.archive import ArchiveFactory, Archive from podcast.factory import Factory from podcast.podcast import Podcast -from podcast.podpost import Podpost +from podcast.podpost import Podpost, PodpostFactory def test_create_archive(): @@ -78,6 +78,6 @@ def test_get_archives(): count = 0 for post in a.get_podposts(): assert type(post) == str - assert type(Factory().get_podpost(post)) == Podpost + assert type(PodpostFactory().get_podpost(post)) == Podpost count += 1 assert count == 2 diff --git a/test/test_factory.py b/test/test_factory.py index 6d8530a..49238a5 100644 --- a/test/test_factory.py +++ b/test/test_factory.py @@ -3,73 +3,18 @@ Factory test """ import sys + sys.path.append("../python") from podcast.factory import Factory -from podcast.queue import Queue -import feedparser def test_singleton(): """ Factory is a Singleton. Let's test, if it really is... """ - f1 = Factory() # first instance: set storedir + f1 = Factory() # first instance: set storedir f2 = Factory() assert f1 == f2 - -def test_getfeed(): - """ - get a feed - """ - - f = Factory() - feed1 = f.get_feed("https://freakshow.fm/feed/mp3") - feed2 = f.get_feed("https://freakshow.fm/feed/mp3") - feed3 = f.get_feed("https://cre.fm/feed/opus") - - assert feed1 == feed2 - assert type(feed1) == feedparser.FeedParserDict - -def test_getfeed2(): - """ - get a feed (should work offline) - """ - - f = Factory() - feed1 = f.get_feed("https://freakshow.fm/feed/mp3") - feed2 = f.get_feed("https://freakshow.fm/feed/mp3") - feed3 = f.get_feed("https://cre.fm/feed/opus") - - assert feed1 == feed2 - assert feed1 != feed3 - - assert type(feed1) == feedparser.FeedParserDict - assert type(feed3) == feedparser.FeedParserDict - -def test_feed_persist(): - """ - Test feed persisting - """ - - f = Factory() - f.safe_feed("https://freakshow.fm/feed/mp3") - f.safe_feed("https://cre.fm/feed/opus") - -def test_feed_unpersist(): - """ - test feed removing - """ - - f = Factory() - f.delete_feed("https://cre.fm/feed/opus") - -def test_feed_unpersist2(): - """ - delete other feed too - """ - - f = Factory() - f.delete_feed("https://freakshow.fm/feed/mp3") diff --git a/test/test_favorites.py b/test/test_favorites.py index c41e8fc..9ad400d 100644 --- a/test/test_favorites.py +++ b/test/test_favorites.py @@ -2,7 +2,8 @@ from httpretty import HTTPretty, httprettified from podcast.archive import ArchiveFactory from podcast.factory import Factory -from . import cleanup_archive +from podcast.podpost import PodpostFactory +from . import cleanup_archive, cleanup_queue from test.test_podcast import read_testdata, xml_headers @@ -17,10 +18,11 @@ def test_queue_favorites(): from podcast import favorite from podcast.podcast import PodcastFactory cleanup_archive() + cleanup_queue() url = 'https://freakshow.fm/feed/opus/' podcast = PodcastFactory().get_podcast(url) - fav_posts = [Factory().get_podpost(podcast.entry_ids_old_to_new[i]) for i in range(0, 10, 2)] + fav_posts = [PodpostFactory().get_podpost(podcast.entry_ids_old_to_new[i]) for i in range(0, 10, 2)] assert len(fav_posts) == 5 archive = ArchiveFactory().get_archive() assert len(list(archive.get_podposts())) == 0 diff --git a/test/test_migration_to_v1.py b/test/test_migration_to_v1.py index c8fc258..2c15b6c 100644 --- a/test/test_migration_to_v1.py +++ b/test/test_migration_to_v1.py @@ -10,6 +10,7 @@ from podcast.factory import Factory from podcast.inbox import InboxFactory from podcast.podcast import Podcast, PodcastFactory from podcast.podcastlist import PodcastListFactory +from podcast.podpost import PodpostFactory from podcast.queue import QueueFactory sys.path.append("../python") @@ -27,7 +28,7 @@ def test_migration(): podcast: Podcast = PodcastFactory().get_podcast(podcasts[0]) assert podcast != None assert len(podcast.entry_ids_old_to_new) == 22 - for post in (Factory().get_podpost(post) for post in podcast.entry_ids_old_to_new for podcast in + for post in (PodpostFactory().get_podpost(post) for post in podcast.entry_ids_old_to_new for podcast in map(PodcastFactory().get_podcast, podcasts)): assert post.insert_date is not None assert len(list(podcast.get_entries())) == 22 diff --git a/test/test_queue.py b/test/test_queue.py index 7043775..919aa37 100644 --- a/test/test_queue.py +++ b/test/test_queue.py @@ -15,7 +15,7 @@ from podcast.queue import QueueFactory from podcast.factory import Factory from podcast.queue import Queue from podcast.podcast import Podcast, PodcastFactory -from podcast.podpost import Podpost +from podcast.podpost import Podpost, PodpostFactory def test_create_queue(): @@ -79,7 +79,7 @@ def test_get_podposts(): assert len(postids) == 2 for post in postids: assert type(post) == str - assert type(Factory().get_podpost(post)) == Podpost + assert type(PodpostFactory().get_podpost(post)) == Podpost assert post in [entry, entry2] @@ -92,7 +92,7 @@ def test_queue_favorites(): queue = QueueFactory().get_queue() url = 'https://freakshow.fm/feed/opus/' podcast = PodcastFactory().get_podcast(url) - for post in (Factory().get_podpost(podcast.entry_ids_old_to_new[i]) for i in range(0, 10, 2)): + for post in (PodpostFactory().get_podpost(podcast.entry_ids_old_to_new[i]) for i in range(0, 10, 2)): post.favorite = True post.save() for id in podcast.entry_ids_old_to_new: -- GitLab From e4073acdf54a9b574102a9abb8e3cd9e29bb98e4 Mon Sep 17 00:00:00 2001 From: Thilo Kogge Date: Wed, 3 Feb 2021 17:02:33 +0100 Subject: [PATCH 08/18] moved callback from qt with paths to constants --- python/podcast/constants.py | 61 +++++++++++++++++++++++++++++++ python/podcast/factory.py | 46 +---------------------- python/podcast/podpost.py | 3 +- qml/components/ExternalHandler.py | 10 +++-- qml/components/FeedParser.py | 4 +- 5 files changed, 74 insertions(+), 50 deletions(-) create mode 100644 python/podcast/constants.py diff --git a/python/podcast/constants.py b/python/podcast/constants.py new file mode 100644 index 0000000..8a2c90b --- /dev/null +++ b/python/podcast/constants.py @@ -0,0 +1,61 @@ +import os +import pyotherside +from typing import Optional + +from podcast import util +from podcast.singleton import Singleton + + +class Constants(metaclass=Singleton): + """ + manages constants supplied by qml like süecific paths etc + """ + favorites_home: Optional[str] + media_home: Optional[str] + external_home: Optional[str] + + object: Optional[object] + + def __init__(self): + self.object = None + self.media_home = None + self.favorites_home = None + self.external_home = None + + + def init_from_qml(self, object): + """ + Get QML object + """ + if self.object: + return + self.object = object + home = os.path.expanduser("~") + self.media_home = os.path.join(self.get_val("musicHomeVal"), "podqast") + self.favorites_home = os.path.join(self.media_home, "favorites") + self.external_home = os.path.join(self.media_home, "external") + + xdg_media_home = os.path.join( + "XDG_DATA_DIRS", os.path.join(home, "podqast") + ) + if os.path.exists(xdg_media_home): + os.rename(xdg_media_home, self.media_home) + + if self.get_val("allowExtVal") or self.get_val("allowFavVal"): + util.make_directory(self.media_home) + util.make_directory(self.favorites_home) + util.make_directory(self.external_home) + epath = os.path.join(self.external_home, ".nomedia") + open(epath, "a").close() + + pyotherside.send("objectLoaded") + + def get_val(self, valname): + """ + Return property value by valname + """ + + try: + return getattr(self.object, valname) + except: + return None \ No newline at end of file diff --git a/python/podcast/factory.py b/python/podcast/factory.py index 450c440..c320c95 100644 --- a/python/podcast/factory.py +++ b/python/podcast/factory.py @@ -7,16 +7,15 @@ Factory is a Singleton import sys import os +from typing import Optional sys.path.append("../") from podcast.singleton import Singleton -from podcast.cache import Cache from podcast.store import Store from podcast import util -import pyotherside class BaseFactory(metaclass=Singleton): data_home: str @@ -68,51 +67,10 @@ class Factory(BaseFactory): """ Factory for """ + def __init__(self): super().__init__() - def init_from_qml(self, object): - """ - Get QML object - """ - - try: - if self.object: - return - except: - pass - - self.object = object - home = os.path.expanduser("~") - self.media_home = os.path.join(self.get_val("musicHomeVal"), "podqast") - self.favorites_home = os.path.join(self.media_home, "favorites") - self.external_home = os.path.join(self.media_home, "external") - - xdg_media_home = os.path.join( - "XDG_DATA_DIRS", os.path.join(home, "podqast") - ) - if os.path.exists(xdg_media_home): - os.rename(xdg_media_home, self.media_home) - - if self.get_val("allowExtVal") or self.get_val("allowFavVal"): - util.make_directory(self.media_home) - util.make_directory(self.favorites_home) - util.make_directory(self.external_home) - epath = os.path.join(self.external_home, ".nomedia") - open(epath, "a").close() - - pyotherside.send("objectLoaded") - - def get_val(self, valname): - """ - Return property value by valname - """ - - try: - return getattr(self.object, valname) - except: - return None - def get_store(self): """ get the store diff --git a/python/podcast/podpost.py b/python/podcast/podpost.py index b012c3b..2d8698b 100644 --- a/python/podcast/podpost.py +++ b/python/podcast/podpost.py @@ -5,6 +5,7 @@ import datetime import sys from podcast.cache import Cache +from podcast.constants import Constants sys.path.append("../") @@ -513,7 +514,7 @@ class Podpost: ) tnb = re.sub(r"(?u)[^-\w.]", "", tn) + "." + ext tnb = "".join(x for x in tnb if x.isalnum() or x in "._-()") - topath = os.path.join(Factory().favorites_home, tnb) + topath = os.path.join(Constants().favorites_home, tnb) pyotherside.send("topath: " + topath) return topath diff --git a/qml/components/ExternalHandler.py b/qml/components/ExternalHandler.py index 02406a3..b2be940 100644 --- a/qml/components/ExternalHandler.py +++ b/qml/components/ExternalHandler.py @@ -7,6 +7,8 @@ import sys import os import hashlib +from podcast.constants import Constants + sys.path.append("/usr/share/harbour-podqast/python") from podcast.external import ExternalFactory @@ -42,7 +44,7 @@ def check_new(): """ pyotherside.send("Check new/lost audio files") - external = Factory().external_home + external = Constants().external_home if not os.path.isdir(external): pyotherside.send("Warning: external directory does not exist!") return @@ -66,7 +68,7 @@ def check_new(): post.save() exlib.insert(post.id) exlib.save() - wheremove = Factory().get_val("extMoveToVal") + wheremove = Constants().get_val("extMoveToVal") movePost(post.id, wheremove) pyotherside.send( "updatesNotification", @@ -90,7 +92,7 @@ def wait_new(): check via inotify for new files in ~/podqast/external """ - external = Factory().external_home + external = Constants().external_home if not os.path.isdir(external): return @@ -123,7 +125,7 @@ def wait_new(): for event in i.event_gen(timeout_s=10, yield_nones=False): (_, type_names, path, filename) = event - wheremove = Factory().get_val("extMoveToVal") + wheremove = Constants().get_val("extMoveToVal") if "IN_CREATE" in type_names: filepath = os.path.join(external, filename) if os.path.islink(filepath): diff --git a/qml/components/FeedParser.py b/qml/components/FeedParser.py index 39984ae..87626a2 100644 --- a/qml/components/FeedParser.py +++ b/qml/components/FeedParser.py @@ -9,6 +9,8 @@ import os import time import tarfile +from podcast.constants import Constants + sys.path.append("/usr/share/harbour-podqast/python") from podcast.podcast import PodcastFactory @@ -49,7 +51,7 @@ def get_latest_entry(url): def init_from_qml(object): - Factory().init_from_qml(object) + Constants().init_from_qml(object) def get_entries(url): -- GitLab From 5ffa76afad89617d71f4eef6ee62dbb6c05b1483 Mon Sep 17 00:00:00 2001 From: Thilo Kogge Date: Wed, 3 Feb 2021 17:34:31 +0100 Subject: [PATCH 09/18] moved podpost and podcast save methods to persist methods on their factories. --- python/podcast/data_migration.py | 6 ++--- python/podcast/external.py | 4 +++- python/podcast/inbox.py | 3 ++- python/podcast/podcast.py | 32 ++++++++++---------------- python/podcast/podcastlist.py | 2 +- python/podcast/podpost.py | 38 ++++++++++++------------------- python/podcast/queue.py | 10 ++++---- qml/components/ExternalHandler.py | 4 ++-- test/test_archive.py | 4 ++-- test/test_favorites.py | 2 +- test/test_podpost.py | 5 ++-- test/test_queue.py | 6 ++--- 12 files changed, 52 insertions(+), 64 deletions(-) diff --git a/python/podcast/data_migration.py b/python/podcast/data_migration.py index 8239696..a9fcb12 100644 --- a/python/podcast/data_migration.py +++ b/python/podcast/data_migration.py @@ -100,7 +100,7 @@ def migrate_podpost_v0_v1(self: Podpost): self.length = 0 if hasattr(self, "entry") and not self.isaudio: self.init(self.entry, self.logo_url, self.podurl) - self.save() + PodpostFactory().persist(self) def migrate_podcast_v0_v1(self: Podcast): @@ -121,7 +121,7 @@ def migrate_podcast_v0_v1(self: Podcast): p2 = PodpostFactory().get_podpost(post.id) if p2: post = p2 - post.save() + PodpostFactory().persist(post) entry_ids_new_to_old.append(post.id) self._set_alt_feeds(self.feed) else: @@ -133,7 +133,7 @@ def migrate_podcast_v0_v1(self: Podcast): self.feed = None if old_entry_count == len(entry_ids_new_to_old): logger.info("migration done") - self.save() + PodcastFactory().persist(self) else: logger.error("Could not complete migration. old count: %d, new: %d", old_entry_count, len(entry_ids_new_to_old)) diff --git a/python/podcast/external.py b/python/podcast/external.py index d2f3ccd..dd51c6b 100644 --- a/python/podcast/external.py +++ b/python/podcast/external.py @@ -6,6 +6,8 @@ import sys import time import pyotherside +from podcast.podpost import PodpostFactory + sys.path.append("../") from podcast.singleton import Singleton @@ -44,7 +46,7 @@ class External: post = Factory().get_podpost(podpost) if post.insert_date == None: post.insert_date = time.time() - post.save() + PodpostFactory().persist(post) self.save() pyotherside.send("got %d entries" % len(self.podposts)) diff --git a/python/podcast/inbox.py b/python/podcast/inbox.py index aad9af7..1e62457 100644 --- a/python/podcast/inbox.py +++ b/python/podcast/inbox.py @@ -49,7 +49,7 @@ class Inbox: post = PodpostFactory().get_podpost(podpost) if post.insert_date == None: post.insert_date = time.time() - post.save() + PodpostFactory().persist(post) pyotherside.send("Inbox element saved %d" % len(self.podposts)) self.save() @@ -142,3 +142,4 @@ class InboxFactory(metaclass=Singleton): return self.inbox return self.inbox + diff --git a/python/podcast/podcast.py b/python/podcast/podcast.py index 1c495a1..1bdace0 100644 --- a/python/podcast/podcast.py +++ b/python/podcast/podcast.py @@ -138,7 +138,7 @@ class Podcast: break all_episode_count += 1 post = Podpost(entry, self.url, image) - post.save() + PodpostFactory().persist(post) new_posts.append(post) # todo: limit stuff probably broken if limit != 0 and all_episode_count >= limit: @@ -148,7 +148,7 @@ class Podcast: new_post_ids = [post.id for post in new_posts] new_post_ids.reverse() self.entry_ids_old_to_new.extend(new_post_ids) - self.save() + PodcastFactory().persist(self) return new_posts def __set_data_from_feed(self, feed): @@ -253,22 +253,6 @@ class Podcast: else: return None - def save(self): - """ - Save this element - """ - self.download_icon() - PodcastFactory().store.store(self.url, self) - PodcastFactory().podcastcache.store(self.url, self) - - def delete(self): - """ - Remove Item from store - """ - - PodcastFactory().store.delete(self.url) - PodcastFactory().podcastcache.delete(self.url) - def download_icon(self): """ Download icon @@ -317,7 +301,7 @@ class Podcast: pyotherside.send("refreshPost", None) return [] - self.save() + PodcastFactory().persist(self) move = self.move if move < 0: @@ -354,7 +338,7 @@ class Podcast: self.move = params["move"] self.autolimit = params["autolimit"] self.playrate = params["playrate"] - self.save() + PodcastFactory().persist(self) def get_params(self): """ @@ -427,3 +411,11 @@ class PodcastFactory(BaseFactory): PodpostFactory().delete_podpost(post) self.podcastcache.delete(url) self.store.delete(url) + + def persist(self, podcast:Podcast): + self.store.store(podcast.url,podcast) + self.podcastcache.store(podcast.url,podcast) + + def delete(self, podcast:Podcast): + self.store.delete(podcast.url,podcast) + self.podcastcache.delete(podcast.url,podcast) diff --git a/python/podcast/podcastlist.py b/python/podcast/podcastlist.py index 5fe15d6..b98b5c6 100644 --- a/python/podcast/podcastlist.py +++ b/python/podcast/podcastlist.py @@ -45,7 +45,7 @@ class PodcastList: if podcast: if podcast.title: podcast.subscribed = True - podcast.save() + PodcastFactory().persist(podcast) if url not in self.podcasts: self.podcasts.append(url) self.save() diff --git a/python/podcast/podpost.py b/python/podcast/podpost.py index 2d8698b..30e390c 100644 --- a/python/podcast/podpost.py +++ b/python/podcast/podpost.py @@ -225,7 +225,7 @@ class Podpost: queue = QueueFactory().get_queue() if position: self.position = position - self.save() + PodpostFactory().persist(self) if self.duration: if self.position > 0: add = "" @@ -304,16 +304,6 @@ class Podpost: "haschapters": haschapters, } - def save(self): - """ - Save this element. If self.insert_date = None: set date - """ - - logger.info("storing podpost %s", self.id) - if not self.logo_path: - self.download_icon() - PodpostFactory().persist(self.id, self) - def download_audio(self): """ Download the audio stuff @@ -359,7 +349,7 @@ class Podpost: self.percentage = 0 self.file_path = file_path - self.save() + PodpostFactory().persist(self) def download_icon(self): """ @@ -417,7 +407,7 @@ class Podpost: """ self.position = position - self.save() + PodpostFactory().persist(self) @property def get_position(self): @@ -434,7 +424,7 @@ class Podpost: if not self.duration: self.duration = duration - self.save() + PodpostFactory().persist(self) def pause(self, position): """ @@ -444,7 +434,7 @@ class Podpost: if position >= 0: self.position = position self.state = PAUSE - self.save() + PodpostFactory().persist(self) def stop(self, position): """ @@ -454,7 +444,7 @@ class Podpost: if position >= 0: self.position = position self.state = STOP - self.save() + PodpostFactory().persist(self) @property def get_play_state(self): @@ -490,7 +480,7 @@ class Podpost: delete_file(self.file_path) self.percentage = 0 self.file_path = None - self.save() + PodpostFactory().persist(self) def toggle_fav(self, do_download=False): """ @@ -533,7 +523,7 @@ class Podpost: if self.id not in queue.podposts: self.delete_file() - self.save() + PodpostFactory().persist(self) return self.favorite def get_chapters(self): @@ -546,7 +536,7 @@ class Podpost: logger.info("setting chapter selection flags") for chapter in self.chapters: chapter["selected"] = True - self.save() + PodpostFactory().persist(self) return self.chapters @@ -557,7 +547,7 @@ class Podpost: """ self.chapters[chapterid]["selected"] = not self.chapters[chapterid]["selected"] - self.save() + PodpostFactory().persist(self) @property def get_fav(self): @@ -598,6 +588,8 @@ class PodpostFactory(BaseFactory): self.podpostcache.delete(index) self.store.delete(index) - def persist(self, id, self1): - self.store.store(id,self1) - self.podpostcache.store(id,self1) + def persist(self, post:Podpost): + if type(post) != Podpost: + raise ValueError("Can only persist podposts") + self.store.store(post.id,post) + self.podpostcache.store(post.id,post) diff --git a/python/podcast/queue.py b/python/podcast/queue.py index 7454a09..9a1e580 100644 --- a/python/podcast/queue.py +++ b/python/podcast/queue.py @@ -93,7 +93,7 @@ class Queue: if post.insert_date == None: post.insert_date = time.time() - post.save() + PodpostFactory().persist(post) self.save() pyotherside.send("needDownload", podpost) pyotherside.send("posplay", post.get_position) @@ -122,7 +122,7 @@ class Queue: if post.insert_date == None: post.insert_date = time.time() - post.save() + PodpostFactory().persist(post) self.save() pyotherside.send("needDownload", podpost) @@ -148,7 +148,7 @@ class Queue: if post.insert_date == None: post.insert_date = time.time() - post.save() + PodpostFactory().persist(post) self.save() pyotherside.send("needDownload", podpost) @@ -161,13 +161,13 @@ class Queue: post = PodpostFactory().get_podpost(podpost) post.delete_file() post.state = 0 - post.save() + PodpostFactory().persist(post) if podpost == self.podposts[0] and len(self.podposts) > 1: post1 = PodpostFactory().get_podpost(self.podposts[1]) pyotherside.send("setpos", post1.position) self.podposts.remove(podpost) else: - PodpostFactory().get_podpost(podpost).save() + PodpostFactory().persist(PodpostFactory().get_podpost(podpost)) self.save() diff --git a/qml/components/ExternalHandler.py b/qml/components/ExternalHandler.py index b2be940..b40eee0 100644 --- a/qml/components/ExternalHandler.py +++ b/qml/components/ExternalHandler.py @@ -65,7 +65,7 @@ def check_new(): if fnh not in exlib.podposts: pyotherside.send("external: " + fn + " is music") post = Podpost.from_audio_file(fn) - post.save() + PodpostFactory().persist(post) exlib.insert(post.id) exlib.save() wheremove = Constants().get_val("extMoveToVal") @@ -107,7 +107,7 @@ def wait_new(): pyotherside.send(post.title) pyotherside.send(post.logo_url) pyotherside.send(post.id) - post.save() + PodpostFactory().persist(post) exlib = ExternalFactory().get_external() exlib.insert(post.id) exlib.save() diff --git a/test/test_archive.py b/test/test_archive.py index b74fc3a..a12fe5e 100644 --- a/test/test_archive.py +++ b/test/test_archive.py @@ -60,10 +60,10 @@ def setup_archive_with_2_posts(archive): body=read_testdata('testdata/freakshow.rss'), adding_headers=xml_headers) podcast = Podcast('https://freakshow.fm/feed/opus/') entry1 = podcast.get_entry(podcast.entry_ids_old_to_new[0]) - entry1.save() + PodpostFactory().persist(entry1) archive.insert(entry1.id) entry2 = podcast.get_entry(podcast.entry_ids_old_to_new[1]) - entry2.save() + PodpostFactory().persist(entry2) archive.insert(entry2.id) return entry1, entry2 diff --git a/test/test_favorites.py b/test/test_favorites.py index 9ad400d..c564923 100644 --- a/test/test_favorites.py +++ b/test/test_favorites.py @@ -28,7 +28,7 @@ def test_queue_favorites(): assert len(list(archive.get_podposts())) == 0 for post in fav_posts: post.favorite = True - post.save() + PodpostFactory().persist(post) archive.insert(post.id) archive.save() assert len(favorite.get_queue_favorites()) == 0 diff --git a/test/test_podpost.py b/test/test_podpost.py index b138568..791ecdb 100644 --- a/test/test_podpost.py +++ b/test/test_podpost.py @@ -13,7 +13,8 @@ sys.path.append("../python") from podcast.factory import Factory from podcast.podcast import Podcast -from podcast.podpost import Podpost +from podcast.podpost import Podpost, PodpostFactory + @httpretty.activate def test_podpost_save(): @@ -27,7 +28,7 @@ def test_podpost_save(): p = Podcast('https://freakshow.fm/feed/opus/') e = p.get_latest_entry() id1 = e.id - e.save() + PodpostFactory().persist(e) n = Factory().get_store().get(e.id) id2 = n.id assert type(n) == Podpost diff --git a/test/test_queue.py b/test/test_queue.py index 919aa37..e39d5fb 100644 --- a/test/test_queue.py +++ b/test/test_queue.py @@ -51,11 +51,11 @@ def test_insert_top(): e = p.get_entry(p.entry_ids_old_to_new[1]) e2 = p.get_entry(p.entry_ids_old_to_new[0]) - e.save() + PodpostFactory().persist(e) q.insert_top(e.id) assert q.podposts[0] == e.id - e2.save() + PodpostFactory().persist(e2) q.insert_top(e2.id) assert q.podposts[0] == e2.id @@ -94,7 +94,7 @@ def test_queue_favorites(): podcast = PodcastFactory().get_podcast(url) for post in (PodpostFactory().get_podpost(podcast.entry_ids_old_to_new[i]) for i in range(0, 10, 2)): post.favorite = True - post.save() + PodpostFactory().persist(post) for id in podcast.entry_ids_old_to_new: queue.insert_bottom(id) assert len(queue.podposts) == 20 -- GitLab From 926fbbec700579e08d47c9deaac7078cf516929f Mon Sep 17 00:00:00 2001 From: Thilo Kogge Date: Wed, 3 Feb 2021 17:36:42 +0100 Subject: [PATCH 10/18] removed useless print statement --- python/podcast/util.py | 1 - 1 file changed, 1 deletion(-) diff --git a/python/podcast/util.py b/python/podcast/util.py index 2bcaee1..8513b41 100644 --- a/python/podcast/util.py +++ b/python/podcast/util.py @@ -70,7 +70,6 @@ def s_to_hr(secs): hours = int(secs / 3600) hr = str(hours) + "h" secs = secs - hours * 3600 - print(secs) if secs >= 59: hr = hr + str(int(secs / 60)) + "m" -- GitLab From b646a30c282b8527c284200a60ea9576a77c650c Mon Sep 17 00:00:00 2001 From: Thilo Kogge Date: Wed, 3 Feb 2021 17:46:07 +0100 Subject: [PATCH 11/18] fixed linter errors --- python/podcast/data_migration.py | 4 ++-- python/podcast/factory.py | 1 - python/podcast/podcast.py | 4 +--- test/test_podpost.py | 2 +- 4 files changed, 4 insertions(+), 7 deletions(-) diff --git a/python/podcast/data_migration.py b/python/podcast/data_migration.py index a9fcb12..b103050 100644 --- a/python/podcast/data_migration.py +++ b/python/podcast/data_migration.py @@ -48,6 +48,7 @@ def run_migrations(): set_versionnumber(1) pyotherside.send("migrationDone") + def get_versionnumber() -> int: versionfilepath = get_versionfile_path() if os.path.isfile(versionfilepath): @@ -82,7 +83,7 @@ def migrate_archive_v0_v1(): post = PodpostFactory().get_podpost(postid) if not post: continue - if hasattr(post, "insert_date") and post.insert_date: + if hasattr(post, "insert_date") and post.insert_date: archive_entry.insert_date = post.insert_date else: archive_entry.insert_date = datetime.datetime.now() @@ -106,7 +107,6 @@ def migrate_podpost_v0_v1(self: Podpost): def migrate_podcast_v0_v1(self: Podcast): logger.info("migrating podcast '%s' to new format version 1", self.title) try: - from podcast.factory import Factory if self.logo_path: image = self.logo_path else: diff --git a/python/podcast/factory.py b/python/podcast/factory.py index c320c95..0f0053f 100644 --- a/python/podcast/factory.py +++ b/python/podcast/factory.py @@ -7,7 +7,6 @@ Factory is a Singleton import sys import os -from typing import Optional sys.path.append("../") diff --git a/python/podcast/podcast.py b/python/podcast/podcast.py index 1bdace0..c2314e0 100644 --- a/python/podcast/podcast.py +++ b/python/podcast/podcast.py @@ -7,11 +7,10 @@ from typing import List, Dict, Optional, Generator from feedparser import FeedParserDict -from podcast.factory import Factory, BaseFactory +from podcast.factory import BaseFactory sys.path.append("/usr/share/podqast/python") -from podcast.singleton import Singleton from podcast.cache import Cache from podcast.store import Store from podcast.podpost import Podpost, PodpostFactory @@ -238,7 +237,6 @@ class Podcast: """ Return a list of entries """ - from podcast.factory import Factory logger.debug("Podcast get_entries started.") for id in self.entry_ids_old_to_new: yield PodpostFactory().get_podpost(id).get_data() diff --git a/test/test_podpost.py b/test/test_podpost.py index 791ecdb..97ff043 100644 --- a/test/test_podpost.py +++ b/test/test_podpost.py @@ -29,7 +29,7 @@ def test_podpost_save(): e = p.get_latest_entry() id1 = e.id PodpostFactory().persist(e) - n = Factory().get_store().get(e.id) + n = PodpostFactory().get_podpost(e.id) id2 = n.id assert type(n) == Podpost assert id1 == id2 -- GitLab From a9908a994954741c097e640128efcaf67ef3bd69 Mon Sep 17 00:00:00 2001 From: Thilo Kogge Date: Wed, 3 Feb 2021 17:49:20 +0100 Subject: [PATCH 12/18] fixed linter errors --- qml/components/ArchiveHandler.py | 1 - qml/components/ExternalHandler.py | 1 - qml/components/InboxHandler.py | 1 - qml/components/QueueHandler.py | 1 - 4 files changed, 4 deletions(-) diff --git a/qml/components/ArchiveHandler.py b/qml/components/ArchiveHandler.py index c9301fa..54ffbd1 100644 --- a/qml/components/ArchiveHandler.py +++ b/qml/components/ArchiveHandler.py @@ -10,7 +10,6 @@ from podcast.podpost import PodpostFactory sys.path.append("/usr/share/harbour-podqast/python") from podcast.archive import ArchiveFactory -from podcast.factory import Factory import logging logger = logging.getLogger(__name__) diff --git a/qml/components/ExternalHandler.py b/qml/components/ExternalHandler.py index b40eee0..267e832 100644 --- a/qml/components/ExternalHandler.py +++ b/qml/components/ExternalHandler.py @@ -16,7 +16,6 @@ from podcast.inbox import InboxFactory from podcast.archive import ArchiveFactory from podcast.queue import QueueFactory -from podcast.factory import Factory from podcast.podpost import Podpost, PodpostFactory import inotify.adapters import mutagen diff --git a/qml/components/InboxHandler.py b/qml/components/InboxHandler.py index a315be6..26313aa 100644 --- a/qml/components/InboxHandler.py +++ b/qml/components/InboxHandler.py @@ -11,7 +11,6 @@ sys.path.append("/usr/share/harbour-podqast/python") from podcast.inbox import InboxFactory from podcast.queue import QueueFactory -from podcast.factory import Factory def get_inbox_posts(podurl=None): diff --git a/qml/components/QueueHandler.py b/qml/components/QueueHandler.py index 59e10e7..d224a6a 100644 --- a/qml/components/QueueHandler.py +++ b/qml/components/QueueHandler.py @@ -10,7 +10,6 @@ from podcast.podpost import PodpostFactory sys.path.append("/usr/share/harbour-podqast/python") from podcast.queue import QueueFactory -from podcast.factory import Factory from podcast.archive import ArchiveFactory -- GitLab From 940005900b15d0a54d6d02bfb4c2ea70c9227d83 Mon Sep 17 00:00:00 2001 From: Thilo Kogge Date: Wed, 3 Feb 2021 20:29:07 +0100 Subject: [PATCH 13/18] moved common queue data operations into queue factory --- python/podcast/queue.py | 12 +++++++++++- qml/components/QueueHandler.py | 15 ++++++--------- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/python/podcast/queue.py b/python/podcast/queue.py index 9a1e580..fd8cd9a 100644 --- a/python/podcast/queue.py +++ b/python/podcast/queue.py @@ -5,10 +5,12 @@ It instanciates Podposts via Factory if needed. import sys import os +from typing import Iterator + import pyotherside import time -from podcast.podpost import PodpostFactory +from podcast.podpost import PodpostFactory, Podpost sys.path.append("../") @@ -323,3 +325,11 @@ class QueueFactory(metaclass=Singleton): self.queue = Queue() return self.queue + + def get_podposts_objects(self)->Iterator[Podpost]: + for postid in self.get_queue().podposts: + yield PodpostFactory().get_podpost(postid) + + def get_first_podpost_object(self): + return next(self.get_podposts_objects(), None) + diff --git a/qml/components/QueueHandler.py b/qml/components/QueueHandler.py index d224a6a..0daf5fa 100644 --- a/qml/components/QueueHandler.py +++ b/qml/components/QueueHandler.py @@ -19,10 +19,8 @@ def get_queue_posts(moved=""): """ entries = [] - queue = QueueFactory().get_queue() - for post in queue.get_podposts(): - entry = PodpostFactory().get_podpost(post) + for entry in QueueFactory().get_podposts_objects(): edata = entry.get_data() edata["moved"] = edata["id"] == moved entries.append(edata) @@ -40,12 +38,11 @@ def get_first_entry(): Return the data of the first entry """ - queue = QueueFactory().get_queue() - if len(queue.podposts) < 1: - return None - pyotherside.send( - "firstentry", PodpostFactory().get_podpost(queue.podposts[0]).get_data() - ) + post = QueueFactory().get_first_podpost_object() + if post is not None: + pyotherside.send( + "firstentry", post.get_data() + ) def queue_insert_top(id, doplay=False): -- GitLab From 152a36f1cfcfc7452b75a6711f55c84fc11cdb38 Mon Sep 17 00:00:00 2001 From: Thilo Kogge Date: Wed, 3 Feb 2021 21:21:29 +0100 Subject: [PATCH 14/18] moved iteration and selection logic into the respective classes. --- python/podcast/archive.py | 6 ++++-- python/podcast/external.py | 4 ++++ python/podcast/favorite.py | 31 ++----------------------------- python/podcast/inbox.py | 9 ++++++++- python/podcast/podcast.py | 22 +++++++++++----------- python/podcast/podcastlist.py | 29 ++++++++++++++++------------- python/podcast/podpost.py | 6 +++--- python/podcast/queue.py | 22 +++++++++++++++++++++- qml/components/ArchiveHandler.py | 3 +-- qml/components/ExternalHandler.py | 10 ++++------ qml/components/FeedParser.py | 9 +++------ qml/components/InboxHandler.py | 14 ++++---------- qml/components/QueueHandler.py | 8 ++++---- test/test_archive.py | 17 ++++++++++++++++- test/test_queue.py | 2 ++ 15 files changed, 103 insertions(+), 89 deletions(-) diff --git a/python/podcast/archive.py b/python/podcast/archive.py index 6cb950c..e432a41 100644 --- a/python/podcast/archive.py +++ b/python/podcast/archive.py @@ -70,11 +70,13 @@ class Archive: for archive_entry in sorted(self._archive_entries, key=lambda e: e.insert_date, reverse=True): yield archive_entry.podpost - def get_podpost_objects(self, url_filter=None) -> Iterator[Podpost]: + def get_podpost_objects(self, url_filter=None, filter_favorite=False) -> Iterator[Podpost]: for id in self.get_podposts(): entry: Podpost = PodpostFactory().get_podpost(id) if entry: - if not url_filter or (url_filter and entry.podurl == url_filter): + if ((not url_filter or (url_filter and entry.podurl == url_filter)) and + (not filter_favorite or entry.favorite) + ): yield entry def remove_podpost(self, podpost): diff --git a/python/podcast/external.py b/python/podcast/external.py index dd51c6b..1557c75 100644 --- a/python/podcast/external.py +++ b/python/podcast/external.py @@ -60,6 +60,10 @@ class External: for podpost in self.podposts: yield podpost + def get_podposts_objects(self): + for post_id in self.get_podposts(): + yield PodpostFactory().get_podpost(post_id) + def remove_podpost(self, podpost): """ Remove a podpost from archive diff --git a/python/podcast/favorite.py b/python/podcast/favorite.py index db481cd..6b66df2 100644 --- a/python/podcast/favorite.py +++ b/python/podcast/favorite.py @@ -1,6 +1,3 @@ -from podcast.podpost import PodpostFactory - - def get_queue_favorites(podurl=None): """ Return a list of all favorites posts from queue @@ -8,19 +5,8 @@ def get_queue_favorites(podurl=None): from podcast.queue import QueueFactory - posts = [] + return list(QueueFactory().get_favorites(podurl)) - queue = QueueFactory().get_queue() - for post in queue.get_podposts(): - p = PodpostFactory().get_podpost(post) - if p.favorite: - if podurl: - if p.podurl == podurl: - posts.append(p) - else: - posts.append(p) - - return posts def append_archive_favorites(posts, podurl=None): @@ -31,21 +17,8 @@ def append_archive_favorites(posts, podurl=None): from podcast.archive import ArchiveFactory archive = ArchiveFactory().get_archive() + return list(archive.get_podpost_objects(url_filter=podurl,filter_favorite=True)) - for post in archive.get_podposts(): - p = PodpostFactory().get_podpost(post) - try: - if p.favorite: - if p not in posts: - if podurl: - if p.podurl == podurl: - posts.append(p) - else: - posts.append(p) - except: - pass - - return posts def get_favorites(podurl=None): diff --git a/python/podcast/inbox.py b/python/podcast/inbox.py index 1e62457..30f8cf0 100644 --- a/python/podcast/inbox.py +++ b/python/podcast/inbox.py @@ -5,7 +5,7 @@ The inbox queue. This also uses a Factory for instancing import sys import time -from podcast.podpost import PodpostFactory +from podcast.podpost import PodpostFactory, Podpost sys.path.append("../") @@ -61,6 +61,13 @@ class Inbox: for podpost in self.podposts: yield podpost + def get_podposts_objects(self, podurl = None): + for post_id in self.get_podposts(): + post:Podpost = PodpostFactory().get_podpost(post_id) + if not podurl or (podurl and post.podurl == podurl): + yield post + + def remove(self, podpost): """ Remove a podpost from archive diff --git a/python/podcast/podcast.py b/python/podcast/podcast.py index c2314e0..0d1a832 100644 --- a/python/podcast/podcast.py +++ b/python/podcast/podcast.py @@ -3,7 +3,7 @@ Podcast """ import sys -from typing import List, Dict, Optional, Generator +from typing import List, Dict, Optional, Generator, Iterator from feedparser import FeedParserDict @@ -111,10 +111,7 @@ class Podcast: logger.debug("fetching episodes for %s", self.title) - if self.logo_path: - image = self.logo_path - else: - image = self.logo_url + image = self.logo() old_first_id = self.entry_ids_old_to_new[-1] if len(self.entry_ids_old_to_new) > 0 else "" @@ -150,6 +147,13 @@ class Podcast: PodcastFactory().persist(self) return new_posts + def logo(self): + if self.logo_path: + image = self.logo_path + else: + image = self.logo_url + return image + def __set_data_from_feed(self, feed): logger.debug("setting instance values from feed") self.__set_times_from_feeds(feed) @@ -196,17 +200,13 @@ class Podcast: description = description[:160] + "..." self.download_icon() - if self.logo_path: - image = self.logo_path - else: - image = self.logo_url return { "url": self.url, "title": title, "description": description, "descriptionfull": descriptionfull, - "logo_url": image, + "logo_url": self.logo(), } def _set_alt_feeds(self, feed): @@ -233,7 +233,7 @@ class Podcast: entry = PodpostFactory().get_podpost(id) return entry - def get_entries(self) -> Generator[dict, None, None]: + def get_entries(self) -> Iterator[Podpost]: """ Return a list of entries """ diff --git a/python/podcast/podcastlist.py b/python/podcast/podcastlist.py index b98b5c6..3083752 100644 --- a/python/podcast/podcastlist.py +++ b/python/podcast/podcastlist.py @@ -3,13 +3,13 @@ List of subscribed podcasts """ import sys -from typing import List +from typing import List, Iterator sys.path.append("../") from podcast.singleton import Singleton from podcast.factory import Factory -from podcast.podcast import PodcastFactory +from podcast.podcast import PodcastFactory, Podcast from podcast import util from podcast import gpodder_import import pyotherside @@ -63,15 +63,19 @@ class PodcastList: pc.delete() self.save() - def get_podcasts(self) -> str: + def get_podcasts(self) -> Iterator[str]: """ return the list of subscribed podcasts """ - for podcast in self.podcasts: - pc = PodcastFactory().get_podcast(podcast) + for podcast in self.get_podcasts_objects(): + yield podcast.url + + def get_podcasts_objects(self, subscribed_only=True) -> Iterator[Podcast]: + for podcast_url in self.podcasts: + pc = PodcastFactory().get_podcast(podcast_url) if pc: - if hasattr(pc, "subscribed") and pc.subscribed: - yield podcast + if not subscribed_only or hasattr(pc, "subscribed") and pc.subscribed: + yield pc def refresh(self, moveto, limit=0): """ @@ -80,16 +84,15 @@ class PodcastList: pllen = len(self.podcasts) count = 0 - for podcast in self.podcasts: - pc = PodcastFactory().get_podcast(podcast) - if pc: + for podcast in self.get_podcasts_objects(subscribed_only=False): + if podcast: try: - if pc.subscribed: - for post in pc.refresh(moveto, limit): + if podcast.subscribed: + for post in podcast.refresh(moveto, limit): yield post except: logger.exception( - "podcast refresh failed for %s." % pc.title + "podcast refresh failed for %s." % podcast.title ) count += 1 diff --git a/python/podcast/podpost.py b/python/podcast/podpost.py index 30e390c..42be1cb 100644 --- a/python/podcast/podpost.py +++ b/python/podcast/podpost.py @@ -229,7 +229,7 @@ class Podpost: if self.duration: if self.position > 0: add = "" - if self.state == PLAY and self.id == queue.podposts[0]: + if self.state == PLAY and self.id == queue.get_top_id(): add = " playing" else: add = " remaining" @@ -250,7 +250,7 @@ class Podpost: from podcast.queue import QueueFactory queue = QueueFactory().get_queue() - inqueue = self.id in queue.podposts + inqueue = self.id in queue.get_podposts() timestring = self.get_hr_time() @@ -520,7 +520,7 @@ class Podpost: if self.file_path: queue = QueueFactory().get_queue() delete_file(get_topath()) - if self.id not in queue.podposts: + if self.id not in queue.get_podposts_ids(): self.delete_file() PodpostFactory().persist(self) diff --git a/python/podcast/queue.py b/python/podcast/queue.py index fd8cd9a..3a35beb 100644 --- a/python/podcast/queue.py +++ b/python/podcast/queue.py @@ -258,7 +258,17 @@ class Queue: """ for podpost in self.podposts: - yield (podpost) + yield podpost + + def get_top_id(self): + return self._get_top().id + + def get_podposts_ids(self): + for p in self.get_podposts(): + yield p.id + + def count(self): + return len(self.podposts) def play(self): """ @@ -333,3 +343,13 @@ class QueueFactory(metaclass=Singleton): def get_first_podpost_object(self): return next(self.get_podposts_objects(), None) + def get_favorites(self, podurl)->Iterator[Podpost]: + for post in self.get_podposts_objects(): + if post.favorite: + if podurl: + if post.podurl == podurl: + yield post + else: + yield post + + diff --git a/qml/components/ArchiveHandler.py b/qml/components/ArchiveHandler.py index 54ffbd1..fba7763 100644 --- a/qml/components/ArchiveHandler.py +++ b/qml/components/ArchiveHandler.py @@ -32,8 +32,7 @@ def get_archive_pod_data(): archive = ArchiveFactory().get_archive() - for post in archive.get_podposts(): - entry = PodpostFactory().get_podpost(post) + for entry in archive.get_podpost_objects(): if entry.podurl in entries: entries[entry.podurl]["count"] += 1 else: diff --git a/qml/components/ExternalHandler.py b/qml/components/ExternalHandler.py index 267e832..ade7117 100644 --- a/qml/components/ExternalHandler.py +++ b/qml/components/ExternalHandler.py @@ -61,7 +61,7 @@ def check_new(): os.path.join(external, f).encode() ).hexdigest() mfiles.append(fnh) - if fnh not in exlib.podposts: + if fnh not in exlib.get_podposts(): pyotherside.send("external: " + fn + " is music") post = Podpost.from_audio_file(fn) PodpostFactory().persist(post) @@ -79,7 +79,7 @@ def check_new(): except: pyotherside.send("File does not exist") - for f in exlib.podposts: + for f in exlib.get_podposts(): pyotherside.send("we are on file %s" % f) if f not in mfiles: exlib.remove_podpost(f) @@ -169,9 +169,8 @@ def get_external_posts(): entries = [] external = ExternalFactory().get_external() - for post in external.get_podposts(): + for entry in external.get_podposts_objects(): pyotherside.send("podposts loop") - entry = PodpostFactory().get_podpost(post) entries.append(entry.get_data()) pyotherside.send("after py external") # entries.sort(key = lambda r: r["edate"], reverse=True) @@ -186,8 +185,7 @@ def get_external_pod_data(): external = ExternalFactory().get_external() - for post in external.get_podposts(): - entry = PodpostFactory().get_podpost(post) + for entry in external.get_podposts_objects(): if entry.podurl in entries: entries[entry.podurl]["count"] += 1 else: diff --git a/qml/components/FeedParser.py b/qml/components/FeedParser.py index 87626a2..21ac64a 100644 --- a/qml/components/FeedParser.py +++ b/qml/components/FeedParser.py @@ -87,8 +87,7 @@ def get_podcasts(): podcasts = [] pclist = PodcastListFactory().get_podcast_list() - for podcast in pclist.get_podcasts(): - pc = PodcastFactory().get_podcast(podcast) + for pc in pclist.get_podcasts_objects(): podcasts.append(pc.feedinfo()) pyotherside.send("podcastslist", podcasts) @@ -101,8 +100,7 @@ def get_podcastscache(): pclist = PodcastListFactory().get_podcast_list() - for podcast in pclist.get_podcasts(): - PodcastFactory().get_podcast(podcast) + list(pclist.get_podcasts_objects()) pyotherside.send("podcast cache ready") @@ -284,8 +282,7 @@ def write_opml(): podcasts = [] pclist = PodcastListFactory().get_podcast_list() - for podcast in pclist.get_podcasts(): - pc = PodcastFactory().get_podcast(podcast) + for pc in pclist.get_podcasts_objects(): podcasts.append( {"name": pc.title, "xml_url": pc.url, "html_url": pc.link} ) diff --git a/qml/components/InboxHandler.py b/qml/components/InboxHandler.py index 26313aa..aa527b5 100644 --- a/qml/components/InboxHandler.py +++ b/qml/components/InboxHandler.py @@ -21,15 +21,10 @@ def get_inbox_posts(podurl=None): entries = [] inbox = InboxFactory().get_inbox() - pyotherside.send("len Inbox %d" % len(inbox.podposts)) + pyotherside.send("len Inbox %d" % len(inbox.get_podposts())) - for post in inbox.get_podposts(): - entry = PodpostFactory().get_podpost(post) - if podurl: - if entry.podurl == podurl: - entries.append(entry.get_data()) - else: - entries.append(entry.get_data()) + for entry in inbox.get_podposts_objects(podurl=podurl): + entries.append(entry.get_data()) entries.sort(key=lambda r: r["date"], reverse=True) pyotherside.send("createInboxList", entries) @@ -43,8 +38,7 @@ def get_inbox_pod_data(): inbox = InboxFactory().get_inbox() - for post in inbox.get_podposts(): - entry = PodpostFactory().get_podpost(post) + for entry in inbox.get_podposts_objects(): if entry.podurl in entries: entries[entry.podurl]["count"] += 1 else: diff --git a/qml/components/QueueHandler.py b/qml/components/QueueHandler.py index 0daf5fa..4297745 100644 --- a/qml/components/QueueHandler.py +++ b/qml/components/QueueHandler.py @@ -148,7 +148,7 @@ def queue_to_archive(id): queue = QueueFactory().get_queue() archive = ArchiveFactory().get_archive() - if queue.podposts[0] == id: + if queue.get_top_id() == id: pyotherside.send("stopping") queue.remove(id) archive.insert(id) @@ -162,7 +162,7 @@ def queue_top_to_archive(): """ queue = QueueFactory().get_queue() - queue_to_archive(queue.podposts[0]) + queue_to_archive(queue.get_top_id()) queue_play() @@ -191,9 +191,9 @@ def queue_hr_time(position): """ queue = QueueFactory().get_queue() - if len(queue.podposts) > 0: + if queue.count() > 0: pyotherside.send( - "hrtime", queue.podposts[0], queue.get_hr_time(position) + "hrtime", queue.get_top_id(), queue.get_hr_time(position) ) diff --git a/test/test_archive.py b/test/test_archive.py index a12fe5e..0586057 100644 --- a/test/test_archive.py +++ b/test/test_archive.py @@ -12,7 +12,6 @@ from test.test_podcast import read_testdata, xml_headers sys.path.append("../python") from podcast.archive import ArchiveFactory, Archive -from podcast.factory import Factory from podcast.podcast import Podcast from podcast.podpost import Podpost, PodpostFactory @@ -80,4 +79,20 @@ def test_get_archives(): assert type(post) == str assert type(PodpostFactory().get_podpost(post)) == Podpost count += 1 + posts = list(a.get_podpost_objects()) + assert len(posts) == 2 + for post in posts: + assert type(post) == Podpost + assert len(list(a.get_podpost_objects(filter_favorite=True))) == 0 + assert len(list(a.get_podpost_objects(url_filter="http://i.am.wrong"))) == 0 + assert len(list(a.get_podpost_objects(url_filter="http://i.am.wrong",filter_favorite=True))) == 0 + assert len(list(a.get_podpost_objects(url_filter="https://freakshow.fm/feed/opus/"))) == 2 + assert len(list(a.get_podpost_objects(url_filter="https://freakshow.fm/feed/opus/",filter_favorite=True))) == 0 + for post in a.get_podpost_objects(): + post.favorite = True + PodpostFactory().persist(post) + assert len(list(a.get_podpost_objects(url_filter="https://freakshow.fm/feed/opus/",filter_favorite=True))) == 2 + assert len(list(a.get_podpost_objects(filter_favorite=True))) == 2 + assert len(list(a.get_podpost_objects(url_filter="http://i.am.wrong",filter_favorite=True))) == 0 + assert count == 2 diff --git a/test/test_queue.py b/test/test_queue.py index e39d5fb..8cc3a87 100644 --- a/test/test_queue.py +++ b/test/test_queue.py @@ -7,6 +7,7 @@ import sys import httpretty from httpretty import HTTPretty +from test import cleanup_queue from test.test_podcast import read_testdata, xml_headers sys.path.append("../python") @@ -68,6 +69,7 @@ def test_get_podposts(): """ Test listing of podposts """ + cleanup_queue() queue = QueueFactory().get_queue() podcast = PodcastFactory().get_podcast('https://freakshow.fm/feed/opus/') -- GitLab From 9167a449e22d8a316867b6a6c4f3ef7f51f28fde Mon Sep 17 00:00:00 2001 From: Thilo Kogge Date: Thu, 4 Feb 2021 12:24:08 +0100 Subject: [PATCH 15/18] fixed linter errors --- python/podcast/podcast.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/podcast/podcast.py b/python/podcast/podcast.py index 0d1a832..ad5f437 100644 --- a/python/podcast/podcast.py +++ b/python/podcast/podcast.py @@ -3,7 +3,7 @@ Podcast """ import sys -from typing import List, Dict, Optional, Generator, Iterator +from typing import List, Dict, Optional, Iterator from feedparser import FeedParserDict -- GitLab From e8426259d9725fef5eb3d9c4f6d72bc289698805 Mon Sep 17 00:00:00 2001 From: Thilo Kogge Date: Thu, 4 Feb 2021 12:28:27 +0100 Subject: [PATCH 16/18] fixed linter errors --- qml/components/ArchiveHandler.py | 2 -- qml/components/ExternalHandler.py | 2 +- qml/components/FavoriteHandler.py | 2 +- qml/components/FeedParser.py | 2 +- qml/components/InboxHandler.py | 2 -- qml/components/QueueHandler.py | 2 +- 6 files changed, 4 insertions(+), 8 deletions(-) diff --git a/qml/components/ArchiveHandler.py b/qml/components/ArchiveHandler.py index fba7763..7d0673d 100644 --- a/qml/components/ArchiveHandler.py +++ b/qml/components/ArchiveHandler.py @@ -5,8 +5,6 @@ import pyotherside import threading import sys -from podcast.podpost import PodpostFactory - sys.path.append("/usr/share/harbour-podqast/python") from podcast.archive import ArchiveFactory diff --git a/qml/components/ExternalHandler.py b/qml/components/ExternalHandler.py index ade7117..72e0075 100644 --- a/qml/components/ExternalHandler.py +++ b/qml/components/ExternalHandler.py @@ -7,10 +7,10 @@ import sys import os import hashlib -from podcast.constants import Constants sys.path.append("/usr/share/harbour-podqast/python") +from podcast.constants import Constants from podcast.external import ExternalFactory from podcast.inbox import InboxFactory from podcast.archive import ArchiveFactory diff --git a/qml/components/FavoriteHandler.py b/qml/components/FavoriteHandler.py index 2f686e9..84eb2da 100644 --- a/qml/components/FavoriteHandler.py +++ b/qml/components/FavoriteHandler.py @@ -5,9 +5,9 @@ import pyotherside import threading import sys -from podcast.podpost import PodpostFactory sys.path.append("/usr/share/harbour-podqast/python") +from podcast.podpost import PodpostFactory import podcast.favorite as favorite def toggle_favorite(podpost, do_download=False): diff --git a/qml/components/FeedParser.py b/qml/components/FeedParser.py index 21ac64a..b732145 100644 --- a/qml/components/FeedParser.py +++ b/qml/components/FeedParser.py @@ -9,10 +9,10 @@ import os import time import tarfile -from podcast.constants import Constants sys.path.append("/usr/share/harbour-podqast/python") +from podcast.constants import Constants from podcast.podcast import PodcastFactory from podcast.podcastlist import PodcastListFactory from podcast.queue import QueueFactory diff --git a/qml/components/InboxHandler.py b/qml/components/InboxHandler.py index aa527b5..b5abb2a 100644 --- a/qml/components/InboxHandler.py +++ b/qml/components/InboxHandler.py @@ -5,8 +5,6 @@ import pyotherside import threading import sys -from podcast.podpost import PodpostFactory - sys.path.append("/usr/share/harbour-podqast/python") from podcast.inbox import InboxFactory diff --git a/qml/components/QueueHandler.py b/qml/components/QueueHandler.py index 4297745..0783249 100644 --- a/qml/components/QueueHandler.py +++ b/qml/components/QueueHandler.py @@ -5,10 +5,10 @@ import pyotherside import threading import sys -from podcast.podpost import PodpostFactory sys.path.append("/usr/share/harbour-podqast/python") +from podcast.podpost import PodpostFactory from podcast.queue import QueueFactory from podcast.archive import ArchiveFactory -- GitLab From bcbf178a5a5a5c4bab6875018797656a3952c7da Mon Sep 17 00:00:00 2001 From: Thilo Kogge Date: Fri, 5 Feb 2021 18:07:58 +0100 Subject: [PATCH 17/18] extracted more data access logic into factories --- python/podcast/podcast.py | 14 +++++++------- python/podcast/podpost.py | 40 +++++++++++++++++++++++++++++++-------- 2 files changed, 39 insertions(+), 15 deletions(-) diff --git a/python/podcast/podcast.py b/python/podcast/podcast.py index ad5f437..575d13a 100644 --- a/python/podcast/podcast.py +++ b/python/podcast/podcast.py @@ -134,13 +134,13 @@ class Podcast: break all_episode_count += 1 post = Podpost(entry, self.url, image) - PodpostFactory().persist(post) new_posts.append(post) # todo: limit stuff probably broken if limit != 0 and all_episode_count >= limit: if not supress_limit_notification: pyotherside.send("apperror", "Auto post limit reached for %s!" % self.title) break + PodpostFactory().persist_bulk(new_posts) new_post_ids = [post.id for post in new_posts] new_post_ids.reverse() self.entry_ids_old_to_new.extend(new_post_ids) @@ -230,16 +230,16 @@ class Podcast: Get an entry from podcast by id """ - entry = PodpostFactory().get_podpost(id) - return entry + return PodpostFactory().get_podpost(id) + def get_entries(self) -> Iterator[Podpost]: """ Return a list of entries """ logger.debug("Podcast get_entries started.") - for id in self.entry_ids_old_to_new: - yield PodpostFactory().get_podpost(id).get_data() + for post in PodpostFactory().get_podcast_posts(self): + yield post.get_data() def get_latest_entry(self) -> Optional[Podpost]: """ @@ -251,6 +251,7 @@ class Podcast: else: return None + # todo handle download icon during init def download_icon(self): """ Download icon @@ -405,8 +406,7 @@ class PodcastFactory(BaseFactory): logger.info("deleting podcast and entries %s", url) to_delete = self.get_podcast(url) if to_delete != None: - for post in to_delete.entry_ids_old_to_new: - PodpostFactory().delete_podpost(post) + PodpostFactory().delete_all_from_podcast(self) self.podcastcache.delete(url) self.store.delete(url) diff --git a/python/podcast/podpost.py b/python/podcast/podpost.py index 42be1cb..d01ad60 100644 --- a/python/podcast/podpost.py +++ b/python/podcast/podpost.py @@ -15,7 +15,7 @@ from urllib.parse import urlparse import html2text from email.utils import mktime_tz, parsedate_tz from calendar import timegm -from typing import Optional, List +from typing import Optional, List, Iterator from podcast.factory import Factory, BaseFactory from podcast.util import s_to_hr, tx_to_s, dl_from_url, dl_from_url_progress @@ -43,7 +43,7 @@ class Podpost: title: str percentage: float position: int - isaudio: bool # if the post comes from the external folder + isaudio: bool # if the post comes from the external folder id: str state: int favorite: bool @@ -53,11 +53,11 @@ class Podpost: href: str duration: Optional[int] length: int - published: Optional[float] # when the post was published according to feed - insert_date: float # when we entered this post into our db + published: Optional[float] # when the post was published according to feed + insert_date: float # when we entered this post into our db link: Optional[str] type: str - podurl: str # the feed url + podurl: str # the feed url chapters: List def __init__(self, entry, podurl, logo_url, data=None, isaudio=False): @@ -588,8 +588,32 @@ class PodpostFactory(BaseFactory): self.podpostcache.delete(index) self.store.delete(index) - def persist(self, post:Podpost): + def persist(self, post: Podpost): if type(post) != Podpost: raise ValueError("Can only persist podposts") - self.store.store(post.id,post) - self.podpostcache.store(post.id,post) + self.store.store(post.id, post) + self.podpostcache.store(post.id, post) + + def get_podcast_posts(self, podcast) -> Iterator[Podpost]: + """ + @type podcast: Podcast + """ + from podcast.podcast import Podcast + if (type(podcast) != Podcast): + raise ValueError("supplied argument must be a podcast") + for id in podcast.entry_ids_old_to_new: + yield self.get_podpost(id) + + def persist_bulk(self, new_posts:List[Podpost]): + for post in new_posts: + self.persist(post) + + def delete_all_from_podcast(self, podcast): + """ + @type podcast: Podcast + """ + from podcast.podcast import Podcast + if (type(podcast) != Podcast): + raise ValueError("supplied argument must be a podcast") + for post_id in podcast.entry_ids_old_to_new: + self.delete_podpost(post_id) \ No newline at end of file -- GitLab From 54f537e79be18a1eb1dfb954e4a5b2bd70656d16 Mon Sep 17 00:00:00 2001 From: Thilo Kogge Date: Sat, 6 Feb 2021 20:20:53 +0100 Subject: [PATCH 18/18] fixed error in podcast remove, added tests --- python/podcast/podcast.py | 10 +++++----- test/test_podcast.py | 11 ++++++++--- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/python/podcast/podcast.py b/python/podcast/podcast.py index 575d13a..1889da8 100644 --- a/python/podcast/podcast.py +++ b/python/podcast/podcast.py @@ -233,7 +233,7 @@ class Podcast: return PodpostFactory().get_podpost(id) - def get_entries(self) -> Iterator[Podpost]: + def get_entries(self) -> Iterator[Dict]: """ Return a list of entries """ @@ -402,11 +402,11 @@ class PodcastFactory(BaseFactory): logger.debug("PodcastFactory: from cache %s", url) return podcast - def remove_podcast(self, url): + def remove_podcast(self, url: str): logger.info("deleting podcast and entries %s", url) to_delete = self.get_podcast(url) if to_delete != None: - PodpostFactory().delete_all_from_podcast(self) + PodpostFactory().delete_all_from_podcast(to_delete) self.podcastcache.delete(url) self.store.delete(url) @@ -415,5 +415,5 @@ class PodcastFactory(BaseFactory): self.podcastcache.store(podcast.url,podcast) def delete(self, podcast:Podcast): - self.store.delete(podcast.url,podcast) - self.podcastcache.delete(podcast.url,podcast) + self.store.delete(podcast.url) + self.podcastcache.delete(podcast.url) diff --git a/test/test_podcast.py b/test/test_podcast.py index 3d71891..f2f1afa 100644 --- a/test/test_podcast.py +++ b/test/test_podcast.py @@ -7,9 +7,11 @@ import os import httpretty from httpretty import HTTPretty +from podcast.podpost import PodpostFactory + sys.path.append("../python") -from podcast.podcast import Podcast +from podcast.podcast import Podcast, PodcastFactory xml_headers = {"content-type": "application/rss+xml; charset=UTF-8"} def read_testdata(filename): @@ -28,10 +30,10 @@ def test_feed_entry(): HTTPretty.register_uri(HTTPretty.GET, 'https://freakshow.fm/feed/opus/', body=read_testdata('testdata/freakshow.rss'), adding_headers=xml_headers) - for data in [ 'https://freakshow.fm/feed/opus/', + for url in [ 'https://freakshow.fm/feed/opus/', 'http://feeds.twit.tv/sn.xml', ]: - podcast = Podcast(data) + podcast = Podcast(url) assert podcast.alt_feeds != None for feed in podcast.alt_feeds: assert type(feed['url']) == str @@ -58,6 +60,9 @@ def test_feed_entry(): assert type(data['logo_url']) == str assert podcast.get_entry(data['id']) != None assert data == podcast.get_entry(data['id']).get_data() + PodcastFactory().remove_podcast(url) + for entry in entries: + assert PodpostFactory().get_podpost(entry['id']) is None invoked = 0 @httpretty.activate -- GitLab