From 3726cb5775cddd3f46e4bfab8ac40f5acc2e67be Mon Sep 17 00:00:00 2001 From: azcomputerguru Date: Fri, 8 May 2026 19:19:41 -0400 Subject: [PATCH] Add channel URL lookup and auto-detection - Accept YouTube URLs in any format (@handle, /c/, /user/, /channel/) - Auto-extract channel ID from URLs using yt-dlp - Auto-detect channel name from URL (optional field) - Support direct channel ID input (backwards compatible) - Prevent duplicate channels - Updated UI with better instructions and examples - Improved user experience - just paste channel URL --- app.py | 75 ++++++++++++++++++++++++++++++++++++++--- templates/channels.html | 43 +++++++++++++++-------- 2 files changed, 100 insertions(+), 18 deletions(-) diff --git a/app.py b/app.py index efa84a9..cf70f22 100644 --- a/app.py +++ b/app.py @@ -7,8 +7,10 @@ Provides a web UI for managing YouTube channel downloads import os import subprocess import json +import re from datetime import datetime from pathlib import Path +from urllib.parse import urlparse, parse_qs from flask import Flask, render_template, request, jsonify, redirect, url_for, flash app = Flask(__name__) @@ -45,6 +47,51 @@ def save_settings(settings): with open(SETTINGS_FILE, 'w') as f: json.dump(settings, f, indent=2) +def extract_channel_id(url_or_id): + """ + Extract channel ID from various YouTube URL formats or validate direct ID + Returns tuple: (channel_id, channel_name_hint or None) + """ + # If it looks like a channel ID already (24 characters starting with UC) + if re.match(r'^UC[\w-]{22}$', url_or_id.strip()): + return url_or_id.strip(), None + + # Parse as URL + try: + parsed = urlparse(url_or_id) + + # Format: youtube.com/channel/CHANNEL_ID + if '/channel/' in parsed.path: + channel_id = parsed.path.split('/channel/')[-1].split('/')[0] + if channel_id: + return channel_id, None + + # Format: youtube.com/@handle or youtube.com/c/name or youtube.com/user/name + # These require fetching the page to get the actual channel ID + if parsed.netloc in ['youtube.com', 'www.youtube.com', 'm.youtube.com']: + # Use yt-dlp to extract channel ID from URL + try: + result = subprocess.run( + ['yt-dlp', '--dump-json', '--playlist-items', '0', url_or_id], + capture_output=True, + text=True, + timeout=10 + ) + + if result.returncode == 0 and result.stdout: + data = json.loads(result.stdout.split('\n')[0]) + channel_id = data.get('channel_id') + channel_name = data.get('channel') + + if channel_id: + return channel_id, channel_name + except: + pass + except: + pass + + return None, None + def get_channels(): """Read channels from channels.txt""" channels = [] @@ -166,18 +213,38 @@ def channels(): @app.route('/channels/add', methods=['POST']) def add_channel(): """Add a new channel""" - channel_id = request.form.get('channel_id', '').strip() + channel_input = request.form.get('channel_id', '').strip() channel_name = request.form.get('channel_name', '').strip() - if not channel_id or not channel_name: - flash('Channel ID and Name are required', 'error') + if not channel_input: + flash('Channel ID or URL is required', 'error') return redirect(url_for('channels')) + # Extract channel ID from URL or validate direct ID + channel_id, auto_name = extract_channel_id(channel_input) + + if not channel_id: + flash('Invalid channel ID or URL. Please check and try again.', 'error') + return redirect(url_for('channels')) + + # Use auto-detected name if no name was provided + if not channel_name and auto_name: + channel_name = auto_name + elif not channel_name: + flash('Channel name is required (could not auto-detect from URL)', 'error') + return redirect(url_for('channels')) + + # Check for duplicates channels = get_channels() + for existing in channels: + if existing['id'] == channel_id: + flash(f'Channel already exists: {existing["name"]}', 'error') + return redirect(url_for('channels')) + channels.append({'id': channel_id, 'name': channel_name}) save_channels(channels) - flash(f'Added channel: {channel_name}', 'success') + flash(f'Added channel: {channel_name} ({channel_id})', 'success') return redirect(url_for('channels')) @app.route('/channels/delete/') diff --git a/templates/channels.html b/templates/channels.html index 756cfc8..7ebb82b 100644 --- a/templates/channels.html +++ b/templates/channels.html @@ -11,17 +11,20 @@

Add New Channel

- + - Find this in the channel URL or page source + placeholder="https://www.youtube.com/@AltonBrown or UCfDNi1aEljAQ17mUrfUjkvg" class="form-control"> + + Paste any YouTube channel URL or enter the channel ID directly
+ Supported formats: youtube.com/@handle, youtube.com/channel/ID, youtube.com/c/name, youtube.com/user/name +
- - - Display name for the channel folder + + + Display name for the channel folder. Leave blank to auto-detect from URL.
@@ -65,14 +68,26 @@
-

How to Find Channel IDs

+

How to Add Channels

+ +

Option 1: Paste the Channel URL (Easiest)

    -
  1. Go to the YouTube channel page
  2. -
  3. Right-click and select "View Page Source" (or press Ctrl+U / Cmd+U)
  4. -
  5. Search for "channelId" (Ctrl+F / Cmd+F)
  6. -
  7. Copy the value that looks like: UCfDNi1aEljAQ17mUrfUjkvg
  8. +
  9. Go to the YouTube channel you want to sync
  10. +
  11. Copy the URL from your browser address bar
  12. +
  13. Paste it into the "Channel URL or ID" field above
  14. +
  15. Click "Add Channel" - the name will be detected automatically!
-

Alternative: Some channel URLs include the ID directly:
- youtube.com/channel/CHANNEL_ID_HERE/videos

+ +

Option 2: Enter Channel ID Directly

+

If you have the channel ID (starts with "UC" and is 24 characters long), just paste it directly.

+

Example: UCfDNi1aEljAQ17mUrfUjkvg

+ +

Supported URL Formats

+
    +
  • https://www.youtube.com/@AltonBrown - Modern @ handle format
  • +
  • https://www.youtube.com/channel/UCfDNi1aEljAQ17mUrfUjkvg - Channel ID format
  • +
  • https://www.youtube.com/c/AltonBrown - Custom URL format
  • +
  • https://www.youtube.com/user/altonbrown - Legacy user format
  • +
{% endblock %}