Inject server config in HTML
This commit is contained in:
parent
c76ecc3ff7
commit
aea0b0e7cd
15 changed files with 143 additions and 89 deletions
|
@ -31,7 +31,8 @@ export class VideoTrendingHeaderComponent extends VideoListHeaderComponent imple
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
private router: Router,
|
private router: Router,
|
||||||
private auth: AuthService,
|
private auth: AuthService,
|
||||||
private serverService: ServerService
|
private serverService: ServerService,
|
||||||
|
private redirectService: RedirectService
|
||||||
) {
|
) {
|
||||||
super(data)
|
super(data)
|
||||||
|
|
||||||
|
@ -84,12 +85,7 @@ export class VideoTrendingHeaderComponent extends VideoListHeaderComponent imple
|
||||||
|
|
||||||
this.algorithmChangeSub = this.route.queryParams.subscribe(
|
this.algorithmChangeSub = this.route.queryParams.subscribe(
|
||||||
queryParams => {
|
queryParams => {
|
||||||
const algorithm = queryParams['alg']
|
this.data.model = queryParams['alg'] || this.redirectService.getDefaultTrendingAlgorithm()
|
||||||
if (algorithm) {
|
|
||||||
this.data.model = algorithm
|
|
||||||
} else {
|
|
||||||
this.data.model = RedirectService.DEFAULT_TRENDING_ALGORITHM
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -99,7 +95,7 @@ export class VideoTrendingHeaderComponent extends VideoListHeaderComponent imple
|
||||||
}
|
}
|
||||||
|
|
||||||
setSort () {
|
setSort () {
|
||||||
const alg = this.data.model !== RedirectService.DEFAULT_TRENDING_ALGORITHM
|
const alg = this.data.model !== this.redirectService.getDefaultTrendingAlgorithm()
|
||||||
? this.data.model
|
? this.data.model
|
||||||
: undefined
|
: undefined
|
||||||
|
|
||||||
|
|
|
@ -35,11 +35,12 @@ export class VideoTrendingComponent extends AbstractVideoList implements OnInit,
|
||||||
protected storageService: LocalStorageService,
|
protected storageService: LocalStorageService,
|
||||||
protected cfr: ComponentFactoryResolver,
|
protected cfr: ComponentFactoryResolver,
|
||||||
private videoService: VideoService,
|
private videoService: VideoService,
|
||||||
|
private redirectService: RedirectService,
|
||||||
private hooks: HooksService
|
private hooks: HooksService
|
||||||
) {
|
) {
|
||||||
super()
|
super()
|
||||||
|
|
||||||
this.defaultSort = this.parseAlgorithm(RedirectService.DEFAULT_TRENDING_ALGORITHM)
|
this.defaultSort = this.parseAlgorithm(this.redirectService.getDefaultTrendingAlgorithm())
|
||||||
|
|
||||||
this.headerComponentInjector = this.getInjector()
|
this.headerComponentInjector = this.getInjector()
|
||||||
}
|
}
|
||||||
|
@ -106,7 +107,7 @@ export class VideoTrendingComponent extends AbstractVideoList implements OnInit,
|
||||||
}
|
}
|
||||||
|
|
||||||
protected loadPageRouteParams (queryParams: Params) {
|
protected loadPageRouteParams (queryParams: Params) {
|
||||||
const algorithm = queryParams['alg'] || RedirectService.DEFAULT_TRENDING_ALGORITHM
|
const algorithm = queryParams['alg'] || this.redirectService.getDefaultTrendingAlgorithm()
|
||||||
|
|
||||||
this.sort = this.parseAlgorithm(algorithm)
|
this.sort = this.parseAlgorithm(algorithm)
|
||||||
}
|
}
|
||||||
|
@ -115,8 +116,10 @@ export class VideoTrendingComponent extends AbstractVideoList implements OnInit,
|
||||||
switch (algorithm) {
|
switch (algorithm) {
|
||||||
case 'most-viewed':
|
case 'most-viewed':
|
||||||
return '-trending'
|
return '-trending'
|
||||||
|
|
||||||
case 'most-liked':
|
case 'most-liked':
|
||||||
return '-likes'
|
return '-likes'
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return '-' + algorithm as VideoSortField
|
return '-' + algorithm as VideoSortField
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,7 +67,7 @@ export class AppComponent implements OnInit, AfterViewInit {
|
||||||
}
|
}
|
||||||
|
|
||||||
goToDefaultRoute () {
|
goToDefaultRoute () {
|
||||||
return this.router.navigateByUrl(RedirectService.DEFAULT_ROUTE)
|
return this.router.navigateByUrl(this.redirectService.getDefaultRoute())
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit () {
|
ngOnInit () {
|
||||||
|
|
|
@ -6,14 +6,14 @@ import { ServerService } from '../server'
|
||||||
export class RedirectService {
|
export class RedirectService {
|
||||||
// Default route could change according to the instance configuration
|
// Default route could change according to the instance configuration
|
||||||
static INIT_DEFAULT_ROUTE = '/videos/trending'
|
static INIT_DEFAULT_ROUTE = '/videos/trending'
|
||||||
static DEFAULT_ROUTE = RedirectService.INIT_DEFAULT_ROUTE
|
|
||||||
static INIT_DEFAULT_TRENDING_ALGORITHM = 'most-viewed'
|
static INIT_DEFAULT_TRENDING_ALGORITHM = 'most-viewed'
|
||||||
static DEFAULT_TRENDING_ALGORITHM = RedirectService.INIT_DEFAULT_TRENDING_ALGORITHM
|
|
||||||
|
|
||||||
private previousUrl: string
|
private previousUrl: string
|
||||||
private currentUrl: string
|
private currentUrl: string
|
||||||
|
|
||||||
private redirectingToHomepage = false
|
private redirectingToHomepage = false
|
||||||
|
private defaultTrendingAlgorithm = RedirectService.INIT_DEFAULT_TRENDING_ALGORITHM
|
||||||
|
private defaultRoute = RedirectService.INIT_DEFAULT_ROUTE
|
||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
private router: Router,
|
private router: Router,
|
||||||
|
@ -22,10 +22,10 @@ export class RedirectService {
|
||||||
// The config is first loaded from the cache so try to get the default route
|
// The config is first loaded from the cache so try to get the default route
|
||||||
const tmpConfig = this.serverService.getTmpConfig()
|
const tmpConfig = this.serverService.getTmpConfig()
|
||||||
if (tmpConfig?.instance?.defaultClientRoute) {
|
if (tmpConfig?.instance?.defaultClientRoute) {
|
||||||
RedirectService.DEFAULT_ROUTE = tmpConfig.instance.defaultClientRoute
|
this.defaultRoute = tmpConfig.instance.defaultClientRoute
|
||||||
}
|
}
|
||||||
if (tmpConfig?.trending?.videos?.algorithms?.default) {
|
if (tmpConfig?.trending?.videos?.algorithms?.default) {
|
||||||
RedirectService.DEFAULT_TRENDING_ALGORITHM = tmpConfig.trending.videos.algorithms.default
|
this.defaultTrendingAlgorithm = tmpConfig.trending.videos.algorithms.default
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load default route
|
// Load default route
|
||||||
|
@ -34,13 +34,8 @@ export class RedirectService {
|
||||||
const defaultRouteConfig = config.instance.defaultClientRoute
|
const defaultRouteConfig = config.instance.defaultClientRoute
|
||||||
const defaultTrendingConfig = config.trending.videos.algorithms.default
|
const defaultTrendingConfig = config.trending.videos.algorithms.default
|
||||||
|
|
||||||
if (defaultRouteConfig) {
|
if (defaultRouteConfig) this.defaultRoute = defaultRouteConfig
|
||||||
RedirectService.DEFAULT_ROUTE = defaultRouteConfig
|
if (defaultTrendingConfig) this.defaultTrendingAlgorithm = defaultTrendingConfig
|
||||||
}
|
|
||||||
|
|
||||||
if (defaultTrendingConfig) {
|
|
||||||
RedirectService.DEFAULT_TRENDING_ALGORITHM = defaultTrendingConfig
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// Track previous url
|
// Track previous url
|
||||||
|
@ -53,6 +48,14 @@ export class RedirectService {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getDefaultRoute () {
|
||||||
|
return this.defaultRoute
|
||||||
|
}
|
||||||
|
|
||||||
|
getDefaultTrendingAlgorithm () {
|
||||||
|
return this.defaultTrendingAlgorithm
|
||||||
|
}
|
||||||
|
|
||||||
redirectToPreviousRoute () {
|
redirectToPreviousRoute () {
|
||||||
const exceptions = [
|
const exceptions = [
|
||||||
'/verify-account',
|
'/verify-account',
|
||||||
|
@ -72,21 +75,21 @@ export class RedirectService {
|
||||||
|
|
||||||
this.redirectingToHomepage = true
|
this.redirectingToHomepage = true
|
||||||
|
|
||||||
console.log('Redirecting to %s...', RedirectService.DEFAULT_ROUTE)
|
console.log('Redirecting to %s...', this.defaultRoute)
|
||||||
|
|
||||||
this.router.navigateByUrl(RedirectService.DEFAULT_ROUTE, { skipLocationChange })
|
this.router.navigateByUrl(this.defaultRoute, { skipLocationChange })
|
||||||
.then(() => this.redirectingToHomepage = false)
|
.then(() => this.redirectingToHomepage = false)
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
this.redirectingToHomepage = false
|
this.redirectingToHomepage = false
|
||||||
|
|
||||||
console.error(
|
console.error(
|
||||||
'Cannot navigate to %s, resetting default route to %s.',
|
'Cannot navigate to %s, resetting default route to %s.',
|
||||||
RedirectService.DEFAULT_ROUTE,
|
this.defaultRoute,
|
||||||
RedirectService.INIT_DEFAULT_ROUTE
|
RedirectService.INIT_DEFAULT_ROUTE
|
||||||
)
|
)
|
||||||
|
|
||||||
RedirectService.DEFAULT_ROUTE = RedirectService.INIT_DEFAULT_ROUTE
|
this.defaultRoute = RedirectService.INIT_DEFAULT_ROUTE
|
||||||
return this.router.navigateByUrl(RedirectService.DEFAULT_ROUTE, { skipLocationChange })
|
return this.router.navigateByUrl(this.defaultRoute, { skipLocationChange })
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,6 @@ import { first, map, share, shareReplay, switchMap, tap } from 'rxjs/operators'
|
||||||
import { HttpClient } from '@angular/common/http'
|
import { HttpClient } from '@angular/common/http'
|
||||||
import { Inject, Injectable, LOCALE_ID } from '@angular/core'
|
import { Inject, Injectable, LOCALE_ID } from '@angular/core'
|
||||||
import { getDevLocale, isOnDevLocale, sortBy } from '@app/helpers'
|
import { getDevLocale, isOnDevLocale, sortBy } from '@app/helpers'
|
||||||
import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage'
|
|
||||||
import { getCompleteLocale, isDefaultLocale, peertubeTranslate } from '@shared/core-utils/i18n'
|
import { getCompleteLocale, isDefaultLocale, peertubeTranslate } from '@shared/core-utils/i18n'
|
||||||
import { SearchTargetType, ServerConfig, ServerStats, VideoConstant } from '@shared/models'
|
import { SearchTargetType, ServerConfig, ServerStats, VideoConstant } from '@shared/models'
|
||||||
import { environment } from '../../../environments/environment'
|
import { environment } from '../../../environments/environment'
|
||||||
|
@ -16,8 +15,6 @@ export class ServerService {
|
||||||
private static BASE_LOCALE_URL = environment.apiUrl + '/client/locales/'
|
private static BASE_LOCALE_URL = environment.apiUrl + '/client/locales/'
|
||||||
private static BASE_STATS_URL = environment.apiUrl + '/api/v1/server/stats'
|
private static BASE_STATS_URL = environment.apiUrl + '/api/v1/server/stats'
|
||||||
|
|
||||||
private static CONFIG_LOCAL_STORAGE_KEY = 'server-config'
|
|
||||||
|
|
||||||
configReloaded = new Subject<ServerConfig>()
|
configReloaded = new Subject<ServerConfig>()
|
||||||
|
|
||||||
private localeObservable: Observable<any>
|
private localeObservable: Observable<any>
|
||||||
|
@ -212,7 +209,6 @@ export class ServerService {
|
||||||
if (!this.configObservable) {
|
if (!this.configObservable) {
|
||||||
this.configObservable = this.http.get<ServerConfig>(ServerService.BASE_CONFIG_URL)
|
this.configObservable = this.http.get<ServerConfig>(ServerService.BASE_CONFIG_URL)
|
||||||
.pipe(
|
.pipe(
|
||||||
tap(config => this.saveConfigLocally(config)),
|
|
||||||
tap(config => {
|
tap(config => {
|
||||||
this.config = config
|
this.config = config
|
||||||
this.configLoaded = true
|
this.configLoaded = true
|
||||||
|
@ -343,20 +339,15 @@ export class ServerService {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private saveConfigLocally (config: ServerConfig) {
|
|
||||||
peertubeLocalStorage.setItem(ServerService.CONFIG_LOCAL_STORAGE_KEY, JSON.stringify(config))
|
|
||||||
}
|
|
||||||
|
|
||||||
private loadConfigLocally () {
|
private loadConfigLocally () {
|
||||||
const configString = peertubeLocalStorage.getItem(ServerService.CONFIG_LOCAL_STORAGE_KEY)
|
const configString = window['PeerTubeServerConfig']
|
||||||
|
if (!configString) return
|
||||||
|
|
||||||
if (configString) {
|
|
||||||
try {
|
try {
|
||||||
const parsed = JSON.parse(configString)
|
const parsed = JSON.parse(configString)
|
||||||
Object.assign(this.config, parsed)
|
Object.assign(this.config, parsed)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Cannot parse config saved in local storage.', err)
|
console.error('Cannot parse config saved in from index.html.', err)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
<!-- description tag -->
|
<!-- description tag -->
|
||||||
<!-- custom css tag -->
|
<!-- custom css tag -->
|
||||||
<!-- meta tags -->
|
<!-- meta tags -->
|
||||||
|
<!-- server config -->
|
||||||
|
|
||||||
<!-- /!\ Do not remove it /!\ -->
|
<!-- /!\ Do not remove it /!\ -->
|
||||||
</head>
|
</head>
|
||||||
|
|
|
@ -1,14 +1,22 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<!-- title tag -->
|
|
||||||
|
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<meta name="robots" content="noindex">
|
<meta name="robots" content="noindex">
|
||||||
<meta property="og:platform" content="PeerTube" />
|
<meta property="og:platform" content="PeerTube" />
|
||||||
|
|
||||||
|
|
||||||
|
<!-- /!\ The following comment is used by the server to prerender some tags /!\ -->
|
||||||
|
|
||||||
|
<!-- title tag -->
|
||||||
|
<!-- description tag -->
|
||||||
<!-- custom css tag -->
|
<!-- custom css tag -->
|
||||||
|
<!-- meta tags -->
|
||||||
|
<!-- server config -->
|
||||||
|
|
||||||
|
<!-- /!\ Do not remove it /!\ -->
|
||||||
|
|
||||||
<link rel="icon" type="image/png" href="/client/assets/images/favicon.png" />
|
<link rel="icon" type="image/png" href="/client/assets/images/favicon.png" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
|
|
|
@ -1,28 +1,28 @@
|
||||||
import './embed.scss'
|
import './embed.scss'
|
||||||
import videojs from 'video.js'
|
import videojs from 'video.js'
|
||||||
import { peertubeTranslate } from '../../../../shared/core-utils/i18n'
|
import { peertubeTranslate } from '../../../../shared/core-utils/i18n'
|
||||||
|
import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
|
||||||
import {
|
import {
|
||||||
|
ClientHookName,
|
||||||
|
HTMLServerConfig,
|
||||||
|
PluginType,
|
||||||
ResultList,
|
ResultList,
|
||||||
ServerConfig,
|
|
||||||
UserRefreshToken,
|
UserRefreshToken,
|
||||||
VideoCaption,
|
VideoCaption,
|
||||||
VideoDetails,
|
VideoDetails,
|
||||||
VideoPlaylist,
|
VideoPlaylist,
|
||||||
VideoPlaylistElement,
|
VideoPlaylistElement,
|
||||||
VideoStreamingPlaylistType,
|
VideoStreamingPlaylistType
|
||||||
PluginType,
|
|
||||||
ClientHookName
|
|
||||||
} from '../../../../shared/models'
|
} from '../../../../shared/models'
|
||||||
import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
|
|
||||||
import { P2PMediaLoaderOptions, PeertubePlayerManagerOptions, PlayerMode } from '../../assets/player/peertube-player-manager'
|
import { P2PMediaLoaderOptions, PeertubePlayerManagerOptions, PlayerMode } from '../../assets/player/peertube-player-manager'
|
||||||
import { VideoJSCaption } from '../../assets/player/peertube-videojs-typings'
|
import { VideoJSCaption } from '../../assets/player/peertube-videojs-typings'
|
||||||
import { TranslationsManager } from '../../assets/player/translations-manager'
|
import { TranslationsManager } from '../../assets/player/translations-manager'
|
||||||
|
import { peertubeLocalStorage } from '../../root-helpers/peertube-web-storage'
|
||||||
import { Hooks, loadPlugin, runHook } from '../../root-helpers/plugins'
|
import { Hooks, loadPlugin, runHook } from '../../root-helpers/plugins'
|
||||||
import { Tokens } from '../../root-helpers/users'
|
import { Tokens } from '../../root-helpers/users'
|
||||||
import { peertubeLocalStorage } from '../../root-helpers/peertube-web-storage'
|
|
||||||
import { objectToUrlEncoded } from '../../root-helpers/utils'
|
import { objectToUrlEncoded } from '../../root-helpers/utils'
|
||||||
import { PeerTubeEmbedApi } from './embed-api'
|
|
||||||
import { RegisterClientHelpers } from '../../types/register-client-option.model'
|
import { RegisterClientHelpers } from '../../types/register-client-option.model'
|
||||||
|
import { PeerTubeEmbedApi } from './embed-api'
|
||||||
|
|
||||||
type Translations = { [ id: string ]: string }
|
type Translations = { [ id: string ]: string }
|
||||||
|
|
||||||
|
@ -56,8 +56,9 @@ export class PeerTubeEmbed {
|
||||||
CLIENT_SECRET: 'client_secret'
|
CLIENT_SECRET: 'client_secret'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
config: HTMLServerConfig
|
||||||
|
|
||||||
private translationsPromise: Promise<{ [id: string]: string }>
|
private translationsPromise: Promise<{ [id: string]: string }>
|
||||||
private configPromise: Promise<ServerConfig>
|
|
||||||
private PeertubePlayerManagerModulePromise: Promise<any>
|
private PeertubePlayerManagerModulePromise: Promise<any>
|
||||||
|
|
||||||
private playlist: VideoPlaylist
|
private playlist: VideoPlaylist
|
||||||
|
@ -77,6 +78,12 @@ export class PeerTubeEmbed {
|
||||||
|
|
||||||
constructor (private videoWrapperId: string) {
|
constructor (private videoWrapperId: string) {
|
||||||
this.wrapperElement = document.getElementById(this.videoWrapperId)
|
this.wrapperElement = document.getElementById(this.videoWrapperId)
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.config = JSON.parse(window['PeerTubeServerConfig'])
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Cannot parse HTML config.', err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getVideoUrl (id: string) {
|
getVideoUrl (id: string) {
|
||||||
|
@ -166,11 +173,6 @@ export class PeerTubeEmbed {
|
||||||
return this.refreshFetch(url.toString(), { headers: this.headers })
|
return this.refreshFetch(url.toString(), { headers: this.headers })
|
||||||
}
|
}
|
||||||
|
|
||||||
loadConfig (): Promise<ServerConfig> {
|
|
||||||
return this.refreshFetch('/api/v1/config')
|
|
||||||
.then(res => res.json())
|
|
||||||
}
|
|
||||||
|
|
||||||
removeElement (element: HTMLElement) {
|
removeElement (element: HTMLElement) {
|
||||||
element.parentElement.removeChild(element)
|
element.parentElement.removeChild(element)
|
||||||
}
|
}
|
||||||
|
@ -466,6 +468,12 @@ export class PeerTubeEmbed {
|
||||||
this.playerElement.setAttribute('playsinline', 'true')
|
this.playerElement.setAttribute('playsinline', 'true')
|
||||||
this.wrapperElement.appendChild(this.playerElement)
|
this.wrapperElement.appendChild(this.playerElement)
|
||||||
|
|
||||||
|
// Issue when we parsed config from HTML, fallback to API
|
||||||
|
if (!this.config) {
|
||||||
|
this.config = await this.refreshFetch('/api/v1/config')
|
||||||
|
.then(res => res.json())
|
||||||
|
}
|
||||||
|
|
||||||
const videoInfoPromise = videoResponse.json()
|
const videoInfoPromise = videoResponse.json()
|
||||||
.then((videoInfo: VideoDetails) => {
|
.then((videoInfo: VideoDetails) => {
|
||||||
if (!alreadyHadPlayer) this.loadPlaceholder(videoInfo)
|
if (!alreadyHadPlayer) this.loadPlaceholder(videoInfo)
|
||||||
|
@ -473,15 +481,14 @@ export class PeerTubeEmbed {
|
||||||
return videoInfo
|
return videoInfo
|
||||||
})
|
})
|
||||||
|
|
||||||
const [ videoInfoTmp, serverTranslations, captionsResponse, config, PeertubePlayerManagerModule ] = await Promise.all([
|
const [ videoInfoTmp, serverTranslations, captionsResponse, PeertubePlayerManagerModule ] = await Promise.all([
|
||||||
videoInfoPromise,
|
videoInfoPromise,
|
||||||
this.translationsPromise,
|
this.translationsPromise,
|
||||||
captionsPromise,
|
captionsPromise,
|
||||||
this.configPromise,
|
|
||||||
this.PeertubePlayerManagerModulePromise
|
this.PeertubePlayerManagerModulePromise
|
||||||
])
|
])
|
||||||
|
|
||||||
await this.ensurePluginsAreLoaded(config, serverTranslations)
|
await this.ensurePluginsAreLoaded(serverTranslations)
|
||||||
|
|
||||||
const videoInfo: VideoDetails = videoInfoTmp
|
const videoInfo: VideoDetails = videoInfoTmp
|
||||||
|
|
||||||
|
@ -576,7 +583,7 @@ export class PeerTubeEmbed {
|
||||||
|
|
||||||
this.buildCSS()
|
this.buildCSS()
|
||||||
|
|
||||||
await this.buildDock(videoInfo, config)
|
await this.buildDock(videoInfo)
|
||||||
|
|
||||||
this.initializeApi()
|
this.initializeApi()
|
||||||
|
|
||||||
|
@ -598,7 +605,6 @@ export class PeerTubeEmbed {
|
||||||
private async initCore () {
|
private async initCore () {
|
||||||
if (this.userTokens) this.setHeadersFromTokens()
|
if (this.userTokens) this.setHeadersFromTokens()
|
||||||
|
|
||||||
this.configPromise = this.loadConfig()
|
|
||||||
this.translationsPromise = TranslationsManager.getServerTranslations(window.location.origin, navigator.language)
|
this.translationsPromise = TranslationsManager.getServerTranslations(window.location.origin, navigator.language)
|
||||||
this.PeertubePlayerManagerModulePromise = import('../../assets/player/peertube-player-manager')
|
this.PeertubePlayerManagerModulePromise = import('../../assets/player/peertube-player-manager')
|
||||||
|
|
||||||
|
@ -653,7 +659,7 @@ export class PeerTubeEmbed {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async buildDock (videoInfo: VideoDetails, config: ServerConfig) {
|
private async buildDock (videoInfo: VideoDetails) {
|
||||||
if (!this.controls) return
|
if (!this.controls) return
|
||||||
|
|
||||||
// On webtorrent fallback, player may have been disposed
|
// On webtorrent fallback, player may have been disposed
|
||||||
|
@ -661,7 +667,7 @@ export class PeerTubeEmbed {
|
||||||
|
|
||||||
const title = this.title ? videoInfo.name : undefined
|
const title = this.title ? videoInfo.name : undefined
|
||||||
|
|
||||||
const description = config.tracker.enabled && this.warningTitle
|
const description = this.config.tracker.enabled && this.warningTitle
|
||||||
? '<span class="text">' + peertubeTranslate('Watching this video may reveal your IP address to others.') + '</span>'
|
? '<span class="text">' + peertubeTranslate('Watching this video may reveal your IP address to others.') + '</span>'
|
||||||
: undefined
|
: undefined
|
||||||
|
|
||||||
|
@ -733,10 +739,10 @@ export class PeerTubeEmbed {
|
||||||
return window.location.pathname.split('/')[1] === 'video-playlists'
|
return window.location.pathname.split('/')[1] === 'video-playlists'
|
||||||
}
|
}
|
||||||
|
|
||||||
private async ensurePluginsAreLoaded (config: ServerConfig, translations?: { [ id: string ]: string }) {
|
private async ensurePluginsAreLoaded (translations?: { [ id: string ]: string }) {
|
||||||
if (config.plugin.registered.length === 0) return
|
if (this.config.plugin.registered.length === 0) return
|
||||||
|
|
||||||
for (const plugin of config.plugin.registered) {
|
for (const plugin of this.config.plugin.registered) {
|
||||||
for (const key of Object.keys(plugin.clientScripts)) {
|
for (const key of Object.keys(plugin.clientScripts)) {
|
||||||
const clientScript = plugin.clientScripts[key]
|
const clientScript = plugin.clientScripts[key]
|
||||||
|
|
||||||
|
|
|
@ -702,7 +702,8 @@ const CUSTOM_HTML_TAG_COMMENTS = {
|
||||||
TITLE: '<!-- title tag -->',
|
TITLE: '<!-- title tag -->',
|
||||||
DESCRIPTION: '<!-- description tag -->',
|
DESCRIPTION: '<!-- description tag -->',
|
||||||
CUSTOM_CSS: '<!-- custom css tag -->',
|
CUSTOM_CSS: '<!-- custom css tag -->',
|
||||||
META_TAGS: '<!-- meta tags -->'
|
META_TAGS: '<!-- meta tags -->',
|
||||||
|
SERVER_CONFIG: '<!-- server config -->'
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
|
@ -2,12 +2,14 @@ import * as express from 'express'
|
||||||
import { readFile } from 'fs-extra'
|
import { readFile } from 'fs-extra'
|
||||||
import { join } from 'path'
|
import { join } from 'path'
|
||||||
import validator from 'validator'
|
import validator from 'validator'
|
||||||
|
import { escapeHTML } from '@shared/core-utils/renderer'
|
||||||
|
import { HTMLServerConfig } from '@shared/models'
|
||||||
import { buildFileLocale, getDefaultLocale, is18nLocale, POSSIBLE_LOCALES } from '../../shared/core-utils/i18n/i18n'
|
import { buildFileLocale, getDefaultLocale, is18nLocale, POSSIBLE_LOCALES } from '../../shared/core-utils/i18n/i18n'
|
||||||
import { HttpStatusCode } from '../../shared/core-utils/miscs/http-error-codes'
|
import { HttpStatusCode } from '../../shared/core-utils/miscs/http-error-codes'
|
||||||
import { VideoPlaylistPrivacy, VideoPrivacy } from '../../shared/models/videos'
|
import { VideoPlaylistPrivacy, VideoPrivacy } from '../../shared/models/videos'
|
||||||
import { isTestInstance, sha256 } from '../helpers/core-utils'
|
import { isTestInstance, sha256 } from '../helpers/core-utils'
|
||||||
import { escapeHTML } from '@shared/core-utils/renderer'
|
|
||||||
import { logger } from '../helpers/logger'
|
import { logger } from '../helpers/logger'
|
||||||
|
import { mdToPlainText } from '../helpers/markdown'
|
||||||
import { CONFIG } from '../initializers/config'
|
import { CONFIG } from '../initializers/config'
|
||||||
import {
|
import {
|
||||||
ACCEPT_HEADERS,
|
ACCEPT_HEADERS,
|
||||||
|
@ -24,7 +26,7 @@ import { VideoChannelModel } from '../models/video/video-channel'
|
||||||
import { getActivityStreamDuration } from '../models/video/video-format-utils'
|
import { getActivityStreamDuration } from '../models/video/video-format-utils'
|
||||||
import { VideoPlaylistModel } from '../models/video/video-playlist'
|
import { VideoPlaylistModel } from '../models/video/video-playlist'
|
||||||
import { MAccountActor, MChannelActor } from '../types/models'
|
import { MAccountActor, MChannelActor } from '../types/models'
|
||||||
import { mdToPlainText } from '../helpers/markdown'
|
import { getHTMLServerConfig } from './config'
|
||||||
|
|
||||||
type Tags = {
|
type Tags = {
|
||||||
ogType: string
|
ogType: string
|
||||||
|
@ -209,11 +211,14 @@ class ClientHtml {
|
||||||
if (!isTestInstance() && ClientHtml.htmlCache[path]) return ClientHtml.htmlCache[path]
|
if (!isTestInstance() && ClientHtml.htmlCache[path]) return ClientHtml.htmlCache[path]
|
||||||
|
|
||||||
const buffer = await readFile(path)
|
const buffer = await readFile(path)
|
||||||
|
const serverConfig = await getHTMLServerConfig()
|
||||||
|
|
||||||
let html = buffer.toString()
|
let html = buffer.toString()
|
||||||
html = await ClientHtml.addAsyncPluginCSS(html)
|
html = await ClientHtml.addAsyncPluginCSS(html)
|
||||||
html = ClientHtml.addCustomCSS(html)
|
html = ClientHtml.addCustomCSS(html)
|
||||||
html = ClientHtml.addTitleTag(html)
|
html = ClientHtml.addTitleTag(html)
|
||||||
|
html = ClientHtml.addDescriptionTag(html)
|
||||||
|
html = ClientHtml.addServerConfig(html, serverConfig)
|
||||||
|
|
||||||
ClientHtml.htmlCache[path] = html
|
ClientHtml.htmlCache[path] = html
|
||||||
|
|
||||||
|
@ -275,6 +280,7 @@ class ClientHtml {
|
||||||
if (!isTestInstance() && ClientHtml.htmlCache[path]) return ClientHtml.htmlCache[path]
|
if (!isTestInstance() && ClientHtml.htmlCache[path]) return ClientHtml.htmlCache[path]
|
||||||
|
|
||||||
const buffer = await readFile(path)
|
const buffer = await readFile(path)
|
||||||
|
const serverConfig = await getHTMLServerConfig()
|
||||||
|
|
||||||
let html = buffer.toString()
|
let html = buffer.toString()
|
||||||
|
|
||||||
|
@ -283,6 +289,7 @@ class ClientHtml {
|
||||||
html = ClientHtml.addFaviconContentHash(html)
|
html = ClientHtml.addFaviconContentHash(html)
|
||||||
html = ClientHtml.addLogoContentHash(html)
|
html = ClientHtml.addLogoContentHash(html)
|
||||||
html = ClientHtml.addCustomCSS(html)
|
html = ClientHtml.addCustomCSS(html)
|
||||||
|
html = ClientHtml.addServerConfig(html, serverConfig)
|
||||||
html = await ClientHtml.addAsyncPluginCSS(html)
|
html = await ClientHtml.addAsyncPluginCSS(html)
|
||||||
|
|
||||||
ClientHtml.htmlCache[path] = html
|
ClientHtml.htmlCache[path] = html
|
||||||
|
@ -355,6 +362,13 @@ class ClientHtml {
|
||||||
return htmlStringPage.replace(CUSTOM_HTML_TAG_COMMENTS.CUSTOM_CSS, styleTag)
|
return htmlStringPage.replace(CUSTOM_HTML_TAG_COMMENTS.CUSTOM_CSS, styleTag)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static addServerConfig (htmlStringPage: string, serverConfig: HTMLServerConfig) {
|
||||||
|
const serverConfigString = JSON.stringify(serverConfig)
|
||||||
|
const configScriptTag = `<script type="application/javascript">window.PeerTubeServerConfig = '${serverConfigString}'</script>`
|
||||||
|
|
||||||
|
return htmlStringPage.replace(CUSTOM_HTML_TAG_COMMENTS.SERVER_CONFIG, configScriptTag)
|
||||||
|
}
|
||||||
|
|
||||||
private static async addAsyncPluginCSS (htmlStringPage: string) {
|
private static async addAsyncPluginCSS (htmlStringPage: string) {
|
||||||
const globalCSSContent = await readFile(PLUGIN_GLOBAL_CSS_PATH)
|
const globalCSSContent = await readFile(PLUGIN_GLOBAL_CSS_PATH)
|
||||||
if (globalCSSContent.byteLength === 0) return htmlStringPage
|
if (globalCSSContent.byteLength === 0) return htmlStringPage
|
||||||
|
|
|
@ -2,17 +2,13 @@ import { isSignupAllowed, isSignupAllowedForCurrentIP } from '@server/helpers/si
|
||||||
import { getServerCommit } from '@server/helpers/utils'
|
import { getServerCommit } from '@server/helpers/utils'
|
||||||
import { CONFIG, isEmailEnabled } from '@server/initializers/config'
|
import { CONFIG, isEmailEnabled } from '@server/initializers/config'
|
||||||
import { CONSTRAINTS_FIELDS, DEFAULT_THEME_NAME, PEERTUBE_VERSION } from '@server/initializers/constants'
|
import { CONSTRAINTS_FIELDS, DEFAULT_THEME_NAME, PEERTUBE_VERSION } from '@server/initializers/constants'
|
||||||
import { RegisteredExternalAuthConfig, RegisteredIdAndPassAuthConfig, ServerConfig } from '@shared/models'
|
import { HTMLServerConfig, RegisteredExternalAuthConfig, RegisteredIdAndPassAuthConfig, ServerConfig } from '@shared/models'
|
||||||
import { Hooks } from './plugins/hooks'
|
import { Hooks } from './plugins/hooks'
|
||||||
import { PluginManager } from './plugins/plugin-manager'
|
import { PluginManager } from './plugins/plugin-manager'
|
||||||
import { getThemeOrDefault } from './plugins/theme-utils'
|
import { getThemeOrDefault } from './plugins/theme-utils'
|
||||||
import { VideoTranscodingProfilesManager } from './transcoding/video-transcoding-profiles'
|
import { VideoTranscodingProfilesManager } from './transcoding/video-transcoding-profiles'
|
||||||
|
|
||||||
let serverCommit: string
|
|
||||||
|
|
||||||
async function getServerConfig (ip?: string): Promise<ServerConfig> {
|
async function getServerConfig (ip?: string): Promise<ServerConfig> {
|
||||||
if (serverCommit === undefined) serverCommit = await getServerCommit()
|
|
||||||
|
|
||||||
const { allowed } = await Hooks.wrapPromiseFun(
|
const { allowed } = await Hooks.wrapPromiseFun(
|
||||||
isSignupAllowed,
|
isSignupAllowed,
|
||||||
{
|
{
|
||||||
|
@ -22,6 +18,23 @@ async function getServerConfig (ip?: string): Promise<ServerConfig> {
|
||||||
)
|
)
|
||||||
|
|
||||||
const allowedForCurrentIP = isSignupAllowedForCurrentIP(ip)
|
const allowedForCurrentIP = isSignupAllowedForCurrentIP(ip)
|
||||||
|
|
||||||
|
const signup = {
|
||||||
|
allowed,
|
||||||
|
allowedForCurrentIP,
|
||||||
|
requiresEmailVerification: CONFIG.SIGNUP.REQUIRES_EMAIL_VERIFICATION
|
||||||
|
}
|
||||||
|
|
||||||
|
const htmlConfig = await getHTMLServerConfig()
|
||||||
|
|
||||||
|
return { ...htmlConfig, signup }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Config injected in HTML
|
||||||
|
let serverCommit: string
|
||||||
|
async function getHTMLServerConfig (): Promise<HTMLServerConfig> {
|
||||||
|
if (serverCommit === undefined) serverCommit = await getServerCommit()
|
||||||
|
|
||||||
const defaultTheme = getThemeOrDefault(CONFIG.THEME.DEFAULT, DEFAULT_THEME_NAME)
|
const defaultTheme = getThemeOrDefault(CONFIG.THEME.DEFAULT, DEFAULT_THEME_NAME)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -65,11 +78,6 @@ async function getServerConfig (ip?: string): Promise<ServerConfig> {
|
||||||
},
|
},
|
||||||
serverVersion: PEERTUBE_VERSION,
|
serverVersion: PEERTUBE_VERSION,
|
||||||
serverCommit,
|
serverCommit,
|
||||||
signup: {
|
|
||||||
allowed,
|
|
||||||
allowedForCurrentIP,
|
|
||||||
requiresEmailVerification: CONFIG.SIGNUP.REQUIRES_EMAIL_VERIFICATION
|
|
||||||
},
|
|
||||||
transcoding: {
|
transcoding: {
|
||||||
hls: {
|
hls: {
|
||||||
enabled: CONFIG.TRANSCODING.HLS.ENABLED
|
enabled: CONFIG.TRANSCODING.HLS.ENABLED
|
||||||
|
@ -223,7 +231,8 @@ export {
|
||||||
getServerConfig,
|
getServerConfig,
|
||||||
getRegisteredThemes,
|
getRegisteredThemes,
|
||||||
getEnabledResolutions,
|
getEnabledResolutions,
|
||||||
getRegisteredPlugins
|
getRegisteredPlugins,
|
||||||
|
getHTMLServerConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
|
@ -16,8 +16,7 @@ import {
|
||||||
UpdatedAt
|
UpdatedAt
|
||||||
} from 'sequelize-typescript'
|
} from 'sequelize-typescript'
|
||||||
import { isAbuseModerationCommentValid, isAbuseReasonValid, isAbuseStateValid } from '@server/helpers/custom-validators/abuses'
|
import { isAbuseModerationCommentValid, isAbuseReasonValid, isAbuseStateValid } from '@server/helpers/custom-validators/abuses'
|
||||||
import { AttributesOnly } from '@shared/core-utils'
|
import { abusePredefinedReasonsMap, AttributesOnly } from '@shared/core-utils'
|
||||||
import { abusePredefinedReasonsMap } from '@shared/core-utils/abuse'
|
|
||||||
import {
|
import {
|
||||||
AbuseFilter,
|
AbuseFilter,
|
||||||
AbuseObject,
|
AbuseObject,
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
import 'mocha'
|
import 'mocha'
|
||||||
import * as chai from 'chai'
|
import * as chai from 'chai'
|
||||||
import * as request from 'supertest'
|
import * as request from 'supertest'
|
||||||
import { Account, VideoPlaylistPrivacy } from '@shared/models'
|
import { Account, HTMLServerConfig, ServerConfig, VideoPlaylistPrivacy } from '@shared/models'
|
||||||
import {
|
import {
|
||||||
addVideoInPlaylist,
|
addVideoInPlaylist,
|
||||||
cleanupTests,
|
cleanupTests,
|
||||||
|
@ -11,6 +11,7 @@ import {
|
||||||
doubleFollow,
|
doubleFollow,
|
||||||
flushAndRunMultipleServers,
|
flushAndRunMultipleServers,
|
||||||
getAccount,
|
getAccount,
|
||||||
|
getConfig,
|
||||||
getCustomConfig,
|
getCustomConfig,
|
||||||
getVideosList,
|
getVideosList,
|
||||||
makeHTMLRequest,
|
makeHTMLRequest,
|
||||||
|
@ -25,13 +26,17 @@ import {
|
||||||
waitJobs
|
waitJobs
|
||||||
} from '../../shared/extra-utils'
|
} from '../../shared/extra-utils'
|
||||||
import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes'
|
import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes'
|
||||||
|
import { omit } from 'lodash'
|
||||||
|
|
||||||
const expect = chai.expect
|
const expect = chai.expect
|
||||||
|
|
||||||
function checkIndexTags (html: string, title: string, description: string, css: string) {
|
function checkIndexTags (html: string, title: string, description: string, css: string, config: ServerConfig) {
|
||||||
expect(html).to.contain('<title>' + title + '</title>')
|
expect(html).to.contain('<title>' + title + '</title>')
|
||||||
expect(html).to.contain('<meta name="description" content="' + description + '" />')
|
expect(html).to.contain('<meta name="description" content="' + description + '" />')
|
||||||
expect(html).to.contain('<style class="custom-css-style">' + css + '</style>')
|
expect(html).to.contain('<style class="custom-css-style">' + css + '</style>')
|
||||||
|
|
||||||
|
const htmlConfig: HTMLServerConfig = omit(config, 'signup')
|
||||||
|
expect(html).to.contain(`<script type="application/javascript">window.PeerTubeServerConfig = '${JSON.stringify(htmlConfig)}'</script>`)
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('Test a client controllers', function () {
|
describe('Test a client controllers', function () {
|
||||||
|
@ -296,10 +301,11 @@ describe('Test a client controllers', function () {
|
||||||
describe('Index HTML', function () {
|
describe('Index HTML', function () {
|
||||||
|
|
||||||
it('Should have valid index html tags (title, description...)', async function () {
|
it('Should have valid index html tags (title, description...)', async function () {
|
||||||
|
const resConfig = await getConfig(servers[0].url)
|
||||||
const res = await makeHTMLRequest(servers[0].url, '/videos/trending')
|
const res = await makeHTMLRequest(servers[0].url, '/videos/trending')
|
||||||
|
|
||||||
const description = 'PeerTube, an ActivityPub-federated video streaming platform using P2P directly in your web browser.'
|
const description = 'PeerTube, an ActivityPub-federated video streaming platform using P2P directly in your web browser.'
|
||||||
checkIndexTags(res.text, 'PeerTube', description, '')
|
checkIndexTags(res.text, 'PeerTube', description, '', resConfig.body)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should update the customized configuration and have the correct index html tags', async function () {
|
it('Should update the customized configuration and have the correct index html tags', async function () {
|
||||||
|
@ -318,15 +324,17 @@ describe('Test a client controllers', function () {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const resConfig = await getConfig(servers[0].url)
|
||||||
const res = await makeHTMLRequest(servers[0].url, '/videos/trending')
|
const res = await makeHTMLRequest(servers[0].url, '/videos/trending')
|
||||||
|
|
||||||
checkIndexTags(res.text, 'PeerTube updated', 'my short description', 'body { background-color: red; }')
|
checkIndexTags(res.text, 'PeerTube updated', 'my short description', 'body { background-color: red; }', resConfig.body)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should have valid index html updated tags (title, description...)', async function () {
|
it('Should have valid index html updated tags (title, description...)', async function () {
|
||||||
|
const resConfig = await getConfig(servers[0].url)
|
||||||
const res = await makeHTMLRequest(servers[0].url, '/videos/trending')
|
const res = await makeHTMLRequest(servers[0].url, '/videos/trending')
|
||||||
|
|
||||||
checkIndexTags(res.text, 'PeerTube updated', 'my short description', 'body { background-color: red; }')
|
checkIndexTags(res.text, 'PeerTube updated', 'my short description', 'body { background-color: red; }', resConfig.body)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should use the original video URL for the canonical tag', async function () {
|
it('Should use the original video URL for the canonical tag', async function () {
|
||||||
|
@ -350,6 +358,16 @@ describe('Test a client controllers', function () {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('Embed HTML', function () {
|
||||||
|
|
||||||
|
it('Should have the correct embed html tags', async function () {
|
||||||
|
const resConfig = await getConfig(servers[0].url)
|
||||||
|
const res = await makeHTMLRequest(servers[0].url, servers[0].video.embedPath)
|
||||||
|
|
||||||
|
checkIndexTags(res.text, 'PeerTube updated', 'my short description', 'body { background-color: red; }', resConfig.body)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
after(async function () {
|
after(async function () {
|
||||||
await cleanupTests(servers)
|
await cleanupTests(servers)
|
||||||
})
|
})
|
||||||
|
|
|
@ -45,9 +45,12 @@ interface ServerInfo {
|
||||||
uuid: string
|
uuid: string
|
||||||
name?: string
|
name?: string
|
||||||
url?: string
|
url?: string
|
||||||
|
|
||||||
account?: {
|
account?: {
|
||||||
name: string
|
name: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
embedPath?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
remoteVideo?: {
|
remoteVideo?: {
|
||||||
|
|
|
@ -215,3 +215,5 @@ export interface ServerConfig {
|
||||||
dismissable: boolean
|
dismissable: boolean
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type HTMLServerConfig = Omit<ServerConfig, 'signup'>
|
||||||
|
|
Loading…
Reference in a new issue