Fix ffmpeg installer

This commit is contained in:
Lovi 2024-12-20 11:41:03 +01:00
parent 31016f45dd
commit b6319b42bd
8 changed files with 134 additions and 95 deletions

View File

@ -141,8 +141,7 @@ class TOR_downloader:
console.print("\n[bold green]🔗 Dettagli Torrent Aggiunto:[/bold green]")
console.print(f"[yellow]Nome:[/yellow] {torrent_info.get('name', torrent_name)}")
console.print(f"[yellow]Hash:[/yellow] {torrent_info['hash']}")
console.print(f"[yellow]Dimensione:[/yellow] {torrent_info.get('size', 'Non disponibile'):,} bytes")
console.print(f"[yellow]Stato:[/yellow] {torrent_info.get('state', 'Sconosciuto')}")
console.print(f"[yellow]Dimensione:[/yellow] {internet_manager.format_file_size(torrent_info.get('size'))}")
print()
# Salva l'hash per usi successivi e il path

View File

@ -41,8 +41,20 @@ FFMPEG_CONFIGURATION = {
}
class FFMPEGDownloader:
"""
A class for downloading and managing FFmpeg executables.
This class handles the detection of the operating system, downloading of FFmpeg binaries,
and management of the FFmpeg executables (ffmpeg, ffprobe, and ffplay).
Attributes:
os_name (str): The detected operating system name
arch (str): The system architecture (e.g., x86_64, arm64)
home_dir (str): User's home directory path
base_dir (str): Base directory for storing FFmpeg binaries
"""
def __init__(self):
self.os_name = self._detect_system()
self.arch = self._detect_arch()
@ -50,20 +62,32 @@ class FFMPEGDownloader:
self.base_dir = self._get_base_directory()
def _detect_system(self) -> str:
"""Detect and normalize operating system name."""
system = platform.system().lower()
"""
Detect and normalize the operating system name.
Returns:
str: Normalized operating system name ('windows', 'darwin', or 'linux')
Raises:
ValueError: If the operating system is not supported
"""
system = platform.system().lower()
if system in FFMPEG_CONFIGURATION:
return system
raise ValueError(f"Unsupported operating system: {system}")
def _detect_arch(self) -> str:
"""
Detect system architecture
Detect and normalize the system architecture.
Returns:
str: Normalized architecture name (e.g., 'x86_64', 'arm64')
The method normalizes various architecture names to consistent values:
- amd64/x86_64/x64 -> x86_64
- arm64/aarch64 -> arm64
"""
machine = platform.machine().lower()
arch_map = {
'amd64': 'x86_64',
'x86_64': 'x86_64',
@ -71,59 +95,80 @@ class FFMPEGDownloader:
'arm64': 'arm64',
'aarch64': 'arm64'
}
return arch_map.get(machine, machine)
def _get_base_directory(self) -> str:
"""
Get base directory for binaries
Get and create the base directory for storing FFmpeg binaries.
Returns:
str: Path to the base directory
The directory location varies by operating system:
- Windows: C:\\binary
- macOS: ~/Applications/binary
- Linux: ~/.local/bin/binary
"""
base_dir = FFMPEG_CONFIGURATION[self.os_name]['base_dir'](self.home_dir)
os.makedirs(base_dir, exist_ok=True)
return base_dir
def _check_existing_binaries(self) -> Tuple[Optional[str], Optional[str]]:
def _check_existing_binaries(self) -> Tuple[Optional[str], Optional[str], Optional[str]]:
"""
Check if FFmpeg binaries already exist in the base directory
Check if FFmpeg binaries already exist in the base directory.
Returns:
Tuple[Optional[str], Optional[str], Optional[str]]: Paths to ffmpeg, ffprobe, and ffplay executables.
Returns None for each executable that is not found.
"""
config = FFMPEG_CONFIGURATION[self.os_name]
executables = config['executables']
found_executables = []
for executable in executables:
# Search for exact executable in base directory
exe_paths = glob.glob(os.path.join(self.base_dir, executable))
if exe_paths:
found_executables.append(exe_paths[0])
else:
found_executables.append(None)
# Return paths if both executables are found
if len(found_executables) == len(executables):
return tuple(found_executables)
return tuple(found_executables) if len(found_executables) == 3 else (None, None, None)
return None, None
def _get_latest_version(self) -> str:
def _get_latest_version(self) -> Optional[str]:
"""
Get the latest FFmpeg version
Get the latest FFmpeg version from the official website.
Returns:
Optional[str]: The latest version string, or None if retrieval fails
Raises:
requests.exceptions.RequestException: If there are network-related errors
"""
try:
version_url = 'https://www.gyan.dev/ffmpeg/builds/release-version'
return requests.get(version_url).text.strip()
except Exception as e:
logging.error(f"Unable to get version: {e}")
return None
def _download_file(self, url: str, destination: str) -> bool:
"""
Download with Rich progress bar
Download a file from URL with a Rich progress bar display.
Parameters:
url (str): The URL to download the file from. Should be a direct download link.
destination (str): Local file path where the downloaded file will be saved.
Returns:
bool: True if download was successful, False otherwise.
Raises:
requests.exceptions.RequestException: If there are network-related errors
IOError: If there are issues writing to the destination file
"""
try:
response = requests.get(url, stream=True)
response.raise_for_status()
total_size = int(response.headers.get('content-length', 0))
with open(destination, 'wb') as file, \
@ -136,140 +181,131 @@ class FFMPEGDownloader:
) as progress:
download_task = progress.add_task("[green]Downloading FFmpeg", total=total_size)
for chunk in response.iter_content(chunk_size=8192):
size = file.write(chunk)
progress.update(download_task, advance=size)
return True
except Exception as e:
logging.error(f"Download error: {e}")
return False
def _extract_and_copy_binaries(self, archive_path: str) -> Tuple[Optional[str], Optional[str]]:
def _extract_and_copy_binaries(self, archive_path: str) -> Tuple[Optional[str], Optional[str], Optional[str]]:
"""
Extract archive and copy executables to base directory
Extract FFmpeg binaries from the downloaded archive and copy them to the base directory.
Parameters:
archive_path (str): Path to the downloaded archive file
Returns:
Tuple[Optional[str], Optional[str], Optional[str]]: Paths to the extracted ffmpeg,
ffprobe, and ffplay executables. Returns None for each executable that couldn't be extracted.
"""
try:
# Temporary extraction path
extraction_path = os.path.join(self.base_dir, 'temp_extract')
os.makedirs(extraction_path, exist_ok=True)
# Extract based on file type
if archive_path.endswith('.zip'):
with zipfile.ZipFile(archive_path, 'r') as zip_ref:
zip_ref.extractall(extraction_path)
elif archive_path.endswith('.tar.xz'):
import lzma
with lzma.open(archive_path, 'rb') as xz_file:
with tarfile.open(fileobj=xz_file) as tar_ref:
tar_ref.extractall(extraction_path)
with tarfile.open(archive_path) as tar_ref:
tar_ref.extractall(extraction_path)
# Find and copy executables
config = FFMPEG_CONFIGURATION[self.os_name]
executables = config['executables']
found_paths = []
for executable in executables:
# Find executable in extracted files
exe_paths = glob.glob(os.path.join(extraction_path, '**', executable), recursive=True)
if exe_paths:
# Copy to base directory
dest_path = os.path.join(self.base_dir, executable)
shutil.copy2(exe_paths[0], dest_path)
# Set execution permissions for Unix-like systems
if self.os_name != 'windows':
os.chmod(dest_path, 0o755)
found_paths.append(dest_path)
else:
found_paths.append(None)
# Clean up temporary extraction directory
shutil.rmtree(extraction_path, ignore_errors=True)
# Remove downloaded archive
os.remove(archive_path)
# Return paths if both executables found
if len(found_paths) == len(executables):
return tuple(found_paths)
return None, None
return tuple(found_paths) if len(found_paths) == 3 else (None, None, None)
except Exception as e:
logging.error(f"Extraction/copy error: {e}")
return None, None
return None, None, None
def download(self) -> Tuple[Optional[str], Optional[str]]:
def download(self) -> Tuple[Optional[str], Optional[str], Optional[str]]:
"""
Main download procedure
Returns paths of ffmpeg and ffprobe
Main method to download and set up FFmpeg executables.
Returns:
Tuple[Optional[str], Optional[str], Optional[str]]: Paths to ffmpeg, ffprobe, and ffplay executables.
Returns None for each executable that couldn't be downloaded or set up.
"""
# First, check if binaries already exist in base directory
existing_ffmpeg, existing_ffprobe, existing_ffplay = self._check_existing_binaries()
if existing_ffmpeg and existing_ffprobe:
return existing_ffmpeg, existing_ffprobe
if all([existing_ffmpeg, existing_ffprobe, existing_ffplay]):
return existing_ffmpeg, existing_ffprobe, existing_ffplay
# Get latest version
version = self._get_latest_version()
if not version:
logging.error("Cannot proceed: version not found")
return None, None
return None, None, None
# Prepare configurations
config = FFMPEG_CONFIGURATION[self.os_name]
# Build download URL
download_url = config['download_url'].format(
version=version,
arch=self.arch
)
# Download path
download_path = os.path.join(
self.base_dir,
f'ffmpeg-{version}{config["file_extension"]}'
)
# Download
console.print(
f"[bold blue]Downloading FFmpeg from:[/] {download_url}",
)
console.print(f"[bold blue]Downloading FFmpeg from:[/] {download_url}")
if not self._download_file(download_url, download_path):
return None, None
return None, None, None
# Extract and copy binaries
ffmpeg_path, ffprobe_path = self._extract_and_copy_binaries(download_path)
if ffmpeg_path and ffprobe_path:
return ffmpeg_path, ffprobe_path
logging.error("FFmpeg executables not found")
return None, None
return self._extract_and_copy_binaries(download_path)
def check_ffmpeg() -> Tuple[Optional[str], Optional[str], Optional[str]]:
"""
Check for FFmpeg executables in the system and download them if not found.
def check_ffmpeg():
Returns:
Tuple[Optional[str], Optional[str], Optional[str]]: Paths to ffmpeg, ffprobe, and ffplay executables.
Returns None for each executable that couldn't be found or downloaded.
The function first checks if FFmpeg executables are available in the system PATH:
- On Windows, uses the 'where' command
- On Unix-like systems, uses 'which'
If the executables are not found in PATH, it attempts to download and install them
using the FFMPEGDownloader class.
"""
try:
# First, use 'where' command to check existing binaries on Windows
if platform.system().lower() == 'windows':
ffmpeg_path = subprocess.check_output(['where', 'ffmpeg'], text=True).strip().split('\n')[0] if subprocess.call(['where', 'ffmpeg'], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) == 0 else None
ffprobe_path = subprocess.check_output(['where', 'ffprobe'], text=True).strip().split('\n')[0] if subprocess.call(['where', 'ffprobe'], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) == 0 else None
ffplay_path = subprocess.check_output(['where', 'ffplay'], text=True).strip().split('\n')[0] if subprocess.call(['where', 'ffplay'], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) == 0 else None
if ffmpeg_path and ffprobe_path:
return ffmpeg_path, ffprobe_path
# Fallback to which/shutil method for Unix-like systems
ffmpeg_path = shutil.which('ffmpeg')
ffprobe_path = shutil.which('ffprobe')
if ffmpeg_path and ffprobe_path:
return ffmpeg_path, ffprobe_path
if all([ffmpeg_path, ffprobe_path, ffplay_path]):
return ffmpeg_path, ffprobe_path, ffplay_path
else:
ffmpeg_path = shutil.which('ffmpeg')
ffprobe_path = shutil.which('ffprobe')
ffplay_path = shutil.which('ffplay')
if all([ffmpeg_path, ffprobe_path, ffplay_path]):
return ffmpeg_path, ffprobe_path, ffplay_path
downloader = FFMPEGDownloader()
return downloader.download()
except Exception as e:
logging.error(f"Error checking FFmpeg: {e}")
return None, None
return None, None, None

View File

@ -311,6 +311,7 @@ class OsSummary:
def __init__(self):
self.ffmpeg_path = None
self.ffprobe_path = None
self.ffplay_path = None
def get_executable_version(self, command: list):
"""
@ -462,7 +463,7 @@ class OsSummary:
# Locate ffmpeg from bin installation
if self.ffmpeg_path is None or self.ffprobe_path is None:
self.ffmpeg_path, self.ffprobe_path = check_ffmpeg()
self.ffmpeg_path, self.ffprobe_path, self.ffplay_path = check_ffmpeg()
if self.ffmpeg_path is None or self.ffprobe_path is None:
console.log("[red]Cant locate ffmpeg or ffprobe")

View File

@ -17,7 +17,6 @@ from StreamingCommunity.Util.console import console, msg
from StreamingCommunity.Util._jsonConfig import config_manager
from StreamingCommunity.Upload.update import update as git_update
from StreamingCommunity.Util.os import os_summary
from StreamingCommunity.Lib.TMBD import tmdb
from StreamingCommunity.Util.logger import Logger

View File

@ -10,6 +10,8 @@ sys.path.append(src_path)
# Import
from StreamingCommunity.Util.message import start_message
from StreamingCommunity.Util.os import os_summary
os_summary.get_system_summary()
from StreamingCommunity.Util.logger import Logger
from StreamingCommunity.Lib.Downloader import HLS_Downloader
@ -19,5 +21,5 @@ start_message()
logger = Logger()
print("Return: ", HLS_Downloader(
output_filename="test.mp4",
m3u8_index=""
m3u8_playlist="https://acdn.ak-stream-videoplatform.sky.it/hls/2024/11/21/968275/master.m3u8"
).start())

View File

@ -18,6 +18,6 @@ from StreamingCommunity.Lib.Downloader import MP4_downloader
start_message()
logger = Logger()
print("Return: ", MP4_downloader(
url="",
url="https://148-251-75-109.top/Getintopc.com/IDA_Pro_2020.mp4",
path=r".\Video\undefined.mp4"
))

View File

@ -19,7 +19,9 @@ start_message()
logger = Logger()
manager = TOR_downloader()
magnet_link = "magnet:?xt="
magnet_link = """magnet:?xt=urn:btih:d1257567d7a76d2e40734f561fd175769285ed33&dn=Alien.Romulus.2024.1080p.BluRay.x264-FHC.mkv&tr=udp%3A%2F%2Ftracker.opentrackr.org%3A1337%2Fannounce&tr=udp%3A%2F%2Ftorrentclub.space%3A6969%2Fannounce&tr=http%3A%2F%2Ftracker.bt4g.com%3A2095%2Fannounce&tr=https%3A%2F%2Ftr.burnabyhighstar.com%3A443%2Fannoun
ce&tr=udp%3A%2F%2Ftracker.monitorit4.me%3A6969%2Fannounce&tr=udp%3A%2F%2Ftracker.0x.tf%3A6969%2Fannounce&tr=http%3A%2F%2Fbt.okmp3.ru%3A2710%2Fannounce&tr=https%3A%2F%2Ftr.doogh.club%3A443%2Fannounce&tr=https%3A%2F%2Ft.btcland.xyz%3A443%2Fannounce&tr=https%3A%2F%2Ftr.fuckbitcoin.xyz%3A443%2Fannounce&tr=http%3A%2F%2Ftrack
er.files.fm%3A6969%2Fannounce"""
manager.add_magnet_link(magnet_link)
manager.start_download()
manager.move_downloaded_files()
manager.move_downloaded_files(".")

View File

@ -11,7 +11,7 @@
"map_episode_name": "%(tv_name)_S%(season)E%(episode)_%(episode_name)",
"config_qbit_tor": {
"host": "192.168.1.58",
"port": "8080",
"port": "7060",
"user": "admin",
"pass": "adminadmin"
},