Add --no-shorts flag to filter Shorts from latest results

This commit is contained in:
2026-04-07 22:29:29 +02:00
parent 12de3f13df
commit 2533b63597
2 changed files with 19 additions and 5 deletions

View File

@@ -16,6 +16,7 @@ That feed is ordered by newest uploads, so fetching the first 3 entries gives th
python3 latest3.py "https://www.youtube.com/@ludwig/videos"
python3 latest3.py "@ludwig" --json
python3 latest3.py "https://www.youtube.com/@ludwig/videos" --limit 3 --json
python3 latest3.py "https://www.youtube.com/@ludwig/videos" --limit 3 --no-shorts
```
## Output fields
@@ -25,6 +26,12 @@ python3 latest3.py "https://www.youtube.com/@ludwig/videos" --limit 3 --json
- `url` - watch URL
- `published` - ISO-8601 timestamp from the feed
## Flags
- `--limit N` how many videos to return (1-20)
- `--json` output as JSON
- `--no-shorts` exclude URLs matching `/shorts/`
## Notes
- Works with `@handle` URLs and plain `@handle` input.

View File

@@ -53,7 +53,7 @@ def extract_channel_id(html: str) -> str:
raise RuntimeError("Could not resolve channel ID from URL")
def fetch_latest_from_feed(channel_id: str, limit: int = 3):
def fetch_latest_from_feed(channel_id: str, limit: int = 3, no_shorts: bool = False):
feed_url = f"https://www.youtube.com/feeds/videos.xml?channel_id={channel_id}"
xml_text = http_get(feed_url)
root = ET.fromstring(xml_text)
@@ -65,13 +65,16 @@ def fetch_latest_from_feed(channel_id: str, limit: int = 3):
}
out = []
for entry in root.findall("atom:entry", ns)[:limit]:
for entry in root.findall("atom:entry", ns):
vid = entry.findtext("yt:videoId", default="", namespaces=ns)
title = entry.findtext("atom:title", default="", namespaces=ns)
published = entry.findtext("atom:published", default="", namespaces=ns)
link_el = entry.find("atom:link", ns)
url = link_el.attrib.get("href") if link_el is not None else (f"https://www.youtube.com/watch?v={vid}" if vid else "")
if no_shorts and "/shorts/" in (url or ""):
continue
out.append(
{
"id": vid,
@@ -81,16 +84,19 @@ def fetch_latest_from_feed(channel_id: str, limit: int = 3):
}
)
if len(out) >= limit:
break
if not out:
raise RuntimeError("No videos found in channel feed")
return out
def get_latest_videos(channel_url_or_handle: str, limit: int = 3):
def get_latest_videos(channel_url_or_handle: str, limit: int = 3, no_shorts: bool = False):
normalized = normalize_channel_url(channel_url_or_handle)
html = http_get(normalized)
channel_id = extract_channel_id(html)
videos = fetch_latest_from_feed(channel_id, limit=limit)
videos = fetch_latest_from_feed(channel_id, limit=limit, no_shorts=no_shorts)
return {
"input": channel_url_or_handle,
"resolved_url": normalized,
@@ -104,6 +110,7 @@ def main():
ap.add_argument("channel", help="YouTube channel URL (including @handle) or @handle")
ap.add_argument("--limit", type=int, default=3, help="How many latest videos to return (default: 3)")
ap.add_argument("--json", action="store_true", help="Print full JSON output")
ap.add_argument("--no-shorts", action="store_true", help="Exclude Shorts URLs")
args = ap.parse_args()
if args.limit < 1 or args.limit > 20:
@@ -111,7 +118,7 @@ def main():
sys.exit(2)
try:
data = get_latest_videos(args.channel, limit=args.limit)
data = get_latest_videos(args.channel, limit=args.limit, no_shorts=args.no_shorts)
except (urllib.error.URLError, urllib.error.HTTPError) as e:
print(f"Network error: {e}", file=sys.stderr)
sys.exit(1)