Remove suppressImplicitAnyIndexErrors

It's deprecated by TS
pull/5817/head
Chocobozzz 2023-05-24 16:48:54 +02:00
parent d0fbc9fd0a
commit 5490930428
No known key found for this signature in database
GPG Key ID: 583A612D890159BE
41 changed files with 112 additions and 82 deletions

View File

@ -273,11 +273,11 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit {
const defaultValues = { const defaultValues = {
transcoding: { transcoding: {
resolutions: {} resolutions: {} as { [id: string]: string }
}, },
live: { live: {
transcoding: { transcoding: {
resolutions: {} resolutions: {} as { [id: string]: string }
} }
} }
} }

View File

@ -53,8 +53,8 @@ export abstract class UserEdit extends FormReactive implements OnInit {
this.serverService.getServerLocale() this.serverService.getServerLocale()
.subscribe(translations => { .subscribe(translations => {
if (authUser.role.id === UserRole.ADMINISTRATOR) { if (authUser.role.id === UserRole.ADMINISTRATOR) {
this.roles = Object.keys(USER_ROLE_LABELS) this.roles = Object.entries(USER_ROLE_LABELS)
.map(key => ({ value: key.toString(), label: peertubeTranslate(USER_ROLE_LABELS[key], translations) })) .map(([ key, value ]) => ({ value: key.toString(), label: peertubeTranslate(value, translations) }))
return return
} }

View File

@ -104,7 +104,7 @@ export class PluginListInstalledComponent implements OnInit {
} }
isUninstalling (plugin: PeerTubePlugin) { isUninstalling (plugin: PeerTubePlugin) {
return !!this.uninstall[this.getPluginKey(plugin)] return !!this.uninstalling[this.getPluginKey(plugin)]
} }
isTheme (plugin: PeerTubePlugin) { isTheme (plugin: PeerTubePlugin) {

View File

@ -141,11 +141,11 @@ export class MyAccountNotificationPreferencesComponent implements OnInit {
} }
private loadNotificationSettings () { private loadNotificationSettings () {
for (const key of Object.keys(this.user.notificationSettings)) { for (const key of Object.keys(this.user.notificationSettings) as (keyof UserNotificationSetting)[]) {
const value = this.user.notificationSettings[key] const value = this.user.notificationSettings[key]
this.emailNotifications[key] = value & UserNotificationSettingValue.EMAIL this.emailNotifications[key] = !!(value & UserNotificationSettingValue.EMAIL)
this.webNotifications[key] = value & UserNotificationSettingValue.WEB this.webNotifications[key] = !!(value & UserNotificationSettingValue.WEB)
} }
} }
} }

View File

@ -124,7 +124,7 @@ export class MyVideoChannelSyncsComponent extends RestTable implements OnInit {
return '/my-library/video-channel-syncs/create' return '/my-library/video-channel-syncs/create'
} }
getSyncStateClass (stateId: number) { getSyncStateClass (stateId: VideoChannelSyncState) {
return [ 'pt-badge', MyVideoChannelSyncsComponent.STATE_CLASS_BY_ID[stateId] ] return [ 'pt-badge', MyVideoChannelSyncsComponent.STATE_CLASS_BY_ID[stateId] ]
} }

View File

@ -171,15 +171,15 @@ export class MyVideosComponent implements OnInit, DisableForReuseHook {
.subscribe(result => { .subscribe(result => {
this.videosContainedInPlaylists = Object.keys(result).reduce((acc, videoId) => ({ this.videosContainedInPlaylists = Object.keys(result).reduce((acc, videoId) => ({
...acc, ...acc,
[videoId]: uniqBy(result[videoId], (p: VideoExistInPlaylist) => p.playlistId) [videoId]: uniqBy(result[+videoId], (p: VideoExistInPlaylist) => p.playlistId)
}), this.videosContainedInPlaylists) }), this.videosContainedInPlaylists)
}) })
} }
async deleteSelectedVideos () { async deleteSelectedVideos () {
const toDeleteVideosIds = Object.keys(this.selection) const toDeleteVideosIds = Object.entries(this.selection)
.filter(k => this.selection[k] === true) .filter(([ _k, v ]) => v === true)
.map(k => parseInt(k, 10)) .map(([ k, _v ]) => parseInt(k, 10))
const res = await this.confirmService.confirm( const res = await this.confirmService.confirm(
prepareIcu($localize`Do you really want to delete {length, plural, =1 {this video} other {{length} videos}}?`)( prepareIcu($localize`Do you really want to delete {length, plural, =1 {this video} other {{length} videos}}?`)(

View File

@ -118,11 +118,11 @@ export class SearchFiltersComponent implements OnInit {
this.onDurationOrPublishedUpdated() this.onDurationOrPublishedUpdated()
} }
resetField (fieldName: string, value?: any) { resetField (fieldName: keyof AdvancedSearch, value?: any) {
this.advancedSearch[fieldName] = value (this.advancedSearch as any)[fieldName] = value
} }
resetLocalField (fieldName: string, value?: any) { resetLocalField (fieldName: keyof SearchFiltersComponent, value?: any) {
this[fieldName] = value this[fieldName] = value
this.onDurationOrPublishedUpdated() this.onDurationOrPublishedUpdated()
} }

View File

@ -47,7 +47,7 @@ export class VideoStatsComponent implements OnInit {
chartHeight = '300px' chartHeight = '300px'
chartWidth: string = null chartWidth: string = null
availableCharts: { id: string, label: string, zoomEnabled: boolean }[] = [] availableCharts: { id: ActiveGraphId, label: string, zoomEnabled: boolean }[] = []
activeGraphId: ActiveGraphId = 'viewers' activeGraphId: ActiveGraphId = 'viewers'
video: VideoDetails video: VideoDetails

View File

@ -8,11 +8,11 @@ function hydrateFormFromVideo (formGroup: FormGroup, video: VideoEdit, thumbnail
const objects = [ const objects = [
{ {
url: 'thumbnailUrl', url: 'thumbnailUrl' as 'thumbnailUrl',
name: 'thumbnailfile' name: 'thumbnailfile'
}, },
{ {
url: 'previewUrl', url: 'previewUrl' as 'previewUrl',
name: 'previewfile' name: 'previewfile'
} }
] ]

View File

@ -263,8 +263,8 @@ export class VideoCommentsComponent implements OnInit, OnChanges, OnDestroy {
this.syndicationItems = this.videoCommentService.getVideoCommentsFeeds(this.video) this.syndicationItems = this.videoCommentService.getVideoCommentsFeeds(this.video)
this.loadMoreThreads() this.loadMoreThreads()
if (this.activatedRoute.params['threadId']) { if (this.activatedRoute.snapshot.params['threadId']) {
this.processHighlightedThread(+this.activatedRoute.params['threadId']) this.processHighlightedThread(+this.activatedRoute.snapshot.params['threadId'])
} }
} }
} }

View File

@ -5,6 +5,7 @@ import { Injectable } from '@angular/core'
import { RestExtractor, ServerService } from '@app/core' import { RestExtractor, ServerService } from '@app/core'
import { immutableAssign } from '@app/helpers' import { immutableAssign } from '@app/helpers'
import { VideoService } from '@app/shared/shared-main' import { VideoService } from '@app/shared/shared-main'
import { objectKeysTyped } from '@shared/core-utils'
import { peertubeTranslate } from '@shared/core-utils/i18n' import { peertubeTranslate } from '@shared/core-utils/i18n'
import { VideosOverview as VideosOverviewServer } from '@shared/models' import { VideosOverview as VideosOverviewServer } from '@shared/models'
import { environment } from '../../../../environments/environment' import { environment } from '../../../../environments/environment'
@ -42,7 +43,7 @@ export class OverviewService {
} }
// Build videos objects // Build videos objects
for (const key of Object.keys(serverVideosOverview)) { for (const key of objectKeysTyped(serverVideosOverview)) {
for (const object of serverVideosOverview[key]) { for (const object of serverVideosOverview[key]) {
observables.push( observables.push(
of(object.videos) of(object.videos)
@ -50,7 +51,9 @@ export class OverviewService {
switchMap(videos => this.videosService.extractVideos({ total: 0, data: videos })), switchMap(videos => this.videosService.extractVideos({ total: 0, data: videos })),
map(result => result.data), map(result => result.data),
tap(videos => { tap(videos => {
videosOverviewResult[key].push(immutableAssign(object, { videos })) // FIXME: typings & lint
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
videosOverviewResult[key].push(immutableAssign(object, { videos }) as any)
}) })
) )
) )

View File

@ -193,7 +193,8 @@ export class ServerService {
} }
private loadHTMLConfigLocally () { private loadHTMLConfigLocally () {
const configString = window['PeerTubeServerConfig'] // FIXME: typings
const configString = (window as any)['PeerTubeServerConfig']
if (!configString) { if (!configString) {
throw new Error('Could not find PeerTubeServerConfig in HTML') throw new Error('Could not find PeerTubeServerConfig in HTML')
} }

View File

@ -4,7 +4,8 @@ import { Injectable } from '@angular/core'
import { AuthService, AuthStatus } from '@app/core/auth' import { AuthService, AuthStatus } from '@app/core/auth'
import { getBoolOrDefault } from '@root-helpers/local-storage-utils' import { getBoolOrDefault } from '@root-helpers/local-storage-utils'
import { logger } from '@root-helpers/logger' import { logger } from '@root-helpers/logger'
import { UserLocalStorageKeys, OAuthUserTokens } from '@root-helpers/users' import { OAuthUserTokens, UserLocalStorageKeys } from '@root-helpers/users'
import { objectKeysTyped } from '@shared/core-utils'
import { UserRole, UserUpdateMe } from '@shared/models' import { UserRole, UserUpdateMe } from '@shared/models'
import { NSFWPolicyType } from '@shared/models/videos' import { NSFWPolicyType } from '@shared/models/videos'
import { ServerService } from '../server' import { ServerService } from '../server'
@ -122,7 +123,7 @@ export class UserLocalStorageService {
} }
setUserInfo (profile: UserUpdateMe) { setUserInfo (profile: UserUpdateMe) {
const localStorageKeys: { [ id in keyof UserUpdateMe ]: string } = { const localStorageKeys = {
nsfwPolicy: UserLocalStorageKeys.NSFW_POLICY, nsfwPolicy: UserLocalStorageKeys.NSFW_POLICY,
p2pEnabled: UserLocalStorageKeys.P2P_ENABLED, p2pEnabled: UserLocalStorageKeys.P2P_ENABLED,
autoPlayVideo: UserLocalStorageKeys.AUTO_PLAY_VIDEO, autoPlayVideo: UserLocalStorageKeys.AUTO_PLAY_VIDEO,
@ -132,7 +133,7 @@ export class UserLocalStorageService {
videoLanguages: UserLocalStorageKeys.VIDEO_LANGUAGES videoLanguages: UserLocalStorageKeys.VIDEO_LANGUAGES
} }
const obj = Object.keys(localStorageKeys) const obj: [ string, string | boolean | string[] ][] = objectKeysTyped(localStorageKeys)
.filter(key => key in profile) .filter(key => key in profile)
.map(key => ([ localStorageKeys[key], profile[key] ])) .map(key => ([ localStorageKeys[key], profile[key] ]))

View File

@ -1,4 +1,5 @@
import { Account } from '@app/shared/shared-main/account/account.model' import { Account } from '@app/shared/shared-main/account/account.model'
import { objectKeysTyped } from '@shared/core-utils'
import { hasUserRight } from '@shared/core-utils/users' import { hasUserRight } from '@shared/core-utils/users'
import { import {
ActorImage, ActorImage,
@ -130,8 +131,9 @@ export class User implements UserServerModel {
} }
patch (obj: UserServerModel) { patch (obj: UserServerModel) {
for (const key of Object.keys(obj)) { for (const key of objectKeysTyped(obj)) {
this[key] = obj[key] // FIXME: typings
(this as any)[key] = obj[key]
} }
if (obj.account !== undefined) { if (obj.account !== undefined) {

View File

@ -1,6 +1,7 @@
import { Component, ElementRef, Inject, LOCALE_ID, ViewChild } from '@angular/core' import { Component, ElementRef, Inject, LOCALE_ID, ViewChild } from '@angular/core'
import { getDevLocale, isOnDevLocale, sortBy } from '@app/helpers' import { getDevLocale, isOnDevLocale, sortBy } from '@app/helpers'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap' import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { objectKeysTyped } from '@shared/core-utils'
import { getCompleteLocale, getShortLocale, I18N_LOCALES } from '@shared/core-utils/i18n' import { getCompleteLocale, getShortLocale, I18N_LOCALES } from '@shared/core-utils/i18n'
@Component({ @Component({
@ -17,8 +18,8 @@ export class LanguageChooserComponent {
private modalService: NgbModal, private modalService: NgbModal,
@Inject(LOCALE_ID) private localeId: string @Inject(LOCALE_ID) private localeId: string
) { ) {
const l = Object.keys(I18N_LOCALES) const l = objectKeysTyped(I18N_LOCALES)
.map(k => ({ id: k, label: I18N_LOCALES[k], iso: getShortLocale(k) })) .map(k => ({ id: k, label: I18N_LOCALES[k], iso: getShortLocale(k) }))
this.languages = sortBy(l, 'label') this.languages = sortBy(l, 'label')
} }
@ -35,7 +36,7 @@ export class LanguageChooserComponent {
const english = 'English' const english = 'English'
const locale = isOnDevLocale() ? getDevLocale() : getCompleteLocale(this.localeId) const locale = isOnDevLocale() ? getDevLocale() : getCompleteLocale(this.localeId)
if (locale) return I18N_LOCALES[locale] || english if (locale) return I18N_LOCALES[locale as keyof typeof I18N_LOCALES] || english
return english return english
} }
} }

View File

@ -1,6 +1,7 @@
import { Component, Input, OnChanges, OnInit } from '@angular/core' import { Component, Input, OnChanges, OnInit } from '@angular/core'
import { VideoChannel } from '../shared-main' import { VideoChannel } from '../shared-main'
import { Account } from '../shared-main/account/account.model' import { Account } from '../shared-main/account/account.model'
import { objectKeysTyped } from '@shared/core-utils'
type ActorInput = { type ActorInput = {
name: string name: string
@ -154,8 +155,8 @@ export class ActorAvatarComponent implements OnInit, OnChanges {
'wxyz': 'dark-blue' 'wxyz': 'dark-blue'
} }
const theme = Object.keys(themes) const theme = objectKeysTyped(themes)
.find(chars => chars.includes(initialLowercase)) .find(chars => chars.includes(initialLowercase))
return themes[theme] || 'blue' return themes[theme] || 'blue'
} }

View File

@ -10,6 +10,7 @@ import {
SimpleChanges, SimpleChanges,
Type Type
} from '@angular/core' } from '@angular/core'
import { objectKeysTyped } from '@shared/core-utils'
@Injectable() @Injectable()
export class DynamicElementService { export class DynamicElementService {
@ -41,12 +42,12 @@ export class DynamicElementService {
setModel <T> (componentRef: ComponentRef<T>, attributes: Partial<T>) { setModel <T> (componentRef: ComponentRef<T>, attributes: Partial<T>) {
const changes: SimpleChanges = {} const changes: SimpleChanges = {}
for (const key of Object.keys(attributes)) { for (const key of objectKeysTyped(attributes)) {
const previousValue = componentRef.instance[key] const previousValue = componentRef.instance[key]
const newValue = attributes[key] const newValue = attributes[key]
componentRef.instance[key] = newValue componentRef.instance[key] = newValue
changes[key] = new SimpleChange(previousValue, newValue, previousValue === undefined) changes[key as string] = new SimpleChange(previousValue, newValue, previousValue === undefined)
} }
const component = componentRef.instance const component = componentRef.instance

View File

@ -2,6 +2,7 @@ import { finalize } from 'rxjs/operators'
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output } from '@angular/core' import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output } from '@angular/core'
import { AuthService, Notifier } from '@app/core' import { AuthService, Notifier } from '@app/core'
import { FindInBulkService } from '@app/shared/shared-search' import { FindInBulkService } from '@app/shared/shared-search'
import { objectKeysTyped } from '@shared/core-utils'
import { Video } from '../../shared-main' import { Video } from '../../shared-main'
import { MiniatureDisplayOptions } from '../../shared-video-miniature' import { MiniatureDisplayOptions } from '../../shared-video-miniature'
import { CustomMarkupComponent } from './shared' import { CustomMarkupComponent } from './shared'
@ -47,7 +48,7 @@ export class VideoMiniatureMarkupComponent implements CustomMarkupComponent, OnI
ngOnInit () { ngOnInit () {
if (this.onlyDisplayTitle) { if (this.onlyDisplayTitle) {
for (const key of Object.keys(this.displayOptions)) { for (const key of objectKeysTyped(this.displayOptions)) {
this.displayOptions[key] = false this.displayOptions[key] = false
} }
} }

View File

@ -1,6 +1,7 @@
import { finalize } from 'rxjs/operators' import { finalize } from 'rxjs/operators'
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output } from '@angular/core' import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output } from '@angular/core'
import { AuthService, Notifier } from '@app/core' import { AuthService, Notifier } from '@app/core'
import { objectKeysTyped } from '@shared/core-utils'
import { VideoSortField } from '@shared/models' import { VideoSortField } from '@shared/models'
import { Video, VideoService } from '../../shared-main' import { Video, VideoService } from '../../shared-main'
import { MiniatureDisplayOptions } from '../../shared-video-miniature' import { MiniatureDisplayOptions } from '../../shared-video-miniature'
@ -66,7 +67,7 @@ export class VideosListMarkupComponent implements CustomMarkupComponent, OnInit
ngOnInit () { ngOnInit () {
if (this.onlyDisplayTitle) { if (this.onlyDisplayTitle) {
for (const key of Object.keys(this.displayOptions)) { for (const key of objectKeysTyped(this.displayOptions)) {
this.displayOptions[key] = false this.displayOptions[key] = false
} }
} }

View File

@ -1,5 +1,6 @@
import { Injectable } from '@angular/core' import { Injectable } from '@angular/core'
import { AsyncValidatorFn, FormArray, FormBuilder, FormControl, FormGroup, ValidatorFn } from '@angular/forms' import { AsyncValidatorFn, FormArray, FormBuilder, FormControl, FormGroup, ValidatorFn } from '@angular/forms'
import { objectKeysTyped } from '@shared/core-utils'
import { BuildFormArgument, BuildFormDefaultValues } from '../form-validators/form-validator.model' import { BuildFormArgument, BuildFormDefaultValues } from '../form-validators/form-validator.model'
import { FormReactiveErrors, FormReactiveValidationMessages } from './form-reactive.service' import { FormReactiveErrors, FormReactiveValidationMessages } from './form-reactive.service'
@ -47,13 +48,14 @@ export class FormValidatorService {
obj: BuildFormArgument, obj: BuildFormArgument,
defaultValues: BuildFormDefaultValues = {} defaultValues: BuildFormDefaultValues = {}
) { ) {
for (const name of Object.keys(obj)) { for (const name of objectKeysTyped(obj)) {
formErrors[name] = '' formErrors[name] = ''
const field = obj[name] const field = obj[name]
if (this.isRecursiveField(field)) { if (this.isRecursiveField(field)) {
this.updateFormGroup( this.updateFormGroup(
form[name], // FIXME: typings
(form as any)[name],
formErrors[name] as FormReactiveErrors, formErrors[name] as FormReactiveErrors,
validationMessages[name] as FormReactiveValidationMessages, validationMessages[name] as FormReactiveValidationMessages,
obj[name] as BuildFormArgument, obj[name] as BuildFormArgument,
@ -67,7 +69,7 @@ export class FormValidatorService {
const defaultValue = defaultValues[name] || '' const defaultValue = defaultValues[name] || ''
form.addControl( form.addControl(
name, name + '',
new FormControl(defaultValue, field?.VALIDATORS as ValidatorFn[], field?.ASYNC_VALIDATORS as AsyncValidatorFn[]) new FormControl(defaultValue, field?.VALIDATORS as ValidatorFn[], field?.ASYNC_VALIDATORS as AsyncValidatorFn[])
) )
} }
@ -75,7 +77,8 @@ export class FormValidatorService {
updateTreeValidity (group: FormGroup | FormArray): void { updateTreeValidity (group: FormGroup | FormArray): void {
for (const key of Object.keys(group.controls)) { for (const key of Object.keys(group.controls)) {
const abstractControl = group.controls[key] as FormControl // FIXME: typings
const abstractControl = (group.controls as any)[key] as FormControl
if (abstractControl instanceof FormGroup || abstractControl instanceof FormArray) { if (abstractControl instanceof FormGroup || abstractControl instanceof FormArray) {
this.updateTreeValidity(abstractControl) this.updateTreeValidity(abstractControl)

View File

@ -112,7 +112,7 @@ export class GlobalIconComponent implements OnInit {
} }
} }
private getSVGContent (options: { name: string }) { private getSVGContent (options: { name: GlobalIconName }) {
return icons[options.name] return icons[options.name]
} }
} }

View File

@ -3,6 +3,7 @@ import { catchError, map } from 'rxjs/operators'
import { HttpClient } from '@angular/common/http' import { HttpClient } from '@angular/common/http'
import { Injectable } from '@angular/core' import { Injectable } from '@angular/core'
import { MarkdownService, RestExtractor, ServerService } from '@app/core' import { MarkdownService, RestExtractor, ServerService } from '@app/core'
import { objectKeysTyped } from '@shared/core-utils'
import { peertubeTranslate } from '@shared/core-utils/i18n' import { peertubeTranslate } from '@shared/core-utils/i18n'
import { About } from '@shared/models' import { About } from '@shared/models'
import { environment } from '../../../environments/environment' import { environment } from '../../../environments/environment'
@ -55,7 +56,7 @@ export class InstanceService {
hardwareInformation: '' hardwareInformation: ''
} }
for (const key of Object.keys(html)) { for (const key of objectKeysTyped(html)) {
html[key] = await this.markdownService.enhancedMarkdownToHTML({ markdown: about.instance[key] }) html[key] = await this.markdownService.enhancedMarkdownToHTML({ markdown: about.instance[key] })
} }

View File

@ -77,7 +77,7 @@ export class HelpComponent implements OnInit, OnChanges, AfterContentInit {
} }
private createMarkdownList (rules: string[]) { private createMarkdownList (rules: string[]) {
const rulesToText = { const rulesToText: { [id: string]: string } = {
emphasis: $localize`Emphasis`, emphasis: $localize`Emphasis`,
link: $localize`Links`, link: $localize`Links`,
newline: $localize`New lines`, newline: $localize`New lines`,

View File

@ -1,6 +1,7 @@
import { getAbsoluteAPIUrl } from '@app/helpers' import { getAbsoluteAPIUrl } from '@app/helpers'
import { VideoPrivacy, VideoScheduleUpdate, VideoUpdate } from '@shared/models' import { VideoPrivacy, VideoScheduleUpdate, VideoUpdate } from '@shared/models'
import { VideoDetails } from './video-details.model' import { VideoDetails } from './video-details.model'
import { objectKeysTyped } from '@shared/core-utils'
export class VideoEdit implements VideoUpdate { export class VideoEdit implements VideoUpdate {
static readonly SPECIAL_SCHEDULED_PRIVACY = -1 static readonly SPECIAL_SCHEDULED_PRIVACY = -1
@ -65,8 +66,9 @@ export class VideoEdit implements VideoUpdate {
} }
patch (values: { [ id: string ]: any }) { patch (values: { [ id: string ]: any }) {
Object.keys(values).forEach((key) => { objectKeysTyped(values).forEach(key => {
this[key] = values[key] // FIXME: typings
(this as any)[key] = values[key]
}) })
// If schedule publication, the video is private and will be changed to public privacy // If schedule publication, the video is private and will be changed to public privacy

View File

@ -106,7 +106,8 @@ export class VideoShareComponent {
includeVideoInPlaylist: false includeVideoInPlaylist: false
}, { }, {
set: (target, prop, value) => { set: (target, prop, value) => {
target[prop] = value // FIXME: typings
(target as any)[prop] = value
if (prop === 'embedP2P') { if (prop === 'embedP2P') {
// Auto enabled warning title if P2P is enabled // Auto enabled warning title if P2P is enabled

View File

@ -1,4 +1,4 @@
import { mapValues, pick } from 'lodash-es' import { mapValues } from 'lodash-es'
import { firstValueFrom } from 'rxjs' import { firstValueFrom } from 'rxjs'
import { tap } from 'rxjs/operators' import { tap } from 'rxjs/operators'
import { Component, ElementRef, Inject, LOCALE_ID, ViewChild } from '@angular/core' import { Component, ElementRef, Inject, LOCALE_ID, ViewChild } from '@angular/core'
@ -6,11 +6,12 @@ import { HooksService } from '@app/core'
import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap' import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'
import { logger } from '@root-helpers/logger' import { logger } from '@root-helpers/logger'
import { videoRequiresAuth } from '@root-helpers/video' import { videoRequiresAuth } from '@root-helpers/video'
import { objectKeysTyped, pick } from '@shared/core-utils'
import { VideoCaption, VideoFile, VideoPrivacy } from '@shared/models' import { VideoCaption, VideoFile, VideoPrivacy } from '@shared/models'
import { BytesPipe, NumberFormatterPipe, VideoDetails, VideoFileTokenService, VideoService } from '../shared-main' import { BytesPipe, NumberFormatterPipe, VideoDetails, VideoFileTokenService, VideoService } from '../shared-main'
type DownloadType = 'video' | 'subtitles' type DownloadType = 'video' | 'subtitles'
type FileMetadata = { [key: string]: { label: string, value: string } } type FileMetadata = { [key: string]: { label: string, value: string | number } }
@Component({ @Component({
selector: 'my-video-download', selector: 'my-video-download',
@ -218,10 +219,10 @@ export class VideoDownloadComponent {
const keyToTranslateFunction = { const keyToTranslateFunction = {
encoder: (value: string) => ({ label: $localize`Encoder`, value }), encoder: (value: string) => ({ label: $localize`Encoder`, value }),
format_long_name: (value: string) => ({ label: $localize`Format name`, value }), format_long_name: (value: string) => ({ label: $localize`Format name`, value }),
size: (value: number) => ({ label: $localize`Size`, value: this.bytesPipe.transform(value, 2) }), size: (value: number | string) => ({ label: $localize`Size`, value: this.bytesPipe.transform(+value, 2) }),
bit_rate: (value: number) => ({ bit_rate: (value: number | string) => ({
label: $localize`Bitrate`, label: $localize`Bitrate`,
value: `${this.numbersPipe.transform(value)}bps` value: `${this.numbersPipe.transform(+value)}bps`
}) })
} }
@ -230,8 +231,8 @@ export class VideoDownloadComponent {
delete sanitizedFormat.tags delete sanitizedFormat.tags
return mapValues( return mapValues(
pick(sanitizedFormat, Object.keys(keyToTranslateFunction)), pick(sanitizedFormat, objectKeysTyped(keyToTranslateFunction)),
(val, key) => keyToTranslateFunction[key](val) (val: string, key: keyof typeof keyToTranslateFunction) => keyToTranslateFunction[key](val)
) )
} }
@ -242,29 +243,29 @@ export class VideoDownloadComponent {
let keyToTranslateFunction = { let keyToTranslateFunction = {
codec_long_name: (value: string) => ({ label: $localize`Codec`, value }), codec_long_name: (value: string) => ({ label: $localize`Codec`, value }),
profile: (value: string) => ({ label: $localize`Profile`, value }), profile: (value: string) => ({ label: $localize`Profile`, value }),
bit_rate: (value: number) => ({ bit_rate: (value: number | string) => ({
label: $localize`Bitrate`, label: $localize`Bitrate`,
value: `${this.numbersPipe.transform(value)}bps` value: `${this.numbersPipe.transform(+value)}bps`
}) })
} }
if (type === 'video') { if (type === 'video') {
keyToTranslateFunction = Object.assign(keyToTranslateFunction, { keyToTranslateFunction = Object.assign(keyToTranslateFunction, {
width: (value: number) => ({ label: $localize`Resolution`, value: `${value}x${stream.height}` }), width: (value: string | number) => ({ label: $localize`Resolution`, value: `${value}x${stream.height}` }),
display_aspect_ratio: (value: string) => ({ label: $localize`Aspect ratio`, value }), display_aspect_ratio: (value: string) => ({ label: $localize`Aspect ratio`, value }),
avg_frame_rate: (value: string) => ({ label: $localize`Average frame rate`, value }), avg_frame_rate: (value: string) => ({ label: $localize`Average frame rate`, value }),
pix_fmt: (value: string) => ({ label: $localize`Pixel format`, value }) pix_fmt: (value: string) => ({ label: $localize`Pixel format`, value })
}) })
} else { } else {
keyToTranslateFunction = Object.assign(keyToTranslateFunction, { keyToTranslateFunction = Object.assign(keyToTranslateFunction, {
sample_rate: (value: number) => ({ label: $localize`Sample rate`, value }), sample_rate: (value: string | number) => ({ label: $localize`Sample rate`, value }),
channel_layout: (value: number) => ({ label: $localize`Channel Layout`, value }) channel_layout: (value: string | number) => ({ label: $localize`Channel Layout`, value })
}) })
} }
return mapValues( return mapValues(
pick(stream, Object.keys(keyToTranslateFunction)), pick(stream, Object.keys(keyToTranslateFunction)),
(val, key) => keyToTranslateFunction[key](val) (val: string, key: keyof typeof keyToTranslateFunction) => keyToTranslateFunction[key](val)
) )
} }

View File

@ -84,7 +84,7 @@ export class VideoFilters {
if (specificKey && specificKey !== key) continue if (specificKey && specificKey !== key) continue
// FIXME: typings // FIXME: typings
this[key as any] = value (this as any)[key] = value
} }
this.buildActiveFilters() this.buildActiveFilters()

View File

@ -2,6 +2,7 @@ import { Observable, Subject } from 'rxjs'
import { AfterContentInit, Component, ContentChildren, EventEmitter, Input, Output, QueryList, TemplateRef } from '@angular/core' import { AfterContentInit, Component, ContentChildren, EventEmitter, Input, Output, QueryList, TemplateRef } from '@angular/core'
import { ComponentPagination, Notifier, User } from '@app/core' import { ComponentPagination, Notifier, User } from '@app/core'
import { logger } from '@root-helpers/logger' import { logger } from '@root-helpers/logger'
import { objectKeysTyped } from '@shared/core-utils'
import { ResultList, VideosExistInPlaylists, VideoSortField } from '@shared/models' import { ResultList, VideosExistInPlaylists, VideoSortField } from '@shared/models'
import { PeerTubeTemplateDirective, Video } from '../shared-main' import { PeerTubeTemplateDirective, Video } from '../shared-main'
import { MiniatureDisplayOptions } from './video-miniature.component' import { MiniatureDisplayOptions } from './video-miniature.component'
@ -93,7 +94,7 @@ export class VideosSelectionComponent implements AfterContentInit {
} }
isInSelectionMode () { isInSelectionMode () {
return Object.keys(this._selection).some(k => this._selection[k] === true) return objectKeysTyped(this._selection).some(k => this._selection[k] === true)
} }
videoById (index: number, video: Video) { videoById (index: number, video: Video) {

View File

@ -349,7 +349,7 @@ class Html5Hlsjs {
} }
private _oneLevelObjClone (obj: { [ id: string ]: any }) { private _oneLevelObjClone (obj: { [ id: string ]: any }) {
const result = {} const result: { [id: string]: any } = {}
const objKeys = Object.keys(obj) const objKeys = Object.keys(obj)
for (let i = 0; i < objKeys.length; i++) { for (let i = 0; i < objKeys.length; i++) {
result[objKeys[i]] = obj[objKeys[i]] result[objKeys[i]] = obj[objKeys[i]]

View File

@ -79,7 +79,7 @@ function fetchSha256Segments (options: {
segmentsSha256Url: string segmentsSha256Url: string
authorizationHeader: () => string authorizationHeader: () => string
requiresAuth: boolean requiresAuth: boolean
}) { }): Promise<SegmentsJSON> {
const { serverUrl, segmentsSha256Url, requiresAuth, authorizationHeader } = options const { serverUrl, segmentsSha256Url, requiresAuth, authorizationHeader } = options
const headers = requiresAuth && isSameOrigin(serverUrl, segmentsSha256Url) const headers = requiresAuth && isSameOrigin(serverUrl, segmentsSha256Url)

View File

@ -339,7 +339,7 @@ class WebTorrentPlugin extends Plugin {
if (err.message.indexOf('incorrect info hash') !== -1) { if (err.message.indexOf('incorrect info hash') !== -1) {
logger.error('Incorrect info hash detected, falling back to torrent file.') logger.error('Incorrect info hash detected, falling back to torrent file.')
const newOptions = { forcePlay: true, seek: options.seek } const newOptions = { forcePlay: true, seek: options.seek }
return this.addTorrent(this.torrent['xs'], previousVideoFile, newOptions, done) return this.addTorrent((this.torrent as any)['xs'], previousVideoFile, newOptions, done)
} }
// Remote instance is down // Remote instance is down
@ -582,7 +582,7 @@ class WebTorrentPlugin extends Plugin {
private stopTorrent (torrent: WebTorrent.Torrent) { private stopTorrent (torrent: WebTorrent.Torrent) {
torrent.pause() torrent.pause()
// Pause does not remove actual peers (in particular the webseed peer) // Pause does not remove actual peers (in particular the webseed peer)
torrent.removePeer(torrent['ws']) torrent.removePeer((torrent as any)['ws'])
} }
private renderFileInFakeElement (file: WebTorrent.TorrentFile, delay: number) { private renderFileInFakeElement (file: WebTorrent.TorrentFile, delay: number) {

View File

@ -317,7 +317,7 @@ async function dynamicImport (url: string) {
const script = document.createElement('script') const script = document.createElement('script')
const destructor = () => { const destructor = () => {
delete window[vector] delete window[vector as any]
script.onerror = null script.onerror = null
script.onload = null script.onload = null
script.remove() script.remove()
@ -333,7 +333,7 @@ async function dynamicImport (url: string) {
destructor() destructor()
} }
script.onload = () => { script.onload = () => {
resolve(window[vector]) resolve(window[vector as any])
destructor() destructor()
} }
const loader = `import * as m from "${url}"; window.${vector} = m;` // export Module const loader = `import * as m from "${url}"; window.${vector} = m;` // export Module

View File

@ -21,7 +21,7 @@ function buildVideoOrPlaylistEmbed (options: {
const wrapper = document.createElement('div') const wrapper = document.createElement('div')
wrapper.style.position = 'relative' wrapper.style.position = 'relative'
wrapper.style['padding-top'] = '56.25%' wrapper.style.paddingTop = '56.25%'
iframe.style.position = 'absolute' iframe.style.position = 'absolute'
iframe.style.inset = '0' iframe.style.inset = '0'

View File

@ -233,4 +233,4 @@ export class PeerTubePlayer {
} }
// put it on the window as well as the export // put it on the window as well as the export
(window['PeerTubePlayer'] as any) = PeerTubePlayer (window as any)['PeerTubePlayer'] = PeerTubePlayer

View File

@ -52,7 +52,7 @@ export class PeerTubeEmbed {
this.liveManager = new LiveManager(this.playerHTML) this.liveManager = new LiveManager(this.playerHTML)
try { try {
this.config = JSON.parse(window['PeerTubeServerConfig']) this.config = JSON.parse((window as any)['PeerTubeServerConfig'])
} catch (err) { } catch (err) {
logger.error('Cannot parse HTML config.', err) logger.error('Cannot parse HTML config.', err)
} }
@ -254,9 +254,9 @@ export class PeerTubeEmbed {
this.player.dispose() this.player.dispose()
this.playerHTML.removePlayerElement() this.playerHTML.removePlayerElement()
this.playerHTML.displayError('This video is not available because the remote instance is not responding.', translations) this.playerHTML.displayError('This video is not available because the remote instance is not responding.', translations)
}) });
window['videojsPlayer'] = this.player (window as any)['videojsPlayer'] = this.player
this.buildCSS() this.buildCSS()
this.buildPlayerDock(video) this.buildPlayerDock(video)

View File

@ -22,9 +22,9 @@ window.addEventListener('load', async () => {
mainElement.appendChild(iframe) mainElement.appendChild(iframe)
logger.info('Document finished loading.') logger.info('Document finished loading.')
const player = new PeerTubePlayer(document.querySelector('iframe')) const player = new PeerTubePlayer(document.querySelector('iframe'));
window['player'] = player (window as any)['player'] = player
logger.info('Awaiting player ready...') logger.info('Awaiting player ready...')
await player.ready await player.ready

View File

@ -10,7 +10,6 @@
"experimentalDecorators": true, "experimentalDecorators": true,
"noImplicitAny": true, "noImplicitAny": true,
"noImplicitThis": true, "noImplicitThis": true,
"suppressImplicitAnyIndexErrors": true,
"alwaysStrict": true, "alwaysStrict": true,
"importHelpers": true, "importHelpers": true,
"allowSyntheticDefaultImports": true, "allowSyntheticDefaultImports": true,

View File

@ -23,10 +23,18 @@ function omit <O extends object, K extends keyof O> (object: O, keys: K[]): Excl
return result return result
} }
function objectKeysTyped <O extends object, K extends keyof O> (object: O): K[] {
return (Object.keys(object) as K[])
}
function getKeys <O extends object, K extends keyof O> (object: O, keys: K[]): K[] { function getKeys <O extends object, K extends keyof O> (object: O, keys: K[]): K[] {
return (Object.keys(object) as K[]).filter(k => keys.includes(k)) return (Object.keys(object) as K[]).filter(k => keys.includes(k))
} }
function hasKey <T extends object> (obj: T, k: keyof any): k is keyof T {
return k in obj
}
function sortObjectComparator (key: string, order: 'asc' | 'desc') { function sortObjectComparator (key: string, order: 'asc' | 'desc') {
return (a: any, b: any) => { return (a: any, b: any) => {
if (a[key] < b[key]) { if (a[key] < b[key]) {
@ -69,7 +77,9 @@ function simpleObjectsDeepEqual (a: any, b: any) {
export { export {
pick, pick,
omit, omit,
objectKeysTyped,
getKeys, getKeys,
hasKey,
shallowCopy, shallowCopy,
sortObjectComparator, sortObjectComparator,
simpleObjectsDeepEqual simpleObjectsDeepEqual

View File

@ -103,9 +103,9 @@ export function is18nLocale (locale: string) {
export function getCompleteLocale (locale: string) { export function getCompleteLocale (locale: string) {
if (!locale) return locale if (!locale) return locale
if (I18N_LOCALE_ALIAS[locale]) return I18N_LOCALE_ALIAS[locale] const found = (I18N_LOCALE_ALIAS as any)[locale] as string
return locale return found || locale
} }
export function getShortLocale (locale: string) { export function getShortLocale (locale: string) {

View File

@ -56,7 +56,7 @@ export function getCustomMarkupSanitizeOptions (additionalAllowedTags: string[]
export function escapeHTML (stringParam: string) { export function escapeHTML (stringParam: string) {
if (!stringParam) return '' if (!stringParam) return ''
const entityMap = { const entityMap: { [id: string ]: string } = {
'&': '&amp;', '&': '&amp;',
'<': '&lt;', '<': '&lt;',
'>': '&gt;', '>': '&gt;',

View File

@ -1,8 +1,8 @@
export type VideosExistInPlaylists = { export type VideosExistInPlaylists = {
[videoId: number ]: VideoExistInPlaylist[] [videoId: number]: VideoExistInPlaylist[]
} }
export type CachedVideosExistInPlaylists = { export type CachedVideosExistInPlaylists = {
[videoId: number ]: CachedVideoExistInPlaylist[] [videoId: number]: CachedVideoExistInPlaylist[]
} }
export type CachedVideoExistInPlaylist = { export type CachedVideoExistInPlaylist = {