mirror of
https://github.com/Chocobozzz/PeerTube.git
synced 2024-11-25 10:10:31 -06:00
Add ability to disable storyboards
This commit is contained in:
parent
482223cc23
commit
b9077c83fc
@ -357,6 +357,20 @@
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
|
||||
<ng-container formGroupName="storyboards">
|
||||
|
||||
<div class="form-group">
|
||||
<my-peertube-checkbox
|
||||
inputName="storyboardsEnabled" formControlName="enabled"
|
||||
i18n-labelText labelText="Enable video storyboards"
|
||||
>
|
||||
<ng-container ngProjectAs="description">
|
||||
<span i18n>Generate storyboards of local videos using ffmpeg so users can see the video preview in the player while scrubbing the video</span>
|
||||
</ng-container>
|
||||
</my-peertube-checkbox>
|
||||
</div>
|
||||
|
||||
</ng-container>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -274,6 +274,10 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit {
|
||||
|
||||
instanceCustomHomepage: {
|
||||
content: null
|
||||
},
|
||||
|
||||
storyboards: {
|
||||
enabled: null
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -873,3 +873,7 @@ client:
|
||||
# If you enable only one external auth plugin
|
||||
# You can automatically redirect your users on this external platform when they click on the login button
|
||||
redirect_on_single_external_auth: false
|
||||
|
||||
storyboards:
|
||||
# Generate storyboards of local videos using ffmpeg so users can see the video preview in the player while scrubbing the video
|
||||
enabled: true
|
||||
|
@ -883,3 +883,7 @@ client:
|
||||
# If you enable only one external auth plugin
|
||||
# You can automatically redirect your users on this external platform when they click on the login button
|
||||
redirect_on_single_external_auth: false
|
||||
|
||||
storyboards:
|
||||
# Generate storyboards of local videos using ffmpeg so users can see the video preview in the player while scrubbing the video
|
||||
enabled: true
|
||||
|
@ -257,4 +257,8 @@ export interface CustomConfig {
|
||||
}
|
||||
}
|
||||
|
||||
storyboards: {
|
||||
enabled: boolean
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -321,6 +321,10 @@ export interface ServerConfig {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
storyboards: {
|
||||
enabled: boolean
|
||||
}
|
||||
}
|
||||
|
||||
export type HTMLServerConfig = Omit<ServerConfig, 'signup'>
|
||||
|
@ -567,6 +567,9 @@ export class ConfigCommand extends AbstractCommand {
|
||||
disableLocalSearch: true,
|
||||
isDefaultSearch: true
|
||||
}
|
||||
},
|
||||
storyboards: {
|
||||
enabled: true
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -16,6 +16,7 @@ describe('Test config API validators', function () {
|
||||
const path = '/api/v1/config/custom'
|
||||
let server: PeerTubeServer
|
||||
let userAccessToken: string
|
||||
|
||||
const updateParams: CustomConfig = {
|
||||
instance: {
|
||||
name: 'PeerTube updated',
|
||||
@ -240,6 +241,9 @@ describe('Test config API validators', function () {
|
||||
disableLocalSearch: true,
|
||||
isDefaultSearch: true
|
||||
}
|
||||
},
|
||||
storyboards: {
|
||||
enabled: false
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -123,6 +123,8 @@ function checkInitialConfig (server: PeerTubeServer, data: CustomConfig) {
|
||||
expect(data.broadcastMessage.level).to.equal('info')
|
||||
expect(data.broadcastMessage.message).to.equal('')
|
||||
expect(data.broadcastMessage.dismissable).to.be.false
|
||||
|
||||
expect(data.storyboards.enabled).to.be.true
|
||||
}
|
||||
|
||||
function checkUpdatedConfig (data: CustomConfig) {
|
||||
@ -236,6 +238,8 @@ function checkUpdatedConfig (data: CustomConfig) {
|
||||
expect(data.broadcastMessage.level).to.equal('error')
|
||||
expect(data.broadcastMessage.message).to.equal('super bad message')
|
||||
expect(data.broadcastMessage.dismissable).to.be.true
|
||||
|
||||
expect(data.storyboards.enabled).to.be.false
|
||||
}
|
||||
|
||||
const newCustomConfig: CustomConfig = {
|
||||
@ -460,6 +464,9 @@ const newCustomConfig: CustomConfig = {
|
||||
disableLocalSearch: true,
|
||||
isDefaultSearch: true
|
||||
}
|
||||
},
|
||||
storyboards: {
|
||||
enabled: false
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -209,6 +209,27 @@ describe('Test video storyboard', function () {
|
||||
}
|
||||
})
|
||||
|
||||
it('Should not generate storyboards if disabled by the admin', async function () {
|
||||
this.timeout(60000)
|
||||
|
||||
await servers[0].config.updateExistingSubConfig({
|
||||
newConfig: {
|
||||
storyboards: {
|
||||
enabled: false
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const { uuid } = await servers[0].videos.quickUpload({ name: 'upload', fixture: 'video_short.webm' })
|
||||
await waitJobs(servers)
|
||||
|
||||
for (const server of servers) {
|
||||
const { storyboards } = await server.storyboard.list({ id: uuid })
|
||||
|
||||
expect(storyboards).to.have.lengthOf(0)
|
||||
}
|
||||
})
|
||||
|
||||
after(async function () {
|
||||
await cleanupTests(servers)
|
||||
})
|
||||
|
@ -355,6 +355,9 @@ function customConfig (): CustomConfig {
|
||||
disableLocalSearch: CONFIG.SEARCH.SEARCH_INDEX.DISABLE_LOCAL_SEARCH,
|
||||
isDefaultSearch: CONFIG.SEARCH.SEARCH_INDEX.IS_DEFAULT_SEARCH
|
||||
}
|
||||
},
|
||||
storyboards: {
|
||||
enabled: CONFIG.STORYBOARDS.ENABLED
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ import { CreateJobArgument, CreateJobOptions, JobQueue } from '@server/lib/job-q
|
||||
import { Hooks } from '@server/lib/plugins/hooks.js'
|
||||
import { regenerateMiniaturesIfNeeded } from '@server/lib/thumbnail.js'
|
||||
import { uploadx } from '@server/lib/uploadx.js'
|
||||
import { buildMoveJob } from '@server/lib/video.js'
|
||||
import { buildMoveJob, buildStoryboardJobIfNeeded } from '@server/lib/video.js'
|
||||
import { autoBlacklistVideoIfNeeded } from '@server/lib/video-blacklist.js'
|
||||
import { buildNewFile } from '@server/lib/video-file.js'
|
||||
import { VideoPathManager } from '@server/lib/video-path-manager.js'
|
||||
@ -152,14 +152,7 @@ async function addVideoJobsAfterUpload (video: MVideoFullLight, videoFile: MVide
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
type: 'generate-video-storyboard' as 'generate-video-storyboard',
|
||||
payload: {
|
||||
videoUUID: video.uuid,
|
||||
// No need to federate, we process these jobs sequentially
|
||||
federate: false
|
||||
}
|
||||
},
|
||||
buildStoryboardJobIfNeeded({ video, federate: false }),
|
||||
|
||||
{
|
||||
type: 'federate-video' as 'federate-video',
|
||||
|
@ -6,7 +6,7 @@ import { getLocalVideoActivityPubUrl } from '@server/lib/activitypub/url.js'
|
||||
import { CreateJobArgument, CreateJobOptions, JobQueue } from '@server/lib/job-queue/index.js'
|
||||
import { Redis } from '@server/lib/redis.js'
|
||||
import { uploadx } from '@server/lib/uploadx.js'
|
||||
import { buildLocalVideoFromReq, buildMoveJob, buildVideoThumbnailsFromReq, setVideoTags } from '@server/lib/video.js'
|
||||
import { buildLocalVideoFromReq, buildMoveJob, buildStoryboardJobIfNeeded, buildVideoThumbnailsFromReq, setVideoTags } from '@server/lib/video.js'
|
||||
import { buildNewFile } from '@server/lib/video-file.js'
|
||||
import { VideoPathManager } from '@server/lib/video-path-manager.js'
|
||||
import { buildNextVideoState } from '@server/lib/video-state.js'
|
||||
@ -248,14 +248,7 @@ async function addVideoJobsAfterUpload (video: MVideoFullLight, videoFile: MVide
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
type: 'generate-video-storyboard' as 'generate-video-storyboard',
|
||||
payload: {
|
||||
videoUUID: video.uuid,
|
||||
// No need to federate, we process these jobs sequentially
|
||||
federate: false
|
||||
}
|
||||
},
|
||||
buildStoryboardJobIfNeeded({ video, federate: false }),
|
||||
|
||||
{
|
||||
type: 'notify',
|
||||
|
@ -84,7 +84,8 @@ function checkMissedConfig () {
|
||||
'live.transcoding.resolutions.144p', 'live.transcoding.resolutions.240p', 'live.transcoding.resolutions.360p',
|
||||
'live.transcoding.resolutions.480p', 'live.transcoding.resolutions.720p', 'live.transcoding.resolutions.1080p',
|
||||
'live.transcoding.resolutions.1440p', 'live.transcoding.resolutions.2160p', 'live.transcoding.always_transcode_original_resolution',
|
||||
'live.transcoding.remote_runners.enabled'
|
||||
'live.transcoding.remote_runners.enabled',
|
||||
'storyboards.enabled'
|
||||
]
|
||||
|
||||
const requiredAlternatives = [
|
||||
|
@ -610,8 +610,10 @@ const CONFIG = {
|
||||
get DISABLE_LOCAL_SEARCH () { return config.get<boolean>('search.search_index.disable_local_search') },
|
||||
get IS_DEFAULT_SEARCH () { return config.get<boolean>('search.search_index.is_default_search') }
|
||||
}
|
||||
},
|
||||
STORYBOARDS: {
|
||||
get ENABLED () { return config.get<boolean>('storyboards.enabled') }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function registerConfigChangedHandler (fun: Function) {
|
||||
@ -682,7 +684,7 @@ export function reloadConfig () {
|
||||
return process.env.NODE_CONFIG_DIR.split(':')
|
||||
}
|
||||
|
||||
return [ join(root(), 'config') ]
|
||||
return [join(root(), 'config')]
|
||||
}
|
||||
|
||||
function purge () {
|
||||
|
@ -25,7 +25,7 @@ import { createOptimizeOrMergeAudioJobs } from '@server/lib/transcoding/create-t
|
||||
import { isAbleToUploadVideo } from '@server/lib/user.js'
|
||||
import { VideoPathManager } from '@server/lib/video-path-manager.js'
|
||||
import { buildNextVideoState } from '@server/lib/video-state.js'
|
||||
import { buildMoveJob } from '@server/lib/video.js'
|
||||
import { buildMoveJob, buildStoryboardJobIfNeeded } from '@server/lib/video.js'
|
||||
import { MUserId, MVideoFile, MVideoFullLight } from '@server/types/models/index.js'
|
||||
import { MVideoImport, MVideoImportDefault, MVideoImportDefaultFiles, MVideoImportVideo } from '@server/types/models/video/video-import.js'
|
||||
import { getLowercaseExtension } from '@peertube/peertube-node-utils'
|
||||
@ -307,13 +307,7 @@ async function afterImportSuccess (options: {
|
||||
}
|
||||
|
||||
// Generate the storyboard in the job queue, and don't forget to federate an update after
|
||||
await JobQueue.Instance.createJob({
|
||||
type: 'generate-video-storyboard' as 'generate-video-storyboard',
|
||||
payload: {
|
||||
videoUUID: video.uuid,
|
||||
federate: true
|
||||
}
|
||||
})
|
||||
await JobQueue.Instance.createJob(buildStoryboardJobIfNeeded({ video, federate: true }))
|
||||
|
||||
if (video.state === VideoState.TO_MOVE_TO_EXTERNAL_STORAGE) {
|
||||
await JobQueue.Instance.createJob(
|
||||
|
@ -30,6 +30,7 @@ import { ffprobePromise, getAudioStream, getVideoStreamDimensionsInfo, getVideoS
|
||||
import { logger, loggerTagsFactory } from '../../../helpers/logger.js'
|
||||
import { JobQueue } from '../job-queue.js'
|
||||
import { isVideoInPublicDirectory } from '@server/lib/video-privacy.js'
|
||||
import { buildStoryboardJobIfNeeded } from '@server/lib/video.js'
|
||||
|
||||
const lTags = loggerTagsFactory('live', 'job')
|
||||
|
||||
@ -302,11 +303,5 @@ async function cleanupLiveAndFederate (options: {
|
||||
}
|
||||
|
||||
function createStoryboardJob (video: MVideo) {
|
||||
return JobQueue.Instance.createJob({
|
||||
type: 'generate-video-storyboard' as 'generate-video-storyboard',
|
||||
payload: {
|
||||
videoUUID: video.uuid,
|
||||
federate: true
|
||||
}
|
||||
})
|
||||
return JobQueue.Instance.createJob(buildStoryboardJobIfNeeded({ video, federate: true }))
|
||||
}
|
||||
|
@ -336,7 +336,9 @@ class JobQueue {
|
||||
.catch(err => logger.error('Cannot create job.', { err, options }))
|
||||
}
|
||||
|
||||
createJob (options: CreateJobArgument & CreateJobOptions) {
|
||||
createJob (options: CreateJobArgument & CreateJobOptions | undefined) {
|
||||
if (!options) return
|
||||
|
||||
const queue: Queue = this.queues[options.type]
|
||||
if (queue === undefined) {
|
||||
logger.error('Unknown queue %s: cannot create job.', options.type)
|
||||
|
@ -290,6 +290,10 @@ class ServerConfigManager {
|
||||
users: CONFIG.VIEWS.VIDEOS.WATCHING_INTERVAL.USERS
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
storyboards: {
|
||||
enabled: CONFIG.STORYBOARDS.ENABLED
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,6 +16,7 @@ import { buildFileMetadata } from '../video-file.js'
|
||||
import { VideoPathManager } from '../video-path-manager.js'
|
||||
import { buildFFmpegVOD } from './shared/index.js'
|
||||
import { buildOriginalFileResolution } from './transcoding-resolutions.js'
|
||||
import { buildStoryboardJobIfNeeded } from '../video.js'
|
||||
|
||||
// Optimize the original video file and replace it. The resolution is not changed.
|
||||
export async function optimizeOriginalVideofile (options: {
|
||||
@ -247,14 +248,7 @@ export async function onWebVideoFileTranscoding (options: {
|
||||
video.VideoFiles = await video.$get('VideoFiles')
|
||||
|
||||
if (wasAudioFile) {
|
||||
await JobQueue.Instance.createJob({
|
||||
type: 'generate-video-storyboard' as 'generate-video-storyboard',
|
||||
payload: {
|
||||
videoUUID: video.uuid,
|
||||
// No need to federate, we process these jobs sequentially
|
||||
federate: false
|
||||
}
|
||||
})
|
||||
await JobQueue.Instance.createJob(buildStoryboardJobIfNeeded({ video, federate: false }))
|
||||
}
|
||||
|
||||
return { video, videoFile }
|
||||
|
@ -11,6 +11,7 @@ import { VideoStudioTranscodingJobHandler } from './runners/index.js'
|
||||
import { getTranscodingJobPriority } from './transcoding/transcoding-priority.js'
|
||||
import { buildNewFile, removeHLSPlaylist, removeWebVideoFile } from './video-file.js'
|
||||
import { VideoPathManager } from './video-path-manager.js'
|
||||
import { buildStoryboardJobIfNeeded } from './video.js'
|
||||
|
||||
const lTags = loggerTagsFactory('video-studio')
|
||||
|
||||
@ -106,13 +107,7 @@ export async function onVideoStudioEnded (options: {
|
||||
await video.save()
|
||||
|
||||
return JobQueue.Instance.createSequentialJobFlow(
|
||||
{
|
||||
type: 'generate-video-storyboard' as 'generate-video-storyboard',
|
||||
payload: {
|
||||
videoUUID: video.uuid,
|
||||
federate: false
|
||||
}
|
||||
},
|
||||
buildStoryboardJobIfNeeded({ video, federate: false }),
|
||||
|
||||
{
|
||||
type: 'federate-video' as 'federate-video',
|
||||
|
@ -16,7 +16,7 @@ import { TagModel } from '@server/models/video/tag.js'
|
||||
import { VideoJobInfoModel } from '@server/models/video/video-job-info.js'
|
||||
import { VideoModel } from '@server/models/video/video.js'
|
||||
import { FilteredModelAttributes } from '@server/types/index.js'
|
||||
import { MThumbnail, MVideoFullLight, MVideoTag, MVideoThumbnail, MVideoUUID } from '@server/types/models/index.js'
|
||||
import { MThumbnail, MVideo, MVideoFullLight, MVideoTag, MVideoThumbnail, MVideoUUID } from '@server/types/models/index.js'
|
||||
import { CreateJobArgument, JobQueue } from './job-queue/job-queue.js'
|
||||
import { updateLocalVideoMiniatureFromExisting } from './thumbnail.js'
|
||||
import { moveFilesIfPrivacyChanged } from './video-privacy.js'
|
||||
@ -117,6 +117,37 @@ export async function buildMoveJob (options: {
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export function buildStoryboardJobIfNeeded (options: {
|
||||
video: MVideo
|
||||
federate: boolean
|
||||
}) {
|
||||
const { video, federate } = options
|
||||
|
||||
if (CONFIG.STORYBOARDS.ENABLED) {
|
||||
return {
|
||||
type: 'generate-video-storyboard' as 'generate-video-storyboard',
|
||||
payload: {
|
||||
videoUUID: video.uuid,
|
||||
federate
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (federate === true) {
|
||||
return {
|
||||
type: 'federate-video' as 'federate-video',
|
||||
payload: {
|
||||
videoUUID: video.uuid,
|
||||
isNewVideoForFederation: false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return undefined
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export async function getVideoDuration (videoId: number | string) {
|
||||
const video = await VideoModel.load(videoId)
|
||||
|
||||
|
@ -4,6 +4,7 @@ import { initDatabaseModels } from '@server/initializers/database.js'
|
||||
import { JobQueue } from '@server/lib/job-queue/index.js'
|
||||
import { StoryboardModel } from '@server/models/video/storyboard.js'
|
||||
import { VideoModel } from '@server/models/video/video.js'
|
||||
import { buildStoryboardJobIfNeeded } from '@server/lib/video.js'
|
||||
|
||||
program
|
||||
.description('Generate videos storyboard')
|
||||
@ -60,13 +61,7 @@ async function run () {
|
||||
|
||||
if (videoFull.isLive) continue
|
||||
|
||||
await JobQueue.Instance.createJob({
|
||||
type: 'generate-video-storyboard',
|
||||
payload: {
|
||||
videoUUID: videoFull.uuid,
|
||||
federate: true
|
||||
}
|
||||
})
|
||||
await JobQueue.Instance.createJob(buildStoryboardJobIfNeeded({ video: videoFull, federate: true }))
|
||||
|
||||
console.log(`Created generate-storyboard job for ${videoFull.name}.`)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user