lightning-pose-app 1.8.1a2__py3-none-any.whl → 1.8.1a4__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.
- lightning_pose_app-1.8.1a4.dist-info/METADATA +20 -0
- lightning_pose_app-1.8.1a4.dist-info/RECORD +29 -0
- litpose_app/config.py +22 -0
- litpose_app/deps.py +77 -0
- litpose_app/main.py +46 -188
- litpose_app/ngdist/ng_app/3rdpartylicenses.txt +11 -11
- litpose_app/ngdist/ng_app/index.html +3 -2
- litpose_app/ngdist/ng_app/{main-WFYIUX2C.js → main-VCJFCLFP.js} +184 -61
- litpose_app/ngdist/ng_app/main-VCJFCLFP.js.map +1 -0
- litpose_app/ngdist/ng_app/{styles-AJ6NQDUD.css → styles-ZM27COY6.css} +37 -7
- litpose_app/ngdist/ng_app/styles-ZM27COY6.css.map +7 -0
- litpose_app/ngdist/ng_app/video-tile.component-RDL4BSJ4.css.map +7 -0
- litpose_app/{run_ffprobe.py → routes/ffprobe.py} +34 -2
- litpose_app/{super_rglob.py → routes/files.py} +60 -0
- litpose_app/routes/project.py +72 -0
- litpose_app/routes/transcode.py +67 -0
- litpose_app/tasks/__init__.py +0 -0
- litpose_app/tasks/management.py +2 -0
- litpose_app/tasks/transcode_fine.py +7 -0
- litpose_app/transcode_fine.py +175 -0
- lightning_pose_app-1.8.1a2.dist-info/METADATA +0 -15
- lightning_pose_app-1.8.1a2.dist-info/RECORD +0 -20
- litpose_app/ngdist/ng_app/main-WFYIUX2C.js.map +0 -1
- litpose_app/ngdist/ng_app/styles-AJ6NQDUD.css.map +0 -7
- {lightning_pose_app-1.8.1a2.dist-info → lightning_pose_app-1.8.1a4.dist-info}/WHEEL +0 -0
- /litpose_app/ngdist/ng_app/{app.component-IZ5OUDH2.css.map → app.component-UAQUAGNZ.css.map} +0 -0
- /litpose_app/ngdist/ng_app/{project-settings.component-BXKZMYM3.css.map → project-settings.component-HKHIVUJR.css.map} +0 -0
- /litpose_app/ngdist/ng_app/{viewer-page.component-KIYG73MW.css.map → viewer-page.component-KDHT6XH5.css.map} +0 -0
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import shutil
|
|
2
|
+
import subprocess
|
|
3
|
+
from multiprocessing import Pool, cpu_count
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
# --- Configuration ---
|
|
7
|
+
TARGET_SUFFIX = "sec.mp4"
|
|
8
|
+
OUTPUT_SUFFIX_ADDITION = ".fine"
|
|
9
|
+
MAX_CONCURRENCY = 6
|
|
10
|
+
# FFmpeg options for transcoding:
|
|
11
|
+
# -g 1: Intra frame for every frame (Group of Pictures size 1)
|
|
12
|
+
# -c:v libx264: Use libx264 encoder
|
|
13
|
+
# -preset medium: A balance between encoding speed and compression.
|
|
14
|
+
# Options: ultrafast, superfast, veryfast, faster, fast, medium, slow, slower, veryslow.
|
|
15
|
+
# -crf 23: Constant Rate Factor. Lower values mean better quality and larger files (0-51, default 23).
|
|
16
|
+
# -c:a copy: Copy audio stream without re-encoding. If audio re-encoding is needed, change this.
|
|
17
|
+
# -y: Overwrite output files without asking.
|
|
18
|
+
FFMPEG_OPTIONS = [
|
|
19
|
+
"-c:v",
|
|
20
|
+
"libx264",
|
|
21
|
+
"-g",
|
|
22
|
+
"1",
|
|
23
|
+
"-preset",
|
|
24
|
+
"medium",
|
|
25
|
+
"-crf",
|
|
26
|
+
"23",
|
|
27
|
+
"-c:a",
|
|
28
|
+
"copy",
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def check_dependencies():
|
|
33
|
+
"""Checks if ffmpeg and ffprobe are installed and in PATH."""
|
|
34
|
+
if shutil.which("ffmpeg") is None:
|
|
35
|
+
print("Error: ffmpeg is not installed or not found in PATH.")
|
|
36
|
+
return False
|
|
37
|
+
if shutil.which("ffprobe") is None:
|
|
38
|
+
print("Error: ffprobe is not installed or not found in PATH.")
|
|
39
|
+
return False
|
|
40
|
+
return True
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def transcode_file(
|
|
44
|
+
input_file_path: Path,
|
|
45
|
+
output_file_path: Path,
|
|
46
|
+
) -> tuple[bool, str, Path | None]:
|
|
47
|
+
"""
|
|
48
|
+
Transcodes a single video file to have an intra frame for every frame.
|
|
49
|
+
The output file will be named by inserting ".fine" before the final ".mp4"
|
|
50
|
+
and placed in the specified output_dir.
|
|
51
|
+
Example: "video.sec.mp4" -> "video.sec.fine.mp4"
|
|
52
|
+
Returns a tuple: (success_status: bool, message: str, output_path: Path | None)
|
|
53
|
+
"""
|
|
54
|
+
try:
|
|
55
|
+
|
|
56
|
+
if output_file_path.exists():
|
|
57
|
+
print(
|
|
58
|
+
f"Output file '{output_file_path.name}' already exists. Skipping transcoding."
|
|
59
|
+
)
|
|
60
|
+
return True, f"Skipped (exists): {output_file_path.name}", output_file_path
|
|
61
|
+
|
|
62
|
+
import sys
|
|
63
|
+
|
|
64
|
+
print(f"Processing: {input_file_path.name} -> {output_file_path.name}")
|
|
65
|
+
|
|
66
|
+
ffmpeg_cmd = [
|
|
67
|
+
"ffmpeg",
|
|
68
|
+
"-i",
|
|
69
|
+
str(input_file_path),
|
|
70
|
+
*FFMPEG_OPTIONS,
|
|
71
|
+
"-y", # Overwrite output without asking (though we check existence above)
|
|
72
|
+
str(output_file_path),
|
|
73
|
+
]
|
|
74
|
+
|
|
75
|
+
process = subprocess.Popen(
|
|
76
|
+
ffmpeg_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
stdout, stderr = process.communicate()
|
|
80
|
+
|
|
81
|
+
if process.returncode == 0:
|
|
82
|
+
print(f"Successfully transcoded: {output_file_path.name}")
|
|
83
|
+
return True, f"Success: {output_file_path.name}", output_file_path
|
|
84
|
+
else:
|
|
85
|
+
print(f"Error transcoding '{input_file_path.name}':")
|
|
86
|
+
print(f"FFmpeg stdout:\n{stdout}")
|
|
87
|
+
print(f"FFmpeg stderr:\n{stderr}")
|
|
88
|
+
# Clean up partially created file on error
|
|
89
|
+
if output_file_path.exists():
|
|
90
|
+
try:
|
|
91
|
+
output_file_path.unlink()
|
|
92
|
+
except OSError as e:
|
|
93
|
+
print(
|
|
94
|
+
f"Could not remove partially created file '{output_file_path}': {e}"
|
|
95
|
+
)
|
|
96
|
+
return (
|
|
97
|
+
False,
|
|
98
|
+
f"Error: {input_file_path.name} - FFmpeg failed (code {process.returncode})",
|
|
99
|
+
None,
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
except Exception as e:
|
|
103
|
+
print(f"Error processing '{input_file_path.name}': {e}")
|
|
104
|
+
return False, f"Error: {input_file_path.name} - Exception: {e}", None
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def main():
|
|
108
|
+
"""
|
|
109
|
+
Main function to find and transcode videos.
|
|
110
|
+
"""
|
|
111
|
+
if not check_dependencies():
|
|
112
|
+
return
|
|
113
|
+
|
|
114
|
+
script_dir = Path(__file__).parent # Process files in the script's directory
|
|
115
|
+
# To process files in the current working directory instead:
|
|
116
|
+
# current_dir = Path.cwd()
|
|
117
|
+
|
|
118
|
+
print(
|
|
119
|
+
f"Scanning for '*{TARGET_SUFFIX}' H.264 files in '{script_dir}' and its subdirectories..."
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
# Find all files ending with TARGET_SUFFIX recursively
|
|
123
|
+
files_to_check = list(script_dir.rglob(f"*{TARGET_SUFFIX}"))
|
|
124
|
+
|
|
125
|
+
if not files_to_check:
|
|
126
|
+
print(f"No files found ending with '{TARGET_SUFFIX}'.")
|
|
127
|
+
return
|
|
128
|
+
|
|
129
|
+
print(f"Found {len(files_to_check)} potential files. Checking H.264 codec...")
|
|
130
|
+
|
|
131
|
+
valid_files_to_transcode = []
|
|
132
|
+
for f_path in files_to_check:
|
|
133
|
+
# Ensure it's not an already processed file
|
|
134
|
+
if OUTPUT_SUFFIX_ADDITION + ".mp4" in f_path.name:
|
|
135
|
+
continue
|
|
136
|
+
valid_files_to_transcode.append(f_path)
|
|
137
|
+
|
|
138
|
+
if not valid_files_to_transcode:
|
|
139
|
+
print("No H.264 files matching the criteria need transcoding.")
|
|
140
|
+
return
|
|
141
|
+
|
|
142
|
+
print(f"\nFound {len(valid_files_to_transcode)} H.264 files to transcode:")
|
|
143
|
+
for f in valid_files_to_transcode:
|
|
144
|
+
print(f" - {f.name}")
|
|
145
|
+
|
|
146
|
+
# Determine number of processes
|
|
147
|
+
num_processes = min(MAX_CONCURRENCY, cpu_count(), len(valid_files_to_transcode))
|
|
148
|
+
print(f"\nStarting transcoding with up to {num_processes} parallel processes...\n")
|
|
149
|
+
|
|
150
|
+
output_file_paths = []
|
|
151
|
+
for f in valid_files_to_transcode:
|
|
152
|
+
base_name = f.name[: -len(TARGET_SUFFIX)]
|
|
153
|
+
output_file_name = f"{base_name}{TARGET_SUFFIX.replace('.mp4', '')}{OUTPUT_SUFFIX_ADDITION}.mp4"
|
|
154
|
+
output_file_path = f.parent / output_file_name
|
|
155
|
+
output_file_paths.append(output_file_path)
|
|
156
|
+
# In this main function, output_dir is still the parent of the input file
|
|
157
|
+
# For RPC, we will specify FINE_VIDEO_DIR as output_dir
|
|
158
|
+
with Pool(processes=num_processes) as pool:
|
|
159
|
+
# A dummy output_dir for the script's main function; not used by RPC.
|
|
160
|
+
# It ensures compatibility if this script were run standalone.
|
|
161
|
+
# In the RPC, we'll pass FINE_VIDEO_DIR explicitly.
|
|
162
|
+
results = pool.starmap(
|
|
163
|
+
transcode_file,
|
|
164
|
+
[(f, of) for f, of in zip(valid_files_to_transcode, output_file_paths)],
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
print("\n--- Transcoding Summary ---")
|
|
168
|
+
for result in results:
|
|
169
|
+
print(result)
|
|
170
|
+
print("--------------------------")
|
|
171
|
+
print("All tasks completed.")
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
if __name__ == "__main__":
|
|
175
|
+
main()
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.3
|
|
2
|
-
Name: lightning-pose-app
|
|
3
|
-
Version: 1.8.1a2
|
|
4
|
-
Summary:
|
|
5
|
-
Requires-Python: >=3.10
|
|
6
|
-
Classifier: Programming Language :: Python :: 3
|
|
7
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
8
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
9
|
-
Classifier: Programming Language :: Python :: 3.12
|
|
10
|
-
Classifier: Programming Language :: Python :: 3.13
|
|
11
|
-
Requires-Dist: fastapi
|
|
12
|
-
Requires-Dist: tomli
|
|
13
|
-
Requires-Dist: tomli_w
|
|
14
|
-
Requires-Dist: uvicorn[standard]
|
|
15
|
-
Requires-Dist: wcmatch
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
litpose_app/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
-
litpose_app/main.py,sha256=UQ9WIxjHDDckhcjSwFqiuKj84tO7Ohvf6tP_OiiMIpE,9737
|
|
3
|
-
litpose_app/ngdist/ng_app/3rdpartylicenses.txt,sha256=k34Q8jZikvFWsgFXPlQxFJVcqMXDEiKKzC_VIEK8jNs,25366
|
|
4
|
-
litpose_app/ngdist/ng_app/app.component-IZ5OUDH2.css.map,sha256=e6lXWzmK3xppKK3tXHUccK1yGZqd1kzyTpDH0F1nC2g,344
|
|
5
|
-
litpose_app/ngdist/ng_app/error-dialog.component-HYLQSJEP.css.map,sha256=zJuF-LfB994Y1IrnIz38mariDFb8yucffbWPXgHGbvw,355
|
|
6
|
-
litpose_app/ngdist/ng_app/favicon.ico,sha256=QtbXVfx3HI-WqWh_kkBIQyYzJsDmLw_3Y4_UN7pBepE,15406
|
|
7
|
-
litpose_app/ngdist/ng_app/index.html,sha256=FA0fsUwQVubXOgASPFXVR4cHlWgcKXezeXR4QX5wYdg,22954
|
|
8
|
-
litpose_app/ngdist/ng_app/main-WFYIUX2C.js,sha256=ZzmVXvxTaXNe0akOR1LXkJNTF9cPinKAUc5MNEM0MMY,2719071
|
|
9
|
-
litpose_app/ngdist/ng_app/main-WFYIUX2C.js.map,sha256=iN_kYNo-dAPFZSLWZ-EFFkKYhOnTTpYfSMOMPQ9PF3g,5553036
|
|
10
|
-
litpose_app/ngdist/ng_app/prerendered-routes.json,sha256=p53cyKEVGQ6cGUee02kUdBp9HbdPChFTUp78gHJVBf4,18
|
|
11
|
-
litpose_app/ngdist/ng_app/project-settings.component-BXKZMYM3.css.map,sha256=v5tyba9p8ec3ZbHYyyUGTEFdEAsqT0l7JqtGRGjki6w,371
|
|
12
|
-
litpose_app/ngdist/ng_app/styles-AJ6NQDUD.css,sha256=IFn3SaSSlwBudgsGOQj_GirVrrJ0moGfEYouc9FLj6g,68857
|
|
13
|
-
litpose_app/ngdist/ng_app/styles-AJ6NQDUD.css.map,sha256=hC5GJkT1Xw6TZqjnb9c5tCUOW17HNwqirjMpJaLKw50,74649
|
|
14
|
-
litpose_app/ngdist/ng_app/video-player-controls.component-C4JZHYJ2.css.map,sha256=vX-dgeDCUCPLiea4Qy9O_EBm6IzzwB7R_uSBa0qU5Go,771
|
|
15
|
-
litpose_app/ngdist/ng_app/viewer-page.component-KIYG73MW.css.map,sha256=Uf1FgoCiF_qJpD4Ggk34Dq7kM73Q7i7NT6h3m30tbaY,211
|
|
16
|
-
litpose_app/run_ffprobe.py,sha256=VlZhCzaqqn7xy7Opb3GrBsWdWX_q_5JoF7n_Dg5l3Fc,4843
|
|
17
|
-
litpose_app/super_rglob.py,sha256=uk5u2WKQ10L4sWvQ0aTxHiKD-yXRnwxbwAg9ci3JlYI,1571
|
|
18
|
-
lightning_pose_app-1.8.1a2.dist-info/METADATA,sha256=zVkJb7nbH8IAHWf8BbYn8sUYnLXG1RfGohk7DqBLgNQ,473
|
|
19
|
-
lightning_pose_app-1.8.1a2.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
|
20
|
-
lightning_pose_app-1.8.1a2.dist-info/RECORD,,
|