fix(frontend): display audio/video bitrate in correct SI units
Closes #1164 — the bitrate value from yt-dlp is in bits per second, but the frontend was formatting it via bitsToBytes() then humanFileSize() with a '/s' suffix, producing labels like '984 KiB/s' for what is actually a per-second *bit* rate. Add a dedicated humanBitrate() helper that formats a bps value with SI decimal units (bps, kbps, Mbps, Gbps, Tbps) — the standard for network bitrates and the convention yt-dlp itself uses. Update VideoListItemTable and the stream-info block on Video.tsx to use the new helper, and drop the now-unused bitsToBytes import from Video.tsx.
This commit is contained in:
parent
5477ca97f1
commit
2becba47c3
|
|
@ -3,12 +3,12 @@ import Routes from '../configuration/routes/RouteList';
|
|||
import { VideoType } from '../pages/Home';
|
||||
import { ViewStylesType } from '../configuration/constants/ViewStyle';
|
||||
import humanFileSize from '../functions/humanFileSize';
|
||||
import humanBitrate from '../functions/humanBitrate';
|
||||
import { FileSizeUnits } from '../api/actions/updateUserConfig';
|
||||
import { useUserConfigStore } from '../stores/UserConfigStore';
|
||||
import { useVideoSelectionStore } from '../stores/VideoSelectionStore';
|
||||
import iconChecked from '/img/icon-seen.svg';
|
||||
import iconUnchecked from '/img/icon-unseen.svg';
|
||||
import bitsToBytes from '../functions/bitsToBytes';
|
||||
|
||||
const StreamsTypeEmun = {
|
||||
Video: 'video',
|
||||
|
|
@ -98,9 +98,9 @@ const VideoListItemTable = ({ videoList, viewStyle }: VideoListItemProps) => {
|
|||
<td>{`${videoStream?.width || '-'}x${videoStream?.height || '-'}`}</td>
|
||||
<td>{humanFileSize(media_size, useSiUnits)}</td>
|
||||
<td>{videoStream?.codec || '-'}</td>
|
||||
<td>{humanFileSize(bitsToBytes(videoStream?.bitrate || 0), useSiUnits)}</td>
|
||||
<td>{humanBitrate(videoStream?.bitrate || 0)}</td>
|
||||
<td>{audioStream?.codec || '-'}</td>
|
||||
<td>{humanFileSize(bitsToBytes(audioStream?.bitrate || 0), useSiUnits)}</td>
|
||||
<td>{humanBitrate(audioStream?.bitrate || 0)}</td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,44 @@
|
|||
/**
|
||||
* Format a bitrate (in bits per second) as human-readable text.
|
||||
*
|
||||
* Bitrate values from yt-dlp come in bits per second. This helper formats
|
||||
* them using SI powers of 1000 (kbps, Mbps, Gbps) — the standard for
|
||||
* bit-rate display, where 1 kbps = 1000 bits per second.
|
||||
*
|
||||
* Note: this intentionally does NOT use the IEC "Kib/s" unit, since
|
||||
* network bitrates (and yt-dlp's own `--format-sort bitrate` values)
|
||||
* are universally expressed in SI decimal units in the wild.
|
||||
*
|
||||
* @param bitsPerSecond Bitrate in bits per second.
|
||||
* @param dp Number of decimal places to display. Default 1.
|
||||
*
|
||||
* @return Formatted string ending in the unit (e.g. "1.5 Mbps").
|
||||
*/
|
||||
function humanBitrate(bitsPerSecond: number, dp = 1): string {
|
||||
if (!isFinite(bitsPerSecond) || bitsPerSecond < 0) {
|
||||
return '-';
|
||||
}
|
||||
if (bitsPerSecond === 0) {
|
||||
return '0 bps';
|
||||
}
|
||||
|
||||
const thresh = 1000;
|
||||
const units = ['bps', 'kbps', 'Mbps', 'Gbps', 'Tbps'];
|
||||
|
||||
if (Math.abs(bitsPerSecond) < thresh) {
|
||||
return bitsPerSecond + ' bps';
|
||||
}
|
||||
|
||||
let value = bitsPerSecond;
|
||||
let u = 0;
|
||||
const r = 10 ** dp;
|
||||
|
||||
do {
|
||||
value /= thresh;
|
||||
++u;
|
||||
} while (Math.round(Math.abs(value) * r) / r >= thresh && u < units.length - 1);
|
||||
|
||||
return value.toFixed(dp) + ' ' + units[u];
|
||||
}
|
||||
|
||||
export default humanBitrate;
|
||||
|
|
@ -16,6 +16,7 @@ import loadSimilarVideosById from '../api/loader/loadSimilarVideosById';
|
|||
import VideoList from '../components/VideoList';
|
||||
import updateWatchedState from '../api/actions/updateWatchedState';
|
||||
import humanFileSize from '../functions/humanFileSize';
|
||||
import humanBitrate from '../functions/humanBitrate';
|
||||
import ScrollToTopOnNavigate from '../components/ScrollToTop';
|
||||
import ChannelOverview from '../components/ChannelOverview';
|
||||
import deleteVideo from '../api/actions/deleteVideo';
|
||||
|
|
@ -45,7 +46,6 @@ import NotFound from './NotFound';
|
|||
import { ApiResponseType } from '../functions/APIClient';
|
||||
import VideoThumbnail from '../components/VideoThumbail';
|
||||
import { ViewStylesEnum, ViewStylesType } from '../configuration/constants/ViewStyle';
|
||||
import bitsToBytes from '../functions/bitsToBytes';
|
||||
|
||||
const isInPlaylist = (videoId: string, playlist: PlaylistType) => {
|
||||
return playlist.playlist_entries.some(entry => {
|
||||
|
|
@ -461,7 +461,7 @@ const Video = () => {
|
|||
return (
|
||||
<p key={stream.index}>
|
||||
{capitalizeFirstLetter(stream.type)}: {stream.codec}{' '}
|
||||
{humanFileSize(bitsToBytes(stream.bitrate), useSiUnits)}/s
|
||||
{humanBitrate(stream.bitrate)}
|
||||
{stream.width && (
|
||||
<>
|
||||
<span className="space-carrot">|</span> {stream.width}x{stream.height}
|
||||
|
|
|
|||
Loading…
Reference in New Issue