From: Christian Heller <c.heller@plomlompom.de> Date: Wed, 4 Dec 2024 16:24:00 +0000 (+0100) Subject: Show reduced file data form/table in playlist view. X-Git-Url: https://plomlompom.com/repos/%7B%7Bprefix%7D%7D/static/day?a=commitdiff_plain;h=7a0110f5e565633f62b0feefc65e9e57ecc823fe;p=ytplom Show reduced file data form/table in playlist view. --- diff --git a/src/templates/_base.tmpl b/src/templates/_base.tmpl index 3f9edf4..d87faeb 100644 --- a/src/templates/_base.tmpl +++ b/src/templates/_base.tmpl @@ -10,8 +10,7 @@ <style> body { background-color: #aaaa00; } table { width: 100%; } -th { text-align: left; } -td { vertical-align: top; } +td, th { vertical-align: top; text-align: left; } {% block css %} {% endblock %} </style> diff --git a/src/templates/_macros.tmpl b/src/templates/_macros.tmpl index aa6ed50..89838d8 100644 --- a/src/templates/_macros.tmpl +++ b/src/templates/_macros.tmpl @@ -11,3 +11,44 @@ </p> <hr /> {% endmacro %} + + +{% macro file_data_form(file, unused_tags, page_names, flag_names=[], playlist_view=false) %} +<form action="/{{page_names.file}}/{{file.digest.b64}}" method="POST" /> +<input type="hidden" name="redir" value="/{% if playlist_view %}{{page_names.playlist}}{% else %}{{page_names.file}}/{{file.digest.b64}}{% endif %}" /> +<table> +<tr><th>path:</th><td class="top_field">{% if playlist_view %}<a href="/{{page_names.file}}/{{file.digest.b64}}">{% endif %}{{file.rel_path}}{% if playlist_view %}</a>{% endif %}</td></tr> +{% if not playlist_view %} +<tr><th>present:</th><td>{% if file.present %}<a href="/{{page_names.download}}/{{file.yt_id}}">yes</a>{% else %}no{% endif %}</td></tr> +{% endif %} +<tr><th>YouTube ID:</th><td><a href="/{{page_names.yt_result}}/{{file.yt_id}}">{{file.yt_id}}</a></tr> +<tr> +<th>tags</th> +<td> +<table> +{% for tag in file.tags %} +<tr><td class="tag_checkboxes"><input type="checkbox" name="tags" value="{{tag}}" checked /></td><td>{{tag}}</td></tr> +{% endfor %} +<tr><td class="tag_checkboxes">add:</td><td><input name="tags" list="unused_tags" autocomplete="off" /></td></tr> +<datalist id="unused_tags" /> +{% for tag in unused_tags %} +<option value="{{tag}}">{{tag}}</option> +{% endfor %} +</datalist> +</table> +</td> +</tr> +{% if not playlist_view %} +<tr> +<th>flags:</th> +<td class="flags"> +{% for flag_name in flag_names %} +{{ flag_name }}: <input type="checkbox" name="flags" value="{{flag_name}}" {% if file.is_flag_set(flag_name) %}checked {% endif %} /><br /> +{% endfor %} +</td> +</tr> +{% endif %} +</table> +<input type="submit" /> +</form> +{% endmacro %} diff --git a/src/templates/file_data.tmpl b/src/templates/file_data.tmpl index b5a45b9..482f8ea 100644 --- a/src/templates/file_data.tmpl +++ b/src/templates/file_data.tmpl @@ -2,41 +2,13 @@ {% block css %} -td { width: 100%; } +td.top_field { width: 100%; } td.flags { text-align: right; } +td.tag_checkboxes { width: 1em; } {% endblock %} {% block body %} {{ macros.nav_head(page_names) }} -<form action="/{{page_names.file}}/{{file.digest.b64}}" method="POST" /> -<table> -<tr><th>path:</th><td>{{file.rel_path}}</td></tr> -<tr><th>YouTube ID:</th><td><a href="/{{page_names.yt_result}}/{{file.yt_id}}">{{file.yt_id}}</a></tr> -<tr><th>present:</th><td>{% if file.present %}<a href="/{{page_names.download}}/{{file.yt_id}}">yes</a>{% else %}no{% endif %}</td></tr> -<tr> -<th>flags:</th> -<td class="flags"> -{% for flag_name in flag_names %} -{{ flag_name }}: <input type="checkbox" name="flags" value="{{flag_name}}" {% if file.is_flag_set(flag_name) %}checked {% endif %} /><br /> -{% endfor %} -</td> -</tr> -<tr> -<th>tags</th> -<td> -{% for tag in file.tags %} -<input type="checkbox" name="tags" value="{{tag}}" checked /> {{tag}}<br /> -{% endfor %} -<input name="tags" list="unused_tags" autocomplete="off" /> -<datalist id="unused_tags" /> -{% for tag in unused_tags %} -<option value="{{tag}}">{{tag}}</option> -{% endfor %} -</datalist> -</td> -</tr> -</table> -<input type="submit" /> -</form> +{{ macros.file_data_form(file, unused_tags, page_names, flag_names) }} {% endblock %} diff --git a/src/templates/files.tmpl b/src/templates/files.tmpl index f8286e5..c4c452d 100644 --- a/src/templates/files.tmpl +++ b/src/templates/files.tmpl @@ -15,7 +15,7 @@ show absent: <input type="checkbox" name="show_absent" {% if show_absent %}check <tr><th>size</th><th>actions</th><th>tags</th><th>path</th></tr> {% for file in files %} <tr> -<td>{{ file.size | round(3) }}</td> +<td>{{ file.size | round(1) }}</td> <td><input type="submit" name="play_{{file.digest.b64}}" value="play" {% if not file.present %}disabled {% endif %}/></td> <td>{% for tag in file.tags %}{{tag}} {%endfor %}</td> <td><a href="/{{page_names.file}}/{{file.digest.b64}}">{{file.rel_path}}</a></td> diff --git a/src/templates/playlist.tmpl b/src/templates/playlist.tmpl index e82aff1..c602362 100644 --- a/src/templates/playlist.tmpl +++ b/src/templates/playlist.tmpl @@ -29,50 +29,53 @@ window.onload = keep_updated; {% block css %} -#status { text-align: center; font-weight: bold; } -th { text-align: center; } -td.history { width: 50%; } +#status { font-weight: bold; } +td.screen_half { width: 50%; } +tr.screen_half_titles>th { text-align: center; } td.entry_buttons { width: 5em; } +td.tag_checkboxes { width: 1em; } {% endblock %} -{% macro playlist_entries(files_w_idx, reverse) %} -<td class="history"> -<table> -{% for idx, file in files_w_idx %} -<tr> -<td class="entry_buttons"> -<input type="submit" name="jump_{{idx}}" value=">" /> -<input type="submit" name="up_{{idx}}" value="{% if reverse %}v{% else %}^{% endif %}" /> -<input type="submit" name="down_{{idx}}" value="{% if reverse %}^{% else %}v{% endif %}" /> -</td> -<td><a href="/{{page_names.file}}/{{file.digest.b64}}">{{ file.rel_path }}</a></td> -</tr> -{% endfor %} -</table> -</td> -{% endmacro %} - - {% block body %} {{ macros.nav_head(page_names, "playlist") }} <table> <tr><td id="status" colspan=2> -{% if running %}{% if pause %}PAUSED{% else %}PLAYING{% endif %}{% else %}STOPPED{% endif %}:<br /> -<a href="/{{page_names.file}}/{{current_video.digest.b64}}">{{ current_video.rel_path }}</a><br /> <form action="/{{page_names.playlist}}" method="POST"> <input type="submit" name="pause" autofocus value="{% if paused %}resume{% else %}pause{% endif %}"> <input type="submit" name="prev" value="prev"> <input type="submit" name="next" value="next"> <input type="submit" name="stop" value="{% if running %}stop{% else %}start{% endif %}"> <input type="submit" name="reload" value="reload"> +{% if running %}{% if pause %}PAUSED{% else %}PLAYING{% endif %}{% else %}STOPPED{% endif %} +</form> </td></tr> -<tr><th>past</th><th>future</th></tr> +<tr class="screen_half_titles"><th>current selection</th><th>playlist</th></tr> <tr> -{{ playlist_entries(prev_files_w_idx, reverse=true) }} -{{ playlist_entries(next_files_w_idx, reverse=false) }} +<td class="screen_half"> +{{ macros.file_data_form(current_file, unused_tags, page_names, playlist_view=true) }} +</td> +<td class="screen_half"> +<form action="/{{page_names.playlist}}" method="POST"> +<table> +{% for idx, file in files_w_idx %} +<tr> +<td class="entry_buttons"> +{% if file.digest == current_file.digest %} +PLAYING +{% else %} +<input type="submit" name="jump_{{idx}}" value=">" /> +<input type="submit" name="up_{{idx}}" value="{% if reverse %}v{% else %}^{% endif %}" /> +<input type="submit" name="down_{{idx}}" value="{% if reverse %}^{% else %}v{% endif %}" /> +{% endif %} +</td> +<td><a href="/{{page_names.file}}/{{file.digest.b64}}">{{ file.rel_path }}</a></td> </tr> +{% endfor %} +</table> </form> +</td> +</tr> </table> {% endblock %} diff --git a/src/ytplom/http.py b/src/ytplom/http.py index d61b75c..b0aea64 100644 --- a/src/ytplom/http.py +++ b/src/ytplom/http.py @@ -28,7 +28,7 @@ _TemplateContext: TypeAlias = dict[ None | bool | FilesWithIndex | _PageNames | _FilterStr | Path | PlayerUpdateId | QueryText | QuotaCost | UrlStr | 'VideoFile' | YoutubeId - | 'YoutubeVideo' | list[FlagName] | list['Tag'] | list['VideoFile'] + | 'YoutubeVideo' | list[FlagName] | set['Tag'] | list['VideoFile'] | list['YoutubeVideo'] | list['YoutubeQuery'] ] @@ -151,9 +151,7 @@ class _TaskHandler(BaseHTTPRequestHandler): file.save(conn) conn.commit() file.ensure_absence_if_deleted() - self._redirect(Path('/') - .joinpath(PAGE_NAMES['file']) - .joinpath(digest.b64)) + self._redirect(Path(postvars['redir'][0])) def _receive_yt_query(self, query_txt: QueryText) -> None: with DbConn() as conn: @@ -273,11 +271,11 @@ class _TaskHandler(BaseHTTPRequestHandler): def _send_file_data(self, digest: Hash) -> None: with DbConn() as conn: file = VideoFile.get_one(conn, digest) - all_tags = VideoFile.get_all_tags(conn) - self._send_rendered_template( - _NAME_TEMPLATE_FILE_DATA, - {'file': file, 'flag_names': list(FILE_FLAGS), - 'unused_tags': [t for t in all_tags if t not in file.tags]}) + unused_tags = file.unused_tags(conn) + self._send_rendered_template(_NAME_TEMPLATE_FILE_DATA, + {'file': file, + 'flag_names': list(FILE_FLAGS), + 'unused_tags': unused_tags}) def _send_files_index(self, params: dict[str, list[str]]) -> None: filter_path = _FilterStr(params.get('filter_path', [''])[0]) @@ -312,11 +310,18 @@ class _TaskHandler(BaseHTTPRequestHandler): def _send_playlist(self) -> None: if self.server.player.empty: self.server.player.load_files() + current_file, unused_tags = None, set() + if self.server.player.current_file_digest: + with DbConn() as conn: + current_file = VideoFile.get_one( + conn, self.server.player.current_file_digest) + unused_tags = current_file.unused_tags(conn) self._send_rendered_template( _NAME_TEMPLATE_PLAYLIST, {'last_update': self.server.player.last_update, 'running': self.server.player.is_running, 'paused': self.server.player.is_paused, - 'current_video': self.server.player.current_file, - 'prev_files_w_idx': self.server.player.prev_files_w_idx, - 'next_files_w_idx': self.server.player.next_files_w_idx}) + 'current_file': current_file, + 'unused_tags': unused_tags, + 'files_w_idx': list(enumerate(self.server.player.files)) + }) diff --git a/src/ytplom/misc.py b/src/ytplom/misc.py index d0b55e8..438a8cd 100644 --- a/src/ytplom/misc.py +++ b/src/ytplom/misc.py @@ -278,7 +278,6 @@ class VideoFile(DbData): f'{self.yt_id}|{self.last_update}|{self.tags_str}') def _renew_last_update(self): - print("DEBUG calling_renew_last_update", self.rel_path) self.last_update = DatetimeStr(datetime.now().strftime(TIMESTAMP_FMT)) self._hash_on_last_update = hash(self) @@ -297,12 +296,11 @@ class VideoFile(DbData): raise NotFoundException(f'no entry for file to Youtube ID {yt_id}') return cls._from_table_row(row) - @classmethod - def get_all_tags(cls, conn: BaseDbConn) -> set[Tag]: - """Return all tags used among VideoFiles.""" + def unused_tags(self, conn: BaseDbConn) -> set[Tag]: + """Return tags used among other VideoFiles, not in self.""" tags = set() - for file in cls.get_all(conn): - for tag in file.tags: + for file in self.get_all(conn): + for tag in [t for t in file.tags if t not in self.tags]: tags.add(tag) return tags @@ -423,11 +421,11 @@ class Player: """Collect files in PATH_DOWNLOADS DB-known and of legal extension.""" with DbConn() as conn: known_files = {f.full_path: f for f in VideoFile.get_all(conn)} - self._files = [known_files[p] for p in PATH_DOWNLOADS.iterdir() - if p in known_files - and p.is_file() - and p.suffix[1:] in LEGAL_EXTENSIONS] - shuffle(self._files) + self.files = [known_files[p] for p in PATH_DOWNLOADS.iterdir() + if p in known_files + and p.is_file() + and p.suffix[1:] in LEGAL_EXTENSIONS] + shuffle(self.files) self._idx = 0 def _signal_update(self) -> None: @@ -439,7 +437,7 @@ class Player: config=True) self._monitor_kill() self._mpv.observe_property('pause', lambda a, b: self._signal_update()) - for path in [f.full_path for f in self._files]: + for path in [f.full_path for f in self.files]: self._mpv.command('loadfile', path, 'append') @self._mpv.event_callback('start-file') @@ -471,28 +469,18 @@ class Player: @property def empty(self) -> bool: """Return if playlist empty.""" - return 0 == len(self._files) + return 0 == len(self.files) @property - def current_file(self) -> Optional[VideoFile]: - """Return what we assume is the currently playing file.""" - if not self._files: - return None - return self._files[self._idx] + def current_file_digest(self) -> Optional[Hash]: + """Return .digest of what we assume is the currently playing file. - @property - def _files_w_idx(self) -> FilesWithIndex: - return list(enumerate(self._files)) - - @property - def prev_files_w_idx(self) -> FilesWithIndex: - """List 'past' files of playlist.""" - return list(reversed(self._files_w_idx[:self._idx])) - - @property - def next_files_w_idx(self) -> FilesWithIndex: - """List 'coming' files of playlist.""" - return self._files_w_idx[self._idx + 1:] + We don't return the actual file object because we cannot guarantee its + data's up-to-date-ness, it being cached from the last .load_files call. + """ + if not self.files: + return None + return self.files[self._idx].digest @property def is_running(self) -> bool: @@ -528,7 +516,7 @@ class Player: def next(self) -> None: """Move player to next item in playlist.""" - if self._idx < len(self._files) - 1: + if self._idx < len(self.files) - 1: self._idx += 1 self._play_at_index() @@ -543,16 +531,16 @@ class Player: or (upwards and start_idx == self._idx + 1) or ((not upwards) and start_idx == self._idx - 1) or (upwards and start_idx < 1) - or ((not upwards) and start_idx > len(self._files) - 2)): + or ((not upwards) and start_idx > len(self.files) - 2)): return i0, i1 = start_idx, start_idx + (-1 if upwards else 1) if self._mpv: # NB: a functional playlist-move would do this in a single step, # but for some reason I don't seem to get it to do anything - path = self._files[i1].full_path + path = self.files[i1].full_path self._mpv.command('playlist-remove', i1) self._mpv.command('loadfile', path, 'insert-at', i0) - self._files[i0], self._files[i1] = self._files[i1], self._files[i0] + self.files[i0], self.files[i1] = self.files[i1], self.files[i0] def reload(self) -> None: """Close MPV, re-read (and re-shuffle) filenames, then re-start MPV.""" @@ -563,9 +551,9 @@ class Player: def inject_and_play(self, file: VideoFile) -> None: """Inject file after current title, then jump to it.""" - if self._files: + if self.files: self._idx += 1 - self._files.insert(self._idx, file) + self.files.insert(self._idx, file) if self._mpv: self._mpv.command('loadfile', file.full_path, 'insert-at', self._idx)