Add offline indicator, surprise me button, and continue watching on channel
- Offline banner in nav when backend is unreachable (network error, not 4xx)
- GET /channels/{id}/random — picks random unwatched video, navigates to watch
- GET /channels/{id}/in-progress — videos with >30s progress, not yet watched
- Channel page: 'Surprise me' button (desktop + mobile) navigates to random video
- Channel page: 'Continue watching' row above video list when in-progress videos exist
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -865,6 +865,66 @@ def _search_channel_task(channel_id: int, youtube_channel_id: str, q: str, user_
|
||||
db.close()
|
||||
|
||||
|
||||
@router.get("/{channel_id}/random", response_model=VideoOut)
|
||||
def get_random_channel_video(
|
||||
channel_id: int,
|
||||
unwatched_only: bool = True,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user),
|
||||
):
|
||||
_get_channel_or_404(db, channel_id)
|
||||
unwatched_clause = "AND COALESCE(uv.watched, 0) = 0" if unwatched_only else ""
|
||||
row = db.execute(
|
||||
text(f"""
|
||||
SELECT v.id, v.youtube_video_id, v.title, v.thumbnail_url,
|
||||
v.duration_seconds, v.published_at, v.view_count,
|
||||
c.id AS channel_id, c.name AS channel_name, c.youtube_channel_id AS channel_youtube_id,
|
||||
COALESCE(uv.downloaded, 0) AS is_downloaded,
|
||||
COALESCE(uv.watched, 0) AS is_watched,
|
||||
COALESCE(uv.queued, 0) AS queued
|
||||
FROM videos v
|
||||
JOIN channels c ON v.channel_id = c.id
|
||||
LEFT JOIN user_videos uv ON v.id = uv.video_id AND uv.user_id = :user_id
|
||||
WHERE v.channel_id = :channel_id {unwatched_clause}
|
||||
ORDER BY RANDOM()
|
||||
LIMIT 1
|
||||
"""),
|
||||
{"user_id": current_user.id, "channel_id": channel_id},
|
||||
).mappings().first()
|
||||
if not row:
|
||||
raise HTTPException(status_code=404, detail="No videos found")
|
||||
return VideoOut(**dict(row))
|
||||
|
||||
|
||||
@router.get("/{channel_id}/in-progress", response_model=list[VideoOut])
|
||||
def get_channel_in_progress(
|
||||
channel_id: int,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user),
|
||||
):
|
||||
_get_channel_or_404(db, channel_id)
|
||||
rows = db.execute(
|
||||
text("""
|
||||
SELECT v.id, v.youtube_video_id, v.title, v.thumbnail_url,
|
||||
v.duration_seconds, v.published_at, v.view_count,
|
||||
c.id AS channel_id, c.name AS channel_name, c.youtube_channel_id AS channel_youtube_id,
|
||||
COALESCE(uv.downloaded, 0) AS is_downloaded,
|
||||
COALESCE(uv.watched, 0) AS is_watched,
|
||||
COALESCE(uv.queued, 0) AS queued
|
||||
FROM videos v
|
||||
JOIN channels c ON v.channel_id = c.id
|
||||
JOIN user_videos uv ON v.id = uv.video_id AND uv.user_id = :user_id
|
||||
WHERE v.channel_id = :channel_id
|
||||
AND uv.watch_progress_seconds > 30
|
||||
AND COALESCE(uv.watched, 0) = 0
|
||||
ORDER BY uv.watch_progress_seconds DESC
|
||||
LIMIT 6
|
||||
"""),
|
||||
{"user_id": current_user.id, "channel_id": channel_id},
|
||||
).mappings().all()
|
||||
return [VideoOut(**dict(r)) for r in rows]
|
||||
|
||||
|
||||
@router.post("/{channel_id}/follow", status_code=status.HTTP_204_NO_CONTENT)
|
||||
def follow_channel(
|
||||
channel_id: int,
|
||||
|
||||
Reference in New Issue
Block a user