mirror of
https://github.com/nirvana-7777/script.service.ultimate.git
synced 2026-10-02 14:02:32 +02:00
change routes
This commit is contained in:
+84
-68
@@ -30,9 +30,9 @@ def setup_m3u_routes(app, manager, service):
|
||||
proxy-gated "fast" endpoints, for their own 503 handling.
|
||||
|
||||
cache_key: pass None for routes that must always regenerate live
|
||||
(the uncached "fast" proxied/ffmpeg endpoints, and the force
|
||||
/generate endpoints, which never checked cache in the original
|
||||
code either).
|
||||
(the uncached clientdrm endpoints and the ffmpeg endpoint, plus the
|
||||
force /generate endpoints, which never checked cache in the
|
||||
original code either).
|
||||
"""
|
||||
try:
|
||||
if cache_key:
|
||||
@@ -61,102 +61,119 @@ def setup_m3u_routes(app, manager, service):
|
||||
pass # cache file may not exist yet - fine
|
||||
return _handle_m3u_route(generate_fn, log_ctx)
|
||||
|
||||
# ── Normal / no-proxy playlists (cached) ──────────────────────────────
|
||||
# ── Plain playlists (cached) ──────────────────────────────────────────
|
||||
# Server-side decrypt, bare stream URLs (no "?client_drm=false" — that's
|
||||
# the route's own default). Nothing per-channel here is request-time-
|
||||
# volatile (no DRM/key lookups happen at generation time), so caching is
|
||||
# safe and this is now the default "/api/m3u" behavior.
|
||||
|
||||
@app.route("/api/m3u")
|
||||
def get_m3u_all():
|
||||
"""Generates M3U playlist for all configured providers."""
|
||||
return _handle_m3u_route(
|
||||
lambda: service.generate_m3u_all(save_to_cache=True),
|
||||
lambda: service.generate_m3u_plain_all(save_to_cache=True),
|
||||
log_ctx="/api/m3u",
|
||||
cache_key="playlist.m3u",
|
||||
filename="playlist.m3u8",
|
||||
)
|
||||
|
||||
@app.route("/api/m3u/noproxy")
|
||||
def get_m3u_all_noproxy():
|
||||
"""Generates M3U playlist using direct (non-proxied) stream URLs."""
|
||||
return _handle_m3u_route(
|
||||
lambda: service.generate_m3u_all(save_to_cache=True, no_proxy=True),
|
||||
log_ctx="/api/m3u/noproxy",
|
||||
cache_key="playlist_noproxy.m3u",
|
||||
filename="playlist_noproxy.m3u8",
|
||||
)
|
||||
|
||||
@app.route("/api/m3u/generate")
|
||||
def generate_m3u_all():
|
||||
"""Force regeneration of M3U playlist for all providers."""
|
||||
return _handle_m3u_route(
|
||||
lambda: service.generate_m3u_all(save_to_cache=True),
|
||||
lambda: service.generate_m3u_plain_all(save_to_cache=True),
|
||||
log_ctx="/api/m3u/generate",
|
||||
)
|
||||
|
||||
@app.route("/api/m3u/noproxy/generate")
|
||||
def generate_m3u_all_noproxy():
|
||||
"""Force regeneration of the no-proxy M3U playlist."""
|
||||
return _handle_m3u_route(
|
||||
lambda: service.generate_m3u_all(save_to_cache=True, no_proxy=True),
|
||||
log_ctx="/api/m3u/noproxy/generate",
|
||||
)
|
||||
|
||||
@app.route("/api/providers/<provider>/m3u")
|
||||
def get_m3u_provider(provider):
|
||||
"""Generates M3U playlist for a specific provider."""
|
||||
return _handle_m3u_route(
|
||||
lambda: service.generate_m3u_provider(provider, save_to_cache=True),
|
||||
lambda: service.generate_m3u_plain_provider(provider, save_to_cache=True),
|
||||
log_ctx=f"/api/providers/{provider}/m3u",
|
||||
cache_key=f"{provider}.m3u",
|
||||
filename=f"{provider}_playlist.m3u8",
|
||||
)
|
||||
|
||||
@app.route("/api/providers/<provider>/m3u/noproxy")
|
||||
def get_m3u_provider_noproxy(provider):
|
||||
"""Generates no-proxy M3U playlist for a specific provider."""
|
||||
return _handle_m3u_route(
|
||||
lambda: service.generate_m3u_provider(provider, save_to_cache=True, no_proxy=True),
|
||||
log_ctx=f"/api/providers/{provider}/m3u/noproxy",
|
||||
cache_key=f"{provider}_noproxy.m3u",
|
||||
filename=f"{provider}_playlist_noproxy.m3u8",
|
||||
)
|
||||
|
||||
@app.route("/api/providers/<provider>/m3u/generate")
|
||||
def generate_m3u_provider(provider):
|
||||
"""Force regeneration of M3U playlist for a specific provider."""
|
||||
return _handle_m3u_route(
|
||||
lambda: service.generate_m3u_provider(provider, save_to_cache=True),
|
||||
lambda: service.generate_m3u_plain_provider(provider, save_to_cache=True),
|
||||
log_ctx=f"/api/providers/{provider}/m3u/generate",
|
||||
)
|
||||
|
||||
# ── Client-side-decrypt playlists (deliberately UNCACHED) ─────────────
|
||||
# Dynamic per-channel ClearKey lookup, key/kid pairs baked into KODIPROP
|
||||
# directives. Uncached on purpose: upstream keys can rotate, and a
|
||||
# cached playlist would silently serve a stale key until someone
|
||||
# force-regenerated it. Fresh generation per request avoids that
|
||||
# failure mode; revisit with a short TTL if per-request DRM lookups
|
||||
# turn out to be too frequent/expensive.
|
||||
|
||||
@app.route("/api/m3u/clientdrm")
|
||||
def get_m3u_clientdrm():
|
||||
"""Generates client-side-decrypt M3U playlist for all providers. No caching."""
|
||||
return _handle_m3u_route(
|
||||
lambda: service.generate_m3u_clientdrm_all(),
|
||||
log_ctx="/api/m3u/clientdrm",
|
||||
)
|
||||
|
||||
@app.route("/api/providers/<provider>/m3u/clientdrm")
|
||||
def get_m3u_clientdrm_provider(provider):
|
||||
"""Generates client-side-decrypt M3U playlist for a specific provider. No caching."""
|
||||
return _handle_m3u_route(
|
||||
lambda: service.generate_m3u_clientdrm_provider(provider),
|
||||
log_ctx=f"/api/providers/{provider}/m3u/clientdrm",
|
||||
)
|
||||
|
||||
# ── No-proxy playlists (cached) ────────────────────────────────────────
|
||||
# Unrelated to the plain/clientdrm split above — "no_proxy" here means
|
||||
# bypassing the media proxy at the stream-route level, independent of
|
||||
# who does the decrypting. Still client-side-decrypt underneath
|
||||
# (client_drm=true&no_proxy=true), same as before this change; only the
|
||||
# service method name changed (generate_m3u_all -> generate_m3u_noproxy_all),
|
||||
# since generate_m3u_all now refers to the plain playlist above.
|
||||
|
||||
@app.route("/api/m3u/noproxy")
|
||||
def get_m3u_all_noproxy():
|
||||
"""Generates M3U playlist using direct (non-proxied) stream URLs."""
|
||||
return _handle_m3u_route(
|
||||
lambda: service.generate_m3u_noproxy_all(save_to_cache=True),
|
||||
log_ctx="/api/m3u/noproxy",
|
||||
cache_key="playlist_noproxy.m3u",
|
||||
filename="playlist_noproxy.m3u8",
|
||||
)
|
||||
|
||||
@app.route("/api/m3u/noproxy/generate")
|
||||
def generate_m3u_all_noproxy():
|
||||
"""Force regeneration of the no-proxy M3U playlist."""
|
||||
return _handle_m3u_route(
|
||||
lambda: service.generate_m3u_noproxy_all(save_to_cache=True),
|
||||
log_ctx="/api/m3u/noproxy/generate",
|
||||
)
|
||||
|
||||
@app.route("/api/providers/<provider>/m3u/noproxy")
|
||||
def get_m3u_provider_noproxy(provider):
|
||||
"""Generates no-proxy M3U playlist for a specific provider."""
|
||||
return _handle_m3u_route(
|
||||
lambda: service.generate_m3u_noproxy_provider(provider, save_to_cache=True),
|
||||
log_ctx=f"/api/providers/{provider}/m3u/noproxy",
|
||||
cache_key=f"{provider}_noproxy.m3u",
|
||||
filename=f"{provider}_playlist_noproxy.m3u8",
|
||||
)
|
||||
|
||||
@app.route("/api/providers/<provider>/m3u/noproxy/generate")
|
||||
def generate_m3u_provider_noproxy(provider):
|
||||
"""Force regeneration of no-proxy M3U playlist for a specific provider."""
|
||||
return _handle_m3u_route(
|
||||
lambda: service.generate_m3u_provider(provider, save_to_cache=True, no_proxy=True),
|
||||
lambda: service.generate_m3u_noproxy_provider(provider, save_to_cache=True),
|
||||
log_ctx=f"/api/providers/{provider}/m3u/noproxy/generate",
|
||||
)
|
||||
|
||||
# ── Proxied / decrypted "fast" playlists (deliberately UNCACHED) ─────
|
||||
# These intentionally skip the cache layer - proxy/DRM session state
|
||||
# can shift between requests, and service.generate_m3u_proxied_fast /
|
||||
# generate_m3u_decrypted_ffmpeg_fast already own the 503 "media proxy
|
||||
# not configured" guard and their own response headers. Do not give
|
||||
# these a cache_key.
|
||||
|
||||
@app.route("/api/m3u/proxied")
|
||||
def get_m3u_proxied():
|
||||
"""Generates proxied M3U playlist for all providers. No caching."""
|
||||
return _handle_m3u_route(
|
||||
lambda: service.generate_m3u_proxied_fast(providers=None),
|
||||
log_ctx="/api/m3u/proxied",
|
||||
)
|
||||
|
||||
@app.route("/api/providers/<provider>/m3u/proxied")
|
||||
def get_m3u_proxied_provider(provider):
|
||||
"""Generates proxied M3U playlist for a specific provider. No caching."""
|
||||
return _handle_m3u_route(
|
||||
lambda: service.generate_m3u_proxied_fast(providers=provider),
|
||||
log_ctx=f"/api/providers/{provider}/m3u/proxied",
|
||||
)
|
||||
# ── ffmpeg-piped playlist (deliberately UNCACHED) ─────────────────────
|
||||
# Unchanged by this turn's split — left as-is per your call to leave
|
||||
# ffmpeg/filtered/subscribed alone for now.
|
||||
|
||||
@app.route("/api/providers/<provider>/m3u/proxied/ffmpeg")
|
||||
def get_m3u_proxied_ffmpeg_provider(provider):
|
||||
@@ -167,8 +184,9 @@ def setup_m3u_routes(app, manager, service):
|
||||
)
|
||||
|
||||
# ── Filtered proxied playlists (cached; ClearKey or unencrypted only) ─
|
||||
# NOTE: cache filenames below match what
|
||||
# _generate_m3u_proxied_filtered_content() actually writes
|
||||
# Unchanged by this turn's split — left as-is per your call to leave
|
||||
# ffmpeg/filtered/subscribed alone for now. NOTE: cache filenames below
|
||||
# match what _generate_m3u_proxied_filtered_content() actually writes
|
||||
# ("*_proxied_filtered.m3u"), fixing a pre-existing mismatch where
|
||||
# this route checked "*_proxied_filtered.m3u" - a file the service
|
||||
# never wrote - so the cache never hit.
|
||||
@@ -212,13 +230,11 @@ def setup_m3u_routes(app, manager, service):
|
||||
)
|
||||
|
||||
# ── Subscribed-channel playlists ──────────────────────────────────────
|
||||
# get_m3u_subscribed / get_m3u_subscribed_proxied still build their own
|
||||
# M3U content directly (they were never moved into service.py) - only the
|
||||
# boilerplate around them is shared via the same helpers used everywhere
|
||||
# else. The two bodies used to be ~90% duplicated hand-written copies of
|
||||
# each other, differing only in stream URL path, whether DRM directives
|
||||
# are looked up per-channel vs a fixed KODIPROP line, and whether the
|
||||
# result gets cached — now unified into one function with a `proxied` flag.
|
||||
# Unchanged by this turn's split — left as-is per your call to leave
|
||||
# ffmpeg/filtered/subscribed alone for now. get_m3u_subscribed /
|
||||
# get_m3u_subscribed_proxied still build their own M3U content directly
|
||||
# (they were never moved into service.py) - only the boilerplate around
|
||||
# them is shared via the same helpers used everywhere else.
|
||||
|
||||
def _generate_m3u_subscribed(proxied: bool = False):
|
||||
if proxied and not service.media_proxy_url:
|
||||
|
||||
+120
-67
@@ -872,21 +872,35 @@ class UltimateService:
|
||||
continue
|
||||
yield provider_name, provider_label, channels
|
||||
|
||||
def _generate_m3u_proxied_fast(self, providers=None):
|
||||
def _generate_m3u_plain_content(self, providers=None, save_to_cache=True, cache_filename=None):
|
||||
"""
|
||||
Fast generation of decrypted M3U content for specified providers.
|
||||
Includes ALL channels with decrypted stream URLs.
|
||||
No DRM filtering, no caching - maximum speed.
|
||||
Generate M3U content for the default, server-side-decrypt stream
|
||||
route — bare "stream/index.mpd", no query string at all (client_drm
|
||||
defaults to false at the route level in channels.py, so this is
|
||||
equivalent to the old explicit "?client_drm=false", just without
|
||||
writing out a param that only restates the default).
|
||||
|
||||
This is what "/api/m3u" and "/api/providers/<provider>/m3u" serve.
|
||||
It replaces the old uncached "/m3u/proxied" fast-path — same DRM
|
||||
handling (drm_directives="", nothing embedded, server does the
|
||||
decrypting) — but adds caching back, because nothing on this path
|
||||
is request-time-volatile: there's no per-channel DRM/key lookup
|
||||
happening at generation time, unlike the dynamic-DRM "clientdrm"
|
||||
variant below.
|
||||
|
||||
Args:
|
||||
providers: List of provider names, or None for all providers
|
||||
save_to_cache: Whether to save to cache
|
||||
cache_filename: Cache filename to use
|
||||
|
||||
Returns:
|
||||
M3U content as string
|
||||
"""
|
||||
# Check if media proxy is configured
|
||||
# Check if media proxy is configured — the bare stream route still
|
||||
# depends on it at playback time, so fail the playlist build fast
|
||||
# rather than handing out URLs that won't resolve.
|
||||
if not self.media_proxy_url:
|
||||
logger.error("Cannot generate proxied M3U: MEDIA_PROXY_URL not set")
|
||||
logger.error("Cannot generate M3U: MEDIA_PROXY_URL not set")
|
||||
response.status = 503
|
||||
response.content_type = "application/json"
|
||||
return json.dumps(
|
||||
@@ -902,25 +916,25 @@ class UltimateService:
|
||||
# Determine which providers to process
|
||||
if providers is None:
|
||||
providers_to_process = self.manager.list_providers()
|
||||
cache_filename = cache_filename or "playlist.m3u"
|
||||
else:
|
||||
providers_to_process = (
|
||||
[providers] if isinstance(providers, str) else providers
|
||||
)
|
||||
cache_filename = cache_filename or f"{providers_to_process[0]}.m3u"
|
||||
|
||||
channels_included = 0
|
||||
|
||||
for provider_name, provider_label, channels in self._iter_m3u_provider_channels(providers_to_process):
|
||||
try:
|
||||
# Process each channel - no DRM checks (all channels are
|
||||
# already routed through the media proxy at playback time)
|
||||
for channel in channels:
|
||||
m3u_content += self._generate_m3u_entry(
|
||||
base_url, provider_name, channel,
|
||||
# client_drm=false (explicit, though it's the default).
|
||||
# No KODIPROP line needed — the client doesn't use
|
||||
# inputstream.adaptive at all when the server does the
|
||||
# decrypting.
|
||||
stream_path="stream/index.mpd?client_drm=false",
|
||||
# Bare path — no ?client_drm=false, since that's the
|
||||
# route's own default. No KODIPROP line needed either
|
||||
# (drm_directives="") — the client never uses
|
||||
# inputstream.adaptive when the server decrypts.
|
||||
stream_path="stream/index.mpd",
|
||||
provider_label=provider_label,
|
||||
drm_directives="",
|
||||
)
|
||||
@@ -932,21 +946,15 @@ class UltimateService:
|
||||
)
|
||||
continue
|
||||
|
||||
logger.info(f"Fast decrypted M3U: included {channels_included} channels")
|
||||
logger.info(f"Plain M3U: included {channels_included} channels")
|
||||
|
||||
# Set appropriate headers
|
||||
response.content_type = "audio/x-mpegurl; charset=utf-8"
|
||||
|
||||
if providers and isinstance(providers, str):
|
||||
# Single provider
|
||||
response.headers["Content-Disposition"] = (
|
||||
f'attachment; filename="{providers}_proxied_playlist.m3u8"'
|
||||
)
|
||||
else:
|
||||
# All providers
|
||||
response.headers["Content-Disposition"] = (
|
||||
'attachment; filename="playlist_proxied.m3u8"'
|
||||
)
|
||||
# Save to cache if requested — safe here (see docstring: nothing on
|
||||
# this path is request-time-volatile).
|
||||
if save_to_cache and cache_filename:
|
||||
if self.vfs.write_text(cache_filename, m3u_content):
|
||||
logger.info(f"M3U playlist cached to {cache_filename}")
|
||||
else:
|
||||
logger.warning(f"Failed to cache M3U playlist to {cache_filename}")
|
||||
|
||||
return m3u_content
|
||||
|
||||
@@ -1175,7 +1183,14 @@ class UltimateService:
|
||||
self, providers=None, save_to_cache=True, cache_filename=None, no_proxy=False
|
||||
):
|
||||
"""
|
||||
Internal method to generate M3U content for specified providers.
|
||||
Internal method to generate M3U content for specified providers, on
|
||||
the client-side-decrypt route (client_drm=true — dynamic per-channel
|
||||
DRM/ClearKey lookup, key material embedded as KODIPROP directives).
|
||||
|
||||
This backs the "clientdrm" playlists (generate_m3u_clientdrm_all/
|
||||
_provider, always save_to_cache=False — see those wrappers for why)
|
||||
and the pre-existing "/m3u/noproxy" playlists, which combine
|
||||
client-side decrypt with a forced non-proxied stream route.
|
||||
|
||||
Args:
|
||||
providers: List of provider names, or None for all providers
|
||||
@@ -1435,17 +1450,85 @@ class UltimateService:
|
||||
"""Public wrapper for EPG ID lookup."""
|
||||
return self._get_epg_id(channel_id)
|
||||
|
||||
def generate_m3u_all(self, save_to_cache: bool = False, no_proxy: bool = False) -> str:
|
||||
"""Public wrapper for full M3U generation."""
|
||||
return self._generate_m3u_all(save_to_cache=save_to_cache, no_proxy=no_proxy)
|
||||
def generate_m3u_plain_all(self, save_to_cache: bool = False) -> str:
|
||||
"""
|
||||
Public wrapper: server-side-decrypt M3U for all providers (bare
|
||||
stream URLs, no query string). This is "/api/m3u" — cached by
|
||||
default when called from the route (save_to_cache=True there).
|
||||
"""
|
||||
logger.info("Generating M3U playlist for all providers")
|
||||
m3u_content = self._generate_m3u_plain_content(providers=None, save_to_cache=save_to_cache)
|
||||
|
||||
def generate_m3u_provider(self, provider: str, save_to_cache: bool = False, no_proxy: bool = False) -> str:
|
||||
"""Public wrapper for per-provider M3U generation."""
|
||||
return self._generate_m3u_provider(provider, save_to_cache=save_to_cache, no_proxy=no_proxy)
|
||||
response.content_type = "audio/x-mpegurl; charset=utf-8"
|
||||
response.headers["Content-Disposition"] = 'attachment; filename="playlist.m3u8"'
|
||||
|
||||
def generate_m3u_proxied_fast(self, providers=None) -> str:
|
||||
"""Public wrapper for fast decrypted M3U generation."""
|
||||
return self._generate_m3u_proxied_fast(providers)
|
||||
return m3u_content
|
||||
|
||||
def generate_m3u_plain_provider(self, provider: str, save_to_cache: bool = False) -> str:
|
||||
"""Public wrapper: server-side-decrypt M3U for a specific provider."""
|
||||
logger.info(f"Generating M3U playlist for provider '{provider}'")
|
||||
m3u_content = self._generate_m3u_plain_content(providers=provider, save_to_cache=save_to_cache)
|
||||
|
||||
response.content_type = "audio/x-mpegurl; charset=utf-8"
|
||||
response.headers["Content-Disposition"] = f'attachment; filename="{provider}_playlist.m3u8"'
|
||||
|
||||
return m3u_content
|
||||
|
||||
def generate_m3u_clientdrm_all(self) -> str:
|
||||
"""
|
||||
Public wrapper: client-side-decrypt M3U for all providers
|
||||
(dynamic per-channel ClearKey lookup, key/kid pairs embedded as
|
||||
KODIPROP directives). Deliberately UNCACHED, unlike the plain
|
||||
playlist above — upstream keys can rotate, and a cached playlist
|
||||
would silently serve a stale key until someone force-regenerates
|
||||
it. Generating fresh per request avoids that failure mode
|
||||
entirely; add a short TTL later if per-request DRM-config lookups
|
||||
turn out to be too frequent/expensive in practice.
|
||||
"""
|
||||
logger.info("Generating clientdrm M3U playlist for all providers")
|
||||
m3u_content = self._generate_m3u_content(providers=None, save_to_cache=False)
|
||||
|
||||
response.content_type = "audio/x-mpegurl; charset=utf-8"
|
||||
response.headers["Content-Disposition"] = 'attachment; filename="playlist_clientdrm.m3u8"'
|
||||
|
||||
return m3u_content
|
||||
|
||||
def generate_m3u_clientdrm_provider(self, provider: str) -> str:
|
||||
"""Public wrapper: client-side-decrypt M3U for a specific provider. Uncached — see generate_m3u_clientdrm_all."""
|
||||
logger.info(f"Generating clientdrm M3U playlist for provider '{provider}'")
|
||||
m3u_content = self._generate_m3u_content(providers=provider, save_to_cache=False)
|
||||
|
||||
response.content_type = "audio/x-mpegurl; charset=utf-8"
|
||||
response.headers["Content-Disposition"] = f'attachment; filename="{provider}_playlist_clientdrm.m3u8"'
|
||||
|
||||
return m3u_content
|
||||
|
||||
def generate_m3u_noproxy_all(self, save_to_cache: bool = False) -> str:
|
||||
"""
|
||||
Public wrapper: client-side-decrypt M3U for all providers, forced
|
||||
onto the non-proxied stream route (client_drm=true&no_proxy=true).
|
||||
Backs "/api/m3u/noproxy" — unrelated to this turn's plain/clientdrm
|
||||
split, kept exactly as it behaved before (cached, via
|
||||
_generate_m3u_content). Renamed from the old generate_m3u_all only
|
||||
because that name now belongs to the plain-playlist wrapper above.
|
||||
"""
|
||||
logger.info("Generating no-proxy M3U playlist for all providers")
|
||||
m3u_content = self._generate_m3u_content(providers=None, save_to_cache=save_to_cache, no_proxy=True)
|
||||
|
||||
response.content_type = "audio/x-mpegurl; charset=utf-8"
|
||||
response.headers["Content-Disposition"] = 'attachment; filename="playlist_noproxy.m3u8"'
|
||||
|
||||
return m3u_content
|
||||
|
||||
def generate_m3u_noproxy_provider(self, provider: str, save_to_cache: bool = False) -> str:
|
||||
"""Public wrapper: no-proxy M3U for a specific provider. See generate_m3u_noproxy_all."""
|
||||
logger.info(f"Generating no-proxy M3U playlist for provider '{provider}'")
|
||||
m3u_content = self._generate_m3u_content(providers=provider, save_to_cache=save_to_cache, no_proxy=True)
|
||||
|
||||
response.content_type = "audio/x-mpegurl; charset=utf-8"
|
||||
response.headers["Content-Disposition"] = f'attachment; filename="{provider}_playlist_noproxy.m3u8"'
|
||||
|
||||
return m3u_content
|
||||
|
||||
def generate_m3u_proxied_ffmpeg_fast(self, providers=None) -> str:
|
||||
"""Public wrapper for fast ffmpeg-decrypted M3U generation."""
|
||||
@@ -1511,36 +1594,6 @@ class UltimateService:
|
||||
logger.warning(f"Unsupported headers type: {type(req_headers)}")
|
||||
return str(req_headers)
|
||||
|
||||
def _generate_m3u_all(self, save_to_cache: bool = False, no_proxy: bool = False) -> str:
|
||||
"""Internal method to generate M3U for all providers."""
|
||||
logger.info(f"Generating {'no-proxy ' if no_proxy else ''}M3U playlist for all providers")
|
||||
m3u_content = self._generate_m3u_content(
|
||||
providers=None, save_to_cache=save_to_cache, no_proxy=no_proxy
|
||||
)
|
||||
|
||||
# Set appropriate headers for M3U
|
||||
response.content_type = "audio/x-mpegurl; charset=utf-8"
|
||||
filename = "playlist_noproxy.m3u8" if no_proxy else "playlist.m3u8"
|
||||
response.headers["Content-Disposition"] = f'attachment; filename="{filename}"'
|
||||
|
||||
return m3u_content
|
||||
|
||||
def _generate_m3u_provider(self, provider: str, save_to_cache: bool = False, no_proxy: bool = False) -> str:
|
||||
"""Internal method to generate M3U for a specific provider."""
|
||||
logger.info(f"Generating {'no-proxy ' if no_proxy else ''}M3U playlist for provider '{provider}'")
|
||||
m3u_content = self._generate_m3u_content(
|
||||
providers=provider, save_to_cache=save_to_cache, no_proxy=no_proxy
|
||||
)
|
||||
|
||||
# Set appropriate headers for M3U
|
||||
response.content_type = "audio/x-mpegurl; charset=utf-8"
|
||||
filename = f"{provider}_playlist_noproxy.m3u8" if no_proxy else f"{provider}_playlist.m3u8"
|
||||
response.headers["Content-Disposition"] = (
|
||||
f'attachment; filename="{filename}"'
|
||||
)
|
||||
|
||||
return m3u_content
|
||||
|
||||
@staticmethod
|
||||
def get_settings_manager():
|
||||
"""Simple helper to get SettingsManager"""
|
||||
|
||||
Reference in New Issue
Block a user