mirror of
https://github.com/Arrowar/StreamingCommunity.git
synced 2025-06-05 02:55:25 +00:00
Fix ffmpeg installation on linux (#244)
* Fix ffmpeg installation * Remove check file exists after ffmpeg conversion
This commit is contained in:
parent
8e20ad8ff6
commit
dd2bec1bb9
@ -1,7 +1,6 @@
|
||||
# 31.01.24
|
||||
|
||||
import sys
|
||||
import time
|
||||
import logging
|
||||
import subprocess
|
||||
from typing import List, Dict
|
||||
@ -36,24 +35,14 @@ FFMPEG_PATH = os_summary.ffmpeg_path
|
||||
|
||||
|
||||
def join_video(video_path: str, out_path: str, codec: M3U8_Codec = None):
|
||||
|
||||
"""
|
||||
Joins single ts video file to mp4
|
||||
|
||||
Parameters:
|
||||
- video_path (str): The path to the video file.
|
||||
- out_path (str): The path to save the output file.
|
||||
- vcodec (str): The video codec to use. Defaults to 'copy'.
|
||||
- acodec (str): The audio codec to use. Defaults to 'aac'.
|
||||
- bitrate (str): The bitrate for the audio stream. Defaults to '192k'.
|
||||
- force_ts (bool): Force video path to be mpegts as input.
|
||||
- codec (M3U8_Codec): The video codec to use. Defaults to 'copy'.
|
||||
"""
|
||||
|
||||
if not os_manager.check_file(video_path):
|
||||
logging.error("Missing input video for ffmpeg conversion.")
|
||||
sys.exit(0)
|
||||
|
||||
# Start command with locate ffmpeg
|
||||
ffmpeg_cmd = [FFMPEG_PATH]
|
||||
|
||||
# Enabled the use of gpu
|
||||
@ -121,11 +110,6 @@ def join_video(video_path: str, out_path: str, codec: M3U8_Codec = None):
|
||||
capture_ffmpeg_real_time(ffmpeg_cmd, "[cyan]Join video")
|
||||
print()
|
||||
|
||||
time.sleep(0.5)
|
||||
if not os_manager.check_file(out_path):
|
||||
logging.error("Missing output video for ffmpeg conversion video.")
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
def join_audios(video_path: str, audio_tracks: List[Dict[str, str]], out_path: str, codec: M3U8_Codec = None):
|
||||
"""
|
||||
@ -137,11 +121,6 @@ def join_audios(video_path: str, audio_tracks: List[Dict[str, str]], out_path: s
|
||||
Each dictionary should contain the 'path' key with the path to the audio file.
|
||||
- out_path (str): The path to save the output file.
|
||||
"""
|
||||
|
||||
if not os_manager.check_file(video_path):
|
||||
logging.error("Missing input video for ffmpeg conversion.")
|
||||
sys.exit(0)
|
||||
|
||||
video_audio_same_duration = check_duration_v_a(video_path, audio_tracks[0].get('path'))
|
||||
|
||||
# Start command with locate ffmpeg
|
||||
@ -225,11 +204,6 @@ def join_audios(video_path: str, audio_tracks: List[Dict[str, str]], out_path: s
|
||||
capture_ffmpeg_real_time(ffmpeg_cmd, "[cyan]Join audio")
|
||||
print()
|
||||
|
||||
time.sleep(0.5)
|
||||
if not os_manager.check_file(out_path):
|
||||
logging.error("Missing output video for ffmpeg conversion audio.")
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
def join_subtitle(video_path: str, subtitles_list: List[Dict[str, str]], out_path: str):
|
||||
"""
|
||||
@ -241,12 +215,6 @@ def join_subtitle(video_path: str, subtitles_list: List[Dict[str, str]], out_pat
|
||||
Each dictionary should contain the 'path' key with the path to the subtitle file and the 'name' key with the name of the subtitle.
|
||||
- out_path (str): The path to save the output file.
|
||||
"""
|
||||
|
||||
if not os_manager.check_file(video_path):
|
||||
logging.error("Missing input video for ffmpeg conversion.")
|
||||
sys.exit(0)
|
||||
|
||||
# Start command with locate ffmpeg
|
||||
ffmpeg_cmd = [FFMPEG_PATH, "-i", video_path]
|
||||
|
||||
# Add subtitle input files first
|
||||
@ -274,7 +242,6 @@ def join_subtitle(video_path: str, subtitles_list: List[Dict[str, str]], out_pat
|
||||
ffmpeg_cmd += [out_path, "-y"]
|
||||
logging.info(f"FFmpeg command: {ffmpeg_cmd}")
|
||||
|
||||
|
||||
# Run join
|
||||
if DEBUG_MODE:
|
||||
subprocess.run(ffmpeg_cmd, check=True)
|
||||
@ -288,9 +255,4 @@ def join_subtitle(video_path: str, subtitles_list: List[Dict[str, str]], out_pat
|
||||
console.log(f"[purple]FFmpeg [white][[cyan]Join subtitle[white]] ...")
|
||||
with suppress_output():
|
||||
capture_ffmpeg_real_time(ffmpeg_cmd, "[cyan]Join subtitle")
|
||||
print()
|
||||
|
||||
time.sleep(0.5)
|
||||
if not os_manager.check_file(out_path):
|
||||
logging.error("Missing output video for ffmpeg conversion subtitle.")
|
||||
sys.exit(0)
|
||||
print()
|
@ -1,18 +1,19 @@
|
||||
# 24.01.2024
|
||||
|
||||
import os
|
||||
import glob
|
||||
import gzip
|
||||
import shutil
|
||||
import tarfile
|
||||
import zipfile
|
||||
import logging
|
||||
import platform
|
||||
import subprocess
|
||||
import zipfile
|
||||
import tarfile
|
||||
import logging
|
||||
import requests
|
||||
import shutil
|
||||
import glob
|
||||
from typing import Optional, Tuple
|
||||
|
||||
|
||||
# External library
|
||||
import requests
|
||||
from rich.console import Console
|
||||
from rich.progress import Progress, SpinnerColumn, BarColumn, TextColumn, TimeRemainingColumn
|
||||
|
||||
@ -20,44 +21,30 @@ from rich.progress import Progress, SpinnerColumn, BarColumn, TextColumn, TimeRe
|
||||
# Variable
|
||||
console = Console()
|
||||
|
||||
# https://github.com/eugeneware/ffmpeg-static/releases
|
||||
# https://github.com/GyanD/codexffmpeg/releases/
|
||||
|
||||
FFMPEG_CONFIGURATION = {
|
||||
'windows': {
|
||||
'base_dir': lambda home: os.path.join(os.path.splitdrive(home)[0] + os.path.sep, 'binary'),
|
||||
'download_url': 'https://github.com/GyanD/codexffmpeg/releases/download/{version}/ffmpeg-{version}-full_build.zip',
|
||||
'file_extension': '.zip',
|
||||
'executables': ['ffmpeg.exe', 'ffprobe.exe', 'ffplay.exe']
|
||||
'download_url': 'https://github.com/eugeneware/ffmpeg-static/releases/latest/download/ffmpeg-win32-{arch}.gz',
|
||||
'file_extension': '.gz',
|
||||
'executables': ['ffmpeg-win32-{arch}', 'ffprobe-win32-{arch}']
|
||||
},
|
||||
'darwin': {
|
||||
'base_dir': lambda home: os.path.join(home, 'Applications', 'binary'),
|
||||
'download_url': 'https://github.com/eugeneware/ffmpeg-static/releases/download/b{version}/ffmpeg-macOS-{arch}.zip',
|
||||
'file_extension': '.zip',
|
||||
'executables': ['ffmpeg', 'ffprobe', 'ffplay']
|
||||
'download_url': 'https://github.com/eugeneware/ffmpeg-static/releases/latest/download/ffmpeg-darwin-{arch}.gz',
|
||||
'file_extension': '.gz',
|
||||
'executables': ['ffmpeg-darwin-{arch}', 'ffprobe-darwin-{arch}']
|
||||
},
|
||||
'linux': {
|
||||
'base_dir': lambda home: os.path.join(home, '.local', 'bin', 'binary'),
|
||||
'download_url': 'https://github.com/eugeneware/ffmpeg-static/releases/download/b{version}/ffmpeg-linux-{arch}.tar.xz',
|
||||
'file_extension': '.tar.xz',
|
||||
'executables': ['ffmpeg', 'ffprobe', 'ffplay']
|
||||
'download_url': 'https://github.com/eugeneware/ffmpeg-static/releases/latest/download/ffmpeg-linux-{arch}.gz',
|
||||
'file_extension': '.gz',
|
||||
'executables': ['ffmpeg-linux-{arch}', 'ffprobe-linux-{arch}']
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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()
|
||||
@ -70,9 +57,6 @@ class FFMPEGDownloader:
|
||||
|
||||
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:
|
||||
@ -85,18 +69,17 @@ class FFMPEGDownloader:
|
||||
|
||||
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',
|
||||
'x64': 'x86_64',
|
||||
'arm64': 'arm64',
|
||||
'aarch64': 'arm64'
|
||||
'amd64': 'x64',
|
||||
'x86_64': 'x64',
|
||||
'x64': 'x64',
|
||||
'arm64': 'arm64',
|
||||
'aarch64': 'arm64',
|
||||
'armv7l': 'arm',
|
||||
'i386': 'ia32',
|
||||
'i686': 'ia32'
|
||||
}
|
||||
return arch_map.get(machine, machine)
|
||||
|
||||
@ -106,11 +89,6 @@ class FFMPEGDownloader:
|
||||
|
||||
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)
|
||||
@ -157,9 +135,6 @@ class FFMPEGDownloader:
|
||||
|
||||
Returns:
|
||||
Optional[str]: The latest version string, or None if retrieval fails.
|
||||
|
||||
Raises:
|
||||
requests.exceptions.RequestException: If there are network-related errors.
|
||||
"""
|
||||
try:
|
||||
# Use GitHub API to fetch the latest release
|
||||
@ -184,10 +159,6 @@ class FFMPEGDownloader:
|
||||
|
||||
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)
|
||||
@ -233,6 +204,13 @@ class FFMPEGDownloader:
|
||||
elif archive_path.endswith('.tar.xz'):
|
||||
with tarfile.open(archive_path) as tar_ref:
|
||||
tar_ref.extractall(extraction_path)
|
||||
elif archive_path.endswith('.gz'):
|
||||
file_name = os.path.basename(archive_path)[:-3] # Remove extension .gz
|
||||
output_path = os.path.join(extraction_path, file_name)
|
||||
with gzip.open(archive_path, 'rb') as f_in, open(output_path, 'wb') as f_out:
|
||||
shutil.copyfileobj(f_in, f_out)
|
||||
else:
|
||||
raise ValueError("Unsupported archive format")
|
||||
|
||||
config = FFMPEG_CONFIGURATION[self.os_name]
|
||||
executables = config['executables']
|
||||
@ -240,7 +218,6 @@ class FFMPEGDownloader:
|
||||
|
||||
for executable in executables:
|
||||
exe_paths = glob.glob(os.path.join(extraction_path, '**', executable), recursive=True)
|
||||
|
||||
if exe_paths:
|
||||
dest_path = os.path.join(self.base_dir, executable)
|
||||
shutil.copy2(exe_paths[0], dest_path)
|
||||
@ -269,34 +246,29 @@ class FFMPEGDownloader:
|
||||
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.
|
||||
"""
|
||||
existing_ffmpeg, existing_ffprobe, existing_ffplay = self._check_existing_binaries()
|
||||
if all([existing_ffmpeg, existing_ffprobe, existing_ffplay]):
|
||||
return existing_ffmpeg, existing_ffprobe, existing_ffplay
|
||||
|
||||
repo = 'GyanD/codexffmpeg' if self._detect_system() == 'windows' else 'eugeneware/ffmpeg-static'
|
||||
console.print(f"[red]Use {repo} repo for downloading ffmpeg.")
|
||||
version = self._get_latest_version(repo)
|
||||
|
||||
if not version:
|
||||
logging.error("Cannot proceed: version not found")
|
||||
return None, None, None
|
||||
|
||||
config = FFMPEG_CONFIGURATION[self.os_name]
|
||||
download_url = config['download_url'].format(
|
||||
version=version,
|
||||
arch=self.arch
|
||||
executables = [exe.format(arch=self.arch) for exe in config['executables']]
|
||||
|
||||
for executable in executables:
|
||||
download_url = f"https://github.com/eugeneware/ffmpeg-static/releases/latest/download/{executable}.gz"
|
||||
download_path = os.path.join(self.base_dir, f"{executable}.gz")
|
||||
final_path = os.path.join(self.base_dir, executable)
|
||||
|
||||
console.print(f"[bold blue]Downloading {executable} from GitHub[/]")
|
||||
if not self._download_file(download_url, download_path):
|
||||
return None, None, None
|
||||
|
||||
with gzip.open(download_path, 'rb') as f_in, open(final_path, 'wb') as f_out:
|
||||
shutil.copyfileobj(f_in, f_out)
|
||||
|
||||
os.chmod(final_path, 0o755)
|
||||
os.remove(download_path)
|
||||
|
||||
return (
|
||||
os.path.join(self.base_dir, executables[0]),
|
||||
os.path.join(self.base_dir, executables[1]),
|
||||
None
|
||||
)
|
||||
|
||||
download_path = os.path.join(
|
||||
self.base_dir,
|
||||
f'ffmpeg-{version}{config["file_extension"]}'
|
||||
)
|
||||
|
||||
console.print(f"[bold blue]Downloading FFmpeg from:[/] {download_url}")
|
||||
if not self._download_file(download_url, download_path):
|
||||
return None, None, None
|
||||
|
||||
return self._extract_and_copy_binaries(download_path)
|
||||
|
||||
def check_ffmpeg() -> Tuple[Optional[str], Optional[str], Optional[str]]:
|
||||
"""
|
||||
|
Loading…
x
Reference in New Issue
Block a user