Working MPD client using distance meetrics only
This commit is contained in:
parent
36357d4423
commit
40b7df9140
0
client/build_cache.py
Normal file → Executable file
0
client/build_cache.py
Normal file → Executable file
69
client/client.py
Normal file → Executable file
69
client/client.py
Normal file → Executable file
@ -2,13 +2,14 @@
|
|||||||
import logging
|
import logging
|
||||||
import math
|
import math
|
||||||
import os
|
import os
|
||||||
|
import random
|
||||||
import sqlite3
|
import sqlite3
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
logging.basicConfig(level=logging.INFO)
|
logging.basicConfig(level=logging.INFO)
|
||||||
|
|
||||||
_QUEUE_LENGTH = 100
|
_QUEUE_LENGTH = 10
|
||||||
# TODO: Use cosine similarity as well
|
# TODO: Use cosine similarity as well
|
||||||
_DISTANCE_THRESHOLD = 4.0
|
_DISTANCE_THRESHOLD = 4.0
|
||||||
|
|
||||||
@ -27,29 +28,34 @@ def main():
|
|||||||
conn.execute('pragma foreign_keys=ON')
|
conn.execute('pragma foreign_keys=ON')
|
||||||
cur = conn.cursor()
|
cur = conn.cursor()
|
||||||
|
|
||||||
|
# Take the last song from current playlist and iterate from it
|
||||||
current_song = subprocess.check_output(
|
current_song = subprocess.check_output(
|
||||||
["mpc", "current", '--format', '"%file%"'])
|
["mpc", "playlist", '--format', '"%file%"'])
|
||||||
current_song = current_song.decode("utf-8").strip('" \r\n\t')
|
current_song = current_song.decode("utf-8").strip().split("\n")[-1]
|
||||||
|
current_song = current_song.strip('"')
|
||||||
|
# If current playlist is empty
|
||||||
if current_song is "":
|
if current_song is "":
|
||||||
logging.warning("Currently played song could not be found.")
|
# Add a random song to start with
|
||||||
sys.exit(1)
|
all_songs = subprocess.check_output(["mpc", "listall"]).decode("utf-8")
|
||||||
|
all_songs = all_songs.strip().split("\n")
|
||||||
|
current_song = random.choice(all_songs).strip("'")
|
||||||
|
subprocess.check_call(["mpc", "add", current_song])
|
||||||
logging.info("Currently played song is %s." % (current_song,))
|
logging.info("Currently played song is %s." % (current_song,))
|
||||||
|
|
||||||
# Get current song coordinates
|
# Get current song coordinates
|
||||||
cur.execute("SELECT id, tempo, amplitude, frequency, attack, filename FROM songs WHERE filename=?", (current_song,))
|
cur.execute("SELECT id, tempo, amplitude, frequency, attack, filename FROM songs WHERE filename=?", (current_song,))
|
||||||
current_song = cur.fetchone()
|
current_song_coords = cur.fetchone()
|
||||||
if current_song is None:
|
if current_song_coords is None:
|
||||||
logging.warning("Current song %s is not in db. You should update the db." %
|
logging.warning("Current song %s is not in db. You should update the db." %
|
||||||
(current_song["filename"],))
|
(current_song,))
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
for i in range(_QUEUE_LENGTH):
|
for i in range(_QUEUE_LENGTH):
|
||||||
# Append current song to the mpd queue to avoid duplicates
|
mpd_queue.append(current_song_coords["filename"])
|
||||||
mpd_queue.append(current_song["filename"])
|
|
||||||
# Get cached distances from db
|
# Get cached distances from db
|
||||||
cur.execute(
|
cur.execute(
|
||||||
"SELECT id, filename, distance FROM (SELECT s2.id AS id, s2.filename AS filename, distances.distance AS distance FROM distances INNER JOIN songs AS s1 ON s1.id=distances.song1 INNER JOIN songs AS s2 on s2.id=distances.song2 WHERE s1.filename=? UNION SELECT s1.id as id, s1.filename AS filename, distances.distance as distance FROM distances INNER JOIN songs AS s1 ON s1.id=distances.song1 INNER JOIN songs AS s2 on s2.id=distances.song2 WHERE s2.filename=?) ORDER BY distance ASC",
|
"SELECT id, filename, distance, tempo, amplitude, frequency, attack FROM (SELECT s2.id AS id, s2.filename AS filename, s2.tempo AS tempo, s2.amplitude AS amplitude, s2.frequency AS frequency, s2.attack AS attack, distances.distance AS distance FROM distances INNER JOIN songs AS s1 ON s1.id=distances.song1 INNER JOIN songs AS s2 on s2.id=distances.song2 WHERE s1.filename=? UNION SELECT s1.id as id, s1.filename AS filename, s1.tempo AS tempo, s1.amplitude AS amplitude, s1.frequency AS frequency, s1.attack AS attack, distances.distance as distance FROM distances INNER JOIN songs AS s1 ON s1.id=distances.song1 INNER JOIN songs AS s2 on s2.id=distances.song2 WHERE s2.filename=?) ORDER BY distance ASC",
|
||||||
(current_song["filename"], current_song["filename"]))
|
(current_song_coords["filename"], current_song_coords["filename"]))
|
||||||
cached_distances = [row
|
cached_distances = [row
|
||||||
for row in cur.fetchall()
|
for row in cur.fetchall()
|
||||||
if row["filename"] not in mpd_queue]
|
if row["filename"] not in mpd_queue]
|
||||||
@ -63,15 +69,16 @@ def main():
|
|||||||
cached_distances[0]["filename"]])
|
cached_distances[0]["filename"]])
|
||||||
# Continue using latest pushed song as current song
|
# Continue using latest pushed song as current song
|
||||||
logging.info("Using cached distance. Found %s. Distance is %f." %
|
logging.info("Using cached distance. Found %s. Distance is %f." %
|
||||||
(current_song["filename"], cached_distances[0]["distance"]))
|
(cached_distances[0]["filename"],
|
||||||
current_song = cached_distances[0]
|
cached_distances[0]["distance"]))
|
||||||
|
current_song_coords = cached_distances[0]
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Get all other songs coordinates
|
# Get all other songs coordinates
|
||||||
closest_song = None
|
closest_song = None
|
||||||
cur.execute("SELECT id, tempo, amplitude, frequency, attack, filename FROM songs")
|
cur.execute("SELECT id, tempo, amplitude, frequency, attack, filename FROM songs")
|
||||||
for tmp_song_data in cur.fetchall():
|
for tmp_song_data in cur.fetchall():
|
||||||
if(tmp_song_data["filename"] == current_song["filename"] or
|
if(tmp_song_data["filename"] == current_song_coords["filename"] or
|
||||||
tmp_song_data["filename"] in cached_distances_songs or
|
tmp_song_data["filename"] in cached_distances_songs or
|
||||||
tmp_song_data["filename"] in mpd_queue):
|
tmp_song_data["filename"] in mpd_queue):
|
||||||
# Skip current song and already processed songs
|
# Skip current song and already processed songs
|
||||||
@ -79,41 +86,49 @@ def main():
|
|||||||
continue
|
continue
|
||||||
# Compute distance
|
# Compute distance
|
||||||
distance = math.sqrt(
|
distance = math.sqrt(
|
||||||
(current_song["tempo"] - tmp_song_data["tempo"])**2 +
|
(current_song_coords["tempo"] - tmp_song_data["tempo"])**2 +
|
||||||
(current_song["amplitude"] - tmp_song_data["amplitude"])**2 +
|
(current_song_coords["amplitude"] - tmp_song_data["amplitude"])**2 +
|
||||||
(current_song["frequency"] - tmp_song_data["frequency"])**2 +
|
(current_song_coords["frequency"] - tmp_song_data["frequency"])**2 +
|
||||||
(current_song["attack"] - tmp_song_data["attack"])**2
|
(current_song_coords["attack"] - tmp_song_data["attack"])**2
|
||||||
)
|
)
|
||||||
logging.debug("Distance between %s and %s is %f." %
|
logging.debug("Distance between %s and %s is %f." %
|
||||||
(current_song["filename"],
|
(current_song_coords["filename"],
|
||||||
tmp_song_data["filename"], distance))
|
tmp_song_data["filename"], distance))
|
||||||
# Store distance in db cache
|
# Store distance in db cache
|
||||||
try:
|
try:
|
||||||
logging.debug("Storing distance in database.")
|
logging.debug("Storing distance in database.")
|
||||||
conn.execute(
|
conn.execute(
|
||||||
"INSERT INTO distances(song1, song2, distance) VALUES(?, ?, ?)",
|
"INSERT INTO distances(song1, song2, distance) VALUES(?, ?, ?)",
|
||||||
(current_song["id"], tmp_song_data["id"], distance))
|
(current_song_coords["id"], tmp_song_data["id"], distance))
|
||||||
conn.commit()
|
conn.commit()
|
||||||
except sqlite3.IntegrityError:
|
except sqlite3.IntegrityError:
|
||||||
logging.warning("Unable to insert distance in database.")
|
logging.warning("Unable to insert distance in database.")
|
||||||
conn.rollback()
|
conn.rollback()
|
||||||
|
|
||||||
# If distance is ok, just add the song
|
# Update the closest song
|
||||||
|
if closest_song is None or distance < closest_song[1]:
|
||||||
|
closest_song = (tmp_song_data, distance)
|
||||||
|
|
||||||
|
# If distance is ok, break from the loop
|
||||||
|
if distance < _DISTANCE_THRESHOLD:
|
||||||
|
break
|
||||||
|
|
||||||
|
# If a close enough song is found
|
||||||
if distance < _DISTANCE_THRESHOLD:
|
if distance < _DISTANCE_THRESHOLD:
|
||||||
# Push it on the queue
|
# Push it on the queue
|
||||||
subprocess.check_call(["mpc", "add", tmp_song_data["filename"]])
|
subprocess.check_call(["mpc", "add", tmp_song_data["filename"]])
|
||||||
# Continue using latest pushed song as current song
|
# Continue using latest pushed song as current song
|
||||||
logging.info("Found a close song: %s. Distance is %f." %
|
logging.info("Found a close song: %s. Distance is %f." %
|
||||||
(tmp_song_data["filename"], distance))
|
(tmp_song_data["filename"], distance))
|
||||||
current_song = tmp_song_data
|
current_song_coords = tmp_song_data
|
||||||
break
|
continue
|
||||||
elif closest_song is None or distance < closest_song[1]:
|
|
||||||
closest_song = (tmp_song_data, distance)
|
|
||||||
# If no song found, take the closest one
|
# If no song found, take the closest one
|
||||||
|
else:
|
||||||
logging.info("No close enough song found. Using %s. Distance is %f." %
|
logging.info("No close enough song found. Using %s. Distance is %f." %
|
||||||
(closest_song[0]["filename"], closest_song[1]))
|
(closest_song[0]["filename"], closest_song[1]))
|
||||||
current_song = closest_song[0]
|
current_song_coords = closest_song[0]
|
||||||
subprocess.check_call(["mpc", "add", closest_song[0]["filename"]])
|
subprocess.check_call(["mpc", "add", closest_song[0]["filename"]])
|
||||||
|
continue
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user