feat(channels): admin reset / re-backfill endpoint

POST /api/channels/{id}/reset-backfill (admin-only): clear the channel's backfill
markers, re-opt into deep backfill and re-run its recent pull now — a re-fetch-from-
scratch trigger regardless of current sync state. Idempotent (videos upsert by id).
This commit is contained in:
npeter83 2026-06-17 19:16:23 +02:00
parent 9872a0115c
commit 0210b84166
2 changed files with 27 additions and 0 deletions

View file

@ -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,

View file

@ -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) =>