aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2021-12-14 17:17:01 +0100
committerChocobozzz <me@florianbigard.com>2021-12-14 17:17:01 +0100
commit3cf68b869decf07ff7435fe1436d4f3134df1bf4 (patch)
tree836efe5ddc626fef3ba4c96269efbca305f46256
parenta6f919e455f2c6ae8f2194da4aa66824a6bfd09e (diff)
downloadPeerTube-3cf68b869decf07ff7435fe1436d4f3134df1bf4.tar.gz
PeerTube-3cf68b869decf07ff7435fe1436d4f3134df1bf4.tar.zst
PeerTube-3cf68b869decf07ff7435fe1436d4f3134df1bf4.zip
Ability for admins to set default upload values
-rw-r--r--client/e2e/src/po/video-watch.po.ts29
-rw-r--r--client/e2e/src/suites-local/custom-server-defaults.e2e-spec.ts37
-rw-r--r--client/e2e/src/suites-local/videos-list.e2e-spec.ts24
-rw-r--r--client/e2e/src/utils/hooks.ts64
-rw-r--r--client/e2e/src/utils/index.ts2
-rw-r--r--client/e2e/src/utils/server.ts63
-rw-r--r--client/e2e/wdio.browserstack.conf.ts7
-rw-r--r--client/e2e/wdio.local-test.conf.ts9
-rw-r--r--client/e2e/wdio.local.conf.ts13
-rw-r--r--client/src/app/+videos/+video-edit/shared/video-edit.component.ts9
-rw-r--r--client/src/app/+videos/+video-edit/video-add-components/video-go-live.component.ts2
-rw-r--r--client/src/app/+videos/+video-edit/video-add-components/video-import-torrent.component.ts2
-rw-r--r--client/src/app/+videos/+video-edit/video-add-components/video-import-url.component.ts2
-rw-r--r--client/src/app/+videos/+video-edit/video-add-components/video-send.ts4
-rw-r--r--client/src/app/+videos/+video-edit/video-add-components/video-upload.component.ts2
-rw-r--r--client/src/app/+videos/+video-watch/shared/action-buttons/action-buttons.component.html2
-rw-r--r--client/src/app/+videos/+video-watch/shared/action-buttons/action-buttons.component.scss3
-rw-r--r--client/src/app/+videos/+video-watch/shared/metadata/video-attributes.component.html14
-rw-r--r--config/default.yaml39
-rw-r--r--config/production.yaml.example39
-rwxr-xr-xscripts/e2e/browserstack.sh6
-rwxr-xr-xscripts/e2e/local.sh14
-rw-r--r--server/controllers/api/videos/import.ts6
-rw-r--r--server/initializers/checker-before-init.ts1
-rw-r--r--server/initializers/config.ts11
-rw-r--r--server/lib/server-config-manager.ts9
-rw-r--r--server/lib/video.ts7
-rw-r--r--server/tests/api/server/config-defaults.ts116
-rw-r--r--server/tests/api/server/index.ts2
-rw-r--r--shared/models/server/server-config.model.ts10
-rw-r--r--support/doc/development/tests.md8
31 files changed, 467 insertions, 89 deletions
diff --git a/client/e2e/src/po/video-watch.po.ts b/client/e2e/src/po/video-watch.po.ts
index 41425f4d7..1406c971a 100644
--- a/client/e2e/src/po/video-watch.po.ts
+++ b/client/e2e/src/po/video-watch.po.ts
@@ -21,6 +21,24 @@ export class VideoWatchPage {
21 return this.getVideoNameElement().then(e => e.getText()) 21 return this.getVideoNameElement().then(e => e.getText())
22 } 22 }
23 23
24 getPrivacy () {
25 return $('.attribute-privacy .attribute-value').getText()
26 }
27
28 getLicence () {
29 return $('.attribute-licence .attribute-value').getText()
30 }
31
32 async isDownloadEnabled () {
33 await this.clickOnMoreDropdownIcon()
34
35 return $('.dropdown-item .icon-download').isExisting()
36 }
37
38 areCommentsEnabled () {
39 return $('my-video-comment-add').isExisting()
40 }
41
24 async goOnAssociatedEmbed () { 42 async goOnAssociatedEmbed () {
25 let url = await browser.getUrl() 43 let url = await browser.getUrl()
26 url = url.replace('/w/', '/videos/embed/') 44 url = url.replace('/w/', '/videos/embed/')
@@ -38,10 +56,8 @@ export class VideoWatchPage {
38 } 56 }
39 57
40 async clickOnUpdate () { 58 async clickOnUpdate () {
41 const dropdown = $('my-video-actions-dropdown .action-button') 59 await this.clickOnMoreDropdownIcon()
42 await dropdown.click()
43 60
44 await $('.dropdown-menu.show .dropdown-item').waitForDisplayed()
45 const items = await $$('.dropdown-menu.show .dropdown-item') 61 const items = await $$('.dropdown-menu.show .dropdown-item')
46 62
47 for (const item of items) { 63 for (const item of items) {
@@ -86,6 +102,13 @@ export class VideoWatchPage {
86 }, { timeout: maxTime }) 102 }, { timeout: maxTime })
87 } 103 }
88 104
105 async clickOnMoreDropdownIcon () {
106 const dropdown = $('my-video-actions-dropdown .action-button')
107 await dropdown.click()
108
109 await $('.dropdown-menu.show .dropdown-item').waitForDisplayed()
110 }
111
89 private async getVideoNameElement () { 112 private async getVideoNameElement () {
90 // We have 2 video info name block, pick the first that is not empty 113 // We have 2 video info name block, pick the first that is not empty
91 const elem = async () => { 114 const elem = async () => {
diff --git a/client/e2e/src/suites-local/custom-server-defaults.e2e-spec.ts b/client/e2e/src/suites-local/custom-server-defaults.e2e-spec.ts
new file mode 100644
index 000000000..c2c8edcc9
--- /dev/null
+++ b/client/e2e/src/suites-local/custom-server-defaults.e2e-spec.ts
@@ -0,0 +1,37 @@
1import { LoginPage } from '../po/login.po'
2import { VideoUploadPage } from '../po/video-upload.po'
3import { VideoWatchPage } from '../po/video-watch.po'
4import { isMobileDevice, isSafari, waitServerUp } from '../utils'
5
6describe('Custom server defaults', () => {
7 let videoUploadPage: VideoUploadPage
8 let loginPage: LoginPage
9 let videoWatchPage: VideoWatchPage
10
11 before(async () => {
12 await waitServerUp()
13 })
14
15 beforeEach(async () => {
16 loginPage = new LoginPage()
17 videoUploadPage = new VideoUploadPage()
18 videoWatchPage = new VideoWatchPage(isMobileDevice(), isSafari())
19
20 await browser.maximizeWindow()
21 })
22
23 it('Should upload a video with custom default values', async function () {
24 await loginPage.loginAsRootUser()
25 await videoUploadPage.navigateTo()
26 await videoUploadPage.uploadVideo()
27 await videoUploadPage.validSecondUploadStep('video')
28
29 await videoWatchPage.waitWatchVideoName('video')
30
31 expect(await videoWatchPage.getPrivacy()).toBe('Internal')
32 expect(await videoWatchPage.getLicence()).toBe('Attribution - Non Commercial')
33 expect(await videoWatchPage.isDownloadEnabled()).toBeFalsy()
34 expect(await videoWatchPage.areCommentsEnabled()).toBeFalsy()
35 })
36
37})
diff --git a/client/e2e/src/suites-local/videos-list.e2e-spec.ts b/client/e2e/src/suites-local/videos-list.e2e-spec.ts
index 1e0a88859..bca6018b9 100644
--- a/client/e2e/src/suites-local/videos-list.e2e-spec.ts
+++ b/client/e2e/src/suites-local/videos-list.e2e-spec.ts
@@ -4,6 +4,7 @@ import { MyAccountPage } from '../po/my-account'
4import { VideoListPage } from '../po/video-list.po' 4import { VideoListPage } from '../po/video-list.po'
5import { VideoSearchPage } from '../po/video-search.po' 5import { VideoSearchPage } from '../po/video-search.po'
6import { VideoUploadPage } from '../po/video-upload.po' 6import { VideoUploadPage } from '../po/video-upload.po'
7import { VideoWatchPage } from '../po/video-watch.po'
7import { NSFWPolicy } from '../types/common' 8import { NSFWPolicy } from '../types/common'
8import { isMobileDevice, isSafari, waitServerUp } from '../utils' 9import { isMobileDevice, isSafari, waitServerUp } from '../utils'
9 10
@@ -14,6 +15,7 @@ describe('Videos list', () => {
14 let loginPage: LoginPage 15 let loginPage: LoginPage
15 let myAccountPage: MyAccountPage 16 let myAccountPage: MyAccountPage
16 let videoSearchPage: VideoSearchPage 17 let videoSearchPage: VideoSearchPage
18 let videoWatchPage: VideoWatchPage
17 19
18 const seed = Math.random() 20 const seed = Math.random()
19 const nsfwVideo = seed + ' - nsfw' 21 const nsfwVideo = seed + ' - nsfw'
@@ -108,6 +110,7 @@ describe('Videos list', () => {
108 videoUploadPage = new VideoUploadPage() 110 videoUploadPage = new VideoUploadPage()
109 myAccountPage = new MyAccountPage() 111 myAccountPage = new MyAccountPage()
110 videoSearchPage = new VideoSearchPage() 112 videoSearchPage = new VideoSearchPage()
113 videoWatchPage = new VideoWatchPage(isMobileDevice(), isSafari())
111 114
112 await browser.maximizeWindow() 115 await browser.maximizeWindow()
113 }) 116 })
@@ -191,5 +194,26 @@ describe('Videos list', () => {
191 await checkCommonVideoListPages('display') 194 await checkCommonVideoListPages('display')
192 await checkSearchPage('display') 195 await checkSearchPage('display')
193 }) 196 })
197
198 after(async () => {
199 await loginPage.logout()
200 })
201 })
202
203 describe('Default upload values', function () {
204
205 it('Should have default video values', async function () {
206 await loginPage.loginAsRootUser()
207 await videoUploadPage.navigateTo()
208 await videoUploadPage.uploadVideo()
209 await videoUploadPage.validSecondUploadStep('video')
210
211 await videoWatchPage.waitWatchVideoName('video')
212
213 expect(await videoWatchPage.getPrivacy()).toBe('Public')
214 expect(await videoWatchPage.getLicence()).toBe('Unknown')
215 expect(await videoWatchPage.isDownloadEnabled()).toBeTruthy()
216 expect(await videoWatchPage.areCommentsEnabled()).toBeTruthy()
217 })
194 }) 218 })
195}) 219})
diff --git a/client/e2e/src/utils/hooks.ts b/client/e2e/src/utils/hooks.ts
new file mode 100644
index 000000000..e42c6a5d8
--- /dev/null
+++ b/client/e2e/src/utils/hooks.ts
@@ -0,0 +1,64 @@
1import { ChildProcessWithoutNullStreams } from 'child_process'
2import { basename } from 'path'
3import { runCommand, runServer } from './server'
4
5let appInstance: string
6let app: ChildProcessWithoutNullStreams
7
8async function beforeLocalSuite (suite: any) {
9 const config = buildConfig(suite.file)
10
11 await runCommand('npm run clean:server:test -- ' + appInstance)
12 app = runServer(appInstance, config)
13}
14
15function afterLocalSuite () {
16 app.kill()
17 app = undefined
18}
19
20function beforeLocalSession (config: { baseUrl: string }, capabilities: { browserName: string }) {
21 appInstance = capabilities['browserName'] === 'chrome' ? '1' : '2'
22 config.baseUrl = 'http://localhost:900' + appInstance
23}
24
25async function onBrowserStackPrepare () {
26 const appInstance = '1'
27
28 await runCommand('npm run clean:server:test -- ' + appInstance)
29 app = runServer(appInstance)
30}
31
32function onBrowserStackComplete () {
33 app.kill()
34 app = undefined
35}
36
37export {
38 beforeLocalSession,
39 afterLocalSuite,
40 beforeLocalSuite,
41 onBrowserStackPrepare,
42 onBrowserStackComplete
43}
44
45// ---------------------------------------------------------------------------
46
47function buildConfig (suiteFile: string = undefined) {
48 const filename = basename(suiteFile)
49
50 if (filename === 'custom-server-defaults.e2e-spec.ts') {
51 return {
52 defaults: {
53 publish: {
54 download_enabled: false,
55 comments_enabled: false,
56 privacy: 4,
57 licence: 4
58 }
59 }
60 }
61 }
62
63 return {}
64}
diff --git a/client/e2e/src/utils/index.ts b/client/e2e/src/utils/index.ts
index 5da1ad517..354352ee2 100644
--- a/client/e2e/src/utils/index.ts
+++ b/client/e2e/src/utils/index.ts
@@ -1,3 +1,5 @@
1export * from './common' 1export * from './common'
2export * from './elements' 2export * from './elements'
3export * from './hooks'
4export * from './server'
3export * from './urls' 5export * from './urls'
diff --git a/client/e2e/src/utils/server.ts b/client/e2e/src/utils/server.ts
new file mode 100644
index 000000000..7089a5c9c
--- /dev/null
+++ b/client/e2e/src/utils/server.ts
@@ -0,0 +1,63 @@
1import { exec, spawn } from 'child_process'
2import { join, resolve } from 'path'
3
4function runServer (appInstance: string, config: any = {}) {
5 const env = Object.create(process.env)
6 env['NODE_ENV'] = 'test'
7 env['NODE_APP_INSTANCE'] = appInstance
8
9 env['NODE_CONFIG'] = JSON.stringify({
10 rates_limit: {
11 api: {
12 max: 5000
13 },
14 login: {
15 max: 5000
16 }
17 },
18 log: {
19 level: 'warn'
20 },
21 signup: {
22 enabled: false
23 },
24 transcoding: {
25 enabled: false
26 },
27
28 ...config
29 })
30
31 const forkOptions = {
32 env,
33 cwd: getRootCWD(),
34 detached: false
35 }
36
37 const p = spawn('node', [ join('dist', 'server.js') ], forkOptions)
38 p.stderr.on('data', data => console.error(data.toString()))
39 p.stdout.on('data', data => console.error(data.toString()))
40
41 return p
42}
43
44function runCommand (command: string) {
45 return new Promise<void>((res, rej) => {
46 const p = exec(command, { cwd: getRootCWD() })
47
48 p.stderr.on('data', data => console.error(data.toString()))
49 p.on('error', err => rej(err))
50 p.on('exit', () => res())
51 })
52}
53
54export {
55 runServer,
56 runCommand
57}
58
59// ---------------------------------------------------------------------------
60
61function getRootCWD () {
62 return resolve('../..')
63}
diff --git a/client/e2e/wdio.browserstack.conf.ts b/client/e2e/wdio.browserstack.conf.ts
index 43614a862..b89cdbc2e 100644
--- a/client/e2e/wdio.browserstack.conf.ts
+++ b/client/e2e/wdio.browserstack.conf.ts
@@ -1,3 +1,4 @@
1import { onBrowserStackComplete, onBrowserStackPrepare } from './src/utils'
1import { config as mainConfig } from './wdio.main.conf' 2import { config as mainConfig } from './wdio.main.conf'
2 3
3const user = process.env.BROWSERSTACK_USER 4const user = process.env.BROWSERSTACK_USER
@@ -114,6 +115,10 @@ module.exports = {
114 if (capabilities['bstack:options'].realMobile === true) { 115 if (capabilities['bstack:options'].realMobile === true) {
115 capabilities['bstack:options'].local = false 116 capabilities['bstack:options'].local = false
116 } 117 }
117 } 118 },
119
120 onPrepare: onBrowserStackPrepare,
121 onComplete: onBrowserStackComplete
122
118 } as WebdriverIO.Config 123 } as WebdriverIO.Config
119} 124}
diff --git a/client/e2e/wdio.local-test.conf.ts b/client/e2e/wdio.local-test.conf.ts
index 32e6d340c..5389ebcf0 100644
--- a/client/e2e/wdio.local-test.conf.ts
+++ b/client/e2e/wdio.local-test.conf.ts
@@ -1,3 +1,4 @@
1import { afterLocalSuite, beforeLocalSuite, beforeLocalSession } from './src/utils'
1import { config as mainConfig } from './wdio.main.conf' 2import { config as mainConfig } from './wdio.main.conf'
2 3
3const prefs = { 4const prefs = {
@@ -21,12 +22,16 @@ module.exports = {
21 browserName: 'chrome', 22 browserName: 'chrome',
22 acceptInsecureCerts: true, 23 acceptInsecureCerts: true,
23 'goog:chromeOptions': { 24 'goog:chromeOptions': {
24 args: [ '--headless', '--disable-gpu', '--window-size=1280,1024' ], 25 args: [ '--disable-gpu', '--window-size=1280,1024' ],
25 prefs 26 prefs
26 } 27 }
27 } 28 }
28 ], 29 ],
29 30
30 services: [ 'chromedriver' ] 31 services: [ 'chromedriver' ],
32
33 beforeSession: beforeLocalSession,
34 beforeSuite: beforeLocalSuite,
35 afterSuite: afterLocalSuite
31 } as WebdriverIO.Config 36 } as WebdriverIO.Config
32} 37}
diff --git a/client/e2e/wdio.local.conf.ts b/client/e2e/wdio.local.conf.ts
index 43b820ca6..d02679e06 100644
--- a/client/e2e/wdio.local.conf.ts
+++ b/client/e2e/wdio.local.conf.ts
@@ -1,3 +1,4 @@
1import { afterLocalSuite, beforeLocalSession, beforeLocalSuite } from './src/utils'
1import { config as mainConfig } from './wdio.main.conf' 2import { config as mainConfig } from './wdio.main.conf'
2 3
3const prefs = { 4const prefs = {
@@ -11,7 +12,7 @@ module.exports = {
11 12
12 runner: 'local', 13 runner: 'local',
13 14
14 maxInstances: 2, 15 maxInstancesPerCapability: 1,
15 16
16 capabilities: [ 17 capabilities: [
17 { 18 {
@@ -34,12 +35,8 @@ module.exports = {
34 35
35 services: [ 'chromedriver', 'geckodriver' ], 36 services: [ 'chromedriver', 'geckodriver' ],
36 37
37 beforeSession: function (config, capabilities) { 38 beforeSession: beforeLocalSession,
38 if (capabilities['browserName'] === 'chrome') { 39 beforeSuite: beforeLocalSuite,
39 config.baseUrl = 'http://localhost:9001' 40 afterSuite: afterLocalSuite
40 } else {
41 config.baseUrl = 'http://localhost:9002'
42 }
43 }
44 } as WebdriverIO.Config 41 } as WebdriverIO.Config
45} 42}
diff --git a/client/src/app/+videos/+video-edit/shared/video-edit.component.ts b/client/src/app/+videos/+video-edit/shared/video-edit.component.ts
index 39767f258..2bec933e9 100644
--- a/client/src/app/+videos/+video-edit/shared/video-edit.component.ts
+++ b/client/src/app/+videos/+video-edit/shared/video-edit.component.ts
@@ -110,9 +110,10 @@ export class VideoEditComponent implements OnInit, OnDestroy {
110 updateForm () { 110 updateForm () {
111 const defaultValues: any = { 111 const defaultValues: any = {
112 nsfw: 'false', 112 nsfw: 'false',
113 commentsEnabled: 'true', 113 commentsEnabled: this.serverConfig.defaults.publish.commentsEnabled,
114 downloadEnabled: 'true', 114 downloadEnabled: this.serverConfig.defaults.publish.downloadEnabled,
115 waitTranscoding: 'true', 115 waitTranscoding: 'true',
116 licence: this.serverConfig.defaults.publish.licence,
116 tags: [] 117 tags: []
117 } 118 }
118 const obj: any = { 119 const obj: any = {
@@ -160,6 +161,8 @@ export class VideoEditComponent implements OnInit, OnDestroy {
160 } 161 }
161 162
162 ngOnInit () { 163 ngOnInit () {
164 this.serverConfig = this.serverService.getHTMLConfig()
165
163 this.updateForm() 166 this.updateForm()
164 167
165 this.pluginService.ensurePluginsAreLoaded('video-edit') 168 this.pluginService.ensurePluginsAreLoaded('video-edit')
@@ -200,8 +203,6 @@ export class VideoEditComponent implements OnInit, OnDestroy {
200 } 203 }
201 }) 204 })
202 205
203 this.serverConfig = this.serverService.getHTMLConfig()
204
205 this.initialVideoCaptions = this.videoCaptions.map(c => c.language.id) 206 this.initialVideoCaptions = this.videoCaptions.map(c => c.language.id)
206 207
207 this.ngZone.runOutsideAngular(() => { 208 this.ngZone.runOutsideAngular(() => {
diff --git a/client/src/app/+videos/+video-edit/video-add-components/video-go-live.component.ts b/client/src/app/+videos/+video-edit/video-add-components/video-go-live.component.ts
index 01eabb0d7..46a7ebb0b 100644
--- a/client/src/app/+videos/+video-edit/video-add-components/video-go-live.component.ts
+++ b/client/src/app/+videos/+video-edit/video-add-components/video-go-live.component.ts
@@ -70,8 +70,6 @@ export class VideoGoLiveComponent extends VideoSend implements OnInit, AfterView
70 privacy: this.highestPrivacy, 70 privacy: this.highestPrivacy,
71 nsfw: this.serverConfig.instance.isNSFW, 71 nsfw: this.serverConfig.instance.isNSFW,
72 waitTranscoding: true, 72 waitTranscoding: true,
73 commentsEnabled: true,
74 downloadEnabled: true,
75 permanentLive: this.firstStepPermanentLive, 73 permanentLive: this.firstStepPermanentLive,
76 saveReplay: this.firstStepPermanentLive === false && this.isReplayAllowed(), 74 saveReplay: this.firstStepPermanentLive === false && this.isReplayAllowed(),
77 channelId: this.firstStepChannelId 75 channelId: this.firstStepChannelId
diff --git a/client/src/app/+videos/+video-edit/video-add-components/video-import-torrent.component.ts b/client/src/app/+videos/+video-edit/video-add-components/video-import-torrent.component.ts
index 87e47683f..5e758910e 100644
--- a/client/src/app/+videos/+video-edit/video-add-components/video-import-torrent.component.ts
+++ b/client/src/app/+videos/+video-edit/video-add-components/video-import-torrent.component.ts
@@ -81,8 +81,6 @@ export class VideoImportTorrentComponent extends VideoSend implements OnInit, Af
81 const videoUpdate: VideoUpdate = { 81 const videoUpdate: VideoUpdate = {
82 privacy: this.highestPrivacy, 82 privacy: this.highestPrivacy,
83 waitTranscoding: false, 83 waitTranscoding: false,
84 commentsEnabled: true,
85 downloadEnabled: true,
86 channelId: this.firstStepChannelId 84 channelId: this.firstStepChannelId
87 } 85 }
88 86
diff --git a/client/src/app/+videos/+video-edit/video-add-components/video-import-url.component.ts b/client/src/app/+videos/+video-edit/video-add-components/video-import-url.component.ts
index 3487c1adf..2ea70ed55 100644
--- a/client/src/app/+videos/+video-edit/video-add-components/video-import-url.component.ts
+++ b/client/src/app/+videos/+video-edit/video-add-components/video-import-url.component.ts
@@ -68,8 +68,6 @@ export class VideoImportUrlComponent extends VideoSend implements OnInit, AfterV
68 const videoUpdate: VideoUpdate = { 68 const videoUpdate: VideoUpdate = {
69 privacy: this.highestPrivacy, 69 privacy: this.highestPrivacy,
70 waitTranscoding: false, 70 waitTranscoding: false,
71 commentsEnabled: true,
72 downloadEnabled: true,
73 channelId: this.firstStepChannelId 71 channelId: this.firstStepChannelId
74 } 72 }
75 73
diff --git a/client/src/app/+videos/+video-edit/video-add-components/video-send.ts b/client/src/app/+videos/+video-edit/video-add-components/video-send.ts
index efa8c85a3..5e086ef42 100644
--- a/client/src/app/+videos/+video-edit/video-add-components/video-send.ts
+++ b/client/src/app/+videos/+video-edit/video-add-components/video-send.ts
@@ -49,7 +49,9 @@ export abstract class VideoSend extends FormReactive implements OnInit {
49 this.serverService.getVideoPrivacies() 49 this.serverService.getVideoPrivacies()
50 .subscribe( 50 .subscribe(
51 privacies => { 51 privacies => {
52 const { videoPrivacies, defaultPrivacyId } = this.videoService.explainedPrivacyLabels(privacies) 52 const defaultPrivacy = this.serverConfig.defaults.publish.privacy
53
54 const { videoPrivacies, defaultPrivacyId } = this.videoService.explainedPrivacyLabels(privacies, defaultPrivacy)
53 55
54 this.videoPrivacies = videoPrivacies 56 this.videoPrivacies = videoPrivacies
55 this.firstStepPrivacyId = defaultPrivacyId 57 this.firstStepPrivacyId = defaultPrivacyId
diff --git a/client/src/app/+videos/+video-edit/video-add-components/video-upload.component.ts b/client/src/app/+videos/+video-edit/video-add-components/video-upload.component.ts
index 28d7ec458..76f154249 100644
--- a/client/src/app/+videos/+video-edit/video-add-components/video-upload.component.ts
+++ b/client/src/app/+videos/+video-edit/video-add-components/video-upload.component.ts
@@ -277,8 +277,6 @@ export class VideoUploadComponent extends VideoSend implements OnInit, OnDestroy
277 private uploadFile (file: File, previewfile?: File) { 277 private uploadFile (file: File, previewfile?: File) {
278 const metadata = { 278 const metadata = {
279 waitTranscoding: true, 279 waitTranscoding: true,
280 commentsEnabled: true,
281 downloadEnabled: true,
282 channelId: this.firstStepChannelId, 280 channelId: this.firstStepChannelId,
283 nsfw: this.serverConfig.instance.isNSFW, 281 nsfw: this.serverConfig.instance.isNSFW,
284 privacy: this.highestPrivacy.toString(), 282 privacy: this.highestPrivacy.toString(),
diff --git a/client/src/app/+videos/+video-watch/shared/action-buttons/action-buttons.component.html b/client/src/app/+videos/+video-watch/shared/action-buttons/action-buttons.component.html
index 8fb244cc4..f23efca98 100644
--- a/client/src/app/+videos/+video-watch/shared/action-buttons/action-buttons.component.html
+++ b/client/src/app/+videos/+video-watch/shared/action-buttons/action-buttons.component.html
@@ -36,7 +36,7 @@
36 36
37 <ng-container *ngIf="!isUserLoggedIn && !video.isLive"> 37 <ng-container *ngIf="!isUserLoggedIn && !video.isLive">
38 <button 38 <button
39 *ngIf="isVideoDownloadable()" class="action-button action-button-save" 39 *ngIf="isVideoDownloadable()" class="action-button action-button-download"
40 (click)="showDownloadModal()" (keydown.enter)="showDownloadModal()" 40 (click)="showDownloadModal()" (keydown.enter)="showDownloadModal()"
41 > 41 >
42 <my-global-icon iconName="download" aria-hidden="true"></my-global-icon> 42 <my-global-icon iconName="download" aria-hidden="true"></my-global-icon>
diff --git a/client/src/app/+videos/+video-watch/shared/action-buttons/action-buttons.component.scss b/client/src/app/+videos/+video-watch/shared/action-buttons/action-buttons.component.scss
index 967d515e6..fdf4e3edb 100644
--- a/client/src/app/+videos/+video-watch/shared/action-buttons/action-buttons.component.scss
+++ b/client/src/app/+videos/+video-watch/shared/action-buttons/action-buttons.component.scss
@@ -49,7 +49,8 @@
49 } 49 }
50 } 50 }
51 51
52 &.action-button-save { 52 &.action-button-save,
53 &.action-button-download {
53 my-global-icon { 54 my-global-icon {
54 top: 0 !important; 55 top: 0 !important;
55 right: -1px; 56 right: -1px;
diff --git a/client/src/app/+videos/+video-watch/shared/metadata/video-attributes.component.html b/client/src/app/+videos/+video-watch/shared/metadata/video-attributes.component.html
index d65c9356a..10ff46595 100644
--- a/client/src/app/+videos/+video-watch/shared/metadata/video-attributes.component.html
+++ b/client/src/app/+videos/+video-watch/shared/metadata/video-attributes.component.html
@@ -1,9 +1,9 @@
1<div class="attribute"> 1<div class="attribute attribute-privacy">
2 <span i18n class="attribute-label">Privacy</span> 2 <span i18n class="attribute-label">Privacy</span>
3 <span class="attribute-value">{{ video.privacy.label }}</span> 3 <span class="attribute-value">{{ video.privacy.label }}</span>
4</div> 4</div>
5 5
6<div *ngIf="video.isLocal === false" class="attribute"> 6<div *ngIf="video.isLocal === false" class="attribute attribute-origin">
7 <span i18n class="attribute-label">Origin</span> 7 <span i18n class="attribute-label">Origin</span>
8 <a 8 <a
9 class="attribute-value" target="_blank" rel="noopener noreferrer" 9 class="attribute-value" target="_blank" rel="noopener noreferrer"
@@ -16,12 +16,12 @@
16 ></a> 16 ></a>
17</div> 17</div>
18 18
19<div *ngIf="!!video.originallyPublishedAt" class="attribute"> 19<div *ngIf="!!video.originallyPublishedAt" class="attribute attribute-originally-published-at">
20 <span i18n class="attribute-label">Originally published</span> 20 <span i18n class="attribute-label">Originally published</span>
21 <span class="attribute-value">{{ video.originallyPublishedAt | date: 'dd MMMM yyyy' }}</span> 21 <span class="attribute-value">{{ video.originallyPublishedAt | date: 'dd MMMM yyyy' }}</span>
22</div> 22</div>
23 23
24<div class="attribute"> 24<div class="attribute attribute-category">
25 <span i18n class="attribute-label">Category</span> 25 <span i18n class="attribute-label">Category</span>
26 <span *ngIf="!video.category.id" class="attribute-value">{{ video.category.label }}</span> 26 <span *ngIf="!video.category.id" class="attribute-value">{{ video.category.label }}</span>
27 <a 27 <a
@@ -30,7 +30,7 @@
30 >{{ video.category.label }}</a> 30 >{{ video.category.label }}</a>
31</div> 31</div>
32 32
33<div class="attribute"> 33<div class="attribute attribute-licence">
34 <span i18n class="attribute-label">Licence</span> 34 <span i18n class="attribute-label">Licence</span>
35 <span *ngIf="!video.licence.id" class="attribute-value">{{ video.licence.label }}</span> 35 <span *ngIf="!video.licence.id" class="attribute-value">{{ video.licence.label }}</span>
36 <a 36 <a
@@ -39,7 +39,7 @@
39 >{{ video.licence.label }}</a> 39 >{{ video.licence.label }}</a>
40</div> 40</div>
41 41
42<div class="attribute"> 42<div class="attribute attribute-language">
43 <span i18n class="attribute-label">Language</span> 43 <span i18n class="attribute-label">Language</span>
44 <span *ngIf="!video.language.id" class="attribute-value">{{ video.language.label }}</span> 44 <span *ngIf="!video.language.id" class="attribute-value">{{ video.language.label }}</span>
45 <a 45 <a
@@ -56,7 +56,7 @@
56 >{{ tag }}</a> 56 >{{ tag }}</a>
57</div> 57</div>
58 58
59<div class="attribute" *ngIf="!video.isLive"> 59<div class="attribute attribute-duration" *ngIf="!video.isLive">
60 <span i18n class="attribute-label">Duration</span> 60 <span i18n class="attribute-label">Duration</span>
61 <span class="attribute-value">{{ video.duration | myDurationFormatter }}</span> 61 <span class="attribute-value">{{ video.duration | myDurationFormatter }}</span>
62</div> 62</div>
diff --git a/config/default.yaml b/config/default.yaml
index 074951117..fbe0dbbfb 100644
--- a/config/default.yaml
+++ b/config/default.yaml
@@ -75,18 +75,22 @@ email:
75 subject: 75 subject:
76 prefix: '[PeerTube]' 76 prefix: '[PeerTube]'
77 77
78# PeerTube client/interface configuration 78# Update default PeerTube values
79client: 79# Set by API when the field is not provided and put as default value in client
80 videos: 80defaults:
81 miniature: 81 # Change default values when publishing a video (upload/import/go Live)
82 # By default PeerTube client displays author username 82 publish:
83 prefer_author_display_name: false 83 download_enabled: true
84 84
85 menu: 85 comments_enabled: true
86 login: 86
87 # If you enable only one external auth plugin 87 # public = 1, unlisted = 2, private = 3, internal = 4
88 # You can automatically redirect your users on this external platform when they click on the login button 88 privacy: 1
89 redirect_on_single_external_auth: false 89
90 # CC-BY = 1, CC-SA = 2, CC-ND = 3, CC-NC = 4, CC-NC-SA = 5, CC-NC-ND = 6, Public Domain = 7
91 # You can also choose a custom licence value added by a plugin
92 # No licence by default
93 licence: null
90 94
91# From the project root directory 95# From the project root directory
92storage: 96storage:
@@ -587,3 +591,16 @@ search:
587 disable_local_search: false 591 disable_local_search: false
588 # If you did not disable local search, you can decide to use the search index by default 592 # If you did not disable local search, you can decide to use the search index by default
589 is_default_search: false 593 is_default_search: false
594
595# PeerTube client/interface configuration
596client:
597 videos:
598 miniature:
599 # By default PeerTube client displays author username
600 prefer_author_display_name: false
601
602 menu:
603 login:
604 # If you enable only one external auth plugin
605 # You can automatically redirect your users on this external platform when they click on the login button
606 redirect_on_single_external_auth: false
diff --git a/config/production.yaml.example b/config/production.yaml.example
index e38b79587..6363a5179 100644
--- a/config/production.yaml.example
+++ b/config/production.yaml.example
@@ -73,18 +73,22 @@ email:
73 subject: 73 subject:
74 prefix: '[PeerTube]' 74 prefix: '[PeerTube]'
75 75
76# PeerTube client/interface configuration 76# Update default PeerTube values
77client: 77# Set by API when the field is not provided and put as default value in client
78 videos: 78defaults:
79 miniature: 79 # Change default values when publishing a video (upload/import/go Live)
80 # By default PeerTube client displays author username 80 publish:
81 prefer_author_display_name: false 81 download_enabled: true
82 82
83 menu: 83 comments_enabled: true
84 login: 84
85 # If you enable only one external auth plugin 85 # public = 1, unlisted = 2, private = 3, internal = 4
86 # You can automatically redirect your users on this external platform when they click on the login button 86 privacy: 1
87 redirect_on_single_external_auth: false 87
88 # CC-BY = 1, CC-SA = 2, CC-ND = 3, CC-NC = 4, CC-NC-SA = 5, CC-NC-ND = 6, Public Domain = 7
89 # You can also choose a custom licence value added by a plugin
90 # No licence by default
91 licence: null
88 92
89# From the project root directory 93# From the project root directory
90storage: 94storage:
@@ -597,3 +601,16 @@ search:
597 disable_local_search: false 601 disable_local_search: false
598 # If you did not disable local search, you can decide to use the search index by default 602 # If you did not disable local search, you can decide to use the search index by default
599 is_default_search: false 603 is_default_search: false
604
605# PeerTube client/interface configuration
606client:
607 videos:
608 miniature:
609 # By default PeerTube client displays author username
610 prefer_author_display_name: false
611
612 menu:
613 login:
614 # If you enable only one external auth plugin
615 # You can automatically redirect your users on this external platform when they click on the login button
616 redirect_on_single_external_auth: false
diff --git a/scripts/e2e/browserstack.sh b/scripts/e2e/browserstack.sh
index fb125ea23..ad6268d42 100755
--- a/scripts/e2e/browserstack.sh
+++ b/scripts/e2e/browserstack.sh
@@ -2,8 +2,4 @@
2 2
3set -eu 3set -eu
4 4
5npm run clean:server:test 5cd client/e2e && ../node_modules/.bin/wdio run ./wdio.browserstack.conf.ts
6
7npm run concurrently -- -k -s first \
8 "cd client/e2e && ../node_modules/.bin/wdio run ./wdio.browserstack.conf.ts" \
9 "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"
diff --git a/scripts/e2e/local.sh b/scripts/e2e/local.sh
index fe8b7f559..0e0368c52 100755
--- a/scripts/e2e/local.sh
+++ b/scripts/e2e/local.sh
@@ -2,16 +2,6 @@
2 2
3set -eu 3set -eu
4 4
5npm run clean:server:test 5cd client/e2e
6 6
7config="{" 7../node_modules/.bin/wdio run ./wdio.local.conf.ts
8config+=" \"rates_limit\": { \"api\": { \"max\": 5000 }, \"login\": { \"max\": 5000 } }"
9config+=", \"log\": { \"level\": \"warn\" }"
10config+=", \"signup\": { \"enabled\": false }"
11config+=", \"transcoding\": { \"enabled\": false }"
12config+="}"
13
14npm run concurrently -- -k -s first \
15 "cd client/e2e && ../node_modules/.bin/wdio run ./wdio.local.conf.ts" \
16 "NODE_ENV=test NODE_CONFIG='$config' NODE_APP_INSTANCE=1 node dist/server" \
17 "NODE_ENV=test NODE_CONFIG='$config' NODE_APP_INSTANCE=2 node dist/server"
diff --git a/server/controllers/api/videos/import.ts b/server/controllers/api/videos/import.ts
index 52864bdfd..3c445efce 100644
--- a/server/controllers/api/videos/import.ts
+++ b/server/controllers/api/videos/import.ts
@@ -216,10 +216,10 @@ async function buildVideo (channelId: number, body: VideoImportCreate, importDat
216 name: body.name || importData.name || 'Unknown name', 216 name: body.name || importData.name || 'Unknown name',
217 remote: false, 217 remote: false,
218 category: body.category || importData.category, 218 category: body.category || importData.category,
219 licence: body.licence || importData.licence, 219 licence: body.licence ?? importData.licence ?? CONFIG.DEFAULTS.PUBLISH.LICENCE,
220 language: body.language || importData.language, 220 language: body.language || importData.language,
221 commentsEnabled: body.commentsEnabled !== false, // If the value is not "false", the default is "true" 221 commentsEnabled: body.commentsEnabled ?? CONFIG.DEFAULTS.PUBLISH.COMMENTS_ENABLED,
222 downloadEnabled: body.downloadEnabled !== false, 222 downloadEnabled: body.downloadEnabled ?? CONFIG.DEFAULTS.PUBLISH.DOWNLOAD_ENABLED,
223 waitTranscoding: body.waitTranscoding || false, 223 waitTranscoding: body.waitTranscoding || false,
224 state: VideoState.TO_IMPORT, 224 state: VideoState.TO_IMPORT,
225 nsfw: body.nsfw || importData.nsfw || false, 225 nsfw: body.nsfw || importData.nsfw || false,
diff --git a/server/initializers/checker-before-init.ts b/server/initializers/checker-before-init.ts
index c85c389cd..2c24e20c8 100644
--- a/server/initializers/checker-before-init.ts
+++ b/server/initializers/checker-before-init.ts
@@ -34,6 +34,7 @@ function checkMissedConfig () {
34 'import.videos.http.enabled', 'import.videos.torrent.enabled', 'import.videos.concurrency', 'auto_blacklist.videos.of_users.enabled', 34 'import.videos.http.enabled', 'import.videos.torrent.enabled', 'import.videos.concurrency', 'auto_blacklist.videos.of_users.enabled',
35 'trending.videos.interval_days', 35 'trending.videos.interval_days',
36 'client.videos.miniature.prefer_author_display_name', 'client.menu.login.redirect_on_single_external_auth', 36 'client.videos.miniature.prefer_author_display_name', 'client.menu.login.redirect_on_single_external_auth',
37 'defaults.publish.download_enabled', 'defaults.publish.comments_enabled', 'defaults.publish.privacy', 'defaults.publish.licence',
37 'instance.name', 'instance.short_description', 'instance.description', 'instance.terms', 'instance.default_client_route', 38 'instance.name', 'instance.short_description', 'instance.description', 'instance.terms', 'instance.default_client_route',
38 'instance.is_nsfw', 'instance.default_nsfw_policy', 'instance.robots', 'instance.securitytxt', 39 'instance.is_nsfw', 'instance.default_nsfw_policy', 'instance.robots', 'instance.securitytxt',
39 'services.twitter.username', 'services.twitter.whitelisted', 40 'services.twitter.username', 'services.twitter.whitelisted',
diff --git a/server/initializers/config.ts b/server/initializers/config.ts
index eb848be6b..70179d25c 100644
--- a/server/initializers/config.ts
+++ b/server/initializers/config.ts
@@ -4,7 +4,7 @@ import { dirname, join } from 'path'
4import { decacheModule } from '@server/helpers/decache' 4import { decacheModule } from '@server/helpers/decache'
5import { VideoRedundancyConfigFilter } from '@shared/models/redundancy/video-redundancy-config-filter.type' 5import { VideoRedundancyConfigFilter } from '@shared/models/redundancy/video-redundancy-config-filter.type'
6import { BroadcastMessageLevel } from '@shared/models/server' 6import { BroadcastMessageLevel } from '@shared/models/server'
7import { VideosRedundancyStrategy } from '../../shared/models' 7import { VideoPrivacy, VideosRedundancyStrategy } from '../../shared/models'
8import { NSFWPolicyType } from '../../shared/models/videos/nsfw-policy.type' 8import { NSFWPolicyType } from '../../shared/models/videos/nsfw-policy.type'
9import { buildPath, parseBytes, parseDurationToMs, root } from '../helpers/core-utils' 9import { buildPath, parseBytes, parseDurationToMs, root } from '../helpers/core-utils'
10 10
@@ -71,6 +71,15 @@ const CONFIG = {
71 } 71 }
72 }, 72 },
73 73
74 DEFAULTS: {
75 PUBLISH: {
76 DOWNLOAD_ENABLED: config.get<boolean>('defaults.publish.download_enabled'),
77 COMMENTS_ENABLED: config.get<boolean>('defaults.publish.comments_enabled'),
78 PRIVACY: config.get<VideoPrivacy>('defaults.publish.privacy'),
79 LICENCE: config.get<number>('defaults.publish.licence')
80 }
81 },
82
74 STORAGE: { 83 STORAGE: {
75 TMP_DIR: buildPath(config.get<string>('storage.tmp')), 84 TMP_DIR: buildPath(config.get<string>('storage.tmp')),
76 BIN_DIR: buildPath(config.get<string>('storage.bin')), 85 BIN_DIR: buildPath(config.get<string>('storage.bin')),
diff --git a/server/lib/server-config-manager.ts b/server/lib/server-config-manager.ts
index 6aa459f82..8aea4cd6d 100644
--- a/server/lib/server-config-manager.ts
+++ b/server/lib/server-config-manager.ts
@@ -55,6 +55,15 @@ class ServerConfigManager {
55 } 55 }
56 }, 56 },
57 57
58 defaults: {
59 publish: {
60 downloadEnabled: CONFIG.DEFAULTS.PUBLISH.DOWNLOAD_ENABLED,
61 commentsEnabled: CONFIG.DEFAULTS.PUBLISH.COMMENTS_ENABLED,
62 privacy: CONFIG.DEFAULTS.PUBLISH.PRIVACY,
63 licence: CONFIG.DEFAULTS.PUBLISH.LICENCE
64 }
65 },
66
58 webadmin: { 67 webadmin: {
59 configuration: { 68 configuration: {
60 edition: { 69 edition: {
diff --git a/server/lib/video.ts b/server/lib/video.ts
index 1cfe4f27c..e5af028ea 100644
--- a/server/lib/video.ts
+++ b/server/lib/video.ts
@@ -9,16 +9,17 @@ import { MThumbnail, MUserId, MVideoFile, MVideoTag, MVideoThumbnail, MVideoUUID
9import { ThumbnailType, VideoCreate, VideoPrivacy, VideoTranscodingPayload } from '@shared/models' 9import { ThumbnailType, VideoCreate, VideoPrivacy, VideoTranscodingPayload } from '@shared/models'
10import { CreateJobOptions, JobQueue } from './job-queue/job-queue' 10import { CreateJobOptions, JobQueue } from './job-queue/job-queue'
11import { updateVideoMiniatureFromExisting } from './thumbnail' 11import { updateVideoMiniatureFromExisting } from './thumbnail'
12import { CONFIG } from '@server/initializers/config'
12 13
13function buildLocalVideoFromReq (videoInfo: VideoCreate, channelId: number): FilteredModelAttributes<VideoModel> { 14function buildLocalVideoFromReq (videoInfo: VideoCreate, channelId: number): FilteredModelAttributes<VideoModel> {
14 return { 15 return {
15 name: videoInfo.name, 16 name: videoInfo.name,
16 remote: false, 17 remote: false,
17 category: videoInfo.category, 18 category: videoInfo.category,
18 licence: videoInfo.licence, 19 licence: videoInfo.licence ?? CONFIG.DEFAULTS.PUBLISH.LICENCE,
19 language: videoInfo.language, 20 language: videoInfo.language,
20 commentsEnabled: videoInfo.commentsEnabled !== false, // If the value is not "false", the default is "true" 21 commentsEnabled: videoInfo.commentsEnabled ?? CONFIG.DEFAULTS.PUBLISH.COMMENTS_ENABLED,
21 downloadEnabled: videoInfo.downloadEnabled !== false, 22 downloadEnabled: videoInfo.downloadEnabled ?? CONFIG.DEFAULTS.PUBLISH.DOWNLOAD_ENABLED,
22 waitTranscoding: videoInfo.waitTranscoding || false, 23 waitTranscoding: videoInfo.waitTranscoding || false,
23 nsfw: videoInfo.nsfw || false, 24 nsfw: videoInfo.nsfw || false,
24 description: videoInfo.description, 25 description: videoInfo.description,
diff --git a/server/tests/api/server/config-defaults.ts b/server/tests/api/server/config-defaults.ts
new file mode 100644
index 000000000..2433d3119
--- /dev/null
+++ b/server/tests/api/server/config-defaults.ts
@@ -0,0 +1,116 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3import 'mocha'
4import * as chai from 'chai'
5import { cleanupTests, createSingleServer, FIXTURE_URLS, PeerTubeServer, setAccessTokensToServers, setDefaultVideoChannel } from '@shared/extra-utils'
6import { VideoDetails, VideoPrivacy } from '@shared/models'
7
8const expect = chai.expect
9
10describe('Test config defaults', function () {
11 let server: PeerTubeServer
12 let channelId: number
13
14 before(async function () {
15 this.timeout(30000)
16
17 const overrideConfig = {
18 defaults: {
19 publish: {
20 comments_enabled: false,
21 download_enabled: false,
22 privacy: VideoPrivacy.INTERNAL,
23 licence: 4
24 }
25 }
26 }
27
28 server = await createSingleServer(1, overrideConfig)
29 await setAccessTokensToServers([ server ])
30 await setDefaultVideoChannel([ server ])
31
32 channelId = server.store.channel.id
33 })
34
35 describe('Default publish values', function () {
36 const attributes = {
37 name: 'video',
38 downloadEnabled: undefined,
39 commentsEnabled: undefined,
40 licence: undefined,
41 privacy: VideoPrivacy.PUBLIC // Privacy is mandatory for server
42 }
43
44 function checkVideo (video: VideoDetails) {
45 expect(video.downloadEnabled).to.be.false
46 expect(video.commentsEnabled).to.be.false
47 expect(video.licence.id).to.equal(4)
48 }
49
50 before(async function () {
51 await server.config.disableTranscoding()
52 await server.config.enableImports()
53 await server.config.enableLive({ allowReplay: false, transcoding: false })
54 })
55
56 it('Should have the correct server configuration', async function () {
57 const config = await server.config.getConfig()
58
59 expect(config.defaults.publish.commentsEnabled).to.be.false
60 expect(config.defaults.publish.downloadEnabled).to.be.false
61 expect(config.defaults.publish.licence).to.equal(4)
62 expect(config.defaults.publish.privacy).to.equal(VideoPrivacy.INTERNAL)
63 })
64
65 it('Should respect default values when uploading a video', async function () {
66 for (const mode of [ 'legacy' as 'legacy', 'resumable' as 'resumable' ]) {
67 const { id } = await server.videos.upload({ attributes, mode })
68
69 const video = await server.videos.get({ id })
70 checkVideo(video)
71 }
72 })
73
74 it('Should respect default values when importing a video using URL', async function () {
75 const { video: { id } } = await server.imports.importVideo({
76 attributes: {
77 ...attributes,
78 channelId,
79 targetUrl: FIXTURE_URLS.goodVideo
80 }
81 })
82
83 const video = await server.videos.get({ id })
84 checkVideo(video)
85 })
86
87 it('Should respect default values when importing a video using magnet URI', async function () {
88 const { video: { id } } = await server.imports.importVideo({
89 attributes: {
90 ...attributes,
91 channelId,
92 magnetUri: FIXTURE_URLS.magnet
93 }
94 })
95
96 const video = await server.videos.get({ id })
97 checkVideo(video)
98 })
99
100 it('Should respect default values when creating a live', async function () {
101 const { id } = await server.live.create({
102 fields: {
103 ...attributes,
104 channelId
105 }
106 })
107
108 const video = await server.videos.get({ id })
109 checkVideo(video)
110 })
111 })
112
113 after(async function () {
114 await cleanupTests([ server ])
115 })
116})
diff --git a/server/tests/api/server/index.ts b/server/tests/api/server/index.ts
index 8136fc3c6..45be107ce 100644
--- a/server/tests/api/server/index.ts
+++ b/server/tests/api/server/index.ts
@@ -1,4 +1,6 @@
1import './auto-follows' 1import './auto-follows'
2import './bulk'
3import './config-defaults'
2import './config' 4import './config'
3import './contact-form' 5import './contact-form'
4import './email' 6import './email'
diff --git a/shared/models/server/server-config.model.ts b/shared/models/server/server-config.model.ts
index 9f17276e0..9c3dcd6d3 100644
--- a/shared/models/server/server-config.model.ts
+++ b/shared/models/server/server-config.model.ts
@@ -1,3 +1,4 @@
1import { VideoPrivacy } from '../videos/video-privacy.enum'
1import { ClientScript } from '../plugins/plugin-package-json.model' 2import { ClientScript } from '../plugins/plugin-package-json.model'
2import { NSFWPolicyType } from '../videos/nsfw-policy.type' 3import { NSFWPolicyType } from '../videos/nsfw-policy.type'
3import { BroadcastMessageLevel } from './broadcast-message-level.type' 4import { BroadcastMessageLevel } from './broadcast-message-level.type'
@@ -47,6 +48,15 @@ export interface ServerConfig {
47 } 48 }
48 } 49 }
49 50
51 defaults: {
52 publish: {
53 downloadEnabled: boolean
54 commentsEnabled: boolean
55 privacy: VideoPrivacy
56 licence: number
57 }
58 }
59
50 webadmin: { 60 webadmin: {
51 configuration: { 61 configuration: {
52 edition: { 62 edition: {
diff --git a/support/doc/development/tests.md b/support/doc/development/tests.md
index d36cf8544..02fc41147 100644
--- a/support/doc/development/tests.md
+++ b/support/doc/development/tests.md
@@ -88,13 +88,7 @@ $ BROWSERSTACK_USER=your_user BROWSERSTACK_KEY=your_key npm run e2e:browserstack
88 88
89### Add E2E tests 89### Add E2E tests
90 90
91To add E2E tests and quickly run tests using a local Chrome, first create a test instance: 91To add E2E tests and quickly run tests using a local Chrome:
92
93```bash
94$ npm run clean:server:test && NODE_APP_INSTANCE=1 NODE_ENV=test npm start
95```
96
97Then, just run your suite using:
98 92
99```bash 93```bash
100$ cd client/e2e 94$ cd client/e2e