Fix funscript parsing issues (#5978)

* Accept floating point numbers for at field in funscript
* Ignore type of script version field
* Write rounded ints for csv
This commit is contained in:
WithoutPants 2025-06-30 07:52:53 +10:00 committed by GitHub
parent 7215b6e918
commit 61ab6ce6bd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -28,7 +28,8 @@ type InteractiveHeatmapSpeedGenerator struct {
type Script struct { type Script struct {
// Version of Launchscript // Version of Launchscript
Version string `json:"version"` // #5600 - ignore version, don't validate type
Version json.RawMessage `json:"version"`
// Inverted causes up and down movement to be flipped. // Inverted causes up and down movement to be flipped.
Inverted bool `json:"inverted,omitempty"` Inverted bool `json:"inverted,omitempty"`
// Range is the percentage of a full stroke to use. // Range is the percentage of a full stroke to use.
@ -40,7 +41,7 @@ type Script struct {
// Action is a move at a specific time. // Action is a move at a specific time.
type Action struct { type Action struct {
// At time in milliseconds the action should fire. // At time in milliseconds the action should fire.
At int64 `json:"at"` At float64 `json:"at"`
// Pos is the place in percent to move to. // Pos is the place in percent to move to.
Pos int `json:"pos"` Pos int `json:"pos"`
@ -109,8 +110,8 @@ func (g *InteractiveHeatmapSpeedGenerator) LoadFunscriptData(path string, sceneD
// trim actions with negative timestamps to avoid index range errors when generating heatmap // trim actions with negative timestamps to avoid index range errors when generating heatmap
// #3181 - also trim actions that occur after the scene duration // #3181 - also trim actions that occur after the scene duration
loggedBadTimestamp := false loggedBadTimestamp := false
sceneDurationMilli := int64(sceneDuration * 1000) sceneDurationMilli := sceneDuration * 1000
isValid := func(x int64) bool { isValid := func(x float64) bool {
return x >= 0 && x < sceneDurationMilli return x >= 0 && x < sceneDurationMilli
} }
@ -132,7 +133,7 @@ func (g *InteractiveHeatmapSpeedGenerator) LoadFunscriptData(path string, sceneD
func (funscript *Script) UpdateIntensityAndSpeed() { func (funscript *Script) UpdateIntensityAndSpeed() {
var t1, t2 int64 var t1, t2 float64
var p1, p2 int var p1, p2 int
var intensity float64 var intensity float64
for i := range funscript.Actions { for i := range funscript.Actions {
@ -241,13 +242,13 @@ func (gt GradientTable) GetYRange(t float64) [2]float64 {
func (funscript Script) getGradientTable(numSegments int, sceneDurationMilli int64) GradientTable { func (funscript Script) getGradientTable(numSegments int, sceneDurationMilli int64) GradientTable {
const windowSize = 15 const windowSize = 15
const backfillThreshold = 500 const backfillThreshold = float64(500)
segments := make([]struct { segments := make([]struct {
count int count int
intensity int intensity int
yRange [2]float64 yRange [2]float64
at int64 at float64
}, numSegments) }, numSegments)
gradient := make(GradientTable, numSegments) gradient := make(GradientTable, numSegments)
posList := []int{} posList := []int{}
@ -297,7 +298,7 @@ func (funscript Script) getGradientTable(numSegments int, sceneDurationMilli int
// Fill in gaps in segments // Fill in gaps in segments
for i := 0; i < numSegments; i++ { for i := 0; i < numSegments; i++ {
segmentTS := (maxts / int64(numSegments)) * int64(i) segmentTS := float64((maxts / int64(numSegments)) * int64(i))
// Empty segment - fill it with the previous up to backfillThreshold ms // Empty segment - fill it with the previous up to backfillThreshold ms
if segments[i].count == 0 { if segments[i].count == 0 {
@ -406,7 +407,8 @@ func ConvertFunscriptToCSV(funscriptPath string) ([]byte, error) {
pos = convertRange(pos, 0, funscript.Range, 0, 100) pos = convertRange(pos, 0, funscript.Range, 0, 100)
} }
buffer.WriteString(fmt.Sprintf("%d,%d\r\n", action.At, pos)) // I don't know whether the csv format requires int or float, so for now we'll use int
buffer.WriteString(fmt.Sprintf("%d,%d\r\n", int(math.Round(action.At)), pos))
} }
return buffer.Bytes(), nil return buffer.Bytes(), nil
} }