2# +==== BEGIN CatFeeder =================+
5# ...............)..(.')
7# ...............\(__)|
8# Inspired by Joan Stark
9# source https://www.asciiart.eu/
14# CREATION DATE: 11-10-2025
15# LAST Modified: 1:59:55 17-01-2026
17# This is the backend server in charge of making the actual website work.
20# ArchitectureNotSupported: This means that the architecure you are running this file on is unknown to it.
21# PackageNotSupported: This class informs the user that the system architecture they are using is not supported by this script
22# PackageNotInstalled: This class informs the user that the ff dependency(ies) they required does not exist.
23# NotImplementedError: This class inform the user that a required operation is not implemented and thus not completable.
24# ValueError: This is used for any error that does not fit into any specific class.
26# COPYRIGHT: (c) Cat Feeder
27# PURPOSE: File in charge of downloading and extracting the FFmpeg binaries for the current system.
29# +==== END CatFeeder =================+
39from typing
import Union, Callable, Optional
40from pathlib
import Path
as PPath
42from pathlib
import Path
55 here = Path(__file__).resolve()
56 for parent
in here.parents:
57 candidate = parent /
"pyaudioop.py"
58 if candidate.exists():
59 spec = importlib.util.spec_from_file_location(
60 "pyaudioop", str(candidate))
61 if spec
and spec.loader:
62 mod = importlib.util.module_from_spec(spec)
63 spec.loader.exec_module(mod)
64 sys.modules.setdefault(
"pyaudioop", mod)
65 sys.modules.setdefault(
"audioop", mod)
72 sys.modules.setdefault(
"audioop", _pa)
77from pydub
import AudioSegment, playback
79from .ff_exceptions
import ArchitectureNotSupported, PackageNotInstalled, PackageNotSupported
80from .
import ff_constants
as CONST
81from ..core
import FinalClass
86 The class in charge of downloading and extracting the FFmpeg binaries for the current system.
87 This class is there so that ffmpeg (and it's familly binaries) ca be run without requiring the user to install it.
90 ArchitectureNotSupported: This means that the architecure you are running this file on is unknown to it.
91 PackageNotSupported: This class informs the user that the system architecture they are using is not supported by this script
92 PackageNotInstalled: This class informs the user that the ff dependency(ies) they required does not exist.
93 NotImplementedError: This class inform the user that a required operation is not implemented and thus not completable.
94 ValueError: This is used for any error that does not fit into any specific class.
95 RuntimeError: Any error that was unexpected and caused the program to stop.
98 available_binaries: list = [CONST.FFMPEG_KEY,
99 CONST.FFPROBE_KEY, CONST.FFPLAY_KEY]
101 _instance: Optional[
"FFMPEGDownloader"] =
None
103 def __new__(cls, *args, **kwargs) -> "FFMPEGDownloader":
108 def __init__(self, cwd: str = os.getcwd(), query_timeout: int = 10, success: int = 0, error: int = 84, debug: bool =
False):
126 This is a rebind function of the process_file_path defined globaly
128 return CONST.process_file_path(
135 Generates a pure tone audio sample using the pydub library.
138 tone (int): Frequency of the tone in Hz.
141 AudioSegment: The generated audio segment.
143 print(f
"Generating audio sample for tone {tone} Hz")
149 num_samples = int(sample_rate * duration)
151 for i
in range(num_samples):
154 sample_value = 0.5 * math.sin(2 * math.pi * frequency * t)
156 waveform_integer = int(sample_value * 32767)
157 samples.append(waveform_integer)
160 raw_audio_data = struct.pack(
"<" +
"h" * len(samples), *samples)
163 audio_segment = AudioSegment(
165 frame_rate=sample_rate,
169 print(
"Audio sample generated")
175 Plays an audio sample using the pydub library.
178 audio_segment (AudioSegment): The audio segment to play.
180 print(
"Playing audio sample")
181 playback.play(audio_segment)
182 print(
"Audio sample played")
187 Saves an audio sample to a file using the pydub library.
190 audio_segment (AudioSegment): _description_
191 file_path (str): _description_
194 _type_: _description_
196 print(f
"Saving audio sample to {file_path}")
197 audio_segment.export(file_path, format=
"wav")
198 print(f
"Audio sample saved to {file_path}")
205 file_path (_type_): _description_
206 destination (_type_): _description_
209 NotImplementedError: _description_
210 ValueError: _description_
212 print(f
"Extracting {file_path} to {destination}")
213 if file_path.endswith(
".zip"):
214 with zipfile.ZipFile(file_path,
'r')
as zip_ref:
215 zip_ref.extractall(destination)
216 elif file_path.endswith(
".tar.xz"):
217 with tarfile.open(file_path,
'r:xz')
as tar_ref:
218 tar_ref.extractall(destination)
219 elif file_path.endswith(
".7z"):
221 raise NotImplementedError(
"7z extraction not implemented")
223 raise ValueError(
"Unsupported file format")
224 print(
"Extraction complete")
231 ArchitectureNotSupported: _description_
232 PackageNotSupported: _description_
233 PackageNotInstalled: _description_
234 PackageNotSupported: _description_
235 PackageNotInstalled: _description_
240 return platform.system().lower()
247 NotImplementedError: _description_
248 ValueError: _description_
253 return platform.architecture()[0]
260 path (_type_): _description_
263 NotImplementedError: _description_
264 ValueError: _description_
266 if not os.path.exists(path):
267 print(f
"Creating directory: {path}")
268 os.makedirs(path, exist_ok=
True)
271 def _download_file(file_url: str, file_path: str, query_timeout: int = 10) ->
None:
275 file_url (_type_): _description_
276 file_path (_type_): _description_
279 PackageNotInstalled: _description_
281 print(f
"Downloading FFmpeg from {file_url}")
282 response_data = requests.get(file_url, timeout=query_timeout)
283 if response_data.status_code != 200:
285 print(f
"Saving to {file_path}")
286 with open(file_path,
"wb")
as file_descriptor:
287 file_descriptor.write(response_data.content)
288 print(
"Download complete")
295 file_path (str, optional): _description_. Defaults to None.
297 if file_path
is None:
299 print(f
"Giving executable rights to {file_path}")
300 if os.path.exists(file_path):
301 os.chmod(file_path, 0o755)
302 print(f
"Executable rights granted to {file_path}")
304 print(f
"{file_path} does not exist, could not grant executable rights")
311 old_name (str): _description_
312 new_name (str): _description_
315 if os.path.exists(old_name):
316 print(f
"Renaming {old_name} to {new_name}")
317 shutil.move(old_name, new_name)
320 def get_ff_family_path(download_if_not_present: bool =
True, cwd: str = os.getcwd(), query_timeout: int = 10, success: int = 0, error: int = 1, debug: bool =
False) -> str:
322 The general path for ff related libraries
325 download_if_not_present (bool, optional): _description_. Defaults to True.
326 cwd (str, optional): _description_. Defaults to os.getcwd().
327 query_timeout (int, optional): _description_. Defaults to 10.
328 success (int, optional): _description_. Defaults to 0.
329 error (int, optional): _description_. Defaults to 1.
330 debug (bool, optional): _description_. Defaults to False.
333 PackageNotSupported: _description_
334 PackageNotSupported: _description_
335 PackageNotInstalled: _description_
336 ArchitectureNotSupported: _description_
337 PackageNotSupported: _description_
338 RuntimeError: _description_
343 system = FFMPEGDownloader.get_system_name()
344 if system
not in (
"windows",
"linux",
"darwin"):
346 precompiled_ffmpeg = os.path.join(cwd,
"ffmpeg", system)
347 precompiled_ffprobe = os.path.join(cwd,
"ffprobe", system)
348 precompiled_ffplay = os.path.join(cwd,
"ffplay", system)
349 if not os.path.isdir(precompiled_ffmpeg)
or not os.path.isdir(precompiled_ffprobe)
or not os.path.isdir(precompiled_ffplay):
350 if not download_if_not_present:
352 print(
"FF_family not found in precompiled paths, setting up")
355 query_timeout=query_timeout,
362 raise RuntimeError(
"FF_family could not be installed")
363 if os.path.exists(cwd)
and os.path.isdir(cwd):
368 def get_ffmpeg_binary_path(download_if_not_present: bool =
True, cwd: str = os.getcwd(), query_timeout: int = 10, success: int = 0, error: int = 1, debug: bool =
False) -> str:
372 download_if_not_present (bool, optional): _description_. Defaults to True.
373 cwd (str, optional): _description_. Defaults to os.getcwd().
374 query_timeout (int, optional): _description_. Defaults to 10.
375 success (int, optional): _description_. Defaults to 0.
376 error (int, optional): _description_. Defaults to 1.
377 debug (bool, optional): _description_. Defaults to False.
380 PackageNotSupported: _description_
381 PackageNotSupported: _description_
382 PackageNotInstalled: _description_
387 system = FFMPEGDownloader.get_system_name()
388 ffmpeg_system_path = FFMPEGDownloader.get_ff_family_path(
389 download_if_not_present=download_if_not_present,
391 query_timeout=query_timeout,
396 ffmpeg_precompiled_path = os.path.join(
397 ffmpeg_system_path,
"ffmpeg", system
400 if system ==
"windows":
402 ffmpeg_precompiled_path,
405 elif system ==
"linux":
407 ffmpeg_precompiled_path,
410 FFMPEGDownloader._grant_executable_rights(path)
411 elif system ==
"darwin":
413 ffmpeg_precompiled_path,
416 FFMPEGDownloader._grant_executable_rights(path)
419 print(f
"FFmpeg path = '{path}'")
420 if os.path.exists(path):
421 if os.path.isfile(path):
427 def get_ffplay_binary_path(download_if_not_present: bool =
True, cwd: str = os.getcwd(), query_timeout: int = 10, success: int = 0, error: int = 1, debug: bool =
False) -> str:
431 download_if_not_present (bool, optional): _description_. Defaults to True.
432 cwd (str, optional): _description_. Defaults to os.getcwd().
433 query_timeout (int, optional): _description_. Defaults to 10.
434 success (int, optional): _description_. Defaults to 0.
435 error (int, optional): _description_. Defaults to 1.
436 debug (bool, optional): _description_. Defaults to False.
439 PackageNotSupported: _description_
440 PackageNotSupported: _description_
441 PackageNotInstalled: _description_
446 system = FFMPEGDownloader.get_system_name()
447 ffmpeg_system_path = FFMPEGDownloader.get_ff_family_path(
448 download_if_not_present=download_if_not_present,
450 query_timeout=query_timeout,
455 ffmpeg_precompiled_path = os.path.join(
456 ffmpeg_system_path,
"ffplay", system
459 if system ==
"windows":
461 ffmpeg_precompiled_path,
464 elif system ==
"linux":
466 ffmpeg_precompiled_path,
469 FFMPEGDownloader._grant_executable_rights(path)
470 elif system ==
"darwin":
472 ffmpeg_precompiled_path,
475 FFMPEGDownloader._grant_executable_rights(path)
478 print(f
"FFplay path = '{path}'")
479 if os.path.exists(path):
480 if os.path.isfile(path):
486 def get_ffprobe_binary_path(download_if_not_present: bool =
True, cwd: str = os.getcwd(), query_timeout: int = 10, success: int = 0, error: int = 1, debug: bool =
False) -> str:
490 download_if_not_present (bool, optional): _description_. Defaults to True.
491 cwd (str, optional): _description_. Defaults to os.getcwd().
492 query_timeout (int, optional): _description_. Defaults to 10.
493 success (int, optional): _description_. Defaults to 0.
494 error (int, optional): _description_. Defaults to 1.
495 debug (bool, optional): _description_. Defaults to False.
498 PackageNotSupported: _description_
499 PackageNotSupported: _description_
500 PackageNotInstalled: _description_
505 system = FFMPEGDownloader.get_system_name()
506 ffmpeg_system_path = FFMPEGDownloader.get_ff_family_path(
507 download_if_not_present=download_if_not_present,
509 query_timeout=query_timeout,
514 ffmpeg_precompiled_path = os.path.join(
515 ffmpeg_system_path,
"ffprobe", system
518 if system ==
"windows":
520 ffmpeg_precompiled_path,
523 elif system ==
"linux":
525 ffmpeg_precompiled_path,
528 FFMPEGDownloader._grant_executable_rights(path)
529 elif system ==
"darwin":
531 ffmpeg_precompiled_path,
534 FFMPEGDownloader._grant_executable_rights(path)
537 print(f
"FFprobe path = '{path}'")
538 if os.path.exists(path):
539 if os.path.isfile(path):
545 def add_ff_family_to_path(ffmpeg_path: Union[str,
None] =
None, ffplay_path: Union[str,
None] =
None, ffprobe_path: Union[str,
None] =
None, download_if_not_present: bool =
True, cwd: str = os.getcwd(), query_timeout: int = 10, success: int = 0, error: int = 1, debug: bool =
False) ->
None:
547 Add the FF family to the system path.
550 ffmpeg_path (str, optional): _description_. Defaults to None.
551 ffplay_path (str, optional): _description_. Defaults to None.
552 ffprobe_path (str, optional): _description_. Defaults to None.
553 download_if_not_present (bool, optional): _description_. Defaults to True.
554 cwd (str, optional): _description_. Defaults to os.getcwd().
555 query_timeout (int, optional): _description_. Defaults to 10.
556 success (int, optional): _description_. Defaults to 0.
557 error (int, optional): _description_. Defaults to 1.
558 debug (bool, optional): _description_. Defaults to False.
560 if ffmpeg_path
is None:
562 print(
"Getting ffmpeg path")
563 ffmpeg_path = FFMPEGDownloader.get_ffmpeg_binary_path(
564 download_if_not_present=download_if_not_present,
566 query_timeout=query_timeout,
571 except (PackageNotSupported, PackageNotSupported, PackageNotInstalled)
as e:
572 print(f
"Failed to expose ffmpeg, error: {e}")
573 if ffplay_path
is None:
575 print(
"Getting ffplay path")
576 ffplay_path = FFMPEGDownloader.get_ffplay_binary_path(
577 download_if_not_present=download_if_not_present,
579 query_timeout=query_timeout,
584 except (PackageNotSupported, PackageNotSupported, PackageNotInstalled)
as e:
585 print(f
"Failed to expose ffplay, error: {e}")
586 if ffprobe_path
is None:
588 print(
"Getting ffprobe path")
589 ffprobe_path = FFMPEGDownloader.get_ffprobe_binary_path(
590 download_if_not_present=download_if_not_present,
592 query_timeout=query_timeout,
597 except (PackageNotSupported, PackageNotSupported, PackageNotInstalled)
as e:
598 print(f
"Failed to expose ffprobe, error: {e}")
599 msg =
"Converting the direct paths (which include the binaries at the end) into system paths (which only include the directories containing the binaries)"
602 ffmpeg_path = os.path.abspath(os.path.dirname(ffmpeg_path))
604 ffplay_path = os.path.abspath(os.path.dirname(ffplay_path))
606 ffprobe_path = os.path.abspath(os.path.dirname(ffprobe_path))
607 print(
"Adding FF family to PATH")
608 for ff_path
in [ffmpeg_path, ffplay_path, ffprobe_path]:
610 print(
"Path not defined, skipping")
612 if ff_path
not in os.environ[
"PATH"]:
613 print(f
"Adding {ff_path} to PATH")
614 os.environ[
"PATH"] = ff_path + os.pathsep + os.environ[
"PATH"]
615 print(f
"Added {ff_path} to PATH")
617 print(f
"{ff_path} is already in PATH")
618 if ff_path
not in sys.path:
619 print(f
"Adding {ff_path} to sys.path")
620 sys.path.append(ff_path)
621 print(f
"Added {ff_path} to sys.path")
623 print(f
"{ff_path} is already in sys.path")
629 NotImplementedError: _description_
630 ValueError: _description_
632 msg =
"Current system (before filtering): "
633 msg += f
"{self.system} {self.architecture}"
641 msg =
"Current system (after filtering): "
642 msg += f
"{self.system} {self.architecture}"
649 binary_name (str, optional): _description_. Defaults to FFMPEG_KEY.
652 ArchitectureNotSupported: _description_
653 PackageNotSupported: _description_
654 PackageNotInstalled: _description_
656 if binary_name
not in CONST.BUNDLE_DOWNLOAD:
658 if self.
system in CONST.BUNDLE_DOWNLOAD[binary_name]:
660 tmp_path: Union[str, Callable] = CONST.BUNDLE_DOWNLOAD[binary_name][
662 if callable(tmp_path):
666 self.
file_url = CONST.BUNDLE_DOWNLOAD[binary_name][
678 PackageNotSupported: _description_
679 PackageNotSupported: _description_
680 PackageNotInstalled: _description_
683 print(f
"Downloading {binary}")
688 self.
fold_path = os.path.dirname(__file__)
691 print(f
"{binary} already downloaded")
694 print(f
"{binary} contains corrupted data, skipping")
704 This is the function that will install all the FF_family binaries if they are already downloaded.
707 print(f
"Installing {binary}")
711 f
"{binary} filepath is empty, the binary meta data appears to be corrupted, skipping")
714 extract_to = os.path.join(
718 final_name = os.path.join(
722 if os.path.isdir(final_name):
723 print(f
"{binary} already installed")
725 if binary == CONST.FFPLAY_KEY:
726 extract_to = os.path.join(
733 new_folder_path = os.path.join(
738 old_folder_path = os.path.join(
742 if binary != CONST.FFPLAY_KEY:
747 print(f
"{binary} installed")
749 def main(self, audio_segment_node: Union[AudioSegment,
None] =
None) -> int:
751 The function in charge of downloading and extracting the FF_family binaries for the current system.
754 PackageNotInstalled: _description_
755 PackageNotSupported: _description_
756 PackageNotInstalled: _description_
763 print(f
"FF_family already installed at {found_path}")
765 download_if_not_present=
False,
773 download_if_not_present=
False,
781 download_if_not_present=
False,
788 print(
"Updating pydub ffmpeg path")
789 if audio_segment_node
is not None:
790 audio_segment_node.ffmpeg = ffmpeg_path
791 AudioSegment.ffmpeg = ffmpeg_path
793 ffmpeg_path, ffplay_path, ffprobe_path, download_if_not_present=
False
795 print(
"FF_family already installed and ready to use!")
797 except PackageNotInstalled:
798 print(
"FFmpeg not found. Installing...")
799 except PackageNotSupported
as e:
801 "FFmpeg cannot be installed on this device because the system is unknown to this script."
807 download_if_not_present=
False,
815 download_if_not_present=
False,
823 download_if_not_present=
False,
830 print(f
"FFmpeg installed at {ffmpeg_path}")
831 print(f
"FFplay installed at {ffplay_path}")
832 print(f
"FFprobe installed at {ffprobe_path}")
833 if audio_segment_node
is not None:
834 audio_segment_node.ffmpeg = ffmpeg_path
835 AudioSegment.ffmpeg = ffmpeg_path
837 ffmpeg_path, ffplay_path, ffprobe_path, download_if_not_present=
False
839 print(
"FF_family installed and ready to use!")
843if __name__ ==
"__main__":
847 AUDIO_SAMPLE_PATH = f
"./{AUDIO_WAVE}.wav"
848 AUDIO_SAMPLE = FDI.generate_audio_sample(AUDIO_WAVE)
849 FDI.save_audio_sample(AUDIO_SAMPLE, AUDIO_SAMPLE_PATH)
850 FDI.play_audio_sample(AUDIO_SAMPLE)
None _extract_package(str file_path, str destination)
None add_ff_family_to_path(Union[str, None] ffmpeg_path=None, Union[str, None] ffplay_path=None, Union[str, None] ffprobe_path=None, bool download_if_not_present=True, str cwd=os.getcwd(), int query_timeout=10, int success=0, int error=1, bool debug=False)
str get_ffplay_binary_path(bool download_if_not_present=True, str cwd=os.getcwd(), int query_timeout=10, int success=0, int error=1, bool debug=False)
"FFMPEGDownloader" __new__(cls, *args, **kwargs)
Union[str, None] new_folder_path
None _download_file(str file_url, str file_path, int query_timeout=10)
str get_ffprobe_binary_path(bool download_if_not_present=True, str cwd=os.getcwd(), int query_timeout=10, int success=0, int error=1, bool debug=False)
Union[str, None] file_path
Union[str, None] extracted_folder
AudioSegment generate_audio_sample(int tone)
__init__(self, str cwd=os.getcwd(), int query_timeout=10, int success=0, int error=84, bool debug=False)
None _grant_executable_rights(Union[str, None] file_path=None)
None _clean_platform_name(self)
None _get_all_binaries(self)
None save_audio_sample(AudioSegment audio_segment, str file_path)
Union[str, None] file_url
None _create_path_if_not_exists(str path)
int main(self, Union[AudioSegment, None] audio_segment_node=None)
None play_audio_sample(AudioSegment audio_segment)
None _rename_extracted_folder(str old_name, str new_name)
Union[str, None] fold_path
None _install_all_binaries(self)
str get_ffmpeg_binary_path(bool download_if_not_present=True, str cwd=os.getcwd(), int query_timeout=10, int success=0, int error=1, bool debug=False)
str process_file_path(*Union[str, PPath] args, Union[str, PPath, None] cwd=None)
str get_ff_family_path(bool download_if_not_present=True, str cwd=os.getcwd(), int query_timeout=10, int success=0, int error=1, bool debug=False)
None _get_correct_download_and_file_path(self, str binary_name=CONST.FFMPEG_KEY)