diff --git a/pkgs/mime/CHANGELOG.md b/pkgs/mime/CHANGELOG.md index fd5a24905c..849ab1ab36 100644 --- a/pkgs/mime/CHANGELOG.md +++ b/pkgs/mime/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.0.1 +* `extensionFromMime(String mimeType)` returns `mp3` instead of `mpga` for mime type `audio/mpeg`. +* `extensionFromMime(String mimeType)` Add default lookup for all mime type. + ## 2.0.0 * **[Breaking]** `extensionFromMime(String mimeType)` returns `null` instead of `mimeType` for an unknown mime type. diff --git a/pkgs/mime/doc/media_types.md b/pkgs/mime/doc/media_types.md index 5878c21bb5..7acb7dc449 100644 --- a/pkgs/mime/doc/media_types.md +++ b/pkgs/mime/doc/media_types.md @@ -601,7 +601,7 @@ Supported media types and file extensions. | `audio/basic` | `snd` | `au` | | `audio/midi` | `mid` | `kar`, `midi`, `rmi` | | `audio/mp4` | `m4a` | `m4b`, `mp4a` | -| `audio/mpeg` | `mpga` | `m2a`, `m3a`, `mp2`, `mp2a`, `mp3` | +| `audio/mpeg` | `mp3` | `m2a`, `m3a`, `mp2`, `mp2a`, `mpga` | | `audio/ogg` | `ogg` | `oga`, `spx` | | `audio/s3m` | `s3m` | | | `audio/silk` | `sil` | | diff --git a/pkgs/mime/lib/src/extension.dart b/pkgs/mime/lib/src/extension.dart index 293449a0e0..d2d1886325 100644 --- a/pkgs/mime/lib/src/extension.dart +++ b/pkgs/mime/lib/src/extension.dart @@ -2,6 +2,8 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. +import 'package:meta/meta.dart'; + import 'default_extension_map.dart'; /// Default extension for recognized MIME types. @@ -13,36 +15,150 @@ import 'default_extension_map.dart'; /// /// Used by [extensionFromMime]. final Map _defaultMimeTypeMap = { + ..._additionalMimeTypesForExistingExtensionsMap, for (var entry in defaultExtensionMap.entries) entry.value: entry.key, + ..._defaultMimeTypeFallbackMap, +}; + +/// A map for with the default file extensions for MIME types. +/// +/// setting default file extensions for MIME types, +/// which are having multiple extensions +/// +/// used by [_defaultMimeTypeMap] +const Map _defaultMimeTypeFallbackMap = { + 'application/inkml+xml': 'inkml', + 'application/mathematica': 'nb', + 'application/mp21': 'mp21', 'application/msword': 'doc', + 'application/octet-stream': 'so', + 'application/onenote': 'onetoc2', + 'application/pgp-signature': 'sig', + 'application/pkcs7-mime': 'p7m', + 'application/postscript': 'ps', + 'application/smil+xml': 'smil', + 'application/tei+xml': 'teicorpus', + 'application/vnd.acucorp': 'atc', + 'application/vnd.adobe.fxp': 'fxpl', + 'application/vnd.clonk.c4group': 'c4u', + 'application/vnd.dece.data': 'uvvf', + 'application/vnd.dece.ttml+xml': 'uvvt', + 'application/vnd.dece.unspecified': 'uvx', + 'application/vnd.dece.zip': 'uvz', + 'application/vnd.eszigno3+xml': 'et3', + 'application/vnd.fdsn.seed': 'seed', + 'application/vnd.framemaker': 'maker', + 'application/vnd.geometry-explorer': 'gre', + 'application/vnd.grafeq': 'gqs', + 'application/vnd.ibm.modcap': 'listafp', + 'application/vnd.iccprofile': 'icm', + 'application/vnd.intercon.formnet': 'xpx', + 'application/vnd.kahootz': 'ktz', + 'application/vnd.kde.kpresenter': 'kpt', + 'application/vnd.kde.kword': 'kwt', + 'application/vnd.kinar': 'knp', + 'application/vnd.koan': 'skt', 'application/vnd.ms-excel': 'xls', 'application/vnd.ms-powerpoint': 'ppt', + 'application/vnd.ms-project': 'mpt', + 'application/vnd.ms-works': 'wps', + 'application/vnd.nitf': 'ntf', + 'application/vnd.palm': 'pqa', + 'application/vnd.quark.quarkxpress': 'qxt', + 'application/vnd.simtech-mindmapper': 'twds', + 'application/vnd.solent.sdkm+xml': 'sdkm', + 'application/vnd.stardivision.writer': 'vor', + 'application/vnd.sus-calendar': 'susp', + 'application/vnd.symbian.install': 'sisx', + 'application/vnd.tcpdump.pcap': 'pcap', + 'application/vnd.ufdl': 'ufdl', + 'application/vnd.visio': 'vsw', + 'application/vnd.zul': 'zirz', + 'application/x-authorware-bin': 'x32', + 'application/x-blorb': 'blorb', + 'application/x-bzip2': 'bz2', + 'application/x-cbr': 'cbz', 'application/x-debian-package': 'deb', + 'application/x-director': 'w3d', + 'application/x-font-ttf': 'ttf', + 'application/x-font-type1': 'pfm', + 'application/x-lzh-compressed': 'lzh', + 'application/x-mobipocket-ebook': 'prc', + 'application/x-msdownload': 'msi', + 'application/x-msmediaview': 'mvb', + 'application/x-msmetafile': 'wmf', + 'application/x-netcdf': 'nc', + 'application/x-pkcs12': 'pfx', + 'application/x-pkcs7-certificates': 'spc', + 'application/x-texinfo': 'texinfo', + 'application/x-x509-ca-cert': 'der', + 'application/x-zmachine': 'z8', 'application/xhtml+xml': 'xhtml', 'application/xml': 'xml', - 'audio/x-aiff': 'aif', + 'application/xv+xml': 'xvml', + 'audio/basic': 'snd', 'audio/midi': 'mid', 'audio/mp4': 'm4a', + 'audio/mpeg': 'mp3', 'audio/ogg': 'ogg', + 'audio/vnd.dece.audio': 'uvva', + 'audio/x-aiff': 'aif', + 'audio/x-pn-realaudio': 'ram', 'image/jpeg': 'jpg', - 'image/tiff': 'tif', 'image/svg+xml': 'svg', + 'image/tiff': 'tif', + 'image/vnd.dece.graphic': 'uvvi', + 'image/vnd.djvu': 'djvu', + 'image/x-freehand': 'fhc', + 'image/x-pict': 'pic', + 'message/rfc822': 'mime', + 'model/iges': 'igs', + 'model/mesh': 'silo', 'model/vrml': 'vrml', + 'model/x3d+binary': 'x3dbz', + 'model/x3d+vrml': 'x3dvz', + 'model/x3d+xml': 'x3dz', 'text/calendar': 'ics', 'text/html': 'html', 'text/javascript': 'js', 'text/markdown': 'md', 'text/plain': 'txt', 'text/sgml': 'sgml', + 'text/troff': 'tr', + 'text/uri-list': 'urls', 'text/x-asm': 'asm', 'text/x-c': 'c', + 'text/x-fortran': 'for', 'text/x-pascal': 'pas', + 'video/jpm': 'jpm', + 'video/mj2': 'mjp2', 'video/mp4': 'mp4', 'video/mpeg': 'mpg', 'video/quicktime': 'mov', + 'video/vnd.dece.hd': 'uvvh', + 'video/vnd.dece.mobile': 'uvvm', + 'video/vnd.dece.pd': 'uvvp', + 'video/vnd.dece.sd': 'uvvs', + 'video/vnd.dece.video': 'uvvv', + 'video/vnd.mpegurl': 'mxu', + 'video/vnd.uvvu.mp4': 'uvvu', 'video/x-matroska': 'mkv', + 'video/x-ms-asf': 'asx', +}; + +/// Additional MIME types for existing extensions. +/// +/// used for additional mime types, used by the existing extensions +/// used by [_defaultMimeTypeMap] +const Map _additionalMimeTypesForExistingExtensionsMap = { + 'audio/wav': 'wav', }; +/// access [_defaultMimeTypeFallbackMap] for testing purposes +@visibleForTesting +const Map defaultMimeTypeFallbackMap = + _defaultMimeTypeFallbackMap; + /// The default file extension for a given MIME type. /// /// If [mimeType] has multiple associated extensions, diff --git a/pkgs/mime/pubspec.yaml b/pkgs/mime/pubspec.yaml index 31ac74885f..118ac0d3d5 100644 --- a/pkgs/mime/pubspec.yaml +++ b/pkgs/mime/pubspec.yaml @@ -1,5 +1,5 @@ name: mime -version: 2.0.0 +version: 2.0.1 description: >- Utilities for handling media (MIME) types, including determining a type from a file extension and file contents. @@ -18,3 +18,5 @@ environment: dev_dependencies: dart_flutter_team_lints: ^3.0.0 test: ^1.16.0 +dependencies: + meta: ^1.16.0 diff --git a/pkgs/mime/test/extension_test.dart b/pkgs/mime/test/extension_test.dart index 40dae6df5f..8f0fa070ed 100644 --- a/pkgs/mime/test/extension_test.dart +++ b/pkgs/mime/test/extension_test.dart @@ -3,6 +3,7 @@ // BSD-style license that can be found in the LICENSE file. import 'package:mime/mime.dart'; +import 'package:mime/src/default_extension_map.dart'; import 'package:test/test.dart'; void main() { @@ -20,6 +21,7 @@ void main() { expect(extensionFromMime('text/html'), equals('html')); expect(extensionFromMime('text/plain'), equals('txt')); expect(extensionFromMime('text/x-c'), equals('c')); + expect(extensionFromMime('audio/mpeg'), equals('mp3')); }); test('invalid-mime-type', () { @@ -30,4 +32,30 @@ void main() { test('unknown-mime-type', () { expect(extensionFromMime('application/to-be-invented'), isNull); }); + + test('fallback-mime-type-missing-implementations', () { + final mimeTypesUsedCounter = {}; + for (final mimeType in defaultExtensionMap.values) { + final current = mimeTypesUsedCounter[mimeType]; + if (current == null) { + mimeTypesUsedCounter[mimeType] = 1; + } else { + mimeTypesUsedCounter[mimeType] = current + 1; + } + } + final requiresOverride = mimeTypesUsedCounter.entries + .where((e) => e.value > 1) + .map((e) => e.key); + final missingOverrides = requiresOverride + .where((e) => !defaultMimeTypeFallbackMap.containsKey(e)); + expect(missingOverrides, isEmpty, + reason: 'missing overrides of mime types for $missingOverrides'); + }); + + test('overridden-type-not-in-map', () { + for (final mimeType in defaultMimeTypeFallbackMap.entries) { + expect(defaultExtensionMap[mimeType.value], mimeType.key, + reason: 'override is missing in original map'); + } + }); }