diff --git a/frontend/src/Activity/History/Details/HistoryDetails.css b/frontend/src/Activity/History/Details/HistoryDetails.css index aaa33e627..383f08afd 100644 --- a/frontend/src/Activity/History/Details/HistoryDetails.css +++ b/frontend/src/Activity/History/Details/HistoryDetails.css @@ -1,5 +1,5 @@ .description { - composes: title from '~Components/DescriptionList/DescriptionListItemDescription.css'; + composes: description from '~Components/DescriptionList/DescriptionListItemDescription.css'; overflow-wrap: break-word; } diff --git a/frontend/src/Activity/Queue/QueueRow.js b/frontend/src/Activity/Queue/QueueRow.js index 5bb637c84..e26b20353 100644 --- a/frontend/src/Activity/Queue/QueueRow.js +++ b/frontend/src/Activity/Queue/QueueRow.js @@ -307,7 +307,7 @@ QueueRow.propTypes = { trackedDownloadStatus: PropTypes.string, statusMessages: PropTypes.arrayOf(PropTypes.object), errorMessage: PropTypes.string, - movie: PropTypes.object.isRequired, + movie: PropTypes.object, quality: PropTypes.object.isRequired, languages: PropTypes.arrayOf(PropTypes.object).isRequired, protocol: PropTypes.string.isRequired, diff --git a/frontend/src/AddMovie/AddNewMovie/AddNewMovie.css b/frontend/src/AddMovie/AddNewMovie/AddNewMovie.css index 7c558d6d0..ed5a43c65 100644 --- a/frontend/src/AddMovie/AddNewMovie/AddNewMovie.css +++ b/frontend/src/AddMovie/AddNewMovie/AddNewMovie.css @@ -35,14 +35,20 @@ .message { margin-top: 30px; text-align: center; + font-weight: 300; + font-size: $largeFontSize; } .helpText { margin-bottom: 10px; - font-weight: 300; font-size: 24px; } +.noMoviesText { + margin-top: 80px; + margin-bottom: 20px; +} + .noResults { margin-bottom: 10px; font-weight: 300; diff --git a/frontend/src/AddMovie/AddNewMovie/AddNewMovie.js b/frontend/src/AddMovie/AddNewMovie/AddNewMovie.js index 0960e4e2e..1aaf4fd9b 100644 --- a/frontend/src/AddMovie/AddNewMovie/AddNewMovie.js +++ b/frontend/src/AddMovie/AddNewMovie/AddNewMovie.js @@ -1,7 +1,7 @@ import PropTypes from 'prop-types'; import React, { Component } from 'react'; import getErrorMessage from 'Utilities/Object/getErrorMessage'; -import { icons } from 'Helpers/Props'; +import { icons, kinds } from 'Helpers/Props'; import Button from 'Components/Link/Button'; import Link from 'Components/Link/Link'; import Icon from 'Components/Icon'; @@ -79,7 +79,8 @@ class AddNewMovie extends Component { render() { const { error, - items + items, + hasExistingMovies } = this.props; const term = this.state.term; @@ -160,13 +161,34 @@ class AddNewMovie extends Component { } { - !term && + term ? + null :
-
It's easy to add a new movie, just start typing the name the movie you want to add.
+
+ It's easy to add a new movie, just start typing the name the movie you want to add. +
You can also search using TMDB ID of a movie. eg. tmdb:71663
} + { + !term && !hasExistingMovies ? +
+
+ You haven't added any movies yet, do you want to import some or all of your movies first? +
+
+ +
+
: + null + } +
@@ -181,6 +203,7 @@ AddNewMovie.propTypes = { isAdding: PropTypes.bool.isRequired, addError: PropTypes.object, items: PropTypes.arrayOf(PropTypes.object).isRequired, + hasExistingMovies: PropTypes.bool.isRequired, onMovieLookupChange: PropTypes.func.isRequired, onClearMovieLookup: PropTypes.func.isRequired }; diff --git a/frontend/src/AddMovie/AddNewMovie/AddNewMovieConnector.js b/frontend/src/AddMovie/AddNewMovie/AddNewMovieConnector.js index 6c238a10d..c44674ebc 100644 --- a/frontend/src/AddMovie/AddNewMovie/AddNewMovieConnector.js +++ b/frontend/src/AddMovie/AddNewMovie/AddNewMovieConnector.js @@ -11,13 +11,15 @@ import AddNewMovie from './AddNewMovie'; function createMapStateToProps() { return createSelector( (state) => state.addMovie, + (state) => state.movies.items.length, (state) => state.router.location, - (addMovie, location) => { + (addMovie, existingMoviesCount, location) => { const { params } = parseUrl(location.search); return { + ...addMovie, term: params.term, - ...addMovie + hasExistingMovies: existingMoviesCount > 0 }; } ); diff --git a/frontend/src/AddMovie/AddNewMovie/AddNewMovieModalContent.js b/frontend/src/AddMovie/AddNewMovie/AddNewMovieModalContent.js index e110274e7..a5eac49a8 100644 --- a/frontend/src/AddMovie/AddNewMovie/AddNewMovieModalContent.js +++ b/frontend/src/AddMovie/AddNewMovie/AddNewMovieModalContent.js @@ -55,8 +55,10 @@ class AddNewMovieModalContent extends Component { rootFolderPath, monitor, qualityProfileId, + folder, tags, isSmallScreen, + isWindows, onModalClose, onInputChange } = this.props; @@ -97,6 +99,15 @@ class AddNewMovieModalContent extends Component { @@ -180,8 +191,10 @@ AddNewMovieModalContent.propTypes = { rootFolderPath: PropTypes.object, monitor: PropTypes.object.isRequired, qualityProfileId: PropTypes.object, + folder: PropTypes.string.isRequired, tags: PropTypes.object.isRequired, isSmallScreen: PropTypes.bool.isRequired, + isWindows: PropTypes.bool.isRequired, onModalClose: PropTypes.func.isRequired, onInputChange: PropTypes.func.isRequired, onAddMoviePress: PropTypes.func.isRequired diff --git a/frontend/src/AddMovie/AddNewMovie/AddNewMovieModalContentConnector.js b/frontend/src/AddMovie/AddNewMovie/AddNewMovieModalContentConnector.js index c86134c14..78691c862 100644 --- a/frontend/src/AddMovie/AddNewMovie/AddNewMovieModalContentConnector.js +++ b/frontend/src/AddMovie/AddNewMovie/AddNewMovieModalContentConnector.js @@ -4,6 +4,7 @@ import { connect } from 'react-redux'; import { createSelector } from 'reselect'; import { setAddMovieDefault, addMovie } from 'Store/Actions/addMovieActions'; import createDimensionsSelector from 'Store/Selectors/createDimensionsSelector'; +import createSystemStatusSelector from 'Store/Selectors/createSystemStatusSelector'; import selectSettings from 'Store/Selectors/selectSettings'; import AddNewMovieModalContent from './AddNewMovieModalContent'; @@ -11,7 +12,8 @@ function createMapStateToProps() { return createSelector( (state) => state.addMovie, createDimensionsSelector(), - (addMovieState, dimensions) => { + createSystemStatusSelector(), + (addMovieState, dimensions, systemStatus) => { const { isAdding, addError, @@ -30,6 +32,7 @@ function createMapStateToProps() { isSmallScreen: dimensions.isSmallScreen, validationErrors, validationWarnings, + isWindows: systemStatus.isWindows, ...settings }; } diff --git a/frontend/src/AddMovie/AddNewMovie/AddNewMovieSearchResult.js b/frontend/src/AddMovie/AddNewMovie/AddNewMovieSearchResult.js index 1afdbb478..618894527 100644 --- a/frontend/src/AddMovie/AddNewMovie/AddNewMovieSearchResult.js +++ b/frontend/src/AddMovie/AddNewMovie/AddNewMovieSearchResult.js @@ -52,6 +52,7 @@ class AddNewMovieSearchResult extends Component { status, overview, ratings, + folder, images, isExistingMovie, isExclusionMovie, @@ -148,6 +149,7 @@ class AddNewMovieSearchResult extends Component { title={title} year={year} overview={overview} + folder={folder} images={images} onModalClose={this.onAddMovieModalClose} /> @@ -165,6 +167,7 @@ AddNewMovieSearchResult.propTypes = { status: PropTypes.string.isRequired, overview: PropTypes.string, ratings: PropTypes.object.isRequired, + folder: PropTypes.string.isRequired, images: PropTypes.arrayOf(PropTypes.object).isRequired, isExistingMovie: PropTypes.bool.isRequired, isExclusionMovie: PropTypes.bool.isRequired, diff --git a/frontend/src/Components/Form/EnhancedSelectInput.js b/frontend/src/Components/Form/EnhancedSelectInput.js index 80ee78e81..6c50c1c82 100644 --- a/frontend/src/Components/Form/EnhancedSelectInput.js +++ b/frontend/src/Components/Form/EnhancedSelectInput.js @@ -262,6 +262,7 @@ class EnhancedSelectInput extends Component { isDisabled, hasError, hasWarning, + valueOptions, selectedValueOptions, selectedValueComponent: SelectedValueComponent, optionComponent: OptionComponent @@ -363,6 +364,7 @@ class EnhancedSelectInput extends Component { key={v.key} id={v.key} isSelected={index === selectedIndex} + {...valueOptions} {...v} isMobile={false} onSelect={this.onSelect} @@ -404,6 +406,7 @@ class EnhancedSelectInput extends Component { key={v.key} id={v.key} isSelected={index === selectedIndex} + {...valueOptions} {...v} isMobile={true} onSelect={this.onSelect} @@ -431,6 +434,7 @@ EnhancedSelectInput.propTypes = { isDisabled: PropTypes.bool, hasError: PropTypes.bool, hasWarning: PropTypes.bool, + valueOptions: PropTypes.object.isRequired, selectedValueOptions: PropTypes.object.isRequired, selectedValueComponent: PropTypes.oneOfType([PropTypes.string, PropTypes.func]).isRequired, optionComponent: PropTypes.elementType, @@ -441,6 +445,7 @@ EnhancedSelectInput.defaultProps = { className: styles.enhancedSelect, disabledClassName: styles.isDisabled, isDisabled: false, + valueOptions: {}, selectedValueOptions: {}, selectedValueComponent: HintedSelectInputSelectedValue, optionComponent: HintedSelectInputOption diff --git a/frontend/src/Components/Form/RootFolderSelectInputConnector.js b/frontend/src/Components/Form/RootFolderSelectInputConnector.js index b3dfcbd20..b76501dc1 100644 --- a/frontend/src/Components/Form/RootFolderSelectInputConnector.js +++ b/frontend/src/Components/Form/RootFolderSelectInputConnector.js @@ -1,4 +1,3 @@ -import _ from 'lodash'; import PropTypes from 'prop-types'; import React, { Component } from 'react'; import { connect } from 'react-redux'; @@ -13,7 +12,7 @@ function createMapStateToProps() { (state) => state.rootFolders, (state, { includeNoChange }) => includeNoChange, (rootFolders, includeNoChange) => { - const values = _.map(rootFolders.items, (rootFolder) => { + const values = rootFolders.items.map((rootFolder) => { return { key: rootFolder.path, value: rootFolder.path, @@ -85,7 +84,7 @@ class RootFolderSelectInputConnector extends Component { onChange } = this.props; - if (!value || !_.some(values, (v) => v.key === value) || value === ADD_NEW_KEY) { + if (!value || !values.some((v) => v.key === value) || value === ADD_NEW_KEY) { const defaultValue = values[0]; if (defaultValue.key === ADD_NEW_KEY) { diff --git a/frontend/src/Components/Form/RootFolderSelectInputOption.css b/frontend/src/Components/Form/RootFolderSelectInputOption.css index d8b44fcad..ebc8ddc54 100644 --- a/frontend/src/Components/Form/RootFolderSelectInputOption.css +++ b/frontend/src/Components/Form/RootFolderSelectInputOption.css @@ -13,6 +13,15 @@ } } +.value { + display: flex; +} + +.movieFolder { + flex: 0 0 auto; + color: $disabledColor; +} + .freeSpace { margin-left: 15px; color: $darkGray; diff --git a/frontend/src/Components/Form/RootFolderSelectInputOption.js b/frontend/src/Components/Form/RootFolderSelectInputOption.js index a4db9cd82..40608d9de 100644 --- a/frontend/src/Components/Form/RootFolderSelectInputOption.js +++ b/frontend/src/Components/Form/RootFolderSelectInputOption.js @@ -7,14 +7,20 @@ import styles from './RootFolderSelectInputOption.css'; function RootFolderSelectInputOption(props) { const { + id, value, freeSpace, + movieFolder, isMobile, + isWindows, ...otherProps } = props; + const slashCharacter = isWindows ? '\\' : '/'; + return ( @@ -23,7 +29,18 @@ function RootFolderSelectInputOption(props) { isMobile && styles.isMobile )} > -
{value}
+
+ {value} + + { + movieFolder && id !== 'addNew' ? +
+ {slashCharacter} + {movieFolder} +
: + null + } +
{ freeSpace != null && @@ -37,9 +54,12 @@ function RootFolderSelectInputOption(props) { } RootFolderSelectInputOption.propTypes = { + id: PropTypes.string.isRequired, value: PropTypes.string.isRequired, freeSpace: PropTypes.number, - isMobile: PropTypes.bool.isRequired + movieFolder: PropTypes.string, + isMobile: PropTypes.bool.isRequired, + isWindows: PropTypes.bool }; export default RootFolderSelectInputOption; diff --git a/frontend/src/Components/Form/RootFolderSelectInputSelectedValue.css b/frontend/src/Components/Form/RootFolderSelectInputSelectedValue.css index 6b0cf9e4f..fe036445b 100644 --- a/frontend/src/Components/Form/RootFolderSelectInputSelectedValue.css +++ b/frontend/src/Components/Form/RootFolderSelectInputSelectedValue.css @@ -7,10 +7,20 @@ overflow: hidden; } +.pathContainer { + @add-mixin truncate; + display: flex; + flex: 1 0 0; +} + .path { @add-mixin truncate; + flex: 0 1 auto; +} - flex: 1 0 0; +.movieFolder { + flex: 0 1 auto; + color: $disabledColor; } .freeSpace { diff --git a/frontend/src/Components/Form/RootFolderSelectInputSelectedValue.js b/frontend/src/Components/Form/RootFolderSelectInputSelectedValue.js index ffd769254..803b8bee9 100644 --- a/frontend/src/Components/Form/RootFolderSelectInputSelectedValue.js +++ b/frontend/src/Components/Form/RootFolderSelectInputSelectedValue.js @@ -8,17 +8,32 @@ function RootFolderSelectInputSelectedValue(props) { const { value, freeSpace, + movieFolder, includeFreeSpace, + isWindows, ...otherProps } = props; + const slashCharacter = isWindows ? '\\' : '/'; + return ( -
- {value} +
+
+ {value} +
+ + { + movieFolder ? +
+ {slashCharacter} + {movieFolder} +
: + null + }
{ @@ -34,6 +49,8 @@ function RootFolderSelectInputSelectedValue(props) { RootFolderSelectInputSelectedValue.propTypes = { value: PropTypes.string, freeSpace: PropTypes.number, + movieFolder: PropTypes.string, + isWindows: PropTypes.bool, includeFreeSpace: PropTypes.bool.isRequired }; diff --git a/src/NzbDrone.Api/Movies/MovieResource.cs b/src/NzbDrone.Api/Movies/MovieResource.cs index c89b08e26..6d3f86f11 100644 --- a/src/NzbDrone.Api/Movies/MovieResource.cs +++ b/src/NzbDrone.Api/Movies/MovieResource.cs @@ -227,19 +227,9 @@ public static Core.Movies.Movie ToModel(this MovieResource resource) public static Core.Movies.Movie ToModel(this MovieResource resource, Core.Movies.Movie movie) { - movie.ImdbId = resource.ImdbId; - movie.TmdbId = resource.TmdbId; + var updatedmovie = resource.ToModel(); - movie.Path = resource.Path; - movie.ProfileId = resource.ProfileId; - movie.PathState = resource.PathState; - - movie.Monitored = resource.Monitored; - movie.MinimumAvailability = resource.MinimumAvailability; - - movie.RootFolderPath = resource.RootFolderPath; - movie.Tags = resource.Tags; - movie.AddOptions = resource.AddOptions; + movie.ApplyChanges(updatedmovie); return movie; } diff --git a/src/NzbDrone.Common/Extensions/PathExtensions.cs b/src/NzbDrone.Common/Extensions/PathExtensions.cs index 1a11fb7c4..bc7636cab 100644 --- a/src/NzbDrone.Common/Extensions/PathExtensions.cs +++ b/src/NzbDrone.Common/Extensions/PathExtensions.cs @@ -81,6 +81,15 @@ public static string GetParentPath(this string childPath) return Directory.GetParent(cleanPath)?.FullName; } + public static string GetCleanPath(this string path) + { + var cleanPath = OsInfo.IsWindows + ? PARENT_PATH_END_SLASH_REGEX.Replace(path, "") + : path.TrimEnd(Path.DirectorySeparatorChar); + + return cleanPath; + } + public static bool IsParentPath(this string parentPath, string childPath) { if (parentPath != "/" && !parentPath.EndsWith(":\\")) diff --git a/src/NzbDrone.Core/Movies/Movie.cs b/src/NzbDrone.Core/Movies/Movie.cs index 111c12208..311127b1b 100644 --- a/src/NzbDrone.Core/Movies/Movie.cs +++ b/src/NzbDrone.Core/Movies/Movie.cs @@ -142,6 +142,22 @@ public override string ToString() { return string.Format("[{1} ({2})][{0}, {3}]", ImdbId, Title.NullSafe(), Year.NullSafe(), TmdbId); } + + public void ApplyChanges(Movie otherMovie) + { + TmdbId = otherMovie.TmdbId; + + Path = otherMovie.Path; + ProfileId = otherMovie.ProfileId; + PathState = otherMovie.PathState; + + Monitored = otherMovie.Monitored; + MinimumAvailability = otherMovie.MinimumAvailability; + + RootFolderPath = otherMovie.RootFolderPath; + Tags = otherMovie.Tags; + AddOptions = otherMovie.AddOptions; + } } public enum MoviePathState diff --git a/src/NzbDrone.Core/Movies/MovieEditedService.cs b/src/NzbDrone.Core/Movies/MovieEditedService.cs deleted file mode 100644 index fcfc04361..000000000 --- a/src/NzbDrone.Core/Movies/MovieEditedService.cs +++ /dev/null @@ -1,25 +0,0 @@ -using NzbDrone.Core.Messaging.Commands; -using NzbDrone.Core.Messaging.Events; -using NzbDrone.Core.Movies.Commands; -using NzbDrone.Core.Movies.Events; - -namespace NzbDrone.Core.Movies -{ - public class MovieEditedService : IHandle - { - private readonly IManageCommandQueue _commandQueueManager; - - public MovieEditedService(IManageCommandQueue commandQueueManager) - { - _commandQueueManager = commandQueueManager; - } - - public void Handle(MovieEditedEvent message) - { - if (message.Movie.ImdbId != message.OldMovie.ImdbId) - { - _commandQueueManager.Push(new RefreshMovieCommand(message.Movie.Id)); //Probably not needed, as metadata should stay the same. - } - } - } -} diff --git a/src/Radarr.Api.V2/Movies/MovieFolderAsRootFolderValidator.cs b/src/Radarr.Api.V2/Movies/MovieFolderAsRootFolderValidator.cs new file mode 100644 index 000000000..6ce918f0b --- /dev/null +++ b/src/Radarr.Api.V2/Movies/MovieFolderAsRootFolderValidator.cs @@ -0,0 +1,39 @@ +using System; +using System.IO; +using FluentValidation.Validators; +using NzbDrone.Common.Extensions; +using NzbDrone.Core.Organizer; + +namespace Radarr.Api.V2.Movies +{ + public class MovieFolderAsRootFolderValidator : PropertyValidator + { + private readonly IBuildFileNames _fileNameBuilder; + + public MovieFolderAsRootFolderValidator(IBuildFileNames fileNameBuilder) + : base("Root folder path contains movie folder") + { + _fileNameBuilder = fileNameBuilder; + } + + protected override bool IsValid(PropertyValidatorContext context) + { + if (context.PropertyValue == null) return true; + + var movieResource = context.Instance as MovieResource; + + if (movieResource == null) return true; + + var rootFolderPath = context.PropertyValue.ToString(); + var rootFolder = new DirectoryInfo(rootFolderPath).Name; + var movie = movieResource.ToModel(); + var movieFolder = _fileNameBuilder.GetMovieFolder(movie); + + if (movieFolder == rootFolder) return false; + + var distance = movieFolder.LevenshteinDistance(rootFolder); + + return distance >= Math.Max(1, movieFolder.Length * 0.2); + } + } +} \ No newline at end of file diff --git a/src/Radarr.Api.V2/Movies/MovieLookupModule.cs b/src/Radarr.Api.V2/Movies/MovieLookupModule.cs index bf850b891..206cc8e51 100644 --- a/src/Radarr.Api.V2/Movies/MovieLookupModule.cs +++ b/src/Radarr.Api.V2/Movies/MovieLookupModule.cs @@ -8,6 +8,7 @@ using System; using Radarr.Http; using Radarr.Http.REST; +using NzbDrone.Core.Organizer; namespace Radarr.Api.V2.Movies { @@ -15,12 +16,14 @@ public class MovieLookupModule : RadarrRestModule { private readonly ISearchForNewMovie _searchProxy; private readonly IProvideMovieInfo _movieInfo; + private readonly IBuildFileNames _fileNameBuilder; - public MovieLookupModule(ISearchForNewMovie searchProxy, IProvideMovieInfo movieInfo) + public MovieLookupModule(ISearchForNewMovie searchProxy, IProvideMovieInfo movieInfo, IBuildFileNames fileNameBuilder) : base("/movie/lookup") { _movieInfo = movieInfo; _searchProxy = searchProxy; + _fileNameBuilder = fileNameBuilder; Get("/", x => Search()); Get("/tmdb", x => SearchByTmdbId()); Get("/imdb", x => SearchByImdbId()); @@ -51,17 +54,19 @@ private object Search() return MapToResource(imdbResults); } - private static IEnumerable MapToResource(IEnumerable movies) + private IEnumerable MapToResource(IEnumerable movies) { - foreach (var currentSeries in movies) + foreach (var currentMovie in movies) { - var resource = currentSeries.ToResource(); - var poster = currentSeries.Images.FirstOrDefault(c => c.CoverType == MediaCoverTypes.Poster); + var resource = currentMovie.ToResource(); + var poster = currentMovie.Images.FirstOrDefault(c => c.CoverType == MediaCoverTypes.Poster); if (poster != null) { resource.RemotePoster = poster.Url; } + resource.Folder = _fileNameBuilder.GetMovieFolder(currentMovie); + yield return resource; } } diff --git a/src/Radarr.Api.V2/Movies/MovieModule.cs b/src/Radarr.Api.V2/Movies/MovieModule.cs index 413979807..14a35caed 100644 --- a/src/Radarr.Api.V2/Movies/MovieModule.cs +++ b/src/Radarr.Api.V2/Movies/MovieModule.cs @@ -38,7 +38,8 @@ public MovieModule(IBroadcastSignalRMessage signalRBroadcaster, MoviePathValidator moviesPathValidator, MovieExistsValidator moviesExistsValidator, MovieAncestorValidator moviesAncestorValidator, - ProfileExistsValidator profileExistsValidator + ProfileExistsValidator profileExistsValidator, + MovieFolderAsRootFolderValidator movieFolderAsRootFolderValidator ) : base(signalRBroadcaster) { @@ -65,7 +66,10 @@ ProfileExistsValidator profileExistsValidator SharedValidator.RuleFor(s => s.QualityProfileId).SetValidator(profileExistsValidator); PostValidator.RuleFor(s => s.Path).IsValidPath().When(s => s.RootFolderPath.IsNullOrWhiteSpace()); - PostValidator.RuleFor(s => s.RootFolderPath).IsValidPath().When(s => s.Path.IsNullOrWhiteSpace()); + PostValidator.RuleFor(s => s.RootFolderPath) + .IsValidPath() + .SetValidator(movieFolderAsRootFolderValidator) + .When(s => s.Path.IsNullOrWhiteSpace()); PostValidator.RuleFor(s => s.Title).NotEmpty(); PostValidator.RuleFor(s => s.TmdbId).NotNull().NotEmpty().SetValidator(moviesExistsValidator); diff --git a/src/Radarr.Api.V2/Movies/MovieResource.cs b/src/Radarr.Api.V2/Movies/MovieResource.cs index c6055ae4d..0149bab11 100644 --- a/src/Radarr.Api.V2/Movies/MovieResource.cs +++ b/src/Radarr.Api.V2/Movies/MovieResource.cs @@ -58,6 +58,7 @@ public MovieResource() public int TmdbId { get; set; } public string TitleSlug { get; set; } public string RootFolderPath { get; set; } + public string Folder { get; set; } public string Certification { get; set; } public List Genres { get; set; } public HashSet Tags { get; set; } @@ -178,19 +179,9 @@ public static Movie ToModel(this MovieResource resource) public static Movie ToModel(this MovieResource resource, Movie movie) { - movie.ImdbId = resource.ImdbId; - movie.TmdbId = resource.TmdbId; + var updatedmovie = resource.ToModel(); - movie.Path = resource.Path; - movie.ProfileId = resource.QualityProfileId; - movie.PathState = resource.PathState; - - movie.Monitored = resource.Monitored; - movie.MinimumAvailability = resource.MinimumAvailability; - - movie.RootFolderPath = resource.RootFolderPath; - movie.Tags = resource.Tags; - movie.AddOptions = resource.AddOptions; + movie.ApplyChanges(updatedmovie); return movie; } diff --git a/src/Radarr.Api.V2/RootFolders/RootFolderResource.cs b/src/Radarr.Api.V2/RootFolders/RootFolderResource.cs index 719e297b3..c8dbab65b 100644 --- a/src/Radarr.Api.V2/RootFolders/RootFolderResource.cs +++ b/src/Radarr.Api.V2/RootFolders/RootFolderResource.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Linq; +using NzbDrone.Common.Extensions; using NzbDrone.Core.RootFolders; using Radarr.Http.REST; @@ -23,7 +24,7 @@ public static RootFolderResource ToResource(this RootFolder model) { Id = model.Id, - Path = model.Path, + Path = model.Path.GetCleanPath(), FreeSpace = model.FreeSpace, UnmappedFolders = model.UnmappedFolders }; @@ -37,7 +38,7 @@ public static RootFolder ToModel(this RootFolderResource resource) { Id = resource.Id, - Path = resource.Path, + Path = resource.Path //FreeSpace //UnmappedFolders };