Add feed deletion and single video addition features
- Implemented DELETE /api/channels/<id> to remove channels and cleanup downloaded files - Added delete button to channels page with confirmation dialog - Added functionality to add single videos via URL - Updated navigation menu
This commit is contained in:
90
main.py
90
main.py
@@ -3,7 +3,7 @@
|
||||
import os
|
||||
from flask import Flask, render_template, request, jsonify, redirect, url_for, flash, send_file
|
||||
from flask_login import LoginManager, login_user, logout_user, login_required, current_user
|
||||
from feed_parser import YouTubeFeedParser
|
||||
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
|
||||
@@ -249,6 +249,51 @@ def add_channel_page():
|
||||
return redirect(url_for("add_channel_page"))
|
||||
|
||||
|
||||
@app.route("/add-video", methods=["GET", "POST"])
|
||||
@login_required
|
||||
def add_video_page():
|
||||
"""Render the add video page and handle video submission."""
|
||||
if request.method == "GET":
|
||||
return render_template("add_video.html")
|
||||
|
||||
# Handle POST - add new video
|
||||
video_url = request.form.get("video_url")
|
||||
if not video_url:
|
||||
flash("Video URL is required", "error")
|
||||
return redirect(url_for("add_video_page"))
|
||||
|
||||
# Validate it's a YouTube URL
|
||||
video_url = video_url.strip()
|
||||
if not ("youtube.com/watch" in video_url or "youtu.be/" in video_url):
|
||||
flash("Invalid YouTube URL. Please provide a valid YouTube video link.", "error")
|
||||
return redirect(url_for("add_video_page"))
|
||||
|
||||
# Check if it's a Shorts video
|
||||
if "shorts" in video_url.lower():
|
||||
flash("YouTube Shorts are not supported.", "error")
|
||||
return redirect(url_for("add_video_page"))
|
||||
|
||||
try:
|
||||
# Fetch video metadata
|
||||
video_data = fetch_single_video(video_url)
|
||||
|
||||
if video_data is None:
|
||||
flash("Failed to fetch video from YouTube. Please check the URL.", "error")
|
||||
return redirect(url_for("add_video_page"))
|
||||
|
||||
# Save to database with current user
|
||||
with get_db_session() as session:
|
||||
video = save_single_video_to_db(session, video_data, current_user.id)
|
||||
video_title = video.title
|
||||
|
||||
flash(f"Successfully added video: {video_title}!", "success")
|
||||
return redirect(url_for("index"))
|
||||
|
||||
except Exception as e:
|
||||
flash(f"Error adding video: {str(e)}", "error")
|
||||
return redirect(url_for("add_video_page"))
|
||||
|
||||
|
||||
@app.route("/watch/<int:video_id>", methods=["GET"])
|
||||
@login_required
|
||||
def watch_video(video_id: int):
|
||||
@@ -562,6 +607,49 @@ def refresh_channel_videos(channel_id: int):
|
||||
return jsonify({"status": "error", "message": f"Failed to refresh channel: {str(e)}"}), 500
|
||||
|
||||
|
||||
@app.route("/api/channels/<int:channel_id>", methods=["DELETE"])
|
||||
@login_required
|
||||
def delete_channel(channel_id: int):
|
||||
"""Delete a channel and all its associated data (videos and files).
|
||||
|
||||
Args:
|
||||
channel_id: Database ID of the Channel
|
||||
|
||||
Returns:
|
||||
JSON response indicating success or failure
|
||||
"""
|
||||
try:
|
||||
with get_db_session() as session:
|
||||
# Verify ownership and get channel with videos
|
||||
channel = session.query(Channel).filter_by(
|
||||
id=channel_id,
|
||||
user_id=current_user.id
|
||||
).first()
|
||||
|
||||
if not channel:
|
||||
return jsonify({"status": "error", "message": "Channel not found"}), 404
|
||||
|
||||
# Delete downloaded files
|
||||
deleted_files_count = 0
|
||||
for video in channel.video_entries:
|
||||
if video.download_path and os.path.exists(video.download_path):
|
||||
try:
|
||||
os.remove(video.download_path)
|
||||
deleted_files_count += 1
|
||||
except OSError as e:
|
||||
print(f"Error deleting file {video.download_path}: {e}")
|
||||
|
||||
# Delete channel (cascade will handle video entries in DB)
|
||||
session.delete(channel)
|
||||
|
||||
return jsonify({
|
||||
"status": "success",
|
||||
"message": f"Channel deleted successfully. Removed {deleted_files_count} downloaded files."
|
||||
})
|
||||
except Exception as e:
|
||||
return jsonify({"status": "error", "message": f"Failed to delete channel: {str(e)}"}), 500
|
||||
|
||||
|
||||
@app.route("/api/video/stream/<int:video_id>", methods=["GET"])
|
||||
@login_required
|
||||
def stream_video(video_id: int):
|
||||
|
||||
Reference in New Issue
Block a user