fix(clip): clip mode keeps its range, its focus and its keys through the player's own moves
- A mark on the very end seeks a moment short of it; while a clip is marked the end of the item neither closes the player nor steps the queue, and Plex's auto-skip waits. - Plex: Escape and Back peel what is over the picture (info, up-next, the countdown) before the clip; Back under a dialog is the dialog's; no range opens while the length is unknown. - YouTube: the focus goes back to the scissors the user can see, else the player; the panel keeps its name, Loop and focus when the view is maximised or restored under it. - A typed time stays in its field until taken, then shows where it landed; unreadable stays marked invalid; a decimal comma is read (ui/TimecodeField, the editor's fields too). - RangeTrack: a drag is its pointer's, a cancelled drag reports final, an adornment sits beside the slider, a caller-chosen focus ring (amber on the clip strip). CropFrame: a drag is its pointer's. - The editor's remove-cut is a real button at the cut's foot; a segment's keep toggle sits over the cut's hit area.
This commit is contained in:
@@ -435,6 +435,8 @@ export function useAutoSkip(opts: {
|
||||
videoRef: RefObject<HTMLVideoElement | null>;
|
||||
/** Says a refused watched mark out loud; the translated string belongs to the component layer. */
|
||||
reportWatchedError: (e: unknown) => void;
|
||||
/** No countdown while true (a clip being marked); the Skip button still skips. */
|
||||
suspended?: boolean;
|
||||
}): {
|
||||
activeMarker: PlexMarker | undefined;
|
||||
skipProgress: number | null;
|
||||
@@ -453,6 +455,7 @@ export function useAutoSkip(opts: {
|
||||
go,
|
||||
seekTo,
|
||||
videoRef,
|
||||
suspended = false,
|
||||
} = opts;
|
||||
const activeMarker: PlexMarker | undefined = markers?.find(
|
||||
(m) => abs >= m.start_s && abs < m.end_s - 1,
|
||||
@@ -513,7 +516,7 @@ export function useAutoSkip(opts: {
|
||||
if (!m) return setSkipProgress(null);
|
||||
const key = `${m.type}-${m.start_s}`;
|
||||
const enabled = (m.type === "intro" ? autoSkipIntro : autoSkipCredits) && activeAuto;
|
||||
if (!enabled || cancelledMarkerRef.current === key) return setSkipProgress(null);
|
||||
if (!enabled || suspended || cancelledMarkerRef.current === key) return setSkipProgress(null);
|
||||
if (autoSkipDelay <= 0) return doSkip(m); // immediate
|
||||
let elapsed = 0;
|
||||
setSkipProgress(0);
|
||||
@@ -541,6 +544,7 @@ export function useAutoSkip(opts: {
|
||||
autoSkipIntro,
|
||||
autoSkipCredits,
|
||||
autoSkipDelay,
|
||||
suspended,
|
||||
doSkip,
|
||||
videoRef,
|
||||
activeMarkerRef,
|
||||
|
||||
@@ -29,8 +29,13 @@ describe("parseTimecode", () => {
|
||||
expect(parseTimecode(" 90.5 ")).toBeCloseTo(90.5);
|
||||
});
|
||||
|
||||
it("takes a decimal comma, in the seconds only", () => {
|
||||
expect(parseTimecode("1:02,5")).toBeCloseTo(62.5);
|
||||
expect(parseTimecode("12,4")).toBeCloseTo(12.4);
|
||||
});
|
||||
|
||||
it("is null for what is not a timecode", () => {
|
||||
for (const v of ["", "abc", "1::2", "1:2:3:4", "-5", "1:-2"])
|
||||
for (const v of ["", "abc", "1::2", "1:2:3:4", "-5", "1:-2", "1,5:00", "1:02,5,5"])
|
||||
expect(parseTimecode(v)).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -13,9 +13,13 @@ export function formatTimecode(s: number, hours = false): string {
|
||||
return hours || h > 0 ? `${h}:${String(m).padStart(2, "0")}:${sec}` : `${m}:${sec}`;
|
||||
}
|
||||
|
||||
/** A typed timecode back to seconds: `h:mm:ss.d`, `m:ss.d` or bare seconds. Null when it is not one. */
|
||||
/** A typed timecode back to seconds: `h:mm:ss.d`, `m:ss.d` or bare seconds, its decimal a point or
|
||||
* a comma (how a Hungarian hand writes one). Null when it is not one. */
|
||||
export function parseTimecode(v: string): number | null {
|
||||
const parts = v.trim().split(":");
|
||||
const parts = v
|
||||
.trim()
|
||||
.replace(/,(\d*)$/, ".$1")
|
||||
.split(":");
|
||||
if (parts.length > 3 || parts.some((p) => p.trim() === "")) return null;
|
||||
let total = 0;
|
||||
for (const p of parts) {
|
||||
|
||||
Reference in New Issue
Block a user