aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Dockerfile2
-rw-r--r--Dockerfile.arm2
-rw-r--r--Dockerfile.arm642
-rw-r--r--Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs4
-rw-r--r--Emby.Server.Implementations/ApplicationHost.cs2
-rw-r--r--Emby.Server.Implementations/Collections/CollectionImageProvider.cs3
-rw-r--r--Emby.Server.Implementations/Dto/DtoService.cs3
-rw-r--r--Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs17
-rw-r--r--Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs3
-rw-r--r--Emby.Server.Implementations/IO/FileRefresher.cs4
-rw-r--r--Emby.Server.Implementations/LiveTv/LiveTvManager.cs8
-rw-r--r--Emby.Server.Implementations/Networking/NetworkManager.cs6
-rw-r--r--Emby.Server.Implementations/Playlists/PlaylistImageProvider.cs4
-rw-r--r--Emby.Server.Implementations/ServerApplicationPaths.cs9
-rw-r--r--Emby.Server.Implementations/UserViews/DynamicImageProvider.cs4
-rw-r--r--Jellyfin.Server/Program.cs27
-rw-r--r--Jellyfin.Server/StartupOptions.cs3
-rw-r--r--MediaBrowser.Api/Library/LibraryService.cs15
-rw-r--r--MediaBrowser.Api/Movies/MoviesService.cs6
-rw-r--r--MediaBrowser.Controller/Entities/CollectionFolder.cs6
-rw-r--r--MediaBrowser.Controller/Entities/TV/Series.cs2
-rw-r--r--MediaBrowser.Controller/Library/NameExtensions.cs8
-rw-r--r--MediaBrowser.Model/Extensions/LinqExtensions.cs85
-rw-r--r--deployment/debian-package-x64/pkg-src/conf/jellyfin19
-rw-r--r--deployment/debian-package-x64/pkg-src/conf/jellyfin-sudoers (renamed from deployment/debian-package-x64/pkg-src/bin/jellyfin-sudoers)0
-rw-r--r--deployment/debian-package-x64/pkg-src/install2
-rw-r--r--deployment/debian-package-x64/pkg-src/jellyfin.service2
-rw-r--r--deployment/debian-package-x64/pkg-src/postinst11
-rw-r--r--deployment/debian-package-x64/pkg-src/postrm8
-rw-r--r--deployment/debian-package-x64/pkg-src/preinst8
-rw-r--r--deployment/debian-package-x64/pkg-src/prerm3
-rw-r--r--deployment/fedora-package-x64/pkg-src/jellyfin.env7
-rw-r--r--deployment/fedora-package-x64/pkg-src/jellyfin.service2
-rw-r--r--deployment/win-generic/install-jellyfin.ps110
34 files changed, 142 insertions, 155 deletions
diff --git a/Dockerfile b/Dockerfile
index c79d6f8ee..37388fda9 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -23,4 +23,4 @@ COPY --from=ffmpeg / /
COPY --from=builder /jellyfin /jellyfin
EXPOSE 8096
VOLUME /config /media
-ENTRYPOINT dotnet /jellyfin/jellyfin.dll -programdata /config
+ENTRYPOINT dotnet /jellyfin/jellyfin.dll --datadir /config
diff --git a/Dockerfile.arm b/Dockerfile.arm
index 657cb4ac6..039274197 100644
--- a/Dockerfile.arm
+++ b/Dockerfile.arm
@@ -21,4 +21,4 @@ RUN apt-get update \
COPY --from=builder /jellyfin /jellyfin
EXPOSE 8096
VOLUME /config /media
-ENTRYPOINT dotnet /jellyfin/jellyfin.dll -programdata /config
+ENTRYPOINT dotnet /jellyfin/jellyfin.dll --datadir /config
diff --git a/Dockerfile.arm64 b/Dockerfile.arm64
index 979dfc1dc..06ba21b91 100644
--- a/Dockerfile.arm64
+++ b/Dockerfile.arm64
@@ -30,4 +30,4 @@ COPY --from=qemu_extract qemu-* /usr/bin
COPY --from=builder /jellyfin /jellyfin
EXPOSE 8096
VOLUME /config /media
-ENTRYPOINT dotnet /jellyfin/jellyfin.dll -programdata /config
+ENTRYPOINT dotnet /jellyfin/jellyfin.dll --datadir /config
diff --git a/Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs b/Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs
index e4a2cd9df..701c04f9e 100644
--- a/Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs
+++ b/Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs
@@ -16,12 +16,14 @@ namespace Emby.Server.Implementations.AppBase
string programDataPath,
string appFolderPath,
string logDirectoryPath = null,
- string configurationDirectoryPath = null)
+ string configurationDirectoryPath = null,
+ string cacheDirectoryPath = null)
{
ProgramDataPath = programDataPath;
ProgramSystemPath = appFolderPath;
LogDirectoryPath = logDirectoryPath;
ConfigurationDirectoryPath = configurationDirectoryPath;
+ CachePath = cacheDirectoryPath;
}
public string ProgramDataPath { get; private set; }
diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs
index 87e3b45ab..c59a648e5 100644
--- a/Emby.Server.Implementations/ApplicationHost.cs
+++ b/Emby.Server.Implementations/ApplicationHost.cs
@@ -904,7 +904,7 @@ namespace Emby.Server.Implementations
PlaylistManager = new PlaylistManager(LibraryManager, FileSystemManager, LibraryMonitor, LoggerFactory, UserManager, ProviderManager);
RegisterSingleInstance(PlaylistManager);
- LiveTvManager = new LiveTvManager(this, ServerConfigurationManager, LoggerFactory, ItemRepository, ImageProcessor, UserDataManager, DtoService, UserManager, LibraryManager, TaskManager, LocalizationManager, JsonSerializer, ProviderManager, FileSystemManager, () => ChannelManager);
+ LiveTvManager = new LiveTvManager(this, ServerConfigurationManager, LoggerFactory, ItemRepository, ImageProcessor, UserDataManager, DtoService, UserManager, LibraryManager, TaskManager, LocalizationManager, JsonSerializer, FileSystemManager, () => ChannelManager);
RegisterSingleInstance(LiveTvManager);
UserViewManager = new UserViewManager(LibraryManager, LocalizationManager, UserManager, ChannelManager, LiveTvManager, ServerConfigurationManager);
diff --git a/Emby.Server.Implementations/Collections/CollectionImageProvider.cs b/Emby.Server.Implementations/Collections/CollectionImageProvider.cs
index 6642d1ef4..6aeadda2f 100644
--- a/Emby.Server.Implementations/Collections/CollectionImageProvider.cs
+++ b/Emby.Server.Implementations/Collections/CollectionImageProvider.cs
@@ -70,7 +70,8 @@ namespace Emby.Server.Implementations.Collections
return null;
})
.Where(i => i != null)
- .DistinctBy(i => i.Id)
+ .GroupBy(x => x.Id)
+ .Select(x => x.First())
.OrderBy(i => Guid.NewGuid())
.ToList();
}
diff --git a/Emby.Server.Implementations/Dto/DtoService.cs b/Emby.Server.Implementations/Dto/DtoService.cs
index a3529fdb4..f5634690f 100644
--- a/Emby.Server.Implementations/Dto/DtoService.cs
+++ b/Emby.Server.Implementations/Dto/DtoService.cs
@@ -620,7 +620,8 @@ namespace Emby.Server.Implementations.Dto
}
}).Where(i => i != null)
- .DistinctBy(i => i.Name, StringComparer.OrdinalIgnoreCase)
+ .GroupBy(i => i.Name, StringComparer.OrdinalIgnoreCase)
+ .Select(x => x.First())
.ToDictionary(i => i.Name, StringComparer.OrdinalIgnoreCase);
for (var i = 0; i < people.Count; i++)
diff --git a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs
index 7a8b09cf7..a670a289c 100644
--- a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs
+++ b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs
@@ -277,14 +277,21 @@ namespace Emby.Server.Implementations.EntryPoints
lock (_libraryChangedSyncLock)
{
// Remove dupes in case some were saved multiple times
- var foldersAddedTo = _foldersAddedTo.DistinctBy(i => i.Id).ToList();
+ var foldersAddedTo = _foldersAddedTo
+ .GroupBy(x => x.Id)
+ .Select(x => x.First())
+ .ToList();
- var foldersRemovedFrom = _foldersRemovedFrom.DistinctBy(i => i.Id).ToList();
+ var foldersRemovedFrom = _foldersRemovedFrom
+ .GroupBy(x => x.Id)
+ .Select(x => x.First())
+ .ToList();
var itemsUpdated = _itemsUpdated
- .Where(i => !_itemsAdded.Contains(i))
- .DistinctBy(i => i.Id)
- .ToList();
+ .Where(i => !_itemsAdded.Contains(i))
+ .GroupBy(x => x.Id)
+ .Select(x => x.First())
+ .ToList();
SendChangeNotifications(_itemsAdded.ToList(), itemsUpdated, _itemsRemoved.ToList(), foldersAddedTo, foldersRemovedFrom, CancellationToken.None);
diff --git a/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs b/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs
index 9e71ffceb..69784e410 100644
--- a/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs
+++ b/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs
@@ -121,7 +121,8 @@ namespace Emby.Server.Implementations.EntryPoints
var user = _userManager.GetUserById(userId);
var dtoList = changedItems
- .DistinctBy(i => i.Id)
+ .GroupBy(x => x.Id)
+ .Select(x => x.First())
.Select(i =>
{
var dto = _userDataManager.GetUserDataDto(i, user);
diff --git a/Emby.Server.Implementations/IO/FileRefresher.cs b/Emby.Server.Implementations/IO/FileRefresher.cs
index 12532a497..1cac0ba5c 100644
--- a/Emby.Server.Implementations/IO/FileRefresher.cs
+++ b/Emby.Server.Implementations/IO/FileRefresher.cs
@@ -146,8 +146,8 @@ namespace Emby.Server.Implementations.IO
.Distinct(StringComparer.OrdinalIgnoreCase)
.Select(GetAffectedBaseItem)
.Where(item => item != null)
- .DistinctBy(i => i.Id)
- .ToList();
+ .GroupBy(x => x.Id)
+ .Select(x => x.First());
foreach (var item in itemsToRefresh)
{
diff --git a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs
index 575cb1c77..c3437bcda 100644
--- a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs
+++ b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs
@@ -6,7 +6,6 @@ using System.Threading.Tasks;
using Emby.Server.Implementations.Library;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Extensions;
-using MediaBrowser.Common.Net;
using MediaBrowser.Common.Progress;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Channels;
@@ -24,7 +23,6 @@ using MediaBrowser.Controller.Sorting;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Events;
-using MediaBrowser.Model.Extensions;
using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.LiveTv;
@@ -48,7 +46,6 @@ namespace Emby.Server.Implementations.LiveTv
private readonly ILibraryManager _libraryManager;
private readonly ITaskManager _taskManager;
private readonly IJsonSerializer _jsonSerializer;
- private readonly IProviderManager _providerManager;
private readonly Func<IChannelManager> _channelManager;
private readonly IDtoService _dtoService;
@@ -85,7 +82,6 @@ namespace Emby.Server.Implementations.LiveTv
ITaskManager taskManager,
ILocalizationManager localization,
IJsonSerializer jsonSerializer,
- IProviderManager providerManager,
IFileSystem fileSystem,
Func<IChannelManager> channelManager)
{
@@ -97,7 +93,6 @@ namespace Emby.Server.Implementations.LiveTv
_taskManager = taskManager;
_localization = localization;
_jsonSerializer = jsonSerializer;
- _providerManager = providerManager;
_fileSystem = fileSystem;
_dtoService = dtoService;
_userDataManager = userDataManager;
@@ -2469,7 +2464,8 @@ namespace Emby.Server.Implementations.LiveTv
.Where(i => i != null)
.Where(i => i.IsVisibleStandalone(user))
.SelectMany(i => _libraryManager.GetCollectionFolders(i))
- .DistinctBy(i => i.Id)
+ .GroupBy(x => x.Id)
+ .Select(x => x.First())
.OrderBy(i => i.SortName)
.ToList();
diff --git a/Emby.Server.Implementations/Networking/NetworkManager.cs b/Emby.Server.Implementations/Networking/NetworkManager.cs
index f4b9f84dc..aa884664f 100644
--- a/Emby.Server.Implementations/Networking/NetworkManager.cs
+++ b/Emby.Server.Implementations/Networking/NetworkManager.cs
@@ -111,7 +111,8 @@ namespace Emby.Server.Implementations.Networking
.OrderBy(i => i.AddressFamily == AddressFamily.InterNetwork ? 0 : 1)
.ThenBy(i => listClone.IndexOf(i))
.Where(FilterIpAddress)
- .DistinctBy(i => i.ToString())
+ .GroupBy(i => i.ToString())
+ .Select(x => x.First())
.ToList();
}
@@ -429,7 +430,8 @@ namespace Emby.Server.Implementations.Networking
return new List<IPAddress>();
}
- }).DistinctBy(i => i.ToString())
+ }).GroupBy(i => i.ToString())
+ .Select(x => x.First())
.ToList();
}
diff --git a/Emby.Server.Implementations/Playlists/PlaylistImageProvider.cs b/Emby.Server.Implementations/Playlists/PlaylistImageProvider.cs
index 223153164..8a7c1492d 100644
--- a/Emby.Server.Implementations/Playlists/PlaylistImageProvider.cs
+++ b/Emby.Server.Implementations/Playlists/PlaylistImageProvider.cs
@@ -13,7 +13,6 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Playlists;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Extensions;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Querying;
@@ -64,7 +63,8 @@ namespace Emby.Server.Implementations.Playlists
})
.Where(i => i != null)
.OrderBy(i => Guid.NewGuid())
- .DistinctBy(i => i.Id)
+ .GroupBy(x => x.Id)
+ .Select(x => x.First())
.ToList();
}
}
diff --git a/Emby.Server.Implementations/ServerApplicationPaths.cs b/Emby.Server.Implementations/ServerApplicationPaths.cs
index 67389536b..36975df50 100644
--- a/Emby.Server.Implementations/ServerApplicationPaths.cs
+++ b/Emby.Server.Implementations/ServerApplicationPaths.cs
@@ -18,8 +18,13 @@ namespace Emby.Server.Implementations
string appFolderPath,
string applicationResourcesPath,
string logDirectoryPath = null,
- string configurationDirectoryPath = null)
- : base(programDataPath, appFolderPath, logDirectoryPath, configurationDirectoryPath)
+ string configurationDirectoryPath = null,
+ string cacheDirectoryPath = null)
+ : base(programDataPath,
+ appFolderPath,
+ logDirectoryPath,
+ configurationDirectoryPath,
+ cacheDirectoryPath)
{
ApplicationResourcesPath = applicationResourcesPath;
}
diff --git a/Emby.Server.Implementations/UserViews/DynamicImageProvider.cs b/Emby.Server.Implementations/UserViews/DynamicImageProvider.cs
index 937db3f23..4ec68e550 100644
--- a/Emby.Server.Implementations/UserViews/DynamicImageProvider.cs
+++ b/Emby.Server.Implementations/UserViews/DynamicImageProvider.cs
@@ -12,7 +12,6 @@ using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Extensions;
using MediaBrowser.Model.IO;
namespace Emby.Server.Implementations.UserViews
@@ -83,7 +82,8 @@ namespace Emby.Server.Implementations.UserViews
return i;
- }).DistinctBy(i => i.Id);
+ }).GroupBy(x => x.Id)
+ .Select(x => x.First());
if (isUsingCollectionStrip)
{
diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs
index 88b10cdbe..bb0eff291 100644
--- a/Jellyfin.Server/Program.cs
+++ b/Jellyfin.Server/Program.cs
@@ -202,6 +202,31 @@ namespace Jellyfin.Server
Directory.CreateDirectory(configDir);
}
+ string cacheDir = Environment.GetEnvironmentVariable("JELLYFIN_CACHE_DIR");
+ if (string.IsNullOrEmpty(cacheDir))
+ {
+ if (options.CacheDir != null)
+ {
+ cacheDir = options.CacheDir;
+ }
+ else if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ {
+ // $XDG_CACHE_HOME defines the base directory relative to which user specific non-essential data files should be stored.
+ cacheDir = Environment.GetEnvironmentVariable("XDG_CACHE_HOME");
+ // If $XDG_CACHE_HOME is either not set or empty, $HOME/.cache should be used.
+ if (string.IsNullOrEmpty(cacheDir))
+ {
+ cacheDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".cache");
+ }
+ cacheDir = Path.Combine(cacheDir, "jellyfin");
+ }
+ }
+
+ if (cacheDir != null)
+ {
+ Directory.CreateDirectory(cacheDir);
+ }
+
string logDir = Environment.GetEnvironmentVariable("JELLYFIN_LOG_DIR");
if (string.IsNullOrEmpty(logDir))
{
@@ -223,7 +248,7 @@ namespace Jellyfin.Server
string appPath = AppContext.BaseDirectory;
- return new ServerApplicationPaths(programDataPath, appPath, appPath, logDir, configDir);
+ return new ServerApplicationPaths(programDataPath, appPath, appPath, logDir, configDir, cacheDir);
}
private static async Task CreateLogger(IApplicationPaths appPaths)
diff --git a/Jellyfin.Server/StartupOptions.cs b/Jellyfin.Server/StartupOptions.cs
index 6e6b61725..5d3f7b171 100644
--- a/Jellyfin.Server/StartupOptions.cs
+++ b/Jellyfin.Server/StartupOptions.cs
@@ -11,6 +11,9 @@ namespace Jellyfin.Server
[Option('d', "datadir", Required = false, HelpText = "Path to use for the data folder (database files, etc.).")]
public string DataDir { get; set; }
+ [Option('C', "cachedir", Required = false, HelpText = "Path to use for caching.")]
+ public string CacheDir { get; set; }
+
[Option('c', "configdir", Required = false, HelpText = "Path to use for configuration data (user settings and pictures).")]
public string ConfigDir { get; set; }
diff --git a/MediaBrowser.Api/Library/LibraryService.cs b/MediaBrowser.Api/Library/LibraryService.cs
index 8e6d1febb..d44b07256 100644
--- a/MediaBrowser.Api/Library/LibraryService.cs
+++ b/MediaBrowser.Api/Library/LibraryService.cs
@@ -551,7 +551,8 @@ namespace MediaBrowser.Api.Library
Name = i.Name,
DefaultEnabled = IsSaverEnabledByDefault(i.Name, types, isNewLibrary)
})
- .DistinctBy(i => i.Name, StringComparer.OrdinalIgnoreCase)
+ .GroupBy(i => i.Name, StringComparer.OrdinalIgnoreCase)
+ .Select(x => x.First())
.ToArray();
result.MetadataReaders = plugins
@@ -561,7 +562,8 @@ namespace MediaBrowser.Api.Library
Name = i.Name,
DefaultEnabled = true
})
- .DistinctBy(i => i.Name, StringComparer.OrdinalIgnoreCase)
+ .GroupBy(i => i.Name, StringComparer.OrdinalIgnoreCase)
+ .Select(x => x.First())
.ToArray();
result.SubtitleFetchers = plugins
@@ -571,7 +573,8 @@ namespace MediaBrowser.Api.Library
Name = i.Name,
DefaultEnabled = true
})
- .DistinctBy(i => i.Name, StringComparer.OrdinalIgnoreCase)
+ .GroupBy(i => i.Name, StringComparer.OrdinalIgnoreCase)
+ .Select(x => x.First())
.ToArray();
var typeOptions = new List<LibraryTypeOptions>();
@@ -593,7 +596,8 @@ namespace MediaBrowser.Api.Library
Name = i.Name,
DefaultEnabled = IsMetadataFetcherEnabledByDefault(i.Name, type, isNewLibrary)
})
- .DistinctBy(i => i.Name, StringComparer.OrdinalIgnoreCase)
+ .GroupBy(i => i.Name, StringComparer.OrdinalIgnoreCase)
+ .Select(x => x.First())
.ToArray(),
ImageFetchers = plugins
@@ -604,7 +608,8 @@ namespace MediaBrowser.Api.Library
Name = i.Name,
DefaultEnabled = IsImageFetcherEnabledByDefault(i.Name, type, isNewLibrary)
})
- .DistinctBy(i => i.Name, StringComparer.OrdinalIgnoreCase)
+ .GroupBy(i => i.Name, StringComparer.OrdinalIgnoreCase)
+ .Select(x => x.First())
.ToArray(),
SupportedImageTypes = plugins
diff --git a/MediaBrowser.Api/Movies/MoviesService.cs b/MediaBrowser.Api/Movies/MoviesService.cs
index 996087018..91766255f 100644
--- a/MediaBrowser.Api/Movies/MoviesService.cs
+++ b/MediaBrowser.Api/Movies/MoviesService.cs
@@ -268,7 +268,8 @@ namespace MediaBrowser.Api.Movies
EnableGroupByMetadataKey = true,
DtoOptions = dtoOptions
- }).DistinctBy(i => i.GetProviderId(MetadataProviders.Imdb) ?? Guid.NewGuid().ToString("N"))
+ }).GroupBy(i => i.GetProviderId(MetadataProviders.Imdb) ?? Guid.NewGuid().ToString("N"))
+ .Select(x => x.First())
.Take(itemLimit)
.ToList();
@@ -308,7 +309,8 @@ namespace MediaBrowser.Api.Movies
EnableGroupByMetadataKey = true,
DtoOptions = dtoOptions
- }).DistinctBy(i => i.GetProviderId(MetadataProviders.Imdb) ?? Guid.NewGuid().ToString("N"))
+ }).GroupBy(i => i.GetProviderId(MetadataProviders.Imdb) ?? Guid.NewGuid().ToString("N"))
+ .Select(x => x.First())
.Take(itemLimit)
.ToList();
diff --git a/MediaBrowser.Controller/Entities/CollectionFolder.cs b/MediaBrowser.Controller/Entities/CollectionFolder.cs
index 91cfcd0ce..275052d48 100644
--- a/MediaBrowser.Controller/Entities/CollectionFolder.cs
+++ b/MediaBrowser.Controller/Entities/CollectionFolder.cs
@@ -335,7 +335,11 @@ namespace MediaBrowser.Controller.Entities
.OfType<Folder>()
.ToList();
- return PhysicalLocations.Where(i => !FileSystem.AreEqual(i, Path)).SelectMany(i => GetPhysicalParents(i, rootChildren)).DistinctBy(i => i.Id);
+ return PhysicalLocations
+ .Where(i => !FileSystem.AreEqual(i, Path))
+ .SelectMany(i => GetPhysicalParents(i, rootChildren))
+ .GroupBy(x => x.Id)
+ .Select(x => x.First());
}
private IEnumerable<Folder> GetPhysicalParents(string path, List<Folder> rootChildren)
diff --git a/MediaBrowser.Controller/Entities/TV/Series.cs b/MediaBrowser.Controller/Entities/TV/Series.cs
index 4539ab0f2..570e9389e 100644
--- a/MediaBrowser.Controller/Entities/TV/Series.cs
+++ b/MediaBrowser.Controller/Entities/TV/Series.cs
@@ -270,7 +270,7 @@ namespace MediaBrowser.Controller.Entities.TV
// This depends on settings for that series
// When this happens, remove the duplicate from season 0
- return allEpisodes.DistinctBy(i => i.Id).Reverse();
+ return allEpisodes.GroupBy(i => i.Id).Select(x => x.First()).Reverse();
}
public async Task RefreshAllMetadata(MetadataRefreshOptions refreshOptions, IProgress<double> progress, CancellationToken cancellationToken)
diff --git a/MediaBrowser.Controller/Library/NameExtensions.cs b/MediaBrowser.Controller/Library/NameExtensions.cs
index e2988a831..6b0b7e53a 100644
--- a/MediaBrowser.Controller/Library/NameExtensions.cs
+++ b/MediaBrowser.Controller/Library/NameExtensions.cs
@@ -1,7 +1,7 @@
using System;
+using System.Linq;
using System.Collections.Generic;
using MediaBrowser.Controller.Extensions;
-using MediaBrowser.Model.Extensions;
namespace MediaBrowser.Controller.Library
{
@@ -14,13 +14,11 @@ namespace MediaBrowser.Controller.Library
return string.Empty;
}
- //return name;
return name.RemoveDiacritics();
}
public static IEnumerable<string> DistinctNames(this IEnumerable<string> names)
- {
- return names.DistinctBy(RemoveDiacritics, StringComparer.OrdinalIgnoreCase);
- }
+ => names.GroupBy(RemoveDiacritics, StringComparer.OrdinalIgnoreCase)
+ .Select(x => x.First());
}
}
diff --git a/MediaBrowser.Model/Extensions/LinqExtensions.cs b/MediaBrowser.Model/Extensions/LinqExtensions.cs
deleted file mode 100644
index f0febf1d0..000000000
--- a/MediaBrowser.Model/Extensions/LinqExtensions.cs
+++ /dev/null
@@ -1,85 +0,0 @@
-using System;
-using System.Collections.Generic;
-
-// TODO: @bond Remove
-namespace MediaBrowser.Model.Extensions
-{
- // MoreLINQ - Extensions to LINQ to Objects
- // Copyright (c) 2008 Jonathan Skeet. All rights reserved.
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
-
- public static class LinqExtensions
- {
- /// <summary>
- /// Returns all distinct elements of the given source, where "distinctness"
- /// is determined via a projection and the default equality comparer for the projected type.
- /// </summary>
- /// <remarks>
- /// This operator uses deferred execution and streams the results, although
- /// a set of already-seen keys is retained. If a key is seen multiple times,
- /// only the first element with that key is returned.
- /// </remarks>
- /// <typeparam name="TSource">Type of the source sequence</typeparam>
- /// <typeparam name="TKey">Type of the projected element</typeparam>
- /// <param name="source">Source sequence</param>
- /// <param name="keySelector">Projection for determining "distinctness"</param>
- /// <returns>A sequence consisting of distinct elements from the source sequence,
- /// comparing them by the specified key projection.</returns>
-
- public static IEnumerable<TSource> DistinctBy<TSource, TKey>(this IEnumerable<TSource> source,
- Func<TSource, TKey> keySelector)
- {
- return source.DistinctBy(keySelector, null);
- }
-
- /// <summary>
- /// Returns all distinct elements of the given source, where "distinctness"
- /// is determined via a projection and the specified comparer for the projected type.
- /// </summary>
- /// <remarks>
- /// This operator uses deferred execution and streams the results, although
- /// a set of already-seen keys is retained. If a key is seen multiple times,
- /// only the first element with that key is returned.
- /// </remarks>
- /// <typeparam name="TSource">Type of the source sequence</typeparam>
- /// <typeparam name="TKey">Type of the projected element</typeparam>
- /// <param name="source">Source sequence</param>
- /// <param name="keySelector">Projection for determining "distinctness"</param>
- /// <param name="comparer">The equality comparer to use to determine whether or not keys are equal.
- /// If null, the default equality comparer for <c>TSource</c> is used.</param>
- /// <returns>A sequence consisting of distinct elements from the source sequence,
- /// comparing them by the specified key projection.</returns>
-
- public static IEnumerable<TSource> DistinctBy<TSource, TKey>(this IEnumerable<TSource> source,
- Func<TSource, TKey> keySelector, IEqualityComparer<TKey> comparer)
- {
- if (source == null) throw new ArgumentNullException(nameof(source));
- if (keySelector == null) throw new ArgumentNullException(nameof(keySelector));
- return DistinctByImpl(source, keySelector, comparer);
- }
-
- private static IEnumerable<TSource> DistinctByImpl<TSource, TKey>(IEnumerable<TSource> source,
- Func<TSource, TKey> keySelector, IEqualityComparer<TKey> comparer)
- {
- var knownKeys = new HashSet<TKey>(comparer);
- foreach (var element in source)
- {
- if (knownKeys.Add(keySelector(element)))
- {
- yield return element;
- }
- }
- }
- }
-}
diff --git a/deployment/debian-package-x64/pkg-src/conf/jellyfin b/deployment/debian-package-x64/pkg-src/conf/jellyfin
index 861865aae..c237e2d69 100644
--- a/deployment/debian-package-x64/pkg-src/conf/jellyfin
+++ b/deployment/debian-package-x64/pkg-src/conf/jellyfin
@@ -1,4 +1,5 @@
# Jellyfin default configuration options
+# This is a POSIX shell fragment
# Use this file to override the default configurations; add additional
# options with JELLYFIN_ADD_OPTS.
@@ -8,10 +9,6 @@
# to override the user or this config file's location.
#
-# This is a POSIX shell fragment
-#
-
-#
# General options
#
@@ -19,10 +16,16 @@
JELLYFIN_DATA_DIRECTORY="/var/lib/jellyfin"
JELLYFIN_CONFIG_DIRECTORY="/etc/jellyfin"
JELLYFIN_LOG_DIRECTORY="/var/log/jellyfin"
+JELLYFIN_CACHE_DIRECTORY="/var/cache/jellyfin"
+
# Restart script for in-app server control
-JELLYFIN_RESTART_SCRIPT="/usr/lib/jellyfin/restart.sh"
-# Additional options for the binary
-JELLYFIN_ADD_OPTS=""
+JELLYFIN_RESTART_OPT="--restartpath /usr/lib/jellyfin/restart.sh"
+
+# [OPTIONAL] ffmpeg binary paths
+#JELLYFIN_FFMPEG_OPTS="--ffmpeg /usr/bin/ffmpeg --ffprobe /usr/bin/ffprobe"
+
+# [OPTIONAL] Additional user-defined options for the binary
+#JELLYFIN_ADD_OPTS=""
#
# SysV init/Upstart options
@@ -31,4 +34,4 @@ JELLYFIN_ADD_OPTS=""
# Application username
JELLYFIN_USER="jellyfin"
# Full application command
-JELLYFIN_ARGS="-programdata $JELLYFIN_DATA_DIRECTORY -configdir $JELLYFIN_CONFIG_DIRECTORY -logdir $JELLYFIN_LOG_DIRECTORY -restartpath $JELLYFIN_RESTART_SCRIPT $JELLYFIN_ADD_OPTS"
+JELLYFIN_ARGS="--datadir $JELLYFIN_DATA_DIRECTORY --configdir $JELLYFIN_CONFIG_DIRECTORY --logdir $JELLYFIN_LOG_DIRECTORY --cachedir $JELLYFIN_CACHE_DIRECTORY $JELLYFIN_RESTART_OPT $JELLYFIN_FFMPEG_OPTS $JELLYFIN_ADD_OPTS"
diff --git a/deployment/debian-package-x64/pkg-src/bin/jellyfin-sudoers b/deployment/debian-package-x64/pkg-src/conf/jellyfin-sudoers
index 4eb91366b..4eb91366b 100644
--- a/deployment/debian-package-x64/pkg-src/bin/jellyfin-sudoers
+++ b/deployment/debian-package-x64/pkg-src/conf/jellyfin-sudoers
diff --git a/deployment/debian-package-x64/pkg-src/install b/deployment/debian-package-x64/pkg-src/install
index adaff7b26..994322d14 100644
--- a/deployment/debian-package-x64/pkg-src/install
+++ b/deployment/debian-package-x64/pkg-src/install
@@ -2,5 +2,5 @@ usr/lib/jellyfin usr/lib/
debian/conf/jellyfin etc/default/
debian/conf/logging.json etc/jellyfin/
debian/conf/jellyfin.service.conf etc/systemd/system/jellyfin.service.d/
-debian/bin/jellyfin-sudoers etc/sudoers.d/
+debian/conf/jellyfin-sudoers etc/sudoers.d/
debian/bin/restart.sh usr/lib/jellyfin/
diff --git a/deployment/debian-package-x64/pkg-src/jellyfin.service b/deployment/debian-package-x64/pkg-src/jellyfin.service
index c17422029..ee89d7d4b 100644
--- a/deployment/debian-package-x64/pkg-src/jellyfin.service
+++ b/deployment/debian-package-x64/pkg-src/jellyfin.service
@@ -6,7 +6,7 @@ After = network.target
Type = simple
EnvironmentFile = /etc/default/jellyfin
User = jellyfin
-ExecStart = /usr/bin/jellyfin -programdata ${JELLYFIN_DATA_DIRECTORY} -configdir ${JELLYFIN_CONFIG_DIRECTORY} -logdir ${JELLYFIN_LOG_DIRECTORY} -restartpath ${JELLYFIN_RESTART_SCRIPT} ${JELLYFIN_ADD_OPTS}
+ExecStart = /usr/bin/jellyfin --datadir ${JELLYFIN_DATA_DIRECTORY} --configdir ${JELLYFIN_CONFIG_DIRECTORY} --logdir ${JELLYFIN_LOG_DIRECTORY} --cachedir ${JELLYFIN_CACHE_DIRECTORY} ${JELLYFIN_RESTART_OPT} ${JELLYFIN_FFMPEG_OPTS} ${JELLYFIN_ADD_OPTS}
Restart = on-failure
TimeoutSec = 15
diff --git a/deployment/debian-package-x64/pkg-src/postinst b/deployment/debian-package-x64/pkg-src/postinst
index 3690d20ba..860222e05 100644
--- a/deployment/debian-package-x64/pkg-src/postinst
+++ b/deployment/debian-package-x64/pkg-src/postinst
@@ -13,6 +13,7 @@ fi
PROGRAMDATA=${JELLYFIN_DATA_DIRECTORY-/var/lib/$NAME}
CONFIGDATA=${JELLYFIN_CONFIG_DIRECTORY-/etc/$NAME}
LOGDATA=${JELLYFIN_LOG_DIRECTORY-/var/log/$NAME}
+CACHEDATA=${JELLYFIN_CACHE_DIRECTORY-/var/cache/$NAME}
case "$1" in
configure)
@@ -37,10 +38,14 @@ case "$1" in
if [[ ! -d $LOGDATA ]]; then
mkdir $LOGDATA
fi
+ # ensure $CACHEDATA exists
+ if [[ ! -d $CACHEDATA ]]; then
+ mkdir $CACHEDATA
+ fi
# Ensure permissions are correct on all config directories
- chown -R jellyfin:jellyfin $PROGRAMDATA
- chown -R jellyfin:jellyfin $CONFIGDATA
- chown -R jellyfin:jellyfin $LOGDATA
+ chown -R jellyfin $PROGRAMDATA $CONFIGDATA $LOGDATA $CACHEDATA
+ chgrp adm $PROGRAMDATA $CONFIGDATA $LOGDATA $CACHEDATA
+ chmod 0750 $PROGRAMDATA $CONFIGDATA $LOGDATA $CACHEDATA
chmod +x /usr/lib/jellyfin/restart.sh > /dev/null 2>&1 || true
diff --git a/deployment/debian-package-x64/pkg-src/postrm b/deployment/debian-package-x64/pkg-src/postrm
index 690f5d587..1d00a984e 100644
--- a/deployment/debian-package-x64/pkg-src/postrm
+++ b/deployment/debian-package-x64/pkg-src/postrm
@@ -12,7 +12,8 @@ fi
# Data directories for program data (cache, db), configs, and logs
PROGRAMDATA=${JELLYFIN_DATA_DIRECTORY-/var/lib/$NAME}
CONFIGDATA=${JELLYFIN_CONFIG_DIRECTORY-/etc/$NAME}
-LOGDATA=${JELLYFIN_DATA_DIRECTORY-/var/log/$NAME}
+LOGDATA=${JELLYFIN_LOG_DIRECTORY-/var/log/$NAME}
+CACHEDATA=${JELLYFIN_CACHE_DIRECTORY-/var/cache/$NAME}
# In case this system is running systemd, we make systemd reload the unit files
# to pick up changes.
@@ -44,6 +45,10 @@ case "$1" in
if [[ -d $LOGDATA ]]; then
rm -rf $LOGDATA
fi
+ # Remove cache dir
+ if [[ -d $CACHEDATA ]]; then
+ rm -rf $CACHEDATA
+ fi
# Remove program data dir
if [[ -d $PROGRAMDATA ]]; then
rm -rf $PROGRAMDATA
@@ -55,6 +60,7 @@ case "$1" in
# Remove anything at the default locations; catches situations where the user moved the defaults
[[ -e /etc/jellyfin ]] && rm -rf /etc/jellyfin
[[ -e /var/log/jellyfin ]] && rm -rf /var/log/jellyfin
+ [[ -e /var/cache/jellyfin ]] && rm -rf /var/cache/jellyfin
[[ -e /var/lib/jellyfin ]] && rm -rf /var/lib/jellyfin
;;
remove)
diff --git a/deployment/debian-package-x64/pkg-src/preinst b/deployment/debian-package-x64/pkg-src/preinst
index 0063e0e63..2713fb9b8 100644
--- a/deployment/debian-package-x64/pkg-src/preinst
+++ b/deployment/debian-package-x64/pkg-src/preinst
@@ -12,7 +12,8 @@ fi
# Data directories for program data (cache, db), configs, and logs
PROGRAMDATA=${JELLYFIN_DATA_DIRECTORY-/var/lib/$NAME}
CONFIGDATA=${JELLYFIN_CONFIG_DIRECTORY-/etc/$NAME}
-LOGDATA=${JELLYFIN_DATA_DIRECTORY-/var/log/$NAME}
+LOGDATA=${JELLYFIN_LOG_DIRECTORY-/var/log/$NAME}
+CACHEDATA=${JELLYFIN_CACHE_DIRECTORY-/var/cache/$NAME}
# In case this system is running systemd, we make systemd reload the unit files
# to pick up changes.
@@ -53,13 +54,16 @@ case "$1" in
# Clean up old Emby cruft that can break the user's system
[[ -f /etc/sudoers.d/emby ]] && rm -f /etc/sudoers.d/emby
- # If we have existing config or log dirs in /var/lib/jellyfin, move them into the right place
+ # If we have existing config, log, or cache dirs in /var/lib/jellyfin, move them into the right place
if [[ -d $PROGRAMDATA/config ]]; then
mv $PROGRAMDATA/config $CONFIGDATA
fi
if [[ -d $PROGRAMDATA/logs ]]; then
mv $PROGRAMDATA/logs $LOGDATA
fi
+ if [[ -d $PROGRAMDATA/logs ]]; then
+ mv $PROGRAMDATA/cache $CACHEDATA
+ fi
;;
abort-upgrade)
diff --git a/deployment/debian-package-x64/pkg-src/prerm b/deployment/debian-package-x64/pkg-src/prerm
index 4770c03c4..e965cb7d7 100644
--- a/deployment/debian-package-x64/pkg-src/prerm
+++ b/deployment/debian-package-x64/pkg-src/prerm
@@ -12,7 +12,8 @@ fi
# Data directories for program data (cache, db), configs, and logs
PROGRAMDATA=${JELLYFIN_DATA_DIRECTORY-/var/lib/$NAME}
CONFIGDATA=${JELLYFIN_CONFIG_DIRECTORY-/etc/$NAME}
-LOGDATA=${JELLYFIN_DATA_DIRECTORY-/var/log/$NAME}
+LOGDATA=${JELLYFIN_LOG_DIRECTORY-/var/log/$NAME}
+CACHEDATA=${JELLYFIN_CACHE_DIRECTORY-/var/cache/$NAME}
case "$1" in
remove|upgrade|deconfigure)
diff --git a/deployment/fedora-package-x64/pkg-src/jellyfin.env b/deployment/fedora-package-x64/pkg-src/jellyfin.env
index 827a33f46..f7f041f75 100644
--- a/deployment/fedora-package-x64/pkg-src/jellyfin.env
+++ b/deployment/fedora-package-x64/pkg-src/jellyfin.env
@@ -15,13 +15,14 @@
#
# Tell jellyfin wich ffmpeg/ffprobe to use
-# JELLYFIN_FFMPEG="-ffmpeg /usr/bin/ffmpeg -ffprobe /usr/bin/ffprobe"
+# JELLYFIN_FFMPEG="--ffmpeg /usr/bin/ffmpeg --ffprobe /usr/bin/ffprobe"
# Program directories
JELLYFIN_DATA_DIRECTORY="/var/lib/jellyfin"
JELLYFIN_CONFIG_DIRECTORY="/etc/jellyfin"
JELLYFIN_LOG_DIRECTORY="/var/log/jellyfin"
+JELLYFIN_CACHE_DIRECTORY="/var/log/jellyfin"
# In-App service control
-JELLYFIN_RESTART_OPT="-restartpath /usr/libexec/jellyfin/restart.sh"
+JELLYFIN_RESTART_OPT="--restartpath /usr/libexec/jellyfin/restart.sh"
# Additional options for the binary
-JELLYFIN_ADD_OPTS="" \ No newline at end of file
+JELLYFIN_ADD_OPTS=""
diff --git a/deployment/fedora-package-x64/pkg-src/jellyfin.service b/deployment/fedora-package-x64/pkg-src/jellyfin.service
index 0ece5b57f..56703a98a 100644
--- a/deployment/fedora-package-x64/pkg-src/jellyfin.service
+++ b/deployment/fedora-package-x64/pkg-src/jellyfin.service
@@ -5,7 +5,7 @@ Description=Jellyfin is a free software media system that puts you in control of
[Service]
EnvironmentFile=/etc/sysconfig/jellyfin
WorkingDirectory=/var/lib/jellyfin
-ExecStart=/usr/bin/jellyfin -programdata ${JELLYFIN_DATA_DIRECTORY} -configdir ${JELLYFIN_CONFIG_DIRECTORY} -logdir ${JELLYFIN_LOG_DIRECTORY} ${JELLYFIN_RESTART_OPT} ${JELLYFIN_ADD_OPTS} ${JELLYFIN_FFMPEG}
+ExecStart=/usr/bin/jellyfin --datadir ${JELLYFIN_DATA_DIRECTORY} --configdir ${JELLYFIN_CONFIG_DIRECTORY} --logdir ${JELLYFIN_LOG_DIRECTORY} --cachedir ${JELLYFIN_CACHE_DIRECTORY} ${JELLYFIN_RESTART_OPT} ${JELLYFIN_ADD_OPTS} ${JELLYFIN_FFMPEG}
TimeoutSec=15
Restart=on-failure
User=jellyfin
diff --git a/deployment/win-generic/install-jellyfin.ps1 b/deployment/win-generic/install-jellyfin.ps1
index 56c098462..b6e00e056 100644
--- a/deployment/win-generic/install-jellyfin.ps1
+++ b/deployment/win-generic/install-jellyfin.ps1
@@ -93,12 +93,12 @@ if($Quiet.IsPresent -or $Quiet -eq $true){
Copy-Item -Path $PSScriptRoot/* -DestinationPath "$Script:DefaultJellyfinInstallDirectory/" -Force -Recurse
if($Script:InstallAsService){
if($Script:InstallServiceAsUser){
- &"$Script:DefaultJellyfinInstallDirectory\nssm.exe" install Jellyfin `"$Script:DefaultJellyfinInstallDirectory\jellyfin.exe`" -programdata `"$Script:JellyfinDataDir`"
+ &"$Script:DefaultJellyfinInstallDirectory\nssm.exe" install Jellyfin `"$Script:DefaultJellyfinInstallDirectory\jellyfin.exe`" --datadir `"$Script:JellyfinDataDir`"
Start-Sleep -Milliseconds 500
&sc.exe config Jellyfin obj=".\$($Script:UserCredentials.UserName)" password="$($Script:UserCredentials.GetNetworkCredential().Password)"
&"$Script:DefaultJellyfinInstallDirectory\nssm.exe" set Jellyfin Start SERVICE_DELAYED_AUTO_START
}else{
- &"$Script:DefaultJellyfinInstallDirectory\nssm.exe" install Jellyfin `"$Script:DefaultJellyfinInstallDirectory\jellyfin.exe`" -programdata `"$Script:JellyfinDataDir`"
+ &"$Script:DefaultJellyfinInstallDirectory\nssm.exe" install Jellyfin `"$Script:DefaultJellyfinInstallDirectory\jellyfin.exe`" --datadir `"$Script:JellyfinDataDir`"
Start-Sleep -Milliseconds 500
#&"$Script:DefaultJellyfinInstallDirectory\nssm.exe" set Jellyfin ObjectName $Script:UserCredentials.UserName $Script:UserCredentials.GetNetworkCredential().Password
#Set-Service -Name Jellyfin -Credential $Script:UserCredentials
@@ -171,13 +171,13 @@ function InstallJellyfin {
if($Script:InstallAsService){
if($Script:InstallServiceAsUser){
Write-Host "Installing Service as user $($Script:UserCredentials.UserName)"
- &"$Script:DefaultJellyfinInstallDirectory\nssm.exe" install Jellyfin `"$Script:DefaultJellyfinInstallDirectory\jellyfin.exe`" -programdata `"$Script:JellyfinDataDir`"
+ &"$Script:DefaultJellyfinInstallDirectory\nssm.exe" install Jellyfin `"$Script:DefaultJellyfinInstallDirectory\jellyfin.exe`" --datadir `"$Script:JellyfinDataDir`"
Start-Sleep -Milliseconds 2000
&sc.exe config Jellyfin obj=".\$($Script:UserCredentials.UserName)" password="$($Script:UserCredentials.GetNetworkCredential().Password)"
&"$Script:DefaultJellyfinInstallDirectory\nssm.exe" set Jellyfin Start SERVICE_DELAYED_AUTO_START
}else{
Write-Host "Installing Service as LocalSystem"
- &"$Script:DefaultJellyfinInstallDirectory\nssm.exe" install Jellyfin `"$Script:DefaultJellyfinInstallDirectory\jellyfin.exe`" -programdata `"$Script:JellyfinDataDir`"
+ &"$Script:DefaultJellyfinInstallDirectory\nssm.exe" install Jellyfin `"$Script:DefaultJellyfinInstallDirectory\jellyfin.exe`" --datadir `"$Script:JellyfinDataDir`"
Start-Sleep -Milliseconds 2000
&"$Script:DefaultJellyfinInstallDirectory\nssm.exe" set Jellyfin Start SERVICE_DELAYED_AUTO_START
}
@@ -457,4 +457,4 @@ $StartProgramCheck.Add_CheckedChanged({StartJellyFinBoxCheckChanged})
#endregion GUI }
-[void]$InstallForm.ShowDialog() \ No newline at end of file
+[void]$InstallForm.ShowDialog()