mirror of
https://github.com/jellyfin/jellyfin.git
synced 2025-12-09 18:43:05 +03:00
Compare commits
63 Commits
v10.7.6
...
release-10
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
72bd98964b | ||
|
|
a3d048a0d9 | ||
|
|
21a6c2e1e2 | ||
|
|
7873fe22fb | ||
|
|
1f5625d7f9 | ||
|
|
a0b053e4a1 | ||
|
|
3af63bf439 | ||
|
|
77f72dc607 | ||
|
|
16d9318e08 | ||
|
|
d8f865e93c | ||
|
|
3044dfc114 | ||
|
|
ace1e70c63 | ||
|
|
6a9a677111 | ||
|
|
cc35876f6b | ||
|
|
5611b2c038 | ||
|
|
163fb94bde | ||
|
|
5c4326daf4 | ||
|
|
2d369ca614 | ||
|
|
3c05079333 | ||
|
|
0c204f4706 | ||
|
|
1e3a524a7a | ||
|
|
99e22c499d | ||
|
|
dbbf97e588 | ||
|
|
4e9df69ffd | ||
|
|
7f38ef4c3c | ||
|
|
16549dead9 | ||
|
|
9bd1a9d19c | ||
|
|
67194994f9 | ||
|
|
c249e15f48 | ||
|
|
48ba5a9a30 | ||
|
|
dd13f8d16a | ||
|
|
b43a8a56dc | ||
|
|
3ec18f085e | ||
|
|
f2728b5a92 | ||
|
|
ee47a75f9f | ||
|
|
66e7e8bcd2 | ||
|
|
0de3a9465e | ||
|
|
b2f7417365 | ||
|
|
bf0c07abfe | ||
|
|
3a4cd01b13 | ||
|
|
7059761806 | ||
|
|
2cde59af44 | ||
|
|
52a850cc9b | ||
|
|
54435a1243 | ||
|
|
899be44388 | ||
|
|
354079862e | ||
|
|
01db9af821 | ||
|
|
9da2635d86 | ||
|
|
4caa597cde | ||
|
|
da34bd940e | ||
|
|
54efde4073 | ||
|
|
02cfa15582 | ||
|
|
0e171d794c | ||
|
|
d5f2384375 | ||
|
|
816b3f5c2e | ||
|
|
a234388552 | ||
|
|
30d38bd5c6 | ||
|
|
9f49fe2e99 | ||
|
|
f720a0fca2 | ||
|
|
aadff77531 | ||
|
|
89fc5aa11a | ||
|
|
cb91595a24 | ||
|
|
6233bae7f0 |
@@ -127,6 +127,7 @@
|
||||
- [xosdy](https://github.com/xosdy)
|
||||
- [XVicarious](https://github.com/XVicarious)
|
||||
- [YouKnowBlom](https://github.com/YouKnowBlom)
|
||||
- [KristupasSavickas](https://github.com/KristupasSavickas)
|
||||
|
||||
# Emby Contributors
|
||||
|
||||
|
||||
37
Dockerfile
37
Dockerfile
@@ -1,11 +1,11 @@
|
||||
ARG DOTNET_VERSION=3.1
|
||||
ARG FFMPEG_VERSION=latest
|
||||
|
||||
FROM node:alpine as web-builder
|
||||
ARG JELLYFIN_WEB_VERSION=master
|
||||
ARG JELLYFIN_WEB_VERSION=10.5.5
|
||||
RUN apk add curl git \
|
||||
&& curl -L https://github.com/jellyfin/jellyfin-web/archive/${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \
|
||||
&& cd jellyfin-web-* \
|
||||
&& git clone --branch release-10.5.z --single-branch https://github.com/jellyfin/jellyfin-web.git \
|
||||
&& cd jellyfin-web \
|
||||
&& git checkout tags/v${JELLYFIN_WEB_VERSION} \
|
||||
&& yarn install \
|
||||
&& yarn build \
|
||||
&& mv dist /dist
|
||||
@@ -18,7 +18,6 @@ ENV DOTNET_CLI_TELEMETRY_OPTOUT=1
|
||||
# see https://success.docker.com/article/how-to-reserve-resource-temporarily-unavailable-errors-due-to-tasksmax-setting
|
||||
RUN dotnet publish Jellyfin.Server --disable-parallel --configuration Release --output="/jellyfin" --self-contained --runtime linux-x64 "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none"
|
||||
|
||||
FROM jellyfin/ffmpeg:${FFMPEG_VERSION} as ffmpeg
|
||||
FROM debian:buster-slim
|
||||
|
||||
# https://askubuntu.com/questions/972516/debian-frontend-environment-variable
|
||||
@@ -28,31 +27,27 @@ ARG APT_KEY_DONT_WARN_ON_DANGEROUS_USAGE=DontWarn
|
||||
# https://github.com/NVIDIA/nvidia-docker/wiki/Installation-(Native-GPU-Support)
|
||||
ENV NVIDIA_DRIVER_CAPABILITIES="compute,video,utility"
|
||||
|
||||
COPY --from=ffmpeg /opt/ffmpeg /opt/ffmpeg
|
||||
COPY --from=builder /jellyfin /jellyfin
|
||||
COPY --from=web-builder /dist /jellyfin/jellyfin-web
|
||||
# Install dependencies:
|
||||
# libfontconfig1: needed for Skia
|
||||
# libgomp1: needed for ffmpeg
|
||||
# libva-drm2: needed for ffmpeg
|
||||
# mesa-va-drivers: needed for VAAPI
|
||||
# mesa-va-drivers: needed for AMD VAAPI
|
||||
RUN apt-get update \
|
||||
&& apt-get install --no-install-recommends --no-install-suggests -y ca-certificates gnupg wget apt-transport-https \
|
||||
&& wget -O - https://repo.jellyfin.org/jellyfin_team.gpg.key | apt-key add - \
|
||||
&& echo "deb [arch=$( dpkg --print-architecture )] https://repo.jellyfin.org/$( awk -F'=' '/^ID=/{ print $NF }' /etc/os-release ) $( awk -F'=' '/^VERSION_CODENAME=/{ print $NF }' /etc/os-release ) main" | tee /etc/apt/sources.list.d/jellyfin.list \
|
||||
&& apt-get update \
|
||||
&& apt-get install --no-install-recommends --no-install-suggests -y \
|
||||
libfontconfig1 \
|
||||
libgomp1 \
|
||||
libva-drm2 \
|
||||
mesa-va-drivers \
|
||||
jellyfin-ffmpeg \
|
||||
openssl \
|
||||
ca-certificates \
|
||||
vainfo \
|
||||
i965-va-driver \
|
||||
&& apt-get clean autoclean -y\
|
||||
&& apt-get autoremove -y\
|
||||
locales \
|
||||
&& apt-get remove gnupg wget apt-transport-https -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 \
|
||||
&& ln -s /opt/ffmpeg/bin/ffmpeg /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
|
||||
|
||||
@@ -61,4 +56,4 @@ VOLUME /cache /config /media
|
||||
ENTRYPOINT ["./jellyfin/jellyfin", \
|
||||
"--datadir", "/config", \
|
||||
"--cachedir", "/cache", \
|
||||
"--ffmpeg", "/usr/local/bin/ffmpeg"]
|
||||
"--ffmpeg", "/usr/lib/jellyfin-ffmpeg/ffmpeg"]
|
||||
|
||||
@@ -6,10 +6,11 @@ ARG DOTNET_VERSION=3.1
|
||||
|
||||
|
||||
FROM node:alpine as web-builder
|
||||
ARG JELLYFIN_WEB_VERSION=master
|
||||
ARG JELLYFIN_WEB_VERSION=10.5.5
|
||||
RUN apk add curl git \
|
||||
&& curl -L https://github.com/jellyfin/jellyfin-web/archive/${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \
|
||||
&& cd jellyfin-web-* \
|
||||
&& git clone --branch release-10.5.z --single-branch https://github.com/jellyfin/jellyfin-web.git \
|
||||
&& cd jellyfin-web \
|
||||
&& git checkout tags/v${JELLYFIN_WEB_VERSION} \
|
||||
&& yarn install \
|
||||
&& yarn build \
|
||||
&& mv dist /dist
|
||||
@@ -38,8 +39,8 @@ ENV NVIDIA_DRIVER_CAPABILITIES="compute,video,utility"
|
||||
COPY --from=qemu /usr/bin/qemu-arm-static /usr/bin
|
||||
RUN apt-get update \
|
||||
&& apt-get install --no-install-recommends --no-install-suggests -y ca-certificates gnupg curl && \
|
||||
curl -s https://repo.jellyfin.org/debian/jellyfin_team.gpg.key | apt-key add - && \
|
||||
curl -s https://keyserver.ubuntu.com/pks/lookup?op=get\&search=0x6587ffd6536b8826e88a62547876ae518cbcf2f2 | apt-key add - && \
|
||||
curl -ks https://repo.jellyfin.org/debian/jellyfin_team.gpg.key | apt-key add - && \
|
||||
curl -ks https://keyserver.ubuntu.com/pks/lookup?op=get\&search=0x6587ffd6536b8826e88a62547876ae518cbcf2f2 | apt-key add - && \
|
||||
echo 'deb [arch=armhf] https://repo.jellyfin.org/debian buster main' > /etc/apt/sources.list.d/jellyfin.list && \
|
||||
echo "deb http://ppa.launchpad.net/ubuntu-raspi2/ppa/ubuntu bionic main">> /etc/apt/sources.list.d/raspbins.list && \
|
||||
apt-get update && \
|
||||
@@ -69,4 +70,4 @@ VOLUME /cache /config /media
|
||||
ENTRYPOINT ["./jellyfin/jellyfin", \
|
||||
"--datadir", "/config", \
|
||||
"--cachedir", "/cache", \
|
||||
"--ffmpeg", "/usr/lib/jellyfin-ffmpeg"]
|
||||
"--ffmpeg", "/usr/lib/jellyfin-ffmpeg/ffmpeg"]
|
||||
|
||||
@@ -6,10 +6,11 @@ ARG DOTNET_VERSION=3.1
|
||||
|
||||
|
||||
FROM node:alpine as web-builder
|
||||
ARG JELLYFIN_WEB_VERSION=master
|
||||
ARG JELLYFIN_WEB_VERSION=10.5.5
|
||||
RUN apk add curl git \
|
||||
&& curl -L https://github.com/jellyfin/jellyfin-web/archive/${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \
|
||||
&& cd jellyfin-web-* \
|
||||
&& git clone --branch release-10.5.z --single-branch https://github.com/jellyfin/jellyfin-web.git \
|
||||
&& cd jellyfin-web \
|
||||
&& git checkout tags/v${JELLYFIN_WEB_VERSION} \
|
||||
&& yarn install \
|
||||
&& yarn build \
|
||||
&& mv dist /dist
|
||||
|
||||
@@ -415,31 +415,99 @@ namespace Emby.Dlna.Didl
|
||||
}
|
||||
}
|
||||
|
||||
if (item is Episode episode && context is Season season)
|
||||
return item is Episode episode
|
||||
? GetEpisodeDisplayName(episode, context)
|
||||
: item.Name;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets episode display name appropriate for the given context.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If context is a season, this will return a string containing just episode number and name.
|
||||
/// Otherwise the result will include series nams and season number.
|
||||
/// </remarks>
|
||||
/// <param name="episode">The episode.</param>
|
||||
/// <param name="context">Current context.</param>
|
||||
/// <returns>Formatted name of the episode.</returns>
|
||||
private string GetEpisodeDisplayName(Episode episode, BaseItem context)
|
||||
{
|
||||
string[] components;
|
||||
|
||||
if (context is Season season)
|
||||
{
|
||||
// This is a special embedded within a season
|
||||
if (item.ParentIndexNumber.HasValue && item.ParentIndexNumber.Value == 0
|
||||
if (episode.ParentIndexNumber.HasValue && episode.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"),
|
||||
episode.Name);
|
||||
}
|
||||
|
||||
if (item.IndexNumber.HasValue)
|
||||
// inside a season use simple format (ex. '12 - Episode Name')
|
||||
var epNumberName = GetEpisodeIndexFullName(episode);
|
||||
components = new[] { epNumberName, episode.Name };
|
||||
}
|
||||
else
|
||||
{
|
||||
// outside a season include series and season details (ex. 'TV Show - S05E11 - Episode Name')
|
||||
var epNumberName = GetEpisodeNumberDisplayName(episode);
|
||||
components = new[] { episode.SeriesName, epNumberName, episode.Name };
|
||||
}
|
||||
|
||||
return string.Join(" - ", components.Where(NotNullOrWhiteSpace));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets complete episode number.
|
||||
/// </summary>
|
||||
/// <param name="episode">The episode.</param>
|
||||
/// <returns>For single episodes returns just the number. For double episodes - current and ending numbers.</returns>
|
||||
private string GetEpisodeIndexFullName(Episode episode)
|
||||
{
|
||||
var name = string.Empty;
|
||||
if (episode.IndexNumber.HasValue)
|
||||
{
|
||||
name += episode.IndexNumber.Value.ToString("00", CultureInfo.InvariantCulture);
|
||||
|
||||
if (episode.IndexNumberEnd.HasValue)
|
||||
{
|
||||
var number = item.IndexNumber.Value.ToString("00", CultureInfo.InvariantCulture);
|
||||
|
||||
if (episode.IndexNumberEnd.HasValue)
|
||||
{
|
||||
number += "-" + episode.IndexNumberEnd.Value.ToString("00", CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
return number + " - " + item.Name;
|
||||
name += "-" + episode.IndexNumberEnd.Value.ToString("00", CultureInfo.InvariantCulture);
|
||||
}
|
||||
}
|
||||
|
||||
return item.Name;
|
||||
return name;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets episode number formatted as 'S##E##'.
|
||||
/// </summary>
|
||||
/// <param name="episode">The episode.</param>
|
||||
/// <returns>Formatted episode number.</returns>
|
||||
private string GetEpisodeNumberDisplayName(Episode episode)
|
||||
{
|
||||
var name = string.Empty;
|
||||
var seasonNumber = episode.Season?.IndexNumber;
|
||||
|
||||
if (seasonNumber.HasValue)
|
||||
{
|
||||
name = "S" + seasonNumber.Value.ToString("00", CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
var indexName = GetEpisodeIndexFullName(episode);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(indexName))
|
||||
{
|
||||
name += "E" + indexName;
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
private bool NotNullOrWhiteSpace(string s) => !string.IsNullOrWhiteSpace(s);
|
||||
|
||||
private void AddAudioResource(DlnaOptions options, XmlWriter writer, BaseItem audio, string deviceId, Filter filter, StreamInfo streamInfo = null)
|
||||
{
|
||||
writer.WriteStartElement(string.Empty, "res", NS_DIDL);
|
||||
@@ -983,19 +1051,58 @@ namespace Emby.Dlna.Didl
|
||||
}
|
||||
}
|
||||
|
||||
item = item.GetParents().FirstOrDefault(i => i.HasImage(ImageType.Primary));
|
||||
|
||||
if (item != null)
|
||||
// For audio tracks without art use album art if available.
|
||||
if (item is Audio audioItem)
|
||||
{
|
||||
if (item.HasImage(ImageType.Primary))
|
||||
{
|
||||
return GetImageInfo(item, ImageType.Primary);
|
||||
}
|
||||
var album = audioItem.AlbumEntity;
|
||||
return album != null && album.HasImage(ImageType.Primary)
|
||||
? GetImageInfo(album, ImageType.Primary)
|
||||
: null;
|
||||
}
|
||||
|
||||
// Don't look beyond album/playlist level. Metadata service may assign an image from a different album/show to the parent folder.
|
||||
if (item is MusicAlbum || item is Playlist)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// For other item types check parents, but be aware that image retrieved from a parent may be not suitable for this media item.
|
||||
var parentWithImage = GetFirstParentWithImageBelowUserRoot(item);
|
||||
if (parentWithImage != null)
|
||||
{
|
||||
return GetImageInfo(parentWithImage, ImageType.Primary);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private BaseItem GetFirstParentWithImageBelowUserRoot(BaseItem item)
|
||||
{
|
||||
if (item == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (item.HasImage(ImageType.Primary))
|
||||
{
|
||||
return item;
|
||||
}
|
||||
|
||||
var parent = item.GetParent();
|
||||
if (parent is UserRootFolder)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// terminate in case we went past user root folder (unlikely?)
|
||||
if (parent is Folder folder && folder.IsRoot)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return GetFirstParentWithImageBelowUserRoot(parent);
|
||||
}
|
||||
|
||||
private ImageDownloadInfo GetImageInfo(BaseItem item, ImageType type)
|
||||
{
|
||||
var imageInfo = item.GetImageInfo(type, 0);
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace Emby.Dlna.Profiles
|
||||
{
|
||||
Name = "Generic Device";
|
||||
|
||||
ProtocolInfo = "http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*image/jpeg:*,http-get:*image/png:*,http-get:*image/gif:*,http-get:*image/tiff:*";
|
||||
ProtocolInfo = "http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma:*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*:image/jpeg:*,http-get:*:image/png:*,http-get:*:image/gif:*,http-get:*:image/tiff:*";
|
||||
|
||||
Manufacturer = "Jellyfin";
|
||||
ModelDescription = "UPnP/AV 1.0 Compliant Media Server";
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<MaxStaticBitrate>140000000</MaxStaticBitrate>
|
||||
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
||||
<MaxStaticMusicBitrate xsi:nil="true" />
|
||||
<ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*image/jpeg:*,http-get:*image/png:*,http-get:*image/gif:*,http-get:*image/tiff:*</ProtocolInfo>
|
||||
<ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma:*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*:image/jpeg:*,http-get:*:image/png:*,http-get:*:image/gif:*,http-get:*:image/tiff:*</ProtocolInfo>
|
||||
<TimelineOffsetSeconds>0</TimelineOffsetSeconds>
|
||||
<RequiresPlainVideoItems>false</RequiresPlainVideoItems>
|
||||
<RequiresPlainFolders>false</RequiresPlainFolders>
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
<MaxStaticBitrate>140000000</MaxStaticBitrate>
|
||||
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
||||
<MaxStaticMusicBitrate xsi:nil="true" />
|
||||
<ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*image/jpeg:*,http-get:*image/png:*,http-get:*image/gif:*,http-get:*image/tiff:*</ProtocolInfo>
|
||||
<ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma:*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*:image/jpeg:*,http-get:*:image/png:*,http-get:*:image/gif:*,http-get:*:image/tiff:*</ProtocolInfo>
|
||||
<TimelineOffsetSeconds>0</TimelineOffsetSeconds>
|
||||
<RequiresPlainVideoItems>false</RequiresPlainVideoItems>
|
||||
<RequiresPlainFolders>false</RequiresPlainFolders>
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
<MaxStaticBitrate>140000000</MaxStaticBitrate>
|
||||
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
||||
<MaxStaticMusicBitrate xsi:nil="true" />
|
||||
<ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*image/jpeg:*,http-get:*image/png:*,http-get:*image/gif:*,http-get:*image/tiff:*</ProtocolInfo>
|
||||
<ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma:*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*:image/jpeg:*,http-get:*:image/png:*,http-get:*:image/gif:*,http-get:*:image/tiff:*</ProtocolInfo>
|
||||
<TimelineOffsetSeconds>10</TimelineOffsetSeconds>
|
||||
<RequiresPlainVideoItems>true</RequiresPlainVideoItems>
|
||||
<RequiresPlainFolders>true</RequiresPlainFolders>
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
<MaxStaticBitrate>140000000</MaxStaticBitrate>
|
||||
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
||||
<MaxStaticMusicBitrate xsi:nil="true" />
|
||||
<ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*image/jpeg:*,http-get:*image/png:*,http-get:*image/gif:*,http-get:*image/tiff:*</ProtocolInfo>
|
||||
<ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma:*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*:image/jpeg:*,http-get:*:image/png:*,http-get:*:image/gif:*,http-get:*:image/tiff:*</ProtocolInfo>
|
||||
<TimelineOffsetSeconds>10</TimelineOffsetSeconds>
|
||||
<RequiresPlainVideoItems>false</RequiresPlainVideoItems>
|
||||
<RequiresPlainFolders>false</RequiresPlainFolders>
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
<MaxStaticBitrate>140000000</MaxStaticBitrate>
|
||||
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
||||
<MaxStaticMusicBitrate xsi:nil="true" />
|
||||
<ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*image/jpeg:*,http-get:*image/png:*,http-get:*image/gif:*,http-get:*image/tiff:*</ProtocolInfo>
|
||||
<ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma:*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*:image/jpeg:*,http-get:*:image/png:*,http-get:*:image/gif:*,http-get:*:image/tiff:*</ProtocolInfo>
|
||||
<TimelineOffsetSeconds>0</TimelineOffsetSeconds>
|
||||
<RequiresPlainVideoItems>false</RequiresPlainVideoItems>
|
||||
<RequiresPlainFolders>false</RequiresPlainFolders>
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
<MaxStaticBitrate>140000000</MaxStaticBitrate>
|
||||
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
||||
<MaxStaticMusicBitrate xsi:nil="true" />
|
||||
<ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*image/jpeg:*,http-get:*image/png:*,http-get:*image/gif:*,http-get:*image/tiff:*</ProtocolInfo>
|
||||
<ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma:*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*:image/jpeg:*,http-get:*:image/png:*,http-get:*:image/gif:*,http-get:*:image/tiff:*</ProtocolInfo>
|
||||
<TimelineOffsetSeconds>0</TimelineOffsetSeconds>
|
||||
<RequiresPlainVideoItems>false</RequiresPlainVideoItems>
|
||||
<RequiresPlainFolders>false</RequiresPlainFolders>
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
<MaxStaticBitrate>140000000</MaxStaticBitrate>
|
||||
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
||||
<MaxStaticMusicBitrate xsi:nil="true" />
|
||||
<ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*image/jpeg:*,http-get:*image/png:*,http-get:*image/gif:*,http-get:*image/tiff:*</ProtocolInfo>
|
||||
<ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma:*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*:image/jpeg:*,http-get:*:image/png:*,http-get:*:image/gif:*,http-get:*:image/tiff:*</ProtocolInfo>
|
||||
<TimelineOffsetSeconds>0</TimelineOffsetSeconds>
|
||||
<RequiresPlainVideoItems>false</RequiresPlainVideoItems>
|
||||
<RequiresPlainFolders>false</RequiresPlainFolders>
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
<MaxStaticBitrate>140000000</MaxStaticBitrate>
|
||||
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
||||
<MaxStaticMusicBitrate xsi:nil="true" />
|
||||
<ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*image/jpeg:*,http-get:*image/png:*,http-get:*image/gif:*,http-get:*image/tiff:*</ProtocolInfo>
|
||||
<ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma:*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*:image/jpeg:*,http-get:*:image/png:*,http-get:*:image/gif:*,http-get:*:image/tiff:*</ProtocolInfo>
|
||||
<TimelineOffsetSeconds>10</TimelineOffsetSeconds>
|
||||
<RequiresPlainVideoItems>false</RequiresPlainVideoItems>
|
||||
<RequiresPlainFolders>false</RequiresPlainFolders>
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<MaxStaticBitrate>140000000</MaxStaticBitrate>
|
||||
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
||||
<MaxStaticMusicBitrate xsi:nil="true" />
|
||||
<ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*image/jpeg:*,http-get:*image/png:*,http-get:*image/gif:*,http-get:*image/tiff:*</ProtocolInfo>
|
||||
<ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma:*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*:image/jpeg:*,http-get:*:image/png:*,http-get:*:image/gif:*,http-get:*:image/tiff:*</ProtocolInfo>
|
||||
<TimelineOffsetSeconds>0</TimelineOffsetSeconds>
|
||||
<RequiresPlainVideoItems>false</RequiresPlainVideoItems>
|
||||
<RequiresPlainFolders>false</RequiresPlainFolders>
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
<MaxStaticBitrate>140000000</MaxStaticBitrate>
|
||||
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
||||
<MaxStaticMusicBitrate xsi:nil="true" />
|
||||
<ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*image/jpeg:*,http-get:*image/png:*,http-get:*image/gif:*,http-get:*image/tiff:*</ProtocolInfo>
|
||||
<ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma:*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*:image/jpeg:*,http-get:*:image/png:*,http-get:*:image/gif:*,http-get:*:image/tiff:*</ProtocolInfo>
|
||||
<TimelineOffsetSeconds>0</TimelineOffsetSeconds>
|
||||
<RequiresPlainVideoItems>false</RequiresPlainVideoItems>
|
||||
<RequiresPlainFolders>false</RequiresPlainFolders>
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
<MaxStaticBitrate>140000000</MaxStaticBitrate>
|
||||
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
||||
<MaxStaticMusicBitrate xsi:nil="true" />
|
||||
<ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*image/jpeg:*,http-get:*image/png:*,http-get:*image/gif:*,http-get:*image/tiff:*</ProtocolInfo>
|
||||
<ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma:*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*:image/jpeg:*,http-get:*:image/png:*,http-get:*:image/gif:*,http-get:*:image/tiff:*</ProtocolInfo>
|
||||
<TimelineOffsetSeconds>0</TimelineOffsetSeconds>
|
||||
<RequiresPlainVideoItems>true</RequiresPlainVideoItems>
|
||||
<RequiresPlainFolders>true</RequiresPlainFolders>
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
||||
<MaxStaticMusicBitrate xsi:nil="true" />
|
||||
<SonyAggregationFlags>10</SonyAggregationFlags>
|
||||
<ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*image/jpeg:*,http-get:*image/png:*,http-get:*image/gif:*,http-get:*image/tiff:*</ProtocolInfo>
|
||||
<ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma:*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*:image/jpeg:*,http-get:*:image/png:*,http-get:*:image/gif:*,http-get:*:image/tiff:*</ProtocolInfo>
|
||||
<TimelineOffsetSeconds>0</TimelineOffsetSeconds>
|
||||
<RequiresPlainVideoItems>false</RequiresPlainVideoItems>
|
||||
<RequiresPlainFolders>false</RequiresPlainFolders>
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
||||
<MaxStaticMusicBitrate xsi:nil="true" />
|
||||
<SonyAggregationFlags>10</SonyAggregationFlags>
|
||||
<ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*image/jpeg:*,http-get:*image/png:*,http-get:*image/gif:*,http-get:*image/tiff:*</ProtocolInfo>
|
||||
<ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma:*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*:image/jpeg:*,http-get:*:image/png:*,http-get:*:image/gif:*,http-get:*:image/tiff:*</ProtocolInfo>
|
||||
<TimelineOffsetSeconds>0</TimelineOffsetSeconds>
|
||||
<RequiresPlainVideoItems>false</RequiresPlainVideoItems>
|
||||
<RequiresPlainFolders>false</RequiresPlainFolders>
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
||||
<MaxStaticMusicBitrate xsi:nil="true" />
|
||||
<SonyAggregationFlags>10</SonyAggregationFlags>
|
||||
<ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*image/jpeg:*,http-get:*image/png:*,http-get:*image/gif:*,http-get:*image/tiff:*</ProtocolInfo>
|
||||
<ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma:*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*:image/jpeg:*,http-get:*:image/png:*,http-get:*:image/gif:*,http-get:*:image/tiff:*</ProtocolInfo>
|
||||
<TimelineOffsetSeconds>0</TimelineOffsetSeconds>
|
||||
<RequiresPlainVideoItems>false</RequiresPlainVideoItems>
|
||||
<RequiresPlainFolders>false</RequiresPlainFolders>
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
||||
<MaxStaticMusicBitrate xsi:nil="true" />
|
||||
<SonyAggregationFlags>10</SonyAggregationFlags>
|
||||
<ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*image/jpeg:*,http-get:*image/png:*,http-get:*image/gif:*,http-get:*image/tiff:*</ProtocolInfo>
|
||||
<ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma:*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*:image/jpeg:*,http-get:*:image/png:*,http-get:*:image/gif:*,http-get:*:image/tiff:*</ProtocolInfo>
|
||||
<TimelineOffsetSeconds>0</TimelineOffsetSeconds>
|
||||
<RequiresPlainVideoItems>false</RequiresPlainVideoItems>
|
||||
<RequiresPlainFolders>false</RequiresPlainFolders>
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
||||
<MaxStaticMusicBitrate xsi:nil="true" />
|
||||
<SonyAggregationFlags>10</SonyAggregationFlags>
|
||||
<ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*image/jpeg:*,http-get:*image/png:*,http-get:*image/gif:*,http-get:*image/tiff:*</ProtocolInfo>
|
||||
<ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma:*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*:image/jpeg:*,http-get:*:image/png:*,http-get:*:image/gif:*,http-get:*:image/tiff:*</ProtocolInfo>
|
||||
<TimelineOffsetSeconds>0</TimelineOffsetSeconds>
|
||||
<RequiresPlainVideoItems>false</RequiresPlainVideoItems>
|
||||
<RequiresPlainFolders>false</RequiresPlainFolders>
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
||||
<MaxStaticMusicBitrate xsi:nil="true" />
|
||||
<SonyAggregationFlags>10</SonyAggregationFlags>
|
||||
<ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*image/jpeg:*,http-get:*image/png:*,http-get:*image/gif:*,http-get:*image/tiff:*</ProtocolInfo>
|
||||
<ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma:*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*:image/jpeg:*,http-get:*:image/png:*,http-get:*:image/gif:*,http-get:*:image/tiff:*</ProtocolInfo>
|
||||
<TimelineOffsetSeconds>0</TimelineOffsetSeconds>
|
||||
<RequiresPlainVideoItems>false</RequiresPlainVideoItems>
|
||||
<RequiresPlainFolders>false</RequiresPlainFolders>
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
<MaxStaticBitrate>140000000</MaxStaticBitrate>
|
||||
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
||||
<MaxStaticMusicBitrate xsi:nil="true" />
|
||||
<ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*image/jpeg:*,http-get:*image/png:*,http-get:*image/gif:*,http-get:*image/tiff:*</ProtocolInfo>
|
||||
<ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma:*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*:image/jpeg:*,http-get:*:image/png:*,http-get:*:image/gif:*,http-get:*:image/tiff:*</ProtocolInfo>
|
||||
<TimelineOffsetSeconds>5</TimelineOffsetSeconds>
|
||||
<RequiresPlainVideoItems>false</RequiresPlainVideoItems>
|
||||
<RequiresPlainFolders>false</RequiresPlainFolders>
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
<MaxStaticBitrate>140000000</MaxStaticBitrate>
|
||||
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
||||
<MaxStaticMusicBitrate xsi:nil="true" />
|
||||
<ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*image/jpeg:*,http-get:*image/png:*,http-get:*image/gif:*,http-get:*image/tiff:*</ProtocolInfo>
|
||||
<ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma:*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*:image/jpeg:*,http-get:*:image/png:*,http-get:*:image/gif:*,http-get:*:image/tiff:*</ProtocolInfo>
|
||||
<TimelineOffsetSeconds>40</TimelineOffsetSeconds>
|
||||
<RequiresPlainVideoItems>false</RequiresPlainVideoItems>
|
||||
<RequiresPlainFolders>false</RequiresPlainFolders>
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
<MaxStaticBitrate>140000000</MaxStaticBitrate>
|
||||
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
||||
<MaxStaticMusicBitrate xsi:nil="true" />
|
||||
<ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*image/jpeg:*,http-get:*image/png:*,http-get:*image/gif:*,http-get:*image/tiff:*</ProtocolInfo>
|
||||
<ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma:*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*:image/jpeg:*,http-get:*:image/png:*,http-get:*:image/gif:*,http-get:*:image/tiff:*</ProtocolInfo>
|
||||
<TimelineOffsetSeconds>0</TimelineOffsetSeconds>
|
||||
<RequiresPlainVideoItems>false</RequiresPlainVideoItems>
|
||||
<RequiresPlainFolders>false</RequiresPlainFolders>
|
||||
|
||||
@@ -1016,48 +1016,12 @@ namespace Emby.Server.Implementations
|
||||
AuthenticatedAttribute.AuthService = AuthService;
|
||||
}
|
||||
|
||||
private async void PluginInstalled(object sender, GenericEventArgs<PackageVersionInfo> args)
|
||||
{
|
||||
string dir = Path.Combine(ApplicationPaths.PluginsPath, args.Argument.name);
|
||||
var types = Directory.EnumerateFiles(dir, "*.dll", SearchOption.AllDirectories)
|
||||
.Select(Assembly.LoadFrom)
|
||||
.SelectMany(x => x.ExportedTypes)
|
||||
.Where(x => x.IsClass && !x.IsAbstract && !x.IsInterface && !x.IsGenericType)
|
||||
.ToArray();
|
||||
|
||||
int oldLen = _allConcreteTypes.Length;
|
||||
Array.Resize(ref _allConcreteTypes, oldLen + types.Length);
|
||||
types.CopyTo(_allConcreteTypes, oldLen);
|
||||
|
||||
var plugins = types.Where(x => x.IsAssignableFrom(typeof(IPlugin)))
|
||||
.Select(CreateInstanceSafe)
|
||||
.Where(x => x != null)
|
||||
.Cast<IPlugin>()
|
||||
.Select(LoadPlugin)
|
||||
.Where(x => x != null)
|
||||
.ToArray();
|
||||
|
||||
oldLen = _plugins.Length;
|
||||
Array.Resize(ref _plugins, oldLen + plugins.Length);
|
||||
plugins.CopyTo(_plugins, oldLen);
|
||||
|
||||
var entries = types.Where(x => x.IsAssignableFrom(typeof(IServerEntryPoint)))
|
||||
.Select(CreateInstanceSafe)
|
||||
.Where(x => x != null)
|
||||
.Cast<IServerEntryPoint>()
|
||||
.ToList();
|
||||
|
||||
await Task.WhenAll(StartEntryPoints(entries, true)).ConfigureAwait(false);
|
||||
await Task.WhenAll(StartEntryPoints(entries, false)).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds the parts.
|
||||
/// </summary>
|
||||
public void FindParts()
|
||||
{
|
||||
InstallationManager = ServiceProvider.GetService<IInstallationManager>();
|
||||
InstallationManager.PluginInstalled += PluginInstalled;
|
||||
|
||||
if (!ServerConfigurationManager.Configuration.IsPortAuthorized)
|
||||
{
|
||||
@@ -1165,7 +1129,7 @@ namespace Emby.Server.Implementations
|
||||
{
|
||||
exportedTypes = ass.GetExportedTypes();
|
||||
}
|
||||
catch (TypeLoadException ex)
|
||||
catch (FileNotFoundException ex)
|
||||
{
|
||||
Logger.LogError(ex, "Error getting exported types from {Assembly}", ass.FullName);
|
||||
continue;
|
||||
@@ -1458,7 +1422,7 @@ namespace Emby.Server.Implementations
|
||||
|
||||
public bool SupportsHttps => Certificate != null || ServerConfigurationManager.Configuration.IsBehindProxy;
|
||||
|
||||
public async Task<string> GetLocalApiUrl(CancellationToken cancellationToken)
|
||||
public async Task<string> GetLocalApiUrl(CancellationToken cancellationToken, bool forceHttp = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -1467,7 +1431,7 @@ namespace Emby.Server.Implementations
|
||||
|
||||
foreach (var address in addresses)
|
||||
{
|
||||
return GetLocalApiUrl(address);
|
||||
return GetLocalApiUrl(address, forceHttp);
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -1497,7 +1461,7 @@ namespace Emby.Server.Implementations
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public string GetLocalApiUrl(IPAddress ipAddress)
|
||||
public string GetLocalApiUrl(IPAddress ipAddress, bool forceHttp = false)
|
||||
{
|
||||
if (ipAddress.AddressFamily == AddressFamily.InterNetworkV6)
|
||||
{
|
||||
@@ -1507,28 +1471,21 @@ namespace Emby.Server.Implementations
|
||||
str.CopyTo(span.Slice(1));
|
||||
span[^1] = ']';
|
||||
|
||||
return GetLocalApiUrl(span);
|
||||
return GetLocalApiUrl(span, forceHttp);
|
||||
}
|
||||
|
||||
return GetLocalApiUrl(ipAddress.ToString());
|
||||
return GetLocalApiUrl(ipAddress.ToString(), forceHttp);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public string GetLocalApiUrl(ReadOnlySpan<char> host)
|
||||
public string GetLocalApiUrl(ReadOnlySpan<char> host, bool forceHttp = false)
|
||||
{
|
||||
var url = new StringBuilder(64);
|
||||
if (EnableHttps)
|
||||
{
|
||||
url.Append("https://");
|
||||
}
|
||||
else
|
||||
{
|
||||
url.Append("http://");
|
||||
}
|
||||
|
||||
url.Append(host)
|
||||
bool useHttps = EnableHttps && !forceHttp;
|
||||
url.Append(useHttps ? "https://" : "http://")
|
||||
.Append(host)
|
||||
.Append(':')
|
||||
.Append(HttpPort);
|
||||
.Append(useHttps ? HttpsPort : HttpPort);
|
||||
|
||||
string baseUrl = ServerConfigurationManager.Configuration.BaseUrl;
|
||||
if (baseUrl.Length != 0)
|
||||
@@ -1801,7 +1758,7 @@ namespace Emby.Server.Implementations
|
||||
}
|
||||
|
||||
_userRepository?.Dispose();
|
||||
_displayPreferencesRepository.Dispose();
|
||||
_displayPreferencesRepository?.Dispose();
|
||||
}
|
||||
|
||||
_userRepository = null;
|
||||
|
||||
@@ -67,6 +67,9 @@ namespace Emby.Server.Implementations.Configuration
|
||||
/// <summary>
|
||||
/// Updates the metadata path.
|
||||
/// </summary>
|
||||
/// <exception cref="UnauthorizedAccessException">If the directory does not exist, and the caller does not have the required permission to create it.</exception>
|
||||
/// <exception cref="NotSupportedException">If there is a custom path transcoding path specified, but it is invalid.</exception>
|
||||
/// <exception cref="IOException">If the directory does not exist, and it also could not be created.</exception>
|
||||
private void UpdateMetadataPath()
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(Configuration.MetadataPath))
|
||||
@@ -77,6 +80,7 @@ namespace Emby.Server.Implementations.Configuration
|
||||
{
|
||||
((ServerApplicationPaths)ApplicationPaths).InternalMetadataPath = Configuration.MetadataPath;
|
||||
}
|
||||
Directory.CreateDirectory(ApplicationPaths.InternalMetadataPath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -2914,29 +2914,30 @@ namespace Emby.Server.Implementations.Data
|
||||
private string GetOrderByText(InternalItemsQuery query)
|
||||
{
|
||||
var orderBy = query.OrderBy;
|
||||
if (string.IsNullOrEmpty(query.SearchTerm))
|
||||
bool hasSimilar = query.SimilarTo != null;
|
||||
bool hasSearch = !string.IsNullOrEmpty(query.SearchTerm);
|
||||
|
||||
if (hasSimilar || hasSearch)
|
||||
{
|
||||
int oldLen = orderBy.Count;
|
||||
if (oldLen == 0 && query.SimilarTo != null)
|
||||
List<(string, SortOrder)> prepend = new List<(string, SortOrder)>(4);
|
||||
if (hasSearch)
|
||||
{
|
||||
var arr = new (string, SortOrder)[oldLen + 2];
|
||||
orderBy.CopyTo(arr, 0);
|
||||
arr[oldLen] = ("SimilarityScore", SortOrder.Descending);
|
||||
arr[oldLen + 1] = (ItemSortBy.Random, SortOrder.Ascending);
|
||||
query.OrderBy = arr;
|
||||
prepend.Add(("SearchScore", SortOrder.Descending));
|
||||
prepend.Add((ItemSortBy.SortName, SortOrder.Ascending));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
query.OrderBy = new[]
|
||||
|
||||
if (hasSimilar)
|
||||
{
|
||||
("SearchScore", SortOrder.Descending),
|
||||
(ItemSortBy.SortName, SortOrder.Ascending)
|
||||
};
|
||||
prepend.Add(("SimilarityScore", SortOrder.Descending));
|
||||
prepend.Add((ItemSortBy.Random, SortOrder.Ascending));
|
||||
}
|
||||
|
||||
var arr = new (string, SortOrder)[prepend.Count + orderBy.Count];
|
||||
prepend.CopyTo(arr, 0);
|
||||
orderBy.CopyTo(arr, prepend.Count);
|
||||
orderBy = query.OrderBy = arr;
|
||||
}
|
||||
|
||||
|
||||
if (orderBy.Count == 0)
|
||||
else if (orderBy.Count == 0)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
@@ -6287,8 +6288,8 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
|
||||
statement.TryBind("@Codec" + index, attachment.Codec);
|
||||
statement.TryBind("@CodecTag" + index, attachment.CodecTag);
|
||||
statement.TryBind("@Comment" + index, attachment.Comment);
|
||||
statement.TryBind("@FileName" + index, attachment.FileName);
|
||||
statement.TryBind("@MimeType" + index, attachment.MimeType);
|
||||
statement.TryBind("@Filename" + index, attachment.FileName);
|
||||
statement.TryBind("@MIMEType" + index, attachment.MimeType);
|
||||
}
|
||||
|
||||
statement.Reset();
|
||||
|
||||
@@ -223,7 +223,7 @@ namespace Emby.Server.Implementations.HttpServer
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ErrorHandler(Exception ex, IRequest httpReq, bool logExceptionStackTrace)
|
||||
private async Task ErrorHandler(Exception ex, IRequest httpReq, bool logExceptionStackTrace, string urlToLog)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -231,11 +231,11 @@ namespace Emby.Server.Implementations.HttpServer
|
||||
|
||||
if (logExceptionStackTrace)
|
||||
{
|
||||
_logger.LogError(ex, "Error processing request");
|
||||
_logger.LogError(ex, "Error processing request. URL: {Url}", urlToLog);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogError("Error processing request: {Message}", ex.Message);
|
||||
_logger.LogError("Error processing request: {Message}. URL: {Url}", ex.Message.TrimEnd('.'), urlToLog);
|
||||
}
|
||||
|
||||
var httpRes = httpReq.Response;
|
||||
@@ -255,7 +255,7 @@ namespace Emby.Server.Implementations.HttpServer
|
||||
}
|
||||
catch (Exception errorEx)
|
||||
{
|
||||
_logger.LogError(errorEx, "Error this.ProcessRequest(context)(Exception while writing error to the response)");
|
||||
_logger.LogError(errorEx, "Error this.ProcessRequest(context)(Exception while writing error to the response). URL: {Url}", urlToLog);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -440,7 +440,7 @@ namespace Emby.Server.Implementations.HttpServer
|
||||
var stopWatch = new Stopwatch();
|
||||
stopWatch.Start();
|
||||
var httpRes = httpReq.Response;
|
||||
string urlToLog = null;
|
||||
string urlToLog = GetUrlToLog(urlString);
|
||||
string remoteIp = httpReq.RemoteIp;
|
||||
|
||||
try
|
||||
@@ -486,8 +486,6 @@ namespace Emby.Server.Implementations.HttpServer
|
||||
return;
|
||||
}
|
||||
|
||||
urlToLog = GetUrlToLog(urlString);
|
||||
|
||||
if (string.Equals(localPath, _baseUrlPrefix + "/", StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(localPath, _baseUrlPrefix, StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(localPath, "/", StringComparison.OrdinalIgnoreCase)
|
||||
@@ -519,22 +517,21 @@ namespace Emby.Server.Implementations.HttpServer
|
||||
}
|
||||
else
|
||||
{
|
||||
await ErrorHandler(new FileNotFoundException(), httpReq, false).ConfigureAwait(false);
|
||||
await ErrorHandler(new FileNotFoundException(), httpReq, false, urlToLog).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
catch (Exception ex) when (ex is SocketException || ex is IOException || ex is OperationCanceledException)
|
||||
{
|
||||
await ErrorHandler(ex, httpReq, false).ConfigureAwait(false);
|
||||
await ErrorHandler(ex, httpReq, false, urlToLog).ConfigureAwait(false);
|
||||
}
|
||||
catch (SecurityException ex)
|
||||
{
|
||||
await ErrorHandler(ex, httpReq, false).ConfigureAwait(false);
|
||||
await ErrorHandler(ex, httpReq, false, urlToLog).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
var logException = !string.Equals(ex.GetType().Name, "SocketException", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
await ErrorHandler(ex, httpReq, logException).ConfigureAwait(false);
|
||||
await ErrorHandler(ex, httpReq, logException, urlToLog).ConfigureAwait(false);
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
||||
@@ -29,6 +29,12 @@ namespace Emby.Server.Implementations.HttpServer
|
||||
/// </summary>
|
||||
public class HttpResultFactory : IHttpResultFactory
|
||||
{
|
||||
// Last-Modified and If-Modified-Since must follow strict date format,
|
||||
// see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/If-Modified-Since
|
||||
private const string HttpDateFormat = "ddd, dd MMM yyyy HH:mm:ss \"GMT\"";
|
||||
// We specifically use en-US culture because both day of week and month names require it
|
||||
private static readonly CultureInfo _enUSculture = new CultureInfo("en-US", false);
|
||||
|
||||
/// <summary>
|
||||
/// The logger.
|
||||
/// </summary>
|
||||
@@ -421,7 +427,11 @@ namespace Emby.Server.Implementations.HttpServer
|
||||
|
||||
if (!noCache)
|
||||
{
|
||||
DateTime.TryParse(requestContext.Headers[HeaderNames.IfModifiedSince], out var ifModifiedSinceHeader);
|
||||
if (!DateTime.TryParseExact(requestContext.Headers[HeaderNames.IfModifiedSince], HttpDateFormat, _enUSculture, DateTimeStyles.AssumeUniversal, out var ifModifiedSinceHeader))
|
||||
{
|
||||
_logger.LogDebug("Failed to parse If-Modified-Since header date: {0}", requestContext.Headers[HeaderNames.IfModifiedSince]);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (IsNotModified(ifModifiedSinceHeader, options.CacheDuration, options.DateLastModified))
|
||||
{
|
||||
@@ -630,7 +640,7 @@ namespace Emby.Server.Implementations.HttpServer
|
||||
|
||||
if (lastModifiedDate.HasValue)
|
||||
{
|
||||
responseHeaders[HeaderNames.LastModified] = lastModifiedDate.Value.ToString(CultureInfo.InvariantCulture);
|
||||
responseHeaders[HeaderNames.LastModified] = lastModifiedDate.Value.ToUniversalTime().ToString(HttpDateFormat, _enUSculture);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ namespace Emby.Server.Implementations.Library
|
||||
// for imdbid we also accept pattern matching
|
||||
if (string.Equals(attrib, "imdbid", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var m = Regex.Match(str, "tt\\d{7}", RegexOptions.IgnoreCase);
|
||||
var m = Regex.Match(str, "tt([0-9]{7,8})", RegexOptions.IgnoreCase);
|
||||
return m.Success ? m.Value : null;
|
||||
}
|
||||
|
||||
|
||||
@@ -1063,7 +1063,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
||||
{
|
||||
var stream = new MediaSourceInfo
|
||||
{
|
||||
EncoderPath = _appHost.GetLocalApiUrl("127.0.0.1") + "/LiveTv/LiveRecordings/" + info.Id + "/stream",
|
||||
EncoderPath = _appHost.GetLocalApiUrl("127.0.0.1", true) + "/LiveTv/LiveRecordings/" + info.Id + "/stream",
|
||||
EncoderProtocol = MediaProtocol.Http,
|
||||
Path = info.Path,
|
||||
Protocol = MediaProtocol.File,
|
||||
|
||||
@@ -122,7 +122,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
||||
//OpenedMediaSource.Path = tempFile;
|
||||
//OpenedMediaSource.ReadAtNativeFramerate = true;
|
||||
|
||||
MediaSource.Path = _appHost.GetLocalApiUrl("127.0.0.1") + "/LiveTv/LiveStreamFiles/" + UniqueId + "/stream.ts";
|
||||
MediaSource.Path = _appHost.GetLocalApiUrl("127.0.0.1", true) + "/LiveTv/LiveStreamFiles/" + UniqueId + "/stream.ts";
|
||||
MediaSource.Protocol = MediaProtocol.Http;
|
||||
//OpenedMediaSource.SupportsDirectPlay = false;
|
||||
//OpenedMediaSource.SupportsDirectStream = true;
|
||||
|
||||
@@ -107,7 +107,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
|
||||
//OpenedMediaSource.Path = tempFile;
|
||||
//OpenedMediaSource.ReadAtNativeFramerate = true;
|
||||
|
||||
MediaSource.Path = _appHost.GetLocalApiUrl("127.0.0.1") + "/LiveTv/LiveStreamFiles/" + UniqueId + "/stream.ts";
|
||||
MediaSource.Path = _appHost.GetLocalApiUrl("127.0.0.1", true) + "/LiveTv/LiveStreamFiles/" + UniqueId + "/stream.ts";
|
||||
MediaSource.Protocol = MediaProtocol.Http;
|
||||
|
||||
//OpenedMediaSource.Path = TempFilePath;
|
||||
|
||||
@@ -9,8 +9,6 @@ namespace Emby.Server.Implementations
|
||||
/// </summary>
|
||||
public class ServerApplicationPaths : BaseApplicationPaths, IServerApplicationPaths
|
||||
{
|
||||
private string _internalMetadataPath;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ServerApplicationPaths" /> class.
|
||||
/// </summary>
|
||||
@@ -27,6 +25,7 @@ namespace Emby.Server.Implementations
|
||||
cacheDirectoryPath,
|
||||
webDirectoryPath)
|
||||
{
|
||||
InternalMetadataPath = DefaultInternalMetadataPath;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -98,12 +97,11 @@ namespace Emby.Server.Implementations
|
||||
/// <value>The user configuration directory path.</value>
|
||||
public string UserConfigurationDirectoryPath => Path.Combine(ConfigurationDirectoryPath, "users");
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string DefaultInternalMetadataPath => Path.Combine(ProgramDataPath, "metadata");
|
||||
|
||||
/// <inheritdoc />
|
||||
public string InternalMetadataPath
|
||||
{
|
||||
get => _internalMetadataPath ?? (_internalMetadataPath = Path.Combine(DataPath, "metadata"));
|
||||
set => _internalMetadataPath = value;
|
||||
}
|
||||
public string InternalMetadataPath { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public string VirtualInternalMetadataPath { get; } = "%MetadataPath%";
|
||||
|
||||
@@ -1401,6 +1401,16 @@ namespace Emby.Server.Implementations.Session
|
||||
user = _userManager.GetUserByName(request.Username);
|
||||
}
|
||||
|
||||
if (enforcePassword)
|
||||
{
|
||||
user = await _userManager.AuthenticateUser(
|
||||
request.Username,
|
||||
request.Password,
|
||||
request.PasswordSha1,
|
||||
request.RemoteEndPoint,
|
||||
true).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (user == null)
|
||||
{
|
||||
AuthenticationFailed?.Invoke(this, new GenericEventArgs<AuthenticationRequest>(request));
|
||||
@@ -1413,16 +1423,6 @@ namespace Emby.Server.Implementations.Session
|
||||
throw new SecurityException("User is not allowed access from this device.");
|
||||
}
|
||||
|
||||
if (enforcePassword)
|
||||
{
|
||||
user = await _userManager.AuthenticateUser(
|
||||
request.Username,
|
||||
request.Password,
|
||||
request.PasswordSha1,
|
||||
request.RemoteEndPoint,
|
||||
true).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
var token = GetAuthorizationToken(user, request.DeviceId, request.App, request.AppVersion, request.DeviceName);
|
||||
|
||||
var session = LogSessionActivity(
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="SkiaSharp" Version="1.68.1" />
|
||||
<PackageReference Include="SkiaSharp.NativeAssets.Linux" Version="1.68.1" />
|
||||
<PackageReference Include="Jellyfin.SkiaSharp.NativeAssets.LinuxArm" Version="1.68.0" />
|
||||
<PackageReference Include="Jellyfin.SkiaSharp.NativeAssets.LinuxArm" Version="1.68.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -71,6 +71,11 @@ namespace Jellyfin.Server.Extensions
|
||||
// Clear app parts to avoid other assemblies being picked up
|
||||
.ConfigureApplicationPartManager(a => a.ApplicationParts.Clear())
|
||||
.AddApplicationPart(typeof(StartupController).Assembly)
|
||||
.AddJsonOptions(options =>
|
||||
{
|
||||
// Setting the naming policy to null leaves the property names as-is when serializing objects to JSON.
|
||||
options.JsonSerializerOptions.PropertyNamingPolicy = null;
|
||||
})
|
||||
.AddControllersAsServices();
|
||||
}
|
||||
|
||||
|
||||
@@ -242,9 +242,15 @@ namespace Jellyfin.Server
|
||||
.LocalNetworkAddresses
|
||||
.Select(appHost.NormalizeConfiguredLocalAddress)
|
||||
.Where(i => i != null)
|
||||
.ToList();
|
||||
if (addresses.Any())
|
||||
.ToHashSet();
|
||||
if (addresses.Any() && !addresses.Contains(IPAddress.Any))
|
||||
{
|
||||
if (!addresses.Contains(IPAddress.Loopback))
|
||||
{
|
||||
// we must listen on loopback for LiveTV to function regardless of the settings
|
||||
addresses.Add(IPAddress.Loopback);
|
||||
}
|
||||
|
||||
foreach (var address in addresses)
|
||||
{
|
||||
_logger.LogInformation("Kestrel listening on {IpAddress}", address);
|
||||
|
||||
@@ -134,7 +134,7 @@ namespace MediaBrowser.Api.Playback
|
||||
var data = $"{state.MediaPath}-{state.UserAgent}-{state.Request.DeviceId}-{state.Request.PlaySessionId}";
|
||||
|
||||
var filename = data.GetMD5().ToString("N", CultureInfo.InvariantCulture);
|
||||
var ext = outputFileExtension.ToLowerInvariant();
|
||||
var ext = outputFileExtension?.ToLowerInvariant();
|
||||
var folder = ServerConfigurationManager.GetTranscodePath();
|
||||
|
||||
if (EnableOutputInSubFolder)
|
||||
|
||||
@@ -927,61 +927,69 @@ namespace MediaBrowser.Api.Playback.Hls
|
||||
}
|
||||
else
|
||||
{
|
||||
var gopArg = string.Empty;
|
||||
var keyFrameArg = string.Format(
|
||||
CultureInfo.InvariantCulture,
|
||||
" -force_key_frames:0 \"expr:gte(t,{0}+n_forced*{1})\"",
|
||||
GetStartNumber(state) * state.SegmentLength,
|
||||
state.SegmentLength);
|
||||
if (state.TargetFramerate.HasValue)
|
||||
|
||||
var framerate = state.VideoStream?.RealFrameRate;
|
||||
|
||||
if (framerate != null && framerate.HasValue)
|
||||
{
|
||||
// This is to make sure keyframe interval is limited to our segment,
|
||||
// as forcing keyframes is not enough.
|
||||
// Example: we encoded half of desired length, then codec detected
|
||||
// scene cut and inserted a keyframe; next forced keyframe would
|
||||
// be created outside of segment, which breaks seeking.
|
||||
keyFrameArg += string.Format(
|
||||
// be created outside of segment, which breaks seeking
|
||||
// -sc_threshold 0 is used to prevent the hardware encoder from post processing to break the set keyframe
|
||||
gopArg = string.Format(
|
||||
CultureInfo.InvariantCulture,
|
||||
" -g {0} -keyint_min {0}",
|
||||
(int)(state.SegmentLength * state.TargetFramerate)
|
||||
" -g {0} -keyint_min {0} -sc_threshold 0",
|
||||
Math.Ceiling(state.SegmentLength * framerate.Value)
|
||||
);
|
||||
}
|
||||
|
||||
var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode;
|
||||
|
||||
args += " " + EncodingHelper.GetVideoQualityParam(state, codec, encodingOptions, GetDefaultEncoderPreset());
|
||||
|
||||
// Unable to force key frames to h264_qsv transcode
|
||||
if (string.Equals(codec, "h264_qsv", StringComparison.OrdinalIgnoreCase))
|
||||
// Unable to force key frames using these hw encoders, set key frames by GOP
|
||||
if (string.Equals(codec, "h264_qsv", StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(codec, "h264_nvenc", StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(codec, "h264_amf", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
Logger.LogInformation("Bug Workaround: Disabling force_key_frames for h264_qsv");
|
||||
args += " " + gopArg;
|
||||
}
|
||||
else
|
||||
{
|
||||
args += " " + keyFrameArg;
|
||||
args += " " + keyFrameArg + gopArg;
|
||||
}
|
||||
|
||||
//args += " -mixed-refs 0 -refs 3 -x264opts b_pyramid=0:weightb=0:weightp=0";
|
||||
|
||||
// Add resolution params, if specified
|
||||
if (!hasGraphicalSubs)
|
||||
{
|
||||
args += EncodingHelper.GetOutputSizeParam(state, encodingOptions, codec, true);
|
||||
}
|
||||
var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode;
|
||||
|
||||
// This is for internal graphical subs
|
||||
// This is for graphical subs
|
||||
if (hasGraphicalSubs)
|
||||
{
|
||||
args += EncodingHelper.GetGraphicalSubtitleParam(state, encodingOptions, codec);
|
||||
}
|
||||
// Add resolution params, if specified
|
||||
else
|
||||
{
|
||||
args += EncodingHelper.GetOutputSizeParam(state, encodingOptions, codec);
|
||||
}
|
||||
|
||||
// -start_at_zero is necessary to use with -ss when seeking,
|
||||
// otherwise the target position cannot be determined.
|
||||
if (!(state.SubtitleStream != null && state.SubtitleStream.IsExternal && !state.SubtitleStream.IsTextSubtitleStream))
|
||||
{
|
||||
args += " -start_at_zero";
|
||||
}
|
||||
|
||||
//args += " -flags -global_header";
|
||||
}
|
||||
|
||||
if (args.IndexOf("-copyts", StringComparison.OrdinalIgnoreCase) == -1)
|
||||
{
|
||||
args += " -copyts";
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(state.OutputVideoSync))
|
||||
{
|
||||
args += " -vsync " + state.OutputVideoSync;
|
||||
@@ -1024,7 +1032,7 @@ namespace MediaBrowser.Api.Playback.Hls
|
||||
}
|
||||
|
||||
return string.Format(
|
||||
"{0} {1} -map_metadata -1 -map_chapters -1 -threads {2} {3} {4} {5} -f hls -max_delay 5000000 -avoid_negative_ts disabled -start_at_zero -hls_time {6} -individual_header_trailer 0 -hls_segment_type {7} -start_number {8} -hls_segment_filename \"{9}\" -hls_playlist_type vod -hls_list_size 0 -y \"{10}\"",
|
||||
"{0} {1} -map_metadata -1 -map_chapters -1 -threads {2} {3} {4} {5} -copyts -avoid_negative_ts disabled -f hls -max_delay 5000000 -hls_time {6} -individual_header_trailer 0 -hls_segment_type {7} -start_number {8} -hls_segment_filename \"{9}\" -hls_playlist_type vod -hls_list_size 0 -y \"{10}\"",
|
||||
inputModifier,
|
||||
EncodingHelper.GetInputArgument(state, encodingOptions),
|
||||
threads,
|
||||
|
||||
@@ -521,10 +521,7 @@ namespace MediaBrowser.Api.Playback
|
||||
streamInfo.StartPositionTicks = startTimeTicks;
|
||||
mediaSource.TranscodingUrl = streamInfo.ToUrl("-", auth.Token).TrimStart('-');
|
||||
mediaSource.TranscodingUrl += "&allowVideoStreamCopy=false";
|
||||
if (!allowAudioStreamCopy)
|
||||
{
|
||||
mediaSource.TranscodingUrl += "&allowAudioStreamCopy=false";
|
||||
}
|
||||
mediaSource.TranscodingUrl += "&allowAudioStreamCopy=false";
|
||||
mediaSource.TranscodingContainer = streamInfo.Container;
|
||||
mediaSource.TranscodingSubProtocol = streamInfo.SubProtocol;
|
||||
|
||||
@@ -573,8 +570,7 @@ namespace MediaBrowser.Api.Playback
|
||||
{
|
||||
attachment.DeliveryUrl = string.Format(
|
||||
CultureInfo.InvariantCulture,
|
||||
"{0}/Videos/{1}/{2}/Attachments/{3}",
|
||||
ServerConfigurationManager.Configuration.BaseUrl,
|
||||
"/Videos/{0}/{1}/Attachments/{2}",
|
||||
item.Id,
|
||||
mediaSource.Id,
|
||||
attachment.Index);
|
||||
|
||||
@@ -213,7 +213,10 @@ namespace MediaBrowser.Api.UserLibrary
|
||||
request.IncludeItemTypes = "Playlist";
|
||||
}
|
||||
|
||||
bool isInEnabledFolder = user.Policy.EnabledFolders.Any(i => new Guid(i) == item.Id);
|
||||
bool isInEnabledFolder = user.Policy.EnabledFolders.Any(i => new Guid(i) == item.Id)
|
||||
// Assume all folders inside an EnabledChannel are enabled
|
||||
|| user.Policy.EnabledChannels.Any(i => new Guid(i) == item.Id);
|
||||
|
||||
var collectionFolders = _libraryManager.GetCollectionFolders(item);
|
||||
foreach (var collectionFolder in collectionFolders)
|
||||
{
|
||||
@@ -225,7 +228,7 @@ namespace MediaBrowser.Api.UserLibrary
|
||||
}
|
||||
}
|
||||
|
||||
if (!(item is UserRootFolder) && !user.Policy.EnableAllFolders && !isInEnabledFolder)
|
||||
if (!(item is UserRootFolder) && !user.Policy.EnableAllFolders && !isInEnabledFolder && !user.Policy.EnableAllChannels)
|
||||
{
|
||||
Logger.LogWarning("{UserName} is not permitted to access Library {ItemName}.", user.Name, item.Name);
|
||||
return new QueryResult<BaseItem>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using MediaBrowser.Model.Configuration;
|
||||
|
||||
@@ -17,18 +18,25 @@ namespace MediaBrowser.Common.Configuration
|
||||
=> configurationManager.GetConfiguration<EncodingOptions>("encoding");
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the transcoding temp path from the encoding configuration.
|
||||
/// Retrieves the transcoding temp path from the encoding configuration, falling back to a default if no path
|
||||
/// is specified in configuration. If the directory does not exist, it will be created.
|
||||
/// </summary>
|
||||
/// <param name="configurationManager">The Configuration manager.</param>
|
||||
/// <param name="configurationManager">The configuration manager.</param>
|
||||
/// <returns>The transcoding temp path.</returns>
|
||||
/// <exception cref="UnauthorizedAccessException">If the directory does not exist, and the caller does not have the required permission to create it.</exception>
|
||||
/// <exception cref="NotSupportedException">If there is a custom path transcoding path specified, but it is invalid.</exception>
|
||||
/// <exception cref="IOException">If the directory does not exist, and it also could not be created.</exception>
|
||||
public static string GetTranscodePath(this IConfigurationManager configurationManager)
|
||||
{
|
||||
// Get the configured path and fall back to a default
|
||||
var transcodingTempPath = configurationManager.GetEncodingOptions().TranscodingTempPath;
|
||||
if (string.IsNullOrEmpty(transcodingTempPath))
|
||||
{
|
||||
return Path.Combine(configurationManager.CommonApplicationPaths.ProgramDataPath, "transcodes");
|
||||
transcodingTempPath = Path.Combine(configurationManager.CommonApplicationPaths.ProgramDataPath, "transcodes");
|
||||
}
|
||||
|
||||
// Make sure the directory exists
|
||||
Directory.CreateDirectory(transcodingTempPath);
|
||||
return transcodingTempPath;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,6 +178,7 @@ namespace MediaBrowser.Controller.Entities
|
||||
|
||||
[JsonIgnore]
|
||||
public int? TotalBitrate { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public ExtraType? ExtraType { get; set; }
|
||||
|
||||
@@ -549,7 +550,6 @@ namespace MediaBrowser.Controller.Entities
|
||||
[JsonIgnore]
|
||||
public DateTime DateModified { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public DateTime DateLastSaved { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
@@ -2883,7 +2883,7 @@ namespace MediaBrowser.Controller.Entities
|
||||
|
||||
public IEnumerable<BaseItem> GetExtras(IReadOnlyCollection<ExtraType> extraTypes)
|
||||
{
|
||||
return ExtraIds.Select(LibraryManager.GetItemById).Where(i => i != null && extraTypes.Contains(i.ExtraType.Value));
|
||||
return ExtraIds.Select(LibraryManager.GetItemById).Where(i => i?.ExtraType != null && extraTypes.Contains(i.ExtraType.Value));
|
||||
}
|
||||
|
||||
public IEnumerable<BaseItem> GetTrailers()
|
||||
|
||||
@@ -65,22 +65,26 @@ namespace MediaBrowser.Controller
|
||||
/// <summary>
|
||||
/// Gets the local API URL.
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">Token to cancel the request if needed.</param>
|
||||
/// <param name="forceHttp">Whether to force usage of plain HTTP protocol.</param>
|
||||
/// <value>The local API URL.</value>
|
||||
Task<string> GetLocalApiUrl(CancellationToken cancellationToken);
|
||||
Task<string> GetLocalApiUrl(CancellationToken cancellationToken, bool forceHttp = false);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the local API URL.
|
||||
/// </summary>
|
||||
/// <param name="hostname">The hostname.</param>
|
||||
/// <param name="forceHttp">Whether to force usage of plain HTTP protocol.</param>
|
||||
/// <returns>The local API URL.</returns>
|
||||
string GetLocalApiUrl(ReadOnlySpan<char> hostname);
|
||||
string GetLocalApiUrl(ReadOnlySpan<char> hostname, bool forceHttp = false);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the local API URL.
|
||||
/// </summary>
|
||||
/// <param name="address">The IP address.</param>
|
||||
/// <param name="forceHttp">Whether to force usage of plain HTTP protocol.</param>
|
||||
/// <returns>The local API URL.</returns>
|
||||
string GetLocalApiUrl(IPAddress address);
|
||||
string GetLocalApiUrl(IPAddress address, bool forceHttp = false);
|
||||
|
||||
void LaunchUrl(string url);
|
||||
|
||||
|
||||
@@ -71,7 +71,12 @@ namespace MediaBrowser.Controller
|
||||
string UserConfigurationDirectoryPath { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the internal metadata path.
|
||||
/// Gets the default internal metadata path.
|
||||
/// </summary>
|
||||
string DefaultInternalMetadataPath { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the internal metadata path, either a custom path or the default.
|
||||
/// </summary>
|
||||
/// <value>The internal metadata path.</value>
|
||||
string InternalMetadataPath { get; }
|
||||
|
||||
@@ -78,8 +78,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
|
||||
if (!string.IsNullOrEmpty(hwType)
|
||||
&& encodingOptions.EnableHardwareEncoding
|
||||
&& codecMap.ContainsKey(hwType)
|
||||
&& CheckVaapi(state, hwType, encodingOptions))
|
||||
&& codecMap.ContainsKey(hwType))
|
||||
{
|
||||
var preferredEncoder = codecMap[hwType];
|
||||
|
||||
@@ -93,23 +92,6 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
return defaultEncoder;
|
||||
}
|
||||
|
||||
private bool CheckVaapi(EncodingJobInfo state, string hwType, EncodingOptions encodingOptions)
|
||||
{
|
||||
if (!string.Equals(hwType, "vaapi", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
// No vaapi requested, return OK.
|
||||
return true;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(encodingOptions.VaapiDevice))
|
||||
{
|
||||
// No device specified, return OK.
|
||||
return true;
|
||||
}
|
||||
|
||||
return IsVaapiSupported(state);
|
||||
}
|
||||
|
||||
private bool IsVaapiSupported(EncodingJobInfo state)
|
||||
{
|
||||
var videoStream = state.VideoStream;
|
||||
@@ -424,7 +406,13 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
|
||||
if (string.Equals(codec, "aac", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return "aac -strict experimental";
|
||||
// Use libfdk_aac for better audio quality if using custom build of FFmpeg which has fdk_aac support
|
||||
if (_mediaEncoder.SupportsEncoder("libfdk_aac"))
|
||||
{
|
||||
return "libfdk_aac";
|
||||
}
|
||||
|
||||
return "aac";
|
||||
}
|
||||
|
||||
if (string.Equals(codec, "mp3", StringComparison.OrdinalIgnoreCase))
|
||||
@@ -460,16 +448,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
if (state.IsVideoRequest
|
||||
&& string.Equals(encodingOptions.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode;
|
||||
var hwOutputFormat = "vaapi";
|
||||
|
||||
if (hasGraphicalSubs)
|
||||
{
|
||||
hwOutputFormat = "yuv420p";
|
||||
}
|
||||
|
||||
arg.Append("-hwaccel vaapi -hwaccel_output_format ")
|
||||
.Append(hwOutputFormat)
|
||||
arg.Append("-hwaccel vaapi -hwaccel_output_format vaapi")
|
||||
.Append(" -vaapi_device ")
|
||||
.Append(encodingOptions.VaapiDevice)
|
||||
.Append(' ');
|
||||
@@ -480,20 +459,26 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
{
|
||||
var videoDecoder = GetHardwareAcceleratedVideoDecoder(state, encodingOptions);
|
||||
var outputVideoCodec = GetVideoEncoder(state, encodingOptions);
|
||||
|
||||
var hasTextSubs = state.SubtitleStream != null && state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode;
|
||||
|
||||
if (encodingOptions.EnableHardwareEncoding && outputVideoCodec.Contains("qsv", StringComparison.OrdinalIgnoreCase))
|
||||
if (!hasTextSubs)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(videoDecoder) && videoDecoder.Contains("qsv", StringComparison.OrdinalIgnoreCase))
|
||||
// While using QSV encoder
|
||||
if ((outputVideoCodec ?? string.Empty).IndexOf("qsv", StringComparison.OrdinalIgnoreCase) != -1)
|
||||
{
|
||||
arg.Append("-hwaccel qsv ");
|
||||
}
|
||||
else
|
||||
{
|
||||
arg.Append("-init_hw_device qsv=hw -filter_hw_device hw ");
|
||||
// While using QSV decoder
|
||||
if ((videoDecoder ?? string.Empty).IndexOf("qsv", StringComparison.OrdinalIgnoreCase) != -1)
|
||||
{
|
||||
arg.Append("-hwaccel qsv ");
|
||||
}
|
||||
// While using SW decoder
|
||||
else
|
||||
{
|
||||
arg.Append("-init_hw_device qsv=hw -filter_hw_device hw ");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
arg.Append(videoDecoder + " ");
|
||||
}
|
||||
|
||||
arg.Append("-i ")
|
||||
@@ -503,17 +488,6 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
&& state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode
|
||||
&& state.SubtitleStream.IsExternal && !state.SubtitleStream.IsTextSubtitleStream)
|
||||
{
|
||||
if (state.VideoStream != null && state.VideoStream.Width.HasValue)
|
||||
{
|
||||
// This is hacky but not sure how to get the exact subtitle resolution
|
||||
int height = Convert.ToInt32(state.VideoStream.Width.Value / 16.0 * 9.0);
|
||||
|
||||
arg.Append(" -canvas_size ")
|
||||
.Append(state.VideoStream.Width.Value.ToString(CultureInfo.InvariantCulture))
|
||||
.Append(':')
|
||||
.Append(height.ToString(CultureInfo.InvariantCulture));
|
||||
}
|
||||
|
||||
var subtitlePath = state.SubtitleStream.Path;
|
||||
|
||||
if (string.Equals(Path.GetExtension(subtitlePath), ".sub", StringComparison.OrdinalIgnoreCase))
|
||||
@@ -1546,9 +1520,12 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the internal graphical subtitle param.
|
||||
/// Gets the graphical subtitle param.
|
||||
/// </summary>
|
||||
public string GetGraphicalSubtitleParam(EncodingJobInfo state, EncodingOptions options, string outputVideoCodec)
|
||||
public string GetGraphicalSubtitleParam(
|
||||
EncodingJobInfo state,
|
||||
EncodingOptions options,
|
||||
string outputVideoCodec)
|
||||
{
|
||||
var outputSizeParam = string.Empty;
|
||||
|
||||
@@ -1562,53 +1539,77 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
{
|
||||
outputSizeParam = GetOutputSizeParam(state, options, outputVideoCodec).TrimEnd('"');
|
||||
|
||||
if (string.Equals(outputVideoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase))
|
||||
var index = outputSizeParam.IndexOf("hwdownload", StringComparison.OrdinalIgnoreCase);
|
||||
if (index != -1)
|
||||
{
|
||||
var index = outputSizeParam.IndexOf("format", StringComparison.OrdinalIgnoreCase);
|
||||
if (index != -1)
|
||||
{
|
||||
outputSizeParam = "," + outputSizeParam.Substring(index);
|
||||
}
|
||||
outputSizeParam = "," + outputSizeParam.Substring(index);
|
||||
}
|
||||
else
|
||||
{
|
||||
var index = outputSizeParam.IndexOf("scale", StringComparison.OrdinalIgnoreCase);
|
||||
index = outputSizeParam.IndexOf("format", StringComparison.OrdinalIgnoreCase);
|
||||
if (index != -1)
|
||||
{
|
||||
outputSizeParam = "," + outputSizeParam.Substring(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (string.Equals(outputVideoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase)
|
||||
&& outputSizeParam.Length == 0)
|
||||
{
|
||||
outputSizeParam = ",format=nv12|vaapi,hwupload";
|
||||
|
||||
// Add parameters to use VAAPI with burn-in subttiles (GH issue #642)
|
||||
if (state.SubtitleStream != null
|
||||
&& state.SubtitleStream.IsTextSubtitleStream
|
||||
&& state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode) {
|
||||
outputSizeParam += ",hwmap=mode=read+write+direct";
|
||||
else
|
||||
{
|
||||
index = outputSizeParam.IndexOf("yadif", StringComparison.OrdinalIgnoreCase);
|
||||
if (index != -1)
|
||||
{
|
||||
outputSizeParam = "," + outputSizeParam.Substring(index);
|
||||
}
|
||||
else
|
||||
{
|
||||
index = outputSizeParam.IndexOf("scale", StringComparison.OrdinalIgnoreCase);
|
||||
if (index != -1)
|
||||
{
|
||||
outputSizeParam = "," + outputSizeParam.Substring(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var videoSizeParam = string.Empty;
|
||||
var videoDecoder = GetHardwareAcceleratedVideoDecoder(state, options);
|
||||
|
||||
// Setup subtitle scaling
|
||||
if (state.VideoStream != null && state.VideoStream.Width.HasValue && state.VideoStream.Height.HasValue)
|
||||
{
|
||||
// force_original_aspect_ratio=decrease
|
||||
// Enable decreasing output video width or height if necessary to keep the original aspect ratio
|
||||
videoSizeParam = string.Format(
|
||||
CultureInfo.InvariantCulture,
|
||||
"scale={0}:{1}",
|
||||
"scale={0}:{1}:force_original_aspect_ratio=decrease",
|
||||
state.VideoStream.Width.Value,
|
||||
state.VideoStream.Height.Value);
|
||||
|
||||
//For QSV, feed it into hardware encoder now
|
||||
// For QSV, feed it into hardware encoder now
|
||||
if (string.Equals(outputVideoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
videoSizeParam += ",hwupload=extra_hw_frames=64";
|
||||
}
|
||||
|
||||
// For VAAPI and CUVID decoder
|
||||
// these encoders cannot automatically adjust the size of graphical subtitles to fit the output video,
|
||||
// thus needs to be manually adjusted.
|
||||
if ((IsVaapiSupported(state) && string.Equals(options.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase))
|
||||
|| (videoDecoder ?? string.Empty).IndexOf("cuvid", StringComparison.OrdinalIgnoreCase) != -1)
|
||||
{
|
||||
var videoStream = state.VideoStream;
|
||||
var inputWidth = videoStream?.Width;
|
||||
var inputHeight = videoStream?.Height;
|
||||
var (width, height) = GetFixedOutputSize(inputWidth, inputHeight, request.Width, request.Height, request.MaxWidth, request.MaxHeight);
|
||||
|
||||
if (width.HasValue && height.HasValue)
|
||||
{
|
||||
videoSizeParam = string.Format(
|
||||
CultureInfo.InvariantCulture,
|
||||
"scale={0}:{1}:force_original_aspect_ratio=decrease",
|
||||
width.Value,
|
||||
height.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var mapPrefix = state.SubtitleStream.IsExternal ?
|
||||
@@ -1619,12 +1620,35 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
? 0
|
||||
: state.SubtitleStream.Index;
|
||||
|
||||
var videoDecoder = GetHardwareAcceleratedVideoDecoder(state, options);
|
||||
|
||||
// Setup default filtergraph utilizing FFMpeg overlay() and FFMpeg scale() (see the return of this function for index reference)
|
||||
var retStr = " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}][sub]overlay{3}\"";
|
||||
|
||||
if (string.Equals(outputVideoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase))
|
||||
// When the input may or may not be hardware VAAPI decodable
|
||||
if (string.Equals(outputVideoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
/*
|
||||
[base]: HW scaling video to OutputSize
|
||||
[sub]: SW scaling subtitle to FixedOutputSize
|
||||
[base][sub]: SW overlay
|
||||
*/
|
||||
outputSizeParam = outputSizeParam.TrimStart(',');
|
||||
retStr = " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}]{3},hwdownload[base];[base][sub]overlay,format=nv12,hwupload\"";
|
||||
}
|
||||
|
||||
// If we're hardware VAAPI decoding and software encoding, download frames from the decoder first
|
||||
else if (IsVaapiSupported(state) && string.Equals(options.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase)
|
||||
&& string.Equals(outputVideoCodec, "libx264", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
/*
|
||||
[base]: SW scaling video to OutputSize
|
||||
[sub]: SW scaling subtitle to FixedOutputSize
|
||||
[base][sub]: SW overlay
|
||||
*/
|
||||
outputSizeParam = outputSizeParam.TrimStart(',');
|
||||
retStr = " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}]{3}[base];[base][sub]overlay\"";
|
||||
}
|
||||
|
||||
else if (string.Equals(outputVideoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
/*
|
||||
QSV in FFMpeg can now setup hardware overlay for transcodes.
|
||||
@@ -1688,7 +1712,8 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
return (Convert.ToInt32(outputWidth), Convert.ToInt32(outputHeight));
|
||||
}
|
||||
|
||||
public List<string> GetScalingFilters(int? videoWidth,
|
||||
public List<string> GetScalingFilters(EncodingJobInfo state,
|
||||
int? videoWidth,
|
||||
int? videoHeight,
|
||||
Video3DFormat? threedFormat,
|
||||
string videoDecoder,
|
||||
@@ -1707,7 +1732,9 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
requestedMaxWidth,
|
||||
requestedMaxHeight);
|
||||
|
||||
if ((string.Equals(videoEncoder, "h264_vaapi", StringComparison.OrdinalIgnoreCase) || string.Equals(videoEncoder, "h264_qsv", StringComparison.OrdinalIgnoreCase))
|
||||
var hasTextSubs = state.SubtitleStream != null && state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode;
|
||||
|
||||
if (string.Equals(videoEncoder, "h264_vaapi", StringComparison.OrdinalIgnoreCase) || (string.Equals(videoEncoder, "h264_qsv", StringComparison.OrdinalIgnoreCase) && !hasTextSubs)
|
||||
&& width.HasValue
|
||||
&& height.HasValue)
|
||||
{
|
||||
@@ -1737,7 +1764,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
filters.Add(string.Format(CultureInfo.InvariantCulture, "scale_{0}=format=nv12", vaapi_or_qsv));
|
||||
}
|
||||
}
|
||||
else if ((videoDecoder ?? string.Empty).IndexOf("_cuvid", StringComparison.OrdinalIgnoreCase) != -1
|
||||
else if ((videoDecoder ?? string.Empty).IndexOf("cuvid", StringComparison.OrdinalIgnoreCase) != -1
|
||||
&& width.HasValue
|
||||
&& height.HasValue)
|
||||
{
|
||||
@@ -1941,8 +1968,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
public string GetOutputSizeParam(
|
||||
EncodingJobInfo state,
|
||||
EncodingOptions options,
|
||||
string outputVideoCodec,
|
||||
bool allowTimeStampCopy = true)
|
||||
string outputVideoCodec)
|
||||
{
|
||||
// http://sonnati.wordpress.com/2012/10/19/ffmpeg-the-swiss-army-knife-of-internet-streaming-part-vi/
|
||||
|
||||
@@ -1951,42 +1977,61 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
var videoStream = state.VideoStream;
|
||||
var filters = new List<string>();
|
||||
|
||||
// If we're hardware VAAPI decoding and software encoding, download frames from the decoder first
|
||||
var hwType = options.HardwareAccelerationType ?? string.Empty;
|
||||
if (string.Equals(hwType, "vaapi", StringComparison.OrdinalIgnoreCase) && !options.EnableHardwareEncoding )
|
||||
{
|
||||
filters.Add("hwdownload");
|
||||
var videoDecoder = GetHardwareAcceleratedVideoDecoder(state, options);
|
||||
var inputWidth = videoStream?.Width;
|
||||
var inputHeight = videoStream?.Height;
|
||||
var threeDFormat = state.MediaSource.Video3DFormat;
|
||||
|
||||
// If transcoding from 10 bit, transform colour spaces too
|
||||
if (!string.IsNullOrEmpty(videoStream.PixelFormat)
|
||||
&& videoStream.PixelFormat.IndexOf("p10", StringComparison.OrdinalIgnoreCase) != -1
|
||||
&& string.Equals(outputVideoCodec, "libx264", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
filters.Add("format=p010le");
|
||||
filters.Add("format=nv12");
|
||||
}
|
||||
else
|
||||
{
|
||||
filters.Add("format=nv12");
|
||||
}
|
||||
}
|
||||
var hasTextSubs = state.SubtitleStream != null && state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode;
|
||||
|
||||
// When the input may or may not be hardware VAAPI decodable
|
||||
if (string.Equals(outputVideoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
filters.Add("format=nv12|vaapi");
|
||||
filters.Add("hwupload");
|
||||
}
|
||||
|
||||
var videoDecoder = GetHardwareAcceleratedVideoDecoder(state, options);
|
||||
|
||||
// If we are software decoding, and hardware encoding
|
||||
if (string.Equals(outputVideoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase)
|
||||
&& (string.IsNullOrEmpty(videoDecoder) || !videoDecoder.Contains("qsv", StringComparison.OrdinalIgnoreCase)))
|
||||
// When the input may or may not be hardware QSV decodable
|
||||
else if (string.Equals(outputVideoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
filters.Add("format=nv12|qsv");
|
||||
filters.Add("hwupload=extra_hw_frames=64");
|
||||
if (!hasTextSubs)
|
||||
{
|
||||
filters.Add("format=nv12|qsv");
|
||||
filters.Add("hwupload=extra_hw_frames=64");
|
||||
}
|
||||
}
|
||||
|
||||
// If we're hardware VAAPI decoding and software encoding, download frames from the decoder first
|
||||
else if (IsVaapiSupported(state) && string.Equals(options.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase)
|
||||
&& string.Equals(outputVideoCodec, "libx264", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var codec = videoStream.Codec.ToLowerInvariant();
|
||||
var isColorDepth10 = !string.IsNullOrEmpty(videoStream.Profile) && (videoStream.Profile.Contains("Main 10", StringComparison.OrdinalIgnoreCase)
|
||||
|| videoStream.Profile.Contains("High 10", StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
// Assert 10-bit hardware VAAPI decodable
|
||||
if (isColorDepth10 && (string.Equals(codec, "hevc", StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(codec, "h265", StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(codec, "vp9", StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
/*
|
||||
Download data from GPU to CPU as p010le format.
|
||||
Colorspace conversion is unnecessary here as libx264 will handle it.
|
||||
If this step is missing, it will fail on AMD but not on intel.
|
||||
*/
|
||||
filters.Add("hwdownload");
|
||||
filters.Add("format=p010le");
|
||||
}
|
||||
|
||||
// Assert 8-bit hardware VAAPI decodable
|
||||
else if (!isColorDepth10)
|
||||
{
|
||||
filters.Add("hwdownload");
|
||||
filters.Add("format=nv12");
|
||||
}
|
||||
}
|
||||
|
||||
// Add hardware deinterlace filter before scaling filter
|
||||
if (state.DeInterlace("h264", true))
|
||||
{
|
||||
if (string.Equals(outputVideoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase))
|
||||
@@ -1995,12 +2040,18 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
}
|
||||
else if (string.Equals(outputVideoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
filters.Add(string.Format(CultureInfo.InvariantCulture, "deinterlace_qsv"));
|
||||
if (!hasTextSubs)
|
||||
{
|
||||
filters.Add(string.Format(CultureInfo.InvariantCulture, "deinterlace_qsv"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((state.DeInterlace("h264", true) || state.DeInterlace("h265", true) || state.DeInterlace("hevc", true))
|
||||
&& !string.Equals(outputVideoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase))
|
||||
// Add software deinterlace filter before scaling filter
|
||||
if (((state.DeInterlace("h264", true) || state.DeInterlace("h265", true) || state.DeInterlace("hevc", true))
|
||||
&& !string.Equals(outputVideoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase)
|
||||
&& !string.Equals(outputVideoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase))
|
||||
|| (hasTextSubs && state.DeInterlace("h264", true) && string.Equals(outputVideoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
var inputFramerate = videoStream?.RealFrameRate;
|
||||
|
||||
@@ -2015,11 +2066,21 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
}
|
||||
}
|
||||
|
||||
var inputWidth = videoStream?.Width;
|
||||
var inputHeight = videoStream?.Height;
|
||||
var threeDFormat = state.MediaSource.Video3DFormat;
|
||||
// Add scaling filter: scale_*=format=nv12 or scale_*=w=*:h=*:format=nv12 or scale=expr
|
||||
filters.AddRange(GetScalingFilters(state, inputWidth, inputHeight, threeDFormat, videoDecoder, outputVideoCodec, request.Width, request.Height, request.MaxWidth, request.MaxHeight));
|
||||
|
||||
filters.AddRange(GetScalingFilters(inputWidth, inputHeight, threeDFormat, videoDecoder, outputVideoCodec, request.Width, request.Height, request.MaxWidth, request.MaxHeight));
|
||||
// Add parameters to use VAAPI with burn-in text subttiles (GH issue #642)
|
||||
if (string.Equals(outputVideoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (state.SubtitleStream != null
|
||||
&& state.SubtitleStream.IsTextSubtitleStream
|
||||
&& state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode)
|
||||
{
|
||||
// Test passed on Intel and AMD gfx
|
||||
filters.Add("hwmap=mode=read+write");
|
||||
filters.Add("format=nv12");
|
||||
}
|
||||
}
|
||||
|
||||
var output = string.Empty;
|
||||
|
||||
@@ -2037,11 +2098,6 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
{
|
||||
filters.Add("hwmap");
|
||||
}
|
||||
|
||||
if (allowTimeStampCopy)
|
||||
{
|
||||
output += " -copyts";
|
||||
}
|
||||
}
|
||||
|
||||
if (filters.Count > 0)
|
||||
@@ -2218,7 +2274,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
{
|
||||
inputModifier += " " + videoDecoder;
|
||||
|
||||
if ((videoDecoder ?? string.Empty).IndexOf("_cuvid", StringComparison.OrdinalIgnoreCase) != -1)
|
||||
if ((videoDecoder ?? string.Empty).IndexOf("cuvid", StringComparison.OrdinalIgnoreCase) != -1)
|
||||
{
|
||||
var videoStream = state.VideoStream;
|
||||
var inputWidth = videoStream?.Width;
|
||||
@@ -2227,7 +2283,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
|
||||
var (width, height) = GetFixedOutputSize(inputWidth, inputHeight, request.Width, request.Height, request.MaxWidth, request.MaxHeight);
|
||||
|
||||
if ((videoDecoder ?? string.Empty).IndexOf("_cuvid", StringComparison.OrdinalIgnoreCase) != -1
|
||||
if ((videoDecoder ?? string.Empty).IndexOf("cuvid", StringComparison.OrdinalIgnoreCase) != -1
|
||||
&& width.HasValue
|
||||
&& height.HasValue)
|
||||
{
|
||||
@@ -2525,6 +2581,12 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
case "h264":
|
||||
if (_mediaEncoder.SupportsDecoder("h264_cuvid") && encodingOptions.HardwareDecodingCodecs.Contains("h264", StringComparer.OrdinalIgnoreCase))
|
||||
{
|
||||
// cuvid decoder does not support 10-bit input
|
||||
if ((videoStream.BitDepth ?? 8) > 8)
|
||||
{
|
||||
encodingOptions.HardwareDecodingCodecs = Array.Empty<string>();
|
||||
return null;
|
||||
}
|
||||
return "-c:v h264_cuvid ";
|
||||
}
|
||||
break;
|
||||
@@ -2772,14 +2834,27 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode;
|
||||
|
||||
var hasCopyTs = false;
|
||||
|
||||
// Add resolution params, if specified
|
||||
if (!hasGraphicalSubs)
|
||||
{
|
||||
var outputSizeParam = GetOutputSizeParam(state, encodingOptions, videoCodec);
|
||||
|
||||
args += outputSizeParam;
|
||||
|
||||
hasCopyTs = outputSizeParam.IndexOf("copyts", StringComparison.OrdinalIgnoreCase) != -1;
|
||||
}
|
||||
|
||||
// This is for graphical subs
|
||||
if (hasGraphicalSubs)
|
||||
{
|
||||
var graphicalSubtitleParam = GetGraphicalSubtitleParam(state, encodingOptions, videoCodec);
|
||||
|
||||
args += graphicalSubtitleParam;
|
||||
|
||||
hasCopyTs = graphicalSubtitleParam.IndexOf("copyts", StringComparison.OrdinalIgnoreCase) != -1;
|
||||
}
|
||||
|
||||
if (state.RunTimeTicks.HasValue && state.BaseRequest.CopyTimestamps)
|
||||
{
|
||||
if (!hasCopyTs)
|
||||
@@ -2787,13 +2862,12 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
args += " -copyts";
|
||||
}
|
||||
|
||||
args += " -avoid_negative_ts disabled -start_at_zero";
|
||||
}
|
||||
args += " -avoid_negative_ts disabled";
|
||||
|
||||
// This is for internal graphical subs
|
||||
if (hasGraphicalSubs)
|
||||
{
|
||||
args += GetGraphicalSubtitleParam(state, encodingOptions, videoCodec);
|
||||
if (!(state.SubtitleStream != null && state.SubtitleStream.IsExternal && !state.SubtitleStream.IsTextSubtitleStream))
|
||||
{
|
||||
args += " -start_at_zero";
|
||||
}
|
||||
}
|
||||
|
||||
var qualityParam = GetVideoQualityParam(state, videoCodec, encodingOptions, defaultPreset);
|
||||
@@ -2899,6 +2973,5 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
string.Empty,
|
||||
string.Empty).Trim();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||
"libvpx",
|
||||
"libvpx-vp9",
|
||||
"aac",
|
||||
"libfdk_aac",
|
||||
"libmp3lame",
|
||||
"libopus",
|
||||
"libvorbis",
|
||||
|
||||
@@ -188,6 +188,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
|
||||
using (var stream = await GetStream(path, protocol, cancellationToken).ConfigureAwait(false))
|
||||
{
|
||||
var result = CharsetDetector.DetectFromStream(stream).Detected;
|
||||
stream.Position = 0;
|
||||
|
||||
if (result != null)
|
||||
{
|
||||
@@ -730,6 +731,14 @@ namespace MediaBrowser.MediaEncoding.Subtitles
|
||||
{
|
||||
var charset = CharsetDetector.DetectFromStream(stream).Detected?.EncodingName;
|
||||
|
||||
// UTF16 is automatically converted to UTF8 by FFmpeg, do not specify a character encoding
|
||||
if ((path.EndsWith(".ass") || path.EndsWith(".ssa"))
|
||||
&& (string.Equals(charset, "utf-16le", StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(charset, "utf-16be", StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
charset = "";
|
||||
}
|
||||
|
||||
_logger.LogDebug("charset {0} detected for {Path}", charset ?? "null", path);
|
||||
|
||||
return charset;
|
||||
@@ -745,6 +754,8 @@ namespace MediaBrowser.MediaEncoding.Subtitles
|
||||
{
|
||||
Url = path,
|
||||
CancellationToken = cancellationToken,
|
||||
|
||||
// Needed for seeking
|
||||
BufferContent = true
|
||||
};
|
||||
|
||||
|
||||
@@ -26,12 +26,12 @@ namespace MediaBrowser.Model.Dlna
|
||||
|
||||
public bool SupportsVideoCodec(string codec)
|
||||
{
|
||||
return ContainerProfile.ContainsContainer(VideoCodec, codec);
|
||||
return Type == DlnaProfileType.Video && ContainerProfile.ContainsContainer(VideoCodec, codec);
|
||||
}
|
||||
|
||||
public bool SupportsAudioCodec(string codec)
|
||||
{
|
||||
return ContainerProfile.ContainsContainer(AudioCodec, codec);
|
||||
return (Type == DlnaProfileType.Audio || Type == DlnaProfileType.Video) && ContainerProfile.ContainsContainer(AudioCodec, codec);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,6 +65,7 @@ namespace MediaBrowser.Model.Net
|
||||
{ ".m3u8", "application/x-mpegURL" },
|
||||
{ ".mobi", "application/x-mobipocket-ebook" },
|
||||
{ ".xml", "application/xml" },
|
||||
{ ".wasm", "application/wasm" },
|
||||
|
||||
// Type image
|
||||
{ ".jpg", "image/jpeg" },
|
||||
@@ -106,6 +107,7 @@ namespace MediaBrowser.Model.Net
|
||||
{ ".3g2", "video/3gpp2" },
|
||||
{ ".mpd", "video/vnd.mpeg.dash.mpd" },
|
||||
{ ".ts", "video/mp2t" },
|
||||
{ ".mpegts", "video/mp2t" },
|
||||
|
||||
// Type audio
|
||||
{ ".mp3", "audio/mpeg" },
|
||||
@@ -123,6 +125,8 @@ namespace MediaBrowser.Model.Net
|
||||
{ ".xsp", "audio/xsp" },
|
||||
{ ".dsp", "audio/dsp" },
|
||||
{ ".flac", "audio/flac" },
|
||||
{ ".ape", "audio/x-ape" },
|
||||
{ ".wv", "audio/x-wavpack" },
|
||||
};
|
||||
|
||||
private static readonly Dictionary<string, string> _extensionLookup = CreateExtensionLookup();
|
||||
|
||||
@@ -32,7 +32,11 @@ namespace MediaBrowser.Providers.Plugins.MusicBrainz
|
||||
{
|
||||
if (value < Plugin.DefaultRateLimit && _server == Plugin.DefaultServer)
|
||||
{
|
||||
RateLimit = Plugin.DefaultRateLimit;
|
||||
_rateLimit = Plugin.DefaultRateLimit;
|
||||
}
|
||||
else
|
||||
{
|
||||
_rateLimit = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -344,7 +344,11 @@ namespace MediaBrowser.Providers.TV.TheTVDB
|
||||
series.ProductionYear = date.Year;
|
||||
}
|
||||
|
||||
series.RunTimeTicks = TimeSpan.FromMinutes(Convert.ToDouble(tvdbSeries.Runtime)).Ticks;
|
||||
if (!string.IsNullOrEmpty(tvdbSeries.Runtime) && double.TryParse(tvdbSeries.Runtime, out double runtime))
|
||||
{
|
||||
series.RunTimeTicks = TimeSpan.FromMinutes(runtime).Ticks;
|
||||
}
|
||||
|
||||
foreach (var genre in tvdbSeries.Genre)
|
||||
{
|
||||
series.AddGenre(genre);
|
||||
|
||||
@@ -203,8 +203,8 @@ namespace MediaBrowser.XbmcMetadata.Parsers
|
||||
|
||||
protected void ParseProviderLinks(T item, string xml)
|
||||
{
|
||||
//Look for a match for the Regex pattern "tt" followed by 7 digits
|
||||
var m = Regex.Match(xml, @"tt([0-9]{7})", RegexOptions.IgnoreCase);
|
||||
// Look for a match for the Regex pattern "tt" followed by 7 or 8 digits
|
||||
var m = Regex.Match(xml, "tt([0-9]{7,8})", RegexOptions.IgnoreCase);
|
||||
if (m.Success)
|
||||
{
|
||||
item.SetProviderId(MetadataProviders.Imdb, m.Value);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.Reflection;
|
||||
|
||||
[assembly: AssemblyVersion("10.5.0")]
|
||||
[assembly: AssemblyFileVersion("10.5.0")]
|
||||
[assembly: AssemblyVersion("10.5.5")]
|
||||
[assembly: AssemblyFileVersion("10.5.5")]
|
||||
|
||||
9
build
9
build
@@ -81,7 +81,14 @@ if [[ $1 == '-b' || $1 == '--web-branch' ]]; then
|
||||
web_branch="$2"
|
||||
shift 2
|
||||
else
|
||||
web_branch="$( git branch 2>/dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/\1/' )"
|
||||
web_branch="$( git describe --tags --exact-match || true )"
|
||||
if [[ -z "$web_branch" ]]; then
|
||||
web_branch="$( git branch 2>/dev/null | grep -v 'HEAD detached' | sed -e '/^[^*]/d' -e 's/* \(.*\)/\1/' )"
|
||||
if [[ -z "${web_branch}" ]]; then
|
||||
echo "Cannot determine web branch, pass it explicitly via --web-branch option"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# Parse platform option
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
# We just wrap `build` so this is really it
|
||||
name: "jellyfin"
|
||||
version: "10.5.0"
|
||||
version: "10.5.5"
|
||||
packages:
|
||||
- debian-package-x64
|
||||
- debian-package-armhf
|
||||
|
||||
@@ -17,7 +17,7 @@ RUN yum install -y @buildsys-build rpmdevtools yum-plugins-core libcurl-devel fo
|
||||
|
||||
# Install recent NodeJS and Yarn
|
||||
RUN curl -fSsLo /etc/yum.repos.d/yarn.repo https://dl.yarnpkg.com/rpm/yarn.repo \
|
||||
&& rpm -i https://rpm.nodesource.com/pub_8.x/el/7/x86_64/nodesource-release-el7-1.noarch.rpm \
|
||||
&& rpm -i https://rpm.nodesource.com/pub_10.x/el/7/x86_64/nodesource-release-el7-1.noarch.rpm \
|
||||
&& yum install -y yarn
|
||||
|
||||
# Install DotNET SDK
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
args="${@}"
|
||||
declare -a docker_envvars
|
||||
for arg in ${args}; do
|
||||
docker_envvars+=("-e ${arg}")
|
||||
docker_envvars+="-e ${arg} "
|
||||
done
|
||||
|
||||
WORKDIR="$( pwd )"
|
||||
@@ -28,7 +28,7 @@ mkdir -p "${package_temporary_dir}"
|
||||
# Set up the build environment Docker image
|
||||
${docker_sudo} docker build ../.. -t "${image_name}" -f ./Dockerfile
|
||||
# Build the RPMs and copy out to ${package_temporary_dir}
|
||||
${docker_sudo} docker run --rm -v "${package_temporary_dir}:/dist" "${image_name}" ${docker_envvars}
|
||||
${docker_sudo} docker run --rm ${docker_envvars} -v "${package_temporary_dir}:/dist" "${image_name}"
|
||||
# Move the RPMs to the output directory
|
||||
mkdir -p "${output_dir}"
|
||||
mv "${package_temporary_dir}"/rpm/* "${output_dir}"
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
args="${@}"
|
||||
declare -a docker_envvars
|
||||
for arg in ${args}; do
|
||||
docker_envvars+=("-e ${arg}")
|
||||
docker_envvars+="-e ${arg} "
|
||||
done
|
||||
|
||||
ARCH="$( arch )"
|
||||
@@ -39,7 +39,7 @@ mkdir -p "${package_temporary_dir}"
|
||||
# Set up the build environment Docker image
|
||||
${docker_sudo} docker build ../.. -t "${image_name}" -f ./${DOCKERFILE}
|
||||
# Build the DEBs and copy out to ${package_temporary_dir}
|
||||
${docker_sudo} docker run --rm -v "${package_temporary_dir}:/dist" "${image_name}" ${docker_envvars}
|
||||
${docker_sudo} docker run --rm ${docker_envvars} -v "${package_temporary_dir}:/dist" "${image_name}"
|
||||
# Move the DEBs to the output directory
|
||||
mkdir -p "${output_dir}"
|
||||
mv "${package_temporary_dir}"/deb/* "${output_dir}"
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
args="${@}"
|
||||
declare -a docker_envvars
|
||||
for arg in ${args}; do
|
||||
docker_envvars+=("-e ${arg}")
|
||||
docker_envvars+="-e ${arg} "
|
||||
done
|
||||
|
||||
ARCH="$( arch )"
|
||||
@@ -39,7 +39,7 @@ mkdir -p "${package_temporary_dir}"
|
||||
# Set up the build environment Docker image
|
||||
${docker_sudo} docker build ../.. -t "${image_name}" -f ./${DOCKERFILE}
|
||||
# Build the DEBs and copy out to ${package_temporary_dir}
|
||||
${docker_sudo} docker run --rm -v "${package_temporary_dir}:/dist" "${image_name}" ${docker_envvars}
|
||||
${docker_sudo} docker run --rm ${docker_envvars} -v "${package_temporary_dir}:/dist" "${image_name}"
|
||||
# Move the DEBs to the output directory
|
||||
mkdir -p "${output_dir}"
|
||||
mv "${package_temporary_dir}"/deb/* "${output_dir}"
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
args="${@}"
|
||||
declare -a docker_envvars
|
||||
for arg in ${args}; do
|
||||
docker_envvars+=("-e ${arg}")
|
||||
docker_envvars+="-e ${arg} "
|
||||
done
|
||||
|
||||
WORKDIR="$( pwd )"
|
||||
@@ -28,7 +28,7 @@ mkdir -p "${package_temporary_dir}"
|
||||
# Set up the build environment Docker image
|
||||
${docker_sudo} docker build ../.. -t "${image_name}" -f ./Dockerfile
|
||||
# Build the DEBs and copy out to ${package_temporary_dir}
|
||||
${docker_sudo} docker run --rm -v "${package_temporary_dir}:/dist" "${image_name}" ${docker_envvars}
|
||||
${docker_sudo} docker run --rm ${docker_envvars} -v "${package_temporary_dir}:/dist" "${image_name}"
|
||||
# Move the DEBs to the output directory
|
||||
mkdir -p "${output_dir}"
|
||||
mv "${package_temporary_dir}"/deb/* "${output_dir}"
|
||||
|
||||
@@ -1,3 +1,33 @@
|
||||
jellyfin (10.5.5-1) unstable; urgency=medium
|
||||
|
||||
* New upstream version 10.5.5; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.5.5
|
||||
|
||||
-- Jellyfin Packaging Team <packaging@jellyfin.org> Sun, 26 Apr 2020 15:25:03 -0400
|
||||
|
||||
jellyfin (10.5.4-1) unstable; urgency=medium
|
||||
|
||||
* New upstream version 10.5.4; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.5.4
|
||||
|
||||
-- Jellyfin Packaging Team <packaging@jellyfin.org> Sun, 12 Apr 2020 15:53:47 -0400
|
||||
|
||||
jellyfin (10.5.3-1) unstable; urgency=medium
|
||||
|
||||
* New upstream version 10.5.3; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.5.3
|
||||
|
||||
-- Jellyfin Packaging Team <packaging@jellyfin.org> Sun, 05 Apr 2020 12:36:25 -0400
|
||||
|
||||
jellyfin (10.5.2-1) unstable; urgency=medium
|
||||
|
||||
* New upstream version 10.5.2; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.5.2
|
||||
|
||||
-- Jellyfin Packaging Team <packaging@jellyfin.org> Sun, 22 Mar 2020 12:06:40 -0400
|
||||
|
||||
jellyfin (10.5.1-1) unstable; urgency=medium
|
||||
|
||||
* New upstream version 10.5.1; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.5.1
|
||||
|
||||
-- Jellyfin Packaging Team <packaging@jellyfin.org> Sun, 15 Mar 2020 23:03:59 -0400
|
||||
|
||||
jellyfin (10.5.0-1) unstable; urgency=medium
|
||||
|
||||
* New upstream version 10.5.0; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.5.0
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
args="${@}"
|
||||
declare -a docker_envvars
|
||||
for arg in ${args}; do
|
||||
docker_envvars+=("-e ${arg}")
|
||||
docker_envvars+="-e ${arg} "
|
||||
done
|
||||
|
||||
WORKDIR="$( pwd )"
|
||||
@@ -28,7 +28,7 @@ mkdir -p "${package_temporary_dir}"
|
||||
# Set up the build environment Docker image
|
||||
${docker_sudo} docker build ../.. -t "${image_name}" -f ./Dockerfile
|
||||
# Build the RPMs and copy out to ${package_temporary_dir}
|
||||
${docker_sudo} docker run --rm -v "${package_temporary_dir}:/dist" "${image_name}" ${docker_envvars}
|
||||
${docker_sudo} docker run --rm ${docker_envvars} -v "${package_temporary_dir}:/dist" "${image_name}"
|
||||
# Move the RPMs to the output directory
|
||||
mkdir -p "${output_dir}"
|
||||
mv "${package_temporary_dir}"/rpm/* "${output_dir}"
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
%endif
|
||||
|
||||
Name: jellyfin
|
||||
Version: 10.5.0
|
||||
Version: 10.5.5
|
||||
Release: 1%{?dist}
|
||||
Summary: The Free Software Media Browser
|
||||
License: GPLv2
|
||||
@@ -26,13 +26,13 @@ Source16: jellyfin-firewalld.xml
|
||||
%{?systemd_requires}
|
||||
BuildRequires: systemd
|
||||
Requires(pre): shadow-utils
|
||||
BuildRequires: libcurl-devel, fontconfig-devel, freetype-devel, openssl-devel, glibc-devel, libicu-devel
|
||||
BuildRequires: libcurl-devel, fontconfig-devel, freetype-devel, openssl-devel, glibc-devel, libicu-devel, git
|
||||
%if 0%{?fedora}
|
||||
BuildRequires: nodejs-yarn
|
||||
BuildRequires: nodejs-yarn, git
|
||||
%else
|
||||
# Requirements not packaged in main repos
|
||||
# From https://rpm.nodesource.com/pub_8.x/el/7/x86_64/
|
||||
BuildRequires: nodejs >= 8 yarn
|
||||
# From https://rpm.nodesource.com/pub_10.x/el/7/x86_64/
|
||||
BuildRequires: nodejs >= 10 yarn
|
||||
%endif
|
||||
Requires: libcurl, fontconfig, freetype, openssl, glibc libicu
|
||||
# Requirements not packaged in main repos
|
||||
@@ -159,6 +159,16 @@ fi
|
||||
%systemd_postun_with_restart jellyfin.service
|
||||
|
||||
%changelog
|
||||
* Sun Apr 26 2020 Jellyfin Packaging Team <packaging@jellyfin.org>
|
||||
- New upstream version 10.5.5; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.5.5
|
||||
* Sun Apr 12 2020 Jellyfin Packaging Team <packaging@jellyfin.org>
|
||||
- New upstream version 10.5.4; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.5.4
|
||||
* Sun Apr 05 2020 Jellyfin Packaging Team <packaging@jellyfin.org>
|
||||
- New upstream version 10.5.3; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.5.3
|
||||
* Sun Mar 22 2020 Jellyfin Packaging Team <packaging@jellyfin.org>
|
||||
- New upstream version 10.5.2; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.5.2
|
||||
* Sun Mar 15 2020 Jellyfin Packaging Team <packaging@jellyfin.org>
|
||||
- New upstream version 10.5.1; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.5.1
|
||||
* Fri Oct 11 2019 Jellyfin Packaging Team <packaging@jellyfin.org>
|
||||
- New upstream version 10.5.0; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.5.0
|
||||
* Sat Aug 31 2019 Jellyfin Packaging Team <packaging@jellyfin.org>
|
||||
|
||||
@@ -13,9 +13,7 @@ web_build_dir="$( mktemp -d )"
|
||||
web_target="${SOURCE_DIR}/MediaBrowser.WebDashboard/jellyfin-web"
|
||||
git clone https://github.com/jellyfin/jellyfin-web.git ${web_build_dir}/
|
||||
pushd ${web_build_dir}
|
||||
if [[ -n ${web_branch} ]]; then
|
||||
checkout -b origin/${web_branch}
|
||||
fi
|
||||
git checkout "${web_branch}"
|
||||
yarn install
|
||||
mkdir -p ${web_target}
|
||||
mv dist/* ${web_target}/
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
args="${@}"
|
||||
declare -a docker_envvars
|
||||
for arg in ${args}; do
|
||||
docker_envvars+=("-e ${arg}")
|
||||
docker_envvars+="-e ${arg} "
|
||||
done
|
||||
|
||||
WORKDIR="$( pwd )"
|
||||
@@ -28,7 +28,7 @@ mkdir -p "${package_temporary_dir}"
|
||||
# Set up the build environment Docker image
|
||||
${docker_sudo} docker build ../.. -t "${image_name}" -f ./Dockerfile
|
||||
# Build the DEBs and copy out to ${package_temporary_dir}
|
||||
${docker_sudo} docker run --rm -v "${package_temporary_dir}:/dist" "${image_name}" ${docker_envvars}
|
||||
${docker_sudo} docker run --rm ${docker_envvars} -v "${package_temporary_dir}:/dist" "${image_name}"
|
||||
# Move the DEBs to the output directory
|
||||
mkdir -p "${output_dir}"
|
||||
mv "${package_temporary_dir}"/* "${output_dir}"
|
||||
|
||||
@@ -13,9 +13,7 @@ web_build_dir="$( mktemp -d )"
|
||||
web_target="${SOURCE_DIR}/MediaBrowser.WebDashboard/jellyfin-web"
|
||||
git clone https://github.com/jellyfin/jellyfin-web.git ${web_build_dir}/
|
||||
pushd ${web_build_dir}
|
||||
if [[ -n ${web_branch} ]]; then
|
||||
checkout -b origin/${web_branch}
|
||||
fi
|
||||
git checkout "${web_branch}"
|
||||
yarn install
|
||||
mkdir -p ${web_target}
|
||||
mv dist/* ${web_target}/
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
args="${@}"
|
||||
declare -a docker_envvars
|
||||
for arg in ${args}; do
|
||||
docker_envvars+=("-e ${arg}")
|
||||
docker_envvars+="-e ${arg} "
|
||||
done
|
||||
|
||||
WORKDIR="$( pwd )"
|
||||
@@ -28,7 +28,7 @@ mkdir -p "${package_temporary_dir}"
|
||||
# Set up the build environment Docker image
|
||||
${docker_sudo} docker build ../.. -t "${image_name}" -f ./Dockerfile
|
||||
# Build the DEBs and copy out to ${package_temporary_dir}
|
||||
${docker_sudo} docker run --rm -v "${package_temporary_dir}:/dist" "${image_name}" ${docker_envvars}
|
||||
${docker_sudo} docker run --rm ${docker_envvars} -v "${package_temporary_dir}:/dist" "${image_name}"
|
||||
# Move the DEBs to the output directory
|
||||
mkdir -p "${output_dir}"
|
||||
mv "${package_temporary_dir}"/* "${output_dir}"
|
||||
|
||||
@@ -13,9 +13,7 @@ web_build_dir="$( mktemp -d )"
|
||||
web_target="${SOURCE_DIR}/MediaBrowser.WebDashboard/jellyfin-web"
|
||||
git clone https://github.com/jellyfin/jellyfin-web.git ${web_build_dir}/
|
||||
pushd ${web_build_dir}
|
||||
if [[ -n ${web_branch} ]]; then
|
||||
checkout -b origin/${web_branch}
|
||||
fi
|
||||
git checkout "${web_branch}"
|
||||
yarn install
|
||||
mkdir -p ${web_target}
|
||||
mv dist/* ${web_target}/
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
args="${@}"
|
||||
declare -a docker_envvars
|
||||
for arg in ${args}; do
|
||||
docker_envvars+=("-e ${arg}")
|
||||
docker_envvars+="-e ${arg} "
|
||||
done
|
||||
|
||||
WORKDIR="$( pwd )"
|
||||
@@ -28,7 +28,7 @@ mkdir -p "${package_temporary_dir}"
|
||||
# Set up the build environment Docker image
|
||||
${docker_sudo} docker build ../.. -t "${image_name}" -f ./Dockerfile
|
||||
# Build the DEBs and copy out to ${package_temporary_dir}
|
||||
${docker_sudo} docker run --rm -v "${package_temporary_dir}:/dist" "${image_name}" ${docker_envvars}
|
||||
${docker_sudo} docker run --rm ${docker_envvars} -v "${package_temporary_dir}:/dist" "${image_name}"
|
||||
# Move the DEBs to the output directory
|
||||
mkdir -p "${output_dir}"
|
||||
mv "${package_temporary_dir}"/* "${output_dir}"
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
args="${@}"
|
||||
declare -a docker_envvars
|
||||
for arg in ${args}; do
|
||||
docker_envvars+=("-e ${arg}")
|
||||
docker_envvars+="-e ${arg} "
|
||||
done
|
||||
|
||||
ARCH="$( arch )"
|
||||
@@ -39,7 +39,7 @@ mkdir -p "${package_temporary_dir}"
|
||||
# Set up the build environment Docker image
|
||||
${docker_sudo} docker build ../.. -t "${image_name}" -f ./${DOCKERFILE}
|
||||
# Build the DEBs and copy out to ${package_temporary_dir}
|
||||
${docker_sudo} docker run --rm -v "${package_temporary_dir}:/dist" "${image_name}" ${docker_envvars}
|
||||
${docker_sudo} docker run --rm ${docker_envvars} -v "${package_temporary_dir}:/dist" "${image_name}"
|
||||
# Move the DEBs to the output directory
|
||||
mkdir -p "${output_dir}"
|
||||
mv "${package_temporary_dir}"/deb/* "${output_dir}"
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
args="${@}"
|
||||
declare -a docker_envvars
|
||||
for arg in ${args}; do
|
||||
docker_envvars+=("-e ${arg}")
|
||||
docker_envvars+="-e ${arg} "
|
||||
done
|
||||
|
||||
ARCH="$( arch )"
|
||||
@@ -39,7 +39,7 @@ mkdir -p "${package_temporary_dir}"
|
||||
# Set up the build environment Docker image
|
||||
${docker_sudo} docker build ../.. -t "${image_name}" -f ./${DOCKERFILE}
|
||||
# Build the DEBs and copy out to ${package_temporary_dir}
|
||||
${docker_sudo} docker run --rm -v "${package_temporary_dir}:/dist" "${image_name}" ${docker_envvars}
|
||||
${docker_sudo} docker run --rm ${docker_envvars} -v "${package_temporary_dir}:/dist" "${image_name}"
|
||||
# Move the DEBs to the output directory
|
||||
mkdir -p "${output_dir}"
|
||||
mv "${package_temporary_dir}"/deb/* "${output_dir}"
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
args="${@}"
|
||||
declare -a docker_envvars
|
||||
for arg in ${args}; do
|
||||
docker_envvars+=("-e ${arg}")
|
||||
docker_envvars+="-e ${arg} "
|
||||
done
|
||||
|
||||
WORKDIR="$( pwd )"
|
||||
@@ -28,7 +28,7 @@ mkdir -p "${package_temporary_dir}"
|
||||
# Set up the build environment Docker image
|
||||
${docker_sudo} docker build ../.. -t "${image_name}" -f ./Dockerfile
|
||||
# Build the DEBs and copy out to ${package_temporary_dir}
|
||||
${docker_sudo} docker run --rm -v "${package_temporary_dir}:/dist" "${image_name}" ${docker_envvars}
|
||||
${docker_sudo} docker run --rm ${docker_envvars} -v "${package_temporary_dir}:/dist" "${image_name}"
|
||||
# Move the DEBs to the output directory
|
||||
mkdir -p "${output_dir}"
|
||||
mv "${package_temporary_dir}"/deb/* "${output_dir}"
|
||||
|
||||
@@ -19,9 +19,7 @@ web_build_dir="$( mktemp -d )"
|
||||
web_target="${SOURCE_DIR}/MediaBrowser.WebDashboard/jellyfin-web"
|
||||
git clone https://github.com/jellyfin/jellyfin-web.git ${web_build_dir}/
|
||||
pushd ${web_build_dir}
|
||||
if [[ -n ${web_branch} ]]; then
|
||||
checkout -b origin/${web_branch}
|
||||
fi
|
||||
git checkout "${web_branch}"
|
||||
yarn install
|
||||
mkdir -p ${web_target}
|
||||
mv dist/* ${web_target}/
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
args="${@}"
|
||||
declare -a docker_envvars
|
||||
for arg in ${args}; do
|
||||
docker_envvars+=("-e ${arg}")
|
||||
docker_envvars+="-e ${arg} "
|
||||
done
|
||||
|
||||
WORKDIR="$( pwd )"
|
||||
@@ -28,7 +28,7 @@ mkdir -p "${package_temporary_dir}"
|
||||
# Set up the build environment Docker image
|
||||
${docker_sudo} docker build ../.. -t "${image_name}" -f ./Dockerfile
|
||||
# Build the DEBs and copy out to ${package_temporary_dir}
|
||||
${docker_sudo} docker run --rm -v "${package_temporary_dir}:/dist" "${image_name}" ${docker_envvars}
|
||||
${docker_sudo} docker run --rm ${docker_envvars} -v "${package_temporary_dir}:/dist" "${image_name}"
|
||||
# Move the DEBs to the output directory
|
||||
mkdir -p "${output_dir}"
|
||||
mv "${package_temporary_dir}"/* "${output_dir}"
|
||||
|
||||
@@ -19,9 +19,7 @@ web_build_dir="$( mktemp -d )"
|
||||
web_target="${SOURCE_DIR}/MediaBrowser.WebDashboard/jellyfin-web"
|
||||
git clone https://github.com/jellyfin/jellyfin-web.git ${web_build_dir}/
|
||||
pushd ${web_build_dir}
|
||||
if [[ -n ${web_branch} ]]; then
|
||||
checkout -b origin/${web_branch}
|
||||
fi
|
||||
git checkout "${web_branch}"
|
||||
yarn install
|
||||
mkdir -p ${web_target}
|
||||
mv dist/* ${web_target}/
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
args="${@}"
|
||||
declare -a docker_envvars
|
||||
for arg in ${args}; do
|
||||
docker_envvars+=("-e ${arg}")
|
||||
docker_envvars+="-e ${arg} "
|
||||
done
|
||||
|
||||
WORKDIR="$( pwd )"
|
||||
@@ -28,7 +28,7 @@ mkdir -p "${package_temporary_dir}"
|
||||
# Set up the build environment Docker image
|
||||
${docker_sudo} docker build ../.. -t "${image_name}" -f ./Dockerfile
|
||||
# Build the DEBs and copy out to ${package_temporary_dir}
|
||||
${docker_sudo} docker run --rm -v "${package_temporary_dir}:/dist" "${image_name}" ${docker_envvars}
|
||||
${docker_sudo} docker run --rm ${docker_envvars} -v "${package_temporary_dir}:/dist" "${image_name}"
|
||||
# Move the DEBs to the output directory
|
||||
mkdir -p "${output_dir}"
|
||||
mv "${package_temporary_dir}"/* "${output_dir}"
|
||||
|
||||
6
nuget.config
Normal file
6
nuget.config
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<packageSources>
|
||||
<add key="NuGet official package source" value="https://api.nuget.org/v3/index.json" />
|
||||
</packageSources>
|
||||
</configuration>
|
||||
Reference in New Issue
Block a user