create.ts 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456
  1. import { DB } from '../../../generated/indexer'
  2. import { Channel } from '../../../generated/graphql-server/src/modules/channel/channel.model'
  3. import { Category } from '../../../generated/graphql-server/src/modules/category/category.model'
  4. import { KnownLicenseEntity } from '../../../generated/graphql-server/src/modules/known-license-entity/known-license-entity.model'
  5. import { UserDefinedLicenseEntity } from '../../../generated/graphql-server/src/modules/user-defined-license-entity/user-defined-license-entity.model'
  6. import { JoystreamMediaLocationEntity } from '../../../generated/graphql-server/src/modules/joystream-media-location-entity/joystream-media-location-entity.model'
  7. import { HttpMediaLocationEntity } from '../../../generated/graphql-server/src/modules/http-media-location-entity/http-media-location-entity.model'
  8. import { VideoMedia } from '../../../generated/graphql-server/src/modules/video-media/video-media.model'
  9. import { Video } from '../../../generated/graphql-server/src/modules/video/video.model'
  10. import { Block, Network } from '../../../generated/graphql-server/src/modules/block/block.model'
  11. import { Language } from '../../../generated/graphql-server/src/modules/language/language.model'
  12. import { VideoMediaEncoding } from '../../../generated/graphql-server/src/modules/video-media-encoding/video-media-encoding.model'
  13. import { ClassEntity } from '../../../generated/graphql-server/src/modules/class-entity/class-entity.model'
  14. import { LicenseEntity } from '../../../generated/graphql-server/src/modules/license-entity/license-entity.model'
  15. import { MediaLocationEntity } from '../../../generated/graphql-server/src/modules/media-location-entity/media-location-entity.model'
  16. import { FeaturedVideo } from '../../../generated/graphql-server/src/modules/featured-video/featured-video.model'
  17. import { contentDirectoryClassNamesWithId } from '../content-dir-consts'
  18. import {
  19. ClassEntityMap,
  20. ICategory,
  21. IChannel,
  22. ICreateEntityOperation,
  23. IDBBlockId,
  24. IEntity,
  25. IFeaturedVideo,
  26. IHttpMediaLocation,
  27. IJoystreamMediaLocation,
  28. IKnownLicense,
  29. ILanguage,
  30. ILicense,
  31. IMediaLocation,
  32. IUserDefinedLicense,
  33. IVideo,
  34. IVideoMedia,
  35. IVideoMediaEncoding,
  36. } from '../../types'
  37. import { getOrCreate } from '../get-or-create'
  38. import BN from 'bn.js'
  39. import {
  40. HttpMediaLocation,
  41. JoystreamMediaLocation,
  42. KnownLicense,
  43. UserDefinedLicense,
  44. } from '../../../generated/graphql-server/src/modules/variants/variants.model'
  45. async function createBlockOrGetFromDatabase(db: DB, blockNumber: number): Promise<Block> {
  46. let b = await db.get(Block, { where: { block: blockNumber } })
  47. if (b === undefined) {
  48. // TODO: get timestamp from the event or extrinsic
  49. b = new Block({ block: blockNumber, network: Network.BABYLON, timestamp: new BN(Date.now()) })
  50. await db.save<Block>(b)
  51. }
  52. return b
  53. }
  54. async function createChannel(
  55. { db, block, id }: IDBBlockId,
  56. classEntityMap: ClassEntityMap,
  57. p: IChannel,
  58. nextEntityIdBeforeTransaction: number
  59. ): Promise<Channel> {
  60. const record = await db.get(Channel, { where: { id } })
  61. if (record) return record
  62. const channel = new Channel()
  63. channel.version = block
  64. channel.id = id
  65. channel.handle = p.handle
  66. channel.description = p.description
  67. channel.isCurated = !!p.isCurated
  68. channel.isPublic = p.isPublic
  69. channel.coverPhotoUrl = p.coverPhotoUrl
  70. channel.avatarPhotoUrl = p.avatarPhotoUrl
  71. channel.happenedIn = await createBlockOrGetFromDatabase(db, block)
  72. const { language } = p
  73. if (language) {
  74. channel.language = await getOrCreate.language(
  75. { db, block, id },
  76. classEntityMap,
  77. language,
  78. nextEntityIdBeforeTransaction
  79. )
  80. }
  81. await db.save(channel)
  82. return channel
  83. }
  84. async function createCategory({ db, block, id }: IDBBlockId, p: ICategory): Promise<Category> {
  85. const record = await db.get(Category, { where: { id } })
  86. if (record) return record
  87. const category = new Category()
  88. category.id = id
  89. category.name = p.name
  90. category.description = p.description
  91. category.version = block
  92. category.happenedIn = await createBlockOrGetFromDatabase(db, block)
  93. await db.save(category)
  94. return category
  95. }
  96. async function createKnownLicense({ db, block, id }: IDBBlockId, p: IKnownLicense): Promise<KnownLicenseEntity> {
  97. const record = await db.get(KnownLicenseEntity, { where: { id } })
  98. if (record) return record
  99. const knownLicence = new KnownLicenseEntity()
  100. knownLicence.id = id
  101. knownLicence.code = p.code
  102. knownLicence.name = p.name
  103. knownLicence.description = p.description
  104. knownLicence.url = p.url
  105. knownLicence.version = block
  106. knownLicence.happenedIn = await createBlockOrGetFromDatabase(db, block)
  107. await db.save(knownLicence)
  108. return knownLicence
  109. }
  110. async function createUserDefinedLicense(
  111. { db, block, id }: IDBBlockId,
  112. p: IUserDefinedLicense
  113. ): Promise<UserDefinedLicenseEntity> {
  114. const record = await db.get(UserDefinedLicenseEntity, { where: { id } })
  115. if (record) return record
  116. const userDefinedLicense = new UserDefinedLicenseEntity()
  117. userDefinedLicense.id = id
  118. userDefinedLicense.content = p.content
  119. userDefinedLicense.version = block
  120. userDefinedLicense.happenedIn = await createBlockOrGetFromDatabase(db, block)
  121. await db.save<UserDefinedLicenseEntity>(userDefinedLicense)
  122. return userDefinedLicense
  123. }
  124. async function createJoystreamMediaLocation(
  125. { db, block, id }: IDBBlockId,
  126. p: IJoystreamMediaLocation
  127. ): Promise<JoystreamMediaLocationEntity> {
  128. const record = await db.get(JoystreamMediaLocationEntity, { where: { id } })
  129. if (record) return record
  130. const joyMediaLoc = new JoystreamMediaLocationEntity()
  131. joyMediaLoc.id = id
  132. joyMediaLoc.dataObjectId = p.dataObjectId
  133. joyMediaLoc.version = block
  134. joyMediaLoc.happenedIn = await createBlockOrGetFromDatabase(db, block)
  135. await db.save(joyMediaLoc)
  136. return joyMediaLoc
  137. }
  138. async function createHttpMediaLocation(
  139. { db, block, id }: IDBBlockId,
  140. p: IHttpMediaLocation
  141. ): Promise<HttpMediaLocationEntity> {
  142. const record = await db.get(HttpMediaLocationEntity, { where: { id } })
  143. if (record) return record
  144. const httpMediaLoc = new HttpMediaLocationEntity()
  145. httpMediaLoc.id = id
  146. httpMediaLoc.url = p.url
  147. httpMediaLoc.port = p.port
  148. httpMediaLoc.version = block
  149. httpMediaLoc.happenedIn = await createBlockOrGetFromDatabase(db, block)
  150. await db.save(httpMediaLoc)
  151. return httpMediaLoc
  152. }
  153. async function createVideoMedia(
  154. { db, block, id }: IDBBlockId,
  155. classEntityMap: ClassEntityMap,
  156. p: IVideoMedia,
  157. nextEntityIdBeforeTransaction: number
  158. ): Promise<VideoMedia> {
  159. const videoMedia = new VideoMedia()
  160. videoMedia.id = id
  161. videoMedia.pixelHeight = p.pixelHeight
  162. videoMedia.pixelWidth = p.pixelWidth
  163. videoMedia.size = p.size
  164. videoMedia.version = block
  165. const { encoding, location } = p
  166. if (encoding !== undefined) {
  167. videoMedia.encoding = await getOrCreate.videoMediaEncoding(
  168. { db, block, id },
  169. classEntityMap,
  170. encoding,
  171. nextEntityIdBeforeTransaction
  172. )
  173. }
  174. if (location !== undefined) {
  175. const m = await getOrCreate.mediaLocation(
  176. { db, block, id },
  177. classEntityMap,
  178. location,
  179. nextEntityIdBeforeTransaction
  180. )
  181. videoMedia.locationEntity = m
  182. const { httpMediaLocation, joystreamMediaLocation } = m
  183. if (httpMediaLocation) {
  184. const mediaLoc = new HttpMediaLocation()
  185. mediaLoc.port = httpMediaLocation.port
  186. mediaLoc.url = httpMediaLocation.url
  187. videoMedia.location = mediaLoc
  188. }
  189. if (joystreamMediaLocation) {
  190. const mediaLoc = new JoystreamMediaLocation()
  191. mediaLoc.dataObjectId = joystreamMediaLocation.dataObjectId
  192. videoMedia.location = mediaLoc
  193. }
  194. }
  195. videoMedia.happenedIn = await createBlockOrGetFromDatabase(db, block)
  196. await db.save<VideoMedia>(videoMedia)
  197. return videoMedia
  198. }
  199. async function createVideo(
  200. { db, block, id }: IDBBlockId,
  201. classEntityMap: ClassEntityMap,
  202. p: IVideo,
  203. nextEntityIdBeforeTransaction: number
  204. ): Promise<Video> {
  205. const record = await db.get(Video, { where: { id } })
  206. if (record) return record
  207. const video = new Video()
  208. video.id = id
  209. video.title = p.title
  210. video.description = p.description
  211. video.duration = p.duration
  212. video.hasMarketing = p.hasMarketing
  213. video.isCurated = !!p.isCurated
  214. video.isExplicit = p.isExplicit
  215. video.isPublic = p.isPublic
  216. video.publishedBeforeJoystream = p.publishedBeforeJoystream
  217. video.skippableIntroDuration = p.skippableIntroDuration
  218. video.thumbnailUrl = p.thumbnailUrl
  219. video.version = block
  220. video.isFeatured = false
  221. const { language, license, category, channel, media } = p
  222. if (language !== undefined) {
  223. video.language = await getOrCreate.language(
  224. { db, block, id },
  225. classEntityMap,
  226. language,
  227. nextEntityIdBeforeTransaction
  228. )
  229. }
  230. if (license) {
  231. const lic = await getOrCreate.license({ db, block, id }, classEntityMap, license, nextEntityIdBeforeTransaction)
  232. video.license = lic
  233. }
  234. if (category !== undefined) {
  235. video.category = await getOrCreate.category(
  236. { db, block, id },
  237. classEntityMap,
  238. category,
  239. nextEntityIdBeforeTransaction
  240. )
  241. }
  242. if (channel !== undefined) {
  243. video.channel = await getOrCreate.channel({ db, block, id }, classEntityMap, channel, nextEntityIdBeforeTransaction)
  244. }
  245. if (media !== undefined) {
  246. video.media = await getOrCreate.videoMedia({ db, block, id }, classEntityMap, media, nextEntityIdBeforeTransaction)
  247. }
  248. video.happenedIn = await createBlockOrGetFromDatabase(db, block)
  249. await db.save<Video>(video)
  250. return video
  251. }
  252. async function createLanguage({ db, block, id }: IDBBlockId, p: ILanguage): Promise<Language> {
  253. const record = await db.get(Language, { where: { id } })
  254. if (record) return record
  255. const language = new Language()
  256. language.id = id
  257. language.name = p.name
  258. language.code = p.code
  259. language.version = block
  260. language.happenedIn = await createBlockOrGetFromDatabase(db, block)
  261. await db.save<Language>(language)
  262. return language
  263. }
  264. async function createVideoMediaEncoding(
  265. { db, block, id }: IDBBlockId,
  266. p: IVideoMediaEncoding
  267. ): Promise<VideoMediaEncoding> {
  268. const record = await db.get(VideoMediaEncoding, { where: { id } })
  269. if (record) return record
  270. const encoding = new VideoMediaEncoding()
  271. encoding.id = id
  272. encoding.name = p.name
  273. encoding.version = block
  274. encoding.happenedIn = await createBlockOrGetFromDatabase(db, block)
  275. await db.save<VideoMediaEncoding>(encoding)
  276. return encoding
  277. }
  278. async function createLicense(
  279. { db, block, id }: IDBBlockId,
  280. classEntityMap: ClassEntityMap,
  281. p: ILicense,
  282. nextEntityIdBeforeTransaction: number
  283. ): Promise<LicenseEntity> {
  284. const record = await db.get(LicenseEntity, { where: { id } })
  285. if (record) return record
  286. const license = new LicenseEntity()
  287. if (p.knownLicense) {
  288. const kLicense = await getOrCreate.knownLicense(
  289. { db, block, id },
  290. classEntityMap,
  291. p.knownLicense,
  292. nextEntityIdBeforeTransaction
  293. )
  294. const k = new KnownLicense()
  295. k.code = kLicense.code
  296. k.description = kLicense.description
  297. k.name = kLicense.name
  298. k.url = kLicense.url
  299. // Set the license type
  300. license.type = k
  301. }
  302. if (p.userDefinedLicense) {
  303. const { content } = await getOrCreate.userDefinedLicense(
  304. { db, block, id },
  305. classEntityMap,
  306. p.userDefinedLicense,
  307. nextEntityIdBeforeTransaction
  308. )
  309. const u = new UserDefinedLicense()
  310. u.content = content
  311. // Set the license type
  312. license.type = u
  313. }
  314. license.id = id
  315. license.attribution = p.attribution
  316. license.happenedIn = await createBlockOrGetFromDatabase(db, block)
  317. await db.save<LicenseEntity>(license)
  318. return license
  319. }
  320. async function createMediaLocation(
  321. { db, block, id }: IDBBlockId,
  322. classEntityMap: ClassEntityMap,
  323. p: IMediaLocation,
  324. nextEntityIdBeforeTransaction: number
  325. ): Promise<MediaLocationEntity> {
  326. const { httpMediaLocation, joystreamMediaLocation } = p
  327. const location = new MediaLocationEntity()
  328. location.id = id
  329. if (httpMediaLocation !== undefined) {
  330. location.httpMediaLocation = await getOrCreate.httpMediaLocation(
  331. { db, block, id },
  332. classEntityMap,
  333. httpMediaLocation,
  334. nextEntityIdBeforeTransaction
  335. )
  336. }
  337. if (joystreamMediaLocation !== undefined) {
  338. location.joystreamMediaLocation = await getOrCreate.joystreamMediaLocation(
  339. { db, block, id },
  340. classEntityMap,
  341. joystreamMediaLocation,
  342. nextEntityIdBeforeTransaction
  343. )
  344. }
  345. location.happenedIn = await createBlockOrGetFromDatabase(db, block)
  346. await db.save<MediaLocationEntity>(location)
  347. return location
  348. }
  349. async function createFeaturedVideo(
  350. { db, block, id }: IDBBlockId,
  351. classEntityMap: ClassEntityMap,
  352. p: IFeaturedVideo,
  353. nextEntityIdBeforeTransaction: number
  354. ): Promise<void> {
  355. const featuredVideo = new FeaturedVideo()
  356. featuredVideo.video = await getOrCreate.video(
  357. { db, block, id },
  358. classEntityMap,
  359. p.video!,
  360. nextEntityIdBeforeTransaction
  361. )
  362. featuredVideo.id = id
  363. featuredVideo.version = block
  364. featuredVideo.video.isFeatured = true
  365. await db.save<Video>(featuredVideo.video)
  366. await db.save<FeaturedVideo>(featuredVideo)
  367. }
  368. async function getClassName(
  369. db: DB,
  370. entity: IEntity,
  371. createEntityOperations: ICreateEntityOperation[]
  372. ): Promise<string | undefined> {
  373. const { entityId, indexOf } = entity
  374. if (entityId === undefined && indexOf === undefined) {
  375. throw Error(`Can not determine class of the entity`)
  376. }
  377. let classId: number | undefined
  378. // Is newly created entity in the same transaction
  379. if (indexOf !== undefined) {
  380. classId = createEntityOperations[indexOf].classId
  381. } else {
  382. const ce = await db.get(ClassEntity, { where: { id: entityId } })
  383. if (ce === undefined) console.log(`Class not found for the entity: ${entityId}`)
  384. classId = ce ? ce.classId : undefined
  385. }
  386. const c = contentDirectoryClassNamesWithId.find((c) => c.classId === classId)
  387. // TODO: stop execution, class should be created before entity creation
  388. if (c === undefined) console.log(`Not recognized class id: ${classId}`)
  389. return c ? c.name : undefined
  390. }
  391. export {
  392. createCategory,
  393. createChannel,
  394. createVideoMedia,
  395. createVideo,
  396. createUserDefinedLicense,
  397. createKnownLicense,
  398. createHttpMediaLocation,
  399. createJoystreamMediaLocation,
  400. createLanguage,
  401. createVideoMediaEncoding,
  402. createLicense,
  403. createMediaLocation,
  404. createBlockOrGetFromDatabase,
  405. getClassName,
  406. createFeaturedVideo,
  407. }