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:
WithoutPants
2023-03-17 10:52:49 +11:00
committed by GitHub
parent c3081700c0
commit 7cff71c35f
105 changed files with 2647 additions and 1086 deletions

View File

@@ -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 {