diff --git a/backend/app.js b/backend/app.js index 224e05b..d6c6666 100644 --- a/backend/app.js +++ b/backend/app.js @@ -1705,7 +1705,18 @@ app.use(compression()); const optionalJwt = function (req, res, next) { const multiUserMode = config_api.getConfigItem('ytdl_multi_user_mode'); - if (multiUserMode) { + if (multiUserMode && ((req.body && req.body.uuid) || (req.query && req.query.uuid)) && (req.path.includes('/api/getFile') || + req.path.includes('/api/audio') || + req.path.includes('/api/video') || + req.path.includes('/api/downloadFile'))) { + // check if shared video + const using_body = req.body && req.body.uuid; + const uuid = using_body ? req.body.uuid : req.query.uuid; + const uid = using_body ? req.body.uid : req.query.uid; + const type = using_body ? req.body.type : req.query.type; + const is_shared = auth_api.getUserVideo(uuid, uid, type, true); + if (is_shared) return next(); + } else if (multiUserMode) { if (!req.query.jwt) { res.sendStatus(401); return; @@ -1878,11 +1889,14 @@ app.get('/api/getMp4s', optionalJwt, function(req, res) { app.post('/api/getFile', optionalJwt, function (req, res) { var uid = req.body.uid; var type = req.body.type; + var uuid = req.body.uuid; var file = null; if (req.isAuthenticated()) { file = auth_api.getUserVideo(req.user.uid, uid, type); + } else if (uuid) { + file = auth_api.getUserVideo(uuid, uid, type, true); } else { if (!type) { file = db.get('files.audio').find({uid: uid}).value(); @@ -1911,10 +1925,21 @@ app.post('/api/getFile', optionalJwt, function (req, res) { }); // video sharing -app.post('/api/enableSharing', function(req, res) { +app.post('/api/enableSharing', optionalJwt, function(req, res) { var type = req.body.type; var uid = req.body.uid; var is_playlist = req.body.is_playlist; + let success = false; + // multi-user mode + if (req.isAuthenticated()) { + // if multi user mode, use this method instead + success = auth_api.changeSharingMode(req.user.uid, uid, type, is_playlist, true); + console.log(success); + res.send({success: success}); + return; + } + + // single-user mode try { success = true; if (!is_playlist && type !== 'subscription') { @@ -1944,10 +1969,20 @@ app.post('/api/enableSharing', function(req, res) { }); }); -app.post('/api/disableSharing', function(req, res) { +app.post('/api/disableSharing', optionalJwt, function(req, res) { var type = req.body.type; var uid = req.body.uid; var is_playlist = req.body.is_playlist; + + // multi-user mode + if (req.isAuthenticated()) { + // if multi user mode, use this method instead + success = auth_api.changeSharingMode(req.user.uid, uid, type, is_playlist, false); + res.send({success: success}); + return; + } + + // single-user mode try { success = true; if (!is_playlist && type !== 'subscription') { diff --git a/backend/authentication/auth.js b/backend/authentication/auth.js index 2a9eceb..a274198 100644 --- a/backend/authentication/auth.js +++ b/backend/authentication/auth.js @@ -237,7 +237,7 @@ exports.getUserVideos = function(user_uid, type) { return user['files'][type]; } -exports.getUserVideo = function(user_uid, file_uid, type) { +exports.getUserVideo = function(user_uid, file_uid, type, requireSharing = false) { if (!type) { file = db.get('users').find({uid: user_uid}).get(`files.audio`).find({uid: file_uid}).value(); if (!file) { @@ -249,6 +249,10 @@ exports.getUserVideo = function(user_uid, file_uid, type) { } if (!file && type) file = db.get('users').find({uid: user_uid}).get(`files.${type}`).find({uid: file_uid}).value(); + + // prevent unauthorized users from accessing the file info + if (requireSharing && !file['sharingEnabled']) file = null; + return file; } @@ -366,6 +370,20 @@ exports.deleteUserFile = function(user_uid, file_uid, type, blacklistMode = fals return success; } +exports.changeSharingMode = function(user_uid, file_uid, type, is_playlist, enabled) { + let success = false; + const user_db_obj = db.get('users').find({uid: user_uid}); + if (user_db_obj.value()) { + const file_db_obj = is_playlist ? user_db_obj.get(`playlists.${type}`).find({uid: file_uid}) : user_db_obj.get(`files.${type}`).find({uid: file_uid}); + if (file_db_obj.value()) { + success = true; + file_db_obj.assign({sharingEnabled: enabled}).write(); + } + } + + return success; +} + function getToken(queryParams) { if (queryParams && queryParams.jwt) { var parted = queryParams.jwt.split(' '); diff --git a/src/app/dialogs/share-media-dialog/share-media-dialog.component.ts b/src/app/dialogs/share-media-dialog/share-media-dialog.component.ts index 950a6cc..d6771f5 100644 --- a/src/app/dialogs/share-media-dialog/share-media-dialog.component.ts +++ b/src/app/dialogs/share-media-dialog/share-media-dialog.component.ts @@ -13,6 +13,7 @@ export class ShareMediaDialogComponent implements OnInit { type = null; uid = null; + uuid = null; share_url = null; sharing_enabled = null; is_playlist = null; @@ -24,11 +25,15 @@ export class ShareMediaDialogComponent implements OnInit { if (this.data) { this.type = this.data.type; this.uid = this.data.uid; + this.uuid = this.data.uuid; this.sharing_enabled = this.data.sharing_enabled; this.is_playlist = this.data.is_playlist; const arg = (this.is_playlist ? ';id=' : ';uid='); this.share_url = window.location.href.split(';')[0] + arg + this.uid; + if (this.uuid) { + this.share_url += ';uuid=' + this.uuid; + } } } diff --git a/src/app/player/player.component.ts b/src/app/player/player.component.ts index 07f33fa..06d3f53 100644 --- a/src/app/player/player.component.ts +++ b/src/app/player/player.component.ts @@ -39,6 +39,7 @@ export class PlayerComponent implements OnInit { uid = null; // used for non-subscription files (audio, video, playlist) subscriptionName = null; subPlaylist = null; + uuid = null; // used for sharing in multi-user mode, uuid is the user that downloaded the video is_shared = false; @@ -50,6 +51,8 @@ export class PlayerComponent implements OnInit { videoFolderPath = null; subscriptionFolderPath = null; + sharingEnabled = null; + // url-mode params url = null; name = null; @@ -73,6 +76,7 @@ export class PlayerComponent implements OnInit { this.subPlaylist = this.route.snapshot.paramMap.get('subPlaylist'); this.url = this.route.snapshot.paramMap.get('url'); this.name = this.route.snapshot.paramMap.get('name'); + this.uuid = this.route.snapshot.paramMap.get('uuid'); // loading config this.postsService.loadNavItems().subscribe(res => { // loads settings @@ -123,12 +127,13 @@ export class PlayerComponent implements OnInit { getFile() { const already_has_filenames = !!this.fileNames; - this.postsService.getFile(this.uid, null).subscribe(res => { + this.postsService.getFile(this.uid, null, this.uuid).subscribe(res => { this.db_file = res['file']; if (!this.db_file) { this.openSnackBar('Failed to get file information from the server.', 'Dismiss'); return; } + this.sharingEnabled = this.db_file.sharingEnabled; if (!this.fileNames) { // means it's a shared video if (!this.id) { @@ -186,7 +191,10 @@ export class PlayerComponent implements OnInit { // adds user token if in multi-user-mode if (this.postsService.isLoggedIn) { - fullLocation += '?jwt=' + this.postsService.token; + fullLocation += `?jwt=${this.postsService.token}`; + if (this.is_shared) { fullLocation += `&uuid=${this.uuid}&uid=${this.db_file.uid}&type=${this.db_file.type}`; } + } else if (this.is_shared) { + fullLocation += `?uuid=${this.uuid}&uid=${this.db_file.uid}&type=${this.db_file.type}`; } // if it has a slash (meaning it's in a directory), only get the file name for the label let label = null; @@ -278,7 +286,8 @@ export class PlayerComponent implements OnInit { const ext = (this.type === 'audio') ? '.mp3' : '.mp4'; const filename = this.playlist[0].title; this.downloading = true; - this.postsService.downloadFileFromServer(filename, this.type, null, null, this.subscriptionName, this.subPlaylist).subscribe(res => { + 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.downloading = false; const blob: Blob = res; saveAs(blob, filename + ext); @@ -365,7 +374,8 @@ export class PlayerComponent implements OnInit { uid: this.id ? this.id : this.uid, type: this.type, sharing_enabled: this.id ? this.db_playlist.sharingEnabled : this.db_file.sharingEnabled, - is_playlist: !!this.id + is_playlist: !!this.id, + uuid: this.postsService.isLoggedIn ? this.postsService.user.uid : null }, width: '60vw' }); diff --git a/src/app/posts.services.ts b/src/app/posts.services.ts index d188006..0689100 100644 --- a/src/app/posts.services.ts +++ b/src/app/posts.services.ts @@ -179,18 +179,21 @@ export class PostsService implements CanActivate { return this.http.get(this.path + 'getMp4s', this.httpOptions); } - getFile(uid, type) { - return this.http.post(this.path + 'getFile', {uid: uid, type: type}, this.httpOptions); + getFile(uid, type, uuid = null) { + return this.http.post(this.path + 'getFile', {uid: uid, type: type, uuid: uuid}, 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) { return this.http.post(this.path + 'downloadFile', {fileNames: fileName, type: type, zip_mode: Array.isArray(fileName), outputName: outputName, fullPathProvided: fullPathProvided, subscriptionName: subscriptionName, - subPlaylist: subPlaylist + subPlaylist: subPlaylist, + uuid: uuid, + uid: uid }, {responseType: 'blob', params: this.httpOptions.params}); }