mirror of
				https://github.com/jellyfin/jellyfin.git
				synced 2025-11-04 03:27:21 -05:00 
			
		
		
		
	Merge remote-tracking branch 'upstream/master' into simplify-https-config
This commit is contained in:
		
						commit
						5bc6c08a0a
					
				@ -45,15 +45,20 @@ RUN apt-get update \
 | 
				
			|||||||
   ca-certificates \
 | 
					   ca-certificates \
 | 
				
			||||||
   vainfo \
 | 
					   vainfo \
 | 
				
			||||||
   i965-va-driver \
 | 
					   i965-va-driver \
 | 
				
			||||||
 | 
					   locales \
 | 
				
			||||||
 && apt-get clean autoclean -y\
 | 
					 && apt-get clean autoclean -y\
 | 
				
			||||||
 && apt-get autoremove -y\
 | 
					 && apt-get autoremove -y\
 | 
				
			||||||
 && 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 \
 | 
				
			||||||
 && ln -s /opt/ffmpeg/bin/ffmpeg /usr/local/bin \
 | 
					 && ln -s /opt/ffmpeg/bin/ffmpeg /usr/local/bin \
 | 
				
			||||||
 && ln -s /opt/ffmpeg/bin/ffprobe /usr/local/bin
 | 
					 && ln -s /opt/ffmpeg/bin/ffprobe /usr/local/bin \
 | 
				
			||||||
 | 
					 && sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen && locale-gen
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1
 | 
					ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1
 | 
				
			||||||
 | 
					ENV LC_ALL en_US.UTF-8
 | 
				
			||||||
 | 
					ENV LANG en_US.UTF-8
 | 
				
			||||||
 | 
					ENV LANGUAGE en_US:en
 | 
				
			||||||
 | 
					
 | 
				
			||||||
EXPOSE 8096
 | 
					EXPOSE 8096
 | 
				
			||||||
VOLUME /cache /config /media
 | 
					VOLUME /cache /config /media
 | 
				
			||||||
 | 
				
			|||||||
@ -52,16 +52,22 @@ RUN apt-get update \
 | 
				
			|||||||
 libraspberrypi0 \
 | 
					 libraspberrypi0 \
 | 
				
			||||||
 vainfo \
 | 
					 vainfo \
 | 
				
			||||||
 libva2 \
 | 
					 libva2 \
 | 
				
			||||||
 | 
					 locales \
 | 
				
			||||||
 && apt-get remove curl gnupg -y \
 | 
					 && apt-get remove curl gnupg -y \
 | 
				
			||||||
 && apt-get clean autoclean -y \
 | 
					 && apt-get clean autoclean -y \
 | 
				
			||||||
 && apt-get autoremove -y \
 | 
					 && apt-get autoremove -y \
 | 
				
			||||||
 && 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 \
 | 
				
			||||||
 | 
					 && sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen && locale-gen
 | 
				
			||||||
 | 
					
 | 
				
			||||||
COPY --from=builder /jellyfin /jellyfin
 | 
					COPY --from=builder /jellyfin /jellyfin
 | 
				
			||||||
COPY --from=web-builder /dist /jellyfin/jellyfin-web
 | 
					COPY --from=web-builder /dist /jellyfin/jellyfin-web
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1
 | 
					ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1
 | 
				
			||||||
 | 
					ENV LC_ALL en_US.UTF-8
 | 
				
			||||||
 | 
					ENV LANG en_US.UTF-8
 | 
				
			||||||
 | 
					ENV LANGUAGE en_US:en
 | 
				
			||||||
 | 
					
 | 
				
			||||||
EXPOSE 8096
 | 
					EXPOSE 8096
 | 
				
			||||||
VOLUME /cache /config /media
 | 
					VOLUME /cache /config /media
 | 
				
			||||||
 | 
				
			|||||||
@ -42,15 +42,21 @@ RUN apt-get update && apt-get install --no-install-recommends --no-install-sugge
 | 
				
			|||||||
 libfreetype6 \
 | 
					 libfreetype6 \
 | 
				
			||||||
 libomxil-bellagio0 \
 | 
					 libomxil-bellagio0 \
 | 
				
			||||||
 libomxil-bellagio-bin \
 | 
					 libomxil-bellagio-bin \
 | 
				
			||||||
 | 
					 locales \
 | 
				
			||||||
 && apt-get clean autoclean -y \
 | 
					 && apt-get clean autoclean -y \
 | 
				
			||||||
 && apt-get autoremove -y \
 | 
					 && apt-get autoremove -y \
 | 
				
			||||||
 && 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 \
 | 
				
			||||||
 | 
					 && sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen && locale-gen
 | 
				
			||||||
 | 
					
 | 
				
			||||||
COPY --from=builder /jellyfin /jellyfin
 | 
					COPY --from=builder /jellyfin /jellyfin
 | 
				
			||||||
COPY --from=web-builder /dist /jellyfin/jellyfin-web
 | 
					COPY --from=web-builder /dist /jellyfin/jellyfin-web
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1
 | 
					ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1
 | 
				
			||||||
 | 
					ENV LC_ALL en_US.UTF-8
 | 
				
			||||||
 | 
					ENV LANG en_US.UTF-8
 | 
				
			||||||
 | 
					ENV LANGUAGE en_US:en
 | 
				
			||||||
 | 
					
 | 
				
			||||||
EXPOSE 8096
 | 
					EXPOSE 8096
 | 
				
			||||||
VOLUME /cache /config /media
 | 
					VOLUME /cache /config /media
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,7 @@
 | 
				
			|||||||
#pragma warning disable CS1591
 | 
					#pragma warning disable CS1591
 | 
				
			||||||
 | 
					
 | 
				
			||||||
using System;
 | 
					using System;
 | 
				
			||||||
 | 
					using System.Diagnostics.CodeAnalysis;
 | 
				
			||||||
using System.IO;
 | 
					using System.IO;
 | 
				
			||||||
using System.Text;
 | 
					using System.Text;
 | 
				
			||||||
using System.Threading.Tasks;
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
@ -151,6 +152,7 @@ namespace Emby.Dlna.Api
 | 
				
			|||||||
            return _resultFactory.GetStaticResult(Request, cacheKey, null, cacheLength, XMLContentType, () => Task.FromResult<Stream>(new MemoryStream(bytes)));
 | 
					            return _resultFactory.GetStaticResult(Request, cacheKey, null, cacheLength, XMLContentType, () => Task.FromResult<Stream>(new MemoryStream(bytes)));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request", Justification = "Required for ServiceStack")]
 | 
				
			||||||
        public object Get(GetContentDirectory request)
 | 
					        public object Get(GetContentDirectory request)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var xml = ContentDirectory.GetServiceXml();
 | 
					            var xml = ContentDirectory.GetServiceXml();
 | 
				
			||||||
@ -158,6 +160,7 @@ namespace Emby.Dlna.Api
 | 
				
			|||||||
            return _resultFactory.GetResult(Request, xml, XMLContentType);
 | 
					            return _resultFactory.GetResult(Request, xml, XMLContentType);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request", Justification = "Required for ServiceStack")]
 | 
				
			||||||
        public object Get(GetMediaReceiverRegistrar request)
 | 
					        public object Get(GetMediaReceiverRegistrar request)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var xml = MediaReceiverRegistrar.GetServiceXml();
 | 
					            var xml = MediaReceiverRegistrar.GetServiceXml();
 | 
				
			||||||
@ -165,6 +168,7 @@ namespace Emby.Dlna.Api
 | 
				
			|||||||
            return _resultFactory.GetResult(Request, xml, XMLContentType);
 | 
					            return _resultFactory.GetResult(Request, xml, XMLContentType);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request", Justification = "Required for ServiceStack")]
 | 
				
			||||||
        public object Get(GetConnnectionManager request)
 | 
					        public object Get(GetConnnectionManager request)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var xml = ConnectionManager.GetServiceXml();
 | 
					            var xml = ConnectionManager.GetServiceXml();
 | 
				
			||||||
@ -313,31 +317,37 @@ namespace Emby.Dlna.Api
 | 
				
			|||||||
            return _resultFactory.GetStaticResult(Request, cacheKey, null, cacheLength, contentType, () => Task.FromResult(_dlnaManager.GetIcon(request.Filename).Stream));
 | 
					            return _resultFactory.GetStaticResult(Request, cacheKey, null, cacheLength, contentType, () => Task.FromResult(_dlnaManager.GetIcon(request.Filename).Stream));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request", Justification = "Required for ServiceStack")]
 | 
				
			||||||
        public object Subscribe(ProcessContentDirectoryEventRequest request)
 | 
					        public object Subscribe(ProcessContentDirectoryEventRequest request)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            return ProcessEventRequest(ContentDirectory);
 | 
					            return ProcessEventRequest(ContentDirectory);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request", Justification = "Required for ServiceStack")]
 | 
				
			||||||
        public object Subscribe(ProcessConnectionManagerEventRequest request)
 | 
					        public object Subscribe(ProcessConnectionManagerEventRequest request)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            return ProcessEventRequest(ConnectionManager);
 | 
					            return ProcessEventRequest(ConnectionManager);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request", Justification = "Required for ServiceStack")]
 | 
				
			||||||
        public object Subscribe(ProcessMediaReceiverRegistrarEventRequest request)
 | 
					        public object Subscribe(ProcessMediaReceiverRegistrarEventRequest request)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            return ProcessEventRequest(MediaReceiverRegistrar);
 | 
					            return ProcessEventRequest(MediaReceiverRegistrar);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request", Justification = "Required for ServiceStack")]
 | 
				
			||||||
        public object Unsubscribe(ProcessContentDirectoryEventRequest request)
 | 
					        public object Unsubscribe(ProcessContentDirectoryEventRequest request)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            return ProcessEventRequest(ContentDirectory);
 | 
					            return ProcessEventRequest(ContentDirectory);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request", Justification = "Required for ServiceStack")]
 | 
				
			||||||
        public object Unsubscribe(ProcessConnectionManagerEventRequest request)
 | 
					        public object Unsubscribe(ProcessConnectionManagerEventRequest request)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            return ProcessEventRequest(ConnectionManager);
 | 
					            return ProcessEventRequest(ConnectionManager);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request", Justification = "Required for ServiceStack")]
 | 
				
			||||||
        public object Unsubscribe(ProcessMediaReceiverRegistrarEventRequest request)
 | 
					        public object Unsubscribe(ProcessMediaReceiverRegistrarEventRequest request)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            return ProcessEventRequest(MediaReceiverRegistrar);
 | 
					            return ProcessEventRequest(MediaReceiverRegistrar);
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,6 @@
 | 
				
			|||||||
#pragma warning disable CS1591
 | 
					#pragma warning disable CS1591
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					using System.Diagnostics.CodeAnalysis;
 | 
				
			||||||
using System.Linq;
 | 
					using System.Linq;
 | 
				
			||||||
using MediaBrowser.Controller.Dlna;
 | 
					using MediaBrowser.Controller.Dlna;
 | 
				
			||||||
using MediaBrowser.Controller.Net;
 | 
					using MediaBrowser.Controller.Net;
 | 
				
			||||||
@ -52,6 +53,7 @@ namespace Emby.Dlna.Api
 | 
				
			|||||||
            _dlnaManager = dlnaManager;
 | 
					            _dlnaManager = dlnaManager;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request", Justification = "Required for ServiceStack")]
 | 
				
			||||||
        public object Get(GetProfileInfos request)
 | 
					        public object Get(GetProfileInfos request)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            return _dlnaManager.GetProfileInfos().ToArray();
 | 
					            return _dlnaManager.GetProfileInfos().ToArray();
 | 
				
			||||||
@ -62,6 +64,7 @@ namespace Emby.Dlna.Api
 | 
				
			|||||||
            return _dlnaManager.GetProfile(request.Id);
 | 
					            return _dlnaManager.GetProfile(request.Id);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request", Justification = "Required for ServiceStack")]
 | 
				
			||||||
        public object Get(GetDefaultProfile request)
 | 
					        public object Get(GetDefaultProfile request)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            return _dlnaManager.GetDefaultProfile();
 | 
					            return _dlnaManager.GetDefaultProfile();
 | 
				
			||||||
 | 
				
			|||||||
@ -78,7 +78,18 @@ 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,
 | 
				
			||||||
 | 
					                libraryManager);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /// <inheritdoc />
 | 
					        /// <inheritdoc />
 | 
				
			||||||
@ -153,7 +164,7 @@ namespace Emby.Dlna.ContentDirectory
 | 
				
			|||||||
        {
 | 
					        {
 | 
				
			||||||
            var id = sparams["ObjectID"];
 | 
					            var id = sparams["ObjectID"];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var serverItem = GetItemFromObjectId(id, _user);
 | 
					            var serverItem = GetItemFromObjectId(id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var item = serverItem.Item;
 | 
					            var item = serverItem.Item;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -276,7 +287,7 @@ namespace Emby.Dlna.ContentDirectory
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                    DidlBuilder.WriteXmlRootAttributes(_profile, writer);
 | 
					                    DidlBuilder.WriteXmlRootAttributes(_profile, writer);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    var serverItem = GetItemFromObjectId(id, _user);
 | 
					                    var serverItem = GetItemFromObjectId(id);
 | 
				
			||||||
                    var item = serverItem.Item;
 | 
					                    var item = serverItem.Item;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -293,7 +304,7 @@ namespace Emby.Dlna.ContentDirectory
 | 
				
			|||||||
                        else
 | 
					                        else
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
                            var dlnaOptions = _config.GetDlnaConfiguration();
 | 
					                            var dlnaOptions = _config.GetDlnaConfiguration();
 | 
				
			||||||
                            _didlBuilder.WriteItemElement(dlnaOptions, writer, item, _user, null, null, deviceId, filter);
 | 
					                            _didlBuilder.WriteItemElement(writer, item, _user, null, null, deviceId, filter);
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        provided++;
 | 
					                        provided++;
 | 
				
			||||||
@ -320,7 +331,7 @@ namespace Emby.Dlna.ContentDirectory
 | 
				
			|||||||
                            }
 | 
					                            }
 | 
				
			||||||
                            else
 | 
					                            else
 | 
				
			||||||
                            {
 | 
					                            {
 | 
				
			||||||
                                _didlBuilder.WriteItemElement(dlnaOptions, writer, childItem, _user, item, serverItem.StubType, deviceId, filter);
 | 
					                                _didlBuilder.WriteItemElement(writer, childItem, _user, item, serverItem.StubType, deviceId, filter);
 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
@ -387,7 +398,7 @@ namespace Emby.Dlna.ContentDirectory
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                    DidlBuilder.WriteXmlRootAttributes(_profile, writer);
 | 
					                    DidlBuilder.WriteXmlRootAttributes(_profile, writer);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    var serverItem = GetItemFromObjectId(sparams["ContainerID"], _user);
 | 
					                    var serverItem = GetItemFromObjectId(sparams["ContainerID"]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    var item = serverItem.Item;
 | 
					                    var item = serverItem.Item;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -406,7 +417,7 @@ namespace Emby.Dlna.ContentDirectory
 | 
				
			|||||||
                        }
 | 
					                        }
 | 
				
			||||||
                        else
 | 
					                        else
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
                            _didlBuilder.WriteItemElement(dlnaOptions, writer, i, _user, item, serverItem.StubType, deviceId, filter);
 | 
					                            _didlBuilder.WriteItemElement(writer, i, _user, item, serverItem.StubType, deviceId, filter);
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -512,11 +523,11 @@ namespace Emby.Dlna.ContentDirectory
 | 
				
			|||||||
                }
 | 
					                }
 | 
				
			||||||
                else if (string.Equals(CollectionType.Folders, collectionFolder.CollectionType, StringComparison.OrdinalIgnoreCase))
 | 
					                else if (string.Equals(CollectionType.Folders, collectionFolder.CollectionType, StringComparison.OrdinalIgnoreCase))
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    return GetFolders(item, user, stubType, sort, startIndex, limit);
 | 
					                    return GetFolders(user, startIndex, limit);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                else if (string.Equals(CollectionType.LiveTv, collectionFolder.CollectionType, StringComparison.OrdinalIgnoreCase))
 | 
					                else if (string.Equals(CollectionType.LiveTv, collectionFolder.CollectionType, StringComparison.OrdinalIgnoreCase))
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    return GetLiveTvChannels(item, user, stubType, sort, startIndex, limit);
 | 
					                    return GetLiveTvChannels(user, sort, startIndex, limit);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -547,7 +558,7 @@ namespace Emby.Dlna.ContentDirectory
 | 
				
			|||||||
            return ToResult(queryResult);
 | 
					            return ToResult(queryResult);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private QueryResult<ServerItem> GetLiveTvChannels(BaseItem item, User user, StubType? stubType, SortCriteria sort, int? startIndex, int? limit)
 | 
					        private QueryResult<ServerItem> GetLiveTvChannels(User user, SortCriteria sort, int? startIndex, int? limit)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var query = new InternalItemsQuery(user)
 | 
					            var query = new InternalItemsQuery(user)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
@ -579,7 +590,7 @@ namespace Emby.Dlna.ContentDirectory
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            if (stubType.HasValue && stubType.Value == StubType.Playlists)
 | 
					            if (stubType.HasValue && stubType.Value == StubType.Playlists)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                return GetMusicPlaylists(item, user, query);
 | 
					                return GetMusicPlaylists(user, query);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (stubType.HasValue && stubType.Value == StubType.Albums)
 | 
					            if (stubType.HasValue && stubType.Value == StubType.Albums)
 | 
				
			||||||
@ -707,7 +718,7 @@ namespace Emby.Dlna.ContentDirectory
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            if (stubType.HasValue && stubType.Value == StubType.Collections)
 | 
					            if (stubType.HasValue && stubType.Value == StubType.Collections)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                return GetMovieCollections(item, user, query);
 | 
					                return GetMovieCollections(user, query);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (stubType.HasValue && stubType.Value == StubType.Favorites)
 | 
					            if (stubType.HasValue && stubType.Value == StubType.Favorites)
 | 
				
			||||||
@ -720,46 +731,42 @@ namespace Emby.Dlna.ContentDirectory
 | 
				
			|||||||
                return GetGenres(item, user, query);
 | 
					                return GetGenres(item, user, query);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var list = new List<ServerItem>();
 | 
					            var array = new ServerItem[]
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
            list.Add(new ServerItem(item)
 | 
					                new ServerItem(item)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    StubType = StubType.ContinueWatching
 | 
					                    StubType = StubType.ContinueWatching
 | 
				
			||||||
            });
 | 
					                },
 | 
				
			||||||
 | 
					                new ServerItem(item)
 | 
				
			||||||
            list.Add(new ServerItem(item)
 | 
					 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    StubType = StubType.Latest
 | 
					                    StubType = StubType.Latest
 | 
				
			||||||
            });
 | 
					                },
 | 
				
			||||||
 | 
					                new ServerItem(item)
 | 
				
			||||||
            list.Add(new ServerItem(item)
 | 
					 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    StubType = StubType.Movies
 | 
					                    StubType = StubType.Movies
 | 
				
			||||||
            });
 | 
					                },
 | 
				
			||||||
 | 
					                new ServerItem(item)
 | 
				
			||||||
            list.Add(new ServerItem(item)
 | 
					 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    StubType = StubType.Collections
 | 
					                    StubType = StubType.Collections
 | 
				
			||||||
            });
 | 
					                },
 | 
				
			||||||
 | 
					                new ServerItem(item)
 | 
				
			||||||
            list.Add(new ServerItem(item)
 | 
					 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    StubType = StubType.Favorites
 | 
					                    StubType = StubType.Favorites
 | 
				
			||||||
            });
 | 
					                },
 | 
				
			||||||
 | 
					                new ServerItem(item)
 | 
				
			||||||
            list.Add(new ServerItem(item)
 | 
					 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    StubType = StubType.Genres
 | 
					                    StubType = StubType.Genres
 | 
				
			||||||
            });
 | 
					                }
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return new QueryResult<ServerItem>
 | 
					            return new QueryResult<ServerItem>
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                Items = list,
 | 
					                Items = array,
 | 
				
			||||||
                TotalRecordCount = list.Count
 | 
					                TotalRecordCount = array.Length
 | 
				
			||||||
            };
 | 
					            };
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private QueryResult<ServerItem> GetFolders(BaseItem item, User user, StubType? stubType, SortCriteria sort, int? startIndex, int? limit)
 | 
					        private QueryResult<ServerItem> GetFolders(User user, int? startIndex, int? limit)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var folders = _libraryManager.GetUserRootFolder().GetChildren(user, true)
 | 
					            var folders = _libraryManager.GetUserRootFolder().GetChildren(user, true)
 | 
				
			||||||
                .OrderBy(i => i.SortName)
 | 
					                .OrderBy(i => i.SortName)
 | 
				
			||||||
@ -792,7 +799,7 @@ namespace Emby.Dlna.ContentDirectory
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            if (stubType.HasValue && stubType.Value == StubType.NextUp)
 | 
					            if (stubType.HasValue && stubType.Value == StubType.NextUp)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                return GetNextUp(item, user, query);
 | 
					                return GetNextUp(item, query);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (stubType.HasValue && stubType.Value == StubType.Latest)
 | 
					            if (stubType.HasValue && stubType.Value == StubType.Latest)
 | 
				
			||||||
@ -910,7 +917,7 @@ namespace Emby.Dlna.ContentDirectory
 | 
				
			|||||||
            return ToResult(result);
 | 
					            return ToResult(result);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private QueryResult<ServerItem> GetMovieCollections(BaseItem parent, User user, InternalItemsQuery query)
 | 
					        private QueryResult<ServerItem> GetMovieCollections(User user, InternalItemsQuery query)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            query.Recursive = true;
 | 
					            query.Recursive = true;
 | 
				
			||||||
            //query.Parent = parent;
 | 
					            //query.Parent = parent;
 | 
				
			||||||
@ -1105,7 +1112,7 @@ namespace Emby.Dlna.ContentDirectory
 | 
				
			|||||||
            return ToResult(result);
 | 
					            return ToResult(result);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private QueryResult<ServerItem> GetMusicPlaylists(BaseItem parent, User user, InternalItemsQuery query)
 | 
					        private QueryResult<ServerItem> GetMusicPlaylists(User user, InternalItemsQuery query)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            query.Parent = null;
 | 
					            query.Parent = null;
 | 
				
			||||||
            query.IncludeItemTypes = new[] { typeof(Playlist).Name };
 | 
					            query.IncludeItemTypes = new[] { typeof(Playlist).Name };
 | 
				
			||||||
@ -1134,7 +1141,7 @@ namespace Emby.Dlna.ContentDirectory
 | 
				
			|||||||
            return ToResult(items);
 | 
					            return ToResult(items);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private QueryResult<ServerItem> GetNextUp(BaseItem parent, User user, InternalItemsQuery query)
 | 
					        private QueryResult<ServerItem> GetNextUp(BaseItem parent, InternalItemsQuery query)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            query.OrderBy = Array.Empty<(string, SortOrder)>();
 | 
					            query.OrderBy = Array.Empty<(string, SortOrder)>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -1289,15 +1296,15 @@ namespace Emby.Dlna.ContentDirectory
 | 
				
			|||||||
            return result;
 | 
					            return result;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private ServerItem GetItemFromObjectId(string id, User user)
 | 
					        private ServerItem GetItemFromObjectId(string id)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            return DidlBuilder.IsIdRoot(id)
 | 
					            return DidlBuilder.IsIdRoot(id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                 ? new ServerItem(_libraryManager.GetUserRootFolder())
 | 
					                 ? new ServerItem(_libraryManager.GetUserRootFolder())
 | 
				
			||||||
                 : ParseItemId(id, user);
 | 
					                 : ParseItemId(id);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private ServerItem ParseItemId(string id, User user)
 | 
					        private ServerItem ParseItemId(string id)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            StubType? stubType = null;
 | 
					            StubType? stubType = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -45,6 +45,7 @@ namespace Emby.Dlna.Didl
 | 
				
			|||||||
        private readonly IMediaSourceManager _mediaSourceManager;
 | 
					        private readonly IMediaSourceManager _mediaSourceManager;
 | 
				
			||||||
        private readonly ILogger _logger;
 | 
					        private readonly ILogger _logger;
 | 
				
			||||||
        private readonly IMediaEncoder _mediaEncoder;
 | 
					        private readonly IMediaEncoder _mediaEncoder;
 | 
				
			||||||
 | 
					        private readonly ILibraryManager _libraryManager;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public DidlBuilder(
 | 
					        public DidlBuilder(
 | 
				
			||||||
            DeviceProfile profile,
 | 
					            DeviceProfile profile,
 | 
				
			||||||
@ -56,7 +57,8 @@ namespace Emby.Dlna.Didl
 | 
				
			|||||||
            ILocalizationManager localization,
 | 
					            ILocalizationManager localization,
 | 
				
			||||||
            IMediaSourceManager mediaSourceManager,
 | 
					            IMediaSourceManager mediaSourceManager,
 | 
				
			||||||
            ILogger logger,
 | 
					            ILogger logger,
 | 
				
			||||||
            IMediaEncoder mediaEncoder)
 | 
					            IMediaEncoder mediaEncoder,
 | 
				
			||||||
 | 
					            ILibraryManager libraryManager)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _profile = profile;
 | 
					            _profile = profile;
 | 
				
			||||||
            _user = user;
 | 
					            _user = user;
 | 
				
			||||||
@ -68,6 +70,7 @@ namespace Emby.Dlna.Didl
 | 
				
			|||||||
            _mediaSourceManager = mediaSourceManager;
 | 
					            _mediaSourceManager = mediaSourceManager;
 | 
				
			||||||
            _logger = logger;
 | 
					            _logger = logger;
 | 
				
			||||||
            _mediaEncoder = mediaEncoder;
 | 
					            _mediaEncoder = mediaEncoder;
 | 
				
			||||||
 | 
					            _libraryManager = libraryManager;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public static string NormalizeDlnaMediaUrl(string url)
 | 
					        public static string NormalizeDlnaMediaUrl(string url)
 | 
				
			||||||
@ -75,7 +78,7 @@ namespace Emby.Dlna.Didl
 | 
				
			|||||||
            return url + "&dlnaheaders=true";
 | 
					            return url + "&dlnaheaders=true";
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public string GetItemDidl(DlnaOptions options, BaseItem item, User user, BaseItem context, string deviceId, Filter filter, StreamInfo streamInfo)
 | 
					        public string GetItemDidl(BaseItem item, User user, BaseItem context, string deviceId, Filter filter, StreamInfo streamInfo)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var settings = new XmlWriterSettings
 | 
					            var settings = new XmlWriterSettings
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
@ -100,7 +103,7 @@ namespace Emby.Dlna.Didl
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                    WriteXmlRootAttributes(_profile, writer);
 | 
					                    WriteXmlRootAttributes(_profile, writer);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    WriteItemElement(options, writer, item, user, context, null, deviceId, filter, streamInfo);
 | 
					                    WriteItemElement(writer, item, user, context, null, deviceId, filter, streamInfo);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    writer.WriteFullEndElement();
 | 
					                    writer.WriteFullEndElement();
 | 
				
			||||||
                    //writer.WriteEndDocument();
 | 
					                    //writer.WriteEndDocument();
 | 
				
			||||||
@ -127,7 +130,6 @@ namespace Emby.Dlna.Didl
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public void WriteItemElement(
 | 
					        public void WriteItemElement(
 | 
				
			||||||
            DlnaOptions options,
 | 
					 | 
				
			||||||
            XmlWriter writer,
 | 
					            XmlWriter writer,
 | 
				
			||||||
            BaseItem item,
 | 
					            BaseItem item,
 | 
				
			||||||
            User user,
 | 
					            User user,
 | 
				
			||||||
@ -164,25 +166,23 @@ namespace Emby.Dlna.Didl
 | 
				
			|||||||
            // refID?
 | 
					            // refID?
 | 
				
			||||||
            // storeAttribute(itemNode, object, ClassProperties.REF_ID, false);
 | 
					            // storeAttribute(itemNode, object, ClassProperties.REF_ID, false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var hasMediaSources = item as IHasMediaSources;
 | 
					            if (item is IHasMediaSources)
 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (hasMediaSources != null)
 | 
					 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                if (string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase))
 | 
					                if (string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase))
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    AddAudioResource(options, writer, item, deviceId, filter, streamInfo);
 | 
					                    AddAudioResource(writer, item, deviceId, filter, streamInfo);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                else if (string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase))
 | 
					                else if (string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase))
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    AddVideoResource(options, writer, item, deviceId, filter, streamInfo);
 | 
					                    AddVideoResource(writer, item, deviceId, filter, streamInfo);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            AddCover(item, context, null, writer);
 | 
					            AddCover(item, null, writer);
 | 
				
			||||||
            writer.WriteFullEndElement();
 | 
					            writer.WriteFullEndElement();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private void AddVideoResource(DlnaOptions options, XmlWriter writer, BaseItem video, string deviceId, Filter filter, StreamInfo streamInfo = null)
 | 
					        private void AddVideoResource(XmlWriter writer, BaseItem video, string deviceId, Filter filter, StreamInfo streamInfo = null)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            if (streamInfo == null)
 | 
					            if (streamInfo == null)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
@ -226,7 +226,7 @@ namespace Emby.Dlna.Didl
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            foreach (var contentFeature in contentFeatureList)
 | 
					            foreach (var contentFeature in contentFeatureList)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                AddVideoResource(writer, video, deviceId, filter, contentFeature, streamInfo);
 | 
					                AddVideoResource(writer, filter, contentFeature, streamInfo);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var subtitleProfiles = streamInfo.GetSubtitleProfiles(_mediaEncoder, false, _serverAddress, _accessToken);
 | 
					            var subtitleProfiles = streamInfo.GetSubtitleProfiles(_mediaEncoder, false, _serverAddress, _accessToken);
 | 
				
			||||||
@ -283,7 +283,10 @@ namespace Emby.Dlna.Didl
 | 
				
			|||||||
            else
 | 
					            else
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                writer.WriteStartElement(string.Empty, "res", NS_DIDL);
 | 
					                writer.WriteStartElement(string.Empty, "res", NS_DIDL);
 | 
				
			||||||
                var protocolInfo = string.Format("http-get:*:text/{0}:*", info.Format.ToLowerInvariant());
 | 
					                var protocolInfo = string.Format(
 | 
				
			||||||
 | 
					                    CultureInfo.InvariantCulture,
 | 
				
			||||||
 | 
					                    "http-get:*:text/{0}:*",
 | 
				
			||||||
 | 
					                    info.Format.ToLowerInvariant());
 | 
				
			||||||
                writer.WriteAttributeString("protocolInfo", protocolInfo);
 | 
					                writer.WriteAttributeString("protocolInfo", protocolInfo);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                writer.WriteString(info.Url);
 | 
					                writer.WriteString(info.Url);
 | 
				
			||||||
@ -293,7 +296,7 @@ namespace Emby.Dlna.Didl
 | 
				
			|||||||
            return true;
 | 
					            return true;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private void AddVideoResource(XmlWriter writer, BaseItem video, string deviceId, Filter filter, string contentFeatures, StreamInfo streamInfo)
 | 
					        private void AddVideoResource(XmlWriter writer, Filter filter, string contentFeatures, StreamInfo streamInfo)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            writer.WriteStartElement(string.Empty, "res", NS_DIDL);
 | 
					            writer.WriteStartElement(string.Empty, "res", NS_DIDL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -335,7 +338,13 @@ namespace Emby.Dlna.Didl
 | 
				
			|||||||
            {
 | 
					            {
 | 
				
			||||||
                if (targetWidth.HasValue && targetHeight.HasValue)
 | 
					                if (targetWidth.HasValue && targetHeight.HasValue)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    writer.WriteAttributeString("resolution", string.Format("{0}x{1}", targetWidth.Value, targetHeight.Value));
 | 
					                    writer.WriteAttributeString(
 | 
				
			||||||
 | 
					                        "resolution",
 | 
				
			||||||
 | 
					                        string.Format(
 | 
				
			||||||
 | 
					                            CultureInfo.InvariantCulture,
 | 
				
			||||||
 | 
					                            "{0}x{1}",
 | 
				
			||||||
 | 
					                            targetWidth.Value,
 | 
				
			||||||
 | 
					                            targetHeight.Value));
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -369,17 +378,19 @@ namespace Emby.Dlna.Didl
 | 
				
			|||||||
                streamInfo.TargetVideoCodecTag,
 | 
					                streamInfo.TargetVideoCodecTag,
 | 
				
			||||||
                streamInfo.IsTargetAVC);
 | 
					                streamInfo.IsTargetAVC);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var filename = url.Substring(0, url.IndexOf('?'));
 | 
					            var filename = url.Substring(0, url.IndexOf('?', StringComparison.Ordinal));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var mimeType = mediaProfile == null || string.IsNullOrEmpty(mediaProfile.MimeType)
 | 
					            var mimeType = mediaProfile == null || string.IsNullOrEmpty(mediaProfile.MimeType)
 | 
				
			||||||
               ? MimeTypes.GetMimeType(filename)
 | 
					               ? MimeTypes.GetMimeType(filename)
 | 
				
			||||||
               : mediaProfile.MimeType;
 | 
					               : mediaProfile.MimeType;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            writer.WriteAttributeString("protocolInfo", string.Format(
 | 
					            writer.WriteAttributeString(
 | 
				
			||||||
 | 
					                "protocolInfo",
 | 
				
			||||||
 | 
					                string.Format(
 | 
				
			||||||
 | 
					                    CultureInfo.InvariantCulture,
 | 
				
			||||||
                    "http-get:*:{0}:{1}",
 | 
					                    "http-get:*:{0}:{1}",
 | 
				
			||||||
                    mimeType,
 | 
					                    mimeType,
 | 
				
			||||||
                contentFeatures
 | 
					                    contentFeatures));
 | 
				
			||||||
                ));
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            writer.WriteString(url);
 | 
					            writer.WriteString(url);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -420,7 +431,10 @@ namespace Emby.Dlna.Didl
 | 
				
			|||||||
                if (item.ParentIndexNumber.HasValue && item.ParentIndexNumber.Value == 0
 | 
					                if (item.ParentIndexNumber.HasValue && item.ParentIndexNumber.Value == 0
 | 
				
			||||||
                    && season.IndexNumber.HasValue && season.IndexNumber.Value != 0)
 | 
					                    && season.IndexNumber.HasValue && season.IndexNumber.Value != 0)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    return string.Format(_localization.GetLocalizedString("ValueSpecialEpisodeName"), item.Name);
 | 
					                    return string.Format(
 | 
				
			||||||
 | 
					                        CultureInfo.InvariantCulture,
 | 
				
			||||||
 | 
					                        _localization.GetLocalizedString("ValueSpecialEpisodeName"),
 | 
				
			||||||
 | 
					                        item.Name);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if (item.IndexNumber.HasValue)
 | 
					                if (item.IndexNumber.HasValue)
 | 
				
			||||||
@ -435,11 +449,34 @@ namespace Emby.Dlna.Didl
 | 
				
			|||||||
                    return number + " - " + item.Name;
 | 
					                    return number + " - " + item.Name;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					            else if (item is Episode ep)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                var parent = ep.GetParent();
 | 
				
			||||||
 | 
					                var name = parent.Name + " - ";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (ep.ParentIndexNumber.HasValue)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    name += "S" + ep.ParentIndexNumber.Value.ToString("00", CultureInfo.InvariantCulture);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                else if (!item.IndexNumber.HasValue)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    return name + " - " + item.Name;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                name += "E" + ep.IndexNumber.Value.ToString("00", CultureInfo.InvariantCulture);
 | 
				
			||||||
 | 
					                if (ep.IndexNumberEnd.HasValue)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    name += "-" + ep.IndexNumberEnd.Value.ToString("00", CultureInfo.InvariantCulture);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                name += " - " + item.Name;
 | 
				
			||||||
 | 
					                return name;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return item.Name;
 | 
					            return item.Name;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private void AddAudioResource(DlnaOptions options, XmlWriter writer, BaseItem audio, string deviceId, Filter filter, StreamInfo streamInfo = null)
 | 
					        private void AddAudioResource(XmlWriter writer, BaseItem audio, string deviceId, Filter filter, StreamInfo streamInfo = null)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            writer.WriteStartElement(string.Empty, "res", NS_DIDL);
 | 
					            writer.WriteStartElement(string.Empty, "res", NS_DIDL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -505,7 +542,7 @@ namespace Emby.Dlna.Didl
 | 
				
			|||||||
                targetSampleRate,
 | 
					                targetSampleRate,
 | 
				
			||||||
                targetAudioBitDepth);
 | 
					                targetAudioBitDepth);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var filename = url.Substring(0, url.IndexOf('?'));
 | 
					            var filename = url.Substring(0, url.IndexOf('?', StringComparison.Ordinal));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var mimeType = mediaProfile == null || string.IsNullOrEmpty(mediaProfile.MimeType)
 | 
					            var mimeType = mediaProfile == null || string.IsNullOrEmpty(mediaProfile.MimeType)
 | 
				
			||||||
                ? MimeTypes.GetMimeType(filename)
 | 
					                ? MimeTypes.GetMimeType(filename)
 | 
				
			||||||
@ -521,11 +558,13 @@ namespace Emby.Dlna.Didl
 | 
				
			|||||||
                streamInfo.RunTimeTicks ?? 0,
 | 
					                streamInfo.RunTimeTicks ?? 0,
 | 
				
			||||||
                streamInfo.TranscodeSeekInfo);
 | 
					                streamInfo.TranscodeSeekInfo);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            writer.WriteAttributeString("protocolInfo", string.Format(
 | 
					            writer.WriteAttributeString(
 | 
				
			||||||
 | 
					                "protocolInfo",
 | 
				
			||||||
 | 
					                string.Format(
 | 
				
			||||||
 | 
					                    CultureInfo.InvariantCulture,
 | 
				
			||||||
                    "http-get:*:{0}:{1}",
 | 
					                    "http-get:*:{0}:{1}",
 | 
				
			||||||
                    mimeType,
 | 
					                    mimeType,
 | 
				
			||||||
                contentFeatures
 | 
					                    contentFeatures));
 | 
				
			||||||
                ));
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            writer.WriteString(url);
 | 
					            writer.WriteString(url);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -548,7 +587,7 @@ namespace Emby.Dlna.Didl
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            var clientId = GetClientId(folder, stubType);
 | 
					            var clientId = GetClientId(folder, stubType);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (string.Equals(requestedId, "0"))
 | 
					            if (string.Equals(requestedId, "0", StringComparison.Ordinal))
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                writer.WriteAttributeString("id", "0");
 | 
					                writer.WriteAttributeString("id", "0");
 | 
				
			||||||
                writer.WriteAttributeString("parentID", "-1");
 | 
					                writer.WriteAttributeString("parentID", "-1");
 | 
				
			||||||
@ -577,7 +616,7 @@ namespace Emby.Dlna.Didl
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            AddGeneralProperties(folder, stubType, context, writer, filter);
 | 
					            AddGeneralProperties(folder, stubType, context, writer, filter);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            AddCover(folder, context, stubType, writer);
 | 
					            AddCover(folder, stubType, writer);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            writer.WriteFullEndElement();
 | 
					            writer.WriteFullEndElement();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -610,7 +649,10 @@ namespace Emby.Dlna.Didl
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            if (playbackPositionTicks > 0)
 | 
					            if (playbackPositionTicks > 0)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                var elementValue = string.Format("BM={0}", Convert.ToInt32(TimeSpan.FromTicks(playbackPositionTicks).TotalSeconds).ToString(_usCulture));
 | 
					                var elementValue = string.Format(
 | 
				
			||||||
 | 
					                    CultureInfo.InvariantCulture,
 | 
				
			||||||
 | 
					                    "BM={0}",
 | 
				
			||||||
 | 
					                    Convert.ToInt32(TimeSpan.FromTicks(playbackPositionTicks).TotalSeconds));
 | 
				
			||||||
                AddValue(writer, "sec", "dcmInfo", elementValue, secAttribute.Value);
 | 
					                AddValue(writer, "sec", "dcmInfo", elementValue, secAttribute.Value);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -763,37 +805,36 @@ namespace Emby.Dlna.Didl
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        private void AddPeople(BaseItem item, XmlWriter writer)
 | 
					        private void AddPeople(BaseItem item, XmlWriter writer)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            //var types = new[]
 | 
					            if (!item.SupportsPeople)
 | 
				
			||||||
            //{
 | 
					            {
 | 
				
			||||||
            //    PersonType.Director,
 | 
					                return;
 | 
				
			||||||
            //    PersonType.Writer,
 | 
					            }
 | 
				
			||||||
            //    PersonType.Producer,
 | 
					 | 
				
			||||||
            //    PersonType.Composer,
 | 
					 | 
				
			||||||
            //    "Creator"
 | 
					 | 
				
			||||||
            //};
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            //var people = _libraryManager.GetPeople(item);
 | 
					            var types = new[]
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                PersonType.Director,
 | 
				
			||||||
 | 
					                PersonType.Writer,
 | 
				
			||||||
 | 
					                PersonType.Producer,
 | 
				
			||||||
 | 
					                PersonType.Composer,
 | 
				
			||||||
 | 
					                "creator"
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            //var index = 0;
 | 
					            // Seeing some LG models locking up due content with large lists of people
 | 
				
			||||||
 | 
					            // The actual issue might just be due to processing a more metadata than it can handle
 | 
				
			||||||
 | 
					            var people = _libraryManager.GetPeople(
 | 
				
			||||||
 | 
					                new InternalPeopleQuery
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    ItemId = item.Id,
 | 
				
			||||||
 | 
					                    Limit = 6
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            //// Seeing some LG models locking up due content with large lists of people
 | 
					            foreach (var actor in people)
 | 
				
			||||||
            //// The actual issue might just be due to processing a more metadata than it can handle
 | 
					            {
 | 
				
			||||||
            //var limit = 6;
 | 
					                var type = types.FirstOrDefault(i => string.Equals(i, actor.Type, StringComparison.OrdinalIgnoreCase) || string.Equals(i, actor.Role, StringComparison.OrdinalIgnoreCase))
 | 
				
			||||||
 | 
					                    ?? PersonType.Actor;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            //foreach (var actor in people)
 | 
					                AddValue(writer, "upnp", type.ToLowerInvariant(), actor.Name, NS_UPNP);
 | 
				
			||||||
            //{
 | 
					            }
 | 
				
			||||||
            //    var type = types.FirstOrDefault(i => string.Equals(i, actor.Type, StringComparison.OrdinalIgnoreCase) || string.Equals(i, actor.Role, StringComparison.OrdinalIgnoreCase))
 | 
					 | 
				
			||||||
            //        ?? PersonType.Actor;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            //    AddValue(writer, "upnp", type.ToLowerInvariant(), actor.Name, NS_UPNP);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            //    index++;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            //    if (index >= limit)
 | 
					 | 
				
			||||||
            //    {
 | 
					 | 
				
			||||||
            //        break;
 | 
					 | 
				
			||||||
            //    }
 | 
					 | 
				
			||||||
            //}
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private void AddGeneralProperties(BaseItem item, StubType? itemStubType, BaseItem context, XmlWriter writer, Filter filter)
 | 
					        private void AddGeneralProperties(BaseItem item, StubType? itemStubType, BaseItem context, XmlWriter writer, Filter filter)
 | 
				
			||||||
@ -870,7 +911,7 @@ namespace Emby.Dlna.Didl
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private void AddCover(BaseItem item, BaseItem context, StubType? stubType, XmlWriter writer)
 | 
					        private void AddCover(BaseItem item, StubType? stubType, XmlWriter writer)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            ImageDownloadInfo imageInfo = GetImageInfo(item);
 | 
					            ImageDownloadInfo imageInfo = GetImageInfo(item);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -915,17 +956,8 @@ namespace Emby.Dlna.Didl
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private void AddEmbeddedImageAsCover(string name, XmlWriter writer)
 | 
					        private void AddImageResElement(
 | 
				
			||||||
        {
 | 
					            BaseItem item,
 | 
				
			||||||
            writer.WriteStartElement("upnp", "albumArtURI", NS_UPNP);
 | 
					 | 
				
			||||||
            writer.WriteAttributeString("dlna", "profileID", NS_DLNA, _profile.AlbumArtPn);
 | 
					 | 
				
			||||||
            writer.WriteString(_serverAddress + "/Dlna/icons/people480.jpg");
 | 
					 | 
				
			||||||
            writer.WriteFullEndElement();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            writer.WriteElementString("upnp", "icon", NS_UPNP, _serverAddress + "/Dlna/icons/people48.jpg");
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private void AddImageResElement(BaseItem item,
 | 
					 | 
				
			||||||
            XmlWriter writer,
 | 
					            XmlWriter writer,
 | 
				
			||||||
            int maxWidth,
 | 
					            int maxWidth,
 | 
				
			||||||
            int maxHeight,
 | 
					            int maxHeight,
 | 
				
			||||||
@ -951,13 +983,17 @@ namespace Emby.Dlna.Didl
 | 
				
			|||||||
            var contentFeatures = new ContentFeatureBuilder(_profile)
 | 
					            var contentFeatures = new ContentFeatureBuilder(_profile)
 | 
				
			||||||
                .BuildImageHeader(format, width, height, imageInfo.IsDirectStream, org_Pn);
 | 
					                .BuildImageHeader(format, width, height, imageInfo.IsDirectStream, org_Pn);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            writer.WriteAttributeString("protocolInfo", string.Format(
 | 
					            writer.WriteAttributeString(
 | 
				
			||||||
 | 
					                "protocolInfo",
 | 
				
			||||||
 | 
					                string.Format(
 | 
				
			||||||
 | 
					                    CultureInfo.InvariantCulture,
 | 
				
			||||||
                    "http-get:*:{0}:{1}",
 | 
					                    "http-get:*:{0}:{1}",
 | 
				
			||||||
                    MimeTypes.GetMimeType("file." + format),
 | 
					                    MimeTypes.GetMimeType("file." + format),
 | 
				
			||||||
                contentFeatures
 | 
					                    contentFeatures));
 | 
				
			||||||
                ));
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            writer.WriteAttributeString("resolution", string.Format("{0}x{1}", width, height));
 | 
					            writer.WriteAttributeString(
 | 
				
			||||||
 | 
					                "resolution",
 | 
				
			||||||
 | 
					                string.Format(CultureInfo.InvariantCulture, "{0}x{1}", width, height));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            writer.WriteString(albumartUrlInfo.Url);
 | 
					            writer.WriteString(albumartUrlInfo.Url);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -1096,7 +1132,9 @@ namespace Emby.Dlna.Didl
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        private ImageUrlInfo GetImageUrl(ImageDownloadInfo info, int maxWidth, int maxHeight, string format)
 | 
					        private ImageUrlInfo GetImageUrl(ImageDownloadInfo info, int maxWidth, int maxHeight, string format)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var url = string.Format("{0}/Items/{1}/Images/{2}/0/{3}/{4}/{5}/{6}/0/0",
 | 
					            var url = string.Format(
 | 
				
			||||||
 | 
					                CultureInfo.InvariantCulture,
 | 
				
			||||||
 | 
					                "{0}/Items/{1}/Images/{2}/0/{3}/{4}/{5}/{6}/0/0",
 | 
				
			||||||
                _serverAddress,
 | 
					                _serverAddress,
 | 
				
			||||||
                info.ItemId.ToString("N", CultureInfo.InvariantCulture),
 | 
					                info.ItemId.ToString("N", CultureInfo.InvariantCulture),
 | 
				
			||||||
                info.Type,
 | 
					                info.Type,
 | 
				
			||||||
 | 
				
			|||||||
@ -53,6 +53,6 @@ namespace Emby.Dlna.Didl
 | 
				
			|||||||
            _encoding = encoding;
 | 
					            _encoding = encoding;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public override Encoding Encoding => (null == _encoding) ? base.Encoding : _encoding;
 | 
					        public override Encoding Encoding => _encoding ?? base.Encoding;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -346,7 +346,12 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
                throw new InvalidOperationException("Unable to find service");
 | 
					                throw new InvalidOperationException("Unable to find service");
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return new SsdpHttpClient(_httpClient).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),
 | 
				
			||||||
 | 
					                cancellationToken: cancellationToken);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public async Task SetPlay(CancellationToken cancellationToken)
 | 
					        public async Task SetPlay(CancellationToken cancellationToken)
 | 
				
			||||||
@ -515,8 +520,12 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var result = await new SsdpHttpClient(_httpClient).SendCommandAsync(Properties.BaseUrl, service, command.Name, rendererCommands.BuildPost(command, service.ServiceType), true)
 | 
					            var result = await new SsdpHttpClient(_httpClient).SendCommandAsync(
 | 
				
			||||||
                .ConfigureAwait(false);
 | 
					                Properties.BaseUrl,
 | 
				
			||||||
 | 
					                service,
 | 
				
			||||||
 | 
					                command.Name,
 | 
				
			||||||
 | 
					                rendererCommands.BuildPost(command, service.ServiceType),
 | 
				
			||||||
 | 
					                cancellationToken: cancellationToken).ConfigureAwait(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (result == null || result.Document == null)
 | 
					            if (result == null || result.Document == null)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
@ -561,8 +570,12 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var result = await new SsdpHttpClient(_httpClient).SendCommandAsync(Properties.BaseUrl, service, command.Name, rendererCommands.BuildPost(command, service.ServiceType), true)
 | 
					            var result = await new SsdpHttpClient(_httpClient).SendCommandAsync(
 | 
				
			||||||
                .ConfigureAwait(false);
 | 
					                Properties.BaseUrl,
 | 
				
			||||||
 | 
					                service,
 | 
				
			||||||
 | 
					                command.Name,
 | 
				
			||||||
 | 
					                rendererCommands.BuildPost(command, service.ServiceType),
 | 
				
			||||||
 | 
					                cancellationToken: cancellationToken).ConfigureAwait(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (result == null || result.Document == null)
 | 
					            if (result == null || result.Document == null)
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
@ -588,8 +601,12 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
                return null;
 | 
					                return null;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var result = await new SsdpHttpClient(_httpClient).SendCommandAsync(Properties.BaseUrl, service, command.Name, avCommands.BuildPost(command, service.ServiceType), false)
 | 
					            var result = await new SsdpHttpClient(_httpClient).SendCommandAsync(
 | 
				
			||||||
                .ConfigureAwait(false);
 | 
					                Properties.BaseUrl,
 | 
				
			||||||
 | 
					                service,
 | 
				
			||||||
 | 
					                command.Name,
 | 
				
			||||||
 | 
					                avCommands.BuildPost(command, service.ServiceType),
 | 
				
			||||||
 | 
					                cancellationToken: cancellationToken).ConfigureAwait(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (result == null || result.Document == null)
 | 
					            if (result == null || result.Document == null)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
@ -599,7 +616,7 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
            var transportState =
 | 
					            var transportState =
 | 
				
			||||||
                result.Document.Descendants(uPnpNamespaces.AvTransport + "GetTransportInfoResponse").Select(i => i.Element("CurrentTransportState")).FirstOrDefault(i => i != null);
 | 
					                result.Document.Descendants(uPnpNamespaces.AvTransport + "GetTransportInfoResponse").Select(i => i.Element("CurrentTransportState")).FirstOrDefault(i => i != null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var transportStateValue = transportState == null ? null : transportState.Value;
 | 
					            var transportStateValue = transportState?.Value;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (transportStateValue != null
 | 
					            if (transportStateValue != null
 | 
				
			||||||
                && Enum.TryParse(transportStateValue, true, out TRANSPORTSTATE state))
 | 
					                && Enum.TryParse(transportStateValue, true, out TRANSPORTSTATE state))
 | 
				
			||||||
@ -626,8 +643,12 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            var rendererCommands = await GetRenderingProtocolAsync(cancellationToken).ConfigureAwait(false);
 | 
					            var rendererCommands = await GetRenderingProtocolAsync(cancellationToken).ConfigureAwait(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var result = await new SsdpHttpClient(_httpClient).SendCommandAsync(Properties.BaseUrl, service, command.Name, rendererCommands.BuildPost(command, service.ServiceType), false)
 | 
					            var result = await new SsdpHttpClient(_httpClient).SendCommandAsync(
 | 
				
			||||||
                .ConfigureAwait(false);
 | 
					                Properties.BaseUrl,
 | 
				
			||||||
 | 
					                service,
 | 
				
			||||||
 | 
					                command.Name,
 | 
				
			||||||
 | 
					                rendererCommands.BuildPost(command, service.ServiceType),
 | 
				
			||||||
 | 
					                cancellationToken: cancellationToken).ConfigureAwait(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (result == null || result.Document == null)
 | 
					            if (result == null || result.Document == null)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
@ -689,8 +710,12 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            var rendererCommands = await GetRenderingProtocolAsync(cancellationToken).ConfigureAwait(false);
 | 
					            var rendererCommands = await GetRenderingProtocolAsync(cancellationToken).ConfigureAwait(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var result = await new SsdpHttpClient(_httpClient).SendCommandAsync(Properties.BaseUrl, service, command.Name, rendererCommands.BuildPost(command, service.ServiceType), false)
 | 
					            var result = await new SsdpHttpClient(_httpClient).SendCommandAsync(
 | 
				
			||||||
                .ConfigureAwait(false);
 | 
					                Properties.BaseUrl,
 | 
				
			||||||
 | 
					                service,
 | 
				
			||||||
 | 
					                command.Name,
 | 
				
			||||||
 | 
					                rendererCommands.BuildPost(command, service.ServiceType),
 | 
				
			||||||
 | 
					                cancellationToken: cancellationToken).ConfigureAwait(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (result == null || result.Document == null)
 | 
					            if (result == null || result.Document == null)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
 | 
				
			|||||||
@ -27,6 +27,8 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
    public class PlayToController : ISessionController, IDisposable
 | 
					    public class PlayToController : ISessionController, IDisposable
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 | 
					        private static readonly CultureInfo _usCulture = CultureInfo.ReadOnly(new CultureInfo("en-US"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private Device _device;
 | 
					        private Device _device;
 | 
				
			||||||
        private readonly SessionInfo _session;
 | 
					        private readonly SessionInfo _session;
 | 
				
			||||||
        private readonly ISessionManager _sessionManager;
 | 
					        private readonly ISessionManager _sessionManager;
 | 
				
			||||||
@ -45,9 +47,10 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
        private readonly string _serverAddress;
 | 
					        private readonly string _serverAddress;
 | 
				
			||||||
        private readonly string _accessToken;
 | 
					        private readonly string _accessToken;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public bool IsSessionActive => !_disposed && _device != null;
 | 
					        private readonly List<PlaylistItem> _playlist = new List<PlaylistItem>();
 | 
				
			||||||
 | 
					        private int _currentPlaylistIndex;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public bool SupportsMediaControl => IsSessionActive;
 | 
					        private bool _disposed;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public PlayToController(
 | 
					        public PlayToController(
 | 
				
			||||||
            SessionInfo session,
 | 
					            SessionInfo session,
 | 
				
			||||||
@ -83,18 +86,22 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
            _mediaEncoder = mediaEncoder;
 | 
					            _mediaEncoder = mediaEncoder;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public bool IsSessionActive => !_disposed && _device != null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public bool SupportsMediaControl => IsSessionActive;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public void Init(Device device)
 | 
					        public void Init(Device device)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _device = device;
 | 
					            _device = device;
 | 
				
			||||||
            _device.OnDeviceUnavailable = OnDeviceUnavailable;
 | 
					            _device.OnDeviceUnavailable = OnDeviceUnavailable;
 | 
				
			||||||
            _device.PlaybackStart += _device_PlaybackStart;
 | 
					            _device.PlaybackStart += OnDevicePlaybackStart;
 | 
				
			||||||
            _device.PlaybackProgress += _device_PlaybackProgress;
 | 
					            _device.PlaybackProgress += OnDevicePlaybackProgress;
 | 
				
			||||||
            _device.PlaybackStopped += _device_PlaybackStopped;
 | 
					            _device.PlaybackStopped += OnDevicePlaybackStopped;
 | 
				
			||||||
            _device.MediaChanged += _device_MediaChanged;
 | 
					            _device.MediaChanged += OnDeviceMediaChanged;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            _device.Start();
 | 
					            _device.Start();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            _deviceDiscovery.DeviceLeft += _deviceDiscovery_DeviceLeft;
 | 
					            _deviceDiscovery.DeviceLeft += OnDeviceDiscoveryDeviceLeft;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private void OnDeviceUnavailable()
 | 
					        private void OnDeviceUnavailable()
 | 
				
			||||||
@ -110,7 +117,7 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        void _deviceDiscovery_DeviceLeft(object sender, GenericEventArgs<UpnpDeviceInfo> e)
 | 
					        private void OnDeviceDiscoveryDeviceLeft(object sender, GenericEventArgs<UpnpDeviceInfo> e)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var info = e.Argument;
 | 
					            var info = e.Argument;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -125,7 +132,7 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        async void _device_MediaChanged(object sender, MediaChangedEventArgs e)
 | 
					        private async void OnDeviceMediaChanged(object sender, MediaChangedEventArgs e)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            if (_disposed)
 | 
					            if (_disposed)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
@ -137,15 +144,15 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
                var streamInfo = StreamParams.ParseFromUrl(e.OldMediaInfo.Url, _libraryManager, _mediaSourceManager);
 | 
					                var streamInfo = StreamParams.ParseFromUrl(e.OldMediaInfo.Url, _libraryManager, _mediaSourceManager);
 | 
				
			||||||
                if (streamInfo.Item != null)
 | 
					                if (streamInfo.Item != null)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    var positionTicks = GetProgressPositionTicks(e.OldMediaInfo, streamInfo);
 | 
					                    var positionTicks = GetProgressPositionTicks(streamInfo);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    ReportPlaybackStopped(e.OldMediaInfo, streamInfo, positionTicks);
 | 
					                    ReportPlaybackStopped(streamInfo, positionTicks);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                streamInfo = StreamParams.ParseFromUrl(e.NewMediaInfo.Url, _libraryManager, _mediaSourceManager);
 | 
					                streamInfo = StreamParams.ParseFromUrl(e.NewMediaInfo.Url, _libraryManager, _mediaSourceManager);
 | 
				
			||||||
                if (streamInfo.Item == null) return;
 | 
					                if (streamInfo.Item == null) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                var newItemProgress = GetProgressInfo(e.NewMediaInfo, streamInfo);
 | 
					                var newItemProgress = GetProgressInfo(streamInfo);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                await _sessionManager.OnPlaybackStart(newItemProgress).ConfigureAwait(false);
 | 
					                await _sessionManager.OnPlaybackStart(newItemProgress).ConfigureAwait(false);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
@ -155,7 +162,7 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        async void _device_PlaybackStopped(object sender, PlaybackStoppedEventArgs e)
 | 
					        private async void OnDevicePlaybackStopped(object sender, PlaybackStoppedEventArgs e)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            if (_disposed)
 | 
					            if (_disposed)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
@ -168,9 +175,9 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                if (streamInfo.Item == null) return;
 | 
					                if (streamInfo.Item == null) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                var positionTicks = GetProgressPositionTicks(e.MediaInfo, streamInfo);
 | 
					                var positionTicks = GetProgressPositionTicks(streamInfo);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                ReportPlaybackStopped(e.MediaInfo, streamInfo, positionTicks);
 | 
					                ReportPlaybackStopped(streamInfo, positionTicks);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                var mediaSource = await streamInfo.GetMediaSource(CancellationToken.None).ConfigureAwait(false);
 | 
					                var mediaSource = await streamInfo.GetMediaSource(CancellationToken.None).ConfigureAwait(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -194,7 +201,7 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
                }
 | 
					                }
 | 
				
			||||||
                else
 | 
					                else
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    Playlist.Clear();
 | 
					                    _playlist.Clear();
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            catch (Exception ex)
 | 
					            catch (Exception ex)
 | 
				
			||||||
@ -203,7 +210,7 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private async void ReportPlaybackStopped(uBaseObject mediaInfo, StreamParams streamInfo, long? positionTicks)
 | 
					        private async void ReportPlaybackStopped(StreamParams streamInfo, long? positionTicks)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            try
 | 
					            try
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
@ -222,7 +229,7 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        async void _device_PlaybackStart(object sender, PlaybackStartEventArgs e)
 | 
					        private async void OnDevicePlaybackStart(object sender, PlaybackStartEventArgs e)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            if (_disposed)
 | 
					            if (_disposed)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
@ -235,7 +242,7 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                if (info.Item != null)
 | 
					                if (info.Item != null)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    var progress = GetProgressInfo(e.MediaInfo, info);
 | 
					                    var progress = GetProgressInfo(info);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    await _sessionManager.OnPlaybackStart(progress).ConfigureAwait(false);
 | 
					                    await _sessionManager.OnPlaybackStart(progress).ConfigureAwait(false);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
@ -246,7 +253,7 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        async void _device_PlaybackProgress(object sender, PlaybackProgressEventArgs e)
 | 
					        private async void OnDevicePlaybackProgress(object sender, PlaybackProgressEventArgs e)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            if (_disposed)
 | 
					            if (_disposed)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
@ -266,7 +273,7 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                if (info.Item != null)
 | 
					                if (info.Item != null)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    var progress = GetProgressInfo(e.MediaInfo, info);
 | 
					                    var progress = GetProgressInfo(info);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    await _sessionManager.OnPlaybackProgress(progress).ConfigureAwait(false);
 | 
					                    await _sessionManager.OnPlaybackProgress(progress).ConfigureAwait(false);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
@ -277,7 +284,7 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private long? GetProgressPositionTicks(uBaseObject mediaInfo, StreamParams info)
 | 
					        private long? GetProgressPositionTicks(StreamParams info)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var ticks = _device.Position.Ticks;
 | 
					            var ticks = _device.Position.Ticks;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -289,13 +296,13 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
            return ticks;
 | 
					            return ticks;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private PlaybackStartInfo GetProgressInfo(uBaseObject mediaInfo, StreamParams info)
 | 
					        private PlaybackStartInfo GetProgressInfo(StreamParams info)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            return new PlaybackStartInfo
 | 
					            return new PlaybackStartInfo
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                ItemId = info.ItemId,
 | 
					                ItemId = info.ItemId,
 | 
				
			||||||
                SessionId = _session.Id,
 | 
					                SessionId = _session.Id,
 | 
				
			||||||
                PositionTicks = GetProgressPositionTicks(mediaInfo, info),
 | 
					                PositionTicks = GetProgressPositionTicks(info),
 | 
				
			||||||
                IsMuted = _device.IsMuted,
 | 
					                IsMuted = _device.IsMuted,
 | 
				
			||||||
                IsPaused = _device.IsPaused,
 | 
					                IsPaused = _device.IsPaused,
 | 
				
			||||||
                MediaSourceId = info.MediaSourceId,
 | 
					                MediaSourceId = info.MediaSourceId,
 | 
				
			||||||
@ -310,9 +317,7 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
            };
 | 
					            };
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        #region SendCommands
 | 
					        public Task SendPlayCommand(PlayRequest command, CancellationToken cancellationToken)
 | 
				
			||||||
 | 
					 | 
				
			||||||
        public async Task SendPlayCommand(PlayRequest command, CancellationToken cancellationToken)
 | 
					 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _logger.LogDebug("{0} - Received PlayRequest: {1}", this._session.DeviceName, command.PlayCommand);
 | 
					            _logger.LogDebug("{0} - Received PlayRequest: {1}", this._session.DeviceName, command.PlayCommand);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -350,11 +355,12 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            if (command.PlayCommand == PlayCommand.PlayLast)
 | 
					            if (command.PlayCommand == PlayCommand.PlayLast)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                Playlist.AddRange(playlist);
 | 
					                _playlist.AddRange(playlist);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (command.PlayCommand == PlayCommand.PlayNext)
 | 
					            if (command.PlayCommand == PlayCommand.PlayNext)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                Playlist.AddRange(playlist);
 | 
					                _playlist.AddRange(playlist);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (!command.ControllingUserId.Equals(Guid.Empty))
 | 
					            if (!command.ControllingUserId.Equals(Guid.Empty))
 | 
				
			||||||
@ -363,7 +369,7 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
                       _session.DeviceName, _session.RemoteEndPoint, user);
 | 
					                       _session.DeviceName, _session.RemoteEndPoint, user);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            await PlayItems(playlist).ConfigureAwait(false);
 | 
					            return PlayItems(playlist, cancellationToken);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private Task SendPlaystateCommand(PlaystateRequest command, CancellationToken cancellationToken)
 | 
					        private Task SendPlaystateCommand(PlaystateRequest command, CancellationToken cancellationToken)
 | 
				
			||||||
@ -371,7 +377,7 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
            switch (command.Command)
 | 
					            switch (command.Command)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                case PlaystateCommand.Stop:
 | 
					                case PlaystateCommand.Stop:
 | 
				
			||||||
                    Playlist.Clear();
 | 
					                    _playlist.Clear();
 | 
				
			||||||
                    return _device.SetStop(CancellationToken.None);
 | 
					                    return _device.SetStop(CancellationToken.None);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                case PlaystateCommand.Pause:
 | 
					                case PlaystateCommand.Pause:
 | 
				
			||||||
@ -387,10 +393,10 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
                    return Seek(command.SeekPositionTicks ?? 0);
 | 
					                    return Seek(command.SeekPositionTicks ?? 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                case PlaystateCommand.NextTrack:
 | 
					                case PlaystateCommand.NextTrack:
 | 
				
			||||||
                    return SetPlaylistIndex(_currentPlaylistIndex + 1);
 | 
					                    return SetPlaylistIndex(_currentPlaylistIndex + 1, cancellationToken);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                case PlaystateCommand.PreviousTrack:
 | 
					                case PlaystateCommand.PreviousTrack:
 | 
				
			||||||
                    return SetPlaylistIndex(_currentPlaylistIndex - 1);
 | 
					                    return SetPlaylistIndex(_currentPlaylistIndex - 1, cancellationToken);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return Task.CompletedTask;
 | 
					            return Task.CompletedTask;
 | 
				
			||||||
@ -426,14 +432,6 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
            return info.IsDirectStream;
 | 
					            return info.IsDirectStream;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #region Playlist
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private int _currentPlaylistIndex;
 | 
					 | 
				
			||||||
        private readonly List<PlaylistItem> _playlist = new List<PlaylistItem>();
 | 
					 | 
				
			||||||
        private List<PlaylistItem> Playlist => _playlist;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private void AddItemFromId(Guid id, List<BaseItem> list)
 | 
					        private void AddItemFromId(Guid id, List<BaseItem> list)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var item = _libraryManager.GetItemById(id);
 | 
					            var item = _libraryManager.GetItemById(id);
 | 
				
			||||||
@ -451,7 +449,7 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
                _dlnaManager.GetDefaultProfile();
 | 
					                _dlnaManager.GetDefaultProfile();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var mediaSources = item is IHasMediaSources
 | 
					            var mediaSources = item is IHasMediaSources
 | 
				
			||||||
                ? (_mediaSourceManager.GetStaticMediaSources(item, true, user))
 | 
					                ? _mediaSourceManager.GetStaticMediaSources(item, true, user)
 | 
				
			||||||
                : new List<MediaSourceInfo>();
 | 
					                : new List<MediaSourceInfo>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var playlistItem = GetPlaylistItem(item, mediaSources, profile, _session.DeviceId, mediaSourceId, audioStreamIndex, subtitleStreamIndex);
 | 
					            var playlistItem = GetPlaylistItem(item, mediaSources, profile, _session.DeviceId, mediaSourceId, audioStreamIndex, subtitleStreamIndex);
 | 
				
			||||||
@ -459,8 +457,19 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            playlistItem.StreamUrl = DidlBuilder.NormalizeDlnaMediaUrl(playlistItem.StreamInfo.ToUrl(_serverAddress, _accessToken));
 | 
					            playlistItem.StreamUrl = DidlBuilder.NormalizeDlnaMediaUrl(playlistItem.StreamInfo.ToUrl(_serverAddress, _accessToken));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var itemXml = new DidlBuilder(profile, user, _imageProcessor, _serverAddress, _accessToken, _userDataManager, _localization, _mediaSourceManager, _logger, _mediaEncoder)
 | 
					            var itemXml = new DidlBuilder(
 | 
				
			||||||
                .GetItemDidl(_config.GetDlnaConfiguration(), item, user, null, _session.DeviceId, new Filter(), playlistItem.StreamInfo);
 | 
					                profile,
 | 
				
			||||||
 | 
					                user,
 | 
				
			||||||
 | 
					                _imageProcessor,
 | 
				
			||||||
 | 
					                _serverAddress,
 | 
				
			||||||
 | 
					                _accessToken,
 | 
				
			||||||
 | 
					                _userDataManager,
 | 
				
			||||||
 | 
					                _localization,
 | 
				
			||||||
 | 
					                _mediaSourceManager,
 | 
				
			||||||
 | 
					                _logger,
 | 
				
			||||||
 | 
					                _mediaEncoder,
 | 
				
			||||||
 | 
					                _libraryManager)
 | 
				
			||||||
 | 
					                .GetItemDidl(item, user, null, _session.DeviceId, new Filter(), playlistItem.StreamInfo);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            playlistItem.Didl = itemXml;
 | 
					            playlistItem.Didl = itemXml;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -570,30 +579,31 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
        /// Plays the items.
 | 
					        /// Plays the items.
 | 
				
			||||||
        /// </summary>
 | 
					        /// </summary>
 | 
				
			||||||
        /// <param name="items">The items.</param>
 | 
					        /// <param name="items">The items.</param>
 | 
				
			||||||
        /// <returns></returns>
 | 
					        /// <param name="cancellationToken">The cancellation token.</param>
 | 
				
			||||||
        private async Task<bool> PlayItems(IEnumerable<PlaylistItem> items)
 | 
					        /// <returns><c>true</c> on success.</returns>
 | 
				
			||||||
 | 
					        private async Task<bool> PlayItems(IEnumerable<PlaylistItem> items, CancellationToken cancellationToken = default)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            Playlist.Clear();
 | 
					            _playlist.Clear();
 | 
				
			||||||
            Playlist.AddRange(items);
 | 
					            _playlist.AddRange(items);
 | 
				
			||||||
            _logger.LogDebug("{0} - Playing {1} items", _session.DeviceName, Playlist.Count);
 | 
					            _logger.LogDebug("{0} - Playing {1} items", _session.DeviceName, _playlist.Count);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            await SetPlaylistIndex(0).ConfigureAwait(false);
 | 
					            await SetPlaylistIndex(0, cancellationToken).ConfigureAwait(false);
 | 
				
			||||||
            return true;
 | 
					            return true;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private async Task SetPlaylistIndex(int index)
 | 
					        private async Task SetPlaylistIndex(int index, CancellationToken cancellationToken = default)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            if (index < 0 || index >= Playlist.Count)
 | 
					            if (index < 0 || index >= _playlist.Count)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                Playlist.Clear();
 | 
					                _playlist.Clear();
 | 
				
			||||||
                await _device.SetStop(CancellationToken.None);
 | 
					                await _device.SetStop(cancellationToken).ConfigureAwait(false);
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            _currentPlaylistIndex = index;
 | 
					            _currentPlaylistIndex = index;
 | 
				
			||||||
            var currentitem = Playlist[index];
 | 
					            var currentitem = _playlist[index];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            await _device.SetAvTransport(currentitem.StreamUrl, GetDlnaHeaders(currentitem), currentitem.Didl, CancellationToken.None);
 | 
					            await _device.SetAvTransport(currentitem.StreamUrl, GetDlnaHeaders(currentitem), currentitem.Didl, cancellationToken).ConfigureAwait(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var streamInfo = currentitem.StreamInfo;
 | 
					            var streamInfo = currentitem.StreamInfo;
 | 
				
			||||||
            if (streamInfo.StartPositionTicks > 0 && EnableClientSideSeek(streamInfo))
 | 
					            if (streamInfo.StartPositionTicks > 0 && EnableClientSideSeek(streamInfo))
 | 
				
			||||||
@ -602,10 +612,7 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        #endregion
 | 
					        /// <inheritdoc />
 | 
				
			||||||
 | 
					 | 
				
			||||||
        private bool _disposed;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public void Dispose()
 | 
					        public void Dispose()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            Dispose(true);
 | 
					            Dispose(true);
 | 
				
			||||||
@ -624,19 +631,17 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
                _device.Dispose();
 | 
					                _device.Dispose();
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            _device.PlaybackStart -= _device_PlaybackStart;
 | 
					            _device.PlaybackStart -= OnDevicePlaybackStart;
 | 
				
			||||||
            _device.PlaybackProgress -= _device_PlaybackProgress;
 | 
					            _device.PlaybackProgress -= OnDevicePlaybackProgress;
 | 
				
			||||||
            _device.PlaybackStopped -= _device_PlaybackStopped;
 | 
					            _device.PlaybackStopped -= OnDevicePlaybackStopped;
 | 
				
			||||||
            _device.MediaChanged -= _device_MediaChanged;
 | 
					            _device.MediaChanged -= OnDeviceMediaChanged;
 | 
				
			||||||
            _deviceDiscovery.DeviceLeft -= _deviceDiscovery_DeviceLeft;
 | 
					            _deviceDiscovery.DeviceLeft -= OnDeviceDiscoveryDeviceLeft;
 | 
				
			||||||
            _device.OnDeviceUnavailable = null;
 | 
					            _device.OnDeviceUnavailable = null;
 | 
				
			||||||
            _device = null;
 | 
					            _device = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            _disposed = true;
 | 
					            _disposed = true;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private static readonly CultureInfo _usCulture = CultureInfo.ReadOnly(new CultureInfo("en-US"));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private Task SendGeneralCommand(GeneralCommand command, CancellationToken cancellationToken)
 | 
					        private Task SendGeneralCommand(GeneralCommand command, CancellationToken cancellationToken)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            if (Enum.TryParse(command.Name, true, out GeneralCommandType commandType))
 | 
					            if (Enum.TryParse(command.Name, true, out GeneralCommandType commandType))
 | 
				
			||||||
@ -713,7 +718,7 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                if (info.Item != null)
 | 
					                if (info.Item != null)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    var newPosition = GetProgressPositionTicks(media, info) ?? 0;
 | 
					                    var newPosition = GetProgressPositionTicks(info) ?? 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    var user = !_session.UserId.Equals(Guid.Empty) ? _userManager.GetUserById(_session.UserId) : null;
 | 
					                    var user = !_session.UserId.Equals(Guid.Empty) ? _userManager.GetUserById(_session.UserId) : null;
 | 
				
			||||||
                    var newItem = CreatePlaylistItem(info.Item, user, newPosition, info.MediaSourceId, newIndex, info.SubtitleStreamIndex);
 | 
					                    var newItem = CreatePlaylistItem(info.Item, user, newPosition, info.MediaSourceId, newIndex, info.SubtitleStreamIndex);
 | 
				
			||||||
@ -738,7 +743,7 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                if (info.Item != null)
 | 
					                if (info.Item != null)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    var newPosition = GetProgressPositionTicks(media, info) ?? 0;
 | 
					                    var newPosition = GetProgressPositionTicks(info) ?? 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    var user = !_session.UserId.Equals(Guid.Empty) ? _userManager.GetUserById(_session.UserId) : null;
 | 
					                    var user = !_session.UserId.Equals(Guid.Empty) ? _userManager.GetUserById(_session.UserId) : null;
 | 
				
			||||||
                    var newItem = CreatePlaylistItem(info.Item, user, newPosition, info.MediaSourceId, info.AudioStreamIndex, newIndex);
 | 
					                    var newItem = CreatePlaylistItem(info.Item, user, newPosition, info.MediaSourceId, info.AudioStreamIndex, newIndex);
 | 
				
			||||||
@ -852,8 +857,11 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
                    return request;
 | 
					                    return request;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                var index = url.IndexOf('?');
 | 
					                var index = url.IndexOf('?', StringComparison.Ordinal);
 | 
				
			||||||
                if (index == -1) return request;
 | 
					                if (index == -1)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    return request;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                var query = url.Substring(index + 1);
 | 
					                var query = url.Substring(index + 1);
 | 
				
			||||||
                Dictionary<string, string> values = QueryHelpers.ParseQuery(query).ToDictionary(kv => kv.Key, kv => kv.Value.ToString());
 | 
					                Dictionary<string, string> values = QueryHelpers.ParseQuery(query).ToDictionary(kv => kv.Key, kv => kv.Value.ToString());
 | 
				
			||||||
 | 
				
			|||||||
@ -23,7 +23,7 @@ using Microsoft.Extensions.Logging;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
namespace Emby.Dlna.PlayTo
 | 
					namespace Emby.Dlna.PlayTo
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    public class PlayToManager : IDisposable
 | 
					    public sealed class PlayToManager : IDisposable
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        private readonly ILogger _logger;
 | 
					        private readonly ILogger _logger;
 | 
				
			||||||
        private readonly ISessionManager _sessionManager;
 | 
					        private readonly ISessionManager _sessionManager;
 | 
				
			||||||
@ -231,6 +231,7 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /// <inheritdoc />
 | 
				
			||||||
        public void Dispose()
 | 
					        public void Dispose()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _deviceDiscovery.DeviceDiscovered -= OnDeviceDiscoveryDeviceDiscovered;
 | 
					            _deviceDiscovery.DeviceDiscovered -= OnDeviceDiscoveryDeviceDiscovered;
 | 
				
			||||||
@ -244,6 +245,9 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            _sessionLock.Dispose();
 | 
				
			||||||
 | 
					            _disposeCancellationTokenSource.Dispose();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            _disposed = true;
 | 
					            _disposed = true;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -32,18 +32,15 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
            DeviceService service,
 | 
					            DeviceService service,
 | 
				
			||||||
            string command,
 | 
					            string command,
 | 
				
			||||||
            string postData,
 | 
					            string postData,
 | 
				
			||||||
            bool logRequest = true,
 | 
					            string header = null,
 | 
				
			||||||
            string header = null)
 | 
					            CancellationToken cancellationToken = default)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var cancellationToken = CancellationToken.None;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            var url = NormalizeServiceUrl(baseUrl, service.ControlUrl);
 | 
					            var url = NormalizeServiceUrl(baseUrl, service.ControlUrl);
 | 
				
			||||||
            using (var response = await PostSoapDataAsync(
 | 
					            using (var response = await PostSoapDataAsync(
 | 
				
			||||||
                url,
 | 
					                url,
 | 
				
			||||||
                $"\"{service.ServiceType}#{command}\"",
 | 
					                $"\"{service.ServiceType}#{command}\"",
 | 
				
			||||||
                postData,
 | 
					                postData,
 | 
				
			||||||
                header,
 | 
					                header,
 | 
				
			||||||
                logRequest,
 | 
					 | 
				
			||||||
                cancellationToken)
 | 
					                cancellationToken)
 | 
				
			||||||
                .ConfigureAwait(false))
 | 
					                .ConfigureAwait(false))
 | 
				
			||||||
            using (var stream = response.Content)
 | 
					            using (var stream = response.Content)
 | 
				
			||||||
@ -63,7 +60,7 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
                return serviceUrl;
 | 
					                return serviceUrl;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (!serviceUrl.StartsWith("/"))
 | 
					            if (!serviceUrl.StartsWith("/", StringComparison.Ordinal))
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                serviceUrl = "/" + serviceUrl;
 | 
					                serviceUrl = "/" + serviceUrl;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
@ -127,7 +124,6 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
            string soapAction,
 | 
					            string soapAction,
 | 
				
			||||||
            string postData,
 | 
					            string postData,
 | 
				
			||||||
            string header,
 | 
					            string header,
 | 
				
			||||||
            bool logRequest,
 | 
					 | 
				
			||||||
            CancellationToken cancellationToken)
 | 
					            CancellationToken cancellationToken)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            if (soapAction[0] != '\"')
 | 
					            if (soapAction[0] != '\"')
 | 
				
			||||||
 | 
				
			|||||||
@ -134,19 +134,19 @@ namespace Emby.Notifications.Api
 | 
				
			|||||||
            _userManager = userManager;
 | 
					            _userManager = userManager;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request")]
 | 
					        [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request", Justification = "Required for ServiceStack")]
 | 
				
			||||||
        public object Get(GetNotificationTypes request)
 | 
					        public object Get(GetNotificationTypes request)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            return _notificationManager.GetNotificationTypes();
 | 
					            return _notificationManager.GetNotificationTypes();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request")]
 | 
					        [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request", Justification = "Required for ServiceStack")]
 | 
				
			||||||
        public object Get(GetNotificationServices request)
 | 
					        public object Get(GetNotificationServices request)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            return _notificationManager.GetNotificationServices().ToList();
 | 
					            return _notificationManager.GetNotificationServices().ToList();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request")]
 | 
					        [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request", Justification = "Required for ServiceStack")]
 | 
				
			||||||
        public object Get(GetNotificationsSummary request)
 | 
					        public object Get(GetNotificationsSummary request)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            return new NotificationsSummary
 | 
					            return new NotificationsSummary
 | 
				
			||||||
@ -170,17 +170,17 @@ namespace Emby.Notifications.Api
 | 
				
			|||||||
            return _notificationManager.SendNotification(notification, CancellationToken.None);
 | 
					            return _notificationManager.SendNotification(notification, CancellationToken.None);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request")]
 | 
					        [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request", Justification = "Required for ServiceStack")]
 | 
				
			||||||
        public void Post(MarkRead request)
 | 
					        public void Post(MarkRead request)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request")]
 | 
					        [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request", Justification = "Required for ServiceStack")]
 | 
				
			||||||
        public void Post(MarkUnread request)
 | 
					        public void Post(MarkUnread request)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request")]
 | 
					        [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request", Justification = "Required for ServiceStack")]
 | 
				
			||||||
        public object Get(GetNotifications request)
 | 
					        public object Get(GetNotifications request)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            return new NotificationResult();
 | 
					            return new NotificationResult();
 | 
				
			||||||
 | 
				
			|||||||
@ -118,6 +118,11 @@ namespace Emby.Server.Implementations
 | 
				
			|||||||
    /// </summary>
 | 
					    /// </summary>
 | 
				
			||||||
    public abstract class ApplicationHost : IServerApplicationHost, IDisposable
 | 
					    public abstract class ApplicationHost : IServerApplicationHost, IDisposable
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// The environment variable prefixes to log at server startup.
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        private static readonly string[] _relevantEnvVarPrefixes = { "JELLYFIN_", "DOTNET_", "ASPNETCORE_" };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private SqliteUserRepository _userRepository;
 | 
					        private SqliteUserRepository _userRepository;
 | 
				
			||||||
        private SqliteDisplayPreferencesRepository _displayPreferencesRepository;
 | 
					        private SqliteDisplayPreferencesRepository _displayPreferencesRepository;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -889,18 +894,18 @@ namespace Emby.Server.Implementations
 | 
				
			|||||||
                .GetCommandLineArgs()
 | 
					                .GetCommandLineArgs()
 | 
				
			||||||
                .Distinct();
 | 
					                .Distinct();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // Get all 'JELLYFIN_' prefixed environment variables
 | 
					            // Get all relevant environment variables
 | 
				
			||||||
            var allEnvVars = Environment.GetEnvironmentVariables();
 | 
					            var allEnvVars = Environment.GetEnvironmentVariables();
 | 
				
			||||||
            var jellyfinEnvVars = new Dictionary<object, object>();
 | 
					            var relevantEnvVars = new Dictionary<object, object>();
 | 
				
			||||||
            foreach (var key in allEnvVars.Keys)
 | 
					            foreach (var key in allEnvVars.Keys)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                if (key.ToString().StartsWith("JELLYFIN_", StringComparison.OrdinalIgnoreCase))
 | 
					                if (_relevantEnvVarPrefixes.Any(prefix => key.ToString().StartsWith(prefix, StringComparison.OrdinalIgnoreCase)))
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    jellyfinEnvVars.Add(key, allEnvVars[key]);
 | 
					                    relevantEnvVars.Add(key, allEnvVars[key]);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            logger.LogInformation("Environment Variables: {EnvVars}", jellyfinEnvVars);
 | 
					            logger.LogInformation("Environment Variables: {EnvVars}", relevantEnvVars);
 | 
				
			||||||
            logger.LogInformation("Arguments: {Args}", commandLineArgs);
 | 
					            logger.LogInformation("Arguments: {Args}", commandLineArgs);
 | 
				
			||||||
            logger.LogInformation("Operating system: {OS}", OperatingSystem.Name);
 | 
					            logger.LogInformation("Operating system: {OS}", OperatingSystem.Name);
 | 
				
			||||||
            logger.LogInformation("Architecture: {Architecture}", RuntimeInformation.OSArchitecture);
 | 
					            logger.LogInformation("Architecture: {Architecture}", RuntimeInformation.OSArchitecture);
 | 
				
			||||||
 | 
				
			|||||||
@ -5011,6 +5011,11 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            commandText += " order by ListOrder";
 | 
					            commandText += " order by ListOrder";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (query.Limit > 0)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                commandText += " LIMIT " + query.Limit;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            using (var connection = GetConnection(true))
 | 
					            using (var connection = GetConnection(true))
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                var list = new List<string>();
 | 
					                var list = new List<string>();
 | 
				
			||||||
@ -5049,6 +5054,11 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            commandText += " order by ListOrder";
 | 
					            commandText += " order by ListOrder";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (query.Limit > 0)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                commandText += " LIMIT " + query.Limit;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            using (var connection = GetConnection(true))
 | 
					            using (var connection = GetConnection(true))
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                var list = new List<PersonInfo>();
 | 
					                var list = new List<PersonInfo>();
 | 
				
			||||||
 | 
				
			|||||||
@ -96,13 +96,13 @@ namespace Emby.Server.Implementations.HttpClientManager
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            switch (options.DecompressionMethod)
 | 
					            switch (options.DecompressionMethod)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                case CompressionMethod.Deflate | CompressionMethod.Gzip:
 | 
					                case CompressionMethods.Deflate | CompressionMethods.Gzip:
 | 
				
			||||||
                    request.Headers.Add(HeaderNames.AcceptEncoding, new[] { "gzip", "deflate" });
 | 
					                    request.Headers.Add(HeaderNames.AcceptEncoding, new[] { "gzip", "deflate" });
 | 
				
			||||||
                    break;
 | 
					                    break;
 | 
				
			||||||
                case CompressionMethod.Deflate:
 | 
					                case CompressionMethods.Deflate:
 | 
				
			||||||
                    request.Headers.Add(HeaderNames.AcceptEncoding, "deflate");
 | 
					                    request.Headers.Add(HeaderNames.AcceptEncoding, "deflate");
 | 
				
			||||||
                    break;
 | 
					                    break;
 | 
				
			||||||
                case CompressionMethod.Gzip:
 | 
					                case CompressionMethods.Gzip:
 | 
				
			||||||
                    request.Headers.Add(HeaderNames.AcceptEncoding, "gzip");
 | 
					                    request.Headers.Add(HeaderNames.AcceptEncoding, "gzip");
 | 
				
			||||||
                    break;
 | 
					                    break;
 | 
				
			||||||
                default:
 | 
					                default:
 | 
				
			||||||
@ -239,15 +239,10 @@ namespace Emby.Server.Implementations.HttpClientManager
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            var httpWebRequest = GetRequestMessage(options, httpMethod);
 | 
					            var httpWebRequest = GetRequestMessage(options, httpMethod);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (options.RequestContentBytes != null
 | 
					            if (!string.IsNullOrEmpty(options.RequestContent)
 | 
				
			||||||
                || !string.IsNullOrEmpty(options.RequestContent)
 | 
					 | 
				
			||||||
                || httpMethod == HttpMethod.Post)
 | 
					                || httpMethod == HttpMethod.Post)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                if (options.RequestContentBytes != null)
 | 
					                if (options.RequestContent != null)
 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    httpWebRequest.Content = new ByteArrayContent(options.RequestContentBytes);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                else if (options.RequestContent != null)
 | 
					 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    httpWebRequest.Content = new StringContent(
 | 
					                    httpWebRequest.Content = new StringContent(
 | 
				
			||||||
                        options.RequestContent,
 | 
					                        options.RequestContent,
 | 
				
			||||||
 | 
				
			|||||||
@ -72,7 +72,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
 | 
				
			|||||||
                UserAgent = "Emby/3.0",
 | 
					                UserAgent = "Emby/3.0",
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                // Shouldn't matter but may cause issues
 | 
					                // Shouldn't matter but may cause issues
 | 
				
			||||||
                DecompressionMethod = CompressionMethod.None
 | 
					                DecompressionMethod = CompressionMethods.None
 | 
				
			||||||
            };
 | 
					            };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            using (var response = await _httpClient.SendAsync(httpRequestOptions, HttpMethod.Get).ConfigureAwait(false))
 | 
					            using (var response = await _httpClient.SendAsync(httpRequestOptions, HttpMethod.Get).ConfigureAwait(false))
 | 
				
			||||||
 | 
				
			|||||||
@ -635,7 +635,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
 | 
				
			|||||||
            ListingsProviderInfo providerInfo)
 | 
					            ListingsProviderInfo providerInfo)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            // Schedules direct requires that the client support compression and will return a 400 response without it
 | 
					            // Schedules direct requires that the client support compression and will return a 400 response without it
 | 
				
			||||||
            options.DecompressionMethod = CompressionMethod.Deflate;
 | 
					            options.DecompressionMethod = CompressionMethods.Deflate;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            try
 | 
					            try
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
@ -665,7 +665,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
 | 
				
			|||||||
            ListingsProviderInfo providerInfo)
 | 
					            ListingsProviderInfo providerInfo)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            // Schedules direct requires that the client support compression and will return a 400 response without it
 | 
					            // Schedules direct requires that the client support compression and will return a 400 response without it
 | 
				
			||||||
            options.DecompressionMethod = CompressionMethod.Deflate;
 | 
					            options.DecompressionMethod = CompressionMethods.Deflate;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            try
 | 
					            try
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
 | 
				
			|||||||
@ -83,7 +83,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
 | 
				
			|||||||
                {
 | 
					                {
 | 
				
			||||||
                    CancellationToken = cancellationToken,
 | 
					                    CancellationToken = cancellationToken,
 | 
				
			||||||
                    Url = path,
 | 
					                    Url = path,
 | 
				
			||||||
                    DecompressionMethod = CompressionMethod.Gzip,
 | 
					                    DecompressionMethod = CompressionMethods.Gzip,
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
                HttpMethod.Get).ConfigureAwait(false))
 | 
					                HttpMethod.Get).ConfigureAwait(false))
 | 
				
			||||||
            using (var stream = res.Content)
 | 
					            using (var stream = res.Content)
 | 
				
			||||||
 | 
				
			|||||||
@ -59,7 +59,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
 | 
				
			|||||||
                Url = url,
 | 
					                Url = url,
 | 
				
			||||||
                CancellationToken = CancellationToken.None,
 | 
					                CancellationToken = CancellationToken.None,
 | 
				
			||||||
                BufferContent = false,
 | 
					                BufferContent = false,
 | 
				
			||||||
                DecompressionMethod = CompressionMethod.None
 | 
					                DecompressionMethod = CompressionMethods.None
 | 
				
			||||||
            };
 | 
					            };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            foreach (var header in mediaSource.RequiredHttpHeaders)
 | 
					            foreach (var header in mediaSource.RequiredHttpHeaders)
 | 
				
			||||||
 | 
				
			|||||||
@ -90,7 +90,17 @@
 | 
				
			|||||||
    "UserPolicyUpdatedWithName": "تم تحديث سياسة المستخدم {0}",
 | 
					    "UserPolicyUpdatedWithName": "تم تحديث سياسة المستخدم {0}",
 | 
				
			||||||
    "UserStartedPlayingItemWithValues": "قام {0} ببدء تشغيل {1} على {2}",
 | 
					    "UserStartedPlayingItemWithValues": "قام {0} ببدء تشغيل {1} على {2}",
 | 
				
			||||||
    "UserStoppedPlayingItemWithValues": "قام {0} بإيقاف تشغيل {1} على {2}",
 | 
					    "UserStoppedPlayingItemWithValues": "قام {0} بإيقاف تشغيل {1} على {2}",
 | 
				
			||||||
    "ValueHasBeenAddedToLibrary": "{0} تم اضافتها الى مكتبة الوسائط",
 | 
					    "ValueHasBeenAddedToLibrary": "تمت اضافت {0} إلى مكتبة الوسائط",
 | 
				
			||||||
    "ValueSpecialEpisodeName": "مميز - {0}",
 | 
					    "ValueSpecialEpisodeName": "خاص - {0}",
 | 
				
			||||||
    "VersionNumber": "الإصدار رقم {0}"
 | 
					    "VersionNumber": "النسخة {0}",
 | 
				
			||||||
 | 
					    "TaskCleanCacheDescription": "يحذف ملفات ذاكرة التخزين المؤقت التي لم يعد النظام بحاجة إليها.",
 | 
				
			||||||
 | 
					    "TaskCleanCache": "احذف مجلد ذاكرة التخزين المؤقت",
 | 
				
			||||||
 | 
					    "TasksChannelsCategory": "قنوات الإنترنت",
 | 
				
			||||||
 | 
					    "TasksLibraryCategory": "مكتبة",
 | 
				
			||||||
 | 
					    "TasksMaintenanceCategory": "صيانة",
 | 
				
			||||||
 | 
					    "TaskRefreshLibraryDescription": "يقوم بفصح مكتبة الوسائط الخاصة بك بحثًا عن ملفات جديدة وتحديث البيانات الوصفية.",
 | 
				
			||||||
 | 
					    "TaskRefreshLibrary": "افحص مكتبة الوسائط",
 | 
				
			||||||
 | 
					    "TaskRefreshChapterImagesDescription": "إنشاء صور مصغرة لمقاطع الفيديو ذات فصول.",
 | 
				
			||||||
 | 
					    "TaskRefreshChapterImages": "استخراج صور الفصل",
 | 
				
			||||||
 | 
					    "TasksApplicationCategory": "تطبيق"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -11,11 +11,11 @@
 | 
				
			|||||||
    "Collections": "Colecciones",
 | 
					    "Collections": "Colecciones",
 | 
				
			||||||
    "DeviceOfflineWithName": "{0} se ha desconectado",
 | 
					    "DeviceOfflineWithName": "{0} se ha desconectado",
 | 
				
			||||||
    "DeviceOnlineWithName": "{0} está conectado",
 | 
					    "DeviceOnlineWithName": "{0} está conectado",
 | 
				
			||||||
    "FailedLoginAttemptWithUserName": "Error al intentar iniciar sesión desde {0}",
 | 
					    "FailedLoginAttemptWithUserName": "Error al intentar iniciar sesión de {0}",
 | 
				
			||||||
    "Favorites": "Favoritos",
 | 
					    "Favorites": "Favoritos",
 | 
				
			||||||
    "Folders": "Carpetas",
 | 
					    "Folders": "Carpetas",
 | 
				
			||||||
    "Genres": "Géneros",
 | 
					    "Genres": "Géneros",
 | 
				
			||||||
    "HeaderAlbumArtists": "Artistas de álbumes",
 | 
					    "HeaderAlbumArtists": "Artistas de álbum",
 | 
				
			||||||
    "HeaderCameraUploads": "Subidas de cámara",
 | 
					    "HeaderCameraUploads": "Subidas de cámara",
 | 
				
			||||||
    "HeaderContinueWatching": "Continuar viendo",
 | 
					    "HeaderContinueWatching": "Continuar viendo",
 | 
				
			||||||
    "HeaderFavoriteAlbums": "Álbumes favoritos",
 | 
					    "HeaderFavoriteAlbums": "Álbumes favoritos",
 | 
				
			||||||
@ -24,7 +24,7 @@
 | 
				
			|||||||
    "HeaderFavoriteShows": "Programas favoritos",
 | 
					    "HeaderFavoriteShows": "Programas favoritos",
 | 
				
			||||||
    "HeaderFavoriteSongs": "Canciones favoritas",
 | 
					    "HeaderFavoriteSongs": "Canciones favoritas",
 | 
				
			||||||
    "HeaderLiveTV": "TV en vivo",
 | 
					    "HeaderLiveTV": "TV en vivo",
 | 
				
			||||||
    "HeaderNextUp": "Continuar Viendo",
 | 
					    "HeaderNextUp": "A Continuación",
 | 
				
			||||||
    "HeaderRecordingGroups": "Grupos de grabación",
 | 
					    "HeaderRecordingGroups": "Grupos de grabación",
 | 
				
			||||||
    "HomeVideos": "Videos caseros",
 | 
					    "HomeVideos": "Videos caseros",
 | 
				
			||||||
    "Inherit": "Heredar",
 | 
					    "Inherit": "Heredar",
 | 
				
			||||||
@ -35,47 +35,47 @@
 | 
				
			|||||||
    "Latest": "Últimos",
 | 
					    "Latest": "Últimos",
 | 
				
			||||||
    "MessageApplicationUpdated": "El servidor Jellyfin fue actualizado",
 | 
					    "MessageApplicationUpdated": "El servidor Jellyfin fue actualizado",
 | 
				
			||||||
    "MessageApplicationUpdatedTo": "Se ha actualizado el servidor Jellyfin a la versión {0}",
 | 
					    "MessageApplicationUpdatedTo": "Se ha actualizado el servidor Jellyfin a la versión {0}",
 | 
				
			||||||
    "MessageNamedServerConfigurationUpdatedWithValue": "Fue actualizada la sección {0} de la configuración del servidor",
 | 
					    "MessageNamedServerConfigurationUpdatedWithValue": "Se ha actualizado la sección {0} de la configuración del servidor",
 | 
				
			||||||
    "MessageServerConfigurationUpdated": "Fue actualizada la configuración del servidor",
 | 
					    "MessageServerConfigurationUpdated": "Se ha actualizado la configuración del servidor",
 | 
				
			||||||
    "MixedContent": "Contenido mixto",
 | 
					    "MixedContent": "Contenido mezclado",
 | 
				
			||||||
    "Movies": "Películas",
 | 
					    "Movies": "Películas",
 | 
				
			||||||
    "Music": "Música",
 | 
					    "Music": "Música",
 | 
				
			||||||
    "MusicVideos": "Videos musicales",
 | 
					    "MusicVideos": "Videos musicales",
 | 
				
			||||||
    "NameInstallFailed": "{0} error de instalación",
 | 
					    "NameInstallFailed": "{0} instalación fallida",
 | 
				
			||||||
    "NameSeasonNumber": "Temporada {0}",
 | 
					    "NameSeasonNumber": "Temporada {0}",
 | 
				
			||||||
    "NameSeasonUnknown": "Temporada desconocida",
 | 
					    "NameSeasonUnknown": "Temporada desconocida",
 | 
				
			||||||
    "NewVersionIsAvailable": "Disponible una nueva versión de Jellyfin para descargar.",
 | 
					    "NewVersionIsAvailable": "Una nueva versión del Servidor Jellyfin está disponible para descargar.",
 | 
				
			||||||
    "NotificationOptionApplicationUpdateAvailable": "Actualización de la aplicación disponible",
 | 
					    "NotificationOptionApplicationUpdateAvailable": "Actualización de la aplicación disponible",
 | 
				
			||||||
    "NotificationOptionApplicationUpdateInstalled": "Actualización de la aplicación instalada",
 | 
					    "NotificationOptionApplicationUpdateInstalled": "Actualización de la aplicación instalada",
 | 
				
			||||||
    "NotificationOptionAudioPlayback": "Se inició la reproducción de audio",
 | 
					    "NotificationOptionAudioPlayback": "Se inició la reproducción de audio",
 | 
				
			||||||
    "NotificationOptionAudioPlaybackStopped": "Se detuvo la reproducción de audio",
 | 
					    "NotificationOptionAudioPlaybackStopped": "Se detuvo la reproducción de audio",
 | 
				
			||||||
    "NotificationOptionCameraImageUploaded": "Imagen de la cámara cargada",
 | 
					    "NotificationOptionCameraImageUploaded": "Imagen de la cámara subida",
 | 
				
			||||||
    "NotificationOptionInstallationFailed": "Error de instalación",
 | 
					    "NotificationOptionInstallationFailed": "Error de instalación",
 | 
				
			||||||
    "NotificationOptionNewLibraryContent": "Nuevo contenido añadido",
 | 
					    "NotificationOptionNewLibraryContent": "Nuevo contenido añadido",
 | 
				
			||||||
    "NotificationOptionPluginError": "Error en plugin",
 | 
					    "NotificationOptionPluginError": "Falla de complemento",
 | 
				
			||||||
    "NotificationOptionPluginInstalled": "Plugin instalado",
 | 
					    "NotificationOptionPluginInstalled": "Complemento instalado",
 | 
				
			||||||
    "NotificationOptionPluginUninstalled": "Plugin desinstalado",
 | 
					    "NotificationOptionPluginUninstalled": "Complemento desinstalado",
 | 
				
			||||||
    "NotificationOptionPluginUpdateInstalled": "Actualización del complemento instalada",
 | 
					    "NotificationOptionPluginUpdateInstalled": "Actualización de complemento instalada",
 | 
				
			||||||
    "NotificationOptionServerRestartRequired": "Se requiere reinicio del servidor",
 | 
					    "NotificationOptionServerRestartRequired": "Se necesita reiniciar el Servidor",
 | 
				
			||||||
    "NotificationOptionTaskFailed": "Error de tarea programada",
 | 
					    "NotificationOptionTaskFailed": "Falla de tarea programada",
 | 
				
			||||||
    "NotificationOptionUserLockedOut": "Usuario bloqueado",
 | 
					    "NotificationOptionUserLockedOut": "Usuario bloqueado",
 | 
				
			||||||
    "NotificationOptionVideoPlayback": "Se inició la reproducción de video",
 | 
					    "NotificationOptionVideoPlayback": "Se inició la reproducción de video",
 | 
				
			||||||
    "NotificationOptionVideoPlaybackStopped": "Reproducción de video detenida",
 | 
					    "NotificationOptionVideoPlaybackStopped": "Reproducción de video detenida",
 | 
				
			||||||
    "Photos": "Fotos",
 | 
					    "Photos": "Fotos",
 | 
				
			||||||
    "Playlists": "Listas de reproducción",
 | 
					    "Playlists": "Listas de reproducción",
 | 
				
			||||||
    "Plugin": "Plugin",
 | 
					    "Plugin": "Complemento",
 | 
				
			||||||
    "PluginInstalledWithName": "{0} fue instalado",
 | 
					    "PluginInstalledWithName": "{0} fue instalado",
 | 
				
			||||||
    "PluginUninstalledWithName": "{0} fue desinstalado",
 | 
					    "PluginUninstalledWithName": "{0} fue desinstalado",
 | 
				
			||||||
    "PluginUpdatedWithName": "{0} fue actualizado",
 | 
					    "PluginUpdatedWithName": "{0} fue actualizado",
 | 
				
			||||||
    "ProviderValue": "Proveedor: {0}",
 | 
					    "ProviderValue": "Proveedor: {0}",
 | 
				
			||||||
    "ScheduledTaskFailedWithName": "{0} falló",
 | 
					    "ScheduledTaskFailedWithName": "{0} falló",
 | 
				
			||||||
    "ScheduledTaskStartedWithName": "{0} iniciada",
 | 
					    "ScheduledTaskStartedWithName": "{0} iniciado",
 | 
				
			||||||
    "ServerNameNeedsToBeRestarted": "{0} necesita ser reiniciado",
 | 
					    "ServerNameNeedsToBeRestarted": "{0} necesita ser reiniciado",
 | 
				
			||||||
    "Shows": "Series",
 | 
					    "Shows": "Series",
 | 
				
			||||||
    "Songs": "Canciones",
 | 
					    "Songs": "Canciones",
 | 
				
			||||||
    "StartupEmbyServerIsLoading": "Jellyfin Server se está cargando. Vuelve a intentarlo en breve.",
 | 
					    "StartupEmbyServerIsLoading": "El servidor Jellyfin se está cargando. Vuelve a intentarlo en breve.",
 | 
				
			||||||
    "SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}",
 | 
					    "SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}",
 | 
				
			||||||
    "SubtitleDownloadFailureFromForItem": "Fallo de descarga de subtítulos desde {0} para {1}",
 | 
					    "SubtitleDownloadFailureFromForItem": "Falló la descarga de subtitulos desde {0} para {1}",
 | 
				
			||||||
    "Sync": "Sincronizar",
 | 
					    "Sync": "Sincronizar",
 | 
				
			||||||
    "System": "Sistema",
 | 
					    "System": "Sistema",
 | 
				
			||||||
    "TvShows": "Series de TV",
 | 
					    "TvShows": "Series de TV",
 | 
				
			||||||
@ -87,10 +87,32 @@
 | 
				
			|||||||
    "UserOfflineFromDevice": "{0} se ha desconectado de {1}",
 | 
					    "UserOfflineFromDevice": "{0} se ha desconectado de {1}",
 | 
				
			||||||
    "UserOnlineFromDevice": "{0} está en línea desde {1}",
 | 
					    "UserOnlineFromDevice": "{0} está en línea desde {1}",
 | 
				
			||||||
    "UserPasswordChangedWithName": "Se ha cambiado la contraseña para el usuario {0}",
 | 
					    "UserPasswordChangedWithName": "Se ha cambiado la contraseña para el usuario {0}",
 | 
				
			||||||
    "UserPolicyUpdatedWithName": "Actualizada política de usuario para {0}",
 | 
					    "UserPolicyUpdatedWithName": "Las política de usuario ha sido actualizada para {0}",
 | 
				
			||||||
    "UserStartedPlayingItemWithValues": "{0} está reproduciendo {1} en {2}",
 | 
					    "UserStartedPlayingItemWithValues": "{0} está reproduciendo {1} en {2}",
 | 
				
			||||||
    "UserStoppedPlayingItemWithValues": "{0} ha terminado de reproducir {1} en {2}",
 | 
					    "UserStoppedPlayingItemWithValues": "{0} ha terminado de reproducir {1} en {2}",
 | 
				
			||||||
    "ValueHasBeenAddedToLibrary": "{0} ha sido añadido a tu biblioteca multimedia",
 | 
					    "ValueHasBeenAddedToLibrary": "{0} ha sido añadido a tu biblioteca multimedia",
 | 
				
			||||||
    "ValueSpecialEpisodeName": "Especial - {0}",
 | 
					    "ValueSpecialEpisodeName": "Especial - {0}",
 | 
				
			||||||
    "VersionNumber": "Versión {0}"
 | 
					    "VersionNumber": "Versión {0}",
 | 
				
			||||||
 | 
					    "TaskDownloadMissingSubtitlesDescription": "Busca en internet los subtítulos que falten basándose en la configuración de los metadatos.",
 | 
				
			||||||
 | 
					    "TaskDownloadMissingSubtitles": "Descargar subtítulos extraviados",
 | 
				
			||||||
 | 
					    "TaskRefreshChannelsDescription": "Actualizar información de canales de internet.",
 | 
				
			||||||
 | 
					    "TaskRefreshChannels": "Actualizar canales",
 | 
				
			||||||
 | 
					    "TaskCleanTranscodeDescription": "Eliminar archivos transcodificados con mas de un día de antigüedad.",
 | 
				
			||||||
 | 
					    "TaskCleanTranscode": "Limpiar directorio de Transcodificado",
 | 
				
			||||||
 | 
					    "TaskUpdatePluginsDescription": "Descargar e instalar actualizaciones para complementos que estén configurados en actualizar automáticamente.",
 | 
				
			||||||
 | 
					    "TaskUpdatePlugins": "Actualizar complementos",
 | 
				
			||||||
 | 
					    "TaskRefreshPeopleDescription": "Actualizar metadatos de actores y directores en su librería multimedia.",
 | 
				
			||||||
 | 
					    "TaskRefreshPeople": "Actualizar personas",
 | 
				
			||||||
 | 
					    "TaskCleanLogsDescription": "Eliminar archivos de registro que tengan mas de {0} días de antigüedad.",
 | 
				
			||||||
 | 
					    "TaskCleanLogs": "Limpiar directorio de registros",
 | 
				
			||||||
 | 
					    "TaskRefreshLibraryDescription": "Escanear su librería multimedia por nuevos archivos y refrescar metadatos.",
 | 
				
			||||||
 | 
					    "TaskRefreshLibrary": "Escanear librería multimedia",
 | 
				
			||||||
 | 
					    "TaskRefreshChapterImagesDescription": "Crear miniaturas de videos que tengan capítulos.",
 | 
				
			||||||
 | 
					    "TaskRefreshChapterImages": "Extraer imágenes de capitulo",
 | 
				
			||||||
 | 
					    "TaskCleanCacheDescription": "Eliminar archivos de cache que no se necesiten en el sistema.",
 | 
				
			||||||
 | 
					    "TaskCleanCache": "Limpiar directorio Cache",
 | 
				
			||||||
 | 
					    "TasksChannelsCategory": "Canales de Internet",
 | 
				
			||||||
 | 
					    "TasksApplicationCategory": "Solicitud",
 | 
				
			||||||
 | 
					    "TasksLibraryCategory": "Biblioteca",
 | 
				
			||||||
 | 
					    "TasksMaintenanceCategory": "Mantenimiento"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -92,5 +92,27 @@
 | 
				
			|||||||
    "UserStoppedPlayingItemWithValues": "{0} ha terminado de reproducirse {1} en {2}",
 | 
					    "UserStoppedPlayingItemWithValues": "{0} ha terminado de reproducirse {1} en {2}",
 | 
				
			||||||
    "ValueHasBeenAddedToLibrary": "{0} se han añadido a su biblioteca de medios",
 | 
					    "ValueHasBeenAddedToLibrary": "{0} se han añadido a su biblioteca de medios",
 | 
				
			||||||
    "ValueSpecialEpisodeName": "Especial - {0}",
 | 
					    "ValueSpecialEpisodeName": "Especial - {0}",
 | 
				
			||||||
    "VersionNumber": "Versión {0}"
 | 
					    "VersionNumber": "Versión {0}",
 | 
				
			||||||
 | 
					    "TaskDownloadMissingSubtitlesDescription": "Buscar subtítulos de internet basado en configuración de metadatos.",
 | 
				
			||||||
 | 
					    "TaskDownloadMissingSubtitles": "Descargar subtítulos perdidos",
 | 
				
			||||||
 | 
					    "TaskRefreshChannelsDescription": "Refrescar información de canales de internet.",
 | 
				
			||||||
 | 
					    "TaskRefreshChannels": "Actualizar canales",
 | 
				
			||||||
 | 
					    "TaskCleanTranscodeDescription": "Eliminar archivos transcodificados que tengan mas de un día.",
 | 
				
			||||||
 | 
					    "TaskCleanTranscode": "Limpiar directorio de transcodificado",
 | 
				
			||||||
 | 
					    "TaskUpdatePluginsDescription": "Descargar y actualizar complementos que están configurados para actualizarse automáticamente.",
 | 
				
			||||||
 | 
					    "TaskUpdatePlugins": "Actualizar complementos",
 | 
				
			||||||
 | 
					    "TaskRefreshPeopleDescription": "Actualizar datos de actores y directores en su librería multimedia.",
 | 
				
			||||||
 | 
					    "TaskRefreshPeople": "Refrescar persona",
 | 
				
			||||||
 | 
					    "TaskCleanLogsDescription": "Eliminar archivos de registro con mas de {0} días.",
 | 
				
			||||||
 | 
					    "TaskCleanLogs": "Directorio de logo limpio",
 | 
				
			||||||
 | 
					    "TaskRefreshLibraryDescription": "Escanear su librería multimedia para nuevos archivos y refrescar metadatos.",
 | 
				
			||||||
 | 
					    "TaskRefreshLibrary": "Escanear librería multimerdia",
 | 
				
			||||||
 | 
					    "TaskRefreshChapterImagesDescription": "Crear miniaturas para videos con capítulos.",
 | 
				
			||||||
 | 
					    "TaskRefreshChapterImages": "Extraer imágenes de capítulos",
 | 
				
			||||||
 | 
					    "TaskCleanCacheDescription": "Eliminar archivos cache que ya no se necesiten por el sistema.",
 | 
				
			||||||
 | 
					    "TaskCleanCache": "Limpiar directorio cache",
 | 
				
			||||||
 | 
					    "TasksChannelsCategory": "Canales de Internet",
 | 
				
			||||||
 | 
					    "TasksApplicationCategory": "Aplicación",
 | 
				
			||||||
 | 
					    "TasksLibraryCategory": "Biblioteca",
 | 
				
			||||||
 | 
					    "TasksMaintenanceCategory": "Mantenimiento"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -92,5 +92,7 @@
 | 
				
			|||||||
    "UserStoppedPlayingItemWithValues": "{0} vient d'arrêter la lecture de {1} sur {2}",
 | 
					    "UserStoppedPlayingItemWithValues": "{0} vient d'arrêter la lecture de {1} sur {2}",
 | 
				
			||||||
    "ValueHasBeenAddedToLibrary": "{0} a été ajouté à votre médiathèque",
 | 
					    "ValueHasBeenAddedToLibrary": "{0} a été ajouté à votre médiathèque",
 | 
				
			||||||
    "ValueSpecialEpisodeName": "Spécial - {0}",
 | 
					    "ValueSpecialEpisodeName": "Spécial - {0}",
 | 
				
			||||||
    "VersionNumber": "Version {0}"
 | 
					    "VersionNumber": "Version {0}",
 | 
				
			||||||
 | 
					    "TasksLibraryCategory": "Bibliothèque",
 | 
				
			||||||
 | 
					    "TasksMaintenanceCategory": "Entretien"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -7,7 +7,7 @@
 | 
				
			|||||||
    "Books": "Könyvek",
 | 
					    "Books": "Könyvek",
 | 
				
			||||||
    "CameraImageUploadedFrom": "Új kamerakép került feltöltésre innen: {0}",
 | 
					    "CameraImageUploadedFrom": "Új kamerakép került feltöltésre innen: {0}",
 | 
				
			||||||
    "Channels": "Csatornák",
 | 
					    "Channels": "Csatornák",
 | 
				
			||||||
    "ChapterNameValue": "Jelenet {0}",
 | 
					    "ChapterNameValue": "{0}. jelenet",
 | 
				
			||||||
    "Collections": "Gyűjtemények",
 | 
					    "Collections": "Gyűjtemények",
 | 
				
			||||||
    "DeviceOfflineWithName": "{0} kijelentkezett",
 | 
					    "DeviceOfflineWithName": "{0} kijelentkezett",
 | 
				
			||||||
    "DeviceOnlineWithName": "{0} belépett",
 | 
					    "DeviceOnlineWithName": "{0} belépett",
 | 
				
			||||||
@ -92,5 +92,27 @@
 | 
				
			|||||||
    "UserStoppedPlayingItemWithValues": "{0} befejezte {1} lejátászását itt: {2}",
 | 
					    "UserStoppedPlayingItemWithValues": "{0} befejezte {1} lejátászását itt: {2}",
 | 
				
			||||||
    "ValueHasBeenAddedToLibrary": "{0} hozzáadva a médiatárhoz",
 | 
					    "ValueHasBeenAddedToLibrary": "{0} hozzáadva a médiatárhoz",
 | 
				
			||||||
    "ValueSpecialEpisodeName": "Special - {0}",
 | 
					    "ValueSpecialEpisodeName": "Special - {0}",
 | 
				
			||||||
    "VersionNumber": "Verzió: {0}"
 | 
					    "VersionNumber": "Verzió: {0}",
 | 
				
			||||||
 | 
					    "TaskCleanTranscode": "Átkódolási könyvtár ürítése",
 | 
				
			||||||
 | 
					    "TaskUpdatePluginsDescription": "Letölti és telepíti a frissítéseket azokhoz a bővítményekhez, amelyeknél az automatikus frissítés engedélyezve van.",
 | 
				
			||||||
 | 
					    "TaskUpdatePlugins": "Bővítmények frissítése",
 | 
				
			||||||
 | 
					    "TaskRefreshPeopleDescription": "Frissíti a szereplők és a stábok metaadatait a könyvtáradban.",
 | 
				
			||||||
 | 
					    "TaskRefreshPeople": "Személyek frissítése",
 | 
				
			||||||
 | 
					    "TaskCleanLogsDescription": "Törli azokat a naplófájlokat, amelyek {0} napnál régebbiek.",
 | 
				
			||||||
 | 
					    "TaskCleanLogs": "Naplózási könyvtár ürítése",
 | 
				
			||||||
 | 
					    "TaskRefreshLibraryDescription": "Átvizsgálja a könyvtáraidat új fájlokért és frissíti a metaadatokat.",
 | 
				
			||||||
 | 
					    "TaskRefreshLibrary": "Média könyvtár beolvasása",
 | 
				
			||||||
 | 
					    "TaskRefreshChapterImagesDescription": "Miniatűröket generál olyan videókhoz, amely tartalmaz fejezeteket.",
 | 
				
			||||||
 | 
					    "TaskRefreshChapterImages": "Fejezetek képeinek generálása",
 | 
				
			||||||
 | 
					    "TaskCleanCacheDescription": "Törli azokat a gyorsítótárazott fájlokat, amikre a rendszernek már nincs szüksége.",
 | 
				
			||||||
 | 
					    "TaskCleanCache": "Gyorsítótár könyvtárának ürítése",
 | 
				
			||||||
 | 
					    "TasksChannelsCategory": "Internetes csatornák",
 | 
				
			||||||
 | 
					    "TasksApplicationCategory": "Alkalmazás",
 | 
				
			||||||
 | 
					    "TasksLibraryCategory": "Könyvtár",
 | 
				
			||||||
 | 
					    "TasksMaintenanceCategory": "Karbantartás",
 | 
				
			||||||
 | 
					    "TaskDownloadMissingSubtitlesDescription": "A metaadat konfiguráció alapján ellenőrzi és letölti a hiányzó feliratokat az internetről.",
 | 
				
			||||||
 | 
					    "TaskDownloadMissingSubtitles": "Hiányzó feliratok letöltése",
 | 
				
			||||||
 | 
					    "TaskRefreshChannelsDescription": "Frissíti az internetes csatornák adatait.",
 | 
				
			||||||
 | 
					    "TaskRefreshChannels": "Csatornák frissítése",
 | 
				
			||||||
 | 
					    "TaskCleanTranscodeDescription": "Törli az egy napnál régebbi átkódolási fájlokat."
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -91,5 +91,23 @@
 | 
				
			|||||||
    "UserStoppedPlayingItemWithValues": "{0} は{2}で{1} の再生が終わりました",
 | 
					    "UserStoppedPlayingItemWithValues": "{0} は{2}で{1} の再生が終わりました",
 | 
				
			||||||
    "ValueHasBeenAddedToLibrary": "{0}はあなたのメディアライブラリに追加されました",
 | 
					    "ValueHasBeenAddedToLibrary": "{0}はあなたのメディアライブラリに追加されました",
 | 
				
			||||||
    "ValueSpecialEpisodeName": "スペシャル - {0}",
 | 
					    "ValueSpecialEpisodeName": "スペシャル - {0}",
 | 
				
			||||||
    "VersionNumber": "バージョン {0}"
 | 
					    "VersionNumber": "バージョン {0}",
 | 
				
			||||||
 | 
					    "TaskCleanLogsDescription": "{0} 日以上前のログを消去します。",
 | 
				
			||||||
 | 
					    "TaskCleanLogs": "ログの掃除",
 | 
				
			||||||
 | 
					    "TaskRefreshLibraryDescription": "メディアライブラリをスキャンして新しいファイルを探し、メタデータをリフレッシュします。",
 | 
				
			||||||
 | 
					    "TaskRefreshLibrary": "メディアライブラリのスキャン",
 | 
				
			||||||
 | 
					    "TaskCleanCacheDescription": "不要なキャッシュを消去します。",
 | 
				
			||||||
 | 
					    "TaskCleanCache": "キャッシュの掃除",
 | 
				
			||||||
 | 
					    "TasksChannelsCategory": "ネットチャンネル",
 | 
				
			||||||
 | 
					    "TasksApplicationCategory": "アプリケーション",
 | 
				
			||||||
 | 
					    "TasksLibraryCategory": "ライブラリ",
 | 
				
			||||||
 | 
					    "TasksMaintenanceCategory": "メンテナンス",
 | 
				
			||||||
 | 
					    "TaskRefreshChannelsDescription": "ネットチャンネルの情報をリフレッシュします。",
 | 
				
			||||||
 | 
					    "TaskRefreshChannels": "チャンネルのリフレッシュ",
 | 
				
			||||||
 | 
					    "TaskCleanTranscodeDescription": "一日以上前のトランスコードを消去します。",
 | 
				
			||||||
 | 
					    "TaskCleanTranscode": "トランスコード用のディレクトリの掃除",
 | 
				
			||||||
 | 
					    "TaskUpdatePluginsDescription": "自動更新可能なプラグインのアップデートをダウンロードしてインストールします。",
 | 
				
			||||||
 | 
					    "TaskUpdatePlugins": "プラグインの更新",
 | 
				
			||||||
 | 
					    "TaskRefreshPeopleDescription": "メディアライブラリで俳優や監督のメタデータをリフレッシュします。",
 | 
				
			||||||
 | 
					    "TaskRefreshPeople": "俳優や監督のデータのリフレッシュ"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -92,5 +92,27 @@
 | 
				
			|||||||
    "UserStoppedPlayingItemWithValues": "{2}에서 {0}이 {1} 재생을 마침",
 | 
					    "UserStoppedPlayingItemWithValues": "{2}에서 {0}이 {1} 재생을 마침",
 | 
				
			||||||
    "ValueHasBeenAddedToLibrary": "{0}가 미디어 라이브러리에 추가되었습니다",
 | 
					    "ValueHasBeenAddedToLibrary": "{0}가 미디어 라이브러리에 추가되었습니다",
 | 
				
			||||||
    "ValueSpecialEpisodeName": "스페셜 - {0}",
 | 
					    "ValueSpecialEpisodeName": "스페셜 - {0}",
 | 
				
			||||||
    "VersionNumber": "버전 {0}"
 | 
					    "VersionNumber": "버전 {0}",
 | 
				
			||||||
 | 
					    "TasksApplicationCategory": "어플리케이션",
 | 
				
			||||||
 | 
					    "TasksMaintenanceCategory": "유지 보수",
 | 
				
			||||||
 | 
					    "TaskDownloadMissingSubtitlesDescription": "메타 데이터 기반으로 누락 된 자막이 있는지 인터넷을 검색합니다.",
 | 
				
			||||||
 | 
					    "TaskDownloadMissingSubtitles": "누락 된 자막 다운로드",
 | 
				
			||||||
 | 
					    "TaskRefreshChannelsDescription": "인터넷 채널 정보를 새로 고칩니다.",
 | 
				
			||||||
 | 
					    "TaskRefreshChannels": "채널 새로고침",
 | 
				
			||||||
 | 
					    "TaskCleanTranscodeDescription": "하루 이상 지난 트랜스 코드 파일을 삭제합니다.",
 | 
				
			||||||
 | 
					    "TaskCleanTranscode": "트랜스코드 폴더 청소",
 | 
				
			||||||
 | 
					    "TaskUpdatePluginsDescription": "자동으로 업데이트되도록 구성된 플러그인 업데이트를 다운로드하여 설치합니다.",
 | 
				
			||||||
 | 
					    "TaskUpdatePlugins": "플러그인 업데이트",
 | 
				
			||||||
 | 
					    "TaskRefreshPeopleDescription": "미디어 라이브러리에서 배우 및 감독의 메타 데이터를 업데이트합니다.",
 | 
				
			||||||
 | 
					    "TaskRefreshPeople": "인물 새로고침",
 | 
				
			||||||
 | 
					    "TaskCleanLogsDescription": "{0} 일이 지난 로그 파일을 삭제합니다.",
 | 
				
			||||||
 | 
					    "TaskCleanLogs": "로그 폴더 청소",
 | 
				
			||||||
 | 
					    "TaskRefreshLibraryDescription": "미디어 라이브러리에서 새 파일을 검색하고 메타 데이터를 새로 고칩니다.",
 | 
				
			||||||
 | 
					    "TaskRefreshLibrary": "미디어 라이브러리 스캔",
 | 
				
			||||||
 | 
					    "TaskRefreshChapterImagesDescription": "챕터가있는 비디오의 썸네일을 만듭니다.",
 | 
				
			||||||
 | 
					    "TaskRefreshChapterImages": "챕터 이미지 추출",
 | 
				
			||||||
 | 
					    "TaskCleanCacheDescription": "시스템에서 더 이상 필요하지 않은 캐시 파일을 삭제합니다.",
 | 
				
			||||||
 | 
					    "TaskCleanCache": "캐시 폴더 청소",
 | 
				
			||||||
 | 
					    "TasksChannelsCategory": "인터넷 채널",
 | 
				
			||||||
 | 
					    "TasksLibraryCategory": "라이브러리"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -91,5 +91,27 @@
 | 
				
			|||||||
    "HeaderFavoriteShows": "Raidījumu Favorīti",
 | 
					    "HeaderFavoriteShows": "Raidījumu Favorīti",
 | 
				
			||||||
    "HeaderFavoriteEpisodes": "Episožu Favorīti",
 | 
					    "HeaderFavoriteEpisodes": "Episožu Favorīti",
 | 
				
			||||||
    "HeaderFavoriteArtists": "Izpildītāju Favorīti",
 | 
					    "HeaderFavoriteArtists": "Izpildītāju Favorīti",
 | 
				
			||||||
    "HeaderFavoriteAlbums": "Albumu Favorīti"
 | 
					    "HeaderFavoriteAlbums": "Albumu Favorīti",
 | 
				
			||||||
 | 
					    "TaskCleanCacheDescription": "Nodzēš keša datnes, kas vairs nav sistēmai vajadzīgas.",
 | 
				
			||||||
 | 
					    "TaskRefreshChapterImages": "Izvilkt Nodaļu Attēlus",
 | 
				
			||||||
 | 
					    "TasksApplicationCategory": "Lietotne",
 | 
				
			||||||
 | 
					    "TasksLibraryCategory": "Bibliotēka",
 | 
				
			||||||
 | 
					    "TaskDownloadMissingSubtitlesDescription": "Internetā meklē trūkstošus subtitrus pēc metadatu uzstādījumiem.",
 | 
				
			||||||
 | 
					    "TaskDownloadMissingSubtitles": "Lejupielādēt trūkstošus subtitrus",
 | 
				
			||||||
 | 
					    "TaskRefreshChannelsDescription": "Atjauno interneta kanālu informāciju.",
 | 
				
			||||||
 | 
					    "TaskRefreshChannels": "Atjaunot Kanālus",
 | 
				
			||||||
 | 
					    "TaskCleanTranscodeDescription": "Izdzēš trans-kodēšanas datnes, kas ir vecākas par vienu dienu.",
 | 
				
			||||||
 | 
					    "TaskCleanTranscode": "Iztīrīt Trans-kodēšanas Mapi",
 | 
				
			||||||
 | 
					    "TaskUpdatePluginsDescription": "Lejupielādē un uzstāda atjauninājumus paplašinājumiem, kam ir uzstādīta automātiskā atjaunināšana.",
 | 
				
			||||||
 | 
					    "TaskUpdatePlugins": "Atjaunot Paplašinājumus",
 | 
				
			||||||
 | 
					    "TaskRefreshPeopleDescription": "Atjauno metadatus priekš aktieriem un direktoriem tavā mediju bibliotēkā.",
 | 
				
			||||||
 | 
					    "TaskRefreshPeople": "Atjaunot Cilvēkus",
 | 
				
			||||||
 | 
					    "TaskCleanLogsDescription": "Nodzēš log datnes, kas ir vairāk par {0} dienām vecas.",
 | 
				
			||||||
 | 
					    "TaskCleanLogs": "Iztīrīt Logdatņu Mapi",
 | 
				
			||||||
 | 
					    "TaskRefreshLibraryDescription": "Skenē tavas mediju bibliotēkas priekš jaunām datnēm un atjauno metadatus.",
 | 
				
			||||||
 | 
					    "TaskRefreshLibrary": "Skanēt Mediju Bibliotēku",
 | 
				
			||||||
 | 
					    "TaskRefreshChapterImagesDescription": "Izveido sīktēlus priekš video ar sadaļām.",
 | 
				
			||||||
 | 
					    "TaskCleanCache": "Iztīrīt Kešošanas Mapi",
 | 
				
			||||||
 | 
					    "TasksChannelsCategory": "Interneta Kanāli",
 | 
				
			||||||
 | 
					    "TasksMaintenanceCategory": "Apkope"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -92,5 +92,9 @@
 | 
				
			|||||||
    "UserStoppedPlayingItemWithValues": "{0} har stoppet avspilling  {1}",
 | 
					    "UserStoppedPlayingItemWithValues": "{0} har stoppet avspilling  {1}",
 | 
				
			||||||
    "ValueHasBeenAddedToLibrary": "{0} har blitt lagt til i mediebiblioteket ditt",
 | 
					    "ValueHasBeenAddedToLibrary": "{0} har blitt lagt til i mediebiblioteket ditt",
 | 
				
			||||||
    "ValueSpecialEpisodeName": "Spesialepisode - {0}",
 | 
					    "ValueSpecialEpisodeName": "Spesialepisode - {0}",
 | 
				
			||||||
    "VersionNumber": "Versjon {0}"
 | 
					    "VersionNumber": "Versjon {0}",
 | 
				
			||||||
 | 
					    "TasksChannelsCategory": "Internett kanaler",
 | 
				
			||||||
 | 
					    "TasksApplicationCategory": "Applikasjon",
 | 
				
			||||||
 | 
					    "TasksLibraryCategory": "Bibliotek",
 | 
				
			||||||
 | 
					    "TasksMaintenanceCategory": "Vedlikehold"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -91,5 +91,27 @@
 | 
				
			|||||||
    "Artists": "Artiști",
 | 
					    "Artists": "Artiști",
 | 
				
			||||||
    "Application": "Aplicație",
 | 
					    "Application": "Aplicație",
 | 
				
			||||||
    "AppDeviceValues": "Aplicație: {0}, Dispozitiv: {1}",
 | 
					    "AppDeviceValues": "Aplicație: {0}, Dispozitiv: {1}",
 | 
				
			||||||
    "Albums": "Albume"
 | 
					    "Albums": "Albume",
 | 
				
			||||||
 | 
					    "TaskDownloadMissingSubtitlesDescription": "Caută pe internet subtitrările lipsă pe baza configurației metadatelor.",
 | 
				
			||||||
 | 
					    "TaskDownloadMissingSubtitles": "Descarcă subtitrările lipsă",
 | 
				
			||||||
 | 
					    "TaskRefreshChannelsDescription": "Actualizează informațiile despre canalul de internet.",
 | 
				
			||||||
 | 
					    "TaskRefreshChannels": "Actualizează canale",
 | 
				
			||||||
 | 
					    "TaskCleanTranscodeDescription": "Șterge fișierele de transcodare mai vechi de o zi.",
 | 
				
			||||||
 | 
					    "TaskCleanTranscode": "Curățați directorul de transcodare",
 | 
				
			||||||
 | 
					    "TaskUpdatePluginsDescription": "Descarcă și instalează actualizări pentru pluginuri care sunt configurate să se actualizeze automat.",
 | 
				
			||||||
 | 
					    "TaskUpdatePlugins": "Actualizați plugin-uri",
 | 
				
			||||||
 | 
					    "TaskRefreshPeopleDescription": "Actualizează metadatele pentru actori și regizori din biblioteca media.",
 | 
				
			||||||
 | 
					    "TaskRefreshPeople": "Actualizează oamenii",
 | 
				
			||||||
 | 
					    "TaskCleanLogsDescription": "Șterge fișierele jurnal care au mai mult de {0} zile.",
 | 
				
			||||||
 | 
					    "TaskCleanLogs": "Curățare director jurnal",
 | 
				
			||||||
 | 
					    "TaskRefreshLibraryDescription": "Scanează biblioteca media pentru fișiere noi și reîmprospătează metadatele.",
 | 
				
			||||||
 | 
					    "TaskRefreshLibrary": "Scanează Biblioteca Media",
 | 
				
			||||||
 | 
					    "TaskRefreshChapterImagesDescription": "Creează miniaturi pentru videourile care au capitole.",
 | 
				
			||||||
 | 
					    "TaskRefreshChapterImages": "Extrage Imaginile de Capitol",
 | 
				
			||||||
 | 
					    "TaskCleanCacheDescription": "Șterge fișierele cache care nu mai sunt necesare sistemului.",
 | 
				
			||||||
 | 
					    "TaskCleanCache": "Curățați directorul cache",
 | 
				
			||||||
 | 
					    "TasksChannelsCategory": "Canale de pe Internet",
 | 
				
			||||||
 | 
					    "TasksApplicationCategory": "Aplicație",
 | 
				
			||||||
 | 
					    "TasksLibraryCategory": "Librărie",
 | 
				
			||||||
 | 
					    "TasksMaintenanceCategory": "Mentenanță"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -81,7 +81,7 @@
 | 
				
			|||||||
    "Favorites": "Омиљено",
 | 
					    "Favorites": "Омиљено",
 | 
				
			||||||
    "FailedLoginAttemptWithUserName": "Неуспела пријава са {0}",
 | 
					    "FailedLoginAttemptWithUserName": "Неуспела пријава са {0}",
 | 
				
			||||||
    "DeviceOnlineWithName": "{0} се повезао",
 | 
					    "DeviceOnlineWithName": "{0} се повезао",
 | 
				
			||||||
    "DeviceOfflineWithName": "{0} се одвезао",
 | 
					    "DeviceOfflineWithName": "{0} је прекинуо везу",
 | 
				
			||||||
    "Collections": "Колекције",
 | 
					    "Collections": "Колекције",
 | 
				
			||||||
    "ChapterNameValue": "Поглавље {0}",
 | 
					    "ChapterNameValue": "Поглавље {0}",
 | 
				
			||||||
    "Channels": "Канали",
 | 
					    "Channels": "Канали",
 | 
				
			||||||
@ -91,5 +91,27 @@
 | 
				
			|||||||
    "Artists": "Извођач",
 | 
					    "Artists": "Извођач",
 | 
				
			||||||
    "Application": "Апликација",
 | 
					    "Application": "Апликација",
 | 
				
			||||||
    "AppDeviceValues": "Апл: {0}, уређај: {1}",
 | 
					    "AppDeviceValues": "Апл: {0}, уређај: {1}",
 | 
				
			||||||
    "Albums": "Албуми"
 | 
					    "Albums": "Албуми",
 | 
				
			||||||
 | 
					    "TaskDownloadMissingSubtitlesDescription": "Претражује интернет за недостајуће титлове на основу конфигурације метаподатака.",
 | 
				
			||||||
 | 
					    "TaskDownloadMissingSubtitles": "Преузмите недостајуће титлове",
 | 
				
			||||||
 | 
					    "TaskRefreshChannelsDescription": "Освежава информације о интернет каналу.",
 | 
				
			||||||
 | 
					    "TaskRefreshChannels": "Освежи канале",
 | 
				
			||||||
 | 
					    "TaskCleanTranscodeDescription": "Брише датотеке за кодирање старије од једног дана.",
 | 
				
			||||||
 | 
					    "TaskCleanTranscode": "Очистите директоријум преноса",
 | 
				
			||||||
 | 
					    "TaskUpdatePluginsDescription": "Преузима и инсталира исправке за додатке који су конфигурисани за аутоматско ажурирање.",
 | 
				
			||||||
 | 
					    "TaskUpdatePlugins": "Ажурирајте додатке",
 | 
				
			||||||
 | 
					    "TaskRefreshPeopleDescription": "Ажурира метаподатке за глумце и редитеље у вашој медијској библиотеци.",
 | 
				
			||||||
 | 
					    "TaskRefreshPeople": "Освежите људе",
 | 
				
			||||||
 | 
					    "TaskCleanLogsDescription": "Брише логове старије од {0} дана.",
 | 
				
			||||||
 | 
					    "TaskCleanLogs": "Очистите директоријум логова",
 | 
				
			||||||
 | 
					    "TaskRefreshLibraryDescription": "Скенира вашу медијску библиотеку за нове датотеке и освежава метаподатке.",
 | 
				
			||||||
 | 
					    "TaskRefreshLibrary": "Скенирај Библиотеку Медија",
 | 
				
			||||||
 | 
					    "TaskRefreshChapterImagesDescription": "Ствара сличице за видео записе који имају поглавља.",
 | 
				
			||||||
 | 
					    "TaskRefreshChapterImages": "Издвоји слике из поглавља",
 | 
				
			||||||
 | 
					    "TaskCleanCacheDescription": "Брише Кеш фајлове који више нису потребни систему.",
 | 
				
			||||||
 | 
					    "TaskCleanCache": "Очистите Кеш Директоријум",
 | 
				
			||||||
 | 
					    "TasksChannelsCategory": "Интернет канали",
 | 
				
			||||||
 | 
					    "TasksApplicationCategory": "Апликација",
 | 
				
			||||||
 | 
					    "TasksLibraryCategory": "Библиотека",
 | 
				
			||||||
 | 
					    "TasksMaintenanceCategory": "Одржавање"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -92,5 +92,10 @@
 | 
				
			|||||||
    "UserStoppedPlayingItemWithValues": "{0}, {2} cihazında {1} izlemeyi bitirdi",
 | 
					    "UserStoppedPlayingItemWithValues": "{0}, {2} cihazında {1} izlemeyi bitirdi",
 | 
				
			||||||
    "ValueHasBeenAddedToLibrary": "Medya kitaplığınıza {0} eklendi",
 | 
					    "ValueHasBeenAddedToLibrary": "Medya kitaplığınıza {0} eklendi",
 | 
				
			||||||
    "ValueSpecialEpisodeName": "Özel - {0}",
 | 
					    "ValueSpecialEpisodeName": "Özel - {0}",
 | 
				
			||||||
    "VersionNumber": "Versiyon {0}"
 | 
					    "VersionNumber": "Versiyon {0}",
 | 
				
			||||||
 | 
					    "TaskCleanCache": "Geçici dosya klasörünü temizle",
 | 
				
			||||||
 | 
					    "TasksChannelsCategory": "İnternet kanalları",
 | 
				
			||||||
 | 
					    "TasksApplicationCategory": "Yazılım",
 | 
				
			||||||
 | 
					    "TasksLibraryCategory": "Kütüphane",
 | 
				
			||||||
 | 
					    "TasksMaintenanceCategory": "Onarım"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -3,7 +3,7 @@
 | 
				
			|||||||
    "AppDeviceValues": "应用: {0}, 设备: {1}",
 | 
					    "AppDeviceValues": "应用: {0}, 设备: {1}",
 | 
				
			||||||
    "Application": "应用程序",
 | 
					    "Application": "应用程序",
 | 
				
			||||||
    "Artists": "艺术家",
 | 
					    "Artists": "艺术家",
 | 
				
			||||||
    "AuthenticationSucceededWithUserName": "成功验证{0} ",
 | 
					    "AuthenticationSucceededWithUserName": "{0} 认证成功",
 | 
				
			||||||
    "Books": "书籍",
 | 
					    "Books": "书籍",
 | 
				
			||||||
    "CameraImageUploadedFrom": "新的相机图像已从 {0} 上传",
 | 
					    "CameraImageUploadedFrom": "新的相机图像已从 {0} 上传",
 | 
				
			||||||
    "Channels": "频道",
 | 
					    "Channels": "频道",
 | 
				
			||||||
 | 
				
			|||||||
@ -25,6 +25,7 @@ using Microsoft.AspNetCore.Server.Kestrel.Core;
 | 
				
			|||||||
using Microsoft.Extensions.Configuration;
 | 
					using Microsoft.Extensions.Configuration;
 | 
				
			||||||
using Microsoft.Extensions.DependencyInjection;
 | 
					using Microsoft.Extensions.DependencyInjection;
 | 
				
			||||||
using Microsoft.Extensions.DependencyInjection.Extensions;
 | 
					using Microsoft.Extensions.DependencyInjection.Extensions;
 | 
				
			||||||
 | 
					using Microsoft.Extensions.Hosting;
 | 
				
			||||||
using Microsoft.Extensions.Logging;
 | 
					using Microsoft.Extensions.Logging;
 | 
				
			||||||
using Microsoft.Extensions.Logging.Abstractions;
 | 
					using Microsoft.Extensions.Logging.Abstractions;
 | 
				
			||||||
using Serilog;
 | 
					using Serilog;
 | 
				
			||||||
@ -259,7 +260,7 @@ namespace Jellyfin.Server
 | 
				
			|||||||
            IApplicationPaths appPaths)
 | 
					            IApplicationPaths appPaths)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            return new WebHostBuilder()
 | 
					            return new WebHostBuilder()
 | 
				
			||||||
                .UseKestrel(options =>
 | 
					                .UseKestrel((builderContext, options) =>
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    var addresses = appHost.ServerConfigurationManager
 | 
					                    var addresses = appHost.ServerConfigurationManager
 | 
				
			||||||
                        .Configuration
 | 
					                        .Configuration
 | 
				
			||||||
@ -282,6 +283,14 @@ namespace Jellyfin.Server
 | 
				
			|||||||
                                    listenOptions.Protocols = HttpProtocols.Http1AndHttp2;
 | 
					                                    listenOptions.Protocols = HttpProtocols.Http1AndHttp2;
 | 
				
			||||||
                                });
 | 
					                                });
 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
 | 
					                            else if (builderContext.HostingEnvironment.IsDevelopment())
 | 
				
			||||||
 | 
					                            {
 | 
				
			||||||
 | 
					                                options.Listen(address, appHost.HttpsPort, listenOptions =>
 | 
				
			||||||
 | 
					                                {
 | 
				
			||||||
 | 
					                                    listenOptions.UseHttps();
 | 
				
			||||||
 | 
					                                    listenOptions.Protocols = HttpProtocols.Http1AndHttp2;
 | 
				
			||||||
 | 
					                                });
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    else
 | 
					                    else
 | 
				
			||||||
@ -297,6 +306,14 @@ namespace Jellyfin.Server
 | 
				
			|||||||
                                listenOptions.Protocols = HttpProtocols.Http1AndHttp2;
 | 
					                                listenOptions.Protocols = HttpProtocols.Http1AndHttp2;
 | 
				
			||||||
                            });
 | 
					                            });
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
 | 
					                        else if (builderContext.HostingEnvironment.IsDevelopment())
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            options.ListenAnyIP(appHost.HttpsPort, listenOptions =>
 | 
				
			||||||
 | 
					                            {
 | 
				
			||||||
 | 
					                                listenOptions.UseHttps();
 | 
				
			||||||
 | 
					                                listenOptions.Protocols = HttpProtocols.Http1AndHttp2;
 | 
				
			||||||
 | 
					                            });
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                })
 | 
					                })
 | 
				
			||||||
                .ConfigureAppConfiguration(config => config.ConfigureAppConfiguration(commandLineOpts, appPaths, startupConfig))
 | 
					                .ConfigureAppConfiguration(config => config.ConfigureAppConfiguration(commandLineOpts, appPaths, startupConfig))
 | 
				
			||||||
 | 
				
			|||||||
@ -1,11 +1,17 @@
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
  "profiles": {
 | 
					  "profiles": {
 | 
				
			||||||
    "Jellyfin.Server": {
 | 
					    "Jellyfin.Server": {
 | 
				
			||||||
      "commandName": "Project"
 | 
					      "commandName": "Project",
 | 
				
			||||||
 | 
					      "environmentVariables": {
 | 
				
			||||||
 | 
					        "ASPNETCORE_ENVIRONMENT": "Development"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "Jellyfin.Server (nowebclient)": {
 | 
					    "Jellyfin.Server (nowebclient)": {
 | 
				
			||||||
      "commandName": "Project",
 | 
					      "commandName": "Project",
 | 
				
			||||||
      "commandLineArgs": "--nowebclient"
 | 
					      "commandLineArgs": "--nowebclient",
 | 
				
			||||||
 | 
					      "environmentVariables": {
 | 
				
			||||||
 | 
					        "ASPNETCORE_ENVIRONMENT": "Development"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -9,7 +9,7 @@ namespace MediaBrowser.Common.Cryptography
 | 
				
			|||||||
    /// <summary>
 | 
					    /// <summary>
 | 
				
			||||||
    /// Class containing extension methods for working with Jellyfin cryptography objects.
 | 
					    /// Class containing extension methods for working with Jellyfin cryptography objects.
 | 
				
			||||||
    /// </summary>
 | 
					    /// </summary>
 | 
				
			||||||
    public static class Extensions
 | 
					    public static class CryptoExtensions
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        /// <summary>
 | 
					        /// <summary>
 | 
				
			||||||
        /// Creates a new <see cref="PasswordHash" /> instance.
 | 
					        /// Creates a new <see cref="PasswordHash" /> instance.
 | 
				
			||||||
@ -30,7 +30,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  <!-- Code analyzers-->
 | 
					  <!-- Code analyzers-->
 | 
				
			||||||
  <ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
 | 
					  <ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
 | 
				
			||||||
    <!-- <PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.8" PrivateAssets="All" /> -->
 | 
					    <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" />
 | 
				
			||||||
 | 
				
			|||||||
@ -20,7 +20,7 @@ namespace MediaBrowser.Common.Net
 | 
				
			|||||||
            RequestHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
 | 
					            RequestHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            CacheMode = CacheMode.None;
 | 
					            CacheMode = CacheMode.None;
 | 
				
			||||||
            DecompressionMethod = CompressionMethod.Deflate;
 | 
					            DecompressionMethod = CompressionMethods.Deflate;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /// <summary>
 | 
					        /// <summary>
 | 
				
			||||||
@ -29,7 +29,7 @@ namespace MediaBrowser.Common.Net
 | 
				
			|||||||
        /// <value>The URL.</value>
 | 
					        /// <value>The URL.</value>
 | 
				
			||||||
        public string Url { get; set; }
 | 
					        public string Url { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public CompressionMethod DecompressionMethod { get; set; }
 | 
					        public CompressionMethods DecompressionMethod { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /// <summary>
 | 
					        /// <summary>
 | 
				
			||||||
        /// Gets or sets the accept header.
 | 
					        /// Gets or sets the accept header.
 | 
				
			||||||
@ -83,8 +83,6 @@ namespace MediaBrowser.Common.Net
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        public string RequestContent { get; set; }
 | 
					        public string RequestContent { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public byte[] RequestContentBytes { get; set; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public bool BufferContent { get; set; }
 | 
					        public bool BufferContent { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public bool LogErrorResponseBody { get; set; }
 | 
					        public bool LogErrorResponseBody { get; set; }
 | 
				
			||||||
@ -112,7 +110,7 @@ namespace MediaBrowser.Common.Net
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    [Flags]
 | 
					    [Flags]
 | 
				
			||||||
    public enum CompressionMethod
 | 
					    public enum CompressionMethods
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        None = 0b00000001,
 | 
					        None = 0b00000001,
 | 
				
			||||||
        Deflate = 0b00000010,
 | 
					        Deflate = 0b00000010,
 | 
				
			||||||
 | 
				
			|||||||
@ -8,7 +8,7 @@ namespace MediaBrowser.Common.Net
 | 
				
			|||||||
    /// <summary>
 | 
					    /// <summary>
 | 
				
			||||||
    /// Class HttpResponseInfo.
 | 
					    /// Class HttpResponseInfo.
 | 
				
			||||||
    /// </summary>
 | 
					    /// </summary>
 | 
				
			||||||
    public class HttpResponseInfo : IDisposable
 | 
					    public sealed class HttpResponseInfo : IDisposable
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
#pragma warning disable CS1591
 | 
					#pragma warning disable CS1591
 | 
				
			||||||
        public HttpResponseInfo()
 | 
					        public HttpResponseInfo()
 | 
				
			||||||
 | 
				
			|||||||
@ -4,11 +4,21 @@ namespace MediaBrowser.Controller.Entities
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
    public class InternalPeopleQuery
 | 
					    public class InternalPeopleQuery
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// Gets or sets the maximum number of items the query should return.
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        public int Limit { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public Guid ItemId { get; set; }
 | 
					        public Guid ItemId { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public string[] PersonTypes { get; set; }
 | 
					        public string[] PersonTypes { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public string[] ExcludePersonTypes { get; set; }
 | 
					        public string[] ExcludePersonTypes { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public int? MaxListOrder { get; set; }
 | 
					        public int? MaxListOrder { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public Guid AppearsInItemId { get; set; }
 | 
					        public Guid AppearsInItemId { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public string NameContains { get; set; }
 | 
					        public string NameContains { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public InternalPeopleQuery()
 | 
					        public InternalPeopleQuery()
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user