StreamingCommunity 1.9.1__py3-none-any.whl → 1.9.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of StreamingCommunity might be problematic. Click here for more details.

Files changed (96) hide show
  1. StreamingCommunity/run.py +4 -5
  2. {StreamingCommunity-1.9.1.dist-info → StreamingCommunity-1.9.4.dist-info}/METADATA +37 -7
  3. StreamingCommunity-1.9.4.dist-info/RECORD +7 -0
  4. {StreamingCommunity-1.9.1.dist-info → StreamingCommunity-1.9.4.dist-info}/WHEEL +1 -1
  5. {StreamingCommunity-1.9.1.dist-info → StreamingCommunity-1.9.4.dist-info}/entry_points.txt +1 -0
  6. StreamingCommunity/Api/Player/Helper/Vixcloud/js_parser.py +0 -143
  7. StreamingCommunity/Api/Player/Helper/Vixcloud/util.py +0 -166
  8. StreamingCommunity/Api/Player/ddl.py +0 -89
  9. StreamingCommunity/Api/Player/maxstream.py +0 -151
  10. StreamingCommunity/Api/Player/supervideo.py +0 -194
  11. StreamingCommunity/Api/Player/vixcloud.py +0 -224
  12. StreamingCommunity/Api/Site/1337xx/__init__.py +0 -50
  13. StreamingCommunity/Api/Site/1337xx/costant.py +0 -15
  14. StreamingCommunity/Api/Site/1337xx/site.py +0 -84
  15. StreamingCommunity/Api/Site/1337xx/title.py +0 -66
  16. StreamingCommunity/Api/Site/altadefinizione/__init__.py +0 -50
  17. StreamingCommunity/Api/Site/altadefinizione/costant.py +0 -15
  18. StreamingCommunity/Api/Site/altadefinizione/film.py +0 -69
  19. StreamingCommunity/Api/Site/altadefinizione/site.py +0 -86
  20. StreamingCommunity/Api/Site/animeunity/__init__.py +0 -50
  21. StreamingCommunity/Api/Site/animeunity/costant.py +0 -15
  22. StreamingCommunity/Api/Site/animeunity/film_serie.py +0 -130
  23. StreamingCommunity/Api/Site/animeunity/site.py +0 -165
  24. StreamingCommunity/Api/Site/animeunity/util/ScrapeSerie.py +0 -97
  25. StreamingCommunity/Api/Site/bitsearch/__init__.py +0 -51
  26. StreamingCommunity/Api/Site/bitsearch/costant.py +0 -15
  27. StreamingCommunity/Api/Site/bitsearch/site.py +0 -84
  28. StreamingCommunity/Api/Site/bitsearch/title.py +0 -47
  29. StreamingCommunity/Api/Site/cb01new/__init__.py +0 -51
  30. StreamingCommunity/Api/Site/cb01new/costant.py +0 -15
  31. StreamingCommunity/Api/Site/cb01new/film.py +0 -69
  32. StreamingCommunity/Api/Site/cb01new/site.py +0 -74
  33. StreamingCommunity/Api/Site/ddlstreamitaly/__init__.py +0 -57
  34. StreamingCommunity/Api/Site/ddlstreamitaly/costant.py +0 -16
  35. StreamingCommunity/Api/Site/ddlstreamitaly/series.py +0 -141
  36. StreamingCommunity/Api/Site/ddlstreamitaly/site.py +0 -93
  37. StreamingCommunity/Api/Site/ddlstreamitaly/util/ScrapeSerie.py +0 -85
  38. StreamingCommunity/Api/Site/guardaserie/__init__.py +0 -52
  39. StreamingCommunity/Api/Site/guardaserie/costant.py +0 -15
  40. StreamingCommunity/Api/Site/guardaserie/series.py +0 -195
  41. StreamingCommunity/Api/Site/guardaserie/site.py +0 -84
  42. StreamingCommunity/Api/Site/guardaserie/util/ScrapeSerie.py +0 -110
  43. StreamingCommunity/Api/Site/mostraguarda/__init__.py +0 -48
  44. StreamingCommunity/Api/Site/mostraguarda/costant.py +0 -15
  45. StreamingCommunity/Api/Site/mostraguarda/film.py +0 -94
  46. StreamingCommunity/Api/Site/piratebays/__init__.py +0 -50
  47. StreamingCommunity/Api/Site/piratebays/costant.py +0 -15
  48. StreamingCommunity/Api/Site/piratebays/site.py +0 -89
  49. StreamingCommunity/Api/Site/piratebays/title.py +0 -45
  50. StreamingCommunity/Api/Site/streamingcommunity/__init__.py +0 -55
  51. StreamingCommunity/Api/Site/streamingcommunity/costant.py +0 -15
  52. StreamingCommunity/Api/Site/streamingcommunity/film.py +0 -70
  53. StreamingCommunity/Api/Site/streamingcommunity/series.py +0 -205
  54. StreamingCommunity/Api/Site/streamingcommunity/site.py +0 -126
  55. StreamingCommunity/Api/Site/streamingcommunity/util/ScrapeSerie.py +0 -113
  56. StreamingCommunity/Api/Template/Class/SearchType.py +0 -101
  57. StreamingCommunity/Api/Template/Util/__init__.py +0 -5
  58. StreamingCommunity/Api/Template/Util/get_domain.py +0 -137
  59. StreamingCommunity/Api/Template/Util/manage_ep.py +0 -153
  60. StreamingCommunity/Api/Template/Util/recall_search.py +0 -37
  61. StreamingCommunity/Api/Template/__init__.py +0 -3
  62. StreamingCommunity/Api/Template/site.py +0 -87
  63. StreamingCommunity/Lib/Downloader/HLS/downloader.py +0 -968
  64. StreamingCommunity/Lib/Downloader/HLS/proxyes.py +0 -110
  65. StreamingCommunity/Lib/Downloader/HLS/segments.py +0 -538
  66. StreamingCommunity/Lib/Downloader/MP4/downloader.py +0 -156
  67. StreamingCommunity/Lib/Downloader/TOR/downloader.py +0 -222
  68. StreamingCommunity/Lib/Downloader/__init__.py +0 -5
  69. StreamingCommunity/Lib/Driver/driver_1.py +0 -76
  70. StreamingCommunity/Lib/FFmpeg/__init__.py +0 -4
  71. StreamingCommunity/Lib/FFmpeg/capture.py +0 -170
  72. StreamingCommunity/Lib/FFmpeg/command.py +0 -292
  73. StreamingCommunity/Lib/FFmpeg/util.py +0 -242
  74. StreamingCommunity/Lib/M3U8/__init__.py +0 -6
  75. StreamingCommunity/Lib/M3U8/decryptor.py +0 -164
  76. StreamingCommunity/Lib/M3U8/estimator.py +0 -176
  77. StreamingCommunity/Lib/M3U8/parser.py +0 -666
  78. StreamingCommunity/Lib/M3U8/url_fixer.py +0 -52
  79. StreamingCommunity/Lib/TMBD/__init__.py +0 -2
  80. StreamingCommunity/Lib/TMBD/obj_tmbd.py +0 -39
  81. StreamingCommunity/Lib/TMBD/tmdb.py +0 -346
  82. StreamingCommunity/Upload/update.py +0 -68
  83. StreamingCommunity/Upload/version.py +0 -5
  84. StreamingCommunity/Util/_jsonConfig.py +0 -204
  85. StreamingCommunity/Util/call_stack.py +0 -42
  86. StreamingCommunity/Util/color.py +0 -20
  87. StreamingCommunity/Util/console.py +0 -12
  88. StreamingCommunity/Util/ffmpeg_installer.py +0 -275
  89. StreamingCommunity/Util/headers.py +0 -147
  90. StreamingCommunity/Util/logger.py +0 -53
  91. StreamingCommunity/Util/message.py +0 -46
  92. StreamingCommunity/Util/os.py +0 -514
  93. StreamingCommunity/Util/table.py +0 -163
  94. StreamingCommunity-1.9.1.dist-info/RECORD +0 -95
  95. {StreamingCommunity-1.9.1.dist-info → StreamingCommunity-1.9.4.dist-info}/LICENSE +0 -0
  96. {StreamingCommunity-1.9.1.dist-info → StreamingCommunity-1.9.4.dist-info}/top_level.txt +0 -0
@@ -1,204 +0,0 @@
1
- # 29.01.24
2
-
3
- import os
4
- import json
5
- import httpx
6
- import logging
7
- from typing import Any, List
8
-
9
-
10
- class ConfigManager:
11
- def __init__(self, file_path: str = 'config.json') -> None:
12
- """Initialize the ConfigManager.
13
-
14
- Parameters:
15
- - file_path (str, optional): The path to the configuration file. Default is 'config.json'.
16
- """
17
- self.file_path = file_path
18
- self.config = {}
19
- self.cache = {}
20
-
21
- def read_config(self) -> None:
22
- """Read the configuration file."""
23
- try:
24
- logging.info(f"Reading file: {self.file_path}")
25
-
26
- # Check if file exist
27
- if os.path.exists(self.file_path):
28
- with open(self.file_path, 'r') as f:
29
- self.config = json.load(f)
30
- logging.info("Configuration file loaded successfully.")
31
-
32
- # Download config.json
33
- else:
34
- logging.info("Configuration file does not exist. Downloading...")
35
- url = "https://raw.githubusercontent.com/Lovi-0/StreamingCommunity/refs/heads/main/config.json"
36
-
37
- with httpx.Client() as client:
38
- response = client.get(url)
39
-
40
- if response.status_code == 200:
41
- with open(self.file_path, 'w') as f:
42
- f.write(response.text)
43
-
44
- self.config = json.loads(response.text)
45
- logging.info("Configuration file downloaded and saved.")
46
-
47
- else:
48
- logging.error(f"Failed to download configuration file. Status code: {response.status_code}")
49
-
50
- except Exception as e:
51
- logging.error(f"Error reading configuration file: {e}")
52
-
53
- def read_key(self, section: str, key: str, data_type: type = str) -> Any:
54
- """Read a key from the configuration file.
55
-
56
- Parameters:
57
- - section (str): The section in the configuration file.
58
- - key (str): The key to be read.
59
- - data_type (type, optional): The expected data type of the key's value. Default is str.
60
-
61
- Returns:
62
- The value of the key converted to the specified data type.
63
- """
64
- cache_key = f"{section}.{key}"
65
- logging.info(f"Read key: {cache_key}")
66
-
67
- if cache_key in self.cache:
68
- return self.cache[cache_key]
69
-
70
- if section in self.config and key in self.config[section]:
71
- value = self.config[section][key]
72
- else:
73
- raise ValueError(f"Key '{key}' not found in section '{section}'")
74
-
75
- value = self._convert_to_data_type(value, data_type)
76
- self.cache[cache_key] = value
77
-
78
- return value
79
-
80
- def _convert_to_data_type(self, value: str, data_type: type) -> Any:
81
- """Convert the value to the specified data type.
82
-
83
- Parameters:
84
- - value (str): The value to be converted.
85
- - data_type (type): The expected data type.
86
-
87
- Returns:
88
- The value converted to the specified data type.
89
- """
90
- if data_type == int:
91
- return int(value)
92
- elif data_type == bool:
93
- return bool(value)
94
- elif data_type == list:
95
- return value if isinstance(value, list) else [item.strip() for item in value.split(',')]
96
- elif data_type == type(None):
97
- return None
98
- else:
99
- return value
100
-
101
- def get(self, section: str, key: str) -> Any:
102
- """Read a value from the configuration file.
103
-
104
- Parameters:
105
- - section (str): The section in the configuration file.
106
- - key (str): The key to be read.
107
-
108
- Returns:
109
- The value associated with the key.
110
- """
111
- return self.read_key(section, key)
112
-
113
- def get_int(self, section: str, key: str) -> int:
114
- """Read an integer value from the configuration file.
115
-
116
- Parameters:
117
- - section (str): The section in the configuration file.
118
- - key (str): The key to be read.
119
-
120
- Returns:
121
- int: The integer value.
122
- """
123
- return self.read_key(section, key, int)
124
-
125
- def get_float(self, section: str, key: str) -> int:
126
- """Read an float value from the configuration file.
127
-
128
- Parameters:
129
- - section (str): The section in the configuration file.
130
- - key (str): The key to be read.
131
-
132
- Returns:
133
- float: The float value.
134
- """
135
- return self.read_key(section, key, float)
136
-
137
- def get_bool(self, section: str, key: str) -> bool:
138
- """Read a boolean value from the configuration file.
139
-
140
- Parameters:
141
- - section (str): The section in the configuration file.
142
- - key (str): The key to be read.
143
-
144
- Returns:
145
- bool: The boolean value.
146
- """
147
- return self.read_key(section, key, bool)
148
-
149
- def get_list(self, section: str, key: str) -> List[str]:
150
- """Read a list value from the configuration file.
151
-
152
- Parameters:
153
- - section (str): The section in the configuration file.
154
- - key (str): The key to be read.
155
-
156
- Returns:
157
- list: The list value.
158
- """
159
- return self.read_key(section, key, list)
160
-
161
- def get_dict(self, section: str, key: str) -> dict:
162
- """Read a dictionary value from the configuration file.
163
-
164
- Parameters:
165
- - section (str): The section in the configuration file.
166
- - key (str): The key to be read.
167
-
168
- Returns:
169
- dict: The dictionary value.
170
- """
171
- return self.read_key(section, key, dict)
172
-
173
- def set_key(self, section: str, key: str, value: Any) -> None:
174
- """Set a key in the configuration file.
175
-
176
- Parameters:
177
- - section (str): The section in the configuration file.
178
- - key (str): The key to be set.
179
- - value (Any): The value to be associated with the key.
180
- """
181
- try:
182
- if section not in self.config:
183
- self.config[section] = {}
184
-
185
- self.config[section][key] = value
186
- cache_key = f"{section}.{key}"
187
- self.cache[cache_key] = value
188
- self.write_config()
189
-
190
- except Exception as e:
191
- print(f"Error setting key '{key}' in section '{section}': {e}")
192
-
193
- def write_config(self) -> None:
194
- """Write the configuration to the file."""
195
- try:
196
- with open(self.file_path, 'w') as f:
197
- json.dump(self.config, f, indent=4)
198
- except Exception as e:
199
- print(f"Error writing configuration file: {e}")
200
-
201
-
202
- # Initialize
203
- config_manager = ConfigManager()
204
- config_manager.read_config()
@@ -1,42 +0,0 @@
1
- # 21.06.24
2
-
3
- import os
4
- import inspect
5
-
6
-
7
- def get_call_stack():
8
- """
9
- Retrieves the current call stack with details about each call.
10
-
11
- This function inspects the current call stack and returns a list of dictionaries,
12
- where each dictionary contains details about a function call in the stack.
13
-
14
- Returns:
15
- list: A list of dictionaries, each containing the following keys:
16
- - function (str): The name of the function.
17
- - folder (str): The directory path of the script containing the function.
18
- - folder_base (str): The base name of the directory path.
19
- - script (str): The name of the script file containing the function.
20
- - line (int): The line number in the script where the function is defined.
21
- """
22
-
23
- stack = inspect.stack()
24
- call_stack = []
25
-
26
- for frame_info in stack:
27
- function_name = frame_info.function
28
- filename = frame_info.filename
29
- lineno = frame_info.lineno
30
- folder_name = os.path.dirname(filename)
31
- folder_base = os.path.basename(folder_name)
32
- script_name = os.path.basename(filename)
33
-
34
- call_stack.append({
35
- "function": function_name,
36
- "folder": folder_name,
37
- "folder_base": folder_base,
38
- "script": script_name,
39
- "line": lineno
40
- })
41
-
42
- return call_stack
@@ -1,20 +0,0 @@
1
- # 24.05.24
2
-
3
- class Colors:
4
- BLACK = "\033[30m"
5
- RED = "\033[31m"
6
- GREEN = "\033[32m"
7
- YELLOW = "\033[33m"
8
- BLUE = "\033[34m"
9
- MAGENTA = "\033[35m"
10
- CYAN = "\033[36m"
11
- LIGHT_GRAY = "\033[37m"
12
- DARK_GRAY = "\033[90m"
13
- LIGHT_RED = "\033[91m"
14
- LIGHT_GREEN = "\033[92m"
15
- LIGHT_YELLOW = "\033[93m"
16
- LIGHT_BLUE = "\033[94m"
17
- LIGHT_MAGENTA = "\033[95m"
18
- LIGHT_CYAN = "\033[96m"
19
- WHITE = "\033[97m"
20
- RESET = "\033[0m"
@@ -1,12 +0,0 @@
1
- # 24.02.24
2
-
3
- from rich.console import Console
4
- from rich.prompt import Prompt, Confirm
5
- from rich.panel import Panel
6
- from rich.table import Table
7
- from rich.text import Text
8
-
9
-
10
- # Variable
11
- msg = Prompt()
12
- console = Console()
@@ -1,275 +0,0 @@
1
- # 24.01.2024
2
-
3
- import os
4
- import platform
5
- import subprocess
6
- import zipfile
7
- import tarfile
8
- import logging
9
- import requests
10
- import shutil
11
- import glob
12
- from typing import Optional, Tuple
13
-
14
-
15
- # External library
16
- from rich.console import Console
17
- from rich.progress import Progress, SpinnerColumn, BarColumn, TextColumn, TimeRemainingColumn
18
-
19
-
20
- # Variable
21
- console = Console()
22
- FFMPEG_CONFIGURATION = {
23
- 'windows': {
24
- 'base_dir': lambda home: os.path.join(os.path.splitdrive(home)[0] + os.path.sep, 'binary'),
25
- 'download_url': 'https://github.com/GyanD/codexffmpeg/releases/download/{version}/ffmpeg-{version}-full_build.zip',
26
- 'file_extension': '.zip',
27
- 'executables': ['ffmpeg.exe', 'ffprobe.exe', 'ffplay.exe']
28
- },
29
- 'darwin': {
30
- 'base_dir': lambda home: os.path.join(home, 'Applications', 'binary'),
31
- 'download_url': 'https://evermeet.cx/ffmpeg/ffmpeg-{version}.zip',
32
- 'file_extension': '.zip',
33
- 'executables': ['ffmpeg', 'ffprobe', 'ffplay']
34
- },
35
- 'linux': {
36
- 'base_dir': lambda home: os.path.join(home, '.local', 'bin', 'binary'),
37
- 'download_url': 'https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-{arch}-static.tar.xz',
38
- 'file_extension': '.tar.xz',
39
- 'executables': ['ffmpeg', 'ffprobe', 'ffplay']
40
- }
41
- }
42
-
43
-
44
-
45
- class FFMPEGDownloader:
46
- def __init__(self):
47
- self.os_name = self._detect_system()
48
- self.arch = self._detect_arch()
49
- self.home_dir = os.path.expanduser('~')
50
- self.base_dir = self._get_base_directory()
51
-
52
- def _detect_system(self) -> str:
53
- """Detect and normalize operating system name."""
54
- system = platform.system().lower()
55
-
56
- if system in FFMPEG_CONFIGURATION:
57
- return system
58
-
59
- raise ValueError(f"Unsupported operating system: {system}")
60
-
61
- def _detect_arch(self) -> str:
62
- """
63
- Detect system architecture
64
- """
65
- machine = platform.machine().lower()
66
-
67
- arch_map = {
68
- 'amd64': 'x86_64',
69
- 'x86_64': 'x86_64',
70
- 'x64': 'x86_64',
71
- 'arm64': 'arm64',
72
- 'aarch64': 'arm64'
73
- }
74
-
75
- return arch_map.get(machine, machine)
76
-
77
- def _get_base_directory(self) -> str:
78
- """
79
- Get base directory for binaries
80
- """
81
- base_dir = FFMPEG_CONFIGURATION[self.os_name]['base_dir'](self.home_dir)
82
- os.makedirs(base_dir, exist_ok=True)
83
-
84
- return base_dir
85
-
86
- def _check_existing_binaries(self) -> Tuple[Optional[str], Optional[str]]:
87
- """
88
- Check if FFmpeg binaries already exist in the base directory
89
- """
90
- config = FFMPEG_CONFIGURATION[self.os_name]
91
- executables = config['executables']
92
-
93
- found_executables = []
94
- for executable in executables:
95
-
96
- # Search for exact executable in base directory
97
- exe_paths = glob.glob(os.path.join(self.base_dir, executable))
98
- if exe_paths:
99
- found_executables.append(exe_paths[0])
100
-
101
- # Return paths if both executables are found
102
- if len(found_executables) == len(executables):
103
- return tuple(found_executables)
104
-
105
- return None, None
106
-
107
- def _get_latest_version(self) -> str:
108
- """
109
- Get the latest FFmpeg version
110
- """
111
- try:
112
- version_url = 'https://www.gyan.dev/ffmpeg/builds/release-version'
113
- return requests.get(version_url).text.strip()
114
-
115
- except Exception as e:
116
- logging.error(f"Unable to get version: {e}")
117
- return None
118
-
119
- def _download_file(self, url: str, destination: str) -> bool:
120
- """
121
- Download with Rich progress bar
122
- """
123
- try:
124
- response = requests.get(url, stream=True)
125
- response.raise_for_status()
126
-
127
- total_size = int(response.headers.get('content-length', 0))
128
-
129
- with open(destination, 'wb') as file, \
130
- Progress(
131
- SpinnerColumn(),
132
- TextColumn("[progress.description]{task.description}"),
133
- BarColumn(),
134
- TextColumn("[progress.percentage]{task.percentage:>3.0f}%"),
135
- TimeRemainingColumn()
136
- ) as progress:
137
-
138
- download_task = progress.add_task("[green]Downloading FFmpeg", total=total_size)
139
-
140
- for chunk in response.iter_content(chunk_size=8192):
141
- size = file.write(chunk)
142
- progress.update(download_task, advance=size)
143
-
144
- return True
145
-
146
- except Exception as e:
147
- logging.error(f"Download error: {e}")
148
- return False
149
-
150
- def _extract_and_copy_binaries(self, archive_path: str) -> Tuple[Optional[str], Optional[str]]:
151
- """
152
- Extract archive and copy executables to base directory
153
- """
154
- try:
155
- # Temporary extraction path
156
- extraction_path = os.path.join(self.base_dir, 'temp_extract')
157
- os.makedirs(extraction_path, exist_ok=True)
158
-
159
- # Extract based on file type
160
- if archive_path.endswith('.zip'):
161
- with zipfile.ZipFile(archive_path, 'r') as zip_ref:
162
- zip_ref.extractall(extraction_path)
163
- elif archive_path.endswith('.tar.xz'):
164
- import lzma
165
- with lzma.open(archive_path, 'rb') as xz_file:
166
- with tarfile.open(fileobj=xz_file) as tar_ref:
167
- tar_ref.extractall(extraction_path)
168
-
169
- # Find and copy executables
170
- config = FFMPEG_CONFIGURATION[self.os_name]
171
- executables = config['executables']
172
-
173
- found_paths = []
174
- for executable in executables:
175
- # Find executable in extracted files
176
- exe_paths = glob.glob(os.path.join(extraction_path, '**', executable), recursive=True)
177
-
178
- if exe_paths:
179
- # Copy to base directory
180
- dest_path = os.path.join(self.base_dir, executable)
181
- shutil.copy2(exe_paths[0], dest_path)
182
-
183
- # Set execution permissions for Unix-like systems
184
- if self.os_name != 'windows':
185
- os.chmod(dest_path, 0o755)
186
-
187
- found_paths.append(dest_path)
188
-
189
- # Clean up temporary extraction directory
190
- shutil.rmtree(extraction_path, ignore_errors=True)
191
-
192
- # Remove downloaded archive
193
- os.remove(archive_path)
194
-
195
- # Return paths if both executables found
196
- if len(found_paths) == len(executables):
197
- return tuple(found_paths)
198
-
199
- return None, None
200
-
201
- except Exception as e:
202
- logging.error(f"Extraction/copy error: {e}")
203
- return None, None
204
-
205
- def download(self) -> Tuple[Optional[str], Optional[str]]:
206
- """
207
- Main download procedure
208
- Returns paths of ffmpeg and ffprobe
209
- """
210
- # First, check if binaries already exist in base directory
211
- existing_ffmpeg, existing_ffprobe, existing_ffplay = self._check_existing_binaries()
212
- if existing_ffmpeg and existing_ffprobe:
213
- return existing_ffmpeg, existing_ffprobe
214
-
215
- # Get latest version
216
- version = self._get_latest_version()
217
- if not version:
218
- logging.error("Cannot proceed: version not found")
219
- return None, None
220
-
221
- # Prepare configurations
222
- config = FFMPEG_CONFIGURATION[self.os_name]
223
-
224
- # Build download URL
225
- download_url = config['download_url'].format(
226
- version=version,
227
- arch=self.arch
228
- )
229
-
230
- # Download path
231
- download_path = os.path.join(
232
- self.base_dir,
233
- f'ffmpeg-{version}{config["file_extension"]}'
234
- )
235
-
236
- # Download
237
- console.print(
238
- f"[bold blue]Downloading FFmpeg from:[/] {download_url}",
239
- )
240
- if not self._download_file(download_url, download_path):
241
- return None, None
242
-
243
- # Extract and copy binaries
244
- ffmpeg_path, ffprobe_path = self._extract_and_copy_binaries(download_path)
245
-
246
- if ffmpeg_path and ffprobe_path:
247
- return ffmpeg_path, ffprobe_path
248
-
249
- logging.error("FFmpeg executables not found")
250
- return None, None
251
-
252
-
253
- def check_ffmpeg():
254
- try:
255
- # First, use 'where' command to check existing binaries on Windows
256
- if platform.system().lower() == 'windows':
257
- 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
258
- 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
259
-
260
- if ffmpeg_path and ffprobe_path:
261
- return ffmpeg_path, ffprobe_path
262
-
263
- # Fallback to which/shutil method for Unix-like systems
264
- ffmpeg_path = shutil.which('ffmpeg')
265
- ffprobe_path = shutil.which('ffprobe')
266
-
267
- if ffmpeg_path and ffprobe_path:
268
- return ffmpeg_path, ffprobe_path
269
-
270
- downloader = FFMPEGDownloader()
271
- return downloader.download()
272
-
273
- except Exception as e:
274
- logging.error(f"Error checking FFmpeg: {e}")
275
- return None, None