2021-12-24 06:01:21 -03:00
//
// SQLiteStore . m
// Cog
//
// Created by Christopher Snowhill on 12 / 22 / 21.
//
2022-02-23 04:10:02 -03:00
# import < Foundation / Foundation . h >
2021-12-24 06:01:21 -03:00
# import "SQLiteStore.h"
2022-01-22 03:38:54 -03:00
# import "Logging.h"
2022-02-23 04:10:02 -03:00
# import "SHA256Digest.h"
2021-12-24 06:01:21 -03:00
2022-02-07 02:49:27 -03:00
NSString * getDatabasePath ( void ) {
NSArray * paths = NSSearchPathForDirectoriesInDomains ( NSApplicationSupportDirectory , NSUserDomainMask , YES ) ;
NSString * basePath = [ [ paths firstObject ] stringByAppendingPathComponent : @ "Cog" ] ;
NSString * filename = @ "Default.sqlite" ;
return [ basePath stringByAppendingPathComponent : filename ] ;
2021-12-24 06:01:21 -03:00
}
2022-02-23 04:10:02 -03:00
static int64_t currentSchemaVersion = 3 ;
2022-01-22 03:38:54 -03:00
2022-02-07 02:49:27 -03:00
NSArray * createSchema ( void ) {
return @ [
@ " CREATE TABLE IF NOT EXISTS stringdictionary ( \
2021-12-24 06:01:21 -03:00
stringid INTEGER PRIMARY KEY AUTOINCREMENT , \
referencecount INTEGER , \
value TEXT NOT NULL \
) ; " ,
2022-02-07 02:49:27 -03:00
@ " CREATE TABLE IF NOT EXISTS artdictionary ( \
2021-12-24 06:01:21 -03:00
artid INTEGER PRIMARY KEY AUTOINCREMENT , \
2022-02-23 04:10:02 -03:00
arthash BLOB NOT NULL , \
2021-12-24 06:01:21 -03:00
referencecount INTEGER , \
value BLOB NOT NULL \
) ; " ,
2022-02-23 04:10:02 -03:00
@ "CREATE UNIQUE INDEX idx_art_hash ON artdictionary (arthash);" ,
2022-02-07 02:49:27 -03:00
@ " CREATE TABLE IF NOT EXISTS knowntracks ( \
2021-12-24 06:01:21 -03:00
trackid INTEGER PRIMARY KEY AUTOINCREMENT , \
referencecount INTEGER , \
urlid INTEGER , \
artid INTEGER , \
albumid INTEGER , \
albumartistid INTEGER , \
artistid INTEGER , \
titleid INTEGER , \
genreid INTEGER , \
codecid INTEGER , \
2022-01-22 03:38:54 -03:00
encodingid INTEGER , \
cuesheetid INTEGER , \
2021-12-24 06:01:21 -03:00
track INTEGER , \
year INTEGER , \
unsigned INTEGER , \
bitrate INTEGER , \
samplerate REAL , \
bitspersample INTEGER , \
channels INTEGER , \
2022-02-07 06:11:30 -03:00
channelconfig INTEGER , \
2021-12-24 06:01:21 -03:00
endianid INTEGER , \
floatingpoint INTEGER , \
totalframes INTEGER , \
metadataloaded INTEGER , \
seekable INTEGER , \
volume REAL , \
replaygainalbumgain REAL , \
replaygainalbumpeak REAL , \
replaygaintrackgain REAL , \
replaygaintrackpeak REAL \
) ; " ,
2022-02-07 02:49:27 -03:00
@ " CREATE TABLE IF NOT EXISTS playlist ( \
2021-12-24 06:01:21 -03:00
entryid INTEGER PRIMARY KEY AUTOINCREMENT , \
entryindex INTEGER , \
trackid INTEGER \
) ; " ,
2022-02-07 02:49:27 -03:00
@ " CREATE TABLE IF NOT EXISTS queue ( \
2021-12-24 06:01:21 -03:00
queueid INTEGER PRIMARY KEY AUTOINCREMENT , \
queueindex INTEGER , \
entryid INTEGER \
2022-01-18 23:12:57 -03:00
) ; "
2022-02-07 02:49:27 -03:00
] ;
2021-12-24 06:01:21 -03:00
}
2022-02-07 02:49:27 -03:00
enum {
stmt_user _version _get = 0 ,
stmt_select _string ,
stmt_select _string _refcount ,
stmt_select _string _value ,
stmt_bump _string ,
stmt_pop _string ,
stmt_add _string ,
stmt_remove _string ,
stmt_select _art ,
2022-02-23 04:10:02 -03:00
stmt_select _art _all ,
2022-02-07 02:49:27 -03:00
stmt_select _art _refcount ,
stmt_select _art _value ,
stmt_bump _art ,
stmt_pop _art ,
stmt_add _art ,
stmt_remove _art ,
2022-02-23 04:10:02 -03:00
stmt_add _art _renamed ,
2022-02-07 02:49:27 -03:00
stmt_select _track ,
stmt_select _track _refcount ,
stmt_select _track _data ,
stmt_bump _track ,
stmt_pop _track ,
stmt_add _track ,
stmt_remove _track ,
stmt_update _track ,
stmt_select _playlist ,
stmt_select _playlist _range ,
stmt_select _playlist _all ,
stmt_increment _playlist _for _insert ,
stmt_decrement _playlist _for _removal ,
stmt_add _playlist ,
stmt_remove _playlist _by _range ,
stmt_count _playlist ,
stmt_update _playlist ,
stmt_select _queue ,
stmt_select _queue _by _playlist _entry ,
stmt_decrement _queue _for _removal ,
stmt_add _queue ,
stmt_remove _queue _by _index ,
stmt_remove _queue _all ,
stmt_count _queue ,
stmt_count ,
2021-12-24 06:01:21 -03:00
} ;
2022-02-07 02:49:27 -03:00
enum {
user_version _get _out _version _number = 0 ,
2022-01-22 03:38:54 -03:00
} ;
2022-02-07 02:49:27 -03:00
const char * query_user _version _get = "PRAGMA user_version" ;
2022-01-22 03:38:54 -03:00
2022-02-07 02:49:27 -03:00
enum {
select_string _in _id = 1 ,
2022-01-22 03:38:54 -03:00
2022-02-07 02:49:27 -03:00
select_string _out _string _id = 0 ,
select_string _out _reference _count ,
2021-12-24 06:01:21 -03:00
} ;
2022-02-07 02:49:27 -03:00
const char * query_select _string = "SELECT stringid, referencecount FROM stringdictionary WHERE (value = ?) LIMIT 1" ;
2021-12-24 06:01:21 -03:00
2022-02-07 02:49:27 -03:00
enum {
select_string _refcount _in _id = 1 ,
select_string _refcount _out _string _id = 0 ,
2021-12-24 06:01:21 -03:00
} ;
2022-02-07 02:49:27 -03:00
const char * query_select _string _refcount = "SELECT referencecount FROM stringdictionary WHERE (stringid = ?) LIMIT 1" ;
2021-12-24 06:01:21 -03:00
2022-02-07 02:49:27 -03:00
enum {
select_string _value _in _id = 1 ,
select_string _value _out _value = 0 ,
2021-12-24 06:01:21 -03:00
} ;
2022-02-07 02:49:27 -03:00
const char * query_select _string _value = "SELECT value FROM stringdictionary WHERE (stringid = ?) LIMIT 1" ;
2021-12-24 06:01:21 -03:00
2022-02-07 02:49:27 -03:00
enum {
bump_string _in _id = 1 ,
2021-12-24 06:01:21 -03:00
} ;
2022-02-07 02:49:27 -03:00
const char * query_bump _string = "UPDATE stringdictionary SET referencecount = referencecount + 1 WHERE (stringid = ?) LIMIT 1" ;
2021-12-24 06:01:21 -03:00
2022-02-07 02:49:27 -03:00
enum {
pop_string _in _id = 1 ,
2021-12-24 06:01:21 -03:00
} ;
2022-02-07 02:49:27 -03:00
const char * query_pop _string = "UPDATE stringdictionary SET referencecount = referencecount - 1 WHERE (stringid = ?) LIMIT 1" ;
2021-12-24 06:01:21 -03:00
2022-02-07 02:49:27 -03:00
enum {
add_string _in _value = 1 ,
2021-12-24 06:01:21 -03:00
} ;
2022-02-07 02:49:27 -03:00
const char * query_add _string = "INSERT INTO stringdictionary (referencecount, value) VALUES (1, ?)" ;
2021-12-24 06:01:21 -03:00
2022-02-07 02:49:27 -03:00
enum {
remove_string _in _id = 1 ,
2021-12-24 06:01:21 -03:00
} ;
2022-02-07 02:49:27 -03:00
const char * query_remove _string = "DELETE FROM stringdictionary WHERE (stringid = ?)" ;
2021-12-24 06:01:21 -03:00
2022-02-07 02:49:27 -03:00
enum {
2022-02-23 04:10:02 -03:00
select_art _in _arthash = 1 ,
2021-12-24 06:01:21 -03:00
2022-02-07 02:49:27 -03:00
select_art _out _art _id = 0 ,
select_art _out _reference _count ,
2021-12-24 06:01:21 -03:00
} ;
2022-02-23 04:10:02 -03:00
const char * query_select _art = "SELECT artid, referencecount FROM artdictionary WHERE (arthash = ?) LIMIT 1" ;
enum {
select_art _all _out _id = 0 ,
select_art _all _out _referencecount ,
select_art _all _out _value ,
} ;
const char * query_select _art _all = "SELECT artid, referencecount, value FROM artdictionary" ;
2021-12-24 06:01:21 -03:00
2022-02-07 02:49:27 -03:00
enum {
select_art _refcount _in _id = 1 ,
select_art _refcount _out _reference _count = 0 ,
2021-12-24 06:01:21 -03:00
} ;
2022-02-07 02:49:27 -03:00
const char * query_select _art _refcount = "SELECT referencecount FROM artdictionary WHERE (artid = ?) LIMIT 1" ;
2021-12-24 06:01:21 -03:00
2022-02-07 02:49:27 -03:00
enum {
select_art _value _in _id = 1 ,
select_art _value _out _value = 0 ,
2021-12-24 06:01:21 -03:00
} ;
2022-02-07 02:49:27 -03:00
const char * query_select _art _value = "SELECT value FROM artdictionary WHERE (artid = ?) LIMIT 1" ;
2021-12-24 06:01:21 -03:00
2022-02-07 02:49:27 -03:00
enum {
bump_art _in _id = 1 ,
2021-12-24 06:01:21 -03:00
} ;
2022-02-07 02:49:27 -03:00
const char * query_bump _art = "UPDATE artdictionary SET referencecount = referencecount + 1 WHERE (artid = ?) LIMIT 1" ;
2021-12-24 06:01:21 -03:00
2022-02-07 02:49:27 -03:00
enum {
pop_art _in _id = 1 ,
2021-12-24 06:01:21 -03:00
} ;
2022-02-07 02:49:27 -03:00
const char * query_pop _art = "UPDATE artdictionary SET referencecount = referencecount - 1 WHERE (artid = ?) LIMIT 1" ;
2021-12-24 06:01:21 -03:00
2022-02-07 02:49:27 -03:00
enum {
2022-02-23 04:10:02 -03:00
add_art _in _hash = 1 ,
add_art _in _value ,
2021-12-24 06:01:21 -03:00
} ;
2022-02-23 04:10:02 -03:00
const char * query_add _art = "INSERT INTO artdictionary (referencecount, arthash, value) VALUES (1, ?, ?)" ;
2021-12-24 06:01:21 -03:00
2022-02-07 02:49:27 -03:00
enum {
remove_art _in _id = 1 ,
2021-12-24 06:01:21 -03:00
} ;
2022-02-07 02:49:27 -03:00
const char * query_remove _art = "DELETE FROM artdictionary WHERE (artid = ?)" ;
2021-12-24 06:01:21 -03:00
2022-02-23 04:10:02 -03:00
enum {
add_art _renamed _in _id = 1 ,
add_art _renamed _in _referencecount ,
add_art _renamed _in _hash ,
add_art _renamed _in _value ,
} ;
const char * query_add _art _renamed = "INSERT INTO artdictionary_v2 (artid, referencecount, arthash, value) VALUES (?, ?, ?, ?)" ;
2022-02-07 02:49:27 -03:00
enum {
select_track _in _id = 1 ,
2021-12-24 06:01:21 -03:00
2022-02-07 02:49:27 -03:00
select_track _out _track _id = 0 ,
select_track _out _reference _count ,
2021-12-24 06:01:21 -03:00
} ;
2022-02-07 02:49:27 -03:00
const char * query_select _track = "SELECT trackid, referencecount FROM knowntracks WHERE (urlid = ?) LIMIT 1" ;
2021-12-24 06:01:21 -03:00
2022-02-07 02:49:27 -03:00
enum {
select_track _refcount _in _id = 1 ,
2021-12-24 06:01:21 -03:00
2022-02-07 02:49:27 -03:00
select_track _refcount _out _reference _count = 0 ,
} ;
2021-12-24 06:01:21 -03:00
2022-02-07 02:49:27 -03:00
const char * query_select _track _refcount = "SELECT referencecount FROM knowntracks WHERE (trackid = ?) LIMIT 1" ;
enum {
select_track _data _in _id = 1 ,
select_track _data _out _url _id = 0 ,
select_track _data _out _art _id ,
select_track _data _out _album _id ,
select_track _data _out _albumartist _id ,
select_track _data _out _artist _id ,
select_track _data _out _title _id ,
select_track _data _out _genre _id ,
select_track _data _out _codec _id ,
select_track _data _out _cuesheet _id ,
select_track _data _out _encoding _id ,
select_track _data _out _track ,
select_track _data _out _year ,
select_track _data _out _unsigned ,
select_track _data _out _bitrate ,
select_track _data _out _samplerate ,
select_track _data _out _bitspersample ,
select_track _data _out _channels ,
2022-02-07 06:11:30 -03:00
select_track _data _out _channelconfig ,
2022-02-07 02:49:27 -03:00
select_track _data _out _endian _id ,
select_track _data _out _floatingpoint ,
select_track _data _out _totalframes ,
select_track _data _out _metadataloaded ,
select_track _data _out _seekable ,
select_track _data _out _volume ,
select_track _data _out _replaygainalbumgain ,
select_track _data _out _replaygainalbumpeak ,
select_track _data _out _replaygaintrackgain ,
select_track _data _out _replaygaintrackpeak ,
2021-12-24 06:01:21 -03:00
} ;
2022-02-07 06:11:30 -03:00
const char * query_select _track _data = "SELECT urlid, artid, albumid, albumartistid, artistid, titleid, genreid, codecid, cuesheetid, encodingid, track, year, unsigned, bitrate, samplerate, bitspersample, channels, channelconfig, endianid, floatingpoint, totalframes, metadataloaded, seekable, volume, replaygainalbumgain, replaygainalbumpeak, replaygaintrackgain, replaygaintrackpeak FROM knowntracks WHERE (trackid = ?) LIMIT 1" ;
2021-12-24 06:01:21 -03:00
2022-02-07 02:49:27 -03:00
enum {
bump_track _in _id = 1 ,
2021-12-24 06:01:21 -03:00
} ;
2022-02-07 02:49:27 -03:00
const char * query_bump _track = "UPDATE knowntracks SET referencecount = referencecount + 1 WHERE (trackid = ?) LIMIT 1" ;
2021-12-24 06:01:21 -03:00
2022-02-07 02:49:27 -03:00
enum {
pop_track _in _id = 1 ,
2021-12-24 06:01:21 -03:00
} ;
2022-02-07 02:49:27 -03:00
const char * query_pop _track = "UPDATE knowntracks SET referencecount = referencecount - 1 WHERE (trackid = ?) LIMIT 1" ;
enum {
add_track _in _url _id = 1 ,
add_track _in _art _id ,
add_track _in _album _id ,
add_track _in _albumartist _id ,
add_track _in _artist _id ,
add_track _in _title _id ,
add_track _in _genre _id ,
add_track _in _codec _id ,
add_track _in _cuesheet _id ,
add_track _in _encoding _id ,
add_track _in _track ,
add_track _in _year ,
add_track _in _unsigned ,
add_track _in _bitrate ,
add_track _in _samplerate ,
add_track _in _bitspersample ,
add_track _in _channels ,
2022-02-07 06:11:30 -03:00
add_track _in _channelconfig ,
2022-02-07 02:49:27 -03:00
add_track _in _endian _id ,
add_track _in _floatingpoint ,
add_track _in _totalframes ,
add_track _in _metadataloaded ,
add_track _in _seekable ,
add_track _in _volume ,
add_track _in _replaygainalbumgain ,
add_track _in _replaygainalbumpeak ,
add_track _in _replaygaintrackgain ,
add_track _in _replaygaintrackpeak ,
2021-12-24 06:01:21 -03:00
} ;
2022-02-07 06:11:30 -03:00
const char * query_add _track = "INSERT INTO knowntracks (referencecount, urlid, artid, albumid, albumartistid, artistid, titleid, genreid, codecid, cuesheetid, encodingid, track, year, unsigned, bitrate, samplerate, bitspersample, channels, channelconfig, endianid, floatingpoint, totalframes, metadataloaded, seekable, volume, replaygainalbumgain, replaygainalbumpeak, replaygaintrackgain, replaygaintrackpeak) VALUES (1, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" ;
2021-12-24 06:01:21 -03:00
2022-02-07 02:49:27 -03:00
enum {
remove_track _in _id = 1 ,
2021-12-24 06:01:21 -03:00
} ;
2022-02-07 02:49:27 -03:00
const char * query_remove _track = "DELETE FROM knowntracks WHERE (trackid = ?)" ;
enum {
update_track _in _url _id = 1 ,
update_track _in _art _id ,
update_track _in _album _id ,
update_track _in _albumartist _id ,
update_track _in _artist _id ,
update_track _in _title _id ,
update_track _in _genre _id ,
update_track _in _codec _id ,
update_track _in _cuesheet _id ,
update_track _in _encoding _id ,
update_track _in _track ,
update_track _in _year ,
update_track _in _unsigned ,
update_track _in _bitrate ,
update_track _in _samplerate ,
update_track _in _bitspersample ,
update_track _in _channels ,
2022-02-07 06:11:30 -03:00
update_track _in _channelconfig ,
2022-02-07 02:49:27 -03:00
update_track _in _endian _id ,
update_track _in _floatingpoint ,
update_track _in _totalframes ,
update_track _in _metadataloaded ,
update_track _in _seekable ,
update_track _in _volume ,
update_track _in _replaygainalbumgain ,
update_track _in _replaygainalbumpeak ,
update_track _in _replaygaintrackgain ,
update_track _in _replaygaintrackpeak ,
update_track _in _id
2021-12-24 06:01:21 -03:00
} ;
2022-02-07 06:11:30 -03:00
const char * query_update _track = "UPDATE knowntracks SET urlid = ?, artid = ?, albumid = ?, albumartistid = ?, artistid = ?, titleid = ?, genreid = ?, codecid = ?, cuesheetid = ?, encodingid = ?, track = ?, year = ?, unsigned = ?, bitrate = ?, samplerate = ?, bitspersample = ?, channels = ?, channelconfig = ?, endianid = ?, floatingpoint = ?, totalframes = ?, metadataloaded = ?, seekable = ?, volume = ?, replaygainalbumgain = ?, replaygainalbumpeak = ?, replaygaintrackgain = ?, replaygaintrackpeak = ? WHERE trackid = ?" ;
2021-12-24 06:01:21 -03:00
2022-02-07 02:49:27 -03:00
enum {
select_playlist _in _id = 1 ,
2021-12-24 06:01:21 -03:00
2022-02-07 02:49:27 -03:00
select_playlist _out _entry _id = 0 ,
select_playlist _out _track _id ,
2021-12-24 06:01:21 -03:00
} ;
2022-02-07 02:49:27 -03:00
const char * query_select _playlist = "SELECT entryid, trackid FROM playlist WHERE (entryindex = ?)" ;
2021-12-24 06:01:21 -03:00
2022-02-07 02:49:27 -03:00
enum {
select_playlist _range _in _id _low = 1 ,
select_playlist _range _in _id _high ,
select_playlist _range _out _entry _id = 0 ,
select_playlist _range _out _track _id ,
2021-12-24 06:01:21 -03:00
} ;
2022-02-07 02:49:27 -03:00
const char * query_select _playlist _range = "SELECT entryid, trackid FROM playlist WHERE (entryindex BETWEEN ? AND ?) ORDER BY entryindex ASC" ;
2021-12-24 06:01:21 -03:00
2022-02-07 02:49:27 -03:00
enum {
select_playlist _all _out _entry _id = 0 ,
select_playlist _all _out _entry _index ,
select_playlist _all _out _track _id ,
2021-12-24 06:01:21 -03:00
} ;
2022-02-07 02:49:27 -03:00
const char * query_select _playlist _all = "SELECT entryid, entryindex, trackid FROM playlist ORDER BY entryindex ASC" ;
2021-12-24 06:01:21 -03:00
2022-02-07 02:49:27 -03:00
enum {
increment_playlist _for _insert _in _count = 1 ,
increment_playlist _for _insert _in _index ,
2021-12-24 06:01:21 -03:00
} ;
2022-02-07 02:49:27 -03:00
const char * query_increment _playlist _for _insert = "UPDATE playlist SET entryindex = entryindex + ? WHERE (entryindex >= ?)" ;
2021-12-24 06:01:21 -03:00
2022-02-07 02:49:27 -03:00
enum {
decrement_playlist _for _removal _in _count = 1 ,
decrement_playlist _for _removal _in _index ,
2021-12-24 06:01:21 -03:00
} ;
2022-02-07 02:49:27 -03:00
const char * query_decrement _playlist _for _removal = "UPDATE playlist SET entryindex = entryindex - ? WHERE (entryindex >= ?)" ;
2021-12-24 06:01:21 -03:00
2022-02-07 02:49:27 -03:00
enum {
add_playlist _in _entry _index = 1 ,
add_playlist _in _track _id ,
2021-12-24 06:01:21 -03:00
} ;
2022-02-07 02:49:27 -03:00
const char * query_add _playlist = "INSERT INTO playlist(entryindex, trackid) VALUES (?, ?)" ;
2021-12-24 06:01:21 -03:00
2022-02-07 02:49:27 -03:00
enum {
remove_playlist _by _range _in _low = 1 ,
remove_playlist _by _range _in _high ,
2021-12-24 06:01:21 -03:00
} ;
2022-02-07 02:49:27 -03:00
const char * query_remove _playlist _by _range = "DELETE FROM playlist WHERE (entryindex BETWEEN ? AND ?)" ;
2021-12-24 06:01:21 -03:00
2022-02-07 02:49:27 -03:00
enum {
count_playlist _out _count = 0 ,
2021-12-24 06:01:21 -03:00
} ;
2022-02-07 02:49:27 -03:00
const char * query_count _playlist = "SELECT COUNT(*) FROM playlist" ;
2021-12-24 06:01:21 -03:00
2022-02-07 02:49:27 -03:00
enum {
update_playlist _in _entry _index = 1 ,
update_playlist _in _track _id ,
update_playlist _in _id
2021-12-24 06:01:21 -03:00
} ;
2022-02-07 02:49:27 -03:00
const char * query_update _playlist = "UPDATE playlist SET entryindex = ?, trackid = ? WHERE (entryid = ?)" ;
2021-12-24 06:01:21 -03:00
2022-02-07 02:49:27 -03:00
enum {
select_queue _in _id = 1 ,
2021-12-24 06:01:21 -03:00
2022-02-07 02:49:27 -03:00
select_queue _out _queue _id = 0 ,
select_queue _out _entry _id ,
2021-12-24 06:01:21 -03:00
} ;
2022-02-07 02:49:27 -03:00
const char * query_select _queue = "SELECT queueid, entryid FROM queue WHERE (queueindex = ?) LIMIT 1" ;
2021-12-24 06:01:21 -03:00
2022-02-07 02:49:27 -03:00
enum {
select_queue _by _playlist _entry _in _id = 1 ,
select_queue _by _playlist _entry _out _queue _index = 0 ,
2021-12-24 06:01:21 -03:00
} ;
2022-02-07 02:49:27 -03:00
const char * query_select _queue _by _playlist _entry = "SELECT queueindex FROM queue WHERE (entryid = ?) LIMIT 1" ;
2021-12-24 06:01:21 -03:00
2022-02-07 02:49:27 -03:00
enum {
decrement_queue _for _removal _in _index = 1 ,
2021-12-24 06:01:21 -03:00
} ;
2022-02-07 02:49:27 -03:00
const char * query_decrement _queue _for _removal = "UPDATE queue SET queueindex = queueindex - 1 WHERE (queueindex >= ?)" ;
2021-12-24 06:01:21 -03:00
2022-02-07 02:49:27 -03:00
enum {
add_queue _in _queue _index = 1 ,
add_queue _in _entry _id ,
2021-12-24 06:01:21 -03:00
} ;
2022-02-07 02:49:27 -03:00
const char * query_add _queue = "INSERT INTO queue(queueindex, entryid) VALUES (?, ?)" ;
2021-12-24 06:01:21 -03:00
2022-02-07 02:49:27 -03:00
enum {
remove_queue _by _index _in _queue _index = 1 ,
2021-12-24 06:01:21 -03:00
} ;
2022-02-07 02:49:27 -03:00
const char * query_remove _queue _by _index = "DELETE FROM queue WHERE (queueindex = ?)" ;
2021-12-24 06:01:21 -03:00
2022-02-07 02:49:27 -03:00
const char * query_remove _queue _all = "DELETE FROM queue" ;
2021-12-24 06:01:21 -03:00
2022-02-07 02:49:27 -03:00
enum {
count_queue _out _count = 0 ,
2021-12-24 06:01:21 -03:00
} ;
2022-02-07 02:49:27 -03:00
const char * query_count _queue = "SELECT COUNT(*) FROM queue" ;
NSURL * urlForPath ( NSString * path ) {
if ( ! path || ! [ path length ] ) {
return [ NSURL URLWithString : @ "silence://10" ] ;
}
NSRange protocolRange = [ path rangeOfString : @ "://" ] ;
if ( protocolRange . location ! = NSNotFound ) {
return [ NSURL URLWithString : path ] ;
}
NSMutableString * unixPath = [ path mutableCopy ] ;
// Get the fragment
NSString * fragment = @ "" ;
NSScanner * scanner = [ NSScanner scannerWithString : unixPath ] ;
NSCharacterSet * characterSet = [ NSCharacterSet characterSetWithCharactersInString : @ "#1234567890" ] ;
while ( ! [ scanner isAtEnd ] ) {
NSString * possibleFragment ;
[ scanner scanUpToString : @ "#" intoString : nil ] ;
if ( [ scanner scanCharactersFromSet : characterSet intoString : & possibleFragment ] && [ scanner isAtEnd ] ) {
fragment = possibleFragment ;
[ unixPath deleteCharactersInRange : NSMakeRange ( [ scanner scanLocation ] - [ possibleFragment length ] , [ possibleFragment length ] ) ] ;
break ;
}
}
// Append the fragment
NSURL * url = [ NSURL URLWithString : [ [ [ NSURL fileURLWithPath : unixPath ] absoluteString ] stringByAppendingString : fragment ] ] ;
return url ;
2021-12-24 06:01:21 -03:00
}
@ interface SQLiteStore ( Private )
2022-02-16 21:57:47 -03:00
- ( int64_t ) addString : ( NSString * * ) string ;
2022-02-07 02:49:27 -03:00
- ( NSString * ) getString : ( int64_t ) stringId ;
- ( void ) removeString : ( int64_t ) stringId ;
2022-02-16 22:06:13 -03:00
- ( int64_t ) addArt : ( NSData * * ) art ;
2022-02-07 02:49:27 -03:00
- ( NSData * ) getArt : ( int64_t ) artId ;
- ( void ) removeArt : ( int64_t ) artId ;
- ( int64_t ) addTrack : ( PlaylistEntry * ) track ;
- ( PlaylistEntry * ) getTrack : ( int64_t ) trackId ;
- ( void ) removeTrack : ( int64_t ) trackId ;
2021-12-24 06:01:21 -03:00
@ end
@ implementation SQLiteStore
static SQLiteStore * g_sharedStore = NULL ;
2022-02-07 02:49:27 -03:00
+ ( SQLiteStore * ) sharedStore {
if ( ! g_sharedStore ) {
g_sharedStore = [ [ SQLiteStore alloc ] init ] ;
}
return g_sharedStore ;
2021-12-24 06:01:21 -03:00
}
@ synthesize databasePath = g_databasePath ;
@ synthesize database = g_database ;
2022-02-07 02:49:27 -03:00
- ( id ) init ;
2021-12-24 06:01:21 -03:00
{
2022-02-07 02:49:27 -03:00
self = [ super init ] ;
if ( self ) {
g_databasePath = getDatabasePath ( ) ;
memset ( stmt , 0 , sizeof ( stmt ) ) ;
BOOL dbExists = NO ;
if ( [ [ NSFileManager defaultManager ] fileExistsAtPath : g_databasePath ] )
dbExists = YES ;
if ( sqlite3_open ( [ g_databasePath UTF8String ] , & g_database ) = = SQLITE_OK ) {
char * error ;
if ( ! dbExists ) {
NSArray * schemas = createSchema ( ) ;
for ( NSString * schema in schemas ) {
if ( sqlite3_exec ( g_database , [ schema UTF8String ] , NULL , NULL , & error ) ! = SQLITE_OK ) {
DLog ( @ "SQLite error: %s" , error ) ;
return nil ;
}
}
NSString * createVersion = [ NSString stringWithFormat : @ "PRAGMA user_version = %lld" , currentSchemaVersion ] ;
if ( sqlite3_exec ( g_database , [ createVersion UTF8String ] , NULL , NULL , & error ) ) {
DLog ( @ "SQLite error: %s" , error ) ;
return nil ;
}
}
2021-12-24 06:01:21 -03:00
2022-01-22 03:38:54 -03:00
# define PREPARE ( name ) ( sqlite3_prepare _v2 ( g_database , query_ # # name , ( int ) strlen ( query_ # # name ) , & stmt [ stmt_ # # name ] , NULL ) )
2022-02-07 02:49:27 -03:00
if ( PREPARE ( user_version _get ) ) {
DLog ( @ "SQlite error: %s" , error ) ;
return nil ;
}
sqlite3_stmt * st = stmt [ stmt_user _version _get ] ;
if ( sqlite3_reset ( st ) ||
sqlite3_step ( st ) ! = SQLITE_ROW ) {
return nil ;
}
int64_t knownVersion = sqlite3_column _int64 ( st , user_version _get _out _version _number ) ;
sqlite3_reset ( st ) ;
if ( knownVersion < currentSchemaVersion ) {
switch ( knownVersion ) {
case 0 :
// Schema 0 to 1 : Add cuesheet and encoding text fields to the knowntracks table
if ( sqlite3_exec ( g_database , "ALTER TABLE knowntracks ADD encodingid INTEGER; ALTER TABLE knowntracks ADD cuesheetid INTEGER" , NULL , NULL , & error ) ) {
DLog ( @ "SQLite error: %s" , error ) ;
return nil ;
}
break ;
2022-02-07 06:11:30 -03:00
case 1 :
// Schema 1 to 2 : Add channelconfig integer field to the knowntracks table
if ( sqlite3_exec ( g_database , "ALTER TABLE knowntracks ADD channelconfig INTEGER" , NULL , NULL , & error ) ) {
DLog ( @ "SQLite error: %s" , error ) ;
return nil ;
}
break ;
2022-02-23 04:10:02 -03:00
case 2 :
// Schema 2 to 3 : Add arthash blob field to the artdictionary table , requires transmutation
{
if ( sqlite3_exec ( g_database , "CREATE TABLE IF NOT EXISTS artdictionary_v2 ( "
" artid INTEGER PRIMARY KEY AUTOINCREMENT, "
" arthash BLOB NOT NULL, "
" referencecount INTEGER, "
" value BLOB NOT NULL); "
"CREATE UNIQUE INDEX idx_art_hash ON artdictionary_v2 (arthash);" ,
NULL , NULL , & error ) ) {
DLog ( @ "SQLite error: %s" , error ) ;
return nil ;
}
if ( PREPARE ( select_art _all ) ||
PREPARE ( add_art _renamed ) )
return nil ;
// Add the art hashes to the table
st = stmt [ stmt_select _art _all ] ;
sqlite3_stmt * sta = stmt [ stmt_add _art _renamed ] ;
if ( sqlite3_reset ( st ) )
return nil ;
while ( sqlite3_step ( st ) = = SQLITE_ROW ) {
int64_t artId = sqlite3_column _int64 ( st , select_art _all _out _id ) ;
int64_t referenceCount = sqlite3_column _int64 ( st , select_art _all _out _referencecount ) ;
const void * artBytes = sqlite3_column _blob ( st , select_art _all _out _value ) ;
size_t artLength = sqlite3_column _bytes ( st , select_art _all _out _value ) ;
NSData * hash = [ SHA256Digest digestBytes : artBytes length : artLength ] ;
if ( sqlite3_reset ( sta ) ||
sqlite3_bind _int64 ( sta , add_art _renamed _in _id , artId ) ||
sqlite3_bind _int64 ( sta , add_art _renamed _in _referencecount , referenceCount ) ||
sqlite3_bind _blob64 ( sta , add_art _renamed _in _hash , [ hash bytes ] , [ hash length ] , SQLITE_STATIC ) ||
sqlite3_bind _blob64 ( sta , add_art _renamed _in _value , artBytes , artLength , SQLITE_STATIC ) ||
sqlite3_step ( sta ) ! = SQLITE_DONE )
return nil ;
}
sqlite3_reset ( sta ) ;
sqlite3_finalize ( sta ) ;
sqlite3_finalize ( st ) ;
stmt [ stmt_select _art _all ] = NULL ;
stmt [ stmt_add _art _renamed ] = NULL ;
if ( sqlite3_exec ( g_database , "PRAGMA foreign_keys=off; BEGIN TRANSACTION; DROP TABLE artdictionary; ALTER TABLE artdictionary_v2 RENAME TO artdictionary; COMMIT; PRAGMA foreign_keys=on;" , NULL , NULL , & error ) ) {
DLog ( @ "SQLite error: %s" , error ) ;
return nil ;
}
}
break ;
2022-02-07 02:49:27 -03:00
default :
break ;
}
NSString * updateVersion = [ NSString stringWithFormat : @ "PRAGMA user_version = %lld" , currentSchemaVersion ] ;
if ( sqlite3_exec ( g_database , [ updateVersion UTF8String ] , NULL , NULL , & error ) ) {
DLog ( @ "SQLite error: %s" , error ) ;
return nil ;
}
}
if ( PREPARE ( select_string ) ||
PREPARE ( select_string _refcount ) ||
PREPARE ( select_string _value ) ||
PREPARE ( bump_string ) ||
PREPARE ( pop_string ) ||
PREPARE ( add_string ) ||
PREPARE ( remove_string ) ||
PREPARE ( select_art ) ||
PREPARE ( select_art _refcount ) ||
PREPARE ( select_art _value ) ||
PREPARE ( bump_art ) ||
PREPARE ( pop_art ) ||
PREPARE ( add_art ) ||
PREPARE ( remove_art ) ||
PREPARE ( select_track ) ||
PREPARE ( select_track _refcount ) ||
PREPARE ( select_track _data ) ||
PREPARE ( bump_track ) ||
PREPARE ( pop_track ) ||
PREPARE ( add_track ) ||
PREPARE ( remove_track ) ||
PREPARE ( update_track ) ||
PREPARE ( select_playlist ) ||
PREPARE ( select_playlist _range ) ||
PREPARE ( select_playlist _all ) ||
PREPARE ( increment_playlist _for _insert ) ||
PREPARE ( decrement_playlist _for _removal ) ||
PREPARE ( add_playlist ) ||
PREPARE ( remove_playlist _by _range ) ||
PREPARE ( count_playlist ) ||
PREPARE ( update_playlist ) ||
PREPARE ( select_queue ) ||
PREPARE ( select_queue _by _playlist _entry ) ||
PREPARE ( decrement_queue _for _removal ) ||
PREPARE ( add_queue ) ||
PREPARE ( remove_queue _by _index ) ||
PREPARE ( remove_queue _all ) ||
PREPARE ( count_queue ) ) {
return nil ;
}
2021-12-24 06:01:21 -03:00
# undef PREPARE
2022-02-07 02:49:27 -03:00
size_t count = [ self playlistGetCount ] ;
databaseMirror = [ [ NSMutableArray alloc ] init ] ;
2022-02-16 21:57:47 -03:00
artTable = [ [ NSMutableDictionary alloc ] init ] ;
stringTable = [ [ NSMutableDictionary alloc ] init ] ;
2022-02-07 02:49:27 -03:00
for ( size_t i = 0 ; i < count ; + + i ) {
PlaylistEntry * pe = [ self playlistGetItem : i ] ;
[ databaseMirror addObject : pe ] ;
}
return self ;
}
}
return nil ;
2021-12-24 06:01:21 -03:00
}
2022-02-07 02:49:27 -03:00
- ( void ) dealloc {
if ( g_database ) {
for ( size_t i = 0 ; i < stmt_count ; + + i ) {
if ( stmt [ i ] ) sqlite3_finalize ( stmt [ i ] ) ;
}
sqlite3_close ( g_database ) ;
}
2021-12-24 06:01:21 -03:00
}
2022-02-16 21:57:47 -03:00
- ( int64_t ) addString : ( NSString * * ) string {
if ( ! * string || [ * string length ] = = 0 ) {
2022-02-07 02:49:27 -03:00
return -1 ;
}
2022-02-16 21:57:47 -03:00
const char * str = [ * string UTF8String ] ;
2022-02-07 02:49:27 -03:00
uint64_t len = strlen ( str ) ; // SQLite expects number of bytes , not characters
sqlite3_stmt * st = stmt [ stmt_select _string ] ;
if ( sqlite3_reset ( st ) ||
sqlite3_bind _text64 ( st , select_string _in _id , str , len , SQLITE_STATIC , SQLITE_UTF8 ) ) {
return -1 ;
}
int64_t ret , refcount ;
int rc = sqlite3_step ( st ) ;
if ( rc ! = SQLITE_DONE && rc ! = SQLITE_ROW ) {
return -1 ;
}
if ( rc = = SQLITE_ROW ) {
ret = sqlite3_column _int64 ( st , select_string _out _string _id ) ;
refcount = sqlite3_column _int64 ( st , select_string _out _reference _count ) ;
}
sqlite3_reset ( st ) ;
if ( rc ! = SQLITE_ROW ) {
st = stmt [ stmt_add _string ] ;
if ( sqlite3_reset ( st ) ||
sqlite3_bind _text64 ( st , add_string _in _value , str , len , SQLITE_STATIC , SQLITE_UTF8 ) ||
sqlite3_step ( st ) ! = SQLITE_DONE ||
sqlite3_reset ( st ) ) {
return -1 ;
}
ret = sqlite3_last _insert _rowid ( g_database ) ;
refcount = 1 ;
2022-02-16 21:57:47 -03:00
[ stringTable setObject : * string forKey : [ [ NSNumber numberWithInteger : ret ] stringValue ] ] ;
2022-02-07 02:49:27 -03:00
} else {
st = stmt [ stmt_bump _string ] ;
if ( sqlite3_reset ( st ) ||
sqlite3_bind _int64 ( st , bump_string _in _id , ret ) ||
sqlite3_step ( st ) ! = SQLITE_DONE ||
sqlite3_reset ( st ) ) {
return -1 ;
}
2022-02-16 21:57:47 -03:00
* string = [ stringTable objectForKey : [ [ NSNumber numberWithInteger : ret ] stringValue ] ] ;
2022-02-07 02:49:27 -03:00
}
return ret ;
2021-12-24 06:01:21 -03:00
}
2022-02-07 02:49:27 -03:00
- ( NSString * ) getString : ( int64_t ) stringId {
if ( stringId < 0 )
return @ "" ;
2022-02-16 21:57:47 -03:00
NSString * ret = [ stringTable objectForKey : [ [ NSNumber numberWithInteger : stringId ] stringValue ] ] ;
if ( ret ) return ret ;
2022-02-07 02:49:27 -03:00
sqlite3_stmt * st = stmt [ stmt_select _string _value ] ;
if ( sqlite3_reset ( st ) ||
sqlite3_bind _int64 ( st , select_string _value _in _id , stringId ) ) {
return @ "" ;
}
int rc = sqlite3_step ( st ) ;
if ( rc ! = SQLITE_ROW && rc ! = SQLITE_DONE ) {
sqlite3_reset ( st ) ;
return @ "" ;
}
2022-02-16 21:57:47 -03:00
ret = @ "" ;
2022-02-07 02:49:27 -03:00
if ( rc = = SQLITE_ROW ) {
const unsigned char * str = sqlite3_column _text ( st , select_string _value _out _value ) ;
int strBytes = sqlite3_column _bytes ( st , select_string _value _out _value ) ;
if ( str && strBytes && * str ) {
ret = [ NSString stringWithUTF8String : ( const char * ) str ] ;
}
}
sqlite3_reset ( st ) ;
2022-02-17 07:58:57 -03:00
[ stringTable setObject : ret forKey : [ [ NSNumber numberWithInteger : stringId ] stringValue ] ] ;
2022-02-07 02:49:27 -03:00
return ret ;
2021-12-24 06:01:21 -03:00
}
2022-02-07 02:49:27 -03:00
- ( void ) removeString : ( int64_t ) stringId {
if ( stringId < 0 )
return ;
int64_t refcount ;
sqlite3_stmt * st = stmt [ stmt_select _string _refcount ] ;
if ( sqlite3_reset ( st ) ||
sqlite3_bind _int64 ( st , select_string _refcount _in _id , stringId ) ) {
return ;
}
int rc = sqlite3_step ( st ) ;
if ( rc ! = SQLITE_ROW && rc ! = SQLITE_DONE ) {
return ;
}
if ( rc = = SQLITE_ROW ) {
refcount = sqlite3_column _int64 ( st , select_string _refcount _out _string _id ) ;
}
sqlite3_reset ( st ) ;
if ( rc ! = SQLITE_ROW ) {
refcount = 1 ;
}
if ( refcount <= 1 ) {
st = stmt [ stmt_remove _string ] ;
if ( sqlite3_reset ( st ) ||
sqlite3_bind _int64 ( st , remove_string _in _id , stringId ) ||
sqlite3_step ( st ) ! = SQLITE_DONE ||
sqlite3_reset ( st ) ) {
return ;
}
2022-02-16 21:57:47 -03:00
[ stringTable removeObjectForKey : [ [ NSNumber numberWithInteger : stringId ] stringValue ] ] ;
2022-02-07 02:49:27 -03:00
} else {
st = stmt [ stmt_pop _string ] ;
if ( sqlite3_reset ( st ) ||
sqlite3_bind _int64 ( st , pop_string _in _id , stringId ) ||
sqlite3_step ( st ) ! = SQLITE_DONE ||
sqlite3_reset ( st ) ) {
return ;
}
}
2021-12-24 06:01:21 -03:00
}
2022-02-16 22:06:13 -03:00
- ( int64_t ) addArt : ( NSData * * ) art {
if ( ! * art || [ * art length ] = = 0 ) {
2022-02-07 02:49:27 -03:00
return -1 ;
}
2022-02-23 04:10:02 -03:00
NSData * digest = [ SHA256Digest digestData : * art ] ;
2022-02-07 02:49:27 -03:00
sqlite3_stmt * st = stmt [ stmt_select _art ] ;
if ( sqlite3_reset ( st ) ||
2022-02-23 04:10:02 -03:00
sqlite3_bind _blob64 ( st , select_art _in _arthash , [ digest bytes ] , [ digest length ] , SQLITE_STATIC ) ) {
2022-02-07 02:49:27 -03:00
return -1 ;
}
int64_t ret , refcount ;
int rc = sqlite3_step ( st ) ;
if ( rc ! = SQLITE_DONE && rc ! = SQLITE_ROW ) {
return -1 ;
}
if ( rc = = SQLITE_ROW ) {
ret = sqlite3_column _int64 ( st , select_art _out _art _id ) ;
refcount = sqlite3_column _int64 ( st , select_art _out _reference _count ) ;
}
sqlite3_reset ( st ) ;
if ( rc ! = SQLITE_ROW ) {
st = stmt [ stmt_add _art ] ;
if ( sqlite3_reset ( st ) ||
2022-02-23 04:10:02 -03:00
sqlite3_bind _blob64 ( st , add_art _in _hash , [ digest bytes ] , [ digest length ] , SQLITE_STATIC ) ||
2022-02-16 22:06:13 -03:00
sqlite3_bind _blob64 ( st , add_art _in _value , [ * art bytes ] , [ * art length ] , SQLITE_STATIC ) ||
2022-02-07 02:49:27 -03:00
sqlite3_step ( st ) ! = SQLITE_DONE ||
sqlite3_reset ( st ) ) {
return -1 ;
}
ret = sqlite3_last _insert _rowid ( g_database ) ;
refcount = 1 ;
2022-02-16 22:06:13 -03:00
[ artTable setObject : * art forKey : [ [ NSNumber numberWithInteger : ret ] stringValue ] ] ;
2022-02-07 02:49:27 -03:00
} else {
st = stmt [ stmt_bump _art ] ;
if ( sqlite3_reset ( st ) ||
sqlite3_bind _int64 ( st , bump_art _in _id , ret ) ||
sqlite3_step ( st ) ! = SQLITE_DONE ||
sqlite3_reset ( st ) ) {
return -1 ;
}
2022-02-16 22:06:13 -03:00
* art = [ artTable objectForKey : [ [ NSNumber numberWithInteger : ret ] stringValue ] ] ;
}
2022-02-11 03:37:37 -03:00
2022-02-07 02:49:27 -03:00
return ret ;
2021-12-24 06:01:21 -03:00
}
2022-02-07 02:49:27 -03:00
- ( NSData * ) getArt : ( int64_t ) artId {
if ( artId < 0 )
return [ NSData data ] ;
2022-02-11 03:37:37 -03:00
NSData * ret = [ artTable valueForKey : [ [ NSNumber numberWithInteger : artId ] stringValue ] ] ;
if ( ret ) return ret ;
2022-02-07 02:49:27 -03:00
sqlite3_stmt * st = stmt [ stmt_select _art _value ] ;
if ( sqlite3_reset ( st ) ||
sqlite3_bind _int64 ( st , select_art _value _in _id , artId ) ) {
return [ NSData data ] ;
}
int rc = sqlite3_step ( st ) ;
if ( rc ! = SQLITE_ROW && rc ! = SQLITE_DONE ) {
sqlite3_reset ( st ) ;
return [ NSData data ] ;
}
2022-02-11 03:37:37 -03:00
ret = [ NSData data ] ;
2022-02-07 02:49:27 -03:00
if ( rc = = SQLITE_ROW ) {
const void * blob = sqlite3_column _blob ( st , select_art _value _out _value ) ;
int blobBytes = sqlite3_column _bytes ( st , select_art _value _out _value ) ;
if ( blob && blobBytes ) {
ret = [ NSData dataWithBytes : blob length : blobBytes ] ;
}
}
sqlite3_reset ( st ) ;
2022-02-11 03:37:37 -03:00
[ artTable setValue : ret forKey : [ [ NSNumber numberWithInteger : artId ] stringValue ] ] ;
2022-02-07 02:49:27 -03:00
return ret ;
2021-12-24 06:01:21 -03:00
}
2022-02-07 02:49:27 -03:00
- ( void ) removeArt : ( int64_t ) artId {
if ( artId < 0 )
return ;
int64_t refcount ;
sqlite3_stmt * st = stmt [ stmt_select _art _refcount ] ;
if ( sqlite3_reset ( st ) ||
sqlite3_bind _int64 ( st , select_art _refcount _in _id , artId ) ) {
return ;
}
int rc = sqlite3_step ( st ) ;
if ( rc ! = SQLITE_ROW && rc ! = SQLITE_DONE ) {
return ;
}
if ( rc = = SQLITE_ROW ) {
refcount = sqlite3_column _int64 ( st , select_art _refcount _out _reference _count ) ;
}
sqlite3_reset ( st ) ;
if ( rc ! = SQLITE_ROW ) {
refcount = 1 ;
}
if ( refcount <= 1 ) {
2022-02-11 03:37:37 -03:00
[ artTable removeObjectForKey : [ [ NSNumber numberWithInteger : artId ] stringValue ] ] ;
2022-02-07 02:49:27 -03:00
st = stmt [ stmt_remove _art ] ;
if ( sqlite3_reset ( st ) ||
sqlite3_bind _int64 ( st , remove_art _in _id , artId ) ||
sqlite3_step ( st ) ! = SQLITE_DONE ||
sqlite3_reset ( st ) ) {
return ;
}
} else {
st = stmt [ stmt_pop _art ] ;
if ( sqlite3_reset ( st ) ||
sqlite3_bind _int64 ( st , pop_art _in _id , artId ) ||
sqlite3_step ( st ) ! = SQLITE_DONE ||
sqlite3_reset ( st ) ) {
return ;
}
}
2021-12-24 06:01:21 -03:00
}
2022-02-07 02:49:27 -03:00
- ( int64_t ) addTrack : ( PlaylistEntry * ) track {
NSURL * url = [ track URL ] ;
NSString * urlString = [ url absoluteString ] ;
2022-02-16 21:57:47 -03:00
int64_t urlId = [ self addString : & urlString ] ;
2022-02-07 02:49:27 -03:00
sqlite3_stmt * st = stmt [ stmt_select _track ] ;
if ( sqlite3_reset ( st ) ||
sqlite3_bind _int64 ( st , select_track _in _id , urlId ) ) {
[ self removeString : urlId ] ;
return -1 ;
}
int64_t ret , refcount ;
int rc = sqlite3_step ( st ) ;
if ( rc ! = SQLITE_DONE && rc ! = SQLITE_ROW ) {
[ self removeString : urlId ] ;
return -1 ;
}
if ( rc = = SQLITE_ROW ) {
ret = sqlite3_column _int64 ( st , select_track _out _track _id ) ;
refcount = sqlite3_column _int64 ( st , select_track _out _reference _count ) ;
}
sqlite3_reset ( stmt [ stmt_select _string ] ) ;
if ( rc ! = SQLITE_ROW ) {
2022-02-16 21:57:47 -03:00
NSString * temp ;
temp = [ track album ] ;
int64_t albumId = [ self addString : & temp ] ;
[ track setAlbum : temp ] ;
temp = [ track albumartist ] ;
int64_t albumartistId = [ self addString : & temp ] ;
[ track setAlbumartist : temp ] ;
temp = [ track artist ] ;
int64_t artistId = [ self addString : & temp ] ;
[ track setArtist : temp ] ;
temp = [ track rawTitle ] ;
int64_t titleId = [ self addString : & temp ] ;
[ track setTitle : temp ] ;
temp = [ track genre ] ;
int64_t genreId = [ self addString : & temp ] ;
[ track setGenre : temp ] ;
temp = [ track codec ] ;
int64_t codecId = [ self addString : & temp ] ;
[ track setCodec : temp ] ;
temp = [ track cuesheet ] ;
int64_t cuesheetId = [ self addString : & temp ] ;
[ track setCuesheet : temp ] ;
temp = [ track encoding ] ;
int64_t encodingId = [ self addString : & temp ] ;
[ track setEncoding : temp ] ;
2022-02-07 02:49:27 -03:00
int64_t trackNr = [ [ track track ] intValue ] | ( ( ( uint64_t ) [ [ track disc ] intValue ] ) < < 32 ) ;
int64_t year = [ [ track year ] intValue ] ;
int64_t unsignedFmt = [ track Unsigned ] ;
int64_t bitrate = [ track bitrate ] ;
double samplerate = [ track sampleRate ] ;
int64_t bitspersample = [ track bitsPerSample ] ;
int64_t channels = [ track channels ] ;
2022-02-07 06:11:30 -03:00
int64_t channelConfig = [ track channelConfig ] ;
2022-02-16 21:57:47 -03:00
temp = [ track endian ] ;
int64_t endianId = [ self addString : & temp ] ;
[ track setEndian : temp ] ;
2022-02-07 02:49:27 -03:00
int64_t floatingpoint = [ track floatingPoint ] ;
int64_t totalframes = [ track totalFrames ] ;
int64_t metadataloaded = [ track metadataLoaded ] ;
int64_t seekable = [ track seekable ] ;
double volume = [ track volume ] ;
double replaygainalbumgain = [ track replayGainAlbumGain ] ;
double replaygainalbumpeak = [ track replayGainAlbumPeak ] ;
double replaygaintrackgain = [ track replayGainTrackGain ] ;
double replaygaintrackpeak = [ track replayGainTrackPeak ] ;
NSData * albumArt = [ track albumArtInternal ] ;
int64_t artId = -1 ;
if ( albumArt )
2022-02-16 22:06:13 -03:00
artId = [ self addArt : & albumArt ] ;
[ track setAlbumArtInternal : albumArt ] ;
2022-02-11 03:45:26 -03:00
[ track setArtId : artId ] ;
2022-02-07 02:49:27 -03:00
st = stmt [ stmt_add _track ] ;
if ( sqlite3_reset ( st ) ||
sqlite3_bind _int64 ( st , add_track _in _url _id , urlId ) ||
sqlite3_bind _int64 ( st , add_track _in _art _id , artId ) ||
sqlite3_bind _int64 ( st , add_track _in _album _id , albumId ) ||
sqlite3_bind _int64 ( st , add_track _in _albumartist _id , albumartistId ) ||
sqlite3_bind _int64 ( st , add_track _in _artist _id , artistId ) ||
sqlite3_bind _int64 ( st , add_track _in _title _id , titleId ) ||
sqlite3_bind _int64 ( st , add_track _in _genre _id , genreId ) ||
sqlite3_bind _int64 ( st , add_track _in _codec _id , codecId ) ||
sqlite3_bind _int64 ( st , add_track _in _cuesheet _id , cuesheetId ) ||
sqlite3_bind _int64 ( st , add_track _in _encoding _id , encodingId ) ||
sqlite3_bind _int64 ( st , add_track _in _track , trackNr ) ||
sqlite3_bind _int64 ( st , add_track _in _year , year ) ||
sqlite3_bind _int64 ( st , add_track _in _unsigned , unsignedFmt ) ||
sqlite3_bind _int64 ( st , add_track _in _bitrate , bitrate ) ||
sqlite3_bind _double ( st , add_track _in _samplerate , samplerate ) ||
sqlite3_bind _int64 ( st , add_track _in _bitspersample , bitspersample ) ||
sqlite3_bind _int64 ( st , add_track _in _channels , channels ) ||
2022-02-07 06:11:30 -03:00
sqlite3_bind _int64 ( st , add_track _in _channelconfig , channelConfig ) ||
2022-02-07 02:49:27 -03:00
sqlite3_bind _int64 ( st , add_track _in _endian _id , endianId ) ||
sqlite3_bind _int64 ( st , add_track _in _floatingpoint , floatingpoint ) ||
sqlite3_bind _int64 ( st , add_track _in _totalframes , totalframes ) ||
sqlite3_bind _int64 ( st , add_track _in _metadataloaded , metadataloaded ) ||
sqlite3_bind _int64 ( st , add_track _in _seekable , seekable ) ||
sqlite3_bind _double ( st , add_track _in _volume , volume ) ||
sqlite3_bind _double ( st , add_track _in _replaygainalbumgain , replaygainalbumgain ) ||
sqlite3_bind _double ( st , add_track _in _replaygainalbumpeak , replaygainalbumpeak ) ||
sqlite3_bind _double ( st , add_track _in _replaygaintrackgain , replaygaintrackgain ) ||
sqlite3_bind _double ( st , add_track _in _replaygaintrackpeak , replaygaintrackpeak ) ||
sqlite3_step ( st ) ! = SQLITE_DONE ||
sqlite3_reset ( st ) ) {
[ self removeArt : artId ] ;
[ self removeString : albumId ] ;
[ self removeString : albumartistId ] ;
[ self removeString : artistId ] ;
[ self removeString : titleId ] ;
[ self removeString : genreId ] ;
[ self removeString : codecId ] ;
[ self removeString : cuesheetId ] ;
[ self removeString : encodingId ] ;
[ self removeString : endianId ] ;
[ self removeString : urlId ] ;
return -1 ;
}
ret = sqlite3_last _insert _rowid ( g_database ) ;
refcount = 1 ;
} else {
[ self removeString : urlId ] ; // should only be bumped once per instance of track
st = stmt [ stmt_bump _track ] ;
if ( sqlite3_reset ( st ) ||
sqlite3_bind _int64 ( st , bump_track _in _id , ret ) ||
sqlite3_step ( st ) ! = SQLITE_DONE ||
sqlite3_reset ( st ) ) {
return -1 ;
}
}
[ track setDbIndex : ret ] ;
return ret ;
2021-12-24 06:01:21 -03:00
}
2022-02-07 02:49:27 -03:00
- ( void ) trackUpdate : ( PlaylistEntry * ) track {
NSURL * url = [ track URL ] ;
NSString * urlString = [ url absoluteString ] ;
2022-02-16 21:57:47 -03:00
int64_t urlId = [ self addString : & urlString ] ;
2022-02-07 02:49:27 -03:00
sqlite3_stmt * st = stmt [ stmt_select _track ] ;
if ( sqlite3_reset ( st ) ||
sqlite3_bind _int64 ( st , select_track _in _id , urlId ) ) {
[ self removeString : urlId ] ;
return ;
}
int64_t trackId , refcount ;
int rc = sqlite3_step ( st ) ;
if ( rc ! = SQLITE_DONE && rc ! = SQLITE_ROW ) {
[ self removeString : urlId ] ;
return ;
}
trackId = -1 ;
if ( rc = = SQLITE_ROW ) {
trackId = sqlite3_column _int64 ( st , select_track _out _track _id ) ;
refcount = sqlite3_column _int64 ( st , select_track _out _reference _count ) ;
}
sqlite3_reset ( stmt [ stmt_select _string ] ) ;
if ( trackId < 0 ) {
[ self removeString : urlId ] ;
return ;
}
if ( trackId ! = [ track dbIndex ] && [ track dbIndex ] ! = 0 ) {
[ self removeString : urlId ] ;
return ;
}
if ( [ track dbIndex ] = = 0 ) {
[ track setDbIndex : trackId ] ;
}
st = stmt [ stmt_select _track _data ] ;
if ( sqlite3_reset ( st ) ||
sqlite3_bind _int64 ( st , select_track _data _in _id , trackId ) ) {
[ self removeString : urlId ] ;
return ;
}
rc = sqlite3_step ( st ) ;
if ( rc ! = SQLITE_ROW && rc ! = SQLITE_DONE ) {
sqlite3_reset ( st ) ;
[ self removeString : urlId ] ;
return ;
}
if ( rc = = SQLITE_ROW ) {
int64_t urlId = sqlite3_column _int64 ( st , select_track _data _out _url _id ) ;
int64_t artId = sqlite3_column _int64 ( st , select_track _data _out _art _id ) ;
int64_t albumId = sqlite3_column _int64 ( st , select_track _data _out _album _id ) ;
int64_t albumartistId = sqlite3_column _int64 ( st , select_track _data _out _albumartist _id ) ;
int64_t artistId = sqlite3_column _int64 ( st , select_track _data _out _artist _id ) ;
int64_t titleId = sqlite3_column _int64 ( st , select_track _data _out _title _id ) ;
int64_t genreId = sqlite3_column _int64 ( st , select_track _data _out _genre _id ) ;
int64_t codecId = sqlite3_column _int64 ( st , select_track _data _out _codec _id ) ;
int64_t cuesheetId = sqlite3_column _int64 ( st , select_track _data _out _cuesheet _id ) ;
int64_t encodingId = sqlite3_column _int64 ( st , select_track _data _out _encoding _id ) ;
int64_t endianId = sqlite3_column _int64 ( st , select_track _data _out _endian _id ) ;
[ self removeArt : artId ] ;
[ self removeString : urlId ] ;
[ self removeString : albumId ] ;
[ self removeString : albumartistId ] ;
[ self removeString : artistId ] ;
[ self removeString : titleId ] ;
[ self removeString : genreId ] ;
[ self removeString : codecId ] ;
[ self removeString : cuesheetId ] ;
[ self removeString : encodingId ] ;
[ self removeString : endianId ] ;
}
sqlite3_reset ( st ) ;
{
2022-02-16 21:57:47 -03:00
NSString * temp ;
temp = [ track album ] ;
int64_t albumId = [ self addString : & temp ] ;
[ track setAlbum : temp ] ;
temp = [ track albumartist ] ;
int64_t albumartistId = [ self addString : & temp ] ;
[ track setAlbumartist : temp ] ;
temp = [ track artist ] ;
int64_t artistId = [ self addString : & temp ] ;
[ track setArtist : temp ] ;
temp = [ track rawTitle ] ;
int64_t titleId = [ self addString : & temp ] ;
[ track setTitle : temp ] ;
temp = [ track genre ] ;
int64_t genreId = [ self addString : & temp ] ;
[ track setGenre : temp ] ;
temp = [ track codec ] ;
int64_t codecId = [ self addString : & temp ] ;
[ track setCodec : temp ] ;
temp = [ track cuesheet ] ;
int64_t cuesheetId = [ self addString : & temp ] ;
[ track setCuesheet : temp ] ;
temp = [ track encoding ] ;
int64_t encodingId = [ self addString : & temp ] ;
[ track setEncoding : temp ] ;
2022-02-07 02:49:27 -03:00
int64_t trackNr = [ [ track track ] intValue ] | ( ( ( uint64_t ) [ [ track disc ] intValue ] ) < < 32 ) ;
int64_t year = [ [ track year ] intValue ] ;
int64_t unsignedFmt = [ track Unsigned ] ;
int64_t bitrate = [ track bitrate ] ;
double samplerate = [ track sampleRate ] ;
int64_t bitspersample = [ track bitsPerSample ] ;
int64_t channels = [ track channels ] ;
2022-02-07 06:11:30 -03:00
int64_t channelConfig = [ track channelConfig ] ;
2022-02-16 21:57:47 -03:00
temp = [ track endian ] ;
int64_t endianId = [ self addString : & temp ] ;
[ track setEndian : temp ] ;
2022-02-07 02:49:27 -03:00
int64_t floatingpoint = [ track floatingPoint ] ;
int64_t totalframes = [ track totalFrames ] ;
int64_t metadataloaded = [ track metadataLoaded ] ;
int64_t seekable = [ track seekable ] ;
double volume = [ track volume ] ;
double replaygainalbumgain = [ track replayGainAlbumGain ] ;
double replaygainalbumpeak = [ track replayGainAlbumPeak ] ;
double replaygaintrackgain = [ track replayGainTrackGain ] ;
double replaygaintrackpeak = [ track replayGainTrackPeak ] ;
NSData * albumArt = [ track albumArtInternal ] ;
int64_t artId = -1 ;
if ( albumArt )
2022-02-16 22:06:13 -03:00
artId = [ self addArt : & albumArt ] ;
[ track setAlbumArtInternal : albumArt ] ;
2022-02-11 03:45:26 -03:00
[ track setArtId : artId ] ;
2022-02-07 02:49:27 -03:00
st = stmt [ stmt_update _track ] ;
if ( sqlite3_reset ( st ) ||
sqlite3_bind _int64 ( st , update_track _in _url _id , urlId ) ||
sqlite3_bind _int64 ( st , update_track _in _art _id , artId ) ||
sqlite3_bind _int64 ( st , update_track _in _album _id , albumId ) ||
sqlite3_bind _int64 ( st , update_track _in _albumartist _id , albumartistId ) ||
sqlite3_bind _int64 ( st , update_track _in _artist _id , artistId ) ||
sqlite3_bind _int64 ( st , update_track _in _title _id , titleId ) ||
sqlite3_bind _int64 ( st , update_track _in _genre _id , genreId ) ||
sqlite3_bind _int64 ( st , update_track _in _codec _id , codecId ) ||
sqlite3_bind _int64 ( st , update_track _in _cuesheet _id , cuesheetId ) ||
sqlite3_bind _int64 ( st , update_track _in _encoding _id , encodingId ) ||
sqlite3_bind _int64 ( st , update_track _in _track , trackNr ) ||
sqlite3_bind _int64 ( st , update_track _in _year , year ) ||
sqlite3_bind _int64 ( st , update_track _in _unsigned , unsignedFmt ) ||
sqlite3_bind _int64 ( st , update_track _in _bitrate , bitrate ) ||
sqlite3_bind _double ( st , update_track _in _samplerate , samplerate ) ||
sqlite3_bind _int64 ( st , update_track _in _bitspersample , bitspersample ) ||
sqlite3_bind _int64 ( st , update_track _in _channels , channels ) ||
2022-02-07 06:11:30 -03:00
sqlite3_bind _int64 ( st , update_track _in _channelconfig , channelConfig ) ||
2022-02-07 02:49:27 -03:00
sqlite3_bind _int64 ( st , update_track _in _endian _id , endianId ) ||
sqlite3_bind _int64 ( st , update_track _in _floatingpoint , floatingpoint ) ||
sqlite3_bind _int64 ( st , update_track _in _totalframes , totalframes ) ||
sqlite3_bind _int64 ( st , update_track _in _metadataloaded , metadataloaded ) ||
sqlite3_bind _int64 ( st , update_track _in _seekable , seekable ) ||
sqlite3_bind _double ( st , update_track _in _volume , volume ) ||
sqlite3_bind _double ( st , update_track _in _replaygainalbumgain , replaygainalbumgain ) ||
sqlite3_bind _double ( st , update_track _in _replaygainalbumpeak , replaygainalbumpeak ) ||
sqlite3_bind _double ( st , update_track _in _replaygaintrackgain , replaygaintrackgain ) ||
sqlite3_bind _double ( st , update_track _in _replaygaintrackpeak , replaygaintrackpeak ) ||
sqlite3_bind _int64 ( st , update_track _in _id , trackId ) ||
sqlite3_step ( st ) ! = SQLITE_DONE ||
sqlite3_reset ( st ) ) {
[ self removeArt : artId ] ;
[ self removeString : albumId ] ;
[ self removeString : albumartistId ] ;
[ self removeString : artistId ] ;
[ self removeString : titleId ] ;
[ self removeString : genreId ] ;
[ self removeString : codecId ] ;
[ self removeString : cuesheetId ] ;
[ self removeString : encodingId ] ;
[ self removeString : endianId ] ;
[ self removeString : urlId ] ;
return ;
}
[ databaseMirror replaceObjectAtIndex : [ track index ] withObject : [ track copy ] ] ;
}
2021-12-24 06:01:21 -03:00
}
2022-02-07 02:49:27 -03:00
- ( PlaylistEntry * ) getTrack : ( int64_t ) trackId {
PlaylistEntry * entry = [ [ PlaylistEntry alloc ] init ] ;
if ( trackId < 0 )
return entry ;
sqlite3_stmt * st = stmt [ stmt_select _track _data ] ;
if ( sqlite3_reset ( st ) ||
sqlite3_bind _int64 ( st , select_track _data _in _id , trackId ) ) {
return entry ;
}
int rc = sqlite3_step ( st ) ;
if ( rc ! = SQLITE_ROW && rc ! = SQLITE_DONE ) {
sqlite3_reset ( st ) ;
return entry ;
}
if ( rc = = SQLITE_ROW ) {
int64_t urlId = sqlite3_column _int64 ( st , select_track _data _out _url _id ) ;
int64_t artId = sqlite3_column _int64 ( st , select_track _data _out _art _id ) ;
int64_t albumId = sqlite3_column _int64 ( st , select_track _data _out _album _id ) ;
int64_t albumartistId = sqlite3_column _int64 ( st , select_track _data _out _albumartist _id ) ;
int64_t artistId = sqlite3_column _int64 ( st , select_track _data _out _artist _id ) ;
int64_t titleId = sqlite3_column _int64 ( st , select_track _data _out _title _id ) ;
int64_t genreId = sqlite3_column _int64 ( st , select_track _data _out _genre _id ) ;
int64_t codecId = sqlite3_column _int64 ( st , select_track _data _out _codec _id ) ;
int64_t cuesheetId = sqlite3_column _int64 ( st , select_track _data _out _cuesheet _id ) ;
int64_t encodingId = sqlite3_column _int64 ( st , select_track _data _out _encoding _id ) ;
int64_t trackNr = sqlite3_column _int64 ( st , select_track _data _out _track ) ;
int64_t year = sqlite3_column _int64 ( st , select_track _data _out _year ) ;
int64_t unsignedFmt = sqlite3_column _int64 ( st , select_track _data _out _unsigned ) ;
int64_t bitrate = sqlite3_column _int64 ( st , select_track _data _out _bitrate ) ;
double samplerate = sqlite3_column _double ( st , select_track _data _out _samplerate ) ;
int64_t bitspersample = sqlite3_column _int64 ( st , select_track _data _out _bitspersample ) ;
int64_t channels = sqlite3_column _int64 ( st , select_track _data _out _channels ) ;
2022-02-07 06:11:30 -03:00
int64_t channelConfig = sqlite3_column _int64 ( st , select_track _data _out _channelconfig ) ;
2022-02-07 02:49:27 -03:00
int64_t endianId = sqlite3_column _int64 ( st , select_track _data _out _endian _id ) ;
int64_t floatingpoint = sqlite3_column _int64 ( st , select_track _data _out _floatingpoint ) ;
int64_t totalframes = sqlite3_column _int64 ( st , select_track _data _out _totalframes ) ;
int64_t metadataloaded = sqlite3_column _int64 ( st , select_track _data _out _metadataloaded ) ;
int64_t seekable = sqlite3_column _int64 ( st , select_track _data _out _seekable ) ;
double volume = sqlite3_column _double ( st , select_track _data _out _volume ) ;
double replaygainalbumgain = sqlite3_column _double ( st , select_track _data _out _replaygainalbumgain ) ;
double replaygainalbumpeak = sqlite3_column _double ( st , select_track _data _out _replaygainalbumpeak ) ;
double replaygaintrackgain = sqlite3_column _double ( st , select_track _data _out _replaygaintrackgain ) ;
double replaygaintrackpeak = sqlite3_column _double ( st , select_track _data _out _replaygaintrackpeak ) ;
uint64_t discNr = ( ( uint64_t ) trackNr ) > > 32 ;
trackNr & = ( 1 UL < < 32 ) - 1 ;
[ entry setURL : urlForPath ( [ self getString : urlId ] ) ] ;
[ entry setAlbum : [ self getString : albumId ] ] ;
[ entry setAlbumartist : [ self getString : albumartistId ] ] ;
[ entry setArtist : [ self getString : artistId ] ] ;
[ entry setTitle : [ self getString : titleId ] ] ;
[ entry setGenre : [ self getString : genreId ] ] ;
[ entry setCodec : [ self getString : codecId ] ] ;
[ entry setCuesheet : [ self getString : cuesheetId ] ] ;
[ entry setEncoding : [ self getString : encodingId ] ] ;
[ entry setTrack : [ NSNumber numberWithInteger : trackNr ] ] ;
[ entry setDisc : [ NSNumber numberWithInteger : discNr ] ] ;
[ entry setYear : [ NSNumber numberWithInteger : year ] ] ;
[ entry setUnsigned : ! ! unsignedFmt ] ;
[ entry setBitrate : ( int ) bitrate ] ;
[ entry setSampleRate : samplerate ] ;
[ entry setBitsPerSample : ( int ) bitspersample ] ;
[ entry setChannels : ( int ) channels ] ;
2022-02-07 06:11:30 -03:00
[ entry setChannelConfig : ( uint32_t ) channelConfig ] ;
2022-02-07 02:49:27 -03:00
[ entry setEndian : [ self getString : endianId ] ] ;
[ entry setFloatingPoint : ! ! floatingpoint ] ;
[ entry setTotalFrames : totalframes ] ;
[ entry setSeekable : ! ! seekable ] ;
[ entry setVolume : volume ] ;
[ entry setReplayGainAlbumGain : replaygainalbumgain ] ;
[ entry setReplayGainAlbumPeak : replaygainalbumpeak ] ;
[ entry setReplayGainTrackGain : replaygaintrackgain ] ;
[ entry setReplayGainTrackPeak : replaygaintrackpeak ] ;
2022-02-11 03:37:37 -03:00
[ entry setArtId : artId ] ;
2022-02-07 02:49:27 -03:00
[ entry setAlbumArtInternal : [ self getArt : artId ] ] ;
[ entry setMetadataLoaded : ! ! metadataloaded ] ;
[ entry setDbIndex : trackId ] ;
}
sqlite3_reset ( st ) ;
return entry ;
2021-12-24 06:01:21 -03:00
}
2022-02-07 02:49:27 -03:00
- ( void ) removeTrack : ( int64_t ) trackId {
if ( trackId < 0 )
return ;
int64_t refcount ;
sqlite3_stmt * st = stmt [ stmt_select _track _refcount ] ;
if ( sqlite3_reset ( st ) ||
sqlite3_bind _int64 ( st , select_track _refcount _in _id , trackId ) ) {
return ;
}
int rc = sqlite3_step ( st ) ;
if ( rc ! = SQLITE_ROW && rc ! = SQLITE_DONE ) {
return ;
}
if ( rc = = SQLITE_ROW ) {
refcount = sqlite3_column _int64 ( st , select_track _refcount _out _reference _count ) ;
}
sqlite3_reset ( st ) ;
if ( rc ! = SQLITE_ROW ) {
refcount = 1 ;
}
if ( refcount <= 1 ) {
// DeRef the strings and art
st = stmt [ stmt_select _track _data ] ;
if ( sqlite3_reset ( st ) = = SQLITE_OK &&
sqlite3_bind _int64 ( st , select_track _data _in _id , trackId ) = = SQLITE_OK &&
sqlite3_step ( st ) = = SQLITE_ROW ) {
int64_t urlId = sqlite3_column _int64 ( st , select_track _data _out _url _id ) ;
int64_t artId = sqlite3_column _int64 ( st , select_track _data _out _art _id ) ;
int64_t albumId = sqlite3_column _int64 ( st , select_track _data _out _album _id ) ;
int64_t albumartistId = sqlite3_column _int64 ( st , select_track _data _out _albumartist _id ) ;
int64_t artistId = sqlite3_column _int64 ( st , select_track _data _out _artist _id ) ;
int64_t titleId = sqlite3_column _int64 ( st , select_track _data _out _title _id ) ;
int64_t genreId = sqlite3_column _int64 ( st , select_track _data _out _genre _id ) ;
int64_t codecId = sqlite3_column _int64 ( st , select_track _data _out _codec _id ) ;
int64_t cuesheetId = sqlite3_column _int64 ( st , select_track _data _out _cuesheet _id ) ;
int64_t encodingId = sqlite3_column _int64 ( st , select_track _data _out _encoding _id ) ;
int64_t endianId = sqlite3_column _int64 ( st , select_track _data _out _endian _id ) ;
sqlite3_reset ( st ) ;
[ self removeArt : artId ] ;
[ self removeString : urlId ] ;
[ self removeString : albumId ] ;
[ self removeString : albumartistId ] ;
[ self removeString : artistId ] ;
[ self removeString : titleId ] ;
[ self removeString : genreId ] ;
[ self removeString : codecId ] ;
[ self removeString : cuesheetId ] ;
[ self removeString : encodingId ] ;
[ self removeString : endianId ] ;
}
st = stmt [ stmt_remove _track ] ;
if ( sqlite3_reset ( st ) ||
sqlite3_bind _int64 ( st , remove_track _in _id , trackId ) ||
sqlite3_step ( st ) ! = SQLITE_DONE ||
sqlite3_reset ( st ) ) {
return ;
}
} else {
st = stmt [ stmt_pop _track ] ;
if ( sqlite3_reset ( st ) ||
sqlite3_bind _int64 ( st , pop_track _in _id , trackId ) ||
sqlite3_step ( st ) ! = SQLITE_DONE ||
sqlite3_reset ( st ) ) {
return ;
}
}
2021-12-24 06:01:21 -03:00
}
2022-02-07 02:49:27 -03:00
- ( void ) playlistInsertTracks : ( NSArray * ) tracks atIndex : ( int64_t ) index progressCall : ( void ( ^ ) ( double ) ) callback {
if ( ! tracks ) {
callback ( -1 ) ;
return ;
}
callback ( 0 ) ;
sqlite3_stmt * st = stmt [ stmt_increment _playlist _for _insert ] ;
if ( sqlite3_reset ( st ) ||
sqlite3_bind _int64 ( st , increment_playlist _for _insert _in _count , [ tracks count ] ) ||
sqlite3_bind _int64 ( st , increment_playlist _for _insert _in _index , index ) ||
sqlite3_step ( st ) ! = SQLITE_DONE ||
sqlite3_reset ( st ) ) {
callback ( -1 ) ;
return ;
}
callback ( 25 ) ;
double progress = 25.0 ;
double progressstep = [ tracks count ] ? 75.0 / ( double ) ( [ tracks count ] ) : 0 ;
st = stmt [ stmt_add _playlist ] ;
for ( PlaylistEntry * entry in tracks ) {
int64_t trackId = [ self addTrack : entry ] ;
if ( sqlite3_reset ( st ) ||
sqlite3_bind _int64 ( st , add_playlist _in _entry _index , index ) ||
sqlite3_bind _int64 ( st , add_playlist _in _track _id , trackId ) ||
sqlite3_step ( st ) ! = SQLITE_DONE ) {
callback ( -1 ) ;
return ;
}
+ + index ;
progress + = progressstep ;
callback ( progress ) ;
}
sqlite3_reset ( st ) ;
callback ( -1 ) ;
2021-12-24 06:01:21 -03:00
}
2022-02-07 02:49:27 -03:00
- ( void ) playlistInsertTracks : ( NSArray * ) tracks atObjectIndexes : ( NSIndexSet * ) indexes progressCall : ( void ( ^ ) ( double ) ) callback {
if ( ! tracks || ! indexes ) {
callback ( -1 ) ;
return ;
}
NSMutableArray * tracksCopy = [ [ NSMutableArray alloc ] init ] ;
for ( PlaylistEntry * pe in tracks ) {
[ tracksCopy addObject : [ pe copy ] ] ;
}
[ databaseMirror insertObjects : tracksCopy atIndexes : indexes ] ;
__block int64_t total_count = 0 ;
[ indexes enumerateRangesUsingBlock : ^ ( NSRange range , BOOL * _Nonnull stop ) {
total_count + = range . length ;
} ] ;
__block int64_t i = 0 ;
__block double progress = 0 ;
[ indexes enumerateRangesUsingBlock : ^ ( NSRange range , BOOL * _Nonnull stop ) {
double progresschunk = ( double ) range . length / ( double ) total_count ;
double progressbase = progress ;
NSRange trackRange = NSMakeRange ( i , range . length ) ;
NSArray * trackSet = ( i = = 0 && range . length = = [ tracksCopy count ] ) ? tracksCopy : [ tracksCopy subarrayWithRange : trackRange ] ;
[ self playlistInsertTracks : trackSet
atIndex : range . location
progressCall : ^ ( double _progress ) {
if ( _progress < 0 ) return ;
callback ( progressbase + progresschunk * _progress ) ;
} ] ;
i + = range . length ;
progress + = 100.0 * progresschunk ;
callback ( progress ) ;
} ] ;
callback ( -1 ) ;
2021-12-24 06:01:21 -03:00
}
2022-02-07 02:49:27 -03:00
- ( void ) playlistRemoveTracks : ( int64_t ) index forCount : ( int64_t ) count progressCall : ( void ( ^ ) ( double ) ) callback {
if ( ! count ) {
callback ( -1 ) ;
return ;
}
sqlite3_stmt * st = stmt [ stmt_select _playlist _range ] ;
if ( sqlite3_reset ( st ) ||
sqlite3_bind _int64 ( st , select_playlist _range _in _id _low , index ) ||
sqlite3_bind _int64 ( st , select_playlist _range _in _id _high , index + count - 1 ) ) {
callback ( -1 ) ;
return ;
}
callback ( 0 ) ;
double progress = 0 ;
double progressstep = 100.0 / ( ( double ) count ) ;
int rc = sqlite3_step ( st ) ;
while ( rc = = SQLITE_ROW ) {
int64_t trackId = sqlite3_column _int64 ( st , select_playlist _range _out _track _id ) ;
[ self removeTrack : trackId ] ;
rc = sqlite3_step ( st ) ;
progress + = progressstep ;
callback ( progress ) ;
}
callback ( 100 ) ;
sqlite3_reset ( st ) ;
if ( rc ! = SQLITE_DONE ) {
return ;
}
st = stmt [ stmt_remove _playlist _by _range ] ;
if ( sqlite3_reset ( st ) ||
sqlite3_bind _int64 ( st , remove_playlist _by _range _in _low , index ) ||
sqlite3_bind _int64 ( st , remove_playlist _by _range _in _high , index + count - 1 ) ||
sqlite3_step ( st ) ! = SQLITE_DONE ||
sqlite3_reset ( st ) ) {
callback ( -1 ) ;
return ;
}
st = stmt [ stmt_decrement _playlist _for _removal ] ;
if ( sqlite3_reset ( st ) ||
sqlite3_bind _int64 ( st , decrement_playlist _for _removal _in _count , count ) ||
sqlite3_bind _int64 ( st , decrement_playlist _for _removal _in _index , index + count ) ||
sqlite3_step ( st ) ! = SQLITE_DONE ||
sqlite3_reset ( st ) ) {
callback ( -1 ) ;
return ;
}
NSMutableArray * items = [ [ NSMutableArray alloc ] init ] ;
for ( int64_t i = index , j = index + count ; i < j ; + + i ) {
[ items addObject : [ NSNumber numberWithInteger : i ] ] ;
}
[ self queueRemovePlaylistItems : items ] ;
callback ( -1 ) ;
2021-12-24 06:01:21 -03:00
}
2022-02-07 02:49:27 -03:00
- ( void ) playlistRemoveTracksAtIndexes : ( NSIndexSet * ) indexes progressCall : ( void ( ^ ) ( double ) ) callback {
if ( ! indexes ) {
callback ( -1 ) ;
return ;
}
[ databaseMirror removeObjectsAtIndexes : indexes ] ;
__block int64_t total_count = 0 ;
[ indexes enumerateRangesUsingBlock : ^ ( NSRange range , BOOL * _Nonnull stop ) {
total_count + = range . length ;
} ] ;
__block int64_t i = 0 ;
__block double progress = 0 ;
callback ( progress ) ;
[ indexes enumerateRangesUsingBlock : ^ ( NSRange range , BOOL * _Nonnull stop ) {
double progresschunk = ( double ) range . length / ( double ) total_count ;
double progressbase = progress ;
[ self playlistRemoveTracks : ( range . location - i )
forCount : range . length
progressCall : ^ ( double _progress ) {
if ( _progress < 0 ) return ;
callback ( progressbase + progresschunk * _progress ) ;
} ] ;
i + = range . length ;
progress + = 100.0 * progresschunk ;
callback ( progress ) ;
} ] ;
callback ( -1 ) ;
2021-12-24 06:01:21 -03:00
}
2022-02-07 02:49:27 -03:00
- ( PlaylistEntry * ) playlistGetCachedItem : ( int64_t ) index {
if ( index >= 0 && index < [ databaseMirror count ] )
return [ [ databaseMirror objectAtIndex : index ] copy ] ;
else
return nil ;
2022-01-15 03:42:57 -03:00
}
2022-02-07 02:49:27 -03:00
- ( PlaylistEntry * ) playlistGetItem : ( int64_t ) index {
PlaylistEntry * entry = [ [ PlaylistEntry alloc ] init ] ;
sqlite3_stmt * st = stmt [ stmt_select _playlist ] ;
if ( sqlite3_reset ( st ) ||
sqlite3_bind _int64 ( st , select_playlist _in _id , index ) ) {
return entry ;
}
int rc = sqlite3_step ( st ) ;
if ( rc ! = SQLITE_ROW && rc ! = SQLITE_DONE ) {
return entry ;
}
if ( rc = = SQLITE_ROW ) {
int64_t trackId = sqlite3_column _int64 ( st , select_playlist _out _track _id ) ;
int64_t entryId = sqlite3_column _int64 ( st , select_playlist _out _entry _id ) ;
entry = [ self getTrack : trackId ] ;
[ entry setIndex : index ] ;
[ entry setEntryId : entryId ] ;
}
sqlite3_reset ( st ) ;
return entry ;
2021-12-24 06:01:21 -03:00
}
2022-02-07 02:49:27 -03:00
- ( int64_t ) playlistGetCount {
sqlite3_stmt * st = stmt [ stmt_count _playlist ] ;
if ( sqlite3_reset ( st ) ||
sqlite3_step ( st ) ! = SQLITE_ROW ) {
return 0 ;
}
int64_t ret = sqlite3_column _int64 ( st , count_playlist _out _count ) ;
sqlite3_reset ( st ) ;
return ret ;
2021-12-24 06:01:21 -03:00
}
2022-02-07 02:49:27 -03:00
- ( void ) playlistMoveObjectsInArrangedObjectsFromIndexes : ( NSIndexSet * ) indexSet toIndex : ( NSUInteger ) insertIndex progressCall : ( void ( ^ ) ( double ) ) callback {
__block NSUInteger rangeCount = 0 ;
__block NSUInteger firstIndex = 0 ;
[ indexSet enumerateRangesUsingBlock : ^ ( NSRange range , BOOL * _Nonnull stop ) {
if ( + + rangeCount = = 1 )
firstIndex = range . location ;
} ] ;
if ( rangeCount = = 1 &&
( insertIndex >= firstIndex &&
insertIndex < firstIndex + [ indexSet count ] ) ) // Null operation
return ;
NSArray * objects = databaseMirror ;
NSUInteger index = [ indexSet lastIndex ] ;
NSUInteger aboveInsertIndexCount = 0 ;
id object ;
NSUInteger removeIndex ;
callback ( 0 ) ;
double progress = 0 ;
double progressstep = 100.0 / [ indexSet count ] ;
while ( NSNotFound ! = index ) {
if ( index >= insertIndex ) {
removeIndex = index + aboveInsertIndexCount ;
aboveInsertIndexCount + = 1 ;
} else {
removeIndex = index ;
insertIndex - = 1 ;
}
object = objects [ removeIndex ] ;
sqlite3_stmt * st = stmt [ stmt_remove _playlist _by _range ] ;
if ( sqlite3_reset ( st ) ||
sqlite3_bind _int64 ( st , remove_playlist _by _range _in _low , removeIndex ) ||
sqlite3_bind _int64 ( st , remove_playlist _by _range _in _high , removeIndex ) ||
sqlite3_step ( st ) ! = SQLITE_DONE )
break ;
st = stmt [ stmt_decrement _playlist _for _removal ] ;
if ( sqlite3_reset ( st ) ||
sqlite3_bind _int64 ( st , decrement_playlist _for _removal _in _count , 1 ) ||
sqlite3_bind _int64 ( st , decrement_playlist _for _removal _in _index , removeIndex + 1 ) ||
sqlite3_step ( st ) ! = SQLITE_DONE )
break ;
[ databaseMirror removeObjectAtIndex : removeIndex ] ;
st = stmt [ stmt_increment _playlist _for _insert ] ;
if ( sqlite3_reset ( st ) ||
sqlite3_bind _int64 ( st , increment_playlist _for _insert _in _count , 1 ) ||
sqlite3_bind _int64 ( st , increment_playlist _for _insert _in _index , insertIndex ) ||
sqlite3_step ( st ) ! = SQLITE_DONE )
break ;
st = stmt [ stmt_add _playlist ] ;
if ( sqlite3_reset ( st ) ||
sqlite3_bind _int64 ( st , add_playlist _in _entry _index , insertIndex ) ||
sqlite3_bind _int64 ( st , add_playlist _in _track _id , [ object dbIndex ] ) ||
sqlite3_step ( st ) ! = SQLITE_DONE )
break ;
[ databaseMirror insertObject : object atIndex : insertIndex ] ;
index = [ indexSet indexLessThanIndex : index ] ;
progress + = progressstep ;
callback ( progress ) ;
}
callback ( -1 ) ;
2022-01-24 09:42:26 -03:00
}
- ( void ) playlistMoveObjectsFromIndex : ( NSUInteger ) fromIndex
toArrangedObjectIndexes : ( NSIndexSet * ) indexSet
progressCall : ( void ( ^ ) ( double ) ) callback {
2022-02-07 02:49:27 -03:00
__block NSUInteger rangeCount = 0 ;
__block NSUInteger firstIndex = 0 ;
__block NSUInteger _fromIndex = fromIndex ;
[ indexSet enumerateRangesUsingBlock : ^ ( NSRange range , BOOL * _Nonnull stop ) {
if ( + + rangeCount = = 1 )
firstIndex = range . location ;
if ( _fromIndex >= range . location ) {
if ( _fromIndex < range . location + range . length )
_fromIndex = range . location ;
else
_fromIndex - = range . length ;
}
} ] ;
if ( rangeCount = = 1 &&
( fromIndex >= firstIndex &&
fromIndex < firstIndex + [ indexSet count ] ) ) // Null operation
return ;
callback ( 0 ) ;
double progress = 0 ;
double progressstep = 50.0 / [ indexSet count ] ;
fromIndex = _fromIndex ;
NSArray * objects = [ databaseMirror subarrayWithRange : NSMakeRange ( fromIndex , [ indexSet count ] ) ] ;
NSUInteger index = [ indexSet firstIndex ] ;
NSUInteger itemIndex = 0 ;
id object ;
sqlite3_stmt * st ;
fromIndex + = [ objects count ] ;
{
st = stmt [ stmt_remove _playlist _by _range ] ;
if ( sqlite3_reset ( st ) ||
sqlite3_bind _int64 ( st , remove_playlist _by _range _in _low , fromIndex ) ||
sqlite3_bind _int64 ( st , remove_playlist _by _range _in _high , fromIndex + [ indexSet count ] - 1 ) ||
sqlite3_step ( st ) ! = SQLITE_DONE ) {
callback ( -1 ) ;
return ;
}
st = stmt [ stmt_decrement _playlist _for _removal ] ;
if ( sqlite3_reset ( st ) ||
sqlite3_bind _int64 ( st , decrement_playlist _for _removal _in _count , [ indexSet count ] ) ||
sqlite3_bind _int64 ( st , decrement_playlist _for _removal _in _index , fromIndex ) ||
sqlite3_step ( st ) ! = SQLITE_DONE ) {
callback ( -1 ) ;
return ;
}
[ databaseMirror removeObjectsInRange : NSMakeRange ( fromIndex , [ indexSet count ] ) ] ;
progress + = progressstep ;
callback ( progress ) ;
}
while ( NSNotFound ! = index ) {
object = objects [ itemIndex + + ] ;
st = stmt [ stmt_increment _playlist _for _insert ] ;
if ( sqlite3_reset ( st ) ||
sqlite3_bind _int64 ( st , increment_playlist _for _insert _in _count , 1 ) ||
sqlite3_bind _int64 ( st , increment_playlist _for _insert _in _index , index ) ||
sqlite3_step ( st ) ! = SQLITE_DONE )
break ;
st = stmt [ stmt_add _playlist ] ;
if ( sqlite3_reset ( st ) ||
sqlite3_bind _int64 ( st , add_playlist _in _entry _index , index ) ||
sqlite3_bind _int64 ( st , add_playlist _in _track _id , [ object dbIndex ] ) ||
sqlite3_step ( st ) ! = SQLITE_DONE )
break ;
[ databaseMirror insertObject : object atIndex : index ] ;
index = [ indexSet indexGreaterThanIndex : index ] ;
progress + = progressstep ;
callback ( progress ) ;
}
callback ( -1 ) ;
2021-12-24 06:01:21 -03:00
}
2022-02-07 02:49:27 -03:00
- ( void ) syncPlaylistEntries : ( NSArray * ) entries progressCall : ( void ( ^ ) ( double ) ) callback {
if ( ! entries || ! [ entries count ] ) {
callback ( -1 ) ;
return ;
}
int64_t count = [ self playlistGetCount ] ;
if ( count ! = [ entries count ] ) {
callback ( -1 ) ;
return ;
}
callback ( 0 ) ;
double progress = 0 ;
double progressstep = 100.0 / ( double ) ( count ) ;
sqlite3_stmt * st = stmt [ stmt_update _playlist ] ;
for ( size_t i = 0 ; i < count ; + + i ) {
PlaylistEntry * newpe = [ entries objectAtIndex : i ] ;
PlaylistEntry * oldpe = [ databaseMirror objectAtIndex : i ] ;
progress + = progressstep ;
if ( ( [ oldpe index ] ! = i ||
[ oldpe dbIndex ] ! = [ newpe dbIndex ] ) &&
[ oldpe entryId ] = = [ newpe entryId ] ) {
if ( sqlite3_reset ( st ) ||
sqlite3_bind _int64 ( st , update_playlist _in _id , [ oldpe entryId ] ) ||
sqlite3_bind _int64 ( st , update_playlist _in _entry _index , i ) ||
sqlite3_bind _int64 ( st , update_playlist _in _track _id , [ newpe dbIndex ] ) ||
sqlite3_step ( st ) ! = SQLITE_ROW ||
sqlite3_reset ( st ) ) {
callback ( -1 ) ;
return ;
}
[ databaseMirror replaceObjectAtIndex : i withObject : [ newpe copy ] ] ;
callback ( progress ) ;
}
}
sqlite3_reset ( st ) ;
callback ( -1 ) ;
2021-12-24 06:01:21 -03:00
}
2022-02-07 02:49:27 -03:00
- ( void ) queueAddItem : ( int64_t ) playlistIndex {
int64_t count = [ self queueGetCount ] ;
sqlite3_stmt * st = stmt [ stmt_add _queue ] ;
if ( sqlite3_reset ( st ) ||
sqlite3_bind _int64 ( st , add_queue _in _queue _index , count ) ||
sqlite3_bind _int64 ( st , add_queue _in _entry _id , playlistIndex ) ||
sqlite3_step ( st ) ! = SQLITE_DONE ||
sqlite3_reset ( st ) ) {
return ;
}
2021-12-24 06:01:21 -03:00
}
2022-02-07 02:49:27 -03:00
- ( void ) queueAddItems : ( NSArray * ) playlistIndexes {
int64_t count = [ self queueGetCount ] ;
sqlite3_stmt * st = stmt [ stmt_add _queue ] ;
for ( NSNumber * index in playlistIndexes ) {
if ( sqlite3_reset ( st ) ||
sqlite3_bind _int64 ( st , add_queue _in _queue _index , count ) ||
sqlite3_bind _int64 ( st , add_queue _in _entry _id , [ index integerValue ] ) ||
sqlite3_step ( st ) ! = SQLITE_DONE ) {
break ;
}
+ + count ;
}
sqlite3_reset ( st ) ;
2021-12-24 06:01:21 -03:00
}
2022-02-07 02:49:27 -03:00
- ( void ) queueRemoveItem : ( int64_t ) queueIndex {
sqlite3_stmt * st = stmt [ stmt_remove _queue _by _index ] ;
if ( sqlite3_reset ( st ) ||
sqlite3_bind _int64 ( st , remove_queue _by _index _in _queue _index , queueIndex ) ||
sqlite3_step ( st ) ! = SQLITE_DONE ||
sqlite3_reset ( st ) ) {
return ;
}
st = stmt [ stmt_decrement _queue _for _removal ] ;
if ( sqlite3_reset ( st ) ||
sqlite3_bind _int64 ( st , decrement_queue _for _removal _in _index , queueIndex + 1 ) ||
sqlite3_step ( st ) ! = SQLITE_DONE ||
sqlite3_reset ( st ) ) {
return ;
}
2021-12-24 06:01:21 -03:00
}
2022-02-07 02:49:27 -03:00
- ( void ) queueRemovePlaylistItems : ( NSArray * ) playlistIndexes {
sqlite3_stmt * st = stmt [ stmt_select _queue _by _playlist _entry ] ;
for ( NSNumber * index in playlistIndexes ) {
if ( sqlite3_reset ( st ) ||
sqlite3_bind _int64 ( st , select_queue _by _playlist _entry _in _id , [ index integerValue ] ) ||
sqlite3_step ( st ) ! = SQLITE_ROW ) {
break ;
}
int64_t queueIndex = sqlite3_column _int64 ( st , select_queue _by _playlist _entry _out _queue _index ) ;
sqlite3_reset ( st ) ;
[ self queueRemoveItem : queueIndex ] ;
}
2021-12-24 06:01:21 -03:00
}
2022-02-07 02:49:27 -03:00
- ( int64_t ) queueGetEntry : ( int64_t ) queueIndex {
sqlite3_stmt * st = stmt [ stmt_select _queue ] ;
if ( sqlite3_reset ( st ) ||
sqlite3_bind _int64 ( st , select_queue _in _id , queueIndex ) ) {
return -1 ;
}
int rc = sqlite3_step ( st ) ;
if ( rc ! = SQLITE_ROW && rc ! = SQLITE_DONE ) {
sqlite3_reset ( st ) ;
return -1 ;
}
int64_t ret = -1 ;
if ( rc = = SQLITE_ROW ) {
ret = sqlite3_column _int64 ( st , select_queue _out _entry _id ) ;
}
sqlite3_reset ( st ) ;
return ret ;
2021-12-24 06:01:21 -03:00
}
2022-02-07 02:49:27 -03:00
- ( void ) queueEmpty {
sqlite3_stmt * st = stmt [ stmt_remove _queue _all ] ;
if ( sqlite3_reset ( st ) ||
sqlite3_step ( st ) ! = SQLITE_DONE ||
sqlite3_reset ( st ) ) {
return ;
}
2022-01-07 02:03:31 -03:00
}
2022-02-07 02:49:27 -03:00
- ( int64_t ) queueGetCount {
sqlite3_stmt * st = stmt [ stmt_count _queue ] ;
if ( sqlite3_reset ( st ) ||
sqlite3_step ( st ) ! = SQLITE_ROW ) {
return 0 ;
}
int64_t ret = sqlite3_column _int64 ( st , count_queue _out _count ) ;
sqlite3_reset ( st ) ;
return ret ;
2021-12-24 06:01:21 -03:00
}
@ end