aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAhmed Rafiq <ahmedrafiq47@gmail.com>2021-12-04 19:50:48 +0600
committerAhmed Rafiq <ahmedrafiq47@gmail.com>2021-12-04 20:08:16 +0600
commit6193fdea698ad4486140bc6a00a9547ab979c8f7 (patch)
tree1840bcf7dd8a65662936d9ee8bd4ec64480580e9
parent9cea773d293d31446e5aac3f505cda3c90f52421 (diff)
Use MimeTypes package to determine MIME type
This simplifies the code since we don't have to keep large mappings of extensions and MIME types. We still keep the ability to override the mappings for: - filling in entries not present in the package, for e.g. ".azw3" - picking preferred extensions, for e.g. MimeTypes provides ".conf" as a possible extionsion for "text/plain", and while that is correct, ".txt" would be preferrable - compatibility reasons
-rw-r--r--MediaBrowser.Model/MediaBrowser.Model.csproj4
-rw-r--r--MediaBrowser.Model/Net/MimeTypes.cs147
-rw-r--r--tests/Jellyfin.Model.Tests/Net/MimeTypesTests.cs164
3 files changed, 217 insertions, 98 deletions
diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj
index 70fef5d66..b1fbe864b 100644
--- a/MediaBrowser.Model/MediaBrowser.Model.csproj
+++ b/MediaBrowser.Model/MediaBrowser.Model.csproj
@@ -31,6 +31,10 @@
<ItemGroup>
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="6.0.0" />
+ <PackageReference Include="MimeTypes" Version="2.2.1">
+ <PrivateAssets>all</PrivateAssets>
+ <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
+ </PackageReference>
<PackageReference Include="System.Globalization" Version="4.3.0" />
<PackageReference Include="System.Text.Json" Version="6.0.0" />
</ItemGroup>
diff --git a/MediaBrowser.Model/Net/MimeTypes.cs b/MediaBrowser.Model/Net/MimeTypes.cs
index 043cee2a2..ff8d1fbae 100644
--- a/MediaBrowser.Model/Net/MimeTypes.cs
+++ b/MediaBrowser.Model/Net/MimeTypes.cs
@@ -12,6 +12,10 @@ namespace MediaBrowser.Model.Net
/// <summary>
/// Class MimeTypes.
/// </summary>
+ ///
+ /// http://en.wikipedia.org/wiki/Internet_media_type
+ /// https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types
+ /// http://www.iana.org/assignments/media-types/media-types.xhtml
public static class MimeTypes
{
/// <summary>
@@ -50,81 +54,26 @@ namespace MediaBrowser.Model.Net
".wtv",
};
- // http://en.wikipedia.org/wiki/Internet_media_type
- // https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types
- // http://www.iana.org/assignments/media-types/media-types.xhtml
- // Add more as needed
+ /// <summary>
+ /// Used for extensions not in <see cref="Model.MimeTypes"/> or to override them.
+ /// </summary>
private static readonly Dictionary<string, string> _mimeTypeLookup = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
{
// Type application
- { ".7z", "application/x-7z-compressed" },
- { ".azw", "application/vnd.amazon.ebook" },
{ ".azw3", "application/vnd.amazon.ebook" },
- { ".cbz", "application/x-cbz" },
- { ".cbr", "application/epub+zip" },
- { ".eot", "application/vnd.ms-fontobject" },
- { ".epub", "application/epub+zip" },
- { ".js", "application/x-javascript" },
- { ".json", "application/json" },
- { ".m3u8", "application/x-mpegURL" },
- { ".map", "application/x-javascript" },
- { ".mobi", "application/x-mobipocket-ebook" },
- { ".opf", "application/oebps-package+xml" },
- { ".pdf", "application/pdf" },
- { ".rar", "application/vnd.rar" },
- { ".srt", "application/x-subrip" },
- { ".ttml", "application/ttml+xml" },
- { ".wasm", "application/wasm" },
- { ".xml", "application/xml" },
- { ".zip", "application/zip" },
// Type image
- { ".bmp", "image/bmp" },
- { ".gif", "image/gif" },
- { ".ico", "image/vnd.microsoft.icon" },
- { ".jpg", "image/jpeg" },
- { ".jpeg", "image/jpeg" },
- { ".png", "image/png" },
- { ".svg", "image/svg+xml" },
- { ".svgz", "image/svg+xml" },
{ ".tbn", "image/jpeg" },
- { ".tif", "image/tiff" },
- { ".tiff", "image/tiff" },
- { ".webp", "image/webp" },
-
- // Type font
- { ".ttf", "font/ttf" },
- { ".woff", "font/woff" },
- { ".woff2", "font/woff2" },
// Type text
{ ".ass", "text/x-ssa" },
{ ".ssa", "text/x-ssa" },
- { ".css", "text/css" },
- { ".csv", "text/csv" },
{ ".edl", "text/plain" },
- { ".rtf", "text/rtf" },
- { ".txt", "text/plain" },
- { ".vtt", "text/vtt" },
+ { ".html", "text/html; charset=UTF-8" },
+ { ".htm", "text/html; charset=UTF-8" },
// Type video
- { ".3gp", "video/3gpp" },
- { ".3g2", "video/3gpp2" },
- { ".asf", "video/x-ms-asf" },
- { ".avi", "video/x-msvideo" },
- { ".flv", "video/x-flv" },
- { ".mp4", "video/mp4" },
- { ".m4s", "video/mp4" },
- { ".m4v", "video/x-m4v" },
{ ".mpegts", "video/mp2t" },
- { ".mpg", "video/mpeg" },
- { ".mkv", "video/x-matroska" },
- { ".mov", "video/quicktime" },
- { ".mpd", "video/vnd.mpeg.dash.mpd" },
- { ".ogv", "video/ogg" },
- { ".ts", "video/mp2t" },
- { ".webm", "video/webm" },
- { ".wmv", "video/x-ms-wmv" },
// Type audio
{ ".aac", "audio/aac" },
@@ -133,37 +82,47 @@ namespace MediaBrowser.Model.Net
{ ".dsf", "audio/dsf" },
{ ".dsp", "audio/dsp" },
{ ".flac", "audio/flac" },
- { ".m4a", "audio/mp4" },
{ ".m4b", "audio/m4b" },
- { ".mid", "audio/midi" },
- { ".midi", "audio/midi" },
{ ".mp3", "audio/mpeg" },
- { ".oga", "audio/ogg" },
- { ".ogg", "audio/ogg" },
- { ".opus", "audio/ogg" },
{ ".vorbis", "audio/vorbis" },
- { ".wav", "audio/wav" },
{ ".webma", "audio/webm" },
- { ".wma", "audio/x-ms-wma" },
{ ".wv", "audio/x-wavpack" },
{ ".xsp", "audio/xsp" },
};
- private static readonly Dictionary<string, string> _extensionLookup = CreateExtensionLookup();
-
- private static Dictionary<string, string> CreateExtensionLookup()
+ private static readonly Dictionary<string, string> _extensionLookup = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
{
- var dict = _mimeTypeLookup
- .GroupBy(i => i.Value)
- .ToDictionary(x => x.Key, x => x.First().Key, StringComparer.OrdinalIgnoreCase);
+ // Type application
+ { "application/x-cbz", ".cbz" },
+ { "application/x-javascript", ".js" },
+ { "application/xml", ".xml" },
+ { "application/x-mpegURL", ".m3u8" },
+
+ // Type audio
+ { "audio/aac", ".aac" },
+ { "audio/ac3", ".ac3" },
+ { "audio/dsf", ".dsf" },
+ { "audio/dsp", ".dsp" },
+ { "audio/flac", ".flac" },
+ { "audio/m4b", ".m4b" },
+ { "audio/vorbis", ".vorbis" },
+ { "audio/x-ape", ".ape" },
+ { "audio/xsp", ".xsp" },
+ { "audio/x-wavpack", ".wv" },
- dict["image/jpg"] = ".jpg";
- dict["image/x-png"] = ".png";
+ // Type image
+ { "image/jpg", ".jpg" },
+ { "image/x-png", ".png" },
- dict["audio/x-aac"] = ".aac";
+ // Type text
+ { "text/plain", ".txt" },
+ { "text/rtf", ".rtf" },
+ { "text/x-ssa", ".ssa" },
- return dict;
- }
+ // Type video
+ { "video/vnd.mpeg.dash.mpd", ".mpd" },
+ { "video/x-matroska", ".mkv" },
+ };
public static string GetMimeType(string path) => GetMimeType(path, "application/octet-stream");
@@ -188,29 +147,15 @@ namespace MediaBrowser.Model.Net
return result;
}
- // Catch-all for all video types that don't require specific mime types
- if (_videoFileExtensions.Contains(ext))
- {
- return string.Concat("video/", ext.AsSpan(1));
- }
-
- // Type text
- if (string.Equals(ext, ".html", StringComparison.OrdinalIgnoreCase)
- || string.Equals(ext, ".htm", StringComparison.OrdinalIgnoreCase))
- {
- return "text/html; charset=UTF-8";
- }
-
- if (string.Equals(ext, ".log", StringComparison.OrdinalIgnoreCase)
- || string.Equals(ext, ".srt", StringComparison.OrdinalIgnoreCase))
+ if (Model.MimeTypes.TryGetMimeType(filename, out var mimeType))
{
- return "text/plain";
+ return mimeType;
}
- // Misc
- if (string.Equals(ext, ".dll", StringComparison.OrdinalIgnoreCase))
+ // Catch-all for all video types that don't require specific mime types
+ if (_videoFileExtensions.Contains(ext))
{
- return "application/octet-stream";
+ return string.Concat("video/", ext.AsSpan(1));
}
return defaultValue;
@@ -231,6 +176,12 @@ namespace MediaBrowser.Model.Net
return result;
}
+ var extensions = Model.MimeTypes.GetMimeTypeExtensions(mimeType);
+ if (extensions.Any())
+ {
+ return "." + extensions.First();
+ }
+
return null;
}
}
diff --git a/tests/Jellyfin.Model.Tests/Net/MimeTypesTests.cs b/tests/Jellyfin.Model.Tests/Net/MimeTypesTests.cs
new file mode 100644
index 000000000..b82607bf0
--- /dev/null
+++ b/tests/Jellyfin.Model.Tests/Net/MimeTypesTests.cs
@@ -0,0 +1,164 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using MediaBrowser.Model.Net;
+using Xunit;
+
+namespace Jellyfin.Model.Tests.Net
+{
+ public class MimeTypesTests
+ {
+ [Theory]
+ [InlineData(".dll", "application/octet-stream")]
+ [InlineData(".log", "text/plain")]
+ [InlineData(".srt", "application/x-subrip")]
+ [InlineData(".html", "text/html; charset=UTF-8")]
+ [InlineData(".htm", "text/html; charset=UTF-8")]
+ [InlineData(".7z", "application/x-7z-compressed")]
+ [InlineData(".azw", "application/vnd.amazon.ebook")]
+ [InlineData(".azw3", "application/vnd.amazon.ebook")]
+ [InlineData(".eot", "application/vnd.ms-fontobject")]
+ [InlineData(".epub", "application/epub+zip")]
+ [InlineData(".json", "application/json")]
+ [InlineData(".mobi", "application/x-mobipocket-ebook")]
+ [InlineData(".opf", "application/oebps-package+xml")]
+ [InlineData(".pdf", "application/pdf")]
+ [InlineData(".rar", "application/vnd.rar")]
+ [InlineData(".ttml", "application/ttml+xml")]
+ [InlineData(".wasm", "application/wasm")]
+ [InlineData(".xml", "application/xml")]
+ [InlineData(".zip", "application/zip")]
+ [InlineData(".bmp", "image/bmp")]
+ [InlineData(".gif", "image/gif")]
+ [InlineData(".ico", "image/vnd.microsoft.icon")]
+ [InlineData(".jpg", "image/jpeg")]
+ [InlineData(".jpeg", "image/jpeg")]
+ [InlineData(".png", "image/png")]
+ [InlineData(".svg", "image/svg+xml")]
+ [InlineData(".svgz", "image/svg+xml")]
+ [InlineData(".tbn", "image/jpeg")]
+ [InlineData(".tif", "image/tiff")]
+ [InlineData(".tiff", "image/tiff")]
+ [InlineData(".webp", "image/webp")]
+ [InlineData(".ttf", "font/ttf")]
+ [InlineData(".woff", "font/woff")]
+ [InlineData(".woff2", "font/woff2")]
+ [InlineData(".ass", "text/x-ssa")]
+ [InlineData(".ssa", "text/x-ssa")]
+ [InlineData(".css", "text/css")]
+ [InlineData(".csv", "text/csv")]
+ [InlineData(".edl", "text/plain")]
+ [InlineData(".txt", "text/plain")]
+ [InlineData(".vtt", "text/vtt")]
+ [InlineData(".3gp", "video/3gpp")]
+ [InlineData(".3g2", "video/3gpp2")]
+ [InlineData(".asf", "video/x-ms-asf")]
+ [InlineData(".avi", "video/x-msvideo")]
+ [InlineData(".flv", "video/x-flv")]
+ [InlineData(".mp4", "video/mp4")]
+ [InlineData(".m4v", "video/x-m4v")]
+ [InlineData(".mpegts", "video/mp2t")]
+ [InlineData(".mpg", "video/mpeg")]
+ [InlineData(".mkv", "video/x-matroska")]
+ [InlineData(".mov", "video/quicktime")]
+ [InlineData(".ogv", "video/ogg")]
+ [InlineData(".ts", "video/mp2t")]
+ [InlineData(".webm", "video/webm")]
+ [InlineData(".wmv", "video/x-ms-wmv")]
+ [InlineData(".aac", "audio/aac")]
+ [InlineData(".ac3", "audio/ac3")]
+ [InlineData(".ape", "audio/x-ape")]
+ [InlineData(".dsf", "audio/dsf")]
+ [InlineData(".dsp", "audio/dsp")]
+ [InlineData(".flac", "audio/flac")]
+ [InlineData(".m4a", "audio/mp4")]
+ [InlineData(".m4b", "audio/m4b")]
+ [InlineData(".mid", "audio/midi")]
+ [InlineData(".midi", "audio/midi")]
+ [InlineData(".mp3", "audio/mpeg")]
+ [InlineData(".oga", "audio/ogg")]
+ [InlineData(".ogg", "audio/ogg")]
+ [InlineData(".opus", "audio/ogg")]
+ [InlineData(".vorbis", "audio/vorbis")]
+ [InlineData(".wav", "audio/wav")]
+ [InlineData(".webma", "audio/webm")]
+ [InlineData(".wma", "audio/x-ms-wma")]
+ [InlineData(".wv", "audio/x-wavpack")]
+ [InlineData(".xsp", "audio/xsp")]
+ public void GetMimeType(string input, string expectedResult)
+ {
+ Assert.Equal(expectedResult, MimeTypes.GetMimeType(input, null));
+ }
+
+ [Theory]
+ [InlineData("application/epub+zip", ".epub")]
+ [InlineData("application/json", ".json")]
+ [InlineData("application/oebps-package+xml", ".opf")]
+ [InlineData("application/pdf", ".pdf")]
+ [InlineData("application/ttml+xml", ".ttml")]
+ [InlineData("application/vnd.amazon.ebook", ".azw")]
+ [InlineData("application/vnd.ms-fontobject", ".eot")]
+ [InlineData("application/vnd.rar", ".rar")]
+ [InlineData("application/wasm", ".wasm")]
+ [InlineData("application/x-7z-compressed", ".7z")]
+ [InlineData("application/x-cbz", ".cbz")]
+ [InlineData("application/x-javascript", ".js")]
+ [InlineData("application/x-mobipocket-ebook", ".mobi")]
+ [InlineData("application/x-mpegURL", ".m3u8")]
+ [InlineData("application/x-subrip", ".srt")]
+ [InlineData("application/xml", ".xml")]
+ [InlineData("application/zip", ".zip")]
+ [InlineData("audio/aac", ".aac")]
+ [InlineData("audio/ac3", ".ac3")]
+ [InlineData("audio/dsf", ".dsf")]
+ [InlineData("audio/dsp", ".dsp")]
+ [InlineData("audio/flac", ".flac")]
+ [InlineData("audio/m4b", ".m4b")]
+ [InlineData("audio/mp4", ".m4a")]
+ [InlineData("audio/vorbis", ".vorbis")]
+ [InlineData("audio/wav", ".wav")]
+ [InlineData("audio/x-aac", ".aac")]
+ [InlineData("audio/x-ape", ".ape")]
+ [InlineData("audio/x-ms-wma", ".wma")]
+ [InlineData("audio/x-wavpack", ".wv")]
+ [InlineData("audio/xsp", ".xsp")]
+ [InlineData("font/ttf", ".ttf")]
+ [InlineData("font/woff", ".woff")]
+ [InlineData("font/woff2", ".woff2")]
+ [InlineData("image/bmp", ".bmp")]
+ [InlineData("image/gif", ".gif")]
+ [InlineData("image/jpg", ".jpg")]
+ [InlineData("image/png", ".png")]
+ [InlineData("image/svg+xml", ".svg")]
+ [InlineData("image/tiff", ".tif")]
+ [InlineData("image/vnd.microsoft.icon", ".ico")]
+ [InlineData("image/webp", ".webp")]
+ [InlineData("image/x-png", ".png")]
+ [InlineData("text/css", ".css")]
+ [InlineData("text/csv", ".csv")]
+ [InlineData("text/plain", ".txt")]
+ [InlineData("text/rtf", ".rtf")]
+ [InlineData("text/vtt", ".vtt")]
+ [InlineData("text/x-ssa", ".ssa")]
+ [InlineData("video/3gpp", ".3gp")]
+ [InlineData("video/3gpp2", ".3g2")]
+ [InlineData("video/mp2t", ".ts")]
+ [InlineData("video/mp4", ".mp4")]
+ [InlineData("video/ogg", ".ogv")]
+ [InlineData("video/quicktime", ".mov")]
+ [InlineData("video/vnd.mpeg.dash.mpd", ".mpd")]
+ [InlineData("video/webm", ".webm")]
+ [InlineData("video/x-flv", ".flv")]
+ [InlineData("video/x-m4v", ".m4v")]
+ [InlineData("video/x-matroska", ".mkv")]
+ [InlineData("video/x-ms-asf", ".asf")]
+ [InlineData("video/x-ms-wmv", ".wmv")]
+ [InlineData("video/x-msvideo", ".avi")]
+ public void ToExtension(string input, string expectedResult)
+ {
+ Assert.Equal(expectedResult, MimeTypes.ToExtension(input));
+ }
+ }
+}