Skip to content

Commit 6a43556

Browse files
committed
checkpoint commit
1 parent db1c919 commit 6a43556

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+905
-142
lines changed

.gitignore

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,13 +97,13 @@ ipython_config.py
9797
# UV
9898
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
9999
# This is especially recommended for binary packages to ensure reproducibility, and is more
100-
# commonly ignored for libraries.
100+
# commonly ignored for app.
101101
#uv.lock
102102

103103
# poetry
104104
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
105105
# This is especially recommended for binary packages to ensure reproducibility, and is more
106-
# commonly ignored for libraries.
106+
# commonly ignored for app.
107107
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
108108
#poetry.lock
109109
#poetry.toml
File renamed without changes.
File renamed without changes.
Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
1-
import logging
21
from pathlib import Path
32

43
import yaml
5-
64
from jinja2 import Environment, PackageLoader, select_autoescape
75
from pydantic import BaseModel
86

Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from frikanalen_django_api_client import AuthenticatedClient
22

3-
from libraries.config import config
3+
from app.config import config
44

55
api_client = AuthenticatedClient(config.django.base_url, config.django.token)
Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,13 @@
33

44
from fastapi import Depends
55
from frikanalen_django_api_client import AuthenticatedClient
6-
from frikanalen_django_api_client.api.videofiles import videofiles_list, videofiles_partial_update, videofiles_create
7-
from frikanalen_django_api_client.api.videos import videos_partial_update, videos_list
8-
from frikanalen_django_api_client.models import VideoFile, VideoFileRequest, PatchedVideoRequest
9-
from libraries.util.pprint_object_list import pprint_object_list
10-
from libraries.tus_hook.hook_server import get_client_from_app_state, build_client
11-
from libraries.loudness.loudness_measurement import LoudnessMeasurement
6+
from frikanalen_django_api_client.api.videofiles import videofiles_create, videofiles_list, videofiles_partial_update
7+
from frikanalen_django_api_client.api.videos import videos_list, videos_partial_update
8+
from frikanalen_django_api_client.models import PatchedVideoRequest, VideoFile, VideoFileRequest
9+
10+
from app.loudness.loudness_measurement import LoudnessMeasurement
11+
from app.tus_hook.hook_server import build_client, get_client_from_app_state
12+
from app.util.pprint_object_list import pprint_object_list
1213

1314

1415
class DjangoApiService:
@@ -58,7 +59,8 @@ async def get_videos(self, limit=10):
5859

5960
if __name__ == "__main__":
6061
import asyncio
61-
from libraries.tus_hook.hook_server import get_client_from_app_state
62+
63+
from app.tus_hook.hook_server import get_client_from_app_state
6264

6365
async def main():
6466
service = DjangoApiService(build_client())

app/ffprobe/ffprobe_schema.py

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
# generated by datamodel-codegen:
2+
# filename: ffprobe.json
3+
4+
from __future__ import annotations
5+
6+
from typing import Optional, Union
7+
8+
from pydantic import BaseModel, ConfigDict
9+
10+
11+
class ProgramVersion(BaseModel):
12+
version: str
13+
copyright: str
14+
compiler_ident: str
15+
configuration: str
16+
17+
18+
class LibraryVersion(BaseModel):
19+
name: str
20+
major: int
21+
minor: int
22+
micro: int
23+
version: int
24+
ident: str
25+
26+
27+
class PixelFormat(BaseModel):
28+
pass
29+
30+
31+
class Packet(BaseModel):
32+
pass
33+
34+
35+
class Frame(BaseModel):
36+
pass
37+
38+
39+
class Format(BaseModel):
40+
filename: str
41+
nb_streams: int
42+
nb_programs: int
43+
format_name: str
44+
probe_score: int
45+
format_long_name: Optional[str] = None
46+
start_time: Optional[str] = None
47+
duration: Optional[str] = None
48+
size: Optional[str] = None
49+
bit_rate: Optional[str] = None
50+
tags: Optional[dict[str, str]] = None
51+
52+
53+
class Chapter(BaseModel):
54+
id: int
55+
time_base: str
56+
start: int
57+
start_time: str
58+
end: int
59+
end_time: str
60+
tags: Optional[dict[str, str]] = None
61+
62+
63+
class Disposition(BaseModel):
64+
default: Optional[int] = None
65+
dub: Optional[int] = None
66+
original: Optional[int] = None
67+
comment: Optional[int] = None
68+
lyrics: Optional[int] = None
69+
karaoke: Optional[int] = None
70+
forced: Optional[int] = None
71+
hearing_impaired: Optional[int] = None
72+
visual_impaired: Optional[int] = None
73+
clean_effects: Optional[int] = None
74+
attached_pic: Optional[int] = None
75+
timed_thumbnails: Optional[int] = None
76+
captions: Optional[int] = None
77+
descriptions: Optional[int] = None
78+
metadata: Optional[int] = None
79+
dependent: Optional[int] = None
80+
still_image: Optional[int] = None
81+
82+
83+
class Error(BaseModel):
84+
code: int
85+
string: str
86+
87+
88+
class Stream(BaseModel):
89+
index: int
90+
codec_tag_string: str
91+
codec_tag: str
92+
codec_name: Optional[str] = None
93+
codec_long_name: Optional[str] = None
94+
profile: Optional[str] = None
95+
codec_type: Optional[str] = None
96+
width: Optional[int] = None
97+
height: Optional[int] = None
98+
coded_width: Optional[int] = None
99+
coded_height: Optional[int] = None
100+
closed_captions: Optional[int] = None
101+
has_b_frames: Optional[int] = None
102+
sample_aspect_ratio: Optional[str] = None
103+
display_aspect_ratio: Optional[str] = None
104+
pix_fmt: Optional[str] = None
105+
level: Optional[int] = None
106+
color_range: Optional[str] = None
107+
color_space: Optional[str] = None
108+
color_transfer: Optional[str] = None
109+
color_primaries: Optional[str] = None
110+
chroma_location: Optional[str] = None
111+
field_order: Optional[str] = None
112+
refs: Optional[int] = None
113+
sample_fmt: Optional[str] = None
114+
sample_rate: Optional[str] = None
115+
channels: Optional[int] = None
116+
channel_layout: Optional[str] = None
117+
bits_per_sample: Optional[int] = None
118+
id: Optional[str] = None
119+
r_frame_rate: Optional[str] = None
120+
avg_frame_rate: Optional[str] = None
121+
time_base: Optional[str] = None
122+
start_pts: Optional[int] = None
123+
start_time: Optional[str] = None
124+
duration_ts: Optional[int] = None
125+
duration: Optional[str] = None
126+
bit_rate: Optional[str] = None
127+
max_bit_rate: Optional[str] = None
128+
bits_per_raw_sample: Optional[str] = None
129+
nb_frames: Optional[str] = None
130+
nb_read_frames: Optional[str] = None
131+
nb_read_packets: Optional[str] = None
132+
disposition: Optional[Disposition] = None
133+
tags: Optional[dict[str, str]] = None
134+
135+
136+
class Program(BaseModel):
137+
program_id: int
138+
program_num: int
139+
nb_streams: int
140+
pmt_pid: int
141+
pcr_pid: int
142+
start_pts: int
143+
start_time: str
144+
end_pts: int
145+
end_time: str
146+
streams: list[Stream]
147+
tags: Optional[dict[str, str]] = None
148+
149+
150+
class FfprobeOutput(BaseModel):
151+
model_config = ConfigDict(
152+
extra="forbid",
153+
)
154+
program_version: Optional[ProgramVersion] = None
155+
library_version: Optional[list[LibraryVersion]] = None
156+
pixel_formats: Optional[list[PixelFormat]] = None
157+
packets: Optional[list[Packet]] = None
158+
frames: Optional[list[Frame]] = None
159+
packets_and_frames: Optional[list[Union[Packet, Frame]]] = None
160+
programs: Optional[list[Program]] = None
161+
streams: Optional[list[Stream]] = None
162+
format: Optional[Format] = None
163+
chapters: Optional[list[Chapter]] = None
164+
error: Optional[Error] = None

app/ffprobe/probe.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import asyncio
2+
import logging
3+
from pathlib import Path
4+
5+
from app.ffprobe.ffprobe_schema import FfprobeOutput
6+
7+
8+
async def _run_ffprobe(filepath: Path) -> str:
9+
cmd = ["ffprobe", "-v", "quiet", "-show_format", "-show_streams", "-of", "json", str(filepath)]
10+
logging.debug("Running ffprobe command: %s", cmd)
11+
12+
process = await asyncio.create_subprocess_exec(
13+
*cmd,
14+
stdout=asyncio.subprocess.PIPE,
15+
stderr=asyncio.subprocess.PIPE,
16+
)
17+
stdout, stderr = await process.communicate()
18+
19+
if process.returncode != 0:
20+
raise RuntimeError(f"ffprobe failed: {stderr.decode()}")
21+
22+
return stdout.decode()
23+
24+
25+
async def ffprobe_file(filepath: Path) -> FfprobeOutput:
26+
data = await _run_ffprobe(filepath)
27+
logging.debug("Validating ffprobe output against JSON Schema: %s", data)
28+
return FfprobeOutput.model_validate_json(data)
29+
30+
31+
if __name__ == "__main__":
32+
import sys
33+
34+
async def main():
35+
result = await ffprobe_file(Path(sys.argv[1]))
36+
print(result)
37+
38+
asyncio.run(main())

0 commit comments

Comments
 (0)