- Ohne Kapitel im Feed (kein PSC, keine chapters_url oder dort nichts)
liest die Folgen-Detailansicht einmalig den ID3v2-Tag am Anfang der
Audiodatei (Range-Anfrage, höchstens 4 MB, Weiterleitungen mit
SSRF-Prüfung) und übernimmt dessen CHAP-Frames (v2.3/v2.4,
Unsynchronisation, erweiterter Header, Synchsafe-Größen, CTOC-Reihenfolge).
Eigener Parser nach der ID3-Spezifikation; Ergebnis wird wie die
JSON-Kapitel zwischengespeichert, vorübergehende Fehler nicht.
- Neu: GET /podcasts/episodes/<id>/transcript/ holt das beste Transkript
(JSON vor VTT vor SRT, Deutsch bzw. ohne Sprachangabe vor Englisch),
zerlegt es in Abschnitte {start, end, speaker, text}, fasst kurze
Schnipsel desselben Sprechers bis zum Satzende bzw. ~30 s zusammen und
speichert das Ergebnis (eigene Tabelle EpisodeTranscript). HTML: 404
{"error": "unsupported"}.
- Folgen-Detail liefert zusätzlich has_transcript.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
30 lines
1.9 KiB
Python
30 lines
1.9 KiB
Python
from django.urls import path
|
|
from . import views
|
|
|
|
urlpatterns = [
|
|
path('search/', views.podcast_search, name='podcast_search'),
|
|
path('feeds/', views.feed_list, name='podcast_feed_list'),
|
|
path('feeds/add/', views.add_feed, name='podcast_add_feed'),
|
|
path('feeds/import/', views.import_opml, name='podcast_import_opml'),
|
|
path('feeds/export.opml', views.export_opml, name='podcast_export_opml'),
|
|
path('feeds/refresh/', views.refresh_feed_now, name='podcast_refresh_feed'),
|
|
path('feeds/refresh-all/', views.refresh_all_feeds, name='podcast_refresh_all_feeds'),
|
|
path('state/', views.podcast_state, name='podcast_state'),
|
|
path('feeds/<int:pk>/remove/', views.remove_feed, name='podcast_remove_feed'),
|
|
path('feeds/<int:pk>/set-auto-queue/', views.set_auto_queue, name='podcast_set_auto_queue'),
|
|
path('feeds/<int:pk>/episodes/', views.feed_episodes, name='podcast_feed_episodes'),
|
|
path('episodes/', views.episode_list, name='podcast_episode_list'),
|
|
path('episodes/<int:pk>/', views.episode_detail, name='podcast_episode_detail'),
|
|
path('episodes/<int:pk>/transcript/', views.episode_transcript, name='podcast_episode_transcript'),
|
|
path('episodes/<int:pk>/favorite/', views.set_favorite, name='podcast_set_favorite'),
|
|
path('stats/', views.podcast_stats, name='podcast_stats'),
|
|
path('queue/', views.queue_get, name='podcast_queue_get'),
|
|
path('queue/add/', views.queue_add, name='podcast_queue_add'),
|
|
path('queue/remove/', views.queue_remove, name='podcast_queue_remove'),
|
|
path('queue/reorder/', views.queue_reorder, name='podcast_queue_reorder'),
|
|
path('settings/', views.podcast_settings, name='podcast_settings'),
|
|
path('progress/save/', views.save_progress, name='podcast_save_progress'),
|
|
path('progress/mark-played/', views.mark_played, name='podcast_mark_played'),
|
|
path('progress/dismiss/', views.dismiss_episodes, name='podcast_dismiss_episodes'),
|
|
path('inbox/', views.inbox, name='podcast_inbox'),
|
|
]
|