mirror of
https://github.com/Tzahi12345/YoutubeDL-Material.git
synced 2026-03-16 01:30:56 +03:00
Compare commits
34 Commits
dockertest
...
experiment
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3ea47af7a1 | ||
|
|
cf01a0d8ab | ||
|
|
d49a67dfd0 | ||
|
|
96c52f2d5b | ||
|
|
61c03b6681 | ||
|
|
7c349163b6 | ||
|
|
a0acdd1d86 | ||
|
|
25bfb9e518 | ||
|
|
c9b615c659 | ||
|
|
c4f21dc1cc | ||
|
|
e678f4b476 | ||
|
|
356f31343e | ||
|
|
38c46b5be5 | ||
|
|
12dcdfcb3c | ||
|
|
47c19c0cdc | ||
|
|
d0eff42f2a | ||
|
|
4472aae3e9 | ||
|
|
c55d3de9a0 | ||
|
|
1cdc1640ac | ||
|
|
fcaf8b5a62 | ||
|
|
8c7b2dfc79 | ||
|
|
59ad74ed79 | ||
|
|
690871f6b2 | ||
|
|
c6c80579df | ||
|
|
0ab6535fec | ||
|
|
d7aa39599d | ||
|
|
68037613d8 | ||
|
|
3df384de22 | ||
|
|
f0c4ed4590 | ||
|
|
fd35153721 | ||
|
|
8384b73c4c | ||
|
|
cc189a3abd | ||
|
|
4ebb2d4297 | ||
|
|
d371ccf094 |
@@ -2049,6 +2049,52 @@ app.post('/api/getFile', optionalJwt, function (req, res) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
app.post('/api/getAllFiles', optionalJwt, function (req, res) {
|
||||||
|
// these are returned
|
||||||
|
let files = [];
|
||||||
|
let playlists = [];
|
||||||
|
let subscription_files = [];
|
||||||
|
|
||||||
|
let videos = null;
|
||||||
|
let audios = null;
|
||||||
|
let audio_playlists = null;
|
||||||
|
let video_playlists = null;
|
||||||
|
let subscriptions = subscriptions_api.getAllSubscriptions(req.isAuthenticated() ? req.user.uid : null);
|
||||||
|
|
||||||
|
// get basic info depending on multi-user mode being enabled
|
||||||
|
if (req.isAuthenticated()) {
|
||||||
|
videos = auth_api.getUserVideos(req.user.uid, 'video');
|
||||||
|
audios = auth_api.getUserVideos(req.user.uid, 'audio');
|
||||||
|
audio_playlists = auth_api.getUserPlaylists(req.user.uid, 'audio');
|
||||||
|
video_playlists = auth_api.getUserPlaylists(req.user.uid, 'video');
|
||||||
|
} else {
|
||||||
|
videos = db.get('files.audio').value();
|
||||||
|
audios = db.get('files.video').value();
|
||||||
|
audio_playlists = db.get('playlists.audio').value();
|
||||||
|
video_playlists = db.get('playlists.video').value();
|
||||||
|
}
|
||||||
|
|
||||||
|
files = videos.concat(audios);
|
||||||
|
playlists = video_playlists.concat(audio_playlists);
|
||||||
|
|
||||||
|
// loop through subscriptions and add videos
|
||||||
|
for (let i = 0; i < subscriptions.length; i++) {
|
||||||
|
sub = subscriptions[i];
|
||||||
|
if (!sub.videos) continue;
|
||||||
|
// add sub id for UI
|
||||||
|
for (let j = 0; j < sub.videos.length; j++) {
|
||||||
|
sub.videos[j].sub_id = sub.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
files = files.concat(sub.videos);
|
||||||
|
}
|
||||||
|
|
||||||
|
res.send({
|
||||||
|
files: files,
|
||||||
|
playlists: playlists
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// video sharing
|
// video sharing
|
||||||
app.post('/api/enableSharing', optionalJwt, function(req, res) {
|
app.post('/api/enableSharing', optionalJwt, function(req, res) {
|
||||||
var type = req.body.type;
|
var type = req.body.type;
|
||||||
@@ -2336,13 +2382,16 @@ app.post('/api/createPlaylist', optionalJwt, async (req, res) => {
|
|||||||
let fileNames = req.body.fileNames;
|
let fileNames = req.body.fileNames;
|
||||||
let type = req.body.type;
|
let type = req.body.type;
|
||||||
let thumbnailURL = req.body.thumbnailURL;
|
let thumbnailURL = req.body.thumbnailURL;
|
||||||
|
let duration = req.body.duration;
|
||||||
|
|
||||||
let new_playlist = {
|
let new_playlist = {
|
||||||
'name': playlistName,
|
name: playlistName,
|
||||||
fileNames: fileNames,
|
fileNames: fileNames,
|
||||||
id: shortid.generate(),
|
id: shortid.generate(),
|
||||||
thumbnailURL: thumbnailURL,
|
thumbnailURL: thumbnailURL,
|
||||||
type: type
|
type: type,
|
||||||
|
registered: Date.now(),
|
||||||
|
duration: duration
|
||||||
};
|
};
|
||||||
|
|
||||||
if (req.isAuthenticated()) {
|
if (req.isAuthenticated()) {
|
||||||
@@ -2543,7 +2592,7 @@ app.post('/api/downloadFile', optionalJwt, async (req, res) => {
|
|||||||
else
|
else
|
||||||
basePath = config_api.getConfigItem('ytdl_subscriptions_base_path');
|
basePath = config_api.getConfigItem('ytdl_subscriptions_base_path');
|
||||||
|
|
||||||
file = path.join(__dirname, basePath, (subscriptionPlaylist === 'true' ? 'playlists' : 'channels'), subscriptionName, fileNames + ext);
|
file = path.join(__dirname, basePath, (subscriptionPlaylist === true || subscriptionPlaylist === 'true' ? 'playlists' : 'channels'), subscriptionName, fileNames + ext);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (let i = 0; i < fileNames.length; i++) {
|
for (let i = 0; i < fileNames.length; i++) {
|
||||||
|
|||||||
@@ -37,8 +37,7 @@
|
|||||||
"Subscriptions": {
|
"Subscriptions": {
|
||||||
"allow_subscriptions": true,
|
"allow_subscriptions": true,
|
||||||
"subscriptions_base_path": "subscriptions/",
|
"subscriptions_base_path": "subscriptions/",
|
||||||
"subscriptions_check_interval": "300",
|
"subscriptions_check_interval": "300"
|
||||||
"subscriptions_use_youtubedl_archive": true
|
|
||||||
},
|
},
|
||||||
"Users": {
|
"Users": {
|
||||||
"base_path": "users/",
|
"base_path": "users/",
|
||||||
@@ -50,6 +49,7 @@
|
|||||||
"multi_user_mode": false,
|
"multi_user_mode": false,
|
||||||
"allow_advanced_download": false,
|
"allow_advanced_download": false,
|
||||||
"use_cookies": false,
|
"use_cookies": false,
|
||||||
|
"jwt_expiration": 86400,
|
||||||
"logger_level": "info"
|
"logger_level": "info"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,8 +37,7 @@
|
|||||||
"Subscriptions": {
|
"Subscriptions": {
|
||||||
"allow_subscriptions": true,
|
"allow_subscriptions": true,
|
||||||
"subscriptions_base_path": "subscriptions/",
|
"subscriptions_base_path": "subscriptions/",
|
||||||
"subscriptions_check_interval": "300",
|
"subscriptions_check_interval": "300"
|
||||||
"subscriptions_use_youtubedl_archive": true
|
|
||||||
},
|
},
|
||||||
"Users": {
|
"Users": {
|
||||||
"base_path": "users/",
|
"base_path": "users/",
|
||||||
@@ -50,6 +49,7 @@
|
|||||||
"multi_user_mode": false,
|
"multi_user_mode": false,
|
||||||
"allow_advanced_download": false,
|
"allow_advanced_download": false,
|
||||||
"use_cookies": false,
|
"use_cookies": false,
|
||||||
|
"jwt_expiration": 86400,
|
||||||
"logger_level": "info"
|
"logger_level": "info"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ exports.initialize = function(input_users_db, input_logger) {
|
|||||||
************************/
|
************************/
|
||||||
saltRounds = 10;
|
saltRounds = 10;
|
||||||
|
|
||||||
JWT_EXPIRATION = (60 * 60); // one hour
|
JWT_EXPIRATION = config_api.getConfigItem('ytdl_jwt_expiration');
|
||||||
|
|
||||||
SERVER_SECRET = null;
|
SERVER_SECRET = null;
|
||||||
if (users_db.get('jwt_secret').value()) {
|
if (users_db.get('jwt_secret').value()) {
|
||||||
@@ -307,21 +307,27 @@ exports.getUserVideos = function(user_uid, type) {
|
|||||||
return user['files'][type];
|
return user['files'][type];
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.getUserVideo = function(user_uid, file_uid, type, requireSharing = false) {
|
exports.getUserVideo = function(user_uid, file_uid, type, requireSharing = false, reqBody = null) {
|
||||||
if (!type) {
|
// check if it's a subscription first
|
||||||
file = users_db.get('users').find({uid: user_uid}).get(`files.audio`).find({uid: file_uid}).value();
|
let file = null;
|
||||||
if (!file) {
|
if (reqBody && reqBody.subscriptionName) {
|
||||||
file = users_db.get('users').find({uid: user_uid}).get(`files.video`).find({uid: file_uid}).value();
|
file = users_db.get('users').find({uid: user_uid}).get('subscriptions').find({name: reqBody.subscriptionName}).get('videos').find({id: reqBody.fileNames}).value();
|
||||||
if (file) type = 'video';
|
} else {
|
||||||
} else {
|
if (!type) {
|
||||||
type = 'audio';
|
file = users_db.get('users').find({uid: user_uid}).get(`files.audio`).find({uid: file_uid}).value();
|
||||||
|
if (!file) {
|
||||||
|
file = users_db.get('users').find({uid: user_uid}).get(`files.video`).find({uid: file_uid}).value();
|
||||||
|
if (file) type = 'video';
|
||||||
|
} else {
|
||||||
|
type = 'audio';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!file && type) file = users_db.get('users').find({uid: user_uid}).get(`files.${type}`).find({uid: file_uid}).value();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!file && type) file = users_db.get('users').find({uid: user_uid}).get(`files.${type}`).find({uid: file_uid}).value();
|
|
||||||
|
|
||||||
// prevent unauthorized users from accessing the file info
|
// prevent unauthorized users from accessing the file info
|
||||||
if (requireSharing && !file['sharingEnabled']) file = null;
|
if (file && requireSharing && !file['sharingEnabled']) file = null;
|
||||||
|
|
||||||
return file;
|
return file;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -214,8 +214,7 @@ DEFAULT_CONFIG = {
|
|||||||
"Subscriptions": {
|
"Subscriptions": {
|
||||||
"allow_subscriptions": true,
|
"allow_subscriptions": true,
|
||||||
"subscriptions_base_path": "subscriptions/",
|
"subscriptions_base_path": "subscriptions/",
|
||||||
"subscriptions_check_interval": "300",
|
"subscriptions_check_interval": "300"
|
||||||
"subscriptions_use_youtubedl_archive": true
|
|
||||||
},
|
},
|
||||||
"Users": {
|
"Users": {
|
||||||
"base_path": "users/",
|
"base_path": "users/",
|
||||||
@@ -227,6 +226,7 @@ DEFAULT_CONFIG = {
|
|||||||
"multi_user_mode": false,
|
"multi_user_mode": false,
|
||||||
"allow_advanced_download": false,
|
"allow_advanced_download": false,
|
||||||
"use_cookies": false,
|
"use_cookies": false,
|
||||||
|
"jwt_expiration": 86400,
|
||||||
"logger_level": "info"
|
"logger_level": "info"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -116,10 +116,6 @@ let CONFIG_ITEMS = {
|
|||||||
'key': 'ytdl_subscriptions_check_interval',
|
'key': 'ytdl_subscriptions_check_interval',
|
||||||
'path': 'YoutubeDLMaterial.Subscriptions.subscriptions_check_interval'
|
'path': 'YoutubeDLMaterial.Subscriptions.subscriptions_check_interval'
|
||||||
},
|
},
|
||||||
'ytdl_subscriptions_use_youtubedl_archive': {
|
|
||||||
'key': 'ytdl_use_youtubedl_archive',
|
|
||||||
'path': 'YoutubeDLMaterial.Subscriptions.subscriptions_use_youtubedl_archive'
|
|
||||||
},
|
|
||||||
|
|
||||||
// Users
|
// Users
|
||||||
'ytdl_users_base_path': {
|
'ytdl_users_base_path': {
|
||||||
@@ -152,6 +148,10 @@ let CONFIG_ITEMS = {
|
|||||||
'key': 'ytdl_use_cookies',
|
'key': 'ytdl_use_cookies',
|
||||||
'path': 'YoutubeDLMaterial.Advanced.use_cookies'
|
'path': 'YoutubeDLMaterial.Advanced.use_cookies'
|
||||||
},
|
},
|
||||||
|
'ytdl_jwt_expiration': {
|
||||||
|
'key': 'ytdl_jwt_expiration',
|
||||||
|
'path': 'YoutubeDLMaterial.Advanced.jwt_expiration'
|
||||||
|
},
|
||||||
'ytdl_logger_level': {
|
'ytdl_logger_level': {
|
||||||
'key': 'ytdl_logger_level',
|
'key': 'ytdl_logger_level',
|
||||||
'path': 'YoutubeDLMaterial.Advanced.logger_level'
|
'path': 'YoutubeDLMaterial.Advanced.logger_level'
|
||||||
|
|||||||
@@ -61,6 +61,10 @@ function registerFileDB(file_path, type, multiUserMode = null, sub = null) {
|
|||||||
} else {
|
} else {
|
||||||
sub_db = db.get('subscriptions').find({id: sub.id});
|
sub_db = db.get('subscriptions').find({id: sub.id});
|
||||||
}
|
}
|
||||||
|
if (sub_db.get('videos').find({id: file_object.id}).value()) {
|
||||||
|
logger.verbose(`Subscription video ${file_object.id} already exists, skipping DB registration.`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
sub_db.get('videos').push(file_object).write();
|
sub_db.get('videos').push(file_object).write();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
1
backend/public/1-es5.058e3b6a3de95b8ddbf8.js
Normal file
1
backend/public/1-es5.058e3b6a3de95b8ddbf8.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
@@ -190,6 +190,32 @@ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|||||||
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
|
||||||
|
is-retina
|
||||||
|
MIT
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2014 Kyle Mathews
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
ng-lazyload-image
|
ng-lazyload-image
|
||||||
MIT
|
MIT
|
||||||
The MIT License (MIT)
|
The MIT License (MIT)
|
||||||
@@ -215,6 +241,30 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|||||||
SOFTWARE.
|
SOFTWARE.
|
||||||
|
|
||||||
|
|
||||||
|
ngx-avatar
|
||||||
|
MIT
|
||||||
|
The MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2020 Haithem Mosbahi, ngx-avatar
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
|
||||||
ngx-file-drop
|
ngx-file-drop
|
||||||
MIT
|
MIT
|
||||||
|
|
||||||
@@ -658,6 +708,9 @@ Apache-2.0
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
ts-md5
|
||||||
|
MIT
|
||||||
|
|
||||||
tslib
|
tslib
|
||||||
Apache-2.0
|
Apache-2.0
|
||||||
Apache License
|
Apache License
|
||||||
|
|||||||
@@ -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.42092efdfb84b81949da.js" type="module"></script><script src="runtime-es5.42092efdfb84b81949da.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>
|
<script src="runtime-es2015.49084170e55eb5da181d.js" type="module"></script><script src="runtime-es5.49084170e55eb5da181d.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.63a61681b752f95b7352.js" type="module"></script><script src="main-es5.63a61681b752f95b7352.js" nomodule defer></script></body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
1
backend/public/main-es2015.63a61681b752f95b7352.js
Normal file
1
backend/public/main-es2015.63a61681b752f95b7352.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.63a61681b752f95b7352.js
Normal file
1
backend/public/main-es5.63a61681b752f95b7352.js
Normal file
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
|||||||
!function(e){function r(r){for(var n,a,i=r[0],c=r[1],l=r[2],p=0,s=[];p<i.length;p++)a=i[p],Object.prototype.hasOwnProperty.call(o,a)&&o[a]&&s.push(o[a][0]),o[a]=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,i=1;i<t.length;i++)0!==o[t[i]]&&(n=!1);n&&(u.splice(r--,1),e=a(a.s=t[0]))}return e}var n={},o={0:0},u=[];function a(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,a),t.l=!0,t.exports}a.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,i=document.createElement("script");i.charset="utf-8",i.timeout=120,a.nc&&i.setAttribute("nonce",a.nc),i.src=function(e){return a.p+""+({}[e]||e)+"-es2015."+{1:"c401a556fe28cac6abab"}[e]+".js"}(e);var c=new Error;u=function(r){i.onerror=i.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:i})}),12e4);i.onerror=i.onload=u,document.head.appendChild(i)}return Promise.all(r)},a.m=e,a.c=n,a.d=function(e,r,t){a.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},a.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},a.t=function(e,r){if(1&r&&(e=a(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(a.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)a.d(t,n,(function(r){return e[r]}).bind(null,n));return t},a.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return a.d(r,"a",r),r},a.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},a.p="",a.oe=function(e){throw console.error(e),e};var i=window.webpackJsonp=window.webpackJsonp||[],c=i.push.bind(i);i.push=r,i=i.slice();for(var l=0;l<i.length;l++)r(i[l]);var f=c;t()}([]);
|
|
||||||
1
backend/public/runtime-es2015.49084170e55eb5da181d.js
Normal file
1
backend/public/runtime-es2015.49084170e55eb5da181d.js
Normal file
@@ -0,0 +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:"058e3b6a3de95b8ddbf8"}[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 +0,0 @@
|
|||||||
!function(e){function r(r){for(var n,a,i=r[0],c=r[1],l=r[2],p=0,s=[];p<i.length;p++)a=i[p],Object.prototype.hasOwnProperty.call(o,a)&&o[a]&&s.push(o[a][0]),o[a]=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,i=1;i<t.length;i++)0!==o[t[i]]&&(n=!1);n&&(u.splice(r--,1),e=a(a.s=t[0]))}return e}var n={},o={0:0},u=[];function a(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,a),t.l=!0,t.exports}a.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,i=document.createElement("script");i.charset="utf-8",i.timeout=120,a.nc&&i.setAttribute("nonce",a.nc),i.src=function(e){return a.p+""+({}[e]||e)+"-es5."+{1:"c401a556fe28cac6abab"}[e]+".js"}(e);var c=new Error;u=function(r){i.onerror=i.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:i})}),12e4);i.onerror=i.onload=u,document.head.appendChild(i)}return Promise.all(r)},a.m=e,a.c=n,a.d=function(e,r,t){a.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},a.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},a.t=function(e,r){if(1&r&&(e=a(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(a.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)a.d(t,n,(function(r){return e[r]}).bind(null,n));return t},a.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return a.d(r,"a",r),r},a.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},a.p="",a.oe=function(e){throw console.error(e),e};var i=window.webpackJsonp=window.webpackJsonp||[],c=i.push.bind(i);i.push=r,i=i.slice();for(var l=0;l<i.length;l++)r(i[l]);var f=c;t()}([]);
|
|
||||||
1
backend/public/runtime-es5.49084170e55eb5da181d.js
Normal file
1
backend/public/runtime-es5.49084170e55eb5da181d.js
Normal file
@@ -0,0 +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:"058e3b6a3de95b8ddbf8"}[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()}([]);
|
||||||
@@ -123,7 +123,7 @@ async function getSubscriptionInfo(sub, user_uid = null) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const useArchive = config_api.getConfigItem('ytdl_subscriptions_use_youtubedl_archive');
|
const useArchive = config_api.getConfigItem('ytdl_use_youtubedl_archive');
|
||||||
if (useArchive && !sub.archive) {
|
if (useArchive && !sub.archive) {
|
||||||
// must create the archive
|
// must create the archive
|
||||||
const archive_dir = path.join(__dirname, basePath, 'archives', sub.name);
|
const archive_dir = path.join(__dirname, basePath, 'archives', sub.name);
|
||||||
@@ -197,7 +197,7 @@ async function deleteSubscriptionFile(sub, file, deleteForever, file_uid = null,
|
|||||||
basePath = config_api.getConfigItem('ytdl_subscriptions_base_path');
|
basePath = config_api.getConfigItem('ytdl_subscriptions_base_path');
|
||||||
sub_db = db.get('subscriptions').find({id: sub.id});
|
sub_db = db.get('subscriptions').find({id: sub.id});
|
||||||
}
|
}
|
||||||
const useArchive = config_api.getConfigItem('ytdl_subscriptions_use_youtubedl_archive');
|
const useArchive = config_api.getConfigItem('ytdl_use_youtubedl_archive');
|
||||||
const appendedBasePath = getAppendedBasePath(sub, basePath);
|
const appendedBasePath = getAppendedBasePath(sub, basePath);
|
||||||
const name = file;
|
const name = file;
|
||||||
let retrievedID = null;
|
let retrievedID = null;
|
||||||
@@ -273,7 +273,7 @@ async function getVideosForSub(sub, user_uid = null) {
|
|||||||
else
|
else
|
||||||
basePath = config_api.getConfigItem('ytdl_subscriptions_base_path');
|
basePath = config_api.getConfigItem('ytdl_subscriptions_base_path');
|
||||||
|
|
||||||
const useArchive = config_api.getConfigItem('ytdl_subscriptions_use_youtubedl_archive');
|
const useArchive = config_api.getConfigItem('ytdl_use_youtubedl_archive');
|
||||||
|
|
||||||
let appendedBasePath = null
|
let appendedBasePath = null
|
||||||
appendedBasePath = getAppendedBasePath(sub, basePath);
|
appendedBasePath = getAppendedBasePath(sub, basePath);
|
||||||
@@ -411,7 +411,6 @@ function handleOutputJSON(sub, sub_db, output_json, multiUserMode = null, reset_
|
|||||||
// add to db
|
// add to db
|
||||||
sub_db.get('videos').push(output_json).write();
|
sub_db.get('videos').push(output_json).write();
|
||||||
} else {
|
} else {
|
||||||
// TODO: make multiUserMode obj
|
|
||||||
db_api.registerFileDB(path.basename(output_json['_filename']), sub.type, multiUserMode, sub);
|
db_api.registerFileDB(path.basename(output_json['_filename']), sub.type, multiUserMode, sub);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
19
package-lock.json
generated
19
package-lock.json
generated
@@ -6588,6 +6588,11 @@
|
|||||||
"integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==",
|
"integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"is-retina": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-retina/-/is-retina-1.0.3.tgz",
|
||||||
|
"integrity": "sha1-10AbKGvqKuN/Ykd1iN5QTQuGR+M="
|
||||||
|
},
|
||||||
"is-stream": {
|
"is-stream": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
|
||||||
@@ -9137,6 +9142,15 @@
|
|||||||
"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-avatar": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ngx-avatar/-/ngx-avatar-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-Uk40UXl26RvDy1ori9NDsGFB+f84AaxMnsIwZA6JPJK0pLcbo3F4vZTmzLZeOusOw1Qtgk5IzF630jo06keXwQ==",
|
||||||
|
"requires": {
|
||||||
|
"is-retina": "^1.0.3",
|
||||||
|
"ts-md5": "^1.2.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"ngx-file-drop": {
|
"ngx-file-drop": {
|
||||||
"version": "9.0.1",
|
"version": "9.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/ngx-file-drop/-/ngx-file-drop-9.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/ngx-file-drop/-/ngx-file-drop-9.0.1.tgz",
|
||||||
@@ -13457,6 +13471,11 @@
|
|||||||
"utf8-byte-length": "^1.0.1"
|
"utf8-byte-length": "^1.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"ts-md5": {
|
||||||
|
"version": "1.2.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/ts-md5/-/ts-md5-1.2.7.tgz",
|
||||||
|
"integrity": "sha512-emODogvKGWi1KO1l9c6YxLMBn6CEH3VrH5mVPIyOtxBG52BvV4jP3GWz6bOZCz61nLgBc3ffQYE4+EHfCD+V7w=="
|
||||||
|
},
|
||||||
"ts-node": {
|
"ts-node": {
|
||||||
"version": "3.0.6",
|
"version": "3.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-3.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-3.0.6.tgz",
|
||||||
|
|||||||
@@ -36,6 +36,7 @@
|
|||||||
"fingerprintjs2": "^2.1.0",
|
"fingerprintjs2": "^2.1.0",
|
||||||
"nan": "^2.14.1",
|
"nan": "^2.14.1",
|
||||||
"ng-lazyload-image": "^7.0.1",
|
"ng-lazyload-image": "^7.0.1",
|
||||||
|
"ngx-avatar": "^4.0.0",
|
||||||
"ngx-file-drop": "^9.0.1",
|
"ngx-file-drop": "^9.0.1",
|
||||||
"ngx-videogular": "^9.0.1",
|
"ngx-videogular": "^9.0.1",
|
||||||
"rxjs": "^6.5.3",
|
"rxjs": "^6.5.3",
|
||||||
|
|||||||
@@ -38,12 +38,16 @@
|
|||||||
</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 #sidenav>
|
<mat-sidenav [opened]="postsService.sidepanel_mode === 'side' && router.url === '/home'" [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)="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='/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)="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>
|
<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>
|
||||||
|
<ng-container *ngIf="postsService.config && allowSubscriptions && postsService.subscriptions && (!postsService.config.Advanced.multi_user_mode || (postsService.isLoggedIn && postsService.permissions.includes('subscriptions')))">
|
||||||
|
<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>
|
||||||
|
</ng-container>
|
||||||
</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">
|
||||||
|
|||||||
@@ -86,6 +86,13 @@ export class AppComponent implements OnInit {
|
|||||||
if (!localStorage.getItem('theme')) {
|
if (!localStorage.getItem('theme')) {
|
||||||
this.setTheme(themingExists ? this.defaultTheme : 'default');
|
this.setTheme(themingExists ? this.defaultTheme : 'default');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// gets the subscriptions
|
||||||
|
if (this.allowSubscriptions) {
|
||||||
|
this.postsService.getAllSubscriptions().subscribe(res => {
|
||||||
|
this.postsService.subscriptions = res['subscriptions'];
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// theme stuff
|
// theme stuff
|
||||||
@@ -162,6 +169,11 @@ onSetTheme(theme, old_theme) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
getSubscriptions() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
goBack() {
|
goBack() {
|
||||||
if (!this.navigator) {
|
if (!this.navigator) {
|
||||||
this.router.navigate(['/home']);
|
this.router.navigate(['/home']);
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ import { SubscriptionInfoDialogComponent } from './dialogs/subscription-info-dia
|
|||||||
import { SettingsComponent } from './settings/settings.component';
|
import { SettingsComponent } from './settings/settings.component';
|
||||||
import { MatChipsModule } from '@angular/material/chips';
|
import { MatChipsModule } from '@angular/material/chips';
|
||||||
import { NgxFileDropModule } from 'ngx-file-drop';
|
import { NgxFileDropModule } from 'ngx-file-drop';
|
||||||
|
import { AvatarModule } from 'ngx-avatar';
|
||||||
|
|
||||||
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';
|
||||||
@@ -73,7 +74,10 @@ import { CookiesUploaderDialogComponent } from './dialogs/cookies-uploader-dialo
|
|||||||
import { LogsViewerComponent } from './components/logs-viewer/logs-viewer.component';
|
import { LogsViewerComponent } from './components/logs-viewer/logs-viewer.component';
|
||||||
import { ModifyPlaylistComponent } from './dialogs/modify-playlist/modify-playlist.component';
|
import { ModifyPlaylistComponent } from './dialogs/modify-playlist/modify-playlist.component';
|
||||||
import { ConfirmDialogComponent } from './dialogs/confirm-dialog/confirm-dialog.component';
|
import { ConfirmDialogComponent } from './dialogs/confirm-dialog/confirm-dialog.component';
|
||||||
|
import { UnifiedFileCardComponent } from './components/unified-file-card/unified-file-card.component';
|
||||||
|
import { RecentVideosComponent } from './components/recent-videos/recent-videos.component';
|
||||||
import { EditSubscriptionDialogComponent } from './dialogs/edit-subscription-dialog/edit-subscription-dialog.component';
|
import { EditSubscriptionDialogComponent } from './dialogs/edit-subscription-dialog/edit-subscription-dialog.component';
|
||||||
|
import { CustomPlaylistsComponent } from './components/custom-playlists/custom-playlists.component';
|
||||||
|
|
||||||
registerLocaleData(es, 'es');
|
registerLocaleData(es, 'es');
|
||||||
|
|
||||||
@@ -115,7 +119,10 @@ export function isVisible({ event, element, scrollContainer, offset }: IsVisible
|
|||||||
LogsViewerComponent,
|
LogsViewerComponent,
|
||||||
ModifyPlaylistComponent,
|
ModifyPlaylistComponent,
|
||||||
ConfirmDialogComponent,
|
ConfirmDialogComponent,
|
||||||
EditSubscriptionDialogComponent
|
UnifiedFileCardComponent,
|
||||||
|
RecentVideosComponent,
|
||||||
|
EditSubscriptionDialogComponent,
|
||||||
|
CustomPlaylistsComponent
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
CommonModule,
|
CommonModule,
|
||||||
@@ -156,6 +163,7 @@ export function isVisible({ event, element, scrollContainer, offset }: IsVisible
|
|||||||
DragDropModule,
|
DragDropModule,
|
||||||
ClipboardModule,
|
ClipboardModule,
|
||||||
NgxFileDropModule,
|
NgxFileDropModule,
|
||||||
|
AvatarModule,
|
||||||
VgCoreModule,
|
VgCoreModule,
|
||||||
VgControlsModule,
|
VgControlsModule,
|
||||||
VgOverlayPlayModule,
|
VgOverlayPlayModule,
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
<div *ngIf="playlists && playlists.length > 0">
|
||||||
|
<div class="container">
|
||||||
|
<div class="row justify-content-center">
|
||||||
|
<div *ngFor="let playlist of playlists; let i = index" [ngClass]="[ postsService.card_size === 'small' ? 'col-2 mb-2 mt-2 file-col' : '', postsService.card_size === 'medium' ? 'col-6 col-lg-4 mb-2 mt-2 file-col' : '' ]">
|
||||||
|
<app-unified-file-card [index]="i" [card_size]="postsService.card_size" (goToFile)="goToPlaylist($event)" [file_obj]="playlist" [is_playlist]="true" (editPlaylist)="editPlaylistDialog($event)" (deleteFile)="deletePlaylist($event)"></app-unified-file-card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div *ngIf="playlists && playlists.length === 0" style="text-align: center;">
|
||||||
|
No playlists available. Create one from your downloading files by clicking the blue plus button.
|
||||||
|
</div>
|
||||||
|
<div class="add-playlist-button"><button (click)="openCreatePlaylistDialog()" mat-fab><mat-icon>add</mat-icon></button></div>
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
.add-playlist-button {
|
||||||
|
float: right;
|
||||||
|
position: relative;
|
||||||
|
bottom: 15px;
|
||||||
|
right: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-col {
|
||||||
|
max-width: 240px;
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { CustomPlaylistsComponent } from './custom-playlists.component';
|
||||||
|
|
||||||
|
describe('CustomPlaylistsComponent', () => {
|
||||||
|
let component: CustomPlaylistsComponent;
|
||||||
|
let fixture: ComponentFixture<CustomPlaylistsComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [ CustomPlaylistsComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(CustomPlaylistsComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,112 @@
|
|||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { PostsService } from 'app/posts.services';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
import { MatDialog } from '@angular/material/dialog';
|
||||||
|
import { CreatePlaylistComponent } from 'app/create-playlist/create-playlist.component';
|
||||||
|
import { ModifyPlaylistComponent } from 'app/dialogs/modify-playlist/modify-playlist.component';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-custom-playlists',
|
||||||
|
templateUrl: './custom-playlists.component.html',
|
||||||
|
styleUrls: ['./custom-playlists.component.scss']
|
||||||
|
})
|
||||||
|
export class CustomPlaylistsComponent implements OnInit {
|
||||||
|
|
||||||
|
playlists = null;
|
||||||
|
playlists_received = false;
|
||||||
|
downloading_content = {'video': {}, 'audio': {}};
|
||||||
|
|
||||||
|
constructor(public postsService: PostsService, private router: Router, private dialog: MatDialog) { }
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.postsService.service_initialized.subscribe(init => {
|
||||||
|
if (init) {
|
||||||
|
this.getAllPlaylists();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getAllPlaylists() {
|
||||||
|
this.playlists_received = false;
|
||||||
|
this.postsService.getAllFiles().subscribe(res => {
|
||||||
|
this.playlists = res['playlists'];
|
||||||
|
this.playlists_received = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// creating a playlist
|
||||||
|
openCreatePlaylistDialog() {
|
||||||
|
const dialogRef = this.dialog.open(CreatePlaylistComponent, {
|
||||||
|
data: {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
dialogRef.afterClosed().subscribe(result => {
|
||||||
|
if (result) {
|
||||||
|
this.getAllPlaylists();
|
||||||
|
this.postsService.openSnackBar('Successfully created playlist!', '');
|
||||||
|
} else if (result === false) {
|
||||||
|
this.postsService.openSnackBar('ERROR: failed to create playlist!', '');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
goToPlaylist(playlist) {
|
||||||
|
const playlistID = playlist.id;
|
||||||
|
const type = playlist.type;
|
||||||
|
|
||||||
|
if (playlist) {
|
||||||
|
if (this.postsService.config['Extra']['download_only_mode']) {
|
||||||
|
this.downloading_content[type][playlistID] = true;
|
||||||
|
this.downloadPlaylist(playlist.fileNames, type, playlist.name, playlistID);
|
||||||
|
} else {
|
||||||
|
localStorage.setItem('player_navigator', this.router.url);
|
||||||
|
const fileNames = playlist.fileNames;
|
||||||
|
this.router.navigate(['/player', {fileNames: fileNames.join('|nvr|'), type: type, id: playlistID, uid: playlistID}]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// playlist not found
|
||||||
|
console.error(`Playlist with ID ${playlistID} not found!`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
downloadPlaylist(fileNames, type, zipName = null, playlistID = null) {
|
||||||
|
this.postsService.downloadFileFromServer(fileNames, type, zipName).subscribe(res => {
|
||||||
|
if (playlistID) { this.downloading_content[type][playlistID] = false };
|
||||||
|
const blob: Blob = res;
|
||||||
|
saveAs(blob, zipName + '.zip');
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
deletePlaylist(args) {
|
||||||
|
const playlist = args.file;
|
||||||
|
const index = args.index;
|
||||||
|
const playlistID = playlist.id;
|
||||||
|
this.postsService.removePlaylist(playlistID, 'audio').subscribe(res => {
|
||||||
|
if (res['success']) {
|
||||||
|
this.playlists.splice(index, 1);
|
||||||
|
this.postsService.openSnackBar('Playlist successfully removed.', '');
|
||||||
|
}
|
||||||
|
this.getAllPlaylists();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
editPlaylistDialog(args) {
|
||||||
|
const playlist = args.playlist;
|
||||||
|
const index = args.index;
|
||||||
|
const dialogRef = this.dialog.open(ModifyPlaylistComponent, {
|
||||||
|
data: {
|
||||||
|
playlist: playlist,
|
||||||
|
width: '65vw'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
dialogRef.afterClosed().subscribe(res => {
|
||||||
|
// updates playlist in file manager if it changed
|
||||||
|
if (dialogRef.componentInstance.playlist_updated) {
|
||||||
|
this.playlists[index] = dialogRef.componentInstance.original_playlist;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
<div class="flex-grid">
|
||||||
|
<div class="col">
|
||||||
|
<div style="display: inline-block;">
|
||||||
|
<mat-form-field style="width: 132px;">
|
||||||
|
<mat-select [(ngModel)]="this.filterProperty" (selectionChange)="filterOptionChanged($event.value)">
|
||||||
|
<mat-option *ngFor="let filterOption of filterProperties | keyvalue" [value]="filterOption.value">
|
||||||
|
{{filterOption['value']['label']}}
|
||||||
|
</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
<div style="display: inline-block;">
|
||||||
|
<button (click)="toggleModeChange()" mat-icon-button><mat-icon>{{descendingMode ? 'arrow_downward' : 'arrow_upward'}}</mat-icon></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<h4 style="text-align: center">My videos</h4>
|
||||||
|
</div>
|
||||||
|
<div class="col" style="top: 25px;">
|
||||||
|
<mat-form-field [ngClass]="searchIsFocused ? 'search-bar-focused' : 'search-bar-unfocused'" class="search-bar" color="accent">
|
||||||
|
<input (focus)="searchIsFocused = true" (blur)="searchIsFocused = false" class="search-input" type="text" placeholder="Search" i18n-placeholder="Files search placeholder" [(ngModel)]="search_text" (ngModelChange)="onSearchInputChanged($event)" matInput>
|
||||||
|
<mat-icon matSuffix>search</mat-icon>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="container">
|
||||||
|
<div class="row justify-content-center">
|
||||||
|
<div *ngFor="let file of filtered_files; let i = index" [ngClass]="[ postsService.card_size === 'small' ? 'col-2 mb-2 mt-2 file-col' : '', postsService.card_size === 'medium' ? 'col-6 col-lg-4 mb-2 mt-2 file-col' : '' ]">
|
||||||
|
<app-unified-file-card [index]="i" [card_size]="postsService.card_size" (goToFile)="goToFile($event)" (goToSubscription)="goToSubscription($event)" [file_obj]="file" [use_youtubedl_archive]="postsService.config['Downloader']['use_youtubedl_archive']" (deleteFile)="deleteFile($event)"></app-unified-file-card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
.file-col {
|
||||||
|
max-width: 240px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-bar {
|
||||||
|
transition: all .5s ease;
|
||||||
|
position: relative;
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-bar-unfocused {
|
||||||
|
width: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-input {
|
||||||
|
transition: all .5s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-bar-focused {
|
||||||
|
width: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex-grid {
|
||||||
|
width: 100%;
|
||||||
|
display: block;
|
||||||
|
position: relative;
|
||||||
|
padding-left: 12px;
|
||||||
|
padding-right: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.col {
|
||||||
|
width: 33%;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { RecentVideosComponent } from './recent-videos.component';
|
||||||
|
|
||||||
|
describe('RecentVideosComponent', () => {
|
||||||
|
let component: RecentVideosComponent;
|
||||||
|
let fixture: ComponentFixture<RecentVideosComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [ RecentVideosComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(RecentVideosComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
262
src/app/components/recent-videos/recent-videos.component.ts
Normal file
262
src/app/components/recent-videos/recent-videos.component.ts
Normal file
@@ -0,0 +1,262 @@
|
|||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { PostsService } from 'app/posts.services';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-recent-videos',
|
||||||
|
templateUrl: './recent-videos.component.html',
|
||||||
|
styleUrls: ['./recent-videos.component.scss']
|
||||||
|
})
|
||||||
|
export class RecentVideosComponent implements OnInit {
|
||||||
|
|
||||||
|
normal_files_received = false;
|
||||||
|
subscription_files_received = false;
|
||||||
|
files: any[] = null;
|
||||||
|
filtered_files: any[] = null;
|
||||||
|
downloading_content = {'video': {}, 'audio': {}};
|
||||||
|
search_mode = false;
|
||||||
|
search_text = '';
|
||||||
|
searchIsFocused = false;
|
||||||
|
descendingMode = true;
|
||||||
|
filterProperties = {
|
||||||
|
'registered': {
|
||||||
|
'key': 'registered',
|
||||||
|
'label': 'Download Date',
|
||||||
|
'property': 'registered'
|
||||||
|
},
|
||||||
|
'upload_date': {
|
||||||
|
'key': 'upload_date',
|
||||||
|
'label': 'Upload Date',
|
||||||
|
'property': 'upload_date'
|
||||||
|
},
|
||||||
|
'name': {
|
||||||
|
'key': 'name',
|
||||||
|
'label': 'Name',
|
||||||
|
'property': 'title'
|
||||||
|
},
|
||||||
|
'file_size': {
|
||||||
|
'key': 'file_size',
|
||||||
|
'label': 'File Size',
|
||||||
|
'property': 'size'
|
||||||
|
},
|
||||||
|
'duration': {
|
||||||
|
'key': 'duration',
|
||||||
|
'label': 'Duration',
|
||||||
|
'property': 'duration'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
filterProperty = this.filterProperties['upload_date'];
|
||||||
|
|
||||||
|
constructor(public postsService: PostsService, private router: Router) { }
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.postsService.service_initialized.subscribe(init => {
|
||||||
|
if (init) {
|
||||||
|
this.getAllFiles();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// set filter property to cached
|
||||||
|
const cached_filter_property = localStorage.getItem('filter_property');
|
||||||
|
if (cached_filter_property && this.filterProperties[cached_filter_property]) {
|
||||||
|
this.filterProperty = this.filterProperties[cached_filter_property];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// search
|
||||||
|
|
||||||
|
onSearchInputChanged(newvalue) {
|
||||||
|
if (newvalue.length > 0) {
|
||||||
|
this.search_mode = true;
|
||||||
|
this.filterFiles(newvalue);
|
||||||
|
} else {
|
||||||
|
this.search_mode = false;
|
||||||
|
this.filtered_files = this.files;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private filterFiles(value: string) {
|
||||||
|
const filterValue = value.toLowerCase();
|
||||||
|
this.filtered_files = this.files.filter(option => option.id.toLowerCase().includes(filterValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
filterByProperty(prop) {
|
||||||
|
if (this.descendingMode) {
|
||||||
|
this.filtered_files = this.filtered_files.sort((a, b) => (a[prop] > b[prop] ? -1 : 1));
|
||||||
|
} else {
|
||||||
|
this.filtered_files = this.filtered_files.sort((a, b) => (a[prop] > b[prop] ? 1 : -1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
filterOptionChanged(value) {
|
||||||
|
this.filterByProperty(value['property']);
|
||||||
|
localStorage.setItem('filter_property', value['key']);
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleModeChange() {
|
||||||
|
this.descendingMode = !this.descendingMode;
|
||||||
|
this.filterByProperty(this.filterProperty['property']);
|
||||||
|
}
|
||||||
|
|
||||||
|
// get files
|
||||||
|
|
||||||
|
getAllFiles() {
|
||||||
|
this.normal_files_received = false;
|
||||||
|
this.postsService.getAllFiles().subscribe(res => {
|
||||||
|
this.files = res['files'];
|
||||||
|
this.files.forEach(file => {
|
||||||
|
file.duration = typeof file.duration !== 'string' ? file.duration : this.durationStringToNumber(file.duration);
|
||||||
|
});
|
||||||
|
this.files.sort(this.sortFiles);
|
||||||
|
if (this.search_mode) {
|
||||||
|
this.filterFiles(this.search_text);
|
||||||
|
} else {
|
||||||
|
this.filtered_files = this.files;
|
||||||
|
}
|
||||||
|
this.filterByProperty(this.filterProperty['property']);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// navigation
|
||||||
|
|
||||||
|
goToFile(file) {
|
||||||
|
if (this.postsService.config['Extra']['download_only_mode']) {
|
||||||
|
this.downloadFile(file);
|
||||||
|
} else {
|
||||||
|
this.navigateToFile(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
navigateToFile(file) {
|
||||||
|
localStorage.setItem('player_navigator', this.router.url);
|
||||||
|
if (file.sub_id) {
|
||||||
|
const sub = this.postsService.getSubscriptionByID(file.sub_id)
|
||||||
|
if (sub.streamingOnly) {
|
||||||
|
this.router.navigate(['/player', {name: file.id,
|
||||||
|
url: file.requested_formats ? file.requested_formats[0].url : file.url}]);
|
||||||
|
} else {
|
||||||
|
this.router.navigate(['/player', {fileNames: file.id,
|
||||||
|
type: file.isAudio ? 'audio' : 'video', subscriptionName: sub.name,
|
||||||
|
subPlaylist: sub.isPlaylist}]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.router.navigate(['/player', {type: file.isAudio ? 'audio' : 'video', uid: file.uid}]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
goToSubscription(file) {
|
||||||
|
this.router.navigate(['/subscription', {id: file.sub_id}]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// downloading
|
||||||
|
|
||||||
|
downloadFile(file) {
|
||||||
|
if (file.sub_id) {
|
||||||
|
this.downloadSubscriptionFile(file);
|
||||||
|
} else {
|
||||||
|
this.downloadNormalFile(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
downloadSubscriptionFile(file) {
|
||||||
|
const type = file.isAudio ? 'audio' : 'video';
|
||||||
|
const ext = type === 'audio' ? '.mp3' : '.mp4'
|
||||||
|
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.user ? this.postsService.user.uid : null, null).subscribe(res => {
|
||||||
|
const blob: Blob = res;
|
||||||
|
saveAs(blob, file.id + ext);
|
||||||
|
}, err => {
|
||||||
|
console.log(err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
downloadNormalFile(file) {
|
||||||
|
const type = file.isAudio ? 'audio' : 'video';
|
||||||
|
const ext = type === 'audio' ? '.mp3' : '.mp4'
|
||||||
|
const name = file.id;
|
||||||
|
this.downloading_content[type][name] = true;
|
||||||
|
this.postsService.downloadFileFromServer(name, type).subscribe(res => {
|
||||||
|
this.downloading_content[type][name] = false;
|
||||||
|
const blob: Blob = res;
|
||||||
|
saveAs(blob, decodeURIComponent(name) + ext);
|
||||||
|
|
||||||
|
if (!this.postsService.config.Extra.file_manager_enabled) {
|
||||||
|
// tell server to delete the file once downloaded
|
||||||
|
this.postsService.deleteFile(name, false).subscribe(delRes => {
|
||||||
|
// reload mp4s
|
||||||
|
this.getAllFiles();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// deleting
|
||||||
|
|
||||||
|
deleteFile(args) {
|
||||||
|
const file = args.file;
|
||||||
|
const index = args.index;
|
||||||
|
const blacklistMode = args.blacklistMode;
|
||||||
|
|
||||||
|
if (file.sub_id) {
|
||||||
|
this.deleteSubscriptionFile(file, index, blacklistMode);
|
||||||
|
} else {
|
||||||
|
this.deleteNormalFile(file, index, blacklistMode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteNormalFile(file, index, blacklistMode = false) {
|
||||||
|
this.postsService.deleteFile(file.uid, file.isAudio, blacklistMode).subscribe(result => {
|
||||||
|
if (result) {
|
||||||
|
this.postsService.openSnackBar('Delete success!', 'OK.');
|
||||||
|
this.files.splice(index, 1);
|
||||||
|
} else {
|
||||||
|
this.postsService.openSnackBar('Delete failed!', 'OK.');
|
||||||
|
}
|
||||||
|
}, err => {
|
||||||
|
this.postsService.openSnackBar('Delete failed!', 'OK.');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteSubscriptionFile(file, index, blacklistMode = false) {
|
||||||
|
if (blacklistMode) {
|
||||||
|
this.deleteForever(file, index);
|
||||||
|
} else {
|
||||||
|
this.deleteAndRedownload(file, index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteAndRedownload(file, index) {
|
||||||
|
const sub = this.postsService.getSubscriptionByID(file.sub_id);
|
||||||
|
this.postsService.deleteSubscriptionFile(sub, file.id, false, file.uid).subscribe(res => {
|
||||||
|
this.postsService.openSnackBar(`Successfully deleted file: '${file.id}'`);
|
||||||
|
this.files.splice(index, 1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteForever(file, index) {
|
||||||
|
const sub = this.postsService.getSubscriptionByID(file.sub_id);
|
||||||
|
this.postsService.deleteSubscriptionFile(sub, file.id, true, file.uid).subscribe(res => {
|
||||||
|
this.postsService.openSnackBar(`Successfully deleted file: '${file.id}'`);
|
||||||
|
this.files.splice(index, 1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// sorting and filtering
|
||||||
|
|
||||||
|
sortFiles(a, b) {
|
||||||
|
// uses the 'registered' flag as the timestamp
|
||||||
|
const result = b.registered - a.registered;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
durationStringToNumber(dur_str) {
|
||||||
|
let num_sum = 0;
|
||||||
|
const dur_str_parts = dur_str.split(':');
|
||||||
|
for (let i = dur_str_parts.length-1; i >= 0; i--) {
|
||||||
|
num_sum += parseInt(dur_str_parts[i])*(60**(dur_str_parts.length-1-i));
|
||||||
|
}
|
||||||
|
return num_sum;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
<div (mouseover)="elevated=true" (mouseout)="elevated=false" style="position: relative; width: fit-content;">
|
||||||
|
<div class="download-time"><mat-icon class="audio-video-icon">{{(file_obj.type === 'audio' || file_obj.isAudio) ? 'audiotrack' : 'movie'}}</mat-icon> {{file_obj.registered | date:'shortDate'}}</div>
|
||||||
|
<button [matMenuTriggerFor]="action_menu" class="menuButton" mat-icon-button><mat-icon>more_vert</mat-icon></button>
|
||||||
|
<mat-menu #action_menu="matMenu">
|
||||||
|
<ng-container *ngIf="!is_playlist">
|
||||||
|
<button (click)="openFileInfoDialog()" mat-menu-item><mat-icon>info</mat-icon><ng-container i18n="Video info button">Info</ng-container></button>
|
||||||
|
<button (click)="navigateToSubscription()" mat-menu-item *ngIf="file_obj.sub_id"><mat-icon>{{file_obj.isAudio ? 'library_music' : 'video_library'}}</mat-icon> <ng-container i18n="Go to subscription menu item">Go to subscription</ng-container></button>
|
||||||
|
<mat-divider></mat-divider>
|
||||||
|
<button *ngIf="file_obj.sub_id" (click)="emitDeleteFile()" mat-menu-item>
|
||||||
|
<mat-icon>restore</mat-icon><ng-container i18n="Delete and redownload subscription video button">Delete and redownload</ng-container>
|
||||||
|
</button>
|
||||||
|
<button *ngIf="file_obj.sub_id && use_youtubedl_archive" (click)="emitDeleteFile(true)" mat-menu-item>
|
||||||
|
<mat-icon>delete_forever</mat-icon><ng-container i18n="Delete forever subscription video button">Delete forever</ng-container>
|
||||||
|
</button>
|
||||||
|
<button *ngIf="!file_obj.sub_id" (click)="emitDeleteFile()" mat-menu-item><mat-icon>delete</mat-icon><ng-container i18n="Delete video button">Delete</ng-container></button>
|
||||||
|
<button *ngIf="!file_obj.sub_id && use_youtubedl_archive" (click)="emitDeleteFile(true)" mat-menu-item><mat-icon>delete_forever</mat-icon><ng-container i18n="Delete and blacklist video button">Delete and blacklist</ng-container></button>
|
||||||
|
</ng-container>
|
||||||
|
<ng-container *ngIf="is_playlist">
|
||||||
|
<button (click)="emitEditPlaylist()" mat-menu-item><mat-icon>edit</mat-icon><ng-container i18n="Playlist edit button">Edit</ng-container></button>
|
||||||
|
<mat-divider></mat-divider>
|
||||||
|
<button (click)="emitDeleteFile()" mat-menu-item><mat-icon>delete_forever</mat-icon><ng-container i18n="Delete playlist">Delete</ng-container></button>
|
||||||
|
</ng-container>
|
||||||
|
</mat-menu>
|
||||||
|
<mat-card [matTooltip]="null" (click)="navigateToFile()" matRipple class="file-mat-card" [ngClass]="{'small-mat-card': card_size === 'small', 'file-mat-card': card_size === 'medium', 'mat-elevation-z4': !elevated, 'mat-elevation-z8': elevated}">
|
||||||
|
<div style="padding:5px">
|
||||||
|
<div *ngIf="file_obj.thumbnailURL" class="img-div">
|
||||||
|
<div style="position: relative">
|
||||||
|
<img [ngClass]="{'image-small': card_size === 'small', 'image': card_size === 'medium'}" [src]="file_obj.thumbnailURL" alt="Thumbnail">
|
||||||
|
<div class="duration-time">
|
||||||
|
{{file_length}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<span [ngClass]="{'max-two-lines': card_size !== 'small', 'max-one-line': card_size === 'small' }"><strong>{{!is_playlist ? file_obj.title : file_obj.name}}</strong></span>
|
||||||
|
</div>
|
||||||
|
</mat-card>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,117 @@
|
|||||||
|
.file-mat-card {
|
||||||
|
width: 200px;
|
||||||
|
height: 200px;
|
||||||
|
padding: 0px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.small-mat-card {
|
||||||
|
width: 150px;
|
||||||
|
height: 150px;
|
||||||
|
padding: 0px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menuButton {
|
||||||
|
right: 0px;
|
||||||
|
top: -1px;
|
||||||
|
position: absolute;
|
||||||
|
z-index: 999;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Coerce the <span> icon container away from display:inline */
|
||||||
|
.mat-icon-button .mat-button-wrapper {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image {
|
||||||
|
width: 200px;
|
||||||
|
height: 112.5px;
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-small {
|
||||||
|
width: 150px;
|
||||||
|
height: 84.5px;
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
.example-full-width-height {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%
|
||||||
|
}
|
||||||
|
|
||||||
|
.centered {
|
||||||
|
margin: 0 auto;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.img-div {
|
||||||
|
max-height: 80px;
|
||||||
|
padding: 0px;
|
||||||
|
margin: 32px 0px 0px -5px;
|
||||||
|
width: calc(100% + 5px + 5px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.max-two-lines {
|
||||||
|
display: -webkit-box;
|
||||||
|
display: -moz-box;
|
||||||
|
max-height: 2.4em;
|
||||||
|
line-height: 1.2em;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
-webkit-line-clamp: 2;
|
||||||
|
bottom: 5px;
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|
||||||
|
.max-one-line {
|
||||||
|
display: -webkit-box;
|
||||||
|
display: -moz-box;
|
||||||
|
max-height: 1.2em;
|
||||||
|
line-height: 1.2em;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
-webkit-line-clamp: 2;
|
||||||
|
bottom: 5px;
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|
||||||
|
.duration-time {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 5px;
|
||||||
|
right: 5px;
|
||||||
|
z-index: 99999;
|
||||||
|
background: rgba(255,255,255,0.6);
|
||||||
|
padding-left: 5px;
|
||||||
|
padding-right: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.download-time {
|
||||||
|
position: absolute;
|
||||||
|
top: 1px;
|
||||||
|
left: 5px;
|
||||||
|
z-index: 99999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.audio-video-icon {
|
||||||
|
position: relative;
|
||||||
|
top: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 576px){
|
||||||
|
|
||||||
|
// .example-card {
|
||||||
|
// width: 175px !important;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// .image {
|
||||||
|
// width: 175px;
|
||||||
|
// }
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { UnifiedFileCardComponent } from './unified-file-card.component';
|
||||||
|
|
||||||
|
describe('UnifiedFileCardComponent', () => {
|
||||||
|
let component: UnifiedFileCardComponent;
|
||||||
|
let fixture: ComponentFixture<UnifiedFileCardComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [ UnifiedFileCardComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(UnifiedFileCardComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
|
||||||
|
import { MatDialog } from '@angular/material/dialog';
|
||||||
|
import { VideoInfoDialogComponent } from 'app/dialogs/video-info-dialog/video-info-dialog.component';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-unified-file-card',
|
||||||
|
templateUrl: './unified-file-card.component.html',
|
||||||
|
styleUrls: ['./unified-file-card.component.scss']
|
||||||
|
})
|
||||||
|
export class UnifiedFileCardComponent implements OnInit {
|
||||||
|
|
||||||
|
// required info
|
||||||
|
file_title = '';
|
||||||
|
file_length = '';
|
||||||
|
file_thumbnail = '';
|
||||||
|
type = null;
|
||||||
|
elevated = false;
|
||||||
|
|
||||||
|
@Input() file_obj = null;
|
||||||
|
@Input() card_size = 'medium';
|
||||||
|
@Input() use_youtubedl_archive = false;
|
||||||
|
@Input() is_playlist = false;
|
||||||
|
@Input() index: number;
|
||||||
|
@Output() goToFile = new EventEmitter<any>();
|
||||||
|
@Output() goToSubscription = new EventEmitter<any>();
|
||||||
|
@Output() deleteFile = new EventEmitter<any>();
|
||||||
|
@Output() editPlaylist = new EventEmitter<any>();
|
||||||
|
|
||||||
|
/*
|
||||||
|
Planned sizes:
|
||||||
|
small: 150x175
|
||||||
|
medium: 200x200
|
||||||
|
big: 250x200
|
||||||
|
*/
|
||||||
|
|
||||||
|
constructor(private dialog: MatDialog) { }
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.file_length = fancyTimeFormat(this.file_obj.duration);
|
||||||
|
}
|
||||||
|
|
||||||
|
emitDeleteFile(blacklistMode = false) {
|
||||||
|
this.deleteFile.emit({
|
||||||
|
file: this.file_obj,
|
||||||
|
index: this.index,
|
||||||
|
blacklistMode: blacklistMode
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
navigateToFile() {
|
||||||
|
this.goToFile.emit(this.file_obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
navigateToSubscription() {
|
||||||
|
this.goToSubscription.emit(this.file_obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
openFileInfoDialog() {
|
||||||
|
this.dialog.open(VideoInfoDialogComponent, {
|
||||||
|
data: {
|
||||||
|
file: this.file_obj,
|
||||||
|
},
|
||||||
|
minWidth: '50vw'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
emitEditPlaylist() {
|
||||||
|
this.editPlaylist.emit({
|
||||||
|
playlist: this.file_obj,
|
||||||
|
index: this.index
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function fancyTimeFormat(time) {
|
||||||
|
if (typeof time === 'string') {
|
||||||
|
return time;
|
||||||
|
}
|
||||||
|
// Hours, minutes and seconds
|
||||||
|
const hrs = ~~(time / 3600);
|
||||||
|
const mins = ~~((time % 3600) / 60);
|
||||||
|
const secs = ~~time % 60;
|
||||||
|
|
||||||
|
// Output like "1:01" or "4:03:59" or "123:03:59"
|
||||||
|
let ret = '';
|
||||||
|
|
||||||
|
if (hrs > 0) {
|
||||||
|
ret += '' + hrs + ':' + (mins < 10 ? '0' : '');
|
||||||
|
}
|
||||||
|
|
||||||
|
ret += '' + mins + ':' + (secs < 10 ? '0' : '');
|
||||||
|
ret += '' + secs;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
@@ -1,18 +1,34 @@
|
|||||||
<h4 mat-dialog-title i18n="Create a playlist dialog title">Create a playlist</h4>
|
<h4 mat-dialog-title i18n="Create a playlist dialog title">Create a playlist</h4>
|
||||||
<form>
|
<form>
|
||||||
<div>
|
<div *ngIf="filesToSelectFrom || (audiosToSelectFrom && videosToSelectFrom)">
|
||||||
<mat-form-field color="accent">
|
<div>
|
||||||
<input [(ngModel)]="name" matInput placeholder="Name" i18n-placeholder="Playlist name placeholder" type="text" required aria-required [ngModelOptions]="{standalone: true}">
|
<mat-form-field color="accent">
|
||||||
</mat-form-field>
|
<input [(ngModel)]="name" matInput placeholder="Name" i18n-placeholder="Playlist name placeholder" type="text" required aria-required [ngModelOptions]="{standalone: true}">
|
||||||
</div>
|
</mat-form-field>
|
||||||
<div>
|
</div>
|
||||||
<mat-form-field color="accent">
|
<div *ngIf="!filesToSelectFrom">
|
||||||
<mat-label *ngIf="type === 'audio'"><ng-container i18n="Audio files title">Audio files</ng-container></mat-label>
|
<mat-form-field color="accent">
|
||||||
<mat-label *ngIf="type === 'video'"><ng-container i18n="Videos title">Videos</ng-container></mat-label>
|
<mat-select placeholder="Type" i18n-placeholder="Type select" [(ngModel)]="type" [ngModelOptions]="{standalone: true}">
|
||||||
<mat-select [formControl]="filesSelect" multiple required aria-required>
|
<mat-option value="audio"><ng-container i18n="Audio">Audio</ng-container></mat-option>
|
||||||
<mat-option *ngFor="let file of filesToSelectFrom" [value]="file.id">{{file.id}}</mat-option>
|
<mat-option value="video"><ng-container i18n="Video">Video</ng-container></mat-option>
|
||||||
</mat-select>
|
</mat-select>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<mat-form-field *ngIf="type && ((filesToSelectFrom && filesToSelectFrom.length > 0) || (type === 'audio' && audiosToSelectFrom && audiosToSelectFrom.length > 0) || (type === 'video' && videosToSelectFrom && videosToSelectFrom.length > 0))" color="accent">
|
||||||
|
<mat-label *ngIf="type === 'audio'"><ng-container i18n="Audio files title">Audio files</ng-container></mat-label>
|
||||||
|
<mat-label *ngIf="type === 'video'"><ng-container i18n="Videos title">Videos</ng-container></mat-label>
|
||||||
|
<mat-select [formControl]="filesSelect" multiple required aria-required>
|
||||||
|
<ng-container *ngIf="filesToSelectFrom"><mat-option *ngFor="let file of filesToSelectFrom" [value]="file.id">{{file.id}}</mat-option></ng-container>
|
||||||
|
<ng-container *ngIf="audiosToSelectFrom && type === 'audio'"><mat-option *ngFor="let file of audiosToSelectFrom" [value]="file.id">{{file.id}}</mat-option></ng-container>
|
||||||
|
<ng-container *ngIf="videosToSelectFrom && type === 'video'"><mat-option *ngFor="let file of videosToSelectFrom" [value]="file.id">{{file.id}}</mat-option></ng-container>
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
<!-- No videos available -->
|
||||||
|
<div style="margin-bottom: 15px;" *ngIf="type && ((filesToSelectFrom && filesToSelectFrom.length === 0) || (type === 'audio' && audiosToSelectFrom && audiosToSelectFrom.length === 0) || (type === 'video' && videosToSelectFrom && videosToSelectFrom.length === 0))">
|
||||||
|
No files available.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ export class CreatePlaylistComponent implements OnInit {
|
|||||||
filesToSelectFrom = null;
|
filesToSelectFrom = null;
|
||||||
type = null;
|
type = null;
|
||||||
filesSelect = new FormControl();
|
filesSelect = new FormControl();
|
||||||
|
audiosToSelectFrom = null;
|
||||||
|
videosToSelectFrom = null;
|
||||||
name = '';
|
name = '';
|
||||||
|
|
||||||
create_in_progress = false;
|
create_in_progress = false;
|
||||||
@@ -28,12 +30,30 @@ export class CreatePlaylistComponent implements OnInit {
|
|||||||
this.filesToSelectFrom = this.data.filesToSelectFrom;
|
this.filesToSelectFrom = this.data.filesToSelectFrom;
|
||||||
this.type = this.data.type;
|
this.type = this.data.type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!this.filesToSelectFrom) {
|
||||||
|
this.getMp3s();
|
||||||
|
this.getMp4s();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getMp3s() {
|
||||||
|
this.postsService.getMp3s().subscribe(result => {
|
||||||
|
this.audiosToSelectFrom = result['mp3s'];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getMp4s() {
|
||||||
|
this.postsService.getMp4s().subscribe(result => {
|
||||||
|
this.videosToSelectFrom = result['mp4s'];
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
createPlaylist() {
|
createPlaylist() {
|
||||||
const thumbnailURL = this.getThumbnailURL();
|
const thumbnailURL = this.getThumbnailURL();
|
||||||
|
const duration = this.calculateDuration();
|
||||||
this.create_in_progress = true;
|
this.create_in_progress = true;
|
||||||
this.postsService.createPlaylist(this.name, this.filesSelect.value, this.type, thumbnailURL).subscribe(res => {
|
this.postsService.createPlaylist(this.name, this.filesSelect.value, this.type, thumbnailURL, duration).subscribe(res => {
|
||||||
this.create_in_progress = false;
|
this.create_in_progress = false;
|
||||||
if (res['success']) {
|
if (res['success']) {
|
||||||
this.dialogRef.close(true);
|
this.dialogRef.close(true);
|
||||||
@@ -44,8 +64,12 @@ export class CreatePlaylistComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getThumbnailURL() {
|
getThumbnailURL() {
|
||||||
for (let i = 0; i < this.filesToSelectFrom.length; i++) {
|
let properFilesToSelectFrom = this.filesToSelectFrom;
|
||||||
const file = this.filesToSelectFrom[i];
|
if (!this.filesToSelectFrom) {
|
||||||
|
properFilesToSelectFrom = this.type === 'audio' ? this.audiosToSelectFrom : this.videosToSelectFrom;
|
||||||
|
}
|
||||||
|
for (let i = 0; i < properFilesToSelectFrom.length; i++) {
|
||||||
|
const file = properFilesToSelectFrom[i];
|
||||||
if (file.id === this.filesSelect.value[0]) {
|
if (file.id === this.filesSelect.value[0]) {
|
||||||
// different services store the thumbnail in different places
|
// different services store the thumbnail in different places
|
||||||
if (file.thumbnailURL) { return file.thumbnailURL };
|
if (file.thumbnailURL) { return file.thumbnailURL };
|
||||||
@@ -55,4 +79,35 @@ export class CreatePlaylistComponent implements OnInit {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getDuration(file_id) {
|
||||||
|
let properFilesToSelectFrom = this.filesToSelectFrom;
|
||||||
|
if (!this.filesToSelectFrom) {
|
||||||
|
properFilesToSelectFrom = this.type === 'audio' ? this.audiosToSelectFrom : this.videosToSelectFrom;
|
||||||
|
}
|
||||||
|
for (let i = 0; i < properFilesToSelectFrom.length; i++) {
|
||||||
|
const file = properFilesToSelectFrom[i];
|
||||||
|
if (file.id === file_id) {
|
||||||
|
return file.duration;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
calculateDuration() {
|
||||||
|
let sum = 0;
|
||||||
|
for (let i = 0; i < this.filesSelect.value.length; i++) {
|
||||||
|
const duration_val = this.getDuration(this.filesSelect.value[i]);
|
||||||
|
sum += typeof duration_val === 'string' ? this.durationStringToNumber(duration_val) : duration_val;
|
||||||
|
}
|
||||||
|
return sum;
|
||||||
|
}
|
||||||
|
|
||||||
|
durationStringToNumber(dur_str) {
|
||||||
|
let num_sum = 0;
|
||||||
|
const dur_str_parts = dur_str.split(':');
|
||||||
|
for (let i = dur_str_parts.length-1; i >= 0; i--) {
|
||||||
|
num_sum += parseInt(dur_str_parts[i])*(60**(dur_str_parts.length-1-i));
|
||||||
|
}
|
||||||
|
return num_sum;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,34 @@
|
|||||||
<p>
|
<p>
|
||||||
<ng-container i18n="About bug prefix">Found a bug or have a suggestion?</ng-container> <a [href]="issuesLink" target="_blank"><ng-container i18n="About bug click here">Click here</ng-container></a> <ng-container i18n="About bug suffix">to create an issue!</ng-container>
|
<ng-container i18n="About bug prefix">Found a bug or have a suggestion?</ng-container> <a [href]="issuesLink" target="_blank"><ng-container i18n="About bug click here">Click here</ng-container></a> <ng-container i18n="About bug suffix">to create an issue!</ng-container>
|
||||||
</p>
|
</p>
|
||||||
|
<mat-divider></mat-divider>
|
||||||
|
<div style="margin-top: 10px;">
|
||||||
|
<h5>Personal settings:</h5>
|
||||||
|
<mat-form-field placeholder="Sidepanel mode">
|
||||||
|
<mat-select [(ngModel)]="sidepanel_mode" (selectionChange)="sidePanelModeChanged($event.value)">
|
||||||
|
<mat-option value="over">
|
||||||
|
Over
|
||||||
|
</mat-option>
|
||||||
|
<mat-option value="side">
|
||||||
|
Side
|
||||||
|
</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
<br/>
|
||||||
|
<mat-form-field placeholder="Card size">
|
||||||
|
<mat-select [(ngModel)]="card_size" (selectionChange)="cardSizeOptionChanged($event.value)">
|
||||||
|
<mat-option value="large" [disabled]="true">
|
||||||
|
Large
|
||||||
|
</mat-option>
|
||||||
|
<mat-option value="medium">
|
||||||
|
Medium
|
||||||
|
</mat-option>
|
||||||
|
<mat-option value="small">
|
||||||
|
Small
|
||||||
|
</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</mat-dialog-content>
|
</mat-dialog-content>
|
||||||
|
|
||||||
|
|||||||
@@ -16,11 +16,13 @@ export class AboutDialogComponent implements OnInit {
|
|||||||
checking_for_updates = true;
|
checking_for_updates = true;
|
||||||
|
|
||||||
current_version_tag = CURRENT_VERSION;
|
current_version_tag = CURRENT_VERSION;
|
||||||
|
sidepanel_mode = this.postsService.sidepanel_mode;
|
||||||
|
card_size = this.postsService.card_size;
|
||||||
|
|
||||||
constructor(private postsService: PostsService) { }
|
constructor(private postsService: PostsService) { }
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.getLatestGithubRelease()
|
this.getLatestGithubRelease();
|
||||||
}
|
}
|
||||||
|
|
||||||
getLatestGithubRelease() {
|
getLatestGithubRelease() {
|
||||||
@@ -30,4 +32,14 @@ export class AboutDialogComponent implements OnInit {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sidePanelModeChanged(new_mode) {
|
||||||
|
localStorage.setItem('sidepanel_mode', new_mode);
|
||||||
|
this.postsService.sidepanel_mode = new_mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
cardSizeOptionChanged(new_size) {
|
||||||
|
localStorage.setItem('card_size', new_size);
|
||||||
|
this.postsService.card_size = new_size;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,7 @@
|
|||||||
<br/>
|
<br/>
|
||||||
<div class="big demo-basic">
|
<div class="big demo-basic">
|
||||||
<mat-card id="card" style="margin-right: 20px; margin-left: 20px;" [ngClass]="(allowAdvancedDownload) ? 'no-border-radius-bottom' : null">
|
<mat-card id="card" style="margin-right: 20px; margin-left: 20px;" [ngClass]="(allowAdvancedDownload) ? 'no-border-radius-bottom' : null">
|
||||||
<mat-card-title>
|
<mat-card-content style="padding: 0px 8px 0px 8px;">
|
||||||
<ng-container i18n="Youtube downloader home page label">Youtube Downloader</ng-container>
|
|
||||||
</mat-card-title>
|
|
||||||
<mat-card-content>
|
|
||||||
<div style="position: relative;">
|
<div style="position: relative;">
|
||||||
<form class="example-form">
|
<form class="example-form">
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
@@ -183,7 +180,13 @@
|
|||||||
<ng-template #nofile>
|
<ng-template #nofile>
|
||||||
|
|
||||||
</ng-template>
|
</ng-template>
|
||||||
<div style="margin: 20px" *ngIf="fileManagerEnabled && (!postsService.isLoggedIn || postsService.permissions.includes('filemanager'))">
|
|
||||||
|
<app-recent-videos></app-recent-videos>
|
||||||
|
<br/>
|
||||||
|
<h4 style="text-align: center">Custom playlists</h4>
|
||||||
|
<app-custom-playlists></app-custom-playlists>
|
||||||
|
|
||||||
|
<!--<div style="margin: 20px" *ngIf="fileManagerEnabled && (!postsService.isLoggedIn || postsService.permissions.includes('filemanager'))">
|
||||||
<mat-accordion>
|
<mat-accordion>
|
||||||
<mat-expansion-panel (opened)="accordionOpened('audio')" (closed)="accordionClosed('audio')" (mouseleave)="accordionLeft('audio')" (mouseenter)="accordionEntered('audio')" class="big">
|
<mat-expansion-panel (opened)="accordionOpened('audio')" (closed)="accordionClosed('audio')" (mouseleave)="accordionLeft('audio')" (mouseenter)="accordionEntered('audio')" class="big">
|
||||||
<mat-expansion-panel-header>
|
<mat-expansion-panel-header>
|
||||||
@@ -260,7 +263,7 @@
|
|||||||
</mat-grid-tile>
|
</mat-grid-tile>
|
||||||
</mat-grid-list>
|
</mat-grid-list>
|
||||||
|
|
||||||
<!-- Add video playlist button -->
|
<!-- Add video playlist button --<
|
||||||
<div class="add-playlist-button"><button (click)="openCreatePlaylistDialog('video')" mat-fab><mat-icon>add</mat-icon></button></div>
|
<div class="add-playlist-button"><button (click)="openCreatePlaylistDialog('video')" mat-fab><mat-icon>add</mat-icon></button></div>
|
||||||
<div *ngIf="playlists.video.length === 0">
|
<div *ngIf="playlists.video.length === 0">
|
||||||
<ng-container i18n="No video playlists available text">
|
<ng-container i18n="No video playlists available text">
|
||||||
@@ -270,12 +273,4 @@
|
|||||||
</div>
|
</div>
|
||||||
</mat-expansion-panel>
|
</mat-expansion-panel>
|
||||||
</mat-accordion>
|
</mat-accordion>
|
||||||
</div>
|
</div>-->
|
||||||
|
|
||||||
<ng-template #nomp3s>
|
|
||||||
|
|
||||||
</ng-template>
|
|
||||||
|
|
||||||
<ng-template #nomp4s>
|
|
||||||
|
|
||||||
</ng-template>
|
|
||||||
@@ -15,6 +15,7 @@
|
|||||||
|
|
||||||
.video-styles {
|
.video-styles {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
height: 75vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
::ng-deep .mat-button-toggle-label-content {
|
::ng-deep .mat-button-toggle-label-content {
|
||||||
@@ -65,6 +66,13 @@
|
|||||||
padding-right: 0px;
|
padding-right: 0px;
|
||||||
padding-left: 0.01px;
|
padding-left: 0.01px;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.audio-col {
|
||||||
|
margin: 0 auto;
|
||||||
|
margin-top: 10px;
|
||||||
|
width: 95%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.save-icon {
|
.save-icon {
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
<div *ngIf="playlist.length > 0 && show_player">
|
<div *ngIf="playlist.length > 0 && show_player">
|
||||||
<div [ngClass]="(type === 'audio') ? null : 'container-video'" class="container">
|
<div [ngClass]="(type === 'audio') ? null : 'container-video'">
|
||||||
<div style="max-width: 100%; margin-left: 0px; height: 70vh" class="row">
|
<div style="max-width: 100%; margin-left: 0px; height: 70vh">
|
||||||
<div [ngClass]="(type === 'audio') ? 'my-2 px-1' : 'video-col'" class="col">
|
<div style="height: fit-content" [ngClass]="(type === 'audio') ? 'audio-col' : 'video-col'">
|
||||||
<vg-player (onPlayerReady)="onPlayerReady($event)" [style.background-color]="(type === 'audio') ? 'transparent' : 'black'">
|
<vg-player style="height: fit-content; max-height: 75vh" (onPlayerReady)="onPlayerReady($event)" [style.background-color]="(type === 'audio') ? 'transparent' : 'black'">
|
||||||
<video [ngClass]="(type === 'audio') ? 'audio-styles' : 'video-styles'" #media class="video-player" [vgMedia]="media" [src]="currentItem.src" id="singleVideo" preload="auto" controls>
|
<video [ngClass]="(type === 'audio') ? 'audio-styles' : 'video-styles'" #media class="video-player" [vgMedia]="media" [src]="currentItem.src" id="singleVideo" preload="auto" controls>
|
||||||
</video>
|
</video>
|
||||||
</vg-player>
|
</vg-player>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 my-2">
|
<div style="height: fit-content; width: 100%; margin-top: 10px;">
|
||||||
<mat-button-toggle-group cdkDropList [cdkDropListSortingDisabled]="!id" (cdkDropListDropped)="drop($event)" style="width: 80%; left: 9%" vertical name="videoSelect" aria-label="Video Select" #group="matButtonToggleGroup">
|
<mat-button-toggle-group cdkDropList [cdkDropListSortingDisabled]="!id" (cdkDropListDropped)="drop($event)" style="width: 80%; left: 9%" vertical name="videoSelect" aria-label="Video Select" #group="matButtonToggleGroup">
|
||||||
<mat-button-toggle cdkDrag *ngFor="let playlist_item of playlist; let i = index" [checked]="currentItem.title === playlist_item.title" (click)="onClickPlaylistItem(playlist_item, i)" class="toggle-button" [value]="playlist_item.title">{{playlist_item.label}}</mat-button-toggle>
|
<mat-button-toggle cdkDrag *ngFor="let playlist_item of playlist; let i = index" [checked]="currentItem.title === playlist_item.title" (click)="onClickPlaylistItem(playlist_item, i)" class="toggle-button" [value]="playlist_item.title">{{playlist_item.label}}</mat-button-toggle>
|
||||||
</mat-button-toggle-group>
|
</mat-button-toggle-group>
|
||||||
|
|||||||
@@ -330,7 +330,7 @@ export class PlayerComponent implements OnInit, OnDestroy {
|
|||||||
this.postsService.downloadFileFromServer(filename, this.type, null, null, this.subscriptionName, this.subPlaylist,
|
this.postsService.downloadFileFromServer(filename, this.type, null, null, this.subscriptionName, this.subPlaylist,
|
||||||
this.is_shared ? this.db_file['uid'] : null, this.uuid).subscribe(res => {
|
this.is_shared ? this.db_file['uid'] : null, this.uuid).subscribe(res => {
|
||||||
this.downloading = false;
|
this.downloading = false;
|
||||||
const blob: Blob = res;
|
const blob = new Blob([res], {type: this.type === 'audio' ? 'audio/mp3' : 'video/mp4'})
|
||||||
saveAs(blob, filename + ext);
|
saveAs(blob, filename + ext);
|
||||||
}, err => {
|
}, err => {
|
||||||
console.log(err);
|
console.log(err);
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ export class PostsService implements CanActivate {
|
|||||||
handShakeComplete = false;
|
handShakeComplete = false;
|
||||||
THEMES_CONFIG = THEMES_CONFIG;
|
THEMES_CONFIG = THEMES_CONFIG;
|
||||||
theme;
|
theme;
|
||||||
|
card_size = 'medium';
|
||||||
|
sidepanel_mode = 'over';
|
||||||
settings_changed = new BehaviorSubject<boolean>(false);
|
settings_changed = new BehaviorSubject<boolean>(false);
|
||||||
auth_token = '4241b401-7236-493e-92b5-b72696b9d853';
|
auth_token = '4241b401-7236-493e-92b5-b72696b9d853';
|
||||||
session_id = null;
|
session_id = null;
|
||||||
@@ -47,6 +49,7 @@ export class PostsService implements CanActivate {
|
|||||||
open_create_default_admin_dialog = new BehaviorSubject<boolean>(false);
|
open_create_default_admin_dialog = new BehaviorSubject<boolean>(false);
|
||||||
|
|
||||||
config = null;
|
config = null;
|
||||||
|
subscriptions = 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...');
|
||||||
@@ -101,6 +104,14 @@ export class PostsService implements CanActivate {
|
|||||||
this.reload_config.subscribe(yes_reload => {
|
this.reload_config.subscribe(yes_reload => {
|
||||||
if (yes_reload) { this.reloadConfig(); }
|
if (yes_reload) { this.reloadConfig(); }
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (localStorage.getItem('sidepanel_mode')) {
|
||||||
|
this.sidepanel_mode = localStorage.getItem('sidepanel_mode');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (localStorage.getItem('card_size')) {
|
||||||
|
this.card_size = localStorage.getItem('card_size');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
canActivate(route, state): Promise<boolean> {
|
canActivate(route, state): Promise<boolean> {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
@@ -114,6 +125,15 @@ export class PostsService implements CanActivate {
|
|||||||
this.theme = this.THEMES_CONFIG[theme];
|
this.theme = this.THEMES_CONFIG[theme];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getSubscriptionByID(sub_id) {
|
||||||
|
for (let i = 0; i < this.subscriptions.length; i++) {
|
||||||
|
if (this.subscriptions[i]['id'] === sub_id) {
|
||||||
|
return this.subscriptions[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
startHandshake(url: string) {
|
startHandshake(url: string) {
|
||||||
return this.http.get(url + 'geturl');
|
return this.http.get(url + 'geturl');
|
||||||
}
|
}
|
||||||
@@ -204,6 +224,10 @@ export class PostsService implements CanActivate {
|
|||||||
return this.http.post(this.path + 'getFile', {uid: uid, type: type, uuid: uuid}, this.httpOptions);
|
return this.http.post(this.path + 'getFile', {uid: uid, type: type, uuid: uuid}, this.httpOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getAllFiles() {
|
||||||
|
return this.http.post(this.path + 'getAllFiles', {}, this.httpOptions);
|
||||||
|
}
|
||||||
|
|
||||||
downloadFileFromServer(fileName, type, outputName = null, fullPathProvided = null, subscriptionName = null, subPlaylist = null,
|
downloadFileFromServer(fileName, type, outputName = null, fullPathProvided = null, subscriptionName = null, subPlaylist = null,
|
||||||
uid = null, uuid = null) {
|
uid = null, uuid = null) {
|
||||||
return this.http.post(this.path + 'downloadFile', {fileNames: fileName,
|
return this.http.post(this.path + 'downloadFile', {fileNames: fileName,
|
||||||
@@ -251,11 +275,12 @@ export class PostsService implements CanActivate {
|
|||||||
return this.http.post(this.path + 'disableSharing', {uid: uid, type: type, is_playlist: is_playlist}, this.httpOptions);
|
return this.http.post(this.path + 'disableSharing', {uid: uid, type: type, is_playlist: is_playlist}, this.httpOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
createPlaylist(playlistName, fileNames, type, thumbnailURL) {
|
createPlaylist(playlistName, fileNames, type, thumbnailURL, duration = null) {
|
||||||
return this.http.post(this.path + 'createPlaylist', {playlistName: playlistName,
|
return this.http.post(this.path + 'createPlaylist', {playlistName: playlistName,
|
||||||
fileNames: fileNames,
|
fileNames: fileNames,
|
||||||
type: type,
|
type: type,
|
||||||
thumbnailURL: thumbnailURL}, this.httpOptions);
|
thumbnailURL: thumbnailURL,
|
||||||
|
duration: duration}, this.httpOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
getPlaylist(playlistID, type, uuid = null) {
|
getPlaylist(playlistID, type, uuid = null) {
|
||||||
|
|||||||
@@ -79,11 +79,6 @@
|
|||||||
<mat-hint><ng-container i18n="Check interval setting input hint">Unit is seconds, only include numbers.</ng-container></mat-hint>
|
<mat-hint><ng-container i18n="Check interval setting input hint">Unit is seconds, only include numbers.</ng-container></mat-hint>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 mt-4">
|
|
||||||
<mat-checkbox color="accent" [disabled]="!new_config['Subscriptions']['allow_subscriptions']" [(ngModel)]="new_config['Subscriptions']['subscriptions_use_youtubedl_archive']"><ng-container i18n="Use youtube-dl archive setting">Use youtube-dl archive</ng-container></mat-checkbox>
|
|
||||||
<p><a target="_blank" href="https://github.com/ytdl-org/youtube-dl/blob/master/README.md#how-do-i-download-only-new-videos-from-a-playlist"><ng-container i18n="youtube-dl archive explanation prefix link">With youtube-dl's archive</ng-container></a> <ng-container i18n="youtube-dl archive explanation middle">feature, downloaded videos from your subscriptions get recorded in a text file in the subscriptions archive sub-directory.</ng-container></p>
|
|
||||||
<p><ng-container i18n="youtube-dl archive explanation suffix">This enables the ability to permanently delete videos from your subscriptions without unsubscribing, and allows you to record which videos you downloaded in case of data loss.</ng-container></p>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<mat-divider></mat-divider>
|
<mat-divider></mat-divider>
|
||||||
@@ -150,7 +145,6 @@
|
|||||||
|
|
||||||
<div class="col-12 mt-5">
|
<div class="col-12 mt-5">
|
||||||
<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><ng-container i18n="youtubedl archive setting Note">Note: This setting only applies to downloads on the Home page. If you would like to use youtube-dl archive functionality in subscriptions, head to the Main tab and activate this option there.</ng-container></p>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-12 mt-2">
|
<div class="col-12 mt-2">
|
||||||
@@ -285,6 +279,18 @@
|
|||||||
</mat-select>
|
</mat-select>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="col-12 mt-2 mb-1">
|
||||||
|
<mat-form-field>
|
||||||
|
<mat-label><ng-container i18n="Login expiration select label">Login expiration</ng-container></mat-label>
|
||||||
|
<mat-select color="accent" [(ngModel)]="new_config['Advanced']['jwt_expiration']">
|
||||||
|
<mat-option [value]="3600">1 Hour</mat-option>
|
||||||
|
<mat-option [value]="86400">1 Day</mat-option>
|
||||||
|
<mat-option [value]="604800">1 Week</mat-option>
|
||||||
|
<mat-option [value]="2592000">1 Month</mat-option>
|
||||||
|
<mat-option [value]="31536000">1 Year</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<mat-checkbox color="accent" [(ngModel)]="new_config['Advanced']['allow_advanced_download']"><ng-container i18n="Allow advanced downloading setting">Allow advanced download</ng-container></mat-checkbox>
|
<mat-checkbox color="accent" [(ngModel)]="new_config['Advanced']['allow_advanced_download']"><ng-container i18n="Allow advanced downloading setting">Allow advanced download</ng-container></mat-checkbox>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -108,4 +108,4 @@ function fancyTimeFormat(time) {
|
|||||||
ret += '' + mins + ':' + (secs < 10 ? '0' : '');
|
ret += '' + mins + ':' + (secs < 10 ? '0' : '');
|
||||||
ret += '' + secs;
|
ret += '' + secs;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
import { PostsService } from 'app/posts.services';
|
import { PostsService } from 'app/posts.services';
|
||||||
import { ActivatedRoute, Router } from '@angular/router';
|
import { ActivatedRoute, Router, ParamMap } from '@angular/router';
|
||||||
import { MatDialog } from '@angular/material/dialog';
|
import { MatDialog } from '@angular/material/dialog';
|
||||||
import { EditSubscriptionDialogComponent } from 'app/dialogs/edit-subscription-dialog/edit-subscription-dialog.component';
|
import { EditSubscriptionDialogComponent } from 'app/dialogs/edit-subscription-dialog/edit-subscription-dialog.component';
|
||||||
|
|
||||||
@@ -45,9 +45,21 @@ export class SubscriptionComponent implements OnInit {
|
|||||||
filterProperty = this.filterProperties['upload_date'];
|
filterProperty = this.filterProperties['upload_date'];
|
||||||
downloading = false;
|
downloading = false;
|
||||||
|
|
||||||
|
initialized = false;
|
||||||
|
|
||||||
constructor(private postsService: PostsService, private route: ActivatedRoute, private router: Router, private dialog: MatDialog) { }
|
constructor(private postsService: PostsService, private route: ActivatedRoute, private router: Router, private dialog: MatDialog) { }
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
|
this.route.paramMap.subscribe((params: ParamMap) => {
|
||||||
|
this.id = params.get('id');
|
||||||
|
this.postsService.service_initialized.subscribe(init => {
|
||||||
|
if (init) {
|
||||||
|
this.initialized = true;
|
||||||
|
this.getConfig();
|
||||||
|
this.getSubscription();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
if (this.route.snapshot.paramMap.get('id')) {
|
if (this.route.snapshot.paramMap.get('id')) {
|
||||||
this.id = this.route.snapshot.paramMap.get('id');
|
this.id = this.route.snapshot.paramMap.get('id');
|
||||||
|
|
||||||
@@ -84,7 +96,7 @@ export class SubscriptionComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getConfig() {
|
getConfig() {
|
||||||
this.use_youtubedl_archive = this.postsService.config['Subscriptions']['subscriptions_use_youtubedl_archive'];
|
this.use_youtubedl_archive = this.postsService.config['Downloader']['use_youtubedl_archive'];
|
||||||
}
|
}
|
||||||
|
|
||||||
goToFile(emit_obj) {
|
goToFile(emit_obj) {
|
||||||
@@ -123,7 +135,6 @@ export class SubscriptionComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
filterOptionChanged(value) {
|
filterOptionChanged(value) {
|
||||||
// this.filterProperty = value;
|
|
||||||
this.filterByProperty(value['property']);
|
this.filterByProperty(value['property']);
|
||||||
localStorage.setItem('filter_property', value['key']);
|
localStorage.setItem('filter_property', value['key']);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
"Downloader": {
|
"Downloader": {
|
||||||
"path-audio": "audio/",
|
"path-audio": "audio/",
|
||||||
"path-video": "video/",
|
"path-video": "video/",
|
||||||
"use_youtubedl_archive": false,
|
"use_youtubedl_archive": true,
|
||||||
"custom_args": "",
|
"custom_args": "",
|
||||||
"safe_download_override": false
|
"safe_download_override": false
|
||||||
},
|
},
|
||||||
@@ -50,6 +50,7 @@
|
|||||||
"custom_downloading_agent": "",
|
"custom_downloading_agent": "",
|
||||||
"multi_user_mode": true,
|
"multi_user_mode": true,
|
||||||
"allow_advanced_download": true,
|
"allow_advanced_download": true,
|
||||||
|
"jwt_expiration": 86400,
|
||||||
"logger_level": "debug",
|
"logger_level": "debug",
|
||||||
"use_cookies": true
|
"use_cookies": true
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user