diff --git a/frontend/src/Movie/Details/MovieAlternateTitles.js b/frontend/src/Movie/Details/MovieAlternateTitles.js
index 39cd0f93e..5b0fdaeaa 100644
--- a/frontend/src/Movie/Details/MovieAlternateTitles.js
+++ b/frontend/src/Movie/Details/MovieAlternateTitles.js
@@ -6,7 +6,7 @@ function MovieAlternateTitles({ alternateTitles }) {
return (
{
- alternateTitles.map((alternateTitle) => {
+ alternateTitles.filter((x, i, a) => a.indexOf(x) === i).map((alternateTitle) => {
return (
@@ -722,6 +722,7 @@ MovieDetails.propTypes = {
studio: PropTypes.string,
collection: PropTypes.object,
youTubeTrailerId: PropTypes.string,
+ isAvailable: PropTypes.bool.isRequired,
inCinemas: PropTypes.string,
overview: PropTypes.string.isRequired,
images: PropTypes.arrayOf(PropTypes.object).isRequired,
diff --git a/frontend/src/Movie/Details/MovieStatusLabel.js b/frontend/src/Movie/Details/MovieStatusLabel.js
index 9241e431c..9a3831676 100644
--- a/frontend/src/Movie/Details/MovieStatusLabel.js
+++ b/frontend/src/Movie/Details/MovieStatusLabel.js
@@ -1,10 +1,8 @@
import PropTypes from 'prop-types';
import React from 'react';
-import moment from 'moment';
import styles from './MovieStatusLabel.css';
-function getMovieStatus(hasFile, isMonitored, inCinemas) {
- const currentTime = moment();
+function getMovieStatus(hasFile, isMonitored, isAvailable) {
if (hasFile) {
return 'Downloaded';
@@ -14,7 +12,7 @@ function getMovieStatus(hasFile, isMonitored, inCinemas) {
return 'Unmonitored';
}
- if (inCinemas.isBefore(currentTime) && !hasFile) {
+ if (isAvailable && !hasFile) {
return 'Missing';
}
@@ -25,10 +23,10 @@ function MovieStatusLabel(props) {
const {
hasMovieFiles,
monitored,
- inCinemas
+ isAvailable
} = props;
- const status = getMovieStatus(hasMovieFiles, monitored, moment(inCinemas));
+ const status = getMovieStatus(hasMovieFiles, monitored, isAvailable);
return (
+
+ {titleCase(sourceType)}
+
+
);
}
@@ -39,7 +45,8 @@ class MovieTitlesRow extends Component {
MovieTitlesRow.propTypes = {
id: PropTypes.number.isRequired,
title: PropTypes.string.isRequired,
- language: PropTypes.object.isRequired
+ language: PropTypes.object.isRequired,
+ sourceType: PropTypes.object.isRequired
};
export default MovieTitlesRow;
diff --git a/frontend/src/Movie/Details/Titles/MovieTitlesTableContent.js b/frontend/src/Movie/Details/Titles/MovieTitlesTableContent.js
index 3492ad023..ac2bfe966 100644
--- a/frontend/src/Movie/Details/Titles/MovieTitlesTableContent.js
+++ b/frontend/src/Movie/Details/Titles/MovieTitlesTableContent.js
@@ -16,6 +16,11 @@ const columns = [
name: 'language',
label: 'Language',
isVisible: true
+ },
+ {
+ name: 'sourceType',
+ label: 'Type',
+ isVisible: true
}
];
diff --git a/frontend/src/MovieFile/Language/SelectLanguageModalContentConnector.js b/frontend/src/MovieFile/Language/SelectLanguageModalContentConnector.js
index 02792be02..2fb41a0f6 100644
--- a/frontend/src/MovieFile/Language/SelectLanguageModalContentConnector.js
+++ b/frontend/src/MovieFile/Language/SelectLanguageModalContentConnector.js
@@ -18,11 +18,14 @@ function createMapStateToProps() {
items
} = languages;
+ const filterItems = ['Any', 'Unknown'];
+ const filteredLanguages = items.filter((lang) => !filterItems.includes(lang.name));
+
return {
isFetching,
isPopulated,
error,
- items
+ items: filteredLanguages
};
}
);
diff --git a/frontend/src/Settings/UI/UISettings.js b/frontend/src/Settings/UI/UISettings.js
index bef36cfd4..58d13b5bf 100644
--- a/frontend/src/Settings/UI/UISettings.js
+++ b/frontend/src/Settings/UI/UISettings.js
@@ -55,6 +55,7 @@ class UISettings extends Component {
hasSettings,
onInputChange,
onSavePress,
+ languages,
...otherProps
} = this.props;
@@ -174,6 +175,22 @@ class UISettings extends Component {
/>
+
+
}
@@ -189,6 +206,7 @@ UISettings.propTypes = {
settings: PropTypes.object.isRequired,
hasSettings: PropTypes.bool.isRequired,
onSavePress: PropTypes.func.isRequired,
+ languages: PropTypes.arrayOf(PropTypes.object).isRequired,
onInputChange: PropTypes.func.isRequired
};
diff --git a/frontend/src/Settings/UI/UISettingsConnector.js b/frontend/src/Settings/UI/UISettingsConnector.js
index ac8c9f8f1..01310cd5e 100644
--- a/frontend/src/Settings/UI/UISettingsConnector.js
+++ b/frontend/src/Settings/UI/UISettingsConnector.js
@@ -9,13 +9,38 @@ import UISettings from './UISettings';
const SECTION = 'ui';
+function createLanguagesSelector() {
+ return createSelector(
+ (state) => state.settings.languages,
+ (languages) => {
+ const items = languages.items;
+ const filterItems = ['Any', 'Unknown'];
+
+ if (!items) {
+ return [];
+ }
+
+ const newItems = items.filter((lang) => !filterItems.includes(lang.name)).map((item) => {
+ return {
+ key: item.id,
+ value: item.name
+ };
+ });
+
+ return newItems;
+ }
+ );
+}
+
function createMapStateToProps() {
return createSelector(
(state) => state.settings.advancedSettings,
createSettingsSectionSelector(SECTION),
- (advancedSettings, sectionSettings) => {
+ createLanguagesSelector(),
+ (advancedSettings, sectionSettings, languages) => {
return {
advancedSettings,
+ languages,
...sectionSettings
};
}
diff --git a/src/NzbDrone.Api/Calendar/CalendarFeedModule.cs b/src/NzbDrone.Api/Calendar/CalendarFeedModule.cs
index 7e05403f6..5803dbfd6 100644
--- a/src/NzbDrone.Api/Calendar/CalendarFeedModule.cs
+++ b/src/NzbDrone.Api/Calendar/CalendarFeedModule.cs
@@ -133,9 +133,7 @@ private void CreateEvent(Ical.Net.Calendar calendar, Movie movie, bool cinemasRe
occurrence.Description = movie.Overview;
occurrence.Categories = new List() { movie.Studio };
- var physicalText = movie.PhysicalReleaseNote.IsNotNullOrWhiteSpace()
- ? $"(Physical Release, {movie.PhysicalReleaseNote})"
- : "(Physical Release)";
+ var physicalText = "(Physical Release)";
occurrence.Summary = $"{movie.Title} " + (cinemasRelease ? "(Theatrical Release)" : physicalText);
}
}
diff --git a/src/NzbDrone.Api/Config/NamingConfigResource.cs b/src/NzbDrone.Api/Config/NamingConfigResource.cs
index b6e4a5352..08a0d349f 100644
--- a/src/NzbDrone.Api/Config/NamingConfigResource.cs
+++ b/src/NzbDrone.Api/Config/NamingConfigResource.cs
@@ -27,7 +27,7 @@ public static NamingConfigResource ToResource(this NamingConfig model)
{
Id = model.Id,
- RenameEpisodes = model.RenameEpisodes,
+ RenameEpisodes = model.RenameMovies,
ReplaceIllegalCharacters = model.ReplaceIllegalCharacters,
ColonReplacementFormat = model.ColonReplacementFormat,
MultiEpisodeStyle = model.MultiEpisodeStyle,
@@ -59,7 +59,7 @@ public static NamingConfig ToModel(this NamingConfigResource resource)
{
Id = resource.Id,
- RenameEpisodes = resource.RenameEpisodes,
+ RenameMovies = resource.RenameEpisodes,
ReplaceIllegalCharacters = resource.ReplaceIllegalCharacters,
ColonReplacementFormat = resource.ColonReplacementFormat,
StandardMovieFormat = resource.StandardMovieFormat,
diff --git a/src/NzbDrone.Api/Movies/MovieResource.cs b/src/NzbDrone.Api/Movies/MovieResource.cs
index 9d745b50d..d586300c6 100644
--- a/src/NzbDrone.Api/Movies/MovieResource.cs
+++ b/src/NzbDrone.Api/Movies/MovieResource.cs
@@ -122,7 +122,6 @@ public static MovieResource ToResource(this Core.Movies.Movie model)
SortTitle = model.SortTitle,
InCinemas = model.InCinemas,
PhysicalRelease = model.PhysicalRelease,
- PhysicalReleaseNote = model.PhysicalReleaseNote,
HasFile = model.HasFile,
Downloaded = downloaded,
@@ -139,7 +138,6 @@ public static MovieResource ToResource(this Core.Movies.Movie model)
Year = model.Year,
SecondaryYear = model.SecondaryYear,
- SecondaryYearSourceId = model.SecondaryYearSourceId,
Path = model.Path,
ProfileId = model.ProfileId,
@@ -189,7 +187,6 @@ public static Core.Movies.Movie ToModel(this MovieResource resource)
SortTitle = resource.SortTitle,
InCinemas = resource.InCinemas,
PhysicalRelease = resource.PhysicalRelease,
- PhysicalReleaseNote = resource.PhysicalReleaseNote,
//TotalEpisodeCount
//EpisodeCount
@@ -203,7 +200,6 @@ public static Core.Movies.Movie ToModel(this MovieResource resource)
Year = resource.Year,
SecondaryYear = resource.SecondaryYear,
- SecondaryYearSourceId = resource.SecondaryYearSourceId,
Path = resource.Path,
ProfileId = resource.ProfileId,
diff --git a/src/NzbDrone.Core.Test/Housekeeping/Housekeepers/CleanupOrphanedExtraFilesFixture.cs b/src/NzbDrone.Core.Test/Housekeeping/Housekeepers/CleanupOrphanedExtraFilesFixture.cs
index 81acea20d..e968fc723 100644
--- a/src/NzbDrone.Core.Test/Housekeeping/Housekeepers/CleanupOrphanedExtraFilesFixture.cs
+++ b/src/NzbDrone.Core.Test/Housekeeping/Housekeepers/CleanupOrphanedExtraFilesFixture.cs
@@ -31,7 +31,7 @@ public void should_delete_extra_files_that_dont_have_a_coresponding_movie()
public void should_not_delete_extra_files_that_have_a_coresponding_movie()
{
var movie = Builder.CreateNew()
- .BuildNew();
+ .BuildNew();
Db.Insert(movie);
@@ -49,7 +49,7 @@ public void should_not_delete_extra_files_that_have_a_coresponding_movie()
public void should_delete_extra_files_that_dont_have_a_coresponding_movie_file()
{
var movie = Builder.CreateNew()
- .BuildNew();
+ .BuildNew();
Db.Insert(movie);
@@ -67,7 +67,7 @@ public void should_delete_extra_files_that_dont_have_a_coresponding_movie_file()
public void should_not_delete_extra_files_that_have_a_coresponding_movie_file()
{
var movie = Builder.CreateNew()
- .BuildNew();
+ .BuildNew();
var movieFile = Builder.CreateNew()
.With(h => h.Quality = new QualityModel())
diff --git a/src/NzbDrone.Core.Test/Housekeeping/Housekeepers/CleanupOrphanedHistoryItemsFixture.cs b/src/NzbDrone.Core.Test/Housekeeping/Housekeepers/CleanupOrphanedHistoryItemsFixture.cs
index ae3fde9be..e49daf079 100644
--- a/src/NzbDrone.Core.Test/Housekeeping/Housekeepers/CleanupOrphanedHistoryItemsFixture.cs
+++ b/src/NzbDrone.Core.Test/Housekeeping/Housekeepers/CleanupOrphanedHistoryItemsFixture.cs
@@ -20,7 +20,7 @@ public class CleanupOrphanedHistoryItemsFixture : DbTest.CreateNew()
- .BuildNew();
+ .BuildNew();
}
private void GivenSeries()
diff --git a/src/NzbDrone.Core.Test/Housekeeping/Housekeepers/CleanupOrphanedMetadataFilesFixture.cs b/src/NzbDrone.Core.Test/Housekeeping/Housekeepers/CleanupOrphanedMetadataFilesFixture.cs
index 8c1479c35..fd35f289b 100644
--- a/src/NzbDrone.Core.Test/Housekeeping/Housekeepers/CleanupOrphanedMetadataFilesFixture.cs
+++ b/src/NzbDrone.Core.Test/Housekeeping/Housekeepers/CleanupOrphanedMetadataFilesFixture.cs
@@ -32,7 +32,7 @@ public void should_delete_metadata_files_that_dont_have_a_coresponding_movie()
public void should_not_delete_metadata_files_that_have_a_coresponding_movie()
{
var movie = Builder.CreateNew()
- .BuildNew();
+ .BuildNew();
Db.Insert(movie);
@@ -50,7 +50,7 @@ public void should_not_delete_metadata_files_that_have_a_coresponding_movie()
public void should_delete_metadata_files_that_dont_have_a_coresponding_movie_file()
{
var movie = Builder.CreateNew()
- .BuildNew();
+ .BuildNew();
Db.Insert(movie);
@@ -68,7 +68,7 @@ public void should_delete_metadata_files_that_dont_have_a_coresponding_movie_fil
public void should_not_delete_metadata_files_that_have_a_coresponding_movie_file()
{
var movie = Builder.CreateNew()
- .BuildNew();
+ .BuildNew();
var movieFile = Builder.CreateNew()
.With(h => h.Quality = new QualityModel())
@@ -92,7 +92,7 @@ public void should_not_delete_metadata_files_that_have_a_coresponding_movie_file
public void should_delete_movie_metadata_files_that_have_moviefileid_of_zero()
{
var movie = Builder.CreateNew()
- .BuildNew();
+ .BuildNew();
Db.Insert(movie);
@@ -111,7 +111,7 @@ public void should_delete_movie_metadata_files_that_have_moviefileid_of_zero()
public void should_delete_movie_image_files_that_have_moviefileid_of_zero()
{
var movie = Builder.CreateNew()
- .BuildNew();
+ .BuildNew();
Db.Insert(movie);
diff --git a/src/NzbDrone.Core.Test/Housekeeping/Housekeepers/CleanupOrphanedMovieTranslationsFixture.cs b/src/NzbDrone.Core.Test/Housekeeping/Housekeepers/CleanupOrphanedMovieTranslationsFixture.cs
new file mode 100644
index 000000000..b86512ac6
--- /dev/null
+++ b/src/NzbDrone.Core.Test/Housekeeping/Housekeepers/CleanupOrphanedMovieTranslationsFixture.cs
@@ -0,0 +1,47 @@
+using FizzWare.NBuilder;
+using FluentAssertions;
+using NUnit.Framework;
+using NzbDrone.Core.Housekeeping.Housekeepers;
+using NzbDrone.Core.Languages;
+using NzbDrone.Core.Movies;
+using NzbDrone.Core.Movies.Translations;
+using NzbDrone.Core.Test.Framework;
+
+namespace NzbDrone.Core.Test.Housekeeping.Housekeepers
+{
+ [TestFixture]
+ public class CleanupOrphanedMovieTranslationsFixture : DbTest
+ {
+ [Test]
+ public void should_delete_orphaned_movie_translation_items()
+ {
+ var translation = Builder.CreateNew()
+ .With(h => h.MovieId = default)
+ .With(h => h.Language = Language.English)
+ .BuildNew();
+
+ Db.Insert(translation);
+ Subject.Clean();
+ AllStoredModels.Should().BeEmpty();
+ }
+
+ [Test]
+ public void should_not_delete_unorphaned_movie_translation_items()
+ {
+ var movie = Builder.CreateNew().BuildNew();
+
+ Db.Insert(movie);
+
+ var translation = Builder.CreateNew()
+ .With(h => h.MovieId = default)
+ .With(h => h.Language = Language.English)
+ .With(b => b.MovieId = movie.Id)
+ .BuildNew();
+
+ Db.Insert(translation);
+
+ Subject.Clean();
+ AllStoredModels.Should().HaveCount(1);
+ }
+ }
+}
diff --git a/src/NzbDrone.Core.Test/Housekeeping/Housekeepers/CleanupOrphanedPendingReleasesFixture.cs b/src/NzbDrone.Core.Test/Housekeeping/Housekeepers/CleanupOrphanedPendingReleasesFixture.cs
index 5d05b7ae6..5c106bb79 100644
--- a/src/NzbDrone.Core.Test/Housekeeping/Housekeepers/CleanupOrphanedPendingReleasesFixture.cs
+++ b/src/NzbDrone.Core.Test/Housekeeping/Housekeepers/CleanupOrphanedPendingReleasesFixture.cs
@@ -1,4 +1,4 @@
-using FizzWare.NBuilder;
+using FizzWare.NBuilder;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.Download.Pending;
@@ -28,12 +28,12 @@ public void should_delete_orphaned_pending_items()
[Test]
public void should_not_delete_unorphaned_pending_items()
{
- var series = Builder.CreateNew().BuildNew();
+ var movie = Builder.CreateNew().BuildNew();
- Db.Insert(series);
+ Db.Insert(movie);
var pendingRelease = Builder.CreateNew()
- .With(h => h.MovieId = series.Id)
+ .With(h => h.MovieId = movie.Id)
.With(h => h.ParsedMovieInfo = new ParsedMovieInfo())
.With(h => h.Release = new ReleaseInfo())
.BuildNew();
diff --git a/src/NzbDrone.Core.Test/Housekeeping/Housekeepers/CleanupOrphanedSubtitleFilesFixture.cs b/src/NzbDrone.Core.Test/Housekeeping/Housekeepers/CleanupOrphanedSubtitleFilesFixture.cs
index 28787c63d..7981b00da 100644
--- a/src/NzbDrone.Core.Test/Housekeeping/Housekeepers/CleanupOrphanedSubtitleFilesFixture.cs
+++ b/src/NzbDrone.Core.Test/Housekeeping/Housekeepers/CleanupOrphanedSubtitleFilesFixture.cs
@@ -32,7 +32,7 @@ public void should_delete_subtitle_files_that_dont_have_a_coresponding_movie()
public void should_not_delete_subtitle_files_that_have_a_coresponding_movie()
{
var movie = Builder.CreateNew()
- .BuildNew();
+ .BuildNew();
Db.Insert(movie);
@@ -51,7 +51,7 @@ public void should_not_delete_subtitle_files_that_have_a_coresponding_movie()
public void should_delete_subtitle_files_that_dont_have_a_coresponding_movie_file()
{
var movie = Builder.CreateNew()
- .BuildNew();
+ .BuildNew();
Db.Insert(movie);
@@ -70,7 +70,7 @@ public void should_delete_subtitle_files_that_dont_have_a_coresponding_movie_fil
public void should_not_delete_subtitle_files_that_have_a_coresponding_movie_file()
{
var movie = Builder.CreateNew()
- .BuildNew();
+ .BuildNew();
var movieFile = Builder.CreateNew()
.With(h => h.Quality = new QualityModel())
diff --git a/src/NzbDrone.Core.Test/IndexerTests/FileListTests/FileListRequestGeneratorFixture.cs b/src/NzbDrone.Core.Test/IndexerTests/FileListTests/FileListRequestGeneratorFixture.cs
index 39231521f..cec838c79 100644
--- a/src/NzbDrone.Core.Test/IndexerTests/FileListTests/FileListRequestGeneratorFixture.cs
+++ b/src/NzbDrone.Core.Test/IndexerTests/FileListTests/FileListRequestGeneratorFixture.cs
@@ -1,3 +1,4 @@
+using System.Collections.Generic;
using System.Linq;
using FluentAssertions;
using NUnit.Framework;
@@ -24,7 +25,8 @@ public void Setup()
_movieSearchCriteria = new MovieSearchCriteria
{
- Movie = new Movies.Movie { ImdbId = "tt0076759", Title = "Star Wars", Year = 1977 }
+ Movie = new Movies.Movie { ImdbId = "tt0076759", Title = "Star Wars", Year = 1977 },
+ SceneTitles = new List { "Star Wars" }
};
}
@@ -70,7 +72,7 @@ public void should_search_by_name_and_year_if_missing_imdbid()
var page = results.GetAllTiers().First().First();
page.Url.Query.Should().Contain("type=name");
- page.Url.Query.Should().Contain("query=Star Wars 1977");
+ page.Url.Query.Should().Contain("query=Star+Wars+1977");
}
}
}
diff --git a/src/NzbDrone.Core.Test/IndexerTests/NewznabTests/NewznabRequestGeneratorFixture.cs b/src/NzbDrone.Core.Test/IndexerTests/NewznabTests/NewznabRequestGeneratorFixture.cs
index 2211bd24e..1cb57a2fa 100644
--- a/src/NzbDrone.Core.Test/IndexerTests/NewznabTests/NewznabRequestGeneratorFixture.cs
+++ b/src/NzbDrone.Core.Test/IndexerTests/NewznabTests/NewznabRequestGeneratorFixture.cs
@@ -1,3 +1,4 @@
+using System.Collections.Generic;
using System.Linq;
using FluentAssertions;
using Moq;
@@ -25,7 +26,8 @@ public void SetUp()
_movieSearchCriteria = new MovieSearchCriteria
{
- Movie = new Movies.Movie { ImdbId = "tt0076759", Title = "Star Wars", Year = 1977 }
+ Movie = new Movies.Movie { ImdbId = "tt0076759", Title = "Star Wars", Year = 1977, TmdbId = 11 },
+ SceneTitles = new List { "Star Wars" }
};
_capabilities = new NewznabCapabilities();
@@ -66,7 +68,7 @@ public void should_return_subsequent_pages()
{
var results = Subject.GetSearchRequests(_movieSearchCriteria);
- results.GetAllTiers().Should().HaveCount(1);
+ results.GetAllTiers().Should().HaveCount(2);
var pages = results.GetAllTiers().First().Take(3).ToList();
@@ -80,7 +82,7 @@ public void should_not_get_unlimited_pages()
{
var results = Subject.GetSearchRequests(_movieSearchCriteria);
- results.GetAllTiers().Should().HaveCount(1);
+ results.GetAllTiers().Should().HaveCount(2);
var pages = results.GetAllTiers().First().Take(500).ToList();
@@ -99,7 +101,7 @@ public void should_not_search_by_imdbid_if_not_supported()
var page = results.GetAllTiers().First().First();
page.Url.Query.Should().NotContain("imdbid=0076759");
- page.Url.Query.Should().Contain("q=star");
+ page.Url.Query.Should().Contain("q=Star");
}
[Test]
@@ -114,5 +116,93 @@ public void should_search_by_imdbid_if_supported()
page.Url.Query.Should().Contain("imdbid=0076759");
}
+
+ [Test]
+ public void should_search_by_tmdbid_if_supported()
+ {
+ _capabilities.SupportedMovieSearchParameters = new[] { "q", "tmdbid" };
+
+ var results = Subject.GetSearchRequests(_movieSearchCriteria);
+ results.GetTier(0).Should().HaveCount(1);
+
+ var page = results.GetAllTiers().First().First();
+
+ page.Url.Query.Should().Contain("tmdbid=11");
+ }
+
+ [Test]
+ public void should_prefer_search_by_tmdbid_if_rid_supported()
+ {
+ _capabilities.SupportedMovieSearchParameters = new[] { "q", "tmdbid", "imdbid" };
+
+ var results = Subject.GetSearchRequests(_movieSearchCriteria);
+ results.GetTier(0).Should().HaveCount(1);
+
+ var page = results.GetAllTiers().First().First();
+
+ page.Url.Query.Should().Contain("tmdbid=11");
+ page.Url.Query.Should().NotContain("imdbid=0076759");
+ }
+
+ [Test]
+ public void should_use_aggregrated_id_search_if_supported()
+ {
+ _capabilities.SupportedMovieSearchParameters = new[] { "q", "tmdbid", "imdbid" };
+ _capabilities.SupportsAggregateIdSearch = true;
+
+ var results = Subject.GetSearchRequests(_movieSearchCriteria);
+ results.GetTier(0).Should().HaveCount(1);
+
+ var page = results.GetTier(0).First().First();
+
+ page.Url.Query.Should().Contain("tmdbid=11");
+ page.Url.Query.Should().Contain("imdbid=0076759");
+ }
+
+ [Test]
+ public void should_not_use_aggregrated_id_search_if_no_ids_supported()
+ {
+ _capabilities.SupportedMovieSearchParameters = new[] { "q" };
+ _capabilities.SupportsAggregateIdSearch = true; // Turns true if indexer supplies supportedParams.
+
+ var results = Subject.GetSearchRequests(_movieSearchCriteria);
+ results.Tiers.Should().Be(1);
+ results.GetTier(0).Should().HaveCount(1);
+
+ var page = results.GetTier(0).First().First();
+
+ page.Url.Query.Should().Contain("q=");
+ }
+
+ [Test]
+ public void should_not_use_aggregrated_id_search_if_no_ids_are_known()
+ {
+ _capabilities.SupportedMovieSearchParameters = new[] { "q", "imdbid" };
+ _capabilities.SupportsAggregateIdSearch = true; // Turns true if indexer supplies supportedParams.
+
+ _movieSearchCriteria.Movie.ImdbId = null;
+
+ var results = Subject.GetSearchRequests(_movieSearchCriteria);
+
+ var page = results.GetTier(0).First().First();
+
+ page.Url.Query.Should().Contain("q=");
+ }
+
+ [Test]
+ public void should_fallback_to_q()
+ {
+ _capabilities.SupportedMovieSearchParameters = new[] { "q", "tmdbid", "imdbid" };
+ _capabilities.SupportsAggregateIdSearch = true;
+
+ var results = Subject.GetSearchRequests(_movieSearchCriteria);
+ results.Tiers.Should().Be(2);
+
+ var pageTier2 = results.GetTier(1).First().First();
+
+ pageTier2.Url.Query.Should().NotContain("tmdbid=11");
+ pageTier2.Url.Query.Should().NotContain("imdbid=0076759");
+ pageTier2.Url.Query.Should().Contain("q=");
+ }
}
}
diff --git a/src/NzbDrone.Core.Test/MediaFiles/MovieImport/Aggregation/Aggregators/AggregateLanguageFixture.cs b/src/NzbDrone.Core.Test/MediaFiles/MovieImport/Aggregation/Aggregators/AggregateLanguageFixture.cs
index 0ede3e6ca..49dca170e 100644
--- a/src/NzbDrone.Core.Test/MediaFiles/MovieImport/Aggregation/Aggregators/AggregateLanguageFixture.cs
+++ b/src/NzbDrone.Core.Test/MediaFiles/MovieImport/Aggregation/Aggregators/AggregateLanguageFixture.cs
@@ -35,7 +35,7 @@ private ParsedMovieInfo GetParsedMovieInfo(List languages)
[Test]
public void should_return_default_if_no_info_is_known()
{
- Subject.Aggregate(_localMovie, false).Languages.Should().Contain(Language.English);
+ Subject.Aggregate(_localMovie, false).Languages.Should().Contain(Language.Unknown);
}
[Test]
@@ -70,8 +70,8 @@ public void should_return_download_client_item_language_when_download_client_ite
[Test]
public void should_return_file_language_when_file_language_is_higher_than_others()
{
- _localMovie.DownloadClientMovieInfo = GetParsedMovieInfo(new List { Language.English });
- _localMovie.FolderMovieInfo = GetParsedMovieInfo(new List { Language.English });
+ _localMovie.DownloadClientMovieInfo = GetParsedMovieInfo(new List { Language.Unknown });
+ _localMovie.FolderMovieInfo = GetParsedMovieInfo(new List { Language.Unknown });
_localMovie.FileMovieInfo = GetParsedMovieInfo(new List { Language.French });
Subject.Aggregate(_localMovie, false).Languages.Should().Equal(_localMovie.FileMovieInfo.Languages);
@@ -80,9 +80,9 @@ public void should_return_file_language_when_file_language_is_higher_than_others
[Test]
public void should_return_multi_language()
{
- _localMovie.DownloadClientMovieInfo = GetParsedMovieInfo(new List { Language.English });
+ _localMovie.DownloadClientMovieInfo = GetParsedMovieInfo(new List { Language.Unknown });
_localMovie.FolderMovieInfo = GetParsedMovieInfo(new List { Language.English, Language.German });
- _localMovie.FileMovieInfo = GetParsedMovieInfo(new List { Language.English });
+ _localMovie.FileMovieInfo = GetParsedMovieInfo(new List { Language.Unknown });
Subject.Aggregate(_localMovie, false).Languages.Should().Equal(_localMovie.FolderMovieInfo.Languages);
}
diff --git a/src/NzbDrone.Core.Test/MovieTests/AddMovieFixture.cs b/src/NzbDrone.Core.Test/MovieTests/AddMovieFixture.cs
index 676194a6f..05d9f31dd 100644
--- a/src/NzbDrone.Core.Test/MovieTests/AddMovieFixture.cs
+++ b/src/NzbDrone.Core.Test/MovieTests/AddMovieFixture.cs
@@ -11,6 +11,7 @@
using NzbDrone.Core.MetadataSource;
using NzbDrone.Core.Movies;
using NzbDrone.Core.Movies.Credits;
+using NzbDrone.Core.Movies.Translations;
using NzbDrone.Core.Organizer;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Test.Common;
diff --git a/src/NzbDrone.Core.Test/MovieTests/AlternativeTitleServiceTests/AlternativeTitleServiceFixture.cs b/src/NzbDrone.Core.Test/MovieTests/AlternativeTitleServiceTests/AlternativeTitleServiceFixture.cs
index a09a4dbb2..6b3159384 100644
--- a/src/NzbDrone.Core.Test/MovieTests/AlternativeTitleServiceTests/AlternativeTitleServiceFixture.cs
+++ b/src/NzbDrone.Core.Test/MovieTests/AlternativeTitleServiceTests/AlternativeTitleServiceFixture.cs
@@ -27,7 +27,10 @@ public void Setup()
_title1 = titles[0];
_title2 = titles[1];
_title3 = titles[2];
- _movie = Builder.CreateNew().With(m => m.CleanTitle = "myothertitle").With(m => m.Id = 1).Build();
+ _movie = Builder.CreateNew()
+ .With(m => m.CleanTitle = "myothertitle")
+ .With(m => m.Id = 1)
+ .Build();
}
private void GivenExistingTitles(params AlternativeTitle[] titles)
diff --git a/src/NzbDrone.Core.Test/MovieTests/MovieRepositoryTests/MovieRepositoryFixture.cs b/src/NzbDrone.Core.Test/MovieTests/MovieRepositoryTests/MovieRepositoryFixture.cs
index f0c4cde94..a9ca4e998 100644
--- a/src/NzbDrone.Core.Test/MovieTests/MovieRepositoryTests/MovieRepositoryFixture.cs
+++ b/src/NzbDrone.Core.Test/MovieTests/MovieRepositoryTests/MovieRepositoryFixture.cs
@@ -4,6 +4,7 @@
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.CustomFormats;
+using NzbDrone.Core.Languages;
using NzbDrone.Core.Movies;
using NzbDrone.Core.Profiles;
using NzbDrone.Core.Qualities;
diff --git a/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/CleanTitleFixture.cs b/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/CleanTitleFixture.cs
index be2153705..54015ec77 100644
--- a/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/CleanTitleFixture.cs
+++ b/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/CleanTitleFixture.cs
@@ -30,7 +30,7 @@ public void Setup()
_episodeFile = new MovieFile { Quality = new QualityModel(), ReleaseGroup = "SonarrTest" };
_namingConfig = NamingConfig.Default;
- _namingConfig.RenameEpisodes = true;
+ _namingConfig.RenameMovies = true;
Mocker.GetMock()
.Setup(c => c.GetConfig()).Returns(_namingConfig);
diff --git a/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/FileNameBuilderFixture.cs b/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/FileNameBuilderFixture.cs
index c7ff556ed..d208c1a20 100644
--- a/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/FileNameBuilderFixture.cs
+++ b/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/FileNameBuilderFixture.cs
@@ -6,9 +6,11 @@
using Moq;
using NUnit.Framework;
using NzbDrone.Core.CustomFormats;
+using NzbDrone.Core.Languages;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.MediaFiles.MediaInfo;
using NzbDrone.Core.Movies;
+using NzbDrone.Core.Movies.Translations;
using NzbDrone.Core.Organizer;
using NzbDrone.Core.Qualities;
using NzbDrone.Core.Test.Framework;
@@ -21,18 +23,34 @@ public class FileNameBuilderFixture : CoreTest
{
private Movie _movie;
private MovieFile _movieFile;
+ private List _movieTranslations;
private NamingConfig _namingConfig;
[SetUp]
public void Setup()
{
+ _movieTranslations = new List
+ {
+ new MovieTranslation
+ {
+ Language = Language.German,
+ Title = "German South Park"
+ },
+
+ new MovieTranslation
+ {
+ Language = Language.French,
+ Title = "French South Park"
+ }
+ };
+
_movie = Builder
.CreateNew()
.With(s => s.Title = "South Park")
.Build();
_namingConfig = NamingConfig.Default;
- _namingConfig.RenameEpisodes = true;
+ _namingConfig.RenameMovies = true;
Mocker.GetMock()
.Setup(c => c.GetConfig()).Returns(_namingConfig);
@@ -46,6 +64,10 @@ public void Setup()
Mocker.GetMock()
.Setup(v => v.All())
.Returns(new List());
+
+ Mocker.GetMock()
+ .Setup(v => v.GetAllTranslationsForMovie(It.IsAny()))
+ .Returns(_movieTranslations);
}
private void GivenProper()
@@ -121,6 +143,51 @@ public void should_replace_movie_title_with_all_lower_case()
.Should().Be("south park");
}
+ [Test]
+ public void should_replace_translated_movie_title()
+ {
+ _namingConfig.StandardMovieFormat = "{Movie Title:FR}";
+
+ Subject.BuildFileName(_movie, _movieFile)
+ .Should().Be("French South Park");
+ }
+
+ [Test]
+ public void should_replace_translated_movie_title_with_base_title_if_invalid_code()
+ {
+ _namingConfig.StandardMovieFormat = "{Movie Title:JP}";
+
+ Subject.BuildFileName(_movie, _movieFile)
+ .Should().Be("South Park");
+ }
+
+ [Test]
+ public void should_replace_translated_movie_title_with_base_title_if_no_translation_exists()
+ {
+ _namingConfig.StandardMovieFormat = "{Movie Title:JA}";
+
+ Subject.BuildFileName(_movie, _movieFile)
+ .Should().Be("South Park");
+ }
+
+ [Test]
+ public void should_replace_translated_movie_title_with_fallback_if_no_translation_exists()
+ {
+ _namingConfig.StandardMovieFormat = "{Movie Title:JP|FR}";
+
+ Subject.BuildFileName(_movie, _movieFile)
+ .Should().Be("French South Park");
+ }
+
+ [Test]
+ public void should_replace_translated_movie_title_with_original_if_no_translation_or_fallback_exists()
+ {
+ _namingConfig.StandardMovieFormat = "{Movie Title:JP|CN}";
+
+ Subject.BuildFileName(_movie, _movieFile)
+ .Should().Be("South Park");
+ }
+
[Test]
public void should_cleanup_Movie_Title()
{
@@ -172,7 +239,7 @@ public void should_replace_all_contents_in_pattern()
[Test]
public void use_file_name_when_sceneName_is_null()
{
- _namingConfig.RenameEpisodes = false;
+ _namingConfig.RenameMovies = false;
_movieFile.RelativePath = "30 Rock - S01E01 - Test";
Subject.BuildFileName(_movie, _movieFile)
@@ -182,7 +249,7 @@ public void use_file_name_when_sceneName_is_null()
[Test]
public void use_path_when_sceneName_and_relative_path_are_null()
{
- _namingConfig.RenameEpisodes = false;
+ _namingConfig.RenameMovies = false;
_movieFile.RelativePath = null;
_movieFile.Path = @"C:\Test\Unsorted\Movie - S01E01 - Test";
@@ -193,7 +260,7 @@ public void use_path_when_sceneName_and_relative_path_are_null()
[Test]
public void use_file_name_when_sceneName_is_not_null()
{
- _namingConfig.RenameEpisodes = false;
+ _namingConfig.RenameMovies = false;
_movieFile.SceneName = "30.Rock.S01E01.xvid-LOL";
_movieFile.RelativePath = "30 Rock - S01E01 - Test";
@@ -317,7 +384,7 @@ public void should_remove_duplicate_non_word_characters()
[Test]
public void should_use_existing_filename_when_scene_name_is_not_available()
{
- _namingConfig.RenameEpisodes = true;
+ _namingConfig.RenameMovies = true;
_namingConfig.StandardMovieFormat = "{Original Title}";
_movieFile.SceneName = null;
diff --git a/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/MovieTitleFirstCharacterFixture.cs b/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/MovieTitleFirstCharacterFixture.cs
index f0e8f8731..25e3cc96d 100644
--- a/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/MovieTitleFirstCharacterFixture.cs
+++ b/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/MovieTitleFirstCharacterFixture.cs
@@ -24,7 +24,7 @@ public void Setup()
.Build();
_namingConfig = NamingConfig.Default;
- _namingConfig.RenameEpisodes = true;
+ _namingConfig.RenameMovies = true;
Mocker.GetMock()
.Setup(c => c.GetConfig()).Returns(_namingConfig);
diff --git a/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/TitleTheFixture.cs b/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/TitleTheFixture.cs
index 1c49b444a..dfbabd09a 100644
--- a/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/TitleTheFixture.cs
+++ b/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/TitleTheFixture.cs
@@ -30,7 +30,7 @@ public void Setup()
_movieFile = new MovieFile { Quality = new QualityModel(Quality.HDTV720p), ReleaseGroup = "RadarrTest" };
_namingConfig = NamingConfig.Default;
- _namingConfig.RenameEpisodes = true;
+ _namingConfig.RenameMovies = true;
Mocker.GetMock()
.Setup(c => c.GetConfig()).Returns(_namingConfig);
diff --git a/src/NzbDrone.Core.Test/ParserTests/LanguageParserFixture.cs b/src/NzbDrone.Core.Test/ParserTests/LanguageParserFixture.cs
index e8fcfec77..383f3afb8 100644
--- a/src/NzbDrone.Core.Test/ParserTests/LanguageParserFixture.cs
+++ b/src/NzbDrone.Core.Test/ParserTests/LanguageParserFixture.cs
@@ -11,8 +11,6 @@ namespace NzbDrone.Core.Test.ParserTests
public class LanguageParserFixture : CoreTest
{
[TestCase("Pulp.Fiction.1994.English.1080p.XviD-LOL")]
- [TestCase("The Danish Girl 2015")]
- [TestCase("Fantastic.Beasts.The.Crimes.Of.Grindelwald.2018.2160p.WEBRip.x265.10bit.HDR.DD5.1-GASMASK")]
public void should_parse_language_english(string postTitle)
{
var result = Parser.Parser.ParseMovieTitle(postTitle, true);
@@ -20,6 +18,15 @@ public void should_parse_language_english(string postTitle)
result.Languages.Should().BeEquivalentTo(Language.English);
}
+ [TestCase("The Danish Girl 2015")]
+ [TestCase("Fantastic.Beasts.The.Crimes.Of.Grindelwald.2018.2160p.WEBRip.x265.10bit.HDR.DD5.1-GASMASK")]
+ public void should_parse_language_unknown(string postTitle)
+ {
+ var result = Parser.Parser.ParseMovieTitle(postTitle, true);
+
+ result.Languages.Should().BeEquivalentTo(Language.Unknown);
+ }
+
[TestCase("Pulp.Fiction.1994.French.1080p.XviD-LOL")]
public void should_parse_language_french(string postTitle)
{
@@ -28,6 +35,15 @@ public void should_parse_language_french(string postTitle)
result.Languages.Should().BeEquivalentTo(Language.French);
}
+ [TestCase("E.T. the Extra-Terrestrial.1982.Ger.Eng.AC3.DL.BDRip.x264-iNCEPTiON")]
+ public void should_parse_language_english_german(string postTitle)
+ {
+ var result = Parser.Parser.ParseMovieTitle(postTitle, true);
+
+ result.Languages.Should().Contain(Language.German);
+ result.Languages.Should().Contain(Language.English);
+ }
+
[TestCase("Pulp.Fiction.1994.Spanish.1080p.XviD-LOL")]
public void should_parse_language_spanish(string postTitle)
{
diff --git a/src/NzbDrone.Core.Test/ParserTests/ParserFixture.cs b/src/NzbDrone.Core.Test/ParserTests/ParserFixture.cs
index 11538b7c9..8c65924f2 100644
--- a/src/NzbDrone.Core.Test/ParserTests/ParserFixture.cs
+++ b/src/NzbDrone.Core.Test/ParserTests/ParserFixture.cs
@@ -171,7 +171,7 @@ public void should_not_parse_wrong_language_in_title(string postTitle)
{
var parsed = Parser.Parser.ParseMovieTitle(postTitle, true);
parsed.Languages.Count().Should().Be(1);
- parsed.Languages.First().Should().Be(Language.English);
+ parsed.Languages.First().Should().Be(Language.Unknown);
}
[TestCase("The.Purge.3.Election.Year.2016.German.DTS.DL.720p.BluRay.x264-MULTiPLEX")]
diff --git a/src/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/MapFixture.cs b/src/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/MapFixture.cs
index 52ca3b1f5..0a46da3a6 100644
--- a/src/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/MapFixture.cs
+++ b/src/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/MapFixture.cs
@@ -5,6 +5,7 @@
using Moq;
using NUnit.Framework;
using NzbDrone.Core.IndexerSearch.Definitions;
+using NzbDrone.Core.Languages;
using NzbDrone.Core.Movies;
using NzbDrone.Core.Movies.AlternativeTitles;
using NzbDrone.Core.Parser;
@@ -34,47 +35,55 @@ public void Setup()
.With(m => m.CleanTitle = "fackjugoethe2")
.With(m => m.Year = 2015)
.With(m => m.AlternativeTitles = new List { new AlternativeTitle("Fack Ju Göthe 2: Same same") })
+ .With(m => m.OriginalLanguage = Language.English)
.Build();
_parsedMovieInfo = new ParsedMovieInfo
{
MovieTitle = _movie.Title,
+ Languages = new List { Language.English },
Year = _movie.Year,
};
_wrongYearInfo = new ParsedMovieInfo
{
MovieTitle = _movie.Title,
+ Languages = new List { Language.English },
Year = 1900,
};
_wrongTitleInfo = new ParsedMovieInfo
{
MovieTitle = "Other Title",
+ Languages = new List { Language.English },
Year = 2015
};
_alternativeTitleInfo = new ParsedMovieInfo
{
MovieTitle = _movie.AlternativeTitles.First().Title,
+ Languages = new List { Language.English },
Year = _movie.Year,
};
_romanTitleInfo = new ParsedMovieInfo
{
MovieTitle = "Fack Ju Göthe II",
+ Languages = new List { Language.English },
Year = _movie.Year,
};
_umlautInfo = new ParsedMovieInfo
{
MovieTitle = "Fack Ju Goethe 2",
+ Languages = new List { Language.English },
Year = _movie.Year
};
_umlautAltInfo = new ParsedMovieInfo
{
MovieTitle = "Fack Ju Goethe 2: Same same",
+ Languages = new List { Language.English },
Year = _movie.Year
};
diff --git a/src/NzbDrone.Core.Test/Profiles/ProfileServiceFixture.cs b/src/NzbDrone.Core.Test/Profiles/ProfileServiceFixture.cs
index 3ec5acf57..5745949c8 100644
--- a/src/NzbDrone.Core.Test/Profiles/ProfileServiceFixture.cs
+++ b/src/NzbDrone.Core.Test/Profiles/ProfileServiceFixture.cs
@@ -1,13 +1,16 @@
using System.Collections.Generic;
using System.Linq;
using FizzWare.NBuilder;
+using FluentAssertions;
using Moq;
using NUnit.Framework;
using NzbDrone.Core.CustomFormats;
+using NzbDrone.Core.Languages;
using NzbDrone.Core.Lifecycle;
using NzbDrone.Core.Movies;
using NzbDrone.Core.NetImport;
using NzbDrone.Core.Profiles;
+using NzbDrone.Core.Test.CustomFormats;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.Profiles
@@ -107,5 +110,45 @@ public void should_delete_profile_if_not_assigned_to_movie_or_list()
Mocker.GetMock().Verify(c => c.Delete(1), Times.Once());
}
+
+ [Test]
+ public void get_acceptable_languages_should_return_profile_language()
+ {
+ var profile = Builder.CreateNew().With(c => c.Language = Language.German).Build();
+
+ Mocker.GetMock()
+ .Setup(s => s.Get(It.IsAny()))
+ .Returns(profile);
+
+ var languages = Subject.GetAcceptableLanguages(profile.Id);
+
+ languages.Count.Should().Be(1);
+ languages.Should().Contain(Language.German);
+ }
+
+ [Test]
+ public void get_acceptable_languages_should_return_custom_format_positive_languages()
+ {
+ var profile = Builder.CreateNew()
+ .With(c => c.Language = Language.German)
+ .Build();
+
+ var customFormat1 = new CustomFormat("My Format 1", new LanguageSpecification { Value = (int)Language.English }) { Id = 1 };
+ var customFormat2 = new CustomFormat("My Format 2", new LanguageSpecification { Value = (int)Language.French }) { Id = 2 };
+
+ CustomFormatsFixture.GivenCustomFormats(customFormat1, customFormat2);
+
+ profile.FormatItems = CustomFormatsFixture.GetSampleFormatItems(customFormat2.Name);
+
+ Mocker.GetMock()
+ .Setup(s => s.Get(It.IsAny()))
+ .Returns(profile);
+
+ var languages = Subject.GetAcceptableLanguages(profile.Id);
+
+ languages.Count.Should().Be(2);
+ languages.Should().Contain(Language.German);
+ languages.Should().Contain(Language.French);
+ }
}
}
diff --git a/src/NzbDrone.Core/Configuration/ConfigService.cs b/src/NzbDrone.Core/Configuration/ConfigService.cs
index c3abfd61a..0cae278e0 100644
--- a/src/NzbDrone.Core/Configuration/ConfigService.cs
+++ b/src/NzbDrone.Core/Configuration/ConfigService.cs
@@ -6,6 +6,7 @@
using NzbDrone.Common.EnsureThat;
using NzbDrone.Common.Http.Proxy;
using NzbDrone.Core.Configuration.Events;
+using NzbDrone.Core.Languages;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.MetadataSource.SkyHook.Resource;
@@ -371,6 +372,13 @@ public bool EnableColorImpairedMode
set { SetValue("EnableColorImpairedMode", value); }
}
+ public int MovieInfoLanguage
+ {
+ get { return GetValueInt("MovieInfoLanguage", (int)Language.English); }
+
+ set { SetValue("MovieInfoLanguage", value); }
+ }
+
public bool CleanupMetadataImages
{
get { return GetValueBoolean("CleanupMetadataImages", true); }
diff --git a/src/NzbDrone.Core/Configuration/IConfigService.cs b/src/NzbDrone.Core/Configuration/IConfigService.cs
index b2bc022c2..e2ffcf1a6 100644
--- a/src/NzbDrone.Core/Configuration/IConfigService.cs
+++ b/src/NzbDrone.Core/Configuration/IConfigService.cs
@@ -75,6 +75,7 @@ public interface IConfigService
string TimeFormat { get; set; }
bool ShowRelativeDates { get; set; }
bool EnableColorImpairedMode { get; set; }
+ int MovieInfoLanguage { get; set; }
//Internal
bool CleanupMetadataImages { get; set; }
diff --git a/src/NzbDrone.Core/Datastore/Migration/177_language_improvements.cs b/src/NzbDrone.Core/Datastore/Migration/177_language_improvements.cs
new file mode 100644
index 000000000..8665fe032
--- /dev/null
+++ b/src/NzbDrone.Core/Datastore/Migration/177_language_improvements.cs
@@ -0,0 +1,115 @@
+using System.Collections.Generic;
+using System.Data;
+using System.Linq;
+using System.Text.Json;
+using Dapper;
+using FluentMigrator;
+using NzbDrone.Core.Datastore.Migration.Framework;
+using NzbDrone.Core.Languages;
+
+namespace NzbDrone.Core.Datastore.Migration
+{
+ [Migration(177)]
+ public class language_improvements : NzbDroneMigrationBase
+ {
+ private readonly JsonSerializerOptions _serializerSettings;
+
+ public language_improvements()
+ {
+ _serializerSettings = new JsonSerializerOptions
+ {
+ AllowTrailingCommas = true,
+ IgnoreNullValues = false,
+ PropertyNameCaseInsensitive = true,
+ DictionaryKeyPolicy = JsonNamingPolicy.CamelCase,
+ PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
+ WriteIndented = true
+ };
+ }
+
+ protected override void MainDbUpgrade()
+ {
+ // Use original language to set default language fallback for releases
+ // Set all to English (1) on migration to ensure default behavior persists until refresh
+ Alter.Table("Movies").AddColumn("OriginalLanguage").AsInt32().WithDefaultValue((int)Language.English);
+ Alter.Table("Movies").AddColumn("OriginalTitle").AsString().Nullable();
+
+ Alter.Table("Movies").AddColumn("DigitalRelease").AsDateTime().Nullable();
+
+ // Column not used
+ Delete.Column("PhysicalReleaseNote").FromTable("Movies");
+ Delete.Column("SecondaryYearSourceId").FromTable("Movies");
+
+ Alter.Table("NamingConfig").AddColumn("RenameMovies").AsBoolean().WithDefaultValue(0);
+ Execute.Sql("UPDATE NamingConfig SET RenameMovies=RenameEpisodes");
+ Delete.Column("RenameEpisodes").FromTable("NamingConfig");
+
+ //Manual SQL, Fluent Migrator doesn't support multi-column unique contraint on table creation, SQLite doesn't support adding it after creation
+ Execute.Sql("CREATE TABLE MovieTranslations(" +
+ "Id INTEGER PRIMARY KEY, " +
+ "MovieId INTEGER NOT NULL, " +
+ "Title TEXT, " +
+ "CleanTitle TEXT, " +
+ "Overview TEXT, " +
+ "Language INTEGER NOT NULL, " +
+ "Unique(\"MovieId\", \"Language\"));");
+
+ // Prevent failure if two movies have same alt titles
+ Execute.Sql("DROP INDEX IF EXISTS \"IX_AlternativeTitles_CleanTitle\"");
+
+ Execute.WithConnection(FixLanguagesMoveFile);
+ Execute.WithConnection(FixLanguagesHistory);
+ }
+
+ private void FixLanguagesMoveFile(IDbConnection conn, IDbTransaction tran)
+ {
+ var rows = conn.Query($"SELECT Id, Languages FROM MovieFiles");
+
+ var corrected = new List();
+
+ foreach (var row in rows)
+ {
+ var languages = JsonSerializer.Deserialize>(row.Languages, _serializerSettings);
+
+ var newLanguages = languages.Distinct().ToList();
+
+ corrected.Add(new LanguageEntity177
+ {
+ Id = row.Id,
+ Languages = JsonSerializer.Serialize(newLanguages, _serializerSettings)
+ });
+ }
+
+ var updateSql = "UPDATE MovieFiles SET Languages = @Languages WHERE Id = @Id";
+ conn.Execute(updateSql, corrected, transaction: tran);
+ }
+
+ private void FixLanguagesHistory(IDbConnection conn, IDbTransaction tran)
+ {
+ var rows = conn.Query($"SELECT Id, Languages FROM History");
+
+ var corrected = new List();
+
+ foreach (var row in rows)
+ {
+ var languages = JsonSerializer.Deserialize>(row.Languages, _serializerSettings);
+
+ var newLanguages = languages.Distinct().ToList();
+
+ corrected.Add(new LanguageEntity177
+ {
+ Id = row.Id,
+ Languages = JsonSerializer.Serialize(newLanguages, _serializerSettings)
+ });
+ }
+
+ var updateSql = "UPDATE History SET Languages = @Languages WHERE Id = @Id";
+ conn.Execute(updateSql, corrected, transaction: tran);
+ }
+
+ private class LanguageEntity177 : ModelBase
+ {
+ public string Languages { get; set; }
+ }
+ }
+}
diff --git a/src/NzbDrone.Core/Datastore/TableMapping.cs b/src/NzbDrone.Core/Datastore/TableMapping.cs
index 60cf83c06..710de37bd 100644
--- a/src/NzbDrone.Core/Datastore/TableMapping.cs
+++ b/src/NzbDrone.Core/Datastore/TableMapping.cs
@@ -26,6 +26,7 @@
using NzbDrone.Core.Movies;
using NzbDrone.Core.Movies.AlternativeTitles;
using NzbDrone.Core.Movies.Credits;
+using NzbDrone.Core.Movies.Translations;
using NzbDrone.Core.NetImport;
using NzbDrone.Core.NetImport.ImportExclusions;
using NzbDrone.Core.Notifications;
@@ -105,6 +106,8 @@ public static void Map()
Mapper.Entity("AlternativeTitles").RegisterModel();
+ Mapper.Entity("MovieTranslations").RegisterModel();
+
Mapper.Entity("Credits").RegisterModel();
Mapper.Entity("ImportExclusions").RegisterModel();
diff --git a/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupOrphanedMovieTranslations.cs b/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupOrphanedMovieTranslations.cs
new file mode 100644
index 000000000..efb55262a
--- /dev/null
+++ b/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupOrphanedMovieTranslations.cs
@@ -0,0 +1,28 @@
+using Dapper;
+using NzbDrone.Core.Datastore;
+
+namespace NzbDrone.Core.Housekeeping.Housekeepers
+{
+ public class CleanupOrphanedMovieTranslations : IHousekeepingTask
+ {
+ private readonly IMainDatabase _database;
+
+ public CleanupOrphanedMovieTranslations(IMainDatabase database)
+ {
+ _database = database;
+ }
+
+ public void Clean()
+ {
+ using (var mapper = _database.OpenConnection())
+ {
+ mapper.Execute(@"DELETE FROM MovieTranslations
+ WHERE Id IN (
+ SELECT MovieTranslations.Id FROM MovieTranslations
+ LEFT OUTER JOIN Movies
+ ON MovieTranslations.MovieId = Movies.Id
+ WHERE Movies.Id IS NULL)");
+ }
+ }
+ }
+}
diff --git a/src/NzbDrone.Core/IndexerSearch/NzbSearchService.cs b/src/NzbDrone.Core/IndexerSearch/NzbSearchService.cs
index 0d7219892..e615b899b 100644
--- a/src/NzbDrone.Core/IndexerSearch/NzbSearchService.cs
+++ b/src/NzbDrone.Core/IndexerSearch/NzbSearchService.cs
@@ -9,7 +9,9 @@
using NzbDrone.Core.Indexers;
using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.Movies;
+using NzbDrone.Core.Movies.Translations;
using NzbDrone.Core.Parser.Model;
+using NzbDrone.Core.Profiles;
namespace NzbDrone.Core.IndexerSearch
{
@@ -24,16 +26,22 @@ public class NzbSearchService : ISearchForNzb
private readonly IIndexerFactory _indexerFactory;
private readonly IMakeDownloadDecision _makeDownloadDecision;
private readonly IMovieService _movieService;
+ private readonly IMovieTranslationService _movieTranslationService;
+ private readonly IProfileService _profileService;
private readonly Logger _logger;
public NzbSearchService(IIndexerFactory indexerFactory,
IMakeDownloadDecision makeDownloadDecision,
IMovieService movieService,
+ IMovieTranslationService movieTranslationService,
+ IProfileService profileService,
Logger logger)
{
_indexerFactory = indexerFactory;
_makeDownloadDecision = makeDownloadDecision;
_movieService = movieService;
+ _movieTranslationService = movieTranslationService;
+ _profileService = profileService;
_logger = logger;
}
@@ -60,6 +68,24 @@ private TSpec Get(Movie movie, bool userInvokedSearch, bool interactiveSe
UserInvokedSearch = userInvokedSearch,
InteractiveSearch = interactiveSearch
};
+
+ var wantedLanguages = _profileService.GetAcceptableLanguages(movie.ProfileId);
+ var translations = _movieTranslationService.GetAllTranslationsForMovie(movie.Id);
+
+ var queryTranlations = new List
+ {
+ movie.Title,
+ movie.OriginalTitle
+ };
+
+ //Add Translation of wanted languages to search query
+ foreach (var translation in translations.Where(a => wantedLanguages.Contains(a.Language)))
+ {
+ queryTranlations.Add(translation.Title);
+ }
+
+ spec.SceneTitles = queryTranlations.Distinct().ToList();
+
return spec;
}
diff --git a/src/NzbDrone.Core/Indexers/FileList/FileListRequestGenerator.cs b/src/NzbDrone.Core/Indexers/FileList/FileListRequestGenerator.cs
index c462bdc2e..1d6ac1445 100644
--- a/src/NzbDrone.Core/Indexers/FileList/FileListRequestGenerator.cs
+++ b/src/NzbDrone.Core/Indexers/FileList/FileListRequestGenerator.cs
@@ -30,8 +30,11 @@ public virtual IndexerPageableRequestChain GetSearchRequests(MovieSearchCriteria
}
else
{
- var titleYearSearchQuery = string.Format("{0} {1}", searchCriteria.Movie.Title, searchCriteria.Movie.Year);
- pageableRequests.Add(GetRequest("search-torrents", string.Format("&type=name&query={0}", titleYearSearchQuery.Trim())));
+ foreach (var queryTitle in searchCriteria.QueryTitles)
+ {
+ var titleYearSearchQuery = string.Format("{0}+{1}", queryTitle, searchCriteria.Movie.Year);
+ pageableRequests.Add(GetRequest("search-torrents", string.Format("&type=name&query={0}", titleYearSearchQuery.Trim())));
+ }
}
return pageableRequests;
diff --git a/src/NzbDrone.Core/Indexers/Newznab/NewznabCapabilities.cs b/src/NzbDrone.Core/Indexers/Newznab/NewznabCapabilities.cs
index 717d24a9f..7961b0b2e 100644
--- a/src/NzbDrone.Core/Indexers/Newznab/NewznabCapabilities.cs
+++ b/src/NzbDrone.Core/Indexers/Newznab/NewznabCapabilities.cs
@@ -1,4 +1,4 @@
-using System.Collections.Generic;
+using System.Collections.Generic;
namespace NzbDrone.Core.Indexers.Newznab
{
@@ -7,7 +7,6 @@ public class NewznabCapabilities
public int DefaultPageSize { get; set; }
public int MaxPageSize { get; set; }
public string[] SupportedSearchParameters { get; set; }
- public string[] SupportedTvSearchParameters { get; set; }
public string[] SupportedMovieSearchParameters { get; set; }
public bool SupportsAggregateIdSearch { get; set; }
public List Categories { get; set; }
@@ -18,7 +17,6 @@ public NewznabCapabilities()
MaxPageSize = 100;
SupportedSearchParameters = new[] { "q" };
SupportedMovieSearchParameters = new[] { "q", "imdbid", "imdbtitle", "imdbyear" };
- SupportedTvSearchParameters = new[] { "q", "rid", "season", "ep" }; // This should remain 'rid' for older newznab installs.
SupportsAggregateIdSearch = false;
Categories = new List();
}
diff --git a/src/NzbDrone.Core/Indexers/Newznab/NewznabCapabilitiesProvider.cs b/src/NzbDrone.Core/Indexers/Newznab/NewznabCapabilitiesProvider.cs
index e1a27cefe..5da80a508 100644
--- a/src/NzbDrone.Core/Indexers/Newznab/NewznabCapabilitiesProvider.cs
+++ b/src/NzbDrone.Core/Indexers/Newznab/NewznabCapabilitiesProvider.cs
@@ -118,17 +118,6 @@ private NewznabCapabilities ParseCapabilities(HttpResponse response)
capabilities.SupportedSearchParameters = xmlBasicSearch.Attribute("supportedParams").Value.Split(',');
}
- var xmlTvSearch = xmlSearching.Element("tv-search");
- if (xmlTvSearch == null || xmlTvSearch.Attribute("available").Value != "yes")
- {
- capabilities.SupportedTvSearchParameters = null;
- }
- else if (xmlTvSearch.Attribute("supportedParams") != null)
- {
- capabilities.SupportedTvSearchParameters = xmlTvSearch.Attribute("supportedParams").Value.Split(',');
- capabilities.SupportsAggregateIdSearch = true;
- }
-
var xmlMovieSearch = xmlSearching.Element("movie-search");
if (xmlMovieSearch == null || xmlMovieSearch.Attribute("available").Value != "yes")
{
diff --git a/src/NzbDrone.Core/Indexers/Newznab/NewznabRequestGenerator.cs b/src/NzbDrone.Core/Indexers/Newznab/NewznabRequestGenerator.cs
index d5b31f169..f40e5513c 100644
--- a/src/NzbDrone.Core/Indexers/Newznab/NewznabRequestGenerator.cs
+++ b/src/NzbDrone.Core/Indexers/Newznab/NewznabRequestGenerator.cs
@@ -22,7 +22,18 @@ public NewznabRequestGenerator(INewznabCapabilitiesProvider capabilitiesProvider
PageSize = 100;
}
- private bool SupportsMovieSearch
+ private bool SupportsSearch
+ {
+ get
+ {
+ var capabilities = _capabilitiesProvider.GetCapabilities(Settings);
+
+ return capabilities.SupportedSearchParameters != null &&
+ capabilities.SupportedSearchParameters.Contains("q");
+ }
+ }
+
+ private bool SupportsImdbSearch
{
get
{
@@ -33,6 +44,27 @@ private bool SupportsMovieSearch
}
}
+ private bool SupportsTmdbSearch
+ {
+ get
+ {
+ var capabilities = _capabilitiesProvider.GetCapabilities(Settings);
+
+ return capabilities.SupportedMovieSearchParameters != null &&
+ capabilities.SupportedMovieSearchParameters.Contains("tmdbid");
+ }
+ }
+
+ private bool SupportsAggregatedIdSearch
+ {
+ get
+ {
+ var capabilities = _capabilitiesProvider.GetCapabilities(Settings);
+
+ return capabilities.SupportsAggregateIdSearch;
+ }
+ }
+
public virtual IndexerPageableRequestChain GetRecentRequests()
{
var pageableRequests = new IndexerPageableRequestChain();
@@ -56,34 +88,68 @@ public IndexerPageableRequestChain GetSearchRequests(MovieSearchCriteria searchC
{
var pageableRequests = new IndexerPageableRequestChain();
- if (!Settings.SearchByTitle && SupportsMovieSearch && searchCriteria.Movie.ImdbId.IsNotNullOrWhiteSpace())
+ AddMovieIdPageableRequests(pageableRequests, MaxPages, Settings.Categories, searchCriteria);
+
+ return pageableRequests;
+ }
+
+ private void AddMovieIdPageableRequests(IndexerPageableRequestChain chain, int maxPages, IEnumerable categories, SearchCriteriaBase searchCriteria)
+ {
+ var includeTmdbSearch = SupportsTmdbSearch && searchCriteria.Movie.TmdbId > 0;
+ var includeImdbSearch = SupportsImdbSearch && searchCriteria.Movie.ImdbId.IsNotNullOrWhiteSpace();
+
+ if (SupportsAggregatedIdSearch && (includeTmdbSearch || includeImdbSearch))
{
- pageableRequests.Add(GetPagedRequests(MaxPages, Settings.Categories, "movie", $"&imdbid={searchCriteria.Movie.ImdbId.Substring(2)}"));
+ var ids = "";
+
+ if (includeTmdbSearch)
+ {
+ ids += "&tmdbid=" + searchCriteria.Movie.TmdbId;
+ }
+
+ if (includeImdbSearch)
+ {
+ ids += "&imdbid=" + searchCriteria.Movie.ImdbId.Substring(2);
+ }
+
+ chain.Add(GetPagedRequests(maxPages, categories, "movie", ids));
}
else
{
- var altTitles = searchCriteria.Movie.AlternativeTitles.Take(5).Select(t => t.Title).ToList();
- altTitles.Add(searchCriteria.Movie.Title);
-
- var realMaxPages = (int)MaxPages / altTitles.Count();
-
- //pageableRequests.Add(GetPagedRequests(MaxPages - (altTitles.Count() * realMaxPages), Settings.Categories, "search", $"&q={searchTitle}%20{searchCriteria.Movie.Year}"));
-
- //Also use alt titles for searching.
- foreach (string altTitle in altTitles)
+ if (includeTmdbSearch)
{
- var searchAltTitle = System.Web.HttpUtility.UrlPathEncode(Parser.Parser.ReplaceGermanUmlauts(Parser.Parser.NormalizeTitle(altTitle)));
- var queryString = $"&q={searchAltTitle}";
- if (!Settings.RemoveYear)
- {
- queryString += $"%20{searchCriteria.Movie.Year}";
- }
-
- pageableRequests.Add(GetPagedRequests(realMaxPages, Settings.Categories, "search", queryString));
+ chain.Add(GetPagedRequests(maxPages,
+ categories,
+ "movie",
+ string.Format("&tmdbid={0}", searchCriteria.Movie.TmdbId)));
+ }
+ else if (includeImdbSearch)
+ {
+ chain.Add(GetPagedRequests(maxPages,
+ categories,
+ "movie",
+ string.Format("&imdbid={0}", searchCriteria.Movie.ImdbId.Substring(2))));
}
}
- return pageableRequests;
+ if (SupportsSearch)
+ {
+ chain.AddTier();
+ foreach (var queryTitle in searchCriteria.QueryTitles)
+ {
+ var searchQuery = queryTitle;
+
+ if (!Settings.RemoveYear)
+ {
+ searchQuery = string.Format("{0} {1}", searchQuery, searchCriteria.Movie.Year);
+ }
+
+ chain.Add(GetPagedRequests(MaxPages,
+ Settings.Categories,
+ "movie",
+ string.Format("&q={0}", NewsnabifyTitle(searchQuery))));
+ }
+ }
}
private IEnumerable GetPagedRequests(int maxPages, IEnumerable categories, string searchType, string parameters)
@@ -115,6 +181,11 @@ private IEnumerable GetPagedRequests(int maxPages, IEnumerable> GetCookies { get; set; }
public Action, DateTime?> CookiesUpdater { get; set; }
}
diff --git a/src/NzbDrone.Core/Indexers/Newznab/NewznabRssParser.cs b/src/NzbDrone.Core/Indexers/Newznab/NewznabRssParser.cs
index 3921615aa..889ec1978 100644
--- a/src/NzbDrone.Core/Indexers/Newznab/NewznabRssParser.cs
+++ b/src/NzbDrone.Core/Indexers/Newznab/NewznabRssParser.cs
@@ -77,21 +77,6 @@ protected override ReleaseInfo ProcessItem(XElement item, ReleaseInfo releaseInf
releaseInfo = base.ProcessItem(item, releaseInfo);
releaseInfo.ImdbId = GetImdbId(item);
- //// This shouldn't be needed with changes to the DownloadDecisionMaker
- //var imdbMovieTitle = GetImdbTitle(item);
- //var imdbYear = GetImdbYear(item);
-
- //// Fun, lets try to add year to the releaseTitle for our foriegn friends :)
- //// if (!releaseInfo.Title.ContainsIgnoreCase(imdbMovieTitle + "." + imdbYear))
- //var isMatch = Regex.IsMatch(releaseInfo.Title, $@"^{imdbMovieTitle}.*{imdbYear}", RegexOptions.IgnoreCase);
- //if (!isMatch)
- //{
- // if (imdbYear != 1900 && imdbMovieTitle != string.Empty)
- // {
- // // releaseInfo.Title = releaseInfo.Title.Replace(imdbMovieTitle, imdbMovieTitle + "." + imdbYear);
- // releaseInfo.Title = Regex.Replace(releaseInfo.Title, imdbMovieTitle, imdbMovieTitle + "." + imdbYear, RegexOptions.IgnoreCase);
- // }
- //}
return releaseInfo;
}
diff --git a/src/NzbDrone.Core/Indexers/Newznab/NewznabSettings.cs b/src/NzbDrone.Core/Indexers/Newznab/NewznabSettings.cs
index 5dc327147..18cd16b71 100644
--- a/src/NzbDrone.Core/Indexers/Newznab/NewznabSettings.cs
+++ b/src/NzbDrone.Core/Indexers/Newznab/NewznabSettings.cs
@@ -88,13 +88,6 @@ public NewznabSettings()
Type = FieldType.Checkbox)]
public bool RemoveYear { get; set; }
- [FieldDefinition(7,
- Label = "Search by Title",
- HelpText = "By default, Radarr will try to search by IMDB ID if your indexer supports that. However, some indexers are not very good at tagging their releases correctly, so you can force Radarr to search that indexer by title instead.",
- Advanced = true,
- Type = FieldType.Checkbox)]
- public bool SearchByTitle { get; set; }
-
// Field 8 is used by TorznabSettings MinimumSeeders
// If you need to add another field here, update TorznabSettings as well and this comment
public virtual NzbDroneValidationResult Validate()
diff --git a/src/NzbDrone.Core/Indexers/Nyaa/NyaaRequestGenerator.cs b/src/NzbDrone.Core/Indexers/Nyaa/NyaaRequestGenerator.cs
index 364be6240..014315d03 100644
--- a/src/NzbDrone.Core/Indexers/Nyaa/NyaaRequestGenerator.cs
+++ b/src/NzbDrone.Core/Indexers/Nyaa/NyaaRequestGenerator.cs
@@ -58,7 +58,16 @@ private string PrepareQuery(string query)
public IndexerPageableRequestChain GetSearchRequests(MovieSearchCriteria searchCriteria)
{
- return new IndexerPageableRequestChain();
+ var pageableRequests = new IndexerPageableRequestChain();
+
+ foreach (var queryTitle in searchCriteria.QueryTitles)
+ {
+ pageableRequests.Add(GetPagedRequests(MaxPages,
+ string.Format("&term={0}",
+ PrepareQuery(string.Format("{0} {1}", queryTitle, searchCriteria.Movie.Year)))));
+ }
+
+ return pageableRequests;
}
public Func> GetCookies { get; set; }
diff --git a/src/NzbDrone.Core/Indexers/Omgwtfnzbs/OmgwtfnzbsRequestGenerator.cs b/src/NzbDrone.Core/Indexers/Omgwtfnzbs/OmgwtfnzbsRequestGenerator.cs
index 410b08992..b2bf7d5aa 100644
--- a/src/NzbDrone.Core/Indexers/Omgwtfnzbs/OmgwtfnzbsRequestGenerator.cs
+++ b/src/NzbDrone.Core/Indexers/Omgwtfnzbs/OmgwtfnzbsRequestGenerator.cs
@@ -26,6 +26,19 @@ public virtual IndexerPageableRequestChain GetRecentRequests()
return pageableRequests;
}
+ public IndexerPageableRequestChain GetSearchRequests(MovieSearchCriteria searchCriteria)
+ {
+ var pageableRequests = new IndexerPageableRequestChain();
+
+ foreach (var queryTitle in searchCriteria.QueryTitles)
+ {
+ pageableRequests.Add(GetPagedRequests(string.Format("{0}",
+ queryTitle)));
+ }
+
+ return pageableRequests;
+ }
+
private IEnumerable GetPagedRequests(string query)
{
var url = new StringBuilder();
@@ -40,16 +53,6 @@ private IEnumerable GetPagedRequests(string query)
yield return new IndexerRequest(url.ToString(), HttpAccept.Rss);
}
- public IndexerPageableRequestChain GetSearchRequests(MovieSearchCriteria searchCriteria)
- {
- var pageableRequests = new IndexerPageableRequestChain();
-
- pageableRequests.Add(GetPagedRequests(string.Format("{0}",
- searchCriteria.Movie.Title)));
-
- return pageableRequests;
- }
-
public Func> GetCookies { get; set; }
public Action, DateTime?> CookiesUpdater { get; set; }
}
diff --git a/src/NzbDrone.Core/Indexers/PassThePopcorn/PassThePopcornRequestGenerator.cs b/src/NzbDrone.Core/Indexers/PassThePopcorn/PassThePopcornRequestGenerator.cs
index d98339c16..9a93c7673 100644
--- a/src/NzbDrone.Core/Indexers/PassThePopcorn/PassThePopcornRequestGenerator.cs
+++ b/src/NzbDrone.Core/Indexers/PassThePopcorn/PassThePopcornRequestGenerator.cs
@@ -35,7 +35,10 @@ public IndexerPageableRequestChain GetSearchRequests(MovieSearchCriteria searchC
}
else if (searchCriteria.Movie.Year > 0)
{
- pageableRequests.Add(GetRequest(string.Format("{0}&year={1}", searchCriteria.Movie.Title, searchCriteria.Movie.Year)));
+ foreach (var queryTitle in searchCriteria.QueryTitles)
+ {
+ pageableRequests.Add(GetRequest(string.Format("{0}&year={1}", queryTitle, searchCriteria.Movie.Year)));
+ }
}
return pageableRequests;
diff --git a/src/NzbDrone.Core/MediaFiles/MovieImport/Aggregation/Aggregators/AggregateLanguage.cs b/src/NzbDrone.Core/MediaFiles/MovieImport/Aggregation/Aggregators/AggregateLanguage.cs
index 9372cb75c..80dd6c458 100644
--- a/src/NzbDrone.Core/MediaFiles/MovieImport/Aggregation/Aggregators/AggregateLanguage.cs
+++ b/src/NzbDrone.Core/MediaFiles/MovieImport/Aggregation/Aggregators/AggregateLanguage.cs
@@ -25,37 +25,33 @@ public LocalMovie Aggregate(LocalMovie localMovie, bool otherFiles)
languages.AddRange(localMovie.DownloadClientMovieInfo?.Languages ?? new List());
- if (!languages.Any(l => l != Language.English))
+ if (!languages.Any(l => l != Language.Unknown))
{
languages = localMovie.FolderMovieInfo?.Languages ?? new List();
}
- if (!languages.Any(l => l != Language.English))
+ if (!languages.Any(l => l != Language.Unknown))
{
languages = localMovie.FileMovieInfo?.Languages ?? new List();
}
if (!languages.Any())
{
- languages.Add(Language.English);
+ languages.Add(Language.Unknown);
+ }
+
+ languages = languages.Distinct().ToList();
+
+ if (languages.Count == 1 && languages.Contains(Language.Unknown))
+ {
+ languages = new List { localMovie.Movie.OriginalLanguage };
}
_logger.Debug("Using languages: {0}", languages.Select(l => l.Name).ToList().Join(","));
- localMovie.Languages = languages.Distinct().ToList();
+ localMovie.Languages = languages;
return localMovie;
}
-
- private List GetLanguage(ParsedMovieInfo parsedMovieInfo)
- {
- if (parsedMovieInfo == null)
- {
- // English is the default language when otherwise unknown
- return new List { Language.English };
- }
-
- return parsedMovieInfo.Languages;
- }
}
}
diff --git a/src/NzbDrone.Core/MetadataSource/IProvideMovieInfo.cs b/src/NzbDrone.Core/MetadataSource/IProvideMovieInfo.cs
index 923fda3a4..48de600cd 100644
--- a/src/NzbDrone.Core/MetadataSource/IProvideMovieInfo.cs
+++ b/src/NzbDrone.Core/MetadataSource/IProvideMovieInfo.cs
@@ -2,6 +2,7 @@
using System.Collections.Generic;
using NzbDrone.Core.Movies;
using NzbDrone.Core.Movies.Credits;
+using NzbDrone.Core.Movies.Translations;
namespace NzbDrone.Core.MetadataSource
{
diff --git a/src/NzbDrone.Core/MetadataSource/SkyHook/Resource/MovieResource.cs b/src/NzbDrone.Core/MetadataSource/SkyHook/Resource/MovieResource.cs
index 3a737a9e7..aefe90f72 100644
--- a/src/NzbDrone.Core/MetadataSource/SkyHook/Resource/MovieResource.cs
+++ b/src/NzbDrone.Core/MetadataSource/SkyHook/Resource/MovieResource.cs
@@ -9,6 +9,7 @@ public class MovieResource
public string ImdbId { get; set; }
public string Overview { get; set; }
public string Title { get; set; }
+ public string OriginalTitle { get; set; }
public string TitleSlug { get; set; }
public List Ratings { get; set; }
public int? Runtime { get; set; }
diff --git a/src/NzbDrone.Core/MetadataSource/SkyHook/Resource/TranslationResource.cs b/src/NzbDrone.Core/MetadataSource/SkyHook/Resource/TranslationResource.cs
index fb182adfb..686cd3c29 100644
--- a/src/NzbDrone.Core/MetadataSource/SkyHook/Resource/TranslationResource.cs
+++ b/src/NzbDrone.Core/MetadataSource/SkyHook/Resource/TranslationResource.cs
@@ -3,6 +3,7 @@ namespace NzbDrone.Core.MetadataSource.SkyHook.Resource
public class TranslationResource
{
public string Title { get; set; }
+ public string Overview { get; set; }
public string Language { get; set; }
}
}
diff --git a/src/NzbDrone.Core/MetadataSource/SkyHook/SkyHookProxy.cs b/src/NzbDrone.Core/MetadataSource/SkyHook/SkyHookProxy.cs
index 361dc6660..05f883771 100644
--- a/src/NzbDrone.Core/MetadataSource/SkyHook/SkyHookProxy.cs
+++ b/src/NzbDrone.Core/MetadataSource/SkyHook/SkyHookProxy.cs
@@ -15,6 +15,7 @@
using NzbDrone.Core.Movies;
using NzbDrone.Core.Movies.AlternativeTitles;
using NzbDrone.Core.Movies.Credits;
+using NzbDrone.Core.Movies.Translations;
using NzbDrone.Core.NetImport.TMDb;
using NzbDrone.Core.Parser;
@@ -29,11 +30,13 @@ public class SkyHookProxy : IProvideMovieInfo, ISearchForNewMovie
private readonly IHttpRequestBuilderFactory _radarrMetadata;
private readonly IConfigService _configService;
private readonly IMovieService _movieService;
+ private readonly IMovieTranslationService _movieTranslationService;
public SkyHookProxy(IHttpClient httpClient,
IRadarrCloudRequestBuilder requestBuilder,
IConfigService configService,
IMovieService movieService,
+ IMovieTranslationService movieTranslationService,
Logger logger)
{
_httpClient = httpClient;
@@ -41,6 +44,7 @@ public SkyHookProxy(IHttpClient httpClient,
_radarrMetadata = requestBuilder.RadarrMetadata;
_configService = configService;
_movieService = movieService;
+ _movieTranslationService = movieTranslationService;
_logger = logger;
}
@@ -160,6 +164,7 @@ public Movie MapMovie(MovieResource resource)
movie.TmdbId = resource.TmdbId;
movie.ImdbId = resource.ImdbId;
movie.Title = resource.Title;
+ movie.OriginalTitle = resource.OriginalTitle;
movie.TitleSlug = resource.TitleSlug;
movie.CleanTitle = resource.Title.CleanSeriesTitle();
movie.SortTitle = Parser.Parser.NormalizeTitle(resource.Title);
@@ -167,9 +172,14 @@ public Movie MapMovie(MovieResource resource)
movie.AlternativeTitles.AddRange(resource.AlternativeTitles.Select(MapAlternativeTitle));
+ movie.Translations.AddRange(resource.Translations.Select(MapTranslation));
+
+ movie.OriginalLanguage = IsoLanguages.Find(resource.OriginalLanguage.ToLower())?.Language ?? Language.English;
+
movie.Website = resource.Homepage;
movie.InCinemas = resource.InCinema;
movie.PhysicalRelease = resource.PhysicalRelease;
+ movie.DigitalRelease = resource.DigitalRelease;
movie.Year = resource.Year;
@@ -195,44 +205,24 @@ public Movie MapMovie(MovieResource resource)
var now = DateTime.Now;
- //handle the case when we have both theatrical and physical release dates
- if (resource.InCinema.HasValue && resource.PhysicalRelease.HasValue)
- {
- if (now < resource.InCinema)
- {
- movie.Status = MovieStatusType.Announced;
- }
- else if (now >= resource.InCinema)
- {
- movie.Status = MovieStatusType.InCinemas;
- }
+ movie.Status = MovieStatusType.Announced;
- if (now >= resource.PhysicalRelease)
+ if (resource.InCinema.HasValue && now > resource.InCinema)
+ {
+ movie.Status = MovieStatusType.InCinemas;
+
+ if (!resource.PhysicalRelease.HasValue && !resource.DigitalRelease.HasValue && now > resource.InCinema.Value.AddDays(90))
{
movie.Status = MovieStatusType.Released;
}
}
- //handle the case when we have theatrical release dates but we dont know the physical release date
- else if (resource.InCinema.HasValue && (now >= resource.InCinema))
- {
- movie.Status = MovieStatusType.InCinemas;
- }
-
- //handle the case where we only have a physical release date
- else if ((resource.PhysicalRelease.HasValue && (now >= resource.PhysicalRelease)) || (resource.DigitalRelease.HasValue && (now >= resource.DigitalRelease)))
+ if (resource.PhysicalRelease.HasValue && now >= resource.PhysicalRelease)
{
movie.Status = MovieStatusType.Released;
}
- //otherwise the title has only been announced
- else
- {
- movie.Status = MovieStatusType.Announced;
- }
-
- //since TMDB lacks alot of information lets assume that stuff is released if its been in cinemas for longer than 3 months.
- if (!movie.PhysicalRelease.HasValue && (movie.Status == MovieStatusType.InCinemas) && (DateTime.Now.Subtract(movie.InCinemas.Value).TotalSeconds > 60 * 60 * 24 * 30 * 3))
+ if (resource.DigitalRelease.HasValue && now >= resource.DigitalRelease)
{
movie.Status = MovieStatusType.Released;
}
@@ -425,6 +415,10 @@ private Movie MapSearchResult(MovieResource result)
{
movie = MapMovie(result);
}
+ else
+ {
+ movie.Translations = _movieTranslationService.GetAllTranslationsForMovie(movie.Id);
+ }
return movie;
}
@@ -474,6 +468,19 @@ private static AlternativeTitle MapAlternativeTitle(AlternativeTitleResource arg
return newAlternativeTitle;
}
+ private static MovieTranslation MapTranslation(TranslationResource arg)
+ {
+ var newAlternativeTitle = new MovieTranslation
+ {
+ Title = arg.Title,
+ Overview = arg.Overview,
+ CleanTitle = arg.Title.CleanSeriesTitle(),
+ Language = IsoLanguages.Find(arg.Language.ToLower())?.Language
+ };
+
+ return newAlternativeTitle;
+ }
+
private static Ratings MapRatings(RatingResource rating)
{
if (rating == null)
diff --git a/src/NzbDrone.Core/Movies/Movie.cs b/src/NzbDrone.Core/Movies/Movie.cs
index 623b6507f..2b1ba2a80 100644
--- a/src/NzbDrone.Core/Movies/Movie.cs
+++ b/src/NzbDrone.Core/Movies/Movie.cs
@@ -2,8 +2,10 @@
using System.Collections.Generic;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Datastore;
+using NzbDrone.Core.Languages;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.Movies.AlternativeTitles;
+using NzbDrone.Core.Movies.Translations;
using NzbDrone.Core.Profiles;
namespace NzbDrone.Core.Movies
@@ -16,7 +18,9 @@ public Movie()
Genres = new List();
Tags = new HashSet();
AlternativeTitles = new List();
+ Translations = new List();
Recommendations = new List();
+ OriginalLanguage = Language.English;
}
public int TmdbId { get; set; }
@@ -46,7 +50,7 @@ public Movie()
public DateTime Added { get; set; }
public DateTime? InCinemas { get; set; }
public DateTime? PhysicalRelease { get; set; }
- public string PhysicalReleaseNote { get; set; }
+ public DateTime? DigitalRelease { get; set; }
public Profile Profile { get; set; }
public HashSet Tags { get; set; }
public AddMovieOptions AddOptions { get; set; }
@@ -56,10 +60,12 @@ public Movie()
//Get Loaded via a Join Query
public List AlternativeTitles { get; set; }
+ public List Translations { get; set; }
public int? SecondaryYear { get; set; }
- public int SecondaryYearSourceId { get; set; }
public string YouTubeTrailerId { get; set; }
public string Studio { get; set; }
+ public string OriginalTitle { get; set; }
+ public Language OriginalLanguage { get; set; }
public List Recommendations { get; set; }
public bool IsRecentMovie
@@ -122,7 +128,23 @@ public bool IsAvailable(int delay = 0)
case MovieStatusType.Released:
case MovieStatusType.PreDB:
default:
- minimumAvailabilityDate = PhysicalRelease.HasValue ? PhysicalRelease.Value : (InCinemas.HasValue ? InCinemas.Value.AddDays(90) : DateTime.MaxValue);
+ if (PhysicalRelease.HasValue && DigitalRelease.HasValue)
+ {
+ minimumAvailabilityDate = new DateTime(Math.Min(PhysicalRelease.Value.Ticks, DigitalRelease.Value.Ticks));
+ }
+ else if (PhysicalRelease.HasValue)
+ {
+ minimumAvailabilityDate = PhysicalRelease.Value;
+ }
+ else if (DigitalRelease.HasValue)
+ {
+ minimumAvailabilityDate = DigitalRelease.Value;
+ }
+ else
+ {
+ minimumAvailabilityDate = InCinemas.HasValue ? InCinemas.Value.AddDays(90) : DateTime.MaxValue;
+ }
+
break;
}
diff --git a/src/NzbDrone.Core/Movies/RefreshMovieService.cs b/src/NzbDrone.Core/Movies/RefreshMovieService.cs
index a6f16da3f..ef92a9ad7 100644
--- a/src/NzbDrone.Core/Movies/RefreshMovieService.cs
+++ b/src/NzbDrone.Core/Movies/RefreshMovieService.cs
@@ -15,6 +15,7 @@
using NzbDrone.Core.Movies.Commands;
using NzbDrone.Core.Movies.Credits;
using NzbDrone.Core.Movies.Events;
+using NzbDrone.Core.Movies.Translations;
namespace NzbDrone.Core.Movies
{
@@ -22,6 +23,7 @@ public class RefreshMovieService : IExecute
{
private readonly IProvideMovieInfo _movieInfo;
private readonly IMovieService _movieService;
+ private readonly IMovieTranslationService _movieTranslationService;
private readonly IAlternativeTitleService _titleService;
private readonly ICreditService _creditService;
private readonly IEventAggregator _eventAggregator;
@@ -33,6 +35,7 @@ public class RefreshMovieService : IExecute
public RefreshMovieService(IProvideMovieInfo movieInfo,
IMovieService movieService,
+ IMovieTranslationService movieTranslationService,
IAlternativeTitleService titleService,
ICreditService creditService,
IEventAggregator eventAggregator,
@@ -43,6 +46,7 @@ public RefreshMovieService(IProvideMovieInfo movieInfo,
{
_movieInfo = movieInfo;
_movieService = movieService;
+ _movieTranslationService = movieTranslationService;
_titleService = titleService;
_creditService = creditService;
_eventAggregator = eventAggregator;
@@ -104,8 +108,11 @@ private void RefreshMovieInfo(Movie movie)
movie.Year = movieInfo.Year;
movie.SecondaryYear = movieInfo.SecondaryYear;
movie.PhysicalRelease = movieInfo.PhysicalRelease;
+ movie.DigitalRelease = movieInfo.DigitalRelease;
movie.YouTubeTrailerId = movieInfo.YouTubeTrailerId;
movie.Studio = movieInfo.Studio;
+ movie.OriginalTitle = movieInfo.OriginalTitle;
+ movie.OriginalLanguage = movieInfo.OriginalLanguage;
movie.HasPreDBEntry = movieInfo.HasPreDBEntry;
movie.Recommendations = movieInfo.Recommendations;
@@ -120,6 +127,7 @@ private void RefreshMovieInfo(Movie movie)
}
movie.AlternativeTitles = _titleService.UpdateTitles(movieInfo.AlternativeTitles, movie);
+ _movieTranslationService.UpdateTranslations(movieInfo.Translations, movie);
_movieService.UpdateMovie(new List { movie }, true);
_creditService.UpdateCredits(credits, movie);
diff --git a/src/NzbDrone.Core/Movies/Translations/MovieTranslation.cs b/src/NzbDrone.Core/Movies/Translations/MovieTranslation.cs
new file mode 100644
index 000000000..b02d1add9
--- /dev/null
+++ b/src/NzbDrone.Core/Movies/Translations/MovieTranslation.cs
@@ -0,0 +1,14 @@
+using NzbDrone.Core.Datastore;
+using NzbDrone.Core.Languages;
+
+namespace NzbDrone.Core.Movies.Translations
+{
+ public class MovieTranslation : ModelBase
+ {
+ public int MovieId { get; set; }
+ public string Title { get; set; }
+ public string CleanTitle { get; set; }
+ public string Overview { get; set; }
+ public Language Language { get; set; }
+ }
+}
diff --git a/src/NzbDrone.Core/Movies/Translations/MovieTranslationRepository.cs b/src/NzbDrone.Core/Movies/Translations/MovieTranslationRepository.cs
new file mode 100644
index 000000000..a9e2023f2
--- /dev/null
+++ b/src/NzbDrone.Core/Movies/Translations/MovieTranslationRepository.cs
@@ -0,0 +1,37 @@
+using System.Collections.Generic;
+using NzbDrone.Core.Datastore;
+using NzbDrone.Core.Languages;
+using NzbDrone.Core.Messaging.Events;
+
+namespace NzbDrone.Core.Movies.Translations
+{
+ public interface IMovieTranslationRepository : IBasicRepository
+ {
+ List FindByMovieId(int movieId);
+ List FindByLanguage(Language language);
+ void DeleteForMovies(List movieIds);
+ }
+
+ public class MovieTranslationRepository : BasicRepository, IMovieTranslationRepository
+ {
+ public MovieTranslationRepository(IMainDatabase database, IEventAggregator eventAggregator)
+ : base(database, eventAggregator)
+ {
+ }
+
+ public List FindByMovieId(int movieId)
+ {
+ return Query(x => x.MovieId == movieId);
+ }
+
+ public List FindByLanguage(Language language)
+ {
+ return Query(x => x.Language == language);
+ }
+
+ public void DeleteForMovies(List movieIds)
+ {
+ Delete(x => movieIds.Contains(x.MovieId));
+ }
+ }
+}
diff --git a/src/NzbDrone.Core/Movies/Translations/MovieTranslationService.cs b/src/NzbDrone.Core/Movies/Translations/MovieTranslationService.cs
new file mode 100644
index 000000000..bbe9d65d3
--- /dev/null
+++ b/src/NzbDrone.Core/Movies/Translations/MovieTranslationService.cs
@@ -0,0 +1,79 @@
+using System.Collections.Generic;
+using System.Linq;
+using NLog;
+using NzbDrone.Common.Extensions;
+using NzbDrone.Core.Languages;
+using NzbDrone.Core.Messaging.Events;
+using NzbDrone.Core.Movies.Events;
+
+namespace NzbDrone.Core.Movies.Translations
+{
+ public interface IMovieTranslationService
+ {
+ List GetAllTranslationsForMovie(int movieId);
+ List GetAllTranslationsForLanguage(Language language);
+ List UpdateTranslations(List titles, Movie movie);
+ }
+
+ public class MovieTranslationService : IMovieTranslationService, IHandleAsync
+ {
+ private readonly IMovieTranslationRepository _translationRepo;
+ private readonly Logger _logger;
+
+ public MovieTranslationService(IMovieTranslationRepository translationRepo,
+ Logger logger)
+ {
+ _translationRepo = translationRepo;
+ _logger = logger;
+ }
+
+ public List GetAllTranslationsForMovie(int movieId)
+ {
+ return _translationRepo.FindByMovieId(movieId).ToList();
+ }
+
+ public List GetAllTranslationsForLanguage(Language language)
+ {
+ return _translationRepo.FindByLanguage(language).ToList();
+ }
+
+ public void RemoveTitle(MovieTranslation title)
+ {
+ _translationRepo.Delete(title);
+ }
+
+ public List UpdateTranslations(List translations, Movie movie)
+ {
+ int movieId = movie.Id;
+
+ // First update the movie ids so we can correlate them later
+ translations.ForEach(t => t.MovieId = movieId);
+
+ // Then throw out any we don't have languages for
+ translations = translations.Where(t => t.Language != null).ToList();
+
+ // Then make sure they are all distinct languages
+ translations = translations.DistinctBy(t => t.Language).ToList();
+
+ // Now find translations to delete, update and insert
+ var existingTranslations = _translationRepo.FindByMovieId(movieId);
+
+ translations.ForEach(c => c.Id = existingTranslations.FirstOrDefault(t => t.Language == c.Language)?.Id ?? 0);
+
+ var insert = translations.Where(t => t.Id == 0).ToList();
+ var update = translations.Where(t => t.Id > 0).ToList();
+ var delete = existingTranslations.Where(t => !translations.Any(c => c.Language == t.Language)).ToList();
+
+ _translationRepo.DeleteMany(delete.ToList());
+ _translationRepo.UpdateMany(update.ToList());
+ _translationRepo.InsertMany(insert.ToList());
+
+ return translations;
+ }
+
+ public void HandleAsync(MoviesDeletedEvent message)
+ {
+ _translationRepo.DeleteForMovies(message.Movies.Select(m => m.Id).ToList());
+ }
+ }
+}
diff --git a/src/NzbDrone.Core/Organizer/FileNameBuilder.cs b/src/NzbDrone.Core/Organizer/FileNameBuilder.cs
index faa9294cd..7605bc462 100644
--- a/src/NzbDrone.Core/Organizer/FileNameBuilder.cs
+++ b/src/NzbDrone.Core/Organizer/FileNameBuilder.cs
@@ -11,6 +11,9 @@
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.MediaFiles.MediaInfo;
using NzbDrone.Core.Movies;
+using NzbDrone.Core.Movies.AlternativeTitles;
+using NzbDrone.Core.Movies.Translations;
+using NzbDrone.Core.Parser;
using NzbDrone.Core.Qualities;
namespace NzbDrone.Core.Organizer
@@ -30,10 +33,11 @@ public class FileNameBuilder : IBuildFileNames
private readonly INamingConfigService _namingConfigService;
private readonly IQualityDefinitionService _qualityDefinitionService;
private readonly IUpdateMediaInfo _mediaInfoUpdater;
+ private readonly IMovieTranslationService _movieTranslationService;
private readonly ICustomFormatService _formatService;
private readonly Logger _logger;
- private static readonly Regex TitleRegex = new Regex(@"\{(?[- ._\[(]*)(?(?:[a-z0-9]+)(?:(?[- ._]+)(?:[a-z0-9]+))?)(?::(?[a-z0-9]+))?(?[- ._)\]]*)\}",
+ private static readonly Regex TitleRegex = new Regex(@"\{(?[- ._\[(]*)(?(?:[a-z0-9]+)(?:(?[- ._]+)(?:[a-z0-9]+))?)(?::(?[a-z0-9|]+))?(?[- ._)\]]*)\}",
RegexOptions.Compiled | RegexOptions.IgnoreCase);
private static readonly Regex TagsRegex = new Regex(@"(?\{tags(?:\:0+)?})",
@@ -50,7 +54,7 @@ public class FileNameBuilder : IBuildFileNames
public static readonly Regex SeriesTitleRegex = new Regex(@"(?\{(?:Series)(?[- ._])(Clean)?Title\})",
RegexOptions.Compiled | RegexOptions.IgnoreCase);
- public static readonly Regex MovieTitleRegex = new Regex(@"(?\{((?:(Movie|Original))(?[- ._])(Clean)?(Title|Filename)(The)?)\})",
+ public static readonly Regex MovieTitleRegex = new Regex(@"(?\{((?:(Movie|Original))(?[- ._])(Clean)?(Title|Filename)(The)?)(?::(?[a-z0-9]+))?\})",
RegexOptions.Compiled | RegexOptions.IgnoreCase);
private static readonly Regex FileNameCleanupRegex = new Regex(@"([- ._])(\1)+", RegexOptions.Compiled);
@@ -69,12 +73,14 @@ public class FileNameBuilder : IBuildFileNames
public FileNameBuilder(INamingConfigService namingConfigService,
IQualityDefinitionService qualityDefinitionService,
IUpdateMediaInfo mediaInfoUpdater,
+ IMovieTranslationService movieTranslationService,
ICustomFormatService formatService,
Logger logger)
{
_namingConfigService = namingConfigService;
_qualityDefinitionService = qualityDefinitionService;
_mediaInfoUpdater = mediaInfoUpdater;
+ _movieTranslationService = movieTranslationService;
_formatService = formatService;
_logger = logger;
}
@@ -86,7 +92,7 @@ public string BuildFileName(Movie movie, MovieFile movieFile, NamingConfig namin
namingConfig = _namingConfigService.GetConfig();
}
- if (!namingConfig.RenameEpisodes)
+ if (!namingConfig.RenameMovies)
{
return GetOriginalTitle(movieFile);
}
@@ -226,13 +232,40 @@ public static string CleanFolderName(string name)
private void AddMovieTokens(Dictionary> tokenHandlers, Movie movie)
{
- tokenHandlers["{Movie Title}"] = m => movie.Title;
- tokenHandlers["{Movie CleanTitle}"] = m => CleanTitle(movie.Title);
+ tokenHandlers["{Movie Title}"] = m => GetLanguageTitle(movie, m.CustomFormat);
+ tokenHandlers["{Movie CleanTitle}"] = m => CleanTitle(GetLanguageTitle(movie, m.CustomFormat));
tokenHandlers["{Movie Title The}"] = m => TitleThe(movie.Title);
tokenHandlers["{Movie Certification}"] = mbox => movie.Certification;
tokenHandlers["{Movie TitleFirstCharacter}"] = m => TitleThe(movie.Title).Substring(0, 1).FirstCharToUpper();
}
+ private string GetLanguageTitle(Movie movie, string isoCodes)
+ {
+ if (isoCodes.IsNotNullOrWhiteSpace())
+ {
+ foreach (var isoCode in isoCodes.Split('|'))
+ {
+ var language = IsoLanguages.Find(isoCode.ToLower())?.Language;
+
+ if (language == null)
+ {
+ continue;
+ }
+
+ var titles = movie.Translations.Where(t => t.Language == language).ToList();
+
+ if (!movie.Translations.Any())
+ {
+ titles = _movieTranslationService.GetAllTranslationsForMovie(movie.Id).Where(t => t.Language == language).ToList();
+ }
+
+ return titles.FirstOrDefault()?.Title ?? movie.Title;
+ }
+ }
+
+ return movie.Title;
+ }
+
private void AddTagsTokens(Dictionary> tokenHandlers, MovieFile movieFile)
{
if (movieFile.Edition.IsNotNullOrWhiteSpace())
diff --git a/src/NzbDrone.Core/Organizer/NamingConfig.cs b/src/NzbDrone.Core/Organizer/NamingConfig.cs
index 6c9ad3589..b46447a84 100644
--- a/src/NzbDrone.Core/Organizer/NamingConfig.cs
+++ b/src/NzbDrone.Core/Organizer/NamingConfig.cs
@@ -6,15 +6,15 @@ public class NamingConfig : ModelBase
{
public static NamingConfig Default => new NamingConfig
{
- RenameEpisodes = false,
+ RenameMovies = false,
ReplaceIllegalCharacters = true,
ColonReplacementFormat = 0,
MultiEpisodeStyle = 0,
- MovieFolderFormat = "{Movie Title} ({Release Year})",
- StandardMovieFormat = "{Movie Title} ({Release Year}) {Quality Full}",
+ MovieFolderFormat = "{Movie Title:EN} ({Release Year})",
+ StandardMovieFormat = "{Movie Title:EN} ({Release Year}) {Quality Full}",
};
- public bool RenameEpisodes { get; set; }
+ public bool RenameMovies { get; set; }
public bool ReplaceIllegalCharacters { get; set; }
public ColonReplacementFormat ColonReplacementFormat { get; set; }
public int MultiEpisodeStyle { get; set; }
diff --git a/src/NzbDrone.Core/Parser/LanguageParser.cs b/src/NzbDrone.Core/Parser/LanguageParser.cs
index df115c5a0..5fb6f839a 100644
--- a/src/NzbDrone.Core/Parser/LanguageParser.cs
+++ b/src/NzbDrone.Core/Parser/LanguageParser.cs
@@ -14,7 +14,7 @@ public static class LanguageParser
{
private static readonly Logger Logger = NzbDroneLogger.GetLogger(typeof(LanguageParser));
- private static readonly Regex LanguageRegex = new Regex(@"(?:\W|_|^)(?\b(?:ita|italian)\b)|(?german\b|videomann)|(?flemish)|(?greek)|(?(?:\W|_)(?:FR|VOSTFR|VO|VFF|VFQ|VFI|VF2|TRUEFRENCH)(?:\W|_))|(?\brus\b)|(?nl\W?subs?)|(?\b(?:HUNDUB|HUN)\b)|(?\bHebDub\b)",
+ private static readonly Regex LanguageRegex = new Regex(@"(?:\W|_|^)(?\b(?:ita|italian)\b)|(?\b(?:german|videomann|ger)\b)|(?flemish)|(?greek)|(?(?:\W|_)(?:FR|VOSTFR|VO|VFF|VFQ|VFI|VF2|TRUEFRENCH)(?:\W|_))|(?\brus\b)|(?\beng\b)|(?nl\W?subs?)|(?\b(?:HUNDUB|HUN)\b)|(?\bHebDub\b)",
RegexOptions.IgnoreCase | RegexOptions.Compiled);
private static readonly Regex CaseSensitiveLanguageRegex = new Regex(@"(?\bLT\b)|(?\bCZ\b)",
@@ -140,51 +140,59 @@ public static List ParseLanguages(string title)
languages.Add(Language.Czech);
}
- var match = LanguageRegex.Match(title);
+ var matches = LanguageRegex.Matches(title);
- if (match.Groups["italian"].Captures.Cast().Any())
+ foreach (Match match in matches)
{
- languages.Add(Language.Italian);
- }
+ if (match.Groups["italian"].Captures.Cast().Any())
+ {
+ languages.Add(Language.Italian);
+ }
- if (match.Groups["german"].Captures.Cast().Any())
- {
- languages.Add(Language.German);
- }
+ if (match.Groups["german"].Captures.Cast().Any())
+ {
+ languages.Add(Language.German);
+ }
- if (match.Groups["flemish"].Captures.Cast().Any())
- {
- languages.Add(Language.Flemish);
- }
+ if (match.Groups["flemish"].Captures.Cast().Any())
+ {
+ languages.Add(Language.Flemish);
+ }
- if (match.Groups["greek"].Captures.Cast().Any())
- {
- languages.Add(Language.Greek);
- }
+ if (match.Groups["greek"].Captures.Cast().Any())
+ {
+ languages.Add(Language.Greek);
+ }
- if (match.Groups["french"].Success)
- {
- languages.Add(Language.French);
- }
+ if (match.Groups["french"].Success)
+ {
+ languages.Add(Language.French);
+ }
- if (match.Groups["russian"].Success)
- {
- languages.Add(Language.Russian);
- }
+ if (match.Groups["russian"].Success)
+ {
+ languages.Add(Language.Russian);
+ }
- if (match.Groups["dutch"].Success)
- {
- languages.Add(Language.Dutch);
- }
+ if (match.Groups["english"].Success)
+ {
+ languages.Add(Language.English);
+ }
- if (match.Groups["hungarian"].Success)
- {
- languages.Add(Language.Hungarian);
- }
+ if (match.Groups["dutch"].Success)
+ {
+ languages.Add(Language.Dutch);
+ }
- if (match.Groups["hebrew"].Success)
- {
- languages.Add(Language.Hebrew);
+ if (match.Groups["hungarian"].Success)
+ {
+ languages.Add(Language.Hungarian);
+ }
+
+ if (match.Groups["hebrew"].Success)
+ {
+ languages.Add(Language.Hebrew);
+ }
}
if (title.ToLower().Contains("multi"))
@@ -198,7 +206,7 @@ public static List ParseLanguages(string title)
if (!languages.Any())
{
- languages.Add(Language.English);
+ languages.Add(Language.Unknown);
}
return languages.DistinctBy(l => (int)l).ToList();
diff --git a/src/NzbDrone.Core/Parser/ParsingService.cs b/src/NzbDrone.Core/Parser/ParsingService.cs
index 17f834c41..5eeacd164 100644
--- a/src/NzbDrone.Core/Parser/ParsingService.cs
+++ b/src/NzbDrone.Core/Parser/ParsingService.cs
@@ -6,6 +6,7 @@
using NzbDrone.Core.Configuration;
using NzbDrone.Core.DecisionEngine;
using NzbDrone.Core.IndexerSearch.Definitions;
+using NzbDrone.Core.Languages;
using NzbDrone.Core.Movies;
using NzbDrone.Core.Movies.AlternativeTitles;
using NzbDrone.Core.Parser.Augmenters;
@@ -141,6 +142,12 @@ public MappingResult Map(ParsedMovieInfo parsedMovieInfo, string imdbId, SearchC
result.Movie = null;
}
+ //Use movie language as fallback if we could't parse a language (more accurate than just using English)
+ if (parsedMovieInfo.Languages.Count <= 1 && parsedMovieInfo.Languages.First() == Language.Unknown && result.Movie != null)
+ {
+ parsedMovieInfo.Languages = new List { result.Movie.OriginalLanguage };
+ }
+
result.RemoteMovie.ParsedMovieInfo = parsedMovieInfo;
return result;
diff --git a/src/NzbDrone.Core/Profiles/ProfileService.cs b/src/NzbDrone.Core/Profiles/ProfileService.cs
index 4ce4618c3..fc8c3a5fb 100644
--- a/src/NzbDrone.Core/Profiles/ProfileService.cs
+++ b/src/NzbDrone.Core/Profiles/ProfileService.cs
@@ -21,6 +21,7 @@ public interface IProfileService
Profile Get(int id);
bool Exists(int id);
Profile GetDefaultProfile(string name, Quality cutoff = null, params Quality[] allowed);
+ List GetAcceptableLanguages(int profileId);
}
public class ProfileService : IProfileService,
@@ -265,6 +266,24 @@ public Profile GetDefaultProfile(string name, Quality cutoff = null, params Qual
return qualityProfile;
}
+ public List GetAcceptableLanguages(int profileId)
+ {
+ var profile = Get(profileId);
+
+ var wantedTitleLanguages = profile.FormatItems.Where(i => i.Score > 0).Select(item => item.Format)
+ .SelectMany(format => format.Specifications)
+ .Where(specification => specification is LanguageSpecification && !specification.Negate)
+ .Cast()
+ .Where(specification => specification.Value > 0)
+ .Select(specification => (Language)specification.Value)
+ .Distinct()
+ .ToList();
+
+ wantedTitleLanguages.Add(profile.Language);
+
+ return wantedTitleLanguages;
+ }
+
private Profile AddDefaultProfile(string name, Quality cutoff, params Quality[] allowed)
{
var profile = GetDefaultProfile(name, cutoff, allowed);
diff --git a/src/Radarr.Api.V3/Calendar/CalendarFeedModule.cs b/src/Radarr.Api.V3/Calendar/CalendarFeedModule.cs
index 516c53830..d46cd9957 100644
--- a/src/Radarr.Api.V3/Calendar/CalendarFeedModule.cs
+++ b/src/Radarr.Api.V3/Calendar/CalendarFeedModule.cs
@@ -123,9 +123,7 @@ private void CreateEvent(Ical.Net.Calendar calendar, Movie movie, bool cinemasRe
occurrence.Description = movie.Overview;
occurrence.Categories = new List() { movie.Studio };
- var physicalText = movie.PhysicalReleaseNote.IsNotNullOrWhiteSpace()
- ? $"(Physical Release, {movie.PhysicalReleaseNote})"
- : "(Physical Release)";
+ var physicalText = "(Physical Release)";
occurrence.Summary = $"{movie.Title} " + (cinemasRelease ? "(Theatrical Release)" : physicalText);
}
}
diff --git a/src/Radarr.Api.V3/Calendar/CalendarModule.cs b/src/Radarr.Api.V3/Calendar/CalendarModule.cs
index c27f50b60..b8021a6cb 100644
--- a/src/Radarr.Api.V3/Calendar/CalendarModule.cs
+++ b/src/Radarr.Api.V3/Calendar/CalendarModule.cs
@@ -2,8 +2,11 @@
using System.Collections.Generic;
using System.Linq;
using Nancy;
+using NzbDrone.Core.Configuration;
using NzbDrone.Core.DecisionEngine.Specifications;
+using NzbDrone.Core.Languages;
using NzbDrone.Core.Movies;
+using NzbDrone.Core.Movies.Translations;
using NzbDrone.SignalR;
using Radarr.Api.V3.Movies;
using Radarr.Http;
@@ -13,15 +16,21 @@ namespace Radarr.Api.V3.Calendar
public class CalendarModule : RadarrRestModuleWithSignalR
{
private readonly IMovieService _moviesService;
+ private readonly IMovieTranslationService _movieTranslationService;
private readonly IUpgradableSpecification _qualityUpgradableSpecification;
+ private readonly IConfigService _configService;
public CalendarModule(IBroadcastSignalRMessage signalR,
IMovieService moviesService,
- IUpgradableSpecification qualityUpgradableSpecification)
+ IMovieTranslationService movieTranslationService,
+ IUpgradableSpecification qualityUpgradableSpecification,
+ IConfigService configService)
: base(signalR, "calendar")
{
_moviesService = moviesService;
+ _movieTranslationService = movieTranslationService;
_qualityUpgradableSpecification = qualityUpgradableSpecification;
+ _configService = configService;
GetResourceAll = GetCalendar;
}
@@ -63,7 +72,8 @@ protected MovieResource MapToResource(Movie movie)
return null;
}
- var resource = movie.ToResource(_qualityUpgradableSpecification);
+ var translation = _movieTranslationService.GetAllTranslationsForMovie(movie.Id).FirstOrDefault(t => t.Language == (Language)_configService.MovieInfoLanguage);
+ var resource = movie.ToResource(_qualityUpgradableSpecification, translation);
return resource;
}
diff --git a/src/Radarr.Api.V3/Config/NamingExampleResource.cs b/src/Radarr.Api.V3/Config/NamingExampleResource.cs
index dd57df6b7..597a6e2bb 100644
--- a/src/Radarr.Api.V3/Config/NamingExampleResource.cs
+++ b/src/Radarr.Api.V3/Config/NamingExampleResource.cs
@@ -16,7 +16,7 @@ public static NamingConfigResource ToResource(this NamingConfig model)
{
Id = model.Id,
- RenameMovies = model.RenameEpisodes,
+ RenameMovies = model.RenameMovies,
ReplaceIllegalCharacters = model.ReplaceIllegalCharacters,
ColonReplacementFormat = model.ColonReplacementFormat,
StandardMovieFormat = model.StandardMovieFormat,
@@ -43,7 +43,7 @@ public static NamingConfig ToModel(this NamingConfigResource resource)
{
Id = resource.Id,
- RenameEpisodes = resource.RenameMovies,
+ RenameMovies = resource.RenameMovies,
ReplaceIllegalCharacters = resource.ReplaceIllegalCharacters,
ColonReplacementFormat = resource.ColonReplacementFormat,
StandardMovieFormat = resource.StandardMovieFormat,
diff --git a/src/Radarr.Api.V3/Config/UiConfigResource.cs b/src/Radarr.Api.V3/Config/UiConfigResource.cs
index ed074313c..723a5c13e 100644
--- a/src/Radarr.Api.V3/Config/UiConfigResource.cs
+++ b/src/Radarr.Api.V3/Config/UiConfigResource.cs
@@ -16,6 +16,7 @@ public class UiConfigResource : RestResource
public bool ShowRelativeDates { get; set; }
public bool EnableColorImpairedMode { get; set; }
+ public int MovieInfoLanguage { get; set; }
}
public static class UiConfigResourceMapper
@@ -33,6 +34,7 @@ public static UiConfigResource ToResource(IConfigService model)
ShowRelativeDates = model.ShowRelativeDates,
EnableColorImpairedMode = model.EnableColorImpairedMode,
+ MovieInfoLanguage = model.MovieInfoLanguage
};
}
}
diff --git a/src/Radarr.Api.V3/ManualImport/ManualImportResource.cs b/src/Radarr.Api.V3/ManualImport/ManualImportResource.cs
index 5919596cc..4ebace8c5 100644
--- a/src/Radarr.Api.V3/ManualImport/ManualImportResource.cs
+++ b/src/Radarr.Api.V3/ManualImport/ManualImportResource.cs
@@ -39,8 +39,7 @@ public static ManualImportResource ToResource(this ManualImportItem model)
Id = HashConverter.GetHashInt31(model.Path),
Path = model.Path,
RelativePath = model.RelativePath,
-
- // FolderName = model.FolderName,
+ FolderName = model.FolderName,
Name = model.Name,
Size = model.Size,
Movie = model.Movie.ToResource(),
diff --git a/src/Radarr.Api.V3/Movies/MovieLookupModule.cs b/src/Radarr.Api.V3/Movies/MovieLookupModule.cs
index 48aec8255..d175cf3c6 100644
--- a/src/Radarr.Api.V3/Movies/MovieLookupModule.cs
+++ b/src/Radarr.Api.V3/Movies/MovieLookupModule.cs
@@ -1,6 +1,8 @@
using System.Collections.Generic;
using System.Linq;
using Nancy;
+using NzbDrone.Core.Configuration;
+using NzbDrone.Core.Languages;
using NzbDrone.Core.MediaCover;
using NzbDrone.Core.MetadataSource;
using NzbDrone.Core.Movies;
@@ -15,13 +17,18 @@ public class MovieLookupModule : RadarrRestModule
private readonly ISearchForNewMovie _searchProxy;
private readonly IProvideMovieInfo _movieInfo;
private readonly IBuildFileNames _fileNameBuilder;
+ private readonly IConfigService _configService;
- public MovieLookupModule(ISearchForNewMovie searchProxy, IProvideMovieInfo movieInfo, IBuildFileNames fileNameBuilder)
+ public MovieLookupModule(ISearchForNewMovie searchProxy,
+ IProvideMovieInfo movieInfo,
+ IBuildFileNames fileNameBuilder,
+ IConfigService configService)
: base("/movie/lookup")
{
_movieInfo = movieInfo;
_searchProxy = searchProxy;
_fileNameBuilder = fileNameBuilder;
+ _configService = configService;
Get("/", x => Search());
Get("/tmdb", x => SearchByTmdbId());
Get("/imdb", x => SearchByImdbId());
@@ -33,7 +40,8 @@ private object SearchByTmdbId()
if (int.TryParse(Request.Query.tmdbId, out tmdbId))
{
var result = _movieInfo.GetMovieInfo(tmdbId).Item1;
- return result.ToResource();
+ var translation = result.Translations.FirstOrDefault(t => t.Language == (Language)_configService.MovieInfoLanguage);
+ return result.ToResource(translation);
}
throw new BadRequestException("Tmdb Id was not valid");
@@ -43,20 +51,24 @@ private object SearchByImdbId()
{
string imdbId = Request.Query.imdbId;
var result = _movieInfo.GetMovieByImdbId(imdbId);
- return result.ToResource();
+
+ var translation = result.Translations.FirstOrDefault(t => t.Language == (Language)_configService.MovieInfoLanguage);
+ return result.ToResource(translation);
}
private object Search()
{
- var imdbResults = _searchProxy.SearchForNewMovie((string)Request.Query.term);
- return MapToResource(imdbResults);
+ var searchResults = _searchProxy.SearchForNewMovie((string)Request.Query.term);
+
+ return MapToResource(searchResults);
}
private IEnumerable MapToResource(IEnumerable movies)
{
foreach (var currentMovie in movies)
{
- var resource = currentMovie.ToResource();
+ var translation = currentMovie.Translations.FirstOrDefault(t => t.Language == (Language)_configService.MovieInfoLanguage);
+ var resource = currentMovie.ToResource(translation);
var poster = currentMovie.Images.FirstOrDefault(c => c.CoverType == MediaCoverTypes.Poster);
if (poster != null)
{
diff --git a/src/Radarr.Api.V3/Movies/MovieModule.cs b/src/Radarr.Api.V3/Movies/MovieModule.cs
index 7e311aa59..9d0997cba 100644
--- a/src/Radarr.Api.V3/Movies/MovieModule.cs
+++ b/src/Radarr.Api.V3/Movies/MovieModule.cs
@@ -1,9 +1,12 @@
using System.Collections.Generic;
+using System.Linq;
using FluentValidation;
using Nancy;
using NzbDrone.Common.Extensions;
+using NzbDrone.Core.Configuration;
using NzbDrone.Core.Datastore.Events;
using NzbDrone.Core.DecisionEngine.Specifications;
+using NzbDrone.Core.Languages;
using NzbDrone.Core.MediaCover;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.MediaFiles.Events;
@@ -12,6 +15,7 @@
using NzbDrone.Core.Movies;
using NzbDrone.Core.Movies.Commands;
using NzbDrone.Core.Movies.Events;
+using NzbDrone.Core.Movies.Translations;
using NzbDrone.Core.Validation;
using NzbDrone.Core.Validation.Paths;
using NzbDrone.SignalR;
@@ -30,17 +34,21 @@ public class MovieModule : RadarrRestModuleWithSignalR,
IHandle
{
private readonly IMovieService _moviesService;
+ private readonly IMovieTranslationService _movieTranslationService;
private readonly IAddMovieService _addMovieService;
private readonly IMapCoversToLocal _coverMapper;
private readonly IManageCommandQueue _commandQueueManager;
private readonly IUpgradableSpecification _qualityUpgradableSpecification;
+ private readonly IConfigService _configService;
public MovieModule(IBroadcastSignalRMessage signalRBroadcaster,
IMovieService moviesService,
+ IMovieTranslationService movieTranslationService,
IAddMovieService addMovieService,
IMapCoversToLocal coverMapper,
IManageCommandQueue commandQueueManager,
IUpgradableSpecification qualityUpgradableSpecification,
+ IConfigService configService,
RootFolderValidator rootFolderValidator,
MappedNetworkDriveValidator mappedNetworkDriveValidator,
MoviePathValidator moviesPathValidator,
@@ -52,8 +60,10 @@ public MovieModule(IBroadcastSignalRMessage signalRBroadcaster,
: base(signalRBroadcaster)
{
_moviesService = moviesService;
+ _movieTranslationService = movieTranslationService;
_addMovieService = addMovieService;
_qualityUpgradableSpecification = qualityUpgradableSpecification;
+ _configService = configService;
_coverMapper = coverMapper;
_commandQueueManager = commandQueueManager;
@@ -95,33 +105,44 @@ private List AllMovie()
if (tmdbId > 0)
{
- moviesResources.AddIfNotNull(_moviesService.FindByTmdbId(tmdbId).ToResource(_qualityUpgradableSpecification));
+ var movie = _moviesService.FindByTmdbId(tmdbId);
+ var translation = _movieTranslationService.GetAllTranslationsForMovie(movie.Id).Where(t => t.Language == (Language)_configService.MovieInfoLanguage).FirstOrDefault();
+
+ moviesResources.AddIfNotNull(movie.ToResource(_qualityUpgradableSpecification, translation));
}
else
{
- moviesResources.AddRange(_moviesService.GetAllMovies().ToResource(_qualityUpgradableSpecification));
+ var translations = _movieTranslationService.GetAllTranslationsForLanguage((Language)_configService.MovieInfoLanguage);
+ var movies = _moviesService.GetAllMovies();
+
+ foreach (var movie in movies)
+ {
+ var translation = translations.FirstOrDefault(t => t.MovieId == movie.Id);
+ moviesResources.Add(movie.ToResource(_qualityUpgradableSpecification, translation));
+ }
}
MapCoversToLocal(moviesResources.ToArray());
- PopulateAlternateTitles(moviesResources);
return moviesResources;
}
private MovieResource GetMovie(int id)
{
- var movies = _moviesService.GetMovie(id);
- return MapToResource(movies);
+ var movie = _moviesService.GetMovie(id);
+ return MapToResource(movie);
}
- protected MovieResource MapToResource(Movie movies)
+ protected MovieResource MapToResource(Movie movie)
{
- if (movies == null)
+ if (movie == null)
{
return null;
}
- var resource = movies.ToResource();
+ var translation = _movieTranslationService.GetAllTranslationsForMovie(movie.Id).FirstOrDefault(t => t.Language == (Language)_configService.MovieInfoLanguage);
+
+ var resource = movie.ToResource(_qualityUpgradableSpecification, translation);
MapCoversToLocal(resource);
return resource;
@@ -155,9 +176,10 @@ private void UpdateMovie(MovieResource moviesResource)
var model = moviesResource.ToModel(movie);
- _moviesService.UpdateMovie(model);
+ var updatedMovie = _moviesService.UpdateMovie(model);
+ var translation = _movieTranslationService.GetAllTranslationsForMovie(updatedMovie.Id).FirstOrDefault(t => t.Language == (Language)_configService.MovieInfoLanguage);
- BroadcastResourceChange(ModelAction.Updated, moviesResource);
+ BroadcastResourceChange(ModelAction.Updated, updatedMovie.ToResource(_qualityUpgradableSpecification, translation));
}
private void DeleteMovie(int id)
@@ -176,28 +198,10 @@ private void MapCoversToLocal(params MovieResource[] movies)
}
}
- private void PopulateAlternateTitles(List resources)
- {
- foreach (var resource in resources)
- {
- PopulateAlternateTitles(resource);
- }
- }
-
- private void PopulateAlternateTitles(MovieResource resource)
- {
- //var mappings = null;//_sceneMappingService.FindByTvdbId(resource.TvdbId);
-
- //if (mappings == null) return;
-
- //Not necessary anymore
-
- //resource.AlternateTitles = mappings.Select(v => new AlternateTitleResource { Title = v.Title, SeasonNumber = v.SeasonNumber, SceneSeasonNumber = v.SceneSeasonNumber }).ToList();
- }
-
public void Handle(MovieImportedEvent message)
{
- BroadcastResourceChange(ModelAction.Updated, message.ImportedMovie.MovieId);
+ var translation = _movieTranslationService.GetAllTranslationsForMovie(message.ImportedMovie.MovieId).FirstOrDefault(t => t.Language == (Language)_configService.MovieInfoLanguage);
+ BroadcastResourceChange(ModelAction.Updated, message.ImportedMovie.Movie.ToResource(_qualityUpgradableSpecification, translation));
}
public void Handle(MovieFileDeletedEvent message)
@@ -212,12 +216,14 @@ public void Handle(MovieFileDeletedEvent message)
public void Handle(MovieUpdatedEvent message)
{
- BroadcastResourceChange(ModelAction.Updated, message.Movie.Id);
+ var translation = _movieTranslationService.GetAllTranslationsForMovie(message.Movie.Id).FirstOrDefault(t => t.Language == (Language)_configService.MovieInfoLanguage);
+ BroadcastResourceChange(ModelAction.Updated, message.Movie.ToResource(_qualityUpgradableSpecification, translation));
}
public void Handle(MovieEditedEvent message)
{
- BroadcastResourceChange(ModelAction.Updated, message.Movie.Id);
+ var translation = _movieTranslationService.GetAllTranslationsForMovie(message.Movie.Id).FirstOrDefault(t => t.Language == (Language)_configService.MovieInfoLanguage);
+ BroadcastResourceChange(ModelAction.Updated, message.Movie.ToResource(_qualityUpgradableSpecification, translation));
}
public void Handle(MoviesDeletedEvent message)
@@ -230,7 +236,8 @@ public void Handle(MoviesDeletedEvent message)
public void Handle(MovieRenamedEvent message)
{
- BroadcastResourceChange(ModelAction.Updated, message.Movie.Id);
+ var translation = _movieTranslationService.GetAllTranslationsForMovie(message.Movie.Id).FirstOrDefault(t => t.Language == (Language)_configService.MovieInfoLanguage);
+ BroadcastResourceChange(ModelAction.Updated, message.Movie.ToResource(_qualityUpgradableSpecification, translation));
}
public void Handle(MediaCoversUpdatedEvent message)
diff --git a/src/Radarr.Api.V3/Movies/MovieResource.cs b/src/Radarr.Api.V3/Movies/MovieResource.cs
index 393a09f6d..dcb7f750f 100644
--- a/src/Radarr.Api.V3/Movies/MovieResource.cs
+++ b/src/Radarr.Api.V3/Movies/MovieResource.cs
@@ -2,8 +2,11 @@
using System.Collections.Generic;
using System.Linq;
using NzbDrone.Core.DecisionEngine.Specifications;
+using NzbDrone.Core.Languages;
using NzbDrone.Core.MediaCover;
using NzbDrone.Core.Movies;
+using NzbDrone.Core.Movies.Translations;
+using NzbDrone.Core.Parser;
using Radarr.Api.V3.MovieFiles;
using Radarr.Http.REST;
@@ -22,6 +25,7 @@ public MovieResource()
//View Only
public string Title { get; set; }
+ public string OriginalTitle { get; set; }
public List AlternateTitles { get; set; }
public int? SecondaryYear { get; set; }
public int SecondaryYearSourceId { get; set; }
@@ -86,10 +90,10 @@ public static MovieResource ToResource(this Movie model)
Id = model.Id,
TmdbId = model.TmdbId,
Title = model.Title,
+ OriginalTitle = model.Title,
SortTitle = model.SortTitle,
InCinemas = model.InCinemas,
PhysicalRelease = model.PhysicalRelease,
- PhysicalReleaseNote = model.PhysicalReleaseNote,
HasFile = model.HasFile,
SizeOnDisk = size,
@@ -100,7 +104,6 @@ public static MovieResource ToResource(this Movie model)
Year = model.Year,
SecondaryYear = model.SecondaryYear,
- SecondaryYearSourceId = model.SecondaryYearSourceId,
Path = model.Path,
QualityProfileId = model.ProfileId,
@@ -131,7 +134,69 @@ public static MovieResource ToResource(this Movie model)
};
}
- public static MovieResource ToResource(this Movie model, IUpgradableSpecification upgradableSpecification)
+ public static MovieResource ToResource(this Movie model, MovieTranslation movieTranslation)
+ {
+ if (model == null)
+ {
+ return null;
+ }
+
+ long size = model.MovieFile?.Size ?? 0;
+ MovieFileResource movieFile = model.MovieFile?.ToResource(model);
+
+ var translatedTitle = movieTranslation?.Title ?? model.Title;
+ var translatedOverview = movieTranslation?.Overview ?? model.Overview;
+
+ return new MovieResource
+ {
+ Id = model.Id,
+ TmdbId = model.TmdbId,
+ Title = translatedTitle,
+ OriginalTitle = model.Title,
+ SortTitle = translatedTitle.NormalizeTitle(),
+ InCinemas = model.InCinemas,
+ PhysicalRelease = model.PhysicalRelease,
+ HasFile = model.HasFile,
+
+ SizeOnDisk = size,
+ Status = model.Status,
+ Overview = translatedOverview,
+
+ Images = model.Images,
+
+ Year = model.Year,
+ SecondaryYear = model.SecondaryYear,
+
+ Path = model.Path,
+ QualityProfileId = model.ProfileId,
+
+ Monitored = model.Monitored,
+ MinimumAvailability = model.MinimumAvailability,
+
+ IsAvailable = model.IsAvailable(),
+ FolderName = model.FolderName(),
+
+ Runtime = model.Runtime,
+ CleanTitle = model.CleanTitle,
+ ImdbId = model.ImdbId,
+ TitleSlug = model.TitleSlug,
+ RootFolderPath = model.RootFolderPath,
+ Certification = model.Certification,
+ Website = model.Website,
+ Genres = model.Genres,
+ Tags = model.Tags,
+ Added = model.Added,
+ AddOptions = model.AddOptions,
+ AlternateTitles = model.AlternativeTitles.ToResource(),
+ Ratings = model.Ratings,
+ MovieFile = movieFile,
+ YouTubeTrailerId = model.YouTubeTrailerId,
+ Studio = model.Studio,
+ Collection = model.Collection
+ };
+ }
+
+ public static MovieResource ToResource(this Movie model, IUpgradableSpecification upgradableSpecification, MovieTranslation movieTranslation)
{
if (model == null)
{
@@ -141,26 +206,27 @@ public static MovieResource ToResource(this Movie model, IUpgradableSpecificatio
long size = model.MovieFile?.Size ?? 0;
MovieFileResource movieFile = model.MovieFile?.ToResource(model, upgradableSpecification);
+ var translatedTitle = movieTranslation?.Title ?? model.Title;
+ var translatedOverview = movieTranslation?.Overview ?? model.Overview;
+
return new MovieResource
{
Id = model.Id,
TmdbId = model.TmdbId,
- Title = model.Title,
- SortTitle = model.SortTitle,
+ Title = translatedTitle,
+ SortTitle = translatedTitle.NormalizeTitle(),
InCinemas = model.InCinemas,
PhysicalRelease = model.PhysicalRelease,
- PhysicalReleaseNote = model.PhysicalReleaseNote,
HasFile = model.HasFile,
SizeOnDisk = size,
Status = model.Status,
- Overview = model.Overview,
+ Overview = translatedOverview,
Images = model.Images,
Year = model.Year,
SecondaryYear = model.SecondaryYear,
- SecondaryYearSourceId = model.SecondaryYearSourceId,
Path = model.Path,
QualityProfileId = model.ProfileId,
@@ -207,7 +273,6 @@ public static Movie ToModel(this MovieResource resource)
SortTitle = resource.SortTitle,
InCinemas = resource.InCinemas,
PhysicalRelease = resource.PhysicalRelease,
- PhysicalReleaseNote = resource.PhysicalReleaseNote,
Overview = resource.Overview,
@@ -215,7 +280,6 @@ public static Movie ToModel(this MovieResource resource)
Year = resource.Year,
SecondaryYear = resource.SecondaryYear,
- SecondaryYearSourceId = resource.SecondaryYearSourceId,
Path = resource.Path,
ProfileId = resource.QualityProfileId,
@@ -254,11 +318,6 @@ public static List ToResource(this IEnumerable movies)
return movies.Select(ToResource).ToList();
}
- public static List ToResource(this IEnumerable movies, IUpgradableSpecification upgradableSpecification)
- {
- return movies.ToList().ConvertAll(f => f.ToResource(upgradableSpecification));
- }
-
public static List ToModel(this IEnumerable resources)
{
return resources.Select(ToModel).ToList();