mirror of
https://github.com/Dispatcharr/Dispatcharr.git
synced 2026-07-17 16:50:53 +00:00
fix(api): update HDHR URLs to use port-aware URI builder
- Refactored HDHR API views to utilize `build_absolute_uri_with_port()` for generating URLs, ensuring correct handling of non-standard ports in lineup and discovery responses.
This commit is contained in:
parent
b928de7d50
commit
a8a3c70e55
3 changed files with 18 additions and 8 deletions
|
|
@ -54,6 +54,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
|
||||
### Fixed
|
||||
|
||||
- **HDHR lineup and discovery URLs now use the shared port-aware URI builder.** `lineup.json`, `discover.json`, and `device.xml` previously called Django's `request.build_absolute_uri()`, which drops non-standard ports behind reverse proxies and on dev installs. They now use `build_absolute_uri_with_port()` from `core/utils.py`, matching M3U, EPG, XC, and logo cache URLs.
|
||||
- **EPG channel list did not refresh after a source finished parsing channels.** The `epg_refresh` WebSocket handler closed over a stale `epgs` snapshot from when the socket connected. When a newly created source was not found in that snapshot, the handler updated progress and returned early without calling `fetchEPGData()`, so the channel picker and EPG assignment UI stayed empty until a full page reload. The handler now reads the current store via `getState()` and still calls `fetchEPGData()` when `parsing_channels` completes.
|
||||
- **DVR recordings no longer stop immediately when FFmpeg exits mid-stream.** If FFmpeg crashes, stalls, or loses the source before the scheduled end time, `run_recording` now restarts it in-process instead of going straight to HLS→MKV concat and marking the recording complete. Each restart reuses the same HLS working directory and continues segment numbering (`append_list`, `-start_number`) so the final MKV is a single continuous file. Retries are bounded by a per-outage time window matching live-proxy client tolerance (`STREAM_TIMEOUT` + `FAILOVER_GRACE_PERIOD`, default 80s); the window resets whenever new segments resume, so a long recording can survive multiple separate outages. Reconnect pacing between attempts follows the same `min(0.25 × attempt, 3s)` backoff used by the live-proxy `StreamManager`. Input demuxing also uses `-err_detect ignore_err` to tolerate minor TS corruption without aborting the process. If the outage window expires without recovery, the recording is saved as `interrupted` with `ffmpeg_outage_window_exhausted` rather than falsely reported as `completed`. (Closes #1170)
|
||||
- **DVR HLS→MKV concat now tolerates timestamp splices and corrupt segments.** The finalize step (and startup recovery concat) previously used a bare `ffmpeg -f concat -c copy`, which could fail on truncated tail segments or PTS discontinuities from FFmpeg restarts mid-recording. Concat now uses a shared `_dvr_build_hls_concat_cmd` helper with `-fflags +genpts+igndts+discardcorrupt`, `-err_detect ignore_err`, and `-avoid_negative_ts make_zero`; the existing MP4-intermediate fallback path uses the same tolerant input flags.
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ from django.utils.decorators import method_decorator
|
|||
from django.contrib.auth.decorators import login_required
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
from dispatcharr.utils import network_access_allowed
|
||||
from core.utils import build_absolute_uri_with_port
|
||||
|
||||
# Configure logger
|
||||
logger = logging.getLogger(__name__)
|
||||
|
|
@ -75,7 +76,7 @@ class DiscoverAPIView(APIView):
|
|||
uri_parts.append("output_profile")
|
||||
uri_parts.append(str(output_profile_id))
|
||||
|
||||
base_url = request.build_absolute_uri(f'/{"/".join(uri_parts)}/').rstrip("/")
|
||||
base_url = build_absolute_uri_with_port(request, f'/{"/".join(uri_parts)}/').rstrip("/")
|
||||
device = HDHRDevice.objects.first()
|
||||
|
||||
from apps.m3u.utils import calculate_tuner_count
|
||||
|
|
@ -166,6 +167,13 @@ class LineupAPIView(APIView):
|
|||
|
||||
resolved_output_profile_id = _resolve_hdhr_output_profile_id(output_profile_id)
|
||||
|
||||
_stream_url_prefix = build_absolute_uri_with_port(request, "/proxy/ts/stream/")
|
||||
_output_profile_qs = (
|
||||
f"?output_profile={resolved_output_profile_id}"
|
||||
if resolved_output_profile_id is not None
|
||||
else ""
|
||||
)
|
||||
|
||||
lineup = []
|
||||
for ch in channels:
|
||||
formatted = format_channel_number(ch.effective_channel_number, empty=None)
|
||||
|
|
@ -173,9 +181,7 @@ class LineupAPIView(APIView):
|
|||
continue
|
||||
formatted_channel_number = str(formatted)
|
||||
|
||||
stream_url = request.build_absolute_uri(f"/proxy/ts/stream/{ch.uuid}")
|
||||
if resolved_output_profile_id is not None:
|
||||
stream_url += f"?output_profile={resolved_output_profile_id}"
|
||||
stream_url = f"{_stream_url_prefix}{ch.uuid}{_output_profile_qs}"
|
||||
|
||||
lineup.append(
|
||||
{
|
||||
|
|
@ -224,7 +230,7 @@ class HDHRDeviceXMLAPIView(APIView):
|
|||
if blocked is not None:
|
||||
return blocked
|
||||
|
||||
base_url = request.build_absolute_uri("/hdhr/").rstrip("/")
|
||||
base_url = build_absolute_uri_with_port(request, "/hdhr/").rstrip("/")
|
||||
|
||||
xml_response = f"""<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ from django.views import View
|
|||
from django.utils.decorators import method_decorator
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
from core.utils import build_absolute_uri_with_port
|
||||
|
||||
|
||||
@login_required
|
||||
|
|
@ -46,7 +47,7 @@ class DiscoverAPIView(APIView):
|
|||
description="Retrieve HDHomeRun device discovery information",
|
||||
)
|
||||
def get(self, request):
|
||||
base_url = request.build_absolute_uri("/hdhr/").rstrip("/")
|
||||
base_url = build_absolute_uri_with_port(request, "/hdhr/").rstrip("/")
|
||||
device = HDHRDevice.objects.first()
|
||||
|
||||
if not device:
|
||||
|
|
@ -92,6 +93,8 @@ class LineupAPIView(APIView):
|
|||
.exclude(hidden_from_output=True)
|
||||
.order_by("effective_channel_number")
|
||||
)
|
||||
_stream_url_prefix = build_absolute_uri_with_port(request, "/proxy/ts/stream/")
|
||||
|
||||
lineup = []
|
||||
for ch in channels:
|
||||
formatted = format_channel_number(ch.effective_channel_number, empty=None)
|
||||
|
|
@ -102,7 +105,7 @@ class LineupAPIView(APIView):
|
|||
{
|
||||
"GuideNumber": formatted_channel_number,
|
||||
"GuideName": ch.effective_name,
|
||||
"URL": request.build_absolute_uri(f"/proxy/ts/stream/{ch.uuid}"),
|
||||
"URL": f"{_stream_url_prefix}{ch.uuid}",
|
||||
}
|
||||
)
|
||||
return JsonResponse(lineup, safe=False)
|
||||
|
|
@ -133,7 +136,7 @@ class HDHRDeviceXMLAPIView(APIView):
|
|||
description="Retrieve the HDHomeRun device XML configuration",
|
||||
)
|
||||
def get(self, request):
|
||||
base_url = request.build_absolute_uri("/hdhr/").rstrip("/")
|
||||
base_url = build_absolute_uri_with_port(request, "/hdhr/").rstrip("/")
|
||||
|
||||
xml_response = f"""<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue