mirror of
https://github.com/unshackle-dl/unshackle.git
synced 2026-03-12 01:19:02 +00:00
Compare commits
2 Commits
9952758b38
...
b2686ca2b1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b2686ca2b1 | ||
|
|
abc3b4f1a4 |
@@ -153,6 +153,13 @@ class dl:
|
|||||||
default=[],
|
default=[],
|
||||||
help="Language wanted for Video, you would use this if the video language doesn't match the audio.",
|
help="Language wanted for Video, you would use this if the video language doesn't match the audio.",
|
||||||
)
|
)
|
||||||
|
@click.option(
|
||||||
|
"-al",
|
||||||
|
"--a-lang",
|
||||||
|
type=LANGUAGE_RANGE,
|
||||||
|
default=[],
|
||||||
|
help="Language wanted for Audio, overrides -l/--lang for audio tracks.",
|
||||||
|
)
|
||||||
@click.option("-sl", "--s-lang", type=LANGUAGE_RANGE, default=["all"], help="Language wanted for Subtitles.")
|
@click.option("-sl", "--s-lang", type=LANGUAGE_RANGE, default=["all"], help="Language wanted for Subtitles.")
|
||||||
@click.option("-fs", "--forced-subs", is_flag=True, default=False, help="Include forced subtitle tracks.")
|
@click.option("-fs", "--forced-subs", is_flag=True, default=False, help="Include forced subtitle tracks.")
|
||||||
@click.option(
|
@click.option(
|
||||||
@@ -413,6 +420,7 @@ class dl:
|
|||||||
wanted: list[str],
|
wanted: list[str],
|
||||||
lang: list[str],
|
lang: list[str],
|
||||||
v_lang: list[str],
|
v_lang: list[str],
|
||||||
|
a_lang: list[str],
|
||||||
s_lang: list[str],
|
s_lang: list[str],
|
||||||
forced_subs: bool,
|
forced_subs: bool,
|
||||||
sub_format: Optional[Subtitle.Codec],
|
sub_format: Optional[Subtitle.Codec],
|
||||||
@@ -588,8 +596,9 @@ class dl:
|
|||||||
if language not in processed_video_sort_lang:
|
if language not in processed_video_sort_lang:
|
||||||
processed_video_sort_lang.append(language)
|
processed_video_sort_lang.append(language)
|
||||||
|
|
||||||
|
audio_sort_lang = a_lang or lang
|
||||||
processed_audio_sort_lang = []
|
processed_audio_sort_lang = []
|
||||||
for language in lang:
|
for language in audio_sort_lang:
|
||||||
if language == "orig":
|
if language == "orig":
|
||||||
if title.language:
|
if title.language:
|
||||||
orig_lang = str(title.language) if hasattr(title.language, "__str__") else title.language
|
orig_lang = str(title.language) if hasattr(title.language, "__str__") else title.language
|
||||||
@@ -753,9 +762,10 @@ class dl:
|
|||||||
if not title.tracks.audio:
|
if not title.tracks.audio:
|
||||||
self.log.error(f"There's no {abitrate}kbps Audio Track...")
|
self.log.error(f"There's no {abitrate}kbps Audio Track...")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
if lang:
|
audio_languages = a_lang or lang
|
||||||
|
if audio_languages:
|
||||||
processed_lang = []
|
processed_lang = []
|
||||||
for language in lang:
|
for language in audio_languages:
|
||||||
if language == "orig":
|
if language == "orig":
|
||||||
if title.language:
|
if title.language:
|
||||||
orig_lang = (
|
orig_lang = (
|
||||||
|
|||||||
@@ -4,8 +4,9 @@ from uuid import UUID
|
|||||||
|
|
||||||
|
|
||||||
class Vault(metaclass=ABCMeta):
|
class Vault(metaclass=ABCMeta):
|
||||||
def __init__(self, name: str):
|
def __init__(self, name: str, no_push: bool = False):
|
||||||
self.name = name
|
self.name = name
|
||||||
|
self.no_push = no_push
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return f"{self.name} {type(self).__name__}"
|
return f"{self.name} {type(self).__name__}"
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ class Vaults:
|
|||||||
"""Add a KID:KEY to all Vaults, optionally with an exclusion."""
|
"""Add a KID:KEY to all Vaults, optionally with an exclusion."""
|
||||||
success = 0
|
success = 0
|
||||||
for vault in self.vaults:
|
for vault in self.vaults:
|
||||||
if vault != excluding:
|
if vault != excluding and not vault.no_push:
|
||||||
try:
|
try:
|
||||||
success += vault.add_key(self.service, kid, key)
|
success += vault.add_key(self.service, kid, key)
|
||||||
except (PermissionError, NotImplementedError):
|
except (PermissionError, NotImplementedError):
|
||||||
@@ -68,13 +68,15 @@ class Vaults:
|
|||||||
"""
|
"""
|
||||||
Add multiple KID:KEYs to all Vaults. Duplicate Content Keys are skipped.
|
Add multiple KID:KEYs to all Vaults. Duplicate Content Keys are skipped.
|
||||||
PermissionErrors when the user cannot create Tables are absorbed and ignored.
|
PermissionErrors when the user cannot create Tables are absorbed and ignored.
|
||||||
|
Vaults with no_push=True are skipped.
|
||||||
"""
|
"""
|
||||||
success = 0
|
success = 0
|
||||||
for vault in self.vaults:
|
for vault in self.vaults:
|
||||||
try:
|
if not vault.no_push:
|
||||||
success += bool(vault.add_keys(self.service, kid_keys))
|
try:
|
||||||
except (PermissionError, NotImplementedError):
|
success += bool(vault.add_keys(self.service, kid_keys))
|
||||||
pass
|
except (PermissionError, NotImplementedError):
|
||||||
|
pass
|
||||||
return success
|
return success
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -101,6 +101,8 @@ remote_cdm:
|
|||||||
secret: secret_key
|
secret: secret_key
|
||||||
|
|
||||||
# Key Vaults store your obtained Content Encryption Keys (CEKs)
|
# Key Vaults store your obtained Content Encryption Keys (CEKs)
|
||||||
|
# Use 'no_push: true' to prevent a vault from receiving pushed keys
|
||||||
|
# while still allowing it to provide keys when requested
|
||||||
key_vaults:
|
key_vaults:
|
||||||
- type: SQLite
|
- type: SQLite
|
||||||
name: Local
|
name: Local
|
||||||
@@ -110,6 +112,7 @@ key_vaults:
|
|||||||
# name: "Remote Vault"
|
# name: "Remote Vault"
|
||||||
# uri: "https://key-vault.example.com"
|
# uri: "https://key-vault.example.com"
|
||||||
# token: "secret_token"
|
# token: "secret_token"
|
||||||
|
# no_push: true # This vault will only provide keys, not receive them
|
||||||
# - type: MySQL
|
# - type: MySQL
|
||||||
# name: "MySQL Vault"
|
# name: "MySQL Vault"
|
||||||
# host: "127.0.0.1"
|
# host: "127.0.0.1"
|
||||||
@@ -117,6 +120,7 @@ key_vaults:
|
|||||||
# database: vault
|
# database: vault
|
||||||
# username: user
|
# username: user
|
||||||
# password: pass
|
# password: pass
|
||||||
|
# no_push: false # Default behavior - vault both provides and receives keys
|
||||||
|
|
||||||
# Choose what software to use to download data
|
# Choose what software to use to download data
|
||||||
downloader: aria2c
|
downloader: aria2c
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ from unshackle.core.vault import Vault
|
|||||||
class API(Vault):
|
class API(Vault):
|
||||||
"""Key Vault using a simple RESTful HTTP API call."""
|
"""Key Vault using a simple RESTful HTTP API call."""
|
||||||
|
|
||||||
def __init__(self, name: str, uri: str, token: str):
|
def __init__(self, name: str, uri: str, token: str, no_push: bool = False):
|
||||||
super().__init__(name)
|
super().__init__(name, no_push)
|
||||||
self.uri = uri.rstrip("/")
|
self.uri = uri.rstrip("/")
|
||||||
self.session = Session()
|
self.session = Session()
|
||||||
self.session.headers.update({"User-Agent": f"unshackle v{__version__}"})
|
self.session.headers.update({"User-Agent": f"unshackle v{__version__}"})
|
||||||
|
|||||||
@@ -18,7 +18,15 @@ class InsertResult(Enum):
|
|||||||
class HTTP(Vault):
|
class HTTP(Vault):
|
||||||
"""Key Vault using HTTP API with support for both query parameters and JSON payloads."""
|
"""Key Vault using HTTP API with support for both query parameters and JSON payloads."""
|
||||||
|
|
||||||
def __init__(self, name: str, host: str, password: str, username: Optional[str] = None, api_mode: str = "query"):
|
def __init__(
|
||||||
|
self,
|
||||||
|
name: str,
|
||||||
|
host: str,
|
||||||
|
password: str,
|
||||||
|
username: Optional[str] = None,
|
||||||
|
api_mode: str = "query",
|
||||||
|
no_push: bool = False,
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Initialize HTTP Vault.
|
Initialize HTTP Vault.
|
||||||
|
|
||||||
@@ -28,8 +36,9 @@ class HTTP(Vault):
|
|||||||
password: Password for query mode or API token for json mode
|
password: Password for query mode or API token for json mode
|
||||||
username: Username (required for query mode, ignored for json mode)
|
username: Username (required for query mode, ignored for json mode)
|
||||||
api_mode: "query" for query parameters or "json" for JSON API
|
api_mode: "query" for query parameters or "json" for JSON API
|
||||||
|
no_push: If True, this vault will not receive pushed keys
|
||||||
"""
|
"""
|
||||||
super().__init__(name)
|
super().__init__(name, no_push)
|
||||||
self.url = host
|
self.url = host
|
||||||
self.password = password
|
self.password = password
|
||||||
self.username = username
|
self.username = username
|
||||||
|
|||||||
@@ -12,12 +12,12 @@ from unshackle.core.vault import Vault
|
|||||||
class MySQL(Vault):
|
class MySQL(Vault):
|
||||||
"""Key Vault using a remotely-accessed mysql database connection."""
|
"""Key Vault using a remotely-accessed mysql database connection."""
|
||||||
|
|
||||||
def __init__(self, name: str, host: str, database: str, username: str, **kwargs):
|
def __init__(self, name: str, host: str, database: str, username: str, no_push: bool = False, **kwargs):
|
||||||
"""
|
"""
|
||||||
All extra arguments provided via **kwargs will be sent to pymysql.connect.
|
All extra arguments provided via **kwargs will be sent to pymysql.connect.
|
||||||
This can be used to provide more specific connection information.
|
This can be used to provide more specific connection information.
|
||||||
"""
|
"""
|
||||||
super().__init__(name)
|
super().__init__(name, no_push)
|
||||||
self.slug = f"{host}:{database}:{username}"
|
self.slug = f"{host}:{database}:{username}"
|
||||||
self.conn_factory = ConnectionFactory(
|
self.conn_factory = ConnectionFactory(
|
||||||
dict(host=host, db=database, user=username, cursorclass=DictCursor, **kwargs)
|
dict(host=host, db=database, user=username, cursorclass=DictCursor, **kwargs)
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ from unshackle.core.vault import Vault
|
|||||||
class SQLite(Vault):
|
class SQLite(Vault):
|
||||||
"""Key Vault using a locally-accessed sqlite DB file."""
|
"""Key Vault using a locally-accessed sqlite DB file."""
|
||||||
|
|
||||||
def __init__(self, name: str, path: Union[str, Path]):
|
def __init__(self, name: str, path: Union[str, Path], no_push: bool = False):
|
||||||
super().__init__(name)
|
super().__init__(name, no_push)
|
||||||
self.path = Path(path).expanduser()
|
self.path = Path(path).expanduser()
|
||||||
# TODO: Use a DictCursor or such to get fetches as dict?
|
# TODO: Use a DictCursor or such to get fetches as dict?
|
||||||
self.conn_factory = ConnectionFactory(self.path)
|
self.conn_factory = ConnectionFactory(self.path)
|
||||||
|
|||||||
Reference in New Issue
Block a user