Podcasts: Hörbuch-Kapitel kommen nie in die Wiedergabeliste oder Merkliste; Themen nur aus Folgentiteln (schneller, weniger Rauschen)
Some checks failed
Build and push Docker image / build (push) Successful in 15s
Test / test (push) Has been cancelled
Test / e2e (push) Waiting to run

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
mrwnslz 2026-10-19 19:00:38 +02:00
parent 3fa16b9653
commit 410d4806ca
3 changed files with 21 additions and 3 deletions

View file

@ -281,10 +281,11 @@ def topics_of(user, days=7):
[{'label', 'ids'}], the largest first.""" [{'label', 'ids'}], the largest first."""
from feeds import textproc from feeds import textproc
since = timezone.now() - timedelta(days=days) since = timezone.now() - timedelta(days=days)
# Titles only: the shownotes bring every podcast's furniture ("Link", "Abo", "Mediathek").
eps = list(PodcastEpisode.objects.filter(feed__user=user, feed__kind=PodcastFeed.PODCAST, pub_date__gte=since) eps = list(PodcastEpisode.objects.filter(feed__user=user, feed__kind=PodcastFeed.PODCAST, pub_date__gte=since)
.exclude(feed__new_episodes=PodcastFeed.NEW_IGNORE) .exclude(feed__new_episodes=PodcastFeed.NEW_IGNORE)
.values_list('id', 'title', 'description', 'feed__title')) .values_list('id', 'title', 'feed__title'))
stories = [(i, f'{t}. {_plain(d)[:400]}', {ft}) for i, t, d, ft in eps] stories = [(i, t, {ft}) for i, t, ft in eps]
return textproc.topics(stories, max_topics=10) return textproc.topics(stories, max_topics=10)

View file

@ -81,6 +81,14 @@ class MerklisteTests(Base):
self.post('/podcasts/progress/save/', {'episode_id': self.eps[2].id, 'position_seconds': 100}) self.post('/podcasts/progress/save/', {'episode_id': self.eps[2].id, 'position_seconds': 100})
self.assertEqual(self.later(), {self.eps[2].id}, 'started stays, heard goes') self.assertEqual(self.later(), {self.eps[2].id}, 'started stays, heard goes')
def test_audiobooks_go_on_neither_list(self):
book = PodcastFeed.objects.create(user=self.user, rss_url='https://example.org/book', title='Buch',
kind=PodcastFeed.AUDIOBOOK)
ch = PodcastEpisode.objects.create(feed=book, guid='c1', title='Kapitel 1', audio_url='https://example.org/c1.mp3')
self.post('/podcasts/queue/add/', {'episode_ids': [ch.id, self.eps[0].id]})
self.post('/podcasts/later/add/', {'episode_ids': [ch.id]})
self.assertEqual((self.queue(), self.later()), ([self.eps[0].id], set()))
def test_discard_leaves_both_lists(self): def test_discard_leaves_both_lists(self):
self.post('/podcasts/later/add/', {'episode_ids': [self.eps[0].id]}) self.post('/podcasts/later/add/', {'episode_ids': [self.eps[0].id]})
self.post('/podcasts/queue/add/', {'episode_ids': [self.eps[0].id]}) self.post('/podcasts/queue/add/', {'episode_ids': [self.eps[0].id]})

View file

@ -95,10 +95,19 @@ def _current_episode_id(user, queued, hint=None):
return p return p
def _not_books(episode_ids):
"""The ids that aren't an audiobook's chapters: those are played in the library, in their
book's order, and never sit in the Wiedergabeliste or the Merkliste (docs/audiobooks.md)."""
books = set(PodcastEpisode.objects.filter(id__in=episode_ids, feed__kind=PodcastFeed.AUDIOBOOK)
.values_list('id', flat=True))
return [i for i in episode_ids if i not in books]
def _enqueue(user, episode_ids, where, current_hint=None): def _enqueue(user, episode_ids, where, current_hint=None):
"""Put episodes that aren't queued yet into the queue at `where` """Put episodes that aren't queued yet into the queue at `where`
(back, front or after_current), keeping their given order. (back, front or after_current), keeping their given order.
Returns the ids that were added.""" Returns the ids that were added."""
episode_ids = _not_books(episode_ids)
queued = _queue_order(user) queued = _queue_order(user)
present = set(queued) present = set(queued)
new = [] new = []
@ -199,7 +208,7 @@ def _later_add(user, episode_ids, auto=False):
"""Put episodes on the user's Merkliste (those not on it yet). Like queueing they are no """Put episodes on the user's Merkliste (those not on it yet). Like queueing they are no
longer new; wanted again after "Folge löschen". Returns the ids added.""" longer new; wanted again after "Folge löschen". Returns the ids added."""
have = set(PodcastLater.objects.filter(user=user, episode_id__in=episode_ids).values_list('episode_id', flat=True)) have = set(PodcastLater.objects.filter(user=user, episode_id__in=episode_ids).values_list('episode_id', flat=True))
new = [i for i in dict.fromkeys(episode_ids) if i not in have] new = [i for i in dict.fromkeys(_not_books(episode_ids)) if i not in have]
if not new: if not new:
return [] return []
PodcastLater.objects.bulk_create([PodcastLater(user=user, episode_id=i, auto=auto) for i in new], ignore_conflicts=True) PodcastLater.objects.bulk_create([PodcastLater(user=user, episode_id=i, auto=auto) for i in new], ignore_conflicts=True)