1
0
Fork 0

Add ability to share playlists in modal

This commit is contained in:
Chocobozzz 2020-08-07 13:43:48 +02:00
parent 4891e4c77b
commit 951b582f52
No known key found for this signature in database
GPG Key ID: 583A612D890159BE
13 changed files with 143 additions and 43 deletions

View File

@ -7,7 +7,7 @@ import { DropdownAction, Video, VideoService } from '@app/shared/shared-main'
import { VideoBlockService } from '@app/shared/shared-moderation'
import { I18n } from '@ngx-translate/i18n-polyfill'
import { VideoBlacklist, VideoBlacklistType } from '@shared/models'
import { buildVideoEmbed, buildVideoLink } from 'src/assets/player/utils'
import { buildVideoOrPlaylistEmbed, buildVideoLink } from 'src/assets/player/utils'
import { environment } from 'src/environments/environment'
import { DomSanitizer } from '@angular/platform-browser'
@ -176,7 +176,7 @@ export class VideoBlockListComponent extends RestTable implements OnInit, AfterV
}
getVideoEmbed (entry: VideoBlacklist) {
return buildVideoEmbed(
return buildVideoOrPlaylistEmbed(
buildVideoLink({
baseUrl: `${environment.embedUrl}/videos/embed/${entry.video.uuid}`,
title: false,

View File

@ -135,7 +135,7 @@ export class VideoCommentComponent implements OnInit, OnChanges {
this.comment.account = null
}
if (this.isUserLoggedIn() && this.authService.getUser().account.id !== this.comment.account.id) {
if (this.isUserLoggedIn() && this.comment.isDeleted === false && this.authService.getUser().account.id !== this.comment.account.id) {
this.prependModerationActions = [
{
label: this.i18n('Report comment'),

View File

@ -6,18 +6,56 @@
<div class="modal-body">
<div class="playlist" *ngIf="hasPlaylist()">
<div class="title-page title-page-single" i18n>Share the playlist</div>
<my-input-readonly-copy [value]="getPlaylistUrl()"></my-input-readonly-copy>
<div ngbNav #nav="ngbNav" class="nav-tabs" [(activeId)]="activePlaylistId">
<ng-container ngbNavItem="url">
<a ngbNavLink i18n>URL</a>
<ng-template ngbNavContent>
<div class="nav-content">
<my-input-readonly-copy [value]="getPlaylistUrl()"></my-input-readonly-copy>
</div>
</ng-template>
</ng-container>
<ng-container ngbNavItem="qrcode">
<a ngbNavLink i18n>QR-Code</a>
<ng-template ngbNavContent>
<div class="nav-content">
<qrcode [qrdata]="getPlaylistUrl()" [size]="256" level="Q"></qrcode>
</div>
</ng-template>
</ng-container>
<ng-container ngbNavItem="embed">
<a ngbNavLink i18n>Embed</a>
<ng-template ngbNavContent>
<div class="nav-content">
<my-input-readonly-copy [value]="getPlaylistIframeCode()"></my-input-readonly-copy>
<div i18n *ngIf="notSecure()" class="alert alert-warning">
The url is not secured (no HTTPS), so the embed video won't work on HTTPS websites (web browsers block non secured HTTP requests on HTTPS websites).
</div>
</div>
</ng-template>
</ng-container>
</div>
<div [ngbNavOutlet]="nav"></div>
<div class="filters">
<div class="form-group">
<my-peertube-checkbox
inputName="includeVideoInPlaylist" [(ngModel)]="includeVideoInPlaylist"
i18n-labelText labelText="Share the playlist at this video position"
></my-peertube-checkbox>
<my-peertube-checkbox inputName="includeVideoInPlaylist" [(ngModel)]="includeVideoInPlaylist" i18n-labelText
labelText="Share the playlist at this video position"></my-peertube-checkbox>
</div>
</div>
@ -27,7 +65,7 @@
<div class="video">
<div class="title-page title-page-single" *ngIf="hasPlaylist()" i18n>Share the video</div>
<div ngbNav #nav="ngbNav" class="nav-tabs" [(activeId)]="activeId">
<div ngbNav #nav="ngbNav" class="nav-tabs" [(activeId)]="activeVideoId">
<ng-container ngbNavItem="url">
<a ngbNavLink i18n>URL</a>
@ -137,7 +175,7 @@
</div>
</div>
<ng-container *ngIf="isInEmbedTab()">
<ng-container *ngIf="isVideoInEmbedTab()">
<div class="form-group">
<my-peertube-checkbox
inputName="title" [(ngModel)]="customizations.title"

View File

@ -1,5 +1,5 @@
import { Component, ElementRef, Input, ViewChild } from '@angular/core'
import { buildVideoEmbed, buildVideoLink } from '../../../../assets/player/utils'
import { buildVideoOrPlaylistEmbed, buildVideoLink, buildPlaylistLink } from '../../../../assets/player/utils'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { VideoCaption } from '@shared/models'
import { VideoDetails } from '@app/shared/shared-main'
@ -24,6 +24,8 @@ type Customizations = {
peertubeLink: boolean
}
type TabId = 'url' | 'qrcode' | 'embed'
@Component({
selector: 'my-video-share',
templateUrl: './video-share.component.html',
@ -36,14 +38,18 @@ export class VideoShareComponent {
@Input() videoCaptions: VideoCaption[] = []
@Input() playlist: VideoPlaylist = null
activeId: 'url' | 'qrcode' | 'embed' = 'url'
activeVideoId: TabId = 'url'
activePlaylistId: TabId = 'url'
customizations: Customizations
isAdvancedCustomizationCollapsed = true
includeVideoInPlaylist = false
private playlistPosition: number = null
constructor (private modalService: NgbModal) { }
show (currentVideoTimestamp?: number) {
show (currentVideoTimestamp?: number, currentPlaylistPosition?: number) {
let subtitle: string
if (this.videoCaptions.length !== 0) {
subtitle = this.videoCaptions[0].language.id
@ -70,19 +76,28 @@ export class VideoShareComponent {
peertubeLink: true
}
this.playlistPosition = currentPlaylistPosition
this.modalService.open(this.modal, { centered: true })
}
getVideoIframeCode () {
const options = this.getOptions(this.video.embedUrl)
const options = this.getVideoOptions(this.video.embedUrl)
const embedUrl = buildVideoLink(options)
return buildVideoEmbed(embedUrl)
return buildVideoOrPlaylistEmbed(embedUrl)
}
getPlaylistIframeCode () {
const options = this.getPlaylistOptions(this.playlist.embedUrl)
const embedUrl = buildPlaylistLink(options)
return buildVideoOrPlaylistEmbed(embedUrl)
}
getVideoUrl () {
const baseUrl = window.location.origin + '/videos/watch/' + this.video.uuid
const options = this.getOptions(baseUrl)
const options = this.getVideoOptions(baseUrl)
return buildVideoLink(options)
}
@ -99,15 +114,23 @@ export class VideoShareComponent {
return window.location.protocol === 'http:'
}
isInEmbedTab () {
return this.activeId === 'embed'
isVideoInEmbedTab () {
return this.activeVideoId === 'embed'
}
hasPlaylist () {
return !!this.playlist
}
private getOptions (baseUrl?: string) {
private getPlaylistOptions (baseUrl?: string) {
return {
baseUrl,
playlistPosition: this.playlistPosition || undefined
}
}
private getVideoOptions (baseUrl?: string) {
return {
baseUrl,

View File

@ -244,7 +244,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
showShareModal () {
this.pausePlayer()
this.videoShareModal.show(this.currentTime)
this.videoShareModal.show(this.currentTime, this.videoWatchPlaylist.currentPlaylistPosition)
}
isUserLoggedIn () {

View File

@ -1,7 +1,7 @@
import * as debug from 'debug'
import truncate from 'lodash-es/truncate'
import { SortMeta } from 'primeng/api'
import { buildVideoEmbed, buildVideoLink } from 'src/assets/player/utils'
import { buildVideoOrPlaylistEmbed, buildVideoLink } from 'src/assets/player/utils'
import { environment } from 'src/environments/environment'
import { AfterViewInit, Component, OnInit, ViewChild, Input } from '@angular/core'
import { DomSanitizer } from '@angular/platform-browser'
@ -141,7 +141,7 @@ export class AbuseListTableComponent extends RestTable implements OnInit, AfterV
}
getVideoEmbed (abuse: AdminAbuse) {
return buildVideoEmbed(
return buildVideoOrPlaylistEmbed(
buildVideoLink({
baseUrl: `${environment.embedUrl}/videos/embed/${abuse.video.uuid}`,
title: false,

View File

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

View File

@ -1,4 +1,4 @@
import { getAbsoluteAPIUrl } from '@app/helpers'
import { getAbsoluteAPIUrl, getAbsoluteEmbedUrl } from '@app/helpers'
import { Actor } from '@app/shared/shared-main'
import { peertubeTranslate } from '@shared/core-utils/i18n'
import {
@ -33,6 +33,9 @@ export class VideoPlaylist implements ServerVideoPlaylist {
thumbnailUrl: string
embedPath: string
embedUrl: string
ownerBy: string
ownerAvatarUrl: string
@ -63,6 +66,9 @@ export class VideoPlaylist implements ServerVideoPlaylist {
this.thumbnailUrl = window.location.origin + '/client/assets/images/default-playlist.jpg'
}
this.embedPath = hash.embedPath
this.embedUrl = getAbsoluteEmbedUrl() + hash.embedPath
this.videosLength = hash.videosLength
this.type = hash.type

View File

@ -35,7 +35,7 @@ import {
VideoJSPluginOptions
} from './peertube-videojs-typings'
import { TranslationsManager } from './translations-manager'
import { buildVideoEmbed, buildVideoLink, copyToClipboard, getRtcConfig, isIOS, isSafari } from './utils'
import { buildVideoOrPlaylistEmbed, buildVideoLink, copyToClipboard, getRtcConfig, isIOS, isSafari } from './utils'
// Change 'Playback Rate' to 'Speed' (smaller for our settings menu)
(videojs.getComponent('PlaybackRateMenuButton') as any).prototype.controlText_ = 'Speed'
@ -492,7 +492,7 @@ export class PeertubePlayerManager {
{
label: player.localize('Copy embed code'),
listener: () => {
copyToClipboard(buildVideoEmbed(videoEmbedUrl))
copyToClipboard(buildVideoOrPlaylistEmbed(videoEmbedUrl))
}
}
]

View File

@ -43,20 +43,20 @@ function isMobile () {
}
function buildVideoLink (options: {
baseUrl?: string,
baseUrl?: string
startTime?: number,
stopTime?: number,
startTime?: number
stopTime?: number
subtitle?: string,
subtitle?: string
loop?: boolean,
autoplay?: boolean,
muted?: boolean,
loop?: boolean
autoplay?: boolean
muted?: boolean
// Embed options
title?: boolean,
warningTitle?: boolean,
title?: boolean
warningTitle?: boolean
controls?: boolean
peertubeLink?: boolean
} = {}) {
@ -66,10 +66,7 @@ function buildVideoLink (options: {
? baseUrl
: window.location.origin + window.location.pathname.replace('/embed/', '/watch/')
const params = new URLSearchParams(window.location.search)
// Remove these unused parameters when we are on a playlist page
params.delete('videoId')
params.delete('resume')
const params = generateParams(window.location.search)
if (options.startTime) {
const startTimeInt = Math.floor(options.startTime)
@ -91,6 +88,28 @@ function buildVideoLink (options: {
if (options.controls === false) params.set('controls', '0')
if (options.peertubeLink === false) params.set('peertubeLink', '0')
return buildUrl(url, params)
}
function buildPlaylistLink (options: {
baseUrl?: string
playlistPosition: number
}) {
const { baseUrl } = options
const url = baseUrl
? baseUrl
: window.location.origin + window.location.pathname.replace('/video-playlists/embed/', '/videos/watch/playlist/')
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)
@ -99,6 +118,15 @@ function buildVideoLink (options: {
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
@ -140,7 +168,7 @@ function secondsToTime (seconds: number, full = false, symbol?: string) {
return time
}
function buildVideoEmbed (embedUrl: string) {
function buildVideoOrPlaylistEmbed (embedUrl: string) {
return '<iframe width="560" height="315" ' +
'sandbox="allow-same-origin allow-scripts allow-popups" ' +
'src="' + embedUrl + '" ' +
@ -203,8 +231,9 @@ export {
timeToInt,
secondsToTime,
isWebRTCDisabled,
buildPlaylistLink,
buildVideoLink,
buildVideoEmbed,
buildVideoOrPlaylistEmbed,
videoFileMaxByResolution,
videoFileMinByResolution,
copyToClipboard,

View File

@ -528,6 +528,7 @@ export class VideoPlaylistModel extends Model<VideoPlaylistModel> {
},
thumbnailPath: this.getThumbnailStaticPath(),
embedPath: this.getEmbedStaticPath(),
type: {
id: this.type,

View File

@ -236,7 +236,7 @@ describe('Test video playlists', function () {
const playlistFromList = res.body.data[0] as VideoPlaylist
const res2 = await getVideoPlaylist(server.url, playlistFromList.uuid)
const playlistFromGet = res2.body
const playlistFromGet = res2.body as VideoPlaylist
for (const playlist of [ playlistFromGet, playlistFromList ]) {
expect(playlist.id).to.be.a('number')
@ -250,6 +250,7 @@ describe('Test video playlists', function () {
expect(playlist.privacy.label).to.equal('Public')
expect(playlist.type.id).to.equal(VideoPlaylistType.REGULAR)
expect(playlist.type.label).to.equal('Regular')
expect(playlist.embedPath).to.equal('/video-playlists/embed/' + playlist.uuid)
expect(playlist.videosLength).to.equal(0)

View File

@ -18,6 +18,8 @@ export interface VideoPlaylist {
type: VideoConstant<VideoPlaylistType>
embedPath: string
createdAt: Date | string
updatedAt: Date | string