mirror of
https://github.com/stashapp/stash.git
synced 2025-12-17 04:14:39 +03:00
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:
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user