Skip to content

Commit

Permalink
add basic multi-language detection
Browse files Browse the repository at this point in the history
  • Loading branch information
sylviiu committed Jan 14, 2024
1 parent 4ae66e2 commit 3f3106c
Show file tree
Hide file tree
Showing 5 changed files with 216 additions and 9 deletions.
89 changes: 88 additions & 1 deletion html/afterload/conversionOptions.js
Original file line number Diff line number Diff line change
Expand Up @@ -540,7 +540,7 @@ const conversionOptions = (node, info, colorScheme) => {
if(info.asr) node.querySelector(`#audioSampleRate`).placeholder = `Sample Rate (${info.asr/1000}k)`
if(info.abr) node.querySelector(`#audioBitrate`).placeholder = `Bitrate (${info.abr}k)`;

const metaButtons = node.querySelector(`#metadataOptions`).querySelectorAll(`.btn`)
const metaButtons = node.querySelector(`#metadataOptions`).querySelectorAll(`.btn`);

if(hasFFmpeg) {
if(info._platform == `file`) {
Expand Down Expand Up @@ -605,6 +605,93 @@ const conversionOptions = (node, info, colorScheme) => {
}
}
});

const languages = (info.quickQualities[1]?.langs) || (info.quickQualities[0]?.audioFormat.langs) || false;

console.log(`languages`, info.quickQualities)

if(languages) {
const primaryLanguage = languages.sort((a, b) => (b.language_preference || 0) - (a.language_preference || 0))[0];

console.log(`languages | primaryLanguage`, primaryLanguage)

const languageOptionsTxt = node.querySelector(`#saveLanguageText`);
const languageOptions = node.querySelector(`#languageOptions`);

if(languageOptionsTxt.classList.contains(`d-none`)) languageOptionsTxt.classList.remove(`d-none`);
if(languageOptions.classList.contains(`d-none`)) {
console.log(`languages | adding buttons`)

const btn = languageOptions.querySelector(`button`);
const button = btn.cloneNode(true);
btn.remove();

languages.forEach((l, i) => {
const id = `${l.language || l.format_note || i}`;

const thisBtn = button.cloneNode(true);

thisBtn.id = id;
thisBtn.innerHTML = thisBtn.innerHTML.replace(`(language)`, id);
thisBtn.setAttribute(`value`, `true`);

const thisIcon = thisBtn.querySelector(`#icon`);

thisBtn.onclick = () => {
if(thisBtn.getAttribute(`value`) == `true`) {
thisBtn.setAttribute(`value`, `false`);
if(thisIcon.classList.contains(`fa-check-circle`)) {
thisIcon.classList.remove(`fa-check-circle`);
thisIcon.classList.add(`fa-times-circle`);
}

anime.remove(thisBtn);
anime({
targets: thisBtn,
scale: 0.9,
opacity: 0.65,
duration: 300,
easing: `easeOutExpo`,
})
} else {
thisBtn.setAttribute(`value`, `true`);
if(thisIcon.classList.contains(`fa-times-circle`)) {
thisIcon.classList.remove(`fa-times-circle`);
thisIcon.classList.add(`fa-check-circle`);
}

anime.remove(thisBtn);
anime({
targets: thisBtn,
scale: 1,
opacity: 1,
duration: 300,
easing: `easeOutExpo`,
})
}
}

if(primaryLanguage.format_id !== l.format_id) {
thisBtn.setAttribute(`value`, `false`);
if(thisIcon.classList.contains(`fa-check-circle`)) {
thisIcon.classList.remove(`fa-check-circle`);
thisIcon.classList.add(`fa-times-circle`);
}
thisBtn.style.transform = `scale(0.9)`;
thisBtn.style.opacity = 0.65;
}

console.log(`languages | adding button "${id}"`)

languageOptions.appendChild(thisBtn);
});

console.log(`languages | showing language options`)

languageOptions.classList.remove(`d-none`);
//languageOptions.classList.add(`d-flex`);
}
}
} else {
let sentNotif = false;
metaButtons.forEach(m => {
Expand Down
9 changes: 9 additions & 0 deletions html/afterload/getSaveOptions.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,14 @@ const getSaveOptions = (node, info, overrideDownloadObj, {

console.log(`addMetadata`, addMetadata);

let addLanguage = {};

if(node.querySelector(`#languageOptions`) && !node.querySelector(`#languageOptions`).classList.contains(`d-none`)) node.querySelector(`#languageOptions`).querySelectorAll(`.btn`).forEach(m => {
const active = m.getAttribute(`value`) == `true`;
console.log(`${m.id}: ${active}`)
addLanguage[m.id] = active
});

let value = null;

if(info.entries) {
Expand All @@ -72,6 +80,7 @@ const getSaveOptions = (node, info, overrideDownloadObj, {
convert: convert ? convertInfo : null,
filePath: (node.querySelector(`#saveLocation`) ? node.querySelector(`#saveLocation`).value : null) || null,
addMetadata,
addLanguage,
info: info
}, overrideDownloadObj && typeof overrideDownloadObj == `object` ? overrideDownloadObj : {});
};
Expand Down
2 changes: 2 additions & 0 deletions html/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ <h6 id="fileOptionsText" class="ez-text" style="color: rgba(255,255,255,0.82);te
</div>
<h6 id="saveMetadataText" class="ez-text" style="color: rgba(255,255,255,0.82);text-align: right;margin-bottom: 3px;width: 100%;">Save Metadata</h6>
<div class="d-flex justify-content-end align-items-center" id="metadataOptions" style="width: 100%;margin-bottom: 8px;"><button class="btn btn-primary ez-default2" id="tags" type="button" style="height: 32px;padding: 2px 13px;color: rgb(0,0,0);text-align: right;background: rgba(255,255,255,0.85);border-width: 0px;border-radius: 18px;margin: 0px 3px;font-size: 14px;" value="true"><i class="fas fa-check-circle" id="icon" style="margin-right: 8px;margin-top: 2px;"></i>Save Tags</button><button class="btn btn-primary ez-default2" id="opt-saveAsAlbum" type="button" style="height: 32px;padding: 2px 13px;color: rgb(0,0,0);text-align: right;background: rgba(255,255,255,0.85);border-width: 0px;border-radius: 18px;margin: 0px 3px;font-size: 14px;" value="true"><i class="fas fa-check-circle" id="icon" style="margin-right: 8px;margin-top: 2px;"></i>Save as Album</button><button class="btn btn-primary ez-default2" id="thumbnail" type="button" style="height: 32px;padding: 2px 13px;color: rgb(0,0,0);text-align: right;background: rgba(255,255,255,0.85);border-width: 0px;border-radius: 18px;margin: 0px 3px;margin-right: 0px;font-size: 14px;" value="true"><i class="fas fa-check-circle" id="icon" style="margin-right: 8px;margin-top: 2px;"></i>Save Thumbnail</button></div>
<h6 id="saveLanguageText" class="d-none ez-text" style="color: rgba(255,255,255,0.82);text-align: right;margin-bottom: 3px;width: 100%;">Audio Language Tracks (not functional yet)</h6>
<div class="d-none justify-content-end align-items-center" id="languageOptions" style="width: 100%;margin-bottom: 8px;"><button class="btn btn-primary ez-default2" id="lang" type="button" style="height: 32px;padding: 2px 13px;color: rgb(0,0,0);text-align: right;background: rgba(255,255,255,0.85);border-width: 0px;border-radius: 18px;margin: 0px 3px;font-size: 14px;" value="true"><i class="fas fa-check-circle" id="icon" style="margin-right: 8px;margin-top: 2px;"></i>(language)</button></div>
<div class="d-flex flex-column justify-content-between align-items-center d-none" id="ffmpegOptions" style="width: 100%;margin-bottom: 8px;">
<h6 id="ffmpegOptionsText" class="ez-text" style="color: rgba(255,255,255,0.82);text-align: right;margin-bottom: 3px;width: 100%;">Conversion Options</h6>
<div class="row row-cols-2 d-flex justify-content-between" id="conversionPresets" style="width: 100%;">
Expand Down
54 changes: 54 additions & 0 deletions util/matchingChars.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
module.exports = (arr) => {
const reference = arr.shift();

let debug = {
found: [],
words: [],
};

let matching = [];

reference.split(' ').forEach((word, wordIndex) => {
let w = {
word,
match: [],
matching: []
};

const letters = word.split('');

for(const i in letters) {
const forwardMatches = arr.map(str => str.split(` `)[wordIndex]?.[i] === letters[i]);
const backwardMatches = arr.map(str => str.split(` `).at(-1 - wordIndex)?.[i] === letters[i]);

const matches = forwardMatches.includes(true) || backwardMatches.includes(true);

w.matching[i] = [letters[i], forwardMatches, backwardMatches, matches]

if(matches && w.match) {
w.match.push(letters[i]);
} else if(w.match) {
w.failed = w.match;
w.match = null;
};
};

if(w.match) {
const str = w.match.join('');

matching.push(str);
debug.found.push([letters, str]);

w.matched = w.match.join('');
}

debug.words.push(w);
});

debug.matching = matching;

return {
debug,
matched: matching.join(` `)
}
}
71 changes: 63 additions & 8 deletions util/ytdlp.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const { filterHeaders } = require(`./ytdlpUtil/headers`);
const durationCurve = require(`./durationCurve`);
const recursiveAssign = require(`./recursiveAssign`);
const anyIsTrue = require(`./anyIsTrue`);
const matchingChars = require(`./matchingChars`);

const sortByQuality = (a, b) => {
let retVal = 0;
Expand Down Expand Up @@ -59,6 +60,7 @@ sortByQuality.video = (a, b) => {

const outputTemplateRegex = /%\(\s*([^)]+)\s*\)s/g;
const genericURLRegex = /^https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)$/i;
const formatExtensionRegex = /^[^\s]+-(\d+)$/; // used if multiple languages are present, and will be matched against the format id

const platforms = fs.readdirSync(getPath(`./util/platforms`)).map(f =>
Object.assign(require(`../util/platforms/${f}`), {
Expand Down Expand Up @@ -570,7 +572,53 @@ module.exports = {
return o;
});

d.formats = [...modifiedFormats.filter(o => o.audio && o.video).sort(sortByQuality), ...modifiedFormats.filter(o => o.audio && !o.video).sort(sortByQuality), ...modifiedFormats.filter(o => !o.audio && o.video).sort(sortByQuality), ...modifiedFormats.filter(o => !o.audio && !o.video).sort(sortByQuality)];
const useFormats = [
...modifiedFormats.filter(o => o.audio && o.video).sort(sortByQuality),
...modifiedFormats.filter(o => o.audio && !o.video).sort(sortByQuality),
...modifiedFormats.filter(o => !o.audio && o.video).sort(sortByQuality),
...modifiedFormats.filter(o => !o.audio && !o.video).sort(sortByQuality)
];

if(d.formats.filter(o => o.format_id.match(formatExtensionRegex)).length > 0) {
// if there are multiple languages, group them together

const newFormats = [];
const filterOut = [];

for(const format of d.formats.filter(o => o.format_id.split(`-`).at(-1) == `0`)) {
const allLangs = d.formats.filter(o => o.format_id.split(`-`)[0] == format.format_id.split(`-`)[0]);

const commonNote = matchingChars(allLangs.map(o => o.format_note));

filterOut.push(...allLangs.map(o => o.format_id), format.format_id);

newFormats.push({
...format,
format_id: format.format_id.split(`-`).slice(0, -1).join(`-`),
format_note: (commonNote?.matched && `${commonNote.matched} (${allLangs.length} languages)`) || `${allLangs.length} languages`,
language: undefined,
language_preference: undefined,
langs: allLangs
});
};

newFormats.push(...d.formats.filter(o => !filterOut.includes(o.format_id)));

d.formats = [
...newFormats.filter(o => o.audio && o.video).sort(sortByQuality),
...newFormats.filter(o => o.audio && !o.video).sort(sortByQuality),
...newFormats.filter(o => !o.audio && o.video).sort(sortByQuality),
...newFormats.filter(o => !o.audio && !o.video).sort(sortByQuality)
];
} else {
d.formats = useFormats
};

d.quickQualities = [
module.exports.getFormat({info: d, format: `bv*+ba/b`}),
module.exports.getFormat({info: d, format: `ba`}),
module.exports.getFormat({info: d, format: `bv`}),
];
}

d.duration = time(totalTime || (d.originalDuration ? d.originalDuration*1000 : 0) || 0);
Expand Down Expand Up @@ -1261,33 +1309,40 @@ module.exports = {
let useFormatsArr = info.formats.slice();

if(info.is_live && !useFormatsArr.find(f => f.format_id == format)) {
useFormat = useFormatsArr[depth] || useFormatsArr[0]
useFormat = useFormatsArr[depth] || useFormatsArr.at(-1)
} else if(format == `bv*+ba/b`) {
useFormat = module.exports.getFormat({info, format: `bv`, ext, depth});

if(useFormat) {
useFormat.audioFormat = module.exports.getFormat({info, format: `ba`, depth});
if(useFormat.audioFormat.format_id == useFormat.format_id) delete useFormat.audioFormat;
if(useFormat.audioFormat.format_id == useFormat.format_id) useFormat.audioFormat = undefined;
} else {
useFormat = module.exports.getFormat({info, format: `ba`, depth});
}
} else if(format == `ba`) {
useFormatsArr = useFormatsArr.filter(f => f.audio).sort(sortByQuality.audio)
if(useFormatsArr.find(o => o.langs)) {
useFormatsArr = [ ...useFormatsArr.filter(f => f.audio && !f.video).sort(sortByQuality.audio), ...useFormatsArr.filter(f => f.audio).sort(sortByQuality.audio) ]
useFormatsArr = useFormatsArr.filter((o, i) => useFormatsArr.findIndex(f => f.format_id == o.format_id) == i)
} else {
useFormatsArr = [ ...useFormatsArr.filter(f => f.audio && !f.video), ...useFormatsArr.filter(f => f.audio) ]
useFormatsArr = useFormatsArr.filter((o, i) => useFormatsArr.findIndex(f => f.format_id == o.format_id) == i).sort(sortByQuality.audio)
}

if(ext && useFormatsArr.filter(o => o.ext == ext).length > 0) useFormatsArr = useFormatsArr.filter(o => o.ext == ext);

useFormat = useFormatsArr[depth]
if(!useFormat) useFormat = useFormatsArr[0]
if(!useFormat) useFormat = useFormatsArr.at(-1)
} else if(format == `bv`) {
useFormatsArr = useFormatsArr.filter(f => f.video).sort(sortByQuality.video)
useFormatsArr = [ ...useFormatsArr.filter(f => f.video && !f.audio), ...useFormatsArr.filter(f => f.video) ]
useFormatsArr = useFormatsArr.filter((o, i) => useFormatsArr.findIndex(f => f.format_id == o.format_id) == i).sort(sortByQuality.video)

if(ext && useFormatsArr.filter(o => o.ext == ext).length > 0) useFormatsArr = useFormatsArr.filter(o => o.ext == ext);

useFormat = useFormatsArr.filter(f => f.video && !f.audio).sort((a,b) => ((b.vbr || 1) * (b.fps || 1) * (b.width || 1) * (b.height || 1)) - ((a.vbr || 1) * (a.fps || 1) * (a.width || 1) * (a.height || 1)))[depth]
if(!useFormat) useFormat = useFormatsArr.filter(f => f.video && !f.audio)[depth]
if(!useFormat) useFormat = useFormatsArr[depth]
if(!useFormat) useFormat = useFormatsArr[0]
} else useFormat = useFormatsArr.find(f => f.format_id == format) || useFormatsArr.filter(o => typeof o.quality == `number`).sort((a,b) => a.quality - b.quality)[depth] || useFormatsArr[depth] || useFormatsArr[0];
if(!useFormat) useFormat = useFormatsArr.at(-1)
} else useFormat = useFormatsArr.find(f => f.format_id == format) || useFormatsArr.filter(o => typeof o.quality == `number`).sort((a,b) => a.quality - b.quality)[depth] || useFormatsArr[depth] || useFormatsArr.at(-1);
};

return useFormat || null;
Expand Down

0 comments on commit 3f3106c

Please sign in to comment.