mirror of https://github.com/devine-dl/devine.git
feat(binaries): Move all binary definitions to core/binaries file
This simplifies and centralizes all definitions on where these binaries can be found to a singular reference, making it easier to modify, edit, and improve.
This commit is contained in:
parent
9768de8bf2
commit
677fd9c56a
|
@ -38,6 +38,7 @@ from rich.table import Table
|
||||||
from rich.text import Text
|
from rich.text import Text
|
||||||
from rich.tree import Tree
|
from rich.tree import Tree
|
||||||
|
|
||||||
|
from devine.core import binaries
|
||||||
from devine.core.config import config
|
from devine.core.config import config
|
||||||
from devine.core.console import console
|
from devine.core.console import console
|
||||||
from devine.core.constants import DOWNLOAD_LICENCE_ONLY, AnyTrack, context_settings
|
from devine.core.constants import DOWNLOAD_LICENCE_ONLY, AnyTrack, context_settings
|
||||||
|
@ -51,7 +52,7 @@ from devine.core.titles import Movie, Song, Title_T
|
||||||
from devine.core.titles.episode import Episode
|
from devine.core.titles.episode import Episode
|
||||||
from devine.core.tracks import Audio, Subtitle, Tracks, Video
|
from devine.core.tracks import Audio, Subtitle, Tracks, Video
|
||||||
from devine.core.tracks.attachment import Attachment
|
from devine.core.tracks.attachment import Attachment
|
||||||
from devine.core.utilities import get_binary_path, get_system_fonts, is_close_match, time_elapsed_since
|
from devine.core.utilities import get_system_fonts, is_close_match, time_elapsed_since
|
||||||
from devine.core.utils.click_types import LANGUAGE_RANGE, QUALITY_LIST, SEASON_RANGE, ContextData, MultipleChoice
|
from devine.core.utils.click_types import LANGUAGE_RANGE, QUALITY_LIST, SEASON_RANGE, ContextData, MultipleChoice
|
||||||
from devine.core.utils.collections import merge_dict
|
from devine.core.utils.collections import merge_dict
|
||||||
from devine.core.utils.subprocess import ffprobe
|
from devine.core.utils.subprocess import ffprobe
|
||||||
|
@ -198,7 +199,7 @@ class dl:
|
||||||
self.proxy_providers.append(Basic(**config.proxy_providers["basic"]))
|
self.proxy_providers.append(Basic(**config.proxy_providers["basic"]))
|
||||||
if config.proxy_providers.get("nordvpn"):
|
if config.proxy_providers.get("nordvpn"):
|
||||||
self.proxy_providers.append(NordVPN(**config.proxy_providers["nordvpn"]))
|
self.proxy_providers.append(NordVPN(**config.proxy_providers["nordvpn"]))
|
||||||
if get_binary_path("hola-proxy"):
|
if binaries.HolaProxy:
|
||||||
self.proxy_providers.append(Hola())
|
self.proxy_providers.append(Hola())
|
||||||
for proxy_provider in self.proxy_providers:
|
for proxy_provider in self.proxy_providers:
|
||||||
self.log.info(f"Loaded {proxy_provider.__class__.__name__}: {proxy_provider}")
|
self.log.info(f"Loaded {proxy_provider.__class__.__name__}: {proxy_provider}")
|
||||||
|
|
|
@ -12,13 +12,13 @@ from rich.rule import Rule
|
||||||
from rich.tree import Tree
|
from rich.tree import Tree
|
||||||
|
|
||||||
from devine.commands.dl import dl
|
from devine.commands.dl import dl
|
||||||
|
from devine.core import binaries
|
||||||
from devine.core.config import config
|
from devine.core.config import config
|
||||||
from devine.core.console import console
|
from devine.core.console import console
|
||||||
from devine.core.constants import context_settings
|
from devine.core.constants import context_settings
|
||||||
from devine.core.proxies import Basic, Hola, NordVPN
|
from devine.core.proxies import Basic, Hola, NordVPN
|
||||||
from devine.core.service import Service
|
from devine.core.service import Service
|
||||||
from devine.core.services import Services
|
from devine.core.services import Services
|
||||||
from devine.core.utilities import get_binary_path
|
|
||||||
from devine.core.utils.click_types import ContextData
|
from devine.core.utils.click_types import ContextData
|
||||||
from devine.core.utils.collections import merge_dict
|
from devine.core.utils.collections import merge_dict
|
||||||
|
|
||||||
|
@ -72,7 +72,7 @@ def search(
|
||||||
proxy_providers.append(Basic(**config.proxy_providers["basic"]))
|
proxy_providers.append(Basic(**config.proxy_providers["basic"]))
|
||||||
if config.proxy_providers.get("nordvpn"):
|
if config.proxy_providers.get("nordvpn"):
|
||||||
proxy_providers.append(NordVPN(**config.proxy_providers["nordvpn"]))
|
proxy_providers.append(NordVPN(**config.proxy_providers["nordvpn"]))
|
||||||
if get_binary_path("hola-proxy"):
|
if binaries.HolaProxy:
|
||||||
proxy_providers.append(Hola())
|
proxy_providers.append(Hola())
|
||||||
for proxy_provider in proxy_providers:
|
for proxy_provider in proxy_providers:
|
||||||
log.info(f"Loaded {proxy_provider.__class__.__name__}: {proxy_provider}")
|
log.info(f"Loaded {proxy_provider.__class__.__name__}: {proxy_provider}")
|
||||||
|
|
|
@ -2,9 +2,9 @@ import subprocess
|
||||||
|
|
||||||
import click
|
import click
|
||||||
|
|
||||||
|
from devine.core import binaries
|
||||||
from devine.core.config import config
|
from devine.core.config import config
|
||||||
from devine.core.constants import context_settings
|
from devine.core.constants import context_settings
|
||||||
from devine.core.utilities import get_binary_path
|
|
||||||
|
|
||||||
|
|
||||||
@click.command(
|
@click.command(
|
||||||
|
@ -29,11 +29,10 @@ def serve(host: str, port: int, caddy: bool) -> None:
|
||||||
from pywidevine import serve
|
from pywidevine import serve
|
||||||
|
|
||||||
if caddy:
|
if caddy:
|
||||||
executable = get_binary_path("caddy")
|
if not binaries.Caddy:
|
||||||
if not executable:
|
|
||||||
raise click.ClickException("Caddy executable \"caddy\" not found but is required for --caddy.")
|
raise click.ClickException("Caddy executable \"caddy\" not found but is required for --caddy.")
|
||||||
caddy_p = subprocess.Popen([
|
caddy_p = subprocess.Popen([
|
||||||
executable,
|
binaries.Caddy,
|
||||||
"run",
|
"run",
|
||||||
"--config", str(config.directories.user_configs / "Caddyfile")
|
"--config", str(config.directories.user_configs / "Caddyfile")
|
||||||
])
|
])
|
||||||
|
|
|
@ -4,8 +4,8 @@ from pathlib import Path
|
||||||
import click
|
import click
|
||||||
from pymediainfo import MediaInfo
|
from pymediainfo import MediaInfo
|
||||||
|
|
||||||
|
from devine.core import binaries
|
||||||
from devine.core.constants import context_settings
|
from devine.core.constants import context_settings
|
||||||
from devine.core.utilities import get_binary_path
|
|
||||||
|
|
||||||
|
|
||||||
@click.group(short_help="Various helper scripts and programs.", context_settings=context_settings)
|
@click.group(short_help="Various helper scripts and programs.", context_settings=context_settings)
|
||||||
|
@ -38,8 +38,7 @@ def crop(path: Path, aspect: str, letter: bool, offset: int, preview: bool) -> N
|
||||||
as it may go from being 2px away from a perfect crop, to 20px over-cropping
|
as it may go from being 2px away from a perfect crop, to 20px over-cropping
|
||||||
again due to sub-sampled chroma.
|
again due to sub-sampled chroma.
|
||||||
"""
|
"""
|
||||||
executable = get_binary_path("ffmpeg")
|
if not binaries.FFMPEG:
|
||||||
if not executable:
|
|
||||||
raise click.ClickException("FFmpeg executable \"ffmpeg\" not found but is required.")
|
raise click.ClickException("FFmpeg executable \"ffmpeg\" not found but is required.")
|
||||||
|
|
||||||
if path.is_dir():
|
if path.is_dir():
|
||||||
|
@ -87,7 +86,7 @@ def crop(path: Path, aspect: str, letter: bool, offset: int, preview: bool) -> N
|
||||||
]))))]
|
]))))]
|
||||||
|
|
||||||
ffmpeg_call = subprocess.Popen([
|
ffmpeg_call = subprocess.Popen([
|
||||||
executable, "-y",
|
binaries.FFMPEG, "-y",
|
||||||
"-i", str(video_path),
|
"-i", str(video_path),
|
||||||
"-map", "0:v:0",
|
"-map", "0:v:0",
|
||||||
"-c", "copy",
|
"-c", "copy",
|
||||||
|
@ -95,7 +94,7 @@ def crop(path: Path, aspect: str, letter: bool, offset: int, preview: bool) -> N
|
||||||
] + out_path, stdout=subprocess.PIPE)
|
] + out_path, stdout=subprocess.PIPE)
|
||||||
try:
|
try:
|
||||||
if preview:
|
if preview:
|
||||||
previewer = get_binary_path("mpv", "ffplay")
|
previewer = binaries.MPV or binaries.FFPlay
|
||||||
if not previewer:
|
if not previewer:
|
||||||
raise click.ClickException("MPV/FFplay executables weren't found but are required for previewing.")
|
raise click.ClickException("MPV/FFplay executables weren't found but are required for previewing.")
|
||||||
subprocess.Popen((previewer, "-"), stdin=ffmpeg_call.stdout)
|
subprocess.Popen((previewer, "-"), stdin=ffmpeg_call.stdout)
|
||||||
|
@ -120,8 +119,7 @@ def range_(path: Path, full: bool, preview: bool) -> None:
|
||||||
then you're video may have the range set to the wrong value. Flip its range to the
|
then you're video may have the range set to the wrong value. Flip its range to the
|
||||||
opposite value and see if that fixes it.
|
opposite value and see if that fixes it.
|
||||||
"""
|
"""
|
||||||
executable = get_binary_path("ffmpeg")
|
if not binaries.FFMPEG:
|
||||||
if not executable:
|
|
||||||
raise click.ClickException("FFmpeg executable \"ffmpeg\" not found but is required.")
|
raise click.ClickException("FFmpeg executable \"ffmpeg\" not found but is required.")
|
||||||
|
|
||||||
if path.is_dir():
|
if path.is_dir():
|
||||||
|
@ -157,7 +155,7 @@ def range_(path: Path, full: bool, preview: bool) -> None:
|
||||||
]))))]
|
]))))]
|
||||||
|
|
||||||
ffmpeg_call = subprocess.Popen([
|
ffmpeg_call = subprocess.Popen([
|
||||||
executable, "-y",
|
binaries.FFMPEG, "-y",
|
||||||
"-i", str(video_path),
|
"-i", str(video_path),
|
||||||
"-map", "0:v:0",
|
"-map", "0:v:0",
|
||||||
"-c", "copy",
|
"-c", "copy",
|
||||||
|
@ -165,7 +163,7 @@ def range_(path: Path, full: bool, preview: bool) -> None:
|
||||||
] + out_path, stdout=subprocess.PIPE)
|
] + out_path, stdout=subprocess.PIPE)
|
||||||
try:
|
try:
|
||||||
if preview:
|
if preview:
|
||||||
previewer = get_binary_path("mpv", "ffplay")
|
previewer = binaries.MPV or binaries.FFPlay
|
||||||
if not previewer:
|
if not previewer:
|
||||||
raise click.ClickException("MPV/FFplay executables weren't found but are required for previewing.")
|
raise click.ClickException("MPV/FFplay executables weren't found but are required for previewing.")
|
||||||
subprocess.Popen((previewer, "-"), stdin=ffmpeg_call.stdout)
|
subprocess.Popen((previewer, "-"), stdin=ffmpeg_call.stdout)
|
||||||
|
@ -188,8 +186,7 @@ def test(path: Path, map_: str) -> None:
|
||||||
You may choose specific streams using the -m/--map parameter. E.g.,
|
You may choose specific streams using the -m/--map parameter. E.g.,
|
||||||
'0:v:0' to test the first video stream, or '0:a' to test all audio streams.
|
'0:v:0' to test the first video stream, or '0:a' to test all audio streams.
|
||||||
"""
|
"""
|
||||||
executable = get_binary_path("ffmpeg")
|
if not binaries.FFMPEG:
|
||||||
if not executable:
|
|
||||||
raise click.ClickException("FFmpeg executable \"ffmpeg\" not found but is required.")
|
raise click.ClickException("FFmpeg executable \"ffmpeg\" not found but is required.")
|
||||||
|
|
||||||
if path.is_dir():
|
if path.is_dir():
|
||||||
|
@ -199,7 +196,7 @@ def test(path: Path, map_: str) -> None:
|
||||||
for video_path in paths:
|
for video_path in paths:
|
||||||
print("Starting...")
|
print("Starting...")
|
||||||
p = subprocess.Popen([
|
p = subprocess.Popen([
|
||||||
executable, "-hide_banner",
|
binaries.FFMPEG, "-hide_banner",
|
||||||
"-benchmark",
|
"-benchmark",
|
||||||
"-i", str(video_path),
|
"-i", str(video_path),
|
||||||
"-map", map_,
|
"-map", map_,
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from devine.core.utilities import get_binary_path
|
||||||
|
|
||||||
|
__shaka_platform = {
|
||||||
|
"win32": "win",
|
||||||
|
"darwin": "osx"
|
||||||
|
}.get(sys.platform, sys.platform)
|
||||||
|
|
||||||
|
FFMPEG = get_binary_path("ffmpeg")
|
||||||
|
FFProbe = get_binary_path("ffprobe")
|
||||||
|
FFPlay = get_binary_path("ffplay")
|
||||||
|
SubtitleEdit = get_binary_path("SubtitleEdit")
|
||||||
|
ShakaPackager = get_binary_path(
|
||||||
|
"shaka-packager",
|
||||||
|
"packager",
|
||||||
|
f"packager-{__shaka_platform}",
|
||||||
|
f"packager-{__shaka_platform}-x64"
|
||||||
|
)
|
||||||
|
Aria2 = get_binary_path("aria2c", "aria2")
|
||||||
|
CCExtractor = get_binary_path(
|
||||||
|
"ccextractor",
|
||||||
|
"ccextractorwin",
|
||||||
|
"ccextractorwinfull"
|
||||||
|
)
|
||||||
|
HolaProxy = get_binary_path("hola-proxy")
|
||||||
|
MPV = get_binary_path("mpv")
|
||||||
|
Caddy = get_binary_path("caddy")
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = (
|
||||||
|
"FFMPEG", "FFProbe", "FFPlay", "SubtitleEdit", "ShakaPackager",
|
||||||
|
"Aria2", "CCExtractor", "HolaProxy", "MPV", "Caddy"
|
||||||
|
)
|
|
@ -15,10 +15,11 @@ from requests.cookies import cookiejar_from_dict, get_cookie_header
|
||||||
from rich import filesize
|
from rich import filesize
|
||||||
from rich.text import Text
|
from rich.text import Text
|
||||||
|
|
||||||
|
from devine.core import binaries
|
||||||
from devine.core.config import config
|
from devine.core.config import config
|
||||||
from devine.core.console import console
|
from devine.core.console import console
|
||||||
from devine.core.constants import DOWNLOAD_CANCELLED
|
from devine.core.constants import DOWNLOAD_CANCELLED
|
||||||
from devine.core.utilities import get_binary_path, get_extension, get_free_port
|
from devine.core.utilities import get_extension, get_free_port
|
||||||
|
|
||||||
|
|
||||||
def rpc(caller: Callable, secret: str, method: str, params: Optional[list[Any]] = None) -> Any:
|
def rpc(caller: Callable, secret: str, method: str, params: Optional[list[Any]] = None) -> Any:
|
||||||
|
@ -87,8 +88,7 @@ def download(
|
||||||
if not isinstance(urls, list):
|
if not isinstance(urls, list):
|
||||||
urls = [urls]
|
urls = [urls]
|
||||||
|
|
||||||
executable = get_binary_path("aria2c", "aria2")
|
if not binaries.Aria2:
|
||||||
if not executable:
|
|
||||||
raise EnvironmentError("Aria2c executable not found...")
|
raise EnvironmentError("Aria2c executable not found...")
|
||||||
|
|
||||||
if proxy and not proxy.lower().startswith("http://"):
|
if proxy and not proxy.lower().startswith("http://"):
|
||||||
|
@ -186,7 +186,7 @@ def download(
|
||||||
try:
|
try:
|
||||||
p = subprocess.Popen(
|
p = subprocess.Popen(
|
||||||
[
|
[
|
||||||
executable,
|
binaries.Aria2,
|
||||||
*arguments
|
*arguments
|
||||||
],
|
],
|
||||||
stdin=subprocess.PIPE,
|
stdin=subprocess.PIPE,
|
||||||
|
|
|
@ -3,7 +3,6 @@ from __future__ import annotations
|
||||||
import base64
|
import base64
|
||||||
import shutil
|
import shutil
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
|
||||||
import textwrap
|
import textwrap
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, Callable, Optional, Union
|
from typing import Any, Callable, Optional, Union
|
||||||
|
@ -17,10 +16,11 @@ from pywidevine.pssh import PSSH
|
||||||
from requests import Session
|
from requests import Session
|
||||||
from rich.text import Text
|
from rich.text import Text
|
||||||
|
|
||||||
|
from devine.core import binaries
|
||||||
from devine.core.config import config
|
from devine.core.config import config
|
||||||
from devine.core.console import console
|
from devine.core.console import console
|
||||||
from devine.core.constants import AnyTrack
|
from devine.core.constants import AnyTrack
|
||||||
from devine.core.utilities import get_binary_path, get_boxes
|
from devine.core.utilities import get_boxes
|
||||||
from devine.core.utils.subprocess import ffprobe
|
from devine.core.utils.subprocess import ffprobe
|
||||||
|
|
||||||
|
|
||||||
|
@ -223,9 +223,7 @@ class Widevine:
|
||||||
if not self.content_keys:
|
if not self.content_keys:
|
||||||
raise ValueError("Cannot decrypt a Track without any Content Keys...")
|
raise ValueError("Cannot decrypt a Track without any Content Keys...")
|
||||||
|
|
||||||
platform = {"win32": "win", "darwin": "osx"}.get(sys.platform, sys.platform)
|
if not binaries.ShakaPackager:
|
||||||
executable = get_binary_path("shaka-packager", "packager", f"packager-{platform}", f"packager-{platform}-x64")
|
|
||||||
if not executable:
|
|
||||||
raise EnvironmentError("Shaka Packager executable not found but is required.")
|
raise EnvironmentError("Shaka Packager executable not found but is required.")
|
||||||
if not path or not path.exists():
|
if not path or not path.exists():
|
||||||
raise ValueError("Tried to decrypt a file that does not exist.")
|
raise ValueError("Tried to decrypt a file that does not exist.")
|
||||||
|
@ -252,7 +250,7 @@ class Widevine:
|
||||||
]
|
]
|
||||||
|
|
||||||
p = subprocess.Popen(
|
p = subprocess.Popen(
|
||||||
[executable, *arguments],
|
[binaries.ShakaPackager, *arguments],
|
||||||
stdout=subprocess.DEVNULL,
|
stdout=subprocess.DEVNULL,
|
||||||
stderr=subprocess.PIPE,
|
stderr=subprocess.PIPE,
|
||||||
universal_newlines=True
|
universal_newlines=True
|
||||||
|
|
|
@ -19,12 +19,13 @@ from pywidevine.cdm import Cdm as WidevineCdm
|
||||||
from pywidevine.pssh import PSSH
|
from pywidevine.pssh import PSSH
|
||||||
from requests import Session
|
from requests import Session
|
||||||
|
|
||||||
|
from devine.core import binaries
|
||||||
from devine.core.constants import DOWNLOAD_CANCELLED, DOWNLOAD_LICENCE_ONLY, AnyTrack
|
from devine.core.constants import DOWNLOAD_CANCELLED, DOWNLOAD_LICENCE_ONLY, AnyTrack
|
||||||
from devine.core.downloaders import requests as requests_downloader
|
from devine.core.downloaders import requests as requests_downloader
|
||||||
from devine.core.drm import DRM_T, ClearKey, Widevine
|
from devine.core.drm import DRM_T, ClearKey, Widevine
|
||||||
from devine.core.events import events
|
from devine.core.events import events
|
||||||
from devine.core.tracks import Audio, Subtitle, Tracks, Video
|
from devine.core.tracks import Audio, Subtitle, Tracks, Video
|
||||||
from devine.core.utilities import get_binary_path, get_extension, is_close_match, try_ensure_utf8
|
from devine.core.utilities import get_extension, is_close_match, try_ensure_utf8
|
||||||
|
|
||||||
|
|
||||||
class HLS:
|
class HLS:
|
||||||
|
@ -556,8 +557,7 @@ class HLS:
|
||||||
|
|
||||||
Returns the file size of the merged file.
|
Returns the file size of the merged file.
|
||||||
"""
|
"""
|
||||||
ffmpeg = get_binary_path("ffmpeg")
|
if not binaries.FFMPEG:
|
||||||
if not ffmpeg:
|
|
||||||
raise EnvironmentError("FFmpeg executable was not found but is required to merge HLS segments.")
|
raise EnvironmentError("FFmpeg executable was not found but is required to merge HLS segments.")
|
||||||
|
|
||||||
demuxer_file = segments[0].parent / "ffmpeg_concat_demuxer.txt"
|
demuxer_file = segments[0].parent / "ffmpeg_concat_demuxer.txt"
|
||||||
|
@ -567,7 +567,7 @@ class HLS:
|
||||||
]))
|
]))
|
||||||
|
|
||||||
subprocess.check_call([
|
subprocess.check_call([
|
||||||
ffmpeg, "-hide_banner",
|
binaries.FFMPEG, "-hide_banner",
|
||||||
"-loglevel", "panic",
|
"-loglevel", "panic",
|
||||||
"-f", "concat",
|
"-f", "concat",
|
||||||
"-safe", "0",
|
"-safe", "0",
|
||||||
|
|
|
@ -3,8 +3,8 @@ import re
|
||||||
import subprocess
|
import subprocess
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
|
from devine.core import binaries
|
||||||
from devine.core.proxies.proxy import Proxy
|
from devine.core.proxies.proxy import Proxy
|
||||||
from devine.core.utilities import get_binary_path
|
|
||||||
|
|
||||||
|
|
||||||
class Hola(Proxy):
|
class Hola(Proxy):
|
||||||
|
@ -13,7 +13,7 @@ class Hola(Proxy):
|
||||||
Proxy Service using Hola's direct connections via the hola-proxy project.
|
Proxy Service using Hola's direct connections via the hola-proxy project.
|
||||||
https://github.com/Snawoot/hola-proxy
|
https://github.com/Snawoot/hola-proxy
|
||||||
"""
|
"""
|
||||||
self.binary = get_binary_path("hola-proxy")
|
self.binary = binaries.HolaProxy
|
||||||
if not self.binary:
|
if not self.binary:
|
||||||
raise EnvironmentError("hola-proxy executable not found but is required for the Hola proxy provider.")
|
raise EnvironmentError("hola-proxy executable not found but is required for the Hola proxy provider.")
|
||||||
|
|
||||||
|
|
|
@ -17,8 +17,9 @@ from pycaption.geometry import Layout
|
||||||
from pymp4.parser import MP4
|
from pymp4.parser import MP4
|
||||||
from subtitle_filter import Subtitles
|
from subtitle_filter import Subtitles
|
||||||
|
|
||||||
|
from devine.core import binaries
|
||||||
from devine.core.tracks.track import Track
|
from devine.core.tracks.track import Track
|
||||||
from devine.core.utilities import get_binary_path, try_ensure_utf8
|
from devine.core.utilities import try_ensure_utf8
|
||||||
|
|
||||||
|
|
||||||
class Subtitle(Track):
|
class Subtitle(Track):
|
||||||
|
@ -233,14 +234,13 @@ class Subtitle(Track):
|
||||||
|
|
||||||
output_path = self.path.with_suffix(f".{codec.value.lower()}")
|
output_path = self.path.with_suffix(f".{codec.value.lower()}")
|
||||||
|
|
||||||
sub_edit_executable = get_binary_path("SubtitleEdit")
|
if binaries.SubtitleEdit and self.codec not in (Subtitle.Codec.fTTML, Subtitle.Codec.fVTT):
|
||||||
if sub_edit_executable and self.codec not in (Subtitle.Codec.fTTML, Subtitle.Codec.fVTT):
|
|
||||||
sub_edit_format = {
|
sub_edit_format = {
|
||||||
Subtitle.Codec.SubStationAlphav4: "AdvancedSubStationAlpha",
|
Subtitle.Codec.SubStationAlphav4: "AdvancedSubStationAlpha",
|
||||||
Subtitle.Codec.TimedTextMarkupLang: "TimedText1.0"
|
Subtitle.Codec.TimedTextMarkupLang: "TimedText1.0"
|
||||||
}.get(codec, codec.name)
|
}.get(codec, codec.name)
|
||||||
sub_edit_args = [
|
sub_edit_args = [
|
||||||
sub_edit_executable,
|
binaries.SubtitleEdit,
|
||||||
"/Convert", self.path, sub_edit_format,
|
"/Convert", self.path, sub_edit_format,
|
||||||
f"/outputfilename:{output_path.name}",
|
f"/outputfilename:{output_path.name}",
|
||||||
"/encoding:utf8"
|
"/encoding:utf8"
|
||||||
|
@ -500,8 +500,7 @@ class Subtitle(Track):
|
||||||
if not self.path or not self.path.exists():
|
if not self.path or not self.path.exists():
|
||||||
raise ValueError("You must download the subtitle track first.")
|
raise ValueError("You must download the subtitle track first.")
|
||||||
|
|
||||||
executable = get_binary_path("SubtitleEdit")
|
if binaries.SubtitleEdit:
|
||||||
if executable:
|
|
||||||
if self.codec == Subtitle.Codec.SubStationAlphav4:
|
if self.codec == Subtitle.Codec.SubStationAlphav4:
|
||||||
output_format = "AdvancedSubStationAlpha"
|
output_format = "AdvancedSubStationAlpha"
|
||||||
elif self.codec == Subtitle.Codec.TimedTextMarkupLang:
|
elif self.codec == Subtitle.Codec.TimedTextMarkupLang:
|
||||||
|
@ -510,7 +509,7 @@ class Subtitle(Track):
|
||||||
output_format = self.codec.name
|
output_format = self.codec.name
|
||||||
subprocess.run(
|
subprocess.run(
|
||||||
[
|
[
|
||||||
executable,
|
binaries.SubtitleEdit,
|
||||||
"/Convert", self.path, output_format,
|
"/Convert", self.path, output_format,
|
||||||
"/encoding:utf8",
|
"/encoding:utf8",
|
||||||
"/overwrite",
|
"/overwrite",
|
||||||
|
@ -539,8 +538,7 @@ class Subtitle(Track):
|
||||||
if not self.path or not self.path.exists():
|
if not self.path or not self.path.exists():
|
||||||
raise ValueError("You must download the subtitle track first.")
|
raise ValueError("You must download the subtitle track first.")
|
||||||
|
|
||||||
executable = get_binary_path("SubtitleEdit")
|
if not binaries.SubtitleEdit:
|
||||||
if not executable:
|
|
||||||
raise EnvironmentError("SubtitleEdit executable not found...")
|
raise EnvironmentError("SubtitleEdit executable not found...")
|
||||||
|
|
||||||
if self.codec == Subtitle.Codec.SubStationAlphav4:
|
if self.codec == Subtitle.Codec.SubStationAlphav4:
|
||||||
|
@ -552,7 +550,7 @@ class Subtitle(Track):
|
||||||
|
|
||||||
subprocess.run(
|
subprocess.run(
|
||||||
[
|
[
|
||||||
executable,
|
binaries.SubtitleEdit,
|
||||||
"/Convert", self.path, output_format,
|
"/Convert", self.path, output_format,
|
||||||
"/ReverseRtlStartEnd",
|
"/ReverseRtlStartEnd",
|
||||||
"/encoding:utf8",
|
"/encoding:utf8",
|
||||||
|
|
|
@ -15,12 +15,13 @@ from zlib import crc32
|
||||||
from langcodes import Language
|
from langcodes import Language
|
||||||
from requests import Session
|
from requests import Session
|
||||||
|
|
||||||
|
from devine.core import binaries
|
||||||
from devine.core.config import config
|
from devine.core.config import config
|
||||||
from devine.core.constants import DOWNLOAD_CANCELLED, DOWNLOAD_LICENCE_ONLY
|
from devine.core.constants import DOWNLOAD_CANCELLED, DOWNLOAD_LICENCE_ONLY
|
||||||
from devine.core.downloaders import aria2c, curl_impersonate, requests
|
from devine.core.downloaders import aria2c, curl_impersonate, requests
|
||||||
from devine.core.drm import DRM_T, Widevine
|
from devine.core.drm import DRM_T, Widevine
|
||||||
from devine.core.events import events
|
from devine.core.events import events
|
||||||
from devine.core.utilities import get_binary_path, get_boxes, try_ensure_utf8
|
from devine.core.utilities import get_boxes, try_ensure_utf8
|
||||||
from devine.core.utils.subprocess import ffprobe
|
from devine.core.utils.subprocess import ffprobe
|
||||||
|
|
||||||
|
|
||||||
|
@ -470,8 +471,7 @@ class Track:
|
||||||
if not self.path or not self.path.exists():
|
if not self.path or not self.path.exists():
|
||||||
raise ValueError("Cannot repackage a Track that has not been downloaded.")
|
raise ValueError("Cannot repackage a Track that has not been downloaded.")
|
||||||
|
|
||||||
executable = get_binary_path("ffmpeg")
|
if not binaries.FFMPEG:
|
||||||
if not executable:
|
|
||||||
raise EnvironmentError("FFmpeg executable \"ffmpeg\" was not found but is required for this call.")
|
raise EnvironmentError("FFmpeg executable \"ffmpeg\" was not found but is required for this call.")
|
||||||
|
|
||||||
original_path = self.path
|
original_path = self.path
|
||||||
|
@ -480,7 +480,7 @@ class Track:
|
||||||
def _ffmpeg(extra_args: list[str] = None):
|
def _ffmpeg(extra_args: list[str] = None):
|
||||||
subprocess.run(
|
subprocess.run(
|
||||||
[
|
[
|
||||||
executable, "-hide_banner",
|
binaries.FFMPEG, "-hide_banner",
|
||||||
"-loglevel", "error",
|
"-loglevel", "error",
|
||||||
"-i", original_path,
|
"-i", original_path,
|
||||||
*(extra_args or []),
|
*(extra_args or []),
|
||||||
|
|
|
@ -10,10 +10,11 @@ from typing import Any, Optional, Union
|
||||||
|
|
||||||
from langcodes import Language
|
from langcodes import Language
|
||||||
|
|
||||||
|
from devine.core import binaries
|
||||||
from devine.core.config import config
|
from devine.core.config import config
|
||||||
from devine.core.tracks.subtitle import Subtitle
|
from devine.core.tracks.subtitle import Subtitle
|
||||||
from devine.core.tracks.track import Track
|
from devine.core.tracks.track import Track
|
||||||
from devine.core.utilities import FPS, get_binary_path, get_boxes
|
from devine.core.utilities import FPS, get_boxes
|
||||||
|
|
||||||
|
|
||||||
class Video(Track):
|
class Video(Track):
|
||||||
|
@ -257,8 +258,7 @@ class Video(Track):
|
||||||
f"it's codec, {self.codec.value}, is not yet supported."
|
f"it's codec, {self.codec.value}, is not yet supported."
|
||||||
)
|
)
|
||||||
|
|
||||||
executable = get_binary_path("ffmpeg")
|
if not binaries.FFMPEG:
|
||||||
if not executable:
|
|
||||||
raise EnvironmentError("FFmpeg executable \"ffmpeg\" was not found but is required for this call.")
|
raise EnvironmentError("FFmpeg executable \"ffmpeg\" was not found but is required for this call.")
|
||||||
|
|
||||||
filter_key = {
|
filter_key = {
|
||||||
|
@ -270,7 +270,7 @@ class Video(Track):
|
||||||
output_path = original_path.with_stem(f"{original_path.stem}_{['limited', 'full'][range_]}_range")
|
output_path = original_path.with_stem(f"{original_path.stem}_{['limited', 'full'][range_]}_range")
|
||||||
|
|
||||||
subprocess.run([
|
subprocess.run([
|
||||||
executable, "-hide_banner",
|
binaries.FFMPEG, "-hide_banner",
|
||||||
"-loglevel", "panic",
|
"-loglevel", "panic",
|
||||||
"-i", original_path,
|
"-i", original_path,
|
||||||
"-codec", "copy",
|
"-codec", "copy",
|
||||||
|
@ -288,8 +288,7 @@ class Video(Track):
|
||||||
if not self.path:
|
if not self.path:
|
||||||
raise ValueError("You must download the track first.")
|
raise ValueError("You must download the track first.")
|
||||||
|
|
||||||
executable = get_binary_path("ccextractor", "ccextractorwin", "ccextractorwinfull")
|
if not binaries.CCExtractor:
|
||||||
if not executable:
|
|
||||||
raise EnvironmentError("ccextractor executable was not found.")
|
raise EnvironmentError("ccextractor executable was not found.")
|
||||||
|
|
||||||
# ccextractor often fails in weird ways unless we repack
|
# ccextractor often fails in weird ways unless we repack
|
||||||
|
@ -299,7 +298,7 @@ class Video(Track):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
subprocess.run([
|
subprocess.run([
|
||||||
executable,
|
binaries.CCExtractor,
|
||||||
"-trim",
|
"-trim",
|
||||||
"-nobom",
|
"-nobom",
|
||||||
"-noru", "-ru1",
|
"-noru", "-ru1",
|
||||||
|
@ -380,8 +379,7 @@ class Video(Track):
|
||||||
if not self.path or not self.path.exists():
|
if not self.path or not self.path.exists():
|
||||||
raise ValueError("Cannot clean a Track that has not been downloaded.")
|
raise ValueError("Cannot clean a Track that has not been downloaded.")
|
||||||
|
|
||||||
executable = get_binary_path("ffmpeg")
|
if not binaries.FFMPEG:
|
||||||
if not executable:
|
|
||||||
raise EnvironmentError("FFmpeg executable \"ffmpeg\" was not found but is required for this call.")
|
raise EnvironmentError("FFmpeg executable \"ffmpeg\" was not found but is required for this call.")
|
||||||
|
|
||||||
log = logging.getLogger("x264-clean")
|
log = logging.getLogger("x264-clean")
|
||||||
|
@ -402,7 +400,7 @@ class Video(Track):
|
||||||
original_path = self.path
|
original_path = self.path
|
||||||
cleaned_path = original_path.with_suffix(f".cleaned{original_path.suffix}")
|
cleaned_path = original_path.with_suffix(f".cleaned{original_path.suffix}")
|
||||||
subprocess.run([
|
subprocess.run([
|
||||||
executable, "-hide_banner",
|
binaries.FFMPEG, "-hide_banner",
|
||||||
"-loglevel", "panic",
|
"-loglevel", "panic",
|
||||||
"-i", original_path,
|
"-i", original_path,
|
||||||
"-map_metadata", "-1",
|
"-map_metadata", "-1",
|
||||||
|
|
|
@ -3,11 +3,16 @@ import subprocess
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Union
|
from typing import Union
|
||||||
|
|
||||||
|
from devine.core import binaries
|
||||||
|
|
||||||
|
|
||||||
def ffprobe(uri: Union[bytes, Path]) -> dict:
|
def ffprobe(uri: Union[bytes, Path]) -> dict:
|
||||||
"""Use ffprobe on the provided data to get stream information."""
|
"""Use ffprobe on the provided data to get stream information."""
|
||||||
|
if not binaries.FFProbe:
|
||||||
|
raise EnvironmentError("FFProbe executable \"ffprobe\" not found but is required.")
|
||||||
|
|
||||||
args = [
|
args = [
|
||||||
"ffprobe",
|
binaries.FFProbe,
|
||||||
"-v", "quiet",
|
"-v", "quiet",
|
||||||
"-of", "json",
|
"-of", "json",
|
||||||
"-show_streams"
|
"-show_streams"
|
||||||
|
|
Loading…
Reference in New Issue