"use strict"; class trserver { #rpcurl; #authHeader; #sessionHeader; #requests = []; #torrents = {}; #isOnline = false; #logger = function(msg) {}; #torrentCallback = function(hash, torrentInfo) {}; constructor (initdata, logger, tcb) { this.#rpcurl = initdata.rpcurl; if (logger != null) { this.#logger = logger; } if (tcb != null) { this.#torrentCallback = tcb; } this.#authHeader = initdata.auth; this.#sessionHeader = null; } #rpccall_prepare(method, args) { let request = { 'payload': { 'method': method, 'arguments': args } }; let tag = this.#requests.push(request) - 1; return tag; } #rpccall(tag) { let request = this.#requests[tag]; //this.#logger(`[${tag}] >> ${request.payload.method}`); let rpc = new window.XMLHttpRequest(); rpc.tag = tag; rpc.open('POST', this.#rpcurl); if (this.#authHeader != null) { rpc.setRequestHeader('authorization', this.#authHeader); } if (this.#sessionHeader != null) { rpc.setRequestHeader('X-Transmission-Session-Id', this.#sessionHeader); } rpc.setRequestHeader('content-type', 'application/json'); rpc.addEventListener('readystatechange', this.#handler.bind(this)); rpc.addEventListener('timeout', this.#handler.bind(this)); rpc.addEventListener('error', this.#handler.bind(this)); rpc.timeout = 10000; try { rpc.send(JSON.stringify(request.payload)); } catch (error) { this.#logger(error); } } rpccall_debug(method, args) { var tag = this.#rpccall_prepare(method, args); let request = this.#requests[tag]; request.success = console.info; this.#rpccall(tag); } #handler(e) { switch (e.type) { case 'readystatechange': if (e.target.readyState == 4) { let tag = e.target.tag; let request = this.#requests[tag]; switch (e.target.status) { case 200: if (!this.#isOnline) { this.#logger('Server API connection estabilished'); this.#isOnline = true; this.#forceUpdateTorrents(); } //request.response = JSON.parse(e.target.responseText); if (request.success != null) { request.success(JSON.parse(e.target.responseText).arguments); } else { //this.#logger(e.target.responseText); } break; case 409: this.#sessionHeader = e.target.getResponseHeader('X-Transmission-Session-Id'); this.#rpccall(tag); break; default: request.response = {'code': e.target.status, 'content': e.target.responseText}; //this.#logger(JSON.stringify(request.response)); break; } } break; case 'timeout': if (this.#isOnline) { this.#logger('Server API connection timed out'); this.#isOnline = false; this.#forceUpdateTorrents(); } break; case 'error': if (this.#isOnline) { this.#logger('Server API refuses connection'); this.#isOnline = false; this.#forceUpdateTorrents(); } this.#logger(JSON.stringify(e)); break; } } refreshSession (){ //let tag = this.#rpccall_prepare('session-get', null); //this.#rpccall(tag); } #parseTorrents(response) { // mark everything as deleted but clean for (const [key, value] of Object.entries(this.#torrents)) { value.wasDeleted = value.deleted; value.deleted = true; value.dirty = false; } // Update torrent data for (const torrent of response.torrents) { let hash = torrent.hashString; if (!(hash in this.#torrents)) { this.#torrents[hash] = {'dirty': true}; } let knownTorrent = this.#torrents[hash]; for (const [key, value] of Object.entries(torrent)) { let parsedValue = undefined; switch (key) { case 'hashString': break; case 'percentDone': parsedValue = Math.floor(value * 100); break; default: parsedValue = value; break; } if (parsedValue != undefined && knownTorrent[key] !== parsedValue) { knownTorrent[key] = parsedValue; knownTorrent.dirty = true; } } knownTorrent.deleted = false; } //check for changes for (const [key, value] of Object.entries(this.#torrents)) { if (value.deleted != value.wasDeleted || value.dirty) { // call UI update this.#torrentCallback(key, value); } } } #forceUpdateTorrents() { for (const [key, value] of Object.entries(this.#torrents)) { // call UI update this.#torrentCallback(key, value); } } refreshTorrentList() { let tag = this.#rpccall_prepare('torrent-get', {'fields': ['id', 'hashString', 'name', 'status', 'percentDone', 'comment', 'magnetLink']}); this.#requests[tag].success = this.#parseTorrents.bind(this); this.#rpccall(tag); } torrent_add_url(url, dontstart, path) { let args = { 'filename': url, 'paused': dontstart }; if (path != null) { args['download-dir'] = path; } let tag = this.#rpccall_prepare('torrent-add', args); this.#rpccall(tag); } torrent_start(hash) { let tag = this.#rpccall_prepare('torrent-start', {'ids':[hash]}); this.#rpccall(tag); } torrent_pause(hash) { let tag = this.#rpccall_prepare('torrent-stop', {'ids':[hash]}); this.#rpccall(tag); } getInstanceData() { return { 'rpcurl': this.#rpcurl, 'auth': this.#authHeader }; } isOnline() { return this.#isOnline; } };