From: Christian Heller Date: Wed, 11 Dec 2024 05:55:03 +0000 (+0100) Subject: Minor redesigns/refactorings/renamings on Tag code. X-Git-Url: https://plomlompom.com/repos/%7B%7Bdb.prefix%7D%7D/static/%7B%7B%20web_path%20%7D%7D/decks/bar%20baz.html?a=commitdiff_plain;h=33fc1be02701e8b1496642e31de4438d7acf8f2c;p=ytplom Minor redesigns/refactorings/renamings on Tag code. --- diff --git a/src/templates/files.tmpl b/src/templates/files.tmpl index c115e46..dca7479 100644 --- a/src/templates/files.tmpl +++ b/src/templates/files.tmpl @@ -4,7 +4,7 @@ {% block body %}
filter filename: -filter tags: +needed tags: show absent:
diff --git a/src/templates/playlist.tmpl b/src/templates/playlist.tmpl index fc5ac84..eed3d1e 100644 --- a/src/templates/playlist.tmpl +++ b/src/templates/playlist.tmpl @@ -36,8 +36,8 @@ event_handlers.push(function(data) { // update playlist a_file.href = `${PATH_PREFIX_FILE}${file.digest}`; }}) function redo_playlist() { - send_to({filter_path: document.getElementsByName('filter_path')[0].value, - filter_tags: document.getElementsByName('filter_tags')[0].value}, + send_to({filter_path: [document.getElementsByName('filter_path')[0].value], + needed_tags: [document.getElementsByName('needed_tags')[0].value]}, PATH_PLAYER); player_command('reload'); } @@ -58,7 +58,7 @@ td.entry_control { width: 5em; } - +
playlist config
filter filename
filter tags
needed tags
diff --git a/src/ytplom/http.py b/src/ytplom/http.py index 97f4ada..1f0b703 100644 --- a/src/ytplom/http.py +++ b/src/ytplom/http.py @@ -94,7 +94,7 @@ class Server(ThreadingHTTPServer): *args, **kwargs) self.config = config self.jinja = JinjaEnv(loader=JinjaFSLoader(_PATH_TEMPLATES)) - self.player = Player(config.tags_prefilter) + self.player = Player(config.needed_tags_prefilter) self.downloads = DownloadsManager() self.downloads.clean_unfinished() self.downloads.start_thread() @@ -159,10 +159,9 @@ class _TaskHandler(BaseHTTPRequestHandler): if 'filter_path' in postvars.as_dict: self.server.player.filter_path = FilterStr( postvars.first_for('filter_path')) - if 'filter_tags' in postvars.as_dict: - self.server.player.filter_tags = { - Tag(t) for t in postvars.first_for('filter_tags').split(',') - if t} + if 'needed_tags' in postvars.as_dict: + self.server.player.needed_tags = Tag.from_joined( + postvars.first_for('needed_tags')) self._send_http('OK', code=200) def _receive_files_command(self, postvars: _ReqMap) -> None: @@ -323,19 +322,19 @@ class _TaskHandler(BaseHTTPRequestHandler): def _send_files_index(self, params: _ReqMap) -> None: filter_path = FilterStr(params.first_for('filter_path')) - filter_tags_str = params.first_for('filter_tags') - filter_tags = self.server.config.tags_prefilter | { - Tag(t) for t in filter_tags_str.split(',') if t} + needed_tags_str = params.first_for('needed_tags') + needed_tags = (self.server.config.needed_tags_prefilter + | Tag.from_joined(needed_tags_str)) show_absent = bool(params.first_for('show_absent')) with DbConn() as conn: files = VideoFile.get_filtered( - conn, filter_path, filter_tags, show_absent) + conn, filter_path, needed_tags, show_absent) files.sort(key=lambda t: t.rel_path) self._send_rendered_template(_NAME_TEMPLATE_FILES, {'files': files, 'selected': 'files', 'filter_path': filter_path, - 'filter_tags': filter_tags_str, + 'needed_tags': needed_tags_str, 'show_absent': show_absent}) def _send_missing_json(self) -> None: @@ -352,7 +351,8 @@ class _TaskHandler(BaseHTTPRequestHandler): _NAME_TEMPLATE_PLAYLIST, {'selected': 'playlist', 'filter_path': self.server.player.filter_path, - 'filter_tags': self.server.player.filter_tags}) + 'needed_tags': ','.join([ + str(t) for t in self.server.player.needed_tags])}) def _send_events(self, params: _ReqMap) -> None: self._send_http(headers=[(_HEADER_CONTENT_TYPE, 'text/event-stream'), @@ -375,7 +375,7 @@ class _TaskHandler(BaseHTTPRequestHandler): 'running': self.server.player.is_running, 'paused': self.server.player.is_paused, 'title_digest': playing.digest.b64 if playing else '', - 'title_tags': ', '.join(playing.tags) if playing else '', + 'title_tags': playing.tags_str if playing else '', 'title': str(playing.rel_path) if playing else 'none'} if 'playlist' in params.as_dict: data['playlist_files'] = [ diff --git a/src/ytplom/misc.py b/src/ytplom/misc.py index f07986a..5451924 100644 --- a/src/ytplom/misc.py +++ b/src/ytplom/misc.py @@ -1,7 +1,7 @@ """Main ytplom lib.""" # included libs -from typing import NewType, Optional, Self +from typing import Any, NewType, Optional, Self from os import chdir, environ from random import shuffle from time import sleep @@ -29,7 +29,7 @@ DEFAULTS = { 'port_remote': 8090, 'background_color': '#ffffff', 'queries_cutoff': '', - 'tags_prefilter_str': '', + 'needed_tags_prefilter_str': '', 'allow_file_edit': True } @@ -43,7 +43,6 @@ ProseText = NewType('ProseText', str) FilterStr = NewType('FilterStr', str) FlagName = NewType('FlagName', str) FlagsInt = NewType('FlagsInt', int) -Tag = NewType('Tag', str) # major expected directories PATH_DOWNLOADS = Path.home().joinpath('ytplom_downloads') @@ -92,7 +91,7 @@ class Config: api_key: str background_color: str queries_cutoff: str - tags_prefilter_str: str + needed_tags_prefilter_str: str allow_file_edit: bool def __init__(self): @@ -107,8 +106,8 @@ class Config: set_attrs_from_dict({k[len(ENVIRON_PREFIX):].lower(): v for k, v in environ.items() if k.isupper() and k.startswith(ENVIRON_PREFIX)}) - self.tags_prefilter = { - Tag(t) for t in self.tags_prefilter_str.split(',') if t} + self.needed_tags_prefilter = Tag.from_joined( + self.needed_tags_prefilter_str) class YoutubeQuery(DbData): @@ -251,6 +250,29 @@ class YoutubeVideo(DbData): (query_id, self.id_)) +class Tag: + """Represents individual VideoFile.tags.""" + + def __init__(self, tag_str: str) -> None: + self._str = tag_str + + def __hash__(self) -> int: + return hash(self._str) + + def __eq__(self, other: Any) -> bool: + if not isinstance(other, self.__class__): + return False + return self._str == other._str + + def __str__(self): + return self._str + + @classmethod + def from_joined(cls, joined: str) -> set[Self]: + """Build set from comma-delimited units in joined.""" + return {cls(t) for t in joined.split(',') if t} + + class VideoFile(DbData): """Collects data about downloaded files.""" id_name = 'digest' @@ -272,8 +294,7 @@ class VideoFile(DbData): self.rel_path = rel_path self.digest = digest if digest else Hash.from_file(self.full_path) self.flags = flags - self.tags = set([Tag(t) for t in tags_str.split(',')] - if tags_str else []) + self.tags = Tag.from_joined(tags_str) self.yt_id = yt_id if last_update is None: self._renew_last_update() @@ -307,35 +328,26 @@ class VideoFile(DbData): def get_filtered(cls, conn: BaseDbConn, filter_path: FilterStr, - filter_tags: set[Tag], + needed_tags: set[Tag], show_absent: bool = False ) -> list[Self]: """Return cls.get_all matching provided filter criteria.""" - filtered_before_tags = [ - f for f in cls.get_all(conn) - if str(filter_path).lower() in str(f.rel_path).lower() - and (show_absent or f.present)] - if filter_tags: - to_remove = set() - for f in filtered_before_tags: - for t in [t for t in filter_tags if t not in f.tags]: - to_remove.add(f) - for f in to_remove: - filtered_before_tags.remove(f) - return filtered_before_tags + return [f for f in cls.get_all(conn) + if (show_absent or f.present) + and str(filter_path).lower() in str(f.rel_path).lower() + and not [t for t in needed_tags if t not in f.tags]] def unused_tags(self, conn: BaseDbConn) -> set[Tag]: """Return tags used among other VideoFiles, not in self.""" - tags = set() + tags: set[Tag] = set() 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) + tags = tags | {t for t in file.tags if t not in self.tags} return tags @property - def tags_str(self): + def tags_str(self) -> str: """Return self.tags joined by ','.""" - return ','.join(self.tags) + return ','.join([str(s) for s in self.tags]) @property def full_path(self) -> Path: @@ -418,14 +430,14 @@ class Player: """MPV representation with some additional features.""" _idx: int - def __init__(self, tags_prefilter: set[Tag]) -> None: + def __init__(self, needed_tags_prefilter: set[Tag]) -> None: self.last_update = DatetimeStr('') self._mpv: Optional[MPV] = None self._kill_queue: Queue = Queue() self._monitoring_kill = False self.filter_path = FilterStr('') - self._tags_prefilter = tags_prefilter - self.filter_tags: set[Tag] = set() + self._needed_tags_prefilter = needed_tags_prefilter + self.needed_tags: set[Tag] = set() self.load_files_and_start() def _monitor_kill(self) -> None: @@ -455,7 +467,7 @@ class Player: f.full_path: f for f in VideoFile.get_filtered( conn, self.filter_path, - self._tags_prefilter | self.filter_tags)} + self._needed_tags_prefilter | self.needed_tags)} self.files = [known_files[p] for p in PATH_DOWNLOADS.iterdir() if p in known_files and p.is_file()