Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Markers can have end time #5311

Open
wants to merge 6 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions graphql/schema/types/scene-marker.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ type SceneMarker {
id: ID!
scene: Scene!
title: String!
"The required start time of the marker (in seconds). Supports decimals."
seconds: Float!
"The optional end time of the marker (in seconds). Supports decimals."
end_seconds: Float
primary_tag: Tag!
tags: [Tag!]!
created_at: Time!
Expand All @@ -18,7 +21,10 @@ type SceneMarker {

input SceneMarkerCreateInput {
title: String!
"The required start time of the marker (in seconds). Supports decimals."
seconds: Float!
"The optional end time of the marker (in seconds). Supports decimals."
end_seconds: Float
scene_id: ID!
primary_tag_id: ID!
tag_ids: [ID!]
Expand All @@ -27,7 +33,10 @@ input SceneMarkerCreateInput {
input SceneMarkerUpdateInput {
id: ID!
title: String
"The start time of the marker (in seconds). Supports decimals."
seconds: Float
"The end time of the marker (in seconds). Supports decimals."
end_seconds: Float
scene_id: ID
primary_tag_id: ID
tag_ids: [ID!]
Expand Down
28 changes: 27 additions & 1 deletion internal/api/resolver_mutation_scene.go
Original file line number Diff line number Diff line change
Expand Up @@ -655,6 +655,16 @@ func (r *mutationResolver) SceneMarkerCreate(ctx context.Context, input SceneMar
newMarker.PrimaryTagID = primaryTagID
newMarker.SceneID = sceneID

if input.EndSeconds != nil {
newMarker.EndSeconds = *input.EndSeconds
// Validate that end_seconds is not less than seconds when it's not -1
if newMarker.EndSeconds != -1 && newMarker.EndSeconds < newMarker.Seconds {
return nil, fmt.Errorf("end_seconds (%f) must be greater than or equal to seconds (%f)", newMarker.EndSeconds, newMarker.Seconds)
}
} else {
newMarker.EndSeconds = -1
}

tagIDs, err := stringslice.StringSliceToIntSlice(input.TagIds)
if err != nil {
return nil, fmt.Errorf("converting tag ids: %w", err)
Expand Down Expand Up @@ -695,6 +705,9 @@ func (r *mutationResolver) SceneMarkerUpdate(ctx context.Context, input SceneMar

updatedMarker.Title = translator.optionalString(input.Title, "title")
updatedMarker.Seconds = translator.optionalFloat64(input.Seconds, "seconds")
if input.EndSeconds != nil {
updatedMarker.EndSeconds = translator.optionalFloat64(input.EndSeconds, "end_seconds")
}
updatedMarker.SceneID, err = translator.optionalIntFromString(input.SceneID, "scene_id")
if err != nil {
return nil, fmt.Errorf("converting scene id: %w", err)
Expand Down Expand Up @@ -735,6 +748,19 @@ func (r *mutationResolver) SceneMarkerUpdate(ctx context.Context, input SceneMar
return fmt.Errorf("scene marker with id %d not found", markerID)
}

// Validate end_seconds
newSeconds := existingMarker.Seconds
if updatedMarker.Seconds.Set {
newSeconds = updatedMarker.Seconds.Value
}
newEndSeconds := existingMarker.EndSeconds
if updatedMarker.EndSeconds.Set {
newEndSeconds = updatedMarker.EndSeconds.Value
}
if newEndSeconds != -1 && newEndSeconds < newSeconds {
return fmt.Errorf("end_seconds (%f) must be greater than or equal to seconds (%f)", newEndSeconds, newSeconds)
}

newMarker, err := qb.UpdatePartial(ctx, markerID, updatedMarker)
if err != nil {
return err
Expand All @@ -749,7 +775,7 @@ func (r *mutationResolver) SceneMarkerUpdate(ctx context.Context, input SceneMar
}

// remove the marker preview if the scene changed or if the timestamp was changed
if existingMarker.SceneID != newMarker.SceneID || existingMarker.Seconds != newMarker.Seconds {
if existingMarker.SceneID != newMarker.SceneID || existingMarker.Seconds != newMarker.Seconds || existingMarker.EndSeconds != newMarker.EndSeconds {
seconds := int(existingMarker.Seconds)
if err := fileDeleter.MarkMarkerFiles(existingScene, seconds); err != nil {
return err
Expand Down
2 changes: 2 additions & 0 deletions pkg/models/model_scene_marker.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ type SceneMarker struct {
ID int `json:"id"`
Title string `json:"title"`
Seconds float64 `json:"seconds"`
EndSeconds float64 `json:"end_seconds"`
PrimaryTagID int `json:"primary_tag_id"`
SceneID int `json:"scene_id"`
CreatedAt time.Time `json:"created_at"`
Expand All @@ -27,6 +28,7 @@ func NewSceneMarker() SceneMarker {
type SceneMarkerPartial struct {
Title OptionalString
Seconds OptionalFloat64
EndSeconds OptionalFloat64
PrimaryTagID OptionalInt
SceneID OptionalInt
CreatedAt OptionalTime
Expand Down
2 changes: 1 addition & 1 deletion pkg/sqlite/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ const (
cacheSizeEnv = "STASH_SQLITE_CACHE_SIZE"
)

var appSchemaVersion uint = 68
var appSchemaVersion uint = 69

//go:embed migrations/*.sql
var migrationsBox embed.FS
Expand Down
1 change: 1 addition & 0 deletions pkg/sqlite/migrations/69_markers_end.up.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ALTER TABLE scene_markers ADD COLUMN end_seconds FLOAT NOT NULL DEFAULT -1;
4 changes: 4 additions & 0 deletions pkg/sqlite/scene_marker.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,14 @@ type sceneMarkerRow struct {
SceneID int `db:"scene_id"`
CreatedAt Timestamp `db:"created_at"`
UpdatedAt Timestamp `db:"updated_at"`
EndSeconds float64 `db:"end_seconds"`
}

func (r *sceneMarkerRow) fromSceneMarker(o models.SceneMarker) {
r.ID = o.ID
r.Title = o.Title
r.Seconds = o.Seconds
r.EndSeconds = o.EndSeconds
r.PrimaryTagID = o.PrimaryTagID
r.SceneID = o.SceneID
r.CreatedAt = Timestamp{Timestamp: o.CreatedAt}
Expand All @@ -48,6 +50,7 @@ func (r *sceneMarkerRow) resolve() *models.SceneMarker {
ID: r.ID,
Title: r.Title,
Seconds: r.Seconds,
EndSeconds: r.EndSeconds,
PrimaryTagID: r.PrimaryTagID,
SceneID: r.SceneID,
CreatedAt: r.CreatedAt.Timestamp,
Expand All @@ -69,6 +72,7 @@ func (r *sceneMarkerRowRecord) fromPartial(o models.SceneMarkerPartial) {
r.set("title", o.Title.Value)
}
r.setFloat64("seconds", o.Seconds)
r.setFloat64("end_seconds", o.EndSeconds)
r.setInt("primary_tag_id", o.PrimaryTagID)
r.setInt("scene_id", o.SceneID)
r.setTimestamp("created_at", o.CreatedAt)
Expand Down
1 change: 1 addition & 0 deletions ui/v2.5/graphql/data/scene-marker.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ fragment SceneMarkerData on SceneMarker {
id
title
seconds
end_seconds
stream
preview
screenshot
Expand Down
4 changes: 4 additions & 0 deletions ui/v2.5/graphql/mutations/scene-marker.graphql
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
mutation SceneMarkerCreate(
$title: String!
$seconds: Float!
$end_seconds: Float!
$scene_id: ID!
$primary_tag_id: ID!
$tag_ids: [ID!] = []
Expand All @@ -9,6 +10,7 @@ mutation SceneMarkerCreate(
input: {
title: $title
seconds: $seconds
end_seconds: $end_seconds
scene_id: $scene_id
primary_tag_id: $primary_tag_id
tag_ids: $tag_ids
Expand All @@ -22,6 +24,7 @@ mutation SceneMarkerUpdate(
$id: ID!
$title: String!
$seconds: Float!
$end_seconds: Float!
$scene_id: ID!
$primary_tag_id: ID!
$tag_ids: [ID!] = []
Expand All @@ -31,6 +34,7 @@ mutation SceneMarkerUpdate(
id: $id
title: $title
seconds: $seconds
end_seconds: $end_seconds
scene_id: $scene_id
primary_tag_id: $primary_tag_id
tag_ids: $tag_ids
Expand Down
8 changes: 7 additions & 1 deletion ui/v2.5/src/components/Scenes/SceneDetails/PrimaryTags.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,13 @@ export const PrimaryTags: React.FC<IPrimaryTags> = ({
<FormattedMessage id="actions.edit" />
</Button>
</div>
<div>{TextUtils.secondsToTimestamp(marker.seconds)}</div>
<div>
{typeof marker.end_seconds === "number" && marker.end_seconds !== -1
? `${TextUtils.secondsToTimestamp(
marker.seconds
)}-${TextUtils.secondsToTimestamp(marker.end_seconds)}`
: TextUtils.secondsToTimestamp(marker.seconds)}
</div>
<div className="card-section centered">{tags}</div>
</div>
);
Expand Down
45 changes: 45 additions & 0 deletions ui/v2.5/src/components/Scenes/SceneDetails/SceneMarkerForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,20 @@ export const SceneMarkerForm: React.FC<ISceneMarkerForm> = ({
const schema = yup.object({
title: yup.string().ensure(),
seconds: yup.number().min(0).required(),
end_seconds: yup
.number()
.min(-1)
.test(
"is-greater-than-seconds",
"End time must be greater than or equal to start time",
function (value) {
return (
value !== undefined &&
(value === -1 || value >= this.parent.seconds)
);
}
)
.required(),
primary_tag_id: yup.string().required(),
tag_ids: yup.array(yup.string().required()).defined(),
});
Expand All @@ -53,6 +67,7 @@ export const SceneMarkerForm: React.FC<ISceneMarkerForm> = ({
() => ({
title: marker?.title ?? "",
seconds: marker?.seconds ?? Math.round(getPlayerPosition() ?? 0),
end_seconds: marker?.end_seconds ?? -1,
primary_tag_id: marker?.primary_tag.id ?? "",
tag_ids: marker?.tags.map((tag) => tag.id) ?? [],
}),
Expand Down Expand Up @@ -205,6 +220,35 @@ export const SceneMarkerForm: React.FC<ISceneMarkerForm> = ({
return renderField("seconds", title, control);
}

function renderEndTimeField() {
const { error } = formik.getFieldMeta("end_seconds");

const title = intl.formatMessage({ id: "time_end" });
const control = (
<>
<DurationInput
value={formik.values.end_seconds}
setValue={(v) => formik.setFieldValue("end_seconds", v)}
onReset={() =>
formik.setFieldValue(
"end_seconds",
Math.round(getPlayerPosition() ?? 0)
)
}
error={error}
allowNegative={true}
/>
{formik.touched.end_seconds && formik.errors.end_seconds && (
<Form.Control.Feedback type="invalid">
{formik.errors.end_seconds}
</Form.Control.Feedback>
)}
</>
);

return renderField("end_seconds", title, control);
}

function renderTagsField() {
const title = intl.formatMessage({ id: "tags" });
const control = (
Expand All @@ -225,6 +269,7 @@ export const SceneMarkerForm: React.FC<ISceneMarkerForm> = ({
{renderTitleField()}
{renderPrimaryTagField()}
{renderTimeField()}
{renderEndTimeField()}
{renderTagsField()}
</div>
<div className="buttons-container px-3">
Expand Down
8 changes: 7 additions & 1 deletion ui/v2.5/src/components/Wall/WallItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,13 @@ export const WallItem = <T extends WallItemType>({
case "sceneMarker":
const sceneMarker = data as GQL.SceneMarkerDataFragment;
const newTitle = markerTitle(sceneMarker);
const seconds = TextUtils.secondsToTimestamp(sceneMarker.seconds);
const seconds =
typeof sceneMarker.end_seconds === "number" &&
sceneMarker.end_seconds !== -1
? `${TextUtils.secondsToTimestamp(
sceneMarker.seconds
)}-${TextUtils.secondsToTimestamp(sceneMarker.end_seconds)}`
: TextUtils.secondsToTimestamp(sceneMarker.seconds);
if (newTitle) {
return `${newTitle} - ${seconds}`;
} else {
Expand Down
1 change: 1 addition & 0 deletions ui/v2.5/src/locales/en-GB.json
Original file line number Diff line number Diff line change
Expand Up @@ -1451,6 +1451,7 @@
"tags": "Tags",
"tattoos": "Tattoos",
"time": "Time",
"time_end": "Time End",
"title": "Title",
"toast": {
"added_entity": "Added {count, plural, one {{singularEntity}} other {{pluralEntity}}}",
Expand Down
Loading