Add comprehensive deletion functionality and scheduled cleanup
Features: - Delete entire channels with all videos and downloaded files - Delete individual video files while keeping database entries - Scheduled automatic cleanup of videos older than 7 days - Proper cascading deletes with file cleanup Channel Deletion: - New DELETE endpoint at /api/channels/<id> - Removes channel, all video entries, and downloaded files - User ownership verification - Returns count of deleted files - UI button on channels page with detailed confirmation dialog Video File Deletion: - New DELETE endpoint at /api/videos/<id>/file - Celery async task to remove file from disk - Resets download status to pending (allows re-download) - UI button on watch page for completed videos - Confirmation dialog with clear warnings Scheduled Cleanup: - Celery beat configuration for periodic tasks - cleanup_old_videos task runs daily at midnight - Automatically deletes videos completed more than 7 days ago - Removes files and resets database status - scheduled_tasks.py for beat schedule configuration - verify_schedule.py helper to check task scheduling UI Improvements: - Added .btn-danger CSS class (black/white theme) - Delete buttons with loading states - Detailed confirmation dialogs warning about permanent deletion - Dashboard now filters to show only completed videos Bug Fixes: - Fixed navbar alignment issues - Added proper error handling for file deletion 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
47
main.py
47
main.py
@@ -6,7 +6,7 @@ from flask_login import LoginManager, login_user, logout_user, login_required, c
|
||||
from feed_parser import YouTubeFeedParser, fetch_single_video, save_single_video_to_db
|
||||
from database import init_db, get_db_session
|
||||
from models import Channel, VideoEntry, DownloadStatus, User
|
||||
from download_service import download_video, download_videos_batch
|
||||
from download_service import download_video, download_videos_batch, delete_video_file
|
||||
from sqlalchemy import desc
|
||||
|
||||
|
||||
@@ -157,7 +157,8 @@ def index():
|
||||
with get_db_session() as session:
|
||||
# Query videos for current user's channels, sorted by published date (newest first)
|
||||
videos = session.query(VideoEntry).join(Channel).filter(
|
||||
Channel.user_id == current_user.id
|
||||
Channel.user_id == current_user.id,
|
||||
VideoEntry.download_status == DownloadStatus.COMPLETED
|
||||
).order_by(desc(VideoEntry.published_at)).all()
|
||||
|
||||
return render_template("dashboard.html", videos=videos)
|
||||
@@ -695,7 +696,49 @@ def stream_video(video_id: int):
|
||||
return jsonify({"error": f"Failed to stream video: {str(e)}"}), 500
|
||||
|
||||
|
||||
@app.route("/api/videos/<int:video_id>/file", methods=["DELETE"])
|
||||
@login_required
|
||||
def delete_video(video_id: int):
|
||||
"""Delete a downloaded video file from disk.
|
||||
|
||||
Args:
|
||||
video_id: Database ID of the VideoEntry
|
||||
|
||||
Returns:
|
||||
JSON response indicating success or failure
|
||||
"""
|
||||
try:
|
||||
with get_db_session() as session:
|
||||
# Only allow deleting videos from user's own channels
|
||||
video = session.query(VideoEntry).join(Channel).filter(
|
||||
VideoEntry.id == video_id,
|
||||
Channel.user_id == current_user.id
|
||||
).first()
|
||||
|
||||
if not video:
|
||||
return jsonify({"status": "error", "message": "Video not found"}), 404
|
||||
|
||||
if video.download_status != DownloadStatus.COMPLETED:
|
||||
return jsonify({
|
||||
"status": "error",
|
||||
"message": "Video is not downloaded"
|
||||
}), 400
|
||||
|
||||
# Queue deletion task
|
||||
task = delete_video_file.delay(video_id)
|
||||
|
||||
return jsonify({
|
||||
"status": "success",
|
||||
"video_id": video_id,
|
||||
"task_id": task.id,
|
||||
"message": "Video deletion queued"
|
||||
})
|
||||
except Exception as e:
|
||||
return jsonify({"status": "error", "message": f"Failed to delete video: {str(e)}"}), 500
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
"""CLI entry point for testing feed parser."""
|
||||
parser = YouTubeFeedParser(DEFAULT_CHANNEL_ID)
|
||||
result = parser.fetch_feed()
|
||||
|
||||
Reference in New Issue
Block a user