From d05af753bf49f9e76b90825311ef007878164ca7 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Thu, 16 May 2024 09:45:44 +0200 Subject: [PATCH 1/6] Fix embed api on ios video.js clones the video element so we must rely on the player wrapper instead of the video element --- client/src/standalone/videos/embed-api.ts | 65 +++++++++---------- client/src/standalone/videos/embed.ts | 8 +-- .../standalone/videos/shared/player-html.ts | 24 +++---- .../videos/shared/player-options-builder.ts | 2 +- client/src/standalone/videos/test-embed.ts | 23 +++++-- 5 files changed, 60 insertions(+), 62 deletions(-) diff --git a/client/src/standalone/videos/embed-api.ts b/client/src/standalone/videos/embed-api.ts index 0e8489b6d..879d251a6 100644 --- a/client/src/standalone/videos/embed-api.ts +++ b/client/src/standalone/videos/embed-api.ts @@ -1,8 +1,8 @@ -import './embed.scss' import * as Channel from 'jschannel' import { logger } from '../../root-helpers' import { PeerTubeResolution, PeerTubeTextTrack } from '../embed-player-api/definitions' import { PeerTubeEmbed } from './embed' +import './embed.scss' /** * Embed API exposes control of the embed player to the outside world via @@ -13,7 +13,6 @@ export class PeerTubeEmbedApi { private isReady = false private resolutions: PeerTubeResolution[] = [] - private oldVideoElement: HTMLVideoElement private videoElPlayListener: () => void private videoElPauseListener: () => void private videoElEndedListener: () => void @@ -36,8 +35,8 @@ export class PeerTubeEmbedApi { } } - private get element () { - return this.embed.getPlayerElement() + private get player () { + return this.embed.player } private constructChannel () { @@ -45,12 +44,12 @@ export class PeerTubeEmbedApi { channel.bind('setVideoPassword', (txn, value) => this.embed.setVideoPasswordByAPI(value)) - channel.bind('play', (txn, params) => this.embed.player.play()) - channel.bind('pause', (txn, params) => this.embed.player.pause()) - channel.bind('seek', (txn, time) => this.embed.player.currentTime(time)) + channel.bind('play', (txn, params) => this.player.play()) + channel.bind('pause', (txn, params) => this.player.pause()) + channel.bind('seek', (txn, time) => this.player.currentTime(time)) - channel.bind('setVolume', (txn, value) => this.embed.player.volume(value)) - channel.bind('getVolume', (txn, value) => this.embed.player.volume()) + channel.bind('setVolume', (txn, value) => this.player.volume(value)) + channel.bind('getVolume', (txn, value) => this.player.volume()) channel.bind('isReady', (txn, params) => this.isReady) @@ -60,9 +59,9 @@ export class PeerTubeEmbedApi { channel.bind('getCaptions', (txn, params) => this.getCaptions()) channel.bind('setCaption', (txn, id) => this.setCaption(id)) - channel.bind('setPlaybackRate', (txn, playbackRate) => this.embed.player.playbackRate(playbackRate)) - channel.bind('getPlaybackRate', (txn, params) => this.embed.player.playbackRate()) - channel.bind('getPlaybackRates', (txn, params) => this.embed.player.options_.playbackRates) + channel.bind('setPlaybackRate', (txn, playbackRate) => this.player.playbackRate(playbackRate)) + channel.bind('getPlaybackRate', (txn, params) => this.player.playbackRate()) + channel.bind('getPlaybackRates', (txn, params) => this.player.options_.playbackRates) channel.bind('playNextVideo', (txn, params) => this.embed.playNextPlaylistVideo()) channel.bind('playPreviousVideo', (txn, params) => this.embed.playPreviousPlaylistVideo()) @@ -79,11 +78,11 @@ export class PeerTubeEmbedApi { return } - this.embed.player.peertubeResolutions().select({ id: resolutionId, fireCallback: true }) + this.player.peertubeResolutions().select({ id: resolutionId, fireCallback: true }) } private getCaptions (): PeerTubeTextTrack[] { - return this.embed.player.textTracks().tracks_.map(t => ({ + return this.player.textTracks().tracks_.map(t => ({ id: t.id, src: t.src, label: t.label, @@ -92,7 +91,7 @@ export class PeerTubeEmbedApi { } private setCaption (id: string) { - const tracks = this.embed.player.textTracks().tracks_ + const tracks = this.player.textTracks().tracks_ for (const track of tracks) { if (track.id === id) track.mode = 'showing' @@ -112,15 +111,15 @@ export class PeerTubeEmbedApi { let currentState: 'playing' | 'paused' | 'unstarted' | 'ended' = 'unstarted' this.videoElInterval = setInterval(() => { - const position = this.element.currentTime - const volume = this.element.volume + const position = this.player?.currentTime() ?? 0 + const volume = this.player?.volume() this.channel.notify({ method: 'playbackStatusUpdate', params: { position, volume, - duration: this.embed.player.duration(), + duration: this.player?.duration(), playbackState: currentState } }) @@ -132,52 +131,48 @@ export class PeerTubeEmbedApi { currentState = 'playing' this.channel.notify({ method: 'playbackStatusChange', params: 'playing' }) } - this.element.addEventListener('play', this.videoElPlayListener) + this.player.on('play', this.videoElPlayListener) this.videoElPauseListener = () => { currentState = 'paused' this.channel.notify({ method: 'playbackStatusChange', params: 'paused' }) } - this.element.addEventListener('pause', this.videoElPauseListener) + this.player.on('pause', this.videoElPauseListener) this.videoElEndedListener = () => { currentState = 'ended' this.channel.notify({ method: 'playbackStatusChange', params: 'ended' }) } - this.element.addEventListener('ended', this.videoElEndedListener) - - this.oldVideoElement = this.element + this.player.on('ended', this.videoElEndedListener) // --------------------------------------------------------------------------- // PeerTube specific capabilities - this.embed.player.peertubeResolutions().on('resolutions-added', () => this.loadResolutions()) - this.embed.player.peertubeResolutions().on('resolutions-changed', () => this.loadResolutions()) + this.player.peertubeResolutions().on('resolutions-added', () => this.loadResolutions()) + this.player.peertubeResolutions().on('resolutions-changed', () => this.loadResolutions()) this.loadResolutions() - this.embed.player.on('volumechange', () => { + this.player.on('volumechange', () => { this.channel.notify({ method: 'volumeChange', - params: this.embed.player.volume() + params: this.player.volume() }) }) } private disposeStateTracking () { - if (!this.oldVideoElement) return + if (!this.player) return - this.oldVideoElement.removeEventListener('play', this.videoElPlayListener) - this.oldVideoElement.removeEventListener('pause', this.videoElPauseListener) - this.oldVideoElement.removeEventListener('ended', this.videoElEndedListener) + this.player.off('play', this.videoElPlayListener) + this.player.off('pause', this.videoElPauseListener) + this.player.off('ended', this.videoElEndedListener) clearInterval(this.videoElInterval) - - this.oldVideoElement = undefined } private loadResolutions () { - this.resolutions = this.embed.player.peertubeResolutions().getResolutions() + this.resolutions = this.player.peertubeResolutions().getResolutions() .map(r => ({ id: r.id, label: r.label, @@ -193,6 +188,6 @@ export class PeerTubeEmbedApi { } private isWebVideo () { - return !!this.embed.player.webVideo + return !!this.player.webVideo } } diff --git a/client/src/standalone/videos/embed.ts b/client/src/standalone/videos/embed.ts index b61a665e3..5114ec72b 100644 --- a/client/src/standalone/videos/embed.ts +++ b/client/src/standalone/videos/embed.ts @@ -83,10 +83,6 @@ export class PeerTubeEmbed { await embed.init() } - getPlayerElement () { - return this.playerHTML.getPlayerElement() - } - getScope () { return this.playerOptionsBuilder.getScope() } @@ -420,8 +416,8 @@ export class PeerTubeEmbed { playerElement.className = 'video-js vjs-peertube-skin' playerElement.setAttribute('playsinline', 'true') - this.playerHTML.setPlayerElement(playerElement) - this.playerHTML.addPlayerElementToDOM() + this.playerHTML.setInitVideoEl(playerElement) + this.playerHTML.addInitVideoElToDOM() const [ { PeerTubePlayer } ] = await Promise.all([ this.PeerTubePlayerManagerModulePromise, diff --git a/client/src/standalone/videos/shared/player-html.ts b/client/src/standalone/videos/shared/player-html.ts index ffe0261d3..76e5c12fb 100644 --- a/client/src/standalone/videos/shared/player-html.ts +++ b/client/src/standalone/videos/shared/player-html.ts @@ -5,36 +5,32 @@ import { Translations } from './translations' export class PlayerHTML { private readonly wrapperElement: HTMLElement - private playerElement: HTMLVideoElement + private initVideoEl: HTMLVideoElement private informationElement: HTMLDivElement constructor (private readonly videoWrapperId: string) { this.wrapperElement = document.getElementById(this.videoWrapperId) } - getPlayerElement () { - return this.playerElement + getInitVideoEl () { + return this.initVideoEl } - setPlayerElement (playerElement: HTMLVideoElement) { - this.playerElement = playerElement + setInitVideoEl (playerElement: HTMLVideoElement) { + this.initVideoEl = playerElement } - removePlayerElement () { - this.playerElement = null - } - - addPlayerElementToDOM () { - this.wrapperElement.appendChild(this.playerElement) + addInitVideoElToDOM () { + this.wrapperElement.appendChild(this.initVideoEl) } displayError (text: string, translations: Translations) { logger.error(text) // Remove video element - if (this.playerElement) { - this.removeElement(this.playerElement) - this.playerElement = undefined + if (this.initVideoEl) { + this.removeElement(this.initVideoEl) + this.initVideoEl = undefined } const translatedText = peertubeTranslate(text, translations) diff --git a/client/src/standalone/videos/shared/player-options-builder.ts b/client/src/standalone/videos/shared/player-options-builder.ts index 0cd65f721..ef493eae8 100644 --- a/client/src/standalone/videos/shared/player-options-builder.ts +++ b/client/src/standalone/videos/shared/player-options-builder.ts @@ -196,7 +196,7 @@ export class PlayerOptionsBuilder { authorizationHeader, - playerElement: () => this.playerHTML.getPlayerElement(), + playerElement: () => this.playerHTML.getInitVideoEl(), enableHotkeys: true, peertubeLink: () => this.peertubeLink, diff --git a/client/src/standalone/videos/test-embed.ts b/client/src/standalone/videos/test-embed.ts index 70376c383..2d3df0c7f 100644 --- a/client/src/standalone/videos/test-embed.ts +++ b/client/src/standalone/videos/test-embed.ts @@ -32,6 +32,13 @@ window.addEventListener('load', async () => { await player.ready logger.info('Player is ready.') + const updatePlaylistPosition = () => { + player.getCurrentPosition() + .then(position => { + document.getElementById('playlist-position').innerHTML = position + '' + }) + } + const monitoredEvents = [ 'pause', 'play', @@ -40,15 +47,19 @@ window.addEventListener('load', async () => { ] monitoredEvents.forEach(e => { - player.addEventListener(e as PlayerEventType, (param) => logger.info(`PLAYER: event '${e}' received`, { param })) - logger.info(`PLAYER: now listening for event '${e}'`) + player.addEventListener(e as PlayerEventType, param => { + logger.info(`PLAYER: event '${e}' received`, { param }) + + if (e === 'playbackStatusChange' && isPlaylist) { + updatePlaylistPosition() + } + }) if (isPlaylist) { - player.getCurrentPosition() - .then(position => { - document.getElementById('playlist-position').innerHTML = position + '' - }) + updatePlaylistPosition() } + + logger.info(`PLAYER: now listening for event '${e}'`) }) let playbackRates: number[] = [] From 810519b1d41bd938a0e13588d2ef77ea83411e98 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Thu, 16 May 2024 10:37:38 +0200 Subject: [PATCH 2/6] Fix RTL inconsistencies --- .../overview/videos/video-list.component.scss | 3 +- .../plugin-search.component.scss | 2 +- .../plugins/shared/plugin-card.component.html | 2 +- .../steps/register-step-about.component.scss | 4 +-- .../+stats/video/video-stats.component.scss | 3 +- .../edit/video-studio-edit.component.scss | 3 +- .../header/search-typeahead.component.scss | 3 +- .../instance-about-accordion.component.scss | 3 +- .../video-actions-dropdown.component.ts | 2 +- client/src/sass/bootstrap.scss | 10 ++++++ client/src/sass/include/_mixins.scss | 33 +++++++++++++++++++ 11 files changed, 58 insertions(+), 10 deletions(-) diff --git a/client/src/app/+admin/overview/videos/video-list.component.scss b/client/src/app/+admin/overview/videos/video-list.component.scss index d538ca30a..934e7080b 100644 --- a/client/src/app/+admin/overview/videos/video-list.component.scss +++ b/client/src/app/+admin/overview/videos/video-list.component.scss @@ -15,8 +15,9 @@ my-embed { display: flex; my-global-icon { + @include margin-left(3px); + width: 16px; - margin-left: 3px; position: relative; top: -2px; } diff --git a/client/src/app/+admin/plugins/plugin-search/plugin-search.component.scss b/client/src/app/+admin/plugins/plugin-search/plugin-search.component.scss index d7b41f4d8..1406adbf2 100644 --- a/client/src/app/+admin/plugins/plugin-search/plugin-search.component.scss +++ b/client/src/app/+admin/plugins/plugin-search/plugin-search.component.scss @@ -13,7 +13,7 @@ } .search-bar { - margin-left: auto; + @include margin-left(auto); input { @include peertube-input-text(500px); diff --git a/client/src/app/+admin/plugins/shared/plugin-card.component.html b/client/src/app/+admin/plugins/shared/plugin-card.component.html index c9fe6201c..442d7f6f2 100644 --- a/client/src/app/+admin/plugins/shared/plugin-card.component.html +++ b/client/src/app/+admin/plugins/shared/plugin-card.component.html @@ -21,7 +21,7 @@
-
{{ plugin.description }}
+
{{ plugin.description }}
diff --git a/client/src/app/+signup/+register/steps/register-step-about.component.scss b/client/src/app/+signup/+register/steps/register-step-about.component.scss index ab6d6dd4d..3e76ca7a6 100644 --- a/client/src/app/+signup/+register/steps/register-step-about.component.scss +++ b/client/src/app/+signup/+register/steps/register-step-about.component.scss @@ -30,7 +30,7 @@ h4 { } .callout-content { - margin-left: 30px; + @include margin-left(30px); p { margin: 0; @@ -47,7 +47,7 @@ h4 { } .callout-content { - margin-left: 0; + @include margin-left(0); } } } diff --git a/client/src/app/+stats/video/video-stats.component.scss b/client/src/app/+stats/video/video-stats.component.scss index ea75a48d3..99908d243 100644 --- a/client/src/app/+stats/video/video-stats.component.scss +++ b/client/src/app/+stats/video/video-stats.component.scss @@ -33,13 +33,14 @@ } .stats-card { + @include margin-right(15px); + display: flex; justify-content: center; align-items: center; height: fit-content; min-height: 100px; min-width: 200px; - margin-right: 15px; background-color: pvar(--submenuBackgroundColor); margin-bottom: 15px; diff --git a/client/src/app/+video-studio/edit/video-studio-edit.component.scss b/client/src/app/+video-studio/edit/video-studio-edit.component.scss index 43f336f59..f04218aee 100644 --- a/client/src/app/+video-studio/edit/video-studio-edit.component.scss +++ b/client/src/app/+video-studio/edit/video-studio-edit.component.scss @@ -5,8 +5,9 @@ display: flex; .information { + @include margin-left(50px); + width: 100%; - margin-left: 50px; > div { margin-bottom: 30px; diff --git a/client/src/app/header/search-typeahead.component.scss b/client/src/app/header/search-typeahead.component.scss index ff43abd4e..b7df5e3e5 100644 --- a/client/src/app/header/search-typeahead.component.scss +++ b/client/src/app/header/search-typeahead.component.scss @@ -16,7 +16,8 @@ display: inline-flex; align-self: center; position: absolute; - right: 5px; + + @include right(5px); &:hover { opacity: 0.8; diff --git a/client/src/app/shared/shared-instance/instance-about-accordion.component.scss b/client/src/app/shared/shared-instance/instance-about-accordion.component.scss index 8337a7154..c5ad4b1f1 100644 --- a/client/src/app/shared/shared-instance/instance-about-accordion.component.scss +++ b/client/src/app/shared/shared-instance/instance-about-accordion.component.scss @@ -15,7 +15,8 @@ } my-global-icon { + @include margin-right(15px); + line-height: 24px; - margin-right: 15px; } diff --git a/client/src/app/shared/shared-video-miniature/video-actions-dropdown.component.ts b/client/src/app/shared/shared-video-miniature/video-actions-dropdown.component.ts index 3b8e33ba4..6633b869e 100644 --- a/client/src/app/shared/shared-video-miniature/video-actions-dropdown.component.ts +++ b/client/src/app/shared/shared-video-miniature/video-actions-dropdown.component.ts @@ -84,7 +84,7 @@ export class VideoActionsDropdownComponent implements OnChanges { studio: true, stats: true } - @Input() placement = 'left' + @Input() placement = 'left auto' @Input() moreActions: DropdownAction<{ video: Video }>[][] = [] @Input({ transform: booleanAttribute }) actionAvailabilityHint = false diff --git a/client/src/sass/bootstrap.scss b/client/src/sass/bootstrap.scss index 569958154..8c4729966 100644 --- a/client/src/sass/bootstrap.scss +++ b/client/src/sass/bootstrap.scss @@ -384,3 +384,13 @@ body { // Prevent invalid height in parent: https://stackoverflow.com/a/22425601 vertical-align: top; } + + +// --------------------------------------------------------------------------- +// RTL compatibility +// --------------------------------------------------------------------------- + +.modal .modal-header .modal-title { + margin-inline-end: auto; + margin-right: unset; +} diff --git a/client/src/sass/include/_mixins.scss b/client/src/sass/include/_mixins.scss index 3275dd527..83cf6e220 100644 --- a/client/src/sass/include/_mixins.scss +++ b/client/src/sass/include/_mixins.scss @@ -817,6 +817,8 @@ } } +// --------------------------------------------------------------------------- + @mixin margin ($arg1: null, $arg2: null, $arg3: null, $arg4: null) { @if $arg2 == null and $arg3 == null and $arg4 == null { @include margin-original($arg1, $arg1, $arg1, $arg1); @@ -844,6 +846,8 @@ @include rfs($value, margin-inline-end); } +// --------------------------------------------------------------------------- + @mixin padding-original ($block-start, $inline-end, $block-end, $inline-start) { @include padding-left($inline-start); @include padding-right($inline-end); @@ -870,3 +874,32 @@ @mixin padding-right ($value) { @include rfs($value, padding-inline-end); } + +// --------------------------------------------------------------------------- + + +/** + * + * inset-inline properties are not supported by iOS < 14.5 + * + */ + +@mixin right ($value) { + right: $value; + + @supports (inset-inline-end: $value) { + inset-inline-end: $value; + right: unset; + } +} + + +@mixin left ($value) { + left: $value; + + @supports (inset-inline-start: $value) { + inset-inline-start: $value; + left: unset; + } +} + From 21f4ed5c33638156b8ab047da2ac2fd7ce34f228 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Thu, 16 May 2024 11:06:18 +0200 Subject: [PATCH 3/6] Add missing docker env for object storage --- .../config/custom-environment-variables.yaml | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/support/docker/production/config/custom-environment-variables.yaml b/support/docker/production/config/custom-environment-variables.yaml index 7ac794909..ca625c648 100644 --- a/support/docker/production/config/custom-environment-variables.yaml +++ b/support/docker/production/config/custom-environment-variables.yaml @@ -89,13 +89,25 @@ object_storage: bucket_name: "PEERTUBE_OBJECT_STORAGE_STREAMING_PLAYLISTS_BUCKET_NAME" prefix: "PEERTUBE_OBJECT_STORAGE_STREAMING_PLAYLISTS_PREFIX" base_url: "PEERTUBE_OBJECT_STORAGE_STREAMING_PLAYLISTS_BASE_URL" - upload_acl: "PEERTUBE_OBJECT_STORAGE_UPLOAD_ACL" + store_live_streams: + __name: "PEERTUBE_OBJECT_STORAGE_STREAMING_PLAYLISTS_STORE_LIVE_STREAMS" + __format: "json" web_videos: bucket_name: "PEERTUBE_OBJECT_STORAGE_WEB_VIDEOS_BUCKET_NAME" prefix: "PEERTUBE_OBJECT_STORAGE_WEB_VIDEOS_PREFIX" base_url: "PEERTUBE_OBJECT_STORAGE_WEB_VIDEOS_BASE_URL" + user_exports: + bucket_name: "PEERTUBE_OBJECT_STORAGE_USER_EXPORTS_BUCKET_NAME" + prefix: "PEERTUBE_OBJECT_STORAGE_USER_EXPORTS_PREFIX" + base_url: "PEERTUBE_OBJECT_STORAGE_USER_EXPORTS_BASE_URL" + + original_video_files: + bucket_name: "PEERTUBE_OBJECT_STORAGE_ORIGINAL_VIDEO_FILES_BUCKET_NAME" + prefix: "PEERTUBE_OBJECT_STORAGE_ORIGINAL_VIDEO_FILES_PREFIX" + base_url: "PEERTUBE_OBJECT_STORAGE_ORIGINAL_VIDEO_FILES_BASE_URL" + webadmin: configuration: edition: From 81d4d557f754681de035778cef7a82e0f0c0f93b Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Thu, 16 May 2024 11:15:43 +0200 Subject: [PATCH 4/6] Remove internal log --- server/core/models/server/plugin.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/server/core/models/server/plugin.ts b/server/core/models/server/plugin.ts index 13ca809ef..daf31b808 100644 --- a/server/core/models/server/plugin.ts +++ b/server/core/models/server/plugin.ts @@ -7,7 +7,7 @@ import { type PluginType_Type } from '@peertube/peertube-models' import { MPlugin, MPluginFormattable } from '@server/types/models/index.js' -import { FindAndCountOptions, json, QueryTypes } from 'sequelize' +import { FindAndCountOptions, QueryTypes, json } from 'sequelize' import { AllowNull, Column, CreatedAt, DataType, DefaultScope, Is, Table, UpdatedAt } from 'sequelize-typescript' import { isPluginDescriptionValid, @@ -18,7 +18,6 @@ import { isPluginTypeValid } from '../../helpers/custom-validators/plugins.js' import { SequelizeModel, getSort, throwIfNotValid } from '../shared/index.js' -import { logger } from '@server/helpers/logger.js' @DefaultScope(() => ({ attributes: { @@ -174,7 +173,6 @@ export class PluginModel extends SequelizeModel { result[name] = p.settings[name] } } - logger.error('internal', { result }) return result }) From e7ebcb16acbd43043fd7f95e8918b8ab50595796 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Thu, 16 May 2024 11:15:57 +0200 Subject: [PATCH 5/6] Fix big user export file size --- server/core/initializers/constants.ts | 2 +- .../migrations/0840-user-export-size.ts | 24 +++++++++++++++++++ server/core/models/user/user-export.ts | 2 +- 3 files changed, 26 insertions(+), 2 deletions(-) create mode 100644 server/core/initializers/migrations/0840-user-export-size.ts diff --git a/server/core/initializers/constants.ts b/server/core/initializers/constants.ts index 29f556c7c..a3a0b4951 100644 --- a/server/core/initializers/constants.ts +++ b/server/core/initializers/constants.ts @@ -45,7 +45,7 @@ import { cpus } from 'os' // --------------------------------------------------------------------------- -const LAST_MIGRATION_VERSION = 835 +const LAST_MIGRATION_VERSION = 840 // --------------------------------------------------------------------------- diff --git a/server/core/initializers/migrations/0840-user-export-size.ts b/server/core/initializers/migrations/0840-user-export-size.ts new file mode 100644 index 000000000..c8e1e9f97 --- /dev/null +++ b/server/core/initializers/migrations/0840-user-export-size.ts @@ -0,0 +1,24 @@ +import * as Sequelize from 'sequelize' + +async function up (utils: { + transaction: Sequelize.Transaction + queryInterface: Sequelize.QueryInterface + sequelize: Sequelize.Sequelize +}): Promise { + const { transaction } = utils + + { + await utils.queryInterface.changeColumn('userExport', 'size', { + type: Sequelize.BIGINT, + allowNull: true + }, { transaction }) + } +} + +function down (options) { + throw new Error('Not implemented.') +} + +export { + down, up +} diff --git a/server/core/models/user/user-export.ts b/server/core/models/user/user-export.ts index 9c78fdc5a..3b6377ebe 100644 --- a/server/core/models/user/user-export.ts +++ b/server/core/models/user/user-export.ts @@ -55,7 +55,7 @@ export class UserExportModel extends SequelizeModel { error: string @AllowNull(true) - @Column + @Column(DataType.BIGINT) size: number @AllowNull(false) From 3d105296e67b14c8e47af20f8e72f334602ca774 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Thu, 16 May 2024 11:38:36 +0200 Subject: [PATCH 6/6] Fix concurrent live streams serialization issue --- server/core/lib/live/live-manager.ts | 31 +++++++++++++++------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/server/core/lib/live/live-manager.ts b/server/core/lib/live/live-manager.ts index 2480d1795..8741451db 100644 --- a/server/core/lib/live/live-manager.ts +++ b/server/core/lib/live/live-manager.ts @@ -7,6 +7,7 @@ import { hasAudioStream } from '@peertube/peertube-ffmpeg' import { LiveVideoError, LiveVideoErrorType, VideoState } from '@peertube/peertube-models' +import { retryTransactionWrapper } from '@server/helpers/database-utils.js' import { logger, loggerTagsFactory } from '@server/helpers/logger.js' import { CONFIG, registerConfigChangedHandler } from '@server/initializers/config.js' import { VIDEO_LIVE, WEBSERVER } from '@server/initializers/constants.js' @@ -19,6 +20,7 @@ import { VideoLiveModel } from '@server/models/video/video-live.js' import { VideoStreamingPlaylistModel } from '@server/models/video/video-streaming-playlist.js' import { VideoModel } from '@server/models/video/video.js' import { MUser, MVideo, MVideoLiveSession, MVideoLiveVideo, MVideoLiveVideoWithSetting } from '@server/types/models/index.js' +import { FfprobeData } from 'fluent-ffmpeg' import { readFile, readdir } from 'fs/promises' import { Server, createServer } from 'net' import context from 'node-media-server/src/node_core_ctx.js' @@ -36,7 +38,6 @@ import { computeResolutionsToTranscode } from '../transcoding/transcoding-resolu import { LiveQuotaStore } from './live-quota-store.js' import { cleanupAndDestroyPermanentLive, getLiveSegmentTime } from './live-utils.js' import { MuxingSession } from './shared/index.js' -import { FfprobeData } from 'fluent-ffmpeg' // Disable node media server logs nodeMediaServerLogger.setLogType(0) @@ -84,7 +85,7 @@ class LiveManager { const inputPublicUrl = session.inputOriginPublicUrl + streamPath this.handleSession({ sessionId, inputPublicUrl, inputLocalUrl, streamKey: splittedPath[2] }) - .catch(err => logger.error('Cannot handle sessions.', { err, ...lTags(sessionId) })) + .catch(err => logger.error('Cannot handle session', { err, ...lTags(sessionId) })) }) events.on('donePublish', (sessionId: string) => { @@ -558,25 +559,27 @@ class LiveManager { return resolutionsEnabled } - private async saveStartingSession (videoLive: MVideoLiveVideoWithSetting) { + private saveStartingSession (videoLive: MVideoLiveVideoWithSetting) { const replaySettings = videoLive.saveReplay ? new VideoLiveReplaySettingModel({ privacy: videoLive.ReplaySetting.privacy }) : null - return sequelizeTypescript.transaction(async t => { - if (videoLive.saveReplay) { - await replaySettings.save({ transaction: t }) - } + return retryTransactionWrapper(() => { + return sequelizeTypescript.transaction(async t => { + if (videoLive.saveReplay) { + await replaySettings.save({ transaction: t }) + } - return VideoLiveSessionModel.create({ - startDate: new Date(), - liveVideoId: videoLive.videoId, - saveReplay: videoLive.saveReplay, - replaySettingId: videoLive.saveReplay ? replaySettings.id : null, - endingProcessed: false - }, { transaction: t }) + return VideoLiveSessionModel.create({ + startDate: new Date(), + liveVideoId: videoLive.videoId, + saveReplay: videoLive.saveReplay, + replaySettingId: videoLive.saveReplay ? replaySettings.id : null, + endingProcessed: false + }, { transaction: t }) + }) }) }