ニジカ投稿局 https://tv.nizika.tv
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

video-live.ts 4.4 KiB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. import { LiveVideo, VideoState, type LiveVideoLatencyModeType } from '@peertube/peertube-models'
  2. import { CONFIG } from '@server/initializers/config.js'
  3. import { WEBSERVER } from '@server/initializers/constants.js'
  4. import { MVideoLive, MVideoLiveVideoWithSetting, MVideoLiveWithSetting } from '@server/types/models/index.js'
  5. import { Transaction } from 'sequelize'
  6. import {
  7. AllowNull,
  8. BeforeDestroy,
  9. BelongsTo,
  10. Column,
  11. CreatedAt,
  12. DataType,
  13. DefaultScope,
  14. ForeignKey, Table,
  15. UpdatedAt
  16. } from 'sequelize-typescript'
  17. import { VideoBlacklistModel } from './video-blacklist.js'
  18. import { VideoLiveReplaySettingModel } from './video-live-replay-setting.js'
  19. import { VideoModel } from './video.js'
  20. import { SequelizeModel } from '../shared/index.js'
  21. @DefaultScope(() => ({
  22. include: [
  23. {
  24. model: VideoModel,
  25. required: true,
  26. include: [
  27. {
  28. model: VideoBlacklistModel,
  29. required: false
  30. }
  31. ]
  32. },
  33. {
  34. model: VideoLiveReplaySettingModel,
  35. required: false
  36. }
  37. ]
  38. }))
  39. @Table({
  40. tableName: 'videoLive',
  41. indexes: [
  42. {
  43. fields: [ 'videoId' ],
  44. unique: true
  45. },
  46. {
  47. fields: [ 'replaySettingId' ],
  48. unique: true
  49. }
  50. ]
  51. })
  52. export class VideoLiveModel extends SequelizeModel<VideoLiveModel> {
  53. @AllowNull(true)
  54. @Column(DataType.STRING)
  55. streamKey: string
  56. @AllowNull(false)
  57. @Column
  58. saveReplay: boolean
  59. @AllowNull(false)
  60. @Column
  61. permanentLive: boolean
  62. @AllowNull(false)
  63. @Column
  64. latencyMode: LiveVideoLatencyModeType
  65. @CreatedAt
  66. createdAt: Date
  67. @UpdatedAt
  68. updatedAt: Date
  69. @ForeignKey(() => VideoModel)
  70. @Column
  71. videoId: number
  72. @BelongsTo(() => VideoModel, {
  73. foreignKey: {
  74. allowNull: false
  75. },
  76. onDelete: 'cascade'
  77. })
  78. Video: Awaited<VideoModel>
  79. @ForeignKey(() => VideoLiveReplaySettingModel)
  80. @Column
  81. replaySettingId: number
  82. @BelongsTo(() => VideoLiveReplaySettingModel, {
  83. foreignKey: {
  84. allowNull: true
  85. },
  86. onDelete: 'set null'
  87. })
  88. ReplaySetting: Awaited<VideoLiveReplaySettingModel>
  89. @BeforeDestroy
  90. static deleteReplaySetting (instance: VideoLiveModel, options: { transaction: Transaction }) {
  91. return VideoLiveReplaySettingModel.destroy({
  92. where: {
  93. id: instance.replaySettingId
  94. },
  95. transaction: options.transaction
  96. })
  97. }
  98. static loadByStreamKey (streamKey: string) {
  99. const query = {
  100. where: {
  101. streamKey
  102. },
  103. include: [
  104. {
  105. model: VideoModel.unscoped(),
  106. required: true,
  107. where: {
  108. state: VideoState.WAITING_FOR_LIVE
  109. },
  110. include: [
  111. {
  112. model: VideoBlacklistModel.unscoped(),
  113. required: false
  114. }
  115. ]
  116. },
  117. {
  118. model: VideoLiveReplaySettingModel.unscoped(),
  119. required: false
  120. }
  121. ]
  122. }
  123. return VideoLiveModel.findOne<MVideoLiveVideoWithSetting>(query)
  124. }
  125. static loadByVideoId (videoId: number) {
  126. const query = {
  127. where: {
  128. videoId
  129. }
  130. }
  131. return VideoLiveModel.findOne<MVideoLive>(query)
  132. }
  133. static loadByVideoIdWithSettings (videoId: number) {
  134. const query = {
  135. where: {
  136. videoId
  137. },
  138. include: [
  139. {
  140. model: VideoLiveReplaySettingModel.unscoped(),
  141. required: false
  142. }
  143. ]
  144. }
  145. return VideoLiveModel.findOne<MVideoLiveWithSetting>(query)
  146. }
  147. toFormattedJSON (canSeePrivateInformation: boolean): LiveVideo {
  148. let privateInformation: Pick<LiveVideo, 'rtmpUrl' | 'rtmpsUrl' | 'streamKey'> | {} = {}
  149. // If we don't have a stream key, it means this is a remote live so we don't specify the rtmp URL
  150. // We also display these private information only to the live owne/moderators
  151. if (this.streamKey && canSeePrivateInformation === true) {
  152. privateInformation = {
  153. streamKey: this.streamKey,
  154. rtmpUrl: CONFIG.LIVE.RTMP.ENABLED
  155. ? WEBSERVER.RTMP_BASE_LIVE_URL
  156. : null,
  157. rtmpsUrl: CONFIG.LIVE.RTMPS.ENABLED
  158. ? WEBSERVER.RTMPS_BASE_LIVE_URL
  159. : null
  160. }
  161. }
  162. const replaySettings = this.replaySettingId
  163. ? this.ReplaySetting.toFormattedJSON()
  164. : undefined
  165. return {
  166. ...privateInformation,
  167. permanentLive: this.permanentLive,
  168. saveReplay: this.saveReplay,
  169. replaySettings,
  170. latencyMode: this.latencyMode
  171. }
  172. }
  173. }