aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.ts4
-rw-r--r--client/src/app/+admin/overview/users/user-edit/user-edit.ts4
-rw-r--r--client/src/app/+admin/plugins/plugin-list-installed/plugin-list-installed.component.ts2
-rw-r--r--client/src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.ts6
-rw-r--r--client/src/app/+my-library/my-video-channel-syncs/my-video-channel-syncs.component.ts2
-rw-r--r--client/src/app/+my-library/my-videos/my-videos.component.ts8
-rw-r--r--client/src/app/+search/search-filters.component.ts6
-rw-r--r--client/src/app/+stats/video/video-stats.component.ts2
-rw-r--r--client/src/app/+videos/+video-edit/shared/video-edit-utils.ts4
-rw-r--r--client/src/app/+videos/+video-watch/shared/comment/video-comments.component.ts4
-rw-r--r--client/src/app/+videos/video-list/overview/overview.service.ts7
-rw-r--r--client/src/app/core/server/server.service.ts3
-rw-r--r--client/src/app/core/users/user-local-storage.service.ts7
-rw-r--r--client/src/app/core/users/user.model.ts6
-rw-r--r--client/src/app/menu/language-chooser.component.ts7
-rw-r--r--client/src/app/shared/shared-actor-image/actor-avatar.component.ts5
-rw-r--r--client/src/app/shared/shared-custom-markup/dynamic-element.service.ts5
-rw-r--r--client/src/app/shared/shared-custom-markup/peertube-custom-tags/video-miniature-markup.component.ts3
-rw-r--r--client/src/app/shared/shared-custom-markup/peertube-custom-tags/videos-list-markup.component.ts3
-rw-r--r--client/src/app/shared/shared-forms/form-validator.service.ts11
-rw-r--r--client/src/app/shared/shared-icons/global-icon.component.ts2
-rw-r--r--client/src/app/shared/shared-instance/instance.service.ts3
-rw-r--r--client/src/app/shared/shared-main/misc/help.component.ts2
-rw-r--r--client/src/app/shared/shared-main/video/video-edit.model.ts6
-rw-r--r--client/src/app/shared/shared-share-modal/video-share.component.ts3
-rw-r--r--client/src/app/shared/shared-video-miniature/video-download.component.ts27
-rw-r--r--client/src/app/shared/shared-video-miniature/video-filters.model.ts2
-rw-r--r--client/src/app/shared/shared-video-miniature/videos-selection.component.ts3
-rw-r--r--client/src/assets/player/shared/p2p-media-loader/hls-plugin.ts2
-rw-r--r--client/src/assets/player/shared/p2p-media-loader/segment-validator.ts2
-rw-r--r--client/src/assets/player/shared/webtorrent/webtorrent-plugin.ts4
-rw-r--r--client/src/root-helpers/plugins-manager.ts4
-rw-r--r--client/src/root-helpers/video.ts2
-rw-r--r--client/src/standalone/player/player.ts2
-rw-r--r--client/src/standalone/videos/embed.ts6
-rw-r--r--client/src/standalone/videos/test-embed.ts4
-rw-r--r--client/tsconfig.json1
-rw-r--r--shared/core-utils/common/object.ts10
-rw-r--r--shared/core-utils/i18n/i18n.ts4
-rw-r--r--shared/core-utils/renderer/html.ts2
-rw-r--r--shared/models/videos/playlist/video-exist-in-playlist.model.ts4
41 files changed, 112 insertions, 82 deletions
diff --git a/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.ts b/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.ts
index 30e4aa5d5..2c3b7560d 100644
--- a/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.ts
+++ b/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.ts
@@ -273,11 +273,11 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit {
273 273
274 const defaultValues = { 274 const defaultValues = {
275 transcoding: { 275 transcoding: {
276 resolutions: {} 276 resolutions: {} as { [id: string]: string }
277 }, 277 },
278 live: { 278 live: {
279 transcoding: { 279 transcoding: {
280 resolutions: {} 280 resolutions: {} as { [id: string]: string }
281 } 281 }
282 } 282 }
283 } 283 }
diff --git a/client/src/app/+admin/overview/users/user-edit/user-edit.ts b/client/src/app/+admin/overview/users/user-edit/user-edit.ts
index 5d6c6a91e..9547da2d1 100644
--- a/client/src/app/+admin/overview/users/user-edit/user-edit.ts
+++ b/client/src/app/+admin/overview/users/user-edit/user-edit.ts
@@ -53,8 +53,8 @@ export abstract class UserEdit extends FormReactive implements OnInit {
53 this.serverService.getServerLocale() 53 this.serverService.getServerLocale()
54 .subscribe(translations => { 54 .subscribe(translations => {
55 if (authUser.role.id === UserRole.ADMINISTRATOR) { 55 if (authUser.role.id === UserRole.ADMINISTRATOR) {
56 this.roles = Object.keys(USER_ROLE_LABELS) 56 this.roles = Object.entries(USER_ROLE_LABELS)
57 .map(key => ({ value: key.toString(), label: peertubeTranslate(USER_ROLE_LABELS[key], translations) })) 57 .map(([ key, value ]) => ({ value: key.toString(), label: peertubeTranslate(value, translations) }))
58 return 58 return
59 } 59 }
60 60
diff --git a/client/src/app/+admin/plugins/plugin-list-installed/plugin-list-installed.component.ts b/client/src/app/+admin/plugins/plugin-list-installed/plugin-list-installed.component.ts
index 2fdc14d85..3fa1c56dc 100644
--- a/client/src/app/+admin/plugins/plugin-list-installed/plugin-list-installed.component.ts
+++ b/client/src/app/+admin/plugins/plugin-list-installed/plugin-list-installed.component.ts
@@ -104,7 +104,7 @@ export class PluginListInstalledComponent implements OnInit {
104 } 104 }
105 105
106 isUninstalling (plugin: PeerTubePlugin) { 106 isUninstalling (plugin: PeerTubePlugin) {
107 return !!this.uninstall[this.getPluginKey(plugin)] 107 return !!this.uninstalling[this.getPluginKey(plugin)]
108 } 108 }
109 109
110 isTheme (plugin: PeerTubePlugin) { 110 isTheme (plugin: PeerTubePlugin) {
diff --git a/client/src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.ts b/client/src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.ts
index 769ab647a..8faba676e 100644
--- a/client/src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.ts
+++ b/client/src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.ts
@@ -141,11 +141,11 @@ export class MyAccountNotificationPreferencesComponent implements OnInit {
141 } 141 }
142 142
143 private loadNotificationSettings () { 143 private loadNotificationSettings () {
144 for (const key of Object.keys(this.user.notificationSettings)) { 144 for (const key of Object.keys(this.user.notificationSettings) as (keyof UserNotificationSetting)[]) {
145 const value = this.user.notificationSettings[key] 145 const value = this.user.notificationSettings[key]
146 this.emailNotifications[key] = value & UserNotificationSettingValue.EMAIL 146 this.emailNotifications[key] = !!(value & UserNotificationSettingValue.EMAIL)
147 147
148 this.webNotifications[key] = value & UserNotificationSettingValue.WEB 148 this.webNotifications[key] = !!(value & UserNotificationSettingValue.WEB)
149 } 149 }
150 } 150 }
151} 151}
diff --git a/client/src/app/+my-library/my-video-channel-syncs/my-video-channel-syncs.component.ts b/client/src/app/+my-library/my-video-channel-syncs/my-video-channel-syncs.component.ts
index 74dbe222d..1f7287f44 100644
--- a/client/src/app/+my-library/my-video-channel-syncs/my-video-channel-syncs.component.ts
+++ b/client/src/app/+my-library/my-video-channel-syncs/my-video-channel-syncs.component.ts
@@ -124,7 +124,7 @@ export class MyVideoChannelSyncsComponent extends RestTable implements OnInit {
124 return '/my-library/video-channel-syncs/create' 124 return '/my-library/video-channel-syncs/create'
125 } 125 }
126 126
127 getSyncStateClass (stateId: number) { 127 getSyncStateClass (stateId: VideoChannelSyncState) {
128 return [ 'pt-badge', MyVideoChannelSyncsComponent.STATE_CLASS_BY_ID[stateId] ] 128 return [ 'pt-badge', MyVideoChannelSyncsComponent.STATE_CLASS_BY_ID[stateId] ]
129 } 129 }
130 130
diff --git a/client/src/app/+my-library/my-videos/my-videos.component.ts b/client/src/app/+my-library/my-videos/my-videos.component.ts
index 46dd304ba..b618b3f88 100644
--- a/client/src/app/+my-library/my-videos/my-videos.component.ts
+++ b/client/src/app/+my-library/my-videos/my-videos.component.ts
@@ -171,15 +171,15 @@ export class MyVideosComponent implements OnInit, DisableForReuseHook {
171 .subscribe(result => { 171 .subscribe(result => {
172 this.videosContainedInPlaylists = Object.keys(result).reduce((acc, videoId) => ({ 172 this.videosContainedInPlaylists = Object.keys(result).reduce((acc, videoId) => ({
173 ...acc, 173 ...acc,
174 [videoId]: uniqBy(result[videoId], (p: VideoExistInPlaylist) => p.playlistId) 174 [videoId]: uniqBy(result[+videoId], (p: VideoExistInPlaylist) => p.playlistId)
175 }), this.videosContainedInPlaylists) 175 }), this.videosContainedInPlaylists)
176 }) 176 })
177 } 177 }
178 178
179 async deleteSelectedVideos () { 179 async deleteSelectedVideos () {
180 const toDeleteVideosIds = Object.keys(this.selection) 180 const toDeleteVideosIds = Object.entries(this.selection)
181 .filter(k => this.selection[k] === true) 181 .filter(([ _k, v ]) => v === true)
182 .map(k => parseInt(k, 10)) 182 .map(([ k, _v ]) => parseInt(k, 10))
183 183
184 const res = await this.confirmService.confirm( 184 const res = await this.confirmService.confirm(
185 prepareIcu($localize`Do you really want to delete {length, plural, =1 {this video} other {{length} videos}}?`)( 185 prepareIcu($localize`Do you really want to delete {length, plural, =1 {this video} other {{length} videos}}?`)(
diff --git a/client/src/app/+search/search-filters.component.ts b/client/src/app/+search/search-filters.component.ts
index f9de04706..a6fc51383 100644
--- a/client/src/app/+search/search-filters.component.ts
+++ b/client/src/app/+search/search-filters.component.ts
@@ -118,11 +118,11 @@ export class SearchFiltersComponent implements OnInit {
118 this.onDurationOrPublishedUpdated() 118 this.onDurationOrPublishedUpdated()
119 } 119 }
120 120
121 resetField (fieldName: string, value?: any) { 121 resetField (fieldName: keyof AdvancedSearch, value?: any) {
122 this.advancedSearch[fieldName] = value 122 (this.advancedSearch as any)[fieldName] = value
123 } 123 }
124 124
125 resetLocalField (fieldName: string, value?: any) { 125 resetLocalField (fieldName: keyof SearchFiltersComponent, value?: any) {
126 this[fieldName] = value 126 this[fieldName] = value
127 this.onDurationOrPublishedUpdated() 127 this.onDurationOrPublishedUpdated()
128 } 128 }
diff --git a/client/src/app/+stats/video/video-stats.component.ts b/client/src/app/+stats/video/video-stats.component.ts
index 18312ec33..fa5e33ab6 100644
--- a/client/src/app/+stats/video/video-stats.component.ts
+++ b/client/src/app/+stats/video/video-stats.component.ts
@@ -47,7 +47,7 @@ export class VideoStatsComponent implements OnInit {
47 chartHeight = '300px' 47 chartHeight = '300px'
48 chartWidth: string = null 48 chartWidth: string = null
49 49
50 availableCharts: { id: string, label: string, zoomEnabled: boolean }[] = [] 50 availableCharts: { id: ActiveGraphId, label: string, zoomEnabled: boolean }[] = []
51 activeGraphId: ActiveGraphId = 'viewers' 51 activeGraphId: ActiveGraphId = 'viewers'
52 52
53 video: VideoDetails 53 video: VideoDetails
diff --git a/client/src/app/+videos/+video-edit/shared/video-edit-utils.ts b/client/src/app/+videos/+video-edit/shared/video-edit-utils.ts
index db1ef8d73..214bde680 100644
--- a/client/src/app/+videos/+video-edit/shared/video-edit-utils.ts
+++ b/client/src/app/+videos/+video-edit/shared/video-edit-utils.ts
@@ -8,11 +8,11 @@ function hydrateFormFromVideo (formGroup: FormGroup, video: VideoEdit, thumbnail
8 8
9 const objects = [ 9 const objects = [
10 { 10 {
11 url: 'thumbnailUrl', 11 url: 'thumbnailUrl' as 'thumbnailUrl',
12 name: 'thumbnailfile' 12 name: 'thumbnailfile'
13 }, 13 },
14 { 14 {
15 url: 'previewUrl', 15 url: 'previewUrl' as 'previewUrl',
16 name: 'previewfile' 16 name: 'previewfile'
17 } 17 }
18 ] 18 ]
diff --git a/client/src/app/+videos/+video-watch/shared/comment/video-comments.component.ts b/client/src/app/+videos/+video-watch/shared/comment/video-comments.component.ts
index 28edcfdcb..96bdb28c9 100644
--- a/client/src/app/+videos/+video-watch/shared/comment/video-comments.component.ts
+++ b/client/src/app/+videos/+video-watch/shared/comment/video-comments.component.ts
@@ -263,8 +263,8 @@ export class VideoCommentsComponent implements OnInit, OnChanges, OnDestroy {
263 this.syndicationItems = this.videoCommentService.getVideoCommentsFeeds(this.video) 263 this.syndicationItems = this.videoCommentService.getVideoCommentsFeeds(this.video)
264 this.loadMoreThreads() 264 this.loadMoreThreads()
265 265
266 if (this.activatedRoute.params['threadId']) { 266 if (this.activatedRoute.snapshot.params['threadId']) {
267 this.processHighlightedThread(+this.activatedRoute.params['threadId']) 267 this.processHighlightedThread(+this.activatedRoute.snapshot.params['threadId'])
268 } 268 }
269 } 269 }
270 } 270 }
diff --git a/client/src/app/+videos/video-list/overview/overview.service.ts b/client/src/app/+videos/video-list/overview/overview.service.ts
index 12d2aa1cb..4a7d9c7c5 100644
--- a/client/src/app/+videos/video-list/overview/overview.service.ts
+++ b/client/src/app/+videos/video-list/overview/overview.service.ts
@@ -5,6 +5,7 @@ import { Injectable } from '@angular/core'
5import { RestExtractor, ServerService } from '@app/core' 5import { RestExtractor, ServerService } from '@app/core'
6import { immutableAssign } from '@app/helpers' 6import { immutableAssign } from '@app/helpers'
7import { VideoService } from '@app/shared/shared-main' 7import { VideoService } from '@app/shared/shared-main'
8import { objectKeysTyped } from '@shared/core-utils'
8import { peertubeTranslate } from '@shared/core-utils/i18n' 9import { peertubeTranslate } from '@shared/core-utils/i18n'
9import { VideosOverview as VideosOverviewServer } from '@shared/models' 10import { VideosOverview as VideosOverviewServer } from '@shared/models'
10import { environment } from '../../../../environments/environment' 11import { environment } from '../../../../environments/environment'
@@ -42,7 +43,7 @@ export class OverviewService {
42 } 43 }
43 44
44 // Build videos objects 45 // Build videos objects
45 for (const key of Object.keys(serverVideosOverview)) { 46 for (const key of objectKeysTyped(serverVideosOverview)) {
46 for (const object of serverVideosOverview[key]) { 47 for (const object of serverVideosOverview[key]) {
47 observables.push( 48 observables.push(
48 of(object.videos) 49 of(object.videos)
@@ -50,7 +51,9 @@ export class OverviewService {
50 switchMap(videos => this.videosService.extractVideos({ total: 0, data: videos })), 51 switchMap(videos => this.videosService.extractVideos({ total: 0, data: videos })),
51 map(result => result.data), 52 map(result => result.data),
52 tap(videos => { 53 tap(videos => {
53 videosOverviewResult[key].push(immutableAssign(object, { videos })) 54 // FIXME: typings & lint
55 // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
56 videosOverviewResult[key].push(immutableAssign(object, { videos }) as any)
54 }) 57 })
55 ) 58 )
56 ) 59 )
diff --git a/client/src/app/core/server/server.service.ts b/client/src/app/core/server/server.service.ts
index 9f191f0a6..fc269749b 100644
--- a/client/src/app/core/server/server.service.ts
+++ b/client/src/app/core/server/server.service.ts
@@ -193,7 +193,8 @@ export class ServerService {
193 } 193 }
194 194
195 private loadHTMLConfigLocally () { 195 private loadHTMLConfigLocally () {
196 const configString = window['PeerTubeServerConfig'] 196 // FIXME: typings
197 const configString = (window as any)['PeerTubeServerConfig']
197 if (!configString) { 198 if (!configString) {
198 throw new Error('Could not find PeerTubeServerConfig in HTML') 199 throw new Error('Could not find PeerTubeServerConfig in HTML')
199 } 200 }
diff --git a/client/src/app/core/users/user-local-storage.service.ts b/client/src/app/core/users/user-local-storage.service.ts
index 1e629249a..a87f3b98a 100644
--- a/client/src/app/core/users/user-local-storage.service.ts
+++ b/client/src/app/core/users/user-local-storage.service.ts
@@ -4,7 +4,8 @@ import { Injectable } from '@angular/core'
4import { AuthService, AuthStatus } from '@app/core/auth' 4import { AuthService, AuthStatus } from '@app/core/auth'
5import { getBoolOrDefault } from '@root-helpers/local-storage-utils' 5import { getBoolOrDefault } from '@root-helpers/local-storage-utils'
6import { logger } from '@root-helpers/logger' 6import { logger } from '@root-helpers/logger'
7import { UserLocalStorageKeys, OAuthUserTokens } from '@root-helpers/users' 7import { OAuthUserTokens, UserLocalStorageKeys } from '@root-helpers/users'
8import { objectKeysTyped } from '@shared/core-utils'
8import { UserRole, UserUpdateMe } from '@shared/models' 9import { UserRole, UserUpdateMe } from '@shared/models'
9import { NSFWPolicyType } from '@shared/models/videos' 10import { NSFWPolicyType } from '@shared/models/videos'
10import { ServerService } from '../server' 11import { ServerService } from '../server'
@@ -122,7 +123,7 @@ export class UserLocalStorageService {
122 } 123 }
123 124
124 setUserInfo (profile: UserUpdateMe) { 125 setUserInfo (profile: UserUpdateMe) {
125 const localStorageKeys: { [ id in keyof UserUpdateMe ]: string } = { 126 const localStorageKeys = {
126 nsfwPolicy: UserLocalStorageKeys.NSFW_POLICY, 127 nsfwPolicy: UserLocalStorageKeys.NSFW_POLICY,
127 p2pEnabled: UserLocalStorageKeys.P2P_ENABLED, 128 p2pEnabled: UserLocalStorageKeys.P2P_ENABLED,
128 autoPlayVideo: UserLocalStorageKeys.AUTO_PLAY_VIDEO, 129 autoPlayVideo: UserLocalStorageKeys.AUTO_PLAY_VIDEO,
@@ -132,7 +133,7 @@ export class UserLocalStorageService {
132 videoLanguages: UserLocalStorageKeys.VIDEO_LANGUAGES 133 videoLanguages: UserLocalStorageKeys.VIDEO_LANGUAGES
133 } 134 }
134 135
135 const obj = Object.keys(localStorageKeys) 136 const obj: [ string, string | boolean | string[] ][] = objectKeysTyped(localStorageKeys)
136 .filter(key => key in profile) 137 .filter(key => key in profile)
137 .map(key => ([ localStorageKeys[key], profile[key] ])) 138 .map(key => ([ localStorageKeys[key], profile[key] ]))
138 139
diff --git a/client/src/app/core/users/user.model.ts b/client/src/app/core/users/user.model.ts
index 2d783145f..d57608f1c 100644
--- a/client/src/app/core/users/user.model.ts
+++ b/client/src/app/core/users/user.model.ts
@@ -1,4 +1,5 @@
1import { Account } from '@app/shared/shared-main/account/account.model' 1import { Account } from '@app/shared/shared-main/account/account.model'
2import { objectKeysTyped } from '@shared/core-utils'
2import { hasUserRight } from '@shared/core-utils/users' 3import { hasUserRight } from '@shared/core-utils/users'
3import { 4import {
4 ActorImage, 5 ActorImage,
@@ -130,8 +131,9 @@ export class User implements UserServerModel {
130 } 131 }
131 132
132 patch (obj: UserServerModel) { 133 patch (obj: UserServerModel) {
133 for (const key of Object.keys(obj)) { 134 for (const key of objectKeysTyped(obj)) {
134 this[key] = obj[key] 135 // FIXME: typings
136 (this as any)[key] = obj[key]
135 } 137 }
136 138
137 if (obj.account !== undefined) { 139 if (obj.account !== undefined) {
diff --git a/client/src/app/menu/language-chooser.component.ts b/client/src/app/menu/language-chooser.component.ts
index b42e41855..f7ae69717 100644
--- a/client/src/app/menu/language-chooser.component.ts
+++ b/client/src/app/menu/language-chooser.component.ts
@@ -1,6 +1,7 @@
1import { Component, ElementRef, Inject, LOCALE_ID, ViewChild } from '@angular/core' 1import { Component, ElementRef, Inject, LOCALE_ID, ViewChild } from '@angular/core'
2import { getDevLocale, isOnDevLocale, sortBy } from '@app/helpers' 2import { getDevLocale, isOnDevLocale, sortBy } from '@app/helpers'
3import { NgbModal } from '@ng-bootstrap/ng-bootstrap' 3import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
4import { objectKeysTyped } from '@shared/core-utils'
4import { getCompleteLocale, getShortLocale, I18N_LOCALES } from '@shared/core-utils/i18n' 5import { getCompleteLocale, getShortLocale, I18N_LOCALES } from '@shared/core-utils/i18n'
5 6
6@Component({ 7@Component({
@@ -17,8 +18,8 @@ export class LanguageChooserComponent {
17 private modalService: NgbModal, 18 private modalService: NgbModal,
18 @Inject(LOCALE_ID) private localeId: string 19 @Inject(LOCALE_ID) private localeId: string
19 ) { 20 ) {
20 const l = Object.keys(I18N_LOCALES) 21 const l = objectKeysTyped(I18N_LOCALES)
21 .map(k => ({ id: k, label: I18N_LOCALES[k], iso: getShortLocale(k) })) 22 .map(k => ({ id: k, label: I18N_LOCALES[k], iso: getShortLocale(k) }))
22 23
23 this.languages = sortBy(l, 'label') 24 this.languages = sortBy(l, 'label')
24 } 25 }
@@ -35,7 +36,7 @@ export class LanguageChooserComponent {
35 const english = 'English' 36 const english = 'English'
36 const locale = isOnDevLocale() ? getDevLocale() : getCompleteLocale(this.localeId) 37 const locale = isOnDevLocale() ? getDevLocale() : getCompleteLocale(this.localeId)
37 38
38 if (locale) return I18N_LOCALES[locale] || english 39 if (locale) return I18N_LOCALES[locale as keyof typeof I18N_LOCALES] || english
39 return english 40 return english
40 } 41 }
41} 42}
diff --git a/client/src/app/shared/shared-actor-image/actor-avatar.component.ts b/client/src/app/shared/shared-actor-image/actor-avatar.component.ts
index f1c1aa03f..ab2e02ad7 100644
--- a/client/src/app/shared/shared-actor-image/actor-avatar.component.ts
+++ b/client/src/app/shared/shared-actor-image/actor-avatar.component.ts
@@ -1,6 +1,7 @@
1import { Component, Input, OnChanges, OnInit } from '@angular/core' 1import { Component, Input, OnChanges, OnInit } from '@angular/core'
2import { VideoChannel } from '../shared-main' 2import { VideoChannel } from '../shared-main'
3import { Account } from '../shared-main/account/account.model' 3import { Account } from '../shared-main/account/account.model'
4import { objectKeysTyped } from '@shared/core-utils'
4 5
5type ActorInput = { 6type ActorInput = {
6 name: string 7 name: string
@@ -154,8 +155,8 @@ export class ActorAvatarComponent implements OnInit, OnChanges {
154 'wxyz': 'dark-blue' 155 'wxyz': 'dark-blue'
155 } 156 }
156 157
157 const theme = Object.keys(themes) 158 const theme = objectKeysTyped(themes)
158 .find(chars => chars.includes(initialLowercase)) 159 .find(chars => chars.includes(initialLowercase))
159 160
160 return themes[theme] || 'blue' 161 return themes[theme] || 'blue'
161 } 162 }
diff --git a/client/src/app/shared/shared-custom-markup/dynamic-element.service.ts b/client/src/app/shared/shared-custom-markup/dynamic-element.service.ts
index 208dba721..a12907055 100644
--- a/client/src/app/shared/shared-custom-markup/dynamic-element.service.ts
+++ b/client/src/app/shared/shared-custom-markup/dynamic-element.service.ts
@@ -10,6 +10,7 @@ import {
10 SimpleChanges, 10 SimpleChanges,
11 Type 11 Type
12} from '@angular/core' 12} from '@angular/core'
13import { objectKeysTyped } from '@shared/core-utils'
13 14
14@Injectable() 15@Injectable()
15export class DynamicElementService { 16export class DynamicElementService {
@@ -41,12 +42,12 @@ export class DynamicElementService {
41 setModel <T> (componentRef: ComponentRef<T>, attributes: Partial<T>) { 42 setModel <T> (componentRef: ComponentRef<T>, attributes: Partial<T>) {
42 const changes: SimpleChanges = {} 43 const changes: SimpleChanges = {}
43 44
44 for (const key of Object.keys(attributes)) { 45 for (const key of objectKeysTyped(attributes)) {
45 const previousValue = componentRef.instance[key] 46 const previousValue = componentRef.instance[key]
46 const newValue = attributes[key] 47 const newValue = attributes[key]
47 48
48 componentRef.instance[key] = newValue 49 componentRef.instance[key] = newValue
49 changes[key] = new SimpleChange(previousValue, newValue, previousValue === undefined) 50 changes[key as string] = new SimpleChange(previousValue, newValue, previousValue === undefined)
50 } 51 }
51 52
52 const component = componentRef.instance 53 const component = componentRef.instance
diff --git a/client/src/app/shared/shared-custom-markup/peertube-custom-tags/video-miniature-markup.component.ts b/client/src/app/shared/shared-custom-markup/peertube-custom-tags/video-miniature-markup.component.ts
index 21774b7aa..bd93929c9 100644
--- a/client/src/app/shared/shared-custom-markup/peertube-custom-tags/video-miniature-markup.component.ts
+++ b/client/src/app/shared/shared-custom-markup/peertube-custom-tags/video-miniature-markup.component.ts
@@ -2,6 +2,7 @@ import { finalize } from 'rxjs/operators'
2import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output } from '@angular/core' 2import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output } from '@angular/core'
3import { AuthService, Notifier } from '@app/core' 3import { AuthService, Notifier } from '@app/core'
4import { FindInBulkService } from '@app/shared/shared-search' 4import { FindInBulkService } from '@app/shared/shared-search'
5import { objectKeysTyped } from '@shared/core-utils'
5import { Video } from '../../shared-main' 6import { Video } from '../../shared-main'
6import { MiniatureDisplayOptions } from '../../shared-video-miniature' 7import { MiniatureDisplayOptions } from '../../shared-video-miniature'
7import { CustomMarkupComponent } from './shared' 8import { CustomMarkupComponent } from './shared'
@@ -47,7 +48,7 @@ export class VideoMiniatureMarkupComponent implements CustomMarkupComponent, OnI
47 48
48 ngOnInit () { 49 ngOnInit () {
49 if (this.onlyDisplayTitle) { 50 if (this.onlyDisplayTitle) {
50 for (const key of Object.keys(this.displayOptions)) { 51 for (const key of objectKeysTyped(this.displayOptions)) {
51 this.displayOptions[key] = false 52 this.displayOptions[key] = false
52 } 53 }
53 } 54 }
diff --git a/client/src/app/shared/shared-custom-markup/peertube-custom-tags/videos-list-markup.component.ts b/client/src/app/shared/shared-custom-markup/peertube-custom-tags/videos-list-markup.component.ts
index 7c2e7db6a..81363be87 100644
--- a/client/src/app/shared/shared-custom-markup/peertube-custom-tags/videos-list-markup.component.ts
+++ b/client/src/app/shared/shared-custom-markup/peertube-custom-tags/videos-list-markup.component.ts
@@ -1,6 +1,7 @@
1import { finalize } from 'rxjs/operators' 1import { finalize } from 'rxjs/operators'
2import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output } from '@angular/core' 2import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output } from '@angular/core'
3import { AuthService, Notifier } from '@app/core' 3import { AuthService, Notifier } from '@app/core'
4import { objectKeysTyped } from '@shared/core-utils'
4import { VideoSortField } from '@shared/models' 5import { VideoSortField } from '@shared/models'
5import { Video, VideoService } from '../../shared-main' 6import { Video, VideoService } from '../../shared-main'
6import { MiniatureDisplayOptions } from '../../shared-video-miniature' 7import { MiniatureDisplayOptions } from '../../shared-video-miniature'
@@ -66,7 +67,7 @@ export class VideosListMarkupComponent implements CustomMarkupComponent, OnInit
66 67
67 ngOnInit () { 68 ngOnInit () {
68 if (this.onlyDisplayTitle) { 69 if (this.onlyDisplayTitle) {
69 for (const key of Object.keys(this.displayOptions)) { 70 for (const key of objectKeysTyped(this.displayOptions)) {
70 this.displayOptions[key] = false 71 this.displayOptions[key] = false
71 } 72 }
72 } 73 }
diff --git a/client/src/app/shared/shared-forms/form-validator.service.ts b/client/src/app/shared/shared-forms/form-validator.service.ts
index 897008242..14ee044b5 100644
--- a/client/src/app/shared/shared-forms/form-validator.service.ts
+++ b/client/src/app/shared/shared-forms/form-validator.service.ts
@@ -1,5 +1,6 @@
1import { Injectable } from '@angular/core' 1import { Injectable } from '@angular/core'
2import { AsyncValidatorFn, FormArray, FormBuilder, FormControl, FormGroup, ValidatorFn } from '@angular/forms' 2import { AsyncValidatorFn, FormArray, FormBuilder, FormControl, FormGroup, ValidatorFn } from '@angular/forms'
3import { objectKeysTyped } from '@shared/core-utils'
3import { BuildFormArgument, BuildFormDefaultValues } from '../form-validators/form-validator.model' 4import { BuildFormArgument, BuildFormDefaultValues } from '../form-validators/form-validator.model'
4import { FormReactiveErrors, FormReactiveValidationMessages } from './form-reactive.service' 5import { FormReactiveErrors, FormReactiveValidationMessages } from './form-reactive.service'
5 6
@@ -47,13 +48,14 @@ export class FormValidatorService {
47 obj: BuildFormArgument, 48 obj: BuildFormArgument,
48 defaultValues: BuildFormDefaultValues = {} 49 defaultValues: BuildFormDefaultValues = {}
49 ) { 50 ) {
50 for (const name of Object.keys(obj)) { 51 for (const name of objectKeysTyped(obj)) {
51 formErrors[name] = '' 52 formErrors[name] = ''
52 53
53 const field = obj[name] 54 const field = obj[name]
54 if (this.isRecursiveField(field)) { 55 if (this.isRecursiveField(field)) {
55 this.updateFormGroup( 56 this.updateFormGroup(
56 form[name], 57 // FIXME: typings
58 (form as any)[name],
57 formErrors[name] as FormReactiveErrors, 59 formErrors[name] as FormReactiveErrors,
58 validationMessages[name] as FormReactiveValidationMessages, 60 validationMessages[name] as FormReactiveValidationMessages,
59 obj[name] as BuildFormArgument, 61 obj[name] as BuildFormArgument,
@@ -67,7 +69,7 @@ export class FormValidatorService {
67 const defaultValue = defaultValues[name] || '' 69 const defaultValue = defaultValues[name] || ''
68 70
69 form.addControl( 71 form.addControl(
70 name, 72 name + '',
71 new FormControl(defaultValue, field?.VALIDATORS as ValidatorFn[], field?.ASYNC_VALIDATORS as AsyncValidatorFn[]) 73 new FormControl(defaultValue, field?.VALIDATORS as ValidatorFn[], field?.ASYNC_VALIDATORS as AsyncValidatorFn[])
72 ) 74 )
73 } 75 }
@@ -75,7 +77,8 @@ export class FormValidatorService {
75 77
76 updateTreeValidity (group: FormGroup | FormArray): void { 78 updateTreeValidity (group: FormGroup | FormArray): void {
77 for (const key of Object.keys(group.controls)) { 79 for (const key of Object.keys(group.controls)) {
78 const abstractControl = group.controls[key] as FormControl 80 // FIXME: typings
81 const abstractControl = (group.controls as any)[key] as FormControl
79 82
80 if (abstractControl instanceof FormGroup || abstractControl instanceof FormArray) { 83 if (abstractControl instanceof FormGroup || abstractControl instanceof FormArray) {
81 this.updateTreeValidity(abstractControl) 84 this.updateTreeValidity(abstractControl)
diff --git a/client/src/app/shared/shared-icons/global-icon.component.ts b/client/src/app/shared/shared-icons/global-icon.component.ts
index 96179cbe6..eea460831 100644
--- a/client/src/app/shared/shared-icons/global-icon.component.ts
+++ b/client/src/app/shared/shared-icons/global-icon.component.ts
@@ -112,7 +112,7 @@ export class GlobalIconComponent implements OnInit {
112 } 112 }
113 } 113 }
114 114
115 private getSVGContent (options: { name: string }) { 115 private getSVGContent (options: { name: GlobalIconName }) {
116 return icons[options.name] 116 return icons[options.name]
117 } 117 }
118} 118}
diff --git a/client/src/app/shared/shared-instance/instance.service.ts b/client/src/app/shared/shared-instance/instance.service.ts
index 2defffbbe..3088f0899 100644
--- a/client/src/app/shared/shared-instance/instance.service.ts
+++ b/client/src/app/shared/shared-instance/instance.service.ts
@@ -3,6 +3,7 @@ import { catchError, map } from 'rxjs/operators'
3import { HttpClient } from '@angular/common/http' 3import { HttpClient } from '@angular/common/http'
4import { Injectable } from '@angular/core' 4import { Injectable } from '@angular/core'
5import { MarkdownService, RestExtractor, ServerService } from '@app/core' 5import { MarkdownService, RestExtractor, ServerService } from '@app/core'
6import { objectKeysTyped } from '@shared/core-utils'
6import { peertubeTranslate } from '@shared/core-utils/i18n' 7import { peertubeTranslate } from '@shared/core-utils/i18n'
7import { About } from '@shared/models' 8import { About } from '@shared/models'
8import { environment } from '../../../environments/environment' 9import { environment } from '../../../environments/environment'
@@ -55,7 +56,7 @@ export class InstanceService {
55 hardwareInformation: '' 56 hardwareInformation: ''
56 } 57 }
57 58
58 for (const key of Object.keys(html)) { 59 for (const key of objectKeysTyped(html)) {
59 html[key] = await this.markdownService.enhancedMarkdownToHTML({ markdown: about.instance[key] }) 60 html[key] = await this.markdownService.enhancedMarkdownToHTML({ markdown: about.instance[key] })
60 } 61 }
61 62
diff --git a/client/src/app/shared/shared-main/misc/help.component.ts b/client/src/app/shared/shared-main/misc/help.component.ts
index 37e2abd97..80fe0e160 100644
--- a/client/src/app/shared/shared-main/misc/help.component.ts
+++ b/client/src/app/shared/shared-main/misc/help.component.ts
@@ -77,7 +77,7 @@ export class HelpComponent implements OnInit, OnChanges, AfterContentInit {
77 } 77 }
78 78
79 private createMarkdownList (rules: string[]) { 79 private createMarkdownList (rules: string[]) {
80 const rulesToText = { 80 const rulesToText: { [id: string]: string } = {
81 emphasis: $localize`Emphasis`, 81 emphasis: $localize`Emphasis`,
82 link: $localize`Links`, 82 link: $localize`Links`,
83 newline: $localize`New lines`, 83 newline: $localize`New lines`,
diff --git a/client/src/app/shared/shared-main/video/video-edit.model.ts b/client/src/app/shared/shared-main/video/video-edit.model.ts
index 91d57cb6b..47eee80d8 100644
--- a/client/src/app/shared/shared-main/video/video-edit.model.ts
+++ b/client/src/app/shared/shared-main/video/video-edit.model.ts
@@ -1,6 +1,7 @@
1import { getAbsoluteAPIUrl } from '@app/helpers' 1import { getAbsoluteAPIUrl } from '@app/helpers'
2import { VideoPrivacy, VideoScheduleUpdate, VideoUpdate } from '@shared/models' 2import { VideoPrivacy, VideoScheduleUpdate, VideoUpdate } from '@shared/models'
3import { VideoDetails } from './video-details.model' 3import { VideoDetails } from './video-details.model'
4import { objectKeysTyped } from '@shared/core-utils'
4 5
5export class VideoEdit implements VideoUpdate { 6export class VideoEdit implements VideoUpdate {
6 static readonly SPECIAL_SCHEDULED_PRIVACY = -1 7 static readonly SPECIAL_SCHEDULED_PRIVACY = -1
@@ -65,8 +66,9 @@ export class VideoEdit implements VideoUpdate {
65 } 66 }
66 67
67 patch (values: { [ id: string ]: any }) { 68 patch (values: { [ id: string ]: any }) {
68 Object.keys(values).forEach((key) => { 69 objectKeysTyped(values).forEach(key => {
69 this[key] = values[key] 70 // FIXME: typings
71 (this as any)[key] = values[key]
70 }) 72 })
71 73
72 // If schedule publication, the video is private and will be changed to public privacy 74 // If schedule publication, the video is private and will be changed to public privacy
diff --git a/client/src/app/shared/shared-share-modal/video-share.component.ts b/client/src/app/shared/shared-share-modal/video-share.component.ts
index 1b69aa2d0..32f900f15 100644
--- a/client/src/app/shared/shared-share-modal/video-share.component.ts
+++ b/client/src/app/shared/shared-share-modal/video-share.component.ts
@@ -106,7 +106,8 @@ export class VideoShareComponent {
106 includeVideoInPlaylist: false 106 includeVideoInPlaylist: false
107 }, { 107 }, {
108 set: (target, prop, value) => { 108 set: (target, prop, value) => {
109 target[prop] = value 109 // FIXME: typings
110 (target as any)[prop] = value
110 111
111 if (prop === 'embedP2P') { 112 if (prop === 'embedP2P') {
112 // Auto enabled warning title if P2P is enabled 113 // Auto enabled warning title if P2P is enabled
diff --git a/client/src/app/shared/shared-video-miniature/video-download.component.ts b/client/src/app/shared/shared-video-miniature/video-download.component.ts
index 4135542dc..cac82d8d0 100644
--- a/client/src/app/shared/shared-video-miniature/video-download.component.ts
+++ b/client/src/app/shared/shared-video-miniature/video-download.component.ts
@@ -1,4 +1,4 @@
1import { mapValues, pick } from 'lodash-es' 1import { mapValues } from 'lodash-es'
2import { firstValueFrom } from 'rxjs' 2import { firstValueFrom } from 'rxjs'
3import { tap } from 'rxjs/operators' 3import { tap } from 'rxjs/operators'
4import { Component, ElementRef, Inject, LOCALE_ID, ViewChild } from '@angular/core' 4import { Component, ElementRef, Inject, LOCALE_ID, ViewChild } from '@angular/core'
@@ -6,11 +6,12 @@ import { HooksService } from '@app/core'
6import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap' 6import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'
7import { logger } from '@root-helpers/logger' 7import { logger } from '@root-helpers/logger'
8import { videoRequiresAuth } from '@root-helpers/video' 8import { videoRequiresAuth } from '@root-helpers/video'
9import { objectKeysTyped, pick } from '@shared/core-utils'
9import { VideoCaption, VideoFile, VideoPrivacy } from '@shared/models' 10import { VideoCaption, VideoFile, VideoPrivacy } from '@shared/models'
10import { BytesPipe, NumberFormatterPipe, VideoDetails, VideoFileTokenService, VideoService } from '../shared-main' 11import { BytesPipe, NumberFormatterPipe, VideoDetails, VideoFileTokenService, VideoService } from '../shared-main'
11 12
12type DownloadType = 'video' | 'subtitles' 13type DownloadType = 'video' | 'subtitles'
13type FileMetadata = { [key: string]: { label: string, value: string } } 14type FileMetadata = { [key: string]: { label: string, value: string | number } }
14 15
15@Component({ 16@Component({
16 selector: 'my-video-download', 17 selector: 'my-video-download',
@@ -218,10 +219,10 @@ export class VideoDownloadComponent {
218 const keyToTranslateFunction = { 219 const keyToTranslateFunction = {
219 encoder: (value: string) => ({ label: $localize`Encoder`, value }), 220 encoder: (value: string) => ({ label: $localize`Encoder`, value }),
220 format_long_name: (value: string) => ({ label: $localize`Format name`, value }), 221 format_long_name: (value: string) => ({ label: $localize`Format name`, value }),
221 size: (value: number) => ({ label: $localize`Size`, value: this.bytesPipe.transform(value, 2) }), 222 size: (value: number | string) => ({ label: $localize`Size`, value: this.bytesPipe.transform(+value, 2) }),
222 bit_rate: (value: number) => ({ 223 bit_rate: (value: number | string) => ({
223 label: $localize`Bitrate`, 224 label: $localize`Bitrate`,
224 value: `${this.numbersPipe.transform(value)}bps` 225 value: `${this.numbersPipe.transform(+value)}bps`
225 }) 226 })
226 } 227 }
227 228
@@ -230,8 +231,8 @@ export class VideoDownloadComponent {
230 delete sanitizedFormat.tags 231 delete sanitizedFormat.tags
231 232
232 return mapValues( 233 return mapValues(
233 pick(sanitizedFormat, Object.keys(keyToTranslateFunction)), 234 pick(sanitizedFormat, objectKeysTyped(keyToTranslateFunction)),
234 (val, key) => keyToTranslateFunction[key](val) 235 (val: string, key: keyof typeof keyToTranslateFunction) => keyToTranslateFunction[key](val)
235 ) 236 )
236 } 237 }
237 238
@@ -242,29 +243,29 @@ export class VideoDownloadComponent {
242 let keyToTranslateFunction = { 243 let keyToTranslateFunction = {
243 codec_long_name: (value: string) => ({ label: $localize`Codec`, value }), 244 codec_long_name: (value: string) => ({ label: $localize`Codec`, value }),
244 profile: (value: string) => ({ label: $localize`Profile`, value }), 245 profile: (value: string) => ({ label: $localize`Profile`, value }),
245 bit_rate: (value: number) => ({ 246 bit_rate: (value: number | string) => ({
246 label: $localize`Bitrate`, 247 label: $localize`Bitrate`,
247 value: `${this.numbersPipe.transform(value)}bps` 248 value: `${this.numbersPipe.transform(+value)}bps`
248 }) 249 })
249 } 250 }
250 251
251 if (type === 'video') { 252 if (type === 'video') {
252 keyToTranslateFunction = Object.assign(keyToTranslateFunction, { 253 keyToTranslateFunction = Object.assign(keyToTranslateFunction, {
253 width: (value: number) => ({ label: $localize`Resolution`, value: `${value}x${stream.height}` }), 254 width: (value: string | number) => ({ label: $localize`Resolution`, value: `${value}x${stream.height}` }),
254 display_aspect_ratio: (value: string) => ({ label: $localize`Aspect ratio`, value }), 255 display_aspect_ratio: (value: string) => ({ label: $localize`Aspect ratio`, value }),
255 avg_frame_rate: (value: string) => ({ label: $localize`Average frame rate`, value }), 256 avg_frame_rate: (value: string) => ({ label: $localize`Average frame rate`, value }),
256 pix_fmt: (value: string) => ({ label: $localize`Pixel format`, value }) 257 pix_fmt: (value: string) => ({ label: $localize`Pixel format`, value })
257 }) 258 })
258 } else { 259 } else {
259 keyToTranslateFunction = Object.assign(keyToTranslateFunction, { 260 keyToTranslateFunction = Object.assign(keyToTranslateFunction, {
260 sample_rate: (value: number) => ({ label: $localize`Sample rate`, value }), 261 sample_rate: (value: string | number) => ({ label: $localize`Sample rate`, value }),
261 channel_layout: (value: number) => ({ label: $localize`Channel Layout`, value }) 262 channel_layout: (value: string | number) => ({ label: $localize`Channel Layout`, value })
262 }) 263 })
263 } 264 }
264 265
265 return mapValues( 266 return mapValues(
266 pick(stream, Object.keys(keyToTranslateFunction)), 267 pick(stream, Object.keys(keyToTranslateFunction)),
267 (val, key) => keyToTranslateFunction[key](val) 268 (val: string, key: keyof typeof keyToTranslateFunction) => keyToTranslateFunction[key](val)
268 ) 269 )
269 } 270 }
270 271
diff --git a/client/src/app/shared/shared-video-miniature/video-filters.model.ts b/client/src/app/shared/shared-video-miniature/video-filters.model.ts
index 6b4b72c75..4db73b25a 100644
--- a/client/src/app/shared/shared-video-miniature/video-filters.model.ts
+++ b/client/src/app/shared/shared-video-miniature/video-filters.model.ts
@@ -84,7 +84,7 @@ export class VideoFilters {
84 if (specificKey && specificKey !== key) continue 84 if (specificKey && specificKey !== key) continue
85 85
86 // FIXME: typings 86 // FIXME: typings
87 this[key as any] = value 87 (this as any)[key] = value
88 } 88 }
89 89
90 this.buildActiveFilters() 90 this.buildActiveFilters()
diff --git a/client/src/app/shared/shared-video-miniature/videos-selection.component.ts b/client/src/app/shared/shared-video-miniature/videos-selection.component.ts
index 460a0080e..86fe502e2 100644
--- a/client/src/app/shared/shared-video-miniature/videos-selection.component.ts
+++ b/client/src/app/shared/shared-video-miniature/videos-selection.component.ts
@@ -2,6 +2,7 @@ import { Observable, Subject } from 'rxjs'
2import { AfterContentInit, Component, ContentChildren, EventEmitter, Input, Output, QueryList, TemplateRef } from '@angular/core' 2import { AfterContentInit, Component, ContentChildren, EventEmitter, Input, Output, QueryList, TemplateRef } from '@angular/core'
3import { ComponentPagination, Notifier, User } from '@app/core' 3import { ComponentPagination, Notifier, User } from '@app/core'
4import { logger } from '@root-helpers/logger' 4import { logger } from '@root-helpers/logger'
5import { objectKeysTyped } from '@shared/core-utils'
5import { ResultList, VideosExistInPlaylists, VideoSortField } from '@shared/models' 6import { ResultList, VideosExistInPlaylists, VideoSortField } from '@shared/models'
6import { PeerTubeTemplateDirective, Video } from '../shared-main' 7import { PeerTubeTemplateDirective, Video } from '../shared-main'
7import { MiniatureDisplayOptions } from './video-miniature.component' 8import { MiniatureDisplayOptions } from './video-miniature.component'
@@ -93,7 +94,7 @@ export class VideosSelectionComponent implements AfterContentInit {
93 } 94 }
94 95
95 isInSelectionMode () { 96 isInSelectionMode () {
96 return Object.keys(this._selection).some(k => this._selection[k] === true) 97 return objectKeysTyped(this._selection).some(k => this._selection[k] === true)
97 } 98 }
98 99
99 videoById (index: number, video: Video) { 100 videoById (index: number, video: Video) {
diff --git a/client/src/assets/player/shared/p2p-media-loader/hls-plugin.ts b/client/src/assets/player/shared/p2p-media-loader/hls-plugin.ts
index 63f27eecf..d05d6193c 100644
--- a/client/src/assets/player/shared/p2p-media-loader/hls-plugin.ts
+++ b/client/src/assets/player/shared/p2p-media-loader/hls-plugin.ts
@@ -349,7 +349,7 @@ class Html5Hlsjs {
349 } 349 }
350 350
351 private _oneLevelObjClone (obj: { [ id: string ]: any }) { 351 private _oneLevelObjClone (obj: { [ id: string ]: any }) {
352 const result = {} 352 const result: { [id: string]: any } = {}
353 const objKeys = Object.keys(obj) 353 const objKeys = Object.keys(obj)
354 for (let i = 0; i < objKeys.length; i++) { 354 for (let i = 0; i < objKeys.length; i++) {
355 result[objKeys[i]] = obj[objKeys[i]] 355 result[objKeys[i]] = obj[objKeys[i]]
diff --git a/client/src/assets/player/shared/p2p-media-loader/segment-validator.ts b/client/src/assets/player/shared/p2p-media-loader/segment-validator.ts
index 6e9bcf103..44a31bfb4 100644
--- a/client/src/assets/player/shared/p2p-media-loader/segment-validator.ts
+++ b/client/src/assets/player/shared/p2p-media-loader/segment-validator.ts
@@ -79,7 +79,7 @@ function fetchSha256Segments (options: {
79 segmentsSha256Url: string 79 segmentsSha256Url: string
80 authorizationHeader: () => string 80 authorizationHeader: () => string
81 requiresAuth: boolean 81 requiresAuth: boolean
82}) { 82}): Promise<SegmentsJSON> {
83 const { serverUrl, segmentsSha256Url, requiresAuth, authorizationHeader } = options 83 const { serverUrl, segmentsSha256Url, requiresAuth, authorizationHeader } = options
84 84
85 const headers = requiresAuth && isSameOrigin(serverUrl, segmentsSha256Url) 85 const headers = requiresAuth && isSameOrigin(serverUrl, segmentsSha256Url)
diff --git a/client/src/assets/player/shared/webtorrent/webtorrent-plugin.ts b/client/src/assets/player/shared/webtorrent/webtorrent-plugin.ts
index 46d009410..3dde44a60 100644
--- a/client/src/assets/player/shared/webtorrent/webtorrent-plugin.ts
+++ b/client/src/assets/player/shared/webtorrent/webtorrent-plugin.ts
@@ -339,7 +339,7 @@ class WebTorrentPlugin extends Plugin {
339 if (err.message.indexOf('incorrect info hash') !== -1) { 339 if (err.message.indexOf('incorrect info hash') !== -1) {
340 logger.error('Incorrect info hash detected, falling back to torrent file.') 340 logger.error('Incorrect info hash detected, falling back to torrent file.')
341 const newOptions = { forcePlay: true, seek: options.seek } 341 const newOptions = { forcePlay: true, seek: options.seek }
342 return this.addTorrent(this.torrent['xs'], previousVideoFile, newOptions, done) 342 return this.addTorrent((this.torrent as any)['xs'], previousVideoFile, newOptions, done)
343 } 343 }
344 344
345 // Remote instance is down 345 // Remote instance is down
@@ -582,7 +582,7 @@ class WebTorrentPlugin extends Plugin {
582 private stopTorrent (torrent: WebTorrent.Torrent) { 582 private stopTorrent (torrent: WebTorrent.Torrent) {
583 torrent.pause() 583 torrent.pause()
584 // Pause does not remove actual peers (in particular the webseed peer) 584 // Pause does not remove actual peers (in particular the webseed peer)
585 torrent.removePeer(torrent['ws']) 585 torrent.removePeer((torrent as any)['ws'])
586 } 586 }
587 587
588 private renderFileInFakeElement (file: WebTorrent.TorrentFile, delay: number) { 588 private renderFileInFakeElement (file: WebTorrent.TorrentFile, delay: number) {
diff --git a/client/src/root-helpers/plugins-manager.ts b/client/src/root-helpers/plugins-manager.ts
index f99c7598d..fd7b5233b 100644
--- a/client/src/root-helpers/plugins-manager.ts
+++ b/client/src/root-helpers/plugins-manager.ts
@@ -317,7 +317,7 @@ async function dynamicImport (url: string) {
317 const script = document.createElement('script') 317 const script = document.createElement('script')
318 318
319 const destructor = () => { 319 const destructor = () => {
320 delete window[vector] 320 delete window[vector as any]
321 script.onerror = null 321 script.onerror = null
322 script.onload = null 322 script.onload = null
323 script.remove() 323 script.remove()
@@ -333,7 +333,7 @@ async function dynamicImport (url: string) {
333 destructor() 333 destructor()
334 } 334 }
335 script.onload = () => { 335 script.onload = () => {
336 resolve(window[vector]) 336 resolve(window[vector as any])
337 destructor() 337 destructor()
338 } 338 }
339 const loader = `import * as m from "${url}"; window.${vector} = m;` // export Module 339 const loader = `import * as m from "${url}"; window.${vector} = m;` // export Module
diff --git a/client/src/root-helpers/video.ts b/client/src/root-helpers/video.ts
index 01feddbdc..9022b908b 100644
--- a/client/src/root-helpers/video.ts
+++ b/client/src/root-helpers/video.ts
@@ -21,7 +21,7 @@ function buildVideoOrPlaylistEmbed (options: {
21 const wrapper = document.createElement('div') 21 const wrapper = document.createElement('div')
22 22
23 wrapper.style.position = 'relative' 23 wrapper.style.position = 'relative'
24 wrapper.style['padding-top'] = '56.25%' 24 wrapper.style.paddingTop = '56.25%'
25 25
26 iframe.style.position = 'absolute' 26 iframe.style.position = 'absolute'
27 iframe.style.inset = '0' 27 iframe.style.inset = '0'
diff --git a/client/src/standalone/player/player.ts b/client/src/standalone/player/player.ts
index bbe37a42b..75487258b 100644
--- a/client/src/standalone/player/player.ts
+++ b/client/src/standalone/player/player.ts
@@ -233,4 +233,4 @@ export class PeerTubePlayer {
233} 233}
234 234
235// put it on the window as well as the export 235// put it on the window as well as the export
236(window['PeerTubePlayer'] as any) = PeerTubePlayer 236(window as any)['PeerTubePlayer'] = PeerTubePlayer
diff --git a/client/src/standalone/videos/embed.ts b/client/src/standalone/videos/embed.ts
index d268f4762..cc4274b99 100644
--- a/client/src/standalone/videos/embed.ts
+++ b/client/src/standalone/videos/embed.ts
@@ -52,7 +52,7 @@ export class PeerTubeEmbed {
52 this.liveManager = new LiveManager(this.playerHTML) 52 this.liveManager = new LiveManager(this.playerHTML)
53 53
54 try { 54 try {
55 this.config = JSON.parse(window['PeerTubeServerConfig']) 55 this.config = JSON.parse((window as any)['PeerTubeServerConfig'])
56 } catch (err) { 56 } catch (err) {
57 logger.error('Cannot parse HTML config.', err) 57 logger.error('Cannot parse HTML config.', err)
58 } 58 }
@@ -254,9 +254,9 @@ export class PeerTubeEmbed {
254 this.player.dispose() 254 this.player.dispose()
255 this.playerHTML.removePlayerElement() 255 this.playerHTML.removePlayerElement()
256 this.playerHTML.displayError('This video is not available because the remote instance is not responding.', translations) 256 this.playerHTML.displayError('This video is not available because the remote instance is not responding.', translations)
257 }) 257 });
258 258
259 window['videojsPlayer'] = this.player 259 (window as any)['videojsPlayer'] = this.player
260 260
261 this.buildCSS() 261 this.buildCSS()
262 this.buildPlayerDock(video) 262 this.buildPlayerDock(video)
diff --git a/client/src/standalone/videos/test-embed.ts b/client/src/standalone/videos/test-embed.ts
index ab5262902..b34df11ee 100644
--- a/client/src/standalone/videos/test-embed.ts
+++ b/client/src/standalone/videos/test-embed.ts
@@ -22,9 +22,9 @@ window.addEventListener('load', async () => {
22 mainElement.appendChild(iframe) 22 mainElement.appendChild(iframe)
23 23
24 logger.info('Document finished loading.') 24 logger.info('Document finished loading.')
25 const player = new PeerTubePlayer(document.querySelector('iframe')) 25 const player = new PeerTubePlayer(document.querySelector('iframe'));
26 26
27 window['player'] = player 27 (window as any)['player'] = player
28 28
29 logger.info('Awaiting player ready...') 29 logger.info('Awaiting player ready...')
30 await player.ready 30 await player.ready
diff --git a/client/tsconfig.json b/client/tsconfig.json
index 1668cfced..785ed1c6c 100644
--- a/client/tsconfig.json
+++ b/client/tsconfig.json
@@ -10,7 +10,6 @@
10 "experimentalDecorators": true, 10 "experimentalDecorators": true,
11 "noImplicitAny": true, 11 "noImplicitAny": true,
12 "noImplicitThis": true, 12 "noImplicitThis": true,
13 "suppressImplicitAnyIndexErrors": true,
14 "alwaysStrict": true, 13 "alwaysStrict": true,
15 "importHelpers": true, 14 "importHelpers": true,
16 "allowSyntheticDefaultImports": true, 15 "allowSyntheticDefaultImports": true,
diff --git a/shared/core-utils/common/object.ts b/shared/core-utils/common/object.ts
index 9780b2594..1276bfcc7 100644
--- a/shared/core-utils/common/object.ts
+++ b/shared/core-utils/common/object.ts
@@ -23,10 +23,18 @@ function omit <O extends object, K extends keyof O> (object: O, keys: K[]): Excl
23 return result 23 return result
24} 24}
25 25
26function objectKeysTyped <O extends object, K extends keyof O> (object: O): K[] {
27 return (Object.keys(object) as K[])
28}
29
26function getKeys <O extends object, K extends keyof O> (object: O, keys: K[]): K[] { 30function getKeys <O extends object, K extends keyof O> (object: O, keys: K[]): K[] {
27 return (Object.keys(object) as K[]).filter(k => keys.includes(k)) 31 return (Object.keys(object) as K[]).filter(k => keys.includes(k))
28} 32}
29 33
34function hasKey <T extends object> (obj: T, k: keyof any): k is keyof T {
35 return k in obj
36}
37
30function sortObjectComparator (key: string, order: 'asc' | 'desc') { 38function sortObjectComparator (key: string, order: 'asc' | 'desc') {
31 return (a: any, b: any) => { 39 return (a: any, b: any) => {
32 if (a[key] < b[key]) { 40 if (a[key] < b[key]) {
@@ -69,7 +77,9 @@ function simpleObjectsDeepEqual (a: any, b: any) {
69export { 77export {
70 pick, 78 pick,
71 omit, 79 omit,
80 objectKeysTyped,
72 getKeys, 81 getKeys,
82 hasKey,
73 shallowCopy, 83 shallowCopy,
74 sortObjectComparator, 84 sortObjectComparator,
75 simpleObjectsDeepEqual 85 simpleObjectsDeepEqual
diff --git a/shared/core-utils/i18n/i18n.ts b/shared/core-utils/i18n/i18n.ts
index 38c1b0cc9..54b54077a 100644
--- a/shared/core-utils/i18n/i18n.ts
+++ b/shared/core-utils/i18n/i18n.ts
@@ -103,9 +103,9 @@ export function is18nLocale (locale: string) {
103export function getCompleteLocale (locale: string) { 103export function getCompleteLocale (locale: string) {
104 if (!locale) return locale 104 if (!locale) return locale
105 105
106 if (I18N_LOCALE_ALIAS[locale]) return I18N_LOCALE_ALIAS[locale] 106 const found = (I18N_LOCALE_ALIAS as any)[locale] as string
107 107
108 return locale 108 return found || locale
109} 109}
110 110
111export function getShortLocale (locale: string) { 111export function getShortLocale (locale: string) {
diff --git a/shared/core-utils/renderer/html.ts b/shared/core-utils/renderer/html.ts
index 877f2ec55..365bf7612 100644
--- a/shared/core-utils/renderer/html.ts
+++ b/shared/core-utils/renderer/html.ts
@@ -56,7 +56,7 @@ export function getCustomMarkupSanitizeOptions (additionalAllowedTags: string[]
56export function escapeHTML (stringParam: string) { 56export function escapeHTML (stringParam: string) {
57 if (!stringParam) return '' 57 if (!stringParam) return ''
58 58
59 const entityMap = { 59 const entityMap: { [id: string ]: string } = {
60 '&': '&amp;', 60 '&': '&amp;',
61 '<': '&lt;', 61 '<': '&lt;',
62 '>': '&gt;', 62 '>': '&gt;',
diff --git a/shared/models/videos/playlist/video-exist-in-playlist.model.ts b/shared/models/videos/playlist/video-exist-in-playlist.model.ts
index bc803a99c..6d06c0f4d 100644
--- a/shared/models/videos/playlist/video-exist-in-playlist.model.ts
+++ b/shared/models/videos/playlist/video-exist-in-playlist.model.ts
@@ -1,8 +1,8 @@
1export type VideosExistInPlaylists = { 1export type VideosExistInPlaylists = {
2 [videoId: number ]: VideoExistInPlaylist[] 2 [videoId: number]: VideoExistInPlaylist[]
3} 3}
4export type CachedVideosExistInPlaylists = { 4export type CachedVideosExistInPlaylists = {
5 [videoId: number ]: CachedVideoExistInPlaylist[] 5 [videoId: number]: CachedVideoExistInPlaylist[]
6} 6}
7 7
8export type CachedVideoExistInPlaylist = { 8export type CachedVideoExistInPlaylist = {