diff --git a/config/scripts/rssVideos b/config/scripts/rssVideos index 03495b9..bfc5180 100755 --- a/config/scripts/rssVideos +++ b/config/scripts/rssVideos @@ -17,6 +17,7 @@ import random import re import subprocess import sys +import time import typing import urllib.parse import urllib.request @@ -43,6 +44,9 @@ def configure_logging(args: configargparse.Namespace) -> None: logger=log, ) +def format_duration(duration: int) -> int: + return time.strftime("%H:%M:%S", time.gmtime(duration)) + class RVElement: parent: "RVDatabase" @@ -101,7 +105,7 @@ class RVElement: return "ytdl_infos" in self.__dict__ def salvage_cache(self, cache: "RVElement") -> None: - if cache.is_researched: + if not self.parent.args.research and cache.is_researched: self.__dict__["ytdl_infos"] = cache.__dict__["ytdl_infos"] log.debug(f"From cache: {self}") if cache.was_downloaded: @@ -110,7 +114,16 @@ class RVElement: self.watched = True def __str__(self) -> str: - return f"{self.guid}: {self.creator} – {self.title} – {self.link}" + str = f"{self.guid}: {self.creator if self.creator else '?'} – {self.title}" + if self.is_researched: + if self.is_video: + str += f" ({format_duration(self.duration)})" + else: + str += " (N/A)" + else: + str += " (?)" + str += f" – {self.link}" + return str @property def downloaded(self) -> bool: @@ -194,6 +207,7 @@ class RVElement: } def matches_filter(self, args: configargparse.Namespace) -> bool: + # Inexpensive filters if args.seen != "any" and (args.seen == "seen") != self.watched: log.debug(f"Not {args.seen}: {self}") return False @@ -209,6 +223,8 @@ class RVElement: if args.creator and (not self.creator or not re.search(args.creator, self.creator)): log.debug(f"Creator not matching {args.creator}: {self}") return False + + # Expensive filters if not self.is_video: log.debug(f"Not a video: {self}") return False @@ -239,6 +255,7 @@ class RVElement: if not comparator(self.duration, duration * multiplier): log.debug(f"Duration {self.duration} not matching {args.duration}: {self}") return False + return True def watch(self) -> None: @@ -378,15 +395,32 @@ class RVDatabase: def filter(self, args: configargparse.Namespace) -> typing.Iterable[RVElement]: elements: typing.Iterable[RVElement] - if args.order == "old": - elements = self.elements - elif args.order == "new": + # Inexpensive sort + if args.order == "new": elements = reversed(self.elements) + elif args.order == "title": + elements = sorted(self.elements, key=lambda el: el.title) + elif args.order == "creator": + elements = sorted(self.elements, key=lambda el: el.creator or '') + elif args.order == "link": + elements = sorted(self.elements, key=lambda el: el.link) elif args.order == "random": elements_random = self.elements.copy() random.shuffle(elements_random) elements = elements_random - return filter(lambda el: el.matches_filter(args), elements) + else: + elements = self.elements + + # Possibly expensive filtering + elements = filter(lambda el: el.matches_filter(args), elements) + + # Expensive sort + if args.order == "short": + elements = sorted(elements, key=lambda el: el.duration if el.is_video else 0) + elif args.order == "short": + elements = sorted(elements, key=lambda el: el.duration if el.is_video else 0, reverse=True) + + return elements def get_args() -> configargparse.Namespace: @@ -428,6 +462,11 @@ def get_args() -> configargparse.Namespace: env_var="RSS_VIDEOS_FEED", required=True, ) + parser.add( + "--research", + help="Fetch video info again", + action="store_true", + ) parser.add( "--videos", help="Directory to store videos", @@ -438,7 +477,7 @@ def get_args() -> configargparse.Namespace: # Which videos parser.add( "--order", - choices=("old", "new", "random"), + choices=("old", "new", "title", "creator", "link", "short", "long", "random"), default="old", help="Sorting mechanism", ) @@ -476,7 +515,7 @@ def get_args() -> configargparse.Namespace: parser.add( "action", nargs="?", - choices=("download", "list", "watch", "binge", "clean", "seen", "unseen"), + choices=("download", "list", "watch", "binge", "clean", "seen", "unseen", "duration"), default="download", ) @@ -515,6 +554,8 @@ def main() -> None: database.clean() else: database.attempt_clean() + if args.action == "duration": + duration = 0 for element in database.filter(args): if args.action == "download": element.preload() @@ -522,12 +563,18 @@ def main() -> None: print(element) elif args.action in ("watch", "binge"): element.watch() + if args.action == "watch": + break elif args.action == "seen": element.watched = True elif args.action == "unseen": element.watched = False - if args.action == "watch": - break + elif args.action == "duration": + duration += element.duration + else: + raise NotImplementedError(f"Unimplemented action: {args.action}") + if args.action == "duration": + print(format_duration(duration)) database.attempt_clean() database.save()