aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--client/src/app/+admin/overview/users/user-list/user-list.component.ts5
-rw-r--r--client/src/app/+admin/plugins/plugin-search/plugin-search.component.ts3
-rw-r--r--client/src/app/+admin/system/logs/log-row.model.ts7
-rw-r--r--client/src/app/+admin/system/logs/logs.component.ts6
-rw-r--r--client/src/app/+admin/system/logs/logs.service.ts4
-rw-r--r--client/src/app/+plugin-pages/plugin-pages.component.ts3
-rw-r--r--client/src/app/+search/shared/abstract-lazy-load.resolver.ts5
-rw-r--r--client/src/app/+video-studio/edit/video-studio-edit.component.ts3
-rw-r--r--client/src/app/+videos/+video-edit/shared/video-edit.component.ts3
-rw-r--r--client/src/app/+videos/+video-edit/video-add-components/video-go-live.component.ts3
-rw-r--r--client/src/app/+videos/+video-edit/video-add-components/video-import-torrent.component.ts3
-rw-r--r--client/src/app/+videos/+video-edit/video-add-components/video-import-url.component.ts3
-rw-r--r--client/src/app/+videos/+video-edit/video-add-components/video-upload.component.ts5
-rw-r--r--client/src/app/+videos/+video-edit/video-update.component.ts5
-rw-r--r--client/src/app/+videos/+video-watch/shared/metadata/video-description.component.ts3
-rw-r--r--client/src/app/+videos/+video-watch/video-watch.component.ts13
-rw-r--r--client/src/app/app.component.ts9
-rw-r--r--client/src/app/core/auth/auth.service.ts12
-rw-r--r--client/src/app/core/notification/notifier.service.ts7
-rw-r--r--client/src/app/core/plugins/hooks.service.ts3
-rw-r--r--client/src/app/core/rest/rest-extractor.service.ts7
-rw-r--r--client/src/app/core/rest/rest-table.ts9
-rw-r--r--client/src/app/core/rest/rest.service.ts6
-rw-r--r--client/src/app/core/routing/custom-reuse-strategy.ts7
-rw-r--r--client/src/app/core/routing/redirect.service.ts23
-rw-r--r--client/src/app/core/routing/scroll.service.ts9
-rw-r--r--client/src/app/core/server/server.service.ts3
-rw-r--r--client/src/app/core/theme/theme.service.ts11
-rw-r--r--client/src/app/core/users/user-local-storage.service.ts7
-rw-r--r--client/src/app/header/search-typeahead.component.ts3
-rw-r--r--client/src/app/helpers/i18n-utils.ts7
-rw-r--r--client/src/app/menu/menu.component.ts6
-rw-r--r--client/src/app/modal/account-setup-warning-modal.component.ts3
-rw-r--r--client/src/app/modal/admin-welcome-modal.component.ts3
-rw-r--r--client/src/app/modal/custom-modal.component.ts5
-rw-r--r--client/src/app/modal/instance-config-warning-modal.component.ts3
-rw-r--r--client/src/app/shared/shared-abuse-list/abuse-list-table.component.ts7
-rw-r--r--client/src/app/shared/shared-abuse-list/abuse-message-modal.component.ts3
-rw-r--r--client/src/app/shared/shared-custom-markup/custom-markup.service.ts5
-rw-r--r--client/src/app/shared/shared-forms/advanced-input-filter.component.ts6
-rw-r--r--client/src/app/shared/shared-main/angular/defer-loading.directive.ts4
-rw-r--r--client/src/app/shared/shared-main/misc/list-overflow.component.ts6
-rw-r--r--client/src/app/shared/shared-main/users/user-notification.model.ts3
-rw-r--r--client/src/app/shared/shared-search/find-in-bulk.service.ts14
-rw-r--r--client/src/app/shared/shared-user-subscription/remote-subscribe.component.ts3
-rw-r--r--client/src/app/shared/shared-user-subscription/user-subscription.service.ts8
-rw-r--r--client/src/app/shared/shared-video-miniature/video-download.component.ts5
-rw-r--r--client/src/app/shared/shared-video-miniature/video-filters-header.component.ts6
-rw-r--r--client/src/app/shared/shared-video-miniature/videos-list.component.ts11
-rw-r--r--client/src/app/shared/shared-video-miniature/videos-selection.component.ts3
-rw-r--r--client/src/app/shared/shared-video-playlist/video-add-to-playlist.component.ts10
-rw-r--r--client/src/app/shared/shared-video-playlist/video-playlist.service.ts8
-rw-r--r--client/src/assets/player/peertube-player-local-storage.ts4
-rw-r--r--client/src/assets/player/peertube-player-manager.ts5
-rw-r--r--client/src/assets/player/shared/manager-options/hls-options-builder.ts3
-rw-r--r--client/src/assets/player/shared/mobile/peertube-mobile-plugin.ts19
-rw-r--r--client/src/assets/player/shared/p2p-media-loader/hls-plugin.ts25
-rw-r--r--client/src/assets/player/shared/p2p-media-loader/p2p-media-loader-plugin.ts7
-rw-r--r--client/src/assets/player/shared/p2p-media-loader/redundancy-url-manager.ts3
-rw-r--r--client/src/assets/player/shared/p2p-media-loader/segment-validator.ts9
-rw-r--r--client/src/assets/player/shared/peertube/peertube-plugin.ts7
-rw-r--r--client/src/assets/player/shared/stats/stats-card.ts3
-rw-r--r--client/src/assets/player/shared/webtorrent/peertube-chunk-store.ts15
-rw-r--r--client/src/assets/player/shared/webtorrent/video-renderer.ts5
-rw-r--r--client/src/assets/player/shared/webtorrent/webtorrent-plugin.ts25
-rw-r--r--client/src/assets/player/translations-manager.ts5
-rw-r--r--client/src/main.ts5
-rw-r--r--client/src/root-helpers/images.ts4
-rw-r--r--client/src/root-helpers/index.ts1
-rw-r--r--client/src/root-helpers/logger.ts138
-rw-r--r--client/src/root-helpers/plugins-manager.ts23
-rw-r--r--client/src/standalone/videos/embed-api.ts4
-rw-r--r--client/src/standalone/videos/embed.ts12
-rw-r--r--client/src/standalone/videos/shared/player-html.ts3
-rw-r--r--client/src/standalone/videos/shared/player-manager-options.ts3
-rw-r--r--client/src/standalone/videos/shared/playlist-fetcher.ts5
-rw-r--r--client/src/standalone/videos/shared/playlist-tracker.ts3
-rw-r--r--client/src/standalone/videos/shared/video-fetcher.ts3
-rw-r--r--client/src/standalone/videos/test-embed.ts11
-rw-r--r--client/src/standalone/videos/tsconfig.json7
-rw-r--r--client/webpack/webpack.video-embed.js2
-rw-r--r--config/default.yaml11
-rw-r--r--config/production.yaml.example11
-rw-r--r--server/controllers/api/server/logs.ts41
-rw-r--r--server/helpers/custom-validators/logs.ts36
-rw-r--r--server/initializers/config.ts7
-rw-r--r--server/initializers/constants.ts6
-rw-r--r--server/middlewares/validators/logs.ts52
-rw-r--r--server/tests/api/check-params/logs.ts59
-rw-r--r--server/tests/api/server/logs.ts65
-rw-r--r--shared/models/server/client-log-create.model.ts11
-rw-r--r--shared/models/server/client-log-level.type.ts1
-rw-r--r--shared/models/server/index.ts4
-rw-r--r--shared/models/server/log-level.type.ts1
-rw-r--r--shared/models/server/server-log-level.type.ts1
-rw-r--r--shared/server-commands/logs/logs-command.ts17
-rw-r--r--support/doc/api/openapi.yaml88
97 files changed, 828 insertions, 261 deletions
diff --git a/client/src/app/+admin/overview/users/user-list/user-list.component.ts b/client/src/app/+admin/overview/users/user-list/user-list.component.ts
index 3e1a5f6b8..99987fdff 100644
--- a/client/src/app/+admin/overview/users/user-list/user-list.component.ts
+++ b/client/src/app/+admin/overview/users/user-list/user-list.component.ts
@@ -2,11 +2,12 @@ import { SortMeta } from 'primeng/api'
2import { Component, OnInit, ViewChild } from '@angular/core' 2import { Component, OnInit, ViewChild } from '@angular/core'
3import { ActivatedRoute, Router } from '@angular/router' 3import { ActivatedRoute, Router } from '@angular/router'
4import { AuthService, ConfirmService, LocalStorageService, Notifier, RestPagination, RestTable, ServerService } from '@app/core' 4import { AuthService, ConfirmService, LocalStorageService, Notifier, RestPagination, RestTable, ServerService } from '@app/core'
5import { prepareIcu, getAPIHost } from '@app/helpers' 5import { getAPIHost, prepareIcu } from '@app/helpers'
6import { AdvancedInputFilter } from '@app/shared/shared-forms' 6import { AdvancedInputFilter } from '@app/shared/shared-forms'
7import { Actor, DropdownAction } from '@app/shared/shared-main' 7import { Actor, DropdownAction } from '@app/shared/shared-main'
8import { AccountMutedStatus, BlocklistService, UserBanModalComponent, UserModerationDisplayType } from '@app/shared/shared-moderation' 8import { AccountMutedStatus, BlocklistService, UserBanModalComponent, UserModerationDisplayType } from '@app/shared/shared-moderation'
9import { UserAdminService } from '@app/shared/shared-users' 9import { UserAdminService } from '@app/shared/shared-users'
10import { logger } from '@root-helpers/logger'
10import { User, UserRole } from '@shared/models' 11import { User, UserRole } from '@shared/models'
11 12
12type UserForList = User & { 13type UserForList = User & {
@@ -149,7 +150,7 @@ export class UserListComponent extends RestTable implements OnInit {
149 this.selectedColumns = JSON.parse(result) 150 this.selectedColumns = JSON.parse(result)
150 return 151 return
151 } catch (err) { 152 } catch (err) {
152 console.error('Cannot load selected columns.', err) 153 logger.error('Cannot load selected columns.', err)
153 } 154 }
154 } 155 }
155 156
diff --git a/client/src/app/+admin/plugins/plugin-search/plugin-search.component.ts b/client/src/app/+admin/plugins/plugin-search/plugin-search.component.ts
index d39c2ea1c..b02c054a2 100644
--- a/client/src/app/+admin/plugins/plugin-search/plugin-search.component.ts
+++ b/client/src/app/+admin/plugins/plugin-search/plugin-search.component.ts
@@ -4,6 +4,7 @@ import { Component, OnInit } from '@angular/core'
4import { ActivatedRoute, Router } from '@angular/router' 4import { ActivatedRoute, Router } from '@angular/router'
5import { PluginApiService } from '@app/+admin/plugins/shared/plugin-api.service' 5import { PluginApiService } from '@app/+admin/plugins/shared/plugin-api.service'
6import { ComponentPagination, ConfirmService, hasMoreItems, Notifier, PluginService } from '@app/core' 6import { ComponentPagination, ConfirmService, hasMoreItems, Notifier, PluginService } from '@app/core'
7import { logger } from '@root-helpers/logger'
7import { PeerTubePluginIndex, PluginType } from '@shared/models' 8import { PeerTubePluginIndex, PluginType } from '@shared/models'
8 9
9@Component({ 10@Component({
@@ -94,7 +95,7 @@ export class PluginSearchComponent implements OnInit {
94 }, 95 },
95 96
96 error: err => { 97 error: err => {
97 console.error(err) 98 logger.error(err)
98 99
99 const message = $localize`The plugin index is not available. Please retry later.` 100 const message = $localize`The plugin index is not available. Please retry later.`
100 this.notifier.error(message) 101 this.notifier.error(message)
diff --git a/client/src/app/+admin/system/logs/log-row.model.ts b/client/src/app/+admin/system/logs/log-row.model.ts
index 615778210..e83c7b064 100644
--- a/client/src/app/+admin/system/logs/log-row.model.ts
+++ b/client/src/app/+admin/system/logs/log-row.model.ts
@@ -1,10 +1,11 @@
1import { LogLevel } from '@shared/models'
2import omit from 'lodash-es/omit' 1import omit from 'lodash-es/omit'
2import { logger } from '@root-helpers/logger'
3import { ServerLogLevel } from '@shared/models'
3 4
4export class LogRow { 5export class LogRow {
5 date: Date 6 date: Date
6 localeDate: string 7 localeDate: string
7 level: LogLevel 8 level: ServerLogLevel
8 message: string 9 message: string
9 meta: string 10 meta: string
10 11
@@ -33,7 +34,7 @@ export class LogRow {
33 this.meta = JSON.stringify(message, null, 2) 34 this.meta = JSON.stringify(message, null, 2)
34 this.message = '' 35 this.message = ''
35 } catch (err) { 36 } catch (err) {
36 console.error('Cannot parse audit message.', err) 37 logger.error('Cannot parse audit message.', err)
37 } 38 }
38 } 39 }
39 } 40 }
diff --git a/client/src/app/+admin/system/logs/logs.component.ts b/client/src/app/+admin/system/logs/logs.component.ts
index 06237522a..939e710d7 100644
--- a/client/src/app/+admin/system/logs/logs.component.ts
+++ b/client/src/app/+admin/system/logs/logs.component.ts
@@ -1,6 +1,6 @@
1import { Component, ElementRef, OnInit, ViewChild } from '@angular/core' 1import { Component, ElementRef, OnInit, ViewChild } from '@angular/core'
2import { LocalStorageService, Notifier } from '@app/core' 2import { LocalStorageService, Notifier } from '@app/core'
3import { LogLevel } from '@shared/models' 3import { ServerLogLevel } from '@shared/models'
4import { LogRow } from './log-row.model' 4import { LogRow } from './log-row.model'
5import { LogsService } from './logs.service' 5import { LogsService } from './logs.service'
6 6
@@ -17,11 +17,11 @@ export class LogsComponent implements OnInit {
17 17
18 logs: LogRow[] = [] 18 logs: LogRow[] = []
19 timeChoices: { id: string, label: string, dateFormat: string }[] = [] 19 timeChoices: { id: string, label: string, dateFormat: string }[] = []
20 levelChoices: { id: LogLevel, label: string }[] = [] 20 levelChoices: { id: ServerLogLevel, label: string }[] = []
21 logTypeChoices: { id: 'audit' | 'standard', label: string }[] = [] 21 logTypeChoices: { id: 'audit' | 'standard', label: string }[] = []
22 22
23 startDate: string 23 startDate: string
24 level: LogLevel 24 level: ServerLogLevel
25 logType: 'audit' | 'standard' 25 logType: 'audit' | 'standard'
26 tagsOneOf: string[] = [] 26 tagsOneOf: string[] = []
27 27
diff --git a/client/src/app/+admin/system/logs/logs.service.ts b/client/src/app/+admin/system/logs/logs.service.ts
index ea7e08b9b..933a074a8 100644
--- a/client/src/app/+admin/system/logs/logs.service.ts
+++ b/client/src/app/+admin/system/logs/logs.service.ts
@@ -3,7 +3,7 @@ import { catchError, map } from 'rxjs/operators'
3import { HttpClient, HttpParams } from '@angular/common/http' 3import { HttpClient, HttpParams } from '@angular/common/http'
4import { Injectable } from '@angular/core' 4import { Injectable } from '@angular/core'
5import { RestExtractor, RestService } from '@app/core' 5import { RestExtractor, RestService } from '@app/core'
6import { LogLevel } from '@shared/models' 6import { ServerLogLevel } from '@shared/models'
7import { environment } from '../../../../environments/environment' 7import { environment } from '../../../../environments/environment'
8import { LogRow } from './log-row.model' 8import { LogRow } from './log-row.model'
9 9
@@ -22,7 +22,7 @@ export class LogsService {
22 isAuditLog: boolean 22 isAuditLog: boolean
23 startDate: string 23 startDate: string
24 tagsOneOf?: string[] 24 tagsOneOf?: string[]
25 level?: LogLevel 25 level?: ServerLogLevel
26 endDate?: string 26 endDate?: string
27 }): Observable<any[]> { 27 }): Observable<any[]> {
28 const { isAuditLog, startDate, endDate, tagsOneOf } = options 28 const { isAuditLog, startDate, endDate, tagsOneOf } = options
diff --git a/client/src/app/+plugin-pages/plugin-pages.component.ts b/client/src/app/+plugin-pages/plugin-pages.component.ts
index 973e4d021..9fe4b413e 100644
--- a/client/src/app/+plugin-pages/plugin-pages.component.ts
+++ b/client/src/app/+plugin-pages/plugin-pages.component.ts
@@ -1,6 +1,7 @@
1import { AfterViewInit, Component, ElementRef, ViewChild } from '@angular/core' 1import { AfterViewInit, Component, ElementRef, ViewChild } from '@angular/core'
2import { ActivatedRoute, Router } from '@angular/router' 2import { ActivatedRoute, Router } from '@angular/router'
3import { PluginService } from '@app/core' 3import { PluginService } from '@app/core'
4import { logger } from '@root-helpers/logger'
4 5
5@Component({ 6@Component({
6 templateUrl: './plugin-pages.component.html' 7 templateUrl: './plugin-pages.component.html'
@@ -26,7 +27,7 @@ export class PluginPagesComponent implements AfterViewInit {
26 27
27 const registered = this.pluginService.getRegisteredClientRoute(path) 28 const registered = this.pluginService.getRegisteredClientRoute(path)
28 if (!registered) { 29 if (!registered) {
29 console.log('Could not find registered route %s.', path, this.pluginService.getAllRegisteredClientRoutes()) 30 logger.info(`Could not find registered route ${path}`, this.pluginService.getAllRegisteredClientRoutes())
30 31
31 return this.router.navigate([ '/404' ], { skipLocationChange: true }) 32 return this.router.navigate([ '/404' ], { skipLocationChange: true })
32 } 33 }
diff --git a/client/src/app/+search/shared/abstract-lazy-load.resolver.ts b/client/src/app/+search/shared/abstract-lazy-load.resolver.ts
index b18a5b64d..7551d977d 100644
--- a/client/src/app/+search/shared/abstract-lazy-load.resolver.ts
+++ b/client/src/app/+search/shared/abstract-lazy-load.resolver.ts
@@ -1,6 +1,7 @@
1import { Observable } from 'rxjs' 1import { Observable } from 'rxjs'
2import { map } from 'rxjs/operators' 2import { map } from 'rxjs/operators'
3import { ActivatedRouteSnapshot, Resolve, Router } from '@angular/router' 3import { ActivatedRouteSnapshot, Resolve, Router } from '@angular/router'
4import { logger } from '@root-helpers/logger'
4import { ResultList } from '@shared/models' 5import { ResultList } from '@shared/models'
5 6
6export abstract class AbstractLazyLoadResolver <T> implements Resolve<any> { 7export abstract class AbstractLazyLoadResolver <T> implements Resolve<any> {
@@ -10,7 +11,7 @@ export abstract class AbstractLazyLoadResolver <T> implements Resolve<any> {
10 const url = route.params.url 11 const url = route.params.url
11 12
12 if (!url) { 13 if (!url) {
13 console.error('Could not find url param.', { params: route.params }) 14 logger.error('Could not find url param.', { params: route.params })
14 return this.router.navigateByUrl('/404') 15 return this.router.navigateByUrl('/404')
15 } 16 }
16 17
@@ -18,7 +19,7 @@ export abstract class AbstractLazyLoadResolver <T> implements Resolve<any> {
18 .pipe( 19 .pipe(
19 map(result => { 20 map(result => {
20 if (result.data.length !== 1) { 21 if (result.data.length !== 1) {
21 console.error('Cannot find result for this URL') 22 logger.error('Cannot find result for this URL')
22 return this.router.navigateByUrl('/404') 23 return this.router.navigateByUrl('/404')
23 } 24 }
24 25
diff --git a/client/src/app/+video-studio/edit/video-studio-edit.component.ts b/client/src/app/+video-studio/edit/video-studio-edit.component.ts
index 392b65767..bf91c237a 100644
--- a/client/src/app/+video-studio/edit/video-studio-edit.component.ts
+++ b/client/src/app/+video-studio/edit/video-studio-edit.component.ts
@@ -4,6 +4,7 @@ import { ConfirmService, Notifier, ServerService } from '@app/core'
4import { FormReactive, FormValidatorService } from '@app/shared/shared-forms' 4import { FormReactive, FormValidatorService } from '@app/shared/shared-forms'
5import { VideoDetails } from '@app/shared/shared-main' 5import { VideoDetails } from '@app/shared/shared-main'
6import { LoadingBarService } from '@ngx-loading-bar/core' 6import { LoadingBarService } from '@ngx-loading-bar/core'
7import { logger } from '@root-helpers/logger'
7import { secondsToTime } from '@shared/core-utils' 8import { secondsToTime } from '@shared/core-utils'
8import { VideoStudioTask, VideoStudioTaskCut } from '@shared/models' 9import { VideoStudioTask, VideoStudioTaskCut } from '@shared/models'
9import { VideoStudioService } from '../shared' 10import { VideoStudioService } from '../shared'
@@ -97,7 +98,7 @@ export class VideoStudioEditComponent extends FormReactive implements OnInit {
97 this.loadingBar.useRef().complete() 98 this.loadingBar.useRef().complete()
98 this.isRunningEdition = false 99 this.isRunningEdition = false
99 this.notifier.error(err.message) 100 this.notifier.error(err.message)
100 console.error(err) 101 logger.error(err)
101 } 102 }
102 }) 103 })
103 } 104 }
diff --git a/client/src/app/+videos/+video-edit/shared/video-edit.component.ts b/client/src/app/+videos/+video-edit/shared/video-edit.component.ts
index c74ef5731..99f8c9034 100644
--- a/client/src/app/+videos/+video-edit/shared/video-edit.component.ts
+++ b/client/src/app/+videos/+video-edit/shared/video-edit.component.ts
@@ -38,6 +38,7 @@ import { VideoCaptionAddModalComponent } from './video-caption-add-modal.compone
38import { VideoCaptionEditModalComponent } from './video-caption-edit-modal/video-caption-edit-modal.component' 38import { VideoCaptionEditModalComponent } from './video-caption-edit-modal/video-caption-edit-modal.component'
39import { VideoEditType } from './video-edit.type' 39import { VideoEditType } from './video-edit.type'
40import { VideoSource } from '@shared/models/videos/video-source' 40import { VideoSource } from '@shared/models/videos/video-source'
41import { logger } from '@root-helpers/logger'
41 42
42type VideoLanguages = VideoConstant<string> & { group?: string } 43type VideoLanguages = VideoConstant<string> & { group?: string }
43type PluginField = { 44type PluginField = {
@@ -443,7 +444,7 @@ export class VideoEditComponent implements OnInit, OnDestroy {
443 444
444 const oldChannel = this.userVideoChannels.find(c => c.id === oldChannelId) 445 const oldChannel = this.userVideoChannels.find(c => c.id === oldChannelId)
445 if (!newChannel || !oldChannel) { 446 if (!newChannel || !oldChannel) {
446 console.error('Cannot find new or old channel.') 447 logger.error('Cannot find new or old channel.')
447 return 448 return
448 } 449 }
449 450
diff --git a/client/src/app/+videos/+video-edit/video-add-components/video-go-live.component.ts b/client/src/app/+videos/+video-edit/video-add-components/video-go-live.component.ts
index 80e5a73da..91eb66931 100644
--- a/client/src/app/+videos/+video-edit/video-add-components/video-go-live.component.ts
+++ b/client/src/app/+videos/+video-edit/video-add-components/video-go-live.component.ts
@@ -7,6 +7,7 @@ import { FormValidatorService } from '@app/shared/shared-forms'
7import { Video, VideoCaptionService, VideoEdit, VideoService } from '@app/shared/shared-main' 7import { Video, VideoCaptionService, VideoEdit, VideoService } from '@app/shared/shared-main'
8import { LiveVideoService } from '@app/shared/shared-video-live' 8import { LiveVideoService } from '@app/shared/shared-video-live'
9import { LoadingBarService } from '@ngx-loading-bar/core' 9import { LoadingBarService } from '@ngx-loading-bar/core'
10import { logger } from '@root-helpers/logger'
10import { LiveVideo, LiveVideoCreate, LiveVideoLatencyMode, LiveVideoUpdate, PeerTubeProblemDocument, ServerErrorCode } from '@shared/models' 11import { LiveVideo, LiveVideoCreate, LiveVideoLatencyMode, LiveVideoUpdate, PeerTubeProblemDocument, ServerErrorCode } from '@shared/models'
11import { VideoSend } from './video-send' 12import { VideoSend } from './video-send'
12 13
@@ -141,7 +142,7 @@ export class VideoGoLiveComponent extends VideoSend implements OnInit, AfterView
141 error: err => { 142 error: err => {
142 this.error = err.message 143 this.error = err.message
143 scrollToTop() 144 scrollToTop()
144 console.error(err) 145 logger.error(err)
145 } 146 }
146 }) 147 })
147 } 148 }
diff --git a/client/src/app/+videos/+video-edit/video-add-components/video-import-torrent.component.ts b/client/src/app/+videos/+video-edit/video-add-components/video-import-torrent.component.ts
index da4996902..7b9531d27 100644
--- a/client/src/app/+videos/+video-edit/video-add-components/video-import-torrent.component.ts
+++ b/client/src/app/+videos/+video-edit/video-add-components/video-import-torrent.component.ts
@@ -6,6 +6,7 @@ import { scrollToTop } from '@app/helpers'
6import { FormValidatorService } from '@app/shared/shared-forms' 6import { FormValidatorService } from '@app/shared/shared-forms'
7import { VideoCaptionService, VideoEdit, VideoImportService, VideoService } from '@app/shared/shared-main' 7import { VideoCaptionService, VideoEdit, VideoImportService, VideoService } from '@app/shared/shared-main'
8import { LoadingBarService } from '@ngx-loading-bar/core' 8import { LoadingBarService } from '@ngx-loading-bar/core'
9import { logger } from '@root-helpers/logger'
9import { PeerTubeProblemDocument, ServerErrorCode, VideoUpdate } from '@shared/models' 10import { PeerTubeProblemDocument, ServerErrorCode, VideoUpdate } from '@shared/models'
10import { hydrateFormFromVideo } from '../shared/video-edit-utils' 11import { hydrateFormFromVideo } from '../shared/video-edit-utils'
11import { VideoSend } from './video-send' 12import { VideoSend } from './video-send'
@@ -139,7 +140,7 @@ export class VideoImportTorrentComponent extends VideoSend implements OnInit, Af
139 error: err => { 140 error: err => {
140 this.error = err.message 141 this.error = err.message
141 scrollToTop() 142 scrollToTop()
142 console.error(err) 143 logger.error(err)
143 } 144 }
144 }) 145 })
145 } 146 }
diff --git a/client/src/app/+videos/+video-edit/video-add-components/video-import-url.component.ts b/client/src/app/+videos/+video-edit/video-add-components/video-import-url.component.ts
index 971a2a070..4ef7d1321 100644
--- a/client/src/app/+videos/+video-edit/video-add-components/video-import-url.component.ts
+++ b/client/src/app/+videos/+video-edit/video-add-components/video-import-url.component.ts
@@ -7,6 +7,7 @@ import { scrollToTop } from '@app/helpers'
7import { FormValidatorService } from '@app/shared/shared-forms' 7import { FormValidatorService } from '@app/shared/shared-forms'
8import { VideoCaptionService, VideoEdit, VideoImportService, VideoService } from '@app/shared/shared-main' 8import { VideoCaptionService, VideoEdit, VideoImportService, VideoService } from '@app/shared/shared-main'
9import { LoadingBarService } from '@ngx-loading-bar/core' 9import { LoadingBarService } from '@ngx-loading-bar/core'
10import { logger } from '@root-helpers/logger'
10import { VideoUpdate } from '@shared/models' 11import { VideoUpdate } from '@shared/models'
11import { hydrateFormFromVideo } from '../shared/video-edit-utils' 12import { hydrateFormFromVideo } from '../shared/video-edit-utils'
12import { VideoSend } from './video-send' 13import { VideoSend } from './video-send'
@@ -128,7 +129,7 @@ export class VideoImportUrlComponent extends VideoSend implements OnInit, AfterV
128 error: err => { 129 error: err => {
129 this.error = err.message 130 this.error = err.message
130 scrollToTop() 131 scrollToTop()
131 console.error(err) 132 logger.error(err)
132 } 133 }
133 }) 134 })
134 } 135 }
diff --git a/client/src/app/+videos/+video-edit/video-add-components/video-upload.component.ts b/client/src/app/+videos/+video-edit/video-add-components/video-upload.component.ts
index 663955d27..66a3967c7 100644
--- a/client/src/app/+videos/+video-edit/video-add-components/video-upload.component.ts
+++ b/client/src/app/+videos/+video-edit/video-add-components/video-upload.component.ts
@@ -1,6 +1,5 @@
1import { truncate } from 'lodash-es' 1import { truncate } from 'lodash-es'
2import { UploadState, UploadxOptions, UploadxService } from 'ngx-uploadx' 2import { UploadState, UploadxOptions, UploadxService } from 'ngx-uploadx'
3import { isIOS } from '@root-helpers/web-browser'
4import { HttpErrorResponse, HttpEventType, HttpHeaders } from '@angular/common/http' 3import { HttpErrorResponse, HttpEventType, HttpHeaders } from '@angular/common/http'
5import { AfterViewInit, Component, ElementRef, EventEmitter, OnDestroy, OnInit, Output, ViewChild } from '@angular/core' 4import { AfterViewInit, Component, ElementRef, EventEmitter, OnDestroy, OnInit, Output, ViewChild } from '@angular/core'
6import { ActivatedRoute, Router } from '@angular/router' 5import { ActivatedRoute, Router } from '@angular/router'
@@ -9,6 +8,8 @@ import { genericUploadErrorHandler, scrollToTop } from '@app/helpers'
9import { FormValidatorService } from '@app/shared/shared-forms' 8import { FormValidatorService } from '@app/shared/shared-forms'
10import { BytesPipe, Video, VideoCaptionService, VideoEdit, VideoService } from '@app/shared/shared-main' 9import { BytesPipe, Video, VideoCaptionService, VideoEdit, VideoService } from '@app/shared/shared-main'
11import { LoadingBarService } from '@ngx-loading-bar/core' 10import { LoadingBarService } from '@ngx-loading-bar/core'
11import { logger } from '@root-helpers/logger'
12import { isIOS } from '@root-helpers/web-browser'
12import { HttpStatusCode, VideoCreateResult } from '@shared/models' 13import { HttpStatusCode, VideoCreateResult } from '@shared/models'
13import { UploaderXFormData } from './uploaderx-form-data' 14import { UploaderXFormData } from './uploaderx-form-data'
14import { VideoSend } from './video-send' 15import { VideoSend } from './video-send'
@@ -264,7 +265,7 @@ export class VideoUploadComponent extends VideoSend implements OnInit, OnDestroy
264 error: err => { 265 error: err => {
265 this.error = err.message 266 this.error = err.message
266 scrollToTop() 267 scrollToTop()
267 console.error(err) 268 logger.error(err)
268 } 269 }
269 }) 270 })
270 } 271 }
diff --git a/client/src/app/+videos/+video-edit/video-update.component.ts b/client/src/app/+videos/+video-edit/video-update.component.ts
index 13e786a8e..ed17dff06 100644
--- a/client/src/app/+videos/+video-edit/video-update.component.ts
+++ b/client/src/app/+videos/+video-edit/video-update.component.ts
@@ -8,9 +8,10 @@ import { FormReactive, FormValidatorService } from '@app/shared/shared-forms'
8import { Video, VideoCaptionEdit, VideoCaptionService, VideoDetails, VideoEdit, VideoService } from '@app/shared/shared-main' 8import { Video, VideoCaptionEdit, VideoCaptionService, VideoDetails, VideoEdit, VideoService } from '@app/shared/shared-main'
9import { LiveVideoService } from '@app/shared/shared-video-live' 9import { LiveVideoService } from '@app/shared/shared-video-live'
10import { LoadingBarService } from '@ngx-loading-bar/core' 10import { LoadingBarService } from '@ngx-loading-bar/core'
11import { logger } from '@root-helpers/logger'
11import { LiveVideo, LiveVideoUpdate, VideoPrivacy } from '@shared/models' 12import { LiveVideo, LiveVideoUpdate, VideoPrivacy } from '@shared/models'
12import { hydrateFormFromVideo } from './shared/video-edit-utils'
13import { VideoSource } from '@shared/models/videos/video-source' 13import { VideoSource } from '@shared/models/videos/video-source'
14import { hydrateFormFromVideo } from './shared/video-edit-utils'
14 15
15@Component({ 16@Component({
16 selector: 'my-videos-update', 17 selector: 'my-videos-update',
@@ -156,7 +157,7 @@ export class VideoUpdateComponent extends FormReactive implements OnInit {
156 this.loadingBar.useRef().complete() 157 this.loadingBar.useRef().complete()
157 this.isUpdatingVideo = false 158 this.isUpdatingVideo = false
158 this.notifier.error(err.message) 159 this.notifier.error(err.message)
159 console.error(err) 160 logger.error(err)
160 } 161 }
161 }) 162 })
162 } 163 }
diff --git a/client/src/app/+videos/+video-watch/shared/metadata/video-description.component.ts b/client/src/app/+videos/+video-watch/shared/metadata/video-description.component.ts
index e002b3c22..b5444facb 100644
--- a/client/src/app/+videos/+video-watch/shared/metadata/video-description.component.ts
+++ b/client/src/app/+videos/+video-watch/shared/metadata/video-description.component.ts
@@ -1,6 +1,7 @@
1import { Component, EventEmitter, Input, OnChanges, Output } from '@angular/core' 1import { Component, EventEmitter, Input, OnChanges, Output } from '@angular/core'
2import { MarkdownService, Notifier } from '@app/core' 2import { MarkdownService, Notifier } from '@app/core'
3import { VideoDetails, VideoService } from '@app/shared/shared-main' 3import { VideoDetails, VideoService } from '@app/shared/shared-main'
4import { logger } from '@root-helpers/logger'
4 5
5@Component({ 6@Component({
6 selector: 'my-video-description', 7 selector: 'my-video-description',
@@ -75,7 +76,7 @@ export class VideoDescriptionComponent implements OnChanges {
75 private updateVideoDescription (description: string) { 76 private updateVideoDescription (description: string) {
76 this.video.description = description 77 this.video.description = description
77 this.setVideoDescriptionHTML() 78 this.setVideoDescriptionHTML()
78 .catch(err => console.error(err)) 79 .catch(err => logger.error(err))
79 } 80 }
80 81
81 private async setVideoDescriptionHTML () { 82 private async setVideoDescriptionHTML () {
diff --git a/client/src/app/+videos/+video-watch/video-watch.component.ts b/client/src/app/+videos/+video-watch/video-watch.component.ts
index 6a3bd1522..292ce6441 100644
--- a/client/src/app/+videos/+video-watch/video-watch.component.ts
+++ b/client/src/app/+videos/+video-watch/video-watch.component.ts
@@ -24,6 +24,7 @@ import { Video, VideoCaptionService, VideoDetails, VideoService } from '@app/sha
24import { SubscribeButtonComponent } from '@app/shared/shared-user-subscription' 24import { SubscribeButtonComponent } from '@app/shared/shared-user-subscription'
25import { LiveVideoService } from '@app/shared/shared-video-live' 25import { LiveVideoService } from '@app/shared/shared-video-live'
26import { VideoPlaylist, VideoPlaylistService } from '@app/shared/shared-video-playlist' 26import { VideoPlaylist, VideoPlaylistService } from '@app/shared/shared-video-playlist'
27import { logger } from '@root-helpers/logger'
27import { isP2PEnabled } from '@root-helpers/video' 28import { isP2PEnabled } from '@root-helpers/video'
28import { timeToInt } from '@shared/core-utils' 29import { timeToInt } from '@shared/core-utils'
29import { 30import {
@@ -225,7 +226,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
225 : parseInt(positionParam + '', 10) 226 : parseInt(positionParam + '', 10)
226 227
227 if (isNaN(this.playlistPosition)) { 228 if (isNaN(this.playlistPosition)) {
228 console.error(`playlistPosition query param '${positionParam}' was parsed as NaN, defaulting to 1.`) 229 logger.error(`playlistPosition query param '${positionParam}' was parsed as NaN, defaulting to 1.`)
229 this.playlistPosition = 1 230 this.playlistPosition = 1
230 } 231 }
231 232
@@ -378,7 +379,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
378 } 379 }
379 380
380 this.buildPlayer(urlOptions, loggedInOrAnonymousUser) 381 this.buildPlayer(urlOptions, loggedInOrAnonymousUser)
381 .catch(err => console.error('Cannot build the player', err)) 382 .catch(err => logger.error('Cannot build the player', err))
382 383
383 this.setOpenGraphTags() 384 this.setOpenGraphTags()
384 385
@@ -550,7 +551,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
550 this.player.dispose() 551 this.player.dispose()
551 this.player = undefined 552 this.player = undefined
552 } catch (err) { 553 } catch (err) {
553 console.error('Cannot dispose player.', err) 554 logger.error('Cannot dispose player.', err)
554 } 555 }
555 } 556 }
556 557
@@ -717,7 +718,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
717 private handleLiveStateChange (newState: VideoState) { 718 private handleLiveStateChange (newState: VideoState) {
718 if (newState !== VideoState.PUBLISHED) return 719 if (newState !== VideoState.PUBLISHED) return
719 720
720 console.log('Loading video after live update.') 721 logger.info('Loading video after live update.')
721 722
722 const videoUUID = this.video.uuid 723 const videoUUID = this.video.uuid
723 724
@@ -728,11 +729,11 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
728 729
729 private handleLiveViewsChange (newViewers: number) { 730 private handleLiveViewsChange (newViewers: number) {
730 if (!this.video) { 731 if (!this.video) {
731 console.error('Cannot update video live views because video is no defined.') 732 logger.error('Cannot update video live views because video is no defined.')
732 return 733 return
733 } 734 }
734 735
735 console.log('Updating live views.') 736 logger.info('Updating live views.')
736 737
737 this.video.viewers = newViewers 738 this.video.viewers = newViewers
738 } 739 }
diff --git a/client/src/app/app.component.ts b/client/src/app/app.component.ts
index 8fdab0c40..a2ad4806c 100644
--- a/client/src/app/app.component.ts
+++ b/client/src/app/app.component.ts
@@ -1,5 +1,5 @@
1import { Hotkey, HotkeysService } from 'angular2-hotkeys' 1import { Hotkey, HotkeysService } from 'angular2-hotkeys'
2import { forkJoin, delay } from 'rxjs' 2import { delay, forkJoin } from 'rxjs'
3import { filter, first, map } from 'rxjs/operators' 3import { filter, first, map } from 'rxjs/operators'
4import { DOCUMENT, getLocaleDirection, PlatformLocation } from '@angular/common' 4import { DOCUMENT, getLocaleDirection, PlatformLocation } from '@angular/common'
5import { AfterViewInit, Component, Inject, LOCALE_ID, OnInit, ViewChild } from '@angular/core' 5import { AfterViewInit, Component, Inject, LOCALE_ID, OnInit, ViewChild } from '@angular/core'
@@ -20,18 +20,19 @@ import {
20import { HooksService } from '@app/core/plugins/hooks.service' 20import { HooksService } from '@app/core/plugins/hooks.service'
21import { PluginService } from '@app/core/plugins/plugin.service' 21import { PluginService } from '@app/core/plugins/plugin.service'
22import { AccountSetupWarningModalComponent } from '@app/modal/account-setup-warning-modal.component' 22import { AccountSetupWarningModalComponent } from '@app/modal/account-setup-warning-modal.component'
23import { AdminWelcomeModalComponent } from '@app/modal/admin-welcome-modal.component'
23import { CustomModalComponent } from '@app/modal/custom-modal.component' 24import { CustomModalComponent } from '@app/modal/custom-modal.component'
24import { InstanceConfigWarningModalComponent } from '@app/modal/instance-config-warning-modal.component' 25import { InstanceConfigWarningModalComponent } from '@app/modal/instance-config-warning-modal.component'
25import { AdminWelcomeModalComponent } from '@app/modal/admin-welcome-modal.component'
26import { NgbConfig, NgbModal } from '@ng-bootstrap/ng-bootstrap' 26import { NgbConfig, NgbModal } from '@ng-bootstrap/ng-bootstrap'
27import { LoadingBarService } from '@ngx-loading-bar/core' 27import { LoadingBarService } from '@ngx-loading-bar/core'
28import { logger } from '@root-helpers/logger'
28import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage' 29import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage'
29import { getShortLocale } from '@shared/core-utils/i18n' 30import { getShortLocale } from '@shared/core-utils/i18n'
30import { BroadcastMessageLevel, HTMLServerConfig, UserRole } from '@shared/models' 31import { BroadcastMessageLevel, HTMLServerConfig, UserRole } from '@shared/models'
31import { MenuService } from './core/menu/menu.service' 32import { MenuService } from './core/menu/menu.service'
32import { POP_STATE_MODAL_DISMISS } from './helpers' 33import { POP_STATE_MODAL_DISMISS } from './helpers'
33import { InstanceService } from './shared/shared-instance'
34import { GlobalIconName } from './shared/shared-icons' 34import { GlobalIconName } from './shared/shared-icons'
35import { InstanceService } from './shared/shared-instance'
35 36
36@Component({ 37@Component({
37 selector: 'my-app', 38 selector: 'my-app',
@@ -221,7 +222,7 @@ export class AppComponent implements OnInit, AfterViewInit {
221 /* eslint-disable no-eval */ 222 /* eslint-disable no-eval */
222 eval(this.serverConfig.instance.customizations.javascript) 223 eval(this.serverConfig.instance.customizations.javascript)
223 } catch (err) { 224 } catch (err) {
224 console.error('Cannot eval custom JavaScript.', err) 225 logger.error('Cannot eval custom JavaScript.', err)
225 } 226 }
226 } 227 }
227 } 228 }
diff --git a/client/src/app/core/auth/auth.service.ts b/client/src/app/core/auth/auth.service.ts
index 2ac88c185..ece6bc5d1 100644
--- a/client/src/app/core/auth/auth.service.ts
+++ b/client/src/app/core/auth/auth.service.ts
@@ -5,7 +5,7 @@ import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http'
5import { Injectable } from '@angular/core' 5import { Injectable } from '@angular/core'
6import { Router } from '@angular/router' 6import { Router } from '@angular/router'
7import { Notifier } from '@app/core/notification/notifier.service' 7import { Notifier } from '@app/core/notification/notifier.service'
8import { objectToUrlEncoded, peertubeLocalStorage, UserTokens } from '@root-helpers/index' 8import { logger, objectToUrlEncoded, peertubeLocalStorage, UserTokens } from '@root-helpers/index'
9import { HttpStatusCode, MyUser as UserServerModel, OAuthClientLocal, User, UserLogin, UserRefreshToken } from '@shared/models' 9import { HttpStatusCode, MyUser as UserServerModel, OAuthClientLocal, User, UserLogin, UserRefreshToken } from '@shared/models'
10import { environment } from '../../../environments/environment' 10import { environment } from '../../../environments/environment'
11import { RestExtractor } from '../rest/rest-extractor.service' 11import { RestExtractor } from '../rest/rest-extractor.service'
@@ -90,7 +90,7 @@ export class AuthService {
90 peertubeLocalStorage.setItem(AuthService.LOCAL_STORAGE_OAUTH_CLIENT_KEYS.CLIENT_ID, this.clientId) 90 peertubeLocalStorage.setItem(AuthService.LOCAL_STORAGE_OAUTH_CLIENT_KEYS.CLIENT_ID, this.clientId)
91 peertubeLocalStorage.setItem(AuthService.LOCAL_STORAGE_OAUTH_CLIENT_KEYS.CLIENT_SECRET, this.clientSecret) 91 peertubeLocalStorage.setItem(AuthService.LOCAL_STORAGE_OAUTH_CLIENT_KEYS.CLIENT_SECRET, this.clientSecret)
92 92
93 console.log('Client credentials loaded.') 93 logger.info('Client credentials loaded.')
94 }, 94 },
95 95
96 error: err => { 96 error: err => {
@@ -177,7 +177,7 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
177 } 177 }
178 }, 178 },
179 179
180 error: err => console.error(err) 180 error: err => logger.error(err)
181 }) 181 })
182 182
183 this.user = null 183 this.user = null
@@ -190,7 +190,7 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
190 refreshAccessToken () { 190 refreshAccessToken () {
191 if (this.refreshingTokenObservable) return this.refreshingTokenObservable 191 if (this.refreshingTokenObservable) return this.refreshingTokenObservable
192 192
193 console.log('Refreshing token...') 193 logger.info('Refreshing token...')
194 194
195 const refreshToken = this.getRefreshToken() 195 const refreshToken = this.getRefreshToken()
196 196
@@ -212,8 +212,8 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
212 catchError(err => { 212 catchError(err => {
213 this.refreshingTokenObservable = null 213 this.refreshingTokenObservable = null
214 214
215 console.error(err) 215 logger.error(err)
216 console.log('Cannot refresh token -> logout...') 216 logger.info('Cannot refresh token -> logout...')
217 this.logout() 217 this.logout()
218 this.router.navigate([ '/login' ]) 218 this.router.navigate([ '/login' ])
219 219
diff --git a/client/src/app/core/notification/notifier.service.ts b/client/src/app/core/notification/notifier.service.ts
index 165bb0c76..15af5c1b6 100644
--- a/client/src/app/core/notification/notifier.service.ts
+++ b/client/src/app/core/notification/notifier.service.ts
@@ -1,5 +1,6 @@
1import { MessageService } from 'primeng/api' 1import { MessageService } from 'primeng/api'
2import { Injectable } from '@angular/core' 2import { Injectable } from '@angular/core'
3import { logger } from '@root-helpers/logger'
3 4
4@Injectable() 5@Injectable()
5export class Notifier { 6export class Notifier {
@@ -10,21 +11,21 @@ export class Notifier {
10 info (text: string, title?: string, timeout?: number, sticky?: boolean) { 11 info (text: string, title?: string, timeout?: number, sticky?: boolean) {
11 if (!title) title = $localize`Info` 12 if (!title) title = $localize`Info`
12 13
13 console.info(`${title}: ${text}`) 14 logger.info(`${title}: ${text}`)
14 return this.notify('info', text, title, timeout, sticky) 15 return this.notify('info', text, title, timeout, sticky)
15 } 16 }
16 17
17 error (text: string, title?: string, timeout?: number, sticky?: boolean) { 18 error (text: string, title?: string, timeout?: number, sticky?: boolean) {
18 if (!title) title = $localize`Error` 19 if (!title) title = $localize`Error`
19 20
20 console.error(`${title}: ${text}`) 21 logger.error(`${title}: ${text}`)
21 return this.notify('error', text, title, timeout, sticky) 22 return this.notify('error', text, title, timeout, sticky)
22 } 23 }
23 24
24 success (text: string, title?: string, timeout?: number, sticky?: boolean) { 25 success (text: string, title?: string, timeout?: number, sticky?: boolean) {
25 if (!title) title = $localize`Success` 26 if (!title) title = $localize`Success`
26 27
27 console.log(`${title}: ${text}`) 28 logger.info(`${title}: ${text}`)
28 return this.notify('success', text, title, timeout, sticky) 29 return this.notify('success', text, title, timeout, sticky)
29 } 30 }
30 31
diff --git a/client/src/app/core/plugins/hooks.service.ts b/client/src/app/core/plugins/hooks.service.ts
index 062083fd1..7fd56d92e 100644
--- a/client/src/app/core/plugins/hooks.service.ts
+++ b/client/src/app/core/plugins/hooks.service.ts
@@ -2,6 +2,7 @@ import { from, Observable } from 'rxjs'
2import { mergeMap, switchMap } from 'rxjs/operators' 2import { mergeMap, switchMap } from 'rxjs/operators'
3import { Injectable } from '@angular/core' 3import { Injectable } from '@angular/core'
4import { PluginService } from '@app/core/plugins/plugin.service' 4import { PluginService } from '@app/core/plugins/plugin.service'
5import { logger } from '@root-helpers/logger'
5import { ClientActionHookName, ClientFilterHookName, PluginClientScope } from '@shared/models' 6import { ClientActionHookName, ClientFilterHookName, PluginClientScope } from '@shared/models'
6import { AuthService, AuthStatus } from '../auth' 7import { AuthService, AuthStatus } from '../auth'
7 8
@@ -50,7 +51,7 @@ export class HooksService {
50 runAction<T, U extends ClientActionHookName> (hookName: U, scope: PluginClientScope, params?: T) { 51 runAction<T, U extends ClientActionHookName> (hookName: U, scope: PluginClientScope, params?: T) {
51 this.pluginService.ensurePluginsAreLoaded(scope) 52 this.pluginService.ensurePluginsAreLoaded(scope)
52 .then(() => this.pluginService.runHook(hookName, undefined, params)) 53 .then(() => this.pluginService.runHook(hookName, undefined, params))
53 .catch((err: any) => console.error('Fatal hook error.', { err })) 54 .catch((err: any) => logger.error('Fatal hook error.', err))
54 } 55 }
55 56
56 async wrapObject<T, U extends ClientFilterHookName> (result: T, scope: PluginClientScope, hookName: U) { 57 async wrapObject<T, U extends ClientFilterHookName> (result: T, scope: PluginClientScope, hookName: U) {
diff --git a/client/src/app/core/rest/rest-extractor.service.ts b/client/src/app/core/rest/rest-extractor.service.ts
index 86c7484a5..8a2974563 100644
--- a/client/src/app/core/rest/rest-extractor.service.ts
+++ b/client/src/app/core/rest/rest-extractor.service.ts
@@ -3,6 +3,7 @@ import { Injectable } from '@angular/core'
3import { Router } from '@angular/router' 3import { Router } from '@angular/router'
4import { dateToHuman } from '@app/helpers' 4import { dateToHuman } from '@app/helpers'
5import { HttpStatusCode, ResultList } from '@shared/models' 5import { HttpStatusCode, ResultList } from '@shared/models'
6import { logger } from '@root-helpers/logger'
6 7
7@Injectable() 8@Injectable()
8export class RestExtractor { 9export class RestExtractor {
@@ -64,7 +65,7 @@ export class RestExtractor {
64 if (err.error instanceof Error) { 65 if (err.error instanceof Error) {
65 // A client-side or network error occurred. Handle it accordingly. 66 // A client-side or network error occurred. Handle it accordingly.
66 const errorMessage = err.error.detail || err.error.title 67 const errorMessage = err.error.detail || err.error.title
67 console.error('An error occurred:', errorMessage) 68 logger.error('An error occurred:', errorMessage)
68 69
69 return errorMessage 70 return errorMessage
70 } 71 }
@@ -75,12 +76,12 @@ export class RestExtractor {
75 76
76 if (err.status !== undefined) { 77 if (err.status !== undefined) {
77 const errorMessage = this.buildServerErrorMessage(err) 78 const errorMessage = this.buildServerErrorMessage(err)
78 console.error(`Backend returned code ${err.status}, errorMessage is: ${errorMessage}`) 79 logger.error(`Backend returned code ${err.status}, errorMessage is: ${errorMessage}`)
79 80
80 return errorMessage 81 return errorMessage
81 } 82 }
82 83
83 console.error(err) 84 logger.error(err)
84 return err 85 return err
85 } 86 }
86 87
diff --git a/client/src/app/core/rest/rest-table.ts b/client/src/app/core/rest/rest-table.ts
index cb5bd0b89..ec5646b5d 100644
--- a/client/src/app/core/rest/rest-table.ts
+++ b/client/src/app/core/rest/rest-table.ts
@@ -1,10 +1,11 @@
1import * as debug from 'debug' 1import debug from 'debug'
2import { LazyLoadEvent, SortMeta } from 'primeng/api' 2import { LazyLoadEvent, SortMeta } from 'primeng/api'
3import { ActivatedRoute, Router } from '@angular/router' 3import { ActivatedRoute, Router } from '@angular/router'
4import { logger } from '@root-helpers/logger'
4import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage' 5import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage'
5import { RestPagination } from './rest-pagination' 6import { RestPagination } from './rest-pagination'
6 7
7const logger = debug('peertube:tables:RestTable') 8const debugLogger = debug('peertube:tables:RestTable')
8 9
9export abstract class RestTable { 10export abstract class RestTable {
10 11
@@ -34,7 +35,7 @@ export abstract class RestTable {
34 try { 35 try {
35 this.sort = JSON.parse(result) 36 this.sort = JSON.parse(result)
36 } catch (err) { 37 } catch (err) {
37 console.error('Cannot load sort of local storage key ' + this.getSortLocalStorageKey(), err) 38 logger.error('Cannot load sort of local storage key ' + this.getSortLocalStorageKey(), err)
38 } 39 }
39 } 40 }
40 } 41 }
@@ -44,7 +45,7 @@ export abstract class RestTable {
44 } 45 }
45 46
46 loadLazy (event: LazyLoadEvent) { 47 loadLazy (event: LazyLoadEvent) {
47 logger('Load lazy %o.', event) 48 debugLogger('Load lazy %o.', event)
48 49
49 this.sort = { 50 this.sort = {
50 order: event.sortOrder, 51 order: event.sortOrder,
diff --git a/client/src/app/core/rest/rest.service.ts b/client/src/app/core/rest/rest.service.ts
index fc729f0f6..d8b5ffb18 100644
--- a/client/src/app/core/rest/rest.service.ts
+++ b/client/src/app/core/rest/rest.service.ts
@@ -5,7 +5,7 @@ import { Injectable } from '@angular/core'
5import { ComponentPaginationLight } from './component-pagination.model' 5import { ComponentPaginationLight } from './component-pagination.model'
6import { RestPagination } from './rest-pagination' 6import { RestPagination } from './rest-pagination'
7 7
8const logger = debug('peertube:rest') 8const debugLogger = debug('peertube:rest')
9 9
10interface QueryStringFilterPrefixes { 10interface QueryStringFilterPrefixes {
11 [key: string]: { 11 [key: string]: {
@@ -88,7 +88,7 @@ export class RestService {
88 const prefixeStrings = Object.values(prefixes) 88 const prefixeStrings = Object.values(prefixes)
89 .map(p => p.prefix) 89 .map(p => p.prefix)
90 90
91 logger(`Built tokens "${tokens.join(', ')}" for prefixes "${prefixeStrings.join(', ')}"`) 91 debugLogger(`Built tokens "${tokens.join(', ')}" for prefixes "${prefixeStrings.join(', ')}"`)
92 92
93 // Search is the querystring minus defined filters 93 // Search is the querystring minus defined filters
94 const searchTokens = tokens.filter(t => { 94 const searchTokens = tokens.filter(t => {
@@ -127,7 +127,7 @@ export class RestService {
127 127
128 const search = searchTokens.join(' ') || undefined 128 const search = searchTokens.join(' ') || undefined
129 129
130 logger('Built search: ' + search, additionalFilters) 130 debugLogger('Built search: ' + search, additionalFilters)
131 131
132 return { 132 return {
133 search, 133 search,
diff --git a/client/src/app/core/routing/custom-reuse-strategy.ts b/client/src/app/core/routing/custom-reuse-strategy.ts
index 5d3ad2e67..269b9d193 100644
--- a/client/src/app/core/routing/custom-reuse-strategy.ts
+++ b/client/src/app/core/routing/custom-reuse-strategy.ts
@@ -1,5 +1,6 @@
1import { ComponentRef, Injectable } from '@angular/core' 1import { ComponentRef, Injectable } from '@angular/core'
2import { ActivatedRouteSnapshot, DetachedRouteHandle, RouteReuseStrategy } from '@angular/router' 2import { ActivatedRouteSnapshot, DetachedRouteHandle, RouteReuseStrategy } from '@angular/router'
3import { logger } from '@root-helpers/logger'
3import { DisableForReuseHook } from './disable-for-reuse-hook' 4import { DisableForReuseHook } from './disable-for-reuse-hook'
4import { PeerTubeRouterService, RouterSetting } from './peertube-router.service' 5import { PeerTubeRouterService, RouterSetting } from './peertube-router.service'
5 6
@@ -22,7 +23,7 @@ export class CustomReuseStrategy implements RouteReuseStrategy {
22 const key = this.generateKey(route) 23 const key = this.generateKey(route)
23 this.recentlyUsed = key 24 this.recentlyUsed = key
24 25
25 console.log('Storing component %s to reuse later.', key) 26 logger.info(`Storing component ${key} to reuse later.`)
26 27
27 const componentRef = (handle as any).componentRef as ComponentRef<DisableForReuseHook> 28 const componentRef = (handle as any).componentRef as ComponentRef<DisableForReuseHook>
28 componentRef.instance.disableForReuse() 29 componentRef.instance.disableForReuse()
@@ -46,7 +47,7 @@ export class CustomReuseStrategy implements RouteReuseStrategy {
46 const key = this.generateKey(route) 47 const key = this.generateKey(route)
47 this.recentlyUsed = key 48 this.recentlyUsed = key
48 49
49 console.log('Reusing component %s.', key) 50 logger.info(`Reusing component ${key}.`)
50 51
51 const handle = this.storedRouteHandles.get(key) 52 const handle = this.storedRouteHandles.get(key)
52 if (!handle) return handle; 53 if (!handle) return handle;
@@ -66,7 +67,7 @@ export class CustomReuseStrategy implements RouteReuseStrategy {
66 this.storedRouteHandles.forEach((r, key) => { 67 this.storedRouteHandles.forEach((r, key) => {
67 if (key === this.recentlyUsed) return 68 if (key === this.recentlyUsed) return
68 69
69 console.log('Removing stored component %s.', key); 70 logger.info(`Removing stored component ${key}`);
70 71
71 (r as any).componentRef.destroy() 72 (r as any).componentRef.destroy()
72 this.storedRouteHandles.delete(key) 73 this.storedRouteHandles.delete(key)
diff --git a/client/src/app/core/routing/redirect.service.ts b/client/src/app/core/routing/redirect.service.ts
index 567fd432b..575b3b2a1 100644
--- a/client/src/app/core/routing/redirect.service.ts
+++ b/client/src/app/core/routing/redirect.service.ts
@@ -1,10 +1,11 @@
1import * as debug from 'debug' 1import * as debug from 'debug'
2import { Injectable } from '@angular/core' 2import { Injectable } from '@angular/core'
3import { NavigationCancel, NavigationEnd, Router } from '@angular/router' 3import { NavigationCancel, NavigationEnd, Router } from '@angular/router'
4import { logger } from '@root-helpers/logger'
4import { ServerService } from '../server' 5import { ServerService } from '../server'
5import { SessionStorageService } from '../wrappers/storage.service' 6import { SessionStorageService } from '../wrappers/storage.service'
6 7
7const logger = debug('peertube:router:RedirectService') 8const debugLogger = debug('peertube:router:RedirectService')
8 9
9@Injectable() 10@Injectable()
10export class RedirectService { 11export class RedirectService {
@@ -40,7 +41,7 @@ export class RedirectService {
40 this.latestSessionUrl = this.storage.getItem(RedirectService.SESSION_STORAGE_LATEST_SESSION_URL_KEY) 41 this.latestSessionUrl = this.storage.getItem(RedirectService.SESSION_STORAGE_LATEST_SESSION_URL_KEY)
41 this.storage.removeItem(RedirectService.SESSION_STORAGE_LATEST_SESSION_URL_KEY) 42 this.storage.removeItem(RedirectService.SESSION_STORAGE_LATEST_SESSION_URL_KEY)
42 43
43 logger('Loaded latest session URL %s', this.latestSessionUrl) 44 debugLogger('Loaded latest session URL %s', this.latestSessionUrl)
44 45
45 // Track previous url 46 // Track previous url
46 this.currentUrl = this.router.url 47 this.currentUrl = this.router.url
@@ -51,8 +52,8 @@ export class RedirectService {
51 this.previousUrl = this.currentUrl 52 this.previousUrl = this.currentUrl
52 this.currentUrl = event.url 53 this.currentUrl = event.url
53 54
54 logger('Previous URL is %s, current URL is %s', this.previousUrl, this.currentUrl) 55 debugLogger('Previous URL is %s, current URL is %s', this.previousUrl, this.currentUrl)
55 logger('Setting %s as latest URL in session storage.', this.currentUrl) 56 debugLogger('Setting %s as latest URL in session storage.', this.currentUrl)
56 57
57 this.storage.setItem(RedirectService.SESSION_STORAGE_LATEST_SESSION_URL_KEY, this.currentUrl) 58 this.storage.setItem(RedirectService.SESSION_STORAGE_LATEST_SESSION_URL_KEY, this.currentUrl)
58 } 59 }
@@ -84,18 +85,14 @@ export class RedirectService {
84 85
85 this.redirectingToHomepage = true 86 this.redirectingToHomepage = true
86 87
87 console.log('Redirecting to %s...', this.defaultRoute) 88 logger.info(`Redirecting to ${this.defaultRoute}...`)
88 89
89 this.router.navigateByUrl(this.defaultRoute, { skipLocationChange }) 90 this.router.navigateByUrl(this.defaultRoute, { skipLocationChange })
90 .then(() => this.redirectingToHomepage = false) 91 .then(() => this.redirectingToHomepage = false)
91 .catch(() => { 92 .catch(() => {
92 this.redirectingToHomepage = false 93 this.redirectingToHomepage = false
93 94
94 console.error( 95 logger.error(`Cannot navigate to ${this.defaultRoute}, resetting default route to ${RedirectService.INIT_DEFAULT_ROUTE}`)
95 'Cannot navigate to %s, resetting default route to %s.',
96 this.defaultRoute,
97 RedirectService.INIT_DEFAULT_ROUTE
98 )
99 96
100 this.defaultRoute = RedirectService.INIT_DEFAULT_ROUTE 97 this.defaultRoute = RedirectService.INIT_DEFAULT_ROUTE
101 return this.router.navigateByUrl(this.defaultRoute, { skipLocationChange }) 98 return this.router.navigateByUrl(this.defaultRoute, { skipLocationChange })
@@ -104,18 +101,18 @@ export class RedirectService {
104 } 101 }
105 102
106 private doRedirect (redirectUrl: string, fallbackRoute?: string) { 103 private doRedirect (redirectUrl: string, fallbackRoute?: string) {
107 logger('Redirecting on %s', redirectUrl) 104 debugLogger('Redirecting on %s', redirectUrl)
108 105
109 if (this.isValidRedirection(redirectUrl)) { 106 if (this.isValidRedirection(redirectUrl)) {
110 return this.router.navigateByUrl(redirectUrl) 107 return this.router.navigateByUrl(redirectUrl)
111 } 108 }
112 109
113 logger('%s is not a valid redirection, try fallback route %s', redirectUrl, fallbackRoute) 110 debugLogger('%s is not a valid redirection, try fallback route %s', redirectUrl, fallbackRoute)
114 if (fallbackRoute) { 111 if (fallbackRoute) {
115 return this.router.navigateByUrl(fallbackRoute) 112 return this.router.navigateByUrl(fallbackRoute)
116 } 113 }
117 114
118 logger('There was no fallback route, redirecting to homepage') 115 debugLogger('There was no fallback route, redirecting to homepage')
119 return this.redirectToHomepage() 116 return this.redirectToHomepage()
120 } 117 }
121 118
diff --git a/client/src/app/core/routing/scroll.service.ts b/client/src/app/core/routing/scroll.service.ts
index 6d37fde71..0966255b3 100644
--- a/client/src/app/core/routing/scroll.service.ts
+++ b/client/src/app/core/routing/scroll.service.ts
@@ -4,8 +4,9 @@ import { ViewportScroller } from '@angular/common'
4import { Injectable } from '@angular/core' 4import { Injectable } from '@angular/core'
5import { RouterSetting } from '../' 5import { RouterSetting } from '../'
6import { PeerTubeRouterService } from './peertube-router.service' 6import { PeerTubeRouterService } from './peertube-router.service'
7import { logger } from '@root-helpers/logger'
7 8
8const logger = debug('peertube:main:ScrollService') 9const debugLogger = debug('peertube:main:ScrollService')
9 10
10@Injectable() 11@Injectable()
11export class ScrollService { 12export class ScrollService {
@@ -57,8 +58,8 @@ export class ScrollService {
57 if (nextSearchParams.toString() !== previousSearchParams.toString()) { 58 if (nextSearchParams.toString() !== previousSearchParams.toString()) {
58 this.resetScroll = true 59 this.resetScroll = true
59 } 60 }
60 } catch (e) { 61 } catch (err) {
61 console.error('Cannot parse URL to check next scroll.', e) 62 logger.error('Cannot parse URL to check next scroll.', err)
62 this.resetScroll = true 63 this.resetScroll = true
63 } 64 }
64 }) 65 })
@@ -67,7 +68,7 @@ export class ScrollService {
67 private consumeScroll () { 68 private consumeScroll () {
68 // Handle anchors/restore position 69 // Handle anchors/restore position
69 this.peertubeRouter.getScrollEvents().subscribe(e => { 70 this.peertubeRouter.getScrollEvents().subscribe(e => {
70 logger('Will schedule scroll after router event %o.', { e, resetScroll: this.resetScroll }) 71 debugLogger('Will schedule scroll after router event %o.', { e, resetScroll: this.resetScroll })
71 72
72 // scrollToAnchor first to preserve anchor position when using history navigation 73 // scrollToAnchor first to preserve anchor position when using history navigation
73 if (e.anchor) { 74 if (e.anchor) {
diff --git a/client/src/app/core/server/server.service.ts b/client/src/app/core/server/server.service.ts
index d01942139..9db455cb8 100644
--- a/client/src/app/core/server/server.service.ts
+++ b/client/src/app/core/server/server.service.ts
@@ -3,6 +3,7 @@ import { first, map, share, shareReplay, switchMap, tap } from 'rxjs/operators'
3import { HttpClient } from '@angular/common/http' 3import { HttpClient } from '@angular/common/http'
4import { Inject, Injectable, LOCALE_ID } from '@angular/core' 4import { Inject, Injectable, LOCALE_ID } from '@angular/core'
5import { getDevLocale, isOnDevLocale, sortBy } from '@app/helpers' 5import { getDevLocale, isOnDevLocale, sortBy } from '@app/helpers'
6import { logger } from '@root-helpers/logger'
6import { getCompleteLocale, isDefaultLocale, peertubeTranslate } from '@shared/core-utils/i18n' 7import { getCompleteLocale, isDefaultLocale, peertubeTranslate } from '@shared/core-utils/i18n'
7import { HTMLServerConfig, ServerConfig, ServerStats, VideoConstant } from '@shared/models' 8import { HTMLServerConfig, ServerConfig, ServerStats, VideoConstant } from '@shared/models'
8import { environment } from '../../../environments/environment' 9import { environment } from '../../../environments/environment'
@@ -43,7 +44,7 @@ export class ServerService {
43 } catch (err) { 44 } catch (err) {
44 // Expected in dev mode since we can't inject the config in the HTML 45 // Expected in dev mode since we can't inject the config in the HTML
45 if (environment.production !== false) { 46 if (environment.production !== false) {
46 console.error('Cannot load config locally. Fallback to API.') 47 logger.error('Cannot load config locally. Fallback to API.')
47 } 48 }
48 49
49 return this.getConfig() 50 return this.getConfig()
diff --git a/client/src/app/core/theme/theme.service.ts b/client/src/app/core/theme/theme.service.ts
index 40939ecb8..ead1770ba 100644
--- a/client/src/app/core/theme/theme.service.ts
+++ b/client/src/app/core/theme/theme.service.ts
@@ -1,4 +1,5 @@
1import { Injectable } from '@angular/core' 1import { Injectable } from '@angular/core'
2import { logger } from '@root-helpers/logger'
2import { capitalizeFirstLetter } from '@root-helpers/string' 3import { capitalizeFirstLetter } from '@root-helpers/string'
3import { UserLocalStorageKeys } from '@root-helpers/users' 4import { UserLocalStorageKeys } from '@root-helpers/users'
4import { HTMLServerConfig, ServerConfigTheme } from '@shared/models' 5import { HTMLServerConfig, ServerConfigTheme } from '@shared/models'
@@ -57,7 +58,7 @@ export class ThemeService {
57 private injectThemes (themes: ServerConfigTheme[], fromLocalStorage = false) { 58 private injectThemes (themes: ServerConfigTheme[], fromLocalStorage = false) {
58 this.themes = themes 59 this.themes = themes
59 60
60 console.log('Injecting %d themes.', this.themes.length) 61 logger.info(`Injecting ${this.themes.length} themes.`)
61 62
62 const head = this.getHeadElement() 63 const head = this.getHeadElement()
63 64
@@ -117,13 +118,13 @@ export class ThemeService {
117 118
118 const currentTheme = this.getCurrentTheme() 119 const currentTheme = this.getCurrentTheme()
119 120
120 console.log('Enabling %s theme.', currentTheme) 121 logger.info(`Enabling ${currentTheme} theme.`)
121 122
122 this.loadTheme(currentTheme) 123 this.loadTheme(currentTheme)
123 124
124 const theme = this.getTheme(currentTheme) 125 const theme = this.getTheme(currentTheme)
125 if (theme) { 126 if (theme) {
126 console.log('Adding scripts of theme %s.', currentTheme) 127 logger.info(`Adding scripts of theme ${currentTheme}`)
127 128
128 this.pluginService.addPlugin(theme, true) 129 this.pluginService.addPlugin(theme, true)
129 130
@@ -165,7 +166,7 @@ export class ThemeService {
165 this.injectThemes([ lastActiveTheme ], true) 166 this.injectThemes([ lastActiveTheme ], true)
166 this.updateCurrentTheme() 167 this.updateCurrentTheme()
167 } catch (err) { 168 } catch (err) {
168 console.error('Cannot parse last active theme.', err) 169 logger.error('Cannot parse last active theme.', err)
169 return 170 return
170 } 171 }
171 } 172 }
@@ -173,7 +174,7 @@ export class ThemeService {
173 private removeThemePlugins (themeName: string) { 174 private removeThemePlugins (themeName: string) {
174 const oldTheme = this.getTheme(themeName) 175 const oldTheme = this.getTheme(themeName)
175 if (oldTheme) { 176 if (oldTheme) {
176 console.log('Removing scripts of old theme %s.', themeName) 177 logger.info(`Removing scripts of old theme ${themeName}.`)
177 this.pluginService.removePlugin(oldTheme) 178 this.pluginService.removePlugin(oldTheme)
178 } 179 }
179 } 180 }
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 d15bf735b..fff649eef 100644
--- a/client/src/app/core/users/user-local-storage.service.ts
+++ b/client/src/app/core/users/user-local-storage.service.ts
@@ -2,8 +2,9 @@
2import { filter, throttleTime } from 'rxjs' 2import { filter, throttleTime } from 'rxjs'
3import { Injectable } from '@angular/core' 3import { Injectable } from '@angular/core'
4import { AuthService, AuthStatus } from '@app/core/auth' 4import { AuthService, AuthStatus } from '@app/core/auth'
5import { UserLocalStorageKeys, UserTokens } from '@root-helpers/users'
6import { getBoolOrDefault } from '@root-helpers/local-storage-utils' 5import { getBoolOrDefault } from '@root-helpers/local-storage-utils'
6import { logger } from '@root-helpers/logger'
7import { UserLocalStorageKeys, UserTokens } from '@root-helpers/users'
7import { UserRole, UserUpdateMe } from '@shared/models' 8import { UserRole, UserUpdateMe } from '@shared/models'
8import { NSFWPolicyType } from '@shared/models/videos' 9import { NSFWPolicyType } from '@shared/models/videos'
9import { ServerService } from '../server' 10import { ServerService } from '../server'
@@ -95,7 +96,7 @@ export class UserLocalStorageService {
95 : null 96 : null
96 } catch (err) { 97 } catch (err) {
97 videoLanguages = null 98 videoLanguages = null
98 console.error('Cannot parse desired video languages from localStorage.', err) 99 logger.error('Cannot parse desired video languages from localStorage.', err)
99 } 100 }
100 101
101 const htmlConfig = this.server.getHTMLConfig() 102 const htmlConfig = this.server.getHTMLConfig()
@@ -142,7 +143,7 @@ export class UserLocalStorageService {
142 143
143 this.localStorageService.setItem(key, localStorageValue) 144 this.localStorageService.setItem(key, localStorageValue)
144 } catch (err) { 145 } catch (err) {
145 console.error(`Cannot set ${key}->${value} in localStorage. Likely due to a value impossible to stringify.`, err) 146 logger.error(`Cannot set ${key}->${value} in localStorage. Likely due to a value impossible to stringify.`, err)
146 } 147 }
147 } 148 }
148 } 149 }
diff --git a/client/src/app/header/search-typeahead.component.ts b/client/src/app/header/search-typeahead.component.ts
index 0794ec8f4..d2549315c 100644
--- a/client/src/app/header/search-typeahead.component.ts
+++ b/client/src/app/header/search-typeahead.component.ts
@@ -4,6 +4,7 @@ import { ListKeyManager } from '@angular/cdk/a11y'
4import { AfterViewChecked, Component, OnDestroy, OnInit, QueryList, ViewChildren } from '@angular/core' 4import { AfterViewChecked, Component, OnDestroy, OnInit, QueryList, ViewChildren } from '@angular/core'
5import { ActivatedRoute, Params, Router } from '@angular/router' 5import { ActivatedRoute, Params, Router } from '@angular/router'
6import { AuthService, ServerService } from '@app/core' 6import { AuthService, ServerService } from '@app/core'
7import { logger } from '@root-helpers/logger'
7import { HTMLServerConfig, SearchTargetType } from '@shared/models' 8import { HTMLServerConfig, SearchTargetType } from '@shared/models'
8import { SuggestionComponent, SuggestionPayload, SuggestionPayloadType } from './suggestion.component' 9import { SuggestionComponent, SuggestionPayload, SuggestionPayloadType } from './suggestion.component'
9 10
@@ -91,7 +92,7 @@ export class SearchTypeaheadComponent implements OnInit, AfterViewChecked, OnDes
91 92
92 const activeIndex = this.suggestionItems.toArray().findIndex(i => i.result.default === true) 93 const activeIndex = this.suggestionItems.toArray().findIndex(i => i.result.default === true)
93 if (activeIndex === -1) { 94 if (activeIndex === -1) {
94 console.error('Cannot find active index.', { suggestionItems: this.suggestionItems }) 95 logger.error('Cannot find active index.', { suggestionItems: this.suggestionItems })
95 } 96 }
96 97
97 this.updateItemsState(activeIndex) 98 this.updateItemsState(activeIndex)
diff --git a/client/src/app/helpers/i18n-utils.ts b/client/src/app/helpers/i18n-utils.ts
index 2017a31ea..b7d73d16b 100644
--- a/client/src/app/helpers/i18n-utils.ts
+++ b/client/src/app/helpers/i18n-utils.ts
@@ -1,5 +1,6 @@
1import { environment } from '../../environments/environment'
2import IntlMessageFormat from 'intl-messageformat' 1import IntlMessageFormat from 'intl-messageformat'
2import { logger } from '@root-helpers/logger'
3import { environment } from '../../environments/environment'
3 4
4function isOnDevLocale () { 5function isOnDevLocale () {
5 return environment.production === false && window.location.search === '?lang=fr' 6 return environment.production === false && window.location.search === '?lang=fr'
@@ -19,14 +20,14 @@ function prepareIcu (icu: string) {
19 try { 20 try {
20 return msg.format(context) as string 21 return msg.format(context) as string
21 } catch (err) { 22 } catch (err) {
22 if (!alreadyWarned) console.warn('Cannot format ICU %s.', icu, err) 23 if (!alreadyWarned) logger.warn(`Cannot format ICU ${icu}.`, err)
23 24
24 alreadyWarned = true 25 alreadyWarned = true
25 return fallback 26 return fallback
26 } 27 }
27 } 28 }
28 } catch (err) { 29 } catch (err) {
29 console.warn('Cannot build intl message %s.', icu, err) 30 logger.warn(`Cannot build intl message ${icu}.`, err)
30 31
31 return (_context: unknown, fallback: string) => fallback 32 return (_context: unknown, fallback: string) => fallback
32 } 33 }
diff --git a/client/src/app/menu/menu.component.ts b/client/src/app/menu/menu.component.ts
index 983f0a938..63f01df92 100644
--- a/client/src/app/menu/menu.component.ts
+++ b/client/src/app/menu/menu.component.ts
@@ -24,7 +24,7 @@ import { NgbDropdown } from '@ng-bootstrap/ng-bootstrap'
24import { PluginsManager } from '@root-helpers/plugins-manager' 24import { PluginsManager } from '@root-helpers/plugins-manager'
25import { HTMLServerConfig, ServerConfig, UserRight, VideoConstant } from '@shared/models' 25import { HTMLServerConfig, ServerConfig, UserRight, VideoConstant } from '@shared/models'
26 26
27const logger = debug('peertube:menu:MenuComponent') 27const debugLogger = debug('peertube:menu:MenuComponent')
28 28
29@Component({ 29@Component({
30 selector: 'my-menu', 30 selector: 'my-menu',
@@ -295,8 +295,8 @@ export class MenuComponent implements OnInit {
295 .pipe( 295 .pipe(
296 switchMap(() => this.user.computeCanSeeVideosLink(this.userService.getMyVideoQuotaUsed())) 296 switchMap(() => this.user.computeCanSeeVideosLink(this.userService.getMyVideoQuotaUsed()))
297 ).subscribe(res => { 297 ).subscribe(res => {
298 if (res === true) logger('User can see videos link.') 298 if (res === true) debugLogger('User can see videos link.')
299 else logger('User cannot see videos link.') 299 else debugLogger('User cannot see videos link.')
300 }) 300 })
301 } 301 }
302 302
diff --git a/client/src/app/modal/account-setup-warning-modal.component.ts b/client/src/app/modal/account-setup-warning-modal.component.ts
index c4cbf92b6..b999a3407 100644
--- a/client/src/app/modal/account-setup-warning-modal.component.ts
+++ b/client/src/app/modal/account-setup-warning-modal.component.ts
@@ -1,6 +1,7 @@
1import { Component, ElementRef, ViewChild } from '@angular/core' 1import { Component, ElementRef, ViewChild } from '@angular/core'
2import { Notifier, ServerService, User, UserService } from '@app/core' 2import { Notifier, ServerService, User, UserService } from '@app/core'
3import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap' 3import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'
4import { logger } from '@root-helpers/logger'
4import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage' 5import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage'
5 6
6@Component({ 7@Component({
@@ -71,7 +72,7 @@ export class AccountSetupWarningModalComponent {
71 72
72 this.userService.updateMyProfile({ noAccountSetupWarningModal: true }) 73 this.userService.updateMyProfile({ noAccountSetupWarningModal: true })
73 .subscribe({ 74 .subscribe({
74 next: () => console.log('We will not open the account setup modal again.'), 75 next: () => logger.info('We will not open the account setup modal again.'),
75 76
76 error: err => this.notifier.error(err.message) 77 error: err => this.notifier.error(err.message)
77 }) 78 })
diff --git a/client/src/app/modal/admin-welcome-modal.component.ts b/client/src/app/modal/admin-welcome-modal.component.ts
index 3679f0847..7a9b89d3d 100644
--- a/client/src/app/modal/admin-welcome-modal.component.ts
+++ b/client/src/app/modal/admin-welcome-modal.component.ts
@@ -1,6 +1,7 @@
1import { Component, ElementRef, ViewChild } from '@angular/core' 1import { Component, ElementRef, ViewChild } from '@angular/core'
2import { Notifier, User, UserService } from '@app/core' 2import { Notifier, User, UserService } from '@app/core'
3import { NgbModal } from '@ng-bootstrap/ng-bootstrap' 3import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
4import { logger } from '@root-helpers/logger'
4import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage' 5import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage'
5 6
6@Component({ 7@Component({
@@ -42,7 +43,7 @@ export class AdminWelcomeModalComponent {
42 43
43 this.userService.updateMyProfile({ noWelcomeModal: true }) 44 this.userService.updateMyProfile({ noWelcomeModal: true })
44 .subscribe({ 45 .subscribe({
45 next: () => console.log('We will not open the welcome modal again.'), 46 next: () => logger.info('We will not open the welcome modal again.'),
46 47
47 error: err => this.notifier.error(err.message) 48 error: err => this.notifier.error(err.message)
48 }) 49 })
diff --git a/client/src/app/modal/custom-modal.component.ts b/client/src/app/modal/custom-modal.component.ts
index 559230e04..49343ae71 100644
--- a/client/src/app/modal/custom-modal.component.ts
+++ b/client/src/app/modal/custom-modal.component.ts
@@ -1,5 +1,6 @@
1import { Component, ElementRef, ViewChild, Input } from '@angular/core' 1import { Component, ElementRef, Input, ViewChild } from '@angular/core'
2import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap' 2import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'
3import { logger } from '@root-helpers/logger'
3 4
4@Component({ 5@Component({
5 selector: 'my-custom-modal', 6 selector: 'my-custom-modal',
@@ -29,7 +30,7 @@ export class CustomModalComponent {
29 confirm?: { value: string, action?: () => void } 30 confirm?: { value: string, action?: () => void }
30 }) { 31 }) {
31 if (this.modalRef instanceof NgbModalRef && this.modalService.hasOpenModals()) { 32 if (this.modalRef instanceof NgbModalRef && this.modalService.hasOpenModals()) {
32 console.error('Cannot open another custom modal, one is already opened.') 33 logger.error('Cannot open another custom modal, one is already opened.')
33 return 34 return
34 } 35 }
35 36
diff --git a/client/src/app/modal/instance-config-warning-modal.component.ts b/client/src/app/modal/instance-config-warning-modal.component.ts
index 300738a41..23c2c777e 100644
--- a/client/src/app/modal/instance-config-warning-modal.component.ts
+++ b/client/src/app/modal/instance-config-warning-modal.component.ts
@@ -2,6 +2,7 @@ import { Location } from '@angular/common'
2import { Component, ElementRef, ViewChild } from '@angular/core' 2import { Component, ElementRef, ViewChild } from '@angular/core'
3import { Notifier, User, UserService } from '@app/core' 3import { Notifier, User, UserService } from '@app/core'
4import { NgbModal } from '@ng-bootstrap/ng-bootstrap' 4import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
5import { logger } from '@root-helpers/logger'
5import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage' 6import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage'
6import { About, ServerConfig } from '@shared/models/server' 7import { About, ServerConfig } from '@shared/models/server'
7 8
@@ -64,7 +65,7 @@ export class InstanceConfigWarningModalComponent {
64 65
65 this.userService.updateMyProfile({ noInstanceConfigWarningModal: true }) 66 this.userService.updateMyProfile({ noInstanceConfigWarningModal: true })
66 .subscribe({ 67 .subscribe({
67 next: () => console.log('We will not open the instance config warning modal again.'), 68 next: () => logger.info('We will not open the instance config warning modal again.'),
68 69
69 error: err => this.notifier.error(err.message) 70 error: err => this.notifier.error(err.message)
70 }) 71 })
diff --git a/client/src/app/shared/shared-abuse-list/abuse-list-table.component.ts b/client/src/app/shared/shared-abuse-list/abuse-list-table.component.ts
index 8781c16f5..32d3b0093 100644
--- a/client/src/app/shared/shared-abuse-list/abuse-list-table.component.ts
+++ b/client/src/app/shared/shared-abuse-list/abuse-list-table.component.ts
@@ -8,13 +8,14 @@ import { ConfirmService, MarkdownService, Notifier, RestPagination, RestTable }
8import { Account, Actor, DropdownAction, Video, VideoService } from '@app/shared/shared-main' 8import { Account, Actor, DropdownAction, Video, VideoService } from '@app/shared/shared-main'
9import { AbuseService, BlocklistService, VideoBlockService } from '@app/shared/shared-moderation' 9import { AbuseService, BlocklistService, VideoBlockService } from '@app/shared/shared-moderation'
10import { VideoCommentService } from '@app/shared/shared-video-comment' 10import { VideoCommentService } from '@app/shared/shared-video-comment'
11import { logger } from '@root-helpers/logger'
11import { AbuseState, AdminAbuse } from '@shared/models' 12import { AbuseState, AdminAbuse } from '@shared/models'
12import { AdvancedInputFilter } from '../shared-forms' 13import { AdvancedInputFilter } from '../shared-forms'
13import { AbuseMessageModalComponent } from './abuse-message-modal.component' 14import { AbuseMessageModalComponent } from './abuse-message-modal.component'
14import { ModerationCommentModalComponent } from './moderation-comment-modal.component' 15import { ModerationCommentModalComponent } from './moderation-comment-modal.component'
15import { ProcessedAbuse } from './processed-abuse.model' 16import { ProcessedAbuse } from './processed-abuse.model'
16 17
17const logger = debug('peertube:moderation:AbuseListTableComponent') 18const debugLogger = debug('peertube:moderation:AbuseListTableComponent')
18 19
19@Component({ 20@Component({
20 selector: 'my-abuse-list-table', 21 selector: 'my-abuse-list-table',
@@ -158,7 +159,7 @@ export class AbuseListTableComponent extends RestTable implements OnInit {
158 const abuse = this.abuses.find(a => a.id === event.abuseId) 159 const abuse = this.abuses.find(a => a.id === event.abuseId)
159 160
160 if (!abuse) { 161 if (!abuse) {
161 console.error('Cannot find abuse %d.', event.abuseId) 162 logger.error(`Cannot find abuse ${event.abuseId}`)
162 return 163 return
163 } 164 }
164 165
@@ -177,7 +178,7 @@ export class AbuseListTableComponent extends RestTable implements OnInit {
177 } 178 }
178 179
179 protected reloadData () { 180 protected reloadData () {
180 logger('Loading data.') 181 debugLogger('Loading data.')
181 182
182 const options = { 183 const options = {
183 pagination: this.pagination, 184 pagination: this.pagination,
diff --git a/client/src/app/shared/shared-abuse-list/abuse-message-modal.component.ts b/client/src/app/shared/shared-abuse-list/abuse-message-modal.component.ts
index 6c8dc6d35..d24a5d58d 100644
--- a/client/src/app/shared/shared-abuse-list/abuse-message-modal.component.ts
+++ b/client/src/app/shared/shared-abuse-list/abuse-message-modal.component.ts
@@ -3,6 +3,7 @@ import { AuthService, HtmlRendererService, Notifier } from '@app/core'
3import { FormReactive, FormValidatorService } from '@app/shared/shared-forms' 3import { FormReactive, FormValidatorService } from '@app/shared/shared-forms'
4import { NgbModal } from '@ng-bootstrap/ng-bootstrap' 4import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
5import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref' 5import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref'
6import { logger } from '@root-helpers/logger'
6import { AbuseMessage, UserAbuse } from '@shared/models' 7import { AbuseMessage, UserAbuse } from '@shared/models'
7import { ABUSE_MESSAGE_VALIDATOR } from '../form-validators/abuse-validators' 8import { ABUSE_MESSAGE_VALIDATOR } from '../form-validators/abuse-validators'
8import { AbuseService } from '../shared-moderation' 9import { AbuseService } from '../shared-moderation'
@@ -72,7 +73,7 @@ export class AbuseMessageModalComponent extends FormReactive implements OnInit {
72 73
73 error: err => { 74 error: err => {
74 this.sendingMessage = false 75 this.sendingMessage = false
75 console.error(err) 76 logger.error(err)
76 this.notifier.error('Sorry but you cannot send this message. Please retry later') 77 this.notifier.error('Sorry but you cannot send this message. Please retry later')
77 } 78 }
78 }) 79 })
diff --git a/client/src/app/shared/shared-custom-markup/custom-markup.service.ts b/client/src/app/shared/shared-custom-markup/custom-markup.service.ts
index a959b336d..d738a644e 100644
--- a/client/src/app/shared/shared-custom-markup/custom-markup.service.ts
+++ b/client/src/app/shared/shared-custom-markup/custom-markup.service.ts
@@ -20,6 +20,7 @@ import {
20 VideosListMarkupComponent 20 VideosListMarkupComponent
21} from './peertube-custom-tags' 21} from './peertube-custom-tags'
22import { CustomMarkupComponent } from './peertube-custom-tags/shared' 22import { CustomMarkupComponent } from './peertube-custom-tags/shared'
23import { logger } from '@root-helpers/logger'
23 24
24type AngularBuilderFunction = (el: HTMLElement) => ComponentRef<CustomMarkupComponent> 25type AngularBuilderFunction = (el: HTMLElement) => ComponentRef<CustomMarkupComponent>
25type HTMLBuilderFunction = (el: HTMLElement) => HTMLElement 26type HTMLBuilderFunction = (el: HTMLElement) => HTMLElement
@@ -70,7 +71,7 @@ export class CustomMarkupService {
70 // Insert as first child 71 // Insert as first child
71 e.insertBefore(element, e.firstChild) 72 e.insertBefore(element, e.firstChild)
72 } catch (err) { 73 } catch (err) {
73 console.error('Cannot inject component %s.', selector, err) 74 logger.error(`Cannot inject component ${selector}`, err)
74 } 75 }
75 }) 76 })
76 } 77 }
@@ -90,7 +91,7 @@ export class CustomMarkupService {
90 91
91 this.dynamicElementService.injectElement(e, component) 92 this.dynamicElementService.injectElement(e, component)
92 } catch (err) { 93 } catch (err) {
93 console.error('Cannot inject component %s.', selector, err) 94 logger.error(`Cannot inject component ${selector}`, err)
94 } 95 }
95 }) 96 })
96 } 97 }
diff --git a/client/src/app/shared/shared-forms/advanced-input-filter.component.ts b/client/src/app/shared/shared-forms/advanced-input-filter.component.ts
index d8aeaa0fa..929438749 100644
--- a/client/src/app/shared/shared-forms/advanced-input-filter.component.ts
+++ b/client/src/app/shared/shared-forms/advanced-input-filter.component.ts
@@ -16,7 +16,7 @@ export type AdvancedInputFilterChild = {
16 value: string 16 value: string
17} 17}
18 18
19const logger = debug('peertube:AdvancedInputFilterComponent') 19const debugLogger = debug('peertube:AdvancedInputFilterComponent')
20 20
21@Component({ 21@Component({
22 selector: 'my-advanced-input-filter', 22 selector: 'my-advanced-input-filter',
@@ -98,7 +98,7 @@ export class AdvancedInputFilterComponent implements OnInit, AfterViewInit {
98 .subscribe(params => { 98 .subscribe(params => {
99 const search = params.search || '' 99 const search = params.search || ''
100 100
101 logger('On route search change "%s".', search) 101 debugLogger('On route search change "%s".', search)
102 102
103 if (this.searchValue === search) return 103 if (this.searchValue === search) return
104 104
@@ -132,7 +132,7 @@ export class AdvancedInputFilterComponent implements OnInit, AfterViewInit {
132 return 132 return
133 } 133 }
134 134
135 logger('On search "%s".', this.searchValue) 135 debugLogger('On search "%s".', this.searchValue)
136 136
137 this.search.emit(this.searchValue) 137 this.search.emit(this.searchValue)
138 } 138 }
diff --git a/client/src/app/shared/shared-main/angular/defer-loading.directive.ts b/client/src/app/shared/shared-main/angular/defer-loading.directive.ts
index 9a10e90e3..53d6e70ba 100644
--- a/client/src/app/shared/shared-main/angular/defer-loading.directive.ts
+++ b/client/src/app/shared/shared-main/angular/defer-loading.directive.ts
@@ -13,7 +13,7 @@ import {
13 ViewContainerRef 13 ViewContainerRef
14} from '@angular/core' 14} from '@angular/core'
15 15
16const logger = debug('peertube:main:DeferLoadingDirective') 16const debugLogger = debug('peertube:main:DeferLoadingDirective')
17 17
18@Directive({ 18@Directive({
19 selector: '[myDeferLoading]' 19 selector: '[myDeferLoading]'
@@ -52,7 +52,7 @@ export class DeferLoadingDirective implements AfterViewInit, OnDestroy {
52 load () { 52 load () {
53 if (this.isLoaded()) return 53 if (this.isLoaded()) return
54 54
55 logger('Loading component') 55 debugLogger('Loading component')
56 56
57 this.viewContainer.clear() 57 this.viewContainer.clear()
58 this.view = this.viewContainer.createEmbeddedView(this.template, {}, 0) 58 this.view = this.viewContainer.createEmbeddedView(this.template, {}, 0)
diff --git a/client/src/app/shared/shared-main/misc/list-overflow.component.ts b/client/src/app/shared/shared-main/misc/list-overflow.component.ts
index 541991f74..7e4e1b1d1 100644
--- a/client/src/app/shared/shared-main/misc/list-overflow.component.ts
+++ b/client/src/app/shared/shared-main/misc/list-overflow.component.ts
@@ -17,7 +17,7 @@ import { ScreenService } from '@app/core'
17import { NgbDropdown, NgbModal } from '@ng-bootstrap/ng-bootstrap' 17import { NgbDropdown, NgbModal } from '@ng-bootstrap/ng-bootstrap'
18import * as debug from 'debug' 18import * as debug from 'debug'
19 19
20const logger = debug('peertube:main:ListOverflowItem') 20const debugLogger = debug('peertube:main:ListOverflowItem')
21 21
22export interface ListOverflowItem { 22export interface ListOverflowItem {
23 label: string 23 label: string
@@ -66,7 +66,7 @@ export class ListOverflowComponent<T extends ListOverflowItem> implements AfterV
66 let showItemsUntilIndexExcluded: number 66 let showItemsUntilIndexExcluded: number
67 let accWidth = 0 67 let accWidth = 0
68 68
69 logger('Parent width is %d', parentWidth) 69 debugLogger('Parent width is %d', parentWidth)
70 70
71 for (const [ index, el ] of this.itemsRendered.toArray().entries()) { 71 for (const [ index, el ] of this.itemsRendered.toArray().entries()) {
72 accWidth += el.nativeElement.getBoundingClientRect().width 72 accWidth += el.nativeElement.getBoundingClientRect().width
@@ -79,7 +79,7 @@ export class ListOverflowComponent<T extends ListOverflowItem> implements AfterV
79 e.style.visibility = shouldBeVisible ? 'inherit' : 'hidden' 79 e.style.visibility = shouldBeVisible ? 'inherit' : 'hidden'
80 } 80 }
81 81
82 logger('Accumulated children width is %d so exclude index is %d', accWidth, showItemsUntilIndexExcluded) 82 debugLogger('Accumulated children width is %d so exclude index is %d', accWidth, showItemsUntilIndexExcluded)
83 83
84 this.showItemsUntilIndexExcluded = showItemsUntilIndexExcluded 84 this.showItemsUntilIndexExcluded = showItemsUntilIndexExcluded
85 this.cdr.markForCheck() 85 this.cdr.markForCheck()
diff --git a/client/src/app/shared/shared-main/users/user-notification.model.ts b/client/src/app/shared/shared-main/users/user-notification.model.ts
index a2367166e..bf8870a79 100644
--- a/client/src/app/shared/shared-main/users/user-notification.model.ts
+++ b/client/src/app/shared/shared-main/users/user-notification.model.ts
@@ -2,6 +2,7 @@ import { AuthUser } from '@app/core'
2import { Account } from '@app/shared/shared-main/account/account.model' 2import { Account } from '@app/shared/shared-main/account/account.model'
3import { Actor } from '@app/shared/shared-main/account/actor.model' 3import { Actor } from '@app/shared/shared-main/account/actor.model'
4import { VideoChannel } from '@app/shared/shared-main/video-channel/video-channel.model' 4import { VideoChannel } from '@app/shared/shared-main/video-channel/video-channel.model'
5import { logger } from '@root-helpers/logger'
5import { 6import {
6 AbuseState, 7 AbuseState,
7 ActorInfo, 8 ActorInfo,
@@ -234,7 +235,7 @@ export class UserNotification implements UserNotificationServer {
234 } 235 }
235 } catch (err) { 236 } catch (err) {
236 this.type = null 237 this.type = null
237 console.error(err) 238 logger.error(err)
238 } 239 }
239 } 240 }
240 241
diff --git a/client/src/app/shared/shared-search/find-in-bulk.service.ts b/client/src/app/shared/shared-search/find-in-bulk.service.ts
index 117685cc6..d2f8c3213 100644
--- a/client/src/app/shared/shared-search/find-in-bulk.service.ts
+++ b/client/src/app/shared/shared-search/find-in-bulk.service.ts
@@ -9,7 +9,7 @@ import { VideoPlaylist } from '../shared-video-playlist'
9import { SearchService } from './search.service' 9import { SearchService } from './search.service'
10import { AdvancedSearch } from './advanced-search.model' 10import { AdvancedSearch } from './advanced-search.model'
11 11
12const logger = debug('peertube:search:FindInBulkService') 12const debugLogger = debug('peertube:search:FindInBulkService')
13 13
14type BulkObservables <P extends number | string, R> = { 14type BulkObservables <P extends number | string, R> = {
15 notifier: Subject<P> 15 notifier: Subject<P>
@@ -36,7 +36,7 @@ export class FindInBulkService {
36 } 36 }
37 37
38 getVideo (uuid: string): Observable<Video> { 38 getVideo (uuid: string): Observable<Video> {
39 logger('Schedule video fetch for uuid %s.', uuid) 39 debugLogger('Schedule video fetch for uuid %s.', uuid)
40 40
41 return this.getData({ 41 return this.getData({
42 observableObject: this.getVideoInBulk, 42 observableObject: this.getVideoInBulk,
@@ -46,7 +46,7 @@ export class FindInBulkService {
46 } 46 }
47 47
48 getChannel (handle: string): Observable<VideoChannel> { 48 getChannel (handle: string): Observable<VideoChannel> {
49 logger('Schedule channel fetch for handle %s.', handle) 49 debugLogger('Schedule channel fetch for handle %s.', handle)
50 50
51 return this.getData({ 51 return this.getData({
52 observableObject: this.getChannelInBulk, 52 observableObject: this.getChannelInBulk,
@@ -56,7 +56,7 @@ export class FindInBulkService {
56 } 56 }
57 57
58 getPlaylist (uuid: string): Observable<VideoPlaylist> { 58 getPlaylist (uuid: string): Observable<VideoPlaylist> {
59 logger('Schedule playlist fetch for uuid %s.', uuid) 59 debugLogger('Schedule playlist fetch for uuid %s.', uuid)
60 60
61 return this.getData({ 61 return this.getData({
62 observableObject: this.getPlaylistInBulk, 62 observableObject: this.getPlaylistInBulk,
@@ -94,7 +94,7 @@ export class FindInBulkService {
94 } 94 }
95 95
96 private getVideosInBulk (uuids: string[]) { 96 private getVideosInBulk (uuids: string[]) {
97 logger('Fetching videos %s.', uuids.join(', ')) 97 debugLogger('Fetching videos %s.', uuids.join(', '))
98 98
99 return this.searchService.searchVideos({ 99 return this.searchService.searchVideos({
100 uuids, 100 uuids,
@@ -104,7 +104,7 @@ export class FindInBulkService {
104 } 104 }
105 105
106 private getChannelsInBulk (handles: string[]) { 106 private getChannelsInBulk (handles: string[]) {
107 logger('Fetching channels %s.', handles.join(', ')) 107 debugLogger('Fetching channels %s.', handles.join(', '))
108 108
109 return this.searchService.searchVideoChannels({ 109 return this.searchService.searchVideoChannels({
110 handles, 110 handles,
@@ -114,7 +114,7 @@ export class FindInBulkService {
114 } 114 }
115 115
116 private getPlaylistsInBulk (uuids: string[]) { 116 private getPlaylistsInBulk (uuids: string[]) {
117 logger('Fetching playlists %s.', uuids.join(', ')) 117 debugLogger('Fetching playlists %s.', uuids.join(', '))
118 118
119 return this.searchService.searchVideoPlaylists({ 119 return this.searchService.searchVideoPlaylists({
120 uuids, 120 uuids,
diff --git a/client/src/app/shared/shared-user-subscription/remote-subscribe.component.ts b/client/src/app/shared/shared-user-subscription/remote-subscribe.component.ts
index 369692715..7bcfdd8aa 100644
--- a/client/src/app/shared/shared-user-subscription/remote-subscribe.component.ts
+++ b/client/src/app/shared/shared-user-subscription/remote-subscribe.component.ts
@@ -1,6 +1,7 @@
1import { Component, Input, OnInit } from '@angular/core' 1import { Component, Input, OnInit } from '@angular/core'
2import { Notifier } from '@app/core' 2import { Notifier } from '@app/core'
3import { FormReactive, FormValidatorService } from '@app/shared/shared-forms' 3import { FormReactive, FormValidatorService } from '@app/shared/shared-forms'
4import { logger } from '@root-helpers/logger'
4import { USER_HANDLE_VALIDATOR } from '../form-validators/user-validators' 5import { USER_HANDLE_VALIDATOR } from '../form-validators/user-validators'
5 6
6@Component({ 7@Component({
@@ -59,7 +60,7 @@ export class RemoteSubscribeComponent extends FormReactive implements OnInit {
59 }) 60 })
60 .then(window.open) 61 .then(window.open)
61 .catch(err => { 62 .catch(err => {
62 console.error(err) 63 logger.error(err)
63 64
64 this.notifier.error($localize`Cannot fetch information of this remote account`) 65 this.notifier.error($localize`Cannot fetch information of this remote account`)
65 }) 66 })
diff --git a/client/src/app/shared/shared-user-subscription/user-subscription.service.ts b/client/src/app/shared/shared-user-subscription/user-subscription.service.ts
index 33a2d04fd..9cf6b4d16 100644
--- a/client/src/app/shared/shared-user-subscription/user-subscription.service.ts
+++ b/client/src/app/shared/shared-user-subscription/user-subscription.service.ts
@@ -9,7 +9,7 @@ import { Video, VideoChannel, VideoChannelService, VideoService } from '@app/sha
9import { ActorFollow, ResultList, VideoChannel as VideoChannelServer, VideoSortField } from '@shared/models' 9import { ActorFollow, ResultList, VideoChannel as VideoChannelServer, VideoSortField } from '@shared/models'
10import { environment } from '../../../environments/environment' 10import { environment } from '../../../environments/environment'
11 11
12const logger = debug('peertube:subscriptions:UserSubscriptionService') 12const debugLogger = debug('peertube:subscriptions:UserSubscriptionService')
13 13
14type SubscriptionExistResult = { [ uri: string ]: boolean } 14type SubscriptionExistResult = { [ uri: string ]: boolean }
15type SubscriptionExistResultObservable = { [ uri: string ]: Observable<boolean> } 15type SubscriptionExistResultObservable = { [ uri: string ]: Observable<boolean> }
@@ -176,17 +176,17 @@ export class UserSubscriptionService {
176 } 176 }
177 177
178 doesSubscriptionExist (nameWithHost: string) { 178 doesSubscriptionExist (nameWithHost: string) {
179 logger('Running subscription check for %d.', nameWithHost) 179 debugLogger('Running subscription check for %d.', nameWithHost)
180 180
181 if (nameWithHost in this.myAccountSubscriptionCache) { 181 if (nameWithHost in this.myAccountSubscriptionCache) {
182 logger('Found cache for %d.', nameWithHost) 182 debugLogger('Found cache for %d.', nameWithHost)
183 183
184 return of(this.myAccountSubscriptionCache[nameWithHost]) 184 return of(this.myAccountSubscriptionCache[nameWithHost])
185 } 185 }
186 186
187 this.existsSubject.next(nameWithHost) 187 this.existsSubject.next(nameWithHost)
188 188
189 logger('Fetching from network for %d.', nameWithHost) 189 debugLogger('Fetching from network for %d.', nameWithHost)
190 return this.existsObservable.pipe( 190 return this.existsObservable.pipe(
191 filter(existsResult => existsResult[nameWithHost] !== undefined), 191 filter(existsResult => existsResult[nameWithHost] !== undefined),
192 map(existsResult => existsResult[nameWithHost]), 192 map(existsResult => existsResult[nameWithHost]),
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 bbda39c2d..47482caaa 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
@@ -4,6 +4,7 @@ import { tap } from 'rxjs/operators'
4import { Component, ElementRef, Inject, LOCALE_ID, ViewChild } from '@angular/core' 4import { Component, ElementRef, Inject, LOCALE_ID, ViewChild } from '@angular/core'
5import { AuthService, HooksService, Notifier } from '@app/core' 5import { AuthService, HooksService, Notifier } 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 { VideoCaption, VideoFile, VideoPrivacy } from '@shared/models' 8import { VideoCaption, VideoFile, VideoPrivacy } from '@shared/models'
8import { BytesPipe, NumberFormatterPipe, VideoDetails, VideoService } from '../shared-main' 9import { BytesPipe, NumberFormatterPipe, VideoDetails, VideoService } from '../shared-main'
9 10
@@ -142,7 +143,7 @@ export class VideoDownloadComponent {
142 .find(f => f.resolution.id === this.resolutionId) 143 .find(f => f.resolution.id === this.resolutionId)
143 144
144 if (!file) { 145 if (!file) {
145 console.error('Could not find file with resolution %d.', this.resolutionId) 146 logger.error(`Could not find file with resolution ${this.resolutionId}`)
146 return undefined 147 return undefined
147 } 148 }
148 149
@@ -175,7 +176,7 @@ export class VideoDownloadComponent {
175 .find(c => c.language.id === this.subtitleLanguageId) 176 .find(c => c.language.id === this.subtitleLanguageId)
176 177
177 if (!caption) { 178 if (!caption) {
178 console.error('Cannot find caption %s.', this.subtitleLanguageId) 179 logger.error(`Cannot find caption ${this.subtitleLanguageId}`)
179 return undefined 180 return undefined
180 } 181 }
181 182
diff --git a/client/src/app/shared/shared-video-miniature/video-filters-header.component.ts b/client/src/app/shared/shared-video-miniature/video-filters-header.component.ts
index 7b806248b..a5da9ebf3 100644
--- a/client/src/app/shared/shared-video-miniature/video-filters-header.component.ts
+++ b/client/src/app/shared/shared-video-miniature/video-filters-header.component.ts
@@ -8,7 +8,7 @@ import { UserRight } from '@shared/models'
8import { PeertubeModalService } from '../shared-main' 8import { PeertubeModalService } from '../shared-main'
9import { VideoFilters } from './video-filters.model' 9import { VideoFilters } from './video-filters.model'
10 10
11const logger = debug('peertube:videos:VideoFiltersHeaderComponent') 11const debugLogger = debug('peertube:videos:VideoFiltersHeaderComponent')
12 12
13@Component({ 13@Component({
14 selector: 'my-video-filters-header', 14 selector: 'my-video-filters-header',
@@ -54,7 +54,7 @@ export class VideoFiltersHeaderComponent implements OnInit, OnDestroy {
54 }) 54 })
55 55
56 this.form.valueChanges.subscribe(values => { 56 this.form.valueChanges.subscribe(values => {
57 logger('Loading values from form: %O', values) 57 debugLogger('Loading values from form: %O', values)
58 58
59 this.filters.load(values) 59 this.filters.load(values)
60 this.filtersChanged.emit() 60 this.filtersChanged.emit()
@@ -105,6 +105,6 @@ export class VideoFiltersHeaderComponent implements OnInit, OnDestroy {
105 const defaultValues = this.filters.toFormObject() 105 const defaultValues = this.filters.toFormObject()
106 this.form.patchValue(defaultValues, { emitEvent }) 106 this.form.patchValue(defaultValues, { emitEvent })
107 107
108 logger('Patched form: %O', defaultValues) 108 debugLogger('Patched form: %O', defaultValues)
109 } 109 }
110} 110}
diff --git a/client/src/app/shared/shared-video-miniature/videos-list.component.ts b/client/src/app/shared/shared-video-miniature/videos-list.component.ts
index 38a80b973..508a189fd 100644
--- a/client/src/app/shared/shared-video-miniature/videos-list.component.ts
+++ b/client/src/app/shared/shared-video-miniature/videos-list.component.ts
@@ -14,13 +14,14 @@ import {
14 UserService 14 UserService
15} from '@app/core' 15} from '@app/core'
16import { GlobalIconName } from '@app/shared/shared-icons' 16import { GlobalIconName } from '@app/shared/shared-icons'
17import { logger } from '@root-helpers/logger'
17import { isLastMonth, isLastWeek, isThisMonth, isToday, isYesterday } from '@shared/core-utils' 18import { isLastMonth, isLastWeek, isThisMonth, isToday, isYesterday } from '@shared/core-utils'
18import { ResultList, UserRight, VideoSortField } from '@shared/models' 19import { ResultList, UserRight, VideoSortField } from '@shared/models'
19import { Syndication, Video } from '../shared-main' 20import { Syndication, Video } from '../shared-main'
20import { VideoFilters, VideoFilterScope } from './video-filters.model' 21import { VideoFilters, VideoFilterScope } from './video-filters.model'
21import { MiniatureDisplayOptions } from './video-miniature.component' 22import { MiniatureDisplayOptions } from './video-miniature.component'
22 23
23const logger = debug('peertube:videos:VideosListComponent') 24const debugLogger = debug('peertube:videos:VideosListComponent')
24 25
25export type HeaderAction = { 26export type HeaderAction = {
26 iconName: GlobalIconName 27 iconName: GlobalIconName
@@ -245,7 +246,7 @@ export class VideosListComponent implements OnInit, OnChanges, OnDestroy {
245 error: err => { 246 error: err => {
246 const message = $localize`Cannot load more videos. Try again later.` 247 const message = $localize`Cannot load more videos. Try again later.`
247 248
248 console.error(message, { err }) 249 logger.error(message, err)
249 this.notifier.error(message) 250 this.notifier.error(message)
250 } 251 }
251 }) 252 })
@@ -323,7 +324,7 @@ export class VideosListComponent implements OnInit, OnChanges, OnDestroy {
323 } 324 }
324 325
325 onFiltersChanged (customizedByUser: boolean) { 326 onFiltersChanged (customizedByUser: boolean) {
326 logger('Running on filters changed') 327 debugLogger('Running on filters changed')
327 328
328 this.updateUrl(customizedByUser) 329 this.updateUrl(customizedByUser)
329 330
@@ -364,7 +365,7 @@ export class VideosListComponent implements OnInit, OnChanges, OnDestroy {
364 if (!items || items.length === 0) this.syndicationItems = undefined 365 if (!items || items.length === 0) this.syndicationItems = undefined
365 else this.syndicationItems = items 366 else this.syndicationItems = items
366 }) 367 })
367 .catch(err => console.error('Cannot get syndication items.', err)) 368 .catch(err => logger.error('Cannot get syndication items.', err))
368 } 369 }
369 370
370 private updateUrl (customizedByUser: boolean) { 371 private updateUrl (customizedByUser: boolean) {
@@ -375,7 +376,7 @@ export class VideosListComponent implements OnInit, OnChanges, OnDestroy {
375 ? { ...baseQuery, c: customizedByUser } 376 ? { ...baseQuery, c: customizedByUser }
376 : baseQuery 377 : baseQuery
377 378
378 logger('Will inject %O in URL query', queryParams) 379 debugLogger('Will inject %O in URL query', queryParams)
379 380
380 const baseRoute = this.baseRouteBuilderFunction 381 const baseRoute = this.baseRouteBuilderFunction
381 ? this.baseRouteBuilderFunction(this.filters) 382 ? this.baseRouteBuilderFunction(this.filters)
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 bac828fba..fa3c79bbb 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
@@ -1,6 +1,7 @@
1import { Observable, Subject } from 'rxjs' 1import { 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 { ResultList, VideoSortField } from '@shared/models' 5import { ResultList, VideoSortField } from '@shared/models'
5import { PeerTubeTemplateDirective, Video } from '../shared-main' 6import { PeerTubeTemplateDirective, Video } from '../shared-main'
6import { MiniatureDisplayOptions } from './video-miniature.component' 7import { MiniatureDisplayOptions } from './video-miniature.component'
@@ -128,7 +129,7 @@ export class VideosSelectionComponent implements AfterContentInit {
128 error: err => { 129 error: err => {
129 const message = $localize`Cannot load more videos. Try again later.` 130 const message = $localize`Cannot load more videos. Try again later.`
130 131
131 console.error(message, { err }) 132 logger.error(message, err)
132 this.notifier.error(message) 133 this.notifier.error(message)
133 } 134 }
134 }) 135 })
diff --git a/client/src/app/shared/shared-video-playlist/video-add-to-playlist.component.ts b/client/src/app/shared/shared-video-playlist/video-add-to-playlist.component.ts
index e4972ec10..e019fdd26 100644
--- a/client/src/app/shared/shared-video-playlist/video-add-to-playlist.component.ts
+++ b/client/src/app/shared/shared-video-playlist/video-add-to-playlist.component.ts
@@ -16,7 +16,7 @@ import {
16import { VIDEO_PLAYLIST_DISPLAY_NAME_VALIDATOR } from '../form-validators/video-playlist-validators' 16import { VIDEO_PLAYLIST_DISPLAY_NAME_VALIDATOR } from '../form-validators/video-playlist-validators'
17import { CachedPlaylist, VideoPlaylistService } from './video-playlist.service' 17import { CachedPlaylist, VideoPlaylistService } from './video-playlist.service'
18 18
19const logger = debug('peertube:playlists:VideoAddToPlaylistComponent') 19const debugLogger = debug('peertube:playlists:VideoAddToPlaylistComponent')
20 20
21type PlaylistElement = { 21type PlaylistElement = {
22 enabled: boolean 22 enabled: boolean
@@ -110,7 +110,7 @@ export class VideoAddToPlaylistComponent extends FormReactive implements OnInit,
110 } 110 }
111 111
112 reload () { 112 reload () {
113 logger('Reloading component') 113 debugLogger('Reloading component')
114 114
115 this.videoPlaylists = [] 115 this.videoPlaylists = []
116 this.videoPlaylistSearch = undefined 116 this.videoPlaylistSearch = undefined
@@ -121,7 +121,7 @@ export class VideoAddToPlaylistComponent extends FormReactive implements OnInit,
121 } 121 }
122 122
123 load () { 123 load () {
124 logger('Loading component') 124 debugLogger('Loading component')
125 125
126 this.listenToVideoPlaylistChange() 126 this.listenToVideoPlaylistChange()
127 127
@@ -331,7 +331,7 @@ export class VideoAddToPlaylistComponent extends FormReactive implements OnInit,
331 } 331 }
332 332
333 private rebuildPlaylists (existResult: VideoExistInPlaylist[]) { 333 private rebuildPlaylists (existResult: VideoExistInPlaylist[]) {
334 logger('Got existing results for %d.', this.video.id, existResult) 334 debugLogger('Got existing results for %d.', this.video.id, existResult)
335 335
336 const oldPlaylists = this.videoPlaylists 336 const oldPlaylists = this.videoPlaylists
337 337
@@ -359,7 +359,7 @@ export class VideoAddToPlaylistComponent extends FormReactive implements OnInit,
359 this.videoPlaylists.push(playlistSummary) 359 this.videoPlaylists.push(playlistSummary)
360 } 360 }
361 361
362 logger('Rebuilt playlist state for video %d.', this.video.id, this.videoPlaylists) 362 debugLogger('Rebuilt playlist state for video %d.', this.video.id, this.videoPlaylists)
363 363
364 this.cd.markForCheck() 364 this.cd.markForCheck()
365 } 365 }
diff --git a/client/src/app/shared/shared-video-playlist/video-playlist.service.ts b/client/src/app/shared/shared-video-playlist/video-playlist.service.ts
index db9f78a7a..81ae0f292 100644
--- a/client/src/app/shared/shared-video-playlist/video-playlist.service.ts
+++ b/client/src/app/shared/shared-video-playlist/video-playlist.service.ts
@@ -23,7 +23,7 @@ import { environment } from '../../../environments/environment'
23import { VideoPlaylistElement } from './video-playlist-element.model' 23import { VideoPlaylistElement } from './video-playlist-element.model'
24import { VideoPlaylist } from './video-playlist.model' 24import { VideoPlaylist } from './video-playlist.model'
25 25
26const logger = debug('peertube:playlists:VideoPlaylistService') 26const debugLogger = debug('peertube:playlists:VideoPlaylistService')
27 27
28export type CachedPlaylist = VideoPlaylist | { id: number, displayName: string } 28export type CachedPlaylist = VideoPlaylist | { id: number, displayName: string }
29 29
@@ -287,15 +287,15 @@ export class VideoPlaylistService {
287 } 287 }
288 288
289 runPlaylistCheck (videoId: number) { 289 runPlaylistCheck (videoId: number) {
290 logger('Running playlist check.') 290 debugLogger('Running playlist check.')
291 291
292 if (this.videoExistsCache[videoId]) { 292 if (this.videoExistsCache[videoId]) {
293 logger('Found cache for %d.', videoId) 293 debugLogger('Found cache for %d.', videoId)
294 294
295 return this.videoExistsInPlaylistCacheSubject.next({ [videoId]: this.videoExistsCache[videoId] }) 295 return this.videoExistsInPlaylistCacheSubject.next({ [videoId]: this.videoExistsCache[videoId] })
296 } 296 }
297 297
298 logger('Fetching from network for %d.', videoId) 298 debugLogger('Fetching from network for %d.', videoId)
299 return this.videoExistsInPlaylistNotifier.next(videoId) 299 return this.videoExistsInPlaylistNotifier.next(videoId)
300 } 300 }
301 301
diff --git a/client/src/assets/player/peertube-player-local-storage.ts b/client/src/assets/player/peertube-player-local-storage.ts
index d9dacfba5..64040abf1 100644
--- a/client/src/assets/player/peertube-player-local-storage.ts
+++ b/client/src/assets/player/peertube-player-local-storage.ts
@@ -1,3 +1,5 @@
1import { logger } from '@root-helpers/logger'
2
1function getStoredVolume () { 3function getStoredVolume () {
2 const value = getLocalStorage('volume') 4 const value = getLocalStorage('volume')
3 if (value !== null && value !== undefined) { 5 if (value !== null && value !== undefined) {
@@ -81,7 +83,7 @@ function getStoredVideoWatchHistory (videoUUID?: string) {
81 83
82 data = JSON.parse(value) 84 data = JSON.parse(value)
83 } catch (error) { 85 } catch (error) {
84 console.error('Cannot parse video watch history from local storage: ', error) 86 logger.error('Cannot parse video watch history from local storage/', error)
85 } 87 }
86 88
87 data = data || {} 89 data = data || {}
diff --git a/client/src/assets/player/peertube-player-manager.ts b/client/src/assets/player/peertube-player-manager.ts
index b24b6966e..b9077dcae 100644
--- a/client/src/assets/player/peertube-player-manager.ts
+++ b/client/src/assets/player/peertube-player-manager.ts
@@ -23,6 +23,7 @@ import './shared/mobile/peertube-mobile-plugin'
23import './shared/mobile/peertube-mobile-buttons' 23import './shared/mobile/peertube-mobile-buttons'
24import './shared/hotkeys/peertube-hotkeys-plugin' 24import './shared/hotkeys/peertube-hotkeys-plugin'
25import videojs from 'video.js' 25import videojs from 'video.js'
26import { logger } from '@root-helpers/logger'
26import { PluginsManager } from '@root-helpers/plugins-manager' 27import { PluginsManager } from '@root-helpers/plugins-manager'
27import { isMobile } from '@root-helpers/web-browser' 28import { isMobile } from '@root-helpers/web-browser'
28import { saveAverageBandwidth } from './peertube-player-local-storage' 29import { saveAverageBandwidth } from './peertube-player-local-storage'
@@ -145,7 +146,7 @@ export class PeertubePlayerManager {
145 return 146 return
146 } 147 }
147 148
148 console.log('Fast forwarding HLS to recover from an error.') 149 logger.info('Fast forwarding HLS to recover from an error.')
149 150
150 this.videojsDecodeErrors++ 151 this.videojsDecodeErrors++
151 152
@@ -170,7 +171,7 @@ export class PeertubePlayerManager {
170 return 171 return
171 } 172 }
172 173
173 console.log('Fallback to webtorrent.') 174 logger.info('Fallback to webtorrent.')
174 175
175 this.rebuildAndUpdateVideoElement(currentPlayer, options.common) 176 this.rebuildAndUpdateVideoElement(currentPlayer, options.common)
176 177
diff --git a/client/src/assets/player/shared/manager-options/hls-options-builder.ts b/client/src/assets/player/shared/manager-options/hls-options-builder.ts
index cdfad0f4c..ed12f6e8b 100644
--- a/client/src/assets/player/shared/manager-options/hls-options-builder.ts
+++ b/client/src/assets/player/shared/manager-options/hls-options-builder.ts
@@ -1,5 +1,6 @@
1import { HybridLoaderSettings } from '@peertube/p2p-media-loader-core' 1import { HybridLoaderSettings } from '@peertube/p2p-media-loader-core'
2import { HlsJsEngineSettings } from '@peertube/p2p-media-loader-hlsjs' 2import { HlsJsEngineSettings } from '@peertube/p2p-media-loader-hlsjs'
3import { logger } from '@root-helpers/logger'
3import { LiveVideoLatencyMode } from '@shared/models' 4import { LiveVideoLatencyMode } from '@shared/models'
4import { getAverageBandwidthInStore } from '../../peertube-player-local-storage' 5import { getAverageBandwidthInStore } from '../../peertube-player-local-storage'
5import { P2PMediaLoader, P2PMediaLoaderPluginOptions } from '../../types' 6import { P2PMediaLoader, P2PMediaLoaderPluginOptions } from '../../types'
@@ -61,7 +62,7 @@ export class HLSOptionsBuilder {
61 private getP2PMediaLoaderOptions (redundancyUrlManager: RedundancyUrlManager): HlsJsEngineSettings { 62 private getP2PMediaLoaderOptions (redundancyUrlManager: RedundancyUrlManager): HlsJsEngineSettings {
62 let consumeOnly = false 63 let consumeOnly = false
63 if ((navigator as any)?.connection?.type === 'cellular') { 64 if ((navigator as any)?.connection?.type === 'cellular') {
64 console.log('We are on a cellular connection: disabling seeding.') 65 logger.info('We are on a cellular connection: disabling seeding.')
65 consumeOnly = true 66 consumeOnly = true
66 } 67 }
67 68
diff --git a/client/src/assets/player/shared/mobile/peertube-mobile-plugin.ts b/client/src/assets/player/shared/mobile/peertube-mobile-plugin.ts
index 91dda7f94..646e9f8c6 100644
--- a/client/src/assets/player/shared/mobile/peertube-mobile-plugin.ts
+++ b/client/src/assets/player/shared/mobile/peertube-mobile-plugin.ts
@@ -1,8 +1,9 @@
1import { PeerTubeMobileButtons } from './peertube-mobile-buttons'
2import videojs from 'video.js'
3import debug from 'debug' 1import debug from 'debug'
2import videojs from 'video.js'
3import { logger } from '@root-helpers/logger'
4import { PeerTubeMobileButtons } from './peertube-mobile-buttons'
4 5
5const logger = debug('peertube:player:mobile') 6const debugLogger = debug('peertube:player:mobile')
6 7
7const Plugin = videojs.getPlugin('plugin') 8const Plugin = videojs.getPlugin('plugin')
8 9
@@ -45,7 +46,7 @@ class PeerTubeMobilePlugin extends Plugin {
45 if (!this.player.isFullscreen() || this.isPortraitVideo()) return 46 if (!this.player.isFullscreen() || this.isPortraitVideo()) return
46 47
47 screen.orientation.lock('landscape') 48 screen.orientation.lock('landscape')
48 .catch(err => console.error('Cannot lock screen to landscape.', err)) 49 .catch(err => logger.error('Cannot lock screen to landscape.', err))
49 }) 50 })
50 } 51 }
51 52
@@ -61,7 +62,7 @@ class PeerTubeMobilePlugin extends Plugin {
61 } 62 }
62 63
63 if (this.lastTapEvent && event.timeStamp - this.lastTapEvent.timeStamp < PeerTubeMobilePlugin.DOUBLE_TAP_DELAY_MS) { 64 if (this.lastTapEvent && event.timeStamp - this.lastTapEvent.timeStamp < PeerTubeMobilePlugin.DOUBLE_TAP_DELAY_MS) {
64 logger('Detected double tap') 65 debugLogger('Detected double tap')
65 66
66 this.lastTapEvent = undefined 67 this.lastTapEvent = undefined
67 this.onDoubleTap(event) 68 this.onDoubleTap(event)
@@ -71,7 +72,7 @@ class PeerTubeMobilePlugin extends Plugin {
71 this.newActiveState = !this.player.userActive() 72 this.newActiveState = !this.player.userActive()
72 73
73 this.tapTimeout = setTimeout(() => { 74 this.tapTimeout = setTimeout(() => {
74 logger('No double tap detected, set user active state to %s.', this.newActiveState) 75 debugLogger('No double tap detected, set user active state to %s.', this.newActiveState)
75 76
76 this.player.userActive(this.newActiveState) 77 this.player.userActive(this.newActiveState)
77 }, PeerTubeMobilePlugin.DOUBLE_TAP_DELAY_MS) 78 }, PeerTubeMobilePlugin.DOUBLE_TAP_DELAY_MS)
@@ -100,19 +101,19 @@ class PeerTubeMobilePlugin extends Plugin {
100 const rect = this.findPlayerTarget((event.target as HTMLElement)).getBoundingClientRect() 101 const rect = this.findPlayerTarget((event.target as HTMLElement)).getBoundingClientRect()
101 const offsetX = event.targetTouches[0].pageX - rect.left 102 const offsetX = event.targetTouches[0].pageX - rect.left
102 103
103 logger('Calculating double tap zone (player width: %d, offset X: %d)', playerWidth, offsetX) 104 debugLogger('Calculating double tap zone (player width: %d, offset X: %d)', playerWidth, offsetX)
104 105
105 if (offsetX > 0.66 * playerWidth) { 106 if (offsetX > 0.66 * playerWidth) {
106 if (this.seekAmount < 0) this.seekAmount = 0 107 if (this.seekAmount < 0) this.seekAmount = 0
107 108
108 this.seekAmount += 10 109 this.seekAmount += 10
109 110
110 logger('Will forward %d seconds', this.seekAmount) 111 debugLogger('Will forward %d seconds', this.seekAmount)
111 } else if (offsetX < 0.33 * playerWidth) { 112 } else if (offsetX < 0.33 * playerWidth) {
112 if (this.seekAmount > 0) this.seekAmount = 0 113 if (this.seekAmount > 0) this.seekAmount = 0
113 114
114 this.seekAmount -= 10 115 this.seekAmount -= 10
115 logger('Will rewind %d seconds', this.seekAmount) 116 debugLogger('Will rewind %d seconds', this.seekAmount)
116 } 117 }
117 118
118 this.peerTubeMobileButtons.displayFastSeek(this.seekAmount) 119 this.peerTubeMobileButtons.displayFastSeek(this.seekAmount)
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 d0105fa36..e49e5c694 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
@@ -3,6 +3,7 @@
3 3
4import Hlsjs, { ErrorData, HlsConfig, Level, LevelSwitchingData, ManifestParsedData } from 'hls.js' 4import Hlsjs, { ErrorData, HlsConfig, Level, LevelSwitchingData, ManifestParsedData } from 'hls.js'
5import videojs from 'video.js' 5import videojs from 'video.js'
6import { logger } from '@root-helpers/logger'
6import { HlsjsConfigHandlerOptions, PeerTubeResolution, VideoJSTechHLS } from '../../types' 7import { HlsjsConfigHandlerOptions, PeerTubeResolution, VideoJSTechHLS } from '../../types'
7 8
8type ErrorCounts = { 9type ErrorCounts = {
@@ -17,14 +18,14 @@ type HookFn = (player: videojs.Player, hljs: Hlsjs) => void
17 18
18const registerSourceHandler = function (vjs: typeof videojs) { 19const registerSourceHandler = function (vjs: typeof videojs) {
19 if (!Hlsjs.isSupported()) { 20 if (!Hlsjs.isSupported()) {
20 console.warn('Hls.js is not supported in this browser!') 21 logger.warn('Hls.js is not supported in this browser!')
21 return 22 return
22 } 23 }
23 24
24 const html5 = vjs.getTech('Html5') 25 const html5 = vjs.getTech('Html5')
25 26
26 if (!html5) { 27 if (!html5) {
27 console.error('No Hml5 tech found in videojs') 28 logger.error('No Hml5 tech found in videojs')
28 return 29 return
29 } 30 }
30 31
@@ -120,7 +121,7 @@ class Html5Hlsjs {
120 121
121 if (!mediaError) return 122 if (!mediaError) return
122 123
123 console.log(mediaError) 124 logger.info(mediaError)
124 switch (mediaError.code) { 125 switch (mediaError.code) {
125 case mediaError.MEDIA_ERR_ABORTED: 126 case mediaError.MEDIA_ERR_ABORTED:
126 errorTxt = 'You aborted the video playback' 127 errorTxt = 'You aborted the video playback'
@@ -141,7 +142,7 @@ class Html5Hlsjs {
141 errorTxt = mediaError.message 142 errorTxt = mediaError.message
142 } 143 }
143 144
144 console.error('MEDIA_ERROR: ', errorTxt) 145 logger.error(`MEDIA_ERROR: ${errorTxt}`)
145 }) 146 })
146 147
147 this.initialize() 148 this.initialize()
@@ -212,20 +213,20 @@ class Html5Hlsjs {
212 213
213 private _handleMediaError (error: any) { 214 private _handleMediaError (error: any) {
214 if (this.errorCounts[Hlsjs.ErrorTypes.MEDIA_ERROR] === 1) { 215 if (this.errorCounts[Hlsjs.ErrorTypes.MEDIA_ERROR] === 1) {
215 console.info('trying to recover media error') 216 logger.info('trying to recover media error')
216 this.hls.recoverMediaError() 217 this.hls.recoverMediaError()
217 return 218 return
218 } 219 }
219 220
220 if (this.errorCounts[Hlsjs.ErrorTypes.MEDIA_ERROR] === 2) { 221 if (this.errorCounts[Hlsjs.ErrorTypes.MEDIA_ERROR] === 2) {
221 console.info('2nd try to recover media error (by swapping audio codec') 222 logger.info('2nd try to recover media error (by swapping audio codec')
222 this.hls.swapAudioCodec() 223 this.hls.swapAudioCodec()
223 this.hls.recoverMediaError() 224 this.hls.recoverMediaError()
224 return 225 return
225 } 226 }
226 227
227 if (this.errorCounts[Hlsjs.ErrorTypes.MEDIA_ERROR] > 2) { 228 if (this.errorCounts[Hlsjs.ErrorTypes.MEDIA_ERROR] > 2) {
228 console.info('bubbling media error up to VIDEOJS') 229 logger.info('bubbling media error up to VIDEOJS')
229 this.hls.destroy() 230 this.hls.destroy()
230 this.tech.error = () => error 231 this.tech.error = () => error
231 this.tech.trigger('error') 232 this.tech.trigger('error')
@@ -234,7 +235,7 @@ class Html5Hlsjs {
234 235
235 private _handleNetworkError (error: any) { 236 private _handleNetworkError (error: any) {
236 if (this.errorCounts[Hlsjs.ErrorTypes.NETWORK_ERROR] <= this.maxNetworkErrorRecovery) { 237 if (this.errorCounts[Hlsjs.ErrorTypes.NETWORK_ERROR] <= this.maxNetworkErrorRecovery) {
237 console.info('trying to recover network error') 238 logger.info('trying to recover network error')
238 239
239 // Wait 1 second and retry 240 // Wait 1 second and retry
240 setTimeout(() => this.hls.startLoad(), 1000) 241 setTimeout(() => this.hls.startLoad(), 1000)
@@ -247,7 +248,7 @@ class Html5Hlsjs {
247 return 248 return
248 } 249 }
249 250
250 console.info('bubbling network error up to VIDEOJS') 251 logger.info('bubbling network error up to VIDEOJS')
251 this.hls.destroy() 252 this.hls.destroy()
252 this.tech.error = () => error 253 this.tech.error = () => error
253 this.tech.trigger('error') 254 this.tech.trigger('error')
@@ -262,8 +263,8 @@ class Html5Hlsjs {
262 if (this.errorCounts[data.type]) this.errorCounts[data.type] += 1 263 if (this.errorCounts[data.type]) this.errorCounts[data.type] += 1
263 else this.errorCounts[data.type] = 1 264 else this.errorCounts[data.type] = 1
264 265
265 if (data.fatal) console.warn(error.message) 266 if (data.fatal) logger.warn(error.message)
266 else console.error(error.message, data) 267 else logger.error(error.message, { data })
267 268
268 if (data.type === Hlsjs.ErrorTypes.NETWORK_ERROR) { 269 if (data.type === Hlsjs.ErrorTypes.NETWORK_ERROR) {
269 error.code = 2 270 error.code = 2
@@ -273,7 +274,7 @@ class Html5Hlsjs {
273 this._handleMediaError(error) 274 this._handleMediaError(error)
274 } else if (data.fatal) { 275 } else if (data.fatal) {
275 this.hls.destroy() 276 this.hls.destroy()
276 console.info('bubbling error up to VIDEOJS') 277 logger.info('bubbling error up to VIDEOJS')
277 this.tech.error = () => error as any 278 this.tech.error = () => error as any
278 this.tech.trigger('error') 279 this.tech.trigger('error')
279 } 280 }
diff --git a/client/src/assets/player/shared/p2p-media-loader/p2p-media-loader-plugin.ts b/client/src/assets/player/shared/p2p-media-loader/p2p-media-loader-plugin.ts
index 5c0f0021f..e5f099dea 100644
--- a/client/src/assets/player/shared/p2p-media-loader/p2p-media-loader-plugin.ts
+++ b/client/src/assets/player/shared/p2p-media-loader/p2p-media-loader-plugin.ts
@@ -5,6 +5,7 @@ import { Engine, initHlsJsPlayer, initVideoJsContribHlsJsPlayer } from '@peertub
5import { timeToInt } from '@shared/core-utils' 5import { timeToInt } from '@shared/core-utils'
6import { P2PMediaLoaderPluginOptions, PlayerNetworkInfo } from '../../types' 6import { P2PMediaLoaderPluginOptions, PlayerNetworkInfo } from '../../types'
7import { registerConfigPlugin, registerSourceHandler } from './hls-plugin' 7import { registerConfigPlugin, registerSourceHandler } from './hls-plugin'
8import { logger } from '@root-helpers/logger'
8 9
9registerConfigPlugin(videojs) 10registerConfigPlugin(videojs)
10registerSourceHandler(videojs) 11registerSourceHandler(videojs)
@@ -43,11 +44,11 @@ class P2pMediaLoaderPlugin extends Plugin {
43 44
44 // FIXME: typings https://github.com/Microsoft/TypeScript/issues/14080 45 // FIXME: typings https://github.com/Microsoft/TypeScript/issues/14080
45 if (!(videojs as any).Html5Hlsjs) { 46 if (!(videojs as any).Html5Hlsjs) {
46 console.warn('HLS.js does not seem to be supported. Try to fallback to built in HLS.') 47 logger.warn('HLS.js does not seem to be supported. Try to fallback to built in HLS.')
47 48
48 if (!player.canPlayType('application/vnd.apple.mpegurl')) { 49 if (!player.canPlayType('application/vnd.apple.mpegurl')) {
49 const message = 'Cannot fallback to built-in HLS' 50 const message = 'Cannot fallback to built-in HLS'
50 console.warn(message) 51 logger.warn(message)
51 52
52 player.ready(() => player.trigger('error', new Error(message))) 53 player.ready(() => player.trigger('error', new Error(message)))
53 return 54 return
@@ -114,7 +115,7 @@ class P2pMediaLoaderPlugin extends Plugin {
114 this.p2pEngine = this.options.loader.getEngine() 115 this.p2pEngine = this.options.loader.getEngine()
115 116
116 this.p2pEngine.on(Events.SegmentError, (segment: Segment, err) => { 117 this.p2pEngine.on(Events.SegmentError, (segment: Segment, err) => {
117 console.error('Segment error.', segment, err) 118 logger.error(`Segment ${segment.id} error.`, err)
118 119
119 this.options.redundancyUrlManager.removeBySegmentUrl(segment.requestUrl) 120 this.options.redundancyUrlManager.removeBySegmentUrl(segment.requestUrl)
120 }) 121 })
diff --git a/client/src/assets/player/shared/p2p-media-loader/redundancy-url-manager.ts b/client/src/assets/player/shared/p2p-media-loader/redundancy-url-manager.ts
index abab8aa99..376efb835 100644
--- a/client/src/assets/player/shared/p2p-media-loader/redundancy-url-manager.ts
+++ b/client/src/assets/player/shared/p2p-media-loader/redundancy-url-manager.ts
@@ -1,4 +1,5 @@
1import { basename, dirname } from 'path' 1import { basename, dirname } from 'path'
2import { logger } from '@root-helpers/logger'
2 3
3class RedundancyUrlManager { 4class RedundancyUrlManager {
4 5
@@ -7,7 +8,7 @@ class RedundancyUrlManager {
7 } 8 }
8 9
9 removeBySegmentUrl (segmentUrl: string) { 10 removeBySegmentUrl (segmentUrl: string) {
10 console.log('Removing redundancy of segment URL %s.', segmentUrl) 11 logger.info(`Removing redundancy of segment URL ${segmentUrl}.`)
11 12
12 const baseUrl = dirname(segmentUrl) 13 const baseUrl = dirname(segmentUrl)
13 14
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 f7f83a8a4..18cb6750f 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
@@ -1,6 +1,7 @@
1import { wait } from '@root-helpers/utils'
2import { Segment } from '@peertube/p2p-media-loader-core'
3import { basename } from 'path' 1import { basename } from 'path'
2import { Segment } from '@peertube/p2p-media-loader-core'
3import { logger } from '@root-helpers/logger'
4import { wait } from '@root-helpers/utils'
4 5
5type SegmentsJSON = { [filename: string]: string | { [byterange: string]: string } } 6type SegmentsJSON = { [filename: string]: string | { [byterange: string]: string } }
6 7
@@ -23,7 +24,7 @@ function segmentValidatorFactory (segmentsSha256Url: string, isLive: boolean) {
23 } 24 }
24 25
25 if (!segmentValue) { 26 if (!segmentValue) {
26 console.log('Refetching sha segments for %s.', filename) 27 logger.info(`Refetching sha segments for ${filename}`)
27 28
28 await wait(1000) 29 await wait(1000)
29 30
@@ -71,7 +72,7 @@ function fetchSha256Segments (url: string) {
71 return fetch(url) 72 return fetch(url)
72 .then(res => res.json() as Promise<SegmentsJSON>) 73 .then(res => res.json() as Promise<SegmentsJSON>)
73 .catch(err => { 74 .catch(err => {
74 console.error('Cannot get sha256 segments', err) 75 logger.error('Cannot get sha256 segments', err)
75 return {} 76 return {}
76 }) 77 })
77} 78}
diff --git a/client/src/assets/player/shared/peertube/peertube-plugin.ts b/client/src/assets/player/shared/peertube/peertube-plugin.ts
index a29a0921f..69a7b2d65 100644
--- a/client/src/assets/player/shared/peertube/peertube-plugin.ts
+++ b/client/src/assets/player/shared/peertube/peertube-plugin.ts
@@ -1,5 +1,6 @@
1import debug from 'debug' 1import debug from 'debug'
2import videojs from 'video.js' 2import videojs from 'video.js'
3import { logger } from '@root-helpers/logger'
3import { isMobile } from '@root-helpers/web-browser' 4import { isMobile } from '@root-helpers/web-browser'
4import { timeToInt } from '@shared/core-utils' 5import { timeToInt } from '@shared/core-utils'
5import { VideoView, VideoViewEvent } from '@shared/models/videos' 6import { VideoView, VideoViewEvent } from '@shared/models/videos'
@@ -15,7 +16,7 @@ import {
15import { PeerTubePluginOptions, VideoJSCaption } from '../../types' 16import { PeerTubePluginOptions, VideoJSCaption } from '../../types'
16import { SettingsButton } from '../settings/settings-menu-button' 17import { SettingsButton } from '../settings/settings-menu-button'
17 18
18const logger = debug('peertube:player:peertube') 19const debugLogger = debug('peertube:player:peertube')
19 20
20const Plugin = videojs.getPlugin('plugin') 21const Plugin = videojs.getPlugin('plugin')
21 22
@@ -176,7 +177,7 @@ class PeerTubePlugin extends Plugin {
176 lastCurrentTime = currentTime 177 lastCurrentTime = currentTime
177 178
178 this.notifyUserIsWatching(currentTime, lastViewEvent) 179 this.notifyUserIsWatching(currentTime, lastViewEvent)
179 .catch(err => console.error('Cannot notify user is watching.', err)) 180 .catch(err => logger.error('Cannot notify user is watching.', err))
180 181
181 lastViewEvent = undefined 182 lastViewEvent = undefined
182 183
@@ -249,7 +250,7 @@ class PeerTubePlugin extends Plugin {
249 (this.player as any).cache_.inactivityTimeout = timeout 250 (this.player as any).cache_.inactivityTimeout = timeout
250 this.player.options_.inactivityTimeout = timeout 251 this.player.options_.inactivityTimeout = timeout
251 252
252 logger('Set player inactivity to ' + timeout) 253 debugLogger('Set player inactivity to ' + timeout)
253 } 254 }
254 255
255 private initCaptions () { 256 private initCaptions () {
diff --git a/client/src/assets/player/shared/stats/stats-card.ts b/client/src/assets/player/shared/stats/stats-card.ts
index e9f9b6bd2..b65adcfca 100644
--- a/client/src/assets/player/shared/stats/stats-card.ts
+++ b/client/src/assets/player/shared/stats/stats-card.ts
@@ -1,4 +1,5 @@
1import videojs from 'video.js' 1import videojs from 'video.js'
2import { logger } from '@root-helpers/logger'
2import { secondsToTime } from '@shared/core-utils' 3import { secondsToTime } from '@shared/core-utils'
3import { PlayerNetworkInfo as EventPlayerNetworkInfo } from '../../types' 4import { PlayerNetworkInfo as EventPlayerNetworkInfo } from '../../types'
4import { bytes } from '../common' 5import { bytes } from '../common'
@@ -125,7 +126,7 @@ class StatsCard extends Component {
125 126
126 this.populateInfoValues(options) 127 this.populateInfoValues(options)
127 } catch (err) { 128 } catch (err) {
128 console.error('Cannot update stats.', err) 129 logger.error('Cannot update stats.', err)
129 clearInterval(this.updateInterval) 130 clearInterval(this.updateInterval)
130 } 131 }
131 }, this.intervalMs) 132 }, this.intervalMs)
diff --git a/client/src/assets/player/shared/webtorrent/peertube-chunk-store.ts b/client/src/assets/player/shared/webtorrent/peertube-chunk-store.ts
index 81378c277..74ae17704 100644
--- a/client/src/assets/player/shared/webtorrent/peertube-chunk-store.ts
+++ b/client/src/assets/player/shared/webtorrent/peertube-chunk-store.ts
@@ -2,8 +2,9 @@
2// We use temporary IndexDB (all data are removed on destroy) to avoid RAM issues 2// We use temporary IndexDB (all data are removed on destroy) to avoid RAM issues
3// Thanks @santiagogil and @Feross 3// Thanks @santiagogil and @Feross
4 4
5import { EventEmitter } from 'events'
6import Dexie from 'dexie' 5import Dexie from 'dexie'
6import { EventEmitter } from 'events'
7import { logger } from '@root-helpers/logger'
7 8
8class ChunkDatabase extends Dexie { 9class ChunkDatabase extends Dexie {
9 chunks: Dexie.Table<{ id: number, buf: Buffer }, number> 10 chunks: Dexie.Table<{ id: number, buf: Buffer }, number>
@@ -104,7 +105,7 @@ export class PeertubeChunkStore extends EventEmitter {
104 return this.db.chunks.bulkPut(processing.map(p => ({ id: p.id, buf: p.buf }))) 105 return this.db.chunks.bulkPut(processing.map(p => ({ id: p.id, buf: p.buf })))
105 }) 106 })
106 } catch (err) { 107 } catch (err) {
107 console.log('Cannot bulk insert chunks. Store them in memory.', { err }) 108 logger.info('Cannot bulk insert chunks. Store them in memory.', err)
108 109
109 processing.forEach(p => { 110 processing.forEach(p => {
110 this.memoryChunks[p.id] = p.buf 111 this.memoryChunks[p.id] = p.buf
@@ -143,7 +144,7 @@ export class PeertubeChunkStore extends EventEmitter {
143 return cb(null, buf.slice(offset, len + offset)) 144 return cb(null, buf.slice(offset, len + offset))
144 }) 145 })
145 .catch(err => { 146 .catch(err => {
146 console.error(err) 147 logger.error(err)
147 return cb(err) 148 return cb(err)
148 }) 149 })
149 } 150 }
@@ -176,7 +177,7 @@ export class PeertubeChunkStore extends EventEmitter {
176 177
177 return cb() 178 return cb()
178 } catch (err) { 179 } catch (err) {
179 console.error('Cannot destroy peertube chunk store.', err) 180 logger.error('Cannot destroy peertube chunk store.', err)
180 return cb(err) 181 return cb(err)
181 } 182 }
182 } 183 }
@@ -204,7 +205,7 @@ export class PeertubeChunkStore extends EventEmitter {
204 databasesToDeleteInfo = await this.expirationDB.databases.where('expiration').below(now).toArray() 205 databasesToDeleteInfo = await this.expirationDB.databases.where('expiration').below(now).toArray()
205 }) 206 })
206 } catch (err) { 207 } catch (err) {
207 console.error('Cannot update expiration of fetch expired databases.', err) 208 logger.error('Cannot update expiration of fetch expired databases.', err)
208 } 209 }
209 210
210 for (const databaseToDeleteInfo of databasesToDeleteInfo) { 211 for (const databaseToDeleteInfo of databasesToDeleteInfo) {
@@ -214,7 +215,7 @@ export class PeertubeChunkStore extends EventEmitter {
214 215
215 private async dropDatabase (databaseName: string) { 216 private async dropDatabase (databaseName: string) {
216 const dbToDelete = new ChunkDatabase(databaseName) 217 const dbToDelete = new ChunkDatabase(databaseName)
217 console.log('Destroying IndexDB database %s.', databaseName) 218 logger.info(`Destroying IndexDB database ${databaseName}`)
218 219
219 try { 220 try {
220 await dbToDelete.delete() 221 await dbToDelete.delete()
@@ -223,7 +224,7 @@ export class PeertubeChunkStore extends EventEmitter {
223 return this.expirationDB.databases.where({ name: databaseName }).delete() 224 return this.expirationDB.databases.where({ name: databaseName }).delete()
224 }) 225 })
225 } catch (err) { 226 } catch (err) {
226 console.error('Cannot delete %s.', databaseName, err) 227 logger.error(`Cannot delete ${databaseName}.`, err)
227 } 228 }
228 } 229 }
229 230
diff --git a/client/src/assets/player/shared/webtorrent/video-renderer.ts b/client/src/assets/player/shared/webtorrent/video-renderer.ts
index 9b80fea2c..a85d7a838 100644
--- a/client/src/assets/player/shared/webtorrent/video-renderer.ts
+++ b/client/src/assets/player/shared/webtorrent/video-renderer.ts
@@ -1,6 +1,7 @@
1// Thanks: https://github.com/feross/render-media 1// Thanks: https://github.com/feross/render-media
2 2
3const MediaElementWrapper = require('mediasource') 3const MediaElementWrapper = require('mediasource')
4import { logger } from '@root-helpers/logger'
4import { extname } from 'path' 5import { extname } from 'path'
5const Videostream = require('videostream') 6const Videostream = require('videostream')
6 7
@@ -77,8 +78,8 @@ function renderMedia (file: any, elem: HTMLVideoElement, opts: RenderMediaOption
77 } 78 }
78 79
79 function fallbackToMediaSource (useVP9 = false) { 80 function fallbackToMediaSource (useVP9 = false) {
80 if (useVP9 === true) console.log('Falling back to media source with VP9 enabled.') 81 if (useVP9 === true) logger.info('Falling back to media source with VP9 enabled.')
81 else console.log('Falling back to media source..') 82 else logger.info('Falling back to media source..')
82 83
83 useMediaSource(useVP9) 84 useMediaSource(useVP9)
84 } 85 }
diff --git a/client/src/assets/player/shared/webtorrent/webtorrent-plugin.ts b/client/src/assets/player/shared/webtorrent/webtorrent-plugin.ts
index 83b483d87..ab9ab56ac 100644
--- a/client/src/assets/player/shared/webtorrent/webtorrent-plugin.ts
+++ b/client/src/assets/player/shared/webtorrent/webtorrent-plugin.ts
@@ -1,5 +1,6 @@
1import videojs from 'video.js' 1import videojs from 'video.js'
2import * as WebTorrent from 'webtorrent' 2import * as WebTorrent from 'webtorrent'
3import { logger } from '@root-helpers/logger'
3import { isIOS } from '@root-helpers/web-browser' 4import { isIOS } from '@root-helpers/web-browser'
4import { timeToInt } from '@shared/core-utils' 5import { timeToInt } from '@shared/core-utils'
5import { VideoFile } from '@shared/models' 6import { VideoFile } from '@shared/models'
@@ -210,7 +211,7 @@ class WebTorrentPlugin extends Plugin {
210 if (destroyRenderer === true && this.renderer && this.renderer.destroy) this.renderer.destroy() 211 if (destroyRenderer === true && this.renderer && this.renderer.destroy) this.renderer.destroy()
211 212
212 this.webtorrent.remove(videoFile.magnetUri) 213 this.webtorrent.remove(videoFile.magnetUri)
213 console.log('Removed ' + videoFile.magnetUri) 214 logger.info(`Removed ${videoFile.magnetUri}`)
214 } 215 }
215 } 216 }
216 217
@@ -256,7 +257,7 @@ class WebTorrentPlugin extends Plugin {
256 ) { 257 ) {
257 if (!magnetOrTorrentUrl) return this.fallbackToHttp(options, done) 258 if (!magnetOrTorrentUrl) return this.fallbackToHttp(options, done)
258 259
259 console.log('Adding ' + magnetOrTorrentUrl + '.') 260 logger.info(`Adding ${magnetOrTorrentUrl}.`)
260 261
261 const oldTorrent = this.torrent 262 const oldTorrent = this.torrent
262 const torrentOptions = { 263 const torrentOptions = {
@@ -269,7 +270,7 @@ class WebTorrentPlugin extends Plugin {
269 } 270 }
270 271
271 this.torrent = this.webtorrent.add(magnetOrTorrentUrl, torrentOptions, torrent => { 272 this.torrent = this.webtorrent.add(magnetOrTorrentUrl, torrentOptions, torrent => {
272 console.log('Added ' + magnetOrTorrentUrl + '.') 273 logger.info(`Added ${magnetOrTorrentUrl}.`)
273 274
274 if (oldTorrent) { 275 if (oldTorrent) {
275 // Pause the old torrent 276 // Pause the old torrent
@@ -309,7 +310,7 @@ class WebTorrentPlugin extends Plugin {
309 }, options.delay || 0) 310 }, options.delay || 0)
310 }) 311 })
311 312
312 this.torrent.on('error', (err: any) => console.error(err)) 313 this.torrent.on('error', (err: any) => logger.error(err))
313 314
314 this.torrent.on('warning', (err: any) => { 315 this.torrent.on('warning', (err: any) => {
315 // We don't support HTTP tracker but we don't care -> we use the web socket tracker 316 // We don't support HTTP tracker but we don't care -> we use the web socket tracker
@@ -317,13 +318,13 @@ class WebTorrentPlugin extends Plugin {
317 318
318 // Users don't care about issues with WebRTC, but developers do so log it in the console 319 // Users don't care about issues with WebRTC, but developers do so log it in the console
319 if (err.message.indexOf('Ice connection failed') !== -1) { 320 if (err.message.indexOf('Ice connection failed') !== -1) {
320 console.log(err) 321 logger.info(err)
321 return 322 return
322 } 323 }
323 324
324 // Magnet hash is not up to date with the torrent file, add directly the torrent file 325 // Magnet hash is not up to date with the torrent file, add directly the torrent file
325 if (err.message.indexOf('incorrect info hash') !== -1) { 326 if (err.message.indexOf('incorrect info hash') !== -1) {
326 console.error('Incorrect info hash detected, falling back to torrent file.') 327 logger.error('Incorrect info hash detected, falling back to torrent file.')
327 const newOptions = { forcePlay: true, seek: options.seek } 328 const newOptions = { forcePlay: true, seek: options.seek }
328 return this.addTorrent(this.torrent['xs'], previousVideoFile, newOptions, done) 329 return this.addTorrent(this.torrent['xs'], previousVideoFile, newOptions, done)
329 } 330 }
@@ -333,7 +334,7 @@ class WebTorrentPlugin extends Plugin {
333 this.handleError(err) 334 this.handleError(err)
334 } 335 }
335 336
336 console.warn(err) 337 logger.warn(err)
337 }) 338 })
338 } 339 }
339 340
@@ -348,7 +349,7 @@ class WebTorrentPlugin extends Plugin {
348 return 349 return
349 } 350 }
350 351
351 console.error(err) 352 logger.error(err)
352 this.player.pause() 353 this.player.pause()
353 this.player.posterImage.show() 354 this.player.posterImage.show()
354 this.player.removeClass('vjs-has-autoplay') 355 this.player.removeClass('vjs-has-autoplay')
@@ -465,10 +466,10 @@ class WebTorrentPlugin extends Plugin {
465 466
466 // Lower resolution 467 // Lower resolution
467 if (this.isPlayerWaiting() && file.resolution.id < this.currentVideoFile.resolution.id) { 468 if (this.isPlayerWaiting() && file.resolution.id < this.currentVideoFile.resolution.id) {
468 console.log('Downgrading automatically the resolution to: %s', file.resolution.label) 469 logger.info(`Downgrading automatically the resolution to: ${file.resolution.label}`)
469 changeResolution = true 470 changeResolution = true
470 } else if (file.resolution.id > this.currentVideoFile.resolution.id) { // Higher resolution 471 } else if (file.resolution.id > this.currentVideoFile.resolution.id) { // Higher resolution
471 console.log('Upgrading automatically the resolution to: %s', file.resolution.label) 472 logger.info(`Upgrading automatically the resolution to: ${file.resolution.label}`)
472 changeResolution = true 473 changeResolution = true
473 changeResolutionDelay = this.CONSTANTS.AUTO_QUALITY_HIGHER_RESOLUTION_DELAY 474 changeResolutionDelay = this.CONSTANTS.AUTO_QUALITY_HIGHER_RESOLUTION_DELAY
474 } 475 }
@@ -577,7 +578,7 @@ class WebTorrentPlugin extends Plugin {
577 578
578 // The renderer returns an error when we destroy it, so skip them 579 // The renderer returns an error when we destroy it, so skip them
579 if (this.destroyingFakeRenderer === false && err) { 580 if (this.destroyingFakeRenderer === false && err) {
580 console.error('Cannot render new torrent in fake video element.', err) 581 logger.error('Cannot render new torrent in fake video element.', err)
581 } 582 }
582 583
583 // Load the future file at the correct time (in delay MS - 2 seconds) 584 // Load the future file at the correct time (in delay MS - 2 seconds)
@@ -593,7 +594,7 @@ class WebTorrentPlugin extends Plugin {
593 try { 594 try {
594 this.fakeRenderer.destroy() 595 this.fakeRenderer.destroy()
595 } catch (err) { 596 } catch (err) {
596 console.log('Cannot destroy correctly fake renderer.', err) 597 logger.info('Cannot destroy correctly fake renderer.', err)
597 } 598 }
598 } 599 }
599 this.fakeRenderer = undefined 600 this.fakeRenderer = undefined
diff --git a/client/src/assets/player/translations-manager.ts b/client/src/assets/player/translations-manager.ts
index 8a6e67dda..bf9c2d471 100644
--- a/client/src/assets/player/translations-manager.ts
+++ b/client/src/assets/player/translations-manager.ts
@@ -1,3 +1,4 @@
1import { logger } from '@root-helpers/logger'
1import { getCompleteLocale, getShortLocale, is18nLocale, isDefaultLocale } from '@shared/core-utils/i18n' 2import { getCompleteLocale, getShortLocale, is18nLocale, isDefaultLocale } from '@shared/core-utils/i18n'
2 3
3export class TranslationsManager { 4export class TranslationsManager {
@@ -11,7 +12,7 @@ export class TranslationsManager {
11 return fetch(path + '/server.json') 12 return fetch(path + '/server.json')
12 .then(res => res.json()) 13 .then(res => res.json())
13 .catch(err => { 14 .catch(err => {
14 console.error('Cannot get server translations', err) 15 logger.error('Cannot get server translations', err)
15 return undefined 16 return undefined
16 }) 17 })
17 } 18 }
@@ -33,7 +34,7 @@ export class TranslationsManager {
33 return json 34 return json
34 }) 35 })
35 .catch(err => { 36 .catch(err => {
36 console.error('Cannot get player translations', err) 37 logger.error('Cannot get player translations', err)
37 return undefined 38 return undefined
38 }) 39 })
39 } 40 }
diff --git a/client/src/main.ts b/client/src/main.ts
index 84c82203d..432db0eac 100644
--- a/client/src/main.ts
+++ b/client/src/main.ts
@@ -3,11 +3,14 @@ import { enableDebugTools } from '@angular/platform-browser'
3import { platformBrowserDynamic } from '@angular/platform-browser-dynamic' 3import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'
4import { AppModule } from './app/app.module' 4import { AppModule } from './app/app.module'
5import { environment } from './environments/environment' 5import { environment } from './environments/environment'
6import { logger } from './root-helpers'
6 7
7if (environment.production) { 8if (environment.production) {
8 enableProdMode() 9 enableProdMode()
9} 10}
10 11
12logger.registerServerSending(environment.apiUrl)
13
11const bootstrap = () => platformBrowserDynamic() 14const bootstrap = () => platformBrowserDynamic()
12 .bootstrapModule(AppModule) 15 .bootstrapModule(AppModule)
13 .then(bootstrapModule => { 16 .then(bootstrapModule => {
@@ -22,7 +25,7 @@ const bootstrap = () => platformBrowserDynamic()
22 return bootstrapModule 25 return bootstrapModule
23 }) 26 })
24 .catch(err => { 27 .catch(err => {
25 console.error(err) 28 logger.error(err)
26 return null 29 return null
27 }) 30 })
28 31
diff --git a/client/src/root-helpers/images.ts b/client/src/root-helpers/images.ts
index fb229ce6d..c4b09ec3c 100644
--- a/client/src/root-helpers/images.ts
+++ b/client/src/root-helpers/images.ts
@@ -1,8 +1,10 @@
1import { logger } from './logger'
2
1function imageToDataURL (input: File | Blob) { 3function imageToDataURL (input: File | Blob) {
2 return new Promise<string>(res => { 4 return new Promise<string>(res => {
3 const reader = new FileReader() 5 const reader = new FileReader()
4 6
5 reader.onerror = err => console.error('Cannot read input file.', err) 7 reader.onerror = err => logger.error('Cannot read input file.', err)
6 reader.onloadend = () => res(reader.result as string) 8 reader.onloadend = () => res(reader.result as string)
7 reader.readAsDataURL(input) 9 reader.readAsDataURL(input)
8 }) 10 })
diff --git a/client/src/root-helpers/index.ts b/client/src/root-helpers/index.ts
index a19855761..86301eafa 100644
--- a/client/src/root-helpers/index.ts
+++ b/client/src/root-helpers/index.ts
@@ -2,6 +2,7 @@ export * from './users'
2export * from './bytes' 2export * from './bytes'
3export * from './images' 3export * from './images'
4export * from './local-storage-utils' 4export * from './local-storage-utils'
5export * from './logger'
5export * from './peertube-web-storage' 6export * from './peertube-web-storage'
6export * from './plugins-manager' 7export * from './plugins-manager'
7export * from './string' 8export * from './string'
diff --git a/client/src/root-helpers/logger.ts b/client/src/root-helpers/logger.ts
new file mode 100644
index 000000000..cd559cfa7
--- /dev/null
+++ b/client/src/root-helpers/logger.ts
@@ -0,0 +1,138 @@
1import { ClientLogCreate } from '@shared/models/server'
2import { peertubeLocalStorage } from './peertube-web-storage'
3import { UserTokens } from './users'
4
5export type LoggerHook = (message: LoggerMessage, meta?: LoggerMeta) => void
6export type LoggerLevel = 'info' | 'warn' | 'error'
7
8export type LoggerMessage = string | Error | object
9export type LoggerMeta = Error | { [ id: string ]: any, err?: Error }
10
11declare global {
12 interface Window {
13 logger: Logger
14 }
15}
16
17class Logger {
18 private readonly hooks: { level: LoggerLevel, hook: LoggerHook }[] = []
19
20 info (message: LoggerMessage, meta?: LoggerMeta) {
21 this.runHooks('info', message, meta)
22
23 if (meta) console.log(message, meta)
24 else console.log(message)
25 }
26
27 warn (message: LoggerMessage, meta?: LoggerMeta) {
28 this.runHooks('warn', message, meta)
29
30 if (meta) console.warn(message, meta)
31 else console.warn(message)
32 }
33
34 error (message: LoggerMessage, meta?: LoggerMeta) {
35 this.runHooks('error', message, meta)
36
37 if (meta) console.error(message, meta)
38 else console.error(message)
39 }
40
41 addHook (level: LoggerLevel, hook: LoggerHook) {
42 this.hooks.push({ level, hook })
43 }
44
45 registerServerSending (serverUrl: string) {
46 this.addHook('warn', (message, meta) => this.sendClientLog(serverUrl, this.buildServerLogPayload('warn', message, meta)))
47 this.addHook('error', (message, meta) => this.sendClientLog(serverUrl, this.buildServerLogPayload('error', message, meta)))
48 }
49
50 sendClientLog (serverUrl: string, payload: ClientLogCreate | null) {
51 if (!payload) return
52
53 const headers = new Headers({
54 Accept: 'application/json',
55 'Content-Type': 'application/json'
56 })
57
58 try {
59 const tokens = UserTokens.getUserTokens(peertubeLocalStorage)
60
61 if (tokens) headers.set('Authorization', `${tokens.tokenType} ${tokens.accessToken}`)
62 } catch (err) {
63 console.error('Cannot set tokens to client log sender.', { err })
64 }
65
66 try {
67 fetch(serverUrl + '/api/v1/server/logs/client', {
68 headers,
69 method: 'POST',
70 body: JSON.stringify(payload)
71 })
72 } catch (err) {
73 console.error('Cannot send client warn/error to server.', err)
74 }
75 }
76
77 private buildServerLogPayload (level: Extract<LoggerLevel, 'warn' | 'error'>, message: LoggerMessage, meta?: LoggerMeta) {
78 if (!message) return null
79
80 return {
81 message: this.buildMessageServerLogPayload(message),
82 userAgent: navigator.userAgent,
83 url: window.location.href,
84 level,
85 stackTrace: this.buildStackServerLogPayload(message, meta),
86 meta: this.buildMetaServerLogPayload(meta)
87 }
88 }
89
90 private buildMessageServerLogPayload (message: LoggerMessage) {
91 if (typeof message === 'string') return message
92 if (message instanceof Error) return message.message
93
94 return JSON.stringify(message)
95 }
96
97 private buildStackServerLogPayload (message: LoggerMessage, meta?: LoggerMeta) {
98 if (message instanceof Error) return message.stack
99 if (meta instanceof Error) return meta.stack
100 if (meta?.err instanceof Error) return meta.err.stack
101
102 return undefined
103 }
104
105 private buildMetaServerLogPayload (meta?: LoggerMeta) {
106 if (!meta) return undefined
107 if (meta instanceof Error) return undefined
108
109 let result: string
110
111 try {
112 result = JSON.stringify(meta, (key, value) => {
113 if (key === 'err') return undefined
114
115 return value
116 })
117 } catch (err) {
118 console.error('Cannot stringify meta.', err)
119 }
120
121 return result
122 }
123
124 private runHooks (level: LoggerLevel, message: LoggerMessage, meta?: LoggerMeta) {
125 for (const hookObj of this.hooks) {
126 if (hookObj.level !== level) continue
127
128 hookObj.hook(message, meta)
129 }
130 }
131}
132
133const logger = window.logger || new Logger()
134window.logger = logger
135
136export {
137 logger
138}
diff --git a/client/src/root-helpers/plugins-manager.ts b/client/src/root-helpers/plugins-manager.ts
index 49a19781b..37a52be72 100644
--- a/client/src/root-helpers/plugins-manager.ts
+++ b/client/src/root-helpers/plugins-manager.ts
@@ -21,6 +21,7 @@ import {
21} from '@shared/models' 21} from '@shared/models'
22import { environment } from '../environments/environment' 22import { environment } from '../environments/environment'
23import { ClientScript } from '../types' 23import { ClientScript } from '../types'
24import { logger } from './logger'
24 25
25interface HookStructValue extends RegisterClientHookOptions { 26interface HookStructValue extends RegisterClientHookOptions {
26 plugin: ServerConfigPlugin 27 plugin: ServerConfigPlugin
@@ -48,7 +49,7 @@ type OnSettingsScripts = (pluginInfo: PluginInfo, options: RegisterClientSetting
48 49
49type OnClientRoute = (options: RegisterClientRouteOptions) => void 50type OnClientRoute = (options: RegisterClientRouteOptions) => void
50 51
51const logger = debug('peertube:plugins') 52const debugLogger = debug('peertube:plugins')
52 53
53class PluginsManager { 54class PluginsManager {
54 private hooks: Hooks = {} 55 private hooks: Hooks = {}
@@ -109,10 +110,10 @@ class PluginsManager {
109 const hookType = getHookType(hookName) 110 const hookType = getHookType(hookName)
110 111
111 for (const hook of this.hooks[hookName]) { 112 for (const hook of this.hooks[hookName]) {
112 console.log('Running hook %s of plugin %s.', hookName, hook.plugin.name) 113 logger.info(`Running hook ${hookName} of plugin ${hook.plugin.name}`)
113 114
114 result = await internalRunHook(hook.handler, hookType, result, params, err => { 115 result = await internalRunHook(hook.handler, hookType, result, params, err => {
115 console.error('Cannot run hook %s of script %s of plugin %s.', hookName, hook.clientScript.script, hook.plugin.name, err) 116 logger.error(`Cannot run hook ${hookName} of script ${hook.clientScript.script} of plugin ${hook.plugin.name}`, err)
116 }) 117 })
117 } 118 }
118 119
@@ -170,7 +171,7 @@ class PluginsManager {
170 171
171 this.loadingScopes[scope] = true 172 this.loadingScopes[scope] = true
172 173
173 logger('Loading scope %s', scope) 174 debugLogger('Loading scope %s', scope)
174 175
175 try { 176 try {
176 if (!isReload) this.loadedScopes.push(scope) 177 if (!isReload) this.loadedScopes.push(scope)
@@ -180,7 +181,7 @@ class PluginsManager {
180 this.loadingScopes[scope] = false 181 this.loadingScopes[scope] = false
181 this.pluginsLoaded[scope].next(true) 182 this.pluginsLoaded[scope].next(true)
182 183
183 logger('Nothing to load for scope %s', scope) 184 debugLogger('Nothing to load for scope %s', scope)
184 return 185 return
185 } 186 }
186 187
@@ -200,9 +201,9 @@ class PluginsManager {
200 this.pluginsLoaded[scope].next(true) 201 this.pluginsLoaded[scope].next(true)
201 this.loadingScopes[scope] = false 202 this.loadingScopes[scope] = false
202 203
203 logger('Scope %s loaded', scope) 204 debugLogger('Scope %s loaded', scope)
204 } catch (err) { 205 } catch (err) {
205 console.error('Cannot load plugins by scope %s.', scope, err) 206 logger.error(`Cannot load plugins by scope ${scope}`, err)
206 } 207 }
207 } 208 }
208 209
@@ -211,7 +212,7 @@ class PluginsManager {
211 212
212 const registerHook = (options: RegisterClientHookOptions) => { 213 const registerHook = (options: RegisterClientHookOptions) => {
213 if (clientHookObject[options.target] !== true) { 214 if (clientHookObject[options.target] !== true) {
214 console.error('Unknown hook %s of plugin %s. Skipping.', options.target, plugin.name) 215 logger.error(`Unknown hook ${options.target} of plugin ${plugin.name}. Skipping.`)
215 return 216 return
216 } 217 }
217 218
@@ -252,7 +253,7 @@ class PluginsManager {
252 253
253 const peertubeHelpers = this.peertubeHelpersFactory(pluginInfo) 254 const peertubeHelpers = this.peertubeHelpersFactory(pluginInfo)
254 255
255 console.log('Loading script %s of plugin %s.', clientScript.script, plugin.name) 256 logger.info(`Loading script ${clientScript.script} of plugin ${plugin.name}`)
256 257
257 const absURL = (environment.apiUrl || window.location.origin) + clientScript.script 258 const absURL = (environment.apiUrl || window.location.origin) + clientScript.script
258 return dynamicImport(absURL) 259 return dynamicImport(absURL)
@@ -266,7 +267,7 @@ class PluginsManager {
266 }) 267 })
267 }) 268 })
268 .then(() => this.sortHooksByPriority()) 269 .then(() => this.sortHooksByPriority())
269 .catch(err => console.error('Cannot import or register plugin %s.', pluginInfo.plugin.name, err)) 270 .catch(err => logger.error(`Cannot import or register plugin ${pluginInfo.plugin.name}`, err))
270 } 271 }
271 272
272 private sortHooksByPriority () { 273 private sortHooksByPriority () {
@@ -294,7 +295,7 @@ async function dynamicImport (url: string) {
294 // eslint-disable-next-line no-new-func 295 // eslint-disable-next-line no-new-func
295 return new Function(`return import('${url}')`)() 296 return new Function(`return import('${url}')`)()
296 } catch { 297 } catch {
297 console.log('Fallback to import polyfill') 298 logger.info('Fallback to import polyfill')
298 299
299 return new Promise((resolve, reject) => { 300 return new Promise((resolve, reject) => {
300 const vector = '$importModule$' + Math.random().toString(32).slice(2) 301 const vector = '$importModule$' + Math.random().toString(32).slice(2)
diff --git a/client/src/standalone/videos/embed-api.ts b/client/src/standalone/videos/embed-api.ts
index 84d664654..2124b4711 100644
--- a/client/src/standalone/videos/embed-api.ts
+++ b/client/src/standalone/videos/embed-api.ts
@@ -1,6 +1,6 @@
1import './embed.scss' 1import './embed.scss'
2
3import * as Channel from 'jschannel' 2import * as Channel from 'jschannel'
3import { logger } from '../../root-helpers'
4import { PeerTubeResolution, PeerTubeTextTrack } from '../player/definitions' 4import { PeerTubeResolution, PeerTubeTextTrack } from '../player/definitions'
5import { PeerTubeEmbed } from './embed' 5import { PeerTubeEmbed } from './embed'
6 6
@@ -59,7 +59,7 @@ export class PeerTubeEmbedApi {
59 } 59 }
60 60
61 private setResolution (resolutionId: number) { 61 private setResolution (resolutionId: number) {
62 console.log('set resolution %d', resolutionId) 62 logger.info(`Set resolution ${resolutionId}`)
63 63
64 if (this.isWebtorrent()) { 64 if (this.isWebtorrent()) {
65 if (resolutionId === -1 && this.embed.player.webtorrent().isAutoResolutionPossible() === false) return 65 if (resolutionId === -1 && this.embed.player.webtorrent().isAutoResolutionPossible() === false) return
diff --git a/client/src/standalone/videos/embed.ts b/client/src/standalone/videos/embed.ts
index c15d4db17..5384ada1c 100644
--- a/client/src/standalone/videos/embed.ts
+++ b/client/src/standalone/videos/embed.ts
@@ -6,7 +6,7 @@ import { peertubeTranslate } from '../../../../shared/core-utils/i18n'
6import { HTMLServerConfig, LiveVideo, ResultList, VideoDetails, VideoPlaylist, VideoPlaylistElement } from '../../../../shared/models' 6import { HTMLServerConfig, LiveVideo, ResultList, VideoDetails, VideoPlaylist, VideoPlaylistElement } from '../../../../shared/models'
7import { PeertubePlayerManager } from '../../assets/player' 7import { PeertubePlayerManager } from '../../assets/player'
8import { TranslationsManager } from '../../assets/player/translations-manager' 8import { TranslationsManager } from '../../assets/player/translations-manager'
9import { getParamString } from '../../root-helpers' 9import { getParamString, logger } from '../../root-helpers'
10import { PeerTubeEmbedApi } from './embed-api' 10import { PeerTubeEmbedApi } from './embed-api'
11import { AuthHTTP, LiveManager, PeerTubePlugin, PlayerManagerOptions, PlaylistFetcher, PlaylistTracker, VideoFetcher } from './shared' 11import { AuthHTTP, LiveManager, PeerTubePlugin, PlayerManagerOptions, PlaylistFetcher, PlaylistTracker, VideoFetcher } from './shared'
12import { PlayerHTML } from './shared/player-html' 12import { PlayerHTML } from './shared/player-html'
@@ -31,6 +31,8 @@ export class PeerTubeEmbed {
31 private playlistTracker: PlaylistTracker 31 private playlistTracker: PlaylistTracker
32 32
33 constructor (videoWrapperId: string) { 33 constructor (videoWrapperId: string) {
34 logger.registerServerSending(window.location.origin)
35
34 this.http = new AuthHTTP() 36 this.http = new AuthHTTP()
35 37
36 this.videoFetcher = new VideoFetcher(this.http) 38 this.videoFetcher = new VideoFetcher(this.http)
@@ -43,7 +45,7 @@ export class PeerTubeEmbed {
43 try { 45 try {
44 this.config = JSON.parse(window['PeerTubeServerConfig']) 46 this.config = JSON.parse(window['PeerTubeServerConfig'])
45 } catch (err) { 47 } catch (err) {
46 console.error('Cannot parse HTML config.', err) 48 logger.error('Cannot parse HTML config.', err)
47 } 49 }
48 } 50 }
49 51
@@ -125,7 +127,7 @@ export class PeerTubeEmbed {
125 async playNextPlaylistVideo () { 127 async playNextPlaylistVideo () {
126 const next = this.playlistTracker.getNextPlaylistElement() 128 const next = this.playlistTracker.getNextPlaylistElement()
127 if (!next) { 129 if (!next) {
128 console.log('Next element not found in playlist.') 130 logger.info('Next element not found in playlist.')
129 return 131 return
130 } 132 }
131 133
@@ -137,7 +139,7 @@ export class PeerTubeEmbed {
137 async playPreviousPlaylistVideo () { 139 async playPreviousPlaylistVideo () {
138 const previous = this.playlistTracker.getPreviousPlaylistElement() 140 const previous = this.playlistTracker.getPreviousPlaylistElement()
139 if (!previous) { 141 if (!previous) {
140 console.log('Previous element not found in playlist.') 142 logger.info('Previous element not found in playlist.')
141 return 143 return
142 } 144 }
143 145
@@ -343,5 +345,5 @@ PeerTubeEmbed.main()
343 .catch(err => { 345 .catch(err => {
344 (window as any).displayIncompatibleBrowser() 346 (window as any).displayIncompatibleBrowser()
345 347
346 console.error('Cannot init embed.', err) 348 logger.error('Cannot init embed.', err)
347 }) 349 })
diff --git a/client/src/standalone/videos/shared/player-html.ts b/client/src/standalone/videos/shared/player-html.ts
index eb6324ac7..61231d2cb 100644
--- a/client/src/standalone/videos/shared/player-html.ts
+++ b/client/src/standalone/videos/shared/player-html.ts
@@ -1,5 +1,6 @@
1import { peertubeTranslate } from '../../../../../shared/core-utils/i18n' 1import { peertubeTranslate } from '../../../../../shared/core-utils/i18n'
2import { VideoDetails } from '../../../../../shared/models' 2import { VideoDetails } from '../../../../../shared/models'
3import { logger } from '../../../root-helpers'
3import { Translations } from './translations' 4import { Translations } from './translations'
4 5
5export class PlayerHTML { 6export class PlayerHTML {
@@ -29,7 +30,7 @@ export class PlayerHTML {
29 } 30 }
30 31
31 displayError (text: string, translations: Translations) { 32 displayError (text: string, translations: Translations) {
32 console.error(text) 33 logger.error(text)
33 34
34 // Remove video element 35 // Remove video element
35 if (this.playerElement) { 36 if (this.playerElement) {
diff --git a/client/src/standalone/videos/shared/player-manager-options.ts b/client/src/standalone/videos/shared/player-manager-options.ts
index f3bd46a69..2eeb5ecac 100644
--- a/client/src/standalone/videos/shared/player-manager-options.ts
+++ b/client/src/standalone/videos/shared/player-manager-options.ts
@@ -14,6 +14,7 @@ import {
14 getParamString, 14 getParamString,
15 getParamToggle, 15 getParamToggle,
16 isP2PEnabled, 16 isP2PEnabled,
17 logger,
17 peertubeLocalStorage, 18 peertubeLocalStorage,
18 UserLocalStorageKeys 19 UserLocalStorageKeys
19} from '../../../root-helpers' 20} from '../../../root-helpers'
@@ -137,7 +138,7 @@ export class PlayerManagerOptions {
137 else this.mode = 'webtorrent' 138 else this.mode = 'webtorrent'
138 } 139 }
139 } catch (err) { 140 } catch (err) {
140 console.error('Cannot get params from URL.', err) 141 logger.error('Cannot get params from URL.', err)
141 } 142 }
142 } 143 }
143 144
diff --git a/client/src/standalone/videos/shared/playlist-fetcher.ts b/client/src/standalone/videos/shared/playlist-fetcher.ts
index a7e72c177..713d82e3a 100644
--- a/client/src/standalone/videos/shared/playlist-fetcher.ts
+++ b/client/src/standalone/videos/shared/playlist-fetcher.ts
@@ -1,4 +1,5 @@
1import { HttpStatusCode, ResultList, VideoPlaylistElement } from '../../../../../shared/models' 1import { HttpStatusCode, ResultList, VideoPlaylistElement } from '../../../../../shared/models'
2import { logger } from '../../../root-helpers'
2import { AuthHTTP } from './auth-http' 3import { AuthHTTP } from './auth-http'
3 4
4export class PlaylistFetcher { 5export class PlaylistFetcher {
@@ -18,7 +19,7 @@ export class PlaylistFetcher {
18 playlistResponse = await playlistPromise 19 playlistResponse = await playlistPromise
19 isResponseOk = playlistResponse.status === HttpStatusCode.OK_200 20 isResponseOk = playlistResponse.status === HttpStatusCode.OK_200
20 } catch (err) { 21 } catch (err) {
21 console.error(err) 22 logger.error(err)
22 isResponseOk = false 23 isResponseOk = false
23 } 24 }
24 25
@@ -49,7 +50,7 @@ export class PlaylistFetcher {
49 } 50 }
50 51
51 if (i === 10) { 52 if (i === 10) {
52 console.error('Cannot fetch all playlists elements, there are too many!') 53 logger.error('Cannot fetch all playlists elements, there are too many!')
53 } 54 }
54 55
55 return elements 56 return elements
diff --git a/client/src/standalone/videos/shared/playlist-tracker.ts b/client/src/standalone/videos/shared/playlist-tracker.ts
index 75d10b4e2..9ea4be83f 100644
--- a/client/src/standalone/videos/shared/playlist-tracker.ts
+++ b/client/src/standalone/videos/shared/playlist-tracker.ts
@@ -1,4 +1,5 @@
1import { VideoPlaylist, VideoPlaylistElement } from '../../../../../shared/models' 1import { VideoPlaylist, VideoPlaylistElement } from '../../../../../shared/models'
2import { logger } from '../../../root-helpers'
2 3
3export class PlaylistTracker { 4export class PlaylistTracker {
4 private currentPlaylistElement: VideoPlaylistElement 5 private currentPlaylistElement: VideoPlaylistElement
@@ -68,7 +69,7 @@ export class PlaylistTracker {
68 setPosition (position: number) { 69 setPosition (position: number) {
69 this.currentPlaylistElement = this.playlistElements.find(e => e.position === position) 70 this.currentPlaylistElement = this.playlistElements.find(e => e.position === position)
70 if (!this.currentPlaylistElement || !this.currentPlaylistElement.video) { 71 if (!this.currentPlaylistElement || !this.currentPlaylistElement.video) {
71 console.error('Current playlist element is not valid.', this.currentPlaylistElement) 72 logger.error('Current playlist element is not valid.', this.currentPlaylistElement)
72 this.currentPlaylistElement = this.getNextPlaylistElement() 73 this.currentPlaylistElement = this.getNextPlaylistElement()
73 } 74 }
74 75
diff --git a/client/src/standalone/videos/shared/video-fetcher.ts b/client/src/standalone/videos/shared/video-fetcher.ts
index e78d38536..b42d622f9 100644
--- a/client/src/standalone/videos/shared/video-fetcher.ts
+++ b/client/src/standalone/videos/shared/video-fetcher.ts
@@ -1,4 +1,5 @@
1import { HttpStatusCode, LiveVideo, VideoDetails } from '../../../../../shared/models' 1import { HttpStatusCode, LiveVideo, VideoDetails } from '../../../../../shared/models'
2import { logger } from '../../../root-helpers'
2import { AuthHTTP } from './auth-http' 3import { AuthHTTP } from './auth-http'
3 4
4export class VideoFetcher { 5export class VideoFetcher {
@@ -17,7 +18,7 @@ export class VideoFetcher {
17 videoResponse = await videoPromise 18 videoResponse = await videoPromise
18 isResponseOk = videoResponse.status === HttpStatusCode.OK_200 19 isResponseOk = videoResponse.status === HttpStatusCode.OK_200
19 } catch (err) { 20 } catch (err) {
20 console.error(err) 21 logger.error(err)
21 22
22 isResponseOk = false 23 isResponseOk = false
23 } 24 }
diff --git a/client/src/standalone/videos/test-embed.ts b/client/src/standalone/videos/test-embed.ts
index 18c338a2d..ab5262902 100644
--- a/client/src/standalone/videos/test-embed.ts
+++ b/client/src/standalone/videos/test-embed.ts
@@ -1,6 +1,7 @@
1import './test-embed.scss' 1import './test-embed.scss'
2import { PeerTubeResolution, PlayerEventType } from '../player/definitions' 2import { PeerTubeResolution, PlayerEventType } from '../player/definitions'
3import { PeerTubePlayer } from '../player/player' 3import { PeerTubePlayer } from '../player/player'
4import { logger } from '../../root-helpers'
4 5
5window.addEventListener('load', async () => { 6window.addEventListener('load', async () => {
6 const urlParts = window.location.href.split('/') 7 const urlParts = window.location.href.split('/')
@@ -20,14 +21,14 @@ window.addEventListener('load', async () => {
20 const mainElement = document.querySelector('#host') 21 const mainElement = document.querySelector('#host')
21 mainElement.appendChild(iframe) 22 mainElement.appendChild(iframe)
22 23
23 console.log('Document finished loading.') 24 logger.info('Document finished loading.')
24 const player = new PeerTubePlayer(document.querySelector('iframe')) 25 const player = new PeerTubePlayer(document.querySelector('iframe'))
25 26
26 window['player'] = player 27 window['player'] = player
27 28
28 console.log('Awaiting player ready...') 29 logger.info('Awaiting player ready...')
29 await player.ready 30 await player.ready
30 console.log('Player is ready.') 31 logger.info('Player is ready.')
31 32
32 const monitoredEvents = [ 33 const monitoredEvents = [
33 'pause', 34 'pause',
@@ -37,8 +38,8 @@ window.addEventListener('load', async () => {
37 ] 38 ]
38 39
39 monitoredEvents.forEach(e => { 40 monitoredEvents.forEach(e => {
40 player.addEventListener(e as PlayerEventType, (param) => console.log(`PLAYER: event '${e}' received`, param)) 41 player.addEventListener(e as PlayerEventType, (param) => logger.info(`PLAYER: event '${e}' received`, { param }))
41 console.log(`PLAYER: now listening for event '${e}'`) 42 logger.info(`PLAYER: now listening for event '${e}'`)
42 43
43 player.getCurrentPosition() 44 player.getCurrentPosition()
44 .then(position => { 45 .then(position => {
diff --git a/client/src/standalone/videos/tsconfig.json b/client/src/standalone/videos/tsconfig.json
new file mode 100644
index 000000000..e0cab7ca3
--- /dev/null
+++ b/client/src/standalone/videos/tsconfig.json
@@ -0,0 +1,7 @@
1{
2 "extends": "../../../tsconfig.json",
3 "include": [
4 "src/standalone/videos/embed.ts",
5 "src/standalone/videos/test-embed.ts"
6 ]
7}
diff --git a/client/webpack/webpack.video-embed.js b/client/webpack/webpack.video-embed.js
index f5c75dd47..547e8aa63 100644
--- a/client/webpack/webpack.video-embed.js
+++ b/client/webpack/webpack.video-embed.js
@@ -69,7 +69,7 @@ module.exports = function () {
69 { 69 {
70 loader: 'ts-loader', 70 loader: 'ts-loader',
71 options: { 71 options: {
72 configFile: helpers.root('tsconfig.json') 72 configFile: helpers.root('src/standalone/videos/tsconfig.json')
73 } 73 }
74 } 74 }
75 ] 75 ]
diff --git a/config/default.yaml b/config/default.yaml
index 2c1b9c64f..7e07165b9 100644
--- a/config/default.yaml
+++ b/config/default.yaml
@@ -27,6 +27,10 @@ rates_limit:
27 # 3 attempts in 5 min 27 # 3 attempts in 5 min
28 window: 5 minutes 28 window: 5 minutes
29 max: 3 29 max: 3
30 receive_client_log:
31 # 10 attempts in 10 min
32 window: 10 minutes
33 max: 10
30 34
31# Proxies to trust to get real client IP 35# Proxies to trust to get real client IP
32# If you run PeerTube just behind a local proxy (nginx), keep 'loopback' 36# If you run PeerTube just behind a local proxy (nginx), keep 'loopback'
@@ -168,15 +172,22 @@ object_storage:
168 172
169log: 173log:
170 level: 'info' # 'debug' | 'info' | 'warn' | 'error' 174 level: 'info' # 'debug' | 'info' | 'warn' | 'error'
175
171 rotation: 176 rotation:
172 enabled : true # Enabled by default, if disabled make sure that 'storage.logs' is pointing to a folder handled by logrotate 177 enabled : true # Enabled by default, if disabled make sure that 'storage.logs' is pointing to a folder handled by logrotate
173 max_file_size: 12MB 178 max_file_size: 12MB
174 max_files: 20 179 max_files: 20
180
175 anonymize_ip: false 181 anonymize_ip: false
182
176 log_ping_requests: true 183 log_ping_requests: true
177 log_tracker_unknown_infohash: true 184 log_tracker_unknown_infohash: true
185
178 prettify_sql: false 186 prettify_sql: false
179 187
188 # Accept warn/error logs coming from the client
189 accept_client_log: true
190
180# Highly experimental support of Open Telemetry 191# Highly experimental support of Open Telemetry
181open_telemetry: 192open_telemetry:
182 metrics: 193 metrics:
diff --git a/config/production.yaml.example b/config/production.yaml.example
index 3e4035eaa..042f5a641 100644
--- a/config/production.yaml.example
+++ b/config/production.yaml.example
@@ -25,6 +25,10 @@ rates_limit:
25 # 3 attempts in 5 min 25 # 3 attempts in 5 min
26 window: 5 minutes 26 window: 5 minutes
27 max: 3 27 max: 3
28 receive_client_log:
29 # 10 attempts in 10 min
30 window: 10 minutes
31 max: 10
28 32
29# Proxies to trust to get real client IP 33# Proxies to trust to get real client IP
30# If you run PeerTube just behind a local proxy (nginx), keep 'loopback' 34# If you run PeerTube just behind a local proxy (nginx), keep 'loopback'
@@ -166,15 +170,22 @@ object_storage:
166 170
167log: 171log:
168 level: 'info' # 'debug' | 'info' | 'warn' | 'error' 172 level: 'info' # 'debug' | 'info' | 'warn' | 'error'
173
169 rotation: 174 rotation:
170 enabled : true # Enabled by default, if disabled make sure that 'storage.logs' is pointing to a folder handled by logrotate 175 enabled : true # Enabled by default, if disabled make sure that 'storage.logs' is pointing to a folder handled by logrotate
171 max_file_size: 12MB 176 max_file_size: 12MB
172 max_files: 20 177 max_files: 20
178
173 anonymize_ip: false 179 anonymize_ip: false
180
174 log_ping_requests: true 181 log_ping_requests: true
175 log_tracker_unknown_infohash: true 182 log_tracker_unknown_infohash: true
183
176 prettify_sql: false 184 prettify_sql: false
177 185
186 # Accept warn/error logs coming from the client
187 accept_client_log: true
188
178# Highly experimental support of Open Telemetry 189# Highly experimental support of Open Telemetry
179open_telemetry: 190open_telemetry:
180 metrics: 191 metrics:
diff --git a/server/controllers/api/server/logs.ts b/server/controllers/api/server/logs.ts
index 8aa4b7190..ed0aa6e8e 100644
--- a/server/controllers/api/server/logs.ts
+++ b/server/controllers/api/server/logs.ts
@@ -3,15 +3,29 @@ import { readdir, readFile } from 'fs-extra'
3import { join } from 'path' 3import { join } from 'path'
4import { isArray } from '@server/helpers/custom-validators/misc' 4import { isArray } from '@server/helpers/custom-validators/misc'
5import { logger, mtimeSortFilesDesc } from '@server/helpers/logger' 5import { logger, mtimeSortFilesDesc } from '@server/helpers/logger'
6import { LogLevel } from '../../../../shared/models/server/log-level.type' 6import { pick } from '@shared/core-utils'
7import { ClientLogCreate, HttpStatusCode } from '@shared/models'
8import { ServerLogLevel } from '../../../../shared/models/server/server-log-level.type'
7import { UserRight } from '../../../../shared/models/users' 9import { UserRight } from '../../../../shared/models/users'
8import { CONFIG } from '../../../initializers/config' 10import { CONFIG } from '../../../initializers/config'
9import { AUDIT_LOG_FILENAME, LOG_FILENAME, MAX_LOGS_OUTPUT_CHARACTERS } from '../../../initializers/constants' 11import { AUDIT_LOG_FILENAME, LOG_FILENAME, MAX_LOGS_OUTPUT_CHARACTERS } from '../../../initializers/constants'
10import { asyncMiddleware, authenticate, ensureUserHasRight } from '../../../middlewares' 12import { asyncMiddleware, authenticate, buildRateLimiter, ensureUserHasRight, optionalAuthenticate } from '../../../middlewares'
11import { getAuditLogsValidator, getLogsValidator } from '../../../middlewares/validators/logs' 13import { createClientLogValidator, getAuditLogsValidator, getLogsValidator } from '../../../middlewares/validators/logs'
14
15const createClientLogRateLimiter = buildRateLimiter({
16 windowMs: CONFIG.RATES_LIMIT.RECEIVE_CLIENT_LOG.WINDOW_MS,
17 max: CONFIG.RATES_LIMIT.RECEIVE_CLIENT_LOG.MAX
18})
12 19
13const logsRouter = express.Router() 20const logsRouter = express.Router()
14 21
22logsRouter.post('/logs/client',
23 createClientLogRateLimiter,
24 optionalAuthenticate,
25 createClientLogValidator,
26 createClientLog
27)
28
15logsRouter.get('/logs', 29logsRouter.get('/logs',
16 authenticate, 30 authenticate,
17 ensureUserHasRight(UserRight.MANAGE_LOGS), 31 ensureUserHasRight(UserRight.MANAGE_LOGS),
@@ -34,6 +48,21 @@ export {
34 48
35// --------------------------------------------------------------------------- 49// ---------------------------------------------------------------------------
36 50
51function createClientLog (req: express.Request, res: express.Response) {
52 const logInfo = req.body as ClientLogCreate
53
54 const meta = {
55 tags: [ 'client' ],
56 username: res.locals.oauth?.token?.User?.username,
57
58 ...pick(logInfo, [ 'userAgent', 'stackTrace', 'meta', 'url' ])
59 }
60
61 logger.log(logInfo.level, `Client log: ${logInfo.message}`, meta)
62
63 return res.sendStatus(HttpStatusCode.NO_CONTENT_204)
64}
65
37const auditLogNameFilter = generateLogNameFilter(AUDIT_LOG_FILENAME) 66const auditLogNameFilter = generateLogNameFilter(AUDIT_LOG_FILENAME)
38async function getAuditLogs (req: express.Request, res: express.Response) { 67async function getAuditLogs (req: express.Request, res: express.Response) {
39 const output = await generateOutput({ 68 const output = await generateOutput({
@@ -63,7 +92,7 @@ async function generateOutput (options: {
63 startDateQuery: string 92 startDateQuery: string
64 endDateQuery?: string 93 endDateQuery?: string
65 94
66 level: LogLevel 95 level: ServerLogLevel
67 nameFilter: RegExp 96 nameFilter: RegExp
68 tagsOneOf?: string[] 97 tagsOneOf?: string[]
69}) { 98}) {
@@ -104,7 +133,7 @@ async function getOutputFromFile (options: {
104 path: string 133 path: string
105 startDate: Date 134 startDate: Date
106 endDate: Date 135 endDate: Date
107 level: LogLevel 136 level: ServerLogLevel
108 currentSize: number 137 currentSize: number
109 tagsOneOf: Set<string> 138 tagsOneOf: Set<string>
110}) { 139}) {
@@ -116,7 +145,7 @@ async function getOutputFromFile (options: {
116 145
117 let logTime: number 146 let logTime: number
118 147
119 const logsLevel: { [ id in LogLevel ]: number } = { 148 const logsLevel: { [ id in ServerLogLevel ]: number } = {
120 audit: -1, 149 audit: -1,
121 debug: 0, 150 debug: 0,
122 info: 1, 151 info: 1,
diff --git a/server/helpers/custom-validators/logs.ts b/server/helpers/custom-validators/logs.ts
index 0f266ed3b..41d45cbb2 100644
--- a/server/helpers/custom-validators/logs.ts
+++ b/server/helpers/custom-validators/logs.ts
@@ -1,14 +1,42 @@
1import validator from 'validator'
2import { CONSTRAINTS_FIELDS } from '@server/initializers/constants'
3import { ClientLogLevel, ServerLogLevel } from '@shared/models'
1import { exists } from './misc' 4import { exists } from './misc'
2import { LogLevel } from '../../../shared/models/server/log-level.type'
3 5
4const logLevels: LogLevel[] = [ 'debug', 'info', 'warn', 'error' ] 6const serverLogLevels: Set<ServerLogLevel> = new Set([ 'debug', 'info', 'warn', 'error' ])
7const clientLogLevels: Set<ClientLogLevel> = new Set([ 'warn', 'error' ])
5 8
6function isValidLogLevel (value: any) { 9function isValidLogLevel (value: any) {
7 return exists(value) && logLevels.includes(value) 10 return exists(value) && serverLogLevels.has(value)
11}
12
13function isValidClientLogMessage (value: any) {
14 return typeof value === 'string' && validator.isLength(value, CONSTRAINTS_FIELDS.LOGS.CLIENT_MESSAGE)
15}
16
17function isValidClientLogLevel (value: any) {
18 return exists(value) && clientLogLevels.has(value)
19}
20
21function isValidClientLogStackTrace (value: any) {
22 return typeof value === 'string' && validator.isLength(value, CONSTRAINTS_FIELDS.LOGS.CLIENT_STACK_TRACE)
23}
24
25function isValidClientLogMeta (value: any) {
26 return typeof value === 'string' && validator.isLength(value, CONSTRAINTS_FIELDS.LOGS.CLIENT_META)
27}
28
29function isValidClientLogUserAgent (value: any) {
30 return typeof value === 'string' && validator.isLength(value, CONSTRAINTS_FIELDS.LOGS.CLIENT_USER_AGENT)
8} 31}
9 32
10// --------------------------------------------------------------------------- 33// ---------------------------------------------------------------------------
11 34
12export { 35export {
13 isValidLogLevel 36 isValidLogLevel,
37 isValidClientLogMessage,
38 isValidClientLogStackTrace,
39 isValidClientLogMeta,
40 isValidClientLogLevel,
41 isValidClientLogUserAgent
14} 42}
diff --git a/server/initializers/config.ts b/server/initializers/config.ts
index 0943ffe2d..ba0f756ef 100644
--- a/server/initializers/config.ts
+++ b/server/initializers/config.ts
@@ -149,6 +149,10 @@ const CONFIG = {
149 WINDOW_MS: parseDurationToMs(config.get<string>('rates_limit.login.window')), 149 WINDOW_MS: parseDurationToMs(config.get<string>('rates_limit.login.window')),
150 MAX: config.get<number>('rates_limit.login.max') 150 MAX: config.get<number>('rates_limit.login.max')
151 }, 151 },
152 RECEIVE_CLIENT_LOG: {
153 WINDOW_MS: parseDurationToMs(config.get<string>('rates_limit.receive_client_log.window')),
154 MAX: config.get<number>('rates_limit.receive_client_log.max')
155 },
152 ASK_SEND_EMAIL: { 156 ASK_SEND_EMAIL: {
153 WINDOW_MS: parseDurationToMs(config.get<string>('rates_limit.ask_send_email.window')), 157 WINDOW_MS: parseDurationToMs(config.get<string>('rates_limit.ask_send_email.window')),
154 MAX: config.get<number>('rates_limit.ask_send_email.max') 158 MAX: config.get<number>('rates_limit.ask_send_email.max')
@@ -165,7 +169,8 @@ const CONFIG = {
165 ANONYMIZE_IP: config.get<boolean>('log.anonymize_ip'), 169 ANONYMIZE_IP: config.get<boolean>('log.anonymize_ip'),
166 LOG_PING_REQUESTS: config.get<boolean>('log.log_ping_requests'), 170 LOG_PING_REQUESTS: config.get<boolean>('log.log_ping_requests'),
167 LOG_TRACKER_UNKNOWN_INFOHASH: config.get<boolean>('log.log_tracker_unknown_infohash'), 171 LOG_TRACKER_UNKNOWN_INFOHASH: config.get<boolean>('log.log_tracker_unknown_infohash'),
168 PRETTIFY_SQL: config.get<boolean>('log.prettify_sql') 172 PRETTIFY_SQL: config.get<boolean>('log.prettify_sql'),
173 ACCEPT_CLIENT_LOG: config.get<boolean>('log.accept_client_log')
169 }, 174 },
170 OPEN_TELEMETRY: { 175 OPEN_TELEMETRY: {
171 METRICS: { 176 METRICS: {
diff --git a/server/initializers/constants.ts b/server/initializers/constants.ts
index 009f878fc..8cb4d5f4a 100644
--- a/server/initializers/constants.ts
+++ b/server/initializers/constants.ts
@@ -365,6 +365,12 @@ const CONSTRAINTS_FIELDS = {
365 VIDEO_STUDIO: { 365 VIDEO_STUDIO: {
366 TASKS: { min: 1, max: 10 }, // Number of tasks 366 TASKS: { min: 1, max: 10 }, // Number of tasks
367 CUT_TIME: { min: 0 } // Value 367 CUT_TIME: { min: 0 } // Value
368 },
369 LOGS: {
370 CLIENT_MESSAGE: { min: 1, max: 1000 }, // Length
371 CLIENT_STACK_TRACE: { min: 1, max: 5000 }, // Length
372 CLIENT_META: { min: 1, max: 5000 }, // Length
373 CLIENT_USER_AGENT: { min: 1, max: 200 } // Length
368 } 374 }
369} 375}
370 376
diff --git a/server/middlewares/validators/logs.ts b/server/middlewares/validators/logs.ts
index 901d8ca64..324ba6915 100644
--- a/server/middlewares/validators/logs.ts
+++ b/server/middlewares/validators/logs.ts
@@ -1,11 +1,56 @@
1import express from 'express' 1import express from 'express'
2import { query } from 'express-validator' 2import { body, query } from 'express-validator'
3import { isUrlValid } from '@server/helpers/custom-validators/activitypub/misc'
3import { isStringArray } from '@server/helpers/custom-validators/search' 4import { isStringArray } from '@server/helpers/custom-validators/search'
4import { isValidLogLevel } from '../../helpers/custom-validators/logs' 5import { CONFIG } from '@server/initializers/config'
6import { HttpStatusCode } from '@shared/models'
7import {
8 isValidClientLogLevel,
9 isValidClientLogMessage,
10 isValidClientLogMeta,
11 isValidClientLogStackTrace,
12 isValidClientLogUserAgent,
13 isValidLogLevel
14} from '../../helpers/custom-validators/logs'
5import { isDateValid, toArray } from '../../helpers/custom-validators/misc' 15import { isDateValid, toArray } from '../../helpers/custom-validators/misc'
6import { logger } from '../../helpers/logger' 16import { logger } from '../../helpers/logger'
7import { areValidationErrors } from './shared' 17import { areValidationErrors } from './shared'
8 18
19const createClientLogValidator = [
20 body('message')
21 .custom(isValidClientLogMessage).withMessage('Should have a valid log message'),
22
23 body('url')
24 .custom(isUrlValid).withMessage('Should have a valid log url'),
25
26 body('level')
27 .custom(isValidClientLogLevel).withMessage('Should have a valid log message'),
28
29 body('stackTrace')
30 .optional()
31 .custom(isValidClientLogStackTrace).withMessage('Should have a valid log stack trace'),
32
33 body('meta')
34 .optional()
35 .custom(isValidClientLogMeta).withMessage('Should have a valid log meta'),
36
37 body('userAgent')
38 .optional()
39 .custom(isValidClientLogUserAgent).withMessage('Should have a valid log user agent'),
40
41 (req: express.Request, res: express.Response, next: express.NextFunction) => {
42 logger.debug('Checking createClientLogValidator parameters.', { parameters: req.query })
43
44 if (CONFIG.LOG.ACCEPT_CLIENT_LOG !== true) {
45 return res.sendStatus(HttpStatusCode.FORBIDDEN_403)
46 }
47
48 if (areValidationErrors(req, res)) return
49
50 return next()
51 }
52]
53
9const getLogsValidator = [ 54const getLogsValidator = [
10 query('startDate') 55 query('startDate')
11 .custom(isDateValid).withMessage('Should have a start date that conforms to ISO 8601'), 56 .custom(isDateValid).withMessage('Should have a start date that conforms to ISO 8601'),
@@ -49,5 +94,6 @@ const getAuditLogsValidator = [
49 94
50export { 95export {
51 getLogsValidator, 96 getLogsValidator,
52 getAuditLogsValidator 97 getAuditLogsValidator,
98 createClientLogValidator
53} 99}
diff --git a/server/tests/api/check-params/logs.ts b/server/tests/api/check-params/logs.ts
index 970671c15..fa67408b7 100644
--- a/server/tests/api/check-params/logs.ts
+++ b/server/tests/api/check-params/logs.ts
@@ -1,8 +1,9 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import 'mocha' 3import 'mocha'
4import { cleanupTests, createSingleServer, makeGetRequest, PeerTubeServer, setAccessTokensToServers } from '@shared/server-commands' 4import { expect } from 'chai'
5import { HttpStatusCode } from '@shared/models' 5import { HttpStatusCode } from '@shared/models'
6import { cleanupTests, createSingleServer, makeGetRequest, PeerTubeServer, setAccessTokensToServers } from '@shared/server-commands'
6 7
7describe('Test logs API validators', function () { 8describe('Test logs API validators', function () {
8 const path = '/api/v1/server/logs' 9 const path = '/api/v1/server/logs'
@@ -95,6 +96,62 @@ describe('Test logs API validators', function () {
95 }) 96 })
96 }) 97 })
97 98
99 describe('When creating client logs', function () {
100 const base = {
101 level: 'warn' as 'warn',
102 message: 'my super message',
103 url: 'https://example.com/toto'
104 }
105 const expectedStatus = HttpStatusCode.BAD_REQUEST_400
106
107 it('Should fail with an invalid level', async function () {
108 await server.logs.createLogClient({ payload: { ...base, level: '' as any }, expectedStatus })
109 await server.logs.createLogClient({ payload: { ...base, level: undefined }, expectedStatus })
110 await server.logs.createLogClient({ payload: { ...base, level: 'toto' as any }, expectedStatus })
111 })
112
113 it('Should fail with an invalid message', async function () {
114 await server.logs.createLogClient({ payload: { ...base, message: undefined }, expectedStatus })
115 await server.logs.createLogClient({ payload: { ...base, message: '' }, expectedStatus })
116 await server.logs.createLogClient({ payload: { ...base, message: 'm'.repeat(2500) }, expectedStatus })
117 })
118
119 it('Should fail with an invalid url', async function () {
120 await server.logs.createLogClient({ payload: { ...base, url: undefined }, expectedStatus })
121 await server.logs.createLogClient({ payload: { ...base, url: 'toto' }, expectedStatus })
122 })
123
124 it('Should fail with an invalid stackTrace', async function () {
125 await server.logs.createLogClient({ payload: { ...base, stackTrace: 's'.repeat(10000) }, expectedStatus })
126 })
127
128 it('Should fail with an invalid userAgent', async function () {
129 await server.logs.createLogClient({ payload: { ...base, userAgent: 's'.repeat(500) }, expectedStatus })
130 })
131
132 it('Should fail with an invalid meta', async function () {
133 await server.logs.createLogClient({ payload: { ...base, meta: 's'.repeat(10000) }, expectedStatus })
134 })
135
136 it('Should succeed with the correct params', async function () {
137 await server.logs.createLogClient({ payload: { ...base, stackTrace: 'stackTrace', meta: '{toto}', userAgent: 'userAgent' } })
138 })
139
140 it('Should rate limit log creation', async function () {
141 let fail = false
142
143 for (let i = 0; i < 10; i++) {
144 try {
145 await server.logs.createLogClient({ token: null, payload: base })
146 } catch {
147 fail = true
148 }
149 }
150
151 expect(fail).to.be.true
152 })
153 })
154
98 after(async function () { 155 after(async function () {
99 await cleanupTests([ server ]) 156 await cleanupTests([ server ])
100 }) 157 })
diff --git a/server/tests/api/server/logs.ts b/server/tests/api/server/logs.ts
index 697f10337..ed7555fd7 100644
--- a/server/tests/api/server/logs.ts
+++ b/server/tests/api/server/logs.ts
@@ -2,6 +2,7 @@
2 2
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { HttpStatusCode } from '@shared/models'
5import { 6import {
6 cleanupTests, 7 cleanupTests,
7 createSingleServer, 8 createSingleServer,
@@ -198,6 +199,70 @@ describe('Test logs', function () {
198 }) 199 })
199 }) 200 })
200 201
202 describe('When creating log from the client', function () {
203
204 it('Should create a warn client log', async function () {
205 const now = new Date()
206
207 await server.logs.createLogClient({
208 payload: {
209 level: 'warn',
210 url: 'http://example.com',
211 message: 'my super client message'
212 },
213 token: null
214 })
215
216 const body = await logsCommand.getLogs({ startDate: now })
217 const logsString = JSON.stringify(body)
218
219 expect(logsString.includes('my super client message')).to.be.true
220 })
221
222 it('Should create an error authenticated client log', async function () {
223 const now = new Date()
224
225 await server.logs.createLogClient({
226 payload: {
227 url: 'https://example.com/page1',
228 level: 'error',
229 message: 'my super client message 2',
230 userAgent: 'super user agent',
231 meta: '{hello}',
232 stackTrace: 'super stack trace'
233 }
234 })
235
236 const body = await logsCommand.getLogs({ startDate: now })
237 const logsString = JSON.stringify(body)
238
239 expect(logsString.includes('my super client message 2')).to.be.true
240 expect(logsString.includes('super user agent')).to.be.true
241 expect(logsString.includes('super stack trace')).to.be.true
242 expect(logsString.includes('{hello}')).to.be.true
243 expect(logsString.includes('https://example.com/page1')).to.be.true
244 })
245
246 it('Should refuse to create client logs', async function () {
247 await server.kill()
248
249 await server.run({
250 log: {
251 accept_client_log: false
252 }
253 })
254
255 await server.logs.createLogClient({
256 payload: {
257 level: 'warn',
258 url: 'http://example.com',
259 message: 'my super client message'
260 },
261 expectedStatus: HttpStatusCode.FORBIDDEN_403
262 })
263 })
264 })
265
201 after(async function () { 266 after(async function () {
202 await cleanupTests([ server ]) 267 await cleanupTests([ server ])
203 }) 268 })
diff --git a/shared/models/server/client-log-create.model.ts b/shared/models/server/client-log-create.model.ts
new file mode 100644
index 000000000..c9dc65568
--- /dev/null
+++ b/shared/models/server/client-log-create.model.ts
@@ -0,0 +1,11 @@
1import { ClientLogLevel } from './client-log-level.type'
2
3export interface ClientLogCreate {
4 message: string
5 url: string
6 level: ClientLogLevel
7
8 stackTrace?: string
9 userAgent?: string
10 meta?: string
11}
diff --git a/shared/models/server/client-log-level.type.ts b/shared/models/server/client-log-level.type.ts
new file mode 100644
index 000000000..18dea2751
--- /dev/null
+++ b/shared/models/server/client-log-level.type.ts
@@ -0,0 +1 @@
export type ClientLogLevel = 'warn' | 'error'
diff --git a/shared/models/server/index.ts b/shared/models/server/index.ts
index 0f7646c7a..a9136f3d4 100644
--- a/shared/models/server/index.ts
+++ b/shared/models/server/index.ts
@@ -1,14 +1,16 @@
1export * from './about.model' 1export * from './about.model'
2export * from './broadcast-message-level.type' 2export * from './broadcast-message-level.type'
3export * from './client-log-create.model'
4export * from './client-log-level.type'
3export * from './contact-form.model' 5export * from './contact-form.model'
4export * from './custom-config.model' 6export * from './custom-config.model'
5export * from './debug.model' 7export * from './debug.model'
6export * from './emailer.model' 8export * from './emailer.model'
7export * from './job.model' 9export * from './job.model'
8export * from './log-level.type'
9export * from './peertube-problem-document.model' 10export * from './peertube-problem-document.model'
10export * from './server-config.model' 11export * from './server-config.model'
11export * from './server-debug.model' 12export * from './server-debug.model'
12export * from './server-error-code.enum' 13export * from './server-error-code.enum'
13export * from './server-follow-create.model' 14export * from './server-follow-create.model'
15export * from './server-log-level.type'
14export * from './server-stats.model' 16export * from './server-stats.model'
diff --git a/shared/models/server/log-level.type.ts b/shared/models/server/log-level.type.ts
deleted file mode 100644
index 4afb92d11..000000000
--- a/shared/models/server/log-level.type.ts
+++ /dev/null
@@ -1 +0,0 @@
1export type LogLevel = 'debug' | 'info' | 'warn' | 'error' | 'audit'
diff --git a/shared/models/server/server-log-level.type.ts b/shared/models/server/server-log-level.type.ts
new file mode 100644
index 000000000..f0f31a4ae
--- /dev/null
+++ b/shared/models/server/server-log-level.type.ts
@@ -0,0 +1 @@
export type ServerLogLevel = 'debug' | 'info' | 'warn' | 'error' | 'audit'
diff --git a/shared/server-commands/logs/logs-command.ts b/shared/server-commands/logs/logs-command.ts
index 8f63383ea..1c5de7f59 100644
--- a/shared/server-commands/logs/logs-command.ts
+++ b/shared/server-commands/logs/logs-command.ts
@@ -1,12 +1,25 @@
1import { HttpStatusCode, LogLevel } from '@shared/models' 1import { ClientLogCreate, HttpStatusCode, ServerLogLevel } from '@shared/models'
2import { AbstractCommand, OverrideCommandOptions } from '../shared' 2import { AbstractCommand, OverrideCommandOptions } from '../shared'
3 3
4export class LogsCommand extends AbstractCommand { 4export class LogsCommand extends AbstractCommand {
5 5
6 createLogClient (options: OverrideCommandOptions & { payload: ClientLogCreate }) {
7 const path = '/api/v1/server/logs/client'
8
9 return this.postBodyRequest({
10 ...options,
11
12 path,
13 fields: options.payload,
14 implicitToken: true,
15 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
16 })
17 }
18
6 getLogs (options: OverrideCommandOptions & { 19 getLogs (options: OverrideCommandOptions & {
7 startDate: Date 20 startDate: Date
8 endDate?: Date 21 endDate?: Date
9 level?: LogLevel 22 level?: ServerLogLevel
10 tagsOneOf?: string[] 23 tagsOneOf?: string[]
11 }) { 24 }) {
12 const { startDate, endDate, tagsOneOf, level } = options 25 const { startDate, endDate, tagsOneOf, level } = options
diff --git a/support/doc/api/openapi.yaml b/support/doc/api/openapi.yaml
index 93c545e36..9745f68e9 100644
--- a/support/doc/api/openapi.yaml
+++ b/support/doc/api/openapi.yaml
@@ -330,26 +330,21 @@ x-tagGroups:
330 - name: Search 330 - name: Search
331 tags: 331 tags:
332 - Search 332 - Search
333 - name: Custom pages
334 tags:
335 - Homepage
336 - name: Moderation 333 - name: Moderation
337 tags: 334 tags:
338 - Abuses 335 - Abuses
339 - Video Blocks 336 - Video Blocks
340 - Account Blocks 337 - Account Blocks
341 - Server Blocks 338 - Server Blocks
342 - name: Instance Configuration 339 - name: Instance
343 tags: 340 tags:
344 - Config 341 - Config
342 - Homepage
345 - Instance Follows 343 - Instance Follows
346 - Instance Redundancy 344 - Instance Redundancy
347 - Plugins 345 - Plugins
348 - name: Stats
349 tags:
350 - Stats 346 - Stats
351 - name: Jobs 347 - Logs
352 tags:
353 - Job 348 - Job
354paths: 349paths:
355 '/accounts/{name}': 350 '/accounts/{name}':
@@ -4316,6 +4311,58 @@ paths:
4316 schema: 4311 schema:
4317 $ref: '#/components/schemas/ServerStats' 4312 $ref: '#/components/schemas/ServerStats'
4318 4313
4314 /server/logs/client:
4315 post:
4316 tags:
4317 - Logs
4318 summary: Send client log
4319 operationId: sendClientLog
4320 requestBody:
4321 content:
4322 application/json:
4323 schema:
4324 $ref: '#/components/schemas/SendClientLog'
4325 responses:
4326 '204':
4327 description: successful operation
4328
4329 /server/logs:
4330 get:
4331 tags:
4332 - Logs
4333 summary: Get instance logs
4334 operationId: getInstanceLogs
4335 security:
4336 - OAuth2:
4337 - admin
4338 responses:
4339 '200':
4340 description: successful operation
4341 content:
4342 application/json:
4343 schema:
4344 type: array
4345 items:
4346 type: string
4347
4348 /server/audit-logs:
4349 get:
4350 tags:
4351 - Logs
4352 summary: Get instance audit logs
4353 operationId: getInstanceAuditLogs
4354 security:
4355 - OAuth2:
4356 - admin
4357 responses:
4358 '200':
4359 description: successful operation
4360 content:
4361 application/json:
4362 schema:
4363 type: array
4364 items:
4365 type: string
4319 4366
4320 '/feeds/video-comments.{format}': 4367 '/feeds/video-comments.{format}':
4321 get: 4368 get:
@@ -6526,6 +6573,31 @@ components:
6526 enabled: 6573 enabled:
6527 type: boolean 6574 type: boolean
6528 6575
6576 SendClientLog:
6577 properties:
6578 message:
6579 type: string
6580 url:
6581 type: string
6582 description: URL of the current user page
6583 level:
6584 enum:
6585 - error
6586 - warn
6587 stackTrace:
6588 type: string
6589 description: Stack trace of the error if there is one
6590 userAgent:
6591 type: string
6592 description: User agent of the web browser that sends the message
6593 meta:
6594 type: string
6595 description: Additional information regarding this log
6596 required:
6597 - message
6598 - url
6599 - level
6600
6529 ServerStats: 6601 ServerStats:
6530 properties: 6602 properties:
6531 totalUsers: 6603 totalUsers: