mirror of
https://github.com/unshackle-dl/unshackle.git
synced 2026-03-12 17:39:01 +00:00
feat(audio): codec lists and split muxing
This commit is contained in:
@@ -227,6 +227,7 @@ def _perform_download(
|
||||
range_=params.get("range", ["SDR"]),
|
||||
channels=params.get("channels"),
|
||||
no_atmos=params.get("no_atmos", False),
|
||||
split_audio=params.get("split_audio"),
|
||||
wanted=params.get("wanted", []),
|
||||
latest_episode=params.get("latest_episode", False),
|
||||
lang=params.get("lang", ["orig"]),
|
||||
|
||||
@@ -748,9 +748,17 @@ def validate_download_parameters(data: Dict[str, Any]) -> Optional[str]:
|
||||
return f"Invalid vcodec: {data['vcodec']}. Must be one of: {', '.join(valid_vcodecs)}"
|
||||
|
||||
if "acodec" in data and data["acodec"]:
|
||||
valid_acodecs = ["AAC", "AC3", "EAC3", "OPUS", "FLAC", "ALAC", "VORBIS", "DTS"]
|
||||
if data["acodec"].upper() not in valid_acodecs:
|
||||
return f"Invalid acodec: {data['acodec']}. Must be one of: {', '.join(valid_acodecs)}"
|
||||
valid_acodecs = ["AAC", "AC3", "EC3", "EAC3", "DD", "DD+", "AC4", "OPUS", "FLAC", "ALAC", "VORBIS", "OGG", "DTS"]
|
||||
if isinstance(data["acodec"], str):
|
||||
acodec_values = [v.strip() for v in data["acodec"].split(",") if v.strip()]
|
||||
elif isinstance(data["acodec"], list):
|
||||
acodec_values = [str(v).strip() for v in data["acodec"] if str(v).strip()]
|
||||
else:
|
||||
return "acodec must be a string or list"
|
||||
|
||||
invalid = [value for value in acodec_values if value.upper() not in valid_acodecs]
|
||||
if invalid:
|
||||
return f"Invalid acodec: {', '.join(invalid)}. Must be one of: {', '.join(valid_acodecs)}"
|
||||
|
||||
if "sub_format" in data and data["sub_format"]:
|
||||
valid_sub_formats = ["SRT", "VTT", "ASS", "SSA"]
|
||||
|
||||
@@ -416,7 +416,7 @@ async def download(request: web.Request) -> web.Response:
|
||||
description: Video codec to download (e.g., H264, H265, VP9, AV1) (default - None)
|
||||
acodec:
|
||||
type: string
|
||||
description: Audio codec to download (e.g., AAC, AC3, EAC3) (default - None)
|
||||
description: Audio codec(s) to download (e.g., AAC or AAC,EC3) (default - None)
|
||||
vbitrate:
|
||||
type: integer
|
||||
description: Video bitrate in kbps (default - None)
|
||||
|
||||
@@ -5,6 +5,8 @@ import click
|
||||
from click.shell_completion import CompletionItem
|
||||
from pywidevine.cdm import Cdm as WidevineCdm
|
||||
|
||||
from unshackle.core.tracks.audio import Audio
|
||||
|
||||
|
||||
class VideoCodecChoice(click.Choice):
|
||||
"""
|
||||
@@ -241,6 +243,52 @@ class QualityList(click.ParamType):
|
||||
return sorted(resolutions, reverse=True)
|
||||
|
||||
|
||||
class AudioCodecList(click.ParamType):
|
||||
"""Parses comma-separated audio codecs like 'AAC,EC3'."""
|
||||
|
||||
name = "audio_codec_list"
|
||||
|
||||
def __init__(self, codec_enum):
|
||||
self.codec_enum = codec_enum
|
||||
self._name_to_codec: dict[str, Audio.Codec] = {}
|
||||
for codec in codec_enum:
|
||||
self._name_to_codec[codec.name.lower()] = codec
|
||||
self._name_to_codec[codec.value.lower()] = codec
|
||||
|
||||
aliases = {
|
||||
"eac3": "EC3",
|
||||
"ddp": "EC3",
|
||||
"vorbis": "OGG",
|
||||
}
|
||||
for alias, target in aliases.items():
|
||||
if target in codec_enum.__members__:
|
||||
self._name_to_codec[alias] = codec_enum[target]
|
||||
|
||||
def convert(self, value: Any, param: Optional[click.Parameter] = None, ctx: Optional[click.Context] = None) -> list:
|
||||
if not value:
|
||||
return []
|
||||
if isinstance(value, self.codec_enum):
|
||||
return [value]
|
||||
if isinstance(value, list):
|
||||
if all(isinstance(v, self.codec_enum) for v in value):
|
||||
return value
|
||||
values = [str(v).strip() for v in value]
|
||||
else:
|
||||
values = [v.strip() for v in str(value).split(",")]
|
||||
|
||||
codecs = []
|
||||
for val in values:
|
||||
if not val:
|
||||
continue
|
||||
key = val.lower()
|
||||
if key in self._name_to_codec:
|
||||
codecs.append(self._name_to_codec[key])
|
||||
else:
|
||||
valid = sorted(set(self._name_to_codec.keys()))
|
||||
self.fail(f"'{val}' is not valid. Choices: {', '.join(valid)}", param, ctx)
|
||||
return list(dict.fromkeys(codecs)) # Remove duplicates, preserve order
|
||||
|
||||
|
||||
class MultipleChoice(click.Choice):
|
||||
"""
|
||||
The multiple choice type allows multiple values to be checked against
|
||||
@@ -288,5 +336,6 @@ class MultipleChoice(click.Choice):
|
||||
SEASON_RANGE = SeasonRange()
|
||||
LANGUAGE_RANGE = LanguageRange()
|
||||
QUALITY_LIST = QualityList()
|
||||
AUDIO_CODEC_LIST = AudioCodecList(Audio.Codec)
|
||||
|
||||
# VIDEO_CODEC_CHOICE will be created dynamically when imported
|
||||
|
||||
Reference in New Issue
Block a user