mirror of
https://github.com/tomahawk-player/tomahawk.git
synced 2025-01-17 14:28:24 +01:00
Merge pull request #339 from tomahawk-player/promisify-all-the-things
[WIP] Promisify JSResolver
This commit is contained in:
commit
5395908cbe
@ -49,7 +49,7 @@ RSVP.on('error', function (reason) {
|
||||
resolverName = Tomahawk.resolver.instance.settings.name + " - ";
|
||||
}
|
||||
if (reason) {
|
||||
console.error(resolverName + 'Uncaught error:' + JSON.stringify(reason));
|
||||
console.error(resolverName + 'Uncaught error:', reason);
|
||||
} else {
|
||||
console.error(resolverName + 'Uncaught error: error thrown from RSVP but it was empty');
|
||||
}
|
||||
@ -173,7 +173,8 @@ var TomahawkUrlType = {
|
||||
Playlist: 1,
|
||||
Track: 2,
|
||||
Album: 4,
|
||||
Artist: 8
|
||||
Artist: 8,
|
||||
Xspf: 16
|
||||
};
|
||||
|
||||
//Deprecated for 0.9 resolvers. Use Tomahawk.ConfigTestResultType instead.
|
||||
@ -272,74 +273,19 @@ Tomahawk.Resolver = {
|
||||
getStreamUrl: function (params) {
|
||||
return params;
|
||||
},
|
||||
|
||||
_convertUrls: function (results) {
|
||||
var that = this;
|
||||
return results.map(function (r) {
|
||||
if (r && r.url) {
|
||||
r.url = that._urlProtocol + '://' + r.url;
|
||||
}
|
||||
return r;
|
||||
_adapter_resolve: function (params) {
|
||||
return RSVP.Promise.resolve(this.resolve(params)).then(function (results) {
|
||||
return {
|
||||
'tracks': results
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
_adapter_resolve: function (qid, artist, album, title) {
|
||||
var that = this;
|
||||
var collectionPromises = [];
|
||||
Tomahawk.collections.forEach(function (col) {
|
||||
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))
|
||||
});
|
||||
});
|
||||
_adapter_search: function (params) {
|
||||
return RSVP.Promise.resolve(this.search(params)).then(function (results) {
|
||||
return {
|
||||
'tracks': results
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
@ -366,34 +312,6 @@ Tomahawk.valueForSubNode = function (node, tag) {
|
||||
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
|
||||
* code.
|
||||
@ -489,6 +407,23 @@ Tomahawk.nativeAsyncRequestDone = function (reqId, xhr) {
|
||||
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:
|
||||
* - method: The HTTP request method (default: GET)
|
||||
@ -498,59 +433,45 @@ Tomahawk.nativeAsyncRequestDone = function (reqId, xhr) {
|
||||
* - data: body data included in POST requests
|
||||
* - needCookieHeader: boolean indicating whether or not the request needs to be able to get the
|
||||
* "Set-Cookie" response header
|
||||
* - headers: headers set on the request
|
||||
*/
|
||||
Tomahawk.asyncRequest = function (url, callback, extraHeaders, options) {
|
||||
// unpack options
|
||||
var opt = options || {};
|
||||
var method = opt.method || 'GET';
|
||||
var doRequest = function(options) {
|
||||
if (shouldDoNativeRequest(options)) {
|
||||
return Tomahawk.NativeScriptJobManager.invoke('httpRequest', options).then(function(xhr) {
|
||||
xhr.responseHeaders = xhr.responseHeaders || {};
|
||||
xhr.getAllResponseHeaders = function() {
|
||||
return this.responseHeaders;
|
||||
};
|
||||
xhr.getResponseHeader = function (header) {
|
||||
return this.responseHeaders[header];
|
||||
};
|
||||
|
||||
if (shouldDoNativeRequest(url, callback, extraHeaders, options)) {
|
||||
// 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);
|
||||
return xhr;
|
||||
});
|
||||
} else {
|
||||
var xmlHttpRequest = new XMLHttpRequest();
|
||||
xmlHttpRequest.open(method, url, true, opt.username, opt.password);
|
||||
if (extraHeaders) {
|
||||
for (var headerName in extraHeaders) {
|
||||
xmlHttpRequest.setRequestHeader(headerName, extraHeaders[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);
|
||||
return new RSVP.Promise(function(resolve, reject) {
|
||||
var xmlHttpRequest = new XMLHttpRequest();
|
||||
xmlHttpRequest.open(options.method, options.url, true, options.username, options.password);
|
||||
if (options.headers) {
|
||||
for (var headerName in options.headers) {
|
||||
xmlHttpRequest.setRequestHeader(headerName, options.headers[headerName]);
|
||||
}
|
||||
}
|
||||
};
|
||||
xmlHttpRequest.send(opt.data || null);
|
||||
xmlHttpRequest.onreadystatechange = function () {
|
||||
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) {
|
||||
if (typeof url === "object") {
|
||||
settings = url;
|
||||
@ -608,10 +529,7 @@ Tomahawk.ajax = function (url, settings) {
|
||||
}
|
||||
}
|
||||
|
||||
return new RSVP.Promise(function (resolve, reject) {
|
||||
settings.errorHandler = reject;
|
||||
Tomahawk.asyncRequest(settings.url, resolve, settings.headers, settings);
|
||||
}).then(function (xhr) {
|
||||
return doRequest(settings).then(function (xhr) {
|
||||
if (settings.rawResponse) {
|
||||
return xhr;
|
||||
}
|
||||
@ -817,7 +735,9 @@ Tomahawk.base64Encode = function (b) {
|
||||
return window.btoa(b);
|
||||
};
|
||||
|
||||
|
||||
Tomahawk.PluginManager = {
|
||||
wrapperPrefix: '_adapter_',
|
||||
objects: {},
|
||||
objectCounter: 0,
|
||||
identifyObject: function (object) {
|
||||
@ -829,10 +749,6 @@ Tomahawk.PluginManager = {
|
||||
},
|
||||
registerPlugin: function (type, object) {
|
||||
this.objects[this.identifyObject(object)] = object;
|
||||
if (type === 'collection') {
|
||||
Tomahawk.collections.push(object);
|
||||
}
|
||||
|
||||
Tomahawk.log("registerPlugin: " + type + " id: " + object.id);
|
||||
Tomahawk.registerScriptPlugin(type, object.id);
|
||||
},
|
||||
@ -846,14 +762,11 @@ Tomahawk.PluginManager = {
|
||||
|
||||
resolve: [],
|
||||
invokeSync: function (requestId, objectId, methodName, params) {
|
||||
if (!Tomahawk.resolver.instance.apiVersion || Tomahawk.resolver.instance.apiVersion < 0.9) {
|
||||
if (methodName === 'artistAlbums') {
|
||||
methodName = 'albums';
|
||||
} else if (methodName === 'albumTracks') {
|
||||
methodName = 'tracks';
|
||||
}
|
||||
if (this.objects[objectId][this.wrapperPrefix + methodName]) {
|
||||
methodName = this.wrapperPrefix + methodName;
|
||||
}
|
||||
|
||||
|
||||
var pluginManager = this;
|
||||
if (!this.objects[objectId]) {
|
||||
Tomahawk.log("Object not found! objectId: " + objectId + " methodName: " + methodName);
|
||||
@ -863,52 +776,13 @@ Tomahawk.PluginManager = {
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof this.objects[objectId][methodName] === 'function') {
|
||||
if (!Tomahawk.resolver.instance.apiVersion
|
||||
|| Tomahawk.resolver.instance.apiVersion < 0.9) {
|
||||
if (methodName == 'artists') {
|
||||
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);
|
||||
if (typeof this.objects[objectId][methodName] !== 'function' && this.objects[objectId][methodName]) {
|
||||
return this.objects[objectId][methodName];
|
||||
} else if (typeof this.objects[objectId][methodName] !== 'function') {
|
||||
throw new Error('\'' + methodName + '\' on ScriptObject ' + objectId + ' is not a function', typeof this.objects[objectId][methodName]);
|
||||
}
|
||||
|
||||
return this.objects[objectId][methodName];
|
||||
return this.objects[objectId][methodName](params);
|
||||
},
|
||||
|
||||
invoke: function (requestId, objectId, methodName, params) {
|
||||
@ -927,21 +801,38 @@ Tomahawk.PluginManager = {
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
var encodeParamsToNativeFunctions = function(param) {
|
||||
return param;
|
||||
};
|
||||
|
||||
Tomahawk.NativeScriptJobManager = {
|
||||
idCounter: 0,
|
||||
deferreds: {},
|
||||
invoke: function (methodName, params) {
|
||||
params = params || {};
|
||||
|
||||
var requestId = this.idCounter++;
|
||||
Tomahawk.invokeNativeScriptJob(requestId, methodName, JSON.stringify(params));
|
||||
this.deferreds[requestId] = RSVP.defer();
|
||||
return this.deferreds[requestId].promise;
|
||||
var deferred = RSVP.defer();
|
||||
this.deferreds[requestId] = deferred;
|
||||
Tomahawk.invokeNativeScriptJob(requestId, methodName, encodeParamsToNativeFunctions(params));;
|
||||
return deferred.promise;
|
||||
},
|
||||
reportNativeScriptJobResult: function (requestId, result) {
|
||||
reportNativeScriptJobResult: function(requestId, result) {
|
||||
var deferred = this.deferreds[requestId];
|
||||
if (!deferred) {
|
||||
Tomahawk.log("Deferred object with the given requestId is not present!");
|
||||
}
|
||||
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
|
||||
};
|
||||
|
||||
Tomahawk.collections = [];
|
||||
|
||||
Tomahawk.Collection = {
|
||||
BrowseCapability: {
|
||||
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) {
|
||||
var resultIds = Tomahawk.resolveFromFuzzyIndex(params.artist, params.album, params.track);
|
||||
return this._fuzzyIndexIdsToTracks(resultIds);
|
||||
@ -1671,7 +1568,12 @@ Tomahawk.Collection = {
|
||||
|
||||
search: function (params) {
|
||||
var resultIds = Tomahawk.searchFuzzyIndex(params.query);
|
||||
return this._fuzzyIndexIdsToTracks(resultIds);
|
||||
|
||||
return this._fuzzyIndexIdsToTracks(resultIds).then(function(tracks) {
|
||||
return {
|
||||
tracks: tracks
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
tracks: function (params, where) {
|
||||
@ -1715,7 +1617,7 @@ Tomahawk.Collection = {
|
||||
);
|
||||
return t.execDeferredStatements();
|
||||
}).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.Tracks];
|
||||
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
|
||||
Query.cpp
|
||||
Result.cpp
|
||||
ResultProvider.cpp
|
||||
Source.cpp
|
||||
Track.cpp
|
||||
TrackData.cpp
|
||||
|
@ -175,7 +175,7 @@ DropJob::acceptsMimeData( const QMimeData* data, DropJob::DropTypes acceptedType
|
||||
// Check Scriptresolvers
|
||||
foreach ( QPointer<ExternalResolver> resolver, Pipeline::instance()->scriptResolvers() )
|
||||
{
|
||||
if ( resolver->canParseUrl( url, ExternalResolver::Playlist ) )
|
||||
if ( resolver->canParseUrl( url, ExternalResolver::UrlTypePlaylist ) )
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -201,7 +201,7 @@ DropJob::acceptsMimeData( const QMimeData* data, DropJob::DropTypes acceptedType
|
||||
// Check Scriptresolvers
|
||||
foreach ( QPointer<ExternalResolver> resolver, Pipeline::instance()->scriptResolvers() )
|
||||
{
|
||||
if ( resolver->canParseUrl( url, ExternalResolver::Track ) )
|
||||
if ( resolver->canParseUrl( url, ExternalResolver::UrlTypeTrack ) )
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -218,7 +218,7 @@ DropJob::acceptsMimeData( const QMimeData* data, DropJob::DropTypes acceptedType
|
||||
// Check Scriptresolvers
|
||||
foreach ( QPointer<ExternalResolver> resolver, Pipeline::instance()->scriptResolvers() )
|
||||
{
|
||||
if ( resolver->canParseUrl( url, ExternalResolver::Album ) )
|
||||
if ( resolver->canParseUrl( url, ExternalResolver::UrlTypeAlbum ) )
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -235,7 +235,7 @@ DropJob::acceptsMimeData( const QMimeData* data, DropJob::DropTypes acceptedType
|
||||
// Check Scriptresolvers
|
||||
foreach ( QPointer<ExternalResolver> resolver, Pipeline::instance()->scriptResolvers() )
|
||||
{
|
||||
if ( resolver->canParseUrl( url, ExternalResolver::Artist ) )
|
||||
if ( resolver->canParseUrl( url, ExternalResolver::UrlTypeArtist ) )
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -306,7 +306,7 @@ DropJob::isDropType( DropJob::DropType desired, const QMimeData* data )
|
||||
// Check 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();
|
||||
return true;
|
||||
@ -763,7 +763,7 @@ DropJob::handleTrackUrls( const QString& urls )
|
||||
{
|
||||
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 );
|
||||
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;
|
||||
foreach ( QPointer<ExternalResolver> resolver, Pipeline::instance()->scriptResolvers() )
|
||||
{
|
||||
if ( resolver->canParseUrl( url, ExternalResolver::Any ) )
|
||||
if ( resolver->canParseUrl( url, ExternalResolver::UrlTypeAny ) )
|
||||
{
|
||||
canParse = true;
|
||||
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
|
||||
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() )
|
||||
{
|
||||
ResultProvider* resolvedBy = results[0]->resolvedBy();
|
||||
Resolver* resolvedBy = results[0]->resolvedBy();
|
||||
if ( resolvedBy )
|
||||
{
|
||||
tDebug() << "Result arrived too late for:" << qid << "by" << resolvedBy->name();
|
||||
|
@ -54,6 +54,7 @@ public:
|
||||
unsigned int pendingQueryCount() 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 reportAlbums( QID qid, const QList< album_ptr >& albums );
|
||||
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
|
||||
{
|
||||
if ( !m_collection.isNull() )
|
||||
|
@ -21,7 +21,6 @@
|
||||
#ifndef RESULT_H
|
||||
#define RESULT_H
|
||||
|
||||
#include "ResultProvider.h"
|
||||
#include "DownloadJob.h"
|
||||
#include "utils/TomahawkUtils.h"
|
||||
#include "Typedefs.h"
|
||||
@ -86,9 +85,9 @@ public:
|
||||
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;
|
||||
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 )
|
||||
{
|
||||
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 ) ) );
|
||||
job->start();
|
||||
}
|
||||
|
@ -32,6 +32,8 @@
|
||||
#include "playlist/SingleTrackPlaylistInterface.h"
|
||||
#include "utils/Closure.h"
|
||||
#include "utils/Logger.h"
|
||||
#include "utils/NetworkReply.h"
|
||||
#include "utils/NetworkAccessManager.h"
|
||||
|
||||
#include "Album.h"
|
||||
#include "Artist.h"
|
||||
@ -40,6 +42,7 @@
|
||||
#include "SourceList.h"
|
||||
#include "TomahawkSettings.h"
|
||||
#include "UrlHandler.h"
|
||||
#include "resolvers/ScriptJob.h"
|
||||
|
||||
#include <QDir>
|
||||
|
||||
@ -574,16 +577,64 @@ AudioEngine::loadTrack( const Tomahawk::result_ptr& result )
|
||||
|
||||
setCurrentTrack( result );
|
||||
|
||||
if ( !TomahawkUtils::isLocalResult( d->currentTrack->url() ) && !TomahawkUtils::isHttpResult( d->currentTrack->url() )
|
||||
&& !TomahawkUtils::isRtmpResult( d->currentTrack->url() ) )
|
||||
ScriptJob* job = result->resolvedBy()->getStreamUrl( result );
|
||||
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
|
||||
{
|
||||
QSharedPointer< QIODevice > io;
|
||||
performLoadTrack( result, result->url(), io );
|
||||
// TODO: just make this part of the http(s) IoDeviceFactory (?)
|
||||
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"
|
||||
|
||||
class NetworkReply;
|
||||
class AudioEnginePrivate;
|
||||
|
||||
class DLLEXPORT AudioEngine : public QObject
|
||||
@ -180,6 +181,10 @@ signals:
|
||||
|
||||
private slots:
|
||||
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 performLoadTrack( const Tomahawk::result_ptr result, const QString url, QSharedPointer< QIODevice > io ); //only call from loadTrack or performLoadIODevice kthxbi
|
||||
void loadPreviousTrack();
|
||||
|
@ -35,7 +35,7 @@ using namespace Tomahawk;
|
||||
|
||||
|
||||
Collection::Collection( const source_ptr& source, const QString& name, QObject* parent )
|
||||
: QObject( parent )
|
||||
: Resolver( parent )
|
||||
, m_name( name )
|
||||
, m_lastmodified( 0 )
|
||||
, m_changed( false )
|
||||
|
@ -33,7 +33,7 @@
|
||||
#include "collection/ArtistsRequest.h"
|
||||
#include "collection/AlbumsRequest.h"
|
||||
#include "collection/TracksRequest.h"
|
||||
#include "../ResultProvider.h"
|
||||
#include "../resolvers/Resolver.h"
|
||||
|
||||
#include "DllMacro.h"
|
||||
|
||||
@ -46,7 +46,7 @@
|
||||
namespace Tomahawk
|
||||
{
|
||||
|
||||
class DLLEXPORT Collection : public QObject, public ResultProvider
|
||||
class DLLEXPORT Collection : public Resolver
|
||||
{
|
||||
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;
|
||||
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:
|
||||
virtual void addTracks( const QList<QVariant>& newitems );
|
||||
virtual void removeTracks( const QDir& dir );
|
||||
|
@ -262,8 +262,15 @@ TrackDetailView::onResultsChanged()
|
||||
resolverLabel->setFont( f );
|
||||
resolverLabel->setStyleSheet( "QLabel { color: rgba( 0, 0, 0, 50% ) }" );
|
||||
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() )
|
||||
.arg( !result->track()->album().isEmpty() ? QString( " " ) + tr( "on %1" ).arg( result->track()->album() ) : QString() ) );
|
||||
resolverLabel->setToolTip(
|
||||
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 );
|
||||
|
||||
NewClosure( resolverLabel, SIGNAL( clicked() ), const_cast< AudioEngine* >( AudioEngine::instance() ),
|
||||
|
@ -68,11 +68,12 @@ public:
|
||||
|
||||
enum UrlType
|
||||
{
|
||||
Any = 0x00,
|
||||
Playlist = 0x01,
|
||||
Track = 0x02,
|
||||
Album = 0x04,
|
||||
Artist = 0x08
|
||||
UrlTypeAny = 0x00,
|
||||
UrlTypePlaylist = 0x01,
|
||||
UrlTypeTrack = 0x02,
|
||||
UrlTypeAlbum = 0x04,
|
||||
UrlTypeArtist = 0x08,
|
||||
UrlTypeXspf = 0x10
|
||||
};
|
||||
Q_DECLARE_FLAGS( UrlTypes, UrlType )
|
||||
Q_FLAGS( UrlTypes )
|
||||
|
@ -170,6 +170,41 @@ JSAccount::syncInvoke( const scriptobject_ptr& scriptObject, const QString& meth
|
||||
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
|
||||
JSAccount::evaluateJavaScriptInternal( const QString& scriptSource )
|
||||
|
@ -71,6 +71,9 @@ public:
|
||||
|
||||
static QString serializeQVariantMap(const QVariantMap& map);
|
||||
|
||||
void reportNativeScriptJobResult( int resultId, const QVariantMap& result ) override;
|
||||
void reportNativeScriptJobError( int resultId, const QVariantMap& error ) override;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Wrap the pure evaluateJavaScript call in here, while the threadings guards are in public methods
|
||||
|
@ -44,6 +44,13 @@
|
||||
#include "Track.h"
|
||||
#include "ScriptInfoPlugin.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 <QFile>
|
||||
@ -250,12 +257,11 @@ JSResolver::init()
|
||||
d->scriptAccount->loadScript( filePath() );
|
||||
|
||||
// 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
|
||||
resolverInit();
|
||||
scriptObject()->syncInvoke( "init" );
|
||||
|
||||
QVariantMap m = resolverSettings();
|
||||
QVariantMap m = scriptObject()->syncInvoke( "settings" ).toMap();
|
||||
d->name = m.value( "name" ).toString();
|
||||
d->weight = m.value( "weight", 0 ).toUInt();
|
||||
d->timeout = m.value( "timeout", 25 ).toUInt() * 1000;
|
||||
@ -319,20 +325,13 @@ JSResolver::canParseUrl( const QString& url, UrlType type )
|
||||
{
|
||||
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 ) )
|
||||
{
|
||||
QString eval = QString( "canParseUrl( '%1', %2 )" )
|
||||
.arg( JSAccount::escape( QString( url ) ) )
|
||||
.arg( (int) type );
|
||||
return callOnResolver( eval ).toBool();
|
||||
QVariantMap arguments;
|
||||
arguments["url"] = url;
|
||||
arguments["type"] = (int) type;
|
||||
|
||||
return scriptObject()->syncInvoke( "canParseUrl", arguments ).toBool();
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -345,34 +344,185 @@ JSResolver::canParseUrl( const QString& url, UrlType type )
|
||||
void
|
||||
JSResolver::lookupUrl( const QString& url )
|
||||
{
|
||||
if ( QThread::currentThread() != thread() )
|
||||
{
|
||||
QMetaObject::invokeMethod( this, "lookupUrl", Qt::QueuedConnection,
|
||||
Q_ARG( QString, url ) );
|
||||
return;
|
||||
}
|
||||
|
||||
Q_D( const JSResolver );
|
||||
|
||||
|
||||
if ( !d->capabilities.testFlag( UrlLookup ) )
|
||||
{
|
||||
emit informationFound( url, QSharedPointer<QObject>() );
|
||||
return;
|
||||
}
|
||||
|
||||
QString eval = QString( "lookupUrl( '%1' )" )
|
||||
.arg( JSAccount::escape( QString( url ) ) );
|
||||
QVariantMap arguments;
|
||||
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
|
||||
return;
|
||||
QString name = result.value( "name" ).toString();
|
||||
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 );
|
||||
JobStatusView::instance()->model()->addJob( new ErrorStatusMessage( errorMessage ) );
|
||||
tDebug() << errorMessage << m;
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
JSResolver::resolve( const Tomahawk::query_ptr& query )
|
||||
{
|
||||
if ( QThread::currentThread() != thread() )
|
||||
{
|
||||
QMetaObject::invokeMethod( this, "resolve", Qt::QueuedConnection, Q_ARG(Tomahawk::query_ptr, query) );
|
||||
return;
|
||||
}
|
||||
ScriptJob* job = scriptAccount()->resolve( scriptObject(), query );
|
||||
|
||||
QString eval;
|
||||
if ( !query->isFullTextQuery() )
|
||||
connect( job, SIGNAL( done( QVariantMap ) ), SLOT( onResolveRequestDone( QVariantMap ) ) );
|
||||
|
||||
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' )" )
|
||||
.arg( JSAccount::escape( query->id() ) )
|
||||
.arg( JSAccount::escape( query->queryTrack()->artist() ) )
|
||||
.arg( JSAccount::escape( query->queryTrack()->album() ) )
|
||||
.arg( JSAccount::escape( query->queryTrack()->track() ) );
|
||||
Tomahawk::Pipeline::instance()->reportError( qid, this );
|
||||
}
|
||||
else
|
||||
{
|
||||
eval = QString( "search( '%1', '%2' )" )
|
||||
.arg( JSAccount::escape( query->id() ) )
|
||||
.arg( JSAccount::escape( query->fullTextQuery() ) );
|
||||
|
||||
QList< Tomahawk::result_ptr > results = scriptAccount()->parseResultVariantList( data.value( "tracks" ).toList() );
|
||||
|
||||
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
|
||||
JSResolver::stop()
|
||||
{
|
||||
@ -433,7 +595,7 @@ JSResolver::loadUi()
|
||||
{
|
||||
Q_D( JSResolver );
|
||||
|
||||
QVariantMap m = callOnResolver( "getConfigUi()" ).toMap();
|
||||
QVariantMap m = scriptObject()->syncInvoke( "getConfigUi" ).toMap();
|
||||
|
||||
bool compressed = m.value( "compressed", "false" ).toBool();
|
||||
qDebug() << "Resolver has a preferences widget! compressed?" << compressed;
|
||||
@ -485,7 +647,7 @@ JSResolver::saveConfig()
|
||||
// qDebug() << Q_FUNC_INFO << saveData;
|
||||
|
||||
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
|
||||
JSResolver::resolverUserConfig()
|
||||
{
|
||||
return callOnResolver( "getUserConfig()" ).toMap();
|
||||
return scriptObject()->syncInvoke( "getUserConfig" ).toMap();
|
||||
}
|
||||
|
||||
|
||||
QVariantMap
|
||||
JSResolver::resolverInit()
|
||||
QString
|
||||
JSResolver::instanceUUID()
|
||||
{
|
||||
return callOnResolver( "init()" ).toMap();
|
||||
return Tomahawk::Database::instance()->impl()->dbid();
|
||||
}
|
||||
|
||||
|
||||
QVariant
|
||||
JSResolver::callOnResolver( const QString& scriptSource )
|
||||
ScriptJob*
|
||||
JSResolver::getStreamUrl( const result_ptr& result )
|
||||
{
|
||||
Q_D( JSResolver );
|
||||
QVariantMap arguments;
|
||||
arguments["url"] = result->url();
|
||||
|
||||
QString propertyName = scriptSource.split('(').first();
|
||||
|
||||
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 ) );
|
||||
return scriptObject()->invoke( "getStreamUrl", arguments );
|
||||
}
|
||||
|
@ -75,6 +75,8 @@ public:
|
||||
|
||||
ScriptAccount* scriptAccount() const;
|
||||
|
||||
ScriptJob* getStreamUrl( const result_ptr& result ) override;
|
||||
|
||||
public slots:
|
||||
void resolve( const Tomahawk::query_ptr& query ) override;
|
||||
void stop() override;
|
||||
@ -89,6 +91,10 @@ signals:
|
||||
protected:
|
||||
QVariant callOnResolver( const QString& scriptSource );
|
||||
|
||||
private slots:
|
||||
void onResolveRequestDone(const QVariantMap& data);
|
||||
void onLookupUrlRequestDone(const QVariantMap& data);
|
||||
|
||||
private:
|
||||
void init();
|
||||
|
||||
@ -96,12 +102,20 @@ private:
|
||||
void onCapabilitiesChanged( Capabilities capabilities );
|
||||
|
||||
// encapsulate javascript calls
|
||||
QVariantMap resolverSettings();
|
||||
QVariantMap resolverUserConfig();
|
||||
QVariantMap resolverInit();
|
||||
|
||||
Q_DECLARE_PRIVATE( JSResolver )
|
||||
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
|
||||
|
@ -21,10 +21,6 @@
|
||||
|
||||
#include "JSResolverHelper.h"
|
||||
|
||||
#include "database/Database.h"
|
||||
#include "database/DatabaseImpl.h"
|
||||
#include "playlist/PlaylistTemplate.h"
|
||||
#include "playlist/XspfPlaylistTemplate.h"
|
||||
#include "resolvers/ScriptEngine.h"
|
||||
#include "network/Servent.h"
|
||||
#include "utils/Closure.h"
|
||||
@ -51,6 +47,8 @@
|
||||
#include <QMap>
|
||||
#include <QWebFrame>
|
||||
#include <QLocale>
|
||||
#include <QNetworkReply>
|
||||
|
||||
#include <taglib/asffile.h>
|
||||
#include <taglib/flacfile.h>
|
||||
#include <taglib/id3v2framefactory.h>
|
||||
@ -76,7 +74,6 @@ JSResolverHelper::JSResolverHelper( const QString& scriptPath, JSResolver* paren
|
||||
: QObject( parent )
|
||||
, m_resolver( parent )
|
||||
, 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
|
||||
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
|
||||
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
|
||||
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 )
|
||||
{
|
||||
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
|
||||
JSResolverHelper::nativeRetrieveMetadata( int metadataId, const QString& url,
|
||||
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
|
||||
JSResolverHelper::nativeAsyncRequest( const int requestId, const QString& url,
|
||||
const QVariantMap& headers,
|
||||
const QVariantMap& options )
|
||||
JSResolverHelper::nativeAsyncRequest( const int requestId, const QVariantMap& options )
|
||||
{
|
||||
QString url = options[ "url" ].toString();
|
||||
QVariantMap headers = options[ "headers" ].toMap();
|
||||
|
||||
QNetworkRequest req( url );
|
||||
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["statusText"] = QString("%1 %2").arg( map["status"].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 );" )
|
||||
.arg( QString::number( requestId ) )
|
||||
.arg( json );
|
||||
m_resolver->d_func()->scriptAccount->evaluateJavaScript( javascript );
|
||||
QVariantMap responseHeaders;
|
||||
foreach( const QNetworkReply::RawHeaderPair& pair, reply->reply()->rawHeaderPairs() )
|
||||
{
|
||||
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 );
|
||||
}
|
||||
|
||||
|
||||
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 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
|
||||
@ -89,20 +86,9 @@ public:
|
||||
int sizehint,
|
||||
const QVariantMap& options );
|
||||
|
||||
/**
|
||||
* Native handler for asynchronous HTTP requests.
|
||||
*
|
||||
* 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 );
|
||||
Q_INVOKABLE void invokeNativeScriptJob( int requestId,
|
||||
const QString& methodName,
|
||||
const QVariantMap& params );
|
||||
|
||||
/**
|
||||
* Lucene++ indices for JS resolvers
|
||||
@ -122,18 +108,10 @@ public:
|
||||
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:
|
||||
QByteArray readRaw( const QString& fileName );
|
||||
QString readBase64( const QString& fileName );
|
||||
QString readCompressed( const QString& fileName );
|
||||
QString instanceUUID();
|
||||
QString uuid() const;
|
||||
int currentCountry() const;
|
||||
QString compress( const QString& data );
|
||||
@ -142,10 +120,6 @@ public slots:
|
||||
void log( const QString& message );
|
||||
bool fakeEnv() { return false; }
|
||||
|
||||
void addTrackResults( const QVariantMap& results );
|
||||
|
||||
void addUrlResult( const QString& url, const QVariantMap& result );
|
||||
|
||||
void nativeReportCapabilities( const QVariant& capabilities );
|
||||
|
||||
void reportScriptJobResults( const QVariantMap& result );
|
||||
@ -154,27 +128,19 @@ public slots:
|
||||
void unregisterScriptPlugin( const QString& type, const QString& objectId );
|
||||
|
||||
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 );
|
||||
|
||||
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 );
|
||||
QVariantList searchInFuzzyIndex( const Tomahawk::query_ptr& query );
|
||||
|
||||
// native script jobs
|
||||
void nativeAsyncRequest( int requestId, const QVariantMap& options );
|
||||
|
||||
|
||||
QVariantMap m_resolverConfig;
|
||||
JSResolver* m_resolver;
|
||||
QString m_scriptPath, m_urlCallback, m_urlTranslator;
|
||||
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;
|
||||
QString m_scriptPath;
|
||||
};
|
||||
|
||||
} // ns: Tomahawk
|
||||
|
@ -17,10 +17,31 @@
|
||||
*/
|
||||
#include "Resolver.h"
|
||||
|
||||
#include "../resolvers/SyncScriptJob.h"
|
||||
#include "../Result.h"
|
||||
|
||||
#include <QPixmap>
|
||||
|
||||
|
||||
Tomahawk::Resolver::Resolver( QObject* parent )
|
||||
: QObject( parent )
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
QPixmap
|
||||
Tomahawk::Resolver::icon( const QSize& ) const
|
||||
{
|
||||
Q_ASSERT(false);
|
||||
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
|
||||
#define RESOLVER_H
|
||||
|
||||
#include "../ResultProvider.h"
|
||||
#include "Typedefs.h"
|
||||
#include "DllMacro.h"
|
||||
|
||||
@ -36,21 +35,23 @@
|
||||
|
||||
namespace Tomahawk
|
||||
{
|
||||
class ScriptJob;
|
||||
|
||||
class DLLEXPORT Resolver : public QObject, public ResultProvider
|
||||
class DLLEXPORT Resolver : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
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 timeout() const = 0;
|
||||
|
||||
virtual QPixmap icon( const QSize& size ) const override;
|
||||
|
||||
public slots:
|
||||
virtual void resolve( const Tomahawk::query_ptr& query ) = 0;
|
||||
virtual ScriptJob* getStreamUrl( const result_ptr& result );
|
||||
};
|
||||
|
||||
} //ns
|
||||
|
@ -326,21 +326,6 @@ ScriptAccount::parseResultVariantList( const QVariantList& reslist )
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
@ -348,8 +333,27 @@ ScriptAccount::parseResultVariantList( const QVariantList& reslist )
|
||||
}
|
||||
|
||||
|
||||
QSharedPointer< ScriptCollection >
|
||||
ScriptAccount::scriptCollection( const QString& id ) const
|
||||
ScriptJob*
|
||||
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 );
|
||||
virtual QVariant syncInvoke( const scriptobject_ptr& scriptObject, const QString& methodName, const QVariantMap& arguments ) = 0;
|
||||
|
||||
virtual void startJob( ScriptJob* scriptJob ) = 0;
|
||||
|
||||
void reportScriptJobResult( const QVariantMap& result );
|
||||
void registerScriptPlugin( 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 );
|
||||
|
||||
// helpers
|
||||
QList< Tomahawk::result_ptr > parseResultVariantList( const QVariantList& reslist );
|
||||
|
||||
QSharedPointer< ScriptCollection > scriptCollection( const QString& id ) const;
|
||||
ScriptJob* resolve( const scriptobject_ptr& scriptObject, const query_ptr& query );
|
||||
|
||||
private slots:
|
||||
void onJobDeleted( const QString& jobId );
|
||||
|
@ -26,7 +26,10 @@
|
||||
#include "resolvers/ScriptCommand_AllArtists.h"
|
||||
#include "resolvers/ScriptCommand_AllAlbums.h"
|
||||
#include "resolvers/ScriptCommand_AllTracks.h"
|
||||
#include "resolvers/ScriptJob.h"
|
||||
#include "ScriptAccount.h"
|
||||
#include "Result.h"
|
||||
#include "Pipeline.h"
|
||||
|
||||
#include <QImageReader>
|
||||
#include <QPainter>
|
||||
@ -223,6 +226,15 @@ void ScriptCollection::parseMetaData()
|
||||
return parseMetaData( readMetaData() );
|
||||
}
|
||||
|
||||
ScriptJob*
|
||||
ScriptCollection::getStreamUrl( const result_ptr& result )
|
||||
{
|
||||
QVariantMap arguments;
|
||||
arguments["url"] = result->url();
|
||||
|
||||
return scriptObject()->invoke( "getStreamUrl", arguments );
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ScriptCollection::parseMetaData( const QVariantMap& metadata )
|
||||
@ -326,3 +338,58 @@ ScriptCollection::onIconFetched()
|
||||
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( 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:
|
||||
void onIconFetched();
|
||||
void onResolveRequestDone( const QVariantMap& data );
|
||||
|
||||
private:
|
||||
ScriptAccount* m_scriptAccount;
|
||||
|
@ -117,7 +117,7 @@ ScriptCommand_AllTracks::onTracksJobDone( const QVariantMap& result )
|
||||
QSharedPointer< ScriptCollection > collection = m_collection.objectCast< ScriptCollection >();
|
||||
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;
|
||||
|
@ -19,6 +19,7 @@
|
||||
|
||||
#include "SourceList.h"
|
||||
#include "../ScriptAccount.h"
|
||||
#include "../../Pipeline.h"
|
||||
|
||||
using namespace Tomahawk;
|
||||
|
||||
@ -29,12 +30,14 @@ void ScriptCollectionFactory::addPlugin( const QSharedPointer<ScriptCollection>&
|
||||
|
||||
collection->setOnline( true );
|
||||
SourceList::instance()->addScriptCollection( collection );
|
||||
Pipeline::instance()->addResolver( collection.data() );
|
||||
}
|
||||
|
||||
void ScriptCollectionFactory::removePlugin( const QSharedPointer<ScriptCollection>& collection ) const
|
||||
{
|
||||
collection->setOnline( false );
|
||||
SourceList::instance()->removeScriptCollection( collection );
|
||||
Pipeline::instance()->removeResolver( collection.data() );
|
||||
}
|
||||
|
||||
QSharedPointer< ScriptCollection > ScriptCollectionFactory::createPlugin( const scriptobject_ptr& object, ScriptAccount* scriptAccount )
|
||||
|
Loading…
x
Reference in New Issue
Block a user