mirror of
https://github.com/Tzahi12345/YoutubeDL-Material.git
synced 2026-03-07 12:00:01 +03:00
Skipped tests just meant for manual testing mocha now used prod mode Added automated tests for PRs Fixed backupdb and youtube-dl tests in gh actions Fixed downloader tests Removed erroneous logs in tests Updated mocha.yml Removed last console.log in tests Increased timeout for download video test Automated tests now force stop after tests complete Replaced download test url with shorter video Skip download test... for now increased download test threshold added more timing information to tests unskip download test Bump docker/setup-buildx-action from 1 to 2 in /.github/workflows Bumps [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) from 1 to 2. - [Release notes](https://github.com/docker/setup-buildx-action/releases) - [Commits](https://github.com/docker/setup-buildx-action/compare/v1...v2) --- updated-dependencies: - dependency-name: docker/setup-buildx-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> Bump actions/setup-node from 2 to 3 in /.github/workflows Bumps [actions/setup-node](https://github.com/actions/setup-node) from 2 to 3. - [Release notes](https://github.com/actions/setup-node/releases) - [Commits](https://github.com/actions/setup-node/compare/v2...v3) --- updated-dependencies: - dependency-name: actions/setup-node dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> Bump actions/upload-artifact from 1 to 3 in /.github/workflows Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 1 to 3. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/v1...v3) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> Bump docker/login-action from 1 to 2 in /.github/workflows Bumps [docker/login-action](https://github.com/docker/login-action) from 1 to 2. - [Release notes](https://github.com/docker/login-action/releases) - [Commits](https://github.com/docker/login-action/compare/v1...v2) --- updated-dependencies: - dependency-name: docker/login-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> Removed additional download button for paused download Combined output json processing from youtube-dl between subscriptions and one-off downloads Added workaround for missing video in playlist to one-off downloads Fixed issue of case where output is empty Download video code cleanup Fixed download video test by overriding youtube-dl download method with custom method Adds ffmpeg to automated tests Removed test mp3/mp4 file from repo
528 lines
22 KiB
JavaScript
528 lines
22 KiB
JavaScript
const fs = require('fs-extra');
|
|
const path = require('path');
|
|
const youtubedl = require('youtube-dl');
|
|
|
|
const config_api = require('./config');
|
|
const archive_api = require('./archive');
|
|
const utils = require('./utils');
|
|
const logger = require('./logger');
|
|
const CONSTS = require('./consts');
|
|
|
|
const debugMode = process.env.YTDL_MODE === 'debug';
|
|
|
|
const db_api = require('./db');
|
|
const downloader_api = require('./downloader');
|
|
|
|
exports.subscribe = async (sub, user_uid = null, skip_get_info = false) => {
|
|
const result_obj = {
|
|
success: false,
|
|
error: ''
|
|
};
|
|
return new Promise(async resolve => {
|
|
// sub should just have url and name. here we will get isPlaylist and path
|
|
sub.isPlaylist = sub.isPlaylist || sub.url.includes('playlist');
|
|
sub.videos = [];
|
|
|
|
let url_exists = !!(await db_api.getRecord('subscriptions', {url: sub.url, user_uid: user_uid}));
|
|
|
|
if (!sub.name && url_exists) {
|
|
logger.error(`Sub with the same URL "${sub.url}" already exists -- please provide a custom name for this new subscription.`);
|
|
result_obj.error = 'Subcription with URL ' + sub.url + ' already exists! Custom name is required.';
|
|
resolve(result_obj);
|
|
return;
|
|
}
|
|
|
|
sub['user_uid'] = user_uid ? user_uid : undefined;
|
|
await db_api.insertRecordIntoTable('subscriptions', JSON.parse(JSON.stringify(sub)));
|
|
|
|
let success = skip_get_info ? true : await getSubscriptionInfo(sub);
|
|
exports.writeSubscriptionMetadata(sub);
|
|
|
|
if (success) {
|
|
if (!sub.paused) exports.getVideosForSub(sub, user_uid);
|
|
} else {
|
|
logger.error('Subscribe: Failed to get subscription info. Subscribe failed.')
|
|
}
|
|
|
|
result_obj.success = success;
|
|
result_obj.sub = sub;
|
|
resolve(result_obj);
|
|
});
|
|
|
|
}
|
|
|
|
async function getSubscriptionInfo(sub) {
|
|
// get videos
|
|
let downloadConfig = ['--dump-json', '--playlist-end', '1'];
|
|
let useCookies = config_api.getConfigItem('ytdl_use_cookies');
|
|
if (useCookies) {
|
|
if (await fs.pathExists(path.join(__dirname, 'appdata', 'cookies.txt'))) {
|
|
downloadConfig.push('--cookies', path.join('appdata', 'cookies.txt'));
|
|
} else {
|
|
logger.warn('Cookies file could not be found. You can either upload one, or disable \'use cookies\' in the Advanced tab in the settings.');
|
|
}
|
|
}
|
|
|
|
return new Promise(async resolve => {
|
|
youtubedl.exec(sub.url, downloadConfig, {maxBuffer: Infinity}, async (err, output) => {
|
|
if (debugMode) {
|
|
logger.info('Subscribe: got info for subscription ' + sub.id);
|
|
}
|
|
if (err) {
|
|
logger.error(err.stderr);
|
|
resolve(false);
|
|
} else if (output) {
|
|
if (output.length === 0 || (output.length === 1 && output[0] === '')) {
|
|
logger.verbose('Could not get info for ' + sub.id);
|
|
resolve(false);
|
|
}
|
|
for (let i = 0; i < output.length; i++) {
|
|
let output_json = null;
|
|
try {
|
|
output_json = JSON.parse(output[i]);
|
|
} catch(e) {
|
|
output_json = null;
|
|
}
|
|
if (!output_json) {
|
|
continue;
|
|
}
|
|
if (!sub.name) {
|
|
if (sub.isPlaylist) {
|
|
sub.name = output_json.playlist_title ? output_json.playlist_title : output_json.playlist;
|
|
} else {
|
|
sub.name = output_json.uploader;
|
|
}
|
|
// if it's now valid, update
|
|
if (sub.name) {
|
|
let sub_name = sub.name;
|
|
const sub_name_exists = await db_api.getRecord('subscriptions', {name: sub.name, isPlaylist: sub.isPlaylist, user_uid: sub.user_uid});
|
|
if (sub_name_exists) sub_name += ` - ${sub.id}`;
|
|
await db_api.updateRecord('subscriptions', {id: sub.id}, {name: sub_name});
|
|
}
|
|
}
|
|
|
|
// TODO: get even more info
|
|
|
|
resolve(true);
|
|
}
|
|
resolve(false);
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
exports.unsubscribe = async (sub, deleteMode, user_uid = null) => {
|
|
let basePath = null;
|
|
if (user_uid)
|
|
basePath = path.join(config_api.getConfigItem('ytdl_users_base_path'), user_uid, 'subscriptions');
|
|
else
|
|
basePath = config_api.getConfigItem('ytdl_subscriptions_base_path');
|
|
|
|
let id = sub.id;
|
|
|
|
const sub_files = await db_api.getRecords('files', {sub_id: id});
|
|
for (let i = 0; i < sub_files.length; i++) {
|
|
const sub_file = sub_files[i];
|
|
if (config_api.descriptors[sub_file['uid']]) {
|
|
try {
|
|
for (let i = 0; i < config_api.descriptors[sub_file['uid']].length; i++) {
|
|
config_api.descriptors[sub_file['uid']][i].destroy();
|
|
}
|
|
} catch(e) {
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
await db_api.removeRecord('subscriptions', {id: id});
|
|
await db_api.removeAllRecords('files', {sub_id: id});
|
|
|
|
// failed subs have no name, on unsubscribe they shouldn't error
|
|
if (!sub.name) {
|
|
return;
|
|
}
|
|
|
|
const appendedBasePath = getAppendedBasePath(sub, basePath);
|
|
if (deleteMode && (await fs.pathExists(appendedBasePath))) {
|
|
await fs.remove(appendedBasePath);
|
|
}
|
|
|
|
await db_api.removeAllRecords('archives', {sub_id: sub.id});
|
|
}
|
|
|
|
exports.deleteSubscriptionFile = async (sub, file, deleteForever, file_uid = null, user_uid = null) => {
|
|
if (typeof sub === 'string') {
|
|
// TODO: fix bad workaround where sub is a sub_id
|
|
sub = await db_api.getRecord('subscriptions', {sub_id: sub});
|
|
}
|
|
// TODO: combine this with deletefile
|
|
let basePath = null;
|
|
basePath = user_uid ? path.join(config_api.getConfigItem('ytdl_users_base_path'), user_uid, 'subscriptions')
|
|
: config_api.getConfigItem('ytdl_subscriptions_base_path');
|
|
const appendedBasePath = getAppendedBasePath(sub, basePath);
|
|
const name = file;
|
|
let retrievedID = null;
|
|
let retrievedExtractor = null;
|
|
|
|
await db_api.removeRecord('files', {uid: file_uid});
|
|
|
|
let filePath = appendedBasePath;
|
|
const ext = (sub.type && sub.type === 'audio') ? '.mp3' : '.mp4'
|
|
var jsonPath = path.join(__dirname,filePath,name+'.info.json');
|
|
var videoFilePath = path.join(__dirname,filePath,name+ext);
|
|
var imageFilePath = path.join(__dirname,filePath,name+'.jpg');
|
|
var altImageFilePath = path.join(__dirname,filePath,name+'.webp');
|
|
|
|
const [jsonExists, videoFileExists, imageFileExists, altImageFileExists] = await Promise.all([
|
|
fs.pathExists(jsonPath),
|
|
fs.pathExists(videoFilePath),
|
|
fs.pathExists(imageFilePath),
|
|
fs.pathExists(altImageFilePath),
|
|
]);
|
|
|
|
if (jsonExists) {
|
|
const info_json = fs.readJSONSync(jsonPath);
|
|
retrievedID = info_json['id'];
|
|
retrievedExtractor = info_json['extractor'];
|
|
await fs.unlink(jsonPath);
|
|
}
|
|
|
|
if (imageFileExists) {
|
|
await fs.unlink(imageFilePath);
|
|
}
|
|
|
|
if (altImageFileExists) {
|
|
await fs.unlink(altImageFilePath);
|
|
}
|
|
|
|
if (videoFileExists) {
|
|
await fs.unlink(videoFilePath);
|
|
if ((await fs.pathExists(jsonPath)) || (await fs.pathExists(videoFilePath))) {
|
|
return false;
|
|
} else {
|
|
// check if the user wants the video to be redownloaded (deleteForever === false)
|
|
if (deleteForever) {
|
|
// ensure video is in the archives
|
|
const exists_in_archive = await archive_api.existsInArchive(retrievedExtractor, retrievedID, sub.type, user_uid, sub.id);
|
|
if (!exists_in_archive) {
|
|
await archive_api.addToArchive(retrievedExtractor, retrievedID, sub.type, file.title, user_uid, sub.id);
|
|
}
|
|
} else {
|
|
await archive_api.removeFromArchive(retrievedExtractor, retrievedID, sub.type, user_uid, sub.id);
|
|
}
|
|
return true;
|
|
}
|
|
} else {
|
|
// TODO: tell user that the file didn't exist
|
|
return true;
|
|
}
|
|
}
|
|
|
|
exports.getVideosForSub = async (sub, user_uid = null) => {
|
|
const latest_sub_obj = await exports.getSubscription(sub.id);
|
|
if (!latest_sub_obj || latest_sub_obj['downloading']) {
|
|
return false;
|
|
}
|
|
|
|
updateSubscriptionProperty(sub, {downloading: true}, user_uid);
|
|
|
|
// get basePath
|
|
let basePath = null;
|
|
if (user_uid)
|
|
basePath = path.join(config_api.getConfigItem('ytdl_users_base_path'), user_uid, 'subscriptions');
|
|
else
|
|
basePath = config_api.getConfigItem('ytdl_subscriptions_base_path');
|
|
|
|
let appendedBasePath = getAppendedBasePath(sub, basePath);
|
|
fs.ensureDirSync(appendedBasePath);
|
|
|
|
const downloadConfig = await generateArgsForSubscription(sub, user_uid);
|
|
|
|
// get videos
|
|
logger.verbose(`Subscription: getting list of videos to download for ${sub.name} with args: ${downloadConfig.join(',')}`);
|
|
|
|
return new Promise(async resolve => {
|
|
youtubedl.exec(sub.url, downloadConfig, {maxBuffer: Infinity}, async function(err, output) {
|
|
// cleanup
|
|
updateSubscriptionProperty(sub, {downloading: false}, user_uid);
|
|
|
|
// remove temporary archive file if it exists
|
|
const archive_path = path.join(appendedBasePath, 'archive.txt');
|
|
const archive_exists = await fs.pathExists(archive_path);
|
|
if (archive_exists) {
|
|
await fs.unlink(archive_path);
|
|
}
|
|
|
|
logger.verbose('Subscription: finished check for ' + sub.name);
|
|
const parsed_output = utils.parseOutputJSON(output, err);
|
|
if (!parsed_output) {
|
|
logger.error('Subscription check failed!');
|
|
resolve(null);
|
|
return;
|
|
}
|
|
const files_to_download = await handleOutputJSON(parsed_output, sub, user_uid);
|
|
resolve(files_to_download);
|
|
return;
|
|
});
|
|
}, err => {
|
|
logger.error(err);
|
|
updateSubscriptionProperty(sub, {downloading: false}, user_uid);
|
|
});
|
|
}
|
|
|
|
async function handleOutputJSON(output_jsons, sub, user_uid) {
|
|
if (config_api.getConfigItem('ytdl_subscriptions_redownload_fresh_uploads')) {
|
|
await setFreshUploads(sub, user_uid);
|
|
checkVideosForFreshUploads(sub, user_uid);
|
|
}
|
|
|
|
if (output_jsons.length === 0 || (output_jsons.length === 1 && output_jsons[0] === '')) {
|
|
logger.verbose('No additional videos to download for ' + sub.name);
|
|
return [];
|
|
}
|
|
|
|
const files_to_download = await getFilesToDownload(sub, output_jsons);
|
|
const base_download_options = exports.generateOptionsForSubscriptionDownload(sub, user_uid);
|
|
|
|
for (let j = 0; j < files_to_download.length; j++) {
|
|
const file_to_download = files_to_download[j];
|
|
file_to_download['formats'] = utils.stripPropertiesFromObject(file_to_download['formats'], ['format_id', 'filesize', 'filesize_approx']); // prevent download object from blowing up in size
|
|
await downloader_api.createDownload(file_to_download['webpage_url'], sub.type || 'video', base_download_options, user_uid, sub.id, sub.name, [file_to_download]);
|
|
}
|
|
|
|
return files_to_download;
|
|
}
|
|
|
|
exports.generateOptionsForSubscriptionDownload = (sub, user_uid) => {
|
|
let basePath = null;
|
|
if (user_uid)
|
|
basePath = path.join(config_api.getConfigItem('ytdl_users_base_path'), user_uid, 'subscriptions');
|
|
else
|
|
basePath = config_api.getConfigItem('ytdl_subscriptions_base_path');
|
|
|
|
let default_output = config_api.getConfigItem('ytdl_default_file_output') ? config_api.getConfigItem('ytdl_default_file_output') : '%(title)s';
|
|
|
|
const base_download_options = {
|
|
maxHeight: sub.maxQuality && sub.maxQuality !== 'best' ? sub.maxQuality : null,
|
|
customFileFolderPath: getAppendedBasePath(sub, basePath),
|
|
customOutput: sub.custom_output ? `${sub.custom_output}` : `${default_output}`,
|
|
customArchivePath: path.join(basePath, 'archives', sub.name),
|
|
additionalArgs: sub.custom_args
|
|
}
|
|
|
|
return base_download_options;
|
|
}
|
|
|
|
async function generateArgsForSubscription(sub, user_uid, redownload = false, desired_path = null) {
|
|
// get basePath
|
|
let basePath = null;
|
|
if (user_uid)
|
|
basePath = path.join(config_api.getConfigItem('ytdl_users_base_path'), user_uid, 'subscriptions');
|
|
else
|
|
basePath = config_api.getConfigItem('ytdl_subscriptions_base_path');
|
|
|
|
let appendedBasePath = getAppendedBasePath(sub, basePath);
|
|
|
|
const file_output = config_api.getConfigItem('ytdl_default_file_output') ? config_api.getConfigItem('ytdl_default_file_output') : '%(title)s';
|
|
|
|
let fullOutput = `"${appendedBasePath}/${file_output}.%(ext)s"`;
|
|
if (desired_path) {
|
|
fullOutput = `"${desired_path}.%(ext)s"`;
|
|
} else if (sub.custom_output) {
|
|
fullOutput = `"${appendedBasePath}/${sub.custom_output}.%(ext)s"`;
|
|
}
|
|
|
|
let downloadConfig = ['--dump-json', '-o', fullOutput, !redownload ? '-ciw' : '-ci', '--write-info-json', '--print-json'];
|
|
|
|
let qualityPath = null;
|
|
if (sub.type && sub.type === 'audio') {
|
|
qualityPath = ['-f', 'bestaudio']
|
|
qualityPath.push('-x');
|
|
qualityPath.push('--audio-format', 'mp3');
|
|
} else {
|
|
if (!sub.maxQuality || sub.maxQuality === 'best') qualityPath = ['-f', 'bestvideo[ext=mp4]+bestaudio[ext=m4a]/mp4'];
|
|
else qualityPath = ['-f', `bestvideo[height<=${sub.maxQuality}]+bestaudio/best[height<=${sub.maxQuality}]`, '--merge-output-format', 'mp4'];
|
|
}
|
|
|
|
downloadConfig.push(...qualityPath)
|
|
|
|
// skip videos that are in the archive. otherwise sub download can be permanently slow (vs. just the first time)
|
|
const archive_text = await archive_api.generateArchive(sub.type, sub.user_uid, sub.id);
|
|
const archive_count = archive_text.split('\n').length - 1;
|
|
if (archive_count > 0) {
|
|
logger.verbose(`Generating temporary archive file for subscription ${sub.name} with ${archive_count} entries.`)
|
|
const archive_path = path.join(appendedBasePath, 'archive.txt');
|
|
await fs.writeFile(archive_path, archive_text);
|
|
downloadConfig.push('--download-archive', archive_path);
|
|
}
|
|
|
|
if (sub.custom_args) {
|
|
const customArgsArray = sub.custom_args.split(',,');
|
|
if (customArgsArray.indexOf('-f') !== -1) {
|
|
// if custom args has a custom quality, replce the original quality with that of custom args
|
|
const original_output_index = downloadConfig.indexOf('-f');
|
|
downloadConfig.splice(original_output_index, 2);
|
|
}
|
|
downloadConfig.push(...customArgsArray);
|
|
}
|
|
|
|
if (sub.timerange && !redownload) {
|
|
downloadConfig.push('--dateafter', sub.timerange);
|
|
}
|
|
|
|
let useCookies = config_api.getConfigItem('ytdl_use_cookies');
|
|
if (useCookies) {
|
|
if (await fs.pathExists(path.join(__dirname, 'appdata', 'cookies.txt'))) {
|
|
downloadConfig.push('--cookies', path.join('appdata', 'cookies.txt'));
|
|
} else {
|
|
logger.warn('Cookies file could not be found. You can either upload one, or disable \'use cookies\' in the Advanced tab in the settings.');
|
|
}
|
|
}
|
|
|
|
if (config_api.getConfigItem('ytdl_include_thumbnail')) {
|
|
downloadConfig.push('--write-thumbnail');
|
|
}
|
|
|
|
const rate_limit = config_api.getConfigItem('ytdl_download_rate_limit');
|
|
if (rate_limit && downloadConfig.indexOf('-r') === -1 && downloadConfig.indexOf('--limit-rate') === -1) {
|
|
downloadConfig.push('-r', rate_limit);
|
|
}
|
|
|
|
const default_downloader = utils.getCurrentDownloader() || config_api.getConfigItem('ytdl_default_downloader');
|
|
if (default_downloader === 'yt-dlp') {
|
|
downloadConfig.push('--no-clean-info-json');
|
|
}
|
|
|
|
downloadConfig = utils.filterArgs(downloadConfig, ['--write-comments']);
|
|
|
|
return downloadConfig;
|
|
}
|
|
|
|
async function getFilesToDownload(sub, output_jsons) {
|
|
const files_to_download = [];
|
|
for (let i = 0; i < output_jsons.length; i++) {
|
|
const output_json = output_jsons[i];
|
|
const file_missing = !(await db_api.getRecord('files', {sub_id: sub.id, url: output_json['webpage_url']})) && !(await db_api.getRecord('download_queue', {sub_id: sub.id, url: output_json['webpage_url'], error: null, finished: false}));
|
|
if (file_missing) {
|
|
const file_with_path_exists = await db_api.getRecord('files', {sub_id: sub.id, path: output_json['_filename']});
|
|
if (file_with_path_exists) {
|
|
// or maybe just overwrite???
|
|
logger.info(`Skipping adding file ${output_json['_filename']} for subscription ${sub.name} as a file with that path already exists.`)
|
|
continue;
|
|
}
|
|
const exists_in_archive = await archive_api.existsInArchive(output_json['extractor'], output_json['id'], sub.type, sub.user_uid, sub.id);
|
|
if (exists_in_archive) continue;
|
|
|
|
files_to_download.push(output_json);
|
|
}
|
|
}
|
|
return files_to_download;
|
|
}
|
|
|
|
|
|
exports.getSubscriptions = async (user_uid = null) => {
|
|
return await db_api.getRecords('subscriptions', {user_uid: user_uid});
|
|
}
|
|
|
|
exports.getAllSubscriptions = async () => {
|
|
const all_subs = await db_api.getRecords('subscriptions');
|
|
const multiUserMode = config_api.getConfigItem('ytdl_multi_user_mode');
|
|
return all_subs.filter(sub => !!(sub.user_uid) === !!multiUserMode);
|
|
}
|
|
|
|
exports.getSubscription = async (subID) => {
|
|
// stringify and parse because we may override the 'downloading' property
|
|
const sub = JSON.parse(JSON.stringify(await db_api.getRecord('subscriptions', {id: subID})));
|
|
// now with the download_queue, we may need to override 'downloading'
|
|
const current_downloads = await db_api.getRecords('download_queue', {running: true, sub_id: subID}, true);
|
|
if (!sub['downloading']) sub['downloading'] = current_downloads > 0;
|
|
return sub;
|
|
}
|
|
|
|
exports.getSubscriptionByName = async (subName, user_uid = null) => {
|
|
return await db_api.getRecord('subscriptions', {name: subName, user_uid: user_uid});
|
|
}
|
|
|
|
exports.updateSubscription = async (sub) => {
|
|
await db_api.updateRecord('subscriptions', {id: sub.id}, sub);
|
|
exports.writeSubscriptionMetadata(sub);
|
|
return true;
|
|
}
|
|
|
|
exports.updateSubscriptionPropertyMultiple = async (subs, assignment_obj) => {
|
|
subs.forEach(async sub => {
|
|
await updateSubscriptionProperty(sub, assignment_obj);
|
|
});
|
|
}
|
|
|
|
async function updateSubscriptionProperty(sub, assignment_obj) {
|
|
// TODO: combine with updateSubscription
|
|
await db_api.updateRecord('subscriptions', {id: sub.id}, assignment_obj);
|
|
return true;
|
|
}
|
|
|
|
exports.writeSubscriptionMetadata = (sub) => {
|
|
let basePath = sub.user_uid ? path.join(config_api.getConfigItem('ytdl_users_base_path'), sub.user_uid, 'subscriptions')
|
|
: config_api.getConfigItem('ytdl_subscriptions_base_path');
|
|
const appendedBasePath = getAppendedBasePath(sub, basePath);
|
|
const metadata_path = path.join(appendedBasePath, CONSTS.SUBSCRIPTION_BACKUP_PATH);
|
|
|
|
fs.ensureDirSync(appendedBasePath);
|
|
fs.writeJSONSync(metadata_path, sub);
|
|
}
|
|
|
|
async function setFreshUploads(sub) {
|
|
const sub_files = await db_api.getRecords('files', {sub_id: sub.id});
|
|
if (!sub_files) return;
|
|
const current_date = new Date().toISOString().split('T')[0].replace(/-/g, '');
|
|
sub_files.forEach(async file => {
|
|
if (current_date === file['upload_date'].replace(/-/g, '')) {
|
|
// set upload as fresh
|
|
const file_uid = file['uid'];
|
|
await db_api.setVideoProperty(file_uid, {'fresh_upload': true});
|
|
}
|
|
});
|
|
}
|
|
|
|
async function checkVideosForFreshUploads(sub, user_uid) {
|
|
const sub_files = await db_api.getRecords('files', {sub_id: sub.id});
|
|
const current_date = new Date().toISOString().split('T')[0].replace(/-/g, '');
|
|
sub_files.forEach(async file => {
|
|
if (file['fresh_upload'] && current_date > file['upload_date'].replace(/-/g, '')) {
|
|
await checkVideoIfBetterExists(file, sub, user_uid)
|
|
}
|
|
});
|
|
}
|
|
|
|
async function checkVideoIfBetterExists(file_obj, sub, user_uid) {
|
|
const new_path = file_obj['path'].substring(0, file_obj['path'].length - 4);
|
|
const downloadConfig = await generateArgsForSubscription(sub, user_uid, true, new_path);
|
|
logger.verbose(`Checking if a better version of the fresh upload ${file_obj['id']} exists.`);
|
|
// simulate a download to verify that a better version exists
|
|
youtubedl.getInfo(file_obj['url'], downloadConfig, async (err, output) => {
|
|
if (err) {
|
|
// video is not available anymore for whatever reason
|
|
} else if (output) {
|
|
const metric_to_compare = sub.type === 'audio' ? 'abr' : 'height';
|
|
if (output[metric_to_compare] > file_obj[metric_to_compare]) {
|
|
// download new video as the simulated one is better
|
|
youtubedl.exec(file_obj['url'], downloadConfig, {maxBuffer: Infinity}, async (err, output) => {
|
|
if (err) {
|
|
logger.verbose(`Failed to download better version of video ${file_obj['id']}`);
|
|
} else if (output) {
|
|
logger.verbose(`Successfully upgraded video ${file_obj['id']}'s ${metric_to_compare} from ${file_obj[metric_to_compare]} to ${output[metric_to_compare]}`);
|
|
await db_api.setVideoProperty(file_obj['uid'], {[metric_to_compare]: output[metric_to_compare]});
|
|
}
|
|
});
|
|
}
|
|
}
|
|
});
|
|
await db_api.setVideoProperty(file_obj['uid'], {'fresh_upload': false});
|
|
}
|
|
|
|
// helper functions
|
|
|
|
function getAppendedBasePath(sub, base_path) {
|
|
return path.join(base_path, (sub.isPlaylist ? 'playlists/' : 'channels/'), sub.name);
|
|
}
|