]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blob - client/src/app/core/server/server.service.ts
Fix languages alphabetical sort
[github/Chocobozzz/PeerTube.git] / client / src / app / core / server / server.service.ts
1 import { Observable, of, Subject } from 'rxjs'
2 import { first, map, share, shareReplay, switchMap, tap } from 'rxjs/operators'
3 import { HttpClient } from '@angular/common/http'
4 import { Inject, Injectable, LOCALE_ID } from '@angular/core'
5 import { getDevLocale, isOnDevLocale, sortBy } from '@app/helpers'
6 import { logger } from '@root-helpers/logger'
7 import { getCompleteLocale, isDefaultLocale, peertubeTranslate } from '@shared/core-utils/i18n'
8 import { HTMLServerConfig, ServerConfig, ServerStats, VideoConstant } from '@shared/models'
9 import { environment } from '../../../environments/environment'
10
11 @Injectable()
12 export class ServerService {
13 private static BASE_CONFIG_URL = environment.apiUrl + '/api/v1/config/'
14 private static BASE_VIDEO_URL = environment.apiUrl + '/api/v1/videos/'
15 private static BASE_VIDEO_PLAYLIST_URL = environment.apiUrl + '/api/v1/video-playlists/'
16 private static BASE_LOCALE_URL = environment.apiUrl + '/client/locales/'
17 private static BASE_STATS_URL = environment.apiUrl + '/api/v1/server/stats'
18
19 configReloaded = new Subject<ServerConfig>()
20
21 private localeObservable: Observable<any>
22 private videoLicensesObservable: Observable<VideoConstant<number>[]>
23 private videoCategoriesObservable: Observable<VideoConstant<number>[]>
24 private videoPrivaciesObservable: Observable<VideoConstant<number>[]>
25 private videoPlaylistPrivaciesObservable: Observable<VideoConstant<number>[]>
26 private videoLanguagesObservable: Observable<VideoConstant<string>[]>
27 private configObservable: Observable<ServerConfig>
28
29 private configReset = false
30
31 private configLoaded = false
32 private config: ServerConfig
33 private htmlConfig: HTMLServerConfig
34
35 constructor (
36 private http: HttpClient,
37 @Inject(LOCALE_ID) private localeId: string
38 ) {
39 }
40
41 loadHTMLConfig () {
42 try {
43 this.loadHTMLConfigLocally()
44 } catch (err) {
45 // Expected in dev mode since we can't inject the config in the HTML
46 if (environment.production !== false) {
47 logger.error('Cannot load config locally. Fallback to API.')
48 }
49
50 return this.getConfig()
51 }
52 }
53
54 getServerVersionAndCommit () {
55 const serverVersion = this.config.serverVersion
56 const commit = this.config.serverCommit || ''
57
58 let result = serverVersion
59 if (commit) result += '...' + commit
60
61 return result
62 }
63
64 resetConfig () {
65 this.configLoaded = false
66 this.configReset = true
67
68 // Notify config update
69 return this.getConfig()
70 }
71
72 getConfig () {
73 if (this.configLoaded) return of(this.config)
74
75 if (!this.configObservable) {
76 this.configObservable = this.http.get<ServerConfig>(ServerService.BASE_CONFIG_URL)
77 .pipe(
78 tap(config => {
79 this.config = config
80 this.htmlConfig = config
81 this.configLoaded = true
82 }),
83 tap(config => {
84 if (this.configReset) {
85 this.configReloaded.next(config)
86 this.configReset = false
87 }
88 }),
89 share()
90 )
91 }
92
93 return this.configObservable
94 }
95
96 getHTMLConfig () {
97 return this.htmlConfig
98 }
99
100 getVideoCategories () {
101 if (!this.videoCategoriesObservable) {
102 this.videoCategoriesObservable = this.loadAttributeEnum<number>(ServerService.BASE_VIDEO_URL, 'categories', true)
103 }
104
105 return this.videoCategoriesObservable.pipe(first())
106 }
107
108 getVideoLicences () {
109 if (!this.videoLicensesObservable) {
110 this.videoLicensesObservable = this.loadAttributeEnum<number>(ServerService.BASE_VIDEO_URL, 'licences')
111 }
112
113 return this.videoLicensesObservable.pipe(first())
114 }
115
116 getVideoLanguages () {
117 if (!this.videoLanguagesObservable) {
118 this.videoLanguagesObservable = this.loadAttributeEnum<string>(ServerService.BASE_VIDEO_URL, 'languages', true)
119 }
120
121 return this.videoLanguagesObservable.pipe(first())
122 }
123
124 getVideoPrivacies () {
125 if (!this.videoPrivaciesObservable) {
126 this.videoPrivaciesObservable = this.loadAttributeEnum<number>(ServerService.BASE_VIDEO_URL, 'privacies')
127 }
128
129 return this.videoPrivaciesObservable.pipe(first())
130 }
131
132 getVideoPlaylistPrivacies () {
133 if (!this.videoPlaylistPrivaciesObservable) {
134 this.videoPlaylistPrivaciesObservable = this.loadAttributeEnum<number>(ServerService.BASE_VIDEO_PLAYLIST_URL, 'privacies')
135 }
136
137 return this.videoPlaylistPrivaciesObservable.pipe(first())
138 }
139
140 getServerLocale (): Observable<{ [ id: string ]: string }> {
141 if (!this.localeObservable) {
142 const completeLocale = isOnDevLocale() ? getDevLocale() : getCompleteLocale(this.localeId)
143
144 // Default locale, nothing to translate
145 if (isDefaultLocale(completeLocale)) {
146 this.localeObservable = of({}).pipe(shareReplay())
147 } else {
148 this.localeObservable = this.http
149 .get(ServerService.BASE_LOCALE_URL + completeLocale + '/server.json')
150 .pipe(shareReplay())
151 }
152 }
153
154 return this.localeObservable.pipe(first())
155 }
156
157 getServerStats () {
158 return this.http.get<ServerStats>(ServerService.BASE_STATS_URL)
159 }
160
161 private loadAttributeEnum <T extends string | number> (
162 baseUrl: string,
163 attributeName: 'categories' | 'licences' | 'languages' | 'privacies',
164 sort = false
165 ) {
166 return this.getServerLocale()
167 .pipe(
168 switchMap(translations => {
169 return this.http.get<{ [ id: string ]: string }>(baseUrl + attributeName)
170 .pipe(map(data => ({ data, translations })))
171 }),
172 map(({ data, translations }) => {
173 const hashToPopulate: VideoConstant<T>[] = Object.keys(data)
174 .map(dataKey => {
175 const label = data[dataKey]
176
177 const id = attributeName === 'languages'
178 ? dataKey as T
179 : parseInt(dataKey, 10) as T
180
181 return {
182 id,
183 label: peertubeTranslate(label, translations)
184 }
185 })
186
187 if (sort === true) {
188 hashToPopulate.sort((a, b) => a.label.localeCompare(b.label))
189 }
190
191 return hashToPopulate
192 }),
193 shareReplay()
194 )
195 }
196
197 private loadHTMLConfigLocally () {
198 // FIXME: typings
199 const configString = (window as any)['PeerTubeServerConfig']
200 if (!configString) {
201 throw new Error('Could not find PeerTubeServerConfig in HTML')
202 }
203
204 this.htmlConfig = JSON.parse(configString)
205 }
206 }