diff --git a/backend/app/routes/channels.py b/backend/app/routes/channels.py index fc4e0d3..04ad3f7 100644 --- a/backend/app/routes/channels.py +++ b/backend/app/routes/channels.py @@ -9,6 +9,7 @@ from app import quota from app.auth import current_user, has_write_scope from app.db import get_db from app.models import Channel, ChannelTag, Subscription, Tag, User, Video +from app.routes.admin import admin_user from app.sync.runner import run_recent_backfill from app.youtube.client import YouTubeClient, YouTubeError @@ -134,6 +135,30 @@ def update_channel( } +@router.post("/{channel_id}/reset-backfill") +def reset_backfill( + channel_id: str, + user: User = Depends(admin_user), + db: Session = Depends(get_db), +) -> dict: + """Admin-only reset: re-fetch this channel from scratch regardless of its current sync + state (a "reset" trigger). Clears the channel's backfill markers, opts it back into deep + backfill, and re-runs its recent pull immediately; the deep scheduler re-pages the full + back-catalog on its next run. Idempotent — videos upsert by id, so nothing duplicates.""" + channel = db.get(Channel, channel_id) + if channel is None: + raise HTTPException(status_code=404, detail="Unknown channel") + sub = _user_subscription(db, user, channel_id) + channel.backfill_done = False + channel.backfill_cursor = None + channel.recent_synced_at = None + sub.deep_requested = True + db.commit() + with quota.attribute(user.id, "backfill_recent"): + run_recent_backfill(db, [channel], max_channels=1) + return {"id": channel_id, "reset": True} + + @router.delete("/{channel_id}/subscription") def unsubscribe( channel_id: str, diff --git a/frontend/src/lib/api.ts b/frontend/src/lib/api.ts index 0615f19..e13f097 100644 --- a/frontend/src/lib/api.ts +++ b/frontend/src/lib/api.ts @@ -381,6 +381,8 @@ export const api = { id: string, patch: { priority?: number; hidden?: boolean; deep_requested?: boolean } ) => req(`/api/channels/${id}`, { method: "PATCH", body: JSON.stringify(patch) }), + resetChannelBackfill: (id: string) => + req(`/api/channels/${id}/reset-backfill`, { method: "POST" }), deepAll: (on = true) => req(`/api/sync/deep-all?on=${on}`, { method: "POST" }), attachChannelTag: (id: string, tagId: number) =>