1
0
Fork 0
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

527 lines
12 KiB

/*jslint laxbreak:true */
/*jslint laxcomma:true */
/*jslint loopfunc:true */
/*jslint strict:true */
/*jslint browser:true */
/*jslint devel:true */
/**
* TODO: If the user agrees, set the username/passs in a cookie
**/
define([
"underscore"
, "backbone"
, "toolbox"
, "models/dmap-type"
, "models/dmap"
], function (_, Backbone, Toolbox, DMAPType, DMAPModel) {
"use strict";
var Client = Toolbox.Base.extend(_.extend({
constructor: function(attributes, options) {
_.bindAll(this
, "setAuth"
, "setState"
, "getState"
, "init"
, "deinit"
, "logout"
, "buildRequestUid"
, "url"
, "urlHost"
, "xhr"
, "fetchXHR"
, "fetchContentTypes"
, "fetchServerInfo"
, "fetchLogin"
, "fetchDatabases"
, "fetchDatabase"
, "fetchPlaylists"
, "fetchPlaylist"
, "fetchBrowse"
, "fetchgenres"
, "fetchArtists"
, "fetchAlbums"
, "on"
, "off"
, "trigger"
);
_.extend(this, Client.defaults);
this.attributes = _.extend({}, Client.prototype.attributes, attributes);
this.setAuth();
return this;
}
, setAuth: function(username, password) {
if (!_.isUndefined(username)) {
this.attributes.username = username;
}
if (!_.isUndefined(password)) {
this.attributes.password = password;
}
if (!_.isEmpty(this.attributes.password) || !_.isEmpty(this.attributes.username)) {
this.attributes.basicauth = window.btoa((this.attributes.username || "") + ":" + (this.attributes.password || ""));
}
}
, setState: function (state) {
if (_.isNumber(state)) {
this.trigger("state", this, state);
return this.state = state;
} else if (_.isString(state) && !_.isUndefined(this._states[state])) {
this.trigger("state", this, this._states[state]);
return this.state = this._states[state];
}
return undefined;
}
, getState: function () {
return this.state;
}
, init: function () {
var that = this
;
this.setState("connecting");
//console.debug("Fetching content types");
that.fetchContentTypes(function __fnClientCbFetchContentTypes(contentTypes) {
//console.debug("Fetching server info");
that.fetchServerInfo(function __fnClientCbFetchServerInfo(serverInfo) {
//console.debug("Fetching login");
that.fetchLogin(function __fnClientCbFetchLogin(sessionInfo) {
// TODO: Here we need to request /update for the database version
//console.debug("Fetching databases info");
//that.fetchDatabases(function () {
//console.debug("Fetching database 1");
//that.fetchDatabase(function () {
//console.debug("Fetching playlists info");
//that.fetchPlaylists(function () {
//console.debug("Fetching playlist 1 of db 1");
//that.fetchPlaylist(function () {
//console.debug("Fetching genres of db 1");
//that.fetchGenres(function () {
//console.debug("Fetching artists of db 1");
//that.fetchArtists(function () {
//console.debug("Fetching albums of db 1");
//that.fetchAlbums(function () {
that.setState("connected");
that.trigger("inited", serverInfo);
//});
//});
//});
//});
//});
//});
//});
});
});
});
return this;
}
, deinit: function () {
var that = this
;
this.logout();
}
,logout: function () {
}
, buildRequestUid: function(path, fields, query) {
var that = this
;
if (_.isEmpty(path) || !_.isString(path)) {
throw new Error("INVALID_TYPE_ERROR");
}
if (!_.isArray(fields)) {
fields = [];
}
if (!_.isArray(query)) {
query = [];
}
}
,url: function (request) {
var that = this
, uri = ""
;
if (typeof request === "string") {
uri = request; // Add leading slash here!
}
if (_.isNumber(this.collections.session.id)) {
var prefix = "?";
if (uri.indexOf("?") >= 0) {
prefix = "&";
}
uri += prefix + "session-id=" + this.collections.session.id;
}
return this.attributes.protocol + "://" + this.urlHost() + uri;
}
, urlHost: function () {
var that = this
;
return this.attributes.hostname + ":" + this.attributes.port;
}
// TODO: Could be interesting to set a useragent, could possibly affect transcoding on the server side
, xhr: function (url, responseType, callback) {
var that = this
;
var xhr = new XMLHttpRequest();
var contentType = "text/plain; charset=utf-8";
if (typeof responseType === "function") {
callback = responseType;
responseType = "arraybuffer";
}
xhr.open("GET", url, true);
xhr.responseType = responseType;
xhr.timeout = 2000;
xhr.setRequestHeader("Content-type", contentType);
xhr.overrideMimeType("text/plain; charset=x-user-defined"); // Technically not need with v2 and responseType but for good measures
if (!_.isEmpty(that.attributes.basicauth)) {
xhr.withCredentials = true;
xhr.setRequestHeader("Authorization", "Basic " + that.attributes.basicauth);
}
try {
xhr.onerror = function () {console.log("Error", arguments, this);};
xhr.onabort = function () {console.log("Abort", arguments, this);};
xhr.ontimeout = function () {
that.setState("timedout");
};
xhr.onload = function (e) {
if (this.status === 200) {
callback(null, this.response);
} else if (this.status === 401) {
that.setState("unauthorized");
that.trigger("unauthorized", that, [this, e]);
} else {
that.setState("failed");
callback(e.status);
}
};
xhr.send();
} catch (e) {
console.error(e);
callback(e, null);
}
}
, fetchXHR: function (path, success, error) {
var that = this
;
that.xhr(that.url(path), function __fetchXHRCallback(err, content) {
if (err) {
if (!_.isFunction(error)) {
error = function (err, cont) {
throw [err, cont];
};
}
error(err, content);
} else {
success(content);
}
});
}
, fetchContentTypes: function (success, error) {
var that = this
;
that.fetchXHR("/content-codes", function __fetchContentTypesCallback(content) {
that.collections.contentCodes = new DMAPModel(content);
if (_.isFunction(success)) {
success(that.collections.contentCodes);
}
}, error);
}
, fetchServerInfo: function (success, error) {
var that = this
;
that.fetchXHR("/server-info", function __fetchServerInfoCallback(content) {
that.collections.serverInfo = new DMAPModel(content, {
contentCodes: that.collections.contentCodes
});
if (_.isFunction(success)) {
success(that.collections.serverInfo);
}
}, error);
}
, fetchLogin: function (success, error) {
var that = this
;
that.fetchXHR("/login", success, error);
}
, fetchDatabases: function (success, error) {
var that = this
;
this.setState("loading");
that.fetchXHR("/databases", function __fetchDatabasesCallback(content) {
that.setState("loaded");
that.collections.databasesInfo = new DMAPModel(content, {
contentCodes: that.collections.contentCodes
});
if (_.isFunction(success)) {
success(that.collections.databasesInfo);
}
}, error);
}
, fetchDatabase: function (success, error, dbId) {
var that = this
;
if (_.isUndefined(dbId) || !_.isNumeric(dbId)) {
dbId = 1;
}
this.setState("loading");
that.fetchXHR("/databases/" + dbId + "/items", function __fetchDatabaseCallback(content) {
that.setState("loaded");
that.collections.databases[dbId] = new DMAPModel(content, {
contentCodes: that.collections.contentCodes
});
if (_.isFunction(success)) {
success(that.collections.databases[dbId]);
}
}, error);
}
, fetchPlaylists: function (success, error, dbId) {
var that = this
;
if (_.isUndefined(dbId) || !_.isNumeric(dbId)) {
dbId = 1;
}
this.setState("loading");
that.fetchXHR("/databases/" + dbId + "/containers", function __fetchPlaylistsCallback(content) {
that.setState("loaded");
that.collections.playlistsInfo = new DMAPModel(content, {
contentCodes: that.collections.contentCodes
});
if (_.isFunction(success)) {
success(that.collections.playlistsInfo);
}
}, error);
}
, fetchPlaylist: function (success, error, playlistId, dbId) {
var that = this
;
if (_.isUndefined(playlistId) || !_.isNumeric(playlistId)) {
playlistId = 1;
}
if (_.isUndefined(dbId) || !_.isNumeric(dbId)) {
dbId = 1;
}
var requestUri = "/databases/"
;
requestUri += dbId;
requestUri += "/containers/";
requestUri += playlistId;
requestUri += "/items";
requestUri += "?meta=dmap.itemid,dmap.persistentid,dmap.containeritemid";
this.setState("loading");
that.fetchXHR(requestUri, function __fetchPlaylistCallback(content) {
that.setState("loaded");
if (!_.isArray(that.collections.playlists[dbId])) {
that.collections.playlists[dbId] = [];
}
that.collections.playlists[dbId][playlistId] = new DMAPModel(content, {
contentCodes: that.collections.contentCodes
});
if (_.isFunction(success)) {
success(that.collections.playlists[dbId][playlistId]);
}
}, error);
}
, fetchBrowse: function (success, error, dbId, type) {
var that = this
;
// TODO: We need to detect if the server supports browsing or not an implement our own otherwise.
if (_.isEmpty(type) || !_.isString(type) || !_.isArray(that.collections.browser[type])) {
throw new Error("INVALID_BROWSE_TYPE");
}
var requestUri = "/databases/"
;
requestUri += dbId;
requestUri += "/browse/";
requestUri += type;
requestUri += "?meta=dmap.itemid,dmap.persistentid,dmap.containeritemid";
this.setState("loading");
that.fetchXHR(requestUri, function __fetchBrowseCallback(content) {
that.setState("loaded");
that.collections.browser[type][dbId] = new DMAPModel(content, {
contentCodes: that.collections.contentCodes
});
if (_.isFunction(success)) {
success(that.collections.browser[type][dbId]);
}
}, error);
}
, fetchGenres: function (success, error, dbId) {
var that = this
;
if (_.isUndefined(dbId) || !_.isNumeric(dbId)) {
dbId = 1;
}
return that.fetchBrowse(success, error, dbId, "genres");
}
, fetchArtists: function (success, error, dbId) {
var that = this
;
if (_.isUndefined(dbId) || !_.isNumeric(dbId)) {
dbId = 1;
}
return that.fetchBrowse(success, error, dbId, "artists");
}
, fetchAlbums: function (success, error, dbId) {
var that = this
;
if (_.isUndefined(dbId) || !_.isNumeric(dbId)) {
dbId = 1;
}
return that.fetchBrowse(success, error, dbId, "albums");
}
}, Backbone.Events), {
defaults: {
attributes: {
protocol: window.location.protocol.replace(":", "")
, hostname: window.location.hostname
, port: 3689
, path: ""
, username: ""
, password: ""
}
, state: 0
, _states: {
failed: -1
, disconnected: 0
, timedout: 1
, connecting: 2
, unauthorized: 3
, connected: 4
, loading: 5
, loaded: 6
}
, collections: {
contentCodes: null
, serverInfo: null
, session: {
id: null
}
, databasesInfo:null
, databases: []
, playlistsInfo:null
, playlists: []
, browser: {
genres: []
, artists: []
, albums: []
}
}
}
});
//_.extend(Client.prototype, Backbone.Events);
return Client;
});