diff --git a/src/NzbDrone.Api/Indexers/ReleaseResource.cs b/src/NzbDrone.Api/Indexers/ReleaseResource.cs index 291879a44..fea1ee353 100644 --- a/src/NzbDrone.Api/Indexers/ReleaseResource.cs +++ b/src/NzbDrone.Api/Indexers/ReleaseResource.cs @@ -107,8 +107,8 @@ namespace NzbDrone.Api.Indexers ReleaseGroup = parsedMovieInfo.ReleaseGroup, ReleaseHash = parsedMovieInfo.ReleaseHash, Title = releaseInfo.Title, - FullSeason = parsedMovieInfo.FullSeason, - SeasonNumber = parsedMovieInfo.SeasonNumber, + //FullSeason = parsedMovieInfo.FullSeason, + //SeasonNumber = parsedMovieInfo.SeasonNumber, Language = parsedMovieInfo.Language, AirDate = "", SeriesTitle = parsedMovieInfo.MovieTitle, @@ -138,7 +138,7 @@ namespace NzbDrone.Api.Indexers IsDaily = false, IsAbsoluteNumbering = false, IsPossibleSpecialEpisode = false, - Special = parsedMovieInfo.Special, + //Special = parsedMovieInfo.Special, }; } diff --git a/src/NzbDrone.Api/Movies/MovieModule.cs b/src/NzbDrone.Api/Movies/MovieModule.cs new file mode 100644 index 000000000..2a4d405fc --- /dev/null +++ b/src/NzbDrone.Api/Movies/MovieModule.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace NzbDrone.Api.Movies +{ + class MovieModule + { + } +} diff --git a/src/NzbDrone.Api/Movies/RenameMovieModule.cs b/src/NzbDrone.Api/Movies/RenameMovieModule.cs new file mode 100644 index 000000000..8736cccb0 --- /dev/null +++ b/src/NzbDrone.Api/Movies/RenameMovieModule.cs @@ -0,0 +1,35 @@ +using NzbDrone.Api.REST; +using NzbDrone.Core.MediaFiles; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace NzbDrone.Api.Movies +{ + public class RenameMovieModule : NzbDroneRestModule + { + private readonly IRenameMovieFileService _renameMovieFileService; + + public RenameMovieModule(IRenameMovieFileService renameMovieFileService) + : base("rename") + { + _renameMovieFileService = renameMovieFileService; + + GetResourceAll = GetMovies; //TODO: GetResourceSingle? + } + + private List GetMovies() + { + if(!Request.Query.MovieId.HasValue) + { + throw new BadRequestException("movieId is missing"); + } + + var movieId = (int)Request.Query.MovieId; + + return _renameMovieFileService.GetRenamePreviews(movieId).ToResource(); + } + + } +} diff --git a/src/NzbDrone.Api/Movies/RenameMovieResource.cs b/src/NzbDrone.Api/Movies/RenameMovieResource.cs new file mode 100644 index 000000000..d71f1bbcf --- /dev/null +++ b/src/NzbDrone.Api/Movies/RenameMovieResource.cs @@ -0,0 +1,35 @@ +using NzbDrone.Api.REST; +using System.Collections.Generic; +using System.Linq; + +namespace NzbDrone.Api.Movies +{ + public class RenameMovieResource : RestResource + { + public int MovieId { get; set; } + public int MovieFileId { get; set; } + public string ExistingPath { get; set; } + public string NewPath { get; set; } + } + + public static class RenameMovieResourceMapper + { + public static RenameMovieResource ToResource(this Core.MediaFiles.RenameMovieFilePreview model) + { + if (model == null) return null; + + return new RenameMovieResource + { + MovieId = model.MovieId, + MovieFileId = model.MovieFileId, + ExistingPath = model.ExistingPath, + NewPath = model.NewPath + }; + } + + public static List ToResource(this IEnumerable models) + { + return models.Select(ToResource).ToList(); + } + } +} diff --git a/src/NzbDrone.Api/NzbDrone.Api.csproj b/src/NzbDrone.Api/NzbDrone.Api.csproj index 455cc845a..2a3dcba5a 100644 --- a/src/NzbDrone.Api/NzbDrone.Api.csproj +++ b/src/NzbDrone.Api/NzbDrone.Api.csproj @@ -116,6 +116,9 @@ + + + diff --git a/src/NzbDrone.Api/Series/MovieResource.cs b/src/NzbDrone.Api/Series/MovieResource.cs index e4f484f0d..70a585086 100644 --- a/src/NzbDrone.Api/Series/MovieResource.cs +++ b/src/NzbDrone.Api/Series/MovieResource.cs @@ -18,7 +18,7 @@ namespace NzbDrone.Api.Movie //Todo: Sorters should be done completely on the client //Todo: Is there an easy way to keep IgnoreArticlesWhenSorting in sync between, Series, History, Missing? //Todo: We should get the entire Profile instead of ID and Name separately - + //View Only public string Title { get; set; } public List AlternateTitles { get; set; } @@ -33,6 +33,7 @@ namespace NzbDrone.Api.Movie public bool Downloaded { get; set; } public string RemotePoster { get; set; } public int Year { get; set; } + public bool HasFile { get; set; } //View & Edit public string Path { get; set; } @@ -84,12 +85,17 @@ namespace NzbDrone.Api.Movie long size = 0; bool downloaded = false; + + if(model.MovieFile != null) + { + model.MovieFile.LazyLoad(); + } + if (model.MovieFile != null && model.MovieFile.IsLoaded && model.MovieFile.Value != null) { size = model.MovieFile.Value.Size; downloaded = true; } - //long Size = model.MovieFile != null ? model.MovieFile.Value.Size : 0; return new MovieResource { @@ -100,7 +106,7 @@ namespace NzbDrone.Api.Movie SortTitle = model.SortTitle, InCinemas = model.InCinemas, PhysicalRelease = model.PhysicalRelease, - + HasFile = model.HasFile, Downloaded = downloaded, //TotalEpisodeCount //EpisodeCount diff --git a/src/NzbDrone.Core/Extras/ExtraService.cs b/src/NzbDrone.Core/Extras/ExtraService.cs index 5906de176..012e8f921 100644 --- a/src/NzbDrone.Core/Extras/ExtraService.cs +++ b/src/NzbDrone.Core/Extras/ExtraService.cs @@ -111,6 +111,18 @@ namespace NzbDrone.Core.Extras } } + //TODO: Implementing this will fix a lot of our warning exceptions + //public void Handle(MediaCoversUpdatedEvent message) + //{ + // var movie = message.Movie; + // var movieFiles = GetMovieFiles(movie.Id); + + // foreach (var extraFileManager in _extraFileManagers) + // { + // extraFileManager.CreateAfterMovieScan(movie, movieFiles); + // } + //} + public void Handle(EpisodeFolderCreatedEvent message) { var series = message.Series; diff --git a/src/NzbDrone.Core/MediaFiles/Commands/RenameFilesCommand.cs b/src/NzbDrone.Core/MediaFiles/Commands/RenameFilesCommand.cs index e0dc34e10..5cbbe7dfb 100644 --- a/src/NzbDrone.Core/MediaFiles/Commands/RenameFilesCommand.cs +++ b/src/NzbDrone.Core/MediaFiles/Commands/RenameFilesCommand.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using NzbDrone.Core.Messaging.Commands; namespace NzbDrone.Core.MediaFiles.Commands diff --git a/src/NzbDrone.Core/MediaFiles/Commands/RenameMovieCommand.cs b/src/NzbDrone.Core/MediaFiles/Commands/RenameMovieCommand.cs new file mode 100644 index 000000000..012a835f8 --- /dev/null +++ b/src/NzbDrone.Core/MediaFiles/Commands/RenameMovieCommand.cs @@ -0,0 +1,19 @@ +using NzbDrone.Core.Messaging.Commands; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace NzbDrone.Core.MediaFiles.Commands +{ + public class RenameMovieCommand : Command + { + public List MovieIds { get; set; } + + public override bool SendUpdatesToClient => true; + + public RenameMovieCommand() + { + } + } +} diff --git a/src/NzbDrone.Core/MediaFiles/Commands/RenameMovieFilesCommand.cs b/src/NzbDrone.Core/MediaFiles/Commands/RenameMovieFilesCommand.cs new file mode 100644 index 000000000..d2781e3ab --- /dev/null +++ b/src/NzbDrone.Core/MediaFiles/Commands/RenameMovieFilesCommand.cs @@ -0,0 +1,26 @@ +using NzbDrone.Core.Messaging.Commands; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace NzbDrone.Core.MediaFiles.Commands +{ + public class RenameMovieFilesCommand : Command + { + public int MovieId { get; set; } + public List Files { get; set; } + + public override bool SendUpdatesToClient => true; + + public RenameMovieFilesCommand() + { + } + + public RenameMovieFilesCommand(int movieId, List files) + { + MovieId = movieId; + Files = files; + } + } +} diff --git a/src/NzbDrone.Core/MediaFiles/DiskScanService.cs b/src/NzbDrone.Core/MediaFiles/DiskScanService.cs index 916c5681b..d4d2ec71c 100644 --- a/src/NzbDrone.Core/MediaFiles/DiskScanService.cs +++ b/src/NzbDrone.Core/MediaFiles/DiskScanService.cs @@ -38,6 +38,7 @@ namespace NzbDrone.Core.MediaFiles private readonly IDiskProvider _diskProvider; private readonly IMakeImportDecision _importDecisionMaker; private readonly IImportApprovedEpisodes _importApprovedEpisodes; + private readonly IImportApprovedMovie _importApprovedMovies; private readonly IConfigService _configService; private readonly ISeriesService _seriesService; private readonly IMediaFileTableCleanupService _mediaFileTableCleanupService; @@ -48,6 +49,7 @@ namespace NzbDrone.Core.MediaFiles public DiskScanService(IDiskProvider diskProvider, IMakeImportDecision importDecisionMaker, IImportApprovedEpisodes importApprovedEpisodes, + IImportApprovedMovie importApprovedMovies, IConfigService configService, ISeriesService seriesService, IMediaFileTableCleanupService mediaFileTableCleanupService, @@ -58,6 +60,7 @@ namespace NzbDrone.Core.MediaFiles _diskProvider = diskProvider; _importDecisionMaker = importDecisionMaker; _importApprovedEpisodes = importApprovedEpisodes; + _importApprovedMovies = importApprovedMovies; _configService = configService; _seriesService = seriesService; _mediaFileTableCleanupService = mediaFileTableCleanupService; @@ -179,7 +182,8 @@ namespace NzbDrone.Core.MediaFiles decisionsStopwatch.Stop(); _logger.Trace("Import decisions complete for: {0} [{1}]", movie, decisionsStopwatch.Elapsed); - _importApprovedEpisodes.Import(decisions, false); + //_importApprovedEpisodes.Import(decisions, false); + _importApprovedMovies.Import(decisions, false); _logger.Info("Completed scanning disk for {0}", movie.Title); _eventAggregator.PublishEvent(new MovieScannedEvent(movie)); diff --git a/src/NzbDrone.Core/MediaFiles/DownloadedMovieCommandService.cs b/src/NzbDrone.Core/MediaFiles/DownloadedMovieCommandService.cs index fcda97d24..211f6f13c 100644 --- a/src/NzbDrone.Core/MediaFiles/DownloadedMovieCommandService.cs +++ b/src/NzbDrone.Core/MediaFiles/DownloadedMovieCommandService.cs @@ -159,7 +159,7 @@ namespace NzbDrone.Core.MediaFiles } var cleanedUpName = GetCleanedUpFolderName(directoryInfo.Name); - var folderInfo = Parser.Parser.ParseTitle(directoryInfo.Name); + var folderInfo = Parser.Parser.ParseMovieTitle(directoryInfo.Name); if (folderInfo != null) { diff --git a/src/NzbDrone.Core/MediaFiles/EpisodeImport/ImportApprovedMovie.cs b/src/NzbDrone.Core/MediaFiles/EpisodeImport/ImportApprovedMovie.cs index 6d766a0e3..128ca64f9 100644 --- a/src/NzbDrone.Core/MediaFiles/EpisodeImport/ImportApprovedMovie.cs +++ b/src/NzbDrone.Core/MediaFiles/EpisodeImport/ImportApprovedMovie.cs @@ -47,6 +47,8 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport public List Import(List decisions, bool newDownload, DownloadClientItem downloadClientItem = null, ImportMode importMode = ImportMode.Auto) { + _logger.Debug("Decisions: {0}", decisions.Count); + var qualifiedImports = decisions.Where(c => c.Approved) .GroupBy(c => c.LocalMovie.Movie.Id, (i, s) => s .OrderByDescending(c => c.LocalMovie.Quality, new QualityModelComparer(s.First().LocalMovie.Movie.Profile)) @@ -80,7 +82,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport episodeFile.Quality = localMovie.Quality; episodeFile.MediaInfo = localMovie.MediaInfo; episodeFile.Movie = localMovie.Movie; - episodeFile.ReleaseGroup = localMovie.ParsedEpisodeInfo.ReleaseGroup; + episodeFile.ReleaseGroup = localMovie.ParsedMovieInfo.ReleaseGroup; bool copyOnly; switch (importMode) diff --git a/src/NzbDrone.Core/MediaFiles/EpisodeImport/ImportDecision.cs b/src/NzbDrone.Core/MediaFiles/EpisodeImport/ImportDecision.cs index 8bb5e78ea..406866b65 100644 --- a/src/NzbDrone.Core/MediaFiles/EpisodeImport/ImportDecision.cs +++ b/src/NzbDrone.Core/MediaFiles/EpisodeImport/ImportDecision.cs @@ -24,15 +24,15 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport { LocalMovie = localMovie; Rejections = rejections.ToList(); - LocalEpisode = new LocalEpisode - { - Quality = localMovie.Quality, - ExistingFile = localMovie.ExistingFile, - MediaInfo = localMovie.MediaInfo, - ParsedEpisodeInfo = localMovie.ParsedEpisodeInfo, - Path = localMovie.Path, - Size = localMovie.Size - }; + //LocalMovie = new LocalMovie + //{ + // Quality = localMovie.Quality, + // ExistingFile = localMovie.ExistingFile, + // MediaInfo = localMovie.MediaInfo, + // ParsedMovieInfo = localMovie.ParsedMovieInfo, + // Path = localMovie.Path, + // Size = localMovie.Size + //}; } } } diff --git a/src/NzbDrone.Core/MediaFiles/EpisodeImport/ImportDecisionMaker.cs b/src/NzbDrone.Core/MediaFiles/EpisodeImport/ImportDecisionMaker.cs index 52ffdfa07..9d4abe042 100644 --- a/src/NzbDrone.Core/MediaFiles/EpisodeImport/ImportDecisionMaker.cs +++ b/src/NzbDrone.Core/MediaFiles/EpisodeImport/ImportDecisionMaker.cs @@ -19,7 +19,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport { List GetImportDecisions(List videoFiles, Series series); List GetImportDecisions(List videoFiles, Movie movie); - List GetImportDecisions(List videoFiles, Movie movie, ParsedEpisodeInfo folderInfo, bool sceneSource); //TODO: Needs changing to ParsedMovieInfo!! + List GetImportDecisions(List videoFiles, Movie movie, ParsedMovieInfo folderInfo, bool sceneSource); //TODO: Needs changing to ParsedMovieInfo!! List GetImportDecisions(List videoFiles, Series series, ParsedEpisodeInfo folderInfo, bool sceneSource); } @@ -77,7 +77,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport return decisions; } - public List GetImportDecisions(List videoFiles, Movie movie, ParsedEpisodeInfo folderInfo, bool sceneSource) + public List GetImportDecisions(List videoFiles, Movie movie, ParsedMovieInfo folderInfo, bool sceneSource) { var newFiles = _mediaFileService.FilterExistingFiles(videoFiles.ToList(), movie); @@ -94,7 +94,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport return decisions; } - private ImportDecision GetDecision(string file, Movie movie, ParsedEpisodeInfo folderInfo, bool sceneSource, bool shouldUseFolderName) + private ImportDecision GetDecision(string file, Movie movie, ParsedMovieInfo folderInfo, bool sceneSource, bool shouldUseFolderName) { ImportDecision decision = null; @@ -123,18 +123,18 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport else { - var localEpisode = new LocalEpisode(); - localEpisode.Path = file; + localMovie = new LocalMovie(); + localMovie.Path = file; - decision = new ImportDecision(localEpisode, new Rejection("Unable to parse file")); + decision = new ImportDecision(localMovie, new Rejection("Unable to parse file")); } } catch (Exception e) { _logger.Error(e, "Couldn't import file. " + file); - var localEpisode = new LocalEpisode { Path = file }; - decision = new ImportDecision(localEpisode, new Rejection("Unexpected error processing file")); + var localMovie = new LocalMovie { Path = file }; + decision = new ImportDecision(localMovie, new Rejection("Unexpected error processing file")); } //LocalMovie nullMovie = null; @@ -291,17 +291,17 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport }) == 1; } - private bool ShouldUseFolderName(List videoFiles, Movie movie, ParsedEpisodeInfo folderInfo) + private bool ShouldUseFolderName(List videoFiles, Movie movie, ParsedMovieInfo folderInfo) { if (folderInfo == null) { return false; } - if (folderInfo.FullSeason) - { - return false; - } + //if (folderInfo.FullSeason) + //{ + // return false; + //} return videoFiles.Count(file => { @@ -325,7 +325,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport }) == 1; } - private QualityModel GetQuality(ParsedEpisodeInfo folderInfo, QualityModel fileQuality, Movie movie) + private QualityModel GetQuality(ParsedMovieInfo folderInfo, QualityModel fileQuality, Movie movie) { if (UseFolderQuality(folderInfo, fileQuality, movie)) { @@ -347,7 +347,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport return fileQuality; } - private bool UseFolderQuality(ParsedEpisodeInfo folderInfo, QualityModel fileQuality, Movie movie) + private bool UseFolderQuality(ParsedMovieInfo folderInfo, QualityModel fileQuality, Movie movie) { if (folderInfo == null) { diff --git a/src/NzbDrone.Core/MediaFiles/MediaFileRepository.cs b/src/NzbDrone.Core/MediaFiles/MediaFileRepository.cs index 206942356..3cc90af0f 100644 --- a/src/NzbDrone.Core/MediaFiles/MediaFileRepository.cs +++ b/src/NzbDrone.Core/MediaFiles/MediaFileRepository.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using NzbDrone.Core.Datastore; using NzbDrone.Core.Messaging.Events; diff --git a/src/NzbDrone.Core/MediaFiles/MediaFileService.cs b/src/NzbDrone.Core/MediaFiles/MediaFileService.cs index e3e82daa5..0587bb793 100644 --- a/src/NzbDrone.Core/MediaFiles/MediaFileService.cs +++ b/src/NzbDrone.Core/MediaFiles/MediaFileService.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.IO; using System.Linq; using NLog; @@ -13,9 +13,9 @@ namespace NzbDrone.Core.MediaFiles { public interface IMediaFileService { - MovieFile Add(MovieFile episodeFile); - void Update(MovieFile episodeFile); - void Delete(MovieFile episodeFile, DeleteMediaFileReason reason); + MovieFile Add(MovieFile movieFile); + void Update(MovieFile movieFile); + void Delete(MovieFile movieFile, DeleteMediaFileReason reason); EpisodeFile Add(EpisodeFile episodeFile); void Update(EpisodeFile episodeFile); void Delete(EpisodeFile episodeFile, DeleteMediaFileReason reason); @@ -27,7 +27,9 @@ namespace NzbDrone.Core.MediaFiles List FilterExistingFiles(List files, Movie movie); EpisodeFile Get(int id); List Get(IEnumerable ids); + List GetMovies(IEnumerable ids); + //List Get(IEnumerable ids); } public class MediaFileService : IMediaFileService, IHandleAsync @@ -125,6 +127,11 @@ namespace NzbDrone.Core.MediaFiles return _mediaFileRepository.Get(ids).ToList(); } + public List GetMovies(IEnumerable ids) + { + return _movieFileRepository.Get(ids).ToList(); + } + public void HandleAsync(SeriesDeletedEvent message) { var files = GetFilesBySeries(message.Series.Id); diff --git a/src/NzbDrone.Core/MediaFiles/RenameMovieFilePreview.cs b/src/NzbDrone.Core/MediaFiles/RenameMovieFilePreview.cs new file mode 100644 index 000000000..12f52e42a --- /dev/null +++ b/src/NzbDrone.Core/MediaFiles/RenameMovieFilePreview.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace NzbDrone.Core.MediaFiles +{ + public class RenameMovieFilePreview + { + public int MovieId { get; set; } + public int MovieFileId { get; set; } + public string ExistingPath { get; set; } + public string NewPath { get; set; } + } +} diff --git a/src/NzbDrone.Core/MediaFiles/RenameMovieFileService.cs b/src/NzbDrone.Core/MediaFiles/RenameMovieFileService.cs new file mode 100644 index 000000000..9a4019f56 --- /dev/null +++ b/src/NzbDrone.Core/MediaFiles/RenameMovieFileService.cs @@ -0,0 +1,138 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.IO; +using NLog; +using NzbDrone.Core.Messaging.Commands; +using NzbDrone.Core.Messaging.Events; +using NzbDrone.Core.MediaFiles.Commands; +using NzbDrone.Core.MediaFiles.Events; +using NzbDrone.Common.Disk; +using NzbDrone.Common.Extensions; +using NzbDrone.Common.Instrumentation.Extensions; +using NzbDrone.Core.Organizer; +using NzbDrone.Core.Tv; + +namespace NzbDrone.Core.MediaFiles +{ + public interface IRenameMovieFileService + { + List GetRenamePreviews(int movieId); + } + + public class RenameMovieFileService : IRenameMovieFileService, + IExecute, + IExecute + { + private readonly IMovieService _movieService; + private readonly IMediaFileService _mediaFileService; + private readonly IMoveMovieFiles _movieFileMover; + private readonly IEventAggregator _eventAggregator; + private readonly IBuildFileNames _filenameBuilder; + private readonly Logger _logger; + + public RenameMovieFileService(IMovieService movieService, + IMediaFileService mediaFileService, + IMoveMovieFiles movieFileMover, + IEventAggregator eventAggregator, + IBuildFileNames filenameBuilder, + Logger logger) + { + _movieService = movieService; + _mediaFileService = mediaFileService; + _movieFileMover = movieFileMover; + _eventAggregator = eventAggregator; + _filenameBuilder = filenameBuilder; + _logger = logger; + } + + public List GetRenamePreviews(int movieId) + { + var movie = _movieService.GetMovie(movieId); + var file = _mediaFileService.GetFilesByMovie(movieId); + + return GetPreviews(movie, file).OrderByDescending(m => m.MovieId).ToList(); //TODO: Would really like to not have these be lists + + } + + private IEnumerable GetPreviews(Movie movie, List files) + { + foreach(var file in files) + { + var movieFilePath = Path.Combine(movie.Path, file.RelativePath); + + var newName = _filenameBuilder.BuildFileName(movie, file); + var newPath = _filenameBuilder.BuildFilePath(movie, newName, Path.GetExtension(movieFilePath)); + + if(!movieFilePath.PathEquals(newPath, StringComparison.Ordinal)) + { + yield return new RenameMovieFilePreview + { + MovieId = movie.Id, + MovieFileId = file.Id, + ExistingPath = file.RelativePath, + NewPath = movie.Path.GetRelativePath(newPath) + }; + } + + } + + } + + private void RenameFiles(List movieFiles, Movie movie) + { + var renamed = new List(); + + foreach(var movieFile in movieFiles) + { + var movieFilePath = Path.Combine(movie.Path, movieFile.RelativePath); + + try + { + _logger.Debug("Renaming movie file: {0}", movieFile); + _movieFileMover.MoveMovieFile(movieFile, movie); + + _mediaFileService.Update(movieFile); + renamed.Add(movieFile); + + _logger.Debug("Renamed movie file: {0}", movieFile); + + } + catch(SameFilenameException ex) + { + _logger.Debug("File not renamed, source and destination are the same: {0}", ex.Filename); + } + catch(Exception ex) + { + _logger.Error(ex, "Failed to rename file: " + movieFilePath); + } + } + } + + public void Execute(RenameMovieFilesCommand message) + { + var movie = _movieService.GetMovie(message.MovieId); + var movieFiles = _mediaFileService.GetMovies(message.Files); + + _logger.ProgressInfo("Renaming {0} files for {1}", movieFiles.Count, movie.Title); + RenameFiles(movieFiles, movie); + _logger.ProgressInfo("Selected movie files renamed for {0}", movie.Title); + } + + public void Execute(RenameMovieCommand message) + { + _logger.Debug("Renaming all files for selected movie"); + var moviesToRename = _movieService.GetMovies(message.MovieIds); + + foreach(var movie in moviesToRename) + { + var movieFiles = _mediaFileService.GetFilesByMovie(movie.Id); + _logger.ProgressInfo("Renaming all files in movie: {0}", movie.Title); + RenameFiles(movieFiles, movie); + _logger.ProgressInfo("All movie files renamed for {0}", movie.Title); + } + + } + } +} diff --git a/src/NzbDrone.Core/NzbDrone.Core.csproj b/src/NzbDrone.Core/NzbDrone.Core.csproj index aa3c8b026..97f67da50 100644 --- a/src/NzbDrone.Core/NzbDrone.Core.csproj +++ b/src/NzbDrone.Core/NzbDrone.Core.csproj @@ -707,6 +707,8 @@ + + @@ -780,6 +782,8 @@ + + diff --git a/src/NzbDrone.Core/Parser/Model/LocalMovie.cs b/src/NzbDrone.Core/Parser/Model/LocalMovie.cs index 3f5c2344c..5ae9c481d 100644 --- a/src/NzbDrone.Core/Parser/Model/LocalMovie.cs +++ b/src/NzbDrone.Core/Parser/Model/LocalMovie.cs @@ -1,4 +1,4 @@ -using System.Linq; +using System.Linq; using System.Collections.Generic; using NzbDrone.Core.Qualities; using NzbDrone.Core.Tv; @@ -14,7 +14,7 @@ namespace NzbDrone.Core.Parser.Model public string Path { get; set; } public long Size { get; set; } - public ParsedEpisodeInfo ParsedEpisodeInfo { get; set; } + public ParsedMovieInfo ParsedMovieInfo { get; set; } public Movie Movie { get; set; } public QualityModel Quality { get; set; } public MediaInfoModel MediaInfo { get; set; } diff --git a/src/NzbDrone.Core/Parser/Model/ParsedMovieInfo.cs b/src/NzbDrone.Core/Parser/Model/ParsedMovieInfo.cs index 4b9bc7526..87938423f 100644 --- a/src/NzbDrone.Core/Parser/Model/ParsedMovieInfo.cs +++ b/src/NzbDrone.Core/Parser/Model/ParsedMovieInfo.cs @@ -1,4 +1,4 @@ -using System.Linq; +using System.Linq; using NzbDrone.Common.Extensions; using NzbDrone.Core.Qualities; @@ -9,10 +9,10 @@ namespace NzbDrone.Core.Parser.Model public string MovieTitle { get; set; } public SeriesTitleInfo MovieTitleInfo { get; set; } public QualityModel Quality { get; set; } - public int SeasonNumber { get; set; } + //public int SeasonNumber { get; set; } public Language Language { get; set; } - public bool FullSeason { get; set; } - public bool Special { get; set; } + //public bool FullSeason { get; set; } + //public bool Special { get; set; } public string ReleaseGroup { get; set; } public string ReleaseHash { get; set; } public string Edition { get; set;} diff --git a/src/NzbDrone.Core/Parser/Parser.cs b/src/NzbDrone.Core/Parser/Parser.cs index afe9f8aba..2120c3add 100644 --- a/src/NzbDrone.Core/Parser/Parser.cs +++ b/src/NzbDrone.Core/Parser/Parser.cs @@ -323,6 +323,28 @@ namespace NzbDrone.Core.Parser return result; } + public static ParsedMovieInfo ParseMoviePath(string path) + { + var fileInfo = new FileInfo(path); + + var result = ParseMovieTitle(fileInfo.Name); + + if (result == null) + { + Logger.Debug("Attempting to parse episode info using directory and file names. {0}", fileInfo.Directory.Name); + result = ParseMovieTitle(fileInfo.Directory.Name + " " + fileInfo.Name); + } + + if (result == null) + { + Logger.Debug("Attempting to parse episode info using directory name. {0}", fileInfo.Directory.Name); + result = ParseMovieTitle(fileInfo.Directory.Name + fileInfo.Extension); + } + + return result; + + } + public static ParsedMovieInfo ParseMovieTitle(string title) { diff --git a/src/NzbDrone.Core/Parser/ParsingService.cs b/src/NzbDrone.Core/Parser/ParsingService.cs index bb8c794d5..27f20889f 100644 --- a/src/NzbDrone.Core/Parser/ParsingService.cs +++ b/src/NzbDrone.Core/Parser/ParsingService.cs @@ -16,7 +16,7 @@ namespace NzbDrone.Core.Parser LocalEpisode GetLocalEpisode(string filename, Series series); LocalEpisode GetLocalEpisode(string filename, Series series, ParsedEpisodeInfo folderInfo, bool sceneSource); LocalMovie GetLocalMovie(string filename, Movie movie); - LocalMovie GetLocalMovie(string filename, Movie movie, ParsedEpisodeInfo folderInfo, bool sceneSource); + LocalMovie GetLocalMovie(string filename, Movie movie, ParsedMovieInfo folderInfo, bool sceneSource); Series GetSeries(string title); Movie GetMovie(string title); RemoteEpisode Map(ParsedEpisodeInfo parsedEpisodeInfo, int tvdbId, int tvRageId, SearchCriteriaBase searchCriteria = null); @@ -120,26 +120,26 @@ namespace NzbDrone.Core.Parser return GetLocalMovie(filename, movie, null, false); } - public LocalMovie GetLocalMovie(string filename, Movie movie, ParsedEpisodeInfo folderInfo, bool sceneSource) + public LocalMovie GetLocalMovie(string filename, Movie movie, ParsedMovieInfo folderInfo, bool sceneSource) { - ParsedEpisodeInfo parsedEpisodeInfo; + ParsedMovieInfo parsedMovieInfo; if (folderInfo != null) { - parsedEpisodeInfo = folderInfo.JsonClone(); - parsedEpisodeInfo.Quality = QualityParser.ParseQuality(Path.GetFileName(filename)); + parsedMovieInfo = folderInfo.JsonClone(); + parsedMovieInfo.Quality = QualityParser.ParseQuality(Path.GetFileName(filename)); } else { - parsedEpisodeInfo = Parser.ParsePath(filename); + parsedMovieInfo = Parser.ParseMoviePath(filename); } - if (parsedEpisodeInfo == null) + if (parsedMovieInfo == null) { if (MediaFileExtensions.Extensions.Contains(Path.GetExtension(filename))) { - _logger.Warn("Unable to parse episode info from path {0}", filename); + _logger.Warn("Unable to parse movie info from path {0}", filename); } return null; @@ -148,9 +148,9 @@ namespace NzbDrone.Core.Parser return new LocalMovie { Movie = movie, - Quality = parsedEpisodeInfo.Quality, + Quality = parsedMovieInfo.Quality, Path = filename, - ParsedEpisodeInfo = parsedEpisodeInfo, + ParsedMovieInfo = parsedMovieInfo, ExistingFile = movie.Path.IsParentPath(filename) }; } diff --git a/src/NzbDrone.Core/Tv/Movie.cs b/src/NzbDrone.Core/Tv/Movie.cs index 24329b987..eadff4151 100644 --- a/src/NzbDrone.Core/Tv/Movie.cs +++ b/src/NzbDrone.Core/Tv/Movie.cs @@ -48,6 +48,9 @@ namespace NzbDrone.Core.Tv public LazyLoaded MovieFile { get; set; } public int MovieFileId { get; set; } public List AlternativeTitles { get; set; } + + public bool HasFile => MovieFileId > 0; + public override string ToString() { return string.Format("[{0}][{1}]", ImdbId, Title.NullSafe()); diff --git a/src/UI/AddMovies/SearchResultViewTemplate.hbs b/src/UI/AddMovies/SearchResultViewTemplate.hbs index 87e1167da..35d213adb 100644 --- a/src/UI/AddMovies/SearchResultViewTemplate.hbs +++ b/src/UI/AddMovies/SearchResultViewTemplate.hbs @@ -52,7 +52,7 @@ {{> ProfileSelectionPartial profiles}} -
+ {{!--
@@ -65,7 +65,7 @@
-
+
--}} {{/unless}} {{#unless existing}} diff --git a/src/UI/Cells/MovieTitleCell.js b/src/UI/Cells/MovieTitleCell.js index 4ae8fe20c..652d1ebf3 100644 --- a/src/UI/Cells/MovieTitleCell.js +++ b/src/UI/Cells/MovieTitleCell.js @@ -4,8 +4,8 @@ module.exports = TemplatedCell.extend({ className : 'series-title-cell', template : 'Cells/SeriesTitleTemplate', - render : function() { - this.$el.html(this.model.get("movie").get("title")); //Hack, but somehow handlebar helper does not work. - return this; - } + // render : function() { + // this.$el.html(this.model.get("movie").get("title")); //Hack, but somehow handlebar helper does not work. + // return this; + // } }); diff --git a/src/UI/Controller.js b/src/UI/Controller.js index eb5168daa..89775a3ba 100644 --- a/src/UI/Controller.js +++ b/src/UI/Controller.js @@ -11,6 +11,7 @@ var ReleaseLayout = require('./Release/ReleaseLayout'); var SystemLayout = require('./System/SystemLayout'); var SeasonPassLayout = require('./SeasonPass/SeasonPassLayout'); var SeriesEditorLayout = require('./Series/Editor/SeriesEditorLayout'); +var MovieEditorLayout = require('./Movies/Editor/MovieEditorLayout'); module.exports = NzbDroneController.extend({ addSeries : function(action) { @@ -61,5 +62,10 @@ module.exports = NzbDroneController.extend({ seriesEditor : function() { this.setTitle('Series Editor'); this.showMainRegion(new SeriesEditorLayout()); + }, + + movieEditor : function() { + this.setTitle('Movie Editor'); + this.showMainRegion(new MovieEditorLayout()); } }); diff --git a/src/UI/Movies/Details/MoviesDetailsLayout.js b/src/UI/Movies/Details/MoviesDetailsLayout.js index a38793aa4..da92d260a 100644 --- a/src/UI/Movies/Details/MoviesDetailsLayout.js +++ b/src/UI/Movies/Details/MoviesDetailsLayout.js @@ -97,7 +97,7 @@ module.exports = Marionette.Layout.extend({ CommandController.bindToCommand({ element : this.ui.rename, command : { - name : 'renameFiles', + name : 'renameMovieFiles', movieId : this.model.id, seasonNumber : -1 } @@ -237,7 +237,7 @@ module.exports = Marionette.Layout.extend({ }, _commandComplete : function(options) { - if (options.command.get('name') === 'renamefiles') { + if (options.command.get('name') === 'renameMoviefiles') { if (options.command.get('moviesId') === this.model.get('id')) { this._refresh(); } diff --git a/src/UI/Movies/Details/MoviesDetailsTemplate.hbs b/src/UI/Movies/Details/MoviesDetailsTemplate.hbs index 9c3f6675a..8516e5665 100644 --- a/src/UI/Movies/Details/MoviesDetailsTemplate.hbs +++ b/src/UI/Movies/Details/MoviesDetailsTemplate.hbs @@ -15,7 +15,7 @@
- +