MCPcopy Create free account
hub / github.com/Dispatcharr/Dispatcharr / file

Method file

apps/channels/api_views.py:3346–3431  ·  view source on GitHub ↗

Stream a completed recording file with HTTP Range support for seeking. For in-progress recordings, file_url in custom_properties points to /hls/index.m3u8. If a client hits this endpoint while the recording is still running (or the MKV is not yet produced), it is redirected

(self, request, pk=None)

Source from the content-addressed store, hash-verified

3344 authentication_classes=RECORDING_PLAYBACK_AUTHENTICATORS,
3345 )
3346 def file(self, request, pk=None):
3347 """Stream a completed recording file with HTTP Range support for seeking.
3348
3349 For in-progress recordings, file_url in custom_properties points to
3350 /hls/index.m3u8. If a client hits this endpoint while the recording
3351 is still running (or the MKV is not yet produced), it is redirected to
3352 the HLS playlist endpoint.
3353 """
3354 if not network_access_allowed(request, "STREAMS"):
3355 return JsonResponse({"error": "Forbidden"}, status=403)
3356 recording = get_object_or_404(Recording, pk=pk)
3357 if not self._user_can_play_recording(request, recording):
3358 return JsonResponse({"error": "Forbidden"}, status=403)
3359 cp = recording.custom_properties or {}
3360 file_path = cp.get("file_path")
3361 file_name = cp.get("file_name") or "recording"
3362
3363 if not file_path or not os.path.exists(file_path) or os.path.getsize(file_path) == 0:
3364 # Redirect to HLS if recording is still in progress
3365 hls_dir = cp.get("_hls_dir")
3366 if hls_dir and os.path.isdir(hls_dir):
3367 hls_url = request.build_absolute_uri(
3368 f"/api/channels/recordings/{pk}/hls/index.m3u8"
3369 ) + _recording_auth_query_suffix(request)
3370 return HttpResponseRedirect(hls_url)
3371 if not file_path or not os.path.exists(file_path):
3372 raise Http404("Recording file not found")
3373
3374 # Guess content type
3375 ext = os.path.splitext(file_path)[1].lower()
3376 if ext == ".mp4":
3377 content_type = "video/mp4"
3378 elif ext == ".mkv":
3379 content_type = "video/x-matroska"
3380 else:
3381 content_type = mimetypes.guess_type(file_path)[0] or "application/octet-stream"
3382
3383 file_size = os.path.getsize(file_path)
3384 range_header = request.META.get("HTTP_RANGE", "").strip()
3385
3386 def file_iterator(path, start=0, end=None, chunk_size=8192):
3387 with open(path, "rb") as f:
3388 f.seek(start)
3389 remaining = (end - start + 1) if end is not None else None
3390 while True:
3391 if remaining is not None and remaining <= 0:
3392 break
3393 bytes_to_read = min(chunk_size, remaining) if remaining is not None else chunk_size
3394 data = f.read(bytes_to_read)
3395 if not data:
3396 break
3397 if remaining is not None:
3398 remaining -= len(data)
3399 yield data
3400
3401 if range_header and range_header.startswith("bytes="):
3402 # Parse Range header
3403 try:

Callers

nothing calls this directly

Calls 6

network_access_allowedFunction · 0.90
file_iteratorFunction · 0.85
getMethod · 0.45
existsMethod · 0.45

Tested by

no test coverage detected