2020-05-29 16:16:24 +02:00
|
|
|
import { Observable, of, Subject } from 'rxjs'
|
2019-12-18 15:31:54 +01:00
|
|
|
import { first, map, share, shareReplay, switchMap, tap } from 'rxjs/operators'
|
2017-10-09 19:12:40 +02:00
|
|
|
import { HttpClient } from '@angular/common/http'
|
2018-06-06 16:46:42 +02:00
|
|
|
import { Inject, Injectable, LOCALE_ID } from '@angular/core'
|
2020-08-03 18:06:49 +02:00
|
|
|
import { getDevLocale, isOnDevLocale, sortBy } from '@app/helpers'
|
|
|
|
import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage'
|
2020-08-06 14:58:01 +02:00
|
|
|
import { getCompleteLocale, isDefaultLocale, peertubeTranslate } from '@shared/core-utils/i18n'
|
|
|
|
import { SearchTargetType, ServerConfig, ServerStats, VideoConstant } from '@shared/models'
|
2020-05-29 16:16:24 +02:00
|
|
|
import { environment } from '../../../environments/environment'
|
2017-10-09 19:12:40 +02:00
|
|
|
|
|
|
|
@Injectable()
|
|
|
|
export class ServerService {
|
2017-12-11 17:36:46 +01:00
|
|
|
private static BASE_CONFIG_URL = environment.apiUrl + '/api/v1/config/'
|
|
|
|
private static BASE_VIDEO_URL = environment.apiUrl + '/api/v1/videos/'
|
2019-03-06 15:36:44 +01:00
|
|
|
private static BASE_VIDEO_PLAYLIST_URL = environment.apiUrl + '/api/v1/video-playlists/'
|
2018-06-06 16:46:42 +02:00
|
|
|
private static BASE_LOCALE_URL = environment.apiUrl + '/client/locales/'
|
2020-01-10 10:11:28 +01:00
|
|
|
private static BASE_STATS_URL = environment.apiUrl + '/api/v1/server/stats'
|
|
|
|
|
2018-01-31 17:47:36 +01:00
|
|
|
private static CONFIG_LOCAL_STORAGE_KEY = 'server-config'
|
2017-10-09 19:12:40 +02:00
|
|
|
|
2020-05-28 11:15:38 +02:00
|
|
|
configReloaded = new Subject<ServerConfig>()
|
2017-12-07 17:22:44 +01:00
|
|
|
|
2019-12-18 15:31:54 +01:00
|
|
|
private localeObservable: Observable<any>
|
|
|
|
private videoLicensesObservable: Observable<VideoConstant<number>[]>
|
|
|
|
private videoCategoriesObservable: Observable<VideoConstant<number>[]>
|
|
|
|
private videoPrivaciesObservable: Observable<VideoConstant<number>[]>
|
|
|
|
private videoPlaylistPrivaciesObservable: Observable<VideoConstant<number>[]>
|
|
|
|
private videoLanguagesObservable: Observable<VideoConstant<string>[]>
|
|
|
|
private configObservable: Observable<ServerConfig>
|
|
|
|
|
|
|
|
private configReset = false
|
|
|
|
|
|
|
|
private configLoaded = false
|
2017-10-09 19:12:40 +02:00
|
|
|
private config: ServerConfig = {
|
2018-01-31 17:47:36 +01:00
|
|
|
instance: {
|
2018-02-22 10:22:53 +01:00
|
|
|
name: 'PeerTube',
|
2018-03-22 14:13:30 +01:00
|
|
|
shortDescription: 'PeerTube, a federated (ActivityPub) video streaming platform ' +
|
|
|
|
'using P2P (BitTorrent) directly in the web browser with WebTorrent and Angular.',
|
2018-03-01 13:57:29 +01:00
|
|
|
defaultClientRoute: '',
|
2019-02-20 15:36:43 +01:00
|
|
|
isNSFW: false,
|
2018-04-19 11:01:34 +02:00
|
|
|
defaultNSFWPolicy: 'do_not_list' as 'do_not_list',
|
2018-02-22 10:22:53 +01:00
|
|
|
customizations: {
|
|
|
|
javascript: '',
|
|
|
|
css: ''
|
|
|
|
}
|
2018-01-31 17:47:36 +01:00
|
|
|
},
|
2019-07-09 11:45:19 +02:00
|
|
|
plugin: {
|
2020-04-28 14:49:03 +02:00
|
|
|
registered: [],
|
|
|
|
registeredExternalAuths: [],
|
|
|
|
registeredIdAndPassAuths: []
|
2019-07-09 11:45:19 +02:00
|
|
|
},
|
|
|
|
theme: {
|
|
|
|
registered: [],
|
|
|
|
default: 'default'
|
|
|
|
},
|
2018-12-05 15:10:45 +01:00
|
|
|
email: {
|
|
|
|
enabled: false
|
|
|
|
},
|
2019-01-10 09:58:08 +01:00
|
|
|
contactForm: {
|
|
|
|
enabled: false
|
|
|
|
},
|
2018-01-31 10:19:34 +01:00
|
|
|
serverVersion: 'Unknown',
|
2017-10-09 19:12:40 +02:00
|
|
|
signup: {
|
2018-05-22 19:43:13 +02:00
|
|
|
allowed: false,
|
2018-08-31 09:18:19 +02:00
|
|
|
allowedForCurrentIP: false,
|
|
|
|
requiresEmailVerification: false
|
2017-10-19 17:33:32 +02:00
|
|
|
},
|
|
|
|
transcoding: {
|
2019-01-29 08:37:25 +01:00
|
|
|
enabledResolutions: [],
|
|
|
|
hls: {
|
|
|
|
enabled: false
|
2019-11-21 17:01:24 +01:00
|
|
|
},
|
|
|
|
webtorrent: {
|
|
|
|
enabled: true
|
2019-01-29 08:37:25 +01:00
|
|
|
}
|
2018-01-03 11:10:40 +01:00
|
|
|
},
|
2020-09-17 09:20:52 +02:00
|
|
|
live: {
|
|
|
|
enabled: false,
|
2020-09-25 16:19:35 +02:00
|
|
|
allowReplay: true,
|
|
|
|
maxDuration: null,
|
2020-10-28 15:24:40 +01:00
|
|
|
maxInstanceLives: -1,
|
|
|
|
maxUserLives: -1,
|
2020-09-17 09:20:52 +02:00
|
|
|
transcoding: {
|
|
|
|
enabled: false,
|
|
|
|
enabledResolutions: []
|
2021-01-11 09:20:44 +01:00
|
|
|
},
|
|
|
|
rtmp: {
|
|
|
|
port: 1935
|
2020-09-17 09:20:52 +02:00
|
|
|
}
|
|
|
|
},
|
2018-01-03 11:10:40 +01:00
|
|
|
avatar: {
|
|
|
|
file: {
|
|
|
|
size: { max: 0 },
|
|
|
|
extensions: []
|
|
|
|
}
|
|
|
|
},
|
|
|
|
video: {
|
2018-02-16 16:35:32 +01:00
|
|
|
image: {
|
|
|
|
size: { max: 0 },
|
|
|
|
extensions: []
|
|
|
|
},
|
2018-01-03 11:10:40 +01:00
|
|
|
file: {
|
|
|
|
extensions: []
|
|
|
|
}
|
2018-03-27 13:42:57 +02:00
|
|
|
},
|
2018-07-12 19:02:00 +02:00
|
|
|
videoCaption: {
|
|
|
|
file: {
|
|
|
|
size: { max: 0 },
|
|
|
|
extensions: []
|
|
|
|
}
|
|
|
|
},
|
2018-03-27 13:42:57 +02:00
|
|
|
user: {
|
2018-08-28 09:01:35 +02:00
|
|
|
videoQuota: -1,
|
|
|
|
videoQuotaDaily: -1
|
2018-08-03 11:10:31 +02:00
|
|
|
},
|
|
|
|
import: {
|
2018-08-03 17:00:19 +02:00
|
|
|
videos: {
|
2018-08-03 11:10:31 +02:00
|
|
|
http: {
|
|
|
|
enabled: false
|
2018-08-07 10:07:53 +02:00
|
|
|
},
|
|
|
|
torrent: {
|
|
|
|
enabled: false
|
2018-08-03 11:10:31 +02:00
|
|
|
}
|
|
|
|
}
|
2019-01-14 09:06:48 +01:00
|
|
|
},
|
|
|
|
trending: {
|
|
|
|
videos: {
|
|
|
|
intervalDays: 0
|
|
|
|
}
|
2019-04-02 11:26:47 +02:00
|
|
|
},
|
|
|
|
autoBlacklist: {
|
|
|
|
videos: {
|
|
|
|
ofUsers: {
|
|
|
|
enabled: false
|
|
|
|
}
|
|
|
|
}
|
2019-04-10 09:23:18 +02:00
|
|
|
},
|
|
|
|
tracker: {
|
|
|
|
enabled: true
|
2020-01-10 15:46:17 +01:00
|
|
|
},
|
|
|
|
followings: {
|
|
|
|
instance: {
|
|
|
|
autoFollowIndex: {
|
|
|
|
indexUrl: 'https://instances.joinpeertube.org'
|
|
|
|
}
|
|
|
|
}
|
2020-05-28 11:15:38 +02:00
|
|
|
},
|
|
|
|
broadcastMessage: {
|
|
|
|
enabled: false,
|
|
|
|
message: '',
|
|
|
|
level: 'info',
|
|
|
|
dismissable: false
|
2020-05-29 16:16:24 +02:00
|
|
|
},
|
|
|
|
search: {
|
|
|
|
remoteUri: {
|
|
|
|
users: true,
|
|
|
|
anonymous: false
|
|
|
|
},
|
|
|
|
searchIndex: {
|
|
|
|
enabled: false,
|
|
|
|
url: '',
|
|
|
|
disableLocalSearch: false,
|
|
|
|
isDefaultSearch: false
|
|
|
|
}
|
2017-10-09 19:12:40 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-06 16:46:42 +02:00
|
|
|
constructor (
|
|
|
|
private http: HttpClient,
|
|
|
|
@Inject(LOCALE_ID) private localeId: string
|
|
|
|
) {
|
2018-06-06 17:37:13 +02:00
|
|
|
this.loadConfigLocally()
|
2018-01-31 17:47:36 +01:00
|
|
|
}
|
2017-10-09 19:12:40 +02:00
|
|
|
|
2019-12-18 15:31:54 +01:00
|
|
|
getServerVersionAndCommit () {
|
|
|
|
const serverVersion = this.config.serverVersion
|
|
|
|
const commit = this.config.serverCommit || ''
|
2018-02-22 10:22:53 +01:00
|
|
|
|
2019-12-18 15:31:54 +01:00
|
|
|
let result = serverVersion
|
|
|
|
if (commit) result += '...' + commit
|
2017-10-09 19:12:40 +02:00
|
|
|
|
2019-12-18 15:31:54 +01:00
|
|
|
return result
|
2017-10-09 19:12:40 +02:00
|
|
|
}
|
|
|
|
|
2019-12-18 15:31:54 +01:00
|
|
|
resetConfig () {
|
|
|
|
this.configLoaded = false
|
|
|
|
this.configReset = true
|
2020-05-28 11:15:38 +02:00
|
|
|
|
|
|
|
// Notify config update
|
|
|
|
this.getConfig().subscribe(() => {
|
|
|
|
// empty, to fire a reset config event
|
|
|
|
})
|
2017-10-09 19:12:40 +02:00
|
|
|
}
|
|
|
|
|
2019-12-18 15:31:54 +01:00
|
|
|
getConfig () {
|
|
|
|
if (this.configLoaded) return of(this.config)
|
2017-10-09 19:12:40 +02:00
|
|
|
|
2019-12-18 15:31:54 +01:00
|
|
|
if (!this.configObservable) {
|
|
|
|
this.configObservable = this.http.get<ServerConfig>(ServerService.BASE_CONFIG_URL)
|
|
|
|
.pipe(
|
2020-02-17 11:56:28 +01:00
|
|
|
tap(config => this.saveConfigLocally(config)),
|
|
|
|
tap(config => {
|
|
|
|
this.config = config
|
|
|
|
this.configLoaded = true
|
|
|
|
}),
|
2020-05-28 11:15:38 +02:00
|
|
|
tap(config => {
|
2019-12-18 15:31:54 +01:00
|
|
|
if (this.configReset) {
|
2020-05-28 11:15:38 +02:00
|
|
|
this.configReloaded.next(config)
|
2019-12-18 15:31:54 +01:00
|
|
|
this.configReset = false
|
|
|
|
}
|
|
|
|
}),
|
|
|
|
share()
|
|
|
|
)
|
|
|
|
}
|
2019-03-06 15:36:44 +01:00
|
|
|
|
2019-12-18 15:31:54 +01:00
|
|
|
return this.configObservable
|
2017-10-31 11:52:52 +01:00
|
|
|
}
|
|
|
|
|
2019-12-18 15:31:54 +01:00
|
|
|
getTmpConfig () {
|
|
|
|
return this.config
|
2019-12-13 10:19:41 +01:00
|
|
|
}
|
|
|
|
|
2017-10-09 19:12:40 +02:00
|
|
|
getVideoCategories () {
|
2019-12-18 15:31:54 +01:00
|
|
|
if (!this.videoCategoriesObservable) {
|
|
|
|
this.videoCategoriesObservable = this.loadAttributeEnum<number>(ServerService.BASE_VIDEO_URL, 'categories', true)
|
|
|
|
}
|
|
|
|
|
|
|
|
return this.videoCategoriesObservable.pipe(first())
|
2017-10-09 19:12:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
getVideoLicences () {
|
2019-12-18 15:31:54 +01:00
|
|
|
if (!this.videoLicensesObservable) {
|
|
|
|
this.videoLicensesObservable = this.loadAttributeEnum<number>(ServerService.BASE_VIDEO_URL, 'licences')
|
|
|
|
}
|
|
|
|
|
|
|
|
return this.videoLicensesObservable.pipe(first())
|
2017-10-09 19:12:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
getVideoLanguages () {
|
2019-12-18 15:31:54 +01:00
|
|
|
if (!this.videoLanguagesObservable) {
|
|
|
|
this.videoLanguagesObservable = this.loadAttributeEnum<string>(ServerService.BASE_VIDEO_URL, 'languages', true)
|
|
|
|
}
|
|
|
|
|
|
|
|
return this.videoLanguagesObservable.pipe(first())
|
2017-10-09 19:12:40 +02:00
|
|
|
}
|
|
|
|
|
2017-10-31 11:52:52 +01:00
|
|
|
getVideoPrivacies () {
|
2019-12-18 15:31:54 +01:00
|
|
|
if (!this.videoPrivaciesObservable) {
|
|
|
|
this.videoPrivaciesObservable = this.loadAttributeEnum<number>(ServerService.BASE_VIDEO_URL, 'privacies')
|
|
|
|
}
|
|
|
|
|
|
|
|
return this.videoPrivaciesObservable.pipe(first())
|
2017-10-31 11:52:52 +01:00
|
|
|
}
|
|
|
|
|
2019-03-06 15:36:44 +01:00
|
|
|
getVideoPlaylistPrivacies () {
|
2019-12-18 15:31:54 +01:00
|
|
|
if (!this.videoPlaylistPrivaciesObservable) {
|
|
|
|
this.videoPlaylistPrivaciesObservable = this.loadAttributeEnum<number>(ServerService.BASE_VIDEO_PLAYLIST_URL, 'privacies')
|
|
|
|
}
|
|
|
|
|
|
|
|
return this.videoPlaylistPrivaciesObservable.pipe(first())
|
|
|
|
}
|
|
|
|
|
|
|
|
getServerLocale () {
|
|
|
|
if (!this.localeObservable) {
|
|
|
|
const completeLocale = isOnDevLocale() ? getDevLocale() : getCompleteLocale(this.localeId)
|
|
|
|
|
|
|
|
// Default locale, nothing to translate
|
|
|
|
if (isDefaultLocale(completeLocale)) {
|
|
|
|
this.localeObservable = of({}).pipe(shareReplay())
|
|
|
|
} else {
|
|
|
|
this.localeObservable = this.http
|
|
|
|
.get(ServerService.BASE_LOCALE_URL + completeLocale + '/server.json')
|
|
|
|
.pipe(shareReplay())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return this.localeObservable.pipe(first())
|
2019-03-06 15:36:44 +01:00
|
|
|
}
|
|
|
|
|
2020-01-10 10:11:28 +01:00
|
|
|
getServerStats () {
|
|
|
|
return this.http.get<ServerStats>(ServerService.BASE_STATS_URL)
|
|
|
|
}
|
|
|
|
|
2020-05-29 16:16:24 +02:00
|
|
|
getDefaultSearchTarget (): Promise<SearchTargetType> {
|
|
|
|
return this.getConfig().pipe(
|
|
|
|
map(config => {
|
|
|
|
const searchIndexConfig = config.search.searchIndex
|
|
|
|
|
|
|
|
if (searchIndexConfig.enabled && (searchIndexConfig.isDefaultSearch || searchIndexConfig.disableLocalSearch)) {
|
|
|
|
return 'search-index'
|
|
|
|
}
|
|
|
|
|
|
|
|
return 'local'
|
|
|
|
})
|
|
|
|
).toPromise()
|
|
|
|
}
|
|
|
|
|
2019-12-18 15:31:54 +01:00
|
|
|
private loadAttributeEnum <T extends string | number> (
|
2019-03-06 15:36:44 +01:00
|
|
|
baseUrl: string,
|
2017-10-31 11:52:52 +01:00
|
|
|
attributeName: 'categories' | 'licences' | 'languages' | 'privacies',
|
2018-02-20 09:50:44 +01:00
|
|
|
sort = false
|
2017-10-31 11:52:52 +01:00
|
|
|
) {
|
2019-12-18 15:31:54 +01:00
|
|
|
return this.getServerLocale()
|
|
|
|
.pipe(
|
|
|
|
switchMap(translations => {
|
|
|
|
return this.http.get<{ [ id: string ]: string }>(baseUrl + attributeName)
|
|
|
|
.pipe(map(data => ({ data, translations })))
|
|
|
|
}),
|
|
|
|
map(({ data, translations }) => {
|
2020-03-11 16:41:38 +01:00
|
|
|
const hashToPopulate: VideoConstant<T>[] = Object.keys(data)
|
|
|
|
.map(dataKey => {
|
|
|
|
const label = data[ dataKey ]
|
|
|
|
|
|
|
|
const id = attributeName === 'languages'
|
|
|
|
? dataKey as T
|
|
|
|
: parseInt(dataKey, 10) as T
|
|
|
|
|
|
|
|
return {
|
|
|
|
id,
|
|
|
|
label: peertubeTranslate(label, translations)
|
|
|
|
}
|
|
|
|
})
|
2019-12-18 15:31:54 +01:00
|
|
|
|
|
|
|
if (sort === true) sortBy(hashToPopulate, 'label')
|
2018-06-06 16:46:42 +02:00
|
|
|
|
2019-12-18 15:31:54 +01:00
|
|
|
return hashToPopulate
|
|
|
|
}),
|
|
|
|
shareReplay()
|
|
|
|
)
|
2017-10-09 19:12:40 +02:00
|
|
|
}
|
2018-01-31 17:47:36 +01:00
|
|
|
|
|
|
|
private saveConfigLocally (config: ServerConfig) {
|
2018-03-23 14:26:20 +01:00
|
|
|
peertubeLocalStorage.setItem(ServerService.CONFIG_LOCAL_STORAGE_KEY, JSON.stringify(config))
|
2018-01-31 17:47:36 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
private loadConfigLocally () {
|
2018-03-23 14:26:20 +01:00
|
|
|
const configString = peertubeLocalStorage.getItem(ServerService.CONFIG_LOCAL_STORAGE_KEY)
|
2018-01-31 17:47:36 +01:00
|
|
|
|
|
|
|
if (configString) {
|
|
|
|
try {
|
|
|
|
const parsed = JSON.parse(configString)
|
|
|
|
Object.assign(this.config, parsed)
|
|
|
|
} catch (err) {
|
|
|
|
console.error('Cannot parse config saved in local storage.', err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-10-09 19:12:40 +02:00
|
|
|
}
|