Podcasts: neues Abo bringt nur die neueste Folge in die Inbox
Beim ersten Abruf eines Feeds (Abonnieren, OPML-Import, gpodder) kam der ganze Rückkatalog als „neu“ in die Inbox (bei Lage der Nation 200 Folgen). Jetzt zählt nur die neueste; die älteren sind ausgeblendet, nicht gehört, und stehen weiter in der Folgenliste des Podcasts. Auch automatisches Einreihen nimmt beim ersten Abruf nur diese eine. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
176771a407
commit
a1e17081c5
2 changed files with 48 additions and 0 deletions
|
|
@ -111,6 +111,7 @@ class AutoQueueTests(TestCase):
|
||||||
self.user = User.objects.create_user(username='alice', password='pw12345678')
|
self.user = User.objects.create_user(username='alice', password='pw12345678')
|
||||||
self.feed = PodcastFeed.objects.create(
|
self.feed = PodcastFeed.objects.create(
|
||||||
user=self.user, rss_url='https://example.org/feed', title='Feed', auto_queue=True,
|
user=self.user, rss_url='https://example.org/feed', title='Feed', auto_queue=True,
|
||||||
|
last_refreshed_at=datetime(2026, 1, 1, tzinfo=timezone.utc), # not the first fetch
|
||||||
)
|
)
|
||||||
other = PodcastFeed.objects.create(user=self.user, rss_url='https://example.org/o', title='O')
|
other = PodcastFeed.objects.create(user=self.user, rss_url='https://example.org/o', title='O')
|
||||||
self.old = [
|
self.old = [
|
||||||
|
|
@ -148,6 +149,39 @@ class AutoQueueTests(TestCase):
|
||||||
self.assertEqual(self.refresh(), ['o0', 'o1', 'o2'])
|
self.assertEqual(self.refresh(), ['o0', 'o1', 'o2'])
|
||||||
|
|
||||||
|
|
||||||
|
class FirstFetchTests(TestCase):
|
||||||
|
"""A new subscription doesn't flood the inbox with its back catalogue."""
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.user = User.objects.create_user(username='alice', password='pw12345678')
|
||||||
|
self.client.force_login(self.user)
|
||||||
|
self.rss = _rss([(f'e{i}', f'Mon, {i + 1:02d} Jan 2026 10:00:00 GMT') for i in range(5)])
|
||||||
|
|
||||||
|
def subscribe(self):
|
||||||
|
with patch('podcasts.views._fetch_feed', lambda url: _parse(self.rss)):
|
||||||
|
return self.client.post('/podcasts/feeds/add/', json.dumps({'rss_url': 'https://example.org/f'}),
|
||||||
|
content_type='application/json')
|
||||||
|
|
||||||
|
def test_only_the_newest_is_new(self):
|
||||||
|
self.assertEqual(self.subscribe().json()['new_episodes'], 5)
|
||||||
|
inbox = self.client.get('/podcasts/inbox/').json()['episodes']
|
||||||
|
self.assertEqual([e['title'] for e in inbox], ['e4'])
|
||||||
|
# The others are still listed for the podcast, not marked played.
|
||||||
|
feed = PodcastFeed.objects.get(user=self.user)
|
||||||
|
eps = self.client.get(f'/podcasts/feeds/{feed.id}/episodes/').json()['episodes']
|
||||||
|
self.assertEqual(len(eps), 5)
|
||||||
|
self.assertFalse(any(e['played'] for e in eps))
|
||||||
|
|
||||||
|
def test_later_fetches_keep_every_new_episode(self):
|
||||||
|
self.subscribe()
|
||||||
|
feed = PodcastFeed.objects.get(user=self.user)
|
||||||
|
self.rss = _rss([(f'e{i}', f'Mon, {i + 1:02d} Jan 2026 10:00:00 GMT') for i in range(8)])
|
||||||
|
with patch('podcasts.views._fetch_feed', lambda url: _parse(self.rss)):
|
||||||
|
_refresh_feed(feed)
|
||||||
|
inbox = self.client.get('/podcasts/inbox/').json()['episodes']
|
||||||
|
self.assertEqual([e['title'] for e in inbox], ['e7', 'e6', 'e5', 'e4'])
|
||||||
|
|
||||||
|
|
||||||
class RefreshAllTests(TestCase):
|
class RefreshAllTests(TestCase):
|
||||||
"""One request refreshes all feeds on the server; one request returns the state."""
|
"""One request refreshes all feeds on the server; one request returns the state."""
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ from .models import EpisodeProgress, PodcastEpisode, PodcastFeed, PodcastQueue,
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
FEED_TIMEOUT = 20 # seconds per feed download
|
FEED_TIMEOUT = 20 # seconds per feed download
|
||||||
|
NEW_ON_SUBSCRIBE = 1 # episodes of a new subscription that count as new
|
||||||
FEED_WORKERS = 8 # feeds downloaded at the same time
|
FEED_WORKERS = 8 # feeds downloaded at the same time
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -167,6 +168,8 @@ def _refresh_feed(feed_obj, parsed=None):
|
||||||
if parsed is None:
|
if parsed is None:
|
||||||
parsed = _fetch_feed(feed_obj.rss_url)
|
parsed = _fetch_feed(feed_obj.rss_url)
|
||||||
channel = parsed.feed
|
channel = parsed.feed
|
||||||
|
# The first fetch of a new subscription brings the whole back catalogue.
|
||||||
|
first_fetch = feed_obj.last_refreshed_at is None
|
||||||
|
|
||||||
feed_obj.title = channel.get('title', feed_obj.title or feed_obj.rss_url)[:300]
|
feed_obj.title = channel.get('title', feed_obj.title or feed_obj.rss_url)[:300]
|
||||||
feed_obj.description = channel.get('subtitle', channel.get('description', ''))[:2000]
|
feed_obj.description = channel.get('subtitle', channel.get('description', ''))[:2000]
|
||||||
|
|
@ -245,6 +248,17 @@ def _refresh_feed(feed_obj, parsed=None):
|
||||||
new_count += 1
|
new_count += 1
|
||||||
new_episodes_list.append(episode_obj)
|
new_episodes_list.append(episode_obj)
|
||||||
|
|
||||||
|
if first_fetch and len(new_episodes_list) > NEW_ON_SUBSCRIBE:
|
||||||
|
# Only the newest episodes count as new; the rest stay in the
|
||||||
|
# podcast's episode list, out of the inbox (dismissed, not played).
|
||||||
|
newest_first = sorted(new_episodes_list, key=lambda e: e.pub_date or e.discovered_at, reverse=True)
|
||||||
|
older = newest_first[NEW_ON_SUBSCRIBE:]
|
||||||
|
EpisodeProgress.objects.bulk_create(
|
||||||
|
[EpisodeProgress(user=feed_obj.user, episode=e, dismissed=True) for e in older],
|
||||||
|
ignore_conflicts=True,
|
||||||
|
)
|
||||||
|
new_episodes_list = newest_first[:NEW_ON_SUBSCRIBE]
|
||||||
|
|
||||||
if feed_obj.auto_queue and new_episodes_list:
|
if feed_obj.auto_queue and new_episodes_list:
|
||||||
where = PodcastSettings.for_user(feed_obj.user).auto_queue_position
|
where = PodcastSettings.for_user(feed_obj.user).auto_queue_position
|
||||||
# Newest first in front of what's there; oldest first when appended,
|
# Newest first in front of what's there; oldest first when appended,
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue