Initial commit - Navi.FM v3
This commit is contained in:
@@ -0,0 +1,111 @@
|
||||
import time
|
||||
import random
|
||||
from sqlalchemy.sql.expression import func
|
||||
from database import SessionLocal, Track, Setting
|
||||
|
||||
def get_active_profile(db):
|
||||
setting = db.query(Setting).filter(Setting.key == "active_profile").first()
|
||||
return setting.value if setting else "harmonic"
|
||||
|
||||
def set_active_profile(profile_name: str):
|
||||
db = SessionLocal()
|
||||
try:
|
||||
setting = db.query(Setting).filter(Setting.key == "active_profile").first()
|
||||
if not setting:
|
||||
setting = Setting(key="active_profile", value=profile_name)
|
||||
db.add(setting)
|
||||
else:
|
||||
setting.value = profile_name
|
||||
db.commit()
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
def get_next_track():
|
||||
db = SessionLocal()
|
||||
try:
|
||||
profile = get_active_profile(db)
|
||||
track = None
|
||||
|
||||
# Only select from analyzed tracks
|
||||
base_query = db.query(Track).filter(Track.analyzed == True)
|
||||
|
||||
# Avoid playing tracks that were played in the last hour
|
||||
one_hour_ago = int(time.time()) - 3600
|
||||
pool_query = base_query.filter(Track.last_played < one_hour_ago)
|
||||
|
||||
# Artist Separation Logic
|
||||
artist_sep_enabled = db.query(Setting).filter(Setting.key == "artist_separation_enabled").first()
|
||||
if artist_sep_enabled and artist_sep_enabled.value == "true":
|
||||
artist_sep_count_setting = db.query(Setting).filter(Setting.key == "artist_separation_count").first()
|
||||
sep_count = int(artist_sep_count_setting.value) if artist_sep_count_setting else 5
|
||||
|
||||
recent_tracks = db.query(Track).filter(Track.last_played > 0).order_by(Track.last_played.desc()).limit(sep_count).all()
|
||||
recent_artists = [t.artist for t in recent_tracks if t.artist]
|
||||
|
||||
if recent_artists:
|
||||
filtered_query = pool_query.filter(~Track.artist.in_(recent_artists))
|
||||
if filtered_query.count() > 0:
|
||||
pool_query = filtered_query
|
||||
|
||||
if pool_query.count() == 0:
|
||||
# Fallback if all tracks were played recently or no tracks analyzed
|
||||
pool_query = base_query
|
||||
|
||||
if profile == "high_energy":
|
||||
# Pick from most energetic tracks
|
||||
tracks = pool_query.order_by(Track.energy.desc()).limit(50).all()
|
||||
if tracks:
|
||||
track = random.choice(tracks)
|
||||
elif profile == "chill":
|
||||
# Pick from least energetic tracks
|
||||
tracks = pool_query.order_by(Track.energy.asc()).limit(50).all()
|
||||
if tracks:
|
||||
track = random.choice(tracks)
|
||||
elif profile == "harmonic":
|
||||
# Get last played track to match key/bpm
|
||||
last_played = base_query.order_by(Track.last_played.desc()).first()
|
||||
if last_played and last_played.key and last_played.bpm:
|
||||
# Find matching key and similar BPM (+- 10)
|
||||
track = pool_query.filter(Track.key == last_played.key)\
|
||||
.filter(Track.bpm.between(last_played.bpm - 10, last_played.bpm + 10))\
|
||||
.order_by(func.random()).first()
|
||||
|
||||
# Fallback to random if no track found for profile or profile is 'random'
|
||||
if not track:
|
||||
track = pool_query.order_by(func.random()).first()
|
||||
|
||||
if track:
|
||||
# Update last played
|
||||
track.last_played = int(time.time())
|
||||
track.play_count += 1
|
||||
db.commit()
|
||||
return {"id": track.id, "lufs": track.lufs}
|
||||
return None
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
def get_current_track():
|
||||
db = SessionLocal()
|
||||
try:
|
||||
# The currently playing track is approximated as the most recently queued track
|
||||
track = db.query(Track).order_by(Track.last_played.desc()).first()
|
||||
if track and track.last_played > 0:
|
||||
return {
|
||||
"id": track.id,
|
||||
"title": track.title,
|
||||
"artist": track.artist,
|
||||
"bpm": track.bpm,
|
||||
"energy": track.energy,
|
||||
"lufs": track.lufs
|
||||
}
|
||||
return None
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
# Define available profiles for the WebUI
|
||||
AVAILABLE_PROFILES = [
|
||||
{"id": "harmonic", "name": "Harmonic Mix", "description": "Attempts to match the key and BPM of the previously played track."},
|
||||
{"id": "high_energy", "name": "High Energy", "description": "Prioritizes tracks with high RMS energy."},
|
||||
{"id": "chill", "name": "Chill", "description": "Prioritizes low-energy, ambient tracks."},
|
||||
{"id": "random", "name": "Random", "description": "Plays tracks randomly."}
|
||||
]
|
||||
Reference in New Issue
Block a user