video.ts 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. /*
  2. eslint-disable @typescript-eslint/naming-convention
  3. */
  4. import { EventContext, StoreContext } from '@joystream/hydra-common'
  5. import { In } from 'typeorm'
  6. import { Content } from '../generated/types'
  7. import { deserializeMetadata, inconsistentState, logger } from '../common'
  8. import { processVideoMetadata } from './utils'
  9. import { Channel, Video, VideoCategory, AssetNone } from 'query-node/dist/model'
  10. import { VideoMetadata, VideoCategoryMetadata } from '@joystream/metadata-protobuf'
  11. import { integrateMeta } from '@joystream/metadata-protobuf/utils'
  12. export async function content_VideoCategoryCreated({ store, event }: EventContext & StoreContext): Promise<void> {
  13. // read event data
  14. const [, videoCategoryId, videoCategoryCreationParameters] = new Content.VideoCategoryCreatedEvent(event).params
  15. // read metadata
  16. const metadata = (await deserializeMetadata(VideoCategoryMetadata, videoCategoryCreationParameters.meta)) || {}
  17. // create new video category
  18. const videoCategory = new VideoCategory({
  19. // main data
  20. id: videoCategoryId.toString(),
  21. videos: [],
  22. createdInBlock: event.blockNumber,
  23. // fill in auto-generated fields
  24. createdAt: new Date(event.blockTimestamp),
  25. updatedAt: new Date(event.blockTimestamp),
  26. })
  27. integrateMeta(videoCategory, metadata, ['name'])
  28. // save video category
  29. await store.save<VideoCategory>(videoCategory)
  30. // emit log event
  31. logger.info('Video category has been created', { id: videoCategoryId })
  32. }
  33. export async function content_VideoCategoryUpdated({ store, event }: EventContext & StoreContext): Promise<void> {
  34. // read event data
  35. const [, videoCategoryId, videoCategoryUpdateParameters] = new Content.VideoCategoryUpdatedEvent(event).params
  36. // load video category
  37. const videoCategory = await store.get(VideoCategory, {
  38. where: { id: videoCategoryId.toString() },
  39. })
  40. // ensure video category exists
  41. if (!videoCategory) {
  42. return inconsistentState('Non-existing video category update requested', videoCategoryId)
  43. }
  44. // read metadata
  45. const newMeta = deserializeMetadata(VideoCategoryMetadata, videoCategoryUpdateParameters.new_meta) || {}
  46. integrateMeta(videoCategory, newMeta, ['name'])
  47. // set last update time
  48. videoCategory.updatedAt = new Date(event.blockTimestamp)
  49. // save video category
  50. await store.save<VideoCategory>(videoCategory)
  51. // emit log event
  52. logger.info('Video category has been updated', { id: videoCategoryId })
  53. }
  54. export async function content_VideoCategoryDeleted({ store, event }: EventContext & StoreContext): Promise<void> {
  55. // read event data
  56. const [, videoCategoryId] = new Content.VideoCategoryDeletedEvent(event).params
  57. // load video category
  58. const videoCategory = await store.get(VideoCategory, {
  59. where: { id: videoCategoryId.toString() },
  60. })
  61. // ensure video category exists
  62. if (!videoCategory) {
  63. return inconsistentState('Non-existing video category deletion requested', videoCategoryId)
  64. }
  65. // remove video category
  66. await store.remove<VideoCategory>(videoCategory)
  67. // emit log event
  68. logger.info('Video category has been deleted', { id: videoCategoryId })
  69. }
  70. /// //////////////// Video //////////////////////////////////////////////////////
  71. export async function content_VideoCreated(ctx: EventContext & StoreContext): Promise<void> {
  72. const { store, event } = ctx
  73. // read event data
  74. const [, channelId, videoId, videoCreationParameters] = new Content.VideoCreatedEvent(event).params
  75. // load channel
  76. const channel = await store.get(Channel, { where: { id: channelId.toString() } })
  77. // ensure channel exists
  78. if (!channel) {
  79. return inconsistentState('Trying to add video to non-existing channel', channelId)
  80. }
  81. const video = new Video({
  82. id: videoId.toString(),
  83. channel,
  84. isCensored: false,
  85. isFeatured: false,
  86. createdInBlock: event.blockNumber,
  87. thumbnailPhoto: new AssetNone(),
  88. media: new AssetNone(),
  89. createdAt: new Date(event.blockTimestamp),
  90. updatedAt: new Date(event.blockTimestamp),
  91. })
  92. // deserialize & process metadata
  93. const metadata = deserializeMetadata(VideoMetadata, videoCreationParameters.meta) || {}
  94. await processVideoMetadata(ctx, channel, video, metadata, videoCreationParameters.assets)
  95. // save video
  96. await store.save<Video>(video)
  97. // emit log event
  98. logger.info('Video has been created', { id: videoId })
  99. }
  100. export async function content_VideoUpdated(ctx: EventContext & StoreContext): Promise<void> {
  101. const { event, store } = ctx
  102. // read event data
  103. const [, videoId, videoUpdateParameters] = new Content.VideoUpdatedEvent(event).params
  104. // load video
  105. const video = await store.get(Video, {
  106. where: { id: videoId.toString() },
  107. relations: ['channel', 'license'],
  108. })
  109. // ensure video exists
  110. if (!video) {
  111. return inconsistentState('Non-existing video update requested', videoId)
  112. }
  113. // prepare changed metadata
  114. const newMetadataBytes = videoUpdateParameters.new_meta.unwrapOr(null)
  115. // update metadata if it was changed
  116. if (newMetadataBytes) {
  117. const newMetadata = deserializeMetadata(VideoMetadata, newMetadataBytes) || {}
  118. await processVideoMetadata(ctx, video.channel, video, newMetadata, videoUpdateParameters.assets.unwrapOr([]))
  119. }
  120. // set last update time
  121. video.updatedAt = new Date(event.blockTimestamp)
  122. // save video
  123. await store.save<Video>(video)
  124. // emit log event
  125. logger.info('Video has been updated', { id: videoId })
  126. }
  127. export async function content_VideoDeleted({ store, event }: EventContext & StoreContext): Promise<void> {
  128. // read event data
  129. const [, videoId] = new Content.VideoDeletedEvent(event).params
  130. // load video
  131. const video = await store.get(Video, { where: { id: videoId.toString() } })
  132. // ensure video exists
  133. if (!video) {
  134. return inconsistentState('Non-existing video deletion requested', videoId)
  135. }
  136. // remove video
  137. await store.remove<Video>(video)
  138. // emit log event
  139. logger.info('Video has been deleted', { id: videoId })
  140. }
  141. export async function content_VideoCensorshipStatusUpdated({
  142. store,
  143. event,
  144. }: EventContext & StoreContext): Promise<void> {
  145. // read event data
  146. const [, videoId, isCensored] = new Content.VideoCensorshipStatusUpdatedEvent(event).params
  147. // load video
  148. const video = await store.get(Video, { where: { id: videoId.toString() } })
  149. // ensure video exists
  150. if (!video) {
  151. return inconsistentState('Non-existing video censoring requested', videoId)
  152. }
  153. // update video
  154. video.isCensored = isCensored.isTrue
  155. // set last update time
  156. video.updatedAt = new Date(event.blockTimestamp)
  157. // save video
  158. await store.save<Video>(video)
  159. // emit log event
  160. logger.info('Video censorship status has been updated', { id: videoId, isCensored: isCensored.isTrue })
  161. }
  162. export async function content_FeaturedVideosSet({ store, event }: EventContext & StoreContext): Promise<void> {
  163. // read event data
  164. const [, videoIds] = new Content.FeaturedVideosSetEvent(event).params
  165. // load old featured videos
  166. const existingFeaturedVideos = await store.getMany(Video, { where: { isFeatured: true } })
  167. // comparsion utility
  168. const isSame = (videoIdA: string) => (videoIdB: string) => videoIdA === videoIdB
  169. // calculate diff sets
  170. const videosToRemove = existingFeaturedVideos.filter(
  171. (existingFV) => !videoIds.map((videoId) => videoId.toString()).some(isSame(existingFV.id))
  172. )
  173. const videoIdsToAdd = videoIds.filter(
  174. (videoId) => !existingFeaturedVideos.map((existingFV) => existingFV.id).some(isSame(videoId.toString()))
  175. )
  176. // mark previously featured videos as not-featured
  177. await Promise.all(
  178. videosToRemove.map(async (video) => {
  179. video.isFeatured = false
  180. // set last update time
  181. video.updatedAt = new Date(event.blockTimestamp)
  182. await store.save<Video>(video)
  183. })
  184. )
  185. // read previously not-featured videos that are meant to be featured
  186. const videosToAdd = await store.getMany(Video, {
  187. where: {
  188. id: In(videoIdsToAdd.map((item) => item.toString())),
  189. },
  190. })
  191. if (videosToAdd.length !== videoIdsToAdd.length) {
  192. return inconsistentState(
  193. 'At least one non-existing video featuring requested',
  194. videosToAdd.map((v) => v.id)
  195. )
  196. }
  197. // mark previously not-featured videos as featured
  198. await Promise.all(
  199. videosToAdd.map(async (video) => {
  200. video.isFeatured = true
  201. // set last update time
  202. video.updatedAt = new Date(event.blockTimestamp)
  203. await store.save<Video>(video)
  204. })
  205. )
  206. // emit log event
  207. const newFeaturedVideoIds = videoIds.map((id) => id.toString())
  208. const removedFeaturedVideosIds = videosToRemove.map((v) => v.id)
  209. logger.info('New featured videos have been set', { newFeaturedVideoIds, removedFeaturedVideosIds })
  210. }