Refactor video links builders

pull/4283/head
Chocobozzz 2021-07-26 15:04:37 +02:00
parent 9162fdd363
commit 15a7eafb89
No known key found for this signature in database
GPG Key ID: 583A612D890159BE
41 changed files with 281 additions and 254 deletions

View 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({

View 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({

View 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'

View 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,

View 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'

View 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({

View 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',

View 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'>) {

View 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'

View 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

View 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'

View 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'

View 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'

View 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: {}) {

View 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)

View 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'

View 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')

View 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')

View 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

View 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,

View 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 {

View 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')

View File

@ -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'

View 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'

View 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()
}
]

View 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: [

View 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

View 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,

View File

@ -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 {

View 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 () {

View 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 () {

View 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
}
// ---------------------------------------------------------------------------

View 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'

View 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
}

View 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
}

View 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
}

View 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'

View File

@ -1 +0,0 @@
export * from './logs'

View File

@ -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
}

View 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

View 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'