Add Handy / Funscript support (#1377)

* Add funscript route to scenes

Adds a /scene/:id/funscript route which serves a funscript file, if present.

Current convention is that these are files stored with the same path, but with the extension ".funscript".

* Look for funscript during scan

This is stored in the Scene record and used to drive UI changes for funscript support.

Currently, that's limited to a funscript link in the Scene's file info.

* Add filtering and sorting for interactive
* Add Handy connection key to interface config
* Add Handy client and placeholder component.

Uses defucilis/thehandy, but not thehandy-react as I had difficulty integrating the context with the existing components.

Instead, the expensive calculation for the server time offset is put in localStorage for reuse.

A debounce was added when scrubbing the video, as otherwise it spammed the Handy API with updates to the current offset.
This commit is contained in:
UnluckyChemical765
2021-05-23 20:34:28 -07:00
committed by GitHub
parent 33999d3e93
commit 547f6d79ad
32 changed files with 301 additions and 24 deletions

View File

@@ -295,6 +295,12 @@ func (t *ScanTask) getFileModTime() (time.Time, error) {
return ret, nil
}
func (t *ScanTask) getInteractive() bool {
_, err := os.Stat(utils.GetFunscriptPath(t.FilePath))
return err == nil
}
func (t *ScanTask) isFileModified(fileModTime time.Time, modTime models.NullSQLiteTimestamp) bool {
return !modTime.Timestamp.Equal(fileModTime)
}
@@ -376,6 +382,7 @@ func (t *ScanTask) scanScene() *models.Scene {
if err != nil {
return logError(err)
}
interactive := t.getInteractive()
if s != nil {
// if file mod time is not set, set it now
@@ -484,6 +491,20 @@ func (t *ScanTask) scanScene() *models.Scene {
}
}
if s.Interactive != interactive {
if err := t.TxnManager.WithTxn(context.TODO(), func(r models.Repository) error {
qb := r.Scene()
scenePartial := models.ScenePartial{
ID: s.ID,
Interactive: &interactive,
}
_, err := qb.Update(scenePartial)
return err
}); err != nil {
return logError(err)
}
}
return nil
}
@@ -549,8 +570,9 @@ func (t *ScanTask) scanScene() *models.Scene {
} else {
logger.Infof("%s already exists. Updating path...", t.FilePath)
scenePartial := models.ScenePartial{
ID: s.ID,
Path: &t.FilePath,
ID: s.ID,
Path: &t.FilePath,
Interactive: &interactive,
}
if err := t.TxnManager.WithTxn(context.TODO(), func(r models.Repository) error {
_, err := r.Scene().Update(scenePartial)
@@ -580,8 +602,9 @@ func (t *ScanTask) scanScene() *models.Scene {
Timestamp: fileModTime,
Valid: true,
},
CreatedAt: models.SQLiteTimestamp{Timestamp: currentTime},
UpdatedAt: models.SQLiteTimestamp{Timestamp: currentTime},
CreatedAt: models.SQLiteTimestamp{Timestamp: currentTime},
UpdatedAt: models.SQLiteTimestamp{Timestamp: currentTime},
Interactive: interactive,
}
if t.UseFileMetadata {