1
- import { AfterViewInit , Component , ElementRef , OnInit , ViewChild } from '@angular/core' ;
2
- import { combineLatest , EMPTY , Observable } from 'rxjs' ;
1
+ import { AfterViewInit , Component , ElementRef , OnInit , ViewChild , OnDestroy } from '@angular/core' ;
2
+ import { combineLatest , EMPTY , Observable , share , Subject , takeUntil , timer } from 'rxjs' ;
3
3
import { ActivatedRoute , Router } from '@angular/router' ;
4
4
import { CourseMembers , Lecture , ManService } from '../../man.service' ;
5
5
import { map , switchMap } from 'rxjs/operators' ;
@@ -8,18 +8,19 @@ import 'videojs-hotkeys';
8
8
import 'videojs-youtube' ;
9
9
import { AlertController } from '@ionic/angular/standalone' ;
10
10
import { DomSanitizer } from '@angular/platform-browser' ;
11
- import { PlayHistory , PlayTrackerService } from '../../play-tracker.service' ;
11
+ import { PlayHistory } from '../../play-tracker.service' ;
12
12
import { Analytics , logEvent } from '@angular/fire/analytics' ;
13
13
import { addIcons } from "ionicons" ;
14
14
import { checkmarkOutline , closeOutline , documentAttachOutline , download , pauseCircleOutline } from "ionicons/icons" ;
15
15
import type Player from 'video.js/dist/types/player' ;
16
+ import { ulid } from 'ulid' ;
16
17
17
18
@Component ( {
18
19
selector : 'app-course' ,
19
20
templateUrl : './course.page.html' ,
20
21
styleUrls : [ './course.page.scss' ] ,
21
22
} )
22
- export class CoursePage implements OnInit , AfterViewInit {
23
+ export class CoursePage implements OnInit , AfterViewInit , OnDestroy {
23
24
@ViewChild ( 'videoPlayer' ) videoPlayerElement : ElementRef ;
24
25
videoPlayer : Player ;
25
26
currentVideo : Lecture ;
@@ -33,11 +34,12 @@ export class CoursePage implements OnInit, AfterViewInit {
33
34
isAndroid = / A n d r o i d / i. test ( navigator . userAgent ) ;
34
35
isIos = / i P a d / i. test ( navigator . userAgent ) || / i P h o n e / i. test ( navigator . userAgent ) ;
35
36
lastPlayedVideoKey : string | null = null ;
37
+ sessionUid : string ; // Unique ID for session x video (new id for each video)
38
+ stopPolling = new Subject ( ) ;
36
39
37
40
constructor ( private route : ActivatedRoute , private router : Router ,
38
41
private manService : ManService , private alertController : AlertController ,
39
- private analytics : Analytics , private sanitizer : DomSanitizer ,
40
- private playTracker : PlayTrackerService ) {
42
+ private analytics : Analytics , private sanitizer : DomSanitizer ) {
41
43
addIcons ( { download, documentAttachOutline, checkmarkOutline, closeOutline, pauseCircleOutline } ) ;
42
44
}
43
45
@@ -48,8 +50,13 @@ export class CoursePage implements OnInit, AfterViewInit {
48
50
this . course = s . get ( 'course' ) ;
49
51
if ( this . year && this . course ) {
50
52
return combineLatest ( [
51
- this . manService . getVideosInCourse ( this . year , this . course ) , this . playTracker . retrieve ( ) ] )
52
- . pipe ( map ( ( [ videos , history ] ) => this . mergeVideoInfo ( videos , history ) ) ) ;
53
+ this . manService . getVideosInCourse ( this . year , this . course ) ,
54
+ timer ( 1 , 30000 ) . pipe (
55
+ switchMap ( ( ) => this . manService . getPlayRecord ( this . year , this . course ) ) ,
56
+ share ( ) ,
57
+ takeUntil ( this . stopPolling ) ,
58
+ ) ,
59
+ ] ) . pipe ( map ( ( [ videos , history ] ) => this . mergeVideoInfo ( videos , history ) ) ) ;
53
60
} else if ( this . year ) {
54
61
this . router . navigate ( [ 'home/' + this . year ] ) ;
55
62
} else {
@@ -80,24 +87,23 @@ export class CoursePage implements OnInit, AfterViewInit {
80
87
this . videoPlayer . on ( 'pause' , ( ) => {
81
88
if ( ! this . videoPlayer . seeking ( ) ) {
82
89
// is paused, not seeking
83
- this . playTracker . updateCurrentTime ( this . currentVideo . identifier , this . videoPlayer . currentTime ( ) , this . year , this . course , this . videoPlayer . duration ( ) ) ;
90
+ this . updatePlayRecord ( ) ;
84
91
}
85
92
} ) ;
86
- this . videoPlayer . on ( 'ended' , ( ) =>
87
- this . playTracker . updateCurrentTime ( this . currentVideo . identifier , this . videoPlayer . currentTime ( ) , this . year , this . course , this . videoPlayer . duration ( ) ) ) ;
93
+ this . videoPlayer . on ( 'ended' , ( ) => this . updatePlayRecord ( ) ) ;
88
94
let lastUpdated = 0 ;
89
95
this . videoPlayer . on ( 'timeupdate' , ( ) => {
90
- // Update while playing every 10 minutes
91
- if ( Date . now ( ) - lastUpdated > 600000 ) {
96
+ // Update while playing every 2 minutes
97
+ if ( Date . now ( ) - lastUpdated > 120000 ) {
92
98
lastUpdated = Date . now ( ) ;
93
- this . playTracker . updateCurrentTime ( this . currentVideo . identifier , this . videoPlayer . currentTime ( ) , this . year , this . course , this . videoPlayer . duration ( ) ) ;
99
+ this . updatePlayRecord ( ) ;
94
100
}
95
101
} ) ;
96
102
this . videoPlayer . on ( 'tracking:performance' , ( _e , data ) => {
97
103
console . log ( 'performance' ) ;
98
104
if ( this . videoPlayer . currentTime ( ) > 30 ) {
99
105
logEvent ( this . analytics , 'video_performance' , this . attachEventLabel ( data , true ) ) ;
100
- this . playTracker . updateCurrentTime ( this . currentVideo . identifier , data . currentTime , this . year , this . course ) ;
106
+ this . updatePlayRecord ( ) ;
101
107
}
102
108
} ) ;
103
109
this . videoPlayer . on ( 'loadedmetadata' , ( ) => {
@@ -109,6 +115,10 @@ export class CoursePage implements OnInit, AfterViewInit {
109
115
} ) ;
110
116
}
111
117
118
+ ngOnDestroy ( ) {
119
+ this . stopPolling . next ( true ) ;
120
+ }
121
+
112
122
mergeVideoInfo ( videos : CourseMembers , history : PlayHistory ) {
113
123
const progress = {
114
124
viewed : 0 ,
@@ -119,11 +129,7 @@ export class CoursePage implements OnInit, AfterViewInit {
119
129
return history [ b ] . updatedAt - history [ a ] . updatedAt ;
120
130
} ) . slice ( 0 , 1 ) [ 0 ] ?? null ;
121
131
Object . keys ( videos ) . forEach ( lectureKey => {
122
- videos [ lectureKey ] . history = history [ videos [ lectureKey ] . identifier ] ?? { currentTime : null , updatedAt : null , duration : null } ;
123
- if ( ! videos [ lectureKey ] . duration && videos [ lectureKey ] . history . duration ) {
124
- videos [ lectureKey ] . duration = videos [ lectureKey ] . history . duration ;
125
- videos [ lectureKey ] . durationInMin = videos [ lectureKey ] . duration ? Math . round ( videos [ lectureKey ] . duration / 60 ) : 0
126
- }
132
+ videos [ lectureKey ] . history = history [ videos [ lectureKey ] . id ] ?? { currentTime : null , updatedAt : null } ;
127
133
if ( videos [ lectureKey ] . duration ) {
128
134
progress . duration -= - videos [ lectureKey ] . duration ;
129
135
if ( videos [ lectureKey ] . history . currentTime ) {
@@ -154,6 +160,7 @@ export class CoursePage implements OnInit, AfterViewInit {
154
160
this . videoPlayer . src ( video . sources ) ;
155
161
this . currentVideo = video ;
156
162
this . videoPlayerElement . nativeElement . focus ( ) ;
163
+ this . sessionUid = ulid ( ) ;
157
164
}
158
165
159
166
async setPlaybackSpeed ( ) {
@@ -215,4 +222,7 @@ export class CoursePage implements OnInit, AfterViewInit {
215
222
} ;
216
223
}
217
224
225
+ protected updatePlayRecord ( ) {
226
+ this . manService . updatePlayRecord ( this . sessionUid , this . currentVideo . id , this . videoPlayer . currentTime ( ) , this . videoPlayer . playbackRate ( ) ) . subscribe ( ) ;
227
+ }
218
228
}
0 commit comments