Refactor video links builders
This commit is contained in:
parent
9162fdd363
commit
15a7eafb89
41 changed files with 281 additions and 254 deletions
|
@ -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({
|
||||
|
|
|
@ -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({
|
||||
|
|
|
@ -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'
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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({
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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'>) {
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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'
|
||||
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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: {}) {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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')
|
||||
|
||||
|
|
|
@ -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')
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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')
|
||||
|
||||
|
|
|
@ -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'
|
||||
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
]
|
||||
|
|
|
@ -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: [
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import { logger } from '../../helpers/logger'
|
||||
import { AbstractScheduler } from './abstract-scheduler'
|
||||
import { SCHEDULER_INTERVALS_MS } from '../../initializers/constants'
|
||||
import { CONFIG } from '../../initializers/config'
|
||||
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 { compareSemVer } from '@shared/core-utils'
|
||||
import { logger } from '../../helpers/logger'
|
||||
import { CONFIG } from '../../initializers/config'
|
||||
import { SCHEDULER_INTERVALS_MS } from '../../initializers/constants'
|
||||
import { PluginModel } from '../../models/server/plugin'
|
||||
import { Notifier } from '../notifier'
|
||||
import { getLatestPluginsVersion } from '../plugins/plugin-index'
|
||||
import { AbstractScheduler } from './abstract-scheduler'
|
||||
|
||||
export class PluginsCheckScheduler extends AbstractScheduler {
|
||||
|
||||
|
|
|
@ -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 () {
|
||||
|
|
|
@ -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 () {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
12
shared/core-utils/common/promises.ts
Normal file
12
shared/core-utils/common/promises.ts
Normal file
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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'
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
export * from './logs'
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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'
|
||||
|
|
Loading…
Reference in a new issue