media_organizer 1.20260802.1152033
media_organizer: ^1.20260802.1152033 copied to clipboard
Normalize photos and videos into self-describing, sortable, dedup-friendly filenames (datetime · place · size · checksum). CLI + library, cross-platform.
1.20260802.1152033 #
The filename rule becomes the package's shared contract, importable without
dart:io.
-
New
package:media_organizer/media_naming.dart— the naming contract on its own, with zero imports. Flutter apps (and anything sandboxed or web-bound) can now depend on the rule without pulling in the IO layer. -
New
MediaUploadNameCodec/MediaUploadNameComponents— encode, parse andcontentKeyOffor<YYYYMMDDTHHmmss>[_<place>]_<sizeBytes>_<md5-8>.<ext>, pinned bytest/fixtures/media_filename_contract.json. That fixture is now the single source of truth for every consumer, including a Kotlin parser in another repo that cannot share Dart code. Change the fixtures first when changing the rules. -
MediaFilename.parsenow delegates to that codec, which fixes three ways it disagreed with the contract:20261307T143022_123_abcdef01.jpg(month 13) used to parse, becauseDateTime()silently rolls an out-of-range month into the next year. Hours ≥ 24, minutes and seconds ≥ 60 had the same problem. All are rejected now.- a size token longer than 15 digits, or a negative one, used to be accepted.
- the parse is otherwise unchanged, so normal names behave exactly as before.
Stricter parsing means a malformed name that used to yield a (wrong)
MediaNamenow yieldsnull, and the organizer treats it as not-yet-normalized. -
MediaFilename.buildis unchanged and keeps its filesystem-friendlier place policy (New York→New-York).MediaUploadNameCodec.encodekeeps spaces and underscores instead, for pipelines that must match a corpus already in the wild. Both emit names the shared parser accepts, and dedup keys on<size>_<md5-8>alone, never on the place — so the two policies can coexist in one folder without breaking dedup.
1.20260615.1205955 #
Initial release. Versions follow 1.YYYYMMDD.1HHmmss; mediaOrganizerVersion
(printed by media_organizer --version) and pubspec.yaml are kept in sync by
tool/set_version.dart.
MediaFilename— build/parse<datetime>[_<place>]_<size>_<md5-8>.<ext>.MediaOrganizer— scan a folder, probe capture time + GPS, checksum, dedup by content key, rename into the scheme, copy/move to an output folder (dry-run + idempotent re-runs).- Injectable
MediaProbe(defaultCompositeMediaProbe: image EXIF +Mp4MediaProbefor videomoovtime/GPS + mtime fallback),LocationResolver(default none),MediaTranscoder(default passthrough), andMediaFileSystem(defaultLocalFileSystem, optionallyRandomAccessReader) — keeps the core pure, IO-agnostic, and cross-platform. OfflineGeocoder+GeoPlace— aLocationResolverthat maps GPS to the nearest place name fully offline (haversine, no API key). Data-free: you supply the place list (e.g. from GeoNames / immich-geodata). Examples:example/offline_geocoder_example.dart(GeoNames) andexample/immich_geodata_example.dart(zh-TW localized immich-geodata CSVs).Mp4MediaProbe— pure-Dart MP4/MOV capture time + GPS from themoovbox (Applecreationdate,mvhd1904 time, ISO-6709 location); seeks pastmdatviaRandomAccessReaderinstead of reading the whole file.- Verified copies:
verify(forced on withmove) re-hashes each written file and aborts that item on mismatch, so a corrupt copy never deletes an original. - Progress + cancellation:
onProgressper-file callback and acancelledpredicate onrun(); the CLI shows a live[i/total]line and stops cleanly on Ctrl-C. FfmpegTranscoder— opt-in video re-encode to 1080p H.264 + faststart, withisAvailable()detection and a per-OSinstallHint()when ffmpeg is missing.UploadTarget+MediaUploader— push an organize run to a cloud sink (Google Drive / S3 / SFTP), idempotent via the same content key (NAS needs no target — organize straight into the mount). Core stays SDK-free; targets are injectable and faked in tests.- CLI:
media_organizer organize -i <input> -o <output> [--move] [--verify] [--dry-run] [-r] [--progress] [--transcode] [--ffmpeg <path>], plusmedia_organizer stats <folder>andmedia_organizer --version.