Browse Source

Merge pull request #6941 from holahmeds/mime-type

Use MimeTypes package to determine MIME type
Cody Robibero 3 years ago
parent
commit
707e5bab97

+ 4 - 0
MediaBrowser.Model/MediaBrowser.Model.csproj

@@ -31,6 +31,10 @@
   <ItemGroup>
   <ItemGroup>
     <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All" />
     <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All" />
     <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="6.0.0" />
     <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.Globalization" Version="4.3.0" />
     <PackageReference Include="System.Text.Json" Version="6.0.0" />
     <PackageReference Include="System.Text.Json" Version="6.0.0" />
   </ItemGroup>
   </ItemGroup>

+ 50 - 99
MediaBrowser.Model/Net/MimeTypes.cs

@@ -12,6 +12,15 @@ namespace MediaBrowser.Model.Net
     /// <summary>
     /// <summary>
     /// Class MimeTypes.
     /// Class MimeTypes.
     /// </summary>
     /// </summary>
+    ///
+    /// <remarks>
+    /// For more information on MIME types:
+    /// <list type="bullet">
+    ///     <item>http://en.wikipedia.org/wiki/Internet_media_type</item>
+    ///     <item>https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types</item>
+    ///     <item>http://www.iana.org/assignments/media-types/media-types.xhtml</item>
+    /// </list>
+    /// </remarks>
     public static class MimeTypes
     public static class MimeTypes
     {
     {
         /// <summary>
         /// <summary>
@@ -50,81 +59,26 @@ namespace MediaBrowser.Model.Net
             ".wtv",
             ".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)
         private static readonly Dictionary<string, string> _mimeTypeLookup = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
         {
         {
             // Type application
             // Type application
-            { ".7z", "application/x-7z-compressed" },
-            { ".azw", "application/vnd.amazon.ebook" },
             { ".azw3", "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
             // 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" },
             { ".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
             // Type text
             { ".ass", "text/x-ssa" },
             { ".ass", "text/x-ssa" },
             { ".ssa", "text/x-ssa" },
             { ".ssa", "text/x-ssa" },
-            { ".css", "text/css" },
-            { ".csv", "text/csv" },
             { ".edl", "text/plain" },
             { ".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
             // 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" },
             { ".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
             // Type audio
             { ".aac", "audio/aac" },
             { ".aac", "audio/aac" },
@@ -133,37 +87,47 @@ namespace MediaBrowser.Model.Net
             { ".dsf", "audio/dsf" },
             { ".dsf", "audio/dsf" },
             { ".dsp", "audio/dsp" },
             { ".dsp", "audio/dsp" },
             { ".flac", "audio/flac" },
             { ".flac", "audio/flac" },
-            { ".m4a", "audio/mp4" },
             { ".m4b", "audio/m4b" },
             { ".m4b", "audio/m4b" },
-            { ".mid", "audio/midi" },
-            { ".midi", "audio/midi" },
             { ".mp3", "audio/mpeg" },
             { ".mp3", "audio/mpeg" },
-            { ".oga", "audio/ogg" },
-            { ".ogg", "audio/ogg" },
-            { ".opus", "audio/ogg" },
             { ".vorbis", "audio/vorbis" },
             { ".vorbis", "audio/vorbis" },
-            { ".wav", "audio/wav" },
             { ".webma", "audio/webm" },
             { ".webma", "audio/webm" },
-            { ".wma", "audio/x-ms-wma" },
             { ".wv", "audio/x-wavpack" },
             { ".wv", "audio/x-wavpack" },
             { ".xsp", "audio/xsp" },
             { ".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" },
 
 
-            dict["image/jpg"] = ".jpg";
-            dict["image/x-png"] = ".png";
+            // 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["audio/x-aac"] = ".aac";
+            // Type image
+            { "image/jpg", ".jpg" },
+            { "image/x-png", ".png" },
 
 
-            return dict;
-        }
+            // Type text
+            { "text/plain", ".txt" },
+            { "text/rtf", ".rtf" },
+            { "text/x-ssa", ".ssa" },
+
+            // Type video
+            { "video/vnd.mpeg.dash.mpd", ".mpd" },
+            { "video/x-matroska", ".mkv" },
+        };
 
 
         public static string GetMimeType(string path) => GetMimeType(path, "application/octet-stream");
         public static string GetMimeType(string path) => GetMimeType(path, "application/octet-stream");
 
 
@@ -188,29 +152,15 @@ namespace MediaBrowser.Model.Net
                 return result;
                 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))
+            if (Model.MimeTypes.TryGetMimeType(filename, out var mimeType))
             {
             {
-                return "text/html; charset=UTF-8";
+                return mimeType;
             }
             }
 
 
-            if (string.Equals(ext, ".log", StringComparison.OrdinalIgnoreCase)
-                || string.Equals(ext, ".srt", StringComparison.OrdinalIgnoreCase))
-            {
-                return "text/plain";
-            }
-
-            // 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;
             return defaultValue;
@@ -231,7 +181,8 @@ namespace MediaBrowser.Model.Net
                 return result;
                 return result;
             }
             }
 
 
-            return null;
+            var extension = Model.MimeTypes.GetMimeTypeExtensions(mimeType).FirstOrDefault();
+            return string.IsNullOrEmpty(extension) ? null : "." + extension;
         }
         }
     }
     }
 }
 }

+ 164 - 0
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_Valid_ReturnsCorrectResult(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_Valid_ReturnsCorrectResult(string input, string expectedResult)
+        {
+            Assert.Equal(expectedResult, MimeTypes.ToExtension(input));
+        }
+    }
+}