Compare commits

..

7 Commits

Author SHA1 Message Date
WithoutPants
2a373a25ca Update changelog 2024-06-27 10:16:42 +10:00
dogwithakeyboard
e116775d60 Check for null disambiguation on validate (#5019) 2024-06-27 10:14:14 +10:00
CJ
c5bafeb15c Address resize loop (#5004) 2024-06-27 09:11:00 +10:00
WithoutPants
205b24499b Fix key for tagger scenes (#5000) 2024-06-27 09:09:33 +10:00
WithoutPants
48035061ec Fix identify clearing parent studio when merging (#4993)
* Refactor ScrapedStudio.ToPartial signature
* Add unit test
* Don't clear parent studio during ToPartial
2024-06-27 09:08:26 +10:00
anonymous-ants
e4b89064b1 Fix typos in docs (en) (#4946) 2024-06-11 08:26:56 +10:00
InfiniteStash
efede32dd7 Fix studio selection in scraping dialogs (#4953) 2024-06-11 08:26:03 +10:00
14 changed files with 170 additions and 36 deletions

View File

@@ -46,17 +46,17 @@ func createMissingStudio(ctx context.Context, endpoint string, w models.StudioRe
return nil, err
}
studioPartial := s.Parent.ToPartial(s.Parent.StoredID, endpoint, nil, existingStashIDs)
studioPartial := s.Parent.ToPartial(*s.Parent.StoredID, endpoint, nil, existingStashIDs)
parentImage, err := s.Parent.GetImage(ctx, nil)
if err != nil {
return nil, err
}
if err := studio.ValidateModify(ctx, *studioPartial, w); err != nil {
if err := studio.ValidateModify(ctx, studioPartial, w); err != nil {
return nil, err
}
_, err = w.UpdatePartial(ctx, *studioPartial)
_, err = w.UpdatePartial(ctx, studioPartial)
if err != nil {
return nil, err
}

View File

@@ -311,13 +311,13 @@ func (t *StashBoxBatchTagTask) processMatchedStudio(ctx context.Context, s *mode
return err
}
partial := s.ToPartial(s.StoredID, t.box.Endpoint, excluded, existingStashIDs)
partial := s.ToPartial(*s.StoredID, t.box.Endpoint, excluded, existingStashIDs)
if err := studio.ValidateModify(ctx, *partial, qb); err != nil {
if err := studio.ValidateModify(ctx, partial, qb); err != nil {
return err
}
if _, err := qb.UpdatePartial(ctx, *partial); err != nil {
if _, err := qb.UpdatePartial(ctx, partial); err != nil {
return err
}
@@ -435,13 +435,13 @@ func (t *StashBoxBatchTagTask) processParentStudio(ctx context.Context, parent *
return err
}
partial := parent.ToPartial(parent.StoredID, t.box.Endpoint, excluded, existingStashIDs)
partial := parent.ToPartial(*parent.StoredID, t.box.Endpoint, excluded, existingStashIDs)
if err := studio.ValidateModify(ctx, *partial, qb); err != nil {
if err := studio.ValidateModify(ctx, partial, qb); err != nil {
return err
}
if _, err := qb.UpdatePartial(ctx, *partial); err != nil {
if _, err := qb.UpdatePartial(ctx, partial); err != nil {
return err
}

View File

@@ -62,9 +62,9 @@ func (s *ScrapedStudio) GetImage(ctx context.Context, excluded map[string]bool)
return nil, nil
}
func (s *ScrapedStudio) ToPartial(id *string, endpoint string, excluded map[string]bool, existingStashIDs []StashID) *StudioPartial {
func (s *ScrapedStudio) ToPartial(id string, endpoint string, excluded map[string]bool, existingStashIDs []StashID) StudioPartial {
ret := NewStudioPartial()
ret.ID, _ = strconv.Atoi(*id)
ret.ID, _ = strconv.Atoi(id)
if s.Name != "" && !excluded["name"] {
ret.Name = NewOptionalString(s.Name)
@@ -82,8 +82,6 @@ func (s *ScrapedStudio) ToPartial(id *string, endpoint string, excluded map[stri
ret.ParentID = NewOptionalInt(parentID)
}
}
} else {
ret.ParentID = NewOptionalIntPtr(nil)
}
if s.RemoteSiteID != nil && endpoint != "" {
@@ -97,7 +95,7 @@ func (s *ScrapedStudio) ToPartial(id *string, endpoint string, excluded map[stri
})
}
return &ret
return ret
}
// A performer from a scraping operation...

View File

@@ -249,3 +249,123 @@ func Test_scrapedToPerformerInput(t *testing.T) {
})
}
}
func TestScrapedStudio_ToPartial(t *testing.T) {
var (
id = 1000
idStr = strconv.Itoa(id)
storedID = "storedID"
parentStoredID = 2000
parentStoredIDStr = strconv.Itoa(parentStoredID)
name = "name"
url = "url"
remoteSiteID = "remoteSiteID"
endpoint = "endpoint"
image = "image"
images = []string{image}
existingEndpoint = "existingEndpoint"
existingStashID = StashID{"existingStashID", existingEndpoint}
existingStashIDs = []StashID{existingStashID}
)
fullStudio := ScrapedStudio{
StoredID: &storedID,
Name: name,
URL: &url,
Parent: &ScrapedStudio{
StoredID: &parentStoredIDStr,
},
Image: &image,
Images: images,
RemoteSiteID: &remoteSiteID,
}
type args struct {
id string
endpoint string
excluded map[string]bool
existingStashIDs []StashID
}
stdArgs := args{
id: idStr,
endpoint: endpoint,
excluded: map[string]bool{},
existingStashIDs: existingStashIDs,
}
excludeAll := map[string]bool{
"name": true,
"url": true,
"parent": true,
}
tests := []struct {
name string
o ScrapedStudio
args args
want StudioPartial
}{
{
"full no exclusions",
fullStudio,
stdArgs,
StudioPartial{
ID: id,
Name: NewOptionalString(name),
URL: NewOptionalString(url),
ParentID: NewOptionalInt(parentStoredID),
StashIDs: &UpdateStashIDs{
StashIDs: append(existingStashIDs, StashID{
Endpoint: endpoint,
StashID: remoteSiteID,
}),
Mode: RelationshipUpdateModeSet,
},
},
},
{
"exclude all",
fullStudio,
args{
id: idStr,
excluded: excludeAll,
},
StudioPartial{
ID: id,
},
},
{
"overwrite stash id",
fullStudio,
args{
id: idStr,
excluded: excludeAll,
endpoint: existingEndpoint,
existingStashIDs: existingStashIDs,
},
StudioPartial{
ID: id,
StashIDs: &UpdateStashIDs{
StashIDs: []StashID{{
Endpoint: existingEndpoint,
StashID: remoteSiteID,
}},
Mode: RelationshipUpdateModeSet,
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
s := tt.o
got := s.ToPartial(tt.args.id, tt.args.endpoint, tt.args.excluded, tt.args.existingStashIDs)
// unset updatedAt - we don't need to compare it
got.UpdatedAt = OptionalTime{}
assert.Equal(t, tt.want, got)
})
}
}

View File

@@ -102,11 +102,15 @@ func validateName(ctx context.Context, name string, disambig string, existingID
},
}
modifier := models.CriterionModifierIsNull
if disambig != "" {
performerFilter.Disambiguation = &models.StringCriterionInput{
Value: disambig,
Modifier: models.CriterionModifierEquals,
}
modifier = models.CriterionModifierEquals
}
performerFilter.Disambiguation = &models.StringCriterionInput{
Value: disambig,
Modifier: modifier,
}
if existingID == nil {

View File

@@ -15,6 +15,9 @@ func nameFilter(n string) *models.PerformerFilterType {
Value: n,
Modifier: models.CriterionModifierEquals,
},
Disambiguation: &models.StringCriterionInput{
Modifier: models.CriterionModifierIsNull,
},
}
}
@@ -41,13 +44,6 @@ func TestValidateName(t *testing.T) {
newName = "new name"
newDisambig = "new disambiguation"
)
// existing1 := models.Performer{
// Name: name1,
// }
// existing2 := models.Performer{
// Name: name2,
// Disambiguation: disambig,
// }
pp := 1
findFilter := &models.FindFilterType{

View File

@@ -42,9 +42,9 @@ interface IDimension {
height: number;
}
export const useContainerDimensions = <
T extends HTMLElement = HTMLDivElement
>(): [MutableRefObject<T | null>, IDimension] => {
export const useContainerDimensions = <T extends HTMLElement = HTMLDivElement>(
sensitivityThreshold = 20
): [MutableRefObject<T | null>, IDimension] => {
const target = useRef<T | null>(null);
const [dimension, setDimension] = useState<IDimension>({
width: 0,
@@ -53,7 +53,14 @@ export const useContainerDimensions = <
useResizeObserver(target, (entry) => {
const { inlineSize: width, blockSize: height } = entry.contentBoxSize[0];
setDimension({ width, height });
let difference = Math.abs(dimension.width - width);
// Only adjust when width changed by a significant margin. This addresses the cornercase that sees
// the dimensions toggle back and forward when the window is adjusted perfectly such that overflow
// is trigger then immediable disabled because of a resize event then continues this loop endlessly.
// the scrollbar size varies between platforms. Windows is apparently around 17 pixels.
if (difference > sensitivityThreshold) {
setDimension({ width, height });
}
});
return [target, dimension];

View File

@@ -54,7 +54,11 @@ export const ScrapedStudioRow: React.FC<IScrapedStudioRow> = ({
isDisabled={!isNew}
onSelect={(items) => {
if (onChangeFn) {
onChangeFn(items[0]);
const { id, ...data } = items[0];
onChangeFn({
...data,
stored_id: id,
});
}
}}
values={selectValue}

View File

@@ -264,7 +264,7 @@ export const Tagger: React.FC<ITaggerProps> = ({ scenes, queue }) => {
<div>
{filteredScenes.map((s, i) => (
<Scene
key={i}
key={s.id}
scene={s}
searchResult={searchResults[s.id]}
index={i}

View File

@@ -47,7 +47,7 @@ Once migrated, these files can be deleted. The files can be optionally deleted d
* Fixed `/stream` endpoint serving directory list. ([#3541](https://github.com/stashapp/stash/pull/3541))
* Fixed error when querying with a large or unlimited page size. ([#3544](https://github.com/stashapp/stash/pull/3544))
* Fixed sprites not being displayed for scenes with numeric-only hashes. ([#3513](https://github.com/stashapp/stash/pull/3513))
* Fixed Save button being disabled when stting Tag image. ([#3509](https://github.com/stashapp/stash/pull/3509))
* Fixed Save button being disabled when setting Tag image. ([#3509](https://github.com/stashapp/stash/pull/3509))
* Fixed incorrect performer with identical name being matched when scraping from stash-box. ([#3488](https://github.com/stashapp/stash/pull/3488))
* Fixed scene cover not being included when submitting file-less scenes to stash-box. ([#3465](https://github.com/stashapp/stash/pull/3465))
* Fixed URL not being during stash-box scrape if the Studio URL is not set. ([#3439](https://github.com/stashapp/stash/pull/3439))

View File

@@ -22,6 +22,11 @@
* Changed umask when creating config file to exclude user write (CVE-2024-32233) ([#4866](https://github.com/stashapp/stash/pull/4866))
### 🐛 Bug fixes
* **[0.26.2]** Fixed issue where performer could not be created without disambiguation if a performer with the same name and populated disambiguation exists. ([#5019](https://github.com/stashapp/stash/pull/5019))
* **[0.26.2]** Fix resize loop in grid views. ([#5004](https://github.com/stashapp/stash/pull/5004))
* **[0.26.2]** Fix query field values duplicating in tagger view when scene list is updated. ([#5000](https://github.com/stashapp/stash/pull/5000))
* **[0.26.2]** Fix identify clearing parent studio when merging studio field. ([#4993](https://github.com/stashapp/stash/pull/4993))
* **[0.26.2]** Fix manually selected studio not being applied during scrape. ([#4953](https://github.com/stashapp/stash/pull/4953))
* **[0.26.1]** Fixed identify task defaults not displaying correctly. ([#4931](https://github.com/stashapp/stash/pull/4931))
* **[0.26.1]** Fixed issue where full hardware transcoding did not work where a filter was not required. ([#4934](https://github.com/stashapp/stash/pull/4934))
* **[0.26.1]** Fixed new performer tags not displaying correctly in the performer scrape dialog. ([#4943](https://github.com/stashapp/stash/pull/4943))

View File

@@ -15,7 +15,7 @@ Regex patterns can be added in the config file or from the UI.
If you add manually to the config file a restart is needed while from the UI you just need to click the Save button.
When added through the config file directly special care must be given to double escape the `\` character.
There are 2 sperate exclusion settings. One is for videos, another is for images/galleries.
There are 2 separate exclusion settings. One is for videos, another is for images/galleries.
Some examples:
@@ -23,7 +23,7 @@ Some examples:
- `"/\.[[:word:]]+/"` will exclude all hidden directories like `/.directoryname/`.
- `"c:\\stash\\videos\\exclude"` will exclude specific Windows directory `c:\stash\videos\exclude`.
- `"^/stash/videos/exclude/"` will exclude all directories that match `/stash/videos/exclude/` pattern.
- `"\\\\stash\\network\\share\\excl\\"` will exlcude specific Windows network path `\\stash\network\share\excl\`.
- `"\\\\stash\\network\\share\\excl\\"` will exclude specific Windows network path `\\stash\network\share\excl\`.
> **Note:** If a directory is excluded for images and videos, then the directory will be excluded from scans completely.

View File

@@ -2,6 +2,6 @@
Stash works by cataloging your media using the paths that you provide. Once you have [configured](/settings?tab=library) the locations where your media is stored, you can click the Scan button in [`Settings -> Tasks`](/settings?tab=tasks) and stash will begin scanning and importing your media into its library.
For the best experience, it is recommmended that after a scan is finished, that video previews and sprites are generated. You can do this in [`Settings -> Tasks`](/settings?tab=tasks). Note that currently it is only possible to perform one task at a time and but there is a task queue, so the generate tasks should be performed after scan is complete.
For the best experience, it is recommended that after a scan is finished, that video previews and sprites are generated. You can do this in [`Settings -> Tasks`](/settings?tab=tasks). Note that currently it is only possible to perform one task at a time and but there is a task queue, so the generate tasks should be performed after scan is complete.
Once your media is imported, you are ready to begin creating Performers, Studios and Tags, and curating your content!

View File

@@ -1,5 +1,5 @@
This release introduces scraper and plugin management interfaces. This allows installing, updating and removing scrapers and plugins from the WebUI.
Default package sources have been automatically configured to point at the _stable_ branches of the `CommunityScrapers` and `CommunityScripts` respositories. These branches will correspond to the current stable version of stash.
Default package sources have been automatically configured to point at the _stable_ branches of the `CommunityScrapers` and `CommunityScripts` repositories. These branches will correspond to the current stable version of stash.
**Note:** existing scrapers and plugins will _not_ be able to be managed using the management interface. It is recommended that any existing scrapers and plugins that are available from the community repositories are backed up and removed from the applicable `scrapers` or `plugins` directory, and reinstalled using the management UI.