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:
parent
5efefd804b
commit
c81b2e80ee
12 changed files with 171 additions and 185 deletions
27
frontend/src/MovieFile/Editor/MediaInfo.tsx
Normal file
27
frontend/src/MovieFile/Editor/MediaInfo.tsx
Normal 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;
|
|
@ -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;
|
|
|
@ -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}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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;
|
|
92
frontend/src/MovieFile/MediaInfo.tsx
Normal file
92
frontend/src/MovieFile/MediaInfo.tsx
Normal 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;
|
|
@ -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);
|
|
|
@ -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);
|
|
15
frontend/src/MovieFile/MovieFileLanguages.tsx
Normal file
15
frontend/src/MovieFile/MovieFileLanguages.tsx
Normal 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;
|
18
frontend/src/MovieFile/useMovieFile.ts
Normal file
18
frontend/src/MovieFile/useMovieFile.ts
Normal 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;
|
9
frontend/src/Utilities/Object/getEntries.ts
Normal file
9
frontend/src/Utilities/Object/getEntries.ts
Normal 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;
|
|
@ -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>
|
||||||
|
|
Loading…
Reference in a new issue