From 5b7926df8fc61cf46c7c06f1344fc1bc4aec716e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Geoffrey=20=E2=80=9CFrogeye=E2=80=9D=20Preud=27homme?= Date: Sun, 19 Dec 2021 23:13:41 +0100 Subject: [PATCH] rssVideos: --total-duration Controleld binging --- config/scripts/rssVideos | 116 +++++++++++++++++++++++---------------- 1 file changed, 68 insertions(+), 48 deletions(-) diff --git a/config/scripts/rssVideos b/config/scripts/rssVideos index 93a1723..0954ea4 100755 --- a/config/scripts/rssVideos +++ b/config/scripts/rssVideos @@ -62,6 +62,45 @@ class SaveInfoPP(yt_dlp.postprocessor.common.PostProcessor): self.rvelement.ytdl_infos = info return [], info +def parse_duration(string: str) -> int: + DURATION_MULTIPLIERS = {"s": 1, "m": 60, "h": 3600, "": 1} + + mult_index = string[-1].lower() + if mult_index.isdigit(): + mult_index = "" + else: + string = string[:-1] + try: + multiplier = DURATION_MULTIPLIERS[mult_index] + except IndexError: + raise ValueError(f"Unknown duration multiplier: {mult_index}") + + return int(string) * multiplier + + +def compare_duration(compstr: str) -> typing.Callable[[int], bool]: + DURATION_COMPARATORS = { + "<": int.__lt__, + "-": int.__lt__, + ">": int.__gt__, + "+": int.__gt__, + "=": int.__eq__, + "": int.__le__, + } + + comp_index = compstr[0] + if comp_index.isdigit(): + comp_index = "" + else: + compstr = compstr[1:] + try: + comparator = DURATION_COMPARATORS[comp_index] + except IndexError: + raise ValueError(f"Unknown duration comparator: {comp_index}") + + duration = parse_duration(compstr) + + return lambda d: comparator(d, duration) def format_duration(duration: int) -> str: return time.strftime("%H:%M:%S", time.gmtime(duration)) @@ -152,6 +191,7 @@ class RVElement: @functools.cached_property def ytdl_infos(self) -> typing.Optional[dict]: + # TODO Sanitize according to documentation log.info(f"Researching: {self}") try: infos = self.parent.ytdl_dry.extract_info(self.link, download=False) @@ -215,17 +255,6 @@ class RVElement: return self.download() - MATCHES_DURATION_MULTIPLIERS = {"s": 1, "m": 60, "h": 3600, None: 1} - - MATCHES_DURATION_COMPARATORS = { - "<": int.__lt__, - "-": int.__lt__, - ">": int.__gt__, - "+": int.__gt__, - "=": int.__eq__, - None: int.__le__, - } - def matches_filter(self, args: configargparse.Namespace) -> bool: # Inexpensive filters if args.seen != "any" and (args.seen == "seen") != self.watched: @@ -250,35 +279,11 @@ class RVElement: if not self.is_video: log.debug(f"Not a video: {self}") return False - if args.duration: - dur = args.duration - - mult_index = dur[-1].lower() - if mult_index.isdigit(): - mult_index = None - else: - dur = dur[:-1] - try: - multiplier = self.MATCHES_DURATION_MULTIPLIERS[mult_index] - except IndexError: - raise ValueError(f"Unknown duration multiplier: {mult_index}") - - comp_index = dur[0] - if comp_index.isdigit(): - comp_index = None - else: - dur = dur[1:] - try: - comparator = self.MATCHES_DURATION_COMPARATORS[comp_index] - except IndexError: - raise ValueError(f"Unknown duration comparator: {comp_index}") - - duration = int(dur) - if not comparator(self.duration, duration * multiplier): - log.debug( - f"Duration {self.duration} not matching {args.duration}: {self}" - ) - return False + if args.duration and not compare_duration(args.duration)(self.duration): + log.debug( + f"Duration {self.duration} not matching {args.duration}: {self}" + ) + return False return True @@ -439,11 +444,26 @@ class RVDatabase: elements = sorted( elements, key=lambda el: el.duration if el.is_video else 0 ) - elif args.order == "short": + elif args.order == "long": elements = sorted( elements, key=lambda el: el.duration if el.is_video else 0, reverse=True ) + # Post sorting filtering + if args.total_duration: + rem = parse_duration(args.total_duration) + old_els = list(elements) + elements = list() + while rem > 0: + for el in old_els: + if el.duration < rem: + elements.append(el) + rem -= el.duration + old_els.remove(el) + break + else: + break + return elements @@ -522,6 +542,10 @@ def get_args() -> configargparse.Namespace: default="unseen", help="Only include seen/unseen/any videos", ) + parser.add( + "--total-duration", + help="Use videos that fit under the total given", + ) # TODO Envrionment variables parser.add( "--max-duration", @@ -558,7 +582,6 @@ def get_args() -> configargparse.Namespace: "clean", "seen", "unseen", - "duration", ), default="download", ) @@ -606,8 +629,7 @@ def main() -> None: database.clean() else: database.attempt_clean() - if args.action == "duration": - duration = 0 + duration = 0 for element in database.filter(args): if args.action == "download": element.preload() @@ -625,12 +647,10 @@ def main() -> None: if element.watched: log.info(f"Maked as unseen: {element}") element.watched = False - elif args.action == "duration": - duration += element.duration else: raise NotImplementedError(f"Unimplemented action: {args.action}") - if args.action == "duration": - print(format_duration(duration)) + duration += element.duration if element.is_video else 0 + log.info(f"Total duration: {format_duration(duration)}") database.attempt_clean() database.save()