New sync beta v2

Co-authored-by: AnidlSupport <AnidlSupport@users.noreply.github.com>
This commit is contained in:
AnimeDL 2023-08-21 17:36:55 -07:00
parent 57fe5c289c
commit 0ce179b67a
2 changed files with 97 additions and 42 deletions

3
.gitignore vendored
View file

@ -34,4 +34,5 @@ docker-compose.yml
crunchyendpoints
.vscode
/logs
/tmp/*/
/tmp/*/
/videos/*/

View file

@ -9,6 +9,7 @@ import { console } from './log';
import ffprobe from 'ffprobe';
import ffprobeStatic from 'ffprobe-static';
import lookssame from 'looks-same';
import readline from 'readline';
export type MergerInput = {
path: string,
@ -58,8 +59,9 @@ export type MergerOptions = {
}
}
const SECURITY_FRAMES = 30;
const MAX_OFFSET_SEC = 15;
const SECURITY_FRAMES = 10;
const MAX_OFFSET_SEC = 20;
const LIKENESS_TARGET = 0.990;
class Merger {
@ -86,56 +88,108 @@ class Merger {
return a.duration - b.duration;
});
for (const item of vnas) {
fs.mkdirSync(`tmp/temp-${item.lang.code}`, { recursive: true });
exec('ffmpeg', 'ffmpeg', `-i "${item.path}" -t ${MAX_OFFSET_SEC} tmp/temp-${item.lang.code}/%03d.png`);
}
fs.mkdirSync('tmp/main-frames', { recursive: true });
exec('ffmpeg', 'ffmpeg', `-hide_banner -loglevel error -i "${vnas[0].path}" -t ${MAX_OFFSET_SEC} tmp/main-frames/%03d.png`);
const start = vnas[0];
console.info(`Using ${start.lang.code} as the base for syncing`);
const items = fs.readdirSync(`tmp/temp-${start.lang.code}`);
const items = fs.readdirSync('tmp/main-frames');
console.info('Finding start frame from base...');
let offset = 0;
for (const [index, file] of items.entries()) {
const result = await lookssame(`tmp/main-frames/${file}`, 'tmp/main-frames/001.png', {tolerance: 3});
offset = index;
if (!result.equal) break;
}
items.splice(0, offset);
console.info(`Start frame from base is ${items[0]} - Generating differences`);
const filesToRemove: string[] = [];
itemLoop: for (const vna of vnas.slice(1)) {
console.info(`Trying to find delay for ${vna.lang.code}...`);
for (const [index, file] of items.entries()) {
console.info(`${Math.ceil( (index / items.length) * 100 )} %`);
outer: for (let i = 1; i <= items.length; i++) {
let number = i.toString();
number = '0'.repeat(3 - number.length) + number;
const result = await lookssame(`tmp/temp-${start.lang.code}/${file}`, `tmp/temp-${vna.lang.code}/${number}.png`);
if (result.equal) {
for (let b = i; b < Math.min(items.length, i + SECURITY_FRAMES); b++) {
let number = b.toString();
number = '0'.repeat(3 - number.length) + number;
if (!await lookssame(`tmp/temp-${start.lang.code}/${file}`, `tmp/temp-${vna.lang.code}/${number}.png`)) {
continue outer;
}
}
vna.delay = i;
const subtitles = this.options.subtitles.filter(sub => sub.language.code == vna.lang.code);
for (const [subIndex, sub] of subtitles.entries()) {
if (vna.isPrimary) {
subtitles[subIndex].delay = vna.delay;
subtitles[subIndex].frameRate = vna.frameRate;
} else if (sub.closedCaption) {
subtitles[subIndex].delay = vna.delay;
subtitles[subIndex].frameRate = vna.frameRate;
}
}
console.info(`Found ${vna.delay} frames delay for ${vna.lang.code}`);
continue itemLoop;
}
outer: for (let i = 1; i <= (items.length-offset); i++) {
const closeness = [];
exec('ffmpeg', 'ffmpeg', `-hide_banner -loglevel error -i tmp/main-frames/${items[i]} -i "${vna.path}" -t ${MAX_OFFSET_SEC} -lavfi "ssim=f=tmp/stats-${i}.log;[0:v][1:v]psnr" -f null -`);
filesToRemove.push(`tmp/stats-${i}.log`);
const fileStream = fs.createReadStream(`tmp/stats-${i}.log`);
const rl = readline.createInterface({
input: fileStream
});
for await (const line of rl) {
const values = line.split(' ');
closeness.push({
frame: parseFloat(values[0].replace('n:', '')),
Y: parseFloat(values[1].replace('Y:', '')),
U: parseFloat(values[2].replace('U:', '')),
V: parseFloat(values[3].replace('V:', '')),
overall: parseFloat(values[4].replace('All:', '')),
calc: parseFloat(values[5].replace('(', '').replace(')', ''))
});
}
closeness.sort(function(a, b) {
return b.overall - a.overall;
});
if (closeness[0].overall > LIKENESS_TARGET) {
closeness.sort(function(a, b) {
return a.frame - b.frame;
});
for (const frame of closeness) {
if (frame.overall > LIKENESS_TARGET) {
for (let b = i; b < Math.min(items.length, i + SECURITY_FRAMES); b++) {
console.info('Verifying match...');
exec('ffmpeg', 'ffmpeg', `-hide_banner -loglevel error -i tmp/main-frames/${items[b]} -i "${vna.path}" -t ${MAX_OFFSET_SEC} -lavfi "ssim=f=tmp/stats-${i}-${b}.log;[0:v][1:v]psnr" -f null -`);
filesToRemove.push(`tmp/stats-${i}-${b}.log`);
const fileStream = fs.createReadStream(`tmp/stats-${i}-${b}.log`);
const rl = readline.createInterface({
input: fileStream
});
for await (const line of rl) {
const values = line.split(' ');
const securityframe = {
frame: parseFloat(values[0].replace('n:', '')),
Y: parseFloat(values[1].replace('Y:', '')),
U: parseFloat(values[2].replace('U:', '')),
V: parseFloat(values[3].replace('V:', '')),
overall: parseFloat(values[4].replace('All:', '')),
calc: parseFloat(values[5].replace('(', '').replace(')', ''))
};
if (securityframe.frame === (frame.frame + b))
if (!(securityframe.overall > LIKENESS_TARGET)) {
console.info('Match failed, trying to find another match.');
continue outer;
}
}
console.info('Match Succesful');
}
vna.delay = frame.frame - offset;
const subtitles = this.options.subtitles.filter(sub => sub.language.code == vna.lang.code);
for (const [subIndex, sub] of subtitles.entries()) {
if (vna.isPrimary) {
subtitles[subIndex].delay = vna.delay;
subtitles[subIndex].frameRate = vna.frameRate;
} else if (sub.closedCaption) {
subtitles[subIndex].delay = vna.delay;
subtitles[subIndex].frameRate = vna.frameRate;
}
}
console.info(`Found ${vna.delay} frames delay for ${vna.lang.code}`);
break;
}
}
} else {
continue outer;
}
continue itemLoop;
}
console.error(`Unable to find delay for ${vna.lang.code}`);
}
for (const item of vnas) {
fs.rmSync(`tmp/temp-${item.lang.code}`, { recursive: true, force: true });
//Remove temp files
fs.rmSync('tmp/main-frames/', { recursive: true, force: true });
for (const file of filesToRemove) {
fs.rmSync(file, { recursive: true, force: true });
}
console.info('Processed all files to find a delay.');
console.info('Processed all files to find delays.');
}
}