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:
rlaphoenix 2024-04-24 05:07:25 +01:00
parent 9768de8bf2
commit 677fd9c56a
13 changed files with 91 additions and 61 deletions

View File

@ -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}")

View File

@ -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}")

View File

@ -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")
]) ])

View File

@ -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_,

34
devine/core/binaries.py Normal file
View File

@ -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"
)

View File

@ -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,

View File

@ -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

View File

@ -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",

View File

@ -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.")

View File

@ -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",

View File

@ -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 []),

View File

@ -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",

View File

@ -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"