Merge pull request #9 from jellyfin/master

Yanking in latest changes
This commit is contained in:
LogicalPhallacy 2019-02-11 22:48:50 -08:00 committed by GitHub
commit 8bf88f4cb2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
349 changed files with 2446 additions and 4839 deletions

View File

@ -15,6 +15,7 @@
- [cvium](https://github.com/cvium) - [cvium](https://github.com/cvium)
- [wtayl0r](https://github.com/wtayl0r) - [wtayl0r](https://github.com/wtayl0r)
- [TtheCreator](https://github.com/Tthecreator) - [TtheCreator](https://github.com/Tthecreator)
- [dkanada](https://github.com/dkanada)
- [LogicalPhallacy](https://github.com/LogicalPhallacy/) - [LogicalPhallacy](https://github.com/LogicalPhallacy/)
- [RazeLighter777](https://github.com/RazeLighter777) - [RazeLighter777](https://github.com/RazeLighter777)

View File

@ -10,7 +10,7 @@ RUN export DOTNET_CLI_TELEMETRY_OPTOUT=1 \
--output /jellyfin \ --output /jellyfin \
Jellyfin.Server Jellyfin.Server
FROM jrottenberg/ffmpeg:4.0-scratch as ffmpeg FROM jrottenberg/ffmpeg:4.0-vaapi as ffmpeg
FROM microsoft/dotnet:${DOTNET_VERSION}-runtime FROM microsoft/dotnet:${DOTNET_VERSION}-runtime
# libfontconfig1 is required for Skia # libfontconfig1 is required for Skia
RUN apt-get update \ RUN apt-get update \
@ -23,4 +23,4 @@ COPY --from=ffmpeg / /
COPY --from=builder /jellyfin /jellyfin COPY --from=builder /jellyfin /jellyfin
EXPOSE 8096 EXPOSE 8096
VOLUME /config /media VOLUME /config /media
ENTRYPOINT dotnet /jellyfin/jellyfin.dll -programdata /config ENTRYPOINT dotnet /jellyfin/jellyfin.dll --datadir /config

View File

@ -21,4 +21,4 @@ RUN apt-get update \
COPY --from=builder /jellyfin /jellyfin COPY --from=builder /jellyfin /jellyfin
EXPOSE 8096 EXPOSE 8096
VOLUME /config /media VOLUME /config /media
ENTRYPOINT dotnet /jellyfin/jellyfin.dll -programdata /config ENTRYPOINT dotnet /jellyfin/jellyfin.dll --datadir /config

View File

@ -30,4 +30,4 @@ COPY --from=qemu_extract qemu-* /usr/bin
COPY --from=builder /jellyfin /jellyfin COPY --from=builder /jellyfin /jellyfin
EXPOSE 8096 EXPOSE 8096
VOLUME /config /media VOLUME /config /media
ENTRYPOINT dotnet /jellyfin/jellyfin.dll -programdata /config ENTRYPOINT dotnet /jellyfin/jellyfin.dll --datadir /config

View File

@ -236,7 +236,9 @@ namespace Emby.Dlna.Api
public object Get(GetIcon request) public object Get(GetIcon request)
{ {
var contentType = "image/" + Path.GetExtension(request.Filename).TrimStart('.').ToLower(); var contentType = "image/" + Path.GetExtension(request.Filename)
.TrimStart('.')
.ToLowerInvariant();
var cacheLength = TimeSpan.FromDays(365); var cacheLength = TimeSpan.FromDays(365);
var cacheKey = Request.RawUrl.GetMD5(); var cacheKey = Request.RawUrl.GetMD5();

View File

@ -63,7 +63,7 @@ namespace Emby.Dlna.ContentDirectory
_profile = profile; _profile = profile;
_config = config; _config = config;
_didlBuilder = new DidlBuilder(profile, user, imageProcessor, serverAddress, accessToken, userDataManager, localization, mediaSourceManager, _logger, libraryManager, mediaEncoder); _didlBuilder = new DidlBuilder(profile, user, imageProcessor, serverAddress, accessToken, userDataManager, localization, mediaSourceManager, _logger, mediaEncoder);
} }
protected override IEnumerable<KeyValuePair<string, string>> GetResult(string methodName, IDictionary<string, string> methodParams) protected override IEnumerable<KeyValuePair<string, string>> GetResult(string methodName, IDictionary<string, string> methodParams)
@ -454,7 +454,7 @@ namespace Emby.Dlna.ContentDirectory
User = user, User = user,
Recursive = true, Recursive = true,
IsMissing = false, IsMissing = false,
ExcludeItemTypes = new[] { typeof(Game).Name, typeof(Book).Name }, ExcludeItemTypes = new[] { typeof(Book).Name },
IsFolder = isFolder, IsFolder = isFolder,
MediaTypes = mediaTypes.ToArray(), MediaTypes = mediaTypes.ToArray(),
DtoOptions = GetDtoOptions() DtoOptions = GetDtoOptions()
@ -523,7 +523,7 @@ namespace Emby.Dlna.ContentDirectory
Limit = limit, Limit = limit,
StartIndex = startIndex, StartIndex = startIndex,
IsVirtualItem = false, IsVirtualItem = false,
ExcludeItemTypes = new[] { typeof(Game).Name, typeof(Book).Name }, ExcludeItemTypes = new[] { typeof(Book).Name },
IsPlaceHolder = false, IsPlaceHolder = false,
DtoOptions = GetDtoOptions() DtoOptions = GetDtoOptions()
}; };

View File

@ -43,22 +43,30 @@ namespace Emby.Dlna.Didl
private readonly ILocalizationManager _localization; private readonly ILocalizationManager _localization;
private readonly IMediaSourceManager _mediaSourceManager; private readonly IMediaSourceManager _mediaSourceManager;
private readonly ILogger _logger; private readonly ILogger _logger;
private readonly ILibraryManager _libraryManager;
private readonly IMediaEncoder _mediaEncoder; private readonly IMediaEncoder _mediaEncoder;
public DidlBuilder(DeviceProfile profile, User user, IImageProcessor imageProcessor, string serverAddress, string accessToken, IUserDataManager userDataManager, ILocalizationManager localization, IMediaSourceManager mediaSourceManager, ILogger logger, ILibraryManager libraryManager, IMediaEncoder mediaEncoder) public DidlBuilder(
DeviceProfile profile,
User user,
IImageProcessor imageProcessor,
string serverAddress,
string accessToken,
IUserDataManager userDataManager,
ILocalizationManager localization,
IMediaSourceManager mediaSourceManager,
ILogger logger,
IMediaEncoder mediaEncoder)
{ {
_profile = profile; _profile = profile;
_user = user;
_imageProcessor = imageProcessor; _imageProcessor = imageProcessor;
_serverAddress = serverAddress; _serverAddress = serverAddress;
_accessToken = accessToken;
_userDataManager = userDataManager; _userDataManager = userDataManager;
_localization = localization; _localization = localization;
_mediaSourceManager = mediaSourceManager; _mediaSourceManager = mediaSourceManager;
_logger = logger; _logger = logger;
_libraryManager = libraryManager;
_mediaEncoder = mediaEncoder; _mediaEncoder = mediaEncoder;
_accessToken = accessToken;
_user = user;
} }
public static string NormalizeDlnaMediaUrl(string url) public static string NormalizeDlnaMediaUrl(string url)
@ -117,7 +125,8 @@ namespace Emby.Dlna.Didl
} }
} }
public void WriteItemElement(DlnaOptions options, public void WriteItemElement(
DlnaOptions options,
XmlWriter writer, XmlWriter writer,
BaseItem item, BaseItem item,
User user, User user,
@ -232,12 +241,15 @@ namespace Emby.Dlna.Didl
AddVideoResource(writer, video, deviceId, filter, contentFeature, streamInfo); AddVideoResource(writer, video, deviceId, filter, contentFeature, streamInfo);
} }
var subtitleProfiles = streamInfo.GetSubtitleProfiles(_mediaEncoder, false, _serverAddress, _accessToken) var subtitleProfiles = streamInfo.GetSubtitleProfiles(_mediaEncoder, false, _serverAddress, _accessToken);
.Where(subtitle => subtitle.DeliveryMethod == SubtitleDeliveryMethod.External)
.ToList();
foreach (var subtitle in subtitleProfiles) foreach (var subtitle in subtitleProfiles)
{ {
if (subtitle.DeliveryMethod != SubtitleDeliveryMethod.External)
{
continue;
}
var subtitleAdded = AddSubtitleElement(writer, subtitle); var subtitleAdded = AddSubtitleElement(writer, subtitle);
if (subtitleAdded && _profile.EnableSingleSubtitleLimit) if (subtitleAdded && _profile.EnableSingleSubtitleLimit)
@ -250,7 +262,8 @@ namespace Emby.Dlna.Didl
private bool AddSubtitleElement(XmlWriter writer, SubtitleStreamInfo info) private bool AddSubtitleElement(XmlWriter writer, SubtitleStreamInfo info)
{ {
var subtitleProfile = _profile.SubtitleProfiles var subtitleProfile = _profile.SubtitleProfiles
.FirstOrDefault(i => string.Equals(info.Format, i.Format, StringComparison.OrdinalIgnoreCase) && i.Method == SubtitleDeliveryMethod.External); .FirstOrDefault(i => string.Equals(info.Format, i.Format, StringComparison.OrdinalIgnoreCase)
&& i.Method == SubtitleDeliveryMethod.External);
if (subtitleProfile == null) if (subtitleProfile == null)
{ {
@ -265,7 +278,7 @@ namespace Emby.Dlna.Didl
// <sec:CaptionInfo sec:type="srt">http://192.168.1.3:9999/video.srt</sec:CaptionInfo> // <sec:CaptionInfo sec:type="srt">http://192.168.1.3:9999/video.srt</sec:CaptionInfo>
writer.WriteStartElement("sec", "CaptionInfoEx", null); writer.WriteStartElement("sec", "CaptionInfoEx", null);
writer.WriteAttributeString("sec", "type", null, info.Format.ToLower()); writer.WriteAttributeString("sec", "type", null, info.Format.ToLowerInvariant());
writer.WriteString(info.Url); writer.WriteString(info.Url);
writer.WriteFullEndElement(); writer.WriteFullEndElement();
@ -282,7 +295,7 @@ namespace Emby.Dlna.Didl
else else
{ {
writer.WriteStartElement(string.Empty, "res", NS_DIDL); writer.WriteStartElement(string.Empty, "res", NS_DIDL);
var protocolInfo = string.Format("http-get:*:text/{0}:*", info.Format.ToLower()); var protocolInfo = string.Format("http-get:*:text/{0}:*", info.Format.ToLowerInvariant());
writer.WriteAttributeString("protocolInfo", protocolInfo); writer.WriteAttributeString("protocolInfo", protocolInfo);
writer.WriteString(info.Url); writer.WriteString(info.Url);
@ -387,91 +400,39 @@ namespace Emby.Dlna.Didl
private string GetDisplayName(BaseItem item, StubType? itemStubType, BaseItem context) private string GetDisplayName(BaseItem item, StubType? itemStubType, BaseItem context)
{ {
if (itemStubType.HasValue && itemStubType.Value == StubType.Latest) if (itemStubType.HasValue)
{ {
return _localization.GetLocalizedString("Latest"); switch (itemStubType.Value)
} {
if (itemStubType.HasValue && itemStubType.Value == StubType.Playlists) case StubType.Latest: return _localization.GetLocalizedString("Latest");
{ case StubType.Playlists: return _localization.GetLocalizedString("Playlists");
return _localization.GetLocalizedString("Playlists"); case StubType.AlbumArtists: return _localization.GetLocalizedString("HeaderAlbumArtists");
} case StubType.Albums: return _localization.GetLocalizedString("Albums");
if (itemStubType.HasValue && itemStubType.Value == StubType.AlbumArtists) case StubType.Artists: return _localization.GetLocalizedString("Artists");
{ case StubType.Songs: return _localization.GetLocalizedString("Songs");
return _localization.GetLocalizedString("HeaderAlbumArtists"); case StubType.Genres: return _localization.GetLocalizedString("Genres");
} case StubType.FavoriteAlbums: return _localization.GetLocalizedString("HeaderFavoriteAlbums");
if (itemStubType.HasValue && itemStubType.Value == StubType.Albums) case StubType.FavoriteArtists: return _localization.GetLocalizedString("HeaderFavoriteArtists");
{ case StubType.FavoriteSongs: return _localization.GetLocalizedString("HeaderFavoriteSongs");
return _localization.GetLocalizedString("Albums"); case StubType.ContinueWatching: return _localization.GetLocalizedString("HeaderContinueWatching");
} case StubType.Movies: return _localization.GetLocalizedString("Movies");
if (itemStubType.HasValue && itemStubType.Value == StubType.Artists) case StubType.Collections: return _localization.GetLocalizedString("Collections");
{ case StubType.Favorites: return _localization.GetLocalizedString("Favorites");
return _localization.GetLocalizedString("Artists"); case StubType.NextUp: return _localization.GetLocalizedString("HeaderNextUp");
} case StubType.FavoriteSeries: return _localization.GetLocalizedString("HeaderFavoriteShows");
if (itemStubType.HasValue && itemStubType.Value == StubType.Songs) case StubType.FavoriteEpisodes: return _localization.GetLocalizedString("HeaderFavoriteEpisodes");
{ case StubType.Series: return _localization.GetLocalizedString("Shows");
return _localization.GetLocalizedString("Songs"); default: break;
} }
if (itemStubType.HasValue && itemStubType.Value == StubType.Genres)
{
return _localization.GetLocalizedString("Genres");
}
if (itemStubType.HasValue && itemStubType.Value == StubType.FavoriteAlbums)
{
return _localization.GetLocalizedString("HeaderFavoriteAlbums");
}
if (itemStubType.HasValue && itemStubType.Value == StubType.FavoriteArtists)
{
return _localization.GetLocalizedString("HeaderFavoriteArtists");
}
if (itemStubType.HasValue && itemStubType.Value == StubType.FavoriteSongs)
{
return _localization.GetLocalizedString("HeaderFavoriteSongs");
}
if (itemStubType.HasValue && itemStubType.Value == StubType.ContinueWatching)
{
return _localization.GetLocalizedString("HeaderContinueWatching");
}
if (itemStubType.HasValue && itemStubType.Value == StubType.Movies)
{
return _localization.GetLocalizedString("Movies");
}
if (itemStubType.HasValue && itemStubType.Value == StubType.Collections)
{
return _localization.GetLocalizedString("Collections");
}
if (itemStubType.HasValue && itemStubType.Value == StubType.Favorites)
{
return _localization.GetLocalizedString("Favorites");
}
if (itemStubType.HasValue && itemStubType.Value == StubType.NextUp)
{
return _localization.GetLocalizedString("HeaderNextUp");
}
if (itemStubType.HasValue && itemStubType.Value == StubType.FavoriteSeries)
{
return _localization.GetLocalizedString("HeaderFavoriteShows");
}
if (itemStubType.HasValue && itemStubType.Value == StubType.FavoriteEpisodes)
{
return _localization.GetLocalizedString("HeaderFavoriteEpisodes");
}
if (itemStubType.HasValue && itemStubType.Value == StubType.Series)
{
return _localization.GetLocalizedString("Shows");
} }
var episode = item as Episode; if (item is Episode episode && context is Season season)
var season = context as Season;
if (episode != null && season != null)
{ {
// This is a special embedded within a season // This is a special embedded within a season
if (item.ParentIndexNumber.HasValue && item.ParentIndexNumber.Value == 0) if (item.ParentIndexNumber.HasValue && item.ParentIndexNumber.Value == 0
&& season.IndexNumber.HasValue && season.IndexNumber.Value != 0)
{ {
if (season.IndexNumber.HasValue && season.IndexNumber.Value != 0) return string.Format(_localization.GetLocalizedString("ValueSpecialEpisodeName"), item.Name);
{
return string.Format(_localization.GetLocalizedString("ValueSpecialEpisodeName"), item.Name);
}
} }
if (item.IndexNumber.HasValue) if (item.IndexNumber.HasValue)
@ -585,10 +546,8 @@ namespace Emby.Dlna.Didl
public static bool IsIdRoot(string id) public static bool IsIdRoot(string id)
{ {
if (string.IsNullOrWhiteSpace(id) || if (string.IsNullOrWhiteSpace(id)
|| string.Equals(id, "0", StringComparison.OrdinalIgnoreCase)
string.Equals(id, "0", StringComparison.OrdinalIgnoreCase)
// Samsung sometimes uses 1 as root // Samsung sometimes uses 1 as root
|| string.Equals(id, "1", StringComparison.OrdinalIgnoreCase)) || string.Equals(id, "1", StringComparison.OrdinalIgnoreCase))
{ {
@ -808,7 +767,7 @@ namespace Emby.Dlna.Didl
{ {
writer.WriteString(_profile.RequiresPlainFolders ? "object.container.storageFolder" : "object.container.genre.musicGenre"); writer.WriteString(_profile.RequiresPlainFolders ? "object.container.storageFolder" : "object.container.genre.musicGenre");
} }
else if (item is Genre || item is GameGenre) else if (item is Genre)
{ {
writer.WriteString(_profile.RequiresPlainFolders ? "object.container.storageFolder" : "object.container.genre"); writer.WriteString(_profile.RequiresPlainFolders ? "object.container.storageFolder" : "object.container.genre");
} }
@ -844,7 +803,7 @@ namespace Emby.Dlna.Didl
// var type = types.FirstOrDefault(i => string.Equals(i, actor.Type, StringComparison.OrdinalIgnoreCase) || string.Equals(i, actor.Role, StringComparison.OrdinalIgnoreCase)) // var type = types.FirstOrDefault(i => string.Equals(i, actor.Type, StringComparison.OrdinalIgnoreCase) || string.Equals(i, actor.Role, StringComparison.OrdinalIgnoreCase))
// ?? PersonType.Actor; // ?? PersonType.Actor;
// AddValue(writer, "upnp", type.ToLower(), actor.Name, NS_UPNP); // AddValue(writer, "upnp", type.ToLowerInvariant(), actor.Name, NS_UPNP);
// index++; // index++;
@ -1112,7 +1071,7 @@ namespace Emby.Dlna.Didl
}; };
} }
class ImageDownloadInfo private class ImageDownloadInfo
{ {
internal Guid ItemId; internal Guid ItemId;
internal string ImageTag; internal string ImageTag;
@ -1128,7 +1087,7 @@ namespace Emby.Dlna.Didl
internal ItemImageInfo ItemImageInfo; internal ItemImageInfo ItemImageInfo;
} }
class ImageUrlInfo private class ImageUrlInfo
{ {
internal string Url; internal string Url;
@ -1147,7 +1106,7 @@ namespace Emby.Dlna.Didl
if (stubType.HasValue) if (stubType.HasValue)
{ {
id = stubType.Value.ToString().ToLower() + "_" + id; id = stubType.Value.ToString().ToLowerInvariant() + "_" + id;
} }
return id; return id;

View File

@ -4,6 +4,7 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Emby.Dlna.Profiles; using Emby.Dlna.Profiles;
using Emby.Dlna.Server; using Emby.Dlna.Server;
using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Configuration;
@ -48,11 +49,11 @@ namespace Emby.Dlna
_assemblyInfo = assemblyInfo; _assemblyInfo = assemblyInfo;
} }
public void InitProfiles() public async Task InitProfilesAsync()
{ {
try try
{ {
ExtractSystemProfiles(); await ExtractSystemProfilesAsync();
LoadProfiles(); LoadProfiles();
} }
catch (Exception ex) catch (Exception ex)
@ -300,7 +301,7 @@ namespace Emby.Dlna
profile = ReserializeProfile(tempProfile); profile = ReserializeProfile(tempProfile);
profile.Id = path.ToLower().GetMD5().ToString("N"); profile.Id = path.ToLowerInvariant().GetMD5().ToString("N");
_profiles[path] = new Tuple<InternalProfileInfo, DeviceProfile>(GetInternalProfileInfo(_fileSystem.GetFileInfo(path), type), profile); _profiles[path] = new Tuple<InternalProfileInfo, DeviceProfile>(GetInternalProfileInfo(_fileSystem.GetFileInfo(path), type), profile);
@ -352,14 +353,14 @@ namespace Emby.Dlna
Info = new DeviceProfileInfo Info = new DeviceProfileInfo
{ {
Id = file.FullName.ToLower().GetMD5().ToString("N"), Id = file.FullName.ToLowerInvariant().GetMD5().ToString("N"),
Name = _fileSystem.GetFileNameWithoutExtension(file), Name = _fileSystem.GetFileNameWithoutExtension(file),
Type = type Type = type
} }
}; };
} }
private void ExtractSystemProfiles() private async Task ExtractSystemProfilesAsync()
{ {
var namespaceName = GetType().Namespace + ".Profiles.Xml."; var namespaceName = GetType().Namespace + ".Profiles.Xml.";
@ -383,7 +384,7 @@ namespace Emby.Dlna
using (var fileStream = _fileSystem.GetFileStream(path, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read)) using (var fileStream = _fileSystem.GetFileStream(path, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read))
{ {
stream.CopyTo(fileStream); await stream.CopyToAsync(fileStream);
} }
} }
} }
@ -506,7 +507,7 @@ namespace Emby.Dlna
? ImageFormat.Png ? ImageFormat.Png
: ImageFormat.Jpg; : ImageFormat.Jpg;
var resource = GetType().Namespace + ".Images." + filename.ToLower(); var resource = GetType().Namespace + ".Images." + filename.ToLowerInvariant();
return new ImageStream return new ImageStream
{ {

View File

@ -20,7 +20,6 @@ using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Globalization; using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.Net; using MediaBrowser.Model.Net;
using MediaBrowser.Model.System; using MediaBrowser.Model.System;
using MediaBrowser.Model.Threading;
using MediaBrowser.Model.Xml; using MediaBrowser.Model.Xml;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Rssdp; using Rssdp;
@ -49,8 +48,7 @@ namespace Emby.Dlna.Main
private readonly IDeviceDiscovery _deviceDiscovery; private readonly IDeviceDiscovery _deviceDiscovery;
private SsdpDevicePublisher _Publisher; private SsdpDevicePublisher _Publisher;
private readonly ITimerFactory _timerFactory;
private readonly ISocketFactory _socketFactory; private readonly ISocketFactory _socketFactory;
private readonly IEnvironmentInfo _environmentInfo; private readonly IEnvironmentInfo _environmentInfo;
private readonly INetworkManager _networkManager; private readonly INetworkManager _networkManager;
@ -78,7 +76,6 @@ namespace Emby.Dlna.Main
IDeviceDiscovery deviceDiscovery, IDeviceDiscovery deviceDiscovery,
IMediaEncoder mediaEncoder, IMediaEncoder mediaEncoder,
ISocketFactory socketFactory, ISocketFactory socketFactory,
ITimerFactory timerFactory,
IEnvironmentInfo environmentInfo, IEnvironmentInfo environmentInfo,
INetworkManager networkManager, INetworkManager networkManager,
IUserViewManager userViewManager, IUserViewManager userViewManager,
@ -99,7 +96,6 @@ namespace Emby.Dlna.Main
_deviceDiscovery = deviceDiscovery; _deviceDiscovery = deviceDiscovery;
_mediaEncoder = mediaEncoder; _mediaEncoder = mediaEncoder;
_socketFactory = socketFactory; _socketFactory = socketFactory;
_timerFactory = timerFactory;
_environmentInfo = environmentInfo; _environmentInfo = environmentInfo;
_networkManager = networkManager; _networkManager = networkManager;
_logger = loggerFactory.CreateLogger("Dlna"); _logger = loggerFactory.CreateLogger("Dlna");
@ -125,9 +121,9 @@ namespace Emby.Dlna.Main
Current = this; Current = this;
} }
public void Run() public async Task RunAsync()
{ {
((DlnaManager)_dlnaManager).InitProfiles(); await ((DlnaManager)_dlnaManager).InitProfilesAsync().ConfigureAwait(false);
ReloadComponents(); ReloadComponents();
@ -233,7 +229,7 @@ namespace Emby.Dlna.Main
try try
{ {
_Publisher = new SsdpDevicePublisher(_communicationsServer, _timerFactory, _environmentInfo.OperatingSystemName, _environmentInfo.OperatingSystemVersion); _Publisher = new SsdpDevicePublisher(_communicationsServer, _environmentInfo.OperatingSystemName, _environmentInfo.OperatingSystemVersion);
_Publisher.LogFunction = LogMessage; _Publisher.LogFunction = LogMessage;
_Publisher.SupportPnpRootDevice = false; _Publisher.SupportPnpRootDevice = false;
@ -263,7 +259,7 @@ namespace Emby.Dlna.Main
var fullService = "urn:schemas-upnp-org:device:MediaServer:1"; var fullService = "urn:schemas-upnp-org:device:MediaServer:1";
_logger.LogInformation("Registering publisher for {0} on {1}", fullService, address.ToString()); _logger.LogInformation("Registering publisher for {0} on {1}", fullService, address);
var descriptorUri = "/dlna/" + udn + "/description.xml"; var descriptorUri = "/dlna/" + udn + "/description.xml";
var uri = new Uri(_appHost.GetLocalApiUrl(address) + descriptorUri); var uri = new Uri(_appHost.GetLocalApiUrl(address) + descriptorUri);
@ -353,8 +349,7 @@ namespace Emby.Dlna.Main
_userDataManager, _userDataManager,
_localization, _localization,
_mediaSourceManager, _mediaSourceManager,
_mediaEncoder, _mediaEncoder);
_timerFactory);
_manager.Start(); _manager.Start();
} }

View File

@ -10,7 +10,6 @@ using Emby.Dlna.Server;
using Emby.Dlna.Ssdp; using Emby.Dlna.Ssdp;
using MediaBrowser.Common.Net; using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Configuration;
using MediaBrowser.Model.Threading;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
namespace Emby.Dlna.PlayTo namespace Emby.Dlna.PlayTo
@ -19,7 +18,7 @@ namespace Emby.Dlna.PlayTo
{ {
#region Fields & Properties #region Fields & Properties
private ITimer _timer; private Timer _timer;
public DeviceInfo Properties { get; set; } public DeviceInfo Properties { get; set; }
@ -40,12 +39,7 @@ namespace Emby.Dlna.PlayTo
public TimeSpan? Duration { get; set; } public TimeSpan? Duration { get; set; }
private TimeSpan _position = TimeSpan.FromSeconds(0); public TimeSpan Position { get; set; } = TimeSpan.FromSeconds(0);
public TimeSpan Position
{
get => _position;
set => _position = value;
}
public TRANSPORTSTATE TransportState { get; private set; } public TRANSPORTSTATE TransportState { get; private set; }
@ -61,24 +55,20 @@ namespace Emby.Dlna.PlayTo
private readonly ILogger _logger; private readonly ILogger _logger;
private readonly IServerConfigurationManager _config; private readonly IServerConfigurationManager _config;
public DateTime DateLastActivity { get; private set; }
public Action OnDeviceUnavailable { get; set; } public Action OnDeviceUnavailable { get; set; }
private readonly ITimerFactory _timerFactory; public Device(DeviceInfo deviceProperties, IHttpClient httpClient, ILogger logger, IServerConfigurationManager config)
public Device(DeviceInfo deviceProperties, IHttpClient httpClient, ILogger logger, IServerConfigurationManager config, ITimerFactory timerFactory)
{ {
Properties = deviceProperties; Properties = deviceProperties;
_httpClient = httpClient; _httpClient = httpClient;
_logger = logger; _logger = logger;
_config = config; _config = config;
_timerFactory = timerFactory;
} }
public void Start() public void Start()
{ {
_logger.LogDebug("Dlna Device.Start"); _logger.LogDebug("Dlna Device.Start");
_timer = _timerFactory.Create(TimerCallback, null, 1000, Timeout.Infinite); _timer = new Timer(TimerCallback, null, 1000, Timeout.Infinite);
} }
private DateTime _lastVolumeRefresh; private DateTime _lastVolumeRefresh;
@ -119,7 +109,9 @@ namespace Emby.Dlna.PlayTo
lock (_timerLock) lock (_timerLock)
{ {
if (_disposed) if (_disposed)
{
return; return;
}
_volumeRefreshActive = true; _volumeRefreshActive = true;
@ -136,7 +128,9 @@ namespace Emby.Dlna.PlayTo
lock (_timerLock) lock (_timerLock)
{ {
if (_disposed) if (_disposed)
{
return; return;
}
_volumeRefreshActive = false; _volumeRefreshActive = false;
@ -144,11 +138,6 @@ namespace Emby.Dlna.PlayTo
} }
} }
public void OnPlaybackStartedExternally()
{
RestartTimer(true);
}
#region Commanding #region Commanding
public Task VolumeDown(CancellationToken cancellationToken) public Task VolumeDown(CancellationToken cancellationToken)
@ -333,7 +322,9 @@ namespace Emby.Dlna.PlayTo
private string CreateDidlMeta(string value) private string CreateDidlMeta(string value)
{ {
if (string.IsNullOrEmpty(value)) if (string.IsNullOrEmpty(value))
{
return string.Empty; return string.Empty;
}
return DescriptionXmlBuilder.Escape(value); return DescriptionXmlBuilder.Escape(value);
} }
@ -342,10 +333,11 @@ namespace Emby.Dlna.PlayTo
{ {
var command = avCommands.ServiceActions.FirstOrDefault(c => c.Name == "Play"); var command = avCommands.ServiceActions.FirstOrDefault(c => c.Name == "Play");
if (command == null) if (command == null)
{
return Task.CompletedTask; return Task.CompletedTask;
}
var service = GetAvTransportService(); var service = GetAvTransportService();
if (service == null) if (service == null)
{ {
throw new InvalidOperationException("Unable to find service"); throw new InvalidOperationException("Unable to find service");
@ -369,7 +361,9 @@ namespace Emby.Dlna.PlayTo
var command = avCommands.ServiceActions.FirstOrDefault(c => c.Name == "Stop"); var command = avCommands.ServiceActions.FirstOrDefault(c => c.Name == "Stop");
if (command == null) if (command == null)
{
return; return;
}
var service = GetAvTransportService(); var service = GetAvTransportService();
@ -385,7 +379,9 @@ namespace Emby.Dlna.PlayTo
var command = avCommands.ServiceActions.FirstOrDefault(c => c.Name == "Pause"); var command = avCommands.ServiceActions.FirstOrDefault(c => c.Name == "Pause");
if (command == null) if (command == null)
{
return; return;
}
var service = GetAvTransportService(); var service = GetAvTransportService();
@ -405,7 +401,9 @@ namespace Emby.Dlna.PlayTo
private async void TimerCallback(object sender) private async void TimerCallback(object sender)
{ {
if (_disposed) if (_disposed)
{
return; return;
}
try try
{ {
@ -425,8 +423,6 @@ namespace Emby.Dlna.PlayTo
return; return;
} }
DateLastActivity = DateTime.UtcNow;
if (transportState.HasValue) if (transportState.HasValue)
{ {
// If we're not playing anything no need to get additional data // If we're not playing anything no need to get additional data
@ -505,7 +501,9 @@ namespace Emby.Dlna.PlayTo
var command = rendererCommands.ServiceActions.FirstOrDefault(c => c.Name == "GetVolume"); var command = rendererCommands.ServiceActions.FirstOrDefault(c => c.Name == "GetVolume");
if (command == null) if (command == null)
{
return; return;
}
var service = GetServiceRenderingControl(); var service = GetServiceRenderingControl();
@ -518,13 +516,17 @@ namespace Emby.Dlna.PlayTo
.ConfigureAwait(false); .ConfigureAwait(false);
if (result == null || result.Document == null) if (result == null || result.Document == null)
{
return; return;
}
var volume = result.Document.Descendants(uPnpNamespaces.RenderingControl + "GetVolumeResponse").Select(i => i.Element("CurrentVolume")).FirstOrDefault(i => i != null); var volume = result.Document.Descendants(uPnpNamespaces.RenderingControl + "GetVolumeResponse").Select(i => i.Element("CurrentVolume")).FirstOrDefault(i => i != null);
var volumeValue = volume == null ? null : volume.Value; var volumeValue = volume?.Value;
if (string.IsNullOrWhiteSpace(volumeValue)) if (string.IsNullOrWhiteSpace(volumeValue))
{
return; return;
}
Volume = int.Parse(volumeValue, UsCulture); Volume = int.Parse(volumeValue, UsCulture);
@ -545,7 +547,9 @@ namespace Emby.Dlna.PlayTo
var command = rendererCommands.ServiceActions.FirstOrDefault(c => c.Name == "GetMute"); var command = rendererCommands.ServiceActions.FirstOrDefault(c => c.Name == "GetMute");
if (command == null) if (command == null)
{
return; return;
}
var service = GetServiceRenderingControl(); var service = GetServiceRenderingControl();
@ -560,39 +564,44 @@ namespace Emby.Dlna.PlayTo
if (result == null || result.Document == null) if (result == null || result.Document == null)
return; return;
var valueNode = result.Document.Descendants(uPnpNamespaces.RenderingControl + "GetMuteResponse").Select(i => i.Element("CurrentMute")).FirstOrDefault(i => i != null); var valueNode = result.Document.Descendants(uPnpNamespaces.RenderingControl + "GetMuteResponse")
var value = valueNode == null ? null : valueNode.Value; .Select(i => i.Element("CurrentMute"))
.FirstOrDefault(i => i != null);
IsMuted = string.Equals(value, "1", StringComparison.OrdinalIgnoreCase); IsMuted = string.Equals(valueNode?.Value, "1", StringComparison.OrdinalIgnoreCase);
} }
private async Task<TRANSPORTSTATE?> GetTransportInfo(TransportCommands avCommands, CancellationToken cancellationToken) private async Task<TRANSPORTSTATE?> GetTransportInfo(TransportCommands avCommands, CancellationToken cancellationToken)
{ {
var command = avCommands.ServiceActions.FirstOrDefault(c => c.Name == "GetTransportInfo"); var command = avCommands.ServiceActions.FirstOrDefault(c => c.Name == "GetTransportInfo");
if (command == null) if (command == null)
{
return null; return null;
}
var service = GetAvTransportService(); var service = GetAvTransportService();
if (service == null) if (service == null)
{
return null; return null;
}
var result = await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, avCommands.BuildPost(command, service.ServiceType), false) var result = await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, avCommands.BuildPost(command, service.ServiceType), false)
.ConfigureAwait(false); .ConfigureAwait(false);
if (result == null || result.Document == null) if (result == null || result.Document == null)
{
return null; return null;
}
var transportState = var transportState =
result.Document.Descendants(uPnpNamespaces.AvTransport + "GetTransportInfoResponse").Select(i => i.Element("CurrentTransportState")).FirstOrDefault(i => i != null); result.Document.Descendants(uPnpNamespaces.AvTransport + "GetTransportInfoResponse").Select(i => i.Element("CurrentTransportState")).FirstOrDefault(i => i != null);
var transportStateValue = transportState == null ? null : transportState.Value; var transportStateValue = transportState == null ? null : transportState.Value;
if (transportStateValue != null) if (transportStateValue != null
&& Enum.TryParse(transportStateValue, true, out TRANSPORTSTATE state))
{ {
if (Enum.TryParse(transportStateValue, true, out TRANSPORTSTATE state)) return state;
{
return state;
}
} }
return null; return null;
@ -602,10 +611,11 @@ namespace Emby.Dlna.PlayTo
{ {
var command = avCommands.ServiceActions.FirstOrDefault(c => c.Name == "GetMediaInfo"); var command = avCommands.ServiceActions.FirstOrDefault(c => c.Name == "GetMediaInfo");
if (command == null) if (command == null)
{
return null; return null;
}
var service = GetAvTransportService(); var service = GetAvTransportService();
if (service == null) if (service == null)
{ {
throw new InvalidOperationException("Unable to find service"); throw new InvalidOperationException("Unable to find service");
@ -617,7 +627,9 @@ namespace Emby.Dlna.PlayTo
.ConfigureAwait(false); .ConfigureAwait(false);
if (result == null || result.Document == null) if (result == null || result.Document == null)
{
return null; return null;
}
var track = result.Document.Descendants("CurrentURIMetaData").FirstOrDefault(); var track = result.Document.Descendants("CurrentURIMetaData").FirstOrDefault();
@ -657,11 +669,13 @@ namespace Emby.Dlna.PlayTo
return null; return null;
} }
private async Task<Tuple<bool, uBaseObject>> GetPositionInfo(TransportCommands avCommands, CancellationToken cancellationToken) private async Task<(bool, uBaseObject)> GetPositionInfo(TransportCommands avCommands, CancellationToken cancellationToken)
{ {
var command = avCommands.ServiceActions.FirstOrDefault(c => c.Name == "GetPositionInfo"); var command = avCommands.ServiceActions.FirstOrDefault(c => c.Name == "GetPositionInfo");
if (command == null) if (command == null)
return new Tuple<bool, uBaseObject>(false, null); {
return (false, null);
}
var service = GetAvTransportService(); var service = GetAvTransportService();
@ -676,7 +690,9 @@ namespace Emby.Dlna.PlayTo
.ConfigureAwait(false); .ConfigureAwait(false);
if (result == null || result.Document == null) if (result == null || result.Document == null)
return new Tuple<bool, uBaseObject>(false, null); {
return (false, null);
}
var trackUriElem = result.Document.Descendants(uPnpNamespaces.AvTransport + "GetPositionInfoResponse").Select(i => i.Element("TrackURI")).FirstOrDefault(i => i != null); var trackUriElem = result.Document.Descendants(uPnpNamespaces.AvTransport + "GetPositionInfoResponse").Select(i => i.Element("TrackURI")).FirstOrDefault(i => i != null);
var trackUri = trackUriElem == null ? null : trackUriElem.Value; var trackUri = trackUriElem == null ? null : trackUriElem.Value;
@ -684,8 +700,8 @@ namespace Emby.Dlna.PlayTo
var durationElem = result.Document.Descendants(uPnpNamespaces.AvTransport + "GetPositionInfoResponse").Select(i => i.Element("TrackDuration")).FirstOrDefault(i => i != null); var durationElem = result.Document.Descendants(uPnpNamespaces.AvTransport + "GetPositionInfoResponse").Select(i => i.Element("TrackDuration")).FirstOrDefault(i => i != null);
var duration = durationElem == null ? null : durationElem.Value; var duration = durationElem == null ? null : durationElem.Value;
if (!string.IsNullOrWhiteSpace(duration) && if (!string.IsNullOrWhiteSpace(duration)
!string.Equals(duration, "NOT_IMPLEMENTED", StringComparison.OrdinalIgnoreCase)) && !string.Equals(duration, "NOT_IMPLEMENTED", StringComparison.OrdinalIgnoreCase))
{ {
Duration = TimeSpan.Parse(duration, UsCulture); Duration = TimeSpan.Parse(duration, UsCulture);
} }
@ -707,14 +723,14 @@ namespace Emby.Dlna.PlayTo
if (track == null) if (track == null)
{ {
//If track is null, some vendors do this, use GetMediaInfo instead //If track is null, some vendors do this, use GetMediaInfo instead
return new Tuple<bool, uBaseObject>(true, null); return (true, null);
} }
var trackString = (string)track; var trackString = (string)track;
if (string.IsNullOrWhiteSpace(trackString) || string.Equals(trackString, "NOT_IMPLEMENTED", StringComparison.OrdinalIgnoreCase)) if (string.IsNullOrWhiteSpace(trackString) || string.Equals(trackString, "NOT_IMPLEMENTED", StringComparison.OrdinalIgnoreCase))
{ {
return new Tuple<bool, uBaseObject>(true, null); return (true, null);
} }
XElement uPnpResponse; XElement uPnpResponse;
@ -735,7 +751,7 @@ namespace Emby.Dlna.PlayTo
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogError(ex, "Unable to parse xml {0}", trackString); _logger.LogError(ex, "Unable to parse xml {0}", trackString);
return new Tuple<bool, uBaseObject>(true, null); return (true, null);
} }
} }
@ -743,7 +759,7 @@ namespace Emby.Dlna.PlayTo
var uTrack = CreateUBaseObject(e, trackUri); var uTrack = CreateUBaseObject(e, trackUri);
return new Tuple<bool, uBaseObject>(true, uTrack); return (true, uTrack);
} }
private static uBaseObject CreateUBaseObject(XElement container, string trackUri) private static uBaseObject CreateUBaseObject(XElement container, string trackUri)
@ -801,11 +817,9 @@ namespace Emby.Dlna.PlayTo
private async Task<TransportCommands> GetAVProtocolAsync(CancellationToken cancellationToken) private async Task<TransportCommands> GetAVProtocolAsync(CancellationToken cancellationToken)
{ {
var avCommands = AvCommands; if (AvCommands != null)
if (avCommands != null)
{ {
return avCommands; return AvCommands;
} }
if (_disposed) if (_disposed)
@ -825,18 +839,15 @@ namespace Emby.Dlna.PlayTo
var document = await httpClient.GetDataAsync(url, cancellationToken).ConfigureAwait(false); var document = await httpClient.GetDataAsync(url, cancellationToken).ConfigureAwait(false);
avCommands = TransportCommands.Create(document); AvCommands = TransportCommands.Create(document);
AvCommands = avCommands; return AvCommands;
return avCommands;
} }
private async Task<TransportCommands> GetRenderingProtocolAsync(CancellationToken cancellationToken) private async Task<TransportCommands> GetRenderingProtocolAsync(CancellationToken cancellationToken)
{ {
var rendererCommands = RendererCommands; if (RendererCommands != null)
if (rendererCommands != null)
{ {
return rendererCommands; return RendererCommands;
} }
if (_disposed) if (_disposed)
@ -845,7 +856,6 @@ namespace Emby.Dlna.PlayTo
} }
var avService = GetServiceRenderingControl(); var avService = GetServiceRenderingControl();
if (avService == null) if (avService == null)
{ {
throw new ArgumentException("Device AvService is null"); throw new ArgumentException("Device AvService is null");
@ -857,9 +867,8 @@ namespace Emby.Dlna.PlayTo
_logger.LogDebug("Dlna Device.GetRenderingProtocolAsync"); _logger.LogDebug("Dlna Device.GetRenderingProtocolAsync");
var document = await httpClient.GetDataAsync(url, cancellationToken).ConfigureAwait(false); var document = await httpClient.GetDataAsync(url, cancellationToken).ConfigureAwait(false);
rendererCommands = TransportCommands.Create(document); RendererCommands = TransportCommands.Create(document);
RendererCommands = rendererCommands; return RendererCommands;
return rendererCommands;
} }
private string NormalizeUrl(string baseUrl, string url) private string NormalizeUrl(string baseUrl, string url)
@ -871,85 +880,103 @@ namespace Emby.Dlna.PlayTo
} }
if (!url.Contains("/")) if (!url.Contains("/"))
{
url = "/dmr/" + url; url = "/dmr/" + url;
}
if (!url.StartsWith("/")) if (!url.StartsWith("/"))
{
url = "/" + url; url = "/" + url;
}
return baseUrl + url; return baseUrl + url;
} }
private TransportCommands AvCommands private TransportCommands AvCommands { get; set; }
{
get;
set;
}
private TransportCommands RendererCommands private TransportCommands RendererCommands { get; set; }
{
get;
set;
}
public static async Task<Device> CreateuPnpDeviceAsync(Uri url, IHttpClient httpClient, IServerConfigurationManager config, ILogger logger, ITimerFactory timerFactory, CancellationToken cancellationToken) public static async Task<Device> CreateuPnpDeviceAsync(Uri url, IHttpClient httpClient, IServerConfigurationManager config, ILogger logger, CancellationToken cancellationToken)
{ {
var ssdpHttpClient = new SsdpHttpClient(httpClient, config); var ssdpHttpClient = new SsdpHttpClient(httpClient, config);
var document = await ssdpHttpClient.GetDataAsync(url.ToString(), cancellationToken).ConfigureAwait(false); var document = await ssdpHttpClient.GetDataAsync(url.ToString(), cancellationToken).ConfigureAwait(false);
var deviceProperties = new DeviceInfo();
var friendlyNames = new List<string>(); var friendlyNames = new List<string>();
var name = document.Descendants(uPnpNamespaces.ud.GetName("friendlyName")).FirstOrDefault(); var name = document.Descendants(uPnpNamespaces.ud.GetName("friendlyName")).FirstOrDefault();
if (name != null && !string.IsNullOrWhiteSpace(name.Value)) if (name != null && !string.IsNullOrWhiteSpace(name.Value))
{
friendlyNames.Add(name.Value); friendlyNames.Add(name.Value);
}
var room = document.Descendants(uPnpNamespaces.ud.GetName("roomName")).FirstOrDefault(); var room = document.Descendants(uPnpNamespaces.ud.GetName("roomName")).FirstOrDefault();
if (room != null && !string.IsNullOrWhiteSpace(room.Value)) if (room != null && !string.IsNullOrWhiteSpace(room.Value))
{
friendlyNames.Add(room.Value); friendlyNames.Add(room.Value);
}
deviceProperties.Name = string.Join(" ", friendlyNames.ToArray()); var deviceProperties = new DeviceInfo()
{
Name = string.Join(" ", friendlyNames),
BaseUrl = string.Format("http://{0}:{1}", url.Host, url.Port)
};
var model = document.Descendants(uPnpNamespaces.ud.GetName("modelName")).FirstOrDefault(); var model = document.Descendants(uPnpNamespaces.ud.GetName("modelName")).FirstOrDefault();
if (model != null) if (model != null)
{
deviceProperties.ModelName = model.Value; deviceProperties.ModelName = model.Value;
}
var modelNumber = document.Descendants(uPnpNamespaces.ud.GetName("modelNumber")).FirstOrDefault(); var modelNumber = document.Descendants(uPnpNamespaces.ud.GetName("modelNumber")).FirstOrDefault();
if (modelNumber != null) if (modelNumber != null)
{
deviceProperties.ModelNumber = modelNumber.Value; deviceProperties.ModelNumber = modelNumber.Value;
}
var uuid = document.Descendants(uPnpNamespaces.ud.GetName("UDN")).FirstOrDefault(); var uuid = document.Descendants(uPnpNamespaces.ud.GetName("UDN")).FirstOrDefault();
if (uuid != null) if (uuid != null)
{
deviceProperties.UUID = uuid.Value; deviceProperties.UUID = uuid.Value;
}
var manufacturer = document.Descendants(uPnpNamespaces.ud.GetName("manufacturer")).FirstOrDefault(); var manufacturer = document.Descendants(uPnpNamespaces.ud.GetName("manufacturer")).FirstOrDefault();
if (manufacturer != null) if (manufacturer != null)
{
deviceProperties.Manufacturer = manufacturer.Value; deviceProperties.Manufacturer = manufacturer.Value;
}
var manufacturerUrl = document.Descendants(uPnpNamespaces.ud.GetName("manufacturerURL")).FirstOrDefault(); var manufacturerUrl = document.Descendants(uPnpNamespaces.ud.GetName("manufacturerURL")).FirstOrDefault();
if (manufacturerUrl != null) if (manufacturerUrl != null)
{
deviceProperties.ManufacturerUrl = manufacturerUrl.Value; deviceProperties.ManufacturerUrl = manufacturerUrl.Value;
}
var presentationUrl = document.Descendants(uPnpNamespaces.ud.GetName("presentationURL")).FirstOrDefault(); var presentationUrl = document.Descendants(uPnpNamespaces.ud.GetName("presentationURL")).FirstOrDefault();
if (presentationUrl != null) if (presentationUrl != null)
{
deviceProperties.PresentationUrl = presentationUrl.Value; deviceProperties.PresentationUrl = presentationUrl.Value;
}
var modelUrl = document.Descendants(uPnpNamespaces.ud.GetName("modelURL")).FirstOrDefault(); var modelUrl = document.Descendants(uPnpNamespaces.ud.GetName("modelURL")).FirstOrDefault();
if (modelUrl != null) if (modelUrl != null)
{
deviceProperties.ModelUrl = modelUrl.Value; deviceProperties.ModelUrl = modelUrl.Value;
}
var serialNumber = document.Descendants(uPnpNamespaces.ud.GetName("serialNumber")).FirstOrDefault(); var serialNumber = document.Descendants(uPnpNamespaces.ud.GetName("serialNumber")).FirstOrDefault();
if (serialNumber != null) if (serialNumber != null)
{
deviceProperties.SerialNumber = serialNumber.Value; deviceProperties.SerialNumber = serialNumber.Value;
}
var modelDescription = document.Descendants(uPnpNamespaces.ud.GetName("modelDescription")).FirstOrDefault(); var modelDescription = document.Descendants(uPnpNamespaces.ud.GetName("modelDescription")).FirstOrDefault();
if (modelDescription != null) if (modelDescription != null)
{
deviceProperties.ModelDescription = modelDescription.Value; deviceProperties.ModelDescription = modelDescription.Value;
}
deviceProperties.BaseUrl = string.Format("http://{0}:{1}", url.Host, url.Port);
var icon = document.Descendants(uPnpNamespaces.ud.GetName("icon")).FirstOrDefault(); var icon = document.Descendants(uPnpNamespaces.ud.GetName("icon")).FirstOrDefault();
if (icon != null) if (icon != null)
{ {
deviceProperties.Icon = CreateIcon(icon); deviceProperties.Icon = CreateIcon(icon);
@ -958,12 +985,15 @@ namespace Emby.Dlna.PlayTo
foreach (var services in document.Descendants(uPnpNamespaces.ud.GetName("serviceList"))) foreach (var services in document.Descendants(uPnpNamespaces.ud.GetName("serviceList")))
{ {
if (services == null) if (services == null)
{
continue; continue;
}
var servicesList = services.Descendants(uPnpNamespaces.ud.GetName("service")); var servicesList = services.Descendants(uPnpNamespaces.ud.GetName("service"));
if (servicesList == null) if (servicesList == null)
{
continue; continue;
}
foreach (var element in servicesList) foreach (var element in servicesList)
{ {
@ -976,9 +1006,7 @@ namespace Emby.Dlna.PlayTo
} }
} }
var device = new Device(deviceProperties, httpClient, logger, config, timerFactory); return new Device(deviceProperties, httpClient, logger, config);
return device;
} }
#endregion #endregion
@ -1065,13 +1093,10 @@ namespace Emby.Dlna.PlayTo
private void OnPlaybackStart(uBaseObject mediaInfo) private void OnPlaybackStart(uBaseObject mediaInfo)
{ {
if (PlaybackStart != null) PlaybackStart?.Invoke(this, new PlaybackStartEventArgs
{ {
PlaybackStart.Invoke(this, new PlaybackStartEventArgs MediaInfo = mediaInfo
{ });
MediaInfo = mediaInfo
});
}
} }
private void OnPlaybackProgress(uBaseObject mediaInfo) private void OnPlaybackProgress(uBaseObject mediaInfo)
@ -1082,58 +1107,56 @@ namespace Emby.Dlna.PlayTo
return; return;
} }
if (PlaybackProgress != null) PlaybackProgress?.Invoke(this, new PlaybackProgressEventArgs
{ {
PlaybackProgress.Invoke(this, new PlaybackProgressEventArgs MediaInfo = mediaInfo
{ });
MediaInfo = mediaInfo
});
}
} }
private void OnPlaybackStop(uBaseObject mediaInfo) private void OnPlaybackStop(uBaseObject mediaInfo)
{ {
if (PlaybackStopped != null)
PlaybackStopped?.Invoke(this, new PlaybackStoppedEventArgs
{ {
PlaybackStopped.Invoke(this, new PlaybackStoppedEventArgs MediaInfo = mediaInfo
{ });
MediaInfo = mediaInfo
});
}
} }
private void OnMediaChanged(uBaseObject old, uBaseObject newMedia) private void OnMediaChanged(uBaseObject old, uBaseObject newMedia)
{ {
if (MediaChanged != null) MediaChanged?.Invoke(this, new MediaChangedEventArgs
{ {
MediaChanged.Invoke(this, new MediaChangedEventArgs OldMediaInfo = old,
{ NewMediaInfo = newMedia
OldMediaInfo = old, });
NewMediaInfo = newMedia
});
}
} }
#region IDisposable #region IDisposable
bool _disposed; bool _disposed;
public void Dispose() public void Dispose()
{ {
if (!_disposed) Dispose(true);
{ GC.SuppressFinalize(this);
_disposed = true;
DisposeTimer();
}
} }
private void DisposeTimer() protected virtual void Dispose(bool disposing)
{ {
if (_timer != null) if (_disposed)
{ {
_timer.Dispose(); return;
_timer = null;
} }
if (disposing)
{
_timer?.Dispose();
}
_timer = null;
Properties = null;
_disposed = true;
} }
#endregion #endregion

View File

@ -42,30 +42,43 @@ namespace Emby.Dlna.PlayTo
private readonly IDeviceDiscovery _deviceDiscovery; private readonly IDeviceDiscovery _deviceDiscovery;
private readonly string _serverAddress; private readonly string _serverAddress;
private readonly string _accessToken; private readonly string _accessToken;
private readonly DateTime _creationTime;
public bool IsSessionActive => !_disposed && _device != null; public bool IsSessionActive => !_disposed && _device != null;
public bool SupportsMediaControl => IsSessionActive; public bool SupportsMediaControl => IsSessionActive;
public PlayToController(SessionInfo session, ISessionManager sessionManager, ILibraryManager libraryManager, ILogger logger, IDlnaManager dlnaManager, IUserManager userManager, IImageProcessor imageProcessor, string serverAddress, string accessToken, IDeviceDiscovery deviceDiscovery, IUserDataManager userDataManager, ILocalizationManager localization, IMediaSourceManager mediaSourceManager, IConfigurationManager config, IMediaEncoder mediaEncoder) public PlayToController(
SessionInfo session,
ISessionManager sessionManager,
ILibraryManager libraryManager,
ILogger logger,
IDlnaManager dlnaManager,
IUserManager userManager,
IImageProcessor imageProcessor,
string serverAddress,
string accessToken,
IDeviceDiscovery deviceDiscovery,
IUserDataManager userDataManager,
ILocalizationManager localization,
IMediaSourceManager mediaSourceManager,
IConfigurationManager config,
IMediaEncoder mediaEncoder)
{ {
_session = session; _session = session;
_sessionManager = sessionManager; _sessionManager = sessionManager;
_libraryManager = libraryManager; _libraryManager = libraryManager;
_logger = logger;
_dlnaManager = dlnaManager; _dlnaManager = dlnaManager;
_userManager = userManager; _userManager = userManager;
_imageProcessor = imageProcessor; _imageProcessor = imageProcessor;
_serverAddress = serverAddress; _serverAddress = serverAddress;
_accessToken = accessToken;
_deviceDiscovery = deviceDiscovery; _deviceDiscovery = deviceDiscovery;
_userDataManager = userDataManager; _userDataManager = userDataManager;
_localization = localization; _localization = localization;
_mediaSourceManager = mediaSourceManager; _mediaSourceManager = mediaSourceManager;
_config = config; _config = config;
_mediaEncoder = mediaEncoder; _mediaEncoder = mediaEncoder;
_accessToken = accessToken;
_logger = logger;
_creationTime = DateTime.UtcNow;
} }
public void Init(Device device) public void Init(Device device)
@ -374,9 +387,7 @@ namespace Emby.Dlna.PlayTo
return _device.IsPaused ? _device.SetPlay(CancellationToken.None) : _device.SetPause(CancellationToken.None); return _device.IsPaused ? _device.SetPlay(CancellationToken.None) : _device.SetPause(CancellationToken.None);
case PlaystateCommand.Seek: case PlaystateCommand.Seek:
{ return Seek(command.SeekPositionTicks ?? 0);
return Seek(command.SeekPositionTicks ?? 0);
}
case PlaystateCommand.NextTrack: case PlaystateCommand.NextTrack:
return SetPlaylistIndex(_currentPlaylistIndex + 1); return SetPlaylistIndex(_currentPlaylistIndex + 1);
@ -442,8 +453,7 @@ namespace Emby.Dlna.PlayTo
var profile = _dlnaManager.GetProfile(deviceInfo.ToDeviceIdentification()) ?? var profile = _dlnaManager.GetProfile(deviceInfo.ToDeviceIdentification()) ??
_dlnaManager.GetDefaultProfile(); _dlnaManager.GetDefaultProfile();
var hasMediaSources = item as IHasMediaSources; var mediaSources = item is IHasMediaSources
var mediaSources = hasMediaSources != null
? (_mediaSourceManager.GetStaticMediaSources(item, true, user)) ? (_mediaSourceManager.GetStaticMediaSources(item, true, user))
: new List<MediaSourceInfo>(); : new List<MediaSourceInfo>();
@ -452,7 +462,7 @@ namespace Emby.Dlna.PlayTo
playlistItem.StreamUrl = DidlBuilder.NormalizeDlnaMediaUrl(playlistItem.StreamInfo.ToUrl(_serverAddress, _accessToken)); playlistItem.StreamUrl = DidlBuilder.NormalizeDlnaMediaUrl(playlistItem.StreamInfo.ToUrl(_serverAddress, _accessToken));
var itemXml = new DidlBuilder(profile, user, _imageProcessor, _serverAddress, _accessToken, _userDataManager, _localization, _mediaSourceManager, _logger, _libraryManager, _mediaEncoder) var itemXml = new DidlBuilder(profile, user, _imageProcessor, _serverAddress, _accessToken, _userDataManager, _localization, _mediaSourceManager, _logger, _mediaEncoder)
.GetItemDidl(_config.GetDlnaConfiguration(), item, user, null, _session.DeviceId, new Filter(), playlistItem.StreamInfo); .GetItemDidl(_config.GetDlnaConfiguration(), item, user, null, _session.DeviceId, new Filter(), playlistItem.StreamInfo);
playlistItem.Didl = itemXml; playlistItem.Didl = itemXml;

View File

@ -16,7 +16,6 @@ using MediaBrowser.Model.Events;
using MediaBrowser.Model.Globalization; using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.Net; using MediaBrowser.Model.Net;
using MediaBrowser.Model.Session; using MediaBrowser.Model.Session;
using MediaBrowser.Model.Threading;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
namespace Emby.Dlna.PlayTo namespace Emby.Dlna.PlayTo
@ -39,13 +38,12 @@ namespace Emby.Dlna.PlayTo
private readonly IDeviceDiscovery _deviceDiscovery; private readonly IDeviceDiscovery _deviceDiscovery;
private readonly IMediaSourceManager _mediaSourceManager; private readonly IMediaSourceManager _mediaSourceManager;
private readonly IMediaEncoder _mediaEncoder; private readonly IMediaEncoder _mediaEncoder;
private readonly ITimerFactory _timerFactory;
private bool _disposed; private bool _disposed;
private SemaphoreSlim _sessionLock = new SemaphoreSlim(1, 1); private SemaphoreSlim _sessionLock = new SemaphoreSlim(1, 1);
private CancellationTokenSource _disposeCancellationTokenSource = new CancellationTokenSource(); private CancellationTokenSource _disposeCancellationTokenSource = new CancellationTokenSource();
public PlayToManager(ILogger logger, ISessionManager sessionManager, ILibraryManager libraryManager, IUserManager userManager, IDlnaManager dlnaManager, IServerApplicationHost appHost, IImageProcessor imageProcessor, IDeviceDiscovery deviceDiscovery, IHttpClient httpClient, IServerConfigurationManager config, IUserDataManager userDataManager, ILocalizationManager localization, IMediaSourceManager mediaSourceManager, IMediaEncoder mediaEncoder, ITimerFactory timerFactory) public PlayToManager(ILogger logger, ISessionManager sessionManager, ILibraryManager libraryManager, IUserManager userManager, IDlnaManager dlnaManager, IServerApplicationHost appHost, IImageProcessor imageProcessor, IDeviceDiscovery deviceDiscovery, IHttpClient httpClient, IServerConfigurationManager config, IUserDataManager userDataManager, ILocalizationManager localization, IMediaSourceManager mediaSourceManager, IMediaEncoder mediaEncoder)
{ {
_logger = logger; _logger = logger;
_sessionManager = sessionManager; _sessionManager = sessionManager;
@ -61,7 +59,6 @@ namespace Emby.Dlna.PlayTo
_localization = localization; _localization = localization;
_mediaSourceManager = mediaSourceManager; _mediaSourceManager = mediaSourceManager;
_mediaEncoder = mediaEncoder; _mediaEncoder = mediaEncoder;
_timerFactory = timerFactory;
} }
public void Start() public void Start()
@ -168,7 +165,7 @@ namespace Emby.Dlna.PlayTo
if (controller == null) if (controller == null)
{ {
var device = await Device.CreateuPnpDeviceAsync(uri, _httpClient, _config, _logger, _timerFactory, cancellationToken).ConfigureAwait(false); var device = await Device.CreateuPnpDeviceAsync(uri, _httpClient, _config, _logger, cancellationToken).ConfigureAwait(false);
string deviceName = device.Properties.Name; string deviceName = device.Properties.Name;

View File

@ -107,19 +107,19 @@ namespace Emby.Dlna.Server
'&' '&'
}; };
private static readonly string[] s_escapeStringPairs = new string[] private static readonly string[] s_escapeStringPairs = new[]
{ {
"<", "<",
"&lt;", "&lt;",
">", ">",
"&gt;", "&gt;",
"\"", "\"",
"&quot;", "&quot;",
"'", "'",
"&apos;", "&apos;",
"&", "&",
"&amp;" "&amp;"
}; };
private static string GetEscapeSequence(char c) private static string GetEscapeSequence(char c)
{ {
@ -133,7 +133,7 @@ namespace Emby.Dlna.Server
return result; return result;
} }
} }
return c.ToString(); return c.ToString(CultureInfo.InvariantCulture);
} }
/// <summary>Replaces invalid XML characters in a string with their valid XML equivalent.</summary> /// <summary>Replaces invalid XML characters in a string with their valid XML equivalent.</summary>
@ -145,6 +145,7 @@ namespace Emby.Dlna.Server
{ {
return null; return null;
} }
StringBuilder stringBuilder = null; StringBuilder stringBuilder = null;
int length = str.Length; int length = str.Length;
int num = 0; int num = 0;
@ -230,9 +231,9 @@ namespace Emby.Dlna.Server
var serverName = new string(characters); var serverName = new string(characters);
var name = (_profile.FriendlyName ?? string.Empty).Replace("${HostName}", serverName, StringComparison.OrdinalIgnoreCase); var name = _profile.FriendlyName?.Replace("${HostName}", serverName, StringComparison.OrdinalIgnoreCase);
return name; return name ?? string.Empty;
} }
private void AppendIconList(StringBuilder builder) private void AppendIconList(StringBuilder builder)
@ -295,65 +296,62 @@ namespace Emby.Dlna.Server
} }
private IEnumerable<DeviceIcon> GetIcons() private IEnumerable<DeviceIcon> GetIcons()
{ => new[]
var list = new List<DeviceIcon>();
list.Add(new DeviceIcon
{ {
MimeType = "image/png", new DeviceIcon
Depth = "24", {
Width = 240, MimeType = "image/png",
Height = 240, Depth = "24",
Url = "icons/logo240.png" Width = 240,
}); Height = 240,
Url = "icons/logo240.png"
},
list.Add(new DeviceIcon new DeviceIcon
{ {
MimeType = "image/jpeg", MimeType = "image/jpeg",
Depth = "24", Depth = "24",
Width = 240, Width = 240,
Height = 240, Height = 240,
Url = "icons/logo240.jpg" Url = "icons/logo240.jpg"
}); },
list.Add(new DeviceIcon new DeviceIcon
{ {
MimeType = "image/png", MimeType = "image/png",
Depth = "24", Depth = "24",
Width = 120, Width = 120,
Height = 120, Height = 120,
Url = "icons/logo120.png" Url = "icons/logo120.png"
}); },
list.Add(new DeviceIcon new DeviceIcon
{ {
MimeType = "image/jpeg", MimeType = "image/jpeg",
Depth = "24", Depth = "24",
Width = 120, Width = 120,
Height = 120, Height = 120,
Url = "icons/logo120.jpg" Url = "icons/logo120.jpg"
}); },
list.Add(new DeviceIcon new DeviceIcon
{ {
MimeType = "image/png", MimeType = "image/png",
Depth = "24", Depth = "24",
Width = 48, Width = 48,
Height = 48, Height = 48,
Url = "icons/logo48.png" Url = "icons/logo48.png"
}); },
list.Add(new DeviceIcon new DeviceIcon
{ {
MimeType = "image/jpeg", MimeType = "image/jpeg",
Depth = "24", Depth = "24",
Width = 48, Width = 48,
Height = 48, Height = 48,
Url = "icons/logo48.jpg" Url = "icons/logo48.jpg"
}); }
};
return list;
}
private IEnumerable<DeviceService> GetServices() private IEnumerable<DeviceService> GetServices()
{ {

View File

@ -5,7 +5,6 @@ using MediaBrowser.Controller.Configuration;
using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Events; using MediaBrowser.Model.Events;
using MediaBrowser.Model.Net; using MediaBrowser.Model.Net;
using MediaBrowser.Model.Threading;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Rssdp; using Rssdp;
using Rssdp.Infrastructure; using Rssdp.Infrastructure;
@ -48,20 +47,17 @@ namespace Emby.Dlna.Ssdp
private SsdpDeviceLocator _deviceLocator; private SsdpDeviceLocator _deviceLocator;
private readonly ITimerFactory _timerFactory;
private readonly ISocketFactory _socketFactory; private readonly ISocketFactory _socketFactory;
private ISsdpCommunicationsServer _commsServer; private ISsdpCommunicationsServer _commsServer;
public DeviceDiscovery( public DeviceDiscovery(
ILoggerFactory loggerFactory, ILoggerFactory loggerFactory,
IServerConfigurationManager config, IServerConfigurationManager config,
ISocketFactory socketFactory, ISocketFactory socketFactory)
ITimerFactory timerFactory)
{ {
_logger = loggerFactory.CreateLogger(nameof(DeviceDiscovery)); _logger = loggerFactory.CreateLogger(nameof(DeviceDiscovery));
_config = config; _config = config;
_socketFactory = socketFactory; _socketFactory = socketFactory;
_timerFactory = timerFactory;
} }
// Call this method from somewhere in your code to start the search. // Call this method from somewhere in your code to start the search.
@ -78,7 +74,7 @@ namespace Emby.Dlna.Ssdp
{ {
if (_listenerCount > 0 && _deviceLocator == null) if (_listenerCount > 0 && _deviceLocator == null)
{ {
_deviceLocator = new SsdpDeviceLocator(_commsServer, _timerFactory); _deviceLocator = new SsdpDeviceLocator(_commsServer);
// (Optional) Set the filter so we only see notifications for devices we care about // (Optional) Set the filter so we only see notifications for devices we care about
// (can be any search target value i.e device type, uuid value etc - any value that appears in the // (can be any search target value i.e device type, uuid value etc - any value that appears in the

View File

@ -83,8 +83,8 @@ namespace Emby.Drawing
} }
} }
public string[] SupportedInputFormats => public IReadOnlyCollection<string> SupportedInputFormats =>
new string[] new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{ {
"tiff", "tiff",
"tif", "tif",
@ -137,14 +137,14 @@ namespace Emby.Drawing
} }
} }
public ImageFormat[] GetSupportedImageOutputFormats() public IReadOnlyCollection<ImageFormat> GetSupportedImageOutputFormats()
{ => _imageEncoder.SupportedOutputFormats;
return _imageEncoder.SupportedOutputFormats;
} private static readonly HashSet<string> TransparentImageTypes
= new HashSet<string>(StringComparer.OrdinalIgnoreCase) { ".png", ".webp", ".gif" };
private static readonly string[] TransparentImageTypes = new string[] { ".png", ".webp", ".gif" };
public bool SupportsTransparency(string path) public bool SupportsTransparency(string path)
=> TransparentImageTypes.Contains(Path.GetExtension(path).ToLower()); => TransparentImageTypes.Contains(Path.GetExtension(path));
public async Task<(string path, string mimeType, DateTime dateModified)> ProcessImage(ImageProcessingOptions options) public async Task<(string path, string mimeType, DateTime dateModified)> ProcessImage(ImageProcessingOptions options)
{ {
@ -261,15 +261,6 @@ namespace Emby.Drawing
return (cacheFilePath, GetMimeType(outputFormat, cacheFilePath), _fileSystem.GetLastWriteTimeUtc(cacheFilePath)); return (cacheFilePath, GetMimeType(outputFormat, cacheFilePath), _fileSystem.GetLastWriteTimeUtc(cacheFilePath));
} }
catch (ArgumentOutOfRangeException ex)
{
// Decoder failed to decode it
#if DEBUG
_logger.LogError(ex, "Error encoding image");
#endif
// Just spit out the original file if all the options are default
return (originalImagePath, MimeTypes.GetMimeType(originalImagePath), dateModified);
}
catch (Exception ex) catch (Exception ex)
{ {
// If it fails for whatever reason, return the original image // If it fails for whatever reason, return the original image
@ -374,13 +365,13 @@ namespace Emby.Drawing
filename += "v=" + Version; filename += "v=" + Version;
return GetCachePath(ResizedImageCachePath, filename, "." + format.ToString().ToLower()); return GetCachePath(ResizedImageCachePath, filename, "." + format.ToString().ToLowerInvariant());
} }
public ImageDimensions GetImageSize(BaseItem item, ItemImageInfo info) public ImageDimensions GetImageDimensions(BaseItem item, ItemImageInfo info)
=> GetImageSize(item, info, true); => GetImageDimensions(item, info, true);
public ImageDimensions GetImageSize(BaseItem item, ItemImageInfo info, bool updateItem) public ImageDimensions GetImageDimensions(BaseItem item, ItemImageInfo info, bool updateItem)
{ {
int width = info.Width; int width = info.Width;
int height = info.Height; int height = info.Height;
@ -393,7 +384,7 @@ namespace Emby.Drawing
string path = info.Path; string path = info.Path;
_logger.LogInformation("Getting image size for item {ItemType} {Path}", item.GetType().Name, path); _logger.LogInformation("Getting image size for item {ItemType} {Path}", item.GetType().Name, path);
ImageDimensions size = GetImageSize(path); ImageDimensions size = GetImageDimensions(path);
info.Width = size.Width; info.Width = size.Width;
info.Height = size.Height; info.Height = size.Height;
@ -408,7 +399,7 @@ namespace Emby.Drawing
/// <summary> /// <summary>
/// Gets the size of the image. /// Gets the size of the image.
/// </summary> /// </summary>
public ImageDimensions GetImageSize(string path) public ImageDimensions GetImageDimensions(string path)
=> _imageEncoder.GetImageSize(path); => _imageEncoder.GetImageSize(path);
/// <summary> /// <summary>
@ -481,7 +472,7 @@ namespace Emby.Drawing
return (originalImagePath, dateModified); return (originalImagePath, dateModified);
} }
if (!_imageEncoder.SupportedInputFormats.Contains(inputFormat, StringComparer.OrdinalIgnoreCase)) if (!_imageEncoder.SupportedInputFormats.Contains(inputFormat))
{ {
try try
{ {

View File

@ -1,4 +1,5 @@
using System; using System;
using System.Collections.Generic;
using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Drawing;
using MediaBrowser.Model.Drawing; using MediaBrowser.Model.Drawing;
@ -6,15 +7,11 @@ namespace Emby.Drawing
{ {
public class NullImageEncoder : IImageEncoder public class NullImageEncoder : IImageEncoder
{ {
public string[] SupportedInputFormats => public IReadOnlyCollection<string> SupportedInputFormats
new[] => new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "png", "jpeg", "jpg" };
{
"png",
"jpeg",
"jpg"
};
public ImageFormat[] SupportedOutputFormats => new[] { ImageFormat.Jpg, ImageFormat.Png }; public IReadOnlyCollection<ImageFormat> SupportedOutputFormats
=> new HashSet<ImageFormat>() { ImageFormat.Jpg, ImageFormat.Png };
public void CropWhiteSpace(string inputPath, string outputPath) public void CropWhiteSpace(string inputPath, string outputPath)
{ {

View File

@ -121,7 +121,7 @@ namespace IsoMounter
path, path,
Path.GetExtension(path), Path.GetExtension(path),
EnvironmentInfo.OperatingSystem, EnvironmentInfo.OperatingSystem,
ExecutablesAvailable.ToString() ExecutablesAvailable
); );
if (ExecutablesAvailable) if (ExecutablesAvailable)
@ -183,7 +183,7 @@ namespace IsoMounter
_logger.LogInformation( _logger.LogInformation(
"[{0}] Disposing [{1}].", "[{0}] Disposing [{1}].",
Name, Name,
disposing.ToString() disposing
); );
if (disposing) if (disposing)
@ -229,9 +229,8 @@ namespace IsoMounter
var uid = getuid(); var uid = getuid();
_logger.LogDebug( _logger.LogDebug(
"[{0}] Our current UID is [{1}], GetUserId() returned [{2}].", "[{0}] GetUserId() returned [{2}].",
Name, Name,
uid.ToString(),
uid uid
); );

View File

@ -73,11 +73,6 @@ namespace Emby.Notifications
Type = NotificationType.AudioPlayback.ToString() Type = NotificationType.AudioPlayback.ToString()
}, },
new NotificationTypeInfo
{
Type = NotificationType.GamePlayback.ToString()
},
new NotificationTypeInfo new NotificationTypeInfo
{ {
Type = NotificationType.VideoPlayback.ToString() Type = NotificationType.VideoPlayback.ToString()
@ -88,11 +83,6 @@ namespace Emby.Notifications
Type = NotificationType.AudioPlaybackStopped.ToString() Type = NotificationType.AudioPlaybackStopped.ToString()
}, },
new NotificationTypeInfo
{
Type = NotificationType.GamePlaybackStopped.ToString()
},
new NotificationTypeInfo new NotificationTypeInfo
{ {
Type = NotificationType.VideoPlaybackStopped.ToString() Type = NotificationType.VideoPlaybackStopped.ToString()

View File

@ -20,7 +20,6 @@ using MediaBrowser.Model.Events;
using MediaBrowser.Model.Globalization; using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.Notifications; using MediaBrowser.Model.Notifications;
using MediaBrowser.Model.Tasks; using MediaBrowser.Model.Tasks;
using MediaBrowser.Model.Threading;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
namespace Emby.Notifications namespace Emby.Notifications
@ -40,9 +39,8 @@ namespace Emby.Notifications
private readonly ILibraryManager _libraryManager; private readonly ILibraryManager _libraryManager;
private readonly ISessionManager _sessionManager; private readonly ISessionManager _sessionManager;
private readonly IServerApplicationHost _appHost; private readonly IServerApplicationHost _appHost;
private readonly ITimerFactory _timerFactory;
private ITimer LibraryUpdateTimer { get; set; } private Timer LibraryUpdateTimer { get; set; }
private readonly object _libraryChangedSyncLock = new object(); private readonly object _libraryChangedSyncLock = new object();
private readonly IConfigurationManager _config; private readonly IConfigurationManager _config;
@ -52,7 +50,7 @@ namespace Emby.Notifications
private string[] _coreNotificationTypes; private string[] _coreNotificationTypes;
public Notifications(IInstallationManager installationManager, IActivityManager activityManager, ILocalizationManager localization, IUserManager userManager, ILogger logger, ITaskManager taskManager, INotificationManager notificationManager, ILibraryManager libraryManager, ISessionManager sessionManager, IServerApplicationHost appHost, IConfigurationManager config, IDeviceManager deviceManager, ITimerFactory timerFactory) public Notifications(IInstallationManager installationManager, IActivityManager activityManager, ILocalizationManager localization, IUserManager userManager, ILogger logger, ITaskManager taskManager, INotificationManager notificationManager, ILibraryManager libraryManager, ISessionManager sessionManager, IServerApplicationHost appHost, IConfigurationManager config, IDeviceManager deviceManager)
{ {
_installationManager = installationManager; _installationManager = installationManager;
_userManager = userManager; _userManager = userManager;
@ -64,19 +62,20 @@ namespace Emby.Notifications
_appHost = appHost; _appHost = appHost;
_config = config; _config = config;
_deviceManager = deviceManager; _deviceManager = deviceManager;
_timerFactory = timerFactory;
_localization = localization; _localization = localization;
_activityManager = activityManager; _activityManager = activityManager;
_coreNotificationTypes = new CoreNotificationTypes(localization, appHost).GetNotificationTypes().Select(i => i.Type).ToArray(); _coreNotificationTypes = new CoreNotificationTypes(localization, appHost).GetNotificationTypes().Select(i => i.Type).ToArray();
} }
public void Run() public Task RunAsync()
{ {
_libraryManager.ItemAdded += _libraryManager_ItemAdded; _libraryManager.ItemAdded += _libraryManager_ItemAdded;
_appHost.HasPendingRestartChanged += _appHost_HasPendingRestartChanged; _appHost.HasPendingRestartChanged += _appHost_HasPendingRestartChanged;
_appHost.HasUpdateAvailableChanged += _appHost_HasUpdateAvailableChanged; _appHost.HasUpdateAvailableChanged += _appHost_HasUpdateAvailableChanged;
_activityManager.EntryCreated += _activityManager_EntryCreated; _activityManager.EntryCreated += _activityManager_EntryCreated;
return Task.CompletedTask;
} }
private async void _appHost_HasPendingRestartChanged(object sender, EventArgs e) private async void _appHost_HasPendingRestartChanged(object sender, EventArgs e)
@ -157,7 +156,7 @@ namespace Emby.Notifications
{ {
if (LibraryUpdateTimer == null) if (LibraryUpdateTimer == null)
{ {
LibraryUpdateTimer = _timerFactory.Create(LibraryUpdateTimerCallback, null, 5000, LibraryUpdateTimer = new Timer(LibraryUpdateTimerCallback, null, 5000,
Timeout.Infinite); Timeout.Infinite);
} }
else else

View File

@ -181,7 +181,7 @@ namespace Emby.Photos
try try
{ {
var size = _imageProcessor.GetImageSize(item, img, false); var size = _imageProcessor.GetImageDimensions(item, img, false);
if (size.Width > 0 && size.Height > 0) if (size.Width > 0 && size.Height > 0)
{ {

View File

@ -2,6 +2,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks;
using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Plugins; using MediaBrowser.Common.Plugins;
using MediaBrowser.Common.Updates; using MediaBrowser.Common.Updates;
@ -58,7 +59,7 @@ namespace Emby.Server.Implementations.Activity
_deviceManager = deviceManager; _deviceManager = deviceManager;
} }
public void Run() public Task RunAsync()
{ {
_taskManager.TaskCompleted += _taskManager_TaskCompleted; _taskManager.TaskCompleted += _taskManager_TaskCompleted;
@ -90,6 +91,8 @@ namespace Emby.Server.Implementations.Activity
_deviceManager.CameraImageUploaded += _deviceManager_CameraImageUploaded; _deviceManager.CameraImageUploaded += _deviceManager_CameraImageUploaded;
_appHost.ApplicationUpdated += _appHost_ApplicationUpdated; _appHost.ApplicationUpdated += _appHost_ApplicationUpdated;
return Task.CompletedTask;
} }
void _deviceManager_CameraImageUploaded(object sender, GenericEventArgs<CameraImageUploadInfo> e) void _deviceManager_CameraImageUploaded(object sender, GenericEventArgs<CameraImageUploadInfo> e)
@ -207,10 +210,6 @@ namespace Emby.Server.Implementations.Activity
{ {
return NotificationType.AudioPlayback.ToString(); return NotificationType.AudioPlayback.ToString();
} }
if (string.Equals(mediaType, MediaType.Game, StringComparison.OrdinalIgnoreCase))
{
return NotificationType.GamePlayback.ToString();
}
if (string.Equals(mediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase)) if (string.Equals(mediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase))
{ {
return NotificationType.VideoPlayback.ToString(); return NotificationType.VideoPlayback.ToString();
@ -225,10 +224,6 @@ namespace Emby.Server.Implementations.Activity
{ {
return NotificationType.AudioPlaybackStopped.ToString(); return NotificationType.AudioPlaybackStopped.ToString();
} }
if (string.Equals(mediaType, MediaType.Game, StringComparison.OrdinalIgnoreCase))
{
return NotificationType.GamePlaybackStopped.ToString();
}
if (string.Equals(mediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase)) if (string.Equals(mediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase))
{ {
return NotificationType.VideoPlaybackStopped.ToString(); return NotificationType.VideoPlaybackStopped.ToString();

View File

@ -16,12 +16,14 @@ namespace Emby.Server.Implementations.AppBase
string programDataPath, string programDataPath,
string appFolderPath, string appFolderPath,
string logDirectoryPath = null, string logDirectoryPath = null,
string configurationDirectoryPath = null) string configurationDirectoryPath = null,
string cacheDirectoryPath = null)
{ {
ProgramDataPath = programDataPath; ProgramDataPath = programDataPath;
ProgramSystemPath = appFolderPath; ProgramSystemPath = appFolderPath;
LogDirectoryPath = logDirectoryPath; LogDirectoryPath = logDirectoryPath;
ConfigurationDirectoryPath = configurationDirectoryPath; ConfigurationDirectoryPath = configurationDirectoryPath;
CachePath = cacheDirectoryPath;
} }
public string ProgramDataPath { get; private set; } public string ProgramDataPath { get; private set; }

View File

@ -171,16 +171,29 @@ namespace Emby.Server.Implementations.AppBase
private void UpdateCachePath() private void UpdateCachePath()
{ {
string cachePath; string cachePath;
// If the configuration file has no entry (i.e. not set in UI)
if (string.IsNullOrWhiteSpace(CommonConfiguration.CachePath)) if (string.IsNullOrWhiteSpace(CommonConfiguration.CachePath))
{ {
cachePath = null; // If the current live configuration has no entry (i.e. not set on CLI/envvars, during startup)
if (string.IsNullOrWhiteSpace(((BaseApplicationPaths)CommonApplicationPaths).CachePath))
{
// Set cachePath to a default value under ProgramDataPath
cachePath = Path.Combine(((BaseApplicationPaths)CommonApplicationPaths).ProgramDataPath, "cache");
}
else
{
// Set cachePath to the existing live value; will require restart if UI value is removed (but not replaced)
// TODO: Figure out how to re-grab this from the CLI/envvars while running
cachePath = ((BaseApplicationPaths)CommonApplicationPaths).CachePath;
}
} }
else else
{ {
cachePath = Path.Combine(CommonConfiguration.CachePath, "cache"); // Set cachePath to the new UI-set value
cachePath = CommonConfiguration.CachePath;
} }
Logger.LogInformation("Setting cache path to " + cachePath);
((BaseApplicationPaths)CommonApplicationPaths).CachePath = cachePath; ((BaseApplicationPaths)CommonApplicationPaths).CachePath = cachePath;
} }
@ -217,7 +230,7 @@ namespace Emby.Server.Implementations.AppBase
private string GetConfigurationFile(string key) private string GetConfigurationFile(string key)
{ {
return Path.Combine(CommonApplicationPaths.ConfigurationDirectoryPath, key.ToLower() + ".xml"); return Path.Combine(CommonApplicationPaths.ConfigurationDirectoryPath, key.ToLowerInvariant() + ".xml");
} }
public object GetConfiguration(string key) public object GetConfiguration(string key)

View File

@ -12,7 +12,6 @@ using System.Security.Cryptography.X509Certificates;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Emby.Common.Implementations.Serialization;
using Emby.Dlna; using Emby.Dlna;
using Emby.Dlna.Main; using Emby.Dlna.Main;
using Emby.Dlna.Ssdp; using Emby.Dlna.Ssdp;
@ -43,7 +42,6 @@ using Emby.Server.Implementations.ScheduledTasks;
using Emby.Server.Implementations.Security; using Emby.Server.Implementations.Security;
using Emby.Server.Implementations.Serialization; using Emby.Server.Implementations.Serialization;
using Emby.Server.Implementations.Session; using Emby.Server.Implementations.Session;
using Emby.Server.Implementations.Threading;
using Emby.Server.Implementations.TV; using Emby.Server.Implementations.TV;
using Emby.Server.Implementations.Updates; using Emby.Server.Implementations.Updates;
using Emby.Server.Implementations.Xml; using Emby.Server.Implementations.Xml;
@ -99,7 +97,6 @@ using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.Services; using MediaBrowser.Model.Services;
using MediaBrowser.Model.System; using MediaBrowser.Model.System;
using MediaBrowser.Model.Tasks; using MediaBrowser.Model.Tasks;
using MediaBrowser.Model.Threading;
using MediaBrowser.Model.Updates; using MediaBrowser.Model.Updates;
using MediaBrowser.Model.Xml; using MediaBrowser.Model.Xml;
using MediaBrowser.Providers.Chapters; using MediaBrowser.Providers.Chapters;
@ -110,7 +107,6 @@ using MediaBrowser.XbmcMetadata.Providers;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using ServiceStack; using ServiceStack;
using ServiceStack.Text.Jsv; using ServiceStack.Text.Jsv;
using StringExtensions = MediaBrowser.Controller.Extensions.StringExtensions;
using X509Certificate = System.Security.Cryptography.X509Certificates.X509Certificate; using X509Certificate = System.Security.Cryptography.X509Certificates.X509Certificate;
namespace Emby.Server.Implementations namespace Emby.Server.Implementations
@ -141,7 +137,7 @@ namespace Emby.Server.Implementations
return false; return false;
} }
if (StartupOptions.ContainsOption("-service")) if (StartupOptions.IsService)
{ {
return false; return false;
} }
@ -302,7 +298,7 @@ namespace Emby.Server.Implementations
private ILiveTvManager LiveTvManager { get; set; } private ILiveTvManager LiveTvManager { get; set; }
public ILocalizationManager LocalizationManager { get; set; } public LocalizationManager LocalizationManager { get; set; }
private IEncodingManager EncodingManager { get; set; } private IEncodingManager EncodingManager { get; set; }
private IChannelManager ChannelManager { get; set; } private IChannelManager ChannelManager { get; set; }
@ -343,12 +339,11 @@ namespace Emby.Server.Implementations
protected IHttpResultFactory HttpResultFactory { get; private set; } protected IHttpResultFactory HttpResultFactory { get; private set; }
protected IAuthService AuthService { get; private set; } protected IAuthService AuthService { get; private set; }
public StartupOptions StartupOptions { get; private set; } public IStartupOptions StartupOptions { get; private set; }
internal IImageEncoder ImageEncoder { get; private set; } internal IImageEncoder ImageEncoder { get; private set; }
protected IProcessFactory ProcessFactory { get; private set; } protected IProcessFactory ProcessFactory { get; private set; }
protected ITimerFactory TimerFactory { get; private set; }
protected ICryptoProvider CryptographyProvider = new CryptographyProvider(); protected ICryptoProvider CryptographyProvider = new CryptographyProvider();
protected readonly IXmlSerializer XmlSerializer; protected readonly IXmlSerializer XmlSerializer;
@ -364,7 +359,7 @@ namespace Emby.Server.Implementations
/// </summary> /// </summary>
public ApplicationHost(ServerApplicationPaths applicationPaths, public ApplicationHost(ServerApplicationPaths applicationPaths,
ILoggerFactory loggerFactory, ILoggerFactory loggerFactory,
StartupOptions options, IStartupOptions options,
IFileSystem fileSystem, IFileSystem fileSystem,
IEnvironmentInfo environmentInfo, IEnvironmentInfo environmentInfo,
IImageEncoder imageEncoder, IImageEncoder imageEncoder,
@ -646,8 +641,10 @@ namespace Emby.Server.Implementations
/// <summary> /// <summary>
/// Runs the startup tasks. /// Runs the startup tasks.
/// </summary> /// </summary>
public Task RunStartupTasks() public async Task RunStartupTasks()
{ {
Logger.LogInformation("Running startup tasks");
Resolve<ITaskManager>().AddTasks(GetExports<IScheduledTask>(false)); Resolve<ITaskManager>().AddTasks(GetExports<IScheduledTask>(false));
ConfigurationManager.ConfigurationUpdated += OnConfigurationUpdated; ConfigurationManager.ConfigurationUpdated += OnConfigurationUpdated;
@ -666,20 +663,20 @@ namespace Emby.Server.Implementations
Logger.LogInformation("ServerId: {0}", SystemId); Logger.LogInformation("ServerId: {0}", SystemId);
var entryPoints = GetExports<IServerEntryPoint>(); var entryPoints = GetExports<IServerEntryPoint>();
RunEntryPoints(entryPoints, true);
var now = DateTime.UtcNow;
await Task.WhenAll(StartEntryPoints(entryPoints, true));
Logger.LogInformation("Executed all pre-startup entry points in {Elapsed:fff} ms", DateTime.Now - now);
Logger.LogInformation("Core startup complete"); Logger.LogInformation("Core startup complete");
HttpServer.GlobalResponse = null; HttpServer.GlobalResponse = null;
Logger.LogInformation("Post-init migrations complete"); now = DateTime.UtcNow;
await Task.WhenAll(StartEntryPoints(entryPoints, false));
RunEntryPoints(entryPoints, false); Logger.LogInformation("Executed all post-startup entry points in {Elapsed:fff} ms", DateTime.Now - now);
Logger.LogInformation("All entry points have started");
return Task.CompletedTask;
} }
private void RunEntryPoints(IEnumerable<IServerEntryPoint> entryPoints, bool isBeforeStartup) private IEnumerable<Task> StartEntryPoints(IEnumerable<IServerEntryPoint> entryPoints, bool isBeforeStartup)
{ {
foreach (var entryPoint in entryPoints) foreach (var entryPoint in entryPoints)
{ {
@ -688,22 +685,13 @@ namespace Emby.Server.Implementations
continue; continue;
} }
var name = entryPoint.GetType().FullName; Logger.LogDebug("Starting entry point {Type}", entryPoint.GetType());
Logger.LogInformation("Starting entry point {Name}", name);
var now = DateTime.UtcNow; yield return entryPoint.RunAsync();
try
{
entryPoint.Run();
}
catch (Exception ex)
{
Logger.LogError(ex, "Error while running entrypoint {Name}", name);
}
Logger.LogInformation("Entry point completed: {Name}. Duration: {Duration} seconds", name, (DateTime.UtcNow - now).TotalSeconds.ToString(CultureInfo.InvariantCulture), "ImageInfos");
} }
} }
public void Init() public async Task Init()
{ {
HttpPort = ServerConfigurationManager.Configuration.HttpServerPortNumber; HttpPort = ServerConfigurationManager.Configuration.HttpServerPortNumber;
HttpsPort = ServerConfigurationManager.Configuration.HttpsPortNumber; HttpsPort = ServerConfigurationManager.Configuration.HttpsPortNumber;
@ -733,7 +721,7 @@ namespace Emby.Server.Implementations
SetHttpLimit(); SetHttpLimit();
RegisterResources(); await RegisterResources();
FindParts(); FindParts();
} }
@ -748,7 +736,7 @@ namespace Emby.Server.Implementations
/// <summary> /// <summary>
/// Registers resources that classes will depend on /// Registers resources that classes will depend on
/// </summary> /// </summary>
protected void RegisterResources() protected async Task RegisterResources()
{ {
RegisterSingleInstance(ConfigurationManager); RegisterSingleInstance(ConfigurationManager);
RegisterSingleInstance<IApplicationHost>(this); RegisterSingleInstance<IApplicationHost>(this);
@ -780,9 +768,6 @@ namespace Emby.Server.Implementations
ProcessFactory = new ProcessFactory(); ProcessFactory = new ProcessFactory();
RegisterSingleInstance(ProcessFactory); RegisterSingleInstance(ProcessFactory);
TimerFactory = new TimerFactory();
RegisterSingleInstance(TimerFactory);
var streamHelper = CreateStreamHelper(); var streamHelper = CreateStreamHelper();
ApplicationHost.StreamHelper = streamHelper; ApplicationHost.StreamHelper = streamHelper;
RegisterSingleInstance(streamHelper); RegisterSingleInstance(streamHelper);
@ -809,9 +794,9 @@ namespace Emby.Server.Implementations
IAssemblyInfo assemblyInfo = new AssemblyInfo(); IAssemblyInfo assemblyInfo = new AssemblyInfo();
RegisterSingleInstance(assemblyInfo); RegisterSingleInstance(assemblyInfo);
LocalizationManager = new LocalizationManager(ServerConfigurationManager, FileSystemManager, JsonSerializer, LoggerFactory, assemblyInfo, new TextLocalizer()); LocalizationManager = new LocalizationManager(ServerConfigurationManager, FileSystemManager, JsonSerializer, LoggerFactory);
StringExtensions.LocalizationManager = LocalizationManager; await LocalizationManager.LoadAll();
RegisterSingleInstance(LocalizationManager); RegisterSingleInstance<ILocalizationManager>(LocalizationManager);
BlurayExaminer = new BdInfoExaminer(FileSystemManager); BlurayExaminer = new BdInfoExaminer(FileSystemManager);
RegisterSingleInstance(BlurayExaminer); RegisterSingleInstance(BlurayExaminer);
@ -845,7 +830,7 @@ namespace Emby.Server.Implementations
var musicManager = new MusicManager(LibraryManager); var musicManager = new MusicManager(LibraryManager);
RegisterSingleInstance<IMusicManager>(new MusicManager(LibraryManager)); RegisterSingleInstance<IMusicManager>(new MusicManager(LibraryManager));
LibraryMonitor = new LibraryMonitor(LoggerFactory, TaskManager, LibraryManager, ServerConfigurationManager, FileSystemManager, TimerFactory, EnvironmentInfo); LibraryMonitor = new LibraryMonitor(LoggerFactory, TaskManager, LibraryManager, ServerConfigurationManager, FileSystemManager, EnvironmentInfo);
RegisterSingleInstance(LibraryMonitor); RegisterSingleInstance(LibraryMonitor);
RegisterSingleInstance<ISearchEngine>(() => new SearchEngine(LoggerFactory, LibraryManager, UserManager)); RegisterSingleInstance<ISearchEngine>(() => new SearchEngine(LoggerFactory, LibraryManager, UserManager));
@ -877,7 +862,7 @@ namespace Emby.Server.Implementations
DeviceManager = new DeviceManager(AuthenticationRepository, JsonSerializer, LibraryManager, LocalizationManager, UserManager, FileSystemManager, LibraryMonitor, ServerConfigurationManager, LoggerFactory, NetworkManager); DeviceManager = new DeviceManager(AuthenticationRepository, JsonSerializer, LibraryManager, LocalizationManager, UserManager, FileSystemManager, LibraryMonitor, ServerConfigurationManager, LoggerFactory, NetworkManager);
RegisterSingleInstance(DeviceManager); RegisterSingleInstance(DeviceManager);
MediaSourceManager = new MediaSourceManager(ItemRepository, ApplicationPaths, LocalizationManager, UserManager, LibraryManager, LoggerFactory, JsonSerializer, FileSystemManager, UserDataManager, TimerFactory, () => MediaEncoder); MediaSourceManager = new MediaSourceManager(ItemRepository, ApplicationPaths, LocalizationManager, UserManager, LibraryManager, LoggerFactory, JsonSerializer, FileSystemManager, UserDataManager, () => MediaEncoder);
RegisterSingleInstance(MediaSourceManager); RegisterSingleInstance(MediaSourceManager);
SubtitleManager = new SubtitleManager(LoggerFactory, FileSystemManager, LibraryMonitor, MediaSourceManager, LocalizationManager); SubtitleManager = new SubtitleManager(LoggerFactory, FileSystemManager, LibraryMonitor, MediaSourceManager, LocalizationManager);
@ -892,7 +877,7 @@ namespace Emby.Server.Implementations
ChannelManager = new ChannelManager(UserManager, DtoService, LibraryManager, LoggerFactory, ServerConfigurationManager, FileSystemManager, UserDataManager, JsonSerializer, LocalizationManager, HttpClient, ProviderManager); ChannelManager = new ChannelManager(UserManager, DtoService, LibraryManager, LoggerFactory, ServerConfigurationManager, FileSystemManager, UserDataManager, JsonSerializer, LocalizationManager, HttpClient, ProviderManager);
RegisterSingleInstance(ChannelManager); RegisterSingleInstance(ChannelManager);
SessionManager = new SessionManager(UserDataManager, LoggerFactory, LibraryManager, UserManager, musicManager, DtoService, ImageProcessor, JsonSerializer, this, HttpClient, AuthenticationRepository, DeviceManager, MediaSourceManager, TimerFactory); SessionManager = new SessionManager(UserDataManager, LoggerFactory, LibraryManager, UserManager, musicManager, DtoService, ImageProcessor, JsonSerializer, this, HttpClient, AuthenticationRepository, DeviceManager, MediaSourceManager);
RegisterSingleInstance(SessionManager); RegisterSingleInstance(SessionManager);
var dlnaManager = new DlnaManager(XmlSerializer, FileSystemManager, ApplicationPaths, LoggerFactory, JsonSerializer, this, assemblyInfo); var dlnaManager = new DlnaManager(XmlSerializer, FileSystemManager, ApplicationPaths, LoggerFactory, JsonSerializer, this, assemblyInfo);
@ -904,7 +889,7 @@ namespace Emby.Server.Implementations
PlaylistManager = new PlaylistManager(LibraryManager, FileSystemManager, LibraryMonitor, LoggerFactory, UserManager, ProviderManager); PlaylistManager = new PlaylistManager(LibraryManager, FileSystemManager, LibraryMonitor, LoggerFactory, UserManager, ProviderManager);
RegisterSingleInstance(PlaylistManager); RegisterSingleInstance(PlaylistManager);
LiveTvManager = new LiveTvManager(this, ServerConfigurationManager, LoggerFactory, ItemRepository, ImageProcessor, UserDataManager, DtoService, UserManager, LibraryManager, TaskManager, LocalizationManager, JsonSerializer, ProviderManager, FileSystemManager, () => ChannelManager); LiveTvManager = new LiveTvManager(this, ServerConfigurationManager, LoggerFactory, ItemRepository, ImageProcessor, UserDataManager, DtoService, UserManager, LibraryManager, TaskManager, LocalizationManager, JsonSerializer, FileSystemManager, () => ChannelManager);
RegisterSingleInstance(LiveTvManager); RegisterSingleInstance(LiveTvManager);
UserViewManager = new UserViewManager(LibraryManager, LocalizationManager, UserManager, ChannelManager, LiveTvManager, ServerConfigurationManager); UserViewManager = new UserViewManager(LibraryManager, LocalizationManager, UserManager, ChannelManager, LiveTvManager, ServerConfigurationManager);
@ -913,7 +898,7 @@ namespace Emby.Server.Implementations
NotificationManager = new NotificationManager(LoggerFactory, UserManager, ServerConfigurationManager); NotificationManager = new NotificationManager(LoggerFactory, UserManager, ServerConfigurationManager);
RegisterSingleInstance(NotificationManager); RegisterSingleInstance(NotificationManager);
RegisterSingleInstance<IDeviceDiscovery>(new DeviceDiscovery(LoggerFactory, ServerConfigurationManager, SocketFactory, TimerFactory)); RegisterSingleInstance<IDeviceDiscovery>(new DeviceDiscovery(LoggerFactory, ServerConfigurationManager, SocketFactory));
ChapterManager = new ChapterManager(LibraryManager, LoggerFactory, ServerConfigurationManager, ItemRepository); ChapterManager = new ChapterManager(LibraryManager, LoggerFactory, ServerConfigurationManager, ItemRepository);
RegisterSingleInstance(ChapterManager); RegisterSingleInstance(ChapterManager);
@ -1671,7 +1656,6 @@ namespace Emby.Server.Implementations
var minRequiredVersions = new Dictionary<string, Version>(StringComparer.OrdinalIgnoreCase) var minRequiredVersions = new Dictionary<string, Version>(StringComparer.OrdinalIgnoreCase)
{ {
{ "GameBrowser.dll", new Version(3, 1) },
{ "moviethemesongs.dll", new Version(1, 6) }, { "moviethemesongs.dll", new Version(1, 6) },
{ "themesongs.dll", new Version(1, 2) } { "themesongs.dll", new Version(1, 2) }
}; };
@ -1747,7 +1731,7 @@ namespace Emby.Server.Implementations
EncoderLocationType = MediaEncoder.EncoderLocationType, EncoderLocationType = MediaEncoder.EncoderLocationType,
SystemArchitecture = EnvironmentInfo.SystemArchitecture, SystemArchitecture = EnvironmentInfo.SystemArchitecture,
SystemUpdateLevel = SystemUpdateLevel, SystemUpdateLevel = SystemUpdateLevel,
PackageName = StartupOptions.GetOption("-package") PackageName = StartupOptions.PackageName
}; };
} }

View File

@ -70,7 +70,8 @@ namespace Emby.Server.Implementations.Collections
return null; return null;
}) })
.Where(i => i != null) .Where(i => i != null)
.DistinctBy(i => i.Id) .GroupBy(x => x.Id)
.Select(x => x.First())
.OrderBy(i => Guid.NewGuid()) .OrderBy(i => Guid.NewGuid())
.ToList(); .ToList();
} }

View File

@ -353,7 +353,7 @@ namespace Emby.Server.Implementations.Collections
_logger = logger; _logger = logger;
} }
public async void Run() public async Task RunAsync()
{ {
if (!_config.Configuration.CollectionsUpgraded && _config.Configuration.IsStartupWizardCompleted) if (!_config.Configuration.CollectionsUpgraded && _config.Configuration.IsStartupWizardCompleted)
{ {

View File

@ -1239,10 +1239,6 @@ namespace Emby.Server.Implementations.Data
{ {
return false; return false;
} }
else if (type == typeof(GameGenre))
{
return false;
}
else if (type == typeof(Genre)) else if (type == typeof(Genre))
{ {
return false; return false;
@ -3905,7 +3901,7 @@ namespace Emby.Server.Implementations.Data
// lowercase this because SortName is stored as lowercase // lowercase this because SortName is stored as lowercase
if (statement != null) if (statement != null)
{ {
statement.TryBind("@NameStartsWithOrGreater", query.NameStartsWithOrGreater.ToLower()); statement.TryBind("@NameStartsWithOrGreater", query.NameStartsWithOrGreater.ToLowerInvariant());
} }
} }
if (!string.IsNullOrWhiteSpace(query.NameLessThan)) if (!string.IsNullOrWhiteSpace(query.NameLessThan))
@ -3914,7 +3910,7 @@ namespace Emby.Server.Implementations.Data
// lowercase this because SortName is stored as lowercase // lowercase this because SortName is stored as lowercase
if (statement != null) if (statement != null)
{ {
statement.TryBind("@NameLessThan", query.NameLessThan.ToLower()); statement.TryBind("@NameLessThan", query.NameLessThan.ToLowerInvariant());
} }
} }
@ -4789,10 +4785,6 @@ namespace Emby.Server.Implementations.Data
{ {
list.Add(typeof(MusicGenre).Name); list.Add(typeof(MusicGenre).Name);
} }
if (IsTypeInQuery(typeof(GameGenre).Name, query))
{
list.Add(typeof(GameGenre).Name);
}
if (IsTypeInQuery(typeof(MusicArtist).Name, query)) if (IsTypeInQuery(typeof(MusicArtist).Name, query))
{ {
list.Add(typeof(MusicArtist).Name); list.Add(typeof(MusicArtist).Name);
@ -4822,7 +4814,7 @@ namespace Emby.Server.Implementations.Data
return value; return value;
} }
return value.RemoveDiacritics().ToLower(); return value.RemoveDiacritics().ToLowerInvariant();
} }
private bool EnableGroupByPresentationUniqueKey(InternalItemsQuery query) private bool EnableGroupByPresentationUniqueKey(InternalItemsQuery query)
@ -4891,9 +4883,6 @@ namespace Emby.Server.Implementations.Data
typeof(Book), typeof(Book),
typeof(CollectionFolder), typeof(CollectionFolder),
typeof(Folder), typeof(Folder),
typeof(Game),
typeof(GameGenre),
typeof(GameSystem),
typeof(Genre), typeof(Genre),
typeof(Person), typeof(Person),
typeof(Photo), typeof(Photo),
@ -5251,11 +5240,6 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
return GetItemValues(query, new[] { 2 }, typeof(Genre).FullName); return GetItemValues(query, new[] { 2 }, typeof(Genre).FullName);
} }
public QueryResult<Tuple<BaseItem, ItemCounts>> GetGameGenres(InternalItemsQuery query)
{
return GetItemValues(query, new[] { 2 }, typeof(GameGenre).FullName);
}
public QueryResult<Tuple<BaseItem, ItemCounts>> GetMusicGenres(InternalItemsQuery query) public QueryResult<Tuple<BaseItem, ItemCounts>> GetMusicGenres(InternalItemsQuery query)
{ {
return GetItemValues(query, new[] { 2 }, typeof(MusicGenre).FullName); return GetItemValues(query, new[] { 2 }, typeof(MusicGenre).FullName);
@ -5276,14 +5260,9 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
return GetItemValueNames(new[] { 2 }, new List<string> { "Audio", "MusicVideo", "MusicAlbum", "MusicArtist" }, new List<string>()); return GetItemValueNames(new[] { 2 }, new List<string> { "Audio", "MusicVideo", "MusicAlbum", "MusicArtist" }, new List<string>());
} }
public List<string> GetGameGenreNames()
{
return GetItemValueNames(new[] { 2 }, new List<string> { "Game" }, new List<string>());
}
public List<string> GetGenreNames() public List<string> GetGenreNames()
{ {
return GetItemValueNames(new[] { 2 }, new List<string>(), new List<string> { "Audio", "MusicVideo", "MusicAlbum", "MusicArtist", "Game", "GameSystem" }); return GetItemValueNames(new[] { 2 }, new List<string>(), new List<string> { "Audio", "MusicVideo", "MusicAlbum", "MusicArtist" });
} }
private List<string> GetItemValueNames(int[] itemValueTypes, List<string> withItemTypes, List<string> excludeItemTypes) private List<string> GetItemValueNames(int[] itemValueTypes, List<string> withItemTypes, List<string> excludeItemTypes)
@ -5652,10 +5631,6 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
{ {
counts.SongCount = value; counts.SongCount = value;
} }
else if (string.Equals(typeName, typeof(Game).FullName, StringComparison.OrdinalIgnoreCase))
{
counts.GameCount = value;
}
else if (string.Equals(typeName, typeof(Trailer).FullName, StringComparison.OrdinalIgnoreCase)) else if (string.Equals(typeName, typeof(Trailer).FullName, StringComparison.OrdinalIgnoreCase))
{ {
counts.TrailerCount = value; counts.TrailerCount = value;

View File

@ -425,7 +425,7 @@ namespace Emby.Server.Implementations.Devices
_logger = logger; _logger = logger;
} }
public async void Run() public async Task RunAsync()
{ {
if (!_config.Configuration.CameraUploadUpgraded && _config.Configuration.IsStartupWizardCompleted) if (!_config.Configuration.CameraUploadUpgraded && _config.Configuration.IsStartupWizardCompleted)
{ {

View File

@ -105,7 +105,7 @@ namespace Emby.Server.Implementations.Diagnostics
{ {
return _process.WaitForExit(timeMs); return _process.WaitForExit(timeMs);
} }
public Task<bool> WaitForExitAsync(int timeMs) public Task<bool> WaitForExitAsync(int timeMs)
{ {
//Note: For this function to work correctly, the option EnableRisingEvents needs to be set to true. //Note: For this function to work correctly, the option EnableRisingEvents needs to be set to true.

View File

@ -374,10 +374,6 @@ namespace Emby.Server.Implementations.Dto
dto.MusicVideoCount = taggedItems.Count(i => i is MusicVideo); dto.MusicVideoCount = taggedItems.Count(i => i is MusicVideo);
dto.SongCount = taggedItems.Count(i => i is Audio); dto.SongCount = taggedItems.Count(i => i is Audio);
} }
else if (item is GameGenre)
{
dto.GameCount = taggedItems.Count(i => i is Game);
}
else else
{ {
// This populates them all and covers Genre, Person, Studio, Year // This populates them all and covers Genre, Person, Studio, Year
@ -385,7 +381,6 @@ namespace Emby.Server.Implementations.Dto
dto.ArtistCount = taggedItems.Count(i => i is MusicArtist); dto.ArtistCount = taggedItems.Count(i => i is MusicArtist);
dto.AlbumCount = taggedItems.Count(i => i is MusicAlbum); dto.AlbumCount = taggedItems.Count(i => i is MusicAlbum);
dto.EpisodeCount = taggedItems.Count(i => i is Episode); dto.EpisodeCount = taggedItems.Count(i => i is Episode);
dto.GameCount = taggedItems.Count(i => i is Game);
dto.MovieCount = taggedItems.Count(i => i is Movie); dto.MovieCount = taggedItems.Count(i => i is Movie);
dto.TrailerCount = taggedItems.Count(i => i is Trailer); dto.TrailerCount = taggedItems.Count(i => i is Trailer);
dto.MusicVideoCount = taggedItems.Count(i => i is MusicVideo); dto.MusicVideoCount = taggedItems.Count(i => i is MusicVideo);
@ -532,17 +527,6 @@ namespace Emby.Server.Implementations.Dto
dto.Album = item.Album; dto.Album = item.Album;
} }
private static void SetGameProperties(BaseItemDto dto, Game item)
{
dto.GameSystem = item.GameSystem;
dto.MultiPartGameFiles = item.MultiPartGameFiles;
}
private static void SetGameSystemProperties(BaseItemDto dto, GameSystem item)
{
dto.GameSystem = item.GameSystemName;
}
private string[] GetImageTags(BaseItem item, List<ItemImageInfo> images) private string[] GetImageTags(BaseItem item, List<ItemImageInfo> images)
{ {
return images return images
@ -636,7 +620,8 @@ namespace Emby.Server.Implementations.Dto
} }
}).Where(i => i != null) }).Where(i => i != null)
.DistinctBy(i => i.Name, StringComparer.OrdinalIgnoreCase) .GroupBy(i => i.Name, StringComparer.OrdinalIgnoreCase)
.Select(x => x.First())
.ToDictionary(i => i.Name, StringComparer.OrdinalIgnoreCase); .ToDictionary(i => i.Name, StringComparer.OrdinalIgnoreCase);
for (var i = 0; i < people.Count; i++) for (var i = 0; i < people.Count; i++)
@ -698,11 +683,6 @@ namespace Emby.Server.Implementations.Dto
return _libraryManager.GetMusicGenreId(name); return _libraryManager.GetMusicGenreId(name);
} }
if (owner is Game || owner is GameSystem)
{
return _libraryManager.GetGameGenreId(name);
}
return _libraryManager.GetGenreId(name); return _libraryManager.GetGenreId(name);
} }
@ -1206,20 +1186,6 @@ namespace Emby.Server.Implementations.Dto
} }
} }
var game = item as Game;
if (game != null)
{
SetGameProperties(dto, game);
}
var gameSystem = item as GameSystem;
if (gameSystem != null)
{
SetGameSystemProperties(dto, gameSystem);
}
var musicVideo = item as MusicVideo; var musicVideo = item as MusicVideo;
if (musicVideo != null) if (musicVideo != null)
{ {
@ -1452,7 +1418,7 @@ namespace Emby.Server.Implementations.Dto
try try
{ {
size = _imageProcessor.GetImageSize(item, imageInfo); size = _imageProcessor.GetImageDimensions(item, imageInfo);
if (size.Width <= 0 || size.Height <= 0) if (size.Width <= 0 || size.Height <= 0)
{ {

View File

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Emby.Naming\Emby.Naming.csproj" /> <ProjectReference Include="..\Emby.Naming\Emby.Naming.csproj" />
@ -25,8 +25,7 @@
<PackageReference Include="ServiceStack.Text.Core" Version="5.4.0" /> <PackageReference Include="ServiceStack.Text.Core" Version="5.4.0" />
<PackageReference Include="sharpcompress" Version="0.22.0" /> <PackageReference Include="sharpcompress" Version="0.22.0" />
<PackageReference Include="SimpleInjector" Version="4.4.2" /> <PackageReference Include="SimpleInjector" Version="4.4.2" />
<PackageReference Include="SQLitePCL.pretty.core" Version="1.1.8" /> <PackageReference Include="SQLitePCL.pretty.netstandard" Version="1.0.0" />
<PackageReference Include="SQLitePCLRaw.core" Version="1.1.11" />
<PackageReference Include="UTF.Unknown" Version="1.0.0-beta1" /> <PackageReference Include="UTF.Unknown" Version="1.0.0-beta1" />
</ItemGroup> </ItemGroup>
@ -43,7 +42,7 @@
<EmbeddedResource Include="Localization\iso6392.txt" /> <EmbeddedResource Include="Localization\iso6392.txt" />
<EmbeddedResource Include="Localization\countries.json" /> <EmbeddedResource Include="Localization\countries.json" />
<EmbeddedResource Include="Localization\Core\*.json" /> <EmbeddedResource Include="Localization\Core\*.json" />
<EmbeddedResource Include="Localization\Ratings\*.txt" /> <EmbeddedResource Include="Localization\Ratings\*.csv" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -9,7 +9,6 @@ using MediaBrowser.Controller.Plugins;
using MediaBrowser.Controller.Session; using MediaBrowser.Controller.Session;
using MediaBrowser.Model.LiveTv; using MediaBrowser.Model.LiveTv;
using MediaBrowser.Model.Tasks; using MediaBrowser.Model.Tasks;
using MediaBrowser.Model.Threading;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.EntryPoints namespace Emby.Server.Implementations.EntryPoints
@ -22,11 +21,10 @@ namespace Emby.Server.Implementations.EntryPoints
private readonly ISessionManager _sessionManager; private readonly ISessionManager _sessionManager;
private readonly IServerConfigurationManager _config; private readonly IServerConfigurationManager _config;
private readonly ILiveTvManager _liveTvManager; private readonly ILiveTvManager _liveTvManager;
private readonly ITimerFactory _timerFactory;
private ITimer _timer; private Timer _timer;
public AutomaticRestartEntryPoint(IServerApplicationHost appHost, ILogger logger, ITaskManager iTaskManager, ISessionManager sessionManager, IServerConfigurationManager config, ILiveTvManager liveTvManager, ITimerFactory timerFactory) public AutomaticRestartEntryPoint(IServerApplicationHost appHost, ILogger logger, ITaskManager iTaskManager, ISessionManager sessionManager, IServerConfigurationManager config, ILiveTvManager liveTvManager)
{ {
_appHost = appHost; _appHost = appHost;
_logger = logger; _logger = logger;
@ -34,15 +32,16 @@ namespace Emby.Server.Implementations.EntryPoints
_sessionManager = sessionManager; _sessionManager = sessionManager;
_config = config; _config = config;
_liveTvManager = liveTvManager; _liveTvManager = liveTvManager;
_timerFactory = timerFactory;
} }
public void Run() public Task RunAsync()
{ {
if (_appHost.CanSelfRestart) if (_appHost.CanSelfRestart)
{ {
_appHost.HasPendingRestartChanged += _appHost_HasPendingRestartChanged; _appHost.HasPendingRestartChanged += _appHost_HasPendingRestartChanged;
} }
return Task.CompletedTask;
} }
void _appHost_HasPendingRestartChanged(object sender, EventArgs e) void _appHost_HasPendingRestartChanged(object sender, EventArgs e)
@ -51,7 +50,7 @@ namespace Emby.Server.Implementations.EntryPoints
if (_appHost.HasPendingRestart) if (_appHost.HasPendingRestart)
{ {
_timer = _timerFactory.Create(TimerCallback, null, TimeSpan.FromMinutes(15), TimeSpan.FromMinutes(15)); _timer = new Timer(TimerCallback, null, TimeSpan.FromMinutes(15), TimeSpan.FromMinutes(15));
} }
} }

View File

@ -10,7 +10,6 @@ using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Plugins; using MediaBrowser.Controller.Plugins;
using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Events; using MediaBrowser.Model.Events;
using MediaBrowser.Model.Threading;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Mono.Nat; using Mono.Nat;
@ -24,19 +23,17 @@ namespace Emby.Server.Implementations.EntryPoints
private readonly IServerConfigurationManager _config; private readonly IServerConfigurationManager _config;
private readonly IDeviceDiscovery _deviceDiscovery; private readonly IDeviceDiscovery _deviceDiscovery;
private ITimer _timer; private Timer _timer;
private readonly ITimerFactory _timerFactory;
private NatManager _natManager; private NatManager _natManager;
public ExternalPortForwarding(ILoggerFactory loggerFactory, IServerApplicationHost appHost, IServerConfigurationManager config, IDeviceDiscovery deviceDiscovery, IHttpClient httpClient, ITimerFactory timerFactory) public ExternalPortForwarding(ILoggerFactory loggerFactory, IServerApplicationHost appHost, IServerConfigurationManager config, IDeviceDiscovery deviceDiscovery, IHttpClient httpClient)
{ {
_logger = loggerFactory.CreateLogger("PortMapper"); _logger = loggerFactory.CreateLogger("PortMapper");
_appHost = appHost; _appHost = appHost;
_config = config; _config = config;
_deviceDiscovery = deviceDiscovery; _deviceDiscovery = deviceDiscovery;
_httpClient = httpClient; _httpClient = httpClient;
_timerFactory = timerFactory;
_config.ConfigurationUpdated += _config_ConfigurationUpdated1; _config.ConfigurationUpdated += _config_ConfigurationUpdated1;
} }
@ -61,17 +58,17 @@ namespace Emby.Server.Implementations.EntryPoints
return string.Join("|", values.ToArray()); return string.Join("|", values.ToArray());
} }
void _config_ConfigurationUpdated(object sender, EventArgs e) private async void _config_ConfigurationUpdated(object sender, EventArgs e)
{ {
if (!string.Equals(_lastConfigIdentifier, GetConfigIdentifier(), StringComparison.OrdinalIgnoreCase)) if (!string.Equals(_lastConfigIdentifier, GetConfigIdentifier(), StringComparison.OrdinalIgnoreCase))
{ {
DisposeNat(); DisposeNat();
Run(); await RunAsync();
} }
} }
public void Run() public Task RunAsync()
{ {
if (_config.Configuration.EnableUPnP && _config.Configuration.EnableRemoteAccess) if (_config.Configuration.EnableUPnP && _config.Configuration.EnableRemoteAccess)
{ {
@ -80,6 +77,8 @@ namespace Emby.Server.Implementations.EntryPoints
_config.ConfigurationUpdated -= _config_ConfigurationUpdated; _config.ConfigurationUpdated -= _config_ConfigurationUpdated;
_config.ConfigurationUpdated += _config_ConfigurationUpdated; _config.ConfigurationUpdated += _config_ConfigurationUpdated;
return Task.CompletedTask;
} }
private void Start() private void Start()
@ -92,7 +91,7 @@ namespace Emby.Server.Implementations.EntryPoints
_natManager.StartDiscovery(); _natManager.StartDiscovery();
} }
_timer = _timerFactory.Create(ClearCreatedRules, null, TimeSpan.FromMinutes(10), TimeSpan.FromMinutes(10)); _timer = new Timer(ClearCreatedRules, null, TimeSpan.FromMinutes(10), TimeSpan.FromMinutes(10));
_deviceDiscovery.DeviceDiscovered += _deviceDiscovery_DeviceDiscovered; _deviceDiscovery.DeviceDiscovered += _deviceDiscovery_DeviceDiscovered;

View File

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks;
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;
@ -13,7 +14,6 @@ using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Events; using MediaBrowser.Model.Events;
using MediaBrowser.Model.Extensions; using MediaBrowser.Model.Extensions;
using MediaBrowser.Model.Threading;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.EntryPoints namespace Emby.Server.Implementations.EntryPoints
@ -28,7 +28,6 @@ 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 _logger;
private readonly ITimerFactory _timerFactory;
/// <summary> /// <summary>
/// The _library changed sync lock /// The _library changed sync lock
@ -46,7 +45,7 @@ namespace Emby.Server.Implementations.EntryPoints
/// Gets or sets the library update timer. /// Gets or sets the library update timer.
/// </summary> /// </summary>
/// <value>The library update timer.</value> /// <value>The library update timer.</value>
private ITimer LibraryUpdateTimer { get; set; } private Timer LibraryUpdateTimer { get; set; }
/// <summary> /// <summary>
/// The library update duration /// The library update duration
@ -55,17 +54,16 @@ namespace Emby.Server.Implementations.EntryPoints
private readonly IProviderManager _providerManager; private readonly IProviderManager _providerManager;
public LibraryChangedNotifier(ILibraryManager libraryManager, ISessionManager sessionManager, IUserManager userManager, ILogger logger, ITimerFactory timerFactory, IProviderManager providerManager) public LibraryChangedNotifier(ILibraryManager libraryManager, ISessionManager sessionManager, IUserManager userManager, ILogger logger, IProviderManager providerManager)
{ {
_libraryManager = libraryManager; _libraryManager = libraryManager;
_sessionManager = sessionManager; _sessionManager = sessionManager;
_userManager = userManager; _userManager = userManager;
_logger = logger; _logger = logger;
_timerFactory = timerFactory;
_providerManager = providerManager; _providerManager = providerManager;
} }
public void Run() public Task RunAsync()
{ {
_libraryManager.ItemAdded += libraryManager_ItemAdded; _libraryManager.ItemAdded += libraryManager_ItemAdded;
_libraryManager.ItemUpdated += libraryManager_ItemUpdated; _libraryManager.ItemUpdated += libraryManager_ItemUpdated;
@ -74,6 +72,8 @@ namespace Emby.Server.Implementations.EntryPoints
_providerManager.RefreshCompleted += _providerManager_RefreshCompleted; _providerManager.RefreshCompleted += _providerManager_RefreshCompleted;
_providerManager.RefreshStarted += _providerManager_RefreshStarted; _providerManager.RefreshStarted += _providerManager_RefreshStarted;
_providerManager.RefreshProgress += _providerManager_RefreshProgress; _providerManager.RefreshProgress += _providerManager_RefreshProgress;
return Task.CompletedTask;
} }
private Dictionary<Guid, DateTime> _lastProgressMessageTimes = new Dictionary<Guid, DateTime>(); private Dictionary<Guid, DateTime> _lastProgressMessageTimes = new Dictionary<Guid, DateTime>();
@ -188,7 +188,7 @@ namespace Emby.Server.Implementations.EntryPoints
{ {
if (LibraryUpdateTimer == null) if (LibraryUpdateTimer == null)
{ {
LibraryUpdateTimer = _timerFactory.Create(LibraryUpdateTimerCallback, null, LibraryUpdateDuration, LibraryUpdateTimer = new Timer(LibraryUpdateTimerCallback, null, LibraryUpdateDuration,
Timeout.Infinite); Timeout.Infinite);
} }
else else
@ -222,7 +222,7 @@ namespace Emby.Server.Implementations.EntryPoints
{ {
if (LibraryUpdateTimer == null) if (LibraryUpdateTimer == null)
{ {
LibraryUpdateTimer = _timerFactory.Create(LibraryUpdateTimerCallback, null, LibraryUpdateDuration, LibraryUpdateTimer = new Timer(LibraryUpdateTimerCallback, null, LibraryUpdateDuration,
Timeout.Infinite); Timeout.Infinite);
} }
else else
@ -250,7 +250,7 @@ namespace Emby.Server.Implementations.EntryPoints
{ {
if (LibraryUpdateTimer == null) if (LibraryUpdateTimer == null)
{ {
LibraryUpdateTimer = _timerFactory.Create(LibraryUpdateTimerCallback, null, LibraryUpdateDuration, LibraryUpdateTimer = new Timer(LibraryUpdateTimerCallback, null, LibraryUpdateDuration,
Timeout.Infinite); Timeout.Infinite);
} }
else else
@ -277,14 +277,21 @@ namespace Emby.Server.Implementations.EntryPoints
lock (_libraryChangedSyncLock) lock (_libraryChangedSyncLock)
{ {
// Remove dupes in case some were saved multiple times // Remove dupes in case some were saved multiple times
var foldersAddedTo = _foldersAddedTo.DistinctBy(i => i.Id).ToList(); var foldersAddedTo = _foldersAddedTo
.GroupBy(x => x.Id)
.Select(x => x.First())
.ToList();
var foldersRemovedFrom = _foldersRemovedFrom.DistinctBy(i => i.Id).ToList(); var foldersRemovedFrom = _foldersRemovedFrom
.GroupBy(x => x.Id)
.Select(x => x.First())
.ToList();
var itemsUpdated = _itemsUpdated var itemsUpdated = _itemsUpdated
.Where(i => !_itemsAdded.Contains(i)) .Where(i => !_itemsAdded.Contains(i))
.DistinctBy(i => i.Id) .GroupBy(x => x.Id)
.ToList(); .Select(x => x.First())
.ToList();
SendChangeNotifications(_itemsAdded.ToList(), itemsUpdated, _itemsRemoved.ToList(), foldersAddedTo, foldersRemovedFrom, CancellationToken.None); SendChangeNotifications(_itemsAdded.ToList(), itemsUpdated, _itemsRemoved.ToList(), foldersAddedTo, foldersRemovedFrom, CancellationToken.None);

View File

@ -1,6 +1,7 @@
using System; using System;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Controller.Plugins; using MediaBrowser.Controller.Plugins;
@ -24,12 +25,14 @@ namespace Emby.Server.Implementations.EntryPoints
_liveTvManager = liveTvManager; _liveTvManager = liveTvManager;
} }
public void Run() public Task RunAsync()
{ {
_liveTvManager.TimerCancelled += _liveTvManager_TimerCancelled; _liveTvManager.TimerCancelled += _liveTvManager_TimerCancelled;
_liveTvManager.SeriesTimerCancelled += _liveTvManager_SeriesTimerCancelled; _liveTvManager.SeriesTimerCancelled += _liveTvManager_SeriesTimerCancelled;
_liveTvManager.TimerCreated += _liveTvManager_TimerCreated; _liveTvManager.TimerCreated += _liveTvManager_TimerCreated;
_liveTvManager.SeriesTimerCreated += _liveTvManager_SeriesTimerCreated; _liveTvManager.SeriesTimerCreated += _liveTvManager_SeriesTimerCreated;
return Task.CompletedTask;
} }
private void _liveTvManager_SeriesTimerCreated(object sender, MediaBrowser.Model.Events.GenericEventArgs<TimerEventInfo> e) private void _liveTvManager_SeriesTimerCreated(object sender, MediaBrowser.Model.Events.GenericEventArgs<TimerEventInfo> e)

View File

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading; using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.Plugins; using MediaBrowser.Common.Plugins;
using MediaBrowser.Common.Updates; using MediaBrowser.Common.Updates;
using MediaBrowser.Controller; using MediaBrowser.Controller;
@ -49,7 +50,7 @@ namespace Emby.Server.Implementations.EntryPoints
_sessionManager = sessionManager; _sessionManager = sessionManager;
} }
public void Run() public Task RunAsync()
{ {
_userManager.UserDeleted += userManager_UserDeleted; _userManager.UserDeleted += userManager_UserDeleted;
_userManager.UserUpdated += userManager_UserUpdated; _userManager.UserUpdated += userManager_UserUpdated;
@ -65,6 +66,8 @@ namespace Emby.Server.Implementations.EntryPoints
_installationManager.PackageInstallationFailed += _installationManager_PackageInstallationFailed; _installationManager.PackageInstallationFailed += _installationManager_PackageInstallationFailed;
_taskManager.TaskCompleted += _taskManager_TaskCompleted; _taskManager.TaskCompleted += _taskManager_TaskCompleted;
return Task.CompletedTask;
} }
void _installationManager_PackageInstalling(object sender, InstallationEventArgs e) void _installationManager_PackageInstalling(object sender, InstallationEventArgs e)

View File

@ -1,3 +1,4 @@
using System.Threading.Tasks;
using Emby.Server.Implementations.Browser; using Emby.Server.Implementations.Browser;
using MediaBrowser.Controller; using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Configuration;
@ -32,11 +33,11 @@ namespace Emby.Server.Implementations.EntryPoints
/// <summary> /// <summary>
/// Runs this instance. /// Runs this instance.
/// </summary> /// </summary>
public void Run() public Task RunAsync()
{ {
if (!_appHost.CanLaunchWebBrowser) if (!_appHost.CanLaunchWebBrowser)
{ {
return; return Task.CompletedTask;
} }
if (!_config.Configuration.IsStartupWizardCompleted) if (!_config.Configuration.IsStartupWizardCompleted)
@ -47,11 +48,13 @@ namespace Emby.Server.Implementations.EntryPoints
{ {
var options = ((ApplicationHost)_appHost).StartupOptions; var options = ((ApplicationHost)_appHost).StartupOptions;
if (!options.ContainsOption("-noautorunwebapp")) if (!options.NoAutoRunWebApp)
{ {
BrowserLauncher.OpenWebApp(_appHost); BrowserLauncher.OpenWebApp(_appHost);
} }
} }
return Task.CompletedTask;
} }
/// <summary> /// <summary>

View File

@ -1,4 +1,5 @@
using System; using System;
using System.Threading.Tasks;
using Emby.Server.Implementations.Udp; using Emby.Server.Implementations.Udp;
using MediaBrowser.Controller; using MediaBrowser.Controller;
using MediaBrowser.Controller.Plugins; using MediaBrowser.Controller.Plugins;
@ -43,7 +44,7 @@ namespace Emby.Server.Implementations.EntryPoints
/// <summary> /// <summary>
/// Runs this instance. /// Runs this instance.
/// </summary> /// </summary>
public void Run() public Task RunAsync()
{ {
var udpServer = new UdpServer(_logger, _appHost, _json, _socketFactory); var udpServer = new UdpServer(_logger, _appHost, _json, _socketFactory);
@ -57,6 +58,8 @@ namespace Emby.Server.Implementations.EntryPoints
{ {
_logger.LogError(ex, "Failed to start UDP Server"); _logger.LogError(ex, "Failed to start UDP Server");
} }
return Task.CompletedTask;
} }
/// <summary> /// <summary>

View File

@ -10,7 +10,6 @@ using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Extensions; using MediaBrowser.Model.Extensions;
using MediaBrowser.Model.Session; using MediaBrowser.Model.Session;
using MediaBrowser.Model.Threading;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.EntryPoints namespace Emby.Server.Implementations.EntryPoints
@ -23,24 +22,24 @@ namespace Emby.Server.Implementations.EntryPoints
private readonly IUserManager _userManager; private readonly IUserManager _userManager;
private readonly object _syncLock = new object(); private readonly object _syncLock = new object();
private ITimer UpdateTimer { get; set; } private Timer UpdateTimer { get; set; }
private readonly ITimerFactory _timerFactory;
private const int UpdateDuration = 500; private const int UpdateDuration = 500;
private readonly Dictionary<Guid, List<BaseItem>> _changedItems = new Dictionary<Guid, List<BaseItem>>(); private readonly Dictionary<Guid, List<BaseItem>> _changedItems = new Dictionary<Guid, List<BaseItem>>();
public UserDataChangeNotifier(IUserDataManager userDataManager, ISessionManager sessionManager, ILogger logger, IUserManager userManager, ITimerFactory timerFactory) public UserDataChangeNotifier(IUserDataManager userDataManager, ISessionManager sessionManager, ILogger logger, IUserManager userManager)
{ {
_userDataManager = userDataManager; _userDataManager = userDataManager;
_sessionManager = sessionManager; _sessionManager = sessionManager;
_logger = logger; _logger = logger;
_userManager = userManager; _userManager = userManager;
_timerFactory = timerFactory;
} }
public void Run() public Task RunAsync()
{ {
_userDataManager.UserDataSaved += _userDataManager_UserDataSaved; _userDataManager.UserDataSaved += _userDataManager_UserDataSaved;
return Task.CompletedTask;
} }
void _userDataManager_UserDataSaved(object sender, UserDataSaveEventArgs e) void _userDataManager_UserDataSaved(object sender, UserDataSaveEventArgs e)
@ -54,7 +53,7 @@ namespace Emby.Server.Implementations.EntryPoints
{ {
if (UpdateTimer == null) if (UpdateTimer == null)
{ {
UpdateTimer = _timerFactory.Create(UpdateTimerCallback, null, UpdateDuration, UpdateTimer = new Timer(UpdateTimerCallback, null, UpdateDuration,
Timeout.Infinite); Timeout.Infinite);
} }
else else
@ -121,7 +120,8 @@ namespace Emby.Server.Implementations.EntryPoints
var user = _userManager.GetUserById(userId); var user = _userManager.GetUserById(userId);
var dtoList = changedItems var dtoList = changedItems
.DistinctBy(i => i.Id) .GroupBy(x => x.Id)
.Select(x => x.First())
.Select(i => .Select(i =>
{ {
var dto = _userDataManager.GetUserDataDto(i, user); var dto = _userDataManager.GetUserDataDto(i, user);

View File

@ -28,10 +28,10 @@ namespace Emby.Server.Implementations.FFMpeg
_ffmpegInstallInfo = ffmpegInstallInfo; _ffmpegInstallInfo = ffmpegInstallInfo;
} }
public FFMpegInfo GetFFMpegInfo(StartupOptions options) public FFMpegInfo GetFFMpegInfo(IStartupOptions options)
{ {
var customffMpegPath = options.GetOption("-ffmpeg"); var customffMpegPath = options.FFmpegPath;
var customffProbePath = options.GetOption("-ffprobe"); var customffProbePath = options.FFprobePath;
if (!string.IsNullOrWhiteSpace(customffMpegPath) && !string.IsNullOrWhiteSpace(customffProbePath)) if (!string.IsNullOrWhiteSpace(customffMpegPath) && !string.IsNullOrWhiteSpace(customffProbePath))
{ {

View File

@ -264,7 +264,7 @@ namespace Emby.Server.Implementations.HttpClientManager
} }
var url = options.Url; var url = options.Url;
var urlHash = url.ToLower().GetMD5().ToString("N"); var urlHash = url.ToLowerInvariant().GetMD5().ToString("N");
var responseCachePath = Path.Combine(_appPaths.CachePath, "httpclient", urlHash); var responseCachePath = Path.Combine(_appPaths.CachePath, "httpclient", urlHash);
@ -374,11 +374,11 @@ namespace Emby.Server.Implementations.HttpClientManager
{ {
if (options.LogRequestAsDebug) if (options.LogRequestAsDebug)
{ {
_logger.LogDebug("HttpClientManager {0}: {1}", httpMethod.ToUpper(), options.Url); _logger.LogDebug("HttpClientManager {0}: {1}", httpMethod.ToUpper(CultureInfo.CurrentCulture), options.Url);
} }
else else
{ {
_logger.LogInformation("HttpClientManager {0}: {1}", httpMethod.ToUpper(), options.Url); _logger.LogInformation("HttpClientManager {0}: {1}", httpMethod.ToUpper(CultureInfo.CurrentCulture), options.Url);
} }
} }

View File

@ -394,7 +394,7 @@ namespace Emby.Server.Implementations.HttpServer
{ {
return contentType == null return contentType == null
? null ? null
: contentType.Split(';')[0].ToLower().Trim(); : contentType.Split(';')[0].ToLowerInvariant().Trim();
} }
private static string SerializeToXmlString(object from) private static string SerializeToXmlString(object from)

View File

@ -2,6 +2,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Threading;
using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
@ -9,7 +10,6 @@ using MediaBrowser.Model.Extensions;
using MediaBrowser.Model.IO; using MediaBrowser.Model.IO;
using MediaBrowser.Model.System; using MediaBrowser.Model.System;
using MediaBrowser.Model.Tasks; using MediaBrowser.Model.Tasks;
using MediaBrowser.Model.Threading;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.IO namespace Emby.Server.Implementations.IO
@ -22,8 +22,7 @@ namespace Emby.Server.Implementations.IO
private IServerConfigurationManager ConfigurationManager { get; set; } private IServerConfigurationManager ConfigurationManager { get; set; }
private readonly IFileSystem _fileSystem; private readonly IFileSystem _fileSystem;
private readonly List<string> _affectedPaths = new List<string>(); private readonly List<string> _affectedPaths = new List<string>();
private ITimer _timer; private Timer _timer;
private readonly ITimerFactory _timerFactory;
private readonly object _timerLock = new object(); private readonly object _timerLock = new object();
public string Path { get; private set; } public string Path { get; private set; }
@ -31,7 +30,7 @@ namespace Emby.Server.Implementations.IO
private readonly IEnvironmentInfo _environmentInfo; private readonly IEnvironmentInfo _environmentInfo;
private readonly ILibraryManager _libraryManager; private readonly ILibraryManager _libraryManager;
public FileRefresher(string path, IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILibraryManager libraryManager, ITaskManager taskManager, ILogger logger, ITimerFactory timerFactory, IEnvironmentInfo environmentInfo, ILibraryManager libraryManager1) public FileRefresher(string path, IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILibraryManager libraryManager, ITaskManager taskManager, ILogger logger, IEnvironmentInfo environmentInfo, ILibraryManager libraryManager1)
{ {
logger.LogDebug("New file refresher created for {0}", path); logger.LogDebug("New file refresher created for {0}", path);
Path = path; Path = path;
@ -41,7 +40,6 @@ namespace Emby.Server.Implementations.IO
LibraryManager = libraryManager; LibraryManager = libraryManager;
TaskManager = taskManager; TaskManager = taskManager;
Logger = logger; Logger = logger;
_timerFactory = timerFactory;
_environmentInfo = environmentInfo; _environmentInfo = environmentInfo;
_libraryManager = libraryManager1; _libraryManager = libraryManager1;
AddPath(path); AddPath(path);
@ -90,7 +88,7 @@ namespace Emby.Server.Implementations.IO
if (_timer == null) if (_timer == null)
{ {
_timer = _timerFactory.Create(OnTimerCallback, null, TimeSpan.FromSeconds(ConfigurationManager.Configuration.LibraryMonitorDelay), TimeSpan.FromMilliseconds(-1)); _timer = new Timer(OnTimerCallback, null, TimeSpan.FromSeconds(ConfigurationManager.Configuration.LibraryMonitorDelay), TimeSpan.FromMilliseconds(-1));
} }
else else
{ {
@ -146,8 +144,8 @@ namespace Emby.Server.Implementations.IO
.Distinct(StringComparer.OrdinalIgnoreCase) .Distinct(StringComparer.OrdinalIgnoreCase)
.Select(GetAffectedBaseItem) .Select(GetAffectedBaseItem)
.Where(item => item != null) .Where(item => item != null)
.DistinctBy(i => i.Id) .GroupBy(x => x.Id)
.ToList(); .Select(x => x.First());
foreach (var item in itemsToRefresh) foreach (var item in itemsToRefresh)
{ {

View File

@ -11,7 +11,6 @@ using MediaBrowser.Controller.Plugins;
using MediaBrowser.Model.IO; using MediaBrowser.Model.IO;
using MediaBrowser.Model.System; using MediaBrowser.Model.System;
using MediaBrowser.Model.Tasks; using MediaBrowser.Model.Tasks;
using MediaBrowser.Model.Threading;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.IO namespace Emby.Server.Implementations.IO
@ -35,7 +34,7 @@ namespace Emby.Server.Implementations.IO
/// <summary> /// <summary>
/// Any file name ending in any of these will be ignored by the watchers /// Any file name ending in any of these will be ignored by the watchers
/// </summary> /// </summary>
private readonly string[] _alwaysIgnoreFiles = new string[] private readonly HashSet<string> _alwaysIgnoreFiles = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{ {
"small.jpg", "small.jpg",
"albumart.jpg", "albumart.jpg",
@ -54,7 +53,7 @@ namespace Emby.Server.Implementations.IO
".actors" ".actors"
}; };
private readonly string[] _alwaysIgnoreExtensions = new string[] private readonly HashSet<string> _alwaysIgnoreExtensions = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{ {
// thumbs.db // thumbs.db
".db", ".db",
@ -134,7 +133,6 @@ namespace Emby.Server.Implementations.IO
private IServerConfigurationManager ConfigurationManager { get; set; } private IServerConfigurationManager ConfigurationManager { get; set; }
private readonly IFileSystem _fileSystem; private readonly IFileSystem _fileSystem;
private readonly ITimerFactory _timerFactory;
private readonly IEnvironmentInfo _environmentInfo; private readonly IEnvironmentInfo _environmentInfo;
/// <summary> /// <summary>
@ -146,7 +144,6 @@ namespace Emby.Server.Implementations.IO
ILibraryManager libraryManager, ILibraryManager libraryManager,
IServerConfigurationManager configurationManager, IServerConfigurationManager configurationManager,
IFileSystem fileSystem, IFileSystem fileSystem,
ITimerFactory timerFactory,
IEnvironmentInfo environmentInfo) IEnvironmentInfo environmentInfo)
{ {
if (taskManager == null) if (taskManager == null)
@ -159,7 +156,6 @@ namespace Emby.Server.Implementations.IO
Logger = loggerFactory.CreateLogger(GetType().Name); Logger = loggerFactory.CreateLogger(GetType().Name);
ConfigurationManager = configurationManager; ConfigurationManager = configurationManager;
_fileSystem = fileSystem; _fileSystem = fileSystem;
_timerFactory = timerFactory;
_environmentInfo = environmentInfo; _environmentInfo = environmentInfo;
} }
@ -460,8 +456,8 @@ namespace Emby.Server.Implementations.IO
var filename = Path.GetFileName(path); var filename = Path.GetFileName(path);
var monitorPath = !string.IsNullOrEmpty(filename) && var monitorPath = !string.IsNullOrEmpty(filename) &&
!_alwaysIgnoreFiles.Contains(filename, StringComparer.OrdinalIgnoreCase) && !_alwaysIgnoreFiles.Contains(filename) &&
!_alwaysIgnoreExtensions.Contains(Path.GetExtension(path) ?? string.Empty, StringComparer.OrdinalIgnoreCase) && !_alwaysIgnoreExtensions.Contains(Path.GetExtension(path)) &&
_alwaysIgnoreSubstrings.All(i => path.IndexOf(i, StringComparison.OrdinalIgnoreCase) == -1); _alwaysIgnoreSubstrings.All(i => path.IndexOf(i, StringComparison.OrdinalIgnoreCase) == -1);
// Ignore certain files // Ignore certain files
@ -545,7 +541,7 @@ namespace Emby.Server.Implementations.IO
} }
} }
var newRefresher = new FileRefresher(path, _fileSystem, ConfigurationManager, LibraryManager, TaskManager, Logger, _timerFactory, _environmentInfo, LibraryManager); var newRefresher = new FileRefresher(path, _fileSystem, ConfigurationManager, LibraryManager, TaskManager, Logger, _environmentInfo, LibraryManager);
newRefresher.Completed += NewRefresher_Completed; newRefresher.Completed += NewRefresher_Completed;
_activeRefreshers.Add(newRefresher); _activeRefreshers.Add(newRefresher);
} }
@ -601,20 +597,26 @@ namespace Emby.Server.Implementations.IO
/// </summary> /// </summary>
public void Dispose() public void Dispose()
{ {
_disposed = true;
Dispose(true); Dispose(true);
} }
/// <summary> /// <summary>
/// Releases unmanaged and - optionally - managed resources. /// Releases unmanaged and - optionally - managed resources.
/// </summary> /// </summary>
/// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param> /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
protected virtual void Dispose(bool dispose) protected virtual void Dispose(bool disposing)
{ {
if (dispose) if (_disposed)
{
return;
}
if (disposing)
{ {
Stop(); Stop();
} }
_disposed = true;
} }
} }
@ -627,9 +629,10 @@ namespace Emby.Server.Implementations.IO
_monitor = monitor; _monitor = monitor;
} }
public void Run() public Task RunAsync()
{ {
_monitor.Start(); _monitor.Start();
return Task.CompletedTask;
} }
public void Dispose() public void Dispose()

View File

@ -0,0 +1,40 @@
namespace Emby.Server.Implementations
{
public interface IStartupOptions
{
/// <summary>
/// --ffmpeg
/// </summary>
string FFmpegPath { get; }
/// <summary>
/// --ffprobe
/// </summary>
string FFprobePath { get; }
/// <summary>
/// --service
/// </summary>
bool IsService { get; }
/// <summary>
/// --noautorunwebapp
/// </summary>
bool NoAutoRunWebApp { get; }
/// <summary>
/// --package-name
/// </summary>
string PackageName { get; }
/// <summary>
/// --restartpath
/// </summary>
string RestartPath { get; }
/// <summary>
/// --restartargs
/// </summary>
string RestartArgs { get; }
}
}

View File

@ -215,7 +215,7 @@ namespace Emby.Server.Implementations.Images
{ {
return CreateSquareCollage(item, itemsWithImages, outputPath); return CreateSquareCollage(item, itemsWithImages, outputPath);
} }
if (item is Playlist || item is MusicGenre || item is Genre || item is GameGenre || item is PhotoAlbum) if (item is Playlist || item is MusicGenre || item is Genre || item is PhotoAlbum)
{ {
return CreateSquareCollage(item, itemsWithImages, outputPath); return CreateSquareCollage(item, itemsWithImages, outputPath);
} }

View File

@ -512,7 +512,7 @@ namespace Emby.Server.Implementations.Library
if (forceCaseInsensitive || !ConfigurationManager.Configuration.EnableCaseSensitiveItemIds) if (forceCaseInsensitive || !ConfigurationManager.Configuration.EnableCaseSensitiveItemIds)
{ {
key = key.ToLower(); key = key.ToLowerInvariant();
} }
key = type.FullName + key; key = type.FullName + key;
@ -869,11 +869,6 @@ namespace Emby.Server.Implementations.Library
return GetItemByNameId<MusicGenre>(MusicGenre.GetPath, name); return GetItemByNameId<MusicGenre>(MusicGenre.GetPath, name);
} }
public Guid GetGameGenreId(string name)
{
return GetItemByNameId<GameGenre>(GameGenre.GetPath, name);
}
/// <summary> /// <summary>
/// Gets a Genre /// Gets a Genre
/// </summary> /// </summary>
@ -894,16 +889,6 @@ namespace Emby.Server.Implementations.Library
return CreateItemByName<MusicGenre>(MusicGenre.GetPath, name, new DtoOptions(true)); return CreateItemByName<MusicGenre>(MusicGenre.GetPath, name, new DtoOptions(true));
} }
/// <summary>
/// Gets the game genre.
/// </summary>
/// <param name="name">The name.</param>
/// <returns>Task{GameGenre}.</returns>
public GameGenre GetGameGenre(string name)
{
return CreateItemByName<GameGenre>(GameGenre.GetPath, name, new DtoOptions(true));
}
/// <summary> /// <summary>
/// Gets a Year /// Gets a Year
/// </summary> /// </summary>
@ -1370,17 +1355,6 @@ namespace Emby.Server.Implementations.Library
return ItemRepository.GetGenres(query); return ItemRepository.GetGenres(query);
} }
public QueryResult<Tuple<BaseItem, ItemCounts>> GetGameGenres(InternalItemsQuery query)
{
if (query.User != null)
{
AddUserToQuery(query, query.User);
}
SetTopParentOrAncestorIds(query);
return ItemRepository.GetGameGenres(query);
}
public QueryResult<Tuple<BaseItem, ItemCounts>> GetMusicGenres(InternalItemsQuery query) public QueryResult<Tuple<BaseItem, ItemCounts>> GetMusicGenres(InternalItemsQuery query)
{ {
if (query.User != null) if (query.User != null)

View File

@ -20,7 +20,6 @@ using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.IO; using MediaBrowser.Model.IO;
using MediaBrowser.Model.MediaInfo; using MediaBrowser.Model.MediaInfo;
using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.Threading;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.Library namespace Emby.Server.Implementations.Library
@ -36,7 +35,6 @@ namespace Emby.Server.Implementations.Library
private IMediaSourceProvider[] _providers; private IMediaSourceProvider[] _providers;
private readonly ILogger _logger; private readonly ILogger _logger;
private readonly IUserDataManager _userDataManager; private readonly IUserDataManager _userDataManager;
private readonly ITimerFactory _timerFactory;
private readonly Func<IMediaEncoder> _mediaEncoder; private readonly Func<IMediaEncoder> _mediaEncoder;
private ILocalizationManager _localizationManager; private ILocalizationManager _localizationManager;
private IApplicationPaths _appPaths; private IApplicationPaths _appPaths;
@ -51,7 +49,6 @@ namespace Emby.Server.Implementations.Library
IJsonSerializer jsonSerializer, IJsonSerializer jsonSerializer,
IFileSystem fileSystem, IFileSystem fileSystem,
IUserDataManager userDataManager, IUserDataManager userDataManager,
ITimerFactory timerFactory,
Func<IMediaEncoder> mediaEncoder) Func<IMediaEncoder> mediaEncoder)
{ {
_itemRepo = itemRepo; _itemRepo = itemRepo;
@ -61,7 +58,6 @@ namespace Emby.Server.Implementations.Library
_jsonSerializer = jsonSerializer; _jsonSerializer = jsonSerializer;
_fileSystem = fileSystem; _fileSystem = fileSystem;
_userDataManager = userDataManager; _userDataManager = userDataManager;
_timerFactory = timerFactory;
_mediaEncoder = mediaEncoder; _mediaEncoder = mediaEncoder;
_localizationManager = localizationManager; _localizationManager = localizationManager;
_appPaths = applicationPaths; _appPaths = applicationPaths;
@ -322,18 +318,18 @@ namespace Emby.Server.Implementations.Library
private string[] NormalizeLanguage(string language) private string[] NormalizeLanguage(string language)
{ {
if (language != null) if (language == null)
{ {
var culture = _localizationManager.FindLanguageInfo(language); return Array.Empty<string>();
if (culture != null)
{
return culture.ThreeLetterISOLanguageNames;
}
return new string[] { language };
} }
return Array.Empty<string>(); var culture = _localizationManager.FindLanguageInfo(language);
if (culture != null)
{
return culture.ThreeLetterISOLanguageNames;
}
return new string[] { language };
} }
private void SetDefaultSubtitleStreamIndex(MediaSourceInfo source, UserItemData userData, User user, bool allowRememberingSelection) private void SetDefaultSubtitleStreamIndex(MediaSourceInfo source, UserItemData userData, User user, bool allowRememberingSelection)

View File

@ -1,4 +1,5 @@
using System; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Drawing;
@ -85,7 +86,7 @@ namespace Emby.Server.Implementations.Library.Resolvers
return false; return false;
} }
private static readonly string[] IgnoreFiles = private static readonly HashSet<string> IgnoreFiles = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{ {
"folder", "folder",
"thumb", "thumb",
@ -102,7 +103,7 @@ namespace Emby.Server.Implementations.Library.Resolvers
{ {
var filename = Path.GetFileNameWithoutExtension(path) ?? string.Empty; var filename = Path.GetFileNameWithoutExtension(path) ?? string.Empty;
if (IgnoreFiles.Contains(filename, StringComparer.OrdinalIgnoreCase)) if (IgnoreFiles.Contains(filename))
{ {
return false; return false;
} }
@ -112,7 +113,7 @@ namespace Emby.Server.Implementations.Library.Resolvers
return false; return false;
} }
return imageProcessor.SupportedInputFormats.Contains((Path.GetExtension(path) ?? string.Empty).TrimStart('.'), StringComparer.OrdinalIgnoreCase); return imageProcessor.SupportedInputFormats.Contains((Path.GetExtension(path) ?? string.Empty).TrimStart('.'));
} }
} }

View File

@ -99,14 +99,12 @@ namespace Emby.Server.Implementations.Library
if (!query.IncludeMedia) if (!query.IncludeMedia)
{ {
AddIfMissing(includeItemTypes, typeof(Genre).Name); AddIfMissing(includeItemTypes, typeof(Genre).Name);
AddIfMissing(includeItemTypes, typeof(GameGenre).Name);
AddIfMissing(includeItemTypes, typeof(MusicGenre).Name); AddIfMissing(includeItemTypes, typeof(MusicGenre).Name);
} }
} }
else else
{ {
AddIfMissing(excludeItemTypes, typeof(Genre).Name); AddIfMissing(excludeItemTypes, typeof(Genre).Name);
AddIfMissing(excludeItemTypes, typeof(GameGenre).Name);
AddIfMissing(excludeItemTypes, typeof(MusicGenre).Name); AddIfMissing(excludeItemTypes, typeof(MusicGenre).Name);
} }

View File

@ -24,7 +24,6 @@ using MediaBrowser.Controller.Providers;
using MediaBrowser.Controller.Security; using MediaBrowser.Controller.Security;
using MediaBrowser.Controller.Session; using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Connect;
using MediaBrowser.Model.Cryptography; using MediaBrowser.Model.Cryptography;
using MediaBrowser.Model.Dto; using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
@ -212,11 +211,8 @@ namespace Emby.Server.Implementations.Library
{ {
foreach (var user in users) foreach (var user in users)
{ {
if (!user.ConnectLinkType.HasValue || user.ConnectLinkType.Value == UserLinkType.LinkedUser) user.Policy.IsAdministrator = true;
{ UpdateUserPolicy(user, user.Policy, false);
user.Policy.IsAdministrator = true;
UpdateUserPolicy(user, user.Policy, false);
}
} }
} }
} }
@ -272,13 +268,9 @@ namespace Emby.Server.Implementations.Library
if (user != null) if (user != null)
{ {
// Authenticate using local credentials if not a guest var authResult = await AuthenticateLocalUser(username, password, hashedPassword, user, remoteEndPoint).ConfigureAwait(false);
if (!user.ConnectLinkType.HasValue || user.ConnectLinkType.Value != UserLinkType.Guest) authenticationProvider = authResult.Item1;
{ success = authResult.Item2;
var authResult = await AuthenticateLocalUser(username, password, hashedPassword, user, remoteEndPoint).ConfigureAwait(false);
authenticationProvider = authResult.Item1;
success = authResult.Item2;
}
} }
else else
{ {
@ -455,30 +447,30 @@ namespace Emby.Server.Implementations.Library
private void UpdateInvalidLoginAttemptCount(User user, int newValue) private void UpdateInvalidLoginAttemptCount(User user, int newValue)
{ {
if (user.Policy.InvalidLoginAttemptCount != newValue || newValue > 0) if (user.Policy.InvalidLoginAttemptCount == newValue || newValue <= 0)
{ {
user.Policy.InvalidLoginAttemptCount = newValue; return;
}
var maxCount = user.Policy.IsAdministrator ? 3 : 5; user.Policy.InvalidLoginAttemptCount = newValue;
// TODO: Fix var maxCount = user.Policy.IsAdministrator ? 3 : 5;
/*
var fireLockout = false;
if (newValue >= maxCount) var fireLockout = false;
{
_logger.LogDebug("Disabling user {0} due to {1} unsuccessful login attempts.", user.Name, newValue.ToString(CultureInfo.InvariantCulture));
user.Policy.IsDisabled = true;
fireLockout = true; if (newValue >= maxCount)
}*/ {
_logger.LogDebug("Disabling user {0} due to {1} unsuccessful login attempts.", user.Name, newValue);
user.Policy.IsDisabled = true;
UpdateUserPolicy(user, user.Policy, false); fireLockout = true;
}
/* if (fireLockout) UpdateUserPolicy(user, user.Policy, false);
{
UserLockedOut?.Invoke(this, new GenericEventArgs<User>(user)); if (fireLockout)
}*/ {
UserLockedOut?.Invoke(this, new GenericEventArgs<User>(user));
} }
} }
@ -553,9 +545,6 @@ namespace Emby.Server.Implementations.Library
LastActivityDate = user.LastActivityDate, LastActivityDate = user.LastActivityDate,
LastLoginDate = user.LastLoginDate, LastLoginDate = user.LastLoginDate,
Configuration = user.Configuration, Configuration = user.Configuration,
ConnectLinkType = user.ConnectLinkType,
ConnectUserId = user.ConnectUserId,
ConnectUserName = user.ConnectUserName,
ServerId = _appHost.SystemId, ServerId = _appHost.SystemId,
Policy = user.Policy Policy = user.Policy
}; };
@ -814,11 +803,6 @@ namespace Emby.Server.Implementations.Library
throw new ArgumentNullException(nameof(user)); throw new ArgumentNullException(nameof(user));
} }
if (user.ConnectLinkType.HasValue && user.ConnectLinkType.Value == UserLinkType.Guest)
{
throw new ArgumentException("Passwords for guests cannot be changed.");
}
await GetAuthenticationProvider(user).ChangePassword(user, newPassword).ConfigureAwait(false); await GetAuthenticationProvider(user).ChangePassword(user, newPassword).ConfigureAwait(false);
UpdateUser(user); UpdateUser(user);
@ -925,11 +909,6 @@ namespace Emby.Server.Implementations.Library
null : null :
GetUserByName(enteredUsername); GetUserByName(enteredUsername);
if (user != null && user.ConnectLinkType.HasValue && user.ConnectLinkType.Value == UserLinkType.Guest)
{
throw new ArgumentException("Unable to process forgot password request for guests.");
}
var action = ForgotPasswordAction.InNetworkRequired; var action = ForgotPasswordAction.InNetworkRequired;
string pinFile = null; string pinFile = null;
DateTime? expirationDate = null; DateTime? expirationDate = null;
@ -974,10 +953,7 @@ namespace Emby.Server.Implementations.Library
_lastPin = null; _lastPin = null;
_lastPasswordPinCreationResult = null; _lastPasswordPinCreationResult = null;
var users = Users.Where(i => !i.ConnectLinkType.HasValue || i.ConnectLinkType.Value != UserLinkType.Guest) foreach (var user in Users)
.ToList();
foreach (var user in users)
{ {
await ResetPassword(user).ConfigureAwait(false); await ResetPassword(user).ConfigureAwait(false);
@ -1205,9 +1181,11 @@ namespace Emby.Server.Implementations.Library
_sessionManager = sessionManager; _sessionManager = sessionManager;
} }
public void Run() public Task RunAsync()
{ {
_userManager.UserPolicyUpdated += _userManager_UserPolicyUpdated; _userManager.UserPolicyUpdated += _userManager_UserPolicyUpdated;
return Task.CompletedTask;
} }
private void _userManager_UserPolicyUpdated(object sender, GenericEventArgs<User> e) private void _userManager_UserPolicyUpdated(object sender, GenericEventArgs<User> e)

View File

@ -308,9 +308,6 @@ namespace Emby.Server.Implementations.Library
mediaTypes.Add(MediaType.Book); mediaTypes.Add(MediaType.Book);
mediaTypes.Add(MediaType.Audio); mediaTypes.Add(MediaType.Audio);
break; break;
case CollectionType.Games:
mediaTypes.Add(MediaType.Game);
break;
case CollectionType.Music: case CollectionType.Music:
mediaTypes.Add(MediaType.Audio); mediaTypes.Add(MediaType.Audio);
break; break;
@ -336,7 +333,6 @@ namespace Emby.Server.Implementations.Library
typeof(Person).Name, typeof(Person).Name,
typeof(Studio).Name, typeof(Studio).Name,
typeof(Year).Name, typeof(Year).Name,
typeof(GameGenre).Name,
typeof(MusicGenre).Name, typeof(MusicGenre).Name,
typeof(Genre).Name typeof(Genre).Name

View File

@ -1,45 +0,0 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Persistence;
using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.Library.Validators
{
/// <summary>
/// Class GameGenresPostScanTask
/// </summary>
public class GameGenresPostScanTask : ILibraryPostScanTask
{
/// <summary>
/// The _library manager
/// </summary>
private readonly ILibraryManager _libraryManager;
private readonly ILogger _logger;
private readonly IItemRepository _itemRepo;
/// <summary>
/// Initializes a new instance of the <see cref="GameGenresPostScanTask" /> class.
/// </summary>
/// <param name="libraryManager">The library manager.</param>
/// <param name="logger">The logger.</param>
public GameGenresPostScanTask(ILibraryManager libraryManager, ILogger logger, IItemRepository itemRepo)
{
_libraryManager = libraryManager;
_logger = logger;
_itemRepo = itemRepo;
}
/// <summary>
/// Runs the specified progress.
/// </summary>
/// <param name="progress">The progress.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
public Task Run(IProgress<double> progress, CancellationToken cancellationToken)
{
return new GameGenresValidator(_libraryManager, _logger, _itemRepo).Run(progress, cancellationToken);
}
}
}

View File

@ -1,72 +0,0 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Persistence;
using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.Library.Validators
{
class GameGenresValidator
{
/// <summary>
/// The _library manager
/// </summary>
private readonly ILibraryManager _libraryManager;
/// <summary>
/// The _logger
/// </summary>
private readonly ILogger _logger;
private readonly IItemRepository _itemRepo;
public GameGenresValidator(ILibraryManager libraryManager, ILogger logger, IItemRepository itemRepo)
{
_libraryManager = libraryManager;
_logger = logger;
_itemRepo = itemRepo;
}
/// <summary>
/// Runs the specified progress.
/// </summary>
/// <param name="progress">The progress.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
public async Task Run(IProgress<double> progress, CancellationToken cancellationToken)
{
var names = _itemRepo.GetGameGenreNames();
var numComplete = 0;
var count = names.Count;
foreach (var name in names)
{
try
{
var item = _libraryManager.GetGameGenre(name);
await item.RefreshMetadata(cancellationToken).ConfigureAwait(false);
}
catch (OperationCanceledException)
{
// Don't clutter the log
throw;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error refreshing {GenreName}", name);
}
numComplete++;
double percent = numComplete;
percent /= count;
percent *= 100;
progress.Report(percent);
}
progress.Report(100);
}
}
}

View File

@ -35,8 +35,6 @@ using MediaBrowser.Model.Providers;
using MediaBrowser.Model.Querying; using MediaBrowser.Model.Querying;
using MediaBrowser.Model.Reflection; using MediaBrowser.Model.Reflection;
using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.System;
using MediaBrowser.Model.Threading;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.LiveTv.EmbyTV namespace Emby.Server.Implementations.LiveTv.EmbyTV
@ -65,7 +63,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
public static EmbyTV Current; public static EmbyTV Current;
public event EventHandler DataSourceChanged;
public event EventHandler<GenericEventArgs<TimerInfo>> TimerCreated; public event EventHandler<GenericEventArgs<TimerInfo>> TimerCreated;
public event EventHandler<GenericEventArgs<string>> TimerCancelled; public event EventHandler<GenericEventArgs<string>> TimerCancelled;
@ -88,7 +85,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
ILibraryMonitor libraryMonitor, ILibraryMonitor libraryMonitor,
IProviderManager providerManager, IProviderManager providerManager,
IMediaEncoder mediaEncoder, IMediaEncoder mediaEncoder,
ITimerFactory timerFactory,
IProcessFactory processFactory) IProcessFactory processFactory)
{ {
Current = this; Current = this;
@ -110,7 +106,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
_streamHelper = streamHelper; _streamHelper = streamHelper;
_seriesTimerProvider = new SeriesTimerManager(fileSystem, jsonSerializer, _logger, Path.Combine(DataPath, "seriestimers")); _seriesTimerProvider = new SeriesTimerManager(fileSystem, jsonSerializer, _logger, Path.Combine(DataPath, "seriestimers"));
_timerProvider = new TimerManager(fileSystem, jsonSerializer, _logger, Path.Combine(DataPath, "timers"), _logger, timerFactory); _timerProvider = new TimerManager(fileSystem, jsonSerializer, _logger, Path.Combine(DataPath, "timers"), _logger);
_timerProvider.TimerFired += _timerProvider_TimerFired; _timerProvider.TimerFired += _timerProvider_TimerFired;
_config.NamedConfigurationUpdated += _config_NamedConfigurationUpdated; _config.NamedConfigurationUpdated += _config_NamedConfigurationUpdated;
@ -124,7 +120,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
} }
} }
public async void Start() public async Task Start()
{ {
_timerProvider.RestartTimers(); _timerProvider.RestartTimers();
@ -275,7 +271,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
foreach (var timer in seriesTimers) foreach (var timer in seriesTimers)
{ {
await UpdateTimersForSeriesTimer(timer, false, true).ConfigureAwait(false); UpdateTimersForSeriesTimer(timer, false, true);
} }
} }
@ -763,12 +759,12 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
_timerProvider.AddOrUpdate(timer, false); _timerProvider.AddOrUpdate(timer, false);
} }
await UpdateTimersForSeriesTimer(info, true, false).ConfigureAwait(false); UpdateTimersForSeriesTimer(info, true, false);
return info.Id; return info.Id;
} }
public async Task UpdateSeriesTimerAsync(SeriesTimerInfo info, CancellationToken cancellationToken) public Task UpdateSeriesTimerAsync(SeriesTimerInfo info, CancellationToken cancellationToken)
{ {
var instance = _seriesTimerProvider.GetAll().FirstOrDefault(i => string.Equals(i.Id, info.Id, StringComparison.OrdinalIgnoreCase)); var instance = _seriesTimerProvider.GetAll().FirstOrDefault(i => string.Equals(i.Id, info.Id, StringComparison.OrdinalIgnoreCase));
@ -792,8 +788,10 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
_seriesTimerProvider.Update(instance); _seriesTimerProvider.Update(instance);
await UpdateTimersForSeriesTimer(instance, true, true).ConfigureAwait(false); UpdateTimersForSeriesTimer(instance, true, true);
} }
return Task.CompletedTask;
} }
public Task UpdateTimerAsync(TimerInfo updatedTimer, CancellationToken cancellationToken) public Task UpdateTimerAsync(TimerInfo updatedTimer, CancellationToken cancellationToken)
@ -2193,7 +2191,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
if (lockData) if (lockData)
{ {
writer.WriteElementString("lockdata", true.ToString().ToLower()); writer.WriteElementString("lockdata", true.ToString(CultureInfo.InvariantCulture).ToLowerInvariant());
} }
if (item.CriticRating.HasValue) if (item.CriticRating.HasValue)
@ -2346,10 +2344,9 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
} }
} }
private async Task UpdateTimersForSeriesTimer(SeriesTimerInfo seriesTimer, bool updateTimerSettings, bool deleteInvalidTimers) private void UpdateTimersForSeriesTimer(SeriesTimerInfo seriesTimer, bool updateTimerSettings, bool deleteInvalidTimers)
{ {
var allTimers = GetTimersForSeries(seriesTimer) var allTimers = GetTimersForSeries(seriesTimer).ToList();
.ToList();
var enabledTimersForSeries = new List<TimerInfo>(); var enabledTimersForSeries = new List<TimerInfo>();

View File

@ -1,12 +1,13 @@
using System.Threading.Tasks;
using MediaBrowser.Controller.Plugins; using MediaBrowser.Controller.Plugins;
namespace Emby.Server.Implementations.LiveTv.EmbyTV namespace Emby.Server.Implementations.LiveTv.EmbyTV
{ {
public class EntryPoint : IServerEntryPoint public class EntryPoint : IServerEntryPoint
{ {
public void Run() public Task RunAsync()
{ {
EmbyTV.Current.Start(); return EmbyTV.Current.Start();
} }
public void Dispose() public void Dispose()

View File

@ -2,29 +2,27 @@ using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Globalization; using System.Globalization;
using System.Linq; using System.Linq;
using System.Threading;
using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Model.Events; using MediaBrowser.Model.Events;
using MediaBrowser.Model.IO; using MediaBrowser.Model.IO;
using MediaBrowser.Model.LiveTv; using MediaBrowser.Model.LiveTv;
using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.Threading;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.LiveTv.EmbyTV namespace Emby.Server.Implementations.LiveTv.EmbyTV
{ {
public class TimerManager : ItemDataProvider<TimerInfo> public class TimerManager : ItemDataProvider<TimerInfo>
{ {
private readonly ConcurrentDictionary<string, ITimer> _timers = new ConcurrentDictionary<string, ITimer>(StringComparer.OrdinalIgnoreCase); private readonly ConcurrentDictionary<string, Timer> _timers = new ConcurrentDictionary<string, Timer>(StringComparer.OrdinalIgnoreCase);
private readonly ILogger _logger; private readonly ILogger _logger;
public event EventHandler<GenericEventArgs<TimerInfo>> TimerFired; public event EventHandler<GenericEventArgs<TimerInfo>> TimerFired;
private readonly ITimerFactory _timerFactory;
public TimerManager(IFileSystem fileSystem, IJsonSerializer jsonSerializer, ILogger logger, string dataPath, ILogger logger1, ITimerFactory timerFactory) public TimerManager(IFileSystem fileSystem, IJsonSerializer jsonSerializer, ILogger logger, string dataPath, ILogger logger1)
: base(fileSystem, jsonSerializer, logger, dataPath, (r1, r2) => string.Equals(r1.Id, r2.Id, StringComparison.OrdinalIgnoreCase)) : base(fileSystem, jsonSerializer, logger, dataPath, (r1, r2) => string.Equals(r1.Id, r2.Id, StringComparison.OrdinalIgnoreCase))
{ {
_logger = logger1; _logger = logger1;
_timerFactory = timerFactory;
} }
public void RestartTimers() public void RestartTimers()
@ -125,7 +123,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
private void StartTimer(TimerInfo item, TimeSpan dueTime) private void StartTimer(TimerInfo item, TimeSpan dueTime)
{ {
var timer = _timerFactory.Create(TimerCallback, item.Id, dueTime, TimeSpan.Zero); var timer = new Timer(TimerCallback, item.Id, dueTime, TimeSpan.Zero);
if (_timers.TryAdd(item.Id, timer)) if (_timers.TryAdd(item.Id, timer))
{ {

View File

@ -17,7 +17,7 @@ using MediaBrowser.Model.IO;
using MediaBrowser.Model.LiveTv; using MediaBrowser.Model.LiveTv;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
namespace Jellyfin.Server.Implementations.LiveTv.Listings namespace Emby.Server.Implementations.LiveTv.Listings
{ {
public class XmlTvListingsProvider : IListingsProvider public class XmlTvListingsProvider : IListingsProvider
{ {

View File

@ -399,7 +399,7 @@ namespace Emby.Server.Implementations.LiveTv
{ {
var name = serviceName + externalId + InternalVersionNumber; var name = serviceName + externalId + InternalVersionNumber;
return _libraryManager.GetNewItemId(name.ToLower(), typeof(LiveTvChannel)); return _libraryManager.GetNewItemId(name.ToLowerInvariant(), typeof(LiveTvChannel));
} }
private const string ServiceName = "Emby"; private const string ServiceName = "Emby";
@ -407,21 +407,21 @@ namespace Emby.Server.Implementations.LiveTv
{ {
var name = ServiceName + externalId + InternalVersionNumber; var name = ServiceName + externalId + InternalVersionNumber;
return name.ToLower().GetMD5().ToString("N"); return name.ToLowerInvariant().GetMD5().ToString("N");
} }
public Guid GetInternalSeriesTimerId(string externalId) public Guid GetInternalSeriesTimerId(string externalId)
{ {
var name = ServiceName + externalId + InternalVersionNumber; var name = ServiceName + externalId + InternalVersionNumber;
return name.ToLower().GetMD5(); return name.ToLowerInvariant().GetMD5();
} }
public Guid GetInternalProgramId(string externalId) public Guid GetInternalProgramId(string externalId)
{ {
var name = ServiceName + externalId + InternalVersionNumber; var name = ServiceName + externalId + InternalVersionNumber;
return _libraryManager.GetNewItemId(name.ToLower(), typeof(LiveTvProgram)); return _libraryManager.GetNewItemId(name.ToLowerInvariant(), typeof(LiveTvProgram));
} }
public async Task<TimerInfo> GetTimerInfo(TimerInfoDto dto, bool isNew, LiveTvManager liveTv, CancellationToken cancellationToken) public async Task<TimerInfo> GetTimerInfo(TimerInfoDto dto, bool isNew, LiveTvManager liveTv, CancellationToken cancellationToken)

View File

@ -6,7 +6,6 @@ using System.Threading.Tasks;
using Emby.Server.Implementations.Library; using Emby.Server.Implementations.Library;
using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Net;
using MediaBrowser.Common.Progress; using MediaBrowser.Common.Progress;
using MediaBrowser.Controller; using MediaBrowser.Controller;
using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Channels;
@ -24,7 +23,6 @@ using MediaBrowser.Controller.Sorting;
using MediaBrowser.Model.Dto; using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Events; using MediaBrowser.Model.Events;
using MediaBrowser.Model.Extensions;
using MediaBrowser.Model.Globalization; using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.IO; using MediaBrowser.Model.IO;
using MediaBrowser.Model.LiveTv; using MediaBrowser.Model.LiveTv;
@ -48,7 +46,6 @@ namespace Emby.Server.Implementations.LiveTv
private readonly ILibraryManager _libraryManager; private readonly ILibraryManager _libraryManager;
private readonly ITaskManager _taskManager; private readonly ITaskManager _taskManager;
private readonly IJsonSerializer _jsonSerializer; private readonly IJsonSerializer _jsonSerializer;
private readonly IProviderManager _providerManager;
private readonly Func<IChannelManager> _channelManager; private readonly Func<IChannelManager> _channelManager;
private readonly IDtoService _dtoService; private readonly IDtoService _dtoService;
@ -56,7 +53,7 @@ namespace Emby.Server.Implementations.LiveTv
private readonly LiveTvDtoService _tvDtoService; private readonly LiveTvDtoService _tvDtoService;
private ILiveTvService[] _services = new ILiveTvService[] { }; private ILiveTvService[] _services = Array.Empty<ILiveTvService>();
private ITunerHost[] _tunerHosts = Array.Empty<ITunerHost>(); private ITunerHost[] _tunerHosts = Array.Empty<ITunerHost>();
private IListingsProvider[] _listingProviders = Array.Empty<IListingsProvider>(); private IListingsProvider[] _listingProviders = Array.Empty<IListingsProvider>();
@ -85,7 +82,6 @@ namespace Emby.Server.Implementations.LiveTv
ITaskManager taskManager, ITaskManager taskManager,
ILocalizationManager localization, ILocalizationManager localization,
IJsonSerializer jsonSerializer, IJsonSerializer jsonSerializer,
IProviderManager providerManager,
IFileSystem fileSystem, IFileSystem fileSystem,
Func<IChannelManager> channelManager) Func<IChannelManager> channelManager)
{ {
@ -97,7 +93,6 @@ namespace Emby.Server.Implementations.LiveTv
_taskManager = taskManager; _taskManager = taskManager;
_localization = localization; _localization = localization;
_jsonSerializer = jsonSerializer; _jsonSerializer = jsonSerializer;
_providerManager = providerManager;
_fileSystem = fileSystem; _fileSystem = fileSystem;
_dtoService = dtoService; _dtoService = dtoService;
_userDataManager = userDataManager; _userDataManager = userDataManager;
@ -132,8 +127,6 @@ namespace Emby.Server.Implementations.LiveTv
foreach (var service in _services) foreach (var service in _services)
{ {
service.DataSourceChanged += service_DataSourceChanged;
if (service is EmbyTV.EmbyTV embyTv) if (service is EmbyTV.EmbyTV embyTv)
{ {
embyTv.TimerCreated += EmbyTv_TimerCreated; embyTv.TimerCreated += EmbyTv_TimerCreated;
@ -189,14 +182,6 @@ namespace Emby.Server.Implementations.LiveTv
return EmbyTV.EmbyTV.Current.DiscoverTuners(newDevicesOnly, cancellationToken); return EmbyTV.EmbyTV.Current.DiscoverTuners(newDevicesOnly, cancellationToken);
} }
void service_DataSourceChanged(object sender, EventArgs e)
{
if (!_isDisposed)
{
_taskManager.CancelIfRunningAndQueue<RefreshChannelsScheduledTask>();
}
}
public QueryResult<BaseItem> GetInternalChannels(LiveTvChannelQuery query, DtoOptions dtoOptions, CancellationToken cancellationToken) public QueryResult<BaseItem> GetInternalChannels(LiveTvChannelQuery query, DtoOptions dtoOptions, CancellationToken cancellationToken)
{ {
var user = query.UserId.Equals(Guid.Empty) ? null : _userManager.GetUserById(query.UserId); var user = query.UserId.Equals(Guid.Empty) ? null : _userManager.GetUserById(query.UserId);
@ -2158,17 +2143,28 @@ namespace Emby.Server.Implementations.LiveTv
Dispose(true); Dispose(true);
} }
private bool _isDisposed = false; private bool _disposed = false;
/// <summary> /// <summary>
/// Releases unmanaged and - optionally - managed resources. /// Releases unmanaged and - optionally - managed resources.
/// </summary> /// </summary>
/// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param> /// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
protected virtual void Dispose(bool dispose) protected virtual void Dispose(bool dispose)
{ {
if (_disposed)
{
return;
}
if (dispose) if (dispose)
{ {
_isDisposed = true; // TODO: Dispose stuff
} }
_services = null;
_listingProviders = null;
_tunerHosts = null;
_disposed = true;
} }
private LiveTvServiceInfo[] GetServiceInfos() private LiveTvServiceInfo[] GetServiceInfos()
@ -2469,7 +2465,8 @@ namespace Emby.Server.Implementations.LiveTv
.Where(i => i != null) .Where(i => i != null)
.Where(i => i.IsVisibleStandalone(user)) .Where(i => i.IsVisibleStandalone(user))
.SelectMany(i => _libraryManager.GetCollectionFolders(i)) .SelectMany(i => _libraryManager.GetCollectionFolders(i))
.DistinctBy(i => i.Id) .GroupBy(x => x.Id)
.Select(x => x.First())
.OrderBy(i => i.SortName) .OrderBy(i => i.SortName)
.ToList(); .ToList();

View File

@ -94,7 +94,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
var now = DateTime.UtcNow; var now = DateTime.UtcNow;
StartStreaming(response, taskCompletionSource, LiveStreamCancellationTokenSource.Token); var _ = StartStreaming(response, taskCompletionSource, LiveStreamCancellationTokenSource.Token);
//OpenedMediaSource.Protocol = MediaProtocol.File; //OpenedMediaSource.Protocol = MediaProtocol.File;
//OpenedMediaSource.Path = tempFile; //OpenedMediaSource.Path = tempFile;

View File

@ -14,7 +14,6 @@
"FailedLoginAttemptWithUserName": "عملية تسجيل الدخول فشلت من {0}", "FailedLoginAttemptWithUserName": "عملية تسجيل الدخول فشلت من {0}",
"Favorites": "المفضلات", "Favorites": "المفضلات",
"Folders": "المجلدات", "Folders": "المجلدات",
"Games": "الألعاب",
"Genres": "أنواع الأفلام", "Genres": "أنواع الأفلام",
"HeaderAlbumArtists": "فنانو الألبومات", "HeaderAlbumArtists": "فنانو الألبومات",
"HeaderCameraUploads": "Camera Uploads", "HeaderCameraUploads": "Camera Uploads",
@ -51,8 +50,6 @@
"NotificationOptionAudioPlayback": "بدأ تشغيل المقطع الصوتي", "NotificationOptionAudioPlayback": "بدأ تشغيل المقطع الصوتي",
"NotificationOptionAudioPlaybackStopped": "تم إيقاف تشغيل المقطع الصوتي", "NotificationOptionAudioPlaybackStopped": "تم إيقاف تشغيل المقطع الصوتي",
"NotificationOptionCameraImageUploaded": "تم رقع صورة الكاميرا", "NotificationOptionCameraImageUploaded": "تم رقع صورة الكاميرا",
"NotificationOptionGamePlayback": "تم تشغيل اللعبة",
"NotificationOptionGamePlaybackStopped": "تم إيقاف تشغيل اللعبة",
"NotificationOptionInstallationFailed": "عملية التنصيب فشلت", "NotificationOptionInstallationFailed": "عملية التنصيب فشلت",
"NotificationOptionNewLibraryContent": "تم إضافة محتوى جديد", "NotificationOptionNewLibraryContent": "تم إضافة محتوى جديد",
"NotificationOptionPluginError": "فشل في الملحق", "NotificationOptionPluginError": "فشل في الملحق",

View File

@ -14,7 +14,6 @@
"FailedLoginAttemptWithUserName": "Failed login attempt from {0}", "FailedLoginAttemptWithUserName": "Failed login attempt from {0}",
"Favorites": "Любими", "Favorites": "Любими",
"Folders": "Папки", "Folders": "Папки",
"Games": "Игри",
"Genres": "Жанрове", "Genres": "Жанрове",
"HeaderAlbumArtists": "Изпълнители на албуми", "HeaderAlbumArtists": "Изпълнители на албуми",
"HeaderCameraUploads": "Camera Uploads", "HeaderCameraUploads": "Camera Uploads",
@ -51,8 +50,6 @@
"NotificationOptionAudioPlayback": "Възпроизвеждането на звук започна", "NotificationOptionAudioPlayback": "Възпроизвеждането на звук започна",
"NotificationOptionAudioPlaybackStopped": "Възпроизвеждането на звук е спряно", "NotificationOptionAudioPlaybackStopped": "Възпроизвеждането на звук е спряно",
"NotificationOptionCameraImageUploaded": "Изображението от фотоапарата е качено", "NotificationOptionCameraImageUploaded": "Изображението от фотоапарата е качено",
"NotificationOptionGamePlayback": "Възпроизвеждането на играта започна",
"NotificationOptionGamePlaybackStopped": "Възпроизвеждането на играта е спряна",
"NotificationOptionInstallationFailed": "Неуспешно инсталиране", "NotificationOptionInstallationFailed": "Неуспешно инсталиране",
"NotificationOptionNewLibraryContent": "Добавено е ново съдържание", "NotificationOptionNewLibraryContent": "Добавено е ново съдържание",
"NotificationOptionPluginError": "Грешка в приставка", "NotificationOptionPluginError": "Грешка в приставка",

View File

@ -14,7 +14,6 @@
"FailedLoginAttemptWithUserName": "Intent de connexió fallit des de {0}", "FailedLoginAttemptWithUserName": "Intent de connexió fallit des de {0}",
"Favorites": "Preferits", "Favorites": "Preferits",
"Folders": "Directoris", "Folders": "Directoris",
"Games": "Jocs",
"Genres": "Gèneres", "Genres": "Gèneres",
"HeaderAlbumArtists": "Album Artists", "HeaderAlbumArtists": "Album Artists",
"HeaderCameraUploads": "Camera Uploads", "HeaderCameraUploads": "Camera Uploads",
@ -51,8 +50,6 @@
"NotificationOptionAudioPlayback": "Audio playback started", "NotificationOptionAudioPlayback": "Audio playback started",
"NotificationOptionAudioPlaybackStopped": "Audio playback stopped", "NotificationOptionAudioPlaybackStopped": "Audio playback stopped",
"NotificationOptionCameraImageUploaded": "Camera image uploaded", "NotificationOptionCameraImageUploaded": "Camera image uploaded",
"NotificationOptionGamePlayback": "Game playback started",
"NotificationOptionGamePlaybackStopped": "Game playback stopped",
"NotificationOptionInstallationFailed": "Installation failure", "NotificationOptionInstallationFailed": "Installation failure",
"NotificationOptionNewLibraryContent": "New content added", "NotificationOptionNewLibraryContent": "New content added",
"NotificationOptionPluginError": "Un component ha fallat", "NotificationOptionPluginError": "Un component ha fallat",

View File

@ -14,7 +14,6 @@
"FailedLoginAttemptWithUserName": "Neúspěšný pokus o přihlášení z {0}", "FailedLoginAttemptWithUserName": "Neúspěšný pokus o přihlášení z {0}",
"Favorites": "Oblíbené", "Favorites": "Oblíbené",
"Folders": "Složky", "Folders": "Složky",
"Games": "Hry",
"Genres": "Žánry", "Genres": "Žánry",
"HeaderAlbumArtists": "Umělci alba", "HeaderAlbumArtists": "Umělci alba",
"HeaderCameraUploads": "Camera Uploads", "HeaderCameraUploads": "Camera Uploads",
@ -51,8 +50,6 @@
"NotificationOptionAudioPlayback": "Přehrávání audia zahájeno", "NotificationOptionAudioPlayback": "Přehrávání audia zahájeno",
"NotificationOptionAudioPlaybackStopped": "Přehrávání audia ukončeno", "NotificationOptionAudioPlaybackStopped": "Přehrávání audia ukončeno",
"NotificationOptionCameraImageUploaded": "Kamerový záznam nahrán", "NotificationOptionCameraImageUploaded": "Kamerový záznam nahrán",
"NotificationOptionGamePlayback": "Spuštění hry zahájeno",
"NotificationOptionGamePlaybackStopped": "Hra ukončena",
"NotificationOptionInstallationFailed": "Chyba instalace", "NotificationOptionInstallationFailed": "Chyba instalace",
"NotificationOptionNewLibraryContent": "Přidán nový obsah", "NotificationOptionNewLibraryContent": "Přidán nový obsah",
"NotificationOptionPluginError": "Chyba zásuvného modulu", "NotificationOptionPluginError": "Chyba zásuvného modulu",

View File

@ -14,7 +14,6 @@
"FailedLoginAttemptWithUserName": "Fejlet loginforsøg fra {0}", "FailedLoginAttemptWithUserName": "Fejlet loginforsøg fra {0}",
"Favorites": "Favoritter", "Favorites": "Favoritter",
"Folders": "Mapper", "Folders": "Mapper",
"Games": "Spil",
"Genres": "Genre", "Genres": "Genre",
"HeaderAlbumArtists": "Albumkunstnere", "HeaderAlbumArtists": "Albumkunstnere",
"HeaderCameraUploads": "Camera Uploads", "HeaderCameraUploads": "Camera Uploads",
@ -51,8 +50,6 @@
"NotificationOptionAudioPlayback": "Audioafspilning påbegyndt", "NotificationOptionAudioPlayback": "Audioafspilning påbegyndt",
"NotificationOptionAudioPlaybackStopped": "Audioafspilning stoppet", "NotificationOptionAudioPlaybackStopped": "Audioafspilning stoppet",
"NotificationOptionCameraImageUploaded": "Kamerabillede uploadet", "NotificationOptionCameraImageUploaded": "Kamerabillede uploadet",
"NotificationOptionGamePlayback": "Afspilning af Spil påbegyndt",
"NotificationOptionGamePlaybackStopped": "Afspilning af Spil stoppet",
"NotificationOptionInstallationFailed": "Installationsfejl", "NotificationOptionInstallationFailed": "Installationsfejl",
"NotificationOptionNewLibraryContent": "Nyt indhold tilføjet", "NotificationOptionNewLibraryContent": "Nyt indhold tilføjet",
"NotificationOptionPluginError": "Pluginfejl", "NotificationOptionPluginError": "Pluginfejl",

View File

@ -14,7 +14,6 @@
"FailedLoginAttemptWithUserName": "Fehlgeschlagener Anmeldeversuch von {0}", "FailedLoginAttemptWithUserName": "Fehlgeschlagener Anmeldeversuch von {0}",
"Favorites": "Favoriten", "Favorites": "Favoriten",
"Folders": "Verzeichnisse", "Folders": "Verzeichnisse",
"Games": "Spiele",
"Genres": "Genres", "Genres": "Genres",
"HeaderAlbumArtists": "Album-Künstler", "HeaderAlbumArtists": "Album-Künstler",
"HeaderCameraUploads": "Kamera Uploads", "HeaderCameraUploads": "Kamera Uploads",
@ -51,8 +50,6 @@
"NotificationOptionAudioPlayback": "Audiowiedergabe gestartet", "NotificationOptionAudioPlayback": "Audiowiedergabe gestartet",
"NotificationOptionAudioPlaybackStopped": "Audiowiedergabe gestoppt", "NotificationOptionAudioPlaybackStopped": "Audiowiedergabe gestoppt",
"NotificationOptionCameraImageUploaded": "Kamera Bild hochgeladen", "NotificationOptionCameraImageUploaded": "Kamera Bild hochgeladen",
"NotificationOptionGamePlayback": "Spielwiedergabe gestartet",
"NotificationOptionGamePlaybackStopped": "Spielwiedergabe gestoppt",
"NotificationOptionInstallationFailed": "Installationsfehler", "NotificationOptionInstallationFailed": "Installationsfehler",
"NotificationOptionNewLibraryContent": "Neuer Inhalt hinzugefügt", "NotificationOptionNewLibraryContent": "Neuer Inhalt hinzugefügt",
"NotificationOptionPluginError": "Plugin Fehler", "NotificationOptionPluginError": "Plugin Fehler",

View File

@ -14,7 +14,6 @@
"FailedLoginAttemptWithUserName": "Αποτυχημένη προσπάθεια σύνδεσης από {0}", "FailedLoginAttemptWithUserName": "Αποτυχημένη προσπάθεια σύνδεσης από {0}",
"Favorites": "Αγαπημένα", "Favorites": "Αγαπημένα",
"Folders": "Φάκελοι", "Folders": "Φάκελοι",
"Games": "Παιχνίδια",
"Genres": "Είδη", "Genres": "Είδη",
"HeaderAlbumArtists": "Άλμπουμ Καλλιτεχνών", "HeaderAlbumArtists": "Άλμπουμ Καλλιτεχνών",
"HeaderCameraUploads": "Camera Uploads", "HeaderCameraUploads": "Camera Uploads",
@ -51,8 +50,6 @@
"NotificationOptionAudioPlayback": "Η αναπαραγωγή ήχου ξεκίνησε", "NotificationOptionAudioPlayback": "Η αναπαραγωγή ήχου ξεκίνησε",
"NotificationOptionAudioPlaybackStopped": "Η αναπαραγωγή ήχου σταμάτησε", "NotificationOptionAudioPlaybackStopped": "Η αναπαραγωγή ήχου σταμάτησε",
"NotificationOptionCameraImageUploaded": "Camera image uploaded", "NotificationOptionCameraImageUploaded": "Camera image uploaded",
"NotificationOptionGamePlayback": "Η αναπαραγωγή του παιχνιδιού ξεκίνησε",
"NotificationOptionGamePlaybackStopped": "Η αναπαραγωγή του παιχνιδιού σταμάτησε",
"NotificationOptionInstallationFailed": "Αποτυχία εγκατάστασης", "NotificationOptionInstallationFailed": "Αποτυχία εγκατάστασης",
"NotificationOptionNewLibraryContent": "Προστέθηκε νέο περιεχόμενο", "NotificationOptionNewLibraryContent": "Προστέθηκε νέο περιεχόμενο",
"NotificationOptionPluginError": "Αποτυχία του plugin", "NotificationOptionPluginError": "Αποτυχία του plugin",

View File

@ -14,7 +14,6 @@
"FailedLoginAttemptWithUserName": "Failed login attempt from {0}", "FailedLoginAttemptWithUserName": "Failed login attempt from {0}",
"Favorites": "Favourites", "Favorites": "Favourites",
"Folders": "Folders", "Folders": "Folders",
"Games": "Games",
"Genres": "Genres", "Genres": "Genres",
"HeaderAlbumArtists": "Album Artists", "HeaderAlbumArtists": "Album Artists",
"HeaderCameraUploads": "Camera Uploads", "HeaderCameraUploads": "Camera Uploads",
@ -51,8 +50,6 @@
"NotificationOptionAudioPlayback": "Audio playback started", "NotificationOptionAudioPlayback": "Audio playback started",
"NotificationOptionAudioPlaybackStopped": "Audio playback stopped", "NotificationOptionAudioPlaybackStopped": "Audio playback stopped",
"NotificationOptionCameraImageUploaded": "Camera image uploaded", "NotificationOptionCameraImageUploaded": "Camera image uploaded",
"NotificationOptionGamePlayback": "Game playback started",
"NotificationOptionGamePlaybackStopped": "Game playback stopped",
"NotificationOptionInstallationFailed": "Installation failure", "NotificationOptionInstallationFailed": "Installation failure",
"NotificationOptionNewLibraryContent": "New content added", "NotificationOptionNewLibraryContent": "New content added",
"NotificationOptionPluginError": "Plugin failure", "NotificationOptionPluginError": "Plugin failure",

View File

@ -14,7 +14,6 @@
"FailedLoginAttemptWithUserName": "Failed login attempt from {0}", "FailedLoginAttemptWithUserName": "Failed login attempt from {0}",
"Favorites": "Favorites", "Favorites": "Favorites",
"Folders": "Folders", "Folders": "Folders",
"Games": "Games",
"Genres": "Genres", "Genres": "Genres",
"HeaderAlbumArtists": "Album Artists", "HeaderAlbumArtists": "Album Artists",
"HeaderCameraUploads": "Camera Uploads", "HeaderCameraUploads": "Camera Uploads",
@ -51,8 +50,6 @@
"NotificationOptionAudioPlayback": "Audio playback started", "NotificationOptionAudioPlayback": "Audio playback started",
"NotificationOptionAudioPlaybackStopped": "Audio playback stopped", "NotificationOptionAudioPlaybackStopped": "Audio playback stopped",
"NotificationOptionCameraImageUploaded": "Camera image uploaded", "NotificationOptionCameraImageUploaded": "Camera image uploaded",
"NotificationOptionGamePlayback": "Game playback started",
"NotificationOptionGamePlaybackStopped": "Game playback stopped",
"NotificationOptionInstallationFailed": "Installation failure", "NotificationOptionInstallationFailed": "Installation failure",
"NotificationOptionNewLibraryContent": "New content added", "NotificationOptionNewLibraryContent": "New content added",
"NotificationOptionPluginError": "Plugin failure", "NotificationOptionPluginError": "Plugin failure",

View File

@ -14,7 +14,6 @@
"FailedLoginAttemptWithUserName": "Failed login attempt from {0}", "FailedLoginAttemptWithUserName": "Failed login attempt from {0}",
"Favorites": "Favorites", "Favorites": "Favorites",
"Folders": "Folders", "Folders": "Folders",
"Games": "Games",
"Genres": "Genres", "Genres": "Genres",
"HeaderAlbumArtists": "Album Artists", "HeaderAlbumArtists": "Album Artists",
"HeaderCameraUploads": "Camera Uploads", "HeaderCameraUploads": "Camera Uploads",
@ -51,8 +50,6 @@
"NotificationOptionAudioPlayback": "Audio playback started", "NotificationOptionAudioPlayback": "Audio playback started",
"NotificationOptionAudioPlaybackStopped": "Audio playback stopped", "NotificationOptionAudioPlaybackStopped": "Audio playback stopped",
"NotificationOptionCameraImageUploaded": "Camera image uploaded", "NotificationOptionCameraImageUploaded": "Camera image uploaded",
"NotificationOptionGamePlayback": "Game playback started",
"NotificationOptionGamePlaybackStopped": "Game playback stopped",
"NotificationOptionInstallationFailed": "Installation failure", "NotificationOptionInstallationFailed": "Installation failure",
"NotificationOptionNewLibraryContent": "New content added", "NotificationOptionNewLibraryContent": "New content added",
"NotificationOptionPluginError": "Plugin failure", "NotificationOptionPluginError": "Plugin failure",

View File

@ -14,7 +14,6 @@
"FailedLoginAttemptWithUserName": "Intento fallido de inicio de sesión de {0}", "FailedLoginAttemptWithUserName": "Intento fallido de inicio de sesión de {0}",
"Favorites": "Favoritos", "Favorites": "Favoritos",
"Folders": "Carpetas", "Folders": "Carpetas",
"Games": "Juegos",
"Genres": "Géneros", "Genres": "Géneros",
"HeaderAlbumArtists": "Artistas del Álbum", "HeaderAlbumArtists": "Artistas del Álbum",
"HeaderCameraUploads": "Subidos desde Camara", "HeaderCameraUploads": "Subidos desde Camara",
@ -51,8 +50,6 @@
"NotificationOptionAudioPlayback": "Reproducción de audio iniciada", "NotificationOptionAudioPlayback": "Reproducción de audio iniciada",
"NotificationOptionAudioPlaybackStopped": "Reproducción de audio detenida", "NotificationOptionAudioPlaybackStopped": "Reproducción de audio detenida",
"NotificationOptionCameraImageUploaded": "Imagen de la cámara subida", "NotificationOptionCameraImageUploaded": "Imagen de la cámara subida",
"NotificationOptionGamePlayback": "Ejecución de juego iniciada",
"NotificationOptionGamePlaybackStopped": "Ejecución de juego detenida",
"NotificationOptionInstallationFailed": "Falla de instalación", "NotificationOptionInstallationFailed": "Falla de instalación",
"NotificationOptionNewLibraryContent": "Nuevo contenido agregado", "NotificationOptionNewLibraryContent": "Nuevo contenido agregado",
"NotificationOptionPluginError": "Falla de complemento", "NotificationOptionPluginError": "Falla de complemento",

View File

@ -14,7 +14,6 @@
"FailedLoginAttemptWithUserName": "Error al intentar iniciar sesión a partir de {0}", "FailedLoginAttemptWithUserName": "Error al intentar iniciar sesión a partir de {0}",
"Favorites": "Favoritos", "Favorites": "Favoritos",
"Folders": "Carpetas", "Folders": "Carpetas",
"Games": "Juegos",
"Genres": "Géneros", "Genres": "Géneros",
"HeaderAlbumArtists": "Artistas del Álbum", "HeaderAlbumArtists": "Artistas del Álbum",
"HeaderCameraUploads": "Camera Uploads", "HeaderCameraUploads": "Camera Uploads",
@ -51,8 +50,6 @@
"NotificationOptionAudioPlayback": "Se inició la reproducción de audio", "NotificationOptionAudioPlayback": "Se inició la reproducción de audio",
"NotificationOptionAudioPlaybackStopped": "Se detuvo la reproducción de audio", "NotificationOptionAudioPlaybackStopped": "Se detuvo la reproducción de audio",
"NotificationOptionCameraImageUploaded": "Imagen de la cámara cargada", "NotificationOptionCameraImageUploaded": "Imagen de la cámara cargada",
"NotificationOptionGamePlayback": "Se inició la reproducción del juego",
"NotificationOptionGamePlaybackStopped": "Se detuvo la reproducción del juego",
"NotificationOptionInstallationFailed": "Error de instalación", "NotificationOptionInstallationFailed": "Error de instalación",
"NotificationOptionNewLibraryContent": "Nuevo contenido añadido", "NotificationOptionNewLibraryContent": "Nuevo contenido añadido",
"NotificationOptionPluginError": "Error en plugin", "NotificationOptionPluginError": "Error en plugin",

View File

@ -14,7 +14,6 @@
"FailedLoginAttemptWithUserName": "تلاش برای ورود از {0} ناموفق بود", "FailedLoginAttemptWithUserName": "تلاش برای ورود از {0} ناموفق بود",
"Favorites": "مورد علاقه ها", "Favorites": "مورد علاقه ها",
"Folders": "پوشه ها", "Folders": "پوشه ها",
"Games": "بازی ها",
"Genres": "ژانرها", "Genres": "ژانرها",
"HeaderAlbumArtists": "هنرمندان آلبوم", "HeaderAlbumArtists": "هنرمندان آلبوم",
"HeaderCameraUploads": "آپلودهای دوربین", "HeaderCameraUploads": "آپلودهای دوربین",
@ -51,8 +50,6 @@
"NotificationOptionAudioPlayback": "پخش صدا آغاز شد", "NotificationOptionAudioPlayback": "پخش صدا آغاز شد",
"NotificationOptionAudioPlaybackStopped": "پخش صدا متوقف شد", "NotificationOptionAudioPlaybackStopped": "پخش صدا متوقف شد",
"NotificationOptionCameraImageUploaded": "تصاویر دوربین آپلود شد", "NotificationOptionCameraImageUploaded": "تصاویر دوربین آپلود شد",
"NotificationOptionGamePlayback": "پخش بازی آغاز شد",
"NotificationOptionGamePlaybackStopped": "پخش بازی متوقف شد",
"NotificationOptionInstallationFailed": "شکست نصب", "NotificationOptionInstallationFailed": "شکست نصب",
"NotificationOptionNewLibraryContent": "محتوای جدید افزوده شد", "NotificationOptionNewLibraryContent": "محتوای جدید افزوده شد",
"NotificationOptionPluginError": "خرابی افزونه", "NotificationOptionPluginError": "خرابی افزونه",

View File

@ -14,7 +14,6 @@
"FailedLoginAttemptWithUserName": "Failed login attempt from {0}", "FailedLoginAttemptWithUserName": "Failed login attempt from {0}",
"Favorites": "Favorites", "Favorites": "Favorites",
"Folders": "Folders", "Folders": "Folders",
"Games": "Games",
"Genres": "Genres", "Genres": "Genres",
"HeaderAlbumArtists": "Album Artists", "HeaderAlbumArtists": "Album Artists",
"HeaderCameraUploads": "Camera Uploads", "HeaderCameraUploads": "Camera Uploads",
@ -51,8 +50,6 @@
"NotificationOptionAudioPlayback": "Audio playback started", "NotificationOptionAudioPlayback": "Audio playback started",
"NotificationOptionAudioPlaybackStopped": "Audio playback stopped", "NotificationOptionAudioPlaybackStopped": "Audio playback stopped",
"NotificationOptionCameraImageUploaded": "Camera image uploaded", "NotificationOptionCameraImageUploaded": "Camera image uploaded",
"NotificationOptionGamePlayback": "Game playback started",
"NotificationOptionGamePlaybackStopped": "Game playback stopped",
"NotificationOptionInstallationFailed": "Installation failure", "NotificationOptionInstallationFailed": "Installation failure",
"NotificationOptionNewLibraryContent": "New content added", "NotificationOptionNewLibraryContent": "New content added",
"NotificationOptionPluginError": "Plugin failure", "NotificationOptionPluginError": "Plugin failure",

View File

@ -14,7 +14,6 @@
"FailedLoginAttemptWithUserName": "Échec d'une tentative de connexion de {0}", "FailedLoginAttemptWithUserName": "Échec d'une tentative de connexion de {0}",
"Favorites": "Favoris", "Favorites": "Favoris",
"Folders": "Dossiers", "Folders": "Dossiers",
"Games": "Jeux",
"Genres": "Genres", "Genres": "Genres",
"HeaderAlbumArtists": "Artistes de l'album", "HeaderAlbumArtists": "Artistes de l'album",
"HeaderCameraUploads": "Photos transférées", "HeaderCameraUploads": "Photos transférées",
@ -51,8 +50,6 @@
"NotificationOptionAudioPlayback": "Lecture audio démarrée", "NotificationOptionAudioPlayback": "Lecture audio démarrée",
"NotificationOptionAudioPlaybackStopped": "Lecture audio arrêtée", "NotificationOptionAudioPlaybackStopped": "Lecture audio arrêtée",
"NotificationOptionCameraImageUploaded": "L'image de l'appareil photo a été transférée", "NotificationOptionCameraImageUploaded": "L'image de l'appareil photo a été transférée",
"NotificationOptionGamePlayback": "Lecture de jeu démarrée",
"NotificationOptionGamePlaybackStopped": "Lecture de jeu arrêtée",
"NotificationOptionInstallationFailed": "Échec d'installation", "NotificationOptionInstallationFailed": "Échec d'installation",
"NotificationOptionNewLibraryContent": "Nouveau contenu ajouté", "NotificationOptionNewLibraryContent": "Nouveau contenu ajouté",
"NotificationOptionPluginError": "Erreur d'extension", "NotificationOptionPluginError": "Erreur d'extension",

View File

@ -14,7 +14,6 @@
"FailedLoginAttemptWithUserName": "Failed login attempt from {0}", "FailedLoginAttemptWithUserName": "Failed login attempt from {0}",
"Favorites": "Favorites", "Favorites": "Favorites",
"Folders": "Folders", "Folders": "Folders",
"Games": "Spiel",
"Genres": "Genres", "Genres": "Genres",
"HeaderAlbumArtists": "Albuminterprete", "HeaderAlbumArtists": "Albuminterprete",
"HeaderCameraUploads": "Camera Uploads", "HeaderCameraUploads": "Camera Uploads",
@ -51,8 +50,6 @@
"NotificationOptionAudioPlayback": "Audio playback started", "NotificationOptionAudioPlayback": "Audio playback started",
"NotificationOptionAudioPlaybackStopped": "Audio playback stopped", "NotificationOptionAudioPlaybackStopped": "Audio playback stopped",
"NotificationOptionCameraImageUploaded": "Camera image uploaded", "NotificationOptionCameraImageUploaded": "Camera image uploaded",
"NotificationOptionGamePlayback": "Game playback started",
"NotificationOptionGamePlaybackStopped": "Game playback stopped",
"NotificationOptionInstallationFailed": "Installation failure", "NotificationOptionInstallationFailed": "Installation failure",
"NotificationOptionNewLibraryContent": "New content added", "NotificationOptionNewLibraryContent": "New content added",
"NotificationOptionPluginError": "Plugin failure", "NotificationOptionPluginError": "Plugin failure",

View File

@ -14,7 +14,6 @@
"FailedLoginAttemptWithUserName": "Failed login attempt from {0}", "FailedLoginAttemptWithUserName": "Failed login attempt from {0}",
"Favorites": "Favorites", "Favorites": "Favorites",
"Folders": "Folders", "Folders": "Folders",
"Games": "משחקים",
"Genres": "ז'אנרים", "Genres": "ז'אנרים",
"HeaderAlbumArtists": "Album Artists", "HeaderAlbumArtists": "Album Artists",
"HeaderCameraUploads": "Camera Uploads", "HeaderCameraUploads": "Camera Uploads",
@ -51,8 +50,6 @@
"NotificationOptionAudioPlayback": "Audio playback started", "NotificationOptionAudioPlayback": "Audio playback started",
"NotificationOptionAudioPlaybackStopped": "Audio playback stopped", "NotificationOptionAudioPlaybackStopped": "Audio playback stopped",
"NotificationOptionCameraImageUploaded": "Camera image uploaded", "NotificationOptionCameraImageUploaded": "Camera image uploaded",
"NotificationOptionGamePlayback": "Game playback started",
"NotificationOptionGamePlaybackStopped": "Game playback stopped",
"NotificationOptionInstallationFailed": "Installation failure", "NotificationOptionInstallationFailed": "Installation failure",
"NotificationOptionNewLibraryContent": "New content added", "NotificationOptionNewLibraryContent": "New content added",
"NotificationOptionPluginError": "Plugin failure", "NotificationOptionPluginError": "Plugin failure",

View File

@ -14,7 +14,6 @@
"FailedLoginAttemptWithUserName": "Neuspjeli pokušaj prijave za {0}", "FailedLoginAttemptWithUserName": "Neuspjeli pokušaj prijave za {0}",
"Favorites": "Omiljeni", "Favorites": "Omiljeni",
"Folders": "Mape", "Folders": "Mape",
"Games": "Igre",
"Genres": "Žanrovi", "Genres": "Žanrovi",
"HeaderAlbumArtists": "Izvođači albuma", "HeaderAlbumArtists": "Izvođači albuma",
"HeaderCameraUploads": "Camera Uploads", "HeaderCameraUploads": "Camera Uploads",
@ -51,8 +50,6 @@
"NotificationOptionAudioPlayback": "Reprodukcija glazbe započeta", "NotificationOptionAudioPlayback": "Reprodukcija glazbe započeta",
"NotificationOptionAudioPlaybackStopped": "Reprodukcija audiozapisa je zaustavljena", "NotificationOptionAudioPlaybackStopped": "Reprodukcija audiozapisa je zaustavljena",
"NotificationOptionCameraImageUploaded": "Slike kamere preuzete", "NotificationOptionCameraImageUploaded": "Slike kamere preuzete",
"NotificationOptionGamePlayback": "Igrica pokrenuta",
"NotificationOptionGamePlaybackStopped": "Reprodukcija igre je zaustavljena",
"NotificationOptionInstallationFailed": "Instalacija nije izvršena", "NotificationOptionInstallationFailed": "Instalacija nije izvršena",
"NotificationOptionNewLibraryContent": "Novi sadržaj je dodan", "NotificationOptionNewLibraryContent": "Novi sadržaj je dodan",
"NotificationOptionPluginError": "Dodatak otkazao", "NotificationOptionPluginError": "Dodatak otkazao",

View File

@ -14,7 +14,6 @@
"FailedLoginAttemptWithUserName": "Failed login attempt from {0}", "FailedLoginAttemptWithUserName": "Failed login attempt from {0}",
"Favorites": "Kedvencek", "Favorites": "Kedvencek",
"Folders": "Könyvtárak", "Folders": "Könyvtárak",
"Games": "Játékok",
"Genres": "Műfajok", "Genres": "Műfajok",
"HeaderAlbumArtists": "Album Előadók", "HeaderAlbumArtists": "Album Előadók",
"HeaderCameraUploads": "Camera Uploads", "HeaderCameraUploads": "Camera Uploads",
@ -51,8 +50,6 @@
"NotificationOptionAudioPlayback": "Audió lejátszás elkezdve", "NotificationOptionAudioPlayback": "Audió lejátszás elkezdve",
"NotificationOptionAudioPlaybackStopped": "Audió lejátszás befejezve", "NotificationOptionAudioPlaybackStopped": "Audió lejátszás befejezve",
"NotificationOptionCameraImageUploaded": "Kamera kép feltöltve", "NotificationOptionCameraImageUploaded": "Kamera kép feltöltve",
"NotificationOptionGamePlayback": "Game playback started",
"NotificationOptionGamePlaybackStopped": "Game playback stopped",
"NotificationOptionInstallationFailed": "Telepítési hiba", "NotificationOptionInstallationFailed": "Telepítési hiba",
"NotificationOptionNewLibraryContent": "Új tartalom hozzáadva", "NotificationOptionNewLibraryContent": "Új tartalom hozzáadva",
"NotificationOptionPluginError": "Bővítmény hiba", "NotificationOptionPluginError": "Bővítmény hiba",

View File

@ -14,7 +14,6 @@
"FailedLoginAttemptWithUserName": "Tentativo di accesso fallito da {0}", "FailedLoginAttemptWithUserName": "Tentativo di accesso fallito da {0}",
"Favorites": "Preferiti", "Favorites": "Preferiti",
"Folders": "Cartelle", "Folders": "Cartelle",
"Games": "Giochi",
"Genres": "Generi", "Genres": "Generi",
"HeaderAlbumArtists": "Artisti Album", "HeaderAlbumArtists": "Artisti Album",
"HeaderCameraUploads": "Caricamenti Fotocamera", "HeaderCameraUploads": "Caricamenti Fotocamera",
@ -51,8 +50,6 @@
"NotificationOptionAudioPlayback": "La riproduzione audio è iniziata", "NotificationOptionAudioPlayback": "La riproduzione audio è iniziata",
"NotificationOptionAudioPlaybackStopped": "La riproduzione audio è stata interrotta", "NotificationOptionAudioPlaybackStopped": "La riproduzione audio è stata interrotta",
"NotificationOptionCameraImageUploaded": "Immagine fotocamera caricata", "NotificationOptionCameraImageUploaded": "Immagine fotocamera caricata",
"NotificationOptionGamePlayback": "Il gioco è stato avviato",
"NotificationOptionGamePlaybackStopped": "Il gioco è stato fermato",
"NotificationOptionInstallationFailed": "Installazione fallita", "NotificationOptionInstallationFailed": "Installazione fallita",
"NotificationOptionNewLibraryContent": "Nuovo contenuto aggiunto", "NotificationOptionNewLibraryContent": "Nuovo contenuto aggiunto",
"NotificationOptionPluginError": "Errore del Plug-in", "NotificationOptionPluginError": "Errore del Plug-in",

View File

@ -14,7 +14,6 @@
"FailedLoginAttemptWithUserName": "{0} тарапынан кіру әрекеті сәтсіз", "FailedLoginAttemptWithUserName": "{0} тарапынан кіру әрекеті сәтсіз",
"Favorites": "Таңдаулылар", "Favorites": "Таңдаулылар",
"Folders": "Қалталар", "Folders": "Қалталар",
"Games": "Ойындар",
"Genres": "Жанрлар", "Genres": "Жанрлар",
"HeaderAlbumArtists": "Альбом орындаушылары", "HeaderAlbumArtists": "Альбом орындаушылары",
"HeaderCameraUploads": "Камерадан жүктелгендер", "HeaderCameraUploads": "Камерадан жүктелгендер",
@ -51,8 +50,6 @@
"NotificationOptionAudioPlayback": "Дыбыс ойнатуы басталды", "NotificationOptionAudioPlayback": "Дыбыс ойнатуы басталды",
"NotificationOptionAudioPlaybackStopped": "Дыбыс ойнатуы тоқтатылды", "NotificationOptionAudioPlaybackStopped": "Дыбыс ойнатуы тоқтатылды",
"NotificationOptionCameraImageUploaded": "Камерадан фотосурет кері қотарылған", "NotificationOptionCameraImageUploaded": "Камерадан фотосурет кері қотарылған",
"NotificationOptionGamePlayback": "Ойын ойнатуы басталды",
"NotificationOptionGamePlaybackStopped": "Ойын ойнатуы тоқтатылды",
"NotificationOptionInstallationFailed": "Орнату сәтсіздігі", "NotificationOptionInstallationFailed": "Орнату сәтсіздігі",
"NotificationOptionNewLibraryContent": "Жаңа мазмұн үстелген", "NotificationOptionNewLibraryContent": "Жаңа мазмұн үстелген",
"NotificationOptionPluginError": "Плагин сәтсіздігі", "NotificationOptionPluginError": "Плагин сәтсіздігі",

View File

@ -14,7 +14,6 @@
"FailedLoginAttemptWithUserName": "Failed login attempt from {0}", "FailedLoginAttemptWithUserName": "Failed login attempt from {0}",
"Favorites": "Favorites", "Favorites": "Favorites",
"Folders": "Folders", "Folders": "Folders",
"Games": "Games",
"Genres": "Genres", "Genres": "Genres",
"HeaderAlbumArtists": "앨범 아티스트", "HeaderAlbumArtists": "앨범 아티스트",
"HeaderCameraUploads": "Camera Uploads", "HeaderCameraUploads": "Camera Uploads",
@ -51,8 +50,6 @@
"NotificationOptionAudioPlayback": "Audio playback started", "NotificationOptionAudioPlayback": "Audio playback started",
"NotificationOptionAudioPlaybackStopped": "Audio playback stopped", "NotificationOptionAudioPlaybackStopped": "Audio playback stopped",
"NotificationOptionCameraImageUploaded": "Camera image uploaded", "NotificationOptionCameraImageUploaded": "Camera image uploaded",
"NotificationOptionGamePlayback": "Game playback started",
"NotificationOptionGamePlaybackStopped": "Game playback stopped",
"NotificationOptionInstallationFailed": "Installation failure", "NotificationOptionInstallationFailed": "Installation failure",
"NotificationOptionNewLibraryContent": "New content added", "NotificationOptionNewLibraryContent": "New content added",
"NotificationOptionPluginError": "Plugin failure", "NotificationOptionPluginError": "Plugin failure",

View File

@ -14,7 +14,6 @@
"FailedLoginAttemptWithUserName": "Failed login attempt from {0}", "FailedLoginAttemptWithUserName": "Failed login attempt from {0}",
"Favorites": "Favorites", "Favorites": "Favorites",
"Folders": "Folders", "Folders": "Folders",
"Games": "Games",
"Genres": "Žanrai", "Genres": "Žanrai",
"HeaderAlbumArtists": "Album Artists", "HeaderAlbumArtists": "Album Artists",
"HeaderCameraUploads": "Camera Uploads", "HeaderCameraUploads": "Camera Uploads",
@ -51,8 +50,6 @@
"NotificationOptionAudioPlayback": "Audio playback started", "NotificationOptionAudioPlayback": "Audio playback started",
"NotificationOptionAudioPlaybackStopped": "Audio playback stopped", "NotificationOptionAudioPlaybackStopped": "Audio playback stopped",
"NotificationOptionCameraImageUploaded": "Camera image uploaded", "NotificationOptionCameraImageUploaded": "Camera image uploaded",
"NotificationOptionGamePlayback": "Game playback started",
"NotificationOptionGamePlaybackStopped": "Game playback stopped",
"NotificationOptionInstallationFailed": "Installation failure", "NotificationOptionInstallationFailed": "Installation failure",
"NotificationOptionNewLibraryContent": "New content added", "NotificationOptionNewLibraryContent": "New content added",
"NotificationOptionPluginError": "Plugin failure", "NotificationOptionPluginError": "Plugin failure",

View File

@ -14,7 +14,6 @@
"FailedLoginAttemptWithUserName": "Failed login attempt from {0}", "FailedLoginAttemptWithUserName": "Failed login attempt from {0}",
"Favorites": "Favorites", "Favorites": "Favorites",
"Folders": "Folders", "Folders": "Folders",
"Games": "Games",
"Genres": "Genres", "Genres": "Genres",
"HeaderAlbumArtists": "Album Artists", "HeaderAlbumArtists": "Album Artists",
"HeaderCameraUploads": "Camera Uploads", "HeaderCameraUploads": "Camera Uploads",
@ -51,8 +50,6 @@
"NotificationOptionAudioPlayback": "Audio playback started", "NotificationOptionAudioPlayback": "Audio playback started",
"NotificationOptionAudioPlaybackStopped": "Audio playback stopped", "NotificationOptionAudioPlaybackStopped": "Audio playback stopped",
"NotificationOptionCameraImageUploaded": "Camera image uploaded", "NotificationOptionCameraImageUploaded": "Camera image uploaded",
"NotificationOptionGamePlayback": "Game playback started",
"NotificationOptionGamePlaybackStopped": "Game playback stopped",
"NotificationOptionInstallationFailed": "Installation failure", "NotificationOptionInstallationFailed": "Installation failure",
"NotificationOptionNewLibraryContent": "New content added", "NotificationOptionNewLibraryContent": "New content added",
"NotificationOptionPluginError": "Plugin failure", "NotificationOptionPluginError": "Plugin failure",

View File

@ -14,7 +14,6 @@
"FailedLoginAttemptWithUserName": "Mislykket påloggingsforsøk fra {0}", "FailedLoginAttemptWithUserName": "Mislykket påloggingsforsøk fra {0}",
"Favorites": "Favoritter", "Favorites": "Favoritter",
"Folders": "Mapper", "Folders": "Mapper",
"Games": "Spill",
"Genres": "Sjanger", "Genres": "Sjanger",
"HeaderAlbumArtists": "Albumartist", "HeaderAlbumArtists": "Albumartist",
"HeaderCameraUploads": "Camera Uploads", "HeaderCameraUploads": "Camera Uploads",
@ -51,8 +50,6 @@
"NotificationOptionAudioPlayback": "Lyd tilbakespilling startet", "NotificationOptionAudioPlayback": "Lyd tilbakespilling startet",
"NotificationOptionAudioPlaybackStopped": "Lyd avspilling stoppet", "NotificationOptionAudioPlaybackStopped": "Lyd avspilling stoppet",
"NotificationOptionCameraImageUploaded": "Kamera bilde lastet opp", "NotificationOptionCameraImageUploaded": "Kamera bilde lastet opp",
"NotificationOptionGamePlayback": "Spill avspillingen startet",
"NotificationOptionGamePlaybackStopped": "Filmer",
"NotificationOptionInstallationFailed": "Installasjon feil", "NotificationOptionInstallationFailed": "Installasjon feil",
"NotificationOptionNewLibraryContent": "Ny innhold er lagt til", "NotificationOptionNewLibraryContent": "Ny innhold er lagt til",
"NotificationOptionPluginError": "Plugin feil", "NotificationOptionPluginError": "Plugin feil",

View File

@ -14,7 +14,6 @@
"FailedLoginAttemptWithUserName": "Mislukte aanmeld poging van {0}", "FailedLoginAttemptWithUserName": "Mislukte aanmeld poging van {0}",
"Favorites": "Favorieten", "Favorites": "Favorieten",
"Folders": "Mappen", "Folders": "Mappen",
"Games": "Spellen",
"Genres": "Genres", "Genres": "Genres",
"HeaderAlbumArtists": "Album artiesten", "HeaderAlbumArtists": "Album artiesten",
"HeaderCameraUploads": "Camera uploads", "HeaderCameraUploads": "Camera uploads",
@ -51,8 +50,6 @@
"NotificationOptionAudioPlayback": "Geluid gestart", "NotificationOptionAudioPlayback": "Geluid gestart",
"NotificationOptionAudioPlaybackStopped": "Geluid gestopt", "NotificationOptionAudioPlaybackStopped": "Geluid gestopt",
"NotificationOptionCameraImageUploaded": "Camera afbeelding geüpload", "NotificationOptionCameraImageUploaded": "Camera afbeelding geüpload",
"NotificationOptionGamePlayback": "Spel gestart",
"NotificationOptionGamePlaybackStopped": "Spel gestopt",
"NotificationOptionInstallationFailed": "Installatie mislukt", "NotificationOptionInstallationFailed": "Installatie mislukt",
"NotificationOptionNewLibraryContent": "Nieuwe content toegevoegd", "NotificationOptionNewLibraryContent": "Nieuwe content toegevoegd",
"NotificationOptionPluginError": "Plug-in fout", "NotificationOptionPluginError": "Plug-in fout",

View File

@ -14,7 +14,6 @@
"FailedLoginAttemptWithUserName": "Próba logowania przez {0} zakończona niepowodzeniem", "FailedLoginAttemptWithUserName": "Próba logowania przez {0} zakończona niepowodzeniem",
"Favorites": "Ulubione", "Favorites": "Ulubione",
"Folders": "Foldery", "Folders": "Foldery",
"Games": "Gry",
"Genres": "Gatunki", "Genres": "Gatunki",
"HeaderAlbumArtists": "Wykonawcy albumów", "HeaderAlbumArtists": "Wykonawcy albumów",
"HeaderCameraUploads": "Przekazane obrazy", "HeaderCameraUploads": "Przekazane obrazy",
@ -51,8 +50,6 @@
"NotificationOptionAudioPlayback": "Rozpoczęto odtwarzanie muzyki", "NotificationOptionAudioPlayback": "Rozpoczęto odtwarzanie muzyki",
"NotificationOptionAudioPlaybackStopped": "Odtwarzane dźwięku zatrzymane", "NotificationOptionAudioPlaybackStopped": "Odtwarzane dźwięku zatrzymane",
"NotificationOptionCameraImageUploaded": "Przekazano obraz z urządzenia mobilnego", "NotificationOptionCameraImageUploaded": "Przekazano obraz z urządzenia mobilnego",
"NotificationOptionGamePlayback": "Odtwarzanie gry rozpoczęte",
"NotificationOptionGamePlaybackStopped": "Odtwarzanie gry zatrzymane",
"NotificationOptionInstallationFailed": "Niepowodzenie instalacji", "NotificationOptionInstallationFailed": "Niepowodzenie instalacji",
"NotificationOptionNewLibraryContent": "Dodano nową zawartość", "NotificationOptionNewLibraryContent": "Dodano nową zawartość",
"NotificationOptionPluginError": "Awaria wtyczki", "NotificationOptionPluginError": "Awaria wtyczki",

View File

@ -14,7 +14,6 @@
"FailedLoginAttemptWithUserName": "Falha na tentativa de login de {0}", "FailedLoginAttemptWithUserName": "Falha na tentativa de login de {0}",
"Favorites": "Favoritos", "Favorites": "Favoritos",
"Folders": "Pastas", "Folders": "Pastas",
"Games": "Jogos",
"Genres": "Gêneros", "Genres": "Gêneros",
"HeaderAlbumArtists": "Artistas do Álbum", "HeaderAlbumArtists": "Artistas do Álbum",
"HeaderCameraUploads": "Uploads da Câmera", "HeaderCameraUploads": "Uploads da Câmera",
@ -51,8 +50,6 @@
"NotificationOptionAudioPlayback": "Reprodução de áudio iniciada", "NotificationOptionAudioPlayback": "Reprodução de áudio iniciada",
"NotificationOptionAudioPlaybackStopped": "Reprodução de áudio parada", "NotificationOptionAudioPlaybackStopped": "Reprodução de áudio parada",
"NotificationOptionCameraImageUploaded": "Imagem de câmera enviada", "NotificationOptionCameraImageUploaded": "Imagem de câmera enviada",
"NotificationOptionGamePlayback": "Reprodução de jogo iniciada",
"NotificationOptionGamePlaybackStopped": "Reprodução de jogo parada",
"NotificationOptionInstallationFailed": "Falha na instalação", "NotificationOptionInstallationFailed": "Falha na instalação",
"NotificationOptionNewLibraryContent": "Novo conteúdo adicionado", "NotificationOptionNewLibraryContent": "Novo conteúdo adicionado",
"NotificationOptionPluginError": "Falha de plugin", "NotificationOptionPluginError": "Falha de plugin",

View File

@ -14,7 +14,6 @@
"FailedLoginAttemptWithUserName": "Failed login attempt from {0}", "FailedLoginAttemptWithUserName": "Failed login attempt from {0}",
"Favorites": "Favorites", "Favorites": "Favorites",
"Folders": "Folders", "Folders": "Folders",
"Games": "Games",
"Genres": "Genres", "Genres": "Genres",
"HeaderAlbumArtists": "Album Artists", "HeaderAlbumArtists": "Album Artists",
"HeaderCameraUploads": "Camera Uploads", "HeaderCameraUploads": "Camera Uploads",
@ -51,8 +50,6 @@
"NotificationOptionAudioPlayback": "Audio playback started", "NotificationOptionAudioPlayback": "Audio playback started",
"NotificationOptionAudioPlaybackStopped": "Audio playback stopped", "NotificationOptionAudioPlaybackStopped": "Audio playback stopped",
"NotificationOptionCameraImageUploaded": "Camera image uploaded", "NotificationOptionCameraImageUploaded": "Camera image uploaded",
"NotificationOptionGamePlayback": "Game playback started",
"NotificationOptionGamePlaybackStopped": "Game playback stopped",
"NotificationOptionInstallationFailed": "Installation failure", "NotificationOptionInstallationFailed": "Installation failure",
"NotificationOptionNewLibraryContent": "New content added", "NotificationOptionNewLibraryContent": "New content added",
"NotificationOptionPluginError": "Plugin failure", "NotificationOptionPluginError": "Plugin failure",

View File

@ -14,7 +14,6 @@
"FailedLoginAttemptWithUserName": "{0} - попытка входа неудачна", "FailedLoginAttemptWithUserName": "{0} - попытка входа неудачна",
"Favorites": "Избранное", "Favorites": "Избранное",
"Folders": "Папки", "Folders": "Папки",
"Games": "Игры",
"Genres": "Жанры", "Genres": "Жанры",
"HeaderAlbumArtists": "Исп-ли альбома", "HeaderAlbumArtists": "Исп-ли альбома",
"HeaderCameraUploads": "Камеры", "HeaderCameraUploads": "Камеры",
@ -51,8 +50,6 @@
"NotificationOptionAudioPlayback": "Воспр-ие аудио зап-но", "NotificationOptionAudioPlayback": "Воспр-ие аудио зап-но",
"NotificationOptionAudioPlaybackStopped": "Восп-ие аудио ост-но", "NotificationOptionAudioPlaybackStopped": "Восп-ие аудио ост-но",
"NotificationOptionCameraImageUploaded": "Произведена выкладка отснятого с камеры", "NotificationOptionCameraImageUploaded": "Произведена выкладка отснятого с камеры",
"NotificationOptionGamePlayback": "Воспр-ие игры зап-но",
"NotificationOptionGamePlaybackStopped": "Восп-ие игры ост-но",
"NotificationOptionInstallationFailed": "Сбой установки", "NotificationOptionInstallationFailed": "Сбой установки",
"NotificationOptionNewLibraryContent": "Новое содержание добавлено", "NotificationOptionNewLibraryContent": "Новое содержание добавлено",
"NotificationOptionPluginError": "Сбой плагина", "NotificationOptionPluginError": "Сбой плагина",

View File

@ -14,7 +14,6 @@
"FailedLoginAttemptWithUserName": "Neúspešný pokus o prihlásenie z {0}", "FailedLoginAttemptWithUserName": "Neúspešný pokus o prihlásenie z {0}",
"Favorites": "Obľúbené", "Favorites": "Obľúbené",
"Folders": "Priečinky", "Folders": "Priečinky",
"Games": "Hry",
"Genres": "Žánre", "Genres": "Žánre",
"HeaderAlbumArtists": "Album Artists", "HeaderAlbumArtists": "Album Artists",
"HeaderCameraUploads": "Camera Uploads", "HeaderCameraUploads": "Camera Uploads",
@ -51,8 +50,6 @@
"NotificationOptionAudioPlayback": "Spustené prehrávanie audia", "NotificationOptionAudioPlayback": "Spustené prehrávanie audia",
"NotificationOptionAudioPlaybackStopped": "Zastavené prehrávanie audia", "NotificationOptionAudioPlaybackStopped": "Zastavené prehrávanie audia",
"NotificationOptionCameraImageUploaded": "Nahraný obrázok z fotoaparátu", "NotificationOptionCameraImageUploaded": "Nahraný obrázok z fotoaparátu",
"NotificationOptionGamePlayback": "Game playback started",
"NotificationOptionGamePlaybackStopped": "Hra ukončená",
"NotificationOptionInstallationFailed": "Chyba inštalácie", "NotificationOptionInstallationFailed": "Chyba inštalácie",
"NotificationOptionNewLibraryContent": "Pridaný nový obsah", "NotificationOptionNewLibraryContent": "Pridaný nový obsah",
"NotificationOptionPluginError": "Chyba rozšírenia", "NotificationOptionPluginError": "Chyba rozšírenia",

View File

@ -14,7 +14,6 @@
"FailedLoginAttemptWithUserName": "Failed login attempt from {0}", "FailedLoginAttemptWithUserName": "Failed login attempt from {0}",
"Favorites": "Favorites", "Favorites": "Favorites",
"Folders": "Folders", "Folders": "Folders",
"Games": "Games",
"Genres": "Genres", "Genres": "Genres",
"HeaderAlbumArtists": "Album Artists", "HeaderAlbumArtists": "Album Artists",
"HeaderCameraUploads": "Camera Uploads", "HeaderCameraUploads": "Camera Uploads",
@ -51,8 +50,6 @@
"NotificationOptionAudioPlayback": "Audio playback started", "NotificationOptionAudioPlayback": "Audio playback started",
"NotificationOptionAudioPlaybackStopped": "Audio playback stopped", "NotificationOptionAudioPlaybackStopped": "Audio playback stopped",
"NotificationOptionCameraImageUploaded": "Camera image uploaded", "NotificationOptionCameraImageUploaded": "Camera image uploaded",
"NotificationOptionGamePlayback": "Game playback started",
"NotificationOptionGamePlaybackStopped": "Game playback stopped",
"NotificationOptionInstallationFailed": "Installation failure", "NotificationOptionInstallationFailed": "Installation failure",
"NotificationOptionNewLibraryContent": "New content added", "NotificationOptionNewLibraryContent": "New content added",
"NotificationOptionPluginError": "Plugin failure", "NotificationOptionPluginError": "Plugin failure",

View File

@ -14,7 +14,6 @@
"FailedLoginAttemptWithUserName": "Misslyckat inloggningsförsök från {0}", "FailedLoginAttemptWithUserName": "Misslyckat inloggningsförsök från {0}",
"Favorites": "Favoriter", "Favorites": "Favoriter",
"Folders": "Mappar", "Folders": "Mappar",
"Games": "Spel",
"Genres": "Genrer", "Genres": "Genrer",
"HeaderAlbumArtists": "Albumartister", "HeaderAlbumArtists": "Albumartister",
"HeaderCameraUploads": "Camera Uploads", "HeaderCameraUploads": "Camera Uploads",
@ -51,8 +50,6 @@
"NotificationOptionAudioPlayback": "Ljuduppspelning har påbörjats", "NotificationOptionAudioPlayback": "Ljuduppspelning har påbörjats",
"NotificationOptionAudioPlaybackStopped": "Ljuduppspelning stoppad", "NotificationOptionAudioPlaybackStopped": "Ljuduppspelning stoppad",
"NotificationOptionCameraImageUploaded": "Kamerabild har laddats upp", "NotificationOptionCameraImageUploaded": "Kamerabild har laddats upp",
"NotificationOptionGamePlayback": "Spel har startats",
"NotificationOptionGamePlaybackStopped": "Spel stoppat",
"NotificationOptionInstallationFailed": "Fel vid installation", "NotificationOptionInstallationFailed": "Fel vid installation",
"NotificationOptionNewLibraryContent": "Nytt innehåll har lagts till", "NotificationOptionNewLibraryContent": "Nytt innehåll har lagts till",
"NotificationOptionPluginError": "Fel uppstod med tillägget", "NotificationOptionPluginError": "Fel uppstod med tillägget",

View File

@ -14,7 +14,6 @@
"FailedLoginAttemptWithUserName": "Failed login attempt from {0}", "FailedLoginAttemptWithUserName": "Failed login attempt from {0}",
"Favorites": "Favorites", "Favorites": "Favorites",
"Folders": "Folders", "Folders": "Folders",
"Games": "Games",
"Genres": "Genres", "Genres": "Genres",
"HeaderAlbumArtists": "Album Artists", "HeaderAlbumArtists": "Album Artists",
"HeaderCameraUploads": "Camera Uploads", "HeaderCameraUploads": "Camera Uploads",
@ -51,8 +50,6 @@
"NotificationOptionAudioPlayback": "Audio playback started", "NotificationOptionAudioPlayback": "Audio playback started",
"NotificationOptionAudioPlaybackStopped": "Audio playback stopped", "NotificationOptionAudioPlaybackStopped": "Audio playback stopped",
"NotificationOptionCameraImageUploaded": "Camera image uploaded", "NotificationOptionCameraImageUploaded": "Camera image uploaded",
"NotificationOptionGamePlayback": "Game playback started",
"NotificationOptionGamePlaybackStopped": "Game playback stopped",
"NotificationOptionInstallationFailed": "Installation failure", "NotificationOptionInstallationFailed": "Installation failure",
"NotificationOptionNewLibraryContent": "New content added", "NotificationOptionNewLibraryContent": "New content added",
"NotificationOptionPluginError": "Plugin failure", "NotificationOptionPluginError": "Plugin failure",

View File

@ -14,7 +14,6 @@
"FailedLoginAttemptWithUserName": "来自 {0} 的失败登入", "FailedLoginAttemptWithUserName": "来自 {0} 的失败登入",
"Favorites": "最爱", "Favorites": "最爱",
"Folders": "文件夹", "Folders": "文件夹",
"Games": "游戏",
"Genres": "风格", "Genres": "风格",
"HeaderAlbumArtists": "专辑作家", "HeaderAlbumArtists": "专辑作家",
"HeaderCameraUploads": "相机上传", "HeaderCameraUploads": "相机上传",
@ -51,8 +50,6 @@
"NotificationOptionAudioPlayback": "音频开始播放", "NotificationOptionAudioPlayback": "音频开始播放",
"NotificationOptionAudioPlaybackStopped": "音频播放已停止", "NotificationOptionAudioPlaybackStopped": "音频播放已停止",
"NotificationOptionCameraImageUploaded": "相机图片已上传", "NotificationOptionCameraImageUploaded": "相机图片已上传",
"NotificationOptionGamePlayback": "游戏开始",
"NotificationOptionGamePlaybackStopped": "游戏停止",
"NotificationOptionInstallationFailed": "安装失败", "NotificationOptionInstallationFailed": "安装失败",
"NotificationOptionNewLibraryContent": "已添加新内容", "NotificationOptionNewLibraryContent": "已添加新内容",
"NotificationOptionPluginError": "插件失败", "NotificationOptionPluginError": "插件失败",

View File

@ -14,7 +14,6 @@
"FailedLoginAttemptWithUserName": "Failed login attempt from {0}", "FailedLoginAttemptWithUserName": "Failed login attempt from {0}",
"Favorites": "Favorites", "Favorites": "Favorites",
"Folders": "Folders", "Folders": "Folders",
"Games": "Games",
"Genres": "Genres", "Genres": "Genres",
"HeaderAlbumArtists": "Album Artists", "HeaderAlbumArtists": "Album Artists",
"HeaderCameraUploads": "Camera Uploads", "HeaderCameraUploads": "Camera Uploads",
@ -51,8 +50,6 @@
"NotificationOptionAudioPlayback": "Audio playback started", "NotificationOptionAudioPlayback": "Audio playback started",
"NotificationOptionAudioPlaybackStopped": "Audio playback stopped", "NotificationOptionAudioPlaybackStopped": "Audio playback stopped",
"NotificationOptionCameraImageUploaded": "Camera image uploaded", "NotificationOptionCameraImageUploaded": "Camera image uploaded",
"NotificationOptionGamePlayback": "Game playback started",
"NotificationOptionGamePlaybackStopped": "Game playback stopped",
"NotificationOptionInstallationFailed": "Installation failure", "NotificationOptionInstallationFailed": "Installation failure",
"NotificationOptionNewLibraryContent": "New content added", "NotificationOptionNewLibraryContent": "New content added",
"NotificationOptionPluginError": "Plugin failure", "NotificationOptionPluginError": "Plugin failure",

File diff suppressed because one or more lines are too long

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