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

@@ -23,7 +23,8 @@ const (
performersAliasesTable = "performer_aliases"
performerAliasColumn = "alias"
performersTagsTable = "performers_tags"
performersImageTable = "performers_image" // performer cover image
performerImageBlobColumn = "image_blob"
)
type performerRow struct {
@@ -54,6 +55,9 @@ type performerRow struct {
HairColor zero.String `db:"hair_color"`
Weight null.Int `db:"weight"`
IgnoreAutoTag bool `db:"ignore_auto_tag"`
// not used for resolution
ImageBlob zero.String `db:"image_blob"`
}
func (r *performerRow) fromPerformer(o models.Performer) {
@@ -159,16 +163,21 @@ func (r *performerRowRecord) fromPartial(o models.PerformerPartial) {
type PerformerStore struct {
repository
blobJoinQueryBuilder
tableMgr *table
}
func NewPerformerStore() *PerformerStore {
func NewPerformerStore(blobStore *BlobStore) *PerformerStore {
return &PerformerStore{
repository: repository{
tableName: performerTable,
idColumn: idColumn,
},
blobJoinQueryBuilder: blobJoinQueryBuilder{
blobStore: blobStore,
joinTable: performerTable,
},
tableMgr: performerTableMgr,
}
}
@@ -275,6 +284,11 @@ func (qb *PerformerStore) Update(ctx context.Context, updatedObject *models.Perf
}
func (qb *PerformerStore) Destroy(ctx context.Context, id int) error {
// must handle image checksums manually
if err := qb.DestroyImage(ctx, id); err != nil {
return err
}
return qb.destroyExisting(ctx, []int{id})
}
@@ -690,8 +704,7 @@ func performerIsMissingCriterionHandler(qb *PerformerStore, isMissing *string) c
f.addLeftJoin(performersScenesTable, "scenes_join", "scenes_join.performer_id = performers.id")
f.addWhere("scenes_join.scene_id IS NULL")
case "image":
f.addLeftJoin(performersImageTable, "image_join", "image_join.performer_id = performers.id")
f.addWhere("image_join.performer_id IS NULL")
f.addWhere("performers.image_blob IS NULL")
case "stash_id":
performersStashIDsTableMgr.join(f, "performer_stash_ids", "performers.id")
f.addWhere("performer_stash_ids.performer_id IS NULL")
@@ -911,27 +924,16 @@ func (qb *PerformerStore) GetTagIDs(ctx context.Context, id int) ([]int, error)
return qb.tagsRepository().getIDs(ctx, id)
}
func (qb *PerformerStore) imageRepository() *imageRepository {
return &imageRepository{
repository: repository{
tx: qb.tx,
tableName: "performers_image",
idColumn: performerIDColumn,
},
imageColumn: "image",
}
}
func (qb *PerformerStore) GetImage(ctx context.Context, performerID int) ([]byte, error) {
return qb.imageRepository().get(ctx, performerID)
return qb.blobJoinQueryBuilder.GetImage(ctx, performerID, performerImageBlobColumn)
}
func (qb *PerformerStore) UpdateImage(ctx context.Context, performerID int, image []byte) error {
return qb.imageRepository().replace(ctx, performerID, image)
return qb.blobJoinQueryBuilder.UpdateImage(ctx, performerID, performerImageBlobColumn, image)
}
func (qb *PerformerStore) DestroyImage(ctx context.Context, performerID int) error {
return qb.imageRepository().destroy(ctx, []int{performerID})
return qb.blobJoinQueryBuilder.DestroyImage(ctx, performerID, performerImageBlobColumn)
}
func (qb *PerformerStore) stashIDRepository() *stashIDRepository {