Merge branch 'master' of https://github.com/Tzahi12345/YoutubeDL-Material into subscriptions-custom-path

This commit is contained in:
Tzahi12345
2020-06-02 23:24:54 -04:00
39 changed files with 526 additions and 88 deletions

View File

@@ -1,18 +1,25 @@
FROM alpine:3.11 FROM alpine:3.12
RUN \ ENV UID=1000 GID=1000
apk add --no-cache npm python ffmpeg && \ RUN export user=youtube \
apk add --no-cache --repository http://dl-cdn.alpinelinux.org/alpine/edge/testing/ \ && addgroup -S $user -g $GID && adduser -D -S $user -G $user -u $UID
USER $user
RUN apk add --no-cache \
ffmpeg \
npm \
python2 \
su-exec \
&& apk add --no-cache --repository http://dl-cdn.alpinelinux.org/alpine/edge/testing/ \
atomicparsley atomicparsley
WORKDIR /app WORKDIR /app
COPY package.json /app/ COPY --chown=$UID:$GID [ "package.json", "package-lock.json", "/app/" ]
RUN npm install RUN npm install
COPY ./ /app/ COPY --chown=$UID:$GID [ "./", "/app/" ]
EXPOSE 17442 EXPOSE 17442
ENTRYPOINT [ "/app/entrypoint.sh" ]
CMD [ "node", "app.js" ] CMD [ "node", "app.js" ]

View File

@@ -8,6 +8,7 @@ var youtubedl = require('youtube-dl');
var ffmpeg = require('fluent-ffmpeg'); var ffmpeg = require('fluent-ffmpeg');
var compression = require('compression'); var compression = require('compression');
var https = require('https'); var https = require('https');
var multer = require('multer');
var express = require("express"); var express = require("express");
var bodyParser = require("body-parser"); var bodyParser = require("body-parser");
var archiver = require('archiver'); var archiver = require('archiver');
@@ -164,7 +165,9 @@ var validDownloadingAgents = [
'ffmpeg', 'ffmpeg',
'httpie', 'httpie',
'wget' 'wget'
] ];
const subscription_timeouts = {};
// don't overwrite config if it already happened.. NOT // don't overwrite config if it already happened.. NOT
// let alreadyWritten = db.get('configWriteFlag').value(); // let alreadyWritten = db.get('configWriteFlag').value();
@@ -615,15 +618,14 @@ function loadConfigValues() {
logger.transports[2].level = logger_level; logger.transports[2].level = logger_level;
} }
function calculateSubcriptionRetrievalDelay(amount) { function calculateSubcriptionRetrievalDelay(subscriptions_amount) {
// frequency is 5 mins // frequency is once every 5 mins by default
let frequency_in_ms = subscriptionsCheckInterval * 1000; let interval_in_ms = subscriptionsCheckInterval * 1000;
let minimum_frequency = 60 * 1000; const subinterval_in_ms = interval_in_ms/subscriptions_amount;
const first_frequency = frequency_in_ms/amount; return subinterval_in_ms;
return (first_frequency < minimum_frequency) ? minimum_frequency : first_frequency;
} }
function watchSubscriptions() { async function watchSubscriptions() {
let subscriptions = null; let subscriptions = null;
const multiUserMode = config_api.getConfigItem('ytdl_multi_user_mode'); const multiUserMode = config_api.getConfigItem('ytdl_multi_user_mode');
@@ -645,10 +647,19 @@ function watchSubscriptions() {
let current_delay = 0; let current_delay = 0;
for (let i = 0; i < subscriptions.length; i++) { for (let i = 0; i < subscriptions.length; i++) {
let sub = subscriptions[i]; let sub = subscriptions[i];
// don't check the sub if the last check for the same subscription has not completed
if (subscription_timeouts[sub.id]) {
logger.verbose(`Subscription: skipped checking ${sub.name} as the last check for ${sub.name} has not completed.`);
continue;
}
logger.verbose('Watching ' + sub.name + ' with delay interval of ' + delay_interval); logger.verbose('Watching ' + sub.name + ' with delay interval of ' + delay_interval);
setTimeout(() => { setTimeout(async () => {
subscriptions_api.getVideosForSub(sub, sub.user_uid); await subscriptions_api.getVideosForSub(sub, sub.user_uid);
subscription_timeouts[sub.id] = false;
}, current_delay); }, current_delay);
subscription_timeouts[sub.id] = true;
current_delay += delay_interval; current_delay += delay_interval;
if (current_delay >= subscriptionsCheckInterval * 1000) current_delay = 0; if (current_delay >= subscriptionsCheckInterval * 1000) current_delay = 0;
} }
@@ -1237,6 +1248,7 @@ async function downloadFileByURL_exec(url, type, options, sessionID = null) {
} else if (output) { } else if (output) {
if (output.length === 0 || output[0].length === 0) { if (output.length === 0 || output[0].length === 0) {
download['error'] = 'No output. Check if video already exists in your archive.'; download['error'] = 'No output. Check if video already exists in your archive.';
logger.warn(`No output received for video download, check if it exists in your archive.`)
updateDownloads(); updateDownloads();
resolve(false); resolve(false);
@@ -1287,10 +1299,10 @@ async function downloadFileByURL_exec(url, type, options, sessionID = null) {
let is_playlist = file_names.length > 1; let is_playlist = file_names.length > 1;
if (options.merged_string) { if (options.merged_string !== null && options.merged_string !== undefined) {
let current_merged_archive = fs.readFileSync(fileFolderPath + 'merged.txt', 'utf8'); let current_merged_archive = fs.readFileSync(path.join(fileFolderPath, `merged_${type}.txt`), 'utf8');
let diff = current_merged_archive.replace(options.merged_string, ''); let diff = current_merged_archive.replace(options.merged_string, '');
const archive_path = path.join(archivePath, `archive_${type}.txt`); const archive_path = options.user ? path.join(fileFolderPath, 'archives', `archive_${type}.txt`) : path.join(archivePath, `archive_${type}.txt`);
fs.appendFileSync(archive_path, diff); fs.appendFileSync(archive_path, diff);
} }
@@ -1318,7 +1330,7 @@ async function downloadFileByURL_normal(url, type, options, sessionID = null) {
const ext = is_audio ? '.mp3' : '.mp4'; const ext = is_audio ? '.mp3' : '.mp4';
var fileFolderPath = is_audio ? audioFolderPath : videoFolderPath; var fileFolderPath = is_audio ? audioFolderPath : videoFolderPath;
if (is_audio) options.skip_audio_args = true; if (is_audio && url.includes('youtu')) { options.skip_audio_args = true; }
// prepend with user if needed // prepend with user if needed
let multiUserMode = null; let multiUserMode = null;
@@ -1421,10 +1433,10 @@ async function downloadFileByURL_normal(url, type, options, sessionID = null) {
const base_file_name = video_info._filename.substring(fileFolderPath.length, video_info._filename.length); const base_file_name = video_info._filename.substring(fileFolderPath.length, video_info._filename.length);
file_uid = registerFileDB(base_file_name, type, multiUserMode); file_uid = registerFileDB(base_file_name, type, multiUserMode);
if (options.merged_string) { if (options.merged_string !== null && options.merged_string !== undefined) {
let current_merged_archive = fs.readFileSync(fileFolderPath + 'merged.txt', 'utf8'); let current_merged_archive = fs.readFileSync(path.join(fileFolderPath, `merged_${type}.txt`), 'utf8');
let diff = current_merged_archive.replace(options.merged_string, ''); let diff = current_merged_archive.replace(options.merged_string, '');
const archive_path = req.isAuthenticated() ? path.join(fileFolderPath, 'archives', `archive_${type}.txt`) : path.join(archivePath, `archive_${type}.txt`); const archive_path = options.user ? path.join(fileFolderPath, 'archives', `archive_${type}.txt`) : path.join(archivePath, `archive_${type}.txt`);
fs.appendFileSync(archive_path, diff); fs.appendFileSync(archive_path, diff);
} }
@@ -1453,6 +1465,7 @@ async function generateArgs(url, type, options) {
return new Promise(async resolve => { return new Promise(async resolve => {
var videopath = '%(title)s'; var videopath = '%(title)s';
var globalArgs = config_api.getConfigItem('ytdl_custom_args'); var globalArgs = config_api.getConfigItem('ytdl_custom_args');
let useCookies = config_api.getConfigItem('ytdl_use_cookies');
var is_audio = type === 'audio'; var is_audio = type === 'audio';
var fileFolderPath = is_audio ? audioFolderPath : videoFolderPath; var fileFolderPath = is_audio ? audioFolderPath : videoFolderPath;
@@ -1474,10 +1487,12 @@ async function generateArgs(url, type, options) {
let downloadConfig = null; let downloadConfig = null;
let qualityPath = (is_audio && !options.skip_audio_args) ? '-f bestaudio' :'-f best[ext=mp4]'; let qualityPath = (is_audio && !options.skip_audio_args) ? '-f bestaudio' :'-f best[ext=mp4]';
const is_youtube = url.includes('youtu');
if (!is_audio && (url.includes('tiktok') || url.includes('pscp.tv'))) { if (!is_audio && !is_youtube) {
// tiktok videos fail when using the default format // tiktok videos fail when using the default format
qualityPath = '-f best'; qualityPath = null;
} else if (!is_audio && !is_youtube && (url.includes('reddit') || url.includes('pornhub'))) {
qualityPath = '-f bestvideo+bestaudio'
} }
if (customArgs) { if (customArgs) {
@@ -1492,11 +1507,13 @@ async function generateArgs(url, type, options) {
} }
if (customOutput) { if (customOutput) {
downloadConfig = ['-o', path.join(fileFolderPath, customOutput) + ".%(ext)s", qualityPath, '--write-info-json', '--print-json']; downloadConfig = ['-o', path.join(fileFolderPath, customOutput) + ".%(ext)s", '--write-info-json', '--print-json'];
} else { } else {
downloadConfig = ['-o', path.join(fileFolderPath, videopath + (is_audio ? '.%(ext)s' : '.mp4')), qualityPath, '--write-info-json', '--print-json']; downloadConfig = ['-o', path.join(fileFolderPath, videopath + (is_audio ? '.%(ext)s' : '.mp4')), '--write-info-json', '--print-json'];
} }
if (qualityPath) downloadConfig.push(qualityPath);
if (is_audio && !options.skip_audio_args) { if (is_audio && !options.skip_audio_args) {
downloadConfig.push('-x'); downloadConfig.push('-x');
downloadConfig.push('--audio-format', 'mp3'); downloadConfig.push('--audio-format', 'mp3');
@@ -1506,13 +1523,25 @@ async function generateArgs(url, type, options) {
downloadConfig.push('--username', youtubeUsername, '--password', youtubePassword); downloadConfig.push('--username', youtubeUsername, '--password', youtubePassword);
} }
if (useCookies) {
if (fs.existsSync(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 (!useDefaultDownloadingAgent && customDownloadingAgent) { if (!useDefaultDownloadingAgent && customDownloadingAgent) {
downloadConfig.splice(0, 0, '--external-downloader', customDownloadingAgent); downloadConfig.splice(0, 0, '--external-downloader', customDownloadingAgent);
} }
let useYoutubeDLArchive = config_api.getConfigItem('ytdl_use_youtubedl_archive'); let useYoutubeDLArchive = config_api.getConfigItem('ytdl_use_youtubedl_archive');
if (useYoutubeDLArchive) { if (useYoutubeDLArchive) {
const archive_path = options.user ? path.join(fileFolderPath, 'archives', `archive_${type}.txt`) : path.join(archivePath, `archive_${type}.txt`); const archive_folder = options.user ? path.join(fileFolderPath, 'archives') : archivePath;
const archive_path = path.join(archive_folder, `archive_${type}.txt`);
fs.ensureDirSync(archive_folder);
// create archive file if it doesn't exist // create archive file if it doesn't exist
if (!fs.existsSync(archive_path)) { if (!fs.existsSync(archive_path)) {
fs.closeSync(fs.openSync(archive_path, 'w')); fs.closeSync(fs.openSync(archive_path, 'w'));
@@ -1524,7 +1553,7 @@ async function generateArgs(url, type, options) {
fs.closeSync(fs.openSync(blacklist_path, 'w')); fs.closeSync(fs.openSync(blacklist_path, 'w'));
} }
let merged_path = fileFolderPath + 'merged.txt'; let merged_path = path.join(fileFolderPath, `merged_${type}.txt`);
fs.ensureFileSync(merged_path); fs.ensureFileSync(merged_path);
// merges blacklist and regular archive // merges blacklist and regular archive
let inputPathList = [archive_path, blacklist_path]; let inputPathList = [archive_path, blacklist_path];
@@ -1546,6 +1575,7 @@ async function generateArgs(url, type, options) {
} }
} }
logger.verbose(`youtube-dl args being used: ${downloadConfig.join(',')}`);
// downloadConfig.map((arg) => `"${arg}"`); // downloadConfig.map((arg) => `"${arg}"`);
resolve(downloadConfig); resolve(downloadConfig);
}); });
@@ -1815,7 +1845,7 @@ const optionalJwt = function (req, res, next) {
res.sendStatus(401); res.sendStatus(401);
return; return;
} }
} else if (multiUserMode && !(req.path.includes('/api/auth/register') && !req.query.jwt)) { // registration should get passed through } else if (multiUserMode && !(req.path.includes('/api/auth/register') && !(req.path.includes('/api/config')) && !req.query.jwt)) { // registration should get passed through
if (!req.query.jwt) { if (!req.query.jwt) {
res.sendStatus(401); res.sendStatus(401);
return; return;
@@ -1865,8 +1895,11 @@ app.post('/api/tomp3', optionalJwt, async function(req, res) {
user: req.isAuthenticated() ? req.user.uid : null user: req.isAuthenticated() ? req.user.uid : null
} }
const safeDownloadOverride = config_api.getConfigItem('ytdl_safe_download_override');
const is_playlist = url.includes('playlist'); const is_playlist = url.includes('playlist');
if (is_playlist || options.customQualityConfiguration || options.customArgs || options.maxBitrate)
let result_obj = null;
if (safeDownloadOverride || is_playlist || options.customQualityConfiguration || options.customArgs || options.maxBitrate)
result_obj = await downloadFileByURL_exec(url, 'audio', options, req.query.sessionID); result_obj = await downloadFileByURL_exec(url, 'audio', options, req.query.sessionID);
else else
result_obj = await downloadFileByURL_normal(url, 'audio', options, req.query.sessionID); result_obj = await downloadFileByURL_normal(url, 'audio', options, req.query.sessionID);
@@ -1892,9 +1925,11 @@ app.post('/api/tomp4', optionalJwt, async function(req, res) {
user: req.isAuthenticated() ? req.user.uid : null user: req.isAuthenticated() ? req.user.uid : null
} }
const safeDownloadOverride = config_api.getConfigItem('ytdl_safe_download_override');
const is_playlist = url.includes('playlist'); const is_playlist = url.includes('playlist');
let result_obj = null; let result_obj = null;
if (is_playlist || options.customQualityConfiguration || options.customArgs || options.selectedHeight) if (safeDownloadOverride || is_playlist || options.customQualityConfiguration || options.customArgs || options.selectedHeight || !url.includes('youtu'))
result_obj = await downloadFileByURL_exec(url, 'video', options, req.query.sessionID); result_obj = await downloadFileByURL_exec(url, 'video', options, req.query.sessionID);
else else
result_obj = await downloadFileByURL_normal(url, 'video', options, req.query.sessionID); result_obj = await downloadFileByURL_normal(url, 'video', options, req.query.sessionID);
@@ -2202,7 +2237,7 @@ app.post('/api/getSubscription', optionalJwt, async (req, res) => {
else else
base_path = config_api.getConfigItem('ytdl_subscriptions_base_path'); base_path = config_api.getConfigItem('ytdl_subscriptions_base_path');
let appended_base_path = path.join(base_path, subscription.isPlaylist ? 'playlists' : 'channels', subscription.name, '/'); let appended_base_path = path.join(base_path, (subscription.isPlaylist ? 'playlists' : 'channels'), subscription.name, '/');
let files; let files;
try { try {
files = recFindByExt(appended_base_path, 'mp4'); files = recFindByExt(appended_base_path, 'mp4');
@@ -2532,6 +2567,25 @@ app.post('/api/downloadArchive', async (req, res) => {
}); });
var upload_multer = multer({ dest: __dirname + '/appdata/' });
app.post('/api/uploadCookies', upload_multer.single('cookies'), async (req, res) => {
const new_path = path.join(__dirname, 'appdata', 'cookies.txt');
if (fs.existsSync(req.file.path)) {
fs.renameSync(req.file.path, new_path);
} else {
res.sendStatus(500);
return;
}
if (fs.existsSync(new_path)) {
res.send({success: true});
} else {
res.sendStatus(500);
}
});
// Updater API calls // Updater API calls
app.get('/api/updaterStatus', async (req, res) => { app.get('/api/updaterStatus', async (req, res) => {

View File

@@ -13,7 +13,8 @@
"path-audio": "audio/", "path-audio": "audio/",
"path-video": "video/", "path-video": "video/",
"use_youtubedl_archive": false, "use_youtubedl_archive": false,
"custom_args": "" "custom_args": "",
"safe_download_override": false
}, },
"Extra": { "Extra": {
"title_top": "YoutubeDL-Material", "title_top": "YoutubeDL-Material",
@@ -49,6 +50,7 @@
"custom_downloading_agent": "", "custom_downloading_agent": "",
"multi_user_mode": false, "multi_user_mode": false,
"allow_advanced_download": false, "allow_advanced_download": false,
"use_cookies": false,
"logger_level": "info" "logger_level": "info"
} }
} }

View File

@@ -13,7 +13,8 @@
"path-audio": "audio/", "path-audio": "audio/",
"path-video": "video/", "path-video": "video/",
"use_youtubedl_archive": false, "use_youtubedl_archive": false,
"custom_args": "" "custom_args": "",
"safe_download_override": false
}, },
"Extra": { "Extra": {
"title_top": "YoutubeDL-Material", "title_top": "YoutubeDL-Material",
@@ -49,6 +50,7 @@
"custom_downloading_agent": "", "custom_downloading_agent": "",
"multi_user_mode": false, "multi_user_mode": false,
"allow_advanced_download": false, "allow_advanced_download": false,
"use_cookies": false,
"logger_level": "info" "logger_level": "info"
} }
} }

View File

@@ -430,8 +430,8 @@ exports.deleteUserFile = function(user_uid, file_uid, type, blacklistMode = fals
fs.appendFileSync(blacklistPath, line); fs.appendFileSync(blacklistPath, line);
} }
} else { } else {
logger.info('Could not find archive file for audio files. Creating...'); logger.info(`Could not find archive file for ${type} files. Creating...`);
fs.closeSync(fs.openSync(archive_path, 'w')); fs.ensureFileSync(archive_path);
} }
} }
} }

View File

@@ -182,7 +182,8 @@ DEFAULT_CONFIG = {
"path-audio": "audio/", "path-audio": "audio/",
"path-video": "video/", "path-video": "video/",
"use_youtubedl_archive": false, "use_youtubedl_archive": false,
"custom_args": "" "custom_args": "",
"safe_download_override": false
}, },
"Extra": { "Extra": {
"title_top": "YoutubeDL-Material", "title_top": "YoutubeDL-Material",
@@ -218,6 +219,7 @@ DEFAULT_CONFIG = {
"custom_downloading_agent": "", "custom_downloading_agent": "",
"multi_user_mode": false, "multi_user_mode": false,
"allow_advanced_download": false, "allow_advanced_download": false,
"use_cookies": false,
"logger_level": "info" "logger_level": "info"
} }
} }

View File

@@ -40,6 +40,10 @@ let CONFIG_ITEMS = {
'key': 'ytdl_custom_args', 'key': 'ytdl_custom_args',
'path': 'YoutubeDLMaterial.Downloader.custom_args' 'path': 'YoutubeDLMaterial.Downloader.custom_args'
}, },
'ytdl_safe_download_override': {
'key': 'ytdl_safe_download_override',
'path': 'YoutubeDLMaterial.Downloader.safe_download_override'
},
// Extra // Extra
'ytdl_title_top': { 'ytdl_title_top': {
@@ -148,6 +152,10 @@ let CONFIG_ITEMS = {
'key': 'ytdl_allow_advanced_download', 'key': 'ytdl_allow_advanced_download',
'path': 'YoutubeDLMaterial.Advanced.allow_advanced_download' 'path': 'YoutubeDLMaterial.Advanced.allow_advanced_download'
}, },
'ytdl_use_cookies': {
'key': 'ytdl_use_cookies',
'path': 'YoutubeDLMaterial.Advanced.use_cookies'
},
'ytdl_logger_level': { 'ytdl_logger_level': {
'key': 'ytdl_logger_level', 'key': 'ytdl_logger_level',
'path': 'YoutubeDLMaterial.Advanced.logger_level' 'path': 'YoutubeDLMaterial.Advanced.logger_level'

17
backend/entrypoint.sh Executable file
View File

@@ -0,0 +1,17 @@
#!/bin/sh
set -eu
CMD="node app.js"
# if the first arg starts with "-" pass it to program
if [ "${1#-}" != "$1" ]; then
set -- "$CMD" "$@"
fi
# chown current working directory to current user
if [ "$@" = "$CMD" ] && [ "$(id -u)" = "0" ]; then
find . \! -user "$UID" -exec chown "$UID:$GID" -R '{}' +
exec su-exec "$UID:$GID" "$0" "$@"
fi
exec "$@"

View File

@@ -59,6 +59,11 @@
"picomatch": "^2.0.4" "picomatch": "^2.0.4"
} }
}, },
"append-field": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz",
"integrity": "sha1-HjRA6RXwsSA9I3SOeO3XubW0PlY="
},
"aproba": { "aproba": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
@@ -324,6 +329,11 @@
"resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
"integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=" "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk="
}, },
"buffer-from": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
"integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A=="
},
"buffer-indexof-polyfill": { "buffer-indexof-polyfill": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.1.tgz", "resolved": "https://registry.npmjs.org/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.1.tgz",
@@ -334,6 +344,38 @@
"resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz", "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz",
"integrity": "sha1-skV5w77U1tOWru5tmorn9Ugqt7s=" "integrity": "sha1-skV5w77U1tOWru5tmorn9Ugqt7s="
}, },
"busboy": {
"version": "0.2.14",
"resolved": "https://registry.npmjs.org/busboy/-/busboy-0.2.14.tgz",
"integrity": "sha1-bCpiLvz0fFe7vh4qnDetNseSVFM=",
"requires": {
"dicer": "0.2.5",
"readable-stream": "1.1.x"
},
"dependencies": {
"isarray": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
"integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
},
"readable-stream": {
"version": "1.1.14",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
"integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=",
"requires": {
"core-util-is": "~1.0.0",
"inherits": "~2.0.1",
"isarray": "0.0.1",
"string_decoder": "~0.10.x"
}
},
"string_decoder": {
"version": "0.10.31",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
"integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ="
}
}
},
"bytes": { "bytes": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
@@ -524,6 +566,33 @@
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
}, },
"concat-stream": {
"version": "1.6.2",
"resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
"integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
"requires": {
"buffer-from": "^1.0.0",
"inherits": "^2.0.3",
"readable-stream": "^2.2.2",
"typedarray": "^0.0.6"
},
"dependencies": {
"readable-stream": {
"version": "2.3.7",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
"integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
"requires": {
"core-util-is": "~1.0.0",
"inherits": "~2.0.3",
"isarray": "~1.0.0",
"process-nextick-args": "~2.0.0",
"safe-buffer": "~5.1.1",
"string_decoder": "~1.1.1",
"util-deprecate": "~1.0.1"
}
}
}
},
"config": { "config": {
"version": "3.3.1", "version": "3.3.1",
"resolved": "https://registry.npmjs.org/config/-/config-3.3.1.tgz", "resolved": "https://registry.npmjs.org/config/-/config-3.3.1.tgz",
@@ -679,6 +748,38 @@
"kuler": "1.0.x" "kuler": "1.0.x"
} }
}, },
"dicer": {
"version": "0.2.5",
"resolved": "https://registry.npmjs.org/dicer/-/dicer-0.2.5.tgz",
"integrity": "sha1-WZbAhrszIYyBLAkL3cCc0S+stw8=",
"requires": {
"readable-stream": "1.1.x",
"streamsearch": "0.1.2"
},
"dependencies": {
"isarray": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
"integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
},
"readable-stream": {
"version": "1.1.14",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
"integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=",
"requires": {
"core-util-is": "~1.0.0",
"inherits": "~2.0.1",
"isarray": "0.0.1",
"string_decoder": "~0.10.x"
}
},
"string_decoder": {
"version": "0.10.31",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
"integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ="
}
}
},
"dot-prop": { "dot-prop": {
"version": "4.2.0", "version": "4.2.0",
"resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.0.tgz", "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.0.tgz",
@@ -1718,6 +1819,21 @@
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
}, },
"multer": {
"version": "1.4.2",
"resolved": "https://registry.npmjs.org/multer/-/multer-1.4.2.tgz",
"integrity": "sha512-xY8pX7V+ybyUpbYMxtjM9KAiD9ixtg5/JkeKUTD6xilfDv0vzzOFcCp4Ljb1UU3tSOM3VTZtKo63OmzOrGi3Cg==",
"requires": {
"append-field": "^1.0.0",
"busboy": "^0.2.11",
"concat-stream": "^1.5.2",
"mkdirp": "^0.5.1",
"object-assign": "^4.1.1",
"on-finished": "^2.3.0",
"type-is": "^1.6.4",
"xtend": "^4.0.0"
}
},
"multistream": { "multistream": {
"version": "2.1.1", "version": "2.1.1",
"resolved": "https://registry.npmjs.org/multistream/-/multistream-2.1.1.tgz", "resolved": "https://registry.npmjs.org/multistream/-/multistream-2.1.1.tgz",
@@ -2434,6 +2550,11 @@
"hashish": "~0.0.4" "hashish": "~0.0.4"
} }
}, },
"streamsearch": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz",
"integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo="
},
"string-width": { "string-width": {
"version": "2.1.1", "version": "2.1.1",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
@@ -2670,6 +2791,11 @@
"mime-types": "~2.1.24" "mime-types": "~2.1.24"
} }
}, },
"typedarray": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
"integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c="
},
"undefsafe": { "undefsafe": {
"version": "2.0.3", "version": "2.0.3",
"resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.3.tgz", "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.3.tgz",
@@ -2905,6 +3031,11 @@
"resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-3.0.0.tgz", "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-3.0.0.tgz",
"integrity": "sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ=" "integrity": "sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ="
}, },
"xtend": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="
},
"yallist": { "yallist": {
"version": "2.1.2", "version": "2.1.2",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",

View File

@@ -41,6 +41,7 @@
"lowdb": "^1.0.0", "lowdb": "^1.0.0",
"md5": "^2.2.1", "md5": "^2.2.1",
"merge-files": "^0.1.2", "merge-files": "^0.1.2",
"multer": "^1.4.2",
"node-fetch": "^2.6.0", "node-fetch": "^2.6.0",
"node-id3": "^0.1.14", "node-id3": "^0.1.14",
"nodemon": "^2.0.2", "nodemon": "^2.0.2",

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -215,6 +215,9 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.
ngx-file-drop
MIT
ngx-videogular ngx-videogular
MIT MIT

View File

@@ -14,5 +14,5 @@
<link rel="stylesheet" href="styles.5112d6db78cf21541598.css"></head> <link rel="stylesheet" href="styles.5112d6db78cf21541598.css"></head>
<body> <body>
<app-root></app-root> <app-root></app-root>
<script src="runtime-es2015.adce6980ecb528c465bb.js" type="module"></script><script src="runtime-es5.adce6980ecb528c465bb.js" nomodule defer></script><script src="polyfills-es5.7f923c8f5afda210edd3.js" nomodule defer></script><script src="polyfills-es2015.5b408f108bcea938a7e2.js" type="module"></script><script src="main-es2015.38c23f6efbbfa0797d6d.js" type="module"></script><script src="main-es5.38c23f6efbbfa0797d6d.js" nomodule defer></script></body> <script src="runtime-es2015.6ca29b87fe1fc2ebfd61.js" type="module"></script><script src="runtime-es5.6ca29b87fe1fc2ebfd61.js" nomodule defer></script><script src="polyfills-es5.7f923c8f5afda210edd3.js" nomodule defer></script><script src="polyfills-es2015.5b408f108bcea938a7e2.js" type="module"></script><script src="main-es2015.0cbc545a4a3bee376826.js" type="module"></script><script src="main-es5.0cbc545a4a3bee376826.js" nomodule defer></script></body>
</html> </html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1 +1 @@
!function(e){function r(r){for(var n,i,a=r[0],c=r[1],l=r[2],p=0,s=[];p<a.length;p++)i=a[p],Object.prototype.hasOwnProperty.call(o,i)&&o[i]&&s.push(o[i][0]),o[i]=0;for(n in c)Object.prototype.hasOwnProperty.call(c,n)&&(e[n]=c[n]);for(f&&f(r);s.length;)s.shift()();return u.push.apply(u,l||[]),t()}function t(){for(var e,r=0;r<u.length;r++){for(var t=u[r],n=!0,a=1;a<t.length;a++)0!==o[t[a]]&&(n=!1);n&&(u.splice(r--,1),e=i(i.s=t[0]))}return e}var n={},o={0:0},u=[];function i(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,i),t.l=!0,t.exports}i.e=function(e){var r=[],t=o[e];if(0!==t)if(t)r.push(t[2]);else{var n=new Promise((function(r,n){t=o[e]=[r,n]}));r.push(t[2]=n);var u,a=document.createElement("script");a.charset="utf-8",a.timeout=120,i.nc&&a.setAttribute("nonce",i.nc),a.src=function(e){return i.p+""+({}[e]||e)+"-es2015."+{1:"d61dc0b722bb5a6d7e07"}[e]+".js"}(e);var c=new Error;u=function(r){a.onerror=a.onload=null,clearTimeout(l);var t=o[e];if(0!==t){if(t){var n=r&&("load"===r.type?"missing":r.type),u=r&&r.target&&r.target.src;c.message="Loading chunk "+e+" failed.\n("+n+": "+u+")",c.name="ChunkLoadError",c.type=n,c.request=u,t[1](c)}o[e]=void 0}};var l=setTimeout((function(){u({type:"timeout",target:a})}),12e4);a.onerror=a.onload=u,document.head.appendChild(a)}return Promise.all(r)},i.m=e,i.c=n,i.d=function(e,r,t){i.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},i.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},i.t=function(e,r){if(1&r&&(e=i(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(i.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)i.d(t,n,(function(r){return e[r]}).bind(null,n));return t},i.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return i.d(r,"a",r),r},i.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},i.p="",i.oe=function(e){throw console.error(e),e};var a=window.webpackJsonp=window.webpackJsonp||[],c=a.push.bind(a);a.push=r,a=a.slice();for(var l=0;l<a.length;l++)r(a[l]);var f=c;t()}([]); !function(e){function r(r){for(var n,i,a=r[0],c=r[1],l=r[2],p=0,s=[];p<a.length;p++)i=a[p],Object.prototype.hasOwnProperty.call(o,i)&&o[i]&&s.push(o[i][0]),o[i]=0;for(n in c)Object.prototype.hasOwnProperty.call(c,n)&&(e[n]=c[n]);for(f&&f(r);s.length;)s.shift()();return u.push.apply(u,l||[]),t()}function t(){for(var e,r=0;r<u.length;r++){for(var t=u[r],n=!0,a=1;a<t.length;a++)0!==o[t[a]]&&(n=!1);n&&(u.splice(r--,1),e=i(i.s=t[0]))}return e}var n={},o={0:0},u=[];function i(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,i),t.l=!0,t.exports}i.e=function(e){var r=[],t=o[e];if(0!==t)if(t)r.push(t[2]);else{var n=new Promise((function(r,n){t=o[e]=[r,n]}));r.push(t[2]=n);var u,a=document.createElement("script");a.charset="utf-8",a.timeout=120,i.nc&&a.setAttribute("nonce",i.nc),a.src=function(e){return i.p+""+({}[e]||e)+"-es2015."+{1:"19816864d8bf40ef8134"}[e]+".js"}(e);var c=new Error;u=function(r){a.onerror=a.onload=null,clearTimeout(l);var t=o[e];if(0!==t){if(t){var n=r&&("load"===r.type?"missing":r.type),u=r&&r.target&&r.target.src;c.message="Loading chunk "+e+" failed.\n("+n+": "+u+")",c.name="ChunkLoadError",c.type=n,c.request=u,t[1](c)}o[e]=void 0}};var l=setTimeout((function(){u({type:"timeout",target:a})}),12e4);a.onerror=a.onload=u,document.head.appendChild(a)}return Promise.all(r)},i.m=e,i.c=n,i.d=function(e,r,t){i.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},i.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},i.t=function(e,r){if(1&r&&(e=i(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(i.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)i.d(t,n,(function(r){return e[r]}).bind(null,n));return t},i.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return i.d(r,"a",r),r},i.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},i.p="",i.oe=function(e){throw console.error(e),e};var a=window.webpackJsonp=window.webpackJsonp||[],c=a.push.bind(a);a.push=r,a=a.slice();for(var l=0;l<a.length;l++)r(a[l]);var f=c;t()}([]);

View File

@@ -1 +1 @@
!function(e){function r(r){for(var n,i,a=r[0],c=r[1],l=r[2],p=0,s=[];p<a.length;p++)i=a[p],Object.prototype.hasOwnProperty.call(o,i)&&o[i]&&s.push(o[i][0]),o[i]=0;for(n in c)Object.prototype.hasOwnProperty.call(c,n)&&(e[n]=c[n]);for(f&&f(r);s.length;)s.shift()();return u.push.apply(u,l||[]),t()}function t(){for(var e,r=0;r<u.length;r++){for(var t=u[r],n=!0,a=1;a<t.length;a++)0!==o[t[a]]&&(n=!1);n&&(u.splice(r--,1),e=i(i.s=t[0]))}return e}var n={},o={0:0},u=[];function i(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,i),t.l=!0,t.exports}i.e=function(e){var r=[],t=o[e];if(0!==t)if(t)r.push(t[2]);else{var n=new Promise((function(r,n){t=o[e]=[r,n]}));r.push(t[2]=n);var u,a=document.createElement("script");a.charset="utf-8",a.timeout=120,i.nc&&a.setAttribute("nonce",i.nc),a.src=function(e){return i.p+""+({}[e]||e)+"-es5."+{1:"d61dc0b722bb5a6d7e07"}[e]+".js"}(e);var c=new Error;u=function(r){a.onerror=a.onload=null,clearTimeout(l);var t=o[e];if(0!==t){if(t){var n=r&&("load"===r.type?"missing":r.type),u=r&&r.target&&r.target.src;c.message="Loading chunk "+e+" failed.\n("+n+": "+u+")",c.name="ChunkLoadError",c.type=n,c.request=u,t[1](c)}o[e]=void 0}};var l=setTimeout((function(){u({type:"timeout",target:a})}),12e4);a.onerror=a.onload=u,document.head.appendChild(a)}return Promise.all(r)},i.m=e,i.c=n,i.d=function(e,r,t){i.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},i.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},i.t=function(e,r){if(1&r&&(e=i(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(i.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)i.d(t,n,(function(r){return e[r]}).bind(null,n));return t},i.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return i.d(r,"a",r),r},i.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},i.p="",i.oe=function(e){throw console.error(e),e};var a=window.webpackJsonp=window.webpackJsonp||[],c=a.push.bind(a);a.push=r,a=a.slice();for(var l=0;l<a.length;l++)r(a[l]);var f=c;t()}([]); !function(e){function r(r){for(var n,i,a=r[0],c=r[1],l=r[2],p=0,s=[];p<a.length;p++)i=a[p],Object.prototype.hasOwnProperty.call(o,i)&&o[i]&&s.push(o[i][0]),o[i]=0;for(n in c)Object.prototype.hasOwnProperty.call(c,n)&&(e[n]=c[n]);for(f&&f(r);s.length;)s.shift()();return u.push.apply(u,l||[]),t()}function t(){for(var e,r=0;r<u.length;r++){for(var t=u[r],n=!0,a=1;a<t.length;a++)0!==o[t[a]]&&(n=!1);n&&(u.splice(r--,1),e=i(i.s=t[0]))}return e}var n={},o={0:0},u=[];function i(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,i),t.l=!0,t.exports}i.e=function(e){var r=[],t=o[e];if(0!==t)if(t)r.push(t[2]);else{var n=new Promise((function(r,n){t=o[e]=[r,n]}));r.push(t[2]=n);var u,a=document.createElement("script");a.charset="utf-8",a.timeout=120,i.nc&&a.setAttribute("nonce",i.nc),a.src=function(e){return i.p+""+({}[e]||e)+"-es5."+{1:"19816864d8bf40ef8134"}[e]+".js"}(e);var c=new Error;u=function(r){a.onerror=a.onload=null,clearTimeout(l);var t=o[e];if(0!==t){if(t){var n=r&&("load"===r.type?"missing":r.type),u=r&&r.target&&r.target.src;c.message="Loading chunk "+e+" failed.\n("+n+": "+u+")",c.name="ChunkLoadError",c.type=n,c.request=u,t[1](c)}o[e]=void 0}};var l=setTimeout((function(){u({type:"timeout",target:a})}),12e4);a.onerror=a.onload=u,document.head.appendChild(a)}return Promise.all(r)},i.m=e,i.c=n,i.d=function(e,r,t){i.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},i.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},i.t=function(e,r){if(1&r&&(e=i(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(i.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)i.d(t,n,(function(r){return e[r]}).bind(null,n));return t},i.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return i.d(r,"a",r),r},i.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},i.p="",i.oe=function(e){throw console.error(e),e};var a=window.webpackJsonp=window.webpackJsonp||[],c=a.push.bind(a);a.push=r,a=a.slice();for(var l=0;l<a.length;l++)r(a[l]);var f=c;t()}([]);

View File

@@ -240,10 +240,10 @@ async function getVideosForSub(sub, user_uid = null) {
if (sub.name) { if (sub.name) {
appendedBasePath = getAppendedBasePath(sub, basePath); appendedBasePath = getAppendedBasePath(sub, basePath);
} else { } else {
appendedBasePath = basePath + (sub.isPlaylist ? 'playlists/%(playlist_title)s' : 'channels/%(uploader)s'); appendedBasePath = path.join(basePath, (sub.isPlaylist ? 'playlists/%(playlist_title)s' : 'channels/%(uploader)s'));
} }
let downloadConfig = ['-o', appendedBasePath + '/%(title)s.mp4', '-f', 'bestvideo[ext=mp4]+bestaudio[ext=m4a]/mp4', '-ciw', '--write-annotations', '--write-thumbnail', '--write-info-json', '--print-json']; let downloadConfig = ['-o', appendedBasePath + '/%(title)s.mp4', '-f', 'bestvideo[ext=mp4]+bestaudio[ext=m4a]/mp4', '-ciw', '--write-info-json', '--print-json'];
let archive_dir = null; let archive_dir = null;
let archive_path = null; let archive_path = null;
@@ -265,11 +265,38 @@ async function getVideosForSub(sub, user_uid = null) {
downloadConfig.push('--dateafter', sub.timerange); downloadConfig.push('--dateafter', sub.timerange);
} }
let useCookies = config_api.getConfigItem('ytdl_use_cookies');
if (useCookies) {
if (fs.existsSync(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.');
}
}
// get videos // get videos
logger.verbose('Subscribe: getting videos for subscription ' + sub.name); logger.verbose('Subscription: getting videos for subscription ' + sub.name);
youtubedl.exec(sub.url, downloadConfig, {}, function(err, output) { youtubedl.exec(sub.url, downloadConfig, {}, function(err, output) {
if (err) { logger.verbose('Subscription: finished check for ' + sub.name);
if (err && !output) {
logger.error(err.stderr); logger.error(err.stderr);
if (err.stderr.includes('This video is unavailable')) {
logger.info('An error was encountered with at least one video, backup method will be used.')
try {
const outputs = err.stdout.split(/\r\n|\r|\n/);
for (let i = 0; i < outputs.length; i++) {
const output = JSON.parse(outputs[i]);
handleOutputJSON(sub, sub_db, output, i === 0)
if (err.stderr.includes(output['id']) && archive_path) {
// we found a video that errored! add it to the archive to prevent future errors
fs.appendFileSync(archive_path, output['id']);
}
}
} catch(e) {
logger.error('Backup method failed. See error below:');
logger.error(e);
}
}
resolve(false); resolve(false);
} else if (output) { } else if (output) {
if (output.length === 0 || (output.length === 1 && output[0] === '')) { if (output.length === 0 || (output.length === 1 && output[0] === '')) {
@@ -287,17 +314,8 @@ async function getVideosForSub(sub, user_uid = null) {
continue; continue;
} }
if (sub.streamingOnly) { const reset_videos = i === 0;
if (i === 0) { handleOutputJSON(sub, sub_db, output_json, reset_videos);
sub_db.assign({videos: []}).write();
}
// remove unnecessary info
output_json.formats = null;
// add to db
sub_db.get('videos').push(output_json).write();
}
// TODO: Potentially store downloaded files in db? // TODO: Potentially store downloaded files in db?
@@ -308,6 +326,20 @@ async function getVideosForSub(sub, user_uid = null) {
}); });
} }
function handleOutputJSON(sub, sub_db, output_json, reset_videos = false) {
if (sub.streamingOnly) {
if (reset_videos) {
sub_db.assign({videos: []}).write();
}
// remove unnecessary info
output_json.formats = null;
// add to db
sub_db.get('videos').push(output_json).write();
}
}
function getAllSubscriptions(user_uid = null) { function getAllSubscriptions(user_uid = null) {
if (user_uid) if (user_uid)
return users_db.get('users').find({uid: user_uid}).get('subscriptions').value(); return users_db.get('users').find({uid: user_uid}).get('subscriptions').value();

7
package-lock.json generated
View File

@@ -1,6 +1,6 @@
{ {
"name": "youtube-dl-material", "name": "youtube-dl-material",
"version": "3.6.0", "version": "4.0.0",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {
@@ -9113,6 +9113,11 @@
"resolved": "https://registry.npmjs.org/ng-lazyload-image/-/ng-lazyload-image-7.1.0.tgz", "resolved": "https://registry.npmjs.org/ng-lazyload-image/-/ng-lazyload-image-7.1.0.tgz",
"integrity": "sha512-1fip2FdPBDRnjGyBokI/DupBxOnrKh2lbtT8X8N1oPbE3KBZXXl82VIKcK2Sx+XQD67/+VtFzlISmrgsatzYuw==" "integrity": "sha512-1fip2FdPBDRnjGyBokI/DupBxOnrKh2lbtT8X8N1oPbE3KBZXXl82VIKcK2Sx+XQD67/+VtFzlISmrgsatzYuw=="
}, },
"ngx-file-drop": {
"version": "9.0.1",
"resolved": "https://registry.npmjs.org/ngx-file-drop/-/ngx-file-drop-9.0.1.tgz",
"integrity": "sha512-xtUUjGMr9c8wwSfA4Cyy0iZMPLnBOg9i32A3tHOPfEivRrn9evULvxriCM45Qz6HpuuqA7vZGxGZZTCUIj/h3A=="
},
"ngx-videogular": { "ngx-videogular": {
"version": "9.0.1", "version": "9.0.1",
"resolved": "https://registry.npmjs.org/ngx-videogular/-/ngx-videogular-9.0.1.tgz", "resolved": "https://registry.npmjs.org/ngx-videogular/-/ngx-videogular-9.0.1.tgz",

View File

@@ -33,9 +33,10 @@
"core-js": "^2.4.1", "core-js": "^2.4.1",
"file-saver": "^2.0.2", "file-saver": "^2.0.2",
"filesize": "^6.1.0", "filesize": "^6.1.0",
"ng-lazyload-image": "^7.0.1",
"ngx-videogular": "^9.0.1",
"fingerprintjs2": "^2.1.0", "fingerprintjs2": "^2.1.0",
"ng-lazyload-image": "^7.0.1",
"ngx-file-drop": "^9.0.1",
"ngx-videogular": "^9.0.1",
"rxjs": "^6.5.3", "rxjs": "^6.5.3",
"rxjs-compat": "^6.0.0-rc.0", "rxjs-compat": "^6.0.0-rc.0",
"tslib": "^1.10.0", "tslib": "^1.10.0",

View File

@@ -40,9 +40,10 @@
<mat-sidenav-container style="height: 100%"> <mat-sidenav-container style="height: 100%">
<mat-sidenav #sidenav> <mat-sidenav #sidenav>
<mat-nav-list> <mat-nav-list>
<a mat-list-item (click)="sidenav.close()" routerLink='/home'><ng-container i18n="Navigation menu Home Page title">Home</ng-container></a> <a *ngIf="postsService.config && (!postsService.config.Advanced.multi_user_mode || postsService.isLoggedIn)" mat-list-item (click)="sidenav.close()" routerLink='/home'><ng-container i18n="Navigation menu Home Page title">Home</ng-container></a>
<a *ngIf="allowSubscriptions && (!postsService.isLoggedIn || postsService.permissions.includes('subscriptions'))" mat-list-item (click)="sidenav.close()" routerLink='/subscriptions'><ng-container i18n="Navigation menu Subscriptions Page title">Subscriptions</ng-container></a> <a *ngIf="postsService.config && postsService.config.Advanced.multi_user_mode && !postsService.isLoggedIn" mat-list-item (click)="sidenav.close()" routerLink='/login'><ng-container i18n="Navigation menu Login Page title">Login</ng-container></a>
<a *ngIf="enableDownloadsManager && (!postsService.isLoggedIn || postsService.permissions.includes('downloads_manager'))" mat-list-item (click)="sidenav.close()" routerLink='/downloads'><ng-container i18n="Navigation menu Downloads Page title">Downloads</ng-container></a> <a *ngIf="postsService.config && allowSubscriptions && (!postsService.config.Advanced.multi_user_mode || (postsService.isLoggedIn && postsService.permissions.includes('subscriptions')))" mat-list-item (click)="sidenav.close()" routerLink='/subscriptions'><ng-container i18n="Navigation menu Subscriptions Page title">Subscriptions</ng-container></a>
<a *ngIf="postsService.config && enableDownloadsManager && (!postsService.config.Advanced.multi_user_mode || (postsService.isLoggedIn && postsService.permissions.includes('downloads_manager')))" mat-list-item (click)="sidenav.close()" routerLink='/downloads'><ng-container i18n="Navigation menu Downloads Page title">Downloads</ng-container></a>
</mat-nav-list> </mat-nav-list>
</mat-sidenav> </mat-sidenav>
<mat-sidenav-content [style.background]="postsService.theme ? postsService.theme.background_color : null"> <mat-sidenav-content [style.background]="postsService.theme ? postsService.theme.background_color : null">

View File

@@ -1,5 +1,5 @@
import { BrowserModule } from '@angular/platform-browser'; import { BrowserModule } from '@angular/platform-browser';
import {BrowserAnimationsModule} from '@angular/platform-browser/animations'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { NgModule, LOCALE_ID } from '@angular/core'; import { NgModule, LOCALE_ID } from '@angular/core';
import { registerLocaleData, CommonModule } from '@angular/common'; import { registerLocaleData, CommonModule } from '@angular/common';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
@@ -25,21 +25,21 @@ import { MatSlideToggleModule } from '@angular/material/slide-toggle';
import { MatSnackBarModule } from '@angular/material/snack-bar'; import { MatSnackBarModule } from '@angular/material/snack-bar';
import { MatToolbarModule } from '@angular/material/toolbar'; import { MatToolbarModule } from '@angular/material/toolbar';
import { MatTabsModule } from '@angular/material/tabs'; import { MatTabsModule } from '@angular/material/tabs';
import {MatPaginatorModule} from '@angular/material/paginator'; import { MatPaginatorModule } from '@angular/material/paginator';
import {MatSortModule} from '@angular/material/sort'; import { MatSortModule } from '@angular/material/sort';
import {MatTableModule} from '@angular/material/table'; import { MatTableModule } from '@angular/material/table';
import {DragDropModule} from '@angular/cdk/drag-drop'; import { DragDropModule } from '@angular/cdk/drag-drop';
import {ClipboardModule} from '@angular/cdk/clipboard'; import { ClipboardModule } from '@angular/cdk/clipboard';
import {FormsModule, ReactiveFormsModule} from '@angular/forms'; import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { AppComponent } from './app.component'; import { AppComponent } from './app.component';
import { HttpClientModule, HttpClient } from '@angular/common/http'; import { HttpClientModule, HttpClient } from '@angular/common/http';
import { PostsService } from 'app/posts.services'; import { PostsService } from 'app/posts.services';
import { FileCardComponent } from './file-card/file-card.component'; import { FileCardComponent } from './file-card/file-card.component';
import {RouterModule} from '@angular/router'; import { RouterModule } from '@angular/router';
import { AppRoutingModule } from './app-routing.module'; import { AppRoutingModule } from './app-routing.module';
import { MainComponent } from './main/main.component'; import { MainComponent } from './main/main.component';
import { PlayerComponent } from './player/player.component'; import { PlayerComponent } from './player/player.component';
import {VgCoreModule, VgControlsModule, VgOverlayPlayModule, VgBufferingModule} from 'ngx-videogular'; import { VgCoreModule, VgControlsModule, VgOverlayPlayModule, VgBufferingModule } from 'ngx-videogular';
import { InputDialogComponent } from './input-dialog/input-dialog.component'; import { InputDialogComponent } from './input-dialog/input-dialog.component';
import { LazyLoadImageModule, IsVisibleProps } from 'ng-lazyload-image'; import { LazyLoadImageModule, IsVisibleProps } from 'ng-lazyload-image';
import { audioFilesMouseHovering, videoFilesMouseHovering, audioFilesOpened, videoFilesOpened } from './main/main.component'; import { audioFilesMouseHovering, videoFilesMouseHovering, audioFilesOpened, videoFilesOpened } from './main/main.component';
@@ -52,7 +52,8 @@ import { SubscriptionFileCardComponent } from './subscription/subscription-file-
import { SubscriptionInfoDialogComponent } from './dialogs/subscription-info-dialog/subscription-info-dialog.component'; import { SubscriptionInfoDialogComponent } from './dialogs/subscription-info-dialog/subscription-info-dialog.component';
import { SettingsComponent } from './settings/settings.component'; import { SettingsComponent } from './settings/settings.component';
import { CheckOrSetPinDialogComponent } from './dialogs/check-or-set-pin-dialog/check-or-set-pin-dialog.component'; import { CheckOrSetPinDialogComponent } from './dialogs/check-or-set-pin-dialog/check-or-set-pin-dialog.component';
import {MatChipsModule} from '@angular/material/chips'; import { MatChipsModule } from '@angular/material/chips';
import { NgxFileDropModule } from 'ngx-file-drop';
import es from '@angular/common/locales/es'; import es from '@angular/common/locales/es';
import { AboutDialogComponent } from './dialogs/about-dialog/about-dialog.component'; import { AboutDialogComponent } from './dialogs/about-dialog/about-dialog.component';
@@ -69,6 +70,8 @@ import { ModifyUsersComponent } from './components/modify-users/modify-users.com
import { AddUserDialogComponent } from './dialogs/add-user-dialog/add-user-dialog.component'; import { AddUserDialogComponent } from './dialogs/add-user-dialog/add-user-dialog.component';
import { ManageUserComponent } from './components/manage-user/manage-user.component'; import { ManageUserComponent } from './components/manage-user/manage-user.component';
import { ManageRoleComponent } from './components/manage-role/manage-role.component'; import { ManageRoleComponent } from './components/manage-role/manage-role.component';
import { CookiesUploaderDialogComponent } from './dialogs/cookies-uploader-dialog/cookies-uploader-dialog.component';
registerLocaleData(es, 'es'); registerLocaleData(es, 'es');
export function isVisible({ event, element, scrollContainer, offset }: IsVisibleProps<any>) { export function isVisible({ event, element, scrollContainer, offset }: IsVisibleProps<any>) {
@@ -105,7 +108,8 @@ export function isVisible({ event, element, scrollContainer, offset }: IsVisible
ModifyUsersComponent, ModifyUsersComponent,
AddUserDialogComponent, AddUserDialogComponent,
ManageUserComponent, ManageUserComponent,
ManageRoleComponent ManageRoleComponent,
CookiesUploaderDialogComponent
], ],
imports: [ imports: [
CommonModule, CommonModule,
@@ -145,6 +149,7 @@ export function isVisible({ event, element, scrollContainer, offset }: IsVisible
MatChipsModule, MatChipsModule,
DragDropModule, DragDropModule,
ClipboardModule, ClipboardModule,
NgxFileDropModule,
VgCoreModule, VgCoreModule,
VgControlsModule, VgControlsModule,
VgOverlayPlayModule, VgOverlayPlayModule,

View File

@@ -0,0 +1,40 @@
<h4 mat-dialog-title i18n="Cookies uploader dialog title">Upload new cookies</h4>
<mat-dialog-content>
<div>
<div class="center">
<ngx-file-drop [multiple]="false" accept=".txt" dropZoneLabel="Drop files here" (onFileDrop)="dropped($event)"
(onFileOver)="fileOver($event)" (onFileLeave)="fileLeave($event)">
<ng-template ngx-file-drop-content-tmp let-openFileSelector="openFileSelector">
<div>
<div>
<ng-container i18n="Drag and Drop">Drag and Drop</ng-container>
</div>
<div style="margin-top: 6px;">
<button mat-stroked-button (click)="openFileSelector()">Browse Files</button>
</div>
</div>
</ng-template>
</ngx-file-drop>
<div style="margin-top: 15px;">
<p style="font-size: 14px;" i18n="Cookies upload warning">NOTE: Uploading new cookies will overrride your previous cookies. Also note that cookies are instance-wide, not per-user.</p>
</div>
<div style="margin-top: 10px;">
<table class="table">
<tbody class="upload-name-style">
<tr *ngFor="let item of files; let i=index">
<td style="vertical-align: middle;">
<strong>{{ item.relativePath }}</strong>
</td>
<td>
<button [disabled]="uploading || uploaded" (click)="uploadFile()" style="float: right" matTooltip="Upload" mat-mini-fab><mat-icon>publish</mat-icon><mat-spinner *ngIf="uploading" class="spinner" [diameter]="38"></mat-spinner></button>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</mat-dialog-content>
<mat-dialog-actions><button style="margin-bottom: 5px;" mat-dialog-close mat-stroked-button><ng-container i18n="Close">Close</ng-container></button></mat-dialog-actions>

View File

@@ -0,0 +1,5 @@
.spinner {
bottom: 1px;
left: 0.5px;
position: absolute;
}

View File

@@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { CookiesUploaderDialogComponent } from './cookies-uploader-dialog.component';
describe('CookiesUploaderDialogComponent', () => {
let component: CookiesUploaderDialogComponent;
let fixture: ComponentFixture<CookiesUploaderDialogComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ CookiesUploaderDialogComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(CookiesUploaderDialogComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,57 @@
import { Component, OnInit } from '@angular/core';
import { NgxFileDropEntry, FileSystemFileEntry, FileSystemDirectoryEntry } from 'ngx-file-drop';
import { PostsService } from 'app/posts.services';
@Component({
selector: 'app-cookies-uploader-dialog',
templateUrl: './cookies-uploader-dialog.component.html',
styleUrls: ['./cookies-uploader-dialog.component.scss']
})
export class CookiesUploaderDialogComponent implements OnInit {
public files: NgxFileDropEntry[] = [];
uploading = false;
uploaded = false;
constructor(private postsService: PostsService) { }
ngOnInit(): void {
}
public dropped(files: NgxFileDropEntry[]) {
this.files = files;
this.uploading = false;
this.uploaded = false;
}
uploadFile() {
this.uploading = true;
for (const droppedFile of this.files) {
// Is it a file?
if (droppedFile.fileEntry.isFile) {
const fileEntry = droppedFile.fileEntry as FileSystemFileEntry;
fileEntry.file((file: File) => {
// You could upload it like this:
const formData = new FormData()
formData.append('cookies', file, droppedFile.relativePath);
this.postsService.uploadCookiesFile(formData).subscribe(res => {
this.uploading = false;
if (res['success']) {
this.uploaded = true;
this.postsService.openSnackBar('Cookies successfully uploaded!');
}
}, err => {
this.uploading = false;
});
});
}
}
}
public fileOver(event) {
}
public fileLeave(event) {
}
}

View File

@@ -30,7 +30,7 @@ export class DownloadItemComponent implements OnInit {
constructor() { } constructor() { }
ngOnInit() { ngOnInit() {
if (this.download && this.download.url && this.download.url.includes('youtube')) { if (this.download && this.download.url && this.download.url.includes('youtu')) {
const string_id = (this.download.is_playlist ? '?list=' : '?v=') const string_id = (this.download.is_playlist ? '?list=' : '?v=')
const index_offset = (this.download.is_playlist ? 6 : 3); const index_offset = (this.download.is_playlist ? 6 : 3);
const end_index = this.download.url.indexOf(string_id) + index_offset; const end_index = this.download.url.indexOf(string_id) + index_offset;

View File

@@ -44,6 +44,13 @@ export class FileCardComponent implements OnInit {
ngOnInit() { ngOnInit() {
this.type = this.isAudio ? 'audio' : 'video'; this.type = this.isAudio ? 'audio' : 'video';
if (this.file && this.file.url && this.file.url.includes('youtu')) {
const string_id = (this.isPlaylist ? '?list=' : '?v=')
const index_offset = (this.isPlaylist ? 6 : 3);
const end_index = this.file.url.indexOf(string_id) + index_offset;
this.name = this.file.url.substring(end_index, this.file.url.length);
}
} }
deleteFile(blacklistMode = false) { deleteFile(blacklistMode = false) {

View File

@@ -11,10 +11,7 @@
<div class="row"> <div class="row">
<div [ngClass]="allowQualitySelect ? 'col-sm-9' : null" class="col-12"> <div [ngClass]="allowQualitySelect ? 'col-sm-9' : null" class="col-12">
<mat-form-field color="accent" class="example-full-width"> <mat-form-field color="accent" class="example-full-width">
<input style="padding-right: 25px;" matInput (keyup.enter)="downloadClicked()" (ngModelChange)="inputChanged($event)" [(ngModel)]="url" [placeholder]="'URL' + (youtubeSearchEnabled ? ' or search' : '')" type="url" name="url" [formControl]="urlForm" #urlinput> <input style="padding-right: 25px;" matInput (keyup.enter)="downloadClicked()" (ngModelChange)="inputChanged($event)" [(ngModel)]="url" [placeholder]="'URL' + (youtubeSearchEnabled ? ' or search' : '')" type="url" name="url" #urlinput>
<mat-error *ngIf="urlError || urlForm.invalid">
<ng-container i18n="Enter valid URL error">Please enter a valid URL!</ng-container>
</mat-error>
</mat-form-field> </mat-form-field>
<button type="button" class="input-clear-button" mat-icon-button (click)="clearInput()"><mat-icon>clear</mat-icon></button> <button type="button" class="input-clear-button" mat-icon-button (click)="clearInput()"><mat-icon>clear</mat-icon></button>
</div> </div>

View File

@@ -73,6 +73,8 @@ export class PostsService implements CanActivate {
this.httpOptions.params = this.httpOptions.params.set('sessionID', this.session_id); this.httpOptions.params = this.httpOptions.params.set('sessionID', this.session_id);
}); });
const login_not_required = this.router.url !== '/player'
// get config // get config
this.loadNavItems().subscribe(res => { this.loadNavItems().subscribe(res => {
const result = !this.debugMode ? res['config_file'] : res; const result = !this.debugMode ? res['config_file'] : res;
@@ -84,6 +86,8 @@ export class PostsService implements CanActivate {
this.token = localStorage.getItem('jwt_token'); this.token = localStorage.getItem('jwt_token');
this.httpOptions.params = this.httpOptions.params.set('jwt', this.token); this.httpOptions.params = this.httpOptions.params.set('jwt', this.token);
this.jwtAuth(); this.jwtAuth();
} else if (login_not_required) {
this.setInitialized();
} else { } else {
this.sendToLogin(); this.sendToLogin();
} }
@@ -218,6 +222,10 @@ export class PostsService implements CanActivate {
{responseType: 'blob', params: this.httpOptions.params}); {responseType: 'blob', params: this.httpOptions.params});
} }
uploadCookiesFile(fileFormData) {
return this.http.post(this.path + 'uploadCookies', fileFormData, this.httpOptions);
}
downloadArchive(sub) { downloadArchive(sub) {
return this.http.post(this.path + 'downloadArchive', {sub: sub}, {responseType: 'blob', params: this.httpOptions.params}); return this.http.post(this.path + 'downloadArchive', {sub: sub}, {responseType: 'blob', params: this.httpOptions.params});
} }

View File

@@ -152,6 +152,10 @@
<mat-checkbox color="accent" [(ngModel)]="new_config['Downloader']['use_youtubedl_archive']"><ng-container i18n="Use youtubedl archive setting">Use youtube-dl archive</ng-container></mat-checkbox> <mat-checkbox color="accent" [(ngModel)]="new_config['Downloader']['use_youtubedl_archive']"><ng-container i18n="Use youtubedl archive setting">Use youtube-dl archive</ng-container></mat-checkbox>
<p>Note: This setting only applies to downloads on the Home page. If you would like to use youtube-dl archive functionality in subscriptions, head down to the Subscriptions section.</p> <p>Note: This setting only applies to downloads on the Home page. If you would like to use youtube-dl archive functionality in subscriptions, head down to the Subscriptions section.</p>
</div> </div>
<div class="col-12 mt-3">
<mat-checkbox color="accent" [(ngModel)]="new_config['Downloader']['safe_download_override']"><ng-container i18n="Safe download override setting">Safe download override</ng-container></mat-checkbox>
</div>
</div> </div>
</div> </div>
</ng-template> </ng-template>
@@ -287,6 +291,15 @@
</div> </div>
</div> </div>
<mat-divider></mat-divider> <mat-divider></mat-divider>
<div *ngIf="new_config" class="container-fluid">
<div class="row">
<div class="col-12 mt-4">
<mat-checkbox color="accent" [(ngModel)]="new_config['Advanced']['use_cookies']"><ng-container i18n="Use cookies setting">Use Cookies</ng-container></mat-checkbox>
<button class="checkbox-button" mat-stroked-button (click)="openCookiesUploaderDialog()"><ng-container i18n="Set cookies button">Set Cookies</ng-container></button>
</div>
</div>
</div>
<mat-divider></mat-divider>
<div *ngIf="new_config" class="container-fluid mt-1"> <div *ngIf="new_config" class="container-fluid mt-1">
<app-updater></app-updater> <app-updater></app-updater>
</div> </div>

View File

@@ -25,3 +25,9 @@
.text-field { .text-field {
min-width: 30%; min-width: 30%;
} }
.checkbox-button {
margin-left: 15px;
margin-bottom: 12px;
bottom: 4px;
}

View File

@@ -8,6 +8,7 @@ import { MatDialog } from '@angular/material/dialog';
import { ArgModifierDialogComponent } from 'app/dialogs/arg-modifier-dialog/arg-modifier-dialog.component'; import { ArgModifierDialogComponent } from 'app/dialogs/arg-modifier-dialog/arg-modifier-dialog.component';
import { CURRENT_VERSION } from 'app/consts'; import { CURRENT_VERSION } from 'app/consts';
import { MatCheckboxChange } from '@angular/material/checkbox'; import { MatCheckboxChange } from '@angular/material/checkbox';
import { CookiesUploaderDialogComponent } from 'app/dialogs/cookies-uploader-dialog/cookies-uploader-dialog.component';
@Component({ @Component({
selector: 'app-settings', selector: 'app-settings',
@@ -156,6 +157,12 @@ export class SettingsComponent implements OnInit {
}); });
} }
openCookiesUploaderDialog() {
this.dialog.open(CookiesUploaderDialogComponent, {
width: '65vw'
});
}
// snackbar helper // snackbar helper
public openSnackBar(message: string, action: string = '') { public openSnackBar(message: string, action: string = '') {
this.snackBar.open(message, action, { this.snackBar.open(message, action, {

View File

@@ -13,7 +13,8 @@
"path-audio": "audio/", "path-audio": "audio/",
"path-video": "video/", "path-video": "video/",
"use_youtubedl_archive": false, "use_youtubedl_archive": false,
"custom_args": "" "custom_args": "",
"safe_download_override": false
}, },
"Extra": { "Extra": {
"title_top": "YoutubeDL-Material", "title_top": "YoutubeDL-Material",
@@ -37,7 +38,7 @@
"Subscriptions": { "Subscriptions": {
"allow_subscriptions": true, "allow_subscriptions": true,
"subscriptions_base_path": "subscriptions/", "subscriptions_base_path": "subscriptions/",
"subscriptions_check_interval": "300", "subscriptions_check_interval": "30",
"subscriptions_use_youtubedl_archive": true "subscriptions_use_youtubedl_archive": true
}, },
"Users": { "Users": {
@@ -49,7 +50,8 @@
"custom_downloading_agent": "", "custom_downloading_agent": "",
"multi_user_mode": true, "multi_user_mode": true,
"allow_advanced_download": true, "allow_advanced_download": true,
"logger_level": "debug" "logger_level": "debug",
"use_cookies": true
} }
} }
} }