From 88255ce018694224f74b02755cee9f21247c4121 Mon Sep 17 00:00:00 2001 From: Bob12224 <241886672+bob12224@users.noreply.github.com> Date: Thu, 12 Mar 2026 00:30:48 -0700 Subject: [PATCH 1/5] (feat) Adding frames for all video files, adding to the UI and graphql. --- docs/DEVELOPMENT.md | 2 + graphql/schema/types/file.graphql | 1 + graphql/schema/types/scene.graphql | 1 + internal/manager/generator_sprite.go | 2 +- internal/manager/task_export.go | 1 + pkg/ffmpeg/ffprobe.go | 1 + pkg/file/scan.go | 2 +- pkg/file/video/scan.go | 3 +- pkg/models/jsonschema/file_folder.go | 1 + pkg/models/jsonschema/scene.go | 1 + pkg/models/model_file.go | 1 + pkg/models/model_scene.go | 1 + pkg/scraper/script.go | 2 + pkg/scraper/stash.go | 2 + pkg/sqlite/database.go | 2 +- pkg/sqlite/file.go | 5 + pkg/sqlite/file_test.go | 4 + pkg/sqlite/migrations/85_postmigrate.go | 124 ++++++++++++++++++ .../migrations/85_video_file_frames.up.sql | 1 + pkg/sqlite/setup_test.go | 1 + ui/v2.5/graphql/data/file.graphql | 2 + ui/v2.5/graphql/data/scrapers.graphql | 2 + .../SceneDetails/SceneFileInfoPanel.tsx | 5 + .../src/components/Scenes/SceneListTable.tsx | 15 +++ ui/v2.5/src/locales/en-GB.json | 1 + 25 files changed, 179 insertions(+), 4 deletions(-) create mode 100644 pkg/sqlite/migrations/85_postmigrate.go create mode 100644 pkg/sqlite/migrations/85_video_file_frames.up.sql diff --git a/docs/DEVELOPMENT.md b/docs/DEVELOPMENT.md index 85c2f6f23..591da332b 100644 --- a/docs/DEVELOPMENT.md +++ b/docs/DEVELOPMENT.md @@ -34,6 +34,8 @@ NOTE: The `make` command in Windows will be `mingw32-make` with MinGW. For examp #### Ubuntu 1. Install dependencies: `sudo apt-get install golang git gcc nodejs ffmpeg -y` + * To install `pnpm`: `npm install -g pnpm` + * Check Go Version, if old (`make generate` fails) use `sudo snap install go --classic` or install from tarball ### OpenBSD diff --git a/graphql/schema/types/file.graphql b/graphql/schema/types/file.graphql index 37fb5539f..c4e916e65 100644 --- a/graphql/schema/types/file.graphql +++ b/graphql/schema/types/file.graphql @@ -88,6 +88,7 @@ type VideoFile implements BaseFile { video_codec: String! audio_codec: String! frame_rate: Float! + frames: Int! bit_rate: Int! created_at: Time! diff --git a/graphql/schema/types/scene.graphql b/graphql/schema/types/scene.graphql index 4d99e0a21..1bbc63e9e 100644 --- a/graphql/schema/types/scene.graphql +++ b/graphql/schema/types/scene.graphql @@ -6,6 +6,7 @@ type SceneFileType { width: Int height: Int framerate: Float + frames: Int bitrate: Int } diff --git a/internal/manager/generator_sprite.go b/internal/manager/generator_sprite.go index dc56fde88..c2081d9fd 100644 --- a/internal/manager/generator_sprite.go +++ b/internal/manager/generator_sprite.go @@ -105,7 +105,7 @@ func NewSpriteGenerator(videoFile ffmpeg.VideoFile, videoChecksum string, imageO slowSeek := false - // For files with small duration / low frame count try to seek using frame number intead of seconds + // For files with small duration / low frame count try to seek using frame number instead of seconds if videoFile.VideoStreamDuration < 5 || (0 < videoFile.FrameCount && videoFile.FrameCount <= int64(chunkCount)) { // some files can have FrameCount == 0, only use SlowSeek if duration < 5 if videoFile.VideoStreamDuration <= 0 { s := fmt.Sprintf("video %s: duration(%.3f)/frame count(%d) invalid, skipping sprite creation", videoFile.Path, videoFile.VideoStreamDuration, videoFile.FrameCount) diff --git a/internal/manager/task_export.go b/internal/manager/task_export.go index 01bab9430..0d272b7ff 100644 --- a/internal/manager/task_export.go +++ b/internal/manager/task_export.go @@ -441,6 +441,7 @@ func fileToJSON(f models.File) jsonschema.DirEntry { VideoCodec: ff.VideoCodec, AudioCodec: ff.AudioCodec, FrameRate: ff.FrameRate, + Frames: ff.Frames, BitRate: ff.BitRate, Interactive: ff.Interactive, InteractiveSpeed: ff.InteractiveSpeed, diff --git a/pkg/ffmpeg/ffprobe.go b/pkg/ffmpeg/ffprobe.go index 59f8ed218..e255af363 100644 --- a/pkg/ffmpeg/ffprobe.go +++ b/pkg/ffmpeg/ffprobe.go @@ -221,6 +221,7 @@ func (f *FFProbe) NewVideoFile(videoPath string) (*VideoFile, error) { "-show_format", "-show_streams", "-show_error", + "-count_frames", } // show_entries stream_side_data=rotation requires 5.x or later ffprobe diff --git a/pkg/file/scan.go b/pkg/file/scan.go index 8ff51b359..0f6ffac11 100644 --- a/pkg/file/scan.go +++ b/pkg/file/scan.go @@ -696,7 +696,7 @@ func (s *Scanner) isHandlerRequired(ctx context.Context, f models.File) bool { // Missing metadata includes the following: // - file size // - image format, width or height -// - video codec, audio codec, format, width, height, framerate or bitrate +// - video codec, audio codec, format, width, height, framerate, frames or bitrate func (s *Scanner) isMissingMetadata(ctx context.Context, f ScannedFile, existing models.File) bool { for _, h := range s.FileDecorators { if h.IsMissingMetadata(ctx, f.FS, existing) { diff --git a/pkg/file/video/scan.go b/pkg/file/video/scan.go index 21be0cd11..3a42b4627 100644 --- a/pkg/file/video/scan.go +++ b/pkg/file/video/scan.go @@ -52,6 +52,7 @@ func (d *Decorator) Decorate(ctx context.Context, fs models.FS, f models.File) ( Height: videoFile.Height, Duration: videoFile.FileDuration, FrameRate: videoFile.FrameRate, + Frames: videoFile.FrameCount, BitRate: videoFile.Bitrate, Interactive: interactive, }, nil @@ -76,6 +77,6 @@ func (d *Decorator) IsMissingMetadata(ctx context.Context, fs models.FS, f model return vf.VideoCodec == unsetString || vf.AudioCodec == unsetString || vf.Format == unsetString || vf.Width == unsetNumber || vf.Height == unsetNumber || vf.FrameRate == unsetNumber || - vf.Duration == unsetNumber || + vf.Frames == unsetNumber || vf.Duration == unsetNumber || vf.BitRate == unsetNumber || interactive != vf.Interactive } diff --git a/pkg/models/jsonschema/file_folder.go b/pkg/models/jsonschema/file_folder.go index dfe581f78..c8f819bc5 100644 --- a/pkg/models/jsonschema/file_folder.go +++ b/pkg/models/jsonschema/file_folder.go @@ -84,6 +84,7 @@ type VideoFile struct { VideoCodec string `json:"video_codec,omitempty"` AudioCodec string `json:"audio_codec,omitempty"` FrameRate float64 `json:"frame_rate,omitempty"` + Frames int64 `json:"frames,omitempty"` BitRate int64 `json:"bitrate,omitempty"` Interactive bool `json:"interactive,omitempty"` diff --git a/pkg/models/jsonschema/scene.go b/pkg/models/jsonschema/scene.go index 8f15b9c5d..fcc43e725 100644 --- a/pkg/models/jsonschema/scene.go +++ b/pkg/models/jsonschema/scene.go @@ -31,6 +31,7 @@ type SceneFile struct { Width int `json:"width"` Height int `json:"height"` Framerate string `json:"framerate"` + Frames int `json:"frame"` Bitrate int `json:"bitrate"` } diff --git a/pkg/models/model_file.go b/pkg/models/model_file.go index f6b8bdc51..eae6b926c 100644 --- a/pkg/models/model_file.go +++ b/pkg/models/model_file.go @@ -285,6 +285,7 @@ type VideoFile struct { VideoCodec string `json:"video_codec"` AudioCodec string `json:"audio_codec"` FrameRate float64 `json:"frame_rate"` + Frames int64 `json:"frames"` BitRate int64 `json:"bitrate"` Interactive bool `json:"interactive"` diff --git a/pkg/models/model_scene.go b/pkg/models/model_scene.go index 64ad34b9c..8e0c52dbc 100644 --- a/pkg/models/model_scene.go +++ b/pkg/models/model_scene.go @@ -274,6 +274,7 @@ type SceneFileType struct { Width *int `graphql:"width" json:"width"` Height *int `graphql:"height" json:"height"` Framerate *float64 `graphql:"framerate" json:"framerate"` + Frames *int `graphql:"frames" json:"frames"` Bitrate *int `graphql:"bitrate" json:"bitrate"` } diff --git a/pkg/scraper/script.go b/pkg/scraper/script.go index f8e47b5d8..36b1e6aa5 100644 --- a/pkg/scraper/script.go +++ b/pkg/scraper/script.go @@ -45,6 +45,7 @@ type videoFileInput struct { VideoCodec string `json:"video_codec,omitempty"` AudioCodec string `json:"audio_codec,omitempty"` FrameRate float64 `json:"frame_rate,omitempty"` + Frames int64 `json:"frames,omitempty"` BitRate int64 `json:"bitrate,omitempty"` Interactive bool `json:"interactive,omitempty"` @@ -106,6 +107,7 @@ func videoFileInputFromVideoFile(vf *models.VideoFile) videoFileInput { VideoCodec: vf.VideoCodec, AudioCodec: vf.AudioCodec, FrameRate: vf.FrameRate, + Frames: vf.Frames, BitRate: vf.BitRate, Interactive: vf.Interactive, InteractiveSpeed: vf.InteractiveSpeed, diff --git a/pkg/scraper/stash.go b/pkg/scraper/stash.go index 23c4b9063..9e2a739e8 100644 --- a/pkg/scraper/stash.go +++ b/pkg/scraper/stash.go @@ -307,6 +307,7 @@ type stashVideoFile struct { Width int `graphql:"width" json:"width"` Height int `graphql:"height" json:"height"` Framerate float64 `graphql:"frame_rate" json:"frame_rate"` + Frames int `graphql:"frames" json:"frames"` Bitrate int `graphql:"bit_rate" json:"bit_rate"` } @@ -318,6 +319,7 @@ func (f stashVideoFile) SceneFileType() models.SceneFileType { Width: &f.Width, Height: &f.Height, Framerate: &f.Framerate, + Frames: &f.Frames, Bitrate: &f.Bitrate, } diff --git a/pkg/sqlite/database.go b/pkg/sqlite/database.go index f8c2cdef7..7c383dc4c 100644 --- a/pkg/sqlite/database.go +++ b/pkg/sqlite/database.go @@ -34,7 +34,7 @@ const ( cacheSizeEnv = "STASH_SQLITE_CACHE_SIZE" ) -var appSchemaVersion uint = 84 +var appSchemaVersion uint = 85 //go:embed migrations/*.sql var migrationsBox embed.FS diff --git a/pkg/sqlite/file.go b/pkg/sqlite/file.go index ba925a448..8541f29db 100644 --- a/pkg/sqlite/file.go +++ b/pkg/sqlite/file.go @@ -60,6 +60,7 @@ type videoFileRow struct { VideoCodec string `db:"video_codec"` AudioCodec string `db:"audio_codec"` FrameRate float64 `db:"frame_rate"` + Frames int64 `db:"frames"` BitRate int64 `db:"bit_rate"` Interactive bool `db:"interactive"` InteractiveSpeed null.Int `db:"interactive_speed"` @@ -74,6 +75,7 @@ func (f *videoFileRow) fromVideoFile(ff models.VideoFile) { f.VideoCodec = ff.VideoCodec f.AudioCodec = ff.AudioCodec f.FrameRate = ff.FrameRate + f.Frames = ff.Frames f.BitRate = ff.BitRate f.Interactive = ff.Interactive f.InteractiveSpeed = intFromPtr(ff.InteractiveSpeed) @@ -104,6 +106,7 @@ type videoFileQueryRow struct { VideoCodec null.String `db:"video_codec"` AudioCodec null.String `db:"audio_codec"` FrameRate null.Float `db:"frame_rate"` + Frames null.Int `db:"frames"` BitRate null.Int `db:"bit_rate"` Interactive null.Bool `db:"interactive"` InteractiveSpeed null.Int `db:"interactive_speed"` @@ -118,6 +121,7 @@ func (f *videoFileQueryRow) resolve() *models.VideoFile { VideoCodec: f.VideoCodec.String, AudioCodec: f.AudioCodec.String, FrameRate: f.FrameRate.Float64, + Frames: f.Frames.Int64, BitRate: f.BitRate.Int64, Interactive: f.Interactive.Bool, InteractiveSpeed: nullIntPtr(f.InteractiveSpeed), @@ -135,6 +139,7 @@ func videoFileQueryColumns() []interface{} { table.Col("video_codec"), table.Col("audio_codec"), table.Col("frame_rate"), + table.Col("frames"), table.Col("bit_rate"), table.Col("interactive"), table.Col("interactive_speed"), diff --git a/pkg/sqlite/file_test.go b/pkg/sqlite/file_test.go index 8422390c0..b1fd7afba 100644 --- a/pkg/sqlite/file_test.go +++ b/pkg/sqlite/file_test.go @@ -41,6 +41,7 @@ func Test_fileFileStore_Create(t *testing.T) { width = 640 height = 480 framerate = 2.345 + frames = 3 bitrate int64 = 234 videoCodec = "videoCodec" audioCodec = "audioCodec" @@ -104,6 +105,7 @@ func Test_fileFileStore_Create(t *testing.T) { Width: width, Height: height, FrameRate: framerate, + Frames: frames, BitRate: bitrate, }, false, @@ -258,6 +260,7 @@ func Test_fileStore_Update(t *testing.T) { width = 640 height = 480 framerate = 2.345 + frames = 3 bitrate int64 = 234 videoCodec = "videoCodec" audioCodec = "audioCodec" @@ -323,6 +326,7 @@ func Test_fileStore_Update(t *testing.T) { Width: width, Height: height, FrameRate: framerate, + Frames: frames, BitRate: bitrate, }, false, diff --git a/pkg/sqlite/migrations/85_postmigrate.go b/pkg/sqlite/migrations/85_postmigrate.go new file mode 100644 index 000000000..4fa70c83c --- /dev/null +++ b/pkg/sqlite/migrations/85_postmigrate.go @@ -0,0 +1,124 @@ +package migrations + +import ( + "context" + "fmt" + "os/exec" + "path/filepath" + + "github.com/jmoiron/sqlx" + "github.com/stashapp/stash/pkg/ffmpeg" + "github.com/stashapp/stash/pkg/logger" + "github.com/stashapp/stash/pkg/sqlite" +) + +func post85(ctx context.Context, db *sqlx.DB) error { + logger.Info("Running post-migration for schema version 85") + + ffprobePath, _ := exec.LookPath("ffprobe") + + mm := schema85PostMigrator{ + migrator: migrator{ + db: db, + }, + ffprobe: ffmpeg.NewFFProbe(ffprobePath), + } + + return mm.migrate(ctx) +} + +type schema85PostMigrator struct { + migrator + ffprobe *ffmpeg.FFProbe +} + +func (m *schema85PostMigrator) migrate(ctx context.Context) error { + const ( + limit = 1000 + logEvery = 10000 + ) + + result := struct { + Count int `db:"count"` + }{0} + + if err := m.db.Get(&result, "SELECT COUNT(*) AS count FROM `video_files` WHERE `frames` = 0"); err != nil { + return err + } + + if result.Count == 0 { + return nil + } + + logger.Infof("Backfilling frames for %d video files...", result.Count) + + lastID := 0 + count := 0 + + for { + gotSome := false + + if err := m.withTxn(ctx, func(tx *sqlx.Tx) error { + query := ` + SELECT f.id, folders.path, f.basename + FROM video_files vf + JOIN files f ON f.id = vf.file_id + JOIN folders ON folders.id = f.parent_folder_id + WHERE vf.frames = 0 + ` + if lastID != 0 { + query += fmt.Sprintf(" AND f.id > %d", lastID) + } + query += fmt.Sprintf(" ORDER BY f.id LIMIT %d", limit) + + rows, err := tx.Query(query) + if err != nil { + return err + } + defer rows.Close() + + for rows.Next() { + var fileID int + var dir string + var basename string + + if err := rows.Scan(&fileID, &dir, &basename); err != nil { + return err + } + + gotSome = true + lastID = fileID + count++ + + path := filepath.Join(dir, basename) + + frames, err := m.ffprobe.GetReadFrameCount(path) + if err != nil || frames <= 0 { + continue + } + + if _, err := tx.Exec("UPDATE `video_files` SET `frames` = ? WHERE `file_id` = ?", frames, fileID); err != nil { + return err + } + } + + return rows.Err() + }); err != nil { + return err + } + + if !gotSome { + break + } + + if count%logEvery == 0 { + logger.Infof("Checked %d video files", count) + } + } + + return nil +} + +func init() { + sqlite.RegisterPostMigration(85, post85) +} \ No newline at end of file diff --git a/pkg/sqlite/migrations/85_video_file_frames.up.sql b/pkg/sqlite/migrations/85_video_file_frames.up.sql new file mode 100644 index 000000000..1b7f3d936 --- /dev/null +++ b/pkg/sqlite/migrations/85_video_file_frames.up.sql @@ -0,0 +1 @@ +ALTER TABLE video_files ADD COLUMN frames INTEGER NOT NULL DEFAULT 0; \ No newline at end of file diff --git a/pkg/sqlite/setup_test.go b/pkg/sqlite/setup_test.go index d8baae3b8..052625293 100644 --- a/pkg/sqlite/setup_test.go +++ b/pkg/sqlite/setup_test.go @@ -925,6 +925,7 @@ func makeFile(i int) models.File { VideoCodec: getFileStringValue(i, "videoCodec"), AudioCodec: getFileStringValue(i, "audioCodec"), FrameRate: getFileDuration(i) * 2, + Frames: getFileDuration(i) * 3, BitRate: int64(getFileDuration(i)) * 3, } } else if i >= fileIdxStartImageFiles && i < fileIdxStartGalleryFiles { diff --git a/ui/v2.5/graphql/data/file.graphql b/ui/v2.5/graphql/data/file.graphql index 7386adb81..6a12c8089 100644 --- a/ui/v2.5/graphql/data/file.graphql +++ b/ui/v2.5/graphql/data/file.graphql @@ -15,6 +15,7 @@ fragment VideoFileData on VideoFile { width height frame_rate + frames bit_rate fingerprints { type @@ -80,6 +81,7 @@ fragment VisualFileData on VisualFile { width height frame_rate + frames bit_rate fingerprints { type diff --git a/ui/v2.5/graphql/data/scrapers.graphql b/ui/v2.5/graphql/data/scrapers.graphql index 7214c2064..18a8b4652 100644 --- a/ui/v2.5/graphql/data/scrapers.graphql +++ b/ui/v2.5/graphql/data/scrapers.graphql @@ -183,6 +183,7 @@ fragment ScrapedSceneData on ScrapedScene { width height framerate + frames bitrate } @@ -270,6 +271,7 @@ fragment ScrapedStashBoxSceneData on ScrapedScene { width height framerate + frames bitrate } diff --git a/ui/v2.5/src/components/Scenes/SceneDetails/SceneFileInfoPanel.tsx b/ui/v2.5/src/components/Scenes/SceneDetails/SceneFileInfoPanel.tsx index cd11a2c8a..217912eda 100644 --- a/ui/v2.5/src/components/Scenes/SceneDetails/SceneFileInfoPanel.tsx +++ b/ui/v2.5/src/components/Scenes/SceneDetails/SceneFileInfoPanel.tsx @@ -110,6 +110,11 @@ const FileInfoPanel: React.FC = ( values={{ value: intl.formatNumber(props.file.frame_rate ?? 0) }} /> + = ( ); + const FramesCell = (scene: GQL.SlimSceneDataFragment) => ( +
    + {scene.files.map((file) => ( +
  • + {file?.frames ?? 0} +
  • + ))} +
+ ); + const BitRateCell = (scene: GQL.SlimSceneDataFragment) => (
    {scene.files.map((file) => ( @@ -357,6 +367,11 @@ export const SceneListTable: React.FC = ( label: intl.formatMessage({ id: "framerate" }), render: FrameRateCell, }, + { + value: "frames", + label: intl.formatMessage({ id: "frames" }), + render: FramesCell, + }, { value: "bitrate", label: intl.formatMessage({ id: "bitrate" }), diff --git a/ui/v2.5/src/locales/en-GB.json b/ui/v2.5/src/locales/en-GB.json index 7b4091f8b..3c58858eb 100644 --- a/ui/v2.5/src/locales/en-GB.json +++ b/ui/v2.5/src/locales/en-GB.json @@ -1181,6 +1181,7 @@ "filters": "Filters", "folder": "Folder", "framerate": "Frame Rate", + "frames": "Frame Count", "frames_per_second": "{value} fps", "front_page": { "types": { From 16f31ba796ebf8b4e67888e265268143a845f9eb Mon Sep 17 00:00:00 2001 From: Bob12224 <241886672+bob12224@users.noreply.github.com> Date: Thu, 12 Mar 2026 14:50:13 -0700 Subject: [PATCH 2/5] PR linting fixes --- pkg/sqlite/migrations/85_postmigrate.go | 2 +- .../components/Scenes/SceneDetails/SceneFileInfoPanel.tsx | 6 +----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/pkg/sqlite/migrations/85_postmigrate.go b/pkg/sqlite/migrations/85_postmigrate.go index 4fa70c83c..5e99c7ba0 100644 --- a/pkg/sqlite/migrations/85_postmigrate.go +++ b/pkg/sqlite/migrations/85_postmigrate.go @@ -121,4 +121,4 @@ func (m *schema85PostMigrator) migrate(ctx context.Context) error { func init() { sqlite.RegisterPostMigration(85, post85) -} \ No newline at end of file +} diff --git a/ui/v2.5/src/components/Scenes/SceneDetails/SceneFileInfoPanel.tsx b/ui/v2.5/src/components/Scenes/SceneDetails/SceneFileInfoPanel.tsx index 217912eda..7069ac66c 100644 --- a/ui/v2.5/src/components/Scenes/SceneDetails/SceneFileInfoPanel.tsx +++ b/ui/v2.5/src/components/Scenes/SceneDetails/SceneFileInfoPanel.tsx @@ -110,11 +110,7 @@ const FileInfoPanel: React.FC = ( values={{ value: intl.formatNumber(props.file.frame_rate ?? 0) }} /> - + Date: Thu, 12 Mar 2026 15:22:51 -0700 Subject: [PATCH 3/5] PR Unit test fixes --- pkg/sqlite/file_test.go | 4 ++-- pkg/sqlite/setup_test.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/sqlite/file_test.go b/pkg/sqlite/file_test.go index b1fd7afba..bd971e3a5 100644 --- a/pkg/sqlite/file_test.go +++ b/pkg/sqlite/file_test.go @@ -41,7 +41,7 @@ func Test_fileFileStore_Create(t *testing.T) { width = 640 height = 480 framerate = 2.345 - frames = 3 + frames int64 = 3 bitrate int64 = 234 videoCodec = "videoCodec" audioCodec = "audioCodec" @@ -260,7 +260,7 @@ func Test_fileStore_Update(t *testing.T) { width = 640 height = 480 framerate = 2.345 - frames = 3 + frames int64 = 3 bitrate int64 = 234 videoCodec = "videoCodec" audioCodec = "audioCodec" diff --git a/pkg/sqlite/setup_test.go b/pkg/sqlite/setup_test.go index 052625293..fd4c14527 100644 --- a/pkg/sqlite/setup_test.go +++ b/pkg/sqlite/setup_test.go @@ -925,7 +925,7 @@ func makeFile(i int) models.File { VideoCodec: getFileStringValue(i, "videoCodec"), AudioCodec: getFileStringValue(i, "audioCodec"), FrameRate: getFileDuration(i) * 2, - Frames: getFileDuration(i) * 3, + Frames: int64(getFileDuration(i)) * 3, BitRate: int64(getFileDuration(i)) * 3, } } else if i >= fileIdxStartImageFiles && i < fileIdxStartGalleryFiles { From b679c6dc783db16e7b49bcd091cd9828f0d70e4b Mon Sep 17 00:00:00 2001 From: Bob <241886672+bob12224@users.noreply.github.com> Date: Wed, 18 Mar 2026 11:16:38 -0700 Subject: [PATCH 4/5] PR Fixes --- pkg/models/jsonschema/scene.go | 2 +- pkg/sqlite/migrations/85_postmigrate.go | 4 ++-- pkg/sqlite/migrations/85_video_file_frames.up.sql | 2 +- pkg/sqlite/setup_test.go | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pkg/models/jsonschema/scene.go b/pkg/models/jsonschema/scene.go index fcc43e725..e4eab0666 100644 --- a/pkg/models/jsonschema/scene.go +++ b/pkg/models/jsonschema/scene.go @@ -31,7 +31,7 @@ type SceneFile struct { Width int `json:"width"` Height int `json:"height"` Framerate string `json:"framerate"` - Frames int `json:"frame"` + Frames int `json:"frames"` Bitrate int `json:"bitrate"` } diff --git a/pkg/sqlite/migrations/85_postmigrate.go b/pkg/sqlite/migrations/85_postmigrate.go index 5e99c7ba0..5f9f53045 100644 --- a/pkg/sqlite/migrations/85_postmigrate.go +++ b/pkg/sqlite/migrations/85_postmigrate.go @@ -42,7 +42,7 @@ func (m *schema85PostMigrator) migrate(ctx context.Context) error { Count int `db:"count"` }{0} - if err := m.db.Get(&result, "SELECT COUNT(*) AS count FROM `video_files` WHERE `frames` = 0"); err != nil { + if err := m.db.Get(&result, "SELECT COUNT(*) AS count FROM `video_files` WHERE `frames` IS NULL"); err != nil { return err } @@ -64,7 +64,7 @@ func (m *schema85PostMigrator) migrate(ctx context.Context) error { FROM video_files vf JOIN files f ON f.id = vf.file_id JOIN folders ON folders.id = f.parent_folder_id - WHERE vf.frames = 0 + WHERE vf.frames IS NULL ` if lastID != 0 { query += fmt.Sprintf(" AND f.id > %d", lastID) diff --git a/pkg/sqlite/migrations/85_video_file_frames.up.sql b/pkg/sqlite/migrations/85_video_file_frames.up.sql index 1b7f3d936..33d495f71 100644 --- a/pkg/sqlite/migrations/85_video_file_frames.up.sql +++ b/pkg/sqlite/migrations/85_video_file_frames.up.sql @@ -1 +1 @@ -ALTER TABLE video_files ADD COLUMN frames INTEGER NOT NULL DEFAULT 0; \ No newline at end of file +ALTER TABLE video_files ADD COLUMN frames INTEGER DEFAULT NULL; \ No newline at end of file diff --git a/pkg/sqlite/setup_test.go b/pkg/sqlite/setup_test.go index fd4c14527..bbbb30baa 100644 --- a/pkg/sqlite/setup_test.go +++ b/pkg/sqlite/setup_test.go @@ -925,7 +925,7 @@ func makeFile(i int) models.File { VideoCodec: getFileStringValue(i, "videoCodec"), AudioCodec: getFileStringValue(i, "audioCodec"), FrameRate: getFileDuration(i) * 2, - Frames: int64(getFileDuration(i)) * 3, + Frames: int64(getFileDuration(i) * getFileDuration(i) * 2), BitRate: int64(getFileDuration(i)) * 3, } } else if i >= fileIdxStartImageFiles && i < fileIdxStartGalleryFiles { From 832d1906bca5b6c7a1a591c1ed9d88db988c3e9c Mon Sep 17 00:00:00 2001 From: Bob <241886672+bob12224@users.noreply.github.com> Date: Wed, 18 Mar 2026 11:55:15 -0700 Subject: [PATCH 5/5] Updating migration to 86 --- pkg/sqlite/database.go | 2 +- .../migrations/{85_postmigrate.go => 86_postmigrate.go} | 6 +++--- ...video_file_frames.up.sql => 86_video_file_frames.up.sql} | 0 3 files changed, 4 insertions(+), 4 deletions(-) rename pkg/sqlite/migrations/{85_postmigrate.go => 86_postmigrate.go} (93%) rename pkg/sqlite/migrations/{85_video_file_frames.up.sql => 86_video_file_frames.up.sql} (100%) diff --git a/pkg/sqlite/database.go b/pkg/sqlite/database.go index 7c383dc4c..026a18c08 100644 --- a/pkg/sqlite/database.go +++ b/pkg/sqlite/database.go @@ -34,7 +34,7 @@ const ( cacheSizeEnv = "STASH_SQLITE_CACHE_SIZE" ) -var appSchemaVersion uint = 85 +var appSchemaVersion uint = 86 //go:embed migrations/*.sql var migrationsBox embed.FS diff --git a/pkg/sqlite/migrations/85_postmigrate.go b/pkg/sqlite/migrations/86_postmigrate.go similarity index 93% rename from pkg/sqlite/migrations/85_postmigrate.go rename to pkg/sqlite/migrations/86_postmigrate.go index 5f9f53045..b682dd94d 100644 --- a/pkg/sqlite/migrations/85_postmigrate.go +++ b/pkg/sqlite/migrations/86_postmigrate.go @@ -12,8 +12,8 @@ import ( "github.com/stashapp/stash/pkg/sqlite" ) -func post85(ctx context.Context, db *sqlx.DB) error { - logger.Info("Running post-migration for schema version 85") +func post86(ctx context.Context, db *sqlx.DB) error { + logger.Info("Running post-migration for schema version 86") ffprobePath, _ := exec.LookPath("ffprobe") @@ -120,5 +120,5 @@ func (m *schema85PostMigrator) migrate(ctx context.Context) error { } func init() { - sqlite.RegisterPostMigration(85, post85) + sqlite.RegisterPostMigration(86, post86) } diff --git a/pkg/sqlite/migrations/85_video_file_frames.up.sql b/pkg/sqlite/migrations/86_video_file_frames.up.sql similarity index 100% rename from pkg/sqlite/migrations/85_video_file_frames.up.sql rename to pkg/sqlite/migrations/86_video_file_frames.up.sql