mirror of
https://github.com/Tzahi12345/YoutubeDL-Material.git
synced 2026-04-08 20:31:28 +03:00
Merge branch 'master' of https://github.com/Tzahi12345/YoutubeDL-Material into subscriptions-custom-path
This commit is contained in:
@@ -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" ]
|
||||||
|
|||||||
110
backend/app.js
110
backend/app.js
@@ -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');
|
||||||
@@ -1505,6 +1522,14 @@ async function generateArgs(url, type, options) {
|
|||||||
if (youtubeUsername && youtubePassword) {
|
if (youtubeUsername && youtubePassword) {
|
||||||
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);
|
||||||
@@ -1512,7 +1537,11 @@ async function generateArgs(url, type, options) {
|
|||||||
|
|
||||||
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) => {
|
||||||
|
|||||||
@@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
17
backend/entrypoint.sh
Executable 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 "$@"
|
||||||
131
backend/package-lock.json
generated
131
backend/package-lock.json
generated
@@ -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",
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
1
backend/public/1-es2015.19816864d8bf40ef8134.js
Normal file
1
backend/public/1-es2015.19816864d8bf40ef8134.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
backend/public/1-es5.19816864d8bf40ef8134.js
Normal file
1
backend/public/1-es5.19816864d8bf40ef8134.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
1
backend/public/main-es2015.0cbc545a4a3bee376826.js
Normal file
1
backend/public/main-es2015.0cbc545a4a3bee376826.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
backend/public/main-es5.0cbc545a4a3bee376826.js
Normal file
1
backend/public/main-es5.0cbc545a4a3bee376826.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -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()}([]);
|
||||||
@@ -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()}([]);
|
||||||
@@ -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
7
package-lock.json
generated
@@ -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",
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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>
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
.spinner {
|
||||||
|
bottom: 1px;
|
||||||
|
left: 0.5px;
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
@@ -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();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -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) {
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -24,4 +24,10 @@
|
|||||||
|
|
||||||
.text-field {
|
.text-field {
|
||||||
min-width: 30%;
|
min-width: 30%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox-button {
|
||||||
|
margin-left: 15px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
bottom: 4px;
|
||||||
}
|
}
|
||||||
@@ -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, {
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user