233 lines
8.1 KiB
Python

"""Archive API endpoints with SQLite backend."""
import os
from flask import Blueprint, request, Response, send_file
from utils import make_response, make_error_response
from models import ArchiveItem
archive_bp = Blueprint('archive', __name__, url_prefix='/api')
@archive_bp.route('/archive', methods=['GET'])
def get_archive():
"""Get archived videos with pagination and filtering."""
from app import archive_db
try:
page = int(request.args.get('page', 1))
limit = int(request.args.get('limit', 24))
search = request.args.get('search')
category = request.args.get('category')
start_date = request.args.get('startDate')
end_date = request.args.get('endDate')
videos, total = archive_db.get_videos(
page=page, limit=limit, search=search,
category=category, start_date=start_date, end_date=end_date
)
items = []
for v in videos:
items.append({
"videoId": v.video_id,
"title": v.title,
"url": v.url,
"description": v.description,
"thumbnail": v.thumbnail,
"channel": v.channel,
"views": v.views,
"duration": v.duration,
"category": v.category,
"downloadPath": v.download_path,
"networkSharePath": v.network_share_path,
"fileSize": v.file_size,
"downloadDate": v.download_date.isoformat() if v.download_date else "",
"type": v.item_type,
})
return make_response({
"archive": items,
"items": items,
"total": total,
"page": page,
"hasMore": page * limit < total,
})
except Exception as e:
return make_error_response(f"Failed to get archive: {str(e)}", 500)
@archive_bp.route('/archive/<video_id>', methods=['GET'])
def get_archive_item(video_id):
"""Get a single archive item."""
from app import archive_db
try:
video = archive_db.get_video(video_id)
if not video:
return make_error_response(f"Video {video_id} not found in archive", 404)
return make_response({
"videoId": video.video_id,
"title": video.title,
"url": video.url,
"description": video.description,
"thumbnail": video.thumbnail,
"channel": video.channel,
"views": video.views,
"duration": video.duration,
"category": video.category,
"downloadPath": video.download_path,
"networkSharePath": video.network_share_path,
"fileSize": video.file_size,
"downloadDate": video.download_date.isoformat() if video.download_date else "",
"type": video.item_type,
})
except Exception as e:
return make_error_response(f"Failed to get archive item: {str(e)}", 500)
@archive_bp.route('/archive/<video_id>/stream', methods=['GET'])
def stream_video(video_id):
"""Stream a downloaded video file."""
from app import archive_db
try:
video = archive_db.get_video(video_id)
if not video or not video.download_path:
return make_error_response(f"Video {video_id} not found or has no file", 404)
if not os.path.exists(video.download_path):
return make_error_response(f"Video file not found: {video.download_path}", 404)
return send_file(
video.download_path,
mimetype='video/mp4',
as_attachment=False,
conditional=True,
)
except Exception as e:
return make_error_response(f"Failed to stream video: {str(e)}", 500)
@archive_bp.route('/archive/<video_id>', methods=['DELETE'])
def remove_from_archive(video_id):
"""Remove a video from the archive."""
from app import archive_db
try:
removed = archive_db.delete_video(video_id)
if not removed:
return make_error_response(f"Video {video_id} not found in archive", 404)
return make_response({"message": f"Video {video_id} removed from archive"})
except Exception as e:
return make_error_response(f"Failed to remove from archive: {str(e)}", 500)
@archive_bp.route('/archive', methods=['DELETE'])
def clear_archive():
"""Clear the entire archive."""
from app import archive_db
try:
count = archive_db.clear_archive()
return make_response({
"message": "Archive cleared",
"count": count
})
except Exception as e:
return make_error_response(f"Failed to clear archive: {str(e)}", 500)
@archive_bp.route('/archive/stats', methods=['GET'])
def get_archive_stats():
"""Get archive statistics."""
from app import archive_db
try:
stats = archive_db.get_stats()
return make_response(stats)
except Exception as e:
return make_error_response(f"Failed to get archive stats: {str(e)}", 500)
@archive_bp.route('/archive/categories', methods=['GET'])
def get_archive_categories():
"""Get unique categories from the archive."""
from app import archive_db
try:
categories = archive_db.get_categories()
# Flatten SQLAlchemy row tuples to plain strings
flat = [c[0] if isinstance(c, (tuple, list)) else c for c in categories]
return make_response(flat)
except Exception as e:
return make_error_response(f"Failed to get categories: {str(e)}", 500)
@archive_bp.route('/archive/export', methods=['GET'])
def export_archive():
"""Export archive data as JSON or CSV."""
from app import archive_db
try:
fmt = request.args.get('format', 'json')
if fmt not in ('json', 'csv'):
return make_error_response("Format must be 'json' or 'csv'", 400)
data = archive_db.export_archive(fmt)
if fmt == 'json':
mimetype = 'application/json'
filename = 'archive.json'
else:
mimetype = 'text/csv'
filename = 'archive.csv'
return Response(
data,
mimetype=mimetype,
headers={"Content-Disposition": f"attachment; filename={filename}"}
)
except Exception as e:
return make_error_response(f"Failed to export archive: {str(e)}", 500)
@archive_bp.route('/archive/import', methods=['POST'])
def import_archive():
"""Import archive data from a JSON file."""
from app import archive_db
try:
if 'file' not in request.files:
return make_error_response("No file provided", 400)
file = request.files['file']
if not file.filename.endswith('.json'):
return make_error_response("Only JSON files are supported", 400)
import json
data = json.loads(file.read())
items = data.get('items', data if isinstance(data, list) else [])
imported = 0
for item in items:
video_id = item.get('videoId', item.get('video_id', ''))
if not video_id:
continue
archive_item = ArchiveItem(
video_id=video_id,
title=item.get('title', 'Unknown'),
url=item.get('url', ''),
description=item.get('description', ''),
thumbnail=item.get('thumbnail', ''),
channel=item.get('channel', ''),
views=item.get('views', 0) or 0,
duration=item.get('duration', ''),
category=item.get('category', ''),
download_path=item.get('downloadPath', item.get('download_path', '')),
network_share_path=item.get('networkSharePath', item.get('network_share_path')),
file_size=item.get('fileSize', item.get('file_size', 0)) or 0,
download_date=item.get('downloadDate', item.get('download_date', '')),
item_type=item.get('type', 'video'),
)
archive_db.add_video(archive_item)
imported += 1
return make_response({
"success": True,
"imported": imported
})
except Exception as e:
return make_error_response(f"Failed to import archive: {str(e)}", 500)