ffmpeg-update 1.4.1__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.
@@ -0,0 +1,144 @@
1
+ Metadata-Version: 2.4
2
+ Name: ffmpeg-update
3
+ Version: 1.4.1
4
+ Summary: CLI tool to manage FFmpeg static binaries
5
+ Project-URL: homepage, https://github.com/pantheraleo-7/ffmpeg-update
6
+ Author: Asadullah Shaikh
7
+ Keywords: binaries,ffmpeg,installer,static
8
+ Classifier: Development Status :: 5 - Production/Stable
9
+ Classifier: Environment :: Console
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: Intended Audience :: End Users/Desktop
12
+ Classifier: Intended Audience :: Information Technology
13
+ Classifier: Intended Audience :: System Administrators
14
+ Classifier: Operating System :: MacOS
15
+ Classifier: Operating System :: POSIX
16
+ Classifier: Operating System :: POSIX :: Linux
17
+ Classifier: Operating System :: Unix
18
+ Classifier: Programming Language :: Python :: 3
19
+ Classifier: Topic :: Multimedia :: Video
20
+ Classifier: Topic :: System :: Installation/Setup
21
+ Classifier: Topic :: System :: Software Distribution
22
+ Classifier: Topic :: Utilities
23
+ Requires-Python: >=3.9
24
+ Requires-Dist: fire
25
+ Requires-Dist: requests
26
+ Requires-Dist: tqdm
27
+ Description-Content-Type: text/markdown
28
+
29
+ # FFUp
30
+
31
+ A Python CLI tool to manage FFmpeg static binaries on Unix-like systems. It fetches the latest builds published by [Martin Riedl](https://ffmpeg.martin-riedl.de/).
32
+
33
+ ## Features
34
+
35
+ - Install static builds of FFmpeg, FFprobe, and/or FFplay.
36
+ - Update to the latest version.
37
+ - Check for update.
38
+ - Uninstall from the system.
39
+ - Supports custom installation paths.
40
+ - Supports both **Linux** and **macOS** (Windows builds are not available upstream).
41
+
42
+ ## Installation
43
+
44
+ ```bash
45
+ pip install ffmpeg-update
46
+ ```
47
+
48
+ ## Usage
49
+
50
+ ### With `ffup`:
51
+
52
+ The installation provides the `ffup` command.
53
+
54
+ - **Install**:
55
+ ```bash
56
+ ffup install [--dir <custom-path>]
57
+ ```
58
+ - **Update**:
59
+ ```bash
60
+ ffup update [--dir <custom-path>] [--dry-run]
61
+ ```
62
+ - **Check**:
63
+ ```bash
64
+ ffup check [--dir <custom-path>]
65
+ ```
66
+ - **Uninstall**:
67
+ ```bash
68
+ ffup uninstall [--dir <custom-path>]
69
+ ```
70
+
71
+ ### With `python`:
72
+
73
+ Alternatively, the module can be invoked directly.
74
+
75
+ ```bash
76
+ python -m ffup <command>
77
+ ```
78
+
79
+ ## Documentation
80
+
81
+ ### CLI Commands, Their Flags and Environment Variables
82
+
83
+ 1. **Install**:
84
+
85
+ ```bash
86
+ ffup install [--dir <custom-path>]
87
+ ```
88
+
89
+ - Downloads and installs the binary.
90
+ - Flags and Environment variables:
91
+ - `--dir <custom-path>`: Specifies the installation directory.
92
+ - `$XDG_BIN_HOME`: Used as the installation directory if `--dir` is not specified.
93
+ - Defaults to `~/.local/bin` if none of the above is defined.
94
+
95
+ 2. **Update**:
96
+
97
+ ```bash
98
+ ffup update [--dir <custom-path>] [--dry-run]
99
+ ```
100
+
101
+ - Updates the binary to the latest version.
102
+ - Flags:
103
+ - `--dir <custom-path>`: Specifies the directory where the binary is installed.
104
+ - Defaults to the first executable found on the `$PATH`.
105
+ - `--dry-run`: Only checks for update, skips download and install.
106
+
107
+ 3. **Check**:
108
+
109
+ ```bash
110
+ ffup check [--dir <custom-path>]
111
+ ```
112
+
113
+ - Checks for update.
114
+ - Same as `ffup update [--dir <custom-path>] --dry-run`.
115
+ - Flags:
116
+ - `--dir <custom-path>`: Specifies the directory where the binary is installed.
117
+ - Defaults to the first executable found on the `$PATH`.
118
+
119
+ 4. **Uninstall**:
120
+
121
+ ```bash
122
+ ffup uninstall [--dir <custom-path>]
123
+ ```
124
+
125
+ - Removes the installed binary.
126
+ - Flags:
127
+ - `--dir <custom-path>`: Specifies the directory where the binary is installed.
128
+ - Defaults to the first executable found on the `$PATH`.
129
+
130
+ ### Global Flags and Their Environment Variables
131
+
132
+ - `--sys` or `$FF_SYS`: Specifies the platform name (`macos`, `linux`). Default is to detect using `platform` stdlib.
133
+ - `--arch` or `$FF_ARCH`: Specifies the platform architecture (`arm64`, `amd64`). Default is to detect using `platform` stdlib.
134
+ - `--repo` or `$FF_REPO`: Specifies the static build type (`snapshot`, `release`). Defaults to `snapshot`.
135
+ - `--bin` or `$FF_BIN`: Specifies the binary name (`ffmpeg`, `ffprobe`, `ffplay`). Defaults to `ffmpeg`.
136
+
137
+ > **Note:** Flags have precedence over their respective environment variables.
138
+
139
+ > **Note:** Command arguments may be given positionally. Global arguments are always specified with their respective keywords (flags).
140
+
141
+ ### Error Handling
142
+
143
+ - Permission error triggers automatic escalation via `sudo` [and, consequently, prompts the user for password at `stdin`].
144
+ - Path handling checks are exhaustive, with diagnostics logged to `stdout` and `stderr` as appropriate.
@@ -0,0 +1,7 @@
1
+ ffup/__init__.py,sha256=JOvFvqYGeEJu1XMhcDSw-tVGZhGxX1AjSoX384iB1VU,30
2
+ ffup/__main__.py,sha256=bYt9eEaoRQWdejEHFD8REx9jxVEdZptECFsV7F49Ink,30
3
+ ffup/cli.py,sha256=JrC2y5YAIhdOLs_YhfbXDb3--p2d2KuTteUD-2U5vvY,4781
4
+ ffmpeg_update-1.4.1.dist-info/METADATA,sha256=OTKG0q1VFXYtAKK7KiyilrmJ3jul9IucFG0pXwA8cKc,4308
5
+ ffmpeg_update-1.4.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
6
+ ffmpeg_update-1.4.1.dist-info/entry_points.txt,sha256=1PwlMliyeJa6o_dC6u0A0fPLu7fE72m2LeB671z6_IQ,35
7
+ ffmpeg_update-1.4.1.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.27.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ ffup = ffup:main
ffup/__init__.py ADDED
@@ -0,0 +1 @@
1
+ from .cli import main as main
ffup/__main__.py ADDED
@@ -0,0 +1,3 @@
1
+ from .cli import main
2
+
3
+ main()
ffup/cli.py ADDED
@@ -0,0 +1,146 @@
1
+ import os
2
+ import platform
3
+ import re
4
+ import shutil
5
+ import subprocess
6
+ import sys
7
+ import tempfile
8
+ import zipfile
9
+ from pathlib import Path
10
+
11
+ import fire
12
+ import requests
13
+ from tqdm import tqdm
14
+
15
+
16
+ class FFUp:
17
+ def __init__(self, sys=None, arch=None, repo=None, bin=None):
18
+ self.sys = sys or os.getenv('FF_SYS') \
19
+ or platform.system().replace('Darwin', 'macOS').lower()
20
+
21
+ self.arch = arch or os.getenv('FF_ARCH') \
22
+ or ('arm64' if platform.machine() in ['arm64', 'aarch64'] else 'amd64')
23
+
24
+ self.repo = repo or os.getenv('FF_REPO') or 'snapshot'
25
+ self.bin = bin or os.getenv('FF_BIN') or 'ffmpeg'
26
+
27
+ self.URL = f'https://ffmpeg.martin-riedl.de/redirect/latest/{self.sys}/{self.arch}/{self.repo}/{self.bin}.zip'
28
+ self._TMPDIR = tempfile.TemporaryDirectory()
29
+
30
+ def __del__(self):
31
+ self._TMPDIR.cleanup()
32
+
33
+ def check(self, dir=None):
34
+ self.update(dir=dir, dry_run=True)
35
+
36
+ def install(self, dir=None):
37
+ path = Path(dir or os.getenv('XDG_BIN_HOME') or os.path.expanduser('~/.local/bin'), self.bin)
38
+
39
+ if shutil.which(self.bin) is not None:
40
+ print('Warning: found an existing installation on the `PATH`.')
41
+
42
+ if path.exists():
43
+ print('Error: found an existing installation at the given path.', file=sys.stderr)
44
+ sys.exit(1)
45
+
46
+ self._latest()
47
+ file = self._download()
48
+ self._install(file, path)
49
+
50
+ def update(self, dir=None, dry_run=False):
51
+ path = self._getpath(dir)
52
+ self._current(path)
53
+ self._latest()
54
+ if self.current_version!=self.latest_version:
55
+ print('Update available.')
56
+ if not dry_run:
57
+ file = self._download()
58
+ self._install(file, path)
59
+ else:
60
+ print('Already up to date.')
61
+
62
+ def uninstall(self, dir=None):
63
+ path = self._getpath(dir)
64
+ self._uninstall(path)
65
+
66
+ def _getpath(self, dir):
67
+ if dir is None:
68
+ path = shutil.which(self.bin)
69
+ if path is None:
70
+ print('Error: no installation found on the `PATH`.', file=sys.stderr)
71
+ sys.exit(1)
72
+ else:
73
+ path = Path(dir, self.bin)
74
+ if not path.exists():
75
+ print('Error: no installation found at the given path.', file=sys.stderr)
76
+ sys.exit(1)
77
+
78
+ return Path(path)
79
+
80
+ def _current(self, path):
81
+ output = subprocess.check_output([path, '-version'], text=True)
82
+
83
+ match = re.search(r'version (N-\d+-\w+|\d\.\d)', output)
84
+ if match is None:
85
+ print(f'Error: failed to parse current version from `{path} -version` output.', file=sys.stderr)
86
+ sys.exit(1)
87
+
88
+ self.current_version = match.group(1)
89
+ print('Current version:', self.current_version)
90
+
91
+ def _latest(self):
92
+ response = requests.get(self.URL, allow_redirects=False)
93
+ response.raise_for_status()
94
+
95
+ if response.status_code==307:
96
+ match = re.search(r'_(N-\d+-\w+|\d\.\d)', response.headers['location'])
97
+ if match is None:
98
+ print('Error: failed to parse latest version from redirected url.', file=sys.stderr)
99
+ sys.exit(1)
100
+
101
+ self.latest_version = match.group(1)
102
+ print('Latest version:', self.latest_version)
103
+
104
+ else:
105
+ print('Error: unexpected', response, file=sys.stderr)
106
+ print('Headers:', response.headers, file=sys.stderr)
107
+ sys.exit(1)
108
+
109
+ def _download(self):
110
+ with requests.get(self.URL, stream=True) as response:
111
+ response.raise_for_status()
112
+ bar = tqdm(
113
+ total=int(response.headers['content-length']),
114
+ unit='B', unit_scale=True,
115
+ desc='Downloading', dynamic_ncols=True
116
+ )
117
+ file = Path(self._TMPDIR.name, 'ff.zip')
118
+ with file.open('wb') as zf:
119
+ for chunk in response.iter_content(chunk_size=4096):
120
+ chunk_size = zf.write(chunk)
121
+ bar.update(chunk_size)
122
+ return file
123
+
124
+ def _install(self, file, path):
125
+ with zipfile.ZipFile(file, 'r') as zf:
126
+ bin = zf.extract(self.bin, self._TMPDIR.name)
127
+ os.chmod(bin, 0o755)
128
+
129
+ try:
130
+ os.replace(bin, path)
131
+ except PermissionError:
132
+ subprocess.run(['sudo', 'mv', bin, path], check=True, capture_output=True)
133
+
134
+ print('Successfully installed:', path)
135
+
136
+ def _uninstall(self, path):
137
+ try:
138
+ os.remove(path)
139
+ except PermissionError:
140
+ subprocess.run(['sudo', 'rm', path], check=True, capture_output=True)
141
+
142
+ print('Successfully uninstalled:', path)
143
+
144
+
145
+ def main():
146
+ fire.Fire(FFUp)