Refactor video links building

pull/4283/head
Chocobozzz 2021-07-26 14:12:39 +02:00
parent 764b1a14fc
commit 9162fdd363
No known key found for this signature in database
GPG Key ID: 583A612D890159BE
22 changed files with 136 additions and 67 deletions

View File

@ -1,6 +1,6 @@
import { SortMeta } from 'primeng/api'
import { switchMap } from 'rxjs/operators'
import { buildVideoLink, buildVideoOrPlaylistEmbed } from 'src/assets/player/utils'
import { buildVideoEmbedLink, buildVideoOrPlaylistEmbed, decorateVideoLink } from 'src/assets/player/utils'
import { environment } from 'src/environments/environment'
import { Component, OnInit } from '@angular/core'
import { DomSanitizer } from '@angular/platform-browser'
@ -147,8 +147,9 @@ export class VideoBlockListComponent extends RestTable implements OnInit {
getVideoEmbed (entry: VideoBlacklist) {
return buildVideoOrPlaylistEmbed(
buildVideoLink({
baseUrl: `${environment.originServerUrl}/videos/embed/${entry.video.uuid}`,
decorateVideoLink({
url: buildVideoEmbedLink(entry.video, environment.originServerUrl),
title: false,
warningTitle: false
}),

View File

@ -45,7 +45,7 @@
</ng-template>
</my-help>
<my-markdown-textarea [truncate]="250" formControlName="description" [markdownVideo]="true"></my-markdown-textarea>
<my-markdown-textarea [truncate]="250" formControlName="description" [markdownVideo]="videoToUpdate"></my-markdown-textarea>
<div *ngIf="formErrors.description" class="form-error">
{{ formErrors.description }}

View File

@ -161,7 +161,7 @@ export class VideoCommentComponent implements OnInit, OnChanges {
// Before HTML rendering restore line feed for markdown list compatibility
const commentText = this.comment.text.replace(/<br.?\/?>/g, '\r\n')
const html = await this.markdownService.textMarkdownToHTML(commentText, true, true)
this.sanitizedCommentHTML = this.markdownService.processVideoTimestamps(html)
this.sanitizedCommentHTML = this.markdownService.processVideoTimestamps(this.video.shortUUID, html)
this.newParentComments = this.parentComments.concat([ this.comment ])
if (this.comment.account) {

View File

@ -80,6 +80,7 @@ export class VideoDescriptionComponent implements OnChanges {
private async setVideoDescriptionHTML () {
const html = await this.markdownService.textMarkdownToHTML(this.video.description)
this.videoHTMLDescription = this.markdownService.processVideoTimestamps(html)
this.videoHTMLDescription = this.markdownService.processVideoTimestamps(this.video.shortUUID, html)
}
}

View File

@ -582,6 +582,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
videoCaptions: playerCaptions,
videoShortUUID: video.shortUUID,
videoUUID: video.uuid
},

View File

@ -1,5 +1,5 @@
import * as MarkdownIt from 'markdown-it'
import { buildVideoLink } from 'src/assets/player/utils'
import { buildVideoLink, decorateVideoLink } from 'src/assets/player/utils'
import { Injectable } from '@angular/core'
import {
COMPLETE_RULES,
@ -82,10 +82,14 @@ export class MarkdownService {
return this.render({ name: 'customPageMarkdownIt', markdown, withEmoji: true, additionalAllowedTags })
}
processVideoTimestamps (html: string) {
processVideoTimestamps (videoShortUUID: string, html: string) {
return html.replace(/((\d{1,2}):)?(\d{1,2}):(\d{1,2})/g, function (str, _, h, m, s) {
const t = (3600 * +(h || 0)) + (60 * +(m || 0)) + (+(s || 0))
const url = buildVideoLink({ startTime: t })
const url = decorateVideoLink({
url: buildVideoLink({ shortUUID: videoShortUUID }),
startTime: t
})
return `<a class="video-timestamp" href="${url}">${str}</a>`
})
}

View File

@ -1,7 +1,7 @@
import * as debug from 'debug'
import truncate from 'lodash-es/truncate'
import { SortMeta } from 'primeng/api'
import { buildVideoLink, buildVideoOrPlaylistEmbed } from 'src/assets/player/utils'
import { buildVideoEmbedLink, buildVideoOrPlaylistEmbed, decorateVideoLink } 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'
@ -129,8 +129,8 @@ export class AbuseListTableComponent extends RestTable implements OnInit {
getVideoEmbed (abuse: AdminAbuse) {
return buildVideoOrPlaylistEmbed(
buildVideoLink({
baseUrl: `${environment.originServerUrl}/videos/embed/${abuse.video.uuid}`,
decorateVideoLink({
url: buildVideoEmbedLink(abuse.video, environment.originServerUrl),
title: false,
warningTitle: false,
startTime: abuse.video.startAt,

View File

@ -1,4 +1,4 @@
import { buildPlaylistLink, buildVideoLink, buildVideoOrPlaylistEmbed } from 'src/assets/player/utils'
import { buildPlaylistEmbedLink, buildVideoEmbedLink, buildVideoOrPlaylistEmbed } from 'src/assets/player/utils'
import { environment } from 'src/environments/environment'
import { Component, ElementRef, Input, OnInit } from '@angular/core'
import { CustomMarkupComponent } from './shared'
@ -17,8 +17,8 @@ export class EmbedMarkupComponent implements CustomMarkupComponent, OnInit {
ngOnInit () {
const link = this.type === 'video'
? buildVideoLink({ baseUrl: `${environment.originServerUrl}/videos/embed/${this.uuid}` })
: buildPlaylistLink({ baseUrl: `${environment.originServerUrl}/video-playlists/embed/${this.uuid}` })
? buildVideoEmbedLink({ uuid: this.uuid }, environment.originServerUrl)
: buildPlaylistEmbedLink({ uuid: this.uuid }, environment.originServerUrl)
this.el.nativeElement.innerHTML = buildVideoOrPlaylistEmbed(link, this.uuid)
}

View File

@ -6,6 +6,7 @@ import { Component, ElementRef, forwardRef, Input, OnInit, ViewChild } from '@an
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'
import { SafeHtml } from '@angular/platform-browser'
import { MarkdownService, ScreenService } from '@app/core'
import { Video } from '@shared/models'
@Component({
selector: 'my-markdown-textarea',
@ -33,7 +34,7 @@ export class MarkdownTextareaComponent implements ControlValueAccessor, OnInit {
@Input() markdownType: 'text' | 'enhanced' = 'text'
@Input() customMarkdownRenderer?: (text: string) => Promise<string | HTMLElement>
@Input() markdownVideo = false
@Input() markdownVideo: Video
@Input() name = 'description'
@ -147,7 +148,7 @@ export class MarkdownTextareaComponent implements ControlValueAccessor, OnInit {
}
if (this.markdownVideo) {
html = this.markdownService.processVideoTimestamps(html)
html = this.markdownService.processVideoTimestamps(this.markdownVideo.shortUUID, html)
}
return html

View File

@ -1,5 +1,5 @@
import { mapValues, pickBy } from 'lodash-es'
import { buildVideoLink, buildVideoOrPlaylistEmbed } from 'src/assets/player/utils'
import { buildVideoOrPlaylistEmbed, decorateVideoLink } 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'
@ -57,11 +57,12 @@ export class VideoReportComponent extends FormReactive implements OnInit {
getVideoEmbed () {
return this.sanitizer.bypassSecurityTrustHtml(
buildVideoOrPlaylistEmbed(
buildVideoLink({
baseUrl: this.video.embedUrl,
decorateVideoLink({
url: this.video.embedUrl,
title: false,
warningTitle: false
}),
this.video.name
)
)

View File

@ -1,9 +1,15 @@
import { Component, ElementRef, Input, ViewChild } from '@angular/core'
import { Video, VideoDetails } from '@app/shared/shared-main'
import { VideoDetails } from '@app/shared/shared-main'
import { VideoPlaylist } from '@app/shared/shared-video-playlist'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { VideoCaption } from '@shared/models'
import { buildPlaylistLink, buildVideoLink, buildVideoOrPlaylistEmbed } from '../../../assets/player/utils'
import {
buildPlaylistLink,
buildVideoLink,
buildVideoOrPlaylistEmbed,
decoratePlaylistLink,
decorateVideoLink
} from '../../../assets/player/utils'
type Customizations = {
startAtCheckbox: boolean
@ -83,34 +89,34 @@ export class VideoShareComponent {
}
getVideoIframeCode () {
const options = this.getVideoOptions(this.video.embedUrl)
const embedUrl = decorateVideoLink({ url: this.video.embedUrl, ...this.getVideoOptions() })
const embedUrl = buildVideoLink(options)
return buildVideoOrPlaylistEmbed(embedUrl, this.video.name)
}
getPlaylistIframeCode () {
const options = this.getPlaylistOptions(this.playlist.embedUrl)
const embedUrl = decoratePlaylistLink({ url: this.playlist.embedUrl, ...this.getPlaylistOptions() })
const embedUrl = buildPlaylistLink(options)
return buildVideoOrPlaylistEmbed(embedUrl, this.playlist.displayName)
}
getVideoUrl () {
let baseUrl = this.customizations.originUrl ? this.video.originInstanceUrl : window.location.origin
baseUrl += Video.buildWatchUrl(this.video)
const baseUrl = this.customizations.originUrl
? this.video.originInstanceUrl
: window.location.origin
const options = this.getVideoOptions(baseUrl)
return decorateVideoLink({
url: buildVideoLink(this.video, baseUrl),
return buildVideoLink(options)
...this.getVideoOptions()
})
}
getPlaylistUrl () {
const base = window.location.origin + VideoPlaylist.buildWatchUrl(this.playlist)
const url = buildPlaylistLink(this.playlist)
if (!this.includeVideoInPlaylist) return url
if (!this.includeVideoInPlaylist) return base
return base + '?playlistPosition=' + this.playlistPosition
return decoratePlaylistLink({ url, playlistPosition: this.playlistPosition })
}
notSecure () {
@ -133,10 +139,8 @@ export class VideoShareComponent {
}
}
private getVideoOptions (baseUrl?: string) {
private getVideoOptions () {
return {
baseUrl,
startTime: this.customizations.startAtCheckbox ? this.customizations.startAt : undefined,
stopTime: this.customizations.stopAtCheckbox ? this.customizations.stopAt : undefined,

View File

@ -33,13 +33,14 @@ import { getStoredP2PEnabled } from './peertube-player-local-storage'
import {
NextPreviousVideoButtonOptions,
P2PMediaLoaderPluginOptions,
PeerTubeLinkButtonOptions,
PlaylistPluginOptions,
UserWatching,
VideoJSCaption,
VideoJSPluginOptions
} from './peertube-videojs-typings'
import { TranslationsManager } from './translations-manager'
import { buildVideoLink, buildVideoOrPlaylistEmbed, getRtcConfig, isIOS, isSafari } from './utils'
import { buildVideoLink, buildVideoOrPlaylistEmbed, decorateVideoLink, getRtcConfig, isIOS, isSafari } from './utils'
// Change 'Playback Rate' to 'Speed' (smaller for our settings menu)
(videojs.getComponent('PlaybackRateMenuButton') as any).prototype.controlText_ = 'Speed'
@ -110,6 +111,7 @@ export interface CommonOptions extends CustomizationOptions {
videoCaptions: VideoJSCaption[]
videoUUID: string
videoShortUUID: string
userWatching?: UserWatching
@ -175,7 +177,13 @@ export class PeertubePlayerManager {
PeertubePlayerManager.alreadyPlayed = true
})
self.addContextMenu(mode, player, options.common.embedUrl, options.common.embedTitle)
self.addContextMenu({
mode,
player,
videoShortUUID: options.common.videoShortUUID,
videoEmbedUrl: options.common.embedUrl,
videoEmbedTitle: options.common.embedTitle
})
player.bezels()
player.stats({
@ -218,7 +226,13 @@ export class PeertubePlayerManager {
videojs(newVideoElement, videojsOptions, function (this: videojs.Player) {
const player = this
self.addContextMenu(mode, player, options.common.embedUrl, options.common.embedTitle)
self.addContextMenu({
mode,
player,
videoShortUUID: options.common.videoShortUUID,
videoEmbedUrl: options.common.embedUrl,
videoEmbedTitle: options.common.embedTitle
})
PeertubePlayerManager.onPlayerChange(player)
})
@ -295,6 +309,8 @@ export class PeertubePlayerManager {
controlBar: {
children: this.getControlBarChildren(mode, {
videoShortUUID: commonOptions.videoShortUUID,
captions: commonOptions.captions,
peertubeLink: commonOptions.peertubeLink,
theaterButton: commonOptions.theaterButton,
@ -409,6 +425,8 @@ export class PeertubePlayerManager {
}
private static getControlBarChildren (mode: PlayerMode, options: {
videoShortUUID: string
peertubeLink: boolean
theaterButton: boolean
captions: boolean
@ -497,7 +515,7 @@ export class PeertubePlayerManager {
if (options.peertubeLink === true) {
Object.assign(children, {
'peerTubeLinkButton': {}
'peerTubeLinkButton': { shortUUID: options.videoShortUUID } as PeerTubeLinkButtonOptions
})
}
@ -514,7 +532,15 @@ export class PeertubePlayerManager {
return children
}
private static addContextMenu (mode: PlayerMode, player: videojs.Player, videoEmbedUrl: string, videoEmbedTitle: string) {
private static addContextMenu (options: {
mode: PlayerMode
player: videojs.Player
videoShortUUID: string
videoEmbedUrl: string
videoEmbedTitle: string
}) {
const { mode, player, videoEmbedTitle, videoEmbedUrl, videoShortUUID } = options
const content = () => {
const isLoopEnabled = player.options_['loop']
const items = [
@ -528,13 +554,15 @@ export class PeertubePlayerManager {
{
label: player.localize('Copy the video URL'),
listener: function () {
copyToClipboard(buildVideoLink())
copyToClipboard(buildVideoLink({ shortUUID: videoShortUUID }))
}
},
{
label: player.localize('Copy the video URL at the current time'),
listener: function (this: videojs.Player) {
copyToClipboard(buildVideoLink({ startTime: this.currentTime() }))
const url = buildVideoLink({ shortUUID: videoShortUUID })
copyToClipboard(decorateVideoLink({ url, startTime: this.currentTime() }))
}
},
{

View File

@ -132,6 +132,10 @@ type NextPreviousVideoButtonOptions = {
isDisabled: () => boolean
}
type PeerTubeLinkButtonOptions = {
shortUUID: string
}
type WebtorrentPluginOptions = {
playerElement: HTMLVideoElement
@ -225,5 +229,6 @@ export {
VideoJSPluginOptions,
LoadedQualityData,
QualityLevelRepresentation,
PeerTubeLinkButtonOptions,
QualityLevels
}

View File

@ -1,4 +1,4 @@
import { VideoFile } from '@shared/models'
import { Video, VideoFile, VideoPlaylist } from '@shared/models'
import { escapeHTML } from '@shared/core-utils/renderer'
function toTitleCase (str: string) {
@ -43,8 +43,24 @@ function isMobile () {
return /iPhone|iPad|iPod|Android/i.test(navigator.userAgent)
}
function buildVideoLink (options: {
baseUrl?: string
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
@ -60,12 +76,8 @@ function buildVideoLink (options: {
warningTitle?: boolean
controls?: boolean
peertubeLink?: boolean
} = {}) {
const { baseUrl } = options
const url = baseUrl
? baseUrl
: window.location.origin + window.location.pathname.replace('/embed/', '/w/')
}) {
const { url } = options
const params = generateParams(window.location.search)
@ -92,16 +104,12 @@ function buildVideoLink (options: {
return buildUrl(url, params)
}
function buildPlaylistLink (options: {
baseUrl?: string
function decoratePlaylistLink (options: {
url: string
playlistPosition?: number
}) {
const { baseUrl } = options
const url = baseUrl
? baseUrl
: window.location.origin + window.location.pathname.replace('/video-playlists/embed/', '/w/p/')
const { url } = options
const params = generateParams(window.location.search)
@ -224,8 +232,14 @@ export {
timeToInt,
secondsToTime,
isWebRTCDisabled,
buildPlaylistLink,
buildVideoLink,
decorateVideoLink,
decoratePlaylistLink,
buildPlaylistEmbedLink,
buildVideoEmbedLink,
buildVideoOrPlaylistEmbed,
videoFileMaxByResolution,
videoFileMinByResolution,

View File

@ -1,11 +1,13 @@
import { buildVideoLink } from '../utils'
import videojs from 'video.js'
import { PeerTubeLinkButtonOptions } from '../peertube-videojs-typings'
import { buildVideoLink, decorateVideoLink } from '../utils'
const Button = videojs.getComponent('Button')
class PeerTubeLinkButton extends Button {
private shortUUID: string
constructor (player: videojs.Player, options?: videojs.ComponentOptions) {
super(player, options)
constructor (player: videojs.Player, options?: PeerTubeLinkButtonOptions) {
super(player, options as any)
}
createEl () {
@ -13,7 +15,7 @@ class PeerTubeLinkButton extends Button {
}
updateHref () {
this.el().setAttribute('href', buildVideoLink({ startTime: this.player().currentTime() }))
this.el().setAttribute('href', this.buildLink())
}
handleClick () {
@ -22,7 +24,7 @@ class PeerTubeLinkButton extends Button {
private buildElement () {
const el = videojs.dom.createEl('a', {
href: buildVideoLink(),
href: this.buildLink(),
innerHTML: 'PeerTube',
title: this.player().localize('Video page (new window)'),
className: 'vjs-peertube-link',
@ -33,6 +35,12 @@ class PeerTubeLinkButton extends Button {
return el as HTMLButtonElement
}
private buildLink () {
const url = buildVideoLink({ shortUUID: this.shortUUID })
return decorateVideoLink({ url, startTime: this.player().currentTime() })
}
}
videojs.registerComponent('PeerTubeLinkButton', PeerTubeLinkButton)

View File

@ -1,4 +1,5 @@
export * from './date'
export * from './miscs'
export * from './types'
export * from './regexp'
export * from './types'
export * from './url

View File

View File

@ -1,7 +1,7 @@
export * from './abuse'
export * from './common'
export * from './i18n'
export * from './logs'
export * from './miscs'
export * from './plugins'
export * from './renderer'
export * from './users'