Large refactor
* Fix strings overflows and associated segfaults * Add some client code in Python
This commit is contained in:
parent
54cf7074e8
commit
d5a1855b1d
10
README.md
10
README.md
@ -57,3 +57,13 @@ database and `mpdbliss` database in sync.
|
|||||||
|
|
||||||
Check the `client.sh` script for an example client script to build smooth MPD
|
Check the `client.sh` script for an example client script to build smooth MPD
|
||||||
playlists.
|
playlists.
|
||||||
|
out any flag, `mpdbliss` will listen for MPD IDLE protocol, and trigger
|
||||||
|
an update of the database whenever the MPD database is modified.
|
||||||
|
|
||||||
|
Typical usage would be to run a `--rescan` first, and then either do periodic
|
||||||
|
`--update` or let it run listening at MPD IDLE protocol to maintain MPD
|
||||||
|
database and `mpdbliss` database in sync.
|
||||||
|
|
||||||
|
|
||||||
|
Check the `client/client.py` script for an example client script to build smooth MPD
|
||||||
|
playlists.
|
||||||
|
24
client.sh
24
client.sh
@ -1,24 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
QUEUE_LENGTH=100
|
|
||||||
|
|
||||||
if [ -z "$XDG_DATA_HOME" ]; then
|
|
||||||
mpdbliss_data_home="$HOME/.local/share/mpdbliss"
|
|
||||||
else
|
|
||||||
mpdbliss_data_home="$XDG_DATA_HOME/mpdbliss"
|
|
||||||
fi
|
|
||||||
|
|
||||||
current_song=`mpc current --format "%file%"`
|
|
||||||
current_song="bad/_Compilations/8 Mile_ Music From and Inspired by the Motion Picture/01 - Lose Yourself.mp3"
|
|
||||||
for i in {1..$QUEUE_LENGTH}; do
|
|
||||||
# Find closest song
|
|
||||||
closest_song=`sqlite3 "$mpdbliss_data_home/db.sqlite3" "SELECT filename FROM (SELECT 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='$current_song' UNION SELECT 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=\"$current_song\") ORDER BY distance ASC LIMIT 1"`
|
|
||||||
if [ ! -z "$closest_song" ]; then
|
|
||||||
# Push it on the queue
|
|
||||||
mpc add "$closest_song" 2>&1 > /dev/null
|
|
||||||
# Continue using latest pushed song as current song
|
|
||||||
current_song="$closest_song"
|
|
||||||
# Note: if song could not be found by mpd, it is just not added to the
|
|
||||||
# queue and skipped
|
|
||||||
fi
|
|
||||||
done
|
|
73
client/build_cache.py
Normal file
73
client/build_cache.py
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import logging
|
||||||
|
import math
|
||||||
|
import os
|
||||||
|
import sqlite3
|
||||||
|
|
||||||
|
logging.basicConfig(level=logging.INFO)
|
||||||
|
|
||||||
|
if "XDG_DATA_HOME" in os.environ:
|
||||||
|
_MPDBLISS_DATA_HOME = os.path.expandvars("$XDG_DATA_HOME/mpdbliss")
|
||||||
|
else:
|
||||||
|
_MPDBLISS_DATA_HOME = os.path.expanduser("~/.local/share/mpdbliss")
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
db_path = os.path.join(_MPDBLISS_DATA_HOME, "db.sqlite3")
|
||||||
|
logging.debug("Using DB path: %s." % (db_path,))
|
||||||
|
conn = sqlite3.connect(db_path)
|
||||||
|
conn.row_factory = sqlite3.Row
|
||||||
|
conn.execute('pragma foreign_keys=ON')
|
||||||
|
cur = conn.cursor()
|
||||||
|
|
||||||
|
# Get cached distances from db
|
||||||
|
cur.execute("SELECT song1, song2, distance FROM distances")
|
||||||
|
cached_distances = cur.fetchall()
|
||||||
|
|
||||||
|
# Get all songs
|
||||||
|
cur.execute("SELECT id, tempo, amplitude, frequency, attack, filename FROM songs")
|
||||||
|
all_songs = cur.fetchall()
|
||||||
|
|
||||||
|
for i in range(len(all_songs)):
|
||||||
|
for j in range(i + 1, len(all_songs)):
|
||||||
|
song1 = all_songs[i]
|
||||||
|
song2 = all_songs[j]
|
||||||
|
is_cached = len([i for i in cached_distances
|
||||||
|
if(i["song1"] == song1["id"] and
|
||||||
|
i["song2"] == song2["id"]) or
|
||||||
|
(i["song1"] == song2["id"] and
|
||||||
|
i["song2"] == song1["id"])]) > 0
|
||||||
|
if is_cached:
|
||||||
|
# Pass pair if cached value is already there
|
||||||
|
continue
|
||||||
|
# Compute distance
|
||||||
|
distance = math.sqrt(
|
||||||
|
(song1["tempo"] - song2["tempo"])**2 +
|
||||||
|
(song1["amplitude"] - song2["amplitude"])**2 +
|
||||||
|
(song1["frequency"] - song2["frequency"])**2 +
|
||||||
|
(song1["attack"] - song2["attack"])**2
|
||||||
|
)
|
||||||
|
logging.debug("Distance between %s and %s is %f." %
|
||||||
|
(song1["filename"], song2["filename"], distance))
|
||||||
|
# Store distance in db cache
|
||||||
|
try:
|
||||||
|
logging.debug("Storing distance in database.")
|
||||||
|
conn.execute(
|
||||||
|
"INSERT INTO distances(song1, song2, distance) VALUES(?, ?, ?)",
|
||||||
|
(song1["id"], song2["id"], distance))
|
||||||
|
conn.commit()
|
||||||
|
# Update cached_distances list
|
||||||
|
cached_distances.append({
|
||||||
|
"song1": song1["id"],
|
||||||
|
"song2": song2["id"],
|
||||||
|
"distance": distance
|
||||||
|
})
|
||||||
|
except sqlite3.IntegrityError:
|
||||||
|
logging.warning("Unable to insert distance in database.")
|
||||||
|
conn.rollback()
|
||||||
|
# Close connection
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
121
client/client.py
Normal file
121
client/client.py
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import logging
|
||||||
|
import math
|
||||||
|
import os
|
||||||
|
import sqlite3
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
|
||||||
|
logging.basicConfig(level=logging.INFO)
|
||||||
|
|
||||||
|
_QUEUE_LENGTH = 100
|
||||||
|
# TODO: Use cosine similarity as well
|
||||||
|
_DISTANCE_THRESHOLD = 4.0
|
||||||
|
|
||||||
|
if "XDG_DATA_HOME" in os.environ:
|
||||||
|
_MPDBLISS_DATA_HOME = os.path.expandvars("$XDG_DATA_HOME/mpdbliss")
|
||||||
|
else:
|
||||||
|
_MPDBLISS_DATA_HOME = os.path.expanduser("~/.local/share/mpdbliss")
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
mpd_queue = []
|
||||||
|
db_path = os.path.join(_MPDBLISS_DATA_HOME, "db.sqlite3")
|
||||||
|
logging.debug("Using DB path: %s." % (db_path,))
|
||||||
|
conn = sqlite3.connect(db_path)
|
||||||
|
conn.row_factory = sqlite3.Row
|
||||||
|
conn.execute('pragma foreign_keys=ON')
|
||||||
|
cur = conn.cursor()
|
||||||
|
|
||||||
|
current_song = subprocess.check_output(
|
||||||
|
["mpc", "current", '--format', '"%file%"'])
|
||||||
|
current_song = current_song.decode("utf-8").strip('" \r\n\t')
|
||||||
|
if current_song is "":
|
||||||
|
logging.warning("Currently played song could not be found.")
|
||||||
|
sys.exit(1)
|
||||||
|
logging.info("Currently played song is %s." % (current_song,))
|
||||||
|
|
||||||
|
# Get current song coordinates
|
||||||
|
cur.execute("SELECT id, tempo, amplitude, frequency, attack, filename FROM songs WHERE filename=?", (current_song,))
|
||||||
|
current_song = cur.fetchone()
|
||||||
|
if current_song is None:
|
||||||
|
logging.warning("Current song %s is not in db. You should update the db." %
|
||||||
|
(current_song["filename"],))
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
for i in range(_QUEUE_LENGTH):
|
||||||
|
# Append current song to the mpd queue to avoid duplicates
|
||||||
|
mpd_queue.append(current_song["filename"])
|
||||||
|
# Get cached distances from db
|
||||||
|
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",
|
||||||
|
(current_song["filename"], current_song["filename"]))
|
||||||
|
cached_distances = [row
|
||||||
|
for row in cur.fetchall()
|
||||||
|
if row["filename"] not in mpd_queue]
|
||||||
|
cached_distances_songs = [i["filename"] for i in cached_distances]
|
||||||
|
|
||||||
|
# If distance to closest song is ok, just add the song
|
||||||
|
if len(cached_distances) > 0:
|
||||||
|
if cached_distances[0]["distance"] < _DISTANCE_THRESHOLD:
|
||||||
|
# Push it on the queue
|
||||||
|
subprocess.check_call(["mpc", "add",
|
||||||
|
cached_distances[0]["filename"]])
|
||||||
|
# Continue using latest pushed song as current song
|
||||||
|
logging.info("Using cached distance. Found %s. Distance is %f." %
|
||||||
|
(current_song["filename"], cached_distances[0]["distance"]))
|
||||||
|
current_song = cached_distances[0]
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Get all other songs coordinates
|
||||||
|
closest_song = None
|
||||||
|
cur.execute("SELECT id, tempo, amplitude, frequency, attack, filename FROM songs")
|
||||||
|
for tmp_song_data in cur.fetchall():
|
||||||
|
if(tmp_song_data["filename"] == current_song["filename"] or
|
||||||
|
tmp_song_data["filename"] in cached_distances_songs or
|
||||||
|
tmp_song_data["filename"] in mpd_queue):
|
||||||
|
# Skip current song and already processed songs
|
||||||
|
logging.debug("Skipping %s." % (tmp_song_data["filename"]))
|
||||||
|
continue
|
||||||
|
# Compute distance
|
||||||
|
distance = math.sqrt(
|
||||||
|
(current_song["tempo"] - tmp_song_data["tempo"])**2 +
|
||||||
|
(current_song["amplitude"] - tmp_song_data["amplitude"])**2 +
|
||||||
|
(current_song["frequency"] - tmp_song_data["frequency"])**2 +
|
||||||
|
(current_song["attack"] - tmp_song_data["attack"])**2
|
||||||
|
)
|
||||||
|
logging.debug("Distance between %s and %s is %f." %
|
||||||
|
(current_song["filename"],
|
||||||
|
tmp_song_data["filename"], distance))
|
||||||
|
# Store distance in db cache
|
||||||
|
try:
|
||||||
|
logging.debug("Storing distance in database.")
|
||||||
|
conn.execute(
|
||||||
|
"INSERT INTO distances(song1, song2, distance) VALUES(?, ?, ?)",
|
||||||
|
(current_song["id"], tmp_song_data["id"], distance))
|
||||||
|
conn.commit()
|
||||||
|
except sqlite3.IntegrityError:
|
||||||
|
logging.warning("Unable to insert distance in database.")
|
||||||
|
conn.rollback()
|
||||||
|
|
||||||
|
# If distance is ok, just add the song
|
||||||
|
if distance < _DISTANCE_THRESHOLD:
|
||||||
|
# Push it on the queue
|
||||||
|
subprocess.check_call(["mpc", "add", tmp_song_data["filename"]])
|
||||||
|
# Continue using latest pushed song as current song
|
||||||
|
logging.info("Found a close song: %s. Distance is %f." %
|
||||||
|
(tmp_song_data["filename"], distance))
|
||||||
|
current_song = tmp_song_data
|
||||||
|
break
|
||||||
|
elif closest_song is None or distance < closest_song[1]:
|
||||||
|
closest_song = (tmp_song_data, distance)
|
||||||
|
# If no song found, take the closest one
|
||||||
|
logging.info("No close enough song found. Using %s. Distance is %f." %
|
||||||
|
(closest_song[0]["filename"], closest_song[1]))
|
||||||
|
current_song = closest_song[0]
|
||||||
|
subprocess.check_call(["mpc", "add", closest_song[0]["filename"]])
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
6
include/constants.h
Normal file
6
include/constants.h
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#ifndef CONSTANTS_H
|
||||||
|
#define CONSTANTS_H
|
||||||
|
|
||||||
|
#define DEFAULT_STRING_LENGTH 10000
|
||||||
|
|
||||||
|
#endif // CONSTANTS_H
|
@ -1,8 +1,6 @@
|
|||||||
#ifndef UTILITIES_H
|
#ifndef UTILITIES_H
|
||||||
#define UTILITIES_H
|
#define UTILITIES_H
|
||||||
|
|
||||||
#define DEFAULT_STRING_LENGTH 1024
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Strip the trailing slash from a string.
|
* Strip the trailing slash from a string.
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
#include <sqlite3.h>
|
#include <sqlite3.h>
|
||||||
|
|
||||||
#include "analysis.h"
|
#include "analysis.h"
|
||||||
|
#include "constants.h"
|
||||||
#include "utilities.h"
|
#include "utilities.h"
|
||||||
|
|
||||||
// TODO: Handle deletions from db
|
// TODO: Handle deletions from db
|
||||||
@ -17,8 +18,8 @@ int main(int argc, char** argv) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get data directory, init db file
|
// Get data directory, init db file
|
||||||
char mpdbliss_data_folder[DEFAULT_STRING_LENGTH] = "";
|
char mpdbliss_data_folder[DEFAULT_STRING_LENGTH + 1] = "";
|
||||||
char mpdbliss_data_db[DEFAULT_STRING_LENGTH] = "";
|
char mpdbliss_data_db[DEFAULT_STRING_LENGTH + 1] = "";
|
||||||
if (0 != _init_db(mpdbliss_data_folder, mpdbliss_data_db)) {
|
if (0 != _init_db(mpdbliss_data_folder, mpdbliss_data_db)) {
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
#include "analysis.h"
|
#include "analysis.h"
|
||||||
#include "cmdline.h"
|
#include "cmdline.h"
|
||||||
|
#include "constants.h"
|
||||||
#include "utilities.h"
|
#include "utilities.h"
|
||||||
|
|
||||||
// TODO: Handle deletions from db
|
// TODO: Handle deletions from db
|
||||||
@ -186,20 +187,20 @@ int main(int argc, char** argv) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Handle mpd_root argument
|
// Handle mpd_root argument
|
||||||
char mpd_base_path[DEFAULT_STRING_LENGTH] = "";
|
char mpd_base_path[DEFAULT_STRING_LENGTH + 1] = "";
|
||||||
strncat(mpd_base_path, args_info.mpd_root_arg, DEFAULT_STRING_LENGTH);
|
strncat(mpd_base_path, args_info.mpd_root_arg, DEFAULT_STRING_LENGTH);
|
||||||
strip_trailing_slash(mpd_base_path);
|
strip_trailing_slash(mpd_base_path);
|
||||||
strncat(mpd_base_path, "/", DEFAULT_STRING_LENGTH - strlen(mpd_base_path));
|
strncat(mpd_base_path, "/", DEFAULT_STRING_LENGTH - strlen(mpd_base_path));
|
||||||
|
|
||||||
// Get data directory, init db file
|
// Get data directory, init db file
|
||||||
char mpdbliss_data_folder[DEFAULT_STRING_LENGTH] = "";
|
char mpdbliss_data_folder[DEFAULT_STRING_LENGTH + 1] = "";
|
||||||
char mpdbliss_data_db[DEFAULT_STRING_LENGTH] = "";
|
char mpdbliss_data_db[DEFAULT_STRING_LENGTH + 1] = "";
|
||||||
if (0 != _init_db(mpdbliss_data_folder, mpdbliss_data_db)) {
|
if (0 != _init_db(mpdbliss_data_folder, mpdbliss_data_db)) {
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set data file path
|
// Set data file path
|
||||||
char mpdbliss_data_file[DEFAULT_STRING_LENGTH] = "";
|
char mpdbliss_data_file[DEFAULT_STRING_LENGTH + 1] = "";
|
||||||
strncat(mpdbliss_data_file, mpdbliss_data_folder, DEFAULT_STRING_LENGTH);
|
strncat(mpdbliss_data_file, mpdbliss_data_folder, DEFAULT_STRING_LENGTH);
|
||||||
strncat(mpdbliss_data_file, "/latest_mtime.txt", DEFAULT_STRING_LENGTH - strlen(mpdbliss_data_file));
|
strncat(mpdbliss_data_file, "/latest_mtime.txt", DEFAULT_STRING_LENGTH - strlen(mpdbliss_data_file));
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
#include <bliss.h>
|
#include <bliss.h>
|
||||||
|
|
||||||
|
#include "constants.h"
|
||||||
#include "utilities.h"
|
#include "utilities.h"
|
||||||
|
|
||||||
|
|
||||||
@ -21,7 +22,7 @@ int _init_db(char *data_folder, char* db_path)
|
|||||||
strncat(data_folder, "/.local/share/mpdbliss", DEFAULT_STRING_LENGTH - strlen(data_folder));
|
strncat(data_folder, "/.local/share/mpdbliss", DEFAULT_STRING_LENGTH - strlen(data_folder));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
strncpy(data_folder, xdg_data_home_env, DEFAULT_STRING_LENGTH);
|
strncat(data_folder, xdg_data_home_env, DEFAULT_STRING_LENGTH);
|
||||||
strip_trailing_slash(data_folder);
|
strip_trailing_slash(data_folder);
|
||||||
strncat(data_folder, "/mpdbliss", DEFAULT_STRING_LENGTH - strlen(data_folder));
|
strncat(data_folder, "/mpdbliss", DEFAULT_STRING_LENGTH - strlen(data_folder));
|
||||||
}
|
}
|
||||||
@ -92,9 +93,9 @@ int _parse_music_helper(
|
|||||||
|
|
||||||
// Compute full uri
|
// Compute full uri
|
||||||
printf("\nAdding new song to db: %s\n", song_uri);
|
printf("\nAdding new song to db: %s\n", song_uri);
|
||||||
char song_full_uri[DEFAULT_STRING_LENGTH] = "";
|
char song_full_uri[DEFAULT_STRING_LENGTH + 1] = "";
|
||||||
strncat(song_full_uri, base_path, DEFAULT_STRING_LENGTH);
|
strncat(song_full_uri, base_path, DEFAULT_STRING_LENGTH);
|
||||||
strncat(song_full_uri, song_uri, DEFAULT_STRING_LENGTH);
|
strncat(song_full_uri, song_uri, DEFAULT_STRING_LENGTH - strlen(song_full_uri));
|
||||||
|
|
||||||
// Pass it to bliss
|
// Pass it to bliss
|
||||||
struct bl_song song_analysis;
|
struct bl_song song_analysis;
|
||||||
@ -156,54 +157,7 @@ int _parse_music_helper(
|
|||||||
sqlite3_bind_double(res, 4, song_analysis.force_vector.attack);
|
sqlite3_bind_double(res, 4, song_analysis.force_vector.attack);
|
||||||
sqlite3_bind_text(res, 5, song_uri, strlen(song_uri), SQLITE_STATIC);
|
sqlite3_bind_text(res, 5, song_uri, strlen(song_uri), SQLITE_STATIC);
|
||||||
sqlite3_step(res);
|
sqlite3_step(res);
|
||||||
sqlite3_finalize(res);
|
|
||||||
int last_id = sqlite3_last_insert_rowid(dbh);
|
|
||||||
// Insert updated distances
|
|
||||||
dberr = sqlite3_prepare_v2(dbh, "SELECT id, tempo, amplitude, frequency, attack FROM songs", -1, &res, 0);
|
|
||||||
if (SQLITE_OK != dberr) {
|
if (SQLITE_OK != dberr) {
|
||||||
fprintf(stderr, "Error while inserting data in db: %s\n\n", sqlite3_errmsg(dbh));
|
|
||||||
// Free song analysis
|
|
||||||
bl_free_song(&song_analysis);
|
|
||||||
sqlite3_exec(dbh, "ROLLBACK", NULL, NULL, NULL);
|
|
||||||
// Store error in db
|
|
||||||
sqlite3_prepare_v2(dbh,
|
|
||||||
"INSERT INTO errors(filename) VALUES(?)",
|
|
||||||
-1, &res, 0);
|
|
||||||
sqlite3_bind_text(res, 1, song_uri, strlen(song_uri), SQLITE_STATIC);
|
|
||||||
sqlite3_step(res);
|
|
||||||
sqlite3_finalize(res);
|
|
||||||
// Pass file
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
int dberr2 = SQLITE_OK;
|
|
||||||
while (sqlite3_step(res) == SQLITE_ROW) {
|
|
||||||
int id = sqlite3_column_int(res, 0);
|
|
||||||
if (id == last_id) {
|
|
||||||
// Skip last inserted item
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
struct force_vector_s song_db;
|
|
||||||
song_db.tempo = sqlite3_column_double(res, 1);
|
|
||||||
song_db.amplitude = sqlite3_column_double(res, 2);
|
|
||||||
song_db.frequency = sqlite3_column_double(res, 3);
|
|
||||||
song_db.attack = sqlite3_column_double(res, 4);
|
|
||||||
float distance = bl_distance(song_analysis.force_vector, song_db);
|
|
||||||
|
|
||||||
sqlite3_stmt *res2;
|
|
||||||
dberr2 = sqlite3_prepare_v2(dbh,
|
|
||||||
"INSERT INTO distances(song1, song2, distance) VALUES(?, ?, ?)",
|
|
||||||
-1, &res2, 0);
|
|
||||||
if (SQLITE_OK != dberr2) {
|
|
||||||
fprintf(stderr, "Error while inserting data in db: %s\n\n", sqlite3_errmsg(dbh));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
sqlite3_bind_int(res2, 1, last_id);
|
|
||||||
sqlite3_bind_int(res2, 2, id);
|
|
||||||
sqlite3_bind_double(res2, 3, distance);
|
|
||||||
sqlite3_step(res2);
|
|
||||||
sqlite3_finalize(res2);
|
|
||||||
}
|
|
||||||
if (SQLITE_OK != dberr2) {
|
|
||||||
// Free song analysis
|
// Free song analysis
|
||||||
bl_free_song(&song_analysis);
|
bl_free_song(&song_analysis);
|
||||||
sqlite3_exec(dbh, "ROLLBACK", NULL, NULL, NULL);
|
sqlite3_exec(dbh, "ROLLBACK", NULL, NULL, NULL);
|
||||||
|
Loading…
Reference in New Issue
Block a user