mirror of https://github.com/Chocobozzz/PeerTube
Better resolution label for custom video aspect
parent
340d77b7c6
commit
21215122a8
|
@ -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>
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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', {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
// ---------------------------------------------------------------------------
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in New Issue