Skip to content

Commit fb3cabb

Browse files
committed
Added support for iPhoto/Aperture
1 parent e043857 commit fb3cabb

File tree

13 files changed

+1370
-141
lines changed

13 files changed

+1370
-141
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
## 1.2
2+
- `New` Support for importing directly from iPhoto / Aperture libraries
3+
14
## 1.0
25
- `Improved` Better command line parameters parsing
36
- `Improved` Changed file names. The launch file is now lycheeupload.py

README.md

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
# Lychee Upload
2-
A command line tool for uploading photos on a hard drive to a [Lychee](http://github.com/electerious/Lychee) installation on a remote server via SSH.
3-
Based on [lycheesync](https://github.com/GustavePate/lycheesync) by Gustave Paté
2+
A command line tool for uploading photos in a directory or from iPhoto/Aperture libraries to a [Lychee](http://github.com/electerious/Lychee) installation on a remote server via SSH.
3+
Based on [lycheesync](https://github.com/GustavePate/lycheesync) by Gustave Paté and [Phoshare](https://code.google.com/p/phoshare/) by Google.
44

55

66
# Description
77

8-
Performs a batch image import from a location on hard drive to the Lychee installation on a remote server via SSH. Subdirectories are automatically converted to Lychee albums. As Lychee does not support sub-albums, photos in subsubdirectories are uploaded to the respective album. Photos in the root directory are uploaded to the Unsorted album.
8+
Performs a batch image import from a location on hard drive or from an iPhoto/Aperture library to the Lychee installation on a remote server via SSH. When importing from a directory, subdirectories are automatically converted to Lychee albums. As Lychee does not support sub-albums, photos in subsubdirectories are uploaded to the respective album. Photos in the root directory are uploaded to the Unsorted album. In the iPhoto/Aperture mode you can specify what to export. Possible options are events, albums and smart albums.
99
If you want to replace albums in the Lychee database, then you can use *-r* option. All photos in the existing album will be deleted in the Lychee database and replaced with new ones from the hard drive location.
1010

1111
# Installation
@@ -42,21 +42,50 @@ Finally install dependencies using *pip*
4242

4343
# Usage
4444

45-
`python lycheeupload.py [-h] [-d DIR] [-r] [-p] [-v] username@hostname:path
45+
`python lycheeupload.py [-h] [-d DIR] [-r] [-p] [-v] username@hostname:path`
46+
47+
General options
4648

4749
- `username@hostname:path` Server connection string with a full path to the directory where Lychee is installed.
4850
- `-h`, `--help` show a help message
49-
- `-d DIR`, `--dir DIR` path to the photo directory where to export photos from.
5051
- `-r`, `--replace` replace albums in Lychee with local ones
5152
- `-p`, `--public` make uploaded photos public
5253
- `-v`, `--verbose` print verbose messages
5354

55+
Directory import options
56+
57+
- `-d DIR`, `--dir DIR` path to the photo directory where to export photos from.
58+
59+
iPhoto / Aperture options
60+
61+
- `--iphoto [path]` Import from iPhoto. If path is not provided, then default location is used.
62+
- `--aperture [path]` Import from Aperture. If path is not provided, then default location is used.
63+
- `-e [pattern]`, `--events [pattern]` Export matching events. The argument is a regular expression. If the argument is omitted, then all events are exported.
64+
- `-a [pattern]`, `--albums [pattern]` Export matching regular albums. The argument is a regular expression. If the argument is omitted, then all events are exported.
65+
- `-s [pattern]`, `--smarts [pattern]` Export matching smart albums. The argument is a regular expression. If the argument is omitted, then all events are exported.
66+
- `--originals` Export originals instead of modified images
67+
- `-x pattern`, `--exclude pattern` Don't export matching albums or events. The pattern is a regular expression.
68+
69+
At very least you must specify a connection string and a source where photos should be imported from (`--dir`, `--iphoto` or `--aperture` options).
70+
5471
For example to import photos from the directory */home/user/photos/* to the remote server *example.com* with Lychee installed in the */home/user/mydomain.com/* directory, issue the following command
5572

5673
`python lycheeupload.py -d /home/user/photos/ user@example.com:/home/user/mydomain.com/`
5774

75+
Import all events from the default iPhoto library location (~/Pictures/iPhoto Library)
76+
77+
`python lycheeupload.py --iphoto --events user@example.com:/home/user/mydomain.com/`
78+
79+
Import all the albums starting with the word "Summer" from the Aperture library located in *~/Pictures/My Aperture Library*
80+
81+
`python lycheeupload.py --aperture "~/Pictures/My Aperture Library" --albums "Summer.*" user@example.com:/home/user/mydomain.com/`
82+
83+
84+
5885

5986

6087
# Licence
6188

6289
[MIT License](./LICENSE)
90+
91+
Parts of the program are licensed under the Apache License 2.0

conf.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,12 @@
55

66

77
class Conf:
8+
IPHOTO_DEFAULT_PATH = "~/Pictures/iPhoto Library/"
9+
APERTURE_DEFAULT_PATH = "~/Pictures/Aperture Library.aplibrary/"
810
pass
911

1012
conf = Conf()
1113
conf.verbose = logging.ERROR
1214

1315

14-
def load_conf(conf_file):
15-
loaded_conf = json.load(open(conf_file, 'r'))
16-
17-
for key, value in loaded_conf.items():
18-
setattr(conf, key, value)
19-
2016

database.py

Lines changed: 24 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ def loadAlbumList(self):
104104
return self.albumslist
105105

106106

107-
def albumExists(self, album):
107+
def albumExists(self, album_name):
108108
"""
109109
Check against its name if an album exists
110110
Parameters:
@@ -113,8 +113,8 @@ def albumExists(self, album):
113113
:return: album id or None if the album does not exist
114114
"""
115115

116-
if album['name'] and album['name'] in self.albumslist.keys():
117-
return self.albumslist[album['name']]
116+
if album_name and album_name in self.albumslist.keys():
117+
return self.albumslist[album_name]
118118
else:
119119
return None
120120

@@ -143,63 +143,65 @@ def photoExists(self, photo):
143143
finally:
144144
return res
145145

146-
def createAlbum(self, album):
146+
147+
def createAlbum(self, album_name):
147148
"""
148149
Creates an album
149150
Parameter:
150151
- album: the album properties list, at least the name should be specified
151152
Returns the created albumid or None
152153
"""
153-
album['id'] = None
154+
id = None
154155
query = "INSERT INTO lychee_albums (title, sysstamp, public, password) " \
155-
"VALUES ('{}', '{}', '{}', NULL)".format(album['name'],
156+
"VALUES ('{}', '{}', '{}', NULL)".format(album_name,
156157
datetime.datetime.now().strftime('%s'),
157158
conf.public)
158159
try:
159160
cur = self.db.cursor()
160161
cur.execute(query)
161162
self.db.commit()
162163

163-
query = "select id from lychee_albums where title='" + album['name'] + "'"
164+
query = "select id from lychee_albums where title='" + album_name + "'"
164165
cur.execute(query)
165166
row = cur.fetchone()
166167
self.albumslist['name'] = row[0]
167-
album['id'] = row[0]
168+
id = row[0]
168169

169-
logger.info("Album {} created".format(album["name"]))
170+
logger.info("Album {} created".format(album_name))
170171

171172
except Exception:
172-
logger.error('Failed to create album #{} {}'.format(album["id"], album["name"]), exc_info=True)
173-
album['id'] = None
173+
logger.error('Failed to create album #{} {}'.format(id, album_name), exc_info=True)
174+
id = None
174175
finally:
175-
return album['id']
176+
return id
176177

177178

178-
def eraseAlbum(self, album):
179+
def eraseAlbum(self, id):
179180
"""
180181
Deletes all photos of an album but don't delete the album itself
181182
Parameters:
182183
- album: the album properties list to erase. At least its id must be provided
183184
Return list of the erased photo url
184185
"""
185-
res = []
186-
query = "delete from lychee_photos where album = " + str(album['id']) + ''
187-
selquery = "select url from lychee_photos where album = " + str(album['id']) + ''
186+
query = "delete from lychee_photos where album = " + id + ''
187+
selquery = "select url from lychee_photos where album = " + id + ''
188+
189+
file_list = []
188190
try:
189191
cur = self.db.cursor()
190192
cur.execute(selquery)
191193
rows = cur.fetchall()
192194
for row in rows:
193-
res.append(row[0])
195+
file_list.append(row[0])
194196
cur.execute(query)
195197
self.db.commit()
198+
logger.info("Album #{} deleted.".format(id))
196199

197-
logger.info("Album {} deleted.".format(album["name"]))
200+
return file_list
198201
except Exception:
199-
logger.error('Failed to delete album #{} {}'.format(album["id"], album["name"]), exc_info=True)
202+
logger.error('Failed to delete album #{}'.format(id), exc_info=True)
200203

201-
finally:
202-
return res
204+
return None
203205

204206

205207

@@ -235,7 +237,7 @@ def addFileToAlbum(self, photo):
235237

236238
return True
237239
except Exception as e:
238-
logger.error("Inserting photo {} into database failed".format(photo.id))
240+
logger.error("Inserting photo {} into database failed".format(photo.id), exc_info=True)
239241

240242
return False
241243

0 commit comments

Comments
 (0)