mirror of
https://github.com/knightcrawler-stremio/knightcrawler.git
synced 2024-12-20 03:29:51 +00:00
[addon] add quality exclude filter config option
This commit is contained in:
@@ -1,11 +1,12 @@
|
|||||||
const Bottleneck = require('bottleneck');
|
const Bottleneck = require('bottleneck');
|
||||||
const { addonBuilder } = require('stremio-addon-sdk');
|
const { addonBuilder } = require('stremio-addon-sdk');
|
||||||
const { Type } = require('./lib/types');
|
const { Type } = require('./lib/types');
|
||||||
const { dummyManifest, DefaultProviders } = require('./lib/manifest');
|
const { dummyManifest } = require('./lib/manifest');
|
||||||
const { cacheWrapStream } = require('./lib/cache');
|
const { cacheWrapStream } = require('./lib/cache');
|
||||||
const { toStreamInfo, applyStaticInfo } = require('./lib/streamInfo');
|
const { toStreamInfo, applyStaticInfo } = require('./lib/streamInfo');
|
||||||
const repository = require('./lib/repository');
|
const repository = require('./lib/repository');
|
||||||
const applySorting = require('./lib/sort');
|
const applySorting = require('./lib/sort');
|
||||||
|
const applyFilters = require('./lib/filter');
|
||||||
const { applyMochs, getMochCatalog, getMochItemMeta } = require('./moch/moch');
|
const { applyMochs, getMochCatalog, getMochItemMeta } = require('./moch/moch');
|
||||||
|
|
||||||
const CACHE_MAX_AGE = process.env.CACHE_MAX_AGE || 4 * 60 * 60; // 4 hours in seconds
|
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_REVALIDATE_AGE = 4 * 60 * 60; // 4 hours
|
||||||
const STALE_ERROR_AGE = 7 * 24 * 60 * 60; // 7 days
|
const STALE_ERROR_AGE = 7 * 24 * 60 * 60; // 7 days
|
||||||
|
|
||||||
const defaultProviders = DefaultProviders.map(provider => provider.toLowerCase());
|
|
||||||
const builder = new addonBuilder(dummyManifest());
|
const builder = new addonBuilder(dummyManifest());
|
||||||
const limiter = new Bottleneck({
|
const limiter = new Bottleneck({
|
||||||
maxConcurrent: process.env.LIMIT_MAX_CONCURRENT || 20,
|
maxConcurrent: process.env.LIMIT_MAX_CONCURRENT || 20,
|
||||||
@@ -30,7 +30,7 @@ builder.defineStreamHandler((args) => {
|
|||||||
.then(records => records
|
.then(records => records
|
||||||
.sort((a, b) => b.torrent.seeders - a.torrent.seeders || b.torrent.uploadDate - a.torrent.uploadDate)
|
.sort((a, b) => b.torrent.seeders - a.torrent.seeders || b.torrent.uploadDate - a.torrent.uploadDate)
|
||||||
.map(record => toStreamInfo(record)))))
|
.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 => applySorting(streams, args.extra))
|
||||||
.then(streams => applyStaticInfo(streams))
|
.then(streams => applyStaticInfo(streams))
|
||||||
.then(streams => applyMochs(streams, args.extra))
|
.then(streams => applyMochs(streams, args.extra))
|
||||||
@@ -111,15 +111,4 @@ async function movieRecordsHandler(args) {
|
|||||||
return Promise.resolve([]);
|
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();
|
module.exports = builder.getInterface();
|
||||||
|
|||||||
@@ -1,3 +1,8 @@
|
|||||||
|
const { DebridOptions } = require('../moch/options');
|
||||||
|
const { QualityFilter } = require('./filter');
|
||||||
|
|
||||||
|
const keysToSplit = ['providers', QualityFilter.key, DebridOptions.key];
|
||||||
|
|
||||||
function parseConfiguration(configuration) {
|
function parseConfiguration(configuration) {
|
||||||
const configValues = configuration.split('|')
|
const configValues = configuration.split('|')
|
||||||
.reduce((map, next) => {
|
.reduce((map, next) => {
|
||||||
@@ -7,12 +12,9 @@ function parseConfiguration(configuration) {
|
|||||||
}
|
}
|
||||||
return map;
|
return map;
|
||||||
}, {});
|
}, {});
|
||||||
if (configValues.providers) {
|
keysToSplit
|
||||||
configValues.providers = configValues.providers.split(',').map(provider => provider.toLowerCase());
|
.filter(key => configValues[key])
|
||||||
}
|
.filter(key => configValues[key] = configValues[key].split(',').map(provider => provider.toLowerCase()))
|
||||||
if (configValues.debridoptions) {
|
|
||||||
configValues.debridoptions = configValues.debridoptions.split(',').map(option => option.toLowerCase());
|
|
||||||
}
|
|
||||||
return configValues;
|
return configValues;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
114
addon/lib/filter.js
Normal file
114
addon/lib/filter.js
Normal file
@@ -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;
|
||||||
@@ -180,14 +180,16 @@ button:active {
|
|||||||
box-shadow: 0 0.5vh 1vh rgba(0, 0, 0, 0.2);
|
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 { SortOptions } = require('./sort');
|
||||||
|
const { QualityFilter } = require('./filter');
|
||||||
const { DebridOptions } = require('../moch/options');
|
const { DebridOptions } = require('../moch/options');
|
||||||
const { MochOptions } = require('../moch/moch');
|
const { MochOptions } = require('../moch/moch');
|
||||||
|
|
||||||
function landingTemplate(manifest, config = {}) {
|
function landingTemplate(manifest, config = {}) {
|
||||||
const providers = config.providers || [];
|
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 limit = config.limit || '';
|
||||||
|
|
||||||
const debridProvider = Object.keys(MochOptions).find(mochKey => config[mochKey]);
|
const debridProvider = Object.keys(MochOptions).find(mochKey => config[mochKey]);
|
||||||
@@ -212,6 +214,9 @@ function landingTemplate(manifest, config = {}) {
|
|||||||
const sortOptionsHTML = Object.values(SortOptions.options)
|
const sortOptionsHTML = Object.values(SortOptions.options)
|
||||||
.map((option, i) => `<option value="${option.key}" ${i === 0 ? 'selected' : ''}>${option.description}</option>`)
|
.map((option, i) => `<option value="${option.key}" ${i === 0 ? 'selected' : ''}>${option.description}</option>`)
|
||||||
.join('\n');
|
.join('\n');
|
||||||
|
const qualityFiltersHTML = Object.values(QualityFilter.options)
|
||||||
|
.map(option => `<option value="${option.key}">${option.label}</option>`)
|
||||||
|
.join('\n');
|
||||||
const debridProvidersHTML = Object.values(MochOptions)
|
const debridProvidersHTML = Object.values(MochOptions)
|
||||||
.map(moch => `<option value="${moch.key}">${moch.name}</option>`)
|
.map(moch => `<option value="${moch.key}">${moch.name}</option>`)
|
||||||
.join('\n');
|
.join('\n');
|
||||||
@@ -267,6 +272,11 @@ function landingTemplate(manifest, config = {}) {
|
|||||||
${sortOptionsHTML}
|
${sortOptionsHTML}
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
|
<label class="label" for="iQualityFilter">Exclude qualities/resolutions:</label>
|
||||||
|
<select id="iQualityFilter" class="input" name="qualityFilters[]" multiple="multiple">
|
||||||
|
${qualityFiltersHTML}
|
||||||
|
</select>
|
||||||
|
|
||||||
<label class="label" id="iLimitLabel" for="iLimit">Max results per quality:</label>
|
<label class="label" id="iLimitLabel" for="iLimit">Max results per quality:</label>
|
||||||
<input type="text" id="iLimit" onchange="generateInstallLink()" class="input" placeholder="All results">
|
<input type="text" id="iLimit" onchange="generateInstallLink()" class="input" placeholder="All results">
|
||||||
|
|
||||||
@@ -318,6 +328,11 @@ function landingTemplate(manifest, config = {}) {
|
|||||||
onChange: () => generateInstallLink()
|
onChange: () => generateInstallLink()
|
||||||
});
|
});
|
||||||
$('#iProviders').multiselect('select', [${providers.map(provider => '"' + provider + '"')}]);
|
$('#iProviders').multiselect('select', [${providers.map(provider => '"' + provider + '"')}]);
|
||||||
|
$('#iQualityFilter').multiselect({
|
||||||
|
nonSelectedText: 'None',
|
||||||
|
onChange: () => generateInstallLink()
|
||||||
|
});
|
||||||
|
$('#iQualityFilter').multiselect('select', [${qualityFilters.map(filter => '"' + filter + '"')}]);
|
||||||
$('#iDebridOptions').multiselect({
|
$('#iDebridOptions').multiselect({
|
||||||
nonSelectedText: 'None',
|
nonSelectedText: 'None',
|
||||||
onChange: () => generateInstallLink()
|
onChange: () => generateInstallLink()
|
||||||
@@ -355,6 +370,7 @@ function landingTemplate(manifest, config = {}) {
|
|||||||
|
|
||||||
function generateInstallLink() {
|
function generateInstallLink() {
|
||||||
const providersValue = $('#iProviders').val().join(',') || '';
|
const providersValue = $('#iProviders').val().join(',') || '';
|
||||||
|
const qualityFilterValue = $('#iQualityFilter').val().join(',') || '';
|
||||||
const sortValue = $('#iSort').val() || '';
|
const sortValue = $('#iSort').val() || '';
|
||||||
const limitValue = $('#iLimit').val() || '';
|
const limitValue = $('#iLimit').val() || '';
|
||||||
|
|
||||||
@@ -367,6 +383,7 @@ function landingTemplate(manifest, config = {}) {
|
|||||||
|
|
||||||
|
|
||||||
const providers = providersValue.length && providersValue;
|
const providers = providersValue.length && providersValue;
|
||||||
|
const qualityFilters = qualityFilterValue.length && qualityFilterValue;
|
||||||
const sort = sortValue !== '${SortOptions.options.qualitySeeders.key}' && sortValue;
|
const sort = sortValue !== '${SortOptions.options.qualitySeeders.key}' && sortValue;
|
||||||
const limit = /^[1-9][0-9]*$/.test(limitValue) && limitValue;
|
const limit = /^[1-9][0-9]*$/.test(limitValue) && limitValue;
|
||||||
|
|
||||||
@@ -380,6 +397,7 @@ function landingTemplate(manifest, config = {}) {
|
|||||||
['providers', providers],
|
['providers', providers],
|
||||||
['${SortOptions.key}', sort],
|
['${SortOptions.key}', sort],
|
||||||
['limit', limit],
|
['limit', limit],
|
||||||
|
['${QualityFilter.key}', qualityFilters],
|
||||||
['${DebridOptions.key}', debridOptions],
|
['${DebridOptions.key}', debridOptions],
|
||||||
['${MochOptions.realdebrid.key}', realDebrid],
|
['${MochOptions.realdebrid.key}', realDebrid],
|
||||||
['${MochOptions.premiumize.key}', premiumize],
|
['${MochOptions.premiumize.key}', premiumize],
|
||||||
|
|||||||
@@ -1,20 +1,8 @@
|
|||||||
const { MochOptions } = require('../moch/moch');
|
const { MochOptions } = require('../moch/moch');
|
||||||
|
const { Providers } = require('./filter');
|
||||||
const { showDebridCatalog } = require('../moch/options');
|
const { showDebridCatalog } = require('../moch/options');
|
||||||
const { Type } = require('./types');
|
const { Type } = require('./types');
|
||||||
|
|
||||||
const Providers = [
|
|
||||||
'YTS',
|
|
||||||
'EZTV',
|
|
||||||
'RARBG',
|
|
||||||
'1337x',
|
|
||||||
'ThePirateBay',
|
|
||||||
'KickassTorrents',
|
|
||||||
'TorrentGalaxy',
|
|
||||||
'Rutor',
|
|
||||||
'HorribleSubs',
|
|
||||||
'NyaaSi',
|
|
||||||
'NyaaPantsu'
|
|
||||||
];
|
|
||||||
const DefaultProviders = Providers
|
const DefaultProviders = Providers
|
||||||
const CatalogMochs = Object.values(MochOptions).filter(moch => moch.catalog);
|
const CatalogMochs = Object.values(MochOptions).filter(moch => moch.catalog);
|
||||||
|
|
||||||
@@ -31,7 +19,7 @@ function manifest(config = {}) {
|
|||||||
const mochsDesc = enabledMochs ? ` and ${enabledMochs} enabled` : '';
|
const mochsDesc = enabledMochs ? ` and ${enabledMochs} enabled` : '';
|
||||||
return {
|
return {
|
||||||
id: 'com.stremio.torrentio.addon',
|
id: 'com.stremio.torrentio.addon',
|
||||||
version: '0.0.9',
|
version: '0.0.10',
|
||||||
name: 'Torrentio',
|
name: 'Torrentio',
|
||||||
description: 'Provides torrent streams from scraped torrent providers.'
|
description: 'Provides torrent streams from scraped torrent providers.'
|
||||||
+ ` Currently supports ${enabledProvidersDesc}${mochsDesc}.`
|
+ ` Currently supports ${enabledProvidersDesc}${mochsDesc}.`
|
||||||
@@ -86,4 +74,4 @@ function getResources(config) {
|
|||||||
return [streamResource];
|
return [streamResource];
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = { manifest, dummyManifest, Providers, DefaultProviders };
|
module.exports = { manifest, dummyManifest };
|
||||||
@@ -60,7 +60,7 @@ function toStreamInfo(record) {
|
|||||||
function getQuality(record, torrentInfo, fileInfo) {
|
function getQuality(record, torrentInfo, fileInfo) {
|
||||||
const resolution = fileInfo.resolution || torrentInfo.resolution || record.torrent.resolution;
|
const resolution = fileInfo.resolution || torrentInfo.resolution || record.torrent.resolution;
|
||||||
const source = fileInfo.source || torrentInfo.source;
|
const source = fileInfo.source || torrentInfo.source;
|
||||||
if (['CAM', 'TeleSync'].includes(source)) {
|
if (['CAM', 'TeleSync', 'TeleCine'].includes(source)) {
|
||||||
return source;
|
return source;
|
||||||
}
|
}
|
||||||
return resolution || source;
|
return resolution || source;
|
||||||
|
|||||||
2
addon/package-lock.json
generated
2
addon/package-lock.json
generated
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "stremio-torrentio",
|
"name": "stremio-torrentio",
|
||||||
"version": "1.0.9",
|
"version": "1.0.10",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "stremio-torrentio",
|
"name": "stremio-torrentio",
|
||||||
"version": "1.0.9",
|
"version": "1.0.10",
|
||||||
"main": "addon.js",
|
"main": "addon.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node index.js"
|
"start": "node index.js"
|
||||||
|
|||||||
Reference in New Issue
Block a user