fid-ffmpeg 0.3.5__py3-none-any.whl → 0.4.2__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.
- fid_ffmpeg-0.4.2.dist-info/METADATA +192 -0
- fid_ffmpeg-0.4.2.dist-info/RECORD +65 -0
- {fid_ffmpeg-0.3.5.dist-info → fid_ffmpeg-0.4.2.dist-info}/WHEEL +1 -1
- fid_ffmpeg-0.4.2.dist-info/licenses/LICENSE +21 -0
- fid_ffmpeg-0.4.2.dist-info/top_level.txt +2 -0
- initial_files/error_handling.py +13 -0
- tasks/__init__.py +0 -0
- tasks/audio/__init__.py +0 -0
- tasks/audio/audio_interactive.py +96 -0
- tasks/audio/bitrate.py +0 -0
- tasks/audio/channels.py +0 -0
- tasks/audio/codec.py +0 -0
- tasks/audio/compressor.py +0 -0
- tasks/audio/delay.py +0 -0
- tasks/audio/denoise.py +0 -0
- tasks/audio/equalizer.py +0 -0
- tasks/audio/fade.py +0 -0
- tasks/audio/mix.py +0 -0
- tasks/audio/mute.py +12 -0
- tasks/audio/normalize.py +0 -0
- tasks/audio/replace.py +0 -0
- tasks/audio/speed.py +0 -0
- tasks/audio/trim.py +0 -0
- tasks/audio/volume.py +0 -0
- tasks/encode/__init__.py +0 -0
- tasks/encode/av1.py +0 -0
- tasks/encode/encode_interactive.py +35 -0
- tasks/encode/h264.py +0 -0
- tasks/encode/h265.py +0 -0
- tasks/extract/__init__.py +0 -0
- tasks/extract/attachments.py +0 -0
- tasks/extract/audio.py +12 -0
- tasks/extract/audio_channels.py +0 -0
- tasks/extract/audio_track.py +0 -0
- tasks/extract/chapters.py +0 -0
- tasks/extract/extract_interactive.py +75 -0
- tasks/extract/frames.py +14 -0
- tasks/extract/keyframes.py +0 -0
- tasks/extract/subtitles.py +0 -0
- tasks/extract/subtitles_convert.py +0 -0
- tasks/extract/subtitles_track.py +0 -0
- tasks/extract/thumbnails.py +0 -0
- tasks/info.py +11 -0
- tasks/stream/__init__.py +0 -0
- tasks/stream/dash.py +0 -0
- tasks/stream/hls.py +0 -0
- tasks/stream/http.py +0 -0
- tasks/stream/rtmp.py +0 -0
- tasks/stream/rtsp.py +0 -0
- tasks/stream/srt.py +0 -0
- tasks/stream/stream_interactive.py +56 -0
- tasks/stream/udp.py +0 -0
- tasks/video/__init__.py +0 -0
- tasks/video/compressor.py +14 -0
- tasks/video/concat.py +0 -0
- tasks/video/crop.py +0 -0
- tasks/video/fps.py +0 -0
- tasks/video/gif.py +12 -0
- tasks/video/resize.py +20 -0
- tasks/video/rotate.py +0 -0
- tasks/video/speed.py +0 -0
- tasks/video/trim.py +1 -0
- tasks/video/video_interactive.py +66 -0
- fid/fid.py +0 -71
- fid_ffmpeg-0.3.5.dist-info/METADATA +0 -33
- fid_ffmpeg-0.3.5.dist-info/RECORD +0 -8
- fid_ffmpeg-0.3.5.dist-info/licenses/LICENSE +0 -8
- fid_ffmpeg-0.3.5.dist-info/top_level.txt +0 -1
- {fid_ffmpeg-0.3.5.dist-info → fid_ffmpeg-0.4.2.dist-info}/entry_points.txt +0 -0
- {fid → initial_files}/__init__.py +0 -0
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: fid-ffmpeg
|
|
3
|
+
Version: 0.4.2
|
|
4
|
+
Summary: FFmpeg-based CLI tool for video and audio operations like editing, extracting, streaming, and encoding
|
|
5
|
+
Author-email: Omar Abdalgwad <ahlawyomar95@gmail.com>
|
|
6
|
+
License: MIT License
|
|
7
|
+
|
|
8
|
+
Copyright (c) 2026 Omar Abdalgwad
|
|
9
|
+
|
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
11
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
12
|
+
in the Software without restriction, including without limitation the rights
|
|
13
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
14
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
15
|
+
furnished to do so, subject to the following conditions:
|
|
16
|
+
|
|
17
|
+
The above copyright notice and this permission notice shall be included in all
|
|
18
|
+
copies or substantial portions of the Software.
|
|
19
|
+
|
|
20
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
21
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
22
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
23
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
24
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
25
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
26
|
+
SOFTWARE .
|
|
27
|
+
Project-URL: Homepage, https://github.com/Omarabdalgwad/fid-FFmpeg
|
|
28
|
+
Project-URL: Repository, https://github.com/Omarabdalgwad/fid-FFmpeg.git
|
|
29
|
+
Project-URL: Documentation, https://github.com/Omarabdalgwad/fid-FFmpeg#readme
|
|
30
|
+
Project-URL: Issues, https://github.com/Omarabdalgwad/fid-FFmpeg/issues
|
|
31
|
+
Classifier: Programming Language :: Python :: 3
|
|
32
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
33
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
34
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
35
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
36
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
37
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
38
|
+
Classifier: Operating System :: OS Independent
|
|
39
|
+
Classifier: Topic :: Multimedia :: Video
|
|
40
|
+
Classifier: Topic :: Multimedia :: Sound/Audio
|
|
41
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
42
|
+
Requires-Python: >=3.8
|
|
43
|
+
Description-Content-Type: text/markdown
|
|
44
|
+
License-File: LICENSE
|
|
45
|
+
Requires-Dist: typer>=0.7
|
|
46
|
+
Requires-Dist: questionary>=1.10
|
|
47
|
+
Requires-Dist: rich>=13.0
|
|
48
|
+
Requires-Dist: pyfiglet>=0.8
|
|
49
|
+
Dynamic: license-file
|
|
50
|
+
|
|
51
|
+
# fid-ffmpeg [](https://pepy.tech/project/fid-ffmpeg)
|
|
52
|
+
|
|
53
|
+
Python wrapper around the FFmpeg command line tool for video operations.
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
fid
|
|
57
|
+
```
|
|
58
|
+
https://github.com/user-attachments/assets/abcc8aa0-3ada-4548-8f99-987687cfccd9
|
|
59
|
+
|
|
60
|
+
## Requirements
|
|
61
|
+
|
|
62
|
+
- python >=3.9 : [Download Python](https://www.python.org/downloads/)
|
|
63
|
+
- ffmpeg : [Download FFmpeg](https://www.ffmpeg.org/download.html)
|
|
64
|
+
- install fid-cli with pip :
|
|
65
|
+
```bash
|
|
66
|
+
pip install fid-ffmpeg
|
|
67
|
+
```
|
|
68
|
+
## installation demo
|
|
69
|
+
https://github.com/user-attachments/assets/6063b46b-dd4a-4cb3-a318-869f37bcf60f
|
|
70
|
+
|
|
71
|
+
## Usage
|
|
72
|
+
Run `fid` for the interactive menu, or use direct commands:
|
|
73
|
+
|
|
74
|
+
- `fid --help`: Show help for fid CLI.
|
|
75
|
+
- `fid info "videoPath"`: Get all info about the video.
|
|
76
|
+
- `fid audio "videoPath"`: Extract audio from the video.
|
|
77
|
+
- `fid mute "videoPath"`: Mute the video.
|
|
78
|
+
- `fid gif "videoPath"`: Create a GIF from the video.
|
|
79
|
+
- `fid frames "videoPath"`: Extract all video frames into a folder.
|
|
80
|
+
- `fid compress "videoPath"`: Compress the video to reduce file size.
|
|
81
|
+
|
|
82
|
+
For more advanced options, use the interactive mode by running `fid` without arguments.
|
|
83
|
+
|
|
84
|
+
## Features
|
|
85
|
+
- Interactive CLI with menus for video, audio, extract, stream, and encode operations.
|
|
86
|
+
- Built with Typer for commands and Questionary for interactive prompts.
|
|
87
|
+
- Rich console output for a modern look.
|
|
88
|
+
|
|
89
|
+
## Project Structure
|
|
90
|
+
|
|
91
|
+
```
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
FID
|
|
95
|
+
├─ LICENSE
|
|
96
|
+
├─ pyproject.toml
|
|
97
|
+
├─ README.md
|
|
98
|
+
└─ src
|
|
99
|
+
├─ fid.py
|
|
100
|
+
├─ fid_interactive.py
|
|
101
|
+
├─ initial_files
|
|
102
|
+
│ ├─ error_handling.py
|
|
103
|
+
│ └─ __init__.py
|
|
104
|
+
├─ tasks
|
|
105
|
+
│ ├─ audio
|
|
106
|
+
│ │ ├─ audio_interactive.py
|
|
107
|
+
│ │ ├─ bitrate.py
|
|
108
|
+
│ │ ├─ channels.py
|
|
109
|
+
│ │ ├─ codec.py
|
|
110
|
+
│ │ ├─ compressor.py
|
|
111
|
+
│ │ ├─ delay.py
|
|
112
|
+
│ │ ├─ denoise.py
|
|
113
|
+
│ │ ├─ equalizer.py
|
|
114
|
+
│ │ ├─ fade.py
|
|
115
|
+
│ │ ├─ mix.py
|
|
116
|
+
│ │ ├─ mute.py
|
|
117
|
+
│ │ ├─ normalize.py
|
|
118
|
+
│ │ ├─ replace.py
|
|
119
|
+
│ │ ├─ speed.py
|
|
120
|
+
│ │ ├─ trim.py
|
|
121
|
+
│ │ ├─ volume.py
|
|
122
|
+
│ │ └─ __init__.py
|
|
123
|
+
│ ├─ encode
|
|
124
|
+
│ │ ├─ av1.py
|
|
125
|
+
│ │ ├─ encode_interactive.py
|
|
126
|
+
│ │ ├─ h264.py
|
|
127
|
+
│ │ ├─ h265.py
|
|
128
|
+
│ │ └─ __init__.py
|
|
129
|
+
│ ├─ extract
|
|
130
|
+
│ │ ├─ attachments.py
|
|
131
|
+
│ │ ├─ audio.py
|
|
132
|
+
│ │ ├─ audio_channels.py
|
|
133
|
+
│ │ ├─ audio_track.py
|
|
134
|
+
│ │ ├─ chapters.py
|
|
135
|
+
│ │ ├─ extract_interactive.py
|
|
136
|
+
│ │ ├─ frames.py
|
|
137
|
+
│ │ ├─ keyframes.py
|
|
138
|
+
│ │ ├─ subtitles.py
|
|
139
|
+
│ │ ├─ subtitles_convert.py
|
|
140
|
+
│ │ ├─ subtitles_track.py
|
|
141
|
+
│ │ ├─ thumbnails.py
|
|
142
|
+
│ │ └─ __init__.py
|
|
143
|
+
│ ├─ info.py
|
|
144
|
+
│ ├─ stream
|
|
145
|
+
│ │ ├─ dash.py
|
|
146
|
+
│ │ ├─ hls.py
|
|
147
|
+
│ │ ├─ http.py
|
|
148
|
+
│ │ ├─ rtmp.py
|
|
149
|
+
│ │ ├─ rtsp.py
|
|
150
|
+
│ │ ├─ srt.py
|
|
151
|
+
│ │ ├─ stream_interactive.py
|
|
152
|
+
│ │ ├─ udp.py
|
|
153
|
+
│ │ └─ __init__.py
|
|
154
|
+
│ ├─ video
|
|
155
|
+
│ │ ├─ compressor.py
|
|
156
|
+
│ │ ├─ concat.py
|
|
157
|
+
│ │ ├─ crop.py
|
|
158
|
+
│ │ ├─ fps.py
|
|
159
|
+
│ │ ├─ gif.py
|
|
160
|
+
│ │ ├─ resize.py
|
|
161
|
+
│ │ ├─ rotate.py
|
|
162
|
+
│ │ ├─ speed.py
|
|
163
|
+
│ │ ├─ trim.py
|
|
164
|
+
│ │ ├─ video_interactive.py
|
|
165
|
+
│ │ └─ __init__.py
|
|
166
|
+
│ └─ __init__.py
|
|
167
|
+
├─ welcome.py
|
|
168
|
+
└─ __init__.py
|
|
169
|
+
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
## Contributing
|
|
173
|
+
Contributions are welcome! Fork the repo, create a branch, and submit a pull request. For major changes, open an issue first.
|
|
174
|
+
|
|
175
|
+
## About
|
|
176
|
+
Python wrapper around the FFmpeg command line tool.
|
|
177
|
+
|
|
178
|
+
[PyPI Project](https://pypi.org/project/fid-ffmpeg/)
|
|
179
|
+
|
|
180
|
+
### Topics
|
|
181
|
+
- audio
|
|
182
|
+
- python
|
|
183
|
+
- cli
|
|
184
|
+
- video
|
|
185
|
+
- ffmpeg
|
|
186
|
+
- frames
|
|
187
|
+
- gif
|
|
188
|
+
- compressor
|
|
189
|
+
- ffmpeg-wrapper
|
|
190
|
+
- rich
|
|
191
|
+
- mute
|
|
192
|
+
- typer-cli
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
fid_ffmpeg-0.4.2.dist-info/licenses/LICENSE,sha256=F-pOyGrwdERPucmVHPPrD37KZZ7sphCDJeAzG5gxFOw,1091
|
|
2
|
+
initial_files/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
|
+
initial_files/error_handling.py,sha256=Z1gSb90rS2Vy4VZHS9Zk7OHPL2o6NOw4ipzBQTmjLbo,351
|
|
4
|
+
tasks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
|
+
tasks/info.py,sha256=lDrvHBGTp-PQFb-6jZ9FeB6ou4SNo4CO4Rc9SVPBHPo,367
|
|
6
|
+
tasks/audio/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
|
+
tasks/audio/audio_interactive.py,sha256=U9D7yh2tXmJMP7nmmnOEYPoLBrPs2hNn3soNkYFLIRM,2519
|
|
8
|
+
tasks/audio/bitrate.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
|
+
tasks/audio/channels.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
|
+
tasks/audio/codec.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
11
|
+
tasks/audio/compressor.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
12
|
+
tasks/audio/delay.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
13
|
+
tasks/audio/denoise.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
14
|
+
tasks/audio/equalizer.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
15
|
+
tasks/audio/fade.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
16
|
+
tasks/audio/mix.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
17
|
+
tasks/audio/mute.py,sha256=OsPLClHQfWdJaUg_DOiJok0zKOtIhcAk6Ex7wuGUaFs,466
|
|
18
|
+
tasks/audio/normalize.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
19
|
+
tasks/audio/replace.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
20
|
+
tasks/audio/speed.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
21
|
+
tasks/audio/trim.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
22
|
+
tasks/audio/volume.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
23
|
+
tasks/encode/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
24
|
+
tasks/encode/av1.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
25
|
+
tasks/encode/encode_interactive.py,sha256=pdzbFIeo_EDVK52LOLvhtzT33uivfc8gO4ANFzea1G4,861
|
|
26
|
+
tasks/encode/h264.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
27
|
+
tasks/encode/h265.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
28
|
+
tasks/extract/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
29
|
+
tasks/extract/attachments.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
30
|
+
tasks/extract/audio.py,sha256=vsAcpzp7qauIRakxrbWmleSeXKJoqKqwYwK2QSYsejA,432
|
|
31
|
+
tasks/extract/audio_channels.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
32
|
+
tasks/extract/audio_track.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
33
|
+
tasks/extract/chapters.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
34
|
+
tasks/extract/extract_interactive.py,sha256=ctv-G4YnaS5vUZGKOYDgWpei-44YdXTnp1nAc1umzD0,2198
|
|
35
|
+
tasks/extract/frames.py,sha256=AXo5YYwcPqiGiNzIVh2rbgwdTAuOuFWQ_nVDlzTzOyo,506
|
|
36
|
+
tasks/extract/keyframes.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
37
|
+
tasks/extract/subtitles.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
38
|
+
tasks/extract/subtitles_convert.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
39
|
+
tasks/extract/subtitles_track.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
40
|
+
tasks/extract/thumbnails.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
41
|
+
tasks/stream/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
42
|
+
tasks/stream/dash.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
43
|
+
tasks/stream/hls.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
44
|
+
tasks/stream/http.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
45
|
+
tasks/stream/rtmp.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
46
|
+
tasks/stream/rtsp.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
47
|
+
tasks/stream/srt.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
48
|
+
tasks/stream/stream_interactive.py,sha256=WVOpW9g0HdwPcsMc5ed_RUHJQGgGmQ8Wu2uMXjr5Nz4,1439
|
|
49
|
+
tasks/stream/udp.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
50
|
+
tasks/video/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
51
|
+
tasks/video/compressor.py,sha256=cOBjww_BLYQuccZR0O1TCw5eIif0-loTb-RuZ4eZ7VI,578
|
|
52
|
+
tasks/video/concat.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
53
|
+
tasks/video/crop.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
54
|
+
tasks/video/fps.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
55
|
+
tasks/video/gif.py,sha256=o2_przDXUjNMxs0LlfQLRYK6xRsb2Lti0LRs0fSA07g,426
|
|
56
|
+
tasks/video/resize.py,sha256=aQTGZDiwCW2RhfX_7DMc7S-mqTuYsqw5G91xkDRZ5Y0,677
|
|
57
|
+
tasks/video/rotate.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
58
|
+
tasks/video/speed.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
59
|
+
tasks/video/trim.py,sha256=frcCV1k9oG9oKj3dpUqdJg1PxRT2RSN_XKdLCPjaYaY,2
|
|
60
|
+
tasks/video/video_interactive.py,sha256=W9CODtlwwDjVjgWoRsj4Av-bP_e-3qT2QUtnr64Gdfg,1641
|
|
61
|
+
fid_ffmpeg-0.4.2.dist-info/METADATA,sha256=RdFI6w5Q45GDSvN4d-awmcrXaE9xc4kN0n01Ptl4LnA,6775
|
|
62
|
+
fid_ffmpeg-0.4.2.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
63
|
+
fid_ffmpeg-0.4.2.dist-info/entry_points.txt,sha256=wc8Iju3nkXAuyqF5NB1276l_AGtakuifMGR3D42dqB8,36
|
|
64
|
+
fid_ffmpeg-0.4.2.dist-info/top_level.txt,sha256=glIqk4p9N9EoHXTmkNQdRlbb1HpxOx2xuh9eO52I1E0,20
|
|
65
|
+
fid_ffmpeg-0.4.2.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Omar Abdalgwad
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE .
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import typer
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
import shutil
|
|
4
|
+
|
|
5
|
+
def ffmpeg():
|
|
6
|
+
if shutil.which("ffmpeg")is None:
|
|
7
|
+
print("ffmpeg isn't installed\n download from: https://ffmpeg.org/download.html")
|
|
8
|
+
raise typer.Exit()
|
|
9
|
+
|
|
10
|
+
def ckvideo(video_path:Path):
|
|
11
|
+
if not vid.exists():
|
|
12
|
+
print("file doesn't exist :)")
|
|
13
|
+
raise typer.Exit()
|
tasks/__init__.py
ADDED
|
File without changes
|
tasks/audio/__init__.py
ADDED
|
File without changes
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import questionary
|
|
2
|
+
import typer
|
|
3
|
+
from .denoise import denoise
|
|
4
|
+
from .equalizer import equalizer
|
|
5
|
+
from .compressor import compress
|
|
6
|
+
from .codec import codec
|
|
7
|
+
from .bitrate import bitrate
|
|
8
|
+
from .channels import channels
|
|
9
|
+
from .delay import delay
|
|
10
|
+
from .fade import fade
|
|
11
|
+
from .trim import trim
|
|
12
|
+
from .replace import replace
|
|
13
|
+
from .mute import mute
|
|
14
|
+
from .mix import mix
|
|
15
|
+
from .speed import speed
|
|
16
|
+
from .normalize import normalize
|
|
17
|
+
from .volume import volume
|
|
18
|
+
|
|
19
|
+
def audio_main(video_path):
|
|
20
|
+
|
|
21
|
+
while True:
|
|
22
|
+
choice= questionary.select(
|
|
23
|
+
"select the editing option you want:",
|
|
24
|
+
choices=[
|
|
25
|
+
"volume up/down",
|
|
26
|
+
"mute audio",
|
|
27
|
+
"normalize audio",
|
|
28
|
+
"speed up/down",
|
|
29
|
+
"decrease noise",
|
|
30
|
+
"mix two audio files",
|
|
31
|
+
"compress audio file",
|
|
32
|
+
"equalize audio",
|
|
33
|
+
"change codec",
|
|
34
|
+
"change bitrate",
|
|
35
|
+
"change channels",
|
|
36
|
+
"add fade in/out",
|
|
37
|
+
"trim audio",
|
|
38
|
+
"replace audio",
|
|
39
|
+
"delay audio",
|
|
40
|
+
"Back to main menu",
|
|
41
|
+
"exit"
|
|
42
|
+
]).ask()
|
|
43
|
+
|
|
44
|
+
if choice is None:
|
|
45
|
+
raise typer.Exit()
|
|
46
|
+
|
|
47
|
+
if choice== "volume up/down":
|
|
48
|
+
volume(video_path)
|
|
49
|
+
|
|
50
|
+
elif choice=="mute audio":
|
|
51
|
+
mute(video_path)
|
|
52
|
+
|
|
53
|
+
elif choice=="speed up/down":
|
|
54
|
+
speed(video_path)
|
|
55
|
+
|
|
56
|
+
elif choice=="normalize audio":
|
|
57
|
+
normalize(video_path)
|
|
58
|
+
|
|
59
|
+
elif choice=="decrease noise":
|
|
60
|
+
denoise(video_path)
|
|
61
|
+
|
|
62
|
+
elif choice=="mix two audio files":
|
|
63
|
+
mix(video_path)
|
|
64
|
+
|
|
65
|
+
elif choice=="compress audio file":
|
|
66
|
+
compress(video_path)
|
|
67
|
+
|
|
68
|
+
elif choice=="equalize audio":
|
|
69
|
+
equalizer(video_path)
|
|
70
|
+
|
|
71
|
+
elif choice=="change codec":
|
|
72
|
+
codec(video_path)
|
|
73
|
+
|
|
74
|
+
elif choice=="change bitrate":
|
|
75
|
+
bitrate(video_path)
|
|
76
|
+
|
|
77
|
+
elif choice=="change channels":
|
|
78
|
+
channels(video_path)
|
|
79
|
+
|
|
80
|
+
elif choice=="add fade in/out":
|
|
81
|
+
fade(video_path)
|
|
82
|
+
|
|
83
|
+
elif choice=="trim audio":
|
|
84
|
+
trim(video_path)
|
|
85
|
+
|
|
86
|
+
elif choice=="replace audio":
|
|
87
|
+
replace(video_path)
|
|
88
|
+
|
|
89
|
+
elif choice=="delay audio":
|
|
90
|
+
delay(video_path)
|
|
91
|
+
|
|
92
|
+
elif choice=="Back to main menu":
|
|
93
|
+
return
|
|
94
|
+
|
|
95
|
+
elif choice=="exit":
|
|
96
|
+
raise typer.Exit()
|
tasks/audio/bitrate.py
ADDED
|
File without changes
|
tasks/audio/channels.py
ADDED
|
File without changes
|
tasks/audio/codec.py
ADDED
|
File without changes
|
|
File without changes
|
tasks/audio/delay.py
ADDED
|
File without changes
|
tasks/audio/denoise.py
ADDED
|
File without changes
|
tasks/audio/equalizer.py
ADDED
|
File without changes
|
tasks/audio/fade.py
ADDED
|
File without changes
|
tasks/audio/mix.py
ADDED
|
File without changes
|
tasks/audio/mute.py
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import typer
|
|
2
|
+
import subprocess
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from ...initial_files.error_handling import ffmpeg , ckvideo
|
|
5
|
+
|
|
6
|
+
def mute_main(app: typer.Typer):
|
|
7
|
+
@app.command()
|
|
8
|
+
def mute(video_path: Path):
|
|
9
|
+
ffmpeg()
|
|
10
|
+
ckvideo(video_path)
|
|
11
|
+
mute_out=video_path.with_stem(f"{video_path.stem}_muted").with_suffix(video_path.suffix)
|
|
12
|
+
subprocess.run(["ffmpeg", "-i", str(video_path), "-c", "copy", "-an", "-y", str(mute_out)], check=True)
|
tasks/audio/normalize.py
ADDED
|
File without changes
|
tasks/audio/replace.py
ADDED
|
File without changes
|
tasks/audio/speed.py
ADDED
|
File without changes
|
tasks/audio/trim.py
ADDED
|
File without changes
|
tasks/audio/volume.py
ADDED
|
File without changes
|
tasks/encode/__init__.py
ADDED
|
File without changes
|
tasks/encode/av1.py
ADDED
|
File without changes
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import questionary
|
|
2
|
+
import typer
|
|
3
|
+
from .h264 import h264
|
|
4
|
+
from .h265 import h265
|
|
5
|
+
from .av1 import av1
|
|
6
|
+
|
|
7
|
+
def encode_main(video_path):
|
|
8
|
+
|
|
9
|
+
while True:
|
|
10
|
+
choice= questionary.select(
|
|
11
|
+
"select the encoding option you want:",
|
|
12
|
+
choices=[
|
|
13
|
+
"encode to h264",
|
|
14
|
+
"encode to h265",
|
|
15
|
+
"encode to av1",
|
|
16
|
+
"Back to main menu",
|
|
17
|
+
"exit"
|
|
18
|
+
]).ask()
|
|
19
|
+
if choice is None:
|
|
20
|
+
raise typer.Exit()
|
|
21
|
+
|
|
22
|
+
if choice=="encode to h264":
|
|
23
|
+
h264(video_path)
|
|
24
|
+
|
|
25
|
+
elif choice=="encode to h265":
|
|
26
|
+
h265(video_path)
|
|
27
|
+
|
|
28
|
+
elif choice=="encode to av1":
|
|
29
|
+
av1(video_path)
|
|
30
|
+
|
|
31
|
+
elif choice=="Back to main menu":
|
|
32
|
+
return
|
|
33
|
+
|
|
34
|
+
elif choice=="exit":
|
|
35
|
+
raise typer.Exit()
|
tasks/encode/h264.py
ADDED
|
File without changes
|
tasks/encode/h265.py
ADDED
|
File without changes
|
|
File without changes
|
|
File without changes
|
tasks/extract/audio.py
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import typer
|
|
2
|
+
import subprocess
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from ...initial_files.error_handling import ffmpeg , ckvideo
|
|
5
|
+
|
|
6
|
+
def audio_main(app: typer.Typer):
|
|
7
|
+
@app.command()
|
|
8
|
+
def audio(video_path: Path):
|
|
9
|
+
ffmpeg()
|
|
10
|
+
ckvideo(video_path)
|
|
11
|
+
audio_out=video_path.with_suffix(".mp3")
|
|
12
|
+
subprocess.run(["ffmpeg", "-i", str(video_path), "-vn", "-acodec", "libmp3lame", "-y", str(audio_out)], check=True)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import questionary
|
|
2
|
+
import typer
|
|
3
|
+
from .audio import audio
|
|
4
|
+
from .frames import frames
|
|
5
|
+
from .thumbnails import thumbnails
|
|
6
|
+
from .keyframes import keyframes
|
|
7
|
+
from .audio_track import audio_track
|
|
8
|
+
from .audio_channels import audio_channels
|
|
9
|
+
from .subtitles import subtitles
|
|
10
|
+
from .subtitles_track import subtitles_track
|
|
11
|
+
from .subtitles_convert import subtitles_convert
|
|
12
|
+
from .chapters import chapters
|
|
13
|
+
from .attachments import attachments
|
|
14
|
+
|
|
15
|
+
def extract_main(video_path):
|
|
16
|
+
|
|
17
|
+
while True:
|
|
18
|
+
choice= questionary.select(
|
|
19
|
+
"select the editing option you want:",
|
|
20
|
+
choices=[
|
|
21
|
+
"extract frames",
|
|
22
|
+
"extract audio",
|
|
23
|
+
"extract subtitles",
|
|
24
|
+
"extract chapters",
|
|
25
|
+
"extract thumbnails",
|
|
26
|
+
"extract keyframes",
|
|
27
|
+
"extract audio_track",
|
|
28
|
+
"extract audio_channels",
|
|
29
|
+
"extract subtitles_track",
|
|
30
|
+
"subtitles convert",
|
|
31
|
+
"extract attachments",
|
|
32
|
+
"Back to main menu",
|
|
33
|
+
"exit"
|
|
34
|
+
]).ask()
|
|
35
|
+
if choice is None:
|
|
36
|
+
raise typer.Exit()
|
|
37
|
+
|
|
38
|
+
if choice=="extract audio":
|
|
39
|
+
audio(video_path)
|
|
40
|
+
|
|
41
|
+
elif choice=="extract frames":
|
|
42
|
+
frames(video_path)
|
|
43
|
+
|
|
44
|
+
elif choice=="extract subtitles":
|
|
45
|
+
subtitles(video_path)
|
|
46
|
+
|
|
47
|
+
elif choice=="extract chapters":
|
|
48
|
+
chapters(video_path)
|
|
49
|
+
|
|
50
|
+
elif choice=="extract thumbnails":
|
|
51
|
+
thumbnails(video_path)
|
|
52
|
+
|
|
53
|
+
elif choice=="extract keyframes":
|
|
54
|
+
keyframes(video_path)
|
|
55
|
+
|
|
56
|
+
elif choice=="extract audio_track":
|
|
57
|
+
audio_track(video_path)
|
|
58
|
+
|
|
59
|
+
elif choice=="extract audio_channels":
|
|
60
|
+
audio_channels(video_path)
|
|
61
|
+
|
|
62
|
+
elif choice=="extract subtitles_track":
|
|
63
|
+
subtitles_track(video_path)
|
|
64
|
+
|
|
65
|
+
elif choice=="subtitles convert":
|
|
66
|
+
subtitles_convert(video_path)
|
|
67
|
+
|
|
68
|
+
elif choice=="extract attachments":
|
|
69
|
+
attachments(video_path)
|
|
70
|
+
|
|
71
|
+
elif choice=="Back to main menu":
|
|
72
|
+
return
|
|
73
|
+
|
|
74
|
+
elif choice=="exit":
|
|
75
|
+
raise typer.Exit()
|
tasks/extract/frames.py
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import typer
|
|
2
|
+
import subprocess
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from ...initial_files.error_handling import ffmpeg , ckvideo
|
|
5
|
+
|
|
6
|
+
def frames_main(app: typer.Typer):
|
|
7
|
+
@app.command()
|
|
8
|
+
def frames(video_path: Path):
|
|
9
|
+
ffmpeg()
|
|
10
|
+
ckvideo(video_path)
|
|
11
|
+
Fdir= video_path.parent
|
|
12
|
+
frames_out= Fdir / "Frames" / video_path.stem
|
|
13
|
+
frames_out.mkdir(parents=True,exist_ok=True)
|
|
14
|
+
subprocess.run(["ffmpeg", "-i", str(video_path),str(frames_out/ "frame_%02d.png")],check=True )
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
tasks/info.py
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import typer
|
|
2
|
+
import subprocess
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from .initial_files.error_handling import ffmpeg , ckvideo
|
|
5
|
+
|
|
6
|
+
def info_main(app: typer.Typer):
|
|
7
|
+
@app.command()
|
|
8
|
+
def info(video_path: Path):
|
|
9
|
+
ffmpeg()
|
|
10
|
+
ckvideo(video_path)
|
|
11
|
+
subprocess.run(["ffprobe", "-v", "error", "-show_format", "-show_streams", str(video_path)], check=True)
|
tasks/stream/__init__.py
ADDED
|
File without changes
|
tasks/stream/dash.py
ADDED
|
File without changes
|
tasks/stream/hls.py
ADDED
|
File without changes
|
tasks/stream/http.py
ADDED
|
File without changes
|
tasks/stream/rtmp.py
ADDED
|
File without changes
|
tasks/stream/rtsp.py
ADDED
|
File without changes
|
tasks/stream/srt.py
ADDED
|
File without changes
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import questionary
|
|
2
|
+
import typer
|
|
3
|
+
from .hls import hls
|
|
4
|
+
from .rtmp import rtmp
|
|
5
|
+
from .dash import dash
|
|
6
|
+
from .srt import srt
|
|
7
|
+
from .udp import udp
|
|
8
|
+
from .rtsp import rtsp
|
|
9
|
+
from .http import http
|
|
10
|
+
|
|
11
|
+
def stream_main(video_path):
|
|
12
|
+
|
|
13
|
+
while True:
|
|
14
|
+
choice= questionary.select(
|
|
15
|
+
"select the streaming option you want:",
|
|
16
|
+
choices=[
|
|
17
|
+
"stream with hls",
|
|
18
|
+
"stream with rtmp",
|
|
19
|
+
"stream with dash",
|
|
20
|
+
"stream with srt",
|
|
21
|
+
"stream with udp",
|
|
22
|
+
"stream with rtsp",
|
|
23
|
+
"stream with http",
|
|
24
|
+
"Back to main menu",
|
|
25
|
+
"exit"
|
|
26
|
+
]).ask()
|
|
27
|
+
|
|
28
|
+
if choice is None:
|
|
29
|
+
raise typer.Exit()
|
|
30
|
+
|
|
31
|
+
if choice=="stream with hls":
|
|
32
|
+
hls(video_path)
|
|
33
|
+
|
|
34
|
+
elif choice=="stream with rtmp":
|
|
35
|
+
rtmp(video_path)
|
|
36
|
+
|
|
37
|
+
elif choice=="stream with dash":
|
|
38
|
+
dash(video_path)
|
|
39
|
+
|
|
40
|
+
elif choice=="stream with srt":
|
|
41
|
+
srt(video_path)
|
|
42
|
+
|
|
43
|
+
elif choice=="stream with udp":
|
|
44
|
+
udp(video_path)
|
|
45
|
+
|
|
46
|
+
elif choice=="stream with rtsp":
|
|
47
|
+
rtsp(video_path)
|
|
48
|
+
|
|
49
|
+
elif choice=="stream with http":
|
|
50
|
+
http(video_path)
|
|
51
|
+
|
|
52
|
+
elif choice=="Back to main menu":
|
|
53
|
+
return
|
|
54
|
+
|
|
55
|
+
elif choice=="exit":
|
|
56
|
+
raise typer.Exit()
|
tasks/stream/udp.py
ADDED
|
File without changes
|
tasks/video/__init__.py
ADDED
|
File without changes
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import typer
|
|
2
|
+
import subprocess
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from ...initial_files.error_handling import ffmpeg , ckvideo
|
|
5
|
+
|
|
6
|
+
def compress_main(app: typer.Typer):
|
|
7
|
+
@app.command()
|
|
8
|
+
def compress(video_path: Path, crf: int=28):
|
|
9
|
+
ffmpeg()
|
|
10
|
+
ckvideo(video_path)
|
|
11
|
+
compress_out= video_path.with_stem(f"{video_path.stem}_compressed").with_suffix(".mkv")
|
|
12
|
+
subprocess.run(
|
|
13
|
+
["ffmpeg", "-i", str(video_path),"-c:v", "libx264", "-crf", str(crf), "-preset","medium","-c:a","aac","-b:a","96k","-y",str(compress_out),]
|
|
14
|
+
, check=True)
|
tasks/video/concat.py
ADDED
|
File without changes
|
tasks/video/crop.py
ADDED
|
File without changes
|
tasks/video/fps.py
ADDED
|
File without changes
|
tasks/video/gif.py
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import typer
|
|
2
|
+
import subprocess
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from ...initial_files.error_handling import ffmpeg , ckvideo
|
|
5
|
+
|
|
6
|
+
def gif_main(app: typer.Typer):
|
|
7
|
+
@app.command()
|
|
8
|
+
def gif(video_path: Path):
|
|
9
|
+
ffmpeg()
|
|
10
|
+
ckvideo(video_path)
|
|
11
|
+
gif_out=video_path.with_suffix(".gif")
|
|
12
|
+
subprocess.run(["ffmpeg", "-i", str(video_path), "-t", "3", "-vf", "scale=320:-1", "-y", str(gif_out)], check=True)
|
tasks/video/resize.py
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import typer
|
|
2
|
+
import subprocess
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from ...initial_files.error_handling import ffmpeg , ckvideo
|
|
5
|
+
|
|
6
|
+
def resize_main(app: typer.Typer):
|
|
7
|
+
@app.command()
|
|
8
|
+
def resize(video_path: Path, width: int):
|
|
9
|
+
ffmpeg()
|
|
10
|
+
ckvideo(video_path)
|
|
11
|
+
resize_out= video_path.with_stem(f"{video_path.stem}_{width}w").with_suffix(".mp4")
|
|
12
|
+
subprocess.run(
|
|
13
|
+
["ffmpeg",
|
|
14
|
+
"-i", str(video_path),
|
|
15
|
+
"-vf", f"scale={width}:-1",
|
|
16
|
+
"-c:v", "libx264",
|
|
17
|
+
"-preset", "medium",
|
|
18
|
+
"-c:a", "copy",
|
|
19
|
+
"-y",
|
|
20
|
+
str(resize_out)], check=True)
|
tasks/video/rotate.py
ADDED
|
File without changes
|
tasks/video/speed.py
ADDED
|
File without changes
|
tasks/video/trim.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import questionary
|
|
2
|
+
import typer
|
|
3
|
+
from .compressor import compress
|
|
4
|
+
from .concat import concat
|
|
5
|
+
from .crop import crop
|
|
6
|
+
from .fps import fps
|
|
7
|
+
from .gif import gif
|
|
8
|
+
from .resize import resize
|
|
9
|
+
from .rotate import rotate
|
|
10
|
+
from .speed import speed
|
|
11
|
+
from .trim import trim
|
|
12
|
+
|
|
13
|
+
def video_main(video_path):
|
|
14
|
+
|
|
15
|
+
while True:
|
|
16
|
+
choice= questionary.select(
|
|
17
|
+
"select the editing option you want:",
|
|
18
|
+
choices=[
|
|
19
|
+
"compress the video",
|
|
20
|
+
"make gif",
|
|
21
|
+
"speed up/down",
|
|
22
|
+
"change fps",
|
|
23
|
+
"concat videos",
|
|
24
|
+
"crop video",
|
|
25
|
+
"resize video",
|
|
26
|
+
"rotate video",
|
|
27
|
+
"trim video",
|
|
28
|
+
"Back to main menu",
|
|
29
|
+
"exit"
|
|
30
|
+
]).ask()
|
|
31
|
+
|
|
32
|
+
if choice is None:
|
|
33
|
+
raise typer.Exit()
|
|
34
|
+
|
|
35
|
+
if choice=="compress the video":
|
|
36
|
+
compress(video_path)
|
|
37
|
+
|
|
38
|
+
elif choice=="make gif":
|
|
39
|
+
gif(video_path)
|
|
40
|
+
|
|
41
|
+
elif choice=="speed up/down":
|
|
42
|
+
speed(video_path)
|
|
43
|
+
|
|
44
|
+
elif choice=="change fps":
|
|
45
|
+
fps(video_path)
|
|
46
|
+
|
|
47
|
+
elif choice=="concat videos":
|
|
48
|
+
concat(video_path)
|
|
49
|
+
|
|
50
|
+
elif choice=="crop video":
|
|
51
|
+
crop(video_path)
|
|
52
|
+
|
|
53
|
+
elif choice=="resize video":
|
|
54
|
+
resize(video_path)
|
|
55
|
+
|
|
56
|
+
elif choice=="rotate video":
|
|
57
|
+
rotate(video_path)
|
|
58
|
+
|
|
59
|
+
elif choice=="trim video":
|
|
60
|
+
trim(video_path)
|
|
61
|
+
|
|
62
|
+
elif choice=="Back to main menu":
|
|
63
|
+
return
|
|
64
|
+
|
|
65
|
+
elif choice=="exit":
|
|
66
|
+
raise typer.Exit()
|
fid/fid.py
DELETED
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
import typer
|
|
2
|
-
import subprocess
|
|
3
|
-
from pathlib import Path
|
|
4
|
-
import shutil
|
|
5
|
-
from welcome import welcome
|
|
6
|
-
app = typer.Typer()
|
|
7
|
-
|
|
8
|
-
formats = [".mp4",".mov",".avi",".webm"]
|
|
9
|
-
@app.callback(invoke_without_command=True)
|
|
10
|
-
def main(ctx: typer.context):
|
|
11
|
-
welcome()
|
|
12
|
-
if ctx_involved_subcommand id none:
|
|
13
|
-
print(oops!)
|
|
14
|
-
|
|
15
|
-
def ffmpeg():
|
|
16
|
-
if shutil.which("ffmpeg")is None:
|
|
17
|
-
print("ffmpeg isn't installed\n download from: https://ffmpeg.org/download.html")
|
|
18
|
-
raise typer.Exit()
|
|
19
|
-
|
|
20
|
-
def ckvideo(vid:Path):
|
|
21
|
-
if not vid.exists():
|
|
22
|
-
print("file doesn't exist")
|
|
23
|
-
raise typer.Exit()
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
@app.command()
|
|
27
|
-
def info(vid: Path):
|
|
28
|
-
ffmpeg()
|
|
29
|
-
ckvideo(vid)
|
|
30
|
-
subprocess.run(["ffprobe", "-v", "error", "-show_format", "-show_streams", str(vid)], check=True)
|
|
31
|
-
|
|
32
|
-
@app.command()
|
|
33
|
-
def audio(vid: Path):
|
|
34
|
-
ffmpeg()
|
|
35
|
-
ckvideo(vid)
|
|
36
|
-
audio=vid.with_suffix(".mp3")
|
|
37
|
-
subprocess.run(["ffmpeg", "-i", str(vid), "-vn", "-acodec", "mp3", "-y", str(audio)], check=True)
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
@app.command()
|
|
41
|
-
def frames(vid: Path):
|
|
42
|
-
ffmpeg()
|
|
43
|
-
ckvideo(vid)
|
|
44
|
-
Fdir= vid.parent
|
|
45
|
-
frames= Fdir / "Frames" / vid.stem
|
|
46
|
-
frames.mkdir(parents=True,exist_ok=True)
|
|
47
|
-
subprocess.run(["ffmpeg", "-i", str(vid),str(frames/ "frame_%02d.png")],check=True )
|
|
48
|
-
|
|
49
|
-
@app.command()
|
|
50
|
-
def gif(vid: Path):
|
|
51
|
-
ffmpeg()
|
|
52
|
-
ckvideo(vid)
|
|
53
|
-
gif=vid.with_suffix(".gif")
|
|
54
|
-
subprocess.run(["ffmpeg", "-i", str(vid), "-t", "3", "-vf", "scale=320:-1", "-y", str(gif)], check=True)
|
|
55
|
-
|
|
56
|
-
@app.command()
|
|
57
|
-
def mute(vid: Path):
|
|
58
|
-
ffmpeg()
|
|
59
|
-
ckvideo(vid)
|
|
60
|
-
mute=vid.with_stem(f"{vid.stem}_muted").with_suffix(vid.suffix)
|
|
61
|
-
subprocess.run(["ffmpeg", "-i", str(vid), "-c", "copy", "-an", "-y", str(mute)], check=True)
|
|
62
|
-
|
|
63
|
-
@app.command()
|
|
64
|
-
def compress(vid: Path, crf: int=28):
|
|
65
|
-
ffmpeg()
|
|
66
|
-
ckvideo(vid)
|
|
67
|
-
compress= vid.with_stem(f"{vid.stem}_compressed").with_suffix(".mkv")
|
|
68
|
-
subprocess.run(["ffmpeg", "-i", str(vid),"-c:v", "libx264", "-crf", str(crf), "-preset","medium","-c:a","aac","-b:a","96k","-y",str(compress),], check=True)
|
|
69
|
-
|
|
70
|
-
if __name__=="__main__":
|
|
71
|
-
app()
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: fid-ffmpeg
|
|
3
|
-
Version: 0.3.5
|
|
4
|
-
Summary: based ffmpeg CLI for video operations
|
|
5
|
-
Author-email: omar_abdalgwad <ahlawyomar95@gmail.com>
|
|
6
|
-
Description-Content-Type: text/markdown
|
|
7
|
-
License-File: LICENSE
|
|
8
|
-
Requires-Dist: typer>=0.7
|
|
9
|
-
Dynamic: license-file
|
|
10
|
-
|
|
11
|
-
# fid-ffmpeg
|
|
12
|
-
simple ffmpeg based cli for video operations
|
|
13
|
-
|
|
14
|
-
## installation
|
|
15
|
-
|
|
16
|
-
- you need to install python >=3.9 : [Download Python](https://www.python.org/downloads/)
|
|
17
|
-
- install ffmpeg : [Download FFmpeg](https://www.ffmpeg.org/download.html)
|
|
18
|
-
- then install fid-cli with pip :
|
|
19
|
-
```bash
|
|
20
|
-
pip install fid-ffmpeg
|
|
21
|
-
```
|
|
22
|
-
## installation demo
|
|
23
|
-
https://github.com/user-attachments/assets/5c1bb2ac-1793-44b8-8240-bc71a1919d5a
|
|
24
|
-
|
|
25
|
-
## Commands
|
|
26
|
-
| Command | Description |
|
|
27
|
-
|---------|------------|
|
|
28
|
-
| `fid --help` | show help for fid cli |
|
|
29
|
-
| `fid info "videoPath"` | `to know all info about the video` |
|
|
30
|
-
| `fid audio "videoPath"` | `extract audio from the video` |
|
|
31
|
-
| `fid mute "videoPath"` | `mute the video` |
|
|
32
|
-
| `fid gif "videoPath"` | `make a gif from the video` |
|
|
33
|
-
| `fid frames "videoPath"` | `extract all video frames and add them in a folder`|
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
fid/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
-
fid/fid.py,sha256=9Y5HWADNxrYK79RWnDkJN7XkWRs5F-h5jzGBYIQLVdY,2106
|
|
3
|
-
fid_ffmpeg-0.3.5.dist-info/licenses/LICENSE,sha256=8HFb3MtS3r_RTfGUr8mm6IbZocu4o4vc2f2U00Z3Tmc,255
|
|
4
|
-
fid_ffmpeg-0.3.5.dist-info/METADATA,sha256=LLji_Um5FZExJFeTJIBprPIr-myXKCuQhkDrLTcFXWs,1126
|
|
5
|
-
fid_ffmpeg-0.3.5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
6
|
-
fid_ffmpeg-0.3.5.dist-info/entry_points.txt,sha256=wc8Iju3nkXAuyqF5NB1276l_AGtakuifMGR3D42dqB8,36
|
|
7
|
-
fid_ffmpeg-0.3.5.dist-info/top_level.txt,sha256=5jgogeo314G3j_oXUp3BE4wBE3t82ijRwLwF1KnYiDg,4
|
|
8
|
-
fid_ffmpeg-0.3.5.dist-info/RECORD,,
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
fid
|
|
File without changes
|
|
File without changes
|