Mercurial > lbo > hg > photosync
changeset 3:930d85223b09
Add LICENSE/README.
author | Lewin Bormann <lbo@spheniscida.de> |
---|---|
date | Fri, 14 Jun 2019 15:36:14 +0200 |
parents | b37d4ab762ab |
children | 2ba6fed8671a |
files | LICENSE README.md photosync.py |
diffstat | 3 files changed, 55 insertions(+), 11 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/LICENSE Fri Jun 14 15:36:14 2019 +0200 @@ -0,0 +1,19 @@ +Copyright (c) 2019 Lewin Bormann + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/README.md Fri Jun 14 15:36:14 2019 +0200 @@ -0,0 +1,17 @@ +# photosync + +Now that Google deprecates the Photos<->Drive synchronization, I need another way to back up my photos locally. This +program downloads all photos from your Google Photos account and organizes them locally. It is not very user friendly +yet, but definitely usable. + +**Pull requests are welcome!** + +## Behavior + +By default, photosync will ask for OAuth2 authorization on the console, and then immediately start downloading metadata +from Google Photos. Once no more new photos are fetched and all metadata is stored in `photosync.db`, photosync will +look for photos that are not yet marked as downloaded in the database and fetch the actual image files. By default, it +will organize photos in directories like `year/month/day/` (numerically, 0-padded), but you can write your own method of +mapping photos to directories. + +Albums are currently ignored.
--- a/photosync.py Fri Jun 14 15:27:55 2019 +0200 +++ b/photosync.py Fri Jun 14 15:36:14 2019 +0200 @@ -11,6 +11,16 @@ from google_auth_oauthlib.flow import InstalledAppFlow from google.auth.transport.requests import Request +PROD = False +TRACE = True + +def log(level, msg): + if PROD: + return + if level == 'TRACE' and not TRACE: + return + print(level, "::", msg) + class TokenSource: """Return OAuth token for PhotosService to use. @@ -82,13 +92,12 @@ # Photos are returned in reversed order of creationTime. while True: resp = self._service.mediaItems().search(body={'pageSize': 25, 'filters': filters, 'pageToken': pagetoken}).execute() - print(resp) pagetoken = resp.get('nextPageToken', None) items = resp.get('mediaItems', None) if not items: return for i in items: - print(i['mediaMetadata']['creationTime']) + log('TRACE', i['mediaMetadata']['creationTime']) yield i if pagetoken is None: return @@ -144,10 +153,10 @@ cur = conn.cursor() cur.execute('SELECT id FROM photos WHERE id = "{}"'.format(media_item['id'])) if cur.fetchone(): - print('WARN: Photo already in store.') + log('INFO', 'Photo already in store.') cur.close() return False - print('INFO: Inserting photo {}'.format(media_item['id'])) + log('INFO', 'Inserting photo {}'.format(media_item['id'])) cur.close() creation_time = int(self._dtparse.isoparse(media_item['mediaMetadata']['creationTime']).timestamp()) @@ -199,21 +208,21 @@ if not (date_range[0] or date_range[1]): if start_at_recent: date_range = (self._db.most_recent_creation_date(), datetime.datetime.now()) - print('INFO: Running starting for {}'.format(date_range)) + log('INFO', 'Running starting for {}'.format(date_range)) for photo in self._svc.list_library(start=date_range[0], to=date_range[1]): - print('INFO: Fetched metadata for {}'.format(photo['filename'])) + log('INFO', 'Fetched metadata for {}'.format(photo['filename'])) if self._db.add_online_photo(photo, self._path_mapper(photo)): - print('INFO: Added {} to DB'.format(photo['filename'])) + log('INFO', 'Added {} to DB'.format(photo['filename'])) return True def download_photos(self): """Scans database for photos not yet downloaded and downloads them.""" for photo in self._db.get_not_downloaded_photos(): (id, path, filename) = photo - print ('INFO: Downloading {fn} into {p}'.format(fn=filename, p=path)) + log ('INFO', 'Downloading {fn} into {p}'.format(fn=filename, p=path)) self._svc.download_photo(id, path) - print('INFO: Downloading {fn} successful'.format(fn=filename)) + log('INFO', 'Downloading {fn} successful'.format(fn=filename)) self._db.mark_photo_downloaded(id) def drive(self, date_range=(None, None), start_at_recent=True): @@ -231,11 +240,10 @@ def main(): - db = DB('sq.lite') + db = DB('photosync.db') s = PhotosService(tokens=TokenSource(db=db)) d = Driver(db, s) d.drive() - if __name__ == '__main__': main()