v6.3.2
-----BEGIN PGP SIGNATURE----- iQEzBAABCAAdFiEExEqtY4NnkSypPt1XWDphLYkBWb4FAmcEviwACgkQWDphLYkB Wb6V9QgAgtGTp9zRJREaA769WZP6P09ytaIxuI3SnbVQ2rCLsdpdALFWgZ6ZvteH XQ2cEuykr9BZ+cioVmt5PCrU3nStzYG5P6WziubtEIdJemh0bv6E3En0u5ddau1r UvVedZPkVx5E7tTsl++CvlLBwnWt1OMPRYCK0/6sX5LgGFrHd/tKfrXUbf0kwH5E +Tha+eVINf3Ks4OEU66igqotQA5P7CHI6ls9fCkZM5hRm7bdA0RmCbRHviuOyXlW xAUboQhndo8BHtVoBIMvXJYhbaJ8UCwAK4E8YXSvO65h+pbrkMDG7BjLMX11GcKN BQmp2wA0VupPHvSTZlT9/eQc6w+d7Q== =+ma2 -----END PGP SIGNATURE----- gpgsig -----BEGIN PGP SIGNATURE----- iQJLBAABCAA1FiEEGCBYi9NGimfngx9dVTwOu+tdXwgFAmcNTDcXHGdpdEBrb3Rv dmFsZXhhcmlhbi5jb20ACgkQVTwOu+tdXwgevA/+My9McwmndIHdmLMjKTush7lT LlDIAZtSMX+Om+i0KzRvyE9VXI5kN8QzBolUH8vgBgk4eVKoLUaTPJKFIp4SzQrX UzUxXTA1iSPA9Tjnj/akcrLFpIBtjQUOix0h5itMo1+g/KO2JAgeU0ATrjfK+hH0 nDWDVWjYd4qzLzPSRqUJfUxfwU6FL+KehwW8M2URLP65xjh+CK8gKnxbuJESfaIC 8AJj0rpWm1QvMNKwVxyR2K3eTJ5o7kSiM+DE6GQ30Q5ph+LnZz1J0PxH4yc7c5RU 1DAOxrFmnVcimZ6dZceE0tozs6SMfqXeNm49qoS2aAvUBWgJs0tafB4pmwv2/p0x N39XduMfeeziRv7Ak5LTckqmSUSrF+BkZaI9+GyQxgO6plzRDfyiI3B1NXbqylUk o8ZR3rsxzeGFWfpHYdcoN7a3PT99vZ3E04eOPEYrX6J5/y4+EYp+/eZOr/IYuH8v pYudS5KrYZoPwpGKB4Ndy0+E3Q49Iz7HaMXB9FkqxLSWBwdar4Tzm/gdt37pGUj8 2vCtO/dRxFdsmIm2LRsVMprn4MYaa7FQwB9y3zq3NIj7yV4BnNAdsYszf1XszZql U0dbBrkxE/9epdBIhPuBEo4KAUX284JClbX1iKecdqPNi9Xl4yVIgffztDqBb0r2 i7MuxFhAJbRM/wDqL+U= =t78K -----END PGP SIGNATURE----- Merge tag 'v6.3.2' into changes v6.3.2
This commit is contained in:
commit
d36c84e880
17 changed files with 110 additions and 30 deletions
13
CHANGELOG.md
13
CHANGELOG.md
|
@ -1,5 +1,18 @@
|
|||
# Changelog
|
||||
|
||||
## v6.3.2
|
||||
|
||||
### Bug fixes
|
||||
|
||||
* Fix 403 error when downloading private/internal video
|
||||
* Don't crash video federation and live replay generation on missing thumbnail/preview
|
||||
* Fix advanced search input with multiple automatic search tokens
|
||||
* Fix player "Copy URL" when the video is fullscreen
|
||||
* Fix account videos search
|
||||
* Add missing max transcoding fps config in admin
|
||||
* Don't add mobile buttons if the player controls are disabled
|
||||
|
||||
|
||||
## v6.3.1
|
||||
|
||||
### IMPORTANT NOTES
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "peertube-client",
|
||||
"version": "6.3.1",
|
||||
"version": "6.3.2",
|
||||
"private": true,
|
||||
"license": "AGPL-3.0",
|
||||
"author": {
|
||||
|
|
|
@ -22,6 +22,7 @@ import {
|
|||
SERVICES_TWITTER_USERNAME_VALIDATOR,
|
||||
SIGNUP_LIMIT_VALIDATOR,
|
||||
SIGNUP_MINIMUM_AGE_VALIDATOR,
|
||||
TRANSCODING_MAX_FPS_VALIDATOR,
|
||||
TRANSCODING_THREADS_VALIDATOR
|
||||
} from '@app/shared/form-validators/custom-config-validators'
|
||||
import { USER_VIDEO_QUOTA_DAILY_VALIDATOR, USER_VIDEO_QUOTA_VALIDATOR } from '@app/shared/form-validators/user-validators'
|
||||
|
@ -30,6 +31,7 @@ import { FormReactiveService } from '@app/shared/shared-forms/form-reactive.serv
|
|||
import { CustomPageService } from '@app/shared/shared-main/custom-page/custom-page.service'
|
||||
import { NgbNav, NgbNavContent, NgbNavItem, NgbNavLink, NgbNavLinkBase, NgbNavOutlet } from '@ng-bootstrap/ng-bootstrap'
|
||||
import { CustomConfig, CustomPage, HTMLServerConfig } from '@peertube/peertube-models'
|
||||
import merge from 'lodash-es/merge'
|
||||
import omit from 'lodash-es/omit'
|
||||
import { forkJoin } from 'rxjs'
|
||||
import { SelectOptionsItem } from 'src/types/select-options-item.model'
|
||||
|
@ -40,7 +42,6 @@ import { EditHomepageComponent } from './edit-homepage.component'
|
|||
import { EditInstanceInformationComponent } from './edit-instance-information.component'
|
||||
import { EditLiveConfigurationComponent } from './edit-live-configuration.component'
|
||||
import { EditVODTranscodingComponent } from './edit-vod-transcoding.component'
|
||||
import merge from 'lodash-es/merge'
|
||||
|
||||
type ComponentCustomConfig = CustomConfig & {
|
||||
instanceCustomHomepage: CustomPage
|
||||
|
@ -239,6 +240,9 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit {
|
|||
},
|
||||
remoteRunners: {
|
||||
enabled: null
|
||||
},
|
||||
fps: {
|
||||
max: TRANSCODING_MAX_FPS_VALIDATOR
|
||||
}
|
||||
},
|
||||
live: {
|
||||
|
@ -260,6 +264,9 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit {
|
|||
alwaysTranscodeOriginalResolution: null,
|
||||
remoteRunners: {
|
||||
enabled: null
|
||||
},
|
||||
fps: {
|
||||
max: TRANSCODING_MAX_FPS_VALIDATOR
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -48,7 +48,7 @@
|
|||
<div class="form-group" [ngClass]="getDisabledLiveClass()">
|
||||
<label i18n for="liveMaxInstanceLives">Max simultaneous lives created on your instance</label>
|
||||
|
||||
<span class="ms-2 small muted">(-1 for "unlimited")</span>
|
||||
<span i18n class="ms-2 small muted">(-1 for "unlimited")</span>
|
||||
|
||||
<div class="number-with-unit">
|
||||
<input type="number" name="liveMaxInstanceLives" formControlName="maxInstanceLives" />
|
||||
|
@ -60,7 +60,7 @@
|
|||
|
||||
<div class="form-group" [ngClass]="getDisabledLiveClass()">
|
||||
<label i18n for="liveMaxUserLives">Max simultaneous lives created per user</label>
|
||||
<span class="ms-2 small muted">(-1 for "unlimited")</span>
|
||||
<span i18n class="ms-2 small muted">(-1 for "unlimited")</span>
|
||||
|
||||
<div class="number-with-unit">
|
||||
<input type="number" name="liveMaxUserLives" formControlName="maxUserLives" />
|
||||
|
@ -116,6 +116,19 @@
|
|||
|
||||
<div [ngClass]="getDisabledLiveTranscodingClass()">
|
||||
|
||||
<div class="form-group" formGroupName="fps">
|
||||
<label i18n for="liveTranscodingFPSMax">Max live FPS</label>
|
||||
|
||||
<span i18n class="ms-2 small muted">Cap transcoded live FPS. Max resolution stream still keeps the original FPS.</span>
|
||||
|
||||
<div class="number-with-unit">
|
||||
<input type="number" name="liveTranscodingFPSMax" formControlName="max" />
|
||||
<span>FPS</span>
|
||||
</div>
|
||||
|
||||
<div *ngIf="formErrors.live.transcoding.fps.max" class="form-error" role="alert">{{ formErrors.live.transcoding.fps.max }}</div>
|
||||
</div>
|
||||
|
||||
<div class="ms-2 mt-3">
|
||||
<h4 i18n>Live resolutions to generate</h4>
|
||||
|
||||
|
|
|
@ -141,6 +141,19 @@
|
|||
</div>
|
||||
</ng-container>
|
||||
|
||||
<div class="form-group" formGroupName="fps" [ngClass]="getTranscodingDisabledClass()">
|
||||
<label i18n for="transcodingFPSMax">Max video FPS</label>
|
||||
|
||||
<span i18n class="ms-2 small muted">Cap transcoded video FPS. Max resolution file still keeps the original FPS.</span>
|
||||
|
||||
<div class="number-with-unit">
|
||||
<input type="number" name="transcodingFPSMax" formControlName="max" />
|
||||
<span>FPS</span>
|
||||
</div>
|
||||
|
||||
<div *ngIf="formErrors.transcoding.fps.max" class="form-error" role="alert">{{ formErrors.transcoding.fps.max }}</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group" [ngClass]="getTranscodingDisabledClass()">
|
||||
<div class="mb-2 fw-bold" i18n>Resolutions to generate</div>
|
||||
|
||||
|
|
|
@ -65,6 +65,14 @@ export const TRANSCODING_THREADS_VALIDATOR: BuildFormValidator = {
|
|||
}
|
||||
}
|
||||
|
||||
export const TRANSCODING_MAX_FPS_VALIDATOR: BuildFormValidator = {
|
||||
VALIDATORS: [ Validators.required, Validators.min(1) ],
|
||||
MESSAGES: {
|
||||
required: $localize`Transcoding max FPS is required.`,
|
||||
min: $localize`Transcoding max FPS must be greater or equal to 1.`
|
||||
}
|
||||
}
|
||||
|
||||
export const MAX_LIVE_DURATION_VALIDATOR: BuildFormValidator = {
|
||||
VALIDATORS: [ Validators.required, Validators.min(-1) ],
|
||||
MESSAGES: {
|
||||
|
|
|
@ -155,25 +155,30 @@ export class AdvancedInputFilterComponent implements OnInit, AfterViewInit {
|
|||
}
|
||||
|
||||
private addFilterToSearch (search: string, newFilter: AdvancedInputFilterChild) {
|
||||
const prefix = newFilter.value.split(':').shift()
|
||||
const filterTokens = this.restService.tokenizeString(newFilter.value)
|
||||
let searchTokens = this.restService.tokenizeString(search)
|
||||
|
||||
// Tokenize search and remove a potential existing filter
|
||||
const tokens = this.restService.tokenizeString(search)
|
||||
.filter(t => !t.startsWith(prefix))
|
||||
for (const filterToken of filterTokens) {
|
||||
const prefix = filterToken.split(':').shift()
|
||||
|
||||
tokens.push(newFilter.value)
|
||||
// Tokenize search and remove a potential existing filter
|
||||
searchTokens = searchTokens.filter(t => !t.startsWith(prefix))
|
||||
searchTokens.push(filterToken)
|
||||
}
|
||||
|
||||
return tokens.join(' ')
|
||||
return searchTokens.join(' ')
|
||||
}
|
||||
|
||||
private parseFilters (search: string) {
|
||||
const tokens = this.restService.tokenizeString(search)
|
||||
const searchTokens = this.restService.tokenizeString(search)
|
||||
|
||||
this.enabledFilters = new Set()
|
||||
|
||||
for (const group of this.filters) {
|
||||
for (const filter of group.children) {
|
||||
if (tokens.includes(filter.value)) {
|
||||
const filterTokens = this.restService.tokenizeString(filter.value)
|
||||
|
||||
if (filterTokens.every(filterToken => searchTokens.includes(filterToken))) {
|
||||
this.enabledFilters.add(filter.value)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -384,14 +384,17 @@ export class VideoService {
|
|||
generateDownloadUrl (options: {
|
||||
video: Video
|
||||
files: VideoFile[]
|
||||
videoFileToken?: string
|
||||
}) {
|
||||
const { video, files } = options
|
||||
const { video, files, videoFileToken } = options
|
||||
|
||||
if (files.length === 0) throw new Error('Cannot generate download URL without files')
|
||||
|
||||
let url = `${VideoService.BASE_VIDEO_DOWNLOAD_URL}/${video.uuid}?`
|
||||
url += files.map(f => 'videoFileIds=' + f.id).join('&')
|
||||
|
||||
if (videoFileToken) url += `&videoFileToken=${videoFileToken}`
|
||||
|
||||
return url
|
||||
}
|
||||
|
||||
|
|
|
@ -136,7 +136,7 @@ export class UserVideoSettingsComponent extends FormReactive implements OnInit,
|
|||
next: () => {
|
||||
this.authService.refreshUserInformation()
|
||||
|
||||
if (this.notifyOnUpdate) this.notifier.success($localize`Video settings updated.`, 'toto', 15000)
|
||||
if (this.notifyOnUpdate) this.notifier.success($localize`Video settings updated.`)
|
||||
},
|
||||
|
||||
error: err => this.notifier.error(err.message)
|
||||
|
|
|
@ -104,7 +104,7 @@ export class VideoGenerateDownloadComponent implements OnInit {
|
|||
files.push(this.findAudioFileOnly())
|
||||
}
|
||||
|
||||
return this.videoService.generateDownloadUrl({ video: this.video, files })
|
||||
return this.videoService.generateDownloadUrl({ video: this.video, videoFileToken: this.videoFileToken, files })
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
|
|
@ -45,7 +45,8 @@ export class VideoFilters {
|
|||
[ 'categoryOneOf', undefined ],
|
||||
[ 'scope', 'federated' ],
|
||||
[ 'allVideos', false ],
|
||||
[ 'live', 'both' ]
|
||||
[ 'live', 'both' ],
|
||||
[ 'search', '' ]
|
||||
])
|
||||
|
||||
private activeFilters: VideoFilterActive[] = []
|
||||
|
|
|
@ -502,7 +502,7 @@ export class PeerTubePlayer {
|
|||
{
|
||||
label: player.localize('Copy the video URL'),
|
||||
listener: function () {
|
||||
copyToClipboard(buildVideoLink({ shortUUID }))
|
||||
copyToClipboard(buildVideoLink({ shortUUID }), player.el() as HTMLElement)
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -510,17 +510,17 @@ export class PeerTubePlayer {
|
|||
listener: function () {
|
||||
const url = buildVideoLink({ shortUUID })
|
||||
|
||||
copyToClipboard(decorateVideoLink({ url, startTime: player.currentTime() }))
|
||||
copyToClipboard(decorateVideoLink({ url, startTime: player.currentTime() }), player.el() as HTMLElement)
|
||||
}
|
||||
},
|
||||
{
|
||||
icon: 'code',
|
||||
label: player.localize('Copy embed code'),
|
||||
listener: () => {
|
||||
copyToClipboard(buildVideoOrPlaylistEmbed({
|
||||
embedUrl: self.currentLoadOptions.embedUrl,
|
||||
embedTitle: self.currentLoadOptions.embedTitle
|
||||
}))
|
||||
copyToClipboard(
|
||||
buildVideoOrPlaylistEmbed({ embedUrl: self.currentLoadOptions.embedUrl, embedTitle: self.currentLoadOptions.embedTitle }),
|
||||
player.el() as HTMLElement
|
||||
)
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
|
@ -36,12 +36,15 @@ class PeerTubeMobilePlugin extends Plugin {
|
|||
|
||||
this.seekAmount = 0
|
||||
|
||||
this.peerTubeMobileButtons = player.addChild('PeerTubeMobileButtons', { reportTouchActivity: false }) as PeerTubeMobileButtons
|
||||
|
||||
if (videojs.browser.IS_ANDROID && screen.orientation) {
|
||||
this.handleFullscreenRotation()
|
||||
}
|
||||
|
||||
// Don't add buttons if the player doesn't have controls
|
||||
if (!player.controls()) return
|
||||
|
||||
this.peerTubeMobileButtons = player.addChild('PeerTubeMobileButtons', { reportTouchActivity: false }) as PeerTubeMobileButtons
|
||||
|
||||
if (!this.player.options_.userActions) this.player.options_.userActions = {};
|
||||
|
||||
// FIXME: typings
|
||||
|
|
|
@ -1,13 +1,15 @@
|
|||
function copyToClipboard (text: string) {
|
||||
function copyToClipboard (text: string, container?: HTMLElement) {
|
||||
if (!container) container = document.body
|
||||
|
||||
const el = document.createElement('textarea')
|
||||
el.value = text
|
||||
el.setAttribute('readonly', '')
|
||||
el.style.position = 'absolute'
|
||||
el.style.left = '-9999px'
|
||||
document.body.appendChild(el)
|
||||
container.appendChild(el)
|
||||
el.select()
|
||||
document.execCommand('copy')
|
||||
document.body.removeChild(el)
|
||||
container.removeChild(el)
|
||||
}
|
||||
|
||||
function wait (ms: number) {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "peertube",
|
||||
"description": "PeerTube, an ActivityPub-federated video streaming platform using P2P directly in your web browser.",
|
||||
"version": "6.3.1",
|
||||
"version": "6.3.2",
|
||||
"private": true,
|
||||
"licence": "AGPL-3.0",
|
||||
"engines": {
|
||||
|
|
|
@ -178,7 +178,14 @@ async function saveReplayToExternalVideo (options: {
|
|||
inputFileMutexReleaser()
|
||||
}
|
||||
|
||||
await copyOrRegenerateThumbnails({ liveVideo, replayVideo })
|
||||
try {
|
||||
await copyOrRegenerateThumbnails({ liveVideo, replayVideo })
|
||||
} catch (err) {
|
||||
logger.error(
|
||||
`Cannot copy/regenerate thumbnails of ended live ${liveVideo.uuid} to external video ${replayVideo.uuid}`,
|
||||
lTags(liveVideo.uuid, replayVideo.uuid)
|
||||
)
|
||||
}
|
||||
|
||||
await createStoryboardJob(replayVideo)
|
||||
await createTranscriptionTaskIfNeeded(replayVideo)
|
||||
|
@ -280,7 +287,11 @@ async function replaceLiveByReplay (options: {
|
|||
}
|
||||
|
||||
// Regenerate the thumbnail & preview?
|
||||
await regenerateMiniaturesIfNeeded(videoWithFiles, undefined)
|
||||
try {
|
||||
await regenerateMiniaturesIfNeeded(videoWithFiles, undefined)
|
||||
} catch (err) {
|
||||
logger.error(`Cannot regenerate thumbnails of ended live ${videoWithFiles.uuid}`, lTags(liveVideo.uuid))
|
||||
}
|
||||
|
||||
// We consider this is a new video
|
||||
await moveToNextState({ video: videoWithFiles, isNewVideo: true })
|
||||
|
|
|
@ -284,6 +284,7 @@ function buildTags (video: MVideoAP) {
|
|||
|
||||
function buildIcon (video: MVideoAP): ActivityIconObject[] {
|
||||
return [ video.getMiniature(), video.getPreview() ]
|
||||
.filter(i => !!i)
|
||||
.map(i => i.toActivityPubObject(video))
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue