Added ability to add file to playlist using the context menu

This commit is contained in:
Isaac Abadi
2021-07-26 20:10:22 -07:00
parent 7174ef5f57
commit 92413bd360
10 changed files with 119 additions and 37 deletions

View File

@@ -754,20 +754,6 @@ function generateEnvVarConfigItem(key) {
return {key: key, value: process['env'][key]}; return {key: key, value: process['env'][key]};
} }
function getThumbnailMp3(name)
{
var obj = utils.getJSONMp3(name, audioFolderPath);
var thumbnailLink = obj.thumbnail;
return thumbnailLink;
}
function getThumbnailMp4(name)
{
var obj = utils.getJSONMp4(name, videoFolderPath);
var thumbnailLink = obj.thumbnail;
return thumbnailLink;
}
function getFileSizeMp3(name) function getFileSizeMp3(name)
{ {
var jsonPath = audioFolderPath+name+".mp3.info.json"; var jsonPath = audioFolderPath+name+".mp3.info.json";
@@ -1061,7 +1047,7 @@ async function downloadFileByURL_exec(url, type, options, sessionID = null) {
// create playlist // create playlist
const playlist_name = file_objs.map(file_obj => file_obj.title).join(', '); const playlist_name = file_objs.map(file_obj => file_obj.title).join(', ');
const duration = file_objs.reduce((a, b) => a + utils.durationStringToNumber(b.duration), 0); const duration = file_objs.reduce((a, b) => a + utils.durationStringToNumber(b.duration), 0);
container = await db_api.createPlaylist(playlist_name, file_objs.map(file_obj => file_obj.uid), type, file_objs[0]['thumbnailURL'], options.user); container = await db_api.createPlaylist(playlist_name, file_objs.map(file_obj => file_obj.uid), type, options.user);
} else if (file_objs.length === 1) { } else if (file_objs.length === 1) {
container = file_objs[0]; container = file_objs[0];
} else { } else {
@@ -2181,9 +2167,8 @@ app.post('/api/createPlaylist', optionalJwt, async (req, res) => {
let playlistName = req.body.playlistName; let playlistName = req.body.playlistName;
let uids = req.body.uids; let uids = req.body.uids;
let type = req.body.type; let type = req.body.type;
let thumbnailURL = req.body.thumbnailURL;
const new_playlist = await db_api.createPlaylist(playlistName, uids, type, thumbnailURL, req.isAuthenticated() ? req.user.uid : null); const new_playlist = await db_api.createPlaylist(playlistName, uids, type, req.isAuthenticated() ? req.user.uid : null);
res.send({ res.send({
new_playlist: new_playlist, new_playlist: new_playlist,
@@ -2216,8 +2201,18 @@ app.post('/api/getPlaylist', optionalJwt, async (req, res) => {
}); });
}); });
app.post('/api/getPlaylists', optionalJwt, async (req, res) => {
const uuid = req.isAuthenticated() ? req.user.uid : null;
const playlists = await db_api.getRecords('playlists', {user_uid: uuid});
res.send({
playlists: playlists
});
});
app.post('/api/updatePlaylistFiles', optionalJwt, async (req, res) => { app.post('/api/updatePlaylistFiles', optionalJwt, async (req, res) => {
let playlistID = req.body.playlistID; let playlistID = req.body.playlist_id;
let uids = req.body.uids; let uids = req.body.uids;
let success = false; let success = false;
@@ -2238,6 +2233,20 @@ app.post('/api/updatePlaylistFiles', optionalJwt, async (req, res) => {
}) })
}); });
app.post('/api/addFileToPlaylist', optionalJwt, async (req, res) => {
let playlist_id = req.body.playlist_id;
let file_uid = req.body.file_uid;
const playlist = await db_api.getRecord('playlists', {id: playlist_id});
playlist.uids.push(file_uid);
let success = await db_api.updatePlaylist(playlist);
res.send({
success: success
});
});
app.post('/api/updatePlaylist', optionalJwt, async (req, res) => { app.post('/api/updatePlaylist', optionalJwt, async (req, res) => {
let playlist = req.body.playlist; let playlist = req.body.playlist;
let success = await db_api.updatePlaylist(playlist, req.user && req.user.uid); let success = await db_api.updatePlaylist(playlist, req.user && req.user.uid);
@@ -2247,7 +2256,7 @@ app.post('/api/updatePlaylist', optionalJwt, async (req, res) => {
}); });
app.post('/api/deletePlaylist', optionalJwt, async (req, res) => { app.post('/api/deletePlaylist', optionalJwt, async (req, res) => {
let playlistID = req.body.playlistID; let playlistID = req.body.playlist_id;
let success = null; let success = null;
try { try {

View File

@@ -411,23 +411,26 @@ exports.addMetadataPropertyToDB = async (property_key) => {
} }
} }
exports.createPlaylist = async (playlist_name, uids, type, thumbnail_url, user_uid = null) => { exports.createPlaylist = async (playlist_name, uids, type, user_uid = null) => {
const first_video = await exports.getVideo(uids[0]);
const thumbnailToUse = first_video['thumbnailURL'];
let new_playlist = { let new_playlist = {
name: playlist_name, name: playlist_name,
uids: uids, uids: uids,
id: uuid(), id: uuid(),
thumbnailURL: thumbnail_url, thumbnailURL: thumbnailToUse,
type: type, type: type,
registered: Date.now(), registered: Date.now(),
randomize_order: false randomize_order: false
}; };
const duration = await exports.calculatePlaylistDuration(new_playlist, user_uid);
new_playlist.duration = duration;
new_playlist.user_uid = user_uid ? user_uid : undefined; new_playlist.user_uid = user_uid ? user_uid : undefined;
await exports.insertRecordIntoTable('playlists', new_playlist); await exports.insertRecordIntoTable('playlists', new_playlist);
const duration = await exports.calculatePlaylistDuration(new_playlist);
await exports.updateRecord('playlists', {id: new_playlist.id}, {duration: duration});
return new_playlist; return new_playlist;
} }
@@ -463,10 +466,10 @@ exports.getPlaylist = async (playlist_id, user_uid = null, require_sharing = fal
return playlist; return playlist;
} }
exports.updatePlaylist = async (playlist, user_uid = null) => { exports.updatePlaylist = async (playlist) => {
let playlistID = playlist.id; let playlistID = playlist.id;
const duration = await exports.calculatePlaylistDuration(playlist, user_uid); const duration = await exports.calculatePlaylistDuration(playlist);
playlist.duration = duration; playlist.duration = duration;
return await exports.updateRecord('playlists', {id: playlistID}, playlist); return await exports.updateRecord('playlists', {id: playlistID}, playlist);
@@ -486,12 +489,12 @@ exports.setPlaylistProperty = async (playlist_id, assignment_obj, user_uid = nul
return success; return success;
} }
exports.calculatePlaylistDuration = async (playlist, uuid, playlist_file_objs = null) => { exports.calculatePlaylistDuration = async (playlist, playlist_file_objs = null) => {
if (!playlist_file_objs) { if (!playlist_file_objs) {
playlist_file_objs = []; playlist_file_objs = [];
for (let i = 0; i < playlist['uids'].length; i++) { for (let i = 0; i < playlist['uids'].length; i++) {
const uid = playlist['uids'][i]; const uid = playlist['uids'][i];
const file_obj = await exports.getVideo(uid, uuid); const file_obj = await exports.getVideo(uid);
if (file_obj) playlist_file_objs.push(file_obj); if (file_obj) playlist_file_objs.push(file_obj);
} }
} }
@@ -588,7 +591,7 @@ exports.getVideoUIDByID = async (file_id, uuid = null) => {
return file_obj ? file_obj['uid'] : null; return file_obj ? file_obj['uid'] : null;
} }
exports.getVideo = async (file_uid, uuid = null, sub_id = null) => { exports.getVideo = async (file_uid) => {
return await exports.getRecord('files', {uid: file_uid}); return await exports.getRecord('files', {uid: file_uid});
} }

View File

@@ -24,10 +24,17 @@ export class CustomPlaylistsComponent implements OnInit {
this.getAllPlaylists(); this.getAllPlaylists();
} }
}); });
this.postsService.playlists_changed.subscribe(changed => {
if (changed) {
this.getAllPlaylists();
}
});
} }
getAllPlaylists() { getAllPlaylists() {
this.playlists_received = false; this.playlists_received = false;
// must call getAllFiles as we need to get category playlists as well
this.postsService.getAllFiles().subscribe(res => { this.postsService.getAllFiles().subscribe(res => {
this.playlists = res['playlists']; this.playlists = res['playlists'];
this.playlists_received = true; this.playlists_received = true;

View File

@@ -32,7 +32,7 @@
<div class="row justify-content-center"> <div class="row justify-content-center">
<ng-container *ngIf="normal_files_received && paged_data"> <ng-container *ngIf="normal_files_received && paged_data">
<div *ngFor="let file of paged_data; let i = index" class="mb-2 mt-2 d-flex justify-content-center" [ngClass]="[ postsService.card_size === 'small' ? 'col-2 small-col' : '', postsService.card_size === 'medium' ? 'col-6 col-lg-4 medium-col' : '', postsService.card_size === 'large' ? 'col-12 large-col' : '' ]"> <div *ngFor="let file of paged_data; let i = index" class="mb-2 mt-2 d-flex justify-content-center" [ngClass]="[ postsService.card_size === 'small' ? 'col-2 small-col' : '', postsService.card_size === 'medium' ? 'col-6 col-lg-4 medium-col' : '', postsService.card_size === 'large' ? 'col-12 large-col' : '' ]">
<app-unified-file-card [index]="i" [card_size]="postsService.card_size" [locale]="postsService.locale" (goToFile)="goToFile($event)" (goToSubscription)="goToSubscription($event)" [file_obj]="file" [use_youtubedl_archive]="postsService.config['Downloader']['use_youtubedl_archive']" [loading]="false" (deleteFile)="deleteFile($event)" [baseStreamPath]="postsService.path" [jwtString]="postsService.isLoggedIn ? '?jwt=' + this.postsService.token : ''"></app-unified-file-card> <app-unified-file-card [index]="i" [card_size]="postsService.card_size" [locale]="postsService.locale" (goToFile)="goToFile($event)" (goToSubscription)="goToSubscription($event)" [file_obj]="file" [use_youtubedl_archive]="postsService.config['Downloader']['use_youtubedl_archive']" [availablePlaylists]="playlists" (addFileToPlaylist)="addFileToPlaylist($event)" [loading]="false" (deleteFile)="deleteFile($event)" [baseStreamPath]="postsService.path" [jwtString]="postsService.isLoggedIn ? '?jwt=' + this.postsService.token : ''"></app-unified-file-card>
</div> </div>
<div *ngIf="filtered_files.length === 0"> <div *ngIf="filtered_files.length === 0">
<ng-container i18n="No videos found">No videos found.</ng-container> <ng-container i18n="No videos found">No videos found.</ng-container>

View File

@@ -50,6 +50,8 @@ export class RecentVideosComponent implements OnInit {
} }
}; };
filterProperty = this.filterProperties['upload_date']; filterProperty = this.filterProperties['upload_date'];
playlists = null;
pageSize = 10; pageSize = 10;
paged_data = null; paged_data = null;
@@ -68,14 +70,27 @@ export class RecentVideosComponent implements OnInit {
ngOnInit(): void { ngOnInit(): void {
if (this.postsService.initialized) { if (this.postsService.initialized) {
this.getAllFiles(); this.getAllFiles();
this.getAllPlaylists();
} }
this.postsService.service_initialized.subscribe(init => { this.postsService.service_initialized.subscribe(init => {
if (init) { if (init) {
this.getAllFiles(); this.getAllFiles();
this.getAllPlaylists();
} }
}); });
this.postsService.files_changed.subscribe(changed => {
if (changed) {
this.getAllFiles();
}
});
this.postsService.playlists_changed.subscribe(changed => {
if (changed) {
this.getAllPlaylists();
}
});
// set filter property to cached // set filter property to cached
const cached_filter_property = localStorage.getItem('filter_property'); const cached_filter_property = localStorage.getItem('filter_property');
@@ -84,6 +99,12 @@ export class RecentVideosComponent implements OnInit {
} }
} }
getAllPlaylists() {
this.postsService.getPlaylists().subscribe(res => {
this.playlists = res['playlists'];
});
}
// search // search
onSearchInputChanged(newvalue) { onSearchInputChanged(newvalue) {
@@ -288,6 +309,23 @@ export class RecentVideosComponent implements OnInit {
this.filterByProperty(this.filterProperty['property']); this.filterByProperty(this.filterProperty['property']);
} }
addFileToPlaylist(info_obj) {
const file = info_obj['file'];
const playlist_id = info_obj['playlist_id'];
const playlist = this.playlists.find(potential_playlist => potential_playlist['id'] === playlist_id);
this.postsService.addFileToPlaylist(playlist_id, file['uid']).subscribe(res => {
if (res['success']) {
this.postsService.openSnackBar(`Successfully added ${file.title} to ${playlist.title}!`);
this.postsService.playlists_changed.next(true);
} else {
this.postsService.openSnackBar(`Failed to add ${file.title} to ${playlist.title}! Unknown error.`);
}
}, err => {
console.error(err);
this.postsService.openSnackBar(`Failed to add ${file.title} to ${playlist.title}! See browser console for error.`);
});
}
// sorting and filtering // sorting and filtering
sortFiles(a, b) { sortFiles(a, b) {

View File

@@ -23,6 +23,10 @@
<ng-container *ngIf="!is_playlist && !loading"> <ng-container *ngIf="!is_playlist && !loading">
<button (click)="openFileInfoDialog()" mat-menu-item><mat-icon>info</mat-icon><ng-container i18n="Video info button">Info</ng-container></button> <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>&nbsp;<ng-container i18n="Go to subscription menu item">Go to subscription</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>&nbsp;<ng-container i18n="Go to subscription menu item">Go to subscription</ng-container></button>
<button *ngIf="availablePlaylists" [matMenuTriggerFor]="addtoplaylist" mat-menu-item><mat-icon>playlist_add</mat-icon>&nbsp;<ng-container i18n="Add to playlist menu item">Add to playlist</ng-container></button>
<mat-menu #addtoplaylist="matMenu">
<button *ngFor="let playlist of availablePlaylists" [disabled]="playlist.uids?.includes(file_obj.uid)" (click)="emitAddFileToPlaylist(playlist.id)" mat-menu-item>{{playlist.name}}</button>
</mat-menu>
<mat-divider></mat-divider> <mat-divider></mat-divider>
<button *ngIf="file_obj.sub_id" (click)="emitDeleteFile()" mat-menu-item> <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> <mat-icon>restore</mat-icon><ng-container i18n="Delete and redownload subscription video button">Delete and redownload</ng-container>

View File

@@ -46,9 +46,11 @@ export class UnifiedFileCardComponent implements OnInit {
@Input() locale = null; @Input() locale = null;
@Input() baseStreamPath = null; @Input() baseStreamPath = null;
@Input() jwtString = null; @Input() jwtString = null;
@Input() availablePlaylists = null;
@Output() goToFile = new EventEmitter<any>(); @Output() goToFile = new EventEmitter<any>();
@Output() goToSubscription = new EventEmitter<any>(); @Output() goToSubscription = new EventEmitter<any>();
@Output() deleteFile = new EventEmitter<any>(); @Output() deleteFile = new EventEmitter<any>();
@Output() addFileToPlaylist = new EventEmitter<any>();
@Output() editPlaylist = new EventEmitter<any>(); @Output() editPlaylist = new EventEmitter<any>();
@@ -86,6 +88,13 @@ export class UnifiedFileCardComponent implements OnInit {
}); });
} }
emitAddFileToPlaylist(playlist_id) {
this.addFileToPlaylist.emit({
file: this.file_obj,
playlist_id: playlist_id
});
}
navigateToFile(event) { navigateToFile(event) {
this.goToFile.emit({file: this.file_obj, event: event}); this.goToFile.emit({file: this.file_obj, event: event});
} }

View File

@@ -57,6 +57,7 @@ export class ModifyPlaylistComponent implements OnInit {
this.playlist_updated = true; this.playlist_updated = true;
this.postsService.openSnackBar('Playlist updated successfully.'); this.postsService.openSnackBar('Playlist updated successfully.');
this.getPlaylist(); this.getPlaylist();
this.postsService.playlists_changed.next(true);
}); });
} }

View File

@@ -951,8 +951,6 @@ export class MainComponent implements OnInit {
} }
reloadRecentVideos() { reloadRecentVideos() {
if (this.recentVideos) { this.postsService.files_changed.next(true);
this.recentVideos.getAllFiles();
}
} }
} }

View File

@@ -48,6 +48,9 @@ export class PostsService implements CanActivate {
settings_changed = new BehaviorSubject<boolean>(false); settings_changed = new BehaviorSubject<boolean>(false);
open_create_default_admin_dialog = new BehaviorSubject<boolean>(false); open_create_default_admin_dialog = new BehaviorSubject<boolean>(false);
files_changed = new BehaviorSubject<boolean>(false);
playlists_changed = new BehaviorSubject<boolean>(false);
// app status // app status
initialized = false; initialized = false;
@@ -334,18 +337,28 @@ export class PostsService implements CanActivate {
include_file_metadata: include_file_metadata}, this.httpOptions); include_file_metadata: include_file_metadata}, this.httpOptions);
} }
getPlaylists() {
return this.http.post(this.path + 'getPlaylists', {}, this.httpOptions);
}
updatePlaylist(playlist) { updatePlaylist(playlist) {
return this.http.post(this.path + 'updatePlaylist', {playlist: playlist}, this.httpOptions); return this.http.post(this.path + 'updatePlaylist', {playlist: playlist}, this.httpOptions);
} }
updatePlaylistFiles(playlistID, fileNames, type) { updatePlaylistFiles(playlist_id, fileNames, type) {
return this.http.post(this.path + 'updatePlaylistFiles', {playlistID: playlistID, return this.http.post(this.path + 'updatePlaylistFiles', {playlist_id: playlist_id,
fileNames: fileNames, fileNames: fileNames,
type: type}, this.httpOptions); type: type}, this.httpOptions);
} }
removePlaylist(playlistID, type) { addFileToPlaylist(playlist_id, file_uid) {
return this.http.post(this.path + 'deletePlaylist', {playlistID: playlistID, type: type}, this.httpOptions); return this.http.post(this.path + 'addFileToPlaylist', {playlist_id: playlist_id,
file_uid: file_uid},
this.httpOptions);
}
removePlaylist(playlist_id, type) {
return this.http.post(this.path + 'deletePlaylist', {playlist_id: playlist_id, type: type}, this.httpOptions);
} }
// categories // categories