Skip to content

Commit

Permalink
v1.1
Browse files Browse the repository at this point in the history
Fixes:

- Issue #16: Trailing command is lost.

- Issue #17: Array error accessing manifest title element.

- Issue #18: Windows GUI is not longer easy installable due to expired publishing certificate. Certificate now expires in 2034.

- Issue #19: Use sidecar photoTakenTime element for file and directory naming.

- Issue #20: Ignore json files that are not image sidecars. metadata.json, print-subscriptions.json, shared_album_comments.json,
  user-generated-memory-titles.json, etc. are ignored.

- Issue #20: Deleted photos/videos are extracted.

Enhancements:

- Migrated to .net 7 and refreshed gui project structure

- Added option to ignore (default) or extract deleted photos files in the Bin folder.

- Improved error detail reporting. Display alert details link only if alert has details to display.

- Replaced GUI app icon and splash overlay.

- Added additional readme content and a screenshot.

- Include PubXml files for this solution since they contain no secrets

Note: This is a source-only release as publishing the gui app on Windows is broken for unknown reasons. See issue #22.
  • Loading branch information
andyjohnson0 committed Jan 9, 2024
2 parents cdd34d4 + e61e16e commit 2a6123a
Show file tree
Hide file tree
Showing 37 changed files with 453 additions and 390 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Expand Up @@ -187,7 +187,7 @@ publish/
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
#*.pubxml
*.publishproj

# Microsoft Azure Web App publish settings. Comment the next line if you want to
Expand Down
35 changes: 35 additions & 0 deletions CHANGELOG.md
@@ -1,5 +1,40 @@
#Change Log

##2024-01-08 v1.1

Fixes:

- Issue #16: Trailing command is lost.

- Issue #17: Array error accessing manifest title element.

- Issue #18: Windows GUI is not longer easy installable due to expired publishing certificate. Certificate now expires in 2034.

- Issue #19: Use sidecar photoTakenTime element for file and directory naming.

- Issue #20: Ignore json files that are not image sidecars. metadata.json, print-subscriptions.json, shared_album_comments.json,
user-generated-memory-titles.json, etc. are ignored.

- Issue #20: Deleted photos/videos are extracted.

Enhancements:

- Migrated to .net 7 and refreshed gui project structure

- Added option to ignore (default) or extract deleted photos files in the Bin folder.

- Improved error detail reporting. Display alert details link only if alert has details to display.

- Replaced GUI app icon and splash overlay.

- Added additional readme content and a screenshot.

- Include PubXml files for this solution since they contain no secrets

Note: This is a source-only release as publishing the gui app on Windows is broken for unknown reasons. See issue #22.



##2023-02-01 v1.0

v1.0 release!
Expand Down
29 changes: 16 additions & 13 deletions README.md
@@ -1,9 +1,14 @@
# Takout Extractor

Extracts the contents of a [Google Takeout](https://takeout.google.com/) archive - re-organising it, adding missing metadata, and
applying a uniform file naming convention.
applying a uniform file naming convention. Runs on Windows and MacOS. Requires .NET 7 or later.

This software currently processes only photo and video files. It is planned to add support for other media types in the future.
Gui and command-line implementations are provided - see the TakeoutExtractor.Gui and TakeoutExtractor.Cli projects respectively.
Releases include the Gui for Windows and MacOS, and the cli for Windows only.

![Takeout Extractor GUI screenshot](screenshot-gui-small.png)

This software currently extracts only photo and video files. It is planned to add support for other Takeout media types in the future.

- *Photos and Videos* Image files in a Google takeout dataset have inconsistent naming. They also do not contain exif timestamps -
although, confusingly, they do contain other metadata such as location information and camera settings. This software builds a uniformaly
Expand All @@ -14,22 +19,21 @@ named copy of the image and video files in a takeout dataset and restores their

1. Build the solution in Visual Studio 2022

2. The Gui-extractor, tex-gui.exe, will be found in `TakeoutExtractorGui\bin\Release\net6.0\`.

3. The command-line extractor, `tex.exe`, will be found in `TakeoutExtractorCli\bin\Release\net6.0\`.
2. The command-line extractor, `tex.exe`, will be found in `TakeoutExtractorCli\bin\Release\net6.0\`.
Run `tex /h` for help.



## Built With

- Visual Studio 2022, v17.3 or later for .net Maui support. .net 6.0, with nullable reference type checking enabled
- Fork of [ExifLibNet](https://www.nuget.org/packages/ExifLibNet) with bug-fixes, included in the `ThirdParty` directory with source available at https://github.com/andyjohnson0/exiflibrary.
- Visual Studio 2022. Maui-based gui currently requires VS2022 7.3.0 preview 2.0 or later.
- .net 7.0, with nullable reference type checking enabled
- A fork of [ExifLibNet](https://www.nuget.org/packages/ExifLibNet) v2.1.4 with additional fixes, available at https://github.com/andyjohnson0/exiflibrary.
A pre-built dll is included in the ThirdParty directory.


## Author

Andeew Johnson | [github.com/andyjohnson0](https://github.com/andyjohnson0) | https://andyjohnson.uk
Andrew Johnson | [github.com/andyjohnson0](https://github.com/andyjohnson0) | https://andyjohnson.uk


## Licence
Expand All @@ -38,11 +42,10 @@ Except for third-party elements that are licened separately, this project is lic

The folder picker implementations used in the gui project are based on code from [MauiFolderPickerSample](https://github.com/jfversluis/MauiFolderPickerSample)
and https://blog.verslu.is/maui/folder-picker-with-dotnet-maui/ by Gerald Versluis.
Licenced as [Attribution-ShareAlike 4.0 International (CC BY-SA 4.0)] (https://creativecommons.org/licenses/by-sa/4.0/) by the original author.
Licenced as [Attribution-ShareAlike 4.0 International (CC BY-SA 4.0)](https://creativecommons.org/licenses/by-sa/4.0/) by the original author.

The [shovel icon](https://github.com/frexy/glyph-iconset/blob/master/svg/si-glyph-shovel.svg) used by the gui project is from (SmartIcon's)[https://smarticons.co/]
excellent *Gylph* icon set at https://glyph.smarticons.co/ and https://github.com/frexy/glyph-iconset.
Licenced as [Attribution-ShareAlike 4.0 International (CC BY-SA 4.0)](http://creativecommons.org/licenses/by-sa/4.0/) by the original author.
The shovel icon used by the GUI project is from [svgrepo.com](https://www.svgrepo.com) and is licenced as
[CC0](https://creativecommons.org/publicdomain/zero/1.0/) / Public Domain by svgrepo.


## Future Enhancements, TODOs, and known bugs
Expand Down
4 changes: 2 additions & 2 deletions TakeoutExtractor.Cli.Tests/TakeoutExtractor.Cli.Tests.csproj
@@ -1,12 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
<RootNamespace>uk.andyjohnson.TakeoutExtractor.Cli.Tests</RootNamespace>
<Version>1.0.0</Version>
<Version>1.1.0</Version>
</PropertyGroup>

<ItemGroup>
Expand Down
1 change: 1 addition & 0 deletions TakeoutExtractor.Cli/CommandLine.cs
Expand Up @@ -56,6 +56,7 @@ public CommandLine(string[] args)
if (commands.Contains(arg))
{
currentCommand = arg;
commandToArgs.Add(currentCommand, new List<string>());
}
else
{
Expand Down
7 changes: 5 additions & 2 deletions TakeoutExtractor.Cli/Program.cs
Expand Up @@ -61,7 +61,8 @@ static async Task<int> Main(string[] args)
UpdateExif = kvp.Value.GetArgBool("ux", defaultValue: PhotoOptions.Defaults.UpdateExif),
OutputDirOrganisation = kvp.Value.GetArgEnum<PhotoDirOrganisation>("fd",
new string?[] { null, "y", "ym", "ymd" },
defaultValue: PhotoOptions.Defaults.OutputDirOrganisation)
defaultValue: PhotoOptions.Defaults.OutputDirOrganisation),
ExtractDeletedFiles = kvp.Value.GetArgBool("xd", defaultValue: false)
};
mediaOptions.Add(opt);
break;
Expand Down Expand Up @@ -95,7 +96,7 @@ static async Task<int> Main(string[] args)
Console.WriteLine($"{errorCount} error, {warningCount} warning, {infoCount} information");
foreach(var alert in alerts)
{
await alert.WriteAsync(Console.Out);
alert.Write(Console.Out);
}

// All done
Expand Down Expand Up @@ -179,6 +180,8 @@ private static void ShowHelp()
Console.WriteLine(" Create subdirectories for year, year and month, or year and month and day. Default: none.");
Console.WriteLine(" -ux true | false");
Console.WriteLine(" Update edited EXIF information in output files. Default: true.");
Console.WriteLine(" -xd true | false");
Console.WriteLine(" Extract the deleted files in the Bin directory. Default: false.");
Console.WriteLine();
Console.WriteLine("(end)");
Console.WriteLine();
Expand Down
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
https://go.microsoft.com/fwlink/?LinkID=208121.
-->
<Project>
<PropertyGroup>
<Configuration>Release</Configuration>
<Platform>Any CPU</Platform>
<PublishDir>bin\Release\net6.0\publish\0.8</PublishDir>
<PublishProtocol>FileSystem</PublishProtocol>
<_TargetId>Folder</_TargetId>
</PropertyGroup>
</Project>
4 changes: 2 additions & 2 deletions TakeoutExtractor.Cli/TakeoutExtractor.Cli.csproj
Expand Up @@ -2,11 +2,11 @@

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<RootNamespace>uk.andyjohnson.TakeoutExtractor.Cli</RootNamespace>
<Version>1.0.0</Version>
<Version>1.1.0</Version>
<AssemblyName>tex</AssemblyName>
</PropertyGroup>

Expand Down
1 change: 1 addition & 0 deletions TakeoutExtractor.Gui/AlertsPage.xaml
Expand Up @@ -74,6 +74,7 @@
<Label
Grid.Column="3"
Text="Details"
IsVisible="{Binding Path=HasFullDescription}"
TextColor="{AppThemeBinding Light={StaticResource Blue}, Dark={StaticResource OffWhite}}"
TextDecorations="Underline">
<Label.GestureRecognizers>
Expand Down
17 changes: 14 additions & 3 deletions TakeoutExtractor.Gui/MainPage.xaml
Expand Up @@ -183,7 +183,7 @@
<CheckBox
Grid.Row="2" Grid.Column="1"
x:Name="PhotosUpdateExifCbx"
SemanticProperties.Hint="Update edited EXIF information"
SemanticProperties.Hint="Update edited EXIF information (photos only)"
HorizontalOptions="Start" VerticalOptions="Center"/>

<Label
Expand All @@ -194,7 +194,7 @@
Grid.Row="3" Grid.Column="1"
x:Name="PhotoFileOrganisationPicker"
HorizontalOptions="Start" VerticalOptions="Center"
SemanticProperties.Hint="Organise extracted photos based on whether original or edited">
SemanticProperties.Hint="Organise extracted photos and videos based on whether original or edited">
<Picker.ItemsSource>
<x:Array Type="{x:Type x:String}">
<x:String>Latest version only</x:String>
Expand All @@ -214,7 +214,7 @@
<Picker
Grid.Row="4" Grid.Column="1"
x:Name="PhotosSubdirOrganisationPicker"
SemanticProperties.Hint="Organise extracted photos in folders based on create time"
SemanticProperties.Hint="Organise extracted photos and videos in folders based on create time"
HorizontalOptions="Start" VerticalOptions="Center">
<Picker.ItemsSource>
<x:Array Type="{x:Type x:String}">
Expand All @@ -225,6 +225,17 @@
</x:Array>
</Picker.ItemsSource>
</Picker>

<Label
Grid.Row="5" Grid.Column="0"
Text="Extract deleted files"
HorizontalOptions="End" VerticalOptions="Center" />
<CheckBox
Grid.Row="5" Grid.Column="1"
x:Name="PhotosExtractDeletedCbx"
SemanticProperties.Hint="Extract deleted photos and videos from the bin folder"
HorizontalOptions="Start" VerticalOptions="Center"/>

</Grid>
</VerticalStackLayout>
</Border>
Expand Down
3 changes: 2 additions & 1 deletion TakeoutExtractor.Gui/MainPage.xaml.cs
Expand Up @@ -179,7 +179,8 @@ void OnPhotosExtractChanged(object sender, CheckedChangedEventArgs e)
OutputFileNameTimeKind = Enum.Parse<DateTimeKind>(PhotosFileNameTimeKindPicker.SelectedItem.ToString()!, true),
UpdateExif = PhotosUpdateExifCbx.IsChecked,
OutputFileVersionOrganisation = (PhotoFileVersionOrganisation)PhotoFileOrganisationPicker.SelectedIndex,
OutputDirOrganisation = (PhotoDirOrganisation)PhotosSubdirOrganisationPicker.SelectedIndex
OutputDirOrganisation = (PhotoDirOrganisation)PhotosSubdirOrganisationPicker.SelectedIndex,
ExtractDeletedFiles = PhotosExtractDeletedCbx.IsChecked
};
mediaOptions.Add(photoOptions);
}
Expand Down
9 changes: 5 additions & 4 deletions TakeoutExtractor.Gui/Platforms/Android/MainActivity.cs
Expand Up @@ -2,9 +2,10 @@
using Android.Content.PM;
using Android.OS;

namespace uk.andyjohnson.TakeoutExtractor.Gui;

[Activity(Theme = "@style/Maui.SplashTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize | ConfigChanges.Density)]
public class MainActivity : MauiAppCompatActivity
namespace TakeoutExtractor.Gui
{
[Activity(Theme = "@style/Maui.SplashTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize | ConfigChanges.Density)]
public class MainActivity : MauiAppCompatActivity
{
}
}
19 changes: 10 additions & 9 deletions TakeoutExtractor.Gui/Platforms/Android/MainApplication.cs
@@ -1,15 +1,16 @@
using Android.App;
using Android.Runtime;

namespace uk.andyjohnson.TakeoutExtractor.Gui;

[Application]
public class MainApplication : MauiApplication
namespace uk.andyjohnson.TakeoutExtractor.Gui
{
public MainApplication(IntPtr handle, JniHandleOwnership ownership)
: base(handle, ownership)
{
}
[Application]
public class MainApplication : MauiApplication
{
public MainApplication(IntPtr handle, JniHandleOwnership ownership)
: base(handle, ownership)
{
}

protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
}
}
17 changes: 17 additions & 0 deletions TakeoutExtractor.Gui/Platforms/Tizen/Main.cs
@@ -0,0 +1,17 @@
using Microsoft.Maui;
using Microsoft.Maui.Hosting;
using System;

namespace TakeoutExtractor.Gui
{
internal class Program : MauiApplication
{
protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();

static void Main(string[] args)
{
var app = new Program();
app.Run(args);
}
}
}
15 changes: 15 additions & 0 deletions TakeoutExtractor.Gui/Platforms/Tizen/tizen-manifest.xml
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="maui-application-id-placeholder" version="0.0.0" api-version="7" xmlns="http://tizen.org/ns/packages">
<profile name="common" />
<ui-application appid="maui-application-id-placeholder" exec="TakeoutExtractor.Gui.dll" multiple="false" nodisplay="false" taskmanage="true" type="dotnet" launch_mode="single">
<label>maui-application-title-placeholder</label>
<icon>maui-appicon-placeholder</icon>
<metadata key="http://tizen.org/metadata/prefer_dotnet_aot" value="true" />
</ui-application>
<shortcut-list />
<privileges>
<privilege>http://tizen.org/privilege/internet</privilege>
</privileges>
<dependencies />
<provides-appdefined-privileges />
</manifest>
5 changes: 4 additions & 1 deletion TakeoutExtractor.Gui/Platforms/Windows/Package.appxmanifest
Expand Up @@ -2,10 +2,13 @@
<Package
xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest"
xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
IgnorableNamespaces="uap rescap">

<Identity Name="maui-package-name-placeholder" Publisher="CN=Andrew Johnson, O=Andrew Johnson, C=GB" Version="1.0.0.0" />
<Identity Name="maui-package-name-placeholder" Publisher="CN=Andrew Johnson, O=Andrew Johnson, C=GB" Version="1.1.0.0" />

<mp:PhoneIdentity PhoneProductId="450A1259-7DD2-4092-857B-218C6F9553A1" PhonePublisherId="00000000-0000-0000-0000-000000000000"/>

<Properties>
<DisplayName>$placeholder$</DisplayName>
Expand Down
11 changes: 6 additions & 5 deletions TakeoutExtractor.Gui/Platforms/iOS/AppDelegate.cs
@@ -1,9 +1,10 @@
using Foundation;

namespace uk.andyjohnson.TakeoutExtractor.Gui;

[Register("AppDelegate")]
public class AppDelegate : MauiUIApplicationDelegate
namespace uk.andyjohnson.TakeoutExtractor.Gui
{
protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
[Register("AppDelegate")]
public class AppDelegate : MauiUIApplicationDelegate
{
protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
}
}
21 changes: 11 additions & 10 deletions TakeoutExtractor.Gui/Platforms/iOS/Program.cs
@@ -1,15 +1,16 @@
using ObjCRuntime;
using UIKit;

namespace uk.andyjohnson.TakeoutExtractor.Gui;

public class Program
namespace uk.andyjohnson.TakeoutExtractor.Gui
{
// This is the main entry point of the application.
static void Main(string[] args)
{
// if you want to use a different Application Delegate class from "AppDelegate"
// you can specify it here.
UIApplication.Main(args, null, typeof(AppDelegate));
}
public class Program
{
// This is the main entry point of the application.
static void Main(string[] args)
{
// if you want to use a different Application Delegate class from "AppDelegate"
// you can specify it here.
UIApplication.Main(args, null, typeof(AppDelegate));
}
}
}

0 comments on commit 2a6123a

Please sign in to comment.