Added HLS Support
This commit is contained in:
parent
6509198610
commit
fa776a590a
@ -286,39 +286,69 @@ class KNPY(Service):
|
|||||||
if response_json and response_json.get("errorSubcode") == "playRegionRestricted":
|
if response_json and response_json.get("errorSubcode") == "playRegionRestricted":
|
||||||
self.log.error("Kanopy reports: This video is not available in your country.")
|
self.log.error("Kanopy reports: This video is not available in your country.")
|
||||||
raise PermissionError(
|
raise PermissionError(
|
||||||
"Playback blocked by region restriction. Try connecting through a supported country or verify your library’s access region."
|
"Playback blocked by region restriction. Try connecting through a supported country or verify your library's access region."
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
self.log.error(f"Access forbidden (HTTP 403). Response: {response_json}")
|
self.log.error(f"Access forbidden (HTTP 403). Response: {response_json}")
|
||||||
raise PermissionError("Kanopy denied access to this video. It may require a different library membership or authentication.")
|
raise PermissionError("Kanopy denied access to this video. It may require a different library membership or authentication.")
|
||||||
|
|
||||||
# Raise for any other HTTP errors
|
|
||||||
r.raise_for_status()
|
r.raise_for_status()
|
||||||
play_data = response_json or r.json()
|
play_data = response_json or r.json()
|
||||||
|
|
||||||
manifest_url = None
|
manifest_url = None
|
||||||
|
manifest_type = None
|
||||||
|
drm_info = {}
|
||||||
|
|
||||||
|
# Iterate through manifests: prefer DASH, fallback to HLS
|
||||||
for manifest in play_data.get("manifests", []):
|
for manifest in play_data.get("manifests", []):
|
||||||
if manifest["manifestType"] == "dash":
|
manifest_type_raw = manifest["manifestType"]
|
||||||
url = manifest["url"]
|
url = manifest["url"].strip() # Strip whitespace from URLs
|
||||||
manifest_url = f"https://kanopy.com{url}" if url.startswith("/") else url
|
|
||||||
drm_type = manifest.get("drmType")
|
# Construct full URL if relative
|
||||||
|
if url.startswith("/"):
|
||||||
|
url = f"https://kanopy.com{url}"
|
||||||
|
|
||||||
|
drm_type = manifest.get("drmType")
|
||||||
|
|
||||||
|
if manifest_type_raw == "dash":
|
||||||
|
manifest_url = url
|
||||||
|
manifest_type = "dash"
|
||||||
|
|
||||||
if drm_type == "kanopyDrm":
|
if drm_type == "kanopyDrm":
|
||||||
play_id = play_data.get("playId")
|
play_id = play_data.get("playId")
|
||||||
self.widevine_license_url = self.config["endpoints"]["widevine_license"].format(license_id=f"{play_id}-0")
|
self.widevine_license_url = self.config["endpoints"]["widevine_license"].format(
|
||||||
|
license_id=f"{play_id}-0"
|
||||||
|
)
|
||||||
elif drm_type == "studioDrm":
|
elif drm_type == "studioDrm":
|
||||||
license_id = manifest.get("drmLicenseID", f"{play_data.get('playId')}-1")
|
license_id = manifest.get("drmLicenseID", f"{play_data.get('playId')}-1")
|
||||||
self.widevine_license_url = self.config["endpoints"]["widevine_license"].format(license_id=license_id)
|
self.widevine_license_url = self.config["endpoints"]["widevine_license"].format(
|
||||||
|
license_id=license_id
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
self.log.warning(f"Unknown drmType: {drm_type}")
|
self.log.warning(f"Unknown DASH drmType: {drm_type}")
|
||||||
self.widevine_license_url = None
|
self.widevine_license_url = None
|
||||||
break
|
break # Prefer DASH, exit loop
|
||||||
|
|
||||||
|
elif manifest_type_raw == "hls" and not manifest_url:
|
||||||
|
# Store HLS as fallback if DASH not found
|
||||||
|
manifest_url = url
|
||||||
|
manifest_type = "hls"
|
||||||
|
|
||||||
|
if drm_type == "fairplay":
|
||||||
|
self.log.warning("HLS with FairPlay DRM detected - not currently supported by this service")
|
||||||
|
self.widevine_license_url = None
|
||||||
|
drm_info["fairplay"] = True
|
||||||
|
else:
|
||||||
|
# HLS with no DRM or unsupported DRM type
|
||||||
|
self.widevine_license_url = None
|
||||||
|
drm_info["clear"] = True
|
||||||
|
|
||||||
if not manifest_url:
|
if not manifest_url:
|
||||||
raise ValueError("Could not find a DASH manifest for this title.")
|
raise ValueError("Could not find a DASH or HLS manifest for this title.")
|
||||||
if not self.widevine_license_url:
|
if manifest_type == "dash" and not self.widevine_license_url:
|
||||||
raise ValueError("Could not construct Widevine license URL.")
|
raise ValueError("Could not construct Widevine license URL for DASH manifest.")
|
||||||
|
|
||||||
self.log.info(f"Fetching DASH manifest from: {manifest_url}")
|
self.log.info(f"Fetching {manifest_type.upper()} manifest from: {manifest_url}")
|
||||||
r = self.session.get(manifest_url)
|
r = self.session.get(manifest_url)
|
||||||
r.raise_for_status()
|
r.raise_for_status()
|
||||||
|
|
||||||
@ -331,14 +361,35 @@ class KNPY(Service):
|
|||||||
"Connection": "keep-alive",
|
"Connection": "keep-alive",
|
||||||
})
|
})
|
||||||
|
|
||||||
tracks = DASH.from_text(r.text, url=manifest_url).to_tracks(language=title.language)
|
# Parse manifest based on type
|
||||||
|
if manifest_type == "dash":
|
||||||
|
tracks = DASH.from_text(r.text, url=manifest_url).to_tracks(language=title.language)
|
||||||
|
elif manifest_type == "hls":
|
||||||
|
# Try to import HLS parser from unshackle
|
||||||
|
try:
|
||||||
|
from unshackle.core.manifests import HLS
|
||||||
|
tracks = HLS.from_text(r.text, url=manifest_url).to_tracks(language=title.language)
|
||||||
|
self.log.info("Successfully parsed HLS manifest")
|
||||||
|
except ImportError:
|
||||||
|
self.log.error(
|
||||||
|
"HLS manifest parser not available in unshackle.core.manifests. "
|
||||||
|
"Ensure your unshackle installation supports HLS parsing."
|
||||||
|
)
|
||||||
|
raise
|
||||||
|
except Exception as e:
|
||||||
|
self.log.error(f"Failed to parse HLS manifest: {e}")
|
||||||
|
raise
|
||||||
|
else:
|
||||||
|
raise ValueError(f"Unsupported manifest type: {manifest_type}")
|
||||||
|
|
||||||
|
# Add subtitles/captions from play_data (works for both DASH and HLS)
|
||||||
for caption_data in play_data.get("captions", []):
|
for caption_data in play_data.get("captions", []):
|
||||||
lang = caption_data.get("language", "en")
|
lang = caption_data.get("language", "en")
|
||||||
for file_info in caption_data.get("files", []):
|
for file_info in caption_data.get("files", []):
|
||||||
if file_info.get("type") == "webvtt":
|
if file_info.get("type") == "webvtt":
|
||||||
tracks.add(Subtitle(
|
tracks.add(Subtitle(
|
||||||
id_=f"caption-{lang}",
|
id_=f"caption-{lang}",
|
||||||
url=file_info["url"],
|
url=file_info["url"].strip(),
|
||||||
codec=Subtitle.Codec.WebVTT,
|
codec=Subtitle.Codec.WebVTT,
|
||||||
language=Language.get(lang)
|
language=Language.get(lang)
|
||||||
))
|
))
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user