1- import { Component , Input , OnInit } from '@angular/core' ;
2- import { MetadataBitstream } from 'src/app/core/metadata/metadata-bitstream.model' ;
3- import { HALEndpointService } from '../../../../../core/shared/hal-endpoint.service' ;
4- import { Router } from '@angular/router' ;
5- import { ConfigurationDataService } from '../../../../../core/data/configuration-data.service' ;
1+ import { Component , ElementRef , Input , OnInit , ViewChild } from '@angular/core' ;
2+ import { MetadataBitstream } from 'src/app/core/metadata/metadata-bitstream.model' ;
3+ import { HALEndpointService } from '../../../../../core/shared/hal-endpoint.service' ;
4+ import { Router } from '@angular/router' ;
5+ import { ConfigurationDataService } from '../../../../../core/data/configuration-data.service' ;
66import { getFirstCompletedRemoteData , getFirstSucceededRemoteData } from '../../../../../core/shared/operators' ;
77import { BitstreamDataService } from "../../../../../core/data/bitstream-data.service" ;
88import { Bitstream } from "../../../../../core/shared/bitstream.model" ;
99import { RemoteData } from "../../../../../core/data/remote-data" ;
1010import { followLink } from "../../../../../shared/utils/follow-link-config.model" ;
11+ import { fromEvent , merge , Observable , of } from "rxjs" ;
12+ import { FileService } from "../../../../../core/shared/file.service" ;
13+ import { distinctUntilChanged , switchMap , take } from "rxjs/operators" ;
14+ import { FeatureID } from "../../../../../core/data/feature-authorization/feature-id" ;
15+ import { hasValue } from "../../../../../shared/empty.util" ;
16+ import { AuthorizationDataService } from "../../../../../core/data/feature-authorization/authorization-data.service" ;
17+ import { AuthService } from "../../../../../core/auth/auth.service" ;
1118
1219const allowedPreviewFormats = [ 'text/plain' , 'text/html' , 'application/zip' , 'application/x-tar' ] ;
1320@Component ( {
@@ -23,12 +30,16 @@ export class FileDescriptionComponent implements OnInit {
2330 fileInput : MetadataBitstream ;
2431
2532 emailToContact : string ;
33+ content_url$ : Observable < string > ;
2634 content_url : string ;
27- thumbnail_url : string ;
35+ thumbnail_url$ : Observable < string > ;
2836
2937 constructor ( protected halService : HALEndpointService ,
3038 private router : Router ,
3139 private bitstreamService : BitstreamDataService ,
40+ private auth : AuthService ,
41+ private authDataService : AuthorizationDataService ,
42+ private fileService : FileService ,
3243 private configService : ConfigurationDataService ) { }
3344
3445 ngOnInit ( ) : void {
@@ -37,34 +48,122 @@ export class FileDescriptionComponent implements OnInit {
3748 . subscribe ( remoteData => {
3849 this . emailToContact = remoteData ?. payload ?. values ?. [ 0 ] ;
3950 } ) ;
40- this . bitstreamService . findById ( this . fileInput . id , true , false , followLink ( 'thumbnail' ) )
41- . pipe ( getFirstCompletedRemoteData ( ) )
42- . subscribe ( ( remoteData : RemoteData < Bitstream > ) => {
43- if ( remoteData . hasSucceeded ) {
44- this . content_url = remoteData . payload ?. _links . content . href ;
45- remoteData . payload ?. thumbnail . subscribe ( ( thumbnailRD : RemoteData < Bitstream > ) => {
46- if ( thumbnailRD . hasSucceeded ) {
47- this . thumbnail_url = thumbnailRD . payload ?. _links . content . href ;
51+ this . content_url$ = this . bitstreamService . findById ( this . fileInput . id , true , false , followLink ( 'thumbnail' ) )
52+ . pipe ( getFirstCompletedRemoteData ( ) ,
53+ switchMap ( ( remoteData : RemoteData < Bitstream > ) => {
54+ if ( remoteData . hasSucceeded ) {
55+ this . thumbnail_url$ = remoteData . payload ?. thumbnail . pipe (
56+ switchMap ( ( thumbnailRD : RemoteData < Bitstream > ) => {
57+ if ( thumbnailRD . hasSucceeded ) {
58+ return this . buildUrl ( thumbnailRD . payload ?. _links . content . href ) ;
59+ } else {
60+ return of ( "" ) ;
61+ }
62+ } ) ,
63+ ) ;
64+ return of ( remoteData . payload ?. _links . content . href ) ;
65+ }
66+ }
67+ ) ) ;
68+ this . content_url$ . pipe ( take ( 1 ) ) . subscribe ( ( url ) => {
69+ this . content_url = url ;
70+ } ) ;
71+ }
72+
73+ @ViewChild ( 'videoPreview' ) videoElement : ElementRef ;
74+ ngAfterViewInit ( ) {
75+ const video = this . videoElement ?. nativeElement ;
76+
77+ if ( video ) {
78+ const error$ = fromEvent ( video , 'error' ) ;
79+ error$ . subscribe ( ( event ) => {
80+ //console.log('error', video.error.message);
81+ if ( hasValue ( video . src ) ) {
82+ this . auth . isAuthenticated ( ) . pipe (
83+ switchMap ( ( isLoggedIn ) => {
84+ if ( isLoggedIn ) {
85+ return this . authDataService . isAuthorized ( FeatureID . CanDownload , this . content_url . replace ( '/content' , '' ) )
86+ } else {
87+ return of ( false ) ;
88+ }
89+ } ) ,
90+ ) . subscribe ( ( isAuthorized : boolean ) => {
91+ if ( isAuthorized ) {
92+ this . add_short_lived_token_handling_to_video_playback ( video ) ;
93+ this . resetSource ( ) ;
94+ } else {
95+ video . src = null ;
4896 }
4997 } ) ;
5098 }
5199 } ) ;
100+ }
52101 }
53102
54- public downloadFile ( ) {
55- void this . router . navigateByUrl ( 'bitstreams/' + this . fileInput . id + '/download' ) ;
103+ handlers_added = false ;
104+
105+ private add_short_lived_token_handling_to_video_playback ( video : HTMLVideoElement ) {
106+ if ( this . handlers_added ) {
107+ return ;
108+ }
109+
110+ const seeking$ = fromEvent ( video , 'seeking' ) . pipe (
111+ switchMap ( ( event : Event ) => {
112+ //console.log('seeking');
113+ return of ( video . currentTime ) ;
114+ } ) ,
115+ distinctUntilChanged ( ) ,
116+ ) ;
117+ const stalled$ = fromEvent ( video , 'stalled' ) . pipe (
118+ switchMap ( ( event : Event ) => {
119+ //console.log('stalled');
120+ return of ( video . currentTime ) ;
121+ } ) ,
122+ distinctUntilChanged ( ) ,
123+ ) ;
124+ merge ( seeking$ , stalled$ ) . subscribe ( ( currentTime ) => {
125+ this . resetSource ( currentTime ) ;
126+ } ) ;
127+
128+ this . handlers_added = true ;
129+
130+ }
131+
132+
133+ playPromise ;
134+
135+ private resetSource ( currentTime ?) {
136+ const video = this . videoElement ?. nativeElement ;
137+ //console.log("networkState in resetSource", video.networkState);
138+ if ( this . playPromise ) {
139+ this . playPromise ?. then ( _ => {
140+ //playback has started
141+ // don't want to see The play() request was interrupted by...
142+ // https://developer.chrome.com/blog/play-request-was-interrupted
143+ this . updateSource ( video , currentTime ) ;
144+ } ) . catch ( _ => { } ) ;
145+ } else {
146+ this . updateSource ( video , currentTime ) ;
147+ }
56148 }
57149
58- public hasThumbnail ( ) {
59- return this . thumbnail_url !== undefined && this . thumbnail_url !== null ;
150+ private updateSource ( video , currentTime ) {
151+ //console.log("Updating the src");
152+ this . buildUrl ( this . content_url ) . subscribe ( result => {
153+ video . src = result ;
154+ if ( currentTime ) {
155+ video . currentTime = currentTime ;
156+ }
157+ this . playPromise = video . play ( ) ;
158+ } ) ;
60159 }
61160
62- public thubmnailLink ( ) {
63- return this . thumbnail_url ;
161+ private buildUrl ( url : string ) : Observable < string > {
162+ return url ? this . fileService . retrieveFileDownloadLink ( url ) : of ( url ) ;
64163 }
65164
66- public contentLink ( ) {
67- return this . content_url ;
165+ public downloadFile ( ) {
166+ void this . router . navigateByUrl ( 'bitstreams/' + this . fileInput . id + '/download' ) ;
68167 }
69168
70169 public isTxt ( ) {
0 commit comments