mirror of
https://github.com/Dvorinka/SpotifyRecAlg.git
synced 2026-06-03 20:13:03 +00:00
515 lines
14 KiB
Python
515 lines
14 KiB
Python
from __future__ import annotations
|
|
|
|
from flask import Blueprint, jsonify, request
|
|
|
|
fallback_ux_bp = Blueprint("fallback_ux", __name__, url_prefix="/api/ux")
|
|
fallback_updates_bp = Blueprint("fallback_updates", __name__, url_prefix="/api/updates")
|
|
fallback_audio_quality_bp = Blueprint(
|
|
"fallback_audio_quality", __name__, url_prefix="/api/audio-quality"
|
|
)
|
|
fallback_recap_bp = Blueprint("fallback_recap", __name__, url_prefix="/api/recap")
|
|
fallback_settings_bp = Blueprint(
|
|
"fallback_settings", __name__, url_prefix="/api/settings"
|
|
)
|
|
|
|
|
|
DEFAULT_AUDIO_SETTINGS = {
|
|
"streaming_quality": "high",
|
|
"adaptive_quality": True,
|
|
"network_aware_quality": True,
|
|
"device_specific_quality": True,
|
|
"download_format": "flac",
|
|
"download_sample_rate": "44.1kHz",
|
|
"download_bit_depth": "16bit",
|
|
"enable_loudness_normalization": True,
|
|
"target_loudness": -14.0,
|
|
"enable_adaptive_eq": False,
|
|
"enable_spatial_audio_processing": False,
|
|
"spatial_audio_format": "stereo",
|
|
"enable_crossfade": False,
|
|
"crossfade_duration": 2.0,
|
|
"enable_gapless_playback": True,
|
|
"enable_replaygain": True,
|
|
}
|
|
|
|
|
|
DEFAULT_UPDATE_SETTINGS = {
|
|
"enableArtistMonitoring": False,
|
|
"autoDownloadFavorites": False,
|
|
"enableNotifications": False,
|
|
"checkFrequency": "daily",
|
|
"qualityPreference": "flac",
|
|
"excludeExplicit": False,
|
|
}
|
|
|
|
|
|
DEFAULT_UD_SETTINGS = {
|
|
"defaultQuality": "high",
|
|
"autoAddToLibrary": True,
|
|
"maxConcurrentDownloads": 3,
|
|
}
|
|
|
|
|
|
def _disabled_payload(feature: str, **payload):
|
|
return {"enabled": False, "feature": feature, **payload}
|
|
|
|
|
|
@fallback_ux_bp.get("/search/suggestions")
|
|
def fallback_ux_search_suggestions():
|
|
query = request.args.get("q", "")
|
|
context = request.args.get("context", "general")
|
|
return jsonify(
|
|
_disabled_payload(
|
|
"advanced_ux",
|
|
suggestions=[],
|
|
query=query,
|
|
context=context,
|
|
total_count=0,
|
|
)
|
|
)
|
|
|
|
|
|
@fallback_ux_bp.get("/discovery/recommendations")
|
|
def fallback_ux_discovery():
|
|
recommendation_type = request.args.get("type", "mixed")
|
|
return jsonify(
|
|
_disabled_payload(
|
|
"advanced_ux",
|
|
recommendations=[],
|
|
type=recommendation_type,
|
|
total_count=0,
|
|
)
|
|
)
|
|
|
|
|
|
@fallback_ux_bp.get("/contextual/suggestions")
|
|
def fallback_ux_contextual():
|
|
track_id = request.args.get("track_id")
|
|
context_type = request.args.get("context_type", "similar")
|
|
return jsonify(
|
|
_disabled_payload(
|
|
"advanced_ux",
|
|
suggestions=[],
|
|
track_id=track_id,
|
|
context_type=context_type,
|
|
total_count=0,
|
|
)
|
|
)
|
|
|
|
|
|
@fallback_ux_bp.get("/download/suggestions")
|
|
def fallback_ux_download_suggestions():
|
|
query = request.args.get("q", "")
|
|
return jsonify(
|
|
_disabled_payload(
|
|
"advanced_ux",
|
|
suggestions=[],
|
|
query=query,
|
|
total_count=0,
|
|
)
|
|
)
|
|
|
|
|
|
@fallback_ux_bp.get("/search/filters")
|
|
def fallback_ux_filters():
|
|
return jsonify(_disabled_payload("advanced_ux", filters=[], total_count=0))
|
|
|
|
|
|
@fallback_ux_bp.post("/behavior/track")
|
|
def fallback_ux_track_behavior():
|
|
return jsonify(
|
|
_disabled_payload("advanced_ux", message="Behavior tracking skipped")
|
|
)
|
|
|
|
|
|
@fallback_ux_bp.get("/behavior/profile")
|
|
def fallback_ux_behavior_profile():
|
|
profile = {
|
|
"user_id": None,
|
|
"favorite_genres": [],
|
|
"favorite_artists": [],
|
|
"listening_patterns": {},
|
|
"download_preferences": {},
|
|
"interaction_patterns": {},
|
|
"last_updated": None,
|
|
"search_history_count": 0,
|
|
"recent_searches": [],
|
|
}
|
|
return jsonify(_disabled_payload("advanced_ux", profile=profile))
|
|
|
|
|
|
@fallback_ux_bp.get("/trending/content")
|
|
def fallback_ux_trending():
|
|
return jsonify(
|
|
_disabled_payload(
|
|
"advanced_ux",
|
|
trending=[],
|
|
type=request.args.get("type", "mixed"),
|
|
timeframe=request.args.get("timeframe", "week"),
|
|
total_count=0,
|
|
)
|
|
)
|
|
|
|
|
|
@fallback_ux_bp.post("/search/advanced")
|
|
def fallback_ux_advanced_search():
|
|
payload = request.get_json(silent=True) or {}
|
|
return jsonify(
|
|
_disabled_payload(
|
|
"advanced_ux",
|
|
query=payload.get("query", ""),
|
|
results={
|
|
"tracks": [],
|
|
"albums": [],
|
|
"artists": [],
|
|
"playlists": [],
|
|
},
|
|
)
|
|
)
|
|
|
|
|
|
@fallback_ux_bp.get("/suggestions/quick")
|
|
def fallback_ux_quick_suggestions():
|
|
return jsonify(
|
|
_disabled_payload(
|
|
"advanced_ux",
|
|
suggestions=[],
|
|
type=request.args.get("type", "search"),
|
|
total_count=0,
|
|
)
|
|
)
|
|
|
|
|
|
@fallback_ux_bp.get("/personalization/preferences")
|
|
def fallback_ux_get_preferences():
|
|
return jsonify(
|
|
_disabled_payload(
|
|
"advanced_ux",
|
|
preferences={"enable_personalization": False},
|
|
)
|
|
)
|
|
|
|
|
|
@fallback_ux_bp.put("/personalization/preferences")
|
|
def fallback_ux_update_preferences():
|
|
payload = request.get_json(silent=True) or {}
|
|
return jsonify(
|
|
_disabled_payload(
|
|
"advanced_ux",
|
|
message="Preferences saved in fallback mode",
|
|
preferences=payload,
|
|
)
|
|
)
|
|
|
|
|
|
@fallback_updates_bp.get("/stats")
|
|
def fallback_updates_stats():
|
|
stats = {
|
|
"followedArtists": 0,
|
|
"newReleases": 0,
|
|
"pendingDownloads": 0,
|
|
"unreadNotifications": 0,
|
|
}
|
|
return jsonify(_disabled_payload("update_tracking", stats=stats))
|
|
|
|
|
|
@fallback_updates_bp.get("/recent")
|
|
def fallback_updates_recent():
|
|
limit = request.args.get("limit", 20, type=int)
|
|
offset = request.args.get("offset", 0, type=int)
|
|
return jsonify(
|
|
_disabled_payload(
|
|
"update_tracking",
|
|
updates=[],
|
|
limit=limit,
|
|
offset=offset,
|
|
total=0,
|
|
)
|
|
)
|
|
|
|
|
|
@fallback_updates_bp.get("/followed-artists")
|
|
def fallback_updates_followed_artists():
|
|
return jsonify(
|
|
_disabled_payload(
|
|
"update_tracking",
|
|
artists=[],
|
|
limit=50,
|
|
offset=0,
|
|
total=0,
|
|
)
|
|
)
|
|
|
|
|
|
@fallback_updates_bp.get("/settings")
|
|
def fallback_updates_get_settings():
|
|
return jsonify(_disabled_payload("update_tracking", **DEFAULT_UPDATE_SETTINGS))
|
|
|
|
|
|
@fallback_updates_bp.post("/settings")
|
|
def fallback_updates_set_settings():
|
|
payload = request.get_json(silent=True) or {}
|
|
merged = {**DEFAULT_UPDATE_SETTINGS, **payload}
|
|
return jsonify(
|
|
_disabled_payload(
|
|
"update_tracking",
|
|
message="Settings saved in fallback mode",
|
|
settings=merged,
|
|
)
|
|
)
|
|
|
|
|
|
@fallback_updates_bp.get("/search/artists")
|
|
def fallback_updates_search_artists():
|
|
return jsonify(
|
|
_disabled_payload(
|
|
"update_tracking",
|
|
artists=[],
|
|
query=request.args.get("q", ""),
|
|
)
|
|
)
|
|
|
|
|
|
@fallback_updates_bp.post("/follow-artist")
|
|
def fallback_updates_follow_artist():
|
|
payload = request.get_json(silent=True) or {}
|
|
return jsonify(
|
|
_disabled_payload(
|
|
"update_tracking",
|
|
message="Artist follow stored in fallback mode",
|
|
artist_id=payload.get("artist_id"),
|
|
)
|
|
)
|
|
|
|
|
|
@fallback_updates_bp.post("/unfollow-artist")
|
|
def fallback_updates_unfollow_artist():
|
|
payload = request.get_json(silent=True) or {}
|
|
return jsonify(
|
|
_disabled_payload(
|
|
"update_tracking",
|
|
message="Artist unfollow stored in fallback mode",
|
|
artist_id=payload.get("artist_id"),
|
|
)
|
|
)
|
|
|
|
|
|
@fallback_updates_bp.get("/artist/<artist_id>/follow-status")
|
|
def fallback_updates_follow_status(artist_id: str):
|
|
return jsonify(
|
|
_disabled_payload(
|
|
"update_tracking",
|
|
is_following=False,
|
|
artist_id=artist_id,
|
|
follow_level="followed",
|
|
auto_download_new_releases=False,
|
|
preferred_quality="flac",
|
|
)
|
|
)
|
|
|
|
|
|
@fallback_updates_bp.post("/artist/<artist_id>")
|
|
def fallback_updates_update_artist(artist_id: str):
|
|
payload = request.get_json(silent=True) or {}
|
|
return jsonify(
|
|
_disabled_payload(
|
|
"update_tracking",
|
|
message="Artist preferences saved in fallback mode",
|
|
artist_id=artist_id,
|
|
settings=payload,
|
|
)
|
|
)
|
|
|
|
|
|
@fallback_updates_bp.post("/auto-download/<release_id>")
|
|
def fallback_updates_auto_download(release_id: str):
|
|
return jsonify(
|
|
_disabled_payload(
|
|
"update_tracking",
|
|
message="Download queued in fallback mode",
|
|
release_id=release_id,
|
|
)
|
|
)
|
|
|
|
|
|
@fallback_updates_bp.post("/release/<release_id>/mark-read")
|
|
def fallback_updates_mark_read(release_id: str):
|
|
return jsonify(
|
|
_disabled_payload(
|
|
"update_tracking",
|
|
message="Marked as read",
|
|
release_id=release_id,
|
|
)
|
|
)
|
|
|
|
|
|
@fallback_updates_bp.post("/notifications/mark-all-read")
|
|
def fallback_updates_mark_all_read():
|
|
return jsonify(
|
|
_disabled_payload(
|
|
"update_tracking",
|
|
message="All notifications marked as read",
|
|
)
|
|
)
|
|
|
|
|
|
@fallback_updates_bp.get("/export/followed-artists")
|
|
def fallback_updates_export_followed_artists():
|
|
return jsonify(_disabled_payload("update_tracking", followed_artists=[]))
|
|
|
|
|
|
@fallback_audio_quality_bp.get("/settings")
|
|
def fallback_audio_get_settings():
|
|
return jsonify(_disabled_payload("audio_quality", settings=DEFAULT_AUDIO_SETTINGS))
|
|
|
|
|
|
@fallback_audio_quality_bp.post("/settings")
|
|
def fallback_audio_set_settings():
|
|
payload = request.get_json(silent=True) or {}
|
|
merged = {**DEFAULT_AUDIO_SETTINGS, **payload}
|
|
return jsonify(
|
|
_disabled_payload(
|
|
"audio_quality",
|
|
message="Audio quality settings saved in fallback mode",
|
|
settings=merged,
|
|
)
|
|
)
|
|
|
|
|
|
@fallback_audio_quality_bp.get("/network/status")
|
|
def fallback_audio_network_status():
|
|
return jsonify(
|
|
_disabled_payload(
|
|
"audio_quality",
|
|
network_status={"speed": 0, "quality": "unknown"},
|
|
)
|
|
)
|
|
|
|
|
|
@fallback_audio_quality_bp.get("/device/info")
|
|
def fallback_audio_device_info():
|
|
return jsonify(
|
|
_disabled_payload(
|
|
"audio_quality",
|
|
device_info={"type": "unknown"},
|
|
)
|
|
)
|
|
|
|
|
|
@fallback_audio_quality_bp.post("/apply-preset")
|
|
def fallback_audio_apply_preset():
|
|
payload = request.get_json(silent=True) or {}
|
|
return jsonify(
|
|
_disabled_payload(
|
|
"audio_quality",
|
|
message="Preset applied in fallback mode",
|
|
preset_name=payload.get("preset_name"),
|
|
settings=DEFAULT_AUDIO_SETTINGS,
|
|
)
|
|
)
|
|
|
|
|
|
@fallback_recap_bp.get("/available-years")
|
|
def fallback_recap_available_years():
|
|
return jsonify(_disabled_payload("recap", available_years=[], total_recaps=0))
|
|
|
|
|
|
@fallback_recap_bp.get("/summary/<int:year>")
|
|
def fallback_recap_summary(year: int):
|
|
return jsonify(_disabled_payload("recap", recap=None, year=year))
|
|
|
|
|
|
@fallback_recap_bp.get("/details/<int:year>")
|
|
def fallback_recap_details(year: int):
|
|
return jsonify(_disabled_payload("recap", recap=None, year=year))
|
|
|
|
|
|
@fallback_recap_bp.post("/generate/<int:year>")
|
|
def fallback_recap_generate(year: int):
|
|
return jsonify(
|
|
_disabled_payload(
|
|
"recap",
|
|
message="Recap generation is unavailable",
|
|
year=year,
|
|
)
|
|
)
|
|
|
|
|
|
@fallback_recap_bp.post("/video/<int:year>")
|
|
def fallback_recap_video(year: int):
|
|
return jsonify(
|
|
_disabled_payload(
|
|
"recap",
|
|
message="Recap video generation is unavailable",
|
|
year=year,
|
|
)
|
|
)
|
|
|
|
|
|
@fallback_recap_bp.post("/share/<int:year>")
|
|
def fallback_recap_share(year: int):
|
|
return jsonify(
|
|
_disabled_payload(
|
|
"recap",
|
|
message="Share links are unavailable",
|
|
year=year,
|
|
share_url=None,
|
|
)
|
|
)
|
|
|
|
|
|
@fallback_recap_bp.get("/shared/<token>")
|
|
def fallback_recap_shared(token: str):
|
|
return jsonify(_disabled_payload("recap", recap=None, token=token))
|
|
|
|
|
|
@fallback_recap_bp.get("/compare/<int:year1>/<int:year2>")
|
|
def fallback_recap_compare(year1: int, year2: int):
|
|
return jsonify(_disabled_payload("recap", comparison=None, years=[year1, year2]))
|
|
|
|
|
|
@fallback_settings_bp.get("/universal-downloader")
|
|
def fallback_universal_downloader_get():
|
|
return jsonify(
|
|
_disabled_payload(
|
|
"universal_downloader_settings",
|
|
success=True,
|
|
settings=DEFAULT_UD_SETTINGS,
|
|
)
|
|
)
|
|
|
|
|
|
@fallback_settings_bp.post("/universal-downloader")
|
|
def fallback_universal_downloader_post():
|
|
payload = request.get_json(silent=True) or {}
|
|
merged = {**DEFAULT_UD_SETTINGS, **payload}
|
|
return jsonify(
|
|
_disabled_payload(
|
|
"universal_downloader_settings",
|
|
success=True,
|
|
settings=merged,
|
|
message="Settings saved in fallback mode",
|
|
)
|
|
)
|
|
|
|
|
|
def _has_route(app, route: str) -> bool:
|
|
return any(rule.rule == route for rule in app.url_map.iter_rules())
|
|
|
|
|
|
def register_optional_feature_fallbacks(app):
|
|
if not _has_route(app, "/api/ux/search/suggestions"):
|
|
app.register_blueprint(fallback_ux_bp)
|
|
|
|
if not _has_route(app, "/api/updates/stats"):
|
|
app.register_blueprint(fallback_updates_bp)
|
|
|
|
if not _has_route(app, "/api/audio-quality/settings"):
|
|
app.register_blueprint(fallback_audio_quality_bp)
|
|
|
|
if not _has_route(app, "/api/recap/available-years"):
|
|
app.register_blueprint(fallback_recap_bp)
|
|
|
|
if not _has_route(app, "/api/settings/universal-downloader"):
|
|
app.register_blueprint(fallback_settings_bp)
|