const defaultValues = {
transcoding: {
- resolutions: {}
+ resolutions: {} as { [id: string]: string }
},
live: {
transcoding: {
- resolutions: {}
+ resolutions: {} as { [id: string]: string }
}
}
}
this.serverService.getServerLocale()
.subscribe(translations => {
if (authUser.role.id === UserRole.ADMINISTRATOR) {
- this.roles = Object.keys(USER_ROLE_LABELS)
- .map(key => ({ value: key.toString(), label: peertubeTranslate(USER_ROLE_LABELS[key], translations) }))
+ this.roles = Object.entries(USER_ROLE_LABELS)
+ .map(([ key, value ]) => ({ value: key.toString(), label: peertubeTranslate(value, translations) }))
return
}
}
isUninstalling (plugin: PeerTubePlugin) {
- return !!this.uninstall[this.getPluginKey(plugin)]
+ return !!this.uninstalling[this.getPluginKey(plugin)]
}
isTheme (plugin: PeerTubePlugin) {
}
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]
- this.emailNotifications[key] = value & UserNotificationSettingValue.EMAIL
+ this.emailNotifications[key] = !!(value & UserNotificationSettingValue.EMAIL)
- this.webNotifications[key] = value & UserNotificationSettingValue.WEB
+ this.webNotifications[key] = !!(value & UserNotificationSettingValue.WEB)
}
}
}
return '/my-library/video-channel-syncs/create'
}
- getSyncStateClass (stateId: number) {
+ getSyncStateClass (stateId: VideoChannelSyncState) {
return [ 'pt-badge', MyVideoChannelSyncsComponent.STATE_CLASS_BY_ID[stateId] ]
}
.subscribe(result => {
this.videosContainedInPlaylists = Object.keys(result).reduce((acc, videoId) => ({
...acc,
- [videoId]: uniqBy(result[videoId], (p: VideoExistInPlaylist) => p.playlistId)
+ [videoId]: uniqBy(result[+videoId], (p: VideoExistInPlaylist) => p.playlistId)
}), this.videosContainedInPlaylists)
})
}
async deleteSelectedVideos () {
- const toDeleteVideosIds = Object.keys(this.selection)
- .filter(k => this.selection[k] === true)
- .map(k => parseInt(k, 10))
+ const toDeleteVideosIds = Object.entries(this.selection)
+ .filter(([ _k, v ]) => v === true)
+ .map(([ k, _v ]) => parseInt(k, 10))
const res = await this.confirmService.confirm(
prepareIcu($localize`Do you really want to delete {length, plural, =1 {this video} other {{length} videos}}?`)(
this.onDurationOrPublishedUpdated()
}
- resetField (fieldName: string, value?: any) {
- this.advancedSearch[fieldName] = value
+ resetField (fieldName: keyof AdvancedSearch, value?: any) {
+ (this.advancedSearch as any)[fieldName] = value
}
- resetLocalField (fieldName: string, value?: any) {
+ resetLocalField (fieldName: keyof SearchFiltersComponent, value?: any) {
this[fieldName] = value
this.onDurationOrPublishedUpdated()
}
chartHeight = '300px'
chartWidth: string = null
- availableCharts: { id: string, label: string, zoomEnabled: boolean }[] = []
+ availableCharts: { id: ActiveGraphId, label: string, zoomEnabled: boolean }[] = []
activeGraphId: ActiveGraphId = 'viewers'
video: VideoDetails
const objects = [
{
- url: 'thumbnailUrl',
+ url: 'thumbnailUrl' as 'thumbnailUrl',
name: 'thumbnailfile'
},
{
- url: 'previewUrl',
+ url: 'previewUrl' as 'previewUrl',
name: 'previewfile'
}
]
this.syndicationItems = this.videoCommentService.getVideoCommentsFeeds(this.video)
this.loadMoreThreads()
- if (this.activatedRoute.params['threadId']) {
- this.processHighlightedThread(+this.activatedRoute.params['threadId'])
+ if (this.activatedRoute.snapshot.params['threadId']) {
+ this.processHighlightedThread(+this.activatedRoute.snapshot.params['threadId'])
}
}
}
import { RestExtractor, ServerService } from '@app/core'
import { immutableAssign } from '@app/helpers'
import { VideoService } from '@app/shared/shared-main'
+import { objectKeysTyped } from '@shared/core-utils'
import { peertubeTranslate } from '@shared/core-utils/i18n'
import { VideosOverview as VideosOverviewServer } from '@shared/models'
import { environment } from '../../../../environments/environment'
}
// Build videos objects
- for (const key of Object.keys(serverVideosOverview)) {
+ for (const key of objectKeysTyped(serverVideosOverview)) {
for (const object of serverVideosOverview[key]) {
observables.push(
of(object.videos)
switchMap(videos => this.videosService.extractVideos({ total: 0, data: videos })),
map(result => result.data),
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)
})
)
)
}
private loadHTMLConfigLocally () {
- const configString = window['PeerTubeServerConfig']
+ // FIXME: typings
+ const configString = (window as any)['PeerTubeServerConfig']
if (!configString) {
throw new Error('Could not find PeerTubeServerConfig in HTML')
}
import { AuthService, AuthStatus } from '@app/core/auth'
import { getBoolOrDefault } from '@root-helpers/local-storage-utils'
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 { NSFWPolicyType } from '@shared/models/videos'
import { ServerService } from '../server'
}
setUserInfo (profile: UserUpdateMe) {
- const localStorageKeys: { [ id in keyof UserUpdateMe ]: string } = {
+ const localStorageKeys = {
nsfwPolicy: UserLocalStorageKeys.NSFW_POLICY,
p2pEnabled: UserLocalStorageKeys.P2P_ENABLED,
autoPlayVideo: UserLocalStorageKeys.AUTO_PLAY_VIDEO,
videoLanguages: UserLocalStorageKeys.VIDEO_LANGUAGES
}
- const obj = Object.keys(localStorageKeys)
+ const obj: [ string, string | boolean | string[] ][] = objectKeysTyped(localStorageKeys)
.filter(key => key in profile)
.map(key => ([ localStorageKeys[key], profile[key] ]))
import { Account } from '@app/shared/shared-main/account/account.model'
+import { objectKeysTyped } from '@shared/core-utils'
import { hasUserRight } from '@shared/core-utils/users'
import {
ActorImage,
}
patch (obj: UserServerModel) {
- for (const key of Object.keys(obj)) {
- this[key] = obj[key]
+ for (const key of objectKeysTyped(obj)) {
+ // FIXME: typings
+ (this as any)[key] = obj[key]
}
if (obj.account !== undefined) {
import { Component, ElementRef, Inject, LOCALE_ID, ViewChild } from '@angular/core'
import { getDevLocale, isOnDevLocale, sortBy } from '@app/helpers'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
+import { objectKeysTyped } from '@shared/core-utils'
import { getCompleteLocale, getShortLocale, I18N_LOCALES } from '@shared/core-utils/i18n'
@Component({
private modalService: NgbModal,
@Inject(LOCALE_ID) private localeId: string
) {
- const l = Object.keys(I18N_LOCALES)
- .map(k => ({ id: k, label: I18N_LOCALES[k], iso: getShortLocale(k) }))
+ const l = objectKeysTyped(I18N_LOCALES)
+ .map(k => ({ id: k, label: I18N_LOCALES[k], iso: getShortLocale(k) }))
this.languages = sortBy(l, 'label')
}
const english = 'English'
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
}
}
import { Component, Input, OnChanges, OnInit } from '@angular/core'
import { VideoChannel } from '../shared-main'
import { Account } from '../shared-main/account/account.model'
+import { objectKeysTyped } from '@shared/core-utils'
type ActorInput = {
name: string
'wxyz': 'dark-blue'
}
- const theme = Object.keys(themes)
- .find(chars => chars.includes(initialLowercase))
+ const theme = objectKeysTyped(themes)
+ .find(chars => chars.includes(initialLowercase))
return themes[theme] || 'blue'
}
SimpleChanges,
Type
} from '@angular/core'
+import { objectKeysTyped } from '@shared/core-utils'
@Injectable()
export class DynamicElementService {
setModel <T> (componentRef: ComponentRef<T>, attributes: Partial<T>) {
const changes: SimpleChanges = {}
- for (const key of Object.keys(attributes)) {
+ for (const key of objectKeysTyped(attributes)) {
const previousValue = componentRef.instance[key]
const newValue = attributes[key]
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
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output } from '@angular/core'
import { AuthService, Notifier } from '@app/core'
import { FindInBulkService } from '@app/shared/shared-search'
+import { objectKeysTyped } from '@shared/core-utils'
import { Video } from '../../shared-main'
import { MiniatureDisplayOptions } from '../../shared-video-miniature'
import { CustomMarkupComponent } from './shared'
ngOnInit () {
if (this.onlyDisplayTitle) {
- for (const key of Object.keys(this.displayOptions)) {
+ for (const key of objectKeysTyped(this.displayOptions)) {
this.displayOptions[key] = false
}
}
import { finalize } from 'rxjs/operators'
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output } from '@angular/core'
import { AuthService, Notifier } from '@app/core'
+import { objectKeysTyped } from '@shared/core-utils'
import { VideoSortField } from '@shared/models'
import { Video, VideoService } from '../../shared-main'
import { MiniatureDisplayOptions } from '../../shared-video-miniature'
ngOnInit () {
if (this.onlyDisplayTitle) {
- for (const key of Object.keys(this.displayOptions)) {
+ for (const key of objectKeysTyped(this.displayOptions)) {
this.displayOptions[key] = false
}
}
import { Injectable } from '@angular/core'
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 { FormReactiveErrors, FormReactiveValidationMessages } from './form-reactive.service'
obj: BuildFormArgument,
defaultValues: BuildFormDefaultValues = {}
) {
- for (const name of Object.keys(obj)) {
+ for (const name of objectKeysTyped(obj)) {
formErrors[name] = ''
const field = obj[name]
if (this.isRecursiveField(field)) {
this.updateFormGroup(
- form[name],
+ // FIXME: typings
+ (form as any)[name],
formErrors[name] as FormReactiveErrors,
validationMessages[name] as FormReactiveValidationMessages,
obj[name] as BuildFormArgument,
const defaultValue = defaultValues[name] || ''
form.addControl(
- name,
+ name + '',
new FormControl(defaultValue, field?.VALIDATORS as ValidatorFn[], field?.ASYNC_VALIDATORS as AsyncValidatorFn[])
)
}
updateTreeValidity (group: FormGroup | FormArray): void {
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) {
this.updateTreeValidity(abstractControl)
}
}
- private getSVGContent (options: { name: string }) {
+ private getSVGContent (options: { name: GlobalIconName }) {
return icons[options.name]
}
}
import { HttpClient } from '@angular/common/http'
import { Injectable } from '@angular/core'
import { MarkdownService, RestExtractor, ServerService } from '@app/core'
+import { objectKeysTyped } from '@shared/core-utils'
import { peertubeTranslate } from '@shared/core-utils/i18n'
import { About } from '@shared/models'
import { environment } from '../../../environments/environment'
hardwareInformation: ''
}
- for (const key of Object.keys(html)) {
+ for (const key of objectKeysTyped(html)) {
html[key] = await this.markdownService.enhancedMarkdownToHTML({ markdown: about.instance[key] })
}
}
private createMarkdownList (rules: string[]) {
- const rulesToText = {
+ const rulesToText: { [id: string]: string } = {
emphasis: $localize`Emphasis`,
link: $localize`Links`,
newline: $localize`New lines`,
import { getAbsoluteAPIUrl } from '@app/helpers'
import { VideoPrivacy, VideoScheduleUpdate, VideoUpdate } from '@shared/models'
import { VideoDetails } from './video-details.model'
+import { objectKeysTyped } from '@shared/core-utils'
export class VideoEdit implements VideoUpdate {
static readonly SPECIAL_SCHEDULED_PRIVACY = -1
}
patch (values: { [ id: string ]: any }) {
- Object.keys(values).forEach((key) => {
- this[key] = values[key]
+ objectKeysTyped(values).forEach(key => {
+ // FIXME: typings
+ (this as any)[key] = values[key]
})
// If schedule publication, the video is private and will be changed to public privacy
includeVideoInPlaylist: false
}, {
set: (target, prop, value) => {
- target[prop] = value
+ // FIXME: typings
+ (target as any)[prop] = value
if (prop === 'embedP2P') {
// Auto enabled warning title if P2P is enabled
-import { mapValues, pick } from 'lodash-es'
+import { mapValues } from 'lodash-es'
import { firstValueFrom } from 'rxjs'
import { tap } from 'rxjs/operators'
import { Component, ElementRef, Inject, LOCALE_ID, ViewChild } from '@angular/core'
import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'
import { logger } from '@root-helpers/logger'
import { videoRequiresAuth } from '@root-helpers/video'
+import { objectKeysTyped, pick } from '@shared/core-utils'
import { VideoCaption, VideoFile, VideoPrivacy } from '@shared/models'
import { BytesPipe, NumberFormatterPipe, VideoDetails, VideoFileTokenService, VideoService } from '../shared-main'
type DownloadType = 'video' | 'subtitles'
-type FileMetadata = { [key: string]: { label: string, value: string } }
+type FileMetadata = { [key: string]: { label: string, value: string | number } }
@Component({
selector: 'my-video-download',
const keyToTranslateFunction = {
encoder: (value: string) => ({ label: $localize`Encoder`, value }),
format_long_name: (value: string) => ({ label: $localize`Format name`, value }),
- size: (value: number) => ({ label: $localize`Size`, value: this.bytesPipe.transform(value, 2) }),
- bit_rate: (value: number) => ({
+ size: (value: number | string) => ({ label: $localize`Size`, value: this.bytesPipe.transform(+value, 2) }),
+ bit_rate: (value: number | string) => ({
label: $localize`Bitrate`,
- value: `${this.numbersPipe.transform(value)}bps`
+ value: `${this.numbersPipe.transform(+value)}bps`
})
}
delete sanitizedFormat.tags
return mapValues(
- pick(sanitizedFormat, Object.keys(keyToTranslateFunction)),
- (val, key) => keyToTranslateFunction[key](val)
+ pick(sanitizedFormat, objectKeysTyped(keyToTranslateFunction)),
+ (val: string, key: keyof typeof keyToTranslateFunction) => keyToTranslateFunction[key](val)
)
}
let keyToTranslateFunction = {
codec_long_name: (value: string) => ({ label: $localize`Codec`, value }),
profile: (value: string) => ({ label: $localize`Profile`, value }),
- bit_rate: (value: number) => ({
+ bit_rate: (value: number | string) => ({
label: $localize`Bitrate`,
- value: `${this.numbersPipe.transform(value)}bps`
+ value: `${this.numbersPipe.transform(+value)}bps`
})
}
if (type === 'video') {
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 }),
avg_frame_rate: (value: string) => ({ label: $localize`Average frame rate`, value }),
pix_fmt: (value: string) => ({ label: $localize`Pixel format`, value })
})
} else {
keyToTranslateFunction = Object.assign(keyToTranslateFunction, {
- sample_rate: (value: number) => ({ label: $localize`Sample rate`, value }),
- channel_layout: (value: number) => ({ label: $localize`Channel Layout`, value })
+ sample_rate: (value: string | number) => ({ label: $localize`Sample rate`, value }),
+ channel_layout: (value: string | number) => ({ label: $localize`Channel Layout`, value })
})
}
return mapValues(
pick(stream, Object.keys(keyToTranslateFunction)),
- (val, key) => keyToTranslateFunction[key](val)
+ (val: string, key: keyof typeof keyToTranslateFunction) => keyToTranslateFunction[key](val)
)
}
if (specificKey && specificKey !== key) continue
// FIXME: typings
- this[key as any] = value
+ (this as any)[key] = value
}
this.buildActiveFilters()
import { AfterContentInit, Component, ContentChildren, EventEmitter, Input, Output, QueryList, TemplateRef } from '@angular/core'
import { ComponentPagination, Notifier, User } from '@app/core'
import { logger } from '@root-helpers/logger'
+import { objectKeysTyped } from '@shared/core-utils'
import { ResultList, VideosExistInPlaylists, VideoSortField } from '@shared/models'
import { PeerTubeTemplateDirective, Video } from '../shared-main'
import { MiniatureDisplayOptions } from './video-miniature.component'
}
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) {
}
private _oneLevelObjClone (obj: { [ id: string ]: any }) {
- const result = {}
+ const result: { [id: string]: any } = {}
const objKeys = Object.keys(obj)
for (let i = 0; i < objKeys.length; i++) {
result[objKeys[i]] = obj[objKeys[i]]
segmentsSha256Url: string
authorizationHeader: () => string
requiresAuth: boolean
-}) {
+}): Promise<SegmentsJSON> {
const { serverUrl, segmentsSha256Url, requiresAuth, authorizationHeader } = options
const headers = requiresAuth && isSameOrigin(serverUrl, segmentsSha256Url)
if (err.message.indexOf('incorrect info hash') !== -1) {
logger.error('Incorrect info hash detected, falling back to torrent file.')
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
private stopTorrent (torrent: WebTorrent.Torrent) {
torrent.pause()
// 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) {
const script = document.createElement('script')
const destructor = () => {
- delete window[vector]
+ delete window[vector as any]
script.onerror = null
script.onload = null
script.remove()
destructor()
}
script.onload = () => {
- resolve(window[vector])
+ resolve(window[vector as any])
destructor()
}
const loader = `import * as m from "${url}"; window.${vector} = m;` // export Module
const wrapper = document.createElement('div')
wrapper.style.position = 'relative'
- wrapper.style['padding-top'] = '56.25%'
+ wrapper.style.paddingTop = '56.25%'
iframe.style.position = 'absolute'
iframe.style.inset = '0'
}
// put it on the window as well as the export
-(window['PeerTubePlayer'] as any) = PeerTubePlayer
+(window as any)['PeerTubePlayer'] = PeerTubePlayer
this.liveManager = new LiveManager(this.playerHTML)
try {
- this.config = JSON.parse(window['PeerTubeServerConfig'])
+ this.config = JSON.parse((window as any)['PeerTubeServerConfig'])
} catch (err) {
logger.error('Cannot parse HTML config.', err)
}
this.player.dispose()
this.playerHTML.removePlayerElement()
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.buildPlayerDock(video)
mainElement.appendChild(iframe)
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...')
await player.ready
"experimentalDecorators": true,
"noImplicitAny": true,
"noImplicitThis": true,
- "suppressImplicitAnyIndexErrors": true,
"alwaysStrict": true,
"importHelpers": true,
"allowSyntheticDefaultImports": true,
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[] {
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') {
return (a: any, b: any) => {
if (a[key] < b[key]) {
export {
pick,
omit,
+ objectKeysTyped,
getKeys,
+ hasKey,
shallowCopy,
sortObjectComparator,
simpleObjectsDeepEqual
export function getCompleteLocale (locale: string) {
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 escapeHTML (stringParam: string) {
if (!stringParam) return ''
- const entityMap = {
+ const entityMap: { [id: string ]: string } = {
'&': '&',
'<': '<',
'>': '>',
export type VideosExistInPlaylists = {
- [videoId: number ]: VideoExistInPlaylist[]
+ [videoId: number]: VideoExistInPlaylist[]
}
export type CachedVideosExistInPlaylists = {
- [videoId: number ]: CachedVideoExistInPlaylist[]
+ [videoId: number]: CachedVideoExistInPlaylist[]
}
export type CachedVideoExistInPlaylist = {