]>
Commit | Line | Data |
---|---|---|
1 | import { logger, loggerTagsFactory } from '@server/helpers/logger' | |
2 | import { MVideo, MVideoImmutable } from '@server/types/models' | |
3 | import { VideoViewEvent } from '@shared/models' | |
4 | import { VideoScope, VideoViewerCounters, VideoViewerStats, VideoViews, ViewerScope } from './shared' | |
5 | ||
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 | ||
24 | const lTags = loggerTagsFactory('views') | |
25 | ||
26 | export class VideoViewsManager { | |
27 | ||
28 | private static instance: VideoViewsManager | |
29 | ||
30 | private videoViewerStats: VideoViewerStats | |
31 | private videoViewerCounters: VideoViewerCounters | |
32 | private videoViews: VideoViews | |
33 | ||
34 | private constructor () { | |
35 | } | |
36 | ||
37 | init () { | |
38 | this.videoViewerStats = new VideoViewerStats() | |
39 | this.videoViewerCounters = new VideoViewerCounters() | |
40 | this.videoViews = new VideoViews() | |
41 | } | |
42 | ||
43 | async processLocalView (options: { | |
44 | video: MVideoImmutable | |
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 | ||
53 | await this.videoViewerStats.addLocalViewer({ video, ip, viewEvent, currentTime }) | |
54 | ||
55 | const successViewer = await this.videoViewerCounters.addLocalViewer({ video, ip }) | |
56 | ||
57 | // Do it after added local viewer to fetch updated information | |
58 | const watchTime = await this.videoViewerStats.getWatchTime(video.id, ip) | |
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 | |
67 | viewerId: string | null | |
68 | viewerExpires?: Date | |
69 | }) { | |
70 | const { video, viewerId, viewerExpires } = options | |
71 | ||
72 | logger.debug('Processing remote view for %s.', video.url, { viewerExpires, viewerId, ...lTags() }) | |
73 | ||
74 | if (viewerExpires) await this.videoViewerCounters.addRemoteViewer({ video, viewerId, viewerExpires }) | |
75 | else await this.videoViews.addRemoteView({ video }) | |
76 | } | |
77 | ||
78 | getViewers (video: MVideo) { | |
79 | return this.videoViewerCounters.getViewers(video) | |
80 | } | |
81 | ||
82 | getTotalViewers (options: { | |
83 | viewerScope: ViewerScope | |
84 | videoScope: VideoScope | |
85 | }) { | |
86 | return this.videoViewerCounters.getTotalViewers(options) | |
87 | } | |
88 | ||
89 | buildViewerExpireTime () { | |
90 | return this.videoViewerCounters.buildViewerExpireTime() | |
91 | } | |
92 | ||
93 | processViewerStats () { | |
94 | return this.videoViewerStats.processViewerStats() | |
95 | } | |
96 | ||
97 | static get Instance () { | |
98 | return this.instance || (this.instance = new this()) | |
99 | } | |
100 | } |