Channel features: subscribers, new-videos badges, activity-driven sync
- Store subscriber count from YouTube statistics and show it on channel page - Sync 50 videos per channel with playlistItems pagination support - Show per-channel and per-category new-videos counters (2-day window) - Replace hourly videos sync with activity trigger (2h idle) and incremental backfill (hard cap 200 per channel) - Clicking the sidebar new-videos count filters the category feed to recent videos only (new_only) - Update agent-team docs: deploy after green checks
This commit is contained in:
parent
fde9a439df
commit
e10df8dcbd
27 changed files with 1420 additions and 107 deletions
|
|
@ -1,3 +1,5 @@
|
|||
from datetime import datetime, timedelta, timezone
|
||||
|
||||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
|
|
@ -5,6 +7,7 @@ from app.core.auth_dependency import require_session
|
|||
from app.db import get_db
|
||||
from app.main import app
|
||||
from app.models.channel import Channel
|
||||
from app.models.video import Video
|
||||
from app.services import sync
|
||||
from app.services.youtube_client import YouTubeInsufficientScope
|
||||
|
||||
|
|
@ -86,3 +89,54 @@ def test_unsubscribe_insufficient_scope_returns_403(client, db_session, monkeypa
|
|||
def test_unsubscribe_channel_not_found(client):
|
||||
resp = client.post("/api/channels/9999/unsubscribe")
|
||||
assert resp.status_code == 404
|
||||
|
||||
|
||||
def _seed_video(db_session, channel, video_id, published_at):
|
||||
video = Video(
|
||||
youtube_video_id=video_id,
|
||||
channel_id=channel.id,
|
||||
title=f"Video {video_id}",
|
||||
published_at=published_at,
|
||||
youtube_url=f"https://www.youtube.com/watch?v={video_id}",
|
||||
)
|
||||
db_session.add(video)
|
||||
db_session.commit()
|
||||
return video
|
||||
|
||||
|
||||
def test_channel_responses_include_subscriber_and_new_videos_counts(client, db_session):
|
||||
channel = _seed_channel(db_session)
|
||||
channel.subscriber_count = 1200000
|
||||
db_session.commit()
|
||||
|
||||
now = datetime.now(timezone.utc)
|
||||
_seed_video(db_session, channel, "vidRecent", now - timedelta(days=1))
|
||||
_seed_video(db_session, channel, "vidOld", now - timedelta(days=30))
|
||||
|
||||
resp = client.get("/api/channels")
|
||||
assert resp.status_code == 200
|
||||
payload = resp.json()
|
||||
assert len(payload) == 1
|
||||
assert payload[0]["subscriber_count"] == 1200000
|
||||
# Only the video from 1 day ago is inside the 7-day window.
|
||||
assert payload[0]["new_videos_count"] == 1
|
||||
|
||||
resp_single = client.get(f"/api/channels/{channel.id}")
|
||||
assert resp_single.status_code == 200
|
||||
single = resp_single.json()
|
||||
assert single["subscriber_count"] == 1200000
|
||||
assert single["new_videos_count"] == 1
|
||||
|
||||
|
||||
def test_channel_new_videos_count_zero_without_recent_videos(client, db_session):
|
||||
channel = _seed_channel(db_session)
|
||||
|
||||
now = datetime.now(timezone.utc)
|
||||
_seed_video(db_session, channel, "vidOld", now - timedelta(days=30))
|
||||
|
||||
resp = client.get("/api/channels")
|
||||
assert resp.status_code == 200
|
||||
payload = resp.json()
|
||||
assert len(payload) == 1
|
||||
assert payload[0]["subscriber_count"] is None
|
||||
assert payload[0]["new_videos_count"] == 0
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue