Skip to content

Commit

Permalink
feat: add video & audio codec selection
Browse files Browse the repository at this point in the history
Selecting Preferred Video/Audio Codec
  • Loading branch information
jely2002 authored Jul 30, 2021
2 parents 8770676 + fd0d00f commit 5b98aaf
Show file tree
Hide file tree
Showing 14 changed files with 225 additions and 47 deletions.
2 changes: 1 addition & 1 deletion main.js
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ function startCriticalHandlers(env) {
queryManager.saveThumb(args.url);
break;
case "getSize":
return await queryManager.getSize(args.identifier, args.formatLabel, args.audioOnly, args.videoOnly, args.clicked);
return await queryManager.getSize(args.identifier, args.formatLabel, args.audioOnly, args.videoOnly, args.clicked, args.encoding, args.audioEncoding);
case "setSubtitles":
queryManager.setSubtitle(args);
break;
Expand Down
16 changes: 15 additions & 1 deletion modules/QueryManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,14 @@ class QueryManager {
playlistQuery.start().then((videos) => {
if(videos.length > this.environment.settings.splitMode) {
let totalFormats = [];
let totalAudioCodecs = [];
playlistVideo.videos = videos;
for(const video of videos) {
for(const audioCodec of video.audioCodecs) {
if(!totalAudioCodecs.includes(audioCodec)) {
totalAudioCodecs.push(audioCodec);
}
}
for(const format of video.formats) {
format.display_name = Format.getDisplayName(format.height, format.fps);
totalFormats.push(format);
Expand All @@ -89,6 +95,7 @@ class QueryManager {
formats: totalFormats,
subtitles: this.environment.mainDownloadSubs,
thumb: videos[0].thumbnail,
audioCodecs: totalAudioCodecs,
title: title,
length: videos.length,
uploader: uploader,
Expand Down Expand Up @@ -125,6 +132,7 @@ class QueryManager {
subtitles: video.downloadSubs,
loadSize: this.environment.settings.sizeMode === "full",
hasFilesizes: video.hasFilesizes,
audioCodecs: video.audioCodecs,
formats: formats,
selected_format_index: (video.hasMetadata) ? video.selected_format_index : null,
thumbnail: video.thumbnail
Expand All @@ -134,6 +142,8 @@ class QueryManager {

downloadVideo(args) {
let downloadVideo = this.getVideo(args.identifier);
downloadVideo.selectedEncoding = args.encoding;
downloadVideo.selectedAudioEncoding = args.audioEncoding;
downloadVideo.audioOnly = args.type === "audio";
downloadVideo.videoOnly = args.type === "videoOnly";
if(!downloadVideo.audioOnly) {
Expand Down Expand Up @@ -162,6 +172,8 @@ class QueryManager {
let videoMetadata = [];
for(const videoObj of args.videos) {
let video = this.getVideo(videoObj.identifier);
video.selectedEncoding = videoObj.encoding;
video.selectedAudioEncoding = videoObj.audioEncoding;
if(video.videos == null) {
if(video.downloaded || video.type !== "single") continue;
video.audioOnly = videoObj.type === "audio";
Expand Down Expand Up @@ -253,8 +265,10 @@ class QueryManager {
});
}

async getSize(identifier, formatLabel, audioOnly, videoOnly, clicked) {
async getSize(identifier, formatLabel, audioOnly, videoOnly, clicked, encoding, audioEncoding) {
const video = this.getVideo(identifier);
video.selectedEncoding = encoding;
video.selectedAudioEncoding = audioEncoding;
const cachedSize = this.getCachedSize(video, formatLabel, audioOnly, videoOnly);
if(cachedSize != null) {
//The size for this format was already looked up
Expand Down
23 changes: 23 additions & 0 deletions modules/Utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,22 @@ class Utils {
return filesizeDetected
}

static parseAvailableAudioCodecs(metadata) {
let codecs = [];
if(metadata.formats == null) {
console.error("No audio codecs could be found.")
return codecs;
}
for(let dataFormat of metadata.formats) {
if(dataFormat.height != null) continue;
const acodec = dataFormat.acodec;
if(acodec == null || acodec === "none") continue;
if(codecs.includes(acodec)) continue;
codecs.push(acodec);
}
return codecs;
}

static parseAvailableFormats(metadata) {
let formats = [];
let detectedFormats = [];
Expand All @@ -140,6 +156,13 @@ class Utils {
if(dataFormat.height == null) continue;
let format = new Format(dataFormat.height, dataFormat.fps, null, null);
if(!detectedFormats.includes(format.getDisplayName())) {
for(const dataFormat of metadata.formats) {
const vcodec = dataFormat.vcodec;
if(dataFormat.height !== format.height || dataFormat.fps !== format.fps) continue;
if(vcodec == null || vcodec === "none") continue;
if(format.encodings.includes(vcodec)) continue;
format.encodings.push(vcodec);
}
formats.push(format);
detectedFormats.push(format.getDisplayName());
}
Expand Down
35 changes: 22 additions & 13 deletions modules/download/DownloadQuery.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ class DownloadQuery extends Query {
'-o', output,
'--output-na-placeholder', ""
];
if(this.video.selectedAudioEncoding !== "none") {
args.push("-f");
args.push("bestaudio[acodec=" + this.video.selectedAudioEncoding + "]/bestaudio");
}
if(audioOutputFormat !== "none") {
args.push('--audio-format', audioOutputFormat);
}
Expand All @@ -39,15 +43,17 @@ class DownloadQuery extends Query {
} else {
if (this.video.formats.length !== 0) {
let format;
const encoding = this.video.selectedEncoding === "none" ? "" : "[vcodec=" + this.video.selectedEncoding + "]";
const audioEncoding = this.video.selectedAudioEncoding === "none" ? "" : "[acodec=" + this.video.selectedAudioEncoding + "]";
if(this.video.videoOnly) {
format = `bestvideo[height=${this.format.height}][fps=${this.format.fps}]/bestvideo[height=${this.format.height}]/best[height=${this.format.height}]/bestvideo/best`;
format = `bestvideo[height=${this.format.height}][fps=${this.format.fps}]${encoding}/bestvideo[height=${this.format.height}][fps=${this.format.fps}]/bestvideo[height=${this.format.height}]/best[height=${this.format.height}]/bestvideo/best`;
if (this.format.fps == null) {
format = `bestvideo[height=${this.format.height}]/best[height=${this.format.height}]/bestvideo/best`
format = `bestvideo[height=${this.format.height}]${encoding}/bestvideo[height=${this.format.height}]/best[height=${this.format.height}]/bestvideo/best`
}
} else {
format = `bestvideo[height=${this.format.height}][fps=${this.format.fps}]+${this.video.audioQuality}audio/bestvideo[height=${this.format.height}]+${this.video.audioQuality}audio/best[height=${this.format.height}]/bestvideo+bestaudio/best`;
format = `bestvideo[height=${this.format.height}][fps=${this.format.fps}]${encoding}+${this.video.audioQuality}audio${audioEncoding}/bestvideo[height=${this.format.height}][fps=${this.format.fps}]${encoding}+${this.video.audioQuality}audio/bestvideo[height=${this.format.height}][fps=${this.format.fps}]+${this.video.audioQuality}audio/bestvideo[height=${this.format.height}]+${this.video.audioQuality}audio/best[height=${this.format.height}]/bestvideo+bestaudio/best`;
if (this.format.fps == null) {
format = `bestvideo[height=${this.format.height}]+${this.video.audioQuality}audio/best[height=${this.format.height}]/bestvideo+bestaudio/best`
format = `bestvideo[height=${this.format.height}]${encoding}+${this.video.audioQuality}audio${audioEncoding}/bestvideo[height=${this.format.height}]${encoding}+${this.video.audioQuality}audio/bestvideo[height=${this.format.height}]+${this.video.audioQuality}audio/best[height=${this.format.height}]/bestvideo+bestaudio/best`
}
}
args = [
Expand Down Expand Up @@ -92,7 +98,10 @@ class DownloadQuery extends Query {
let result = null;
try {
result = await this.environment.downloadLimiter.schedule(() => this.start(this.url, args, (liveData) => {
this.video.setFilename(liveData);
const perLine = liveData.split("\n");
for(const line of perLine) {
this.video.setFilename(line);
}
if (!liveData.includes("[download]")) return;
if (!initialReset) {
initialReset = true;
Expand Down Expand Up @@ -121,24 +130,24 @@ class DownloadQuery extends Query {
this.environment.errorHandler.checkError(exception, this.video.identifier);
return exception;
}

if(this.environment.settings.audioOutputFormat === "none") {
await this.removeThumbnail();
if(this.video.audioOnly) {
await this.removeThumbnail(".jpg");
}
return result;
}

async removeThumbnail() {
async removeThumbnail(extension) {
const filename = this.video.filename;
if(filename != null) {
const filenameExt = path.basename(filename, path.extname(filename)) + ".jpg";
const filenameExt = path.basename(filename, path.extname(filename)) + extension;
const filenameAbs = path.join(this.video.downloadedPath, filenameExt);
try {
await fs.promises.unlink(filenameAbs);
} catch(e) {
console.error("Unable to remove failed image embed.");
console.error(filenameExt);
console.error(filenameAbs);
console.log("No left-over thumbnail found to remove. (" + filenameExt + ")")
if(extension !== ".webp") {
await this.removeThumbnail(".webp");
}
}
}
}
Expand Down
6 changes: 5 additions & 1 deletion modules/persistence/Settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ const os = require("os");
const fs = require("fs").promises;

class Settings {
constructor(paths, env, outputFormat, audioOutputFormat, downloadPath, proxy, rateLimit, autoFillClipboard, spoofUserAgent, validateCertificate, taskList, nameFormat, nameFormatMode, sizeMode, splitMode, maxConcurrent, updateBinary, updateApplication, cookiePath, statSend, downloadMetadata, downloadThumbnail, keepUnmerged, calculateTotalSize, theme) {
constructor(paths, env, outputFormat, audioOutputFormat, downloadPath, proxy, rateLimit, autoFillClipboard, spoofUserAgent, validateCertificate, enableEncoding, taskList, nameFormat, nameFormatMode, sizeMode, splitMode, maxConcurrent, updateBinary, updateApplication, cookiePath, statSend, downloadMetadata, downloadThumbnail, keepUnmerged, calculateTotalSize, theme) {
this.paths = paths;
this.env = env
this.outputFormat = outputFormat == null ? "none" : outputFormat;
Expand All @@ -13,6 +13,7 @@ class Settings {
this.autoFillClipboard = autoFillClipboard == null ? true : autoFillClipboard;
this.spoofUserAgent = spoofUserAgent == null ? true : spoofUserAgent;
this.validateCertificate = validateCertificate == null ? false : validateCertificate;
this.enableEncoding = enableEncoding == null ? false : enableEncoding;
this.taskList = taskList == null ? true : taskList;
this.nameFormat = nameFormat == null ? "%(title).200s-(%(height)sp%(fps).0d).%(ext)s" : nameFormat;
this.nameFormatMode = nameFormatMode == null ? "%(title).200s-(%(height)sp%(fps).0d).%(ext)s" : nameFormatMode;
Expand Down Expand Up @@ -45,6 +46,7 @@ class Settings {
data.autoFillClipboard,
data.spoofUserAgent,
data.validateCertificate,
data.enableEncoding,
data.taskList,
data.nameFormat,
data.nameFormatMode,
Expand Down Expand Up @@ -78,6 +80,7 @@ class Settings {
this.autoFillClipboard = settings.autoFillClipboard;
this.spoofUserAgent = settings.spoofUserAgent;
this.validateCertificate = settings.validateCertificate;
this.enableEncoding = settings.enableEncoding;
this.taskList = settings.taskList;
this.nameFormat = settings.nameFormat;
this.nameFormatMode = settings.nameFormatMode;
Expand Down Expand Up @@ -110,6 +113,7 @@ class Settings {
autoFillClipboard: this.autoFillClipboard,
spoofUserAgent: this.spoofUserAgent,
validateCertificate: this.validateCertificate,
enableEncoding: this.enableEncoding,
taskList: this.taskList,
nameFormat: this.nameFormat,
nameFormatMode: this.nameFormatMode,
Expand Down
17 changes: 14 additions & 3 deletions modules/size/SizeQuery.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,24 @@ class SizeQuery extends Query {
}

async connect() {
let formatArgument = `bestvideo[height=${this.format.height}][fps=${this.format.fps}]+${this.audioQuality}audio/bestvideo[height=${this.format.height}]+${this.audioQuality}audio/best[height=${this.format.height}]/bestvideo+bestaudio/best`;
if (this.format.fps == null) {
formatArgument = `bestvideo[height=${this.format.height}]+${this.audioQuality}audio/best[height=${this.format.height}]/bestvideo+bestaudio/best`;
const encoding = this.video.selectedEncoding === "none" ? "" : "[vcodec=" + this.video.selectedEncoding + "]";
const audioEncoding = this.video.selectedAudioEncoding === "none" ? "" : "[acodec=" + this.video.selectedAudioEncoding + "]";
let formatArgument = `bestvideo[height=${this.format.height}][fps=${this.format.fps}]${encoding}+${this.audioQuality}audio/bestvideo[height=${this.format.height}][fps=${this.format.fps}]+${this.audioQuality}audio/bestvideo[height=${this.format.height}]+${this.audioQuality}audio/best[height=${this.format.height}]/bestvideo+bestaudio/best`;
if(this.videoOnly) {
formatArgument = `bestvideo[height=${this.format.height}][fps=${this.format.fps}]${encoding}/bestvideo[height=${this.format.height}][fps=${this.format.fps}]/bestvideo[height=${this.format.height}]/best[height=${this.format.height}]/bestvideo/best`;
if (this.format.fps == null) {
formatArgument = `bestvideo[height=${this.format.height}]${encoding}/bestvideo[height=${this.format.height}]/best[height=${this.format.height}]/bestvideo/best`
}
} else {
formatArgument = `bestvideo[height=${this.format.height}][fps=${this.format.fps}]${encoding}+${this.video.audioQuality}audio${audioEncoding}/bestvideo[height=${this.format.height}][fps=${this.format.fps}]${encoding}+${this.video.audioQuality}audio/bestvideo[height=${this.format.height}][fps=${this.format.fps}]+${this.video.audioQuality}audio/bestvideo[height=${this.format.height}]+${this.video.audioQuality}audio/best[height=${this.format.height}]/bestvideo+bestaudio/best`;
if (this.format.fps == null) {
formatArgument = `bestvideo[height=${this.format.height}]${encoding}+${this.video.audioQuality}audio${audioEncoding}/bestvideo[height=${this.format.height}]${encoding}+${this.video.audioQuality}audio/bestvideo[height=${this.format.height}]+${this.video.audioQuality}audio/best[height=${this.format.height}]/bestvideo+bestaudio/best`
}
}
if(this.audioOnly) {
formatArgument = `bestvideo+${this.format}audio/bestvideo+bestaudio/best`;
}

let output = await this.environment.metadataLimiter.schedule(() => this.start(this.video.url, ["-J", "--flat-playlist", "-f", formatArgument]));
let data = JSON.parse(output);
let totalSize = 0;
Expand Down
4 changes: 3 additions & 1 deletion modules/types/Format.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ class Format {
this.fps = fps;
this.filesize = filesize;
this.filesize_label = filesize_label;
this.encodings = [];
}

getDisplayName() {
Expand All @@ -21,7 +22,8 @@ class Format {
fps: this.fps,
filesize: this.filesize,
filesize_label: this.filesize_label,
display_name: this.getDisplayName()
display_name: this.getDisplayName(),
encodings: this.encodings
};
}

Expand Down
4 changes: 3 additions & 1 deletion modules/types/Video.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ class Video {

setFilename(liveData) {
if(liveData.includes("[download] Destination: ")) {
this.filename = path.basename(liveData.replace("[download] Destination: ", ""));
const replaced = liveData.replace("[download] Destination: ", "");
this.filename = path.basename(replaced);
} else if(liveData.includes("[ffmpeg] Merging formats into \"")) {
const noPrefix = liveData.replace("[ffmpeg] Merging formats into \"", "");
this.filename = path.basename(noPrefix.trim().slice(0, -1));
Expand Down Expand Up @@ -106,6 +107,7 @@ class Video {

this.hasFilesizes = Utils.hasFilesizes(metadata)
this.formats = Utils.parseAvailableFormats(metadata);
this.audioCodecs = Utils.parseAvailableAudioCodecs(metadata);
this.selected_format_index = this.selectHighestQuality();
}

Expand Down
15 changes: 10 additions & 5 deletions renderer/renderer.css
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,8 @@ select {

.video-card {
max-width: 700px;
height: 140px;
min-height: 140px;
max-height: 155px;
margin: 1em 1em 0 1em;
background-color: var(--bg-color);
padding: 0.75em 0 0.75em 0.75em;
Expand All @@ -217,7 +218,7 @@ select {
white-space: nowrap;
overflow: hidden;
text-overflow: clip;
max-width: 330px;
max-width: 360px;
}

@media only screen and (max-width: 1483px) {
Expand All @@ -228,7 +229,7 @@ select {

.video-card .custom-select {
height: unset;
line-height: 1.2;
line-height: 1.3;
width: 160px;
margin-bottom: 1em;
}
Expand All @@ -255,6 +256,10 @@ select {
text-decoration: underline;
}

.video-card .metadata {
margin-top: 0.25em;
}

.video-card a {
cursor: pointer;
transition: 150ms color;
Expand Down Expand Up @@ -287,15 +292,15 @@ select {
border-radius: 3px;
object-fit: cover;
width: 100%;
max-height: 110px;
max-height: 130px;
background-color: var(--secondary-bg-color);
}

.video-card img:before {
content: ' ';
display: block;
position: absolute;
max-height: 110px;
max-height: 130px;
width: 100%;
object-fit: cover;
background-image: url("img/plain-placeholder.png");
Expand Down
Loading

0 comments on commit 5b98aaf

Please sign in to comment.