diff --git a/src/components/Utilities/ReleaseManagement/QuickSelectModal.tsx b/src/components/Utilities/ReleaseManagement/QuickSelectModal.tsx index 33db63a01..1a0acc560 100644 --- a/src/components/Utilities/ReleaseManagement/QuickSelectModal.tsx +++ b/src/components/Utilities/ReleaseManagement/QuickSelectModal.tsx @@ -20,7 +20,8 @@ const QuickSelectModal = ({ onClose, seriesId, show }: Props) => { const fileSummaryQuery = useSeriesFileSummaryQuery( seriesId, { - groupBy: 'GroupName,FileSource,VideoResolution,AudioLanguages,SubtitleLanguages,VideoHasChapters', + groupBy: + 'GroupName,FileSource,FileVersion,VideoCodecs,VideoResolution,AudioLanguages,SubtitleLanguages,VideoHasChapters', includeEpisodeDetails: true, }, show, @@ -85,10 +86,14 @@ const QuickSelectModal = ({ onClose, seriesId, show }: Props) => { ) )} +  -  + {`v${group.FileVersion}`}
{group.FileSource}  |  + {group.VideoCodecs?.toUpperCase()} +  |  {group.VideoResolution} {group.AudioLanguages && ( <> diff --git a/src/hooks/useMediaInfo.ts b/src/hooks/useMediaInfo.ts index 0f6ea6919..c3b712f41 100644 --- a/src/hooks/useMediaInfo.ts +++ b/src/hooks/useMediaInfo.ts @@ -1,99 +1,82 @@ import { useMemo } from 'react'; -import { get, map, toNumber } from 'lodash'; +import { map, toNumber } from 'lodash'; import type { FileType } from '@/core/types/api/file'; import type { FileInfo } from '@/core/types/models/file'; -export const useVideoInfo = (file?: T) => { - const videoInfo: string[] = useMemo(() => { - if (!file) return []; - - const info: string[] = []; - const VideoSource = get(file, 'AniDB.Source'); - if (VideoSource) { - info.push(VideoSource); - } - - const VideoBitDepth = file?.MediaInfo?.Video?.[0]?.BitDepth; - if (VideoBitDepth) { - info.push(`${VideoBitDepth}-bit`); - } - - const VideoBitRate = file?.MediaInfo?.Video?.[0]?.BitRate; - if (VideoBitRate) { - info.push(`${Math.round(toNumber(VideoBitRate) / 1024)} kb/s`); - } - - const VideoResolution = file?.MediaInfo?.Video?.[0]?.Resolution; - if (VideoResolution) { - info.push(VideoResolution); - } - - const VideoWidth = file?.MediaInfo?.Video?.[0]?.Width; - const VideoHeight = file?.MediaInfo?.Video?.[0]?.Height; - if (VideoWidth && VideoHeight) { - info.push(`${VideoWidth}x${VideoHeight}`); - } - - return info; - }, [file]); - return videoInfo; -}; +const getVideoInfo = (file: FileType) => { + const videoInfo = file.MediaInfo?.Video?.[0]; + if (!videoInfo) return []; -export const useAudioInfo = (file?: T) => { - const audioInfo: string[] = useMemo(() => { - if (!file) return []; + const info = [ + videoInfo.Codec.Simplified.toUpperCase(), + videoInfo.Resolution, + ]; - const info: string[] = []; - const AudioFormat = file?.MediaInfo?.Audio?.[0]?.Format.Name; - const AudioLanguages = map(file?.MediaInfo?.Audio, item => item.LanguageCode).filter(item => !!item); + const VideoBitDepth = file.MediaInfo?.Video?.[0]?.BitDepth; + if (VideoBitDepth) { + info.push(`${VideoBitDepth}-bit`); + } - if (AudioFormat) { - info.push(AudioFormat); - } + if (videoInfo.BitRate) { + info.push(`${Math.round(toNumber(videoInfo.BitRate) / 1024)} kb/s`); + } - if (AudioLanguages && AudioLanguages.length > 0) { - info.push(`${AudioLanguages.length > 1 ? 'Multi Audio' : 'Audio'} (${AudioLanguages.join(',')})`); - } + if (videoInfo.Width && videoInfo.Height) { + info.push(`${videoInfo.Width}x${videoInfo.Height}`); + } - const SubtitleLanguages = map(file?.MediaInfo?.Subtitles, item => item.LanguageCode).filter(item => !!item); - if (SubtitleLanguages && SubtitleLanguages.length > 0) { - info.push(`${SubtitleLanguages.length > 1 ? 'Multi Subs' : 'Subs'} (${SubtitleLanguages.join(',')})`); - } + return info; +}; - return info; - }, [file]); - return audioInfo; +const getAudioInfo = (file: FileType) => { + const info: string[] = []; + + if (file.MediaInfo?.Audio?.[0]?.Format?.Name) { + info.push(file.MediaInfo.Audio[0].Format.Name); + } + + const audioLanguages = map(file.MediaInfo?.Audio, item => item.LanguageCode).filter(item => !!item); + if (audioLanguages && audioLanguages.length > 0) { + info.push(`${audioLanguages.length > 1 ? 'Multi Audio' : 'Audio'} (${audioLanguages.join(', ')})`); + } + + const subtitleLanguages = map(file.MediaInfo?.Subtitles, item => item.LanguageCode).filter(item => !!item); + if (subtitleLanguages && subtitleLanguages.length > 0) { + info.push(`${subtitleLanguages.length > 1 ? 'Multi Subs' : 'Subs'} (${subtitleLanguages.join(',')})`); + } + + return info; }; -const useMediaInfo = (file?: T): FileInfo => { - const videoInfo = useVideoInfo(file); - const audioInfo = useAudioInfo(file); - const { fileName, folderPath } = useMemo(() => { - const absolutePath = file?.Locations?.[0]?.AbsolutePath ?? '??'; +const useMediaInfo = (file: FileType): FileInfo => + useMemo(() => { + const videoInfo = getVideoInfo(file); + const audioInfo = getAudioInfo(file); + + const absolutePath = file.Locations?.[0]?.AbsolutePath ?? '??'; + const fileName = absolutePath.split(/[/\\]+/).pop(); + const folderPath = absolutePath.slice(0, absolutePath.replaceAll('\\', '/').lastIndexOf('/') + 1); + + const groupInfo = [file.AniDB?.ReleaseGroup?.Name ?? 'Unknown']; + if (file.AniDB?.Source) groupInfo.push(file.AniDB.Source); + if (file.AniDB?.Version) groupInfo.push(`v${file.AniDB.Version}`); + return { - fileName: absolutePath.split(/[/\\]+/).pop(), - folderPath: absolutePath.slice(0, absolutePath.replaceAll('\\', '/').lastIndexOf('/') + 1), + Name: fileName ?? '', + Location: folderPath ?? '', + Size: file.Size ?? 0, + Group: groupInfo.join(' | '), + Hashes: { + ED2K: file.Hashes?.ED2K ?? '', + SHA1: file.Hashes?.SHA1 ?? '', + CRC32: file.Hashes?.CRC32 ?? '', + MD5: file.Hashes?.MD5 ?? '', + }, + VideoInfo: videoInfo, + AudioInfo: audioInfo, + Chapters: file.AniDB?.Chaptered ?? false, }; }, [file]); - return { - Name: fileName ?? '', - Location: folderPath ?? '', - Size: file?.Size ?? 0, - Group: `${file?.AniDB?.ReleaseGroup?.Name ?? 'Unknown'}${ - file?.AniDB?.Version ? (` | v${file?.AniDB?.Version}`) : '' - }`, - Hashes: { - ED2K: file?.Hashes?.ED2K ?? '', - SHA1: file?.Hashes?.SHA1 ?? '', - CRC32: file?.Hashes?.CRC32 ?? '', - MD5: file?.Hashes?.MD5 ?? '', - }, - VideoInfo: videoInfo, - AudioInfo: audioInfo, - Chapters: file?.AniDB?.Chaptered ?? false, - }; -}; - export default useMediaInfo; diff --git a/src/pages/utilities/FileSearch.tsx b/src/pages/utilities/FileSearch.tsx index 11ed8e26e..19a21ffac 100644 --- a/src/pages/utilities/FileSearch.tsx +++ b/src/pages/utilities/FileSearch.tsx @@ -15,7 +15,7 @@ import { } from '@mdi/js'; import Icon from '@mdi/react'; import cx from 'classnames'; -import { forEach, reverse } from 'lodash'; +import { forEach, get, reverse } from 'lodash'; import prettyBytes from 'pretty-bytes'; import { useDebounceValue } from 'usehooks-ts'; @@ -37,7 +37,7 @@ import { } from '@/core/react-query/file/mutations'; import { useFileQuery, useFilesInfiniteQuery } from '@/core/react-query/file/queries'; import { invalidateQueries } from '@/core/react-query/queryClient'; -import { useSeriesAniDBQuery } from '@/core/react-query/series/queries'; +import { useSeriesQuery } from '@/core/react-query/series/queries'; import { FileSortCriteriaEnum } from '@/core/types/api/file'; import useEventCallback from '@/hooks/useEventCallback'; import useFlattenListResult from '@/hooks/useFlattenListResult'; @@ -47,10 +47,6 @@ import useRowSelection from '@/hooks/useRowSelection'; import type { FileType } from '@/core/types/api/file'; import type { Updater } from 'use-immer'; -type FileSelectedProps = { - fileId: number; -}; - const Menu = ( props: { selectedRows: FileType[]; @@ -159,59 +155,11 @@ const Menu = ( ); }; -const FileDetails = (props: FileSelectedProps) => { - const { fileId } = props; - const { data: file } = useFileQuery(fileId, { - include: ['XRefs', 'MediaInfo', 'AbsolutePaths'], - includeDataFrom: ['AniDB'], - }, !!fileId); - const fileAniDbUrl = `https://anidb.net/file/${file?.AniDB?.ID}`; - const seriesShokoId = file?.SeriesIDs?.[0]?.SeriesID?.ID ?? 0; - const seriesAnidbId = file?.SeriesIDs?.[0]?.SeriesID?.AniDB; - const episodeId = file?.SeriesIDs?.[0]?.EpisodeIDs?.[0]?.ID ?? 0; - const { data: seriesInfo } = useSeriesAniDBQuery(seriesAnidbId!, !!seriesAnidbId); - const { data: episodeInfo } = useEpisodeAniDBQuery(episodeId, !!episodeId); - +const MediaInfoDetails = React.memo(({ file }: { file: FileType }) => { const mediaInfo = useMediaInfo(file); return ( -
-
-
- File Name - {file?.AniDB?.ID && ( - - - {mediaInfo.Name} -
-
-
- Series Name - {seriesInfo !== undefined && ( - -
- - Shoko - -
- - )} -
- {seriesInfo?.Titles.find(x => x.Type === 'Main')?.Name ?? 'N/A'} -
-
-
- Episode Name -
- {episodeInfo?.Title ?? 'N/A'} -
+ <>
Location @@ -258,9 +206,104 @@ const FileDetails = (props: FileSelectedProps) => {
{mediaInfo.Hashes.SHA1 ?? ''}
+ + ); +}); + +const FileDetails = React.memo(({ fileId }: { fileId: number }) => { + const { data: file, isPending: fileQueryIsPending } = useFileQuery( + fileId, + { + include: ['XRefs', 'MediaInfo', 'AbsolutePaths'], + includeDataFrom: ['AniDB'], + }, + ); + + const seriesId: number = get(file, 'SeriesIDs[0].SeriesID.ID', 0); + const { data: seriesInfo, isPending: seriesQueryIsPending } = useSeriesQuery( + seriesId, + {}, + !!seriesId, + ); + + const episodeId: number = get(file, 'SeriesIDs[0].EpisodeIDs[0].ID', 0); + const { data: episodeInfo, isPending: episodeQueryIsPending } = useEpisodeAniDBQuery( + episodeId, + !!episodeId, + ); + + const isPending = useMemo( + () => fileQueryIsPending || seriesQueryIsPending || episodeQueryIsPending, + [ + fileQueryIsPending, + seriesQueryIsPending, + episodeQueryIsPending, + ], + ); + + if (isPending) { + return ( +
+ +
+ ); + } + + if (!file) { + return ( +
+ Error fetching file data! +
+ ); + } + + return ( +
+
+
+ File Name + {file.AniDB?.ID && ( + + + {file.Locations[0]?.RelativePath?.split(/[\\/]+/g).pop()} +
+ + {seriesInfo && ( +
+
+ Series Name + +
+ + Shoko + +
+ +
+ {seriesInfo.Name} +
+ )} + + {episodeInfo && ( +
+
+ Episode Name +
+ {episodeInfo.Title} +
+ )} + +
); -}; +}); const FileSearch = () => { const [sortCriteria, setSortCriteria] = useState(-FileSortCriteriaEnum.ImportedAt); @@ -350,7 +393,7 @@ const FileSearch = () => {
{selectedRows?.length > 0 && (
-
+
@@ -365,9 +408,7 @@ const FileSearch = () => {
- +
)}