add (disabled) possibility to return stream subtitles for non debrid streams

This commit is contained in:
TheBeastLT
2023-10-17 20:37:05 +03:00
parent 76c3e9ac3f
commit b43da7a882
7 changed files with 198 additions and 44 deletions

View File

@@ -71,4 +71,4 @@ function isExtension(filename, extensions) {
return extensionMatch && extensions.includes(extensionMatch[1].toLowerCase());
}
module.exports = { isVideo, isSubtitle, isDisk, isArchive }
module.exports = { isVideo, isSubtitle, isDisk, isArchive, isExtension }

View File

@@ -21,11 +21,15 @@ const languageMapping = {
'tamil': '🇮🇳',
'polish': '🇵🇱',
'lithuanian': '🇱🇹',
'latvian': '🇱🇻',
'estonian': '🇪🇪',
'czech': '🇨🇿',
'slovakian': '🇸🇰',
'slovenian': '🇸🇮',
'hungarian': '🇭🇺',
'romanian': '🇷🇴',
'bulgarian': '🇧🇬',
'serbian': '🇷🇸 ',
'croatian': '🇭🇷',
'ukrainian': '🇺🇦',
'greek': '🇬🇷',
@@ -65,4 +69,9 @@ function containsLanguage(stream, languages) {
return languages.map(lang => languageMapping[lang]).some(lang => stream.title.includes(lang));
}
module.exports = { mapLanguages, containsLanguage, LanguageOptions }
function languageFromCode(code) {
const entry = Object.entries(languageMapping).find(entry => entry[1] === code);
return entry && entry[0];
}
module.exports = { mapLanguages, containsLanguage, languageFromCode, LanguageOptions }

View File

@@ -41,8 +41,31 @@ const File = database.define('file',
},
);
const Subtitle = database.define('subtitle',
{
infoHash: {
type: Sequelize.STRING(64),
allowNull: false,
references: { model: Torrent, key: 'infoHash' },
onDelete: 'CASCADE'
},
fileIndex: { type: Sequelize.INTEGER, allowNull: false },
fileId: {
type: Sequelize.BIGINT,
allowNull: true,
references: { model: File, key: 'id' },
onDelete: 'SET NULL'
},
title: { type: Sequelize.STRING(512), allowNull: false },
size: { type: Sequelize.BIGINT, allowNull: false },
},
{ timestamps: false }
);
Torrent.hasMany(File, { foreignKey: 'infoHash', constraints: false });
File.belongsTo(Torrent, { foreignKey: 'infoHash', constraints: false });
File.hasMany(Subtitle, { foreignKey: 'fileId', constraints: false });
Subtitle.belongsTo(File, { foreignKey: 'fileId', constraints: false });
function getTorrent(infoHash) {
return Torrent.findOne({ where: { infoHash: infoHash } });

View File

@@ -2,6 +2,7 @@ const titleParser = require('parse-torrent-title');
const { Type } = require('./types');
const { mapLanguages } = require('./languages');
const { enrichStreamSources, getSources } = require('./magnetHelper');
const { getSubtitles } = require("./subtitles");
const ADDON_NAME = 'Torrentio';
const SIZE_DELTA = 0.02;
@@ -39,9 +40,8 @@ function toStreamInfo(record) {
'\n'
);
const bingeGroupParts = getBingeGroupParts(record, sameInfo, quality, torrentInfo, fileInfo);
const behaviorHints = {
bingeGroup: joinDetailParts(bingeGroupParts, "torrentio|", "|")
};
const bingeGroup = joinDetailParts(bingeGroupParts, "torrentio|", "|")
const behaviorHints = bingeGroup ? { bingeGroup } : undefined;
return cleanOutputObject({
name: name,
@@ -49,7 +49,8 @@ function toStreamInfo(record) {
infoHash: record.infoHash,
fileIdx: record.fileIndex,
behaviorHints: behaviorHints,
sources: getSources(record.torrent.trackers, record.infoHash)
sources: getSources(record.torrent.trackers, record.infoHash),
subtitles: getSubtitles(record)
});
}
@@ -110,7 +111,23 @@ function applyStaticInfo(streams) {
}
function enrichStaticInfo(stream) {
return enrichStreamSources(stream);
return enrichSubtitles(enrichStreamSources({ ...stream }));
}
function enrichSubtitles(stream) {
if (stream.subtitles?.length) {
stream.subtitles = stream.subtitles.map(subtitle =>{
if (subtitle.url) {
return subtitle;
}
return {
id: `${subtitle.fileIndex}`,
lang: subtitle.lang,
url: `http://localhost:11470/${subtitle.infoHash}/${subtitle.fileIndex}/${subtitle.title.split('/').pop()}`
};
});
}
return stream;
}
function getBingeGroupParts(record, sameInfo, quality, torrentInfo, fileInfo) {

101
addon/lib/subtitles.js Normal file
View File

@@ -0,0 +1,101 @@
const { parse } = require('parse-torrent-title');
const { isExtension } = require("./extension");
const { Providers } = require("./filter");
const { languageFromCode } = require("./languages");
const languageMapping = {
'english': 'eng',
'japanese': 'jpn',
'russian': 'rus',
'italian': 'ita',
'portuguese': 'por',
'spanish': 'spa',
'latino': 'lat',
'korean': 'kor',
'chinese': 'zho',
'taiwanese': 'zht',
'french': 'fre',
'german': 'ger',
'dutch': 'dut',
'hindi': 'hin ',
'telugu': 'tel',
'tamil': 'tam',
'polish': 'pol',
'lithuanian': 'lit',
'latvian': 'lav',
'estonian': 'est',
'czech': 'cze',
'slovakian': 'slo',
'slovenian': 'slv',
'hungarian': 'hun',
'romanian': 'rum',
'bulgarian': 'bul',
'serbian': 'scc',
'croatian': 'hrv',
'ukrainian': 'ukr',
'greek': 'ell',
'danish': 'dan',
'finnish': 'fin',
'swedish': 'swe',
'norwegian': 'nor',
'turkish': 'tur',
'arabic': 'ara',
'persian': 'per',
'hebrew': 'heb',
'vietnamese': 'vie',
'indonesian': 'ind',
'thai': 'tha'
}
const ignoreSet = new Set(['dubbed', 'multi audio', 'multi subs', 'dual audio']);
const allowedExtensions = ['srt', 'vtt', 'ass', 'ssa'];
function getSubtitles(record) {
if (!record.subtitles || !record.subtitles.length) {
return null;
}
return record.subtitles
.filter(subtitle => isExtension(subtitle.title, allowedExtensions))
.sort((a, b) => b.size - a.size)
.map(subtitle => ({
infoHash: subtitle.infoHash,
fileIndex: subtitle.fileIndex,
title: subtitle.title,
lang: parseLanguage(subtitle.title, record),
}));
}
function parseLanguage(title, record) {
const subtitlePathParts = title.split('/');
const subtitleFileName = subtitlePathParts.pop();
const subtitleTitleNoExt = title.replace(/\.\w{2,5}$/, '');
const videoFileName = record.title.split('/').pop().replace(/\.\w{2,5}$/, '');
const fileNameLanguage = getSingleLanguage(subtitleFileName.replace(videoFileName, ''));
if (fileNameLanguage) {
return fileNameLanguage;
}
const videoTitleNoExt = record.title.replace(/\.\w{2,5}$/, '');
if (subtitleTitleNoExt === record.title || subtitleTitleNoExt === videoTitleNoExt) {
const provider = Providers.options.find(provider => provider.label === record.torrent.provider);
return provider?.foreign && languageFromCode(provider.foreign) || 'eng';
}
const folderName = subtitlePathParts.join('/');
const folderNameLanguage = getSingleLanguage(folderName.replace(videoFileName, ''));
if (folderNameLanguage) {
return folderNameLanguage
}
return getFileNameLanguageCode(subtitleFileName) || 'Unknown';
}
function getSingleLanguage(title) {
const parsedInfo = parse(title);
const languages = (parsedInfo.languages || []).filter(language => !ignoreSet.has(language));
return languages.length === 1 ? languageMapping[languages[0]] : undefined;
}
function getFileNameLanguageCode(fileName) {
const match = fileName.match(/(?:(?:^|[._ ])([A-Za-z][a-z]{1,2})|\[([a-z]{2,3})])\.\w{3,4}$/);
return match && match[1].toLowerCase();
}
module.exports = { getSubtitles }

View File

@@ -21,15 +21,15 @@
"name-to-imdb": "^3.0.4",
"named-queue": "^2.2.1",
"offcloud-api": "^1.0.0",
"parse-torrent-title": "git://github.com/TheBeastLT/parse-torrent-title.git#0019426237d273b5214df1a6df81232d7f2ae3fb",
"parse-torrent-title": "git://github.com/TheBeastLT/parse-torrent-title.git#aeae91e512741910bc479ac57dcb0ce78bad3ee0",
"pg": "^8.10.0",
"premiumize-api": "^1.0.3",
"real-debrid-api": "git://github.com/TheBeastLT/node-real-debrid.git#d1f7eaa8593b947edbfbc8a92a176448b48ef445",
"request-ip": "^3.3.0",
"sequelize": "^6.31.1",
"stremio-addon-sdk": "^1.6.10",
"ua-parser-js": "^1.0.32",
"user-agents": "^1.0.1234"
"ua-parser-js": "^1.0.36",
"user-agents": "^1.0.1444"
}
},
"node_modules/@putdotio/api-client": {
@@ -574,9 +574,9 @@
}
},
"node_modules/dot-json": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/dot-json/-/dot-json-1.2.2.tgz",
"integrity": "sha512-AKL+GsO4wSEU4LU+fAk/PqN4nQ6PB1vT3HpMiZous9xCzK5S0kh4DzfUY0EfU67jsIXLlu0ty71659N9Nmg+Tw==",
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/dot-json/-/dot-json-1.3.0.tgz",
"integrity": "sha512-Pu11Prog/Yjf2lBICow82/DSV46n3a2XT1Rqt/CeuhkO1fuacF7xydYhI0SwQx2Ue0jCyLtQzgKPFEO6ewv+bQ==",
"dependencies": {
"detect-indent": "~6.0.0",
"docopt": "~0.6.2",
@@ -1439,8 +1439,8 @@
},
"node_modules/parse-torrent-title": {
"version": "1.3.0",
"resolved": "git+ssh://git@github.com/TheBeastLT/parse-torrent-title.git#0019426237d273b5214df1a6df81232d7f2ae3fb",
"integrity": "sha512-Ay79gA/yYof3TU+8ddeL3kBnt0Mw9oVe2RXyrkN51Xj1PeiqJws7JaGnSuB3bsSsJ3weFBnH9qeXEhhC+9WVPw==",
"resolved": "git+ssh://git@github.com/TheBeastLT/parse-torrent-title.git#aeae91e512741910bc479ac57dcb0ce78bad3ee0",
"integrity": "sha512-Mys327xq6sHDWM4QVOMIZ7PVOY1WxoDI8ps4vjchwoyD26ezY2OQ7a1FsnZiaLBJTH9uCbLp/I4sKwsBcDinXQ==",
"license": "MIT",
"dependencies": {
"moment": "^2.24.0"
@@ -2270,9 +2270,9 @@
}
},
"node_modules/ua-parser-js": {
"version": "1.0.33",
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.33.tgz",
"integrity": "sha512-RqshF7TPTE0XLYAqmjlu5cLLuGdKrNu9O1KLA/qp39QtbZwuzwv1dT46DZSopoUMsYgXpB3Cv8a03FI8b74oFQ==",
"version": "1.0.36",
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.36.tgz",
"integrity": "sha512-znuyCIXzl8ciS3+y3fHJI/2OhQIXbXw9MWC/o3qwyR+RGppjZHrM27CGFSKCJXi2Kctiz537iOu2KnXs1lMQhw==",
"funding": [
{
"type": "opencollective",
@@ -2281,6 +2281,10 @@
{
"type": "paypal",
"url": "https://paypal.me/faisalman"
},
{
"type": "github",
"url": "https://github.com/sponsors/faisalman"
}
],
"engines": {
@@ -2288,9 +2292,9 @@
}
},
"node_modules/underscore": {
"version": "1.13.1",
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.1.tgz",
"integrity": "sha512-hzSoAVtJF+3ZtiFX0VgfFPHEDRm7Y/QPjGyNo4TVdnDTdft3tr8hEkD25a1jC+TjTuE7tkHGKkhwCgs9dgBB2g=="
"version": "1.13.6",
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.6.tgz",
"integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A=="
},
"node_modules/underscore-keypath": {
"version": "0.0.22",
@@ -2322,11 +2326,11 @@
"integrity": "sha512-HXgFDgDommxn5/bIv0cnQZsPhHDA90NPHD6+c/v21U5+Sx5hoP8+dP9IZXBU1gIfvdRfhG8cel9QNPeionfcCQ=="
},
"node_modules/user-agents": {
"version": "1.0.1295",
"resolved": "https://registry.npmjs.org/user-agents/-/user-agents-1.0.1295.tgz",
"integrity": "sha512-9Rgw4X4FeHZ1d11X9zASRgLYlPKeBINZNRsitoL/RwNEXuNTnDiImMfqa5/+OoGBNRJ1kqxmH3bJL8Fu4m0VIg==",
"version": "1.0.1444",
"resolved": "https://registry.npmjs.org/user-agents/-/user-agents-1.0.1444.tgz",
"integrity": "sha512-6WXJ0RZuUKgif1rW5FN02HnpoJ8EzH6COQoXCiVStZEVPz+YnAx3iA48etY3ZD4UwueYN9ALC7j4ayHvYEh7tA==",
"dependencies": {
"dot-json": "^1.2.2",
"dot-json": "^1.3.0",
"lodash.clonedeep": "^4.5.0"
}
},
@@ -2873,9 +2877,9 @@
"integrity": "sha512-NqTbaYeE4gA/wU1hdKFdU+AFahpDOpgGLzHP42k6H6DKExJd0A55KEVWYhL9FEmHmgeLvEU2vuKXDuU+4yToOw=="
},
"dot-json": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/dot-json/-/dot-json-1.2.2.tgz",
"integrity": "sha512-AKL+GsO4wSEU4LU+fAk/PqN4nQ6PB1vT3HpMiZous9xCzK5S0kh4DzfUY0EfU67jsIXLlu0ty71659N9Nmg+Tw==",
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/dot-json/-/dot-json-1.3.0.tgz",
"integrity": "sha512-Pu11Prog/Yjf2lBICow82/DSV46n3a2XT1Rqt/CeuhkO1fuacF7xydYhI0SwQx2Ue0jCyLtQzgKPFEO6ewv+bQ==",
"requires": {
"detect-indent": "~6.0.0",
"docopt": "~0.6.2",
@@ -3515,9 +3519,9 @@
"integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ=="
},
"parse-torrent-title": {
"version": "git+ssh://git@github.com/TheBeastLT/parse-torrent-title.git#0019426237d273b5214df1a6df81232d7f2ae3fb",
"integrity": "sha512-Ay79gA/yYof3TU+8ddeL3kBnt0Mw9oVe2RXyrkN51Xj1PeiqJws7JaGnSuB3bsSsJ3weFBnH9qeXEhhC+9WVPw==",
"from": "parse-torrent-title@git://github.com/TheBeastLT/parse-torrent-title.git#0019426237d273b5214df1a6df81232d7f2ae3fb",
"version": "git+ssh://git@github.com/TheBeastLT/parse-torrent-title.git#aeae91e512741910bc479ac57dcb0ce78bad3ee0",
"integrity": "sha512-Mys327xq6sHDWM4QVOMIZ7PVOY1WxoDI8ps4vjchwoyD26ezY2OQ7a1FsnZiaLBJTH9uCbLp/I4sKwsBcDinXQ==",
"from": "parse-torrent-title@git://github.com/TheBeastLT/parse-torrent-title.git#aeae91e512741910bc479ac57dcb0ce78bad3ee0",
"requires": {
"moment": "^2.24.0"
}
@@ -4149,14 +4153,14 @@
}
},
"ua-parser-js": {
"version": "1.0.33",
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.33.tgz",
"integrity": "sha512-RqshF7TPTE0XLYAqmjlu5cLLuGdKrNu9O1KLA/qp39QtbZwuzwv1dT46DZSopoUMsYgXpB3Cv8a03FI8b74oFQ=="
"version": "1.0.36",
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.36.tgz",
"integrity": "sha512-znuyCIXzl8ciS3+y3fHJI/2OhQIXbXw9MWC/o3qwyR+RGppjZHrM27CGFSKCJXi2Kctiz537iOu2KnXs1lMQhw=="
},
"underscore": {
"version": "1.13.1",
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.1.tgz",
"integrity": "sha512-hzSoAVtJF+3ZtiFX0VgfFPHEDRm7Y/QPjGyNo4TVdnDTdft3tr8hEkD25a1jC+TjTuE7tkHGKkhwCgs9dgBB2g=="
"version": "1.13.6",
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.6.tgz",
"integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A=="
},
"underscore-keypath": {
"version": "0.0.22",
@@ -4185,11 +4189,11 @@
"integrity": "sha512-HXgFDgDommxn5/bIv0cnQZsPhHDA90NPHD6+c/v21U5+Sx5hoP8+dP9IZXBU1gIfvdRfhG8cel9QNPeionfcCQ=="
},
"user-agents": {
"version": "1.0.1295",
"resolved": "https://registry.npmjs.org/user-agents/-/user-agents-1.0.1295.tgz",
"integrity": "sha512-9Rgw4X4FeHZ1d11X9zASRgLYlPKeBINZNRsitoL/RwNEXuNTnDiImMfqa5/+OoGBNRJ1kqxmH3bJL8Fu4m0VIg==",
"version": "1.0.1444",
"resolved": "https://registry.npmjs.org/user-agents/-/user-agents-1.0.1444.tgz",
"integrity": "sha512-6WXJ0RZuUKgif1rW5FN02HnpoJ8EzH6COQoXCiVStZEVPz+YnAx3iA48etY3ZD4UwueYN9ALC7j4ayHvYEh7tA==",
"requires": {
"dot-json": "^1.2.2",
"dot-json": "^1.3.0",
"lodash.clonedeep": "^4.5.0"
}
},

View File

@@ -20,14 +20,14 @@
"name-to-imdb": "^3.0.4",
"named-queue": "^2.2.1",
"offcloud-api": "^1.0.0",
"parse-torrent-title": "git://github.com/TheBeastLT/parse-torrent-title.git#0019426237d273b5214df1a6df81232d7f2ae3fb",
"parse-torrent-title": "git://github.com/TheBeastLT/parse-torrent-title.git#aeae91e512741910bc479ac57dcb0ce78bad3ee0",
"pg": "^8.10.0",
"premiumize-api": "^1.0.3",
"real-debrid-api": "git://github.com/TheBeastLT/node-real-debrid.git#d1f7eaa8593b947edbfbc8a92a176448b48ef445",
"request-ip": "^3.3.0",
"sequelize": "^6.31.1",
"stremio-addon-sdk": "^1.6.10",
"ua-parser-js": "^1.0.32",
"user-agents": "^1.0.1234"
"ua-parser-js": "^1.0.36",
"user-agents": "^1.0.1444"
}
}