# motionphoto
Python library for creating Google and Samsung compatible Motion Photos.
Most notably this can be used to convert Live Photos downloaded from iCloud
(where each photo comes as a separate image and video file) into Motion Photos that users
with Android can use.
## Table of Contents
<!--ts-->
* [Installation](#installation)
* [Usage](#usage)
* [Motion Photo Format](#motion-photo-format)
* [Google](#google)
* [Samsung](#samsung)
* [Future](#future)
<!--te-->
## Installation
Linux/MacOS:
```shell
$ python3 -m pip install motionphoto
```
Windows:
```shell
$ py -m pip install motionphoto
```
This library has a dependency on `exiftool`.
Instructions for installing it can be found [here](https://exiftool.org/install.html) and it must be available on
`PATH`, or it can be installed with a package manager.
## Usage
`motionphoto -i <imagePath> -v <videoPath> -m <outputPath> [-t_us <keyFrameOffset> --overwrite]`
Notes:
- `<imagePath>` image format must be JPEG
- `<keyFrameOffset>` is where the image is, in microseconds, from the start of the video file
- `--overwrite` flag allows overwriting an existing file instead of returning an error
## Motion Photo Format
This section will cover the requirements to make a valid Motion Photo.
Since each application/vendor has their own implementation, they also have separate requirements.
The image format should be JPEG, not HEIC. HEIC occassionally works, but is not reliable.
### Google
Google requires that the video file be embedded inside the image file, however no requirements are
placed on how this should be done. The video file is commonly appended to the end of the image file.
#### Photos
The Google Photos app requires, at a minimum, the following metadata tags to be set on the image:
- `MicroVideo`: set to `1`
- `MicroVideoVersion`: this library sets it to `1`
- `MicroVideoOffset`: offset in bytes from the end of the Motion Photo file to the start of the embedded video file
The offset is encoded as an XML field, so it does not impose any limit on the video size.
The additional metadata tag may be optionally set if known, may be set to `-1` if unknown, or may
be omitted entirely:
- `MicroVideoPresentationTimestampUs`: key-frame time offset in microseconds
#### Gallery
The Google Gallery app additionally requires that the Motion Photo filename start with `MV`.
### Samsung
Samsung requires that the video file be embedded inside the image file by constructing a trailer in a custom
format and appending it to the end of the image file.
It **MUST** be the last trailer in the image file.
#### Trailer Data
Trailer fields are written sequentially in the following format:
```
[\x00\x00][marker_value(ule16)][name_size(ule32)][name][data]
```
In our case we want:
```
name size raw video file
∨∨ ∨∨∨∨∨
[\x00\x00][\x30\x0A][16]["MotionPhoto_Data"][<bin>]
∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧ ∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧
marker name
```
Additional fields can be found [here](https://github.com/exiftool/exiftool/blob/ecc573fc04ac6538802fd0a61a9c4ca53837ca1d/lib/Image/ExifTool/Samsung.pm#L945)
but are not necessary.
#### Trailer Check
The trailer also has an additional SEF section that contains the sizes of fields and is used for validation.
It is appended directly after the fields, and has the following format:
```
["SEFH"][version(ule32)][field_count(ule32)]
for each field: [\x00\x00][field_marker_value(ule16)][field_offset_from_sef(ule32)][field_size(ule32)]
[sef_size(ule32)]["SEFT"]
```
The `sef_size` is the size of that whole section not including `"SEFH"` and `"SEFT"` (head and tail markers).
In our case (assuming `tsbsef` is the trailer size before SEF) we want:
```
head count marker field size tail
∨∨∨∨ ∨ ∨∨∨∨∨∨∨∨∨∨∨∨∨∨∨∨∨∨ ∨∨∨∨∨∨∨ ∨∨∨∨
["SEFH"][106][1][\x00\x00][\x30\x0A][<tsbsef>][<tsbef>][24]["SEFT"]
∧∧∧ ∧∧∧∧∧∧∧∧ ∧∧
version field offset sef size
```
#### Metadata
The following metadata tags may optionally be set:
- `MotionPhoto`: set to `1`
- `MotionPhotoVersion`: this library sets it to `1`
- `MotionPhotoPresentationTimestampUs`: key-frame time offset in microseconds
Samsung does not place any requirements on the file name.
## Future:
- attempt to parse key frame timestamp from Live Photo (discussed [here]())
- write `XMP-Container:Directory` data for Samsung (discussed [here](https://github.com/exiftool/exiftool/issues/254))
- add support for working with whole directories
Raw data
{
"_id": null,
"home_page": "https://github.com/doodspav/motionphoto",
"name": "motionphoto",
"maintainer": null,
"docs_url": null,
"requires_python": "<4,>=3.8",
"maintainer_email": null,
"keywords": "livephoto, LivePhoto, motionphoto, MotionPhoto",
"author": "doodspav",
"author_email": null,
"download_url": "https://files.pythonhosted.org/packages/c6/eb/737a1d385694a6a3bc7c1acf6510b0b7434b1e43fcbe2923602a0e626bcd/motionphoto-0.1.2.tar.gz",
"platform": "any",
"description": "# motionphoto\nPython library for creating Google and Samsung compatible Motion Photos.\n\nMost notably this can be used to convert Live Photos downloaded from iCloud \n(where each photo comes as a separate image and video file) into Motion Photos that users\nwith Android can use.\n\n## Table of Contents\n<!--ts-->\n* [Installation](#installation)\n* [Usage](#usage)\n* [Motion Photo Format](#motion-photo-format)\n * [Google](#google)\n * [Samsung](#samsung)\n* [Future](#future)\n<!--te-->\n\n## Installation\n\nLinux/MacOS:\n```shell\n$ python3 -m pip install motionphoto\n```\n\nWindows:\n```shell\n$ py -m pip install motionphoto\n```\n\nThis library has a dependency on `exiftool`.\n\nInstructions for installing it can be found [here](https://exiftool.org/install.html) and it must be available on\n`PATH`, or it can be installed with a package manager.\n\n## Usage\n\n`motionphoto -i <imagePath> -v <videoPath> -m <outputPath> [-t_us <keyFrameOffset> --overwrite]`\n\nNotes:\n- `<imagePath>` image format must be JPEG\n- `<keyFrameOffset>` is where the image is, in microseconds, from the start of the video file\n- `--overwrite` flag allows overwriting an existing file instead of returning an error\n\n## Motion Photo Format\nThis section will cover the requirements to make a valid Motion Photo. \nSince each application/vendor has their own implementation, they also have separate requirements.\n\nThe image format should be JPEG, not HEIC. HEIC occassionally works, but is not reliable.\n\n### Google\nGoogle requires that the video file be embedded inside the image file, however no requirements are\nplaced on how this should be done. The video file is commonly appended to the end of the image file.\n\n#### Photos\nThe Google Photos app requires, at a minimum, the following metadata tags to be set on the image:\n- `MicroVideo`: set to `1`\n- `MicroVideoVersion`: this library sets it to `1`\n- `MicroVideoOffset`: offset in bytes from the end of the Motion Photo file to the start of the embedded video file\n\nThe offset is encoded as an XML field, so it does not impose any limit on the video size.\n\nThe additional metadata tag may be optionally set if known, may be set to `-1` if unknown, or may\nbe omitted entirely:\n- `MicroVideoPresentationTimestampUs`: key-frame time offset in microseconds\n\n#### Gallery\nThe Google Gallery app additionally requires that the Motion Photo filename start with `MV`.\n\n### Samsung\nSamsung requires that the video file be embedded inside the image file by constructing a trailer in a custom\nformat and appending it to the end of the image file. \nIt **MUST** be the last trailer in the image file.\n\n#### Trailer Data\nTrailer fields are written sequentially in the following format:\n```\n[\\x00\\x00][marker_value(ule16)][name_size(ule32)][name][data]\n```\nIn our case we want:\n```\n name size raw video file\n \u2228\u2228 \u2228\u2228\u2228\u2228\u2228\n[\\x00\\x00][\\x30\\x0A][16][\"MotionPhoto_Data\"][<bin>]\n \u2227\u2227\u2227\u2227\u2227\u2227\u2227\u2227\u2227\u2227\u2227\u2227\u2227\u2227\u2227\u2227\u2227\u2227 \u2227\u2227\u2227\u2227\u2227\u2227\u2227\u2227\u2227\u2227\u2227\u2227\u2227\u2227\u2227\u2227\u2227\u2227\n marker name\n```\nAdditional fields can be found [here](https://github.com/exiftool/exiftool/blob/ecc573fc04ac6538802fd0a61a9c4ca53837ca1d/lib/Image/ExifTool/Samsung.pm#L945)\nbut are not necessary.\n\n#### Trailer Check\nThe trailer also has an additional SEF section that contains the sizes of fields and is used for validation.\nIt is appended directly after the fields, and has the following format: \n```\n[\"SEFH\"][version(ule32)][field_count(ule32)]\nfor each field: [\\x00\\x00][field_marker_value(ule16)][field_offset_from_sef(ule32)][field_size(ule32)]\n[sef_size(ule32)][\"SEFT\"]\n```\nThe `sef_size` is the size of that whole section not including `\"SEFH\"` and `\"SEFT\"` (head and tail markers).\n\nIn our case (assuming `tsbsef` is the trailer size before SEF) we want:\n```\n head count marker field size tail\n \u2228\u2228\u2228\u2228 \u2228 \u2228\u2228\u2228\u2228\u2228\u2228\u2228\u2228\u2228\u2228\u2228\u2228\u2228\u2228\u2228\u2228\u2228\u2228 \u2228\u2228\u2228\u2228\u2228\u2228\u2228 \u2228\u2228\u2228\u2228\n[\"SEFH\"][106][1][\\x00\\x00][\\x30\\x0A][<tsbsef>][<tsbef>][24][\"SEFT\"]\n \u2227\u2227\u2227 \u2227\u2227\u2227\u2227\u2227\u2227\u2227\u2227 \u2227\u2227\n version field offset sef size\n```\n\n#### Metadata\nThe following metadata tags may optionally be set:\n- `MotionPhoto`: set to `1`\n- `MotionPhotoVersion`: this library sets it to `1`\n- `MotionPhotoPresentationTimestampUs`: key-frame time offset in microseconds\n\nSamsung does not place any requirements on the file name.\n\n## Future:\n- attempt to parse key frame timestamp from Live Photo (discussed [here]())\n- write `XMP-Container:Directory` data for Samsung (discussed [here](https://github.com/exiftool/exiftool/issues/254))\n- add support for working with whole directories\n\n",
"bugtrack_url": null,
"license": null,
"summary": "Python library for creating Google and Samsung compatible Motion Photos",
"version": "0.1.2",
"project_urls": {
"Download": "https://github.com/doodspav/motionphoto",
"Homepage": "https://github.com/doodspav/motionphoto",
"Source": "https://github.com/doodspav/motionphoto"
},
"split_keywords": [
"livephoto",
" livephoto",
" motionphoto",
" motionphoto"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "b15239ae886394907ef1c29eea289dcb7952981c658280a0b770fe367a86aa1f",
"md5": "ac5b7eca73a3d8f2ae766cee1ff91d5e",
"sha256": "7ba2f2f8536462060ee835ae105ad026339d09594e2f69d449612c665b7c4676"
},
"downloads": -1,
"filename": "motionphoto-0.1.2-py3-none-any.whl",
"has_sig": false,
"md5_digest": "ac5b7eca73a3d8f2ae766cee1ff91d5e",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": "<4,>=3.8",
"size": 9454,
"upload_time": "2024-04-19T19:40:40",
"upload_time_iso_8601": "2024-04-19T19:40:40.435642Z",
"url": "https://files.pythonhosted.org/packages/b1/52/39ae886394907ef1c29eea289dcb7952981c658280a0b770fe367a86aa1f/motionphoto-0.1.2-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "c6eb737a1d385694a6a3bc7c1acf6510b0b7434b1e43fcbe2923602a0e626bcd",
"md5": "1822c03165c787f6dafffdbbc7699739",
"sha256": "02d8d7bc7cbea8df718df0c5af68407c70349c7f2f381753cc15cbd54a9580fd"
},
"downloads": -1,
"filename": "motionphoto-0.1.2.tar.gz",
"has_sig": false,
"md5_digest": "1822c03165c787f6dafffdbbc7699739",
"packagetype": "sdist",
"python_version": "source",
"requires_python": "<4,>=3.8",
"size": 9285,
"upload_time": "2024-04-19T19:40:41",
"upload_time_iso_8601": "2024-04-19T19:40:41.965752Z",
"url": "https://files.pythonhosted.org/packages/c6/eb/737a1d385694a6a3bc7c1acf6510b0b7434b1e43fcbe2923602a0e626bcd/motionphoto-0.1.2.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-04-19 19:40:41",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "doodspav",
"github_project": "motionphoto",
"travis_ci": false,
"coveralls": false,
"github_actions": false,
"requirements": [],
"lcname": "motionphoto"
}