home · contact · privacy
Add .flags field to VideoFile and their table, page with checkboxes to set. master
authorChristian Heller <c.heller@plomlompom.de>
Sat, 23 Nov 2024 06:56:34 +0000 (07:56 +0100)
committerChristian Heller <c.heller@plomlompom.de>
Sat, 23 Nov 2024 06:56:34 +0000 (07:56 +0100)
templates/video.tmpl [new file with mode: 0644]
templates/videos.tmpl
ytplom/misc.py

diff --git a/templates/video.tmpl b/templates/video.tmpl
new file mode 100644 (file)
index 0000000..a0142bb
--- /dev/null
@@ -0,0 +1,19 @@
+<html>
+<meta charset="UTF-8">
+<body>
+<p><a href="/playlist">playlist</a> · <a href="/videos">videos</a> · <a href="/queries">queries</a></p>
+<table>
+<tr><th>path:</th><td>{{file.rel_path}}</td></tr>
+<tr><th>YouTube ID:</th><td><a href="/yt_video/{{file.yt_id}}">{{file.yt_id}}</a></tr>
+<tr><th>present:</th><td>{% if file.missing %}no{% else %}<a href="/dl/{{file.yt_id}}">yes</a>{% endif %}</td></tr>
+</table>
+<form action="/video/{{file.yt_id}}" method="POST" />
+{% for flag_name in flag_names %}
+{{ flag_name }}: <input type="checkbox" name="{{flag_name}}" {% if file.flag_set(flag_name) %}checked {% endif %} /><br />
+{% endfor %}
+<input type="submit" />
+</form>
+</body>
+</html>
+
+
index 3fb3648668cd58e0ffd8ef28eca91a64181a1a90..14c3a2f64f3227533e4681d6c62890e41197a67a 100644 (file)
@@ -5,7 +5,7 @@
 <p>downloaded videos:</p>
 <ul>
 {% for video_id, path in videos %}
-<li><a href="/yt_video/{{video_id}}">{{ path }}</a>
+<li><a href="/video/{{video_id}}">{{ path }}</a>
 {% endfor %}
 </ul>
 </body>
index 4d06781d4c01635f5cfe8ecf1bfb75a9b7aceaff..9e547eca30119fa9c19e5c59d5fea6a6bb984089 100644 (file)
@@ -34,14 +34,16 @@ QueryId = NewType('QueryId', str)
 QueryText = NewType('QueryText', str)
 ProseText = NewType('ProseText', str)
 SqlText = NewType('SqlText', str)
+FlagName = NewType('FlagName', str)
+FlagsInt = NewType('FlagsInt', int)
 AmountDownloads = NewType('AmountDownloads', int)
 PlayerUpdateId = NewType('PlayerUpdateId', str)
 DownloadsIndex: TypeAlias = dict[YoutubeId, PathStr]
 TemplateContext: TypeAlias = dict[
         str, None | bool | PlayerUpdateId | Optional[PathStr] | YoutubeId
-        | QueryText | QuotaCost | 'YoutubeVideo' | list['YoutubeVideo']
-        | list['YoutubeQuery'] | list[tuple[YoutubeId, PathStr]]
-        | list[tuple[PathStr, PathStr]]]
+        | QueryText | QuotaCost | list[FlagName] | 'VideoFile' | 'YoutubeVideo'
+        | list['YoutubeVideo'] | list['YoutubeQuery']
+        | list[tuple[YoutubeId, PathStr]] | list[tuple[PathStr, PathStr]]]
 
 # local data reasonably expected to be in user home directory
 PATH_HOME = PathStr(environ.get('HOME', ''))
@@ -56,6 +58,7 @@ PATH_TEMPLATES = PathStr(path_join(PATH_WORKDIR, 'templates'))
 NAME_TEMPLATE_QUERIES = PathStr('queries.tmpl')
 NAME_TEMPLATE_RESULTS = PathStr('results.tmpl')
 NAME_TEMPLATE_VIDEOS = PathStr('videos.tmpl')
+NAME_TEMPLATE_VIDEO = PathStr('video.tmpl')
 NAME_TEMPLATE_YT_VIDEO = PathStr('yt_video.tmpl')
 NAME_TEMPLATE_PLAYLIST = PathStr('playlist.tmpl')
 PATH_TEMPLATE_QUERIES = PathStr(path_join(PATH_TEMPLATES,
@@ -109,10 +112,17 @@ CREATE TABLE quota_costs (
 CREATE TABLE files (
   rel_path TEXT PRIMARY KEY,
   yt_id TEXT NOT NULL DEFAULT "",
+  flags INTEGER NOT NULL DEFAULT 0;
   FOREIGN KEY (yt_id) REFERENCES yt_videos(id)
 );
 '''
 
+VIDEO_FLAGS: dict[FlagName, FlagsInt] = {
+  FlagName('foo'): FlagsInt(1 << 0),
+  FlagName('bar'): FlagsInt(1 << 1),
+  FlagName('baz'): FlagsInt(1 << 2)
+}
+
 
 class NotFoundException(BaseException):
     """Call on DB fetches finding less than expected."""
@@ -288,13 +298,28 @@ class YoutubeVideo(DbData):
 class VideoFile(DbData):
     """Collects data about downloaded files."""
     _table_name = 'files'
-    _cols = ('rel_path', 'yt_id')
+    _cols = ('rel_path', 'yt_id', 'flags')
 
-    def __init__(self, rel_path: PathStr, yt_id: YoutubeId) -> None:
+    def __init__(self, rel_path: PathStr, yt_id: YoutubeId, flags=FlagsInt(0)
+                 ) -> None:
         self.rel_path = rel_path
         self.yt_id = yt_id
+        self.flags = flags
         self.missing = False
 
+    @classmethod
+    def get_by_yt_id(cls, conn: DatabaseConnection, yt_id: YoutubeId) -> Self:
+        """Return VideoFile of .yt_id."""
+        sql = SqlText(f'SELECT * FROM {cls._table_name} WHERE yt_id = ?')
+        row = conn.exec(sql, (yt_id,)).fetchone()
+        if not row:
+            raise NotFoundException
+        return cls._from_table_row(row)
+
+    def flag_set(self, flag_name: FlagName) -> bool:
+        """Return if flag of flag_name is set in self.flags."""
+        return self.flags & VIDEO_FLAGS[flag_name]
+
 
 class QuotaLog(DbData):
     """Collects API access quota costs."""
@@ -582,23 +607,39 @@ class TaskHandler(BaseHTTPRequestHandler):
         postvars = parse_qs(self.rfile.read(body_length).decode())
         if 'playlist' == page_name:
             self._post_player_command(list(postvars.keys())[0])
+        elif 'video' == page_name:
+            self._post_video_flag(YoutubeId(toks_url[2]),
+                                  [FlagName(k) for k in postvars])
         elif 'queries' == page_name:
             self._post_query(QueryText(postvars['query'][0]))
 
-    def _post_player_command(self, commands: str) -> None:
-        if 'pause' in commands:
+    def _post_player_command(self, command: str) -> None:
+        if 'pause' == command:
             self.server.player.toggle_pause()
-        elif 'prev' in commands:
+        elif 'prev' == command:
             self.server.player.prev()
-        elif 'next' in commands:
+        elif 'next' == command:
             self.server.player.next()
-        elif 'stop' in commands:
+        elif 'stop' == command:
             self.server.player.toggle_run()
-        elif 'reload' in commands:
+        elif 'reload' == command:
             self.server.player.reload()
         sleep(0.5)  # avoid redir happening before current_file update
         self._send_http(headers=[('Location', '/')], code=302)
 
+    def _post_video_flag(self,
+                         yt_id: YoutubeId,
+                         flag_names: list[FlagName]
+                         ) -> None:
+        conn = DatabaseConnection()
+        file = VideoFile.get_by_yt_id(conn, yt_id)
+        file.flags = 0
+        for flag_name in flag_names:
+            file.flags |= VIDEO_FLAGS[flag_name]
+        file.save(conn)
+        conn.commit_close()
+        self._send_http(headers=[('Location', f'/video/{yt_id}')], code=302)
+
     def _post_query(self, query_txt: QueryText) -> None:
         conn = DatabaseConnection()
 
@@ -663,6 +704,8 @@ class TaskHandler(BaseHTTPRequestHandler):
                 self._send_or_download_video(YoutubeId(toks_url[2]))
             elif 'videos' == page_name:
                 self._send_videos_index()
+            elif 'video' == page_name:
+                self._send_video_data(YoutubeId(toks_url[2]))
             elif 'yt_video' == page_name:
                 self._send_yt_video_data(YoutubeId(toks_url[2]))
             elif 'missing.json' == page_name:
@@ -752,6 +795,14 @@ class TaskHandler(BaseHTTPRequestHandler):
                  'youtube_prefix': YOUTUBE_URL_PREFIX,
                  'queries': linked_queries})
 
+    def _send_video_data(self, yt_id: YoutubeId) -> None:
+        conn = DatabaseConnection()
+        file = VideoFile.get_by_yt_id(conn, yt_id)
+        conn.commit_close()
+        self._send_rendered_template(
+                NAME_TEMPLATE_VIDEO,
+                {'file': file, 'flag_names': list(VIDEO_FLAGS)})
+
     def _send_videos_index(self) -> None:
         videos = [(id_, PathStr(basename(path)))
                   for id_, path in self.server.downloads.ids_to_paths.items()]