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

Convert MediaInfo to TypeScript

This commit is contained in:
Mark McDowall 2024-12-07 19:04:18 -08:00
parent 34ae65c087
commit 4e4bf3507f
11 changed files with 166 additions and 198 deletions

View file

@ -9,26 +9,27 @@ import Popover from 'Components/Tooltip/Popover';
import EpisodeFormats from 'Episode/EpisodeFormats';
import EpisodeLanguages from 'Episode/EpisodeLanguages';
import EpisodeQuality from 'Episode/EpisodeQuality';
import { EpisodeFile } from 'EpisodeFile/EpisodeFile';
import useModalOpenState from 'Helpers/Hooks/useModalOpenState';
import { icons, kinds, tooltipPositions } from 'Helpers/Props';
import Language from 'Language/Language';
import { QualityModel } from 'Quality/Quality';
import CustomFormat from 'typings/CustomFormat';
import formatBytes from 'Utilities/Number/formatBytes';
import formatCustomFormatScore from 'Utilities/Number/formatCustomFormatScore';
import translate from 'Utilities/String/translate';
import MediaInfo from './MediaInfo';
import styles from './EpisodeFileRow.css';
interface EpisodeFileRowProps {
path: string;
size: number;
languages: Language[];
quality: QualityModel;
qualityCutoffNotMet: boolean;
customFormats: CustomFormat[];
customFormatScore: number;
mediaInfo: object;
interface EpisodeFileRowProps
extends Pick<
EpisodeFile,
| 'path'
| 'size'
| 'languages'
| 'quality'
| 'customFormats'
| 'customFormatScore'
| 'qualityCutoffNotMet'
| 'mediaInfo'
> {
columns: Column[];
onDeleteEpisodeFile(): void;
}

View file

@ -1,33 +0,0 @@
import React from 'react';
import DescriptionList from 'Components/DescriptionList/DescriptionList';
import DescriptionListItem from 'Components/DescriptionList/DescriptionListItem';
function MediaInfo(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 MediaInfo;

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,17 +0,0 @@
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import EpisodeLanguages from 'Episode/EpisodeLanguages';
import createEpisodeFileSelector from 'Store/Selectors/createEpisodeFileSelector';
function createMapStateToProps() {
return createSelector(
createEpisodeFileSelector(),
(episodeFile) => {
return {
languages: episodeFile ? episodeFile.languages : undefined
};
}
);
}
export default connect(createMapStateToProps)(EpisodeLanguages);

View file

@ -0,0 +1,15 @@
import React from 'react';
import EpisodeLanguages from 'Episode/EpisodeLanguages';
import useEpisodeFile from './useEpisodeFile';
interface EpisodeFileLanguagesProps {
episodeFileId: number;
}
function EpisodeFileLanguages({ episodeFileId }: EpisodeFileLanguagesProps) {
const episodeFile = useEpisodeFile(episodeFileId);
return <EpisodeLanguages languages={episodeFile?.languages ?? []} />;
}
export default EpisodeFileLanguages;

View file

@ -1,105 +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 useEpisodeFile from './useEpisodeFile';
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 {
episodeFileId?: number;
type: MediaInfoType;
}
function MediaInfo({ episodeFileId, type }: MediaInfoProps) {
const episodeFile = useEpisodeFile(episodeFileId);
if (!episodeFile?.mediaInfo) {
return null;
}
const {
audioChannels,
audioCodec,
audioLanguages,
subtitles,
videoCodec,
videoDynamicRangeType,
} = episodeFile.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 createEpisodeFileSelector from 'Store/Selectors/createEpisodeFileSelector';
import MediaInfo from './MediaInfo';
function createMapStateToProps() {
return createSelector(
createEpisodeFileSelector(),
(episodeFile) => {
if (episodeFile) {
return {
...episodeFile.mediaInfo
};
}
return {};
}
);
}
export default connect(createMapStateToProps)(MediaInfo);

View file

@ -13,8 +13,8 @@ import EpisodeSearchCell from 'Episode/EpisodeSearchCell';
import EpisodeStatus from 'Episode/EpisodeStatus';
import EpisodeTitleLink from 'Episode/EpisodeTitleLink';
import IndexerFlags from 'Episode/IndexerFlags';
import EpisodeFileLanguageConnector from 'EpisodeFile/EpisodeFileLanguageConnector';
import MediaInfoConnector from 'EpisodeFile/MediaInfoConnector';
import EpisodeFileLanguages from 'EpisodeFile/EpisodeFileLanguages';
import MediaInfo from 'EpisodeFile/MediaInfo';
import * as mediaInfoTypes from 'EpisodeFile/mediaInfoTypes';
import { icons, kinds, tooltipPositions } from 'Helpers/Props';
import formatBytes from 'Utilities/Number/formatBytes';
@ -229,7 +229,7 @@ class EpisodeRow extends Component {
key={name}
className={styles.languages}
>
<EpisodeFileLanguageConnector
<EpisodeFileLanguages
episodeFileId={episodeFileId}
/>
</TableRowCell>
@ -242,7 +242,7 @@ class EpisodeRow extends Component {
key={name}
className={styles.audio}
>
<MediaInfoConnector
<MediaInfo
type={mediaInfoTypes.AUDIO}
episodeFileId={episodeFileId}
/>
@ -256,7 +256,7 @@ class EpisodeRow extends Component {
key={name}
className={styles.audioLanguages}
>
<MediaInfoConnector
<MediaInfo
type={mediaInfoTypes.AUDIO_LANGUAGES}
episodeFileId={episodeFileId}
/>
@ -270,7 +270,7 @@ class EpisodeRow extends Component {
key={name}
className={styles.subtitles}
>
<MediaInfoConnector
<MediaInfo
type={mediaInfoTypes.SUBTITLES}
episodeFileId={episodeFileId}
/>
@ -284,7 +284,7 @@ class EpisodeRow extends Component {
key={name}
className={styles.video}
>
<MediaInfoConnector
<MediaInfo
type={mediaInfoTypes.VIDEO}
episodeFileId={episodeFileId}
/>
@ -298,7 +298,7 @@ class EpisodeRow extends Component {
key={name}
className={styles.videoDynamicRangeType}
>
<MediaInfoConnector
<MediaInfo
type={mediaInfoTypes.VIDEO_DYNAMIC_RANGE_TYPE}
episodeFileId={episodeFileId}
/>

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

@ -9,7 +9,7 @@ import EpisodeSearchCell from 'Episode/EpisodeSearchCell';
import EpisodeStatus from 'Episode/EpisodeStatus';
import EpisodeTitleLink from 'Episode/EpisodeTitleLink';
import SeasonEpisodeNumber from 'Episode/SeasonEpisodeNumber';
import EpisodeFileLanguageConnector from 'EpisodeFile/EpisodeFileLanguageConnector';
import EpisodeFileLanguages from 'EpisodeFile/EpisodeFileLanguages';
import SeriesTitleLink from 'Series/SeriesTitleLink';
import styles from './CutoffUnmetRow.css';
@ -123,7 +123,7 @@ function CutoffUnmetRow(props) {
key={name}
className={styles.languages}
>
<EpisodeFileLanguageConnector
<EpisodeFileLanguages
episodeFileId={episodeFileId}
/>
</TableRowCell>