aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Dockerfile6
-rw-r--r--Dockerfile.arm6
-rw-r--r--Dockerfile.arm646
-rw-r--r--DvdLib/Ifo/Dvd.cs16
-rw-r--r--Emby.Dlna/ContentDirectory/ControlHandler.cs4
-rw-r--r--Emby.Dlna/Didl/DidlBuilder.cs3
-rw-r--r--Emby.Dlna/DlnaManager.cs21
-rw-r--r--Emby.Dlna/Main/DlnaEntryPoint.cs2
-rw-r--r--Emby.Dlna/PlayTo/TransportCommands.cs19
-rw-r--r--Emby.Naming/TV/EpisodePathParser.cs32
-rw-r--r--Emby.Server.Implementations/Activity/ActivityManager.cs7
-rw-r--r--Emby.Server.Implementations/ApplicationHost.cs6
-rw-r--r--Emby.Server.Implementations/Channels/ChannelManager.cs11
-rw-r--r--Emby.Server.Implementations/Data/SqliteItemRepository.cs7
-rw-r--r--Emby.Server.Implementations/Data/SqliteUserDataRepository.cs4
-rw-r--r--Emby.Server.Implementations/Security/EncryptionManager.cs57
-rw-r--r--Emby.XmlTv/Emby.XmlTv/Classes/XmlTvReader.cs27
-rw-r--r--Jellyfin.Drawing.Skia/PercentPlayedDrawer.cs2
-rw-r--r--Jellyfin.Drawing.Skia/PlayedIndicatorDrawer.cs2
-rw-r--r--Jellyfin.Drawing.Skia/UnplayedCountIndicator.cs2
-rw-r--r--MediaBrowser.Controller/Security/IEncryptionManager.cs19
-rw-r--r--MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs103
-rw-r--r--MediaBrowser.MediaEncoding/Subtitles/OpenSubtitleDownloader.cs19
-rwxr-xr-xbuild60
-rw-r--r--build.yaml15
-rwxr-xr-xdeployment/win-x64/package.sh4
-rwxr-xr-xdeployment/win-x86/package.sh4
27 files changed, 279 insertions, 185 deletions
diff --git a/Dockerfile b/Dockerfile
index e2d424df7..91a4f5a2d 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -20,6 +20,12 @@ RUN apt-get update \
&& chmod 777 /cache /config /media
COPY --from=ffmpeg / /
COPY --from=builder /jellyfin /jellyfin
+
+ARG JELLYFIN_WEB_VERSION=10.2.2
+RUN curl -L https://github.com/jellyfin/jellyfin-web/archive/v${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \
+ && rm -rf /jellyfin/jellyfin-web \
+ && mv jellyfin-web-${JELLYFIN_WEB_VERSION} /jellyfin/jellyfin-web
+
EXPOSE 8096
VOLUME /cache /config /media
ENTRYPOINT dotnet /jellyfin/jellyfin.dll \
diff --git a/Dockerfile.arm b/Dockerfile.arm
index d220763a2..42f0354a3 100644
--- a/Dockerfile.arm
+++ b/Dockerfile.arm
@@ -28,6 +28,12 @@ RUN apt-get update \
&& mkdir -p /cache /config /media \
&& chmod 777 /cache /config /media
COPY --from=builder /jellyfin /jellyfin
+
+ARG JELLYFIN_WEB_VERSION=10.2.2
+RUN curl -L https://github.com/jellyfin/jellyfin-web/archive/v${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \
+ && rm -rf /jellyfin/jellyfin-web \
+ && mv jellyfin-web-${JELLYFIN_WEB_VERSION} /jellyfin/jellyfin-web
+
EXPOSE 8096
VOLUME /cache /config /media
ENTRYPOINT dotnet /jellyfin/jellyfin.dll \
diff --git a/Dockerfile.arm64 b/Dockerfile.arm64
index 243b841e9..d3103d389 100644
--- a/Dockerfile.arm64
+++ b/Dockerfile.arm64
@@ -29,6 +29,12 @@ RUN apt-get update \
&& mkdir -p /cache /config /media \
&& chmod 777 /cache /config /media
COPY --from=builder /jellyfin /jellyfin
+
+ARG JELLYFIN_WEB_VERSION=10.2.2
+RUN curl -L https://github.com/jellyfin/jellyfin-web/archive/v${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \
+ && rm -rf /jellyfin/jellyfin-web \
+ && mv jellyfin-web-${JELLYFIN_WEB_VERSION} /jellyfin/jellyfin-web
+
EXPOSE 8096
VOLUME /cache /config /media
ENTRYPOINT dotnet /jellyfin/jellyfin.dll \
diff --git a/DvdLib/Ifo/Dvd.cs b/DvdLib/Ifo/Dvd.cs
index f784be83e..90125fa3e 100644
--- a/DvdLib/Ifo/Dvd.cs
+++ b/DvdLib/Ifo/Dvd.cs
@@ -26,17 +26,17 @@ namespace DvdLib.Ifo
if (vmgPath == null)
{
- var allIfos = allFiles.Where(i => string.Equals(i.Extension, ".ifo", StringComparison.OrdinalIgnoreCase));
-
- foreach (var ifo in allIfos)
+ foreach (var ifo in allFiles)
{
- var num = ifo.Name.Split('_').ElementAtOrDefault(1);
- var numbersRead = new List<ushort>();
+ if (!string.Equals(ifo.Extension, ".ifo", StringComparison.OrdinalIgnoreCase))
+ {
+ continue;
+ }
- if (!string.IsNullOrEmpty(num) && ushort.TryParse(num, out var ifoNumber) && !numbersRead.Contains(ifoNumber))
+ var nums = ifo.Name.Split(new [] { '_' }, StringSplitOptions.RemoveEmptyEntries);
+ if (nums.Length >= 2 && ushort.TryParse(nums[1], out var ifoNumber))
{
ReadVTS(ifoNumber, ifo.FullName);
- numbersRead.Add(ifoNumber);
}
}
}
@@ -76,7 +76,7 @@ namespace DvdLib.Ifo
}
}
- private void ReadVTS(ushort vtsNum, List<FileSystemMetadata> allFiles)
+ private void ReadVTS(ushort vtsNum, IEnumerable<FileSystemMetadata> allFiles)
{
var filename = string.Format("VTS_{0:00}_0.IFO", vtsNum);
diff --git a/Emby.Dlna/ContentDirectory/ControlHandler.cs b/Emby.Dlna/ContentDirectory/ControlHandler.cs
index 1150afdba..84f38ff76 100644
--- a/Emby.Dlna/ContentDirectory/ControlHandler.cs
+++ b/Emby.Dlna/ContentDirectory/ControlHandler.cs
@@ -260,7 +260,7 @@ namespace Emby.Dlna.ContentDirectory
if (item.IsDisplayedAsFolder || serverItem.StubType.HasValue)
{
- var childrenResult = (GetUserItems(item, serverItem.StubType, user, sortCriteria, start, requestedCount));
+ var childrenResult = GetUserItems(item, serverItem.StubType, user, sortCriteria, start, requestedCount);
_didlBuilder.WriteFolderElement(writer, item, serverItem.StubType, null, childrenResult.TotalRecordCount, filter, id);
}
@@ -273,7 +273,7 @@ namespace Emby.Dlna.ContentDirectory
}
else
{
- var childrenResult = (GetUserItems(item, serverItem.StubType, user, sortCriteria, start, requestedCount));
+ var childrenResult = GetUserItems(item, serverItem.StubType, user, sortCriteria, start, requestedCount);
totalCount = childrenResult.TotalRecordCount;
provided = childrenResult.Items.Length;
diff --git a/Emby.Dlna/Didl/DidlBuilder.cs b/Emby.Dlna/Didl/DidlBuilder.cs
index 605f4f37b..1268f3d5c 100644
--- a/Emby.Dlna/Didl/DidlBuilder.cs
+++ b/Emby.Dlna/Didl/DidlBuilder.cs
@@ -818,10 +818,9 @@ namespace Emby.Dlna.Didl
{
AddCommonFields(item, itemStubType, context, writer, filter);
- var hasArtists = item as IHasArtist;
var hasAlbumArtists = item as IHasAlbumArtist;
- if (hasArtists != null)
+ if (item is IHasArtist hasArtists)
{
foreach (var artist in hasArtists.Artists)
{
diff --git a/Emby.Dlna/DlnaManager.cs b/Emby.Dlna/DlnaManager.cs
index f53d27451..d6ee5d13a 100644
--- a/Emby.Dlna/DlnaManager.cs
+++ b/Emby.Dlna/DlnaManager.cs
@@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
+using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
@@ -15,7 +16,6 @@ using MediaBrowser.Controller.Drawing;
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Drawing;
using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Reflection;
using MediaBrowser.Model.Serialization;
using Microsoft.Extensions.Logging;
@@ -29,7 +29,7 @@ namespace Emby.Dlna
private readonly ILogger _logger;
private readonly IJsonSerializer _jsonSerializer;
private readonly IServerApplicationHost _appHost;
- private readonly IAssemblyInfo _assemblyInfo;
+ private static readonly Assembly _assembly = typeof(DlnaManager).Assembly;
private readonly Dictionary<string, Tuple<InternalProfileInfo, DeviceProfile>> _profiles = new Dictionary<string, Tuple<InternalProfileInfo, DeviceProfile>>(StringComparer.Ordinal);
@@ -39,8 +39,7 @@ namespace Emby.Dlna
IApplicationPaths appPaths,
ILoggerFactory loggerFactory,
IJsonSerializer jsonSerializer,
- IServerApplicationHost appHost,
- IAssemblyInfo assemblyInfo)
+ IServerApplicationHost appHost)
{
_xmlSerializer = xmlSerializer;
_fileSystem = fileSystem;
@@ -48,7 +47,6 @@ namespace Emby.Dlna
_logger = loggerFactory.CreateLogger("Dlna");
_jsonSerializer = jsonSerializer;
_appHost = appHost;
- _assemblyInfo = assemblyInfo;
}
public async Task InitProfilesAsync()
@@ -368,15 +366,18 @@ namespace Emby.Dlna
var systemProfilesPath = SystemProfilesPath;
- foreach (var name in _assemblyInfo.GetManifestResourceNames(GetType())
- .Where(i => i.StartsWith(namespaceName))
- .ToList())
+ foreach (var name in _assembly.GetManifestResourceNames())
{
+ if (!name.StartsWith(namespaceName))
+ {
+ continue;
+ }
+
var filename = Path.GetFileName(name).Substring(namespaceName.Length);
var path = Path.Combine(systemProfilesPath, filename);
- using (var stream = _assemblyInfo.GetManifestResourceStream(GetType(), name))
+ using (var stream = _assembly.GetManifestResourceStream(name))
{
var fileInfo = _fileSystem.GetFileInfo(path);
@@ -514,7 +515,7 @@ namespace Emby.Dlna
return new ImageStream
{
Format = format,
- Stream = _assemblyInfo.GetManifestResourceStream(GetType(), resource)
+ Stream = _assembly.GetManifestResourceStream(resource)
};
}
}
diff --git a/Emby.Dlna/Main/DlnaEntryPoint.cs b/Emby.Dlna/Main/DlnaEntryPoint.cs
index 5a7c9b617..57ed0097a 100644
--- a/Emby.Dlna/Main/DlnaEntryPoint.cs
+++ b/Emby.Dlna/Main/DlnaEntryPoint.cs
@@ -246,7 +246,7 @@ namespace Emby.Dlna.Main
private async Task RegisterServerEndpoints()
{
- var addresses = (await _appHost.GetLocalIpAddresses(CancellationToken.None).ConfigureAwait(false)).ToList();
+ var addresses = await _appHost.GetLocalIpAddresses(CancellationToken.None).ConfigureAwait(false);
var udn = CreateUuid(_appHost.SystemId);
diff --git a/Emby.Dlna/PlayTo/TransportCommands.cs b/Emby.Dlna/PlayTo/TransportCommands.cs
index b96fa43e5..4f9e398e9 100644
--- a/Emby.Dlna/PlayTo/TransportCommands.cs
+++ b/Emby.Dlna/PlayTo/TransportCommands.cs
@@ -107,12 +107,18 @@ namespace Emby.Dlna.PlayTo
foreach (var arg in action.ArgumentList)
{
if (arg.Direction == "out")
+ {
continue;
+ }
if (arg.Name == "InstanceID")
+ {
stateString += BuildArgumentXml(arg, "0");
+ }
else
+ {
stateString += BuildArgumentXml(arg, null);
+ }
}
return string.Format(CommandBase, action.Name, xmlNamespace, stateString);
@@ -125,11 +131,18 @@ namespace Emby.Dlna.PlayTo
foreach (var arg in action.ArgumentList)
{
if (arg.Direction == "out")
+ {
continue;
+ }
+
if (arg.Name == "InstanceID")
+ {
stateString += BuildArgumentXml(arg, "0");
+ }
else
+ {
stateString += BuildArgumentXml(arg, value.ToString(), commandParameter);
+ }
}
return string.Format(CommandBase, action.Name, xmlNamesapce, stateString);
@@ -142,11 +155,17 @@ namespace Emby.Dlna.PlayTo
foreach (var arg in action.ArgumentList)
{
if (arg.Name == "InstanceID")
+ {
stateString += BuildArgumentXml(arg, "0");
+ }
else if (dictionary.ContainsKey(arg.Name))
+ {
stateString += BuildArgumentXml(arg, dictionary[arg.Name]);
+ }
else
+ {
stateString += BuildArgumentXml(arg, value.ToString());
+ }
}
return string.Format(CommandBase, action.Name, xmlNamesapce, stateString);
diff --git a/Emby.Naming/TV/EpisodePathParser.cs b/Emby.Naming/TV/EpisodePathParser.cs
index 9485d697b..a8f81a3b8 100644
--- a/Emby.Naming/TV/EpisodePathParser.cs
+++ b/Emby.Naming/TV/EpisodePathParser.cs
@@ -2,7 +2,6 @@ using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
-using System.Text.RegularExpressions;
using Emby.Naming.Common;
namespace Emby.Naming.TV
@@ -22,7 +21,9 @@ namespace Emby.Naming.TV
// There were no failed tests without this block, but to be safe, we can keep it until
// the regex which require file extensions are modified so that they don't need them.
if (IsDirectory)
+ {
path += ".mp4";
+ }
EpisodePathParserResult result = null;
@@ -35,6 +36,7 @@ namespace Emby.Naming.TV
continue;
}
}
+
if (isNamed.HasValue)
{
if (expression.IsNamed != isNamed.Value)
@@ -42,6 +44,7 @@ namespace Emby.Naming.TV
continue;
}
}
+
if (isOptimistic.HasValue)
{
if (expression.IsOptimistic != isOptimistic.Value)
@@ -191,13 +194,20 @@ namespace Emby.Naming.TV
private void FillAdditional(string path, EpisodePathParserResult info, IEnumerable<EpisodeExpression> expressions)
{
- var results = expressions
- .Where(i => i.IsNamed)
- .Select(i => Parse(path, i))
- .Where(i => i.Success);
-
- foreach (var result in results)
+ foreach (var i in expressions)
{
+ if (!i.IsNamed)
+ {
+ continue;
+ }
+
+ var result = Parse(path, i);
+
+ if (!result.Success)
+ {
+ continue;
+ }
+
if (string.IsNullOrEmpty(info.SeriesName))
{
info.SeriesName = result.SeriesName;
@@ -208,12 +218,10 @@ namespace Emby.Naming.TV
info.EndingEpsiodeNumber = result.EndingEpsiodeNumber;
}
- if (!string.IsNullOrEmpty(info.SeriesName))
+ if (!string.IsNullOrEmpty(info.SeriesName)
+ && (!info.EpisodeNumber.HasValue || info.EndingEpsiodeNumber.HasValue))
{
- if (!info.EpisodeNumber.HasValue || info.EndingEpsiodeNumber.HasValue)
- {
- break;
- }
+ break;
}
}
}
diff --git a/Emby.Server.Implementations/Activity/ActivityManager.cs b/Emby.Server.Implementations/Activity/ActivityManager.cs
index 6febcc2f7..0c513ea12 100644
--- a/Emby.Server.Implementations/Activity/ActivityManager.cs
+++ b/Emby.Server.Implementations/Activity/ActivityManager.cs
@@ -39,8 +39,13 @@ namespace Emby.Server.Implementations.Activity
{
var result = _repo.GetActivityLogEntries(minDate, hasUserId, startIndex, limit);
- foreach (var item in result.Items.Where(i => !i.UserId.Equals(Guid.Empty)))
+ foreach (var item in result.Items)
{
+ if (item.UserId == Guid.Empty)
+ {
+ continue;
+ }
+
var user = _userManager.GetUserById(item.UserId);
if (user != null)
diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs
index b5a64cbdd..b3bb4f740 100644
--- a/Emby.Server.Implementations/ApplicationHost.cs
+++ b/Emby.Server.Implementations/ApplicationHost.cs
@@ -744,10 +744,8 @@ namespace Emby.Server.Implementations
TVSeriesManager = new TVSeriesManager(UserManager, UserDataManager, LibraryManager, ServerConfigurationManager);
serviceCollection.AddSingleton(TVSeriesManager);
- var encryptionManager = new EncryptionManager();
- serviceCollection.AddSingleton<IEncryptionManager>(encryptionManager);
-
DeviceManager = new DeviceManager(AuthenticationRepository, JsonSerializer, LibraryManager, LocalizationManager, UserManager, FileSystemManager, LibraryMonitor, ServerConfigurationManager);
+
serviceCollection.AddSingleton(DeviceManager);
MediaSourceManager = new MediaSourceManager(ItemRepository, ApplicationPaths, LocalizationManager, UserManager, LibraryManager, LoggerFactory, JsonSerializer, FileSystemManager, UserDataManager, () => MediaEncoder);
@@ -769,7 +767,7 @@ namespace Emby.Server.Implementations
serviceCollection.AddSingleton(SessionManager);
serviceCollection.AddSingleton<IDlnaManager>(
- new DlnaManager(XmlSerializer, FileSystemManager, ApplicationPaths, LoggerFactory, JsonSerializer, this, assemblyInfo));
+ new DlnaManager(XmlSerializer, FileSystemManager, ApplicationPaths, LoggerFactory, JsonSerializer, this));
CollectionManager = new CollectionManager(LibraryManager, ApplicationPaths, LocalizationManager, FileSystemManager, LibraryMonitor, LoggerFactory, ProviderManager);
serviceCollection.AddSingleton(CollectionManager);
diff --git a/Emby.Server.Implementations/Channels/ChannelManager.cs b/Emby.Server.Implementations/Channels/ChannelManager.cs
index 949b89226..7e50650d7 100644
--- a/Emby.Server.Implementations/Channels/ChannelManager.cs
+++ b/Emby.Server.Implementations/Channels/ChannelManager.cs
@@ -243,8 +243,7 @@ namespace Emby.Server.Implementations.Channels
{
foreach (var item in returnItems)
{
- var task = RefreshLatestChannelItems(GetChannelProvider(item), CancellationToken.None);
- Task.WaitAll(task);
+ RefreshLatestChannelItems(GetChannelProvider(item), CancellationToken.None).GetAwaiter().GetResult();
}
}
@@ -303,9 +302,7 @@ namespace Emby.Server.Implementations.Channels
}
numComplete++;
- double percent = numComplete;
- percent /= allChannelsList.Count;
-
+ double percent = (double)numComplete / allChannelsList.Count;
progress.Report(100 * percent);
}
@@ -658,9 +655,7 @@ namespace Emby.Server.Implementations.Channels
foreach (var item in result.Items)
{
- var folder = item as Folder;
-
- if (folder != null)
+ if (item is Folder folder)
{
await GetChannelItemsInternal(new InternalItemsQuery
{
diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs
index 70e5fa640..06f6563a3 100644
--- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs
+++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs
@@ -2279,11 +2279,10 @@ namespace Emby.Server.Implementations.Data
private static readonly HashSet<string> _seriesTypes = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
- "Audio",
- "MusicAlbum",
- "MusicVideo",
+ "Book",
"AudioBook",
- "AudioPodcast"
+ "Episode",
+ "Season"
};
private bool HasSeriesFields(InternalItemsQuery query)
diff --git a/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs b/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs
index 7a9b72244..4109b7ad1 100644
--- a/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs
+++ b/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs
@@ -119,9 +119,9 @@ namespace Emby.Server.Implementations.Data
{
list.Add(row[0].ReadGuidFromBlob());
}
- catch
+ catch (Exception ex)
{
-
+ Logger.LogError(ex, "Error while getting user");
}
}
}
diff --git a/Emby.Server.Implementations/Security/EncryptionManager.cs b/Emby.Server.Implementations/Security/EncryptionManager.cs
deleted file mode 100644
index fa8872ccc..000000000
--- a/Emby.Server.Implementations/Security/EncryptionManager.cs
+++ /dev/null
@@ -1,57 +0,0 @@
-using System;
-using System.Text;
-using MediaBrowser.Controller.Security;
-
-namespace Emby.Server.Implementations.Security
-{
- public class EncryptionManager : IEncryptionManager
- {
- /// <summary>
- /// Encrypts the string.
- /// </summary>
- /// <param name="value">The value.</param>
- /// <returns>System.String.</returns>
- /// <exception cref="ArgumentNullException">value</exception>
- public string EncryptString(string value)
- {
- if (value == null)
- {
- throw new ArgumentNullException(nameof(value));
- }
-
- return EncryptStringUniversal(value);
- }
-
- /// <summary>
- /// Decrypts the string.
- /// </summary>
- /// <param name="value">The value.</param>
- /// <returns>System.String.</returns>
- /// <exception cref="ArgumentNullException">value</exception>
- public string DecryptString(string value)
- {
- if (value == null)
- {
- throw new ArgumentNullException(nameof(value));
- }
-
- return DecryptStringUniversal(value);
- }
-
- private static string EncryptStringUniversal(string value)
- {
- // Yes, this isn't good, but ProtectedData in mono is throwing exceptions, so use this for now
-
- var bytes = Encoding.UTF8.GetBytes(value);
- return Convert.ToBase64String(bytes);
- }
-
- private static string DecryptStringUniversal(string value)
- {
- // Yes, this isn't good, but ProtectedData in mono is throwing exceptions, so use this for now
-
- var bytes = Convert.FromBase64String(value);
- return Encoding.UTF8.GetString(bytes, 0, bytes.Length);
- }
- }
-}
diff --git a/Emby.XmlTv/Emby.XmlTv/Classes/XmlTvReader.cs b/Emby.XmlTv/Emby.XmlTv/Classes/XmlTvReader.cs
index 52ec7a135..46bf6cc21 100644
--- a/Emby.XmlTv/Emby.XmlTv/Classes/XmlTvReader.cs
+++ b/Emby.XmlTv/Emby.XmlTv/Classes/XmlTvReader.cs
@@ -495,9 +495,7 @@ namespace Emby.XmlTv.Classes
ParseMovieDbSystem(reader, result);
break;
case "SxxExx":
- // TODO
- // <episode-num system="SxxExx">S03E12</episode-num>
- reader.Skip();
+ ParseSxxExxSystem(reader, result);
break;
default: // Handles empty string and nulls
reader.Skip();
@@ -505,6 +503,29 @@ namespace Emby.XmlTv.Classes
}
}
+ public void ParseSxxExxSystem(XmlReader reader, XmlTvProgram result)
+ {
+ // <episode-num system="SxxExx">S012E32</episode-num>
+
+ var value = reader.ReadElementContentAsString();
+ var res = Regex.Match(value, "s([0-9]+)e([0-9]+)", RegexOptions.IgnoreCase);
+
+ if (res.Success)
+ {
+ int parsedInt;
+
+ if (int.TryParse(res.Groups[1].Value, out parsedInt))
+ {
+ result.Episode.Series = parsedInt;
+ }
+
+ if (int.TryParse(res.Groups[2].Value, out parsedInt))
+ {
+ result.Episode.Episode = parsedInt;
+ }
+ }
+ }
+
public void ParseMovieDbSystem(XmlReader reader, XmlTvProgram result)
{
// <episode-num system="thetvdb.com">series/248841</episode-num>
diff --git a/Jellyfin.Drawing.Skia/PercentPlayedDrawer.cs b/Jellyfin.Drawing.Skia/PercentPlayedDrawer.cs
index 0d5a1d3c0..c72f295fd 100644
--- a/Jellyfin.Drawing.Skia/PercentPlayedDrawer.cs
+++ b/Jellyfin.Drawing.Skia/PercentPlayedDrawer.cs
@@ -23,7 +23,7 @@ namespace Jellyfin.Drawing.Skia
foregroundWidth *= percent;
foregroundWidth /= 100;
- paint.Color = SKColor.Parse("#FF52B54B");
+ paint.Color = SKColor.Parse("#FF00A4DC");
canvas.DrawRect(SKRect.Create(0, (float)endY - IndicatorHeight, Convert.ToInt32(foregroundWidth), (float)endY), paint);
}
}
diff --git a/Jellyfin.Drawing.Skia/PlayedIndicatorDrawer.cs b/Jellyfin.Drawing.Skia/PlayedIndicatorDrawer.cs
index 62497da27..7f3c18bb2 100644
--- a/Jellyfin.Drawing.Skia/PlayedIndicatorDrawer.cs
+++ b/Jellyfin.Drawing.Skia/PlayedIndicatorDrawer.cs
@@ -13,7 +13,7 @@ namespace Jellyfin.Drawing.Skia
using (var paint = new SKPaint())
{
- paint.Color = SKColor.Parse("#CC52B54B");
+ paint.Color = SKColor.Parse("#CC00A4DC");
paint.Style = SKPaintStyle.Fill;
canvas.DrawCircle((float)x, OffsetFromTopRightCorner, 20, paint);
}
diff --git a/Jellyfin.Drawing.Skia/UnplayedCountIndicator.cs b/Jellyfin.Drawing.Skia/UnplayedCountIndicator.cs
index ba712bff7..dbf935f4e 100644
--- a/Jellyfin.Drawing.Skia/UnplayedCountIndicator.cs
+++ b/Jellyfin.Drawing.Skia/UnplayedCountIndicator.cs
@@ -15,7 +15,7 @@ namespace Jellyfin.Drawing.Skia
using (var paint = new SKPaint())
{
- paint.Color = SKColor.Parse("#CC52B54B");
+ paint.Color = SKColor.Parse("#CC00A4DC");
paint.Style = SKPaintStyle.Fill;
canvas.DrawCircle((float)x, OffsetFromTopRightCorner, 20, paint);
}
diff --git a/MediaBrowser.Controller/Security/IEncryptionManager.cs b/MediaBrowser.Controller/Security/IEncryptionManager.cs
deleted file mode 100644
index 68680fdf3..000000000
--- a/MediaBrowser.Controller/Security/IEncryptionManager.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-namespace MediaBrowser.Controller.Security
-{
- public interface IEncryptionManager
- {
- /// <summary>
- /// Encrypts the string.
- /// </summary>
- /// <param name="value">The value.</param>
- /// <returns>System.String.</returns>
- string EncryptString(string value);
-
- /// <summary>
- /// Decrypts the string.
- /// </summary>
- /// <param name="value">The value.</param>
- /// <returns>System.String.</returns>
- string DecryptString(string value);
- }
-}
diff --git a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs
index 262772959..f725d2c01 100644
--- a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs
+++ b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs
@@ -1,6 +1,6 @@
using System;
using System.Collections.Generic;
-using System.Globalization;
+using System.Collections.ObjectModel;
using System.Linq;
using System.Text.RegularExpressions;
using MediaBrowser.Model.Diagnostics;
@@ -58,18 +58,107 @@ namespace MediaBrowser.MediaEncoding.Encoder
return false;
}
- output = " " + output + " ";
+ // The min and max FFmpeg versions required to run jellyfin successfully
+ var minRequired = new Version(4, 0);
+ var maxRequired = new Version(4, 0);
- for (var i = 2013; i <= 2015; i++)
+ // Work out what the version under test is
+ var underTest = GetFFmpegVersion(output);
+
+ if (logOutput)
{
- var yearString = i.ToString(CultureInfo.InvariantCulture);
- if (output.IndexOf(" " + yearString + " ", StringComparison.OrdinalIgnoreCase) != -1)
+ _logger.LogInformation("FFmpeg validation: Found ffmpeg version {0}", underTest != null ? underTest.ToString() : "unknown");
+
+ if (underTest == null) // Version is unknown
+ {
+ if (minRequired.Equals(maxRequired))
+ {
+ _logger.LogWarning("FFmpeg validation: We recommend ffmpeg version {0}", minRequired.ToString());
+ }
+ else
+ {
+ _logger.LogWarning("FFmpeg validation: We recommend a minimum of {0} and maximum of {1}", minRequired.ToString(), maxRequired.ToString());
+ }
+ }
+ else if (underTest.CompareTo(minRequired) < 0) // Version is below what we recommend
{
- return false;
+ _logger.LogWarning("FFmpeg validation: The minimum recommended ffmpeg version is {0}", minRequired.ToString());
}
+ else if (underTest.CompareTo(maxRequired) > 0) // Version is above what we recommend
+ {
+ _logger.LogWarning("FFmpeg validation: The maximum recommended ffmpeg version is {0}", maxRequired.ToString());
+ }
+ else // Version is ok
+ {
+ _logger.LogInformation("FFmpeg validation: Found suitable ffmpeg version");
+ }
+ }
+
+ // underTest shall be null if versions is unknown
+ return (underTest == null) ? false : (underTest.CompareTo(minRequired) >= 0 && underTest.CompareTo(maxRequired) <= 0);
+ }
+
+ /// <summary>
+ /// Using the output from "ffmpeg -version" work out the FFmpeg version.
+ /// For pre-built binaries the first line should contain a string like "ffmpeg version x.y", which is easy
+ /// to parse. If this is not available, then we try to match known library versions to FFmpeg versions.
+ /// If that fails then we use one of the main libraries to determine if it's new/older than the latest
+ /// we have stored.
+ /// </summary>
+ /// <param name="output"></param>
+ /// <returns></returns>
+ static private Version GetFFmpegVersion(string output)
+ {
+ // For pre-built binaries the FFmpeg version should be mentioned at the very start of the output
+ var match = Regex.Match(output, @"ffmpeg version (\d+\.\d+)");
+
+ if (match.Success)
+ {
+ return new Version(match.Groups[1].Value);
+ }
+ else
+ {
+ // Try and use the individual library versions to determine a FFmpeg version
+ // This lookup table is to be maintained with the following command line:
+ // $ ./ffmpeg.exe -version | perl -ne ' print "$1=$2.$3," if /^(lib\w+)\s+(\d+)\.\s*(\d+)/'
+ var lut = new ReadOnlyDictionary<Version, string>
+ (new Dictionary<Version, string>
+ {
+ { new Version("4.1"), "libavutil=56.22,libavcodec=58.35,libavformat=58.20,libavdevice=58.5,libavfilter=7.40,libswscale=5.3,libswresample=3.3,libpostproc=55.3," },
+ { new Version("4.0"), "libavutil=56.14,libavcodec=58.18,libavformat=58.12,libavdevice=58.3,libavfilter=7.16,libswscale=5.1,libswresample=3.1,libpostproc=55.1," },
+ { new Version("3.4"), "libavutil=55.78,libavcodec=57.107,libavformat=57.83,libavdevice=57.10,libavfilter=6.107,libswscale=4.8,libswresample=2.9,libpostproc=54.7," },
+ { new Version("3.3"), "libavutil=55.58,libavcodec=57.89,libavformat=57.71,libavdevice=57.6,libavfilter=6.82,libswscale=4.6,libswresample=2.7,libpostproc=54.5," },
+ { new Version("3.2"), "libavutil=55.34,libavcodec=57.64,libavformat=57.56,libavdevice=57.1,libavfilter=6.65,libswscale=4.2,libswresample=2.3,libpostproc=54.1," },
+ { new Version("2.8"), "libavutil=54.31,libavcodec=56.60,libavformat=56.40,libavdevice=56.4,libavfilter=5.40,libswscale=3.1,libswresample=1.2,libpostproc=53.3," }
+ });
+
+ // Create a reduced version string and lookup key from dictionary
+ var reducedVersion = GetVersionString(output);
+
+ // Try to lookup the string and return Key, otherwise if not found returns null
+ return lut.FirstOrDefault(x => x.Value == reducedVersion).Key;
+ }
+ }
+
+ /// <summary>
+ /// Grabs the library names and major.minor version numbers from the 'ffmpeg -version' output
+ /// and condenses them on to one line. Output format is "name1=major.minor,name2=major.minor,etc."
+ /// </summary>
+ /// <param name="output"></param>
+ /// <returns></returns>
+ static private string GetVersionString(string output)
+ {
+ string pattern = @"((?<name>lib\w+)\s+(?<major>\d+)\.\s*(?<minor>\d+))";
+ RegexOptions options = RegexOptions.Multiline;
+
+ string rc = null;
+
+ foreach (Match m in Regex.Matches(output, pattern, options))
+ {
+ rc += string.Concat(m.Groups["name"], '=', m.Groups["major"], '.', m.Groups["minor"], ',');
}
- return true;
+ return rc;
}
private static readonly string[] requiredDecoders = new[]
diff --git a/MediaBrowser.MediaEncoding/Subtitles/OpenSubtitleDownloader.cs b/MediaBrowser.MediaEncoding/Subtitles/OpenSubtitleDownloader.cs
index 6a5162b8d..a7e3f6197 100644
--- a/MediaBrowser.MediaEncoding/Subtitles/OpenSubtitleDownloader.cs
+++ b/MediaBrowser.MediaEncoding/Subtitles/OpenSubtitleDownloader.cs
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
+using System.Text;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.Configuration;
@@ -29,17 +30,15 @@ namespace MediaBrowser.MediaEncoding.Subtitles
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
private readonly IServerConfigurationManager _config;
- private readonly IEncryptionManager _encryption;
private readonly IJsonSerializer _json;
private readonly IFileSystem _fileSystem;
- public OpenSubtitleDownloader(ILoggerFactory loggerFactory, IHttpClient httpClient, IServerConfigurationManager config, IEncryptionManager encryption, IJsonSerializer json, IFileSystem fileSystem)
+ public OpenSubtitleDownloader(ILoggerFactory loggerFactory, IHttpClient httpClient, IServerConfigurationManager config, IJsonSerializer json, IFileSystem fileSystem)
{
_logger = loggerFactory.CreateLogger(GetType().Name);
_httpClient = httpClient;
_config = config;
- _encryption = encryption;
_json = json;
_fileSystem = fileSystem;
@@ -63,16 +62,17 @@ namespace MediaBrowser.MediaEncoding.Subtitles
!string.IsNullOrWhiteSpace(options.OpenSubtitlesPasswordHash) &&
!options.OpenSubtitlesPasswordHash.StartsWith(PasswordHashPrefix, StringComparison.OrdinalIgnoreCase))
{
- options.OpenSubtitlesPasswordHash = EncryptPassword(options.OpenSubtitlesPasswordHash);
+ options.OpenSubtitlesPasswordHash = EncodePassword(options.OpenSubtitlesPasswordHash);
}
}
- private string EncryptPassword(string password)
+ private static string EncodePassword(string password)
{
- return PasswordHashPrefix + _encryption.EncryptString(password);
+ var bytes = Encoding.UTF8.GetBytes(password);
+ return PasswordHashPrefix + Convert.ToBase64String(bytes);
}
- private string DecryptPassword(string password)
+ private static string DecodePassword(string password)
{
if (password == null ||
!password.StartsWith(PasswordHashPrefix, StringComparison.OrdinalIgnoreCase))
@@ -80,7 +80,8 @@ namespace MediaBrowser.MediaEncoding.Subtitles
return string.Empty;
}
- return _encryption.DecryptString(password.Substring(2));
+ var bytes = Convert.FromBase64String(password.Substring(2));
+ return Encoding.UTF8.GetString(bytes, 0, bytes.Length);
}
public string Name => "Open Subtitles";
@@ -186,7 +187,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
var options = GetOptions();
var user = options.OpenSubtitlesUsername ?? string.Empty;
- var password = DecryptPassword(options.OpenSubtitlesPasswordHash);
+ var password = DecodePassword(options.OpenSubtitlesPasswordHash);
var loginResponse = await OpenSubtitles.LogInAsync(user, password, "en", cancellationToken).ConfigureAwait(false);
diff --git a/build b/build
index 3b4167dae..51d4b79a2 100755
--- a/build
+++ b/build
@@ -26,7 +26,7 @@ usage() {
echo -e " $ build [-k/--keep-artifacts] [-b/--web-branch <web_branch>] <platform> <action>"
echo -e ""
echo -e "The 'keep-artifacts' option preserves build artifacts, e.g. Docker images for system package builds."
- echo -e "The web_branch defaults to the same branch name as the current main branch."
+ echo -e "The web_branch defaults to the same branch name as the current main branch or can be 'local' to not touch the submodule branching."
echo -e "To build all platforms, use 'all'."
echo -e "To perform all build actions, use 'all'."
echo -e "Build output files are collected at '../jellyfin-build/<platform>'."
@@ -164,37 +164,39 @@ for target_platform in ${platform[@]}; do
fi
done
-# Initialize submodules
-git submodule update --init --recursive
+if [[ ${web_branch} != 'local' ]]; then
+ # Initialize submodules
+ git submodule update --init --recursive
-# configure branch
-pushd MediaBrowser.WebDashboard/jellyfin-web
+ # configure branch
+ pushd MediaBrowser.WebDashboard/jellyfin-web
-if ! git diff-index --quiet HEAD --; then
- popd
- echo
- echo "ERROR: Your 'jellyfin-web' submodule working directory is not clean!"
- echo "This script will overwrite your unstaged and unpushed changes."
- echo "Please do development on 'jellyfin-web' outside of the submodule."
- exit 1
-fi
-
-git fetch --all
-# If this is an official branch name, fetch it from origin
-official_branches_regex="^master$|^dev$|^release-.*$|^hotfix-.*$"
-if [[ ${web_branch} =~ ${official_branches_regex} ]]; then
- git checkout origin/${web_branch} || {
- echo "ERROR: 'jellyfin-web' branch 'origin/${web_branch}' is invalid."
+ if ! git diff-index --quiet HEAD --; then
+ popd
+ echo
+ echo "ERROR: Your 'jellyfin-web' submodule working directory is not clean!"
+ echo "This script will overwrite your unstaged and unpushed changes."
+ echo "Please do development on 'jellyfin-web' outside of the submodule."
exit 1
- }
-# Otherwise, just check out the local branch (for testing, etc.)
-else
- git checkout ${web_branch} || {
- echo "ERROR: 'jellyfin-web' branch '${web_branch}' is invalid."
- exit 1
- }
+ fi
+
+ git fetch --all
+ # If this is an official branch name, fetch it from origin
+ official_branches_regex="^master$|^dev$|^release-.*$|^hotfix-.*$"
+ if [[ ${web_branch} =~ ${official_branches_regex} ]]; then
+ git checkout origin/${web_branch} || {
+ echo "ERROR: 'jellyfin-web' branch 'origin/${web_branch}' is invalid."
+ exit 1
+ }
+ # Otherwise, just check out the local branch (for testing, etc.)
+ else
+ git checkout ${web_branch} || {
+ echo "ERROR: 'jellyfin-web' branch '${web_branch}' is invalid."
+ exit 1
+ }
+ fi
+ popd
fi
-popd
# Execute each platform and action in order, if said action is enabled
pushd deployment/
@@ -217,7 +219,7 @@ for target_platform in ${platform[@]}; do
done
if [[ -d pkg-dist/ ]]; then
echo -e ">> Collecting build artifacts"
- target_dir="../../../jellyfin-build/${target_platform}"
+ target_dir="../../../bin/${target_platform}"
mkdir -p ${target_dir}
mv pkg-dist/* ${target_dir}/
fi
diff --git a/build.yaml b/build.yaml
new file mode 100644
index 000000000..b0d2502d5
--- /dev/null
+++ b/build.yaml
@@ -0,0 +1,15 @@
+---
+# We just wrap `build` so this is really it
+name: "jellyfin"
+version: "10.2.2"
+packages:
+ - debian-package-x64
+ - debian-package-armhf
+ - ubuntu-package-x64
+ - fedora-package-x64
+ - centos-package-x64
+ - linux-x64
+ - macos
+ - portable
+ - win-x64
+ - win-x86
diff --git a/deployment/win-x64/package.sh b/deployment/win-x64/package.sh
index d21e3b532..b438c28e4 100755
--- a/deployment/win-x64/package.sh
+++ b/deployment/win-x64/package.sh
@@ -21,8 +21,8 @@ package_win64() (
cp ${TEMP_DIR}/${FFMPEG_VERSION}/bin/ffmpeg.exe ${OUTPUT_DIR}/ffmpeg.exe
cp ${TEMP_DIR}/${FFMPEG_VERSION}/bin/ffprobe.exe ${OUTPUT_DIR}/ffprobe.exe
rm -r ${TEMP_DIR}
- cp ${ROOT}/deployment/win-generic/install-jellyfin.ps1 ${OUTPUT_DIR}/install-jellyfin.ps1
- cp ${ROOT}/deployment/win-generic/install.bat ${OUTPUT_DIR}/install.bat
+ cp ${ROOT}/deployment/windows/install-jellyfin.ps1 ${OUTPUT_DIR}/install-jellyfin.ps1
+ cp ${ROOT}/deployment/windows/install.bat ${OUTPUT_DIR}/install.bat
mkdir -p ${PKG_DIR}
pushd ${OUTPUT_DIR}
${ARCHIVE_CMD} ${ROOT}/${PKG_DIR}/`basename "${OUTPUT_DIR}"`.zip .
diff --git a/deployment/win-x86/package.sh b/deployment/win-x86/package.sh
index 3cc4eb623..8752d92a8 100755
--- a/deployment/win-x86/package.sh
+++ b/deployment/win-x86/package.sh
@@ -20,8 +20,8 @@ package_win32() (
cp ${TEMP_DIR}/${FFMPEG_VERSION}/bin/ffmpeg.exe ${OUTPUT_DIR}/ffmpeg.exe
cp ${TEMP_DIR}/${FFMPEG_VERSION}/bin/ffprobe.exe ${OUTPUT_DIR}/ffprobe.exe
rm -r ${TEMP_DIR}
- cp ${ROOT}/deployment/win-generic/install-jellyfin.ps1 ${OUTPUT_DIR}/install-jellyfin.ps1
- cp ${ROOT}/deployment/win-generic/install.bat ${OUTPUT_DIR}/install.bat
+ cp ${ROOT}/deployment/windows/install-jellyfin.ps1 ${OUTPUT_DIR}/install-jellyfin.ps1
+ cp ${ROOT}/deployment/windows/install.bat ${OUTPUT_DIR}/install.bat
mkdir -p ${PKG_DIR}
pushd ${OUTPUT_DIR}
${ARCHIVE_CMD} ${ROOT}/${PKG_DIR}/`basename "${OUTPUT_DIR}"`.zip .