]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/commitdiff
Improve viewer counter
authorChocobozzz <me@florianbigard.com>
Wed, 6 Apr 2022 06:50:43 +0000 (08:50 +0200)
committerChocobozzz <chocobozzz@cpy.re>
Fri, 15 Apr 2022 07:49:35 +0000 (09:49 +0200)
More precise, avoid weird decrease, reuse an id to federate viewers

17 files changed:
server/controllers/api/server/debug.ts
server/controllers/api/videos/view.ts
server/initializers/constants.ts
server/lib/activitypub/process/process-view.ts
server/lib/activitypub/send/send-view.ts
server/lib/activitypub/url.ts
server/lib/redis.ts
server/lib/views/shared/index.ts
server/lib/views/shared/video-viewer-counters.ts [new file with mode: 0644]
server/lib/views/shared/video-viewer-stats.ts [moved from server/lib/views/shared/video-viewers.ts with 62% similarity]
server/lib/views/shared/video-views.ts
server/lib/views/video-views-manager.ts
server/tests/api/check-params/views.ts
server/tests/api/views/video-views-counter.ts
server/tests/api/views/video-views-overall-stats.ts
server/tests/api/views/video-views-retention-stats.ts
server/tests/api/views/video-views-timeserie-stats.ts

index 6b6ff027c72fe75d924d9d41f88df01852461d30..e09510dc3754cfb08657dcb03a1da188a91aba53 100644 (file)
@@ -43,7 +43,7 @@ async function runCommand (req: express.Request, res: express.Response) {
   const processors: { [id in SendDebugCommand['command']]: () => Promise<any> } = {
     'remove-dandling-resumable-uploads': () => RemoveDanglingResumableUploadsScheduler.Instance.execute(),
     'process-video-views-buffer': () => VideoViewsBufferScheduler.Instance.execute(),
-    'process-video-viewers': () => VideoViewsManager.Instance.processViewers()
+    'process-video-viewers': () => VideoViewsManager.Instance.processViewerStats()
   }
 
   await processors[body.command]()
index e28cf371a9111b9adf22b72cbd7a071523645d93..db1091f2dc6a4770a1fb2dfb4b1cc43c99b2c350 100644 (file)
@@ -1,8 +1,6 @@
 import express from 'express'
-import { sendView } from '@server/lib/activitypub/send/send-view'
 import { Hooks } from '@server/lib/plugins/hooks'
 import { VideoViewsManager } from '@server/lib/views/video-views-manager'
-import { getServerActor } from '@server/models/application/application'
 import { MVideoId } from '@server/types/models'
 import { HttpStatusCode, VideoView } from '@shared/models'
 import { asyncMiddleware, methodsValidator, openapiOperationDoc, optionalAuthenticate, videoViewValidator } from '../../../middlewares'
@@ -33,7 +31,7 @@ async function viewVideo (req: express.Request, res: express.Response) {
   const body = req.body as VideoView
 
   const ip = req.ip
-  const { successView, successViewer } = await VideoViewsManager.Instance.processLocalView({
+  const { successView } = await VideoViewsManager.Instance.processLocalView({
     video,
     ip,
     currentTime: body.currentTime,
@@ -41,15 +39,9 @@ async function viewVideo (req: express.Request, res: express.Response) {
   })
 
   if (successView) {
-    await sendView({ byActor: await getServerActor(), video, type: 'view' })
-
     Hooks.runAction('action:api.video.viewed', { video: video, ip, req, res })
   }
 
-  if (successViewer) {
-    await sendView({ byActor: await getServerActor(), video, type: 'viewer' })
-  }
-
   await updateUserHistoryIfNeeded(body, video, res)
 
   return res.status(HttpStatusCode.NO_CONTENT_204).end()
index 9afbc5aeaf939d6c4382c4338a4e244d2a770969..a4d8d8fe72f256a6da7c1b52e30c3858bda2a548 100644 (file)
@@ -367,7 +367,7 @@ const CONSTRAINTS_FIELDS = {
 
 const VIEW_LIFETIME = {
   VIEW: CONFIG.VIEWS.VIDEOS.IP_VIEW_EXPIRATION,
-  VIEWER_COUNTER: 60000 * 5, // 5 minutes
+  VIEWER_COUNTER: 60000 * 1, // 1 minute
   VIEWER_STATS: 60000 * 60 // 1 hour
 }
 
index bad079843322fc7ba3a4147bc976bba43d369f77..e49506d8271b7041a8c39238c7d382eaa2e95561 100644 (file)
@@ -32,7 +32,7 @@ async function processCreateView (activity: ActivityView, byActor: MActorSignatu
     ? new Date(activity.expires)
     : undefined
 
-  await VideoViewsManager.Instance.processRemoteView({ video, viewerExpires })
+  await VideoViewsManager.Instance.processRemoteView({ video, viewerId: activity.id, viewerExpires })
 
   if (video.isOwned()) {
     // Forward the view but don't resend the activity to the sender
index 1088bf2582d3456271ec822a966122e8400369ca..25a20ec6decad3a31d7cf7514bd3cc6c619ccf95 100644 (file)
@@ -13,14 +13,15 @@ async function sendView (options: {
   byActor: MActorLight
   type: ViewType
   video: MVideoImmutable
+  viewerIdentifier: string
   transaction?: Transaction
 }) {
-  const { byActor, type, video, transaction } = options
+  const { byActor, type, video, viewerIdentifier, transaction } = options
 
   logger.info('Creating job to send %s of %s.', type, video.url)
 
   const activityBuilder = (audience: ActivityAudience) => {
-    const url = getLocalVideoViewActivityPubUrl(byActor, video)
+    const url = getLocalVideoViewActivityPubUrl(byActor, video, viewerIdentifier)
 
     return buildViewActivity({ url, byActor, video, audience, type })
   }
index 8443fef4cfd1d6b8731c3ac4fa8b397563a447f1..2f68f7a17017d8925d7454c47c176835bddb8d4f 100644 (file)
@@ -56,8 +56,8 @@ function getLocalAbuseActivityPubUrl (abuse: MAbuseId) {
   return WEBSERVER.URL + '/admin/abuses/' + abuse.id
 }
 
-function getLocalVideoViewActivityPubUrl (byActor: MActorUrl, video: MVideoId) {
-  return byActor.url + '/views/videos/' + video.id + '/' + new Date().toISOString()
+function getLocalVideoViewActivityPubUrl (byActor: MActorUrl, video: MVideoId, viewerIdentifier: string) {
+  return byActor.url + '/views/videos/' + video.id + '/' + viewerIdentifier
 }
 
 function getLocalVideoViewerActivityPubUrl (stats: MLocalVideoViewer) {
index f9cea57cde3096b36b08e7d4656648cc8f06c2ed..d052de7863c412198213383bd9931703f2b0a7b6 100644 (file)
@@ -145,18 +145,10 @@ class Redis {
     return this.setValue(this.generateIPViewKey(ip, videoUUID), '1', VIEW_LIFETIME.VIEW)
   }
 
-  setIPVideoViewer (ip: string, videoUUID: string) {
-    return this.setValue(this.generateIPViewerKey(ip, videoUUID), '1', VIEW_LIFETIME.VIEWER_COUNTER)
-  }
-
   async doesVideoIPViewExist (ip: string, videoUUID: string) {
     return this.exists(this.generateIPViewKey(ip, videoUUID))
   }
 
-  async doesVideoIPViewerExist (ip: string, videoUUID: string) {
-    return this.exists(this.generateIPViewerKey(ip, videoUUID))
-  }
-
   /* ************ Tracker IP block ************ */
 
   setTrackerBlockIP (ip: string) {
@@ -361,10 +353,6 @@ class Redis {
     return `views-${videoUUID}-${ip}`
   }
 
-  private generateIPViewerKey (ip: string, videoUUID: string) {
-    return `viewer-${videoUUID}-${ip}`
-  }
-
   private generateTrackerBlockIPKey (ip: string) {
     return `tracker-block-ip-${ip}`
   }
index dd510f4e27c86c734edc37fb437c5a64c6cfcc72..13947118352a70849cd4f0de33609dfdd4a75695 100644 (file)
@@ -1,2 +1,3 @@
-export * from './video-viewers'
+export * from './video-viewer-counters'
+export * from './video-viewer-stats'
 export * from './video-views'
diff --git a/server/lib/views/shared/video-viewer-counters.ts b/server/lib/views/shared/video-viewer-counters.ts
new file mode 100644 (file)
index 0000000..941b62e
--- /dev/null
@@ -0,0 +1,176 @@
+
+import { isTestInstance } from '@server/helpers/core-utils'
+import { logger, loggerTagsFactory } from '@server/helpers/logger'
+import { VIEW_LIFETIME } from '@server/initializers/constants'
+import { sendView } from '@server/lib/activitypub/send/send-view'
+import { PeerTubeSocket } from '@server/lib/peertube-socket'
+import { getServerActor } from '@server/models/application/application'
+import { VideoModel } from '@server/models/video/video'
+import { MVideo } from '@server/types/models'
+import { buildUUID, sha256 } from '@shared/extra-utils'
+
+const lTags = loggerTagsFactory('views')
+
+type Viewer = {
+  expires: number
+  id: string
+  lastFederation?: number
+}
+
+export class VideoViewerCounters {
+
+  // expires is new Date().getTime()
+  private readonly viewersPerVideo = new Map<number, Viewer[]>()
+  private readonly idToViewer = new Map<string, Viewer>()
+
+  private readonly salt = buildUUID()
+
+  private processingViewerCounters = false
+
+  constructor () {
+    setInterval(() => this.cleanViewerCounters(), VIEW_LIFETIME.VIEWER_COUNTER)
+  }
+
+  // ---------------------------------------------------------------------------
+
+  async addLocalViewer (options: {
+    video: MVideo
+    ip: string
+  }) {
+    const { video, ip } = options
+
+    logger.debug('Adding local viewer to video viewers counter %s.', video.uuid, { ...lTags(video.uuid) })
+
+    const viewerId = this.generateViewerId(ip, video.uuid)
+    const viewer = this.idToViewer.get(viewerId)
+
+    if (viewer) {
+      viewer.expires = this.buildViewerExpireTime()
+      await this.federateViewerIfNeeded(video, viewer)
+
+      return false
+    }
+
+    const newViewer = await this.addViewerToVideo({ viewerId, video })
+    await this.federateViewerIfNeeded(video, newViewer)
+
+    return true
+  }
+
+  async addRemoteViewer (options: {
+    video: MVideo
+    viewerId: string
+    viewerExpires: Date
+  }) {
+    const { video, viewerExpires, viewerId } = options
+
+    logger.debug('Adding remote viewer to video %s.', video.uuid, { ...lTags(video.uuid) })
+
+    await this.addViewerToVideo({ video, viewerExpires, viewerId })
+
+    return true
+  }
+
+  // ---------------------------------------------------------------------------
+
+  getViewers (video: MVideo) {
+    const viewers = this.viewersPerVideo.get(video.id)
+    if (!viewers) return 0
+
+    return viewers.length
+  }
+
+  buildViewerExpireTime () {
+    return new Date().getTime() + VIEW_LIFETIME.VIEWER_COUNTER
+  }
+
+  // ---------------------------------------------------------------------------
+
+  private async addViewerToVideo (options: {
+    video: MVideo
+    viewerId: string
+    viewerExpires?: Date
+  }) {
+    const { video, viewerExpires, viewerId } = options
+
+    let watchers = this.viewersPerVideo.get(video.id)
+
+    if (!watchers) {
+      watchers = []
+      this.viewersPerVideo.set(video.id, watchers)
+    }
+
+    const expires = viewerExpires
+      ? viewerExpires.getTime()
+      : this.buildViewerExpireTime()
+
+    const viewer = { id: viewerId, expires }
+    watchers.push(viewer)
+
+    this.idToViewer.set(viewerId, viewer)
+
+    await this.notifyClients(video.id, watchers.length)
+
+    return viewer
+  }
+
+  private async cleanViewerCounters () {
+    if (this.processingViewerCounters) return
+    this.processingViewerCounters = true
+
+    if (!isTestInstance()) logger.info('Cleaning video viewers.', lTags())
+
+    try {
+      for (const videoId of this.viewersPerVideo.keys()) {
+        const notBefore = new Date().getTime()
+
+        const viewers = this.viewersPerVideo.get(videoId)
+
+        // Only keep not expired viewers
+        const newViewers: Viewer[] = []
+
+        // Filter new viewers
+        for (const viewer of viewers) {
+          if (viewer.expires > notBefore) {
+            newViewers.push(viewer)
+          } else {
+            this.idToViewer.delete(viewer.id)
+          }
+        }
+
+        if (newViewers.length === 0) this.viewersPerVideo.delete(videoId)
+        else this.viewersPerVideo.set(videoId, newViewers)
+
+        await this.notifyClients(videoId, newViewers.length)
+      }
+    } catch (err) {
+      logger.error('Error in video clean viewers scheduler.', { err, ...lTags() })
+    }
+
+    this.processingViewerCounters = false
+  }
+
+  private async notifyClients (videoId: string | number, viewersLength: number) {
+    const video = await VideoModel.loadImmutableAttributes(videoId)
+    if (!video) return
+
+    PeerTubeSocket.Instance.sendVideoViewsUpdate(video, viewersLength)
+
+    logger.debug('Video viewers update for %s is %d.', video.url, viewersLength, lTags())
+  }
+
+  private generateViewerId (ip: string, videoUUID: string) {
+    return sha256(this.salt + '-' + ip + '-' + videoUUID)
+  }
+
+  private async federateViewerIfNeeded (video: MVideo, viewer: Viewer) {
+    // Federate the viewer if it's been a "long" time we did not
+    const now = new Date().getTime()
+    const federationLimit = now - (VIEW_LIFETIME.VIEWER_COUNTER / 2)
+
+    if (viewer.lastFederation && viewer.lastFederation > federationLimit) return
+
+    await sendView({ byActor: await getServerActor(), video, type: 'viewer', viewerIdentifier: viewer.id })
+    viewer.lastFederation = now
+  }
+}
similarity index 62%
rename from server/lib/views/shared/video-viewers.ts
rename to server/lib/views/shared/video-viewer-stats.ts
index 4dad1f0e88f7c734854e80830ba5bd3b2e7ddb21..fd66fd5c7eee7b82a280775c3e12c01ed99889ff 100644 (file)
@@ -6,7 +6,6 @@ import { MAX_LOCAL_VIEWER_WATCH_SECTIONS, VIEW_LIFETIME } from '@server/initiali
 import { sequelizeTypescript } from '@server/initializers/database'
 import { sendCreateWatchAction } from '@server/lib/activitypub/send'
 import { getLocalVideoViewerActivityPubUrl } from '@server/lib/activitypub/url'
-import { PeerTubeSocket } from '@server/lib/peertube-socket'
 import { Redis } from '@server/lib/redis'
 import { VideoModel } from '@server/models/video/video'
 import { LocalVideoViewerModel } from '@server/models/view/local-video-viewer'
@@ -32,39 +31,15 @@ type LocalViewerStats = {
   videoId: number
 }
 
-export class VideoViewers {
-
-  // Values are Date().getTime()
-  private readonly viewersPerVideo = new Map<number, number[]>()
-
-  private processingViewerCounters = false
-  private processingViewerStats = false
+export class VideoViewerStats {
+  private processingViewersStats = false
 
   constructor () {
-    setInterval(() => this.cleanViewerCounters(), VIEW_LIFETIME.VIEWER_COUNTER)
-
     setInterval(() => this.processViewerStats(), VIEW_LIFETIME.VIEWER_STATS)
   }
 
   // ---------------------------------------------------------------------------
 
-  getViewers (video: MVideo) {
-    const viewers = this.viewersPerVideo.get(video.id)
-    if (!viewers) return 0
-
-    return viewers.length
-  }
-
-  buildViewerExpireTime () {
-    return new Date().getTime() + VIEW_LIFETIME.VIEWER_COUNTER
-  }
-
-  async getWatchTime (videoId: number, ip: string) {
-    const stats: LocalViewerStats = await Redis.Instance.getLocalVideoViewer({ ip, videoId })
-
-    return stats?.watchTime || 0
-  }
-
   async addLocalViewer (options: {
     video: MVideo
     currentTime: number
@@ -73,51 +48,20 @@ export class VideoViewers {
   }) {
     const { video, ip, viewEvent, currentTime } = options
 
-    logger.debug('Adding local viewer to video %s.', video.uuid, { currentTime, viewEvent, ...lTags(video.uuid) })
-
-    await this.updateLocalViewerStats({ video, viewEvent, currentTime, ip })
-
-    const viewExists = await Redis.Instance.doesVideoIPViewerExist(ip, video.uuid)
-    if (viewExists) return false
+    logger.debug('Adding local viewer to video stats %s.', video.uuid, { currentTime, viewEvent, ...lTags(video.uuid) })
 
-    await Redis.Instance.setIPVideoViewer(ip, video.uuid)
-
-    return this.addViewerToVideo({ video })
+    return this.updateLocalViewerStats({ video, viewEvent, currentTime, ip })
   }
 
-  async addRemoteViewer (options: {
-    video: MVideo
-    viewerExpires: Date
-  }) {
-    const { video, viewerExpires } = options
+  // ---------------------------------------------------------------------------
 
-    logger.debug('Adding remote viewer to video %s.', video.uuid, { ...lTags(video.uuid) })
+  async getWatchTime (videoId: number, ip: string) {
+    const stats: LocalViewerStats = await Redis.Instance.getLocalVideoViewer({ ip, videoId })
 
-    return this.addViewerToVideo({ video, viewerExpires })
+    return stats?.watchTime || 0
   }
 
-  private async addViewerToVideo (options: {
-    video: MVideo
-    viewerExpires?: Date
-  }) {
-    const { video, viewerExpires } = options
-
-    let watchers = this.viewersPerVideo.get(video.id)
-
-    if (!watchers) {
-      watchers = []
-      this.viewersPerVideo.set(video.id, watchers)
-    }
-
-    const expiration = viewerExpires
-      ? viewerExpires.getTime()
-      : this.buildViewerExpireTime()
-
-    watchers.push(expiration)
-    await this.notifyClients(video.id, watchers.length)
-
-    return true
-  }
+  // ---------------------------------------------------------------------------
 
   private async updateLocalViewerStats (options: {
     video: MVideo
@@ -170,45 +114,9 @@ export class VideoViewers {
     await Redis.Instance.setLocalVideoViewer(ip, video.id, stats)
   }
 
-  private async cleanViewerCounters () {
-    if (this.processingViewerCounters) return
-    this.processingViewerCounters = true
-
-    if (!isTestInstance()) logger.info('Cleaning video viewers.', lTags())
-
-    try {
-      for (const videoId of this.viewersPerVideo.keys()) {
-        const notBefore = new Date().getTime()
-
-        const viewers = this.viewersPerVideo.get(videoId)
-
-        // Only keep not expired viewers
-        const newViewers = viewers.filter(w => w > notBefore)
-
-        if (newViewers.length === 0) this.viewersPerVideo.delete(videoId)
-        else this.viewersPerVideo.set(videoId, newViewers)
-
-        await this.notifyClients(videoId, newViewers.length)
-      }
-    } catch (err) {
-      logger.error('Error in video clean viewers scheduler.', { err, ...lTags() })
-    }
-
-    this.processingViewerCounters = false
-  }
-
-  private async notifyClients (videoId: string | number, viewersLength: number) {
-    const video = await VideoModel.loadImmutableAttributes(videoId)
-    if (!video) return
-
-    PeerTubeSocket.Instance.sendVideoViewsUpdate(video, viewersLength)
-
-    logger.debug('Video viewers update for %s is %d.', video.url, viewersLength, lTags())
-  }
-
   async processViewerStats () {
-    if (this.processingViewerStats) return
-    this.processingViewerStats = true
+    if (this.processingViewersStats) return
+    this.processingViewersStats = true
 
     if (!isTestInstance()) logger.info('Processing viewer statistics.', lTags())
 
@@ -245,7 +153,7 @@ export class VideoViewers {
       logger.error('Error in video save viewers stats scheduler.', { err, ...lTags() })
     }
 
-    this.processingViewerStats = false
+    this.processingViewersStats = false
   }
 
   private async saveViewerStats (video: MVideo, stats: LocalViewerStats, transaction: Transaction) {
index 19250f99360202c91bddefbdb4577bccc54274bc..275f7a014e7e4776042a0a34f73abde321a82146 100644 (file)
@@ -1,5 +1,8 @@
 import { logger, loggerTagsFactory } from '@server/helpers/logger'
+import { sendView } from '@server/lib/activitypub/send/send-view'
+import { getServerActor } from '@server/models/application/application'
 import { MVideo } from '@server/types/models'
+import { buildUUID } from '@shared/extra-utils'
 import { Redis } from '../../redis'
 
 const lTags = loggerTagsFactory('views')
@@ -24,6 +27,8 @@ export class VideoViews {
 
     await this.addView(video)
 
+    await sendView({ byActor: await getServerActor(), video, type: 'view', viewerIdentifier: buildUUID() })
+
     return true
   }
 
@@ -39,6 +44,8 @@ export class VideoViews {
     return true
   }
 
+  // ---------------------------------------------------------------------------
+
   private async addView (video: MVideo) {
     const promises: Promise<any>[] = []
 
index 9382fb482401fb4441cb1f3dbf34430620dd1ade..ea3b35c6cec68ff60802c1dc5920a03cf689a6e8 100644 (file)
@@ -1,7 +1,7 @@
 import { logger, loggerTagsFactory } from '@server/helpers/logger'
 import { MVideo } from '@server/types/models'
 import { VideoViewEvent } from '@shared/models'
-import { VideoViewers, VideoViews } from './shared'
+import { VideoViewerCounters, VideoViewerStats, VideoViews } from './shared'
 
 /**
  * If processing a local view:
@@ -27,14 +27,16 @@ export class VideoViewsManager {
 
   private static instance: VideoViewsManager
 
-  private videoViewers: VideoViewers
+  private videoViewerStats: VideoViewerStats
+  private videoViewerCounters: VideoViewerCounters
   private videoViews: VideoViews
 
   private constructor () {
   }
 
   init () {
-    this.videoViewers = new VideoViewers()
+    this.videoViewerStats = new VideoViewerStats()
+    this.videoViewerCounters = new VideoViewerCounters()
     this.videoViews = new VideoViews()
   }
 
@@ -48,10 +50,12 @@ export class VideoViewsManager {
 
     logger.debug('Processing local view for %s and ip %s.', video.url, ip, lTags())
 
-    const successViewer = await this.videoViewers.addLocalViewer({ video, ip, viewEvent, currentTime })
+    await this.videoViewerStats.addLocalViewer({ video, ip, viewEvent, currentTime })
+
+    const successViewer = await this.videoViewerCounters.addLocalViewer({ video, ip })
 
     // Do it after added local viewer to fetch updated information
-    const watchTime = await this.videoViewers.getWatchTime(video.id, ip)
+    const watchTime = await this.videoViewerStats.getWatchTime(video.id, ip)
 
     const successView = await this.videoViews.addLocalView({ video, watchTime, ip })
 
@@ -60,26 +64,27 @@ export class VideoViewsManager {
 
   async processRemoteView (options: {
     video: MVideo
+    viewerId: string | null
     viewerExpires?: Date
   }) {
-    const { video, viewerExpires } = options
+    const { video, viewerId, viewerExpires } = options
 
-    logger.debug('Processing remote view for %s.', video.url, { viewerExpires, ...lTags() })
+    logger.debug('Processing remote view for %s.', video.url, { viewerExpires, viewerId, ...lTags() })
 
-    if (viewerExpires) await this.videoViewers.addRemoteViewer({ video, viewerExpires })
+    if (viewerExpires) await this.videoViewerCounters.addRemoteViewer({ video, viewerId, viewerExpires })
     else await this.videoViews.addRemoteView({ video })
   }
 
   getViewers (video: MVideo) {
-    return this.videoViewers.getViewers(video)
+    return this.videoViewerCounters.getViewers(video)
   }
 
   buildViewerExpireTime () {
-    return this.videoViewers.buildViewerExpireTime()
+    return this.videoViewerCounters.buildViewerExpireTime()
   }
 
-  processViewers () {
-    return this.videoViewers.processViewerStats()
+  processViewerStats () {
+    return this.videoViewerStats.processViewerStats()
   }
 
   static get Instance () {
index 185b04af1c3747f0bd86c7548365cd0c387aa5e7..ca4752345ed95fb948c99efd266e87fe3a4bdf6a 100644 (file)
@@ -19,7 +19,7 @@ describe('Test videos views', function () {
   let userAccessToken: string
 
   before(async function () {
-    this.timeout(30000)
+    this.timeout(120000)
 
     servers = await createMultipleServers(2)
     await setAccessTokensToServers(servers)
index b68aaa3504f08cf79659112622bb39bd9297f07b..b8969d52dbca9c0a0628d760cecca3974205731b 100644 (file)
@@ -57,10 +57,11 @@ describe('Test video views/viewers counters', function () {
     })
 
     it('Should not view again this video with the same IP', async function () {
-      await servers[0].views.simulateViewer({ id: videoUUID, currentTimes: [ 1, 4 ] })
+      await servers[0].views.simulateViewer({ id: videoUUID, xForwardedFor: '0.0.0.1,127.0.0.1', currentTimes: [ 1, 4 ] })
+      await servers[0].views.simulateViewer({ id: videoUUID, xForwardedFor: '0.0.0.1,127.0.0.1', currentTimes: [ 1, 4 ] })
       await processViewsBuffer(servers)
 
-      await checkCounter('views', videoUUID, 1)
+      await checkCounter('views', videoUUID, 2)
     })
 
     it('Should view the video from server 2 and send the event', async function () {
@@ -68,7 +69,7 @@ describe('Test video views/viewers counters', function () {
       await waitJobs(servers)
       await processViewsBuffer(servers)
 
-      await checkCounter('views', videoUUID, 2)
+      await checkCounter('views', videoUUID, 3)
     })
   })
 
@@ -78,7 +79,7 @@ describe('Test video views/viewers counters', function () {
     let command: FfmpegCommand
 
     before(async function () {
-      this.timeout(60000);
+      this.timeout(120000);
 
       ({ vodVideoId, liveVideoId, ffmpegCommand: command } = await prepareViewsVideos({ servers, live: true, vod: true }))
     })
index 22761d6ec656a8b24628845d7f61409772b9b53e..d8de73cbcec65c084c3beff79386f27a06934469 100644 (file)
@@ -21,7 +21,7 @@ describe('Test views overall stats', function () {
     let vodVideoId: string
 
     before(async function () {
-      this.timeout(60000);
+      this.timeout(120000);
 
       ({ vodVideoId } = await prepareViewsVideos({ servers, live: false, vod: true }))
     })
@@ -74,7 +74,7 @@ describe('Test views overall stats', function () {
     let command: FfmpegCommand
 
     before(async function () {
-      this.timeout(60000);
+      this.timeout(120000);
 
       ({ vodVideoId, liveVideoId, ffmpegCommand: command } = await prepareViewsVideos({ servers, live: true, vod: true }))
     })
@@ -189,7 +189,7 @@ describe('Test views overall stats', function () {
     let videoUUID: string
 
     before(async function () {
-      this.timeout(60000);
+      this.timeout(120000);
 
       ({ vodVideoId: videoUUID } = await prepareViewsVideos({ servers, live: true, vod: true }))
     })
index 98be7bfdb37433e602155184cb4794f389cc9b26..a27141d6850e0e3bffc6d6ad29c7117ac10f242a 100644 (file)
@@ -20,7 +20,7 @@ describe('Test views retention stats', function () {
     let vodVideoId: string
 
     before(async function () {
-      this.timeout(60000);
+      this.timeout(120000);
 
       ({ vodVideoId } = await prepareViewsVideos({ servers, live: false, vod: true }))
     })
index 98c041cdfae1b2f74fedc175d0bdf2cb595b550f..858edeff7eb58ee3988ca2b3ba0d4d060e462599 100644 (file)
@@ -24,7 +24,7 @@ describe('Test views timeserie stats', function () {
     let vodVideoId: string
 
     before(async function () {
-      this.timeout(60000);
+      this.timeout(120000);
 
       ({ vodVideoId } = await prepareViewsVideos({ servers, live: false, vod: true }))
     })
@@ -63,7 +63,7 @@ describe('Test views timeserie stats', function () {
     }
 
     before(async function () {
-      this.timeout(60000);
+      this.timeout(120000);
 
       ({ vodVideoId, liveVideoId, ffmpegCommand: command } = await prepareViewsVideos({ servers, live: true, vod: true }))
     })