diff --git a/pkgs/mime/lib/src/extension.dart b/pkgs/mime/lib/src/extension.dart index 293449a0e0..759d97560d 100644 --- a/pkgs/mime/lib/src/extension.dart +++ b/pkgs/mime/lib/src/extension.dart @@ -14,33 +14,96 @@ import 'default_extension_map.dart'; /// Used by [extensionFromMime]. final Map _defaultMimeTypeMap = { for (var entry in defaultExtensionMap.entries) entry.value: entry.key, - 'application/msword': 'doc', - 'application/vnd.ms-excel': 'xls', - 'application/vnd.ms-powerpoint': 'ppt', - 'application/x-debian-package': 'deb', - 'application/xhtml+xml': 'xhtml', - 'application/xml': 'xml', - 'audio/x-aiff': 'aif', - 'audio/midi': 'mid', + // GENERATED by tool/generate_defaults.dart + 'application/epub+zip': 'epub', + 'application/lrc': 'lrc', + 'application/pgp-signature': 'pgp', + 'application/pkix-cert': 'cer', + 'application/rss+xml': 'rss', + 'application/sdp': 'sdp', + 'application/smil+xml': 'smil', + 'application/ttml+xml': 'ttml', + 'application/vnd.android.ota': 'ota', + 'application/vnd.apple.mpegurl': 'm3u8', + 'application/vnd.ms-pki.stl': 'stl', + 'application/vnd.ms-powerpoint': 'pot', + 'application/vnd.ms-wpl': 'wpl', + 'application/vnd.stardivision.impress': 'sdp', + 'application/vnd.stardivision.writer': 'vor', + 'application/vnd.youtube.yt': 'yt', + 'application/x-android-drm-fl': 'fl', + 'application/x-flac': 'flac', + 'application/x-font': 'pcf', + 'application/x-mobipocket-ebook': 'prc', + 'application/x-mpegurl': 'm3u', + 'application/x-pem-file': 'pem', + 'application/x-pkcs12': 'p12', + 'application/x-subrip': 'srt', + 'application/x-webarchive': 'webarchive', + 'application/x-webarchive-xml': 'webarchivexml', + 'application/x-wifi-config': 'xml', + 'application/x-x509-ca-cert': 'crt', + 'application/x-x509-server-cert': 'crt', + 'application/x-x509-user-cert': 'crt', + 'audio/3gpp': '3ga', + 'audio/aac': 'aac', + 'audio/aac-adts': 'aac', + 'audio/ac3': 'ac3', + 'audio/amr': 'amr', + 'audio/basic': 'snd', + 'audio/flac': 'flac', + 'audio/imelody': 'imy', + 'audio/midi': 'rtx', + 'audio/mobile-xmf': 'mxmf', 'audio/mp4': 'm4a', - 'audio/ogg': 'ogg', + 'audio/mpeg': 'mp3', + 'audio/mpegurl': 'm3u', + 'audio/sp-midi': 'smf', + 'audio/x-matroska': 'mka', + 'audio/x-mpeg': 'mp3', + 'audio/x-mpegurl': 'm3u', + 'audio/x-pn-realaudio': 'ra', + 'image/avif': 'avif', + 'image/bmp': 'bmp', + 'image/gif': 'gif', + 'image/heic': 'heic', + 'image/heic-sequence': 'heics', + 'image/heif': 'heif', + 'image/heif-sequence': 'heifs', + 'image/ico': 'cur', 'image/jpeg': 'jpg', - 'image/tiff': 'tif', - 'image/svg+xml': 'svg', - 'model/vrml': 'vrml', - 'text/calendar': 'ics', - 'text/html': 'html', - 'text/javascript': 'js', - 'text/markdown': 'md', + 'image/webp': 'webp', + 'image/x-adobe-dng': 'dng', + 'image/x-fuji-raf': 'raf', + 'image/x-icon': 'ico', + 'image/x-ms-bmp': 'bmp', + 'image/x-nikon-nrw': 'nrw', + 'image/x-panasonic-rw2': 'rw2', + 'image/x-pentax-pef': 'pef', + 'image/x-samsung-srw': 'srw', + 'image/x-sony-arw': 'arw', + 'text/comma-separated-values': 'csv', 'text/plain': 'txt', - 'text/sgml': 'sgml', - 'text/x-asm': 'asm', - 'text/x-c': 'c', - 'text/x-pascal': 'pas', - 'video/mp4': 'mp4', - 'video/mpeg': 'mpg', + 'text/rtf': 'rtf', + 'text/text': 'phps', + 'text/x-c++hdr': 'hpp', + 'text/x-c++src': 'cpp', + 'text/x-vcard': 'vcf', + 'text/xml': 'xml', + 'video/3gpp': '3gpp', + 'video/3gpp2': '3gpp2', + 'video/avi': 'avi', + 'video/m4v': 'm4v', + 'video/mp2p': 'mpeg', + 'video/mp2t': 'm2ts', + 'video/mp2ts': 'ts', + 'video/mp4': 'm4v', + 'video/mpeg': 'mpeg', 'video/quicktime': 'mov', + 'video/vnd.youtube.yt': 'yt', 'video/x-matroska': 'mkv', + 'video/x-webex': 'wrf', + // END GENERATED }; /// The default file extension for a given MIME type. diff --git a/pkgs/mime/tool/generate_defaults.dart b/pkgs/mime/tool/generate_defaults.dart new file mode 100644 index 0000000000..41244325bd --- /dev/null +++ b/pkgs/mime/tool/generate_defaults.dart @@ -0,0 +1,112 @@ +// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file +// 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 'dart:io'; + +const startMarker = '\n // GENERATED by tool/generate_defaults.dart\n'; +const endMarker = '\n // END GENERATED\n'; +const androidMimeTypesPath = '../third_party/preferred_extension.mime.types'; +const extensionLibraryPath = '../lib/src/extension.dart'; + +void main(List args) { + var dryRun = false; + var printUsage = false; + var hadInvalidArgument = false; + Uri? mimeTypesOverride; + for (var arg in args) { + if (arg == '-h' || arg == '--help') { + printUsage = true; + } else if (arg == '-n' || arg == '--dryrun' || arg == '--dry-run') { + dryRun = true; + } else if (!arg.startsWith('-') && mimeTypesOverride == null) { + mimeTypesOverride = Directory.current.uri.resolve(arg); + } else { + hadInvalidArgument = true; + stderr.writeln('Unexpected command-line argument: $arg'); + } + } + if (hadInvalidArgument) { + stderr.writeln(usage); + exit(1); + } else if (printUsage) { + stdout.writeln(usage); + exit(0); + } + final script = Platform.script; + final library = script.resolve(extensionLibraryPath); + final libraryFile = File.fromUri(library); + final librarySource = libraryFile.readAsStringSync(); + final int startTagOffset, endTagOffset; + if (librarySource.indexOf(startMarker) case >= 0 && final start) { + startTagOffset = start; + if (librarySource.indexOf(endMarker, start + startMarker.length) + case >= 0 && final end) { + endTagOffset = end; + } else { + stderr.writeln('Library file (${libraryFile.path})\n' + ' does not contain end marker: $endMarker'); + exit(1); + } + } else { + stderr.writeln('Library file (${libraryFile.path})\n' + ' does not contain start marker: $startMarker'); + exit(1); + } + + final androidDefaults = + mimeTypesOverride ?? script.resolve(androidMimeTypesPath); + final defaultsFile = File.fromUri(androidDefaults); + if (!defaultsFile.existsSync()) { + stderr.writeln('Cannot find file: ${androidDefaults.toFilePath()}'); + if (mimeTypesOverride == null) { + stderr.writeln('Download file from rx: \n' + // ignore: missing_whitespace_between_adjacent_strings + ' https://android.googlesource.com/platform/frameworks/' + 'base/+/main/mime/java-res/android.mime.types'); + } + exit(1); + } + final defaults = defaultsFile.readAsLinesSync(); + final mapping = {}; + for (var line in defaults) { + if (line.startsWith('#')) continue; + final parts = line.split(' '); + var mediaType = parts[0]; + var defaultExtension = parts[1]; + if (defaultExtension.startsWith('?')) { + defaultExtension = defaultExtension.substring(1); + } + if (mediaType.startsWith('?')) { + mediaType = mediaType.substring(1); + mapping[mediaType] ??= defaultExtension; + } else { + mapping[mediaType] = defaultExtension; + } + } + final sortedKeys = mapping.keys.toList()..sort(); + final newMapping = + [for (var key in sortedKeys) " '$key': '${mapping[key]}',"].join('\n'); + final newLibrarySource = librarySource.replaceRange( + startTagOffset + startMarker.length, endTagOffset, newMapping); + if (newLibrarySource != librarySource) { + if (!dryRun) { + libraryFile.writeAsStringSync(newLibrarySource); + stdout.writeln('Changes to mapping written to file.'); + } else { + stdout.writeln('Changes to mapping. (Not written as dry-run.)'); + } + } else { + stdout.writeln('No change to mapping'); + } +} + +const String usage = ''' +Usage: dart tool/generate_defaults.dart [-h|-n] [MIMETYPES] + +--help or -h : Print this usage information +--dry-run or -n : Don't write changes back to library +MIMETYPES : Path to file in the `mime.types` format + where the first extension for each media type + is the preferred extension. +''';