Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

More extensions to move/copy (.MP .MV .DNG and .CR2) and option to transform Pixel Motion Photo to .mp4 #384

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion bin/gpth.dart
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,10 @@ void main(List<String> arguments) async {
help: "Copy files instead of moving them.\n"
"This is usually slower, and uses extra space, "
"but doesn't break your input folder",
);
)
..addFlag(
'transform-pixel-mp',
help: 'Transform Pixel .MP or .MV extensions to ".mp4"');
final args = <String, dynamic>{};
try {
final res = parser.parse(arguments);
Expand Down Expand Up @@ -109,6 +112,8 @@ void main(List<String> arguments) async {
print('');
args['albums'] = await interactive.askAlbums();
print('');
args['transform-pixel-mp'] = await interactive.askTransformPixelMP();
print('');

// @Deprecated('Interactive unzipping is suspended for now!')
// // calculate approx space required for everything
Expand Down Expand Up @@ -355,6 +360,15 @@ void main(List<String> arguments) async {
print('Finding albums (this may take some time, dont worry :) ...');
findAlbums(media);

// Change Pixel Motion Photos extension to .mp4 using a list of Medias.
// This is done after the dates of files have been defined, and before
// the files are moved to the output folder, to avoid shortcuts/symlinks problems
if (args['transform-pixel-mp']) {
print('Changing .MP or .MV extensions to .mp4 (this may take some time) ...');
await changeMPExtensions(media, ".mp4");
}
print('');

/// #######################

/// ##### Copy/move files to actual output folder #####
Expand Down
22 changes: 22 additions & 0 deletions lib/interactive.dart
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,28 @@ Future<bool> askForCleanOutput() async {
}
}

Future<bool> askTransformPixelMP() async {
print('Pixel Motion Pictures are saved with the .MP or .MV '
'extensions. Do you want to change them to .mp4 '
'for better compatibility?');
print('[1] (default) - no, keep original extension');
print('[2] - yes, change extension to .mp4');
print('(Type 1 or 2 or press enter for default):');
final answer = await askForInt();
switch (answer) {
case '1':
case '':
print('Okay, will keep original extension');
return false;
case '2':
print('Okay, will change to mp4!');
return true;
default:
error('Invalid answer - try again');
return askTransformPixelMP();
}
}

/// Checks free space on disk and notifies user accordingly
@Deprecated('Interactive unzipping is suspended for now!')
Future<void> freeSpaceNotice(int required, Directory dir) async {
Expand Down
40 changes: 38 additions & 2 deletions lib/utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import 'package:gpth/interactive.dart' as interactive;
import 'package:mime/mime.dart';
import 'package:path/path.dart' as p;
import 'package:proper_filesize/proper_filesize.dart';
import 'package:unorm_dart/unorm_dart.dart' as unorm;

import 'media.dart';

Expand All @@ -30,28 +31,35 @@ extension X on Iterable<FileSystemEntity> {
/// Easy extension allowing you to filter for files that are photo or video
Iterable<File> wherePhotoVideo() => whereType<File>().where((e) {
final mime = lookupMimeType(e.path) ?? "";
final fileExtension = p.extension(e.path).toLowerCase();
return mime.startsWith('image/') ||
mime.startsWith('video/') ||
// https://github.com/TheLastGimbus/GooglePhotosTakeoutHelper/issues/223
// https://github.com/dart-lang/mime/issues/102
// 🙃🙃
mime == 'model/vnd.mts';
mime == 'model/vnd.mts'||
_moreExtensions.contains(fileExtension);
});
}

extension Y on Stream<FileSystemEntity> {
/// Easy extension allowing you to filter for files that are photo or video
Stream<File> wherePhotoVideo() => whereType<File>().where((e) {
final mime = lookupMimeType(e.path) ?? "";
final fileExtension = p.extension(e.path).toLowerCase();
return mime.startsWith('image/') ||
mime.startsWith('video/') ||
// https://github.com/TheLastGimbus/GooglePhotosTakeoutHelper/issues/223
// https://github.com/dart-lang/mime/issues/102
// 🙃🙃
mime == 'model/vnd.mts';
mime == 'model/vnd.mts'||
_moreExtensions.contains(fileExtension);
});
}

//Support raw formats (dng, cr2) and Pixel motion photos (mp, mv)
const _moreExtensions = ['.mp', '.mv', '.dng', '.cr2'];

extension Util on Stream {
Stream<T> whereType<T>() => where((e) => e is T).cast<T>();
}
Expand Down Expand Up @@ -133,3 +141,31 @@ extension Z on String {
return replaceRange(lastIndex, lastIndex + from.length, to);
}
}

Future<void> changeMPExtensions(List<Media> allMedias, String finalExtension) async {
int renamedCount = 0;
for (final m in allMedias) {
for (final entry in m.files.entries) {
final file = entry.value;
final ext = p.extension(file.path).toLowerCase();
if (ext == '.mv' || ext == '.mp') {
final originalName = p.basenameWithoutExtension(file.path);
final normalizedName = unorm.nfc(originalName);

final newName = '$normalizedName$finalExtension';
if (newName != normalizedName) {
final newPath = p.join(p.dirname(file.path), newName);
// Rename file and update reference in map
try {
final newFile = await file.rename(newPath);
m.files[entry.key] = newFile;
renamedCount++;
} on FileSystemException catch (e) {
print('[Error] Error changing extension to $finalExtension -> ${file.path}: ${e.message}');
}
}
}
}
}
print('Successfully changed Pixel Motion Photos files extensions (change it to $finalExtension): $renamedCount');
}