From 46724b231a44e9b3a879ba92ae516f2114b68618 Mon Sep 17 00:00:00 2001 From: Evin Callahan Date: Thu, 7 Jun 2018 17:00:16 -0500 Subject: [PATCH 1/8] fixed that as a result of the plugin work --- app/components/plugins/transmission/index.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/components/plugins/transmission/index.js b/app/components/plugins/transmission/index.js index fed070a..51793ee 100644 --- a/app/components/plugins/transmission/index.js +++ b/app/components/plugins/transmission/index.js @@ -32,8 +32,6 @@ var TransmissionDownloader = function( options ) { TransmissionDownloader.super_.apply( this, arguments ); - this.logger.debug( '[TODO] this needs to be fixed, should not be instantiated every time we need to use transmission' ); - // TODO: optimize this messyness // TODO: add a path.resolve to the values below this.mediaTypePathMap = { From 6cc9779b13445d58c64da3dd3e9c53657f18dbcd Mon Sep 17 00:00:00 2001 From: Evin Callahan Date: Thu, 14 Jun 2018 14:42:35 -0500 Subject: [PATCH 2/8] merge changes --- app/components/plugins/showrss/index.js | 130 ++++++++++++++++++++++++ app/components/plugins/url/index.js | 109 +++++++++++--------- 2 files changed, 190 insertions(+), 49 deletions(-) diff --git a/app/components/plugins/showrss/index.js b/app/components/plugins/showrss/index.js index 2292522..f1d8752 100644 --- a/app/components/plugins/showrss/index.js +++ b/app/components/plugins/showrss/index.js @@ -10,6 +10,136 @@ const Plugin = require('../../plugin_handler/base'); const plugins = require('../../plugin_handler'); +<<<<<<< Updated upstream +======= +var ShowRssSource = function (options) { + this.metadata = { + pluginId: 'info_showrss', // Unique ID of plugin + pluginName: 'ShowRss', // Display name of plugin + pluginTypes: ['source', 'series'], // 'source', 'downloader', 'player' + sourceType: 'continuous', // 'adhoc', 'continuous' + requires: ['com_flexget', 'com_transmissionbt'], // this plugin requires the flexget plugin + link: 'http://showrss.info/', // Link to provider site + description: 'Updated feed of TV shows' // Description of plugin provider + }; + + this.defaultSettings = { + enabled: true + }; + + ShowRssSource.super_.apply(this, arguments); + + return this; +} + + + +ShowRssSource.prototype.search = function (query) { + var self = this; + var SHOWS_URL = 'http://showrss.info/browse'; + var SHOWS_XPATH = '//*[@id="showselector"]/option'; + + if (_.isEmpty(query)) { + return Promise.reject(new Error('no query string in search')); + } + + return new Promise(function (resolve, reject) { + request({ + url: SHOWS_URL + }, function (err, resp, body) { + if (err) return reject(err); + + try { + var doc = new dom({ errorHandler: function (o) { } }).parseFromString(body); + var shownames_xml = xpath.select(SHOWS_XPATH, doc); + } catch (err) { + return reject(new Error(`cant parse showrss xml: ${err}`)); + } + + var shownames = _.filter(shownames_xml, function (e) { + return e.firstChild !== undefined; + }).map(function (e) { + const slug = Buffer + .from(`info_showrss:${e.getAttribute('value')}`) + .toString('base64'); + + return { + sourceId: self.metadata.pluginId, + sourceName: self.metadata.pluginName, + score: self.calculateScore(e), + downloadMechanism: 'flexget', + flexgetModel: 'showrss', + mediaType: 'series', + id: e.getAttribute('value'), + slug: slug, + category: 'TV Shows', + size: 'N/A', + title: e.firstChild.data + }; + }); + + // gives us just what was searched for + var shownames_filtered = _.filter(shownames, function (obj) { + return obj + && _.isString(obj.title) + && obj.title.toLowerCase().indexOf(query.toLowerCase()) > -1; + }); + + self.logger.info('[showrss] results: ', shownames_filtered.length); + resolve(shownames_filtered); + }) + }); +}; + + +ShowRssSource.prototype.removeId = function (id) { + var self = this; + return new Promise(function (resolve, reject) { + try { + self.removeState(id); + resolve(); + } catch (err) { + reject(err); + } + }); +} + +ShowRssSource.prototype.disableId = ShowRssSource.prototype.removeId; + + +ShowRssSource.prototype.calculateScore = function (result) { + return 1.0 / 10; +} + +ShowRssSource.prototype.status = function () { + return Promise.resolve([]); // uses torrent downloader and flexget +} + +// DEPRECATED +ShowRssSource.prototype.download = function (item) { + return this.enableId(item.id); +}; + +// DEPRECATED +ShowRssSource.prototype.remove = function (item) { + var self = this; + return new Promise(function (resolve, reject) { + try { + self.removeState(item.id); + resolve(item); + } catch (err) { + reject(new Error(err.toString())); + } + }); +} + + + +/////////////////// +// SERIES SPECIFIC +/////////////////// + +>>>>>>> Stashed changes // %d is the id # as stored by showrss const SHOW_HISTORY_DATA_URL = 'https://showrss.info/show/%d.rss'; diff --git a/app/components/plugins/url/index.js b/app/components/plugins/url/index.js index 9b54802..59af887 100644 --- a/app/components/plugins/url/index.js +++ b/app/components/plugins/url/index.js @@ -60,34 +60,43 @@ class KaptureURLHandler extends Plugin { url(url) { var self = this; - return new Promise(function (resolve, reject) { - var dest = self.getDestPath(url); - - // initially get content-type to try to figure out what type it is - request.head(url) - .on('error', reject) - .on('response', function (head) { - self.logger.debug('head:', head.headers); - - try { - // goes off and does its thing asyncronously - // we do this via try/catch because we want a quick response to say - // download started, not wait til it's finished to report back - self.handleMediaType(url, head); - } catch (err) { - return reject(err); - } - - // send back to client that download is in progress, then find status - // via normal download status method - return resolve({ - url: url, - contentType: head.headers['content-type'] || 'text/plain' - }); - }) + if( where ) { + this.logger.warn(`[KaptureUrlDownloader] where argument "${where}" ignored.. not yet supported`); + } + + return new Promise(function( resolve, reject ) { + // initially get content-type to try to figure out what type it is + request.head( url ) + .on( 'error', reject ) + .on( 'response', function( head ) { + self.logger.debug( 'head:', head.headers ); + + // initially get content-type to try to figure out what type it is + request.head(url) + .on('error', reject) + .on('response', function (head) { + self.logger.debug('head:', head.headers); + + try { + // goes off and does its thing asyncronously + // we do this via try/catch because we want a quick response to say + // download started, not wait til it's finished to report back + self.handleMediaType(url, head); + } catch (err) { + return reject(err); + } + + // send back to client that download is in progress, then find status + // via normal download status method + return resolve({ + url: url, + contentType: head.headers['content-type'] || 'text/plain' + }); + }) + }); }); - }; + } @@ -212,10 +221,9 @@ class KaptureURLHandler extends Plugin { downloadSlug(slug) { - return Promise.reject(new Error('KaptureURLHandler: downloadSlug() not yet implemented')); + return this.url(slug, where); } - /////////// // HELPERS /////////// @@ -225,18 +233,23 @@ class KaptureURLHandler extends Plugin { getDestPath(url, mediaPathSetting) { - return path.join( - this.config.getUserSetting('downloadPaths.root'), - this.config.getUserSetting(mediaPathSetting || 'downloadPaths.default'), - this.getFilename(url) + return path.join( + this.config.getUserSetting( 'downloadPaths.root' ), + this.config.getUserSetting( 'downloadPaths.' + (mediaPathSetting || 'default') ), + this.getFilename( url ) ); } - getFilename(url) { - return sanitize( - _.last(Url.parse(url).pathname.split('/')) // grabs filename of url - ); + const urlObj = Url.parse( url ); + + if( /https?:/.test(urlObj.protocol) ) { + return sanitize( + _.last( urlObj.pathname.split('/') ) // grabs filename of url + ); + } else { + return undefined; + } } @@ -255,21 +268,19 @@ class KaptureURLHandler extends Plugin { } - assumeDownloadPathFromCtype(contentType) { - if (/^image\/.*/.test(contentType)) { - return 'downloadPaths.photos'; - } else if (/^audio\/.*/.test(contentType)) { - return 'downloadPaths.music'; - } else if (/^video\/.*/.test(contentType)) { - return 'downloadPaths.movies'; - } else if (/^(text|application)\/.*/.test(contentType)) { - return 'downloadPaths.default'; - } else { - return 'downloadPaths.default'; - } + if( /^image\/.*/.test( contentType ) ) { + return 'photos'; + } else if( /^audio\/.*/.test( contentType ) ) { + return 'music'; + } else if( /^video\/.*/.test( contentType ) ) { + return 'movies'; + } else if( /^(text|application)\/.*/.test( contentType ) ) { + return 'default'; + } else { + return 'default'; + } } - } From a21cd6cf4b42b3b8b87e4324b09e05cd641685f3 Mon Sep 17 00:00:00 2001 From: Evin Callahan Date: Thu, 14 Jun 2018 14:46:48 -0500 Subject: [PATCH 3/8] whoops, didnt save --- app/components/plugins/showrss/index.js | 130 ------------------------ app/components/plugins/url/index.js | 2 +- 2 files changed, 1 insertion(+), 131 deletions(-) diff --git a/app/components/plugins/showrss/index.js b/app/components/plugins/showrss/index.js index f1d8752..2292522 100644 --- a/app/components/plugins/showrss/index.js +++ b/app/components/plugins/showrss/index.js @@ -10,136 +10,6 @@ const Plugin = require('../../plugin_handler/base'); const plugins = require('../../plugin_handler'); -<<<<<<< Updated upstream -======= -var ShowRssSource = function (options) { - this.metadata = { - pluginId: 'info_showrss', // Unique ID of plugin - pluginName: 'ShowRss', // Display name of plugin - pluginTypes: ['source', 'series'], // 'source', 'downloader', 'player' - sourceType: 'continuous', // 'adhoc', 'continuous' - requires: ['com_flexget', 'com_transmissionbt'], // this plugin requires the flexget plugin - link: 'http://showrss.info/', // Link to provider site - description: 'Updated feed of TV shows' // Description of plugin provider - }; - - this.defaultSettings = { - enabled: true - }; - - ShowRssSource.super_.apply(this, arguments); - - return this; -} - - - -ShowRssSource.prototype.search = function (query) { - var self = this; - var SHOWS_URL = 'http://showrss.info/browse'; - var SHOWS_XPATH = '//*[@id="showselector"]/option'; - - if (_.isEmpty(query)) { - return Promise.reject(new Error('no query string in search')); - } - - return new Promise(function (resolve, reject) { - request({ - url: SHOWS_URL - }, function (err, resp, body) { - if (err) return reject(err); - - try { - var doc = new dom({ errorHandler: function (o) { } }).parseFromString(body); - var shownames_xml = xpath.select(SHOWS_XPATH, doc); - } catch (err) { - return reject(new Error(`cant parse showrss xml: ${err}`)); - } - - var shownames = _.filter(shownames_xml, function (e) { - return e.firstChild !== undefined; - }).map(function (e) { - const slug = Buffer - .from(`info_showrss:${e.getAttribute('value')}`) - .toString('base64'); - - return { - sourceId: self.metadata.pluginId, - sourceName: self.metadata.pluginName, - score: self.calculateScore(e), - downloadMechanism: 'flexget', - flexgetModel: 'showrss', - mediaType: 'series', - id: e.getAttribute('value'), - slug: slug, - category: 'TV Shows', - size: 'N/A', - title: e.firstChild.data - }; - }); - - // gives us just what was searched for - var shownames_filtered = _.filter(shownames, function (obj) { - return obj - && _.isString(obj.title) - && obj.title.toLowerCase().indexOf(query.toLowerCase()) > -1; - }); - - self.logger.info('[showrss] results: ', shownames_filtered.length); - resolve(shownames_filtered); - }) - }); -}; - - -ShowRssSource.prototype.removeId = function (id) { - var self = this; - return new Promise(function (resolve, reject) { - try { - self.removeState(id); - resolve(); - } catch (err) { - reject(err); - } - }); -} - -ShowRssSource.prototype.disableId = ShowRssSource.prototype.removeId; - - -ShowRssSource.prototype.calculateScore = function (result) { - return 1.0 / 10; -} - -ShowRssSource.prototype.status = function () { - return Promise.resolve([]); // uses torrent downloader and flexget -} - -// DEPRECATED -ShowRssSource.prototype.download = function (item) { - return this.enableId(item.id); -}; - -// DEPRECATED -ShowRssSource.prototype.remove = function (item) { - var self = this; - return new Promise(function (resolve, reject) { - try { - self.removeState(item.id); - resolve(item); - } catch (err) { - reject(new Error(err.toString())); - } - }); -} - - - -/////////////////// -// SERIES SPECIFIC -/////////////////// - ->>>>>>> Stashed changes // %d is the id # as stored by showrss const SHOW_HISTORY_DATA_URL = 'https://showrss.info/show/%d.rss'; diff --git a/app/components/plugins/url/index.js b/app/components/plugins/url/index.js index 59af887..74cd1eb 100644 --- a/app/components/plugins/url/index.js +++ b/app/components/plugins/url/index.js @@ -16,7 +16,7 @@ class KaptureURLHandler extends Plugin { pluginName: 'Kapture URL Handler', // Display name of plugin pluginTypes: ['downloader'], // 'source', 'downloader', 'player' sourceTypes: 'adhoc', // 'adhoc', 'continuous' - link: 'https://kapture.com', // Link to provider site + link: 'http://kapturebox.com', // Link to provider site downloadProviders: 'url', // if plugin can also download, what // downloadMechanism can it download? description: 'Simple URL download handler' // Description of plugin provider From d911b7ded52410dc864fc495e196d6f084b08e6d Mon Sep 17 00:00:00 2001 From: Evin Callahan Date: Thu, 14 Jun 2018 18:05:29 -0500 Subject: [PATCH 4/8] url stuff fixed, tests working --- app/components/plugins/flexget/index.js | 2 +- app/components/plugins/showrss/index.js | 1 - app/components/plugins/transmission/index.js | 4 +- app/components/plugins/url/index.js | 172 ++++---- app/components/plugins/youtube/index.js | 4 +- app/endpoints/getdownload.js | 19 +- app/endpoints/startdownloadfrommethod.js | 10 +- app/endpoints/stopdownload.js | 6 +- app/tests/scenarios.json | 426 +------------------ 9 files changed, 110 insertions(+), 534 deletions(-) diff --git a/app/components/plugins/flexget/index.js b/app/components/plugins/flexget/index.js index 33facd4..d4a2591 100644 --- a/app/components/plugins/flexget/index.js +++ b/app/components/plugins/flexget/index.js @@ -227,7 +227,7 @@ class FlexgetDownloader extends Plugin { .then(this.getModelsAndUpdateFlexget); } - removeDownloadId(id, deleteOnDisk) { + removeDownloadId(id, fromDisk) { return Promise.reject(new Error('Flexget: removeDownloadId: not yet implemented')); } diff --git a/app/components/plugins/showrss/index.js b/app/components/plugins/showrss/index.js index 2292522..0b4e3ba 100644 --- a/app/components/plugins/showrss/index.js +++ b/app/components/plugins/showrss/index.js @@ -33,7 +33,6 @@ class ShowRssSource extends Plugin { }; super(metadata, defaultSettings); - } diff --git a/app/components/plugins/transmission/index.js b/app/components/plugins/transmission/index.js index fb11db3..4fb6df1 100644 --- a/app/components/plugins/transmission/index.js +++ b/app/components/plugins/transmission/index.js @@ -55,8 +55,8 @@ class TransmissionDownloader extends Plugin { } - removeDownloadId(id, deleteFromDisk) { - return this.removeId(id, deleteFromDisk); + removeDownloadId(id, fromDisk) { + return this.removeId(id, fromDisk); } diff --git a/app/components/plugins/url/index.js b/app/components/plugins/url/index.js index 74cd1eb..a9fdfac 100644 --- a/app/components/plugins/url/index.js +++ b/app/components/plugins/url/index.js @@ -57,72 +57,67 @@ class KaptureURLHandler extends Plugin { - url(url) { + url(url, where) { var self = this; - if( where ) { - this.logger.warn(`[KaptureUrlDownloader] where argument "${where}" ignored.. not yet supported`); - } - - - return new Promise(function( resolve, reject ) { - // initially get content-type to try to figure out what type it is - request.head( url ) - .on( 'error', reject ) - .on( 'response', function( head ) { - self.logger.debug( 'head:', head.headers ); - - // initially get content-type to try to figure out what type it is - request.head(url) - .on('error', reject) - .on('response', function (head) { - self.logger.debug('head:', head.headers); - - try { - // goes off and does its thing asyncronously - // we do this via try/catch because we want a quick response to say - // download started, not wait til it's finished to report back - self.handleMediaType(url, head); - } catch (err) { - return reject(err); - } - - // send back to client that download is in progress, then find status - // via normal download status method - return resolve({ - url: url, - contentType: head.headers['content-type'] || 'text/plain' - }); - }) - }); + return new Promise(function (resolve, reject) { + // initially get content-type to try to figure out what type it is + request.head(url) + .on('error', reject) + .on('response', function (head) { + self.logger.debug('head:', head.headers); + + const dest = where || + self.assumeDownloadPathFromCtype(head.headers['content-type']); + + const fullDestPath = self.getDestPath(url, dest); + + try { + // goes off and does its thing asyncronously + // we do this via try/catch because we want a quick response to say + // download started, not wait til it's finished to report back + self.handleMediaType(url, head, fullDestPath); + } catch (err) { + return reject(err); + } + + // send back to client that download is in progress, then find status + // via normal download status method + return resolve({ + url: url, + contentType: head.headers['content-type'], + destName: dest, + fullDestPath: fullDestPath + }); + }); }); } // likely an rss feed of elements, handle continuously with flexget - handleXml(url, head) { + handleXml(url, head, dest) { this.logger.warn('[KaptureURLHandler.handleXml] not yet implemented: %s', url); throw new Error('rss feature not yet implemented'); } - handleLastResort(url, head) { + handleLastResort(url, head, dest) { var self = this; new Promise(function (resolve, reject) { var contentType = head.headers['content-type']; - var assumedDest = self.assumeDownloadPathFromCtype(contentType); - var fullDestPath = self.getDestPath(url, assumedDest); - var sha1 = crypto.createHash('sha1').update(url).digest('hex'); + var sha1 = crypto.createHash('sha1') + .update(`${url}+${dest}`) + .digest('hex'); var state = { sourceId: self.metadata.pluginId, sourceName: self.metadata.pluginName, title: self.getFilename(url), downloadMechanism: self.metadata.downloadProviders, - fullPath: fullDestPath, + fullPath: dest, pos: 0, eta: -1, percentDone: 0, @@ -136,7 +131,7 @@ class KaptureURLHandler extends Plugin { hashString: sha1 }; - self.logger.debug('[KaptureURLHandler.lastResort] storing %s of type %s in %s', url, contentType, assumedDest); + self.logger.debug('[KaptureURLHandler.lastResort] storing %s of type %s in %s', url, contentType, dest); request(url) .on('data', function (chunk) { @@ -166,47 +161,42 @@ class KaptureURLHandler extends Plugin { reject(err); }) - .pipe(fs.createWriteStream(fullDestPath)); // send to default + .pipe(fs.createWriteStream(dest)); // send to default }); } // nothing for now - status() { - // TODO: read from store - return this.getState() || []; + status(id) { + return this.getState(id) || []; } + // DEPRECATED + remove(item, fromDisk) { + return this.removeDownloadId(id, fromDisk); + } - remove(item, deleteFromDisk) { - var self = this; - return new Promise(function (resolve, reject) { - try { - var canonical = self.getState(item.id); - if (canonical === undefined) { - throw new Error(); - } - } catch (err) { - return reject(new Error('cant find item to delete in store')); - } - try { - if (deleteFromDisk) { - fs.unlinkSync(canonical.fullPath); - } + removeDownloadId(id, fromDisk) { + const self = this; + const infoObj = this.getState(id); - resolve(self.removeState(canonical.id)); - } catch (err) { - return reject(new Error(err.toString())); + return new Promise((resolve, reject) => { + if (infoObj === undefined) { + return reject(new Error('cant find item to delete in store')); } - }) - } + if (!infoObj.isFinished) { + return reject(new Error('download is not finished, wait for completion..')); + } + if (fromDisk) { + fs.unlinkSync(infoObj.fullPath); + } - removeDownloadId(id) { - return Promise.reject(new Error('KaptureURLHandler: removeDownloadId() not yet implemented')); + resolve(self.removeState(infoObj.id)); + }); } @@ -220,7 +210,7 @@ class KaptureURLHandler extends Plugin { } - downloadSlug(slug) { + downloadSlug(slug, where) { return this.url(slug, where); } @@ -232,20 +222,22 @@ class KaptureURLHandler extends Plugin { } - getDestPath(url, mediaPathSetting) { - return path.join( - this.config.getUserSetting( 'downloadPaths.root' ), - this.config.getUserSetting( 'downloadPaths.' + (mediaPathSetting || 'default') ), - this.getFilename( url ) + getDestPath(url, dest) { + return path.resolve( + path.join( + this.config.getUserSetting('downloadPaths.root'), + this.config.getUserSetting('downloadPaths.' + (dest || 'default')), + this.getFilename(url) + ) ); } getFilename(url) { - const urlObj = Url.parse( url ); + const urlObj = Url.parse(url); - if( /https?:/.test(urlObj.protocol) ) { - return sanitize( - _.last( urlObj.pathname.split('/') ) // grabs filename of url + if (/https?:/.test(urlObj.protocol)) { + return sanitize( + _.last(urlObj.pathname.split('/')) // grabs filename of url ); } else { return undefined; @@ -253,33 +245,31 @@ class KaptureURLHandler extends Plugin { } - handleMediaType(url, head) { - var cTypeHeader = head.headers['content-type'] || 'text/plain'; + handleMediaType(url, head, dest) { + var cTypeHeader = head.headers['content-type']; var cType = _.first(cTypeHeader.split(';')); - this.logger.debug('content type: %s', cType) - switch (cType) { case 'text/xml': - return this.handleXml(url, head); + return this.handleXml(url, head, dest); default: - return this.handleLastResort(url, head); + return this.handleLastResort(url, head, dest); } } assumeDownloadPathFromCtype(contentType) { - if( /^image\/.*/.test( contentType ) ) { + if (/^image\/.*/.test(contentType)) { return 'photos'; - } else if( /^audio\/.*/.test( contentType ) ) { + } else if (/^audio\/.*/.test(contentType)) { return 'music'; - } else if( /^video\/.*/.test( contentType ) ) { + } else if (/^video\/.*/.test(contentType)) { return 'movies'; - } else if( /^(text|application)\/.*/.test( contentType ) ) { + } else if (/^(text|application)\/.*/.test(contentType)) { return 'default'; - } else { + } else { return 'default'; - } + } } } diff --git a/app/components/plugins/youtube/index.js b/app/components/plugins/youtube/index.js index 07aa2ed..bfeb6d7 100644 --- a/app/components/plugins/youtube/index.js +++ b/app/components/plugins/youtube/index.js @@ -287,8 +287,8 @@ class YoutubeSource extends Plugin { // DEPRECATED - remove(item, deleteFromDisk) { - return this.removeDownloadId(item.id, deleteFromDisk); + remove(item, fromDisk) { + return this.removeDownloadId(item.id, fromDisk); } diff --git a/app/endpoints/getdownload.js b/app/endpoints/getdownload.js index 0f46e5f..dcb089c 100644 --- a/app/endpoints/getdownload.js +++ b/app/endpoints/getdownload.js @@ -4,7 +4,22 @@ * GET: /api/v1/downloads/{id} * */ + +const plugins = require('../components/plugin_handler'); + exports.handler = function getdownload(req, res, next) { - res.send('getdownload') - next() + const id = req.params.id; + const parsed = Buffer + .from(id, 'base64') + .toString('ascii') + .split(':'); + + const pluginId = parsed[0]; + const entryId = parsed[1]; + + const results = plugins + .getPlugin(pluginId) + .status(entryId) + + return res.status(200).json(results) } diff --git a/app/endpoints/startdownloadfrommethod.js b/app/endpoints/startdownloadfrommethod.js index ada5447..96623e9 100644 --- a/app/endpoints/startdownloadfrommethod.js +++ b/app/endpoints/startdownloadfrommethod.js @@ -5,19 +5,11 @@ * */ const plugins = require('../components/plugin_handler'); -// const config = require('../config'); -// const _ = require('lodash'); exports.handler = function startdownloadfrommethod(req, res, next) { const methodId = req.params.methodId; const slug = Buffer.from(req.params.slug, 'base64').toString('ascii'); - const where = req.query.where || 'default'; - - // const targets = Object.keys(config.getUserSetting().downloadPaths); - - // if(!_.includes(targets, where)) { - // return next(new Error(`${where} is not a valid target path, options: ${targets.join(',')}`)); - // } + const where = req.query.where; plugins .getDownloadMechanismProvider(methodId) diff --git a/app/endpoints/stopdownload.js b/app/endpoints/stopdownload.js index 9fce0f7..40d9cdd 100644 --- a/app/endpoints/stopdownload.js +++ b/app/endpoints/stopdownload.js @@ -11,7 +11,11 @@ exports.handler = function stopdownload(req, res, next) { const id = req.params.id; const fromDisk = req.params.fromDisk || false; - const buf = Buffer.from(id, 'base64').toString('ascii').split(':'); + const buf = Buffer + .from(id, 'base64') + .toString('ascii') + .split(':'); + const downloaderId = buf[0]; const entryId = buf[1]; diff --git a/app/tests/scenarios.json b/app/tests/scenarios.json index f4e6dbc..87f8d23 100644 --- a/app/tests/scenarios.json +++ b/app/tests/scenarios.json @@ -1,425 +1 @@ -{ - "scenarioVersion": "1.1", - "name": "Main Functionality", - "settings": { - "testing": { - "oas2": [] - } - }, - "before": { - "settings": { - "name": "Ensure settings are set properly", - "description": "Tests some basic settings stuff, and ensures that last step allows for all sources to be enabled properly", - "steps": [ - { - "type": "http", - "name": "Get settings", - "input": { - "method": "get", - "url": "{$.env.host}/api/v1/settings" - }, - "after": { - "assertions": [ - { - "target": "output.status", - "op": "eq", - "expected": 200 - }, - { - "target": "output.body.downloadPaths", - "op": "exists" - } - ], - "transforms": [ - { - "target": "$.ctx.settings", - "source": "output.body" - } - ] - } - }, - { - "type": "http", - "name": "Change all settings to something silly", - "input": { - "method": "put", - "url": "{$.env.host}/api/v1/settings", - "headers": { - "Content-Type": "application/json" - }, - "body": "{$.ctx.badSettings}" - }, - "before": { - "script": "\n$.ctx.set('badSettings', {\n downloadPaths: {},\n plugins: {},\n system: {\n name: \"woohoo\"\n },\n userInfo: {}\n}); \n" - }, - "after": { - "script": "const mergedSettings = _.merge($.ctx.get('settings'), $.ctx.get('badSettings'));\n\n\ntests['bad config comes back'] = _.isEqual(output.body.get(), mergedSettings);" - } - }, - { - "type": "http", - "name": "Re-enable the previous settings via PUT", - "input": { - "method": "put", - "url": "{$.env.host}/api/v1/settings", - "body": "{$.ctx.settings}" - } - }, - { - "type": "http", - "name": "Update settings to enable proper plugins for test runs", - "input": { - "method": "patch", - "url": "{$.env.host}/api/v1/settings", - "body": "{\n\t\"plugins\": {\n \t\"com_flexget\": {\n \"enabled\": true\n },\n \t\"com_kapture_url\": {\n \"enabled\": true\n },\n \t\"com_piratebay\": {\n \"enabled\": true\n },\n \t\"com_transmissionbt\": {\n \"enabled\": true\n },\n \t\"com_youtube\": {\n \"enabled\": true\n },\n \t\"info_showrss\": {\n \"enabled\": true\n },\n \t\"tv_trakt\": {\n \"enabled\": true\n }\n }\n}", - "headers": { - "Content-Type": "application/json" - } - }, - "before": {}, - "after": { - "assertions": [ - { - "target": "output.status", - "op": "eq", - "expected": 202 - } - ], - "script": "const enabledKeys = [\n 'plugins.com_flexget.enabled',\n 'plugins.com_kapture_url.enabled',\n 'plugins.com_piratebay.enabled',\n 'plugins.com_transmissionbt.enabled',\n 'plugins.com_youtube.enabled',\n 'plugins.info_showrss.enabled',\n 'plugins.tv_trakt.enabled'\n];\n\nenabledKeys.forEach(function (k) {\n tests[k + ' is enabled'] = output.body.get(k) === true;\n});\n" - } - } - ] - } - }, - "scenarios": { - "series": { - "name": "Verify series functionality works", - "steps": [ - { - "type": "http", - "name": "Search for \"sunny\" with series filter", - "input": { - "method": "get", - "url": "{$$.env.host}/api/v1/search", - "query": { - "q": "sunny", - "filter": "mediaType:series" - }, - "headers": { - "": "" - } - }, - "after": { - "transforms": [ - { - "target": "$.ctx.id", - "source": "output.body[0].id" - }, - { - "target": "$.ctx.slug", - "source": "output.body[0].slug" - }, - { - "target": "$.ctx.sourceId", - "source": "output.body[0].sourceId" - }, - { - "target": "$.ctx.downloadMechanism", - "source": "output.body[0].downloadMechanism" - } - ], - "assertions": [ - { - "target": "output.status", - "op": "eq", - "expected": 200 - } - ], - "script": "var bdy = output.body.get();\n\ntests['1 entry in response'] = bdy.length === 1\ntests['title is always sunny'] = bdy[0].title === \"It's Always Sunny in Philadelphia\"" - } - }, - { - "type": "http", - "name": "Add sunny to download via \"source\" method", - "input": { - "method": "post", - "url": "{$.env.host}/api/v1/downloads/source:{$.ctx.sourceId}/{$.ctx.id}" - }, - "after": { - "assertions": [ - { - "target": "output.status", - "op": "eq", - "expected": 200 - }, - { - "target": "output.body.id", - "op": "eq", - "expected": "{$.ctx.id}" - } - ], - "script": "const bdy = output.body.get();\n\ntests['seen has length > 0'] = Array.isArray(bdy.seen) && bdy.seen.length > 0" - } - }, - { - "type": "http", - "name": "Add sunny via \"method\" method", - "input": { - "method": "post", - "url": "{$.env.host}/api/v1/downloads/method:{$.ctx.downloadMechanism}/{$.ctx.slug}" - } - }, - { - "type": "http", - "name": "Get info about show requested", - "input": { - "method": "get", - "url": "{$.env.host}/api/v1/series/{$.ctx.sourceId}/{$.ctx.id}" - }, - "after": { - "assertions": [ - { - "target": "output.body.id", - "op": "eq", - "expected": "{$.ctx.id}" - } - ] - } - }, - { - "type": "http", - "name": "Ensure that the /series endpoint returns this entry", - "input": { - "method": "get", - "url": "{$.env.host}/api/v1/series" - }, - "after": { - "assertions": [ - { - "target": "output.status", - "op": "eq", - "expected": 200 - }, - { - "target": "output.body[0].id", - "op": "eq", - "expected": "{$.ctx.id}" - }, - { - "target": "output.body", - "op": "length", - "expected": 1 - } - ] - } - }, - { - "type": "http", - "name": "Delete series from autokapture", - "input": { - "method": "delete", - "url": "{$.env.host}/api/v1/series/{$.ctx.sourceId}/{$.ctx.id}" - } - } - ] - }, - "trending": { - "name": "Verify trending functionality", - "steps": [ - { - "type": "http", - "name": "Get trending list", - "input": { - "method": "get", - "url": "{$.env.host}/api/v1/trending" - }, - "after": { - "script": "tests['movies list > 0'] = output.body.get('movies').length > 0\ntests['series list > 0'] = output.body.get('series').length > 0\n", - "transforms": [ - { - "target": "$.ctx.movieEntry", - "source": "output.body.movies[0]" - }, - { - "target": "$.ctx.seriesEntry", - "source": "output.body.series[0]" - } - ] - } - }, - { - "type": "http", - "name": "Get info for top movie", - "input": { - "method": "get", - "url": "{$.env.host}/api/v1/trending/{$.ctx.movieEntry.sourceId}/info/{$.ctx.movieEntry.id}" - }, - "after": { - "assertions": [ - { - "target": "output.status", - "op": "eq", - "expected": 200 - }, - { - "target": "output.body.title", - "op": "exists" - } - ] - } - } - ] - }, - "downloads": { - "name": "Verify download functionality", - "description": "Verifies that we can search, get an ad-hoc download, and see it progress", - "steps": [ - { - "type": "http", - "name": "Verify there are 0 downloads", - "input": { - "method": "get", - "url": "{$.env.host}/api/v1/downloads" - }, - "after": { - "assertions": [ - { - "target": "output.body", - "op": "exists" - } - ], - "script": "tests['verify list is empty'] = output.body.get().length === 0;" - } - }, - { - "type": "http", - "name": "Search and capture some results", - "input": { - "method": "get", - "url": "{$.env.host}/api/v1/search", - "query": { - "q": "happy" - } - }, - "after": { - "script": "const bdy = output.body.get();\n\nconst yt = _.filter(bdy, {sourceId: 'com_youtube'});\nconst tpb = _.filter(bdy, {sourceId: 'com_piratebay'});\n\ntests['youtube results > 0'] = yt.length > 0;\ntests['tpb results > 0'] = tpb.length > 0;\n\n$.ctx.set('yt', yt[0]);\n$.ctx.set('tpb', tpb[0]);\n" - } - }, - { - "type": "http", - "name": "Start download from youtube via method", - "input": { - "method": "post", - "url": "{$.env.host}/api/v1/downloads/method:{$.ctx.yt.downloadMechanism}/{$.ctx.yt.slug}" - } - }, - { - "type": "http", - "name": "Start download from tpb via method", - "input": { - "method": "post", - "url": "{$.env.host}/api/v1/downloads/method:{$.ctx.tpb.downloadMechanism}/{$.ctx.tpb.slug}", - "query": { - "where": "{$.ctx.tpb.mediaType}" - } - } - }, - { - "type": "http", - "name": "Verify there are 2 downloads", - "input": { - "method": "get", - "url": "{$.env.host}/api/v1/downloads" - }, - "after": { - "assertions": [ - { - "target": "output.body", - "op": "exists" - } - ], - "script": "tests['verify list has 2 download entries'] = output.body.get().length === 2;\n\n$.ctx.set('todelete', output.body.get().map(function(e) {\n return e.id;\n}));" - } - }, - { - "type": "http", - "name": "Delete download 1", - "input": { - "method": "delete", - "url": "{$.env.host}/api/v1/downloads/{$.ctx.todelete[0]}", - "query": { - "fromDisk": "true" - } - } - }, - { - "type": "http", - "name": "Delete download 2", - "input": { - "method": "delete", - "url": "{$.env.host}/api/v1/downloads/{$.ctx.todelete[1]}", - "query": { - "fromDisk": "true" - } - } - }, - { - "type": "http", - "name": "Start download from youtube via source", - "input": { - "method": "post", - "url": "{$.env.host}/api/v1/downloads/source:{$.ctx.yt.sourceId}/{$.ctx.yt.id}" - } - }, - { - "type": "http", - "name": "Start download from tpb via source", - "input": { - "method": "post", - "url": "{$.env.host}/api/v1/downloads/source:{$.ctx.tpb.sourceId}/{$.ctx.tpb.id}" - } - }, - { - "type": "http", - "name": "Verify there are 2 downloads", - "input": { - "method": "get", - "url": "{$.env.host}/api/v1/downloads" - }, - "after": { - "assertions": [ - { - "target": "output.body", - "op": "exists" - } - ], - "script": "tests['verify list has 2 download entries'] = output.body.get().length === 2;\n\n$.ctx.set('todelete', output.body.get().map(function(e) {\n return e.id;\n}));" - } - }, - { - "type": "http", - "name": "Delete download 3", - "input": { - "method": "delete", - "url": "{$.env.host}/api/v1/downloads/{$.ctx.todelete[0]}", - "query": { - "fromDisk": "true" - } - } - }, - { - "type": "http", - "name": "Delete download 4", - "input": { - "method": "delete", - "url": "{$.env.host}/api/v1/downloads/{$.ctx.todelete[1]}", - "query": { - "fromDisk": "true" - } - } - } - ] - } - }, - "utilities": {} -} +{"scenarioVersion":"1.1","name":"Main Functionality","settings":{"testing":{"oas2":[]}},"before":{"settings":{"name":"Ensure settings are set properly","description":"Tests some basic settings stuff, and ensures that last step allows for all sources to be enabled properly","steps":[{"type":"http","name":"Get settings","input":{"method":"get","url":"{$.env.host}/api/v1/settings"},"after":{"assertions":[{"target":"output.status","op":"eq","expected":200},{"target":"output.body.downloadPaths","op":"exists"}],"transforms":[{"target":"$.ctx.settings","source":"output.body"}]}},{"type":"http","name":"Change all settings to something silly","input":{"method":"put","url":"{$.env.host}/api/v1/settings","headers":{"Content-Type":"application/json"},"body":"{$.ctx.badSettings}"},"before":{"script":"\n$.ctx.set('badSettings', {\n downloadPaths: {},\n plugins: {},\n system: {\n name: \"woohoo\"\n },\n userInfo: {}\n}); \n"},"after":{"script":"const mergedSettings = _.merge($.ctx.get('settings'), $.ctx.get('badSettings'));\n\n\ntests['bad config comes back'] = _.isEqual(output.body.get(), mergedSettings);"}},{"type":"http","name":"Re-enable the previous settings via PUT","input":{"method":"put","url":"{$.env.host}/api/v1/settings","body":"{$.ctx.settings}"}},{"type":"http","name":"Update settings to enable proper plugins for test runs","input":{"method":"patch","url":"{$.env.host}/api/v1/settings","body":"{\n\t\"plugins\": {\n \t\"com_flexget\": {\n \"enabled\": true\n },\n \t\"com_kapture_url\": {\n \"enabled\": true\n },\n \t\"com_piratebay\": {\n \"enabled\": true\n },\n \t\"com_transmissionbt\": {\n \"enabled\": true\n },\n \t\"com_youtube\": {\n \"enabled\": true\n },\n \t\"info_showrss\": {\n \"enabled\": true\n },\n \t\"tv_trakt\": {\n \"enabled\": true\n }\n }\n}","headers":{"Content-Type":"application/json"}},"before":{},"after":{"assertions":[{"target":"output.status","op":"eq","expected":202}],"script":"const enabledKeys = [\n 'plugins.com_flexget.enabled',\n 'plugins.com_kapture_url.enabled',\n 'plugins.com_piratebay.enabled',\n 'plugins.com_transmissionbt.enabled',\n 'plugins.com_youtube.enabled',\n 'plugins.info_showrss.enabled',\n 'plugins.tv_trakt.enabled'\n];\n\nenabledKeys.forEach(function (k) {\n tests[k + ' is enabled'] = output.body.get(k) === true;\n});\n"}}]}},"scenarios":{"series":{"name":"Verify series functionality works","steps":[{"type":"http","name":"Search for \"sunny\" with series filter","input":{"method":"get","url":"{$$.env.host}/api/v1/search","query":{"q":"sunny","filter":"mediaType:series"},"headers":{"":""}},"after":{"transforms":[{"target":"$.ctx.id","source":"output.body[0].id"},{"target":"$.ctx.slug","source":"output.body[0].slug"},{"target":"$.ctx.sourceId","source":"output.body[0].sourceId"},{"target":"$.ctx.downloadMechanism","source":"output.body[0].downloadMechanism"}],"assertions":[{"target":"output.status","op":"eq","expected":200}],"script":"var bdy = output.body.get();\n\ntests['1 entry in response'] = bdy.length === 1\ntests['title is always sunny'] = bdy[0].title === \"It's Always Sunny in Philadelphia\""}},{"type":"http","name":"Add sunny to download via \"source\" method","input":{"method":"post","url":"{$.env.host}/api/v1/downloads/source:{$.ctx.sourceId}/{$.ctx.id}"},"after":{"assertions":[{"target":"output.status","op":"eq","expected":200},{"target":"output.body.id","op":"eq","expected":"{$.ctx.id}"}],"script":"const bdy = output.body.get();\n\ntests['seen has length > 0'] = Array.isArray(bdy.seen) && bdy.seen.length > 0"}},{"type":"http","name":"Add sunny via \"method\" method","input":{"method":"post","url":"{$.env.host}/api/v1/downloads/method:{$.ctx.downloadMechanism}/{$.ctx.slug}"}},{"type":"http","name":"Get info about show requested","input":{"method":"get","url":"{$.env.host}/api/v1/series/{$.ctx.sourceId}/{$.ctx.id}"},"after":{"assertions":[{"target":"output.body.id","op":"eq","expected":"{$.ctx.id}"}]}},{"type":"http","name":"Ensure that the /series endpoint returns this entry","input":{"method":"get","url":"{$.env.host}/api/v1/series"},"after":{"assertions":[{"target":"output.status","op":"eq","expected":200},{"target":"output.body[0].id","op":"eq","expected":"{$.ctx.id}"},{"target":"output.body","op":"length","expected":1}]}},{"type":"http","name":"Delete series from autokapture","input":{"method":"delete","url":"{$.env.host}/api/v1/series/{$.ctx.sourceId}/{$.ctx.id}"}}]},"trending":{"name":"Verify trending functionality","steps":[{"type":"http","name":"Get trending list","input":{"method":"get","url":"{$.env.host}/api/v1/trending"},"after":{"script":"tests['movies list > 0'] = output.body.get('movies').length > 0\ntests['series list > 0'] = output.body.get('series').length > 0\n","transforms":[{"target":"$.ctx.movieEntry","source":"output.body.movies[0]"},{"target":"$.ctx.seriesEntry","source":"output.body.series[0]"}]}},{"type":"http","name":"Get info for top movie","input":{"method":"get","url":"{$.env.host}/api/v1/trending/{$.ctx.movieEntry.sourceId}/info/{$.ctx.movieEntry.id}"},"after":{"assertions":[{"target":"output.status","op":"eq","expected":200},{"target":"output.body.title","op":"exists"}]}}]},"downloads":{"name":"Verify download functionality","description":"Verifies that we can search, get an ad-hoc download, and see it progress","steps":[{"type":"http","name":"Verify there are 0 downloads","input":{"method":"get","url":"{$.env.host}/api/v1/downloads"},"after":{"assertions":[{"target":"output.body","op":"exists"}],"script":"tests['verify list is empty'] = output.body.get().length === 0;"}},{"type":"http","name":"Search and capture some results","input":{"method":"get","url":"{$.env.host}/api/v1/search","query":{"q":"happy"}},"after":{"script":"const bdy = output.body.get();\n\nconst yt = _.filter(bdy, {sourceId: 'com_youtube'});\nconst tpb = _.filter(bdy, {sourceId: 'com_piratebay'});\n\ntests['youtube results > 0'] = yt.length > 0;\ntests['tpb results > 0'] = tpb.length > 0;\n\n$.ctx.set('yt', yt[0]);\n$.ctx.set('tpb', tpb[0]);\n"}},{"type":"http","name":"Start download from youtube via method","input":{"method":"post","url":"{$.env.host}/api/v1/downloads/method:{$.ctx.yt.downloadMechanism}/{$.ctx.yt.slug}"}},{"type":"http","name":"Start download from tpb via method","input":{"method":"post","url":"{$.env.host}/api/v1/downloads/method:{$.ctx.tpb.downloadMechanism}/{$.ctx.tpb.slug}","query":{"where":"{$.ctx.tpb.mediaType}"}}},{"type":"http","name":"Verify there are 2 downloads","input":{"method":"get","url":"{$.env.host}/api/v1/downloads"},"after":{"assertions":[{"target":"output.body","op":"exists"}],"script":"tests['verify list has 2 download entries'] = output.body.get().length === 2;\n\n$.ctx.set('todelete', output.body.get().map(function(e) {\n return e.id;\n}));"}},{"type":"http","name":"Delete download 1","input":{"method":"delete","url":"{$.env.host}/api/v1/downloads/{$.ctx.todelete[0]}","query":{"fromDisk":"true"}}},{"type":"http","name":"Delete download 2","input":{"method":"delete","url":"{$.env.host}/api/v1/downloads/{$.ctx.todelete[1]}","query":{"fromDisk":"true"}}},{"type":"http","name":"Start download from youtube via source","input":{"method":"post","url":"{$.env.host}/api/v1/downloads/source:{$.ctx.yt.sourceId}/{$.ctx.yt.id}"}},{"type":"http","name":"Start download from tpb via source","input":{"method":"post","url":"{$.env.host}/api/v1/downloads/source:{$.ctx.tpb.sourceId}/{$.ctx.tpb.id}"}},{"type":"http","name":"Verify there are 2 downloads","input":{"method":"get","url":"{$.env.host}/api/v1/downloads"},"after":{"assertions":[{"target":"output.body","op":"exists"}],"script":"tests['verify list has 2 download entries'] = output.body.get().length === 2;\n\n$.ctx.set('todelete', output.body.get().map(function(e) {\n return e.id;\n}));"}},{"type":"http","name":"Delete download 3","input":{"method":"delete","url":"{$.env.host}/api/v1/downloads/{$.ctx.todelete[0]}","query":{"fromDisk":"true"}}},{"type":"http","name":"Delete download 4","input":{"method":"delete","url":"{$.env.host}/api/v1/downloads/{$.ctx.todelete[1]}","query":{"fromDisk":"true"}}}]},"url":{"name":"Verify URL functionality","steps":[{"type":"http","input":{"method":"post","url":"{$.env.host}/api/v1/downloads/method:url/{$.ctx.videoUrl}"},"name":"Try video file","before":{"script":"const videoUrl = \"http://kapturebox.com/video/explainer.webm\"; // video/webm\nconst imageUrl = \"http://kapturebox.com/img/logo2.png\"; // image/png\nconst musicUrl = \"https://freemusicarchive.org/music/download/69f6cb3d0a235db46bb69186ef634b9da5d43bfd\"; // audio/mpeg\n\nconst videoUrlEnc = Buffer.from(videoUrl).toString('base64');\nconst imageUrlEnc = Buffer.from(imageUrl).toString('base64');\nconst musicUrlEnc = Buffer.from(musicUrl).toString('base64');\n\n$.ctx.set('videoUrl', videoUrlEnc);\n$.ctx.set('imageUrl', imageUrlEnc);\n$.ctx.set('musicUrl', musicUrlEnc);"},"after":{"assertions":[{"target":"output.status","op":"eq","expected":200},{"target":"output.body.destName","op":"eq","expected":"movies"},{"target":"output.body.contentType","op":"eq","expected":"video/webm"}]}},{"type":"http","input":{"method":"post","url":"{$.env.host}/api/v1/downloads/method:url/{$.ctx.videoUrl}","query":{"where":"shows"}},"name":"Try video file with dest override","before":{},"after":{"assertions":[{"target":"output.status","op":"eq","expected":200},{"target":"output.body.destName","op":"eq","expected":"shows"},{"target":"output.body.contentType","op":"eq","expected":"video/webm"},{"target":"output.body.fullDestPath","op":"contains","expected":"/tvshows/"}]}},{"type":"http","input":{"method":"post","url":"{$.env.host}/api/v1/downloads/method:url/{$.ctx.imageUrl}"},"name":"Try image file","before":{},"after":{"assertions":[{"target":"output.status","op":"eq","expected":200},{"target":"output.body.destName","op":"eq","expected":"photos"},{"target":"output.body.fullDestPath","op":"contains","expected":"/photos/"},{"target":"output.body.contentType","op":"eq","expected":"image/png"}]}},{"type":"http","input":{"method":"post","url":"{$.env.host}/api/v1/downloads/method:url/{$.ctx.musicUrl}"},"name":"Try music file","before":{},"after":{"assertions":[{"target":"output.status","op":"eq","expected":200},{"target":"output.body.destName","op":"eq","expected":"music"},{"target":"output.body.contentType","op":"eq","expected":"audio/mpeg"},{"target":"output.body.fullDestPath","op":"contains","expected":"/music/"}]}},{"type":"http","name":"Make sure downloads exist","input":{"method":"get","url":"{$.env.host}/api/v1/downloads"},"after":{"assertions":[{"target":"output.status","op":"eq","expected":200},{"target":"output.body","op":"length","expected":4}],"script":"const bdy = output.body.get();\nconst ids = bdy.map(function(e) {return e.id});\n\n$.ctx.set('todelete', ids);"},"before":{"script":"SL.sleep(5000);"}},{"type":"http","name":"Remove download 1","input":{"method":"delete","url":"{$.env.host}/api/v1/downloads/{$.ctx.todelete[0]}"}},{"type":"http","name":"Remove download 2","input":{"method":"delete","url":"{$.env.host}/api/v1/downloads/{$.ctx.todelete[1]}"}},{"type":"http","name":"Remove download 3","input":{"method":"delete","url":"{$.env.host}/api/v1/downloads/{$.ctx.todelete[2]}"}},{"type":"http","name":"Remove download 4","input":{"method":"delete","url":"{$.env.host}/api/v1/downloads/{$.ctx.todelete[3]}"}}],"description":"Checks that the url plugin works"}},"utilities":{}} \ No newline at end of file From 799906a14a67e08dc80c1e2d45bb63b6c706eee5 Mon Sep 17 00:00:00 2001 From: Evin Callahan Date: Thu, 14 Jun 2018 18:06:26 -0500 Subject: [PATCH 5/8] format json for easier reading and diffing --- app/tests/scenarios.json | 622 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 621 insertions(+), 1 deletion(-) diff --git a/app/tests/scenarios.json b/app/tests/scenarios.json index 87f8d23..6907f09 100644 --- a/app/tests/scenarios.json +++ b/app/tests/scenarios.json @@ -1 +1,621 @@ -{"scenarioVersion":"1.1","name":"Main Functionality","settings":{"testing":{"oas2":[]}},"before":{"settings":{"name":"Ensure settings are set properly","description":"Tests some basic settings stuff, and ensures that last step allows for all sources to be enabled properly","steps":[{"type":"http","name":"Get settings","input":{"method":"get","url":"{$.env.host}/api/v1/settings"},"after":{"assertions":[{"target":"output.status","op":"eq","expected":200},{"target":"output.body.downloadPaths","op":"exists"}],"transforms":[{"target":"$.ctx.settings","source":"output.body"}]}},{"type":"http","name":"Change all settings to something silly","input":{"method":"put","url":"{$.env.host}/api/v1/settings","headers":{"Content-Type":"application/json"},"body":"{$.ctx.badSettings}"},"before":{"script":"\n$.ctx.set('badSettings', {\n downloadPaths: {},\n plugins: {},\n system: {\n name: \"woohoo\"\n },\n userInfo: {}\n}); \n"},"after":{"script":"const mergedSettings = _.merge($.ctx.get('settings'), $.ctx.get('badSettings'));\n\n\ntests['bad config comes back'] = _.isEqual(output.body.get(), mergedSettings);"}},{"type":"http","name":"Re-enable the previous settings via PUT","input":{"method":"put","url":"{$.env.host}/api/v1/settings","body":"{$.ctx.settings}"}},{"type":"http","name":"Update settings to enable proper plugins for test runs","input":{"method":"patch","url":"{$.env.host}/api/v1/settings","body":"{\n\t\"plugins\": {\n \t\"com_flexget\": {\n \"enabled\": true\n },\n \t\"com_kapture_url\": {\n \"enabled\": true\n },\n \t\"com_piratebay\": {\n \"enabled\": true\n },\n \t\"com_transmissionbt\": {\n \"enabled\": true\n },\n \t\"com_youtube\": {\n \"enabled\": true\n },\n \t\"info_showrss\": {\n \"enabled\": true\n },\n \t\"tv_trakt\": {\n \"enabled\": true\n }\n }\n}","headers":{"Content-Type":"application/json"}},"before":{},"after":{"assertions":[{"target":"output.status","op":"eq","expected":202}],"script":"const enabledKeys = [\n 'plugins.com_flexget.enabled',\n 'plugins.com_kapture_url.enabled',\n 'plugins.com_piratebay.enabled',\n 'plugins.com_transmissionbt.enabled',\n 'plugins.com_youtube.enabled',\n 'plugins.info_showrss.enabled',\n 'plugins.tv_trakt.enabled'\n];\n\nenabledKeys.forEach(function (k) {\n tests[k + ' is enabled'] = output.body.get(k) === true;\n});\n"}}]}},"scenarios":{"series":{"name":"Verify series functionality works","steps":[{"type":"http","name":"Search for \"sunny\" with series filter","input":{"method":"get","url":"{$$.env.host}/api/v1/search","query":{"q":"sunny","filter":"mediaType:series"},"headers":{"":""}},"after":{"transforms":[{"target":"$.ctx.id","source":"output.body[0].id"},{"target":"$.ctx.slug","source":"output.body[0].slug"},{"target":"$.ctx.sourceId","source":"output.body[0].sourceId"},{"target":"$.ctx.downloadMechanism","source":"output.body[0].downloadMechanism"}],"assertions":[{"target":"output.status","op":"eq","expected":200}],"script":"var bdy = output.body.get();\n\ntests['1 entry in response'] = bdy.length === 1\ntests['title is always sunny'] = bdy[0].title === \"It's Always Sunny in Philadelphia\""}},{"type":"http","name":"Add sunny to download via \"source\" method","input":{"method":"post","url":"{$.env.host}/api/v1/downloads/source:{$.ctx.sourceId}/{$.ctx.id}"},"after":{"assertions":[{"target":"output.status","op":"eq","expected":200},{"target":"output.body.id","op":"eq","expected":"{$.ctx.id}"}],"script":"const bdy = output.body.get();\n\ntests['seen has length > 0'] = Array.isArray(bdy.seen) && bdy.seen.length > 0"}},{"type":"http","name":"Add sunny via \"method\" method","input":{"method":"post","url":"{$.env.host}/api/v1/downloads/method:{$.ctx.downloadMechanism}/{$.ctx.slug}"}},{"type":"http","name":"Get info about show requested","input":{"method":"get","url":"{$.env.host}/api/v1/series/{$.ctx.sourceId}/{$.ctx.id}"},"after":{"assertions":[{"target":"output.body.id","op":"eq","expected":"{$.ctx.id}"}]}},{"type":"http","name":"Ensure that the /series endpoint returns this entry","input":{"method":"get","url":"{$.env.host}/api/v1/series"},"after":{"assertions":[{"target":"output.status","op":"eq","expected":200},{"target":"output.body[0].id","op":"eq","expected":"{$.ctx.id}"},{"target":"output.body","op":"length","expected":1}]}},{"type":"http","name":"Delete series from autokapture","input":{"method":"delete","url":"{$.env.host}/api/v1/series/{$.ctx.sourceId}/{$.ctx.id}"}}]},"trending":{"name":"Verify trending functionality","steps":[{"type":"http","name":"Get trending list","input":{"method":"get","url":"{$.env.host}/api/v1/trending"},"after":{"script":"tests['movies list > 0'] = output.body.get('movies').length > 0\ntests['series list > 0'] = output.body.get('series').length > 0\n","transforms":[{"target":"$.ctx.movieEntry","source":"output.body.movies[0]"},{"target":"$.ctx.seriesEntry","source":"output.body.series[0]"}]}},{"type":"http","name":"Get info for top movie","input":{"method":"get","url":"{$.env.host}/api/v1/trending/{$.ctx.movieEntry.sourceId}/info/{$.ctx.movieEntry.id}"},"after":{"assertions":[{"target":"output.status","op":"eq","expected":200},{"target":"output.body.title","op":"exists"}]}}]},"downloads":{"name":"Verify download functionality","description":"Verifies that we can search, get an ad-hoc download, and see it progress","steps":[{"type":"http","name":"Verify there are 0 downloads","input":{"method":"get","url":"{$.env.host}/api/v1/downloads"},"after":{"assertions":[{"target":"output.body","op":"exists"}],"script":"tests['verify list is empty'] = output.body.get().length === 0;"}},{"type":"http","name":"Search and capture some results","input":{"method":"get","url":"{$.env.host}/api/v1/search","query":{"q":"happy"}},"after":{"script":"const bdy = output.body.get();\n\nconst yt = _.filter(bdy, {sourceId: 'com_youtube'});\nconst tpb = _.filter(bdy, {sourceId: 'com_piratebay'});\n\ntests['youtube results > 0'] = yt.length > 0;\ntests['tpb results > 0'] = tpb.length > 0;\n\n$.ctx.set('yt', yt[0]);\n$.ctx.set('tpb', tpb[0]);\n"}},{"type":"http","name":"Start download from youtube via method","input":{"method":"post","url":"{$.env.host}/api/v1/downloads/method:{$.ctx.yt.downloadMechanism}/{$.ctx.yt.slug}"}},{"type":"http","name":"Start download from tpb via method","input":{"method":"post","url":"{$.env.host}/api/v1/downloads/method:{$.ctx.tpb.downloadMechanism}/{$.ctx.tpb.slug}","query":{"where":"{$.ctx.tpb.mediaType}"}}},{"type":"http","name":"Verify there are 2 downloads","input":{"method":"get","url":"{$.env.host}/api/v1/downloads"},"after":{"assertions":[{"target":"output.body","op":"exists"}],"script":"tests['verify list has 2 download entries'] = output.body.get().length === 2;\n\n$.ctx.set('todelete', output.body.get().map(function(e) {\n return e.id;\n}));"}},{"type":"http","name":"Delete download 1","input":{"method":"delete","url":"{$.env.host}/api/v1/downloads/{$.ctx.todelete[0]}","query":{"fromDisk":"true"}}},{"type":"http","name":"Delete download 2","input":{"method":"delete","url":"{$.env.host}/api/v1/downloads/{$.ctx.todelete[1]}","query":{"fromDisk":"true"}}},{"type":"http","name":"Start download from youtube via source","input":{"method":"post","url":"{$.env.host}/api/v1/downloads/source:{$.ctx.yt.sourceId}/{$.ctx.yt.id}"}},{"type":"http","name":"Start download from tpb via source","input":{"method":"post","url":"{$.env.host}/api/v1/downloads/source:{$.ctx.tpb.sourceId}/{$.ctx.tpb.id}"}},{"type":"http","name":"Verify there are 2 downloads","input":{"method":"get","url":"{$.env.host}/api/v1/downloads"},"after":{"assertions":[{"target":"output.body","op":"exists"}],"script":"tests['verify list has 2 download entries'] = output.body.get().length === 2;\n\n$.ctx.set('todelete', output.body.get().map(function(e) {\n return e.id;\n}));"}},{"type":"http","name":"Delete download 3","input":{"method":"delete","url":"{$.env.host}/api/v1/downloads/{$.ctx.todelete[0]}","query":{"fromDisk":"true"}}},{"type":"http","name":"Delete download 4","input":{"method":"delete","url":"{$.env.host}/api/v1/downloads/{$.ctx.todelete[1]}","query":{"fromDisk":"true"}}}]},"url":{"name":"Verify URL functionality","steps":[{"type":"http","input":{"method":"post","url":"{$.env.host}/api/v1/downloads/method:url/{$.ctx.videoUrl}"},"name":"Try video file","before":{"script":"const videoUrl = \"http://kapturebox.com/video/explainer.webm\"; // video/webm\nconst imageUrl = \"http://kapturebox.com/img/logo2.png\"; // image/png\nconst musicUrl = \"https://freemusicarchive.org/music/download/69f6cb3d0a235db46bb69186ef634b9da5d43bfd\"; // audio/mpeg\n\nconst videoUrlEnc = Buffer.from(videoUrl).toString('base64');\nconst imageUrlEnc = Buffer.from(imageUrl).toString('base64');\nconst musicUrlEnc = Buffer.from(musicUrl).toString('base64');\n\n$.ctx.set('videoUrl', videoUrlEnc);\n$.ctx.set('imageUrl', imageUrlEnc);\n$.ctx.set('musicUrl', musicUrlEnc);"},"after":{"assertions":[{"target":"output.status","op":"eq","expected":200},{"target":"output.body.destName","op":"eq","expected":"movies"},{"target":"output.body.contentType","op":"eq","expected":"video/webm"}]}},{"type":"http","input":{"method":"post","url":"{$.env.host}/api/v1/downloads/method:url/{$.ctx.videoUrl}","query":{"where":"shows"}},"name":"Try video file with dest override","before":{},"after":{"assertions":[{"target":"output.status","op":"eq","expected":200},{"target":"output.body.destName","op":"eq","expected":"shows"},{"target":"output.body.contentType","op":"eq","expected":"video/webm"},{"target":"output.body.fullDestPath","op":"contains","expected":"/tvshows/"}]}},{"type":"http","input":{"method":"post","url":"{$.env.host}/api/v1/downloads/method:url/{$.ctx.imageUrl}"},"name":"Try image file","before":{},"after":{"assertions":[{"target":"output.status","op":"eq","expected":200},{"target":"output.body.destName","op":"eq","expected":"photos"},{"target":"output.body.fullDestPath","op":"contains","expected":"/photos/"},{"target":"output.body.contentType","op":"eq","expected":"image/png"}]}},{"type":"http","input":{"method":"post","url":"{$.env.host}/api/v1/downloads/method:url/{$.ctx.musicUrl}"},"name":"Try music file","before":{},"after":{"assertions":[{"target":"output.status","op":"eq","expected":200},{"target":"output.body.destName","op":"eq","expected":"music"},{"target":"output.body.contentType","op":"eq","expected":"audio/mpeg"},{"target":"output.body.fullDestPath","op":"contains","expected":"/music/"}]}},{"type":"http","name":"Make sure downloads exist","input":{"method":"get","url":"{$.env.host}/api/v1/downloads"},"after":{"assertions":[{"target":"output.status","op":"eq","expected":200},{"target":"output.body","op":"length","expected":4}],"script":"const bdy = output.body.get();\nconst ids = bdy.map(function(e) {return e.id});\n\n$.ctx.set('todelete', ids);"},"before":{"script":"SL.sleep(5000);"}},{"type":"http","name":"Remove download 1","input":{"method":"delete","url":"{$.env.host}/api/v1/downloads/{$.ctx.todelete[0]}"}},{"type":"http","name":"Remove download 2","input":{"method":"delete","url":"{$.env.host}/api/v1/downloads/{$.ctx.todelete[1]}"}},{"type":"http","name":"Remove download 3","input":{"method":"delete","url":"{$.env.host}/api/v1/downloads/{$.ctx.todelete[2]}"}},{"type":"http","name":"Remove download 4","input":{"method":"delete","url":"{$.env.host}/api/v1/downloads/{$.ctx.todelete[3]}"}}],"description":"Checks that the url plugin works"}},"utilities":{}} \ No newline at end of file +{ + "scenarioVersion": "1.1", + "name": "Main Functionality", + "settings": { + "testing": { + "oas2": [] + } + }, + "before": { + "settings": { + "name": "Ensure settings are set properly", + "description": "Tests some basic settings stuff, and ensures that last step allows for all sources to be enabled properly", + "steps": [ + { + "type": "http", + "name": "Get settings", + "input": { + "method": "get", + "url": "{$.env.host}/api/v1/settings" + }, + "after": { + "assertions": [ + { + "target": "output.status", + "op": "eq", + "expected": 200 + }, + { + "target": "output.body.downloadPaths", + "op": "exists" + } + ], + "transforms": [ + { + "target": "$.ctx.settings", + "source": "output.body" + } + ] + } + }, + { + "type": "http", + "name": "Change all settings to something silly", + "input": { + "method": "put", + "url": "{$.env.host}/api/v1/settings", + "headers": { + "Content-Type": "application/json" + }, + "body": "{$.ctx.badSettings}" + }, + "before": { + "script": "\n$.ctx.set('badSettings', {\n downloadPaths: {},\n plugins: {},\n system: {\n name: \"woohoo\"\n },\n userInfo: {}\n}); \n" + }, + "after": { + "script": "const mergedSettings = _.merge($.ctx.get('settings'), $.ctx.get('badSettings'));\n\n\ntests['bad config comes back'] = _.isEqual(output.body.get(), mergedSettings);" + } + }, + { + "type": "http", + "name": "Re-enable the previous settings via PUT", + "input": { + "method": "put", + "url": "{$.env.host}/api/v1/settings", + "body": "{$.ctx.settings}" + } + }, + { + "type": "http", + "name": "Update settings to enable proper plugins for test runs", + "input": { + "method": "patch", + "url": "{$.env.host}/api/v1/settings", + "body": "{\n\t\"plugins\": {\n \t\"com_flexget\": {\n \"enabled\": true\n },\n \t\"com_kapture_url\": {\n \"enabled\": true\n },\n \t\"com_piratebay\": {\n \"enabled\": true\n },\n \t\"com_transmissionbt\": {\n \"enabled\": true\n },\n \t\"com_youtube\": {\n \"enabled\": true\n },\n \t\"info_showrss\": {\n \"enabled\": true\n },\n \t\"tv_trakt\": {\n \"enabled\": true\n }\n }\n}", + "headers": { + "Content-Type": "application/json" + } + }, + "before": {}, + "after": { + "assertions": [ + { + "target": "output.status", + "op": "eq", + "expected": 202 + } + ], + "script": "const enabledKeys = [\n 'plugins.com_flexget.enabled',\n 'plugins.com_kapture_url.enabled',\n 'plugins.com_piratebay.enabled',\n 'plugins.com_transmissionbt.enabled',\n 'plugins.com_youtube.enabled',\n 'plugins.info_showrss.enabled',\n 'plugins.tv_trakt.enabled'\n];\n\nenabledKeys.forEach(function (k) {\n tests[k + ' is enabled'] = output.body.get(k) === true;\n});\n" + } + } + ] + } + }, + "scenarios": { + "series": { + "name": "Verify series functionality works", + "steps": [ + { + "type": "http", + "name": "Search for \"sunny\" with series filter", + "input": { + "method": "get", + "url": "{$$.env.host}/api/v1/search", + "query": { + "q": "sunny", + "filter": "mediaType:series" + }, + "headers": { + "": "" + } + }, + "after": { + "transforms": [ + { + "target": "$.ctx.id", + "source": "output.body[0].id" + }, + { + "target": "$.ctx.slug", + "source": "output.body[0].slug" + }, + { + "target": "$.ctx.sourceId", + "source": "output.body[0].sourceId" + }, + { + "target": "$.ctx.downloadMechanism", + "source": "output.body[0].downloadMechanism" + } + ], + "assertions": [ + { + "target": "output.status", + "op": "eq", + "expected": 200 + } + ], + "script": "var bdy = output.body.get();\n\ntests['1 entry in response'] = bdy.length === 1\ntests['title is always sunny'] = bdy[0].title === \"It's Always Sunny in Philadelphia\"" + } + }, + { + "type": "http", + "name": "Add sunny to download via \"source\" method", + "input": { + "method": "post", + "url": "{$.env.host}/api/v1/downloads/source:{$.ctx.sourceId}/{$.ctx.id}" + }, + "after": { + "assertions": [ + { + "target": "output.status", + "op": "eq", + "expected": 200 + }, + { + "target": "output.body.id", + "op": "eq", + "expected": "{$.ctx.id}" + } + ], + "script": "const bdy = output.body.get();\n\ntests['seen has length > 0'] = Array.isArray(bdy.seen) && bdy.seen.length > 0" + } + }, + { + "type": "http", + "name": "Add sunny via \"method\" method", + "input": { + "method": "post", + "url": "{$.env.host}/api/v1/downloads/method:{$.ctx.downloadMechanism}/{$.ctx.slug}" + } + }, + { + "type": "http", + "name": "Get info about show requested", + "input": { + "method": "get", + "url": "{$.env.host}/api/v1/series/{$.ctx.sourceId}/{$.ctx.id}" + }, + "after": { + "assertions": [ + { + "target": "output.body.id", + "op": "eq", + "expected": "{$.ctx.id}" + } + ] + } + }, + { + "type": "http", + "name": "Ensure that the /series endpoint returns this entry", + "input": { + "method": "get", + "url": "{$.env.host}/api/v1/series" + }, + "after": { + "assertions": [ + { + "target": "output.status", + "op": "eq", + "expected": 200 + }, + { + "target": "output.body[0].id", + "op": "eq", + "expected": "{$.ctx.id}" + }, + { + "target": "output.body", + "op": "length", + "expected": 1 + } + ] + } + }, + { + "type": "http", + "name": "Delete series from autokapture", + "input": { + "method": "delete", + "url": "{$.env.host}/api/v1/series/{$.ctx.sourceId}/{$.ctx.id}" + } + } + ] + }, + "trending": { + "name": "Verify trending functionality", + "steps": [ + { + "type": "http", + "name": "Get trending list", + "input": { + "method": "get", + "url": "{$.env.host}/api/v1/trending" + }, + "after": { + "script": "tests['movies list > 0'] = output.body.get('movies').length > 0\ntests['series list > 0'] = output.body.get('series').length > 0\n", + "transforms": [ + { + "target": "$.ctx.movieEntry", + "source": "output.body.movies[0]" + }, + { + "target": "$.ctx.seriesEntry", + "source": "output.body.series[0]" + } + ] + } + }, + { + "type": "http", + "name": "Get info for top movie", + "input": { + "method": "get", + "url": "{$.env.host}/api/v1/trending/{$.ctx.movieEntry.sourceId}/info/{$.ctx.movieEntry.id}" + }, + "after": { + "assertions": [ + { + "target": "output.status", + "op": "eq", + "expected": 200 + }, + { + "target": "output.body.title", + "op": "exists" + } + ] + } + } + ] + }, + "downloads": { + "name": "Verify download functionality", + "description": "Verifies that we can search, get an ad-hoc download, and see it progress", + "steps": [ + { + "type": "http", + "name": "Verify there are 0 downloads", + "input": { + "method": "get", + "url": "{$.env.host}/api/v1/downloads" + }, + "after": { + "assertions": [ + { + "target": "output.body", + "op": "exists" + } + ], + "script": "tests['verify list is empty'] = output.body.get().length === 0;" + } + }, + { + "type": "http", + "name": "Search and capture some results", + "input": { + "method": "get", + "url": "{$.env.host}/api/v1/search", + "query": { + "q": "happy" + } + }, + "after": { + "script": "const bdy = output.body.get();\n\nconst yt = _.filter(bdy, {sourceId: 'com_youtube'});\nconst tpb = _.filter(bdy, {sourceId: 'com_piratebay'});\n\ntests['youtube results > 0'] = yt.length > 0;\ntests['tpb results > 0'] = tpb.length > 0;\n\n$.ctx.set('yt', yt[0]);\n$.ctx.set('tpb', tpb[0]);\n" + } + }, + { + "type": "http", + "name": "Start download from youtube via method", + "input": { + "method": "post", + "url": "{$.env.host}/api/v1/downloads/method:{$.ctx.yt.downloadMechanism}/{$.ctx.yt.slug}" + } + }, + { + "type": "http", + "name": "Start download from tpb via method", + "input": { + "method": "post", + "url": "{$.env.host}/api/v1/downloads/method:{$.ctx.tpb.downloadMechanism}/{$.ctx.tpb.slug}", + "query": { + "where": "{$.ctx.tpb.mediaType}" + } + } + }, + { + "type": "http", + "name": "Verify there are 2 downloads", + "input": { + "method": "get", + "url": "{$.env.host}/api/v1/downloads" + }, + "after": { + "assertions": [ + { + "target": "output.body", + "op": "exists" + } + ], + "script": "tests['verify list has 2 download entries'] = output.body.get().length === 2;\n\n$.ctx.set('todelete', output.body.get().map(function(e) {\n return e.id;\n}));" + } + }, + { + "type": "http", + "name": "Delete download 1", + "input": { + "method": "delete", + "url": "{$.env.host}/api/v1/downloads/{$.ctx.todelete[0]}", + "query": { + "fromDisk": "true" + } + } + }, + { + "type": "http", + "name": "Delete download 2", + "input": { + "method": "delete", + "url": "{$.env.host}/api/v1/downloads/{$.ctx.todelete[1]}", + "query": { + "fromDisk": "true" + } + } + }, + { + "type": "http", + "name": "Start download from youtube via source", + "input": { + "method": "post", + "url": "{$.env.host}/api/v1/downloads/source:{$.ctx.yt.sourceId}/{$.ctx.yt.id}" + } + }, + { + "type": "http", + "name": "Start download from tpb via source", + "input": { + "method": "post", + "url": "{$.env.host}/api/v1/downloads/source:{$.ctx.tpb.sourceId}/{$.ctx.tpb.id}" + } + }, + { + "type": "http", + "name": "Verify there are 2 downloads", + "input": { + "method": "get", + "url": "{$.env.host}/api/v1/downloads" + }, + "after": { + "assertions": [ + { + "target": "output.body", + "op": "exists" + } + ], + "script": "tests['verify list has 2 download entries'] = output.body.get().length === 2;\n\n$.ctx.set('todelete', output.body.get().map(function(e) {\n return e.id;\n}));" + } + }, + { + "type": "http", + "name": "Delete download 3", + "input": { + "method": "delete", + "url": "{$.env.host}/api/v1/downloads/{$.ctx.todelete[0]}", + "query": { + "fromDisk": "true" + } + } + }, + { + "type": "http", + "name": "Delete download 4", + "input": { + "method": "delete", + "url": "{$.env.host}/api/v1/downloads/{$.ctx.todelete[1]}", + "query": { + "fromDisk": "true" + } + } + } + ] + }, + "url": { + "name": "Verify URL functionality", + "steps": [ + { + "type": "http", + "input": { + "method": "post", + "url": "{$.env.host}/api/v1/downloads/method:url/{$.ctx.videoUrl}" + }, + "name": "Try video file", + "before": { + "script": "const videoUrl = \"http://kapturebox.com/video/explainer.webm\"; // video/webm\nconst imageUrl = \"http://kapturebox.com/img/logo2.png\"; // image/png\nconst musicUrl = \"https://freemusicarchive.org/music/download/69f6cb3d0a235db46bb69186ef634b9da5d43bfd\"; // audio/mpeg\n\nconst videoUrlEnc = Buffer.from(videoUrl).toString('base64');\nconst imageUrlEnc = Buffer.from(imageUrl).toString('base64');\nconst musicUrlEnc = Buffer.from(musicUrl).toString('base64');\n\n$.ctx.set('videoUrl', videoUrlEnc);\n$.ctx.set('imageUrl', imageUrlEnc);\n$.ctx.set('musicUrl', musicUrlEnc);" + }, + "after": { + "assertions": [ + { + "target": "output.status", + "op": "eq", + "expected": 200 + }, + { + "target": "output.body.destName", + "op": "eq", + "expected": "movies" + }, + { + "target": "output.body.contentType", + "op": "eq", + "expected": "video/webm" + } + ] + } + }, + { + "type": "http", + "input": { + "method": "post", + "url": "{$.env.host}/api/v1/downloads/method:url/{$.ctx.videoUrl}", + "query": { + "where": "shows" + } + }, + "name": "Try video file with dest override", + "before": {}, + "after": { + "assertions": [ + { + "target": "output.status", + "op": "eq", + "expected": 200 + }, + { + "target": "output.body.destName", + "op": "eq", + "expected": "shows" + }, + { + "target": "output.body.contentType", + "op": "eq", + "expected": "video/webm" + }, + { + "target": "output.body.fullDestPath", + "op": "contains", + "expected": "/tvshows/" + } + ] + } + }, + { + "type": "http", + "input": { + "method": "post", + "url": "{$.env.host}/api/v1/downloads/method:url/{$.ctx.imageUrl}" + }, + "name": "Try image file", + "before": {}, + "after": { + "assertions": [ + { + "target": "output.status", + "op": "eq", + "expected": 200 + }, + { + "target": "output.body.destName", + "op": "eq", + "expected": "photos" + }, + { + "target": "output.body.fullDestPath", + "op": "contains", + "expected": "/photos/" + }, + { + "target": "output.body.contentType", + "op": "eq", + "expected": "image/png" + } + ] + } + }, + { + "type": "http", + "input": { + "method": "post", + "url": "{$.env.host}/api/v1/downloads/method:url/{$.ctx.musicUrl}" + }, + "name": "Try music file", + "before": {}, + "after": { + "assertions": [ + { + "target": "output.status", + "op": "eq", + "expected": 200 + }, + { + "target": "output.body.destName", + "op": "eq", + "expected": "music" + }, + { + "target": "output.body.contentType", + "op": "eq", + "expected": "audio/mpeg" + }, + { + "target": "output.body.fullDestPath", + "op": "contains", + "expected": "/music/" + } + ] + } + }, + { + "type": "http", + "name": "Make sure downloads exist", + "input": { + "method": "get", + "url": "{$.env.host}/api/v1/downloads" + }, + "after": { + "assertions": [ + { + "target": "output.status", + "op": "eq", + "expected": 200 + }, + { + "target": "output.body", + "op": "length", + "expected": 4 + } + ], + "script": "const bdy = output.body.get();\nconst ids = bdy.map(function(e) {return e.id});\n\n$.ctx.set('todelete', ids);" + }, + "before": { + "script": "SL.sleep(5000);" + } + }, + { + "type": "http", + "name": "Remove download 1", + "input": { + "method": "delete", + "url": "{$.env.host}/api/v1/downloads/{$.ctx.todelete[0]}" + } + }, + { + "type": "http", + "name": "Remove download 2", + "input": { + "method": "delete", + "url": "{$.env.host}/api/v1/downloads/{$.ctx.todelete[1]}" + } + }, + { + "type": "http", + "name": "Remove download 3", + "input": { + "method": "delete", + "url": "{$.env.host}/api/v1/downloads/{$.ctx.todelete[2]}" + } + }, + { + "type": "http", + "name": "Remove download 4", + "input": { + "method": "delete", + "url": "{$.env.host}/api/v1/downloads/{$.ctx.todelete[3]}" + } + } + ], + "description": "Checks that the url plugin works" + } + }, + "utilities": {} +} From b4228d8c7c8ee33b2fc846ca4d56ecdd8805d05b Mon Sep 17 00:00:00 2001 From: Evin Callahan Date: Sun, 17 Jun 2018 19:50:24 -0500 Subject: [PATCH 6/8] add upload ability --- app/components/dest.js | 28 ++++++ app/components/plugin_handler/plugins.js | 3 + app/components/plugins/upload/index.js | 109 +++++++++++++++++++++++ app/components/plugins/url/index.js | 4 +- app/endpoints/getuploads.js | 10 ++- app/endpoints/newupload.js | 27 +++++- app/express.js | 5 +- app/oas/main.oas2.yml | 31 ++++--- app/tests/uploadtest.html | 12 +++ package.json | 2 + yarn.lock | 91 ++++++++++++++++--- 11 files changed, 289 insertions(+), 33 deletions(-) create mode 100644 app/components/dest.js create mode 100644 app/components/plugins/upload/index.js create mode 100644 app/tests/uploadtest.html diff --git a/app/components/dest.js b/app/components/dest.js new file mode 100644 index 0000000..bcf7fef --- /dev/null +++ b/app/components/dest.js @@ -0,0 +1,28 @@ +const _ = require('lodash'); +const config = require('../config'); +const path = require('path'); + +const WHERES = { + music: 'downloadPaths.music', + photo: 'downloadPaths.photos', + photos: 'downloadPaths.photos', + tvshows: 'downloadPaths.shows', + tvshow: 'downloadPaths.shows', + series: 'downloadPaths.shows', + movie: 'downloadPaths.movies', + movies: 'downloadPaths.movies', + other: 'downloadPaths.default', + default: 'downloadPaths.default', + auto: 'downloadPaths.default' +}; + +exports.determineDest = function(where) { + if(!_.includes(Object.keys(WHERES), where)) { + throw new Error(`key ${where} not valid target location`); + } + + let subpath = config.get(WHERES[where]); + let fullbase = path.join(config.get('downloadPaths.root'), subpath); + + return path.resolve(fullbase); +} diff --git a/app/components/plugin_handler/plugins.js b/app/components/plugin_handler/plugins.js index 629dc99..cbe9174 100644 --- a/app/components/plugin_handler/plugins.js +++ b/app/components/plugin_handler/plugins.js @@ -21,6 +21,9 @@ const PLUGIN_REQUIREMENTS = { }, series: { // ie showrss functions: ['enableId', 'disableId', 'getEnabledSeries'] + }, + uploader: { + functions: ['uploadFile'] } } diff --git a/app/components/plugins/upload/index.js b/app/components/plugins/upload/index.js new file mode 100644 index 0000000..2d4929f --- /dev/null +++ b/app/components/plugins/upload/index.js @@ -0,0 +1,109 @@ +const crypto = require('crypto'); +const path = require('path'); +const prettyBytes = require('pretty-bytes'); + +const dest = require('../../dest'); +const Plugin = require('../../plugin_handler/base'); + +// standard plugin metadata, and some additional flexget properties +class UploadHandler extends Plugin { + constructor() { + const metadata = { + pluginId: 'com_kapturebox_uploader', // Unique ID of plugin + pluginName: 'Uploader', // Display name of plugin + pluginTypes: ['downloader', 'uploader'], // 'source', 'downloader', 'player' + link: 'http://kapturebox.com', // Link to provider site + description: 'Uploader plugin' // Description of plugin provider + }; + + const defaultSettings = { + enabled: true, + }; + + super(metadata, defaultSettings); + } + + // we keep this around so that we can add the uploads to the + // 'download' holistic list of all files in system + status() { + // handle ID if passed in optionally + if(arguments[0]) { + return this.getState(arguments[0]); + } else { + return this.getState(); + } + } + + uploadFile(file, where) { + const self = this; + + return new Promise((resolve, reject) => { + const fname = file.name; + const basepath = dest.determineDest(where); + const fullpath = path.join(basepath, fname); + const id = crypto.createHash('sha1') + .update(`${fullpath}`) + .digest('hex'); + + const saveObj = { + id: id, + fullPath: fullpath, + title: fname, + where: where, + contentType: file.mimetype, + size: prettyBytes(file.data.byteLength), + sourceId: self.metadata.sourceId, + sourceName: self.metadata.sourceName + }; + + file.mv(path.resolve(fullpath), function(err) { + if (err) + return reject(err); + + self.setState(id, saveObj); + + return resolve(saveObj); + }); + }); + } + + removeDownloadId(id, fromDisk) { + const self = this; + + return new Promise((resolve,reject) => { + let canonical = self.getState(id); + + if(!canonical) { + let err = new Error(`not found ${id}`); + err.statusCode(404); + throw err; + } + + self.removeState(id); + + if(fromDisk) { + fs.unlink(canonical.fullPath, (err) => { + if(err) { + return reject(err); + } + resolve(canonical); + }); + } else { + resolve(canonical); + } + }); + } + + + + downloadSlug(slug, where) { + return Promise.reject(new Error('KaptureUploadPlugin.downloadSlug not yet implemented')); + } + + + removeSlug(slug) { + return Promise.reject(new Error('KaptureUploadPlugin.removeSlug not yet implemented')); + } +} + +module.exports = UploadHandler; diff --git a/app/components/plugins/url/index.js b/app/components/plugins/url/index.js index a9fdfac..8db3e98 100644 --- a/app/components/plugins/url/index.js +++ b/app/components/plugins/url/index.js @@ -12,9 +12,9 @@ const Plugin = require('../../plugin_handler/base'); class KaptureURLHandler extends Plugin { constructor() { const metadata = { - pluginId: 'com_kapture_url', // Unique ID of plugin + pluginId: 'com_kapturebox_url', // Unique ID of plugin pluginName: 'Kapture URL Handler', // Display name of plugin - pluginTypes: ['downloader'], // 'source', 'downloader', 'player' + pluginTypes: ['downloader'], // 'source', 'downloader', 'player' sourceTypes: 'adhoc', // 'adhoc', 'continuous' link: 'http://kapturebox.com', // Link to provider site downloadProviders: 'url', // if plugin can also download, what diff --git a/app/endpoints/getuploads.js b/app/endpoints/getuploads.js index 7f91641..f441edf 100644 --- a/app/endpoints/getuploads.js +++ b/app/endpoints/getuploads.js @@ -4,7 +4,13 @@ * GET: /api/v1/uploads * */ +const plugins = require('../components/plugin_handler'); + exports.handler = function getuploads(req, res, next) { - res.send('getuploads') - next() + const uploader = plugins.getPlugin('com_kapturebox_uploader'); + + uploader + .status() + .then((results) => res.status(200).json(results)) + .catch(next); } diff --git a/app/endpoints/newupload.js b/app/endpoints/newupload.js index ec2ef44..1dd9a8a 100644 --- a/app/endpoints/newupload.js +++ b/app/endpoints/newupload.js @@ -1,14 +1,33 @@ /** * Uploads a new file * - * POST: /api/v1/uploads + * POST: /api/v1/uploads?where={where} * * formData: * file {file} Binary upload data. - * type {string} Type of the media being uploaded. + * where {string} destination where file should be placed * */ +const plugins = require('../components/plugin_handler'); + +// TODO: This upload module needs tests + exports.handler = function newupload(req, res, next) { - res.send('newupload') - next() + const where = req.query.where || 'default'; + + if (!req.files) { + let err = new Error('no files were uploaded'); + err.statusCode = 400; + return next(err); + } + + const uploader = plugins.getPlugin('com_kapturebox_uploader'); + + const uplPromises = Object.keys(req.files) + .map((k) => uploader.uploadFile(req.files[k], where)); + + Promise + .all(uplPromises) + .then((results) => res.status(202).send(results)) + .catch(next) } diff --git a/app/express.js b/app/express.js index f8b5cb2..9790f0d 100644 --- a/app/express.js +++ b/app/express.js @@ -4,13 +4,11 @@ -const express = require('express'); const compression = require('compression'); const bodyParser = require('body-parser'); const methodOverride = require('method-override'); -const errorHandler = require('errorhandler'); -const path = require('path'); const winstonExpress = require('express-winston'); +const uploader = require('express-fileupload'); const config = require('./config'); @@ -20,6 +18,7 @@ module.exports = function( app ) { app.use(bodyParser.urlencoded({ extended: false })); app.use(bodyParser.json()); app.use(methodOverride()); + app.use(uploader()); app.set('x-powered-by', false); app.set('json spaces', 2); app.use( diff --git a/app/oas/main.oas2.yml b/app/oas/main.oas2.yml index 6958a9a..ed5cbeb 100644 --- a/app/oas/main.oas2.yml +++ b/app/oas/main.oas2.yml @@ -301,17 +301,7 @@ - "Downloading" parameters: - - in: "query" - name: "where" - type: "string" - description: "String of download path where the downloaded file should be placed after completion. If not set, defaults to `default` download path setting" - enum: - - "default" - - "movies" - - "music" - - "photos" - - "shows" - allowEmptyValue: true + $ref: "#/parameters/whereQueryString" parameters: - name: "methodId" @@ -420,10 +410,14 @@ - "auto" required: true default: "other" + - + $ref: "#/parameters/whereQueryString" operationId: "newupload" tags: - "Uploading" description: "Upload a new file to kapture instance. This should be uploaded via the multipart upload method, and will accept any files that are sent to it, regardless if the media player enabled can handle it." + consumes: + - "multipart/form-data" /trending/{sourceId}/info/{id}: get: responses: @@ -934,6 +928,21 @@ type: "string" description: "This field allows for users to filter on specific fields that come back in objects from search results. The format is simply: `key1:value;key2:value2`." pattern: "((?:\\w+:\\w+));?" + whereQueryString: + name: "where" + in: "query" + type: "string" + description: "Where the file should be placed. This should be one of:\n\n- `series`\n- `tvshow`\n- `movie[s]`\n- `music`\n- `photo[s]`\n- `other`\n- `auto` - will auto detect possible locations based on file type" + enum: + - "movie" + - "tvshow" + - "music" + - "other" + - "photo" + - "auto" + - "series" + - "photos" + - "movies" tags: - name: "Searching" diff --git a/app/tests/uploadtest.html b/app/tests/uploadtest.html new file mode 100644 index 0000000..eb19178 --- /dev/null +++ b/app/tests/uploadtest.html @@ -0,0 +1,12 @@ + + +
+ + +
+ + diff --git a/package.json b/package.json index 2cc08ff..b0c7560 100644 --- a/package.json +++ b/package.json @@ -15,11 +15,13 @@ "debug": "^2.6.8", "errorhandler": "^1.5.0", "express": "^4.15.3", + "express-fileupload": "^0.4.0", "express-session": "^1.15.4", "express-winston": "^2.4.0", "lodash": "^4.17.4", "method-override": "^2.3.9", "node-persist": "^2.1.0", + "pretty-bytes": "^5.1.0", "request": "^2.81.0", "sanitize-filename": "^1.6.1", "swagger-routes": "^1.7.0", diff --git a/yarn.lock b/yarn.lock index 2d07eb8..68c1ece 100644 --- a/yarn.lock +++ b/yarn.lock @@ -319,6 +319,13 @@ braces@^2.3.0, braces@^2.3.1: split-string "^3.0.2" to-regex "^3.0.1" +busboy@^0.2.14: + version "0.2.14" + resolved "https://registry.yarnpkg.com/busboy/-/busboy-0.2.14.tgz#6c2a622efcf47c57bbbe1e2a9c37ad36c7925453" + dependencies: + dicer "0.2.5" + readable-stream "1.1.x" + bytes@1: version "1.0.0" resolved "https://registry.yarnpkg.com/bytes/-/bytes-1.0.0.tgz#3569ede8ba34315fab99c3e92cb04c7220de1fa8" @@ -367,6 +374,10 @@ chalk@~0.4.0: has-color "~0.1.0" strip-ansi "~0.1.0" +charenc@~0.0.1: + version "0.0.2" + resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667" + cheerio@^0.22.0: version "0.22.0" resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-0.22.0.tgz#a9baa860a3f9b595a6b81b1a86873121ed3a269e" @@ -574,6 +585,10 @@ crc@3.4.4: version "3.4.4" resolved "https://registry.yarnpkg.com/crc/-/crc-3.4.4.tgz#9da1e980e3bd44fc5c93bf5ab3da3378d85e466b" +crypt@~0.0.1: + version "0.0.2" + resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b" + cryptiles@2.x.x: version "2.0.5" resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-2.0.5.tgz#3bdfecdc608147c1c67202fa291e7dca59eaa3b8" @@ -711,6 +726,13 @@ detect-libc@^1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" +dicer@0.2.5: + version "0.2.5" + resolved "https://registry.yarnpkg.com/dicer/-/dicer-0.2.5.tgz#5996c086bb33218c812c090bddc09cd12facb70f" + dependencies: + readable-stream "1.1.x" + streamsearch "0.1.2" + dom-serializer@0, dom-serializer@~0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.0.tgz#073c697546ce0780ce23be4a28e293e40bc30c82" @@ -896,6 +918,15 @@ expect@^1.13.0: object-keys "^1.0.9" tmatch "^2.0.1" +express-fileupload@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/express-fileupload/-/express-fileupload-0.4.0.tgz#de6d1dbe3122732c416f6965aa88bbf70721ad84" + dependencies: + busboy "^0.2.14" + fs-extra "^4.0.1" + md5 "^2.2.1" + streamifier "^0.1.1" + express-session@^1.15.4: version "1.15.6" resolved "https://registry.yarnpkg.com/express-session/-/express-session-1.15.6.tgz#47b4160c88f42ab70fe8a508e31cbff76757ab0a" @@ -1188,6 +1219,14 @@ from@~0: version "0.1.7" resolved "https://registry.yarnpkg.com/from/-/from-0.1.7.tgz#83c60afc58b9c56997007ed1a768b3ab303a44fe" +fs-extra@^4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-4.0.3.tgz#0d852122e5bc5beb453fb028e9c0c9bf36340c94" + dependencies: + graceful-fs "^4.1.2" + jsonfile "^4.0.0" + universalify "^0.1.0" + fs-minipass@^1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.5.tgz#06c277218454ec288df77ada54a03b8702aacb9d" @@ -1364,7 +1403,7 @@ graceful-fs@^3.0.0: dependencies: natives "^1.1.0" -graceful-fs@^4.1.2: +graceful-fs@^4.1.2, graceful-fs@^4.1.6: version "4.1.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" @@ -1716,7 +1755,7 @@ is-boolean-object@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.0.0.tgz#98f8b28030684219a95f375cfbd88ce3405dff93" -is-buffer@^1.1.5: +is-buffer@^1.1.5, is-buffer@~1.1.1: version "1.1.6" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" @@ -2029,6 +2068,12 @@ json5@0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/json5/-/json5-0.4.0.tgz#054352e4c4c80c86c0923877d449de176a732c8d" +jsonfile@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" + optionalDependencies: + graceful-fs "^4.1.6" + jsonpointer@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/jsonpointer/-/jsonpointer-4.0.1.tgz#4fd92cb34e0e9db3c89c8622ecf51f9b978c6cb9" @@ -2269,6 +2314,14 @@ math-random@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/math-random/-/math-random-1.0.1.tgz#8b3aac588b8a66e4975e3cdea67f7bb329601fac" +md5@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/md5/-/md5-2.2.1.tgz#53ab38d5fe3c8891ba465329ea23fac0540126f9" + dependencies: + charenc "~0.0.1" + crypt "~0.0.1" + is-buffer "~1.1.1" + media-typer@0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" @@ -2760,6 +2813,10 @@ preserve@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" +pretty-bytes@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.1.0.tgz#6237ecfbdc6525beaef4de722cc60a58ae0e6c6d" + pretty-hrtime@^1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz#b7e3ea42435a4c9b2759d99e0f201eb195802ee1" @@ -2873,6 +2930,15 @@ rc@^1.1.7: minimist "^1.2.0" strip-json-comments "~2.0.1" +readable-stream@1.1.x, readable-stream@~1.1.9: + version "1.1.14" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "0.0.1" + string_decoder "~0.10.x" + "readable-stream@>=1.0.33-1 <1.1.0-0": version "1.0.34" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c" @@ -2894,15 +2960,6 @@ readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.0.6, readable string_decoder "~1.1.1" util-deprecate "~1.0.1" -readable-stream@~1.1.9: - version "1.1.14" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.1" - isarray "0.0.1" - string_decoder "~0.10.x" - readable-stream@~2.0.5: version "2.0.6" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.0.6.tgz#8f90341e68a53ccc928788dacfcd11b36eb9b78e" @@ -3302,12 +3359,20 @@ stream-consume@^0.1.0, stream-consume@~0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/stream-consume/-/stream-consume-0.1.1.tgz#d3bdb598c2bd0ae82b8cac7ac50b1107a7996c48" +streamifier@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/streamifier/-/streamifier-0.1.1.tgz#97e98d8fa4d105d62a2691d1dc07e820db8dfc4f" + streamify@^0.2.9: version "0.2.9" resolved "https://registry.yarnpkg.com/streamify/-/streamify-0.2.9.tgz#8938b14db491e2b6be4f8d99cc4133c9f0384f0b" dependencies: hashish "~0.0.4" +streamsearch@0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-0.1.2.tgz#808b9d0e56fc273d809ba57338e929919a1a9f1a" + string-length@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/string-length/-/string-length-1.0.1.tgz#56970fb1c38558e9e70b728bf3de269ac45adfac" @@ -3582,6 +3647,10 @@ unique-stream@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unique-stream/-/unique-stream-1.0.0.tgz#d59a4a75427447d9aa6c91e70263f8d26a4b104b" +universalify@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.1.tgz#fa71badd4437af4c148841e3b3b165f9e9e590b7" + unpipe@1.0.0, unpipe@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" From 03cf9fa9ffef354956df4c4977c8d50116db003c Mon Sep 17 00:00:00 2001 From: Evin Callahan Date: Sun, 17 Jun 2018 20:13:34 -0500 Subject: [PATCH 7/8] some fixes --- app/components/plugins/upload/index.js | 4 ++-- app/components/plugins/url/index.js | 14 +++++++------- app/oas/main.oas2.yml | 15 +-------------- app/tests/scenarios.json | 22 +++++++++++++++------- 4 files changed, 25 insertions(+), 30 deletions(-) diff --git a/app/components/plugins/upload/index.js b/app/components/plugins/upload/index.js index 2d4929f..8371a16 100644 --- a/app/components/plugins/upload/index.js +++ b/app/components/plugins/upload/index.js @@ -2,7 +2,7 @@ const crypto = require('crypto'); const path = require('path'); const prettyBytes = require('pretty-bytes'); -const dest = require('../../dest'); +const Dest = require('../../dest'); const Plugin = require('../../plugin_handler/base'); // standard plugin metadata, and some additional flexget properties @@ -39,7 +39,7 @@ class UploadHandler extends Plugin { return new Promise((resolve, reject) => { const fname = file.name; - const basepath = dest.determineDest(where); + const basepath = Dest.determineDest(where); const fullpath = path.join(basepath, fname); const id = crypto.createHash('sha1') .update(`${fullpath}`) diff --git a/app/components/plugins/url/index.js b/app/components/plugins/url/index.js index 8db3e98..3c5a7e6 100644 --- a/app/components/plugins/url/index.js +++ b/app/components/plugins/url/index.js @@ -6,7 +6,9 @@ const path = require('path'); const sanitize = require('sanitize-filename'); const Promise = require('bluebird'); const Url = require('url'); + const Plugin = require('../../plugin_handler/base'); +const Dest = require('../../dest'); class KaptureURLHandler extends Plugin { @@ -67,6 +69,8 @@ class KaptureURLHandler extends Plugin { .on('response', function (head) { self.logger.debug('head:', head.headers); + + const dest = where || self.assumeDownloadPathFromCtype(head.headers['content-type']); @@ -223,13 +227,9 @@ class KaptureURLHandler extends Plugin { getDestPath(url, dest) { - return path.resolve( - path.join( - this.config.getUserSetting('downloadPaths.root'), - this.config.getUserSetting('downloadPaths.' + (dest || 'default')), - this.getFilename(url) - ) - ); + let cleanDest = Dest.determineDest(dest); + + return path.join(cleanDest, this.getFilename(url)); } getFilename(url) { diff --git a/app/oas/main.oas2.yml b/app/oas/main.oas2.yml index ed5cbeb..5e0ac15 100644 --- a/app/oas/main.oas2.yml +++ b/app/oas/main.oas2.yml @@ -396,20 +396,6 @@ type: "file" required: true description: "Binary upload data" - - - in: "formData" - name: "type" - type: "string" - description: "Type of the media being uploaded" - enum: - - "movie" - - "tvshow" - - "music" - - "other" - - "photo" - - "auto" - required: true - default: "other" - $ref: "#/parameters/whereQueryString" operationId: "newupload" @@ -943,6 +929,7 @@ - "series" - "photos" - "movies" + - "tvshows" tags: - name: "Searching" diff --git a/app/tests/scenarios.json b/app/tests/scenarios.json index 6907f09..5d0ebb5 100644 --- a/app/tests/scenarios.json +++ b/app/tests/scenarios.json @@ -339,6 +339,9 @@ } ], "script": "tests['verify list has 2 download entries'] = output.body.get().length === 2;\n\n$.ctx.set('todelete', output.body.get().map(function(e) {\n return e.id;\n}));" + }, + "before": { + "script": "SL.sleep(5000)" } }, { @@ -394,6 +397,9 @@ } ], "script": "tests['verify list has 2 download entries'] = output.body.get().length === 2;\n\n$.ctx.set('todelete', output.body.get().map(function(e) {\n return e.id;\n}));" + }, + "before": { + "script": "SL.sleep(5000)" } }, { @@ -419,7 +425,10 @@ } } ] - }, + } + }, + "utilities": {}, + "after": { "url": { "name": "Verify URL functionality", "steps": [ @@ -431,7 +440,7 @@ }, "name": "Try video file", "before": { - "script": "const videoUrl = \"http://kapturebox.com/video/explainer.webm\"; // video/webm\nconst imageUrl = \"http://kapturebox.com/img/logo2.png\"; // image/png\nconst musicUrl = \"https://freemusicarchive.org/music/download/69f6cb3d0a235db46bb69186ef634b9da5d43bfd\"; // audio/mpeg\n\nconst videoUrlEnc = Buffer.from(videoUrl).toString('base64');\nconst imageUrlEnc = Buffer.from(imageUrl).toString('base64');\nconst musicUrlEnc = Buffer.from(musicUrl).toString('base64');\n\n$.ctx.set('videoUrl', videoUrlEnc);\n$.ctx.set('imageUrl', imageUrlEnc);\n$.ctx.set('musicUrl', musicUrlEnc);" + "script": "const videoUrl = \"http://kapturebox.com/video/explainer.webm\"; // video/webm\nconst imageUrl = \"http://kapturebox.com/img/logo2.png\"; // image/png\nconst musicUrl = \"https://freemusicarchive.org/music/download/62ae36da26642672bdc9f2e822a09cf3050cd4ac\"; // audio/mpeg\n\nconst videoUrlEnc = Buffer.from(videoUrl).toString('base64');\nconst imageUrlEnc = Buffer.from(imageUrl).toString('base64');\nconst musicUrlEnc = Buffer.from(musicUrl).toString('base64');\n\n$.ctx.set('videoUrl', videoUrlEnc);\n$.ctx.set('imageUrl', imageUrlEnc);\n$.ctx.set('musicUrl', musicUrlEnc);" }, "after": { "assertions": [ @@ -459,7 +468,7 @@ "method": "post", "url": "{$.env.host}/api/v1/downloads/method:url/{$.ctx.videoUrl}", "query": { - "where": "shows" + "where": "tvshows" } }, "name": "Try video file with dest override", @@ -474,7 +483,7 @@ { "target": "output.body.destName", "op": "eq", - "expected": "shows" + "expected": "tvshows" }, { "target": "output.body.contentType", @@ -578,7 +587,7 @@ "script": "const bdy = output.body.get();\nconst ids = bdy.map(function(e) {return e.id});\n\n$.ctx.set('todelete', ids);" }, "before": { - "script": "SL.sleep(5000);" + "script": "SL.sleep(10000);" } }, { @@ -616,6 +625,5 @@ ], "description": "Checks that the url plugin works" } - }, - "utilities": {} + } } From ef8c14c7c917eaf0b90a8d145db915a800d29534 Mon Sep 17 00:00:00 2001 From: Evin Callahan Date: Sun, 17 Jun 2018 20:23:09 -0500 Subject: [PATCH 8/8] minor readme updates --- README.md | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index fffb6de..8398156 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,18 @@ > This is the backend for the kapture server (API) -# Getting started + +# Quickstart with Docker + +To get the entire system up and running, the quickest way is to use the docker build: + +```bash +docker-compose up +``` + +This will get you a fully working kapture server installation (with dependent services) in docker that you can access via [localhost:9000](localhost:9000). See the [API documentation](http://kapture.docs.stoplight.io) on how to properly use it. + +# Local Development Easiest way to get up and running here is to start up a local server: @@ -19,6 +30,6 @@ Then connect to http://localhost:9000 -# API Spec +# API [The entire API spec is well documented here](http://kapture.docs.stoplight.io)