Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add seeders and provider back to the torrent info #72

Closed
wants to merge 2 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
216 changes: 109 additions & 107 deletions src/node/addon/src/lib/streamInfo.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,145 +4,147 @@ import { getSources } from './magnetHelper.js';
import { getSubtitles } from './subtitles.js';
import { Type } from './types.js';

const ADDON_NAME = 'knightcrawler';
const ADDON_NAME = 'ᴋɴɪɢʜᴛᴄʀᴀᴡʟᴇʀ';
const SIZE_DELTA = 0.02;
const UNKNOWN_SIZE = 300000000;
const CAM_SOURCES = ['CAM', 'TeleSync', 'TeleCine', 'SCR'];

export function toStreamInfo(record) {
const torrentInfo = titleParser.parse(record.torrent.title);
const fileInfo = titleParser.parse(record.title);
const sameInfo = !Number.isInteger(record.fileIndex)
|| Math.abs(record.size / record.torrent.size - 1) < SIZE_DELTA
|| record.title.includes(record.torrent.title);
const quality = getQuality(record, torrentInfo, fileInfo);
const hdrProfiles = torrentInfo.hdr || fileInfo.hdr || []
const title = joinDetailParts(
[
joinDetailParts([record.torrent.title.replace(/[, ]+/g, ' ')]),
joinDetailParts([!sameInfo && record.title || undefined]),
joinDetailParts([
joinDetailParts([formatSize(record.size)], '💾 ')
]),
joinDetailParts(getLanguages(record, torrentInfo, fileInfo), '', ' / '),
],
'',
'\n'
);
const name = joinDetailParts(
[
joinDetailParts([ADDON_NAME]),
joinDetailParts([quality, joinDetailParts(hdrProfiles, '', ' | ')])
],
'',
'\n'
);
const bingeGroupParts = getBingeGroupParts(record, sameInfo, quality, torrentInfo, fileInfo);
const bingeGroup = joinDetailParts(bingeGroupParts, "knightcrawler|", "|")
const behaviorHints = bingeGroup ? { bingeGroup } : undefined;
const torrentInfo = titleParser.parse(record.torrent.title);
const fileInfo = titleParser.parse(record.title);
const sameInfo = !Number.isInteger(record.fileIndex)
|| Math.abs(record.size / record.torrent.size - 1) < SIZE_DELTA
|| record.title.includes(record.torrent.title);
const quality = getQuality(record, torrentInfo, fileInfo);
const hdrProfiles = torrentInfo.hdr || fileInfo.hdr || []
const title = joinDetailParts(
[
joinDetailParts([record.torrent.title.replace(/[, ]+/g, ' ')]),
joinDetailParts([!sameInfo && record.title || undefined]),
joinDetailParts([
joinDetailParts([record.torrent.seeders], '👤 '),
joinDetailParts([formatSize(record.size)], '💾 '),
joinDetailParts([record.torrent.provider], '⚙️ ')
]),
joinDetailParts(getLanguages(record, torrentInfo, fileInfo), '', ' / '),
],
'',
'\n'
);
const name = joinDetailParts(
[
joinDetailParts([ADDON_NAME]),
joinDetailParts([quality, joinDetailParts(hdrProfiles, '', ' | ')])
],
'',
'\n'
);
const bingeGroupParts = getBingeGroupParts(record, sameInfo, quality, torrentInfo, fileInfo);
const bingeGroup = joinDetailParts(bingeGroupParts, "knightcrawler|", "|")
const behaviorHints = bingeGroup ? { bingeGroup } : undefined;

return cleanOutputObject({
name: name,
title: title,
infoHash: record.infoHash,
fileIdx: record.fileIndex,
behaviorHints: behaviorHints,
sources: getSources(record.torrent.trackers, record.infoHash),
subtitles: getSubtitles(record)
});
return cleanOutputObject({
name: name,
title: title,
infoHash: record.infoHash,
fileIdx: record.fileIndex,
behaviorHints: behaviorHints,
sources: getSources(record.torrent.trackers, record.infoHash),
subtitles: getSubtitles(record)
});
}

function getQuality(record, torrentInfo, fileInfo) {
if (CAM_SOURCES.includes(fileInfo.source)) {
return fileInfo.source;
}
if (CAM_SOURCES.includes(torrentInfo.source)) {
return torrentInfo.source;
}
const resolution = fileInfo.resolution || torrentInfo.resolution || record.torrent.resolution;
const source = fileInfo.source || torrentInfo.source;
return resolution || source;
if (CAM_SOURCES.includes(fileInfo.source)) {
return fileInfo.source;
}
if (CAM_SOURCES.includes(torrentInfo.source)) {
return torrentInfo.source;
}
const resolution = fileInfo.resolution || torrentInfo.resolution || record.torrent.resolution;
const source = fileInfo.source || torrentInfo.source;
return resolution || source;
}

function getLanguages(record, torrentInfo, fileInfo) {
const providerLanguages = record.torrent.languages && titleParser.parse(record.torrent.languages + '.srt').languages || [];
const torrentLanguages = torrentInfo.languages || [];
const fileLanguages = fileInfo.languages || [];
const dubbed = torrentInfo.dubbed || fileInfo.dubbed;
let languages = Array.from(new Set([].concat(torrentLanguages).concat(fileLanguages).concat(providerLanguages)));
if (record.kitsuId || record.torrent.type === Type.ANIME) {
// no need to display japanese for anime
languages = languages.concat(dubbed ? ['dubbed'] : [])
.filter(lang => lang !== 'japanese');
}
if (languages.length === 1 && languages.includes('english')) {
// no need to display languages if only english is present
languages = [];
}
if (languages.length === 0 && dubbed) {
// display dubbed only if there are no other languages defined for non anime
languages = ['dubbed'];
}
return mapLanguages(languages);
const providerLanguages = record.torrent.languages && titleParser.parse(record.torrent.languages + '.srt').languages || [];
const torrentLanguages = torrentInfo.languages || [];
const fileLanguages = fileInfo.languages || [];
const dubbed = torrentInfo.dubbed || fileInfo.dubbed;
let languages = Array.from(new Set([].concat(torrentLanguages).concat(fileLanguages).concat(providerLanguages)));
if (record.kitsuId || record.torrent.type === Type.ANIME) {
// no need to display japanese for anime
languages = languages.concat(dubbed ? ['dubbed'] : [])
.filter(lang => lang !== 'japanese');
}
if (languages.length === 1 && languages.includes('english')) {
// no need to display languages if only english is present
languages = [];
}
if (languages.length === 0 && dubbed) {
// display dubbed only if there are no other languages defined for non anime
languages = ['dubbed'];
}
return mapLanguages(languages);
}

function joinDetailParts(parts, prefix = '', delimiter = ' ') {
const filtered = parts.filter((part) => part !== undefined && part !== null).join(delimiter);
const filtered = parts.filter((part) => part !== undefined && part !== null).join(delimiter);

return filtered.length > 0 ? `${prefix}${filtered}` : undefined;
return filtered.length > 0 ? `${prefix}${filtered}` : undefined;
}

function formatSize(size) {
if (!size) {
return undefined;
}
if (size === UNKNOWN_SIZE) {
return undefined;
}
const i = size === 0 ? 0 : Math.floor(Math.log(size) / Math.log(1024));
return Number((size / Math.pow(1024, i)).toFixed(2)) + ' ' + ['B', 'kB', 'MB', 'GB', 'TB'][i];
if (!size) {
return undefined;
}
if (size === UNKNOWN_SIZE) {
return undefined;
}
const i = size === 0 ? 0 : Math.floor(Math.log(size) / Math.log(1024));
return Number((size / Math.pow(1024, i)).toFixed(2)) + ' ' + ['B', 'kB', 'MB', 'GB', 'TB'][i];
}

export function applyStaticInfo(streams) {
return streams.map(stream => enrichStaticInfo(stream));
return streams.map(stream => enrichStaticInfo(stream));
}

function enrichStaticInfo(stream) {
return enrichSubtitles(stream);
return enrichSubtitles(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;
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) {
if (record.torrent.type === Type.MOVIE) {
const source = torrentInfo.source || fileInfo.source
return [quality]
.concat(source !== quality ? source : [])
.concat(torrentInfo.codec || fileInfo.codec)
.concat(torrentInfo.bitDepth || fileInfo.bitDepth)
.concat(torrentInfo.hdr || fileInfo.hdr);
} else if (sameInfo) {
return [quality]
.concat(fileInfo.hdr)
.concat(fileInfo.group);
}
return [record.infoHash];
if (record.torrent.type === Type.MOVIE) {
const source = torrentInfo.source || fileInfo.source
return [quality]
.concat(source !== quality ? source : [])
.concat(torrentInfo.codec || fileInfo.codec)
.concat(torrentInfo.bitDepth || fileInfo.bitDepth)
.concat(torrentInfo.hdr || fileInfo.hdr);
} else if (sameInfo) {
return [quality]
.concat(fileInfo.hdr)
.concat(fileInfo.group);
}
return [record.infoHash];
}

function cleanOutputObject(object) {
return Object.fromEntries(Object.entries(object).filter(([_, v]) => v != null));
return Object.fromEntries(Object.entries(object).filter(([_, v]) => v != null));
}