From 1d750b25a9d67a98dd45d2fb2e958594f8914dc6 Mon Sep 17 00:00:00 2001 From: Anton Romanov Date: Wed, 9 Sep 2015 22:49:16 -0700 Subject: [PATCH 01/13] First attempt at 0.9 js resolver API --- data/js/tomahawk.js | 703 +++++++++++++++++- .../resolvers/JSResolverHelper.cpp | 8 +- src/libtomahawk/resolvers/ScriptAccount.cpp | 2 +- .../resolvers/ScriptCommand_AllTracks.cpp | 2 +- src/libtomahawk/resolvers/ScriptEngine.cpp | 2 + src/tomahawk/TomahawkApp.cpp | 4 + 6 files changed, 707 insertions(+), 14 deletions(-) diff --git a/data/js/tomahawk.js b/data/js/tomahawk.js index 156fcb86c..5ea630c67 100644 --- a/data/js/tomahawk.js +++ b/data/js/tomahawk.js @@ -237,24 +237,50 @@ var TomahawkResolver = { } }; -Tomahawk.Resolver = {}; -Tomahawk.Resolver.Promise = Tomahawk.extend(TomahawkResolver, { + +Tomahawk.Resolver = Tomahawk.extend(TomahawkResolver, { + saveUserConfig: function () { + window.localStorage[this.scriptPath()] = JSON.stringify(Tomahawk.resolverData().config); + this.newConfigSaved(Tomahawk.resolverData().config); + }, _adapter_resolve: function (qid, artist, album, title) { - Promise.resolve(this.resolve(artist, album, title)).then(function(results){ + var that = this; + var collectionPromises = []; + Tomahawk.collections.forEach(function(col) { + if(col.resolve) + collectionPromises.push(col.resolve({artist: artist, album: album, track:title})); + }); + Promise.all(collectionPromises).then(function(collectionResults){ + var merged = []; + return merged.concat.apply(merged,collectionResults); + }).then(function(collectionResults) { + Promise.resolve(that.resolve({artist: artist, album: album, track:title})).then(function(results){ + Tomahawk.addTrackResults({ + 'qid': qid, + 'results': results.concat(collectionResults) + }); + }); + }); + }, + + _adapter_getStreamUrl: function (params) { + Promise.resolve(this.getStreamUrl(params)).then(function(result){ + Tomahawk.reportStreamUrl(params.qid, result.url, result.headers); + }); + }, + + _adapter_search: function (qid, query) + { + Promise.resolve(this.search({query:query})).then(function(results){ Tomahawk.addTrackResults({ 'qid': qid, 'results': results }); }); }, - - _adapter_search: function (qid, query) - { - Promise.resolve(this.search(query)).then(function(results){ - Tomahawk.addTrackResults({ - 'qid': qid, - 'results': results - }); + _adapter_testConfig: function (config) { + return Promise.resolve(this.testConfig(config)).then(function() { + return { result: Tomahawk.ConfigTestResultType.Success }; }); } }); @@ -558,6 +584,9 @@ Tomahawk.ajax = function(url, settings) { settings.errorHandler = reject; Tomahawk.asyncRequest(settings.url, resolve, settings.headers, settings); }).then(function(xhr) { + if (settings.rawResponse) { + return xhr; + } var responseText = xhr.responseText; var contentType; if (settings.dataType === 'json') { @@ -762,6 +791,10 @@ Tomahawk.PluginManager = { }, registerPlugin: function (type, object) { this.objects[this.identifyObject(object)] = object; + if (type === 'collection') + { + Tomahawk.collections.push(object); + } Tomahawk.log("registerPlugin: " + type + " id: " + object.id); Tomahawk.registerScriptPlugin(type, object.id); @@ -776,6 +809,9 @@ Tomahawk.PluginManager = { resolve: [], invokeSync: function (requestId, objectId, methodName, params) { + Tomahawk.log('invokeSync ' + methodName); + Tomahawk.log('invokeSync params ' + JSON.stringify(params)); + Tomahawk.log(Tomahawk.resolver.instance.appVersion); if (!Tomahawk.resolver.instance.apiVersion || Tomahawk.resolver.instance.apiVersion < 0.9) { if (methodName === 'artistAlbums') { methodName = 'albums'; @@ -813,6 +849,8 @@ Tomahawk.PluginManager = { } } + Tomahawk.log(methodName); + Tomahawk.log(JSON.stringify(params)); return this.objects[objectId][methodName](params); } @@ -820,6 +858,8 @@ Tomahawk.PluginManager = { }, invoke: function (requestId, objectId, methodName, params ) { + Tomahawk.log('invoke ' + methodName); + Tomahawk.log('invoke params ' + JSON.stringify(params)); Promise.resolve(this.invokeSync(requestId, objectId, methodName, params)).then(function (result) { if (typeof result === 'object') { Tomahawk.reportScriptJobResults({ @@ -1103,11 +1143,652 @@ Tomahawk.Country = { LatinAmericaAndTheCaribbean: 246 }; +Tomahawk.collections = []; + Tomahawk.Collection = { BrowseCapability: { Artists: 1, Albums: 2, Tracks: 4 + }, + + cachedDbs: {}, + + Transaction: function (collection, id) { + + this.ensureDb = function () { + var that = this; + + return new RSVP.Promise(function (resolve, reject) { + if (!collection.cachedDbs.hasOwnProperty(id)) { + Tomahawk.log("Opening database"); + var estimatedSize = 5 * 1024 * 1024; // 5MB + collection.cachedDbs[id] = + openDatabase(id + "_collection", "", "Collection", estimatedSize); + + collection.cachedDbs[id].transaction(function (tx) { + Tomahawk.log("Creating initial db tables"); + tx.executeSql("CREATE TABLE IF NOT EXISTS artists(" + + "_id INTEGER PRIMARY KEY AUTOINCREMENT," + + "artist TEXT ," + + "artistDisambiguation TEXT," + + "UNIQUE (artist, artistDisambiguation) ON CONFLICT IGNORE)", []); + tx.executeSql("CREATE TABLE IF NOT EXISTS albumArtists(" + + "_id INTEGER PRIMARY KEY AUTOINCREMENT," + + "albumArtist TEXT ," + + "albumArtistDisambiguation TEXT," + + "UNIQUE (albumArtist, albumArtistDisambiguation) ON CONFLICT IGNORE)", + []); + tx.executeSql("CREATE TABLE IF NOT EXISTS albums(" + + "_id INTEGER PRIMARY KEY AUTOINCREMENT," + + "album TEXT," + + "albumArtistId INTEGER," + + "UNIQUE (album, albumArtistId) ON CONFLICT IGNORE," + + "FOREIGN KEY(albumArtistId) REFERENCES albumArtists(_id))", []); + tx.executeSql("CREATE TABLE IF NOT EXISTS artistAlbums(" + + "_id INTEGER PRIMARY KEY AUTOINCREMENT," + + "albumId INTEGER," + + "artistId INTEGER," + + "UNIQUE (albumId, artistId) ON CONFLICT IGNORE," + + "FOREIGN KEY(albumId) REFERENCES albums(_id)," + + "FOREIGN KEY(artistId) REFERENCES artists(_id))", []); + tx.executeSql("CREATE TABLE IF NOT EXISTS tracks(" + + "_id INTEGER PRIMARY KEY AUTOINCREMENT," + + "track TEXT," + + "artistId INTEGER," + + "albumId INTEGER," + + "url TEXT," + + "duration INTEGER," + + "albumPos INTEGER," + + "linkUrl TEXT," + + "UNIQUE (track, artistId, albumId) ON CONFLICT IGNORE," + + "FOREIGN KEY(artistId) REFERENCES artists(_id)," + + "FOREIGN KEY(albumId) REFERENCES albums(_id))", []); + }); + + } + resolve(collection.cachedDbs[id]); + }); + }; + + this.beginTransaction = function () { + var that = this; + return this.ensureDb().then(function (db) { + return new RSVP.Promise(function (resolve, reject) { + that.db = db; + that.statements = []; + resolve(); + }) + }); + }; + + this.execDefferedStatements = function (resolve, reject) { + var that = this; + that.stmtsToResolve = that.statements.length; + that.results = that.statements.slice(); + Tomahawk.log('Executing ' + that.stmtsToResolve + ' deffered SQL statements in transaction'); + return new Promise(function (resolve, reject) { + if (that.statements.length == 0) + resolve([]); + else{ + that.db.transaction(function (tx) { + for (var i = 0; i < that.statements.length; ++i) + { + var stmt = that.statements[i]; + var originalI = i; + tx.executeSql(stmt.statement, stmt.args, + function (tx, results) { + that.stmtsToResolve--; + if (typeof that.statements[originalI].map !== 'undefined') + { + var map = that.statements[originalI].map; + that.results[originalI] = []; + for (var ii = 0; ii < results.rows.length; ii++) { + that.results[originalI].push(map( + results.rows.item(ii) + )); + } + } + else + that.results[originalI] = results; + if(that.stmtsToResolve == 0) + { + that.statements = []; + resolve(that.results); + } + }, function (tx, error) { + Tomahawk.log("Error in tx.executeSql: " + error.code + " - " + + error.message); + that.statements = []; + reject(error); + } + ); + } + }); + } + }); + }, + + this.sql = function (sqlStatement, sqlArgs, mapFunction) { + this.statements.push({statement: sqlStatement, args: sqlArgs, map: mapFunction}); + //var that = this; + //return new RSVP.Promise(function (resolve, reject) { + //that.tx.executeSql(sqlStatement, sqlArgs, + //function (tx, results) { + //resolve(results); + //}, function (tx, error) { + //Tomahawk.log("Error in tx.executeSql: " + error.code + " - " + //+ error.message); + //reject(error); + //}); + //}) + }; + + this.sqlSelect = function (table, mapResults, fields, where, join) { + var whereKeys = []; + var whereValues = []; + for (var whereKey in where) { + if (where.hasOwnProperty(whereKey)) { + whereKeys.push(table + "." + whereKey + " = ?"); + whereValues.push(where[whereKey]); + } + } + var whereString = whereKeys.length > 0 ? " WHERE " + whereKeys.join(" AND ") : ""; + + var joinString = ""; + for (var i = 0; join && i < join.length; i++) { + var joinConditions = []; + for (var joinKey in join[i].conditions) { + if (join[i].conditions.hasOwnProperty(joinKey)) { + joinConditions.push(table + "." + joinKey + " = " + join[i].table + "." + + join[i].conditions[joinKey]); + } + } + joinString += " INNER JOIN " + join[i].table + " ON " + + joinConditions.join(" AND "); + } + + var fieldsString = fields && fields.length > 0 ? fields.join(", ") : "*"; + var statement = "SELECT " + fieldsString + " FROM " + table + joinString + whereString; + return this.sql(statement, whereValues, mapResults); + }; + + this.sqlInsert = function (table, fields) { + var fieldsKeys = []; + var fieldsValues = []; + var valuesString = ""; + for (var key in fields) { + fieldsKeys.push(key); + fieldsValues.push(fields[key]); + if (valuesString.length > 0) { + valuesString += ", "; + } + valuesString += "?"; + } + var statement = "INSERT INTO " + table + " (" + fieldsKeys.join(", ") + ") VALUES (" + + valuesString + ")"; + return this.sql(statement, fieldsValues); + }; + + this.sqlDrop = function (table) { + var statement = "DROP TABLE IF EXISTS " + table; + return this.sql(statement, []); + }; + + }, + addTracks: function (params) { + var id = params.id; + var tracks = params.tracks; + + var cachedAlbumArtists = {}, + cachedArtists = {}, + cachedAlbums = {}, + cachedArtistIds = {}, + cachedAlbumIds = {}; + + var t = new Tomahawk.Collection.Transaction(this, id); + return t.beginTransaction().then(function () { + // First we insert all artists and albumArtists + t.sqlInsert("artists", { + artist: "Various Artists", + artistDisambiguation: "" + }); + for (var i = 0; i < tracks.length; i++) { + tracks[i].track = tracks[i].track || ""; + tracks[i].album = tracks[i].album || ""; + tracks[i].artist = tracks[i].artist || ""; + tracks[i].artistDisambiguation = tracks[i].artistDisambiguation || ""; + tracks[i].albumArtist = tracks[i].albumArtist || ""; + tracks[i].albumArtistDisambiguation = tracks[i].albumArtistDisambiguation || ""; + (function (track) { + t.sqlInsert("artists", { + artist: track.artist, + artistDisambiguation: track.artistDisambiguation + }); + t.sqlInsert("albumArtists", { + albumArtist: track.albumArtist, + albumArtistDisambiguation: track.albumArtistDisambiguation + }); + })(tracks[i]); + } + return t.execDefferedStatements(); + }).then(function () { + // Get all artists' and albumArtists' db ids + t.sqlSelect("albumArtists", function(r) { + return { + albumArtist: r.albumArtist, + albumArtistDisambiguation: r.albumArtistDisambiguation, + _id: r._id + }; + }); + t.sqlSelect("artists", function(r) { + return { + artist: r.artist, + artistDisambiguation: r.artistDisambiguation, + _id: r._id + }; + }); + return t.execDefferedStatements(); + }).then(function (resultsArray) { + // Store the db ids in a map + var i, row, albumArtists = {}; + for (i = 0; i < resultsArray[0].length; i++) { + row = resultsArray[0][i]; + albumArtists[row.albumArtist + "♣" + row.albumArtistDisambiguation] = row._id; + } + for (i = 0; i < resultsArray[1].length; i++) { + row = resultsArray[1][i]; + cachedArtists[row.artist + "♣" + row.artistDisambiguation] = row._id; + cachedArtistIds[row._id] = { + artist: row.artist, + artistDisambiguation: row.artistDisambiguation + }; + } + + for (i = 0; i < tracks.length; i++) { + var track = tracks[i]; + var album = cachedAlbumArtists[track.album]; + if (!album) { + album = cachedAlbumArtists[track.album] = { + artists: {} + }; + } + album.artists[track.artist] = true; + var artistCount = Object.keys(album.artists).length; + if (artistCount == 1) { + album.albumArtistId = + cachedArtists[track.artist + "♣" + track.artistDisambiguation]; + } else if (artistCount == 2) { + album.albumArtistId = cachedArtists["Various Artists♣"]; + } + } + }).then(function () { + // Insert all albums + var promises = []; + for (var i = 0; i < tracks.length; i++) { + (function (track) { + var albumArtistId = cachedAlbumArtists[track.album].albumArtistId; + t.sqlInsert("albums", { + album: track.album, + albumArtistId: albumArtistId + }); + })(tracks[i]); + } + return t.execDefferedStatements(); + }).then(function () { + // Get the albums' db ids + t.sqlSelect("albums", function(r) { + return { + album: r.album, + albumArtistId: r.albumArtistId, + _id: r._id + }; + }); + return t.execDefferedStatements(); + }).then(function (results) { + // Store the db ids in a map + results = results[0]; + for (var i = 0; i < results.length; i++) { + var row = results[i]; + cachedAlbums[row.album + "♣" + row.albumArtistId] = row._id; + cachedAlbumIds[row._id] = { + album: row.album, + albumArtistId: row.albumArtistId + }; + } + }).then(function () { + // Now we are ready to insert the tracks + var promises = []; + for (var i = 0; i < tracks.length; i++) { + (function (track) { + // Get all relevant ids that we stored in the previous steps + var artistId = cachedArtists[track.artist + "♣" + track.artistDisambiguation]; + var albumArtistId = cachedAlbumArtists[track.album].albumArtistId; + var albumId = cachedAlbums[track.album + "♣" + albumArtistId]; + // Insert the artist <=> album relations + t.sqlInsert("artistAlbums", { + albumId: albumId, + artistId: artistId + }); + // Insert the tracks + t.sqlInsert("tracks", { + track: track.track, + artistId: artistId, + albumId: albumId, + url: track.url, + duration: track.duration, + linkUrl: track.linkUrl, + albumPos: track.albumPos + }); + })(tracks[i]); + } + return t.execDefferedStatements(); + }).then(function () { + var resultMap = function (r) { + return { + _id : r._id, + artistId: r.artistId, + albumId: r.albumId, + track: r.track + }; + }; + // Get the tracks' db ids + t.sqlSelect("tracks", resultMap, ["_id", "artistId", "albumId", "track"]); + return t.execDefferedStatements(); + }).then(function (results) { + Tomahawk.log("Added " + results[0].length + " tracks to collection '" + id + "'"); + // Add the db ids together with the basic metadata to the fuzzy index list + var fuzzyIndexList = []; + for (var i = 0; i < results[0].length; i++) { + var row = results[0][i]; + fuzzyIndexList.push({ + id: row._id, + artist: cachedArtistIds[row.artistId].artist, + album: cachedAlbumIds[row.albumId].album, + track: row.track + }); + } + Tomahawk.createFuzzyIndex(fuzzyIndexList); + }); + }, + + wipe: function (params) { + var id = params.id; + + var that = this; + + var t = new Tomahawk.Collection.Transaction(this, id); + return t.beginTransaction().then(function () { + t.sqlDrop("artists"); + t.sqlDrop("albumArtists"); + t.sqlDrop("albums"); + t.sqlDrop("artistAlbums"); + t.sqlDrop("tracks"); + return t.execDefferedStatements(); + }).then(function () { + return new Promise(function (resolve, reject) { + that.cachedDbs[id].changeVersion(that.cachedDbs[id].version, "", null, + function (err) { + if (console.error) { + console.error("Error!: %o", err); + } + reject(); + }, function () { + delete that.cachedDbs[id]; + Tomahawk.deleteFuzzyIndex(id); + Tomahawk.log("Wiped collection '" + id + "'"); + resolve(); + }); + }); + }); + }, + + resolve: function(params) { + var id = params.id; + if(typeof id === 'undefined') + id = this.settings.id; + Tomahawk.log('called resolve'); + var resultIds = Tomahawk.resolveFromFuzzyIndex(params.artist, params.album, params.track); + var t = new Tomahawk.Collection.Transaction(this, id); + return t.beginTransaction().then(function () { + var mapFn = function(row) { + return { + artist: row.artist, + artistDisambiguation: row.artistDisambiguation, + album: row.album, + track: row.track, + duration: row.duration, + url: row.url, + linkUrl: row.linkUrl, + albumPos: row.albumPos + }; + }; + for (var idx = 0; resultIds && idx < resultIds.length; idx++) { + var trackid = resultIds[idx][0]; + var where = { _id : trackid }; + t.sqlSelect("tracks",mapFn, + ["artist", "artistDisambiguation", "album", "track", "duration", "url", "linkUrl"], + where, [ + { + table: "artists", + conditions: { + artistId: "_id" + } + }, { + table: "albums", + conditions: { + albumId: "_id" + } + } + ] + ); + } + return t.execDefferedStatements(); + }).then(function (results) { + var merged = []; + return merged.concat.apply(merged, + results.map(function(e){ + //every result has one track + return e[0]; + })); + }); + return this.tracks({}); + }, + + search: function() { + //TODO + Tomahawk.log('called search'); + }, + + tracks: function (params, where) { + //TODO filter/where support + var id = params.id; + if(typeof id === 'undefined') + id = this.settings.id; + + var t = new Tomahawk.Collection.Transaction(this, id); + return t.beginTransaction().then(function () { + var mapFn = function(row) { + return { + artist: row.artist, + artistDisambiguation: row.artistDisambiguation, + album: row.album, + track: row.track, + duration: row.duration, + url: row.url, + linkUrl: row.linkUrl, + albumPos: row.albumPos + }; + }; + t.sqlSelect("tracks",mapFn, + ["artist", "artistDisambiguation", "album", "track", "duration", "url", "linkUrl"], + where, [ + { + table: "artists", + conditions: { + artistId: "_id" + } + }, { + table: "albums", + conditions: { + albumId: "_id" + } + } + ] + ); + return t.execDefferedStatements(); + }).then(function (results) { + return {results: results[0]}; + }); + }, + + //albums: function (params, where) { + ////TODO filter/where support + //var id = params.id; + //if(typeof id === 'undefined') + //id = this.settings.id; + + //var t = new Tomahawk.Collection.Transaction(this, id); + //return t.beginTransaction().then(function () { + //var mapFn = function(row) { + //return { + //albumArtist: row.artist, + //albumArtistDisambiguation: row.artistDisambiguation, + //album: row.album + //}; + //}; + //t.sqlSelect("albums",mapFn, + //["album", "artist", "artistDisambiguation"], + //where, [ + //{ + //table: "artists", + //conditions: { + //albumArtistId: "_id" + //} + //} + //] + //); + //return t.execDefferedStatements(); + //}).then(function (results) { + //return { + //artists: results[0].map(function(i){ return i.albumArtist;}), + //albums: results[0].map(function(i){ return i.album;}) + //}; + //}); + //}, + + artists: function (params) { + //TODO filter/where support + var id = params.id; + if(typeof id === 'undefined') + id = this.settings.id; + + var t = new Tomahawk.Collection.Transaction(this, id); + return t.beginTransaction().then(function () { + var mapFn = function(r) + { + return r.artist; + }; + t.sqlSelect("artists", mapFn, ["artist", "artistDisambiguation"]); + return t.execDefferedStatements(); + }).then(function (artists) { + return {artists: artists[0]}; + }); + }, + + //TODO: not exactly sure how is this one supposed to work + //albumArtists: function (params) { + //var id = params.id; + + //var t = new Tomahawk.Collection.Transaction(this, id); + //return t.beginTransaction().then(function () { + //var mapFn = function(row) { + //return { + //albumArtist: row.albumArtist, + //albumArtistDisambiguation: row.albumArtistDisambiguation + //}; + //}; + //t.sqlSelect("albumArtists", ["albumArtist", "albumArtistDisambiguation"]); + //return t.execDefferedStatements(); + //}).then(function (results) { + //return results[0]; + //}); + //}, + + artistAlbums: function (params) { + //TODO filter/where support + var id = params.id; + if(typeof id === 'undefined') + id = this.settings.id; + var artist = params.artist; + //var artistDisambiguation = params.artistDisambiguation; + + var that = this; + + var t = new Tomahawk.Collection.Transaction(this, id); + return t.beginTransaction().then(function () { + + t.sqlSelect("artists",function(r){return r._id;}, ["_id"], { + artist: artist + //artistDisambiguation: artistDisambiguation + }); + return t.execDefferedStatements(); + }).then(function (results) { + var artistId = results[0][0]; + t.sqlSelect("artistAlbums",function(r){return r.album;}, ["albumId", 'album'], { + artistId: artistId + },[ + { + table: "albums", + conditions: { + albumId: "_id" + } + } + ]); + return t.execDefferedStatements(); + }).then(function (results) { + return { + artist: artist, + albums: results[0] + }; + }); + }, + + albumTracks: function (params) { + //TODO filter/where support + var id = params.id; + if(typeof id === 'undefined') + id = this.settings.id; + var albumArtist = params.artist; + //var albumArtistDisambiguation = params.albumArtistDisambiguation; + var album = params.album; + + var that = this; + + var t = new Tomahawk.Collection.Transaction(this, id); + return t.beginTransaction().then(function () { + t.sqlSelect("artists", function(r){return r._id;},["_id"], { + artist: albumArtist, + //artistDisambiguation: albumArtistDisambiguation + }); + return t.execDefferedStatements(); + }).then(function (results) { + var albumArtistId = results[0][0]; + t.sqlSelect("albums",function(r){return r._id;}, ["_id"], { + album: album, + albumArtistId: albumArtistId + }); + return t.execDefferedStatements(); + }).then(function (results) { + var albumId = results[0][0]; + return that.tracks(params, { + albumId: albumId + }); + }); + }, + + collection: function () { + return this.settings; } }; diff --git a/src/libtomahawk/resolvers/JSResolverHelper.cpp b/src/libtomahawk/resolvers/JSResolverHelper.cpp index ae03fae9e..4b9ea8ec1 100644 --- a/src/libtomahawk/resolvers/JSResolverHelper.cpp +++ b/src/libtomahawk/resolvers/JSResolverHelper.cpp @@ -711,7 +711,13 @@ JSResolverHelper::customIODeviceFactory( const Tomahawk::result_ptr&, const QStr if ( m_urlCallbackIsAsync ) { QString qid = uuid(); - QString getUrl = QString( "Tomahawk.resolver.instance.%1( '%2', '%3' );" ).arg( m_urlCallback ) + QString getUrl = QString( + "if(Tomahawk.resolver.instance['_adapter_%1']) {" + " Tomahawk.resolver.instance._adapter_%1( {qid: '%2', url: '%3'} );" + "} else {" + " Tomahawk.resolver.instance.%1( {qid: '%2', url: '%3'} );" + "}" + ).arg( m_urlCallback ) .arg( qid ) .arg( url ); diff --git a/src/libtomahawk/resolvers/ScriptAccount.cpp b/src/libtomahawk/resolvers/ScriptAccount.cpp index 33e8d136c..13dae9ada 100644 --- a/src/libtomahawk/resolvers/ScriptAccount.cpp +++ b/src/libtomahawk/resolvers/ScriptAccount.cpp @@ -145,7 +145,7 @@ ScriptAccount::invoke( const scriptobject_ptr& scriptObject, const QString& meth void ScriptAccount::reportScriptJobResult( const QVariantMap& result ) { - tLog() << Q_FUNC_INFO << result; + //tLog() << Q_FUNC_INFO << result; const QString requestId = result[ "requestId" ].toString(); Q_ASSERT( !requestId.isEmpty() ); diff --git a/src/libtomahawk/resolvers/ScriptCommand_AllTracks.cpp b/src/libtomahawk/resolvers/ScriptCommand_AllTracks.cpp index fb8f09088..1251cebbd 100644 --- a/src/libtomahawk/resolvers/ScriptCommand_AllTracks.cpp +++ b/src/libtomahawk/resolvers/ScriptCommand_AllTracks.cpp @@ -106,7 +106,7 @@ ScriptCommand_AllTracks::onTracksJobDone( const QVariantMap& result ) ScriptJob* job = qobject_cast< ScriptJob* >( sender() ); Q_ASSERT( job ); - qDebug() << "Resolver reporting album tracks:" << result; + //qDebug() << "Resolver reporting album tracks:" << result; if ( job->error() ) { diff --git a/src/libtomahawk/resolvers/ScriptEngine.cpp b/src/libtomahawk/resolvers/ScriptEngine.cpp index c3d06246f..bd76d627b 100644 --- a/src/libtomahawk/resolvers/ScriptEngine.cpp +++ b/src/libtomahawk/resolvers/ScriptEngine.cpp @@ -49,6 +49,8 @@ ScriptEngine::ScriptEngine( JSAccount* parent ) settings()->setAttribute( QWebSettings::LocalStorageDatabaseEnabled, true ); settings()->setAttribute( QWebSettings::LocalContentCanAccessFileUrls, true ); settings()->setAttribute( QWebSettings::LocalContentCanAccessRemoteUrls, true ); + settings()->setOfflineStorageDefaultQuota(100 * 1024 * 1024 /* 100 Mb */); + settings()->setOfflineWebApplicationCacheQuota(100 * 1024 * 1024 /* 100 Mb */); // HACK QStringList cmdArgs = QCoreApplication::instance()->arguments(); diff --git a/src/tomahawk/TomahawkApp.cpp b/src/tomahawk/TomahawkApp.cpp index 8cfb985a3..5ca7d26c3 100644 --- a/src/tomahawk/TomahawkApp.cpp +++ b/src/tomahawk/TomahawkApp.cpp @@ -171,6 +171,10 @@ TomahawkApp::init() tLog() << "Starting Tomahawk..."; + QWebSettings::globalSettings()->setAttribute(QWebSettings::OfflineStorageDatabaseEnabled, true); + //QWebSettings::setOfflineStoragePath(QLatin1String("/tmp")); + + m_headless = true; m_headless = arguments().contains( "--headless" ); setWindowIcon( QIcon( RESPATH "icons/tomahawk-icon-128x128.png" ) ); From 82dcb959d915107e72bcd43b2ce8e95023855c5e Mon Sep 17 00:00:00 2001 From: Anton Romanov Date: Thu, 10 Sep 2015 15:13:43 -0700 Subject: [PATCH 02/13] Use prettyname if description is missing in collection's settings, save trackcount when updating index --- data/js/tomahawk.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/data/js/tomahawk.js b/data/js/tomahawk.js index 5ea630c67..47f925f34 100644 --- a/data/js/tomahawk.js +++ b/data/js/tomahawk.js @@ -1496,6 +1496,7 @@ Tomahawk.Collection = { t.sqlSelect("tracks", resultMap, ["_id", "artistId", "albumId", "track"]); return t.execDefferedStatements(); }).then(function (results) { + that._trackCount = results[0].length; Tomahawk.log("Added " + results[0].length + " tracks to collection '" + id + "'"); // Add the db ids together with the basic metadata to the fuzzy index list var fuzzyIndexList = []; @@ -1788,6 +1789,9 @@ Tomahawk.Collection = { }, collection: function () { + this.settings.trackcount = this._trackCount; + if(! this.settings.description) + this.settings.description = this.settings.prettyname; return this.settings; } }; From 4a3c5a5b7856b4df059da019373aa417f6c63e85 Mon Sep 17 00:00:00 2001 From: Anton Romanov Date: Thu, 10 Sep 2015 15:58:07 -0700 Subject: [PATCH 03/13] [0.9 api]always register custom url handler for resolver --- data/js/tomahawk.js | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/data/js/tomahawk.js b/data/js/tomahawk.js index 47f925f34..c5f11ae07 100644 --- a/data/js/tomahawk.js +++ b/data/js/tomahawk.js @@ -228,6 +228,9 @@ var TomahawkResolver = { collection: function () { return {}; }, + getStreamUrl: function(params) { + Tomahawk.reportStreamUrl(params.qid, params.url); + }, _testConfig: function (config) { return Promise.resolve(this.testConfig(config)).then(function() { return { result: Tomahawk.ConfigTestResultType.Success }; @@ -243,6 +246,17 @@ Tomahawk.Resolver = Tomahawk.extend(TomahawkResolver, { window.localStorage[this.scriptPath()] = JSON.stringify(Tomahawk.resolverData().config); this.newConfigSaved(Tomahawk.resolverData().config); }, + + _convertUrls: function(results) { + var that = this; + return results.map(function(r){ + if(r.url) { + r.url = that._urlProtocol + '://' + r.url; + } + return r; + }); + }, + _adapter_resolve: function (qid, artist, album, title) { var that = this; var collectionPromises = []; @@ -257,13 +271,22 @@ Tomahawk.Resolver = Tomahawk.extend(TomahawkResolver, { Promise.resolve(that.resolve({artist: artist, album: album, track:title})).then(function(results){ Tomahawk.addTrackResults({ 'qid': qid, - 'results': results.concat(collectionResults) + 'results': that._convertUrls(results.concat(collectionResults)) }); }); }); }, + _adapter_init: function () + { + this._urlProtocol = this.settings.name.replace(/\s+/g, '').toLowerCase(); + Tomahawk.addCustomUrlHandler( this._urlProtocol, 'getStreamUrl', true ); + Tomahawk.log('Registered custom url handler for protocol "' + this._urlProtocol + '"'); + this.init(); + }, + _adapter_getStreamUrl: function (params) { + params.url = params.url.slice(this._urlProtocol.length + 3); Promise.resolve(this.getStreamUrl(params)).then(function(result){ Tomahawk.reportStreamUrl(params.qid, result.url, result.headers); }); @@ -271,13 +294,15 @@ Tomahawk.Resolver = Tomahawk.extend(TomahawkResolver, { _adapter_search: function (qid, query) { + var that = this; Promise.resolve(this.search({query:query})).then(function(results){ Tomahawk.addTrackResults({ 'qid': qid, - 'results': results + 'results': that._convertUrls(results) }); }); }, + _adapter_testConfig: function (config) { return Promise.resolve(this.testConfig(config)).then(function() { return { result: Tomahawk.ConfigTestResultType.Success }; @@ -1337,6 +1362,7 @@ Tomahawk.Collection = { }, addTracks: function (params) { + var that = this; var id = params.id; var tracks = params.tracks; @@ -1639,7 +1665,7 @@ Tomahawk.Collection = { ); return t.execDefferedStatements(); }).then(function (results) { - return {results: results[0]}; + return {results: Tomahawk.resolver.instance._convertUrls(results[0])}; }); }, From 0501ab1e2b7e6cc03b2b09289f333d56dd88bcc9 Mon Sep 17 00:00:00 2001 From: Anton Romanov Date: Thu, 10 Sep 2015 16:34:53 -0700 Subject: [PATCH 04/13] [0.9 api]Support non-relative icon paths --- src/libtomahawk/resolvers/ScriptCollection.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/libtomahawk/resolvers/ScriptCollection.cpp b/src/libtomahawk/resolvers/ScriptCollection.cpp index 80fc46b05..57a89bd12 100644 --- a/src/libtomahawk/resolvers/ScriptCollection.cpp +++ b/src/libtomahawk/resolvers/ScriptCollection.cpp @@ -252,6 +252,20 @@ ScriptCollection::parseMetaData( const QVariantMap& metadata ) bool ok = iconPixmap.load( iconPath ); if ( ok && !iconPixmap.isNull() ) setIcon( iconPixmap ); + else + { + //see if it is a new way of specifying path + //like "contents/images/icon.png" + iconPath = QFileInfo( scriptAccount()->filePath() ).path(); + for(int i = 0; i < 3 && !ok ; ++i) + { + iconPath += "/../"; + ok = iconPixmap.load(iconPath + metadata.value("iconfile").toString()); + } + + if ( ok && !iconPixmap.isNull() ) + setIcon( iconPixmap ); + } fetchIcon( metadata.value( "iconurl" ).toString() ); } From 7171d475a26ac9332ad3b28c2f70c6b765cba0b5 Mon Sep 17 00:00:00 2001 From: Anton Romanov Date: Thu, 10 Sep 2015 18:54:37 -0700 Subject: [PATCH 05/13] [0.9 api] use collection for search --- data/js/tomahawk.js | 92 +++++++++++++++++++++++++++++---------------- 1 file changed, 59 insertions(+), 33 deletions(-) diff --git a/data/js/tomahawk.js b/data/js/tomahawk.js index c5f11ae07..984e75914 100644 --- a/data/js/tomahawk.js +++ b/data/js/tomahawk.js @@ -250,7 +250,7 @@ Tomahawk.Resolver = Tomahawk.extend(TomahawkResolver, { _convertUrls: function(results) { var that = this; return results.map(function(r){ - if(r.url) { + if(r && r.url) { r.url = that._urlProtocol + '://' + r.url; } return r; @@ -295,10 +295,22 @@ Tomahawk.Resolver = Tomahawk.extend(TomahawkResolver, { _adapter_search: function (qid, query) { var that = this; - Promise.resolve(this.search({query:query})).then(function(results){ - Tomahawk.addTrackResults({ - 'qid': qid, - 'results': that._convertUrls(results) + var collectionPromises = []; + Tomahawk.collections.forEach(function(col) { + if(col.search) + collectionPromises.push(col.search({query: query})); + }); + Promise.all(collectionPromises).then(function(collectionResults){ + var merged = []; + return merged.concat.apply(merged,collectionResults); + }).then(function(collectionResults) { + Promise.resolve(that.search({query:query})).then(function(results){ + Tomahawk.log(JSON.stringify(results)); + Tomahawk.log(JSON.stringify(collectionResults)); + Tomahawk.addTrackResults({ + 'qid': qid, + 'results': that._convertUrls(results.concat(collectionResults)) + }); }); }); }, @@ -1260,28 +1272,34 @@ Tomahawk.Collection = { for (var i = 0; i < that.statements.length; ++i) { var stmt = that.statements[i]; - var originalI = i; tx.executeSql(stmt.statement, stmt.args, - function (tx, results) { - that.stmtsToResolve--; - if (typeof that.statements[originalI].map !== 'undefined') - { - var map = that.statements[originalI].map; - that.results[originalI] = []; - for (var ii = 0; ii < results.rows.length; ii++) { - that.results[originalI].push(map( - results.rows.item(ii) - )); + (function () { + //A function returning a function to + //capture value of i + var originalI = i; + return function (tx, results) { + if (typeof that.statements[originalI].map !== 'undefined') + { + var map = that.statements[originalI].map; + that.results[originalI] = []; + Tomahawk.log('originalI ' + originalI); + for (var ii = 0; ii < results.rows.length; ii++) { + that.results[originalI].push(map( + results.rows.item(ii) + )); + } } - } - else - that.results[originalI] = results; - if(that.stmtsToResolve == 0) - { - that.statements = []; - resolve(that.results); - } - }, function (tx, error) { + else + that.results[originalI] = results; + that.stmtsToResolve--; + if(that.stmtsToResolve == 0) + { + that.statements = []; + Tomahawk.log(JSON.stringify(that.results)); + resolve(that.results); + } + }; + })(), function (tx, error) { Tomahawk.log("Error in tx.executeSql: " + error.code + " - " + error.message); that.statements = []; @@ -1570,12 +1588,10 @@ Tomahawk.Collection = { }); }, - resolve: function(params) { - var id = params.id; + _fuzzyIndexIdsToTracks: function(resultIds, id) { + var that = this; if(typeof id === 'undefined') id = this.settings.id; - Tomahawk.log('called resolve'); - var resultIds = Tomahawk.resolveFromFuzzyIndex(params.artist, params.album, params.track); var t = new Tomahawk.Collection.Transaction(this, id); return t.beginTransaction().then(function () { var mapFn = function(row) { @@ -1619,12 +1635,22 @@ Tomahawk.Collection = { return e[0]; })); }); - return this.tracks({}); }, - search: function() { - //TODO - Tomahawk.log('called search'); + resolve: function(params) { + var id = params.id; + if(typeof id === 'undefined') + id = this.settings.id; + var resultIds = Tomahawk.resolveFromFuzzyIndex(params.artist, params.album, params.track); + return this._fuzzyIndexIdsToTracks(resultIds); + }, + + search: function(params) { + var id = params.id; + if(typeof id === 'undefined') + id = this.settings.id; + var resultIds = Tomahawk.searchFuzzyIndex(params.query); + return this._fuzzyIndexIdsToTracks(resultIds); }, tracks: function (params, where) { From 079f793b74103a19ba34da463350d910fa27b752 Mon Sep 17 00:00:00 2001 From: Anton Romanov Date: Fri, 11 Sep 2015 12:22:30 -0700 Subject: [PATCH 06/13] [api 0.9] support all layouts for collections by default --- src/libtomahawk/resolvers/ScriptCollection.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libtomahawk/resolvers/ScriptCollection.cpp b/src/libtomahawk/resolvers/ScriptCollection.cpp index 57a89bd12..3c624a1a1 100644 --- a/src/libtomahawk/resolvers/ScriptCollection.cpp +++ b/src/libtomahawk/resolvers/ScriptCollection.cpp @@ -288,6 +288,8 @@ ScriptCollection::parseMetaData( const QVariantMap& metadata ) else { m_browseCapabilities << CapabilityBrowseArtists; + m_browseCapabilities << CapabilityBrowseAlbums; + m_browseCapabilities << CapabilityBrowseTracks; } } From 46bbe7ac746a300806601c5f490a8d0044987279 Mon Sep 17 00:00:00 2001 From: Anton Romanov Date: Fri, 11 Sep 2015 14:00:29 -0700 Subject: [PATCH 07/13] [0.9 api]Implement albums view for collections --- data/js/tomahawk.js | 71 +++++++++++++++++++++++---------------------- 1 file changed, 36 insertions(+), 35 deletions(-) diff --git a/data/js/tomahawk.js b/data/js/tomahawk.js index 984e75914..33ce5e637 100644 --- a/data/js/tomahawk.js +++ b/data/js/tomahawk.js @@ -1282,7 +1282,6 @@ Tomahawk.Collection = { { var map = that.statements[originalI].map; that.results[originalI] = []; - Tomahawk.log('originalI ' + originalI); for (var ii = 0; ii < results.rows.length; ii++) { that.results[originalI].push(map( results.rows.item(ii) @@ -1295,7 +1294,6 @@ Tomahawk.Collection = { if(that.stmtsToResolve == 0) { that.statements = []; - Tomahawk.log(JSON.stringify(that.results)); resolve(that.results); } }; @@ -1695,40 +1693,43 @@ Tomahawk.Collection = { }); }, - //albums: function (params, where) { - ////TODO filter/where support - //var id = params.id; - //if(typeof id === 'undefined') - //id = this.settings.id; + albums: function (params, where) { + //TODO filter/where support + var id = params.id; + if(typeof id === 'undefined') + id = this.settings.id; - //var t = new Tomahawk.Collection.Transaction(this, id); - //return t.beginTransaction().then(function () { - //var mapFn = function(row) { - //return { - //albumArtist: row.artist, - //albumArtistDisambiguation: row.artistDisambiguation, - //album: row.album - //}; - //}; - //t.sqlSelect("albums",mapFn, - //["album", "artist", "artistDisambiguation"], - //where, [ - //{ - //table: "artists", - //conditions: { - //albumArtistId: "_id" - //} - //} - //] - //); - //return t.execDefferedStatements(); - //}).then(function (results) { - //return { - //artists: results[0].map(function(i){ return i.albumArtist;}), - //albums: results[0].map(function(i){ return i.album;}) - //}; - //}); - //}, + var t = new Tomahawk.Collection.Transaction(this, id); + return t.beginTransaction().then(function () { + var mapFn = function(row) { + return { + albumArtist: row.artist, + albumArtistDisambiguation: row.artistDisambiguation, + album: row.album + }; + }; + t.sqlSelect("albums",mapFn, + ["album", "artist", "artistDisambiguation"], + where, [ + { + table: "artists", + conditions: { + albumArtistId: "_id" + } + } + ] + ); + return t.execDefferedStatements(); + }).then(function (results) { + results = results[0].filter(function(e){ + return (e.albumArtist != '' && e.album != ''); + }); + return { + artists: results.map(function(i){ return i.albumArtist;}), + albums: results.map(function(i){ return i.album;}) + }; + }); + }, artists: function (params) { //TODO filter/where support From 39ea028518187a04ef0d48c2f477d08e5c68550c Mon Sep 17 00:00:00 2001 From: Anton Romanov Date: Fri, 11 Sep 2015 23:45:58 -0700 Subject: [PATCH 08/13] Revert "[api 0.9] support all layouts for collections by default" This reverts commit 079f793b74103a19ba34da463350d910fa27b752. --- src/libtomahawk/resolvers/ScriptCollection.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/libtomahawk/resolvers/ScriptCollection.cpp b/src/libtomahawk/resolvers/ScriptCollection.cpp index 3c624a1a1..57a89bd12 100644 --- a/src/libtomahawk/resolvers/ScriptCollection.cpp +++ b/src/libtomahawk/resolvers/ScriptCollection.cpp @@ -288,8 +288,6 @@ ScriptCollection::parseMetaData( const QVariantMap& metadata ) else { m_browseCapabilities << CapabilityBrowseArtists; - m_browseCapabilities << CapabilityBrowseAlbums; - m_browseCapabilities << CapabilityBrowseTracks; } } From ddffd7c935d58ab562b5a6ef59c9d15753327cdb Mon Sep 17 00:00:00 2001 From: Anton Romanov Date: Fri, 11 Sep 2015 23:52:52 -0700 Subject: [PATCH 09/13] [0.9 api]Report all browse capabilities for builtin collection implementation --- data/js/tomahawk.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/data/js/tomahawk.js b/data/js/tomahawk.js index 33ce5e637..756ff907b 100644 --- a/data/js/tomahawk.js +++ b/data/js/tomahawk.js @@ -1845,6 +1845,8 @@ Tomahawk.Collection = { this.settings.trackcount = this._trackCount; if(! this.settings.description) this.settings.description = this.settings.prettyname; + this.settings.capabilities = [Tomahawk.Collection.BrowseCapability.Artists, + Tomahawk.Collection.BrowseCapability.Albums, Tomahawk.Collection.BrowseCapability.Tracks]; return this.settings; } }; From b12df536adcb2845196a6bbfa5c129fe303d5fa5 Mon Sep 17 00:00:00 2001 From: Anton Romanov Date: Wed, 16 Sep 2015 12:09:59 -0700 Subject: [PATCH 10/13] [0.9 api]Fix built-in custom url --- data/js/tomahawk.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/data/js/tomahawk.js b/data/js/tomahawk.js index 756ff907b..2f23b30fd 100644 --- a/data/js/tomahawk.js +++ b/data/js/tomahawk.js @@ -268,7 +268,8 @@ Tomahawk.Resolver = Tomahawk.extend(TomahawkResolver, { var merged = []; return merged.concat.apply(merged,collectionResults); }).then(function(collectionResults) { - Promise.resolve(that.resolve({artist: artist, album: album, track:title})).then(function(results){ + if(typeof results === 'undefined') + results = []; Tomahawk.addTrackResults({ 'qid': qid, 'results': that._convertUrls(results.concat(collectionResults)) @@ -279,7 +280,7 @@ Tomahawk.Resolver = Tomahawk.extend(TomahawkResolver, { _adapter_init: function () { - this._urlProtocol = this.settings.name.replace(/\s+/g, '').toLowerCase(); + this._urlProtocol = this.settings.name.replace(/[^a-zA-Z]/g, '').toLowerCase(); Tomahawk.addCustomUrlHandler( this._urlProtocol, 'getStreamUrl', true ); Tomahawk.log('Registered custom url handler for protocol "' + this._urlProtocol + '"'); this.init(); From f2c652c1e9523621687390f48005637c4ba30794 Mon Sep 17 00:00:00 2001 From: Anton Romanov Date: Thu, 17 Sep 2015 16:27:57 -0700 Subject: [PATCH 11/13] Fix accidentally deleted line --- data/js/tomahawk.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/data/js/tomahawk.js b/data/js/tomahawk.js index 2f23b30fd..ee25992e9 100644 --- a/data/js/tomahawk.js +++ b/data/js/tomahawk.js @@ -268,8 +268,7 @@ Tomahawk.Resolver = Tomahawk.extend(TomahawkResolver, { var merged = []; return merged.concat.apply(merged,collectionResults); }).then(function(collectionResults) { - if(typeof results === 'undefined') - results = []; + Promise.resolve(that.resolve({artist: artist, album: album, track:title})).then(function(results){ Tomahawk.addTrackResults({ 'qid': qid, 'results': that._convertUrls(results.concat(collectionResults)) From 2db60807311ba73b96c97ad5a94a10bdf7449083 Mon Sep 17 00:00:00 2001 From: Anton Romanov Date: Thu, 17 Sep 2015 16:54:35 -0700 Subject: [PATCH 12/13] [api 0.9] fix track number in collection, add release year and bitrate to collection storage --- data/js/tomahawk.js | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/data/js/tomahawk.js b/data/js/tomahawk.js index ee25992e9..d1522d186 100644 --- a/data/js/tomahawk.js +++ b/data/js/tomahawk.js @@ -1238,6 +1238,8 @@ Tomahawk.Collection = { "duration INTEGER," + "albumPos INTEGER," + "linkUrl TEXT," + + 'releaseyear INTEGER,' + + 'bitrate INTEGER,' + "UNIQUE (track, artistId, albumId) ON CONFLICT IGNORE," + "FOREIGN KEY(artistId) REFERENCES artists(_id)," + "FOREIGN KEY(albumId) REFERENCES albums(_id))", []); @@ -1312,17 +1314,6 @@ Tomahawk.Collection = { this.sql = function (sqlStatement, sqlArgs, mapFunction) { this.statements.push({statement: sqlStatement, args: sqlArgs, map: mapFunction}); - //var that = this; - //return new RSVP.Promise(function (resolve, reject) { - //that.tx.executeSql(sqlStatement, sqlArgs, - //function (tx, results) { - //resolve(results); - //}, function (tx, error) { - //Tomahawk.log("Error in tx.executeSql: " + error.code + " - " - //+ error.message); - //reject(error); - //}); - //}) }; this.sqlSelect = function (table, mapResults, fields, where, join) { @@ -1520,7 +1511,9 @@ Tomahawk.Collection = { url: track.url, duration: track.duration, linkUrl: track.linkUrl, - albumPos: track.albumPos + releaseyear: track.releaseyear, + bitrate: track.bitrate, + albumPos: track.albumpos }); })(tracks[i]); } @@ -1601,14 +1594,16 @@ Tomahawk.Collection = { duration: row.duration, url: row.url, linkUrl: row.linkUrl, - albumPos: row.albumPos + releaseyear: row.releaseyear, + bitrate: row.bitrate, + albumpos: row.albumPos }; }; for (var idx = 0; resultIds && idx < resultIds.length; idx++) { var trackid = resultIds[idx][0]; var where = { _id : trackid }; t.sqlSelect("tracks",mapFn, - ["artist", "artistDisambiguation", "album", "track", "duration", "url", "linkUrl"], + [], where, [ { table: "artists", @@ -1668,11 +1663,13 @@ Tomahawk.Collection = { duration: row.duration, url: row.url, linkUrl: row.linkUrl, - albumPos: row.albumPos + releaseyear: row.releaseyear, + bitrate: row.bitrate, + albumpos: row.albumPos }; }; t.sqlSelect("tracks",mapFn, - ["artist", "artistDisambiguation", "album", "track", "duration", "url", "linkUrl"], + [], where, [ { table: "artists", From 011bfdaef0044ee99af623f02f1e822a04cbf61b Mon Sep 17 00:00:00 2001 From: Anton Romanov Date: Thu, 17 Sep 2015 17:18:09 -0700 Subject: [PATCH 13/13] Implement Tomahawk.localStorage --- data/js/tomahawk.js | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/data/js/tomahawk.js b/data/js/tomahawk.js index d1522d186..fe372fb4f 100644 --- a/data/js/tomahawk.js +++ b/data/js/tomahawk.js @@ -805,9 +805,15 @@ Tomahawk.removeDiacritics = function (str) { }; Tomahawk.localStorage = Tomahawk.localStorage || { - setItem: function() {}, - getItem: function() {}, - removeItem: function() {} + setItem: function(key, value) { + window.localStorage[key] = value; + }, + getItem: function(key) { + return window.localStorage[key]; + }, + removeItem: function(key) { + delete window.localStorage[key]; + } }; // some aliases