diff --git a/addon/addon.js b/addon/addon.js index 5854b27..a2dbd0e 100644 --- a/addon/addon.js +++ b/addon/addon.js @@ -1,11 +1,12 @@ const Bottleneck = require('bottleneck'); const { addonBuilder } = require('stremio-addon-sdk'); const { Type } = require('./lib/types'); -const { dummyManifest, DefaultProviders } = require('./lib/manifest'); +const { dummyManifest } = require('./lib/manifest'); const { cacheWrapStream } = require('./lib/cache'); const { toStreamInfo, applyStaticInfo } = require('./lib/streamInfo'); const repository = require('./lib/repository'); const applySorting = require('./lib/sort'); +const applyFilters = require('./lib/filter'); const { applyMochs, getMochCatalog, getMochItemMeta } = require('./moch/moch'); const CACHE_MAX_AGE = process.env.CACHE_MAX_AGE || 4 * 60 * 60; // 4 hours in seconds @@ -13,7 +14,6 @@ const CACHE_MAX_AGE_EMPTY = 30 * 60; // 30 minutes const STALE_REVALIDATE_AGE = 4 * 60 * 60; // 4 hours const STALE_ERROR_AGE = 7 * 24 * 60 * 60; // 7 days -const defaultProviders = DefaultProviders.map(provider => provider.toLowerCase()); const builder = new addonBuilder(dummyManifest()); const limiter = new Bottleneck({ maxConcurrent: process.env.LIMIT_MAX_CONCURRENT || 20, @@ -30,7 +30,7 @@ builder.defineStreamHandler((args) => { .then(records => records .sort((a, b) => b.torrent.seeders - a.torrent.seeders || b.torrent.uploadDate - a.torrent.uploadDate) .map(record => toStreamInfo(record))))) - .then(streams => filterByProvider(streams, args.extra.providers || defaultProviders)) + .then(streams => applyFilters(streams, args.extra)) .then(streams => applySorting(streams, args.extra)) .then(streams => applyStaticInfo(streams)) .then(streams => applyMochs(streams, args.extra)) @@ -111,15 +111,4 @@ async function movieRecordsHandler(args) { return Promise.resolve([]); } -function filterByProvider(streams, providers) { - if (!providers || !providers.length) { - return streams; - } - return streams.filter(stream => { - const match = stream.title.match(/⚙.* ([^ \n]+)/); - const provider = match && match[1].toLowerCase(); - return providers.includes(provider); - }) -} - module.exports = builder.getInterface(); diff --git a/addon/lib/configuration.js b/addon/lib/configuration.js index ef4df8d..a83a291 100644 --- a/addon/lib/configuration.js +++ b/addon/lib/configuration.js @@ -1,3 +1,8 @@ +const { DebridOptions } = require('../moch/options'); +const { QualityFilter } = require('./filter'); + +const keysToSplit = ['providers', QualityFilter.key, DebridOptions.key]; + function parseConfiguration(configuration) { const configValues = configuration.split('|') .reduce((map, next) => { @@ -7,12 +12,9 @@ function parseConfiguration(configuration) { } return map; }, {}); - if (configValues.providers) { - configValues.providers = configValues.providers.split(',').map(provider => provider.toLowerCase()); - } - if (configValues.debridoptions) { - configValues.debridoptions = configValues.debridoptions.split(',').map(option => option.toLowerCase()); - } + keysToSplit + .filter(key => configValues[key]) + .filter(key => configValues[key] = configValues[key].split(',').map(provider => provider.toLowerCase())) return configValues; } diff --git a/addon/lib/filter.js b/addon/lib/filter.js new file mode 100644 index 0000000..4b1ffe8 --- /dev/null +++ b/addon/lib/filter.js @@ -0,0 +1,114 @@ +const Providers = [ + 'YTS', + 'EZTV', + 'RARBG', + '1337x', + 'ThePirateBay', + 'KickassTorrents', + 'TorrentGalaxy', + 'Rutor', + 'HorribleSubs', + 'NyaaSi', + 'NyaaPantsu' +]; +const QualityFilter = { + key: 'qualityfilter', + options: [ + { + key: "4k", + label: "4k", + items: ["4k"], + test(quality) { + return this.items.includes(quality); + } + }, + { + key: "1080p", + label: "1080p", + items: ["1080p"], + test(quality) { + return this.items.includes(quality) + } + }, + { + key: "720p", + label: "720p", + items: ["720p"], + test(quality) { + return this.items.includes(quality) + } + }, + { + key: "480p", + label: "480p", + items: ["480p"], + test(quality) { + return this.items.includes(quality) + } + }, + { + key: "sd", + label: "SD", + items: ["DVDRip", "HDRip", "BDRip", "BRRip", "BluRay", "WED-DL", "WEBRip", "HDTV", "DivX", "XviD"], + test(quality) { + return this.items.includes(quality) + } + }, + { + key: "scr", + label: "Screener", + items: ["SCR"], + test(quality) { + return this.items.includes(quality) + } + }, + { + key: "cam", + label: "Cam", + items: ["CAM", "TeleSync", "TeleCine"], + test(quality) { + return this.items.includes(quality) + } + }, + { + key: "unknown", + label: "Unknown", + test(quality) { + return !quality + } + } + ] +}; +const defaultProviderKeys = Providers.map(provider => provider.toLowerCase()); + +function applyFilters(streams, config) { + return filterByQuality(filterByProvider(streams, config), config); +} + +function filterByProvider(streams, config) { + const providers = config.providers || defaultProviderKeys; + if (!providers || !providers.length) { + return streams; + } + return streams.filter(stream => { + const match = stream.title.match(/⚙.* ([^ \n]+)/); + const provider = match && match[1].toLowerCase(); + return providers.includes(provider); + }) +} + +function filterByQuality(streams, config) { + const filters = config[QualityFilter.key]; + if (!filters) { + return streams; + } + const filterOptions = QualityFilter.options.filter(option => filters.includes(option.key)); + return streams.filter(stream => { + const streamQuality = stream.name.split('\n')[1]; + return !filterOptions.some(option => option.test(streamQuality)); + }); +} + +module.exports = applyFilters; +module.exports.Providers = Providers; +module.exports.QualityFilter = QualityFilter; \ No newline at end of file diff --git a/addon/lib/landingTemplate.js b/addon/lib/landingTemplate.js index 038fee5..d031c9d 100644 --- a/addon/lib/landingTemplate.js +++ b/addon/lib/landingTemplate.js @@ -180,14 +180,16 @@ button:active { box-shadow: 0 0.5vh 1vh rgba(0, 0, 0, 0.2); } `; -const { Providers } = require('./manifest'); +const { Providers } = require('./filter'); const { SortOptions } = require('./sort'); +const { QualityFilter } = require('./filter'); const { DebridOptions } = require('../moch/options'); const { MochOptions } = require('../moch/moch'); function landingTemplate(manifest, config = {}) { const providers = config.providers || []; - const sort = config.sort || SortOptions.options.qualitySeeders.key; + const sort = config[SortOptions.key] || SortOptions.options.qualitySeeders.key; + const qualityFilters = config[QualityFilter.key] || []; const limit = config.limit || ''; const debridProvider = Object.keys(MochOptions).find(mochKey => config[mochKey]); @@ -212,6 +214,9 @@ function landingTemplate(manifest, config = {}) { const sortOptionsHTML = Object.values(SortOptions.options) .map((option, i) => ``) .join('\n'); + const qualityFiltersHTML = Object.values(QualityFilter.options) + .map(option => ``) + .join('\n'); const debridProvidersHTML = Object.values(MochOptions) .map(moch => ``) .join('\n'); @@ -267,6 +272,11 @@ function landingTemplate(manifest, config = {}) { ${sortOptionsHTML} + + + @@ -318,6 +328,11 @@ function landingTemplate(manifest, config = {}) { onChange: () => generateInstallLink() }); $('#iProviders').multiselect('select', [${providers.map(provider => '"' + provider + '"')}]); + $('#iQualityFilter').multiselect({ + nonSelectedText: 'None', + onChange: () => generateInstallLink() + }); + $('#iQualityFilter').multiselect('select', [${qualityFilters.map(filter => '"' + filter + '"')}]); $('#iDebridOptions').multiselect({ nonSelectedText: 'None', onChange: () => generateInstallLink() @@ -355,6 +370,7 @@ function landingTemplate(manifest, config = {}) { function generateInstallLink() { const providersValue = $('#iProviders').val().join(',') || ''; + const qualityFilterValue = $('#iQualityFilter').val().join(',') || ''; const sortValue = $('#iSort').val() || ''; const limitValue = $('#iLimit').val() || ''; @@ -367,6 +383,7 @@ function landingTemplate(manifest, config = {}) { const providers = providersValue.length && providersValue; + const qualityFilters = qualityFilterValue.length && qualityFilterValue; const sort = sortValue !== '${SortOptions.options.qualitySeeders.key}' && sortValue; const limit = /^[1-9][0-9]*$/.test(limitValue) && limitValue; @@ -380,6 +397,7 @@ function landingTemplate(manifest, config = {}) { ['providers', providers], ['${SortOptions.key}', sort], ['limit', limit], + ['${QualityFilter.key}', qualityFilters], ['${DebridOptions.key}', debridOptions], ['${MochOptions.realdebrid.key}', realDebrid], ['${MochOptions.premiumize.key}', premiumize], diff --git a/addon/lib/manifest.js b/addon/lib/manifest.js index 89a6973..be36f96 100644 --- a/addon/lib/manifest.js +++ b/addon/lib/manifest.js @@ -1,20 +1,8 @@ const { MochOptions } = require('../moch/moch'); +const { Providers } = require('./filter'); const { showDebridCatalog } = require('../moch/options'); const { Type } = require('./types'); -const Providers = [ - 'YTS', - 'EZTV', - 'RARBG', - '1337x', - 'ThePirateBay', - 'KickassTorrents', - 'TorrentGalaxy', - 'Rutor', - 'HorribleSubs', - 'NyaaSi', - 'NyaaPantsu' -]; const DefaultProviders = Providers const CatalogMochs = Object.values(MochOptions).filter(moch => moch.catalog); @@ -31,7 +19,7 @@ function manifest(config = {}) { const mochsDesc = enabledMochs ? ` and ${enabledMochs} enabled` : ''; return { id: 'com.stremio.torrentio.addon', - version: '0.0.9', + version: '0.0.10', name: 'Torrentio', description: 'Provides torrent streams from scraped torrent providers.' + ` Currently supports ${enabledProvidersDesc}${mochsDesc}.` @@ -86,4 +74,4 @@ function getResources(config) { return [streamResource]; } -module.exports = { manifest, dummyManifest, Providers, DefaultProviders }; \ No newline at end of file +module.exports = { manifest, dummyManifest }; \ No newline at end of file diff --git a/addon/lib/streamInfo.js b/addon/lib/streamInfo.js index 4a279a1..d1f822e 100644 --- a/addon/lib/streamInfo.js +++ b/addon/lib/streamInfo.js @@ -60,7 +60,7 @@ function toStreamInfo(record) { function getQuality(record, torrentInfo, fileInfo) { const resolution = fileInfo.resolution || torrentInfo.resolution || record.torrent.resolution; const source = fileInfo.source || torrentInfo.source; - if (['CAM', 'TeleSync'].includes(source)) { + if (['CAM', 'TeleSync', 'TeleCine'].includes(source)) { return source; } return resolution || source; diff --git a/addon/package-lock.json b/addon/package-lock.json index 382d1d6..919fb64 100644 --- a/addon/package-lock.json +++ b/addon/package-lock.json @@ -1,6 +1,6 @@ { "name": "stremio-torrentio", - "version": "1.0.9", + "version": "1.0.10", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/addon/package.json b/addon/package.json index 85f1125..565ace3 100644 --- a/addon/package.json +++ b/addon/package.json @@ -1,6 +1,6 @@ { "name": "stremio-torrentio", - "version": "1.0.9", + "version": "1.0.10", "main": "addon.js", "scripts": { "start": "node index.js"