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 }