aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Emby.Server.Implementations/ApplicationHost.cs6
-rw-r--r--Emby.Server.Implementations/Dto/DtoService.cs6
-rw-r--r--Emby.Server.Implementations/Library/Validators/CollectionPostScanTask.cs156
-rw-r--r--Emby.Server.Implementations/Localization/LocalizationManager.cs117
-rw-r--r--MediaBrowser.Model/Configuration/LibraryOptions.cs2
5 files changed, 233 insertions, 54 deletions
diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs
index 99ad9fdf4..903c31133 100644
--- a/Emby.Server.Implementations/ApplicationHost.cs
+++ b/Emby.Server.Implementations/ApplicationHost.cs
@@ -1128,12 +1128,12 @@ namespace Emby.Server.Implementations
}
/// <inheritdoc/>
- public string GetApiUrlForLocalAccess(bool allowHttps)
+ public string GetApiUrlForLocalAccess(bool allowHttps = true)
{
// With an empty source, the port will be null
string smart = NetManager.GetBindInterface(string.Empty, out _);
- var scheme = allowHttps ? Uri.UriSchemeHttps : Uri.UriSchemeHttp;
- var port = allowHttps ? HttpsPort : HttpPort;
+ var scheme = !allowHttps ? Uri.UriSchemeHttp : null;
+ int? port = !allowHttps ? HttpPort : null;
return GetLocalApiUrl(smart.Trim('/'), scheme, port);
}
diff --git a/Emby.Server.Implementations/Dto/DtoService.cs b/Emby.Server.Implementations/Dto/DtoService.cs
index 67ecd04e0..b91ff6408 100644
--- a/Emby.Server.Implementations/Dto/DtoService.cs
+++ b/Emby.Server.Implementations/Dto/DtoService.cs
@@ -370,6 +370,12 @@ namespace Emby.Server.Implementations.Dto
if (item is MusicAlbum || item is Season || item is Playlist)
{
dto.ChildCount = dto.RecursiveItemCount;
+ var folderChildCount = folder.LinkedChildren.Length;
+ // The default is an empty array, so we can't reliably use the count when it's empty
+ if (folderChildCount > 0)
+ {
+ dto.ChildCount ??= folderChildCount;
+ }
}
if (options.ContainsField(ItemFields.ChildCount))
diff --git a/Emby.Server.Implementations/Library/Validators/CollectionPostScanTask.cs b/Emby.Server.Implementations/Library/Validators/CollectionPostScanTask.cs
new file mode 100644
index 000000000..945b559ad
--- /dev/null
+++ b/Emby.Server.Implementations/Library/Validators/CollectionPostScanTask.cs
@@ -0,0 +1,156 @@
+using System;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Collections.Generic;
+using MediaBrowser.Controller.Collections;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Entities.Movies;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Model.Querying;
+using Jellyfin.Data.Enums;
+using Microsoft.Extensions.Logging;
+using MediaBrowser.Model.Entities;
+
+namespace Emby.Server.Implementations.Library.Validators
+{
+ /// <summary>
+ /// Class CollectionPostScanTask.
+ /// </summary>
+ public class CollectionPostScanTask : ILibraryPostScanTask
+ {
+ private readonly ILibraryManager _libraryManager;
+ private readonly ICollectionManager _collectionManager;
+ private readonly ILogger<CollectionPostScanTask> _logger;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="CollectionPostScanTask" /> class.
+ /// </summary>
+ /// <param name="libraryManager">The library manager.</param>
+ /// <param name="collectionManager">The collection manager.</param>
+ /// <param name="logger">The logger.</param>
+ public CollectionPostScanTask(
+ ILibraryManager libraryManager,
+ ICollectionManager collectionManager,
+ ILogger<CollectionPostScanTask> logger)
+ {
+ _libraryManager = libraryManager;
+ _collectionManager = collectionManager;
+ _logger = logger;
+ }
+
+ /// <summary>
+ /// Runs the specified progress.
+ /// </summary>
+ /// <param name="progress">The progress.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>Task.</returns>
+ public async Task Run(IProgress<double> progress, CancellationToken cancellationToken)
+ {
+ var collectionNameMoviesMap = new Dictionary<string, HashSet<Guid>>();
+
+ foreach (var library in _libraryManager.RootFolder.Children)
+ {
+ if (!_libraryManager.GetLibraryOptions(library).AutomaticallyAddToCollection)
+ {
+ continue;
+ }
+
+ var startIndex = 0;
+ var pagesize = 1000;
+
+ while (true)
+ {
+ var movies = _libraryManager.GetItemList(new InternalItemsQuery
+ {
+ MediaTypes = new string[] { MediaType.Video },
+ IncludeItemTypes = new[] { nameof(Movie) },
+ IsVirtualItem = false,
+ OrderBy = new[] { (ItemSortBy.SortName, SortOrder.Ascending) },
+ Parent = library,
+ StartIndex = startIndex,
+ Limit = pagesize,
+ Recursive = true
+ });
+
+ foreach (var m in movies)
+ {
+ if (m is Movie movie && !string.IsNullOrEmpty(movie.CollectionName))
+ {
+ if (collectionNameMoviesMap.TryGetValue(movie.CollectionName, out var movieList))
+ {
+ movieList.Add(movie.Id);
+ }
+ else
+ {
+ collectionNameMoviesMap[movie.CollectionName] = new HashSet<Guid> { movie.Id };
+ }
+ }
+ }
+
+ if (movies.Count < pagesize)
+ {
+ break;
+ }
+
+ startIndex += pagesize;
+ }
+ }
+
+ var numComplete = 0;
+ var count = collectionNameMoviesMap.Count;
+
+ if (count == 0)
+ {
+ progress.Report(100);
+ return;
+ }
+
+ var boxSets = _libraryManager.GetItemList(new InternalItemsQuery
+ {
+ IncludeItemTypes = new[] { nameof(BoxSet) },
+ CollapseBoxSetItems = false,
+ Recursive = true
+ });
+
+ foreach (var (collectionName, movieIds) in collectionNameMoviesMap)
+ {
+ try
+ {
+ var boxSet = boxSets.FirstOrDefault(b => b?.Name == collectionName) as BoxSet;
+ if (boxSet == null)
+ {
+ // won't automatically create collection if only one movie in it
+ if (movieIds.Count >= 2)
+ {
+ boxSet = await _collectionManager.CreateCollectionAsync(new CollectionCreationOptions
+ {
+ Name = collectionName,
+ IsLocked = true
+ });
+
+ await _collectionManager.AddToCollectionAsync(boxSet.Id, movieIds);
+ }
+ }
+ else
+ {
+ await _collectionManager.AddToCollectionAsync(boxSet.Id, movieIds);
+ }
+
+ numComplete++;
+ double percent = numComplete;
+ percent /= count;
+ percent *= 100;
+
+ progress.Report(percent);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Error refreshing {CollectionName} with {@MovieIds}", collectionName, movieIds);
+ }
+ }
+
+ progress.Report(100);
+ }
+ }
+}
diff --git a/Emby.Server.Implementations/Localization/LocalizationManager.cs b/Emby.Server.Implementations/Localization/LocalizationManager.cs
index 1524fcdb2..9cdbbb6a3 100644
--- a/Emby.Server.Implementations/Localization/LocalizationManager.cs
+++ b/Emby.Server.Implementations/Localization/LocalizationManager.cs
@@ -373,60 +373,75 @@ namespace Emby.Server.Implementations.Localization
public IEnumerable<LocalizationOption> GetLocalizationOptions()
{
yield return new LocalizationOption("Afrikaans", "af");
- yield return new LocalizationOption("Arabic", "ar");
- yield return new LocalizationOption("Bulgarian (Bulgaria)", "bg-BG");
- yield return new LocalizationOption("Catalan", "ca");
- yield return new LocalizationOption("Chinese (Hong Kong)", "zh-HK");
- yield return new LocalizationOption("Chinese Simplified", "zh-CN");
- yield return new LocalizationOption("Chinese Traditional", "zh-TW");
- yield return new LocalizationOption("Croatian", "hr");
- yield return new LocalizationOption("Czech", "cs");
- yield return new LocalizationOption("Danish", "da");
- yield return new LocalizationOption("Dutch", "nl");
+ yield return new LocalizationOption("العربية", "ar");
+ yield return new LocalizationOption("Беларуская", "be");
+ yield return new LocalizationOption("Български", "bg-BG");
+ yield return new LocalizationOption("বাংলা (বাংলাদেশ)", "bn");
+ yield return new LocalizationOption("Català", "ca");
+ yield return new LocalizationOption("Čeština", "cs");
+ yield return new LocalizationOption("Cymraeg", "cy");
+ yield return new LocalizationOption("Dansk", "da");
+ yield return new LocalizationOption("Deutsch", "de");
yield return new LocalizationOption("English (United Kingdom)", "en-GB");
- yield return new LocalizationOption("English (United States)", "en-US");
+ yield return new LocalizationOption("English", "en-US");
+ yield return new LocalizationOption("Ελληνικά", "el");
yield return new LocalizationOption("Esperanto", "eo");
- yield return new LocalizationOption("Estonian", "et");
- yield return new LocalizationOption("Finnish", "fi");
- yield return new LocalizationOption("French", "fr");
- yield return new LocalizationOption("French (Canada)", "fr-CA");
- yield return new LocalizationOption("German", "de");
- yield return new LocalizationOption("Greek", "el");
- yield return new LocalizationOption("Hebrew", "he");
- yield return new LocalizationOption("Hungarian", "hu");
- yield return new LocalizationOption("Icelandic", "is");
- yield return new LocalizationOption("Indonesian", "id");
- yield return new LocalizationOption("Italian", "it");
- yield return new LocalizationOption("Japanese", "ja");
- yield return new LocalizationOption("Kazakh", "kk");
- yield return new LocalizationOption("Korean", "ko");
- yield return new LocalizationOption("Latvian", "lv");
- yield return new LocalizationOption("Lithuanian", "lt-LT");
- yield return new LocalizationOption("Malay", "ms");
- yield return new LocalizationOption("Malayalam", "ml");
- yield return new LocalizationOption("Norwegian Bokmål", "nb");
- yield return new LocalizationOption("Norwegian Nynorsk", "nn");
- yield return new LocalizationOption("Persian", "fa");
- yield return new LocalizationOption("Polish", "pl");
- yield return new LocalizationOption("Portuguese", "pt");
- yield return new LocalizationOption("Portuguese (Brazil)", "pt-BR");
- yield return new LocalizationOption("Portuguese (Portugal)", "pt-PT");
- yield return new LocalizationOption("Romanian", "ro");
- yield return new LocalizationOption("Russian", "ru");
- yield return new LocalizationOption("Serbian", "sr");
- yield return new LocalizationOption("Slovak", "sk");
- yield return new LocalizationOption("Slovenian (Slovenia)", "sl-SI");
- yield return new LocalizationOption("Spanish", "es");
- yield return new LocalizationOption("Spanish (Argentina)", "es-AR");
- yield return new LocalizationOption("Spanish (Latin America)", "es-419");
- yield return new LocalizationOption("Spanish (Mexico)", "es-MX");
- yield return new LocalizationOption("Swedish", "sv");
- yield return new LocalizationOption("Swiss German", "gsw");
- yield return new LocalizationOption("Tamil", "ta");
- yield return new LocalizationOption("Telugu", "te");
- yield return new LocalizationOption("Turkish", "tr");
+ yield return new LocalizationOption("Español", "es");
+ yield return new LocalizationOption("Español americano", "es_419");
+ yield return new LocalizationOption("Español (Argentina)", "es-AR");
+ yield return new LocalizationOption("Español (Dominicana)", "es_DO");
+ yield return new LocalizationOption("Español (México)", "es-MX");
+ yield return new LocalizationOption("Eesti", "et");
+ yield return new LocalizationOption("فارسی", "fa");
+ yield return new LocalizationOption("Suomi", "fi");
+ yield return new LocalizationOption("Filipino", "fil");
+ yield return new LocalizationOption("Français", "fr");
+ yield return new LocalizationOption("Français (Canada)", "fr-CA");
+ yield return new LocalizationOption("Galego", "gl");
+ yield return new LocalizationOption("Schwiizerdütsch", "gsw");
+ yield return new LocalizationOption("עִבְרִית", "he");
+ yield return new LocalizationOption("हिन्दी", "hi");
+ yield return new LocalizationOption("Hrvatski", "hr");
+ yield return new LocalizationOption("Magyar", "hu");
+ yield return new LocalizationOption("Bahasa Indonesia", "id");
+ yield return new LocalizationOption("Íslenska", "is");
+ yield return new LocalizationOption("Italiano", "it");
+ yield return new LocalizationOption("日本語", "ja");
+ yield return new LocalizationOption("Qazaqşa", "kk");
+ yield return new LocalizationOption("한국어", "ko");
+ yield return new LocalizationOption("Lietuvių", "lt");
+ yield return new LocalizationOption("Latviešu", "lv");
+ yield return new LocalizationOption("Македонски", "mk");
+ yield return new LocalizationOption("മലയാളം", "ml");
+ yield return new LocalizationOption("मराठी", "mr");
+ yield return new LocalizationOption("Bahasa Melayu", "ms");
+ yield return new LocalizationOption("Norsk bokmål", "nb");
+ yield return new LocalizationOption("नेपाली", "ne");
+ yield return new LocalizationOption("Nederlands", "nl");
+ yield return new LocalizationOption("Norsk nynorsk", "nn");
+ yield return new LocalizationOption("ਪੰਜਾਬੀ", "pa");
+ yield return new LocalizationOption("Polski", "pl");
+ yield return new LocalizationOption("Pirate", "pr");
+ yield return new LocalizationOption("Português", "pt");
+ yield return new LocalizationOption("Português (Brasil)", "pt-BR");
+ yield return new LocalizationOption("Português (Portugal)", "pt-PT");
+ yield return new LocalizationOption("Românește", "ro");
+ yield return new LocalizationOption("Русский", "ru");
+ yield return new LocalizationOption("Slovenčina", "sk");
+ yield return new LocalizationOption("Slovenščina", "sl-SI");
+ yield return new LocalizationOption("Shqip", "sq");
+ yield return new LocalizationOption("Српски", "sr");
+ yield return new LocalizationOption("Svenska", "sv");
+ yield return new LocalizationOption("தமிழ்", "ta");
+ yield return new LocalizationOption("తెలుగు", "te");
+ yield return new LocalizationOption("ภาษาไทย", "th");
+ yield return new LocalizationOption("Türkçe", "tr");
+ yield return new LocalizationOption("Українська", "uk");
+ yield return new LocalizationOption("اُردُو", "ur_PK");
yield return new LocalizationOption("Tiếng Việt", "vi");
- yield return new LocalizationOption("Ukrainian", "uk");
+ yield return new LocalizationOption("汉语 (简化字)", "zh-CN");
+ yield return new LocalizationOption("漢語 (繁体字)", "zh-TW");
+ yield return new LocalizationOption("廣東話 (香港)", "zh-HK");
}
}
}
diff --git a/MediaBrowser.Model/Configuration/LibraryOptions.cs b/MediaBrowser.Model/Configuration/LibraryOptions.cs
index aae5359b1..90cf8f43b 100644
--- a/MediaBrowser.Model/Configuration/LibraryOptions.cs
+++ b/MediaBrowser.Model/Configuration/LibraryOptions.cs
@@ -16,6 +16,7 @@ namespace MediaBrowser.Model.Configuration
SkipSubtitlesIfAudioTrackMatches = true;
RequirePerfectSubtitleMatch = true;
+ AutomaticallyAddToCollection = true;
EnablePhotos = true;
SaveSubtitlesWithMedia = true;
EnableRealtimeMonitor = true;
@@ -80,6 +81,7 @@ namespace MediaBrowser.Model.Configuration
public bool RequirePerfectSubtitleMatch { get; set; }
public bool SaveSubtitlesWithMedia { get; set; }
+ public bool AutomaticallyAddToCollection { get; set; }
public TypeOptions[] TypeOptions { get; set; }