commit
8bf88f4cb2
|
@ -15,6 +15,7 @@
|
|||
- [cvium](https://github.com/cvium)
|
||||
- [wtayl0r](https://github.com/wtayl0r)
|
||||
- [TtheCreator](https://github.com/Tthecreator)
|
||||
- [dkanada](https://github.com/dkanada)
|
||||
- [LogicalPhallacy](https://github.com/LogicalPhallacy/)
|
||||
- [RazeLighter777](https://github.com/RazeLighter777)
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ RUN export DOTNET_CLI_TELEMETRY_OPTOUT=1 \
|
|||
--output /jellyfin \
|
||||
Jellyfin.Server
|
||||
|
||||
FROM jrottenberg/ffmpeg:4.0-scratch as ffmpeg
|
||||
FROM jrottenberg/ffmpeg:4.0-vaapi as ffmpeg
|
||||
FROM microsoft/dotnet:${DOTNET_VERSION}-runtime
|
||||
# libfontconfig1 is required for Skia
|
||||
RUN apt-get update \
|
||||
|
@ -23,4 +23,4 @@ COPY --from=ffmpeg / /
|
|||
COPY --from=builder /jellyfin /jellyfin
|
||||
EXPOSE 8096
|
||||
VOLUME /config /media
|
||||
ENTRYPOINT dotnet /jellyfin/jellyfin.dll -programdata /config
|
||||
ENTRYPOINT dotnet /jellyfin/jellyfin.dll --datadir /config
|
||||
|
|
|
@ -21,4 +21,4 @@ RUN apt-get update \
|
|||
COPY --from=builder /jellyfin /jellyfin
|
||||
EXPOSE 8096
|
||||
VOLUME /config /media
|
||||
ENTRYPOINT dotnet /jellyfin/jellyfin.dll -programdata /config
|
||||
ENTRYPOINT dotnet /jellyfin/jellyfin.dll --datadir /config
|
||||
|
|
|
@ -30,4 +30,4 @@ COPY --from=qemu_extract qemu-* /usr/bin
|
|||
COPY --from=builder /jellyfin /jellyfin
|
||||
EXPOSE 8096
|
||||
VOLUME /config /media
|
||||
ENTRYPOINT dotnet /jellyfin/jellyfin.dll -programdata /config
|
||||
ENTRYPOINT dotnet /jellyfin/jellyfin.dll --datadir /config
|
||||
|
|
|
@ -236,7 +236,9 @@ namespace Emby.Dlna.Api
|
|||
|
||||
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 cacheKey = Request.RawUrl.GetMD5();
|
||||
|
|
|
@ -63,7 +63,7 @@ namespace Emby.Dlna.ContentDirectory
|
|||
_profile = profile;
|
||||
_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)
|
||||
|
@ -454,7 +454,7 @@ namespace Emby.Dlna.ContentDirectory
|
|||
User = user,
|
||||
Recursive = true,
|
||||
IsMissing = false,
|
||||
ExcludeItemTypes = new[] { typeof(Game).Name, typeof(Book).Name },
|
||||
ExcludeItemTypes = new[] { typeof(Book).Name },
|
||||
IsFolder = isFolder,
|
||||
MediaTypes = mediaTypes.ToArray(),
|
||||
DtoOptions = GetDtoOptions()
|
||||
|
@ -523,7 +523,7 @@ namespace Emby.Dlna.ContentDirectory
|
|||
Limit = limit,
|
||||
StartIndex = startIndex,
|
||||
IsVirtualItem = false,
|
||||
ExcludeItemTypes = new[] { typeof(Game).Name, typeof(Book).Name },
|
||||
ExcludeItemTypes = new[] { typeof(Book).Name },
|
||||
IsPlaceHolder = false,
|
||||
DtoOptions = GetDtoOptions()
|
||||
};
|
||||
|
|
|
@ -43,22 +43,30 @@ namespace Emby.Dlna.Didl
|
|||
private readonly ILocalizationManager _localization;
|
||||
private readonly IMediaSourceManager _mediaSourceManager;
|
||||
private readonly ILogger _logger;
|
||||
private readonly ILibraryManager _libraryManager;
|
||||
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;
|
||||
_user = user;
|
||||
_imageProcessor = imageProcessor;
|
||||
_serverAddress = serverAddress;
|
||||
_accessToken = accessToken;
|
||||
_userDataManager = userDataManager;
|
||||
_localization = localization;
|
||||
_mediaSourceManager = mediaSourceManager;
|
||||
_logger = logger;
|
||||
_libraryManager = libraryManager;
|
||||
_mediaEncoder = mediaEncoder;
|
||||
_accessToken = accessToken;
|
||||
_user = user;
|
||||
}
|
||||
|
||||
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,
|
||||
BaseItem item,
|
||||
User user,
|
||||
|
@ -232,12 +241,15 @@ namespace Emby.Dlna.Didl
|
|||
AddVideoResource(writer, video, deviceId, filter, contentFeature, streamInfo);
|
||||
}
|
||||
|
||||
var subtitleProfiles = streamInfo.GetSubtitleProfiles(_mediaEncoder, false, _serverAddress, _accessToken)
|
||||
.Where(subtitle => subtitle.DeliveryMethod == SubtitleDeliveryMethod.External)
|
||||
.ToList();
|
||||
var subtitleProfiles = streamInfo.GetSubtitleProfiles(_mediaEncoder, false, _serverAddress, _accessToken);
|
||||
|
||||
foreach (var subtitle in subtitleProfiles)
|
||||
{
|
||||
if (subtitle.DeliveryMethod != SubtitleDeliveryMethod.External)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var subtitleAdded = AddSubtitleElement(writer, subtitle);
|
||||
|
||||
if (subtitleAdded && _profile.EnableSingleSubtitleLimit)
|
||||
|
@ -250,7 +262,8 @@ namespace Emby.Dlna.Didl
|
|||
private bool AddSubtitleElement(XmlWriter writer, SubtitleStreamInfo info)
|
||||
{
|
||||
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)
|
||||
{
|
||||
|
@ -265,7 +278,7 @@ namespace Emby.Dlna.Didl
|
|||
// <sec:CaptionInfo sec:type="srt">http://192.168.1.3:9999/video.srt</sec:CaptionInfo>
|
||||
|
||||
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.WriteFullEndElement();
|
||||
|
@ -282,7 +295,7 @@ namespace Emby.Dlna.Didl
|
|||
else
|
||||
{
|
||||
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.WriteString(info.Url);
|
||||
|
@ -387,91 +400,39 @@ namespace Emby.Dlna.Didl
|
|||
|
||||
private string GetDisplayName(BaseItem item, StubType? itemStubType, BaseItem context)
|
||||
{
|
||||
if (itemStubType.HasValue && itemStubType.Value == StubType.Latest)
|
||||
if (itemStubType.HasValue)
|
||||
{
|
||||
return _localization.GetLocalizedString("Latest");
|
||||
}
|
||||
if (itemStubType.HasValue && itemStubType.Value == StubType.Playlists)
|
||||
{
|
||||
return _localization.GetLocalizedString("Playlists");
|
||||
}
|
||||
if (itemStubType.HasValue && itemStubType.Value == StubType.AlbumArtists)
|
||||
{
|
||||
return _localization.GetLocalizedString("HeaderAlbumArtists");
|
||||
}
|
||||
if (itemStubType.HasValue && itemStubType.Value == StubType.Albums)
|
||||
{
|
||||
return _localization.GetLocalizedString("Albums");
|
||||
}
|
||||
if (itemStubType.HasValue && itemStubType.Value == StubType.Artists)
|
||||
{
|
||||
return _localization.GetLocalizedString("Artists");
|
||||
}
|
||||
if (itemStubType.HasValue && itemStubType.Value == StubType.Songs)
|
||||
{
|
||||
return _localization.GetLocalizedString("Songs");
|
||||
}
|
||||
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");
|
||||
switch (itemStubType.Value)
|
||||
{
|
||||
case StubType.Latest: return _localization.GetLocalizedString("Latest");
|
||||
case StubType.Playlists: return _localization.GetLocalizedString("Playlists");
|
||||
case StubType.AlbumArtists: return _localization.GetLocalizedString("HeaderAlbumArtists");
|
||||
case StubType.Albums: return _localization.GetLocalizedString("Albums");
|
||||
case StubType.Artists: return _localization.GetLocalizedString("Artists");
|
||||
case StubType.Songs: return _localization.GetLocalizedString("Songs");
|
||||
case StubType.Genres: return _localization.GetLocalizedString("Genres");
|
||||
case StubType.FavoriteAlbums: return _localization.GetLocalizedString("HeaderFavoriteAlbums");
|
||||
case StubType.FavoriteArtists: return _localization.GetLocalizedString("HeaderFavoriteArtists");
|
||||
case StubType.FavoriteSongs: return _localization.GetLocalizedString("HeaderFavoriteSongs");
|
||||
case StubType.ContinueWatching: return _localization.GetLocalizedString("HeaderContinueWatching");
|
||||
case StubType.Movies: return _localization.GetLocalizedString("Movies");
|
||||
case StubType.Collections: return _localization.GetLocalizedString("Collections");
|
||||
case StubType.Favorites: return _localization.GetLocalizedString("Favorites");
|
||||
case StubType.NextUp: return _localization.GetLocalizedString("HeaderNextUp");
|
||||
case StubType.FavoriteSeries: return _localization.GetLocalizedString("HeaderFavoriteShows");
|
||||
case StubType.FavoriteEpisodes: return _localization.GetLocalizedString("HeaderFavoriteEpisodes");
|
||||
case StubType.Series: return _localization.GetLocalizedString("Shows");
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
var episode = item as Episode;
|
||||
var season = context as Season;
|
||||
|
||||
if (episode != null && season != null)
|
||||
if (item is Episode episode && context is Season 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)
|
||||
|
@ -585,10 +546,8 @@ namespace Emby.Dlna.Didl
|
|||
|
||||
public static bool IsIdRoot(string id)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(id) ||
|
||||
|
||||
string.Equals(id, "0", StringComparison.OrdinalIgnoreCase)
|
||||
|
||||
if (string.IsNullOrWhiteSpace(id)
|
||||
|| string.Equals(id, "0", StringComparison.OrdinalIgnoreCase)
|
||||
// Samsung sometimes uses 1 as root
|
||||
|| 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");
|
||||
}
|
||||
else if (item is Genre || item is GameGenre)
|
||||
else if (item is 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))
|
||||
// ?? PersonType.Actor;
|
||||
|
||||
// AddValue(writer, "upnp", type.ToLower(), actor.Name, NS_UPNP);
|
||||
// AddValue(writer, "upnp", type.ToLowerInvariant(), actor.Name, NS_UPNP);
|
||||
|
||||
// index++;
|
||||
|
||||
|
@ -1112,7 +1071,7 @@ namespace Emby.Dlna.Didl
|
|||
};
|
||||
}
|
||||
|
||||
class ImageDownloadInfo
|
||||
private class ImageDownloadInfo
|
||||
{
|
||||
internal Guid ItemId;
|
||||
internal string ImageTag;
|
||||
|
@ -1128,7 +1087,7 @@ namespace Emby.Dlna.Didl
|
|||
internal ItemImageInfo ItemImageInfo;
|
||||
}
|
||||
|
||||
class ImageUrlInfo
|
||||
private class ImageUrlInfo
|
||||
{
|
||||
internal string Url;
|
||||
|
||||
|
@ -1147,7 +1106,7 @@ namespace Emby.Dlna.Didl
|
|||
|
||||
if (stubType.HasValue)
|
||||
{
|
||||
id = stubType.Value.ToString().ToLower() + "_" + id;
|
||||
id = stubType.Value.ToString().ToLowerInvariant() + "_" + id;
|
||||
}
|
||||
|
||||
return id;
|
||||
|
|
|
@ -4,6 +4,7 @@ using System.IO;
|
|||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using Emby.Dlna.Profiles;
|
||||
using Emby.Dlna.Server;
|
||||
using MediaBrowser.Common.Configuration;
|
||||
|
@ -48,11 +49,11 @@ namespace Emby.Dlna
|
|||
_assemblyInfo = assemblyInfo;
|
||||
}
|
||||
|
||||
public void InitProfiles()
|
||||
public async Task InitProfilesAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
ExtractSystemProfiles();
|
||||
await ExtractSystemProfilesAsync();
|
||||
LoadProfiles();
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
@ -300,7 +301,7 @@ namespace Emby.Dlna
|
|||
|
||||
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);
|
||||
|
||||
|
@ -352,14 +353,14 @@ namespace Emby.Dlna
|
|||
|
||||
Info = new DeviceProfileInfo
|
||||
{
|
||||
Id = file.FullName.ToLower().GetMD5().ToString("N"),
|
||||
Id = file.FullName.ToLowerInvariant().GetMD5().ToString("N"),
|
||||
Name = _fileSystem.GetFileNameWithoutExtension(file),
|
||||
Type = type
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void ExtractSystemProfiles()
|
||||
private async Task ExtractSystemProfilesAsync()
|
||||
{
|
||||
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))
|
||||
{
|
||||
stream.CopyTo(fileStream);
|
||||
await stream.CopyToAsync(fileStream);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -506,7 +507,7 @@ namespace Emby.Dlna
|
|||
? ImageFormat.Png
|
||||
: ImageFormat.Jpg;
|
||||
|
||||
var resource = GetType().Namespace + ".Images." + filename.ToLower();
|
||||
var resource = GetType().Namespace + ".Images." + filename.ToLowerInvariant();
|
||||
|
||||
return new ImageStream
|
||||
{
|
||||
|
|
|
@ -20,7 +20,6 @@ using MediaBrowser.Model.Dlna;
|
|||
using MediaBrowser.Model.Globalization;
|
||||
using MediaBrowser.Model.Net;
|
||||
using MediaBrowser.Model.System;
|
||||
using MediaBrowser.Model.Threading;
|
||||
using MediaBrowser.Model.Xml;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Rssdp;
|
||||
|
@ -49,8 +48,7 @@ namespace Emby.Dlna.Main
|
|||
private readonly IDeviceDiscovery _deviceDiscovery;
|
||||
|
||||
private SsdpDevicePublisher _Publisher;
|
||||
|
||||
private readonly ITimerFactory _timerFactory;
|
||||
|
||||
private readonly ISocketFactory _socketFactory;
|
||||
private readonly IEnvironmentInfo _environmentInfo;
|
||||
private readonly INetworkManager _networkManager;
|
||||
|
@ -78,7 +76,6 @@ namespace Emby.Dlna.Main
|
|||
IDeviceDiscovery deviceDiscovery,
|
||||
IMediaEncoder mediaEncoder,
|
||||
ISocketFactory socketFactory,
|
||||
ITimerFactory timerFactory,
|
||||
IEnvironmentInfo environmentInfo,
|
||||
INetworkManager networkManager,
|
||||
IUserViewManager userViewManager,
|
||||
|
@ -99,7 +96,6 @@ namespace Emby.Dlna.Main
|
|||
_deviceDiscovery = deviceDiscovery;
|
||||
_mediaEncoder = mediaEncoder;
|
||||
_socketFactory = socketFactory;
|
||||
_timerFactory = timerFactory;
|
||||
_environmentInfo = environmentInfo;
|
||||
_networkManager = networkManager;
|
||||
_logger = loggerFactory.CreateLogger("Dlna");
|
||||
|
@ -125,9 +121,9 @@ namespace Emby.Dlna.Main
|
|||
Current = this;
|
||||
}
|
||||
|
||||
public void Run()
|
||||
public async Task RunAsync()
|
||||
{
|
||||
((DlnaManager)_dlnaManager).InitProfiles();
|
||||
await ((DlnaManager)_dlnaManager).InitProfilesAsync().ConfigureAwait(false);
|
||||
|
||||
ReloadComponents();
|
||||
|
||||
|
@ -233,7 +229,7 @@ namespace Emby.Dlna.Main
|
|||
|
||||
try
|
||||
{
|
||||
_Publisher = new SsdpDevicePublisher(_communicationsServer, _timerFactory, _environmentInfo.OperatingSystemName, _environmentInfo.OperatingSystemVersion);
|
||||
_Publisher = new SsdpDevicePublisher(_communicationsServer, _environmentInfo.OperatingSystemName, _environmentInfo.OperatingSystemVersion);
|
||||
_Publisher.LogFunction = LogMessage;
|
||||
_Publisher.SupportPnpRootDevice = false;
|
||||
|
||||
|
@ -263,7 +259,7 @@ namespace Emby.Dlna.Main
|
|||
|
||||
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 uri = new Uri(_appHost.GetLocalApiUrl(address) + descriptorUri);
|
||||
|
@ -353,8 +349,7 @@ namespace Emby.Dlna.Main
|
|||
_userDataManager,
|
||||
_localization,
|
||||
_mediaSourceManager,
|
||||
_mediaEncoder,
|
||||
_timerFactory);
|
||||
_mediaEncoder);
|
||||
|
||||
_manager.Start();
|
||||
}
|
||||
|
|
|
@ -10,7 +10,6 @@ using Emby.Dlna.Server;
|
|||
using Emby.Dlna.Ssdp;
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
using MediaBrowser.Model.Threading;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Emby.Dlna.PlayTo
|
||||
|
@ -19,7 +18,7 @@ namespace Emby.Dlna.PlayTo
|
|||
{
|
||||
#region Fields & Properties
|
||||
|
||||
private ITimer _timer;
|
||||
private Timer _timer;
|
||||
|
||||
public DeviceInfo Properties { get; set; }
|
||||
|
||||
|
@ -40,12 +39,7 @@ namespace Emby.Dlna.PlayTo
|
|||
|
||||
public TimeSpan? Duration { get; set; }
|
||||
|
||||
private TimeSpan _position = TimeSpan.FromSeconds(0);
|
||||
public TimeSpan Position
|
||||
{
|
||||
get => _position;
|
||||
set => _position = value;
|
||||
}
|
||||
public TimeSpan Position { get; set; } = TimeSpan.FromSeconds(0);
|
||||
|
||||
public TRANSPORTSTATE TransportState { get; private set; }
|
||||
|
||||
|
@ -61,24 +55,20 @@ namespace Emby.Dlna.PlayTo
|
|||
private readonly ILogger _logger;
|
||||
private readonly IServerConfigurationManager _config;
|
||||
|
||||
public DateTime DateLastActivity { get; private set; }
|
||||
public Action OnDeviceUnavailable { get; set; }
|
||||
|
||||
private readonly ITimerFactory _timerFactory;
|
||||
|
||||
public Device(DeviceInfo deviceProperties, IHttpClient httpClient, ILogger logger, IServerConfigurationManager config, ITimerFactory timerFactory)
|
||||
public Device(DeviceInfo deviceProperties, IHttpClient httpClient, ILogger logger, IServerConfigurationManager config)
|
||||
{
|
||||
Properties = deviceProperties;
|
||||
_httpClient = httpClient;
|
||||
_logger = logger;
|
||||
_config = config;
|
||||
_timerFactory = timerFactory;
|
||||
}
|
||||
|
||||
public void 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;
|
||||
|
@ -119,7 +109,9 @@ namespace Emby.Dlna.PlayTo
|
|||
lock (_timerLock)
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_volumeRefreshActive = true;
|
||||
|
||||
|
@ -136,7 +128,9 @@ namespace Emby.Dlna.PlayTo
|
|||
lock (_timerLock)
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_volumeRefreshActive = false;
|
||||
|
||||
|
@ -144,11 +138,6 @@ namespace Emby.Dlna.PlayTo
|
|||
}
|
||||
}
|
||||
|
||||
public void OnPlaybackStartedExternally()
|
||||
{
|
||||
RestartTimer(true);
|
||||
}
|
||||
|
||||
#region Commanding
|
||||
|
||||
public Task VolumeDown(CancellationToken cancellationToken)
|
||||
|
@ -333,7 +322,9 @@ namespace Emby.Dlna.PlayTo
|
|||
private string CreateDidlMeta(string value)
|
||||
{
|
||||
if (string.IsNullOrEmpty(value))
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
return DescriptionXmlBuilder.Escape(value);
|
||||
}
|
||||
|
@ -342,10 +333,11 @@ namespace Emby.Dlna.PlayTo
|
|||
{
|
||||
var command = avCommands.ServiceActions.FirstOrDefault(c => c.Name == "Play");
|
||||
if (command == null)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
var service = GetAvTransportService();
|
||||
|
||||
if (service == null)
|
||||
{
|
||||
throw new InvalidOperationException("Unable to find service");
|
||||
|
@ -369,7 +361,9 @@ namespace Emby.Dlna.PlayTo
|
|||
|
||||
var command = avCommands.ServiceActions.FirstOrDefault(c => c.Name == "Stop");
|
||||
if (command == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var service = GetAvTransportService();
|
||||
|
||||
|
@ -385,7 +379,9 @@ namespace Emby.Dlna.PlayTo
|
|||
|
||||
var command = avCommands.ServiceActions.FirstOrDefault(c => c.Name == "Pause");
|
||||
if (command == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var service = GetAvTransportService();
|
||||
|
||||
|
@ -405,7 +401,9 @@ namespace Emby.Dlna.PlayTo
|
|||
private async void TimerCallback(object sender)
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
|
@ -425,8 +423,6 @@ namespace Emby.Dlna.PlayTo
|
|||
return;
|
||||
}
|
||||
|
||||
DateLastActivity = DateTime.UtcNow;
|
||||
|
||||
if (transportState.HasValue)
|
||||
{
|
||||
// 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");
|
||||
if (command == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var service = GetServiceRenderingControl();
|
||||
|
||||
|
@ -518,13 +516,17 @@ namespace Emby.Dlna.PlayTo
|
|||
.ConfigureAwait(false);
|
||||
|
||||
if (result == null || result.Document == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
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))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Volume = int.Parse(volumeValue, UsCulture);
|
||||
|
||||
|
@ -545,7 +547,9 @@ namespace Emby.Dlna.PlayTo
|
|||
|
||||
var command = rendererCommands.ServiceActions.FirstOrDefault(c => c.Name == "GetMute");
|
||||
if (command == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var service = GetServiceRenderingControl();
|
||||
|
||||
|
@ -560,39 +564,44 @@ namespace Emby.Dlna.PlayTo
|
|||
if (result == null || result.Document == null)
|
||||
return;
|
||||
|
||||
var valueNode = result.Document.Descendants(uPnpNamespaces.RenderingControl + "GetMuteResponse").Select(i => i.Element("CurrentMute")).FirstOrDefault(i => i != null);
|
||||
var value = valueNode == null ? null : valueNode.Value;
|
||||
var valueNode = result.Document.Descendants(uPnpNamespaces.RenderingControl + "GetMuteResponse")
|
||||
.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)
|
||||
{
|
||||
var command = avCommands.ServiceActions.FirstOrDefault(c => c.Name == "GetTransportInfo");
|
||||
if (command == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var service = GetAvTransportService();
|
||||
if (service == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var result = await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, avCommands.BuildPost(command, service.ServiceType), false)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
if (result == null || result.Document == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var transportState =
|
||||
result.Document.Descendants(uPnpNamespaces.AvTransport + "GetTransportInfoResponse").Select(i => i.Element("CurrentTransportState")).FirstOrDefault(i => i != null);
|
||||
|
||||
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;
|
||||
|
@ -602,10 +611,11 @@ namespace Emby.Dlna.PlayTo
|
|||
{
|
||||
var command = avCommands.ServiceActions.FirstOrDefault(c => c.Name == "GetMediaInfo");
|
||||
if (command == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var service = GetAvTransportService();
|
||||
|
||||
if (service == null)
|
||||
{
|
||||
throw new InvalidOperationException("Unable to find service");
|
||||
|
@ -617,7 +627,9 @@ namespace Emby.Dlna.PlayTo
|
|||
.ConfigureAwait(false);
|
||||
|
||||
if (result == null || result.Document == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var track = result.Document.Descendants("CurrentURIMetaData").FirstOrDefault();
|
||||
|
||||
|
@ -657,11 +669,13 @@ namespace Emby.Dlna.PlayTo
|
|||
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");
|
||||
if (command == null)
|
||||
return new Tuple<bool, uBaseObject>(false, null);
|
||||
{
|
||||
return (false, null);
|
||||
}
|
||||
|
||||
var service = GetAvTransportService();
|
||||
|
||||
|
@ -676,7 +690,9 @@ namespace Emby.Dlna.PlayTo
|
|||
.ConfigureAwait(false);
|
||||
|
||||
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 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 duration = durationElem == null ? null : durationElem.Value;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(duration) &&
|
||||
!string.Equals(duration, "NOT_IMPLEMENTED", StringComparison.OrdinalIgnoreCase))
|
||||
if (!string.IsNullOrWhiteSpace(duration)
|
||||
&& !string.Equals(duration, "NOT_IMPLEMENTED", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
Duration = TimeSpan.Parse(duration, UsCulture);
|
||||
}
|
||||
|
@ -707,14 +723,14 @@ namespace Emby.Dlna.PlayTo
|
|||
if (track == null)
|
||||
{
|
||||
//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;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(trackString) || string.Equals(trackString, "NOT_IMPLEMENTED", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return new Tuple<bool, uBaseObject>(true, null);
|
||||
return (true, null);
|
||||
}
|
||||
|
||||
XElement uPnpResponse;
|
||||
|
@ -735,7 +751,7 @@ namespace Emby.Dlna.PlayTo
|
|||
catch (Exception ex)
|
||||
{
|
||||
_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);
|
||||
|
||||
return new Tuple<bool, uBaseObject>(true, uTrack);
|
||||
return (true, uTrack);
|
||||
}
|
||||
|
||||
private static uBaseObject CreateUBaseObject(XElement container, string trackUri)
|
||||
|
@ -801,11 +817,9 @@ namespace Emby.Dlna.PlayTo
|
|||
|
||||
private async Task<TransportCommands> GetAVProtocolAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var avCommands = AvCommands;
|
||||
|
||||
if (avCommands != null)
|
||||
if (AvCommands != null)
|
||||
{
|
||||
return avCommands;
|
||||
return AvCommands;
|
||||
}
|
||||
|
||||
if (_disposed)
|
||||
|
@ -825,18 +839,15 @@ namespace Emby.Dlna.PlayTo
|
|||
|
||||
var document = await httpClient.GetDataAsync(url, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
avCommands = TransportCommands.Create(document);
|
||||
AvCommands = avCommands;
|
||||
return avCommands;
|
||||
AvCommands = TransportCommands.Create(document);
|
||||
return AvCommands;
|
||||
}
|
||||
|
||||
private async Task<TransportCommands> GetRenderingProtocolAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var rendererCommands = RendererCommands;
|
||||
|
||||
if (rendererCommands != null)
|
||||
if (RendererCommands != null)
|
||||
{
|
||||
return rendererCommands;
|
||||
return RendererCommands;
|
||||
}
|
||||
|
||||
if (_disposed)
|
||||
|
@ -845,7 +856,6 @@ namespace Emby.Dlna.PlayTo
|
|||
}
|
||||
|
||||
var avService = GetServiceRenderingControl();
|
||||
|
||||
if (avService == null)
|
||||
{
|
||||
throw new ArgumentException("Device AvService is null");
|
||||
|
@ -857,9 +867,8 @@ namespace Emby.Dlna.PlayTo
|
|||
_logger.LogDebug("Dlna Device.GetRenderingProtocolAsync");
|
||||
var document = await httpClient.GetDataAsync(url, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
rendererCommands = TransportCommands.Create(document);
|
||||
RendererCommands = rendererCommands;
|
||||
return rendererCommands;
|
||||
RendererCommands = TransportCommands.Create(document);
|
||||
return RendererCommands;
|
||||
}
|
||||
|
||||
private string NormalizeUrl(string baseUrl, string url)
|
||||
|
@ -871,85 +880,103 @@ namespace Emby.Dlna.PlayTo
|
|||
}
|
||||
|
||||
if (!url.Contains("/"))
|
||||
{
|
||||
url = "/dmr/" + url;
|
||||
}
|
||||
|
||||
if (!url.StartsWith("/"))
|
||||
{
|
||||
url = "/" + url;
|
||||
}
|
||||
|
||||
return baseUrl + url;
|
||||
}
|
||||
|
||||
private TransportCommands AvCommands
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
private TransportCommands AvCommands { get; set; }
|
||||
|
||||
private TransportCommands RendererCommands
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
private TransportCommands RendererCommands { 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 document = await ssdpHttpClient.GetDataAsync(url.ToString(), cancellationToken).ConfigureAwait(false);
|
||||
|
||||
var deviceProperties = new DeviceInfo();
|
||||
|
||||
var friendlyNames = new List<string>();
|
||||
|
||||
var name = document.Descendants(uPnpNamespaces.ud.GetName("friendlyName")).FirstOrDefault();
|
||||
if (name != null && !string.IsNullOrWhiteSpace(name.Value))
|
||||
{
|
||||
friendlyNames.Add(name.Value);
|
||||
}
|
||||
|
||||
var room = document.Descendants(uPnpNamespaces.ud.GetName("roomName")).FirstOrDefault();
|
||||
if (room != null && !string.IsNullOrWhiteSpace(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();
|
||||
if (model != null)
|
||||
{
|
||||
deviceProperties.ModelName = model.Value;
|
||||
}
|
||||
|
||||
var modelNumber = document.Descendants(uPnpNamespaces.ud.GetName("modelNumber")).FirstOrDefault();
|
||||
if (modelNumber != null)
|
||||
{
|
||||
deviceProperties.ModelNumber = modelNumber.Value;
|
||||
}
|
||||
|
||||
var uuid = document.Descendants(uPnpNamespaces.ud.GetName("UDN")).FirstOrDefault();
|
||||
if (uuid != null)
|
||||
{
|
||||
deviceProperties.UUID = uuid.Value;
|
||||
}
|
||||
|
||||
var manufacturer = document.Descendants(uPnpNamespaces.ud.GetName("manufacturer")).FirstOrDefault();
|
||||
if (manufacturer != null)
|
||||
{
|
||||
deviceProperties.Manufacturer = manufacturer.Value;
|
||||
}
|
||||
|
||||
var manufacturerUrl = document.Descendants(uPnpNamespaces.ud.GetName("manufacturerURL")).FirstOrDefault();
|
||||
if (manufacturerUrl != null)
|
||||
{
|
||||
deviceProperties.ManufacturerUrl = manufacturerUrl.Value;
|
||||
}
|
||||
|
||||
var presentationUrl = document.Descendants(uPnpNamespaces.ud.GetName("presentationURL")).FirstOrDefault();
|
||||
if (presentationUrl != null)
|
||||
{
|
||||
deviceProperties.PresentationUrl = presentationUrl.Value;
|
||||
}
|
||||
|
||||
var modelUrl = document.Descendants(uPnpNamespaces.ud.GetName("modelURL")).FirstOrDefault();
|
||||
if (modelUrl != null)
|
||||
{
|
||||
deviceProperties.ModelUrl = modelUrl.Value;
|
||||
}
|
||||
|
||||
var serialNumber = document.Descendants(uPnpNamespaces.ud.GetName("serialNumber")).FirstOrDefault();
|
||||
if (serialNumber != null)
|
||||
{
|
||||
deviceProperties.SerialNumber = serialNumber.Value;
|
||||
}
|
||||
|
||||
var modelDescription = document.Descendants(uPnpNamespaces.ud.GetName("modelDescription")).FirstOrDefault();
|
||||
if (modelDescription != null)
|
||||
{
|
||||
deviceProperties.ModelDescription = modelDescription.Value;
|
||||
|
||||
deviceProperties.BaseUrl = string.Format("http://{0}:{1}", url.Host, url.Port);
|
||||
}
|
||||
|
||||
var icon = document.Descendants(uPnpNamespaces.ud.GetName("icon")).FirstOrDefault();
|
||||
|
||||
if (icon != null)
|
||||
{
|
||||
deviceProperties.Icon = CreateIcon(icon);
|
||||
|
@ -958,12 +985,15 @@ namespace Emby.Dlna.PlayTo
|
|||
foreach (var services in document.Descendants(uPnpNamespaces.ud.GetName("serviceList")))
|
||||
{
|
||||
if (services == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var servicesList = services.Descendants(uPnpNamespaces.ud.GetName("service"));
|
||||
|
||||
if (servicesList == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (var element in servicesList)
|
||||
{
|
||||
|
@ -976,9 +1006,7 @@ namespace Emby.Dlna.PlayTo
|
|||
}
|
||||
}
|
||||
|
||||
var device = new Device(deviceProperties, httpClient, logger, config, timerFactory);
|
||||
|
||||
return device;
|
||||
return new Device(deviceProperties, httpClient, logger, config);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
@ -1065,13 +1093,10 @@ namespace Emby.Dlna.PlayTo
|
|||
|
||||
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)
|
||||
|
@ -1082,58 +1107,56 @@ namespace Emby.Dlna.PlayTo
|
|||
return;
|
||||
}
|
||||
|
||||
if (PlaybackProgress != null)
|
||||
PlaybackProgress?.Invoke(this, new PlaybackProgressEventArgs
|
||||
{
|
||||
PlaybackProgress.Invoke(this, new PlaybackProgressEventArgs
|
||||
{
|
||||
MediaInfo = mediaInfo
|
||||
});
|
||||
}
|
||||
MediaInfo = 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)
|
||||
{
|
||||
if (MediaChanged != null)
|
||||
MediaChanged?.Invoke(this, new MediaChangedEventArgs
|
||||
{
|
||||
MediaChanged.Invoke(this, new MediaChangedEventArgs
|
||||
{
|
||||
OldMediaInfo = old,
|
||||
NewMediaInfo = newMedia
|
||||
});
|
||||
}
|
||||
OldMediaInfo = old,
|
||||
NewMediaInfo = newMedia
|
||||
});
|
||||
}
|
||||
|
||||
#region IDisposable
|
||||
|
||||
bool _disposed;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (!_disposed)
|
||||
{
|
||||
_disposed = true;
|
||||
|
||||
DisposeTimer();
|
||||
}
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private void DisposeTimer()
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (_timer != null)
|
||||
if (_disposed)
|
||||
{
|
||||
_timer.Dispose();
|
||||
_timer = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (disposing)
|
||||
{
|
||||
_timer?.Dispose();
|
||||
}
|
||||
|
||||
_timer = null;
|
||||
Properties = null;
|
||||
|
||||
_disposed = true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
|
|
@ -42,30 +42,43 @@ namespace Emby.Dlna.PlayTo
|
|||
private readonly IDeviceDiscovery _deviceDiscovery;
|
||||
private readonly string _serverAddress;
|
||||
private readonly string _accessToken;
|
||||
private readonly DateTime _creationTime;
|
||||
|
||||
public bool IsSessionActive => !_disposed && _device != null;
|
||||
|
||||
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;
|
||||
_sessionManager = sessionManager;
|
||||
_libraryManager = libraryManager;
|
||||
_logger = logger;
|
||||
_dlnaManager = dlnaManager;
|
||||
_userManager = userManager;
|
||||
_imageProcessor = imageProcessor;
|
||||
_serverAddress = serverAddress;
|
||||
_accessToken = accessToken;
|
||||
_deviceDiscovery = deviceDiscovery;
|
||||
_userDataManager = userDataManager;
|
||||
_localization = localization;
|
||||
_mediaSourceManager = mediaSourceManager;
|
||||
_config = config;
|
||||
_mediaEncoder = mediaEncoder;
|
||||
_accessToken = accessToken;
|
||||
_logger = logger;
|
||||
_creationTime = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
public void Init(Device device)
|
||||
|
@ -374,9 +387,7 @@ namespace Emby.Dlna.PlayTo
|
|||
return _device.IsPaused ? _device.SetPlay(CancellationToken.None) : _device.SetPause(CancellationToken.None);
|
||||
|
||||
case PlaystateCommand.Seek:
|
||||
{
|
||||
return Seek(command.SeekPositionTicks ?? 0);
|
||||
}
|
||||
return Seek(command.SeekPositionTicks ?? 0);
|
||||
|
||||
case PlaystateCommand.NextTrack:
|
||||
return SetPlaylistIndex(_currentPlaylistIndex + 1);
|
||||
|
@ -442,8 +453,7 @@ namespace Emby.Dlna.PlayTo
|
|||
var profile = _dlnaManager.GetProfile(deviceInfo.ToDeviceIdentification()) ??
|
||||
_dlnaManager.GetDefaultProfile();
|
||||
|
||||
var hasMediaSources = item as IHasMediaSources;
|
||||
var mediaSources = hasMediaSources != null
|
||||
var mediaSources = item is IHasMediaSources
|
||||
? (_mediaSourceManager.GetStaticMediaSources(item, true, user))
|
||||
: new List<MediaSourceInfo>();
|
||||
|
||||
|
@ -452,7 +462,7 @@ namespace Emby.Dlna.PlayTo
|
|||
|
||||
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);
|
||||
|
||||
playlistItem.Didl = itemXml;
|
||||
|
|
|
@ -16,7 +16,6 @@ using MediaBrowser.Model.Events;
|
|||
using MediaBrowser.Model.Globalization;
|
||||
using MediaBrowser.Model.Net;
|
||||
using MediaBrowser.Model.Session;
|
||||
using MediaBrowser.Model.Threading;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Emby.Dlna.PlayTo
|
||||
|
@ -39,13 +38,12 @@ namespace Emby.Dlna.PlayTo
|
|||
private readonly IDeviceDiscovery _deviceDiscovery;
|
||||
private readonly IMediaSourceManager _mediaSourceManager;
|
||||
private readonly IMediaEncoder _mediaEncoder;
|
||||
private readonly ITimerFactory _timerFactory;
|
||||
|
||||
private bool _disposed;
|
||||
private SemaphoreSlim _sessionLock = new SemaphoreSlim(1, 1);
|
||||
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;
|
||||
_sessionManager = sessionManager;
|
||||
|
@ -61,7 +59,6 @@ namespace Emby.Dlna.PlayTo
|
|||
_localization = localization;
|
||||
_mediaSourceManager = mediaSourceManager;
|
||||
_mediaEncoder = mediaEncoder;
|
||||
_timerFactory = timerFactory;
|
||||
}
|
||||
|
||||
public void Start()
|
||||
|
@ -168,7 +165,7 @@ namespace Emby.Dlna.PlayTo
|
|||
|
||||
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;
|
||||
|
||||
|
|
|
@ -107,19 +107,19 @@ namespace Emby.Dlna.Server
|
|||
'&'
|
||||
};
|
||||
|
||||
private static readonly string[] s_escapeStringPairs = new string[]
|
||||
{
|
||||
"<",
|
||||
"<",
|
||||
">",
|
||||
">",
|
||||
"\"",
|
||||
""",
|
||||
"'",
|
||||
"'",
|
||||
"&",
|
||||
"&"
|
||||
};
|
||||
private static readonly string[] s_escapeStringPairs = new[]
|
||||
{
|
||||
"<",
|
||||
"<",
|
||||
">",
|
||||
">",
|
||||
"\"",
|
||||
""",
|
||||
"'",
|
||||
"'",
|
||||
"&",
|
||||
"&"
|
||||
};
|
||||
|
||||
private static string GetEscapeSequence(char c)
|
||||
{
|
||||
|
@ -133,7 +133,7 @@ namespace Emby.Dlna.Server
|
|||
return result;
|
||||
}
|
||||
}
|
||||
return c.ToString();
|
||||
return c.ToString(CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
/// <summary>Replaces invalid XML characters in a string with their valid XML equivalent.</summary>
|
||||
|
@ -145,6 +145,7 @@ namespace Emby.Dlna.Server
|
|||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
StringBuilder stringBuilder = null;
|
||||
int length = str.Length;
|
||||
int num = 0;
|
||||
|
@ -230,9 +231,9 @@ namespace Emby.Dlna.Server
|
|||
|
||||
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)
|
||||
|
@ -295,65 +296,62 @@ namespace Emby.Dlna.Server
|
|||
}
|
||||
|
||||
private IEnumerable<DeviceIcon> GetIcons()
|
||||
{
|
||||
var list = new List<DeviceIcon>();
|
||||
|
||||
list.Add(new DeviceIcon
|
||||
=> new[]
|
||||
{
|
||||
MimeType = "image/png",
|
||||
Depth = "24",
|
||||
Width = 240,
|
||||
Height = 240,
|
||||
Url = "icons/logo240.png"
|
||||
});
|
||||
new DeviceIcon
|
||||
{
|
||||
MimeType = "image/png",
|
||||
Depth = "24",
|
||||
Width = 240,
|
||||
Height = 240,
|
||||
Url = "icons/logo240.png"
|
||||
},
|
||||
|
||||
list.Add(new DeviceIcon
|
||||
{
|
||||
MimeType = "image/jpeg",
|
||||
Depth = "24",
|
||||
Width = 240,
|
||||
Height = 240,
|
||||
Url = "icons/logo240.jpg"
|
||||
});
|
||||
new DeviceIcon
|
||||
{
|
||||
MimeType = "image/jpeg",
|
||||
Depth = "24",
|
||||
Width = 240,
|
||||
Height = 240,
|
||||
Url = "icons/logo240.jpg"
|
||||
},
|
||||
|
||||
list.Add(new DeviceIcon
|
||||
{
|
||||
MimeType = "image/png",
|
||||
Depth = "24",
|
||||
Width = 120,
|
||||
Height = 120,
|
||||
Url = "icons/logo120.png"
|
||||
});
|
||||
new DeviceIcon
|
||||
{
|
||||
MimeType = "image/png",
|
||||
Depth = "24",
|
||||
Width = 120,
|
||||
Height = 120,
|
||||
Url = "icons/logo120.png"
|
||||
},
|
||||
|
||||
list.Add(new DeviceIcon
|
||||
{
|
||||
MimeType = "image/jpeg",
|
||||
Depth = "24",
|
||||
Width = 120,
|
||||
Height = 120,
|
||||
Url = "icons/logo120.jpg"
|
||||
});
|
||||
new DeviceIcon
|
||||
{
|
||||
MimeType = "image/jpeg",
|
||||
Depth = "24",
|
||||
Width = 120,
|
||||
Height = 120,
|
||||
Url = "icons/logo120.jpg"
|
||||
},
|
||||
|
||||
list.Add(new DeviceIcon
|
||||
{
|
||||
MimeType = "image/png",
|
||||
Depth = "24",
|
||||
Width = 48,
|
||||
Height = 48,
|
||||
Url = "icons/logo48.png"
|
||||
});
|
||||
new DeviceIcon
|
||||
{
|
||||
MimeType = "image/png",
|
||||
Depth = "24",
|
||||
Width = 48,
|
||||
Height = 48,
|
||||
Url = "icons/logo48.png"
|
||||
},
|
||||
|
||||
list.Add(new DeviceIcon
|
||||
{
|
||||
MimeType = "image/jpeg",
|
||||
Depth = "24",
|
||||
Width = 48,
|
||||
Height = 48,
|
||||
Url = "icons/logo48.jpg"
|
||||
});
|
||||
|
||||
return list;
|
||||
}
|
||||
new DeviceIcon
|
||||
{
|
||||
MimeType = "image/jpeg",
|
||||
Depth = "24",
|
||||
Width = 48,
|
||||
Height = 48,
|
||||
Url = "icons/logo48.jpg"
|
||||
}
|
||||
};
|
||||
|
||||
private IEnumerable<DeviceService> GetServices()
|
||||
{
|
||||
|
|
|
@ -5,7 +5,6 @@ using MediaBrowser.Controller.Configuration;
|
|||
using MediaBrowser.Model.Dlna;
|
||||
using MediaBrowser.Model.Events;
|
||||
using MediaBrowser.Model.Net;
|
||||
using MediaBrowser.Model.Threading;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Rssdp;
|
||||
using Rssdp.Infrastructure;
|
||||
|
@ -48,20 +47,17 @@ namespace Emby.Dlna.Ssdp
|
|||
|
||||
private SsdpDeviceLocator _deviceLocator;
|
||||
|
||||
private readonly ITimerFactory _timerFactory;
|
||||
private readonly ISocketFactory _socketFactory;
|
||||
private ISsdpCommunicationsServer _commsServer;
|
||||
|
||||
public DeviceDiscovery(
|
||||
ILoggerFactory loggerFactory,
|
||||
IServerConfigurationManager config,
|
||||
ISocketFactory socketFactory,
|
||||
ITimerFactory timerFactory)
|
||||
ISocketFactory socketFactory)
|
||||
{
|
||||
_logger = loggerFactory.CreateLogger(nameof(DeviceDiscovery));
|
||||
_config = config;
|
||||
_socketFactory = socketFactory;
|
||||
_timerFactory = timerFactory;
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
_deviceLocator = new SsdpDeviceLocator(_commsServer, _timerFactory);
|
||||
_deviceLocator = new SsdpDeviceLocator(_commsServer);
|
||||
|
||||
// (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
|
||||
|
|
|
@ -83,8 +83,8 @@ namespace Emby.Drawing
|
|||
}
|
||||
}
|
||||
|
||||
public string[] SupportedInputFormats =>
|
||||
new string[]
|
||||
public IReadOnlyCollection<string> SupportedInputFormats =>
|
||||
new HashSet<string>(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
"tiff",
|
||||
"tif",
|
||||
|
@ -137,14 +137,14 @@ namespace Emby.Drawing
|
|||
}
|
||||
}
|
||||
|
||||
public ImageFormat[] GetSupportedImageOutputFormats()
|
||||
{
|
||||
return _imageEncoder.SupportedOutputFormats;
|
||||
}
|
||||
public IReadOnlyCollection<ImageFormat> GetSupportedImageOutputFormats()
|
||||
=> _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)
|
||||
=> TransparentImageTypes.Contains(Path.GetExtension(path).ToLower());
|
||||
=> TransparentImageTypes.Contains(Path.GetExtension(path));
|
||||
|
||||
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));
|
||||
}
|
||||
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)
|
||||
{
|
||||
// If it fails for whatever reason, return the original image
|
||||
|
@ -374,13 +365,13 @@ namespace Emby.Drawing
|
|||
|
||||
filename += "v=" + Version;
|
||||
|
||||
return GetCachePath(ResizedImageCachePath, filename, "." + format.ToString().ToLower());
|
||||
return GetCachePath(ResizedImageCachePath, filename, "." + format.ToString().ToLowerInvariant());
|
||||
}
|
||||
|
||||
public ImageDimensions GetImageSize(BaseItem item, ItemImageInfo info)
|
||||
=> GetImageSize(item, info, true);
|
||||
public ImageDimensions GetImageDimensions(BaseItem item, ItemImageInfo info)
|
||||
=> 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 height = info.Height;
|
||||
|
@ -393,7 +384,7 @@ namespace Emby.Drawing
|
|||
string path = info.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.Height = size.Height;
|
||||
|
||||
|
@ -408,7 +399,7 @@ namespace Emby.Drawing
|
|||
/// <summary>
|
||||
/// Gets the size of the image.
|
||||
/// </summary>
|
||||
public ImageDimensions GetImageSize(string path)
|
||||
public ImageDimensions GetImageDimensions(string path)
|
||||
=> _imageEncoder.GetImageSize(path);
|
||||
|
||||
/// <summary>
|
||||
|
@ -481,7 +472,7 @@ namespace Emby.Drawing
|
|||
return (originalImagePath, dateModified);
|
||||
}
|
||||
|
||||
if (!_imageEncoder.SupportedInputFormats.Contains(inputFormat, StringComparer.OrdinalIgnoreCase))
|
||||
if (!_imageEncoder.SupportedInputFormats.Contains(inputFormat))
|
||||
{
|
||||
try
|
||||
{
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using MediaBrowser.Controller.Drawing;
|
||||
using MediaBrowser.Model.Drawing;
|
||||
|
||||
|
@ -6,15 +7,11 @@ namespace Emby.Drawing
|
|||
{
|
||||
public class NullImageEncoder : IImageEncoder
|
||||
{
|
||||
public string[] SupportedInputFormats =>
|
||||
new[]
|
||||
{
|
||||
"png",
|
||||
"jpeg",
|
||||
"jpg"
|
||||
};
|
||||
public IReadOnlyCollection<string> SupportedInputFormats
|
||||
=> new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "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)
|
||||
{
|
||||
|
|
|
@ -121,7 +121,7 @@ namespace IsoMounter
|
|||
path,
|
||||
Path.GetExtension(path),
|
||||
EnvironmentInfo.OperatingSystem,
|
||||
ExecutablesAvailable.ToString()
|
||||
ExecutablesAvailable
|
||||
);
|
||||
|
||||
if (ExecutablesAvailable)
|
||||
|
@ -183,7 +183,7 @@ namespace IsoMounter
|
|||
_logger.LogInformation(
|
||||
"[{0}] Disposing [{1}].",
|
||||
Name,
|
||||
disposing.ToString()
|
||||
disposing
|
||||
);
|
||||
|
||||
if (disposing)
|
||||
|
@ -229,9 +229,8 @@ namespace IsoMounter
|
|||
var uid = getuid();
|
||||
|
||||
_logger.LogDebug(
|
||||
"[{0}] Our current UID is [{1}], GetUserId() returned [{2}].",
|
||||
"[{0}] GetUserId() returned [{2}].",
|
||||
Name,
|
||||
uid.ToString(),
|
||||
uid
|
||||
);
|
||||
|
||||
|
|
|
@ -73,11 +73,6 @@ namespace Emby.Notifications
|
|||
Type = NotificationType.AudioPlayback.ToString()
|
||||
},
|
||||
|
||||
new NotificationTypeInfo
|
||||
{
|
||||
Type = NotificationType.GamePlayback.ToString()
|
||||
},
|
||||
|
||||
new NotificationTypeInfo
|
||||
{
|
||||
Type = NotificationType.VideoPlayback.ToString()
|
||||
|
@ -88,11 +83,6 @@ namespace Emby.Notifications
|
|||
Type = NotificationType.AudioPlaybackStopped.ToString()
|
||||
},
|
||||
|
||||
new NotificationTypeInfo
|
||||
{
|
||||
Type = NotificationType.GamePlaybackStopped.ToString()
|
||||
},
|
||||
|
||||
new NotificationTypeInfo
|
||||
{
|
||||
Type = NotificationType.VideoPlaybackStopped.ToString()
|
||||
|
|
|
@ -20,7 +20,6 @@ using MediaBrowser.Model.Events;
|
|||
using MediaBrowser.Model.Globalization;
|
||||
using MediaBrowser.Model.Notifications;
|
||||
using MediaBrowser.Model.Tasks;
|
||||
using MediaBrowser.Model.Threading;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Emby.Notifications
|
||||
|
@ -40,9 +39,8 @@ namespace Emby.Notifications
|
|||
private readonly ILibraryManager _libraryManager;
|
||||
private readonly ISessionManager _sessionManager;
|
||||
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 IConfigurationManager _config;
|
||||
|
@ -52,7 +50,7 @@ namespace Emby.Notifications
|
|||
|
||||
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;
|
||||
_userManager = userManager;
|
||||
|
@ -64,19 +62,20 @@ namespace Emby.Notifications
|
|||
_appHost = appHost;
|
||||
_config = config;
|
||||
_deviceManager = deviceManager;
|
||||
_timerFactory = timerFactory;
|
||||
_localization = localization;
|
||||
_activityManager = activityManager;
|
||||
|
||||
_coreNotificationTypes = new CoreNotificationTypes(localization, appHost).GetNotificationTypes().Select(i => i.Type).ToArray();
|
||||
}
|
||||
|
||||
public void Run()
|
||||
public Task RunAsync()
|
||||
{
|
||||
_libraryManager.ItemAdded += _libraryManager_ItemAdded;
|
||||
_appHost.HasPendingRestartChanged += _appHost_HasPendingRestartChanged;
|
||||
_appHost.HasUpdateAvailableChanged += _appHost_HasUpdateAvailableChanged;
|
||||
_activityManager.EntryCreated += _activityManager_EntryCreated;
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private async void _appHost_HasPendingRestartChanged(object sender, EventArgs e)
|
||||
|
@ -157,7 +156,7 @@ namespace Emby.Notifications
|
|||
{
|
||||
if (LibraryUpdateTimer == null)
|
||||
{
|
||||
LibraryUpdateTimer = _timerFactory.Create(LibraryUpdateTimerCallback, null, 5000,
|
||||
LibraryUpdateTimer = new Timer(LibraryUpdateTimerCallback, null, 5000,
|
||||
Timeout.Infinite);
|
||||
}
|
||||
else
|
||||
|
|
|
@ -181,7 +181,7 @@ namespace Emby.Photos
|
|||
|
||||
try
|
||||
{
|
||||
var size = _imageProcessor.GetImageSize(item, img, false);
|
||||
var size = _imageProcessor.GetImageDimensions(item, img, false);
|
||||
|
||||
if (size.Width > 0 && size.Height > 0)
|
||||
{
|
||||
|
|
|
@ -2,6 +2,7 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Common.Configuration;
|
||||
using MediaBrowser.Common.Plugins;
|
||||
using MediaBrowser.Common.Updates;
|
||||
|
@ -58,7 +59,7 @@ namespace Emby.Server.Implementations.Activity
|
|||
_deviceManager = deviceManager;
|
||||
}
|
||||
|
||||
public void Run()
|
||||
public Task RunAsync()
|
||||
{
|
||||
_taskManager.TaskCompleted += _taskManager_TaskCompleted;
|
||||
|
||||
|
@ -90,6 +91,8 @@ namespace Emby.Server.Implementations.Activity
|
|||
_deviceManager.CameraImageUploaded += _deviceManager_CameraImageUploaded;
|
||||
|
||||
_appHost.ApplicationUpdated += _appHost_ApplicationUpdated;
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
void _deviceManager_CameraImageUploaded(object sender, GenericEventArgs<CameraImageUploadInfo> e)
|
||||
|
@ -207,10 +210,6 @@ namespace Emby.Server.Implementations.Activity
|
|||
{
|
||||
return NotificationType.AudioPlayback.ToString();
|
||||
}
|
||||
if (string.Equals(mediaType, MediaType.Game, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return NotificationType.GamePlayback.ToString();
|
||||
}
|
||||
if (string.Equals(mediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return NotificationType.VideoPlayback.ToString();
|
||||
|
@ -225,10 +224,6 @@ namespace Emby.Server.Implementations.Activity
|
|||
{
|
||||
return NotificationType.AudioPlaybackStopped.ToString();
|
||||
}
|
||||
if (string.Equals(mediaType, MediaType.Game, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return NotificationType.GamePlaybackStopped.ToString();
|
||||
}
|
||||
if (string.Equals(mediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return NotificationType.VideoPlaybackStopped.ToString();
|
||||
|
|
|
@ -16,12 +16,14 @@ namespace Emby.Server.Implementations.AppBase
|
|||
string programDataPath,
|
||||
string appFolderPath,
|
||||
string logDirectoryPath = null,
|
||||
string configurationDirectoryPath = null)
|
||||
string configurationDirectoryPath = null,
|
||||
string cacheDirectoryPath = null)
|
||||
{
|
||||
ProgramDataPath = programDataPath;
|
||||
ProgramSystemPath = appFolderPath;
|
||||
LogDirectoryPath = logDirectoryPath;
|
||||
ConfigurationDirectoryPath = configurationDirectoryPath;
|
||||
CachePath = cacheDirectoryPath;
|
||||
}
|
||||
|
||||
public string ProgramDataPath { get; private set; }
|
||||
|
|
|
@ -171,16 +171,29 @@ namespace Emby.Server.Implementations.AppBase
|
|||
private void UpdateCachePath()
|
||||
{
|
||||
string cachePath;
|
||||
|
||||
// If the configuration file has no entry (i.e. not set in UI)
|
||||
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
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -217,7 +230,7 @@ namespace Emby.Server.Implementations.AppBase
|
|||
|
||||
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)
|
||||
|
|
|
@ -12,7 +12,6 @@ using System.Security.Cryptography.X509Certificates;
|
|||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Emby.Common.Implementations.Serialization;
|
||||
using Emby.Dlna;
|
||||
using Emby.Dlna.Main;
|
||||
using Emby.Dlna.Ssdp;
|
||||
|
@ -43,7 +42,6 @@ using Emby.Server.Implementations.ScheduledTasks;
|
|||
using Emby.Server.Implementations.Security;
|
||||
using Emby.Server.Implementations.Serialization;
|
||||
using Emby.Server.Implementations.Session;
|
||||
using Emby.Server.Implementations.Threading;
|
||||
using Emby.Server.Implementations.TV;
|
||||
using Emby.Server.Implementations.Updates;
|
||||
using Emby.Server.Implementations.Xml;
|
||||
|
@ -99,7 +97,6 @@ using MediaBrowser.Model.Serialization;
|
|||
using MediaBrowser.Model.Services;
|
||||
using MediaBrowser.Model.System;
|
||||
using MediaBrowser.Model.Tasks;
|
||||
using MediaBrowser.Model.Threading;
|
||||
using MediaBrowser.Model.Updates;
|
||||
using MediaBrowser.Model.Xml;
|
||||
using MediaBrowser.Providers.Chapters;
|
||||
|
@ -110,7 +107,6 @@ using MediaBrowser.XbmcMetadata.Providers;
|
|||
using Microsoft.Extensions.Logging;
|
||||
using ServiceStack;
|
||||
using ServiceStack.Text.Jsv;
|
||||
using StringExtensions = MediaBrowser.Controller.Extensions.StringExtensions;
|
||||
using X509Certificate = System.Security.Cryptography.X509Certificates.X509Certificate;
|
||||
|
||||
namespace Emby.Server.Implementations
|
||||
|
@ -141,7 +137,7 @@ namespace Emby.Server.Implementations
|
|||
return false;
|
||||
}
|
||||
|
||||
if (StartupOptions.ContainsOption("-service"))
|
||||
if (StartupOptions.IsService)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -302,7 +298,7 @@ namespace Emby.Server.Implementations
|
|||
|
||||
private ILiveTvManager LiveTvManager { get; set; }
|
||||
|
||||
public ILocalizationManager LocalizationManager { get; set; }
|
||||
public LocalizationManager LocalizationManager { get; set; }
|
||||
|
||||
private IEncodingManager EncodingManager { get; set; }
|
||||
private IChannelManager ChannelManager { get; set; }
|
||||
|
@ -343,12 +339,11 @@ namespace Emby.Server.Implementations
|
|||
protected IHttpResultFactory HttpResultFactory { 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; }
|
||||
|
||||
protected IProcessFactory ProcessFactory { get; private set; }
|
||||
protected ITimerFactory TimerFactory { get; private set; }
|
||||
protected ICryptoProvider CryptographyProvider = new CryptographyProvider();
|
||||
protected readonly IXmlSerializer XmlSerializer;
|
||||
|
||||
|
@ -364,7 +359,7 @@ namespace Emby.Server.Implementations
|
|||
/// </summary>
|
||||
public ApplicationHost(ServerApplicationPaths applicationPaths,
|
||||
ILoggerFactory loggerFactory,
|
||||
StartupOptions options,
|
||||
IStartupOptions options,
|
||||
IFileSystem fileSystem,
|
||||
IEnvironmentInfo environmentInfo,
|
||||
IImageEncoder imageEncoder,
|
||||
|
@ -646,8 +641,10 @@ namespace Emby.Server.Implementations
|
|||
/// <summary>
|
||||
/// Runs the startup tasks.
|
||||
/// </summary>
|
||||
public Task RunStartupTasks()
|
||||
public async Task RunStartupTasks()
|
||||
{
|
||||
Logger.LogInformation("Running startup tasks");
|
||||
|
||||
Resolve<ITaskManager>().AddTasks(GetExports<IScheduledTask>(false));
|
||||
|
||||
ConfigurationManager.ConfigurationUpdated += OnConfigurationUpdated;
|
||||
|
@ -666,20 +663,20 @@ namespace Emby.Server.Implementations
|
|||
Logger.LogInformation("ServerId: {0}", SystemId);
|
||||
|
||||
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");
|
||||
HttpServer.GlobalResponse = null;
|
||||
|
||||
Logger.LogInformation("Post-init migrations complete");
|
||||
|
||||
RunEntryPoints(entryPoints, false);
|
||||
Logger.LogInformation("All entry points have started");
|
||||
|
||||
return Task.CompletedTask;
|
||||
now = DateTime.UtcNow;
|
||||
await Task.WhenAll(StartEntryPoints(entryPoints, false));
|
||||
Logger.LogInformation("Executed all post-startup entry points in {Elapsed:fff} ms", DateTime.Now - now);
|
||||
}
|
||||
|
||||
private void RunEntryPoints(IEnumerable<IServerEntryPoint> entryPoints, bool isBeforeStartup)
|
||||
private IEnumerable<Task> StartEntryPoints(IEnumerable<IServerEntryPoint> entryPoints, bool isBeforeStartup)
|
||||
{
|
||||
foreach (var entryPoint in entryPoints)
|
||||
{
|
||||
|
@ -688,22 +685,13 @@ namespace Emby.Server.Implementations
|
|||
continue;
|
||||
}
|
||||
|
||||
var name = entryPoint.GetType().FullName;
|
||||
Logger.LogInformation("Starting entry point {Name}", name);
|
||||
var now = DateTime.UtcNow;
|
||||
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");
|
||||
Logger.LogDebug("Starting entry point {Type}", entryPoint.GetType());
|
||||
|
||||
yield return entryPoint.RunAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public void Init()
|
||||
public async Task Init()
|
||||
{
|
||||
HttpPort = ServerConfigurationManager.Configuration.HttpServerPortNumber;
|
||||
HttpsPort = ServerConfigurationManager.Configuration.HttpsPortNumber;
|
||||
|
@ -733,7 +721,7 @@ namespace Emby.Server.Implementations
|
|||
|
||||
SetHttpLimit();
|
||||
|
||||
RegisterResources();
|
||||
await RegisterResources();
|
||||
|
||||
FindParts();
|
||||
}
|
||||
|
@ -748,7 +736,7 @@ namespace Emby.Server.Implementations
|
|||
/// <summary>
|
||||
/// Registers resources that classes will depend on
|
||||
/// </summary>
|
||||
protected void RegisterResources()
|
||||
protected async Task RegisterResources()
|
||||
{
|
||||
RegisterSingleInstance(ConfigurationManager);
|
||||
RegisterSingleInstance<IApplicationHost>(this);
|
||||
|
@ -780,9 +768,6 @@ namespace Emby.Server.Implementations
|
|||
ProcessFactory = new ProcessFactory();
|
||||
RegisterSingleInstance(ProcessFactory);
|
||||
|
||||
TimerFactory = new TimerFactory();
|
||||
RegisterSingleInstance(TimerFactory);
|
||||
|
||||
var streamHelper = CreateStreamHelper();
|
||||
ApplicationHost.StreamHelper = streamHelper;
|
||||
RegisterSingleInstance(streamHelper);
|
||||
|
@ -809,9 +794,9 @@ namespace Emby.Server.Implementations
|
|||
IAssemblyInfo assemblyInfo = new AssemblyInfo();
|
||||
RegisterSingleInstance(assemblyInfo);
|
||||
|
||||
LocalizationManager = new LocalizationManager(ServerConfigurationManager, FileSystemManager, JsonSerializer, LoggerFactory, assemblyInfo, new TextLocalizer());
|
||||
StringExtensions.LocalizationManager = LocalizationManager;
|
||||
RegisterSingleInstance(LocalizationManager);
|
||||
LocalizationManager = new LocalizationManager(ServerConfigurationManager, FileSystemManager, JsonSerializer, LoggerFactory);
|
||||
await LocalizationManager.LoadAll();
|
||||
RegisterSingleInstance<ILocalizationManager>(LocalizationManager);
|
||||
|
||||
BlurayExaminer = new BdInfoExaminer(FileSystemManager);
|
||||
RegisterSingleInstance(BlurayExaminer);
|
||||
|
@ -845,7 +830,7 @@ namespace Emby.Server.Implementations
|
|||
var musicManager = 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<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);
|
||||
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);
|
||||
|
||||
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);
|
||||
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);
|
||||
|
||||
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);
|
||||
RegisterSingleInstance(PlaylistManager);
|
||||
|
||||
LiveTvManager = new LiveTvManager(this, ServerConfigurationManager, LoggerFactory, ItemRepository, ImageProcessor, UserDataManager, DtoService, UserManager, LibraryManager, TaskManager, LocalizationManager, JsonSerializer, ProviderManager, FileSystemManager, () => ChannelManager);
|
||||
LiveTvManager = new LiveTvManager(this, ServerConfigurationManager, LoggerFactory, ItemRepository, ImageProcessor, UserDataManager, DtoService, UserManager, LibraryManager, TaskManager, LocalizationManager, JsonSerializer, FileSystemManager, () => ChannelManager);
|
||||
RegisterSingleInstance(LiveTvManager);
|
||||
|
||||
UserViewManager = new UserViewManager(LibraryManager, LocalizationManager, UserManager, ChannelManager, LiveTvManager, ServerConfigurationManager);
|
||||
|
@ -913,7 +898,7 @@ namespace Emby.Server.Implementations
|
|||
NotificationManager = new NotificationManager(LoggerFactory, UserManager, ServerConfigurationManager);
|
||||
RegisterSingleInstance(NotificationManager);
|
||||
|
||||
RegisterSingleInstance<IDeviceDiscovery>(new DeviceDiscovery(LoggerFactory, ServerConfigurationManager, SocketFactory, TimerFactory));
|
||||
RegisterSingleInstance<IDeviceDiscovery>(new DeviceDiscovery(LoggerFactory, ServerConfigurationManager, SocketFactory));
|
||||
|
||||
ChapterManager = new ChapterManager(LibraryManager, LoggerFactory, ServerConfigurationManager, ItemRepository);
|
||||
RegisterSingleInstance(ChapterManager);
|
||||
|
@ -1671,7 +1656,6 @@ namespace Emby.Server.Implementations
|
|||
|
||||
var minRequiredVersions = new Dictionary<string, Version>(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
{ "GameBrowser.dll", new Version(3, 1) },
|
||||
{ "moviethemesongs.dll", new Version(1, 6) },
|
||||
{ "themesongs.dll", new Version(1, 2) }
|
||||
};
|
||||
|
@ -1747,7 +1731,7 @@ namespace Emby.Server.Implementations
|
|||
EncoderLocationType = MediaEncoder.EncoderLocationType,
|
||||
SystemArchitecture = EnvironmentInfo.SystemArchitecture,
|
||||
SystemUpdateLevel = SystemUpdateLevel,
|
||||
PackageName = StartupOptions.GetOption("-package")
|
||||
PackageName = StartupOptions.PackageName
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -70,7 +70,8 @@ namespace Emby.Server.Implementations.Collections
|
|||
return null;
|
||||
})
|
||||
.Where(i => i != null)
|
||||
.DistinctBy(i => i.Id)
|
||||
.GroupBy(x => x.Id)
|
||||
.Select(x => x.First())
|
||||
.OrderBy(i => Guid.NewGuid())
|
||||
.ToList();
|
||||
}
|
||||
|
|
|
@ -353,7 +353,7 @@ namespace Emby.Server.Implementations.Collections
|
|||
_logger = logger;
|
||||
}
|
||||
|
||||
public async void Run()
|
||||
public async Task RunAsync()
|
||||
{
|
||||
if (!_config.Configuration.CollectionsUpgraded && _config.Configuration.IsStartupWizardCompleted)
|
||||
{
|
||||
|
|
|
@ -1239,10 +1239,6 @@ namespace Emby.Server.Implementations.Data
|
|||
{
|
||||
return false;
|
||||
}
|
||||
else if (type == typeof(GameGenre))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (type == typeof(Genre))
|
||||
{
|
||||
return false;
|
||||
|
@ -3905,7 +3901,7 @@ namespace Emby.Server.Implementations.Data
|
|||
// lowercase this because SortName is stored as lowercase
|
||||
if (statement != null)
|
||||
{
|
||||
statement.TryBind("@NameStartsWithOrGreater", query.NameStartsWithOrGreater.ToLower());
|
||||
statement.TryBind("@NameStartsWithOrGreater", query.NameStartsWithOrGreater.ToLowerInvariant());
|
||||
}
|
||||
}
|
||||
if (!string.IsNullOrWhiteSpace(query.NameLessThan))
|
||||
|
@ -3914,7 +3910,7 @@ namespace Emby.Server.Implementations.Data
|
|||
// lowercase this because SortName is stored as lowercase
|
||||
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);
|
||||
}
|
||||
if (IsTypeInQuery(typeof(GameGenre).Name, query))
|
||||
{
|
||||
list.Add(typeof(GameGenre).Name);
|
||||
}
|
||||
if (IsTypeInQuery(typeof(MusicArtist).Name, query))
|
||||
{
|
||||
list.Add(typeof(MusicArtist).Name);
|
||||
|
@ -4822,7 +4814,7 @@ namespace Emby.Server.Implementations.Data
|
|||
return value;
|
||||
}
|
||||
|
||||
return value.RemoveDiacritics().ToLower();
|
||||
return value.RemoveDiacritics().ToLowerInvariant();
|
||||
}
|
||||
|
||||
private bool EnableGroupByPresentationUniqueKey(InternalItemsQuery query)
|
||||
|
@ -4891,9 +4883,6 @@ namespace Emby.Server.Implementations.Data
|
|||
typeof(Book),
|
||||
typeof(CollectionFolder),
|
||||
typeof(Folder),
|
||||
typeof(Game),
|
||||
typeof(GameGenre),
|
||||
typeof(GameSystem),
|
||||
typeof(Genre),
|
||||
typeof(Person),
|
||||
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);
|
||||
}
|
||||
|
||||
public QueryResult<Tuple<BaseItem, ItemCounts>> GetGameGenres(InternalItemsQuery query)
|
||||
{
|
||||
return GetItemValues(query, new[] { 2 }, typeof(GameGenre).FullName);
|
||||
}
|
||||
|
||||
public QueryResult<Tuple<BaseItem, ItemCounts>> GetMusicGenres(InternalItemsQuery query)
|
||||
{
|
||||
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>());
|
||||
}
|
||||
|
||||
public List<string> GetGameGenreNames()
|
||||
{
|
||||
return GetItemValueNames(new[] { 2 }, new List<string> { "Game" }, new List<string>());
|
||||
}
|
||||
|
||||
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)
|
||||
|
@ -5652,10 +5631,6 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
|
|||
{
|
||||
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))
|
||||
{
|
||||
counts.TrailerCount = value;
|
||||
|
|
|
@ -425,7 +425,7 @@ namespace Emby.Server.Implementations.Devices
|
|||
_logger = logger;
|
||||
}
|
||||
|
||||
public async void Run()
|
||||
public async Task RunAsync()
|
||||
{
|
||||
if (!_config.Configuration.CameraUploadUpgraded && _config.Configuration.IsStartupWizardCompleted)
|
||||
{
|
||||
|
|
|
@ -105,7 +105,7 @@ namespace Emby.Server.Implementations.Diagnostics
|
|||
{
|
||||
return _process.WaitForExit(timeMs);
|
||||
}
|
||||
|
||||
|
||||
public Task<bool> WaitForExitAsync(int timeMs)
|
||||
{
|
||||
//Note: For this function to work correctly, the option EnableRisingEvents needs to be set to true.
|
||||
|
|
|
@ -374,10 +374,6 @@ namespace Emby.Server.Implementations.Dto
|
|||
dto.MusicVideoCount = taggedItems.Count(i => i is MusicVideo);
|
||||
dto.SongCount = taggedItems.Count(i => i is Audio);
|
||||
}
|
||||
else if (item is GameGenre)
|
||||
{
|
||||
dto.GameCount = taggedItems.Count(i => i is Game);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 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.AlbumCount = taggedItems.Count(i => i is MusicAlbum);
|
||||
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.TrailerCount = taggedItems.Count(i => i is Trailer);
|
||||
dto.MusicVideoCount = taggedItems.Count(i => i is MusicVideo);
|
||||
|
@ -532,17 +527,6 @@ namespace Emby.Server.Implementations.Dto
|
|||
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)
|
||||
{
|
||||
return images
|
||||
|
@ -636,7 +620,8 @@ namespace Emby.Server.Implementations.Dto
|
|||
}
|
||||
|
||||
}).Where(i => i != null)
|
||||
.DistinctBy(i => i.Name, StringComparer.OrdinalIgnoreCase)
|
||||
.GroupBy(i => i.Name, StringComparer.OrdinalIgnoreCase)
|
||||
.Select(x => x.First())
|
||||
.ToDictionary(i => i.Name, StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
for (var i = 0; i < people.Count; i++)
|
||||
|
@ -698,11 +683,6 @@ namespace Emby.Server.Implementations.Dto
|
|||
return _libraryManager.GetMusicGenreId(name);
|
||||
}
|
||||
|
||||
if (owner is Game || owner is GameSystem)
|
||||
{
|
||||
return _libraryManager.GetGameGenreId(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;
|
||||
if (musicVideo != null)
|
||||
{
|
||||
|
@ -1452,7 +1418,7 @@ namespace Emby.Server.Implementations.Dto
|
|||
|
||||
try
|
||||
{
|
||||
size = _imageProcessor.GetImageSize(item, imageInfo);
|
||||
size = _imageProcessor.GetImageDimensions(item, imageInfo);
|
||||
|
||||
if (size.Width <= 0 || size.Height <= 0)
|
||||
{
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Emby.Naming\Emby.Naming.csproj" />
|
||||
|
@ -25,8 +25,7 @@
|
|||
<PackageReference Include="ServiceStack.Text.Core" Version="5.4.0" />
|
||||
<PackageReference Include="sharpcompress" Version="0.22.0" />
|
||||
<PackageReference Include="SimpleInjector" Version="4.4.2" />
|
||||
<PackageReference Include="SQLitePCL.pretty.core" Version="1.1.8" />
|
||||
<PackageReference Include="SQLitePCLRaw.core" Version="1.1.11" />
|
||||
<PackageReference Include="SQLitePCL.pretty.netstandard" Version="1.0.0" />
|
||||
<PackageReference Include="UTF.Unknown" Version="1.0.0-beta1" />
|
||||
</ItemGroup>
|
||||
|
||||
|
@ -43,7 +42,7 @@
|
|||
<EmbeddedResource Include="Localization\iso6392.txt" />
|
||||
<EmbeddedResource Include="Localization\countries.json" />
|
||||
<EmbeddedResource Include="Localization\Core\*.json" />
|
||||
<EmbeddedResource Include="Localization\Ratings\*.txt" />
|
||||
<EmbeddedResource Include="Localization\Ratings\*.csv" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
@ -9,7 +9,6 @@ using MediaBrowser.Controller.Plugins;
|
|||
using MediaBrowser.Controller.Session;
|
||||
using MediaBrowser.Model.LiveTv;
|
||||
using MediaBrowser.Model.Tasks;
|
||||
using MediaBrowser.Model.Threading;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Emby.Server.Implementations.EntryPoints
|
||||
|
@ -22,11 +21,10 @@ namespace Emby.Server.Implementations.EntryPoints
|
|||
private readonly ISessionManager _sessionManager;
|
||||
private readonly IServerConfigurationManager _config;
|
||||
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;
|
||||
_logger = logger;
|
||||
|
@ -34,15 +32,16 @@ namespace Emby.Server.Implementations.EntryPoints
|
|||
_sessionManager = sessionManager;
|
||||
_config = config;
|
||||
_liveTvManager = liveTvManager;
|
||||
_timerFactory = timerFactory;
|
||||
}
|
||||
|
||||
public void Run()
|
||||
public Task RunAsync()
|
||||
{
|
||||
if (_appHost.CanSelfRestart)
|
||||
{
|
||||
_appHost.HasPendingRestartChanged += _appHost_HasPendingRestartChanged;
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
void _appHost_HasPendingRestartChanged(object sender, EventArgs e)
|
||||
|
@ -51,7 +50,7 @@ namespace Emby.Server.Implementations.EntryPoints
|
|||
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -10,7 +10,6 @@ using MediaBrowser.Controller.Configuration;
|
|||
using MediaBrowser.Controller.Plugins;
|
||||
using MediaBrowser.Model.Dlna;
|
||||
using MediaBrowser.Model.Events;
|
||||
using MediaBrowser.Model.Threading;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Mono.Nat;
|
||||
|
||||
|
@ -24,19 +23,17 @@ namespace Emby.Server.Implementations.EntryPoints
|
|||
private readonly IServerConfigurationManager _config;
|
||||
private readonly IDeviceDiscovery _deviceDiscovery;
|
||||
|
||||
private ITimer _timer;
|
||||
private readonly ITimerFactory _timerFactory;
|
||||
private Timer _timer;
|
||||
|
||||
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");
|
||||
_appHost = appHost;
|
||||
_config = config;
|
||||
_deviceDiscovery = deviceDiscovery;
|
||||
_httpClient = httpClient;
|
||||
_timerFactory = timerFactory;
|
||||
_config.ConfigurationUpdated += _config_ConfigurationUpdated1;
|
||||
}
|
||||
|
||||
|
@ -61,17 +58,17 @@ namespace Emby.Server.Implementations.EntryPoints
|
|||
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))
|
||||
{
|
||||
DisposeNat();
|
||||
|
||||
Run();
|
||||
await RunAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public void Run()
|
||||
public Task RunAsync()
|
||||
{
|
||||
if (_config.Configuration.EnableUPnP && _config.Configuration.EnableRemoteAccess)
|
||||
{
|
||||
|
@ -80,6 +77,8 @@ namespace Emby.Server.Implementations.EntryPoints
|
|||
|
||||
_config.ConfigurationUpdated -= _config_ConfigurationUpdated;
|
||||
_config.ConfigurationUpdated += _config_ConfigurationUpdated;
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private void Start()
|
||||
|
@ -92,7 +91,7 @@ namespace Emby.Server.Implementations.EntryPoints
|
|||
_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;
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
|||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Controller.Channels;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Entities.Audio;
|
||||
|
@ -13,7 +14,6 @@ using MediaBrowser.Controller.Session;
|
|||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.Events;
|
||||
using MediaBrowser.Model.Extensions;
|
||||
using MediaBrowser.Model.Threading;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Emby.Server.Implementations.EntryPoints
|
||||
|
@ -28,7 +28,6 @@ namespace Emby.Server.Implementations.EntryPoints
|
|||
private readonly ISessionManager _sessionManager;
|
||||
private readonly IUserManager _userManager;
|
||||
private readonly ILogger _logger;
|
||||
private readonly ITimerFactory _timerFactory;
|
||||
|
||||
/// <summary>
|
||||
/// The _library changed sync lock
|
||||
|
@ -46,7 +45,7 @@ namespace Emby.Server.Implementations.EntryPoints
|
|||
/// Gets or sets the library update timer.
|
||||
/// </summary>
|
||||
/// <value>The library update timer.</value>
|
||||
private ITimer LibraryUpdateTimer { get; set; }
|
||||
private Timer LibraryUpdateTimer { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The library update duration
|
||||
|
@ -55,17 +54,16 @@ namespace Emby.Server.Implementations.EntryPoints
|
|||
|
||||
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;
|
||||
_sessionManager = sessionManager;
|
||||
_userManager = userManager;
|
||||
_logger = logger;
|
||||
_timerFactory = timerFactory;
|
||||
_providerManager = providerManager;
|
||||
}
|
||||
|
||||
public void Run()
|
||||
public Task RunAsync()
|
||||
{
|
||||
_libraryManager.ItemAdded += libraryManager_ItemAdded;
|
||||
_libraryManager.ItemUpdated += libraryManager_ItemUpdated;
|
||||
|
@ -74,6 +72,8 @@ namespace Emby.Server.Implementations.EntryPoints
|
|||
_providerManager.RefreshCompleted += _providerManager_RefreshCompleted;
|
||||
_providerManager.RefreshStarted += _providerManager_RefreshStarted;
|
||||
_providerManager.RefreshProgress += _providerManager_RefreshProgress;
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private Dictionary<Guid, DateTime> _lastProgressMessageTimes = new Dictionary<Guid, DateTime>();
|
||||
|
@ -188,7 +188,7 @@ namespace Emby.Server.Implementations.EntryPoints
|
|||
{
|
||||
if (LibraryUpdateTimer == null)
|
||||
{
|
||||
LibraryUpdateTimer = _timerFactory.Create(LibraryUpdateTimerCallback, null, LibraryUpdateDuration,
|
||||
LibraryUpdateTimer = new Timer(LibraryUpdateTimerCallback, null, LibraryUpdateDuration,
|
||||
Timeout.Infinite);
|
||||
}
|
||||
else
|
||||
|
@ -222,7 +222,7 @@ namespace Emby.Server.Implementations.EntryPoints
|
|||
{
|
||||
if (LibraryUpdateTimer == null)
|
||||
{
|
||||
LibraryUpdateTimer = _timerFactory.Create(LibraryUpdateTimerCallback, null, LibraryUpdateDuration,
|
||||
LibraryUpdateTimer = new Timer(LibraryUpdateTimerCallback, null, LibraryUpdateDuration,
|
||||
Timeout.Infinite);
|
||||
}
|
||||
else
|
||||
|
@ -250,7 +250,7 @@ namespace Emby.Server.Implementations.EntryPoints
|
|||
{
|
||||
if (LibraryUpdateTimer == null)
|
||||
{
|
||||
LibraryUpdateTimer = _timerFactory.Create(LibraryUpdateTimerCallback, null, LibraryUpdateDuration,
|
||||
LibraryUpdateTimer = new Timer(LibraryUpdateTimerCallback, null, LibraryUpdateDuration,
|
||||
Timeout.Infinite);
|
||||
}
|
||||
else
|
||||
|
@ -277,14 +277,21 @@ namespace Emby.Server.Implementations.EntryPoints
|
|||
lock (_libraryChangedSyncLock)
|
||||
{
|
||||
// Remove dupes in case some were saved multiple times
|
||||
var foldersAddedTo = _foldersAddedTo.DistinctBy(i => i.Id).ToList();
|
||||
var foldersAddedTo = _foldersAddedTo
|
||||
.GroupBy(x => x.Id)
|
||||
.Select(x => x.First())
|
||||
.ToList();
|
||||
|
||||
var foldersRemovedFrom = _foldersRemovedFrom.DistinctBy(i => i.Id).ToList();
|
||||
var foldersRemovedFrom = _foldersRemovedFrom
|
||||
.GroupBy(x => x.Id)
|
||||
.Select(x => x.First())
|
||||
.ToList();
|
||||
|
||||
var itemsUpdated = _itemsUpdated
|
||||
.Where(i => !_itemsAdded.Contains(i))
|
||||
.DistinctBy(i => i.Id)
|
||||
.ToList();
|
||||
.Where(i => !_itemsAdded.Contains(i))
|
||||
.GroupBy(x => x.Id)
|
||||
.Select(x => x.First())
|
||||
.ToList();
|
||||
|
||||
SendChangeNotifications(_itemsAdded.ToList(), itemsUpdated, _itemsRemoved.ToList(), foldersAddedTo, foldersRemovedFrom, CancellationToken.None);
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.LiveTv;
|
||||
using MediaBrowser.Controller.Plugins;
|
||||
|
@ -24,12 +25,14 @@ namespace Emby.Server.Implementations.EntryPoints
|
|||
_liveTvManager = liveTvManager;
|
||||
}
|
||||
|
||||
public void Run()
|
||||
public Task RunAsync()
|
||||
{
|
||||
_liveTvManager.TimerCancelled += _liveTvManager_TimerCancelled;
|
||||
_liveTvManager.SeriesTimerCancelled += _liveTvManager_SeriesTimerCancelled;
|
||||
_liveTvManager.TimerCreated += _liveTvManager_TimerCreated;
|
||||
_liveTvManager.SeriesTimerCreated += _liveTvManager_SeriesTimerCreated;
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private void _liveTvManager_SeriesTimerCreated(object sender, MediaBrowser.Model.Events.GenericEventArgs<TimerEventInfo> e)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Common.Plugins;
|
||||
using MediaBrowser.Common.Updates;
|
||||
using MediaBrowser.Controller;
|
||||
|
@ -49,7 +50,7 @@ namespace Emby.Server.Implementations.EntryPoints
|
|||
_sessionManager = sessionManager;
|
||||
}
|
||||
|
||||
public void Run()
|
||||
public Task RunAsync()
|
||||
{
|
||||
_userManager.UserDeleted += userManager_UserDeleted;
|
||||
_userManager.UserUpdated += userManager_UserUpdated;
|
||||
|
@ -65,6 +66,8 @@ namespace Emby.Server.Implementations.EntryPoints
|
|||
_installationManager.PackageInstallationFailed += _installationManager_PackageInstallationFailed;
|
||||
|
||||
_taskManager.TaskCompleted += _taskManager_TaskCompleted;
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
void _installationManager_PackageInstalling(object sender, InstallationEventArgs e)
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
using System.Threading.Tasks;
|
||||
using Emby.Server.Implementations.Browser;
|
||||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
|
@ -32,11 +33,11 @@ namespace Emby.Server.Implementations.EntryPoints
|
|||
/// <summary>
|
||||
/// Runs this instance.
|
||||
/// </summary>
|
||||
public void Run()
|
||||
public Task RunAsync()
|
||||
{
|
||||
if (!_appHost.CanLaunchWebBrowser)
|
||||
{
|
||||
return;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
if (!_config.Configuration.IsStartupWizardCompleted)
|
||||
|
@ -47,11 +48,13 @@ namespace Emby.Server.Implementations.EntryPoints
|
|||
{
|
||||
var options = ((ApplicationHost)_appHost).StartupOptions;
|
||||
|
||||
if (!options.ContainsOption("-noautorunwebapp"))
|
||||
if (!options.NoAutoRunWebApp)
|
||||
{
|
||||
BrowserLauncher.OpenWebApp(_appHost);
|
||||
}
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Emby.Server.Implementations.Udp;
|
||||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Controller.Plugins;
|
||||
|
@ -43,7 +44,7 @@ namespace Emby.Server.Implementations.EntryPoints
|
|||
/// <summary>
|
||||
/// Runs this instance.
|
||||
/// </summary>
|
||||
public void Run()
|
||||
public Task RunAsync()
|
||||
{
|
||||
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");
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -10,7 +10,6 @@ using MediaBrowser.Controller.Session;
|
|||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.Extensions;
|
||||
using MediaBrowser.Model.Session;
|
||||
using MediaBrowser.Model.Threading;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Emby.Server.Implementations.EntryPoints
|
||||
|
@ -23,24 +22,24 @@ namespace Emby.Server.Implementations.EntryPoints
|
|||
private readonly IUserManager _userManager;
|
||||
|
||||
private readonly object _syncLock = new object();
|
||||
private ITimer UpdateTimer { get; set; }
|
||||
private readonly ITimerFactory _timerFactory;
|
||||
private Timer UpdateTimer { get; set; }
|
||||
private const int UpdateDuration = 500;
|
||||
|
||||
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;
|
||||
_sessionManager = sessionManager;
|
||||
_logger = logger;
|
||||
_userManager = userManager;
|
||||
_timerFactory = timerFactory;
|
||||
}
|
||||
|
||||
public void Run()
|
||||
public Task RunAsync()
|
||||
{
|
||||
_userDataManager.UserDataSaved += _userDataManager_UserDataSaved;
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
void _userDataManager_UserDataSaved(object sender, UserDataSaveEventArgs e)
|
||||
|
@ -54,7 +53,7 @@ namespace Emby.Server.Implementations.EntryPoints
|
|||
{
|
||||
if (UpdateTimer == null)
|
||||
{
|
||||
UpdateTimer = _timerFactory.Create(UpdateTimerCallback, null, UpdateDuration,
|
||||
UpdateTimer = new Timer(UpdateTimerCallback, null, UpdateDuration,
|
||||
Timeout.Infinite);
|
||||
}
|
||||
else
|
||||
|
@ -121,7 +120,8 @@ namespace Emby.Server.Implementations.EntryPoints
|
|||
var user = _userManager.GetUserById(userId);
|
||||
|
||||
var dtoList = changedItems
|
||||
.DistinctBy(i => i.Id)
|
||||
.GroupBy(x => x.Id)
|
||||
.Select(x => x.First())
|
||||
.Select(i =>
|
||||
{
|
||||
var dto = _userDataManager.GetUserDataDto(i, user);
|
||||
|
|
|
@ -28,10 +28,10 @@ namespace Emby.Server.Implementations.FFMpeg
|
|||
_ffmpegInstallInfo = ffmpegInstallInfo;
|
||||
}
|
||||
|
||||
public FFMpegInfo GetFFMpegInfo(StartupOptions options)
|
||||
public FFMpegInfo GetFFMpegInfo(IStartupOptions options)
|
||||
{
|
||||
var customffMpegPath = options.GetOption("-ffmpeg");
|
||||
var customffProbePath = options.GetOption("-ffprobe");
|
||||
var customffMpegPath = options.FFmpegPath;
|
||||
var customffProbePath = options.FFprobePath;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(customffMpegPath) && !string.IsNullOrWhiteSpace(customffProbePath))
|
||||
{
|
||||
|
|
|
@ -264,7 +264,7 @@ namespace Emby.Server.Implementations.HttpClientManager
|
|||
}
|
||||
|
||||
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);
|
||||
|
||||
|
@ -374,11 +374,11 @@ namespace Emby.Server.Implementations.HttpClientManager
|
|||
{
|
||||
if (options.LogRequestAsDebug)
|
||||
{
|
||||
_logger.LogDebug("HttpClientManager {0}: {1}", httpMethod.ToUpper(), options.Url);
|
||||
_logger.LogDebug("HttpClientManager {0}: {1}", httpMethod.ToUpper(CultureInfo.CurrentCulture), options.Url);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogInformation("HttpClientManager {0}: {1}", httpMethod.ToUpper(), options.Url);
|
||||
_logger.LogInformation("HttpClientManager {0}: {1}", httpMethod.ToUpper(CultureInfo.CurrentCulture), options.Url);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -394,7 +394,7 @@ namespace Emby.Server.Implementations.HttpServer
|
|||
{
|
||||
return contentType == null
|
||||
? null
|
||||
: contentType.Split(';')[0].ToLower().Trim();
|
||||
: contentType.Split(';')[0].ToLowerInvariant().Trim();
|
||||
}
|
||||
|
||||
private static string SerializeToXmlString(object from)
|
||||
|
|
|
@ -2,6 +2,7 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Library;
|
||||
|
@ -9,7 +10,6 @@ using MediaBrowser.Model.Extensions;
|
|||
using MediaBrowser.Model.IO;
|
||||
using MediaBrowser.Model.System;
|
||||
using MediaBrowser.Model.Tasks;
|
||||
using MediaBrowser.Model.Threading;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Emby.Server.Implementations.IO
|
||||
|
@ -22,8 +22,7 @@ namespace Emby.Server.Implementations.IO
|
|||
private IServerConfigurationManager ConfigurationManager { get; set; }
|
||||
private readonly IFileSystem _fileSystem;
|
||||
private readonly List<string> _affectedPaths = new List<string>();
|
||||
private ITimer _timer;
|
||||
private readonly ITimerFactory _timerFactory;
|
||||
private Timer _timer;
|
||||
private readonly object _timerLock = new object();
|
||||
public string Path { get; private set; }
|
||||
|
||||
|
@ -31,7 +30,7 @@ namespace Emby.Server.Implementations.IO
|
|||
private readonly IEnvironmentInfo _environmentInfo;
|
||||
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);
|
||||
Path = path;
|
||||
|
@ -41,7 +40,6 @@ namespace Emby.Server.Implementations.IO
|
|||
LibraryManager = libraryManager;
|
||||
TaskManager = taskManager;
|
||||
Logger = logger;
|
||||
_timerFactory = timerFactory;
|
||||
_environmentInfo = environmentInfo;
|
||||
_libraryManager = libraryManager1;
|
||||
AddPath(path);
|
||||
|
@ -90,7 +88,7 @@ namespace Emby.Server.Implementations.IO
|
|||
|
||||
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
|
||||
{
|
||||
|
@ -146,8 +144,8 @@ namespace Emby.Server.Implementations.IO
|
|||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||
.Select(GetAffectedBaseItem)
|
||||
.Where(item => item != null)
|
||||
.DistinctBy(i => i.Id)
|
||||
.ToList();
|
||||
.GroupBy(x => x.Id)
|
||||
.Select(x => x.First());
|
||||
|
||||
foreach (var item in itemsToRefresh)
|
||||
{
|
||||
|
|
|
@ -11,7 +11,6 @@ using MediaBrowser.Controller.Plugins;
|
|||
using MediaBrowser.Model.IO;
|
||||
using MediaBrowser.Model.System;
|
||||
using MediaBrowser.Model.Tasks;
|
||||
using MediaBrowser.Model.Threading;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Emby.Server.Implementations.IO
|
||||
|
@ -35,7 +34,7 @@ namespace Emby.Server.Implementations.IO
|
|||
/// <summary>
|
||||
/// Any file name ending in any of these will be ignored by the watchers
|
||||
/// </summary>
|
||||
private readonly string[] _alwaysIgnoreFiles = new string[]
|
||||
private readonly HashSet<string> _alwaysIgnoreFiles = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
"small.jpg",
|
||||
"albumart.jpg",
|
||||
|
@ -54,7 +53,7 @@ namespace Emby.Server.Implementations.IO
|
|||
".actors"
|
||||
};
|
||||
|
||||
private readonly string[] _alwaysIgnoreExtensions = new string[]
|
||||
private readonly HashSet<string> _alwaysIgnoreExtensions = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
// thumbs.db
|
||||
".db",
|
||||
|
@ -134,7 +133,6 @@ namespace Emby.Server.Implementations.IO
|
|||
private IServerConfigurationManager ConfigurationManager { get; set; }
|
||||
|
||||
private readonly IFileSystem _fileSystem;
|
||||
private readonly ITimerFactory _timerFactory;
|
||||
private readonly IEnvironmentInfo _environmentInfo;
|
||||
|
||||
/// <summary>
|
||||
|
@ -146,7 +144,6 @@ namespace Emby.Server.Implementations.IO
|
|||
ILibraryManager libraryManager,
|
||||
IServerConfigurationManager configurationManager,
|
||||
IFileSystem fileSystem,
|
||||
ITimerFactory timerFactory,
|
||||
IEnvironmentInfo environmentInfo)
|
||||
{
|
||||
if (taskManager == null)
|
||||
|
@ -159,7 +156,6 @@ namespace Emby.Server.Implementations.IO
|
|||
Logger = loggerFactory.CreateLogger(GetType().Name);
|
||||
ConfigurationManager = configurationManager;
|
||||
_fileSystem = fileSystem;
|
||||
_timerFactory = timerFactory;
|
||||
_environmentInfo = environmentInfo;
|
||||
}
|
||||
|
||||
|
@ -460,8 +456,8 @@ namespace Emby.Server.Implementations.IO
|
|||
var filename = Path.GetFileName(path);
|
||||
|
||||
var monitorPath = !string.IsNullOrEmpty(filename) &&
|
||||
!_alwaysIgnoreFiles.Contains(filename, StringComparer.OrdinalIgnoreCase) &&
|
||||
!_alwaysIgnoreExtensions.Contains(Path.GetExtension(path) ?? string.Empty, StringComparer.OrdinalIgnoreCase) &&
|
||||
!_alwaysIgnoreFiles.Contains(filename) &&
|
||||
!_alwaysIgnoreExtensions.Contains(Path.GetExtension(path)) &&
|
||||
_alwaysIgnoreSubstrings.All(i => path.IndexOf(i, StringComparison.OrdinalIgnoreCase) == -1);
|
||||
|
||||
// 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;
|
||||
_activeRefreshers.Add(newRefresher);
|
||||
}
|
||||
|
@ -601,20 +597,26 @@ namespace Emby.Server.Implementations.IO
|
|||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
_disposed = true;
|
||||
Dispose(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases unmanaged and - optionally - managed resources.
|
||||
/// </summary>
|
||||
/// <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)
|
||||
/// <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 disposing)
|
||||
{
|
||||
if (dispose)
|
||||
if (_disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (disposing)
|
||||
{
|
||||
Stop();
|
||||
}
|
||||
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -627,9 +629,10 @@ namespace Emby.Server.Implementations.IO
|
|||
_monitor = monitor;
|
||||
}
|
||||
|
||||
public void Run()
|
||||
public Task RunAsync()
|
||||
{
|
||||
_monitor.Start();
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
|
|
40
Emby.Server.Implementations/IStartupOptions.cs
Normal file
40
Emby.Server.Implementations/IStartupOptions.cs
Normal 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; }
|
||||
}
|
||||
}
|
|
@ -215,7 +215,7 @@ namespace Emby.Server.Implementations.Images
|
|||
{
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -512,7 +512,7 @@ namespace Emby.Server.Implementations.Library
|
|||
|
||||
if (forceCaseInsensitive || !ConfigurationManager.Configuration.EnableCaseSensitiveItemIds)
|
||||
{
|
||||
key = key.ToLower();
|
||||
key = key.ToLowerInvariant();
|
||||
}
|
||||
|
||||
key = type.FullName + key;
|
||||
|
@ -869,11 +869,6 @@ namespace Emby.Server.Implementations.Library
|
|||
return GetItemByNameId<MusicGenre>(MusicGenre.GetPath, name);
|
||||
}
|
||||
|
||||
public Guid GetGameGenreId(string name)
|
||||
{
|
||||
return GetItemByNameId<GameGenre>(GameGenre.GetPath, name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a Genre
|
||||
/// </summary>
|
||||
|
@ -894,16 +889,6 @@ namespace Emby.Server.Implementations.Library
|
|||
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>
|
||||
/// Gets a Year
|
||||
/// </summary>
|
||||
|
@ -1370,17 +1355,6 @@ namespace Emby.Server.Implementations.Library
|
|||
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)
|
||||
{
|
||||
if (query.User != null)
|
||||
|
|
|
@ -20,7 +20,6 @@ using MediaBrowser.Model.Globalization;
|
|||
using MediaBrowser.Model.IO;
|
||||
using MediaBrowser.Model.MediaInfo;
|
||||
using MediaBrowser.Model.Serialization;
|
||||
using MediaBrowser.Model.Threading;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Emby.Server.Implementations.Library
|
||||
|
@ -36,7 +35,6 @@ namespace Emby.Server.Implementations.Library
|
|||
private IMediaSourceProvider[] _providers;
|
||||
private readonly ILogger _logger;
|
||||
private readonly IUserDataManager _userDataManager;
|
||||
private readonly ITimerFactory _timerFactory;
|
||||
private readonly Func<IMediaEncoder> _mediaEncoder;
|
||||
private ILocalizationManager _localizationManager;
|
||||
private IApplicationPaths _appPaths;
|
||||
|
@ -51,7 +49,6 @@ namespace Emby.Server.Implementations.Library
|
|||
IJsonSerializer jsonSerializer,
|
||||
IFileSystem fileSystem,
|
||||
IUserDataManager userDataManager,
|
||||
ITimerFactory timerFactory,
|
||||
Func<IMediaEncoder> mediaEncoder)
|
||||
{
|
||||
_itemRepo = itemRepo;
|
||||
|
@ -61,7 +58,6 @@ namespace Emby.Server.Implementations.Library
|
|||
_jsonSerializer = jsonSerializer;
|
||||
_fileSystem = fileSystem;
|
||||
_userDataManager = userDataManager;
|
||||
_timerFactory = timerFactory;
|
||||
_mediaEncoder = mediaEncoder;
|
||||
_localizationManager = localizationManager;
|
||||
_appPaths = applicationPaths;
|
||||
|
@ -322,18 +318,18 @@ namespace Emby.Server.Implementations.Library
|
|||
|
||||
private string[] NormalizeLanguage(string language)
|
||||
{
|
||||
if (language != null)
|
||||
if (language == null)
|
||||
{
|
||||
var culture = _localizationManager.FindLanguageInfo(language);
|
||||
if (culture != null)
|
||||
{
|
||||
return culture.ThreeLetterISOLanguageNames;
|
||||
}
|
||||
|
||||
return new string[] { language };
|
||||
return Array.Empty<string>();
|
||||
}
|
||||
|
||||
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)
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using MediaBrowser.Controller.Drawing;
|
||||
|
@ -85,7 +86,7 @@ namespace Emby.Server.Implementations.Library.Resolvers
|
|||
return false;
|
||||
}
|
||||
|
||||
private static readonly string[] IgnoreFiles =
|
||||
private static readonly HashSet<string> IgnoreFiles = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
"folder",
|
||||
"thumb",
|
||||
|
@ -102,7 +103,7 @@ namespace Emby.Server.Implementations.Library.Resolvers
|
|||
{
|
||||
var filename = Path.GetFileNameWithoutExtension(path) ?? string.Empty;
|
||||
|
||||
if (IgnoreFiles.Contains(filename, StringComparer.OrdinalIgnoreCase))
|
||||
if (IgnoreFiles.Contains(filename))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -112,7 +113,7 @@ namespace Emby.Server.Implementations.Library.Resolvers
|
|||
return false;
|
||||
}
|
||||
|
||||
return imageProcessor.SupportedInputFormats.Contains((Path.GetExtension(path) ?? string.Empty).TrimStart('.'), StringComparer.OrdinalIgnoreCase);
|
||||
return imageProcessor.SupportedInputFormats.Contains((Path.GetExtension(path) ?? string.Empty).TrimStart('.'));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -99,14 +99,12 @@ namespace Emby.Server.Implementations.Library
|
|||
if (!query.IncludeMedia)
|
||||
{
|
||||
AddIfMissing(includeItemTypes, typeof(Genre).Name);
|
||||
AddIfMissing(includeItemTypes, typeof(GameGenre).Name);
|
||||
AddIfMissing(includeItemTypes, typeof(MusicGenre).Name);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AddIfMissing(excludeItemTypes, typeof(Genre).Name);
|
||||
AddIfMissing(excludeItemTypes, typeof(GameGenre).Name);
|
||||
AddIfMissing(excludeItemTypes, typeof(MusicGenre).Name);
|
||||
}
|
||||
|
||||
|
|
|
@ -24,7 +24,6 @@ using MediaBrowser.Controller.Providers;
|
|||
using MediaBrowser.Controller.Security;
|
||||
using MediaBrowser.Controller.Session;
|
||||
using MediaBrowser.Model.Configuration;
|
||||
using MediaBrowser.Model.Connect;
|
||||
using MediaBrowser.Model.Cryptography;
|
||||
using MediaBrowser.Model.Dto;
|
||||
using MediaBrowser.Model.Entities;
|
||||
|
@ -212,11 +211,8 @@ namespace Emby.Server.Implementations.Library
|
|||
{
|
||||
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)
|
||||
{
|
||||
// Authenticate using local credentials if not a guest
|
||||
if (!user.ConnectLinkType.HasValue || user.ConnectLinkType.Value != UserLinkType.Guest)
|
||||
{
|
||||
var authResult = await AuthenticateLocalUser(username, password, hashedPassword, user, remoteEndPoint).ConfigureAwait(false);
|
||||
authenticationProvider = authResult.Item1;
|
||||
success = authResult.Item2;
|
||||
}
|
||||
var authResult = await AuthenticateLocalUser(username, password, hashedPassword, user, remoteEndPoint).ConfigureAwait(false);
|
||||
authenticationProvider = authResult.Item1;
|
||||
success = authResult.Item2;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -455,30 +447,30 @@ namespace Emby.Server.Implementations.Library
|
|||
|
||||
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 fireLockout = false;
|
||||
var maxCount = user.Policy.IsAdministrator ? 3 : 5;
|
||||
|
||||
if (newValue >= maxCount)
|
||||
{
|
||||
_logger.LogDebug("Disabling user {0} due to {1} unsuccessful login attempts.", user.Name, newValue.ToString(CultureInfo.InvariantCulture));
|
||||
user.Policy.IsDisabled = true;
|
||||
var fireLockout = false;
|
||||
|
||||
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)
|
||||
{
|
||||
UserLockedOut?.Invoke(this, new GenericEventArgs<User>(user));
|
||||
}*/
|
||||
UpdateUserPolicy(user, user.Policy, false);
|
||||
|
||||
if (fireLockout)
|
||||
{
|
||||
UserLockedOut?.Invoke(this, new GenericEventArgs<User>(user));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -553,9 +545,6 @@ namespace Emby.Server.Implementations.Library
|
|||
LastActivityDate = user.LastActivityDate,
|
||||
LastLoginDate = user.LastLoginDate,
|
||||
Configuration = user.Configuration,
|
||||
ConnectLinkType = user.ConnectLinkType,
|
||||
ConnectUserId = user.ConnectUserId,
|
||||
ConnectUserName = user.ConnectUserName,
|
||||
ServerId = _appHost.SystemId,
|
||||
Policy = user.Policy
|
||||
};
|
||||
|
@ -814,11 +803,6 @@ namespace Emby.Server.Implementations.Library
|
|||
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);
|
||||
|
||||
UpdateUser(user);
|
||||
|
@ -925,11 +909,6 @@ namespace Emby.Server.Implementations.Library
|
|||
null :
|
||||
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;
|
||||
string pinFile = null;
|
||||
DateTime? expirationDate = null;
|
||||
|
@ -974,10 +953,7 @@ namespace Emby.Server.Implementations.Library
|
|||
_lastPin = null;
|
||||
_lastPasswordPinCreationResult = null;
|
||||
|
||||
var users = Users.Where(i => !i.ConnectLinkType.HasValue || i.ConnectLinkType.Value != UserLinkType.Guest)
|
||||
.ToList();
|
||||
|
||||
foreach (var user in users)
|
||||
foreach (var user in Users)
|
||||
{
|
||||
await ResetPassword(user).ConfigureAwait(false);
|
||||
|
||||
|
@ -1205,9 +1181,11 @@ namespace Emby.Server.Implementations.Library
|
|||
_sessionManager = sessionManager;
|
||||
}
|
||||
|
||||
public void Run()
|
||||
public Task RunAsync()
|
||||
{
|
||||
_userManager.UserPolicyUpdated += _userManager_UserPolicyUpdated;
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private void _userManager_UserPolicyUpdated(object sender, GenericEventArgs<User> e)
|
||||
|
|
|
@ -308,9 +308,6 @@ namespace Emby.Server.Implementations.Library
|
|||
mediaTypes.Add(MediaType.Book);
|
||||
mediaTypes.Add(MediaType.Audio);
|
||||
break;
|
||||
case CollectionType.Games:
|
||||
mediaTypes.Add(MediaType.Game);
|
||||
break;
|
||||
case CollectionType.Music:
|
||||
mediaTypes.Add(MediaType.Audio);
|
||||
break;
|
||||
|
@ -336,7 +333,6 @@ namespace Emby.Server.Implementations.Library
|
|||
typeof(Person).Name,
|
||||
typeof(Studio).Name,
|
||||
typeof(Year).Name,
|
||||
typeof(GameGenre).Name,
|
||||
typeof(MusicGenre).Name,
|
||||
typeof(Genre).Name
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -35,8 +35,6 @@ using MediaBrowser.Model.Providers;
|
|||
using MediaBrowser.Model.Querying;
|
||||
using MediaBrowser.Model.Reflection;
|
||||
using MediaBrowser.Model.Serialization;
|
||||
using MediaBrowser.Model.System;
|
||||
using MediaBrowser.Model.Threading;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
||||
|
@ -65,7 +63,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
|||
|
||||
public static EmbyTV Current;
|
||||
|
||||
public event EventHandler DataSourceChanged;
|
||||
public event EventHandler<GenericEventArgs<TimerInfo>> TimerCreated;
|
||||
public event EventHandler<GenericEventArgs<string>> TimerCancelled;
|
||||
|
||||
|
@ -88,7 +85,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
|||
ILibraryMonitor libraryMonitor,
|
||||
IProviderManager providerManager,
|
||||
IMediaEncoder mediaEncoder,
|
||||
ITimerFactory timerFactory,
|
||||
IProcessFactory processFactory)
|
||||
{
|
||||
Current = this;
|
||||
|
@ -110,7 +106,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
|||
_streamHelper = streamHelper;
|
||||
|
||||
_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;
|
||||
|
||||
_config.NamedConfigurationUpdated += _config_NamedConfigurationUpdated;
|
||||
|
@ -124,7 +120,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
|||
}
|
||||
}
|
||||
|
||||
public async void Start()
|
||||
public async Task Start()
|
||||
{
|
||||
_timerProvider.RestartTimers();
|
||||
|
||||
|
@ -275,7 +271,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
|||
|
||||
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);
|
||||
}
|
||||
|
||||
await UpdateTimersForSeriesTimer(info, true, false).ConfigureAwait(false);
|
||||
UpdateTimersForSeriesTimer(info, true, false);
|
||||
|
||||
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));
|
||||
|
||||
|
@ -792,8 +788,10 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
|||
|
||||
_seriesTimerProvider.Update(instance);
|
||||
|
||||
await UpdateTimersForSeriesTimer(instance, true, true).ConfigureAwait(false);
|
||||
UpdateTimersForSeriesTimer(instance, true, true);
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task UpdateTimerAsync(TimerInfo updatedTimer, CancellationToken cancellationToken)
|
||||
|
@ -2193,7 +2191,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
|||
|
||||
if (lockData)
|
||||
{
|
||||
writer.WriteElementString("lockdata", true.ToString().ToLower());
|
||||
writer.WriteElementString("lockdata", true.ToString(CultureInfo.InvariantCulture).ToLowerInvariant());
|
||||
}
|
||||
|
||||
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)
|
||||
.ToList();
|
||||
var allTimers = GetTimersForSeries(seriesTimer).ToList();
|
||||
|
||||
|
||||
var enabledTimersForSeries = new List<TimerInfo>();
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Controller.Plugins;
|
||||
|
||||
namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
||||
{
|
||||
public class EntryPoint : IServerEntryPoint
|
||||
{
|
||||
public void Run()
|
||||
public Task RunAsync()
|
||||
{
|
||||
EmbyTV.Current.Start();
|
||||
return EmbyTV.Current.Start();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
|
|
|
@ -2,29 +2,27 @@ using System;
|
|||
using System.Collections.Concurrent;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using MediaBrowser.Controller.LiveTv;
|
||||
using MediaBrowser.Model.Events;
|
||||
using MediaBrowser.Model.IO;
|
||||
using MediaBrowser.Model.LiveTv;
|
||||
using MediaBrowser.Model.Serialization;
|
||||
using MediaBrowser.Model.Threading;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
||||
{
|
||||
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;
|
||||
|
||||
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))
|
||||
{
|
||||
_logger = logger1;
|
||||
_timerFactory = timerFactory;
|
||||
}
|
||||
|
||||
public void RestartTimers()
|
||||
|
@ -125,7 +123,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
|||
|
||||
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))
|
||||
{
|
||||
|
|
|
@ -17,7 +17,7 @@ using MediaBrowser.Model.IO;
|
|||
using MediaBrowser.Model.LiveTv;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Jellyfin.Server.Implementations.LiveTv.Listings
|
||||
namespace Emby.Server.Implementations.LiveTv.Listings
|
||||
{
|
||||
public class XmlTvListingsProvider : IListingsProvider
|
||||
{
|
||||
|
|
|
@ -399,7 +399,7 @@ namespace Emby.Server.Implementations.LiveTv
|
|||
{
|
||||
var name = serviceName + externalId + InternalVersionNumber;
|
||||
|
||||
return _libraryManager.GetNewItemId(name.ToLower(), typeof(LiveTvChannel));
|
||||
return _libraryManager.GetNewItemId(name.ToLowerInvariant(), typeof(LiveTvChannel));
|
||||
}
|
||||
|
||||
private const string ServiceName = "Emby";
|
||||
|
@ -407,21 +407,21 @@ namespace Emby.Server.Implementations.LiveTv
|
|||
{
|
||||
var name = ServiceName + externalId + InternalVersionNumber;
|
||||
|
||||
return name.ToLower().GetMD5().ToString("N");
|
||||
return name.ToLowerInvariant().GetMD5().ToString("N");
|
||||
}
|
||||
|
||||
public Guid GetInternalSeriesTimerId(string externalId)
|
||||
{
|
||||
var name = ServiceName + externalId + InternalVersionNumber;
|
||||
|
||||
return name.ToLower().GetMD5();
|
||||
return name.ToLowerInvariant().GetMD5();
|
||||
}
|
||||
|
||||
public Guid GetInternalProgramId(string externalId)
|
||||
{
|
||||
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)
|
||||
|
|
|
@ -6,7 +6,6 @@ using System.Threading.Tasks;
|
|||
using Emby.Server.Implementations.Library;
|
||||
using MediaBrowser.Common.Configuration;
|
||||
using MediaBrowser.Common.Extensions;
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Common.Progress;
|
||||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Controller.Channels;
|
||||
|
@ -24,7 +23,6 @@ using MediaBrowser.Controller.Sorting;
|
|||
using MediaBrowser.Model.Dto;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.Events;
|
||||
using MediaBrowser.Model.Extensions;
|
||||
using MediaBrowser.Model.Globalization;
|
||||
using MediaBrowser.Model.IO;
|
||||
using MediaBrowser.Model.LiveTv;
|
||||
|
@ -48,7 +46,6 @@ namespace Emby.Server.Implementations.LiveTv
|
|||
private readonly ILibraryManager _libraryManager;
|
||||
private readonly ITaskManager _taskManager;
|
||||
private readonly IJsonSerializer _jsonSerializer;
|
||||
private readonly IProviderManager _providerManager;
|
||||
private readonly Func<IChannelManager> _channelManager;
|
||||
|
||||
private readonly IDtoService _dtoService;
|
||||
|
@ -56,7 +53,7 @@ namespace Emby.Server.Implementations.LiveTv
|
|||
|
||||
private readonly LiveTvDtoService _tvDtoService;
|
||||
|
||||
private ILiveTvService[] _services = new ILiveTvService[] { };
|
||||
private ILiveTvService[] _services = Array.Empty<ILiveTvService>();
|
||||
|
||||
private ITunerHost[] _tunerHosts = Array.Empty<ITunerHost>();
|
||||
private IListingsProvider[] _listingProviders = Array.Empty<IListingsProvider>();
|
||||
|
@ -85,7 +82,6 @@ namespace Emby.Server.Implementations.LiveTv
|
|||
ITaskManager taskManager,
|
||||
ILocalizationManager localization,
|
||||
IJsonSerializer jsonSerializer,
|
||||
IProviderManager providerManager,
|
||||
IFileSystem fileSystem,
|
||||
Func<IChannelManager> channelManager)
|
||||
{
|
||||
|
@ -97,7 +93,6 @@ namespace Emby.Server.Implementations.LiveTv
|
|||
_taskManager = taskManager;
|
||||
_localization = localization;
|
||||
_jsonSerializer = jsonSerializer;
|
||||
_providerManager = providerManager;
|
||||
_fileSystem = fileSystem;
|
||||
_dtoService = dtoService;
|
||||
_userDataManager = userDataManager;
|
||||
|
@ -132,8 +127,6 @@ namespace Emby.Server.Implementations.LiveTv
|
|||
|
||||
foreach (var service in _services)
|
||||
{
|
||||
service.DataSourceChanged += service_DataSourceChanged;
|
||||
|
||||
if (service is EmbyTV.EmbyTV embyTv)
|
||||
{
|
||||
embyTv.TimerCreated += EmbyTv_TimerCreated;
|
||||
|
@ -189,14 +182,6 @@ namespace Emby.Server.Implementations.LiveTv
|
|||
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)
|
||||
{
|
||||
var user = query.UserId.Equals(Guid.Empty) ? null : _userManager.GetUserById(query.UserId);
|
||||
|
@ -2158,17 +2143,28 @@ namespace Emby.Server.Implementations.LiveTv
|
|||
Dispose(true);
|
||||
}
|
||||
|
||||
private bool _isDisposed = false;
|
||||
private bool _disposed = false;
|
||||
/// <summary>
|
||||
/// Releases unmanaged and - optionally - managed resources.
|
||||
/// </summary>
|
||||
/// <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)
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (dispose)
|
||||
{
|
||||
_isDisposed = true;
|
||||
// TODO: Dispose stuff
|
||||
}
|
||||
|
||||
_services = null;
|
||||
_listingProviders = null;
|
||||
_tunerHosts = null;
|
||||
|
||||
_disposed = true;
|
||||
}
|
||||
|
||||
private LiveTvServiceInfo[] GetServiceInfos()
|
||||
|
@ -2469,7 +2465,8 @@ namespace Emby.Server.Implementations.LiveTv
|
|||
.Where(i => i != null)
|
||||
.Where(i => i.IsVisibleStandalone(user))
|
||||
.SelectMany(i => _libraryManager.GetCollectionFolders(i))
|
||||
.DistinctBy(i => i.Id)
|
||||
.GroupBy(x => x.Id)
|
||||
.Select(x => x.First())
|
||||
.OrderBy(i => i.SortName)
|
||||
.ToList();
|
||||
|
||||
|
|
|
@ -94,7 +94,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
|
|||
|
||||
var now = DateTime.UtcNow;
|
||||
|
||||
StartStreaming(response, taskCompletionSource, LiveStreamCancellationTokenSource.Token);
|
||||
var _ = StartStreaming(response, taskCompletionSource, LiveStreamCancellationTokenSource.Token);
|
||||
|
||||
//OpenedMediaSource.Protocol = MediaProtocol.File;
|
||||
//OpenedMediaSource.Path = tempFile;
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
"FailedLoginAttemptWithUserName": "عملية تسجيل الدخول فشلت من {0}",
|
||||
"Favorites": "المفضلات",
|
||||
"Folders": "المجلدات",
|
||||
"Games": "الألعاب",
|
||||
"Genres": "أنواع الأفلام",
|
||||
"HeaderAlbumArtists": "فنانو الألبومات",
|
||||
"HeaderCameraUploads": "Camera Uploads",
|
||||
|
@ -51,8 +50,6 @@
|
|||
"NotificationOptionAudioPlayback": "بدأ تشغيل المقطع الصوتي",
|
||||
"NotificationOptionAudioPlaybackStopped": "تم إيقاف تشغيل المقطع الصوتي",
|
||||
"NotificationOptionCameraImageUploaded": "تم رقع صورة الكاميرا",
|
||||
"NotificationOptionGamePlayback": "تم تشغيل اللعبة",
|
||||
"NotificationOptionGamePlaybackStopped": "تم إيقاف تشغيل اللعبة",
|
||||
"NotificationOptionInstallationFailed": "عملية التنصيب فشلت",
|
||||
"NotificationOptionNewLibraryContent": "تم إضافة محتوى جديد",
|
||||
"NotificationOptionPluginError": "فشل في الملحق",
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
"FailedLoginAttemptWithUserName": "Failed login attempt from {0}",
|
||||
"Favorites": "Любими",
|
||||
"Folders": "Папки",
|
||||
"Games": "Игри",
|
||||
"Genres": "Жанрове",
|
||||
"HeaderAlbumArtists": "Изпълнители на албуми",
|
||||
"HeaderCameraUploads": "Camera Uploads",
|
||||
|
@ -51,8 +50,6 @@
|
|||
"NotificationOptionAudioPlayback": "Възпроизвеждането на звук започна",
|
||||
"NotificationOptionAudioPlaybackStopped": "Възпроизвеждането на звук е спряно",
|
||||
"NotificationOptionCameraImageUploaded": "Изображението от фотоапарата е качено",
|
||||
"NotificationOptionGamePlayback": "Възпроизвеждането на играта започна",
|
||||
"NotificationOptionGamePlaybackStopped": "Възпроизвеждането на играта е спряна",
|
||||
"NotificationOptionInstallationFailed": "Неуспешно инсталиране",
|
||||
"NotificationOptionNewLibraryContent": "Добавено е ново съдържание",
|
||||
"NotificationOptionPluginError": "Грешка в приставка",
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
"FailedLoginAttemptWithUserName": "Intent de connexió fallit des de {0}",
|
||||
"Favorites": "Preferits",
|
||||
"Folders": "Directoris",
|
||||
"Games": "Jocs",
|
||||
"Genres": "Gèneres",
|
||||
"HeaderAlbumArtists": "Album Artists",
|
||||
"HeaderCameraUploads": "Camera Uploads",
|
||||
|
@ -51,8 +50,6 @@
|
|||
"NotificationOptionAudioPlayback": "Audio playback started",
|
||||
"NotificationOptionAudioPlaybackStopped": "Audio playback stopped",
|
||||
"NotificationOptionCameraImageUploaded": "Camera image uploaded",
|
||||
"NotificationOptionGamePlayback": "Game playback started",
|
||||
"NotificationOptionGamePlaybackStopped": "Game playback stopped",
|
||||
"NotificationOptionInstallationFailed": "Installation failure",
|
||||
"NotificationOptionNewLibraryContent": "New content added",
|
||||
"NotificationOptionPluginError": "Un component ha fallat",
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
"FailedLoginAttemptWithUserName": "Neúspěšný pokus o přihlášení z {0}",
|
||||
"Favorites": "Oblíbené",
|
||||
"Folders": "Složky",
|
||||
"Games": "Hry",
|
||||
"Genres": "Žánry",
|
||||
"HeaderAlbumArtists": "Umělci alba",
|
||||
"HeaderCameraUploads": "Camera Uploads",
|
||||
|
@ -51,8 +50,6 @@
|
|||
"NotificationOptionAudioPlayback": "Přehrávání audia zahájeno",
|
||||
"NotificationOptionAudioPlaybackStopped": "Přehrávání audia ukončeno",
|
||||
"NotificationOptionCameraImageUploaded": "Kamerový záznam nahrán",
|
||||
"NotificationOptionGamePlayback": "Spuštění hry zahájeno",
|
||||
"NotificationOptionGamePlaybackStopped": "Hra ukončena",
|
||||
"NotificationOptionInstallationFailed": "Chyba instalace",
|
||||
"NotificationOptionNewLibraryContent": "Přidán nový obsah",
|
||||
"NotificationOptionPluginError": "Chyba zásuvného modulu",
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
"FailedLoginAttemptWithUserName": "Fejlet loginforsøg fra {0}",
|
||||
"Favorites": "Favoritter",
|
||||
"Folders": "Mapper",
|
||||
"Games": "Spil",
|
||||
"Genres": "Genre",
|
||||
"HeaderAlbumArtists": "Albumkunstnere",
|
||||
"HeaderCameraUploads": "Camera Uploads",
|
||||
|
@ -51,8 +50,6 @@
|
|||
"NotificationOptionAudioPlayback": "Audioafspilning påbegyndt",
|
||||
"NotificationOptionAudioPlaybackStopped": "Audioafspilning stoppet",
|
||||
"NotificationOptionCameraImageUploaded": "Kamerabillede uploadet",
|
||||
"NotificationOptionGamePlayback": "Afspilning af Spil påbegyndt",
|
||||
"NotificationOptionGamePlaybackStopped": "Afspilning af Spil stoppet",
|
||||
"NotificationOptionInstallationFailed": "Installationsfejl",
|
||||
"NotificationOptionNewLibraryContent": "Nyt indhold tilføjet",
|
||||
"NotificationOptionPluginError": "Pluginfejl",
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
"FailedLoginAttemptWithUserName": "Fehlgeschlagener Anmeldeversuch von {0}",
|
||||
"Favorites": "Favoriten",
|
||||
"Folders": "Verzeichnisse",
|
||||
"Games": "Spiele",
|
||||
"Genres": "Genres",
|
||||
"HeaderAlbumArtists": "Album-Künstler",
|
||||
"HeaderCameraUploads": "Kamera Uploads",
|
||||
|
@ -51,8 +50,6 @@
|
|||
"NotificationOptionAudioPlayback": "Audiowiedergabe gestartet",
|
||||
"NotificationOptionAudioPlaybackStopped": "Audiowiedergabe gestoppt",
|
||||
"NotificationOptionCameraImageUploaded": "Kamera Bild hochgeladen",
|
||||
"NotificationOptionGamePlayback": "Spielwiedergabe gestartet",
|
||||
"NotificationOptionGamePlaybackStopped": "Spielwiedergabe gestoppt",
|
||||
"NotificationOptionInstallationFailed": "Installationsfehler",
|
||||
"NotificationOptionNewLibraryContent": "Neuer Inhalt hinzugefügt",
|
||||
"NotificationOptionPluginError": "Plugin Fehler",
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
"FailedLoginAttemptWithUserName": "Αποτυχημένη προσπάθεια σύνδεσης από {0}",
|
||||
"Favorites": "Αγαπημένα",
|
||||
"Folders": "Φάκελοι",
|
||||
"Games": "Παιχνίδια",
|
||||
"Genres": "Είδη",
|
||||
"HeaderAlbumArtists": "Άλμπουμ Καλλιτεχνών",
|
||||
"HeaderCameraUploads": "Camera Uploads",
|
||||
|
@ -51,8 +50,6 @@
|
|||
"NotificationOptionAudioPlayback": "Η αναπαραγωγή ήχου ξεκίνησε",
|
||||
"NotificationOptionAudioPlaybackStopped": "Η αναπαραγωγή ήχου σταμάτησε",
|
||||
"NotificationOptionCameraImageUploaded": "Camera image uploaded",
|
||||
"NotificationOptionGamePlayback": "Η αναπαραγωγή του παιχνιδιού ξεκίνησε",
|
||||
"NotificationOptionGamePlaybackStopped": "Η αναπαραγωγή του παιχνιδιού σταμάτησε",
|
||||
"NotificationOptionInstallationFailed": "Αποτυχία εγκατάστασης",
|
||||
"NotificationOptionNewLibraryContent": "Προστέθηκε νέο περιεχόμενο",
|
||||
"NotificationOptionPluginError": "Αποτυχία του plugin",
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
"FailedLoginAttemptWithUserName": "Failed login attempt from {0}",
|
||||
"Favorites": "Favourites",
|
||||
"Folders": "Folders",
|
||||
"Games": "Games",
|
||||
"Genres": "Genres",
|
||||
"HeaderAlbumArtists": "Album Artists",
|
||||
"HeaderCameraUploads": "Camera Uploads",
|
||||
|
@ -51,8 +50,6 @@
|
|||
"NotificationOptionAudioPlayback": "Audio playback started",
|
||||
"NotificationOptionAudioPlaybackStopped": "Audio playback stopped",
|
||||
"NotificationOptionCameraImageUploaded": "Camera image uploaded",
|
||||
"NotificationOptionGamePlayback": "Game playback started",
|
||||
"NotificationOptionGamePlaybackStopped": "Game playback stopped",
|
||||
"NotificationOptionInstallationFailed": "Installation failure",
|
||||
"NotificationOptionNewLibraryContent": "New content added",
|
||||
"NotificationOptionPluginError": "Plugin failure",
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
"FailedLoginAttemptWithUserName": "Failed login attempt from {0}",
|
||||
"Favorites": "Favorites",
|
||||
"Folders": "Folders",
|
||||
"Games": "Games",
|
||||
"Genres": "Genres",
|
||||
"HeaderAlbumArtists": "Album Artists",
|
||||
"HeaderCameraUploads": "Camera Uploads",
|
||||
|
@ -51,8 +50,6 @@
|
|||
"NotificationOptionAudioPlayback": "Audio playback started",
|
||||
"NotificationOptionAudioPlaybackStopped": "Audio playback stopped",
|
||||
"NotificationOptionCameraImageUploaded": "Camera image uploaded",
|
||||
"NotificationOptionGamePlayback": "Game playback started",
|
||||
"NotificationOptionGamePlaybackStopped": "Game playback stopped",
|
||||
"NotificationOptionInstallationFailed": "Installation failure",
|
||||
"NotificationOptionNewLibraryContent": "New content added",
|
||||
"NotificationOptionPluginError": "Plugin failure",
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
"FailedLoginAttemptWithUserName": "Failed login attempt from {0}",
|
||||
"Favorites": "Favorites",
|
||||
"Folders": "Folders",
|
||||
"Games": "Games",
|
||||
"Genres": "Genres",
|
||||
"HeaderAlbumArtists": "Album Artists",
|
||||
"HeaderCameraUploads": "Camera Uploads",
|
||||
|
@ -51,8 +50,6 @@
|
|||
"NotificationOptionAudioPlayback": "Audio playback started",
|
||||
"NotificationOptionAudioPlaybackStopped": "Audio playback stopped",
|
||||
"NotificationOptionCameraImageUploaded": "Camera image uploaded",
|
||||
"NotificationOptionGamePlayback": "Game playback started",
|
||||
"NotificationOptionGamePlaybackStopped": "Game playback stopped",
|
||||
"NotificationOptionInstallationFailed": "Installation failure",
|
||||
"NotificationOptionNewLibraryContent": "New content added",
|
||||
"NotificationOptionPluginError": "Plugin failure",
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
"FailedLoginAttemptWithUserName": "Intento fallido de inicio de sesión de {0}",
|
||||
"Favorites": "Favoritos",
|
||||
"Folders": "Carpetas",
|
||||
"Games": "Juegos",
|
||||
"Genres": "Géneros",
|
||||
"HeaderAlbumArtists": "Artistas del Álbum",
|
||||
"HeaderCameraUploads": "Subidos desde Camara",
|
||||
|
@ -51,8 +50,6 @@
|
|||
"NotificationOptionAudioPlayback": "Reproducción de audio iniciada",
|
||||
"NotificationOptionAudioPlaybackStopped": "Reproducción de audio detenida",
|
||||
"NotificationOptionCameraImageUploaded": "Imagen de la cámara subida",
|
||||
"NotificationOptionGamePlayback": "Ejecución de juego iniciada",
|
||||
"NotificationOptionGamePlaybackStopped": "Ejecución de juego detenida",
|
||||
"NotificationOptionInstallationFailed": "Falla de instalación",
|
||||
"NotificationOptionNewLibraryContent": "Nuevo contenido agregado",
|
||||
"NotificationOptionPluginError": "Falla de complemento",
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
"FailedLoginAttemptWithUserName": "Error al intentar iniciar sesión a partir de {0}",
|
||||
"Favorites": "Favoritos",
|
||||
"Folders": "Carpetas",
|
||||
"Games": "Juegos",
|
||||
"Genres": "Géneros",
|
||||
"HeaderAlbumArtists": "Artistas del Álbum",
|
||||
"HeaderCameraUploads": "Camera Uploads",
|
||||
|
@ -51,8 +50,6 @@
|
|||
"NotificationOptionAudioPlayback": "Se inició la reproducción de audio",
|
||||
"NotificationOptionAudioPlaybackStopped": "Se detuvo la reproducción de audio",
|
||||
"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",
|
||||
"NotificationOptionNewLibraryContent": "Nuevo contenido añadido",
|
||||
"NotificationOptionPluginError": "Error en plugin",
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
"FailedLoginAttemptWithUserName": "تلاش برای ورود از {0} ناموفق بود",
|
||||
"Favorites": "مورد علاقه ها",
|
||||
"Folders": "پوشه ها",
|
||||
"Games": "بازی ها",
|
||||
"Genres": "ژانرها",
|
||||
"HeaderAlbumArtists": "هنرمندان آلبوم",
|
||||
"HeaderCameraUploads": "آپلودهای دوربین",
|
||||
|
@ -51,8 +50,6 @@
|
|||
"NotificationOptionAudioPlayback": "پخش صدا آغاز شد",
|
||||
"NotificationOptionAudioPlaybackStopped": "پخش صدا متوقف شد",
|
||||
"NotificationOptionCameraImageUploaded": "تصاویر دوربین آپلود شد",
|
||||
"NotificationOptionGamePlayback": "پخش بازی آغاز شد",
|
||||
"NotificationOptionGamePlaybackStopped": "پخش بازی متوقف شد",
|
||||
"NotificationOptionInstallationFailed": "شکست نصب",
|
||||
"NotificationOptionNewLibraryContent": "محتوای جدید افزوده شد",
|
||||
"NotificationOptionPluginError": "خرابی افزونه",
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
"FailedLoginAttemptWithUserName": "Failed login attempt from {0}",
|
||||
"Favorites": "Favorites",
|
||||
"Folders": "Folders",
|
||||
"Games": "Games",
|
||||
"Genres": "Genres",
|
||||
"HeaderAlbumArtists": "Album Artists",
|
||||
"HeaderCameraUploads": "Camera Uploads",
|
||||
|
@ -51,8 +50,6 @@
|
|||
"NotificationOptionAudioPlayback": "Audio playback started",
|
||||
"NotificationOptionAudioPlaybackStopped": "Audio playback stopped",
|
||||
"NotificationOptionCameraImageUploaded": "Camera image uploaded",
|
||||
"NotificationOptionGamePlayback": "Game playback started",
|
||||
"NotificationOptionGamePlaybackStopped": "Game playback stopped",
|
||||
"NotificationOptionInstallationFailed": "Installation failure",
|
||||
"NotificationOptionNewLibraryContent": "New content added",
|
||||
"NotificationOptionPluginError": "Plugin failure",
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
"FailedLoginAttemptWithUserName": "Échec d'une tentative de connexion de {0}",
|
||||
"Favorites": "Favoris",
|
||||
"Folders": "Dossiers",
|
||||
"Games": "Jeux",
|
||||
"Genres": "Genres",
|
||||
"HeaderAlbumArtists": "Artistes de l'album",
|
||||
"HeaderCameraUploads": "Photos transférées",
|
||||
|
@ -51,8 +50,6 @@
|
|||
"NotificationOptionAudioPlayback": "Lecture audio démarrée",
|
||||
"NotificationOptionAudioPlaybackStopped": "Lecture audio arrêté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",
|
||||
"NotificationOptionNewLibraryContent": "Nouveau contenu ajouté",
|
||||
"NotificationOptionPluginError": "Erreur d'extension",
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
"FailedLoginAttemptWithUserName": "Failed login attempt from {0}",
|
||||
"Favorites": "Favorites",
|
||||
"Folders": "Folders",
|
||||
"Games": "Spiel",
|
||||
"Genres": "Genres",
|
||||
"HeaderAlbumArtists": "Albuminterprete",
|
||||
"HeaderCameraUploads": "Camera Uploads",
|
||||
|
@ -51,8 +50,6 @@
|
|||
"NotificationOptionAudioPlayback": "Audio playback started",
|
||||
"NotificationOptionAudioPlaybackStopped": "Audio playback stopped",
|
||||
"NotificationOptionCameraImageUploaded": "Camera image uploaded",
|
||||
"NotificationOptionGamePlayback": "Game playback started",
|
||||
"NotificationOptionGamePlaybackStopped": "Game playback stopped",
|
||||
"NotificationOptionInstallationFailed": "Installation failure",
|
||||
"NotificationOptionNewLibraryContent": "New content added",
|
||||
"NotificationOptionPluginError": "Plugin failure",
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
"FailedLoginAttemptWithUserName": "Failed login attempt from {0}",
|
||||
"Favorites": "Favorites",
|
||||
"Folders": "Folders",
|
||||
"Games": "משחקים",
|
||||
"Genres": "ז'אנרים",
|
||||
"HeaderAlbumArtists": "Album Artists",
|
||||
"HeaderCameraUploads": "Camera Uploads",
|
||||
|
@ -51,8 +50,6 @@
|
|||
"NotificationOptionAudioPlayback": "Audio playback started",
|
||||
"NotificationOptionAudioPlaybackStopped": "Audio playback stopped",
|
||||
"NotificationOptionCameraImageUploaded": "Camera image uploaded",
|
||||
"NotificationOptionGamePlayback": "Game playback started",
|
||||
"NotificationOptionGamePlaybackStopped": "Game playback stopped",
|
||||
"NotificationOptionInstallationFailed": "Installation failure",
|
||||
"NotificationOptionNewLibraryContent": "New content added",
|
||||
"NotificationOptionPluginError": "Plugin failure",
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
"FailedLoginAttemptWithUserName": "Neuspjeli pokušaj prijave za {0}",
|
||||
"Favorites": "Omiljeni",
|
||||
"Folders": "Mape",
|
||||
"Games": "Igre",
|
||||
"Genres": "Žanrovi",
|
||||
"HeaderAlbumArtists": "Izvođači albuma",
|
||||
"HeaderCameraUploads": "Camera Uploads",
|
||||
|
@ -51,8 +50,6 @@
|
|||
"NotificationOptionAudioPlayback": "Reprodukcija glazbe započeta",
|
||||
"NotificationOptionAudioPlaybackStopped": "Reprodukcija audiozapisa je zaustavljena",
|
||||
"NotificationOptionCameraImageUploaded": "Slike kamere preuzete",
|
||||
"NotificationOptionGamePlayback": "Igrica pokrenuta",
|
||||
"NotificationOptionGamePlaybackStopped": "Reprodukcija igre je zaustavljena",
|
||||
"NotificationOptionInstallationFailed": "Instalacija nije izvršena",
|
||||
"NotificationOptionNewLibraryContent": "Novi sadržaj je dodan",
|
||||
"NotificationOptionPluginError": "Dodatak otkazao",
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
"FailedLoginAttemptWithUserName": "Failed login attempt from {0}",
|
||||
"Favorites": "Kedvencek",
|
||||
"Folders": "Könyvtárak",
|
||||
"Games": "Játékok",
|
||||
"Genres": "Műfajok",
|
||||
"HeaderAlbumArtists": "Album Előadók",
|
||||
"HeaderCameraUploads": "Camera Uploads",
|
||||
|
@ -51,8 +50,6 @@
|
|||
"NotificationOptionAudioPlayback": "Audió lejátszás elkezdve",
|
||||
"NotificationOptionAudioPlaybackStopped": "Audió lejátszás befejezve",
|
||||
"NotificationOptionCameraImageUploaded": "Kamera kép feltöltve",
|
||||
"NotificationOptionGamePlayback": "Game playback started",
|
||||
"NotificationOptionGamePlaybackStopped": "Game playback stopped",
|
||||
"NotificationOptionInstallationFailed": "Telepítési hiba",
|
||||
"NotificationOptionNewLibraryContent": "Új tartalom hozzáadva",
|
||||
"NotificationOptionPluginError": "Bővítmény hiba",
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
"FailedLoginAttemptWithUserName": "Tentativo di accesso fallito da {0}",
|
||||
"Favorites": "Preferiti",
|
||||
"Folders": "Cartelle",
|
||||
"Games": "Giochi",
|
||||
"Genres": "Generi",
|
||||
"HeaderAlbumArtists": "Artisti Album",
|
||||
"HeaderCameraUploads": "Caricamenti Fotocamera",
|
||||
|
@ -51,8 +50,6 @@
|
|||
"NotificationOptionAudioPlayback": "La riproduzione audio è iniziata",
|
||||
"NotificationOptionAudioPlaybackStopped": "La riproduzione audio è stata interrotta",
|
||||
"NotificationOptionCameraImageUploaded": "Immagine fotocamera caricata",
|
||||
"NotificationOptionGamePlayback": "Il gioco è stato avviato",
|
||||
"NotificationOptionGamePlaybackStopped": "Il gioco è stato fermato",
|
||||
"NotificationOptionInstallationFailed": "Installazione fallita",
|
||||
"NotificationOptionNewLibraryContent": "Nuovo contenuto aggiunto",
|
||||
"NotificationOptionPluginError": "Errore del Plug-in",
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
"FailedLoginAttemptWithUserName": "{0} тарапынан кіру әрекеті сәтсіз",
|
||||
"Favorites": "Таңдаулылар",
|
||||
"Folders": "Қалталар",
|
||||
"Games": "Ойындар",
|
||||
"Genres": "Жанрлар",
|
||||
"HeaderAlbumArtists": "Альбом орындаушылары",
|
||||
"HeaderCameraUploads": "Камерадан жүктелгендер",
|
||||
|
@ -51,8 +50,6 @@
|
|||
"NotificationOptionAudioPlayback": "Дыбыс ойнатуы басталды",
|
||||
"NotificationOptionAudioPlaybackStopped": "Дыбыс ойнатуы тоқтатылды",
|
||||
"NotificationOptionCameraImageUploaded": "Камерадан фотосурет кері қотарылған",
|
||||
"NotificationOptionGamePlayback": "Ойын ойнатуы басталды",
|
||||
"NotificationOptionGamePlaybackStopped": "Ойын ойнатуы тоқтатылды",
|
||||
"NotificationOptionInstallationFailed": "Орнату сәтсіздігі",
|
||||
"NotificationOptionNewLibraryContent": "Жаңа мазмұн үстелген",
|
||||
"NotificationOptionPluginError": "Плагин сәтсіздігі",
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
"FailedLoginAttemptWithUserName": "Failed login attempt from {0}",
|
||||
"Favorites": "Favorites",
|
||||
"Folders": "Folders",
|
||||
"Games": "Games",
|
||||
"Genres": "Genres",
|
||||
"HeaderAlbumArtists": "앨범 아티스트",
|
||||
"HeaderCameraUploads": "Camera Uploads",
|
||||
|
@ -51,8 +50,6 @@
|
|||
"NotificationOptionAudioPlayback": "Audio playback started",
|
||||
"NotificationOptionAudioPlaybackStopped": "Audio playback stopped",
|
||||
"NotificationOptionCameraImageUploaded": "Camera image uploaded",
|
||||
"NotificationOptionGamePlayback": "Game playback started",
|
||||
"NotificationOptionGamePlaybackStopped": "Game playback stopped",
|
||||
"NotificationOptionInstallationFailed": "Installation failure",
|
||||
"NotificationOptionNewLibraryContent": "New content added",
|
||||
"NotificationOptionPluginError": "Plugin failure",
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
"FailedLoginAttemptWithUserName": "Failed login attempt from {0}",
|
||||
"Favorites": "Favorites",
|
||||
"Folders": "Folders",
|
||||
"Games": "Games",
|
||||
"Genres": "Žanrai",
|
||||
"HeaderAlbumArtists": "Album Artists",
|
||||
"HeaderCameraUploads": "Camera Uploads",
|
||||
|
@ -51,8 +50,6 @@
|
|||
"NotificationOptionAudioPlayback": "Audio playback started",
|
||||
"NotificationOptionAudioPlaybackStopped": "Audio playback stopped",
|
||||
"NotificationOptionCameraImageUploaded": "Camera image uploaded",
|
||||
"NotificationOptionGamePlayback": "Game playback started",
|
||||
"NotificationOptionGamePlaybackStopped": "Game playback stopped",
|
||||
"NotificationOptionInstallationFailed": "Installation failure",
|
||||
"NotificationOptionNewLibraryContent": "New content added",
|
||||
"NotificationOptionPluginError": "Plugin failure",
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
"FailedLoginAttemptWithUserName": "Failed login attempt from {0}",
|
||||
"Favorites": "Favorites",
|
||||
"Folders": "Folders",
|
||||
"Games": "Games",
|
||||
"Genres": "Genres",
|
||||
"HeaderAlbumArtists": "Album Artists",
|
||||
"HeaderCameraUploads": "Camera Uploads",
|
||||
|
@ -51,8 +50,6 @@
|
|||
"NotificationOptionAudioPlayback": "Audio playback started",
|
||||
"NotificationOptionAudioPlaybackStopped": "Audio playback stopped",
|
||||
"NotificationOptionCameraImageUploaded": "Camera image uploaded",
|
||||
"NotificationOptionGamePlayback": "Game playback started",
|
||||
"NotificationOptionGamePlaybackStopped": "Game playback stopped",
|
||||
"NotificationOptionInstallationFailed": "Installation failure",
|
||||
"NotificationOptionNewLibraryContent": "New content added",
|
||||
"NotificationOptionPluginError": "Plugin failure",
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
"FailedLoginAttemptWithUserName": "Mislykket påloggingsforsøk fra {0}",
|
||||
"Favorites": "Favoritter",
|
||||
"Folders": "Mapper",
|
||||
"Games": "Spill",
|
||||
"Genres": "Sjanger",
|
||||
"HeaderAlbumArtists": "Albumartist",
|
||||
"HeaderCameraUploads": "Camera Uploads",
|
||||
|
@ -51,8 +50,6 @@
|
|||
"NotificationOptionAudioPlayback": "Lyd tilbakespilling startet",
|
||||
"NotificationOptionAudioPlaybackStopped": "Lyd avspilling stoppet",
|
||||
"NotificationOptionCameraImageUploaded": "Kamera bilde lastet opp",
|
||||
"NotificationOptionGamePlayback": "Spill avspillingen startet",
|
||||
"NotificationOptionGamePlaybackStopped": "Filmer",
|
||||
"NotificationOptionInstallationFailed": "Installasjon feil",
|
||||
"NotificationOptionNewLibraryContent": "Ny innhold er lagt til",
|
||||
"NotificationOptionPluginError": "Plugin feil",
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
"FailedLoginAttemptWithUserName": "Mislukte aanmeld poging van {0}",
|
||||
"Favorites": "Favorieten",
|
||||
"Folders": "Mappen",
|
||||
"Games": "Spellen",
|
||||
"Genres": "Genres",
|
||||
"HeaderAlbumArtists": "Album artiesten",
|
||||
"HeaderCameraUploads": "Camera uploads",
|
||||
|
@ -51,8 +50,6 @@
|
|||
"NotificationOptionAudioPlayback": "Geluid gestart",
|
||||
"NotificationOptionAudioPlaybackStopped": "Geluid gestopt",
|
||||
"NotificationOptionCameraImageUploaded": "Camera afbeelding geüpload",
|
||||
"NotificationOptionGamePlayback": "Spel gestart",
|
||||
"NotificationOptionGamePlaybackStopped": "Spel gestopt",
|
||||
"NotificationOptionInstallationFailed": "Installatie mislukt",
|
||||
"NotificationOptionNewLibraryContent": "Nieuwe content toegevoegd",
|
||||
"NotificationOptionPluginError": "Plug-in fout",
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
"FailedLoginAttemptWithUserName": "Próba logowania przez {0} zakończona niepowodzeniem",
|
||||
"Favorites": "Ulubione",
|
||||
"Folders": "Foldery",
|
||||
"Games": "Gry",
|
||||
"Genres": "Gatunki",
|
||||
"HeaderAlbumArtists": "Wykonawcy albumów",
|
||||
"HeaderCameraUploads": "Przekazane obrazy",
|
||||
|
@ -51,8 +50,6 @@
|
|||
"NotificationOptionAudioPlayback": "Rozpoczęto odtwarzanie muzyki",
|
||||
"NotificationOptionAudioPlaybackStopped": "Odtwarzane dźwięku zatrzymane",
|
||||
"NotificationOptionCameraImageUploaded": "Przekazano obraz z urządzenia mobilnego",
|
||||
"NotificationOptionGamePlayback": "Odtwarzanie gry rozpoczęte",
|
||||
"NotificationOptionGamePlaybackStopped": "Odtwarzanie gry zatrzymane",
|
||||
"NotificationOptionInstallationFailed": "Niepowodzenie instalacji",
|
||||
"NotificationOptionNewLibraryContent": "Dodano nową zawartość",
|
||||
"NotificationOptionPluginError": "Awaria wtyczki",
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
"FailedLoginAttemptWithUserName": "Falha na tentativa de login de {0}",
|
||||
"Favorites": "Favoritos",
|
||||
"Folders": "Pastas",
|
||||
"Games": "Jogos",
|
||||
"Genres": "Gêneros",
|
||||
"HeaderAlbumArtists": "Artistas do Álbum",
|
||||
"HeaderCameraUploads": "Uploads da Câmera",
|
||||
|
@ -51,8 +50,6 @@
|
|||
"NotificationOptionAudioPlayback": "Reprodução de áudio iniciada",
|
||||
"NotificationOptionAudioPlaybackStopped": "Reprodução de áudio parada",
|
||||
"NotificationOptionCameraImageUploaded": "Imagem de câmera enviada",
|
||||
"NotificationOptionGamePlayback": "Reprodução de jogo iniciada",
|
||||
"NotificationOptionGamePlaybackStopped": "Reprodução de jogo parada",
|
||||
"NotificationOptionInstallationFailed": "Falha na instalação",
|
||||
"NotificationOptionNewLibraryContent": "Novo conteúdo adicionado",
|
||||
"NotificationOptionPluginError": "Falha de plugin",
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
"FailedLoginAttemptWithUserName": "Failed login attempt from {0}",
|
||||
"Favorites": "Favorites",
|
||||
"Folders": "Folders",
|
||||
"Games": "Games",
|
||||
"Genres": "Genres",
|
||||
"HeaderAlbumArtists": "Album Artists",
|
||||
"HeaderCameraUploads": "Camera Uploads",
|
||||
|
@ -51,8 +50,6 @@
|
|||
"NotificationOptionAudioPlayback": "Audio playback started",
|
||||
"NotificationOptionAudioPlaybackStopped": "Audio playback stopped",
|
||||
"NotificationOptionCameraImageUploaded": "Camera image uploaded",
|
||||
"NotificationOptionGamePlayback": "Game playback started",
|
||||
"NotificationOptionGamePlaybackStopped": "Game playback stopped",
|
||||
"NotificationOptionInstallationFailed": "Installation failure",
|
||||
"NotificationOptionNewLibraryContent": "New content added",
|
||||
"NotificationOptionPluginError": "Plugin failure",
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
"FailedLoginAttemptWithUserName": "{0} - попытка входа неудачна",
|
||||
"Favorites": "Избранное",
|
||||
"Folders": "Папки",
|
||||
"Games": "Игры",
|
||||
"Genres": "Жанры",
|
||||
"HeaderAlbumArtists": "Исп-ли альбома",
|
||||
"HeaderCameraUploads": "Камеры",
|
||||
|
@ -51,8 +50,6 @@
|
|||
"NotificationOptionAudioPlayback": "Воспр-ие аудио зап-но",
|
||||
"NotificationOptionAudioPlaybackStopped": "Восп-ие аудио ост-но",
|
||||
"NotificationOptionCameraImageUploaded": "Произведена выкладка отснятого с камеры",
|
||||
"NotificationOptionGamePlayback": "Воспр-ие игры зап-но",
|
||||
"NotificationOptionGamePlaybackStopped": "Восп-ие игры ост-но",
|
||||
"NotificationOptionInstallationFailed": "Сбой установки",
|
||||
"NotificationOptionNewLibraryContent": "Новое содержание добавлено",
|
||||
"NotificationOptionPluginError": "Сбой плагина",
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
"FailedLoginAttemptWithUserName": "Neúspešný pokus o prihlásenie z {0}",
|
||||
"Favorites": "Obľúbené",
|
||||
"Folders": "Priečinky",
|
||||
"Games": "Hry",
|
||||
"Genres": "Žánre",
|
||||
"HeaderAlbumArtists": "Album Artists",
|
||||
"HeaderCameraUploads": "Camera Uploads",
|
||||
|
@ -51,8 +50,6 @@
|
|||
"NotificationOptionAudioPlayback": "Spustené prehrávanie audia",
|
||||
"NotificationOptionAudioPlaybackStopped": "Zastavené prehrávanie audia",
|
||||
"NotificationOptionCameraImageUploaded": "Nahraný obrázok z fotoaparátu",
|
||||
"NotificationOptionGamePlayback": "Game playback started",
|
||||
"NotificationOptionGamePlaybackStopped": "Hra ukončená",
|
||||
"NotificationOptionInstallationFailed": "Chyba inštalácie",
|
||||
"NotificationOptionNewLibraryContent": "Pridaný nový obsah",
|
||||
"NotificationOptionPluginError": "Chyba rozšírenia",
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
"FailedLoginAttemptWithUserName": "Failed login attempt from {0}",
|
||||
"Favorites": "Favorites",
|
||||
"Folders": "Folders",
|
||||
"Games": "Games",
|
||||
"Genres": "Genres",
|
||||
"HeaderAlbumArtists": "Album Artists",
|
||||
"HeaderCameraUploads": "Camera Uploads",
|
||||
|
@ -51,8 +50,6 @@
|
|||
"NotificationOptionAudioPlayback": "Audio playback started",
|
||||
"NotificationOptionAudioPlaybackStopped": "Audio playback stopped",
|
||||
"NotificationOptionCameraImageUploaded": "Camera image uploaded",
|
||||
"NotificationOptionGamePlayback": "Game playback started",
|
||||
"NotificationOptionGamePlaybackStopped": "Game playback stopped",
|
||||
"NotificationOptionInstallationFailed": "Installation failure",
|
||||
"NotificationOptionNewLibraryContent": "New content added",
|
||||
"NotificationOptionPluginError": "Plugin failure",
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
"FailedLoginAttemptWithUserName": "Misslyckat inloggningsförsök från {0}",
|
||||
"Favorites": "Favoriter",
|
||||
"Folders": "Mappar",
|
||||
"Games": "Spel",
|
||||
"Genres": "Genrer",
|
||||
"HeaderAlbumArtists": "Albumartister",
|
||||
"HeaderCameraUploads": "Camera Uploads",
|
||||
|
@ -51,8 +50,6 @@
|
|||
"NotificationOptionAudioPlayback": "Ljuduppspelning har påbörjats",
|
||||
"NotificationOptionAudioPlaybackStopped": "Ljuduppspelning stoppad",
|
||||
"NotificationOptionCameraImageUploaded": "Kamerabild har laddats upp",
|
||||
"NotificationOptionGamePlayback": "Spel har startats",
|
||||
"NotificationOptionGamePlaybackStopped": "Spel stoppat",
|
||||
"NotificationOptionInstallationFailed": "Fel vid installation",
|
||||
"NotificationOptionNewLibraryContent": "Nytt innehåll har lagts till",
|
||||
"NotificationOptionPluginError": "Fel uppstod med tillägget",
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
"FailedLoginAttemptWithUserName": "Failed login attempt from {0}",
|
||||
"Favorites": "Favorites",
|
||||
"Folders": "Folders",
|
||||
"Games": "Games",
|
||||
"Genres": "Genres",
|
||||
"HeaderAlbumArtists": "Album Artists",
|
||||
"HeaderCameraUploads": "Camera Uploads",
|
||||
|
@ -51,8 +50,6 @@
|
|||
"NotificationOptionAudioPlayback": "Audio playback started",
|
||||
"NotificationOptionAudioPlaybackStopped": "Audio playback stopped",
|
||||
"NotificationOptionCameraImageUploaded": "Camera image uploaded",
|
||||
"NotificationOptionGamePlayback": "Game playback started",
|
||||
"NotificationOptionGamePlaybackStopped": "Game playback stopped",
|
||||
"NotificationOptionInstallationFailed": "Installation failure",
|
||||
"NotificationOptionNewLibraryContent": "New content added",
|
||||
"NotificationOptionPluginError": "Plugin failure",
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
"FailedLoginAttemptWithUserName": "来自 {0} 的失败登入",
|
||||
"Favorites": "最爱",
|
||||
"Folders": "文件夹",
|
||||
"Games": "游戏",
|
||||
"Genres": "风格",
|
||||
"HeaderAlbumArtists": "专辑作家",
|
||||
"HeaderCameraUploads": "相机上传",
|
||||
|
@ -51,8 +50,6 @@
|
|||
"NotificationOptionAudioPlayback": "音频开始播放",
|
||||
"NotificationOptionAudioPlaybackStopped": "音频播放已停止",
|
||||
"NotificationOptionCameraImageUploaded": "相机图片已上传",
|
||||
"NotificationOptionGamePlayback": "游戏开始",
|
||||
"NotificationOptionGamePlaybackStopped": "游戏停止",
|
||||
"NotificationOptionInstallationFailed": "安装失败",
|
||||
"NotificationOptionNewLibraryContent": "已添加新内容",
|
||||
"NotificationOptionPluginError": "插件失败",
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
"FailedLoginAttemptWithUserName": "Failed login attempt from {0}",
|
||||
"Favorites": "Favorites",
|
||||
"Folders": "Folders",
|
||||
"Games": "Games",
|
||||
"Genres": "Genres",
|
||||
"HeaderAlbumArtists": "Album Artists",
|
||||
"HeaderCameraUploads": "Camera Uploads",
|
||||
|
@ -51,8 +50,6 @@
|
|||
"NotificationOptionAudioPlayback": "Audio playback started",
|
||||
"NotificationOptionAudioPlaybackStopped": "Audio playback stopped",
|
||||
"NotificationOptionCameraImageUploaded": "Camera image uploaded",
|
||||
"NotificationOptionGamePlayback": "Game playback started",
|
||||
"NotificationOptionGamePlaybackStopped": "Game playback stopped",
|
||||
"NotificationOptionInstallationFailed": "Installation failure",
|
||||
"NotificationOptionNewLibraryContent": "New content added",
|
||||
"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
Loading…
Reference in New Issue
Block a user