Scene play and o-counter history view and editing (#4532)

Co-authored-by: randemgame <61895715+randemgame@users.noreply.github.com>
This commit is contained in:
WithoutPants
2024-02-22 11:28:18 +11:00
committed by GitHub
parent 0c2a2190e5
commit a303446bb7
51 changed files with 3581 additions and 564 deletions

View File

@@ -14,7 +14,9 @@ import (
"github.com/stashapp/stash/pkg/utils"
)
type CoverGetter interface {
type ExportGetter interface {
models.ViewDateReader
models.ODateReader
GetCover(ctx context.Context, sceneID int) ([]byte, error)
}
@@ -27,7 +29,7 @@ type TagFinder interface {
// ToBasicJSON converts a scene object into its JSON object equivalent. It
// does not convert the relationships to other objects, with the exception
// of cover image.
func ToBasicJSON(ctx context.Context, reader CoverGetter, scene *models.Scene) (*jsonschema.Scene, error) {
func ToBasicJSON(ctx context.Context, reader ExportGetter, scene *models.Scene) (*jsonschema.Scene, error) {
newSceneJSON := jsonschema.Scene{
Title: scene.Title,
Code: scene.Code,
@@ -47,7 +49,6 @@ func ToBasicJSON(ctx context.Context, reader CoverGetter, scene *models.Scene) (
}
newSceneJSON.Organized = scene.Organized
newSceneJSON.OCounter = scene.OCounter
for _, f := range scene.Files.List() {
newSceneJSON.Files = append(newSceneJSON.Files, f.Base().Path)
@@ -73,6 +74,24 @@ func ToBasicJSON(ctx context.Context, reader CoverGetter, scene *models.Scene) (
newSceneJSON.StashIDs = ret
dates, err := reader.GetViewDates(ctx, scene.ID)
if err != nil {
return nil, fmt.Errorf("error getting view dates: %v", err)
}
for _, date := range dates {
newSceneJSON.PlayHistory = append(newSceneJSON.PlayHistory, json.JSONTime{Time: date})
}
odates, err := reader.GetODates(ctx, scene.ID)
if err != nil {
return nil, fmt.Errorf("error getting o dates: %v", err)
}
for _, date := range odates {
newSceneJSON.OHistory = append(newSceneJSON.OHistory, json.JSONTime{Time: date})
}
return &newSceneJSON, nil
}

View File

@@ -8,6 +8,7 @@ import (
"github.com/stashapp/stash/pkg/models/jsonschema"
"github.com/stashapp/stash/pkg/models/mocks"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"testing"
"time"
@@ -40,7 +41,6 @@ var (
date = "2001-01-01"
dateObj, _ = models.ParseDate(date)
rating = 5
ocounter = 2
organized = true
details = "details"
)
@@ -88,7 +88,6 @@ func createFullScene(id int) models.Scene {
Title: title,
Date: &dateObj,
Details: details,
OCounter: ocounter,
Rating: &rating,
Organized: organized,
URLs: models.NewRelatedStrings([]string{url}),
@@ -130,7 +129,6 @@ func createFullJSONScene(image string) *jsonschema.Scene {
Files: []string{path},
Date: date,
Details: details,
OCounter: ocounter,
Rating: rating,
Organized: organized,
URLs: []string{url},
@@ -193,6 +191,8 @@ func TestToJSON(t *testing.T) {
db.Scene.On("GetCover", testCtx, sceneID).Return(imageBytes, nil).Once()
db.Scene.On("GetCover", testCtx, noImageID).Return(nil, nil).Once()
db.Scene.On("GetCover", testCtx, errImageID).Return(nil, imageErr).Once()
db.Scene.On("GetViewDates", testCtx, mock.Anything).Return(nil, nil)
db.Scene.On("GetODates", testCtx, mock.Anything).Return(nil, nil)
for i, s := range scenarios {
scene := s.input

View File

@@ -4,8 +4,10 @@ import (
"context"
"fmt"
"strings"
"time"
"github.com/stashapp/stash/pkg/models"
"github.com/stashapp/stash/pkg/models/json"
"github.com/stashapp/stash/pkg/models/jsonschema"
"github.com/stashapp/stash/pkg/sliceutil"
"github.com/stashapp/stash/pkg/utils"
@@ -13,6 +15,8 @@ import (
type ImporterReaderWriter interface {
models.SceneCreatorUpdater
models.ViewHistoryWriter
models.OHistoryWriter
FindByFileID(ctx context.Context, fileID models.FileID) ([]*models.Scene, error)
}
@@ -31,6 +35,8 @@ type Importer struct {
ID int
scene models.Scene
coverImageData []byte
viewHistory []time.Time
oHistory []time.Time
}
func (i *Importer) PreImport(ctx context.Context) error {
@@ -68,6 +74,9 @@ func (i *Importer) PreImport(ctx context.Context) error {
}
}
i.populateViewHistory()
i.populateOHistory()
return nil
}
@@ -101,20 +110,54 @@ func (i *Importer) sceneJSONToScene(sceneJSON jsonschema.Scene) models.Scene {
}
newScene.Organized = sceneJSON.Organized
newScene.OCounter = sceneJSON.OCounter
newScene.CreatedAt = sceneJSON.CreatedAt.GetTime()
newScene.UpdatedAt = sceneJSON.UpdatedAt.GetTime()
if !sceneJSON.LastPlayedAt.IsZero() {
t := sceneJSON.LastPlayedAt.GetTime()
newScene.LastPlayedAt = &t
}
newScene.ResumeTime = sceneJSON.ResumeTime
newScene.PlayDuration = sceneJSON.PlayDuration
newScene.PlayCount = sceneJSON.PlayCount
return newScene
}
func getHistory(historyJSON []json.JSONTime, count int, last json.JSONTime, createdAt json.JSONTime) []time.Time {
var ret []time.Time
if len(historyJSON) > 0 {
for _, d := range historyJSON {
ret = append(ret, d.GetTime())
}
} else if count > 0 {
createdAt := createdAt.GetTime()
for j := 0; j < count; j++ {
t := createdAt
if j+1 == count && !last.IsZero() {
// last one, use last play date
t = last.GetTime()
}
ret = append(ret, t)
}
}
return ret
}
func (i *Importer) populateViewHistory() {
i.viewHistory = getHistory(
i.Input.PlayHistory,
i.Input.PlayCount,
i.Input.LastPlayedAt,
i.Input.CreatedAt,
)
}
func (i *Importer) populateOHistory() {
i.viewHistory = getHistory(
i.Input.OHistory,
i.Input.OCounter,
i.Input.CreatedAt, // no last o count date
i.Input.CreatedAt,
)
}
func (i *Importer) populateFiles(ctx context.Context) error {
files := make([]*models.VideoFile, 0)
@@ -365,6 +408,28 @@ func (i *Importer) populateTags(ctx context.Context) error {
return nil
}
func (i *Importer) addViewHistory(ctx context.Context) error {
if len(i.viewHistory) > 0 {
_, err := i.ReaderWriter.AddViews(ctx, i.ID, i.viewHistory)
if err != nil {
return fmt.Errorf("error adding view date: %v", err)
}
}
return nil
}
func (i *Importer) addOHistory(ctx context.Context) error {
if len(i.oHistory) > 0 {
_, err := i.ReaderWriter.AddO(ctx, i.ID, i.oHistory)
if err != nil {
return fmt.Errorf("error adding o date: %v", err)
}
}
return nil
}
func (i *Importer) PostImport(ctx context.Context, id int) error {
if len(i.coverImageData) > 0 {
if err := i.ReaderWriter.UpdateCover(ctx, id, i.coverImageData); err != nil {
@@ -372,6 +437,15 @@ func (i *Importer) PostImport(ctx context.Context, id int) error {
}
}
// add histories
if err := i.addViewHistory(ctx); err != nil {
return err
}
if err := i.addOHistory(ctx); err != nil {
return err
}
return nil
}

View File

@@ -6,6 +6,7 @@ import (
"fmt"
"os"
"path/filepath"
"time"
"github.com/stashapp/stash/pkg/fsutil"
"github.com/stashapp/stash/pkg/logger"
@@ -14,13 +15,15 @@ import (
"github.com/stashapp/stash/pkg/txn"
)
func (s *Service) Merge(
ctx context.Context,
sourceIDs []int,
destinationID int,
scenePartial models.ScenePartial,
fileDeleter *FileDeleter,
) error {
type MergeOptions struct {
ScenePartial models.ScenePartial
IncludePlayHistory bool
IncludeOHistory bool
}
func (s *Service) Merge(ctx context.Context, sourceIDs []int, destinationID int, fileDeleter *FileDeleter, options MergeOptions) error {
scenePartial := options.ScenePartial
// ensure source ids are unique
sourceIDs = sliceutil.AppendUniques(nil, sourceIDs)
@@ -74,6 +77,44 @@ func (s *Service) Merge(
return fmt.Errorf("updating scene: %w", err)
}
// merge play history
if options.IncludePlayHistory {
var allDates []time.Time
for _, src := range sources {
thisDates, err := s.Repository.GetViewDates(ctx, src.ID)
if err != nil {
return fmt.Errorf("getting view dates for scene %d: %w", src.ID, err)
}
allDates = append(allDates, thisDates...)
}
if len(allDates) > 0 {
if _, err := s.Repository.AddViews(ctx, destinationID, allDates); err != nil {
return fmt.Errorf("adding view dates to scene %d: %w", destinationID, err)
}
}
}
// merge o history
if options.IncludeOHistory {
var allDates []time.Time
for _, src := range sources {
thisDates, err := s.Repository.GetODates(ctx, src.ID)
if err != nil {
return fmt.Errorf("getting o dates for scene %d: %w", src.ID, err)
}
allDates = append(allDates, thisDates...)
}
if len(allDates) > 0 {
if _, err := s.Repository.AddO(ctx, destinationID, allDates); err != nil {
return fmt.Errorf("adding o dates to scene %d: %w", destinationID, err)
}
}
}
// delete old scenes
for _, src := range sources {
const deleteGenerated = true