Passed
Push — master ( aeb165...d9ae97 )
by Dean
03:04
created

AccountMigration.refresh_account()   C

Complexity

Conditions 7

Size

Total Lines 31

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 49.3305
Metric Value
dl 0
loc 31
ccs 1
cts 21
cp 0.0476
rs 5.5
cc 7
crap 49.3305
1 1
from plugin.core.environment import Environment
2 1
from plugin.models import (
3
    Account, ClientRule, UserRule,
4
    PlexAccount, PlexBasicCredential,
5
    TraktAccount, TraktBasicCredential, TraktOAuthCredential
6
)
7 1
from plugin.modules.migrations.core.base import Migration
8
9 1
import apsw
10 1
import logging
11 1
import os
12 1
import peewee
13 1
import requests
14
15 1
log = logging.getLogger(__name__)
16
17
18 1
class AccountMigration(Migration):
19 1
    def run(self, token_plex=None):
20
        # Ensure server `Account` exists
21
        self.create_server_account()
22
23
        # Ensure administrator `Account` exists
24
        self.create_administrator_account(token_plex=token_plex)
25
26
        # Refresh extra accounts
27
        accounts = Account.select().where(
28
            Account.id > 1,
29
            Account.deleted == False
30
        )
31
32
        for account in accounts:
33
            self.refresh_account(account)
34
35
        return True
36
37 1
    @classmethod
38
    def create_server_account(cls):
39
        try:
40
            Account.get(Account.id == 0)
41
        except Account.DoesNotExist:
42
            Account.create(
43
                id=0,
44
                name=''
45
            )
46
47 1
    @classmethod
48 1
    def create_administrator_account(cls, token_plex=None):
49
        username = cls.get_trakt_username()
50
51
        try:
52
            account = Account.get(Account.id == 1)
53
        except Account.DoesNotExist:
54
            account = Account.create(
55
                id=1,
56
                name=username
57
            )
58
59
            # Create default rules for account
60
            cls.create_rules(account)
61
62
        # Ensure plex account details exist
63
        p_created, p_account = cls.create_plex_account(account)
64
65
        cls.create_plex_basic_credential(p_account, token_plex=token_plex)
66
67
        # Refresh plex account details
68
        try:
69
            p_refreshed = p_account.refresh(force=p_created)
70
        except:
71
            log.warn('Unable to refresh plex account (not authenticated?)', exc_info=True)
72
            p_refreshed = False
73
74
        # Ensure trakt account details exist
75
        t_created, t_account = cls.create_trakt_account(account, username)
76
77
        cls.create_trakt_basic_credential(t_account)
78
        cls.create_trakt_oauth_credential(t_account)
79
80
        # Refresh trakt account details
81
        try:
82
            t_refreshed = t_account.refresh(force=t_created)
83
        except:
84
            log.warn('Unable to refresh trakt account (not authenticated?)', exc_info=True)
85
            t_refreshed = False
86
87
        # Refresh account
88
        account.refresh(force=p_refreshed or t_refreshed)
89
90 1
    @classmethod
91
    def refresh_account(cls, account):
92
        if not account or account.deleted:
93
            return
94
95
        log.debug('Refreshing account: %r', account)
96
97
        # Refresh plex account details
98
        p_account = account.plex
99
        p_refreshed = False
100
101
        if p_account:
102
            try:
103
                p_refreshed = p_account.refresh()
104
            except:
105
                log.info('Unable to refresh plex account (not authenticated?)', exc_info=True)
106
                p_refreshed = False
107
108
        # Refresh trakt account details
109
        t_account = account.trakt
110
        t_refreshed = False
111
112
        if t_account:
113
            try:
114
                t_refreshed = t_account.refresh()
115
            except:
116
                log.info('Unable to refresh trakt account (not authenticated?)', exc_info=True)
117
                t_refreshed = False
118
119
        # Refresh account
120
        account.refresh(force=p_refreshed or t_refreshed)
121
122 1
    @classmethod
123
    def create_rules(cls, account):
124
        ClientRule.create(account=account, priority=1)
125
        UserRule.create(account=account, priority=1)
126
127
    #
128
    # Plex
129
    #
130
131 1
    @classmethod
132
    def create_plex_account(cls, account):
133
        try:
134
            return True, PlexAccount.create(
135
                account=account
136
            )
137
        except (apsw.ConstraintError, peewee.IntegrityError):
138
            return False, PlexAccount.get(
139
                account=account
140
            )
141
142 1
    @classmethod
143 1
    def create_plex_basic_credential(cls, plex_account, token_plex=None):
144
        if token_plex is None:
145
            token_plex = cls.get_token()
146
147
        if not token_plex:
148
            log.warn('No plex token available, unable to authenticate plex account')
149
            return False
150
151
        try:
152
            PlexBasicCredential.create(
153
                account=plex_account,
154
155
                token_plex=token_plex
156
            )
157
        except (apsw.ConstraintError, peewee.IntegrityError), ex:
158
            # Ensure basic credential has a token
159
            rows_updated = PlexBasicCredential.update(
160
                token_plex=token_plex,
161
                token_server=None
162
            ).where(
163
                PlexBasicCredential.account == plex_account,
164
                PlexBasicCredential.token_plex != token_plex
165
            ).execute()
166
167
            # Check if basic credential was updated
168
            if rows_updated:
169
                return True
170
171
            log.debug('Ignoring basic credential update for %r, already exists (%s)', plex_account, ex)
172
            return False
173
174
        return True
175
176
    #
177
    # Trakt
178
    #
179
180 1
    @classmethod
181
    def create_trakt_account(cls, account, username):
182
        try:
183
            return True, TraktAccount.create(
184
                account=account,
185
                username=username
186
            )
187
        except (apsw.ConstraintError, peewee.IntegrityError):
188
            return False, TraktAccount.get(
189
                account=account
190
            )
191
192 1
    @classmethod
193
    def create_trakt_basic_credential(cls, trakt_account):
194
        if not Environment.dict['trakt.token']:
195
            return False
196
197
        try:
198
            TraktBasicCredential.create(
199
                account=trakt_account,
200
                password=Environment.get_pref('password'),
201
202
                token=Environment.dict['trakt.token']
203
            )
204
        except (apsw.ConstraintError, peewee.IntegrityError), ex:
205
            log.debug('Ignoring basic credential update for %r, already exists (%s)', trakt_account, ex)
206
            return False
207
208
        return True
209
210 1
    @classmethod
211
    def create_trakt_oauth_credential(cls, trakt_account):
212
        if not Environment.dict['trakt.pin.code'] or not Environment.dict['trakt.pin.authorization']:
213
            return False
214
215
        try:
216
            TraktOAuthCredential.create(
217
                account=trakt_account,
218
                code=Environment.dict['trakt.pin.code'],
219
220
                **Environment.dict['trakt.pin.authorization']
221
            )
222
        except (apsw.ConstraintError, peewee.IntegrityError), ex:
223
            log.debug('Ignoring oauth credential update for %r, already exists (%s)', trakt_account, ex)
224
            return False
225
226
        return True
227
228 1
    @classmethod
229
    def get_trakt_username(cls):
230
        if Environment.get_pref('username'):
231
            return Environment.get_pref('username')
232
233
        if Environment.dict['trakt.username']:
234
            return Environment.dict['trakt.username']
235
236
        return None
237
238 1
    @classmethod
239 1
    def get_token(cls, request_headers=None):
240
        # Environment token
241
        env_token = os.environ.get('PLEXTOKEN')
242
243
        if env_token:
244
            log.info('Plex Token: environment')
245
            return env_token
246
247
        # Check if anonymous access is available
248
        server = requests.get('http://localhost:32400')
249
250
        if server.status_code == 200:
251
            log.info('Plex Token: anonymous')
252
            return 'anonymous'
253
254
        # No token available
255
        if request_headers is None:
256
            log.error('Plex Token: not available')
257
            return None
258
259
        # Try retrieve token from request
260
        req_token = request_headers.get('X-Plex-Token')
261
262
        if req_token:
263
            log.info('Plex Token: request')
264
            return req_token
265
266
        # No token available in request
267
        data = {
268
            'Client': {
269
                'User-Agent': request_headers.get('User-Agent'),
270
                'X-Plex-Product': request_headers.get('X-Plex-Product'),
271
            },
272
            'Headers': request_headers.keys()
273
        }
274
275
        log.debug('Request details: %r', data)
276
        log.error('Plex Token: not available', extra={
277
            'data': data
278
        })
279
        return None
280