This commit is contained in:
Peter Drier 2026-01-13 20:58:46 +01:00 committed by GitHub
commit 3347df618a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 177 additions and 0 deletions

View file

@ -88,9 +88,32 @@
.sizeOnDisk {
composes: cell;
justify-content: flex-end;
flex: 0 0 120px;
}
.gigabytesPerHour {
composes: cell;
justify-content: flex-end;
flex: 0 0 100px;
}
.audioCodec,
.videoCodec {
composes: cell;
flex: 0 0 110px;
}
.resolution {
composes: cell;
flex: 0 0 100px;
}
.imdbRating,
.tmdbRating,
.rottenTomatoesRating,

View file

@ -3,6 +3,7 @@
interface CssExports {
'actions': string;
'added': string;
'audioCodec': string;
'cell': string;
'certification': string;
'checkInput': string;
@ -10,6 +11,7 @@ interface CssExports {
'digitalRelease': string;
'externalLinks': string;
'genres': string;
'gigabytesPerHour': string;
'imdbRating': string;
'inCinemas': string;
'keywords': string;
@ -23,6 +25,7 @@ interface CssExports {
'qualityProfileId': string;
'releaseDate': string;
'releaseGroups': string;
'resolution': string;
'rottenTomatoesRating': string;
'runtime': string;
'sizeOnDisk': string;
@ -32,6 +35,7 @@ interface CssExports {
'tags': string;
'tmdbRating': string;
'traktRating': string;
'videoCodec': string;
'year': string;
}
export const cssExports: CssExports;

View file

@ -28,6 +28,7 @@ import createUISettingsSelector from 'Store/Selectors/createUISettingsSelector';
import { SelectStateInputProps } from 'typings/props';
import formatRuntime from 'Utilities/Date/formatRuntime';
import formatBytes from 'Utilities/Number/formatBytes';
import parseRuntimeToHours from 'Utilities/Number/parseRuntimeToHours';
import firstCharToUpper from 'Utilities/String/firstCharToUpper';
import translate from 'Utilities/String/translate';
import MovieIndexProgressBar from '../ProgressBar/MovieIndexProgressBar';
@ -330,6 +331,44 @@ function MovieIndexRow(props: MovieIndexRowProps) {
);
}
if (name === 'gigabytesPerHour') {
const runtimeHours = parseRuntimeToHours(
movieFile?.mediaInfo?.runTime
);
const gigabytesPerHour =
runtimeHours > 0 ? sizeOnDisk / 1073741824 / runtimeHours : 0;
return (
<VirtualTableRowCell key={name} className={styles[name]}>
{gigabytesPerHour.toFixed(2)}
</VirtualTableRowCell>
);
}
if (name === 'audioCodec') {
return (
<VirtualTableRowCell key={name} className={styles[name]}>
{movieFile?.mediaInfo?.audioCodec ?? ''}
</VirtualTableRowCell>
);
}
if (name === 'videoCodec') {
return (
<VirtualTableRowCell key={name} className={styles[name]}>
{movieFile?.mediaInfo?.videoCodec ?? ''}
</VirtualTableRowCell>
);
}
if (name === 'resolution') {
return (
<VirtualTableRowCell key={name} className={styles[name]}>
{movieFile?.mediaInfo?.resolution ?? ''}
</VirtualTableRowCell>
);
}
if (name === 'genres') {
const joinedGenres = genres.join(', ');

View file

@ -78,9 +78,32 @@
.sizeOnDisk {
composes: headerCell from '~Components/Table/VirtualTableHeaderCell.css';
justify-content: flex-end;
flex: 0 0 120px;
}
.gigabytesPerHour {
composes: headerCell from '~Components/Table/VirtualTableHeaderCell.css';
justify-content: flex-end;
flex: 0 0 100px;
}
.audioCodec,
.videoCodec {
composes: headerCell from '~Components/Table/VirtualTableHeaderCell.css';
flex: 0 0 110px;
}
.resolution {
composes: headerCell from '~Components/Table/VirtualTableHeaderCell.css';
flex: 0 0 100px;
}
.imdbRating,
.tmdbRating,
.rottenTomatoesRating,

View file

@ -3,10 +3,12 @@
interface CssExports {
'actions': string;
'added': string;
'audioCodec': string;
'certification': string;
'collection': string;
'digitalRelease': string;
'genres': string;
'gigabytesPerHour': string;
'imdbRating': string;
'inCinemas': string;
'keywords': string;
@ -20,6 +22,7 @@ interface CssExports {
'qualityProfileId': string;
'releaseDate': string;
'releaseGroups': string;
'resolution': string;
'rottenTomatoesRating': string;
'runtime': string;
'sizeOnDisk': string;
@ -29,6 +32,7 @@ interface CssExports {
'tags': string;
'tmdbRating': string;
'traktRating': string;
'videoCodec': string;
'year': string;
}
export const cssExports: CssExports;

View file

@ -1,6 +1,7 @@
import { createAction } from 'redux-actions';
import { filterBuilderTypes, filterBuilderValueTypes, sortDirections } from 'Helpers/Props';
import sortByProp from 'Utilities/Array/sortByProp';
import parseRuntimeToHours from 'Utilities/Number/parseRuntimeToHours';
import translate from 'Utilities/String/translate';
import createHandleActions from './Creators/createHandleActions';
import createSetClientSideCollectionFilterReducer from './Creators/Reducers/createSetClientSideCollectionFilterReducer';
@ -175,6 +176,30 @@ export const defaultState = {
isSortable: true,
isVisible: false
},
{
name: 'gigabytesPerHour',
label: () => translate('GigabytesPerHour'),
isSortable: true,
isVisible: false
},
{
name: 'audioCodec',
label: () => translate('AudioCodec'),
isSortable: true,
isVisible: false
},
{
name: 'videoCodec',
label: () => translate('VideoCodec'),
isSortable: true,
isVisible: false
},
{
name: 'resolution',
label: () => translate('Resolution'),
isSortable: true,
isVisible: false
},
{
name: 'genres',
label: () => translate('Genres'),
@ -295,6 +320,36 @@ export const defaultState = {
traktRating: function({ ratings = {} }) {
return ratings.trakt ? ratings.trakt.value : 0;
},
gigabytesPerHour: function(item) {
const { statistics = {}, movieFile } = item;
const { sizeOnDisk = 0 } = statistics;
const runtimeHours = parseRuntimeToHours(movieFile?.mediaInfo?.runTime);
if (runtimeHours === 0) {
return 0;
}
return sizeOnDisk / runtimeHours;
},
audioCodec: function(item) {
const { movieFile } = item;
return movieFile?.mediaInfo?.audioCodec ?? '';
},
videoCodec: function(item) {
const { movieFile } = item;
return movieFile?.mediaInfo?.videoCodec ?? '';
},
resolution: function(item) {
const { movieFile } = item;
return movieFile?.mediaInfo?.resolution ?? '';
}
},

View file

@ -0,0 +1,26 @@
// Parses a runtime string in "H:MM:SS" or "M:SS" format to decimal hours
function parseRuntimeToHours(runTime: string | undefined): number {
if (!runTime) {
return 0;
}
const parts = runTime.split(':').map(Number);
if (parts.some(isNaN)) {
return 0;
}
if (parts.length === 3) {
// H:MM:SS format
return parts[0] + parts[1] / 60 + parts[2] / 3600;
}
if (parts.length === 2) {
// M:SS format
return parts[0] / 60 + parts[1] / 3600;
}
return 0;
}
export default parseRuntimeToHours;

View file

@ -95,6 +95,7 @@
"ApplyTagsHelpTextRemove": "Remove: Remove the entered tags",
"ApplyTagsHelpTextReplace": "Replace: Replace the tags with the entered tags (enter no tags to clear all tags)",
"AptUpdater": "Use apt to install the update",
"AudioCodec": "Audio Codec",
"AudioInfo": "Audio Info",
"AudioLanguages": "Audio Languages",
"AuthBasic": "Basic (Browser Popup)",
@ -725,6 +726,7 @@
"From": "from",
"FullColorEvents": "Full Color Events",
"FullColorEventsHelpText": "Altered style to color the entire event with the status color, instead of just the left edge. Does not apply to Agenda",
"GigabytesPerHour": "Gigabytes Per Hour",
"General": "General",
"GeneralSettings": "General Settings",
"GeneralSettingsLoadError": "Unable to load General settings",
@ -1683,6 +1685,7 @@
"ResetQualityDefinitions": "Reset Quality Definitions",
"ResetQualityDefinitionsMessageText": "Are you sure you want to reset quality definitions?",
"ResetTitles": "Reset Titles",
"Resolution": "Resolution",
"Restart": "Restart",
"RestartLater": "I'll restart later",
"RestartNow": "Restart Now",