Merge remote-tracking branch 'upstream/master' into quickconnect

pull/2888/head
ConfusedPolarBear 4 years ago
commit e2f16fc255

@ -33,6 +33,13 @@ jobs:
packageType: sdk packageType: sdk
version: ${{ parameters.DotNetSdkVersion }} version: ${{ parameters.DotNetSdkVersion }}
- task: DotNetCoreCLI@2
displayName: 'Install ABI CompatibilityChecker tool'
inputs:
command: custom
custom: tool
arguments: 'update compatibilitychecker -g'
- task: DownloadPipelineArtifact@2 - task: DownloadPipelineArtifact@2
displayName: "Download New Assembly Build Artifact" displayName: "Download New Assembly Build Artifact"
inputs: inputs:
@ -72,25 +79,11 @@ jobs:
overWrite: true overWrite: true
flattenFolders: true flattenFolders: true
- task: DownloadGitHubRelease@0
displayName: "Download ABI Compatibility Check Tool"
inputs:
connection: Jellyfin Release Download
userRepository: EraYaN/dotnet-compatibility
defaultVersionType: "latest"
itemPattern: "**-ci.zip"
downloadPath: "$(System.ArtifactsDirectory)"
- task: ExtractFiles@1
displayName: "Extract ABI Compatibility Check Tool"
inputs:
archiveFilePatterns: "$(System.ArtifactsDirectory)/*-ci.zip"
destinationFolder: $(System.ArtifactsDirectory)/tools
cleanDestinationFolder: true
# The `--warnings-only` switch will swallow the return code and not emit any errors. # The `--warnings-only` switch will swallow the return code and not emit any errors.
- task: CmdLine@2 - task: DotNetCoreCLI@2
displayName: "Execute ABI Compatibility Check Tool" displayName: 'Execute ABI Compatibility Check Tool'
inputs: inputs:
script: "dotnet tools/CompatibilityCheckerCLI.dll current-release/$(AssemblyFileName) new-release/$(AssemblyFileName) --azure-pipelines --warnings-only" command: custom
custom: compat
arguments: 'current-release/$(AssemblyFileName) new-release/$(AssemblyFileName) --azure-pipelines --warnings-only'
workingDirectory: $(System.ArtifactsDirectory) workingDirectory: $(System.ArtifactsDirectory)

@ -0,0 +1,9 @@
version: 2
updates:
- package-ecosystem: nuget
directory: "/"
schedule:
interval: weekly
time: '12:00'
open-pull-requests-limit: 10

@ -7,6 +7,7 @@
- [anthonylavado](https://github.com/anthonylavado) - [anthonylavado](https://github.com/anthonylavado)
- [Artiume](https://github.com/Artiume) - [Artiume](https://github.com/Artiume)
- [AThomsen](https://github.com/AThomsen) - [AThomsen](https://github.com/AThomsen)
- [barronpm](https://github.com/barronpm)
- [bilde2910](https://github.com/bilde2910) - [bilde2910](https://github.com/bilde2910)
- [bfayers](https://github.com/bfayers) - [bfayers](https://github.com/bfayers)
- [BnMcG](https://github.com/BnMcG) - [BnMcG](https://github.com/BnMcG)
@ -131,6 +132,7 @@
- [XVicarious](https://github.com/XVicarious) - [XVicarious](https://github.com/XVicarious)
- [YouKnowBlom](https://github.com/YouKnowBlom) - [YouKnowBlom](https://github.com/YouKnowBlom)
- [KristupasSavickas](https://github.com/KristupasSavickas) - [KristupasSavickas](https://github.com/KristupasSavickas)
- [Pusta](https://github.com/pusta)
# Emby Contributors # Emby Contributors

@ -7,6 +7,7 @@ namespace DvdLib.Ifo
public class Cell public class Cell
{ {
public CellPlaybackInfo PlaybackInfo { get; private set; } public CellPlaybackInfo PlaybackInfo { get; private set; }
public CellPositionInfo PositionInfo { get; private set; } public CellPositionInfo PositionInfo { get; private set; }
internal void ParsePlayback(BinaryReader br) internal void ParsePlayback(BinaryReader br)

@ -5,7 +5,9 @@ namespace DvdLib.Ifo
public class Chapter public class Chapter
{ {
public ushort ProgramChainNumber { get; private set; } public ushort ProgramChainNumber { get; private set; }
public ushort ProgramNumber { get; private set; } public ushort ProgramNumber { get; private set; }
public uint ChapterNumber { get; private set; } public uint ChapterNumber { get; private set; }
public Chapter(ushort pgcNum, ushort programNum, uint chapterNum) public Chapter(ushort pgcNum, ushort programNum, uint chapterNum)

@ -22,7 +22,9 @@ namespace DvdLib.Ifo
public readonly List<Cell> Cells; public readonly List<Cell> Cells;
public DvdTime PlaybackTime { get; private set; } public DvdTime PlaybackTime { get; private set; }
public UserOperation ProhibitedUserOperations { get; private set; } public UserOperation ProhibitedUserOperations { get; private set; }
public byte[] AudioStreamControl { get; private set; } // 8*2 entries public byte[] AudioStreamControl { get; private set; } // 8*2 entries
public byte[] SubpictureStreamControl { get; private set; } // 32*4 entries public byte[] SubpictureStreamControl { get; private set; } // 32*4 entries
@ -33,9 +35,11 @@ namespace DvdLib.Ifo
private ushort _goupProgramNumber; private ushort _goupProgramNumber;
public ProgramPlaybackMode PlaybackMode { get; private set; } public ProgramPlaybackMode PlaybackMode { get; private set; }
public uint ProgramCount { get; private set; } public uint ProgramCount { get; private set; }
public byte StillTime { get; private set; } public byte StillTime { get; private set; }
public byte[] Palette { get; private set; } // 16*4 entries public byte[] Palette { get; private set; } // 16*4 entries
private ushort _commandTableOffset; private ushort _commandTableOffset;

@ -8,8 +8,11 @@ namespace DvdLib.Ifo
public class Title public class Title
{ {
public uint TitleNumber { get; private set; } public uint TitleNumber { get; private set; }
public uint AngleCount { get; private set; } public uint AngleCount { get; private set; }
public ushort ChapterCount { get; private set; } public ushort ChapterCount { get; private set; }
public byte VideoTitleSetNumber { get; private set; } public byte VideoTitleSetNumber { get; private set; }
private ushort _parentalManagementMask; private ushort _parentalManagementMask;
@ -17,6 +20,7 @@ namespace DvdLib.Ifo
private uint _vtsStartSector; // relative to start of entire disk private uint _vtsStartSector; // relative to start of entire disk
public ProgramChain EntryProgramChain { get; private set; } public ProgramChain EntryProgramChain { get; private set; }
public readonly List<ProgramChain> ProgramChains; public readonly List<ProgramChain> ProgramChains;
public readonly List<Chapter> Chapters; public readonly List<Chapter> Chapters;

@ -4,11 +4,12 @@ using System;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Emby.Dlna.Service; using Emby.Dlna.Service;
using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums;
using MediaBrowser.Common.Net; using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dlna; using MediaBrowser.Controller.Dlna;
using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Controller.TV; using MediaBrowser.Controller.TV;
@ -32,7 +33,8 @@ namespace Emby.Dlna.ContentDirectory
private readonly IMediaEncoder _mediaEncoder; private readonly IMediaEncoder _mediaEncoder;
private readonly ITVSeriesManager _tvSeriesManager; private readonly ITVSeriesManager _tvSeriesManager;
public ContentDirectory(IDlnaManager dlna, public ContentDirectory(
IDlnaManager dlna,
IUserDataManager userDataManager, IUserDataManager userDataManager,
IImageProcessor imageProcessor, IImageProcessor imageProcessor,
ILibraryManager libraryManager, ILibraryManager libraryManager,
@ -131,7 +133,7 @@ namespace Emby.Dlna.ContentDirectory
foreach (var user in _userManager.Users) foreach (var user in _userManager.Users)
{ {
if (user.Policy.IsAdministrator) if (user.HasPermission(PermissionKind.IsAdministrator))
{ {
return user; return user;
} }

@ -10,6 +10,7 @@ using System.Threading;
using System.Xml; using System.Xml;
using Emby.Dlna.Didl; using Emby.Dlna.Didl;
using Emby.Dlna.Service; using Emby.Dlna.Service;
using Jellyfin.Data.Entities;
using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Drawing;
@ -17,7 +18,6 @@ using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Controller.MediaEncoding;
@ -28,6 +28,12 @@ using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Globalization; using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.Querying; using MediaBrowser.Model.Querying;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Book = MediaBrowser.Controller.Entities.Book;
using Episode = MediaBrowser.Controller.Entities.TV.Episode;
using Genre = MediaBrowser.Controller.Entities.Genre;
using Movie = MediaBrowser.Controller.Entities.Movies.Movie;
using MusicAlbum = MediaBrowser.Controller.Entities.Audio.MusicAlbum;
using Series = MediaBrowser.Controller.Entities.TV.Series;
namespace Emby.Dlna.ContentDirectory namespace Emby.Dlna.ContentDirectory
{ {
@ -731,7 +737,7 @@ namespace Emby.Dlna.ContentDirectory
return GetGenres(item, user, query); return GetGenres(item, user, query);
} }
var array = new ServerItem[] var array = new[]
{ {
new ServerItem(item) new ServerItem(item)
{ {
@ -1115,7 +1121,7 @@ namespace Emby.Dlna.ContentDirectory
private QueryResult<ServerItem> GetMusicPlaylists(User user, InternalItemsQuery query) private QueryResult<ServerItem> GetMusicPlaylists(User user, InternalItemsQuery query)
{ {
query.Parent = null; query.Parent = null;
query.IncludeItemTypes = new[] { typeof(Playlist).Name }; query.IncludeItemTypes = new[] { nameof(Playlist) };
query.SetUser(user); query.SetUser(user);
query.Recursive = true; query.Recursive = true;
@ -1132,10 +1138,9 @@ namespace Emby.Dlna.ContentDirectory
{ {
UserId = user.Id, UserId = user.Id,
Limit = 50, Limit = 50,
IncludeItemTypes = new[] { typeof(Audio).Name }, IncludeItemTypes = new[] { nameof(Audio) },
ParentId = parent == null ? Guid.Empty : parent.Id, ParentId = parent?.Id ?? Guid.Empty,
GroupItems = true GroupItems = true
}, query.DtoOptions).Select(i => i.Item1 ?? i.Item2.FirstOrDefault()).Where(i => i != null).ToArray(); }, query.DtoOptions).Select(i => i.Item1 ?? i.Item2.FirstOrDefault()).Where(i => i != null).ToArray();
return ToResult(items); return ToResult(items);
@ -1150,7 +1155,6 @@ namespace Emby.Dlna.ContentDirectory
Limit = query.Limit, Limit = query.Limit,
StartIndex = query.StartIndex, StartIndex = query.StartIndex,
UserId = query.User.Id UserId = query.User.Id
}, new[] { parent }, query.DtoOptions); }, new[] { parent }, query.DtoOptions);
return ToResult(result); return ToResult(result);
@ -1167,7 +1171,6 @@ namespace Emby.Dlna.ContentDirectory
IncludeItemTypes = new[] { typeof(Episode).Name }, IncludeItemTypes = new[] { typeof(Episode).Name },
ParentId = parent == null ? Guid.Empty : parent.Id, ParentId = parent == null ? Guid.Empty : parent.Id,
GroupItems = false GroupItems = false
}, query.DtoOptions).Select(i => i.Item1 ?? i.Item2.FirstOrDefault()).Where(i => i != null).ToArray(); }, query.DtoOptions).Select(i => i.Item1 ?? i.Item2.FirstOrDefault()).Where(i => i != null).ToArray();
return ToResult(items); return ToResult(items);
@ -1177,14 +1180,14 @@ namespace Emby.Dlna.ContentDirectory
{ {
query.OrderBy = Array.Empty<(string, SortOrder)>(); query.OrderBy = Array.Empty<(string, SortOrder)>();
var items = _userViewManager.GetLatestItems(new LatestItemsQuery var items = _userViewManager.GetLatestItems(
new LatestItemsQuery
{ {
UserId = user.Id, UserId = user.Id,
Limit = 50, Limit = 50,
IncludeItemTypes = new[] { typeof(Movie).Name }, IncludeItemTypes = new[] { nameof(Movie) },
ParentId = parent == null ? Guid.Empty : parent.Id, ParentId = parent?.Id ?? Guid.Empty,
GroupItems = true GroupItems = true
}, query.DtoOptions).Select(i => i.Item1 ?? i.Item2.FirstOrDefault()).Where(i => i != null).ToArray(); }, query.DtoOptions).Select(i => i.Item1 ?? i.Item2.FirstOrDefault()).Where(i => i != null).ToArray();
return ToResult(items); return ToResult(items);
@ -1217,7 +1220,11 @@ namespace Emby.Dlna.ContentDirectory
Recursive = true, Recursive = true,
ParentId = parentId, ParentId = parentId,
GenreIds = new[] { item.Id }, GenreIds = new[] { item.Id },
IncludeItemTypes = new[] { typeof(Movie).Name, typeof(Series).Name }, IncludeItemTypes = new[]
{
nameof(Movie),
nameof(Series)
},
Limit = limit, Limit = limit,
StartIndex = startIndex, StartIndex = startIndex,
DtoOptions = GetDtoOptions() DtoOptions = GetDtoOptions()
@ -1350,6 +1357,7 @@ namespace Emby.Dlna.ContentDirectory
internal class ServerItem internal class ServerItem
{ {
public BaseItem Item { get; set; } public BaseItem Item { get; set; }
public StubType? StubType { get; set; } public StubType? StubType { get; set; }
public ServerItem(BaseItem item) public ServerItem(BaseItem item)

@ -6,14 +6,13 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Xml; using System.Xml;
using Emby.Dlna.Configuration;
using Emby.Dlna.ContentDirectory; using Emby.Dlna.ContentDirectory;
using Jellyfin.Data.Entities;
using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Controller.Playlists; using MediaBrowser.Controller.Playlists;
@ -23,6 +22,13 @@ using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Globalization; using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.Net; using MediaBrowser.Model.Net;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Episode = MediaBrowser.Controller.Entities.TV.Episode;
using Genre = MediaBrowser.Controller.Entities.Genre;
using Movie = MediaBrowser.Controller.Entities.Movies.Movie;
using MusicAlbum = MediaBrowser.Controller.Entities.Audio.MusicAlbum;
using Season = MediaBrowser.Controller.Entities.TV.Season;
using Series = MediaBrowser.Controller.Entities.TV.Series;
using XmlAttribute = MediaBrowser.Model.Dlna.XmlAttribute;
namespace Emby.Dlna.Didl namespace Emby.Dlna.Didl
{ {
@ -421,7 +427,6 @@ namespace Emby.Dlna.Didl
case StubType.FavoriteSeries: return _localization.GetLocalizedString("HeaderFavoriteShows"); case StubType.FavoriteSeries: return _localization.GetLocalizedString("HeaderFavoriteShows");
case StubType.FavoriteEpisodes: return _localization.GetLocalizedString("HeaderFavoriteEpisodes"); case StubType.FavoriteEpisodes: return _localization.GetLocalizedString("HeaderFavoriteEpisodes");
case StubType.Series: return _localization.GetLocalizedString("Shows"); case StubType.Series: return _localization.GetLocalizedString("Shows");
default: break;
} }
} }
@ -670,7 +675,7 @@ namespace Emby.Dlna.Didl
return; return;
} }
MediaBrowser.Model.Dlna.XmlAttribute secAttribute = null; XmlAttribute secAttribute = null;
foreach (var attribute in _profile.XmlRootAttributes) foreach (var attribute in _profile.XmlRootAttributes)
{ {
if (string.Equals(attribute.Name, "xmlns:sec", StringComparison.OrdinalIgnoreCase)) if (string.Equals(attribute.Name, "xmlns:sec", StringComparison.OrdinalIgnoreCase))
@ -700,7 +705,7 @@ namespace Emby.Dlna.Didl
} }
/// <summary> /// <summary>
/// Adds fields used by both items and folders /// Adds fields used by both items and folders.
/// </summary> /// </summary>
private void AddCommonFields(BaseItem item, StubType? itemStubType, BaseItem context, XmlWriter writer, Filter filter) private void AddCommonFields(BaseItem item, StubType? itemStubType, BaseItem context, XmlWriter writer, Filter filter)
{ {
@ -760,6 +765,7 @@ namespace Emby.Dlna.Didl
{ {
AddValue(writer, "dc", "rating", item.OfficialRating, NS_DC); AddValue(writer, "dc", "rating", item.OfficialRating, NS_DC);
} }
if (filter.Contains("upnp:rating")) if (filter.Contains("upnp:rating"))
{ {
AddValue(writer, "upnp", "rating", item.OfficialRating, NS_UPNP); AddValue(writer, "upnp", "rating", item.OfficialRating, NS_UPNP);
@ -995,7 +1001,6 @@ namespace Emby.Dlna.Didl
} }
AddImageResElement(item, writer, 160, 160, "jpg", "JPEG_TN"); AddImageResElement(item, writer, 160, 160, "jpg", "JPEG_TN");
} }
private void AddImageResElement( private void AddImageResElement(
@ -1048,10 +1053,12 @@ namespace Emby.Dlna.Didl
{ {
return GetImageInfo(item, ImageType.Primary); return GetImageInfo(item, ImageType.Primary);
} }
if (item.HasImage(ImageType.Thumb)) if (item.HasImage(ImageType.Thumb))
{ {
return GetImageInfo(item, ImageType.Thumb); return GetImageInfo(item, ImageType.Thumb);
} }
if (item.HasImage(ImageType.Backdrop)) if (item.HasImage(ImageType.Backdrop))
{ {
if (item is Channel) if (item is Channel)
@ -1135,7 +1142,6 @@ namespace Emby.Dlna.Didl
width = null; width = null;
height = null; height = null;
} }
else if (width == -1 || height == -1) else if (width == -1 || height == -1)
{ {
width = null; width = null;

@ -12,7 +12,6 @@ namespace Emby.Dlna.Didl
public Filter() public Filter()
: this("*") : this("*")
{ {
} }
public Filter(string filter) public Filter(string filter)

@ -31,7 +31,7 @@ namespace Emby.Dlna
private readonly IApplicationPaths _appPaths; private readonly IApplicationPaths _appPaths;
private readonly IXmlSerializer _xmlSerializer; private readonly IXmlSerializer _xmlSerializer;
private readonly IFileSystem _fileSystem; private readonly IFileSystem _fileSystem;
private readonly ILogger _logger; private readonly ILogger<DlnaManager> _logger;
private readonly IJsonSerializer _jsonSerializer; private readonly IJsonSerializer _jsonSerializer;
private readonly IServerApplicationHost _appHost; private readonly IServerApplicationHost _appHost;
private static readonly Assembly _assembly = typeof(DlnaManager).Assembly; private static readonly Assembly _assembly = typeof(DlnaManager).Assembly;
@ -49,7 +49,7 @@ namespace Emby.Dlna
_xmlSerializer = xmlSerializer; _xmlSerializer = xmlSerializer;
_fileSystem = fileSystem; _fileSystem = fileSystem;
_appPaths = appPaths; _appPaths = appPaths;
_logger = loggerFactory.CreateLogger("Dlna"); _logger = loggerFactory.CreateLogger<DlnaManager>();
_jsonSerializer = jsonSerializer; _jsonSerializer = jsonSerializer;
_appHost = appHost; _appHost = appHost;
} }
@ -88,7 +88,6 @@ namespace Emby.Dlna
.Select(i => i.Item2) .Select(i => i.Item2)
.ToList(); .ToList();
} }
} }
public DeviceProfile GetDefaultProfile() public DeviceProfile GetDefaultProfile()
@ -439,6 +438,7 @@ namespace Emby.Dlna
{ {
throw new ArgumentException("Profile is missing Id"); throw new ArgumentException("Profile is missing Id");
} }
if (string.IsNullOrEmpty(profile.Name)) if (string.IsNullOrEmpty(profile.Name))
{ {
throw new ArgumentException("Profile is missing Name"); throw new ArgumentException("Profile is missing Name");
@ -464,6 +464,7 @@ namespace Emby.Dlna
{ {
_profiles[path] = new Tuple<InternalProfileInfo, DeviceProfile>(GetInternalProfileInfo(_fileSystem.GetFileInfo(path), type), profile); _profiles[path] = new Tuple<InternalProfileInfo, DeviceProfile>(GetInternalProfileInfo(_fileSystem.GetFileInfo(path), type), profile);
} }
SerializeToXml(profile, path); SerializeToXml(profile, path);
} }
@ -474,7 +475,7 @@ namespace Emby.Dlna
/// <summary> /// <summary>
/// Recreates the object using serialization, to ensure it's not a subclass. /// Recreates the object using serialization, to ensure it's not a subclass.
/// If it's a subclass it may not serlialize properly to xml (different root element tag name) /// If it's a subclass it may not serlialize properly to xml (different root element tag name).
/// </summary> /// </summary>
/// <param name="profile"></param> /// <param name="profile"></param>
/// <returns></returns> /// <returns></returns>
@ -493,6 +494,7 @@ namespace Emby.Dlna
class InternalProfileInfo class InternalProfileInfo
{ {
internal DeviceProfileInfo Info { get; set; } internal DeviceProfileInfo Info { get; set; }
internal string Path { get; set; } internal string Path { get; set; }
} }

@ -31,7 +31,8 @@ namespace Emby.Dlna.Eventing
public EventSubscriptionResponse RenewEventSubscription(string subscriptionId, string notificationType, string requestedTimeoutString, string callbackUrl) public EventSubscriptionResponse RenewEventSubscription(string subscriptionId, string notificationType, string requestedTimeoutString, string callbackUrl)
{ {
var subscription = GetSubscription(subscriptionId, false); var subscription = GetSubscription(subscriptionId, false);
if (subscription != null)
{
subscription.TimeoutSeconds = ParseTimeout(requestedTimeoutString) ?? 300; subscription.TimeoutSeconds = ParseTimeout(requestedTimeoutString) ?? 300;
int timeoutSeconds = subscription.TimeoutSeconds; int timeoutSeconds = subscription.TimeoutSeconds;
subscription.SubscriptionTime = DateTime.UtcNow; subscription.SubscriptionTime = DateTime.UtcNow;
@ -45,6 +46,13 @@ namespace Emby.Dlna.Eventing
return GetEventSubscriptionResponse(subscriptionId, requestedTimeoutString, timeoutSeconds); return GetEventSubscriptionResponse(subscriptionId, requestedTimeoutString, timeoutSeconds);
} }
return new EventSubscriptionResponse
{
Content = string.Empty,
ContentType = "text/plain"
};
}
public EventSubscriptionResponse CreateEventSubscription(string notificationType, string requestedTimeoutString, string callbackUrl) public EventSubscriptionResponse CreateEventSubscription(string notificationType, string requestedTimeoutString, string callbackUrl)
{ {
var timeout = ParseTimeout(requestedTimeoutString) ?? 300; var timeout = ParseTimeout(requestedTimeoutString) ?? 300;
@ -150,6 +158,7 @@ namespace Emby.Dlna.Eventing
builder.Append("</" + key + ">"); builder.Append("</" + key + ">");
builder.Append("</e:property>"); builder.Append("</e:property>");
} }
builder.Append("</e:propertyset>"); builder.Append("</e:propertyset>");
var options = new HttpRequestOptions var options = new HttpRequestOptions
@ -169,7 +178,6 @@ namespace Emby.Dlna.Eventing
{ {
using (await _httpClient.SendAsync(options, new HttpMethod("NOTIFY")).ConfigureAwait(false)) using (await _httpClient.SendAsync(options, new HttpMethod("NOTIFY")).ConfigureAwait(false))
{ {
} }
} }
catch (OperationCanceledException) catch (OperationCanceledException)

@ -7,10 +7,13 @@ namespace Emby.Dlna.Eventing
public class EventSubscription public class EventSubscription
{ {
public string Id { get; set; } public string Id { get; set; }
public string CallbackUrl { get; set; } public string CallbackUrl { get; set; }
public string NotificationType { get; set; } public string NotificationType { get; set; }
public DateTime SubscriptionTime { get; set; } public DateTime SubscriptionTime { get; set; }
public int TimeoutSeconds { get; set; } public int TimeoutSeconds { get; set; }
public long TriggerCount { get; set; } public long TriggerCount { get; set; }

@ -33,7 +33,7 @@ namespace Emby.Dlna.Main
public class DlnaEntryPoint : IServerEntryPoint, IRunBeforeStartup public class DlnaEntryPoint : IServerEntryPoint, IRunBeforeStartup
{ {
private readonly IServerConfigurationManager _config; private readonly IServerConfigurationManager _config;
private readonly ILogger _logger; private readonly ILogger<DlnaEntryPoint> _logger;
private readonly IServerApplicationHost _appHost; private readonly IServerApplicationHost _appHost;
private PlayToManager _manager; private PlayToManager _manager;
@ -65,7 +65,8 @@ namespace Emby.Dlna.Main
public static DlnaEntryPoint Current; public static DlnaEntryPoint Current;
public DlnaEntryPoint(IServerConfigurationManager config, public DlnaEntryPoint(
IServerConfigurationManager config,
ILoggerFactory loggerFactory, ILoggerFactory loggerFactory,
IServerApplicationHost appHost, IServerApplicationHost appHost,
ISessionManager sessionManager, ISessionManager sessionManager,
@ -99,7 +100,7 @@ namespace Emby.Dlna.Main
_mediaEncoder = mediaEncoder; _mediaEncoder = mediaEncoder;
_socketFactory = socketFactory; _socketFactory = socketFactory;
_networkManager = networkManager; _networkManager = networkManager;
_logger = loggerFactory.CreateLogger("Dlna"); _logger = loggerFactory.CreateLogger<DlnaEntryPoint>();
ContentDirectory = new ContentDirectory.ContentDirectory( ContentDirectory = new ContentDirectory.ContentDirectory(
dlnaManager, dlnaManager,
@ -319,6 +320,7 @@ namespace Emby.Dlna.Main
{ {
guid = text.GetMD5(); guid = text.GetMD5();
} }
return guid.ToString("N", CultureInfo.InvariantCulture); return guid.ToString("N", CultureInfo.InvariantCulture);
} }
@ -347,7 +349,8 @@ namespace Emby.Dlna.Main
try try
{ {
_manager = new PlayToManager(_logger, _manager = new PlayToManager(
_logger,
_sessionManager, _sessionManager,
_libraryManager, _libraryManager,
_userManager, _userManager,
@ -386,6 +389,7 @@ namespace Emby.Dlna.Main
{ {
_logger.LogError(ex, "Error disposing PlayTo manager"); _logger.LogError(ex, "Error disposing PlayTo manager");
} }
_manager = null; _manager = null;
} }
} }

@ -37,6 +37,7 @@ namespace Emby.Dlna.PlayTo
RefreshVolumeIfNeeded().GetAwaiter().GetResult(); RefreshVolumeIfNeeded().GetAwaiter().GetResult();
return _volume; return _volume;
} }
set => _volume = value; set => _volume = value;
} }
@ -232,7 +233,7 @@ namespace Emby.Dlna.PlayTo
} }
/// <summary> /// <summary>
/// Sets volume on a scale of 0-100 /// Sets volume on a scale of 0-100.
/// </summary> /// </summary>
public async Task SetVolume(int value, CancellationToken cancellationToken) public async Task SetVolume(int value, CancellationToken cancellationToken)
{ {
@ -494,6 +495,7 @@ namespace Emby.Dlna.PlayTo
return; return;
} }
} }
RestartTimerInactive(); RestartTimerInactive();
} }
} }
@ -794,7 +796,6 @@ namespace Emby.Dlna.PlayTo
} }
catch (XmlException) catch (XmlException)
{ {
} }
// first try to add a root node with a dlna namesapce // first try to add a root node with a dlna namesapce
@ -806,7 +807,6 @@ namespace Emby.Dlna.PlayTo
} }
catch (XmlException) catch (XmlException)
{ {
} }
// some devices send back invalid xml // some devices send back invalid xml
@ -816,7 +816,6 @@ namespace Emby.Dlna.PlayTo
} }
catch (XmlException) catch (XmlException)
{ {
} }
return null; return null;

@ -7,6 +7,7 @@ using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Emby.Dlna.Didl; using Emby.Dlna.Didl;
using Jellyfin.Data.Entities;
using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Dlna; using MediaBrowser.Controller.Dlna;
using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Drawing;
@ -22,6 +23,7 @@ using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.Session; using MediaBrowser.Model.Session;
using Microsoft.AspNetCore.WebUtilities; using Microsoft.AspNetCore.WebUtilities;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Photo = MediaBrowser.Controller.Entities.Photo;
namespace Emby.Dlna.PlayTo namespace Emby.Dlna.PlayTo
{ {
@ -423,6 +425,7 @@ namespace Emby.Dlna.PlayTo
await _device.SetAvTransport(newItem.StreamUrl, GetDlnaHeaders(newItem), newItem.Didl, CancellationToken.None).ConfigureAwait(false); await _device.SetAvTransport(newItem.StreamUrl, GetDlnaHeaders(newItem), newItem.Didl, CancellationToken.None).ConfigureAwait(false);
return; return;
} }
await SeekAfterTransportChange(newPosition, CancellationToken.None).ConfigureAwait(false); await SeekAfterTransportChange(newPosition, CancellationToken.None).ConfigureAwait(false);
} }
} }
@ -446,7 +449,13 @@ namespace Emby.Dlna.PlayTo
} }
} }
private PlaylistItem CreatePlaylistItem(BaseItem item, User user, long startPostionTicks, string mediaSourceId, int? audioStreamIndex, int? subtitleStreamIndex) private PlaylistItem CreatePlaylistItem(
BaseItem item,
User user,
long startPostionTicks,
string mediaSourceId,
int? audioStreamIndex,
int? subtitleStreamIndex)
{ {
var deviceInfo = _device.Properties; var deviceInfo = _device.Properties;
@ -705,6 +714,7 @@ namespace Emby.Dlna.PlayTo
throw new ArgumentException("Volume argument cannot be null"); throw new ArgumentException("Volume argument cannot be null");
} }
default: default:
return Task.CompletedTask; return Task.CompletedTask;
} }
@ -790,12 +800,15 @@ namespace Emby.Dlna.PlayTo
public int? SubtitleStreamIndex { get; set; } public int? SubtitleStreamIndex { get; set; }
public string DeviceProfileId { get; set; } public string DeviceProfileId { get; set; }
public string DeviceId { get; set; } public string DeviceId { get; set; }
public string MediaSourceId { get; set; } public string MediaSourceId { get; set; }
public string LiveStreamId { get; set; } public string LiveStreamId { get; set; }
public BaseItem Item { get; set; } public BaseItem Item { get; set; }
private MediaSourceInfo MediaSource; private MediaSourceInfo MediaSource;
private IMediaSourceManager _mediaSourceManager; private IMediaSourceManager _mediaSourceManager;

@ -112,7 +112,6 @@ namespace Emby.Dlna.PlayTo
} }
catch (OperationCanceledException) catch (OperationCanceledException)
{ {
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -133,6 +132,7 @@ namespace Emby.Dlna.PlayTo
usn = usn.Substring(index); usn = usn.Substring(index);
found = true; found = true;
} }
index = usn.IndexOf("::", StringComparison.OrdinalIgnoreCase); index = usn.IndexOf("::", StringComparison.OrdinalIgnoreCase);
if (index != -1) if (index != -1)
{ {
@ -184,7 +184,8 @@ namespace Emby.Dlna.PlayTo
serverAddress = _appHost.GetLocalApiUrl(info.LocalIpAddress); serverAddress = _appHost.GetLocalApiUrl(info.LocalIpAddress);
} }
controller = new PlayToController(sessionInfo, controller = new PlayToController(
sessionInfo,
_sessionManager, _sessionManager,
_libraryManager, _libraryManager,
_logger, _logger,
@ -242,7 +243,6 @@ namespace Emby.Dlna.PlayTo
} }
catch catch
{ {
} }
_sessionLock.Dispose(); _sessionLock.Dispose();

@ -12,6 +12,7 @@ namespace Emby.Dlna.PlayTo
public class MediaChangedEventArgs : EventArgs public class MediaChangedEventArgs : EventArgs
{ {
public uBaseObject OldMediaInfo { get; set; } public uBaseObject OldMediaInfo { get; set; }
public uBaseObject NewMediaInfo { get; set; } public uBaseObject NewMediaInfo { get; set; }
} }
} }

@ -91,7 +91,6 @@ namespace Emby.Dlna.PlayTo
using (await _httpClient.SendAsync(options, new HttpMethod("SUBSCRIBE")).ConfigureAwait(false)) using (await _httpClient.SendAsync(options, new HttpMethod("SUBSCRIBE")).ConfigureAwait(false))
{ {
} }
} }

@ -44,10 +44,12 @@ namespace Emby.Dlna.PlayTo
{ {
return MediaBrowser.Model.Entities.MediaType.Audio; return MediaBrowser.Model.Entities.MediaType.Audio;
} }
if (classType.IndexOf(MediaBrowser.Model.Entities.MediaType.Video, StringComparison.Ordinal) != -1) if (classType.IndexOf(MediaBrowser.Model.Entities.MediaType.Video, StringComparison.Ordinal) != -1)
{ {
return MediaBrowser.Model.Entities.MediaType.Video; return MediaBrowser.Model.Entities.MediaType.Video;
} }
if (classType.IndexOf("image", StringComparison.Ordinal) != -1) if (classType.IndexOf("image", StringComparison.Ordinal) != -1)
{ {
return MediaBrowser.Model.Entities.MediaType.Photo; return MediaBrowser.Model.Entities.MediaType.Photo;

@ -134,6 +134,7 @@ namespace Emby.Dlna.Server
return result; return result;
} }
} }
return c.ToString(CultureInfo.InvariantCulture); return c.ToString(CultureInfo.InvariantCulture);
} }
@ -157,18 +158,22 @@ namespace Emby.Dlna.Server
{ {
break; break;
} }
if (stringBuilder == null) if (stringBuilder == null)
{ {
stringBuilder = new StringBuilder(); stringBuilder = new StringBuilder();
} }
stringBuilder.Append(str, num, num2 - num); stringBuilder.Append(str, num, num2 - num);
stringBuilder.Append(GetEscapeSequence(str[num2])); stringBuilder.Append(GetEscapeSequence(str[num2]));
num = num2 + 1; num = num2 + 1;
} }
if (stringBuilder == null) if (stringBuilder == null)
{ {
return str; return str;
} }
stringBuilder.Append(str, num, length - num); stringBuilder.Append(str, num, length - num);
return stringBuilder.ToString(); return stringBuilder.ToString();
} }

@ -18,6 +18,7 @@ namespace Emby.Dlna.Service
private const string NS_SOAPENV = "http://schemas.xmlsoap.org/soap/envelope/"; private const string NS_SOAPENV = "http://schemas.xmlsoap.org/soap/envelope/";
protected IServerConfigurationManager Config { get; } protected IServerConfigurationManager Config { get; }
protected ILogger Logger { get; } protected ILogger Logger { get; }
protected BaseControlHandler(IServerConfigurationManager config, ILogger logger) protected BaseControlHandler(IServerConfigurationManager config, ILogger logger)
@ -135,6 +136,7 @@ namespace Emby.Dlna.Service
break; break;
} }
default: default:
{ {
await reader.SkipAsync().ConfigureAwait(false); await reader.SkipAsync().ConfigureAwait(false);
@ -211,7 +213,9 @@ namespace Emby.Dlna.Service
private class ControlRequestInfo private class ControlRequestInfo
{ {
public string LocalName { get; set; } public string LocalName { get; set; }
public string NamespaceURI { get; set; } public string NamespaceURI { get; set; }
public Dictionary<string, string> Headers { get; } = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); public Dictionary<string, string> Headers { get; } = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
} }

@ -17,7 +17,7 @@ namespace Emby.Dlna.Service
Logger = logger; Logger = logger;
HttpClient = httpClient; HttpClient = httpClient;
EventManager = new EventManager(Logger, HttpClient); EventManager = new EventManager(logger, HttpClient);
} }
public EventSubscriptionResponse CancelEventSubscription(string subscriptionId) public EventSubscriptionResponse CancelEventSubscription(string subscriptionId)

@ -80,6 +80,7 @@ namespace Emby.Dlna.Service
{ {
builder.Append("<allowedValue>" + DescriptionXmlBuilder.Escape(allowedValue) + "</allowedValue>"); builder.Append("<allowedValue>" + DescriptionXmlBuilder.Escape(allowedValue) + "</allowedValue>");
} }
builder.Append("</allowedValueList>"); builder.Append("</allowedValueList>");
} }

@ -4,6 +4,7 @@ using System.Globalization;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Jellyfin.Data.Entities;
using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller; using MediaBrowser.Controller;
using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Drawing;
@ -14,6 +15,7 @@ using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO; using MediaBrowser.Model.IO;
using MediaBrowser.Model.Net; using MediaBrowser.Model.Net;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Photo = MediaBrowser.Controller.Entities.Photo;
namespace Emby.Drawing namespace Emby.Drawing
{ {
@ -28,7 +30,7 @@ namespace Emby.Drawing
private static readonly HashSet<string> _transparentImageTypes private static readonly HashSet<string> _transparentImageTypes
= new HashSet<string>(StringComparer.OrdinalIgnoreCase) { ".png", ".webp", ".gif" }; = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { ".png", ".webp", ".gif" };
private readonly ILogger _logger; private readonly ILogger<ImageProcessor> _logger;
private readonly IFileSystem _fileSystem; private readonly IFileSystem _fileSystem;
private readonly IServerApplicationPaths _appPaths; private readonly IServerApplicationPaths _appPaths;
private readonly IImageEncoder _imageEncoder; private readonly IImageEncoder _imageEncoder;
@ -349,6 +351,13 @@ namespace Emby.Drawing
}); });
} }
/// <inheritdoc />
public string GetImageCacheTag(User user)
{
return (user.ProfileImage.Path + user.ProfileImage.LastModified.Ticks).GetMD5()
.ToString("N", CultureInfo.InvariantCulture);
}
private async Task<(string path, DateTime dateModified)> GetSupportedImage(string originalImagePath, DateTime dateModified) private async Task<(string path, DateTime dateModified)> GetSupportedImage(string originalImagePath, DateTime dateModified)
{ {
var inputFormat = Path.GetExtension(originalImagePath) var inputFormat = Path.GetExtension(originalImagePath)

@ -64,6 +64,7 @@ namespace Emby.Naming.AudioBook
{ {
result.ChapterNumber = int.Parse(matches[0].Groups[0].Value); result.ChapterNumber = int.Parse(matches[0].Groups[0].Value);
} }
if (matches.Count > 1) if (matches.Count > 1)
{ {
result.PartNumber = int.Parse(matches[matches.Count - 1].Groups[0].Value); result.PartNumber = int.Parse(matches[matches.Count - 1].Groups[0].Value);

@ -5,17 +5,17 @@ namespace Emby.Naming.Common
public enum MediaType public enum MediaType
{ {
/// <summary> /// <summary>
/// The audio /// The audio.
/// </summary> /// </summary>
Audio = 0, Audio = 0,
/// <summary> /// <summary>
/// The photo /// The photo.
/// </summary> /// </summary>
Photo = 1, Photo = 1,
/// <summary> /// <summary>
/// The video /// The video.
/// </summary> /// </summary>
Video = 2 Video = 2
} }

@ -8,6 +8,7 @@ using System.Diagnostics.CodeAnalysis;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Notifications; using MediaBrowser.Controller.Notifications;
@ -149,9 +150,7 @@ namespace Emby.Notifications.Api
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request", Justification = "Required for ServiceStack")] [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request", Justification = "Required for ServiceStack")]
public object Get(GetNotificationsSummary request) public object Get(GetNotificationsSummary request)
{ {
return new NotificationsSummary return new NotificationsSummary();
{
};
} }
public Task Post(AddAdminNotification request) public Task Post(AddAdminNotification request)
@ -164,7 +163,10 @@ namespace Emby.Notifications.Api
Level = request.Level, Level = request.Level,
Name = request.Name, Name = request.Name,
Url = request.Url, Url = request.Url,
UserIds = _userManager.Users.Where(i => i.Policy.IsAdministrator).Select(i => i.Id).ToArray() UserIds = _userManager.Users
.Where(user => user.HasPermission(PermissionKind.IsAdministrator))
.Select(user => user.Id)
.ToArray()
}; };
return _notificationManager.SendNotification(notification, CancellationToken.None); return _notificationManager.SendNotification(notification, CancellationToken.None);

@ -25,7 +25,7 @@ namespace Emby.Notifications
/// </summary> /// </summary>
public class NotificationEntryPoint : IServerEntryPoint public class NotificationEntryPoint : IServerEntryPoint
{ {
private readonly ILogger _logger; private readonly ILogger<NotificationEntryPoint> _logger;
private readonly IActivityManager _activityManager; private readonly IActivityManager _activityManager;
private readonly ILocalizationManager _localization; private readonly ILocalizationManager _localization;
private readonly INotificationManager _notificationManager; private readonly INotificationManager _notificationManager;

@ -4,6 +4,8 @@ using System.Globalization;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums;
using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Configuration;
@ -21,7 +23,7 @@ namespace Emby.Notifications
/// </summary> /// </summary>
public class NotificationManager : INotificationManager public class NotificationManager : INotificationManager
{ {
private readonly ILogger _logger; private readonly ILogger<NotificationManager> _logger;
private readonly IUserManager _userManager; private readonly IUserManager _userManager;
private readonly IServerConfigurationManager _config; private readonly IServerConfigurationManager _config;
@ -101,7 +103,7 @@ namespace Emby.Notifications
switch (request.SendToUserMode.Value) switch (request.SendToUserMode.Value)
{ {
case SendToUserType.Admins: case SendToUserType.Admins:
return _userManager.Users.Where(i => i.Policy.IsAdministrator) return _userManager.Users.Where(i => i.HasPermission(PermissionKind.IsAdministrator))
.Select(i => i.Id); .Select(i => i.Id);
case SendToUserType.All: case SendToUserType.All:
return _userManager.UsersIds; return _userManager.UsersIds;
@ -117,7 +119,7 @@ namespace Emby.Notifications
var config = GetConfiguration(); var config = GetConfiguration();
return _userManager.Users return _userManager.Users
.Where(i => config.IsEnabledToSendToUser(request.NotificationType, i.Id.ToString("N", CultureInfo.InvariantCulture), i.Policy)) .Where(i => config.IsEnabledToSendToUser(request.NotificationType, i.Id.ToString("N", CultureInfo.InvariantCulture), i))
.Select(i => i.Id); .Select(i => i.Id);
} }
@ -142,7 +144,7 @@ namespace Emby.Notifications
User = user User = user
}; };
_logger.LogDebug("Sending notification via {0} to user {1}", service.Name, user.Name); _logger.LogDebug("Sending notification via {0} to user {1}", service.Name, user.Username);
try try
{ {

@ -22,7 +22,7 @@ namespace Emby.Photos
/// </summary> /// </summary>
public class PhotoProvider : ICustomMetadataProvider<Photo>, IForcedProvider, IHasItemChangeMonitor public class PhotoProvider : ICustomMetadataProvider<Photo>, IForcedProvider, IHasItemChangeMonitor
{ {
private readonly ILogger _logger; private readonly ILogger<PhotoProvider> _logger;
private readonly IImageProcessor _imageProcessor; private readonly IImageProcessor _imageProcessor;
// These are causing taglib to hang // These are causing taglib to hang
@ -104,7 +104,7 @@ namespace Emby.Photos
item.Overview = image.ImageTag.Comment; item.Overview = image.ImageTag.Comment;
if (!string.IsNullOrWhiteSpace(image.ImageTag.Title) if (!string.IsNullOrWhiteSpace(image.ImageTag.Title)
&& !item.LockedFields.Contains(MetadataFields.Name)) && !item.LockedFields.Contains(MetadataField.Name))
{ {
item.Name = image.ImageTag.Title; item.Name = image.ImageTag.Title;
} }

@ -88,25 +88,26 @@ namespace Emby.Server.Implementations.Activity
_subManager.SubtitleDownloadFailure += OnSubtitleDownloadFailure; _subManager.SubtitleDownloadFailure += OnSubtitleDownloadFailure;
_userManager.UserCreated += OnUserCreated; _userManager.OnUserCreated += OnUserCreated;
_userManager.UserPasswordChanged += OnUserPasswordChanged; _userManager.OnUserPasswordChanged += OnUserPasswordChanged;
_userManager.UserDeleted += OnUserDeleted; _userManager.OnUserDeleted += OnUserDeleted;
_userManager.UserPolicyUpdated += OnUserPolicyUpdated; _userManager.OnUserLockedOut += OnUserLockedOut;
_userManager.UserLockedOut += OnUserLockedOut;
return Task.CompletedTask; return Task.CompletedTask;
} }
private async void OnUserLockedOut(object sender, GenericEventArgs<MediaBrowser.Controller.Entities.User> e) private async void OnUserLockedOut(object sender, GenericEventArgs<User> e)
{ {
await CreateLogEntry(new ActivityLog( await CreateLogEntry(new ActivityLog(
string.Format( string.Format(
CultureInfo.InvariantCulture, CultureInfo.InvariantCulture,
_localization.GetLocalizedString("UserLockedOutWithName"), _localization.GetLocalizedString("UserLockedOutWithName"),
e.Argument.Name), e.Argument.Username),
NotificationType.UserLockedOut.ToString(), NotificationType.UserLockedOut.ToString(),
e.Argument.Id)) e.Argument.Id)
.ConfigureAwait(false); {
LogSeverity = LogLevel.Error
}).ConfigureAwait(false);
} }
private async void OnSubtitleDownloadFailure(object sender, SubtitleDownloadFailureEventArgs e) private async void OnSubtitleDownloadFailure(object sender, SubtitleDownloadFailureEventArgs e)
@ -152,7 +153,7 @@ namespace Emby.Server.Implementations.Activity
string.Format( string.Format(
CultureInfo.InvariantCulture, CultureInfo.InvariantCulture,
_localization.GetLocalizedString("UserStoppedPlayingItemWithValues"), _localization.GetLocalizedString("UserStoppedPlayingItemWithValues"),
user.Name, user.Username,
GetItemName(item), GetItemName(item),
e.DeviceName), e.DeviceName),
GetPlaybackStoppedNotificationType(item.MediaType), GetPlaybackStoppedNotificationType(item.MediaType),
@ -187,7 +188,7 @@ namespace Emby.Server.Implementations.Activity
string.Format( string.Format(
CultureInfo.InvariantCulture, CultureInfo.InvariantCulture,
_localization.GetLocalizedString("UserStartedPlayingItemWithValues"), _localization.GetLocalizedString("UserStartedPlayingItemWithValues"),
user.Name, user.Username,
GetItemName(item), GetItemName(item),
e.DeviceName), e.DeviceName),
GetPlaybackNotificationType(item.MediaType), GetPlaybackNotificationType(item.MediaType),
@ -304,49 +305,37 @@ namespace Emby.Server.Implementations.Activity
}).ConfigureAwait(false); }).ConfigureAwait(false);
} }
private async void OnUserPolicyUpdated(object sender, GenericEventArgs<MediaBrowser.Controller.Entities.User> e) private async void OnUserDeleted(object sender, GenericEventArgs<User> e)
{
await CreateLogEntry(new ActivityLog(
string.Format(
CultureInfo.InvariantCulture,
_localization.GetLocalizedString("UserPolicyUpdatedWithName"),
e.Argument.Name),
"UserPolicyUpdated",
e.Argument.Id))
.ConfigureAwait(false);
}
private async void OnUserDeleted(object sender, GenericEventArgs<MediaBrowser.Controller.Entities.User> e)
{ {
await CreateLogEntry(new ActivityLog( await CreateLogEntry(new ActivityLog(
string.Format( string.Format(
CultureInfo.InvariantCulture, CultureInfo.InvariantCulture,
_localization.GetLocalizedString("UserDeletedWithName"), _localization.GetLocalizedString("UserDeletedWithName"),
e.Argument.Name), e.Argument.Username),
"UserDeleted", "UserDeleted",
Guid.Empty)) Guid.Empty))
.ConfigureAwait(false); .ConfigureAwait(false);
} }
private async void OnUserPasswordChanged(object sender, GenericEventArgs<MediaBrowser.Controller.Entities.User> e) private async void OnUserPasswordChanged(object sender, GenericEventArgs<User> e)
{ {
await CreateLogEntry(new ActivityLog( await CreateLogEntry(new ActivityLog(
string.Format( string.Format(
CultureInfo.InvariantCulture, CultureInfo.InvariantCulture,
_localization.GetLocalizedString("UserPasswordChangedWithName"), _localization.GetLocalizedString("UserPasswordChangedWithName"),
e.Argument.Name), e.Argument.Username),
"UserPasswordChanged", "UserPasswordChanged",
e.Argument.Id)) e.Argument.Id))
.ConfigureAwait(false); .ConfigureAwait(false);
} }
private async void OnUserCreated(object sender, GenericEventArgs<MediaBrowser.Controller.Entities.User> e) private async void OnUserCreated(object sender, GenericEventArgs<User> e)
{ {
await CreateLogEntry(new ActivityLog( await CreateLogEntry(new ActivityLog(
string.Format( string.Format(
CultureInfo.InvariantCulture, CultureInfo.InvariantCulture,
_localization.GetLocalizedString("UserCreatedWithName"), _localization.GetLocalizedString("UserCreatedWithName"),
e.Argument.Name), e.Argument.Username),
"UserCreated", "UserCreated",
e.Argument.Id)) e.Argument.Id))
.ConfigureAwait(false); .ConfigureAwait(false);
@ -510,11 +499,10 @@ namespace Emby.Server.Implementations.Activity
_subManager.SubtitleDownloadFailure -= OnSubtitleDownloadFailure; _subManager.SubtitleDownloadFailure -= OnSubtitleDownloadFailure;
_userManager.UserCreated -= OnUserCreated; _userManager.OnUserCreated -= OnUserCreated;
_userManager.UserPasswordChanged -= OnUserPasswordChanged; _userManager.OnUserPasswordChanged -= OnUserPasswordChanged;
_userManager.UserDeleted -= OnUserDeleted; _userManager.OnUserDeleted -= OnUserDeleted;
_userManager.UserPolicyUpdated -= OnUserPolicyUpdated; _userManager.OnUserLockedOut -= OnUserLockedOut;
_userManager.UserLockedOut -= OnUserLockedOut;
} }
/// <summary> /// <summary>

@ -53,7 +53,7 @@ namespace Emby.Server.Implementations.AppBase
CommonApplicationPaths = applicationPaths; CommonApplicationPaths = applicationPaths;
XmlSerializer = xmlSerializer; XmlSerializer = xmlSerializer;
_fileSystem = fileSystem; _fileSystem = fileSystem;
Logger = loggerFactory.CreateLogger(GetType().Name); Logger = loggerFactory.CreateLogger<BaseConfigurationManager>();
UpdateCachePath(); UpdateCachePath();
} }
@ -83,7 +83,7 @@ namespace Emby.Server.Implementations.AppBase
/// Gets the logger. /// Gets the logger.
/// </summary> /// </summary>
/// <value>The logger.</value> /// <value>The logger.</value>
protected ILogger Logger { get; private set; } protected ILogger<BaseConfigurationManager> Logger { get; private set; }
/// <summary> /// <summary>
/// Gets the XML serializer. /// Gets the XML serializer.

@ -175,7 +175,7 @@ namespace Emby.Server.Implementations
/// <summary> /// <summary>
/// Gets the logger. /// Gets the logger.
/// </summary> /// </summary>
protected ILogger Logger { get; } protected ILogger<ApplicationHost> Logger { get; }
private IPlugin[] _plugins; private IPlugin[] _plugins;
@ -564,11 +564,8 @@ namespace Emby.Server.Implementations
serviceCollection.AddSingleton<IAuthenticationRepository, AuthenticationRepository>(); serviceCollection.AddSingleton<IAuthenticationRepository, AuthenticationRepository>();
serviceCollection.AddSingleton<IUserRepository, SqliteUserRepository>();
// TODO: Refactor to eliminate the circular dependency here so that Lazy<T> isn't required // TODO: Refactor to eliminate the circular dependency here so that Lazy<T> isn't required
serviceCollection.AddTransient(provider => new Lazy<IDtoService>(provider.GetRequiredService<IDtoService>)); serviceCollection.AddTransient(provider => new Lazy<IDtoService>(provider.GetRequiredService<IDtoService>));
serviceCollection.AddSingleton<IUserManager, UserManager>();
// TODO: Refactor to eliminate the circular dependency here so that Lazy<T> isn't required // TODO: Refactor to eliminate the circular dependency here so that Lazy<T> isn't required
// TODO: Add StartupOptions.FFmpegPath to IConfiguration and remove this custom activation // TODO: Add StartupOptions.FFmpegPath to IConfiguration and remove this custom activation
@ -662,15 +659,11 @@ namespace Emby.Server.Implementations
((SqliteDisplayPreferencesRepository)Resolve<IDisplayPreferencesRepository>()).Initialize(); ((SqliteDisplayPreferencesRepository)Resolve<IDisplayPreferencesRepository>()).Initialize();
((AuthenticationRepository)Resolve<IAuthenticationRepository>()).Initialize(); ((AuthenticationRepository)Resolve<IAuthenticationRepository>()).Initialize();
((SqliteUserRepository)Resolve<IUserRepository>()).Initialize();
SetStaticProperties(); SetStaticProperties();
var userManager = (UserManager)Resolve<IUserManager>();
userManager.Initialize();
var userDataRepo = (SqliteUserDataRepository)Resolve<IUserDataRepository>(); var userDataRepo = (SqliteUserDataRepository)Resolve<IUserDataRepository>();
((SqliteItemRepository)Resolve<IItemRepository>()).Initialize(userDataRepo, userManager); ((SqliteItemRepository)Resolve<IItemRepository>()).Initialize(userDataRepo, Resolve<IUserManager>());
FindParts(); FindParts();
} }
@ -753,7 +746,6 @@ namespace Emby.Server.Implementations
BaseItem.ProviderManager = Resolve<IProviderManager>(); BaseItem.ProviderManager = Resolve<IProviderManager>();
BaseItem.LocalizationManager = Resolve<ILocalizationManager>(); BaseItem.LocalizationManager = Resolve<ILocalizationManager>();
BaseItem.ItemRepository = Resolve<IItemRepository>(); BaseItem.ItemRepository = Resolve<IItemRepository>();
User.UserManager = Resolve<IUserManager>();
BaseItem.FileSystem = _fileSystemManager; BaseItem.FileSystem = _fileSystemManager;
BaseItem.UserDataManager = Resolve<IUserDataManager>(); BaseItem.UserDataManager = Resolve<IUserDataManager>();
BaseItem.ChannelManager = Resolve<IChannelManager>(); BaseItem.ChannelManager = Resolve<IChannelManager>();
@ -967,7 +959,7 @@ namespace Emby.Server.Implementations
} }
/// <summary> /// <summary>
/// Notifies that the kernel that a change has been made that requires a restart /// Notifies that the kernel that a change has been made that requires a restart.
/// </summary> /// </summary>
public void NotifyPendingRestart() public void NotifyPendingRestart()
{ {

@ -41,7 +41,7 @@ namespace Emby.Server.Implementations.Browser
} }
catch (Exception ex) catch (Exception ex)
{ {
var logger = appHost.Resolve<ILogger>(); var logger = appHost.Resolve<ILogger<IServerApplicationHost>>();
logger?.LogError(ex, "Failed to open browser window with URL {URL}", relativeUrl); logger?.LogError(ex, "Failed to open browser window with URL {URL}", relativeUrl);
} }
} }

@ -6,6 +6,7 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Jellyfin.Data.Entities;
using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Progress; using MediaBrowser.Common.Progress;
using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Channels;
@ -13,8 +14,6 @@ using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Channels; using MediaBrowser.Model.Channels;
@ -24,6 +23,11 @@ using MediaBrowser.Model.IO;
using MediaBrowser.Model.Querying; using MediaBrowser.Model.Querying;
using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Serialization;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Episode = MediaBrowser.Controller.Entities.TV.Episode;
using Movie = MediaBrowser.Controller.Entities.Movies.Movie;
using MusicAlbum = MediaBrowser.Controller.Entities.Audio.MusicAlbum;
using Season = MediaBrowser.Controller.Entities.TV.Season;
using Series = MediaBrowser.Controller.Entities.TV.Series;
namespace Emby.Server.Implementations.Channels namespace Emby.Server.Implementations.Channels
{ {
@ -36,7 +40,7 @@ namespace Emby.Server.Implementations.Channels
private readonly IUserDataManager _userDataManager; private readonly IUserDataManager _userDataManager;
private readonly IDtoService _dtoService; private readonly IDtoService _dtoService;
private readonly ILibraryManager _libraryManager; private readonly ILibraryManager _libraryManager;
private readonly ILogger _logger; private readonly ILogger<ChannelManager> _logger;
private readonly IServerConfigurationManager _config; private readonly IServerConfigurationManager _config;
private readonly IFileSystem _fileSystem; private readonly IFileSystem _fileSystem;
private readonly IJsonSerializer _jsonSerializer; private readonly IJsonSerializer _jsonSerializer;
@ -791,7 +795,8 @@ namespace Emby.Server.Implementations.Channels
return result; return result;
} }
private async Task<ChannelItemResult> GetChannelItems(IChannel channel, private async Task<ChannelItemResult> GetChannelItems(
IChannel channel,
User user, User user,
string externalFolderId, string externalFolderId,
ChannelItemSortField? sortField, ChannelItemSortField? sortField,

@ -17,7 +17,7 @@ namespace Emby.Server.Implementations.Channels
public class RefreshChannelsScheduledTask : IScheduledTask, IConfigurableScheduledTask public class RefreshChannelsScheduledTask : IScheduledTask, IConfigurableScheduledTask
{ {
private readonly IChannelManager _channelManager; private readonly IChannelManager _channelManager;
private readonly ILogger _logger; private readonly ILogger<RefreshChannelsScheduledTask> _logger;
private readonly ILibraryManager _libraryManager; private readonly ILibraryManager _libraryManager;
private readonly ILocalizationManager _localization; private readonly ILocalizationManager _localization;

@ -5,6 +5,7 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Jellyfin.Data.Entities;
using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Collections; using MediaBrowser.Controller.Collections;
using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Configuration;
@ -29,7 +30,7 @@ namespace Emby.Server.Implementations.Collections
private readonly ILibraryManager _libraryManager; private readonly ILibraryManager _libraryManager;
private readonly IFileSystem _fileSystem; private readonly IFileSystem _fileSystem;
private readonly ILibraryMonitor _iLibraryMonitor; private readonly ILibraryMonitor _iLibraryMonitor;
private readonly ILogger _logger; private readonly ILogger<CollectionManager> _logger;
private readonly IProviderManager _providerManager; private readonly IProviderManager _providerManager;
private readonly ILocalizationManager _localizationManager; private readonly ILocalizationManager _localizationManager;
private readonly IApplicationPaths _appPaths; private readonly IApplicationPaths _appPaths;
@ -56,7 +57,7 @@ namespace Emby.Server.Implementations.Collections
_libraryManager = libraryManager; _libraryManager = libraryManager;
_fileSystem = fileSystem; _fileSystem = fileSystem;
_iLibraryMonitor = iLibraryMonitor; _iLibraryMonitor = iLibraryMonitor;
_logger = loggerFactory.CreateLogger(nameof(CollectionManager)); _logger = loggerFactory.CreateLogger<CollectionManager>();
_providerManager = providerManager; _providerManager = providerManager;
_localizationManager = localizationManager; _localizationManager = localizationManager;
_appPaths = appPaths; _appPaths = appPaths;
@ -370,7 +371,7 @@ namespace Emby.Server.Implementations.Collections
{ {
private readonly CollectionManager _collectionManager; private readonly CollectionManager _collectionManager;
private readonly IServerConfigurationManager _config; private readonly IServerConfigurationManager _config;
private readonly ILogger _logger; private readonly ILogger<CollectionManagerEntryPoint> _logger;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="CollectionManagerEntryPoint"/> class. /// Initializes a new instance of the <see cref="CollectionManagerEntryPoint"/> class.

@ -17,7 +17,7 @@ namespace Emby.Server.Implementations.Data
/// Initializes a new instance of the <see cref="BaseSqliteRepository"/> class. /// Initializes a new instance of the <see cref="BaseSqliteRepository"/> class.
/// </summary> /// </summary>
/// <param name="logger">The logger.</param> /// <param name="logger">The logger.</param>
protected BaseSqliteRepository(ILogger logger) protected BaseSqliteRepository(ILogger<BaseSqliteRepository> logger)
{ {
Logger = logger; Logger = logger;
} }
@ -32,7 +32,7 @@ namespace Emby.Server.Implementations.Data
/// Gets the logger. /// Gets the logger.
/// </summary> /// </summary>
/// <value>The logger.</value> /// <value>The logger.</value>
protected ILogger Logger { get; } protected ILogger<BaseSqliteRepository> Logger { get; }
/// <summary> /// <summary>
/// Gets the default connection flags. /// Gets the default connection flags.
@ -162,7 +162,6 @@ namespace Emby.Server.Implementations.Data
} }
return false; return false;
}, ReadTransactionMode); }, ReadTransactionMode);
} }
@ -248,12 +247,12 @@ namespace Emby.Server.Implementations.Data
public enum SynchronousMode public enum SynchronousMode
{ {
/// <summary> /// <summary>
/// SQLite continues without syncing as soon as it has handed data off to the operating system /// SQLite continues without syncing as soon as it has handed data off to the operating system.
/// </summary> /// </summary>
Off = 0, Off = 0,
/// <summary> /// <summary>
/// SQLite database engine will still sync at the most critical moments /// SQLite database engine will still sync at the most critical moments.
/// </summary> /// </summary>
Normal = 1, Normal = 1,

@ -12,7 +12,7 @@ namespace Emby.Server.Implementations.Data
public class CleanDatabaseScheduledTask : ILibraryPostScanTask public class CleanDatabaseScheduledTask : ILibraryPostScanTask
{ {
private readonly ILibraryManager _libraryManager; private readonly ILibraryManager _libraryManager;
private readonly ILogger _logger; private readonly ILogger<CleanDatabaseScheduledTask> _logger;
public CleanDatabaseScheduledTask(ILibraryManager libraryManager, ILogger<CleanDatabaseScheduledTask> logger) public CleanDatabaseScheduledTask(ILibraryManager libraryManager, ILogger<CleanDatabaseScheduledTask> logger)
{ {
@ -51,7 +51,6 @@ namespace Emby.Server.Implementations.Data
_libraryManager.DeleteItem(item, new DeleteOptions _libraryManager.DeleteItem(item, new DeleteOptions
{ {
DeleteFileLocation = false DeleteFileLocation = false
}); });
} }

@ -1,4 +1,4 @@
#pragma warning disable CS1591 #pragma warning disable CS1591
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@ -59,7 +59,7 @@ namespace Emby.Server.Implementations.Data
} }
/// <summary> /// <summary>
/// Opens the connection to the database /// Opens the connection to the database.
/// </summary> /// </summary>
/// <returns>Task.</returns> /// <returns>Task.</returns>
private void InitializeInternal() private void InitializeInternal()
@ -77,7 +77,7 @@ namespace Emby.Server.Implementations.Data
} }
/// <summary> /// <summary>
/// Save the display preferences associated with an item in the repo /// Save the display preferences associated with an item in the repo.
/// </summary> /// </summary>
/// <param name="displayPreferences">The display preferences.</param> /// <param name="displayPreferences">The display preferences.</param>
/// <param name="userId">The user id.</param> /// <param name="userId">The user id.</param>
@ -122,7 +122,7 @@ namespace Emby.Server.Implementations.Data
} }
/// <summary> /// <summary>
/// Save all display preferences associated with a user in the repo /// Save all display preferences associated with a user in the repo.
/// </summary> /// </summary>
/// <param name="displayPreferences">The display preferences.</param> /// <param name="displayPreferences">The display preferences.</param>
/// <param name="userId">The user id.</param> /// <param name="userId">The user id.</param>

@ -102,7 +102,7 @@ namespace Emby.Server.Implementations.Data
protected override TempStoreMode TempStore => TempStoreMode.Memory; protected override TempStoreMode TempStore => TempStoreMode.Memory;
/// <summary> /// <summary>
/// Opens the connection to the database /// Opens the connection to the database.
/// </summary> /// </summary>
public void Initialize(SqliteUserDataRepository userDataRepo, IUserManager userManager) public void Initialize(SqliteUserDataRepository userDataRepo, IUserManager userManager)
{ {
@ -321,7 +321,6 @@ namespace Emby.Server.Implementations.Data
AddColumn(db, "MediaStreams", "ColorPrimaries", "TEXT", existingColumnNames); AddColumn(db, "MediaStreams", "ColorPrimaries", "TEXT", existingColumnNames);
AddColumn(db, "MediaStreams", "ColorSpace", "TEXT", existingColumnNames); AddColumn(db, "MediaStreams", "ColorSpace", "TEXT", existingColumnNames);
AddColumn(db, "MediaStreams", "ColorTransfer", "TEXT", existingColumnNames); AddColumn(db, "MediaStreams", "ColorTransfer", "TEXT", existingColumnNames);
}, TransactionMode); }, TransactionMode);
connection.RunQueries(postQueries); connection.RunQueries(postQueries);
@ -549,7 +548,7 @@ namespace Emby.Server.Implementations.Data
} }
/// <summary> /// <summary>
/// Save a standard item in the repo /// Save a standard item in the repo.
/// </summary> /// </summary>
/// <param name="item">The item.</param> /// <param name="item">The item.</param>
/// <param name="cancellationToken">The cancellation token.</param> /// <param name="cancellationToken">The cancellation token.</param>
@ -794,6 +793,7 @@ namespace Emby.Server.Implementations.Data
{ {
saveItemStatement.TryBindNull("@Width"); saveItemStatement.TryBindNull("@Width");
} }
if (item.Height > 0) if (item.Height > 0)
{ {
saveItemStatement.TryBind("@Height", item.Height); saveItemStatement.TryBind("@Height", item.Height);
@ -933,6 +933,7 @@ namespace Emby.Server.Implementations.Data
{ {
saveItemStatement.TryBindNull("@SeriesName"); saveItemStatement.TryBindNull("@SeriesName");
} }
if (string.IsNullOrWhiteSpace(userDataKey)) if (string.IsNullOrWhiteSpace(userDataKey))
{ {
saveItemStatement.TryBindNull("@UserDataKey"); saveItemStatement.TryBindNull("@UserDataKey");
@ -1008,6 +1009,7 @@ namespace Emby.Server.Implementations.Data
{ {
artists = string.Join("|", hasArtists.Artists); artists = string.Join("|", hasArtists.Artists);
} }
saveItemStatement.TryBind("@Artists", artists); saveItemStatement.TryBind("@Artists", artists);
string albumArtists = null; string albumArtists = null;
@ -1107,6 +1109,7 @@ namespace Emby.Server.Implementations.Data
{ {
continue; continue;
} }
str.Append(ToValueString(i) + "|"); str.Append(ToValueString(i) + "|");
} }
@ -1205,7 +1208,7 @@ namespace Emby.Server.Implementations.Data
} }
/// <summary> /// <summary>
/// Internal retrieve from items or users table /// Internal retrieve from items or users table.
/// </summary> /// </summary>
/// <param name="id">The id.</param> /// <param name="id">The id.</param>
/// <returns>BaseItem.</returns> /// <returns>BaseItem.</returns>
@ -1367,6 +1370,7 @@ namespace Emby.Server.Implementations.Data
hasStartDate.StartDate = reader[index].ReadDateTime(); hasStartDate.StartDate = reader[index].ReadDateTime();
} }
} }
index++; index++;
} }
@ -1374,12 +1378,14 @@ namespace Emby.Server.Implementations.Data
{ {
item.EndDate = reader[index].TryReadDateTime(); item.EndDate = reader[index].TryReadDateTime();
} }
index++; index++;
if (!reader.IsDBNull(index)) if (!reader.IsDBNull(index))
{ {
item.ChannelId = new Guid(reader.GetString(index)); item.ChannelId = new Guid(reader.GetString(index));
} }
index++; index++;
if (enableProgramAttributes) if (enableProgramAttributes)
@ -1390,24 +1396,28 @@ namespace Emby.Server.Implementations.Data
{ {
hasProgramAttributes.IsMovie = reader.GetBoolean(index); hasProgramAttributes.IsMovie = reader.GetBoolean(index);
} }
index++; index++;
if (!reader.IsDBNull(index)) if (!reader.IsDBNull(index))
{ {
hasProgramAttributes.IsSeries = reader.GetBoolean(index); hasProgramAttributes.IsSeries = reader.GetBoolean(index);
} }
index++; index++;
if (!reader.IsDBNull(index)) if (!reader.IsDBNull(index))
{ {
hasProgramAttributes.EpisodeTitle = reader.GetString(index); hasProgramAttributes.EpisodeTitle = reader.GetString(index);
} }
index++; index++;
if (!reader.IsDBNull(index)) if (!reader.IsDBNull(index))
{ {
hasProgramAttributes.IsRepeat = reader.GetBoolean(index); hasProgramAttributes.IsRepeat = reader.GetBoolean(index);
} }
index++; index++;
} }
else else
@ -1420,6 +1430,7 @@ namespace Emby.Server.Implementations.Data
{ {
item.CommunityRating = reader.GetFloat(index); item.CommunityRating = reader.GetFloat(index);
} }
index++; index++;
if (HasField(query, ItemFields.CustomRating)) if (HasField(query, ItemFields.CustomRating))
@ -1428,6 +1439,7 @@ namespace Emby.Server.Implementations.Data
{ {
item.CustomRating = reader.GetString(index); item.CustomRating = reader.GetString(index);
} }
index++; index++;
} }
@ -1435,6 +1447,7 @@ namespace Emby.Server.Implementations.Data
{ {
item.IndexNumber = reader.GetInt32(index); item.IndexNumber = reader.GetInt32(index);
} }
index++; index++;
if (HasField(query, ItemFields.Settings)) if (HasField(query, ItemFields.Settings))
@ -1443,18 +1456,21 @@ namespace Emby.Server.Implementations.Data
{ {
item.IsLocked = reader.GetBoolean(index); item.IsLocked = reader.GetBoolean(index);
} }
index++; index++;
if (!reader.IsDBNull(index)) if (!reader.IsDBNull(index))
{ {
item.PreferredMetadataLanguage = reader.GetString(index); item.PreferredMetadataLanguage = reader.GetString(index);
} }
index++; index++;
if (!reader.IsDBNull(index)) if (!reader.IsDBNull(index))
{ {
item.PreferredMetadataCountryCode = reader.GetString(index); item.PreferredMetadataCountryCode = reader.GetString(index);
} }
index++; index++;
} }
@ -1464,6 +1480,7 @@ namespace Emby.Server.Implementations.Data
{ {
item.Width = reader.GetInt32(index); item.Width = reader.GetInt32(index);
} }
index++; index++;
} }
@ -1473,6 +1490,7 @@ namespace Emby.Server.Implementations.Data
{ {
item.Height = reader.GetInt32(index); item.Height = reader.GetInt32(index);
} }
index++; index++;
} }
@ -1482,6 +1500,7 @@ namespace Emby.Server.Implementations.Data
{ {
item.DateLastRefreshed = reader[index].ReadDateTime(); item.DateLastRefreshed = reader[index].ReadDateTime();
} }
index++; index++;
} }
@ -1489,18 +1508,21 @@ namespace Emby.Server.Implementations.Data
{ {
item.Name = reader.GetString(index); item.Name = reader.GetString(index);
} }
index++; index++;
if (!reader.IsDBNull(index)) if (!reader.IsDBNull(index))
{ {
item.Path = RestorePath(reader.GetString(index)); item.Path = RestorePath(reader.GetString(index));
} }
index++; index++;
if (!reader.IsDBNull(index)) if (!reader.IsDBNull(index))
{ {
item.PremiereDate = reader[index].TryReadDateTime(); item.PremiereDate = reader[index].TryReadDateTime();
} }
index++; index++;
if (HasField(query, ItemFields.Overview)) if (HasField(query, ItemFields.Overview))
@ -1509,6 +1531,7 @@ namespace Emby.Server.Implementations.Data
{ {
item.Overview = reader.GetString(index); item.Overview = reader.GetString(index);
} }
index++; index++;
} }
@ -1516,18 +1539,21 @@ namespace Emby.Server.Implementations.Data
{ {
item.ParentIndexNumber = reader.GetInt32(index); item.ParentIndexNumber = reader.GetInt32(index);
} }
index++; index++;
if (!reader.IsDBNull(index)) if (!reader.IsDBNull(index))
{ {
item.ProductionYear = reader.GetInt32(index); item.ProductionYear = reader.GetInt32(index);
} }
index++; index++;
if (!reader.IsDBNull(index)) if (!reader.IsDBNull(index))
{ {
item.OfficialRating = reader.GetString(index); item.OfficialRating = reader.GetString(index);
} }
index++; index++;
if (HasField(query, ItemFields.SortName)) if (HasField(query, ItemFields.SortName))
@ -1536,6 +1562,7 @@ namespace Emby.Server.Implementations.Data
{ {
item.ForcedSortName = reader.GetString(index); item.ForcedSortName = reader.GetString(index);
} }
index++; index++;
} }
@ -1543,12 +1570,14 @@ namespace Emby.Server.Implementations.Data
{ {
item.RunTimeTicks = reader.GetInt64(index); item.RunTimeTicks = reader.GetInt64(index);
} }
index++; index++;
if (!reader.IsDBNull(index)) if (!reader.IsDBNull(index))
{ {
item.Size = reader.GetInt64(index); item.Size = reader.GetInt64(index);
} }
index++; index++;
if (HasField(query, ItemFields.DateCreated)) if (HasField(query, ItemFields.DateCreated))
@ -1557,6 +1586,7 @@ namespace Emby.Server.Implementations.Data
{ {
item.DateCreated = reader[index].ReadDateTime(); item.DateCreated = reader[index].ReadDateTime();
} }
index++; index++;
} }
@ -1564,6 +1594,7 @@ namespace Emby.Server.Implementations.Data
{ {
item.DateModified = reader[index].ReadDateTime(); item.DateModified = reader[index].ReadDateTime();
} }
index++; index++;
item.Id = reader.GetGuid(index); item.Id = reader.GetGuid(index);
@ -1575,6 +1606,7 @@ namespace Emby.Server.Implementations.Data
{ {
item.Genres = reader.GetString(index).Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries); item.Genres = reader.GetString(index).Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
} }
index++; index++;
} }
@ -1582,6 +1614,7 @@ namespace Emby.Server.Implementations.Data
{ {
item.ParentId = reader.GetGuid(index); item.ParentId = reader.GetGuid(index);
} }
index++; index++;
if (!reader.IsDBNull(index)) if (!reader.IsDBNull(index))
@ -1591,6 +1624,7 @@ namespace Emby.Server.Implementations.Data
item.Audio = audio; item.Audio = audio;
} }
} }
index++; index++;
// TODO: Even if not needed by apps, the server needs it internally // TODO: Even if not needed by apps, the server needs it internally
@ -1604,6 +1638,7 @@ namespace Emby.Server.Implementations.Data
liveTvChannel.ServiceName = reader.GetString(index); liveTvChannel.ServiceName = reader.GetString(index);
} }
} }
index++; index++;
} }
@ -1611,6 +1646,7 @@ namespace Emby.Server.Implementations.Data
{ {
item.IsInMixedFolder = reader.GetBoolean(index); item.IsInMixedFolder = reader.GetBoolean(index);
} }
index++; index++;
if (HasField(query, ItemFields.DateLastSaved)) if (HasField(query, ItemFields.DateLastSaved))
@ -1619,6 +1655,7 @@ namespace Emby.Server.Implementations.Data
{ {
item.DateLastSaved = reader[index].ReadDateTime(); item.DateLastSaved = reader[index].ReadDateTime();
} }
index++; index++;
} }
@ -1626,18 +1663,20 @@ namespace Emby.Server.Implementations.Data
{ {
if (!reader.IsDBNull(index)) if (!reader.IsDBNull(index))
{ {
IEnumerable<MetadataFields> GetLockedFields(string s) IEnumerable<MetadataField> GetLockedFields(string s)
{ {
foreach (var i in s.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries)) foreach (var i in s.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries))
{ {
if (Enum.TryParse(i, true, out MetadataFields parsedValue)) if (Enum.TryParse(i, true, out MetadataField parsedValue))
{ {
yield return parsedValue; yield return parsedValue;
} }
} }
} }
item.LockedFields = GetLockedFields(reader.GetString(index)).ToArray(); item.LockedFields = GetLockedFields(reader.GetString(index)).ToArray();
} }
index++; index++;
} }
@ -1647,6 +1686,7 @@ namespace Emby.Server.Implementations.Data
{ {
item.Studios = reader.GetString(index).Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries); item.Studios = reader.GetString(index).Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
} }
index++; index++;
} }
@ -1656,6 +1696,7 @@ namespace Emby.Server.Implementations.Data
{ {
item.Tags = reader.GetString(index).Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries); item.Tags = reader.GetString(index).Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
} }
index++; index++;
} }
@ -1675,9 +1716,11 @@ namespace Emby.Server.Implementations.Data
} }
} }
} }
trailer.TrailerTypes = GetTrailerTypes(reader.GetString(index)).ToArray(); trailer.TrailerTypes = GetTrailerTypes(reader.GetString(index)).ToArray();
} }
} }
index++; index++;
} }
@ -1687,6 +1730,7 @@ namespace Emby.Server.Implementations.Data
{ {
item.OriginalTitle = reader.GetString(index); item.OriginalTitle = reader.GetString(index);
} }
index++; index++;
} }
@ -1697,6 +1741,7 @@ namespace Emby.Server.Implementations.Data
video.PrimaryVersionId = reader.GetString(index); video.PrimaryVersionId = reader.GetString(index);
} }
} }
index++; index++;
if (HasField(query, ItemFields.DateLastMediaAdded)) if (HasField(query, ItemFields.DateLastMediaAdded))
@ -1705,6 +1750,7 @@ namespace Emby.Server.Implementations.Data
{ {
folder.DateLastMediaAdded = reader[index].TryReadDateTime(); folder.DateLastMediaAdded = reader[index].TryReadDateTime();
} }
index++; index++;
} }
@ -1712,18 +1758,21 @@ namespace Emby.Server.Implementations.Data
{ {
item.Album = reader.GetString(index); item.Album = reader.GetString(index);
} }
index++; index++;
if (!reader.IsDBNull(index)) if (!reader.IsDBNull(index))
{ {
item.CriticRating = reader.GetFloat(index); item.CriticRating = reader.GetFloat(index);
} }
index++; index++;
if (!reader.IsDBNull(index)) if (!reader.IsDBNull(index))
{ {
item.IsVirtualItem = reader.GetBoolean(index); item.IsVirtualItem = reader.GetBoolean(index);
} }
index++; index++;
if (item is IHasSeries hasSeriesName) if (item is IHasSeries hasSeriesName)
@ -1733,6 +1782,7 @@ namespace Emby.Server.Implementations.Data
hasSeriesName.SeriesName = reader.GetString(index); hasSeriesName.SeriesName = reader.GetString(index);
} }
} }
index++; index++;
if (hasEpisodeAttributes) if (hasEpisodeAttributes)
@ -1743,6 +1793,7 @@ namespace Emby.Server.Implementations.Data
{ {
episode.SeasonName = reader.GetString(index); episode.SeasonName = reader.GetString(index);
} }
index++; index++;
if (!reader.IsDBNull(index)) if (!reader.IsDBNull(index))
{ {
@ -1753,6 +1804,7 @@ namespace Emby.Server.Implementations.Data
{ {
index++; index++;
} }
index++; index++;
} }
@ -1766,6 +1818,7 @@ namespace Emby.Server.Implementations.Data
hasSeries.SeriesId = reader.GetGuid(index); hasSeries.SeriesId = reader.GetGuid(index);
} }
} }
index++; index++;
} }
@ -1775,6 +1828,7 @@ namespace Emby.Server.Implementations.Data
{ {
item.PresentationUniqueKey = reader.GetString(index); item.PresentationUniqueKey = reader.GetString(index);
} }
index++; index++;
} }
@ -1784,6 +1838,7 @@ namespace Emby.Server.Implementations.Data
{ {
item.InheritedParentalRatingValue = reader.GetInt32(index); item.InheritedParentalRatingValue = reader.GetInt32(index);
} }
index++; index++;
} }
@ -1793,6 +1848,7 @@ namespace Emby.Server.Implementations.Data
{ {
item.ExternalSeriesId = reader.GetString(index); item.ExternalSeriesId = reader.GetString(index);
} }
index++; index++;
} }
@ -1802,6 +1858,7 @@ namespace Emby.Server.Implementations.Data
{ {
item.Tagline = reader.GetString(index); item.Tagline = reader.GetString(index);
} }
index++; index++;
} }
@ -1809,6 +1866,7 @@ namespace Emby.Server.Implementations.Data
{ {
DeserializeProviderIds(reader.GetString(index), item); DeserializeProviderIds(reader.GetString(index), item);
} }
index++; index++;
if (query.DtoOptions.EnableImages) if (query.DtoOptions.EnableImages)
@ -1817,6 +1875,7 @@ namespace Emby.Server.Implementations.Data
{ {
DeserializeImages(reader.GetString(index), item); DeserializeImages(reader.GetString(index), item);
} }
index++; index++;
} }
@ -1826,6 +1885,7 @@ namespace Emby.Server.Implementations.Data
{ {
item.ProductionLocations = reader.GetString(index).Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries).ToArray(); item.ProductionLocations = reader.GetString(index).Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries).ToArray();
} }
index++; index++;
} }
@ -1835,6 +1895,7 @@ namespace Emby.Server.Implementations.Data
{ {
item.ExtraIds = SplitToGuids(reader.GetString(index)); item.ExtraIds = SplitToGuids(reader.GetString(index));
} }
index++; index++;
} }
@ -1842,6 +1903,7 @@ namespace Emby.Server.Implementations.Data
{ {
item.TotalBitrate = reader.GetInt32(index); item.TotalBitrate = reader.GetInt32(index);
} }
index++; index++;
if (!reader.IsDBNull(index)) if (!reader.IsDBNull(index))
@ -1851,6 +1913,7 @@ namespace Emby.Server.Implementations.Data
item.ExtraType = extraType; item.ExtraType = extraType;
} }
} }
index++; index++;
if (hasArtistFields) if (hasArtistFields)
@ -1859,12 +1922,14 @@ namespace Emby.Server.Implementations.Data
{ {
hasArtists.Artists = reader.GetString(index).Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries); hasArtists.Artists = reader.GetString(index).Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
} }
index++; index++;
if (item is IHasAlbumArtist hasAlbumArtists && !reader.IsDBNull(index)) if (item is IHasAlbumArtist hasAlbumArtists && !reader.IsDBNull(index))
{ {
hasAlbumArtists.AlbumArtists = reader.GetString(index).Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries); hasAlbumArtists.AlbumArtists = reader.GetString(index).Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
} }
index++; index++;
} }
@ -1872,6 +1937,7 @@ namespace Emby.Server.Implementations.Data
{ {
item.ExternalId = reader.GetString(index); item.ExternalId = reader.GetString(index);
} }
index++; index++;
if (HasField(query, ItemFields.SeriesPresentationUniqueKey)) if (HasField(query, ItemFields.SeriesPresentationUniqueKey))
@ -1883,6 +1949,7 @@ namespace Emby.Server.Implementations.Data
hasSeries.SeriesPresentationUniqueKey = reader.GetString(index); hasSeries.SeriesPresentationUniqueKey = reader.GetString(index);
} }
} }
index++; index++;
} }
@ -1892,6 +1959,7 @@ namespace Emby.Server.Implementations.Data
{ {
program.ShowId = reader.GetString(index); program.ShowId = reader.GetString(index);
} }
index++; index++;
} }
@ -1899,6 +1967,7 @@ namespace Emby.Server.Implementations.Data
{ {
item.OwnerId = reader.GetGuid(index); item.OwnerId = reader.GetGuid(index);
} }
index++; index++;
return item; return item;
@ -1919,7 +1988,7 @@ namespace Emby.Server.Implementations.Data
} }
/// <summary> /// <summary>
/// Gets chapters for an item /// Gets chapters for an item.
/// </summary> /// </summary>
/// <param name="item">The item.</param> /// <param name="item">The item.</param>
/// <returns>IEnumerable{ChapterInfo}.</returns> /// <returns>IEnumerable{ChapterInfo}.</returns>
@ -1947,7 +2016,7 @@ namespace Emby.Server.Implementations.Data
} }
/// <summary> /// <summary>
/// Gets a single chapter for an item /// Gets a single chapter for an item.
/// </summary> /// </summary>
/// <param name="item">The item.</param> /// <param name="item">The item.</param>
/// <param name="index">The index.</param> /// <param name="index">The index.</param>
@ -2044,7 +2113,6 @@ namespace Emby.Server.Implementations.Data
db.Execute("delete from " + ChaptersTableName + " where ItemId=@ItemId", idBlob); db.Execute("delete from " + ChaptersTableName + " where ItemId=@ItemId", idBlob);
InsertChapters(idBlob, chapters, db); InsertChapters(idBlob, chapters, db);
}, TransactionMode); }, TransactionMode);
} }
} }
@ -2475,6 +2543,7 @@ namespace Emby.Server.Implementations.Data
{ {
statement.TryBind("@SearchTermStartsWith", searchTerm + "%"); statement.TryBind("@SearchTermStartsWith", searchTerm + "%");
} }
if (commandText.IndexOf("@SearchTermContains", StringComparison.OrdinalIgnoreCase) != -1) if (commandText.IndexOf("@SearchTermContains", StringComparison.OrdinalIgnoreCase) != -1)
{ {
statement.TryBind("@SearchTermContains", "%" + searchTerm + "%"); statement.TryBind("@SearchTermContains", "%" + searchTerm + "%");
@ -2734,7 +2803,7 @@ namespace Emby.Server.Implementations.Data
foreach (var providerId in newItem.ProviderIds) foreach (var providerId in newItem.ProviderIds)
{ {
if (providerId.Key == MetadataProviders.TmdbCollection.ToString()) if (providerId.Key == MetadataProvider.TmdbCollection.ToString())
{ {
continue; continue;
} }
@ -2745,6 +2814,7 @@ namespace Emby.Server.Implementations.Data
{ {
items[i] = newItem; items[i] = newItem;
} }
return; return;
} }
} }
@ -2837,6 +2907,7 @@ namespace Emby.Server.Implementations.Data
{ {
statementTexts.Add(commandText); statementTexts.Add(commandText);
} }
if (query.EnableTotalRecordCount) if (query.EnableTotalRecordCount)
{ {
commandText = string.Empty; commandText = string.Empty;
@ -3241,6 +3312,7 @@ namespace Emby.Server.Implementations.Data
{ {
statementTexts.Add(commandText); statementTexts.Add(commandText);
} }
if (query.EnableTotalRecordCount) if (query.EnableTotalRecordCount)
{ {
commandText = string.Empty; commandText = string.Empty;
@ -3594,11 +3666,13 @@ namespace Emby.Server.Implementations.Data
whereClauses.Add("IndexNumber=@IndexNumber"); whereClauses.Add("IndexNumber=@IndexNumber");
statement?.TryBind("@IndexNumber", query.IndexNumber.Value); statement?.TryBind("@IndexNumber", query.IndexNumber.Value);
} }
if (query.ParentIndexNumber.HasValue) if (query.ParentIndexNumber.HasValue)
{ {
whereClauses.Add("ParentIndexNumber=@ParentIndexNumber"); whereClauses.Add("ParentIndexNumber=@ParentIndexNumber");
statement?.TryBind("@ParentIndexNumber", query.ParentIndexNumber.Value); statement?.TryBind("@ParentIndexNumber", query.ParentIndexNumber.Value);
} }
if (query.ParentIndexNumberNotEquals.HasValue) if (query.ParentIndexNumberNotEquals.HasValue)
{ {
whereClauses.Add("(ParentIndexNumber<>@ParentIndexNumberNotEquals or ParentIndexNumber is null)"); whereClauses.Add("(ParentIndexNumber<>@ParentIndexNumberNotEquals or ParentIndexNumber is null)");
@ -3884,6 +3958,7 @@ namespace Emby.Server.Implementations.Data
{ {
statement.TryBind(paramName, artistId.ToByteArray()); statement.TryBind(paramName, artistId.ToByteArray());
} }
index++; index++;
} }
@ -3904,6 +3979,7 @@ namespace Emby.Server.Implementations.Data
{ {
statement.TryBind(paramName, artistId.ToByteArray()); statement.TryBind(paramName, artistId.ToByteArray());
} }
index++; index++;
} }
@ -3924,8 +4000,10 @@ namespace Emby.Server.Implementations.Data
{ {
statement.TryBind(paramName, artistId.ToByteArray()); statement.TryBind(paramName, artistId.ToByteArray());
} }
index++; index++;
} }
var clause = "(" + string.Join(" OR ", clauses) + ")"; var clause = "(" + string.Join(" OR ", clauses) + ")";
whereClauses.Add(clause); whereClauses.Add(clause);
} }
@ -3943,8 +4021,10 @@ namespace Emby.Server.Implementations.Data
{ {
statement.TryBind(paramName, albumId.ToByteArray()); statement.TryBind(paramName, albumId.ToByteArray());
} }
index++; index++;
} }
var clause = "(" + string.Join(" OR ", clauses) + ")"; var clause = "(" + string.Join(" OR ", clauses) + ")";
whereClauses.Add(clause); whereClauses.Add(clause);
} }
@ -3962,8 +4042,10 @@ namespace Emby.Server.Implementations.Data
{ {
statement.TryBind(paramName, artistId.ToByteArray()); statement.TryBind(paramName, artistId.ToByteArray());
} }
index++; index++;
} }
var clause = "(" + string.Join(" OR ", clauses) + ")"; var clause = "(" + string.Join(" OR ", clauses) + ")";
whereClauses.Add(clause); whereClauses.Add(clause);
} }
@ -3981,8 +4063,10 @@ namespace Emby.Server.Implementations.Data
{ {
statement.TryBind(paramName, genreId.ToByteArray()); statement.TryBind(paramName, genreId.ToByteArray());
} }
index++; index++;
} }
var clause = "(" + string.Join(" OR ", clauses) + ")"; var clause = "(" + string.Join(" OR ", clauses) + ")";
whereClauses.Add(clause); whereClauses.Add(clause);
} }
@ -3998,8 +4082,10 @@ namespace Emby.Server.Implementations.Data
{ {
statement.TryBind("@Genre" + index, GetCleanValue(item)); statement.TryBind("@Genre" + index, GetCleanValue(item));
} }
index++; index++;
} }
var clause = "(" + string.Join(" OR ", clauses) + ")"; var clause = "(" + string.Join(" OR ", clauses) + ")";
whereClauses.Add(clause); whereClauses.Add(clause);
} }
@ -4015,8 +4101,10 @@ namespace Emby.Server.Implementations.Data
{ {
statement.TryBind("@Tag" + index, GetCleanValue(item)); statement.TryBind("@Tag" + index, GetCleanValue(item));
} }
index++; index++;
} }
var clause = "(" + string.Join(" OR ", clauses) + ")"; var clause = "(" + string.Join(" OR ", clauses) + ")";
whereClauses.Add(clause); whereClauses.Add(clause);
} }
@ -4032,8 +4120,10 @@ namespace Emby.Server.Implementations.Data
{ {
statement.TryBind("@ExcludeTag" + index, GetCleanValue(item)); statement.TryBind("@ExcludeTag" + index, GetCleanValue(item));
} }
index++; index++;
} }
var clause = "(" + string.Join(" OR ", clauses) + ")"; var clause = "(" + string.Join(" OR ", clauses) + ")";
whereClauses.Add(clause); whereClauses.Add(clause);
} }
@ -4052,8 +4142,10 @@ namespace Emby.Server.Implementations.Data
{ {
statement.TryBind(paramName, studioId.ToByteArray()); statement.TryBind(paramName, studioId.ToByteArray());
} }
index++; index++;
} }
var clause = "(" + string.Join(" OR ", clauses) + ")"; var clause = "(" + string.Join(" OR ", clauses) + ")";
whereClauses.Add(clause); whereClauses.Add(clause);
} }
@ -4069,8 +4161,10 @@ namespace Emby.Server.Implementations.Data
{ {
statement.TryBind("@OfficialRating" + index, item); statement.TryBind("@OfficialRating" + index, item);
} }
index++; index++;
} }
var clause = "(" + string.Join(" OR ", clauses) + ")"; var clause = "(" + string.Join(" OR ", clauses) + ")";
whereClauses.Add(clause); whereClauses.Add(clause);
} }
@ -4245,6 +4339,7 @@ namespace Emby.Server.Implementations.Data
statement.TryBind("@IsVirtualItem", isVirtualItem.Value); statement.TryBind("@IsVirtualItem", isVirtualItem.Value);
} }
} }
if (query.IsSpecialSeason.HasValue) if (query.IsSpecialSeason.HasValue)
{ {
if (query.IsSpecialSeason.Value) if (query.IsSpecialSeason.Value)
@ -4256,6 +4351,7 @@ namespace Emby.Server.Implementations.Data
whereClauses.Add("IndexNumber <> 0"); whereClauses.Add("IndexNumber <> 0");
} }
} }
if (query.IsUnaired.HasValue) if (query.IsUnaired.HasValue)
{ {
if (query.IsUnaired.Value) if (query.IsUnaired.Value)
@ -4267,6 +4363,7 @@ namespace Emby.Server.Implementations.Data
whereClauses.Add("PremiereDate < DATETIME('now')"); whereClauses.Add("PremiereDate < DATETIME('now')");
} }
} }
var queryMediaTypes = query.MediaTypes.Where(IsValidMediaType).ToArray(); var queryMediaTypes = query.MediaTypes.Where(IsValidMediaType).ToArray();
if (queryMediaTypes.Length == 1) if (queryMediaTypes.Length == 1)
{ {
@ -4282,6 +4379,7 @@ namespace Emby.Server.Implementations.Data
whereClauses.Add("MediaType in (" + val + ")"); whereClauses.Add("MediaType in (" + val + ")");
} }
if (query.ItemIds.Length > 0) if (query.ItemIds.Length > 0)
{ {
var includeIds = new List<string>(); var includeIds = new List<string>();
@ -4294,11 +4392,13 @@ namespace Emby.Server.Implementations.Data
{ {
statement.TryBind("@IncludeId" + index, id); statement.TryBind("@IncludeId" + index, id);
} }
index++; index++;
} }
whereClauses.Add("(" + string.Join(" OR ", includeIds) + ")"); whereClauses.Add("(" + string.Join(" OR ", includeIds) + ")");
} }
if (query.ExcludeItemIds.Length > 0) if (query.ExcludeItemIds.Length > 0)
{ {
var excludeIds = new List<string>(); var excludeIds = new List<string>();
@ -4311,6 +4411,7 @@ namespace Emby.Server.Implementations.Data
{ {
statement.TryBind("@ExcludeId" + index, id); statement.TryBind("@ExcludeId" + index, id);
} }
index++; index++;
} }
@ -4324,7 +4425,7 @@ namespace Emby.Server.Implementations.Data
var index = 0; var index = 0;
foreach (var pair in query.ExcludeProviderIds) foreach (var pair in query.ExcludeProviderIds)
{ {
if (string.Equals(pair.Key, MetadataProviders.TmdbCollection.ToString(), StringComparison.OrdinalIgnoreCase)) if (string.Equals(pair.Key, MetadataProvider.TmdbCollection.ToString(), StringComparison.OrdinalIgnoreCase))
{ {
continue; continue;
} }
@ -4335,6 +4436,7 @@ namespace Emby.Server.Implementations.Data
{ {
statement.TryBind(paramName, "%" + pair.Key + "=" + pair.Value + "%"); statement.TryBind(paramName, "%" + pair.Key + "=" + pair.Value + "%");
} }
index++; index++;
break; break;
@ -4353,7 +4455,7 @@ namespace Emby.Server.Implementations.Data
var index = 0; var index = 0;
foreach (var pair in query.HasAnyProviderId) foreach (var pair in query.HasAnyProviderId)
{ {
if (string.Equals(pair.Key, MetadataProviders.TmdbCollection.ToString(), StringComparison.OrdinalIgnoreCase)) if (string.Equals(pair.Key, MetadataProvider.TmdbCollection.ToString(), StringComparison.OrdinalIgnoreCase))
{ {
continue; continue;
} }
@ -4377,6 +4479,7 @@ namespace Emby.Server.Implementations.Data
{ {
statement.TryBind(paramName, "%" + pair.Key + "=" + pair.Value + "%"); statement.TryBind(paramName, "%" + pair.Key + "=" + pair.Value + "%");
} }
index++; index++;
break; break;
@ -4427,6 +4530,7 @@ namespace Emby.Server.Implementations.Data
{ {
whereClauses.Add("(TopParentId=@TopParentId)"); whereClauses.Add("(TopParentId=@TopParentId)");
} }
if (statement != null) if (statement != null)
{ {
statement.TryBind("@TopParentId", queryTopParentIds[0].ToString("N", CultureInfo.InvariantCulture)); statement.TryBind("@TopParentId", queryTopParentIds[0].ToString("N", CultureInfo.InvariantCulture));
@ -4464,11 +4568,13 @@ namespace Emby.Server.Implementations.Data
statement.TryBind("@AncestorId", query.AncestorIds[0]); statement.TryBind("@AncestorId", query.AncestorIds[0]);
} }
} }
if (query.AncestorIds.Length > 1) if (query.AncestorIds.Length > 1)
{ {
var inClause = string.Join(",", query.AncestorIds.Select(i => "'" + i.ToString("N", CultureInfo.InvariantCulture) + "'")); var inClause = string.Join(",", query.AncestorIds.Select(i => "'" + i.ToString("N", CultureInfo.InvariantCulture) + "'"));
whereClauses.Add(string.Format("Guid in (select itemId from AncestorIds where AncestorIdText in ({0}))", inClause)); whereClauses.Add(string.Format("Guid in (select itemId from AncestorIds where AncestorIdText in ({0}))", inClause));
} }
if (!string.IsNullOrWhiteSpace(query.AncestorWithPresentationUniqueKey)) if (!string.IsNullOrWhiteSpace(query.AncestorWithPresentationUniqueKey))
{ {
var inClause = "select guid from TypedBaseItems where PresentationUniqueKey=@AncestorWithPresentationUniqueKey"; var inClause = "select guid from TypedBaseItems where PresentationUniqueKey=@AncestorWithPresentationUniqueKey";
@ -4497,6 +4603,7 @@ namespace Emby.Server.Implementations.Data
statement.TryBind("@UnratedType", query.BlockUnratedItems[0].ToString()); statement.TryBind("@UnratedType", query.BlockUnratedItems[0].ToString());
} }
} }
if (query.BlockUnratedItems.Length > 1) if (query.BlockUnratedItems.Length > 1)
{ {
var inClause = string.Join(",", query.BlockUnratedItems.Select(i => "'" + i.ToString() + "'")); var inClause = string.Join(",", query.BlockUnratedItems.Select(i => "'" + i.ToString() + "'"));
@ -4789,7 +4896,6 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
connection.RunInTransaction(db => connection.RunInTransaction(db =>
{ {
connection.ExecuteAll(sql); connection.ExecuteAll(sql);
}, TransactionMode); }, TransactionMode);
} }
} }
@ -4972,6 +5078,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
statement.TryBind("@ItemId", query.ItemId.ToByteArray()); statement.TryBind("@ItemId", query.ItemId.ToByteArray());
} }
} }
if (!query.AppearsInItemId.Equals(Guid.Empty)) if (!query.AppearsInItemId.Equals(Guid.Empty))
{ {
whereClauses.Add("Name in (Select Name from People where ItemId=@AppearsInItemId)"); whereClauses.Add("Name in (Select Name from People where ItemId=@AppearsInItemId)");
@ -4980,6 +5087,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
statement.TryBind("@AppearsInItemId", query.AppearsInItemId.ToByteArray()); statement.TryBind("@AppearsInItemId", query.AppearsInItemId.ToByteArray());
} }
} }
var queryPersonTypes = query.PersonTypes.Where(IsValidPersonType).ToList(); var queryPersonTypes = query.PersonTypes.Where(IsValidPersonType).ToList();
if (queryPersonTypes.Count == 1) if (queryPersonTypes.Count == 1)
@ -4996,6 +5104,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
whereClauses.Add("PersonType in (" + val + ")"); whereClauses.Add("PersonType in (" + val + ")");
} }
var queryExcludePersonTypes = query.ExcludePersonTypes.Where(IsValidPersonType).ToList(); var queryExcludePersonTypes = query.ExcludePersonTypes.Where(IsValidPersonType).ToList();
if (queryExcludePersonTypes.Count == 1) if (queryExcludePersonTypes.Count == 1)
@ -5012,6 +5121,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
whereClauses.Add("PersonType not in (" + val + ")"); whereClauses.Add("PersonType not in (" + val + ")");
} }
if (query.MaxListOrder.HasValue) if (query.MaxListOrder.HasValue)
{ {
whereClauses.Add("ListOrder<=@MaxListOrder"); whereClauses.Add("ListOrder<=@MaxListOrder");
@ -5020,6 +5130,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
statement.TryBind("@MaxListOrder", query.MaxListOrder.Value); statement.TryBind("@MaxListOrder", query.MaxListOrder.Value);
} }
} }
if (!string.IsNullOrWhiteSpace(query.NameContains)) if (!string.IsNullOrWhiteSpace(query.NameContains))
{ {
whereClauses.Add("Name like @NameContains"); whereClauses.Add("Name like @NameContains");
@ -5159,6 +5270,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
var typeString = string.Join(",", withItemTypes.Select(i => "'" + i + "'")); var typeString = string.Join(",", withItemTypes.Select(i => "'" + i + "'"));
commandText += " AND ItemId In (select guid from typedbaseitems where type in (" + typeString + "))"; commandText += " AND ItemId In (select guid from typedbaseitems where type in (" + typeString + "))";
} }
if (excludeItemTypes.Count > 0) if (excludeItemTypes.Count > 0)
{ {
var typeString = string.Join(",", excludeItemTypes.Select(i => "'" + i + "'")); var typeString = string.Join(",", excludeItemTypes.Select(i => "'" + i + "'"));
@ -5180,7 +5292,6 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
} }
} }
} }
} }
LogQueryTime("GetItemValueNames", commandText, now); LogQueryTime("GetItemValueNames", commandText, now);
@ -5631,7 +5742,6 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
db.Execute("delete from People where ItemId=@ItemId", itemIdBlob); db.Execute("delete from People where ItemId=@ItemId", itemIdBlob);
InsertPeople(itemIdBlob, people, db); InsertPeople(itemIdBlob, people, db);
}, TransactionMode); }, TransactionMode);
} }
} }
@ -5788,7 +5898,6 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
db.Execute("delete from mediastreams where ItemId=@ItemId", itemIdBlob); db.Execute("delete from mediastreams where ItemId=@ItemId", itemIdBlob);
InsertMediaStreams(itemIdBlob, streams, db); InsertMediaStreams(itemIdBlob, streams, db);
}, TransactionMode); }, TransactionMode);
} }
} }
@ -6134,7 +6243,6 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
db.Execute("delete from mediaattachments where ItemId=@ItemId", itemIdBlob); db.Execute("delete from mediaattachments where ItemId=@ItemId", itemIdBlob);
InsertMediaAttachments(itemIdBlob, attachments, db, cancellationToken); InsertMediaAttachments(itemIdBlob, attachments, db, cancellationToken);
}, TransactionMode); }, TransactionMode);
} }
} }

@ -4,6 +4,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Threading; using System.Threading;
using Jellyfin.Data.Entities;
using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
@ -134,10 +135,12 @@ namespace Emby.Server.Implementations.Data
{ {
throw new ArgumentNullException(nameof(userData)); throw new ArgumentNullException(nameof(userData));
} }
if (internalUserId <= 0) if (internalUserId <= 0)
{ {
throw new ArgumentNullException(nameof(internalUserId)); throw new ArgumentNullException(nameof(internalUserId));
} }
if (string.IsNullOrEmpty(key)) if (string.IsNullOrEmpty(key))
{ {
throw new ArgumentNullException(nameof(key)); throw new ArgumentNullException(nameof(key));
@ -152,6 +155,7 @@ namespace Emby.Server.Implementations.Data
{ {
throw new ArgumentNullException(nameof(userData)); throw new ArgumentNullException(nameof(userData));
} }
if (internalUserId <= 0) if (internalUserId <= 0)
{ {
throw new ArgumentNullException(nameof(internalUserId)); throw new ArgumentNullException(nameof(internalUserId));
@ -234,7 +238,7 @@ namespace Emby.Server.Implementations.Data
} }
/// <summary> /// <summary>
/// Persist all user data for the specified user /// Persist all user data for the specified user.
/// </summary> /// </summary>
private void PersistAllUserData(long internalUserId, UserItemData[] userDataList, CancellationToken cancellationToken) private void PersistAllUserData(long internalUserId, UserItemData[] userDataList, CancellationToken cancellationToken)
{ {
@ -308,7 +312,7 @@ namespace Emby.Server.Implementations.Data
} }
/// <summary> /// <summary>
/// Return all user-data associated with the given user /// Return all user-data associated with the given user.
/// </summary> /// </summary>
/// <param name="internalUserId"></param> /// <param name="internalUserId"></param>
/// <returns></returns> /// <returns></returns>
@ -338,7 +342,7 @@ namespace Emby.Server.Implementations.Data
} }
/// <summary> /// <summary>
/// Read a row from the specified reader into the provided userData object /// Read a row from the specified reader into the provided userData object.
/// </summary> /// </summary>
/// <param name="reader"></param> /// <param name="reader"></param>
private UserItemData ReadRow(IReadOnlyList<IResultSetValue> reader) private UserItemData ReadRow(IReadOnlyList<IResultSetValue> reader)

@ -1,240 +0,0 @@
#pragma warning disable CS1591
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.Json;
using MediaBrowser.Common.Json;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Persistence;
using Microsoft.Extensions.Logging;
using SQLitePCL.pretty;
namespace Emby.Server.Implementations.Data
{
/// <summary>
/// Class SQLiteUserRepository
/// </summary>
public class SqliteUserRepository : BaseSqliteRepository, IUserRepository
{
private readonly JsonSerializerOptions _jsonOptions;
public SqliteUserRepository(
ILogger<SqliteUserRepository> logger,
IServerApplicationPaths appPaths)
: base(logger)
{
_jsonOptions = JsonDefaults.GetOptions();
DbFilePath = Path.Combine(appPaths.DataPath, "users.db");
}
/// <summary>
/// Gets the name of the repository
/// </summary>
/// <value>The name.</value>
public string Name => "SQLite";
/// <summary>
/// Opens the connection to the database.
/// </summary>
public void Initialize()
{
using (var connection = GetConnection())
{
var localUsersTableExists = TableExists(connection, "LocalUsersv2");
connection.RunQueries(new[] {
"create table if not exists LocalUsersv2 (Id INTEGER PRIMARY KEY, guid GUID NOT NULL, data BLOB NOT NULL)",
"drop index if exists idx_users"
});
if (!localUsersTableExists && TableExists(connection, "Users"))
{
TryMigrateToLocalUsersTable(connection);
}
RemoveEmptyPasswordHashes(connection);
}
}
private void TryMigrateToLocalUsersTable(ManagedConnection connection)
{
try
{
connection.RunQueries(new[]
{
"INSERT INTO LocalUsersv2 (guid, data) SELECT guid,data from users"
});
}
catch (Exception ex)
{
Logger.LogError(ex, "Error migrating users database");
}
}
private void RemoveEmptyPasswordHashes(ManagedConnection connection)
{
foreach (var user in RetrieveAllUsers(connection))
{
// If the user password is the sha1 hash of the empty string, remove it
if (!string.Equals(user.Password, "DA39A3EE5E6B4B0D3255BFEF95601890AFD80709", StringComparison.Ordinal)
&& !string.Equals(user.Password, "$SHA1$DA39A3EE5E6B4B0D3255BFEF95601890AFD80709", StringComparison.Ordinal))
{
continue;
}
user.Password = null;
var serialized = JsonSerializer.SerializeToUtf8Bytes(user, _jsonOptions);
connection.RunInTransaction(db =>
{
using (var statement = db.PrepareStatement("update LocalUsersv2 set data=@data where Id=@InternalId"))
{
statement.TryBind("@InternalId", user.InternalId);
statement.TryBind("@data", serialized);
statement.MoveNext();
}
}, TransactionMode);
}
}
/// <summary>
/// Save a user in the repo
/// </summary>
public void CreateUser(User user)
{
if (user == null)
{
throw new ArgumentNullException(nameof(user));
}
var serialized = JsonSerializer.SerializeToUtf8Bytes(user, _jsonOptions);
using (var connection = GetConnection())
{
connection.RunInTransaction(db =>
{
using (var statement = db.PrepareStatement("insert into LocalUsersv2 (guid, data) values (@guid, @data)"))
{
statement.TryBind("@guid", user.Id.ToByteArray());
statement.TryBind("@data", serialized);
statement.MoveNext();
}
var createdUser = GetUser(user.Id, connection);
if (createdUser == null)
{
throw new ApplicationException("created user should never be null");
}
user.InternalId = createdUser.InternalId;
}, TransactionMode);
}
}
public void UpdateUser(User user)
{
if (user == null)
{
throw new ArgumentNullException(nameof(user));
}
var serialized = JsonSerializer.SerializeToUtf8Bytes(user, _jsonOptions);
using (var connection = GetConnection())
{
connection.RunInTransaction(db =>
{
using (var statement = db.PrepareStatement("update LocalUsersv2 set data=@data where Id=@InternalId"))
{
statement.TryBind("@InternalId", user.InternalId);
statement.TryBind("@data", serialized);
statement.MoveNext();
}
}, TransactionMode);
}
}
private User GetUser(Guid guid, ManagedConnection connection)
{
using (var statement = connection.PrepareStatement("select id,guid,data from LocalUsersv2 where guid=@guid"))
{
statement.TryBind("@guid", guid);
foreach (var row in statement.ExecuteQuery())
{
return GetUser(row);
}
}
return null;
}
private User GetUser(IReadOnlyList<IResultSetValue> row)
{
var id = row[0].ToInt64();
var guid = row[1].ReadGuidFromBlob();
var user = JsonSerializer.Deserialize<User>(row[2].ToBlob(), _jsonOptions);
user.InternalId = id;
user.Id = guid;
return user;
}
/// <summary>
/// Retrieve all users from the database
/// </summary>
/// <returns>IEnumerable{User}.</returns>
public List<User> RetrieveAllUsers()
{
using (var connection = GetConnection(true))
{
return new List<User>(RetrieveAllUsers(connection));
}
}
/// <summary>
/// Retrieve all users from the database
/// </summary>
/// <returns>IEnumerable{User}.</returns>
private IEnumerable<User> RetrieveAllUsers(ManagedConnection connection)
{
foreach (var row in connection.Query("select id,guid,data from LocalUsersv2"))
{
yield return GetUser(row);
}
}
/// <summary>
/// Deletes the user.
/// </summary>
/// <param name="user">The user.</param>
/// <returns>Task.</returns>
/// <exception cref="ArgumentNullException">user</exception>
public void DeleteUser(User user)
{
if (user == null)
{
throw new ArgumentNullException(nameof(user));
}
using (var connection = GetConnection())
{
connection.RunInTransaction(db =>
{
using (var statement = db.PrepareStatement("delete from LocalUsersv2 where Id=@id"))
{
statement.TryBind("@id", user.InternalId);
statement.MoveNext();
}
}, TransactionMode);
}
}
}
}

@ -12,7 +12,7 @@ namespace Emby.Server.Implementations.Devices
public class DeviceId public class DeviceId
{ {
private readonly IApplicationPaths _appPaths; private readonly IApplicationPaths _appPaths;
private readonly ILogger _logger; private readonly ILogger<DeviceId> _logger;
private readonly object _syncLock = new object(); private readonly object _syncLock = new object();
@ -90,7 +90,7 @@ namespace Emby.Server.Implementations.Devices
public DeviceId(IApplicationPaths appPaths, ILoggerFactory loggerFactory) public DeviceId(IApplicationPaths appPaths, ILoggerFactory loggerFactory)
{ {
_appPaths = appPaths; _appPaths = appPaths;
_logger = loggerFactory.CreateLogger("SystemId"); _logger = loggerFactory.CreateLogger<DeviceId>();
} }
public string Value => _id ?? (_id = GetDeviceId()); public string Value => _id ?? (_id = GetDeviceId());

@ -5,10 +5,11 @@ using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using Jellyfin.Data.Enums;
using Jellyfin.Data.Entities;
using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Devices; using MediaBrowser.Controller.Devices;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Security; using MediaBrowser.Controller.Security;
using MediaBrowser.Model.Devices; using MediaBrowser.Model.Devices;
@ -16,7 +17,6 @@ using MediaBrowser.Model.Events;
using MediaBrowser.Model.Querying; using MediaBrowser.Model.Querying;
using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.Session; using MediaBrowser.Model.Session;
using MediaBrowser.Model.Users;
namespace Emby.Server.Implementations.Devices namespace Emby.Server.Implementations.Devices
{ {
@ -27,11 +27,10 @@ namespace Emby.Server.Implementations.Devices
private readonly IServerConfigurationManager _config; private readonly IServerConfigurationManager _config;
private readonly IAuthenticationRepository _authRepo; private readonly IAuthenticationRepository _authRepo;
private readonly Dictionary<string, ClientCapabilities> _capabilitiesCache; private readonly Dictionary<string, ClientCapabilities> _capabilitiesCache;
private readonly object _capabilitiesSyncLock = new object();
public event EventHandler<GenericEventArgs<Tuple<string, DeviceOptions>>> DeviceOptionsUpdated; public event EventHandler<GenericEventArgs<Tuple<string, DeviceOptions>>> DeviceOptionsUpdated;
private readonly object _capabilitiesSyncLock = new object();
public DeviceManager( public DeviceManager(
IAuthenticationRepository authRepo, IAuthenticationRepository authRepo,
IJsonSerializer json, IJsonSerializer json,
@ -170,12 +169,18 @@ namespace Emby.Server.Implementations.Devices
{ {
throw new ArgumentException("user not found"); throw new ArgumentException("user not found");
} }
if (string.IsNullOrEmpty(deviceId)) if (string.IsNullOrEmpty(deviceId))
{ {
throw new ArgumentNullException(nameof(deviceId)); throw new ArgumentNullException(nameof(deviceId));
} }
if (!CanAccessDevice(user.Policy, deviceId)) if (user.HasPermission(PermissionKind.EnableAllDevices) || user.HasPermission(PermissionKind.IsAdministrator))
{
return true;
}
if (!user.GetPreference(PreferenceKind.EnabledDevices).Contains(deviceId, StringComparer.OrdinalIgnoreCase))
{ {
var capabilities = GetCapabilities(deviceId); var capabilities = GetCapabilities(deviceId);
@ -187,20 +192,5 @@ namespace Emby.Server.Implementations.Devices
return true; return true;
} }
private static bool CanAccessDevice(UserPolicy policy, string id)
{
if (policy.EnableAllDevices)
{
return true;
}
if (policy.IsAdministrator)
{
return true;
}
return policy.EnabledDevices.Contains(id, StringComparer.OrdinalIgnoreCase);
}
} }
} }

@ -6,14 +6,14 @@ using System.Globalization;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums;
using MediaBrowser.Common; using MediaBrowser.Common;
using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Controller.Persistence; using MediaBrowser.Controller.Persistence;
@ -24,12 +24,20 @@ using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Querying; using MediaBrowser.Model.Querying;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Book = MediaBrowser.Controller.Entities.Book;
using Episode = MediaBrowser.Controller.Entities.TV.Episode;
using Movie = MediaBrowser.Controller.Entities.Movies.Movie;
using MusicAlbum = MediaBrowser.Controller.Entities.Audio.MusicAlbum;
using Person = MediaBrowser.Controller.Entities.Person;
using Photo = MediaBrowser.Controller.Entities.Photo;
using Season = MediaBrowser.Controller.Entities.TV.Season;
using Series = MediaBrowser.Controller.Entities.TV.Series;
namespace Emby.Server.Implementations.Dto namespace Emby.Server.Implementations.Dto
{ {
public class DtoService : IDtoService public class DtoService : IDtoService
{ {
private readonly ILogger _logger; private readonly ILogger<DtoService> _logger;
private readonly ILibraryManager _libraryManager; private readonly ILibraryManager _libraryManager;
private readonly IUserDataManager _userDataRepository; private readonly IUserDataManager _userDataRepository;
private readonly IItemRepository _itemRepo; private readonly IItemRepository _itemRepo;
@ -66,7 +74,7 @@ namespace Emby.Server.Implementations.Dto
} }
/// <summary> /// <summary>
/// Converts a BaseItem to a DTOBaseItem /// Converts a BaseItem to a DTOBaseItem.
/// </summary> /// </summary>
/// <param name="item">The item.</param> /// <param name="item">The item.</param>
/// <param name="fields">The fields.</param> /// <param name="fields">The fields.</param>
@ -269,6 +277,7 @@ namespace Emby.Server.Implementations.Dto
dto.EpisodeTitle = dto.Name; dto.EpisodeTitle = dto.Name;
dto.Name = dto.SeriesName; dto.Name = dto.SeriesName;
} }
liveTvManager.AddInfoToRecordingDto(item, dto, activeRecording, user); liveTvManager.AddInfoToRecordingDto(item, dto, activeRecording, user);
} }
@ -284,6 +293,7 @@ namespace Emby.Server.Implementations.Dto
{ {
continue; continue;
} }
var containers = container.Split(new[] { ',' }); var containers = container.Split(new[] { ',' });
if (containers.Length < 2) if (containers.Length < 2)
{ {
@ -384,7 +394,7 @@ namespace Emby.Server.Implementations.Dto
if (options.ContainsField(ItemFields.ChildCount)) if (options.ContainsField(ItemFields.ChildCount))
{ {
dto.ChildCount = dto.ChildCount ?? GetChildCount(folder, user); dto.ChildCount ??= GetChildCount(folder, user);
} }
} }
@ -398,7 +408,6 @@ namespace Emby.Server.Implementations.Dto
dto.DateLastMediaAdded = folder.DateLastMediaAdded; dto.DateLastMediaAdded = folder.DateLastMediaAdded;
} }
} }
else else
{ {
if (options.EnableUserData) if (options.EnableUserData)
@ -414,7 +423,7 @@ namespace Emby.Server.Implementations.Dto
if (options.ContainsField(ItemFields.BasicSyncInfo)) if (options.ContainsField(ItemFields.BasicSyncInfo))
{ {
var userCanSync = user != null && user.Policy.EnableContentDownloading; var userCanSync = user != null && user.HasPermission(PermissionKind.EnableContentDownloading);
if (userCanSync && item.SupportsExternalTransfer) if (userCanSync && item.SupportsExternalTransfer)
{ {
dto.SupportsSync = true; dto.SupportsSync = true;
@ -435,7 +444,7 @@ namespace Emby.Server.Implementations.Dto
} }
/// <summary> /// <summary>
/// Gets client-side Id of a server-side BaseItem /// Gets client-side Id of a server-side BaseItem.
/// </summary> /// </summary>
/// <param name="item">The item.</param> /// <param name="item">The item.</param>
/// <returns>System.String.</returns> /// <returns>System.String.</returns>
@ -449,6 +458,7 @@ namespace Emby.Server.Implementations.Dto
{ {
dto.SeriesName = item.SeriesName; dto.SeriesName = item.SeriesName;
} }
private static void SetPhotoProperties(BaseItemDto dto, Photo item) private static void SetPhotoProperties(BaseItemDto dto, Photo item)
{ {
dto.CameraMake = item.CameraMake; dto.CameraMake = item.CameraMake;
@ -530,7 +540,7 @@ namespace Emby.Server.Implementations.Dto
} }
/// <summary> /// <summary>
/// Attaches People DTO's to a DTOBaseItem /// Attaches People DTO's to a DTOBaseItem.
/// </summary> /// </summary>
/// <param name="dto">The dto.</param> /// <param name="dto">The dto.</param>
/// <param name="item">The item.</param> /// <param name="item">The item.</param>
@ -547,22 +557,27 @@ namespace Emby.Server.Implementations.Dto
{ {
return 0; return 0;
} }
if (i.IsType(PersonType.GuestStar)) if (i.IsType(PersonType.GuestStar))
{ {
return 1; return 1;
} }
if (i.IsType(PersonType.Director)) if (i.IsType(PersonType.Director))
{ {
return 2; return 2;
} }
if (i.IsType(PersonType.Writer)) if (i.IsType(PersonType.Writer))
{ {
return 3; return 3;
} }
if (i.IsType(PersonType.Producer)) if (i.IsType(PersonType.Producer))
{ {
return 4; return 4;
} }
if (i.IsType(PersonType.Composer)) if (i.IsType(PersonType.Composer))
{ {
return 4; return 4;
@ -586,7 +601,6 @@ namespace Emby.Server.Implementations.Dto
_logger.LogError(ex, "Error getting person {Name}", c); _logger.LogError(ex, "Error getting person {Name}", c);
return null; return null;
} }
}).Where(i => i != null) }).Where(i => i != null)
.GroupBy(i => i.Name, StringComparer.OrdinalIgnoreCase) .GroupBy(i => i.Name, StringComparer.OrdinalIgnoreCase)
.Select(x => x.First()) .Select(x => x.First())
@ -607,6 +621,7 @@ namespace Emby.Server.Implementations.Dto
{ {
baseItemPerson.PrimaryImageTag = GetTagAndFillBlurhash(dto, entity, ImageType.Primary); baseItemPerson.PrimaryImageTag = GetTagAndFillBlurhash(dto, entity, ImageType.Primary);
baseItemPerson.Id = entity.Id.ToString("N", CultureInfo.InvariantCulture); baseItemPerson.Id = entity.Id.ToString("N", CultureInfo.InvariantCulture);
baseItemPerson.ImageBlurHashes = dto.ImageBlurHashes;
list.Add(baseItemPerson); list.Add(baseItemPerson);
} }
} }
@ -719,7 +734,7 @@ namespace Emby.Server.Implementations.Dto
} }
/// <summary> /// <summary>
/// Sets simple property values on a DTOBaseItem /// Sets simple property values on a DTOBaseItem.
/// </summary> /// </summary>
/// <param name="dto">The dto.</param> /// <param name="dto">The dto.</param>
/// <param name="item">The item.</param> /// <param name="item">The item.</param>
@ -993,7 +1008,6 @@ namespace Emby.Server.Implementations.Dto
} }
return null; return null;
}).Where(i => i != null).ToArray(); }).Where(i => i != null).ToArray();
} }
@ -1044,7 +1058,6 @@ namespace Emby.Server.Implementations.Dto
} }
return null; return null;
}).Where(i => i != null).ToArray(); }).Where(i => i != null).ToArray();
} }
@ -1342,6 +1355,7 @@ namespace Emby.Server.Implementations.Dto
dto.ParentLogoImageTag = GetTagAndFillBlurhash(dto, parent, image); dto.ParentLogoImageTag = GetTagAndFillBlurhash(dto, parent, image);
} }
} }
if (artLimit > 0 && !(imageTags != null && imageTags.ContainsKey(ImageType.Art)) && dto.ParentArtItemId == null) if (artLimit > 0 && !(imageTags != null && imageTags.ContainsKey(ImageType.Art)) && dto.ParentArtItemId == null)
{ {
var image = allImages.FirstOrDefault(i => i.Type == ImageType.Art); var image = allImages.FirstOrDefault(i => i.Type == ImageType.Art);
@ -1352,6 +1366,7 @@ namespace Emby.Server.Implementations.Dto
dto.ParentArtImageTag = GetTagAndFillBlurhash(dto, parent, image); dto.ParentArtImageTag = GetTagAndFillBlurhash(dto, parent, image);
} }
} }
if (thumbLimit > 0 && !(imageTags != null && imageTags.ContainsKey(ImageType.Thumb)) && (dto.ParentThumbItemId == null || parent is Series) && !(parent is ICollectionFolder) && !(parent is UserView)) if (thumbLimit > 0 && !(imageTags != null && imageTags.ContainsKey(ImageType.Thumb)) && (dto.ParentThumbItemId == null || parent is Series) && !(parent is ICollectionFolder) && !(parent is UserView))
{ {
var image = allImages.FirstOrDefault(i => i.Type == ImageType.Thumb); var image = allImages.FirstOrDefault(i => i.Type == ImageType.Thumb);
@ -1362,6 +1377,7 @@ namespace Emby.Server.Implementations.Dto
dto.ParentThumbImageTag = GetTagAndFillBlurhash(dto, parent, image); dto.ParentThumbImageTag = GetTagAndFillBlurhash(dto, parent, image);
} }
} }
if (backdropLimit > 0 && !((dto.BackdropImageTags != null && dto.BackdropImageTags.Length > 0) || (dto.ParentBackdropImageTags != null && dto.ParentBackdropImageTags.Length > 0))) if (backdropLimit > 0 && !((dto.BackdropImageTags != null && dto.BackdropImageTags.Length > 0) || (dto.ParentBackdropImageTags != null && dto.ParentBackdropImageTags.Length > 0)))
{ {
var images = allImages.Where(i => i.Type == ImageType.Backdrop).Take(backdropLimit).ToList(); var images = allImages.Where(i => i.Type == ImageType.Backdrop).Take(backdropLimit).ToList();

@ -24,7 +24,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="IPNetwork2" Version="2.4.0.126" /> <PackageReference Include="IPNetwork2" Version="2.5.211" />
<PackageReference Include="Jellyfin.XmlTv" Version="10.4.3" /> <PackageReference Include="Jellyfin.XmlTv" Version="10.4.3" />
<PackageReference Include="Microsoft.AspNetCore.Hosting" Version="2.2.7" /> <PackageReference Include="Microsoft.AspNetCore.Hosting" Version="2.2.7" />
<PackageReference Include="Microsoft.AspNetCore.Hosting.Abstractions" Version="2.2.0" /> <PackageReference Include="Microsoft.AspNetCore.Hosting.Abstractions" Version="2.2.0" />
@ -34,14 +34,14 @@
<PackageReference Include="Microsoft.AspNetCore.ResponseCompression" Version="2.2.0" /> <PackageReference Include="Microsoft.AspNetCore.ResponseCompression" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="2.2.0" /> <PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.WebSockets" Version="2.2.1" /> <PackageReference Include="Microsoft.AspNetCore.WebSockets" Version="2.2.1" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.1.4" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.1.5" />
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="3.1.4" /> <PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="3.1.5" />
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="3.1.4" /> <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="3.1.5" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="3.1.4" /> <PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="3.1.5" />
<PackageReference Include="Mono.Nat" Version="2.0.1" /> <PackageReference Include="Mono.Nat" Version="2.0.1" />
<PackageReference Include="prometheus-net.DotNetRuntime" Version="3.3.1" /> <PackageReference Include="prometheus-net.DotNetRuntime" Version="3.3.1" />
<PackageReference Include="ServiceStack.Text.Core" Version="5.8.0" /> <PackageReference Include="ServiceStack.Text.Core" Version="5.9.0" />
<PackageReference Include="sharpcompress" Version="0.25.0" /> <PackageReference Include="sharpcompress" Version="0.25.1" />
<PackageReference Include="SQLitePCL.pretty.netstandard" Version="2.1.0" /> <PackageReference Include="SQLitePCL.pretty.netstandard" Version="2.1.0" />
<PackageReference Include="DotNet.Glob" Version="3.0.9" /> <PackageReference Include="DotNet.Glob" Version="3.0.9" />
</ItemGroup> </ItemGroup>

@ -23,7 +23,7 @@ namespace Emby.Server.Implementations.EntryPoints
public class ExternalPortForwarding : IServerEntryPoint public class ExternalPortForwarding : IServerEntryPoint
{ {
private readonly IServerApplicationHost _appHost; private readonly IServerApplicationHost _appHost;
private readonly ILogger _logger; private readonly ILogger<ExternalPortForwarding> _logger;
private readonly IServerConfigurationManager _config; private readonly IServerConfigurationManager _config;
private readonly IDeviceDiscovery _deviceDiscovery; private readonly IDeviceDiscovery _deviceDiscovery;

@ -6,6 +6,7 @@ using System.Globalization;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Jellyfin.Data.Entities;
using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Entities.Audio;
@ -28,7 +29,7 @@ namespace Emby.Server.Implementations.EntryPoints
private readonly ISessionManager _sessionManager; private readonly ISessionManager _sessionManager;
private readonly IUserManager _userManager; private readonly IUserManager _userManager;
private readonly ILogger _logger; private readonly ILogger<LibraryChangedNotifier> _logger;
/// <summary> /// <summary>
/// The library changed sync lock. /// The library changed sync lock.
@ -131,7 +132,6 @@ namespace Emby.Server.Implementations.EntryPoints
} }
catch catch
{ {
} }
} }
} }

@ -4,6 +4,7 @@ using System;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Controller.Plugins; using MediaBrowser.Controller.Plugins;
@ -17,7 +18,7 @@ namespace Emby.Server.Implementations.EntryPoints
private readonly ILiveTvManager _liveTvManager; private readonly ILiveTvManager _liveTvManager;
private readonly ISessionManager _sessionManager; private readonly ISessionManager _sessionManager;
private readonly IUserManager _userManager; private readonly IUserManager _userManager;
private readonly ILogger _logger; private readonly ILogger<RecordingNotifier> _logger;
public RecordingNotifier( public RecordingNotifier(
ISessionManager sessionManager, ISessionManager sessionManager,
@ -64,7 +65,7 @@ namespace Emby.Server.Implementations.EntryPoints
private async Task SendMessage(string name, TimerEventInfo info) private async Task SendMessage(string name, TimerEventInfo info)
{ {
var users = _userManager.Users.Where(i => i.Policy.EnableLiveTvAccess).Select(i => i.Id).ToList(); var users = _userManager.Users.Where(i => i.HasPermission(PermissionKind.EnableLiveTvAccess)).Select(i => i.Id).ToList();
try try
{ {

@ -1,77 +0,0 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Tasks;
namespace Emby.Server.Implementations.EntryPoints
{
/// <summary>
/// Class RefreshUsersMetadata.
/// </summary>
public class RefreshUsersMetadata : IScheduledTask, IConfigurableScheduledTask
{
/// <summary>
/// The user manager.
/// </summary>
private readonly IUserManager _userManager;
private readonly IFileSystem _fileSystem;
/// <summary>
/// Initializes a new instance of the <see cref="RefreshUsersMetadata" /> class.
/// </summary>
public RefreshUsersMetadata(IUserManager userManager, IFileSystem fileSystem)
{
_userManager = userManager;
_fileSystem = fileSystem;
}
/// <inheritdoc />
public string Name => "Refresh Users";
/// <inheritdoc />
public string Key => "RefreshUsers";
/// <inheritdoc />
public string Description => "Refresh user infos";
/// <inheritdoc />
public string Category => "Library";
/// <inheritdoc />
public bool IsHidden => true;
/// <inheritdoc />
public bool IsEnabled => true;
/// <inheritdoc />
public bool IsLogged => true;
/// <inheritdoc />
public async Task Execute(CancellationToken cancellationToken, IProgress<double> progress)
{
foreach (var user in _userManager.Users)
{
cancellationToken.ThrowIfCancellationRequested();
await user.RefreshMetadata(new MetadataRefreshOptions(new DirectoryService(_fileSystem)), cancellationToken).ConfigureAwait(false);
}
}
/// <inheritdoc />
public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
{
return new[]
{
new TaskTriggerInfo
{
IntervalTicks = TimeSpan.FromDays(1).Ticks,
Type = TaskTriggerInfo.TriggerInterval
}
};
}
}
}

@ -3,10 +3,10 @@ using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Jellyfin.Data.Entities;
using MediaBrowser.Common.Plugins; using MediaBrowser.Common.Plugins;
using MediaBrowser.Common.Updates; using MediaBrowser.Common.Updates;
using MediaBrowser.Controller; using MediaBrowser.Controller;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Plugins; using MediaBrowser.Controller.Plugins;
using MediaBrowser.Controller.Session; using MediaBrowser.Controller.Session;
@ -68,10 +68,8 @@ namespace Emby.Server.Implementations.EntryPoints
/// <inheritdoc /> /// <inheritdoc />
public Task RunAsync() public Task RunAsync()
{ {
_userManager.UserDeleted += OnUserDeleted; _userManager.OnUserDeleted += OnUserDeleted;
_userManager.UserUpdated += OnUserUpdated; _userManager.OnUserUpdated += OnUserUpdated;
_userManager.UserPolicyUpdated += OnUserPolicyUpdated;
_userManager.UserConfigurationUpdated += OnUserConfigurationUpdated;
_appHost.HasPendingRestartChanged += OnHasPendingRestartChanged; _appHost.HasPendingRestartChanged += OnHasPendingRestartChanged;
@ -153,20 +151,6 @@ namespace Emby.Server.Implementations.EntryPoints
await SendMessageToUserSession(e.Argument, "UserDeleted", e.Argument.Id.ToString("N", CultureInfo.InvariantCulture)).ConfigureAwait(false); await SendMessageToUserSession(e.Argument, "UserDeleted", e.Argument.Id.ToString("N", CultureInfo.InvariantCulture)).ConfigureAwait(false);
} }
private async void OnUserPolicyUpdated(object sender, GenericEventArgs<User> e)
{
var dto = _userManager.GetUserDto(e.Argument);
await SendMessageToUserSession(e.Argument, "UserPolicyUpdated", dto).ConfigureAwait(false);
}
private async void OnUserConfigurationUpdated(object sender, GenericEventArgs<User> e)
{
var dto = _userManager.GetUserDto(e.Argument);
await SendMessageToUserSession(e.Argument, "UserConfigurationUpdated", dto).ConfigureAwait(false);
}
private async Task SendMessageToAdminSessions<T>(string name, T data) private async Task SendMessageToAdminSessions<T>(string name, T data)
{ {
try try
@ -175,7 +159,6 @@ namespace Emby.Server.Implementations.EntryPoints
} }
catch (Exception) catch (Exception)
{ {
} }
} }
@ -191,7 +174,6 @@ namespace Emby.Server.Implementations.EntryPoints
} }
catch (Exception) catch (Exception)
{ {
} }
} }
@ -210,10 +192,8 @@ namespace Emby.Server.Implementations.EntryPoints
{ {
if (dispose) if (dispose)
{ {
_userManager.UserDeleted -= OnUserDeleted; _userManager.OnUserDeleted -= OnUserDeleted;
_userManager.UserUpdated -= OnUserUpdated; _userManager.OnUserUpdated -= OnUserUpdated;
_userManager.UserPolicyUpdated -= OnUserPolicyUpdated;
_userManager.UserConfigurationUpdated -= OnUserConfigurationUpdated;
_installationManager.PluginUninstalled -= OnPluginUninstalled; _installationManager.PluginUninstalled -= OnPluginUninstalled;
_installationManager.PackageInstalling -= OnPackageInstalling; _installationManager.PackageInstalling -= OnPackageInstalling;

@ -21,7 +21,7 @@ namespace Emby.Server.Implementations.EntryPoints
/// <summary> /// <summary>
/// The logger. /// The logger.
/// </summary> /// </summary>
private readonly ILogger _logger; private readonly ILogger<UdpServerEntryPoint> _logger;
private readonly IServerApplicationHost _appHost; private readonly IServerApplicationHost _appHost;
private readonly IConfiguration _config; private readonly IConfiguration _config;
@ -43,7 +43,6 @@ namespace Emby.Server.Implementations.EntryPoints
_logger = logger; _logger = logger;
_appHost = appHost; _appHost = appHost;
_config = configuration; _config = configuration;
} }
/// <inheritdoc /> /// <inheritdoc />

@ -22,7 +22,7 @@ namespace Emby.Server.Implementations.HttpClientManager
/// </summary> /// </summary>
public class HttpClientManager : IHttpClient public class HttpClientManager : IHttpClient
{ {
private readonly ILogger _logger; private readonly ILogger<HttpClientManager> _logger;
private readonly IApplicationPaths _appPaths; private readonly IApplicationPaths _appPaths;
private readonly IFileSystem _fileSystem; private readonly IFileSystem _fileSystem;
private readonly IApplicationHost _appHost; private readonly IApplicationHost _appHost;
@ -140,7 +140,7 @@ namespace Emby.Server.Implementations.HttpClientManager
=> SendAsync(options, HttpMethod.Get); => SendAsync(options, HttpMethod.Get);
/// <summary> /// <summary>
/// Performs a GET request and returns the resulting stream /// Performs a GET request and returns the resulting stream.
/// </summary> /// </summary>
/// <param name="options">The options.</param> /// <param name="options">The options.</param>
/// <returns>Task{Stream}.</returns> /// <returns>Task{Stream}.</returns>

@ -32,12 +32,12 @@ namespace Emby.Server.Implementations.HttpServer
private readonly IFileSystem _fileSystem; private readonly IFileSystem _fileSystem;
/// <summary> /// <summary>
/// The _options /// The _options.
/// </summary> /// </summary>
private readonly IDictionary<string, string> _options = new Dictionary<string, string>(); private readonly IDictionary<string, string> _options = new Dictionary<string, string>();
/// <summary> /// <summary>
/// The _requested ranges /// The _requested ranges.
/// </summary> /// </summary>
private List<KeyValuePair<long, long?>> _requestedRanges; private List<KeyValuePair<long, long?>> _requestedRanges;

@ -41,7 +41,7 @@ namespace Emby.Server.Implementations.HttpServer
/// </summary> /// </summary>
public const string DefaultRedirectKey = "HttpListenerHost:DefaultRedirectPath"; public const string DefaultRedirectKey = "HttpListenerHost:DefaultRedirectPath";
private readonly ILogger _logger; private readonly ILogger<HttpListenerHost> _logger;
private readonly ILoggerFactory _loggerFactory; private readonly ILoggerFactory _loggerFactory;
private readonly IServerConfigurationManager _config; private readonly IServerConfigurationManager _config;
private readonly INetworkManager _networkManager; private readonly INetworkManager _networkManager;
@ -230,7 +230,9 @@ namespace Emby.Server.Implementations.HttpServer
httpRes.StatusCode = statusCode; httpRes.StatusCode = statusCode;
var errContent = NormalizeExceptionMessage(ex) ?? string.Empty; var errContent = _hostEnvironment.IsDevelopment()
? (NormalizeExceptionMessage(ex) ?? string.Empty)
: "Error processing request.";
httpRes.ContentType = "text/plain"; httpRes.ContentType = "text/plain";
httpRes.ContentLength = errContent.Length; httpRes.ContentLength = errContent.Length;
await httpRes.WriteAsync(errContent).ConfigureAwait(false); await httpRes.WriteAsync(errContent).ConfigureAwait(false);
@ -397,7 +399,7 @@ namespace Emby.Server.Implementations.HttpServer
var response = context.Response; var response = context.Response;
var localPath = context.Request.Path.ToString(); var localPath = context.Request.Path.ToString();
var req = new WebSocketSharpRequest(request, response, request.Path, _logger); var req = new WebSocketSharpRequest(request, response, request.Path);
return RequestHandler(req, request.GetDisplayUrl(), request.Host.ToString(), localPath, context.RequestAborted); return RequestHandler(req, request.GetDisplayUrl(), request.Host.ToString(), localPath, context.RequestAborted);
} }
@ -451,6 +453,7 @@ namespace Emby.Server.Implementations.HttpServer
{ {
httpRes.Headers.Add(key, value); httpRes.Headers.Add(key, value);
} }
httpRes.ContentType = "text/plain"; httpRes.ContentType = "text/plain";
await httpRes.WriteAsync(string.Empty, cancellationToken).ConfigureAwait(false); await httpRes.WriteAsync(string.Empty, cancellationToken).ConfigureAwait(false);
return; return;
@ -589,7 +592,7 @@ namespace Emby.Server.Implementations.HttpServer
} }
/// <summary> /// <summary>
/// Get the default CORS headers /// Get the default CORS headers.
/// </summary> /// </summary>
/// <param name="req"></param> /// <param name="req"></param>
/// <returns></returns> /// <returns></returns>

@ -37,7 +37,7 @@ namespace Emby.Server.Implementations.HttpServer
/// <summary> /// <summary>
/// The logger. /// The logger.
/// </summary> /// </summary>
private readonly ILogger _logger; private readonly ILogger<HttpResultFactory> _logger;
private readonly IFileSystem _fileSystem; private readonly IFileSystem _fileSystem;
private readonly IJsonSerializer _jsonSerializer; private readonly IJsonSerializer _jsonSerializer;
private readonly IStreamHelper _streamHelper; private readonly IStreamHelper _streamHelper;
@ -50,7 +50,7 @@ namespace Emby.Server.Implementations.HttpServer
_fileSystem = fileSystem; _fileSystem = fileSystem;
_jsonSerializer = jsonSerializer; _jsonSerializer = jsonSerializer;
_streamHelper = streamHelper; _streamHelper = streamHelper;
_logger = loggerfactory.CreateLogger("HttpResultFactory"); _logger = loggerfactory.CreateLogger<HttpResultFactory>();
} }
/// <summary> /// <summary>
@ -580,7 +580,6 @@ namespace Emby.Server.Implementations.HttpServer
} }
catch (NotSupportedException) catch (NotSupportedException)
{ {
} }
} }
@ -693,7 +692,7 @@ namespace Emby.Server.Implementations.HttpServer
/// <summary> /// <summary>
/// When the browser sends the IfModifiedDate, it's precision is limited to seconds, so this will account for that /// When the browser sends the IfModifiedDate, it's precision is limited to seconds, so this will account for that.
/// </summary> /// </summary>
/// <param name="date">The date.</param> /// <param name="date">The date.</param>
/// <returns>DateTime.</returns> /// <returns>DateTime.</returns>

@ -20,31 +20,37 @@ namespace Emby.Server.Implementations.HttpServer
/// </summary> /// </summary>
/// <value>The source stream.</value> /// <value>The source stream.</value>
private Stream SourceStream { get; set; } private Stream SourceStream { get; set; }
private string RangeHeader { get; set; } private string RangeHeader { get; set; }
private bool IsHeadRequest { get; set; } private bool IsHeadRequest { get; set; }
private long RangeStart { get; set; } private long RangeStart { get; set; }
private long RangeEnd { get; set; } private long RangeEnd { get; set; }
private long RangeLength { get; set; } private long RangeLength { get; set; }
private long TotalContentLength { get; set; } private long TotalContentLength { get; set; }
public Action OnComplete { get; set; } public Action OnComplete { get; set; }
private readonly ILogger _logger; private readonly ILogger _logger;
private const int BufferSize = 81920; private const int BufferSize = 81920;
/// <summary> /// <summary>
/// The _options /// The _options.
/// </summary> /// </summary>
private readonly Dictionary<string, string> _options = new Dictionary<string, string>(); private readonly Dictionary<string, string> _options = new Dictionary<string, string>();
/// <summary> /// <summary>
/// The us culture /// The us culture.
/// </summary> /// </summary>
private static readonly CultureInfo UsCulture = new CultureInfo("en-US"); private static readonly CultureInfo UsCulture = new CultureInfo("en-US");
/// <summary> /// <summary>
/// Additional HTTP Headers /// Additional HTTP Headers.
/// </summary> /// </summary>
/// <value>The headers.</value> /// <value>The headers.</value>
public IDictionary<string, string> Headers => _options; public IDictionary<string, string> Headers => _options;
@ -110,7 +116,7 @@ namespace Emby.Server.Implementations.HttpServer
} }
/// <summary> /// <summary>
/// The _requested ranges /// The _requested ranges.
/// </summary> /// </summary>
private List<KeyValuePair<long, long?>> _requestedRanges; private List<KeyValuePair<long, long?>> _requestedRanges;
/// <summary> /// <summary>
@ -139,6 +145,7 @@ namespace Emby.Server.Implementations.HttpServer
{ {
start = long.Parse(vals[0], UsCulture); start = long.Parse(vals[0], UsCulture);
} }
if (!string.IsNullOrEmpty(vals[1])) if (!string.IsNullOrEmpty(vals[1]))
{ {
end = long.Parse(vals[1], UsCulture); end = long.Parse(vals[1], UsCulture);

@ -1,5 +1,4 @@
using System; using System;
using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.Text; using System.Text;
using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Net;

@ -2,11 +2,12 @@
using System; using System;
using System.Linq; using System.Linq;
using System.Security.Authentication;
using Emby.Server.Implementations.SocketSharp; using Emby.Server.Implementations.SocketSharp;
using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums;
using MediaBrowser.Common.Net; using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Authentication;
using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Security; using MediaBrowser.Controller.Security;
using MediaBrowser.Controller.Session; using MediaBrowser.Controller.Session;
@ -45,7 +46,7 @@ namespace Emby.Server.Implementations.HttpServer.Security
public User Authenticate(HttpRequest request, IAuthenticationAttributes authAttributes) public User Authenticate(HttpRequest request, IAuthenticationAttributes authAttributes)
{ {
var req = new WebSocketSharpRequest(request, null, request.Path, _logger); var req = new WebSocketSharpRequest(request, null, request.Path);
var user = ValidateUser(req, authAttributes); var user = ValidateUser(req, authAttributes);
return user; return user;
} }
@ -90,7 +91,8 @@ namespace Emby.Server.Implementations.HttpServer.Security
!string.IsNullOrEmpty(auth.Client) && !string.IsNullOrEmpty(auth.Client) &&
!string.IsNullOrEmpty(auth.Device)) !string.IsNullOrEmpty(auth.Device))
{ {
_sessionManager.LogSessionActivity(auth.Client, _sessionManager.LogSessionActivity(
auth.Client,
auth.Version, auth.Version,
auth.DeviceId, auth.DeviceId,
auth.Device, auth.Device,
@ -104,21 +106,21 @@ namespace Emby.Server.Implementations.HttpServer.Security
private void ValidateUserAccess( private void ValidateUserAccess(
User user, User user,
IRequest request, IRequest request,
IAuthenticationAttributes authAttribtues, IAuthenticationAttributes authAttributes,
AuthorizationInfo auth) AuthorizationInfo auth)
{ {
if (user.Policy.IsDisabled) if (user.HasPermission(PermissionKind.IsDisabled))
{ {
throw new SecurityException("User account has been disabled."); throw new SecurityException("User account has been disabled.");
} }
if (!user.Policy.EnableRemoteAccess && !_networkManager.IsInLocalNetwork(request.RemoteIp)) if (!user.HasPermission(PermissionKind.EnableRemoteAccess) && !_networkManager.IsInLocalNetwork(request.RemoteIp))
{ {
throw new SecurityException("User account has been disabled."); throw new SecurityException("User account has been disabled.");
} }
if (!user.Policy.IsAdministrator if (!user.HasPermission(PermissionKind.IsAdministrator)
&& !authAttribtues.EscapeParentalControl && !authAttributes.EscapeParentalControl
&& !user.IsParentalScheduleAllowed()) && !user.IsParentalScheduleAllowed())
{ {
request.Response.Headers.Add("X-Application-Error-Code", "ParentalControl"); request.Response.Headers.Add("X-Application-Error-Code", "ParentalControl");
@ -138,6 +140,7 @@ namespace Emby.Server.Implementations.HttpServer.Security
{ {
return true; return true;
} }
if (authAttribtues.AllowLocalOnly && request.IsLocal) if (authAttribtues.AllowLocalOnly && request.IsLocal)
{ {
return true; return true;
@ -180,7 +183,7 @@ namespace Emby.Server.Implementations.HttpServer.Security
{ {
if (roles.Contains("admin", StringComparer.OrdinalIgnoreCase)) if (roles.Contains("admin", StringComparer.OrdinalIgnoreCase))
{ {
if (user == null || !user.Policy.IsAdministrator) if (user == null || !user.HasPermission(PermissionKind.IsAdministrator))
{ {
throw new SecurityException("User does not have admin access."); throw new SecurityException("User does not have admin access.");
} }
@ -188,7 +191,7 @@ namespace Emby.Server.Implementations.HttpServer.Security
if (roles.Contains("delete", StringComparer.OrdinalIgnoreCase)) if (roles.Contains("delete", StringComparer.OrdinalIgnoreCase))
{ {
if (user == null || !user.Policy.EnableContentDeletion) if (user == null || !user.HasPermission(PermissionKind.EnableContentDeletion))
{ {
throw new SecurityException("User does not have delete access."); throw new SecurityException("User does not have delete access.");
} }
@ -196,7 +199,7 @@ namespace Emby.Server.Implementations.HttpServer.Security
if (roles.Contains("download", StringComparer.OrdinalIgnoreCase)) if (roles.Contains("download", StringComparer.OrdinalIgnoreCase))
{ {
if (user == null || !user.Policy.EnableContentDownloading) if (user == null || !user.HasPermission(PermissionKind.EnableContentDownloading))
{ {
throw new SecurityException("User does not have download access."); throw new SecurityException("User does not have download access.");
} }

@ -71,6 +71,7 @@ namespace Emby.Server.Implementations.HttpServer.Security
{ {
token = httpReq.Headers["X-MediaBrowser-Token"]; token = httpReq.Headers["X-MediaBrowser-Token"];
} }
if (string.IsNullOrEmpty(token)) if (string.IsNullOrEmpty(token))
{ {
token = httpReq.QueryString["api_key"]; token = httpReq.QueryString["api_key"];
@ -116,7 +117,6 @@ namespace Emby.Server.Implementations.HttpServer.Security
{ {
info.Device = tokenInfo.DeviceName; info.Device = tokenInfo.DeviceName;
} }
else if (!string.Equals(info.Device, tokenInfo.DeviceName, StringComparison.OrdinalIgnoreCase)) else if (!string.Equals(info.Device, tokenInfo.DeviceName, StringComparison.OrdinalIgnoreCase))
{ {
if (allowTokenInfoUpdate) if (allowTokenInfoUpdate)
@ -149,9 +149,9 @@ namespace Emby.Server.Implementations.HttpServer.Security
{ {
info.User = _userManager.GetUserById(tokenInfo.UserId); info.User = _userManager.GetUserById(tokenInfo.UserId);
if (info.User != null && !string.Equals(info.User.Name, tokenInfo.UserName, StringComparison.OrdinalIgnoreCase)) if (info.User != null && !string.Equals(info.User.Username, tokenInfo.UserName, StringComparison.OrdinalIgnoreCase))
{ {
tokenInfo.UserName = info.User.Name; tokenInfo.UserName = info.User.Username;
updateToken = true; updateToken = true;
} }
} }
@ -161,6 +161,7 @@ namespace Emby.Server.Implementations.HttpServer.Security
_authRepo.Update(tokenInfo); _authRepo.Update(tokenInfo);
} }
} }
httpReq.Items["OriginalAuthenticationInfo"] = tokenInfo; httpReq.Items["OriginalAuthenticationInfo"] = tokenInfo;
} }

@ -1,7 +1,7 @@
#pragma warning disable CS1591 #pragma warning disable CS1591
using System; using System;
using MediaBrowser.Controller.Entities; using Jellyfin.Data.Entities;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Security; using MediaBrowser.Controller.Security;

@ -24,7 +24,7 @@ namespace Emby.Server.Implementations.HttpServer
/// <summary> /// <summary>
/// The logger. /// The logger.
/// </summary> /// </summary>
private readonly ILogger _logger; private readonly ILogger<WebSocketConnection> _logger;
/// <summary> /// <summary>
/// The json serializer options. /// The json serializer options.
@ -234,8 +234,10 @@ namespace Emby.Server.Implementations.HttpServer
private Task SendKeepAliveResponse() private Task SendKeepAliveResponse()
{ {
LastKeepAliveDate = DateTime.UtcNow; LastKeepAliveDate = DateTime.UtcNow;
return SendAsync(new WebSocketMessage<string> return SendAsync(
new WebSocketMessage<string>
{ {
MessageId = Guid.NewGuid(),
MessageType = "KeepAlive" MessageType = "KeepAlive"
}, CancellationToken.None); }, CancellationToken.None);
} }

@ -18,7 +18,7 @@ namespace Emby.Server.Implementations.IO
{ {
public class LibraryMonitor : ILibraryMonitor public class LibraryMonitor : ILibraryMonitor
{ {
private readonly ILogger _logger; private readonly ILogger<LibraryMonitor> _logger;
private readonly ILibraryManager _libraryManager; private readonly ILibraryManager _libraryManager;
private readonly IServerConfigurationManager _configurationManager; private readonly IServerConfigurationManager _configurationManager;
private readonly IFileSystem _fileSystem; private readonly IFileSystem _fileSystem;
@ -266,7 +266,6 @@ namespace Emby.Server.Implementations.IO
{ {
DisposeWatcher(newWatcher, false); DisposeWatcher(newWatcher, false);
} }
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -393,7 +392,6 @@ namespace Emby.Server.Implementations.IO
} }
return false; return false;
})) }))
{ {
monitorPath = false; monitorPath = false;

@ -20,7 +20,7 @@ namespace Emby.Server.Implementations.IO
/// </summary> /// </summary>
public class ManagedFileSystem : IFileSystem public class ManagedFileSystem : IFileSystem
{ {
protected ILogger Logger; protected ILogger<ManagedFileSystem> Logger;
private readonly List<IShortcutHandler> _shortcutHandlers = new List<IShortcutHandler>(); private readonly List<IShortcutHandler> _shortcutHandlers = new List<IShortcutHandler>();
private readonly string _tempPath; private readonly string _tempPath;
@ -628,6 +628,7 @@ namespace Emby.Server.Implementations.IO
{ {
return false; return false;
} }
return extensions.Contains(ext, StringComparer.OrdinalIgnoreCase); return extensions.Contains(ext, StringComparer.OrdinalIgnoreCase);
}); });
} }
@ -682,6 +683,7 @@ namespace Emby.Server.Implementations.IO
{ {
return false; return false;
} }
return extensions.Contains(ext, StringComparer.OrdinalIgnoreCase); return extensions.Contains(ext, StringComparer.OrdinalIgnoreCase);
}); });
} }

@ -71,7 +71,6 @@ namespace Emby.Server.Implementations.Images
new ValueTuple<string, SortOrder>(ItemSortBy.Random, SortOrder.Ascending) new ValueTuple<string, SortOrder>(ItemSortBy.Random, SortOrder.Ascending)
}, },
IncludeItemTypes = includeItemTypes IncludeItemTypes = includeItemTypes
}); });
} }

@ -78,7 +78,6 @@ namespace Emby.Server.Implementations.Images
} }
return i; return i;
}).GroupBy(x => x.Id) }).GroupBy(x => x.Id)
.Select(x => x.First()); .Select(x => x.First());

@ -8,7 +8,7 @@ using MediaBrowser.Model.IO;
namespace Emby.Server.Implementations.Library namespace Emby.Server.Implementations.Library
{ {
/// <summary> /// <summary>
/// Provides the core resolver ignore rules /// Provides the core resolver ignore rules.
/// </summary> /// </summary>
public class CoreResolutionIgnoreRule : IResolverIgnoreRule public class CoreResolutionIgnoreRule : IResolverIgnoreRule
{ {

@ -12,11 +12,13 @@ namespace Emby.Server.Implementations.Library
public class ExclusiveLiveStream : ILiveStream public class ExclusiveLiveStream : ILiveStream
{ {
public int ConsumerCount { get; set; } public int ConsumerCount { get; set; }
public string OriginalStreamId { get; set; } public string OriginalStreamId { get; set; }
public string TunerHostId => null; public string TunerHostId => null;
public bool EnableStreamSharing { get; set; } public bool EnableStreamSharing { get; set; }
public MediaSourceInfo MediaSource { get; set; } public MediaSourceInfo MediaSource { get; set; }
public string UniqueId { get; private set; } public string UniqueId { get; private set; }

@ -4,12 +4,12 @@ using DotNet.Globbing;
namespace Emby.Server.Implementations.Library namespace Emby.Server.Implementations.Library
{ {
/// <summary> /// <summary>
/// Glob patterns for files to ignore /// Glob patterns for files to ignore.
/// </summary> /// </summary>
public static class IgnorePatterns public static class IgnorePatterns
{ {
/// <summary> /// <summary>
/// Files matching these glob patterns will be ignored /// Files matching these glob patterns will be ignored.
/// </summary> /// </summary>
public static readonly string[] Patterns = new string[] public static readonly string[] Patterns = new string[]
{ {
@ -64,7 +64,7 @@ namespace Emby.Server.Implementations.Library
private static readonly Glob[] _globs = Patterns.Select(p => Glob.Parse(p, _globOptions)).ToArray(); private static readonly Glob[] _globs = Patterns.Select(p => Glob.Parse(p, _globOptions)).ToArray();
/// <summary> /// <summary>
/// Returns true if the supplied path should be ignored /// Returns true if the supplied path should be ignored.
/// </summary> /// </summary>
public static bool ShouldIgnore(string path) public static bool ShouldIgnore(string path)
{ {

@ -17,6 +17,8 @@ using Emby.Server.Implementations.Library.Resolvers;
using Emby.Server.Implementations.Library.Validators; using Emby.Server.Implementations.Library.Validators;
using Emby.Server.Implementations.Playlists; using Emby.Server.Implementations.Playlists;
using Emby.Server.Implementations.ScheduledTasks; using Emby.Server.Implementations.ScheduledTasks;
using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums;
using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Progress; using MediaBrowser.Common.Progress;
using MediaBrowser.Controller; using MediaBrowser.Controller;
@ -25,7 +27,6 @@ using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.IO; using MediaBrowser.Controller.IO;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.LiveTv;
@ -46,6 +47,9 @@ using MediaBrowser.Model.Querying;
using MediaBrowser.Model.Tasks; using MediaBrowser.Model.Tasks;
using MediaBrowser.Providers.MediaInfo; using MediaBrowser.Providers.MediaInfo;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Episode = MediaBrowser.Controller.Entities.TV.Episode;
using Genre = MediaBrowser.Controller.Entities.Genre;
using Person = MediaBrowser.Controller.Entities.Person;
using SortOrder = MediaBrowser.Model.Entities.SortOrder; using SortOrder = MediaBrowser.Model.Entities.SortOrder;
using VideoResolver = Emby.Naming.Video.VideoResolver; using VideoResolver = Emby.Naming.Video.VideoResolver;
@ -56,7 +60,7 @@ namespace Emby.Server.Implementations.Library
/// </summary> /// </summary>
public class LibraryManager : ILibraryManager public class LibraryManager : ILibraryManager
{ {
private readonly ILogger _logger; private readonly ILogger<LibraryManager> _logger;
private readonly ITaskManager _taskManager; private readonly ITaskManager _taskManager;
private readonly IUserManager _userManager; private readonly IUserManager _userManager;
private readonly IUserDataManager _userDataRepository; private readonly IUserDataManager _userDataRepository;
@ -93,13 +97,13 @@ namespace Emby.Server.Implementations.Library
private IIntroProvider[] IntroProviders { get; set; } private IIntroProvider[] IntroProviders { get; set; }
/// <summary> /// <summary>
/// Gets or sets the list of entity resolution ignore rules /// Gets or sets the list of entity resolution ignore rules.
/// </summary> /// </summary>
/// <value>The entity resolution ignore rules.</value> /// <value>The entity resolution ignore rules.</value>
private IResolverIgnoreRule[] EntityResolutionIgnoreRules { get; set; } private IResolverIgnoreRule[] EntityResolutionIgnoreRules { get; set; }
/// <summary> /// <summary>
/// Gets or sets the list of currently registered entity resolvers /// Gets or sets the list of currently registered entity resolvers.
/// </summary> /// </summary>
/// <value>The entity resolvers enumerable.</value> /// <value>The entity resolvers enumerable.</value>
private IItemResolver[] EntityResolvers { get; set; } private IItemResolver[] EntityResolvers { get; set; }
@ -205,12 +209,12 @@ namespace Emby.Server.Implementations.Library
} }
/// <summary> /// <summary>
/// The _root folder /// The _root folder.
/// </summary> /// </summary>
private volatile AggregateFolder _rootFolder; private volatile AggregateFolder _rootFolder;
/// <summary> /// <summary>
/// The _root folder sync lock /// The _root folder sync lock.
/// </summary> /// </summary>
private readonly object _rootFolderSyncLock = new object(); private readonly object _rootFolderSyncLock = new object();
@ -623,7 +627,7 @@ namespace Emby.Server.Implementations.Library
} }
/// <summary> /// <summary>
/// Determines whether a path should be ignored based on its contents - called after the contents have been read /// Determines whether a path should be ignored based on its contents - called after the contents have been read.
/// </summary> /// </summary>
/// <param name="args">The args.</param> /// <param name="args">The args.</param>
/// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns> /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns>
@ -905,7 +909,7 @@ namespace Emby.Server.Implementations.Library
} }
/// <summary> /// <summary>
/// Gets a Genre /// Gets a Genre.
/// </summary> /// </summary>
/// <param name="name">The name.</param> /// <param name="name">The name.</param>
/// <returns>Task{Genre}.</returns> /// <returns>Task{Genre}.</returns>
@ -986,7 +990,7 @@ namespace Emby.Server.Implementations.Library
} }
/// <summary> /// <summary>
/// Reloads the root media folder /// Reloads the root media folder.
/// </summary> /// </summary>
/// <param name="progress">The progress.</param> /// <param name="progress">The progress.</param>
/// <param name="cancellationToken">The cancellation token.</param> /// <param name="cancellationToken">The cancellation token.</param>
@ -1539,7 +1543,8 @@ namespace Emby.Server.Implementations.Library
} }
// Handle grouping // Handle grouping
if (user != null && !string.IsNullOrEmpty(view.ViewType) && UserView.IsEligibleForGrouping(view.ViewType) && user.Configuration.GroupedFolders.Length > 0) if (user != null && !string.IsNullOrEmpty(view.ViewType) && UserView.IsEligibleForGrouping(view.ViewType)
&& user.GetPreference(PreferenceKind.GroupedFolders).Length > 0)
{ {
return GetUserRootFolder() return GetUserRootFolder()
.GetChildren(user, true) .GetChildren(user, true)
@ -2779,10 +2784,12 @@ namespace Emby.Server.Implementations.Library
{ {
throw new ArgumentNullException(nameof(path)); throw new ArgumentNullException(nameof(path));
} }
if (string.IsNullOrWhiteSpace(from)) if (string.IsNullOrWhiteSpace(from))
{ {
throw new ArgumentNullException(nameof(from)); throw new ArgumentNullException(nameof(from));
} }
if (string.IsNullOrWhiteSpace(to)) if (string.IsNullOrWhiteSpace(to))
{ {
throw new ArgumentNullException(nameof(to)); throw new ArgumentNullException(nameof(to));
@ -2856,7 +2863,6 @@ namespace Emby.Server.Implementations.Library
_logger.LogError(ex, "Error getting person"); _logger.LogError(ex, "Error getting person");
return null; return null;
} }
}).Where(i => i != null).ToList(); }).Where(i => i != null).ToList();
} }

@ -148,17 +148,14 @@ namespace Emby.Server.Implementations.Library
{ {
videoStream.BitRate = 30000000; videoStream.BitRate = 30000000;
} }
else if (width >= 1900) else if (width >= 1900)
{ {
videoStream.BitRate = 20000000; videoStream.BitRate = 20000000;
} }
else if (width >= 1200) else if (width >= 1200)
{ {
videoStream.BitRate = 8000000; videoStream.BitRate = 8000000;
} }
else if (width >= 700) else if (width >= 700)
{ {
videoStream.BitRate = 2000000; videoStream.BitRate = 2000000;

@ -7,6 +7,8 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums;
using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
@ -14,7 +16,6 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Controller.Persistence; using MediaBrowser.Controller.Persistence;
using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Dto; using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
@ -33,7 +34,7 @@ namespace Emby.Server.Implementations.Library
private readonly ILibraryManager _libraryManager; private readonly ILibraryManager _libraryManager;
private readonly IJsonSerializer _jsonSerializer; private readonly IJsonSerializer _jsonSerializer;
private readonly IFileSystem _fileSystem; private readonly IFileSystem _fileSystem;
private readonly ILogger _logger; private readonly ILogger<MediaSourceManager> _logger;
private readonly IUserDataManager _userDataManager; private readonly IUserDataManager _userDataManager;
private readonly IMediaEncoder _mediaEncoder; private readonly IMediaEncoder _mediaEncoder;
private readonly ILocalizationManager _localizationManager; private readonly ILocalizationManager _localizationManager;
@ -190,10 +191,7 @@ namespace Emby.Server.Implementations.Library
{ {
if (string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase)) if (string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase))
{ {
if (!user.Policy.EnableAudioPlaybackTranscoding) source.SupportsTranscoding = user.HasPermission(PermissionKind.EnableAudioPlaybackTranscoding);
{
source.SupportsTranscoding = false;
}
} }
} }
} }
@ -207,22 +205,27 @@ namespace Emby.Server.Implementations.Library
{ {
return MediaProtocol.Rtsp; return MediaProtocol.Rtsp;
} }
if (path.StartsWith("Rtmp", StringComparison.OrdinalIgnoreCase)) if (path.StartsWith("Rtmp", StringComparison.OrdinalIgnoreCase))
{ {
return MediaProtocol.Rtmp; return MediaProtocol.Rtmp;
} }
if (path.StartsWith("Http", StringComparison.OrdinalIgnoreCase)) if (path.StartsWith("Http", StringComparison.OrdinalIgnoreCase))
{ {
return MediaProtocol.Http; return MediaProtocol.Http;
} }
if (path.StartsWith("rtp", StringComparison.OrdinalIgnoreCase)) if (path.StartsWith("rtp", StringComparison.OrdinalIgnoreCase))
{ {
return MediaProtocol.Rtp; return MediaProtocol.Rtp;
} }
if (path.StartsWith("ftp", StringComparison.OrdinalIgnoreCase)) if (path.StartsWith("ftp", StringComparison.OrdinalIgnoreCase))
{ {
return MediaProtocol.Ftp; return MediaProtocol.Ftp;
} }
if (path.StartsWith("udp", StringComparison.OrdinalIgnoreCase)) if (path.StartsWith("udp", StringComparison.OrdinalIgnoreCase))
{ {
return MediaProtocol.Udp; return MediaProtocol.Udp;
@ -352,7 +355,9 @@ namespace Emby.Server.Implementations.Library
private void SetDefaultSubtitleStreamIndex(MediaSourceInfo source, UserItemData userData, User user, bool allowRememberingSelection) private void SetDefaultSubtitleStreamIndex(MediaSourceInfo source, UserItemData userData, User user, bool allowRememberingSelection)
{ {
if (userData.SubtitleStreamIndex.HasValue && user.Configuration.RememberSubtitleSelections && user.Configuration.SubtitleMode != SubtitlePlaybackMode.None && allowRememberingSelection) if (userData.SubtitleStreamIndex.HasValue
&& user.RememberSubtitleSelections
&& user.SubtitleMode != SubtitlePlaybackMode.None && allowRememberingSelection)
{ {
var index = userData.SubtitleStreamIndex.Value; var index = userData.SubtitleStreamIndex.Value;
// Make sure the saved index is still valid // Make sure the saved index is still valid
@ -363,26 +368,27 @@ namespace Emby.Server.Implementations.Library
} }
} }
var preferredSubs = string.IsNullOrEmpty(user.Configuration.SubtitleLanguagePreference)
? Array.Empty<string>() : NormalizeLanguage(user.Configuration.SubtitleLanguagePreference); var preferredSubs = string.IsNullOrEmpty(user.SubtitleLanguagePreference)
? Array.Empty<string>() : NormalizeLanguage(user.SubtitleLanguagePreference);
var defaultAudioIndex = source.DefaultAudioStreamIndex; var defaultAudioIndex = source.DefaultAudioStreamIndex;
var audioLangage = defaultAudioIndex == null var audioLangage = defaultAudioIndex == null
? null ? null
: source.MediaStreams.Where(i => i.Type == MediaStreamType.Audio && i.Index == defaultAudioIndex).Select(i => i.Language).FirstOrDefault(); : source.MediaStreams.Where(i => i.Type == MediaStreamType.Audio && i.Index == defaultAudioIndex).Select(i => i.Language).FirstOrDefault();
source.DefaultSubtitleStreamIndex = MediaStreamSelector.GetDefaultSubtitleStreamIndex(source.MediaStreams, source.DefaultSubtitleStreamIndex = MediaStreamSelector.GetDefaultSubtitleStreamIndex(
source.MediaStreams,
preferredSubs, preferredSubs,
user.Configuration.SubtitleMode, user.SubtitleMode,
audioLangage); audioLangage);
MediaStreamSelector.SetSubtitleStreamScores(source.MediaStreams, preferredSubs, MediaStreamSelector.SetSubtitleStreamScores(source.MediaStreams, preferredSubs, user.SubtitleMode, audioLangage);
user.Configuration.SubtitleMode, audioLangage);
} }
private void SetDefaultAudioStreamIndex(MediaSourceInfo source, UserItemData userData, User user, bool allowRememberingSelection) private void SetDefaultAudioStreamIndex(MediaSourceInfo source, UserItemData userData, User user, bool allowRememberingSelection)
{ {
if (userData.AudioStreamIndex.HasValue && user.Configuration.RememberAudioSelections && allowRememberingSelection) if (userData.AudioStreamIndex.HasValue && user.RememberAudioSelections && allowRememberingSelection)
{ {
var index = userData.AudioStreamIndex.Value; var index = userData.AudioStreamIndex.Value;
// Make sure the saved index is still valid // Make sure the saved index is still valid
@ -393,11 +399,11 @@ namespace Emby.Server.Implementations.Library
} }
} }
var preferredAudio = string.IsNullOrEmpty(user.Configuration.AudioLanguagePreference) var preferredAudio = string.IsNullOrEmpty(user.AudioLanguagePreference)
? Array.Empty<string>() ? Array.Empty<string>()
: NormalizeLanguage(user.Configuration.AudioLanguagePreference); : NormalizeLanguage(user.AudioLanguagePreference);
source.DefaultAudioStreamIndex = MediaStreamSelector.GetDefaultAudioStreamIndex(source.MediaStreams, preferredAudio, user.Configuration.PlayDefaultAudioTrack); source.DefaultAudioStreamIndex = MediaStreamSelector.GetDefaultAudioStreamIndex(source.MediaStreams, preferredAudio, user.PlayDefaultAudioTrack);
} }
public void SetDefaultAudioAndSubtitleStreamIndexes(BaseItem item, MediaSourceInfo source, User user) public void SetDefaultAudioAndSubtitleStreamIndexes(BaseItem item, MediaSourceInfo source, User user)
@ -435,7 +441,6 @@ namespace Emby.Server.Implementations.Library
} }
return 1; return 1;
}).ThenBy(i => i.Video3DFormat.HasValue ? 1 : 0) }).ThenBy(i => i.Video3DFormat.HasValue ? 1 : 0)
.ThenByDescending(i => .ThenByDescending(i =>
{ {
@ -534,7 +539,7 @@ namespace Emby.Server.Implementations.Library
mediaSource.RunTimeTicks = null; mediaSource.RunTimeTicks = null;
} }
var audioStream = mediaSource.MediaStreams.FirstOrDefault(i => i.Type == MediaBrowser.Model.Entities.MediaStreamType.Audio); var audioStream = mediaSource.MediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Audio);
if (audioStream == null || audioStream.Index == -1) if (audioStream == null || audioStream.Index == -1)
{ {
@ -545,7 +550,7 @@ namespace Emby.Server.Implementations.Library
mediaSource.DefaultAudioStreamIndex = audioStream.Index; mediaSource.DefaultAudioStreamIndex = audioStream.Index;
} }
var videoStream = mediaSource.MediaStreams.FirstOrDefault(i => i.Type == MediaBrowser.Model.Entities.MediaStreamType.Video); var videoStream = mediaSource.MediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Video);
if (videoStream != null) if (videoStream != null)
{ {
if (!videoStream.BitRate.HasValue) if (!videoStream.BitRate.HasValue)
@ -556,17 +561,14 @@ namespace Emby.Server.Implementations.Library
{ {
videoStream.BitRate = 30000000; videoStream.BitRate = 30000000;
} }
else if (width >= 1900) else if (width >= 1900)
{ {
videoStream.BitRate = 20000000; videoStream.BitRate = 20000000;
} }
else if (width >= 1200) else if (width >= 1200)
{ {
videoStream.BitRate = 8000000; videoStream.BitRate = 8000000;
} }
else if (width >= 700) else if (width >= 700)
{ {
videoStream.BitRate = 2000000; videoStream.BitRate = 2000000;
@ -622,7 +624,6 @@ namespace Emby.Server.Implementations.Library
MediaSource = mediaSource, MediaSource = mediaSource,
ExtractChapters = false, ExtractChapters = false,
MediaType = DlnaProfileType.Video MediaType = DlnaProfileType.Video
}, cancellationToken).ConfigureAwait(false); }, cancellationToken).ConfigureAwait(false);
mediaSource.MediaStreams = info.MediaStreams; mediaSource.MediaStreams = info.MediaStreams;
@ -670,13 +671,14 @@ namespace Emby.Server.Implementations.Library
mediaSource.AnalyzeDurationMs = 3000; mediaSource.AnalyzeDurationMs = 3000;
} }
mediaInfo = await _mediaEncoder.GetMediaInfo(new MediaInfoRequest mediaInfo = await _mediaEncoder.GetMediaInfo(
new MediaInfoRequest
{ {
MediaSource = mediaSource, MediaSource = mediaSource,
MediaType = isAudio ? DlnaProfileType.Audio : DlnaProfileType.Video, MediaType = isAudio ? DlnaProfileType.Audio : DlnaProfileType.Video,
ExtractChapters = false ExtractChapters = false
},
}, cancellationToken).ConfigureAwait(false); cancellationToken).ConfigureAwait(false);
if (cacheFilePath != null) if (cacheFilePath != null)
{ {
@ -749,17 +751,14 @@ namespace Emby.Server.Implementations.Library
{ {
videoStream.BitRate = 30000000; videoStream.BitRate = 30000000;
} }
else if (width >= 1900) else if (width >= 1900)
{ {
videoStream.BitRate = 20000000; videoStream.BitRate = 20000000;
} }
else if (width >= 1200) else if (width >= 1200)
{ {
videoStream.BitRate = 8000000; videoStream.BitRate = 8000000;
} }
else if (width >= 700) else if (width >= 700)
{ {
videoStream.BitRate = 2000000; videoStream.BitRate = 2000000;

@ -3,7 +3,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using MediaBrowser.Model.Configuration; using Jellyfin.Data.Enums;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
namespace Emby.Server.Implementations.Library namespace Emby.Server.Implementations.Library

@ -3,6 +3,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Jellyfin.Data.Entities;
using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Entities.Audio;
@ -10,6 +11,7 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Playlists; using MediaBrowser.Controller.Playlists;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Querying; using MediaBrowser.Model.Querying;
using MusicAlbum = MediaBrowser.Controller.Entities.Audio.MusicAlbum;
namespace Emby.Server.Implementations.Library namespace Emby.Server.Implementations.Library
{ {
@ -75,7 +77,6 @@ namespace Emby.Server.Implementations.Library
{ {
return Guid.Empty; return Guid.Empty;
} }
}).Where(i => !i.Equals(Guid.Empty)).ToArray(); }).Where(i => !i.Equals(Guid.Empty)).ToArray();
return GetInstantMixFromGenreIds(genreIds, user, dtoOptions); return GetInstantMixFromGenreIds(genreIds, user, dtoOptions);
@ -105,32 +106,27 @@ namespace Emby.Server.Implementations.Library
return GetInstantMixFromGenreIds(new[] { item.Id }, user, dtoOptions); return GetInstantMixFromGenreIds(new[] { item.Id }, user, dtoOptions);
} }
var playlist = item as Playlist; if (item is Playlist playlist)
if (playlist != null)
{ {
return GetInstantMixFromPlaylist(playlist, user, dtoOptions); return GetInstantMixFromPlaylist(playlist, user, dtoOptions);
} }
var album = item as MusicAlbum; if (item is MusicAlbum album)
if (album != null)
{ {
return GetInstantMixFromAlbum(album, user, dtoOptions); return GetInstantMixFromAlbum(album, user, dtoOptions);
} }
var artist = item as MusicArtist; if (item is MusicArtist artist)
if (artist != null)
{ {
return GetInstantMixFromArtist(artist, user, dtoOptions); return GetInstantMixFromArtist(artist, user, dtoOptions);
} }
var song = item as Audio; if (item is Audio song)
if (song != null)
{ {
return GetInstantMixFromSong(song, user, dtoOptions); return GetInstantMixFromSong(song, user, dtoOptions);
} }
var folder = item as Folder; if (item is Folder folder)
if (folder != null)
{ {
return GetInstantMixFromFolder(folder, user, dtoOptions); return GetInstantMixFromFolder(folder, user, dtoOptions);
} }

@ -107,7 +107,7 @@ namespace Emby.Server.Implementations.Library
} }
/// <summary> /// <summary>
/// Ensures DateCreated and DateModified have values /// Ensures DateCreated and DateModified have values.
/// </summary> /// </summary>
/// <param name="fileSystem">The file system.</param> /// <param name="fileSystem">The file system.</param>
/// <param name="item">The item.</param> /// <param name="item">The item.</param>

@ -109,7 +109,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
IEnumerable<FileSystemMetadata> list, IEnumerable<FileSystemMetadata> list,
bool allowSubfolders, bool allowSubfolders,
IDirectoryService directoryService, IDirectoryService directoryService,
ILogger logger, ILogger<MusicAlbumResolver> logger,
IFileSystem fileSystem, IFileSystem fileSystem,
ILibraryManager libraryManager) ILibraryManager libraryManager)
{ {

@ -28,7 +28,7 @@ namespace Emby.Server.Implementations.Library.Resolvers
public virtual ResolverPriority Priority => ResolverPriority.First; public virtual ResolverPriority Priority => ResolverPriority.First;
/// <summary> /// <summary>
/// Sets initial values on the newly resolved item /// Sets initial values on the newly resolved item.
/// </summary> /// </summary>
/// <param name="item">The item.</param> /// <param name="item">The item.</param>
/// <param name="args">The args.</param> /// <param name="args">The args.</param>

@ -69,7 +69,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
if (!string.IsNullOrEmpty(id)) if (!string.IsNullOrEmpty(id))
{ {
item.SetProviderId(MetadataProviders.Tmdb, id); item.SetProviderId(MetadataProvider.Tmdb, id);
} }
} }
} }

@ -350,7 +350,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
if (!string.IsNullOrWhiteSpace(tmdbid)) if (!string.IsNullOrWhiteSpace(tmdbid))
{ {
item.SetProviderId(MetadataProviders.Tmdb, tmdbid); item.SetProviderId(MetadataProvider.Tmdb, tmdbid);
} }
} }
@ -361,7 +361,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
if (!string.IsNullOrWhiteSpace(imdbid)) if (!string.IsNullOrWhiteSpace(imdbid))
{ {
item.SetProviderId(MetadataProviders.Imdb, imdbid); item.SetProviderId(MetadataProvider.Imdb, imdbid);
} }
} }
} }

@ -41,10 +41,12 @@ namespace Emby.Server.Implementations.Library.Resolvers
{ {
return new AggregateFolder(); return new AggregateFolder();
} }
if (string.Equals(args.Path, _appPaths.DefaultUserViewsPath, StringComparison.OrdinalIgnoreCase)) if (string.Equals(args.Path, _appPaths.DefaultUserViewsPath, StringComparison.OrdinalIgnoreCase))
{ {
return new UserRootFolder(); // if we got here and still a root - must be user root return new UserRootFolder(); // if we got here and still a root - must be user root
} }
if (args.IsVf) if (args.IsVf)
{ {
return new CollectionFolder return new CollectionFolder
@ -73,7 +75,6 @@ namespace Emby.Server.Implementations.Library.Resolvers
{ {
return false; return false;
} }
}) })
.Select(i => _fileSystem.GetFileNameWithoutExtension(i)) .Select(i => _fileSystem.GetFileNameWithoutExtension(i))
.FirstOrDefault(); .FirstOrDefault();

@ -55,6 +55,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
episode.SeriesId = series.Id; episode.SeriesId = series.Id;
episode.SeriesName = series.Name; episode.SeriesName = series.Name;
} }
if (season != null) if (season != null)
{ {
episode.SeasonId = season.Id; episode.SeasonId = season.Id;

@ -16,7 +16,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
private readonly IServerConfigurationManager _config; private readonly IServerConfigurationManager _config;
private readonly ILibraryManager _libraryManager; private readonly ILibraryManager _libraryManager;
private readonly ILocalizationManager _localization; private readonly ILocalizationManager _localization;
private readonly ILogger _logger; private readonly ILogger<SeasonResolver> _logger;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="SeasonResolver"/> class. /// Initializes a new instance of the <see cref="SeasonResolver"/> class.
@ -94,7 +94,6 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
_localization.GetLocalizedString("NameSeasonNumber"), _localization.GetLocalizedString("NameSeasonNumber"),
seasonNumber, seasonNumber,
args.GetLibraryOptions().PreferredMetadataLanguage); args.GetLibraryOptions().PreferredMetadataLanguage);
} }
return season; return season;

@ -20,7 +20,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
public class SeriesResolver : FolderResolver<Series> public class SeriesResolver : FolderResolver<Series>
{ {
private readonly IFileSystem _fileSystem; private readonly IFileSystem _fileSystem;
private readonly ILogger _logger; private readonly ILogger<SeriesResolver> _logger;
private readonly ILibraryManager _libraryManager; private readonly ILibraryManager _libraryManager;
/// <summary> /// <summary>
@ -119,7 +119,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
IEnumerable<FileSystemMetadata> fileSystemChildren, IEnumerable<FileSystemMetadata> fileSystemChildren,
IDirectoryService directoryService, IDirectoryService directoryService,
IFileSystem fileSystem, IFileSystem fileSystem,
ILogger logger, ILogger<SeriesResolver> logger,
ILibraryManager libraryManager, ILibraryManager libraryManager,
bool isTvContentType) bool isTvContentType)
{ {
@ -217,7 +217,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
if (!string.IsNullOrEmpty(id)) if (!string.IsNullOrEmpty(id))
{ {
item.SetProviderId(MetadataProviders.Tvdb, id); item.SetProviderId(MetadataProvider.Tvdb, id);
} }
} }
} }

@ -3,6 +3,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Jellyfin.Data.Entities;
using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Entities.Audio;
@ -12,12 +13,14 @@ using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Querying; using MediaBrowser.Model.Querying;
using MediaBrowser.Model.Search; using MediaBrowser.Model.Search;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Genre = MediaBrowser.Controller.Entities.Genre;
using Person = MediaBrowser.Controller.Entities.Person;
namespace Emby.Server.Implementations.Library namespace Emby.Server.Implementations.Library
{ {
public class SearchEngine : ISearchEngine public class SearchEngine : ISearchEngine
{ {
private readonly ILogger _logger; private readonly ILogger<SearchEngine> _logger;
private readonly ILibraryManager _libraryManager; private readonly ILibraryManager _libraryManager;
private readonly IUserManager _userManager; private readonly IUserManager _userManager;
@ -191,6 +194,7 @@ namespace Emby.Server.Implementations.Library
{ {
searchQuery.AncestorIds = new[] { searchQuery.ParentId }; searchQuery.AncestorIds = new[] { searchQuery.ParentId };
} }
searchQuery.ParentId = Guid.Empty; searchQuery.ParentId = Guid.Empty;
searchQuery.IncludeItemsByName = true; searchQuery.IncludeItemsByName = true;
searchQuery.IncludeItemTypes = Array.Empty<string>(); searchQuery.IncludeItemTypes = Array.Empty<string>();
@ -204,7 +208,6 @@ namespace Emby.Server.Implementations.Library
return mediaItems.Select(i => new SearchHintInfo return mediaItems.Select(i => new SearchHintInfo
{ {
Item = i Item = i
}).ToList(); }).ToList();
} }
} }

@ -5,6 +5,7 @@ using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.Threading; using System.Threading;
using Jellyfin.Data.Entities;
using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
@ -13,6 +14,7 @@ using MediaBrowser.Controller.Persistence;
using MediaBrowser.Model.Dto; using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Book = MediaBrowser.Controller.Entities.Book;
namespace Emby.Server.Implementations.Library namespace Emby.Server.Implementations.Library
{ {
@ -26,7 +28,7 @@ namespace Emby.Server.Implementations.Library
private readonly ConcurrentDictionary<string, UserItemData> _userData = private readonly ConcurrentDictionary<string, UserItemData> _userData =
new ConcurrentDictionary<string, UserItemData>(StringComparer.OrdinalIgnoreCase); new ConcurrentDictionary<string, UserItemData>(StringComparer.OrdinalIgnoreCase);
private readonly ILogger _logger; private readonly ILogger<UserDataManager> _logger;
private readonly IServerConfigurationManager _config; private readonly IServerConfigurationManager _config;
private readonly IUserManager _userManager; private readonly IUserManager _userManager;
private readonly IUserDataRepository _repository; private readonly IUserDataRepository _repository;
@ -101,7 +103,7 @@ namespace Emby.Server.Implementations.Library
} }
/// <summary> /// <summary>
/// Retrieve all user data for the given user /// Retrieve all user data for the given user.
/// </summary> /// </summary>
/// <param name="userId"></param> /// <param name="userId"></param>
/// <returns></returns> /// <returns></returns>
@ -186,7 +188,7 @@ namespace Emby.Server.Implementations.Library
} }
/// <summary> /// <summary>
/// Converts a UserItemData to a DTOUserItemData /// Converts a UserItemData to a DTOUserItemData.
/// </summary> /// </summary>
/// <param name="data">The data.</param> /// <param name="data">The data.</param>
/// <returns>DtoUserItemData.</returns> /// <returns>DtoUserItemData.</returns>

File diff suppressed because it is too large Load Diff

@ -5,6 +5,8 @@ using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Dto;
@ -17,6 +19,8 @@ using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Globalization; using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.Library; using MediaBrowser.Model.Library;
using MediaBrowser.Model.Querying; using MediaBrowser.Model.Querying;
using Genre = MediaBrowser.Controller.Entities.Genre;
using Person = MediaBrowser.Controller.Entities.Person;
namespace Emby.Server.Implementations.Library namespace Emby.Server.Implementations.Library
{ {
@ -125,12 +129,12 @@ namespace Emby.Server.Implementations.Library
if (!query.IncludeHidden) if (!query.IncludeHidden)
{ {
list = list.Where(i => !user.Configuration.MyMediaExcludes.Contains(i.Id.ToString("N", CultureInfo.InvariantCulture))).ToList(); list = list.Where(i => !user.GetPreference(PreferenceKind.MyMediaExcludes).Contains(i.Id.ToString("N", CultureInfo.InvariantCulture))).ToList();
} }
var sorted = _libraryManager.Sort(list, user, new[] { ItemSortBy.SortName }, SortOrder.Ascending).ToList(); var sorted = _libraryManager.Sort(list, user, new[] { ItemSortBy.SortName }, SortOrder.Ascending).ToList();
var orders = user.Configuration.OrderedViews.ToList(); var orders = user.GetPreference(PreferenceKind.OrderedViews).ToList();
return list return list
.OrderBy(i => .OrderBy(i =>
@ -165,7 +169,13 @@ namespace Emby.Server.Implementations.Library
return GetUserSubViewWithName(name, parentId, type, sortName); return GetUserSubViewWithName(name, parentId, type, sortName);
} }
private Folder GetUserView(List<ICollectionFolder> parents, string viewType, string localizationKey, string sortName, User user, string[] presetViews) private Folder GetUserView(
List<ICollectionFolder> parents,
string viewType,
string localizationKey,
string sortName,
Jellyfin.Data.Entities.User user,
string[] presetViews)
{ {
if (parents.Count == 1 && parents.All(i => string.Equals(i.CollectionType, viewType, StringComparison.OrdinalIgnoreCase))) if (parents.Count == 1 && parents.All(i => string.Equals(i.CollectionType, viewType, StringComparison.OrdinalIgnoreCase)))
{ {
@ -270,7 +280,8 @@ namespace Emby.Server.Implementations.Library
{ {
parents = _libraryManager.GetUserRootFolder().GetChildren(user, true) parents = _libraryManager.GetUserRootFolder().GetChildren(user, true)
.Where(i => i is Folder) .Where(i => i is Folder)
.Where(i => !user.Configuration.LatestItemsExcludes.Contains(i.Id.ToString("N", CultureInfo.InvariantCulture))) .Where(i => !user.GetPreference(PreferenceKind.LatestItemExcludes)
.Contains(i.Id.ToString("N", CultureInfo.InvariantCulture)))
.ToList(); .ToList();
} }
@ -331,12 +342,11 @@ namespace Emby.Server.Implementations.Library
var excludeItemTypes = includeItemTypes.Length == 0 && mediaTypes.Count == 0 ? new[] var excludeItemTypes = includeItemTypes.Length == 0 && mediaTypes.Count == 0 ? new[]
{ {
typeof(Person).Name, nameof(Person),
typeof(Studio).Name, nameof(Studio),
typeof(Year).Name, nameof(Year),
typeof(MusicGenre).Name, nameof(MusicGenre),
typeof(Genre).Name nameof(Genre)
} : Array.Empty<string>(); } : Array.Empty<string>();
var query = new InternalItemsQuery(user) var query = new InternalItemsQuery(user)

@ -16,7 +16,7 @@ namespace Emby.Server.Implementations.Library.Validators
/// The _library manager. /// The _library manager.
/// </summary> /// </summary>
private readonly ILibraryManager _libraryManager; private readonly ILibraryManager _libraryManager;
private readonly ILogger _logger; private readonly ILogger<ArtistsValidator> _logger;
private readonly IItemRepository _itemRepo; private readonly IItemRepository _itemRepo;
/// <summary> /// <summary>
@ -27,7 +27,7 @@ namespace Emby.Server.Implementations.Library.Validators
/// <param name="itemRepo">The item repository.</param> /// <param name="itemRepo">The item repository.</param>
public ArtistsPostScanTask( public ArtistsPostScanTask(
ILibraryManager libraryManager, ILibraryManager libraryManager,
ILogger<ArtistsPostScanTask> logger, ILogger<ArtistsValidator> logger,
IItemRepository itemRepo) IItemRepository itemRepo)
{ {
_libraryManager = libraryManager; _libraryManager = libraryManager;

@ -24,7 +24,7 @@ namespace Emby.Server.Implementations.Library.Validators
/// <summary> /// <summary>
/// The logger. /// The logger.
/// </summary> /// </summary>
private readonly ILogger _logger; private readonly ILogger<ArtistsValidator> _logger;
private readonly IItemRepository _itemRepo; private readonly IItemRepository _itemRepo;
/// <summary> /// <summary>
@ -33,7 +33,7 @@ namespace Emby.Server.Implementations.Library.Validators
/// <param name="libraryManager">The library manager.</param> /// <param name="libraryManager">The library manager.</param>
/// <param name="logger">The logger.</param> /// <param name="logger">The logger.</param>
/// <param name="itemRepo">The item repository.</param> /// <param name="itemRepo">The item repository.</param>
public ArtistsValidator(ILibraryManager libraryManager, ILogger logger, IItemRepository itemRepo) public ArtistsValidator(ILibraryManager libraryManager, ILogger<ArtistsValidator> logger, IItemRepository itemRepo)
{ {
_libraryManager = libraryManager; _libraryManager = libraryManager;
_logger = logger; _logger = logger;
@ -98,7 +98,6 @@ namespace Emby.Server.Implementations.Library.Validators
_libraryManager.DeleteItem(item, new DeleteOptions _libraryManager.DeleteItem(item, new DeleteOptions
{ {
DeleteFileLocation = false DeleteFileLocation = false
}, false); }, false);
} }

@ -16,7 +16,7 @@ namespace Emby.Server.Implementations.Library.Validators
/// The _library manager. /// The _library manager.
/// </summary> /// </summary>
private readonly ILibraryManager _libraryManager; private readonly ILibraryManager _libraryManager;
private readonly ILogger _logger; private readonly ILogger<GenresValidator> _logger;
private readonly IItemRepository _itemRepo; private readonly IItemRepository _itemRepo;
/// <summary> /// <summary>
@ -27,7 +27,7 @@ namespace Emby.Server.Implementations.Library.Validators
/// <param name="itemRepo">The item repository.</param> /// <param name="itemRepo">The item repository.</param>
public GenresPostScanTask( public GenresPostScanTask(
ILibraryManager libraryManager, ILibraryManager libraryManager,
ILogger<GenresPostScanTask> logger, ILogger<GenresValidator> logger,
IItemRepository itemRepo) IItemRepository itemRepo)
{ {
_libraryManager = libraryManager; _libraryManager = libraryManager;

@ -21,7 +21,7 @@ namespace Emby.Server.Implementations.Library.Validators
/// <summary> /// <summary>
/// The logger. /// The logger.
/// </summary> /// </summary>
private readonly ILogger _logger; private readonly ILogger<GenresValidator> _logger;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="GenresValidator"/> class. /// Initializes a new instance of the <see cref="GenresValidator"/> class.
@ -29,7 +29,7 @@ namespace Emby.Server.Implementations.Library.Validators
/// <param name="libraryManager">The library manager.</param> /// <param name="libraryManager">The library manager.</param>
/// <param name="logger">The logger.</param> /// <param name="logger">The logger.</param>
/// <param name="itemRepo">The item repository.</param> /// <param name="itemRepo">The item repository.</param>
public GenresValidator(ILibraryManager libraryManager, ILogger logger, IItemRepository itemRepo) public GenresValidator(ILibraryManager libraryManager, ILogger<GenresValidator> logger, IItemRepository itemRepo)
{ {
_libraryManager = libraryManager; _libraryManager = libraryManager;
_logger = logger; _logger = logger;

@ -16,7 +16,7 @@ namespace Emby.Server.Implementations.Library.Validators
/// The library manager. /// The library manager.
/// </summary> /// </summary>
private readonly ILibraryManager _libraryManager; private readonly ILibraryManager _libraryManager;
private readonly ILogger _logger; private readonly ILogger<MusicGenresValidator> _logger;
private readonly IItemRepository _itemRepo; private readonly IItemRepository _itemRepo;
/// <summary> /// <summary>
@ -27,7 +27,7 @@ namespace Emby.Server.Implementations.Library.Validators
/// <param name="itemRepo">The item repository.</param> /// <param name="itemRepo">The item repository.</param>
public MusicGenresPostScanTask( public MusicGenresPostScanTask(
ILibraryManager libraryManager, ILibraryManager libraryManager,
ILogger<MusicGenresPostScanTask> logger, ILogger<MusicGenresValidator> logger,
IItemRepository itemRepo) IItemRepository itemRepo)
{ {
_libraryManager = libraryManager; _libraryManager = libraryManager;

@ -20,7 +20,7 @@ namespace Emby.Server.Implementations.Library.Validators
/// <summary> /// <summary>
/// The logger. /// The logger.
/// </summary> /// </summary>
private readonly ILogger _logger; private readonly ILogger<MusicGenresValidator> _logger;
private readonly IItemRepository _itemRepo; private readonly IItemRepository _itemRepo;
/// <summary> /// <summary>
@ -29,7 +29,7 @@ namespace Emby.Server.Implementations.Library.Validators
/// <param name="libraryManager">The library manager.</param> /// <param name="libraryManager">The library manager.</param>
/// <param name="logger">The logger.</param> /// <param name="logger">The logger.</param>
/// <param name="itemRepo">The item repository.</param> /// <param name="itemRepo">The item repository.</param>
public MusicGenresValidator(ILibraryManager libraryManager, ILogger logger, IItemRepository itemRepo) public MusicGenresValidator(ILibraryManager libraryManager, ILogger<MusicGenresValidator> logger, IItemRepository itemRepo)
{ {
_libraryManager = libraryManager; _libraryManager = libraryManager;
_logger = logger; _logger = logger;

@ -17,7 +17,7 @@ namespace Emby.Server.Implementations.Library.Validators
/// </summary> /// </summary>
private readonly ILibraryManager _libraryManager; private readonly ILibraryManager _libraryManager;
private readonly ILogger _logger; private readonly ILogger<StudiosValidator> _logger;
private readonly IItemRepository _itemRepo; private readonly IItemRepository _itemRepo;
/// <summary> /// <summary>
@ -28,7 +28,7 @@ namespace Emby.Server.Implementations.Library.Validators
/// <param name="itemRepo">The item repository.</param> /// <param name="itemRepo">The item repository.</param>
public StudiosPostScanTask( public StudiosPostScanTask(
ILibraryManager libraryManager, ILibraryManager libraryManager,
ILogger<StudiosPostScanTask> logger, ILogger<StudiosValidator> logger,
IItemRepository itemRepo) IItemRepository itemRepo)
{ {
_libraryManager = libraryManager; _libraryManager = libraryManager;

@ -24,7 +24,7 @@ namespace Emby.Server.Implementations.Library.Validators
/// <summary> /// <summary>
/// The logger. /// The logger.
/// </summary> /// </summary>
private readonly ILogger _logger; private readonly ILogger<StudiosValidator> _logger;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="StudiosValidator" /> class. /// Initializes a new instance of the <see cref="StudiosValidator" /> class.
@ -32,7 +32,7 @@ namespace Emby.Server.Implementations.Library.Validators
/// <param name="libraryManager">The library manager.</param> /// <param name="libraryManager">The library manager.</param>
/// <param name="logger">The logger.</param> /// <param name="logger">The logger.</param>
/// <param name="itemRepo">The item repository.</param> /// <param name="itemRepo">The item repository.</param>
public StudiosValidator(ILibraryManager libraryManager, ILogger logger, IItemRepository itemRepo) public StudiosValidator(ILibraryManager libraryManager, ILogger<StudiosValidator> logger, IItemRepository itemRepo)
{ {
_libraryManager = libraryManager; _libraryManager = libraryManager;
_logger = logger; _logger = logger;
@ -92,7 +92,6 @@ namespace Emby.Server.Implementations.Library.Validators
_libraryManager.DeleteItem(item, new DeleteOptions _libraryManager.DeleteItem(item, new DeleteOptions
{ {
DeleteFileLocation = false DeleteFileLocation = false
}, false); }, false);
} }

@ -46,7 +46,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
private const int TunerDiscoveryDurationMs = 3000; private const int TunerDiscoveryDurationMs = 3000;
private readonly IServerApplicationHost _appHost; private readonly IServerApplicationHost _appHost;
private readonly ILogger _logger; private readonly ILogger<EmbyTV> _logger;
private readonly IHttpClient _httpClient; private readonly IHttpClient _httpClient;
private readonly IServerConfigurationManager _config; private readonly IServerConfigurationManager _config;
private readonly IJsonSerializer _jsonSerializer; private readonly IJsonSerializer _jsonSerializer;
@ -1547,7 +1547,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
IsFolder = false, IsFolder = false,
Recursive = true, Recursive = true,
DtoOptions = new DtoOptions(true) DtoOptions = new DtoOptions(true)
}) })
.Where(i => i.IsFileProtocol && File.Exists(i.Path)) .Where(i => i.IsFileProtocol && File.Exists(i.Path))
.Skip(seriesTimer.KeepUpTo - 1) .Skip(seriesTimer.KeepUpTo - 1)
@ -1893,22 +1892,22 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
writer.WriteStartDocument(true); writer.WriteStartDocument(true);
writer.WriteStartElement("tvshow"); writer.WriteStartElement("tvshow");
string id; string id;
if (timer.SeriesProviderIds.TryGetValue(MetadataProviders.Tvdb.ToString(), out id)) if (timer.SeriesProviderIds.TryGetValue(MetadataProvider.Tvdb.ToString(), out id))
{ {
writer.WriteElementString("id", id); writer.WriteElementString("id", id);
} }
if (timer.SeriesProviderIds.TryGetValue(MetadataProviders.Imdb.ToString(), out id)) if (timer.SeriesProviderIds.TryGetValue(MetadataProvider.Imdb.ToString(), out id))
{ {
writer.WriteElementString("imdb_id", id); writer.WriteElementString("imdb_id", id);
} }
if (timer.SeriesProviderIds.TryGetValue(MetadataProviders.Tmdb.ToString(), out id)) if (timer.SeriesProviderIds.TryGetValue(MetadataProvider.Tmdb.ToString(), out id))
{ {
writer.WriteElementString("tmdbid", id); writer.WriteElementString("tmdbid", id);
} }
if (timer.SeriesProviderIds.TryGetValue(MetadataProviders.Zap2It.ToString(), out id)) if (timer.SeriesProviderIds.TryGetValue(MetadataProvider.Zap2It.ToString(), out id))
{ {
writer.WriteElementString("zap2itid", id); writer.WriteElementString("zap2itid", id);
} }
@ -2075,14 +2074,14 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
writer.WriteElementString("credits", person); writer.WriteElementString("credits", person);
} }
var tmdbCollection = item.GetProviderId(MetadataProviders.TmdbCollection); var tmdbCollection = item.GetProviderId(MetadataProvider.TmdbCollection);
if (!string.IsNullOrEmpty(tmdbCollection)) if (!string.IsNullOrEmpty(tmdbCollection))
{ {
writer.WriteElementString("collectionnumber", tmdbCollection); writer.WriteElementString("collectionnumber", tmdbCollection);
} }
var imdb = item.GetProviderId(MetadataProviders.Imdb); var imdb = item.GetProviderId(MetadataProvider.Imdb);
if (!string.IsNullOrEmpty(imdb)) if (!string.IsNullOrEmpty(imdb))
{ {
if (!isSeriesEpisode) if (!isSeriesEpisode)
@ -2096,7 +2095,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
lockData = false; lockData = false;
} }
var tvdb = item.GetProviderId(MetadataProviders.Tvdb); var tvdb = item.GetProviderId(MetadataProvider.Tvdb);
if (!string.IsNullOrEmpty(tvdb)) if (!string.IsNullOrEmpty(tvdb))
{ {
writer.WriteElementString("tvdbid", tvdb); writer.WriteElementString("tvdbid", tvdb);
@ -2105,7 +2104,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
lockData = false; lockData = false;
} }
var tmdb = item.GetProviderId(MetadataProviders.Tmdb); var tmdb = item.GetProviderId(MetadataProvider.Tmdb);
if (!string.IsNullOrEmpty(tmdb)) if (!string.IsNullOrEmpty(tmdb))
{ {
writer.WriteElementString("tmdbid", tmdb); writer.WriteElementString("tmdbid", tmdb);

@ -56,7 +56,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
name += " " + info.EpisodeTitle; name += " " + info.EpisodeTitle;
} }
} }
else if (info.IsMovie && info.ProductionYear != null) else if (info.IsMovie && info.ProductionYear != null)
{ {
name += " (" + info.ProductionYear + ")"; name += " (" + info.ProductionYear + ")";

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save