Ability for admins to set default upload values

This commit is contained in:
Chocobozzz 2021-12-14 17:17:01 +01:00
parent a6f919e455
commit 3cf68b869d
No known key found for this signature in database
GPG Key ID: 583A612D890159BE
31 changed files with 467 additions and 89 deletions

View File

@ -21,6 +21,24 @@ export class VideoWatchPage {
return this.getVideoNameElement().then(e => e.getText()) return this.getVideoNameElement().then(e => e.getText())
} }
getPrivacy () {
return $('.attribute-privacy .attribute-value').getText()
}
getLicence () {
return $('.attribute-licence .attribute-value').getText()
}
async isDownloadEnabled () {
await this.clickOnMoreDropdownIcon()
return $('.dropdown-item .icon-download').isExisting()
}
areCommentsEnabled () {
return $('my-video-comment-add').isExisting()
}
async goOnAssociatedEmbed () { async goOnAssociatedEmbed () {
let url = await browser.getUrl() let url = await browser.getUrl()
url = url.replace('/w/', '/videos/embed/') url = url.replace('/w/', '/videos/embed/')
@ -38,10 +56,8 @@ export class VideoWatchPage {
} }
async clickOnUpdate () { async clickOnUpdate () {
const dropdown = $('my-video-actions-dropdown .action-button') await this.clickOnMoreDropdownIcon()
await dropdown.click()
await $('.dropdown-menu.show .dropdown-item').waitForDisplayed()
const items = await $$('.dropdown-menu.show .dropdown-item') const items = await $$('.dropdown-menu.show .dropdown-item')
for (const item of items) { for (const item of items) {
@ -86,6 +102,13 @@ export class VideoWatchPage {
}, { timeout: maxTime }) }, { timeout: maxTime })
} }
async clickOnMoreDropdownIcon () {
const dropdown = $('my-video-actions-dropdown .action-button')
await dropdown.click()
await $('.dropdown-menu.show .dropdown-item').waitForDisplayed()
}
private async getVideoNameElement () { private async getVideoNameElement () {
// We have 2 video info name block, pick the first that is not empty // We have 2 video info name block, pick the first that is not empty
const elem = async () => { const elem = async () => {

View File

@ -0,0 +1,37 @@
import { LoginPage } from '../po/login.po'
import { VideoUploadPage } from '../po/video-upload.po'
import { VideoWatchPage } from '../po/video-watch.po'
import { isMobileDevice, isSafari, waitServerUp } from '../utils'
describe('Custom server defaults', () => {
let videoUploadPage: VideoUploadPage
let loginPage: LoginPage
let videoWatchPage: VideoWatchPage
before(async () => {
await waitServerUp()
})
beforeEach(async () => {
loginPage = new LoginPage()
videoUploadPage = new VideoUploadPage()
videoWatchPage = new VideoWatchPage(isMobileDevice(), isSafari())
await browser.maximizeWindow()
})
it('Should upload a video with custom default values', async function () {
await loginPage.loginAsRootUser()
await videoUploadPage.navigateTo()
await videoUploadPage.uploadVideo()
await videoUploadPage.validSecondUploadStep('video')
await videoWatchPage.waitWatchVideoName('video')
expect(await videoWatchPage.getPrivacy()).toBe('Internal')
expect(await videoWatchPage.getLicence()).toBe('Attribution - Non Commercial')
expect(await videoWatchPage.isDownloadEnabled()).toBeFalsy()
expect(await videoWatchPage.areCommentsEnabled()).toBeFalsy()
})
})

View File

@ -4,6 +4,7 @@ import { MyAccountPage } from '../po/my-account'
import { VideoListPage } from '../po/video-list.po' import { VideoListPage } from '../po/video-list.po'
import { VideoSearchPage } from '../po/video-search.po' import { VideoSearchPage } from '../po/video-search.po'
import { VideoUploadPage } from '../po/video-upload.po' import { VideoUploadPage } from '../po/video-upload.po'
import { VideoWatchPage } from '../po/video-watch.po'
import { NSFWPolicy } from '../types/common' import { NSFWPolicy } from '../types/common'
import { isMobileDevice, isSafari, waitServerUp } from '../utils' import { isMobileDevice, isSafari, waitServerUp } from '../utils'
@ -14,6 +15,7 @@ describe('Videos list', () => {
let loginPage: LoginPage let loginPage: LoginPage
let myAccountPage: MyAccountPage let myAccountPage: MyAccountPage
let videoSearchPage: VideoSearchPage let videoSearchPage: VideoSearchPage
let videoWatchPage: VideoWatchPage
const seed = Math.random() const seed = Math.random()
const nsfwVideo = seed + ' - nsfw' const nsfwVideo = seed + ' - nsfw'
@ -108,6 +110,7 @@ describe('Videos list', () => {
videoUploadPage = new VideoUploadPage() videoUploadPage = new VideoUploadPage()
myAccountPage = new MyAccountPage() myAccountPage = new MyAccountPage()
videoSearchPage = new VideoSearchPage() videoSearchPage = new VideoSearchPage()
videoWatchPage = new VideoWatchPage(isMobileDevice(), isSafari())
await browser.maximizeWindow() await browser.maximizeWindow()
}) })
@ -191,5 +194,26 @@ describe('Videos list', () => {
await checkCommonVideoListPages('display') await checkCommonVideoListPages('display')
await checkSearchPage('display') await checkSearchPage('display')
}) })
after(async () => {
await loginPage.logout()
})
})
describe('Default upload values', function () {
it('Should have default video values', async function () {
await loginPage.loginAsRootUser()
await videoUploadPage.navigateTo()
await videoUploadPage.uploadVideo()
await videoUploadPage.validSecondUploadStep('video')
await videoWatchPage.waitWatchVideoName('video')
expect(await videoWatchPage.getPrivacy()).toBe('Public')
expect(await videoWatchPage.getLicence()).toBe('Unknown')
expect(await videoWatchPage.isDownloadEnabled()).toBeTruthy()
expect(await videoWatchPage.areCommentsEnabled()).toBeTruthy()
})
}) })
}) })

View File

@ -0,0 +1,64 @@
import { ChildProcessWithoutNullStreams } from 'child_process'
import { basename } from 'path'
import { runCommand, runServer } from './server'
let appInstance: string
let app: ChildProcessWithoutNullStreams
async function beforeLocalSuite (suite: any) {
const config = buildConfig(suite.file)
await runCommand('npm run clean:server:test -- ' + appInstance)
app = runServer(appInstance, config)
}
function afterLocalSuite () {
app.kill()
app = undefined
}
function beforeLocalSession (config: { baseUrl: string }, capabilities: { browserName: string }) {
appInstance = capabilities['browserName'] === 'chrome' ? '1' : '2'
config.baseUrl = 'http://localhost:900' + appInstance
}
async function onBrowserStackPrepare () {
const appInstance = '1'
await runCommand('npm run clean:server:test -- ' + appInstance)
app = runServer(appInstance)
}
function onBrowserStackComplete () {
app.kill()
app = undefined
}
export {
beforeLocalSession,
afterLocalSuite,
beforeLocalSuite,
onBrowserStackPrepare,
onBrowserStackComplete
}
// ---------------------------------------------------------------------------
function buildConfig (suiteFile: string = undefined) {
const filename = basename(suiteFile)
if (filename === 'custom-server-defaults.e2e-spec.ts') {
return {
defaults: {
publish: {
download_enabled: false,
comments_enabled: false,
privacy: 4,
licence: 4
}
}
}
}
return {}
}

View File

@ -1,3 +1,5 @@
export * from './common' export * from './common'
export * from './elements' export * from './elements'
export * from './hooks'
export * from './server'
export * from './urls' export * from './urls'

View File

@ -0,0 +1,63 @@
import { exec, spawn } from 'child_process'
import { join, resolve } from 'path'
function runServer (appInstance: string, config: any = {}) {
const env = Object.create(process.env)
env['NODE_ENV'] = 'test'
env['NODE_APP_INSTANCE'] = appInstance
env['NODE_CONFIG'] = JSON.stringify({
rates_limit: {
api: {
max: 5000
},
login: {
max: 5000
}
},
log: {
level: 'warn'
},
signup: {
enabled: false
},
transcoding: {
enabled: false
},
...config
})
const forkOptions = {
env,
cwd: getRootCWD(),
detached: false
}
const p = spawn('node', [ join('dist', 'server.js') ], forkOptions)
p.stderr.on('data', data => console.error(data.toString()))
p.stdout.on('data', data => console.error(data.toString()))
return p
}
function runCommand (command: string) {
return new Promise<void>((res, rej) => {
const p = exec(command, { cwd: getRootCWD() })
p.stderr.on('data', data => console.error(data.toString()))
p.on('error', err => rej(err))
p.on('exit', () => res())
})
}
export {
runServer,
runCommand
}
// ---------------------------------------------------------------------------
function getRootCWD () {
return resolve('../..')
}

View File

@ -1,3 +1,4 @@
import { onBrowserStackComplete, onBrowserStackPrepare } from './src/utils'
import { config as mainConfig } from './wdio.main.conf' import { config as mainConfig } from './wdio.main.conf'
const user = process.env.BROWSERSTACK_USER const user = process.env.BROWSERSTACK_USER
@ -114,6 +115,10 @@ module.exports = {
if (capabilities['bstack:options'].realMobile === true) { if (capabilities['bstack:options'].realMobile === true) {
capabilities['bstack:options'].local = false capabilities['bstack:options'].local = false
} }
} },
onPrepare: onBrowserStackPrepare,
onComplete: onBrowserStackComplete
} as WebdriverIO.Config } as WebdriverIO.Config
} }

View File

@ -1,3 +1,4 @@
import { afterLocalSuite, beforeLocalSuite, beforeLocalSession } from './src/utils'
import { config as mainConfig } from './wdio.main.conf' import { config as mainConfig } from './wdio.main.conf'
const prefs = { const prefs = {
@ -21,12 +22,16 @@ module.exports = {
browserName: 'chrome', browserName: 'chrome',
acceptInsecureCerts: true, acceptInsecureCerts: true,
'goog:chromeOptions': { 'goog:chromeOptions': {
args: [ '--headless', '--disable-gpu', '--window-size=1280,1024' ], args: [ '--disable-gpu', '--window-size=1280,1024' ],
prefs prefs
} }
} }
], ],
services: [ 'chromedriver' ] services: [ 'chromedriver' ],
beforeSession: beforeLocalSession,
beforeSuite: beforeLocalSuite,
afterSuite: afterLocalSuite
} as WebdriverIO.Config } as WebdriverIO.Config
} }

View File

@ -1,3 +1,4 @@
import { afterLocalSuite, beforeLocalSession, beforeLocalSuite } from './src/utils'
import { config as mainConfig } from './wdio.main.conf' import { config as mainConfig } from './wdio.main.conf'
const prefs = { const prefs = {
@ -11,7 +12,7 @@ module.exports = {
runner: 'local', runner: 'local',
maxInstances: 2, maxInstancesPerCapability: 1,
capabilities: [ capabilities: [
{ {
@ -34,12 +35,8 @@ module.exports = {
services: [ 'chromedriver', 'geckodriver' ], services: [ 'chromedriver', 'geckodriver' ],
beforeSession: function (config, capabilities) { beforeSession: beforeLocalSession,
if (capabilities['browserName'] === 'chrome') { beforeSuite: beforeLocalSuite,
config.baseUrl = 'http://localhost:9001' afterSuite: afterLocalSuite
} else {
config.baseUrl = 'http://localhost:9002'
}
}
} as WebdriverIO.Config } as WebdriverIO.Config
} }

View File

@ -110,9 +110,10 @@ export class VideoEditComponent implements OnInit, OnDestroy {
updateForm () { updateForm () {
const defaultValues: any = { const defaultValues: any = {
nsfw: 'false', nsfw: 'false',
commentsEnabled: 'true', commentsEnabled: this.serverConfig.defaults.publish.commentsEnabled,
downloadEnabled: 'true', downloadEnabled: this.serverConfig.defaults.publish.downloadEnabled,
waitTranscoding: 'true', waitTranscoding: 'true',
licence: this.serverConfig.defaults.publish.licence,
tags: [] tags: []
} }
const obj: any = { const obj: any = {
@ -160,6 +161,8 @@ export class VideoEditComponent implements OnInit, OnDestroy {
} }
ngOnInit () { ngOnInit () {
this.serverConfig = this.serverService.getHTMLConfig()
this.updateForm() this.updateForm()
this.pluginService.ensurePluginsAreLoaded('video-edit') this.pluginService.ensurePluginsAreLoaded('video-edit')
@ -200,8 +203,6 @@ export class VideoEditComponent implements OnInit, OnDestroy {
} }
}) })
this.serverConfig = this.serverService.getHTMLConfig()
this.initialVideoCaptions = this.videoCaptions.map(c => c.language.id) this.initialVideoCaptions = this.videoCaptions.map(c => c.language.id)
this.ngZone.runOutsideAngular(() => { this.ngZone.runOutsideAngular(() => {

View File

@ -70,8 +70,6 @@ export class VideoGoLiveComponent extends VideoSend implements OnInit, AfterView
privacy: this.highestPrivacy, privacy: this.highestPrivacy,
nsfw: this.serverConfig.instance.isNSFW, nsfw: this.serverConfig.instance.isNSFW,
waitTranscoding: true, waitTranscoding: true,
commentsEnabled: true,
downloadEnabled: true,
permanentLive: this.firstStepPermanentLive, permanentLive: this.firstStepPermanentLive,
saveReplay: this.firstStepPermanentLive === false && this.isReplayAllowed(), saveReplay: this.firstStepPermanentLive === false && this.isReplayAllowed(),
channelId: this.firstStepChannelId channelId: this.firstStepChannelId

View File

@ -81,8 +81,6 @@ export class VideoImportTorrentComponent extends VideoSend implements OnInit, Af
const videoUpdate: VideoUpdate = { const videoUpdate: VideoUpdate = {
privacy: this.highestPrivacy, privacy: this.highestPrivacy,
waitTranscoding: false, waitTranscoding: false,
commentsEnabled: true,
downloadEnabled: true,
channelId: this.firstStepChannelId channelId: this.firstStepChannelId
} }

View File

@ -68,8 +68,6 @@ export class VideoImportUrlComponent extends VideoSend implements OnInit, AfterV
const videoUpdate: VideoUpdate = { const videoUpdate: VideoUpdate = {
privacy: this.highestPrivacy, privacy: this.highestPrivacy,
waitTranscoding: false, waitTranscoding: false,
commentsEnabled: true,
downloadEnabled: true,
channelId: this.firstStepChannelId channelId: this.firstStepChannelId
} }

View File

@ -49,7 +49,9 @@ export abstract class VideoSend extends FormReactive implements OnInit {
this.serverService.getVideoPrivacies() this.serverService.getVideoPrivacies()
.subscribe( .subscribe(
privacies => { privacies => {
const { videoPrivacies, defaultPrivacyId } = this.videoService.explainedPrivacyLabels(privacies) const defaultPrivacy = this.serverConfig.defaults.publish.privacy
const { videoPrivacies, defaultPrivacyId } = this.videoService.explainedPrivacyLabels(privacies, defaultPrivacy)
this.videoPrivacies = videoPrivacies this.videoPrivacies = videoPrivacies
this.firstStepPrivacyId = defaultPrivacyId this.firstStepPrivacyId = defaultPrivacyId

View File

@ -277,8 +277,6 @@ export class VideoUploadComponent extends VideoSend implements OnInit, OnDestroy
private uploadFile (file: File, previewfile?: File) { private uploadFile (file: File, previewfile?: File) {
const metadata = { const metadata = {
waitTranscoding: true, waitTranscoding: true,
commentsEnabled: true,
downloadEnabled: true,
channelId: this.firstStepChannelId, channelId: this.firstStepChannelId,
nsfw: this.serverConfig.instance.isNSFW, nsfw: this.serverConfig.instance.isNSFW,
privacy: this.highestPrivacy.toString(), privacy: this.highestPrivacy.toString(),

View File

@ -36,7 +36,7 @@
<ng-container *ngIf="!isUserLoggedIn && !video.isLive"> <ng-container *ngIf="!isUserLoggedIn && !video.isLive">
<button <button
*ngIf="isVideoDownloadable()" class="action-button action-button-save" *ngIf="isVideoDownloadable()" class="action-button action-button-download"
(click)="showDownloadModal()" (keydown.enter)="showDownloadModal()" (click)="showDownloadModal()" (keydown.enter)="showDownloadModal()"
> >
<my-global-icon iconName="download" aria-hidden="true"></my-global-icon> <my-global-icon iconName="download" aria-hidden="true"></my-global-icon>

View File

@ -49,7 +49,8 @@
} }
} }
&.action-button-save { &.action-button-save,
&.action-button-download {
my-global-icon { my-global-icon {
top: 0 !important; top: 0 !important;
right: -1px; right: -1px;

View File

@ -1,9 +1,9 @@
<div class="attribute"> <div class="attribute attribute-privacy">
<span i18n class="attribute-label">Privacy</span> <span i18n class="attribute-label">Privacy</span>
<span class="attribute-value">{{ video.privacy.label }}</span> <span class="attribute-value">{{ video.privacy.label }}</span>
</div> </div>
<div *ngIf="video.isLocal === false" class="attribute"> <div *ngIf="video.isLocal === false" class="attribute attribute-origin">
<span i18n class="attribute-label">Origin</span> <span i18n class="attribute-label">Origin</span>
<a <a
class="attribute-value" target="_blank" rel="noopener noreferrer" class="attribute-value" target="_blank" rel="noopener noreferrer"
@ -16,12 +16,12 @@
></a> ></a>
</div> </div>
<div *ngIf="!!video.originallyPublishedAt" class="attribute"> <div *ngIf="!!video.originallyPublishedAt" class="attribute attribute-originally-published-at">
<span i18n class="attribute-label">Originally published</span> <span i18n class="attribute-label">Originally published</span>
<span class="attribute-value">{{ video.originallyPublishedAt | date: 'dd MMMM yyyy' }}</span> <span class="attribute-value">{{ video.originallyPublishedAt | date: 'dd MMMM yyyy' }}</span>
</div> </div>
<div class="attribute"> <div class="attribute attribute-category">
<span i18n class="attribute-label">Category</span> <span i18n class="attribute-label">Category</span>
<span *ngIf="!video.category.id" class="attribute-value">{{ video.category.label }}</span> <span *ngIf="!video.category.id" class="attribute-value">{{ video.category.label }}</span>
<a <a
@ -30,7 +30,7 @@
>{{ video.category.label }}</a> >{{ video.category.label }}</a>
</div> </div>
<div class="attribute"> <div class="attribute attribute-licence">
<span i18n class="attribute-label">Licence</span> <span i18n class="attribute-label">Licence</span>
<span *ngIf="!video.licence.id" class="attribute-value">{{ video.licence.label }}</span> <span *ngIf="!video.licence.id" class="attribute-value">{{ video.licence.label }}</span>
<a <a
@ -39,7 +39,7 @@
>{{ video.licence.label }}</a> >{{ video.licence.label }}</a>
</div> </div>
<div class="attribute"> <div class="attribute attribute-language">
<span i18n class="attribute-label">Language</span> <span i18n class="attribute-label">Language</span>
<span *ngIf="!video.language.id" class="attribute-value">{{ video.language.label }}</span> <span *ngIf="!video.language.id" class="attribute-value">{{ video.language.label }}</span>
<a <a
@ -56,7 +56,7 @@
>{{ tag }}</a> >{{ tag }}</a>
</div> </div>
<div class="attribute" *ngIf="!video.isLive"> <div class="attribute attribute-duration" *ngIf="!video.isLive">
<span i18n class="attribute-label">Duration</span> <span i18n class="attribute-label">Duration</span>
<span class="attribute-value">{{ video.duration | myDurationFormatter }}</span> <span class="attribute-value">{{ video.duration | myDurationFormatter }}</span>
</div> </div>

View File

@ -75,18 +75,22 @@ email:
subject: subject:
prefix: '[PeerTube]' prefix: '[PeerTube]'
# PeerTube client/interface configuration # Update default PeerTube values
client: # Set by API when the field is not provided and put as default value in client
videos: defaults:
miniature: # Change default values when publishing a video (upload/import/go Live)
# By default PeerTube client displays author username publish:
prefer_author_display_name: false download_enabled: true
menu: comments_enabled: true
login:
# If you enable only one external auth plugin # public = 1, unlisted = 2, private = 3, internal = 4
# You can automatically redirect your users on this external platform when they click on the login button privacy: 1
redirect_on_single_external_auth: false
# CC-BY = 1, CC-SA = 2, CC-ND = 3, CC-NC = 4, CC-NC-SA = 5, CC-NC-ND = 6, Public Domain = 7
# You can also choose a custom licence value added by a plugin
# No licence by default
licence: null
# From the project root directory # From the project root directory
storage: storage:
@ -587,3 +591,16 @@ search:
disable_local_search: false disable_local_search: false
# If you did not disable local search, you can decide to use the search index by default # If you did not disable local search, you can decide to use the search index by default
is_default_search: false is_default_search: false
# PeerTube client/interface configuration
client:
videos:
miniature:
# By default PeerTube client displays author username
prefer_author_display_name: false
menu:
login:
# 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

View File

@ -73,18 +73,22 @@ email:
subject: subject:
prefix: '[PeerTube]' prefix: '[PeerTube]'
# PeerTube client/interface configuration # Update default PeerTube values
client: # Set by API when the field is not provided and put as default value in client
videos: defaults:
miniature: # Change default values when publishing a video (upload/import/go Live)
# By default PeerTube client displays author username publish:
prefer_author_display_name: false download_enabled: true
menu: comments_enabled: true
login:
# If you enable only one external auth plugin # public = 1, unlisted = 2, private = 3, internal = 4
# You can automatically redirect your users on this external platform when they click on the login button privacy: 1
redirect_on_single_external_auth: false
# CC-BY = 1, CC-SA = 2, CC-ND = 3, CC-NC = 4, CC-NC-SA = 5, CC-NC-ND = 6, Public Domain = 7
# You can also choose a custom licence value added by a plugin
# No licence by default
licence: null
# From the project root directory # From the project root directory
storage: storage:
@ -597,3 +601,16 @@ search:
disable_local_search: false disable_local_search: false
# If you did not disable local search, you can decide to use the search index by default # If you did not disable local search, you can decide to use the search index by default
is_default_search: false is_default_search: false
# PeerTube client/interface configuration
client:
videos:
miniature:
# By default PeerTube client displays author username
prefer_author_display_name: false
menu:
login:
# 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

View File

@ -2,8 +2,4 @@
set -eu set -eu
npm run clean:server:test cd client/e2e && ../node_modules/.bin/wdio run ./wdio.browserstack.conf.ts
npm run concurrently -- -k -s first \
"cd client/e2e && ../node_modules/.bin/wdio run ./wdio.browserstack.conf.ts" \
"NODE_ENV=test NODE_APP_INSTANCE=1 NODE_CONFIG='{ \"rates_limit\": { \"api\": { \"max\": 5000 }, \"login\": { \"max\": 5000 } }, \"log\": { \"level\": \"warn\" }, \"signup\": { \"enabled\": false } }' node dist/server"

View File

@ -2,16 +2,6 @@
set -eu set -eu
npm run clean:server:test cd client/e2e
config="{" ../node_modules/.bin/wdio run ./wdio.local.conf.ts
config+=" \"rates_limit\": { \"api\": { \"max\": 5000 }, \"login\": { \"max\": 5000 } }"
config+=", \"log\": { \"level\": \"warn\" }"
config+=", \"signup\": { \"enabled\": false }"
config+=", \"transcoding\": { \"enabled\": false }"
config+="}"
npm run concurrently -- -k -s first \
"cd client/e2e && ../node_modules/.bin/wdio run ./wdio.local.conf.ts" \
"NODE_ENV=test NODE_CONFIG='$config' NODE_APP_INSTANCE=1 node dist/server" \
"NODE_ENV=test NODE_CONFIG='$config' NODE_APP_INSTANCE=2 node dist/server"

View File

@ -216,10 +216,10 @@ async function buildVideo (channelId: number, body: VideoImportCreate, importDat
name: body.name || importData.name || 'Unknown name', name: body.name || importData.name || 'Unknown name',
remote: false, remote: false,
category: body.category || importData.category, category: body.category || importData.category,
licence: body.licence || importData.licence, licence: body.licence ?? importData.licence ?? CONFIG.DEFAULTS.PUBLISH.LICENCE,
language: body.language || importData.language, language: body.language || importData.language,
commentsEnabled: body.commentsEnabled !== false, // If the value is not "false", the default is "true" commentsEnabled: body.commentsEnabled ?? CONFIG.DEFAULTS.PUBLISH.COMMENTS_ENABLED,
downloadEnabled: body.downloadEnabled !== false, downloadEnabled: body.downloadEnabled ?? CONFIG.DEFAULTS.PUBLISH.DOWNLOAD_ENABLED,
waitTranscoding: body.waitTranscoding || false, waitTranscoding: body.waitTranscoding || false,
state: VideoState.TO_IMPORT, state: VideoState.TO_IMPORT,
nsfw: body.nsfw || importData.nsfw || false, nsfw: body.nsfw || importData.nsfw || false,

View File

@ -34,6 +34,7 @@ function checkMissedConfig () {
'import.videos.http.enabled', 'import.videos.torrent.enabled', 'import.videos.concurrency', 'auto_blacklist.videos.of_users.enabled', 'import.videos.http.enabled', 'import.videos.torrent.enabled', 'import.videos.concurrency', 'auto_blacklist.videos.of_users.enabled',
'trending.videos.interval_days', 'trending.videos.interval_days',
'client.videos.miniature.prefer_author_display_name', 'client.menu.login.redirect_on_single_external_auth', 'client.videos.miniature.prefer_author_display_name', 'client.menu.login.redirect_on_single_external_auth',
'defaults.publish.download_enabled', 'defaults.publish.comments_enabled', 'defaults.publish.privacy', 'defaults.publish.licence',
'instance.name', 'instance.short_description', 'instance.description', 'instance.terms', 'instance.default_client_route', 'instance.name', 'instance.short_description', 'instance.description', 'instance.terms', 'instance.default_client_route',
'instance.is_nsfw', 'instance.default_nsfw_policy', 'instance.robots', 'instance.securitytxt', 'instance.is_nsfw', 'instance.default_nsfw_policy', 'instance.robots', 'instance.securitytxt',
'services.twitter.username', 'services.twitter.whitelisted', 'services.twitter.username', 'services.twitter.whitelisted',

View File

@ -4,7 +4,7 @@ import { dirname, join } from 'path'
import { decacheModule } from '@server/helpers/decache' import { decacheModule } from '@server/helpers/decache'
import { VideoRedundancyConfigFilter } from '@shared/models/redundancy/video-redundancy-config-filter.type' import { VideoRedundancyConfigFilter } from '@shared/models/redundancy/video-redundancy-config-filter.type'
import { BroadcastMessageLevel } from '@shared/models/server' import { BroadcastMessageLevel } from '@shared/models/server'
import { VideosRedundancyStrategy } from '../../shared/models' import { VideoPrivacy, VideosRedundancyStrategy } from '../../shared/models'
import { NSFWPolicyType } from '../../shared/models/videos/nsfw-policy.type' import { NSFWPolicyType } from '../../shared/models/videos/nsfw-policy.type'
import { buildPath, parseBytes, parseDurationToMs, root } from '../helpers/core-utils' import { buildPath, parseBytes, parseDurationToMs, root } from '../helpers/core-utils'
@ -71,6 +71,15 @@ const CONFIG = {
} }
}, },
DEFAULTS: {
PUBLISH: {
DOWNLOAD_ENABLED: config.get<boolean>('defaults.publish.download_enabled'),
COMMENTS_ENABLED: config.get<boolean>('defaults.publish.comments_enabled'),
PRIVACY: config.get<VideoPrivacy>('defaults.publish.privacy'),
LICENCE: config.get<number>('defaults.publish.licence')
}
},
STORAGE: { STORAGE: {
TMP_DIR: buildPath(config.get<string>('storage.tmp')), TMP_DIR: buildPath(config.get<string>('storage.tmp')),
BIN_DIR: buildPath(config.get<string>('storage.bin')), BIN_DIR: buildPath(config.get<string>('storage.bin')),

View File

@ -55,6 +55,15 @@ class ServerConfigManager {
} }
}, },
defaults: {
publish: {
downloadEnabled: CONFIG.DEFAULTS.PUBLISH.DOWNLOAD_ENABLED,
commentsEnabled: CONFIG.DEFAULTS.PUBLISH.COMMENTS_ENABLED,
privacy: CONFIG.DEFAULTS.PUBLISH.PRIVACY,
licence: CONFIG.DEFAULTS.PUBLISH.LICENCE
}
},
webadmin: { webadmin: {
configuration: { configuration: {
edition: { edition: {

View File

@ -9,16 +9,17 @@ import { MThumbnail, MUserId, MVideoFile, MVideoTag, MVideoThumbnail, MVideoUUID
import { ThumbnailType, VideoCreate, VideoPrivacy, VideoTranscodingPayload } from '@shared/models' import { ThumbnailType, VideoCreate, VideoPrivacy, VideoTranscodingPayload } from '@shared/models'
import { CreateJobOptions, JobQueue } from './job-queue/job-queue' import { CreateJobOptions, JobQueue } from './job-queue/job-queue'
import { updateVideoMiniatureFromExisting } from './thumbnail' import { updateVideoMiniatureFromExisting } from './thumbnail'
import { CONFIG } from '@server/initializers/config'
function buildLocalVideoFromReq (videoInfo: VideoCreate, channelId: number): FilteredModelAttributes<VideoModel> { function buildLocalVideoFromReq (videoInfo: VideoCreate, channelId: number): FilteredModelAttributes<VideoModel> {
return { return {
name: videoInfo.name, name: videoInfo.name,
remote: false, remote: false,
category: videoInfo.category, category: videoInfo.category,
licence: videoInfo.licence, licence: videoInfo.licence ?? CONFIG.DEFAULTS.PUBLISH.LICENCE,
language: videoInfo.language, language: videoInfo.language,
commentsEnabled: videoInfo.commentsEnabled !== false, // If the value is not "false", the default is "true" commentsEnabled: videoInfo.commentsEnabled ?? CONFIG.DEFAULTS.PUBLISH.COMMENTS_ENABLED,
downloadEnabled: videoInfo.downloadEnabled !== false, downloadEnabled: videoInfo.downloadEnabled ?? CONFIG.DEFAULTS.PUBLISH.DOWNLOAD_ENABLED,
waitTranscoding: videoInfo.waitTranscoding || false, waitTranscoding: videoInfo.waitTranscoding || false,
nsfw: videoInfo.nsfw || false, nsfw: videoInfo.nsfw || false,
description: videoInfo.description, description: videoInfo.description,

View File

@ -0,0 +1,116 @@
/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
import 'mocha'
import * as chai from 'chai'
import { cleanupTests, createSingleServer, FIXTURE_URLS, PeerTubeServer, setAccessTokensToServers, setDefaultVideoChannel } from '@shared/extra-utils'
import { VideoDetails, VideoPrivacy } from '@shared/models'
const expect = chai.expect
describe('Test config defaults', function () {
let server: PeerTubeServer
let channelId: number
before(async function () {
this.timeout(30000)
const overrideConfig = {
defaults: {
publish: {
comments_enabled: false,
download_enabled: false,
privacy: VideoPrivacy.INTERNAL,
licence: 4
}
}
}
server = await createSingleServer(1, overrideConfig)
await setAccessTokensToServers([ server ])
await setDefaultVideoChannel([ server ])
channelId = server.store.channel.id
})
describe('Default publish values', function () {
const attributes = {
name: 'video',
downloadEnabled: undefined,
commentsEnabled: undefined,
licence: undefined,
privacy: VideoPrivacy.PUBLIC // Privacy is mandatory for server
}
function checkVideo (video: VideoDetails) {
expect(video.downloadEnabled).to.be.false
expect(video.commentsEnabled).to.be.false
expect(video.licence.id).to.equal(4)
}
before(async function () {
await server.config.disableTranscoding()
await server.config.enableImports()
await server.config.enableLive({ allowReplay: false, transcoding: false })
})
it('Should have the correct server configuration', async function () {
const config = await server.config.getConfig()
expect(config.defaults.publish.commentsEnabled).to.be.false
expect(config.defaults.publish.downloadEnabled).to.be.false
expect(config.defaults.publish.licence).to.equal(4)
expect(config.defaults.publish.privacy).to.equal(VideoPrivacy.INTERNAL)
})
it('Should respect default values when uploading a video', async function () {
for (const mode of [ 'legacy' as 'legacy', 'resumable' as 'resumable' ]) {
const { id } = await server.videos.upload({ attributes, mode })
const video = await server.videos.get({ id })
checkVideo(video)
}
})
it('Should respect default values when importing a video using URL', async function () {
const { video: { id } } = await server.imports.importVideo({
attributes: {
...attributes,
channelId,
targetUrl: FIXTURE_URLS.goodVideo
}
})
const video = await server.videos.get({ id })
checkVideo(video)
})
it('Should respect default values when importing a video using magnet URI', async function () {
const { video: { id } } = await server.imports.importVideo({
attributes: {
...attributes,
channelId,
magnetUri: FIXTURE_URLS.magnet
}
})
const video = await server.videos.get({ id })
checkVideo(video)
})
it('Should respect default values when creating a live', async function () {
const { id } = await server.live.create({
fields: {
...attributes,
channelId
}
})
const video = await server.videos.get({ id })
checkVideo(video)
})
})
after(async function () {
await cleanupTests([ server ])
})
})

View File

@ -1,4 +1,6 @@
import './auto-follows' import './auto-follows'
import './bulk'
import './config-defaults'
import './config' import './config'
import './contact-form' import './contact-form'
import './email' import './email'

View File

@ -1,3 +1,4 @@
import { VideoPrivacy } from '../videos/video-privacy.enum'
import { ClientScript } from '../plugins/plugin-package-json.model' import { ClientScript } from '../plugins/plugin-package-json.model'
import { NSFWPolicyType } from '../videos/nsfw-policy.type' import { NSFWPolicyType } from '../videos/nsfw-policy.type'
import { BroadcastMessageLevel } from './broadcast-message-level.type' import { BroadcastMessageLevel } from './broadcast-message-level.type'
@ -47,6 +48,15 @@ export interface ServerConfig {
} }
} }
defaults: {
publish: {
downloadEnabled: boolean
commentsEnabled: boolean
privacy: VideoPrivacy
licence: number
}
}
webadmin: { webadmin: {
configuration: { configuration: {
edition: { edition: {

View File

@ -88,13 +88,7 @@ $ BROWSERSTACK_USER=your_user BROWSERSTACK_KEY=your_key npm run e2e:browserstack
### Add E2E tests ### Add E2E tests
To add E2E tests and quickly run tests using a local Chrome, first create a test instance: To add E2E tests and quickly run tests using a local Chrome:
```bash
$ npm run clean:server:test && NODE_APP_INSTANCE=1 NODE_ENV=test npm start
```
Then, just run your suite using:
```bash ```bash
$ cd client/e2e $ cd client/e2e