Files
spacedrive/interface/app/$libraryId/overview/LibraryStats.tsx
Vítor Vasconcellos e797b02e65 Media metadata extraction & Thumbnailer rework (#2285)
* initial ffprobe commit

* Working slim down version ffprobe

* Auto format ffprobe and deps source

* Remove show_pixel_formats logic
- Fix do_bitexact incorrect check in main after last changes
- Fix some clangd warning

* Remove show_* and print_format options and their respective logic

* Rework ffprobe into simple_ffprobe
- Simplify ffprobe logic into a simple program that gather and print a media file metadata

* Reduce the amount of ffmpeg log messages while generating thumbnails

* Fix completly wrong comments

* mend

* Start modeling ffmpeg extracted metadata on schema
 - Start porting ffprobe code to rust
 - Rename some references to media_data to exif_data

* Finish modeling media info data
 - Add MediaProgram, MediaStream, MediaCodec, MediaVideoProps, MediaAudioProps, MediaSubtitleProps to Schema
 - Fix simple_ffproble to use its custom print_codec, instead of ffmpeg's impl

* Add relation between MediaInfo and FilePath
 - Remove shared properties from MediaInfo and related structs
 - Implement Iterator for FFmpegDict

* Fix and update schema

* Data models and start populating MediaInfo in rust

* Finish populating media info, chapters and program

* Improve FFmpegFormatContext data raw pointer access
 - Implement stream data gathering

* Impl FFmpegCodecContext, retrieve codec information
 - Improve some unsafe pointer uses
 - Impl from FFmpegFormatContext to MediaInfo conversion

* Fix FFmpegDict Drop

* Fix some crago warnings

* Impl retrieval of video props
 - Fix C char* to Rust String convertion

* Impl retrieval of audio and subtitle props
 - Fill props for MediaCodec

* Remove simple_ffprobe now that the Rust impl is done

* Fix schema to match actually retrieved media info
 - Fix import some FFmpeg constants instead of directly using values

* Rework movie_decoder
 - Re-implement create_scale_string and add support anamorphic video
 - Improve C pointer access for FFmpegFormatContext and FFmpegCodecContext
 - Use newer FFmpeg abstractions in movie_decoder

* Fix incorrect props when initializing MovieDecoder

* Remove unecessary lifetimes

* Added more native wrappers for some FFmpeg native objects used in movie_decoder

* Remove FFmpegPacket
 - Some more improvements to movie_decoder

* WIP

* Some small fixes

* More fixes
Rename movie_decoder to frame_decoder
Remove more references to film_strips

* fmt

* Fix duplicate migration for job error changes

* fix rebase

* Solving segfaults, fuck C lang

Co-authored-by: Vítor Vasconcellos <HeavenVolkoff@users.noreply.github.com>

* Update rust to version 1.77
 - Pin rust version with rust-toolchain.toml
 - Change from dtolnay/rust-toolchain to IronCoreLabs/rust-toolchain for rust-toolchain support
 - Remove unused function and imports
 - Replace most CString uses with new c literal string

* More segfault solving and other minor fixes

Co-authored-by: Vítor Vasconcellos <HeavenVolkoff@users.noreply.github.com>

* Fix ffmpeg rotation filter breaking portrait video thumbnails #2150
 - Plus some other misc fixes

* Auto format

* Retrieve video/audio metadata on frontend

* Auto format

* First draft on ffmpeg data save on db

Co-authored-by: Vítor Vasconcellos <HeavenVolkoff@users.noreply.github.com>

* Fix some incorrect changes to prisma schema

* Some fixes for the FFmpegData schema
 - Expand logic to save FFmpegData to db

* A ton of things

Co-authored-by: Vítor Vasconcellos <HeavenVolkoff@users.noreply.github.com>

* Integrating ffmpeg media data in jobs and API

* Rspc can't BigInt

* 🙄

* Add initial ffmpeg metadata entries to Inspector
 - Fix ephemeral metadata api to match the files metadata api call

* Fix Inspector not showing ffmpeg metadata

* Add bitrate, start time and chapters video metadata to Inspector
- Fix backend BigInt conversion incorrectly using i32 instead of u32
- Change FFmpegFormatContext/FFmpegMetaData bit_rate to i64
- Rename byteSize to humanizeSize
- Expand humanizeSize logic to allow handling bits and Binary units
- Move capitalize to @sd/client utils

* Solving some issues

* Fix ffmpeg probe getting incorrect stream id and breaking database unique constraint
 - Fix humanizeSize breaking when receiving floating numbers
 - Fix incorrect equality in StatCard
 - Fix unhandled error in Dialog when trying to remove an unknown dialog

* fmt

* small improvements
 - Remove some unecessary recursion_limit directive
 - Remove unused app_image releated functions
 - Fix metadata query enabled flag

* Add migration for ffmpeg media data

* Fix cypress test

* Requested changes

* Implement feedback
 - Update locale keys for all languages
 - Add pnpm command to update all language keys

* Fix thumb reactivity in non indexed locations

---------

Co-authored-by: Ericson Soares <ericson.ds999@gmail.com>
Co-authored-by: Vítor Vasconcellos <HeavenVolkoff@users.noreply.github.com>
2024-05-09 02:20:28 +00:00

118 lines
3.3 KiB
TypeScript

import { Info } from '@phosphor-icons/react';
import clsx from 'clsx';
import { useEffect, useState } from 'react';
import { humanizeSize, Statistics, useLibraryContext, useLibraryQuery } from '@sd/client';
import { Tooltip } from '@sd/ui';
import { useCounter, useLocale } from '~/hooks';
interface StatItemProps {
title: string;
bytes: bigint;
isLoading: boolean;
info?: string;
}
let mounted = false;
const StatItem = (props: StatItemProps) => {
const { title, bytes, isLoading } = props;
// This is important to the counter working.
// On first render of the counter this will potentially be `false` which means the counter should the count up.
// but in a `useEffect` `mounted` will be set to `true` so that subsequent renders of the counter will not run the count up.
// The acts as a cache of the value of `mounted` on the first render of this `StateItem`.
const [isMounted] = useState(mounted);
const size = humanizeSize(bytes);
const count = useCounter({
name: title,
end: size.value,
duration: isMounted ? 0 : 1,
saveState: false
});
return (
<div
className={clsx(
'group/stat flex w-32 shrink-0 flex-col duration-75',
!bytes && 'hidden'
)}
>
<span className="whitespace-nowrap text-sm font-medium text-ink-faint">
{title}
{props.info && (
<Tooltip label={props.info}>
<Info
weight="fill"
className="-mt-0.5 ml-1 inline size-3 text-ink-faint opacity-0 transition-opacity group-hover/stat:opacity-70"
/>
</Tooltip>
)}
</span>
<span className="text-2xl">
<div
className={clsx({
hidden: isLoading
})}
>
<span className="font-black tabular-nums">{count}</span>
<span className="ml-1 text-[16px] font-medium text-ink-faint">{size.unit}</span>
</div>
</span>
</div>
);
};
const LibraryStats = () => {
const { library } = useLibraryContext();
const stats = useLibraryQuery(['library.statistics']);
useEffect(() => {
if (!stats.isLoading) mounted = true;
});
const { t } = useLocale();
const StatItemNames: Partial<Record<keyof Statistics, string>> = {
total_bytes_capacity: t('total_bytes_capacity'),
preview_media_bytes: t('preview_media_bytes'),
library_db_size: t('library_db_size'),
total_bytes_free: t('total_bytes_free'),
total_bytes_used: t('total_bytes_used')
};
const StatDescriptions: Partial<Record<keyof Statistics, string>> = {
total_bytes_capacity: t('total_bytes_capacity_description'),
preview_media_bytes: t('preview_media_bytes_description'),
library_db_size: t('library_db_size_description'),
total_bytes_free: t('total_bytes_free_description'),
total_bytes_used: t('total_bytes_used_description')
};
const displayableStatItems = Object.keys(
StatItemNames
) as unknown as keyof typeof StatItemNames;
return (
<div className="flex w-full">
<div className="flex gap-3 overflow-hidden">
{Object.entries(stats?.data?.statistics || []).map(([key, value]) => {
if (!displayableStatItems.includes(key)) return null;
return (
<StatItem
key={`${library.uuid} ${key}`}
title={StatItemNames[key as keyof Statistics]!}
bytes={BigInt(value)}
isLoading={stats.isLoading}
info={StatDescriptions[key as keyof Statistics]}
/>
);
})}
</div>
</div>
);
};
export default LibraryStats;