server.service.ts 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  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 { peertubeLocalStorage } from '@root-helpers/peertube-web-storage'
  7. import {
  8. getCompleteLocale,
  9. isDefaultLocale,
  10. peertubeTranslate,
  11. SearchTargetType,
  12. ServerConfig,
  13. ServerStats,
  14. VideoConstant
  15. } from '@shared/models'
  16. import { environment } from '../../../environments/environment'
  17. @Injectable()
  18. export class ServerService {
  19. private static BASE_CONFIG_URL = environment.apiUrl + '/api/v1/config/'
  20. private static BASE_VIDEO_URL = environment.apiUrl + '/api/v1/videos/'
  21. private static BASE_VIDEO_PLAYLIST_URL = environment.apiUrl + '/api/v1/video-playlists/'
  22. private static BASE_LOCALE_URL = environment.apiUrl + '/client/locales/'
  23. private static BASE_STATS_URL = environment.apiUrl + '/api/v1/server/stats'
  24. private static CONFIG_LOCAL_STORAGE_KEY = 'server-config'
  25. configReloaded = new Subject<ServerConfig>()
  26. private localeObservable: Observable<any>
  27. private videoLicensesObservable: Observable<VideoConstant<number>[]>
  28. private videoCategoriesObservable: Observable<VideoConstant<number>[]>
  29. private videoPrivaciesObservable: Observable<VideoConstant<number>[]>
  30. private videoPlaylistPrivaciesObservable: Observable<VideoConstant<number>[]>
  31. private videoLanguagesObservable: Observable<VideoConstant<string>[]>
  32. private configObservable: Observable<ServerConfig>
  33. private configReset = false
  34. private configLoaded = false
  35. private config: ServerConfig = {
  36. instance: {
  37. name: 'PeerTube',
  38. shortDescription: 'PeerTube, a federated (ActivityPub) video streaming platform ' +
  39. 'using P2P (BitTorrent) directly in the web browser with WebTorrent and Angular.',
  40. defaultClientRoute: '',
  41. isNSFW: false,
  42. defaultNSFWPolicy: 'do_not_list' as 'do_not_list',
  43. customizations: {
  44. javascript: '',
  45. css: ''
  46. }
  47. },
  48. plugin: {
  49. registered: [],
  50. registeredExternalAuths: [],
  51. registeredIdAndPassAuths: []
  52. },
  53. theme: {
  54. registered: [],
  55. default: 'default'
  56. },
  57. email: {
  58. enabled: false
  59. },
  60. contactForm: {
  61. enabled: false
  62. },
  63. serverVersion: 'Unknown',
  64. signup: {
  65. allowed: false,
  66. allowedForCurrentIP: false,
  67. requiresEmailVerification: false
  68. },
  69. transcoding: {
  70. enabledResolutions: [],
  71. hls: {
  72. enabled: false
  73. },
  74. webtorrent: {
  75. enabled: true
  76. }
  77. },
  78. avatar: {
  79. file: {
  80. size: { max: 0 },
  81. extensions: []
  82. }
  83. },
  84. video: {
  85. image: {
  86. size: { max: 0 },
  87. extensions: []
  88. },
  89. file: {
  90. extensions: []
  91. }
  92. },
  93. videoCaption: {
  94. file: {
  95. size: { max: 0 },
  96. extensions: []
  97. }
  98. },
  99. user: {
  100. videoQuota: -1,
  101. videoQuotaDaily: -1
  102. },
  103. import: {
  104. videos: {
  105. http: {
  106. enabled: false
  107. },
  108. torrent: {
  109. enabled: false
  110. }
  111. }
  112. },
  113. trending: {
  114. videos: {
  115. intervalDays: 0
  116. }
  117. },
  118. autoBlacklist: {
  119. videos: {
  120. ofUsers: {
  121. enabled: false
  122. }
  123. }
  124. },
  125. tracker: {
  126. enabled: true
  127. },
  128. followings: {
  129. instance: {
  130. autoFollowIndex: {
  131. indexUrl: 'https://instances.joinpeertube.org'
  132. }
  133. }
  134. },
  135. broadcastMessage: {
  136. enabled: false,
  137. message: '',
  138. level: 'info',
  139. dismissable: false
  140. },
  141. search: {
  142. remoteUri: {
  143. users: true,
  144. anonymous: false
  145. },
  146. searchIndex: {
  147. enabled: false,
  148. url: '',
  149. disableLocalSearch: false,
  150. isDefaultSearch: false
  151. }
  152. }
  153. }
  154. constructor (
  155. private http: HttpClient,
  156. @Inject(LOCALE_ID) private localeId: string
  157. ) {
  158. this.loadConfigLocally()
  159. }
  160. getServerVersionAndCommit () {
  161. const serverVersion = this.config.serverVersion
  162. const commit = this.config.serverCommit || ''
  163. let result = serverVersion
  164. if (commit) result += '...' + commit
  165. return result
  166. }
  167. resetConfig () {
  168. this.configLoaded = false
  169. this.configReset = true
  170. // Notify config update
  171. this.getConfig().subscribe(() => {
  172. // empty, to fire a reset config event
  173. })
  174. }
  175. getConfig () {
  176. if (this.configLoaded) return of(this.config)
  177. if (!this.configObservable) {
  178. this.configObservable = this.http.get<ServerConfig>(ServerService.BASE_CONFIG_URL)
  179. .pipe(
  180. tap(config => this.saveConfigLocally(config)),
  181. tap(config => {
  182. this.config = config
  183. this.configLoaded = true
  184. }),
  185. tap(config => {
  186. if (this.configReset) {
  187. this.configReloaded.next(config)
  188. this.configReset = false
  189. }
  190. }),
  191. share()
  192. )
  193. }
  194. return this.configObservable
  195. }
  196. getTmpConfig () {
  197. return this.config
  198. }
  199. getVideoCategories () {
  200. if (!this.videoCategoriesObservable) {
  201. this.videoCategoriesObservable = this.loadAttributeEnum<number>(ServerService.BASE_VIDEO_URL, 'categories', true)
  202. }
  203. return this.videoCategoriesObservable.pipe(first())
  204. }
  205. getVideoLicences () {
  206. if (!this.videoLicensesObservable) {
  207. this.videoLicensesObservable = this.loadAttributeEnum<number>(ServerService.BASE_VIDEO_URL, 'licences')
  208. }
  209. return this.videoLicensesObservable.pipe(first())
  210. }
  211. getVideoLanguages () {
  212. if (!this.videoLanguagesObservable) {
  213. this.videoLanguagesObservable = this.loadAttributeEnum<string>(ServerService.BASE_VIDEO_URL, 'languages', true)
  214. }
  215. return this.videoLanguagesObservable.pipe(first())
  216. }
  217. getVideoPrivacies () {
  218. if (!this.videoPrivaciesObservable) {
  219. this.videoPrivaciesObservable = this.loadAttributeEnum<number>(ServerService.BASE_VIDEO_URL, 'privacies')
  220. }
  221. return this.videoPrivaciesObservable.pipe(first())
  222. }
  223. getVideoPlaylistPrivacies () {
  224. if (!this.videoPlaylistPrivaciesObservable) {
  225. this.videoPlaylistPrivaciesObservable = this.loadAttributeEnum<number>(ServerService.BASE_VIDEO_PLAYLIST_URL, 'privacies')
  226. }
  227. return this.videoPlaylistPrivaciesObservable.pipe(first())
  228. }
  229. getServerLocale () {
  230. if (!this.localeObservable) {
  231. const completeLocale = isOnDevLocale() ? getDevLocale() : getCompleteLocale(this.localeId)
  232. // Default locale, nothing to translate
  233. if (isDefaultLocale(completeLocale)) {
  234. this.localeObservable = of({}).pipe(shareReplay())
  235. } else {
  236. this.localeObservable = this.http
  237. .get(ServerService.BASE_LOCALE_URL + completeLocale + '/server.json')
  238. .pipe(shareReplay())
  239. }
  240. }
  241. return this.localeObservable.pipe(first())
  242. }
  243. getServerStats () {
  244. return this.http.get<ServerStats>(ServerService.BASE_STATS_URL)
  245. }
  246. getDefaultSearchTarget (): Promise<SearchTargetType> {
  247. return this.getConfig().pipe(
  248. map(config => {
  249. const searchIndexConfig = config.search.searchIndex
  250. if (searchIndexConfig.enabled && (searchIndexConfig.isDefaultSearch || searchIndexConfig.disableLocalSearch)) {
  251. return 'search-index'
  252. }
  253. return 'local'
  254. })
  255. ).toPromise()
  256. }
  257. private loadAttributeEnum <T extends string | number> (
  258. baseUrl: string,
  259. attributeName: 'categories' | 'licences' | 'languages' | 'privacies',
  260. sort = false
  261. ) {
  262. return this.getServerLocale()
  263. .pipe(
  264. switchMap(translations => {
  265. return this.http.get<{ [ id: string ]: string }>(baseUrl + attributeName)
  266. .pipe(map(data => ({ data, translations })))
  267. }),
  268. map(({ data, translations }) => {
  269. const hashToPopulate: VideoConstant<T>[] = Object.keys(data)
  270. .map(dataKey => {
  271. const label = data[ dataKey ]
  272. const id = attributeName === 'languages'
  273. ? dataKey as T
  274. : parseInt(dataKey, 10) as T
  275. return {
  276. id,
  277. label: peertubeTranslate(label, translations)
  278. }
  279. })
  280. if (sort === true) sortBy(hashToPopulate, 'label')
  281. return hashToPopulate
  282. }),
  283. shareReplay()
  284. )
  285. }
  286. private saveConfigLocally (config: ServerConfig) {
  287. peertubeLocalStorage.setItem(ServerService.CONFIG_LOCAL_STORAGE_KEY, JSON.stringify(config))
  288. }
  289. private loadConfigLocally () {
  290. const configString = peertubeLocalStorage.getItem(ServerService.CONFIG_LOCAL_STORAGE_KEY)
  291. if (configString) {
  292. try {
  293. const parsed = JSON.parse(configString)
  294. Object.assign(this.config, parsed)
  295. } catch (err) {
  296. console.error('Cannot parse config saved in local storage.', err)
  297. }
  298. }
  299. }
  300. }