diff --git a/backend/app.js b/backend/app.js
index 02daddf..3eba4ec 100644
--- a/backend/app.js
+++ b/backend/app.js
@@ -970,6 +970,7 @@ app.post('/api/downloadFile', optionalJwt, async function(req, res) {
req.setTimeout(0); // remove timeout in case of long videos
const url = req.body.url;
const type = req.body.type;
+ const user_uid = req.isAuthenticated() ? req.user.uid : null;
var options = {
customArgs: req.body.customArgs,
customOutput: req.body.customOutput,
@@ -978,11 +979,10 @@ app.post('/api/downloadFile', optionalJwt, async function(req, res) {
youtubeUsername: req.body.youtubeUsername,
youtubePassword: req.body.youtubePassword,
ui_uid: req.body.ui_uid,
- user: req.isAuthenticated() ? req.user.uid : null,
cropFileSettings: req.body.cropFileSettings
}
- const download = await downloader_api.createDownload(url, type, options);
+ const download = await downloader_api.createDownload(url, type, options, user_uid);
if (download) {
res.send({download: download});
@@ -1740,7 +1740,8 @@ app.get('/api/thumbnail/:path', optionalJwt, async (req, res) => {
// Downloads management
app.get('/api/downloads', optionalJwt, async (req, res) => {
- const downloads = await db_api.getRecords('download_queue');
+ const user_uid = req.isAuthenticated() ? req.user.uid : null;
+ const downloads = await db_api.getRecords('download_queue', {user_uid: user_uid});
res.send({downloads: downloads});
});
@@ -1757,7 +1758,8 @@ app.post('/api/download', optionalJwt, async (req, res) => {
});
app.post('/api/clearFinishedDownloads', optionalJwt, async (req, res) => {
- const success = db_api.removeAllRecords('download_queue', {finished: true});
+ const user_uid = req.isAuthenticated() ? req.user.uid : null;
+ const success = db_api.removeAllRecords('download_queue', {finished: true, user_uid: user_uid});
res.send({success: success});
});
diff --git a/backend/appdata/default.json b/backend/appdata/default.json
index 713a92d..c25bdce 100644
--- a/backend/appdata/default.json
+++ b/backend/appdata/default.json
@@ -20,7 +20,7 @@
"file_manager_enabled": true,
"allow_quality_select": true,
"download_only_mode": false,
- "allow_multi_download_mode": true,
+ "allow_autoplay": true,
"enable_downloads_manager": true,
"allow_playlist_categorization": true
},
@@ -68,8 +68,8 @@
"multi_user_mode": false,
"allow_advanced_download": false,
"use_cookies": false,
- "jwt_expiration": 86400,
+ "jwt_expiration": 86400,
"logger_level": "info"
}
}
-}
+}
\ No newline at end of file
diff --git a/backend/config.js b/backend/config.js
index c8a8aaa..90d3525 100644
--- a/backend/config.js
+++ b/backend/config.js
@@ -195,7 +195,7 @@ const DEFAULT_CONFIG = {
"file_manager_enabled": true,
"allow_quality_select": true,
"download_only_mode": false,
- "allow_multi_download_mode": true,
+ "allow_autoplay": true,
"enable_downloads_manager": true,
"allow_playlist_categorization": true
},
diff --git a/backend/consts.js b/backend/consts.js
index 28ee62c..5392b4e 100644
--- a/backend/consts.js
+++ b/backend/consts.js
@@ -64,9 +64,9 @@ exports.CONFIG_ITEMS = {
'key': 'ytdl_download_only_mode',
'path': 'YoutubeDLMaterial.Extra.download_only_mode'
},
- 'ytdl_allow_multi_download_mode': {
- 'key': 'ytdl_allow_multi_download_mode',
- 'path': 'YoutubeDLMaterial.Extra.allow_multi_download_mode'
+ 'ytdl_allow_autoplay': {
+ 'key': 'ytdl_allow_autoplay',
+ 'path': 'YoutubeDLMaterial.Extra.allow_autoplay'
},
'ytdl_enable_downloads_manager': {
'key': 'ytdl_enable_downloads_manager',
diff --git a/backend/downloader.js b/backend/downloader.js
index b5daff9..3528168 100644
--- a/backend/downloader.js
+++ b/backend/downloader.js
@@ -29,12 +29,13 @@ exports.initialize = (input_db_api) => {
setupDownloads();
}
-exports.createDownload = async (url, type, options) => {
+exports.createDownload = async (url, type, options, user_uid = null) => {
return await mutex.runExclusive(async () => {
const download = {
url: url,
type: type,
title: '',
+ user_uid: user_uid,
options: options,
uid: uuid(),
step_index: 0,
@@ -82,7 +83,7 @@ exports.resumeDownload = async (download_uid) => {
exports.restartDownload = async (download_uid) => {
const download = await db_api.getRecord('download_queue', {uid: download_uid});
await exports.clearDownload(download_uid);
- const success = !!(await exports.createDownload(download['url'], download['type'], download['options']));
+ const success = !!(await exports.createDownload(download['url'], download['type'], download['options'], download['user_uid']));
should_check_downloads = true;
return success;
@@ -171,13 +172,13 @@ async function collectInfo(download_uid) {
const type = download['type'];
const options = download['options'];
- if (options.user && !options.customFileFolderPath) {
+ if (download['user_uid'] && !options.customFileFolderPath) {
let usersFileFolder = config_api.getConfigItem('ytdl_users_base_path');
- const user_path = path.join(usersFileFolder, options.user, type);
+ const user_path = path.join(usersFileFolder, download['user_uid'], type);
options.customFileFolderPath = user_path + path.sep;
}
- let args = await generateArgs(url, type, options);
+ let args = await generateArgs(url, type, options, download['user_uid']);
// get video info prior to download
let info = await getVideoInfoByURL(url, args, download_uid);
@@ -198,7 +199,7 @@ async function collectInfo(download_uid) {
if (category && category['custom_output']) {
options.customOutput = category['custom_output'];
options.noRelativePath = true;
- args = await generateArgs(url, type, options);
+ args = await generateArgs(url, type, options, download['user_uid']);
info = await getVideoInfoByURL(url, args, download_uid);
}
@@ -294,7 +295,7 @@ async function downloadQueuedFile(download_uid) {
&& config_api.getConfigItem('ytdl_use_twitch_api') && config_api.getConfigItem('ytdl_twitch_auto_download_chat')) {
let vodId = url.split('twitch.tv/videos/')[1];
vodId = vodId.split('?')[0];
- twitch_api.downloadTwitchChatByVODID(vodId, file_name, type, options.user);
+ twitch_api.downloadTwitchChatByVODID(vodId, file_name, type, download['user_uid']);
}
// renames file if necessary due to bug
@@ -321,7 +322,7 @@ async function downloadQueuedFile(download_uid) {
}
// registers file in DB
- const file_obj = await db_api.registerFileDB2(full_file_path, type, options.user, category, null, options.cropFileSettings);
+ const file_obj = await db_api.registerFileDB2(full_file_path, type, download['user_uid'], category, null, options.cropFileSettings);
file_objs.push(file_obj);
}
@@ -329,7 +330,7 @@ async function downloadQueuedFile(download_uid) {
if (options.merged_string !== null && options.merged_string !== undefined) {
let current_merged_archive = fs.readFileSync(path.join(fileFolderPath, `merged_${type}.txt`), 'utf8');
let diff = current_merged_archive.replace(options.merged_string, '');
- const archive_path = options.user ? path.join(fileFolderPath, 'archives', `archive_${type}.txt`) : path.join(archivePath, `archive_${type}.txt`);
+ const archive_path = download['user_uid'] ? path.join(fileFolderPath, 'archives', `archive_${type}.txt`) : path.join(archivePath, `archive_${type}.txt`);
fs.appendFileSync(archive_path, diff);
}
@@ -338,7 +339,7 @@ async function downloadQueuedFile(download_uid) {
if (file_objs.length > 1) {
// create playlist
const playlist_name = file_objs.map(file_obj => file_obj.title).join(', ');
- container = await db_api.createPlaylist(playlist_name, file_objs.map(file_obj => file_obj.uid), type, options.user);
+ container = await db_api.createPlaylist(playlist_name, file_objs.map(file_obj => file_obj.uid), type, download['user_uid']);
} else if (file_objs.length === 1) {
container = file_objs[0];
} else {
@@ -355,7 +356,7 @@ async function downloadQueuedFile(download_uid) {
// helper functions
-async function generateArgs(url, type, options) {
+async function generateArgs(url, type, options, user_uid = null) {
const audioFolderPath = config_api.getConfigItem('ytdl_audio_folder_path');
const videoFolderPath = config_api.getConfigItem('ytdl_video_folder_path');
@@ -436,13 +437,13 @@ async function generateArgs(url, type, options) {
let useYoutubeDLArchive = config_api.getConfigItem('ytdl_use_youtubedl_archive');
if (useYoutubeDLArchive) {
- const archive_folder = options.user ? path.join(fileFolderPath, 'archives') : archivePath;
+ const archive_folder = user_uid ? path.join(fileFolderPath, 'archives') : archivePath;
const archive_path = path.join(archive_folder, `archive_${type}.txt`);
await fs.ensureDir(archive_folder);
await fs.ensureFile(archive_path);
- let blacklist_path = options.user ? path.join(fileFolderPath, 'archives', `blacklist_${type}.txt`) : path.join(archivePath, `blacklist_${type}.txt`);
+ let blacklist_path = user_uid ? path.join(fileFolderPath, 'archives', `blacklist_${type}.txt`) : path.join(archivePath, `blacklist_${type}.txt`);
await fs.ensureFile(blacklist_path);
let merged_path = path.join(fileFolderPath, `merged_${type}.txt`);
diff --git a/src/app/components/downloads/downloads.component.html b/src/app/components/downloads/downloads.component.html
index bbb953c..50f2762 100644
--- a/src/app/components/downloads/downloads.component.html
+++ b/src/app/components/downloads/downloads.component.html
@@ -1,4 +1,4 @@
-
+
0">
@@ -63,4 +63,8 @@
aria-label="Select page of downloads">
+
+
+
+
No downloads available!
\ No newline at end of file
diff --git a/src/app/components/downloads/downloads.component.ts b/src/app/components/downloads/downloads.component.ts
index ba0eee5..380b77c 100644
--- a/src/app/components/downloads/downloads.component.ts
+++ b/src/app/components/downloads/downloads.component.ts
@@ -54,6 +54,7 @@ export class DownloadsComponent implements OnInit, OnDestroy {
displayedColumns: string[] = ['date', 'title', 'stage', 'progress', 'actions'];
dataSource = null; // new MatTableDataSource
();
+ downloads_retrieved = false;
@ViewChild(MatPaginator) paginator: MatPaginator;
@@ -93,10 +94,12 @@ export class DownloadsComponent implements OnInit, OnDestroy {
getCurrentDownloads(): void {
this.postsService.getCurrentDownloads().subscribe(res => {
+ this.downloads_retrieved = true;
if (res['downloads'] !== null
&& res['downloads'] !== undefined
&& JSON.stringify(this.downloads) !== JSON.stringify(res['downloads'])) {
- this.downloads = res['downloads'];
+ this.downloads = this.combineDownloads(this.downloads, res['downloads']);
+ // this.downloads = res['downloads'];
this.downloads.sort(this.sort_downloads);
this.dataSource = new MatTableDataSource(this.downloads);
this.dataSource.paginator = this.paginator;
@@ -114,7 +117,7 @@ export class DownloadsComponent implements OnInit, OnDestroy {
});
}
- pauseDownload(download_uid) {
+ pauseDownload(download_uid: string): void {
this.postsService.pauseDownload(download_uid).subscribe(res => {
if (!res['success']) {
this.postsService.openSnackBar('Failed to pause download! See server logs for more info.');
@@ -122,7 +125,7 @@ export class DownloadsComponent implements OnInit, OnDestroy {
});
}
- resumeDownload(download_uid) {
+ resumeDownload(download_uid: string): void {
this.postsService.resumeDownload(download_uid).subscribe(res => {
if (!res['success']) {
this.postsService.openSnackBar('Failed to resume download! See server logs for more info.');
@@ -130,7 +133,7 @@ export class DownloadsComponent implements OnInit, OnDestroy {
});
}
- restartDownload(download_uid) {
+ restartDownload(download_uid: string): void {
this.postsService.restartDownload(download_uid).subscribe(res => {
if (!res['success']) {
this.postsService.openSnackBar('Failed to restart download! See server logs for more info.');
@@ -138,7 +141,7 @@ export class DownloadsComponent implements OnInit, OnDestroy {
});
}
- cancelDownload(download_uid) {
+ cancelDownload(download_uid: string): void {
this.postsService.cancelDownload(download_uid).subscribe(res => {
if (!res['success']) {
this.postsService.openSnackBar('Failed to cancel download! See server logs for more info.');
@@ -146,7 +149,7 @@ export class DownloadsComponent implements OnInit, OnDestroy {
});
}
- clearDownload(download_uid) {
+ clearDownload(download_uid: string): void {
this.postsService.clearDownload(download_uid).subscribe(res => {
if (!res['success']) {
this.postsService.openSnackBar('Failed to pause download! See server logs for more info.');
@@ -154,7 +157,7 @@ export class DownloadsComponent implements OnInit, OnDestroy {
});
}
- watchContent(download) {
+ watchContent(download): void {
const container = download['container'];
localStorage.setItem('player_navigator', this.router.url.split(';')[0]);
const is_playlist = container['uids']; // hacky, TODO: fix
@@ -165,6 +168,26 @@ export class DownloadsComponent implements OnInit, OnDestroy {
}
}
+ combineDownloads(downloads_old, downloads_new) {
+ // only keeps downloads that exist in the new set
+ downloads_old = downloads_old.filter(download_old => downloads_new.some(download_new => download_new.uid === download_old.uid));
+
+ // add downloads from the new set that the old one doesn't have
+ const downloads_to_add = downloads_new.filter(download_new => !downloads_old.some(download_old => download_new.uid === download_old.uid));
+ downloads_old.push(...downloads_to_add);
+ downloads_old.forEach(download_old => {
+ const download_new = downloads_new.find(download_to_check => download_old.uid === download_to_check.uid);
+ Object.keys(download_new).forEach(key => {
+ download_old[key] = download_new[key];
+ });
+
+ Object.keys(download_old).forEach(key => {
+ if (!download_new[key]) delete download_old[key];
+ });
+ });
+
+ return downloads_old;
+ }
}
export interface Download {
diff --git a/src/app/main/main.component.html b/src/app/main/main.component.html
index 604f9ce..84ae4f7 100644
--- a/src/app/main/main.component.html
+++ b/src/app/main/main.component.html
@@ -65,9 +65,9 @@
Only Audio
-
-
- Multi-download Mode
+
+
+ Autoplay
@@ -169,18 +169,6 @@
- 0 && !current_download" style="margin-top: 15px;" class="big demo-basic">
-
-
-
-
diff --git a/src/app/main/main.component.ts b/src/app/main/main.component.ts
index 934f484..fb4a5ce 100644
--- a/src/app/main/main.component.ts
+++ b/src/app/main/main.component.ts
@@ -46,7 +46,7 @@ export class MainComponent implements OnInit {
determinateProgress = false;
downloadingfile = false;
audioOnly: boolean;
- multiDownloadMode = false;
+ autoplay = false;
customArgsEnabled = false;
customArgs = null;
customOutputEnabled = false;
@@ -68,7 +68,7 @@ export class MainComponent implements OnInit {
fileManagerEnabled = false;
allowQualitySelect = false;
downloadOnlyMode = false;
- allowMultiDownloadMode = false;
+ allowAutoplay = false;
audioFolderPath;
videoFolderPath;
use_youtubedl_archive = false;
@@ -232,7 +232,7 @@ export class MainComponent implements OnInit {
this.fileManagerEnabled = this.postsService.config['Extra']['file_manager_enabled']
&& this.postsService.hasPermission('filemanager');
this.downloadOnlyMode = this.postsService.config['Extra']['download_only_mode'];
- this.allowMultiDownloadMode = this.postsService.config['Extra']['allow_multi_download_mode'];
+ this.allowAutoplay = this.postsService.config['Extra']['allow_autoplay'];
this.audioFolderPath = this.postsService.config['Downloader']['path-audio'];
this.videoFolderPath = this.postsService.config['Downloader']['path-video'];
this.use_youtubedl_archive = this.postsService.config['Downloader']['use_youtubedl_archive'];
@@ -314,8 +314,8 @@ export class MainComponent implements OnInit {
this.audioOnly = localStorage.getItem('audioOnly') === 'true';
}
- if (localStorage.getItem('multiDownloadMode') !== null) {
- this.multiDownloadMode = localStorage.getItem('multiDownloadMode') === 'true';
+ if (localStorage.getItem('autoplay') !== null) {
+ this.autoplay = localStorage.getItem('autoplay') === 'true';
}
// check if params exist
@@ -374,10 +374,9 @@ export class MainComponent implements OnInit {
}
// download helpers
-
downloadHelper(container, type, is_playlist = false, force_view = false, navigate_mode = false) {
this.downloadingfile = false;
- if (this.multiDownloadMode && !this.downloadOnlyMode && !navigate_mode) {
+ if (!this.autoplay && !this.downloadOnlyMode && !navigate_mode) {
// do nothing
this.reloadRecentVideos();
} else {
@@ -398,9 +397,6 @@ export class MainComponent implements OnInit {
}
}
}
-
- // // remove download from current downloads
- // this.removeDownloadFromCurrentDownloads(new_download);
}
// download click handler
@@ -432,8 +428,6 @@ export class MainComponent implements OnInit {
}
const type = this.audioOnly ? 'audio' : 'video';
- // this.downloads.push(new_download);
- // if (!this.current_download && !this.multiDownloadMode) { this.current_download = new_download };
this.downloadingfile = true;
const customQualityConfiguration = type === 'audio' ? this.getSelectedAudioFormat() : this.getSelectedVideoFormat();
@@ -449,22 +443,17 @@ export class MainComponent implements OnInit {
this.postsService.downloadFile(this.url, type, (this.selectedQuality === '' ? null : this.selectedQuality),
customQualityConfiguration, customArgs, customOutput, youtubeUsername, youtubePassword, cropFileSettings).subscribe(res => {
- console.log(res);
this.current_download = res['download'];
this.downloadingfile = true;
}, error => { // can't access server
this.downloadingfile = false;
this.current_download = null;
- // new_download['downloading'] = false;
- // // removes download from list of downloads
- // const downloads_index = this.downloads.indexOf(new_download);
- // if (downloads_index !== -1) {
- // this.downloads.splice(downloads_index)
- // }
this.openSnackBar('Download failed!', 'OK.');
});
- if (this.multiDownloadMode) {
+ if (!this.autoplay) {
+ const download_queued_message = $localize`Download for ${this.url}:url has been queued!`;
+ this.postsService.openSnackBar(download_queued_message);
this.url = '';
this.downloadingfile = false;
}
@@ -755,8 +744,8 @@ export class MainComponent implements OnInit {
localStorage.setItem('audioOnly', new_val.checked.toString());
}
- multiDownloadModeChanged(new_val) {
- localStorage.setItem('multiDownloadMode', new_val.checked.toString());
+ autoplayChanged(new_val) {
+ localStorage.setItem('autoplay', new_val.checked.toString());
}
customArgsEnabledChanged(new_val) {
diff --git a/src/app/settings/settings.component.html b/src/app/settings/settings.component.html
index bad9daf..43d0579 100644
--- a/src/app/settings/settings.component.html
+++ b/src/app/settings/settings.component.html
@@ -225,7 +225,7 @@
Download only mode
- Allow multi-download mode
+ Allow autoplay