mirror of
				https://github.com/devine-dl/devine.git
				synced 2025-11-03 19:34:49 +00:00 
			
		
		
		
	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_,
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										34
									
								
								devine/core/binaries.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								devine/core/binaries.py
									
									
									
									
									
										Normal 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"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
@ -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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user