Merge remote-tracking branch 'upstream/master' into simplify-https-config

This commit is contained in:
Mark Monteiro 2020-04-06 00:24:11 -04:00
commit 5bc6c08a0a
40 changed files with 641 additions and 299 deletions

View File

@ -45,15 +45,20 @@ RUN apt-get update \
ca-certificates \
vainfo \
i965-va-driver \
locales \
&& apt-get clean autoclean -y\
&& apt-get autoremove -y\
&& rm -rf /var/lib/apt/lists/* \
&& mkdir -p /cache /config /media \
&& chmod 777 /cache /config /media \
&& 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 LC_ALL en_US.UTF-8
ENV LANG en_US.UTF-8
ENV LANGUAGE en_US:en
EXPOSE 8096
VOLUME /cache /config /media

View File

@ -52,16 +52,22 @@ RUN apt-get update \
libraspberrypi0 \
vainfo \
libva2 \
locales \
&& apt-get remove curl gnupg -y \
&& apt-get clean autoclean -y \
&& apt-get autoremove -y \
&& rm -rf /var/lib/apt/lists/* \
&& 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=web-builder /dist /jellyfin/jellyfin-web
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
VOLUME /cache /config /media

View File

@ -42,15 +42,21 @@ RUN apt-get update && apt-get install --no-install-recommends --no-install-sugge
libfreetype6 \
libomxil-bellagio0 \
libomxil-bellagio-bin \
locales \
&& apt-get clean autoclean -y \
&& apt-get autoremove -y \
&& rm -rf /var/lib/apt/lists/* \
&& 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=web-builder /dist /jellyfin/jellyfin-web
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
VOLUME /cache /config /media

View File

@ -1,6 +1,7 @@
#pragma warning disable CS1591
using System;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Text;
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)));
}
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request", Justification = "Required for ServiceStack")]
public object Get(GetContentDirectory request)
{
var xml = ContentDirectory.GetServiceXml();
@ -158,6 +160,7 @@ namespace Emby.Dlna.Api
return _resultFactory.GetResult(Request, xml, XMLContentType);
}
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request", Justification = "Required for ServiceStack")]
public object Get(GetMediaReceiverRegistrar request)
{
var xml = MediaReceiverRegistrar.GetServiceXml();
@ -165,6 +168,7 @@ namespace Emby.Dlna.Api
return _resultFactory.GetResult(Request, xml, XMLContentType);
}
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request", Justification = "Required for ServiceStack")]
public object Get(GetConnnectionManager request)
{
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));
}
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request", Justification = "Required for ServiceStack")]
public object Subscribe(ProcessContentDirectoryEventRequest request)
{
return ProcessEventRequest(ContentDirectory);
}
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request", Justification = "Required for ServiceStack")]
public object Subscribe(ProcessConnectionManagerEventRequest request)
{
return ProcessEventRequest(ConnectionManager);
}
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request", Justification = "Required for ServiceStack")]
public object Subscribe(ProcessMediaReceiverRegistrarEventRequest request)
{
return ProcessEventRequest(MediaReceiverRegistrar);
}
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request", Justification = "Required for ServiceStack")]
public object Unsubscribe(ProcessContentDirectoryEventRequest request)
{
return ProcessEventRequest(ContentDirectory);
}
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request", Justification = "Required for ServiceStack")]
public object Unsubscribe(ProcessConnectionManagerEventRequest request)
{
return ProcessEventRequest(ConnectionManager);
}
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request", Justification = "Required for ServiceStack")]
public object Unsubscribe(ProcessMediaReceiverRegistrarEventRequest request)
{
return ProcessEventRequest(MediaReceiverRegistrar);

View File

@ -1,5 +1,6 @@
#pragma warning disable CS1591
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using MediaBrowser.Controller.Dlna;
using MediaBrowser.Controller.Net;
@ -52,6 +53,7 @@ namespace Emby.Dlna.Api
_dlnaManager = dlnaManager;
}
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request", Justification = "Required for ServiceStack")]
public object Get(GetProfileInfos request)
{
return _dlnaManager.GetProfileInfos().ToArray();
@ -62,6 +64,7 @@ namespace Emby.Dlna.Api
return _dlnaManager.GetProfile(request.Id);
}
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request", Justification = "Required for ServiceStack")]
public object Get(GetDefaultProfile request)
{
return _dlnaManager.GetDefaultProfile();

View File

@ -78,7 +78,18 @@ namespace Emby.Dlna.ContentDirectory
_profile = profile;
_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 />
@ -153,7 +164,7 @@ namespace Emby.Dlna.ContentDirectory
{
var id = sparams["ObjectID"];
var serverItem = GetItemFromObjectId(id, _user);
var serverItem = GetItemFromObjectId(id);
var item = serverItem.Item;
@ -276,7 +287,7 @@ namespace Emby.Dlna.ContentDirectory
DidlBuilder.WriteXmlRootAttributes(_profile, writer);
var serverItem = GetItemFromObjectId(id, _user);
var serverItem = GetItemFromObjectId(id);
var item = serverItem.Item;
@ -293,7 +304,7 @@ namespace Emby.Dlna.ContentDirectory
else
{
var dlnaOptions = _config.GetDlnaConfiguration();
_didlBuilder.WriteItemElement(dlnaOptions, writer, item, _user, null, null, deviceId, filter);
_didlBuilder.WriteItemElement(writer, item, _user, null, null, deviceId, filter);
}
provided++;
@ -320,7 +331,7 @@ namespace Emby.Dlna.ContentDirectory
}
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);
var serverItem = GetItemFromObjectId(sparams["ContainerID"], _user);
var serverItem = GetItemFromObjectId(sparams["ContainerID"]);
var item = serverItem.Item;
@ -406,7 +417,7 @@ namespace Emby.Dlna.ContentDirectory
}
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))
{
return GetFolders(item, user, stubType, sort, startIndex, limit);
return GetFolders(user, startIndex, limit);
}
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);
}
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)
{
@ -579,7 +590,7 @@ namespace Emby.Dlna.ContentDirectory
if (stubType.HasValue && stubType.Value == StubType.Playlists)
{
return GetMusicPlaylists(item, user, query);
return GetMusicPlaylists(user, query);
}
if (stubType.HasValue && stubType.Value == StubType.Albums)
@ -707,7 +718,7 @@ namespace Emby.Dlna.ContentDirectory
if (stubType.HasValue && stubType.Value == StubType.Collections)
{
return GetMovieCollections(item, user, query);
return GetMovieCollections(user, query);
}
if (stubType.HasValue && stubType.Value == StubType.Favorites)
@ -720,46 +731,42 @@ namespace Emby.Dlna.ContentDirectory
return GetGenres(item, user, query);
}
var list = new List<ServerItem>();
list.Add(new ServerItem(item)
var array = new ServerItem[]
{
StubType = StubType.ContinueWatching
});
list.Add(new ServerItem(item)
{
StubType = StubType.Latest
});
list.Add(new ServerItem(item)
{
StubType = StubType.Movies
});
list.Add(new ServerItem(item)
{
StubType = StubType.Collections
});
list.Add(new ServerItem(item)
{
StubType = StubType.Favorites
});
list.Add(new ServerItem(item)
{
StubType = StubType.Genres
});
new ServerItem(item)
{
StubType = StubType.ContinueWatching
},
new ServerItem(item)
{
StubType = StubType.Latest
},
new ServerItem(item)
{
StubType = StubType.Movies
},
new ServerItem(item)
{
StubType = StubType.Collections
},
new ServerItem(item)
{
StubType = StubType.Favorites
},
new ServerItem(item)
{
StubType = StubType.Genres
}
};
return new QueryResult<ServerItem>
{
Items = list,
TotalRecordCount = list.Count
Items = array,
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)
.OrderBy(i => i.SortName)
@ -792,7 +799,7 @@ namespace Emby.Dlna.ContentDirectory
if (stubType.HasValue && stubType.Value == StubType.NextUp)
{
return GetNextUp(item, user, query);
return GetNextUp(item, query);
}
if (stubType.HasValue && stubType.Value == StubType.Latest)
@ -910,7 +917,7 @@ namespace Emby.Dlna.ContentDirectory
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.Parent = parent;
@ -1105,7 +1112,7 @@ namespace Emby.Dlna.ContentDirectory
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.IncludeItemTypes = new[] { typeof(Playlist).Name };
@ -1134,7 +1141,7 @@ namespace Emby.Dlna.ContentDirectory
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)>();
@ -1289,15 +1296,15 @@ namespace Emby.Dlna.ContentDirectory
return result;
}
private ServerItem GetItemFromObjectId(string id, User user)
private ServerItem GetItemFromObjectId(string id)
{
return DidlBuilder.IsIdRoot(id)
? new ServerItem(_libraryManager.GetUserRootFolder())
: ParseItemId(id, user);
: ParseItemId(id);
}
private ServerItem ParseItemId(string id, User user)
private ServerItem ParseItemId(string id)
{
StubType? stubType = null;

View File

@ -45,6 +45,7 @@ namespace Emby.Dlna.Didl
private readonly IMediaSourceManager _mediaSourceManager;
private readonly ILogger _logger;
private readonly IMediaEncoder _mediaEncoder;
private readonly ILibraryManager _libraryManager;
public DidlBuilder(
DeviceProfile profile,
@ -56,7 +57,8 @@ namespace Emby.Dlna.Didl
ILocalizationManager localization,
IMediaSourceManager mediaSourceManager,
ILogger logger,
IMediaEncoder mediaEncoder)
IMediaEncoder mediaEncoder,
ILibraryManager libraryManager)
{
_profile = profile;
_user = user;
@ -68,6 +70,7 @@ namespace Emby.Dlna.Didl
_mediaSourceManager = mediaSourceManager;
_logger = logger;
_mediaEncoder = mediaEncoder;
_libraryManager = libraryManager;
}
public static string NormalizeDlnaMediaUrl(string url)
@ -75,7 +78,7 @@ namespace Emby.Dlna.Didl
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
{
@ -100,7 +103,7 @@ namespace Emby.Dlna.Didl
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.WriteEndDocument();
@ -127,7 +130,6 @@ namespace Emby.Dlna.Didl
}
public void WriteItemElement(
DlnaOptions options,
XmlWriter writer,
BaseItem item,
User user,
@ -164,25 +166,23 @@ namespace Emby.Dlna.Didl
// refID?
// storeAttribute(itemNode, object, ClassProperties.REF_ID, false);
var hasMediaSources = item as IHasMediaSources;
if (hasMediaSources != null)
if (item is IHasMediaSources)
{
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))
{
AddVideoResource(options, writer, item, deviceId, filter, streamInfo);
AddVideoResource(writer, item, deviceId, filter, streamInfo);
}
}
AddCover(item, context, null, writer);
AddCover(item, null, writer);
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)
{
@ -226,7 +226,7 @@ namespace Emby.Dlna.Didl
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);
@ -283,7 +283,10 @@ namespace Emby.Dlna.Didl
else
{
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.WriteString(info.Url);
@ -293,7 +296,7 @@ namespace Emby.Dlna.Didl
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);
@ -335,7 +338,13 @@ namespace Emby.Dlna.Didl
{
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.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)
? MimeTypes.GetMimeType(filename)
: mediaProfile.MimeType;
writer.WriteAttributeString("protocolInfo", string.Format(
"http-get:*:{0}:{1}",
mimeType,
contentFeatures
));
writer.WriteAttributeString(
"protocolInfo",
string.Format(
CultureInfo.InvariantCulture,
"http-get:*:{0}:{1}",
mimeType,
contentFeatures));
writer.WriteString(url);
@ -420,7 +431,10 @@ namespace Emby.Dlna.Didl
if (item.ParentIndexNumber.HasValue && item.ParentIndexNumber.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)
@ -435,11 +449,34 @@ namespace Emby.Dlna.Didl
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;
}
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);
@ -505,7 +542,7 @@ namespace Emby.Dlna.Didl
targetSampleRate,
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)
? MimeTypes.GetMimeType(filename)
@ -521,11 +558,13 @@ namespace Emby.Dlna.Didl
streamInfo.RunTimeTicks ?? 0,
streamInfo.TranscodeSeekInfo);
writer.WriteAttributeString("protocolInfo", string.Format(
"http-get:*:{0}:{1}",
mimeType,
contentFeatures
));
writer.WriteAttributeString(
"protocolInfo",
string.Format(
CultureInfo.InvariantCulture,
"http-get:*:{0}:{1}",
mimeType,
contentFeatures));
writer.WriteString(url);
@ -548,7 +587,7 @@ namespace Emby.Dlna.Didl
var clientId = GetClientId(folder, stubType);
if (string.Equals(requestedId, "0"))
if (string.Equals(requestedId, "0", StringComparison.Ordinal))
{
writer.WriteAttributeString("id", "0");
writer.WriteAttributeString("parentID", "-1");
@ -577,7 +616,7 @@ namespace Emby.Dlna.Didl
AddGeneralProperties(folder, stubType, context, writer, filter);
AddCover(folder, context, stubType, writer);
AddCover(folder, stubType, writer);
writer.WriteFullEndElement();
}
@ -610,7 +649,10 @@ namespace Emby.Dlna.Didl
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);
}
}
@ -763,37 +805,36 @@ namespace Emby.Dlna.Didl
private void AddPeople(BaseItem item, XmlWriter writer)
{
//var types = new[]
//{
// PersonType.Director,
// PersonType.Writer,
// PersonType.Producer,
// PersonType.Composer,
// "Creator"
//};
if (!item.SupportsPeople)
{
return;
}
//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
//// The actual issue might just be due to processing a more metadata than it can handle
//var limit = 6;
foreach (var actor in people)
{
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)
//{
// 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;
// }
//}
AddValue(writer, "upnp", type.ToLowerInvariant(), actor.Name, NS_UPNP);
}
}
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);
@ -915,17 +956,8 @@ namespace Emby.Dlna.Didl
}
private void AddEmbeddedImageAsCover(string name, XmlWriter writer)
{
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,
private void AddImageResElement(
BaseItem item,
XmlWriter writer,
int maxWidth,
int maxHeight,
@ -951,13 +983,17 @@ namespace Emby.Dlna.Didl
var contentFeatures = new ContentFeatureBuilder(_profile)
.BuildImageHeader(format, width, height, imageInfo.IsDirectStream, org_Pn);
writer.WriteAttributeString("protocolInfo", string.Format(
"http-get:*:{0}:{1}",
MimeTypes.GetMimeType("file." + format),
contentFeatures
));
writer.WriteAttributeString(
"protocolInfo",
string.Format(
CultureInfo.InvariantCulture,
"http-get:*:{0}:{1}",
MimeTypes.GetMimeType("file." + format),
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);
@ -1096,7 +1132,9 @@ namespace Emby.Dlna.Didl
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,
info.ItemId.ToString("N", CultureInfo.InvariantCulture),
info.Type,

View File

@ -53,6 +53,6 @@ namespace Emby.Dlna.Didl
_encoding = encoding;
}
public override Encoding Encoding => (null == _encoding) ? base.Encoding : _encoding;
public override Encoding Encoding => _encoding ?? base.Encoding;
}
}

View File

@ -346,7 +346,12 @@ namespace Emby.Dlna.PlayTo
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)
@ -515,8 +520,12 @@ namespace Emby.Dlna.PlayTo
return;
}
var result = await new SsdpHttpClient(_httpClient).SendCommandAsync(Properties.BaseUrl, service, command.Name, rendererCommands.BuildPost(command, service.ServiceType), true)
.ConfigureAwait(false);
var result = await new SsdpHttpClient(_httpClient).SendCommandAsync(
Properties.BaseUrl,
service,
command.Name,
rendererCommands.BuildPost(command, service.ServiceType),
cancellationToken: cancellationToken).ConfigureAwait(false);
if (result == null || result.Document == null)
{
@ -561,8 +570,12 @@ namespace Emby.Dlna.PlayTo
return;
}
var result = await new SsdpHttpClient(_httpClient).SendCommandAsync(Properties.BaseUrl, service, command.Name, rendererCommands.BuildPost(command, service.ServiceType), true)
.ConfigureAwait(false);
var result = await new SsdpHttpClient(_httpClient).SendCommandAsync(
Properties.BaseUrl,
service,
command.Name,
rendererCommands.BuildPost(command, service.ServiceType),
cancellationToken: cancellationToken).ConfigureAwait(false);
if (result == null || result.Document == null)
return;
@ -588,8 +601,12 @@ namespace Emby.Dlna.PlayTo
return null;
}
var result = await new SsdpHttpClient(_httpClient).SendCommandAsync(Properties.BaseUrl, service, command.Name, avCommands.BuildPost(command, service.ServiceType), false)
.ConfigureAwait(false);
var result = await new SsdpHttpClient(_httpClient).SendCommandAsync(
Properties.BaseUrl,
service,
command.Name,
avCommands.BuildPost(command, service.ServiceType),
cancellationToken: cancellationToken).ConfigureAwait(false);
if (result == null || result.Document == null)
{
@ -599,7 +616,7 @@ namespace Emby.Dlna.PlayTo
var transportState =
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
&& Enum.TryParse(transportStateValue, true, out TRANSPORTSTATE state))
@ -626,8 +643,12 @@ namespace Emby.Dlna.PlayTo
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)
.ConfigureAwait(false);
var result = await new SsdpHttpClient(_httpClient).SendCommandAsync(
Properties.BaseUrl,
service,
command.Name,
rendererCommands.BuildPost(command, service.ServiceType),
cancellationToken: cancellationToken).ConfigureAwait(false);
if (result == null || result.Document == null)
{
@ -689,8 +710,12 @@ namespace Emby.Dlna.PlayTo
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)
.ConfigureAwait(false);
var result = await new SsdpHttpClient(_httpClient).SendCommandAsync(
Properties.BaseUrl,
service,
command.Name,
rendererCommands.BuildPost(command, service.ServiceType),
cancellationToken: cancellationToken).ConfigureAwait(false);
if (result == null || result.Document == null)
{

View File

@ -27,6 +27,8 @@ namespace Emby.Dlna.PlayTo
{
public class PlayToController : ISessionController, IDisposable
{
private static readonly CultureInfo _usCulture = CultureInfo.ReadOnly(new CultureInfo("en-US"));
private Device _device;
private readonly SessionInfo _session;
private readonly ISessionManager _sessionManager;
@ -45,9 +47,10 @@ namespace Emby.Dlna.PlayTo
private readonly string _serverAddress;
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(
SessionInfo session,
@ -83,18 +86,22 @@ namespace Emby.Dlna.PlayTo
_mediaEncoder = mediaEncoder;
}
public bool IsSessionActive => !_disposed && _device != null;
public bool SupportsMediaControl => IsSessionActive;
public void Init(Device device)
{
_device = device;
_device.OnDeviceUnavailable = OnDeviceUnavailable;
_device.PlaybackStart += _device_PlaybackStart;
_device.PlaybackProgress += _device_PlaybackProgress;
_device.PlaybackStopped += _device_PlaybackStopped;
_device.MediaChanged += _device_MediaChanged;
_device.PlaybackStart += OnDevicePlaybackStart;
_device.PlaybackProgress += OnDevicePlaybackProgress;
_device.PlaybackStopped += OnDevicePlaybackStopped;
_device.MediaChanged += OnDeviceMediaChanged;
_device.Start();
_deviceDiscovery.DeviceLeft += _deviceDiscovery_DeviceLeft;
_deviceDiscovery.DeviceLeft += OnDeviceDiscoveryDeviceLeft;
}
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;
@ -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)
{
@ -137,15 +144,15 @@ namespace Emby.Dlna.PlayTo
var streamInfo = StreamParams.ParseFromUrl(e.OldMediaInfo.Url, _libraryManager, _mediaSourceManager);
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);
if (streamInfo.Item == null) return;
var newItemProgress = GetProgressInfo(e.NewMediaInfo, streamInfo);
var newItemProgress = GetProgressInfo(streamInfo);
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)
{
@ -168,9 +175,9 @@ namespace Emby.Dlna.PlayTo
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);
@ -194,7 +201,7 @@ namespace Emby.Dlna.PlayTo
}
else
{
Playlist.Clear();
_playlist.Clear();
}
}
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
{
@ -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)
{
@ -235,7 +242,7 @@ namespace Emby.Dlna.PlayTo
if (info.Item != null)
{
var progress = GetProgressInfo(e.MediaInfo, info);
var progress = GetProgressInfo(info);
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)
{
@ -266,7 +273,7 @@ namespace Emby.Dlna.PlayTo
if (info.Item != null)
{
var progress = GetProgressInfo(e.MediaInfo, info);
var progress = GetProgressInfo(info);
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;
@ -289,13 +296,13 @@ namespace Emby.Dlna.PlayTo
return ticks;
}
private PlaybackStartInfo GetProgressInfo(uBaseObject mediaInfo, StreamParams info)
private PlaybackStartInfo GetProgressInfo(StreamParams info)
{
return new PlaybackStartInfo
{
ItemId = info.ItemId,
SessionId = _session.Id,
PositionTicks = GetProgressPositionTicks(mediaInfo, info),
PositionTicks = GetProgressPositionTicks(info),
IsMuted = _device.IsMuted,
IsPaused = _device.IsPaused,
MediaSourceId = info.MediaSourceId,
@ -310,9 +317,7 @@ namespace Emby.Dlna.PlayTo
};
}
#region SendCommands
public async Task SendPlayCommand(PlayRequest command, CancellationToken cancellationToken)
public Task SendPlayCommand(PlayRequest command, CancellationToken cancellationToken)
{
_logger.LogDebug("{0} - Received PlayRequest: {1}", this._session.DeviceName, command.PlayCommand);
@ -350,11 +355,12 @@ namespace Emby.Dlna.PlayTo
if (command.PlayCommand == PlayCommand.PlayLast)
{
Playlist.AddRange(playlist);
_playlist.AddRange(playlist);
}
if (command.PlayCommand == PlayCommand.PlayNext)
{
Playlist.AddRange(playlist);
_playlist.AddRange(playlist);
}
if (!command.ControllingUserId.Equals(Guid.Empty))
@ -363,7 +369,7 @@ namespace Emby.Dlna.PlayTo
_session.DeviceName, _session.RemoteEndPoint, user);
}
await PlayItems(playlist).ConfigureAwait(false);
return PlayItems(playlist, cancellationToken);
}
private Task SendPlaystateCommand(PlaystateRequest command, CancellationToken cancellationToken)
@ -371,7 +377,7 @@ namespace Emby.Dlna.PlayTo
switch (command.Command)
{
case PlaystateCommand.Stop:
Playlist.Clear();
_playlist.Clear();
return _device.SetStop(CancellationToken.None);
case PlaystateCommand.Pause:
@ -387,10 +393,10 @@ namespace Emby.Dlna.PlayTo
return Seek(command.SeekPositionTicks ?? 0);
case PlaystateCommand.NextTrack:
return SetPlaylistIndex(_currentPlaylistIndex + 1);
return SetPlaylistIndex(_currentPlaylistIndex + 1, cancellationToken);
case PlaystateCommand.PreviousTrack:
return SetPlaylistIndex(_currentPlaylistIndex - 1);
return SetPlaylistIndex(_currentPlaylistIndex - 1, cancellationToken);
}
return Task.CompletedTask;
@ -426,14 +432,6 @@ namespace Emby.Dlna.PlayTo
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)
{
var item = _libraryManager.GetItemById(id);
@ -451,7 +449,7 @@ namespace Emby.Dlna.PlayTo
_dlnaManager.GetDefaultProfile();
var mediaSources = item is IHasMediaSources
? (_mediaSourceManager.GetStaticMediaSources(item, true, user))
? _mediaSourceManager.GetStaticMediaSources(item, true, user)
: new List<MediaSourceInfo>();
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));
var itemXml = new DidlBuilder(profile, user, _imageProcessor, _serverAddress, _accessToken, _userDataManager, _localization, _mediaSourceManager, _logger, _mediaEncoder)
.GetItemDidl(_config.GetDlnaConfiguration(), item, user, null, _session.DeviceId, new Filter(), playlistItem.StreamInfo);
var itemXml = new DidlBuilder(
profile,
user,
_imageProcessor,
_serverAddress,
_accessToken,
_userDataManager,
_localization,
_mediaSourceManager,
_logger,
_mediaEncoder,
_libraryManager)
.GetItemDidl(item, user, null, _session.DeviceId, new Filter(), playlistItem.StreamInfo);
playlistItem.Didl = itemXml;
@ -570,30 +579,31 @@ namespace Emby.Dlna.PlayTo
/// Plays the items.
/// </summary>
/// <param name="items">The items.</param>
/// <returns></returns>
private async Task<bool> PlayItems(IEnumerable<PlaylistItem> items)
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns><c>true</c> on success.</returns>
private async Task<bool> PlayItems(IEnumerable<PlaylistItem> items, CancellationToken cancellationToken = default)
{
Playlist.Clear();
Playlist.AddRange(items);
_logger.LogDebug("{0} - Playing {1} items", _session.DeviceName, Playlist.Count);
_playlist.Clear();
_playlist.AddRange(items);
_logger.LogDebug("{0} - Playing {1} items", _session.DeviceName, _playlist.Count);
await SetPlaylistIndex(0).ConfigureAwait(false);
await SetPlaylistIndex(0, cancellationToken).ConfigureAwait(false);
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();
await _device.SetStop(CancellationToken.None);
_playlist.Clear();
await _device.SetStop(cancellationToken).ConfigureAwait(false);
return;
}
_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;
if (streamInfo.StartPositionTicks > 0 && EnableClientSideSeek(streamInfo))
@ -602,10 +612,7 @@ namespace Emby.Dlna.PlayTo
}
}
#endregion
private bool _disposed;
/// <inheritdoc />
public void Dispose()
{
Dispose(true);
@ -624,19 +631,17 @@ namespace Emby.Dlna.PlayTo
_device.Dispose();
}
_device.PlaybackStart -= _device_PlaybackStart;
_device.PlaybackProgress -= _device_PlaybackProgress;
_device.PlaybackStopped -= _device_PlaybackStopped;
_device.MediaChanged -= _device_MediaChanged;
_deviceDiscovery.DeviceLeft -= _deviceDiscovery_DeviceLeft;
_device.PlaybackStart -= OnDevicePlaybackStart;
_device.PlaybackProgress -= OnDevicePlaybackProgress;
_device.PlaybackStopped -= OnDevicePlaybackStopped;
_device.MediaChanged -= OnDeviceMediaChanged;
_deviceDiscovery.DeviceLeft -= OnDeviceDiscoveryDeviceLeft;
_device.OnDeviceUnavailable = null;
_device = null;
_disposed = true;
}
private static readonly CultureInfo _usCulture = CultureInfo.ReadOnly(new CultureInfo("en-US"));
private Task SendGeneralCommand(GeneralCommand command, CancellationToken cancellationToken)
{
if (Enum.TryParse(command.Name, true, out GeneralCommandType commandType))
@ -713,7 +718,7 @@ namespace Emby.Dlna.PlayTo
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 newItem = CreatePlaylistItem(info.Item, user, newPosition, info.MediaSourceId, newIndex, info.SubtitleStreamIndex);
@ -738,7 +743,7 @@ namespace Emby.Dlna.PlayTo
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 newItem = CreatePlaylistItem(info.Item, user, newPosition, info.MediaSourceId, info.AudioStreamIndex, newIndex);
@ -852,8 +857,11 @@ namespace Emby.Dlna.PlayTo
return request;
}
var index = url.IndexOf('?');
if (index == -1) return request;
var index = url.IndexOf('?', StringComparison.Ordinal);
if (index == -1)
{
return request;
}
var query = url.Substring(index + 1);
Dictionary<string, string> values = QueryHelpers.ParseQuery(query).ToDictionary(kv => kv.Key, kv => kv.Value.ToString());

View File

@ -23,7 +23,7 @@ using Microsoft.Extensions.Logging;
namespace Emby.Dlna.PlayTo
{
public class PlayToManager : IDisposable
public sealed class PlayToManager : IDisposable
{
private readonly ILogger _logger;
private readonly ISessionManager _sessionManager;
@ -231,6 +231,7 @@ namespace Emby.Dlna.PlayTo
}
}
/// <inheritdoc />
public void Dispose()
{
_deviceDiscovery.DeviceDiscovered -= OnDeviceDiscoveryDeviceDiscovered;
@ -244,6 +245,9 @@ namespace Emby.Dlna.PlayTo
}
_sessionLock.Dispose();
_disposeCancellationTokenSource.Dispose();
_disposed = true;
}
}

View File

@ -32,18 +32,15 @@ namespace Emby.Dlna.PlayTo
DeviceService service,
string command,
string postData,
bool logRequest = true,
string header = null)
string header = null,
CancellationToken cancellationToken = default)
{
var cancellationToken = CancellationToken.None;
var url = NormalizeServiceUrl(baseUrl, service.ControlUrl);
using (var response = await PostSoapDataAsync(
url,
$"\"{service.ServiceType}#{command}\"",
postData,
header,
logRequest,
cancellationToken)
.ConfigureAwait(false))
using (var stream = response.Content)
@ -63,7 +60,7 @@ namespace Emby.Dlna.PlayTo
return serviceUrl;
}
if (!serviceUrl.StartsWith("/"))
if (!serviceUrl.StartsWith("/", StringComparison.Ordinal))
{
serviceUrl = "/" + serviceUrl;
}
@ -127,7 +124,6 @@ namespace Emby.Dlna.PlayTo
string soapAction,
string postData,
string header,
bool logRequest,
CancellationToken cancellationToken)
{
if (soapAction[0] != '\"')

View File

@ -134,19 +134,19 @@ namespace Emby.Notifications.Api
_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)
{
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)
{
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)
{
return new NotificationsSummary
@ -170,17 +170,17 @@ namespace Emby.Notifications.Api
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)
{
}
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request")]
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request", Justification = "Required for ServiceStack")]
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)
{
return new NotificationResult();

View File

@ -118,6 +118,11 @@ namespace Emby.Server.Implementations
/// </summary>
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 SqliteDisplayPreferencesRepository _displayPreferencesRepository;
@ -889,18 +894,18 @@ namespace Emby.Server.Implementations
.GetCommandLineArgs()
.Distinct();
// Get all 'JELLYFIN_' prefixed environment variables
// Get all relevant environment variables
var allEnvVars = Environment.GetEnvironmentVariables();
var jellyfinEnvVars = new Dictionary<object, object>();
var relevantEnvVars = new Dictionary<object, object>();
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("Operating system: {OS}", OperatingSystem.Name);
logger.LogInformation("Architecture: {Architecture}", RuntimeInformation.OSArchitecture);

View File

@ -5011,6 +5011,11 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
commandText += " order by ListOrder";
if (query.Limit > 0)
{
commandText += " LIMIT " + query.Limit;
}
using (var connection = GetConnection(true))
{
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";
if (query.Limit > 0)
{
commandText += " LIMIT " + query.Limit;
}
using (var connection = GetConnection(true))
{
var list = new List<PersonInfo>();

View File

@ -96,13 +96,13 @@ namespace Emby.Server.Implementations.HttpClientManager
switch (options.DecompressionMethod)
{
case CompressionMethod.Deflate | CompressionMethod.Gzip:
case CompressionMethods.Deflate | CompressionMethods.Gzip:
request.Headers.Add(HeaderNames.AcceptEncoding, new[] { "gzip", "deflate" });
break;
case CompressionMethod.Deflate:
case CompressionMethods.Deflate:
request.Headers.Add(HeaderNames.AcceptEncoding, "deflate");
break;
case CompressionMethod.Gzip:
case CompressionMethods.Gzip:
request.Headers.Add(HeaderNames.AcceptEncoding, "gzip");
break;
default:
@ -239,15 +239,10 @@ namespace Emby.Server.Implementations.HttpClientManager
var httpWebRequest = GetRequestMessage(options, httpMethod);
if (options.RequestContentBytes != null
|| !string.IsNullOrEmpty(options.RequestContent)
if (!string.IsNullOrEmpty(options.RequestContent)
|| httpMethod == HttpMethod.Post)
{
if (options.RequestContentBytes != null)
{
httpWebRequest.Content = new ByteArrayContent(options.RequestContentBytes);
}
else if (options.RequestContent != null)
if (options.RequestContent != null)
{
httpWebRequest.Content = new StringContent(
options.RequestContent,

View File

@ -72,7 +72,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
UserAgent = "Emby/3.0",
// Shouldn't matter but may cause issues
DecompressionMethod = CompressionMethod.None
DecompressionMethod = CompressionMethods.None
};
using (var response = await _httpClient.SendAsync(httpRequestOptions, HttpMethod.Get).ConfigureAwait(false))

View File

@ -635,7 +635,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
ListingsProviderInfo providerInfo)
{
// 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
{
@ -665,7 +665,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
ListingsProviderInfo providerInfo)
{
// 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
{

View File

@ -83,7 +83,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
{
CancellationToken = cancellationToken,
Url = path,
DecompressionMethod = CompressionMethod.Gzip,
DecompressionMethod = CompressionMethods.Gzip,
},
HttpMethod.Get).ConfigureAwait(false))
using (var stream = res.Content)

View File

@ -59,7 +59,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
Url = url,
CancellationToken = CancellationToken.None,
BufferContent = false,
DecompressionMethod = CompressionMethod.None
DecompressionMethod = CompressionMethods.None
};
foreach (var header in mediaSource.RequiredHttpHeaders)

View File

@ -90,7 +90,17 @@
"UserPolicyUpdatedWithName": "تم تحديث سياسة المستخدم {0}",
"UserStartedPlayingItemWithValues": "قام {0} ببدء تشغيل {1} على {2}",
"UserStoppedPlayingItemWithValues": "قام {0} بإيقاف تشغيل {1} على {2}",
"ValueHasBeenAddedToLibrary": "{0} تم اضافتها الى مكتبة الوسائط",
"ValueSpecialEpisodeName": "مميز - {0}",
"VersionNumber": "الإصدار رقم {0}"
"ValueHasBeenAddedToLibrary": "تمت اضافت {0} إلى مكتبة الوسائط",
"ValueSpecialEpisodeName": "خاص - {0}",
"VersionNumber": "النسخة {0}",
"TaskCleanCacheDescription": "يحذف ملفات ذاكرة التخزين المؤقت التي لم يعد النظام بحاجة إليها.",
"TaskCleanCache": "احذف مجلد ذاكرة التخزين المؤقت",
"TasksChannelsCategory": "قنوات الإنترنت",
"TasksLibraryCategory": "مكتبة",
"TasksMaintenanceCategory": "صيانة",
"TaskRefreshLibraryDescription": "يقوم بفصح مكتبة الوسائط الخاصة بك بحثًا عن ملفات جديدة وتحديث البيانات الوصفية.",
"TaskRefreshLibrary": "افحص مكتبة الوسائط",
"TaskRefreshChapterImagesDescription": "إنشاء صور مصغرة لمقاطع الفيديو ذات فصول.",
"TaskRefreshChapterImages": "استخراج صور الفصل",
"TasksApplicationCategory": "تطبيق"
}

View File

@ -11,11 +11,11 @@
"Collections": "Colecciones",
"DeviceOfflineWithName": "{0} se ha desconectado",
"DeviceOnlineWithName": "{0} está conectado",
"FailedLoginAttemptWithUserName": "Error al intentar iniciar sesión desde {0}",
"FailedLoginAttemptWithUserName": "Error al intentar iniciar sesión de {0}",
"Favorites": "Favoritos",
"Folders": "Carpetas",
"Genres": "Géneros",
"HeaderAlbumArtists": "Artistas de álbumes",
"HeaderAlbumArtists": "Artistas de álbum",
"HeaderCameraUploads": "Subidas de cámara",
"HeaderContinueWatching": "Continuar viendo",
"HeaderFavoriteAlbums": "Álbumes favoritos",
@ -24,7 +24,7 @@
"HeaderFavoriteShows": "Programas favoritos",
"HeaderFavoriteSongs": "Canciones favoritas",
"HeaderLiveTV": "TV en vivo",
"HeaderNextUp": "Continuar Viendo",
"HeaderNextUp": "A Continuación",
"HeaderRecordingGroups": "Grupos de grabación",
"HomeVideos": "Videos caseros",
"Inherit": "Heredar",
@ -35,47 +35,47 @@
"Latest": "Últimos",
"MessageApplicationUpdated": "El servidor Jellyfin fue actualizado",
"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",
"MessageServerConfigurationUpdated": "Fue actualizada la configuración del servidor",
"MixedContent": "Contenido mixto",
"MessageNamedServerConfigurationUpdatedWithValue": "Se ha actualizado la sección {0} de la configuración del servidor",
"MessageServerConfigurationUpdated": "Se ha actualizado la configuración del servidor",
"MixedContent": "Contenido mezclado",
"Movies": "Películas",
"Music": "Música",
"MusicVideos": "Videos musicales",
"NameInstallFailed": "{0} error de instalación",
"NameInstallFailed": "{0} instalación fallida",
"NameSeasonNumber": "Temporada {0}",
"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",
"NotificationOptionApplicationUpdateInstalled": "Actualización de la aplicación instalada",
"NotificationOptionAudioPlayback": "Se inició 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",
"NotificationOptionNewLibraryContent": "Nuevo contenido añadido",
"NotificationOptionPluginError": "Error en plugin",
"NotificationOptionPluginInstalled": "Plugin instalado",
"NotificationOptionPluginUninstalled": "Plugin desinstalado",
"NotificationOptionPluginUpdateInstalled": "Actualización del complemento instalada",
"NotificationOptionServerRestartRequired": "Se requiere reinicio del servidor",
"NotificationOptionTaskFailed": "Error de tarea programada",
"NotificationOptionPluginError": "Falla de complemento",
"NotificationOptionPluginInstalled": "Complemento instalado",
"NotificationOptionPluginUninstalled": "Complemento desinstalado",
"NotificationOptionPluginUpdateInstalled": "Actualización de complemento instalada",
"NotificationOptionServerRestartRequired": "Se necesita reiniciar el Servidor",
"NotificationOptionTaskFailed": "Falla de tarea programada",
"NotificationOptionUserLockedOut": "Usuario bloqueado",
"NotificationOptionVideoPlayback": "Se inició la reproducción de video",
"NotificationOptionVideoPlaybackStopped": "Reproducción de video detenida",
"Photos": "Fotos",
"Playlists": "Listas de reproducción",
"Plugin": "Plugin",
"Plugin": "Complemento",
"PluginInstalledWithName": "{0} fue instalado",
"PluginUninstalledWithName": "{0} fue desinstalado",
"PluginUpdatedWithName": "{0} fue actualizado",
"ProviderValue": "Proveedor: {0}",
"ScheduledTaskFailedWithName": "{0} falló",
"ScheduledTaskStartedWithName": "{0} iniciada",
"ScheduledTaskStartedWithName": "{0} iniciado",
"ServerNameNeedsToBeRestarted": "{0} necesita ser reiniciado",
"Shows": "Series",
"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}",
"SubtitleDownloadFailureFromForItem": "Fallo de descarga de subtítulos desde {0} para {1}",
"SubtitleDownloadFailureFromForItem": "Falló la descarga de subtitulos desde {0} para {1}",
"Sync": "Sincronizar",
"System": "Sistema",
"TvShows": "Series de TV",
@ -87,10 +87,32 @@
"UserOfflineFromDevice": "{0} se ha desconectado de {1}",
"UserOnlineFromDevice": "{0} está en línea desde {1}",
"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}",
"UserStoppedPlayingItemWithValues": "{0} ha terminado de reproducir {1} en {2}",
"ValueHasBeenAddedToLibrary": "{0} ha sido añadido a tu biblioteca multimedia",
"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"
}

View File

@ -92,5 +92,27 @@
"UserStoppedPlayingItemWithValues": "{0} ha terminado de reproducirse {1} en {2}",
"ValueHasBeenAddedToLibrary": "{0} se han añadido a su biblioteca de medios",
"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"
}

View File

@ -92,5 +92,7 @@
"UserStoppedPlayingItemWithValues": "{0} vient d'arrêter la lecture de {1} sur {2}",
"ValueHasBeenAddedToLibrary": "{0} a été ajouté à votre médiathèque",
"ValueSpecialEpisodeName": "Spécial - {0}",
"VersionNumber": "Version {0}"
"VersionNumber": "Version {0}",
"TasksLibraryCategory": "Bibliothèque",
"TasksMaintenanceCategory": "Entretien"
}

View File

@ -7,7 +7,7 @@
"Books": "Könyvek",
"CameraImageUploadedFrom": "Új kamerakép került feltöltésre innen: {0}",
"Channels": "Csatornák",
"ChapterNameValue": "Jelenet {0}",
"ChapterNameValue": "{0}. jelenet",
"Collections": "Gyűjtemények",
"DeviceOfflineWithName": "{0} kijelentkezett",
"DeviceOnlineWithName": "{0} belépett",
@ -92,5 +92,27 @@
"UserStoppedPlayingItemWithValues": "{0} befejezte {1} lejátászását itt: {2}",
"ValueHasBeenAddedToLibrary": "{0} hozzáadva a médiatárhoz",
"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."
}

View File

@ -91,5 +91,23 @@
"UserStoppedPlayingItemWithValues": "{0} は{2}で{1} の再生が終わりました",
"ValueHasBeenAddedToLibrary": "{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": "俳優や監督のデータのリフレッシュ"
}

View File

@ -92,5 +92,27 @@
"UserStoppedPlayingItemWithValues": "{2}에서 {0}이 {1} 재생을 마침",
"ValueHasBeenAddedToLibrary": "{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": "라이브러리"
}

View File

@ -91,5 +91,27 @@
"HeaderFavoriteShows": "Raidījumu Favorīti",
"HeaderFavoriteEpisodes": "Episožu 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"
}

View File

@ -92,5 +92,9 @@
"UserStoppedPlayingItemWithValues": "{0} har stoppet avspilling {1}",
"ValueHasBeenAddedToLibrary": "{0} har blitt lagt til i mediebiblioteket ditt",
"ValueSpecialEpisodeName": "Spesialepisode - {0}",
"VersionNumber": "Versjon {0}"
"VersionNumber": "Versjon {0}",
"TasksChannelsCategory": "Internett kanaler",
"TasksApplicationCategory": "Applikasjon",
"TasksLibraryCategory": "Bibliotek",
"TasksMaintenanceCategory": "Vedlikehold"
}

View File

@ -91,5 +91,27 @@
"Artists": "Artiști",
"Application": "Aplicație",
"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ță"
}

View File

@ -81,7 +81,7 @@
"Favorites": "Омиљено",
"FailedLoginAttemptWithUserName": "Неуспела пријава са {0}",
"DeviceOnlineWithName": "{0} се повезао",
"DeviceOfflineWithName": "{0} се одвезао",
"DeviceOfflineWithName": "{0} је прекинуо везу",
"Collections": "Колекције",
"ChapterNameValue": "Поглавље {0}",
"Channels": "Канали",
@ -91,5 +91,27 @@
"Artists": "Извођач",
"Application": "Апликација",
"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": "Одржавање"
}

View File

@ -92,5 +92,10 @@
"UserStoppedPlayingItemWithValues": "{0}, {2} cihazında {1} izlemeyi bitirdi",
"ValueHasBeenAddedToLibrary": "Medya kitaplığınıza {0} eklendi",
"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"
}

View File

@ -3,7 +3,7 @@
"AppDeviceValues": "应用: {0}, 设备: {1}",
"Application": "应用程序",
"Artists": "艺术家",
"AuthenticationSucceededWithUserName": "成功验证{0} ",
"AuthenticationSucceededWithUserName": "{0} 认证成功",
"Books": "书籍",
"CameraImageUploadedFrom": "新的相机图像已从 {0} 上传",
"Channels": "频道",

View File

@ -25,6 +25,7 @@ using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Serilog;
@ -259,7 +260,7 @@ namespace Jellyfin.Server
IApplicationPaths appPaths)
{
return new WebHostBuilder()
.UseKestrel(options =>
.UseKestrel((builderContext, options) =>
{
var addresses = appHost.ServerConfigurationManager
.Configuration
@ -282,6 +283,14 @@ namespace Jellyfin.Server
listenOptions.Protocols = HttpProtocols.Http1AndHttp2;
});
}
else if (builderContext.HostingEnvironment.IsDevelopment())
{
options.Listen(address, appHost.HttpsPort, listenOptions =>
{
listenOptions.UseHttps();
listenOptions.Protocols = HttpProtocols.Http1AndHttp2;
});
}
}
}
else
@ -297,6 +306,14 @@ namespace Jellyfin.Server
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))

View File

@ -1,11 +1,17 @@
{
"profiles": {
"Jellyfin.Server": {
"commandName": "Project"
"commandName": "Project",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"Jellyfin.Server (nowebclient)": {
"commandName": "Project",
"commandLineArgs": "--nowebclient"
"commandLineArgs": "--nowebclient",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

View File

@ -9,7 +9,7 @@ namespace MediaBrowser.Common.Cryptography
/// <summary>
/// Class containing extension methods for working with Jellyfin cryptography objects.
/// </summary>
public static class Extensions
public static class CryptoExtensions
{
/// <summary>
/// Creates a new <see cref="PasswordHash" /> instance.

View File

@ -30,7 +30,7 @@
<!-- Code analyzers-->
<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="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="All" /> -->
<PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" />

View File

@ -20,7 +20,7 @@ namespace MediaBrowser.Common.Net
RequestHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
CacheMode = CacheMode.None;
DecompressionMethod = CompressionMethod.Deflate;
DecompressionMethod = CompressionMethods.Deflate;
}
/// <summary>
@ -29,7 +29,7 @@ namespace MediaBrowser.Common.Net
/// <value>The URL.</value>
public string Url { get; set; }
public CompressionMethod DecompressionMethod { get; set; }
public CompressionMethods DecompressionMethod { get; set; }
/// <summary>
/// Gets or sets the accept header.
@ -83,8 +83,6 @@ namespace MediaBrowser.Common.Net
public string RequestContent { get; set; }
public byte[] RequestContentBytes { get; set; }
public bool BufferContent { get; set; }
public bool LogErrorResponseBody { get; set; }
@ -112,7 +110,7 @@ namespace MediaBrowser.Common.Net
}
[Flags]
public enum CompressionMethod
public enum CompressionMethods
{
None = 0b00000001,
Deflate = 0b00000010,

View File

@ -8,7 +8,7 @@ namespace MediaBrowser.Common.Net
/// <summary>
/// Class HttpResponseInfo.
/// </summary>
public class HttpResponseInfo : IDisposable
public sealed class HttpResponseInfo : IDisposable
{
#pragma warning disable CS1591
public HttpResponseInfo()

View File

@ -4,11 +4,21 @@ namespace MediaBrowser.Controller.Entities
{
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 string[] PersonTypes { get; set; }
public string[] ExcludePersonTypes { get; set; }
public int? MaxListOrder { get; set; }
public Guid AppearsInItemId { get; set; }
public string NameContains { get; set; }
public InternalPeopleQuery()