mirror of
https://github.com/stashapp/stash.git
synced 2025-12-19 05:14:38 +03:00
File storage rewrite (#2676)
* Restructure data layer part 2 (#2599) * Refactor and separate image model * Refactor image query builder * Handle relationships in image query builder * Remove relationship management methods * Refactor gallery model/query builder * Add scenes to gallery model * Convert scene model * Refactor scene models * Remove unused methods * Add unit tests for gallery * Add image tests * Add scene tests * Convert unnecessary scene value pointers to values * Convert unnecessary pointer values to values * Refactor scene partial * Add scene partial tests * Refactor ImagePartial * Add image partial tests * Refactor gallery partial update * Add partial gallery update tests * Use zero/null package for null values * Add files and scan system * Add sqlite implementation for files/folders * Add unit tests for files/folders * Image refactors * Update image data layer * Refactor gallery model and creation * Refactor scene model * Refactor scenes * Don't set title from filename * Allow galleries to freely add/remove images * Add multiple scene file support to graphql and UI * Add multiple file support for images in graphql/UI * Add multiple file for galleries in graphql/UI * Remove use of some deprecated fields * Remove scene path usage * Remove gallery path usage * Remove path from image * Move funscript to video file * Refactor caption detection * Migrate existing data * Add post commit/rollback hook system * Lint. Comment out import/export tests * Add WithDatabase read only wrapper * Prepend tasks to list * Add 32 pre-migration * Add warnings in release and migration notes
This commit is contained in:
@@ -134,10 +134,8 @@ export const SceneDuplicateChecker: React.FC = () => {
|
||||
setEditingScenes(true);
|
||||
}
|
||||
|
||||
const renderFilesize = (filesize: string | null | undefined) => {
|
||||
const { size: parsedSize, unit } = TextUtils.fileSize(
|
||||
Number.parseInt(filesize ?? "0", 10)
|
||||
);
|
||||
const renderFilesize = (filesize: number | null | undefined) => {
|
||||
const { size: parsedSize, unit } = TextUtils.fileSize(filesize ?? 0);
|
||||
return (
|
||||
<FormattedNumber
|
||||
value={parsedSize}
|
||||
@@ -477,78 +475,84 @@ export const SceneDuplicateChecker: React.FC = () => {
|
||||
</thead>
|
||||
<tbody>
|
||||
{filteredScenes.map((group, groupIndex) =>
|
||||
group.map((scene, i) => (
|
||||
<>
|
||||
{i === 0 && groupIndex !== 0 ? (
|
||||
<tr className="separator" />
|
||||
) : undefined}
|
||||
<tr
|
||||
className={i === 0 ? "duplicate-group" : ""}
|
||||
key={scene.id}
|
||||
>
|
||||
<td>
|
||||
<Form.Check
|
||||
onChange={(e) =>
|
||||
handleCheck(e.currentTarget.checked, scene.id)
|
||||
}
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
<HoverPopover
|
||||
content={
|
||||
group.map((scene, i) => {
|
||||
const file =
|
||||
scene.files.length > 0 ? scene.files[0] : undefined;
|
||||
|
||||
return (
|
||||
<>
|
||||
{i === 0 && groupIndex !== 0 ? (
|
||||
<tr className="separator" />
|
||||
) : undefined}
|
||||
<tr
|
||||
className={i === 0 ? "duplicate-group" : ""}
|
||||
key={scene.id}
|
||||
>
|
||||
<td>
|
||||
<Form.Check
|
||||
onChange={(e) =>
|
||||
handleCheck(e.currentTarget.checked, scene.id)
|
||||
}
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
<HoverPopover
|
||||
content={
|
||||
<img
|
||||
src={scene.paths.sprite ?? ""}
|
||||
alt=""
|
||||
width={600}
|
||||
/>
|
||||
}
|
||||
placement="right"
|
||||
>
|
||||
<img
|
||||
src={scene.paths.sprite ?? ""}
|
||||
alt=""
|
||||
width={600}
|
||||
width={100}
|
||||
/>
|
||||
}
|
||||
placement="right"
|
||||
>
|
||||
<img
|
||||
src={scene.paths.sprite ?? ""}
|
||||
alt=""
|
||||
width={100}
|
||||
</HoverPopover>
|
||||
</td>
|
||||
<td className="text-left">
|
||||
<p>
|
||||
<Link to={`/scenes/${scene.id}`}>
|
||||
{scene.title
|
||||
? scene.title
|
||||
: TextUtils.fileNameFromPath(file?.path ?? "")}
|
||||
</Link>
|
||||
</p>
|
||||
<p className="scene-path">{file?.path ?? ""}</p>
|
||||
</td>
|
||||
<td className="scene-details">
|
||||
{maybeRenderPopoverButtonGroup(scene)}
|
||||
</td>
|
||||
<td>
|
||||
{file?.duration &&
|
||||
TextUtils.secondsToTimestamp(file.duration)}
|
||||
</td>
|
||||
<td>{renderFilesize(file?.size ?? 0)}</td>
|
||||
<td>{`${file?.width ?? 0}x${file?.height ?? 0}`}</td>
|
||||
<td>
|
||||
<FormattedNumber
|
||||
value={(file?.bit_rate ?? 0) / 1000000}
|
||||
maximumFractionDigits={2}
|
||||
/>
|
||||
</HoverPopover>
|
||||
</td>
|
||||
<td className="text-left">
|
||||
<p>
|
||||
<Link to={`/scenes/${scene.id}`}>
|
||||
{scene.title ??
|
||||
TextUtils.fileNameFromPath(scene.path)}
|
||||
</Link>
|
||||
</p>
|
||||
<p className="scene-path">{scene.path}</p>
|
||||
</td>
|
||||
<td className="scene-details">
|
||||
{maybeRenderPopoverButtonGroup(scene)}
|
||||
</td>
|
||||
<td>
|
||||
{scene.file.duration &&
|
||||
TextUtils.secondsToTimestamp(scene.file.duration)}
|
||||
</td>
|
||||
<td>{renderFilesize(scene.file.size)}</td>
|
||||
<td>{`${scene.file.width}x${scene.file.height}`}</td>
|
||||
<td>
|
||||
<FormattedNumber
|
||||
value={(scene.file.bitrate ?? 0) / 1000000}
|
||||
maximumFractionDigits={2}
|
||||
/>
|
||||
mbps
|
||||
</td>
|
||||
<td>{scene.file.video_codec}</td>
|
||||
<td>
|
||||
<Button
|
||||
className="edit-button"
|
||||
variant="danger"
|
||||
onClick={() => handleDeleteScene(scene)}
|
||||
>
|
||||
<FormattedMessage id="actions.delete" />
|
||||
</Button>
|
||||
</td>
|
||||
</tr>
|
||||
</>
|
||||
))
|
||||
mbps
|
||||
</td>
|
||||
<td>{file?.video_codec ?? ""}</td>
|
||||
<td>
|
||||
<Button
|
||||
className="edit-button"
|
||||
variant="danger"
|
||||
onClick={() => handleDeleteScene(scene)}
|
||||
>
|
||||
<FormattedMessage id="actions.delete" />
|
||||
</Button>
|
||||
</td>
|
||||
</tr>
|
||||
</>
|
||||
);
|
||||
})
|
||||
)}
|
||||
</tbody>
|
||||
</Table>
|
||||
|
||||
Reference in New Issue
Block a user