mirror of
https://github.com/stashapp/stash.git
synced 2025-12-17 20:34:37 +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:
@@ -1,4 +1,4 @@
|
||||
import React, { useEffect, useRef } from "react";
|
||||
import React, { useEffect, useMemo, useRef } from "react";
|
||||
import { Button, ButtonGroup } from "react-bootstrap";
|
||||
import { Link } from "react-router-dom";
|
||||
import cx from "classnames";
|
||||
@@ -25,6 +25,7 @@ import {
|
||||
faMapMarkerAlt,
|
||||
faTag,
|
||||
} from "@fortawesome/free-solid-svg-icons";
|
||||
import { objectPath, objectTitle } from "src/core/files";
|
||||
|
||||
interface IScenePreviewProps {
|
||||
isPortrait: boolean;
|
||||
@@ -92,6 +93,11 @@ export const SceneCard: React.FC<ISceneCardProps> = (
|
||||
) => {
|
||||
const { configuration } = React.useContext(ConfigurationContext);
|
||||
|
||||
const file = useMemo(
|
||||
() => (props.scene.files.length > 0 ? props.scene.files[0] : undefined),
|
||||
[props.scene]
|
||||
);
|
||||
|
||||
// studio image is missing if it uses the default
|
||||
const missingStudioImage = props.scene.studio?.image_path?.endsWith(
|
||||
"?default=true"
|
||||
@@ -101,8 +107,8 @@ export const SceneCard: React.FC<ISceneCardProps> = (
|
||||
|
||||
function maybeRenderSceneSpecsOverlay() {
|
||||
let sizeObj = null;
|
||||
if (props.scene.file.size) {
|
||||
sizeObj = TextUtils.fileSize(parseInt(props.scene.file.size));
|
||||
if (file?.size) {
|
||||
sizeObj = TextUtils.fileSize(file.size);
|
||||
}
|
||||
return (
|
||||
<div className="scene-specs-overlay">
|
||||
@@ -119,19 +125,16 @@ export const SceneCard: React.FC<ISceneCardProps> = (
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
{props.scene.file.width && props.scene.file.height ? (
|
||||
{file?.width && file?.height ? (
|
||||
<span className="overlay-resolution">
|
||||
{" "}
|
||||
{TextUtils.resolution(
|
||||
props.scene.file.width,
|
||||
props.scene.file.height
|
||||
)}
|
||||
{TextUtils.resolution(file?.width, file?.height)}
|
||||
</span>
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
{(props.scene.file.duration ?? 0) >= 1
|
||||
? TextUtils.secondsToTimestamp(props.scene.file.duration ?? 0)
|
||||
{(file?.duration ?? 0) >= 1
|
||||
? TextUtils.secondsToTimestamp(file?.duration ?? 0)
|
||||
: ""}
|
||||
</div>
|
||||
);
|
||||
@@ -300,11 +303,15 @@ export const SceneCard: React.FC<ISceneCardProps> = (
|
||||
}
|
||||
|
||||
function maybeRenderDupeCopies() {
|
||||
if (props.scene.phash) {
|
||||
const phash = file
|
||||
? file.fingerprints.find((fp) => fp.type === "phash")
|
||||
: undefined;
|
||||
|
||||
if (phash) {
|
||||
return (
|
||||
<div className="other-copies extra-scene-info">
|
||||
<Button
|
||||
href={NavUtils.makeScenesPHashMatchUrl(props.scene.phash)}
|
||||
href={NavUtils.makeScenesPHashMatchUrl(phash.value)}
|
||||
className="minimal"
|
||||
>
|
||||
<Icon icon={faCopy} />
|
||||
@@ -344,9 +351,8 @@ export const SceneCard: React.FC<ISceneCardProps> = (
|
||||
}
|
||||
|
||||
function isPortrait() {
|
||||
const { file } = props.scene;
|
||||
const width = file.width ? file.width : 0;
|
||||
const height = file.height ? file.height : 0;
|
||||
const width = file?.width ? file.width : 0;
|
||||
const height = file?.height ? file.height : 0;
|
||||
return height > width;
|
||||
}
|
||||
|
||||
@@ -369,11 +375,7 @@ export const SceneCard: React.FC<ISceneCardProps> = (
|
||||
<GridCard
|
||||
className={`scene-card ${zoomIndex()}`}
|
||||
url={sceneLink}
|
||||
title={
|
||||
props.scene.title
|
||||
? props.scene.title
|
||||
: TextUtils.fileNameFromPath(props.scene.path)
|
||||
}
|
||||
title={objectTitle(props.scene)}
|
||||
linkClassName="scene-card-link"
|
||||
thumbnailSectionClassName="video-section"
|
||||
interactiveHeatmap={
|
||||
@@ -398,7 +400,9 @@ export const SceneCard: React.FC<ISceneCardProps> = (
|
||||
details={
|
||||
<div className="scene-card__details">
|
||||
<span className="scene-card__date">{props.scene.date}</span>
|
||||
<span className="file-path extra-scene-info">{props.scene.path}</span>
|
||||
<span className="file-path extra-scene-info">
|
||||
{objectPath(props.scene)}
|
||||
</span>
|
||||
<TruncatedText
|
||||
className="scene-card__description"
|
||||
text={props.scene.details}
|
||||
|
||||
Reference in New Issue
Block a user