Merge branch 'master' into images

pull/2321/head
Bond-009 4 years ago committed by GitHub
commit a2490a7ae5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -3,7 +3,7 @@ ARG FFMPEG_VERSION=latest
FROM node:alpine as web-builder FROM node:alpine as web-builder
ARG JELLYFIN_WEB_VERSION=master ARG JELLYFIN_WEB_VERSION=master
RUN apk add curl \ RUN apk add curl git \
&& curl -L https://github.com/jellyfin/jellyfin-web/archive/${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \ && curl -L https://github.com/jellyfin/jellyfin-web/archive/${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \
&& cd jellyfin-web-* \ && cd jellyfin-web-* \
&& yarn install \ && yarn install \
@ -31,7 +31,7 @@ COPY --from=web-builder /dist /jellyfin/jellyfin-web
# mesa-va-drivers: needed for VAAPI # mesa-va-drivers: needed for VAAPI
RUN apt-get update \ RUN apt-get update \
&& apt-get install --no-install-recommends --no-install-suggests -y \ && apt-get install --no-install-recommends --no-install-suggests -y \
libfontconfig1 libgomp1 libva-drm2 mesa-va-drivers openssl \ libfontconfig1 libgomp1 libva-drm2 mesa-va-drivers openssl ca-certificates \
&& apt-get clean autoclean \ && apt-get clean autoclean \
&& apt-get autoremove \ && apt-get autoremove \
&& rm -rf /var/lib/apt/lists/* \ && rm -rf /var/lib/apt/lists/* \

@ -3,7 +3,7 @@ ARG DOTNET_VERSION=3.1
FROM node:alpine as web-builder FROM node:alpine as web-builder
ARG JELLYFIN_WEB_VERSION=master ARG JELLYFIN_WEB_VERSION=master
RUN apk add curl \ RUN apk add curl git \
&& curl -L https://github.com/jellyfin/jellyfin-web/archive/${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \ && curl -L https://github.com/jellyfin/jellyfin-web/archive/${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \
&& cd jellyfin-web-* \ && cd jellyfin-web-* \
&& yarn install \ && yarn install \
@ -24,7 +24,7 @@ RUN dotnet publish Jellyfin.Server --configuration Release --output="/jellyfin"
FROM debian:buster-slim FROM debian:buster-slim
RUN apt-get update \ RUN apt-get update \
&& apt-get install --no-install-recommends --no-install-suggests -y ffmpeg \ && apt-get install --no-install-recommends --no-install-suggests -y ffmpeg \
libssl-dev \ libssl-dev ca-certificates \
&& rm -rf /var/lib/apt/lists/* \ && rm -rf /var/lib/apt/lists/* \
&& mkdir -p /cache /config /media \ && mkdir -p /cache /config /media \
&& chmod 777 /cache /config /media && chmod 777 /cache /config /media

@ -3,7 +3,7 @@ ARG DOTNET_VERSION=3.1
FROM node:alpine as web-builder FROM node:alpine as web-builder
ARG JELLYFIN_WEB_VERSION=master ARG JELLYFIN_WEB_VERSION=master
RUN apk add curl \ RUN apk add curl git \
&& curl -L https://github.com/jellyfin/jellyfin-web/archive/${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \ && curl -L https://github.com/jellyfin/jellyfin-web/archive/${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \
&& cd jellyfin-web-* \ && cd jellyfin-web-* \
&& yarn install \ && yarn install \
@ -24,7 +24,7 @@ RUN dotnet publish Jellyfin.Server --configuration Release --output="/jellyfin"
FROM debian:buster-slim FROM debian:buster-slim
RUN apt-get update \ RUN apt-get update \
&& apt-get install --no-install-recommends --no-install-suggests -y ffmpeg \ && apt-get install --no-install-recommends --no-install-suggests -y ffmpeg \
libssl-dev \ libssl-dev ca-certificates \
&& rm -rf /var/lib/apt/lists/* \ && rm -rf /var/lib/apt/lists/* \
&& mkdir -p /cache /config /media \ && mkdir -p /cache /config /media \
&& chmod 777 /cache /config /media && chmod 777 /cache /config /media

@ -9,7 +9,7 @@
</ItemGroup> </ItemGroup>
<PropertyGroup> <PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework> <TargetFramework>netstandard2.1</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo> <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<GenerateDocumentationFile>true</GenerateDocumentationFile> <GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup> </PropertyGroup>

@ -42,7 +42,7 @@ namespace DvdLib.Ifo
} }
else else
{ {
using (var vmgFs = _fileSystem.GetFileStream(vmgPath.FullName, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read)) using (var vmgFs = new FileStream(vmgPath.FullName, FileMode.Open, FileAccess.Read, FileShare.Read))
{ {
using (var vmgRead = new BigEndianBinaryReader(vmgFs)) using (var vmgRead = new BigEndianBinaryReader(vmgFs))
{ {
@ -95,7 +95,7 @@ namespace DvdLib.Ifo
{ {
VTSPaths[vtsNum] = vtsPath; VTSPaths[vtsNum] = vtsPath;
using (var vtsFs = _fileSystem.GetFileStream(vtsPath, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read)) using (var vtsFs = new FileStream(vtsPath, FileMode.Open, FileAccess.Read, FileShare.Read))
{ {
using (var vtsRead = new BigEndianBinaryReader(vtsFs)) using (var vtsRead = new BigEndianBinaryReader(vtsFs))
{ {

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System; using System;
using System.IO; using System.IO;
using System.Text; using System.Text;
@ -170,32 +173,32 @@ namespace Emby.Dlna.Api
return _resultFactory.GetResult(Request, xml, XMLContentType); return _resultFactory.GetResult(Request, xml, XMLContentType);
} }
public object Post(ProcessMediaReceiverRegistrarControlRequest request) public async Task<object> Post(ProcessMediaReceiverRegistrarControlRequest request)
{ {
var response = PostAsync(request.RequestStream, MediaReceiverRegistrar); var response = await PostAsync(request.RequestStream, MediaReceiverRegistrar).ConfigureAwait(false);
return _resultFactory.GetResult(Request, response.Xml, XMLContentType); return _resultFactory.GetResult(Request, response.Xml, XMLContentType);
} }
public object Post(ProcessContentDirectoryControlRequest request) public async Task<object> Post(ProcessContentDirectoryControlRequest request)
{ {
var response = PostAsync(request.RequestStream, ContentDirectory); var response = await PostAsync(request.RequestStream, ContentDirectory).ConfigureAwait(false);
return _resultFactory.GetResult(Request, response.Xml, XMLContentType); return _resultFactory.GetResult(Request, response.Xml, XMLContentType);
} }
public object Post(ProcessConnectionManagerControlRequest request) public async Task<object> Post(ProcessConnectionManagerControlRequest request)
{ {
var response = PostAsync(request.RequestStream, ConnectionManager); var response = await PostAsync(request.RequestStream, ConnectionManager).ConfigureAwait(false);
return _resultFactory.GetResult(Request, response.Xml, XMLContentType); return _resultFactory.GetResult(Request, response.Xml, XMLContentType);
} }
private ControlResponse PostAsync(Stream requestStream, IUpnpService service) private Task<ControlResponse> PostAsync(Stream requestStream, IUpnpService service)
{ {
var id = GetPathValue(2).ToString(); var id = GetPathValue(2).ToString();
return service.ProcessControlRequest(new ControlRequest return service.ProcessControlRequestAsync(new ControlRequest
{ {
Headers = Request.Headers, Headers = Request.Headers,
InputXml = requestStream, InputXml = requestStream,

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System.Linq; using System.Linq;
using MediaBrowser.Controller.Dlna; using MediaBrowser.Controller.Dlna;
using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Net;

@ -1,3 +1,5 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
namespace Emby.Dlna.Common namespace Emby.Dlna.Common
{ {

@ -1,3 +1,7 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System.Globalization;
namespace Emby.Dlna.Common namespace Emby.Dlna.Common
{ {
@ -13,9 +17,14 @@ namespace Emby.Dlna.Common
public string Depth { get; set; } public string Depth { get; set; }
/// <inheritdoc />
public override string ToString() public override string ToString()
{ {
return string.Format("{0}x{1}", Height, Width); return string.Format(
CultureInfo.InvariantCulture,
"{0}x{1}",
Height,
Width);
} }
} }
} }

@ -1,3 +1,5 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
namespace Emby.Dlna.Common namespace Emby.Dlna.Common
{ {
@ -13,9 +15,8 @@ namespace Emby.Dlna.Common
public string EventSubUrl { get; set; } public string EventSubUrl { get; set; }
/// <inheritdoc />
public override string ToString() public override string ToString()
{ => ServiceId;
return string.Format("{0}", ServiceId);
}
} }
} }

@ -1,21 +1,25 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System.Collections.Generic; using System.Collections.Generic;
namespace Emby.Dlna.Common namespace Emby.Dlna.Common
{ {
public class ServiceAction public class ServiceAction
{ {
public ServiceAction()
{
ArgumentList = new List<Argument>();
}
public string Name { get; set; } public string Name { get; set; }
public List<Argument> ArgumentList { get; set; } public List<Argument> ArgumentList { get; set; }
/// <inheritdoc />
public override string ToString() public override string ToString()
{ {
return Name; return Name;
} }
public ServiceAction()
{
ArgumentList = new List<Argument>();
}
} }
} }

@ -1,9 +1,17 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System; using System;
namespace Emby.Dlna.Common namespace Emby.Dlna.Common
{ {
public class StateVariable public class StateVariable
{ {
public StateVariable()
{
AllowedValues = Array.Empty<string>();
}
public string Name { get; set; } public string Name { get; set; }
public string DataType { get; set; } public string DataType { get; set; }
@ -12,14 +20,8 @@ namespace Emby.Dlna.Common
public string[] AllowedValues { get; set; } public string[] AllowedValues { get; set; }
/// <inheritdoc />
public override string ToString() public override string ToString()
{ => Name;
return Name;
}
public StateVariable()
{
AllowedValues = Array.Empty<string>();
}
} }
} }

@ -1,17 +1,10 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
namespace Emby.Dlna.Configuration namespace Emby.Dlna.Configuration
{ {
public class DlnaOptions public class DlnaOptions
{ {
public bool EnablePlayTo { get; set; }
public bool EnableServer { get; set; }
public bool EnableDebugLog { get; set; }
public bool BlastAliveMessages { get; set; }
public bool SendOnlyMatchedHost { get; set; }
public int ClientDiscoveryIntervalSeconds { get; set; }
public int BlastAliveMessageIntervalSeconds { get; set; }
public string DefaultUserId { get; set; }
public DlnaOptions() public DlnaOptions()
{ {
EnablePlayTo = true; EnablePlayTo = true;
@ -21,5 +14,21 @@ namespace Emby.Dlna.Configuration
ClientDiscoveryIntervalSeconds = 60; ClientDiscoveryIntervalSeconds = 60;
BlastAliveMessageIntervalSeconds = 1800; BlastAliveMessageIntervalSeconds = 1800;
} }
public bool EnablePlayTo { get; set; }
public bool EnableServer { get; set; }
public bool EnableDebugLog { get; set; }
public bool BlastAliveMessages { get; set; }
public bool SendOnlyMatchedHost { get; set; }
public int ClientDiscoveryIntervalSeconds { get; set; }
public int BlastAliveMessageIntervalSeconds { get; set; }
public string DefaultUserId { get; set; }
} }
} }

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System.Collections.Generic; using System.Collections.Generic;
using Emby.Dlna.Configuration; using Emby.Dlna.Configuration;
using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Configuration;

@ -1,3 +1,7 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System.Threading.Tasks;
using Emby.Dlna.Service; using Emby.Dlna.Service;
using MediaBrowser.Common.Net; using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Configuration;
@ -20,17 +24,19 @@ namespace Emby.Dlna.ConnectionManager
_logger = logger; _logger = logger;
} }
/// <inheritdoc />
public string GetServiceXml() public string GetServiceXml()
{ {
return new ConnectionManagerXmlBuilder().GetXml(); return new ConnectionManagerXmlBuilder().GetXml();
} }
public ControlResponse ProcessControlRequest(ControlRequest request) /// <inheritdoc />
public Task<ControlResponse> ProcessControlRequestAsync(ControlRequest request)
{ {
var profile = _dlna.GetProfile(request.Headers) ?? var profile = _dlna.GetProfile(request.Headers) ??
_dlna.GetDefaultProfile(); _dlna.GetDefaultProfile();
return new ControlHandler(_config, _logger, profile).ProcessControlRequest(request); return new ControlHandler(_config, _logger, profile).ProcessControlRequestAsync(request);
} }
} }
} }

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System.Collections.Generic; using System.Collections.Generic;
using Emby.Dlna.Common; using Emby.Dlna.Common;
using Emby.Dlna.Service; using Emby.Dlna.Service;

@ -1,5 +1,9 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Xml;
using Emby.Dlna.Service; using Emby.Dlna.Service;
using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Configuration;
@ -12,29 +16,28 @@ namespace Emby.Dlna.ConnectionManager
{ {
private readonly DeviceProfile _profile; private readonly DeviceProfile _profile;
protected override IEnumerable<KeyValuePair<string, string>> GetResult(string methodName, IDictionary<string, string> methodParams) public ControlHandler(IServerConfigurationManager config, ILogger logger, DeviceProfile profile)
: base(config, logger)
{
_profile = profile;
}
/// <inheritdoc />
protected override void WriteResult(string methodName, IDictionary<string, string> methodParams, XmlWriter xmlWriter)
{ {
if (string.Equals(methodName, "GetProtocolInfo", StringComparison.OrdinalIgnoreCase)) if (string.Equals(methodName, "GetProtocolInfo", StringComparison.OrdinalIgnoreCase))
{ {
return HandleGetProtocolInfo(); HandleGetProtocolInfo(xmlWriter);
return;
} }
throw new ResourceNotFoundException("Unexpected control request name: " + methodName); throw new ResourceNotFoundException("Unexpected control request name: " + methodName);
} }
private IEnumerable<KeyValuePair<string, string>> HandleGetProtocolInfo() private void HandleGetProtocolInfo(XmlWriter xmlWriter)
{ {
return new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase) xmlWriter.WriteElementString("Source", _profile.ProtocolInfo);
{ xmlWriter.WriteElementString("Sink", string.Empty);
{ "Source", _profile.ProtocolInfo },
{ "Sink", "" }
};
}
public ControlHandler(IServerConfigurationManager config, ILogger logger, DeviceProfile profile)
: base(config, logger)
{
_profile = profile;
} }
} }
} }

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System.Collections.Generic; using System.Collections.Generic;
using Emby.Dlna.Common; using Emby.Dlna.Common;

@ -1,4 +1,8 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System; using System;
using System.Threading.Tasks;
using Emby.Dlna.Service; using Emby.Dlna.Service;
using MediaBrowser.Common.Net; using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Configuration;
@ -66,12 +70,14 @@ namespace Emby.Dlna.ContentDirectory
} }
} }
/// <inheritdoc />
public string GetServiceXml() public string GetServiceXml()
{ {
return new ContentDirectoryXmlBuilder().GetXml(); return new ContentDirectoryXmlBuilder().GetXml();
} }
public ControlResponse ProcessControlRequest(ControlRequest request) /// <inheritdoc />
public Task<ControlResponse> ProcessControlRequestAsync(ControlRequest request)
{ {
var profile = _dlna.GetProfile(request.Headers) ?? var profile = _dlna.GetProfile(request.Headers) ??
_dlna.GetDefaultProfile(); _dlna.GetDefaultProfile();
@ -96,7 +102,7 @@ namespace Emby.Dlna.ContentDirectory
_userViewManager, _userViewManager,
_mediaEncoder, _mediaEncoder,
_tvSeriesManager) _tvSeriesManager)
.ProcessControlRequest(request); .ProcessControlRequestAsync(request);
} }
private User GetUser(DeviceProfile profile) private User GetUser(DeviceProfile profile)

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System.Collections.Generic; using System.Collections.Generic;
using Emby.Dlna.Common; using Emby.Dlna.Common;
using Emby.Dlna.Service; using Emby.Dlna.Service;

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
@ -44,7 +47,6 @@ namespace Emby.Dlna.ContentDirectory
private const string NS_UPNP = "urn:schemas-upnp-org:metadata-1-0/upnp/"; private const string NS_UPNP = "urn:schemas-upnp-org:metadata-1-0/upnp/";
private readonly int _systemUpdateId; private readonly int _systemUpdateId;
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
private readonly DidlBuilder _didlBuilder; private readonly DidlBuilder _didlBuilder;
@ -58,7 +60,8 @@ namespace Emby.Dlna.ContentDirectory
string accessToken, string accessToken,
IImageProcessor imageProcessor, IImageProcessor imageProcessor,
IUserDataManager userDataManager, IUserDataManager userDataManager,
User user, int systemUpdateId, User user,
int systemUpdateId,
IServerConfigurationManager config, IServerConfigurationManager config,
ILocalizationManager localization, ILocalizationManager localization,
IMediaSourceManager mediaSourceManager, IMediaSourceManager mediaSourceManager,
@ -76,117 +79,132 @@ namespace Emby.Dlna.ContentDirectory
_profile = profile; _profile = profile;
_config = config; _config = config;
_didlBuilder = new DidlBuilder(profile, user, imageProcessor, serverAddress, accessToken, userDataManager, localization, mediaSourceManager, _logger, 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) /// <inheritdoc />
protected override void WriteResult(string methodName, IDictionary<string, string> methodParams, XmlWriter xmlWriter)
{ {
var deviceId = "test"; const string DeviceId = "test";
var user = _user;
if (string.Equals(methodName, "GetSearchCapabilities", StringComparison.OrdinalIgnoreCase)) if (string.Equals(methodName, "GetSearchCapabilities", StringComparison.OrdinalIgnoreCase))
return HandleGetSearchCapabilities(); {
HandleGetSearchCapabilities(xmlWriter);
return;
}
if (string.Equals(methodName, "GetSortCapabilities", StringComparison.OrdinalIgnoreCase)) if (string.Equals(methodName, "GetSortCapabilities", StringComparison.OrdinalIgnoreCase))
return HandleGetSortCapabilities(); {
HandleGetSortCapabilities(xmlWriter);
return;
}
if (string.Equals(methodName, "GetSortExtensionCapabilities", StringComparison.OrdinalIgnoreCase)) if (string.Equals(methodName, "GetSortExtensionCapabilities", StringComparison.OrdinalIgnoreCase))
return HandleGetSortExtensionCapabilities(); {
HandleGetSortExtensionCapabilities(xmlWriter);
return;
}
if (string.Equals(methodName, "GetSystemUpdateID", StringComparison.OrdinalIgnoreCase)) if (string.Equals(methodName, "GetSystemUpdateID", StringComparison.OrdinalIgnoreCase))
return HandleGetSystemUpdateID(); {
HandleGetSystemUpdateID(xmlWriter);
return;
}
if (string.Equals(methodName, "Browse", StringComparison.OrdinalIgnoreCase)) if (string.Equals(methodName, "Browse", StringComparison.OrdinalIgnoreCase))
return HandleBrowse(methodParams, user, deviceId); {
HandleBrowse(xmlWriter, methodParams, DeviceId);
return;
}
if (string.Equals(methodName, "X_GetFeatureList", StringComparison.OrdinalIgnoreCase)) if (string.Equals(methodName, "X_GetFeatureList", StringComparison.OrdinalIgnoreCase))
return HandleXGetFeatureList(); {
HandleXGetFeatureList(xmlWriter);
return;
}
if (string.Equals(methodName, "GetFeatureList", StringComparison.OrdinalIgnoreCase)) if (string.Equals(methodName, "GetFeatureList", StringComparison.OrdinalIgnoreCase))
return HandleGetFeatureList(); {
HandleGetFeatureList(xmlWriter);
return;
}
if (string.Equals(methodName, "X_SetBookmark", StringComparison.OrdinalIgnoreCase)) if (string.Equals(methodName, "X_SetBookmark", StringComparison.OrdinalIgnoreCase))
return HandleXSetBookmark(methodParams, user); {
HandleXSetBookmark(methodParams);
return;
}
if (string.Equals(methodName, "Search", StringComparison.OrdinalIgnoreCase)) if (string.Equals(methodName, "Search", StringComparison.OrdinalIgnoreCase))
return HandleSearch(methodParams, user, deviceId); {
HandleSearch(xmlWriter, methodParams, DeviceId);
return;
}
if (string.Equals(methodName, "X_BrowseByLetter", StringComparison.OrdinalIgnoreCase)) if (string.Equals(methodName, "X_BrowseByLetter", StringComparison.OrdinalIgnoreCase))
return HandleX_BrowseByLetter(methodParams, user, deviceId); {
HandleXBrowseByLetter(xmlWriter, methodParams, DeviceId);
return;
}
throw new ResourceNotFoundException("Unexpected control request name: " + methodName); throw new ResourceNotFoundException("Unexpected control request name: " + methodName);
} }
private IEnumerable<KeyValuePair<string, string>> HandleXSetBookmark(IDictionary<string, string> sparams, User user) private void HandleXSetBookmark(IDictionary<string, string> sparams)
{ {
var id = sparams["ObjectID"]; var id = sparams["ObjectID"];
var serverItem = GetItemFromObjectId(id, user); var serverItem = GetItemFromObjectId(id, _user);
var item = serverItem.Item; var item = serverItem.Item;
var newbookmark = int.Parse(sparams["PosSecond"], _usCulture); var newbookmark = int.Parse(sparams["PosSecond"], CultureInfo.InvariantCulture);
var userdata = _userDataManager.GetUserData(user, item); var userdata = _userDataManager.GetUserData(_user, item);
userdata.PlaybackPositionTicks = TimeSpan.FromSeconds(newbookmark).Ticks; userdata.PlaybackPositionTicks = TimeSpan.FromSeconds(newbookmark).Ticks;
_userDataManager.SaveUserData(user, item, userdata, UserDataSaveReason.TogglePlayed, _userDataManager.SaveUserData(_user, item, userdata, UserDataSaveReason.TogglePlayed,
CancellationToken.None); CancellationToken.None);
return new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
} }
private IEnumerable<KeyValuePair<string, string>> HandleGetSearchCapabilities() private void HandleGetSearchCapabilities(XmlWriter xmlWriter)
{ {
return new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase) xmlWriter.WriteElementString(
{ "SearchCaps",
{ "SearchCaps", "res@resolution,res@size,res@duration,dc:title,dc:creator,upnp:actor,upnp:artist,upnp:genre,upnp:album,dc:date,upnp:class,@id,@refID,@protocolInfo,upnp:author,dc:description,pv:avKeywords" } "res@resolution,res@size,res@duration,dc:title,dc:creator,upnp:actor,upnp:artist,upnp:genre,upnp:album,dc:date,upnp:class,@id,@refID,@protocolInfo,upnp:author,dc:description,pv:avKeywords");
};
} }
private IEnumerable<KeyValuePair<string, string>> HandleGetSortCapabilities() private void HandleGetSortCapabilities(XmlWriter xmlWriter)
{ {
return new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase) xmlWriter.WriteElementString(
{ "SortCaps",
{ "SortCaps", "res@duration,res@size,res@bitrate,dc:date,dc:title,dc:size,upnp:album,upnp:artist,upnp:albumArtist,upnp:episodeNumber,upnp:genre,upnp:originalTrackNumber,upnp:rating" } "res@duration,res@size,res@bitrate,dc:date,dc:title,dc:size,upnp:album,upnp:artist,upnp:albumArtist,upnp:episodeNumber,upnp:genre,upnp:originalTrackNumber,upnp:rating");
};
} }
private IEnumerable<KeyValuePair<string, string>> HandleGetSortExtensionCapabilities() private void HandleGetSortExtensionCapabilities(XmlWriter xmlWriter)
{ {
return new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase) xmlWriter.WriteElementString(
{ "SortExtensionCaps",
{ "SortExtensionCaps", "res@duration,res@size,res@bitrate,dc:date,dc:title,dc:size,upnp:album,upnp:artist,upnp:albumArtist,upnp:episodeNumber,upnp:genre,upnp:originalTrackNumber,upnp:rating" } "res@duration,res@size,res@bitrate,dc:date,dc:title,dc:size,upnp:album,upnp:artist,upnp:albumArtist,upnp:episodeNumber,upnp:genre,upnp:originalTrackNumber,upnp:rating");
};
} }
private IEnumerable<KeyValuePair<string, string>> HandleGetSystemUpdateID() private void HandleGetSystemUpdateID(XmlWriter xmlWriter)
{ {
var headers = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); xmlWriter.WriteElementString("Id", _systemUpdateId.ToString(CultureInfo.InvariantCulture));
headers.Add("Id", _systemUpdateId.ToString(_usCulture));
return headers;
} }
private IEnumerable<KeyValuePair<string, string>> HandleGetFeatureList() private void HandleGetFeatureList(XmlWriter xmlWriter)
{ {
return new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase) xmlWriter.WriteElementString("FeatureList", WriteFeatureListXml());
{
{ "FeatureList", GetFeatureListXml() }
};
} }
private IEnumerable<KeyValuePair<string, string>> HandleXGetFeatureList() private void HandleXGetFeatureList(XmlWriter xmlWriter)
{ => HandleGetFeatureList(xmlWriter);
return new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
{
{ "FeatureList", GetFeatureListXml() }
};
}
private string GetFeatureListXml() private string WriteFeatureListXml()
{ {
// TODO: clean this up
var builder = new StringBuilder(); var builder = new StringBuilder();
builder.Append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); builder.Append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
@ -213,7 +231,7 @@ namespace Emby.Dlna.ContentDirectory
return defaultValue; return defaultValue;
} }
private IEnumerable<KeyValuePair<string, string>> HandleBrowse(IDictionary<string, string> sparams, User user, string deviceId) private void HandleBrowse(XmlWriter xmlWriter, IDictionary<string, string> sparams, string deviceId)
{ {
var id = sparams["ObjectID"]; var id = sparams["ObjectID"];
var flag = sparams["BrowseFlag"]; var flag = sparams["BrowseFlag"];
@ -237,101 +255,95 @@ namespace Emby.Dlna.ContentDirectory
start = startVal; start = startVal;
} }
var settings = new XmlWriterSettings
{
Encoding = Encoding.UTF8,
CloseOutput = false,
OmitXmlDeclaration = true,
ConformanceLevel = ConformanceLevel.Fragment
};
StringWriter builder = new StringWriterWithEncoding(Encoding.UTF8);
int totalCount; int totalCount;
var dlnaOptions = _config.GetDlnaConfiguration(); using (StringWriter builder = new StringWriterWithEncoding(Encoding.UTF8))
using (var writer = XmlWriter.Create(builder, settings))
{ {
//writer.WriteStartDocument(); var settings = new XmlWriterSettings()
{
writer.WriteStartElement(string.Empty, "DIDL-Lite", NS_DIDL); Encoding = Encoding.UTF8,
CloseOutput = false,
writer.WriteAttributeString("xmlns", "dc", null, NS_DC); OmitXmlDeclaration = true,
writer.WriteAttributeString("xmlns", "dlna", null, NS_DLNA); ConformanceLevel = ConformanceLevel.Fragment
writer.WriteAttributeString("xmlns", "upnp", null, NS_UPNP); };
//didl.SetAttribute("xmlns:sec", NS_SEC);
DidlBuilder.WriteXmlRootAttributes(_profile, writer);
var serverItem = GetItemFromObjectId(id, user);
var item = serverItem.Item;
if (string.Equals(flag, "BrowseMetadata")) using (var writer = XmlWriter.Create(builder, settings))
{ {
totalCount = 1; writer.WriteStartElement(string.Empty, "DIDL-Lite", NS_DIDL);
if (item.IsDisplayedAsFolder || serverItem.StubType.HasValue) writer.WriteAttributeString("xmlns", "dc", null, NS_DC);
{ writer.WriteAttributeString("xmlns", "dlna", null, NS_DLNA);
var childrenResult = GetUserItems(item, serverItem.StubType, user, sortCriteria, start, requestedCount); writer.WriteAttributeString("xmlns", "upnp", null, NS_UPNP);
_didlBuilder.WriteFolderElement(writer, item, serverItem.StubType, null, childrenResult.TotalRecordCount, filter, id); DidlBuilder.WriteXmlRootAttributes(_profile, writer);
}
else
{
_didlBuilder.WriteItemElement(dlnaOptions, writer, item, user, null, null, deviceId, filter);
}
provided++; var serverItem = GetItemFromObjectId(id, _user);
} var item = serverItem.Item;
else
{
var childrenResult = GetUserItems(item, serverItem.StubType, user, sortCriteria, start, requestedCount);
totalCount = childrenResult.TotalRecordCount;
provided = childrenResult.Items.Count;
foreach (var i in childrenResult.Items) if (string.Equals(flag, "BrowseMetadata", StringComparison.Ordinal))
{ {
var childItem = i.Item; totalCount = 1;
var displayStubType = i.StubType;
if (childItem.IsDisplayedAsFolder || displayStubType.HasValue) if (item.IsDisplayedAsFolder || serverItem.StubType.HasValue)
{ {
var childCount = (GetUserItems(childItem, displayStubType, user, sortCriteria, null, 0)) var childrenResult = GetUserItems(item, serverItem.StubType, _user, sortCriteria, start, requestedCount);
.TotalRecordCount;
_didlBuilder.WriteFolderElement(writer, childItem, displayStubType, item, childCount, filter); _didlBuilder.WriteFolderElement(writer, item, serverItem.StubType, null, childrenResult.TotalRecordCount, filter, id);
} }
else else
{ {
_didlBuilder.WriteItemElement(dlnaOptions, writer, childItem, user, item, serverItem.StubType, deviceId, filter); var dlnaOptions = _config.GetDlnaConfiguration();
_didlBuilder.WriteItemElement(dlnaOptions, writer, item, _user, null, null, deviceId, filter);
} }
provided++;
} }
else
{
var childrenResult = GetUserItems(item, serverItem.StubType, _user, sortCriteria, start, requestedCount);
totalCount = childrenResult.TotalRecordCount;
provided = childrenResult.Items.Count;
var dlnaOptions = _config.GetDlnaConfiguration();
foreach (var i in childrenResult.Items)
{
var childItem = i.Item;
var displayStubType = i.StubType;
if (childItem.IsDisplayedAsFolder || displayStubType.HasValue)
{
var childCount = GetUserItems(childItem, displayStubType, _user, sortCriteria, null, 0)
.TotalRecordCount;
_didlBuilder.WriteFolderElement(writer, childItem, displayStubType, item, childCount, filter);
}
else
{
_didlBuilder.WriteItemElement(dlnaOptions, writer, childItem, _user, item, serverItem.StubType, deviceId, filter);
}
}
}
writer.WriteFullEndElement();
} }
writer.WriteFullEndElement(); xmlWriter.WriteElementString("Result", builder.ToString());
//writer.WriteEndDocument();
} }
var resXML = builder.ToString(); xmlWriter.WriteElementString("NumberReturned", provided.ToString(CultureInfo.InvariantCulture));
xmlWriter.WriteElementString("TotalMatches", totalCount.ToString(CultureInfo.InvariantCulture));
return new[] xmlWriter.WriteElementString("UpdateID", _systemUpdateId.ToString(CultureInfo.InvariantCulture));
{
new KeyValuePair<string,string>("Result", resXML),
new KeyValuePair<string,string>("NumberReturned", provided.ToString(_usCulture)),
new KeyValuePair<string,string>("TotalMatches", totalCount.ToString(_usCulture)),
new KeyValuePair<string,string>("UpdateID", _systemUpdateId.ToString(_usCulture))
};
} }
private IEnumerable<KeyValuePair<string, string>> HandleX_BrowseByLetter(IDictionary<string, string> sparams, User user, string deviceId) private void HandleXBrowseByLetter(XmlWriter xmlWriter, IDictionary<string, string> sparams, string deviceId)
{ {
// TODO: Implement this method // TODO: Implement this method
return HandleSearch(sparams, user, deviceId); HandleSearch(xmlWriter, sparams, deviceId);
} }
private IEnumerable<KeyValuePair<string, string>> HandleSearch(IDictionary<string, string> sparams, User user, string deviceId) private void HandleSearch(XmlWriter xmlWriter, IDictionary<string, string> sparams, string deviceId)
{ {
var searchCriteria = new SearchCriteria(GetValueOrDefault(sparams, "SearchCriteria", "")); var searchCriteria = new SearchCriteria(GetValueOrDefault(sparams, "SearchCriteria", ""));
var sortCriteria = new SortCriteria(GetValueOrDefault(sparams, "SortCriteria", "")); var sortCriteria = new SortCriteria(GetValueOrDefault(sparams, "SortCriteria", ""));
@ -354,99 +366,86 @@ namespace Emby.Dlna.ContentDirectory
start = startVal; start = startVal;
} }
var settings = new XmlWriterSettings QueryResult<BaseItem> childrenResult;
{
Encoding = Encoding.UTF8,
CloseOutput = false,
OmitXmlDeclaration = true,
ConformanceLevel = ConformanceLevel.Fragment
};
StringWriter builder = new StringWriterWithEncoding(Encoding.UTF8);
int totalCount = 0;
int provided = 0;
using (var writer = XmlWriter.Create(builder, settings)) using (StringWriter builder = new StringWriterWithEncoding(Encoding.UTF8))
{ {
//writer.WriteStartDocument(); var settings = new XmlWriterSettings()
{
writer.WriteStartElement(string.Empty, "DIDL-Lite", NS_DIDL); Encoding = Encoding.UTF8,
CloseOutput = false,
writer.WriteAttributeString("xmlns", "dc", null, NS_DC); OmitXmlDeclaration = true,
writer.WriteAttributeString("xmlns", "dlna", null, NS_DLNA); ConformanceLevel = ConformanceLevel.Fragment
writer.WriteAttributeString("xmlns", "upnp", null, NS_UPNP); };
//didl.SetAttribute("xmlns:sec", NS_SEC);
DidlBuilder.WriteXmlRootAttributes(_profile, writer); using (var writer = XmlWriter.Create(builder, settings))
{
writer.WriteStartElement(string.Empty, "DIDL-Lite", NS_DIDL);
var serverItem = GetItemFromObjectId(sparams["ContainerID"], user); writer.WriteAttributeString("xmlns", "dc", null, NS_DC);
writer.WriteAttributeString("xmlns", "dlna", null, NS_DLNA);
writer.WriteAttributeString("xmlns", "upnp", null, NS_UPNP);
var item = serverItem.Item; DidlBuilder.WriteXmlRootAttributes(_profile, writer);
var childrenResult = (GetChildrenSorted(item, user, searchCriteria, sortCriteria, start, requestedCount)); var serverItem = GetItemFromObjectId(sparams["ContainerID"], _user);
totalCount = childrenResult.TotalRecordCount; var item = serverItem.Item;
provided = childrenResult.Items.Count; childrenResult = GetChildrenSorted(item, _user, searchCriteria, sortCriteria, start, requestedCount);
var dlnaOptions = _config.GetDlnaConfiguration(); var dlnaOptions = _config.GetDlnaConfiguration();
foreach (var i in childrenResult.Items) foreach (var i in childrenResult.Items)
{
if (i.IsDisplayedAsFolder)
{ {
var childCount = (GetChildrenSorted(i, user, searchCriteria, sortCriteria, null, 0)) if (i.IsDisplayedAsFolder)
.TotalRecordCount; {
var childCount = GetChildrenSorted(i, _user, searchCriteria, sortCriteria, null, 0)
.TotalRecordCount;
_didlBuilder.WriteFolderElement(writer, i, null, item, childCount, filter); _didlBuilder.WriteFolderElement(writer, i, null, item, childCount, filter);
} }
else else
{ {
_didlBuilder.WriteItemElement(dlnaOptions, writer, i, user, item, serverItem.StubType, deviceId, filter); _didlBuilder.WriteItemElement(dlnaOptions, writer, i, _user, item, serverItem.StubType, deviceId, filter);
}
} }
writer.WriteFullEndElement();
} }
writer.WriteFullEndElement(); xmlWriter.WriteElementString("Result", builder.ToString());
//writer.WriteEndDocument();
} }
var resXML = builder.ToString(); xmlWriter.WriteElementString("NumberReturned", childrenResult.Items.Count.ToString(CultureInfo.InvariantCulture));
xmlWriter.WriteElementString("TotalMatches", childrenResult.TotalRecordCount.ToString(CultureInfo.InvariantCulture));
return new List<KeyValuePair<string, string>> xmlWriter.WriteElementString("UpdateID", _systemUpdateId.ToString(CultureInfo.InvariantCulture));
{
new KeyValuePair<string,string>("Result", resXML),
new KeyValuePair<string,string>("NumberReturned", provided.ToString(_usCulture)),
new KeyValuePair<string,string>("TotalMatches", totalCount.ToString(_usCulture)),
new KeyValuePair<string,string>("UpdateID", _systemUpdateId.ToString(_usCulture))
};
} }
private QueryResult<BaseItem> GetChildrenSorted(BaseItem item, User user, SearchCriteria search, SortCriteria sort, int? startIndex, int? limit) private QueryResult<BaseItem> GetChildrenSorted(BaseItem item, User user, SearchCriteria search, SortCriteria sort, int? startIndex, int? limit)
{ {
var folder = (Folder)item; var folder = (Folder)item;
var sortOrders = new List<(string, SortOrder)>(); var sortOrders = folder.IsPreSorted
if (!folder.IsPreSorted) ? Array.Empty<(string, SortOrder)>()
{ : new[] { (ItemSortBy.SortName, sort.SortOrder) };
sortOrders.Add((ItemSortBy.SortName, sort.SortOrder));
}
var mediaTypes = new List<string>(); string[] mediaTypes = Array.Empty<string>();
bool? isFolder = null; bool? isFolder = null;
if (search.SearchType == SearchType.Audio) if (search.SearchType == SearchType.Audio)
{ {
mediaTypes.Add(MediaType.Audio); mediaTypes = new[] { MediaType.Audio };
isFolder = false; isFolder = false;
} }
else if (search.SearchType == SearchType.Video) else if (search.SearchType == SearchType.Video)
{ {
mediaTypes.Add(MediaType.Video); mediaTypes = new[] { MediaType.Video };
isFolder = false; isFolder = false;
} }
else if (search.SearchType == SearchType.Image) else if (search.SearchType == SearchType.Image)
{ {
mediaTypes.Add(MediaType.Photo); mediaTypes = new[] { MediaType.Photo };
isFolder = false; isFolder = false;
} }
else if (search.SearchType == SearchType.Playlist) else if (search.SearchType == SearchType.Playlist)
@ -470,7 +469,7 @@ namespace Emby.Dlna.ContentDirectory
IsMissing = false, IsMissing = false,
ExcludeItemTypes = new[] { typeof(Book).Name }, ExcludeItemTypes = new[] { typeof(Book).Name },
IsFolder = isFolder, IsFolder = isFolder,
MediaTypes = mediaTypes.ToArray(), MediaTypes = mediaTypes,
DtoOptions = GetDtoOptions() DtoOptions = GetDtoOptions()
}); });
} }
@ -1304,11 +1303,11 @@ namespace Emby.Dlna.ContentDirectory
StubType? stubType = null; StubType? stubType = null;
// After using PlayTo, MediaMonkey sends a request to the server trying to get item info // After using PlayTo, MediaMonkey sends a request to the server trying to get item info
const string paramsSrch = "Params="; const string ParamsSrch = "Params=";
var paramsIndex = id.IndexOf(paramsSrch, StringComparison.OrdinalIgnoreCase); var paramsIndex = id.IndexOf(ParamsSrch, StringComparison.OrdinalIgnoreCase);
if (paramsIndex != -1) if (paramsIndex != -1)
{ {
id = id.Substring(paramsIndex + paramsSrch.Length); id = id.Substring(paramsIndex + ParamsSrch.Length);
var parts = id.Split(';'); var parts = id.Split(';');
id = parts[23]; id = parts[23];
@ -1336,7 +1335,7 @@ namespace Emby.Dlna.ContentDirectory
}; };
} }
_logger.LogError("Error parsing item Id: {id}. Returning user root folder.", id); Logger.LogError("Error parsing item Id: {id}. Returning user root folder.", id);
return new ServerItem(_libraryManager.GetUserRootFolder()); return new ServerItem(_libraryManager.GetUserRootFolder());
} }

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System.Collections.Generic; using System.Collections.Generic;
using Emby.Dlna.Common; using Emby.Dlna.Common;

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System.IO; using System.IO;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;

@ -1,18 +1,21 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System.Collections.Generic; using System.Collections.Generic;
namespace Emby.Dlna namespace Emby.Dlna
{ {
public class ControlResponse public class ControlResponse
{ {
public ControlResponse()
{
Headers = new Dictionary<string, string>();
}
public IDictionary<string, string> Headers { get; set; } public IDictionary<string, string> Headers { get; set; }
public string Xml { get; set; } public string Xml { get; set; }
public bool IsSuccessful { get; set; } public bool IsSuccessful { get; set; }
public ControlResponse()
{
Headers = new Dictionary<string, string>();
}
} }
} }

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System; using System;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
@ -18,7 +21,6 @@ using MediaBrowser.Controller.Playlists;
using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Drawing; using MediaBrowser.Model.Drawing;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Extensions;
using MediaBrowser.Model.Globalization; using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.Net; using MediaBrowser.Model.Net;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
@ -632,7 +634,7 @@ namespace Emby.Dlna.Didl
{ {
if (item.PremiereDate.HasValue) if (item.PremiereDate.HasValue)
{ {
AddValue(writer, "dc", "date", item.PremiereDate.Value.ToString("o"), NS_DC); AddValue(writer, "dc", "date", item.PremiereDate.Value.ToString("o", CultureInfo.InvariantCulture), NS_DC);
} }
} }

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System; using System;
using MediaBrowser.Model.Extensions; using MediaBrowser.Model.Extensions;
@ -16,7 +19,7 @@ namespace Emby.Dlna.Didl
public Filter(string filter) public Filter(string filter)
{ {
_all = StringHelper.EqualsIgnoreCase(filter, "*"); _all = string.Equals(filter, "*", StringComparison.OrdinalIgnoreCase);
_fields = (filter ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); _fields = (filter ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
} }

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System; using System;
using System.IO; using System.IO;
using System.Text; using System.Text;

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
@ -385,7 +388,7 @@ namespace Emby.Dlna
{ {
Directory.CreateDirectory(systemProfilesPath); Directory.CreateDirectory(systemProfilesPath);
using (var fileStream = _fileSystem.GetFileStream(path, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read)) using (var fileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read))
{ {
await stream.CopyToAsync(fileStream); await stream.CopyToAsync(fileStream);
} }

@ -15,6 +15,19 @@
<TargetFramework>netstandard2.1</TargetFramework> <TargetFramework>netstandard2.1</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo> <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<GenerateDocumentationFile>true</GenerateDocumentationFile> <GenerateDocumentationFile>true</GenerateDocumentationFile>
<TreatWarningsAsErrors Condition=" '$(Configuration)' == 'Release'" >true</TreatWarningsAsErrors>
</PropertyGroup>
<!-- Code Analyzers-->
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.8" PrivateAssets="All" />
<PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="All" />
<PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" />
</ItemGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<CodeAnalysisRuleSet>../jellyfin.ruleset</CodeAnalysisRuleSet>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

@ -1,17 +1,21 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System.Collections.Generic; using System.Collections.Generic;
namespace Emby.Dlna namespace Emby.Dlna
{ {
public class EventSubscriptionResponse public class EventSubscriptionResponse
{ {
public string Content { get; set; }
public string ContentType { get; set; }
public Dictionary<string, string> Headers { get; set; }
public EventSubscriptionResponse() public EventSubscriptionResponse()
{ {
Headers = new Dictionary<string, string>(); Headers = new Dictionary<string, string>();
} }
public string Content { get; set; }
public string ContentType { get; set; }
public Dictionary<string, string> Headers { get; set; }
} }
} }

@ -1,8 +1,12 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.Linq; using System.Linq;
using System.Net.Http;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Extensions;
@ -164,7 +168,7 @@ namespace Emby.Dlna.Eventing
try try
{ {
using (await _httpClient.SendAsync(options, "NOTIFY").ConfigureAwait(false)) using (await _httpClient.SendAsync(options, new HttpMethod("NOTIFY")).ConfigureAwait(false))
{ {
} }

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System; using System;
namespace Emby.Dlna.Eventing namespace Emby.Dlna.Eventing
@ -13,6 +16,8 @@ namespace Emby.Dlna.Eventing
public long TriggerCount { get; set; } public long TriggerCount { get; set; }
public bool IsExpired => SubscriptionTime.AddSeconds(TimeoutSeconds) >= DateTime.UtcNow;
public void IncrementTriggerCount() public void IncrementTriggerCount()
{ {
if (TriggerCount == long.MaxValue) if (TriggerCount == long.MaxValue)
@ -22,7 +27,5 @@ namespace Emby.Dlna.Eventing
TriggerCount++; TriggerCount++;
} }
public bool IsExpired => SubscriptionTime.AddSeconds(TimeoutSeconds) >= DateTime.UtcNow;
} }
} }

@ -1,3 +1,5 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
namespace Emby.Dlna namespace Emby.Dlna
{ {

@ -1,3 +1,5 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
namespace Emby.Dlna namespace Emby.Dlna
{ {

@ -1,3 +1,5 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
namespace Emby.Dlna namespace Emby.Dlna
{ {

@ -1,3 +1,5 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
namespace Emby.Dlna namespace Emby.Dlna
{ {

@ -1,3 +1,8 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System.Threading.Tasks;
namespace Emby.Dlna namespace Emby.Dlna
{ {
public interface IUpnpService public interface IUpnpService
@ -13,6 +18,6 @@ namespace Emby.Dlna
/// </summary> /// </summary>
/// <param name="request">The request.</param> /// <param name="request">The request.</param>
/// <returns>ControlResponse.</returns> /// <returns>ControlResponse.</returns>
ControlResponse ProcessControlRequest(ControlRequest request); Task<ControlResponse> ProcessControlRequestAsync(ControlRequest request);
} }
} }

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System; using System;
using System.Net.Sockets; using System.Net.Sockets;
using System.Globalization; using System.Globalization;

@ -1,5 +1,9 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Xml;
using Emby.Dlna.Service; using Emby.Dlna.Service;
using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Configuration;
@ -9,35 +13,33 @@ namespace Emby.Dlna.MediaReceiverRegistrar
{ {
public class ControlHandler : BaseControlHandler public class ControlHandler : BaseControlHandler
{ {
protected override IEnumerable<KeyValuePair<string, string>> GetResult(string methodName, IDictionary<string, string> methodParams) public ControlHandler(IServerConfigurationManager config, ILogger logger)
: base(config, logger)
{ {
if (string.Equals(methodName, "IsAuthorized", StringComparison.OrdinalIgnoreCase))
return HandleIsAuthorized();
if (string.Equals(methodName, "IsValidated", StringComparison.OrdinalIgnoreCase))
return HandleIsValidated();
throw new ResourceNotFoundException("Unexpected control request name: " + methodName);
} }
private static IEnumerable<KeyValuePair<string, string>> HandleIsAuthorized() /// <inheritdoc />
protected override void WriteResult(string methodName, IDictionary<string, string> methodParams, XmlWriter xmlWriter)
{ {
return new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase) if (string.Equals(methodName, "IsAuthorized", StringComparison.OrdinalIgnoreCase))
{ {
{ "Result", "1" } HandleIsAuthorized(xmlWriter);
}; return;
} }
private static IEnumerable<KeyValuePair<string, string>> HandleIsValidated() if (string.Equals(methodName, "IsValidated", StringComparison.OrdinalIgnoreCase))
{
return new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
{ {
{ "Result", "1" } HandleIsValidated(xmlWriter);
}; return;
} }
public ControlHandler(IServerConfigurationManager config, ILogger logger) throw new ResourceNotFoundException("Unexpected control request name: " + methodName);
: base(config, logger)
{
} }
private static void HandleIsAuthorized(XmlWriter xmlWriter)
=> xmlWriter.WriteElementString("Result", "1");
private static void HandleIsValidated(XmlWriter xmlWriter)
=> xmlWriter.WriteElementString("Result", "1");
} }
} }

@ -1,3 +1,7 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System.Threading.Tasks;
using Emby.Dlna.Service; using Emby.Dlna.Service;
using MediaBrowser.Common.Net; using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Configuration;
@ -15,17 +19,19 @@ namespace Emby.Dlna.MediaReceiverRegistrar
_config = config; _config = config;
} }
/// <inheritdoc />
public string GetServiceXml() public string GetServiceXml()
{ {
return new MediaReceiverRegistrarXmlBuilder().GetXml(); return new MediaReceiverRegistrarXmlBuilder().GetXml();
} }
public ControlResponse ProcessControlRequest(ControlRequest request) /// <inheritdoc />
public Task<ControlResponse> ProcessControlRequestAsync(ControlRequest request)
{ {
return new ControlHandler( return new ControlHandler(
_config, _config,
Logger) Logger)
.ProcessControlRequest(request); .ProcessControlRequestAsync(request);
} }
} }
} }

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System.Collections.Generic; using System.Collections.Generic;
using Emby.Dlna.Common; using Emby.Dlna.Common;
using Emby.Dlna.Service; using Emby.Dlna.Service;

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System.Collections.Generic; using System.Collections.Generic;
using Emby.Dlna.Common; using Emby.Dlna.Common;

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
@ -221,7 +224,7 @@ namespace Emby.Dlna.PlayTo
_logger.LogDebug("Setting mute"); _logger.LogDebug("Setting mute");
var value = mute ? 1 : 0; var value = mute ? 1 : 0;
await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, rendererCommands.BuildPost(command, service.ServiceType, value)) await new SsdpHttpClient(_httpClient).SendCommandAsync(Properties.BaseUrl, service, command.Name, rendererCommands.BuildPost(command, service.ServiceType, value))
.ConfigureAwait(false); .ConfigureAwait(false);
IsMuted = mute; IsMuted = mute;
@ -251,7 +254,7 @@ namespace Emby.Dlna.PlayTo
// Remote control will perform better // Remote control will perform better
Volume = value; Volume = value;
await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, rendererCommands.BuildPost(command, service.ServiceType, value)) await new SsdpHttpClient(_httpClient).SendCommandAsync(Properties.BaseUrl, service, command.Name, rendererCommands.BuildPost(command, service.ServiceType, value))
.ConfigureAwait(false); .ConfigureAwait(false);
} }
@ -270,7 +273,7 @@ namespace Emby.Dlna.PlayTo
throw new InvalidOperationException("Unable to find service"); throw new InvalidOperationException("Unable to find service");
} }
await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, avCommands.BuildPost(command, service.ServiceType, string.Format("{0:hh}:{0:mm}:{0:ss}", value), "REL_TIME")) await new SsdpHttpClient(_httpClient).SendCommandAsync(Properties.BaseUrl, service, command.Name, avCommands.BuildPost(command, service.ServiceType, string.Format("{0:hh}:{0:mm}:{0:ss}", value), "REL_TIME"))
.ConfigureAwait(false); .ConfigureAwait(false);
RestartTimer(true); RestartTimer(true);
@ -302,7 +305,7 @@ namespace Emby.Dlna.PlayTo
} }
var post = avCommands.BuildPost(command, service.ServiceType, url, dictionary); var post = avCommands.BuildPost(command, service.ServiceType, url, dictionary);
await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, post, header: header) await new SsdpHttpClient(_httpClient).SendCommandAsync(Properties.BaseUrl, service, command.Name, post, header: header)
.ConfigureAwait(false); .ConfigureAwait(false);
await Task.Delay(50).ConfigureAwait(false); await Task.Delay(50).ConfigureAwait(false);
@ -344,7 +347,7 @@ namespace Emby.Dlna.PlayTo
throw new InvalidOperationException("Unable to find service"); throw new InvalidOperationException("Unable to find service");
} }
return new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, avCommands.BuildPost(command, service.ServiceType, 1)); return new SsdpHttpClient(_httpClient).SendCommandAsync(Properties.BaseUrl, service, command.Name, avCommands.BuildPost(command, service.ServiceType, 1));
} }
public async Task SetPlay(CancellationToken cancellationToken) public async Task SetPlay(CancellationToken cancellationToken)
@ -368,7 +371,7 @@ namespace Emby.Dlna.PlayTo
var service = GetAvTransportService(); var service = GetAvTransportService();
await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, avCommands.BuildPost(command, service.ServiceType, 1)) await new SsdpHttpClient(_httpClient).SendCommandAsync(Properties.BaseUrl, service, command.Name, avCommands.BuildPost(command, service.ServiceType, 1))
.ConfigureAwait(false); .ConfigureAwait(false);
RestartTimer(true); RestartTimer(true);
@ -386,7 +389,7 @@ namespace Emby.Dlna.PlayTo
var service = GetAvTransportService(); var service = GetAvTransportService();
await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, avCommands.BuildPost(command, service.ServiceType, 1)) await new SsdpHttpClient(_httpClient).SendCommandAsync(Properties.BaseUrl, service, command.Name, avCommands.BuildPost(command, service.ServiceType, 1))
.ConfigureAwait(false); .ConfigureAwait(false);
TransportState = TRANSPORTSTATE.PAUSED; TransportState = TRANSPORTSTATE.PAUSED;
@ -513,7 +516,7 @@ namespace Emby.Dlna.PlayTo
return; return;
} }
var result = await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, rendererCommands.BuildPost(command, service.ServiceType), true) var result = await new SsdpHttpClient(_httpClient).SendCommandAsync(Properties.BaseUrl, service, command.Name, rendererCommands.BuildPost(command, service.ServiceType), true)
.ConfigureAwait(false); .ConfigureAwait(false);
if (result == null || result.Document == null) if (result == null || result.Document == null)
@ -559,7 +562,7 @@ namespace Emby.Dlna.PlayTo
return; return;
} }
var result = await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, rendererCommands.BuildPost(command, service.ServiceType), true) var result = await new SsdpHttpClient(_httpClient).SendCommandAsync(Properties.BaseUrl, service, command.Name, rendererCommands.BuildPost(command, service.ServiceType), true)
.ConfigureAwait(false); .ConfigureAwait(false);
if (result == null || result.Document == null) if (result == null || result.Document == null)
@ -586,7 +589,7 @@ namespace Emby.Dlna.PlayTo
return null; return null;
} }
var result = await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, avCommands.BuildPost(command, service.ServiceType), false) var result = await new SsdpHttpClient(_httpClient).SendCommandAsync(Properties.BaseUrl, service, command.Name, avCommands.BuildPost(command, service.ServiceType), false)
.ConfigureAwait(false); .ConfigureAwait(false);
if (result == null || result.Document == null) if (result == null || result.Document == null)
@ -624,7 +627,7 @@ namespace Emby.Dlna.PlayTo
var rendererCommands = await GetRenderingProtocolAsync(cancellationToken).ConfigureAwait(false); var rendererCommands = await GetRenderingProtocolAsync(cancellationToken).ConfigureAwait(false);
var result = await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, rendererCommands.BuildPost(command, service.ServiceType), false) var result = await new SsdpHttpClient(_httpClient).SendCommandAsync(Properties.BaseUrl, service, command.Name, rendererCommands.BuildPost(command, service.ServiceType), false)
.ConfigureAwait(false); .ConfigureAwait(false);
if (result == null || result.Document == null) if (result == null || result.Document == null)
@ -687,7 +690,7 @@ namespace Emby.Dlna.PlayTo
var rendererCommands = await GetRenderingProtocolAsync(cancellationToken).ConfigureAwait(false); var rendererCommands = await GetRenderingProtocolAsync(cancellationToken).ConfigureAwait(false);
var result = await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, rendererCommands.BuildPost(command, service.ServiceType), false) var result = await new SsdpHttpClient(_httpClient).SendCommandAsync(Properties.BaseUrl, service, command.Name, rendererCommands.BuildPost(command, service.ServiceType), false)
.ConfigureAwait(false); .ConfigureAwait(false);
if (result == null || result.Document == null) if (result == null || result.Document == null)
@ -868,7 +871,7 @@ namespace Emby.Dlna.PlayTo
string url = NormalizeUrl(Properties.BaseUrl, avService.ScpdUrl); string url = NormalizeUrl(Properties.BaseUrl, avService.ScpdUrl);
var httpClient = new SsdpHttpClient(_httpClient, _config); var httpClient = new SsdpHttpClient(_httpClient);
var document = await httpClient.GetDataAsync(url, cancellationToken).ConfigureAwait(false); var document = await httpClient.GetDataAsync(url, cancellationToken).ConfigureAwait(false);
@ -896,7 +899,7 @@ namespace Emby.Dlna.PlayTo
string url = NormalizeUrl(Properties.BaseUrl, avService.ScpdUrl); string url = NormalizeUrl(Properties.BaseUrl, avService.ScpdUrl);
var httpClient = new SsdpHttpClient(_httpClient, _config); var httpClient = new SsdpHttpClient(_httpClient);
_logger.LogDebug("Dlna Device.GetRenderingProtocolAsync"); _logger.LogDebug("Dlna Device.GetRenderingProtocolAsync");
var document = await httpClient.GetDataAsync(url, cancellationToken).ConfigureAwait(false); var document = await httpClient.GetDataAsync(url, cancellationToken).ConfigureAwait(false);
@ -931,7 +934,7 @@ namespace Emby.Dlna.PlayTo
public static async Task<Device> CreateuPnpDeviceAsync(Uri url, IHttpClient httpClient, IServerConfigurationManager config, ILogger logger, CancellationToken cancellationToken) public static async Task<Device> CreateuPnpDeviceAsync(Uri url, IHttpClient httpClient, IServerConfigurationManager config, ILogger logger, CancellationToken cancellationToken)
{ {
var ssdpHttpClient = new SsdpHttpClient(httpClient, config); var ssdpHttpClient = new SsdpHttpClient(httpClient);
var document = await ssdpHttpClient.GetDataAsync(url.ToString(), cancellationToken).ConfigureAwait(false); var document = await ssdpHttpClient.GetDataAsync(url.ToString(), cancellationToken).ConfigureAwait(false);

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System.Collections.Generic; using System.Collections.Generic;
using Emby.Dlna.Common; using Emby.Dlna.Common;
using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dlna;

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
@ -6,7 +9,6 @@ using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Emby.Dlna.Didl; using Emby.Dlna.Didl;
using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Dlna; using MediaBrowser.Controller.Dlna;
using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System; using System;
using System.Globalization; using System.Globalization;
using System.Linq; using System.Linq;
@ -21,7 +24,7 @@ using Microsoft.Extensions.Logging;
namespace Emby.Dlna.PlayTo namespace Emby.Dlna.PlayTo
{ {
class PlayToManager : IDisposable public class PlayToManager : IDisposable
{ {
private readonly ILogger _logger; private readonly ILogger _logger;
private readonly ISessionManager _sessionManager; private readonly ISessionManager _sessionManager;
@ -64,10 +67,10 @@ namespace Emby.Dlna.PlayTo
public void Start() public void Start()
{ {
_deviceDiscovery.DeviceDiscovered += _deviceDiscovery_DeviceDiscovered; _deviceDiscovery.DeviceDiscovered += OnDeviceDiscoveryDeviceDiscovered;
} }
async void _deviceDiscovery_DeviceDiscovered(object sender, GenericEventArgs<UpnpDeviceInfo> e) private async void OnDeviceDiscoveryDeviceDiscovered(object sender, GenericEventArgs<UpnpDeviceInfo> e)
{ {
if (_disposed) if (_disposed)
{ {
@ -231,7 +234,7 @@ namespace Emby.Dlna.PlayTo
public void Dispose() public void Dispose()
{ {
_deviceDiscovery.DeviceDiscovered -= _deviceDiscovery_DeviceDiscovered; _deviceDiscovery.DeviceDiscovered -= OnDeviceDiscoveryDeviceDiscovered;
try try
{ {

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System; using System;
namespace Emby.Dlna.PlayTo namespace Emby.Dlna.PlayTo

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System; using System;
namespace Emby.Dlna.PlayTo namespace Emby.Dlna.PlayTo

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System; using System;
namespace Emby.Dlna.PlayTo namespace Emby.Dlna.PlayTo

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dlna;
namespace Emby.Dlna.PlayTo namespace Emby.Dlna.PlayTo

@ -1,4 +1,6 @@
using System.Globalization; #pragma warning disable CS1591
#pragma warning disable SA1600
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;

@ -1,13 +1,16 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System; using System;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Net.Http;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Xml.Linq; using System.Xml.Linq;
using Emby.Dlna.Common; using Emby.Dlna.Common;
using MediaBrowser.Common.Net; using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
namespace Emby.Dlna.PlayTo namespace Emby.Dlna.PlayTo
{ {
@ -19,12 +22,10 @@ namespace Emby.Dlna.PlayTo
private readonly CultureInfo _usCulture = new CultureInfo("en-US"); private readonly CultureInfo _usCulture = new CultureInfo("en-US");
private readonly IHttpClient _httpClient; private readonly IHttpClient _httpClient;
private readonly IServerConfigurationManager _config;
public SsdpHttpClient(IHttpClient httpClient, IServerConfigurationManager config) public SsdpHttpClient(IHttpClient httpClient)
{ {
_httpClient = httpClient; _httpClient = httpClient;
_config = config;
} }
public async Task<XDocument> SendCommandAsync( public async Task<XDocument> SendCommandAsync(
@ -64,7 +65,9 @@ namespace Emby.Dlna.PlayTo
} }
if (!serviceUrl.StartsWith("/")) if (!serviceUrl.StartsWith("/"))
{
serviceUrl = "/" + serviceUrl; serviceUrl = "/" + serviceUrl;
}
return baseUrl + serviceUrl; return baseUrl + serviceUrl;
} }
@ -90,7 +93,7 @@ namespace Emby.Dlna.PlayTo
options.RequestHeaders["NT"] = "upnp:event"; options.RequestHeaders["NT"] = "upnp:event";
options.RequestHeaders["TIMEOUT"] = "Second-" + timeOut.ToString(_usCulture); options.RequestHeaders["TIMEOUT"] = "Second-" + timeOut.ToString(_usCulture);
using (await _httpClient.SendAsync(options, "SUBSCRIBE").ConfigureAwait(false)) using (await _httpClient.SendAsync(options, new HttpMethod("SUBSCRIBE")).ConfigureAwait(false))
{ {
} }
@ -110,7 +113,7 @@ namespace Emby.Dlna.PlayTo
options.RequestHeaders["FriendlyName.DLNA.ORG"] = FriendlyName; options.RequestHeaders["FriendlyName.DLNA.ORG"] = FriendlyName;
using (var response = await _httpClient.SendAsync(options, "GET").ConfigureAwait(false)) using (var response = await _httpClient.SendAsync(options, HttpMethod.Get).ConfigureAwait(false))
using (var stream = response.Content) using (var stream = response.Content)
using (var reader = new StreamReader(stream, Encoding.UTF8)) using (var reader = new StreamReader(stream, Encoding.UTF8))
{ {

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
namespace Emby.Dlna.PlayTo namespace Emby.Dlna.PlayTo
{ {
public enum TRANSPORTSTATE public enum TRANSPORTSTATE

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System; using System;
using System.Xml.Linq; using System.Xml.Linq;
using Emby.Dlna.Ssdp; using Emby.Dlna.Ssdp;

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System; using System;
namespace Emby.Dlna.PlayTo namespace Emby.Dlna.PlayTo

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System.Xml.Linq; using System.Xml.Linq;
namespace Emby.Dlna.PlayTo namespace Emby.Dlna.PlayTo

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System.Linq; using System.Linq;
using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dlna;

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dlna;
namespace Emby.Dlna.Profiles namespace Emby.Dlna.Profiles

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dlna;
namespace Emby.Dlna.Profiles namespace Emby.Dlna.Profiles

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dlna;
namespace Emby.Dlna.Profiles namespace Emby.Dlna.Profiles

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dlna;
namespace Emby.Dlna.Profiles namespace Emby.Dlna.Profiles

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dlna;
namespace Emby.Dlna.Profiles namespace Emby.Dlna.Profiles

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dlna;
namespace Emby.Dlna.Profiles namespace Emby.Dlna.Profiles

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dlna;
namespace Emby.Dlna.Profiles namespace Emby.Dlna.Profiles

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dlna;
namespace Emby.Dlna.Profiles namespace Emby.Dlna.Profiles

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dlna;
namespace Emby.Dlna.Profiles namespace Emby.Dlna.Profiles

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dlna;
namespace Emby.Dlna.Profiles namespace Emby.Dlna.Profiles

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dlna;
namespace Emby.Dlna.Profiles namespace Emby.Dlna.Profiles

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dlna;
namespace Emby.Dlna.Profiles namespace Emby.Dlna.Profiles

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dlna;
namespace Emby.Dlna.Profiles namespace Emby.Dlna.Profiles

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dlna;
namespace Emby.Dlna.Profiles namespace Emby.Dlna.Profiles

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dlna;
namespace Emby.Dlna.Profiles namespace Emby.Dlna.Profiles

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dlna;
namespace Emby.Dlna.Profiles namespace Emby.Dlna.Profiles

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dlna;
namespace Emby.Dlna.Profiles namespace Emby.Dlna.Profiles

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dlna;
namespace Emby.Dlna.Profiles namespace Emby.Dlna.Profiles

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dlna;
namespace Emby.Dlna.Profiles namespace Emby.Dlna.Profiles

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dlna;
namespace Emby.Dlna.Profiles namespace Emby.Dlna.Profiles

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dlna;
namespace Emby.Dlna.Profiles namespace Emby.Dlna.Profiles

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dlna;
namespace Emby.Dlna.Profiles namespace Emby.Dlna.Profiles

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dlna;
namespace Emby.Dlna.Profiles namespace Emby.Dlna.Profiles

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dlna;
namespace Emby.Dlna.Profiles namespace Emby.Dlna.Profiles

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dlna;
namespace Emby.Dlna.Profiles namespace Emby.Dlna.Profiles

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dlna;
namespace Emby.Dlna.Profiles namespace Emby.Dlna.Profiles

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
@ -5,7 +8,6 @@ using System.Linq;
using System.Text; using System.Text;
using Emby.Dlna.Common; using Emby.Dlna.Common;
using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Extensions;
namespace Emby.Dlna.Server namespace Emby.Dlna.Server
{ {

@ -1,8 +1,11 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks;
using System.Xml; using System.Xml;
using Emby.Dlna.Didl; using Emby.Dlna.Didl;
using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Configuration;
@ -15,44 +18,34 @@ namespace Emby.Dlna.Service
{ {
private const string NS_SOAPENV = "http://schemas.xmlsoap.org/soap/envelope/"; private const string NS_SOAPENV = "http://schemas.xmlsoap.org/soap/envelope/";
protected readonly IServerConfigurationManager Config; protected IServerConfigurationManager Config { get; }
protected readonly ILogger _logger; protected ILogger Logger { get; }
protected BaseControlHandler(IServerConfigurationManager config, ILogger logger) protected BaseControlHandler(IServerConfigurationManager config, ILogger logger)
{ {
Config = config; Config = config;
_logger = logger; Logger = logger;
} }
public ControlResponse ProcessControlRequest(ControlRequest request) public async Task<ControlResponse> ProcessControlRequestAsync(ControlRequest request)
{ {
try try
{ {
var enableDebugLogging = Config.GetDlnaConfiguration().EnableDebugLog; LogRequest(request);
if (enableDebugLogging)
{
LogRequest(request);
}
var response = ProcessControlRequestInternal(request);
if (enableDebugLogging)
{
LogResponse(response);
}
var response = await ProcessControlRequestInternalAsync(request).ConfigureAwait(false);
LogResponse(response);
return response; return response;
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogError(ex, "Error processing control request"); Logger.LogError(ex, "Error processing control request");
return new ControlErrorHandler().GetResponse(ex); return ControlErrorHandler.GetResponse(ex);
} }
} }
private ControlResponse ProcessControlRequestInternal(ControlRequest request) private async Task<ControlResponse> ProcessControlRequestInternalAsync(ControlRequest request)
{ {
ControlRequestInfo requestInfo = null; ControlRequestInfo requestInfo = null;
@ -63,18 +56,17 @@ namespace Emby.Dlna.Service
ValidationType = ValidationType.None, ValidationType = ValidationType.None,
CheckCharacters = false, CheckCharacters = false,
IgnoreProcessingInstructions = true, IgnoreProcessingInstructions = true,
IgnoreComments = true IgnoreComments = true,
Async = true
}; };
using (var reader = XmlReader.Create(streamReader, readerSettings)) using (var reader = XmlReader.Create(streamReader, readerSettings))
{ {
requestInfo = ParseRequest(reader); requestInfo = await ParseRequestAsync(reader).ConfigureAwait(false);
} }
} }
_logger.LogDebug("Received control request {0}", requestInfo.LocalName); Logger.LogDebug("Received control request {0}", requestInfo.LocalName);
var result = GetResult(requestInfo.LocalName, requestInfo.Headers);
var settings = new XmlWriterSettings var settings = new XmlWriterSettings
{ {
@ -93,12 +85,9 @@ namespace Emby.Dlna.Service
writer.WriteStartElement("SOAP-ENV", "Body", NS_SOAPENV); writer.WriteStartElement("SOAP-ENV", "Body", NS_SOAPENV);
writer.WriteStartElement("u", requestInfo.LocalName + "Response", requestInfo.NamespaceURI); writer.WriteStartElement("u", requestInfo.LocalName + "Response", requestInfo.NamespaceURI);
foreach (var i in result)
{ WriteResult(requestInfo.LocalName, requestInfo.Headers, writer);
writer.WriteStartElement(i.Key);
writer.WriteString(i.Value);
writer.WriteFullEndElement();
}
writer.WriteFullEndElement(); writer.WriteFullEndElement();
writer.WriteFullEndElement(); writer.WriteFullEndElement();
@ -106,7 +95,7 @@ namespace Emby.Dlna.Service
writer.WriteEndDocument(); writer.WriteEndDocument();
} }
var xml = builder.ToString().Replace("xmlns:m=", "xmlns:u="); var xml = builder.ToString().Replace("xmlns:m=", "xmlns:u=", StringComparison.Ordinal);
var controlResponse = new ControlResponse var controlResponse = new ControlResponse
{ {
@ -114,17 +103,15 @@ namespace Emby.Dlna.Service
IsSuccessful = true IsSuccessful = true
}; };
//logger.LogDebug(xml);
controlResponse.Headers.Add("EXT", string.Empty); controlResponse.Headers.Add("EXT", string.Empty);
return controlResponse; return controlResponse;
} }
private ControlRequestInfo ParseRequest(XmlReader reader) private async Task<ControlRequestInfo> ParseRequestAsync(XmlReader reader)
{ {
reader.MoveToContent(); await reader.MoveToContentAsync().ConfigureAwait(false);
reader.Read(); await reader.ReadAsync().ConfigureAwait(false);
// Loop through each element // Loop through each element
while (!reader.EOF && reader.ReadState == ReadState.Interactive) while (!reader.EOF && reader.ReadState == ReadState.Interactive)
@ -139,37 +126,38 @@ namespace Emby.Dlna.Service
{ {
using (var subReader = reader.ReadSubtree()) using (var subReader = reader.ReadSubtree())
{ {
return ParseBodyTag(subReader); return await ParseBodyTagAsync(subReader).ConfigureAwait(false);
} }
} }
else else
{ {
reader.Read(); await reader.ReadAsync().ConfigureAwait(false);
} }
break; break;
} }
default: default:
{ {
reader.Skip(); await reader.SkipAsync().ConfigureAwait(false);
break; break;
} }
} }
} }
else else
{ {
reader.Read(); await reader.ReadAsync().ConfigureAwait(false);
} }
} }
return new ControlRequestInfo(); return new ControlRequestInfo();
} }
private ControlRequestInfo ParseBodyTag(XmlReader reader) private async Task<ControlRequestInfo> ParseBodyTagAsync(XmlReader reader)
{ {
var result = new ControlRequestInfo(); var result = new ControlRequestInfo();
reader.MoveToContent(); await reader.MoveToContentAsync().ConfigureAwait(false);
reader.Read(); await reader.ReadAsync().ConfigureAwait(false);
// Loop through each element // Loop through each element
while (!reader.EOF && reader.ReadState == ReadState.Interactive) while (!reader.EOF && reader.ReadState == ReadState.Interactive)
@ -183,28 +171,28 @@ namespace Emby.Dlna.Service
{ {
using (var subReader = reader.ReadSubtree()) using (var subReader = reader.ReadSubtree())
{ {
ParseFirstBodyChild(subReader, result.Headers); await ParseFirstBodyChildAsync(subReader, result.Headers).ConfigureAwait(false);
return result; return result;
} }
} }
else else
{ {
reader.Read(); await reader.ReadAsync().ConfigureAwait(false);
} }
} }
else else
{ {
reader.Read(); await reader.ReadAsync().ConfigureAwait(false);
} }
} }
return result; return result;
} }
private void ParseFirstBodyChild(XmlReader reader, IDictionary<string, string> headers) private async Task ParseFirstBodyChildAsync(XmlReader reader, IDictionary<string, string> headers)
{ {
reader.MoveToContent(); await reader.MoveToContentAsync().ConfigureAwait(false);
reader.Read(); await reader.ReadAsync().ConfigureAwait(false);
// Loop through each element // Loop through each element
while (!reader.EOF && reader.ReadState == ReadState.Interactive) while (!reader.EOF && reader.ReadState == ReadState.Interactive)
@ -212,23 +200,23 @@ namespace Emby.Dlna.Service
if (reader.NodeType == XmlNodeType.Element) if (reader.NodeType == XmlNodeType.Element)
{ {
// TODO: Should we be doing this here, or should it be handled earlier when decoding the request? // TODO: Should we be doing this here, or should it be handled earlier when decoding the request?
headers[reader.LocalName.RemoveDiacritics()] = reader.ReadElementContentAsString(); headers[reader.LocalName.RemoveDiacritics()] = await reader.ReadElementContentAsStringAsync().ConfigureAwait(false);
} }
else else
{ {
reader.Read(); await reader.ReadAsync().ConfigureAwait(false);
} }
} }
} }
private class ControlRequestInfo private class ControlRequestInfo
{ {
public string LocalName; public string LocalName { get; set; }
public string NamespaceURI; public string NamespaceURI { get; set; }
public IDictionary<string, string> Headers = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); public Dictionary<string, string> Headers { get; } = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
} }
protected abstract IEnumerable<KeyValuePair<string, string>> GetResult(string methodName, IDictionary<string, string> methodParams); protected abstract void WriteResult(string methodName, IDictionary<string, string> methodParams, XmlWriter xmlWriter);
private void LogRequest(ControlRequest request) private void LogRequest(ControlRequest request)
{ {
@ -237,10 +225,7 @@ namespace Emby.Dlna.Service
return; return;
} }
var originalHeaders = request.Headers; Logger.LogDebug("Control request. Headers: {@Headers}", request.Headers);
var headers = string.Join(", ", originalHeaders.Select(i => string.Format("{0}={1}", i.Key, i.Value)).ToArray());
_logger.LogDebug("Control request. Headers: {0}", headers);
} }
private void LogResponse(ControlResponse response) private void LogResponse(ControlResponse response)
@ -250,11 +235,7 @@ namespace Emby.Dlna.Service
return; return;
} }
var originalHeaders = response.Headers; Logger.LogDebug("Control response. Headers: {@Headers}\n{Xml}", response.Headers, response.Xml);
var headers = string.Join(", ", originalHeaders.Select(i => string.Format("{0}={1}", i.Key, i.Value)).ToArray());
//builder.Append(response.Xml);
_logger.LogDebug("Control response. Headers: {0}", headers);
} }
} }
} }

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using Emby.Dlna.Eventing; using Emby.Dlna.Eventing;
using MediaBrowser.Common.Net; using MediaBrowser.Common.Net;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System; using System;
using System.IO; using System.IO;
using System.Text; using System.Text;
@ -6,11 +9,11 @@ using Emby.Dlna.Didl;
namespace Emby.Dlna.Service namespace Emby.Dlna.Service
{ {
public class ControlErrorHandler public static class ControlErrorHandler
{ {
private const string NS_SOAPENV = "http://schemas.xmlsoap.org/soap/envelope/"; private const string NS_SOAPENV = "http://schemas.xmlsoap.org/soap/envelope/";
public ControlResponse GetResponse(Exception ex) public static ControlResponse GetResponse(Exception ex)
{ {
var settings = new XmlWriterSettings var settings = new XmlWriterSettings
{ {

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Text;
using Emby.Dlna.Common; using Emby.Dlna.Common;

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
@ -9,16 +12,16 @@ using Rssdp.Infrastructure;
namespace Emby.Dlna.Ssdp namespace Emby.Dlna.Ssdp
{ {
public class DeviceDiscovery : IDeviceDiscovery public sealed class DeviceDiscovery : IDeviceDiscovery, IDisposable
{ {
private bool _disposed; private readonly object _syncLock = new object();
private readonly IServerConfigurationManager _config; private readonly IServerConfigurationManager _config;
private event EventHandler<GenericEventArgs<UpnpDeviceInfo>> DeviceDiscoveredInternal;
private int _listenerCount; private int _listenerCount;
private object _syncLock = new object(); private bool _disposed;
private event EventHandler<GenericEventArgs<UpnpDeviceInfo>> DeviceDiscoveredInternal;
/// <inheritdoc /> /// <inheritdoc />
public event EventHandler<GenericEventArgs<UpnpDeviceInfo>> DeviceDiscovered public event EventHandler<GenericEventArgs<UpnpDeviceInfo>> DeviceDiscovered
@ -33,6 +36,7 @@ namespace Emby.Dlna.Ssdp
StartInternal(); StartInternal();
} }
remove remove
{ {
lock (_syncLock) lock (_syncLock)
@ -130,6 +134,7 @@ namespace Emby.Dlna.Ssdp
DeviceLeft?.Invoke(this, args); DeviceLeft?.Invoke(this, args);
} }
/// <inheritdoc />
public void Dispose() public void Dispose()
{ {
if (!_disposed) if (!_disposed)

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System.Xml.Linq; using System.Xml.Linq;
namespace Emby.Dlna.Ssdp namespace Emby.Dlna.Ssdp

@ -104,7 +104,7 @@ namespace Emby.Drawing
{ {
var file = await ProcessImage(options).ConfigureAwait(false); var file = await ProcessImage(options).ConfigureAwait(false);
using (var fileStream = _fileSystem.GetFileStream(file.Item1, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read, true)) using (var fileStream = new FileStream(file.Item1, FileMode.Open, FileAccess.Read, FileShare.Read, IODefaults.FileStreamBufferSize, true))
{ {
await fileStream.CopyToAsync(toStream).ConfigureAwait(false); await fileStream.CopyToAsync(toStream).ConfigureAwait(false);
} }

@ -19,15 +19,13 @@ namespace Emby.Naming.Audio
_options = options; _options = options;
} }
public MultiPartResult ParseMultiPart(string path) public bool IsMultiPart(string path)
{ {
var result = new MultiPartResult();
var filename = Path.GetFileName(path); var filename = Path.GetFileName(path);
if (string.IsNullOrEmpty(filename)) if (string.IsNullOrEmpty(filename))
{ {
return result; return false;
} }
// TODO: Move this logic into options object // TODO: Move this logic into options object
@ -57,12 +55,11 @@ namespace Emby.Naming.Audio
if (int.TryParse(tmp, NumberStyles.Integer, CultureInfo.InvariantCulture, out _)) if (int.TryParse(tmp, NumberStyles.Integer, CultureInfo.InvariantCulture, out _))
{ {
result.IsMultiPart = true; return true;
break;
} }
} }
return result; return false;
} }
} }
} }

@ -1,26 +0,0 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
namespace Emby.Naming.Audio
{
public class MultiPartResult
{
/// <summary>
/// Gets or sets the name.
/// </summary>
/// <value>The name.</value>
public string Name { get; set; }
/// <summary>
/// Gets or sets the part.
/// </summary>
/// <value>The part.</value>
public string Part { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this instance is multi part.
/// </summary>
/// <value><c>true</c> if this instance is multi part; otherwise, <c>false</c>.</value>
public bool IsMultiPart { get; set; }
}
}

@ -32,7 +32,7 @@ namespace Emby.Naming.AudioBook
public int? ChapterNumber { get; set; } public int? ChapterNumber { get; set; }
/// <summary> /// <summary>
/// Gets or sets the type. /// Gets or sets a value indicating whether this instance is a directory.
/// </summary> /// </summary>
/// <value>The type.</value> /// <value>The type.</value>
public bool IsDirectory { get; set; } public bool IsDirectory { get; set; }

@ -39,9 +39,7 @@ namespace Emby.Naming.AudioBook
var stackResult = new StackResolver(_options) var stackResult = new StackResolver(_options)
.ResolveAudioBooks(metadata); .ResolveAudioBooks(metadata);
var list = new List<AudioBookInfo>(); foreach (var stack in stackResult)
foreach (var stack in stackResult.Stacks)
{ {
var stackFiles = stack.Files.Select(i => audioBookResolver.Resolve(i, stack.IsDirectoryStack)).ToList(); var stackFiles = stack.Files.Select(i => audioBookResolver.Resolve(i, stack.IsDirectoryStack)).ToList();
stackFiles.Sort(); stackFiles.Sort();
@ -50,20 +48,9 @@ namespace Emby.Naming.AudioBook
Files = stackFiles, Files = stackFiles,
Name = stack.Name Name = stack.Name
}; };
list.Add(info);
}
// Whatever files are left, just add them
/*list.AddRange(remainingFiles.Select(i => new AudioBookInfo
{
Files = new List<AudioBookFileInfo> { i },
Name = i.,
Year = i.Year
}));*/
var orderedList = list.OrderBy(i => i.Name);
return orderedList; yield return info;
}
} }
} }
} }

@ -11,6 +11,24 @@ namespace Emby.Naming.Common
private string _expression; private string _expression;
private Regex _regex; private Regex _regex;
public EpisodeExpression(string expression, bool byDate)
{
Expression = expression;
IsByDate = byDate;
DateTimeFormats = Array.Empty<string>();
SupportsAbsoluteEpisodeNumbers = true;
}
public EpisodeExpression(string expression)
: this(expression, false)
{
}
public EpisodeExpression()
: this(null)
{
}
public string Expression public string Expression
{ {
get => _expression; get => _expression;
@ -32,23 +50,5 @@ namespace Emby.Naming.Common
public string[] DateTimeFormats { get; set; } public string[] DateTimeFormats { get; set; }
public Regex Regex => _regex ?? (_regex = new Regex(Expression, RegexOptions.IgnoreCase | RegexOptions.Compiled)); public Regex Regex => _regex ?? (_regex = new Regex(Expression, RegexOptions.IgnoreCase | RegexOptions.Compiled));
public EpisodeExpression(string expression, bool byDate)
{
Expression = expression;
IsByDate = byDate;
DateTimeFormats = Array.Empty<string>();
SupportsAbsoluteEpisodeNumbers = true;
}
public EpisodeExpression(string expression)
: this(expression, false)
{
}
public EpisodeExpression()
: this(null)
{
}
} }
} }

@ -11,46 +11,6 @@ namespace Emby.Naming.Common
{ {
public class NamingOptions public class NamingOptions
{ {
public string[] AudioFileExtensions { get; set; }
public string[] AlbumStackingPrefixes { get; set; }
public string[] SubtitleFileExtensions { get; set; }
public char[] SubtitleFlagDelimiters { get; set; }
public string[] SubtitleForcedFlags { get; set; }
public string[] SubtitleDefaultFlags { get; set; }
public EpisodeExpression[] EpisodeExpressions { get; set; }
public string[] EpisodeWithoutSeasonExpressions { get; set; }
public string[] EpisodeMultiPartExpressions { get; set; }
public string[] VideoFileExtensions { get; set; }
public string[] StubFileExtensions { get; set; }
public string[] AudioBookPartsExpressions { get; set; }
public StubTypeRule[] StubTypes { get; set; }
public char[] VideoFlagDelimiters { get; set; }
public Format3DRule[] Format3DRules { get; set; }
public string[] VideoFileStackingExpressions { get; set; }
public string[] CleanDateTimes { get; set; }
public string[] CleanStrings { get; set; }
public EpisodeExpression[] MultipleEpisodeExpressions { get; set; }
public ExtraRule[] VideoExtraRules { get; set; }
public NamingOptions() public NamingOptions()
{ {
VideoFileExtensions = new[] VideoFileExtensions = new[]
@ -681,11 +641,54 @@ namespace Emby.Naming.Common
Compile(); Compile();
} }
public string[] AudioFileExtensions { get; set; }
public string[] AlbumStackingPrefixes { get; set; }
public string[] SubtitleFileExtensions { get; set; }
public char[] SubtitleFlagDelimiters { get; set; }
public string[] SubtitleForcedFlags { get; set; }
public string[] SubtitleDefaultFlags { get; set; }
public EpisodeExpression[] EpisodeExpressions { get; set; }
public string[] EpisodeWithoutSeasonExpressions { get; set; }
public string[] EpisodeMultiPartExpressions { get; set; }
public string[] VideoFileExtensions { get; set; }
public string[] StubFileExtensions { get; set; }
public string[] AudioBookPartsExpressions { get; set; }
public StubTypeRule[] StubTypes { get; set; }
public char[] VideoFlagDelimiters { get; set; }
public Format3DRule[] Format3DRules { get; set; }
public string[] VideoFileStackingExpressions { get; set; }
public string[] CleanDateTimes { get; set; }
public string[] CleanStrings { get; set; }
public EpisodeExpression[] MultipleEpisodeExpressions { get; set; }
public ExtraRule[] VideoExtraRules { get; set; }
public Regex[] VideoFileStackingRegexes { get; private set; } public Regex[] VideoFileStackingRegexes { get; private set; }
public Regex[] CleanDateTimeRegexes { get; private set; } public Regex[] CleanDateTimeRegexes { get; private set; }
public Regex[] CleanStringRegexes { get; private set; } public Regex[] CleanStringRegexes { get; private set; }
public Regex[] EpisodeWithoutSeasonRegexes { get; private set; } public Regex[] EpisodeWithoutSeasonRegexes { get; private set; }
public Regex[] EpisodeMultiPartRegexes { get; private set; } public Regex[] EpisodeMultiPartRegexes { get; private set; }
public void Compile() public void Compile()

@ -4,9 +4,6 @@
<TargetFramework>netstandard2.1</TargetFramework> <TargetFramework>netstandard2.1</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo> <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<GenerateDocumentationFile>true</GenerateDocumentationFile> <GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<TreatWarningsAsErrors>true</TreatWarningsAsErrors> <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup> </PropertyGroup>
@ -27,7 +24,7 @@
<!-- Code Analyzers--> <!-- Code Analyzers-->
<ItemGroup Condition=" '$(Configuration)' == 'Debug' "> <ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.8" PrivateAssets="All" /> <!-- TODO: <PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.8" PrivateAssets="All" /> -->
<PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" /> <PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="All" /> <PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="All" />
<PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" /> <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" />

@ -1,5 +1,6 @@
#pragma warning disable CS1591 #pragma warning disable CS1591
#pragma warning disable SA1600 #pragma warning disable SA1600
#nullable enable
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@ -28,7 +29,7 @@ namespace Emby.Naming.TV
path += ".mp4"; path += ".mp4";
} }
EpisodePathParserResult result = null; EpisodePathParserResult? result = null;
foreach (var expression in _options.EpisodeExpressions) foreach (var expression in _options.EpisodeExpressions)
{ {
@ -136,7 +137,7 @@ namespace Emby.Naming.TV
// It avoids erroneous parsing of something like "series-s09e14-1080p.mkv" as a multi-episode from E14 to E108 // It avoids erroneous parsing of something like "series-s09e14-1080p.mkv" as a multi-episode from E14 to E108
int nextIndex = endingNumberGroup.Index + endingNumberGroup.Length; int nextIndex = endingNumberGroup.Index + endingNumberGroup.Length;
if (nextIndex >= name.Length if (nextIndex >= name.Length
|| "0123456789iIpP".IndexOf(name[nextIndex]) == -1) || !"0123456789iIpP".Contains(name[nextIndex], StringComparison.Ordinal))
{ {
if (int.TryParse(endingNumberGroup.Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out num)) if (int.TryParse(endingNumberGroup.Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out num))
{ {

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

Loading…
Cancel
Save