mirror of
https://github.com/Dvorinka/SpotifyRecAlg.git
synced 2026-06-03 20:13:03 +00:00
222 lines
6.8 KiB
Python
222 lines
6.8 KiB
Python
"""
|
|
Music Unlocker - Auth-free music metadata service
|
|
Wraps swingmusic's reverse-engineered clients
|
|
"""
|
|
|
|
import sys
|
|
import json
|
|
import logging
|
|
from pathlib import Path
|
|
from dataclasses import asdict
|
|
|
|
# Add swingmusic to path
|
|
sys.path.insert(0, str(Path(__file__).parent.parent.parent / "swingmusic"))
|
|
|
|
from flask import Flask, jsonify, request
|
|
from flask_cors import CORS
|
|
|
|
from services.spotify_web_player_client import SpotifyWebPlayerClient
|
|
from services.songlink_client import SongLinkClient
|
|
from services.universal_url_parser import UniversalMusicURLParser, MusicService
|
|
|
|
logging.basicConfig(level=logging.INFO)
|
|
logger = logging.getLogger(__name__)
|
|
|
|
app = Flask(__name__)
|
|
CORS(app)
|
|
|
|
# Clients
|
|
spotify_client = SpotifyWebPlayerClient()
|
|
songlink_client = SongLinkClient()
|
|
url_parser = UniversalMusicURLParser()
|
|
|
|
|
|
def track_to_dict(track):
|
|
"""Convert SpotifyTrack to dict"""
|
|
return {
|
|
"id": track.id,
|
|
"title": track.name,
|
|
"artist": track.artists[0]["name"] if track.artists else "Unknown",
|
|
"artists": [a["name"] for a in track.artists],
|
|
"album": track.album.get("name", "") if track.album else "",
|
|
"duration_ms": track.duration_ms,
|
|
"explicit": track.explicit,
|
|
"external_urls": track.external_urls or {},
|
|
}
|
|
|
|
|
|
@app.route("/health", methods=["GET"])
|
|
def health():
|
|
return jsonify({"status": "ok"})
|
|
|
|
|
|
@app.route("/parse", methods=["POST"])
|
|
def parse_url():
|
|
"""Parse any music service URL"""
|
|
data = request.get_json()
|
|
url = data.get("url", "")
|
|
|
|
parsed = url_parser.parse_url(url)
|
|
if not parsed:
|
|
return jsonify({"error": "Unsupported URL"}), 400
|
|
|
|
return jsonify({
|
|
"service": parsed.service.value,
|
|
"item_type": parsed.item_type,
|
|
"id": parsed.id,
|
|
"metadata": parsed.metadata or {}
|
|
})
|
|
|
|
|
|
@app.route("/spotify/track/<track_id>", methods=["GET"])
|
|
def get_track(track_id):
|
|
"""Get track metadata from Spotify (no auth required)"""
|
|
try:
|
|
track = spotify_client.get_track(track_id)
|
|
if not track:
|
|
return jsonify({"error": "Track not found"}), 404
|
|
|
|
return jsonify(track_to_dict(track))
|
|
except Exception as e:
|
|
logger.error(f"Error fetching track: {e}")
|
|
return jsonify({"error": str(e)}), 500
|
|
|
|
|
|
@app.route("/spotify/album/<album_id>", methods=["GET"])
|
|
def get_album(album_id):
|
|
"""Get album with tracks from Spotify (no auth required)"""
|
|
try:
|
|
album = spotify_client.get_album(album_id)
|
|
if not album:
|
|
return jsonify({"error": "Album not found"}), 404
|
|
|
|
return jsonify({
|
|
"id": album.id,
|
|
"title": album.name,
|
|
"artist": album.artists[0]["name"] if album.artists else "Unknown",
|
|
"tracks": [track_to_dict(t) for t in (album.tracks or [])],
|
|
})
|
|
except Exception as e:
|
|
logger.error(f"Error fetching album: {e}")
|
|
return jsonify({"error": str(e)}), 500
|
|
|
|
|
|
@app.route("/spotify/playlist/<playlist_id>", methods=["GET"])
|
|
def get_playlist(playlist_id):
|
|
"""Get playlist with tracks from Spotify (no auth required)"""
|
|
try:
|
|
playlist = spotify_client.get_playlist(playlist_id)
|
|
if not playlist:
|
|
return jsonify({"error": "Playlist not found"}), 404
|
|
|
|
return jsonify({
|
|
"id": playlist.id,
|
|
"title": playlist.name,
|
|
"description": playlist.description,
|
|
"tracks": [track_to_dict(t) for t in (playlist.tracks or [])],
|
|
})
|
|
except Exception as e:
|
|
logger.error(f"Error fetching playlist: {e}")
|
|
return jsonify({"error": str(e)}), 500
|
|
|
|
|
|
@app.route("/spotify/search", methods=["POST"])
|
|
def search():
|
|
"""Search Spotify (no auth required)"""
|
|
data = request.get_json()
|
|
query = data.get("q", "")
|
|
item_type = data.get("type", "track")
|
|
limit = data.get("limit", 10)
|
|
|
|
try:
|
|
results = spotify_client.search(query, item_type, limit)
|
|
return jsonify({
|
|
"results": [track_to_dict(r) for r in results]
|
|
})
|
|
except Exception as e:
|
|
logger.error(f"Error searching: {e}")
|
|
return jsonify({"error": str(e)}), 500
|
|
|
|
|
|
@app.route("/links/<spotify_id>", methods=["GET"])
|
|
def get_links(spotify_id):
|
|
"""Get cross-platform links for a Spotify track"""
|
|
try:
|
|
links = songlink_client.get_links_from_spotify_id(spotify_id, "track")
|
|
if not links:
|
|
return jsonify({"error": "Links not found"}), 404
|
|
|
|
return jsonify({
|
|
"spotify_id": links.spotify_id,
|
|
"isrc": links.isrc,
|
|
"links": {
|
|
platform: {"url": link.url, "id": link.id}
|
|
for platform, link in links.links.items()
|
|
}
|
|
})
|
|
except Exception as e:
|
|
logger.error(f"Error getting links: {e}")
|
|
return jsonify({"error": str(e)}), 500
|
|
|
|
|
|
@app.route("/import", methods=["POST"])
|
|
def import_track():
|
|
"""Import track from any URL - returns metadata + cross-platform links"""
|
|
data = request.get_json()
|
|
url = data.get("url", "")
|
|
|
|
# Parse URL
|
|
parsed = url_parser.parse_url(url)
|
|
if not parsed:
|
|
return jsonify({"error": "Unsupported URL format"}), 400
|
|
|
|
try:
|
|
track_data = None
|
|
|
|
# Get metadata based on service
|
|
if parsed.service == MusicService.SPOTIFY:
|
|
if parsed.item_type == "track":
|
|
track = spotify_client.get_track(parsed.id)
|
|
if track:
|
|
track_data = track_to_dict(track)
|
|
elif parsed.item_type == "album":
|
|
album = spotify_client.get_album(parsed.id)
|
|
if album and album.tracks:
|
|
track_data = track_to_dict(album.tracks[0])
|
|
elif parsed.item_type == "playlist":
|
|
playlist = spotify_client.get_playlist(parsed.id)
|
|
if playlist and playlist.tracks:
|
|
track_data = track_to_dict(playlist.tracks[0])
|
|
|
|
# For other services, we'd need their respective clients
|
|
# For now, return the parsed info
|
|
|
|
if not track_data:
|
|
return jsonify({
|
|
"parsed": {
|
|
"service": parsed.service.value,
|
|
"type": parsed.item_type,
|
|
"id": parsed.id,
|
|
},
|
|
"note": "Metadata fetch for this service not yet implemented"
|
|
})
|
|
|
|
# Get cross-platform links
|
|
links = songlink_client.get_links_from_spotify_id(track_data["id"], "track")
|
|
|
|
return jsonify({
|
|
"track": track_data,
|
|
"links": {
|
|
platform: link.url
|
|
for platform, link in (links.links.items() if links else [])
|
|
} if links else {}
|
|
})
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error importing: {e}")
|
|
return jsonify({"error": str(e)}), 500
|
|
|
|
|
|
if __name__ == "__main__":
|
|
app.run(host="0.0.0.0", port=5000, debug=False)
|