mirror of
https://github.com/Chocobozzz/PeerTube.git
synced 2025-10-06 03:50:26 +02:00
Refactor video views
Introduce viewers attribute for live videos Count views for live videos Reduce delay to see the viewer update for lives Add ability to configure video views buffer interval and view ip expiration
This commit is contained in:
parent
221ee1adc9
commit
51353d9a03
31 changed files with 434 additions and 251 deletions
|
@ -13,6 +13,7 @@ import {
|
|||
RESUMABLE_UPLOAD_SESSION_LIFETIME
|
||||
} from '../initializers/constants'
|
||||
import { CONFIG } from '../initializers/config'
|
||||
import { exists } from '@server/helpers/custom-validators/misc'
|
||||
|
||||
type CachedRoute = {
|
||||
body: string
|
||||
|
@ -119,16 +120,20 @@ class Redis {
|
|||
|
||||
/* ************ Views per IP ************ */
|
||||
|
||||
setIPVideoView (ip: string, videoUUID: string, isLive: boolean) {
|
||||
const lifetime = isLive
|
||||
? VIEW_LIFETIME.LIVE
|
||||
: VIEW_LIFETIME.VIDEO
|
||||
setIPVideoView (ip: string, videoUUID: string) {
|
||||
return this.setValue(this.generateIPViewKey(ip, videoUUID), '1', VIEW_LIFETIME.VIEW)
|
||||
}
|
||||
|
||||
return this.setValue(this.generateViewKey(ip, videoUUID), '1', lifetime)
|
||||
setIPVideoViewer (ip: string, videoUUID: string) {
|
||||
return this.setValue(this.generateIPViewerKey(ip, videoUUID), '1', VIEW_LIFETIME.VIEWER)
|
||||
}
|
||||
|
||||
async doesVideoIPViewExist (ip: string, videoUUID: string) {
|
||||
return this.exists(this.generateViewKey(ip, videoUUID))
|
||||
return this.exists(this.generateIPViewKey(ip, videoUUID))
|
||||
}
|
||||
|
||||
async doesVideoIPViewerExist (ip: string, videoUUID: string) {
|
||||
return this.exists(this.generateIPViewerKey(ip, videoUUID))
|
||||
}
|
||||
|
||||
/* ************ Tracker IP block ************ */
|
||||
|
@ -160,46 +165,85 @@ class Redis {
|
|||
return this.setObject(this.generateCachedRouteKey(req), cached, lifetime)
|
||||
}
|
||||
|
||||
/* ************ Video views ************ */
|
||||
/* ************ Video views stats ************ */
|
||||
|
||||
addVideoView (videoId: number) {
|
||||
const keyIncr = this.generateVideoViewKey(videoId)
|
||||
const keySet = this.generateVideosViewKey()
|
||||
addVideoViewStats (videoId: number) {
|
||||
const { videoKey, setKey } = this.generateVideoViewStatsKeys({ videoId })
|
||||
|
||||
return Promise.all([
|
||||
this.addToSet(keySet, videoId.toString()),
|
||||
this.increment(keyIncr)
|
||||
this.addToSet(setKey, videoId.toString()),
|
||||
this.increment(videoKey)
|
||||
])
|
||||
}
|
||||
|
||||
async getVideoViews (videoId: number, hour: number) {
|
||||
const key = this.generateVideoViewKey(videoId, hour)
|
||||
async getVideoViewsStats (videoId: number, hour: number) {
|
||||
const { videoKey } = this.generateVideoViewStatsKeys({ videoId, hour })
|
||||
|
||||
const valueString = await this.getValue(key)
|
||||
const valueString = await this.getValue(videoKey)
|
||||
const valueInt = parseInt(valueString, 10)
|
||||
|
||||
if (isNaN(valueInt)) {
|
||||
logger.error('Cannot get videos views of video %d in hour %d: views number is NaN (%s).', videoId, hour, valueString)
|
||||
logger.error('Cannot get videos views stats of video %d in hour %d: views number is NaN (%s).', videoId, hour, valueString)
|
||||
return undefined
|
||||
}
|
||||
|
||||
return valueInt
|
||||
}
|
||||
|
||||
async getVideosIdViewed (hour: number) {
|
||||
const key = this.generateVideosViewKey(hour)
|
||||
async listVideosViewedForStats (hour: number) {
|
||||
const { setKey } = this.generateVideoViewStatsKeys({ hour })
|
||||
|
||||
const stringIds = await this.getSet(key)
|
||||
const stringIds = await this.getSet(setKey)
|
||||
return stringIds.map(s => parseInt(s, 10))
|
||||
}
|
||||
|
||||
deleteVideoViews (videoId: number, hour: number) {
|
||||
const keySet = this.generateVideosViewKey(hour)
|
||||
const keyIncr = this.generateVideoViewKey(videoId, hour)
|
||||
deleteVideoViewsStats (videoId: number, hour: number) {
|
||||
const { setKey, videoKey } = this.generateVideoViewStatsKeys({ videoId, hour })
|
||||
|
||||
return Promise.all([
|
||||
this.deleteFromSet(keySet, videoId.toString()),
|
||||
this.deleteKey(keyIncr)
|
||||
this.deleteFromSet(setKey, videoId.toString()),
|
||||
this.deleteKey(videoKey)
|
||||
])
|
||||
}
|
||||
|
||||
/* ************ Local video views buffer ************ */
|
||||
|
||||
addLocalVideoView (videoId: number) {
|
||||
const { videoKey, setKey } = this.generateLocalVideoViewsKeys(videoId)
|
||||
|
||||
return Promise.all([
|
||||
this.addToSet(setKey, videoId.toString()),
|
||||
this.increment(videoKey)
|
||||
])
|
||||
}
|
||||
|
||||
async getLocalVideoViews (videoId: number) {
|
||||
const { videoKey } = this.generateLocalVideoViewsKeys(videoId)
|
||||
|
||||
const valueString = await this.getValue(videoKey)
|
||||
const valueInt = parseInt(valueString, 10)
|
||||
|
||||
if (isNaN(valueInt)) {
|
||||
logger.error('Cannot get videos views of video %d: views number is NaN (%s).', videoId, valueString)
|
||||
return undefined
|
||||
}
|
||||
|
||||
return valueInt
|
||||
}
|
||||
|
||||
async listLocalVideosViewed () {
|
||||
const { setKey } = this.generateLocalVideoViewsKeys()
|
||||
|
||||
const stringIds = await this.getSet(setKey)
|
||||
return stringIds.map(s => parseInt(s, 10))
|
||||
}
|
||||
|
||||
deleteLocalVideoViews (videoId: number) {
|
||||
const { setKey, videoKey } = this.generateLocalVideoViewsKeys(videoId)
|
||||
|
||||
return Promise.all([
|
||||
this.deleteFromSet(setKey, videoId.toString()),
|
||||
this.deleteKey(videoKey)
|
||||
])
|
||||
}
|
||||
|
||||
|
@ -233,16 +277,16 @@ class Redis {
|
|||
return req.method + '-' + req.originalUrl
|
||||
}
|
||||
|
||||
private generateVideosViewKey (hour?: number) {
|
||||
if (!hour) hour = new Date().getHours()
|
||||
|
||||
return `videos-view-h${hour}`
|
||||
private generateLocalVideoViewsKeys (videoId?: Number) {
|
||||
return { setKey: `local-video-views-buffer`, videoKey: `local-video-views-buffer-${videoId}` }
|
||||
}
|
||||
|
||||
private generateVideoViewKey (videoId: number, hour?: number) {
|
||||
if (hour === undefined || hour === null) hour = new Date().getHours()
|
||||
private generateVideoViewStatsKeys (options: { videoId?: number, hour?: number }) {
|
||||
const hour = exists(options.hour)
|
||||
? options.hour
|
||||
: new Date().getHours()
|
||||
|
||||
return `video-view-${videoId}-h${hour}`
|
||||
return { setKey: `videos-view-h${hour}`, videoKey: `video-view-${options.videoId}-h${hour}` }
|
||||
}
|
||||
|
||||
private generateResetPasswordKey (userId: number) {
|
||||
|
@ -253,10 +297,14 @@ class Redis {
|
|||
return 'verify-email-' + userId
|
||||
}
|
||||
|
||||
private generateViewKey (ip: string, videoUUID: string) {
|
||||
private generateIPViewKey (ip: string, videoUUID: string) {
|
||||
return `views-${videoUUID}-${ip}`
|
||||
}
|
||||
|
||||
private generateIPViewerKey (ip: string, videoUUID: string) {
|
||||
return `viewer-${videoUUID}-${ip}`
|
||||
}
|
||||
|
||||
private generateTrackerBlockIPKey (ip: string) {
|
||||
return `tracker-block-ip-${ip}`
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue