aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--client/src/app/+admin/moderation/video-block-list/video-block-list.component.ts3
-rw-r--r--client/src/app/+admin/plugins/plugin-list-installed/plugin-list-installed.component.ts2
-rw-r--r--client/src/app/+videos/+video-watch/video-watch.component.ts2
-rw-r--r--client/src/app/core/renderer/markdown.service.ts2
-rw-r--r--client/src/app/shared/shared-abuse-list/abuse-list-table.component.ts3
-rw-r--r--client/src/app/shared/shared-custom-markup/peertube-custom-tags/embed-markup.component.ts3
-rw-r--r--client/src/app/shared/shared-forms/timestamp-input.component.ts2
-rw-r--r--client/src/app/shared/shared-main/video/video.model.ts3
-rw-r--r--client/src/app/shared/shared-moderation/report-modals/video-report.component.ts3
-rw-r--r--client/src/app/shared/shared-share-modal/video-share.component.ts9
-rw-r--r--client/src/app/shared/shared-video-miniature/abstract-video-list.ts2
-rw-r--r--client/src/app/shared/shared-video-playlist/video-add-to-playlist.component.ts2
-rw-r--r--client/src/app/shared/shared-video-playlist/video-playlist-element-miniature.component.ts2
-rw-r--r--client/src/app/shared/shared-video-playlist/video-playlist.model.ts3
-rw-r--r--client/src/assets/player/p2p-media-loader/p2p-media-loader-plugin.ts2
-rw-r--r--client/src/assets/player/peertube-player-manager.ts3
-rw-r--r--client/src/assets/player/peertube-plugin.ts12
-rw-r--r--client/src/assets/player/playlist/playlist-menu-item.ts2
-rw-r--r--client/src/assets/player/stats/stats-card.ts3
-rw-r--r--client/src/assets/player/utils.ts148
-rw-r--r--client/src/assets/player/videojs-components/peertube-link-button.ts2
-rw-r--r--client/src/assets/player/webtorrent/webtorrent-plugin.ts11
-rwxr-xr-xscripts/parse-log.ts3
-rw-r--r--server/controllers/api/server/logs.ts3
-rw-r--r--server/controllers/bots.ts16
-rw-r--r--server/controllers/feeds.ts2
-rw-r--r--server/helpers/logger.ts23
-rw-r--r--server/initializers/constants.ts2
-rw-r--r--server/lib/schedulers/plugins-check-scheduler.ts10
-rw-r--r--server/models/video/video-playlist.ts8
-rw-r--r--server/models/video/video.ts7
-rw-r--r--shared/core-utils/common/date.ts47
-rw-r--r--shared/core-utils/common/index.ts3
-rw-r--r--shared/core-utils/common/miscs.ts10
-rw-r--r--shared/core-utils/common/promises.ts12
-rw-r--r--shared/core-utils/common/url.ts130
-rw-r--r--shared/core-utils/index.ts1
-rw-r--r--shared/core-utils/logs/index.ts1
-rw-r--r--shared/core-utils/logs/logs.ts25
-rw-r--r--shared/core-utils/plugins/hooks.ts2
-rw-r--r--shared/extra-utils/server/server.ts2
41 files changed, 279 insertions, 252 deletions
diff --git a/client/src/app/+admin/moderation/video-block-list/video-block-list.component.ts b/client/src/app/+admin/moderation/video-block-list/video-block-list.component.ts
index 3af20ea0a..4fe5ec441 100644
--- a/client/src/app/+admin/moderation/video-block-list/video-block-list.component.ts
+++ b/client/src/app/+admin/moderation/video-block-list/video-block-list.component.ts
@@ -1,6 +1,6 @@
1import { SortMeta } from 'primeng/api' 1import { SortMeta } from 'primeng/api'
2import { switchMap } from 'rxjs/operators' 2import { switchMap } from 'rxjs/operators'
3import { buildVideoEmbedLink, buildVideoOrPlaylistEmbed, decorateVideoLink } from 'src/assets/player/utils' 3import { buildVideoOrPlaylistEmbed } from 'src/assets/player/utils'
4import { environment } from 'src/environments/environment' 4import { environment } from 'src/environments/environment'
5import { Component, OnInit } from '@angular/core' 5import { Component, OnInit } from '@angular/core'
6import { DomSanitizer } from '@angular/platform-browser' 6import { DomSanitizer } from '@angular/platform-browser'
@@ -9,6 +9,7 @@ import { ConfirmService, MarkdownService, Notifier, RestPagination, RestTable, S
9import { AdvancedInputFilter } from '@app/shared/shared-forms' 9import { AdvancedInputFilter } from '@app/shared/shared-forms'
10import { DropdownAction, Video, VideoService } from '@app/shared/shared-main' 10import { DropdownAction, Video, VideoService } from '@app/shared/shared-main'
11import { VideoBlockService } from '@app/shared/shared-moderation' 11import { VideoBlockService } from '@app/shared/shared-moderation'
12import { buildVideoEmbedLink, decorateVideoLink } from '@shared/core-utils'
12import { VideoBlacklist, VideoBlacklistType } from '@shared/models' 13import { VideoBlacklist, VideoBlacklistType } from '@shared/models'
13 14
14@Component({ 15@Component({
diff --git a/client/src/app/+admin/plugins/plugin-list-installed/plugin-list-installed.component.ts b/client/src/app/+admin/plugins/plugin-list-installed/plugin-list-installed.component.ts
index 6af224920..968abcbe5 100644
--- a/client/src/app/+admin/plugins/plugin-list-installed/plugin-list-installed.component.ts
+++ b/client/src/app/+admin/plugins/plugin-list-installed/plugin-list-installed.component.ts
@@ -4,7 +4,7 @@ import { ActivatedRoute, Router } from '@angular/router'
4import { PluginApiService } from '@app/+admin/plugins/shared/plugin-api.service' 4import { PluginApiService } from '@app/+admin/plugins/shared/plugin-api.service'
5import { ComponentPagination, ConfirmService, hasMoreItems, Notifier } from '@app/core' 5import { ComponentPagination, ConfirmService, hasMoreItems, Notifier } from '@app/core'
6import { PluginService } from '@app/core/plugins/plugin.service' 6import { PluginService } from '@app/core/plugins/plugin.service'
7import { compareSemVer } from '@shared/core-utils/miscs/miscs' 7import { compareSemVer } from '@shared/core-utils'
8import { PeerTubePlugin, PluginType } from '@shared/models' 8import { PeerTubePlugin, PluginType } from '@shared/models'
9 9
10@Component({ 10@Component({
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 9212b78be..ccb9c5e71 100644
--- a/client/src/app/+videos/+video-watch/video-watch.component.ts
+++ b/client/src/app/+videos/+video-watch/video-watch.component.ts
@@ -21,6 +21,7 @@ import { isXPercentInViewport, scrollToTop } from '@app/helpers'
21import { Video, VideoCaptionService, VideoDetails, VideoService } from '@app/shared/shared-main' 21import { Video, VideoCaptionService, VideoDetails, VideoService } from '@app/shared/shared-main'
22import { SubscribeButtonComponent } from '@app/shared/shared-user-subscription' 22import { SubscribeButtonComponent } from '@app/shared/shared-user-subscription'
23import { VideoPlaylist, VideoPlaylistService } from '@app/shared/shared-video-playlist' 23import { VideoPlaylist, VideoPlaylistService } from '@app/shared/shared-video-playlist'
24import { timeToInt } from '@shared/core-utils'
24import { 25import {
25 HTMLServerConfig, 26 HTMLServerConfig,
26 HttpStatusCode, 27 HttpStatusCode,
@@ -39,7 +40,6 @@ import {
39 PlayerMode, 40 PlayerMode,
40 videojs 41 videojs
41} from '../../../assets/player/peertube-player-manager' 42} from '../../../assets/player/peertube-player-manager'
42import { timeToInt } from '../../../assets/player/utils'
43import { environment } from '../../../environments/environment' 43import { environment } from '../../../environments/environment'
44import { VideoWatchPlaylistComponent } from './shared' 44import { VideoWatchPlaylistComponent } from './shared'
45 45
diff --git a/client/src/app/core/renderer/markdown.service.ts b/client/src/app/core/renderer/markdown.service.ts
index 01d44864b..36258ca98 100644
--- a/client/src/app/core/renderer/markdown.service.ts
+++ b/client/src/app/core/renderer/markdown.service.ts
@@ -1,6 +1,6 @@
1import * as MarkdownIt from 'markdown-it' 1import * as MarkdownIt from 'markdown-it'
2import { buildVideoLink, decorateVideoLink } from 'src/assets/player/utils'
3import { Injectable } from '@angular/core' 2import { Injectable } from '@angular/core'
3import { buildVideoLink, decorateVideoLink } from '@shared/core-utils'
4import { 4import {
5 COMPLETE_RULES, 5 COMPLETE_RULES,
6 ENHANCED_RULES, 6 ENHANCED_RULES,
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 393108ac9..a7932ebab 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
@@ -1,7 +1,7 @@
1import * as debug from 'debug' 1import * as debug from 'debug'
2import truncate from 'lodash-es/truncate' 2import truncate from 'lodash-es/truncate'
3import { SortMeta } from 'primeng/api' 3import { SortMeta } from 'primeng/api'
4import { buildVideoEmbedLink, buildVideoOrPlaylistEmbed, decorateVideoLink } from 'src/assets/player/utils' 4import { buildVideoOrPlaylistEmbed } from 'src/assets/player/utils'
5import { environment } from 'src/environments/environment' 5import { environment } from 'src/environments/environment'
6import { Component, Input, OnInit, ViewChild } from '@angular/core' 6import { Component, Input, OnInit, ViewChild } from '@angular/core'
7import { DomSanitizer } from '@angular/platform-browser' 7import { DomSanitizer } from '@angular/platform-browser'
@@ -10,6 +10,7 @@ import { ConfirmService, MarkdownService, Notifier, RestPagination, RestTable }
10import { Account, Actor, DropdownAction, Video, VideoService } from '@app/shared/shared-main' 10import { Account, Actor, DropdownAction, Video, VideoService } from '@app/shared/shared-main'
11import { AbuseService, BlocklistService, VideoBlockService } from '@app/shared/shared-moderation' 11import { AbuseService, BlocklistService, VideoBlockService } from '@app/shared/shared-moderation'
12import { VideoCommentService } from '@app/shared/shared-video-comment' 12import { VideoCommentService } from '@app/shared/shared-video-comment'
13import { buildVideoEmbedLink, decorateVideoLink } from '@shared/core-utils'
13import { AbuseState, AdminAbuse } from '@shared/models' 14import { AbuseState, AdminAbuse } from '@shared/models'
14import { AdvancedInputFilter } from '../shared-forms' 15import { AdvancedInputFilter } from '../shared-forms'
15import { AbuseMessageModalComponent } from './abuse-message-modal.component' 16import { AbuseMessageModalComponent } from './abuse-message-modal.component'
diff --git a/client/src/app/shared/shared-custom-markup/peertube-custom-tags/embed-markup.component.ts b/client/src/app/shared/shared-custom-markup/peertube-custom-tags/embed-markup.component.ts
index ba8969d5b..53b70cc47 100644
--- a/client/src/app/shared/shared-custom-markup/peertube-custom-tags/embed-markup.component.ts
+++ b/client/src/app/shared/shared-custom-markup/peertube-custom-tags/embed-markup.component.ts
@@ -1,6 +1,7 @@
1import { buildPlaylistEmbedLink, buildVideoEmbedLink, buildVideoOrPlaylistEmbed } from 'src/assets/player/utils' 1import { buildVideoOrPlaylistEmbed } from 'src/assets/player/utils'
2import { environment } from 'src/environments/environment' 2import { environment } from 'src/environments/environment'
3import { Component, ElementRef, Input, OnInit } from '@angular/core' 3import { Component, ElementRef, Input, OnInit } from '@angular/core'
4import { buildPlaylistEmbedLink, buildVideoEmbedLink } from '@shared/core-utils'
4import { CustomMarkupComponent } from './shared' 5import { CustomMarkupComponent } from './shared'
5 6
6@Component({ 7@Component({
diff --git a/client/src/app/shared/shared-forms/timestamp-input.component.ts b/client/src/app/shared/shared-forms/timestamp-input.component.ts
index 0ffd03d02..3fc705905 100644
--- a/client/src/app/shared/shared-forms/timestamp-input.component.ts
+++ b/client/src/app/shared/shared-forms/timestamp-input.component.ts
@@ -1,6 +1,6 @@
1import { ChangeDetectorRef, Component, EventEmitter, forwardRef, Input, OnInit, Output } from '@angular/core' 1import { ChangeDetectorRef, Component, EventEmitter, forwardRef, Input, OnInit, Output } from '@angular/core'
2import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms' 2import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'
3import { secondsToTime, timeToInt } from '../../../assets/player/utils' 3import { secondsToTime, timeToInt } from '@shared/core-utils'
4 4
5@Component({ 5@Component({
6 selector: 'my-timestamp-input', 6 selector: 'my-timestamp-input',
diff --git a/client/src/app/shared/shared-main/video/video.model.ts b/client/src/app/shared/shared-main/video/video.model.ts
index f0a4a3f37..b7720c8d2 100644
--- a/client/src/app/shared/shared-main/video/video.model.ts
+++ b/client/src/app/shared/shared-main/video/video.model.ts
@@ -2,6 +2,7 @@ import { AuthUser } from '@app/core'
2import { User } from '@app/core/users/user.model' 2import { User } from '@app/core/users/user.model'
3import { durationToString, getAbsoluteAPIUrl, getAbsoluteEmbedUrl } from '@app/helpers' 3import { durationToString, getAbsoluteAPIUrl, getAbsoluteEmbedUrl } from '@app/helpers'
4import { Actor } from '@app/shared/shared-main/account/actor.model' 4import { Actor } from '@app/shared/shared-main/account/actor.model'
5import { buildVideoWatchPath } from '@shared/core-utils'
5import { peertubeTranslate } from '@shared/core-utils/i18n' 6import { peertubeTranslate } from '@shared/core-utils/i18n'
6import { 7import {
7 ActorImage, 8 ActorImage,
@@ -92,7 +93,7 @@ export class Video implements VideoServerModel {
92 pluginData?: any 93 pluginData?: any
93 94
94 static buildWatchUrl (video: Partial<Pick<Video, 'uuid' | 'shortUUID'>>) { 95 static buildWatchUrl (video: Partial<Pick<Video, 'uuid' | 'shortUUID'>>) {
95 return '/w/' + (video.shortUUID || video.uuid) 96 return buildVideoWatchPath({ shortUUID: video.shortUUID || video.uuid })
96 } 97 }
97 98
98 static buildUpdateUrl (video: Pick<Video, 'uuid'>) { 99 static buildUpdateUrl (video: Pick<Video, 'uuid'>) {
diff --git a/client/src/app/shared/shared-moderation/report-modals/video-report.component.ts b/client/src/app/shared/shared-moderation/report-modals/video-report.component.ts
index 41f4fa30d..e509ac88f 100644
--- a/client/src/app/shared/shared-moderation/report-modals/video-report.component.ts
+++ b/client/src/app/shared/shared-moderation/report-modals/video-report.component.ts
@@ -1,5 +1,5 @@
1import { mapValues, pickBy } from 'lodash-es' 1import { mapValues, pickBy } from 'lodash-es'
2import { buildVideoOrPlaylistEmbed, decorateVideoLink } from 'src/assets/player/utils' 2import { buildVideoOrPlaylistEmbed } from 'src/assets/player/utils'
3import { Component, Input, OnInit, ViewChild } from '@angular/core' 3import { Component, Input, OnInit, ViewChild } from '@angular/core'
4import { DomSanitizer, SafeHtml } from '@angular/platform-browser' 4import { DomSanitizer, SafeHtml } from '@angular/platform-browser'
5import { Notifier } from '@app/core' 5import { Notifier } from '@app/core'
@@ -7,6 +7,7 @@ import { ABUSE_REASON_VALIDATOR } from '@app/shared/form-validators/abuse-valida
7import { FormReactive, FormValidatorService } from '@app/shared/shared-forms' 7import { FormReactive, FormValidatorService } from '@app/shared/shared-forms'
8import { NgbModal } from '@ng-bootstrap/ng-bootstrap' 8import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
9import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref' 9import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref'
10import { decorateVideoLink } from '@shared/core-utils'
10import { abusePredefinedReasonsMap } from '@shared/core-utils/abuse' 11import { abusePredefinedReasonsMap } from '@shared/core-utils/abuse'
11import { AbusePredefinedReasonsString } from '@shared/models' 12import { AbusePredefinedReasonsString } from '@shared/models'
12import { Video } from '../../shared-main' 13import { Video } from '../../shared-main'
diff --git a/client/src/app/shared/shared-share-modal/video-share.component.ts b/client/src/app/shared/shared-share-modal/video-share.component.ts
index cdfe50836..341abdc2b 100644
--- a/client/src/app/shared/shared-share-modal/video-share.component.ts
+++ b/client/src/app/shared/shared-share-modal/video-share.component.ts
@@ -2,14 +2,9 @@ import { Component, ElementRef, Input, ViewChild } from '@angular/core'
2import { VideoDetails } from '@app/shared/shared-main' 2import { VideoDetails } from '@app/shared/shared-main'
3import { VideoPlaylist } from '@app/shared/shared-video-playlist' 3import { VideoPlaylist } from '@app/shared/shared-video-playlist'
4import { NgbModal } from '@ng-bootstrap/ng-bootstrap' 4import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
5import { buildPlaylistLink, buildVideoLink, decoratePlaylistLink, decorateVideoLink } from '@shared/core-utils'
5import { VideoCaption } from '@shared/models' 6import { VideoCaption } from '@shared/models'
6import { 7import { buildVideoOrPlaylistEmbed } from '../../../assets/player/utils'
7 buildPlaylistLink,
8 buildVideoLink,
9 buildVideoOrPlaylistEmbed,
10 decoratePlaylistLink,
11 decorateVideoLink
12} from '../../../assets/player/utils'
13 8
14type Customizations = { 9type Customizations = {
15 startAtCheckbox: boolean 10 startAtCheckbox: boolean
diff --git a/client/src/app/shared/shared-video-miniature/abstract-video-list.ts b/client/src/app/shared/shared-video-miniature/abstract-video-list.ts
index 52e72d35b..33061a837 100644
--- a/client/src/app/shared/shared-video-miniature/abstract-video-list.ts
+++ b/client/src/app/shared/shared-video-miniature/abstract-video-list.ts
@@ -24,7 +24,7 @@ import {
24} from '@app/core' 24} from '@app/core'
25import { DisableForReuseHook } from '@app/core/routing/disable-for-reuse-hook' 25import { DisableForReuseHook } from '@app/core/routing/disable-for-reuse-hook'
26import { GlobalIconName } from '@app/shared/shared-icons' 26import { GlobalIconName } from '@app/shared/shared-icons'
27import { isLastMonth, isLastWeek, isThisMonth, isToday, isYesterday } from '@shared/core-utils/miscs/date' 27import { isLastMonth, isLastWeek, isThisMonth, isToday, isYesterday } from '@shared/core-utils'
28import { HTMLServerConfig, UserRight, VideoFilter, VideoSortField } from '@shared/models' 28import { HTMLServerConfig, UserRight, VideoFilter, VideoSortField } from '@shared/models'
29import { NSFWPolicyType } from '@shared/models/videos/nsfw-policy.type' 29import { NSFWPolicyType } from '@shared/models/videos/nsfw-policy.type'
30import { Syndication, Video } from '../shared-main' 30import { Syndication, Video } from '../shared-main'
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 681e5becd..8b019103c 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
@@ -4,6 +4,7 @@ import { debounceTime, filter } from 'rxjs/operators'
4import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnChanges, OnDestroy, OnInit, SimpleChanges } from '@angular/core' 4import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnChanges, OnDestroy, OnInit, SimpleChanges } from '@angular/core'
5import { AuthService, DisableForReuseHook, Notifier } from '@app/core' 5import { AuthService, DisableForReuseHook, Notifier } from '@app/core'
6import { FormReactive, FormValidatorService } from '@app/shared/shared-forms' 6import { FormReactive, FormValidatorService } from '@app/shared/shared-forms'
7import { secondsToTime } from '@shared/core-utils'
7import { 8import {
8 Video, 9 Video,
9 VideoExistInPlaylist, 10 VideoExistInPlaylist,
@@ -12,7 +13,6 @@ import {
12 VideoPlaylistElementUpdate, 13 VideoPlaylistElementUpdate,
13 VideoPlaylistPrivacy 14 VideoPlaylistPrivacy
14} from '@shared/models' 15} from '@shared/models'
15import { secondsToTime } from '../../../assets/player/utils'
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
diff --git a/client/src/app/shared/shared-video-playlist/video-playlist-element-miniature.component.ts b/client/src/app/shared/shared-video-playlist/video-playlist-element-miniature.component.ts
index d99170e4e..2e495ec26 100644
--- a/client/src/app/shared/shared-video-playlist/video-playlist-element-miniature.component.ts
+++ b/client/src/app/shared/shared-video-playlist/video-playlist-element-miniature.component.ts
@@ -2,8 +2,8 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, In
2import { AuthService, Notifier, ServerService } from '@app/core' 2import { AuthService, Notifier, ServerService } from '@app/core'
3import { Video } from '@app/shared/shared-main' 3import { Video } from '@app/shared/shared-main'
4import { NgbDropdown } from '@ng-bootstrap/ng-bootstrap' 4import { NgbDropdown } from '@ng-bootstrap/ng-bootstrap'
5import { secondsToTime } from '@shared/core-utils'
5import { HTMLServerConfig, VideoPlaylistElementType, VideoPlaylistElementUpdate } from '@shared/models' 6import { HTMLServerConfig, VideoPlaylistElementType, VideoPlaylistElementUpdate } from '@shared/models'
6import { secondsToTime } from '../../../assets/player/utils'
7import { VideoPlaylistElement } from './video-playlist-element.model' 7import { VideoPlaylistElement } from './video-playlist-element.model'
8import { VideoPlaylist } from './video-playlist.model' 8import { VideoPlaylist } from './video-playlist.model'
9import { VideoPlaylistService } from './video-playlist.service' 9import { VideoPlaylistService } from './video-playlist.service'
diff --git a/client/src/app/shared/shared-video-playlist/video-playlist.model.ts b/client/src/app/shared/shared-video-playlist/video-playlist.model.ts
index 55013e4c5..fcc2ce705 100644
--- a/client/src/app/shared/shared-video-playlist/video-playlist.model.ts
+++ b/client/src/app/shared/shared-video-playlist/video-playlist.model.ts
@@ -1,5 +1,6 @@
1import { getAbsoluteAPIUrl, getAbsoluteEmbedUrl } from '@app/helpers' 1import { getAbsoluteAPIUrl, getAbsoluteEmbedUrl } from '@app/helpers'
2import { Actor } from '@app/shared/shared-main' 2import { Actor } from '@app/shared/shared-main'
3import { buildPlaylistWatchPath } from '@shared/core-utils'
3import { peertubeTranslate } from '@shared/core-utils/i18n' 4import { peertubeTranslate } from '@shared/core-utils/i18n'
4import { 5import {
5 AccountSummary, 6 AccountSummary,
@@ -44,7 +45,7 @@ export class VideoPlaylist implements ServerVideoPlaylist {
44 videoChannelBy?: string 45 videoChannelBy?: string
45 46
46 static buildWatchUrl (playlist: Pick<VideoPlaylist, 'uuid' | 'shortUUID'>) { 47 static buildWatchUrl (playlist: Pick<VideoPlaylist, 'uuid' | 'shortUUID'>) {
47 return '/w/p/' + (playlist.shortUUID || playlist.uuid) 48 return buildPlaylistWatchPath({ shortUUID: playlist.shortUUID || playlist.uuid })
48 } 49 }
49 50
50 constructor (hash: ServerVideoPlaylist, translations: {}) { 51 constructor (hash: ServerVideoPlaylist, translations: {}) {
diff --git a/client/src/assets/player/p2p-media-loader/p2p-media-loader-plugin.ts b/client/src/assets/player/p2p-media-loader/p2p-media-loader-plugin.ts
index f1bd9f0c4..2eb849d2b 100644
--- a/client/src/assets/player/p2p-media-loader/p2p-media-loader-plugin.ts
+++ b/client/src/assets/player/p2p-media-loader/p2p-media-loader-plugin.ts
@@ -2,8 +2,8 @@ import * as Hlsjs from 'hls.js/dist/hls.light.js'
2import { Events, Segment } from 'p2p-media-loader-core' 2import { Events, Segment } from 'p2p-media-loader-core'
3import { Engine, initHlsJsPlayer, initVideoJsContribHlsJsPlayer } from 'p2p-media-loader-hlsjs' 3import { Engine, initHlsJsPlayer, initVideoJsContribHlsJsPlayer } from 'p2p-media-loader-hlsjs'
4import videojs from 'video.js' 4import videojs from 'video.js'
5import { timeToInt } from '@shared/core-utils'
5import { P2PMediaLoaderPluginOptions, PlayerNetworkInfo } from '../peertube-videojs-typings' 6import { P2PMediaLoaderPluginOptions, PlayerNetworkInfo } from '../peertube-videojs-typings'
6import { timeToInt } from '../utils'
7import { registerConfigPlugin, registerSourceHandler } from './hls-plugin' 7import { registerConfigPlugin, registerSourceHandler } from './hls-plugin'
8 8
9registerConfigPlugin(videojs) 9registerConfigPlugin(videojs)
diff --git a/client/src/assets/player/peertube-player-manager.ts b/client/src/assets/player/peertube-player-manager.ts
index 6f0b804cd..766ad203e 100644
--- a/client/src/assets/player/peertube-player-manager.ts
+++ b/client/src/assets/player/peertube-player-manager.ts
@@ -23,6 +23,7 @@ import './videojs-components/theater-button'
23import './playlist/playlist-plugin' 23import './playlist/playlist-plugin'
24import videojs from 'video.js' 24import videojs from 'video.js'
25import { PluginsManager } from '@root-helpers/plugins-manager' 25import { PluginsManager } from '@root-helpers/plugins-manager'
26import { buildVideoLink, decorateVideoLink } from '@shared/core-utils'
26import { isDefaultLocale } from '@shared/core-utils/i18n' 27import { isDefaultLocale } from '@shared/core-utils/i18n'
27import { VideoFile } from '@shared/models' 28import { VideoFile } from '@shared/models'
28import { copyToClipboard } from '../../root-helpers/utils' 29import { copyToClipboard } from '../../root-helpers/utils'
@@ -40,7 +41,7 @@ import {
40 VideoJSPluginOptions 41 VideoJSPluginOptions
41} from './peertube-videojs-typings' 42} from './peertube-videojs-typings'
42import { TranslationsManager } from './translations-manager' 43import { TranslationsManager } from './translations-manager'
43import { buildVideoLink, buildVideoOrPlaylistEmbed, decorateVideoLink, getRtcConfig, isIOS, isSafari } from './utils' 44import { buildVideoOrPlaylistEmbed, getRtcConfig, isIOS, isSafari } from './utils'
44 45
45// Change 'Playback Rate' to 'Speed' (smaller for our settings menu) 46// Change 'Playback Rate' to 'Speed' (smaller for our settings menu)
46(videojs.getComponent('PlaybackRateMenuButton') as any).prototype.controlText_ = 'Speed' 47(videojs.getComponent('PlaybackRateMenuButton') as any).prototype.controlText_ = 'Speed'
diff --git a/client/src/assets/player/peertube-plugin.ts b/client/src/assets/player/peertube-plugin.ts
index 07c7e33f6..919b7c239 100644
--- a/client/src/assets/player/peertube-plugin.ts
+++ b/client/src/assets/player/peertube-plugin.ts
@@ -1,12 +1,6 @@
1import videojs from 'video.js'
2import './videojs-components/settings-menu-button' 1import './videojs-components/settings-menu-button'
3import { 2import videojs from 'video.js'
4 PeerTubePluginOptions, 3import { timeToInt } from '@shared/core-utils'
5 ResolutionUpdateData,
6 UserWatching,
7 VideoJSCaption
8} from './peertube-videojs-typings'
9import { isMobile, timeToInt } from './utils'
10import { 4import {
11 getStoredLastSubtitle, 5 getStoredLastSubtitle,
12 getStoredMute, 6 getStoredMute,
@@ -16,6 +10,8 @@ import {
16 saveVideoWatchHistory, 10 saveVideoWatchHistory,
17 saveVolumeInStore 11 saveVolumeInStore
18} from './peertube-player-local-storage' 12} from './peertube-player-local-storage'
13import { PeerTubePluginOptions, ResolutionUpdateData, UserWatching, VideoJSCaption } from './peertube-videojs-typings'
14import { isMobile } from './utils'
19 15
20const Plugin = videojs.getPlugin('plugin') 16const Plugin = videojs.getPlugin('plugin')
21 17
diff --git a/client/src/assets/player/playlist/playlist-menu-item.ts b/client/src/assets/player/playlist/playlist-menu-item.ts
index 87a72b6a3..2519a34c7 100644
--- a/client/src/assets/player/playlist/playlist-menu-item.ts
+++ b/client/src/assets/player/playlist/playlist-menu-item.ts
@@ -1,7 +1,7 @@
1import videojs from 'video.js' 1import videojs from 'video.js'
2import { secondsToTime } from '@shared/core-utils'
2import { VideoPlaylistElement } from '@shared/models' 3import { VideoPlaylistElement } from '@shared/models'
3import { PlaylistItemOptions } from '../peertube-videojs-typings' 4import { PlaylistItemOptions } from '../peertube-videojs-typings'
4import { secondsToTime } from '../utils'
5 5
6const Component = videojs.getComponent('Component') 6const Component = videojs.getComponent('Component')
7 7
diff --git a/client/src/assets/player/stats/stats-card.ts b/client/src/assets/player/stats/stats-card.ts
index a93f59506..b271d0526 100644
--- a/client/src/assets/player/stats/stats-card.ts
+++ b/client/src/assets/player/stats/stats-card.ts
@@ -1,6 +1,7 @@
1import videojs from 'video.js' 1import videojs from 'video.js'
2import { secondsToTime } from '@shared/core-utils'
2import { PlayerNetworkInfo as EventPlayerNetworkInfo } from '../peertube-videojs-typings' 3import { PlayerNetworkInfo as EventPlayerNetworkInfo } from '../peertube-videojs-typings'
3import { bytes, secondsToTime } from '../utils' 4import { bytes } from '../utils'
4 5
5interface StatsCardOptions extends videojs.ComponentOptions { 6interface StatsCardOptions extends videojs.ComponentOptions {
6 videoUUID: string 7 videoUUID: string
diff --git a/client/src/assets/player/utils.ts b/client/src/assets/player/utils.ts
index eb9302493..f0a1b1aee 100644
--- a/client/src/assets/player/utils.ts
+++ b/client/src/assets/player/utils.ts
@@ -1,5 +1,5 @@
1import { Video, VideoFile, VideoPlaylist } from '@shared/models'
2import { escapeHTML } from '@shared/core-utils/renderer' 1import { escapeHTML } from '@shared/core-utils/renderer'
2import { VideoFile } from '@shared/models'
3 3
4function toTitleCase (str: string) { 4function toTitleCase (str: string) {
5 return str.charAt(0).toUpperCase() + str.slice(1) 5 return str.charAt(0).toUpperCase() + str.slice(1)
@@ -43,144 +43,9 @@ function isMobile () {
43 return /iPhone|iPad|iPod|Android/i.test(navigator.userAgent) 43 return /iPhone|iPad|iPod|Android/i.test(navigator.userAgent)
44} 44}
45 45
46function buildPlaylistLink (playlist: Pick<VideoPlaylist, 'shortUUID'>, base?: string) {
47 return (base ?? window.location.origin) + '/w/p/' + playlist.shortUUID
48}
49
50function buildVideoLink (video: Pick<Video, 'shortUUID'>, base?: string) {
51 return (base ?? window.location.origin) + '/w/' + video.shortUUID
52}
53
54function buildPlaylistEmbedLink (playlist: Pick<VideoPlaylist, 'uuid'>, base?: string) {
55 return (base ?? window.location.origin) + '/video-playlists/embed/' + playlist.uuid
56}
57
58function buildVideoEmbedLink (video: Pick<Video, 'uuid'>, base?: string) {
59 return (base ?? window.location.origin) + '/videos/embed/' + video.uuid
60}
61
62function decorateVideoLink (options: {
63 url: string
64
65 startTime?: number
66 stopTime?: number
67
68 subtitle?: string
69
70 loop?: boolean
71 autoplay?: boolean
72 muted?: boolean
73
74 // Embed options
75 title?: boolean
76 warningTitle?: boolean
77 controls?: boolean
78 peertubeLink?: boolean
79}) {
80 const { url } = options
81
82 const params = generateParams(window.location.search)
83
84 if (options.startTime !== undefined && options.startTime !== null) {
85 const startTimeInt = Math.floor(options.startTime)
86 params.set('start', secondsToTime(startTimeInt))
87 }
88
89 if (options.stopTime) {
90 const stopTimeInt = Math.floor(options.stopTime)
91 params.set('stop', secondsToTime(stopTimeInt))
92 }
93
94 if (options.subtitle) params.set('subtitle', options.subtitle)
95
96 if (options.loop === true) params.set('loop', '1')
97 if (options.autoplay === true) params.set('autoplay', '1')
98 if (options.muted === true) params.set('muted', '1')
99 if (options.title === false) params.set('title', '0')
100 if (options.warningTitle === false) params.set('warningTitle', '0')
101 if (options.controls === false) params.set('controls', '0')
102 if (options.peertubeLink === false) params.set('peertubeLink', '0')
103
104 return buildUrl(url, params)
105}
106
107function decoratePlaylistLink (options: {
108 url: string
109
110 playlistPosition?: number
111}) {
112 const { url } = options
113
114 const params = generateParams(window.location.search)
115
116 if (options.playlistPosition) params.set('playlistPosition', '' + options.playlistPosition)
117
118 return buildUrl(url, params)
119}
120
121function buildUrl (url: string, params: URLSearchParams) {
122 let hasParams = false
123 params.forEach(() => hasParams = true)
124
125 if (hasParams) return url + '?' + params.toString()
126
127 return url
128}
129
130function generateParams (url: string) {
131 const params = new URLSearchParams(window.location.search)
132 // Unused parameters in embed
133 params.delete('videoId')
134 params.delete('resume')
135
136 return params
137}
138
139function timeToInt (time: number | string) {
140 if (!time) return 0
141 if (typeof time === 'number') return time
142
143 const reg = /^((\d+)[h:])?((\d+)[m:])?((\d+)s?)?$/
144 const matches = time.match(reg)
145
146 if (!matches) return 0
147
148 const hours = parseInt(matches[2] || '0', 10)
149 const minutes = parseInt(matches[4] || '0', 10)
150 const seconds = parseInt(matches[6] || '0', 10)
151
152 return hours * 3600 + minutes * 60 + seconds
153}
154
155function secondsToTime (seconds: number, full = false, symbol?: string) {
156 let time = ''
157
158 if (seconds === 0 && !full) return '0s'
159
160 const hourSymbol = (symbol || 'h')
161 const minuteSymbol = (symbol || 'm')
162 const secondsSymbol = full ? '' : 's'
163
164 const hours = Math.floor(seconds / 3600)
165 if (hours >= 1) time = hours + hourSymbol
166 else if (full) time = '0' + hourSymbol
167
168 seconds %= 3600
169 const minutes = Math.floor(seconds / 60)
170 if (minutes >= 1 && minutes < 10 && full) time += '0' + minutes + minuteSymbol
171 else if (minutes >= 1) time += minutes + minuteSymbol
172 else if (full) time += '00' + minuteSymbol
173
174 seconds %= 60
175 if (seconds >= 1 && seconds < 10 && full) time += '0' + seconds + secondsSymbol
176 else if (seconds >= 1) time += seconds + secondsSymbol
177 else if (full) time += '00'
178
179 return time
180}
181
182function buildVideoOrPlaylistEmbed (embedUrl: string, embedTitle: string) { 46function buildVideoOrPlaylistEmbed (embedUrl: string, embedTitle: string) {
183 const title = escapeHTML(embedTitle) 47 const title = escapeHTML(embedTitle)
48
184 return '<iframe width="560" height="315" ' + 49 return '<iframe width="560" height="315" ' +
185 'sandbox="allow-same-origin allow-scripts allow-popups" ' + 50 'sandbox="allow-same-origin allow-scripts allow-popups" ' +
186 'title="' + title + '" ' + 51 'title="' + title + '" ' +
@@ -229,17 +94,8 @@ function getRtcConfig () {
229export { 94export {
230 getRtcConfig, 95 getRtcConfig,
231 toTitleCase, 96 toTitleCase,
232 timeToInt,
233 secondsToTime,
234 isWebRTCDisabled, 97 isWebRTCDisabled,
235 98
236 buildPlaylistLink,
237 buildVideoLink,
238 decorateVideoLink,
239 decoratePlaylistLink,
240 buildPlaylistEmbedLink,
241 buildVideoEmbedLink,
242
243 buildVideoOrPlaylistEmbed, 99 buildVideoOrPlaylistEmbed,
244 videoFileMaxByResolution, 100 videoFileMaxByResolution,
245 videoFileMinByResolution, 101 videoFileMinByResolution,
diff --git a/client/src/assets/player/videojs-components/peertube-link-button.ts b/client/src/assets/player/videojs-components/peertube-link-button.ts
index f47c165d9..9267b2ed4 100644
--- a/client/src/assets/player/videojs-components/peertube-link-button.ts
+++ b/client/src/assets/player/videojs-components/peertube-link-button.ts
@@ -1,6 +1,6 @@
1import videojs from 'video.js' 1import videojs from 'video.js'
2import { buildVideoLink, decorateVideoLink } from '@shared/core-utils'
2import { PeerTubeLinkButtonOptions } from '../peertube-videojs-typings' 3import { PeerTubeLinkButtonOptions } from '../peertube-videojs-typings'
3import { buildVideoLink, decorateVideoLink } from '../utils'
4 4
5const Button = videojs.getComponent('Button') 5const Button = videojs.getComponent('Button')
6class PeerTubeLinkButton extends Button { 6class PeerTubeLinkButton extends Button {
diff --git a/client/src/assets/player/webtorrent/webtorrent-plugin.ts b/client/src/assets/player/webtorrent/webtorrent-plugin.ts
index b648b29e8..17d369c10 100644
--- a/client/src/assets/player/webtorrent/webtorrent-plugin.ts
+++ b/client/src/assets/player/webtorrent/webtorrent-plugin.ts
@@ -1,9 +1,7 @@
1import videojs from 'video.js' 1import videojs from 'video.js'
2import * as WebTorrent from 'webtorrent' 2import * as WebTorrent from 'webtorrent'
3import { renderVideo } from './video-renderer' 3import { timeToInt } from '@shared/core-utils'
4import { LoadedQualityData, PlayerNetworkInfo, WebtorrentPluginOptions } from '../peertube-videojs-typings' 4import { VideoFile } from '@shared/models'
5import { getRtcConfig, timeToInt, videoFileMaxByResolution, videoFileMinByResolution, isIOS, isSafari } from '../utils'
6import { PeertubeChunkStore } from './peertube-chunk-store'
7import { 5import {
8 getAverageBandwidthInStore, 6 getAverageBandwidthInStore,
9 getStoredMute, 7 getStoredMute,
@@ -11,7 +9,10 @@ import {
11 getStoredVolume, 9 getStoredVolume,
12 saveAverageBandwidth 10 saveAverageBandwidth
13} from '../peertube-player-local-storage' 11} from '../peertube-player-local-storage'
14import { VideoFile } from '@shared/models' 12import { LoadedQualityData, PlayerNetworkInfo, WebtorrentPluginOptions } from '../peertube-videojs-typings'
13import { getRtcConfig, isIOS, videoFileMaxByResolution, videoFileMinByResolution } from '../utils'
14import { PeertubeChunkStore } from './peertube-chunk-store'
15import { renderVideo } from './video-renderer'
15 16
16const CacheChunkStore = require('cache-chunk-store') 17const CacheChunkStore = require('cache-chunk-store')
17 18
diff --git a/scripts/parse-log.ts b/scripts/parse-log.ts
index c16503589..6cd3a1860 100755
--- a/scripts/parse-log.ts
+++ b/scripts/parse-log.ts
@@ -6,9 +6,8 @@ import { createReadStream, readdir } from 'fs-extra'
6import { join } from 'path' 6import { join } from 'path'
7import { createInterface } from 'readline' 7import { createInterface } from 'readline'
8import * as winston from 'winston' 8import * as winston from 'winston'
9import { labelFormatter } from '../server/helpers/logger' 9import { labelFormatter, mtimeSortFilesDesc } from '../server/helpers/logger'
10import { CONFIG } from '../server/initializers/config' 10import { CONFIG } from '../server/initializers/config'
11import { mtimeSortFilesDesc } from '../shared/core-utils/logs/logs'
12import { inspect } from 'util' 11import { inspect } from 'util'
13import { format as sqlFormat } from 'sql-formatter' 12import { format as sqlFormat } from 'sql-formatter'
14 13
diff --git a/server/controllers/api/server/logs.ts b/server/controllers/api/server/logs.ts
index f78607d35..39eceb654 100644
--- a/server/controllers/api/server/logs.ts
+++ b/server/controllers/api/server/logs.ts
@@ -1,8 +1,7 @@
1import * as express from 'express' 1import * as express from 'express'
2import { readdir, readFile } from 'fs-extra' 2import { readdir, readFile } from 'fs-extra'
3import { join } from 'path' 3import { join } from 'path'
4import { logger } from '@server/helpers/logger' 4import { logger, mtimeSortFilesDesc } from '@server/helpers/logger'
5import { mtimeSortFilesDesc } from '../../../../shared/core-utils/logs/logs'
6import { LogLevel } from '../../../../shared/models/server/log-level.type' 5import { LogLevel } from '../../../../shared/models/server/log-level.type'
7import { UserRight } from '../../../../shared/models/users' 6import { UserRight } from '../../../../shared/models/users'
8import { CONFIG } from '../../../initializers/config' 7import { CONFIG } from '../../../initializers/config'
diff --git a/server/controllers/bots.ts b/server/controllers/bots.ts
index 93aa0cf30..de0411608 100644
--- a/server/controllers/bots.ts
+++ b/server/controllers/bots.ts
@@ -1,13 +1,13 @@
1import * as express from 'express' 1import * as express from 'express'
2import { asyncMiddleware } from '../middlewares' 2import { truncate } from 'lodash'
3import { ROUTE_CACHE_LIFETIME, WEBSERVER } from '../initializers/constants'
4import { SitemapStream, streamToPromise } from 'sitemap' 3import { SitemapStream, streamToPromise } from 'sitemap'
4import { buildNSFWFilter } from '../helpers/express-utils'
5import { ROUTE_CACHE_LIFETIME, WEBSERVER } from '../initializers/constants'
6import { asyncMiddleware } from '../middlewares'
7import { cacheRoute } from '../middlewares/cache/cache'
8import { AccountModel } from '../models/account/account'
5import { VideoModel } from '../models/video/video' 9import { VideoModel } from '../models/video/video'
6import { VideoChannelModel } from '../models/video/video-channel' 10import { VideoChannelModel } from '../models/video/video-channel'
7import { AccountModel } from '../models/account/account'
8import { cacheRoute } from '../middlewares/cache/cache'
9import { buildNSFWFilter } from '../helpers/express-utils'
10import { truncate } from 'lodash'
11 11
12const botsRouter = express.Router() 12const botsRouter = express.Router()
13 13
@@ -75,13 +75,13 @@ async function getSitemapLocalVideoUrls () {
75 }) 75 })
76 76
77 return data.map(v => ({ 77 return data.map(v => ({
78 url: WEBSERVER.URL + '/w/' + v.uuid, 78 url: WEBSERVER.URL + v.getWatchStaticPath(),
79 video: [ 79 video: [
80 { 80 {
81 title: v.name, 81 title: v.name,
82 // Sitemap description should be < 2000 characters 82 // Sitemap description should be < 2000 characters
83 description: truncate(v.description || v.name, { length: 2000, omission: '...' }), 83 description: truncate(v.description || v.name, { length: 2000, omission: '...' }),
84 player_loc: WEBSERVER.URL + '/videos/embed/' + v.uuid, 84 player_loc: WEBSERVER.URL + v.getEmbedStaticPath(),
85 thumbnail_loc: WEBSERVER.URL + v.getMiniatureStaticPath() 85 thumbnail_loc: WEBSERVER.URL + v.getMiniatureStaticPath()
86 } 86 }
87 ] 87 ]
diff --git a/server/controllers/feeds.ts b/server/controllers/feeds.ts
index cdc6bfb8b..9fa70a7c8 100644
--- a/server/controllers/feeds.ts
+++ b/server/controllers/feeds.ts
@@ -286,7 +286,7 @@ function addVideosToFeed (feed, videos: VideoModel[]) {
286 feed.addItem({ 286 feed.addItem({
287 title: video.name, 287 title: video.name,
288 id: video.url, 288 id: video.url,
289 link: WEBSERVER.URL + '/w/' + video.uuid, 289 link: WEBSERVER.URL + video.getWatchStaticPath(),
290 description: video.getTruncatedDescription(), 290 description: video.getTruncatedDescription(),
291 content: video.description, 291 content: video.description,
292 author: [ 292 author: [
diff --git a/server/helpers/logger.ts b/server/helpers/logger.ts
index 29e06860d..20c3c3edb 100644
--- a/server/helpers/logger.ts
+++ b/server/helpers/logger.ts
@@ -1,5 +1,5 @@
1// Thanks http://tostring.it/2014/06/23/advanced-logging-with-nodejs/ 1// Thanks http://tostring.it/2014/06/23/advanced-logging-with-nodejs/
2import { mkdirpSync } from 'fs-extra' 2import { mkdirpSync, stat } from 'fs-extra'
3import { omit } from 'lodash' 3import { omit } from 'lodash'
4import * as path from 'path' 4import * as path from 'path'
5import { format as sqlFormat } from 'sql-formatter' 5import { format as sqlFormat } from 'sql-formatter'
@@ -158,6 +158,26 @@ function loggerTagsFactory (...defaultTags: string[]): LoggerTagsFn {
158 } 158 }
159} 159}
160 160
161async function mtimeSortFilesDesc (files: string[], basePath: string) {
162 const promises = []
163 const out: { file: string, mtime: number }[] = []
164
165 for (const file of files) {
166 const p = stat(basePath + '/' + file)
167 .then(stats => {
168 if (stats.isFile()) out.push({ file, mtime: stats.mtime.getTime() })
169 })
170
171 promises.push(p)
172 }
173
174 await Promise.all(promises)
175
176 out.sort((a, b) => b.mtime - a.mtime)
177
178 return out
179}
180
161// --------------------------------------------------------------------------- 181// ---------------------------------------------------------------------------
162 182
163export { 183export {
@@ -168,6 +188,7 @@ export {
168 labelFormatter, 188 labelFormatter,
169 consoleLoggerFormat, 189 consoleLoggerFormat,
170 jsonLoggerFormat, 190 jsonLoggerFormat,
191 mtimeSortFilesDesc,
171 logger, 192 logger,
172 loggerTagsFactory, 193 loggerTagsFactory,
173 bunyanLogger 194 bunyanLogger
diff --git a/server/initializers/constants.ts b/server/initializers/constants.ts
index ee4503b2c..5f121d9a4 100644
--- a/server/initializers/constants.ts
+++ b/server/initializers/constants.ts
@@ -2,7 +2,7 @@ import { CronRepeatOptions, EveryRepeatOptions } from 'bull'
2import { randomBytes } from 'crypto' 2import { randomBytes } from 'crypto'
3import { invert } from 'lodash' 3import { invert } from 'lodash'
4import { join } from 'path' 4import { join } from 'path'
5import { randomInt } from '../../shared/core-utils/miscs/miscs' 5import { randomInt } from '../../shared/core-utils/common/miscs'
6import { 6import {
7 AbuseState, 7 AbuseState,
8 JobType, 8 JobType,
diff --git a/server/lib/schedulers/plugins-check-scheduler.ts b/server/lib/schedulers/plugins-check-scheduler.ts
index 9a1ae3ec5..c95e109b0 100644
--- a/server/lib/schedulers/plugins-check-scheduler.ts
+++ b/server/lib/schedulers/plugins-check-scheduler.ts
@@ -1,12 +1,12 @@
1import { chunk } from 'lodash'
2import { compareSemVer } from '@shared/core-utils'
1import { logger } from '../../helpers/logger' 3import { logger } from '../../helpers/logger'
2import { AbstractScheduler } from './abstract-scheduler'
3import { SCHEDULER_INTERVALS_MS } from '../../initializers/constants'
4import { CONFIG } from '../../initializers/config' 4import { CONFIG } from '../../initializers/config'
5import { SCHEDULER_INTERVALS_MS } from '../../initializers/constants'
5import { PluginModel } from '../../models/server/plugin' 6import { PluginModel } from '../../models/server/plugin'
6import { chunk } from 'lodash'
7import { getLatestPluginsVersion } from '../plugins/plugin-index'
8import { compareSemVer } from '../../../shared/core-utils/miscs/miscs'
9import { Notifier } from '../notifier' 7import { Notifier } from '../notifier'
8import { getLatestPluginsVersion } from '../plugins/plugin-index'
9import { AbstractScheduler } from './abstract-scheduler'
10 10
11export class PluginsCheckScheduler extends AbstractScheduler { 11export class PluginsCheckScheduler extends AbstractScheduler {
12 12
diff --git a/server/models/video/video-playlist.ts b/server/models/video/video-playlist.ts
index af81c9906..245475f94 100644
--- a/server/models/video/video-playlist.ts
+++ b/server/models/video/video-playlist.ts
@@ -20,7 +20,7 @@ import {
20import { setAsUpdated } from '@server/helpers/database-utils' 20import { setAsUpdated } from '@server/helpers/database-utils'
21import { buildUUID, uuidToShort } from '@server/helpers/uuid' 21import { buildUUID, uuidToShort } from '@server/helpers/uuid'
22import { MAccountId, MChannelId } from '@server/types/models' 22import { MAccountId, MChannelId } from '@server/types/models'
23import { AttributesOnly } from '@shared/core-utils' 23import { AttributesOnly, buildPlaylistEmbedPath, buildPlaylistLink, buildPlaylistWatchPath } from '@shared/core-utils'
24import { ActivityIconObject } from '../../../shared/models/activitypub/objects' 24import { ActivityIconObject } from '../../../shared/models/activitypub/objects'
25import { PlaylistObject } from '../../../shared/models/activitypub/objects/playlist-object' 25import { PlaylistObject } from '../../../shared/models/activitypub/objects/playlist-object'
26import { VideoPlaylistPrivacy } from '../../../shared/models/videos/playlist/video-playlist-privacy.model' 26import { VideoPlaylistPrivacy } from '../../../shared/models/videos/playlist/video-playlist-privacy.model'
@@ -560,12 +560,12 @@ export class VideoPlaylistModel extends Model<Partial<AttributesOnly<VideoPlayli
560 return join(STATIC_PATHS.THUMBNAILS, this.Thumbnail.filename) 560 return join(STATIC_PATHS.THUMBNAILS, this.Thumbnail.filename)
561 } 561 }
562 562
563 getWatchUrl () { 563 getWatchStaticPath () {
564 return WEBSERVER.URL + '/w/p/' + this.uuid 564 return buildPlaylistWatchPath({ shortUUID: uuidToShort(this.uuid) })
565 } 565 }
566 566
567 getEmbedStaticPath () { 567 getEmbedStaticPath () {
568 return '/video-playlists/embed/' + this.uuid 568 return buildPlaylistEmbedPath(this)
569 } 569 }
570 570
571 static async getStats () { 571 static async getStats () {
diff --git a/server/models/video/video.ts b/server/models/video/video.ts
index 0f0f894e4..543e604bb 100644
--- a/server/models/video/video.ts
+++ b/server/models/video/video.ts
@@ -26,12 +26,13 @@ import {
26} from 'sequelize-typescript' 26} from 'sequelize-typescript'
27import { setAsUpdated } from '@server/helpers/database-utils' 27import { setAsUpdated } from '@server/helpers/database-utils'
28import { buildNSFWFilter } from '@server/helpers/express-utils' 28import { buildNSFWFilter } from '@server/helpers/express-utils'
29import { shortToUUID } from '@server/helpers/uuid'
29import { getPrivaciesForFederation, isPrivacyForFederation, isStateForFederation } from '@server/helpers/video' 30import { getPrivaciesForFederation, isPrivacyForFederation, isStateForFederation } from '@server/helpers/video'
30import { LiveManager } from '@server/lib/live/live-manager' 31import { LiveManager } from '@server/lib/live/live-manager'
31import { getHLSDirectory, getVideoFilePath } from '@server/lib/video-paths' 32import { getHLSDirectory, getVideoFilePath } from '@server/lib/video-paths'
32import { getServerActor } from '@server/models/application/application' 33import { getServerActor } from '@server/models/application/application'
33import { ModelCache } from '@server/models/model-cache' 34import { ModelCache } from '@server/models/model-cache'
34import { AttributesOnly } from '@shared/core-utils' 35import { AttributesOnly, buildVideoEmbedPath, buildVideoWatchPath } from '@shared/core-utils'
35import { VideoFile } from '@shared/models/videos/video-file.model' 36import { VideoFile } from '@shared/models/videos/video-file.model'
36import { ResultList, UserRight, VideoPrivacy, VideoState } from '../../../shared' 37import { ResultList, UserRight, VideoPrivacy, VideoState } from '../../../shared'
37import { VideoObject } from '../../../shared/models/activitypub/objects' 38import { VideoObject } from '../../../shared/models/activitypub/objects'
@@ -1578,11 +1579,11 @@ export class VideoModel extends Model<Partial<AttributesOnly<VideoModel>>> {
1578 } 1579 }
1579 1580
1580 getWatchStaticPath () { 1581 getWatchStaticPath () {
1581 return '/w/' + this.uuid 1582 return buildVideoWatchPath({ shortUUID: shortToUUID(this.uuid) })
1582 } 1583 }
1583 1584
1584 getEmbedStaticPath () { 1585 getEmbedStaticPath () {
1585 return '/videos/embed/' + this.uuid 1586 return buildVideoEmbedPath(this)
1586 } 1587 }
1587 1588
1588 getMiniatureStaticPath () { 1589 getMiniatureStaticPath () {
diff --git a/shared/core-utils/common/date.ts b/shared/core-utils/common/date.ts
index 4f92f758f..3e4a3c08c 100644
--- a/shared/core-utils/common/date.ts
+++ b/shared/core-utils/common/date.ts
@@ -43,6 +43,49 @@ function isLastWeek (d: Date) {
43 return getDaysDifferences(now, d) <= 7 43 return getDaysDifferences(now, d) <= 7
44} 44}
45 45
46function timeToInt (time: number | string) {
47 if (!time) return 0
48 if (typeof time === 'number') return time
49
50 const reg = /^((\d+)[h:])?((\d+)[m:])?((\d+)s?)?$/
51 const matches = time.match(reg)
52
53 if (!matches) return 0
54
55 const hours = parseInt(matches[2] || '0', 10)
56 const minutes = parseInt(matches[4] || '0', 10)
57 const seconds = parseInt(matches[6] || '0', 10)
58
59 return hours * 3600 + minutes * 60 + seconds
60}
61
62function secondsToTime (seconds: number, full = false, symbol?: string) {
63 let time = ''
64
65 if (seconds === 0 && !full) return '0s'
66
67 const hourSymbol = (symbol || 'h')
68 const minuteSymbol = (symbol || 'm')
69 const secondsSymbol = full ? '' : 's'
70
71 const hours = Math.floor(seconds / 3600)
72 if (hours >= 1) time = hours + hourSymbol
73 else if (full) time = '0' + hourSymbol
74
75 seconds %= 3600
76 const minutes = Math.floor(seconds / 60)
77 if (minutes >= 1 && minutes < 10 && full) time += '0' + minutes + minuteSymbol
78 else if (minutes >= 1) time += minutes + minuteSymbol
79 else if (full) time += '00' + minuteSymbol
80
81 seconds %= 60
82 if (seconds >= 1 && seconds < 10 && full) time += '0' + seconds + secondsSymbol
83 else if (seconds >= 1) time += seconds + secondsSymbol
84 else if (full) time += '00'
85
86 return time
87}
88
46// --------------------------------------------------------------------------- 89// ---------------------------------------------------------------------------
47 90
48export { 91export {
@@ -51,7 +94,9 @@ export {
51 isThisMonth, 94 isThisMonth,
52 isToday, 95 isToday,
53 isLastMonth, 96 isLastMonth,
54 isLastWeek 97 isLastWeek,
98 timeToInt,
99 secondsToTime
55} 100}
56 101
57// --------------------------------------------------------------------------- 102// ---------------------------------------------------------------------------
diff --git a/shared/core-utils/common/index.ts b/shared/core-utils/common/index.ts
index 83f2ccbb6..0908ff981 100644
--- a/shared/core-utils/common/index.ts
+++ b/shared/core-utils/common/index.ts
@@ -1,5 +1,6 @@
1export * from './date' 1export * from './date'
2export * from './miscs' 2export * from './miscs'
3export * from './regexp' 3export * from './regexp'
4export * from './promises'
4export * from './types' 5export * from './types'
5export * from './url 6export * from './url'
diff --git a/shared/core-utils/common/miscs.ts b/shared/core-utils/common/miscs.ts
index 4780ca922..bc65dc338 100644
--- a/shared/core-utils/common/miscs.ts
+++ b/shared/core-utils/common/miscs.ts
@@ -20,14 +20,6 @@ function compareSemVer (a: string, b: string) {
20 return segmentsA.length - segmentsB.length 20 return segmentsA.length - segmentsB.length
21} 21}
22 22
23function isPromise (value: any) {
24 return value && typeof value.then === 'function'
25}
26
27function isCatchable (value: any) {
28 return value && typeof value.catch === 'function'
29}
30
31function sortObjectComparator (key: string, order: 'asc' | 'desc') { 23function sortObjectComparator (key: string, order: 'asc' | 'desc') {
32 return (a: any, b: any) => { 24 return (a: any, b: any) => {
33 if (a[key] < b[key]) { 25 if (a[key] < b[key]) {
@@ -45,7 +37,5 @@ function sortObjectComparator (key: string, order: 'asc' | 'desc') {
45export { 37export {
46 randomInt, 38 randomInt,
47 compareSemVer, 39 compareSemVer,
48 isPromise,
49 isCatchable,
50 sortObjectComparator 40 sortObjectComparator
51} 41}
diff --git a/shared/core-utils/common/promises.ts b/shared/core-utils/common/promises.ts
new file mode 100644
index 000000000..7ef9d60b6
--- /dev/null
+++ b/shared/core-utils/common/promises.ts
@@ -0,0 +1,12 @@
1function isPromise (value: any) {
2 return value && typeof value.then === 'function'
3}
4
5function isCatchable (value: any) {
6 return value && typeof value.catch === 'function'
7}
8
9export {
10 isPromise,
11 isCatchable
12}
diff --git a/shared/core-utils/common/url.ts b/shared/core-utils/common/url.ts
index e69de29bb..52ed247c4 100644
--- a/shared/core-utils/common/url.ts
+++ b/shared/core-utils/common/url.ts
@@ -0,0 +1,130 @@
1import { Video, VideoPlaylist } from '../../models'
2import { secondsToTime } from './date'
3
4function buildPlaylistLink (playlist: Pick<VideoPlaylist, 'shortUUID'>, base?: string) {
5 return (base ?? window.location.origin) + buildPlaylistWatchPath(playlist)
6}
7
8function buildPlaylistWatchPath (playlist: Pick<VideoPlaylist, 'shortUUID'>) {
9 return '/w/p/' + playlist.shortUUID
10}
11
12function buildVideoWatchPath (video: Pick<Video, 'shortUUID'>) {
13 return '/w/' + video.shortUUID
14}
15
16function buildVideoLink (video: Pick<Video, 'shortUUID'>, base?: string) {
17 return (base ?? window.location.origin) + buildVideoWatchPath(video)
18}
19
20function buildPlaylistEmbedPath (playlist: Pick<VideoPlaylist, 'uuid'>) {
21 return '/video-playlists/embed/' + playlist.uuid
22}
23
24function buildPlaylistEmbedLink (playlist: Pick<VideoPlaylist, 'uuid'>, base?: string) {
25 return (base ?? window.location.origin) + buildPlaylistEmbedPath(playlist)
26}
27
28function buildVideoEmbedPath (video: Pick<Video, 'uuid'>) {
29 return '/videos/embed/' + video.uuid
30}
31
32function buildVideoEmbedLink (video: Pick<Video, 'uuid'>, base?: string) {
33 return (base ?? window.location.origin) + buildVideoEmbedPath(video)
34}
35
36function decorateVideoLink (options: {
37 url: string
38
39 startTime?: number
40 stopTime?: number
41
42 subtitle?: string
43
44 loop?: boolean
45 autoplay?: boolean
46 muted?: boolean
47
48 // Embed options
49 title?: boolean
50 warningTitle?: boolean
51 controls?: boolean
52 peertubeLink?: boolean
53}) {
54 const { url } = options
55
56 const params = generateParams(window.location.search)
57
58 if (options.startTime !== undefined && options.startTime !== null) {
59 const startTimeInt = Math.floor(options.startTime)
60 params.set('start', secondsToTime(startTimeInt))
61 }
62
63 if (options.stopTime) {
64 const stopTimeInt = Math.floor(options.stopTime)
65 params.set('stop', secondsToTime(stopTimeInt))
66 }
67
68 if (options.subtitle) params.set('subtitle', options.subtitle)
69
70 if (options.loop === true) params.set('loop', '1')
71 if (options.autoplay === true) params.set('autoplay', '1')
72 if (options.muted === true) params.set('muted', '1')
73 if (options.title === false) params.set('title', '0')
74 if (options.warningTitle === false) params.set('warningTitle', '0')
75 if (options.controls === false) params.set('controls', '0')
76 if (options.peertubeLink === false) params.set('peertubeLink', '0')
77
78 return buildUrl(url, params)
79}
80
81function decoratePlaylistLink (options: {
82 url: string
83
84 playlistPosition?: number
85}) {
86 const { url } = options
87
88 const params = generateParams(window.location.search)
89
90 if (options.playlistPosition) params.set('playlistPosition', '' + options.playlistPosition)
91
92 return buildUrl(url, params)
93}
94
95// ---------------------------------------------------------------------------
96
97export {
98 buildPlaylistLink,
99 buildVideoLink,
100
101 buildVideoWatchPath,
102 buildPlaylistWatchPath,
103
104 buildPlaylistEmbedPath,
105 buildVideoEmbedPath,
106
107 buildPlaylistEmbedLink,
108 buildVideoEmbedLink,
109
110 decorateVideoLink,
111 decoratePlaylistLink
112}
113
114function buildUrl (url: string, params: URLSearchParams) {
115 let hasParams = false
116 params.forEach(() => { hasParams = true })
117
118 if (hasParams) return url + '?' + params.toString()
119
120 return url
121}
122
123function generateParams (url: string) {
124 const params = new URLSearchParams(window.location.search)
125 // Unused parameters in embed
126 params.delete('videoId')
127 params.delete('resume')
128
129 return params
130}
diff --git a/shared/core-utils/index.ts b/shared/core-utils/index.ts
index 0b05dc9eb..66d50ef93 100644
--- a/shared/core-utils/index.ts
+++ b/shared/core-utils/index.ts
@@ -1,7 +1,6 @@
1export * from './abuse' 1export * from './abuse'
2export * from './common' 2export * from './common'
3export * from './i18n' 3export * from './i18n'
4export * from './logs'
5export * from './plugins' 4export * from './plugins'
6export * from './renderer' 5export * from './renderer'
7export * from './users' 6export * from './users'
diff --git a/shared/core-utils/logs/index.ts b/shared/core-utils/logs/index.ts
deleted file mode 100644
index ceb5d7a7f..000000000
--- a/shared/core-utils/logs/index.ts
+++ /dev/null
@@ -1 +0,0 @@
1export * from './logs'
diff --git a/shared/core-utils/logs/logs.ts b/shared/core-utils/logs/logs.ts
deleted file mode 100644
index d0996cf55..000000000
--- a/shared/core-utils/logs/logs.ts
+++ /dev/null
@@ -1,25 +0,0 @@
1import { stat } from 'fs-extra'
2
3async function mtimeSortFilesDesc (files: string[], basePath: string) {
4 const promises = []
5 const out: { file: string, mtime: number }[] = []
6
7 for (const file of files) {
8 const p = stat(basePath + '/' + file)
9 .then(stats => {
10 if (stats.isFile()) out.push({ file, mtime: stats.mtime.getTime() })
11 })
12
13 promises.push(p)
14 }
15
16 await Promise.all(promises)
17
18 out.sort((a, b) => b.mtime - a.mtime)
19
20 return out
21}
22
23export {
24 mtimeSortFilesDesc
25}
diff --git a/shared/core-utils/plugins/hooks.ts b/shared/core-utils/plugins/hooks.ts
index 5405e0529..92cb5ad68 100644
--- a/shared/core-utils/plugins/hooks.ts
+++ b/shared/core-utils/plugins/hooks.ts
@@ -1,5 +1,5 @@
1import { HookType } from '../../models/plugins/hook-type.enum' 1import { HookType } from '../../models/plugins/hook-type.enum'
2import { isCatchable, isPromise } from '../miscs/miscs' 2import { isCatchable, isPromise } from '../common/promises'
3 3
4function getHookType (hookName: string) { 4function getHookType (hookName: string) {
5 if (hookName.startsWith('filter:')) return HookType.FILTER 5 if (hookName.startsWith('filter:')) return HookType.FILTER
diff --git a/shared/extra-utils/server/server.ts b/shared/extra-utils/server/server.ts
index d37a7f39c..3c335b8e4 100644
--- a/shared/extra-utils/server/server.ts
+++ b/shared/extra-utils/server/server.ts
@@ -2,7 +2,7 @@ import { ChildProcess, fork } from 'child_process'
2import { copy } from 'fs-extra' 2import { copy } from 'fs-extra'
3import { join } from 'path' 3import { join } from 'path'
4import { root } from '@server/helpers/core-utils' 4import { root } from '@server/helpers/core-utils'
5import { randomInt } from '../../core-utils/miscs/miscs' 5import { randomInt } from '@shared/core-utils'
6import { Video, VideoChannel, VideoCreateResult, VideoDetails } from '../../models/videos' 6import { Video, VideoChannel, VideoCreateResult, VideoDetails } from '../../models/videos'
7import { BulkCommand } from '../bulk' 7import { BulkCommand } from '../bulk'
8import { CLICommand } from '../cli' 8import { CLICommand } from '../cli'