feat: Implement playlist management routes and integrate with music player service
This commit is contained in:
250
app/routes/playlist.py
Normal file
250
app/routes/playlist.py
Normal file
@@ -0,0 +1,250 @@
|
||||
"""Playlist management routes."""
|
||||
|
||||
from flask import Blueprint, jsonify, request
|
||||
from app.models.playlist import Playlist
|
||||
from app.models.sound import Sound
|
||||
from app.services.decorators import require_auth
|
||||
from app.services.music_player_service import music_player_service
|
||||
from app.services.logging_service import LoggingService
|
||||
|
||||
logger = LoggingService.get_logger(__name__)
|
||||
|
||||
bp = Blueprint("playlist", __name__)
|
||||
|
||||
|
||||
@bp.route("/", methods=["GET"])
|
||||
@require_auth
|
||||
def get_playlists():
|
||||
"""Get all playlists."""
|
||||
try:
|
||||
# Get system playlists and user playlists
|
||||
system_playlists = Playlist.find_system_playlists()
|
||||
user_playlists = [] # TODO: Add user-specific playlists when user auth is implemented
|
||||
|
||||
playlists = system_playlists + user_playlists
|
||||
|
||||
return jsonify({
|
||||
"playlists": [playlist.to_dict() for playlist in playlists]
|
||||
})
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting playlists: {e}")
|
||||
return jsonify({"error": "Failed to get playlists"}), 500
|
||||
|
||||
|
||||
@bp.route("/<int:playlist_id>", methods=["GET"])
|
||||
@require_auth
|
||||
def get_playlist(playlist_id):
|
||||
"""Get a specific playlist with sounds."""
|
||||
try:
|
||||
playlist = Playlist.query.get_or_404(playlist_id)
|
||||
return jsonify({"playlist": playlist.to_detailed_dict()})
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting playlist {playlist_id}: {e}")
|
||||
return jsonify({"error": "Failed to get playlist"}), 500
|
||||
|
||||
|
||||
@bp.route("/", methods=["POST"])
|
||||
@require_auth
|
||||
def create_playlist():
|
||||
"""Create a new playlist."""
|
||||
try:
|
||||
data = request.get_json()
|
||||
name = data.get("name")
|
||||
description = data.get("description")
|
||||
genre = data.get("genre")
|
||||
|
||||
if not name:
|
||||
return jsonify({"error": "Playlist name is required"}), 400
|
||||
|
||||
# Check if playlist with same name already exists
|
||||
existing = Playlist.find_by_name(name)
|
||||
if existing:
|
||||
return jsonify({"error": "Playlist with this name already exists"}), 400
|
||||
|
||||
playlist = Playlist.create_playlist(
|
||||
name=name,
|
||||
description=description,
|
||||
genre=genre,
|
||||
user_id=None, # System playlist for now
|
||||
is_deletable=True
|
||||
)
|
||||
|
||||
return jsonify({"playlist": playlist.to_dict()}), 201
|
||||
except Exception as e:
|
||||
logger.error(f"Error creating playlist: {e}")
|
||||
return jsonify({"error": "Failed to create playlist"}), 500
|
||||
|
||||
|
||||
@bp.route("/<int:playlist_id>", methods=["PUT"])
|
||||
@require_auth
|
||||
def update_playlist(playlist_id):
|
||||
"""Update a playlist."""
|
||||
try:
|
||||
playlist = Playlist.query.get_or_404(playlist_id)
|
||||
data = request.get_json()
|
||||
|
||||
if "name" in data:
|
||||
playlist.name = data["name"]
|
||||
if "description" in data:
|
||||
playlist.description = data["description"]
|
||||
if "genre" in data:
|
||||
playlist.genre = data["genre"]
|
||||
|
||||
playlist.save()
|
||||
|
||||
return jsonify({"playlist": playlist.to_dict()})
|
||||
except Exception as e:
|
||||
logger.error(f"Error updating playlist {playlist_id}: {e}")
|
||||
return jsonify({"error": "Failed to update playlist"}), 500
|
||||
|
||||
|
||||
@bp.route("/<int:playlist_id>", methods=["DELETE"])
|
||||
@require_auth
|
||||
def delete_playlist(playlist_id):
|
||||
"""Delete a playlist."""
|
||||
try:
|
||||
playlist = Playlist.query.get_or_404(playlist_id)
|
||||
|
||||
if not playlist.is_deletable:
|
||||
return jsonify({"error": "This playlist cannot be deleted"}), 400
|
||||
|
||||
# If this is the current playlist, clear it from the player
|
||||
current_playlist = Playlist.find_current_playlist()
|
||||
if current_playlist and current_playlist.id == playlist_id:
|
||||
# Set main playlist as current if it exists
|
||||
main_playlist = Playlist.find_main_playlist()
|
||||
if main_playlist:
|
||||
main_playlist.set_as_current()
|
||||
music_player_service.reload_current_playlist_if_modified(main_playlist.id)
|
||||
|
||||
playlist.delete()
|
||||
|
||||
return jsonify({"message": "Playlist deleted successfully"})
|
||||
except Exception as e:
|
||||
logger.error(f"Error deleting playlist {playlist_id}: {e}")
|
||||
return jsonify({"error": "Failed to delete playlist"}), 500
|
||||
|
||||
|
||||
@bp.route("/<int:playlist_id>/set-current", methods=["POST"])
|
||||
@require_auth
|
||||
def set_current_playlist(playlist_id):
|
||||
"""Set a playlist as the current one."""
|
||||
try:
|
||||
playlist = Playlist.query.get_or_404(playlist_id)
|
||||
playlist.set_as_current()
|
||||
|
||||
# Reload the playlist in the music player
|
||||
music_player_service.reload_current_playlist_if_modified(playlist_id)
|
||||
|
||||
return jsonify({"message": "Playlist set as current"})
|
||||
except Exception as e:
|
||||
logger.error(f"Error setting current playlist {playlist_id}: {e}")
|
||||
return jsonify({"error": "Failed to set current playlist"}), 500
|
||||
|
||||
|
||||
@bp.route("/<int:playlist_id>/sounds", methods=["POST"])
|
||||
@require_auth
|
||||
def add_sound_to_playlist(playlist_id):
|
||||
"""Add a sound to a playlist."""
|
||||
try:
|
||||
playlist = Playlist.query.get_or_404(playlist_id)
|
||||
data = request.get_json()
|
||||
sound_id = data.get("sound_id")
|
||||
order = data.get("order")
|
||||
|
||||
if not sound_id:
|
||||
return jsonify({"error": "Sound ID is required"}), 400
|
||||
|
||||
# Verify sound exists
|
||||
sound = Sound.query.get_or_404(sound_id)
|
||||
|
||||
# Add sound to playlist
|
||||
playlist_sound = playlist.add_sound(sound_id, order)
|
||||
|
||||
# Reload playlist in music player if it's the current one
|
||||
music_player_service.reload_current_playlist_if_modified(playlist_id)
|
||||
|
||||
return jsonify({
|
||||
"message": "Sound added to playlist",
|
||||
"playlist_sound": {
|
||||
"sound_id": playlist_sound.sound_id,
|
||||
"order": playlist_sound.order
|
||||
}
|
||||
}), 201
|
||||
except Exception as e:
|
||||
logger.error(f"Error adding sound to playlist {playlist_id}: {e}")
|
||||
return jsonify({"error": "Failed to add sound to playlist"}), 500
|
||||
|
||||
|
||||
@bp.route("/<int:playlist_id>/sounds/<int:sound_id>", methods=["DELETE"])
|
||||
@require_auth
|
||||
def remove_sound_from_playlist(playlist_id, sound_id):
|
||||
"""Remove a sound from a playlist."""
|
||||
try:
|
||||
playlist = Playlist.query.get_or_404(playlist_id)
|
||||
|
||||
success = playlist.remove_sound(sound_id)
|
||||
if not success:
|
||||
return jsonify({"error": "Sound not found in playlist"}), 404
|
||||
|
||||
# Reload playlist in music player if it's the current one
|
||||
music_player_service.reload_current_playlist_if_modified(playlist_id)
|
||||
|
||||
return jsonify({"message": "Sound removed from playlist"})
|
||||
except Exception as e:
|
||||
logger.error(f"Error removing sound from playlist {playlist_id}: {e}")
|
||||
return jsonify({"error": "Failed to remove sound from playlist"}), 500
|
||||
|
||||
|
||||
@bp.route("/<int:playlist_id>/sounds/reorder", methods=["PUT"])
|
||||
@require_auth
|
||||
def reorder_playlist_sounds(playlist_id):
|
||||
"""Reorder sounds in a playlist."""
|
||||
try:
|
||||
playlist = Playlist.query.get_or_404(playlist_id)
|
||||
data = request.get_json()
|
||||
sound_orders = data.get("sound_orders", [])
|
||||
|
||||
if not sound_orders:
|
||||
return jsonify({"error": "Sound orders are required"}), 400
|
||||
|
||||
# Validate sound_orders format
|
||||
for item in sound_orders:
|
||||
if not isinstance(item, dict) or "sound_id" not in item or "order" not in item:
|
||||
return jsonify({"error": "Invalid sound_orders format"}), 400
|
||||
|
||||
# Reorder sounds
|
||||
playlist.reorder_sounds(sound_orders)
|
||||
|
||||
# Reload playlist in music player if it's the current one
|
||||
music_player_service.reload_current_playlist_if_modified(playlist_id)
|
||||
|
||||
return jsonify({"message": "Playlist sounds reordered"})
|
||||
except Exception as e:
|
||||
logger.error(f"Error reordering playlist sounds {playlist_id}: {e}")
|
||||
return jsonify({"error": "Failed to reorder playlist sounds"}), 500
|
||||
|
||||
|
||||
@bp.route("/<int:playlist_id>/duplicate", methods=["POST"])
|
||||
@require_auth
|
||||
def duplicate_playlist(playlist_id):
|
||||
"""Duplicate a playlist."""
|
||||
try:
|
||||
playlist = Playlist.query.get_or_404(playlist_id)
|
||||
data = request.get_json()
|
||||
new_name = data.get("name")
|
||||
|
||||
if not new_name:
|
||||
return jsonify({"error": "New playlist name is required"}), 400
|
||||
|
||||
# Check if playlist with same name already exists
|
||||
existing = Playlist.find_by_name(new_name)
|
||||
if existing:
|
||||
return jsonify({"error": "Playlist with this name already exists"}), 400
|
||||
|
||||
new_playlist = playlist.duplicate(new_name)
|
||||
|
||||
return jsonify({"playlist": new_playlist.to_dict()}), 201
|
||||
except Exception as e:
|
||||
logger.error(f"Error duplicating playlist {playlist_id}: {e}")
|
||||
return jsonify({"error": "Failed to duplicate playlist"}), 500
|
||||
Reference in New Issue
Block a user