From d6dfb7a6ba0feec8d6ada0004ccbcc1e927e84b6 Mon Sep 17 00:00:00 2001 From: Abel <196466003+DisabledAbel@users.noreply.github.com> Date: Wed, 13 May 2026 21:33:12 -0700 Subject: [PATCH 1/5] Always return official YouTube feed URLs in API responses --- api/app.py | 5 +++-- rss_scanner.py | 26 +++++++++++++++++++++----- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/api/app.py b/api/app.py index bf5b252..db73eea 100644 --- a/api/app.py +++ b/api/app.py @@ -63,7 +63,7 @@ def api_feed(): include_api_endpoints = False base_url = request.host_url.rstrip('/') - youtube_rss, channel_id, channel_name, atom_feed, video_count, _, api_endpoints = rss_scanner.get_rss_feed( + youtube_rss, channel_id, channel_name, atom_feed, video_count, _, api_endpoints, official_feeds = rss_scanner.get_rss_feed( url, include_api_endpoints=include_api_endpoints, base_url=base_url, @@ -94,6 +94,7 @@ def api_feed(): 'atom_feed': atom_feed, 'video_count': video_count, 'api_endpoints': api_endpoints, + 'official_feeds': official_feeds, 'discord': discord_result }), mimetype='application/json') except Exception as e: @@ -167,7 +168,7 @@ def get_feed(feed_type, channel_url=None): full_url = channel_url try: - _, channel_id, channel_name, atom_feed, video_count, _, _ = rss_scanner.get_rss_feed(full_url, feed_type=feed_type) + _, channel_id, channel_name, atom_feed, video_count, _, _, _ = rss_scanner.get_rss_feed(full_url, feed_type=feed_type) if atom_feed: # Fix channel name in feed diff --git a/rss_scanner.py b/rss_scanner.py index e2d19e2..1467641 100644 --- a/rss_scanner.py +++ b/rss_scanner.py @@ -91,7 +91,7 @@ def generate_atom_feed(channel_id: str, channel_name: str, videos: list[dict]) - return feed PATTERNS = [ - (r"/channel/([a-zA-Z0-9_-]{22})", "channel"), + (r"/channel/(UC[a-zA-Z0-9_-]{22})", "channel"), (r"/c/([a-zA-Z0-9_-]+)", "custom"), (r"/user/([a-zA-Z0-9_-]+)", "user"), (r"/@([a-zA-Z0-9_-]+)", "handle"), @@ -295,15 +295,31 @@ def build_youtube_feed_url(channel_id: str, feed_type: str = None) -> str: """Build a YouTube RSS feed URL for the given channel ID.""" return f"https://www.youtube.com/feeds/videos.xml?channel_id={channel_id}" + + +def build_official_feeds(channel_id: str, feed_type: str = "all") -> dict[str, str]: + """Return official YouTube feed URLs keyed by feed type.""" + all_feed = build_youtube_feed_url(channel_id, feed_type="all") + feeds = { + "all": all_feed, + "videos": all_feed, + "shorts": all_feed, + "live": all_feed, + } + selected_type = feed_type if feed_type in feeds else "all" + feeds["selected"] = feeds[selected_type] + return feeds + def get_rss_feed(url: str, include_api_endpoints: bool = False, base_url: str = "http://localhost:8080", feed_type: str = "all") -> tuple: """Get RSS feed data for a YouTube channel. - Returns: (youtube_rss, channel_id, channel_name, atom_feed, video_count, invidious_rss, api_endpoints) + Returns: (youtube_rss, channel_id, channel_name, atom_feed, video_count, invidious_rss, api_endpoints, official_feeds) """ channel_id, channel_name = extract_channel_id(url) # YouTube RSS URL (supports hidden filtered variants for shorts/live) - youtube_rss = build_youtube_feed_url(channel_id, feed_type=feed_type) + official_feeds = build_official_feeds(channel_id, feed_type=feed_type) + youtube_rss = official_feeds["selected"] # Try to get videos from the selected YouTube channel page videos = get_channel_videos(channel_id, feed_type=feed_type) @@ -337,7 +353,7 @@ def get_rss_feed(url: str, include_api_endpoints: bool = False, base_url: str = "live_feed": f"{base_url.rstrip('/')}/feed/live/{encoded_url}", } - return youtube_rss, channel_id, channel_name, atom_feed, video_count, invidious_rss, api_endpoints + return youtube_rss, channel_id, channel_name, atom_feed, video_count, invidious_rss, api_endpoints, official_feeds def main(): @@ -383,7 +399,7 @@ def main(): url = "https://" + url try: - youtube_rss, channel_id, channel_name, atom_feed, video_count, invidious_rss, api_endpoints = get_rss_feed( + youtube_rss, channel_id, channel_name, atom_feed, video_count, invidious_rss, api_endpoints, official_feeds = get_rss_feed( url, include_api_endpoints=args.include_api_endpoints, base_url=args.base_url From e06ca729a1516a7c698abe620c3c1ad727d28c24 Mon Sep 17 00:00:00 2001 From: Abel <196466003+DisabledAbel@users.noreply.github.com> Date: Wed, 13 May 2026 21:39:32 -0700 Subject: [PATCH 2/5] Add copy buttons for official YouTube feed URLs --- api/index.html | 9 ++++++++- index.html | 20 ++++++++++++++++++-- templates/index.html | 9 ++++++++- 3 files changed, 34 insertions(+), 4 deletions(-) diff --git a/api/index.html b/api/index.html index f7e5d8d..ba2c36c 100644 --- a/api/index.html +++ b/api/index.html @@ -224,7 +224,14 @@

YouTube RSS Scanner

if (data.official_feeds) { html += '
Official YouTube Feeds:
'; - ['selected','all','videos','shorts','live'].forEach(function(k){ if (data.official_feeds[k]) { html += ''; }}); + ['selected','all','videos','shorts','live'].forEach(function(k){ + if (!data.official_feeds[k]) return; + html += '
'; + html += '
Official (' + k + '):
'; + html += ''; + html += ''; + html += '
'; + }); html += '
'; } diff --git a/index.html b/index.html index 044a89c..99d6dd1 100644 --- a/index.html +++ b/index.html @@ -270,8 +270,24 @@

YouTube RSS Scanner

['selected','all','videos','shorts','live'].forEach((key) => { if (!data.official_feeds[key]) return; const row = document.createElement('div'); - row.className = 'feed-link'; - row.textContent = `${key}: ${data.official_feeds[key]}`; + row.className = 'feed-row'; + + const feedLabel = document.createElement('div'); + feedLabel.className = 'feed-label'; + feedLabel.textContent = `Official (${key}):`; + row.appendChild(feedLabel); + + const feedLink = document.createElement('div'); + feedLink.className = 'feed-link'; + feedLink.textContent = data.official_feeds[key]; + row.appendChild(feedLink); + + const copyBtn = document.createElement('button'); + copyBtn.className = 'copy-btn'; + copyBtn.textContent = `Copy ${key} RSS`; + copyBtn.addEventListener('click', () => copyText(data.official_feeds[key])); + row.appendChild(copyBtn); + officialBox.appendChild(row); }); resultDiv.appendChild(officialBox); diff --git a/templates/index.html b/templates/index.html index a034472..e32ce67 100644 --- a/templates/index.html +++ b/templates/index.html @@ -218,7 +218,14 @@

YouTube RSS Scanner

if (data.official_feeds) { html += '
Official YouTube Feeds:
'; - ['selected','all','videos','shorts','live'].forEach(function(k){ if (data.official_feeds[k]) { html += ''; }}); + ['selected','all','videos','shorts','live'].forEach(function(k){ + if (!data.official_feeds[k]) return; + html += '
'; + html += '
Official (' + k + '):
'; + html += ''; + html += ''; + html += '
'; + }); html += '
'; } From c99db9e513e87d1c4adc3b3e211fa7fed7f64975 Mon Sep 17 00:00:00 2001 From: Abel <196466003+DisabledAbel@users.noreply.github.com> Date: Wed, 13 May 2026 21:48:31 -0700 Subject: [PATCH 3/5] Stop duplicating official feed URLs in the UI --- api/index.html | 9 +++++---- index.html | 7 +++---- rss_scanner.py | 12 ++++-------- templates/index.html | 9 +++++---- 4 files changed, 17 insertions(+), 20 deletions(-) diff --git a/api/index.html b/api/index.html index ba2c36c..b070aed 100644 --- a/api/index.html +++ b/api/index.html @@ -224,12 +224,13 @@

YouTube RSS Scanner

if (data.official_feeds) { html += '
Official YouTube Feeds:
'; - ['selected','all','videos','shorts','live'].forEach(function(k){ - if (!data.official_feeds[k]) return; + Object.entries(data.official_feeds).forEach(function(entry){ + var k = entry[0]; + var v = entry[1]; html += '
'; html += '
Official (' + k + '):
'; - html += ''; - html += ''; + html += ''; + html += ''; html += '
'; }); html += '
'; diff --git a/index.html b/index.html index 99d6dd1..a873bad 100644 --- a/index.html +++ b/index.html @@ -267,8 +267,7 @@

YouTube RSS Scanner

label.className = 'feed-label'; label.textContent = 'Official YouTube Feeds:'; officialBox.appendChild(label); - ['selected','all','videos','shorts','live'].forEach((key) => { - if (!data.official_feeds[key]) return; + Object.entries(data.official_feeds).forEach(([key, value]) => { const row = document.createElement('div'); row.className = 'feed-row'; @@ -279,13 +278,13 @@

YouTube RSS Scanner

const feedLink = document.createElement('div'); feedLink.className = 'feed-link'; - feedLink.textContent = data.official_feeds[key]; + feedLink.textContent = value; row.appendChild(feedLink); const copyBtn = document.createElement('button'); copyBtn.className = 'copy-btn'; copyBtn.textContent = `Copy ${key} RSS`; - copyBtn.addEventListener('click', () => copyText(data.official_feeds[key])); + copyBtn.addEventListener('click', () => copyText(value)); row.appendChild(copyBtn); officialBox.appendChild(row); diff --git a/rss_scanner.py b/rss_scanner.py index 1467641..38233e3 100644 --- a/rss_scanner.py +++ b/rss_scanner.py @@ -298,16 +298,12 @@ def build_youtube_feed_url(channel_id: str, feed_type: str = None) -> str: def build_official_feeds(channel_id: str, feed_type: str = "all") -> dict[str, str]: - """Return official YouTube feed URLs keyed by feed type.""" - all_feed = build_youtube_feed_url(channel_id, feed_type="all") + """Return official YouTube feed URLs without duplicating identical links.""" + youtube_feed = build_youtube_feed_url(channel_id, feed_type="all") feeds = { - "all": all_feed, - "videos": all_feed, - "shorts": all_feed, - "live": all_feed, + "youtube": youtube_feed, + "selected": youtube_feed, } - selected_type = feed_type if feed_type in feeds else "all" - feeds["selected"] = feeds[selected_type] return feeds def get_rss_feed(url: str, include_api_endpoints: bool = False, base_url: str = "http://localhost:8080", feed_type: str = "all") -> tuple: diff --git a/templates/index.html b/templates/index.html index e32ce67..18c7b15 100644 --- a/templates/index.html +++ b/templates/index.html @@ -218,12 +218,13 @@

YouTube RSS Scanner

if (data.official_feeds) { html += '
Official YouTube Feeds:
'; - ['selected','all','videos','shorts','live'].forEach(function(k){ - if (!data.official_feeds[k]) return; + Object.entries(data.official_feeds).forEach(function(entry){ + var k = entry[0]; + var v = entry[1]; html += '
'; html += '
Official (' + k + '):
'; - html += ''; - html += ''; + html += ''; + html += ''; html += '
'; }); html += '
'; From 8723ad702141dd0bdac7a6630e60a74b8d5fd92f Mon Sep 17 00:00:00 2001 From: "coderabbitai[bot]" <136622811+coderabbitai[bot]@users.noreply.github.com> Date: Thu, 14 May 2026 04:56:42 +0000 Subject: [PATCH 4/5] fix: apply CodeRabbit auto-fixes Fixed 2 file(s) based on 2 unresolved review comments. Co-authored-by: CodeRabbit --- api/index.html | 10 ++++++++++ rss_scanner.py | 6 +++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/api/index.html b/api/index.html index b070aed..2d53dc1 100644 --- a/api/index.html +++ b/api/index.html @@ -267,6 +267,16 @@

YouTube RSS Scanner

document.getElementById('channelUrl').addEventListener('keypress', function(e) { if (e.key === 'Enter') getFeed(); }); + + // Event delegation for dynamically added copy buttons + document.addEventListener('click', function(e) { + if (e.target && e.target.classList.contains('copy-btn')) { + const encoded = e.target.getAttribute('data-encoded'); + if (encoded) { + copyEncodedText(encoded); + } + } + }); diff --git a/rss_scanner.py b/rss_scanner.py index 38233e3..7807bed 100644 --- a/rss_scanner.py +++ b/rss_scanner.py @@ -302,7 +302,11 @@ def build_official_feeds(channel_id: str, feed_type: str = "all") -> dict[str, s youtube_feed = build_youtube_feed_url(channel_id, feed_type="all") feeds = { "youtube": youtube_feed, - "selected": youtube_feed, + "all": build_youtube_feed_url(channel_id, feed_type="all"), + "videos": build_youtube_feed_url(channel_id, feed_type="videos"), + "shorts": build_youtube_feed_url(channel_id, feed_type="shorts"), + "live": build_youtube_feed_url(channel_id, feed_type="live"), + "selected": build_youtube_feed_url(channel_id, feed_type=feed_type), } return feeds From ebf51a06bd1e9b78a86964c22987483168d99528 Mon Sep 17 00:00:00 2001 From: "coderabbitai[bot]" <136622811+coderabbitai[bot]@users.noreply.github.com> Date: Thu, 14 May 2026 05:02:12 +0000 Subject: [PATCH 5/5] fix: apply CodeRabbit auto-fixes Fixed 1 file(s) based on 1 unresolved review comment. Co-authored-by: CodeRabbit --- rss_scanner.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rss_scanner.py b/rss_scanner.py index 7807bed..96ad749 100644 --- a/rss_scanner.py +++ b/rss_scanner.py @@ -399,7 +399,7 @@ def main(): url = "https://" + url try: - youtube_rss, channel_id, channel_name, atom_feed, video_count, invidious_rss, api_endpoints, official_feeds = get_rss_feed( + youtube_rss, channel_id, channel_name, atom_feed, video_count, invidious_rss, api_endpoints, _ = get_rss_feed( url, include_api_endpoints=args.include_api_endpoints, base_url=args.base_url