From: Christian Heller Date: Wed, 25 Dec 2024 18:28:56 +0000 (+0100) Subject: To file data view, add duration as per ffprobe. X-Git-Url: https://plomlompom.com/repos/%7B%7B%20web_path%20%7D%7D/decks/%7B%7B%20deck_id%20%7D%7D/processes?a=commitdiff_plain;h=b68e80d357225cdd846fe0a9871f4e33631b2e4d;p=ytplom To file data view, add duration as per ffprobe. --- diff --git a/src/requirements.txt b/src/requirements.txt index a0a02a1..cf78ba3 100644 --- a/src/requirements.txt +++ b/src/requirements.txt @@ -3,3 +3,4 @@ Jinja2==3.1.4 python-mpv==1.0.7 scp==0.15.0 yt-dlp==2024.12.06 +ffmpeg-python==0.2.0 diff --git a/src/templates/file_data.tmpl b/src/templates/file_data.tmpl index da8726f..d4dcf8b 100644 --- a/src/templates/file_data.tmpl +++ b/src/templates/file_data.tmpl @@ -30,6 +30,11 @@ td.tag_checkboxes { width: 1em; } {{file.yt_id}} + +duration +{{file.ffprobed_duration}} + + tags diff --git a/src/ytplom/misc.py b/src/ytplom/misc.py index 596ad7c..a68edbe 100644 --- a/src/ytplom/misc.py +++ b/src/ytplom/misc.py @@ -14,6 +14,7 @@ from threading import Thread from queue import Queue from sqlite3 import Cursor # non-included libs +from ffmpeg import probe as ffprobe # type: ignore import googleapiclient.discovery # type: ignore from mpv import MPV # type: ignore from yt_dlp import YoutubeDL # type: ignore @@ -84,6 +85,15 @@ def ensure_expected_dirs(expected_dirs: list[Path]) -> None: dir_path.mkdir(parents=True, exist_ok=True) +def _readable_seconds(seconds: int) -> str: + """Represent seconds in (up-to-hours) hexagesimal division.""" + seconds_str = str(seconds % 60) + minutes_str = str(seconds // 60) + hours_str = str(seconds // (60 * 60)) + return ':'.join([f'0{s}' if len(s) == 1 else s + for s in (hours_str, minutes_str, seconds_str)]) + + class TagSet: """Collection of tags as used in VideoFile.tags.""" @@ -292,11 +302,7 @@ class YoutubeVideo(DbData): if dur_char in time_dur: dur_str, time_dur = time_dur.split(dur_char) seconds += int(dur_str) * len_seconds - seconds_str = str(seconds % 60) - minutes_str = str(seconds // 60) - hours_str = str(seconds // (60 * 60)) - self.duration = ':'.join([f'0{s}' if len(s) == 1 else s for s - in (hours_str, minutes_str, seconds_str)]) + self.duration = _readable_seconds(seconds) @classmethod def get_all_for_query(cls, @@ -438,6 +444,16 @@ class VideoFile(DbData): return -1 return self.full_path.stat().st_size / (1024 * 1024) + @property + def ffprobed_duration(self) -> str: + """Return human-friendly formatting of file duration as per ffprobe.""" + json = ffprobe(self.full_path) + duration_str = json['format']['duration'] + m_seconds_str = duration_str.split('.')[1] + duration_float = float(duration_str) + seconds = int(duration_float) + return f'{_readable_seconds(seconds)}.{m_seconds_str}' + @property def present(self) -> bool: """Return if file exists in filesystem."""