2022-03-24 07:36:47 -05:00
|
|
|
import { logger, loggerTagsFactory } from '@server/helpers/logger'
|
|
|
|
import { MVideo } from '@server/types/models'
|
|
|
|
import { VideoViewEvent } from '@shared/models'
|
|
|
|
import { VideoViewers, VideoViews } from './shared'
|
|
|
|
|
2022-04-05 08:15:09 -05:00
|
|
|
/**
|
|
|
|
* If processing a local view:
|
|
|
|
* - We update viewer information (segments watched, watch time etc)
|
|
|
|
* - We add +1 to video viewers counter if this is a new viewer
|
|
|
|
* - We add +1 to video views counter if this is a new view and if the user watched enough seconds
|
|
|
|
* - We send AP message to notify about this viewer and this view
|
|
|
|
* - We update last video time for the user if authenticated
|
|
|
|
*
|
|
|
|
* If processing a remote view:
|
|
|
|
* - We add +1 to video viewers counter
|
|
|
|
* - We add +1 to video views counter
|
|
|
|
*
|
|
|
|
* A viewer is a someone that watched one or multiple sections of a video
|
|
|
|
* A viewer that watched only a few seconds of a video may not increment the video views counter
|
|
|
|
* Viewers statistics are sent to origin instance using the `WatchAction` ActivityPub object
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2022-03-24 07:36:47 -05:00
|
|
|
const lTags = loggerTagsFactory('views')
|
|
|
|
|
|
|
|
export class VideoViewsManager {
|
|
|
|
|
|
|
|
private static instance: VideoViewsManager
|
|
|
|
|
|
|
|
private videoViewers: VideoViewers
|
|
|
|
private videoViews: VideoViews
|
|
|
|
|
|
|
|
private constructor () {
|
|
|
|
}
|
|
|
|
|
|
|
|
init () {
|
|
|
|
this.videoViewers = new VideoViewers()
|
|
|
|
this.videoViews = new VideoViews()
|
|
|
|
}
|
|
|
|
|
|
|
|
async processLocalView (options: {
|
|
|
|
video: MVideo
|
|
|
|
currentTime: number
|
|
|
|
ip: string | null
|
|
|
|
viewEvent?: VideoViewEvent
|
|
|
|
}) {
|
|
|
|
const { video, ip, viewEvent, currentTime } = options
|
|
|
|
|
|
|
|
logger.debug('Processing local view for %s and ip %s.', video.url, ip, lTags())
|
|
|
|
|
|
|
|
const successViewer = await this.videoViewers.addLocalViewer({ video, ip, viewEvent, currentTime })
|
|
|
|
|
|
|
|
// Do it after added local viewer to fetch updated information
|
|
|
|
const watchTime = await this.videoViewers.getWatchTime(video.id, ip)
|
|
|
|
|
|
|
|
const successView = await this.videoViews.addLocalView({ video, watchTime, ip })
|
|
|
|
|
|
|
|
return { successView, successViewer }
|
|
|
|
}
|
|
|
|
|
|
|
|
async processRemoteView (options: {
|
|
|
|
video: MVideo
|
|
|
|
viewerExpires?: Date
|
|
|
|
}) {
|
|
|
|
const { video, viewerExpires } = options
|
|
|
|
|
|
|
|
logger.debug('Processing remote view for %s.', video.url, { viewerExpires, ...lTags() })
|
|
|
|
|
|
|
|
if (viewerExpires) await this.videoViewers.addRemoteViewer({ video, viewerExpires })
|
|
|
|
else await this.videoViews.addRemoteView({ video })
|
|
|
|
}
|
|
|
|
|
|
|
|
getViewers (video: MVideo) {
|
|
|
|
return this.videoViewers.getViewers(video)
|
|
|
|
}
|
|
|
|
|
|
|
|
buildViewerExpireTime () {
|
|
|
|
return this.videoViewers.buildViewerExpireTime()
|
|
|
|
}
|
|
|
|
|
|
|
|
processViewers () {
|
|
|
|
return this.videoViewers.processViewerStats()
|
|
|
|
}
|
|
|
|
|
|
|
|
static get Instance () {
|
|
|
|
return this.instance || (this.instance = new this())
|
|
|
|
}
|
|
|
|
}
|