File size: 4,542 Bytes
f70dd7e
 
 
8f2b05f
1185ec1
38d787b
f70dd7e
29f166e
f70dd7e
 
 
 
 
 
29f166e
f70dd7e
29f166e
f70dd7e
 
29f166e
 
 
 
 
 
 
 
f70dd7e
 
29f166e
f70dd7e
29f166e
 
 
 
 
 
 
 
f70dd7e
 
 
 
 
 
 
 
 
29f166e
 
 
f70dd7e
 
 
 
 
 
 
 
 
 
8f2b05f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
99dcdd2
8f2b05f
99dcdd2
 
8f2b05f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
e657a34
 
 
 
 
 
be5bb51
e657a34
 
 
 
8f2b05f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
"use server"

import { developerMode } from "@/app/config"
import { WhoAmIUser, whoAmI } from "@/huggingface/hub/src"
import { VideoRating } from "@/types/general"
import { redis } from "./redis";

export async function getStatsForVideos(videoIds: string[]): Promise<Record<string, { numberOfViews: number; numberOfLikes: number; numberOfDislikes: number}>> {
  if (!Array.isArray(videoIds)) {
    return {}
  }

  try {
    
    const stats: Record<string, { numberOfViews: number; numberOfLikes: number; numberOfDislikes: number; }> = {}

    const listOfRedisIDs: string[] = []

    for (const videoId of videoIds) {
      listOfRedisIDs.push(`videos:${videoId}:stats:views`)
      listOfRedisIDs.push(`videos:${videoId}:stats:likes`)
      listOfRedisIDs.push(`videos:${videoId}:stats:dislikes`)
      stats[videoId] = {
        numberOfViews: 0,
        numberOfLikes: 0,
        numberOfDislikes: 0,
      }
    }

    const listOfRedisValues = await redis.mget<number[]>(...listOfRedisIDs)

    let v = 0
    for (let i = 0; i < listOfRedisValues.length; i += 3) {
      stats[videoIds[v++]] = {
        numberOfViews: listOfRedisValues[i] || 0,
        numberOfLikes: listOfRedisValues[i + 1] || 0,
        numberOfDislikes: listOfRedisValues[i + 2] || 0
      }
    }

    return stats
  } catch (err) {
    return {}
  }
}

export async function watchVideo(videoId: string): Promise<number> {
  if (developerMode) {
    const stats = await getStatsForVideos([videoId])

    return stats[videoId].numberOfViews
  }

  try {
    const result = await redis.incr(`videos:${videoId}:stats:views`)
    
    return result
  } catch (err) {
    return 0
  }
}

export async function getVideoRating(videoId: string, apiKey?: string): Promise<VideoRating> {
  let numberOfLikes = 0
  let numberOfDislikes = 0
  let isLikedByUser = false
  let isDislikedByUser = false

  try {
    // update video likes counter
    numberOfLikes = (await redis.get<number>(`videos:${videoId}:stats:likes`)) || 0
    numberOfDislikes = (await redis.get<number>(`videos:${videoId}:stats:dislikes`)) || 0
  } catch (err) {
  }

  // optional: determine if the user liked or disliked the content
  if (apiKey) {
    try {
      const credentials = { accessToken: apiKey }
      
      const user = await whoAmI({ credentials }) as unknown as WhoAmIUser
      const isLiked = await redis.get<boolean>(`users:${user.id}:activity:videos:${videoId}:liked`)
      if (isLiked !== null) {
        isLikedByUser = !!isLiked
        isDislikedByUser = !isLiked
      }
    } catch (err) {
      console.error("failed to get user like status")
    }
  }

  return {
    isLikedByUser,
    isDislikedByUser,
    numberOfLikes,
    numberOfDislikes,
  }
}

export async function rateVideo(videoId: string, liked: boolean, apiKey: string): Promise<VideoRating> {
  // note: we want the like to throw an exception if it failed
  let numberOfLikes = 0
  let numberOfDislikes = 0
  let isLikedByUser = false
  let isDislikedByUser = false

  const credentials = { accessToken: apiKey }
  
  const user = await whoAmI({ credentials }) as unknown as WhoAmIUser

  const hasLiked = await redis.get<boolean>(`users:${user.id}:activity:videos:${videoId}:liked`)
  
  const hasAlreadyRatedTheSame =  hasLiked !== null && liked === hasLiked

  if (hasAlreadyRatedTheSame) {
    return {
      numberOfLikes: await redis.get(`videos:${videoId}:stats:likes`) || 0,
      numberOfDislikes: await redis.get(`videos:${videoId}:stats:dislikes`) || 0,
      isLikedByUser: liked,
      isDislikedByUser: !liked
    }
  }
  const hasAlreadyRatedAndDifferently = hasLiked !== null && liked !== hasLiked

  await redis.set(`users:${user.id}:activity:videos:${videoId}:liked`, liked)

  isLikedByUser = liked
  isDislikedByUser = !liked

  // if user has already rated the content, and it's different from the desired value,
  // then we need to undo the rating

  try {
    if (liked) {
      // update video likes counter
      numberOfLikes = await redis.incr(`videos:${videoId}:stats:likes`)
      if (hasAlreadyRatedAndDifferently) {
        numberOfDislikes = await redis.decr(`videos:${videoId}:stats:dislikes`)
      }
    } else {
      numberOfDislikes = await redis.incr(`videos:${videoId}:stats:dislikes`)
      if (hasAlreadyRatedAndDifferently) {
        numberOfLikes = await redis.decr(`videos:${videoId}:stats:likes`)
      }
    }
  } catch (err) {
  } finally {
    return {
      numberOfLikes,
      numberOfDislikes,
      isLikedByUser,
      isDislikedByUser,
    }
  }
}