mirror of
https://github.com/stashapp/stash.git
synced 2025-12-17 20:34:37 +03:00
Sub second marker timestamp precision (#5431)
* Allow DurationInput to accept/format timestamps with milliseconds * Get current frame at sub-second precision
This commit is contained in:
@@ -297,10 +297,14 @@ export const ScenePlayer: React.FC<IScenePlayerProps> = ({
|
|||||||
sendSetTimestamp((value: number) => {
|
sendSetTimestamp((value: number) => {
|
||||||
const player = getPlayer();
|
const player = getPlayer();
|
||||||
if (player && value >= 0) {
|
if (player && value >= 0) {
|
||||||
|
if (player.hasStarted() && player.paused()) {
|
||||||
|
player.currentTime(value);
|
||||||
|
} else {
|
||||||
player.play()?.then(() => {
|
player.play()?.then(() => {
|
||||||
player.currentTime(value);
|
player.currentTime(value);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}, [sendSetTimestamp, getPlayer]);
|
}, [sendSetTimestamp, getPlayer]);
|
||||||
|
|
||||||
|
|||||||
@@ -213,7 +213,7 @@ export const SceneMarkerForm: React.FC<ISceneMarkerForm> = ({
|
|||||||
value={formik.values.seconds}
|
value={formik.values.seconds}
|
||||||
setValue={(v) => formik.setFieldValue("seconds", v)}
|
setValue={(v) => formik.setFieldValue("seconds", v)}
|
||||||
onReset={() =>
|
onReset={() =>
|
||||||
formik.setFieldValue("seconds", Math.round(getPlayerPosition() ?? 0))
|
formik.setFieldValue("seconds", getPlayerPosition() ?? 0)
|
||||||
}
|
}
|
||||||
error={error}
|
error={error}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import {
|
|||||||
faChevronUp,
|
faChevronUp,
|
||||||
faClock,
|
faClock,
|
||||||
} from "@fortawesome/free-solid-svg-icons";
|
} from "@fortawesome/free-solid-svg-icons";
|
||||||
import React, { useState } from "react";
|
import React, { useMemo, useState } from "react";
|
||||||
import { Button, ButtonGroup, InputGroup, Form } from "react-bootstrap";
|
import { Button, ButtonGroup, InputGroup, Form } from "react-bootstrap";
|
||||||
import { Icon } from "./Icon";
|
import { Icon } from "./Icon";
|
||||||
import TextUtils from "src/utils/text";
|
import TextUtils from "src/utils/text";
|
||||||
@@ -19,6 +19,8 @@ interface IProps {
|
|||||||
allowNegative?: boolean;
|
allowNegative?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const includeMS = true;
|
||||||
|
|
||||||
export const DurationInput: React.FC<IProps> = ({
|
export const DurationInput: React.FC<IProps> = ({
|
||||||
disabled,
|
disabled,
|
||||||
value,
|
value,
|
||||||
@@ -96,17 +98,20 @@ export const DurationInput: React.FC<IProps> = ({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let inputValue = "";
|
const inputValue = useMemo(() => {
|
||||||
if (tmpValue !== undefined) {
|
if (tmpValue !== undefined) {
|
||||||
inputValue = tmpValue;
|
return tmpValue;
|
||||||
} else if (value !== null && value !== undefined) {
|
} else if (value !== null && value !== undefined) {
|
||||||
inputValue = TextUtils.secondsToTimestamp(value);
|
return TextUtils.secondsToTimestamp(value, includeMS);
|
||||||
}
|
}
|
||||||
|
}, [value, tmpValue]);
|
||||||
|
|
||||||
|
const format = "hh:mm:ss.ms";
|
||||||
|
|
||||||
if (placeholder) {
|
if (placeholder) {
|
||||||
placeholder = `${placeholder} (hh:mm:ss)`;
|
placeholder = `${placeholder} (${format})`;
|
||||||
} else {
|
} else {
|
||||||
placeholder = "hh:mm:ss";
|
placeholder = format;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -151,16 +151,20 @@ const fileSizeFractionalDigits = (unit: Unit) => {
|
|||||||
return 0;
|
return 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Converts seconds to a hh:mm:ss or mm:ss timestamp.
|
// Converts seconds to a [hh:]mm:ss[.ffff] where hh is only shown if hours is non-zero,
|
||||||
|
// and ffff is shown only if frameRate is set, and the seconds includes a fractional component.
|
||||||
// A negative input will result in a -hh:mm:ss or -mm:ss output.
|
// A negative input will result in a -hh:mm:ss or -mm:ss output.
|
||||||
// Fractional inputs are truncated.
|
const secondsToTimestamp = (secondsInput: number, includeMS?: boolean) => {
|
||||||
const secondsToTimestamp = (seconds: number) => {
|
|
||||||
let neg = false;
|
let neg = false;
|
||||||
if (seconds < 0) {
|
if (secondsInput < 0) {
|
||||||
neg = true;
|
neg = true;
|
||||||
seconds = -seconds;
|
secondsInput = -secondsInput;
|
||||||
}
|
}
|
||||||
seconds = Math.trunc(seconds);
|
|
||||||
|
const fracSeconds = secondsInput % 1;
|
||||||
|
const ms = Math.round(fracSeconds * 1000);
|
||||||
|
|
||||||
|
let seconds = Math.trunc(secondsInput);
|
||||||
|
|
||||||
const s = seconds % 60;
|
const s = seconds % 60;
|
||||||
seconds = (seconds - s) / 60;
|
seconds = (seconds - s) / 60;
|
||||||
@@ -177,6 +181,11 @@ const secondsToTimestamp = (seconds: number) => {
|
|||||||
ret = String(m).padStart(2, "0") + ":" + ret;
|
ret = String(m).padStart(2, "0") + ":" + ret;
|
||||||
ret = String(h) + ":" + ret;
|
ret = String(h) + ":" + ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (includeMS && ms > 0) {
|
||||||
|
ret += "." + ms.toString().padStart(3, "0");
|
||||||
|
}
|
||||||
|
|
||||||
if (neg) {
|
if (neg) {
|
||||||
return "-" + ret;
|
return "-" + ret;
|
||||||
} else {
|
} else {
|
||||||
@@ -202,6 +211,24 @@ const timestampToSeconds = (v: string | null | undefined) => {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let secondsPart = splits[splits.length - 1];
|
||||||
|
let msFrac = 0;
|
||||||
|
if (secondsPart.includes(".")) {
|
||||||
|
const secondsParts = secondsPart.split(".");
|
||||||
|
if (secondsParts.length !== 2) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
secondsPart = secondsParts[0];
|
||||||
|
|
||||||
|
const msPart = parseInt(secondsParts[1], 10);
|
||||||
|
if (Number.isNaN(msPart)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
msFrac = msPart / 1000;
|
||||||
|
}
|
||||||
|
|
||||||
let seconds = 0;
|
let seconds = 0;
|
||||||
let factor = 1;
|
let factor = 1;
|
||||||
while (splits.length > 0) {
|
while (splits.length > 0) {
|
||||||
@@ -219,7 +246,7 @@ const timestampToSeconds = (v: string | null | undefined) => {
|
|||||||
factor *= 60;
|
factor *= 60;
|
||||||
}
|
}
|
||||||
|
|
||||||
return seconds;
|
return seconds + msFrac;
|
||||||
};
|
};
|
||||||
|
|
||||||
const fileNameFromPath = (path: string) => {
|
const fileNameFromPath = (path: string) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user