Skip to content

Instantly share code, notes, and snippets.

@jkitching
Created May 27, 2024 07:31
Show Gist options
  • Save jkitching/3fa5a0c238fa825278db83acd05d7742 to your computer and use it in GitHub Desktop.
Save jkitching/3fa5a0c238fa825278db83acd05d7742 to your computer and use it in GitHub Desktop.
Combine a photo (HEIC or JPEG) and a video into a file which Google Photos will recognize as a motion photo
#!/bin/bash
# Inline XMP data
inline_xmp='<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.1.0-jc003">
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<rdf:Description rdf:about=""
xmlns:GCamera="http://ns.google.com/photos/1.0/camera/"
xmlns:Container="http://ns.google.com/photos/1.0/container/"
xmlns:Item="http://ns.google.com/photos/1.0/container/item/"
xmlns:xmpNote="http://ns.adobe.com/xmp/note/"
GCamera:MotionPhoto="1"
GCamera:MotionPhotoVersion="1"
GCamera:MotionPhotoPresentationTimestampUs="0">
<Container:Directory>
<rdf:Seq>
<rdf:li rdf:parseType="Resource">
<Container:Item
Item:Mime="image/jpeg"
Item:Semantic="Primary"
Item:Length="0"
Item:Padding="0"/>
</rdf:li>
<rdf:li rdf:parseType="Resource">
<Container:Item
Item:Mime="video/mp4"
Item:Semantic="MotionPhoto"
Item:Length="0"
Item:Padding="0"/>
</rdf:li>
</rdf:Seq>
</Container:Directory>
</rdf:Description>
</rdf:RDF>
</x:xmpmeta>'
# Check if both photo and video files are provided and exist
if [ "$#" -lt 2 ] || [ ! -e "$1" ] || [ ! -e "$2" ]; then
echo "Usage: $0 <photo_file> <video_file> [<output_file>]"
echo "Both files must be provided and exist."
exit 1
fi
temporary_file=$(mktemp /tmp/combined_image_XXXXXX.jpg)
# Check if the input file is in HEIC format
if [[ $(file -b --mime-type "$1") == "image/heic" ]]; then
# Convert HEIC to JPEG using the "convert" command and save to a temporary file
convert "$1" "$temporary_file"
else
# If not HEIC, copy the JPEG to the temporary file
cp "$1" "$temporary_file"
fi
# Create a temporary file for XMP data
xmp_file=$(mktemp /tmp/xmp_data_XXXXXX.xml)
# Write inline XMP data to the temporary XMP file
echo "$inline_xmp" > "$xmp_file"
# Check for existing XMP data and print a warning to stderr
existing_xmp_data=$(exiftool -a -xmp "$temporary_file" 2>&1)
if [ -n "$existing_xmp_data" ]; then
echo "Warning: Existing XMP data found in $1. It will be replaced." >&2
fi
# Drop all XMP data in the JPEG file and add new XMP data
exiftool -q -XMP:all= "$temporary_file"
exiftool -q "-xmp<=$xmp_file" "$temporary_file"
# Determine output destination (stdout or specified file)
output_file="/dev/stdout"
if [ "$#" -eq 3 ]; then
output_file="$3"
fi
# Concatenate the temporary file with XMP and the video file together and write to stdout
cat "$temporary_file" "$2" > "$output_file"
# Clean up temporary files
rm -f "$temporary_file" "$xmp_file"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment