From 690cc38899e2e3dff3a077e500b492660870e1d6 Mon Sep 17 00:00:00 2001 From: Isaac Abadi Date: Sun, 19 Jun 2022 23:09:30 -0400 Subject: [PATCH] Updated playlist file selection to use recent videos component Playlists are now file type agnostic Updated translations --- Public API v1.yaml | 14 ++-- backend/app.js | 4 +- backend/db.js | 3 +- backend/subscriptions.js | 2 +- src/api-types/models/CreatePlaylistRequest.ts | 3 - src/api-types/models/DeletePlaylistRequest.ts | 3 - src/api-types/models/GetPlaylistResponse.ts | 7 +- .../custom-playlists.component.ts | 21 +++--- .../recent-videos.component.html | 30 +++++++- .../recent-videos.component.scss | 10 +++ .../recent-videos/recent-videos.component.ts | 28 ++++++- .../unified-file-card.component.html | 2 +- .../create-playlist.component.html | 26 +------ .../create-playlist.component.ts | 54 ++++---------- .../modify-playlist.component.ts | 12 +-- src/app/main/main.component.html | 2 +- src/app/main/main.component.ts | 15 +--- src/app/posts.services.ts | 9 +-- src/assets/i18n/messages.en.xlf | 74 ++----------------- 19 files changed, 121 insertions(+), 198 deletions(-) diff --git a/Public API v1.yaml b/Public API v1.yaml index 0931cf6..464871f 100644 --- a/Public API v1.yaml +++ b/Public API v1.yaml @@ -1939,7 +1939,6 @@ components: - uids - playlistName - thumbnailURL - - type type: object properties: playlistName: @@ -1948,8 +1947,6 @@ components: type: array items: type: string - type: - $ref: '#/components/schemas/FileType' thumbnailURL: type: string CreatePlaylistResponse: @@ -1979,15 +1976,17 @@ components: required: - playlist - success - - type type: object properties: playlist: $ref: '#/components/schemas/Playlist' - type: - $ref: '#/components/schemas/FileType' success: type: boolean + file_objs: + type: array + description: File objects for every uid in the playlist's uids property, in the same order + items: + $ref: '#/components/schemas/DatabaseFile' GetPlaylistsRequest: type: object properties: @@ -2012,13 +2011,10 @@ components: DeletePlaylistRequest: required: - playlist_id - - type type: object properties: playlist_id: type: string - type: - $ref: '#/components/schemas/FileType' DownloadFileRequest: type: object properties: diff --git a/backend/app.js b/backend/app.js index 4c57139..322bb44 100644 --- a/backend/app.js +++ b/backend/app.js @@ -1339,9 +1339,8 @@ app.post('/api/getSubscriptions', optionalJwt, async (req, res) => { app.post('/api/createPlaylist', optionalJwt, async (req, res) => { let playlistName = req.body.playlistName; let uids = req.body.uids; - let type = req.body.type; - const new_playlist = await db_api.createPlaylist(playlistName, uids, type, req.isAuthenticated() ? req.user.uid : null); + const new_playlist = await db_api.createPlaylist(playlistName, uids, req.isAuthenticated() ? req.user.uid : null); res.send({ new_playlist: new_playlist, @@ -1369,7 +1368,6 @@ app.post('/api/getPlaylist', optionalJwt, async (req, res) => { res.send({ playlist: playlist, file_objs: file_objs, - type: playlist && playlist.type, success: !!playlist }); }); diff --git a/backend/db.js b/backend/db.js index 73f56f1..e35ddca 100644 --- a/backend/db.js +++ b/backend/db.js @@ -357,7 +357,7 @@ exports.addMetadataPropertyToDB = async (property_key) => { } } -exports.createPlaylist = async (playlist_name, uids, type, user_uid = null) => { +exports.createPlaylist = async (playlist_name, uids, user_uid = null) => { const first_video = await exports.getVideo(uids[0]); const thumbnailToUse = first_video['thumbnailURL']; @@ -366,7 +366,6 @@ exports.createPlaylist = async (playlist_name, uids, type, user_uid = null) => { uids: uids, id: uuid(), thumbnailURL: thumbnailToUse, - type: type, registered: Date.now(), randomize_order: false }; diff --git a/backend/subscriptions.js b/backend/subscriptions.js index e14f121..bad2064 100644 --- a/backend/subscriptions.js +++ b/backend/subscriptions.js @@ -178,7 +178,7 @@ async function deleteSubscriptionFile(sub, file, deleteForever, file_uid = null, ]); if (jsonExists) { - retrievedID = JSON.parse(await fs.readFile(jsonPath, 'utf8'))['id']; + retrievedID = fs.readJSONSync(jsonPath)['id']; await fs.unlink(jsonPath); } diff --git a/src/api-types/models/CreatePlaylistRequest.ts b/src/api-types/models/CreatePlaylistRequest.ts index c747d8d..e61a2a0 100644 --- a/src/api-types/models/CreatePlaylistRequest.ts +++ b/src/api-types/models/CreatePlaylistRequest.ts @@ -2,11 +2,8 @@ /* tslint:disable */ /* eslint-disable */ -import type { FileType } from './FileType'; - export type CreatePlaylistRequest = { playlistName: string; uids: Array; - type: FileType; thumbnailURL: string; }; \ No newline at end of file diff --git a/src/api-types/models/DeletePlaylistRequest.ts b/src/api-types/models/DeletePlaylistRequest.ts index 07cf9f7..82e25b2 100644 --- a/src/api-types/models/DeletePlaylistRequest.ts +++ b/src/api-types/models/DeletePlaylistRequest.ts @@ -2,9 +2,6 @@ /* tslint:disable */ /* eslint-disable */ -import type { FileType } from './FileType'; - export type DeletePlaylistRequest = { playlist_id: string; - type: FileType; }; \ No newline at end of file diff --git a/src/api-types/models/GetPlaylistResponse.ts b/src/api-types/models/GetPlaylistResponse.ts index daca2d4..14ff0c0 100644 --- a/src/api-types/models/GetPlaylistResponse.ts +++ b/src/api-types/models/GetPlaylistResponse.ts @@ -2,11 +2,14 @@ /* tslint:disable */ /* eslint-disable */ -import type { FileType } from './FileType'; +import type { DatabaseFile } from './DatabaseFile'; import type { Playlist } from './Playlist'; export type GetPlaylistResponse = { playlist: Playlist; - type: FileType; success: boolean; + /** + * File objects for every uid in the playlist's uids property, in the same order + */ + file_objs?: Array; }; \ No newline at end of file diff --git a/src/app/components/custom-playlists/custom-playlists.component.ts b/src/app/components/custom-playlists/custom-playlists.component.ts index e659724..86a76ee 100644 --- a/src/app/components/custom-playlists/custom-playlists.component.ts +++ b/src/app/components/custom-playlists/custom-playlists.component.ts @@ -4,6 +4,7 @@ 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'; +import { Playlist } from 'api-types'; @Component({ selector: 'app-custom-playlists', @@ -32,7 +33,7 @@ export class CustomPlaylistsComponent implements OnInit { }); } - getAllPlaylists() { + getAllPlaylists(): void { this.playlists_received = false; // must call getAllFiles as we need to get category playlists as well this.postsService.getPlaylists(true).subscribe(res => { @@ -42,10 +43,10 @@ export class CustomPlaylistsComponent implements OnInit { } // creating a playlist - openCreatePlaylistDialog() { + openCreatePlaylistDialog(): void { const dialogRef = this.dialog.open(CreatePlaylistComponent, { - data: { - } + minWidth: '90vw', + minHeight: '95vh' }); dialogRef.afterClosed().subscribe(result => { if (result) { @@ -57,7 +58,7 @@ export class CustomPlaylistsComponent implements OnInit { }); } - goToPlaylist(info_obj) { + goToPlaylist(info_obj: { file: Playlist; }): void { const playlist = info_obj.file; const playlistID = playlist.id; @@ -76,7 +77,7 @@ export class CustomPlaylistsComponent implements OnInit { } } - downloadPlaylist(playlist_id, playlist_name) { + downloadPlaylist(playlist_id: string, playlist_name: string): void { this.downloading_content[playlist_id] = true; this.postsService.downloadPlaylistFromServer(playlist_id).subscribe(res => { this.downloading_content[playlist_id] = false; @@ -86,11 +87,11 @@ export class CustomPlaylistsComponent implements OnInit { } - deletePlaylist(args) { + deletePlaylist(args: { file: Playlist; index: number; }): void { const playlist = args.file; const index = args.index; const playlistID = playlist.id; - this.postsService.removePlaylist(playlistID, playlist.type).subscribe(res => { + this.postsService.removePlaylist(playlistID).subscribe(res => { if (res['success']) { this.playlists.splice(index, 1); this.postsService.openSnackBar('Playlist successfully removed.', ''); @@ -99,7 +100,7 @@ export class CustomPlaylistsComponent implements OnInit { }); } - editPlaylistDialog(args) { + editPlaylistDialog(args: { playlist: Playlist; index: number; }): void { const playlist = args.playlist; const index = args.index; const dialogRef = this.dialog.open(ModifyPlaylistComponent, { @@ -109,7 +110,7 @@ export class CustomPlaylistsComponent implements OnInit { } }); - dialogRef.afterClosed().subscribe(res => { + dialogRef.afterClosed().subscribe(() => { // updates playlist in file manager if it changed if (dialogRef.componentInstance.playlist_updated) { this.playlists[index] = dialogRef.componentInstance.original_playlist; diff --git a/src/app/components/recent-videos/recent-videos.component.html b/src/app/components/recent-videos/recent-videos.component.html index 1f83685..5603378 100644 --- a/src/app/components/recent-videos/recent-videos.component.html +++ b/src/app/components/recent-videos/recent-videos.component.html @@ -17,7 +17,8 @@
-

My files

+

My files

+

{{customHeader}}

@@ -28,7 +29,7 @@
-
+
@@ -46,6 +47,31 @@
+
+ + +
+
+
+ {{(file.type === 'audio' || file.isAudio) ? 'audiotrack' : 'movie'}} + {{file.title}} +
+
{{file.registered | date:'shortDate'}}
+
+
+ +
+
+ + + + + + + + +
+
diff --git a/src/app/components/recent-videos/recent-videos.component.scss b/src/app/components/recent-videos/recent-videos.component.scss index 49023ec..b9fd2fb 100644 --- a/src/app/components/recent-videos/recent-videos.component.scss +++ b/src/app/components/recent-videos/recent-videos.component.scss @@ -61,4 +61,14 @@ .my-videos-title { top: 0px; } +} + +.list-ghosts { + position: relative; + top: 4px; +} + +.audio-video-icon { + position: relative; + top: 6px; } \ No newline at end of file diff --git a/src/app/components/recent-videos/recent-videos.component.ts b/src/app/components/recent-videos/recent-videos.component.ts index 11f8713..7437524 100644 --- a/src/app/components/recent-videos/recent-videos.component.ts +++ b/src/app/components/recent-videos/recent-videos.component.ts @@ -1,7 +1,7 @@ -import { Component, Input, OnInit, ViewChild } from '@angular/core'; +import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core'; import { PostsService } from 'app/posts.services'; import { Router } from '@angular/router'; -import { FileType, FileTypeFilter } from '../../../api-types'; +import { DatabaseFile, FileType, FileTypeFilter } from '../../../api-types'; import { MatPaginator } from '@angular/material/paginator'; import { Subject } from 'rxjs'; import { distinctUntilChanged } from 'rxjs/operators'; @@ -14,7 +14,10 @@ import { distinctUntilChanged } from 'rxjs/operators'; export class RecentVideosComponent implements OnInit { @Input() usePaginator = true; + @Input() selectMode = false; @Input() sub_id = null; + @Input() customHeader = null; + @Output() fileSelectionEmitter = new EventEmitter<{new_selection: string[], thumbnailURL: string}>(); cached_file_count = 0; loading_files = null; @@ -61,7 +64,9 @@ export class RecentVideosComponent implements OnInit { playlists = null; pageSize = 10; - paged_data = null; + paged_data: DatabaseFile[] = null; + + selected_data: string[] = []; @ViewChild('paginator') paginator: MatPaginator @@ -358,4 +363,21 @@ export class RecentVideosComponent implements OnInit { this.loading_files = Array(this.pageSize).fill(0); this.getAllFiles(); } + + fileSelectionChanged(event): void { + const adding = event.option._selected; + const value = event.option.value; + if (adding) + this.selected_data.push(value); + else + this.selected_data = this.selected_data.filter(e => e !== value); + + let thumbnail_url = null; + if (this.selected_data.length) { + const file_obj = this.paged_data.find(file => file.uid === this.selected_data[0]); + if (file_obj) { thumbnail_url = file_obj['thumbnailURL'] } + } + + this.fileSelectionEmitter.emit({new_selection: this.selected_data, thumbnailURL: thumbnail_url}); + } } diff --git a/src/app/components/unified-file-card/unified-file-card.component.html b/src/app/components/unified-file-card/unified-file-card.component.html index 1fef6d8..b14f9cd 100644 --- a/src/app/components/unified-file-card/unified-file-card.component.html +++ b/src/app/components/unified-file-card/unified-file-card.component.html @@ -23,7 +23,7 @@ - + diff --git a/src/app/create-playlist/create-playlist.component.html b/src/app/create-playlist/create-playlist.component.html index ad21ec0..d82c23e 100644 --- a/src/app/create-playlist/create-playlist.component.html +++ b/src/app/create-playlist/create-playlist.component.html @@ -1,34 +1,12 @@

Create a playlist

-
+
-
- - - Audio - Video - - -
-
- - Audio files - Videos - - {{file.id}} - {{file.id}} - {{file.id}} - - - -
- No files available. -
-
+
diff --git a/src/app/create-playlist/create-playlist.component.ts b/src/app/create-playlist/create-playlist.component.ts index c22d32d..6a5e308 100644 --- a/src/app/create-playlist/create-playlist.component.ts +++ b/src/app/create-playlist/create-playlist.component.ts @@ -17,42 +17,20 @@ export class CreatePlaylistComponent implements OnInit { audiosToSelectFrom = null; videosToSelectFrom = null; name = ''; + cached_thumbnail_url = null; create_in_progress = false; - constructor(@Inject(MAT_DIALOG_DATA) public data: any, - private postsService: PostsService, + constructor(private postsService: PostsService, public dialogRef: MatDialogRef) { } - ngOnInit() { - if (this.data) { - this.filesToSelectFrom = this.data.filesToSelectFrom; - this.type = this.data.type; - } + ngOnInit(): void {} - 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(): void { const thumbnailURL = this.getThumbnailURL(); 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, thumbnailURL).subscribe(res => { this.create_in_progress = false; if (res['success']) { this.dialogRef.close(true); @@ -62,19 +40,13 @@ export class CreatePlaylistComponent implements OnInit { }); } - getThumbnailURL() { - 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 === this.filesSelect.value[0]) { - // different services store the thumbnail in different places - if (file.thumbnailURL) { return file.thumbnailURL }; - if (file.thumbnail) { return file.thumbnail }; - } - } - return null; + getThumbnailURL(): string { + return this.cached_thumbnail_url; + } + + fileSelectionChanged({new_selection, thumbnailURL}: {new_selection: string[], thumbnailURL: string}): void { + this.filesSelect.setValue(new_selection); + if (new_selection.length) this.cached_thumbnail_url = thumbnailURL; + else this.cached_thumbnail_url = null; } } diff --git a/src/app/dialogs/modify-playlist/modify-playlist.component.ts b/src/app/dialogs/modify-playlist/modify-playlist.component.ts index 18a0f2a..17b2afd 100644 --- a/src/app/dialogs/modify-playlist/modify-playlist.component.ts +++ b/src/app/dialogs/modify-playlist/modify-playlist.component.ts @@ -35,15 +35,9 @@ export class ModifyPlaylistComponent implements OnInit { } getFiles() { - if (this.playlist.type === 'audio') { - this.postsService.getMp3s().subscribe(res => { - this.processFiles(res['mp3s']); - }); - } else { - this.postsService.getMp4s().subscribe(res => { - this.processFiles(res['mp4s']); - }); - } + this.postsService.getAllFiles().subscribe(res => { + this.processFiles(res['files']); + }); } processFiles(new_files = null) { diff --git a/src/app/main/main.component.html b/src/app/main/main.component.html index e8d14a8..84f7e7f 100644 --- a/src/app/main/main.component.html +++ b/src/app/main/main.component.html @@ -60,7 +60,7 @@

- + Only Audio diff --git a/src/app/main/main.component.ts b/src/app/main/main.component.ts index 69e5eb7..25719ad 100644 --- a/src/app/main/main.component.ts +++ b/src/app/main/main.component.ts @@ -10,12 +10,7 @@ import { Router, ActivatedRoute } from '@angular/router'; import { Platform } from '@angular/cdk/platform'; import { ArgModifierDialogComponent } from 'app/dialogs/arg-modifier-dialog/arg-modifier-dialog.component'; import { RecentVideosComponent } from 'app/components/recent-videos/recent-videos.component'; -import { Download, FileType } from 'api-types'; - -export let audioFilesMouseHovering = false; -export let videoFilesMouseHovering = false; -export let audioFilesOpened = false; -export let videoFilesOpened = false; +import { DatabaseFile, Download, FileType, Playlist } from 'api-types'; @Component({ selector: 'app-root', @@ -78,7 +73,6 @@ export class MainComponent implements OnInit { mp4s: any[] = []; playlists = {'audio': [], 'video': []}; playlist_thumbnails = {}; - downloading_content = {'audio': {}, 'video': {}}; downloads: Download[] = []; download_uids: string[] = []; current_download: Download = null; @@ -466,11 +460,9 @@ export class MainComponent implements OnInit { } } - downloadFileFromServer(file, type: string): void { + downloadFileFromServer(file: DatabaseFile, type: string): void { const ext = type === 'audio' ? 'mp3' : 'mp4' - this.downloading_content[type][file.id] = true; this.postsService.downloadFileFromServer(file.uid).subscribe(res => { - this.downloading_content[type][file.id] = false; const blob: Blob = res; saveAs(blob, decodeURIComponent(file.id) + `.${ext}`); @@ -481,9 +473,8 @@ export class MainComponent implements OnInit { }); } - downloadPlaylist(playlist): void { + downloadPlaylist(playlist: Playlist): void { this.postsService.downloadPlaylistFromServer(playlist.id).subscribe(res => { - if (playlist.id) { this.downloading_content[playlist.type][playlist.id] = false }; const blob: Blob = res; saveAs(blob, playlist.name + '.zip'); }); diff --git a/src/app/posts.services.ts b/src/app/posts.services.ts index 5ec95d9..6417f5c 100644 --- a/src/app/posts.services.ts +++ b/src/app/posts.services.ts @@ -358,7 +358,7 @@ export class PostsService implements CanActivate { return this.http.post(this.path + 'getFile', body, this.httpOptions); } - getAllFiles(sort: Sort, range: number[], text_search: string, file_type_filter: FileTypeFilter, sub_id: string) { + getAllFiles(sort: Sort = null, range: number[] = null, text_search: string = null, file_type_filter: FileTypeFilter = FileTypeFilter.BOTH, sub_id: string = null) { const body: GetAllFilesRequest = {sort: sort, range: range, text_search: text_search, file_type_filter: file_type_filter, sub_id: sub_id}; return this.http.post(this.path + 'getAllFiles', body, this.httpOptions); } @@ -447,10 +447,9 @@ export class PostsService implements CanActivate { return this.http.post(this.path + 'disableSharing', body, this.httpOptions); } - createPlaylist(playlistName: string, uids: string[], type: FileType, thumbnailURL: string) { + createPlaylist(playlistName: string, uids: string[], thumbnailURL: string) { const body: CreatePlaylistRequest = {playlistName: playlistName, uids: uids, - type: type, thumbnailURL: thumbnailURL}; return this.http.post(this.path + 'createPlaylist', body, this.httpOptions); } @@ -475,8 +474,8 @@ export class PostsService implements CanActivate { return this.http.post(this.path + 'updatePlaylist', body, this.httpOptions); } - removePlaylist(playlist_id: string, type: FileType) { - const body: DeletePlaylistRequest = {playlist_id: playlist_id, type: type}; + removePlaylist(playlist_id: string) { + const body: DeletePlaylistRequest = {playlist_id: playlist_id}; return this.http.post(this.path + 'deletePlaylist', body, this.httpOptions); } diff --git a/src/assets/i18n/messages.en.xlf b/src/assets/i18n/messages.en.xlf index 8eff8b7..a24d0b6 100644 --- a/src/assets/i18n/messages.en.xlf +++ b/src/assets/i18n/messages.en.xlf @@ -590,7 +590,7 @@ src/app/components/recent-videos/recent-videos.component.html - 24 + 25 search field description @@ -690,7 +690,7 @@ No files found. src/app/components/recent-videos/recent-videos.component.html - 38 + 39 No files found @@ -698,7 +698,7 @@ File type src/app/components/recent-videos/recent-videos.component.html - 52 + 78 File type @@ -706,7 +706,7 @@ Both src/app/components/recent-videos/recent-videos.component.html - 54 + 80 Both @@ -714,7 +714,7 @@ Video only src/app/components/recent-videos/recent-videos.component.html - 55 + 81 Video only @@ -722,7 +722,7 @@ Audio only src/app/components/recent-videos/recent-videos.component.html - 56 + 82 Audio only @@ -991,10 +991,6 @@ src/app/components/unified-file-card/unified-file-card.component.html 24 - - src/app/subscription/subscription-file-card/subscription-file-card.component.html - 7 - Video info button @@ -1019,10 +1015,6 @@ src/app/components/unified-file-card/unified-file-card.component.html 34 - - src/app/subscription/subscription-file-card/subscription-file-card.component.html - 8 - Delete and redownload subscription video button @@ -1031,10 +1023,6 @@ src/app/components/unified-file-card/unified-file-card.component.html 37 - - src/app/subscription/subscription-file-card/subscription-file-card.component.html - 9 - Delete forever subscription video button @@ -1093,46 +1081,6 @@ Playlist name placeholder - - Type - - src/app/create-playlist/create-playlist.component.html - 11 - - Type select - - - Audio - - src/app/create-playlist/create-playlist.component.html - 12 - - Audio - - - Video - - src/app/create-playlist/create-playlist.component.html - 13 - - Video - - - Audio files - - src/app/create-playlist/create-playlist.component.html - 19 - - Audio files title - - - Videos - - src/app/create-playlist/create-playlist.component.html - 20 - - Videos title - About YoutubeDL-Material @@ -2108,7 +2056,7 @@ Download for has been queued! src/app/main/main.component.ts - 403 + 397 @@ -2906,14 +2854,6 @@ 48 - - Length: - - src/app/subscription/subscription-file-card/subscription-file-card.component.html - 3 - - Video duration label - Your subscriptions