downloads: youtube 'open' goes to the in-app channel page, not youtube.com
the open button now opens the channel inside soulsync (channels-as-shows) via
video-open-detail {kind:'channel', source:'youtube', id}, matching how movie/show
cards open their in-app detail. enqueue_ctx now carries channel_id (new downloads)
and youtube_video_detail returns it too (so older downloads resolve the channel in
the drawer). drawer gets an 'Open channel' button alongside 'Open on YouTube';
falls back to the youtube video link only when the channel id is unknown.
This commit is contained in:
parent
3f924812da
commit
ca1348b2c2
5 changed files with 32 additions and 11 deletions
|
|
@ -50,6 +50,7 @@ def enqueue_ctx(video: Dict[str, Any], channel_settings: Dict[str, Any]) -> Dict
|
|||
cs = channel_settings if isinstance(channel_settings, dict) else {}
|
||||
ctx = {
|
||||
"channel": cs.get("custom_name") or video.get("channel_title"),
|
||||
"channel_id": video.get("channel_id"), # so the drawer can open the in-app channel page
|
||||
"video_title": video.get("video_title"),
|
||||
"published_at": video.get("published_at"),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1480,7 +1480,7 @@ class VideoDatabase:
|
|||
conn = self._get_connection()
|
||||
try:
|
||||
r = conn.execute(
|
||||
"SELECT title, thumbnail_url, duration, view_count "
|
||||
"SELECT channel_id, title, thumbnail_url, duration, view_count "
|
||||
"FROM youtube_channel_videos WHERE youtube_id=? LIMIT 1", (youtube_id,)).fetchone()
|
||||
return dict(r) if r else None
|
||||
finally:
|
||||
|
|
|
|||
|
|
@ -50,15 +50,15 @@ def test_drawer_renders_the_rich_tmdb_fields():
|
|||
assert "trailer_url" in _JS and "providers" in _JS
|
||||
|
||||
|
||||
def test_youtube_open_button_links_to_youtube_not_a_show_page():
|
||||
def test_youtube_open_button_opens_the_channel_page_not_a_show():
|
||||
# regression: the open-show button ran parseInt(youtube_id) → opened a random library
|
||||
# show (e.g. id 3) or a broken link. youtube cards must open a YouTube link instead.
|
||||
# show (id 3 → '3rd Rock'). youtube cards must open the in-app CHANNEL page instead.
|
||||
open_block = _JS.split("var openBtn")[1].split("var stateBtn")[0]
|
||||
assert "dlType(d.kind) === 'youtube'" in open_block
|
||||
assert "youtube.com/watch?v=" in open_block
|
||||
# the data-vdpg-open (open-show) path must NOT be taken for youtube
|
||||
assert "data-vdpg-open" in open_block # still there for movie/show
|
||||
assert open_block.index("'youtube'") < open_block.index("data-vdpg-open") # youtube branch first
|
||||
assert "data-vdpg-open-channel" in open_block # opens the in-app channel page
|
||||
assert "youtube.com/watch?v=" in open_block # fallback when the channel id is unknown
|
||||
# clicking it dispatches an open-detail for the channel (reuses the show-detail page)
|
||||
assert "kind: 'channel', source: 'youtube'" in _JS
|
||||
|
||||
|
||||
def test_drawer_has_episode_youtube_and_availability_blocks():
|
||||
|
|
|
|||
|
|
@ -34,6 +34,13 @@ def test_videos_to_enqueue_skips_already_queued_no_cap():
|
|||
assert [v["video_id"] for v in out] == ["a", "c", "d"] # b in flight; rest ALL kept
|
||||
|
||||
|
||||
def test_enqueue_ctx_carries_channel_id_for_the_drawer():
|
||||
from core.automation.handlers.video_process_youtube_wishlist import enqueue_ctx
|
||||
ctx = enqueue_ctx({"channel_id": "UC1", "channel_title": "Chan",
|
||||
"video_title": "V", "published_at": "2024-01-01"}, {})
|
||||
assert ctx["channel_id"] == "UC1" and ctx["channel"] == "Chan"
|
||||
|
||||
|
||||
def test_videos_to_enqueue_drops_idless():
|
||||
assert videos_to_enqueue([{"video_title": "no id"}], []) == []
|
||||
|
||||
|
|
|
|||
|
|
@ -154,12 +154,16 @@
|
|||
var lab = q('label'); if (lab.textContent !== labelTxt) lab.textContent = labelTxt;
|
||||
|
||||
var act = q('actions');
|
||||
// youtube videos aren't TMDB titles — the open button links to YouTube, not a show page
|
||||
// (parseInt on a video id was opening a random library show / a broken link).
|
||||
// youtube videos aren't TMDB titles. Open the in-app CHANNEL page (channels-as-shows)
|
||||
// when we know the channel id; fall back to the YouTube video link otherwise. (The old
|
||||
// open-show button ran parseInt on the video id → opened a random library show.)
|
||||
var ytCh = dlType(d.kind) === 'youtube' ? parseCtx(d).channel_id : null;
|
||||
var openBtn = !d.media_id ? ''
|
||||
: (dlType(d.kind) === 'youtube'
|
||||
? '<a class="vdpg-open" href="https://www.youtube.com/watch?v=' + encodeURIComponent(d.media_id) +
|
||||
'" target="_blank" rel="noopener" title="Open on YouTube">' + OPEN_SVG + '</a>'
|
||||
? (ytCh
|
||||
? '<button class="vdpg-open" type="button" data-vdpg-open-channel="' + esc(ytCh) + '" title="Open channel">' + OPEN_SVG + '</button>'
|
||||
: '<a class="vdpg-open" href="https://www.youtube.com/watch?v=' + encodeURIComponent(d.media_id) +
|
||||
'" target="_blank" rel="noopener" title="Open on YouTube">' + OPEN_SVG + '</a>')
|
||||
: '<button class="vdpg-open" type="button" data-vdpg-open="' + esc(d.media_id) +
|
||||
'" data-kind="' + esc(d.kind || 'movie') + '" data-source="' + esc(d.media_source || 'library') +
|
||||
'" title="Open ' + (d.kind === 'movie' ? 'movie' : 'show') + ' page">' + OPEN_SVG + '</button>');
|
||||
|
|
@ -296,6 +300,8 @@
|
|||
var btns = [];
|
||||
if (d.media_id && !isYt) btns.push('<button class="vdpg-dr-btn" type="button" data-vdpg-open="' + esc(d.media_id) +
|
||||
'" data-kind="' + esc(d.kind || 'movie') + '" data-source="' + esc(d.media_source || 'library') + '">Open in library</button>');
|
||||
var ytCh = meta.channel_id || ctx.channel_id;
|
||||
if (isYt && ytCh) btns.push('<button class="vdpg-dr-btn" type="button" data-vdpg-open-channel="' + esc(ytCh) + '">Open channel</button>');
|
||||
if (isYt && d.media_id) btns.push('<a class="vdpg-dr-btn" href="https://www.youtube.com/watch?v=' + encodeURIComponent(d.media_id) + '" target="_blank" rel="noopener">Open on YouTube</a>');
|
||||
if (isActive(d.status)) btns.push('<button class="vdpg-dr-btn vdpg-dr-danger" type="button" data-vdpg-cancel="' + d.id + '">Cancel</button>');
|
||||
else if (isFail(d.status)) btns.push('<button class="vdpg-dr-btn vdpg-dr-accent" type="button" data-vdpg-retry="' + d.id + '">Retry</button>');
|
||||
|
|
@ -437,6 +443,13 @@
|
|||
}));
|
||||
return;
|
||||
}
|
||||
var och = e.target.closest('[data-vdpg-open-channel]');
|
||||
if (och) {
|
||||
document.dispatchEvent(new CustomEvent('soulsync:video-open-detail', {
|
||||
detail: { kind: 'channel', source: 'youtube', id: och.getAttribute('data-vdpg-open-channel') }
|
||||
}));
|
||||
return;
|
||||
}
|
||||
var cp = e.target.closest('[data-vdpg-copy]');
|
||||
if (cp) {
|
||||
var path = cp.getAttribute('data-vdpg-copy');
|
||||
|
|
|
|||
Loading…
Reference in a new issue