Passed
Pull Request — master (#319)
by Jaisen
01:52
created

GooglePhotos.upload()   C

Complexity

Conditions 10

Size

Total Lines 31
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 10
eloc 25
nop 2
dl 0
loc 31
rs 5.9999
c 0
b 0
f 0

How to fix   Complexity   

Complexity

Complex classes like elodie.plugins.googlephotos.googlephotos.GooglePhotos.upload() often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
"""
2
Google Photos plugin object.
3
Upload code adapted from https://github.com/eshmu/gphotos-upload
4
5
.. moduleauthor:: Jaisen Mathai <[email protected]>
6
"""
7
from __future__ import print_function
8
9
import json
10
11
from os.path import basename, isfile
12
13
from google_auth_oauthlib.flow import InstalledAppFlow
14
from google.auth.transport.requests import AuthorizedSession
15
from google.oauth2.credentials import Credentials
16
17
from elodie.plugins.plugins import PluginBase
18
19
class GooglePhotos(PluginBase):
20
    """A class to execute plugin actions.
21
       
22
       Requires a config file with the following configurations set.
23
       secrets_file:
24
            The full file path where to find the downloaded secrets.
25
       auth_file:
26
            The full file path where to store authenticated tokens.
27
    
28
    """
29
30
    __name__ = 'GooglePhotos'
31
32
    def __init__(self):
33
        super(GooglePhotos, self).__init__()
34
        self.upload_url = 'https://photoslibrary.googleapis.com/v1/uploads'
35
        self.media_create_url = 'https://photoslibrary.googleapis.com/v1/mediaItems:batchCreate'
36
        self.scopes = [
37
            'https://www.googleapis.com/auth/photoslibrary',
38
            'https://www.googleapis.com/auth/photoslibrary.appendonly',
39
            'https://www.googleapis.com/auth/photoslibrary.sharing'
40
        ]
41
        
42
        self.secrets_file = None
43
        if('secrets_file' in self.config_for_plugin):
44
            self.secrets_file = self.config_for_plugin['secrets_file']
45
        # 'client_id.json'
46
        self.auth_file = None
47
        if('auth_file' in self.config_for_plugin):
48
            self.auth_file = self.config_for_plugin['auth_file']
49
        self.session = None
50
51
    def after(self, file_path, destination_folder, final_file_path, media):
52
        pass
53
54
    def before(self, file_path, destination_folder, media):
55
        pass
56
57
    def set_session(self):
58
        # Try to load credentials from an auth file.
59
        # If it doesn't exist or is not valid then catch the 
60
        #  exception and reauthenticate.
61
        try:
62
            creds = Credentials.from_authorized_user_file(self.auth_file, self.scopes)
63
        except:
64
            print(self.secrets_file)
65
            flow = InstalledAppFlow.from_client_secrets_file(self.secrets_file, self.scopes)
66
            creds = flow.run_local_server()
67
            cred_dict = {
68
                'token': creds.token,
69
                'refresh_token': creds.refresh_token,
70
                'id_token': creds.id_token,
71
                'scopes': creds.scopes,
72
                'token_uri': creds.token_uri,
73
                'client_id': creds.client_id,
74
                'client_secret': creds.client_secret
75
            }
76
77
            # Store the returned authentication tokens to the auth_file.
78
            with open(self.auth_file, 'w') as f:
79
                f.write(json.dumps(cred_dict))
80
81
        self.session = AuthorizedSession(creds)
82
        self.session.headers["Content-type"] = "application/octet-stream"
83
        self.session.headers["X-Goog-Upload-Protocol"] = "raw"
84
85
    def upload(self, path_to_photo):
86
        self.set_session()
87
        if(self.session is None):
88
            self.log('Could not initialize session')
89
            return None
90
91
        self.session.headers["X-Goog-Upload-File-Name"] = basename(path_to_photo)
92
        if(not isfile(path_to_photo)):
93
            self.log('Could not find file: {}'.format(path_to_photo))
94
            return None
95
96
        with open(path_to_photo, 'rb') as f:
97
            photo_bytes = f.read()
98
99
        upload_token = self.session.post(self.upload_url, photo_bytes)
100
        if(upload_token.status_code != 200 or not upload_token.content):
101
            self.log('Uploading media failed: ({}) {}'.format(upload_token.status_code, upload_token.content))
102
            return None
103
104
        create_body = json.dumps({'newMediaItems':[{'description':'','simpleMediaItem':{'uploadToken':upload_token.content.decode()}}]}, indent=4)
105
        resp = self.session.post(self.media_create_url, create_body).json()
106
        if(
107
            'newMediaItemResults' not in resp or
108
            'status' not in resp['newMediaItemResults'][0] or
109
            'message' not in resp['newMediaItemResults'][0]['status'] or
110
            resp['newMediaItemResults'][0]['status']['message'] != 'Success'
111
        ):
112
            self.log('Creating new media item failed: {}'.format(resp['newMediaItemResults'][0]['status']))
113
            return None
114
        
115
        return resp['newMediaItemResults'][0]
116