From 14bf2248cfbc9fdf7ed74f467ad3c3f4ca03969d Mon Sep 17 00:00:00 2001 From: Isaac Grynsztein Date: Tue, 7 Apr 2020 18:19:12 -0400 Subject: [PATCH] Added UI support for sharing videos --- backend/app.js | 40 +++++++++-- src/app/app.module.ts | 10 ++- .../share-media-dialog.component.html | 25 +++++++ .../share-media-dialog.component.scss | 0 .../share-media-dialog.component.spec.ts | 25 +++++++ .../share-media-dialog.component.ts | 71 +++++++++++++++++++ src/app/player/player.component.css | 10 ++- src/app/player/player.component.html | 2 + src/app/player/player.component.ts | 58 +++++++++++++-- src/app/posts.services.ts | 13 ++-- 10 files changed, 234 insertions(+), 20 deletions(-) create mode 100644 src/app/dialogs/share-media-dialog/share-media-dialog.component.html create mode 100644 src/app/dialogs/share-media-dialog/share-media-dialog.component.scss create mode 100644 src/app/dialogs/share-media-dialog/share-media-dialog.component.spec.ts create mode 100644 src/app/dialogs/share-media-dialog/share-media-dialog.component.ts diff --git a/backend/app.js b/backend/app.js index 16fd923..69c489a 100644 --- a/backend/app.js +++ b/backend/app.js @@ -1640,14 +1640,15 @@ app.post('/api/getFile', function (req, res) { app.post('/api/enableSharing', function(req, res) { var type = req.body.type; var uid = req.body.uid; + var is_playlist = req.body.is_playlist; try { success = true; - if (type === 'audio' || type === 'video') { + if (!is_playlist && type !== 'subscription') { db.get(`files.${type}`) .find({uid: uid}) .assign({sharingEnabled: true}) .write(); - } else if (type === 'playlist') { + } else if (is_playlist) { db.get(`playlists.${type}`) .find({id: uid}) .assign({sharingEnabled: true}) @@ -1672,15 +1673,16 @@ app.post('/api/enableSharing', function(req, res) { app.post('/api/disableSharing', function(req, res) { var type = req.body.type; var uid = req.body.uid; + var is_playlist = req.body.is_playlist; try { success = true; - if (type === 'audio' || type === 'video') { + if (!is_playlist && type !== 'subscription') { db.get(`files.${type}`) .find({uid: uid}) .assign({sharingEnabled: false}) .write(); - } else if (type === 'playlist') { - db.get(`playlists.${type}`) + } else if (is_playlist) { + db.get(`playlists.${type}`) .find({id: uid}) .assign({sharingEnabled: false}) .write(); @@ -1851,7 +1853,8 @@ app.post('/api/createPlaylist', async (req, res) => { 'name': playlistName, fileNames: fileNames, id: shortid.generate(), - thumbnailURL: thumbnailURL + thumbnailURL: thumbnailURL, + type: type }; db.get(`playlists.${type}`) @@ -1864,6 +1867,31 @@ app.post('/api/createPlaylist', async (req, res) => { }) }); +app.post('/api/getPlaylist', async (req, res) => { + let playlistID = req.body.playlistID; + let type = req.body.type; + + let playlist = null; + + if (!type) { + playlist = db.get('playlists.audio').find({id: playlistID}).value(); + if (!playlist) { + playlist = db.get('playlists.video').find({id: playlistID}).value(); + if (playlist) type = 'video'; + } else { + type = 'audio'; + } + } + + if (!playlist) playlist = db.get(`playlists.${type}`).find({id: playlistID}).value(); + + res.send({ + playlist: playlist, + type: type, + success: !!playlist + }); +}); + app.post('/api/updatePlaylist', async (req, res) => { let playlistID = req.body.playlistID; let fileNames = req.body.fileNames; diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 3784230..6f03650 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -25,8 +25,9 @@ import { MatSlideToggleModule } from '@angular/material/slide-toggle'; import { MatSnackBarModule } from '@angular/material/snack-bar'; import { MatToolbarModule } from '@angular/material/toolbar'; import { MatTabsModule } from '@angular/material/tabs'; - import {DragDropModule} from '@angular/cdk/drag-drop'; - import {FormsModule, ReactiveFormsModule} from '@angular/forms'; +import {DragDropModule} from '@angular/cdk/drag-drop'; +import {ClipboardModule} from '@angular/cdk/clipboard'; +import {FormsModule, ReactiveFormsModule} from '@angular/forms'; import { AppComponent } from './app.component'; import { HttpClientModule, HttpClient } from '@angular/common/http'; import { PostsService } from 'app/posts.services'; @@ -55,6 +56,7 @@ import { VideoInfoDialogComponent } from './dialogs/video-info-dialog/video-info import { ArgModifierDialogComponent, HighlightPipe } from './dialogs/arg-modifier-dialog/arg-modifier-dialog.component'; import { UpdaterComponent } from './updater/updater.component'; import { UpdateProgressDialogComponent } from './dialogs/update-progress-dialog/update-progress-dialog.component'; +import { ShareMediaDialogComponent } from './dialogs/share-media-dialog/share-media-dialog.component'; registerLocaleData(es, 'es'); export function isVisible({ event, element, scrollContainer, offset }: IsVisibleProps) { @@ -82,7 +84,8 @@ export function isVisible({ event, element, scrollContainer, offset }: IsVisible ArgModifierDialogComponent, HighlightPipe, UpdaterComponent, - UpdateProgressDialogComponent + UpdateProgressDialogComponent, + ShareMediaDialogComponent ], imports: [ CommonModule, @@ -117,6 +120,7 @@ export function isVisible({ event, element, scrollContainer, offset }: IsVisible MatTabsModule, MatTooltipModule, DragDropModule, + ClipboardModule, VgCoreModule, VgControlsModule, VgOverlayPlayModule, diff --git a/src/app/dialogs/share-media-dialog/share-media-dialog.component.html b/src/app/dialogs/share-media-dialog/share-media-dialog.component.html new file mode 100644 index 0000000..02dff40 --- /dev/null +++ b/src/app/dialogs/share-media-dialog/share-media-dialog.component.html @@ -0,0 +1,25 @@ +

+ Share playlist + Share video + Share audio +

+ + +
+
+ Enable sharing +
+
+ + + +
+
+ +
+
+
+ + + + diff --git a/src/app/dialogs/share-media-dialog/share-media-dialog.component.scss b/src/app/dialogs/share-media-dialog/share-media-dialog.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/app/dialogs/share-media-dialog/share-media-dialog.component.spec.ts b/src/app/dialogs/share-media-dialog/share-media-dialog.component.spec.ts new file mode 100644 index 0000000..fddbc1a --- /dev/null +++ b/src/app/dialogs/share-media-dialog/share-media-dialog.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ShareMediaDialogComponent } from './share-media-dialog.component'; + +describe('ShareMediaDialogComponent', () => { + let component: ShareMediaDialogComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ ShareMediaDialogComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ShareMediaDialogComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); 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 new file mode 100644 index 0000000..950a6cc --- /dev/null +++ b/src/app/dialogs/share-media-dialog/share-media-dialog.component.ts @@ -0,0 +1,71 @@ +import { Component, OnInit, Inject } from '@angular/core'; +import { MAT_DIALOG_DATA } from '@angular/material/dialog'; +import { Router } from '@angular/router'; +import { MatSnackBar } from '@angular/material/snack-bar'; +import { PostsService } from 'app/posts.services'; + +@Component({ + selector: 'app-share-media-dialog', + templateUrl: './share-media-dialog.component.html', + styleUrls: ['./share-media-dialog.component.scss'] +}) +export class ShareMediaDialogComponent implements OnInit { + + type = null; + uid = null; + share_url = null; + sharing_enabled = null; + is_playlist = null; + + constructor(@Inject(MAT_DIALOG_DATA) public data: any, public router: Router, private snackBar: MatSnackBar, + private postsService: PostsService) { } + + ngOnInit(): void { + if (this.data) { + this.type = this.data.type; + this.uid = this.data.uid; + 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; + } + } + + copiedToClipboard() { + this.openSnackBar('Copied to clipboard!'); + } + + sharingChanged(event) { + if (event.checked) { + this.postsService.enableSharing(this.uid, this.type, this.is_playlist).subscribe(res => { + if (res['success']) { + this.openSnackBar('Sharing enabled.'); + this.sharing_enabled = true; + } else { + this.openSnackBar('Failed to enable sharing.'); + } + }, err => { + this.openSnackBar('Failed to enable sharing - server error.'); + }); + } else { + this.postsService.disableSharing(this.uid, this.type, this.is_playlist).subscribe(res => { + if (res['success']) { + this.openSnackBar('Sharing disabled.'); + this.sharing_enabled = false; + } else { + this.openSnackBar('Failed to disable sharing.'); + } + }, err => { + this.openSnackBar('Failed to disable sharing - server error.'); + }); + } + } + + public openSnackBar(message: string, action: string = '') { + this.snackBar.open(message, action, { + duration: 2000, + }); + } + +} diff --git a/src/app/player/player.component.css b/src/app/player/player.component.css index 68ccbb3..bc27f1f 100644 --- a/src/app/player/player.component.css +++ b/src/app/player/player.component.css @@ -45,13 +45,19 @@ .save-button { right: 25px; - position: absolute; + position: fixed; + bottom: 25px; +} + +.share-button { + left: 25px; + position: fixed; bottom: 25px; } .favorite-button { left: 25px; - position: absolute; + position: fixed; bottom: 25px; } diff --git a/src/app/player/player.component.html b/src/app/player/player.component.html index 30aac2b..f9221b3 100644 --- a/src/app/player/player.component.html +++ b/src/app/player/player.component.html @@ -26,8 +26,10 @@
+
+
\ No newline at end of file diff --git a/src/app/player/player.component.ts b/src/app/player/player.component.ts index d89ee12..a43bfad 100644 --- a/src/app/player/player.component.ts +++ b/src/app/player/player.component.ts @@ -6,6 +6,7 @@ import { MatDialog } from '@angular/material/dialog'; import { MatSnackBar } from '@angular/material/snack-bar'; import { InputDialogComponent } from 'app/input-dialog/input-dialog.component'; import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop'; +import { ShareMediaDialogComponent } from 'app/dialogs/share-media-dialog/share-media-dialog.component'; export interface IMedia { title: string; @@ -39,6 +40,11 @@ export class PlayerComponent implements OnInit { subscriptionName = null; subPlaylist = null; + is_shared = false; + + db_playlist = null; + db_file = null; + baseStreamPath = null; audioFolderPath = null; videoFolderPath = null; @@ -71,8 +77,14 @@ export class PlayerComponent implements OnInit { this.subscriptionFolderPath = result['YoutubeDLMaterial']['Subscriptions']['subscriptions_base_path']; this.fileNames = this.route.snapshot.paramMap.get('fileNames') ? this.route.snapshot.paramMap.get('fileNames').split('|nvr|') : null; - if (this.uid) { + if (!this.fileNames) { + this.is_shared = true; + } + + if (this.uid && !this.id) { this.getFile(); + } else if (this.id) { + this.getPlaylistFiles(); } if (this.type === 'subscription' || this.fileNames) { @@ -91,17 +103,33 @@ export class PlayerComponent implements OnInit { } getFile() { + const already_has_filenames = !!this.fileNames; this.postsService.getFile(this.uid, null).subscribe(res => { + this.db_file = res['file']; if (!this.fileNames) { // means it's a shared video if (!this.id) { // regular video/audio file (not playlist) - this.fileNames = [res['file']['id']]; - this.type = res['file']['isAudio'] ? 'audio' : 'video'; - this.parseFileNames(); + this.fileNames = [this.db_file['id']]; + this.type = this.db_file['isAudio'] ? 'audio' : 'video'; + if (!already_has_filenames) { this.parseFileNames(); } } } + if (this.db_file['sharingEnabled']) { + this.show_player = true; + } else if (!already_has_filenames) { + this.openSnackBar('Error: Sharing has been disabled for this video!', 'Dismiss'); + } + }); + } + + 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(); }); } @@ -118,7 +146,7 @@ export class PlayerComponent implements OnInit { // error console.error('Must have valid file type! Use \'audio\', \'video\', or \'subscription\'.'); } - + this.playlist = []; for (let i = 0; i < this.fileNames.length; i++) { const fileName = this.fileNames[i]; let baseLocation = null; @@ -302,6 +330,26 @@ export class PlayerComponent implements OnInit { }) } + openShareDialog() { + const dialogRef = this.dialog.open(ShareMediaDialogComponent, { + data: { + 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 + }, + width: '60vw' + }); + + dialogRef.afterClosed().subscribe(res => { + if (!this.id) { + this.getFile(); + } else { + this.getPlaylistFiles(); + } + }); + } + // snackbar helper public openSnackBar(message: string, action: string) { this.snackBar.open(message, action, { diff --git a/src/app/posts.services.ts b/src/app/posts.services.ts index e73e311..e17b508 100644 --- a/src/app/posts.services.ts +++ b/src/app/posts.services.ts @@ -151,12 +151,12 @@ export class PostsService { return this.http.post(this.path + 'checkPin', {input_pin: unhashed_pin}); } - enableSharing(uid, type) { - return this.http.post(this.path + 'enableSharing', {uid: uid, type: type}); + enableSharing(uid, type, is_playlist) { + return this.http.post(this.path + 'enableSharing', {uid: uid, type: type, is_playlist: is_playlist}); } - disableSharing(uid, type) { - return this.http.post(this.path + 'disableSharing', {uid: uid, type: type}); + disableSharing(uid, type, is_playlist) { + return this.http.post(this.path + 'disableSharing', {uid: uid, type: type, is_playlist: is_playlist}); } createPlaylist(playlistName, fileNames, type, thumbnailURL) { @@ -166,6 +166,11 @@ export class PostsService { thumbnailURL: thumbnailURL}); } + getPlaylist(playlistID, type) { + return this.http.post(this.path + 'getPlaylist', {playlistID: playlistID, + type: type}); + } + updatePlaylist(playlistID, fileNames, type) { return this.http.post(this.path + 'updatePlaylist', {playlistID: playlistID, fileNames: fileNames,