Updated video playing/sharing logic to support sharing of playlists in multi user mode and when multi user mode is disabled

Fixed bug that caused normal archive to be used in multi-user mode

Updated login logic when username is not found or user file is missing

Fixed bug that prevented playlist sharing from working

Added ability to use timestamps when sharing videos
pull/82/head
Tzahi12345 5 years ago
parent 8bc99fb557
commit 4e6d68d9e6

@ -1762,8 +1762,14 @@ const optionalJwt = function (req, res, next) {
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();
const is_shared = !req.query.id ? auth_api.getUserVideo(uuid, uid, type, true) : auth_api.getUserPlaylist(uuid, req.query.id, null, true);
if (is_shared) {
req.can_watch = true;
return next();
} else {
res.sendStatus(401);
return;
}
} else if (multiUserMode && !(req.path.includes('/api/auth/register') && !req.query.jwt)) { // registration should get passed through
if (!req.query.jwt) {
res.sendStatus(401);
@ -1983,7 +1989,6 @@ app.post('/api/enableSharing', optionalJwt, function(req, res) {
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;
}
@ -2262,11 +2267,12 @@ app.post('/api/createPlaylist', optionalJwt, async (req, res) => {
app.post('/api/getPlaylist', optionalJwt, async (req, res) => {
let playlistID = req.body.playlistID;
let type = req.body.type;
let uuid = req.body.uuid;
let playlist = null;
if (req.isAuthenticated()) {
playlist = auth_api.getUserPlaylist(req.user.uid, playlistID, type);
playlist = auth_api.getUserPlaylist(uuid ? uuid : req.user.uid, playlistID, type);
type = playlist.type;
} else {
if (!type) {
@ -2563,13 +2569,13 @@ app.get('/api/video/:id', optionalJwt, function(req , res){
let optionalParams = url_api.parse(req.url,true).query;
let id = decodeURIComponent(req.params.id);
let file_path = videoFolderPath + id + '.mp4';
if (req.isAuthenticated()) {
if (req.isAuthenticated() || req.can_watch) {
let usersFileFolder = config_api.getConfigItem('ytdl_users_base_path');
if (optionalParams['subName']) {
const isPlaylist = optionalParams['subPlaylist'];
file_path = path.join(usersFileFolder, req.user.uid, 'subscriptions', (isPlaylist === 'true' ? 'playlists/' : 'channels/'),optionalParams['subName'], id + '.mp4')
} else {
file_path = path.join(usersFileFolder, req.user.uid, 'video', id + '.mp4');
file_path = path.join(usersFileFolder, req.query.uuid ? req.query.uuid : req.user.uid, 'video', id + '.mp4');
}
} else if (optionalParams['subName']) {
let basePath = config_api.getConfigItem('ytdl_subscriptions_base_path');

@ -157,7 +157,7 @@ exports.passport.use(new LocalStrategy({
passwordField: 'password'},
function(username, password, done) {
const user = users_db.get('users').find({name: username}).value();
if (!user) { console.log('user not found'); return done(null, false); }
if (!user) { logger.error(`User ${username} not found`); return done(null, false); }
if (user) {
return done(null, bcrypt.compareSync(password, user.passhash) ? user : false);
}
@ -346,7 +346,7 @@ exports.getUserPlaylists = function(user_uid, type) {
return user['playlists'][type];
}
exports.getUserPlaylist = function(user_uid, playlistID, type) {
exports.getUserPlaylist = function(user_uid, playlistID, type, requireSharing = false) {
let playlist = null;
if (!type) {
playlist = users_db.get('users').find({uid: user_uid}).get(`playlists.audio`).find({id: playlistID}).value();
@ -358,6 +358,10 @@ exports.getUserPlaylist = function(user_uid, playlistID, type) {
}
}
if (!playlist) playlist = users_db.get('users').find({uid: user_uid}).get(`playlists.${type}`).find({id: playlistID}).value();
// prevent unauthorized users from accessing the file info
if (requireSharing && !playlist['sharingEnabled']) playlist = null;
return playlist;
}
@ -434,7 +438,7 @@ exports.deleteUserFile = function(user_uid, file_uid, type, blacklistMode = fals
success = true;
} else {
success = false;
console.log('file does not exist!');
logger.warn(`User file ${file_uid} does not exist!`);
}
return success;
@ -444,7 +448,7 @@ exports.changeSharingMode = function(user_uid, file_uid, type, is_playlist, enab
let success = false;
const user_db_obj = users_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});
const file_db_obj = is_playlist ? user_db_obj.get(`playlists.${type}`).find({id: 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();

@ -49,7 +49,7 @@ async function subscribe(sub, user_uid = null) {
else
db.get('subscriptions').push(sub).write();
let success = await getSubscriptionInfo(sub);
let success = await getSubscriptionInfo(sub, user_uid);
result_obj.success = success;
result_obj.sub = sub;
getVideosForSub(sub, user_uid);

@ -9,6 +9,12 @@
<div>
<mat-checkbox [checked]="sharing_enabled" (change)="sharingChanged($event)"><ng-container i18n="Enable sharing checkbox">Enable sharing</ng-container></mat-checkbox>
</div>
<div>
<mat-checkbox style="margin-right: 15px;" (change)="useTimestampChanged()" [(ngModel)]="timestamp_enabled">Use timestamp</mat-checkbox>
<mat-form-field>
<input matInput type="number" [(ngModel)]="current_timestamp" [disabled]="!timestamp_enabled" (change)="timestampInputChanged($event)" placeholder="Seconds" i18n-placeholder="Seconds">
</mat-form-field>
</div>
<div>
<mat-form-field style="width: 100%">
<input matInput [disabled]="!sharing_enabled" [readonly]="true" [value]="share_url">

@ -15,8 +15,11 @@ export class ShareMediaDialogComponent implements OnInit {
uid = null;
uuid = null;
share_url = null;
default_share_url = null;
sharing_enabled = null;
is_playlist = null;
current_timestamp = null
timestamp_enabled = false;
constructor(@Inject(MAT_DIALOG_DATA) public data: any, public router: Router, private snackBar: MatSnackBar,
private postsService: PostsService) { }
@ -28,15 +31,34 @@ export class ShareMediaDialogComponent implements OnInit {
this.uuid = this.data.uuid;
this.sharing_enabled = this.data.sharing_enabled;
this.is_playlist = this.data.is_playlist;
this.current_timestamp = (this.data.current_timestamp / 1000).toFixed(2);
const arg = (this.is_playlist ? ';id=' : ';uid=');
this.share_url = window.location.href.split(';')[0] + arg + this.uid;
this.default_share_url = window.location.href.split(';')[0] + arg + this.uid;
if (this.uuid) {
this.share_url += ';uuid=' + this.uuid;
this.default_share_url += ';uuid=' + this.uuid;
}
this.share_url = this.default_share_url;
}
}
timestampInputChanged(change) {
if (!this.timestamp_enabled) {
this.share_url = this.default_share_url;
return;
}
const new_val = change.target.value;
if (new_val > 0) {
this.share_url = this.default_share_url + ';timestamp=' + new_val;
} else {
this.share_url = this.default_share_url;
}
}
useTimestampChanged() {
this.timestampInputChanged({target: {value: this.current_timestamp}})
}
copiedToClipboard() {
this.openSnackBar('Copied to clipboard!');
}

@ -491,7 +491,7 @@ export class MainComponent implements OnInit {
if (is_playlist) {
this.router.navigate(['/player', {fileNames: name.join('|nvr|'), type: 'audio'}]);
} else {
this.router.navigate(['/player', {fileNames: name, type: 'audio', uid: uid}]);
this.router.navigate(['/player', {type: 'audio', uid: uid}]);
}
}
}
@ -528,7 +528,7 @@ export class MainComponent implements OnInit {
if (is_playlist) {
this.router.navigate(['/player', {fileNames: name.join('|nvr|'), type: 'video'}]);
} else {
this.router.navigate(['/player', {fileNames: name, type: 'video', uid: uid}]);
this.router.navigate(['/player', {type: 'video', uid: uid}]);
}
}
}

@ -40,6 +40,7 @@ export class PlayerComponent implements OnInit {
subscriptionName = null;
subPlaylist = null;
uuid = null; // used for sharing in multi-user mode, uuid is the user that downloaded the video
timestamp = null;
is_shared = false;
@ -77,6 +78,7 @@ export class PlayerComponent implements OnInit {
this.url = this.route.snapshot.paramMap.get('url');
this.name = this.route.snapshot.paramMap.get('name');
this.uuid = this.route.snapshot.paramMap.get('uuid');
this.timestamp = this.route.snapshot.paramMap.get('timestamp');
// loading config
if (this.postsService.initialized) {
@ -102,7 +104,7 @@ export class PlayerComponent implements OnInit {
this.subscriptionFolderPath = this.postsService.config['Subscriptions']['subscriptions_base_path'];
this.fileNames = this.route.snapshot.paramMap.get('fileNames') ? this.route.snapshot.paramMap.get('fileNames').split('|nvr|') : null;
if (!this.fileNames) {
if (!this.fileNames && !this.type) {
this.is_shared = true;
}
@ -149,7 +151,7 @@ export class PlayerComponent implements OnInit {
if (!already_has_filenames) { this.parseFileNames(); }
}
}
if (this.db_file['sharingEnabled']) {
if (this.db_file['sharingEnabled'] || !this.uuid) {
this.show_player = true;
} else if (!already_has_filenames) {
this.openSnackBar('Error: Sharing has been disabled for this video!', 'Dismiss');
@ -158,12 +160,18 @@ export class PlayerComponent implements OnInit {
}
getPlaylistFiles() {
this.postsService.getPlaylist(this.id, null).subscribe(res => {
this.db_playlist = res['playlist'];
this.fileNames = this.db_playlist['fileNames'];
this.type = res['type'];
this.show_player = true;
this.parseFileNames();
this.postsService.getPlaylist(this.id, null, this.uuid).subscribe(res => {
if (res['playlist']) {
this.db_playlist = res['playlist'];
this.fileNames = this.db_playlist['fileNames'];
this.type = res['type'];
this.show_player = true;
this.parseFileNames();
} else {
this.openSnackBar('Failed to load playlist!', '');
}
}, err => {
this.openSnackBar('Failed to load playlist!', '');
});
}
@ -196,11 +204,15 @@ export class PlayerComponent implements OnInit {
}
// adds user token if in multi-user-mode
const uuid_str = this.uuid ? `&uuid=${this.uuid}` : '';
const uid_str = (this.id || !this.db_file) ? '' : `&uid=${this.db_file.uid}`;
const type_str = (this.id || !this.db_file) ? '' : `&type=${this.db_file.type}`
const id_str = this.id ? `&id=${this.id}` : '';
if (this.postsService.isLoggedIn) {
fullLocation += (this.subscriptionName ? '&' : '?') + `jwt=${this.postsService.token}`;
if (this.is_shared) { fullLocation += `&uuid=${this.uuid}&uid=${this.db_file.uid}&type=${this.db_file.type}`; }
if (this.is_shared) { fullLocation += `${uuid_str}${uid_str}${type_str}${id_str}`; }
} else if (this.is_shared) {
fullLocation += (this.subscriptionName ? '&' : '?') + `uuid=${this.uuid}&uid=${this.db_file.uid}&type=${this.db_file.type}`;
fullLocation += (this.subscriptionName ? '&' : '?') + `test=test${uuid_str}${uid_str}${type_str}${id_str}`;
}
// if it has a slash (meaning it's in a directory), only get the file name for the label
let label = null;
@ -228,6 +240,10 @@ export class PlayerComponent implements OnInit {
this.api.getDefaultMedia().subscriptions.loadedMetadata.subscribe(this.playVideo.bind(this));
this.api.getDefaultMedia().subscriptions.ended.subscribe(this.nextVideo.bind(this));
if (this.timestamp) {
this.api.seekTime(+this.timestamp);
}
}
nextVideo() {
@ -381,7 +397,8 @@ export class PlayerComponent implements OnInit {
type: this.type,
sharing_enabled: this.id ? this.db_playlist.sharingEnabled : this.db_file.sharingEnabled,
is_playlist: !!this.id,
uuid: this.postsService.isLoggedIn ? this.postsService.user.uid : null
uuid: this.postsService.isLoggedIn ? this.postsService.user.uid : null,
current_timestamp: this.api.time.current
},
width: '60vw'
});

@ -258,9 +258,9 @@ export class PostsService implements CanActivate {
thumbnailURL: thumbnailURL}, this.httpOptions);
}
getPlaylist(playlistID, type) {
getPlaylist(playlistID, type, uuid = null) {
return this.http.post(this.path + 'getPlaylist', {playlistID: playlistID,
type: type}, this.httpOptions);
type: type, uuid: uuid}, this.httpOptions);
}
updatePlaylist(playlistID, fileNames, type) {
@ -335,6 +335,7 @@ export class PostsService implements CanActivate {
this.permissions = permissions;
this.available_permissions = available_permissions;
this.token = token;
this.setInitialized();
localStorage.setItem('jwt_token', this.token);
@ -365,7 +366,6 @@ export class PostsService implements CanActivate {
call.subscribe(res => {
if (res['token']) {
this.afterLogin(res['user'], res['token'], res['permissions'], res['available_permissions']);
this.setInitialized();
}
}, err => {
if (err.status === 401) {

Loading…
Cancel
Save