Fix startup migration, #build

Changed:
- Fixed startup migration for nested dict update by query
- Added pot provider plugin noop default
- Fixed timeout in json backups fetching
- Fixed date parser fload in search processing
This commit is contained in:
Simon 2026-01-30 19:40:42 +07:00
commit df19f44394
No known key found for this signature in database
GPG Key ID: 2C15AA5E89985DD4
10 changed files with 59 additions and 9 deletions

View File

@ -134,6 +134,7 @@ The documentation available at [docs.tubearchivist.com](https://docs.tubearchivi
This codebase is set up to be developed natively outside of docker as well as in a docker container. Developing outside of a docker container can be convenient, as IDE and hot reload usually works out of the box. But testing inside of a container is still essential, as there are subtle differences, especially when working with the filesystem and networking between containers. This codebase is set up to be developed natively outside of docker as well as in a docker container. Developing outside of a docker container can be convenient, as IDE and hot reload usually works out of the box. But testing inside of a container is still essential, as there are subtle differences, especially when working with the filesystem and networking between containers.
Note: Note:
- This project doesn't look for contributions to this to cover additional dev setup environments from new contributors. If you are a regular contributor and you see ways to improve this, please reach out on Discord first.
- Subtitles currently fail to load with `DJANGO_DEBUG=True`, that is due to incorrect `Content-Type` error set by Django's static file implementation. That's only if you run the Django dev server, Nginx sets the correct headers in the container. - Subtitles currently fail to load with `DJANGO_DEBUG=True`, that is due to incorrect `Content-Type` error set by Django's static file implementation. That's only if you run the Django dev server, Nginx sets the correct headers in the container.
### Native Instruction ### Native Instruction

View File

@ -29,3 +29,4 @@ class MembershipProfileSerializer(serializers.Serializer):
sponsor_tier = SponsortierSerializer() sponsor_tier = SponsortierSerializer()
subscription_count = serializers.IntegerField() subscription_count = serializers.IntegerField()
subscription_is_max = serializers.BooleanField() subscription_is_max = serializers.BooleanField()
is_connected = serializers.BooleanField()

View File

@ -21,7 +21,7 @@ class ElasticBackup:
"""dump index to nd-json files for later bulk import""" """dump index to nd-json files for later bulk import"""
INDEX_SIZE_CONF = { INDEX_SIZE_CONF = {
"comment": 200, "comment": 100,
"subtitle": 10000, "subtitle": 10000,
} }
CACHE_DIR = EnvironmentSettings.CACHE_DIR CACHE_DIR = EnvironmentSettings.CACHE_DIR
@ -64,6 +64,7 @@ class ElasticBackup:
"callback": BackupCallback, "callback": BackupCallback,
"task": self.task, "task": self.task,
"total": self._get_total(index_name), "total": self._get_total(index_name),
"timeout": 30,
} }
if size_overwrite := self.INDEX_SIZE_CONF.get(index_name): if size_overwrite := self.INDEX_SIZE_CONF.get(index_name):

View File

@ -148,6 +148,7 @@ class IndexPaginate:
- callback: obj, Class implementing run method callback for every loop - callback: obj, Class implementing run method callback for every loop
- task: task object to send notification - task: task object to send notification
- total: int, total items in index for progress message - total: int, total items in index for progress message
- timeout: int, overwrite timeout in get request
""" """
DEFAULT_SIZE = 500 DEFAULT_SIZE = 500
@ -191,7 +192,11 @@ class IndexPaginate:
all_results = [] all_results = []
counter = 0 counter = 0
while True: while True:
response, _ = ElasticWrap("_search").get(data=self.data) get_kwargs = {"data": self.data}
if timeout_overwrite := self.kwargs.get("timeout"):
get_kwargs.update({"timeout": timeout_overwrite})
response, _ = ElasticWrap("_search").get(**get_kwargs)
all_hits = response["hits"]["hits"] all_hits = response["hits"]["hits"]
if not all_hits: if not all_hits:
break break

View File

@ -103,13 +103,15 @@ def requests_headers() -> dict[str, str]:
return {"User-Agent": template} return {"User-Agent": template}
def date_parser(timestamp: int | str | None) -> str | None: def date_parser(timestamp: int | float | str | None) -> str | None:
"""return formatted date string""" """return formatted date string"""
if timestamp is None: if timestamp is None:
return None return None
if isinstance(timestamp, int): if isinstance(timestamp, int):
date_obj = datetime.fromtimestamp(timestamp, tz=timezone.utc) date_obj = datetime.fromtimestamp(timestamp, tz=timezone.utc)
elif isinstance(timestamp, float):
date_obj = datetime.fromtimestamp(int(timestamp), tz=timezone.utc)
elif isinstance(timestamp, str) and timestamp.isdigit(): elif isinstance(timestamp, str) and timestamp.isdigit():
date_obj = datetime.fromtimestamp(int(timestamp), tz=timezone.utc) date_obj = datetime.fromtimestamp(int(timestamp), tz=timezone.utc)
elif isinstance(timestamp, str): elif isinstance(timestamp, str):

View File

@ -33,6 +33,13 @@ def test_date_parser_with_digit():
assert date_parser(timestamp) == expected_date assert date_parser(timestamp) == expected_date
def test_date_parser_with_float():
"""iso timestamp"""
date_float = 1766210400.0
expected_date = "2025-12-20T06:00:00+00:00"
assert date_parser(date_float) == expected_date
def test_date_parser_with_str(): def test_date_parser_with_str():
"""iso timestamp""" """iso timestamp"""
date_str = "2021-05-21" date_str = "2021-05-21"

View File

@ -376,14 +376,15 @@ class Command(BaseCommand):
"lang": "painless", "lang": "painless",
}, },
) )
source = f"""
if (ctx._source.containsKey('channel'))
{{ctx._source.channel.remove('{field}');}}
"""
self._run_migration( self._run_migration(
index_name="ta_video", index_name="ta_video",
desc=f"fix missing data type for field channel.{field}", desc=f"fix missing data type for field channel.{field}",
query={"term": {f"channel.{field}": {"value": False}}}, query={"term": {f"channel.{field}": {"value": False}}},
script={ script={"source": source, "lang": "painless"},
"source": f"ctx._source.remove('channel.{field}')",
"lang": "painless",
},
) )
def _mig_fix_channel_description(self) -> None: def _mig_fix_channel_description(self) -> None:

View File

@ -89,6 +89,17 @@ class YtWrap:
} }
}, },
) )
return
# https://github.com/Brainicism/bgutil-ytdlp-pot-provider/pull/185
deep_merge(
self.obs,
{
"extractor_args": {
"youtubepot-bgutilhttp": {"disable": ["True"]}
}
},
)
def download(self, url): def download(self, url):
"""make download request""" """make download request"""

View File

@ -1,5 +1,5 @@
apprise==1.9.7 apprise==1.9.7
bgutil-ytdlp-pot-provider==1.2.2 bgutil-ytdlp-pot-provider @ git+https://github.com/bbilly1/bgutil-ytdlp-pot-provider@c3f7bf4c99f8eb62015cb1c4142324f3d681cf07#subdirectory=plugin
celery==5.6.2 celery==5.6.2
deepdiff==8.6.1 deepdiff==8.6.1
django-auth-ldap==5.3.0 django-auth-ldap==5.3.0
@ -13,4 +13,4 @@ redis==7.1.0
requests==2.32.5 requests==2.32.5
ryd-client==0.0.6 ryd-client==0.0.6
uvicorn==0.40.0 uvicorn==0.40.0
yt-dlp[default] @ git+https://github.com/yt-dlp/yt-dlp@23b846506378a6a9c9a0958382d37f943f7cfa51 yt-dlp[default]==2026.1.29

View File

@ -24,6 +24,7 @@ type ProfileResponseType = {
sponsor_tier: SponsorTierType; sponsor_tier: SponsorTierType;
subscription_count: number; subscription_count: number;
subscription_is_max: boolean; subscription_is_max: boolean;
is_connected: boolean;
}; };
export default function MembershipAppsettings({ show_help_text }: { show_help_text: boolean }) { export default function MembershipAppsettings({ show_help_text }: { show_help_text: boolean }) {
@ -136,6 +137,13 @@ export default function MembershipAppsettings({ show_help_text }: { show_help_te
. .
</li> </li>
<li>Click on validate to verify everything is working.</li> <li>Click on validate to verify everything is working.</li>
<li>
Setup the{' '}
<a href="https://github.com/tubearchivist/members" target="_blank">
Client Container
</a>
.
</li>
<li> <li>
If you are subscribed to less channels than your sponsor tier allows, you can directly If you are subscribed to less channels than your sponsor tier allows, you can directly
sync all your subscriptions here. sync all your subscriptions here.
@ -206,6 +214,19 @@ export default function MembershipAppsettings({ show_help_text }: { show_help_te
<br /> <br />
Subscriptions: {profileResponse.subscription_count}/ Subscriptions: {profileResponse.subscription_count}/
{profileResponse.sponsor_tier.max_subs} {profileResponse.sponsor_tier.max_subs}
<br />
Socket:{' '}
{profileResponse.is_connected ? (
'Established'
) : (
<>
Not established. Make sure the{' '}
<a href="https://github.com/tubearchivist/members" target="_blank">
Client Container
</a>{' '}
is connected.
</>
)}
</p> </p>
</> </>
)} )}