]>
Commit | Line | Data |
---|---|---|
b2111066 C |
1 | import { logger, loggerTagsFactory } from '@server/helpers/logger' |
2 | import { MVideo } from '@server/types/models' | |
3 | import { VideoViewEvent } from '@shared/models' | |
ac907dc7 | 4 | import { VideoViewerCounters, VideoViewerStats, VideoViews } from './shared' |
b2111066 | 5 | |
dfbcefc2 C |
6 | /** |
7 | * If processing a local view: | |
8 | * - We update viewer information (segments watched, watch time etc) | |
9 | * - We add +1 to video viewers counter if this is a new viewer | |
10 | * - We add +1 to video views counter if this is a new view and if the user watched enough seconds | |
11 | * - We send AP message to notify about this viewer and this view | |
12 | * - We update last video time for the user if authenticated | |
13 | * | |
14 | * If processing a remote view: | |
15 | * - We add +1 to video viewers counter | |
16 | * - We add +1 to video views counter | |
17 | * | |
18 | * A viewer is a someone that watched one or multiple sections of a video | |
19 | * A viewer that watched only a few seconds of a video may not increment the video views counter | |
20 | * Viewers statistics are sent to origin instance using the `WatchAction` ActivityPub object | |
21 | * | |
22 | */ | |
23 | ||
b2111066 C |
24 | const lTags = loggerTagsFactory('views') |
25 | ||
26 | export class VideoViewsManager { | |
27 | ||
28 | private static instance: VideoViewsManager | |
29 | ||
ac907dc7 C |
30 | private videoViewerStats: VideoViewerStats |
31 | private videoViewerCounters: VideoViewerCounters | |
b2111066 C |
32 | private videoViews: VideoViews |
33 | ||
34 | private constructor () { | |
35 | } | |
36 | ||
37 | init () { | |
ac907dc7 C |
38 | this.videoViewerStats = new VideoViewerStats() |
39 | this.videoViewerCounters = new VideoViewerCounters() | |
b2111066 C |
40 | this.videoViews = new VideoViews() |
41 | } | |
42 | ||
43 | async processLocalView (options: { | |
44 | video: MVideo | |
45 | currentTime: number | |
46 | ip: string | null | |
47 | viewEvent?: VideoViewEvent | |
48 | }) { | |
49 | const { video, ip, viewEvent, currentTime } = options | |
50 | ||
51 | logger.debug('Processing local view for %s and ip %s.', video.url, ip, lTags()) | |
52 | ||
ac907dc7 C |
53 | await this.videoViewerStats.addLocalViewer({ video, ip, viewEvent, currentTime }) |
54 | ||
55 | const successViewer = await this.videoViewerCounters.addLocalViewer({ video, ip }) | |
b2111066 C |
56 | |
57 | // Do it after added local viewer to fetch updated information | |
ac907dc7 | 58 | const watchTime = await this.videoViewerStats.getWatchTime(video.id, ip) |
b2111066 C |
59 | |
60 | const successView = await this.videoViews.addLocalView({ video, watchTime, ip }) | |
61 | ||
62 | return { successView, successViewer } | |
63 | } | |
64 | ||
65 | async processRemoteView (options: { | |
66 | video: MVideo | |
ac907dc7 | 67 | viewerId: string | null |
b2111066 C |
68 | viewerExpires?: Date |
69 | }) { | |
ac907dc7 | 70 | const { video, viewerId, viewerExpires } = options |
b2111066 | 71 | |
ac907dc7 | 72 | logger.debug('Processing remote view for %s.', video.url, { viewerExpires, viewerId, ...lTags() }) |
b2111066 | 73 | |
ac907dc7 | 74 | if (viewerExpires) await this.videoViewerCounters.addRemoteViewer({ video, viewerId, viewerExpires }) |
b2111066 C |
75 | else await this.videoViews.addRemoteView({ video }) |
76 | } | |
77 | ||
78 | getViewers (video: MVideo) { | |
ac907dc7 | 79 | return this.videoViewerCounters.getViewers(video) |
b2111066 C |
80 | } |
81 | ||
82 | buildViewerExpireTime () { | |
ac907dc7 | 83 | return this.videoViewerCounters.buildViewerExpireTime() |
b2111066 C |
84 | } |
85 | ||
ac907dc7 C |
86 | processViewerStats () { |
87 | return this.videoViewerStats.processViewerStats() | |
b2111066 C |
88 | } |
89 | ||
90 | static get Instance () { | |
91 | return this.instance || (this.instance = new this()) | |
92 | } | |
93 | } |