home · contact · privacy
To file data view, add duration as per ffprobe. master
authorChristian Heller <c.heller@plomlompom.de>
Wed, 25 Dec 2024 18:28:56 +0000 (19:28 +0100)
committerChristian Heller <c.heller@plomlompom.de>
Wed, 25 Dec 2024 18:28:56 +0000 (19:28 +0100)
src/requirements.txt
src/templates/file_data.tmpl
src/ytplom/misc.py

index a0a02a1708812de75f0a6b39099e5e18aa832c52..cf78ba3e72e6509e6192d32f61bb2a852413c636 100644 (file)
@@ -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
index da8726f2512d7270bd6b7a477a583fc7f896fef2..d4dcf8b6f24cc29649ddf945a3c49d6d32414c1e 100644 (file)
@@ -30,6 +30,11 @@ td.tag_checkboxes { width: 1em; }
 <td><a href="/{{page_names.yt_result}}/{{file.yt_id}}">{{file.yt_id}}</a>
 </tr>
 
+<tr>
+<th>duration</th>
+<td>{{file.ffprobed_duration}}</td>
+</tr>
+
 <tr>
 <th>tags</th>
 <td>
index 596ad7c4b4905cfc86f3806ab170ea27b4890e3a..a68edbe85bc6f1a20f234baee4340f38bb19f124 100644 (file)
@@ -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."""