Giter Club home page Giter Club logo

elodie's Introduction

Hello, I'm Elodie

~~ Your Personal EXIF-based Photo, Video and Audio Assistant ~~

CircleCI Coverage Status Scrutinizer Code Quality

Quickstart guide

Getting started takes just a few minutes.

Install ExifTool

Elodie relies on the great ExifTool library by Phil Harvey. You'll need to install it for your platform.

Some features for video files will only work with newer versions of ExifTool and have been tested on version 10.20 or higher. Support for HEIC files requires version 11.50 or higher. Check your version by typing exiftool -ver and see the manual installation instructions for ExifTool if needed.

# OSX (uses homebrew, http://brew.sh/)
brew install exiftool

# Debian / Ubuntu
apt-get install libimage-exiftool-perl

# Fedora / Redhat
dnf install perl-Image-ExifTool

# Windows users can install the binary
# http://www.sno.phy.queensu.ca/~phil/exiftool/install.html

Clone the Elodie repository

You can clone Elodie from GitHub. You'll need git installed (instructions).

git clone https://github.com/jmathai/elodie.git
cd elodie
pip install -r requirements.txt

Give Elodie a test drive

Now that you've got the minimum dependencies installed you can start using Elodie. You'll need a photo, video or audio file and a folder you'd like Elodie to organize them into.

# Run these commands from the root of the repository you just cloned.
./elodie.py import --debug --destination="/where/i/want/my/photos/to/go" /where/my/photo/is.jpg

You'll notice that the photo was organized into an Unknown Location folder. That's because you haven't set up your MapQuest API (instructions).

Now you're ready to learn more about Elodie.

Slowstart guide

Read a 3 part blog post on why I was created and how I can be used with Google Photos.

I work tirelessly to make sure your photos are always sorted and organized so you can focus on more important things. By photos I mean JPEG, DNG, NEF and common video and audio files.

You don't love me yet but you will.

I only do 3 things.

  • Firstly I organize your existing collection of photos into a customizable folder structure.
  • Second I help make it easy for all the photos you haven't taken yet to flow into the exact location they belong.
  • Third but not least I promise to do all this without a yucky propietary database that some friends of mine use.

NOTE: make sure you've installed everything I need before running the commands below. Instructions at the top of this page.

Let's organize your existing photos

My guess is you've got quite a few photos scattered around. The first thing I'll help you do is to get those photos organized. It doesn't matter if you have hundreds, thousands or tens of thousands of photos; the more the merrier.

Fire up your terminal and run this command which copies your photos into something a bit more structured.

./elodie.py import --destination="/where/i/want/my/photos/to/go" /where/my/photos/are

I'm pretty fast but depending on how many photos you have you might want to grab a snack. When you run this command I'll print out my work as I go along. If you're bored you can open /where/i/want/my/photos/to/go in Finder and watch as I effortlessly copy your photos there.

You'll notice that your photos are now organized by date and location. Some photos do not have proper dates or location information in them. I do my best and in the worst case scenario I'll use the earlier of the files access or modified time. Ideally your photos have dates and location in the EXIF so my work is more accurate.

Don't fret if your photos don't have much EXIF information. I'll show you how you can fix them up later on but let's walk before we run.

Back to your photos. When I'm done you should see something like this. Notice that I've renamed your files by adding the date and time they were taken. This helps keep them in chronological order when using most viewing applications. You'll thank me later.

├── 2015-06-Jun
│   ├── California
│   │   ├── 2015-06-29_16-34-14-img_3900.jpg
│   │   └── 2015-06-29_17-07-06-img_3901.jpg
│   └── Paris
│       └── 2015-06-30_02-40-43-img_3903.jpg
├── 2015-07-Jul
│   ├── Mountain View
│   │   ├── 2015-07-19_17-16-37-img_9426.jpg
│   │   └── 2015-07-24_19-06-33-img_9432.jpg
└── 2015-09-Sep
│   ├── Unknown Location
    │   ├── 2015-09-27_01-41-38-_dsc8705.dng
    │   └── 2015-09-27_01-41-38-_dsc8705.nef

Not too bad, eh? Wait a second, what's Unknown Location? If I'm not able to figure out where a photo was taken I'll place it into a folder named Unknown Location. This typically happens when photos do not have GPS information in their EXIF. You shouldn't see this for photos taken on a smartphone but it's often the case with digital cameras and SLRs. I can help you add GPS information to those photos and get them organized better. Let me show you how.

Usage Instructions

You can view these instructions on the command line by typing ./elodie.py import --help, ./elodie.py update --help or ./elodie.py generate-db --help.

Import photos

Usage: elodie.py import [OPTIONS] [PATHS]...

  Import files or directories by reading their EXIF and organizing them
  accordingly.

Options:
  --destination DIRECTORY  Copy imported files into this directory.
                           [required]
  --source DIRECTORY       Import files from this directory, if specified.
  --file PATH              Import this file, if specified.
  --album-from-folder      Use images' folders as their album names.
  --trash                  After copying files, move the old files to the
                           trash.
  --allow-duplicates       Import the file even if it's already been imported.
  --debug                  Override the value in constants.py with True.
  --exclude-regex TEXT     Regular expression for directories or files to
                           exclude.
  --help                   Show this message and exit.

Update photos

Usage: elodie.py update [OPTIONS] FILES...

  Update a file's EXIF. Automatically modifies the file's location and file
  name accordingly.

Options:
  --album TEXT     Update the image album.
  --location TEXT  Update the image location. Location should be the name of a
                   place, like "Las Vegas, NV".
  --time TEXT      Update the image time. Time should be in YYYY-mm-dd
                   hh:ii:ss or YYYY-mm-dd format.
  --title TEXT     Update the image title.
  --help           Show this message and exit.

(Re)Generate checksum database

Usage: elodie.py generate-db [OPTIONS]

  Regenerate the hash.json database which contains all of the sha256
  signatures of media files. The hash.json file is located at ~/.elodie/.

Options:
  --source DIRECTORY  Source of your photo library.  [required]
  --help              Show this message and exit.

Verify library against bit rot / data rot

Usage: elodie.py verify

Excluding folders and files from being imported

If you have specific folders or files which you would like to prevent from being imported you can provide regular expressions which will be used to match and skip files from being imported.

You can specify an exclusion at run time by using the --exclude-regex argument of the import command. You can pass multiple --exclude-regex arguments and all folder/file paths which match will be (silently) skipped.

If there are certain file or folder paths you never want to import then you can also add an [Exclusions] section to your config.ini file. Similar to the command line argument you can provide multiple exclusions. Here is an example.

[Exclusions]
synology_folders=@eaDir
thumbnails=.thumbnails

Create your own folder structure

OK, so what if you don't like the folders being named 2015-07-Jul/Mountain View? No problem!

You can add a custom folder structure by editing your config.ini file (which should be placed under ~/.elodie/config.ini). If you'd like to use a different folder for your configuration file then set an environment variable named ELODIE_APPLICATION_DIRECTORY with the fully qualified directory path.

Custom folder examples

Sometimes examples are easier to understand than explainations so I'll start there. If you'd like to understand my magic I explain it in more detail below these examples. You customize your folder structure in the Directory section of your config.ini. For details of the supported formats see strftime.org

[Directory]
location=%city, %state
year=%Y
full_path=%year/%location
# -> 2015/Sunnyvale, California

location=%city, %state
month=%B
year=%Y
full_path=%year/%month/%location
# -> 2015/December/Sunnyvale, California

location=%city, %state
month=%m
year=%Y
full_path=%year-%month/%location
# -> 2015-12/Sunnyvale, California

date=%Y
location=%city, %state
custom=%date %album
full_path=%location/%custom
# -> Sunnyvale, California/2015 Birthday Party

Using fallback folders

There are times when the EXIF needed to correctly name a folder doesn't exist on a photo. I came up with fallback folders to help you deal with situations such as this. Here's how it works.

You can specify a series of folder names by separating them with a |. That's a pipe, not an L. Let's look at an example.

[Directory]
month=%m
year=%Y
location=%city
full_path=%month/%year/%album|%location|%"Beats me"

What this asks me to do is to name the last folder the same as the album I find in EXIF. If I don't find an album in EXIF then I should use the location. If there's no GPS in the EXIf then I should name the last folder Beats me.

How folder customization works

You can construct your folder structure using a combination of the location, dates and camera make/model. Under the Directory section of your config.ini file you can define placeholder names and assign each a value. For example, date=%Y-%m would create a date placeholder with a value of YYYY-MM which would be filled in with the date from the EXIF on the photo.

The placeholders can be used to define the folder structure you'd like to create. The default structure would look like 2015-07-Jul/Mountain View.

I have some date placeholders you can customize. You can use any of the standard Python time directives to customize the date format to your liking.

  • %day the day the photo was taken.
  • %month the month the photo was taken.
  • %year the year the photo was taken.

I have camera make and model placeholders which can be used to include the camera make and model into the folder path.

  • %camera_make the make of the camera which took the photo.
  • %camera_model the model of the camera which took the photo.

I also have a few built-in location placeholders you can use. Use this to construct the %location you use in full_path.

  • %city the name of the city the photo was taken. Requires geolocation data in EXIF.
  • %state the name of the state the photo was taken. Requires geolocation data in EXIF.
  • %country the name of the country the photo was taken. Requires geolocation data in EXIF.

In addition to my built-in and date placeholders you can combine them into a single folder name using my complex placeholders.

  • %location can be used to combine multiple values of %city, %state and %country. For example, location=%city, %state would result in folder names like Sunnyvale, California.
  • %date can be used to combine multiple values from the standard Python time directives. For example, date=%Y-%m would result in folder names like 2015-12.
  • %custom can be used to combine multiple values from anything else. Think of it as a catch-all when %location and %date don't meet your needs.

How file customization works

You can configure how Elodie names your files using placeholders. This works similarly to how folder customization works. The default naming format is what's referred to elsewhere in this document and has many thought through benefits. Using the default will gives you files named like 2015-09-27_01-41-38-_dsc8705.jpg.

  • Minimizes the likelihood of naming conflicts.
  • Encodes important EXIF information into the file name.
  • Optimizes for sort order when listing in most file and photo viewers.

If you'd like to specify your own naming convention it's recommended you include something that's mostly unique like the time including seconds. You'll need to include a [File] section in your config.ini file with a name attribute. If a placeholder doesn't have a value then it plus any preceding characters which are not alphabetic are removed.

By default the resulting filename is all lowercased. To change this behavior to uppercasing add capitalization=upper.

[File]
date=%Y-%m-%b-%H-%M-%S
name=%date-%original_name-%title.%extension
# -> 2012-05-mar-12-59-30-dsc_1234-my-title.jpg

date=%Y-%m-%b-%H-%M-%S
name=%date-%original_name-%album.%extension
capitalization=upper
# -> 2012-05-MAR-12-59-30-DSC_1234-MY-ALBUM.JPG

Reorganize by changing location and dates

If you notice some photos were incorrectly organized you should definitely let me know. In the example above I put two photos into an Unknown Location folder because I didn't find GPS information in their EXIF. To fix this I'll help you add GPS information into the photos' EXIF and then I'll reorganize them.

Tell me where your photos were taken

Run the command below if you want to tell me the photos were taken in Las Vegas. You don't have to type all that in though. It's easier to just type ./elodie.py update --location="Las Vegas, NV" and select and drag the files from OS X Finder into the terminal.

./elodie.py update --location="Las Vegas, NV" /where/i/want/my/photos/to/go/2015-09-Sep/Unknown\ Location/2015-09-27_01-41-38-_dsc8705.dng /where/i/want/my/photos/to/go/2015-09-Sep/Unknown\ Location/2015-09-27_01-41-38-_dsc8705.nef

You should see this after running that command.

└── 2015-09-Sep
│   ├── Las Vegas
    │   ├── 2015-09-27_01-41-38-_dsc8705.dng
    │   └── 2015-09-27_01-41-38-_dsc8705.nef

Tell me when you took your photos

Run the command below if I got the date wrong when organizing your photos. Similarly to the above command you can drag files from Finder into your terminal.

./elodie.py update --time="2015-04-15" /where/i/want/my/photos/to/go/2015-09-Sep/Unknown\ Location/2015-09-27_01-41-38-_dsc8705.dng /where/i/want/my/photos/to/go/2015-09-Sep/Unknown\ Location/2015-09-27_01-41-38-_dsc8705.nef

That will change the date folder like so.

└── 2015-04-Apr
│   ├── Las Vegas
    │   ├── 2015-09-27_01-41-38-_dsc8705.dng
    │   └── 2015-09-27_01-41-38-_dsc8705.nef

You can, of course, ask me to change the location and time. I'll happily update the photos and move them around accordingly.

./elodie.py update --location="Las Vegas, NV" --time="2015-04-15" /where/i/want/my/photos/to/go/2015-09-Sep/Unknown\ Location/2015-09-27_01-41-38-_dsc8705.dng /where/i/want/my/photos/to/go/2015-09-Sep/Unknown\ Location/2015-09-27_01-41-38-_dsc8705.nef

What about photos I take in the future?

Organizing your existing photos is great. But I'd be lying if I said I was the only one who could help you with that. Unlike other programs I put the same effort into keeping your library organized into the future as I have in getting it organized in the first place.

Letting me know when you've got more photos to organize

In order to sort new photos that I haven't already organized I need someone to tell me about them. There's no single way to do this. You could use inotify, cron, Automator or my favorite app - Hazel; it doesn't matter.

If you'd like to let me know of a specific photo or group of photos to add to your library you would run one of the following commands. Use fully qualified paths for everything since you won't be running this manually.

# I can import a single file into your library.
./elodie.py import --destination="/where/i/want/my/photo/to/go" /full/path/to/file.jpg

# I can also import all the photos from a directory into your library.
./elodie.py import --destination="/where/i/want/my/photo/to/go" /where/my/photos/are

Why not use a database?

Look, it's not that I think databases are evil. One of my friends is a database. It's just that I've been doing this for a long time and I've always used a database for it. In the end they're more trouble than they're worth. I should have listened to my mother when she told me to not date a database.

It's a lot more work to organize photos without a database. No wonder everyone else uses them. But your happiness is my happiness. If a little elbow grease on my part makes you happy then I'm glad to do it.

A bit on how I do all this without a database

Every photo is essentially a database. So it's more accurate to say I use the thousands of tiny databases you already have and use them to organize your photos.

I'm simple. I put a photo into its proper location. I can update a photo to have the right date or location. The latter triggers the first; creating a nice tidy loop of organizational goodness.

I don't do anything else so don't bother asking.

EXIF and XMP tags

When I organize photos I look at the embedded metadata. Here are the details of how I determine what information to use in order of precedence.

Dimension Fields Notes
Date Taken (photo) EXIF:DateTimeOriginal, EXIF:CreateDate, EXIF:ModifyDate, file created, file modified
Date Taken (video, audio) QuickTime:CreationDate, QuickTime:CreateDate, QuickTime:CreationDate-und-US, QuickTime:MediaCreateDate, H264:DateTimeOriginal, file created, file modified
Location (photo) EXIF:GPSLatitude/EXIF:GPSLatitudeRef, EXIF:GPSLongitude/EXIF:GPSLongitudeRef
Location (video, audio) XMP:GPSLatitude, Composite:GPSLatitude, XMP:GPSLongitude, Composite:GPSLongitude Composite tags are read-only
Title (photo) XMP:Title
Title (video, audio) XMP:DisplayName
Album XMP-xmpDM:Album, XMP:Album XMP:Album is user defined in configs/ExifTool_config for backwards compatability
Camera Make (photo, video) EXIF:Make, QuickTime:Make
Camera Model (photo, video) EXIF:Model, QuickTime:Model

Using OpenStreetMap data from MapQuest

I use MapQuest to help me organize your photos by location. You'll need to sign up for a free developer account and get an API key. They give you 15,000 calls per month so I can't do any more than that unless you shell out some big bucks to them. Once I hit my limit the best I'll be able to do is Unknown Location until the following month.

Once you sign up you'll have to get an API key and copy it into a file named ~/.elodie/config.ini. I've included a config.ini-sample file which you can copy to config.ini.

mkdir ~/.elodie
cp config.ini-sample ~/.elodie/config.ini
# now you're ready to add your MapQuest key

If you're an english speaker then you will probably want to add prefer_english_names=True to the [MapQuest] section else you'll have cities named using the local language.

Questions, comments or concerns?

The best ways to provide feedback is by opening a GitHub issue or emailing me at [email protected].

elodie's People

Contributors

adamantike avatar adamcandy avatar amaleki avatar arikfr avatar auenkind avatar benmccann avatar clach04 avatar davisonio avatar dzamataev avatar edent avatar evanjt avatar gti96 avatar ibizaman avatar jmathai avatar kraymer avatar mattca avatar misterpipine avatar noonat avatar ostavnaas avatar patricksan avatar pbuyle avatar petertiedemann avatar phifogg avatar qasimk avatar ramblurr avatar rkoster avatar srynot4sale avatar vkuptcov avatar zingo avatar zserg avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

elodie's Issues

Address pep8 related issues

So far this is the list of pep8 issues.

# pep8 elodie
elodie/arguments.py:3:11: E401 multiple imports on one line
elodie/arguments.py:6:1: E302 expected 2 blank lines, found 1
elodie/arguments.py:12:1: W293 blank line contains whitespace
elodie/filesystem.py:17:1: E302 expected 2 blank lines, found 0
elodie/filesystem.py:21:80: E501 line too long (86 > 79 characters)
elodie/filesystem.py:38:80: E501 line too long (93 > 79 characters)
elodie/filesystem.py:38:94: W291 trailing whitespace
elodie/filesystem.py:41:80: E501 line too long (86 > 79 characters)
elodie/filesystem.py:63:31: E711 comparison to None should be 'if cond is None:'
elodie/filesystem.py:63:80: E501 line too long (80 > 79 characters)
elodie/filesystem.py:78:80: E501 line too long (86 > 79 characters)
elodie/filesystem.py:89:21: E711 comparison to None should be 'if cond is None:'
elodie/filesystem.py:92:80: E501 line too long (103 > 79 characters)
elodie/filesystem.py:94:80: E501 line too long (86 > 79 characters)
elodie/filesystem.py:94:87: W291 trailing whitespace
elodie/filesystem.py:95:80: E501 line too long (94 > 79 characters)
elodie/filesystem.py:98:80: E501 line too long (97 > 79 characters)
elodie/filesystem.py:103:80: E501 line too long (127 > 79 characters)
elodie/filesystem.py:128:80: E501 line too long (85 > 79 characters)
elodie/filesystem.py:129:80: E501 line too long (92 > 79 characters)
elodie/filesystem.py:137:9: E265 block comment should start with '# '
elodie/filesystem.py:159:21: E711 comparison to None should be 'if cond is None:'
elodie/filesystem.py:160:32: E712 comparison to True should be 'if cond is True:' or 'if cond:'
elodie/filesystem.py:164:80: E501 line too long (85 > 79 characters)
elodie/filesystem.py:165:27: E712 comparison to False should be 'if cond is False:' or 'if not cond:'
elodie/filesystem.py:166:32: E712 comparison to True should be 'if cond is True:' or 'if cond:'
elodie/filesystem.py:167:80: E501 line too long (93 > 79 characters)
elodie/filesystem.py:172:17: E712 comparison to True should be 'if cond is True:' or 'if cond:'
elodie/filesystem.py:194:80: E501 line too long (117 > 79 characters)
elodie/filesystem.py:197:80: E501 line too long (84 > 79 characters)
elodie/filesystem.py:201:80: E501 line too long (82 > 79 characters)
elodie/filesystem.py:205:80: E501 line too long (97 > 79 characters)
elodie/filesystem.py:208:80: E501 line too long (91 > 79 characters)
elodie/filesystem.py:210:80: E501 line too long (80 > 79 characters)
elodie/filesystem.py:211:1: W293 blank line contains whitespace
elodie/geolocation.py:14:1: E302 expected 2 blank lines, found 1
elodie/geolocation.py:25:1: E302 expected 2 blank lines, found 1
elodie/geolocation.py:39:80: E501 line too long (86 > 79 characters)
elodie/geolocation.py:39:87: W291 trailing whitespace
elodie/geolocation.py:40:80: E501 line too long (121 > 79 characters)
elodie/geolocation.py:42:80: E501 line too long (92 > 79 characters)
elodie/geolocation.py:43:80: E501 line too long (83 > 79 characters)
elodie/geolocation.py:44:80: E501 line too long (86 > 79 characters)
elodie/geolocation.py:47:80: E501 line too long (154 > 79 characters)
elodie/geolocation.py:50:1: W293 blank line contains whitespace
elodie/geolocation.py:55:1: W293 blank line contains whitespace
elodie/geolocation.py:58:1: E302 expected 2 blank lines, found 1
elodie/geolocation.py:59:80: E501 line too long (82 > 79 characters)
elodie/geolocation.py:71:15: E712 comparison to False should be 'if cond is False:' or 'if not cond:'
elodie/geolocation.py:76:80: E501 line too long (153 > 79 characters)
elodie/geolocation.py:78:1: E302 expected 2 blank lines, found 1
elodie/geolocation.py:80:23: E221 multiple spaces before operator
elodie/geolocation.py:81:28: E221 multiple spaces before operator
elodie/geolocation.py:85:1: E302 expected 2 blank lines, found 1
elodie/geolocation.py:89:1: W293 blank line contains whitespace
elodie/geolocation.py:97:1: E302 expected 2 blank lines, found 1
elodie/geolocation.py:102:54: E231 missing whitespace after ','
elodie/geolocation.py:106:29: E703 statement ends with a semicolon
elodie/geolocation.py:133:80: E501 line too long (110 > 79 characters)
elodie/geolocation.py:136:28: E712 comparison to True should be 'if cond is True:' or 'if cond:'
elodie/geolocation.py:140:28: E712 comparison to True should be 'if cond is True:' or 'if cond:'
elodie/geolocation.py:145:1: E302 expected 2 blank lines, found 1
elodie/geolocation.py:153:28: E712 comparison to True should be 'if cond is True:' or 'if cond:'
elodie/geolocation.py:154:80: E501 line too long (98 > 79 characters)
elodie/geolocation.py:155:80: E501 line too long (106 > 79 characters)
elodie/geolocation.py:158:28: E712 comparison to True should be 'if cond is True:' or 'if cond:'
elodie/geolocation.py:162:28: E712 comparison to True should be 'if cond is True:' or 'if cond:'
elodie/localstorage.py:9:1: E302 expected 2 blank lines, found 1
elodie/localstorage.py:11:80: E501 line too long (82 > 79 characters)
elodie/localstorage.py:23:80: E501 line too long (82 > 79 characters)
elodie/localstorage.py:38:80: E501 line too long (82 > 79 characters)
elodie/localstorage.py:47:18: E712 comparison to True should be 'if cond is True:' or 'if cond:'
elodie/localstorage.py:91:18: E712 comparison to True should be 'if cond is True:' or 'if cond:'
elodie/localstorage.py:94:52: E231 missing whitespace after ','
elodie/localstorage.py:99:80: E501 line too long (140 > 79 characters)
elodie/localstorage.py:102:80: E501 line too long (99 > 79 characters)
elodie/localstorage.py:105:37: E201 whitespace after '('
elodie/localstorage.py:105:53: E202 whitespace before ')'
elodie/localstorage.py:107:26: E201 whitespace after '('
elodie/localstorage.py:107:36: E202 whitespace before ')'
elodie/localstorage.py:110:17: E265 block comment should start with '# '
elodie/localstorage.py:111:36: E703 statement ends with a semicolon
elodie/plist_parser.py:15:1: E302 expected 2 blank lines, found 0
elodie/media/media.py:23:1: E302 expected 2 blank lines, found 0
elodie/media/media.py:33:80: E501 line too long (106 > 79 characters)
elodie/media/media.py:33:82: E261 at least two spaces before inline comment
elodie/media/media.py:33:83: E262 inline comment should start with '# '
elodie/media/media.py:54:1: W293 blank line contains whitespace
elodie/media/media.py:68:80: E501 line too long (81 > 79 characters)
elodie/media/media.py:74:5: E303 too many blank lines (2)
elodie/media/media.py:91:80: E501 line too long (108 > 79 characters)
elodie/media/media.py:98:1: W293 blank line contains whitespace
elodie/media/media.py:117:80: E501 line too long (111 > 79 characters)
elodie/media/media.py:134:26: E703 statement ends with a semicolon
elodie/media/media.py:144:5: E303 too many blank lines (2)
elodie/media/media.py:166:55: E712 comparison to False should be 'if cond is False:' or 'if not cond:'
elodie/media/media.py:184:1: W293 blank line contains whitespace
elodie/media/media.py:196:21: E711 comparison to None should be 'if cond is None:'
elodie/media/media.py:200:1: W293 blank line contains whitespace
elodie/media/media.py:235:28: E712 comparison to True should be 'if cond is True:' or 'if cond:'
elodie/media/media.py:236:80: E501 line too long (107 > 79 characters)
elodie/media/media.py:237:80: E501 line too long (170 > 79 characters)
elodie/media/media.py:270:5: E303 too many blank lines (2)
elodie/media/media.py:271:80: E501 line too long (92 > 79 characters)
elodie/media/media.py:273:80: E501 line too long (121 > 79 characters)
elodie/media/media.py:276:80: E501 line too long (82 > 79 characters)
elodie/media/photo.py:26:1: E302 expected 2 blank lines, found 0
elodie/media/photo.py:38:1: W293 blank line contains whitespace
elodie/media/photo.py:51:13: E128 continuation line under-indented for visual indent
elodie/media/photo.py:51:19: E251 unexpected spaces around keyword / parameter equals
elodie/media/photo.py:51:21: E251 unexpected spaces around keyword / parameter equals
elodie/media/photo.py:51:45: E251 unexpected spaces around keyword / parameter equals
elodie/media/photo.py:51:47: E251 unexpected spaces around keyword / parameter equals
elodie/media/photo.py:54:80: E501 line too long (87 > 79 characters)
elodie/media/photo.py:66:80: E501 line too long (94 > 79 characters)
elodie/media/photo.py:73:80: E501 line too long (91 > 79 characters)
elodie/media/photo.py:75:80: E501 line too long (93 > 79 characters)
elodie/media/photo.py:78:23: E225 missing whitespace around operator
elodie/media/photo.py:78:80: E501 line too long (94 > 79 characters)
elodie/media/photo.py:83:80: E501 line too long (113 > 79 characters)
elodie/media/photo.py:85:80: E501 line too long (114 > 79 characters)
elodie/media/photo.py:100:80: E501 line too long (85 > 79 characters)
elodie/media/photo.py:102:80: E501 line too long (93 > 79 characters)
elodie/media/photo.py:103:80: E501 line too long (102 > 79 characters)
elodie/media/photo.py:105:80: E501 line too long (104 > 79 characters)
elodie/media/photo.py:110:80: E501 line too long (96 > 79 characters)
elodie/media/photo.py:111:80: E501 line too long (86 > 79 characters)
elodie/media/photo.py:112:30: E703 statement ends with a semicolon
elodie/media/photo.py:114:36: E712 comparison to True should be 'if cond is True:' or 'if cond:'
elodie/media/photo.py:124:80: E501 line too long (89 > 79 characters)
elodie/media/photo.py:125:1: W293 blank line contains whitespace
elodie/media/photo.py:134:25: E703 statement ends with a semicolon
elodie/media/photo.py:175:80: E501 line too long (95 > 79 characters)
elodie/media/photo.py:176:80: E501 line too long (132 > 79 characters)
elodie/media/photo.py:177:80: E501 line too long (97 > 79 characters)
elodie/media/photo.py:178:80: E501 line too long (135 > 79 characters)
elodie/media/video.py:26:1: E302 expected 2 blank lines, found 0
elodie/media/video.py:28:24: E231 missing whitespace after ','
elodie/media/video.py:28:30: E231 missing whitespace after ','
elodie/media/video.py:28:36: E231 missing whitespace after ','
elodie/media/video.py:28:42: E231 missing whitespace after ','
elodie/media/video.py:46:80: E501 line too long (95 > 79 characters)
elodie/media/video.py:52:5: E303 too many blank lines (2)
elodie/media/video.py:74:80: E501 line too long (100 > 79 characters)
elodie/media/video.py:92:80: E501 line too long (102 > 79 characters)
elodie/media/video.py:94:80: E501 line too long (85 > 79 characters)
elodie/media/video.py:102:80: E501 line too long (123 > 79 characters)
elodie/media/video.py:114:1: W293 blank line contains whitespace
elodie/media/video.py:127:13: E128 continuation line under-indented for visual indent
elodie/media/video.py:127:19: E251 unexpected spaces around keyword / parameter equals
elodie/media/video.py:127:21: E251 unexpected spaces around keyword / parameter equals
elodie/media/video.py:127:45: E251 unexpected spaces around keyword / parameter equals
elodie/media/video.py:127:47: E251 unexpected spaces around keyword / parameter equals
elodie/media/video.py:130:80: E501 line too long (87 > 79 characters)
elodie/media/video.py:135:80: E501 line too long (87 > 79 characters)
elodie/media/video.py:145:80: E501 line too long (111 > 79 characters)
elodie/media/video.py:149:80: E501 line too long (89 > 79 characters)
elodie/media/video.py:150:1: W293 blank line contains whitespace
elodie/media/video.py:171:19: E712 comparison to True should be 'if cond is True:' or 'if cond:'
elodie/media/video.py:172:80: E501 line too long (97 > 79 characters)
elodie/media/video.py:188:80: E501 line too long (82 > 79 characters)
elodie/media/video.py:213:80: E501 line too long (89 > 79 characters)
elodie/media/video.py:215:80: E501 line too long (84 > 79 characters)
elodie/media/video.py:224:80: E501 line too long (118 > 79 characters)
elodie/media/video.py:225:32: E712 comparison to True should be 'if cond is True:' or 'if cond:'
elodie/media/video.py:231:32: E712 comparison to True should be 'if cond is True:' or 'if cond:'
elodie/media/video.py:237:80: E501 line too long (87 > 79 characters)
elodie/media/video.py:239:80: E501 line too long (102 > 79 characters)
elodie/media/video.py:241:80: E501 line too long (115 > 79 characters)
elodie/media/video.py:242:80: E501 line too long (122 > 79 characters)
elodie/media/video.py:245:36: E712 comparison to True should be 'if cond is True:' or 'if cond:'
elodie/media/video.py:250:80: E501 line too long (106 > 79 characters)
elodie/media/video.py:261:80: E501 line too long (92 > 79 characters)
elodie/media/video.py:263:80: E501 line too long (82 > 79 characters)
elodie/media/video.py:264:80: E501 line too long (88 > 79 characters)
elodie/media/video.py:280:80: E501 line too long (84 > 79 characters)
elodie/media/video.py:285:80: E501 line too long (82 > 79 characters)
elodie/media/video.py:286:21: E265 block comment should start with '# '
elodie/media/video.py:299:36: E712 comparison to True should be 'if cond is True:' or 'if cond:'
elodie/media/video.py:304:80: E501 line too long (81 > 79 characters)
elodie/media/video.py:305:80: E501 line too long (82 > 79 characters)
elodie/media/video.py:313:80: E501 line too long (111 > 79 characters)
elodie/media/video.py:314:80: E501 line too long (108 > 79 characters)
elodie/media/video.py:317:36: E712 comparison to True should be 'if cond is True:' or 'if cond:'
elodie/media/video.py:318:80: E501 line too long (86 > 79 characters)
elodie/media/video.py:321:80: E501 line too long (84 > 79 characters)
elodie/media/video.py:324:80: E501 line too long (207 > 79 characters)
elodie/media/video.py:325:36: E712 comparison to True should be 'if cond is True:' or 'if cond:'
elodie/media/video.py:329:80: E501 line too long (99 > 79 characters)
elodie/media/video.py:346:1: E302 expected 2 blank lines, found 1
elodie/tests/filesystem_test.py:8:80: E501 line too long (114 > 79 characters)
elodie/tests/filesystem_test.py:10:1: E402 module level import not at top of file
elodie/tests/filesystem_test.py:11:1: E402 module level import not at top of file
elodie/tests/filesystem_test.py:12:1: E402 module level import not at top of file
elodie/tests/filesystem_test.py:13:1: E402 module level import not at top of file
elodie/tests/filesystem_test.py:14:1: E402 module level import not at top of file
elodie/tests/filesystem_test.py:27:19: E712 comparison to True should be 'if cond is True:' or 'if cond:'
elodie/tests/filesystem_test.py:30:1: W293 blank line contains whitespace
elodie/tests/filesystem_test.py:37:80: E501 line too long (97 > 79 characters)
elodie/tests/filesystem_test.py:43:19: E712 comparison to True should be 'if cond is True:' or 'if cond:'
elodie/tests/filesystem_test.py:49:1: E302 expected 2 blank lines, found 1
elodie/tests/filesystem_test.py:51:80: E501 line too long (92 > 79 characters)
elodie/tests/filesystem_test.py:53:19: E712 comparison to False should be 'if cond is False:' or 'if not cond:'
elodie/tests/filesystem_test.py:55:1: E302 expected 2 blank lines, found 1
elodie/tests/filesystem_test.py:68:1: E302 expected 2 blank lines, found 1
elodie/tests/filesystem_test.py:70:80: E501 line too long (97 > 79 characters)
elodie/tests/filesystem_test.py:88:1: E302 expected 2 blank lines, found 1
elodie/tests/filesystem_test.py:120:1: E302 expected 2 blank lines, found 1
elodie/tests/filesystem_test.py:124:1: E302 expected 2 blank lines, found 1
elodie/tests/filesystem_test.py:131:1: E302 expected 2 blank lines, found 1
elodie/tests/filesystem_test.py:136:80: E501 line too long (82 > 79 characters)
elodie/tests/filesystem_test.py:138:1: E302 expected 2 blank lines, found 1
elodie/tests/filesystem_test.py:150:1: E302 expected 2 blank lines, found 1
elodie/tests/filesystem_test.py:157:1: E302 expected 2 blank lines, found 1
elodie/tests/filesystem_test.py:164:1: E302 expected 2 blank lines, found 1
elodie/tests/filesystem_test.py:171:1: E302 expected 2 blank lines, found 1
elodie/tests/filesystem_test.py:178:1: E302 expected 2 blank lines, found 1
elodie/tests/filesystem_test.py:186:80: E501 line too long (95 > 79 characters)
elodie/tests/filesystem_test.py:196:80: E501 line too long (99 > 79 characters)
elodie/tests/filesystem_test.py:198:1: E302 expected 2 blank lines, found 1
elodie/tests/filesystem_test.py:206:80: E501 line too long (95 > 79 characters)
elodie/tests/filesystem_test.py:216:80: E501 line too long (110 > 79 characters)
elodie/tests/filesystem_test.py:218:1: E302 expected 2 blank lines, found 1
elodie/tests/filesystem_test.py:226:80: E501 line too long (95 > 79 characters)
elodie/tests/filesystem_test.py:236:80: E501 line too long (92 > 79 characters)
elodie/tests/filesystem_test.py:238:1: E302 expected 2 blank lines, found 1
elodie/tests/filesystem_test.py:246:80: E501 line too long (95 > 79 characters)
elodie/tests/filesystem_test.py:256:80: E501 line too long (103 > 79 characters)
elodie/tests/filesystem_test.py:258:1: E302 expected 2 blank lines, found 1
elodie/tests/filesystem_test.py:266:80: E501 line too long (95 > 79 characters)
elodie/tests/filesystem_test.py:276:80: E501 line too long (93 > 79 characters)
elodie/tests/filesystem_test.py:278:1: E302 expected 2 blank lines, found 1
elodie/tests/filesystem_test.py:286:80: E501 line too long (95 > 79 characters)
elodie/tests/filesystem_test.py:296:80: E501 line too long (104 > 79 characters)
elodie/tests/filesystem_test.py:298:1: E302 expected 2 blank lines, found 1
elodie/tests/filesystem_test.py:303:80: E501 line too long (85 > 79 characters)
elodie/tests/filesystem_test.py:306:80: E501 line too long (95 > 79 characters)
elodie/tests/filesystem_test.py:316:80: E501 line too long (104 > 79 characters)
elodie/tests/geolocation_test.py:7:80: E501 line too long (114 > 79 characters)
elodie/tests/geolocation_test.py:9:1: E402 module level import not at top of file
elodie/tests/geolocation_test.py:10:1: E402 module level import not at top of file
elodie/tests/geolocation_test.py:14:1: E302 expected 2 blank lines, found 1
elodie/tests/geolocation_test.py:20:1: W293 blank line contains whitespace
elodie/tests/geolocation_test.py:22:80: E501 line too long (91 > 79 characters)
elodie/tests/geolocation_test.py:27:80: E501 line too long (112 > 79 characters)
elodie/tests/geolocation_test.py:29:1: E302 expected 2 blank lines, found 1
elodie/tests/geolocation_test.py:33:1: W293 blank line contains whitespace
elodie/tests/geolocation_test.py:35:80: E501 line too long (91 > 79 characters)
elodie/tests/geolocation_test.py:42:80: E501 line too long (120 > 79 characters)
elodie/tests/helper.py:8:1: E302 expected 2 blank lines, found 1
elodie/tests/helper.py:19:1: E302 expected 2 blank lines, found 1
elodie/tests/helper.py:21:80: E501 line too long (82 > 79 characters)
elodie/tests/helper.py:26:1: E302 expected 2 blank lines, found 1
elodie/tests/helper.py:30:1: E302 expected 2 blank lines, found 1
elodie/tests/helper.py:33:1: E302 expected 2 blank lines, found 1
elodie/tests/helper.py:45:1: E302 expected 2 blank lines, found 1
elodie/tests/helper.py:46:80: E501 line too long (111 > 79 characters)
elodie/tests/helper.py:48:1: E302 expected 2 blank lines, found 1
elodie/tests/helper.py:51:1: E302 expected 2 blank lines, found 1
elodie/tests/helper.py:55:1: E302 expected 2 blank lines, found 1
elodie/tests/localstorage_test.py:5:80: E501 line too long (114 > 79 characters)
elodie/tests/localstorage_test.py:7:1: E402 module level import not at top of file
elodie/tests/localstorage_test.py:8:1: E402 module level import not at top of file
elodie/tests/localstorage_test.py:9:1: E402 module level import not at top of file
elodie/tests/localstorage_test.py:13:1: E302 expected 2 blank lines, found 1
elodie/tests/localstorage_test.py:19:1: E302 expected 2 blank lines, found 1
elodie/tests/localstorage_test.py:28:80: E501 line too long (83 > 79 characters)
elodie/tests/localstorage_test.py:33:1: W293 blank line contains whitespace
elodie/tests/localstorage_test.py:34:1: E302 expected 2 blank lines, found 1
elodie/tests/localstorage_test.py:43:80: E501 line too long (83 > 79 characters)
elodie/tests/localstorage_test.py:48:1: W293 blank line contains whitespace
elodie/tests/localstorage_test.py:49:1: E302 expected 2 blank lines, found 1
elodie/tests/localstorage_test.py:58:80: E501 line too long (83 > 79 characters)
elodie/tests/localstorage_test.py:63:1: W293 blank line contains whitespace
elodie/tests/localstorage_test.py:64:1: E302 expected 2 blank lines, found 1
elodie/tests/localstorage_test.py:73:80: E501 line too long (83 > 79 characters)
elodie/tests/localstorage_test.py:74:1: W293 blank line contains whitespace
elodie/tests/localstorage_test.py:75:1: E302 expected 2 blank lines, found 1
elodie/tests/localstorage_test.py:80:80: E501 line too long (100 > 79 characters)
elodie/tests/localstorage_test.py:82:1: E302 expected 2 blank lines, found 1
elodie/tests/localstorage_test.py:91:80: E501 line too long (102 > 79 characters)
elodie/tests/localstorage_test.py:92:1: W293 blank line contains whitespace
elodie/tests/localstorage_test.py:93:1: E302 expected 2 blank lines, found 1
elodie/tests/localstorage_test.py:98:80: E501 line too long (103 > 79 characters)
elodie/tests/localstorage_test.py:100:1: E302 expected 2 blank lines, found 1
elodie/tests/localstorage_test.py:109:80: E501 line too long (83 > 79 characters)
elodie/tests/localstorage_test.py:121:1: E302 expected 2 blank lines, found 1
elodie/tests/localstorage_test.py:127:80: E501 line too long (129 > 79 characters)
elodie/tests/localstorage_test.py:129:1: E302 expected 2 blank lines, found 1
elodie/tests/localstorage_test.py:139:1: E302 expected 2 blank lines, found 1
elodie/tests/localstorage_test.py:145:1: W293 blank line contains whitespace
elodie/tests/localstorage_test.py:146:5: E303 too many blank lines (2)
elodie/tests/localstorage_test.py:151:1: E302 expected 2 blank lines, found 1
elodie/tests/localstorage_test.py:165:80: E501 line too long (105 > 79 characters)
elodie/tests/localstorage_test.py:167:1: E302 expected 2 blank lines, found 1
elodie/tests/localstorage_test.py:181:1: E302 expected 2 blank lines, found 1
elodie/tests/localstorage_test.py:183:1: W293 blank line contains whitespace
elodie/tests/localstorage_test.py:198:1: E302 expected 2 blank lines, found 1
elodie/tests/localstorage_test.py:200:1: W293 blank line contains whitespace
elodie/tests/media/media_test.py:13:80: E501 line too long (131 > 79 characters)
elodie/tests/media/media_test.py:14:80: E501 line too long (97 > 79 characters)
elodie/tests/media/media_test.py:16:1: E402 module level import not at top of file
elodie/tests/media/media_test.py:17:1: E402 module level import not at top of file
elodie/tests/media/media_test.py:18:1: E402 module level import not at top of file
elodie/tests/media/media_test.py:19:1: E402 module level import not at top of file
elodie/tests/media/media_test.py:23:1: E302 expected 2 blank lines, found 1
elodie/tests/media/media_test.py:29:1: E302 expected 2 blank lines, found 1
elodie/tests/media/media_test.py:35:1: E302 expected 2 blank lines, found 1
elodie/tests/media/media_test.py:36:80: E501 line too long (81 > 79 characters)
elodie/tests/media/media_test.py:40:1: E302 expected 2 blank lines, found 1
elodie/tests/media/media_test.py:41:80: E501 line too long (81 > 79 characters)
elodie/tests/media/media_test.py:45:1: E302 expected 2 blank lines, found 1
elodie/tests/media/media_test.py:46:80: E501 line too long (80 > 79 characters)
elodie/tests/media/media_test.py:50:1: E302 expected 2 blank lines, found 1
elodie/tests/media/photo_test.py:13:80: E501 line too long (131 > 79 characters)
elodie/tests/media/photo_test.py:14:80: E501 line too long (97 > 79 characters)
elodie/tests/media/photo_test.py:16:1: E402 module level import not at top of file
elodie/tests/media/photo_test.py:17:1: E402 module level import not at top of file
elodie/tests/media/photo_test.py:18:1: E402 module level import not at top of file
elodie/tests/media/photo_test.py:22:1: E302 expected 2 blank lines, found 1
elodie/tests/media/photo_test.py:36:1: E302 expected 2 blank lines, found 1
elodie/tests/media/photo_test.py:40:1: E302 expected 2 blank lines, found 1
elodie/tests/media/photo_test.py:46:1: E302 expected 2 blank lines, found 1
elodie/tests/media/photo_test.py:51:1: E302 expected 2 blank lines, found 1
elodie/tests/media/photo_test.py:56:1: E302 expected 2 blank lines, found 1
elodie/tests/media/photo_test.py:62:1: E302 expected 2 blank lines, found 1
elodie/tests/media/photo_test.py:68:1: E302 expected 2 blank lines, found 1
elodie/tests/media/photo_test.py:74:1: E302 expected 2 blank lines, found 1
elodie/tests/media/photo_test.py:80:1: E302 expected 2 blank lines, found 1
elodie/tests/media/photo_test.py:86:1: E302 expected 2 blank lines, found 1
elodie/tests/media/photo_test.py:94:1: E302 expected 2 blank lines, found 1
elodie/tests/media/photo_test.py:100:1: E302 expected 2 blank lines, found 1
elodie/tests/media/photo_test.py:105:80: E501 line too long (95 > 79 characters)
elodie/tests/media/photo_test.py:109:1: E302 expected 2 blank lines, found 1
elodie/tests/media/photo_test.py:114:1: E302 expected 2 blank lines, found 1
elodie/tests/media/photo_test.py:119:1: E302 expected 2 blank lines, found 1
elodie/tests/media/photo_test.py:128:19: E712 comparison to True should be 'if cond is True:' or 'if cond:'
elodie/tests/media/photo_test.py:137:80: E501 line too long (82 > 79 characters)
elodie/tests/media/photo_test.py:139:1: E302 expected 2 blank lines, found 1
elodie/tests/media/photo_test.py:140:80: E501 line too long (80 > 79 characters)
elodie/tests/media/photo_test.py:155:19: E712 comparison to True should be 'if cond is True:' or 'if cond:'
elodie/tests/media/photo_test.py:166:1: E302 expected 2 blank lines, found 1
elodie/tests/media/photo_test.py:177:19: E712 comparison to True should be 'if cond is True:' or 'if cond:'
elodie/tests/media/photo_test.py:186:1: E302 expected 2 blank lines, found 1
elodie/tests/media/photo_test.py:198:19: E712 comparison to True should be 'if cond is True:' or 'if cond:'
elodie/tests/media/video_test.py:13:80: E501 line too long (131 > 79 characters)
elodie/tests/media/video_test.py:14:80: E501 line too long (97 > 79 characters)
elodie/tests/media/video_test.py:16:1: E402 module level import not at top of file
elodie/tests/media/video_test.py:17:1: E402 module level import not at top of file
elodie/tests/media/video_test.py:18:1: E402 module level import not at top of file
elodie/tests/media/video_test.py:22:1: E302 expected 2 blank lines, found 1
elodie/tests/media/video_test.py:36:1: E302 expected 2 blank lines, found 1
elodie/tests/media/video_test.py:42:1: E302 expected 2 blank lines, found 1
elodie/tests/media/video_test.py:48:1: E302 expected 2 blank lines, found 1
elodie/tests/media/video_test.py:54:1: E302 expected 2 blank lines, found 1
elodie/tests/media/video_test.py:61:1: E302 expected 2 blank lines, found 1
elodie/tests/media/video_test.py:67:1: E302 expected 2 blank lines, found 1
elodie/tests/media/video_test.py:72:1: E302 expected 2 blank lines, found 1
elodie/tests/media/video_test.py:77:1: E302 expected 2 blank lines, found 1
elodie/tests/media/video_test.py:89:19: E712 comparison to True should be 'if cond is True:' or 'if cond:'
elodie/tests/media/video_test.py:98:80: E501 line too long (82 > 79 characters)
elodie/tests/media/video_test.py:100:1: E302 expected 2 blank lines, found 1
elodie/tests/media/video_test.py:104:80: E501 line too long (80 > 79 characters)
elodie/tests/media/video_test.py:114:5: E265 block comment should start with '# '
elodie/tests/media/video_test.py:115:5: E265 block comment should start with '# '
elodie/tests/media/video_test.py:119:19: E712 comparison to True should be 'if cond is True:' or 'if cond:'
elodie/tests/media/video_test.py:130:1: E302 expected 2 blank lines, found 1
elodie/tests/media/video_test.py:144:19: E712 comparison to True should be 'if cond is True:' or 'if cond:'
elodie/tests/media/video_test.py:153:1: E302 expected 2 blank lines, found 1
elodie/tests/media/video_test.py:168:19: E712 comparison to True should be 'if cond is True:' or 'if cond:'
elodie/tests/media/video_test.py:177:1: E302 expected 2 blank lines, found 1

Command not present

I couldn't find the command ./elodie.py in the root, as suggested in the README.
To test I used the import.py

Error in media/photo.py

It seems like error in photo.py line 82:

 if(key == 'latitude'):

should be:

if(type == 'latitude'):

import pyexiv2

Hi again,

I have this error:
./elodie.py", line 4, in
import pyexiv2
ImportError: No module named pyexiv2

But everything is well installed.(pyexiv2-0.3.2_1 already installed)

Thanks

Sync EXIF and filesystem without issuing commands

TL;DR: what would you think about using fuse?

First of all THANK YOU. This is the project I always wanted to do - in the exact same way.

So for now, the only way to update pictures is to run the elodie.py command. I would propose to make importing and updating even more intuitive by adding fuse into the mix. This way, making a modification on the filesystem - renaming a directory, moving a file, etc. - would trigger elodie.py and automatically change the EXIF information.

The UI could then be very inuitive. Assuming:

├── 2015-06-Jun
│   ├── California
│   │   ├── 2015-06-29_16-34-14-img_3900.jpg
│   │   └── 2015-06-29_17-07-06-img_3901.jpg
│   └── Paris
│       └── 2015-06-30_02-40-43-img_3903.jpg
├── 2015-07-Jul
│   ├── Mountain View
│   │   ├── 2015-07-19_17-16-37-img_9426.jpg
│   │   └── 2015-07-24_19-06-33-img_9432.jpg

Renaming California to Silicon Valley would trigger:

./elodie.py update --location="Silicon Valley" \
    2015-06-Jun/California/2015-06-29_16-34-14-img_3900.jpg \
    2015-06-Jun/California/2015-06-29_16-34-14-img_3901.jpg

Renaming 2015-07-Jul to 2015-08 would trigger, with a bit of magick:

./elodie.py update --time="2015-08-19" 2015-07-Jul/Mountain View/2015-07-19_17-16-37-img_9426.jpg
./elodie.py update --time="2015-08-24" 2015-07-Jul/Mountain View/2015-07-24_19-06-33-img_9432.jpg

And of course, editing the EXIF information directly would also trigger corresponding ./elodie.py commands.

Python bindings for fuse can be installed with pip too: pip install fuse-python.

What do you think of this?

Cache coordinates and place names for geolocation lookups

When looking up a geolocation coordinate we can cache the value to limit the number of lookups needed. A few considerations to make...

  • Store location name (i.e. Sunnyvale, CA) to a lat/lon pair
  • Store a lat/lon pair (i.e. extracted from EXIF)

lat/lon pairs within a certain radius of a pair that's already stored should return the stored location name. (see gelocation.place_name). When a pair is not within a certain radius of a stored lat/lon pair it should be looked up against an API and stored.

// cc @zingo

Cache location names and return them when looking up coordinates

gh-47 by @zingo added caching for location coordinates. We can leverage the same cache to lookup a name and return its corresponding name.

In addition to a performance boost this lets us further customize location labels and folders as a result. If the location db contains a name, lat, long set you can update a set of photos with that name. That means you can explicitly set labels and use ./elodie.py update --location="Home" /path/to/file.jpg which would assign it the exact location from the cache and place the photo(s) into a folder named Home instead of the normal "city name".

{"lat": 39.387227, "name": "Home", "long": -115.026129}

TODO: We need to come up with a way to backup/store the databases/caches that we're storing.

No status at Elodie app

  1. open app
  2. put folder A and folder B
  3. Click Start Organising
  4. Click in another window. At this moment the app disappear (minimised)
  5. Open the app again, there is no status

--album doesn't work in the import

I tried to execute this command:

./elodie.py import --source="~/Desktop/chris" --destination="/Volumes/Data/Pictures/elodie-photos" --album="Aniversario Chris" 

but it always return Usage message:

Usage: main.py import --source=<s> --destination=<d>
       main.py import --file=<f> --destination=<d>
       main.py update [--time=<t>] [--location=<l>] [--album=<a>] [--title=<t>] INPUT ...

If I remove the --album it does work.

Deduce geolocation from photos taken the same day

Hi, discovered your project on HN, looks like something I could use :)

Regarding the problem of photos with unknown location, the current solution consist to run the update subcommand on them.
But say I have this folder tree :

├── 2015-06-Jun
│   ├── California
│   │   ├── 2015-06-29_16-34-14-img_3900.jpg

and an unknown location photo named 2015-06-29_17-07-06-img_3901.jpg
I think it would make the workflow even more painless, if I could ./elodie.py import --propagate ... or something that would deduce that the photo has been taken in Cali since one half hour I took another one in this place.
A little magic/dangerous so should probably be turned off by default but does it make sense to you ?

Regards

API KEY for Map Quest

Which API should I use to put in the configuration?
Consumer key or secret or other?

I tried the consumer key and it didn't work.

screen shot 2015-12-02 at 22 34 30

The import continue appears as "Unknown location"

Read-only source fails on exiftool trying to create a file in source directory

When importing from a read-only source, I get the following error:

$ ./elodie.py import --source=~/Pictures/source --destination=~/Pictures/organized --album-from-folder
/usr/local/bin/exiftool -config "./elodie/configs/ExifTool_config" -xmp-elodie:Album="2015-08 MyEvent" "~/Pictures/source/2015/2015-08 MyEvent/P8242306.JPG"
Error: Error creating file: ~/Pictures/source/2015/2015-08 MyEvent/P8242306.JPG_exiftool_tmp - ~/Pictures/source/2015/2015-08 MyEvent/P8242306.JPG

And nothing is imported in the destination folder.

AttributeError: type object 'Media' has no attribute 'photo_extensions'

When running a simple import, I get the following error:

Traceback (most recent call last):
  File "./elodie.py", line 157, in <module>
    _import(params)
  File "./elodie.py", line 37, in _import
    media = Media.get_class_by_file(current_file, [Photo, Video])
  File "./elodie/elodie/media/media.py", line 298, in get_class_by_file
    if(extension in Media.photo_extensions):
AttributeError: type object 'Media' has no attribute 'photo_extensions'

I'm on master - 5818978 and all tests seem to pass fine:

nosetests -s
..............S.S....S...********************.S********************.S......................
----------------------------------------------------------------------
Ran 51 tests in 10.250s

OK (SKIP=5)

I outputted some values if that can help:

>>> Media.__class__
<class 'elodie.media.media.Media'>

>>> dir(Media)
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__',
'__hash__', '__init__', '__module__', '__name__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__',
'__weakref__', 'get_album', 'get_class_by_file', 'get_exif', 'get_exiftool',
'get_exiftool_attributes', 'get_extension', 'get_file_path', 'get_metadata',
'get_mimetype', 'get_title', 'is_valid', 'set_album', 'set_album_from_folder',
'set_metadata', 'set_metadata_basename']

README not updated

I just tried to run the commands but they don't exist anymore. I guess it was renamed to ./elodie

After installing pip

I am at this stage:
pip install LatLon
I am at the root of my Mac, but have this error:
running install_lib
creating /Library/Python/2.7/site-packages/pyproj
error: could not create '/Library/Python/2.7/site-packages/pyproj': Permission denied

Thanks for your help :)

Johan

Hazel script

I am new with Hazel script stuff. Do you mind to describe the rule you made in Hazel to notify Elodie to do this:
"to set up a Hazel rule to notify me when photos arrive in ~/Downloads so I can import them."

Thanks

Use of '~' in folder doesn't work

I tried to run this command and it didn't work:

$ ./elodie.py import --source="~/Desktop/chris" --destination="/Volumes/Data/Pictures/elodie-photos"

I had to convert to:

$ ./elodie.py import --source="/Users/patrick/Desktop/chris" --destination="/Volumes/Data/Pictures/elodie-photos"

Video test for get_date_taken fails on Travis but not locally

FAIL: elodie.tests.media.video_test.test_get_date_taken
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/travis/virtualenv/python2.7_with_system_site_packages/local/lib/python2.7/site-packages/nose/case.py", line 197, in runTest
    self.test(*self.arg)
  File "/home/travis/build/jmathai/elodie/elodie/tests/media/video_test.py", line 58, in test_get_date_taken
    assert date_taken == (2015, 1, 19, 12, 45, 11, 0, 19, 0), date_taken
AssertionError: time.struct_time(tm_year=2015, tm_mon=12, tm_mday=12, tm_hour=8, tm_min=59, tm_sec=26, tm_wday=5, tm_yday=346, tm_isdst=0)

Import/update script exits when it encounters an invalid file

The import/update script recursively gets a listing of files and checks the extension against a list of supported extensions. If a file has a supported extension but does not contain valid data (i.e. is not a picture or movie) then the script exits.

Traceback (most recent call last):
  File "<string>", line 150, in <module>
  File "<string>", line 44, in _import
  File "/......../filesystem.py", line 137, in process_file
  File "/......../media.py", line 243, in get_metadata
  File "/......../media.py", line 143, in get_date_taken
  File "/......../media.py", line 175, in get_exif
  File "/usr/local/Cellar/pyexiv2/0.3.2_1/lib/python2.7/site-packages/pyexiv2/metadata.py", line 107, in read
    self.__image = self._instantiate_image(self.filename)
  File "/usr/local/Cellar/pyexiv2/0.3.2_1/lib/python2.7/site-packages/pyexiv2/metadata.py", line 79, in _instantiate_image
    return libexiv2python._Image(filename)
IOError: /......../Photos/._2012-04-21 17.59.46.jpg: The file contains data of an unknown image type
elodie returned -1

Folder created with update command get misnamed

Some UTC problem ?

./elodie.py update --time="2010-05-01" /Users/flap/Pictures/elodie/2004-12-Dec/Unknown\ Location/2004-12-31_23-0*
{"source":"/Users/flap/Pictures/elodie/2004-12-Dec/Unknown Location/2004-12-31_23-01-39-imgp4530.jpg", 
"destination":"/Users/flap/Pictures/elodie/2010-04-Apr/Unknown Location/2010-04-30_22-00-00-imgp4530.jpg"}

destination folder should be 2010-05-May, not 2010-04-Apr

Folder respect when no data or gps are found

I already have this kind of organisation in my photos:
screen shot 2015-12-03 at 00 00 14

if I import, probably I will loose all information as years ago we didn't embedded dates/gps on photos.

How could we solve this problem?

EXIF title only supports ascii characters when updating

Marking a test as skipped for this by adding # -*- coding: utf-8 to the top of the file.

Traceback (most recent call last):
  File "/usr/local/lib/python2.7/site-packages/nose/case.py", line 197, in runTest
    self.test(*self.arg)
  File "/Users/jmathai/elodie/elodie/tests/media/photo.py", line 168, in test_set_title_non_ascii
    status = photo.set_title('形声字 / 形聲字')
  File "/Users/jmathai/elodie/elodie/media/photo.py", line 165, in set_title
    exif_metadata['Xmp.dc.title'] = title
  File "/usr/local/lib/python2.7/site-packages/pyexiv2/metadata.py", line 271, in __setitem__
    return getattr(self, '_set_%s_tag' % family)(key, tag_or_value)
  File "/usr/local/lib/python2.7/site-packages/pyexiv2/metadata.py", line 244, in _set_xmp_tag
    tag = XmpTag(key, tag_or_value)
  File "/usr/local/lib/python2.7/site-packages/pyexiv2/xmp.py", line 114, in __init__
    self._set_value(value)
  File "/usr/local/lib/python2.7/site-packages/pyexiv2/xmp.py", line 235, in _set_value
    raw_value[k.encode('utf-8')] = v.encode('utf-8')
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe5 in position 0: ordinal not in range(128)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.