diff --git a/src/Jellyfin.Plugin.Dlna/Api/DlnaServerController.cs b/src/Jellyfin.Plugin.Dlna/Api/DlnaServerController.cs index a322d7c..e814d37 100644 --- a/src/Jellyfin.Plugin.Dlna/Api/DlnaServerController.cs +++ b/src/Jellyfin.Plugin.Dlna/Api/DlnaServerController.cs @@ -4,6 +4,7 @@ using System.IO; using System.Net.Mime; using System.Threading.Tasks; +using Jellyfin.Extensions; using Jellyfin.Plugin.Dlna.Model; using MediaBrowser.Common.Api; using MediaBrowser.Model.Net; @@ -21,6 +22,8 @@ namespace Jellyfin.Plugin.Dlna.Api; [Authorize(Policy = Policies.AnonymousLanAccessPolicy)] public class DlnaServerController : ControllerBase { + private static readonly string[] _relativePathUserAgents = { "Bigscreen" }; + private readonly IDlnaManager _dlnaManager; private readonly IContentDirectory _contentDirectory; private readonly IConnectionManager _connectionManager; @@ -59,7 +62,15 @@ public DlnaServerController( [Produces(MediaTypeNames.Text.Xml)] public ActionResult GetDescriptionXml([FromRoute, Required] string serverId) { - var url = GetAbsoluteUri(); + var useRelativePath = false; + string? userAgent = Request.Headers.UserAgent; + if (userAgent is not null) + { + userAgent = userAgent.Substring(0, userAgent.IndexOf('/')); + useRelativePath = _relativePathUserAgents.Contains(userAgent, StringComparison.Ordinal); + } + + var url = useRelativePath ? GetRelativePath() : GetAbsoluteUri(); var serverAddress = url.Substring(0, url.IndexOf("/dlna/", StringComparison.OrdinalIgnoreCase)); var xml = _dlnaManager.GetServerDescriptionXml(Request.Headers, serverId, serverAddress); return Ok(xml); @@ -279,6 +290,11 @@ private string GetAbsoluteUri() return $"{Request.Scheme}://{Request.Host}{Request.PathBase}{Request.Path}"; } + private string GetRelativePath() + { + return $"{Request.PathBase}{Request.Path}"; + } + private Task ProcessControlRequestInternalAsync(string id, Stream requestStream, IUpnpService service) { return service.ProcessControlRequestAsync(new ControlRequest(Request.Headers) diff --git a/src/Jellyfin.Plugin.Dlna/ContentDirectory/ControlHandler.cs b/src/Jellyfin.Plugin.Dlna/ContentDirectory/ControlHandler.cs index b300231..53eddfa 100644 --- a/src/Jellyfin.Plugin.Dlna/ContentDirectory/ControlHandler.cs +++ b/src/Jellyfin.Plugin.Dlna/ContentDirectory/ControlHandler.cs @@ -681,11 +681,7 @@ private QueryResult GetMusicFolders(BaseItem item, User user, StubTy new(item, StubType.FavoriteSongs) }; - if (limit < serverItems.Length) - { - serverItems = serverItems[..limit.Value]; - } - + serverItems = GetTrimmedServerItemsArray(serverItems, startIndex, limit); return new QueryResult( startIndex, serverItems.Length, @@ -737,11 +733,7 @@ private QueryResult GetMovieFolders(BaseItem item, User user, StubTy new(item, StubType.Genres) }; - if (limit < array.Length) - { - array = array[..limit.Value]; - } - + array = GetTrimmedServerItemsArray(array, startIndex, limit); return new QueryResult( startIndex, array.Length, @@ -821,11 +813,7 @@ private QueryResult GetTvFolders(BaseItem item, User user, StubType? new(item, StubType.Genres) }; - if (limit < serverItems.Length) - { - serverItems = serverItems[..limit.Value]; - } - + serverItems = GetTrimmedServerItemsArray(serverItems, startIndex, limit); return new QueryResult( startIndex, serverItems.Length, @@ -1023,18 +1011,34 @@ private QueryResult GetLatest(BaseItem parent, InternalItemsQuery qu { query.OrderBy = Array.Empty<(ItemSortBy, SortOrder)>(); + int limit; + + if (query.StartIndex > 0) + { + limit = (query.Limit <= 0) ? int.MaxValue : (query.StartIndex.Value + (query.Limit ?? 50)); + } + else + { + limit = query.Limit ?? 50; + } + var items = _userViewManager.GetLatestItems( new LatestItemsQuery { // User cannot be null here as the caller has set it User = query.User!, - Limit = query.Limit ?? 50, + Limit = limit, IncludeItemTypes = new[] { itemType }, ParentId = parent?.Id ?? Guid.Empty, GroupItems = true }, query.DtoOptions).Select(i => i.Item1 ?? i.Item2.FirstOrDefault()).Where(i => i is not null).ToArray(); + if (query.StartIndex > 0) + { + items = (items.Length <= query.StartIndex) ? Array.Empty() : items[query.StartIndex.Value..]; + } + return ToResult(query.StartIndex, items); } @@ -1243,4 +1247,31 @@ private ServerItem ParseItemId(string id) return new ServerItem(_libraryManager.GetUserRootFolder(), null); } + + /// + /// Discards elements before startIndex and elements after startIndex+limit from an array of . + /// + /// An array of . + /// The start index. + /// The maximum number to return. + /// The corresponding trimmed array of + private static ServerItem[] GetTrimmedServerItemsArray(ServerItem[] serverItems, int? startIndex, int? limit) + { + if (startIndex >= serverItems.Length) + { + return Array.Empty(); + } + + if (startIndex > 0) + { + serverItems = serverItems[startIndex.Value..]; + } + + if (limit < serverItems.Length) + { + serverItems = serverItems[..limit.Value]; + } + + return serverItems; + } } diff --git a/src/Jellyfin.Plugin.Dlna/Server/DescriptionXmlBuilder.cs b/src/Jellyfin.Plugin.Dlna/Server/DescriptionXmlBuilder.cs index 536c3e0..24e2ff8 100644 --- a/src/Jellyfin.Plugin.Dlna/Server/DescriptionXmlBuilder.cs +++ b/src/Jellyfin.Plugin.Dlna/Server/DescriptionXmlBuilder.cs @@ -23,7 +23,6 @@ public class DescriptionXmlBuilder public DescriptionXmlBuilder(DlnaDeviceProfile profile, string serverUdn, string serverAddress, string serverName, string serverId) { ArgumentException.ThrowIfNullOrEmpty(serverUdn); - ArgumentException.ThrowIfNullOrEmpty(serverAddress); _profile = profile; _serverUdn = serverUdn;