mirror of
https://github.com/Tzahi12345/YoutubeDL-Material.git
synced 2026-03-22 20:51:00 +03:00
Compare commits
21 Commits
arm-autobu
...
kodi-integ
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a623012901 | ||
|
|
95e53b9549 | ||
|
|
46ed0fe992 | ||
|
|
082252ab1e | ||
|
|
5eccaa13e5 | ||
|
|
71633950b2 | ||
|
|
f31dad0215 | ||
|
|
7efbe40bb2 | ||
|
|
5b768b5bda | ||
|
|
365cbc3ffa | ||
|
|
44647f3306 | ||
|
|
8a7409478a | ||
|
|
70159813e5 | ||
|
|
babba9aa30 | ||
|
|
d292275956 | ||
|
|
ba2acedb94 | ||
|
|
aa0558b770 | ||
|
|
d7f04fc90a | ||
|
|
087c9f1bb1 | ||
|
|
f874617965 | ||
|
|
8fb8543829 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -65,3 +65,4 @@ backend/appdata/logs/error.log
|
|||||||
backend/appdata/users.json
|
backend/appdata/users.json
|
||||||
backend/users/*
|
backend/users/*
|
||||||
backend/appdata/cookies.txt
|
backend/appdata/cookies.txt
|
||||||
|
backend/public
|
||||||
150
backend/app.js
150
backend/app.js
@@ -7,7 +7,7 @@ var path = require('path');
|
|||||||
var youtubedl = require('youtube-dl');
|
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 glob = require("glob")
|
||||||
var multer = require('multer');
|
var multer = require('multer');
|
||||||
var express = require("express");
|
var express = require("express");
|
||||||
var bodyParser = require("body-parser");
|
var bodyParser = require("body-parser");
|
||||||
@@ -1146,12 +1146,29 @@ async function downloadFileByURL_exec(url, type, options, sessionID = null) {
|
|||||||
type: type,
|
type: type,
|
||||||
percent_complete: 0,
|
percent_complete: 0,
|
||||||
is_playlist: url.includes('playlist'),
|
is_playlist: url.includes('playlist'),
|
||||||
timestamp_start: Date.now()
|
timestamp_start: Date.now(),
|
||||||
|
filesize: null
|
||||||
};
|
};
|
||||||
const download = downloads[session][download_uid];
|
const download = downloads[session][download_uid];
|
||||||
updateDownloads();
|
updateDownloads();
|
||||||
|
|
||||||
|
// get video info prior to download
|
||||||
|
const info = await getVideoInfoByURL(url, downloadConfig, download);
|
||||||
|
if (!info) {
|
||||||
|
resolve(false);
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
// store info in download for future use
|
||||||
|
download['_filename'] = info['_filename'];
|
||||||
|
download['filesize'] = utils.getExpectedFileSize(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
const download_checker = setInterval(() => checkDownloadPercent(download), 1000);
|
||||||
|
|
||||||
|
// download file
|
||||||
youtubedl.exec(url, downloadConfig, {}, function(err, output) {
|
youtubedl.exec(url, downloadConfig, {}, function(err, output) {
|
||||||
|
clearInterval(download_checker); // stops the download checker from running as the download finished (or errored)
|
||||||
|
|
||||||
download['downloading'] = false;
|
download['downloading'] = false;
|
||||||
download['timestamp_end'] = Date.now();
|
download['timestamp_end'] = Date.now();
|
||||||
var file_uid = null;
|
var file_uid = null;
|
||||||
@@ -1164,7 +1181,7 @@ async function downloadFileByURL_exec(url, type, options, sessionID = null) {
|
|||||||
download['error'] = err.stderr;
|
download['error'] = err.stderr;
|
||||||
updateDownloads();
|
updateDownloads();
|
||||||
resolve(false);
|
resolve(false);
|
||||||
throw err;
|
return;
|
||||||
} 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.';
|
||||||
@@ -1407,7 +1424,7 @@ async function generateArgs(url, type, options) {
|
|||||||
var youtubePassword = options.youtubePassword;
|
var youtubePassword = options.youtubePassword;
|
||||||
|
|
||||||
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', 'bestvideo+bestaudio', '--merge-output-format', 'mp4'];
|
||||||
const is_youtube = url.includes('youtu');
|
const is_youtube = url.includes('youtu');
|
||||||
if (!is_audio && !is_youtube) {
|
if (!is_audio && !is_youtube) {
|
||||||
// tiktok videos fail when using the default format
|
// tiktok videos fail when using the default format
|
||||||
@@ -1485,6 +1502,10 @@ async function generateArgs(url, type, options) {
|
|||||||
downloadConfig.push('--download-archive', merged_path);
|
downloadConfig.push('--download-archive', merged_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (config_api.getConfigItem('ytdl_include_thumbnail')) {
|
||||||
|
downloadConfig.push('--write-thumbnail');
|
||||||
|
}
|
||||||
|
|
||||||
if (globalArgs && globalArgs !== '') {
|
if (globalArgs && globalArgs !== '') {
|
||||||
// adds global args
|
// adds global args
|
||||||
if (downloadConfig.indexOf('-o') !== -1 && globalArgs.split(',,').indexOf('-o') !== -1) {
|
if (downloadConfig.indexOf('-o') !== -1 && globalArgs.split(',,').indexOf('-o') !== -1) {
|
||||||
@@ -1497,11 +1518,36 @@ async function generateArgs(url, type, options) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
logger.verbose(`youtube-dl args being used: ${downloadConfig.join(',')}`);
|
logger.verbose(`youtube-dl args being used: ${downloadConfig.join(',')}`);
|
||||||
// downloadConfig.map((arg) => `"${arg}"`);
|
|
||||||
resolve(downloadConfig);
|
resolve(downloadConfig);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function getVideoInfoByURL(url, args = [], download = null) {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
// remove bad args
|
||||||
|
const new_args = [...args];
|
||||||
|
|
||||||
|
const archiveArgIndex = new_args.indexOf('--download-archive');
|
||||||
|
if (archiveArgIndex !== -1) {
|
||||||
|
new_args.splice(archiveArgIndex, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// actually get info
|
||||||
|
youtubedl.getInfo(url, new_args, (err, output) => {
|
||||||
|
if (output) {
|
||||||
|
resolve(output);
|
||||||
|
} else {
|
||||||
|
logger.error(`Error while retrieving info on video with URL ${url} with the following message: ${err}`);
|
||||||
|
if (download) {
|
||||||
|
download['error'] = `Failed pre-check for video info: ${err}`;
|
||||||
|
updateDownloads();
|
||||||
|
}
|
||||||
|
resolve(null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// currently only works for single urls
|
// currently only works for single urls
|
||||||
async function getUrlInfos(urls) {
|
async function getUrlInfos(urls) {
|
||||||
let startDate = Date.now();
|
let startDate = Date.now();
|
||||||
@@ -1559,47 +1605,33 @@ function updateDownloads() {
|
|||||||
db.assign({downloads: downloads}).write();
|
db.assign({downloads: downloads}).write();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
function checkDownloadPercent(download) {
|
||||||
function checkDownloads() {
|
/*
|
||||||
for (let [session_id, session_downloads] of Object.entries(downloads)) {
|
This is more of an art than a science, we're just selecting files that start with the file name,
|
||||||
for (let [download_uid, download_obj] of Object.entries(session_downloads)) {
|
thus capturing the parts being downloaded in files named like so: '<video title>.<format>.<ext>.part'.
|
||||||
if (download_obj && !download_obj['complete'] && !download_obj['error']
|
|
||||||
&& download_obj.timestamp_start > timestamp_server_start) {
|
|
||||||
// download is still running (presumably)
|
|
||||||
download_obj.percent_complete = getDownloadPercent(download_obj);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
function getDownloadPercent(download_obj) {
|
Any file that starts with <video title> will be counted as part of the "bytes downloaded", which will
|
||||||
if (!download_obj.final_size) {
|
be divided by the "total expected bytes."
|
||||||
if (fs.existsSync(download_obj.expected_json_path)) {
|
*/
|
||||||
const file_json = JSON.parse(fs.readFileSync(download_obj.expected_json_path, 'utf8'));
|
const file_id = download['file_id'];
|
||||||
let calculated_filesize = null;
|
const filename = path.format(path.parse(download['_filename'].substring(0, download['_filename'].length-4)));
|
||||||
if (file_json['format_id']) {
|
const resulting_file_size = download['filesize'];
|
||||||
calculated_filesize = 0;
|
|
||||||
const formats_used = file_json['format_id'].split('+');
|
glob(`${filename}*`, (err, files) => {
|
||||||
for (let i = 0; i < file_json['formats'].length; i++) {
|
let sum_size = 0;
|
||||||
if (formats_used.includes(file_json['formats'][i]['format_id'])) {
|
files.forEach(file => {
|
||||||
calculated_filesize += file_json['formats'][i]['filesize'];
|
try {
|
||||||
}
|
const file_stats = fs.statSync(file);
|
||||||
|
if (file_stats && file_stats.size) {
|
||||||
|
sum_size += file_stats.size;
|
||||||
}
|
}
|
||||||
|
} catch (e) {
|
||||||
|
|
||||||
}
|
}
|
||||||
download_obj.final_size = calculated_filesize;
|
});
|
||||||
} else {
|
download['percent_complete'] = (sum_size/resulting_file_size * 100).toFixed(2);
|
||||||
console.log('could not find json file');
|
updateDownloads();
|
||||||
}
|
});
|
||||||
}
|
|
||||||
if (fs.existsSync(download_obj.expected_path)) {
|
|
||||||
const stats = fs.statSync(download_obj.expected_path);
|
|
||||||
const size = stats.size;
|
|
||||||
return (size / download_obj.final_size)*100;
|
|
||||||
} else {
|
|
||||||
console.log('could not find file');
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// youtube-dl functions
|
// youtube-dl functions
|
||||||
@@ -1821,7 +1853,7 @@ app.post('/api/tomp3', optionalJwt, async function(req, res) {
|
|||||||
const is_playlist = url.includes('playlist');
|
const is_playlist = url.includes('playlist');
|
||||||
|
|
||||||
let result_obj = null;
|
let result_obj = null;
|
||||||
if (safeDownloadOverride || is_playlist || options.customQualityConfiguration || options.customArgs || options.maxBitrate)
|
if (true || 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);
|
||||||
@@ -1833,6 +1865,7 @@ app.post('/api/tomp3', optionalJwt, async function(req, res) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
app.post('/api/tomp4', optionalJwt, async function(req, res) {
|
app.post('/api/tomp4', optionalJwt, async function(req, res) {
|
||||||
|
req.setTimeout(0); // remove timeout in case of long videos
|
||||||
var url = req.body.url;
|
var url = req.body.url;
|
||||||
var options = {
|
var options = {
|
||||||
customArgs: req.body.customArgs,
|
customArgs: req.body.customArgs,
|
||||||
@@ -1850,7 +1883,7 @@ app.post('/api/tomp4', optionalJwt, async function(req, res) {
|
|||||||
const is_playlist = url.includes('playlist');
|
const is_playlist = url.includes('playlist');
|
||||||
|
|
||||||
let result_obj = null;
|
let result_obj = null;
|
||||||
if (safeDownloadOverride || is_playlist || options.customQualityConfiguration || options.customArgs || options.selectedHeight || !url.includes('youtu'))
|
if (true || 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);
|
||||||
@@ -1878,6 +1911,14 @@ app.get('/api/getMp3s', optionalJwt, function(req, res) {
|
|||||||
playlists = auth_api.getUserPlaylists(req.user.uid, 'audio');
|
playlists = auth_api.getUserPlaylists(req.user.uid, 'audio');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mp3s = JSON.parse(JSON.stringify(mp3s));
|
||||||
|
|
||||||
|
// add thumbnails if present
|
||||||
|
mp3s.forEach(mp3 => {
|
||||||
|
if (mp3['thumbnailPath'] && fs.existsSync(mp3['thumbnailPath']))
|
||||||
|
mp3['thumbnailBlob'] = fs.readFileSync(mp3['thumbnailPath']);
|
||||||
|
});
|
||||||
|
|
||||||
res.send({
|
res.send({
|
||||||
mp3s: mp3s,
|
mp3s: mp3s,
|
||||||
playlists: playlists
|
playlists: playlists
|
||||||
@@ -1897,6 +1938,14 @@ app.get('/api/getMp4s', optionalJwt, function(req, res) {
|
|||||||
playlists = auth_api.getUserPlaylists(req.user.uid, 'video');
|
playlists = auth_api.getUserPlaylists(req.user.uid, 'video');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mp4s = JSON.parse(JSON.stringify(mp4s));
|
||||||
|
|
||||||
|
// add thumbnails if present
|
||||||
|
mp4s.forEach(mp4 => {
|
||||||
|
if (mp4['thumbnailPath'] && fs.existsSync(mp4['thumbnailPath']))
|
||||||
|
mp4['thumbnailBlob'] = fs.readFileSync(mp4['thumbnailPath']);
|
||||||
|
});
|
||||||
|
|
||||||
res.send({
|
res.send({
|
||||||
mp4s: mp4s,
|
mp4s: mp4s,
|
||||||
playlists: playlists
|
playlists: playlists
|
||||||
@@ -1981,6 +2030,14 @@ app.post('/api/getAllFiles', optionalJwt, function (req, res) {
|
|||||||
files = files.concat(sub.videos);
|
files = files.concat(sub.videos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
files = JSON.parse(JSON.stringify(files));
|
||||||
|
|
||||||
|
// add thumbnails if present
|
||||||
|
files.forEach(file => {
|
||||||
|
if (file['thumbnailPath'] && fs.existsSync(file['thumbnailPath']))
|
||||||
|
file['thumbnailBlob'] = fs.readFileSync(file['thumbnailPath']);
|
||||||
|
});
|
||||||
|
|
||||||
res.send({
|
res.send({
|
||||||
files: files,
|
files: files,
|
||||||
playlists: playlists
|
playlists: playlists
|
||||||
@@ -2821,8 +2878,7 @@ app.post('/api/auth/register'
|
|||||||
, optionalJwt
|
, optionalJwt
|
||||||
, auth_api.registerUser);
|
, auth_api.registerUser);
|
||||||
app.post('/api/auth/login'
|
app.post('/api/auth/login'
|
||||||
, auth_api.passport.authenticate('local', {})
|
, auth_api.passport.authenticate(['local', 'ldapauth'], {})
|
||||||
, auth_api.passport.authorize('local')
|
|
||||||
, auth_api.generateJWT
|
, auth_api.generateJWT
|
||||||
, auth_api.returnAuthResponse
|
, auth_api.returnAuthResponse
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -9,7 +9,9 @@
|
|||||||
"path-video": "video/",
|
"path-video": "video/",
|
||||||
"use_youtubedl_archive": false,
|
"use_youtubedl_archive": false,
|
||||||
"custom_args": "",
|
"custom_args": "",
|
||||||
"safe_download_override": false
|
"safe_download_override": false,
|
||||||
|
"include_thumbnail": true,
|
||||||
|
"include_metadata": true
|
||||||
},
|
},
|
||||||
"Extra": {
|
"Extra": {
|
||||||
"title_top": "YoutubeDL-Material",
|
"title_top": "YoutubeDL-Material",
|
||||||
@@ -36,7 +38,15 @@
|
|||||||
},
|
},
|
||||||
"Users": {
|
"Users": {
|
||||||
"base_path": "users/",
|
"base_path": "users/",
|
||||||
"allow_registration": true
|
"allow_registration": true,
|
||||||
|
"auth_method": "internal",
|
||||||
|
"ldap_config": {
|
||||||
|
"url": "ldap://localhost:389",
|
||||||
|
"bindDN": "cn=root",
|
||||||
|
"bindCredentials": "secret",
|
||||||
|
"searchBase": "ou=passport-ldapauth",
|
||||||
|
"searchFilter": "(uid={{username}})"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"Advanced": {
|
"Advanced": {
|
||||||
"use_default_downloading_agent": true,
|
"use_default_downloading_agent": true,
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ var bcrypt = require('bcryptjs');
|
|||||||
|
|
||||||
|
|
||||||
var LocalStrategy = require('passport-local').Strategy;
|
var LocalStrategy = require('passport-local').Strategy;
|
||||||
|
var LdapStrategy = require('passport-ldapauth');
|
||||||
var JwtStrategy = require('passport-jwt').Strategy,
|
var JwtStrategy = require('passport-jwt').Strategy,
|
||||||
ExtractJwt = require('passport-jwt').ExtractJwt;
|
ExtractJwt = require('passport-jwt').ExtractJwt;
|
||||||
|
|
||||||
@@ -90,24 +91,7 @@ exports.registerUser = function(req, res) {
|
|||||||
|
|
||||||
bcrypt.hash(plaintextPassword, saltRounds)
|
bcrypt.hash(plaintextPassword, saltRounds)
|
||||||
.then(function(hash) {
|
.then(function(hash) {
|
||||||
let new_user = {
|
let new_user = generateUserObject(userid, username, hash);
|
||||||
name: username,
|
|
||||||
uid: userid,
|
|
||||||
passhash: hash,
|
|
||||||
files: {
|
|
||||||
audio: [],
|
|
||||||
video: []
|
|
||||||
},
|
|
||||||
playlists: {
|
|
||||||
audio: [],
|
|
||||||
video: []
|
|
||||||
},
|
|
||||||
subscriptions: [],
|
|
||||||
created: Date.now(),
|
|
||||||
role: userid === 'admin' ? 'admin' : 'user',
|
|
||||||
permissions: [],
|
|
||||||
permission_overrides: []
|
|
||||||
};
|
|
||||||
// check if user exists
|
// check if user exists
|
||||||
if (users_db.get('users').find({uid: userid}).value()) {
|
if (users_db.get('users').find({uid: userid}).value()) {
|
||||||
// user id is taken!
|
// user id is taken!
|
||||||
@@ -153,52 +137,43 @@ exports.registerUser = function(req, res) {
|
|||||||
|
|
||||||
|
|
||||||
exports.passport.use(new LocalStrategy({
|
exports.passport.use(new LocalStrategy({
|
||||||
usernameField: 'userid',
|
usernameField: 'username',
|
||||||
passwordField: 'password'},
|
passwordField: 'password'},
|
||||||
function(username, password, done) {
|
function(username, password, done) {
|
||||||
const user = users_db.get('users').find({name: username}).value();
|
const user = users_db.get('users').find({name: username}).value();
|
||||||
if (!user) { logger.error(`User ${username} not found`); return done(null, false); }
|
if (!user) { logger.error(`User ${username} not found`); return done(null, false); }
|
||||||
|
if (user.auth_method && user.auth_method !== 'internal') { return done(null, false); }
|
||||||
if (user) {
|
if (user) {
|
||||||
return done(null, bcrypt.compareSync(password, user.passhash) ? user : false);
|
return done(null, bcrypt.compareSync(password, user.passhash) ? user : false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
));
|
));
|
||||||
|
|
||||||
/*passport.use(new BasicStrategy(
|
var getLDAPConfiguration = function(req, callback) {
|
||||||
function(userid, plainTextPassword, done) {
|
const ldap_config = config_api.getConfigItem('ytdl_ldap_config');
|
||||||
const user = users_db.get('users').find({name: userid}).value();
|
const opts = {server: ldap_config};
|
||||||
if (user) {
|
callback(null, opts);
|
||||||
var hashedPwd = user.passhash;
|
};
|
||||||
return bcrypt.compare(plainTextPassword, hashedPwd);
|
|
||||||
} else {
|
exports.passport.use(new LdapStrategy(getLDAPConfiguration,
|
||||||
return false;
|
function(user, done) {
|
||||||
|
// check if ldap auth is enabled
|
||||||
|
const ldap_enabled = config_api.getConfigItem('ytdl_auth_method') === 'ldap';
|
||||||
|
if (!ldap_enabled) return done(null, false);
|
||||||
|
|
||||||
|
const user_uid = user.uid;
|
||||||
|
let db_user = users_db.get('users').find({uid: user_uid}).value();
|
||||||
|
if (!db_user) {
|
||||||
|
// generate DB user
|
||||||
|
let new_user = generateUserObject(user_uid, user_uid, null, 'ldap');
|
||||||
|
users_db.get('users').push(new_user).write();
|
||||||
|
db_user = new_user;
|
||||||
|
logger.verbose(`Generated new user ${user_uid} using LDAP`);
|
||||||
}
|
}
|
||||||
|
return done(null, db_user);
|
||||||
}
|
}
|
||||||
));
|
));
|
||||||
*/
|
|
||||||
|
|
||||||
/*************************************************************
|
|
||||||
* This is a wrapper for auth.passport.authenticate().
|
|
||||||
* We use this to change WWW-Authenticate header so
|
|
||||||
* the browser doesn't pop-up challenge dialog box by default.
|
|
||||||
* Browser's will pop-up up dialog when status is 401 and
|
|
||||||
* "WWW-Authenticate:Basic..."
|
|
||||||
*************************************************************/
|
|
||||||
/*
|
|
||||||
exports.authenticateViaPassport = function(req, res, next) {
|
|
||||||
exports.passport.authenticate('basic',{session:false},
|
|
||||||
function(err, user, info) {
|
|
||||||
if(!user){
|
|
||||||
res.set('WWW-Authenticate', 'x'+info); // change to xBasic
|
|
||||||
res.status(401).send('Invalid Authentication');
|
|
||||||
} else {
|
|
||||||
req.user = user;
|
|
||||||
next();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)(req, res, next);
|
|
||||||
};
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**********************************
|
/**********************************
|
||||||
* Generating/Signing a JWT token
|
* Generating/Signing a JWT token
|
||||||
@@ -538,3 +513,26 @@ function getToken(queryParams) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function generateUserObject(userid, username, hash, auth_method = 'internal') {
|
||||||
|
let new_user = {
|
||||||
|
name: username,
|
||||||
|
uid: userid,
|
||||||
|
passhash: auth_method === 'internal' ? hash : null,
|
||||||
|
files: {
|
||||||
|
audio: [],
|
||||||
|
video: []
|
||||||
|
},
|
||||||
|
playlists: {
|
||||||
|
audio: [],
|
||||||
|
video: []
|
||||||
|
},
|
||||||
|
subscriptions: [],
|
||||||
|
created: Date.now(),
|
||||||
|
role: userid === 'admin' && auth_method === 'internal' ? 'admin' : 'user',
|
||||||
|
permissions: [],
|
||||||
|
permission_overrides: [],
|
||||||
|
auth_method: auth_method
|
||||||
|
};
|
||||||
|
return new_user;
|
||||||
|
}
|
||||||
|
|||||||
@@ -186,7 +186,9 @@ DEFAULT_CONFIG = {
|
|||||||
"path-video": "video/",
|
"path-video": "video/",
|
||||||
"use_youtubedl_archive": false,
|
"use_youtubedl_archive": false,
|
||||||
"custom_args": "",
|
"custom_args": "",
|
||||||
"safe_download_override": false
|
"safe_download_override": false,
|
||||||
|
"include_thumbnail": true,
|
||||||
|
"include_metadata": true
|
||||||
},
|
},
|
||||||
"Extra": {
|
"Extra": {
|
||||||
"title_top": "YoutubeDL-Material",
|
"title_top": "YoutubeDL-Material",
|
||||||
@@ -213,7 +215,15 @@ DEFAULT_CONFIG = {
|
|||||||
},
|
},
|
||||||
"Users": {
|
"Users": {
|
||||||
"base_path": "users/",
|
"base_path": "users/",
|
||||||
"allow_registration": true
|
"allow_registration": true,
|
||||||
|
"auth_method": "internal",
|
||||||
|
"ldap_config": {
|
||||||
|
"url": "ldap://localhost:389",
|
||||||
|
"bindDN": "cn=root",
|
||||||
|
"bindCredentials": "secret",
|
||||||
|
"searchBase": "ou=passport-ldapauth",
|
||||||
|
"searchFilter": "(uid={{username}})"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"Advanced": {
|
"Advanced": {
|
||||||
"use_default_downloading_agent": true,
|
"use_default_downloading_agent": true,
|
||||||
|
|||||||
@@ -30,6 +30,14 @@ let CONFIG_ITEMS = {
|
|||||||
'key': 'ytdl_safe_download_override',
|
'key': 'ytdl_safe_download_override',
|
||||||
'path': 'YoutubeDLMaterial.Downloader.safe_download_override'
|
'path': 'YoutubeDLMaterial.Downloader.safe_download_override'
|
||||||
},
|
},
|
||||||
|
'ytdl_include_thumbnail': {
|
||||||
|
'key': 'ytdl_include_thumbnail',
|
||||||
|
'path': 'YoutubeDLMaterial.Downloader.include_thumbnail'
|
||||||
|
},
|
||||||
|
'ytdl_include_metadata': {
|
||||||
|
'key': 'ytdl_include_metadata',
|
||||||
|
'path': 'YoutubeDLMaterial.Downloader.include_metadata'
|
||||||
|
},
|
||||||
|
|
||||||
// Extra
|
// Extra
|
||||||
'ytdl_title_top': {
|
'ytdl_title_top': {
|
||||||
@@ -112,6 +120,14 @@ let CONFIG_ITEMS = {
|
|||||||
'key': 'ytdl_allow_registration',
|
'key': 'ytdl_allow_registration',
|
||||||
'path': 'YoutubeDLMaterial.Users.allow_registration'
|
'path': 'YoutubeDLMaterial.Users.allow_registration'
|
||||||
},
|
},
|
||||||
|
'ytdl_auth_method': {
|
||||||
|
'key': 'ytdl_auth_method',
|
||||||
|
'path': 'YoutubeDLMaterial.Users.auth_method'
|
||||||
|
},
|
||||||
|
'ytdl_ldap_config': {
|
||||||
|
'key': 'ytdl_ldap_config',
|
||||||
|
'path': 'YoutubeDLMaterial.Users.ldap_config'
|
||||||
|
},
|
||||||
|
|
||||||
// Advanced
|
// Advanced
|
||||||
'ytdl_use_default_downloading_agent': {
|
'ytdl_use_default_downloading_agent': {
|
||||||
|
|||||||
@@ -26,11 +26,11 @@ function registerFileDB(file_path, type, multiUserMode = null, sub = null) {
|
|||||||
|
|
||||||
utils.fixVideoMetadataPerms(file_id, type, multiUserMode && multiUserMode.file_path);
|
utils.fixVideoMetadataPerms(file_id, type, multiUserMode && multiUserMode.file_path);
|
||||||
|
|
||||||
// add additional info
|
// creates XML if kodi support is enabled
|
||||||
file_object['uid'] = uuid();
|
if (true) utils.generateNFOFile(file_id, type, multiUserMode && multiUserMode.file_path);
|
||||||
file_object['registered'] = Date.now();
|
|
||||||
path_object = path.parse(file_object['path']);
|
// add thumbnail path
|
||||||
file_object['path'] = path.format(path_object);
|
file_object['thumbnailPath'] = utils.getDownloadedThumbnail(file_id, type, multiUserMode && multiUserMode.file_path);
|
||||||
|
|
||||||
if (!sub) {
|
if (!sub) {
|
||||||
if (multiUserMode) {
|
if (multiUserMode) {
|
||||||
@@ -48,7 +48,13 @@ function registerFileDB(file_path, type, multiUserMode = null, sub = null) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const file_uid = registerFileDBManual(db_path, file_object)
|
const file_uid = registerFileDBManual(db_path, file_object);
|
||||||
|
|
||||||
|
// remove metadata JSON if needed
|
||||||
|
if (!config_api.getConfigItem('ytdl_include_metadata')) {
|
||||||
|
utils.deleteJSONFile(file_id, type, multiUserMode && multiUserMode.file_path)
|
||||||
|
}
|
||||||
|
|
||||||
return file_uid;
|
return file_uid;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -165,6 +171,10 @@ async function importUnregisteredFiles() {
|
|||||||
// add subscriptions to check list
|
// add subscriptions to check list
|
||||||
for (let i = 0; i < subscriptions_to_check.length; i++) {
|
for (let i = 0; i < subscriptions_to_check.length; i++) {
|
||||||
let subscription_to_check = subscriptions_to_check[i];
|
let subscription_to_check = subscriptions_to_check[i];
|
||||||
|
if (!subscription_to_check.name) {
|
||||||
|
// TODO: Remove subscription as it'll never complete
|
||||||
|
continue;
|
||||||
|
}
|
||||||
dirs_to_check.push({
|
dirs_to_check.push({
|
||||||
basePath: multi_user_mode ? path.join(usersFileFolder, subscription_to_check.user_uid, 'subscriptions', subscription_to_check.isPlaylist ? 'playlists/' : 'channels/', subscription_to_check.name)
|
basePath: multi_user_mode ? path.join(usersFileFolder, subscription_to_check.user_uid, 'subscriptions', subscription_to_check.isPlaylist ? 'playlists/' : 'channels/', subscription_to_check.name)
|
||||||
: path.join(subscriptions_base_path, subscription_to_check.isPlaylist ? 'playlists/' : 'channels/', subscription_to_check.name),
|
: path.join(subscriptions_base_path, subscription_to_check.isPlaylist ? 'playlists/' : 'channels/', subscription_to_check.name),
|
||||||
|
|||||||
275
backend/package-lock.json
generated
275
backend/package-lock.json
generated
@@ -4,6 +4,89 @@
|
|||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@types/body-parser": {
|
||||||
|
"version": "1.19.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.0.tgz",
|
||||||
|
"integrity": "sha512-W98JrE0j2K78swW4ukqMleo8R7h/pFETjM2DQ90MF6XK2i4LO4W3gQ71Lt4w3bfm2EvVSyWHplECvB5sK22yFQ==",
|
||||||
|
"requires": {
|
||||||
|
"@types/connect": "*",
|
||||||
|
"@types/node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@types/connect": {
|
||||||
|
"version": "3.4.33",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.33.tgz",
|
||||||
|
"integrity": "sha512-2+FrkXY4zllzTNfJth7jOqEHC+enpLeGslEhpnTAkg21GkRrWV4SsAtqchtT4YS9/nODBU2/ZfsBY2X4J/dX7A==",
|
||||||
|
"requires": {
|
||||||
|
"@types/node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@types/express": {
|
||||||
|
"version": "4.17.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.7.tgz",
|
||||||
|
"integrity": "sha512-dCOT5lcmV/uC2J9k0rPafATeeyz+99xTt54ReX11/LObZgfzJqZNcW27zGhYyX+9iSEGXGt5qLPwRSvBZcLvtQ==",
|
||||||
|
"requires": {
|
||||||
|
"@types/body-parser": "*",
|
||||||
|
"@types/express-serve-static-core": "*",
|
||||||
|
"@types/qs": "*",
|
||||||
|
"@types/serve-static": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@types/express-serve-static-core": {
|
||||||
|
"version": "4.17.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.9.tgz",
|
||||||
|
"integrity": "sha512-DG0BYg6yO+ePW+XoDENYz8zhNGC3jDDEpComMYn7WJc4mY1Us8Rw9ax2YhJXxpyk2SF47PQAoQ0YyVT1a0bEkA==",
|
||||||
|
"requires": {
|
||||||
|
"@types/node": "*",
|
||||||
|
"@types/qs": "*",
|
||||||
|
"@types/range-parser": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@types/ldapjs": {
|
||||||
|
"version": "1.0.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/ldapjs/-/ldapjs-1.0.9.tgz",
|
||||||
|
"integrity": "sha512-3PvY7Drp1zoLbcGlothCAkoc5o6Jp9KvUPwHadlHyKp3yPvyeIh7w2zQc9UXMzgDRkoeGXUEODtbEs5XCh9ZyA==",
|
||||||
|
"requires": {
|
||||||
|
"@types/node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@types/mime": {
|
||||||
|
"version": "2.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-2.0.3.tgz",
|
||||||
|
"integrity": "sha512-Jus9s4CDbqwocc5pOAnh8ShfrnMcPHuJYzVcSUU7lrh8Ni5HuIqX3oilL86p3dlTrk0LzHRCgA/GQ7uNCw6l2Q=="
|
||||||
|
},
|
||||||
|
"@types/node": {
|
||||||
|
"version": "14.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.6.0.tgz",
|
||||||
|
"integrity": "sha512-mikldZQitV94akrc4sCcSjtJfsTKt4p+e/s0AGscVA6XArQ9kFclP+ZiYUMnq987rc6QlYxXv/EivqlfSLxpKA=="
|
||||||
|
},
|
||||||
|
"@types/passport": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.4.tgz",
|
||||||
|
"integrity": "sha512-h5OfAbfBBYSzjeU0GTuuqYEk9McTgWeGQql9g3gUw2/NNCfD7VgExVRYJVVeU13Twn202Mvk9BT0bUrl30sEgA==",
|
||||||
|
"requires": {
|
||||||
|
"@types/express": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@types/qs": {
|
||||||
|
"version": "6.9.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.4.tgz",
|
||||||
|
"integrity": "sha512-+wYo+L6ZF6BMoEjtf8zB2esQsqdV6WsjRK/GP9WOgLPrq87PbNWgIxS76dS5uvl/QXtHGakZmwTznIfcPXcKlQ=="
|
||||||
|
},
|
||||||
|
"@types/range-parser": {
|
||||||
|
"version": "1.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz",
|
||||||
|
"integrity": "sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA=="
|
||||||
|
},
|
||||||
|
"@types/serve-static": {
|
||||||
|
"version": "1.13.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.5.tgz",
|
||||||
|
"integrity": "sha512-6M64P58N+OXjU432WoLLBQxbA0LRGBCRm7aAGQJ+SMC1IMl0dgRVi9EFfoDcS2a7Xogygk/eGN94CfwU9UF7UQ==",
|
||||||
|
"requires": {
|
||||||
|
"@types/express-serve-static-core": "*",
|
||||||
|
"@types/mime": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"abbrev": {
|
"abbrev": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
|
||||||
@@ -169,6 +252,14 @@
|
|||||||
"resolved": "https://registry.npmjs.org/aws4/-/aws4-1.9.1.tgz",
|
"resolved": "https://registry.npmjs.org/aws4/-/aws4-1.9.1.tgz",
|
||||||
"integrity": "sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug=="
|
"integrity": "sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug=="
|
||||||
},
|
},
|
||||||
|
"backoff": {
|
||||||
|
"version": "2.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/backoff/-/backoff-2.5.0.tgz",
|
||||||
|
"integrity": "sha1-9hbtqdPktmuMp/ynn2lXIsX44m8=",
|
||||||
|
"requires": {
|
||||||
|
"precond": "0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"balanced-match": {
|
"balanced-match": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
|
||||||
@@ -315,6 +406,17 @@
|
|||||||
"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="
|
||||||
},
|
},
|
||||||
|
"bunyan": {
|
||||||
|
"version": "1.8.14",
|
||||||
|
"resolved": "https://registry.npmjs.org/bunyan/-/bunyan-1.8.14.tgz",
|
||||||
|
"integrity": "sha512-LlahJUxXzZLuw/hetUQJmRgZ1LF6+cr5TPpRj6jf327AsiIq2jhYEH4oqUUkVKTor+9w2BT3oxVwhzE5lw9tcg==",
|
||||||
|
"requires": {
|
||||||
|
"dtrace-provider": "~0.8",
|
||||||
|
"moment": "^2.19.3",
|
||||||
|
"mv": "~2",
|
||||||
|
"safe-json-stringify": "~1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"busboy": {
|
"busboy": {
|
||||||
"version": "0.2.14",
|
"version": "0.2.14",
|
||||||
"resolved": "https://registry.npmjs.org/busboy/-/busboy-0.2.14.tgz",
|
"resolved": "https://registry.npmjs.org/busboy/-/busboy-0.2.14.tgz",
|
||||||
@@ -739,6 +841,15 @@
|
|||||||
"is-obj": "^1.0.0"
|
"is-obj": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"dtrace-provider": {
|
||||||
|
"version": "0.8.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.8.8.tgz",
|
||||||
|
"integrity": "sha512-b7Z7cNtHPhH9EJhNNbbeqTcXB8LGFFZhq1PGgEvpeHlzd36bhbdTWoE/Ba/YguqpBSlAPKnARWhVlhunCMwfxg==",
|
||||||
|
"optional": true,
|
||||||
|
"requires": {
|
||||||
|
"nan": "^2.14.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"duplexer2": {
|
"duplexer2": {
|
||||||
"version": "0.1.4",
|
"version": "0.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz",
|
||||||
@@ -1472,6 +1583,72 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"ldap-filter": {
|
||||||
|
"version": "0.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/ldap-filter/-/ldap-filter-0.2.2.tgz",
|
||||||
|
"integrity": "sha1-8rhCvguG2jNSeYUFsx68rlkNd9A=",
|
||||||
|
"requires": {
|
||||||
|
"assert-plus": "0.1.5"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"assert-plus": {
|
||||||
|
"version": "0.1.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.1.5.tgz",
|
||||||
|
"integrity": "sha1-7nQAlBMALYTOxyGcasgRgS5yMWA="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ldapauth-fork": {
|
||||||
|
"version": "4.3.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/ldapauth-fork/-/ldapauth-fork-4.3.3.tgz",
|
||||||
|
"integrity": "sha512-x76VpQ5ZqkwAJmqwcD6KIwDiNEbgIGIPGwC/eA17e1dxWhlTx36w0DlLOFwjTuZ2iuaLTsZsUprlVqvSlwc/1Q==",
|
||||||
|
"requires": {
|
||||||
|
"@types/ldapjs": "^1.0.0",
|
||||||
|
"@types/node": "*",
|
||||||
|
"bcryptjs": "^2.4.0",
|
||||||
|
"ldapjs": "^1.0.2",
|
||||||
|
"lru-cache": "^5.1.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"lru-cache": {
|
||||||
|
"version": "5.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
|
||||||
|
"integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
|
||||||
|
"requires": {
|
||||||
|
"yallist": "^3.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"yallist": {
|
||||||
|
"version": "3.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
|
||||||
|
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ldapjs": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/ldapjs/-/ldapjs-1.0.2.tgz",
|
||||||
|
"integrity": "sha1-VE/3Ayt7g8aPBwEyjZKXqmlDQPk=",
|
||||||
|
"requires": {
|
||||||
|
"asn1": "0.2.3",
|
||||||
|
"assert-plus": "^1.0.0",
|
||||||
|
"backoff": "^2.5.0",
|
||||||
|
"bunyan": "^1.8.3",
|
||||||
|
"dashdash": "^1.14.0",
|
||||||
|
"dtrace-provider": "~0.8",
|
||||||
|
"ldap-filter": "0.2.2",
|
||||||
|
"once": "^1.4.0",
|
||||||
|
"vasync": "^1.6.4",
|
||||||
|
"verror": "^1.8.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"asn1": {
|
||||||
|
"version": "0.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz",
|
||||||
|
"integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"listenercount": {
|
"listenercount": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/listenercount/-/listenercount-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/listenercount/-/listenercount-1.0.1.tgz",
|
||||||
@@ -1672,6 +1849,12 @@
|
|||||||
"minimist": "^1.2.5"
|
"minimist": "^1.2.5"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"moment": {
|
||||||
|
"version": "2.27.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/moment/-/moment-2.27.0.tgz",
|
||||||
|
"integrity": "sha512-al0MUK7cpIcglMv3YF13qSgdAIqxHTO7brRtaz3DlSULbqfazqkc5kEjNrLDOM7fsjshoFIihnU8snrP7zUvhQ==",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
"ms": {
|
"ms": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||||
@@ -1717,6 +1900,41 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"mv": {
|
||||||
|
"version": "2.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/mv/-/mv-2.1.1.tgz",
|
||||||
|
"integrity": "sha1-rmzg1vbV4KT32JN5jQPB6pVZtqI=",
|
||||||
|
"optional": true,
|
||||||
|
"requires": {
|
||||||
|
"mkdirp": "~0.5.1",
|
||||||
|
"ncp": "~2.0.0",
|
||||||
|
"rimraf": "~2.4.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"glob": {
|
||||||
|
"version": "6.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz",
|
||||||
|
"integrity": "sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI=",
|
||||||
|
"optional": true,
|
||||||
|
"requires": {
|
||||||
|
"inflight": "^1.0.4",
|
||||||
|
"inherits": "2",
|
||||||
|
"minimatch": "2 || 3",
|
||||||
|
"once": "^1.3.0",
|
||||||
|
"path-is-absolute": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"rimraf": {
|
||||||
|
"version": "2.4.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz",
|
||||||
|
"integrity": "sha1-7nEM5dk6j9uFb7Xqj/Di11k0sto=",
|
||||||
|
"optional": true,
|
||||||
|
"requires": {
|
||||||
|
"glob": "^6.0.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"mz": {
|
"mz": {
|
||||||
"version": "2.7.0",
|
"version": "2.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz",
|
||||||
@@ -1727,11 +1945,23 @@
|
|||||||
"thenify-all": "^1.0.0"
|
"thenify-all": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"nan": {
|
||||||
|
"version": "2.14.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/nan/-/nan-2.14.1.tgz",
|
||||||
|
"integrity": "sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw==",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
"nanoid": {
|
"nanoid": {
|
||||||
"version": "2.1.11",
|
"version": "2.1.11",
|
||||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-2.1.11.tgz",
|
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-2.1.11.tgz",
|
||||||
"integrity": "sha512-s/snB+WGm6uwi0WjsZdaVcuf3KJXlfGl2LcxgwkEwJF0D/BWzVWAZW/XY4bFaiR7s0Jk3FPvlnepg1H1b1UwlA=="
|
"integrity": "sha512-s/snB+WGm6uwi0WjsZdaVcuf3KJXlfGl2LcxgwkEwJF0D/BWzVWAZW/XY4bFaiR7s0Jk3FPvlnepg1H1b1UwlA=="
|
||||||
},
|
},
|
||||||
|
"ncp": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz",
|
||||||
|
"integrity": "sha1-GVoh1sRuNh0vsSgbo4uR6d9727M=",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
"negotiator": {
|
"negotiator": {
|
||||||
"version": "0.6.2",
|
"version": "0.6.2",
|
||||||
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
|
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
|
||||||
@@ -1918,6 +2148,17 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"passport-ldapauth": {
|
||||||
|
"version": "2.1.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/passport-ldapauth/-/passport-ldapauth-2.1.4.tgz",
|
||||||
|
"integrity": "sha512-VeVL69ZK+cpJe0DKMSGuwcf7k+V4dr0U0Y7ZhXL785pcRb5gRA6qYZfIH+XTsAzwqTK9l0Dn3Ds4weOZ1jKkLQ==",
|
||||||
|
"requires": {
|
||||||
|
"@types/node": "*",
|
||||||
|
"@types/passport": "^1.0.0",
|
||||||
|
"ldapauth-fork": "^4.3.2",
|
||||||
|
"passport-strategy": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"passport-local": {
|
"passport-local": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/passport-local/-/passport-local-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/passport-local/-/passport-local-1.0.0.tgz",
|
||||||
@@ -1971,6 +2212,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
|
||||||
"integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY="
|
"integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY="
|
||||||
},
|
},
|
||||||
|
"precond": {
|
||||||
|
"version": "0.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/precond/-/precond-0.2.3.tgz",
|
||||||
|
"integrity": "sha1-qpWRvKokkj8eD0hJ0kD0fvwQdaw="
|
||||||
|
},
|
||||||
"prepend-http": {
|
"prepend-http": {
|
||||||
"version": "1.0.4",
|
"version": "1.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz",
|
||||||
@@ -2166,6 +2412,12 @@
|
|||||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
|
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
|
||||||
},
|
},
|
||||||
|
"safe-json-stringify": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/safe-json-stringify/-/safe-json-stringify-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-gH8eh2nZudPQO6TytOvbxnuhYBOvDBBLW52tz5q6X58lJcd/tkmqFR+5Z9adS8aJtURSXWThWy/xJtJwixErvg==",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
"safer-buffer": {
|
"safer-buffer": {
|
||||||
"version": "2.1.2",
|
"version": "2.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||||
@@ -2682,6 +2934,29 @@
|
|||||||
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
||||||
"integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
|
"integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
|
||||||
},
|
},
|
||||||
|
"vasync": {
|
||||||
|
"version": "1.6.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/vasync/-/vasync-1.6.4.tgz",
|
||||||
|
"integrity": "sha1-3+k2Fq0OeugBszKp2Iv8XNyOHR8=",
|
||||||
|
"requires": {
|
||||||
|
"verror": "1.6.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"extsprintf": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.2.0.tgz",
|
||||||
|
"integrity": "sha1-WtlGwi9bMrp/jNdCZxHG6KP8JSk="
|
||||||
|
},
|
||||||
|
"verror": {
|
||||||
|
"version": "1.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/verror/-/verror-1.6.0.tgz",
|
||||||
|
"integrity": "sha1-fROyex+swuLakEBetepuW90lLqU=",
|
||||||
|
"requires": {
|
||||||
|
"extsprintf": "1.2.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"verror": {
|
"verror": {
|
||||||
"version": "1.10.0",
|
"version": "1.10.0",
|
||||||
"resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
|
"resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
|
||||||
|
|||||||
@@ -37,6 +37,7 @@
|
|||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
"fluent-ffmpeg": "^2.1.2",
|
"fluent-ffmpeg": "^2.1.2",
|
||||||
"fs-extra": "^9.0.0",
|
"fs-extra": "^9.0.0",
|
||||||
|
"glob": "^7.1.6",
|
||||||
"jsonwebtoken": "^8.5.1",
|
"jsonwebtoken": "^8.5.1",
|
||||||
"lowdb": "^1.0.0",
|
"lowdb": "^1.0.0",
|
||||||
"md5": "^2.2.1",
|
"md5": "^2.2.1",
|
||||||
@@ -48,6 +49,7 @@
|
|||||||
"passport": "^0.4.1",
|
"passport": "^0.4.1",
|
||||||
"passport-http": "^0.3.0",
|
"passport-http": "^0.3.0",
|
||||||
"passport-jwt": "^4.0.0",
|
"passport-jwt": "^4.0.0",
|
||||||
|
"passport-ldapauth": "^2.1.4",
|
||||||
"passport-local": "^1.0.0",
|
"passport-local": "^1.0.0",
|
||||||
"progress": "^2.0.3",
|
"progress": "^2.0.3",
|
||||||
"ps-node": "^0.1.6",
|
"ps-node": "^0.1.6",
|
||||||
|
|||||||
@@ -345,6 +345,10 @@ async function getVideosForSub(sub, user_uid = null) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (config_api.getConfigItem('ytdl_include_thumbnail')) {
|
||||||
|
downloadConfig.push('--write-thumbnail');
|
||||||
|
}
|
||||||
|
|
||||||
// get videos
|
// get videos
|
||||||
logger.verbose('Subscription: 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) {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
var fs = require('fs-extra')
|
var fs = require('fs-extra')
|
||||||
var path = require('path')
|
var path = require('path')
|
||||||
const config_api = require('./config');
|
const config_api = require('./config');
|
||||||
|
const { create } = require('xmlbuilder2');
|
||||||
|
|
||||||
const is_windows = process.platform === 'win32';
|
const is_windows = process.platform === 'win32';
|
||||||
|
|
||||||
@@ -28,7 +29,7 @@ function getDownloadedFilesByType(basePath, type) {
|
|||||||
var located_files = recFindByExt(basePath, ext);
|
var located_files = recFindByExt(basePath, ext);
|
||||||
for (let i = 0; i < located_files.length; i++) {
|
for (let i = 0; i < located_files.length; i++) {
|
||||||
let file = located_files[i];
|
let file = located_files[i];
|
||||||
var file_path = path.basename(file);
|
var file_path = file.substring(basePath.includes('\\') ? basePath.length+1 : basePath.length, file.length);
|
||||||
|
|
||||||
var stats = fs.statSync(file);
|
var stats = fs.statSync(file);
|
||||||
|
|
||||||
@@ -88,6 +89,41 @@ function getJSONByType(type, name, customPath, openReadPerms = false) {
|
|||||||
return type === 'audio' ? getJSONMp3(name, customPath, openReadPerms) : getJSONMp4(name, customPath, openReadPerms)
|
return type === 'audio' ? getJSONMp3(name, customPath, openReadPerms) : getJSONMp4(name, customPath, openReadPerms)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getDownloadedThumbnail(name, type, customPath = null) {
|
||||||
|
if (!customPath) customPath = type === 'audio' ? config_api.getConfigItem('ytdl_audio_folder_path') : config_api.getConfigItem('ytdl_video_folder_path');
|
||||||
|
|
||||||
|
let jpgPath = path.join(customPath, name + '.jpg');
|
||||||
|
let webpPath = path.join(customPath, name + '.webp');
|
||||||
|
let pngPath = path.join(customPath, name + '.png');
|
||||||
|
|
||||||
|
if (fs.existsSync(jpgPath))
|
||||||
|
return jpgPath;
|
||||||
|
else if (fs.existsSync(webpPath))
|
||||||
|
return webpPath;
|
||||||
|
else if (fs.existsSync(pngPath))
|
||||||
|
return pngPath;
|
||||||
|
else
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getExpectedFileSize(info_json) {
|
||||||
|
if (info_json['filesize']) {
|
||||||
|
return info_json['filesize'];
|
||||||
|
}
|
||||||
|
|
||||||
|
const formats = info_json['format_id'].split('+');
|
||||||
|
let expected_filesize = 0;
|
||||||
|
formats.forEach(format_id => {
|
||||||
|
info_json.formats.forEach(available_format => {
|
||||||
|
if (available_format.format_id === format_id && available_format.filesize) {
|
||||||
|
expected_filesize += available_format.filesize;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return expected_filesize;
|
||||||
|
}
|
||||||
|
|
||||||
function fixVideoMetadataPerms(name, type, customPath = null) {
|
function fixVideoMetadataPerms(name, type, customPath = null) {
|
||||||
if (is_windows) return;
|
if (is_windows) return;
|
||||||
if (!customPath) customPath = type === 'audio' ? config_api.getConfigItem('ytdl_audio_folder_path')
|
if (!customPath) customPath = type === 'audio' ? config_api.getConfigItem('ytdl_audio_folder_path')
|
||||||
@@ -110,6 +146,48 @@ function fixVideoMetadataPerms(name, type, customPath = null) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function generateNFOFile(file_id, type, customPath = null) {
|
||||||
|
if (!customPath) customPath = type === 'audio' ? config_api.getConfigItem('ytdl_audio_folder_path')
|
||||||
|
: config_api.getConfigItem('ytdl_video_folder_path');
|
||||||
|
|
||||||
|
const file_obj = getJSONByType(type, file_id, customPath);
|
||||||
|
|
||||||
|
const target_dir = path.dirname(file_obj['_filename']);
|
||||||
|
|
||||||
|
const file_name = path.basename(file_obj['_filename']);
|
||||||
|
const target_file_name = file_name.substring(0, file_name.length-4) + '.nfo';
|
||||||
|
|
||||||
|
const xml_obj = {
|
||||||
|
episodedetails: {
|
||||||
|
title: file_obj['fulltitle'],
|
||||||
|
episode: file_obj['playlist_index'] ? file_obj['playlist_index'] : undefined,
|
||||||
|
premiered: file_obj['upload_date'],
|
||||||
|
plot: `${file_obj['uploader_url']}\n${file_obj['description']}\n${file_obj['playlist_title']}`
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const generated_xml = create(xml_obj).end({prettyPrint: true});
|
||||||
|
|
||||||
|
const xml_parts = generated_xml.split('\n');
|
||||||
|
xml_parts[0] = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>';
|
||||||
|
const final_xml = xml_parts.join('\n');
|
||||||
|
|
||||||
|
fs.writeFileSync(path.join(target_dir, target_file_name), final_xml);
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteJSONFile(name, type, customPath = null) {
|
||||||
|
if (!customPath) customPath = type === 'audio' ? config_api.getConfigItem('ytdl_audio_folder_path')
|
||||||
|
: config_api.getConfigItem('ytdl_video_folder_path');
|
||||||
|
|
||||||
|
const ext = type === 'audio' ? '.mp3' : '.mp4';
|
||||||
|
let json_path = path.join(customPath, name + '.info.json');
|
||||||
|
let alternate_json_path = path.join(customPath, name + ext + '.info.json');
|
||||||
|
|
||||||
|
if (fs.existsSync(json_path)) fs.unlinkSync(json_path);
|
||||||
|
if (fs.existsSync(alternate_json_path)) fs.unlinkSync(alternate_json_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
function recFindByExt(base,ext,files,result)
|
function recFindByExt(base,ext,files,result)
|
||||||
{
|
{
|
||||||
files = files || fs.readdirSync(base)
|
files = files || fs.readdirSync(base)
|
||||||
@@ -153,7 +231,11 @@ module.exports = {
|
|||||||
getJSONMp3: getJSONMp3,
|
getJSONMp3: getJSONMp3,
|
||||||
getJSONMp4: getJSONMp4,
|
getJSONMp4: getJSONMp4,
|
||||||
getTrueFileName: getTrueFileName,
|
getTrueFileName: getTrueFileName,
|
||||||
|
getDownloadedThumbnail: getDownloadedThumbnail,
|
||||||
|
getExpectedFileSize: getExpectedFileSize,
|
||||||
fixVideoMetadataPerms: fixVideoMetadataPerms,
|
fixVideoMetadataPerms: fixVideoMetadataPerms,
|
||||||
|
generateNFOFile: generateNFOFile,
|
||||||
|
deleteJSONFile: deleteJSONFile,
|
||||||
getDownloadedFilesByType: getDownloadedFilesByType,
|
getDownloadedFilesByType: getDownloadedFilesByType,
|
||||||
recFindByExt: recFindByExt,
|
recFindByExt: recFindByExt,
|
||||||
File: File
|
File: File
|
||||||
|
|||||||
@@ -38,15 +38,15 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="sidenav-container" style="height: calc(100% - 64px)">
|
<div class="sidenav-container" style="height: calc(100% - 64px)">
|
||||||
<mat-sidenav-container style="height: 100%">
|
<mat-sidenav-container style="height: 100%">
|
||||||
<mat-sidenav [opened]="postsService.sidepanel_mode === 'side' && router.url === '/home'" [mode]="postsService.sidepanel_mode" #sidenav>
|
<mat-sidenav [opened]="postsService.sidepanel_mode === 'side' && !window.location.href.includes('/player')" [mode]="postsService.sidepanel_mode" #sidenav>
|
||||||
<mat-nav-list>
|
<mat-nav-list>
|
||||||
<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="postsService.config && (!postsService.config.Advanced.multi_user_mode || postsService.isLoggedIn)" mat-list-item (click)="postsService.sidepanel_mode === 'over' ? sidenav.close() : null" 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='/login'><ng-container i18n="Navigation menu Login Page title">Login</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="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 && allowSubscriptions && (!postsService.config.Advanced.multi_user_mode || (postsService.isLoggedIn && postsService.permissions.includes('subscriptions')))" mat-list-item (click)="postsService.sidepanel_mode === 'over' ? sidenav.close() : null" 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>
|
<a *ngIf="postsService.config && enableDownloadsManager && (!postsService.config.Advanced.multi_user_mode || (postsService.isLoggedIn && postsService.permissions.includes('downloads_manager')))" mat-list-item (click)="postsService.sidepanel_mode === 'over' ? sidenav.close() : null" routerLink='/downloads'><ng-container i18n="Navigation menu Downloads Page title">Downloads</ng-container></a>
|
||||||
<ng-container *ngIf="postsService.config && allowSubscriptions && postsService.subscriptions && (!postsService.config.Advanced.multi_user_mode || (postsService.isLoggedIn && postsService.permissions.includes('subscriptions')))">
|
<ng-container *ngIf="postsService.config && allowSubscriptions && postsService.subscriptions && (!postsService.config.Advanced.multi_user_mode || (postsService.isLoggedIn && postsService.permissions.includes('subscriptions')))">
|
||||||
<mat-divider></mat-divider>
|
<mat-divider></mat-divider>
|
||||||
<a *ngFor="let subscription of postsService.subscriptions" mat-list-item (click)="sidenav.close()" [routerLink]="['/subscription', { id: subscription.id }]"><ngx-avatar [style.margin-right]="'10px'" size="32" [name]="subscription.name"></ngx-avatar><ng-container i18n="Navigation menu Downloads Page title">{{subscription.name}}</ng-container></a>
|
<a *ngFor="let subscription of postsService.subscriptions" mat-list-item (click)="postsService.sidepanel_mode === 'over' ? sidenav.close() : null" [routerLink]="['/subscription', { id: subscription.id }]"><ngx-avatar [style.margin-right]="'10px'" size="32" [name]="subscription.name"></ngx-avatar><ng-container i18n="Navigation menu Downloads Page title">{{subscription.name}}</ng-container></a>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</mat-nav-list>
|
</mat-nav-list>
|
||||||
</mat-sidenav>
|
</mat-sidenav>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Component, OnInit, ElementRef, ViewChild, HostBinding } from '@angular/core';
|
import { Component, OnInit, ElementRef, ViewChild, HostBinding, AfterViewInit } from '@angular/core';
|
||||||
import {PostsService} from './posts.services';
|
import {PostsService} from './posts.services';
|
||||||
import {FileCardComponent} from './file-card/file-card.component';
|
import {FileCardComponent} from './file-card/file-card.component';
|
||||||
import { Observable } from 'rxjs/Observable';
|
import { Observable } from 'rxjs/Observable';
|
||||||
@@ -30,11 +30,13 @@ import { SetDefaultAdminDialogComponent } from './dialogs/set-default-admin-dial
|
|||||||
templateUrl: './app.component.html',
|
templateUrl: './app.component.html',
|
||||||
styleUrls: ['./app.component.css']
|
styleUrls: ['./app.component.css']
|
||||||
})
|
})
|
||||||
export class AppComponent implements OnInit {
|
export class AppComponent implements OnInit, AfterViewInit {
|
||||||
|
|
||||||
@HostBinding('class') componentCssClass;
|
@HostBinding('class') componentCssClass;
|
||||||
THEMES_CONFIG = THEMES_CONFIG;
|
THEMES_CONFIG = THEMES_CONFIG;
|
||||||
|
|
||||||
|
window = window;
|
||||||
|
|
||||||
// config items
|
// config items
|
||||||
topBarTitle = 'Youtube Downloader';
|
topBarTitle = 'Youtube Downloader';
|
||||||
defaultTheme = null;
|
defaultTheme = null;
|
||||||
@@ -69,6 +71,29 @@ export class AppComponent implements OnInit {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
if (localStorage.getItem('theme')) {
|
||||||
|
this.setTheme(localStorage.getItem('theme'));
|
||||||
|
}
|
||||||
|
|
||||||
|
this.postsService.open_create_default_admin_dialog.subscribe(open => {
|
||||||
|
if (open) {
|
||||||
|
const dialogRef = this.dialog.open(SetDefaultAdminDialogComponent);
|
||||||
|
dialogRef.afterClosed().subscribe(success => {
|
||||||
|
if (success) {
|
||||||
|
if (this.router.url !== '/login') { this.router.navigate(['/login']); }
|
||||||
|
} else {
|
||||||
|
console.error('Failed to create default admin account. See logs for details.');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ngAfterViewInit() {
|
||||||
|
this.postsService.sidenav = this.sidenav;
|
||||||
|
}
|
||||||
|
|
||||||
toggleSidenav() {
|
toggleSidenav() {
|
||||||
this.sidenav.toggle();
|
this.sidenav.toggle();
|
||||||
}
|
}
|
||||||
@@ -89,9 +114,7 @@ export class AppComponent implements OnInit {
|
|||||||
|
|
||||||
// gets the subscriptions
|
// gets the subscriptions
|
||||||
if (this.allowSubscriptions) {
|
if (this.allowSubscriptions) {
|
||||||
this.postsService.getAllSubscriptions().subscribe(res => {
|
this.postsService.reloadSubscriptions();
|
||||||
this.postsService.subscriptions = res['subscriptions'];
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,9 +147,9 @@ export class AppComponent implements OnInit {
|
|||||||
this.postsService.setTheme(theme);
|
this.postsService.setTheme(theme);
|
||||||
|
|
||||||
this.onSetTheme(this.THEMES_CONFIG[theme]['css_label'], old_theme ? this.THEMES_CONFIG[old_theme]['css_label'] : old_theme);
|
this.onSetTheme(this.THEMES_CONFIG[theme]['css_label'], old_theme ? this.THEMES_CONFIG[old_theme]['css_label'] : old_theme);
|
||||||
}
|
}
|
||||||
|
|
||||||
onSetTheme(theme, old_theme) {
|
onSetTheme(theme, old_theme) {
|
||||||
if (old_theme) {
|
if (old_theme) {
|
||||||
document.body.classList.remove(old_theme);
|
document.body.classList.remove(old_theme);
|
||||||
this.overlayContainer.getContainerElement().classList.remove(old_theme);
|
this.overlayContainer.getContainerElement().classList.remove(old_theme);
|
||||||
@@ -148,27 +171,6 @@ onSetTheme(theme, old_theme) {
|
|||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
|
||||||
if (localStorage.getItem('theme')) {
|
|
||||||
this.setTheme(localStorage.getItem('theme'));
|
|
||||||
} else {
|
|
||||||
//
|
|
||||||
}
|
|
||||||
this.postsService.open_create_default_admin_dialog.subscribe(open => {
|
|
||||||
if (open) {
|
|
||||||
const dialogRef = this.dialog.open(SetDefaultAdminDialogComponent);
|
|
||||||
dialogRef.afterClosed().subscribe(success => {
|
|
||||||
if (success) {
|
|
||||||
if (this.router.url !== '/login') { this.router.navigate(['/login']); }
|
|
||||||
} else {
|
|
||||||
console.error('Failed to create default admin account. See logs for details.');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
getSubscriptions() {
|
getSubscriptions() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,17 +55,21 @@ export class RecentVideosComponent implements OnInit {
|
|||||||
if (localStorage.getItem('cached_file_count')) {
|
if (localStorage.getItem('cached_file_count')) {
|
||||||
this.cached_file_count = +localStorage.getItem('cached_file_count');
|
this.cached_file_count = +localStorage.getItem('cached_file_count');
|
||||||
this.loading_files = Array(this.cached_file_count).fill(0);
|
this.loading_files = Array(this.cached_file_count).fill(0);
|
||||||
console.log(this.loading_files);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
|
if (this.postsService.initialized) {
|
||||||
|
this.getAllFiles();
|
||||||
|
}
|
||||||
|
|
||||||
this.postsService.service_initialized.subscribe(init => {
|
this.postsService.service_initialized.subscribe(init => {
|
||||||
if (init) {
|
if (init) {
|
||||||
this.getAllFiles();
|
this.getAllFiles();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
// set filter property to cached
|
// set filter property to cached
|
||||||
const cached_filter_property = localStorage.getItem('filter_property');
|
const cached_filter_property = localStorage.getItem('filter_property');
|
||||||
if (cached_filter_property && this.filterProperties[cached_filter_property]) {
|
if (cached_filter_property && this.filterProperties[cached_filter_property]) {
|
||||||
@@ -145,7 +149,7 @@ export class RecentVideosComponent implements OnInit {
|
|||||||
navigateToFile(file) {
|
navigateToFile(file) {
|
||||||
localStorage.setItem('player_navigator', this.router.url);
|
localStorage.setItem('player_navigator', this.router.url);
|
||||||
if (file.sub_id) {
|
if (file.sub_id) {
|
||||||
const sub = this.postsService.getSubscriptionByID(file.sub_id)
|
const sub = this.postsService.getSubscriptionByID(file.sub_id);
|
||||||
if (sub.streamingOnly) {
|
if (sub.streamingOnly) {
|
||||||
this.router.navigate(['/player', {name: file.id,
|
this.router.navigate(['/player', {name: file.id,
|
||||||
url: file.requested_formats ? file.requested_formats[0].url : file.url}]);
|
url: file.requested_formats ? file.requested_formats[0].url : file.url}]);
|
||||||
@@ -177,7 +181,6 @@ export class RecentVideosComponent implements OnInit {
|
|||||||
const type = file.isAudio ? 'audio' : 'video';
|
const type = file.isAudio ? 'audio' : 'video';
|
||||||
const ext = type === 'audio' ? '.mp3' : '.mp4'
|
const ext = type === 'audio' ? '.mp3' : '.mp4'
|
||||||
const sub = this.postsService.getSubscriptionByID(file.sub_id);
|
const sub = this.postsService.getSubscriptionByID(file.sub_id);
|
||||||
console.log(sub.isPlaylist)
|
|
||||||
this.postsService.downloadFileFromServer(file.id, type, null, null, sub.name, sub.isPlaylist,
|
this.postsService.downloadFileFromServer(file.id, type, null, null, sub.name, sub.isPlaylist,
|
||||||
this.postsService.user ? this.postsService.user.uid : null, null).subscribe(res => {
|
this.postsService.user ? this.postsService.user.uid : null, null).subscribe(res => {
|
||||||
const blob: Blob = res;
|
const blob: Blob = res;
|
||||||
|
|||||||
@@ -29,7 +29,7 @@
|
|||||||
<div style="padding:5px">
|
<div style="padding:5px">
|
||||||
<div *ngIf="!loading && file_obj.thumbnailURL" class="img-div">
|
<div *ngIf="!loading && file_obj.thumbnailURL" class="img-div">
|
||||||
<div style="position: relative">
|
<div style="position: relative">
|
||||||
<img [ngClass]="{'image-small': card_size === 'small', 'image': card_size === 'medium', 'image-large': card_size === 'large'}" [src]="file_obj.thumbnailURL" alt="Thumbnail">
|
<img [ngClass]="{'image-small': card_size === 'small', 'image': card_size === 'medium', 'image-large': card_size === 'large'}" [src]="file_obj.thumbnailBlob ? thumbnailBlobURL : file_obj.thumbnailURL" alt="Thumbnail">
|
||||||
<div class="duration-time">
|
<div class="duration-time">
|
||||||
{{file_length}}
|
{{file_length}}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -103,6 +103,7 @@
|
|||||||
background: rgba(255,255,255,0.6);
|
background: rgba(255,255,255,0.6);
|
||||||
padding-left: 5px;
|
padding-left: 5px;
|
||||||
padding-right: 5px;
|
padding-right: 5px;
|
||||||
|
color: black;
|
||||||
}
|
}
|
||||||
|
|
||||||
.download-time {
|
.download-time {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
|
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
|
||||||
import { MatDialog } from '@angular/material/dialog';
|
import { MatDialog } from '@angular/material/dialog';
|
||||||
import { VideoInfoDialogComponent } from 'app/dialogs/video-info-dialog/video-info-dialog.component';
|
import { VideoInfoDialogComponent } from 'app/dialogs/video-info-dialog/video-info-dialog.component';
|
||||||
|
import { DomSanitizer } from '@angular/platform-browser';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-unified-file-card',
|
selector: 'app-unified-file-card',
|
||||||
@@ -16,6 +17,10 @@ export class UnifiedFileCardComponent implements OnInit {
|
|||||||
type = null;
|
type = null;
|
||||||
elevated = false;
|
elevated = false;
|
||||||
|
|
||||||
|
// optional vars
|
||||||
|
thumbnailBlobURL = null;
|
||||||
|
|
||||||
|
// input/output
|
||||||
@Input() loading = true;
|
@Input() loading = true;
|
||||||
@Input() theme = null;
|
@Input() theme = null;
|
||||||
@Input() file_obj = null;
|
@Input() file_obj = null;
|
||||||
@@ -35,12 +40,19 @@ export class UnifiedFileCardComponent implements OnInit {
|
|||||||
big: 250x200
|
big: 250x200
|
||||||
*/
|
*/
|
||||||
|
|
||||||
constructor(private dialog: MatDialog) { }
|
constructor(private dialog: MatDialog, private sanitizer: DomSanitizer) { }
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
if (!this.loading) {
|
if (!this.loading) {
|
||||||
this.file_length = fancyTimeFormat(this.file_obj.duration);
|
this.file_length = fancyTimeFormat(this.file_obj.duration);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.file_obj && this.file_obj.thumbnailBlob) {
|
||||||
|
const mime = getMimeByFilename(this.file_obj.thumbnailPath);
|
||||||
|
const blob = new Blob([new Uint8Array(this.file_obj.thumbnailBlob.data)], {type: mime});
|
||||||
|
const bloburl = URL.createObjectURL(blob);
|
||||||
|
this.thumbnailBlobURL = this.sanitizer.bypassSecurityTrustUrl(bloburl);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
emitDeleteFile(blacklistMode = false) {
|
emitDeleteFile(blacklistMode = false) {
|
||||||
@@ -97,3 +109,16 @@ function fancyTimeFormat(time) {
|
|||||||
ret += '' + secs;
|
ret += '' + secs;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getMimeByFilename(name) {
|
||||||
|
switch (name.substring(name.length-4, name.length)) {
|
||||||
|
case '.jpg':
|
||||||
|
return 'image/jpeg';
|
||||||
|
case '.png':
|
||||||
|
return 'image/png';
|
||||||
|
case 'webp':
|
||||||
|
return 'image/webp';
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -164,7 +164,7 @@
|
|||||||
<br/>
|
<br/>
|
||||||
<div class="centered big" id="bar_div" *ngIf="current_download && current_download.downloading; else nofile">
|
<div class="centered big" id="bar_div" *ngIf="current_download && current_download.downloading; else nofile">
|
||||||
<div class="margined">
|
<div class="margined">
|
||||||
<div [ngClass]="(+percentDownloaded > 99)?'make-room-for-spinner':'equal-sizes'" style="display: inline-block; width: 100%; padding-left: 20px" *ngIf="current_download.percent_complete && current_download.percent_complete > 15;else indeterminateprogress">
|
<div [ngClass]="(+percentDownloaded > 99)?'make-room-for-spinner':'equal-sizes'" style="display: inline-block; width: 100%; padding-left: 20px" *ngIf="current_download.percent_complete && current_download.percent_complete > 1;else indeterminateprogress">
|
||||||
<mat-progress-bar style="border-radius: 5px;" mode="determinate" value="{{percentDownloaded}}"></mat-progress-bar>
|
<mat-progress-bar style="border-radius: 5px;" mode="determinate" value="{{percentDownloaded}}"></mat-progress-bar>
|
||||||
<br/>
|
<br/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1033,8 +1033,8 @@ export class MainComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
} else if (format_obj.type === 'video') {
|
} else if (format_obj.type === 'video') {
|
||||||
// check if video format is mp4
|
// check if video format is mp4
|
||||||
const key = format.height.toString();
|
const key = format.format_note.replace('p', '');
|
||||||
if (format.ext === 'mp4') {
|
if (format.ext === 'mp4' || format.ext === 'mkv' || format.ext === 'webm') {
|
||||||
format_obj['height'] = format.height;
|
format_obj['height'] = format.height;
|
||||||
format_obj['acodec'] = format.acodec;
|
format_obj['acodec'] = format.acodec;
|
||||||
format_obj['format_id'] = format.format_id;
|
format_obj['format_id'] = format.format_id;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Component, OnInit, HostListener, EventEmitter, OnDestroy } from '@angular/core';
|
import { Component, OnInit, HostListener, EventEmitter, OnDestroy, AfterViewInit } from '@angular/core';
|
||||||
import { VgAPI } from 'ngx-videogular';
|
import { VgAPI } from 'ngx-videogular';
|
||||||
import { PostsService } from 'app/posts.services';
|
import { PostsService } from 'app/posts.services';
|
||||||
import { ActivatedRoute, Router } from '@angular/router';
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
@@ -20,7 +20,7 @@ export interface IMedia {
|
|||||||
templateUrl: './player.component.html',
|
templateUrl: './player.component.html',
|
||||||
styleUrls: ['./player.component.css']
|
styleUrls: ['./player.component.css']
|
||||||
})
|
})
|
||||||
export class PlayerComponent implements OnInit, OnDestroy {
|
export class PlayerComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||||
|
|
||||||
playlist: Array<IMedia> = [];
|
playlist: Array<IMedia> = [];
|
||||||
original_playlist: string = null;
|
original_playlist: string = null;
|
||||||
@@ -95,6 +95,10 @@ export class PlayerComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ngAfterViewInit() {
|
||||||
|
this.postsService.sidenav.close();
|
||||||
|
}
|
||||||
|
|
||||||
ngOnDestroy() {
|
ngOnDestroy() {
|
||||||
// prevents volume save feature from running in the background
|
// prevents volume save feature from running in the background
|
||||||
clearInterval(this.save_volume_timer);
|
clearInterval(this.save_volume_timer);
|
||||||
|
|||||||
@@ -15,16 +15,14 @@ import * as Fingerprint2 from 'fingerprintjs2';
|
|||||||
@Injectable()
|
@Injectable()
|
||||||
export class PostsService implements CanActivate {
|
export class PostsService implements CanActivate {
|
||||||
path = '';
|
path = '';
|
||||||
audioFolder = '';
|
|
||||||
videoFolder = '';
|
// local settings
|
||||||
startPath = null; // 'http://localhost:17442/';
|
|
||||||
startPathSSL = null; // 'https://localhost:17442/'
|
|
||||||
handShakeComplete = false;
|
|
||||||
THEMES_CONFIG = THEMES_CONFIG;
|
THEMES_CONFIG = THEMES_CONFIG;
|
||||||
theme;
|
theme;
|
||||||
card_size = 'medium';
|
card_size = 'medium';
|
||||||
sidepanel_mode = 'over';
|
sidepanel_mode = 'over';
|
||||||
settings_changed = new BehaviorSubject<boolean>(false);
|
|
||||||
|
// auth
|
||||||
auth_token = '4241b401-7236-493e-92b5-b72696b9d853';
|
auth_token = '4241b401-7236-493e-92b5-b72696b9d853';
|
||||||
session_id = null;
|
session_id = null;
|
||||||
httpOptions = null;
|
httpOptions = null;
|
||||||
@@ -41,20 +39,24 @@ export class PostsService implements CanActivate {
|
|||||||
|
|
||||||
available_permissions = null;
|
available_permissions = null;
|
||||||
|
|
||||||
|
// behavior subjects
|
||||||
reload_config = new BehaviorSubject<boolean>(false);
|
reload_config = new BehaviorSubject<boolean>(false);
|
||||||
config_reloaded = new BehaviorSubject<boolean>(false);
|
config_reloaded = new BehaviorSubject<boolean>(false);
|
||||||
service_initialized = new BehaviorSubject<boolean>(false);
|
service_initialized = new BehaviorSubject<boolean>(false);
|
||||||
initialized = false;
|
settings_changed = new BehaviorSubject<boolean>(false);
|
||||||
|
|
||||||
open_create_default_admin_dialog = new BehaviorSubject<boolean>(false);
|
open_create_default_admin_dialog = new BehaviorSubject<boolean>(false);
|
||||||
|
|
||||||
|
// app status
|
||||||
|
initialized = false;
|
||||||
|
|
||||||
|
// global vars
|
||||||
config = null;
|
config = null;
|
||||||
subscriptions = null;
|
subscriptions = null;
|
||||||
|
sidenav = null;
|
||||||
|
|
||||||
constructor(private http: HttpClient, private router: Router, @Inject(DOCUMENT) private document: Document,
|
constructor(private http: HttpClient, private router: Router, @Inject(DOCUMENT) private document: Document,
|
||||||
public snackBar: MatSnackBar) {
|
public snackBar: MatSnackBar) {
|
||||||
console.log('PostsService Initialized...');
|
console.log('PostsService Initialized...');
|
||||||
// this.startPath = window.location.href + '/api/';
|
|
||||||
// this.startPathSSL = window.location.href + '/api/';
|
|
||||||
this.path = this.document.location.origin + '/api/';
|
this.path = this.document.location.origin + '/api/';
|
||||||
|
|
||||||
if (isDevMode()) {
|
if (isDevMode()) {
|
||||||
@@ -152,14 +154,6 @@ export class PostsService implements CanActivate {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getVideoFolder() {
|
|
||||||
return this.http.get(this.startPath + 'videofolder');
|
|
||||||
}
|
|
||||||
|
|
||||||
getAudioFolder() {
|
|
||||||
return this.http.get(this.startPath + 'audiofolder');
|
|
||||||
}
|
|
||||||
|
|
||||||
// tslint:disable-next-line: max-line-length
|
// tslint:disable-next-line: max-line-length
|
||||||
makeMP3(url: string, selectedQuality: string, customQualityConfiguration: string, customArgs: string = null, customOutput: string = null, youtubeUsername: string = null, youtubePassword: string = null, ui_uid = null) {
|
makeMP3(url: string, selectedQuality: string, customQualityConfiguration: string, customArgs: string = null, customOutput: string = null, youtubeUsername: string = null, youtubePassword: string = null, ui_uid = null) {
|
||||||
return this.http.post(this.path + 'tomp3', {url: url,
|
return this.http.post(this.path + 'tomp3', {url: url,
|
||||||
@@ -384,7 +378,7 @@ export class PostsService implements CanActivate {
|
|||||||
|
|
||||||
// user methods
|
// user methods
|
||||||
login(username, password) {
|
login(username, password) {
|
||||||
const call = this.http.post(this.path + 'auth/login', {userid: username, password: password}, this.httpOptions);
|
const call = this.http.post(this.path + 'auth/login', {username: username, password: password}, this.httpOptions);
|
||||||
return call;
|
return call;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -398,6 +392,8 @@ export class PostsService implements CanActivate {
|
|||||||
}, err => {
|
}, err => {
|
||||||
if (err.status === 401) {
|
if (err.status === 401) {
|
||||||
this.sendToLogin();
|
this.sendToLogin();
|
||||||
|
this.token = null;
|
||||||
|
this.resetHttpParams();
|
||||||
}
|
}
|
||||||
console.log(err);
|
console.log(err);
|
||||||
});
|
});
|
||||||
@@ -414,14 +410,7 @@ export class PostsService implements CanActivate {
|
|||||||
this.router.navigate(['/login']);
|
this.router.navigate(['/login']);
|
||||||
}
|
}
|
||||||
|
|
||||||
// resets http params
|
this.resetHttpParams();
|
||||||
this.http_params = `apiKey=${this.auth_token}&sessionID=${this.session_id}`
|
|
||||||
|
|
||||||
this.httpOptions = {
|
|
||||||
params: new HttpParams({
|
|
||||||
fromString: this.http_params
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// user methods
|
// user methods
|
||||||
@@ -446,12 +435,29 @@ export class PostsService implements CanActivate {
|
|||||||
this.openSnackBar('You must log in to access this page!');
|
this.openSnackBar('You must log in to access this page!');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resetHttpParams() {
|
||||||
|
// resets http params
|
||||||
|
this.http_params = `apiKey=${this.auth_token}&sessionID=${this.session_id}`
|
||||||
|
|
||||||
|
this.httpOptions = {
|
||||||
|
params: new HttpParams({
|
||||||
|
fromString: this.http_params
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
setInitialized() {
|
setInitialized() {
|
||||||
this.service_initialized.next(true);
|
this.service_initialized.next(true);
|
||||||
this.initialized = true;
|
this.initialized = true;
|
||||||
this.config_reloaded.next(true);
|
this.config_reloaded.next(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
reloadSubscriptions() {
|
||||||
|
this.getAllSubscriptions().subscribe(res => {
|
||||||
|
this.subscriptions = res['subscriptions'];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
adminExists() {
|
adminExists() {
|
||||||
return this.http.post(this.path + 'auth/adminExists', {}, this.httpOptions);
|
return this.http.post(this.path + 'auth/adminExists', {}, this.httpOptions);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -128,7 +128,11 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-12 mt-2">
|
<div class="col-12 mt-2">
|
||||||
<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>
|
<mat-checkbox color="accent" [(ngModel)]="new_config['Downloader']['include_thumbnail']"><ng-container i18n="Include thumbnail setting">Include thumbnail</ng-container></mat-checkbox>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-12 mt-2">
|
||||||
|
<mat-checkbox color="accent" [(ngModel)]="new_config['Downloader']['include_metadata']"><ng-container i18n="Include metadata setting">Include metadata</ng-container></mat-checkbox>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-12 mt-2">
|
<div class="col-12 mt-2">
|
||||||
@@ -292,8 +296,50 @@
|
|||||||
</ng-template>
|
</ng-template>
|
||||||
</mat-tab>
|
</mat-tab>
|
||||||
<mat-tab *ngIf="postsService.config && postsService.config.Advanced.multi_user_mode" label="Users" i18n-label="Users settings label">
|
<mat-tab *ngIf="postsService.config && postsService.config.Advanced.multi_user_mode" label="Users" i18n-label="Users settings label">
|
||||||
<div style="margin-left: 48px; margin-top: 24px;">
|
|
||||||
<mat-checkbox color="accent" [(ngModel)]="new_config['Users']['allow_registration']"><ng-container i18n="Allow registration setting">Allow user registration</ng-container></mat-checkbox>
|
<div style="margin-left: 48px; margin-top: 24px; margin-bottom: -25px;">
|
||||||
|
<div>
|
||||||
|
<mat-checkbox color="accent" [(ngModel)]="new_config['Users']['allow_registration']"><ng-container i18n="Allow registration setting">Allow user registration</ng-container></mat-checkbox>
|
||||||
|
</div>
|
||||||
|
<mat-divider></mat-divider>
|
||||||
|
<mat-form-field style="margin-top: 15px;">
|
||||||
|
<mat-select [(ngModel)]="new_config['Users']['auth_method']" placeholder="Auth method" i18n-placeholder="Auth method select">
|
||||||
|
<mat-option value="internal">
|
||||||
|
<ng-container i18n="Internal auth method">Internal</ng-container>
|
||||||
|
</mat-option>
|
||||||
|
<mat-option value="ldap">
|
||||||
|
<ng-container i18n="LDAP auth method">LDAP</ng-container>
|
||||||
|
</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
<div *ngIf="new_config['Users']['auth_method'] === 'ldap'">
|
||||||
|
<div>
|
||||||
|
<mat-form-field>
|
||||||
|
<input matInput i18n-placeholder="LDAP URL" placeholder="LDAP URL" [(ngModel)]="new_config['Users']['ldap_config']['url']">
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<mat-form-field>
|
||||||
|
<input matInput i18n-placeholder="Bind DN" placeholder="Bind DN" [(ngModel)]="new_config['Users']['ldap_config']['bindDN']">
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<mat-form-field>
|
||||||
|
<input matInput i18n-placeholder="Bind Credentials" placeholder="Bind Credentials" [(ngModel)]="new_config['Users']['ldap_config']['bindCredentials']">
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<mat-form-field>
|
||||||
|
<input matInput i18n-placeholder="Search Base" placeholder="Search Base" [(ngModel)]="new_config['Users']['ldap_config']['searchBase']">
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<mat-form-field>
|
||||||
|
<input matInput i18n-placeholder="Search Filter" placeholder="Search Filter" [(ngModel)]="new_config['Users']['ldap_config']['searchFilter']">
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<mat-divider></mat-divider>
|
||||||
</div>
|
</div>
|
||||||
<app-modify-users></app-modify-users>
|
<app-modify-users></app-modify-users>
|
||||||
</mat-tab>
|
</mat-tab>
|
||||||
|
|||||||
@@ -80,6 +80,7 @@ export class SubscriptionsComponent implements OnInit {
|
|||||||
} else {
|
} else {
|
||||||
this.channel_subscriptions.push(result);
|
this.channel_subscriptions.push(result);
|
||||||
}
|
}
|
||||||
|
this.postsService.reloadSubscriptions();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -96,6 +97,7 @@ export class SubscriptionsComponent implements OnInit {
|
|||||||
if (success) {
|
if (success) {
|
||||||
this.openSnackBar(`${sub.name} successfully deleted!`)
|
this.openSnackBar(`${sub.name} successfully deleted!`)
|
||||||
this.getSubscriptions();
|
this.getSubscriptions();
|
||||||
|
this.postsService.reloadSubscriptions();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user