Sprint 002: Ingest duration & view count, FTS search, infinite scroll pagination, and transcript accordion
Deploy t-youtube / Build & Deploy (push) Failing after 1s

This commit is contained in:
Gan, Jimmy
2026-07-11 13:16:59 +08:00
parent 623dff149c
commit 022b024603
6 changed files with 207 additions and 91 deletions
+27 -4
View File
@@ -6,6 +6,7 @@ import logging
import aiosqlite
import httpx
import xml.etree.ElementTree as ET
import re
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError
from auth import get_credentials
@@ -65,6 +66,20 @@ async def _fetch_rss(client, channel_id):
return []
def parse_duration(duration_str: str) -> int:
"""Parse ISO 8601 duration string (e.g., PT1H2M10S) to seconds."""
if not duration_str:
return 0
pattern = re.compile(r'PT(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?')
match = pattern.match(duration_str)
if not match:
return 0
hours = int(match.group(1)) if match.group(1) else 0
minutes = int(match.group(2)) if match.group(2) else 0
seconds = int(match.group(3)) if match.group(3) else 0
return hours * 3600 + minutes * 60 + seconds
async def fetch_subscriptions():
"""Fetch latest videos from subscribed channels using RSS + YouTube Data API v3."""
try:
@@ -144,16 +159,22 @@ async def fetch_subscriptions():
for i in range(0, len(new_ids), 50):
batch = new_ids[i:i+50]
try:
resp = youtube.videos().list(part="snippet", id=",".join(batch)).execute()
resp = youtube.videos().list(part="snippet,contentDetails,statistics", id=",".join(batch)).execute()
for item in resp.get("items", []):
vid = item["id"]
snippet = item["snippet"]
cid, rss_title = rss_map.get(vid, (snippet.get("channelId", ""), snippet.get("title", "")))
content_details = item.get("contentDetails", {})
statistics = item.get("statistics", {})
duration_str = content_details.get("duration", "")
duration_secs = parse_duration(duration_str)
view_count = int(statistics.get("viewCount", 0))
await db.execute("""
INSERT INTO videos
(video_id, title, channel_name, channel_id, url, thumbnail_url, publish_date)
VALUES (?, ?, ?, ?, ?, ?, ?)
(video_id, title, channel_name, channel_id, url, thumbnail_url, publish_date, duration, view_count)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
""", (
vid,
html.unescape(snippet.get("title", rss_title)),
@@ -161,7 +182,9 @@ async def fetch_subscriptions():
snippet.get("channelId", cid),
f"https://www.youtube.com/watch?v={vid}",
snippet.get("thumbnails", {}).get("high", {}).get("url", ""),
(snippet.get("publishTime") or "")[:10]
(snippet.get("publishTime") or "")[:10],
duration_secs,
view_count
))
new_count += 1
except HttpError as e: