1
0
Fork 0
mirror of https://github.com/Radarr/Radarr synced 2024-12-21 23:42:23 +00:00

Convert MediaInfo to TypeScript

(cherry picked from commit 4e4bf3507f20c0f8581c66804f8ef406c41952d8)

Closes #10753
This commit is contained in:
Mark McDowall 2024-12-07 19:04:18 -08:00 committed by Bogdan
parent 5efefd804b
commit c81b2e80ee
12 changed files with 171 additions and 185 deletions

View file

@ -0,0 +1,27 @@
import React from 'react';
import DescriptionList from 'Components/DescriptionList/DescriptionList';
import DescriptionListItem from 'Components/DescriptionList/DescriptionListItem';
import MediaInfoProps from 'typings/MediaInfo';
import getEntries from 'Utilities/Object/getEntries';
function MediaInfo(props: MediaInfoProps) {
return (
<DescriptionList>
{getEntries(props).map(([key, value]) => {
const title = key
.replace(/([A-Z])/g, ' $1')
.replace(/^./, (str) => str.toUpperCase());
if (!value) {
return null;
}
return (
<DescriptionListItem key={key} title={title} data={props[key]} />
);
})}
</DescriptionList>
);
}
export default MediaInfo;

View file

@ -1,33 +0,0 @@
import React from 'react';
import DescriptionList from 'Components/DescriptionList/DescriptionList';
import DescriptionListItem from 'Components/DescriptionList/DescriptionListItem';
function MediaInfoPopover(props) {
return (
<DescriptionList>
{
Object.keys(props).map((key) => {
const title = key
.replace(/([A-Z])/g, ' $1')
.replace(/^./, (str) => str.toUpperCase());
const value = props[key];
if (!value) {
return null;
}
return (
<DescriptionListItem
key={key}
title={title}
data={props[key]}
/>
);
})
}
</DescriptionList>
);
}
export default MediaInfoPopover;

View file

@ -14,7 +14,7 @@ import MovieFormats from 'Movie/MovieFormats';
import MovieLanguages from 'Movie/MovieLanguages'; import MovieLanguages from 'Movie/MovieLanguages';
import MovieQuality from 'Movie/MovieQuality'; import MovieQuality from 'Movie/MovieQuality';
import FileEditModal from 'MovieFile/Edit/FileEditModal'; import FileEditModal from 'MovieFile/Edit/FileEditModal';
import MediaInfoConnector from 'MovieFile/MediaInfoConnector'; import MediaInfo from 'MovieFile/MediaInfo';
import * as mediaInfoTypes from 'MovieFile/mediaInfoTypes'; import * as mediaInfoTypes from 'MovieFile/mediaInfoTypes';
import formatBytes from 'Utilities/Number/formatBytes'; import formatBytes from 'Utilities/Number/formatBytes';
import formatCustomFormatScore from 'Utilities/Number/formatCustomFormatScore'; import formatCustomFormatScore from 'Utilities/Number/formatCustomFormatScore';
@ -224,7 +224,7 @@ class MovieFileEditorRow extends Component {
key={name} key={name}
className={styles.audio} className={styles.audio}
> >
<MediaInfoConnector <MediaInfo
type={mediaInfoTypes.AUDIO} type={mediaInfoTypes.AUDIO}
movieFileId={id} movieFileId={id}
/> />
@ -238,7 +238,7 @@ class MovieFileEditorRow extends Component {
key={name} key={name}
className={styles.audioLanguages} className={styles.audioLanguages}
> >
<MediaInfoConnector <MediaInfo
type={mediaInfoTypes.AUDIO_LANGUAGES} type={mediaInfoTypes.AUDIO_LANGUAGES}
movieFileId={id} movieFileId={id}
/> />
@ -252,7 +252,7 @@ class MovieFileEditorRow extends Component {
key={name} key={name}
className={styles.subtitles} className={styles.subtitles}
> >
<MediaInfoConnector <MediaInfo
type={mediaInfoTypes.SUBTITLES} type={mediaInfoTypes.SUBTITLES}
movieFileId={id} movieFileId={id}
/> />
@ -266,7 +266,7 @@ class MovieFileEditorRow extends Component {
key={name} key={name}
className={styles.video} className={styles.video}
> >
<MediaInfoConnector <MediaInfo
type={mediaInfoTypes.VIDEO} type={mediaInfoTypes.VIDEO}
movieFileId={id} movieFileId={id}
/> />
@ -280,7 +280,7 @@ class MovieFileEditorRow extends Component {
key={name} key={name}
className={styles.videoDynamicRangeType} className={styles.videoDynamicRangeType}
> >
<MediaInfoConnector <MediaInfo
type={mediaInfoTypes.VIDEO_DYNAMIC_RANGE_TYPE} type={mediaInfoTypes.VIDEO_DYNAMIC_RANGE_TYPE}
movieFileId={id} movieFileId={id}
/> />

View file

@ -8,7 +8,7 @@ import ModalFooter from 'Components/Modal/ModalFooter';
import ModalHeader from 'Components/Modal/ModalHeader'; import ModalHeader from 'Components/Modal/ModalHeader';
import { sizes } from 'Helpers/Props'; import { sizes } from 'Helpers/Props';
import translate from 'Utilities/String/translate'; import translate from 'Utilities/String/translate';
import MediaInfoPopover from './Editor/MediaInfoPopover'; import MediaInfo from './Editor/MediaInfo';
function FileDetailsModal(props) { function FileDetailsModal(props) {
const { const {
@ -31,7 +31,7 @@ function FileDetailsModal(props) {
</ModalHeader> </ModalHeader>
<ModalBody> <ModalBody>
<MediaInfoPopover {...mediaInfo} /> <MediaInfo {...mediaInfo} />
</ModalBody> </ModalBody>
<ModalFooter> <ModalFooter>

View file

@ -1,104 +0,0 @@
import _ from 'lodash';
import PropTypes from 'prop-types';
import React from 'react';
import getLanguageName from 'Utilities/String/getLanguageName';
import translate from 'Utilities/String/translate';
import * as mediaInfoTypes from './mediaInfoTypes';
function formatLanguages(languages) {
if (!languages) {
return null;
}
const splitLanguages = _.uniq(languages.split('/')).map((l) => {
const simpleLanguage = l.split('_')[0];
if (simpleLanguage === 'und') {
return translate('Unknown');
}
return getLanguageName(simpleLanguage);
});
if (splitLanguages.length > 3) {
return (
<span title={splitLanguages.join(', ')}>
{splitLanguages.slice(0, 2).join(', ')}, {splitLanguages.length - 2} more
</span>
);
}
return (
<span>
{splitLanguages.join(', ')}
</span>
);
}
function MediaInfo(props) {
const {
type,
audioChannels,
audioCodec,
audioLanguages,
subtitles,
videoCodec,
videoDynamicRangeType
} = props;
if (type === mediaInfoTypes.AUDIO) {
return (
<span>
{
audioCodec ? audioCodec : ''
}
{
audioCodec && audioChannels ? ' - ' : ''
}
{
audioChannels ? audioChannels.toFixed(1) : ''
}
</span>
);
}
if (type === mediaInfoTypes.AUDIO_LANGUAGES) {
return formatLanguages(audioLanguages);
}
if (type === mediaInfoTypes.SUBTITLES) {
return formatLanguages(subtitles);
}
if (type === mediaInfoTypes.VIDEO) {
return (
<span>
{videoCodec}
</span>
);
}
if (type === mediaInfoTypes.VIDEO_DYNAMIC_RANGE_TYPE) {
return (
<span>
{videoDynamicRangeType}
</span>
);
}
return null;
}
MediaInfo.propTypes = {
type: PropTypes.string.isRequired,
audioChannels: PropTypes.number,
audioCodec: PropTypes.string,
audioLanguages: PropTypes.string,
subtitles: PropTypes.string,
videoCodec: PropTypes.string,
videoDynamicRangeType: PropTypes.string
};
export default MediaInfo;

View file

@ -0,0 +1,92 @@
import React from 'react';
import getLanguageName from 'Utilities/String/getLanguageName';
import translate from 'Utilities/String/translate';
import useMovieFile from './useMovieFile';
function formatLanguages(languages: string | undefined) {
if (!languages) {
return null;
}
const splitLanguages = [...new Set(languages.split('/'))].map((l) => {
const simpleLanguage = l.split('_')[0];
if (simpleLanguage === 'und') {
return translate('Unknown');
}
return getLanguageName(simpleLanguage);
});
if (splitLanguages.length > 3) {
return (
<span title={splitLanguages.join(', ')}>
{splitLanguages.slice(0, 2).join(', ')}, {splitLanguages.length - 2}{' '}
more
</span>
);
}
return <span>{splitLanguages.join(', ')}</span>;
}
export type MediaInfoType =
| 'audio'
| 'audioLanguages'
| 'subtitles'
| 'video'
| 'videoDynamicRangeType';
interface MediaInfoProps {
movieFileId?: number;
type: MediaInfoType;
}
function MediaInfo({ movieFileId, type }: MediaInfoProps) {
const movieFile = useMovieFile(movieFileId);
if (!movieFile?.mediaInfo) {
return null;
}
const {
audioChannels,
audioCodec,
audioLanguages,
subtitles,
videoCodec,
videoDynamicRangeType,
} = movieFile.mediaInfo;
if (type === 'audio') {
return (
<span>
{audioCodec ? audioCodec : ''}
{audioCodec && audioChannels ? ' - ' : ''}
{audioChannels ? audioChannels.toFixed(1) : ''}
</span>
);
}
if (type === 'audioLanguages') {
return formatLanguages(audioLanguages);
}
if (type === 'subtitles') {
return formatLanguages(subtitles);
}
if (type === 'video') {
return <span>{videoCodec}</span>;
}
if (type === 'videoDynamicRangeType') {
return <span>{videoDynamicRangeType}</span>;
}
return null;
}
export default MediaInfo;

View file

@ -1,21 +0,0 @@
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import createMovieFileSelector from 'Store/Selectors/createMovieFileSelector';
import MediaInfo from './MediaInfo';
function createMapStateToProps() {
return createSelector(
createMovieFileSelector(),
(movieFile) => {
if (movieFile) {
return {
...movieFile.mediaInfo
};
}
return {};
}
);
}
export default connect(createMapStateToProps)(MediaInfo);

View file

@ -1,17 +0,0 @@
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import MovieLanguages from 'Movie/MovieLanguages';
import createMovieFileSelector from 'Store/Selectors/createMovieFileSelector';
function createMapStateToProps() {
return createSelector(
createMovieFileSelector(),
(movieFile) => {
return {
languages: movieFile ? movieFile.languages : undefined
};
}
);
}
export default connect(createMapStateToProps)(MovieLanguages);

View file

@ -0,0 +1,15 @@
import React from 'react';
import MovieLanguages from 'Movie/MovieLanguages';
import useMovieFile from './useMovieFile';
interface MovieFileLanguagesProps {
movieFileId: number;
}
function MovieFileLanguages({ movieFileId }: MovieFileLanguagesProps) {
const movieFile = useMovieFile(movieFileId);
return <MovieLanguages languages={movieFile?.languages ?? []} />;
}
export default MovieFileLanguages;

View file

@ -0,0 +1,18 @@
import { useSelector } from 'react-redux';
import { createSelector } from 'reselect';
import AppState from 'App/State/AppState';
function createMovieFileSelector(movieFileId?: number) {
return createSelector(
(state: AppState) => state.movieFiles.items,
(movieFiles) => {
return movieFiles.find(({ id }) => id === movieFileId);
}
);
}
function useMovieFile(movieFileId: number | undefined) {
return useSelector(createMovieFileSelector(movieFileId));
}
export default useMovieFile;

View file

@ -0,0 +1,9 @@
export type Entries<T> = {
[K in keyof T]: [K, T[K]];
}[keyof T][];
function getEntries<T extends object>(obj: T): Entries<T> {
return Object.entries(obj) as Entries<T>;
}
export default getEntries;

View file

@ -8,7 +8,7 @@ import movieEntities from 'Movie/movieEntities';
import MovieSearchCell from 'Movie/MovieSearchCell'; import MovieSearchCell from 'Movie/MovieSearchCell';
import MovieStatusConnector from 'Movie/MovieStatusConnector'; import MovieStatusConnector from 'Movie/MovieStatusConnector';
import MovieTitleLink from 'Movie/MovieTitleLink'; import MovieTitleLink from 'Movie/MovieTitleLink';
import MovieFileLanguageConnector from 'MovieFile/MovieFileLanguageConnector'; import MovieFileLanguages from 'MovieFile/MovieFileLanguages';
import styles from './CutoffUnmetRow.css'; import styles from './CutoffUnmetRow.css';
function CutoffUnmetRow(props) { function CutoffUnmetRow(props) {
@ -104,7 +104,7 @@ function CutoffUnmetRow(props) {
key={name} key={name}
className={styles.languages} className={styles.languages}
> >
<MovieFileLanguageConnector <MovieFileLanguages
movieFileId={movieFileId} movieFileId={movieFileId}
/> />
</TableRowCell> </TableRowCell>