video settings: per-connection Test button (mirrors music's testConnection)
Each video connection item (TMDB/TVDB) now has a Test button that behaves like
music's: saves the key, hits POST /api/video/enrichment/<svc>/test, and toasts
the result via the shared showToast — isolated (own endpoint, own data-attr
handler, reuses the .test-button CSS).
- Client .test() pings TMDB /configuration and TVDB /login to verify the key.
- Endpoint returns {success,message,error}; unknown service -> 404.
94 tests green; music untouched.
This commit is contained in:
parent
d35bb695ae
commit
2901e4ec4d
6 changed files with 75 additions and 1 deletions
|
|
@ -86,6 +86,18 @@ def register_routes(bp):
|
|||
w.resume()
|
||||
return jsonify({"status": "running"})
|
||||
|
||||
@bp.route("/enrichment/<service>/test", methods=["POST"])
|
||||
def video_enrichment_test(service):
|
||||
w = engine().worker(service)
|
||||
if not w:
|
||||
return jsonify({"success": False, "error": "unknown service"}), 404
|
||||
try:
|
||||
ok, msg = w.client.test()
|
||||
return jsonify({"success": bool(ok), "message": msg, "error": None if ok else msg})
|
||||
except Exception:
|
||||
logger.exception("video enrichment test failed for %s", service)
|
||||
return jsonify({"success": False, "error": "Test failed"})
|
||||
|
||||
@bp.route("/enrichment/<service>/breakdown", methods=["GET"])
|
||||
def video_enrichment_breakdown(service):
|
||||
from . import get_video_db
|
||||
|
|
|
|||
|
|
@ -31,6 +31,21 @@ class TMDBClient:
|
|||
def enabled(self):
|
||||
return bool(self.api_key)
|
||||
|
||||
def test(self):
|
||||
if not self.api_key:
|
||||
return False, "No TMDB API key set"
|
||||
import requests
|
||||
try:
|
||||
r = requests.get(self.BASE + "/configuration", params={"api_key": self.api_key}, timeout=12)
|
||||
if r.status_code == 200:
|
||||
return True, "TMDB connection OK"
|
||||
if r.status_code == 401:
|
||||
return False, "Invalid TMDB API key"
|
||||
return False, "TMDB returned HTTP " + str(r.status_code)
|
||||
except Exception:
|
||||
logger.exception("TMDB test failed")
|
||||
return False, "Could not reach TMDB"
|
||||
|
||||
def match(self, kind, title, year):
|
||||
if not self.api_key or not title:
|
||||
return None
|
||||
|
|
@ -77,6 +92,18 @@ class TVDBClient:
|
|||
def enabled(self):
|
||||
return bool(self.api_key)
|
||||
|
||||
def test(self):
|
||||
if not self.api_key:
|
||||
return False, "No TVDB API key set"
|
||||
try:
|
||||
token = self._auth()
|
||||
if token:
|
||||
return True, "TVDB connection OK"
|
||||
return False, "TVDB login failed — check the key"
|
||||
except Exception:
|
||||
logger.exception("TVDB test failed")
|
||||
return False, "Could not reach TVDB"
|
||||
|
||||
def _auth(self):
|
||||
if self._token:
|
||||
return self._token
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ def test_blueprint_exposes_dashboard_route():
|
|||
assert "/api/video/enrichment/<service>/status" in rules
|
||||
assert "/api/video/enrichment/<service>/unmatched" in rules
|
||||
assert "/api/video/enrichment/config" in rules
|
||||
assert "/api/video/enrichment/<service>/test" in rules
|
||||
|
||||
|
||||
def test_dashboard_endpoint_returns_zeroed_json(tmp_path):
|
||||
|
|
@ -100,6 +101,7 @@ def test_enrichment_endpoints(tmp_path):
|
|||
class FakeClient:
|
||||
enabled = True
|
||||
def match(self, *a): return None
|
||||
def test(self): return (True, "ok")
|
||||
|
||||
db = VideoDatabase(database_path=str(tmp_path / "video_library.db"))
|
||||
videoapi._video_db = db
|
||||
|
|
@ -125,6 +127,8 @@ def test_enrichment_endpoints(tmp_path):
|
|||
assert client.post("/api/video/enrichment/tmdb/resume").get_json()["status"] == "running"
|
||||
assert client.post("/api/video/enrichment/tmdb/retry",
|
||||
json={"kind": "movie", "scope": "failed"}).get_json()["reset"] == 1
|
||||
assert client.post("/api/video/enrichment/tmdb/test").get_json()["success"] is True
|
||||
assert client.post("/api/video/enrichment/nope/test").status_code == 404
|
||||
assert client.get("/api/video/enrichment/nope/status").status_code == 404
|
||||
finally:
|
||||
videoapi._video_db = None
|
||||
|
|
|
|||
|
|
@ -237,6 +237,8 @@ def test_video_side_hides_music_api_config_and_shows_placeholders():
|
|||
assert 'class="api-service-frame stg-service" data-video-service="tvdb"' in _INDEX
|
||||
# They must NOT carry the music-hooked data-service attribute.
|
||||
assert 'data-service="tmdb"' not in _INDEX and 'data-service="tvdb"' not in _INDEX
|
||||
# Each connection item has a Test button (like music's connections).
|
||||
assert 'data-video-test-service="tmdb"' in _INDEX and 'data-video-test-service="tvdb"' in _INDEX
|
||||
|
||||
|
||||
def test_dashboard_enrichment_buttons_present():
|
||||
|
|
|
|||
|
|
@ -4856,6 +4856,7 @@
|
|||
<div class="callback-help">Get your key from <a href="https://www.themoviedb.org/settings/api" target="_blank" style="color: #01b4e4;">TMDB API Settings</a></div>
|
||||
<div class="callback-help">Primary movie & TV metadata source.</div>
|
||||
</div>
|
||||
<button class="test-button" type="button" data-video-test-service="tmdb">Test TMDB</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="api-service-frame stg-service" data-video-service="tvdb">
|
||||
|
|
@ -4873,6 +4874,7 @@
|
|||
<div class="callback-help">Get your key from <a href="https://thetvdb.com/dashboard/account/apikey" target="_blank" style="color: #6cd391;">TheTVDB API</a></div>
|
||||
<div class="callback-help">TV series metadata source.</div>
|
||||
</div>
|
||||
<button class="test-button" type="button" data-video-test-service="tvdb">Test TVDB</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -80,13 +80,31 @@
|
|||
function saveKeys() {
|
||||
var t = document.getElementById('tmdb-api-key');
|
||||
var v = document.getElementById('tvdb-api-key');
|
||||
fetch(CONFIG_URL, {
|
||||
return fetch(CONFIG_URL, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' },
|
||||
body: JSON.stringify({ tmdb_api_key: t ? t.value : '', tvdb_api_key: v ? v.value : '' })
|
||||
}).catch(function () { /* ignore */ });
|
||||
}
|
||||
|
||||
function toast(msg, type) {
|
||||
if (typeof showToast === 'function') showToast(msg, type); // shared shell helper
|
||||
}
|
||||
|
||||
// Mirrors music's testConnection(): save the key, then hit the test
|
||||
// endpoint, then toast the result. Isolated -> /api/video/enrichment/<svc>/test.
|
||||
function testConnection(svc) {
|
||||
var name = svc.toUpperCase();
|
||||
toast('Testing ' + name + ' connection…', 'info');
|
||||
saveKeys().then(function () {
|
||||
return fetch('/api/video/enrichment/' + svc + '/test',
|
||||
{ method: 'POST', headers: { 'Accept': 'application/json' } });
|
||||
}).then(function (r) { return r.json(); }).then(function (res) {
|
||||
if (res && res.success) toast(res.message || (name + ' connection successful'), 'success');
|
||||
else toast(name + ' connection failed: ' + ((res && res.error) || 'unknown'), 'error');
|
||||
}).catch(function () { toast('Failed to test ' + name + ' connection', 'error'); });
|
||||
}
|
||||
|
||||
function onPageShown(e) {
|
||||
if (e && e.detail !== PAGE_ID) return;
|
||||
load();
|
||||
|
|
@ -105,6 +123,15 @@
|
|||
var el = document.getElementById(id);
|
||||
if (el) el.addEventListener('change', saveKeys);
|
||||
});
|
||||
// Per-connection Test buttons (same behaviour as music's testConnection).
|
||||
var testBtns = document.querySelectorAll('[data-video-test-service]');
|
||||
for (var k = 0; k < testBtns.length; k++) {
|
||||
(function (b) {
|
||||
b.addEventListener('click', function () {
|
||||
testConnection(b.getAttribute('data-video-test-service'));
|
||||
});
|
||||
})(testBtns[k]);
|
||||
}
|
||||
document.addEventListener('soulsync:video-page-shown', onPageShown);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue