mirror of
https://github.com/Dvorinka/SpotifyRecAlg.git
synced 2026-06-04 04:23:02 +00:00
first commit
This commit is contained in:
@@ -0,0 +1,326 @@
|
||||
"""
|
||||
Update Tracking API Endpoints
|
||||
|
||||
Provides stable endpoints for following artists, update preferences,
|
||||
recent release updates, and dashboard statistics.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import csv
|
||||
import io
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
from flask import Blueprint, Response, jsonify, request
|
||||
|
||||
from swingmusic.services.update_tracker import (
|
||||
VALID_CHECK_FREQUENCIES,
|
||||
VALID_FOLLOW_LEVELS,
|
||||
VALID_QUALITY_VALUES,
|
||||
VALID_RELEASE_TYPES,
|
||||
update_tracker,
|
||||
)
|
||||
from swingmusic.utils.auth import get_current_userid
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
update_tracking_bp = Blueprint("update_tracking", __name__, url_prefix="/api/updates")
|
||||
|
||||
|
||||
def _error(message: str, status: int = 400):
|
||||
return jsonify({"error": message}), status
|
||||
|
||||
|
||||
def _user_id() -> int:
|
||||
return int(get_current_userid())
|
||||
|
||||
|
||||
def _safe_limit(value: Any, default: int, max_value: int) -> int:
|
||||
try:
|
||||
parsed = int(value)
|
||||
except (TypeError, ValueError):
|
||||
parsed = default
|
||||
|
||||
return max(0, min(parsed, max_value))
|
||||
|
||||
|
||||
@update_tracking_bp.post("/follow-artist")
|
||||
def follow_artist():
|
||||
data = request.get_json(silent=True) or {}
|
||||
artist_id = str(data.get("artist_id") or "").strip()
|
||||
|
||||
if not artist_id:
|
||||
return _error("artist_id is required")
|
||||
|
||||
payload = {
|
||||
"user_id": _user_id(),
|
||||
"artist_id": artist_id,
|
||||
"artist_name": str(data.get("artist_name") or artist_id),
|
||||
"follow_level": str(data.get("follow_level") or "followed"),
|
||||
"auto_download": bool(data.get("auto_download", False)),
|
||||
"preferred_quality": str(data.get("preferred_quality") or "flac"),
|
||||
"notification_preferences": data.get("notification_preferences"),
|
||||
"image": data.get("image"),
|
||||
}
|
||||
|
||||
if payload["follow_level"] not in VALID_FOLLOW_LEVELS:
|
||||
return _error("Invalid follow_level")
|
||||
|
||||
if payload["preferred_quality"] not in VALID_QUALITY_VALUES:
|
||||
return _error("Invalid preferred_quality")
|
||||
|
||||
success = update_tracker.follow_artist(payload)
|
||||
if not success:
|
||||
return _error("Failed to follow artist", 500)
|
||||
|
||||
return jsonify(
|
||||
{
|
||||
"message": "Artist followed successfully",
|
||||
"artist_id": artist_id,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@update_tracking_bp.post("/unfollow-artist")
|
||||
def unfollow_artist():
|
||||
data = request.get_json(silent=True) or {}
|
||||
artist_id = str(data.get("artist_id") or "").strip()
|
||||
|
||||
if not artist_id:
|
||||
return _error("artist_id is required")
|
||||
|
||||
success = update_tracker.unfollow_artist(_user_id(), artist_id)
|
||||
if not success:
|
||||
return _error("Artist not followed", 404)
|
||||
|
||||
return jsonify(
|
||||
{
|
||||
"message": "Artist unfollowed successfully",
|
||||
"artist_id": artist_id,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@update_tracking_bp.get("/recent")
|
||||
def get_recent_updates():
|
||||
limit = _safe_limit(request.args.get("limit"), default=20, max_value=100)
|
||||
offset = _safe_limit(request.args.get("offset"), default=0, max_value=100000)
|
||||
release_type = request.args.get("release_type")
|
||||
unread_only = str(request.args.get("unread_only", "false")).lower() == "true"
|
||||
|
||||
if release_type and release_type not in VALID_RELEASE_TYPES:
|
||||
return _error("Invalid release_type")
|
||||
|
||||
updates = update_tracker.get_user_updates(
|
||||
user_id=_user_id(),
|
||||
limit=limit,
|
||||
offset=offset,
|
||||
release_type=release_type,
|
||||
unread_only=unread_only,
|
||||
)
|
||||
|
||||
return jsonify(
|
||||
{
|
||||
"updates": updates,
|
||||
"limit": limit,
|
||||
"offset": offset,
|
||||
"total": len(updates),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@update_tracking_bp.get("/settings")
|
||||
def get_settings():
|
||||
return jsonify(update_tracker.get_user_settings(_user_id()))
|
||||
|
||||
|
||||
@update_tracking_bp.post("/settings")
|
||||
def update_settings():
|
||||
data = request.get_json(silent=True) or {}
|
||||
|
||||
check_frequency = data.get("checkFrequency", data.get("check_frequency"))
|
||||
if check_frequency and check_frequency not in VALID_CHECK_FREQUENCIES:
|
||||
return _error("Invalid checkFrequency")
|
||||
|
||||
quality_preference = data.get("qualityPreference", data.get("quality_preference"))
|
||||
if quality_preference and quality_preference not in VALID_QUALITY_VALUES:
|
||||
return _error("Invalid qualityPreference")
|
||||
|
||||
if not update_tracker.update_user_settings(_user_id(), data):
|
||||
return _error("Failed to update settings", 500)
|
||||
|
||||
return jsonify(
|
||||
{
|
||||
"message": "Settings updated successfully",
|
||||
"settings": update_tracker.get_user_settings(_user_id()),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@update_tracking_bp.post("/auto-download/<release_id>")
|
||||
def auto_download_release(release_id: str):
|
||||
if not update_tracker.auto_download_release(_user_id(), release_id):
|
||||
return _error("Release not found", 404)
|
||||
|
||||
return jsonify(
|
||||
{
|
||||
"message": "Download queued successfully",
|
||||
"release_id": release_id,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@update_tracking_bp.get("/stats")
|
||||
def get_update_stats():
|
||||
stats = update_tracker.get_user_stats(_user_id())
|
||||
return jsonify({"stats": stats})
|
||||
|
||||
|
||||
@update_tracking_bp.get("/followed-artists")
|
||||
def get_followed_artists():
|
||||
limit = _safe_limit(request.args.get("limit"), default=50, max_value=200)
|
||||
offset = _safe_limit(request.args.get("offset"), default=0, max_value=100000)
|
||||
follow_level = request.args.get("follow_level")
|
||||
|
||||
if follow_level and follow_level not in VALID_FOLLOW_LEVELS:
|
||||
return _error("Invalid follow_level")
|
||||
|
||||
artists = update_tracker.get_followed_artists(
|
||||
user_id=_user_id(),
|
||||
limit=limit,
|
||||
offset=offset,
|
||||
follow_level=follow_level,
|
||||
)
|
||||
|
||||
return jsonify(
|
||||
{
|
||||
"artists": artists,
|
||||
"limit": limit,
|
||||
"offset": offset,
|
||||
"total": len(artists),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@update_tracking_bp.get("/artist/<artist_id>/follow-status")
|
||||
def get_artist_follow_status(artist_id: str):
|
||||
status = update_tracker.get_artist_follow_status(_user_id(), artist_id)
|
||||
|
||||
if status:
|
||||
return jsonify(status)
|
||||
|
||||
return jsonify(
|
||||
{
|
||||
"is_following": False,
|
||||
"artist_id": artist_id,
|
||||
"follow_level": "followed",
|
||||
"auto_download_new_releases": False,
|
||||
"preferred_quality": "flac",
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@update_tracking_bp.route("/artist/<artist_id>", methods=["POST", "PUT"])
|
||||
def update_artist_follow(artist_id: str):
|
||||
data = request.get_json(silent=True) or {}
|
||||
|
||||
follow_level = data.get("follow_level")
|
||||
if follow_level and follow_level not in VALID_FOLLOW_LEVELS:
|
||||
return _error("Invalid follow_level")
|
||||
|
||||
preferred_quality = data.get("preferred_quality")
|
||||
if preferred_quality and preferred_quality not in VALID_QUALITY_VALUES:
|
||||
return _error("Invalid preferred_quality")
|
||||
|
||||
success = update_tracker.update_artist_follow(_user_id(), artist_id, data)
|
||||
if not success:
|
||||
return _error("Failed to update artist", 500)
|
||||
|
||||
return jsonify(
|
||||
{
|
||||
"message": "Artist follow settings updated",
|
||||
"artist_id": artist_id,
|
||||
"settings": data,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@update_tracking_bp.get("/search/artists")
|
||||
def search_artists():
|
||||
query = str(request.args.get("q") or "").strip()
|
||||
limit = _safe_limit(request.args.get("limit"), default=20, max_value=100)
|
||||
|
||||
artists = update_tracker.search_artists(query, _user_id(), limit=limit)
|
||||
return jsonify(
|
||||
{
|
||||
"artists": artists,
|
||||
"query": query,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@update_tracking_bp.post("/release/<release_id>/mark-read")
|
||||
def mark_release_read(release_id: str):
|
||||
if not update_tracker.mark_release_read(_user_id(), release_id):
|
||||
return _error("Failed to mark release as read", 500)
|
||||
|
||||
return jsonify(
|
||||
{
|
||||
"message": "Marked release as read",
|
||||
"release_id": release_id,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@update_tracking_bp.post("/notifications/mark-all-read")
|
||||
def mark_all_read():
|
||||
count = update_tracker.mark_all_notifications_read(_user_id())
|
||||
return jsonify(
|
||||
{
|
||||
"message": "All notifications marked as read",
|
||||
"updated": count,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@update_tracking_bp.get("/export/followed-artists")
|
||||
def export_followed_artists():
|
||||
export_format = str(request.args.get("format") or "json").lower()
|
||||
artists = update_tracker.export_followed_artists(_user_id())
|
||||
|
||||
if export_format == "csv":
|
||||
output = io.StringIO()
|
||||
writer = csv.DictWriter(
|
||||
output,
|
||||
fieldnames=[
|
||||
"artist_id",
|
||||
"artist_name",
|
||||
"follow_level",
|
||||
"auto_download",
|
||||
"preferred_quality",
|
||||
"follow_date",
|
||||
],
|
||||
)
|
||||
writer.writeheader()
|
||||
writer.writerows(artists)
|
||||
|
||||
return Response(
|
||||
output.getvalue(),
|
||||
mimetype="text/csv",
|
||||
headers={
|
||||
"Content-Disposition": "attachment; filename=followed_artists.csv",
|
||||
},
|
||||
)
|
||||
|
||||
return jsonify({"followed_artists": artists})
|
||||
|
||||
|
||||
@update_tracking_bp.errorhandler(404)
|
||||
def not_found(_error):
|
||||
return jsonify({"error": "Endpoint not found"}), 404
|
||||
|
||||
|
||||
@update_tracking_bp.errorhandler(500)
|
||||
def internal_error(_error):
|
||||
return jsonify({"error": "Internal server error"}), 500
|
||||
Reference in New Issue
Block a user