mirror of
https://github.com/tomahawk-player/tomahawk.git
synced 2025-08-08 15:16:34 +02:00
Merge pull request #339 from tomahawk-player/promisify-all-the-things
[WIP] Promisify JSResolver
This commit is contained in:
@@ -49,7 +49,7 @@ RSVP.on('error', function (reason) {
|
|||||||
resolverName = Tomahawk.resolver.instance.settings.name + " - ";
|
resolverName = Tomahawk.resolver.instance.settings.name + " - ";
|
||||||
}
|
}
|
||||||
if (reason) {
|
if (reason) {
|
||||||
console.error(resolverName + 'Uncaught error:' + JSON.stringify(reason));
|
console.error(resolverName + 'Uncaught error:', reason);
|
||||||
} else {
|
} else {
|
||||||
console.error(resolverName + 'Uncaught error: error thrown from RSVP but it was empty');
|
console.error(resolverName + 'Uncaught error: error thrown from RSVP but it was empty');
|
||||||
}
|
}
|
||||||
@@ -173,7 +173,8 @@ var TomahawkUrlType = {
|
|||||||
Playlist: 1,
|
Playlist: 1,
|
||||||
Track: 2,
|
Track: 2,
|
||||||
Album: 4,
|
Album: 4,
|
||||||
Artist: 8
|
Artist: 8,
|
||||||
|
Xspf: 16
|
||||||
};
|
};
|
||||||
|
|
||||||
//Deprecated for 0.9 resolvers. Use Tomahawk.ConfigTestResultType instead.
|
//Deprecated for 0.9 resolvers. Use Tomahawk.ConfigTestResultType instead.
|
||||||
@@ -272,74 +273,19 @@ Tomahawk.Resolver = {
|
|||||||
getStreamUrl: function (params) {
|
getStreamUrl: function (params) {
|
||||||
return params;
|
return params;
|
||||||
},
|
},
|
||||||
|
_adapter_resolve: function (params) {
|
||||||
_convertUrls: function (results) {
|
return RSVP.Promise.resolve(this.resolve(params)).then(function (results) {
|
||||||
var that = this;
|
return {
|
||||||
return results.map(function (r) {
|
'tracks': results
|
||||||
if (r && r.url) {
|
};
|
||||||
r.url = that._urlProtocol + '://' + r.url;
|
|
||||||
}
|
|
||||||
return r;
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
_adapter_resolve: function (qid, artist, album, title) {
|
_adapter_search: function (params) {
|
||||||
var that = this;
|
return RSVP.Promise.resolve(this.search(params)).then(function (results) {
|
||||||
var collectionPromises = [];
|
return {
|
||||||
Tomahawk.collections.forEach(function (col) {
|
'tracks': results
|
||||||
if (col.resolve) {
|
};
|
||||||
collectionPromises.push(col.resolve({artist: artist, album: album, track: title}));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
RSVP.Promise.all(collectionPromises).then(function (collectionResults) {
|
|
||||||
var merged = [];
|
|
||||||
return merged.concat.apply(merged, collectionResults);
|
|
||||||
}).then(function (collectionResults) {
|
|
||||||
RSVP.Promise.resolve(that.resolve({
|
|
||||||
artist: artist,
|
|
||||||
album: album,
|
|
||||||
track: title
|
|
||||||
})).then(function (results) {
|
|
||||||
Tomahawk.addTrackResults({
|
|
||||||
'qid': qid,
|
|
||||||
'results': that._convertUrls(results.concat(collectionResults))
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
_adapter_init: function () {
|
|
||||||
this._urlProtocol = this.settings.name.replace(/[^a-zA-Z]/g, '').toLowerCase();
|
|
||||||
Tomahawk.addCustomUrlHandler(this._urlProtocol, '_adapter_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);
|
|
||||||
RSVP.Promise.resolve(this.getStreamUrl(params)).then(function (result) {
|
|
||||||
Tomahawk.reportStreamUrl(params.qid, result.url, result.headers);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
_adapter_search: function (qid, query) {
|
|
||||||
var that = this;
|
|
||||||
var collectionPromises = [];
|
|
||||||
Tomahawk.collections.forEach(function (col) {
|
|
||||||
if (col.search) {
|
|
||||||
collectionPromises.push(col.search({query: query}));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
RSVP.Promise.all(collectionPromises).then(function (collectionResults) {
|
|
||||||
var merged = [];
|
|
||||||
return merged.concat.apply(merged, collectionResults);
|
|
||||||
}).then(function (collectionResults) {
|
|
||||||
RSVP.Promise.resolve(that.search({query: query})).then(function (results) {
|
|
||||||
Tomahawk.addTrackResults({
|
|
||||||
'qid': qid,
|
|
||||||
'results': that._convertUrls(results.concat(collectionResults))
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -366,34 +312,6 @@ Tomahawk.valueForSubNode = function (node, tag) {
|
|||||||
return element.textContent;
|
return element.textContent;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Do a synchronous HTTP(S) request. For further options see
|
|
||||||
* Tomahawk.asyncRequest
|
|
||||||
*/
|
|
||||||
Tomahawk.syncRequest = function (url, extraHeaders, options) {
|
|
||||||
// unpack options
|
|
||||||
var opt = options || {};
|
|
||||||
var method = opt.method || 'GET';
|
|
||||||
|
|
||||||
var xmlHttpRequest = new XMLHttpRequest();
|
|
||||||
xmlHttpRequest.open(method, url, false, opt.username, opt.password);
|
|
||||||
if (extraHeaders) {
|
|
||||||
for (var headerName in extraHeaders) {
|
|
||||||
xmlHttpRequest.setRequestHeader(headerName, extraHeaders[headerName]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
xmlHttpRequest.send(null);
|
|
||||||
if (httpSuccessStatuses.indexOf(xmlHttpRequest.status) != -1) {
|
|
||||||
return xmlHttpRequest.responseText;
|
|
||||||
} else {
|
|
||||||
Tomahawk.log("Failed to do GET request: to: " + url);
|
|
||||||
Tomahawk.log("Status Code was: " + xmlHttpRequest.status);
|
|
||||||
if (opt.hasOwnProperty('errorHandler')) {
|
|
||||||
opt.errorHandler.call(window, xmlHttpRequest);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal counter used to identify retrievedMetadata call back from native
|
* Internal counter used to identify retrievedMetadata call back from native
|
||||||
* code.
|
* code.
|
||||||
@@ -489,6 +407,23 @@ Tomahawk.nativeAsyncRequestDone = function (reqId, xhr) {
|
|||||||
delete Tomahawk.asyncRequestCallbacks[reqId];
|
delete Tomahawk.asyncRequestCallbacks[reqId];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is externalized from Tomahawk.asyncRequest, so that other clients
|
||||||
|
* (like tomahawk-android) can inject their own logic that determines whether or not to do a request
|
||||||
|
* natively.
|
||||||
|
*
|
||||||
|
* @returns boolean indicating whether or not to do a request with the given parameters natively
|
||||||
|
*/
|
||||||
|
var shouldDoNativeRequest = function (options) {
|
||||||
|
var extraHeaders = options.headers;
|
||||||
|
return (extraHeaders && (extraHeaders.hasOwnProperty("Referer")
|
||||||
|
|| extraHeaders.hasOwnProperty("referer")
|
||||||
|
|| extraHeaders.hasOwnProperty("User-Agent")));
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Possible options:
|
* Possible options:
|
||||||
* - method: The HTTP request method (default: GET)
|
* - method: The HTTP request method (default: GET)
|
||||||
@@ -498,59 +433,45 @@ Tomahawk.nativeAsyncRequestDone = function (reqId, xhr) {
|
|||||||
* - data: body data included in POST requests
|
* - data: body data included in POST requests
|
||||||
* - needCookieHeader: boolean indicating whether or not the request needs to be able to get the
|
* - needCookieHeader: boolean indicating whether or not the request needs to be able to get the
|
||||||
* "Set-Cookie" response header
|
* "Set-Cookie" response header
|
||||||
|
* - headers: headers set on the request
|
||||||
*/
|
*/
|
||||||
Tomahawk.asyncRequest = function (url, callback, extraHeaders, options) {
|
var doRequest = function(options) {
|
||||||
// unpack options
|
if (shouldDoNativeRequest(options)) {
|
||||||
var opt = options || {};
|
return Tomahawk.NativeScriptJobManager.invoke('httpRequest', options).then(function(xhr) {
|
||||||
var method = opt.method || 'GET';
|
xhr.responseHeaders = xhr.responseHeaders || {};
|
||||||
|
xhr.getAllResponseHeaders = function() {
|
||||||
|
return this.responseHeaders;
|
||||||
|
};
|
||||||
|
xhr.getResponseHeader = function (header) {
|
||||||
|
return this.responseHeaders[header];
|
||||||
|
};
|
||||||
|
|
||||||
if (shouldDoNativeRequest(url, callback, extraHeaders, options)) {
|
return xhr;
|
||||||
// Assign a request Id to the callback so we can use it when we are
|
});
|
||||||
// returning from the native call.
|
|
||||||
var reqId = Tomahawk.asyncRequestIdCounter;
|
|
||||||
Tomahawk.asyncRequestIdCounter++;
|
|
||||||
Tomahawk.asyncRequestCallbacks[reqId] = {
|
|
||||||
callback: callback,
|
|
||||||
errorHandler: opt.errorHandler
|
|
||||||
};
|
|
||||||
Tomahawk.nativeAsyncRequest(reqId, url, extraHeaders, options);
|
|
||||||
} else {
|
} else {
|
||||||
var xmlHttpRequest = new XMLHttpRequest();
|
return new RSVP.Promise(function(resolve, reject) {
|
||||||
xmlHttpRequest.open(method, url, true, opt.username, opt.password);
|
var xmlHttpRequest = new XMLHttpRequest();
|
||||||
if (extraHeaders) {
|
xmlHttpRequest.open(options.method, options.url, true, options.username, options.password);
|
||||||
for (var headerName in extraHeaders) {
|
if (options.headers) {
|
||||||
xmlHttpRequest.setRequestHeader(headerName, extraHeaders[headerName]);
|
for (var headerName in options.headers) {
|
||||||
}
|
xmlHttpRequest.setRequestHeader(headerName, options.headers[headerName]);
|
||||||
}
|
|
||||||
xmlHttpRequest.onreadystatechange = function () {
|
|
||||||
if (xmlHttpRequest.readyState == 4
|
|
||||||
&& httpSuccessStatuses.indexOf(xmlHttpRequest.status) != -1) {
|
|
||||||
callback.call(window, xmlHttpRequest);
|
|
||||||
} else if (xmlHttpRequest.readyState === 4) {
|
|
||||||
Tomahawk.log("Failed to do " + method + " request: to: " + url);
|
|
||||||
Tomahawk.log("Status Code was: " + xmlHttpRequest.status);
|
|
||||||
if (opt.hasOwnProperty('errorHandler')) {
|
|
||||||
opt.errorHandler.call(window, xmlHttpRequest);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
xmlHttpRequest.onreadystatechange = function () {
|
||||||
xmlHttpRequest.send(opt.data || null);
|
if (xmlHttpRequest.readyState == 4
|
||||||
|
&& httpSuccessStatuses.indexOf(xmlHttpRequest.status) != -1) {
|
||||||
|
resolve(xmlHttpRequest);
|
||||||
|
} else if (xmlHttpRequest.readyState === 4) {
|
||||||
|
Tomahawk.log("Failed to do " + options.method + " request: to: " + options.url);
|
||||||
|
Tomahawk.log("Status Code was: " + xmlHttpRequest.status);
|
||||||
|
reject(xmlHttpRequest);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
xmlHttpRequest.send(options.data || null);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* This method is externalized from Tomahawk.asyncRequest, so that other clients
|
|
||||||
* (like tomahawk-android) can inject their own logic that determines whether or not to do a request
|
|
||||||
* natively.
|
|
||||||
*
|
|
||||||
* @returns boolean indicating whether or not to do a request with the given parameters natively
|
|
||||||
*/
|
|
||||||
var shouldDoNativeRequest = function (url, callback, extraHeaders, options) {
|
|
||||||
return (extraHeaders && (extraHeaders.hasOwnProperty("Referer")
|
|
||||||
|| extraHeaders.hasOwnProperty("referer")
|
|
||||||
|| extraHeaders.hasOwnProperty("User-Agent")));
|
|
||||||
};
|
|
||||||
|
|
||||||
Tomahawk.ajax = function (url, settings) {
|
Tomahawk.ajax = function (url, settings) {
|
||||||
if (typeof url === "object") {
|
if (typeof url === "object") {
|
||||||
settings = url;
|
settings = url;
|
||||||
@@ -608,10 +529,7 @@ Tomahawk.ajax = function (url, settings) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new RSVP.Promise(function (resolve, reject) {
|
return doRequest(settings).then(function (xhr) {
|
||||||
settings.errorHandler = reject;
|
|
||||||
Tomahawk.asyncRequest(settings.url, resolve, settings.headers, settings);
|
|
||||||
}).then(function (xhr) {
|
|
||||||
if (settings.rawResponse) {
|
if (settings.rawResponse) {
|
||||||
return xhr;
|
return xhr;
|
||||||
}
|
}
|
||||||
@@ -817,7 +735,9 @@ Tomahawk.base64Encode = function (b) {
|
|||||||
return window.btoa(b);
|
return window.btoa(b);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
Tomahawk.PluginManager = {
|
Tomahawk.PluginManager = {
|
||||||
|
wrapperPrefix: '_adapter_',
|
||||||
objects: {},
|
objects: {},
|
||||||
objectCounter: 0,
|
objectCounter: 0,
|
||||||
identifyObject: function (object) {
|
identifyObject: function (object) {
|
||||||
@@ -829,10 +749,6 @@ Tomahawk.PluginManager = {
|
|||||||
},
|
},
|
||||||
registerPlugin: function (type, object) {
|
registerPlugin: function (type, object) {
|
||||||
this.objects[this.identifyObject(object)] = object;
|
this.objects[this.identifyObject(object)] = object;
|
||||||
if (type === 'collection') {
|
|
||||||
Tomahawk.collections.push(object);
|
|
||||||
}
|
|
||||||
|
|
||||||
Tomahawk.log("registerPlugin: " + type + " id: " + object.id);
|
Tomahawk.log("registerPlugin: " + type + " id: " + object.id);
|
||||||
Tomahawk.registerScriptPlugin(type, object.id);
|
Tomahawk.registerScriptPlugin(type, object.id);
|
||||||
},
|
},
|
||||||
@@ -846,14 +762,11 @@ Tomahawk.PluginManager = {
|
|||||||
|
|
||||||
resolve: [],
|
resolve: [],
|
||||||
invokeSync: function (requestId, objectId, methodName, params) {
|
invokeSync: function (requestId, objectId, methodName, params) {
|
||||||
if (!Tomahawk.resolver.instance.apiVersion || Tomahawk.resolver.instance.apiVersion < 0.9) {
|
if (this.objects[objectId][this.wrapperPrefix + methodName]) {
|
||||||
if (methodName === 'artistAlbums') {
|
methodName = this.wrapperPrefix + methodName;
|
||||||
methodName = 'albums';
|
|
||||||
} else if (methodName === 'albumTracks') {
|
|
||||||
methodName = 'tracks';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
var pluginManager = this;
|
var pluginManager = this;
|
||||||
if (!this.objects[objectId]) {
|
if (!this.objects[objectId]) {
|
||||||
Tomahawk.log("Object not found! objectId: " + objectId + " methodName: " + methodName);
|
Tomahawk.log("Object not found! objectId: " + objectId + " methodName: " + methodName);
|
||||||
@@ -863,52 +776,13 @@ Tomahawk.PluginManager = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof this.objects[objectId][methodName] === 'function') {
|
if (typeof this.objects[objectId][methodName] !== 'function' && this.objects[objectId][methodName]) {
|
||||||
if (!Tomahawk.resolver.instance.apiVersion
|
return this.objects[objectId][methodName];
|
||||||
|| Tomahawk.resolver.instance.apiVersion < 0.9) {
|
} else if (typeof this.objects[objectId][methodName] !== 'function') {
|
||||||
if (methodName == 'artists') {
|
throw new Error('\'' + methodName + '\' on ScriptObject ' + objectId + ' is not a function', typeof this.objects[objectId][methodName]);
|
||||||
return new RSVP.Promise(function (resolve, reject) {
|
|
||||||
pluginManager.resolve[requestId] = resolve;
|
|
||||||
Tomahawk.resolver.instance.artists(requestId);
|
|
||||||
});
|
|
||||||
} else if (methodName == 'albums') {
|
|
||||||
return new RSVP.Promise(function (resolve, reject) {
|
|
||||||
pluginManager.resolve[requestId] = resolve;
|
|
||||||
Tomahawk.resolver.instance.albums(requestId, params.artist);
|
|
||||||
});
|
|
||||||
} else if (methodName == 'tracks') {
|
|
||||||
return new RSVP.Promise(function (resolve, reject) {
|
|
||||||
pluginManager.resolve[requestId] = resolve;
|
|
||||||
Tomahawk.resolver.instance.tracks(requestId, params.artist, params.album);
|
|
||||||
});
|
|
||||||
} else if (methodName == 'lookupUrl') {
|
|
||||||
return new RSVP.Promise(function (resolve, reject) {
|
|
||||||
pluginManager.resolve[params.url] = resolve;
|
|
||||||
Tomahawk.resolver.instance.lookupUrl(params.url);
|
|
||||||
});
|
|
||||||
} else if (methodName == 'getStreamUrl') {
|
|
||||||
return new RSVP.Promise(function (resolve, reject) {
|
|
||||||
pluginManager.resolve[requestId] = resolve;
|
|
||||||
Tomahawk.resolver.instance.getStreamUrl(requestId, params.url);
|
|
||||||
});
|
|
||||||
} else if (methodName == 'resolve') {
|
|
||||||
return new RSVP.Promise(function (resolve, reject) {
|
|
||||||
pluginManager.resolve[requestId] = resolve;
|
|
||||||
Tomahawk.resolver.instance.resolve(requestId, params.artist,
|
|
||||||
params.album, params.track);
|
|
||||||
});
|
|
||||||
} else if (methodName == 'search') {
|
|
||||||
return new RSVP.Promise(function (resolve, reject) {
|
|
||||||
pluginManager.resolve[requestId] = resolve;
|
|
||||||
Tomahawk.resolver.instance.search(requestId, params.query);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.objects[objectId][methodName](params);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.objects[objectId][methodName];
|
return this.objects[objectId][methodName](params);
|
||||||
},
|
},
|
||||||
|
|
||||||
invoke: function (requestId, objectId, methodName, params) {
|
invoke: function (requestId, objectId, methodName, params) {
|
||||||
@@ -927,21 +801,38 @@ Tomahawk.PluginManager = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
var encodeParamsToNativeFunctions = function(param) {
|
||||||
|
return param;
|
||||||
|
};
|
||||||
|
|
||||||
Tomahawk.NativeScriptJobManager = {
|
Tomahawk.NativeScriptJobManager = {
|
||||||
idCounter: 0,
|
idCounter: 0,
|
||||||
deferreds: {},
|
deferreds: {},
|
||||||
invoke: function (methodName, params) {
|
invoke: function (methodName, params) {
|
||||||
|
params = params || {};
|
||||||
|
|
||||||
var requestId = this.idCounter++;
|
var requestId = this.idCounter++;
|
||||||
Tomahawk.invokeNativeScriptJob(requestId, methodName, JSON.stringify(params));
|
var deferred = RSVP.defer();
|
||||||
this.deferreds[requestId] = RSVP.defer();
|
this.deferreds[requestId] = deferred;
|
||||||
return this.deferreds[requestId].promise;
|
Tomahawk.invokeNativeScriptJob(requestId, methodName, encodeParamsToNativeFunctions(params));;
|
||||||
|
return deferred.promise;
|
||||||
},
|
},
|
||||||
reportNativeScriptJobResult: function (requestId, result) {
|
reportNativeScriptJobResult: function(requestId, result) {
|
||||||
var deferred = this.deferreds[requestId];
|
var deferred = this.deferreds[requestId];
|
||||||
if (!deferred) {
|
if (!deferred) {
|
||||||
Tomahawk.log("Deferred object with the given requestId is not present!");
|
Tomahawk.log("Deferred object with the given requestId is not present!");
|
||||||
}
|
}
|
||||||
deferred.resolve(result);
|
deferred.resolve(result);
|
||||||
|
delete this.deferreds[requestId];
|
||||||
|
},
|
||||||
|
reportNativeScriptJobError: function(requestId, error) {
|
||||||
|
var deferred = this.deferreds[requestId];
|
||||||
|
if (!deferred) {
|
||||||
|
console.log("Deferred object with the given requestId is not present!");
|
||||||
|
}
|
||||||
|
deferred.reject(error);
|
||||||
|
delete this.deferreds[requestId];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1215,8 +1106,6 @@ Tomahawk.Country = {
|
|||||||
LatinAmericaAndTheCaribbean: 246
|
LatinAmericaAndTheCaribbean: 246
|
||||||
};
|
};
|
||||||
|
|
||||||
Tomahawk.collections = [];
|
|
||||||
|
|
||||||
Tomahawk.Collection = {
|
Tomahawk.Collection = {
|
||||||
BrowseCapability: {
|
BrowseCapability: {
|
||||||
Artists: 1,
|
Artists: 1,
|
||||||
@@ -1664,6 +1553,14 @@ Tomahawk.Collection = {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_adapter_resolve: function (params) {
|
||||||
|
return RSVP.Promise.resolve(this.resolve(params)).then(function (results) {
|
||||||
|
return {
|
||||||
|
'tracks': results
|
||||||
|
};
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
resolve: function (params) {
|
resolve: function (params) {
|
||||||
var resultIds = Tomahawk.resolveFromFuzzyIndex(params.artist, params.album, params.track);
|
var resultIds = Tomahawk.resolveFromFuzzyIndex(params.artist, params.album, params.track);
|
||||||
return this._fuzzyIndexIdsToTracks(resultIds);
|
return this._fuzzyIndexIdsToTracks(resultIds);
|
||||||
@@ -1671,7 +1568,12 @@ Tomahawk.Collection = {
|
|||||||
|
|
||||||
search: function (params) {
|
search: function (params) {
|
||||||
var resultIds = Tomahawk.searchFuzzyIndex(params.query);
|
var resultIds = Tomahawk.searchFuzzyIndex(params.query);
|
||||||
return this._fuzzyIndexIdsToTracks(resultIds);
|
|
||||||
|
return this._fuzzyIndexIdsToTracks(resultIds).then(function(tracks) {
|
||||||
|
return {
|
||||||
|
tracks: tracks
|
||||||
|
};
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
tracks: function (params, where) {
|
tracks: function (params, where) {
|
||||||
@@ -1715,7 +1617,7 @@ Tomahawk.Collection = {
|
|||||||
);
|
);
|
||||||
return t.execDeferredStatements();
|
return t.execDeferredStatements();
|
||||||
}).then(function (results) {
|
}).then(function (results) {
|
||||||
return {results: Tomahawk.resolver.instance._convertUrls(results[0])};
|
return {tracks: results[0]};
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -1889,71 +1791,13 @@ Tomahawk.Collection = {
|
|||||||
Tomahawk.Collection.BrowseCapability.Albums,
|
Tomahawk.Collection.BrowseCapability.Albums,
|
||||||
Tomahawk.Collection.BrowseCapability.Tracks];
|
Tomahawk.Collection.BrowseCapability.Tracks];
|
||||||
return this.settings;
|
return this.settings;
|
||||||
|
},
|
||||||
|
|
||||||
|
getStreamUrl: function(params) {
|
||||||
|
if(this.resolver) {
|
||||||
|
return this.resolver.getStreamUrl(params);
|
||||||
|
}
|
||||||
|
|
||||||
|
return params;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Legacy compability for 0.8 and before
|
|
||||||
Tomahawk.reportCapabilities = function (capabilities) {
|
|
||||||
if (capabilities & TomahawkResolverCapability.Browsable) {
|
|
||||||
Tomahawk.PluginManager.registerPlugin("collection", Tomahawk.resolver.instance);
|
|
||||||
}
|
|
||||||
|
|
||||||
Tomahawk.nativeReportCapabilities(capabilities);
|
|
||||||
};
|
|
||||||
|
|
||||||
Tomahawk.addArtistResults = Tomahawk.addAlbumResults = Tomahawk.addAlbumTrackResults
|
|
||||||
= function (result) {
|
|
||||||
Tomahawk.PluginManager.resolve[result.qid](result);
|
|
||||||
delete Tomahawk.PluginManager.resolve[result.qid];
|
|
||||||
};
|
|
||||||
|
|
||||||
Tomahawk.addTrackResults = function (result) {
|
|
||||||
Tomahawk.PluginManager.resolve[result.qid](result.results);
|
|
||||||
delete Tomahawk.PluginManager.resolve[result.qid];
|
|
||||||
};
|
|
||||||
|
|
||||||
Tomahawk.reportStreamUrl = function (qid, streamUrl, headers) {
|
|
||||||
Tomahawk.PluginManager.resolve[qid]({
|
|
||||||
url: streamUrl,
|
|
||||||
headers: headers
|
|
||||||
});
|
|
||||||
delete Tomahawk.PluginManager.resolve[qid];
|
|
||||||
};
|
|
||||||
|
|
||||||
Tomahawk.addUrlResult = function (url, result) {
|
|
||||||
/* Merge the whole mess into one consistent result which is independent of type
|
|
||||||
var cleanResult = {
|
|
||||||
type: result.type,
|
|
||||||
guid: result.guid,
|
|
||||||
info: result.info,
|
|
||||||
creator: result.creator,
|
|
||||||
linkUrl: result.url
|
|
||||||
};
|
|
||||||
if (cleanResult.type == "track") {
|
|
||||||
cleanResult.track = result.title;
|
|
||||||
cleanResult.artist = result.artist;
|
|
||||||
} else if (cleanResult.type == "artist") {
|
|
||||||
cleanResult.artist = result.name;
|
|
||||||
} else if (cleanResult.type == "album") {
|
|
||||||
cleanResult.album = result.name;
|
|
||||||
cleanResult.artist = result.artist;
|
|
||||||
} else if (cleanResult.type == "playlist") {
|
|
||||||
cleanResult.title = result.title;
|
|
||||||
} else if (cleanResult.type == "xspf-url") {
|
|
||||||
cleanResult.url = result.url;
|
|
||||||
}
|
|
||||||
if (result.tracks) {
|
|
||||||
cleanResult.tracks = [];
|
|
||||||
var i;
|
|
||||||
for (i=0;i<result.tracks.length;i++) {
|
|
||||||
var cleanTrack = {
|
|
||||||
track: result.tracks[i].title,
|
|
||||||
artist: result.tracks[i].artist
|
|
||||||
};
|
|
||||||
cleanResult.push(cleanTrack)
|
|
||||||
}
|
|
||||||
Tomahawk.PluginManager.resolve[url](cleanResult);
|
|
||||||
*/
|
|
||||||
Tomahawk.PluginManager.resolve[url](result);
|
|
||||||
delete Tomahawk.PluginManager.resolve[url];
|
|
||||||
};
|
|
||||||
|
@@ -199,7 +199,6 @@ list(APPEND libSources
|
|||||||
MetaPlaylistInterface.cpp
|
MetaPlaylistInterface.cpp
|
||||||
Query.cpp
|
Query.cpp
|
||||||
Result.cpp
|
Result.cpp
|
||||||
ResultProvider.cpp
|
|
||||||
Source.cpp
|
Source.cpp
|
||||||
Track.cpp
|
Track.cpp
|
||||||
TrackData.cpp
|
TrackData.cpp
|
||||||
|
@@ -175,7 +175,7 @@ DropJob::acceptsMimeData( const QMimeData* data, DropJob::DropTypes acceptedType
|
|||||||
// Check Scriptresolvers
|
// Check Scriptresolvers
|
||||||
foreach ( QPointer<ExternalResolver> resolver, Pipeline::instance()->scriptResolvers() )
|
foreach ( QPointer<ExternalResolver> resolver, Pipeline::instance()->scriptResolvers() )
|
||||||
{
|
{
|
||||||
if ( resolver->canParseUrl( url, ExternalResolver::Playlist ) )
|
if ( resolver->canParseUrl( url, ExternalResolver::UrlTypePlaylist ) )
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -201,7 +201,7 @@ DropJob::acceptsMimeData( const QMimeData* data, DropJob::DropTypes acceptedType
|
|||||||
// Check Scriptresolvers
|
// Check Scriptresolvers
|
||||||
foreach ( QPointer<ExternalResolver> resolver, Pipeline::instance()->scriptResolvers() )
|
foreach ( QPointer<ExternalResolver> resolver, Pipeline::instance()->scriptResolvers() )
|
||||||
{
|
{
|
||||||
if ( resolver->canParseUrl( url, ExternalResolver::Track ) )
|
if ( resolver->canParseUrl( url, ExternalResolver::UrlTypeTrack ) )
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -218,7 +218,7 @@ DropJob::acceptsMimeData( const QMimeData* data, DropJob::DropTypes acceptedType
|
|||||||
// Check Scriptresolvers
|
// Check Scriptresolvers
|
||||||
foreach ( QPointer<ExternalResolver> resolver, Pipeline::instance()->scriptResolvers() )
|
foreach ( QPointer<ExternalResolver> resolver, Pipeline::instance()->scriptResolvers() )
|
||||||
{
|
{
|
||||||
if ( resolver->canParseUrl( url, ExternalResolver::Album ) )
|
if ( resolver->canParseUrl( url, ExternalResolver::UrlTypeAlbum ) )
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -235,7 +235,7 @@ DropJob::acceptsMimeData( const QMimeData* data, DropJob::DropTypes acceptedType
|
|||||||
// Check Scriptresolvers
|
// Check Scriptresolvers
|
||||||
foreach ( QPointer<ExternalResolver> resolver, Pipeline::instance()->scriptResolvers() )
|
foreach ( QPointer<ExternalResolver> resolver, Pipeline::instance()->scriptResolvers() )
|
||||||
{
|
{
|
||||||
if ( resolver->canParseUrl( url, ExternalResolver::Artist ) )
|
if ( resolver->canParseUrl( url, ExternalResolver::UrlTypeArtist ) )
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -306,7 +306,7 @@ DropJob::isDropType( DropJob::DropType desired, const QMimeData* data )
|
|||||||
// Check Scriptresolvers
|
// Check Scriptresolvers
|
||||||
foreach ( QPointer<ExternalResolver> resolver, Pipeline::instance()->scriptResolvers() )
|
foreach ( QPointer<ExternalResolver> resolver, Pipeline::instance()->scriptResolvers() )
|
||||||
{
|
{
|
||||||
if ( resolver->canParseUrl( url, ExternalResolver::Playlist ) )
|
if ( resolver->canParseUrl( url, ExternalResolver::UrlTypePlaylist ) )
|
||||||
{
|
{
|
||||||
tLog( LOGVERBOSE ) << Q_FUNC_INFO << "Accepting current drop as a playlist" << resolver->name();
|
tLog( LOGVERBOSE ) << Q_FUNC_INFO << "Accepting current drop as a playlist" << resolver->name();
|
||||||
return true;
|
return true;
|
||||||
@@ -763,7 +763,7 @@ DropJob::handleTrackUrls( const QString& urls )
|
|||||||
{
|
{
|
||||||
foreach ( QPointer<ExternalResolver> resolver, Pipeline::instance()->scriptResolvers() )
|
foreach ( QPointer<ExternalResolver> resolver, Pipeline::instance()->scriptResolvers() )
|
||||||
{
|
{
|
||||||
if ( resolver->canParseUrl( track, ExternalResolver::Any ) )
|
if ( resolver->canParseUrl( track, ExternalResolver::UrlTypeAny ) )
|
||||||
{
|
{
|
||||||
ScriptCommand_LookupUrl* cmd = new ScriptCommand_LookupUrl( resolver, track );
|
ScriptCommand_LookupUrl* cmd = new ScriptCommand_LookupUrl( resolver, track );
|
||||||
connect( cmd, SIGNAL( information( QString, QSharedPointer<QObject> ) ), this, SLOT( informationForUrl( QString, QSharedPointer<QObject> ) ) );
|
connect( cmd, SIGNAL( information( QString, QSharedPointer<QObject> ) ), this, SLOT( informationForUrl( QString, QSharedPointer<QObject> ) ) );
|
||||||
|
@@ -167,7 +167,7 @@ GlobalActionManager::openUrl( const QString& url )
|
|||||||
QList< QPointer< ExternalResolver > > possibleResolvers;
|
QList< QPointer< ExternalResolver > > possibleResolvers;
|
||||||
foreach ( QPointer<ExternalResolver> resolver, Pipeline::instance()->scriptResolvers() )
|
foreach ( QPointer<ExternalResolver> resolver, Pipeline::instance()->scriptResolvers() )
|
||||||
{
|
{
|
||||||
if ( resolver->canParseUrl( url, ExternalResolver::Any ) )
|
if ( resolver->canParseUrl( url, ExternalResolver::UrlTypeAny ) )
|
||||||
{
|
{
|
||||||
canParse = true;
|
canParse = true;
|
||||||
possibleResolvers << resolver;
|
possibleResolvers << resolver;
|
||||||
|
@@ -323,6 +323,13 @@ Pipeline::resolve( QID qid, bool prioritized, bool temporaryQuery )
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
Pipeline::reportError( QID qid, Tomahawk::Resolver* r )
|
||||||
|
{
|
||||||
|
reportResults( qid, r, QList< result_ptr>() );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
Pipeline::reportResults( QID qid, Tomahawk::Resolver* r, const QList< result_ptr >& results )
|
Pipeline::reportResults( QID qid, Tomahawk::Resolver* r, const QList< result_ptr >& results )
|
||||||
{
|
{
|
||||||
@@ -333,7 +340,7 @@ Pipeline::reportResults( QID qid, Tomahawk::Resolver* r, const QList< result_ptr
|
|||||||
{
|
{
|
||||||
if ( !results.isEmpty() )
|
if ( !results.isEmpty() )
|
||||||
{
|
{
|
||||||
ResultProvider* resolvedBy = results[0]->resolvedBy();
|
Resolver* resolvedBy = results[0]->resolvedBy();
|
||||||
if ( resolvedBy )
|
if ( resolvedBy )
|
||||||
{
|
{
|
||||||
tDebug() << "Result arrived too late for:" << qid << "by" << resolvedBy->name();
|
tDebug() << "Result arrived too late for:" << qid << "by" << resolvedBy->name();
|
||||||
|
@@ -54,6 +54,7 @@ public:
|
|||||||
unsigned int pendingQueryCount() const;
|
unsigned int pendingQueryCount() const;
|
||||||
unsigned int activeQueryCount() const;
|
unsigned int activeQueryCount() const;
|
||||||
|
|
||||||
|
void reportError( QID qid, Tomahawk::Resolver* r );
|
||||||
void reportResults( QID qid, Tomahawk::Resolver* r, const QList< result_ptr >& results );
|
void reportResults( QID qid, Tomahawk::Resolver* r, const QList< result_ptr >& results );
|
||||||
void reportAlbums( QID qid, const QList< album_ptr >& albums );
|
void reportAlbums( QID qid, const QList< album_ptr >& albums );
|
||||||
void reportArtists( QID qid, const QList< artist_ptr >& artists );
|
void reportArtists( QID qid, const QList< artist_ptr >& artists );
|
||||||
|
@@ -491,7 +491,7 @@ Result::setFileId( unsigned int id )
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Tomahawk::ResultProvider*
|
Tomahawk::Resolver*
|
||||||
Result::resolvedBy() const
|
Result::resolvedBy() const
|
||||||
{
|
{
|
||||||
if ( !m_collection.isNull() )
|
if ( !m_collection.isNull() )
|
||||||
|
@@ -21,7 +21,6 @@
|
|||||||
#ifndef RESULT_H
|
#ifndef RESULT_H
|
||||||
#define RESULT_H
|
#define RESULT_H
|
||||||
|
|
||||||
#include "ResultProvider.h"
|
|
||||||
#include "DownloadJob.h"
|
#include "DownloadJob.h"
|
||||||
#include "utils/TomahawkUtils.h"
|
#include "utils/TomahawkUtils.h"
|
||||||
#include "Typedefs.h"
|
#include "Typedefs.h"
|
||||||
@@ -86,9 +85,9 @@ public:
|
|||||||
void setResolvedByResolver( Tomahawk::Resolver* resolver );
|
void setResolvedByResolver( Tomahawk::Resolver* resolver );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is very bad. ResultProvider is not a QObject and thus can not be tracked by a qt smart pointer ... :-(
|
* TODO: Make this a smart pointer
|
||||||
*/
|
*/
|
||||||
ResultProvider* resolvedBy() const;
|
Resolver* resolvedBy() const;
|
||||||
|
|
||||||
RID id() const;
|
RID id() const;
|
||||||
bool isOnline() const;
|
bool isOnline() const;
|
||||||
|
@@ -1,24 +0,0 @@
|
|||||||
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
|
|
||||||
*
|
|
||||||
* Copyright (C) 2015 Dominik Schmidt <domme@tomahawk-player.org>
|
|
||||||
*
|
|
||||||
* Tomahawk is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Tomahawk is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
#include "ResultProvider.h"
|
|
||||||
|
|
||||||
using namespace Tomahawk;
|
|
||||||
|
|
||||||
ResultProvider::~ResultProvider()
|
|
||||||
{
|
|
||||||
}
|
|
@@ -1,42 +0,0 @@
|
|||||||
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
|
|
||||||
*
|
|
||||||
* Copyright (C) 2015 Dominik Schmidt <domme@tomahawk-player.org>
|
|
||||||
*
|
|
||||||
* Tomahawk is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Tomahawk is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
#ifndef TOMAHAWK_RESULTPROVIDER_H
|
|
||||||
#define TOMAHAWK_RESULTPROVIDER_H
|
|
||||||
|
|
||||||
#include "DllMacro.h"
|
|
||||||
|
|
||||||
class QPixmap;
|
|
||||||
class QString;
|
|
||||||
class QSize;
|
|
||||||
|
|
||||||
namespace Tomahawk
|
|
||||||
{
|
|
||||||
|
|
||||||
class DLLEXPORT ResultProvider
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
virtual ~ResultProvider();
|
|
||||||
|
|
||||||
virtual QString name() const = 0;
|
|
||||||
virtual QPixmap icon( const QSize& size ) const = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // TOMAHAWK_RESULTPROVIDER_H
|
|
@@ -524,7 +524,7 @@ void ResolverAccount::testConfig()
|
|||||||
if ( resolver )
|
if ( resolver )
|
||||||
{
|
{
|
||||||
QVariantMap data = resolver->loadDataFromWidgets();
|
QVariantMap data = resolver->loadDataFromWidgets();
|
||||||
ScriptJob* job = resolver->scriptObject()->invoke( "_testConfig", data );
|
ScriptJob* job = resolver->scriptObject()->invoke( "testConfig", data );
|
||||||
connect( job, SIGNAL( done( QVariantMap ) ), SLOT( onTestConfig( QVariantMap ) ) );
|
connect( job, SIGNAL( done( QVariantMap ) ), SLOT( onTestConfig( QVariantMap ) ) );
|
||||||
job->start();
|
job->start();
|
||||||
}
|
}
|
||||||
|
@@ -32,6 +32,8 @@
|
|||||||
#include "playlist/SingleTrackPlaylistInterface.h"
|
#include "playlist/SingleTrackPlaylistInterface.h"
|
||||||
#include "utils/Closure.h"
|
#include "utils/Closure.h"
|
||||||
#include "utils/Logger.h"
|
#include "utils/Logger.h"
|
||||||
|
#include "utils/NetworkReply.h"
|
||||||
|
#include "utils/NetworkAccessManager.h"
|
||||||
|
|
||||||
#include "Album.h"
|
#include "Album.h"
|
||||||
#include "Artist.h"
|
#include "Artist.h"
|
||||||
@@ -40,6 +42,7 @@
|
|||||||
#include "SourceList.h"
|
#include "SourceList.h"
|
||||||
#include "TomahawkSettings.h"
|
#include "TomahawkSettings.h"
|
||||||
#include "UrlHandler.h"
|
#include "UrlHandler.h"
|
||||||
|
#include "resolvers/ScriptJob.h"
|
||||||
|
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
|
|
||||||
@@ -574,16 +577,64 @@ AudioEngine::loadTrack( const Tomahawk::result_ptr& result )
|
|||||||
|
|
||||||
setCurrentTrack( result );
|
setCurrentTrack( result );
|
||||||
|
|
||||||
if ( !TomahawkUtils::isLocalResult( d->currentTrack->url() ) && !TomahawkUtils::isHttpResult( d->currentTrack->url() )
|
ScriptJob* job = result->resolvedBy()->getStreamUrl( result );
|
||||||
&& !TomahawkUtils::isRtmpResult( d->currentTrack->url() ) )
|
connect( job, SIGNAL( done( QVariantMap ) ), SLOT( gotStreamUrl( QVariantMap ) ) );
|
||||||
|
job->setProperty( "result", QVariant::fromValue( result ) );
|
||||||
|
job->start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
AudioEngine::gotStreamUrl( const QVariantMap& data )
|
||||||
|
{
|
||||||
|
QString streamUrl = data[ "url" ].toString();
|
||||||
|
QVariantMap headers = data[ "headers" ].toMap();
|
||||||
|
Tomahawk::result_ptr result = sender()->property( "result" ).value<result_ptr>();
|
||||||
|
|
||||||
|
if ( streamUrl.isEmpty() || !( TomahawkUtils::isHttpResult( streamUrl ) || TomahawkUtils::isHttpsResult( streamUrl ) || TomahawkUtils::isRtmpResult( streamUrl ) ) )
|
||||||
{
|
{
|
||||||
performLoadIODevice( d->currentTrack, d->currentTrack->url() );
|
// Not an http(s) or RTMP URL, get IO device
|
||||||
|
QSharedPointer< QIODevice > sp;
|
||||||
|
performLoadIODevice( result, streamUrl );
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
QSharedPointer< QIODevice > io;
|
// TODO: just make this part of the http(s) IoDeviceFactory (?)
|
||||||
performLoadTrack( result, result->url(), io );
|
QUrl url = QUrl::fromEncoded( streamUrl.toUtf8() );
|
||||||
|
QNetworkRequest req( url );
|
||||||
|
|
||||||
|
QMap<QString, QString> parsedHeaders;
|
||||||
|
foreach ( const QString& key, headers.keys() )
|
||||||
|
{
|
||||||
|
Q_ASSERT_X( headers[key].canConvert( QVariant::String ), Q_FUNC_INFO, "Expected a Map of string for additional headers" );
|
||||||
|
if ( headers[key].canConvert( QVariant::String ) )
|
||||||
|
{
|
||||||
|
parsedHeaders.insert( key, headers[key].toString() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ( const QString& key, parsedHeaders.keys() )
|
||||||
|
{
|
||||||
|
req.setRawHeader( key.toLatin1(), parsedHeaders[key].toLatin1() );
|
||||||
|
}
|
||||||
|
|
||||||
|
tDebug() << "Creating a QNetworkReply with url:" << req.url().toString();
|
||||||
|
NetworkReply* reply = new NetworkReply( Tomahawk::Utils::nam()->get( req ) );
|
||||||
|
NewClosure( reply, SIGNAL( finalUrlReached() ), this, SLOT( gotRedirectedStreamUrl( Tomahawk::result_ptr, NetworkReply* )), result, reply );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sender()->deleteLater();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
AudioEngine::gotRedirectedStreamUrl( const Tomahawk::result_ptr& result, NetworkReply* reply )
|
||||||
|
{
|
||||||
|
// std::functions cannot accept temporaries as parameters
|
||||||
|
QSharedPointer< QIODevice > sp ( reply->reply(), &QObject::deleteLater );
|
||||||
|
QString url = reply->reply()->url().toString();
|
||||||
|
reply->disconnectFromReply();
|
||||||
|
reply->deleteLater();
|
||||||
|
|
||||||
|
performLoadTrack( result, url, sp );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -28,6 +28,7 @@
|
|||||||
|
|
||||||
#include "DllMacro.h"
|
#include "DllMacro.h"
|
||||||
|
|
||||||
|
class NetworkReply;
|
||||||
class AudioEnginePrivate;
|
class AudioEnginePrivate;
|
||||||
|
|
||||||
class DLLEXPORT AudioEngine : public QObject
|
class DLLEXPORT AudioEngine : public QObject
|
||||||
@@ -180,6 +181,10 @@ signals:
|
|||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void loadTrack( const Tomahawk::result_ptr& result ); //async!
|
void loadTrack( const Tomahawk::result_ptr& result ); //async!
|
||||||
|
void gotStreamUrl( const QVariantMap& data );
|
||||||
|
void gotRedirectedStreamUrl( const Tomahawk::result_ptr& result, NetworkReply* reply );
|
||||||
|
|
||||||
|
|
||||||
void performLoadIODevice( const Tomahawk::result_ptr& result, const QString& url ); //only call from loadTrack kthxbi
|
void performLoadIODevice( const Tomahawk::result_ptr& result, const QString& url ); //only call from loadTrack kthxbi
|
||||||
void performLoadTrack( const Tomahawk::result_ptr result, const QString url, QSharedPointer< QIODevice > io ); //only call from loadTrack or performLoadIODevice kthxbi
|
void performLoadTrack( const Tomahawk::result_ptr result, const QString url, QSharedPointer< QIODevice > io ); //only call from loadTrack or performLoadIODevice kthxbi
|
||||||
void loadPreviousTrack();
|
void loadPreviousTrack();
|
||||||
|
@@ -35,7 +35,7 @@ using namespace Tomahawk;
|
|||||||
|
|
||||||
|
|
||||||
Collection::Collection( const source_ptr& source, const QString& name, QObject* parent )
|
Collection::Collection( const source_ptr& source, const QString& name, QObject* parent )
|
||||||
: QObject( parent )
|
: Resolver( parent )
|
||||||
, m_name( name )
|
, m_name( name )
|
||||||
, m_lastmodified( 0 )
|
, m_lastmodified( 0 )
|
||||||
, m_changed( false )
|
, m_changed( false )
|
||||||
|
@@ -33,7 +33,7 @@
|
|||||||
#include "collection/ArtistsRequest.h"
|
#include "collection/ArtistsRequest.h"
|
||||||
#include "collection/AlbumsRequest.h"
|
#include "collection/AlbumsRequest.h"
|
||||||
#include "collection/TracksRequest.h"
|
#include "collection/TracksRequest.h"
|
||||||
#include "../ResultProvider.h"
|
#include "../resolvers/Resolver.h"
|
||||||
|
|
||||||
#include "DllMacro.h"
|
#include "DllMacro.h"
|
||||||
|
|
||||||
@@ -46,7 +46,7 @@
|
|||||||
namespace Tomahawk
|
namespace Tomahawk
|
||||||
{
|
{
|
||||||
|
|
||||||
class DLLEXPORT Collection : public QObject, public ResultProvider
|
class DLLEXPORT Collection : public Resolver
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
|
@@ -251,3 +251,32 @@ DatabaseCollection::stationCreated( const source_ptr& source, const QVariantList
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Resolver interface
|
||||||
|
*
|
||||||
|
* We implement searching the database in the DatabaseResolver which avoids a n+1 query here.
|
||||||
|
* We can't simply let ScriptCollection inherit Collection and Resolver because both are QObjects,
|
||||||
|
* although Resolver doesn't need to be a QObject atm, blocking adding signals/slots to Resolver
|
||||||
|
* in future seems to me worse than violating Liskov's law here. ~ domme
|
||||||
|
*/
|
||||||
|
unsigned int
|
||||||
|
DatabaseCollection::timeout() const
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
unsigned int
|
||||||
|
DatabaseCollection::weight() const
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
DatabaseCollection::resolve( const Tomahawk::query_ptr& query )
|
||||||
|
{
|
||||||
|
Q_UNUSED( query );
|
||||||
|
Q_ASSERT(false);
|
||||||
|
}
|
||||||
|
@@ -65,6 +65,11 @@ public:
|
|||||||
int trackCount() const override;
|
int trackCount() const override;
|
||||||
QPixmap icon( const QSize& size ) const override;
|
QPixmap icon( const QSize& size ) const override;
|
||||||
|
|
||||||
|
// Resolver interface
|
||||||
|
unsigned int weight() const override;
|
||||||
|
unsigned int timeout() const override;
|
||||||
|
void resolve( const Tomahawk::query_ptr& query ) override;
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
virtual void addTracks( const QList<QVariant>& newitems );
|
virtual void addTracks( const QList<QVariant>& newitems );
|
||||||
virtual void removeTracks( const QDir& dir );
|
virtual void removeTracks( const QDir& dir );
|
||||||
|
@@ -262,8 +262,15 @@ TrackDetailView::onResultsChanged()
|
|||||||
resolverLabel->setFont( f );
|
resolverLabel->setFont( f );
|
||||||
resolverLabel->setStyleSheet( "QLabel { color: rgba( 0, 0, 0, 50% ) }" );
|
resolverLabel->setStyleSheet( "QLabel { color: rgba( 0, 0, 0, 50% ) }" );
|
||||||
resolverLabel->setText( QString( "%1 - %2" ).arg( result->track()->track() ).arg( result->track()->artist() ) );
|
resolverLabel->setText( QString( "%1 - %2" ).arg( result->track()->track() ).arg( result->track()->artist() ) );
|
||||||
resolverLabel->setToolTip( QString( "%1 by %2%3" ).arg( result->track()->track() ).arg( result->track()->artist() )
|
resolverLabel->setToolTip(
|
||||||
.arg( !result->track()->album().isEmpty() ? QString( " " ) + tr( "on %1" ).arg( result->track()->album() ) : QString() ) );
|
QString( "%1 by %2%3 (%4)" )
|
||||||
|
.arg( result->track()->track() )
|
||||||
|
.arg( result->track()->artist() )
|
||||||
|
.arg( !result->track()->album().isEmpty() ? QString( " " ) + tr( "on %1" ).arg( result->track()->album() ) : QString() )
|
||||||
|
.arg( result->friendlySource() )
|
||||||
|
);
|
||||||
|
|
||||||
|
;
|
||||||
resolverLabel->setFixedWidth( width() - 32 - 4 );
|
resolverLabel->setFixedWidth( width() - 32 - 4 );
|
||||||
|
|
||||||
NewClosure( resolverLabel, SIGNAL( clicked() ), const_cast< AudioEngine* >( AudioEngine::instance() ),
|
NewClosure( resolverLabel, SIGNAL( clicked() ), const_cast< AudioEngine* >( AudioEngine::instance() ),
|
||||||
|
@@ -68,11 +68,12 @@ public:
|
|||||||
|
|
||||||
enum UrlType
|
enum UrlType
|
||||||
{
|
{
|
||||||
Any = 0x00,
|
UrlTypeAny = 0x00,
|
||||||
Playlist = 0x01,
|
UrlTypePlaylist = 0x01,
|
||||||
Track = 0x02,
|
UrlTypeTrack = 0x02,
|
||||||
Album = 0x04,
|
UrlTypeAlbum = 0x04,
|
||||||
Artist = 0x08
|
UrlTypeArtist = 0x08,
|
||||||
|
UrlTypeXspf = 0x10
|
||||||
};
|
};
|
||||||
Q_DECLARE_FLAGS( UrlTypes, UrlType )
|
Q_DECLARE_FLAGS( UrlTypes, UrlType )
|
||||||
Q_FLAGS( UrlTypes )
|
Q_FLAGS( UrlTypes )
|
||||||
|
@@ -170,6 +170,41 @@ JSAccount::syncInvoke( const scriptobject_ptr& scriptObject, const QString& meth
|
|||||||
return evaluateJavaScriptWithResult( eval );
|
return evaluateJavaScriptWithResult( eval );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
JSAccount::reportNativeScriptJobResult( int resultId, const QVariantMap& result )
|
||||||
|
{
|
||||||
|
QString eval = QString(
|
||||||
|
"Tomahawk.NativeScriptJobManager.reportNativeScriptJobResult("
|
||||||
|
"%1," // requestId
|
||||||
|
"%2" // results
|
||||||
|
");"
|
||||||
|
).arg( resultId )
|
||||||
|
.arg( serializeQVariantMap( result ) );
|
||||||
|
|
||||||
|
// Remove when new scripting api turned out to work reliably
|
||||||
|
tDebug( LOGVERBOSE ) << Q_FUNC_INFO << eval;
|
||||||
|
|
||||||
|
evaluateJavaScript( eval );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
JSAccount::reportNativeScriptJobError( int resultId, const QVariantMap& error )
|
||||||
|
{
|
||||||
|
QString eval = QString(
|
||||||
|
"Tomahawk.NativeScriptJobManager.reportNativeScriptJobError("
|
||||||
|
"%1," // requestId
|
||||||
|
"%2" // results
|
||||||
|
");"
|
||||||
|
).arg( resultId )
|
||||||
|
.arg( serializeQVariantMap( error ) );
|
||||||
|
|
||||||
|
// Remove when new scripting api turned out to work reliably
|
||||||
|
tDebug( LOGVERBOSE ) << Q_FUNC_INFO << eval;
|
||||||
|
|
||||||
|
evaluateJavaScript( eval );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
QVariant
|
QVariant
|
||||||
JSAccount::evaluateJavaScriptInternal( const QString& scriptSource )
|
JSAccount::evaluateJavaScriptInternal( const QString& scriptSource )
|
||||||
|
@@ -71,6 +71,9 @@ public:
|
|||||||
|
|
||||||
static QString serializeQVariantMap(const QVariantMap& map);
|
static QString serializeQVariantMap(const QVariantMap& map);
|
||||||
|
|
||||||
|
void reportNativeScriptJobResult( int resultId, const QVariantMap& result ) override;
|
||||||
|
void reportNativeScriptJobError( int resultId, const QVariantMap& error ) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/**
|
/**
|
||||||
* Wrap the pure evaluateJavaScript call in here, while the threadings guards are in public methods
|
* Wrap the pure evaluateJavaScript call in here, while the threadings guards are in public methods
|
||||||
|
@@ -44,6 +44,13 @@
|
|||||||
#include "Track.h"
|
#include "Track.h"
|
||||||
#include "ScriptInfoPlugin.h"
|
#include "ScriptInfoPlugin.h"
|
||||||
#include "JSAccount.h"
|
#include "JSAccount.h"
|
||||||
|
#include "ScriptJob.h"
|
||||||
|
|
||||||
|
// lookupUrl stuff
|
||||||
|
#include "playlist/PlaylistTemplate.h"
|
||||||
|
#include "playlist/XspfPlaylistTemplate.h"
|
||||||
|
#include "database/Database.h"
|
||||||
|
#include "database/DatabaseImpl.h"
|
||||||
|
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
@@ -250,12 +257,11 @@ JSResolver::init()
|
|||||||
d->scriptAccount->loadScript( filePath() );
|
d->scriptAccount->loadScript( filePath() );
|
||||||
|
|
||||||
// HACK: register resolver object
|
// HACK: register resolver object
|
||||||
d->scriptAccount->evaluateJavaScript( "Tomahawk.PluginManager.registerPlugin('resolver', Tomahawk.resolver.instance);" )
|
d->scriptAccount->evaluateJavaScript( "Tomahawk.PluginManager.registerPlugin('resolver', Tomahawk.resolver.instance);" );
|
||||||
;
|
|
||||||
// init resolver
|
// init resolver
|
||||||
resolverInit();
|
scriptObject()->syncInvoke( "init" );
|
||||||
|
|
||||||
QVariantMap m = resolverSettings();
|
QVariantMap m = scriptObject()->syncInvoke( "settings" ).toMap();
|
||||||
d->name = m.value( "name" ).toString();
|
d->name = m.value( "name" ).toString();
|
||||||
d->weight = m.value( "weight", 0 ).toUInt();
|
d->weight = m.value( "weight", 0 ).toUInt();
|
||||||
d->timeout = m.value( "timeout", 25 ).toUInt() * 1000;
|
d->timeout = m.value( "timeout", 25 ).toUInt() * 1000;
|
||||||
@@ -319,20 +325,13 @@ JSResolver::canParseUrl( const QString& url, UrlType type )
|
|||||||
{
|
{
|
||||||
Q_D( const JSResolver );
|
Q_D( const JSResolver );
|
||||||
|
|
||||||
// FIXME: How can we do this?
|
|
||||||
/*if ( QThread::currentThread() != thread() )
|
|
||||||
{
|
|
||||||
QMetaObject::invokeMethod( this, "canParseUrl", Qt::QueuedConnection,
|
|
||||||
Q_ARG( QString, url ) );
|
|
||||||
return;
|
|
||||||
}*/
|
|
||||||
|
|
||||||
if ( d->capabilities.testFlag( UrlLookup ) )
|
if ( d->capabilities.testFlag( UrlLookup ) )
|
||||||
{
|
{
|
||||||
QString eval = QString( "canParseUrl( '%1', %2 )" )
|
QVariantMap arguments;
|
||||||
.arg( JSAccount::escape( QString( url ) ) )
|
arguments["url"] = url;
|
||||||
.arg( (int) type );
|
arguments["type"] = (int) type;
|
||||||
return callOnResolver( eval ).toBool();
|
|
||||||
|
return scriptObject()->syncInvoke( "canParseUrl", arguments ).toBool();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -345,34 +344,185 @@ JSResolver::canParseUrl( const QString& url, UrlType type )
|
|||||||
void
|
void
|
||||||
JSResolver::lookupUrl( const QString& url )
|
JSResolver::lookupUrl( const QString& url )
|
||||||
{
|
{
|
||||||
if ( QThread::currentThread() != thread() )
|
|
||||||
{
|
|
||||||
QMetaObject::invokeMethod( this, "lookupUrl", Qt::QueuedConnection,
|
|
||||||
Q_ARG( QString, url ) );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Q_D( const JSResolver );
|
Q_D( const JSResolver );
|
||||||
|
|
||||||
|
|
||||||
if ( !d->capabilities.testFlag( UrlLookup ) )
|
if ( !d->capabilities.testFlag( UrlLookup ) )
|
||||||
{
|
{
|
||||||
emit informationFound( url, QSharedPointer<QObject>() );
|
emit informationFound( url, QSharedPointer<QObject>() );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString eval = QString( "lookupUrl( '%1' )" )
|
QVariantMap arguments;
|
||||||
.arg( JSAccount::escape( QString( url ) ) );
|
arguments["url"] = url;
|
||||||
|
Tomahawk::ScriptJob* job = scriptObject()->invoke( "lookupUrl", arguments );
|
||||||
|
connect( job, SIGNAL( done( QVariantMap ) ), SLOT( onLookupUrlRequestDone( QVariantMap ) ) );
|
||||||
|
job->setProperty( "url", url );
|
||||||
|
job->start();
|
||||||
|
}
|
||||||
|
|
||||||
QVariantMap m = callOnResolver( eval ).toMap();
|
|
||||||
if ( m.isEmpty() )
|
void
|
||||||
|
JSResolver::onLookupUrlRequestDone( const QVariantMap& result )
|
||||||
|
{
|
||||||
|
sender()->deleteLater();
|
||||||
|
|
||||||
|
QString url = sender()->property( "url" ).toString();
|
||||||
|
|
||||||
|
tLog() << "ON LOOKUP URL REQUEST DONE" << url << result;
|
||||||
|
|
||||||
|
// It may seem a bit weird, but currently no slot should do anything
|
||||||
|
// more as we starting on a new URL and not task are waiting for it yet.
|
||||||
|
m_pendingUrl = QString();
|
||||||
|
m_pendingAlbum = album_ptr();
|
||||||
|
|
||||||
|
UrlTypes type = (UrlTypes) result.value( "type" ).toInt();
|
||||||
|
if ( type == UrlTypeArtist )
|
||||||
{
|
{
|
||||||
// if the resolver doesn't return anything, async api is used
|
QString name = result.value( "name" ).toString();
|
||||||
return;
|
Q_ASSERT( !name.isEmpty() );
|
||||||
|
emit informationFound( url, Artist::get( name, true ).objectCast<QObject>() );
|
||||||
|
}
|
||||||
|
else if ( type == UrlTypeAlbum )
|
||||||
|
{
|
||||||
|
QString name = result.value( "name" ).toString();
|
||||||
|
QString artist = result.value( "artist" ).toString();
|
||||||
|
album_ptr album = Album::get( Artist::get( artist, true ), name );
|
||||||
|
m_pendingUrl = url;
|
||||||
|
m_pendingAlbum = album;
|
||||||
|
connect( album.data(), SIGNAL( tracksAdded( QList<Tomahawk::query_ptr>, Tomahawk::ModelMode, Tomahawk::collection_ptr ) ),
|
||||||
|
SLOT( tracksAdded( QList<Tomahawk::query_ptr>, Tomahawk::ModelMode, Tomahawk::collection_ptr ) ) );
|
||||||
|
if ( !album->tracks().isEmpty() )
|
||||||
|
{
|
||||||
|
emit informationFound( url, album.objectCast<QObject>() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if ( type == UrlTypeTrack )
|
||||||
|
{
|
||||||
|
Tomahawk::query_ptr query = parseTrack( result );
|
||||||
|
if ( query.isNull() )
|
||||||
|
{
|
||||||
|
// A valid track result shoud have non-empty title and artist.
|
||||||
|
tLog() << Q_FUNC_INFO << name() << "Got empty track information for " << url;
|
||||||
|
emit informationFound( url, QSharedPointer<QObject>() );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
emit informationFound( url, query.objectCast<QObject>() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if ( type == UrlTypePlaylist )
|
||||||
|
{
|
||||||
|
QString guid = result.value( "guid" ).toString();
|
||||||
|
Q_ASSERT( !guid.isEmpty() );
|
||||||
|
// Append nodeid to guid to make it globally unique.
|
||||||
|
guid += instanceUUID();
|
||||||
|
|
||||||
|
// Do we already have this playlist loaded?
|
||||||
|
{
|
||||||
|
playlist_ptr playlist = Playlist::get( guid );
|
||||||
|
if ( !playlist.isNull() )
|
||||||
|
{
|
||||||
|
emit informationFound( url, playlist.objectCast<QObject>() );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get all information to build a new playlist but do not build it until we know,
|
||||||
|
// if it is really handled as a playlist and not as a set of tracks.
|
||||||
|
Tomahawk::source_ptr source = SourceList::instance()->getLocal();
|
||||||
|
const QString title = result.value( "title" ).toString();
|
||||||
|
const QString info = result.value( "info" ).toString();
|
||||||
|
const QString creator = result.value( "creator" ).toString();
|
||||||
|
QList<query_ptr> queries;
|
||||||
|
foreach( QVariant track, result.value( "tracks" ).toList() )
|
||||||
|
{
|
||||||
|
query_ptr query = parseTrack( track.toMap() );
|
||||||
|
if ( !query.isNull() )
|
||||||
|
{
|
||||||
|
queries << query;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tLog( LOGVERBOSE ) << Q_FUNC_INFO << name() << "Got playlist for " << url;
|
||||||
|
playlisttemplate_ptr pltemplate( new PlaylistTemplate( source, guid, title, info, creator, false, queries ) );
|
||||||
|
emit informationFound( url, pltemplate.objectCast<QObject>() );
|
||||||
|
}
|
||||||
|
else if ( type == UrlTypeXspf )
|
||||||
|
{
|
||||||
|
QString xspfUrl = result.value( "url" ).toString();
|
||||||
|
Q_ASSERT( !xspfUrl.isEmpty() );
|
||||||
|
QString guid = QString( "xspf-%1-%2" ).arg( xspfUrl.toUtf8().toBase64().constData() ).arg( instanceUUID() );
|
||||||
|
|
||||||
|
// Do we already have this playlist loaded?
|
||||||
|
{
|
||||||
|
playlist_ptr playlist = Playlist::get( guid );
|
||||||
|
if ( !playlist.isNull() )
|
||||||
|
{
|
||||||
|
emit informationFound( url, playlist.objectCast<QObject>() );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Get all information to build a new playlist but do not build it until we know,
|
||||||
|
// if it is really handled as a playlist and not as a set of tracks.
|
||||||
|
Tomahawk::source_ptr source = SourceList::instance()->getLocal();
|
||||||
|
QSharedPointer<XspfPlaylistTemplate> pltemplate( new XspfPlaylistTemplate( xspfUrl, source, guid ) );
|
||||||
|
NewClosure( pltemplate, SIGNAL( tracksLoaded( QList< Tomahawk::query_ptr > ) ),
|
||||||
|
this, SLOT( pltemplateTracksLoadedForUrl( QString, Tomahawk::playlisttemplate_ptr ) ),
|
||||||
|
url, pltemplate.objectCast<Tomahawk::PlaylistTemplate>() );
|
||||||
|
tLog( LOGVERBOSE ) << Q_FUNC_INFO << name() << "Got playlist for " << url;
|
||||||
|
pltemplate->load();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
tLog( LOGVERBOSE ) << Q_FUNC_INFO << name() << "No usable information found for " << url;
|
||||||
|
emit informationFound( url, QSharedPointer<QObject>() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
query_ptr
|
||||||
|
JSResolver::parseTrack( const QVariantMap& track )
|
||||||
|
{
|
||||||
|
QString title = track.value( "track" ).toString();
|
||||||
|
QString artist = track.value( "artist" ).toString();
|
||||||
|
QString album = track.value( "album" ).toString();
|
||||||
|
if ( title.isEmpty() || artist.isEmpty() )
|
||||||
|
{
|
||||||
|
return query_ptr();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString errorMessage = tr( "Script Resolver Warning: API call %1 returned data synchronously." ).arg( eval );
|
Tomahawk::query_ptr query = Tomahawk::Query::get( artist, title, album );
|
||||||
JobStatusView::instance()->model()->addJob( new ErrorStatusMessage( errorMessage ) );
|
QString resultHint = track.value( "hint" ).toString();
|
||||||
tDebug() << errorMessage << m;
|
if ( !resultHint.isEmpty() )
|
||||||
|
{
|
||||||
|
query->setResultHint( resultHint );
|
||||||
|
query->setSaveHTTPResultHint( true );
|
||||||
|
}
|
||||||
|
|
||||||
|
return query;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
JSResolver::tracksAdded( const QList<query_ptr>&, const ModelMode, const collection_ptr&)
|
||||||
|
{
|
||||||
|
// Check if we still are actively waiting
|
||||||
|
if ( m_pendingAlbum.isNull() || m_pendingUrl.isNull() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
emit informationFound( m_pendingUrl, m_pendingAlbum.objectCast<QObject>() );
|
||||||
|
m_pendingAlbum = album_ptr();
|
||||||
|
m_pendingUrl = QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
JSResolver::pltemplateTracksLoadedForUrl( const QString& url, const playlisttemplate_ptr& pltemplate )
|
||||||
|
{
|
||||||
|
tLog() << Q_FUNC_INFO;
|
||||||
|
emit informationFound( url, pltemplate.objectCast<QObject>() );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -388,32 +538,44 @@ JSResolver::error() const
|
|||||||
void
|
void
|
||||||
JSResolver::resolve( const Tomahawk::query_ptr& query )
|
JSResolver::resolve( const Tomahawk::query_ptr& query )
|
||||||
{
|
{
|
||||||
if ( QThread::currentThread() != thread() )
|
ScriptJob* job = scriptAccount()->resolve( scriptObject(), query );
|
||||||
{
|
|
||||||
QMetaObject::invokeMethod( this, "resolve", Qt::QueuedConnection, Q_ARG(Tomahawk::query_ptr, query) );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString eval;
|
connect( job, SIGNAL( done( QVariantMap ) ), SLOT( onResolveRequestDone( QVariantMap ) ) );
|
||||||
if ( !query->isFullTextQuery() )
|
|
||||||
|
job->start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
JSResolver::onResolveRequestDone( const QVariantMap& data )
|
||||||
|
{
|
||||||
|
Q_ASSERT( QThread::currentThread() == thread() );
|
||||||
|
Q_D( JSResolver );
|
||||||
|
|
||||||
|
ScriptJob* job = qobject_cast< ScriptJob* >( sender() );
|
||||||
|
|
||||||
|
QID qid = job->property( "qid" ).toString();
|
||||||
|
|
||||||
|
if ( job->error() )
|
||||||
{
|
{
|
||||||
eval = QString( "resolve( '%1', '%2', '%3', '%4' )" )
|
Tomahawk::Pipeline::instance()->reportError( qid, this );
|
||||||
.arg( JSAccount::escape( query->id() ) )
|
|
||||||
.arg( JSAccount::escape( query->queryTrack()->artist() ) )
|
|
||||||
.arg( JSAccount::escape( query->queryTrack()->album() ) )
|
|
||||||
.arg( JSAccount::escape( query->queryTrack()->track() ) );
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
eval = QString( "search( '%1', '%2' )" )
|
|
||||||
.arg( JSAccount::escape( query->id() ) )
|
QList< Tomahawk::result_ptr > results = scriptAccount()->parseResultVariantList( data.value( "tracks" ).toList() );
|
||||||
.arg( JSAccount::escape( query->fullTextQuery() ) );
|
|
||||||
|
foreach( const result_ptr& result, results )
|
||||||
|
{
|
||||||
|
result->setResolvedByResolver( this );
|
||||||
|
result->setFriendlySource( name() );
|
||||||
|
}
|
||||||
|
|
||||||
|
Tomahawk::Pipeline::instance()->reportResults( qid, this, results );
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariantMap m = callOnResolver( eval ).toMap();
|
sender()->deleteLater();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
JSResolver::stop()
|
JSResolver::stop()
|
||||||
{
|
{
|
||||||
@@ -433,7 +595,7 @@ JSResolver::loadUi()
|
|||||||
{
|
{
|
||||||
Q_D( JSResolver );
|
Q_D( JSResolver );
|
||||||
|
|
||||||
QVariantMap m = callOnResolver( "getConfigUi()" ).toMap();
|
QVariantMap m = scriptObject()->syncInvoke( "getConfigUi" ).toMap();
|
||||||
|
|
||||||
bool compressed = m.value( "compressed", "false" ).toBool();
|
bool compressed = m.value( "compressed", "false" ).toBool();
|
||||||
qDebug() << "Resolver has a preferences widget! compressed?" << compressed;
|
qDebug() << "Resolver has a preferences widget! compressed?" << compressed;
|
||||||
@@ -485,7 +647,7 @@ JSResolver::saveConfig()
|
|||||||
// qDebug() << Q_FUNC_INFO << saveData;
|
// qDebug() << Q_FUNC_INFO << saveData;
|
||||||
|
|
||||||
d->resolverHelper->setResolverConfig( saveData.toMap() );
|
d->resolverHelper->setResolverConfig( saveData.toMap() );
|
||||||
callOnResolver( "saveUserConfig()" );
|
scriptObject()->syncInvoke( "saveUserConfig" );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -516,39 +678,25 @@ JSResolver::onCapabilitiesChanged( Tomahawk::ExternalResolver::Capabilities capa
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
QVariantMap
|
|
||||||
JSResolver::resolverSettings()
|
|
||||||
{
|
|
||||||
return callOnResolver( "settings" ).toMap();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
QVariantMap
|
QVariantMap
|
||||||
JSResolver::resolverUserConfig()
|
JSResolver::resolverUserConfig()
|
||||||
{
|
{
|
||||||
return callOnResolver( "getUserConfig()" ).toMap();
|
return scriptObject()->syncInvoke( "getUserConfig" ).toMap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
QVariantMap
|
QString
|
||||||
JSResolver::resolverInit()
|
JSResolver::instanceUUID()
|
||||||
{
|
{
|
||||||
return callOnResolver( "init()" ).toMap();
|
return Tomahawk::Database::instance()->impl()->dbid();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
QVariant
|
ScriptJob*
|
||||||
JSResolver::callOnResolver( const QString& scriptSource )
|
JSResolver::getStreamUrl( const result_ptr& result )
|
||||||
{
|
{
|
||||||
Q_D( JSResolver );
|
QVariantMap arguments;
|
||||||
|
arguments["url"] = result->url();
|
||||||
|
|
||||||
QString propertyName = scriptSource.split('(').first();
|
return scriptObject()->invoke( "getStreamUrl", arguments );
|
||||||
|
|
||||||
return d->scriptAccount->evaluateJavaScriptWithResult( QString(
|
|
||||||
"if(Tomahawk.resolver.instance['_adapter_%1']) {"
|
|
||||||
" Tomahawk.resolver.instance._adapter_%2;"
|
|
||||||
"} else {"
|
|
||||||
" Tomahawk.resolver.instance.%2"
|
|
||||||
"}"
|
|
||||||
).arg( propertyName ).arg( scriptSource ) );
|
|
||||||
}
|
}
|
||||||
|
@@ -75,6 +75,8 @@ public:
|
|||||||
|
|
||||||
ScriptAccount* scriptAccount() const;
|
ScriptAccount* scriptAccount() const;
|
||||||
|
|
||||||
|
ScriptJob* getStreamUrl( const result_ptr& result ) override;
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void resolve( const Tomahawk::query_ptr& query ) override;
|
void resolve( const Tomahawk::query_ptr& query ) override;
|
||||||
void stop() override;
|
void stop() override;
|
||||||
@@ -89,6 +91,10 @@ signals:
|
|||||||
protected:
|
protected:
|
||||||
QVariant callOnResolver( const QString& scriptSource );
|
QVariant callOnResolver( const QString& scriptSource );
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onResolveRequestDone(const QVariantMap& data);
|
||||||
|
void onLookupUrlRequestDone(const QVariantMap& data);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void init();
|
void init();
|
||||||
|
|
||||||
@@ -96,12 +102,20 @@ private:
|
|||||||
void onCapabilitiesChanged( Capabilities capabilities );
|
void onCapabilitiesChanged( Capabilities capabilities );
|
||||||
|
|
||||||
// encapsulate javascript calls
|
// encapsulate javascript calls
|
||||||
QVariantMap resolverSettings();
|
|
||||||
QVariantMap resolverUserConfig();
|
QVariantMap resolverUserConfig();
|
||||||
QVariantMap resolverInit();
|
|
||||||
|
|
||||||
Q_DECLARE_PRIVATE( JSResolver )
|
Q_DECLARE_PRIVATE( JSResolver )
|
||||||
QScopedPointer<JSResolverPrivate> d_ptr;
|
QScopedPointer<JSResolverPrivate> d_ptr;
|
||||||
|
|
||||||
|
|
||||||
|
// TODO: move lookupUrl stuff to its own plugin type
|
||||||
|
QString instanceUUID();
|
||||||
|
static Tomahawk::query_ptr parseTrack( const QVariantMap& track );
|
||||||
|
QString m_pendingUrl;
|
||||||
|
Tomahawk::album_ptr m_pendingAlbum;
|
||||||
|
private slots:
|
||||||
|
void tracksAdded( const QList<Tomahawk::query_ptr>& tracks, const Tomahawk::ModelMode, const Tomahawk::collection_ptr& collection );
|
||||||
|
void pltemplateTracksLoadedForUrl( const QString& url, const Tomahawk::playlisttemplate_ptr& pltemplate );
|
||||||
};
|
};
|
||||||
|
|
||||||
} // ns: Tomahawk
|
} // ns: Tomahawk
|
||||||
|
@@ -21,10 +21,6 @@
|
|||||||
|
|
||||||
#include "JSResolverHelper.h"
|
#include "JSResolverHelper.h"
|
||||||
|
|
||||||
#include "database/Database.h"
|
|
||||||
#include "database/DatabaseImpl.h"
|
|
||||||
#include "playlist/PlaylistTemplate.h"
|
|
||||||
#include "playlist/XspfPlaylistTemplate.h"
|
|
||||||
#include "resolvers/ScriptEngine.h"
|
#include "resolvers/ScriptEngine.h"
|
||||||
#include "network/Servent.h"
|
#include "network/Servent.h"
|
||||||
#include "utils/Closure.h"
|
#include "utils/Closure.h"
|
||||||
@@ -51,6 +47,8 @@
|
|||||||
#include <QMap>
|
#include <QMap>
|
||||||
#include <QWebFrame>
|
#include <QWebFrame>
|
||||||
#include <QLocale>
|
#include <QLocale>
|
||||||
|
#include <QNetworkReply>
|
||||||
|
|
||||||
#include <taglib/asffile.h>
|
#include <taglib/asffile.h>
|
||||||
#include <taglib/flacfile.h>
|
#include <taglib/flacfile.h>
|
||||||
#include <taglib/id3v2framefactory.h>
|
#include <taglib/id3v2framefactory.h>
|
||||||
@@ -76,7 +74,6 @@ JSResolverHelper::JSResolverHelper( const QString& scriptPath, JSResolver* paren
|
|||||||
: QObject( parent )
|
: QObject( parent )
|
||||||
, m_resolver( parent )
|
, m_resolver( parent )
|
||||||
, m_scriptPath( scriptPath )
|
, m_scriptPath( scriptPath )
|
||||||
, m_urlCallbackIsAsync( false )
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -140,55 +137,6 @@ JSResolverHelper::log( const QString& message )
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
JSResolverHelper::addTrackResults( const QVariantMap& results )
|
|
||||||
{
|
|
||||||
Q_ASSERT( results["results"].toMap().isEmpty() );
|
|
||||||
|
|
||||||
QList< Tomahawk::result_ptr > tracks = m_resolver->scriptAccount()->parseResultVariantList( results.value( "results" ).toList() );
|
|
||||||
|
|
||||||
foreach( const result_ptr& track, tracks )
|
|
||||||
{
|
|
||||||
track->setResolvedByResolver( m_resolver );
|
|
||||||
track->setFriendlySource( m_resolver->name() );
|
|
||||||
}
|
|
||||||
|
|
||||||
QString qid = results.value("qid").toString();
|
|
||||||
|
|
||||||
Tomahawk::Pipeline::instance()->reportResults( qid, m_resolver, tracks );
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
query_ptr
|
|
||||||
JSResolverHelper::parseTrack( const QVariantMap& track )
|
|
||||||
{
|
|
||||||
QString title = track.value( "title" ).toString();
|
|
||||||
QString artist = track.value( "artist" ).toString();
|
|
||||||
QString album = track.value( "album" ).toString();
|
|
||||||
if ( title.isEmpty() || artist.isEmpty() )
|
|
||||||
{
|
|
||||||
return query_ptr();
|
|
||||||
}
|
|
||||||
|
|
||||||
Tomahawk::query_ptr query = Tomahawk::Query::get( artist, title, album );
|
|
||||||
QString resultHint = track.value( "hint" ).toString();
|
|
||||||
if ( !resultHint.isEmpty() )
|
|
||||||
{
|
|
||||||
query->setResultHint( resultHint );
|
|
||||||
query->setSaveHTTPResultHint( true );
|
|
||||||
}
|
|
||||||
|
|
||||||
return query;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
QString
|
|
||||||
JSResolverHelper::instanceUUID()
|
|
||||||
{
|
|
||||||
return Tomahawk::Database::instance()->impl()->dbid();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
QString
|
QString
|
||||||
JSResolverHelper::uuid() const
|
JSResolverHelper::uuid() const
|
||||||
{
|
{
|
||||||
@@ -483,120 +431,6 @@ JSResolverHelper::currentCountry() const
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
JSResolverHelper::addUrlResult( const QString& url, const QVariantMap& result )
|
|
||||||
{
|
|
||||||
// It may seem a bit weird, but currently no slot should do anything
|
|
||||||
// more as we starting on a new URL and not task are waiting for it yet.
|
|
||||||
m_pendingUrl = QString();
|
|
||||||
m_pendingAlbum = album_ptr();
|
|
||||||
|
|
||||||
QString type = result.value( "type" ).toString();
|
|
||||||
if ( type == "artist" )
|
|
||||||
{
|
|
||||||
QString name = result.value( "name" ).toString();
|
|
||||||
Q_ASSERT( !name.isEmpty() );
|
|
||||||
emit m_resolver->informationFound( url, Artist::get( name, true ).objectCast<QObject>() );
|
|
||||||
}
|
|
||||||
else if ( type == "album" )
|
|
||||||
{
|
|
||||||
QString name = result.value( "name" ).toString();
|
|
||||||
QString artist = result.value( "artist" ).toString();
|
|
||||||
album_ptr album = Album::get( Artist::get( artist, true ), name );
|
|
||||||
m_pendingUrl = url;
|
|
||||||
m_pendingAlbum = album;
|
|
||||||
connect( album.data(), SIGNAL( tracksAdded( QList<Tomahawk::query_ptr>, Tomahawk::ModelMode, Tomahawk::collection_ptr ) ),
|
|
||||||
SLOT( tracksAdded( QList<Tomahawk::query_ptr>, Tomahawk::ModelMode, Tomahawk::collection_ptr ) ) );
|
|
||||||
if ( !album->tracks().isEmpty() )
|
|
||||||
{
|
|
||||||
emit m_resolver->informationFound( url, album.objectCast<QObject>() );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if ( type == "track" )
|
|
||||||
{
|
|
||||||
Tomahawk::query_ptr query = parseTrack( result );
|
|
||||||
if ( query.isNull() )
|
|
||||||
{
|
|
||||||
// A valid track result shoud have non-empty title and artist.
|
|
||||||
tLog() << Q_FUNC_INFO << m_resolver->name() << "Got empty track information for " << url;
|
|
||||||
emit m_resolver->informationFound( url, QSharedPointer<QObject>() );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
emit m_resolver->informationFound( url, query.objectCast<QObject>() );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if ( type == "playlist" )
|
|
||||||
{
|
|
||||||
QString guid = result.value( "guid" ).toString();
|
|
||||||
Q_ASSERT( !guid.isEmpty() );
|
|
||||||
// Append nodeid to guid to make it globally unique.
|
|
||||||
guid += instanceUUID();
|
|
||||||
|
|
||||||
// Do we already have this playlist loaded?
|
|
||||||
{
|
|
||||||
playlist_ptr playlist = Playlist::get( guid );
|
|
||||||
if ( !playlist.isNull() )
|
|
||||||
{
|
|
||||||
emit m_resolver->informationFound( url, playlist.objectCast<QObject>() );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get all information to build a new playlist but do not build it until we know,
|
|
||||||
// if it is really handled as a playlist and not as a set of tracks.
|
|
||||||
Tomahawk::source_ptr source = SourceList::instance()->getLocal();
|
|
||||||
const QString title = result.value( "title" ).toString();
|
|
||||||
const QString info = result.value( "info" ).toString();
|
|
||||||
const QString creator = result.value( "creator" ).toString();
|
|
||||||
QList<query_ptr> queries;
|
|
||||||
foreach( QVariant track, result.value( "tracks" ).toList() )
|
|
||||||
{
|
|
||||||
query_ptr query = parseTrack( track.toMap() );
|
|
||||||
if ( !query.isNull() )
|
|
||||||
{
|
|
||||||
queries << query;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tLog( LOGVERBOSE ) << Q_FUNC_INFO << m_resolver->name() << "Got playlist for " << url;
|
|
||||||
playlisttemplate_ptr pltemplate( new PlaylistTemplate( source, guid, title, info, creator, false, queries ) );
|
|
||||||
emit m_resolver->informationFound( url, pltemplate.objectCast<QObject>() );
|
|
||||||
}
|
|
||||||
else if ( type == "xspf-url" )
|
|
||||||
{
|
|
||||||
QString xspfUrl = result.value( "url" ).toString();
|
|
||||||
Q_ASSERT( !xspfUrl.isEmpty() );
|
|
||||||
QString guid = QString( "xspf-%1-%2" ).arg( xspfUrl.toUtf8().toBase64().constData() ).arg( instanceUUID() );
|
|
||||||
|
|
||||||
// Do we already have this playlist loaded?
|
|
||||||
{
|
|
||||||
playlist_ptr playlist = Playlist::get( guid );
|
|
||||||
if ( !playlist.isNull() )
|
|
||||||
{
|
|
||||||
emit m_resolver->informationFound( url, playlist.objectCast<QObject>() );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Get all information to build a new playlist but do not build it until we know,
|
|
||||||
// if it is really handled as a playlist and not as a set of tracks.
|
|
||||||
Tomahawk::source_ptr source = SourceList::instance()->getLocal();
|
|
||||||
QSharedPointer<XspfPlaylistTemplate> pltemplate( new XspfPlaylistTemplate( xspfUrl, source, guid ) );
|
|
||||||
NewClosure( pltemplate, SIGNAL( tracksLoaded( QList< Tomahawk::query_ptr > ) ),
|
|
||||||
this, SLOT( pltemplateTracksLoadedForUrl( QString, Tomahawk::playlisttemplate_ptr ) ),
|
|
||||||
url, pltemplate.objectCast<Tomahawk::PlaylistTemplate>() );
|
|
||||||
tLog( LOGVERBOSE ) << Q_FUNC_INFO << m_resolver->name() << "Got playlist for " << url;
|
|
||||||
pltemplate->load();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
tLog( LOGVERBOSE ) << Q_FUNC_INFO << m_resolver->name() << "No usable information found for " << url;
|
|
||||||
emit m_resolver->informationFound( url, QSharedPointer<QObject>() );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
JSResolverHelper::nativeReportCapabilities( const QVariant& v )
|
JSResolverHelper::nativeReportCapabilities( const QVariant& v )
|
||||||
{
|
{
|
||||||
@@ -633,27 +467,6 @@ JSResolverHelper::unregisterScriptPlugin( const QString& type, const QString& ob
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
JSResolverHelper::tracksAdded( const QList<query_ptr>&, const ModelMode, const collection_ptr&)
|
|
||||||
{
|
|
||||||
// Check if we still are actively waiting
|
|
||||||
if ( m_pendingAlbum.isNull() || m_pendingUrl.isNull() )
|
|
||||||
return;
|
|
||||||
|
|
||||||
emit m_resolver->informationFound( m_pendingUrl, m_pendingAlbum.objectCast<QObject>() );
|
|
||||||
m_pendingAlbum = album_ptr();
|
|
||||||
m_pendingUrl = QString();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
JSResolverHelper::pltemplateTracksLoadedForUrl( const QString& url, const playlisttemplate_ptr& pltemplate )
|
|
||||||
{
|
|
||||||
tLog() << Q_FUNC_INFO;
|
|
||||||
emit m_resolver->informationFound( url, pltemplate.objectCast<QObject>() );
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
JSResolverHelper::setResolverConfig( const QVariantMap& config )
|
JSResolverHelper::setResolverConfig( const QVariantMap& config )
|
||||||
{
|
{
|
||||||
@@ -668,31 +481,6 @@ JSResolverHelper::accountId()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
JSResolverHelper::addCustomUrlHandler( const QString& protocol,
|
|
||||||
const QString& callbackFuncName,
|
|
||||||
const QString& isAsynchronous )
|
|
||||||
{
|
|
||||||
m_urlCallbackIsAsync = ( isAsynchronous.toLower() == "true" );
|
|
||||||
|
|
||||||
std::function< void( const Tomahawk::result_ptr&, const QString&,
|
|
||||||
std::function< void( const QString&, QSharedPointer< QIODevice >& ) > )> fac =
|
|
||||||
std::bind( &JSResolverHelper::customIODeviceFactory, this,
|
|
||||||
std::placeholders::_1, std::placeholders::_2,
|
|
||||||
std::placeholders::_3 );
|
|
||||||
Tomahawk::UrlHandler::registerIODeviceFactory( protocol, fac );
|
|
||||||
|
|
||||||
m_urlCallback = callbackFuncName;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
JSResolverHelper::reportStreamUrl( const QString& qid, const QString& streamUrl )
|
|
||||||
{
|
|
||||||
reportStreamUrl( qid, streamUrl, QVariantMap() );
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void JSResolverHelper::nativeAssert( bool assertion, const QString& message )
|
void JSResolverHelper::nativeAssert( bool assertion, const QString& message )
|
||||||
{
|
{
|
||||||
if ( !assertion )
|
if ( !assertion )
|
||||||
@@ -703,61 +491,6 @@ void JSResolverHelper::nativeAssert( bool assertion, const QString& message )
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
JSResolverHelper::customIODeviceFactory( const Tomahawk::result_ptr&, const QString& url,
|
|
||||||
std::function< void( const QString&, QSharedPointer< QIODevice >& ) > callback )
|
|
||||||
{
|
|
||||||
//can be sync or async
|
|
||||||
if ( m_urlCallbackIsAsync )
|
|
||||||
{
|
|
||||||
QString qid = uuid();
|
|
||||||
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 );
|
|
||||||
|
|
||||||
m_streamCallbacks.insert( qid, callback );
|
|
||||||
m_resolver->d_func()->scriptAccount->evaluateJavaScript( getUrl );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
QString getUrl = QString( "Tomahawk.resolver.instance.%1( '%2' );" ).arg( m_urlCallback )
|
|
||||||
.arg( url );
|
|
||||||
|
|
||||||
QString urlStr = m_resolver->d_func()->scriptAccount->evaluateJavaScriptWithResult( getUrl ).toString();
|
|
||||||
|
|
||||||
returnStreamUrl( urlStr, QMap<QString, QString>(), callback );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
JSResolverHelper::reportStreamUrl( const QString& qid, const QString& streamUrl, const QVariantMap& headers )
|
|
||||||
{
|
|
||||||
if ( !m_streamCallbacks.contains( qid ) )
|
|
||||||
return;
|
|
||||||
|
|
||||||
std::function< void( const QString&, QSharedPointer< QIODevice >& ) > callback = m_streamCallbacks.take( qid );
|
|
||||||
|
|
||||||
QMap<QString, QString> parsedHeaders;
|
|
||||||
foreach ( const QString& key, headers.keys() )
|
|
||||||
{
|
|
||||||
Q_ASSERT_X( headers[key].canConvert( QVariant::String ), Q_FUNC_INFO, "Expected a Map of string for additional headers" );
|
|
||||||
if ( headers[key].canConvert( QVariant::String ) )
|
|
||||||
{
|
|
||||||
parsedHeaders.insert( key, headers[key].toString() );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
returnStreamUrl( streamUrl, parsedHeaders, callback );
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
JSResolverHelper::nativeRetrieveMetadata( int metadataId, const QString& url,
|
JSResolverHelper::nativeRetrieveMetadata( int metadataId, const QString& url,
|
||||||
const QString& mime_type, int sizehint,
|
const QString& mime_type, int sizehint,
|
||||||
@@ -896,12 +629,27 @@ JSResolverHelper::nativeRetrieveMetadata( int metadataId, const QString& url,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
JSResolverHelper::invokeNativeScriptJob( int requestId, const QString& methodName, const QVariantMap& params )
|
||||||
|
{
|
||||||
|
if ( methodName == "httpRequest" ) {
|
||||||
|
nativeAsyncRequest( requestId, params );
|
||||||
|
} else {
|
||||||
|
QVariantMap error;
|
||||||
|
error["message"] = "NativeScriptJob methodName was not found";
|
||||||
|
error["name"] = "method_was_not_found";
|
||||||
|
|
||||||
|
m_resolver->d_func()->scriptAccount->reportNativeScriptJobError( requestId, error );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
JSResolverHelper::nativeAsyncRequest( const int requestId, const QString& url,
|
JSResolverHelper::nativeAsyncRequest( const int requestId, const QVariantMap& options )
|
||||||
const QVariantMap& headers,
|
|
||||||
const QVariantMap& options )
|
|
||||||
{
|
{
|
||||||
|
QString url = options[ "url" ].toString();
|
||||||
|
QVariantMap headers = options[ "headers" ].toMap();
|
||||||
|
|
||||||
QNetworkRequest req( url );
|
QNetworkRequest req( url );
|
||||||
foreach ( const QString& key, headers.keys() )
|
foreach ( const QString& key, headers.keys() )
|
||||||
{
|
{
|
||||||
@@ -957,17 +705,16 @@ JSResolverHelper::nativeAsyncRequestDone( int requestId, NetworkReply* reply )
|
|||||||
map["status"] = reply->reply()->attribute( QNetworkRequest::HttpStatusCodeAttribute ).toInt();
|
map["status"] = reply->reply()->attribute( QNetworkRequest::HttpStatusCodeAttribute ).toInt();
|
||||||
map["statusText"] = QString("%1 %2").arg( map["status"].toString() )
|
map["statusText"] = QString("%1 %2").arg( map["status"].toString() )
|
||||||
.arg( reply->reply()->attribute( QNetworkRequest::HttpReasonPhraseAttribute ).toString() );
|
.arg( reply->reply()->attribute( QNetworkRequest::HttpReasonPhraseAttribute ).toString() );
|
||||||
if (reply->reply()->hasRawHeader( "Content-Type" ))
|
|
||||||
map["contentType"] = reply->reply()->rawHeader( "Content-Type" );
|
|
||||||
|
|
||||||
bool ok = false;
|
|
||||||
QString json = QString::fromUtf8( TomahawkUtils::toJson( map, &ok ) );
|
|
||||||
Q_ASSERT( ok );
|
|
||||||
|
|
||||||
QString javascript = QString( "Tomahawk.nativeAsyncRequestDone( %1, %2 );" )
|
QVariantMap responseHeaders;
|
||||||
.arg( QString::number( requestId ) )
|
foreach( const QNetworkReply::RawHeaderPair& pair, reply->reply()->rawHeaderPairs() )
|
||||||
.arg( json );
|
{
|
||||||
m_resolver->d_func()->scriptAccount->evaluateJavaScript( javascript );
|
responseHeaders[ pair.first ] = pair.second;
|
||||||
|
}
|
||||||
|
map["responseHeaders"] = responseHeaders;
|
||||||
|
|
||||||
|
m_resolver->d_func()->scriptAccount->reportNativeScriptJobResult( requestId, map );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1143,43 +890,3 @@ JSResolverHelper::readdResolver()
|
|||||||
Pipeline::instance()->addResolver( m_resolver );
|
Pipeline::instance()->addResolver( m_resolver );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
JSResolverHelper::returnStreamUrl( const QString& streamUrl, const QMap<QString, QString>& headers,
|
|
||||||
std::function< void( const QString&, QSharedPointer< QIODevice >& ) > callback )
|
|
||||||
{
|
|
||||||
if ( streamUrl.isEmpty() || !( TomahawkUtils::isHttpResult( streamUrl ) || TomahawkUtils::isHttpsResult( streamUrl ) ) )
|
|
||||||
{
|
|
||||||
// Not an https? URL, so let Phonon handle it
|
|
||||||
QSharedPointer< QIODevice > sp;
|
|
||||||
callback( streamUrl, sp );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
QUrl url = QUrl::fromEncoded( streamUrl.toUtf8() );
|
|
||||||
QNetworkRequest req( url );
|
|
||||||
foreach ( const QString& key, headers.keys() )
|
|
||||||
{
|
|
||||||
req.setRawHeader( key.toLatin1(), headers[key].toLatin1() );
|
|
||||||
}
|
|
||||||
tDebug() << "Creating a QNetworkReply with url:" << req.url().toString();
|
|
||||||
NetworkReply* reply = new NetworkReply( Tomahawk::Utils::nam()->get( req ) );
|
|
||||||
|
|
||||||
NewClosure( reply, SIGNAL( finalUrlReached() ), this, SLOT( gotStreamUrl( IODeviceCallback, NetworkReply* )), callback, reply );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Q_DECLARE_METATYPE( IODeviceCallback )
|
|
||||||
|
|
||||||
void
|
|
||||||
JSResolverHelper::gotStreamUrl( std::function< void( const QString&, QSharedPointer< QIODevice >& ) > callback, NetworkReply* reply )
|
|
||||||
{
|
|
||||||
// std::functions cannot accept temporaries as parameters
|
|
||||||
QSharedPointer< QIODevice > sp ( reply->reply(), &QObject::deleteLater );
|
|
||||||
QString url = reply->reply()->url().toString();
|
|
||||||
reply->disconnectFromReply();
|
|
||||||
reply->deleteLater();
|
|
||||||
|
|
||||||
callback( url, sp );
|
|
||||||
}
|
|
||||||
|
@@ -61,9 +61,6 @@ public:
|
|||||||
*/
|
*/
|
||||||
Q_INVOKABLE QString accountId();
|
Q_INVOKABLE QString accountId();
|
||||||
|
|
||||||
Q_INVOKABLE void addCustomUrlHandler( const QString& protocol, const QString& callbackFuncName, const QString& isAsynchronous = "false" );
|
|
||||||
Q_INVOKABLE void reportStreamUrl( const QString& qid, const QString& streamUrl );
|
|
||||||
Q_INVOKABLE void reportStreamUrl( const QString& qid, const QString& streamUrl, const QVariantMap& headers );
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Make Tomahawk assert the assertion is true, probably not to be used by resolvers directly
|
* Make Tomahawk assert the assertion is true, probably not to be used by resolvers directly
|
||||||
@@ -89,20 +86,9 @@ public:
|
|||||||
int sizehint,
|
int sizehint,
|
||||||
const QVariantMap& options );
|
const QVariantMap& options );
|
||||||
|
|
||||||
/**
|
Q_INVOKABLE void invokeNativeScriptJob( int requestId,
|
||||||
* Native handler for asynchronous HTTP requests.
|
const QString& methodName,
|
||||||
*
|
const QVariantMap& params );
|
||||||
* This handler shall only be used if we cannot achieve the request with
|
|
||||||
* XMLHttpRequest as that would be more efficient.
|
|
||||||
* Use cases are:
|
|
||||||
* * Referer header: Stripped on MacOS and the specification says it
|
|
||||||
* should be stripped
|
|
||||||
*
|
|
||||||
* INTERNAL USE ONLY!
|
|
||||||
*/
|
|
||||||
Q_INVOKABLE void nativeAsyncRequest( int requestId, const QString& url,
|
|
||||||
const QVariantMap& headers,
|
|
||||||
const QVariantMap& options );
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lucene++ indices for JS resolvers
|
* Lucene++ indices for JS resolvers
|
||||||
@@ -122,18 +108,10 @@ public:
|
|||||||
Q_INVOKABLE void readdResolver();
|
Q_INVOKABLE void readdResolver();
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* INTERNAL USE ONLY!
|
|
||||||
*/
|
|
||||||
void customIODeviceFactory( const Tomahawk::result_ptr&, const QString& url,
|
|
||||||
std::function< void( const QString&, QSharedPointer< QIODevice >& ) > callback ); // async
|
|
||||||
|
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
QByteArray readRaw( const QString& fileName );
|
QByteArray readRaw( const QString& fileName );
|
||||||
QString readBase64( const QString& fileName );
|
QString readBase64( const QString& fileName );
|
||||||
QString readCompressed( const QString& fileName );
|
QString readCompressed( const QString& fileName );
|
||||||
QString instanceUUID();
|
|
||||||
QString uuid() const;
|
QString uuid() const;
|
||||||
int currentCountry() const;
|
int currentCountry() const;
|
||||||
QString compress( const QString& data );
|
QString compress( const QString& data );
|
||||||
@@ -142,10 +120,6 @@ public slots:
|
|||||||
void log( const QString& message );
|
void log( const QString& message );
|
||||||
bool fakeEnv() { return false; }
|
bool fakeEnv() { return false; }
|
||||||
|
|
||||||
void addTrackResults( const QVariantMap& results );
|
|
||||||
|
|
||||||
void addUrlResult( const QString& url, const QVariantMap& result );
|
|
||||||
|
|
||||||
void nativeReportCapabilities( const QVariant& capabilities );
|
void nativeReportCapabilities( const QVariant& capabilities );
|
||||||
|
|
||||||
void reportScriptJobResults( const QVariantMap& result );
|
void reportScriptJobResults( const QVariantMap& result );
|
||||||
@@ -154,27 +128,19 @@ public slots:
|
|||||||
void unregisterScriptPlugin( const QString& type, const QString& objectId );
|
void unregisterScriptPlugin( const QString& type, const QString& objectId );
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void gotStreamUrl( IODeviceCallback callback, NetworkReply* reply );
|
|
||||||
void tracksAdded( const QList<Tomahawk::query_ptr>& tracks, const Tomahawk::ModelMode, const Tomahawk::collection_ptr& collection );
|
|
||||||
void pltemplateTracksLoadedForUrl( const QString& url, const Tomahawk::playlisttemplate_ptr& pltemplate );
|
|
||||||
void nativeAsyncRequestDone( int requestId, NetworkReply* reply );
|
void nativeAsyncRequestDone( int requestId, NetworkReply* reply );
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Tomahawk::query_ptr parseTrack( const QVariantMap& track );
|
|
||||||
void returnStreamUrl( const QString& streamUrl, const QMap<QString, QString>& headers,
|
|
||||||
std::function< void( const QString&, QSharedPointer< QIODevice >& ) > callback );
|
|
||||||
|
|
||||||
bool indexDataFromVariant( const QVariantMap& map, struct Tomahawk::IndexData& indexData );
|
bool indexDataFromVariant( const QVariantMap& map, struct Tomahawk::IndexData& indexData );
|
||||||
QVariantList searchInFuzzyIndex( const Tomahawk::query_ptr& query );
|
QVariantList searchInFuzzyIndex( const Tomahawk::query_ptr& query );
|
||||||
|
|
||||||
|
// native script jobs
|
||||||
|
void nativeAsyncRequest( int requestId, const QVariantMap& options );
|
||||||
|
|
||||||
|
|
||||||
QVariantMap m_resolverConfig;
|
QVariantMap m_resolverConfig;
|
||||||
JSResolver* m_resolver;
|
JSResolver* m_resolver;
|
||||||
QString m_scriptPath, m_urlCallback, m_urlTranslator;
|
QString m_scriptPath;
|
||||||
QHash< QString, std::function< void( const QString&, QSharedPointer< QIODevice >& ) > > m_streamCallbacks;
|
|
||||||
QHash< QString, std::function< void( const QString& ) > > m_translatorCallbacks;
|
|
||||||
bool m_urlCallbackIsAsync;
|
|
||||||
QString m_pendingUrl;
|
|
||||||
Tomahawk::album_ptr m_pendingAlbum;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // ns: Tomahawk
|
} // ns: Tomahawk
|
||||||
|
@@ -17,10 +17,31 @@
|
|||||||
*/
|
*/
|
||||||
#include "Resolver.h"
|
#include "Resolver.h"
|
||||||
|
|
||||||
|
#include "../resolvers/SyncScriptJob.h"
|
||||||
|
#include "../Result.h"
|
||||||
|
|
||||||
#include <QPixmap>
|
#include <QPixmap>
|
||||||
|
|
||||||
|
|
||||||
|
Tomahawk::Resolver::Resolver( QObject* parent )
|
||||||
|
: QObject( parent )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
QPixmap
|
QPixmap
|
||||||
Tomahawk::Resolver::icon( const QSize& ) const
|
Tomahawk::Resolver::icon( const QSize& ) const
|
||||||
{
|
{
|
||||||
|
Q_ASSERT(false);
|
||||||
return QPixmap();
|
return QPixmap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Tomahawk::ScriptJob*
|
||||||
|
Tomahawk::Resolver::getStreamUrl( const result_ptr& result )
|
||||||
|
{
|
||||||
|
QVariantMap data;
|
||||||
|
data[ "url" ] = result->url();
|
||||||
|
|
||||||
|
return new SyncScriptJob( data );
|
||||||
|
}
|
||||||
|
@@ -19,7 +19,6 @@
|
|||||||
#ifndef RESOLVER_H
|
#ifndef RESOLVER_H
|
||||||
#define RESOLVER_H
|
#define RESOLVER_H
|
||||||
|
|
||||||
#include "../ResultProvider.h"
|
|
||||||
#include "Typedefs.h"
|
#include "Typedefs.h"
|
||||||
#include "DllMacro.h"
|
#include "DllMacro.h"
|
||||||
|
|
||||||
@@ -36,21 +35,23 @@
|
|||||||
|
|
||||||
namespace Tomahawk
|
namespace Tomahawk
|
||||||
{
|
{
|
||||||
|
class ScriptJob;
|
||||||
|
|
||||||
class DLLEXPORT Resolver : public QObject, public ResultProvider
|
class DLLEXPORT Resolver : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Resolver() {}
|
Resolver( QObject* parent = nullptr );
|
||||||
|
|
||||||
|
virtual QString name() const = 0;
|
||||||
|
virtual QPixmap icon( const QSize& size ) const;
|
||||||
|
|
||||||
virtual unsigned int weight() const = 0;
|
virtual unsigned int weight() const = 0;
|
||||||
virtual unsigned int timeout() const = 0;
|
virtual unsigned int timeout() const = 0;
|
||||||
|
|
||||||
virtual QPixmap icon( const QSize& size ) const override;
|
|
||||||
|
|
||||||
public slots:
|
|
||||||
virtual void resolve( const Tomahawk::query_ptr& query ) = 0;
|
virtual void resolve( const Tomahawk::query_ptr& query ) = 0;
|
||||||
|
virtual ScriptJob* getStreamUrl( const result_ptr& result );
|
||||||
};
|
};
|
||||||
|
|
||||||
} //ns
|
} //ns
|
||||||
|
@@ -326,21 +326,6 @@ ScriptAccount::parseResultVariantList( const QVariantList& reslist )
|
|||||||
}
|
}
|
||||||
rp->setDownloadFormats( fl );
|
rp->setDownloadFormats( fl );
|
||||||
|
|
||||||
// find collection
|
|
||||||
const QString collectionId = m.value( "collectionId" ).toString();
|
|
||||||
if ( !collectionId.isEmpty() )
|
|
||||||
{
|
|
||||||
if ( scriptCollection( collectionId ).isNull() )
|
|
||||||
{
|
|
||||||
tLog() << "Resolver returned invalid collection id";
|
|
||||||
Q_ASSERT( false );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
rp->setResolvedByCollection( scriptCollection( collectionId ) );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
results << rp;
|
results << rp;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -348,8 +333,27 @@ ScriptAccount::parseResultVariantList( const QVariantList& reslist )
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
QSharedPointer< ScriptCollection >
|
ScriptJob*
|
||||||
ScriptAccount::scriptCollection( const QString& id ) const
|
ScriptAccount::resolve( const scriptobject_ptr& scriptObject, const query_ptr& query )
|
||||||
{
|
{
|
||||||
return m_collectionFactory->scriptPlugins().value( id );
|
ScriptJob* job = nullptr;
|
||||||
|
if ( !query->isFullTextQuery() )
|
||||||
|
{
|
||||||
|
QVariantMap arguments;
|
||||||
|
arguments["artist"] = query->queryTrack()->artist();
|
||||||
|
arguments["album"] = query->queryTrack()->album();
|
||||||
|
arguments["track"] = query->queryTrack()->track();
|
||||||
|
|
||||||
|
job = scriptObject->invoke( "resolve", arguments );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
QVariantMap arguments;
|
||||||
|
arguments["query"] = query->fullTextQuery();
|
||||||
|
job = scriptObject->invoke( "search", arguments );
|
||||||
|
}
|
||||||
|
|
||||||
|
job->setProperty( "qid", query->id() );
|
||||||
|
|
||||||
|
return job;
|
||||||
}
|
}
|
||||||
|
@@ -65,18 +65,20 @@ public:
|
|||||||
|
|
||||||
ScriptJob* invoke( const scriptobject_ptr& scriptObject, const QString& methodName, const QVariantMap& arguments );
|
ScriptJob* invoke( const scriptobject_ptr& scriptObject, const QString& methodName, const QVariantMap& arguments );
|
||||||
virtual QVariant syncInvoke( const scriptobject_ptr& scriptObject, const QString& methodName, const QVariantMap& arguments ) = 0;
|
virtual QVariant syncInvoke( const scriptobject_ptr& scriptObject, const QString& methodName, const QVariantMap& arguments ) = 0;
|
||||||
|
|
||||||
virtual void startJob( ScriptJob* scriptJob ) = 0;
|
virtual void startJob( ScriptJob* scriptJob ) = 0;
|
||||||
|
|
||||||
void reportScriptJobResult( const QVariantMap& result );
|
void reportScriptJobResult( const QVariantMap& result );
|
||||||
void registerScriptPlugin( const QString& type, const QString& objectId );
|
void registerScriptPlugin( const QString& type, const QString& objectId );
|
||||||
void unregisterScriptPlugin( const QString& type, const QString& objectId );
|
void unregisterScriptPlugin( const QString& type, const QString& objectId );
|
||||||
|
|
||||||
|
virtual void reportNativeScriptJobResult( int resultId, const QVariantMap& result ) = 0;
|
||||||
|
virtual void reportNativeScriptJobError( int resultId, const QVariantMap& error ) = 0;
|
||||||
|
|
||||||
virtual void scriptPluginFactory( const QString& type, const scriptobject_ptr& object );
|
virtual void scriptPluginFactory( const QString& type, const scriptobject_ptr& object );
|
||||||
|
|
||||||
|
// helpers
|
||||||
QList< Tomahawk::result_ptr > parseResultVariantList( const QVariantList& reslist );
|
QList< Tomahawk::result_ptr > parseResultVariantList( const QVariantList& reslist );
|
||||||
|
ScriptJob* resolve( const scriptobject_ptr& scriptObject, const query_ptr& query );
|
||||||
QSharedPointer< ScriptCollection > scriptCollection( const QString& id ) const;
|
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void onJobDeleted( const QString& jobId );
|
void onJobDeleted( const QString& jobId );
|
||||||
|
@@ -26,7 +26,10 @@
|
|||||||
#include "resolvers/ScriptCommand_AllArtists.h"
|
#include "resolvers/ScriptCommand_AllArtists.h"
|
||||||
#include "resolvers/ScriptCommand_AllAlbums.h"
|
#include "resolvers/ScriptCommand_AllAlbums.h"
|
||||||
#include "resolvers/ScriptCommand_AllTracks.h"
|
#include "resolvers/ScriptCommand_AllTracks.h"
|
||||||
|
#include "resolvers/ScriptJob.h"
|
||||||
#include "ScriptAccount.h"
|
#include "ScriptAccount.h"
|
||||||
|
#include "Result.h"
|
||||||
|
#include "Pipeline.h"
|
||||||
|
|
||||||
#include <QImageReader>
|
#include <QImageReader>
|
||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
@@ -223,6 +226,15 @@ void ScriptCollection::parseMetaData()
|
|||||||
return parseMetaData( readMetaData() );
|
return parseMetaData( readMetaData() );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ScriptJob*
|
||||||
|
ScriptCollection::getStreamUrl( const result_ptr& result )
|
||||||
|
{
|
||||||
|
QVariantMap arguments;
|
||||||
|
arguments["url"] = result->url();
|
||||||
|
|
||||||
|
return scriptObject()->invoke( "getStreamUrl", arguments );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
ScriptCollection::parseMetaData( const QVariantMap& metadata )
|
ScriptCollection::parseMetaData( const QVariantMap& metadata )
|
||||||
@@ -326,3 +338,58 @@ ScriptCollection::onIconFetched()
|
|||||||
reply->deleteLater();
|
reply->deleteLater();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
unsigned int
|
||||||
|
ScriptCollection::timeout() const
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
unsigned int
|
||||||
|
ScriptCollection::weight() const
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
ScriptCollection::resolve( const Tomahawk::query_ptr& query )
|
||||||
|
{
|
||||||
|
ScriptJob* job = scriptAccount()->resolve( scriptObject(), query );
|
||||||
|
|
||||||
|
connect( job, SIGNAL( done( QVariantMap ) ), SLOT( onResolveRequestDone( QVariantMap ) ) );
|
||||||
|
|
||||||
|
job->start();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
ScriptCollection::onResolveRequestDone( const QVariantMap& data )
|
||||||
|
{
|
||||||
|
Q_ASSERT( QThread::currentThread() == thread() );
|
||||||
|
|
||||||
|
ScriptJob* job = qobject_cast< ScriptJob* >( sender() );
|
||||||
|
|
||||||
|
QID qid = job->property( "qid" ).toString();
|
||||||
|
|
||||||
|
if ( job->error() )
|
||||||
|
{
|
||||||
|
Tomahawk::Pipeline::instance()->reportError( qid, this );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
QList< Tomahawk::result_ptr > results = scriptAccount()->parseResultVariantList( data.value( "tracks" ).toList() );
|
||||||
|
|
||||||
|
foreach( const result_ptr& result, results )
|
||||||
|
{
|
||||||
|
result->setResolvedByCollection( weakRef().toStrongRef() );
|
||||||
|
result->setFriendlySource( prettyName() );
|
||||||
|
}
|
||||||
|
|
||||||
|
Tomahawk::Pipeline::instance()->reportResults( qid, this, results );
|
||||||
|
}
|
||||||
|
|
||||||
|
sender()->deleteLater();
|
||||||
|
}
|
||||||
|
@@ -91,8 +91,15 @@ public:
|
|||||||
void parseMetaData();
|
void parseMetaData();
|
||||||
void parseMetaData( const QVariantMap& metadata );
|
void parseMetaData( const QVariantMap& metadata );
|
||||||
|
|
||||||
|
// Resolver interface
|
||||||
|
unsigned int weight() const override;
|
||||||
|
unsigned int timeout() const override;
|
||||||
|
void resolve( const Tomahawk::query_ptr& query ) override;
|
||||||
|
ScriptJob* getStreamUrl( const result_ptr& result ) override;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void onIconFetched();
|
void onIconFetched();
|
||||||
|
void onResolveRequestDone( const QVariantMap& data );
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ScriptAccount* m_scriptAccount;
|
ScriptAccount* m_scriptAccount;
|
||||||
|
@@ -117,7 +117,7 @@ ScriptCommand_AllTracks::onTracksJobDone( const QVariantMap& result )
|
|||||||
QSharedPointer< ScriptCollection > collection = m_collection.objectCast< ScriptCollection >();
|
QSharedPointer< ScriptCollection > collection = m_collection.objectCast< ScriptCollection >();
|
||||||
Q_ASSERT( !collection.isNull() );
|
Q_ASSERT( !collection.isNull() );
|
||||||
|
|
||||||
QList< Tomahawk::result_ptr > t = collection->scriptAccount()->parseResultVariantList( result[ "results"].toList() );
|
QList< Tomahawk::result_ptr > t = collection->scriptAccount()->parseResultVariantList( result[ "tracks" ].toList() );
|
||||||
|
|
||||||
|
|
||||||
QList< Tomahawk::query_ptr > queries;
|
QList< Tomahawk::query_ptr > queries;
|
||||||
|
@@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
#include "SourceList.h"
|
#include "SourceList.h"
|
||||||
#include "../ScriptAccount.h"
|
#include "../ScriptAccount.h"
|
||||||
|
#include "../../Pipeline.h"
|
||||||
|
|
||||||
using namespace Tomahawk;
|
using namespace Tomahawk;
|
||||||
|
|
||||||
@@ -29,12 +30,14 @@ void ScriptCollectionFactory::addPlugin( const QSharedPointer<ScriptCollection>&
|
|||||||
|
|
||||||
collection->setOnline( true );
|
collection->setOnline( true );
|
||||||
SourceList::instance()->addScriptCollection( collection );
|
SourceList::instance()->addScriptCollection( collection );
|
||||||
|
Pipeline::instance()->addResolver( collection.data() );
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScriptCollectionFactory::removePlugin( const QSharedPointer<ScriptCollection>& collection ) const
|
void ScriptCollectionFactory::removePlugin( const QSharedPointer<ScriptCollection>& collection ) const
|
||||||
{
|
{
|
||||||
collection->setOnline( false );
|
collection->setOnline( false );
|
||||||
SourceList::instance()->removeScriptCollection( collection );
|
SourceList::instance()->removeScriptCollection( collection );
|
||||||
|
Pipeline::instance()->removeResolver( collection.data() );
|
||||||
}
|
}
|
||||||
|
|
||||||
QSharedPointer< ScriptCollection > ScriptCollectionFactory::createPlugin( const scriptobject_ptr& object, ScriptAccount* scriptAccount )
|
QSharedPointer< ScriptCollection > ScriptCollectionFactory::createPlugin( const scriptobject_ptr& object, ScriptAccount* scriptAccount )
|
||||||
|
Reference in New Issue
Block a user