rssVideos: --total-duration
Controleld binging
This commit is contained in:
parent
00a9da6afc
commit
5b7926df8f
|
@ -62,6 +62,45 @@ class SaveInfoPP(yt_dlp.postprocessor.common.PostProcessor):
|
||||||
self.rvelement.ytdl_infos = info
|
self.rvelement.ytdl_infos = info
|
||||||
return [], 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:
|
def format_duration(duration: int) -> str:
|
||||||
return time.strftime("%H:%M:%S", time.gmtime(duration))
|
return time.strftime("%H:%M:%S", time.gmtime(duration))
|
||||||
|
@ -152,6 +191,7 @@ class RVElement:
|
||||||
|
|
||||||
@functools.cached_property
|
@functools.cached_property
|
||||||
def ytdl_infos(self) -> typing.Optional[dict]:
|
def ytdl_infos(self) -> typing.Optional[dict]:
|
||||||
|
# TODO Sanitize according to documentation
|
||||||
log.info(f"Researching: {self}")
|
log.info(f"Researching: {self}")
|
||||||
try:
|
try:
|
||||||
infos = self.parent.ytdl_dry.extract_info(self.link, download=False)
|
infos = self.parent.ytdl_dry.extract_info(self.link, download=False)
|
||||||
|
@ -215,17 +255,6 @@ class RVElement:
|
||||||
return
|
return
|
||||||
self.download()
|
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:
|
def matches_filter(self, args: configargparse.Namespace) -> bool:
|
||||||
# Inexpensive filters
|
# Inexpensive filters
|
||||||
if args.seen != "any" and (args.seen == "seen") != self.watched:
|
if args.seen != "any" and (args.seen == "seen") != self.watched:
|
||||||
|
@ -250,35 +279,11 @@ class RVElement:
|
||||||
if not self.is_video:
|
if not self.is_video:
|
||||||
log.debug(f"Not a video: {self}")
|
log.debug(f"Not a video: {self}")
|
||||||
return False
|
return False
|
||||||
if args.duration:
|
if args.duration and not compare_duration(args.duration)(self.duration):
|
||||||
dur = args.duration
|
log.debug(
|
||||||
|
f"Duration {self.duration} not matching {args.duration}: {self}"
|
||||||
mult_index = dur[-1].lower()
|
)
|
||||||
if mult_index.isdigit():
|
return False
|
||||||
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
|
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@ -439,11 +444,26 @@ class RVDatabase:
|
||||||
elements = sorted(
|
elements = sorted(
|
||||||
elements, key=lambda el: el.duration if el.is_video else 0
|
elements, key=lambda el: el.duration if el.is_video else 0
|
||||||
)
|
)
|
||||||
elif args.order == "short":
|
elif args.order == "long":
|
||||||
elements = sorted(
|
elements = sorted(
|
||||||
elements, key=lambda el: el.duration if el.is_video else 0, reverse=True
|
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
|
return elements
|
||||||
|
|
||||||
|
|
||||||
|
@ -522,6 +542,10 @@ def get_args() -> configargparse.Namespace:
|
||||||
default="unseen",
|
default="unseen",
|
||||||
help="Only include seen/unseen/any videos",
|
help="Only include seen/unseen/any videos",
|
||||||
)
|
)
|
||||||
|
parser.add(
|
||||||
|
"--total-duration",
|
||||||
|
help="Use videos that fit under the total given",
|
||||||
|
)
|
||||||
# TODO Envrionment variables
|
# TODO Envrionment variables
|
||||||
parser.add(
|
parser.add(
|
||||||
"--max-duration",
|
"--max-duration",
|
||||||
|
@ -558,7 +582,6 @@ def get_args() -> configargparse.Namespace:
|
||||||
"clean",
|
"clean",
|
||||||
"seen",
|
"seen",
|
||||||
"unseen",
|
"unseen",
|
||||||
"duration",
|
|
||||||
),
|
),
|
||||||
default="download",
|
default="download",
|
||||||
)
|
)
|
||||||
|
@ -606,8 +629,7 @@ def main() -> None:
|
||||||
database.clean()
|
database.clean()
|
||||||
else:
|
else:
|
||||||
database.attempt_clean()
|
database.attempt_clean()
|
||||||
if args.action == "duration":
|
duration = 0
|
||||||
duration = 0
|
|
||||||
for element in database.filter(args):
|
for element in database.filter(args):
|
||||||
if args.action == "download":
|
if args.action == "download":
|
||||||
element.preload()
|
element.preload()
|
||||||
|
@ -625,12 +647,10 @@ def main() -> None:
|
||||||
if element.watched:
|
if element.watched:
|
||||||
log.info(f"Maked as unseen: {element}")
|
log.info(f"Maked as unseen: {element}")
|
||||||
element.watched = False
|
element.watched = False
|
||||||
elif args.action == "duration":
|
|
||||||
duration += element.duration
|
|
||||||
else:
|
else:
|
||||||
raise NotImplementedError(f"Unimplemented action: {args.action}")
|
raise NotImplementedError(f"Unimplemented action: {args.action}")
|
||||||
if args.action == "duration":
|
duration += element.duration if element.is_video else 0
|
||||||
print(format_duration(duration))
|
log.info(f"Total duration: {format_duration(duration)}")
|
||||||
database.attempt_clean()
|
database.attempt_clean()
|
||||||
database.save()
|
database.save()
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue