Skip to content

Add option to leave files where they are while importing library#503

Open
T4g1 wants to merge 3 commits intoListenarrs:canaryfrom
T4g1:fix/494-library-safe-imports
Open

Add option to leave files where they are while importing library#503
T4g1 wants to merge 3 commits intoListenarrs:canaryfrom
T4g1:fix/494-library-safe-imports

Conversation

@T4g1
Copy link
Copy Markdown
Contributor

@T4g1 T4g1 commented Apr 9, 2026

Summary

Changes

Added

  • Ability to add root folders while selecting the root folder from which we want to import files
  • Ability to leave files in place when importing library
  • Ability to set the desired monitoring for audiobooks added through library importation

Testing

  • Check that the import does not change anything in the files when it is not asked to modify them

Notes

Next steps

@T4g1 T4g1 marked this pull request as ready for review April 10, 2026 11:56
@T4g1 T4g1 force-pushed the fix/494-library-safe-imports branch from df07abd to 19eed3f Compare April 10, 2026 12:50
@T4g1 T4g1 force-pushed the fix/494-library-safe-imports branch from 2024229 to b5cf323 Compare April 10, 2026 12:56
Copy link
Copy Markdown
Collaborator

@therobbiedavis therobbiedavis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Solid job! Thank you! Just a few changes and a request to please update the changelog!


foreach (var item in orderedItems)
{
var fileCount = orderedItems.Select(f => f.MatchedAudiobookId).Count();
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will count every item in the batch regardless of audiobook. Example: if you import 1 file for Book A and 1 file for Book B, both files see fileCount = 2 and are treated as multi-file imports, causing wrong naming.

}
}

public async Task PerformActionOn(Listenarr.Api.Services.FileMover.FileAction action, string source, string? destination = null, HashSet<string>? usedDestinations = null)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not use usedDestinations? It's used for collision detection in the filemover.

/// <param name="action">What we want to do with the file</param>
/// <param name="source">File</param>
/// <param name="destination">Optional destination of the action</param>
/// <param name="usedDestinations">To remove probably</param>
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we're removing then lets refactor and remove, otherwise lets update this description.

public string? BasePath {
get
{
return FileUtils.NormalizeStoredPath(field);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lets instead normalize on the setter so reads are free

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would agree but what about values already in DB then ? Are we sure all values are currently normalized already ?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, so let's include a one-time normalization migration with this to normalize existing BasePaths

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm gonna need some pointers on how to achieve that, afaik, migration cannot be used to run arbitrary C# methods on the database and the logic of NormalizeStoredPath seems really complex to translate to SQL

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generate a migration like so:
dotnet ef migrations add NormalizeStoredPaths --project listenarr.infrastructure --startup-project listenarr.api

then add the following to the generated Up() in the migration:

migrationBuilder.Sql(@"
    UPDATE Audiobooks
    SET BasePath = RTRIM(TRIM(BasePath), '/\')
    WHERE BasePath IS NOT NULL AND BasePath != RTRIM(TRIM(BasePath), '/\');

    UPDATE ApplicationSettings
    SET OutputPath = RTRIM(TRIM(OutputPath), '/\')
    WHERE OutputPath != RTRIM(TRIM(OutputPath), '/\');
");

{
get
{
return FileUtils.NormalizeStoredPath(field);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here, normalize on the setter

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's include a one-time normalization migration with this to normalize existing OutputPaths, then normalize the setter


return { folders, loading, defaultFolder, load, create, update, remove }
function getLast() {
return folders.value.at(-1);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this assumes two people are not creating folders at the same time. We might want RootFolderFormModal to emit the saved entity's ID and resolve it directly.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know this probably isn't a valid use-case, but also I've learned long ago not to assume how a user will use your software.

@T4g1 T4g1 requested a review from therobbiedavis April 14, 2026 12:10
@T4g1
Copy link
Copy Markdown
Contributor Author

T4g1 commented Apr 14, 2026

@therobbiedavis Thanks! I did a pass on your reviews, can you check again to see if we are aligned ?

Copy link
Copy Markdown
Collaborator

@therobbiedavis therobbiedavis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@T4g1 Added a couple comments on the existing issues, outside of these it lgtm!

public string? BasePath {
get
{
return FileUtils.NormalizeStoredPath(field);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, so let's include a one-time normalization migration with this to normalize existing BasePaths

{
get
{
return FileUtils.NormalizeStoredPath(field);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's include a one-time normalization migration with this to normalize existing OutputPaths, then normalize the setter

CHANGELOG.md Outdated

### Changed
- **Reduced redundancy for the manual import modal:** Small improvement that make it easier to add or edit import fields.
## [0.2.66] - ?
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changelog needs to be updated from canary or rebased, then your changes would be under
## [Unreleased]
at the top. We will update this with a version and date before release.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature Request] option for keep local files while library import from root folder

2 participants