mirror of https://github.com/Chocobozzz/PeerTube
Refactor video links builders
parent
9162fdd363
commit
15a7eafb89
|
@ -1,6 +1,6 @@
|
||||||
import { SortMeta } from 'primeng/api'
|
import { SortMeta } from 'primeng/api'
|
||||||
import { switchMap } from 'rxjs/operators'
|
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 { environment } from 'src/environments/environment'
|
||||||
import { Component, OnInit } from '@angular/core'
|
import { Component, OnInit } from '@angular/core'
|
||||||
import { DomSanitizer } from '@angular/platform-browser'
|
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 { AdvancedInputFilter } from '@app/shared/shared-forms'
|
||||||
import { DropdownAction, Video, VideoService } from '@app/shared/shared-main'
|
import { DropdownAction, Video, VideoService } from '@app/shared/shared-main'
|
||||||
import { VideoBlockService } from '@app/shared/shared-moderation'
|
import { VideoBlockService } from '@app/shared/shared-moderation'
|
||||||
|
import { buildVideoEmbedLink, decorateVideoLink } from '@shared/core-utils'
|
||||||
import { VideoBlacklist, VideoBlacklistType } from '@shared/models'
|
import { VideoBlacklist, VideoBlacklistType } from '@shared/models'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { ActivatedRoute, Router } from '@angular/router'
|
||||||
import { PluginApiService } from '@app/+admin/plugins/shared/plugin-api.service'
|
import { PluginApiService } from '@app/+admin/plugins/shared/plugin-api.service'
|
||||||
import { ComponentPagination, ConfirmService, hasMoreItems, Notifier } from '@app/core'
|
import { ComponentPagination, ConfirmService, hasMoreItems, Notifier } from '@app/core'
|
||||||
import { PluginService } from '@app/core/plugins/plugin.service'
|
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'
|
import { PeerTubePlugin, PluginType } from '@shared/models'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
|
|
|
@ -21,6 +21,7 @@ import { isXPercentInViewport, scrollToTop } from '@app/helpers'
|
||||||
import { Video, VideoCaptionService, VideoDetails, VideoService } from '@app/shared/shared-main'
|
import { Video, VideoCaptionService, VideoDetails, VideoService } from '@app/shared/shared-main'
|
||||||
import { SubscribeButtonComponent } from '@app/shared/shared-user-subscription'
|
import { SubscribeButtonComponent } from '@app/shared/shared-user-subscription'
|
||||||
import { VideoPlaylist, VideoPlaylistService } from '@app/shared/shared-video-playlist'
|
import { VideoPlaylist, VideoPlaylistService } from '@app/shared/shared-video-playlist'
|
||||||
|
import { timeToInt } from '@shared/core-utils'
|
||||||
import {
|
import {
|
||||||
HTMLServerConfig,
|
HTMLServerConfig,
|
||||||
HttpStatusCode,
|
HttpStatusCode,
|
||||||
|
@ -39,7 +40,6 @@ import {
|
||||||
PlayerMode,
|
PlayerMode,
|
||||||
videojs
|
videojs
|
||||||
} from '../../../assets/player/peertube-player-manager'
|
} from '../../../assets/player/peertube-player-manager'
|
||||||
import { timeToInt } from '../../../assets/player/utils'
|
|
||||||
import { environment } from '../../../environments/environment'
|
import { environment } from '../../../environments/environment'
|
||||||
import { VideoWatchPlaylistComponent } from './shared'
|
import { VideoWatchPlaylistComponent } from './shared'
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import * as MarkdownIt from 'markdown-it'
|
import * as MarkdownIt from 'markdown-it'
|
||||||
import { buildVideoLink, decorateVideoLink } from 'src/assets/player/utils'
|
|
||||||
import { Injectable } from '@angular/core'
|
import { Injectable } from '@angular/core'
|
||||||
|
import { buildVideoLink, decorateVideoLink } from '@shared/core-utils'
|
||||||
import {
|
import {
|
||||||
COMPLETE_RULES,
|
COMPLETE_RULES,
|
||||||
ENHANCED_RULES,
|
ENHANCED_RULES,
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import * as debug from 'debug'
|
import * as debug from 'debug'
|
||||||
import truncate from 'lodash-es/truncate'
|
import truncate from 'lodash-es/truncate'
|
||||||
import { SortMeta } from 'primeng/api'
|
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 { environment } from 'src/environments/environment'
|
||||||
import { Component, Input, OnInit, ViewChild } from '@angular/core'
|
import { Component, Input, OnInit, ViewChild } from '@angular/core'
|
||||||
import { DomSanitizer } from '@angular/platform-browser'
|
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 { Account, Actor, DropdownAction, Video, VideoService } from '@app/shared/shared-main'
|
||||||
import { AbuseService, BlocklistService, VideoBlockService } from '@app/shared/shared-moderation'
|
import { AbuseService, BlocklistService, VideoBlockService } from '@app/shared/shared-moderation'
|
||||||
import { VideoCommentService } from '@app/shared/shared-video-comment'
|
import { VideoCommentService } from '@app/shared/shared-video-comment'
|
||||||
|
import { buildVideoEmbedLink, decorateVideoLink } from '@shared/core-utils'
|
||||||
import { AbuseState, AdminAbuse } from '@shared/models'
|
import { AbuseState, AdminAbuse } from '@shared/models'
|
||||||
import { AdvancedInputFilter } from '../shared-forms'
|
import { AdvancedInputFilter } from '../shared-forms'
|
||||||
import { AbuseMessageModalComponent } from './abuse-message-modal.component'
|
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 { environment } from 'src/environments/environment'
|
||||||
import { Component, ElementRef, Input, OnInit } from '@angular/core'
|
import { Component, ElementRef, Input, OnInit } from '@angular/core'
|
||||||
|
import { buildPlaylistEmbedLink, buildVideoEmbedLink } from '@shared/core-utils'
|
||||||
import { CustomMarkupComponent } from './shared'
|
import { CustomMarkupComponent } from './shared'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { ChangeDetectorRef, Component, EventEmitter, forwardRef, Input, OnInit, Output } from '@angular/core'
|
import { ChangeDetectorRef, Component, EventEmitter, forwardRef, Input, OnInit, Output } from '@angular/core'
|
||||||
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'
|
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'
|
||||||
import { secondsToTime, timeToInt } from '../../../assets/player/utils'
|
import { secondsToTime, timeToInt } from '@shared/core-utils'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'my-timestamp-input',
|
selector: 'my-timestamp-input',
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { AuthUser } from '@app/core'
|
||||||
import { User } from '@app/core/users/user.model'
|
import { User } from '@app/core/users/user.model'
|
||||||
import { durationToString, getAbsoluteAPIUrl, getAbsoluteEmbedUrl } from '@app/helpers'
|
import { durationToString, getAbsoluteAPIUrl, getAbsoluteEmbedUrl } from '@app/helpers'
|
||||||
import { Actor } from '@app/shared/shared-main/account/actor.model'
|
import { Actor } from '@app/shared/shared-main/account/actor.model'
|
||||||
|
import { buildVideoWatchPath } from '@shared/core-utils'
|
||||||
import { peertubeTranslate } from '@shared/core-utils/i18n'
|
import { peertubeTranslate } from '@shared/core-utils/i18n'
|
||||||
import {
|
import {
|
||||||
ActorImage,
|
ActorImage,
|
||||||
|
@ -92,7 +93,7 @@ export class Video implements VideoServerModel {
|
||||||
pluginData?: any
|
pluginData?: any
|
||||||
|
|
||||||
static buildWatchUrl (video: Partial<Pick<Video, 'uuid' | 'shortUUID'>>) {
|
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'>) {
|
static buildUpdateUrl (video: Pick<Video, 'uuid'>) {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { mapValues, pickBy } from 'lodash-es'
|
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 { Component, Input, OnInit, ViewChild } from '@angular/core'
|
||||||
import { DomSanitizer, SafeHtml } from '@angular/platform-browser'
|
import { DomSanitizer, SafeHtml } from '@angular/platform-browser'
|
||||||
import { Notifier } from '@app/core'
|
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 { FormReactive, FormValidatorService } from '@app/shared/shared-forms'
|
||||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
||||||
import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref'
|
import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref'
|
||||||
|
import { decorateVideoLink } from '@shared/core-utils'
|
||||||
import { abusePredefinedReasonsMap } from '@shared/core-utils/abuse'
|
import { abusePredefinedReasonsMap } from '@shared/core-utils/abuse'
|
||||||
import { AbusePredefinedReasonsString } from '@shared/models'
|
import { AbusePredefinedReasonsString } from '@shared/models'
|
||||||
import { Video } from '../../shared-main'
|
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 { VideoDetails } from '@app/shared/shared-main'
|
||||||
import { VideoPlaylist } from '@app/shared/shared-video-playlist'
|
import { VideoPlaylist } from '@app/shared/shared-video-playlist'
|
||||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
||||||
|
import { buildPlaylistLink, buildVideoLink, decoratePlaylistLink, decorateVideoLink } from '@shared/core-utils'
|
||||||
import { VideoCaption } from '@shared/models'
|
import { VideoCaption } from '@shared/models'
|
||||||
import {
|
import { buildVideoOrPlaylistEmbed } from '../../../assets/player/utils'
|
||||||
buildPlaylistLink,
|
|
||||||
buildVideoLink,
|
|
||||||
buildVideoOrPlaylistEmbed,
|
|
||||||
decoratePlaylistLink,
|
|
||||||
decorateVideoLink
|
|
||||||
} from '../../../assets/player/utils'
|
|
||||||
|
|
||||||
type Customizations = {
|
type Customizations = {
|
||||||
startAtCheckbox: boolean
|
startAtCheckbox: boolean
|
||||||
|
|
|
@ -24,7 +24,7 @@ import {
|
||||||
} from '@app/core'
|
} from '@app/core'
|
||||||
import { DisableForReuseHook } from '@app/core/routing/disable-for-reuse-hook'
|
import { DisableForReuseHook } from '@app/core/routing/disable-for-reuse-hook'
|
||||||
import { GlobalIconName } from '@app/shared/shared-icons'
|
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 { HTMLServerConfig, UserRight, VideoFilter, VideoSortField } from '@shared/models'
|
||||||
import { NSFWPolicyType } from '@shared/models/videos/nsfw-policy.type'
|
import { NSFWPolicyType } from '@shared/models/videos/nsfw-policy.type'
|
||||||
import { Syndication, Video } from '../shared-main'
|
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 { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnChanges, OnDestroy, OnInit, SimpleChanges } from '@angular/core'
|
||||||
import { AuthService, DisableForReuseHook, Notifier } from '@app/core'
|
import { AuthService, DisableForReuseHook, Notifier } from '@app/core'
|
||||||
import { FormReactive, FormValidatorService } from '@app/shared/shared-forms'
|
import { FormReactive, FormValidatorService } from '@app/shared/shared-forms'
|
||||||
|
import { secondsToTime } from '@shared/core-utils'
|
||||||
import {
|
import {
|
||||||
Video,
|
Video,
|
||||||
VideoExistInPlaylist,
|
VideoExistInPlaylist,
|
||||||
|
@ -12,7 +13,6 @@ import {
|
||||||
VideoPlaylistElementUpdate,
|
VideoPlaylistElementUpdate,
|
||||||
VideoPlaylistPrivacy
|
VideoPlaylistPrivacy
|
||||||
} from '@shared/models'
|
} from '@shared/models'
|
||||||
import { secondsToTime } from '../../../assets/player/utils'
|
|
||||||
import { VIDEO_PLAYLIST_DISPLAY_NAME_VALIDATOR } from '../form-validators/video-playlist-validators'
|
import { VIDEO_PLAYLIST_DISPLAY_NAME_VALIDATOR } from '../form-validators/video-playlist-validators'
|
||||||
import { CachedPlaylist, VideoPlaylistService } from './video-playlist.service'
|
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 { AuthService, Notifier, ServerService } from '@app/core'
|
||||||
import { Video } from '@app/shared/shared-main'
|
import { Video } from '@app/shared/shared-main'
|
||||||
import { NgbDropdown } from '@ng-bootstrap/ng-bootstrap'
|
import { NgbDropdown } from '@ng-bootstrap/ng-bootstrap'
|
||||||
|
import { secondsToTime } from '@shared/core-utils'
|
||||||
import { HTMLServerConfig, VideoPlaylistElementType, VideoPlaylistElementUpdate } from '@shared/models'
|
import { HTMLServerConfig, VideoPlaylistElementType, VideoPlaylistElementUpdate } from '@shared/models'
|
||||||
import { secondsToTime } from '../../../assets/player/utils'
|
|
||||||
import { VideoPlaylistElement } from './video-playlist-element.model'
|
import { VideoPlaylistElement } from './video-playlist-element.model'
|
||||||
import { VideoPlaylist } from './video-playlist.model'
|
import { VideoPlaylist } from './video-playlist.model'
|
||||||
import { VideoPlaylistService } from './video-playlist.service'
|
import { VideoPlaylistService } from './video-playlist.service'
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { getAbsoluteAPIUrl, getAbsoluteEmbedUrl } from '@app/helpers'
|
import { getAbsoluteAPIUrl, getAbsoluteEmbedUrl } from '@app/helpers'
|
||||||
import { Actor } from '@app/shared/shared-main'
|
import { Actor } from '@app/shared/shared-main'
|
||||||
|
import { buildPlaylistWatchPath } from '@shared/core-utils'
|
||||||
import { peertubeTranslate } from '@shared/core-utils/i18n'
|
import { peertubeTranslate } from '@shared/core-utils/i18n'
|
||||||
import {
|
import {
|
||||||
AccountSummary,
|
AccountSummary,
|
||||||
|
@ -44,7 +45,7 @@ export class VideoPlaylist implements ServerVideoPlaylist {
|
||||||
videoChannelBy?: string
|
videoChannelBy?: string
|
||||||
|
|
||||||
static buildWatchUrl (playlist: Pick<VideoPlaylist, 'uuid' | 'shortUUID'>) {
|
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: {}) {
|
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 { Events, Segment } from 'p2p-media-loader-core'
|
||||||
import { Engine, initHlsJsPlayer, initVideoJsContribHlsJsPlayer } from 'p2p-media-loader-hlsjs'
|
import { Engine, initHlsJsPlayer, initVideoJsContribHlsJsPlayer } from 'p2p-media-loader-hlsjs'
|
||||||
import videojs from 'video.js'
|
import videojs from 'video.js'
|
||||||
|
import { timeToInt } from '@shared/core-utils'
|
||||||
import { P2PMediaLoaderPluginOptions, PlayerNetworkInfo } from '../peertube-videojs-typings'
|
import { P2PMediaLoaderPluginOptions, PlayerNetworkInfo } from '../peertube-videojs-typings'
|
||||||
import { timeToInt } from '../utils'
|
|
||||||
import { registerConfigPlugin, registerSourceHandler } from './hls-plugin'
|
import { registerConfigPlugin, registerSourceHandler } from './hls-plugin'
|
||||||
|
|
||||||
registerConfigPlugin(videojs)
|
registerConfigPlugin(videojs)
|
||||||
|
|
|
@ -23,6 +23,7 @@ import './videojs-components/theater-button'
|
||||||
import './playlist/playlist-plugin'
|
import './playlist/playlist-plugin'
|
||||||
import videojs from 'video.js'
|
import videojs from 'video.js'
|
||||||
import { PluginsManager } from '@root-helpers/plugins-manager'
|
import { PluginsManager } from '@root-helpers/plugins-manager'
|
||||||
|
import { buildVideoLink, decorateVideoLink } from '@shared/core-utils'
|
||||||
import { isDefaultLocale } from '@shared/core-utils/i18n'
|
import { isDefaultLocale } from '@shared/core-utils/i18n'
|
||||||
import { VideoFile } from '@shared/models'
|
import { VideoFile } from '@shared/models'
|
||||||
import { copyToClipboard } from '../../root-helpers/utils'
|
import { copyToClipboard } from '../../root-helpers/utils'
|
||||||
|
@ -40,7 +41,7 @@ import {
|
||||||
VideoJSPluginOptions
|
VideoJSPluginOptions
|
||||||
} from './peertube-videojs-typings'
|
} from './peertube-videojs-typings'
|
||||||
import { TranslationsManager } from './translations-manager'
|
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)
|
// Change 'Playback Rate' to 'Speed' (smaller for our settings menu)
|
||||||
(videojs.getComponent('PlaybackRateMenuButton') as any).prototype.controlText_ = 'Speed'
|
(videojs.getComponent('PlaybackRateMenuButton') as any).prototype.controlText_ = 'Speed'
|
||||||
|
|
|
@ -1,12 +1,6 @@
|
||||||
import videojs from 'video.js'
|
|
||||||
import './videojs-components/settings-menu-button'
|
import './videojs-components/settings-menu-button'
|
||||||
import {
|
import videojs from 'video.js'
|
||||||
PeerTubePluginOptions,
|
import { timeToInt } from '@shared/core-utils'
|
||||||
ResolutionUpdateData,
|
|
||||||
UserWatching,
|
|
||||||
VideoJSCaption
|
|
||||||
} from './peertube-videojs-typings'
|
|
||||||
import { isMobile, timeToInt } from './utils'
|
|
||||||
import {
|
import {
|
||||||
getStoredLastSubtitle,
|
getStoredLastSubtitle,
|
||||||
getStoredMute,
|
getStoredMute,
|
||||||
|
@ -16,6 +10,8 @@ import {
|
||||||
saveVideoWatchHistory,
|
saveVideoWatchHistory,
|
||||||
saveVolumeInStore
|
saveVolumeInStore
|
||||||
} from './peertube-player-local-storage'
|
} from './peertube-player-local-storage'
|
||||||
|
import { PeerTubePluginOptions, ResolutionUpdateData, UserWatching, VideoJSCaption } from './peertube-videojs-typings'
|
||||||
|
import { isMobile } from './utils'
|
||||||
|
|
||||||
const Plugin = videojs.getPlugin('plugin')
|
const Plugin = videojs.getPlugin('plugin')
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import videojs from 'video.js'
|
import videojs from 'video.js'
|
||||||
|
import { secondsToTime } from '@shared/core-utils'
|
||||||
import { VideoPlaylistElement } from '@shared/models'
|
import { VideoPlaylistElement } from '@shared/models'
|
||||||
import { PlaylistItemOptions } from '../peertube-videojs-typings'
|
import { PlaylistItemOptions } from '../peertube-videojs-typings'
|
||||||
import { secondsToTime } from '../utils'
|
|
||||||
|
|
||||||
const Component = videojs.getComponent('Component')
|
const Component = videojs.getComponent('Component')
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import videojs from 'video.js'
|
import videojs from 'video.js'
|
||||||
|
import { secondsToTime } from '@shared/core-utils'
|
||||||
import { PlayerNetworkInfo as EventPlayerNetworkInfo } from '../peertube-videojs-typings'
|
import { PlayerNetworkInfo as EventPlayerNetworkInfo } from '../peertube-videojs-typings'
|
||||||
import { bytes, secondsToTime } from '../utils'
|
import { bytes } from '../utils'
|
||||||
|
|
||||||
interface StatsCardOptions extends videojs.ComponentOptions {
|
interface StatsCardOptions extends videojs.ComponentOptions {
|
||||||
videoUUID: string
|
videoUUID: string
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { Video, VideoFile, VideoPlaylist } from '@shared/models'
|
|
||||||
import { escapeHTML } from '@shared/core-utils/renderer'
|
import { escapeHTML } from '@shared/core-utils/renderer'
|
||||||
|
import { VideoFile } from '@shared/models'
|
||||||
|
|
||||||
function toTitleCase (str: string) {
|
function toTitleCase (str: string) {
|
||||||
return str.charAt(0).toUpperCase() + str.slice(1)
|
return str.charAt(0).toUpperCase() + str.slice(1)
|
||||||
|
@ -43,144 +43,9 @@ function isMobile () {
|
||||||
return /iPhone|iPad|iPod|Android/i.test(navigator.userAgent)
|
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) {
|
function buildVideoOrPlaylistEmbed (embedUrl: string, embedTitle: string) {
|
||||||
const title = escapeHTML(embedTitle)
|
const title = escapeHTML(embedTitle)
|
||||||
|
|
||||||
return '<iframe width="560" height="315" ' +
|
return '<iframe width="560" height="315" ' +
|
||||||
'sandbox="allow-same-origin allow-scripts allow-popups" ' +
|
'sandbox="allow-same-origin allow-scripts allow-popups" ' +
|
||||||
'title="' + title + '" ' +
|
'title="' + title + '" ' +
|
||||||
|
@ -229,17 +94,8 @@ function getRtcConfig () {
|
||||||
export {
|
export {
|
||||||
getRtcConfig,
|
getRtcConfig,
|
||||||
toTitleCase,
|
toTitleCase,
|
||||||
timeToInt,
|
|
||||||
secondsToTime,
|
|
||||||
isWebRTCDisabled,
|
isWebRTCDisabled,
|
||||||
|
|
||||||
buildPlaylistLink,
|
|
||||||
buildVideoLink,
|
|
||||||
decorateVideoLink,
|
|
||||||
decoratePlaylistLink,
|
|
||||||
buildPlaylistEmbedLink,
|
|
||||||
buildVideoEmbedLink,
|
|
||||||
|
|
||||||
buildVideoOrPlaylistEmbed,
|
buildVideoOrPlaylistEmbed,
|
||||||
videoFileMaxByResolution,
|
videoFileMaxByResolution,
|
||||||
videoFileMinByResolution,
|
videoFileMinByResolution,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import videojs from 'video.js'
|
import videojs from 'video.js'
|
||||||
|
import { buildVideoLink, decorateVideoLink } from '@shared/core-utils'
|
||||||
import { PeerTubeLinkButtonOptions } from '../peertube-videojs-typings'
|
import { PeerTubeLinkButtonOptions } from '../peertube-videojs-typings'
|
||||||
import { buildVideoLink, decorateVideoLink } from '../utils'
|
|
||||||
|
|
||||||
const Button = videojs.getComponent('Button')
|
const Button = videojs.getComponent('Button')
|
||||||
class PeerTubeLinkButton extends Button {
|
class PeerTubeLinkButton extends Button {
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
import videojs from 'video.js'
|
import videojs from 'video.js'
|
||||||
import * as WebTorrent from 'webtorrent'
|
import * as WebTorrent from 'webtorrent'
|
||||||
import { renderVideo } from './video-renderer'
|
import { timeToInt } from '@shared/core-utils'
|
||||||
import { LoadedQualityData, PlayerNetworkInfo, WebtorrentPluginOptions } from '../peertube-videojs-typings'
|
import { VideoFile } from '@shared/models'
|
||||||
import { getRtcConfig, timeToInt, videoFileMaxByResolution, videoFileMinByResolution, isIOS, isSafari } from '../utils'
|
|
||||||
import { PeertubeChunkStore } from './peertube-chunk-store'
|
|
||||||
import {
|
import {
|
||||||
getAverageBandwidthInStore,
|
getAverageBandwidthInStore,
|
||||||
getStoredMute,
|
getStoredMute,
|
||||||
|
@ -11,7 +9,10 @@ import {
|
||||||
getStoredVolume,
|
getStoredVolume,
|
||||||
saveAverageBandwidth
|
saveAverageBandwidth
|
||||||
} from '../peertube-player-local-storage'
|
} 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')
|
const CacheChunkStore = require('cache-chunk-store')
|
||||||
|
|
||||||
|
|
|
@ -6,9 +6,8 @@ import { createReadStream, readdir } from 'fs-extra'
|
||||||
import { join } from 'path'
|
import { join } from 'path'
|
||||||
import { createInterface } from 'readline'
|
import { createInterface } from 'readline'
|
||||||
import * as winston from 'winston'
|
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 { CONFIG } from '../server/initializers/config'
|
||||||
import { mtimeSortFilesDesc } from '../shared/core-utils/logs/logs'
|
|
||||||
import { inspect } from 'util'
|
import { inspect } from 'util'
|
||||||
import { format as sqlFormat } from 'sql-formatter'
|
import { format as sqlFormat } from 'sql-formatter'
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
import * as express from 'express'
|
import * as express from 'express'
|
||||||
import { readdir, readFile } from 'fs-extra'
|
import { readdir, readFile } from 'fs-extra'
|
||||||
import { join } from 'path'
|
import { join } from 'path'
|
||||||
import { logger } from '@server/helpers/logger'
|
import { logger, mtimeSortFilesDesc } from '@server/helpers/logger'
|
||||||
import { mtimeSortFilesDesc } from '../../../../shared/core-utils/logs/logs'
|
|
||||||
import { LogLevel } from '../../../../shared/models/server/log-level.type'
|
import { LogLevel } from '../../../../shared/models/server/log-level.type'
|
||||||
import { UserRight } from '../../../../shared/models/users'
|
import { UserRight } from '../../../../shared/models/users'
|
||||||
import { CONFIG } from '../../../initializers/config'
|
import { CONFIG } from '../../../initializers/config'
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
import * as express from 'express'
|
import * as express from 'express'
|
||||||
import { asyncMiddleware } from '../middlewares'
|
import { truncate } from 'lodash'
|
||||||
import { ROUTE_CACHE_LIFETIME, WEBSERVER } from '../initializers/constants'
|
|
||||||
import { SitemapStream, streamToPromise } from 'sitemap'
|
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 { VideoModel } from '../models/video/video'
|
||||||
import { VideoChannelModel } from '../models/video/video-channel'
|
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()
|
const botsRouter = express.Router()
|
||||||
|
|
||||||
|
@ -75,13 +75,13 @@ async function getSitemapLocalVideoUrls () {
|
||||||
})
|
})
|
||||||
|
|
||||||
return data.map(v => ({
|
return data.map(v => ({
|
||||||
url: WEBSERVER.URL + '/w/' + v.uuid,
|
url: WEBSERVER.URL + v.getWatchStaticPath(),
|
||||||
video: [
|
video: [
|
||||||
{
|
{
|
||||||
title: v.name,
|
title: v.name,
|
||||||
// Sitemap description should be < 2000 characters
|
// Sitemap description should be < 2000 characters
|
||||||
description: truncate(v.description || v.name, { length: 2000, omission: '...' }),
|
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()
|
thumbnail_loc: WEBSERVER.URL + v.getMiniatureStaticPath()
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -286,7 +286,7 @@ function addVideosToFeed (feed, videos: VideoModel[]) {
|
||||||
feed.addItem({
|
feed.addItem({
|
||||||
title: video.name,
|
title: video.name,
|
||||||
id: video.url,
|
id: video.url,
|
||||||
link: WEBSERVER.URL + '/w/' + video.uuid,
|
link: WEBSERVER.URL + video.getWatchStaticPath(),
|
||||||
description: video.getTruncatedDescription(),
|
description: video.getTruncatedDescription(),
|
||||||
content: video.description,
|
content: video.description,
|
||||||
author: [
|
author: [
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// Thanks http://tostring.it/2014/06/23/advanced-logging-with-nodejs/
|
// 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 { omit } from 'lodash'
|
||||||
import * as path from 'path'
|
import * as path from 'path'
|
||||||
import { format as sqlFormat } from 'sql-formatter'
|
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 {
|
export {
|
||||||
|
@ -168,6 +188,7 @@ export {
|
||||||
labelFormatter,
|
labelFormatter,
|
||||||
consoleLoggerFormat,
|
consoleLoggerFormat,
|
||||||
jsonLoggerFormat,
|
jsonLoggerFormat,
|
||||||
|
mtimeSortFilesDesc,
|
||||||
logger,
|
logger,
|
||||||
loggerTagsFactory,
|
loggerTagsFactory,
|
||||||
bunyanLogger
|
bunyanLogger
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { CronRepeatOptions, EveryRepeatOptions } from 'bull'
|
||||||
import { randomBytes } from 'crypto'
|
import { randomBytes } from 'crypto'
|
||||||
import { invert } from 'lodash'
|
import { invert } from 'lodash'
|
||||||
import { join } from 'path'
|
import { join } from 'path'
|
||||||
import { randomInt } from '../../shared/core-utils/miscs/miscs'
|
import { randomInt } from '../../shared/core-utils/common/miscs'
|
||||||
import {
|
import {
|
||||||
AbuseState,
|
AbuseState,
|
||||||
JobType,
|
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 { chunk } from 'lodash'
|
||||||
import { getLatestPluginsVersion } from '../plugins/plugin-index'
|
import { compareSemVer } from '@shared/core-utils'
|
||||||
import { compareSemVer } from '../../../shared/core-utils/miscs/miscs'
|
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 { Notifier } from '../notifier'
|
||||||
|
import { getLatestPluginsVersion } from '../plugins/plugin-index'
|
||||||
|
import { AbstractScheduler } from './abstract-scheduler'
|
||||||
|
|
||||||
export class PluginsCheckScheduler extends AbstractScheduler {
|
export class PluginsCheckScheduler extends AbstractScheduler {
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,7 @@ import {
|
||||||
import { setAsUpdated } from '@server/helpers/database-utils'
|
import { setAsUpdated } from '@server/helpers/database-utils'
|
||||||
import { buildUUID, uuidToShort } from '@server/helpers/uuid'
|
import { buildUUID, uuidToShort } from '@server/helpers/uuid'
|
||||||
import { MAccountId, MChannelId } from '@server/types/models'
|
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 { ActivityIconObject } from '../../../shared/models/activitypub/objects'
|
||||||
import { PlaylistObject } from '../../../shared/models/activitypub/objects/playlist-object'
|
import { PlaylistObject } from '../../../shared/models/activitypub/objects/playlist-object'
|
||||||
import { VideoPlaylistPrivacy } from '../../../shared/models/videos/playlist/video-playlist-privacy.model'
|
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)
|
return join(STATIC_PATHS.THUMBNAILS, this.Thumbnail.filename)
|
||||||
}
|
}
|
||||||
|
|
||||||
getWatchUrl () {
|
getWatchStaticPath () {
|
||||||
return WEBSERVER.URL + '/w/p/' + this.uuid
|
return buildPlaylistWatchPath({ shortUUID: uuidToShort(this.uuid) })
|
||||||
}
|
}
|
||||||
|
|
||||||
getEmbedStaticPath () {
|
getEmbedStaticPath () {
|
||||||
return '/video-playlists/embed/' + this.uuid
|
return buildPlaylistEmbedPath(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
static async getStats () {
|
static async getStats () {
|
||||||
|
|
|
@ -26,12 +26,13 @@ import {
|
||||||
} from 'sequelize-typescript'
|
} from 'sequelize-typescript'
|
||||||
import { setAsUpdated } from '@server/helpers/database-utils'
|
import { setAsUpdated } from '@server/helpers/database-utils'
|
||||||
import { buildNSFWFilter } from '@server/helpers/express-utils'
|
import { buildNSFWFilter } from '@server/helpers/express-utils'
|
||||||
|
import { shortToUUID } from '@server/helpers/uuid'
|
||||||
import { getPrivaciesForFederation, isPrivacyForFederation, isStateForFederation } from '@server/helpers/video'
|
import { getPrivaciesForFederation, isPrivacyForFederation, isStateForFederation } from '@server/helpers/video'
|
||||||
import { LiveManager } from '@server/lib/live/live-manager'
|
import { LiveManager } from '@server/lib/live/live-manager'
|
||||||
import { getHLSDirectory, getVideoFilePath } from '@server/lib/video-paths'
|
import { getHLSDirectory, getVideoFilePath } from '@server/lib/video-paths'
|
||||||
import { getServerActor } from '@server/models/application/application'
|
import { getServerActor } from '@server/models/application/application'
|
||||||
import { ModelCache } from '@server/models/model-cache'
|
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 { VideoFile } from '@shared/models/videos/video-file.model'
|
||||||
import { ResultList, UserRight, VideoPrivacy, VideoState } from '../../../shared'
|
import { ResultList, UserRight, VideoPrivacy, VideoState } from '../../../shared'
|
||||||
import { VideoObject } from '../../../shared/models/activitypub/objects'
|
import { VideoObject } from '../../../shared/models/activitypub/objects'
|
||||||
|
@ -1578,11 +1579,11 @@ export class VideoModel extends Model<Partial<AttributesOnly<VideoModel>>> {
|
||||||
}
|
}
|
||||||
|
|
||||||
getWatchStaticPath () {
|
getWatchStaticPath () {
|
||||||
return '/w/' + this.uuid
|
return buildVideoWatchPath({ shortUUID: shortToUUID(this.uuid) })
|
||||||
}
|
}
|
||||||
|
|
||||||
getEmbedStaticPath () {
|
getEmbedStaticPath () {
|
||||||
return '/videos/embed/' + this.uuid
|
return buildVideoEmbedPath(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
getMiniatureStaticPath () {
|
getMiniatureStaticPath () {
|
||||||
|
|
|
@ -43,6 +43,49 @@ function isLastWeek (d: Date) {
|
||||||
return getDaysDifferences(now, d) <= 7
|
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 {
|
export {
|
||||||
|
@ -51,7 +94,9 @@ export {
|
||||||
isThisMonth,
|
isThisMonth,
|
||||||
isToday,
|
isToday,
|
||||||
isLastMonth,
|
isLastMonth,
|
||||||
isLastWeek
|
isLastWeek,
|
||||||
|
timeToInt,
|
||||||
|
secondsToTime
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
export * from './date'
|
export * from './date'
|
||||||
export * from './miscs'
|
export * from './miscs'
|
||||||
export * from './regexp'
|
export * from './regexp'
|
||||||
|
export * from './promises'
|
||||||
export * from './types'
|
export * from './types'
|
||||||
export * from './url
|
export * from './url'
|
||||||
|
|
|
@ -20,14 +20,6 @@ function compareSemVer (a: string, b: string) {
|
||||||
return segmentsA.length - segmentsB.length
|
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') {
|
function sortObjectComparator (key: string, order: 'asc' | 'desc') {
|
||||||
return (a: any, b: any) => {
|
return (a: any, b: any) => {
|
||||||
if (a[key] < b[key]) {
|
if (a[key] < b[key]) {
|
||||||
|
@ -45,7 +37,5 @@ function sortObjectComparator (key: string, order: 'asc' | 'desc') {
|
||||||
export {
|
export {
|
||||||
randomInt,
|
randomInt,
|
||||||
compareSemVer,
|
compareSemVer,
|
||||||
isPromise,
|
|
||||||
isCatchable,
|
|
||||||
sortObjectComparator
|
sortObjectComparator
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 './abuse'
|
||||||
export * from './common'
|
export * from './common'
|
||||||
export * from './i18n'
|
export * from './i18n'
|
||||||
export * from './logs'
|
|
||||||
export * from './plugins'
|
export * from './plugins'
|
||||||
export * from './renderer'
|
export * from './renderer'
|
||||||
export * from './users'
|
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 { HookType } from '../../models/plugins/hook-type.enum'
|
||||||
import { isCatchable, isPromise } from '../miscs/miscs'
|
import { isCatchable, isPromise } from '../common/promises'
|
||||||
|
|
||||||
function getHookType (hookName: string) {
|
function getHookType (hookName: string) {
|
||||||
if (hookName.startsWith('filter:')) return HookType.FILTER
|
if (hookName.startsWith('filter:')) return HookType.FILTER
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { ChildProcess, fork } from 'child_process'
|
||||||
import { copy } from 'fs-extra'
|
import { copy } from 'fs-extra'
|
||||||
import { join } from 'path'
|
import { join } from 'path'
|
||||||
import { root } from '@server/helpers/core-utils'
|
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 { Video, VideoChannel, VideoCreateResult, VideoDetails } from '../../models/videos'
|
||||||
import { BulkCommand } from '../bulk'
|
import { BulkCommand } from '../bulk'
|
||||||
import { CLICommand } from '../cli'
|
import { CLICommand } from '../cli'
|
||||||
|
|
Loading…
Reference in New Issue