Dispatcharr/dispatcharr/urls.py
Cédric Marcoux 4f45249067 fix(timeshift): resolve catch-up channel by Channel.id only + cleanup
- timeshift_proxy(): drop the resolve_channel_by_provider_stream_id
  fallback. The client only ever sends Dispatcharr's internal Channel.id,
  so resolve by id and return 404 on miss. Remove the dead `channel = None`
  sentinel and the now-unused import.
- Delete the now-orphaned resolve_channel_by_provider_stream_id helper and
  its local Stream import from apps/channels/utils.py (zero remaining refs).
- Import django.core.cache.cache at module top-level instead of inside
  _get_cached_format_index / _set_cached_format_index.
- Fix the stale dispatcharr/urls.py comment: the "duration" slot carries
  Channel.id, not the provider stream_id.
- Genericize provider host references in code comments.
2026-06-01 12:51:14 +02:00

79 lines
3.5 KiB
Python

from django.contrib import admin
from django.urls import path, include, re_path
from django.conf import settings
from django.conf.urls.static import static
from django.views.generic import TemplateView, RedirectView
from .routing import websocket_urlpatterns
from apps.output.views import xc_player_api, xc_panel_api, xc_get, xc_xmltv
from apps.proxy.live_proxy.views import stream_xc
from apps.proxy.vod_proxy.views import stream_xc_movie, stream_xc_episode
from apps.timeshift.views import timeshift_proxy
urlpatterns = [
# API Routes
path("api/", include(("apps.api.urls", "api"), namespace="api")),
path("api", RedirectView.as_view(url="/api/", permanent=True)),
# Swagger redirects (Swagger UI is served at /api/swagger/)
path("swagger/", RedirectView.as_view(url="/api/swagger/", permanent=True)),
path("swagger", RedirectView.as_view(url="/api/swagger/", permanent=True)),
path("redoc/", RedirectView.as_view(url="/api/redoc/", permanent=True)),
path("redoc", RedirectView.as_view(url="/api/redoc/", permanent=True)),
# Outputs
path("output", RedirectView.as_view(url="/output/", permanent=True)),
path("output/", include(("apps.output.urls", "output"), namespace="output")),
# HDHR
path("hdhr", RedirectView.as_view(url="/hdhr/", permanent=True)),
path("hdhr/", include(("apps.hdhr.urls", "hdhr"), namespace="hdhr")),
# Add proxy apps - Move these before the catch-all
path("proxy/", include(("apps.proxy.urls", "proxy"), namespace="proxy")),
path("proxy", RedirectView.as_view(url="/proxy/", permanent=True)),
# xc
re_path("player_api.php", xc_player_api, name="xc_player_api"),
re_path("panel_api.php", xc_panel_api, name="xc_panel_api"),
re_path("get.php", xc_get, name="xc_get"),
re_path("xmltv.php", xc_xmltv, name="xc_xmltv"),
path(
"live/<str:username>/<str:password>/<str:channel_id>",
stream_xc,
name="xc_live_stream_endpoint",
),
path(
"<str:username>/<str:password>/<str:channel_id>",
stream_xc,
name="xc_stream_endpoint",
),
# XC catch-up (timeshift). The "duration" slot carries Dispatcharr's internal
# Channel.id (the XC API emits channel.id as stream_id to clients), see
# apps/timeshift/views.
path(
"timeshift/<str:username>/<str:password>/<str:stream_id>/<str:timestamp>/<str:duration>",
timeshift_proxy,
name="timeshift_proxy",
),
# XC VOD endpoints
path(
"movie/<str:username>/<str:password>/<str:stream_id>.<str:extension>",
stream_xc_movie,
name="stream_xc_movie",
),
path(
"series/<str:username>/<str:password>/<str:stream_id>.<str:extension>",
stream_xc_episode,
name="stream_xc_episode",
),
# Admin
path("admin", RedirectView.as_view(url="/admin/", permanent=True)),
path("admin/", admin.site.urls),
# VOD proxy is now handled by the main proxy URLs above
# Catch-all routes should always be last
path("", TemplateView.as_view(template_name="index.html")), # React entry point
path("<path:unused_path>", TemplateView.as_view(template_name="index.html")),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
urlpatterns += websocket_urlpatterns
# Serve static files for development (React's JS, CSS, etc.)
if settings.DEBUG:
urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)