Phyks (Lucas Verney) 3 years ago
parent
commit
6edc90c287
1 changed files with 63 additions and 103 deletions
  1. 63
    103
      mpd/client.py

+ 63
- 103
mpd/client.py View File

@@ -103,60 +103,63 @@ if "XDG_DATA_HOME" in os.environ:
103 103
 else:
104 104
     _BLISSIFY_DATA_HOME = os.path.expanduser("~/.local/share/blissify")
105 105
 
106
-# Distance between two songs
106
+
107 107
 def distance(x, y):
108
-    distance = math.sqrt(
108
+    """
109
+    Compute the distance between two songs.
110
+
111
+    Params:
112
+        - x: First song dict
113
+        - y: Second song dict
114
+    Returns: The cartesian distance between the two songs.
115
+    """
116
+    return math.sqrt(
109 117
         (x["tempo"] - y["tempo"])**2 +
110 118
         (x["amplitude"] - y["amplitude"])**2 +
111 119
         (x["frequency"] - y["frequency"])**2 +
112
-        (x["attack"] - y["attack"])**2)
113
-    return distance
120
+        (x["attack"] - y["attack"])**2
121
+    )
114 122
 
115
-# Distance between a set and a point
116
-def distance_set(X, y):
117
-    temp_distance = distance(X[0], y);
118
-    for song in X:
119
-        a = distance(song, y); 
120
-        temp_distance = a if a < temp_distance else temp_distance;
121
-    return temp_distance;
122 123
 
123
-# Hausdorff distance between two sets
124
-def hauff_distance_sets(X, Y):
125
-    temp_distance = distance_set(X, Y[0]);
126
-    for song in X:
127
-        a = distance_set(Y, song);
128
-        temp_distance = a if a > temp_distance else temp_distance;
124
+def mean_song(X):
125
+    """
126
+    Compute a "mean" song for a given iterable of song dicts.
129 127
 
130
-    for song in Y:
131
-        a = distance_set(X, song);
132
-        temp_distance = a if a > temp_distance else temp_distance;
133
-    return temp_distance
128
+    Params:
129
+        - X: An iterable of song dicts.
130
+    Returns: A "mean" song, whose features are the mean features of the songs
131
+    in the iterable.
132
+    """
133
+    count = 0
134
+    result = {'tempo': 0, 'amplitude': 0, 'frequency': 0, 'attack': 0}
134 135
 
135
-def mean_song(X):
136
-    count = 0;
137
-    result = { 'tempo': 0, 'amplitude': 0, 'frequency':0, 'attack':0 } 
138
-   
139 136
     for song in X:
140
-        result["tempo"] += song["tempo"];
141
-        result["amplitude"] += song["amplitude"];
142
-        result["frequency"] += song["frequency"];
143
-        result["attack"] += song["attack"];
144
-        count = count + 1;
145
-    result["tempo"] /= count;
146
-    result["amplitude"] /= count;
147
-    result["frequency"] /= count;
148
-    result["attack"] /= count;
149
-    return result;
150
-   
151
-
152
-# Custom distance between two sets
137
+        result["tempo"] += song["tempo"]
138
+        result["amplitude"] += song["amplitude"]
139
+        result["frequency"] += song["frequency"]
140
+        result["attack"] += song["attack"]
141
+        count = count + 1
142
+    result["tempo"] /= count
143
+    result["amplitude"] /= count
144
+    result["frequency"] /= count
145
+    result["attack"] /= count
146
+    return result
147
+
148
+
153 149
 def distance_sets(X, Y):
154
-    a = mean_song(X);
155
-    b = mean_song(Y);
150
+    """
151
+    Compute the distance between two iterables of song dicts, defined as the
152
+    distance between the two mean songs of the iterables.
156 153
 
157
-    return distance(a, b);
154
+    Params:
155
+        - X: First iterable of song dicts.
156
+        - Y: First iterable of song dicts.
157
+    Returns: The distance between the two iterables.
158
+    """
159
+    return distance(mean_song(X), mean_song(Y))
158 160
 
159
-def main_album(queue_length):
161
+
162
+def _init():
160 163
     # Get MPD connection settings
161 164
     try:
162 165
         mpd_host = os.environ["MPD_HOST"]
@@ -199,6 +202,7 @@ def main_album(queue_length):
199 202
         all_songs = [x["file"] for x in client.listall() if "file" in x]
200 203
         current_song = random.choice(all_songs)
201 204
         client.add(current_song)
205
+
202 206
     logging.info("Currently played song is %s." % (current_song,))
203 207
 
204 208
     # Get current song coordinates
@@ -211,17 +215,23 @@ def main_album(queue_length):
211 215
         client.disconnect()
212 216
         sys.exit(1)
213 217
 
218
+    return client, conn, cur, current_song_coords
219
+
220
+
221
+def main_album(queue_length):
222
+    client, conn, cur, current_song_coords = _init()
223
+
214 224
     for i in range(queue_length):
215 225
         # No cache management
216 226
         # Get all songs from the current album
217
-        album = current_song_coords["album"];
227
+        album = current_song_coords["album"]
218 228
         cur.execute("SELECT id, tempo, amplitude, frequency, attack, filename, album FROM songs WHERE album=?", (album,))
219
-        target_album_set = cur.fetchall();
229
+        target_album_set = cur.fetchall()
220 230
 
221 231
         # Get all other songs
222 232
         cur.execute("SELECT id, tempo, amplitude, frequency, attack, filename, album FROM songs ORDER BY album")
223 233
         tmp_song_data = cur.fetchone()
224
-        shortest_distance = -1 
234
+        shortest_distance = -1
225 235
 
226 236
         # Check the best suitable album
227 237
         while tmp_song_data:
@@ -233,18 +243,18 @@ def main_album(queue_length):
233 243
             # Get all songs from the current temporary album
234 244
             while tmp_song_data:
235 245
                 if (current_album_set[i]["album"] == tmp_song_data["album"]):
236
-                    current_album_set.append(tmp_song_data);
246
+                    current_album_set.append(tmp_song_data)
237 247
                 else:
238
-                    break;
248
+                    break
239 249
                 tmp_song_data = cur.fetchone()
240 250
                 i = i + 1
241
-            # Skip current album and already processed albums    
242
-            if ( (current_album_set[0]["album"] != target_album_set[0]["album"]) and
243
-                not (("file: %s" % (current_album_set[0]["filename"],)) in client.playlist()) ):
251
+            # Skip current album and already processed albums
252
+            if((current_album_set[0]["album"] != target_album_set[0]["album"]) and
253
+               not (("file: %s" % (current_album_set[0]["filename"],)) in client.playlist())):
244 254
                 tmp_distance = distance_sets(current_album_set, target_album_set)
245 255
                 if tmp_distance < shortest_distance or shortest_distance == -1:
246 256
                     shortest_distance = tmp_distance
247
-                    closest_album = current_album_set;
257
+                    closest_album = current_album_set
248 258
 
249 259
         logging.info("Closest album found is \"%s\". Distance is %f." % (closest_album[0]["album"], shortest_distance))
250 260
         for song in closest_album:
@@ -255,59 +265,9 @@ def main_album(queue_length):
255 265
     client.close()
256 266
     client.disconnect()
257 267
 
258
-def main_single(queue_length):
259
-    # Get MPD connection settings
260
-    try:
261
-        mpd_host = os.environ["MPD_HOST"]
262
-        try:
263
-            mpd_password, mpd_host = mpd_host.split("@")
264
-        except ValueError:
265
-            mpd_password = None
266
-    except KeyError:
267
-        mpd_host = "localhost"
268
-        mpd_password = None
269
-    try:
270
-        mpd_port = os.environ["MPD_PORT"]
271
-    except KeyError:
272
-        mpd_port = 6600
273 268
 
274
-    # Connect to MPD
275
-    client = PersistentMPDClient(host=mpd_host, port=mpd_port)
276
-    if mpd_password is not None:
277
-        client.password(mpd_password)
278
-    # Connect to db
279
-    db_path = os.path.join(_BLISSIFY_DATA_HOME, "db.sqlite3")
280
-    logging.debug("Using DB path: %s." % (db_path,))
281
-    conn = sqlite3.connect(db_path)
282
-    conn.row_factory = sqlite3.Row
283
-    conn.execute('pragma foreign_keys=ON')
284
-    cur = conn.cursor()
285
-
286
-    # Ensure random is not enabled
287
-    status = client.status()
288
-    if int(status["random"]) != 0:
289
-        logging.warning("Random mode is enabled. Are you sure you want it?")
290
-
291
-	# Take the last song from current playlist and iterate from it
292
-    playlist = client.playlist()
293
-    if len(playlist) > 0:
294
-        current_song = playlist[-1].replace("file: ", "").rstrip()
295
-    # If current playlist is empty
296
-    else:
297
-        # Add a random song to start with
298
-        all_songs = [x["file"] for x in client.listall() if "file" in x]
299
-        current_song = random.choice(all_songs)
300
-        client.add(current_song)
301
-    logging.info("Currently played song is %s." % (current_song,))
302
-    # Get current song coordinates
303
-    cur.execute("SELECT id, tempo, amplitude, frequency, attack, filename FROM songs WHERE filename=?", (current_song,))
304
-    current_song_coords = cur.fetchone()
305
-    if current_song_coords is None:
306
-        logging.error("Current song %s is not in db. You should update the db." %
307
-                      (current_song,))
308
-        client.close()
309
-        client.disconnect()
310
-        sys.exit(1)
269
+def main_single(queue_length):
270
+    client, conn, cur, current_song_coords = _init()
311 271
 
312 272
     for i in range(queue_length):
313 273
         # Get cached distances from db