Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/Chaptarr.Api.V1/Author/AuthorController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,8 @@ public AuthorController(IBroadcastSignalRMessage signalRBroadcaster,
PostValidator.RuleFor(s => s.ForeignAuthorId).NotEmpty().SetValidator(authorExistsValidator);

PutValidator.RuleFor(s => s.Path).IsValidPath();
PutValidator.RuleFor(s => s.AudiobookPath).IsValidPath().When(s => s.AudiobookPath.IsNotNullOrWhiteSpace());
PutValidator.RuleFor(s => s.EbookPath).IsValidPath().When(s => s.EbookPath.IsNotNullOrWhiteSpace());
}

private bool IsImportActive()
Expand Down
18 changes: 18 additions & 0 deletions src/Chaptarr.Api.V1/Author/AuthorResource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ public class AuthorResource : RestResource
// Readarr/Seerr compatibility (monitor mode string, e.g. "none")
public string MonitorNewItems { get; set; }
public string Folder { get; set; }
// Writable per-media author paths. The edit UI submits these; AudiobookFolder/
// EbookFolder below remain the read-only display values set by the controller.
public string AudiobookPath { get; set; }
public string EbookPath { get; set; }
public string AudiobookFolder { get; set; }
public string EbookFolder { get; set; }
public List<string> Genres { get; set; }
Expand Down Expand Up @@ -131,6 +135,8 @@ public static AuthorResource ToResource(this NzbDrone.Core.Books.Author model, R
Images = displayImages.JsonClone(),

Path = model.Path,
AudiobookPath = model.AudiobookPath,
EbookPath = model.EbookPath,
AudiobookQualityProfileId = model.AudiobookQualityProfileId,
EbookQualityProfileId = model.EbookQualityProfileId,
MetadataProfileId = model.MetadataProfileId,
Expand Down Expand Up @@ -463,6 +469,8 @@ public static NzbDrone.Core.Books.Author ToModel(this AuthorResource resource, R

//AlternateTitles
Path = resource.Path,
AudiobookPath = resource.AudiobookPath,
EbookPath = resource.EbookPath,
AudiobookQualityProfileId = audiobookQualityProfileId,
EbookQualityProfileId = ebookQualityProfileId,
MetadataProfileId = resource.MetadataProfileId,
Expand Down Expand Up @@ -568,6 +576,16 @@ private static void PreserveStoredAuthorStateForFacadeUpdate(AuthorResource reso
updatedAuthor.Path = storedAuthor.Path;
}

if (resource.AudiobookPath == null)
{
updatedAuthor.AudiobookPath = storedAuthor.AudiobookPath;
}

if (resource.EbookPath == null)
{
updatedAuthor.EbookPath = storedAuthor.EbookPath;
}

if (resource.AddOptions == null)
{
updatedAuthor.AddOptions = storedAuthor.AddOptions;
Expand Down
47 changes: 47 additions & 0 deletions src/Chaptarr.Core.Test/Books/AuthorResourceMapperFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,53 @@ public void should_not_wipe_tri_state_monitoring_fields_when_not_provided_on_put
Assert.That(updated.EbookSettingsManuallyOverridden, Is.False);
}

[Test]
public void should_apply_media_paths_on_put_update()
{
var existing = new NzbDrone.Core.Books.Author
{
Id = 1,
Name = "J.R.R. Tolkien",
AudiobookPath = "/audiobooks/J.R.R. Tolkien",
EbookPath = "/ebooks/J.R.R. Tolkien"
};

var resource = new AuthorResource
{
Id = 1,
AuthorName = "J.R.R. Tolkien",
AudiobookPath = "/audiobooks/J. R. R. Tolkien"
};

var updated = resource.ToModel(existing);

Assert.That(updated.AudiobookPath, Is.EqualTo("/audiobooks/J. R. R. Tolkien"));
Assert.That(updated.EbookPath, Is.EqualTo("/ebooks/J.R.R. Tolkien"), "an omitted media path must keep its stored value");
}

[Test]
public void should_not_wipe_media_paths_when_not_provided_on_put_update()
{
var existing = new NzbDrone.Core.Books.Author
{
Id = 1,
Name = "J.R.R. Tolkien",
AudiobookPath = "/audiobooks/J.R.R. Tolkien",
EbookPath = "/ebooks/J.R.R. Tolkien"
};

var resource = new AuthorResource
{
Id = 1,
AuthorName = "J.R.R. Tolkien"
};

var updated = resource.ToModel(existing);

Assert.That(updated.AudiobookPath, Is.EqualTo("/audiobooks/J.R.R. Tolkien"));
Assert.That(updated.EbookPath, Is.EqualTo("/ebooks/J.R.R. Tolkien"));
}

[Test]
public void should_apply_per_type_metadata_profiles_on_put_update()
{
Expand Down
5 changes: 5 additions & 0 deletions src/NzbDrone.Core/Books/Model/Author.cs
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,11 @@ public override void ApplyChanges(Author other)
AddOptions = other.AddOptions;
AudiobookRootFolderPath = other.AudiobookRootFolderPath;
EbookRootFolderPath = other.EbookRootFolderPath;

// Per-media author paths: null means the caller did not provide a value,
// so keep the stored one (an explicit clear arrives as an empty string).
AudiobookPath = other.AudiobookPath ?? AudiobookPath;
EbookPath = other.EbookPath ?? EbookPath;
Monitored = other.Monitored;
// TRI-STATE MONITORING SYSTEM - Copy only when explicitly provided (not null).
// This prevents partial updates from wiping existing monitoring values.
Expand Down
Loading