1
0
mirror of https://github.com/tomahawk-player/tomahawk.git synced 2025-08-15 10:33:59 +02:00

Move to new es6 based standalone resolvers

This commit is contained in:
Dominik Schmidt
2015-10-25 20:18:08 +01:00
parent 39eda52f05
commit 72dab1aae6
3 changed files with 14 additions and 458 deletions

View File

@@ -39,21 +39,6 @@ if ((typeof Tomahawk === "undefined") || (Tomahawk === null)) {
Tomahawk.apiVersion = "0.2.2";
//Statuses considered a success for HTTP request
var httpSuccessStatuses = [200, 201];
// install RSVP error handler for uncaught(!) errors
RSVP.on('error', function (reason) {
var resolverName = "";
if (Tomahawk.resolver.instance) {
resolverName = Tomahawk.resolver.instance.settings.name + " - ";
}
if (reason) {
console.error(resolverName + 'Uncaught error:' + JSON.stringify(reason));
} else {
console.error(resolverName + 'Uncaught error: error thrown from RSVP but it was empty');
}
});
/**
* Compares versions strings
@@ -158,14 +143,6 @@ Tomahawk.extend = function (object, members) {
return newObject;
};
//Deprecated for 0.9 resolvers. Reporting resolver capabilities is no longer necessary.
var TomahawkResolverCapability = {
NullCapability: 0,
Browsable: 1,
PlaylistSync: 2,
AccountFactory: 4,
UrlLookup: 8
};
//Deprecated for 0.9 resolvers. Use Tomahawk.UrlType instead.
var TomahawkUrlType = {
@@ -249,106 +226,6 @@ var TomahawkResolver = {
}
};
Tomahawk.Resolver = {
init: function () {
},
scriptPath: function () {
return Tomahawk.resolverData().scriptPath;
},
getConfigUi: function () {
return {};
},
getUserConfig: function () {
return JSON.parse(window.localStorage[this.scriptPath()] || "{}");
},
saveUserConfig: function () {
window.localStorage[this.scriptPath()] = JSON.stringify(Tomahawk.resolverData().config);
this.newConfigSaved(Tomahawk.resolverData().config);
},
newConfigSaved: function () {
},
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 (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))
});
});
});
},
_testConfig: function (config) {
return RSVP.Promise.resolve(this.testConfig(config)).then(function () {
return {result: Tomahawk.ConfigTestResultType.Success};
});
}
};
// help functions
Tomahawk.valueForSubNode = function (node, tag) {
@@ -487,175 +364,7 @@ Tomahawk.nativeAsyncRequestDone = function (reqId, xhr) {
delete Tomahawk.asyncRequestCallbacks[reqId];
};
/**
* Possible options:
* - method: The HTTP request method (default: GET)
* - username: The username for HTTP Basic Auth
* - password: The password for HTTP Basic Auth
* - errorHandler: callback called if the request was not completed
* - 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
*/
Tomahawk.asyncRequest = function (url, callback, extraHeaders, options) {
// unpack options
var opt = options || {};
var method = opt.method || 'GET';
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);
} 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);
}
}
};
xmlHttpRequest.send(opt.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;
} else {
settings = settings || {};
settings.url = url;
}
settings.type = settings.type || settings.method || 'get';
settings.method = settings.type;
settings.dataFormat = settings.dataFormat || 'form';
if (settings.data) {
var formEncode = function (obj) {
var str = [];
for (var p in obj) {
if (obj[p] !== undefined) {
if (Array.isArray(obj[p])) {
for (var i = 0; i < obj[p].length; i++) {
str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p][i]));
}
} else {
str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]));
}
}
}
str.sort();
return str.join("&");
};
if (typeof settings.data === 'object') {
if (settings.dataFormat == 'form') {
settings.data = formEncode(settings.data);
settings.contentType = settings.contentType || 'application/x-www-form-urlencoded';
} else if (settings.dataFormat == 'json') {
settings.data = JSON.stringify(settings.data);
settings.contentType = settings.contentType || 'application/json';
} else {
throw new Error("Tomahawk.ajax: unknown dataFormat requested: "
+ settings.dataFormat);
}
} else {
throw new Error("Tomahawk.ajax: data should be either object or string");
}
if (settings.type.toLowerCase() === 'get') {
settings.url += '?' + settings.data;
delete settings.data;
} else {
settings.headers = settings.headers || {};
if (!settings.headers.hasOwnProperty('Content-Type')) {
settings.headers['Content-Type'] = settings.contentType;
}
}
}
return new RSVP.Promise(function (resolve, reject) {
settings.errorHandler = reject;
Tomahawk.asyncRequest(settings.url, resolve, settings.headers, settings);
}).then(function (xhr) {
if (settings.rawResponse) {
return xhr;
}
var responseText = xhr.responseText;
var contentType;
if (settings.dataType === 'json') {
contentType = 'application/json';
} else if (settings.dataType === 'xml') {
contentType = 'text/xml';
} else if (typeof xhr.getResponseHeader !== 'undefined') {
contentType = xhr.getResponseHeader('Content-Type');
} else if (xhr.hasOwnProperty('contentType')) {
contentType = xhr['contentType'];
} else {
contentType = 'text/html';
}
if (~contentType.indexOf('application/json')) {
return JSON.parse(responseText);
}
if (~contentType.indexOf('text/xml')) {
var domParser = new DOMParser();
return domParser.parseFromString(responseText, "text/xml");
}
return xhr.responseText;
});
};
Tomahawk.post = function (url, settings) {
if (typeof url === "object") {
settings = url;
} else {
settings = settings || {};
settings.url = url;
}
settings.method = 'POST';
return Tomahawk.ajax(settings);
};
Tomahawk.get = function (url, settings) {
return Tomahawk.ajax(url, settings);
};
Tomahawk.assert = function (assertion, message) {
Tomahawk.nativeAssert(assertion, message);
@@ -815,116 +524,6 @@ Tomahawk.base64Encode = function (b) {
return window.btoa(b);
};
Tomahawk.PluginManager = {
objects: {},
objectCounter: 0,
identifyObject: function (object) {
if (!object.hasOwnProperty('id')) {
object.id = this.objectCounter++;
}
return object.id;
},
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);
},
unregisterPlugin: function (type, object) {
this.objects[this.identifyObject(object)] = object;
Tomahawk.log("unregisterPlugin: " + type + " id: " + object.id);
Tomahawk.unregisterScriptPlugin(type, object.id);
},
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';
}
}
var pluginManager = this;
if (!this.objects[objectId]) {
Tomahawk.log("Object not found! objectId: " + objectId + " methodName: " + methodName);
} else {
if (!this.objects[objectId][methodName]) {
Tomahawk.log("Function not found: " + methodName);
}
}
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);
}
return this.objects[objectId][methodName];
},
invoke: function (requestId, objectId, methodName, params) {
RSVP.Promise.resolve(this.invokeSync(requestId, objectId, methodName, params))
.then(function (result) {
Tomahawk.reportScriptJobResults({
requestId: requestId,
data: result
});
}, function (error) {
Tomahawk.reportScriptJobResults({
requestId: requestId,
error: error
});
});
}
};
Tomahawk.NativeScriptJobManager = {
idCounter: 0,
deferreds: {},
@@ -943,15 +542,6 @@ Tomahawk.NativeScriptJobManager = {
}
};
Tomahawk.UrlType = {
Any: 0,
Playlist: 1,
Track: 2,
Album: 3,
Artist: 4,
XspfPlaylist: 5
};
Tomahawk.ConfigTestResultType = {
Other: 0,
Success: 1,
@@ -1213,8 +803,6 @@ Tomahawk.Country = {
LatinAmericaAndTheCaribbean: 246
};
Tomahawk.collections = [];
Tomahawk.Collection = {
BrowseCapability: {
Artists: 1,
@@ -1709,7 +1297,7 @@ Tomahawk.Collection = {
);
return t.execDeferredStatements();
}).then(function (results) {
return {results: Tomahawk.resolver.instance._convertUrls(results[0])};
return {results: resolverInstance._convertUrls(results[0])};
});
},

View File

@@ -209,49 +209,17 @@ JSResolver::init()
}
const QByteArray scriptContents = scriptFile.readAll();
// tomahawk.js
{
// add c++ part of tomahawk javascript library
d->scriptAccount->addToJavaScriptWindowObject( "Tomahawk", d->resolverHelper );
// load es6-promises shim
d->scriptAccount->loadScript( RESPATH "js/rsvp-latest.min.js" );
// Load CrytoJS core
d->scriptAccount->loadScript( RESPATH "js/cryptojs-core.js" );
// Load CryptoJS modules
QStringList jsfiles;
jsfiles << "*.js";
QDir cryptojs( RESPATH "js/cryptojs" );
foreach ( QString jsfile, cryptojs.entryList( jsfiles ) )
{
d->scriptAccount->loadScript( RESPATH "js/cryptojs/" + jsfile );
}
// Load tomahawk.js
d->scriptAccount->loadScript( RESPATH "js/tomahawk.js" );
}
// tomahawk-infosystem.js
{
// TODO: be smarter about this, only include this if the resolver supports infoplugins
// add deps
d->scriptAccount->loadScript( RESPATH "js/tomahawk-infosystem.js" );
}
// add resolver dependencies, if any
d->scriptAccount->loadScripts( d->requiredScriptPaths );
d->scriptAccount->addToJavaScriptWindowObject( "Tomahawk", d->resolverHelper );
// add resolver
d->scriptAccount->loadScript( filePath() );
// HACK: register resolver object
d->scriptAccount->evaluateJavaScript( "Tomahawk.PluginManager.registerPlugin('resolver', Tomahawk.resolver.instance);" )
;
d->scriptAccount->evaluateJavaScript(
"var resolverInstance = new (require('main').default);"
"Tomahawk.PluginManager.registerPlugin('resolver', resolverInstance);"
);
// init resolver
resolverInit();
@@ -545,10 +513,10 @@ JSResolver::callOnResolver( const QString& scriptSource )
QString propertyName = scriptSource.split('(').first();
return d->scriptAccount->evaluateJavaScriptWithResult( QString(
"if(Tomahawk.resolver.instance['_adapter_%1']) {"
" Tomahawk.resolver.instance._adapter_%2;"
"if(resolverInstance['_adapter_%1']) {"
" resolverInstance._adapter_%2;"
"} else {"
" Tomahawk.resolver.instance.%2"
" resolverInstance.%2;"
"}"
).arg( propertyName ).arg( scriptSource ) );
}

View File

@@ -712,10 +712,10 @@ JSResolverHelper::customIODeviceFactory( const Tomahawk::result_ptr&, const QStr
{
QString qid = uuid();
QString getUrl = QString(
"if(Tomahawk.resolver.instance['_adapter_%1']) {"
" Tomahawk.resolver.instance._adapter_%1( {qid: '%2', url: '%3'} );"
"if(resolverInstance['_adapter_%1']) {"
" resolverInstance._adapter_%1( {qid: '%2', url: '%3'} );"
"} else {"
" Tomahawk.resolver.instance.%1( {qid: '%2', url: '%3'} );"
" resolverInstance.%1( {qid: '%2', url: '%3'} );"
"}"
).arg( m_urlCallback )
.arg( qid )
@@ -726,7 +726,7 @@ JSResolverHelper::customIODeviceFactory( const Tomahawk::result_ptr&, const QStr
}
else
{
QString getUrl = QString( "Tomahawk.resolver.instance.%1( '%2' );" ).arg( m_urlCallback )
QString getUrl = QString( "resolverInstance.%1( '%2' );" ).arg( m_urlCallback )
.arg( url );
QString urlStr = m_resolver->d_func()->scriptAccount->evaluateJavaScriptWithResult( getUrl ).toString();