aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/lib/redis.ts
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2021-11-09 10:11:20 +0100
committerChocobozzz <chocobozzz@cpy.re>2021-11-09 15:00:31 +0100
commit51353d9a035fb6b81f903a8b5f391292841649fd (patch)
tree75acb6eea5e043bf2e15a6a5a92e9a3c5967b156 /server/lib/redis.ts
parent221ee1adc916684d4881d2a9c4c01954dcde986e (diff)
downloadPeerTube-51353d9a035fb6b81f903a8b5f391292841649fd.tar.gz
PeerTube-51353d9a035fb6b81f903a8b5f391292841649fd.tar.zst
PeerTube-51353d9a035fb6b81f903a8b5f391292841649fd.zip
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
Diffstat (limited to 'server/lib/redis.ts')
-rw-r--r--server/lib/redis.ts112
1 files changed, 80 insertions, 32 deletions
diff --git a/server/lib/redis.ts b/server/lib/redis.ts
index 46617b07e..76b7868e8 100644
--- a/server/lib/redis.ts
+++ b/server/lib/redis.ts
@@ -13,6 +13,7 @@ import {
13 RESUMABLE_UPLOAD_SESSION_LIFETIME 13 RESUMABLE_UPLOAD_SESSION_LIFETIME
14} from '../initializers/constants' 14} from '../initializers/constants'
15import { CONFIG } from '../initializers/config' 15import { CONFIG } from '../initializers/config'
16import { exists } from '@server/helpers/custom-validators/misc'
16 17
17type CachedRoute = { 18type CachedRoute = {
18 body: string 19 body: string
@@ -119,16 +120,20 @@ class Redis {
119 120
120 /* ************ Views per IP ************ */ 121 /* ************ Views per IP ************ */
121 122
122 setIPVideoView (ip: string, videoUUID: string, isLive: boolean) { 123 setIPVideoView (ip: string, videoUUID: string) {
123 const lifetime = isLive 124 return this.setValue(this.generateIPViewKey(ip, videoUUID), '1', VIEW_LIFETIME.VIEW)
124 ? VIEW_LIFETIME.LIVE 125 }
125 : VIEW_LIFETIME.VIDEO
126 126
127 return this.setValue(this.generateViewKey(ip, videoUUID), '1', lifetime) 127 setIPVideoViewer (ip: string, videoUUID: string) {
128 return this.setValue(this.generateIPViewerKey(ip, videoUUID), '1', VIEW_LIFETIME.VIEWER)
128 } 129 }
129 130
130 async doesVideoIPViewExist (ip: string, videoUUID: string) { 131 async doesVideoIPViewExist (ip: string, videoUUID: string) {
131 return this.exists(this.generateViewKey(ip, videoUUID)) 132 return this.exists(this.generateIPViewKey(ip, videoUUID))
133 }
134
135 async doesVideoIPViewerExist (ip: string, videoUUID: string) {
136 return this.exists(this.generateIPViewerKey(ip, videoUUID))
132 } 137 }
133 138
134 /* ************ Tracker IP block ************ */ 139 /* ************ Tracker IP block ************ */
@@ -160,46 +165,85 @@ class Redis {
160 return this.setObject(this.generateCachedRouteKey(req), cached, lifetime) 165 return this.setObject(this.generateCachedRouteKey(req), cached, lifetime)
161 } 166 }
162 167
163 /* ************ Video views ************ */ 168 /* ************ Video views stats ************ */
164 169
165 addVideoView (videoId: number) { 170 addVideoViewStats (videoId: number) {
166 const keyIncr = this.generateVideoViewKey(videoId) 171 const { videoKey, setKey } = this.generateVideoViewStatsKeys({ videoId })
167 const keySet = this.generateVideosViewKey()
168 172
169 return Promise.all([ 173 return Promise.all([
170 this.addToSet(keySet, videoId.toString()), 174 this.addToSet(setKey, videoId.toString()),
171 this.increment(keyIncr) 175 this.increment(videoKey)
172 ]) 176 ])
173 } 177 }
174 178
175 async getVideoViews (videoId: number, hour: number) { 179 async getVideoViewsStats (videoId: number, hour: number) {
176 const key = this.generateVideoViewKey(videoId, hour) 180 const { videoKey } = this.generateVideoViewStatsKeys({ videoId, hour })
177 181
178 const valueString = await this.getValue(key) 182 const valueString = await this.getValue(videoKey)
179 const valueInt = parseInt(valueString, 10) 183 const valueInt = parseInt(valueString, 10)
180 184
181 if (isNaN(valueInt)) { 185 if (isNaN(valueInt)) {
182 logger.error('Cannot get videos views of video %d in hour %d: views number is NaN (%s).', videoId, hour, valueString) 186 logger.error('Cannot get videos views stats of video %d in hour %d: views number is NaN (%s).', videoId, hour, valueString)
183 return undefined 187 return undefined
184 } 188 }
185 189
186 return valueInt 190 return valueInt
187 } 191 }
188 192
189 async getVideosIdViewed (hour: number) { 193 async listVideosViewedForStats (hour: number) {
190 const key = this.generateVideosViewKey(hour) 194 const { setKey } = this.generateVideoViewStatsKeys({ hour })
191 195
192 const stringIds = await this.getSet(key) 196 const stringIds = await this.getSet(setKey)
193 return stringIds.map(s => parseInt(s, 10)) 197 return stringIds.map(s => parseInt(s, 10))
194 } 198 }
195 199
196 deleteVideoViews (videoId: number, hour: number) { 200 deleteVideoViewsStats (videoId: number, hour: number) {
197 const keySet = this.generateVideosViewKey(hour) 201 const { setKey, videoKey } = this.generateVideoViewStatsKeys({ videoId, hour })
198 const keyIncr = this.generateVideoViewKey(videoId, hour) 202
203 return Promise.all([
204 this.deleteFromSet(setKey, videoId.toString()),
205 this.deleteKey(videoKey)
206 ])
207 }
208
209 /* ************ Local video views buffer ************ */
210
211 addLocalVideoView (videoId: number) {
212 const { videoKey, setKey } = this.generateLocalVideoViewsKeys(videoId)
199 213
200 return Promise.all([ 214 return Promise.all([
201 this.deleteFromSet(keySet, videoId.toString()), 215 this.addToSet(setKey, videoId.toString()),
202 this.deleteKey(keyIncr) 216 this.increment(videoKey)
217 ])
218 }
219
220 async getLocalVideoViews (videoId: number) {
221 const { videoKey } = this.generateLocalVideoViewsKeys(videoId)
222
223 const valueString = await this.getValue(videoKey)
224 const valueInt = parseInt(valueString, 10)
225
226 if (isNaN(valueInt)) {
227 logger.error('Cannot get videos views of video %d: views number is NaN (%s).', videoId, valueString)
228 return undefined
229 }
230
231 return valueInt
232 }
233
234 async listLocalVideosViewed () {
235 const { setKey } = this.generateLocalVideoViewsKeys()
236
237 const stringIds = await this.getSet(setKey)
238 return stringIds.map(s => parseInt(s, 10))
239 }
240
241 deleteLocalVideoViews (videoId: number) {
242 const { setKey, videoKey } = this.generateLocalVideoViewsKeys(videoId)
243
244 return Promise.all([
245 this.deleteFromSet(setKey, videoId.toString()),
246 this.deleteKey(videoKey)
203 ]) 247 ])
204 } 248 }
205 249
@@ -233,16 +277,16 @@ class Redis {
233 return req.method + '-' + req.originalUrl 277 return req.method + '-' + req.originalUrl
234 } 278 }
235 279
236 private generateVideosViewKey (hour?: number) { 280 private generateLocalVideoViewsKeys (videoId?: Number) {
237 if (!hour) hour = new Date().getHours() 281 return { setKey: `local-video-views-buffer`, videoKey: `local-video-views-buffer-${videoId}` }
238
239 return `videos-view-h${hour}`
240 } 282 }
241 283
242 private generateVideoViewKey (videoId: number, hour?: number) { 284 private generateVideoViewStatsKeys (options: { videoId?: number, hour?: number }) {
243 if (hour === undefined || hour === null) hour = new Date().getHours() 285 const hour = exists(options.hour)
286 ? options.hour
287 : new Date().getHours()
244 288
245 return `video-view-${videoId}-h${hour}` 289 return { setKey: `videos-view-h${hour}`, videoKey: `video-view-${options.videoId}-h${hour}` }
246 } 290 }
247 291
248 private generateResetPasswordKey (userId: number) { 292 private generateResetPasswordKey (userId: number) {
@@ -253,10 +297,14 @@ class Redis {
253 return 'verify-email-' + userId 297 return 'verify-email-' + userId
254 } 298 }
255 299
256 private generateViewKey (ip: string, videoUUID: string) { 300 private generateIPViewKey (ip: string, videoUUID: string) {
257 return `views-${videoUUID}-${ip}` 301 return `views-${videoUUID}-${ip}`
258 } 302 }
259 303
304 private generateIPViewerKey (ip: string, videoUUID: string) {
305 return `viewer-${videoUUID}-${ip}`
306 }
307
260 private generateTrackerBlockIPKey (ip: string) { 308 private generateTrackerBlockIPKey (ip: string) {
261 return `tracker-block-ip-${ip}` 309 return `tracker-block-ip-${ip}`
262 } 310 }