Better resolution label for custom video aspect

pull/6544/head
Chocobozzz 2024-08-06 15:43:24 +02:00
parent 340d77b7c6
commit 21215122a8
No known key found for this signature in database
GPG Key ID: 583A612D890159BE
8 changed files with 88 additions and 47 deletions

View File

@ -17,9 +17,15 @@
<input type="radio" name="video-file" [id]="'file-' + file.id" [(ngModel)]="videoFileChosen" [value]="'file-' + file.id">
<label [for]="'file-' + file.id">
<strong>{{ file.resolution.label }}</strong>
<strong>{{ getLabel(file) }}</strong>
<span class="muted">{{ getFileSize(file) | bytes: 1 }} @if (file.width) { | {{ file.width }}x{{ file.height }} }</span>
<span class="muted">
{{ getFileSize(file) | bytes: 1 }}
@if (file.width) { | {{ file.width }}x{{ file.height }} }
@if (file.fps && file.fps >= 50) { | <ng-container i18n>{{ file.fps }}fps</ng-container> }
</span>
</label>
</div>
}

View File

@ -6,7 +6,7 @@ import { VideoService } from '@app/shared/shared-main/video/video.service'
import {
NgbTooltip
} from '@ng-bootstrap/ng-bootstrap'
import { maxBy } from '@peertube/peertube-core-utils'
import { getResolutionAndFPSLabel, maxBy } from '@peertube/peertube-core-utils'
import { VideoFile, VideoResolution, VideoSource } from '@peertube/peertube-models'
import { videoRequiresFileToken } from '@root-helpers/video'
import { GlobalIconComponent } from '../../shared-icons/global-icon.component'
@ -52,6 +52,10 @@ export class VideoGenerateDownloadComponent implements OnInit {
this.videoFileChosen = 'file-' + maxBy(this.videoFiles, 'resolution').id
}
getLabel (file: VideoFile) {
return getResolutionAndFPSLabel(file.resolution.label, file.fps)
}
getFileSize (file: VideoFile) {
if (file.hasAudio && file.hasVideo) return file.size
if (file.hasAudio) return file.size

View File

@ -303,15 +303,7 @@ export class Html5Hlsjs {
// ---------------------------------------------------------------------------
private buildLevelLabel (level: Level) {
if (this.player.srOptions_.levelLabelHandler) {
return this.player.srOptions_.levelLabelHandler(level, this.player)
}
if (level.height) return level.height + 'p'
if (level.width) return Math.round(level.width * 9 / 16) + 'p'
if (level.bitrate) return (level.bitrate / 1000) + 'kbps'
return this.player.localize('Audio only')
return this.player.srOptions_.levelLabelHandler(level, this.player)
}
private _removeQuality (index: number) {

View File

@ -1,8 +1,10 @@
import { HybridLoaderSettings } from '@peertube/p2p-media-loader-core'
import { Engine, HlsJsEngineSettings } from '@peertube/p2p-media-loader-hlsjs'
import { getResolutionAndFPSLabel, getResolutionLabel } from '@peertube/peertube-core-utils'
import { LiveVideoLatencyMode } from '@peertube/peertube-models'
import { logger } from '@root-helpers/logger'
import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage'
import { Level } from 'hls.js'
import { getAverageBandwidthInStore } from '../../peertube-player-local-storage'
import {
HLSLoaderClass,
@ -70,21 +72,17 @@ export class HLSOptionsBuilder {
const hlsjs = {
hlsjsConfig: this.getHLSJSOptions(loaderBuilder),
levelLabelHandler: (level: { height: number, width: number }, player: videojs.VideoJsPlayer) => {
levelLabelHandler: (level: Level, player: videojs.VideoJsPlayer) => {
const resolution = Math.min(level.height || 0, level.width || 0)
const file = this.options.hls.videoFiles.find(f => f.resolution.id === resolution)
// We don't have files for live videos
if (!file) {
if (resolution === 0) return player.localize('Audio only')
return level.height + 'p'
}
const resolutionLabel = getResolutionLabel({
resolution,
height: file?.height ?? level.height,
width: file?.width ?? level.width
})
let label = file.resolution.label
if (file.fps >= 50) label += file.fps
return label
return player.localize(getResolutionAndFPSLabel(resolutionLabel, file?.fps ?? level.frameRate))
}
}

View File

@ -1,8 +1,8 @@
import { addQueryParams, getResolutionAndFPSLabel } from '@peertube/peertube-core-utils'
import { VideoFile } from '@peertube/peertube-models'
import { logger } from '@root-helpers/logger'
import debug from 'debug'
import videojs from 'video.js'
import { logger } from '@root-helpers/logger'
import { addQueryParams } from '@peertube/peertube-core-utils'
import { VideoFile } from '@peertube/peertube-models'
import { PeerTubeResolution, PlayerNetworkInfo, WebVideoPluginOptions } from '../../types'
const debugLogger = debug('peertube:player:web-video-plugin')
@ -155,7 +155,7 @@ class WebVideoPlugin extends Plugin {
private buildQualities () {
const resolutions: PeerTubeResolution[] = this.videoFiles.map(videoFile => ({
id: videoFile.resolution.id,
label: this.buildQualityLabel(videoFile),
label: this.player.localize(getResolutionAndFPSLabel(videoFile.resolution.label, videoFile.fps)),
height: videoFile.resolution.id,
selected: videoFile.id === this.currentVideoFile?.id,
selectCallback: () => this.updateVideoFile({ videoFile, isUserResolutionChange: true })
@ -164,16 +164,6 @@ class WebVideoPlugin extends Plugin {
this.player.peertubeResolutions().add(resolutions)
}
private buildQualityLabel (file: VideoFile) {
let label = file.resolution.label
if (file.fps && file.fps >= 50) {
label += file.fps
}
return label
}
private setupNetworkInfoInterval () {
this.networkInfoInterval = setInterval(() => {
return this.player.trigger('network-info', {

View File

@ -1,4 +1,4 @@
import { VideoDetails, VideoPrivacy, VideoStreamingPlaylistType } from '@peertube/peertube-models'
import { VideoDetails, VideoPrivacy, VideoResolution, VideoStreamingPlaylistType } from '@peertube/peertube-models'
export function getAllPrivacies () {
return [ VideoPrivacy.PUBLIC, VideoPrivacy.INTERNAL, VideoPrivacy.PRIVATE, VideoPrivacy.UNLISTED, VideoPrivacy.PASSWORD_PROTECTED ]
@ -23,3 +23,54 @@ export function buildAspectRatio (options: { width: number, height: number }) {
return Math.round((width / height) * 10000) / 10000 // 4 decimals precision
}
const classicResolutions = new Set<number>([
VideoResolution.H_NOVIDEO,
VideoResolution.H_144P,
VideoResolution.H_240P,
VideoResolution.H_360P,
VideoResolution.H_480P,
VideoResolution.H_720P,
VideoResolution.H_1080P,
VideoResolution.H_1440P,
VideoResolution.H_4K
])
const resolutionConverter = {
3840: VideoResolution.H_4K,
1920: VideoResolution.H_1080P,
1280: VideoResolution.H_720P,
854: VideoResolution.H_480P,
640: VideoResolution.H_360P,
426: VideoResolution.H_240P,
256: VideoResolution.H_144P
}
export function getResolutionLabel (options: {
resolution: number
height: number
width: number
}) {
const { height, width } = options
if (options.resolution === 0) return 'Audio only'
let resolution = options.resolution
// Try to find a better resolution label
// For example with a video 1920x816 we prefer to display "1080p"
if (!classicResolutions.has(resolution) && typeof height === 'number' && typeof width === 'number') {
const max = Math.max(height, width)
const alternativeLabel = resolutionConverter[max]
if (alternativeLabel) resolution = resolutionConverter[max]
}
return `${resolution}p`
}
export function getResolutionAndFPSLabel (resolutionLabel: string, fps: number) {
if (fps && fps >= 50) return resolutionLabel + fps
return resolutionLabel
}

View File

@ -25,6 +25,7 @@ import {
import { MServer, MStreamingPlaylistRedundanciesOpt, MVideoFormattable, MVideoFormattableDetails } from '../../../types/models/index.js'
import { MVideoFileRedundanciesOpt } from '../../../types/models/video/video-file.js'
import { sortByResolutionDesc } from './shared/index.js'
import { getResolutionLabel } from '@peertube/peertube-core-utils'
export type VideoFormattingJSONOptions = {
completeDescription?: boolean
@ -230,7 +231,12 @@ export function videoFilesModelToFormattedJSON (
resolution: {
id: videoFile.resolution,
label: getResolutionLabel(videoFile.resolution)
label: getResolutionLabel({
resolution: videoFile.resolution,
height: videoFile.height,
width: videoFile.width
})
},
width: videoFile.width,
@ -279,12 +285,6 @@ export function getStateLabel (id: number) {
return VIDEO_STATES[id] || 'Unknown'
}
export function getResolutionLabel (resolution: number) {
if (resolution === 0) return 'Audio'
return `${resolution}p`
}
// ---------------------------------------------------------------------------
// Private
// ---------------------------------------------------------------------------

View File

@ -1,3 +1,4 @@
import { getResolutionLabel } from '@peertube/peertube-core-utils'
import { ActivityVideoUrlObject, type FileStorageType, type VideoSource } from '@peertube/peertube-models'
import { DOWNLOAD_PATHS, WEBSERVER } from '@server/initializers/constants.js'
import { getVideoFileMimeType } from '@server/lib/video-file.js'
@ -6,7 +7,6 @@ import { extname, join } from 'path'
import { Transaction } from 'sequelize'
import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, Table, UpdatedAt } from 'sequelize-typescript'
import { SequelizeModel, doesExist, getSort } from '../shared/index.js'
import { getResolutionLabel } from './formatter/video-api-format.js'
import { VideoModel } from './video.js'
@Table({
@ -148,7 +148,7 @@ export class VideoSourceModel extends SequelizeModel<VideoSourceModel> {
resolution: {
id: this.resolution,
label: this.resolution !== null
? getResolutionLabel(this.resolution)
? getResolutionLabel({ resolution: this.resolution, height: this.height, width: this.width })
: null
},
size: this.size,