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()