New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(video): add download button and overlay to video player (DEV-1151) #798
Changes from 6 commits
29049b2
a4f1940
a5b8133
04e16a2
0fec35a
234e713
b852777
4b8f227
6ae945d
82afe67
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,6 +9,24 @@ | |
(canplaythrough)="loadedVideo()"> | ||
</video> | ||
|
||
<div class="video-overlay" *ngIf="!play && firstPlayed"> | ||
<div> | ||
<button class="icon-button-large" mat-icon-button (click)="updateTimeFromButton(-10)" | ||
matTooltip="10 seconds backward" [matTooltipPosition]="matTooltipPos" | ||
[disabled]="failedToLoad"> | ||
<mat-icon>replay_10</mat-icon> | ||
</button> | ||
<button class="icon-button-large" mat-icon-button (click)="togglePlay()" [matTooltip]="(reachedTheEnd ? 'Replay' : (play ? 'Pause' : 'Play'))"> | ||
<mat-icon>{{ reachedTheEnd ? "replay" : (play ? "pause" : "play_arrow") }}</mat-icon> | ||
</button> | ||
<button class="icon-button-large" mat-icon-button (click)="updateTimeFromButton(10)" | ||
matTooltip="10 seconds forward" [matTooltipPosition]="matTooltipPos" | ||
[disabled]="failedToLoad"> | ||
<mat-icon>forward_10</mat-icon> | ||
</button> | ||
</div> | ||
</div> | ||
|
||
<ng-template #loading> | ||
<app-progress-indicator></app-progress-indicator> | ||
</ng-template> | ||
|
@@ -23,7 +41,6 @@ | |
[fileHasChanged]="fileHasChanged" | ||
(loaded)="displayPreview(!$event)"> | ||
</app-video-preview> | ||
|
||
</div> | ||
</div> | ||
</div> | ||
|
@@ -37,6 +54,25 @@ | |
</mat-toolbar-row> | ||
|
||
<mat-toolbar-row class="action"> | ||
<!-- vertical more button with menu to open and copy url --> | ||
<button mat-icon-button [matMenuTriggerFor]="more"> | ||
<mat-icon>more_vert</mat-icon> | ||
</button> | ||
<mat-menu #more="matMenu"> | ||
<button mat-menu-item (click)="openVideoInNewTab(video['changingThisBreaksApplicationSecurity'])"> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I believe this will break in production.
We already have access to the file url via There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done in 6ae945d |
||
Open file in new tab | ||
</button> | ||
<button mat-menu-item [cdkCopyToClipboard]="video['changingThisBreaksApplicationSecurity']" | ||
(click)="openSnackBar('URL copied to clipboard!')"> | ||
Copy URL to clipboard | ||
</button> | ||
<button mat-menu-item (click)="downloadVideo(video['changingThisBreaksApplicationSecurity'])"> | ||
Download file | ||
</button> | ||
<button mat-menu-item (click)="openReplaceFileDialog()"> | ||
Replace file | ||
</button> | ||
</mat-menu> | ||
<button mat-icon-button (click)="goToStart()" [disabled]="currentTime === 0 || failedToLoad" | ||
matTooltip="Stop and go to start" [matTooltipPosition]="matTooltipPos"> | ||
<mat-icon>skip_previous</mat-icon> | ||
|
@@ -46,37 +82,27 @@ | |
[disabled]="failedToLoad"> | ||
<mat-icon>{{ reachedTheEnd ? "replay" : (play ? "pause" : "play_arrow") }}</mat-icon> | ||
</button> | ||
|
||
<span class="empty-space"></span> | ||
<span class="fill-remaining-space"></span> | ||
<button mat-icon-button (click)="updateTimeFromButton(-10)" | ||
matTooltip="10 seconds backward" [matTooltipPosition]="matTooltipPos" | ||
[disabled]="failedToLoad"> | ||
<mat-icon>replay_10</mat-icon> | ||
</button> | ||
|
||
<div (wheel)="updateTimeFromScroll($event)"> | ||
<p class="mat-body-1 time" (click)="togglePlay()"> | ||
{{ currentTime | appTime }} | ||
<span *ngIf="duration">/ {{ duration | appTime }}</span> | ||
</p> | ||
</div> | ||
|
||
<button mat-icon-button (click)="updateTimeFromButton(10)" | ||
matTooltip="10 seconds forward" [matTooltipPosition]="matTooltipPos" | ||
[disabled]="failedToLoad"> | ||
<mat-icon>forward_10</mat-icon> | ||
</button> | ||
<!-- <button mat-icon-button (click)="muted = !muted" [matTooltip]="(muted ? 'Unmute' : 'Mute')"--> | ||
<!-- [matTooltipPosition]="matTooltipPos"--> | ||
<!-- [disabled]="failedToLoad">--> | ||
<!-- <mat-icon>--> | ||
<!-- {{ muted ? "volume_mute" : "volume_up" }}--> | ||
<!-- </mat-icon>--> | ||
<!-- </button>--> | ||
<span class="fill-remaining-space"></span> | ||
<button mat-icon-button (click)="muted = !muted" [matTooltip]="(muted ? 'Unmute' : 'Mute')" | ||
[matTooltipPosition]="matTooltipPos" | ||
[disabled]="failedToLoad"> | ||
<mat-icon> | ||
{{ muted ? "volume_mute" : "volume_up" }} | ||
</mat-icon> | ||
</button> | ||
<button mat-icon-button (click)="openReplaceFileDialog()" class="replace-file" matTooltip="Replace video file" [matTooltipPosition]="matTooltipPos"> | ||
<mat-icon>cloud_upload</mat-icon> | ||
</button> | ||
<span class="empty-space"></span> | ||
<span class="empty-space"></span> | ||
|
||
<button mat-icon-button (click)="toggleCinemaMode()" [disabled]="failedToLoad" | ||
[matTooltip]="(cinemaMode ? 'Default view' : 'Cinema mode')" [matTooltipPosition]="matTooltipPos"> | ||
<mat-icon>{{cinemaMode ? "fullscreen_exit" : "fullscreen"}}</mat-icon> | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
import { AfterViewInit, Component, ElementRef, EventEmitter, HostListener, Inject, Input, OnInit, Output, ViewChild } from '@angular/core'; | ||
import { MatDialog, MatDialogConfig } from '@angular/material/dialog'; | ||
import { DomSanitizer, SafeUrl } from '@angular/platform-browser'; | ||
import { HttpClient, HttpHeaders } from '@angular/common/http'; | ||
import { | ||
ApiResponseError, | ||
Constants, | ||
|
@@ -21,6 +22,7 @@ import { EmitEvent, Events, UpdatedFileEventValue, ValueOperationEventService } | |
import { PointerValue } from '../av-timeline/av-timeline.component'; | ||
import { FileRepresentation } from '../file-representation'; | ||
import { RepresentationService } from '../representation.service'; | ||
import { NotificationService } from '../../../../main/services/notification.service'; | ||
|
||
@Component({ | ||
selector: 'app-video', | ||
|
@@ -48,7 +50,7 @@ export class VideoComponent implements OnInit, AfterViewInit { | |
@ViewChild('preview') preview: ElementRef; | ||
|
||
loading = true; | ||
|
||
originalFilename: string; | ||
failedToLoad = false; | ||
|
||
// video file url | ||
|
@@ -85,6 +87,7 @@ export class VideoComponent implements OnInit, AfterViewInit { | |
|
||
// status | ||
play = false; | ||
firstPlayed = false; | ||
reachedTheEnd = false; | ||
|
||
// volume | ||
|
@@ -102,10 +105,12 @@ export class VideoComponent implements OnInit, AfterViewInit { | |
|
||
constructor( | ||
@Inject(DspApiConnectionToken) private _dspApiConnection: KnoraApiConnection, | ||
private readonly _http: HttpClient, | ||
private _dialog: MatDialog, | ||
private _sanitizer: DomSanitizer, | ||
private _errorHandler: ErrorHandlerService, | ||
private _rs: RepresentationService, | ||
private _errorHandler: ErrorHandlerService, | ||
private _notification: NotificationService, | ||
private _valueOperationEventService: ValueOperationEventService | ||
) { } | ||
|
||
|
@@ -119,6 +124,7 @@ export class VideoComponent implements OnInit, AfterViewInit { | |
this.video = this._sanitizer.bypassSecurityTrustUrl(this.src.fileValue.fileUrl); | ||
this.failedToLoad = !this._rs.doesFileExist(this.src.fileValue.fileUrl); | ||
this.fileHasChanged = false; | ||
this._getOriginalFilename(); | ||
} | ||
|
||
ngAfterViewInit() { | ||
|
@@ -143,6 +149,9 @@ export class VideoComponent implements OnInit, AfterViewInit { | |
this.play = !this.play; | ||
|
||
if (this.play) { | ||
if (!this.firstPlayed) { | ||
this.firstPlayed = true; | ||
} | ||
this.videoEle.nativeElement.play(); | ||
} else { | ||
this.videoEle.nativeElement.pause(); | ||
|
@@ -293,6 +302,39 @@ export class VideoComponent implements OnInit, AfterViewInit { | |
this.preview.nativeElement.style.display = (status ? 'block' : 'none'); | ||
} | ||
|
||
/** | ||
* display message to confirm the copy of the citation link (ARK URL) | ||
*/ | ||
openSnackBar(message: string) { | ||
this._notification.openSnackBar(message); | ||
} | ||
|
||
async downloadVideo(url: string) { | ||
try { | ||
const res = await this._http.get(url, { responseType: 'blob' }).toPromise(); | ||
this.downloadFile(res); | ||
} catch (e) { | ||
this._errorHandler.showMessage(e); | ||
} | ||
} | ||
|
||
downloadFile(data) { | ||
const url = window.URL.createObjectURL(data); | ||
const e = document.createElement('a'); | ||
e.href = url; | ||
|
||
// set filename | ||
if (this.originalFilename === undefined) { | ||
e.download = url.substr(url.lastIndexOf('/') + 1); | ||
} else { | ||
e.download = this.originalFilename; | ||
} | ||
|
||
document.body.appendChild(e); | ||
e.click(); | ||
document.body.removeChild(e); | ||
} | ||
|
||
/** | ||
* opens replace file dialog | ||
* | ||
|
@@ -321,6 +363,10 @@ export class VideoComponent implements OnInit, AfterViewInit { | |
}); | ||
} | ||
|
||
openVideoInNewTab(url: string) { | ||
window.open(url, '_blank'); | ||
} | ||
|
||
/** | ||
* general video navigation: Update current video time from position | ||
* | ||
|
@@ -340,6 +386,24 @@ export class VideoComponent implements OnInit, AfterViewInit { | |
this.previewTime = this.previewTime < 0 ? 0 : this.previewTime; | ||
} | ||
|
||
private _getOriginalFilename() { | ||
const requestOptions = { | ||
headers: new HttpHeaders({ 'Content-Type': 'application/json' }), | ||
withCredentials: true | ||
}; | ||
|
||
const index = this.src.fileValue.fileUrl.indexOf(this.src.fileValue.filename); | ||
const pathToJson = this.src.fileValue.fileUrl.substring(0, index + this.src.fileValue.filename.length) + '/knora.json'; | ||
|
||
this._http.get(pathToJson, requestOptions).subscribe( | ||
res => { | ||
console.log('a', res); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. these console.logs can be removed There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fixed in b852777 |
||
this.originalFilename = res['originalFilename']; | ||
console.log(this.originalFilename); | ||
} | ||
); | ||
} | ||
|
||
/** | ||
* replaces file | ||
* @param file | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The video overlay should only show when hovering over the video no matter if it's playing or not. The play button should appear if it's paused and vice versa.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
4b8f227