2019-08-23 15:23:27 +02:00
import { forkJoin } from 'rxjs'
2020-03-11 19:38:17 +01:00
import { ViewportScroller } from '@angular/common'
2020-06-23 14:10:17 +02:00
import { AfterViewChecked , Component , OnInit , ViewChild } from '@angular/core'
import { ConfigService } from '@app/+admin/config/shared/config.service'
import { Notifier } from '@app/core'
import { ServerService } from '@app/core/server/server.service'
2020-08-11 16:07:53 +02:00
import {
2020-08-17 11:47:04 +02:00
ADMIN_EMAIL_VALIDATOR ,
CACHE_CAPTIONS_SIZE_VALIDATOR ,
CACHE_PREVIEWS_SIZE_VALIDATOR ,
INDEX_URL_VALIDATOR ,
INSTANCE_NAME_VALIDATOR ,
INSTANCE_SHORT_DESCRIPTION_VALIDATOR ,
SEARCH_INDEX_URL_VALIDATOR ,
SERVICES_TWITTER_USERNAME_VALIDATOR ,
SIGNUP_LIMIT_VALIDATOR ,
TRANSCODING_THREADS_VALIDATOR
} from '@app/shared/form-validators/custom-config-validators'
import { USER_VIDEO_QUOTA_DAILY_VALIDATOR , USER_VIDEO_QUOTA_VALIDATOR } from '@app/shared/form-validators/user-validators'
import { FormReactive , FormValidatorService , SelectOptionsItem } from '@app/shared/shared-forms'
2020-04-01 16:52:59 +02:00
import { NgbNav } from '@ng-bootstrap/ng-bootstrap'
2020-06-23 14:10:17 +02:00
import { CustomConfig , ServerConfig } from '@shared/models'
2020-11-18 15:55:19 +01:00
import { pairwise } from 'rxjs/operators'
2018-01-17 10:32:03 +01:00
@Component ( {
selector : 'my-edit-custom-config' ,
templateUrl : './edit-custom-config.component.html' ,
styleUrls : [ './edit-custom-config.component.scss' ]
} )
2020-03-11 19:38:17 +01:00
export class EditCustomConfigComponent extends FormReactive implements OnInit , AfterViewChecked {
2020-04-01 16:52:59 +02:00
// FIXME: use built-in router
@ViewChild ( 'nav' ) nav : NgbNav
2020-03-11 19:38:17 +01:00
initDone = false
2018-08-28 09:01:35 +02:00
customConfig : CustomConfig
2020-01-06 12:40:59 +01:00
resolutions : { id : string , label : string , description? : string } [ ] = [ ]
2020-09-17 09:20:52 +02:00
liveResolutions : { id : string , label : string , description? : string } [ ] = [ ]
2018-09-26 14:46:54 +02:00
transcodingThreadOptions : { label : string , value : number } [ ] = [ ]
2020-09-25 16:19:35 +02:00
liveMaxDurationOptions : { label : string , value : number } [ ] = [ ]
2018-01-17 10:32:03 +01:00
2020-08-11 16:07:53 +02:00
languageItems : SelectOptionsItem [ ] = [ ]
categoryItems : SelectOptionsItem [ ] = [ ]
2019-08-23 15:23:27 +02:00
2020-11-18 15:55:19 +01:00
signupAlertMessage : string
2019-12-18 15:31:54 +01:00
private serverConfig : ServerConfig
2018-01-17 10:32:03 +01:00
constructor (
2020-03-11 19:38:17 +01:00
private viewportScroller : ViewportScroller ,
2018-06-05 10:58:45 +02:00
protected formValidatorService : FormValidatorService ,
2018-12-19 16:04:34 +01:00
private notifier : Notifier ,
2018-01-17 10:32:03 +01:00
private configService : ConfigService ,
2020-08-12 10:40:04 +02:00
private serverService : ServerService
2018-01-17 10:32:03 +01:00
) {
super ( )
2018-09-26 14:46:54 +02:00
this . resolutions = [
2019-11-27 10:13:31 +01:00
{
2019-11-01 02:06:19 +01:00
id : '0p' ,
2020-08-12 10:40:04 +02:00
label : $localize ` Audio-only ` ,
description : $localize ` A <code>.mp4</code> that keeps the original audio track, with no video `
2019-11-01 02:06:19 +01:00
} ,
2019-06-12 17:26:23 +02:00
{
id : '240p' ,
2020-08-12 10:40:04 +02:00
label : $localize ` 240p `
2019-06-12 17:26:23 +02:00
} ,
{
id : '360p' ,
2020-08-12 10:40:04 +02:00
label : $localize ` 360p `
2019-06-12 17:26:23 +02:00
} ,
{
id : '480p' ,
2020-08-12 10:40:04 +02:00
label : $localize ` 480p `
2019-06-12 17:26:23 +02:00
} ,
{
id : '720p' ,
2020-08-12 10:40:04 +02:00
label : $localize ` 720p `
2019-06-12 17:26:23 +02:00
} ,
{
id : '1080p' ,
2020-08-12 10:40:04 +02:00
label : $localize ` 1080p `
2019-06-12 17:26:23 +02:00
} ,
2020-12-24 18:02:04 +01:00
{
id : '1440p' ,
label : $localize ` 1440p `
} ,
2019-06-12 17:26:23 +02:00
{
id : '2160p' ,
2020-08-12 10:40:04 +02:00
label : $localize ` 2160p `
2019-06-12 17:26:23 +02:00
}
2018-09-26 14:46:54 +02:00
]
2020-09-17 09:20:52 +02:00
this . liveResolutions = this . resolutions . filter ( r = > r . id !== '0p' )
2018-09-26 14:46:54 +02:00
this . transcodingThreadOptions = [
2020-08-12 10:40:04 +02:00
{ value : 0 , label : $localize ` Auto (via ffmpeg) ` } ,
2018-09-26 14:46:54 +02:00
{ value : 1 , label : '1' } ,
{ value : 2 , label : '2' } ,
{ value : 4 , label : '4' } ,
{ value : 8 , label : '8' }
]
2020-09-25 16:19:35 +02:00
this . liveMaxDurationOptions = [
2020-12-15 09:23:28 +01:00
{ value : - 1 , label : $localize ` No limit ` } ,
2020-09-25 16:19:35 +02:00
{ value : 1000 * 3600 , label : $localize ` 1 hour ` } ,
{ value : 1000 * 3600 * 3 , label : $localize ` 3 hours ` } ,
{ value : 1000 * 3600 * 5 , label : $localize ` 5 hours ` } ,
{ value : 1000 * 3600 * 10 , label : $localize ` 10 hours ` }
]
2018-01-17 10:32:03 +01:00
}
2018-08-28 17:39:29 +02:00
get videoQuotaOptions ( ) {
2018-09-26 14:46:54 +02:00
return this . configService . videoQuotaOptions
2018-08-28 17:39:29 +02:00
}
get videoQuotaDailyOptions ( ) {
2018-09-26 14:46:54 +02:00
return this . configService . videoQuotaDailyOptions
2018-08-28 17:39:29 +02:00
}
2019-07-09 11:45:19 +02:00
get availableThemes ( ) {
2019-12-18 15:31:54 +01:00
return this . serverConfig . theme . registered
2019-07-10 14:06:19 +02:00
. map ( t = > t . name )
2019-07-09 11:45:19 +02:00
}
2021-01-11 09:20:44 +01:00
get liveRTMPPort ( ) {
return this . serverConfig . live . rtmp . port
}
2020-12-14 12:47:10 +01:00
getTotalTranscodingThreads ( ) {
const transcodingEnabled = this . form . value [ 'transcoding' ] [ 'enabled' ]
const transcodingThreads = this . form . value [ 'transcoding' ] [ 'threads' ]
const liveTranscodingEnabled = this . form . value [ 'live' ] [ 'transcoding' ] [ 'enabled' ]
const liveTranscodingThreads = this . form . value [ 'live' ] [ 'transcoding' ] [ 'threads' ]
// checks whether all enabled method are on fixed values and not on auto (= 0)
let noneOnAuto = ! transcodingEnabled || + transcodingThreads > 0
noneOnAuto && = ! liveTranscodingEnabled || + liveTranscodingThreads > 0
// count total of fixed value, repalcing auto by a single thread (knowing it will display "at least")
let value = 0
if ( transcodingEnabled ) value += + transcodingThreads || 1
if ( liveTranscodingEnabled ) value += + liveTranscodingThreads || 1
return {
value ,
atMost : noneOnAuto , // auto switches everything to a least estimation since ffmpeg will take as many threads as possible
unit : value > 1
? $localize ` threads `
: $localize ` thread `
}
}
2018-01-17 10:32:03 +01:00
getResolutionKey ( resolution : string ) {
2019-01-10 09:58:08 +01:00
return 'transcoding.resolutions.' + resolution
2018-01-17 10:32:03 +01:00
}
2018-06-05 10:58:45 +02:00
ngOnInit ( ) {
2019-12-18 15:31:54 +01:00
this . serverConfig = this . serverService . getTmpConfig ( )
this . serverService . getConfig ( )
2020-09-25 16:19:35 +02:00
. subscribe ( config = > {
this . serverConfig = config
} )
2019-12-18 15:31:54 +01:00
2019-01-10 09:58:08 +01:00
const formGroupData : { [ key in keyof CustomConfig ] : any } = {
instance : {
2020-08-17 11:47:04 +02:00
name : INSTANCE_NAME_VALIDATOR ,
shortDescription : INSTANCE_SHORT_DESCRIPTION_VALIDATOR ,
2019-01-10 09:58:08 +01:00
description : null ,
2019-08-23 15:23:27 +02:00
2019-02-20 15:36:43 +01:00
isNSFW : false ,
2019-01-10 09:58:08 +01:00
defaultNSFWPolicy : null ,
2019-08-23 15:23:27 +02:00
terms : null ,
codeOfConduct : null ,
2019-09-03 09:49:04 +02:00
creationReason : null ,
2019-08-23 15:23:27 +02:00
moderationInformation : null ,
administrator : null ,
maintenanceLifetime : null ,
businessModel : null ,
2019-09-05 09:43:35 +02:00
hardwareInformation : null ,
2019-08-23 15:23:27 +02:00
categories : null ,
languages : null ,
defaultClientRoute : null ,
2021-01-26 01:53:13 +01:00
defaultTrendingRoute : null ,
pages : {
hot : {
enabled : null
}
} ,
2019-08-23 15:23:27 +02:00
2019-01-10 09:58:08 +01:00
customizations : {
javascript : null ,
css : null
}
} ,
2019-07-09 11:45:19 +02:00
theme : {
default : null
} ,
2019-01-10 09:58:08 +01:00
services : {
twitter : {
2020-08-17 11:47:04 +02:00
username : SERVICES_TWITTER_USERNAME_VALIDATOR ,
2019-01-10 09:58:08 +01:00
whitelisted : null
}
} ,
cache : {
previews : {
2020-08-17 11:47:04 +02:00
size : CACHE_PREVIEWS_SIZE_VALIDATOR
2019-01-10 09:58:08 +01:00
} ,
captions : {
2020-08-17 11:47:04 +02:00
size : CACHE_CAPTIONS_SIZE_VALIDATOR
2019-01-10 09:58:08 +01:00
}
} ,
signup : {
enabled : null ,
2020-08-17 11:47:04 +02:00
limit : SIGNUP_LIMIT_VALIDATOR ,
2019-01-10 09:58:08 +01:00
requiresEmailVerification : null
} ,
import : {
videos : {
http : {
enabled : null
} ,
torrent : {
enabled : null
}
}
} ,
admin : {
2020-08-17 11:47:04 +02:00
email : ADMIN_EMAIL_VALIDATOR
2019-01-10 09:58:08 +01:00
} ,
contactForm : {
enabled : null
} ,
user : {
2020-08-17 11:47:04 +02:00
videoQuota : USER_VIDEO_QUOTA_VALIDATOR ,
videoQuotaDaily : USER_VIDEO_QUOTA_DAILY_VALIDATOR
2019-01-10 09:58:08 +01:00
} ,
transcoding : {
enabled : null ,
2020-08-17 11:47:04 +02:00
threads : TRANSCODING_THREADS_VALIDATOR ,
2019-01-10 09:58:08 +01:00
allowAdditionalExtensions : null ,
2019-05-16 16:55:34 +02:00
allowAudioFiles : null ,
2019-11-05 10:13:37 +01:00
resolutions : { } ,
hls : {
enabled : null
2019-11-21 17:01:24 +01:00
} ,
webtorrent : {
enabled : null
2019-11-05 10:13:37 +01:00
}
2019-04-02 11:26:47 +02:00
} ,
2020-09-17 09:20:52 +02:00
live : {
enabled : null ,
2020-09-25 16:19:35 +02:00
maxDuration : null ,
2020-10-28 15:24:40 +01:00
maxInstanceLives : null ,
maxUserLives : null ,
2020-09-25 16:19:35 +02:00
allowReplay : null ,
2020-09-17 09:20:52 +02:00
transcoding : {
enabled : null ,
threads : TRANSCODING_THREADS_VALIDATOR ,
resolutions : { }
}
} ,
2019-04-02 11:26:47 +02:00
autoBlacklist : {
videos : {
ofUsers : {
enabled : null
}
}
2019-04-08 15:47:44 +02:00
} ,
followers : {
instance : {
enabled : null ,
manualApproval : null
}
2019-09-04 14:30:34 +02:00
} ,
followings : {
instance : {
autoFollowBack : {
enabled : null
} ,
autoFollowIndex : {
enabled : null ,
2020-08-17 11:47:04 +02:00
indexUrl : INDEX_URL_VALIDATOR
2019-09-04 14:30:34 +02:00
}
}
2020-05-28 11:15:38 +02:00
} ,
broadcastMessage : {
enabled : null ,
level : null ,
dismissable : null ,
message : null
2020-05-29 16:16:24 +02:00
} ,
search : {
remoteUri : {
users : null ,
anonymous : null
} ,
searchIndex : {
enabled : null ,
2020-08-17 11:47:04 +02:00
url : SEARCH_INDEX_URL_VALIDATOR ,
2020-05-29 16:16:24 +02:00
disableLocalSearch : null ,
isDefaultSearch : null
}
2019-01-10 09:58:08 +01:00
}
2018-01-17 10:32:03 +01:00
}
2019-01-10 09:58:08 +01:00
const defaultValues = {
transcoding : {
resolutions : { }
2020-09-17 09:20:52 +02:00
} ,
live : {
transcoding : {
resolutions : { }
}
2019-01-10 09:58:08 +01:00
}
}
2020-09-17 09:20:52 +02:00
2018-01-17 10:32:03 +01:00
for ( const resolution of this . resolutions ) {
2019-06-12 17:26:23 +02:00
defaultValues . transcoding . resolutions [ resolution . id ] = 'false'
formGroupData . transcoding . resolutions [ resolution . id ] = null
2018-01-17 10:32:03 +01:00
}
2020-09-17 09:20:52 +02:00
for ( const resolution of this . liveResolutions ) {
defaultValues . live . transcoding . resolutions [ resolution . id ] = 'false'
formGroupData . live . transcoding . resolutions [ resolution . id ] = null
}
2018-06-05 10:58:45 +02:00
this . buildForm ( formGroupData )
2020-01-06 16:43:15 +01:00
this . loadForm ( )
2020-11-18 15:55:19 +01:00
2020-01-06 16:43:15 +01:00
this . checkTranscodingFields ( )
2020-11-18 15:55:19 +01:00
this . checkSignupField ( )
2018-01-17 10:32:03 +01:00
}
2020-03-11 19:38:17 +01:00
ngAfterViewChecked ( ) {
if ( ! this . initDone ) {
this . initDone = true
this . gotoAnchor ( )
}
}
2018-01-17 10:32:03 +01:00
isTranscodingEnabled ( ) {
2019-01-10 09:58:08 +01:00
return this . form . value [ 'transcoding' ] [ 'enabled' ] === true
2018-01-17 10:32:03 +01:00
}
2020-09-17 09:20:52 +02:00
isLiveEnabled ( ) {
return this . form . value [ 'live' ] [ 'enabled' ] === true
}
isLiveTranscodingEnabled ( ) {
return this . form . value [ 'live' ] [ 'transcoding' ] [ 'enabled' ] === true
}
2018-01-17 10:32:03 +01:00
isSignupEnabled ( ) {
2019-01-10 09:58:08 +01:00
return this . form . value [ 'signup' ] [ 'enabled' ] === true
2018-01-17 10:32:03 +01:00
}
2020-05-29 16:16:24 +02:00
isSearchIndexEnabled ( ) {
return this . form . value [ 'search' ] [ 'searchIndex' ] [ 'enabled' ] === true
}
2020-01-21 10:47:56 +01:00
isAutoFollowIndexEnabled ( ) {
return this . form . value [ 'followings' ] [ 'instance' ] [ 'autoFollowIndex' ] [ 'enabled' ] === true
}
2021-01-26 01:53:13 +01:00
isTrendingHotEnabled ( ) {
return this . form . value [ 'instance' ] [ 'pages' ] [ 'hot' ] [ 'enabled' ] === true
}
2018-02-22 15:29:32 +01:00
async formValidated ( ) {
2020-11-10 14:15:59 +01:00
const value : CustomConfig = this . form . getRawValue ( )
this . configService . updateCustomConfig ( value )
2018-01-17 10:32:03 +01:00
. subscribe (
res = > {
this . customConfig = res
// Reload general configuration
2019-12-18 15:31:54 +01:00
this . serverService . resetConfig ( )
2018-01-17 10:32:03 +01:00
this . updateForm ( )
2018-01-31 16:42:40 +01:00
2020-08-12 10:40:04 +02:00
this . notifier . success ( $localize ` Configuration updated. ` )
2018-01-17 10:32:03 +01:00
} ,
2018-12-19 16:04:34 +01:00
err = > this . notifier . error ( err . message )
2018-01-17 10:32:03 +01:00
)
}
2020-03-11 19:38:17 +01:00
gotoAnchor ( ) {
2020-04-01 16:52:59 +02:00
const hashToNav = {
2020-03-11 19:38:17 +01:00
'customizations' : 'advanced-configuration'
}
const hash = window . location . hash . replace ( '#' , '' )
2020-04-01 16:52:59 +02:00
if ( hash && Object . keys ( hashToNav ) . includes ( hash ) ) {
this . nav . select ( hashToNav [ hash ] )
2020-03-11 19:38:17 +01:00
setTimeout ( ( ) = > this . viewportScroller . scrollToAnchor ( hash ) , 100 )
}
}
2020-09-25 16:19:35 +02:00
hasConsistentOptions ( ) {
if ( this . hasLiveAllowReplayConsistentOptions ( ) ) return true
return false
}
hasLiveAllowReplayConsistentOptions ( ) {
if ( this . isTranscodingEnabled ( ) === false && this . isLiveEnabled ( ) && this . form . value [ 'live' ] [ 'allowReplay' ] === true ) {
return false
}
return true
}
2018-01-17 10:32:03 +01:00
private updateForm ( ) {
2019-01-10 09:58:08 +01:00
this . form . patchValue ( this . customConfig )
2018-01-17 10:32:03 +01:00
}
2020-01-06 16:43:15 +01:00
private loadForm ( ) {
forkJoin ( [
this . configService . getCustomConfig ( ) ,
this . serverService . getVideoLanguages ( ) ,
this . serverService . getVideoCategories ( )
] ) . subscribe (
( [ config , languages , categories ] ) = > {
this . customConfig = config
2020-08-11 16:07:53 +02:00
this . languageItems = languages . map ( l = > ( { label : l.label , id : l.id } ) )
this . categoryItems = categories . map ( l = > ( { label : l.label , id : l.id + '' } ) )
2020-01-06 16:43:15 +01:00
this . updateForm ( )
// Force form validation
this . forceCheck ( )
} ,
err = > this . notifier . error ( err . message )
)
}
private checkTranscodingFields ( ) {
const hlsControl = this . form . get ( 'transcoding.hls.enabled' )
const webtorrentControl = this . form . get ( 'transcoding.webtorrent.enabled' )
webtorrentControl . valueChanges
. subscribe ( newValue = > {
if ( newValue === false && ! hlsControl . disabled ) {
hlsControl . disable ( )
}
if ( newValue === true && ! hlsControl . enabled ) {
hlsControl . enable ( )
}
} )
hlsControl . valueChanges
. subscribe ( newValue = > {
if ( newValue === false && ! webtorrentControl . disabled ) {
webtorrentControl . disable ( )
}
if ( newValue === true && ! webtorrentControl . enabled ) {
webtorrentControl . enable ( )
}
} )
}
2020-11-18 15:55:19 +01:00
private checkSignupField ( ) {
const signupControl = this . form . get ( 'signup.enabled' )
signupControl . valueChanges
. pipe ( pairwise ( ) )
. subscribe ( ( [ oldValue , newValue ] ) = > {
if ( oldValue !== true && newValue === true ) {
// tslint:disable:max-line-length
this . signupAlertMessage = $localize ` You enabled signup: we automatically enabled the "Block new videos automatically" checkbox of the "Videos" section just below. `
this . form . patchValue ( {
autoBlacklist : {
videos : {
ofUsers : {
enabled : true
}
}
}
} )
}
} )
}
2018-01-17 10:32:03 +01:00
}