Clarify local provider update status
This commit is contained in:
@@ -1,5 +1,10 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 0.52.19 - 2026-09-01
|
||||||
|
|
||||||
|
- Changed the provider update check to report newer local provider patches separately from upstream updates, avoiding false update warnings for the patched Anikoto module.
|
||||||
|
- Updated the Config page provider badge list to show local provider patch versions.
|
||||||
|
|
||||||
## 0.52.18 - 2026-08-30
|
## 0.52.18 - 2026-08-30
|
||||||
|
|
||||||
- Fixed Anikoto dub availability detection when episode-list flags report sub-only episodes but the episode server list exposes dub streams.
|
- Fixed Anikoto dub availability detection when episode-list flags report sub-only episodes but the episode server list exposes dub streams.
|
||||||
|
|||||||
@@ -116,4 +116,4 @@ The Anikoto, AniNeko, and AnimePahe parser modules in `providers/extensions/Anim
|
|||||||
- AniNeko `3.0.3`
|
- AniNeko `3.0.3`
|
||||||
- AnimePahe `4.0.1`
|
- AnimePahe `4.0.1`
|
||||||
|
|
||||||
The Config page checks the bundled JavaScript against the upstream `main` branch and reports whether local provider files differ. Kaizoku acts as a local client-side parser/downloader wrapper and does not host media.
|
The Config page checks the bundled JavaScript against the upstream `main` branch and reports whether upstream updates are available. If Kaizoku carries a newer local provider patch than upstream, such as the patched Anikoto module, the Config page reports it separately as a local change. Kaizoku acts as a local client-side parser/downloader wrapper and does not host media.
|
||||||
|
|||||||
@@ -1101,6 +1101,7 @@ CONFIG_HTML = r"""<!doctype html>
|
|||||||
|
|
||||||
function providerUpdateBadgeClass(status) {
|
function providerUpdateBadgeClass(status) {
|
||||||
if (status === "up_to_date") return "ok";
|
if (status === "up_to_date") return "ok";
|
||||||
|
if (status === "local_changes") return "ok";
|
||||||
if (status === "updates_available") return "warn";
|
if (status === "updates_available") return "warn";
|
||||||
if (status === "error") return "bad";
|
if (status === "error") return "bad";
|
||||||
return "";
|
return "";
|
||||||
@@ -1117,6 +1118,7 @@ CONFIG_HTML = r"""<!doctype html>
|
|||||||
|
|
||||||
const files = [
|
const files = [
|
||||||
...(data.updated_files || []).map((item) => item.path),
|
...(data.updated_files || []).map((item) => item.path),
|
||||||
|
...(data.locally_modified_files || []).map((item) => `${item.path} local ${item.local_version || "modified"}`),
|
||||||
...(data.missing_upstream_files || [])
|
...(data.missing_upstream_files || [])
|
||||||
];
|
];
|
||||||
if (!files.length && status !== "checking") {
|
if (!files.length && status !== "checking") {
|
||||||
|
|||||||
+36
-5
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
import hashlib
|
import hashlib
|
||||||
import json
|
import json
|
||||||
|
import re
|
||||||
import urllib.error
|
import urllib.error
|
||||||
import urllib.request
|
import urllib.request
|
||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
@@ -36,6 +37,7 @@ def default_status(status="idle", message="Provider update check has not run yet
|
|||||||
"upstream_tree_sha": "",
|
"upstream_tree_sha": "",
|
||||||
"checked_files": 0,
|
"checked_files": 0,
|
||||||
"updated_files": [],
|
"updated_files": [],
|
||||||
|
"locally_modified_files": [],
|
||||||
"missing_upstream_files": [],
|
"missing_upstream_files": [],
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,6 +57,25 @@ def sha256_bytes(content):
|
|||||||
return hashlib.sha256(content).hexdigest()
|
return hashlib.sha256(content).hexdigest()
|
||||||
|
|
||||||
|
|
||||||
|
def provider_module_version(content):
|
||||||
|
match = re.search(rb'version:\s*["\']([^"\']+)["\']', content)
|
||||||
|
if not match:
|
||||||
|
return ""
|
||||||
|
return match.group(1).decode("utf-8", errors="replace")
|
||||||
|
|
||||||
|
|
||||||
|
def compare_versions(left, right):
|
||||||
|
left_parts = [int(part) for part in re.findall(r"\d+", left or "")]
|
||||||
|
right_parts = [int(part) for part in re.findall(r"\d+", right or "")]
|
||||||
|
length = max(len(left_parts), len(right_parts))
|
||||||
|
for index in range(length):
|
||||||
|
left_value = left_parts[index] if index < len(left_parts) else 0
|
||||||
|
right_value = right_parts[index] if index < len(right_parts) else 0
|
||||||
|
if left_value != right_value:
|
||||||
|
return 1 if left_value > right_value else -1
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
def fetch_json(url, timeout=10):
|
def fetch_json(url, timeout=10):
|
||||||
request = urllib.request.Request(
|
request = urllib.request.Request(
|
||||||
url,
|
url,
|
||||||
@@ -100,6 +121,7 @@ def check_provider_updates(local_root=LOCAL_PROVIDER_ROOT, timeout=10):
|
|||||||
|
|
||||||
upstream_paths, upstream_tree_sha = upstream_provider_js_paths(timeout=timeout)
|
upstream_paths, upstream_tree_sha = upstream_provider_js_paths(timeout=timeout)
|
||||||
updated_files = []
|
updated_files = []
|
||||||
|
locally_modified_files = []
|
||||||
missing_upstream_files = []
|
missing_upstream_files = []
|
||||||
checked_files = 0
|
checked_files = 0
|
||||||
|
|
||||||
@@ -114,19 +136,27 @@ def check_provider_updates(local_root=LOCAL_PROVIDER_ROOT, timeout=10):
|
|||||||
local_hash = sha256_bytes(local_content)
|
local_hash = sha256_bytes(local_content)
|
||||||
remote_hash = sha256_bytes(remote_content)
|
remote_hash = sha256_bytes(remote_content)
|
||||||
if local_hash != remote_hash:
|
if local_hash != remote_hash:
|
||||||
updated_files.append(
|
local_version = provider_module_version(local_content)
|
||||||
{
|
upstream_version = provider_module_version(remote_content)
|
||||||
|
item = {
|
||||||
"path": relative_path,
|
"path": relative_path,
|
||||||
"local_sha256": local_hash,
|
"local_sha256": local_hash,
|
||||||
"upstream_sha256": remote_hash,
|
"upstream_sha256": remote_hash,
|
||||||
"upstream_url": remote_url,
|
"upstream_url": remote_url,
|
||||||
|
"local_version": local_version,
|
||||||
|
"upstream_version": upstream_version,
|
||||||
}
|
}
|
||||||
)
|
if local_version and upstream_version and compare_versions(local_version, upstream_version) > 0:
|
||||||
|
locally_modified_files.append(item)
|
||||||
|
else:
|
||||||
|
updated_files.append(item)
|
||||||
|
|
||||||
has_updates = bool(updated_files or missing_upstream_files)
|
if updated_files or missing_upstream_files:
|
||||||
if has_updates:
|
|
||||||
message = "Provider updates are available from TheYogMehta/extensions."
|
message = "Provider updates are available from TheYogMehta/extensions."
|
||||||
status_name = "updates_available"
|
status_name = "updates_available"
|
||||||
|
elif locally_modified_files:
|
||||||
|
message = "Bundled provider JavaScript includes local changes ahead of upstream."
|
||||||
|
status_name = "local_changes"
|
||||||
else:
|
else:
|
||||||
message = "Bundled provider JavaScript is up to date."
|
message = "Bundled provider JavaScript is up to date."
|
||||||
status_name = "up_to_date"
|
status_name = "up_to_date"
|
||||||
@@ -140,6 +170,7 @@ def check_provider_updates(local_root=LOCAL_PROVIDER_ROOT, timeout=10):
|
|||||||
"upstream_tree_sha": upstream_tree_sha,
|
"upstream_tree_sha": upstream_tree_sha,
|
||||||
"checked_files": checked_files,
|
"checked_files": checked_files,
|
||||||
"updated_files": updated_files,
|
"updated_files": updated_files,
|
||||||
|
"locally_modified_files": locally_modified_files,
|
||||||
"missing_upstream_files": missing_upstream_files,
|
"missing_upstream_files": missing_upstream_files,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+20
@@ -3479,6 +3479,26 @@ class ProviderUpdateTests(unittest.TestCase):
|
|||||||
self.assertEqual(status["checked_files"], 1)
|
self.assertEqual(status["checked_files"], 1)
|
||||||
self.assertEqual([item["path"] for item in status["updated_files"]], ["Anime/pahe.js"])
|
self.assertEqual([item["path"] for item in status["updated_files"]], ["Anime/pahe.js"])
|
||||||
|
|
||||||
|
def test_provider_update_check_reports_local_provider_patch(self):
|
||||||
|
with tempfile.TemporaryDirectory() as tmp:
|
||||||
|
root = Path(tmp)
|
||||||
|
provider_file = root / "Anime" / "anikoto.js"
|
||||||
|
provider_file.parent.mkdir(parents=True)
|
||||||
|
provider_file.write_text('module.exports = { version: "5.0.3" };\n', encoding="utf-8")
|
||||||
|
|
||||||
|
with mock.patch.object(
|
||||||
|
provider_updates, "upstream_provider_js_paths", return_value=({"Anime/anikoto.js"}, "tree-sha")
|
||||||
|
), mock.patch.object(
|
||||||
|
provider_updates, "fetch_bytes", return_value=b'module.exports = { version: "5.0.2" };\n'
|
||||||
|
):
|
||||||
|
status = provider_updates.check_provider_updates(local_root=root)
|
||||||
|
|
||||||
|
self.assertEqual(status["status"], "local_changes")
|
||||||
|
self.assertEqual(status["updated_files"], [])
|
||||||
|
self.assertEqual([item["path"] for item in status["locally_modified_files"]], ["Anime/anikoto.js"])
|
||||||
|
self.assertEqual(status["locally_modified_files"][0]["local_version"], "5.0.3")
|
||||||
|
self.assertEqual(status["locally_modified_files"][0]["upstream_version"], "5.0.2")
|
||||||
|
|
||||||
def test_provider_update_api_returns_current_status(self):
|
def test_provider_update_api_returns_current_status(self):
|
||||||
status = provider_updates.default_status("updates_available", "Providers changed.")
|
status = provider_updates.default_status("updates_available", "Providers changed.")
|
||||||
status["checked_files"] = 3
|
status["checked_files"] = 3
|
||||||
|
|||||||
Reference in New Issue
Block a user