]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/commitdiff
Refactor video links builders
authorChocobozzz <me@florianbigard.com>
Mon, 26 Jul 2021 13:04:37 +0000 (15:04 +0200)
committerChocobozzz <me@florianbigard.com>
Mon, 26 Jul 2021 13:04:37 +0000 (15:04 +0200)
41 files changed:
client/src/app/+admin/moderation/video-block-list/video-block-list.component.ts
client/src/app/+admin/plugins/plugin-list-installed/plugin-list-installed.component.ts
client/src/app/+videos/+video-watch/video-watch.component.ts
client/src/app/core/renderer/markdown.service.ts
client/src/app/shared/shared-abuse-list/abuse-list-table.component.ts
client/src/app/shared/shared-custom-markup/peertube-custom-tags/embed-markup.component.ts
client/src/app/shared/shared-forms/timestamp-input.component.ts
client/src/app/shared/shared-main/video/video.model.ts
client/src/app/shared/shared-moderation/report-modals/video-report.component.ts
client/src/app/shared/shared-share-modal/video-share.component.ts
client/src/app/shared/shared-video-miniature/abstract-video-list.ts
client/src/app/shared/shared-video-playlist/video-add-to-playlist.component.ts
client/src/app/shared/shared-video-playlist/video-playlist-element-miniature.component.ts
client/src/app/shared/shared-video-playlist/video-playlist.model.ts
client/src/assets/player/p2p-media-loader/p2p-media-loader-plugin.ts
client/src/assets/player/peertube-player-manager.ts
client/src/assets/player/peertube-plugin.ts
client/src/assets/player/playlist/playlist-menu-item.ts
client/src/assets/player/stats/stats-card.ts
client/src/assets/player/utils.ts
client/src/assets/player/videojs-components/peertube-link-button.ts
client/src/assets/player/webtorrent/webtorrent-plugin.ts
scripts/parse-log.ts
server/controllers/api/server/logs.ts
server/controllers/bots.ts
server/controllers/feeds.ts
server/helpers/logger.ts
server/initializers/constants.ts
server/lib/schedulers/plugins-check-scheduler.ts
server/models/video/video-playlist.ts
server/models/video/video.ts
shared/core-utils/common/date.ts
shared/core-utils/common/index.ts
shared/core-utils/common/miscs.ts
shared/core-utils/common/promises.ts [new file with mode: 0644]
shared/core-utils/common/url.ts
shared/core-utils/index.ts
shared/core-utils/logs/index.ts [deleted file]
shared/core-utils/logs/logs.ts [deleted file]
shared/core-utils/plugins/hooks.ts
shared/extra-utils/server/server.ts

index 3af20ea0a362808f15e4ecc0b0f772c2a46e511d..4fe5ec441d0770c2098de60c5b4a8b3af5a1712d 100644 (file)
@@ -1,6 +1,6 @@
 import { SortMeta } from 'primeng/api'
 import { switchMap } from 'rxjs/operators'
-import { buildVideoEmbedLink, buildVideoOrPlaylistEmbed, decorateVideoLink } from 'src/assets/player/utils'
+import { buildVideoOrPlaylistEmbed } from 'src/assets/player/utils'
 import { environment } from 'src/environments/environment'
 import { Component, OnInit } from '@angular/core'
 import { DomSanitizer } from '@angular/platform-browser'
@@ -9,6 +9,7 @@ import { ConfirmService, MarkdownService, Notifier, RestPagination, RestTable, S
 import { AdvancedInputFilter } from '@app/shared/shared-forms'
 import { DropdownAction, Video, VideoService } from '@app/shared/shared-main'
 import { VideoBlockService } from '@app/shared/shared-moderation'
+import { buildVideoEmbedLink, decorateVideoLink } from '@shared/core-utils'
 import { VideoBlacklist, VideoBlacklistType } from '@shared/models'
 
 @Component({
index 6af2249207b552e63b353dd30ca4dfd001fb8359..968abcbe5854c1d1119c37268789f32eb4675425 100644 (file)
@@ -4,7 +4,7 @@ import { ActivatedRoute, Router } from '@angular/router'
 import { PluginApiService } from '@app/+admin/plugins/shared/plugin-api.service'
 import { ComponentPagination, ConfirmService, hasMoreItems, Notifier } from '@app/core'
 import { PluginService } from '@app/core/plugins/plugin.service'
-import { compareSemVer } from '@shared/core-utils/miscs/miscs'
+import { compareSemVer } from '@shared/core-utils'
 import { PeerTubePlugin, PluginType } from '@shared/models'
 
 @Component({
index 9212b78be0ea3843c7a3e66fea6d69141878677f..ccb9c5e718e7a384e64a15484f01bda27c4fb637 100644 (file)
@@ -21,6 +21,7 @@ import { isXPercentInViewport, scrollToTop } from '@app/helpers'
 import { Video, VideoCaptionService, VideoDetails, VideoService } from '@app/shared/shared-main'
 import { SubscribeButtonComponent } from '@app/shared/shared-user-subscription'
 import { VideoPlaylist, VideoPlaylistService } from '@app/shared/shared-video-playlist'
+import { timeToInt } from '@shared/core-utils'
 import {
   HTMLServerConfig,
   HttpStatusCode,
@@ -39,7 +40,6 @@ import {
   PlayerMode,
   videojs
 } from '../../../assets/player/peertube-player-manager'
-import { timeToInt } from '../../../assets/player/utils'
 import { environment } from '../../../environments/environment'
 import { VideoWatchPlaylistComponent } from './shared'
 
index 01d44864b5296e912778058ae708db6c665b6b33..36258ca9872f6f1a471b9ac83c8b57f5341a0148 100644 (file)
@@ -1,6 +1,6 @@
 import * as MarkdownIt from 'markdown-it'
-import { buildVideoLink, decorateVideoLink } from 'src/assets/player/utils'
 import { Injectable } from '@angular/core'
+import { buildVideoLink, decorateVideoLink } from '@shared/core-utils'
 import {
   COMPLETE_RULES,
   ENHANCED_RULES,
index 393108ac91a786c24d7cd8bee4360965817d8330..a7932ebabd6127e93edd0e2020f24b0c33c8b7cd 100644 (file)
@@ -1,7 +1,7 @@
 import * as debug from 'debug'
 import truncate from 'lodash-es/truncate'
 import { SortMeta } from 'primeng/api'
-import { buildVideoEmbedLink, buildVideoOrPlaylistEmbed, decorateVideoLink } from 'src/assets/player/utils'
+import { buildVideoOrPlaylistEmbed } from 'src/assets/player/utils'
 import { environment } from 'src/environments/environment'
 import { Component, Input, OnInit, ViewChild } from '@angular/core'
 import { DomSanitizer } from '@angular/platform-browser'
@@ -10,6 +10,7 @@ import { ConfirmService, MarkdownService, Notifier, RestPagination, RestTable }
 import { Account, Actor, DropdownAction, Video, VideoService } from '@app/shared/shared-main'
 import { AbuseService, BlocklistService, VideoBlockService } from '@app/shared/shared-moderation'
 import { VideoCommentService } from '@app/shared/shared-video-comment'
+import { buildVideoEmbedLink, decorateVideoLink } from '@shared/core-utils'
 import { AbuseState, AdminAbuse } from '@shared/models'
 import { AdvancedInputFilter } from '../shared-forms'
 import { AbuseMessageModalComponent } from './abuse-message-modal.component'
index ba8969d5b5afcb8e9c783fb3ab9b5e970ed39ce4..53b70cc4759aeaf714ada1dcf01cdfa7a51120b0 100644 (file)
@@ -1,6 +1,7 @@
-import { buildPlaylistEmbedLink, buildVideoEmbedLink, buildVideoOrPlaylistEmbed } from 'src/assets/player/utils'
+import { buildVideoOrPlaylistEmbed } from 'src/assets/player/utils'
 import { environment } from 'src/environments/environment'
 import { Component, ElementRef, Input, OnInit } from '@angular/core'
+import { buildPlaylistEmbedLink, buildVideoEmbedLink } from '@shared/core-utils'
 import { CustomMarkupComponent } from './shared'
 
 @Component({
index 0ffd03d02886082da3e8519751a0278997d313a1..3fc705905e267b5b48f3de21b89310595f72dd90 100644 (file)
@@ -1,6 +1,6 @@
 import { ChangeDetectorRef, Component, EventEmitter, forwardRef, Input, OnInit, Output } from '@angular/core'
 import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'
-import { secondsToTime, timeToInt } from '../../../assets/player/utils'
+import { secondsToTime, timeToInt } from '@shared/core-utils'
 
 @Component({
   selector: 'my-timestamp-input',
index f0a4a3f37c031a9a7b33797d31ed2209e1750ec5..b7720c8d21da3617869c159a391f2d1be60a5f6a 100644 (file)
@@ -2,6 +2,7 @@ import { AuthUser } from '@app/core'
 import { User } from '@app/core/users/user.model'
 import { durationToString, getAbsoluteAPIUrl, getAbsoluteEmbedUrl } from '@app/helpers'
 import { Actor } from '@app/shared/shared-main/account/actor.model'
+import { buildVideoWatchPath } from '@shared/core-utils'
 import { peertubeTranslate } from '@shared/core-utils/i18n'
 import {
   ActorImage,
@@ -92,7 +93,7 @@ export class Video implements VideoServerModel {
   pluginData?: any
 
   static buildWatchUrl (video: Partial<Pick<Video, 'uuid' | 'shortUUID'>>) {
-    return '/w/' + (video.shortUUID || video.uuid)
+    return buildVideoWatchPath({ shortUUID: video.shortUUID || video.uuid })
   }
 
   static buildUpdateUrl (video: Pick<Video, 'uuid'>) {
index 41f4fa30d028462e50f2a067d60f092e2fba94f9..e509ac88f124594f6f51e85017f3342de9d790ca 100644 (file)
@@ -1,5 +1,5 @@
 import { mapValues, pickBy } from 'lodash-es'
-import { buildVideoOrPlaylistEmbed, decorateVideoLink } from 'src/assets/player/utils'
+import { buildVideoOrPlaylistEmbed } from 'src/assets/player/utils'
 import { Component, Input, OnInit, ViewChild } from '@angular/core'
 import { DomSanitizer, SafeHtml } from '@angular/platform-browser'
 import { Notifier } from '@app/core'
@@ -7,6 +7,7 @@ import { ABUSE_REASON_VALIDATOR } from '@app/shared/form-validators/abuse-valida
 import { FormReactive, FormValidatorService } from '@app/shared/shared-forms'
 import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
 import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref'
+import { decorateVideoLink } from '@shared/core-utils'
 import { abusePredefinedReasonsMap } from '@shared/core-utils/abuse'
 import { AbusePredefinedReasonsString } from '@shared/models'
 import { Video } from '../../shared-main'
index cdfe508362da7285815b6c3f0a81222a42ac7223..341abdc2b7eb0a8deac72a165db282a4309ba966 100644 (file)
@@ -2,14 +2,9 @@ import { Component, ElementRef, Input, ViewChild } from '@angular/core'
 import { VideoDetails } from '@app/shared/shared-main'
 import { VideoPlaylist } from '@app/shared/shared-video-playlist'
 import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
+import { buildPlaylistLink, buildVideoLink, decoratePlaylistLink, decorateVideoLink } from '@shared/core-utils'
 import { VideoCaption } from '@shared/models'
-import {
-  buildPlaylistLink,
-  buildVideoLink,
-  buildVideoOrPlaylistEmbed,
-  decoratePlaylistLink,
-  decorateVideoLink
-} from '../../../assets/player/utils'
+import { buildVideoOrPlaylistEmbed } from '../../../assets/player/utils'
 
 type Customizations = {
   startAtCheckbox: boolean
index 52e72d35b8833cfe7463d9af7ea94ad6052e8887..33061a837509276b5a8b0122293b86fa199bf3bf 100644 (file)
@@ -24,7 +24,7 @@ import {
 } from '@app/core'
 import { DisableForReuseHook } from '@app/core/routing/disable-for-reuse-hook'
 import { GlobalIconName } from '@app/shared/shared-icons'
-import { isLastMonth, isLastWeek, isThisMonth, isToday, isYesterday } from '@shared/core-utils/miscs/date'
+import { isLastMonth, isLastWeek, isThisMonth, isToday, isYesterday } from '@shared/core-utils'
 import { HTMLServerConfig, UserRight, VideoFilter, VideoSortField } from '@shared/models'
 import { NSFWPolicyType } from '@shared/models/videos/nsfw-policy.type'
 import { Syndication, Video } from '../shared-main'
index 681e5becd0e40e84704375cce29fbbbb3d7e83d9..8b019103c2c41fa4949d596adff4d79b4d09ca9f 100644 (file)
@@ -4,6 +4,7 @@ import { debounceTime, filter } from 'rxjs/operators'
 import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnChanges, OnDestroy, OnInit, SimpleChanges } from '@angular/core'
 import { AuthService, DisableForReuseHook, Notifier } from '@app/core'
 import { FormReactive, FormValidatorService } from '@app/shared/shared-forms'
+import { secondsToTime } from '@shared/core-utils'
 import {
   Video,
   VideoExistInPlaylist,
@@ -12,7 +13,6 @@ import {
   VideoPlaylistElementUpdate,
   VideoPlaylistPrivacy
 } from '@shared/models'
-import { secondsToTime } from '../../../assets/player/utils'
 import { VIDEO_PLAYLIST_DISPLAY_NAME_VALIDATOR } from '../form-validators/video-playlist-validators'
 import { CachedPlaylist, VideoPlaylistService } from './video-playlist.service'
 
index d99170e4e236913b4fcade137612778912bca6fa..2e495ec266c7a95e8a472df0840947690f4b9195 100644 (file)
@@ -2,8 +2,8 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, In
 import { AuthService, Notifier, ServerService } from '@app/core'
 import { Video } from '@app/shared/shared-main'
 import { NgbDropdown } from '@ng-bootstrap/ng-bootstrap'
+import { secondsToTime } from '@shared/core-utils'
 import { HTMLServerConfig, VideoPlaylistElementType, VideoPlaylistElementUpdate } from '@shared/models'
-import { secondsToTime } from '../../../assets/player/utils'
 import { VideoPlaylistElement } from './video-playlist-element.model'
 import { VideoPlaylist } from './video-playlist.model'
 import { VideoPlaylistService } from './video-playlist.service'
index 55013e4c54841763ccbc751cf690a47288da4423..fcc2ce705dda5539b2c509f5bb6458060b6118a9 100644 (file)
@@ -1,5 +1,6 @@
 import { getAbsoluteAPIUrl, getAbsoluteEmbedUrl } from '@app/helpers'
 import { Actor } from '@app/shared/shared-main'
+import { buildPlaylistWatchPath } from '@shared/core-utils'
 import { peertubeTranslate } from '@shared/core-utils/i18n'
 import {
   AccountSummary,
@@ -44,7 +45,7 @@ export class VideoPlaylist implements ServerVideoPlaylist {
   videoChannelBy?: string
 
   static buildWatchUrl (playlist: Pick<VideoPlaylist, 'uuid' | 'shortUUID'>) {
-    return '/w/p/' + (playlist.shortUUID || playlist.uuid)
+    return buildPlaylistWatchPath({ shortUUID: playlist.shortUUID || playlist.uuid })
   }
 
   constructor (hash: ServerVideoPlaylist, translations: {}) {
index f1bd9f0c41d1b4f812116bbe4ed87875303a4160..2eb849d2b157f34849c35b878458a0f705c4d291 100644 (file)
@@ -2,8 +2,8 @@ import * as Hlsjs from 'hls.js/dist/hls.light.js'
 import { Events, Segment } from 'p2p-media-loader-core'
 import { Engine, initHlsJsPlayer, initVideoJsContribHlsJsPlayer } from 'p2p-media-loader-hlsjs'
 import videojs from 'video.js'
+import { timeToInt } from '@shared/core-utils'
 import { P2PMediaLoaderPluginOptions, PlayerNetworkInfo } from '../peertube-videojs-typings'
-import { timeToInt } from '../utils'
 import { registerConfigPlugin, registerSourceHandler } from './hls-plugin'
 
 registerConfigPlugin(videojs)
index 6f0b804cd4756e5ae9a63047347944bbc1cbf704..766ad203ef7868d57f461148ab09ce3a16032c2b 100644 (file)
@@ -23,6 +23,7 @@ import './videojs-components/theater-button'
 import './playlist/playlist-plugin'
 import videojs from 'video.js'
 import { PluginsManager } from '@root-helpers/plugins-manager'
+import { buildVideoLink, decorateVideoLink } from '@shared/core-utils'
 import { isDefaultLocale } from '@shared/core-utils/i18n'
 import { VideoFile } from '@shared/models'
 import { copyToClipboard } from '../../root-helpers/utils'
@@ -40,7 +41,7 @@ import {
   VideoJSPluginOptions
 } from './peertube-videojs-typings'
 import { TranslationsManager } from './translations-manager'
-import { buildVideoLink, buildVideoOrPlaylistEmbed, decorateVideoLink, getRtcConfig, isIOS, isSafari } from './utils'
+import { buildVideoOrPlaylistEmbed, getRtcConfig, isIOS, isSafari } from './utils'
 
 // Change 'Playback Rate' to 'Speed' (smaller for our settings menu)
 (videojs.getComponent('PlaybackRateMenuButton') as any).prototype.controlText_ = 'Speed'
index 07c7e33f6f8ff4ac004571f9e1d57da43437ffa4..919b7c2398cc0a338da3a797f07fe2063003666b 100644 (file)
@@ -1,12 +1,6 @@
-import videojs from 'video.js'
 import './videojs-components/settings-menu-button'
-import {
-  PeerTubePluginOptions,
-  ResolutionUpdateData,
-  UserWatching,
-  VideoJSCaption
-} from './peertube-videojs-typings'
-import { isMobile, timeToInt } from './utils'
+import videojs from 'video.js'
+import { timeToInt } from '@shared/core-utils'
 import {
   getStoredLastSubtitle,
   getStoredMute,
@@ -16,6 +10,8 @@ import {
   saveVideoWatchHistory,
   saveVolumeInStore
 } from './peertube-player-local-storage'
+import { PeerTubePluginOptions, ResolutionUpdateData, UserWatching, VideoJSCaption } from './peertube-videojs-typings'
+import { isMobile } from './utils'
 
 const Plugin = videojs.getPlugin('plugin')
 
index 87a72b6a360758c5ad4c609ed534fd8618366449..2519a34c780fd841c10ce6b018cc40ed1eebbc2f 100644 (file)
@@ -1,7 +1,7 @@
 import videojs from 'video.js'
+import { secondsToTime } from '@shared/core-utils'
 import { VideoPlaylistElement } from '@shared/models'
 import { PlaylistItemOptions } from '../peertube-videojs-typings'
-import { secondsToTime } from '../utils'
 
 const Component = videojs.getComponent('Component')
 
index a93f595062ac98f6e4ccd5e108af2467a67fc282..b271d052660fb129039ef5c82037a2e84e7e55df 100644 (file)
@@ -1,6 +1,7 @@
 import videojs from 'video.js'
+import { secondsToTime } from '@shared/core-utils'
 import { PlayerNetworkInfo as EventPlayerNetworkInfo } from '../peertube-videojs-typings'
-import { bytes, secondsToTime } from '../utils'
+import { bytes } from '../utils'
 
 interface StatsCardOptions extends videojs.ComponentOptions {
   videoUUID: string
index eb9302493f45a4de0f3098d29e99a5f91a030a2d..f0a1b1aee246c7e5141a30a139e8560ad36d475e 100644 (file)
@@ -1,5 +1,5 @@
-import { Video, VideoFile, VideoPlaylist } from '@shared/models'
 import { escapeHTML } from '@shared/core-utils/renderer'
+import { VideoFile } from '@shared/models'
 
 function toTitleCase (str: string) {
   return str.charAt(0).toUpperCase() + str.slice(1)
@@ -43,144 +43,9 @@ function isMobile () {
   return /iPhone|iPad|iPod|Android/i.test(navigator.userAgent)
 }
 
-function buildPlaylistLink (playlist: Pick<VideoPlaylist, 'shortUUID'>, base?: string) {
-  return (base ?? window.location.origin) + '/w/p/' + playlist.shortUUID
-}
-
-function buildVideoLink (video: Pick<Video, 'shortUUID'>, base?: string) {
-  return (base ?? window.location.origin) + '/w/' + video.shortUUID
-}
-
-function buildPlaylistEmbedLink (playlist: Pick<VideoPlaylist, 'uuid'>, base?: string) {
-  return (base ?? window.location.origin) + '/video-playlists/embed/' + playlist.uuid
-}
-
-function buildVideoEmbedLink (video: Pick<Video, 'uuid'>, base?: string) {
-  return (base ?? window.location.origin) + '/videos/embed/' + video.uuid
-}
-
-function decorateVideoLink (options: {
-  url: string
-
-  startTime?: number
-  stopTime?: number
-
-  subtitle?: string
-
-  loop?: boolean
-  autoplay?: boolean
-  muted?: boolean
-
-  // Embed options
-  title?: boolean
-  warningTitle?: boolean
-  controls?: boolean
-  peertubeLink?: boolean
-}) {
-  const { url } = options
-
-  const params = generateParams(window.location.search)
-
-  if (options.startTime !== undefined && options.startTime !== null) {
-    const startTimeInt = Math.floor(options.startTime)
-    params.set('start', secondsToTime(startTimeInt))
-  }
-
-  if (options.stopTime) {
-    const stopTimeInt = Math.floor(options.stopTime)
-    params.set('stop', secondsToTime(stopTimeInt))
-  }
-
-  if (options.subtitle) params.set('subtitle', options.subtitle)
-
-  if (options.loop === true) params.set('loop', '1')
-  if (options.autoplay === true) params.set('autoplay', '1')
-  if (options.muted === true) params.set('muted', '1')
-  if (options.title === false) params.set('title', '0')
-  if (options.warningTitle === false) params.set('warningTitle', '0')
-  if (options.controls === false) params.set('controls', '0')
-  if (options.peertubeLink === false) params.set('peertubeLink', '0')
-
-  return buildUrl(url, params)
-}
-
-function decoratePlaylistLink (options: {
-  url: string
-
-  playlistPosition?: number
-}) {
-  const { url } = options
-
-  const params = generateParams(window.location.search)
-
-  if (options.playlistPosition) params.set('playlistPosition', '' + options.playlistPosition)
-
-  return buildUrl(url, params)
-}
-
-function buildUrl (url: string, params: URLSearchParams) {
-  let hasParams = false
-  params.forEach(() => hasParams = true)
-
-  if (hasParams) return url + '?' + params.toString()
-
-  return url
-}
-
-function generateParams (url: string) {
-  const params = new URLSearchParams(window.location.search)
-  // Unused parameters in embed
-  params.delete('videoId')
-  params.delete('resume')
-
-  return params
-}
-
-function timeToInt (time: number | string) {
-  if (!time) return 0
-  if (typeof time === 'number') return time
-
-  const reg = /^((\d+)[h:])?((\d+)[m:])?((\d+)s?)?$/
-  const matches = time.match(reg)
-
-  if (!matches) return 0
-
-  const hours = parseInt(matches[2] || '0', 10)
-  const minutes = parseInt(matches[4] || '0', 10)
-  const seconds = parseInt(matches[6] || '0', 10)
-
-  return hours * 3600 + minutes * 60 + seconds
-}
-
-function secondsToTime (seconds: number, full = false, symbol?: string) {
-  let time = ''
-
-  if (seconds === 0 && !full) return '0s'
-
-  const hourSymbol = (symbol || 'h')
-  const minuteSymbol = (symbol || 'm')
-  const secondsSymbol = full ? '' : 's'
-
-  const hours = Math.floor(seconds / 3600)
-  if (hours >= 1) time = hours + hourSymbol
-  else if (full) time = '0' + hourSymbol
-
-  seconds %= 3600
-  const minutes = Math.floor(seconds / 60)
-  if (minutes >= 1 && minutes < 10 && full) time += '0' + minutes + minuteSymbol
-  else if (minutes >= 1) time += minutes + minuteSymbol
-  else if (full) time += '00' + minuteSymbol
-
-  seconds %= 60
-  if (seconds >= 1 && seconds < 10 && full) time += '0' + seconds + secondsSymbol
-  else if (seconds >= 1) time += seconds + secondsSymbol
-  else if (full) time += '00'
-
-  return time
-}
-
 function buildVideoOrPlaylistEmbed (embedUrl: string, embedTitle: string) {
   const title = escapeHTML(embedTitle)
+
   return '<iframe width="560" height="315" ' +
     'sandbox="allow-same-origin allow-scripts allow-popups" ' +
     'title="' + title + '" ' +
@@ -229,17 +94,8 @@ function getRtcConfig () {
 export {
   getRtcConfig,
   toTitleCase,
-  timeToInt,
-  secondsToTime,
   isWebRTCDisabled,
 
-  buildPlaylistLink,
-  buildVideoLink,
-  decorateVideoLink,
-  decoratePlaylistLink,
-  buildPlaylistEmbedLink,
-  buildVideoEmbedLink,
-
   buildVideoOrPlaylistEmbed,
   videoFileMaxByResolution,
   videoFileMinByResolution,
index f47c165d98091ef50c5e587019754aac6909a96a..9267b2ed4cc768f3455ae880fe94962fd805010b 100644 (file)
@@ -1,6 +1,6 @@
 import videojs from 'video.js'
+import { buildVideoLink, decorateVideoLink } from '@shared/core-utils'
 import { PeerTubeLinkButtonOptions } from '../peertube-videojs-typings'
-import { buildVideoLink, decorateVideoLink } from '../utils'
 
 const Button = videojs.getComponent('Button')
 class PeerTubeLinkButton extends Button {
index b648b29e8105eb73a39484c2d0c7d61dd2239eb6..17d369c10ff1944f6db9cdd071a8fde8e1f36d38 100644 (file)
@@ -1,9 +1,7 @@
 import videojs from 'video.js'
 import * as WebTorrent from 'webtorrent'
-import { renderVideo } from './video-renderer'
-import { LoadedQualityData, PlayerNetworkInfo, WebtorrentPluginOptions } from '../peertube-videojs-typings'
-import { getRtcConfig, timeToInt, videoFileMaxByResolution, videoFileMinByResolution, isIOS, isSafari } from '../utils'
-import { PeertubeChunkStore } from './peertube-chunk-store'
+import { timeToInt } from '@shared/core-utils'
+import { VideoFile } from '@shared/models'
 import {
   getAverageBandwidthInStore,
   getStoredMute,
@@ -11,7 +9,10 @@ import {
   getStoredVolume,
   saveAverageBandwidth
 } from '../peertube-player-local-storage'
-import { VideoFile } from '@shared/models'
+import { LoadedQualityData, PlayerNetworkInfo, WebtorrentPluginOptions } from '../peertube-videojs-typings'
+import { getRtcConfig, isIOS, videoFileMaxByResolution, videoFileMinByResolution } from '../utils'
+import { PeertubeChunkStore } from './peertube-chunk-store'
+import { renderVideo } from './video-renderer'
 
 const CacheChunkStore = require('cache-chunk-store')
 
index c1650358923010a3594aae5dffb6a9f0264a63e5..6cd3a1860aed75898c357e6c73462a058ce41f02 100755 (executable)
@@ -6,9 +6,8 @@ import { createReadStream, readdir } from 'fs-extra'
 import { join } from 'path'
 import { createInterface } from 'readline'
 import * as winston from 'winston'
-import { labelFormatter } from '../server/helpers/logger'
+import { labelFormatter, mtimeSortFilesDesc } from '../server/helpers/logger'
 import { CONFIG } from '../server/initializers/config'
-import { mtimeSortFilesDesc } from '../shared/core-utils/logs/logs'
 import { inspect } from 'util'
 import { format as sqlFormat } from 'sql-formatter'
 
index f78607d35966f08aac1ca5e72cb6c9f96b6538ce..39eceb6547d7c4902de199c3f568ddc380672690 100644 (file)
@@ -1,8 +1,7 @@
 import * as express from 'express'
 import { readdir, readFile } from 'fs-extra'
 import { join } from 'path'
-import { logger } from '@server/helpers/logger'
-import { mtimeSortFilesDesc } from '../../../../shared/core-utils/logs/logs'
+import { logger, mtimeSortFilesDesc } from '@server/helpers/logger'
 import { LogLevel } from '../../../../shared/models/server/log-level.type'
 import { UserRight } from '../../../../shared/models/users'
 import { CONFIG } from '../../../initializers/config'
index 93aa0cf3080bd9912ad5566f86cbc6870a1ace0e..de04116086ee2fb6257713f3360ffacf5cec0e9e 100644 (file)
@@ -1,13 +1,13 @@
 import * as express from 'express'
-import { asyncMiddleware } from '../middlewares'
-import { ROUTE_CACHE_LIFETIME, WEBSERVER } from '../initializers/constants'
+import { truncate } from 'lodash'
 import { SitemapStream, streamToPromise } from 'sitemap'
+import { buildNSFWFilter } from '../helpers/express-utils'
+import { ROUTE_CACHE_LIFETIME, WEBSERVER } from '../initializers/constants'
+import { asyncMiddleware } from '../middlewares'
+import { cacheRoute } from '../middlewares/cache/cache'
+import { AccountModel } from '../models/account/account'
 import { VideoModel } from '../models/video/video'
 import { VideoChannelModel } from '../models/video/video-channel'
-import { AccountModel } from '../models/account/account'
-import { cacheRoute } from '../middlewares/cache/cache'
-import { buildNSFWFilter } from '../helpers/express-utils'
-import { truncate } from 'lodash'
 
 const botsRouter = express.Router()
 
@@ -75,13 +75,13 @@ async function getSitemapLocalVideoUrls () {
   })
 
   return data.map(v => ({
-    url: WEBSERVER.URL + '/w/' + v.uuid,
+    url: WEBSERVER.URL + v.getWatchStaticPath(),
     video: [
       {
         title: v.name,
         // Sitemap description should be < 2000 characters
         description: truncate(v.description || v.name, { length: 2000, omission: '...' }),
-        player_loc: WEBSERVER.URL + '/videos/embed/' + v.uuid,
+        player_loc: WEBSERVER.URL + v.getEmbedStaticPath(),
         thumbnail_loc: WEBSERVER.URL + v.getMiniatureStaticPath()
       }
     ]
index cdc6bfb8be176047b3dde2cd271b9c2daf8c3ebd..9fa70a7c8ca89e7f9cc9983f422252a909801517 100644 (file)
@@ -286,7 +286,7 @@ function addVideosToFeed (feed, videos: VideoModel[]) {
     feed.addItem({
       title: video.name,
       id: video.url,
-      link: WEBSERVER.URL + '/w/' + video.uuid,
+      link: WEBSERVER.URL + video.getWatchStaticPath(),
       description: video.getTruncatedDescription(),
       content: video.description,
       author: [
index 29e06860dbf9ca0604805dc94bfeab2e75aa1d46..20c3c3edbaa111bb5ef304a98ae7781fb5648ebd 100644 (file)
@@ -1,5 +1,5 @@
 // Thanks http://tostring.it/2014/06/23/advanced-logging-with-nodejs/
-import { mkdirpSync } from 'fs-extra'
+import { mkdirpSync, stat } from 'fs-extra'
 import { omit } from 'lodash'
 import * as path from 'path'
 import { format as sqlFormat } from 'sql-formatter'
@@ -158,6 +158,26 @@ function loggerTagsFactory (...defaultTags: string[]): LoggerTagsFn {
   }
 }
 
+async function mtimeSortFilesDesc (files: string[], basePath: string) {
+  const promises = []
+  const out: { file: string, mtime: number }[] = []
+
+  for (const file of files) {
+    const p = stat(basePath + '/' + file)
+      .then(stats => {
+        if (stats.isFile()) out.push({ file, mtime: stats.mtime.getTime() })
+      })
+
+    promises.push(p)
+  }
+
+  await Promise.all(promises)
+
+  out.sort((a, b) => b.mtime - a.mtime)
+
+  return out
+}
+
 // ---------------------------------------------------------------------------
 
 export {
@@ -168,6 +188,7 @@ export {
   labelFormatter,
   consoleLoggerFormat,
   jsonLoggerFormat,
+  mtimeSortFilesDesc,
   logger,
   loggerTagsFactory,
   bunyanLogger
index ee4503b2c39cad0c04c00055243f98bc312a01f1..5f121d9a4db6cb842a03ed812bbc4cb5ad46a8b8 100644 (file)
@@ -2,7 +2,7 @@ import { CronRepeatOptions, EveryRepeatOptions } from 'bull'
 import { randomBytes } from 'crypto'
 import { invert } from 'lodash'
 import { join } from 'path'
-import { randomInt } from '../../shared/core-utils/miscs/miscs'
+import { randomInt } from '../../shared/core-utils/common/miscs'
 import {
   AbuseState,
   JobType,
index 9a1ae3ec50861dc9e26695dd6ca6680e7f9dc0b8..c95e109b03cf8460b259236ac19986535c46d0ff 100644 (file)
@@ -1,12 +1,12 @@
+import { chunk } from 'lodash'
+import { compareSemVer } from '@shared/core-utils'
 import { logger } from '../../helpers/logger'
-import { AbstractScheduler } from './abstract-scheduler'
-import { SCHEDULER_INTERVALS_MS } from '../../initializers/constants'
 import { CONFIG } from '../../initializers/config'
+import { SCHEDULER_INTERVALS_MS } from '../../initializers/constants'
 import { PluginModel } from '../../models/server/plugin'
-import { chunk } from 'lodash'
-import { getLatestPluginsVersion } from '../plugins/plugin-index'
-import { compareSemVer } from '../../../shared/core-utils/miscs/miscs'
 import { Notifier } from '../notifier'
+import { getLatestPluginsVersion } from '../plugins/plugin-index'
+import { AbstractScheduler } from './abstract-scheduler'
 
 export class PluginsCheckScheduler extends AbstractScheduler {
 
index af81c9906808f24e8b1b17ce9354e057d1a317e0..245475f94f8211f8105f70afb0efff773c8e3132 100644 (file)
@@ -20,7 +20,7 @@ import {
 import { setAsUpdated } from '@server/helpers/database-utils'
 import { buildUUID, uuidToShort } from '@server/helpers/uuid'
 import { MAccountId, MChannelId } from '@server/types/models'
-import { AttributesOnly } from '@shared/core-utils'
+import { AttributesOnly, buildPlaylistEmbedPath, buildPlaylistLink, buildPlaylistWatchPath } from '@shared/core-utils'
 import { ActivityIconObject } from '../../../shared/models/activitypub/objects'
 import { PlaylistObject } from '../../../shared/models/activitypub/objects/playlist-object'
 import { VideoPlaylistPrivacy } from '../../../shared/models/videos/playlist/video-playlist-privacy.model'
@@ -560,12 +560,12 @@ export class VideoPlaylistModel extends Model<Partial<AttributesOnly<VideoPlayli
     return join(STATIC_PATHS.THUMBNAILS, this.Thumbnail.filename)
   }
 
-  getWatchUrl () {
-    return WEBSERVER.URL + '/w/p/' + this.uuid
+  getWatchStaticPath () {
+    return buildPlaylistWatchPath({ shortUUID: uuidToShort(this.uuid) })
   }
 
   getEmbedStaticPath () {
-    return '/video-playlists/embed/' + this.uuid
+    return buildPlaylistEmbedPath(this)
   }
 
   static async getStats () {
index 0f0f894e45ebed6bc81ab3fd8680797c0867349c..543e604bb9391131040f7f5abeb3a490d9a4e22d 100644 (file)
@@ -26,12 +26,13 @@ import {
 } from 'sequelize-typescript'
 import { setAsUpdated } from '@server/helpers/database-utils'
 import { buildNSFWFilter } from '@server/helpers/express-utils'
+import { shortToUUID } from '@server/helpers/uuid'
 import { getPrivaciesForFederation, isPrivacyForFederation, isStateForFederation } from '@server/helpers/video'
 import { LiveManager } from '@server/lib/live/live-manager'
 import { getHLSDirectory, getVideoFilePath } from '@server/lib/video-paths'
 import { getServerActor } from '@server/models/application/application'
 import { ModelCache } from '@server/models/model-cache'
-import { AttributesOnly } from '@shared/core-utils'
+import { AttributesOnly, buildVideoEmbedPath, buildVideoWatchPath } from '@shared/core-utils'
 import { VideoFile } from '@shared/models/videos/video-file.model'
 import { ResultList, UserRight, VideoPrivacy, VideoState } from '../../../shared'
 import { VideoObject } from '../../../shared/models/activitypub/objects'
@@ -1578,11 +1579,11 @@ export class VideoModel extends Model<Partial<AttributesOnly<VideoModel>>> {
   }
 
   getWatchStaticPath () {
-    return '/w/' + this.uuid
+    return buildVideoWatchPath({ shortUUID: shortToUUID(this.uuid) })
   }
 
   getEmbedStaticPath () {
-    return '/videos/embed/' + this.uuid
+    return buildVideoEmbedPath(this)
   }
 
   getMiniatureStaticPath () {
index 4f92f758ff665600d5b392d614e4366c0b18522a..3e4a3c08c7e03c366997588431169dd4a719bf37 100644 (file)
@@ -43,6 +43,49 @@ function isLastWeek (d: Date) {
   return getDaysDifferences(now, d) <= 7
 }
 
+function timeToInt (time: number | string) {
+  if (!time) return 0
+  if (typeof time === 'number') return time
+
+  const reg = /^((\d+)[h:])?((\d+)[m:])?((\d+)s?)?$/
+  const matches = time.match(reg)
+
+  if (!matches) return 0
+
+  const hours = parseInt(matches[2] || '0', 10)
+  const minutes = parseInt(matches[4] || '0', 10)
+  const seconds = parseInt(matches[6] || '0', 10)
+
+  return hours * 3600 + minutes * 60 + seconds
+}
+
+function secondsToTime (seconds: number, full = false, symbol?: string) {
+  let time = ''
+
+  if (seconds === 0 && !full) return '0s'
+
+  const hourSymbol = (symbol || 'h')
+  const minuteSymbol = (symbol || 'm')
+  const secondsSymbol = full ? '' : 's'
+
+  const hours = Math.floor(seconds / 3600)
+  if (hours >= 1) time = hours + hourSymbol
+  else if (full) time = '0' + hourSymbol
+
+  seconds %= 3600
+  const minutes = Math.floor(seconds / 60)
+  if (minutes >= 1 && minutes < 10 && full) time += '0' + minutes + minuteSymbol
+  else if (minutes >= 1) time += minutes + minuteSymbol
+  else if (full) time += '00' + minuteSymbol
+
+  seconds %= 60
+  if (seconds >= 1 && seconds < 10 && full) time += '0' + seconds + secondsSymbol
+  else if (seconds >= 1) time += seconds + secondsSymbol
+  else if (full) time += '00'
+
+  return time
+}
+
 // ---------------------------------------------------------------------------
 
 export {
@@ -51,7 +94,9 @@ export {
   isThisMonth,
   isToday,
   isLastMonth,
-  isLastWeek
+  isLastWeek,
+  timeToInt,
+  secondsToTime
 }
 
 // ---------------------------------------------------------------------------
index 83f2ccbb6e04807b60aca80895b99f018818c7c1..0908ff9811c81700d74f8cf9398788a1a540a7d1 100644 (file)
@@ -1,5 +1,6 @@
 export * from './date'
 export * from './miscs'
 export * from './regexp'
+export * from './promises'
 export * from './types'
-export * from './url
+export * from './url'
index 4780ca922d9d615843493b89a780092f2976a650..bc65dc33863ebcf111d18e05b3a0f47287ce9473 100644 (file)
@@ -20,14 +20,6 @@ function compareSemVer (a: string, b: string) {
   return segmentsA.length - segmentsB.length
 }
 
-function isPromise (value: any) {
-  return value && typeof value.then === 'function'
-}
-
-function isCatchable (value: any) {
-  return value && typeof value.catch === 'function'
-}
-
 function sortObjectComparator (key: string, order: 'asc' | 'desc') {
   return (a: any, b: any) => {
     if (a[key] < b[key]) {
@@ -45,7 +37,5 @@ function sortObjectComparator (key: string, order: 'asc' | 'desc') {
 export {
   randomInt,
   compareSemVer,
-  isPromise,
-  isCatchable,
   sortObjectComparator
 }
diff --git a/shared/core-utils/common/promises.ts b/shared/core-utils/common/promises.ts
new file mode 100644 (file)
index 0000000..7ef9d60
--- /dev/null
@@ -0,0 +1,12 @@
+function isPromise (value: any) {
+  return value && typeof value.then === 'function'
+}
+
+function isCatchable (value: any) {
+  return value && typeof value.catch === 'function'
+}
+
+export {
+  isPromise,
+  isCatchable
+}
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..52ed247c437a0e1f20ba8c3d38f9108de394e7c1 100644 (file)
@@ -0,0 +1,130 @@
+import { Video, VideoPlaylist } from '../../models'
+import { secondsToTime } from './date'
+
+function buildPlaylistLink (playlist: Pick<VideoPlaylist, 'shortUUID'>, base?: string) {
+  return (base ?? window.location.origin) + buildPlaylistWatchPath(playlist)
+}
+
+function buildPlaylistWatchPath (playlist: Pick<VideoPlaylist, 'shortUUID'>) {
+  return '/w/p/' + playlist.shortUUID
+}
+
+function buildVideoWatchPath (video: Pick<Video, 'shortUUID'>) {
+  return '/w/' + video.shortUUID
+}
+
+function buildVideoLink (video: Pick<Video, 'shortUUID'>, base?: string) {
+  return (base ?? window.location.origin) + buildVideoWatchPath(video)
+}
+
+function buildPlaylistEmbedPath (playlist: Pick<VideoPlaylist, 'uuid'>) {
+  return '/video-playlists/embed/' + playlist.uuid
+}
+
+function buildPlaylistEmbedLink (playlist: Pick<VideoPlaylist, 'uuid'>, base?: string) {
+  return (base ?? window.location.origin) + buildPlaylistEmbedPath(playlist)
+}
+
+function buildVideoEmbedPath (video: Pick<Video, 'uuid'>) {
+  return '/videos/embed/' + video.uuid
+}
+
+function buildVideoEmbedLink (video: Pick<Video, 'uuid'>, base?: string) {
+  return (base ?? window.location.origin) + buildVideoEmbedPath(video)
+}
+
+function decorateVideoLink (options: {
+  url: string
+
+  startTime?: number
+  stopTime?: number
+
+  subtitle?: string
+
+  loop?: boolean
+  autoplay?: boolean
+  muted?: boolean
+
+  // Embed options
+  title?: boolean
+  warningTitle?: boolean
+  controls?: boolean
+  peertubeLink?: boolean
+}) {
+  const { url } = options
+
+  const params = generateParams(window.location.search)
+
+  if (options.startTime !== undefined && options.startTime !== null) {
+    const startTimeInt = Math.floor(options.startTime)
+    params.set('start', secondsToTime(startTimeInt))
+  }
+
+  if (options.stopTime) {
+    const stopTimeInt = Math.floor(options.stopTime)
+    params.set('stop', secondsToTime(stopTimeInt))
+  }
+
+  if (options.subtitle) params.set('subtitle', options.subtitle)
+
+  if (options.loop === true) params.set('loop', '1')
+  if (options.autoplay === true) params.set('autoplay', '1')
+  if (options.muted === true) params.set('muted', '1')
+  if (options.title === false) params.set('title', '0')
+  if (options.warningTitle === false) params.set('warningTitle', '0')
+  if (options.controls === false) params.set('controls', '0')
+  if (options.peertubeLink === false) params.set('peertubeLink', '0')
+
+  return buildUrl(url, params)
+}
+
+function decoratePlaylistLink (options: {
+  url: string
+
+  playlistPosition?: number
+}) {
+  const { url } = options
+
+  const params = generateParams(window.location.search)
+
+  if (options.playlistPosition) params.set('playlistPosition', '' + options.playlistPosition)
+
+  return buildUrl(url, params)
+}
+
+// ---------------------------------------------------------------------------
+
+export {
+  buildPlaylistLink,
+  buildVideoLink,
+
+  buildVideoWatchPath,
+  buildPlaylistWatchPath,
+
+  buildPlaylistEmbedPath,
+  buildVideoEmbedPath,
+
+  buildPlaylistEmbedLink,
+  buildVideoEmbedLink,
+
+  decorateVideoLink,
+  decoratePlaylistLink
+}
+
+function buildUrl (url: string, params: URLSearchParams) {
+  let hasParams = false
+  params.forEach(() => { hasParams = true })
+
+  if (hasParams) return url + '?' + params.toString()
+
+  return url
+}
+
+function generateParams (url: string) {
+  const params = new URLSearchParams(window.location.search)
+  // Unused parameters in embed
+  params.delete('videoId')
+  params.delete('resume')
+
+  return params
+}
index 0b05dc9ebf4ec74304dbe978627b8a2ae0756008..66d50ef938756d90fbea2c5bf52edd4a488037bd 100644 (file)
@@ -1,7 +1,6 @@
 export * from './abuse'
 export * from './common'
 export * from './i18n'
-export * from './logs'
 export * from './plugins'
 export * from './renderer'
 export * from './users'
diff --git a/shared/core-utils/logs/index.ts b/shared/core-utils/logs/index.ts
deleted file mode 100644 (file)
index ceb5d7a..0000000
+++ /dev/null
@@ -1 +0,0 @@
-export * from './logs'
diff --git a/shared/core-utils/logs/logs.ts b/shared/core-utils/logs/logs.ts
deleted file mode 100644 (file)
index d0996cf..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-import { stat } from 'fs-extra'
-
-async function mtimeSortFilesDesc (files: string[], basePath: string) {
-  const promises = []
-  const out: { file: string, mtime: number }[] = []
-
-  for (const file of files) {
-    const p = stat(basePath + '/' + file)
-      .then(stats => {
-        if (stats.isFile()) out.push({ file, mtime: stats.mtime.getTime() })
-      })
-
-    promises.push(p)
-  }
-
-  await Promise.all(promises)
-
-  out.sort((a, b) => b.mtime - a.mtime)
-
-  return out
-}
-
-export {
-  mtimeSortFilesDesc
-}
index 5405e0529ad0eeab00bc08ed6cee44d84a8b5ef6..92cb5ad68d0543ded523a79de2db51410445cbdd 100644 (file)
@@ -1,5 +1,5 @@
 import { HookType } from '../../models/plugins/hook-type.enum'
-import { isCatchable, isPromise } from '../miscs/miscs'
+import { isCatchable, isPromise } from '../common/promises'
 
 function getHookType (hookName: string) {
   if (hookName.startsWith('filter:')) return HookType.FILTER
index d37a7f39c05b3d7eddc4e0a449d8bef8e907e0b0..3c335b8e448ab77f7b52b6c8a663f59f0218fdbf 100644 (file)
@@ -2,7 +2,7 @@ import { ChildProcess, fork } from 'child_process'
 import { copy } from 'fs-extra'
 import { join } from 'path'
 import { root } from '@server/helpers/core-utils'
-import { randomInt } from '../../core-utils/miscs/miscs'
+import { randomInt } from '@shared/core-utils'
 import { Video, VideoChannel, VideoCreateResult, VideoDetails } from '../../models/videos'
 import { BulkCommand } from '../bulk'
 import { CLICommand } from '../cli'