mirror of
https://github.com/Radarr/Radarr.git
synced 2024-08-18 00:09:37 +02:00
Fixed: Remove Static/Dynamic Settings, Allow Folder Move from Editor
This commit is contained in:
parent
1c8f94f1d8
commit
e954b01921
@ -49,6 +49,13 @@ function MoveMovieModal(props) {
|
||||
`Would you like to move the movie folders to '${destinationRootFolder}'?` :
|
||||
`Would you like to move the movie files from '${originalPath}' to '${destinationPath}'?`
|
||||
}
|
||||
{
|
||||
destinationRootFolder ?
|
||||
<div>
|
||||
This will also rename the movie folder per the movie folder format in settings.
|
||||
</div> :
|
||||
null
|
||||
}
|
||||
</ModalBody>
|
||||
|
||||
<ModalFooter>
|
||||
|
@ -39,7 +39,6 @@ public static MediaManagementConfigResource ToResource(IConfigService model)
|
||||
CreateEmptySeriesFolders = model.CreateEmptyMovieFolders,
|
||||
FileDate = model.FileDate,
|
||||
AutoRenameFolders = model.AutoRenameFolders,
|
||||
PathsDefaultStatic = model.PathsDefaultStatic,
|
||||
|
||||
SetPermissionsLinux = model.SetPermissionsLinux,
|
||||
FileChmod = model.FileChmod,
|
||||
|
@ -25,7 +25,7 @@ private object SaveAll()
|
||||
|
||||
var movie = resources.Select(movieResource => movieResource.ToModel(_movieService.GetMovie(movieResource.Id))).ToList();
|
||||
|
||||
return ResponseWithCode(_movieService.UpdateMovie(movie)
|
||||
return ResponseWithCode(_movieService.UpdateMovie(movie, true)
|
||||
.ToResource(),
|
||||
HttpStatusCode.Accepted);
|
||||
}
|
||||
|
@ -43,7 +43,6 @@ public MovieResource()
|
||||
//View & Edit
|
||||
public string Path { get; set; }
|
||||
public int ProfileId { get; set; }
|
||||
public MoviePathState PathState { get; set; }
|
||||
|
||||
//Editing Only
|
||||
public bool Monitored { get; set; }
|
||||
@ -144,7 +143,6 @@ public static MovieResource ToResource(this Core.Movies.Movie model)
|
||||
|
||||
Path = model.Path,
|
||||
ProfileId = model.ProfileId,
|
||||
PathState = model.PathState,
|
||||
|
||||
Monitored = model.Monitored,
|
||||
MinimumAvailability = model.MinimumAvailability,
|
||||
@ -209,7 +207,6 @@ public static Core.Movies.Movie ToModel(this MovieResource resource)
|
||||
|
||||
Path = resource.Path,
|
||||
ProfileId = resource.ProfileId,
|
||||
PathState = resource.PathState,
|
||||
|
||||
Monitored = resource.Monitored,
|
||||
MinimumAvailability = resource.MinimumAvailability,
|
||||
|
@ -153,30 +153,30 @@ public void where_in_list_2()
|
||||
[Test]
|
||||
public void enum_as_int()
|
||||
{
|
||||
_subject = Where(x => x.PathState == MoviePathState.Static);
|
||||
_subject = Where(x => x.Status == MovieStatusType.Released);
|
||||
|
||||
var name = _subject.Parameters.ParameterNames.First();
|
||||
_subject.ToString().Should().Be($"(\"Movies\".\"PathState\" = @{name})");
|
||||
_subject.ToString().Should().Be($"(\"Movies\".\"Status\" = @{name})");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void enum_in_list()
|
||||
{
|
||||
var allowed = new List<MoviePathState> { MoviePathState.Dynamic, MoviePathState.Static };
|
||||
_subject = Where(x => allowed.Contains(x.PathState));
|
||||
var allowed = new List<MovieStatusType> { MovieStatusType.InCinemas, MovieStatusType.Released };
|
||||
_subject = Where(x => allowed.Contains(x.Status));
|
||||
|
||||
var name = _subject.Parameters.ParameterNames.First();
|
||||
_subject.ToString().Should().Be($"(\"Movies\".\"PathState\" IN @{name})");
|
||||
_subject.ToString().Should().Be($"(\"Movies\".\"Status\" IN @{name})");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void enum_in_array()
|
||||
{
|
||||
var allowed = new MoviePathState[] { MoviePathState.Dynamic, MoviePathState.Static };
|
||||
_subject = Where(x => allowed.Contains(x.PathState));
|
||||
var allowed = new MovieStatusType[] { MovieStatusType.InCinemas, MovieStatusType.Released };
|
||||
_subject = Where(x => allowed.Contains(x.Status));
|
||||
|
||||
var name = _subject.Parameters.ParameterNames.First();
|
||||
_subject.ToString().Should().Be($"(\"Movies\".\"PathState\" IN @{name})");
|
||||
_subject.ToString().Should().Be($"(\"Movies\".\"Status\" IN @{name})");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,94 @@
|
||||
using System.IO;
|
||||
using FizzWare.NBuilder;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Movies;
|
||||
using NzbDrone.Core.Organizer;
|
||||
using NzbDrone.Core.RootFolders;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Test.Common;
|
||||
|
||||
namespace NzbDrone.Core.Test.MovieTests
|
||||
{
|
||||
[TestFixture]
|
||||
public class MovieFolderPathBuilderFixture : CoreTest<MoviePathBuilder>
|
||||
{
|
||||
private Movie _movie;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
_movie = Builder<Movie>.CreateNew()
|
||||
.With(s => s.Title = "Movie Title")
|
||||
.With(s => s.Path = @"C:\Test\Movies\Movie.Title".AsOsAgnostic())
|
||||
.With(s => s.RootFolderPath = null)
|
||||
.Build();
|
||||
}
|
||||
|
||||
public void GivenMovieFolderName(string name)
|
||||
{
|
||||
Mocker.GetMock<IBuildFileNames>()
|
||||
.Setup(s => s.GetMovieFolder(_movie, null))
|
||||
.Returns(name);
|
||||
}
|
||||
|
||||
public void GivenExistingRootFolder(string rootFolder)
|
||||
{
|
||||
Mocker.GetMock<IRootFolderService>()
|
||||
.Setup(s => s.GetBestRootFolderPath(It.IsAny<string>()))
|
||||
.Returns(rootFolder);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_create_new_movie_path()
|
||||
{
|
||||
var rootFolder = @"C:\Test\Movies2".AsOsAgnostic();
|
||||
|
||||
GivenMovieFolderName(_movie.Title);
|
||||
_movie.RootFolderPath = rootFolder;
|
||||
|
||||
Subject.BuildPath(_movie, false).Should().Be(Path.Combine(rootFolder, _movie.Title));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_reuse_existing_relative_folder_name()
|
||||
{
|
||||
var folderName = Path.GetFileName(_movie.Path);
|
||||
var rootFolder = @"C:\Test\Movies2".AsOsAgnostic();
|
||||
|
||||
GivenExistingRootFolder(Path.GetDirectoryName(_movie.Path));
|
||||
GivenMovieFolderName(_movie.Title);
|
||||
_movie.RootFolderPath = rootFolder;
|
||||
|
||||
Subject.BuildPath(_movie, true).Should().Be(Path.Combine(rootFolder, folderName));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_reuse_existing_relative_folder_structure()
|
||||
{
|
||||
var existingRootFolder = @"C:\Test\Movies".AsOsAgnostic();
|
||||
var existingRelativePath = @"M\Movie.Title";
|
||||
var rootFolder = @"C:\Test\Movies2".AsOsAgnostic();
|
||||
|
||||
GivenExistingRootFolder(existingRootFolder);
|
||||
GivenMovieFolderName(_movie.Title);
|
||||
_movie.RootFolderPath = rootFolder;
|
||||
_movie.Path = Path.Combine(existingRootFolder, existingRelativePath);
|
||||
|
||||
Subject.BuildPath(_movie, true).Should().Be(Path.Combine(rootFolder, existingRelativePath));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_use_built_path_for_new_movie()
|
||||
{
|
||||
var rootFolder = @"C:\Test\Movies2".AsOsAgnostic();
|
||||
|
||||
GivenMovieFolderName(_movie.Title);
|
||||
_movie.RootFolderPath = rootFolder;
|
||||
_movie.Path = null;
|
||||
|
||||
Subject.BuildPath(_movie, true).Should().Be(Path.Combine(rootFolder, _movie.Title));
|
||||
}
|
||||
}
|
||||
}
|
@ -1,10 +1,12 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using FizzWare.NBuilder;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Movies;
|
||||
using NzbDrone.Core.Organizer;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Test.Common;
|
||||
|
||||
@ -30,7 +32,7 @@ public void Setup()
|
||||
[Test]
|
||||
public void should_call_repo_updateMany()
|
||||
{
|
||||
Subject.UpdateMovie(_movies);
|
||||
Subject.UpdateMovie(_movies, false);
|
||||
|
||||
Mocker.GetMock<IMovieRepository>().Verify(v => v.UpdateMany(_movies), Times.Once());
|
||||
}
|
||||
@ -41,13 +43,17 @@ public void should_update_path_when_rootFolderPath_is_supplied()
|
||||
var newRoot = @"C:\Test\TV2".AsOsAgnostic();
|
||||
_movies.ForEach(s => s.RootFolderPath = newRoot);
|
||||
|
||||
Subject.UpdateMovie(_movies).ForEach(s => s.Path.Should().StartWith(newRoot));
|
||||
Mocker.GetMock<IBuildMoviePaths>()
|
||||
.Setup(s => s.BuildPath(It.IsAny<Movie>(), false))
|
||||
.Returns<Movie, bool>((s, u) => Path.Combine(s.RootFolderPath, s.Title));
|
||||
|
||||
Subject.UpdateMovie(_movies, false).ForEach(s => s.Path.Should().StartWith(newRoot));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_update_path_when_rootFolderPath_is_empty()
|
||||
{
|
||||
Subject.UpdateMovie(_movies).ForEach(s =>
|
||||
Subject.UpdateMovie(_movies, false).ForEach(s =>
|
||||
{
|
||||
var expectedPath = _movies.Single(ser => ser.Id == s.Id).Path;
|
||||
s.Path.Should().Be(expectedPath);
|
||||
@ -66,7 +72,11 @@ public void should_be_able_to_update_many_movies()
|
||||
var newRoot = @"C:\Test\Movies2".AsOsAgnostic();
|
||||
movies.ForEach(s => s.RootFolderPath = newRoot);
|
||||
|
||||
Subject.UpdateMovie(movies);
|
||||
Mocker.GetMock<IBuildFileNames>()
|
||||
.Setup(s => s.GetMovieFolder(It.IsAny<Movie>(), (NamingConfig)null))
|
||||
.Returns<Movie, NamingConfig>((s, n) => s.Title);
|
||||
|
||||
Subject.UpdateMovie(movies, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -116,7 +116,7 @@ public void should_not_clean_library_if_config_value_disable()
|
||||
.Verify(v => v.GetAllMovies(), Times.Never());
|
||||
|
||||
Mocker.GetMock<IMovieService>()
|
||||
.Verify(v => v.UpdateMovie(new List<Movie>()), Times.Once());
|
||||
.Verify(v => v.UpdateMovie(new List<Movie>(), true), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -139,7 +139,7 @@ public void should_log_only_on_clean_library_if_config_value_logonly()
|
||||
.Verify(v => v.DeleteMovie(It.IsAny<int>(), It.IsAny<bool>(), It.IsAny<bool>()), Times.Never());
|
||||
|
||||
Mocker.GetMock<IMovieService>()
|
||||
.Verify(v => v.UpdateMovie(new List<Movie>()), Times.Once());
|
||||
.Verify(v => v.UpdateMovie(new List<Movie>(), true), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -162,7 +162,7 @@ public void should_unmonitor_on_clean_library_if_config_value_keepAndUnmonitor()
|
||||
.Verify(v => v.DeleteMovie(It.IsAny<int>(), It.IsAny<bool>(), It.IsAny<bool>()), Times.Never());
|
||||
|
||||
Mocker.GetMock<IMovieService>()
|
||||
.Verify(v => v.UpdateMovie(It.Is<List<Movie>>(s => s.Count == 3 && s.All(m => !m.Monitored))), Times.Once());
|
||||
.Verify(v => v.UpdateMovie(It.Is<List<Movie>>(s => s.Count == 3 && s.All(m => !m.Monitored)), true), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -181,7 +181,7 @@ public void should_not_clean_on_clean_library_if_tmdb_match()
|
||||
Subject.Execute(_command);
|
||||
|
||||
Mocker.GetMock<IMovieService>()
|
||||
.Verify(v => v.UpdateMovie(It.Is<List<Movie>>(s => s.Count == 2 && s.All(m => !m.Monitored))), Times.Once());
|
||||
.Verify(v => v.UpdateMovie(It.Is<List<Movie>>(s => s.Count == 2 && s.All(m => !m.Monitored)), true), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -201,7 +201,7 @@ public void should_fallback_to_imdbid_on_clean_library_if_tmdb_not_found()
|
||||
Subject.Execute(_command);
|
||||
|
||||
Mocker.GetMock<IMovieService>()
|
||||
.Verify(v => v.UpdateMovie(It.Is<List<Movie>>(s => s.Count == 2 && s.All(m => !m.Monitored))), Times.Once());
|
||||
.Verify(v => v.UpdateMovie(It.Is<List<Movie>>(s => s.Count == 2 && s.All(m => !m.Monitored)), true), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -227,7 +227,7 @@ public void should_delete_movies_not_files_on_clean_library_if_config_value_logo
|
||||
.Verify(v => v.DeleteMovie(It.IsAny<int>(), true, It.IsAny<bool>()), Times.Never());
|
||||
|
||||
Mocker.GetMock<IMovieService>()
|
||||
.Verify(v => v.UpdateMovie(new List<Movie>()), Times.Once());
|
||||
.Verify(v => v.UpdateMovie(new List<Movie>(), true), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -253,7 +253,7 @@ public void should_delete_movies_and_files_on_clean_library_if_config_value_logo
|
||||
.Verify(v => v.DeleteMovie(It.IsAny<int>(), true, It.IsAny<bool>()), Times.Exactly(3));
|
||||
|
||||
Mocker.GetMock<IMovieService>()
|
||||
.Verify(v => v.UpdateMovie(new List<Movie>()), Times.Once());
|
||||
.Verify(v => v.UpdateMovie(new List<Movie>(), true), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -267,7 +267,7 @@ public void should_not_clean_if_list_failures()
|
||||
Subject.Execute(_command);
|
||||
|
||||
Mocker.GetMock<IMovieService>()
|
||||
.Verify(v => v.UpdateMovie(new List<Movie>()), Times.Never());
|
||||
.Verify(v => v.UpdateMovie(new List<Movie>(), true), Times.Never());
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
@ -299,13 +299,6 @@ public bool AutoRenameFolders
|
||||
set { SetValue("AutoRenameFolders", value); }
|
||||
}
|
||||
|
||||
public bool PathsDefaultStatic
|
||||
{
|
||||
get { return GetValueBoolean("PathsDefaultStatic", true); }
|
||||
|
||||
set { SetValue("PathsDefaultStatic", value); }
|
||||
}
|
||||
|
||||
public RescanAfterRefreshType RescanAfterRefresh
|
||||
{
|
||||
get { return GetValueEnum("RescanAfterRefresh", RescanAfterRefreshType.Always); }
|
||||
|
@ -40,7 +40,6 @@ public interface IConfigService
|
||||
string ExtraFileExtensions { get; set; }
|
||||
RescanAfterRefreshType RescanAfterRefresh { get; set; }
|
||||
bool AutoRenameFolders { get; set; }
|
||||
bool PathsDefaultStatic { get; set; }
|
||||
|
||||
//Permissions (Media Management)
|
||||
bool SetPermissionsLinux { get; set; }
|
||||
|
@ -0,0 +1,21 @@
|
||||
using FluentMigrator;
|
||||
using NzbDrone.Core.Datastore.Migration.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Datastore.Migration
|
||||
{
|
||||
[Migration(167)]
|
||||
public class remove_movie_pathstate : NzbDroneMigrationBase
|
||||
{
|
||||
protected override void MainDbUpgrade()
|
||||
{
|
||||
Delete.Column("PathState").FromTable("Movies");
|
||||
|
||||
Execute.Sql("DELETE FROM Config WHERE [KEY] IN ('pathsdefaultstatic')");
|
||||
|
||||
Alter.Table("MovieFiles").AddColumn("OriginalFilePath").AsString().Nullable();
|
||||
|
||||
//This is Ignored in mapping, should not be in DB
|
||||
Delete.Column("Path").FromTable("MovieFiles");
|
||||
}
|
||||
}
|
||||
}
|
@ -1,13 +1,11 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.SQLite;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Disk;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Instrumentation.Extensions;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.MediaFiles.Commands;
|
||||
using NzbDrone.Core.MediaFiles.Events;
|
||||
using NzbDrone.Core.Messaging.Commands;
|
||||
@ -20,22 +18,18 @@ namespace NzbDrone.Core.MediaFiles
|
||||
public interface IRenameMovieFileService
|
||||
{
|
||||
List<RenameMovieFilePreview> GetRenamePreviews(int movieId);
|
||||
void RenameMoviePath(Movie movie, bool shouldRenameFiles);
|
||||
}
|
||||
|
||||
public class RenameMovieFileService : IRenameMovieFileService,
|
||||
IExecute<RenameFilesCommand>,
|
||||
IExecute<RenameMovieCommand>,
|
||||
IExecute<RenameMovieFolderCommand>
|
||||
IExecute<RenameMovieCommand>
|
||||
{
|
||||
private readonly IMovieService _movieService;
|
||||
private readonly IMediaFileService _mediaFileService;
|
||||
private readonly IMoveMovieFiles _movieFileMover;
|
||||
private readonly IEventAggregator _eventAggregator;
|
||||
private readonly IBuildFileNames _filenameBuilder;
|
||||
private readonly IConfigService _configService;
|
||||
private readonly IDiskProvider _diskProvider;
|
||||
private readonly IRecycleBinProvider _recycleBinProvider;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public RenameMovieFileService(IMovieService movieService,
|
||||
@ -43,8 +37,6 @@ public RenameMovieFileService(IMovieService movieService,
|
||||
IMoveMovieFiles movieFileMover,
|
||||
IEventAggregator eventAggregator,
|
||||
IBuildFileNames filenameBuilder,
|
||||
IConfigService configService,
|
||||
IRecycleBinProvider recycleBinProvider,
|
||||
IDiskProvider diskProvider,
|
||||
Logger logger)
|
||||
{
|
||||
@ -53,8 +45,6 @@ public RenameMovieFileService(IMovieService movieService,
|
||||
_movieFileMover = movieFileMover;
|
||||
_eventAggregator = eventAggregator;
|
||||
_filenameBuilder = filenameBuilder;
|
||||
_configService = configService;
|
||||
_recycleBinProvider = recycleBinProvider;
|
||||
_diskProvider = diskProvider;
|
||||
_logger = logger;
|
||||
}
|
||||
@ -82,28 +72,21 @@ private IEnumerable<RenameMovieFilePreview> GetPreviews(Movie movie, List<MovieF
|
||||
{
|
||||
MovieId = movie.Id,
|
||||
MovieFileId = file.Id,
|
||||
ExistingPath = movieFilePath,
|
||||
ExistingPath = file.RelativePath,
|
||||
|
||||
//NewPath = movie.Path.GetRelativePath(newPath)
|
||||
NewPath = newPath
|
||||
NewPath = movie.Path.GetRelativePath(newPath)
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void RenameFiles(List<MovieFile> movieFiles, Movie movie, string oldMoviePath = null)
|
||||
private void RenameFiles(List<MovieFile> movieFiles, Movie movie)
|
||||
{
|
||||
var renamed = new List<MovieFile>();
|
||||
|
||||
if (oldMoviePath == null)
|
||||
{
|
||||
oldMoviePath = movie.Path;
|
||||
}
|
||||
|
||||
foreach (var movieFile in movieFiles)
|
||||
{
|
||||
var oldMovieFilePath = Path.Combine(oldMoviePath, movieFile.RelativePath);
|
||||
movieFile.Path = oldMovieFilePath;
|
||||
var movieFilePath = Path.Combine(movie.Path, movieFile.RelativePath);
|
||||
|
||||
try
|
||||
{
|
||||
@ -116,7 +99,7 @@ private void RenameFiles(List<MovieFile> movieFiles, Movie movie, string oldMovi
|
||||
|
||||
_logger.Debug("Renamed movie file: {0}", movieFile);
|
||||
|
||||
_eventAggregator.PublishEvent(new MovieFileRenamedEvent(movie, movieFile, oldMovieFilePath));
|
||||
_eventAggregator.PublishEvent(new MovieFileRenamedEvent(movie, movieFile, movieFilePath));
|
||||
}
|
||||
catch (SameFilenameException ex)
|
||||
{
|
||||
@ -124,55 +107,18 @@ private void RenameFiles(List<MovieFile> movieFiles, Movie movie, string oldMovi
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(ex, "Failed to rename file: {0}", oldMovieFilePath);
|
||||
_logger.Error(ex, "Failed to rename file: {0}", movieFilePath);
|
||||
}
|
||||
}
|
||||
|
||||
if (renamed.Any())
|
||||
{
|
||||
_diskProvider.RemoveEmptySubfolders(movie.Path);
|
||||
|
||||
_eventAggregator.PublishEvent(new MovieRenamedEvent(movie));
|
||||
}
|
||||
}
|
||||
|
||||
public void RenameMoviePath(Movie movie, bool shouldRenameFiles = true)
|
||||
{
|
||||
var newFolder = _filenameBuilder.BuildMoviePath(movie);
|
||||
if (newFolder != movie.Path && movie.PathState == MoviePathState.Dynamic)
|
||||
{
|
||||
if (!_configService.AutoRenameFolders)
|
||||
{
|
||||
_logger.Info("{0}'s movie should be {1} according to your naming config.", movie, newFolder);
|
||||
return;
|
||||
}
|
||||
|
||||
_logger.Info("{0}'s movie folder changed to: {1}", movie, newFolder);
|
||||
var oldFolder = movie.Path;
|
||||
movie.Path = newFolder;
|
||||
|
||||
_diskProvider.MoveFolder(oldFolder, movie.Path);
|
||||
|
||||
// if (false)
|
||||
// {
|
||||
// var movieFiles = _mediaFileService.GetFilesByMovie(movie.Id);
|
||||
// _logger.ProgressInfo("Renaming movie files for {0}", movie.Title);
|
||||
// RenameFiles(movieFiles, movie, oldFolder);
|
||||
// _logger.ProgressInfo("All movie files renamed for {0}", movie.Title);
|
||||
// }
|
||||
_movieService.UpdateMovie(movie);
|
||||
|
||||
if (_diskProvider.GetFiles(oldFolder, SearchOption.AllDirectories).Count() == 0)
|
||||
{
|
||||
_recycleBinProvider.DeleteFolder(oldFolder);
|
||||
}
|
||||
}
|
||||
|
||||
if (movie.PathState == MoviePathState.StaticOnce)
|
||||
{
|
||||
movie.PathState = MoviePathState.Dynamic;
|
||||
_movieService.UpdateMovie(movie);
|
||||
}
|
||||
}
|
||||
|
||||
public void Execute(RenameFilesCommand message)
|
||||
{
|
||||
var movie = _movieService.GetMovie(message.MovieId);
|
||||
@ -196,25 +142,5 @@ public void Execute(RenameMovieCommand message)
|
||||
_logger.ProgressInfo("All movie files renamed for {0}", movie.Title);
|
||||
}
|
||||
}
|
||||
|
||||
public void Execute(RenameMovieFolderCommand message)
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger.Debug("Renaming movie folder for selected movie if necessary");
|
||||
var moviesToRename = _movieService.GetMovies(message.MovieIds);
|
||||
foreach (var movie in moviesToRename)
|
||||
{
|
||||
var movieFiles = _mediaFileService.GetFilesByMovie(movie.Id);
|
||||
|
||||
//_logger.ProgressInfo("Renaming movie folder for {0}", movie.Title);
|
||||
RenameMoviePath(movie);
|
||||
}
|
||||
}
|
||||
catch (SQLiteException ex)
|
||||
{
|
||||
_logger.Warn(ex, "wtf: {0}, {1}", ex.ResultCode, ex.Data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ public class MoveMovieCommand : Command
|
||||
public string DestinationPath { get; set; }
|
||||
public string DestinationRootFolder { get; set; }
|
||||
|
||||
public override bool SendUpdatesToClient => true;
|
||||
public override bool RequiresDiskAccess => true;
|
||||
}
|
||||
}
|
||||
|
@ -42,7 +42,6 @@ public Movie()
|
||||
|
||||
public string Certification { get; set; }
|
||||
public string RootFolderPath { get; set; }
|
||||
public MoviePathState PathState { get; set; }
|
||||
public DateTime Added { get; set; }
|
||||
public DateTime? InCinemas { get; set; }
|
||||
public DateTime? PhysicalRelease { get; set; }
|
||||
@ -154,7 +153,6 @@ public void ApplyChanges(Movie otherMovie)
|
||||
|
||||
Path = otherMovie.Path;
|
||||
ProfileId = otherMovie.ProfileId;
|
||||
PathState = otherMovie.PathState;
|
||||
|
||||
Monitored = otherMovie.Monitored;
|
||||
MinimumAvailability = otherMovie.MinimumAvailability;
|
||||
@ -164,11 +162,4 @@ public void ApplyChanges(Movie otherMovie)
|
||||
AddOptions = otherMovie.AddOptions;
|
||||
}
|
||||
}
|
||||
|
||||
public enum MoviePathState
|
||||
{
|
||||
Dynamic,
|
||||
StaticOnce,
|
||||
Static,
|
||||
}
|
||||
}
|
||||
|
48
src/NzbDrone.Core/Movies/MoviePathBuilder.cs
Normal file
48
src/NzbDrone.Core/Movies/MoviePathBuilder.cs
Normal file
@ -0,0 +1,48 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Organizer;
|
||||
using NzbDrone.Core.RootFolders;
|
||||
|
||||
namespace NzbDrone.Core.Movies
|
||||
{
|
||||
public interface IBuildMoviePaths
|
||||
{
|
||||
string BuildPath(Movie movie, bool useExistingRelativeFolder);
|
||||
}
|
||||
|
||||
public class MoviePathBuilder : IBuildMoviePaths
|
||||
{
|
||||
private readonly IBuildFileNames _fileNameBuilder;
|
||||
private readonly IRootFolderService _rootFolderService;
|
||||
|
||||
public MoviePathBuilder(IBuildFileNames fileNameBuilder, IRootFolderService rootFolderService)
|
||||
{
|
||||
_fileNameBuilder = fileNameBuilder;
|
||||
_rootFolderService = rootFolderService;
|
||||
}
|
||||
|
||||
public string BuildPath(Movie movie, bool useExistingRelativeFolder)
|
||||
{
|
||||
if (movie.RootFolderPath.IsNullOrWhiteSpace())
|
||||
{
|
||||
throw new ArgumentException("Root folder was not provided", nameof(movie));
|
||||
}
|
||||
|
||||
if (useExistingRelativeFolder && movie.Path.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
var relativePath = GetExistingRelativePath(movie);
|
||||
return Path.Combine(movie.RootFolderPath, relativePath);
|
||||
}
|
||||
|
||||
return Path.Combine(movie.RootFolderPath, _fileNameBuilder.GetMovieFolder(movie));
|
||||
}
|
||||
|
||||
private string GetExistingRelativePath(Movie movie)
|
||||
{
|
||||
var rootFolderPath = _rootFolderService.GetBestRootFolderPath(movie.Path);
|
||||
|
||||
return rootFolderPath.GetRelativePath(movie.Path);
|
||||
}
|
||||
}
|
||||
}
|
@ -43,7 +43,7 @@ public interface IMovieService
|
||||
List<Movie> GetAllMovies();
|
||||
List<Movie> AllForTag(int tagId);
|
||||
Movie UpdateMovie(Movie movie);
|
||||
List<Movie> UpdateMovie(List<Movie> movie);
|
||||
List<Movie> UpdateMovie(List<Movie> movie, bool useExistingRelativeFolder);
|
||||
List<Movie> FilterExistingMovies(List<Movie> movies);
|
||||
bool MoviePathExists(string folder);
|
||||
void RemoveAddOptions(Movie movie);
|
||||
@ -57,6 +57,7 @@ public class MovieService : IMovieService, IHandle<MovieFileAddedEvent>,
|
||||
private readonly IEventAggregator _eventAggregator;
|
||||
private readonly IBuildFileNames _fileNameBuilder;
|
||||
private readonly IImportExclusionsService _exclusionService;
|
||||
private readonly IBuildMoviePaths _moviePathBuilder;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public MovieService(IMovieRepository movieRepository,
|
||||
@ -64,6 +65,7 @@ public MovieService(IMovieRepository movieRepository,
|
||||
IBuildFileNames fileNameBuilder,
|
||||
IConfigService configService,
|
||||
IImportExclusionsService exclusionService,
|
||||
IBuildMoviePaths moviePathBuilder,
|
||||
Logger logger)
|
||||
{
|
||||
_movieRepository = movieRepository;
|
||||
@ -71,6 +73,7 @@ public MovieService(IMovieRepository movieRepository,
|
||||
_fileNameBuilder = fileNameBuilder;
|
||||
_configService = configService;
|
||||
_exclusionService = exclusionService;
|
||||
_moviePathBuilder = moviePathBuilder;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
@ -93,21 +96,10 @@ public Movie AddMovie(Movie newMovie)
|
||||
{
|
||||
Ensure.That(newMovie, () => newMovie).IsNotNull();
|
||||
|
||||
MoviePathState defaultState = MoviePathState.Static;
|
||||
if (!_configService.PathsDefaultStatic)
|
||||
{
|
||||
defaultState = MoviePathState.Dynamic;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(newMovie.Path))
|
||||
{
|
||||
var folderName = _fileNameBuilder.GetMovieFolder(newMovie);
|
||||
newMovie.Path = Path.Combine(newMovie.RootFolderPath, folderName);
|
||||
newMovie.PathState = defaultState;
|
||||
}
|
||||
else
|
||||
{
|
||||
newMovie.PathState = defaultState == MoviePathState.Dynamic ? MoviePathState.StaticOnce : MoviePathState.Static;
|
||||
}
|
||||
|
||||
_logger.Info("Adding Movie {0} Path: [{1}]", newMovie, newMovie.Path);
|
||||
@ -128,21 +120,10 @@ public List<Movie> AddMovies(List<Movie> newMovies)
|
||||
|
||||
newMovies.ForEach(m =>
|
||||
{
|
||||
MoviePathState defaultState = MoviePathState.Static;
|
||||
if (!_configService.PathsDefaultStatic)
|
||||
{
|
||||
defaultState = MoviePathState.Dynamic;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(m.Path))
|
||||
{
|
||||
var folderName = _fileNameBuilder.GetMovieFolder(m);
|
||||
m.Path = Path.Combine(m.RootFolderPath, folderName);
|
||||
m.PathState = defaultState;
|
||||
}
|
||||
else
|
||||
{
|
||||
m.PathState = defaultState == MoviePathState.Dynamic ? MoviePathState.StaticOnce : MoviePathState.Static;
|
||||
}
|
||||
|
||||
m.CleanTitle = m.Title.CleanSeriesTitle();
|
||||
@ -324,21 +305,22 @@ public Movie UpdateMovie(Movie movie)
|
||||
return updatedMovie;
|
||||
}
|
||||
|
||||
public List<Movie> UpdateMovie(List<Movie> movie)
|
||||
public List<Movie> UpdateMovie(List<Movie> movie, bool useExistingRelativeFolder)
|
||||
{
|
||||
_logger.Debug("Updating {0} movie", movie.Count);
|
||||
foreach (var s in movie)
|
||||
foreach (var m in movie)
|
||||
{
|
||||
_logger.Trace("Updating: {0}", s.Title);
|
||||
if (!s.RootFolderPath.IsNullOrWhiteSpace())
|
||||
_logger.Trace("Updating: {0}", m.Title);
|
||||
|
||||
if (!m.RootFolderPath.IsNullOrWhiteSpace())
|
||||
{
|
||||
var folderName = new DirectoryInfo(s.Path).Name;
|
||||
s.Path = Path.Combine(s.RootFolderPath, folderName);
|
||||
_logger.Trace("Changing path for {0} to {1}", s.Title, s.Path);
|
||||
m.Path = _moviePathBuilder.BuildPath(m, useExistingRelativeFolder);
|
||||
|
||||
_logger.Trace("Changing path for {0} to {1}", m.Title, m.Path);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Trace("Not changing path for: {0}", s.Title);
|
||||
_logger.Trace("Not changing path for: {0}", m.Title);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -138,7 +138,7 @@ private void RefreshMovieInfo(Movie movie)
|
||||
_logger.Info(ex, "Unable to communicate with Mappings Server.");
|
||||
}
|
||||
|
||||
_movieService.UpdateMovie(new List<Movie> { movie });
|
||||
_movieService.UpdateMovie(new List<Movie> { movie }, true);
|
||||
_creditService.UpdateCredits(tuple.Item2, movie);
|
||||
|
||||
try
|
||||
|
@ -194,7 +194,7 @@ private void CleanLibrary(List<Movie> movies)
|
||||
}
|
||||
}
|
||||
|
||||
_movieService.UpdateMovie(moviesToUpdate);
|
||||
_movieService.UpdateMovie(moviesToUpdate, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -18,7 +18,6 @@ public interface IBuildFileNames
|
||||
{
|
||||
string BuildFileName(Movie movie, MovieFile movieFile, NamingConfig namingConfig = null);
|
||||
string BuildFilePath(Movie movie, string fileName, string extension);
|
||||
string BuildMoviePath(Movie movie, NamingConfig namingConfig = null);
|
||||
BasicNamingConfig GetBasicNamingConfig(NamingConfig nameSpec);
|
||||
string GetMovieFolder(Movie movie, NamingConfig namingConfig = null);
|
||||
}
|
||||
@ -110,59 +109,11 @@ public string BuildFilePath(Movie movie, string fileName, string extension)
|
||||
{
|
||||
Ensure.That(extension, () => extension).IsNotNullOrWhiteSpace();
|
||||
|
||||
var path = "";
|
||||
|
||||
if (movie.PathState > 0)
|
||||
{
|
||||
path = movie.Path;
|
||||
}
|
||||
else
|
||||
{
|
||||
path = BuildMoviePath(movie);
|
||||
}
|
||||
var path = movie.Path;
|
||||
|
||||
return Path.Combine(path, fileName + extension);
|
||||
}
|
||||
|
||||
public string BuildMoviePath(Movie movie, NamingConfig namingConfig = null)
|
||||
{
|
||||
if (namingConfig == null)
|
||||
{
|
||||
namingConfig = _namingConfigService.GetConfig();
|
||||
}
|
||||
|
||||
var path = movie.Path;
|
||||
var directory = new DirectoryInfo(path).Name;
|
||||
var parentDirectoryPath = new DirectoryInfo(path).Parent.FullName;
|
||||
|
||||
var movieFile = movie.MovieFile;
|
||||
|
||||
var pattern = namingConfig.MovieFolderFormat;
|
||||
var tokenHandlers = new Dictionary<string, Func<TokenMatch, string>>(FileNameBuilderTokenEqualityComparer.Instance);
|
||||
|
||||
AddMovieTokens(tokenHandlers, movie);
|
||||
AddReleaseDateTokens(tokenHandlers, movie.Year);
|
||||
AddIdTokens(tokenHandlers, movie);
|
||||
|
||||
if (movie.MovieFile != null)
|
||||
{
|
||||
AddQualityTokens(tokenHandlers, movie, movieFile);
|
||||
AddMediaInfoTokens(tokenHandlers, movieFile);
|
||||
AddMovieFileTokens(tokenHandlers, movieFile);
|
||||
AddTagsTokens(tokenHandlers, movieFile);
|
||||
}
|
||||
else
|
||||
{
|
||||
AddMovieFileTokens(tokenHandlers, new MovieFile { SceneName = $"{movie.Title} {movie.Year}", RelativePath = $"{movie.Title} {movie.Year}" });
|
||||
}
|
||||
|
||||
var directoryName = ReplaceTokens(pattern, tokenHandlers, namingConfig).Trim();
|
||||
directoryName = FileNameCleanupRegex.Replace(directoryName, match => match.Captures[0].Value[0].ToString());
|
||||
directoryName = TrimSeparatorsRegex.Replace(directoryName, string.Empty);
|
||||
|
||||
return Path.Combine(parentDirectoryPath, directoryName);
|
||||
}
|
||||
|
||||
public BasicNamingConfig GetBasicNamingConfig(NamingConfig nameSpec)
|
||||
{
|
||||
return new BasicNamingConfig(); //For now let's be lazy
|
||||
|
@ -46,7 +46,6 @@ public static MediaManagementConfigResource ToResource(IConfigService model)
|
||||
FileDate = model.FileDate,
|
||||
RescanAfterRefresh = model.RescanAfterRefresh,
|
||||
AutoRenameFolders = model.AutoRenameFolders,
|
||||
PathsDefaultStatic = model.PathsDefaultStatic,
|
||||
|
||||
SetPermissionsLinux = model.SetPermissionsLinux,
|
||||
FileChmod = model.FileChmod,
|
||||
|
@ -85,7 +85,7 @@ private object SaveAll()
|
||||
});
|
||||
}
|
||||
|
||||
return ResponseWithCode(_movieService.UpdateMovie(moviesToUpdate)
|
||||
return ResponseWithCode(_movieService.UpdateMovie(moviesToUpdate, !resource.MoveFiles)
|
||||
.ToResource(),
|
||||
HttpStatusCode.Accepted);
|
||||
}
|
||||
|
@ -7,8 +7,10 @@
|
||||
using NzbDrone.Core.MediaCover;
|
||||
using NzbDrone.Core.MediaFiles;
|
||||
using NzbDrone.Core.MediaFiles.Events;
|
||||
using NzbDrone.Core.Messaging.Commands;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.Movies;
|
||||
using NzbDrone.Core.Movies.Commands;
|
||||
using NzbDrone.Core.Movies.Events;
|
||||
using NzbDrone.Core.Validation;
|
||||
using NzbDrone.Core.Validation.Paths;
|
||||
@ -29,11 +31,13 @@ public class MovieModule : RadarrRestModuleWithSignalR<MovieResource, Movie>,
|
||||
{
|
||||
protected readonly IMovieService _moviesService;
|
||||
private readonly IMapCoversToLocal _coverMapper;
|
||||
private readonly IManageCommandQueue _commandQueueManager;
|
||||
private readonly IUpgradableSpecification _qualityUpgradableSpecification;
|
||||
|
||||
public MovieModule(IBroadcastSignalRMessage signalRBroadcaster,
|
||||
IMovieService moviesService,
|
||||
IMapCoversToLocal coverMapper,
|
||||
IManageCommandQueue commandQueueManager,
|
||||
IUpgradableSpecification qualityUpgradableSpecification,
|
||||
RootFolderValidator rootFolderValidator,
|
||||
MoviePathValidator moviesPathValidator,
|
||||
@ -46,6 +50,7 @@ public MovieModule(IBroadcastSignalRMessage signalRBroadcaster,
|
||||
_moviesService = moviesService;
|
||||
_qualityUpgradableSpecification = qualityUpgradableSpecification;
|
||||
_coverMapper = coverMapper;
|
||||
_commandQueueManager = commandQueueManager;
|
||||
|
||||
GetResourceAll = AllMovie;
|
||||
GetResourceById = GetMovie;
|
||||
@ -114,7 +119,24 @@ private int AddMovie(MovieResource moviesResource)
|
||||
|
||||
private void UpdateMovie(MovieResource moviesResource)
|
||||
{
|
||||
var model = moviesResource.ToModel(_moviesService.GetMovie(moviesResource.Id));
|
||||
var moveFiles = Request.GetBooleanQueryParameter("moveFiles");
|
||||
var movie = _moviesService.GetMovie(moviesResource.Id);
|
||||
|
||||
if (moveFiles)
|
||||
{
|
||||
var sourcePath = movie.Path;
|
||||
var destinationPath = moviesResource.Path;
|
||||
|
||||
_commandQueueManager.Push(new MoveMovieCommand
|
||||
{
|
||||
MovieId = movie.Id,
|
||||
SourcePath = sourcePath,
|
||||
DestinationPath = destinationPath,
|
||||
Trigger = CommandTrigger.Manual
|
||||
});
|
||||
}
|
||||
|
||||
var model = moviesResource.ToModel(movie);
|
||||
|
||||
_moviesService.UpdateMovie(model);
|
||||
|
||||
|
@ -45,7 +45,6 @@ public MovieResource()
|
||||
//View & Edit
|
||||
public string Path { get; set; }
|
||||
public int QualityProfileId { get; set; }
|
||||
public MoviePathState PathState { get; set; }
|
||||
|
||||
//Editing Only
|
||||
public bool Monitored { get; set; }
|
||||
@ -105,7 +104,6 @@ public static MovieResource ToResource(this Movie model)
|
||||
|
||||
Path = model.Path,
|
||||
QualityProfileId = model.ProfileId,
|
||||
PathState = model.PathState,
|
||||
|
||||
Monitored = model.Monitored,
|
||||
MinimumAvailability = model.MinimumAvailability,
|
||||
@ -166,7 +164,6 @@ public static MovieResource ToResource(this Movie model, IUpgradableSpecificatio
|
||||
|
||||
Path = model.Path,
|
||||
QualityProfileId = model.ProfileId,
|
||||
PathState = model.PathState,
|
||||
|
||||
Monitored = model.Monitored,
|
||||
MinimumAvailability = model.MinimumAvailability,
|
||||
@ -221,7 +218,6 @@ public static Movie ToModel(this MovieResource resource)
|
||||
|
||||
Path = resource.Path,
|
||||
ProfileId = resource.QualityProfileId,
|
||||
PathState = resource.PathState,
|
||||
|
||||
Monitored = resource.Monitored,
|
||||
MinimumAvailability = resource.MinimumAvailability,
|
||||
|
Loading…
Reference in New Issue
Block a user