Various yt-dlp fixes, #build

Changed:
- Fix for channel about page parsing
- Fix for like_count extraction
- Fix for comment author extraction
- Refactor RedisQueue use sorted set
- Fix chrome scaling issue
- Fix autodelete
This commit is contained in:
Simon 2023-12-17 11:34:46 +07:00
commit ddc0b7a481
No known key found for this signature in database
GPG Key ID: 2C15AA5E89985DD4
9 changed files with 51 additions and 65 deletions

View File

@ -36,7 +36,7 @@ class Command(BaseCommand):
self.stdout.write(TOPIC) self.stdout.write(TOPIC)
self._sync_redis_state() self._sync_redis_state()
self._make_folders() self._make_folders()
self._release_locks() self._clear_redis_keys()
self._clear_tasks() self._clear_tasks()
self._clear_dl_cache() self._clear_dl_cache()
self._version_check() self._version_check()
@ -73,10 +73,10 @@ class Command(BaseCommand):
self.stdout.write(self.style.SUCCESS(" ✓ expected folders created")) self.stdout.write(self.style.SUCCESS(" ✓ expected folders created"))
def _release_locks(self): def _clear_redis_keys(self):
"""make sure there are no leftover locks set in redis""" """make sure there are no leftover locks or keys set in redis"""
self.stdout.write("[3] clear leftover locks in redis") self.stdout.write("[3] clear leftover keys in redis")
all_locks = [ all_keys = [
"dl_queue_id", "dl_queue_id",
"dl_queue", "dl_queue",
"downloading", "downloading",
@ -85,19 +85,22 @@ class Command(BaseCommand):
"rescan", "rescan",
"run_backup", "run_backup",
"startup_check", "startup_check",
"reindex:ta_video",
"reindex:ta_channel",
"reindex:ta_playlist",
] ]
redis_con = RedisArchivist() redis_con = RedisArchivist()
has_changed = False has_changed = False
for lock in all_locks: for key in all_keys:
if redis_con.del_message(lock): if redis_con.del_message(key):
self.stdout.write( self.stdout.write(
self.style.SUCCESS(f" ✓ cleared lock {lock}") self.style.SUCCESS(f" ✓ cleared key {key}")
) )
has_changed = True has_changed = True
if not has_changed: if not has_changed:
self.stdout.write(self.style.SUCCESS(" no locks found")) self.stdout.write(self.style.SUCCESS(" no keys found"))
def _clear_tasks(self): def _clear_tasks(self):
"""clear tasks and messages""" """clear tasks and messages"""

View File

@ -50,7 +50,7 @@ class DownloadPostProcess:
return return
print(f"auto delete older than {autodelete_days} days") print(f"auto delete older than {autodelete_days} days")
now_lte = self.now - autodelete_days * 24 * 60 * 60 now_lte = str(self.now - autodelete_days * 24 * 60 * 60)
data = { data = {
"query": {"range": {"player.watched_date": {"lte": now_lte}}}, "query": {"range": {"player.watched_date": {"lte": now_lte}}},
"sort": [{"player.watched_date": {"order": "asc"}}], "sort": [{"player.watched_date": {"order": "asc"}}],
@ -63,7 +63,7 @@ class DownloadPostProcess:
if "autodelete_days" in value: if "autodelete_days" in value:
autodelete_days = value.get("autodelete_days") autodelete_days = value.get("autodelete_days")
print(f"{channel_id}: delete older than {autodelete_days}d") print(f"{channel_id}: delete older than {autodelete_days}d")
now_lte = self.now - autodelete_days * 24 * 60 * 60 now_lte = str(self.now - autodelete_days * 24 * 60 * 60)
must_list = [ must_list = [
{"range": {"player.watched_date": {"lte": now_lte}}}, {"range": {"player.watched_date": {"lte": now_lte}}},
{"term": {"channel.channel_id": {"value": channel_id}}}, {"term": {"channel.channel_id": {"value": channel_id}}},

View File

@ -31,8 +31,8 @@ class YoutubeChannel(YouTubeItem):
self.task = task self.task = task
def build_yt_url(self): def build_yt_url(self):
"""build youtube url""" """overwrite base to use channel about page"""
return f"{self.yt_base}{self.youtube_id}/featured" return f"{self.yt_base}{self.youtube_id}/about"
def build_json(self, upload=False, fallback=False): def build_json(self, upload=False, fallback=False):
"""get from es or from youtube""" """get from es or from youtube"""

View File

@ -126,7 +126,7 @@ class Comments:
"comment_author_id": comment["author_id"], "comment_author_id": comment["author_id"],
"comment_author_thumbnail": comment["author_thumbnail"], "comment_author_thumbnail": comment["author_thumbnail"],
"comment_author_is_uploader": comment.get( "comment_author_is_uploader": comment.get(
"comment_author_is_uploader", False "author_is_uploader", False
), ),
"comment_parent": comment["parent"], "comment_parent": comment["parent"],
} }

View File

@ -243,7 +243,7 @@ class Reindex(ReindexBase):
return return
for name, index_config in self.REINDEX_CONFIG.items(): for name, index_config in self.REINDEX_CONFIG.items():
if not RedisQueue(index_config["queue_name"]).has_item(): if not RedisQueue(index_config["queue_name"]).length():
continue continue
self.total = RedisQueue(index_config["queue_name"]).length() self.total = RedisQueue(index_config["queue_name"]).length()

View File

@ -20,6 +20,7 @@ class RedisBase:
self.conn = redis.Redis( self.conn = redis.Redis(
host=EnvironmentSettings.REDIS_HOST, host=EnvironmentSettings.REDIS_HOST,
port=EnvironmentSettings.REDIS_PORT, port=EnvironmentSettings.REDIS_PORT,
decode_responses=True,
) )
@ -82,7 +83,7 @@ class RedisArchivist(RedisBase):
if not reply: if not reply:
return [] return []
return [i.decode().lstrip(self.NAME_SPACE) for i in reply] return [i.lstrip(self.NAME_SPACE) for i in reply]
def list_items(self, query: str) -> list: def list_items(self, query: str) -> list:
"""list all matches""" """list all matches"""
@ -99,65 +100,49 @@ class RedisArchivist(RedisBase):
class RedisQueue(RedisBase): class RedisQueue(RedisBase):
"""dynamically interact with queues in redis""" """
dynamically interact with queues in redis using sorted set
- low score number is first in queue
- add new items with high score number
"""
def __init__(self, queue_name: str): def __init__(self, queue_name: str):
super().__init__() super().__init__()
self.key = f"{self.NAME_SPACE}{queue_name}" self.key = f"{self.NAME_SPACE}{queue_name}"
def get_all(self): def get_all(self) -> list[str]:
"""return all elements in list""" """return all elements in list"""
result = self.conn.execute_command("LRANGE", self.key, 0, -1) result = self.conn.zrange(self.key, 0, -1)
all_elements = [i.decode() for i in result] return result
return all_elements
def length(self) -> int: def length(self) -> int:
"""return total elements in list""" """return total elements in list"""
return self.conn.execute_command("LLEN", self.key) return self.conn.zcard(self.key)
def in_queue(self, element) -> str | bool: def in_queue(self, element) -> str | bool:
"""check if element is in list""" """check if element is in list"""
result = self.conn.execute_command("LPOS", self.key, element) result = self.conn.zrank(self.key, element)
if result is not None: if result is not None:
return "in_queue" return "in_queue"
return False return False
def add_list(self, to_add): def add_list(self, to_add: list) -> None:
"""add list to queue""" """add list to queue"""
self.conn.execute_command("RPUSH", self.key, *to_add) mapping = {i: "+inf" for i in to_add}
self.conn.zadd(self.key, mapping)
def add_priority(self, to_add: str) -> None:
"""add single video to front of queue"""
item: str = json.dumps(to_add)
self.clear_item(item)
self.conn.execute_command("LPUSH", self.key, item)
def get_next(self) -> str | bool: def get_next(self) -> str | bool:
"""return next element in the queue, False if none""" """return next element in the queue, if available"""
result = self.conn.execute_command("LPOP", self.key) result = self.conn.zpopmin(self.key)
if not result: if not result:
return False return False
next_element = result.decode() return result[0][0]
return next_element
def clear(self) -> None: def clear(self) -> None:
"""delete list from redis""" """delete list from redis"""
self.conn.execute_command("DEL", self.key) self.conn.delete(self.key)
def clear_item(self, to_clear: str) -> None:
"""remove single item from list if it's there"""
self.conn.execute_command("LREM", self.key, 0, to_clear)
def trim(self, size: int) -> None:
"""trim the queue based on settings amount"""
self.conn.execute_command("LTRIM", self.key, 0, size)
def has_item(self) -> bool:
"""check if queue as at least one pending item"""
result = self.conn.execute_command("LRANGE", self.key, 0, 0)
return bool(result)
class TaskRedis(RedisBase): class TaskRedis(RedisBase):
@ -170,7 +155,7 @@ class TaskRedis(RedisBase):
def get_all(self) -> list: def get_all(self) -> list:
"""return all tasks""" """return all tasks"""
all_keys = self.conn.execute_command("KEYS", f"{self.BASE}*") all_keys = self.conn.execute_command("KEYS", f"{self.BASE}*")
return [i.decode().replace(self.BASE, "") for i in all_keys] return [i.replace(self.BASE, "") for i in all_keys]
def get_single(self, task_id: str) -> dict: def get_single(self, task_id: str) -> dict:
"""return content of single task""" """return content of single task"""
@ -178,7 +163,7 @@ class TaskRedis(RedisBase):
if not result: if not result:
return {} return {}
return json.loads(result.decode()) return json.loads(result)
def set_key( def set_key(
self, task_id: str, message: dict, expire: bool | int = False self, task_id: str, message: dict, expire: bool | int = False

View File

@ -33,11 +33,9 @@
<body> <body>
<div class="main-content"> <div class="main-content">
<div class="boxed-content"> <div class="boxed-content">
<div class="top-banner"> <a href="{% url 'home' %}">
<a href="{% url 'home' %}"> <div class="top-banner"></div>
<img alt="tube-archivist-banner"> </a>
</a>
</div>
<div class="top-nav"> <div class="top-nav">
<div class="nav-items"> <div class="nav-items">
<a href="{% url 'home' %}"> <a href="{% url 'home' %}">

View File

@ -5,9 +5,9 @@ django-auth-ldap==4.6.0
django-cors-headers==4.3.1 django-cors-headers==4.3.1
djangorestframework==3.14.0 djangorestframework==3.14.0
Pillow==10.1.0 Pillow==10.1.0
redis==5.0.1 redis==5.0.0
requests==2.31.0 requests==2.31.0
ryd-client==0.0.6 ryd-client==0.0.6
uWSGI==2.0.23 uWSGI==2.0.23
whitenoise==6.6.0 whitenoise==6.6.0
yt-dlp==2023.11.16 yt-dlp @ git+https://github.com/yt-dlp/yt-dlp@6b5d93b0b0240e287389d1d43b2d5293e18aa4cc

View File

@ -159,13 +159,13 @@ button:hover {
} }
.top-banner { .top-banner {
text-align: center; background-image: var(--banner);
} background-repeat: no-repeat;
background-size: contain;
.top-banner img { height: 10vh;
width: 100%; min-height: 80px;
max-width: 700px; max-height: 120px;
content: var(--banner); background-position: center center;
} }
.footer { .footer {