diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs
index 9c096916f..14723c0a7 100644
--- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs
+++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs
@@ -291,6 +291,7 @@ namespace Emby.Server.Implementations.Data
AddColumn(db, "TypedBaseItems", "Artists", "Text", existingColumnNames);
AddColumn(db, "TypedBaseItems", "AlbumArtists", "Text", existingColumnNames);
AddColumn(db, "TypedBaseItems", "ExternalId", "Text", existingColumnNames);
+ AddColumn(db, "TypedBaseItems", "SeriesPresentationUniqueKey", "Text", existingColumnNames);
existingColumnNames = GetColumnNames(db, "ItemValues");
AddColumn(db, "ItemValues", "CleanValue", "Text", existingColumnNames);
@@ -341,6 +342,7 @@ namespace Emby.Server.Implementations.Data
"drop index if exists Idx_ProviderIds1",
"drop table if exists Images",
"drop index if exists idx_Images",
+ "drop index if exists idx_TypeSeriesPresentationUniqueKey",
"create index if not exists idx_PathTypedBaseItems on TypedBaseItems(Path)",
"create index if not exists idx_ParentIdTypedBaseItems on TypedBaseItems(ParentId)",
@@ -353,6 +355,9 @@ namespace Emby.Server.Implementations.Data
// covering index
"create index if not exists idx_TopParentIdGuid on TypedBaseItems(TopParentId,Guid)",
+ // series
+ "create index if not exists idx_TypeSeriesPresentationUniqueKey1 on TypedBaseItems(Type,SeriesPresentationUniqueKey,PresentationUniqueKey,SortName)",
+
// live tv programs
"create index if not exists idx_TypeTopParentIdStartDate on TypedBaseItems(Type,TopParentId,StartDate)",
@@ -488,7 +493,8 @@ namespace Emby.Server.Implementations.Data
"ExtraType",
"Artists",
"AlbumArtists",
- "ExternalId"
+ "ExternalId",
+ "SeriesPresentationUniqueKey"
};
private readonly string[] _mediaStreamSaveColumns =
@@ -619,7 +625,8 @@ namespace Emby.Server.Implementations.Data
"ExtraType",
"Artists",
"AlbumArtists",
- "ExternalId"
+ "ExternalId",
+ "SeriesPresentationUniqueKey"
};
var saveItemCommandCommandText = "replace into TypedBaseItems (" + string.Join(",", saveColumns.ToArray()) + ") values (";
@@ -1024,11 +1031,13 @@ namespace Emby.Server.Implementations.Data
{
saveItemStatement.TryBind("@SeriesId", hasSeries.SeriesId);
saveItemStatement.TryBind("@SeriesSortName", hasSeries.SeriesSortName);
+ saveItemStatement.TryBind("@SeriesPresentationUniqueKey", hasSeries.SeriesPresentationUniqueKey);
}
else
{
saveItemStatement.TryBindNull("@SeriesId");
saveItemStatement.TryBindNull("@SeriesSortName");
+ saveItemStatement.TryBindNull("@SeriesPresentationUniqueKey");
}
saveItemStatement.TryBind("@ExternalSeriesId", item.ExternalSeriesId);
@@ -1983,6 +1992,15 @@ namespace Emby.Server.Implementations.Data
}
index++;
+ if (hasSeries != null)
+ {
+ if (!reader.IsDBNull(index))
+ {
+ hasSeries.SeriesPresentationUniqueKey = reader.GetString(index);
+ }
+ }
+ index++;
+
if (string.IsNullOrWhiteSpace(item.Tagline))
{
var movie = item as Movie;
@@ -4292,6 +4310,16 @@ namespace Emby.Server.Implementations.Data
}
}
+ if (!string.IsNullOrWhiteSpace(query.SeriesPresentationUniqueKey))
+ {
+ whereClauses.Add("SeriesPresentationUniqueKey=@SeriesPresentationUniqueKey");
+
+ if (statement != null)
+ {
+ statement.TryBind("@SeriesPresentationUniqueKey", query.SeriesPresentationUniqueKey);
+ }
+ }
+
if (query.BlockUnratedItems.Length == 1)
{
whereClauses.Add("(InheritedParentalRatingValue > 0 or UnratedType <> @UnratedType)");
diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs
index 8fb28fb59..d96756ce1 100644
--- a/Emby.Server.Implementations/Library/LibraryManager.cs
+++ b/Emby.Server.Implementations/Library/LibraryManager.cs
@@ -1057,6 +1057,12 @@ namespace Emby.Server.Implementations.Library
try
{
await PerformLibraryValidation(progress, cancellationToken).ConfigureAwait(false);
+
+ if (!ConfigurationManager.Configuration.EnableSeriesPresentationUniqueKey)
+ {
+ ConfigurationManager.Configuration.EnableSeriesPresentationUniqueKey = true;
+ ConfigurationManager.SaveConfiguration();
+ }
}
finally
{
@@ -1478,8 +1484,9 @@ namespace Emby.Server.Implementations.Library
!query.ParentId.HasValue &&
query.ChannelIds.Length == 0 &&
query.TopParentIds.Length == 0 &&
- string.IsNullOrWhiteSpace(query.AncestorWithPresentationUniqueKey)
- && query.ItemIds.Length == 0)
+ string.IsNullOrWhiteSpace(query.AncestorWithPresentationUniqueKey) &&
+ string.IsNullOrWhiteSpace(query.SeriesPresentationUniqueKey) &&
+ query.ItemIds.Length == 0)
{
var userViews = _userviewManager().GetUserViews(new UserViewQuery
{
diff --git a/Emby.Server.Implementations/TV/TVSeriesManager.cs b/Emby.Server.Implementations/TV/TVSeriesManager.cs
index 56e729995..4f876f6a3 100644
--- a/Emby.Server.Implementations/TV/TVSeriesManager.cs
+++ b/Emby.Server.Implementations/TV/TVSeriesManager.cs
@@ -158,9 +158,13 @@ namespace Emby.Server.Implementations.TV
/// Task{Episode}.
private Tuple> GetNextUp(Series series, User user)
{
+ var enableSeriesPresentationKey = _config.Configuration.EnableSeriesPresentationUniqueKey;
+ var seriesKey = GetUniqueSeriesKey(series);
+
var lastWatchedEpisode = _libraryManager.GetItemList(new InternalItemsQuery(user)
{
- AncestorWithPresentationUniqueKey = GetUniqueSeriesKey(series),
+ AncestorWithPresentationUniqueKey = enableSeriesPresentationKey ? null : seriesKey,
+ SeriesPresentationUniqueKey = enableSeriesPresentationKey ? seriesKey : null,
IncludeItemTypes = new[] { typeof(Episode).Name },
SortBy = new[] { ItemSortBy.SortName },
SortOrder = SortOrder.Descending,
@@ -174,7 +178,8 @@ namespace Emby.Server.Implementations.TV
{
return _libraryManager.GetItemList(new InternalItemsQuery(user)
{
- AncestorWithPresentationUniqueKey = GetUniqueSeriesKey(series),
+ AncestorWithPresentationUniqueKey = enableSeriesPresentationKey ? null : seriesKey,
+ SeriesPresentationUniqueKey = enableSeriesPresentationKey ? seriesKey : null,
IncludeItemTypes = new[] { typeof(Episode).Name },
SortBy = new[] { ItemSortBy.SortName },
SortOrder = SortOrder.Ascending,
diff --git a/MediaBrowser.Api/StartupWizardService.cs b/MediaBrowser.Api/StartupWizardService.cs
index 4e5047f78..25a460935 100644
--- a/MediaBrowser.Api/StartupWizardService.cs
+++ b/MediaBrowser.Api/StartupWizardService.cs
@@ -119,6 +119,7 @@ namespace MediaBrowser.Api
config.SkipDeserializationForBasicTypes = true;
config.SkipDeserializationForPrograms = true;
config.SkipDeserializationForAudio = true;
+ config.EnableSeriesPresentationUniqueKey = true;
}
public void Post(UpdateStartupConfiguration request)
diff --git a/MediaBrowser.Controller/Entities/Book.cs b/MediaBrowser.Controller/Entities/Book.cs
index 9ccc7125a..a6da389f0 100644
--- a/MediaBrowser.Controller/Entities/Book.cs
+++ b/MediaBrowser.Controller/Entities/Book.cs
@@ -18,6 +18,8 @@ namespace MediaBrowser.Controller.Entities
}
}
+ [IgnoreDataMember]
+ public string SeriesPresentationUniqueKey { get; set; }
[IgnoreDataMember]
public string SeriesName { get; set; }
[IgnoreDataMember]
@@ -33,6 +35,10 @@ namespace MediaBrowser.Controller.Entities
{
return SeriesName;
}
+ public string FindSeriesPresentationUniqueKey()
+ {
+ return SeriesPresentationUniqueKey;
+ }
[IgnoreDataMember]
public override bool EnableRefreshOnDateModifiedChange
diff --git a/MediaBrowser.Controller/Entities/IHasSeries.cs b/MediaBrowser.Controller/Entities/IHasSeries.cs
index 531f58788..203be93e8 100644
--- a/MediaBrowser.Controller/Entities/IHasSeries.cs
+++ b/MediaBrowser.Controller/Entities/IHasSeries.cs
@@ -15,5 +15,7 @@ namespace MediaBrowser.Controller.Entities
string FindSeriesSortName();
Guid? SeriesId { get; set; }
Guid? FindSeriesId();
+ string SeriesPresentationUniqueKey { get; set; }
+ string FindSeriesPresentationUniqueKey();
}
}
diff --git a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs
index 17ef81db9..a2d278a71 100644
--- a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs
+++ b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs
@@ -147,6 +147,7 @@ namespace MediaBrowser.Controller.Entities
public string[] ArtistNames { get; set; }
public string[] ExcludeArtistIds { get; set; }
public string AncestorWithPresentationUniqueKey { get; set; }
+ public string SeriesPresentationUniqueKey { get; set; }
public bool GroupByPresentationUniqueKey { get; set; }
public bool EnableTotalRecordCount { get; set; }
diff --git a/MediaBrowser.Controller/Entities/TV/Episode.cs b/MediaBrowser.Controller/Entities/TV/Episode.cs
index 29a63f317..2bcccf5e8 100644
--- a/MediaBrowser.Controller/Entities/TV/Episode.cs
+++ b/MediaBrowser.Controller/Entities/TV/Episode.cs
@@ -165,13 +165,22 @@ namespace MediaBrowser.Controller.Entities.TV
{
return FindParent() != null;
}
- }
+ }
+
+ [IgnoreDataMember]
+ public string SeriesPresentationUniqueKey { get; set; }
[IgnoreDataMember]
public string SeriesName { get; set; }
[IgnoreDataMember]
- public string SeasonName { get; set; }
+ public string SeasonName { get; set; }
+
+ public string FindSeriesPresentationUniqueKey()
+ {
+ var series = Series;
+ return series == null ? null : series.PresentationUniqueKey;
+ }
public string FindSeasonName()
{
diff --git a/MediaBrowser.Controller/Entities/TV/Season.cs b/MediaBrowser.Controller/Entities/TV/Season.cs
index 2663a9dd5..e0cc496a1 100644
--- a/MediaBrowser.Controller/Entities/TV/Season.cs
+++ b/MediaBrowser.Controller/Entities/TV/Season.cs
@@ -207,12 +207,21 @@ namespace MediaBrowser.Controller.Entities.TV
return UnratedItem.Series;
}
+ [IgnoreDataMember]
+ public string SeriesPresentationUniqueKey { get; set; }
+
[IgnoreDataMember]
public string SeriesName { get; set; }
[IgnoreDataMember]
public Guid? SeriesId { get; set; }
+ public string FindSeriesPresentationUniqueKey()
+ {
+ var series = Series;
+ return series == null ? null : series.PresentationUniqueKey;
+ }
+
public string FindSeriesName()
{
var series = Series;
diff --git a/MediaBrowser.Controller/Entities/TV/Series.cs b/MediaBrowser.Controller/Entities/TV/Series.cs
index ef25faf91..92cd20769 100644
--- a/MediaBrowser.Controller/Entities/TV/Series.cs
+++ b/MediaBrowser.Controller/Entities/TV/Series.cs
@@ -95,12 +95,16 @@ namespace MediaBrowser.Controller.Entities.TV
public override string CreatePresentationUniqueKey()
{
- var userdatakeys = GetUserDataKeys();
-
- if (userdatakeys.Count > 1)
+ if (LibraryManager.GetLibraryOptions(this).EnableAutomaticSeriesGrouping)
{
- return AddLibrariesToPresentationUniqueKey(userdatakeys[0]);
+ var userdatakeys = GetUserDataKeys();
+
+ if (userdatakeys.Count > 1)
+ {
+ return AddLibrariesToPresentationUniqueKey(userdatakeys[0]);
+ }
}
+
return base.CreatePresentationUniqueKey();
}
@@ -131,9 +135,13 @@ namespace MediaBrowser.Controller.Entities.TV
public override int GetChildCount(User user)
{
+ var enableSeriesPresentationKey = ConfigurationManager.Configuration.EnableSeriesPresentationUniqueKey;
+ var seriesKey = GetUniqueSeriesKey(this);
+
var result = LibraryManager.GetItemsResult(new InternalItemsQuery(user)
{
- AncestorWithPresentationUniqueKey = GetUniqueSeriesKey(this),
+ AncestorWithPresentationUniqueKey = enableSeriesPresentationKey ? null : seriesKey,
+ SeriesPresentationUniqueKey = enableSeriesPresentationKey ? seriesKey : null,
IncludeItemTypes = new[] { typeof(Season).Name },
IsVirtualItem = false,
Limit = 0
@@ -144,9 +152,15 @@ namespace MediaBrowser.Controller.Entities.TV
public override int GetRecursiveChildCount(User user)
{
- var query = new InternalItemsQuery(user);
+ var enableSeriesPresentationKey = ConfigurationManager.Configuration.EnableSeriesPresentationUniqueKey;
+ var seriesKey = GetUniqueSeriesKey(this);
+
+ var query = new InternalItemsQuery(user)
+ {
+ AncestorWithPresentationUniqueKey = enableSeriesPresentationKey ? null : seriesKey,
+ SeriesPresentationUniqueKey = enableSeriesPresentationKey ? seriesKey : null,
+ };
- query.AncestorWithPresentationUniqueKey = GetUniqueSeriesKey(this);
if (query.SortBy.Length == 0)
{
query.SortBy = new[] { ItemSortBy.SortName };
@@ -223,11 +237,13 @@ namespace MediaBrowser.Controller.Entities.TV
{
var config = user.Configuration;
+ var enableSeriesPresentationKey = ConfigurationManager.Configuration.EnableSeriesPresentationUniqueKey;
var seriesKey = GetUniqueSeriesKey(this);
var query = new InternalItemsQuery(user)
{
- AncestorWithPresentationUniqueKey = seriesKey,
+ AncestorWithPresentationUniqueKey = enableSeriesPresentationKey ? null : seriesKey,
+ SeriesPresentationUniqueKey = enableSeriesPresentationKey ? seriesKey : null,
IncludeItemTypes = new[] { typeof(Season).Name },
SortBy = new[] { ItemSortBy.SortName }
};
@@ -259,7 +275,11 @@ namespace MediaBrowser.Controller.Entities.TV
if (query.Recursive)
{
- query.AncestorWithPresentationUniqueKey = GetUniqueSeriesKey(this);
+ var enableSeriesPresentationKey = ConfigurationManager.Configuration.EnableSeriesPresentationUniqueKey;
+ var seriesKey = GetUniqueSeriesKey(this);
+
+ query.AncestorWithPresentationUniqueKey = enableSeriesPresentationKey ? null : seriesKey;
+ query.SeriesPresentationUniqueKey = enableSeriesPresentationKey ? seriesKey : null;
if (query.SortBy.Length == 0)
{
query.SortBy = new[] { ItemSortBy.SortName };
@@ -281,11 +301,13 @@ namespace MediaBrowser.Controller.Entities.TV
public IEnumerable GetEpisodes(User user)
{
+ var enableSeriesPresentationKey = ConfigurationManager.Configuration.EnableSeriesPresentationUniqueKey;
var seriesKey = GetUniqueSeriesKey(this);
var query = new InternalItemsQuery(user)
{
- AncestorWithPresentationUniqueKey = seriesKey,
+ AncestorWithPresentationUniqueKey = enableSeriesPresentationKey ? null : seriesKey,
+ SeriesPresentationUniqueKey = enableSeriesPresentationKey ? seriesKey : null,
IncludeItemTypes = new[] { typeof(Episode).Name, typeof(Season).Name },
SortBy = new[] { ItemSortBy.SortName }
};
@@ -387,14 +409,19 @@ namespace MediaBrowser.Controller.Entities.TV
public IEnumerable GetSeasonEpisodes(Season parentSeason, User user)
{
+ var enableSeriesPresentationKey = ConfigurationManager.Configuration.EnableSeriesPresentationUniqueKey;
+
+ var queryFromSeries = ConfigurationManager.Configuration.DisplaySpecialsWithinSeasons;
+
// add optimization when this setting is not enabled
- var seriesKey = ConfigurationManager.Configuration.DisplaySpecialsWithinSeasons ?
+ var seriesKey = queryFromSeries ?
GetUniqueSeriesKey(this) :
GetUniqueSeriesKey(parentSeason);
var query = new InternalItemsQuery(user)
{
- AncestorWithPresentationUniqueKey = seriesKey,
+ AncestorWithPresentationUniqueKey = queryFromSeries && enableSeriesPresentationKey ? null : seriesKey,
+ SeriesPresentationUniqueKey = queryFromSeries && enableSeriesPresentationKey ? seriesKey : null,
IncludeItemTypes = new[] { typeof(Episode).Name },
SortBy = new[] { ItemSortBy.SortName }
};
diff --git a/MediaBrowser.Model/Configuration/LibraryOptions.cs b/MediaBrowser.Model/Configuration/LibraryOptions.cs
index ffcb79cfb..e79253d19 100644
--- a/MediaBrowser.Model/Configuration/LibraryOptions.cs
+++ b/MediaBrowser.Model/Configuration/LibraryOptions.cs
@@ -14,6 +14,7 @@
public bool SaveLocalMetadata { get; set; }
public bool EnableInternetProviders { get; set; }
public bool ImportMissingEpisodes { get; set; }
+ public bool EnableAutomaticSeriesGrouping { get; set; }
public LibraryOptions()
{
@@ -21,6 +22,7 @@
EnableRealtimeMonitor = true;
PathInfos = new MediaPathInfo[] { };
EnableInternetProviders = true;
+ EnableAutomaticSeriesGrouping = true;
}
}
diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs
index 520cc9701..35677813d 100644
--- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs
+++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs
@@ -46,6 +46,7 @@ namespace MediaBrowser.Model.Configuration
///
/// true if [use HTTPS]; otherwise, false.
public bool EnableHttps { get; set; }
+ public bool EnableSeriesPresentationUniqueKey { get; set; }
///
/// Gets or sets the value pointing to the file system where the ssl certiifcate is located..
diff --git a/MediaBrowser.Providers/TV/EpisodeMetadataService.cs b/MediaBrowser.Providers/TV/EpisodeMetadataService.cs
index 2e4271c0f..538d96c17 100644
--- a/MediaBrowser.Providers/TV/EpisodeMetadataService.cs
+++ b/MediaBrowser.Providers/TV/EpisodeMetadataService.cs
@@ -55,6 +55,13 @@ namespace MediaBrowser.Providers.TV
updateType |= ItemUpdateType.MetadataImport;
}
+ var seriesPresentationUniqueKey = item.FindSeriesPresentationUniqueKey();
+ if (!string.Equals(item.SeriesPresentationUniqueKey, seriesPresentationUniqueKey, StringComparison.Ordinal))
+ {
+ item.SeriesPresentationUniqueKey = seriesPresentationUniqueKey;
+ updateType |= ItemUpdateType.MetadataImport;
+ }
+
return updateType;
}
diff --git a/MediaBrowser.Providers/TV/SeasonMetadataService.cs b/MediaBrowser.Providers/TV/SeasonMetadataService.cs
index 81a0c0b4a..af7dea59e 100644
--- a/MediaBrowser.Providers/TV/SeasonMetadataService.cs
+++ b/MediaBrowser.Providers/TV/SeasonMetadataService.cs
@@ -51,6 +51,13 @@ namespace MediaBrowser.Providers.TV
updateType |= ItemUpdateType.MetadataImport;
}
+ var seriesPresentationUniqueKey = item.FindSeriesPresentationUniqueKey();
+ if (!string.Equals(item.SeriesPresentationUniqueKey, seriesPresentationUniqueKey, StringComparison.Ordinal))
+ {
+ item.SeriesPresentationUniqueKey = seriesPresentationUniqueKey;
+ updateType |= ItemUpdateType.MetadataImport;
+ }
+
var seriesId = item.FindSeriesId();
if (item.SeriesId != seriesId)
{
diff --git a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj
index 3564cffa1..3cf7e54c0 100644
--- a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj
+++ b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj
@@ -693,9 +693,6 @@
PreserveNewest
-
- PreserveNewest
-
PreserveNewest
@@ -876,9 +873,6 @@
PreserveNewest
-
- PreserveNewest
-
PreserveNewest
diff --git a/MediaBrowser.XbmcMetadata/Savers/MovieNfoSaver.cs b/MediaBrowser.XbmcMetadata/Savers/MovieNfoSaver.cs
index dc827b861..3bd3a1555 100644
--- a/MediaBrowser.XbmcMetadata/Savers/MovieNfoSaver.cs
+++ b/MediaBrowser.XbmcMetadata/Savers/MovieNfoSaver.cs
@@ -35,7 +35,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
list.Add(Path.Combine(path, "VIDEO_TS", "VIDEO_TS.nfo"));
}
- if (item.VideoType == VideoType.Dvd || item.VideoType == VideoType.BluRay || item.VideoType == VideoType.HdDvd)
+ if (!item.IsPlaceHolder && (item.VideoType == VideoType.Dvd || item.VideoType == VideoType.BluRay || item.VideoType == VideoType.HdDvd))
{
var path = item.ContainingFolderPath;