Video enrichment: surface enriched YouTube data
- YouTube video cards show 👍/👎 like/dislike counts (from Return YouTube Dislike) next to views, threaded through ytEpisodeOf. - New GET /youtube/video/<id>/segments exposes stored SponsorBlock segments so the player can offer skips. Update config-shape test for the new keys/toggles.
This commit is contained in:
parent
21a1e5ddbb
commit
6f14f15dd6
3 changed files with 20 additions and 2 deletions
|
|
@ -36,6 +36,14 @@ def _server():
|
||||||
|
|
||||||
|
|
||||||
def register_routes(bp):
|
def register_routes(bp):
|
||||||
|
@bp.route("/youtube/video/<video_id>/segments", methods=["GET"])
|
||||||
|
def video_youtube_segments(video_id):
|
||||||
|
"""SponsorBlock crowd segments stored for a video (sponsor/intro/outro/…),
|
||||||
|
so the player can offer skips. [] until the SponsorBlock worker enriches it."""
|
||||||
|
from . import get_video_db
|
||||||
|
return jsonify({"video_id": video_id,
|
||||||
|
"segments": get_video_db().youtube_video_segments(video_id)})
|
||||||
|
|
||||||
@bp.route("/youtube/resolve", methods=["GET"])
|
@bp.route("/youtube/resolve", methods=["GET"])
|
||||||
def video_youtube_resolve():
|
def video_youtube_resolve():
|
||||||
"""Preview a pasted channel URL without committing. Returns the channel +
|
"""Preview a pasted channel URL without committing. Returns the channel +
|
||||||
|
|
|
||||||
|
|
@ -334,12 +334,18 @@ def test_enrichment_config_save_load(tmp_path, monkeypatch):
|
||||||
try:
|
try:
|
||||||
assert client.get("/api/video/enrichment/config").get_json() == {
|
assert client.get("/api/video/enrichment/config").get_json() == {
|
||||||
"tmdb_api_key": "", "tvdb_api_key": "", "omdb_api_key": "",
|
"tmdb_api_key": "", "tvdb_api_key": "", "omdb_api_key": "",
|
||||||
|
"fanart_api_key": "", "opensubtitles_api_key": "",
|
||||||
|
"ryd_enabled": True, "sponsorblock_enabled": True,
|
||||||
"billboard_autoplay": True, "watch_region": "US"}
|
"billboard_autoplay": True, "watch_region": "US"}
|
||||||
client.post("/api/video/enrichment/config",
|
client.post("/api/video/enrichment/config",
|
||||||
json={"tmdb_api_key": "abc", "tvdb_api_key": "xyz", "omdb_api_key": "om",
|
json={"tmdb_api_key": "abc", "tvdb_api_key": "xyz", "omdb_api_key": "om",
|
||||||
|
"fanart_api_key": "fa", "opensubtitles_api_key": "os",
|
||||||
|
"ryd_enabled": False, "sponsorblock_enabled": True,
|
||||||
"billboard_autoplay": False, "watch_region": "gb"})
|
"billboard_autoplay": False, "watch_region": "gb"})
|
||||||
assert client.get("/api/video/enrichment/config").get_json() == {
|
assert client.get("/api/video/enrichment/config").get_json() == {
|
||||||
"tmdb_api_key": "abc", "tvdb_api_key": "xyz", "omdb_api_key": "om",
|
"tmdb_api_key": "abc", "tvdb_api_key": "xyz", "omdb_api_key": "om",
|
||||||
|
"fanart_api_key": "fa", "opensubtitles_api_key": "os",
|
||||||
|
"ryd_enabled": False, "sponsorblock_enabled": True,
|
||||||
"billboard_autoplay": False, "watch_region": "GB"}
|
"billboard_autoplay": False, "watch_region": "GB"}
|
||||||
assert db.get_setting("tmdb_api_key") == "abc" and db.get_setting("omdb_api_key") == "om"
|
assert db.get_setting("tmdb_api_key") == "abc" and db.get_setting("omdb_api_key") == "om"
|
||||||
assert client.get("/api/video/prefs").get_json() == {
|
assert client.get("/api/video/prefs").get_json() == {
|
||||||
|
|
|
||||||
|
|
@ -975,7 +975,10 @@
|
||||||
: '';
|
: '';
|
||||||
var dur = ep.yt_duration ? '<span class="vd-ep-dur">' + esc(ep.yt_duration) + '</span>' : '';
|
var dur = ep.yt_duration ? '<span class="vd-ep-dur">' + esc(ep.yt_duration) + '</span>' : '';
|
||||||
var meta = [];
|
var meta = [];
|
||||||
if (ep.view_count) { var yc = window.VideoYoutube; meta.push((yc ? yc.compactCount(ep.view_count) : ep.view_count) + ' views'); }
|
var yc0 = window.VideoYoutube;
|
||||||
|
if (ep.view_count) { meta.push((yc0 ? yc0.compactCount(ep.view_count) : ep.view_count) + ' views'); }
|
||||||
|
if (ep.like_count) { meta.push('👍 ' + (yc0 ? yc0.compactCount(ep.like_count) : ep.like_count)); }
|
||||||
|
if (ep.dislike_count) { meta.push('👎 ' + (yc0 ? yc0.compactCount(ep.dislike_count) : ep.dislike_count)); }
|
||||||
if (ep.air_date) meta.push(fmtDate(ep.air_date));
|
if (ep.air_date) meta.push(fmtDate(ep.air_date));
|
||||||
var wished = !!ep.owned;
|
var wished = !!ep.owned;
|
||||||
return '<div class="vd-ep vd-ep--yt" data-vd-ep-key="' + key + '" data-vd-yt-vid="' + esc(ep.youtube_id) + '">' +
|
return '<div class="vd-ep vd-ep--yt" data-vd-ep-key="' + key + '" data-vd-yt-vid="' + esc(ep.youtube_id) + '">' +
|
||||||
|
|
@ -1373,7 +1376,8 @@
|
||||||
return { episode_number: i + 1, title: v.title, overview: v.description || '',
|
return { episode_number: i + 1, title: v.title, overview: v.description || '',
|
||||||
air_date: v.published_at, owned: !!v.wished, has_still: false,
|
air_date: v.published_at, owned: !!v.wished, has_still: false,
|
||||||
still_url: ytProx(v.thumbnail_url), youtube_id: v.youtube_id,
|
still_url: ytProx(v.thumbnail_url), youtube_id: v.youtube_id,
|
||||||
yt_duration: v.duration || '', view_count: v.view_count || 0 };
|
yt_duration: v.duration || '', view_count: v.view_count || 0,
|
||||||
|
like_count: v.like_count || 0, dislike_count: v.dislike_count || 0 };
|
||||||
}
|
}
|
||||||
function ytDurSecs(d) {
|
function ytDurSecs(d) {
|
||||||
if (!d) return 0;
|
if (!d) return 0;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue