diff --git a/frontend/src/Movie/Index/Table/MovieIndexRow.css b/frontend/src/Movie/Index/Table/MovieIndexRow.css
index 6b8091960c..bcc4eb6d6c 100644
--- a/frontend/src/Movie/Index/Table/MovieIndexRow.css
+++ b/frontend/src/Movie/Index/Table/MovieIndexRow.css
@@ -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,
diff --git a/frontend/src/Movie/Index/Table/MovieIndexRow.css.d.ts b/frontend/src/Movie/Index/Table/MovieIndexRow.css.d.ts
index 441f0219d4..049e414c24 100644
--- a/frontend/src/Movie/Index/Table/MovieIndexRow.css.d.ts
+++ b/frontend/src/Movie/Index/Table/MovieIndexRow.css.d.ts
@@ -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;
diff --git a/frontend/src/Movie/Index/Table/MovieIndexRow.tsx b/frontend/src/Movie/Index/Table/MovieIndexRow.tsx
index d546a8c511..b3d15dccca 100644
--- a/frontend/src/Movie/Index/Table/MovieIndexRow.tsx
+++ b/frontend/src/Movie/Index/Table/MovieIndexRow.tsx
@@ -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 (
+
+ {gigabytesPerHour.toFixed(2)}
+
+ );
+ }
+
+ if (name === 'audioCodec') {
+ return (
+
+ {movieFile?.mediaInfo?.audioCodec ?? ''}
+
+ );
+ }
+
+ if (name === 'videoCodec') {
+ return (
+
+ {movieFile?.mediaInfo?.videoCodec ?? ''}
+
+ );
+ }
+
+ if (name === 'resolution') {
+ return (
+
+ {movieFile?.mediaInfo?.resolution ?? ''}
+
+ );
+ }
+
if (name === 'genres') {
const joinedGenres = genres.join(', ');
diff --git a/frontend/src/Movie/Index/Table/MovieIndexTableHeader.css b/frontend/src/Movie/Index/Table/MovieIndexTableHeader.css
index fc237d8c0c..3e0278cc01 100644
--- a/frontend/src/Movie/Index/Table/MovieIndexTableHeader.css
+++ b/frontend/src/Movie/Index/Table/MovieIndexTableHeader.css
@@ -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,
diff --git a/frontend/src/Movie/Index/Table/MovieIndexTableHeader.css.d.ts b/frontend/src/Movie/Index/Table/MovieIndexTableHeader.css.d.ts
index 39890935ac..37cb4410c1 100644
--- a/frontend/src/Movie/Index/Table/MovieIndexTableHeader.css.d.ts
+++ b/frontend/src/Movie/Index/Table/MovieIndexTableHeader.css.d.ts
@@ -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;
diff --git a/frontend/src/Store/Actions/movieIndexActions.js b/frontend/src/Store/Actions/movieIndexActions.js
index e36bee132a..c5248229bf 100644
--- a/frontend/src/Store/Actions/movieIndexActions.js
+++ b/frontend/src/Store/Actions/movieIndexActions.js
@@ -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 ?? '';
}
},
diff --git a/frontend/src/Utilities/Number/parseRuntimeToHours.ts b/frontend/src/Utilities/Number/parseRuntimeToHours.ts
new file mode 100644
index 0000000000..95f88213e4
--- /dev/null
+++ b/frontend/src/Utilities/Number/parseRuntimeToHours.ts
@@ -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;
diff --git a/src/NzbDrone.Core/Localization/Core/en.json b/src/NzbDrone.Core/Localization/Core/en.json
index 23a3b71ab8..8b205580ea 100644
--- a/src/NzbDrone.Core/Localization/Core/en.json
+++ b/src/NzbDrone.Core/Localization/Core/en.json
@@ -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",