mirror of
https://github.com/stashapp/stash.git
synced 2025-12-17 04:14:39 +03:00
Add filesystem based blob storage (#3187)
* Refactor transaction hooks. Add preCommit * Add BlobStore * Use blobStore for tag images * Use blobStore for studio images * Use blobStore for performer images * Use blobStore for scene covers * Don't generate screenshots in legacy directory * Run post-hooks outside original transaction * Use blobStore for movie images * Remove unnecessary DestroyImage methods * Add missing filter for scene cover * Add covers to generate options * Add generate cover option to UI * Add screenshot migration * Delete thumb files as part of screenshot migration
This commit is contained in:
@@ -31,6 +31,8 @@ const (
|
||||
scenesTagsTable = "scenes_tags"
|
||||
scenesGalleriesTable = "scenes_galleries"
|
||||
moviesScenesTable = "movies_scenes"
|
||||
|
||||
sceneCoverBlobColumn = "cover_blob"
|
||||
)
|
||||
|
||||
var findExactDuplicateQuery = `
|
||||
@@ -72,6 +74,9 @@ type sceneRow struct {
|
||||
ResumeTime float64 `db:"resume_time"`
|
||||
PlayDuration float64 `db:"play_duration"`
|
||||
PlayCount int `db:"play_count"`
|
||||
|
||||
// not used in resolutions or updates
|
||||
CoverBlob zero.String `db:"cover_blob"`
|
||||
}
|
||||
|
||||
func (r *sceneRow) fromScene(o models.Scene) {
|
||||
@@ -172,6 +177,7 @@ func (r *sceneRowRecord) fromPartial(o models.ScenePartial) {
|
||||
|
||||
type SceneStore struct {
|
||||
repository
|
||||
blobJoinQueryBuilder
|
||||
|
||||
tableMgr *table
|
||||
oCounterManager
|
||||
@@ -179,12 +185,16 @@ type SceneStore struct {
|
||||
fileStore *FileStore
|
||||
}
|
||||
|
||||
func NewSceneStore(fileStore *FileStore) *SceneStore {
|
||||
func NewSceneStore(fileStore *FileStore, blobStore *BlobStore) *SceneStore {
|
||||
return &SceneStore{
|
||||
repository: repository{
|
||||
tableName: sceneTable,
|
||||
idColumn: idColumn,
|
||||
},
|
||||
blobJoinQueryBuilder: blobJoinQueryBuilder{
|
||||
blobStore: blobStore,
|
||||
joinTable: sceneTable,
|
||||
},
|
||||
|
||||
tableMgr: sceneTableMgr,
|
||||
oCounterManager: oCounterManager{sceneTableMgr},
|
||||
@@ -353,6 +363,11 @@ func (qb *SceneStore) Update(ctx context.Context, updatedObject *models.Scene) e
|
||||
}
|
||||
|
||||
func (qb *SceneStore) Destroy(ctx context.Context, id int) error {
|
||||
// must handle image checksums manually
|
||||
if err := qb.destroyCover(ctx, id); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// scene markers should be handled prior to calling destroy
|
||||
// galleries should be handled prior to calling destroy
|
||||
|
||||
@@ -1187,6 +1202,8 @@ func sceneIsMissingCriterionHandler(qb *SceneStore, isMissing *string) criterion
|
||||
qb.addSceneFilesTable(f)
|
||||
f.addLeftJoin(fingerprintTable, "fingerprints_phash", "scenes_files.file_id = fingerprints_phash.file_id AND fingerprints_phash.type = 'phash'")
|
||||
f.addWhere("fingerprints_phash.fingerprint IS NULL")
|
||||
case "cover":
|
||||
f.addWhere("scenes.cover_blob IS NULL")
|
||||
default:
|
||||
f.addWhere("(scenes." + *isMissing + " IS NULL OR TRIM(scenes." + *isMissing + ") = '')")
|
||||
}
|
||||
@@ -1464,17 +1481,6 @@ func (qb *SceneStore) setSceneSort(query *queryBuilder, findFilter *models.FindF
|
||||
}
|
||||
}
|
||||
|
||||
func (qb *SceneStore) imageRepository() *imageRepository {
|
||||
return &imageRepository{
|
||||
repository: repository{
|
||||
tx: qb.tx,
|
||||
tableName: "scenes_cover",
|
||||
idColumn: sceneIDColumn,
|
||||
},
|
||||
imageColumn: "cover",
|
||||
}
|
||||
}
|
||||
|
||||
func (qb *SceneStore) getPlayCount(ctx context.Context, id int) (int, error) {
|
||||
q := dialect.From(qb.tableMgr.table).Select("play_count").Where(goqu.Ex{"id": id})
|
||||
|
||||
@@ -1532,15 +1538,19 @@ func (qb *SceneStore) IncrementWatchCount(ctx context.Context, id int) (int, err
|
||||
}
|
||||
|
||||
func (qb *SceneStore) GetCover(ctx context.Context, sceneID int) ([]byte, error) {
|
||||
return qb.imageRepository().get(ctx, sceneID)
|
||||
return qb.GetImage(ctx, sceneID, sceneCoverBlobColumn)
|
||||
}
|
||||
|
||||
func (qb *SceneStore) HasCover(ctx context.Context, sceneID int) (bool, error) {
|
||||
return qb.HasImage(ctx, sceneID, sceneCoverBlobColumn)
|
||||
}
|
||||
|
||||
func (qb *SceneStore) UpdateCover(ctx context.Context, sceneID int, image []byte) error {
|
||||
return qb.imageRepository().replace(ctx, sceneID, image)
|
||||
return qb.UpdateImage(ctx, sceneID, sceneCoverBlobColumn, image)
|
||||
}
|
||||
|
||||
func (qb *SceneStore) DestroyCover(ctx context.Context, sceneID int) error {
|
||||
return qb.imageRepository().destroy(ctx, []int{sceneID})
|
||||
func (qb *SceneStore) destroyCover(ctx context.Context, sceneID int) error {
|
||||
return qb.DestroyImage(ctx, sceneID, sceneCoverBlobColumn)
|
||||
}
|
||||
|
||||
func (qb *SceneStore) AssignFiles(ctx context.Context, sceneID int, fileIDs []file.ID) error {
|
||||
|
||||
Reference in New Issue
Block a user