Main.update_proxies()   F
last analyzed

Complexity

Conditions 13

Size

Total Lines 62

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 62
rs 2.8934
cc 13

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Complexity

Complex classes like Main.update_proxies() 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
from core.header import Header
2
from core.helpers import get_class_name, spawn
3
from core.logger import Logger
4
5
from plugin.core.configuration import Configuration
6
from plugin.core.constants import ACTIVITY_MODE, PLUGIN_VERSION
7
from plugin.core.helpers.thread import module_start
8
from plugin.core.logger import LOG_HANDLER, LoggerManager
9
from plugin.managers.account import TraktAccountManager
10
from plugin.managers.m_trakt.credential import TraktOAuthCredentialManager
11
from plugin.models import TraktAccount
12
from plugin.modules.core.manager import ModuleManager
13
from plugin.preferences import Preferences
14
from plugin.scrobbler.core.session_prefix import SessionPrefix
15
16
from plex import Plex
17
from plex_activity import Activity
18
from plex_metadata import Metadata
19
from six.moves.urllib.parse import quote_plus, urlsplit, urlunsplit
20
from requests.packages.urllib3.util import Retry
21
from trakt import Trakt
22
import os
23
import uuid
24
25
log = Logger()
26
27
28
class Main(object):
29
    modules = []
30
31
    def __init__(self):
32
        Header.show(self)
33
34
        # Initial configuration update
35
        self.on_configuration_changed()
36
37
        # Initialize clients
38
        self.init_trakt()
39
        self.init_plex()
40
        self.init()
41
42
        # Initialize modules
43
        ModuleManager.initialize()
44
45
    def init(self):
46
        names = []
47
48
        # Initialize modules
49
        for module in self.modules:
50
            names.append(get_class_name(module))
51
52
            if hasattr(module, 'initialize'):
53
                module.initialize()
54
55
        log.info('Initialized %s modules: %s', len(names), ', '.join(names))
56
57
    @staticmethod
58
    def init_plex():
59
        # Ensure client identifier has been generated
60
        if not Dict['plex.client.identifier']:
0 ignored issues
show
Comprehensibility Best Practice introduced by
Undefined variable 'Dict'
Loading history...
61
            # Generate identifier
62
            Dict['plex.client.identifier'] = uuid.uuid4()
0 ignored issues
show
Comprehensibility Best Practice introduced by
Undefined variable 'Dict'
Loading history...
63
64
        # Retrieve current client identifier
65
        client_id = Dict['plex.client.identifier']
0 ignored issues
show
Comprehensibility Best Practice introduced by
Undefined variable 'Dict'
Loading history...
66
67
        if isinstance(client_id, uuid.UUID):
68
            client_id = str(client_id)
69
70
        # plex.py
71
        Plex.configuration.defaults.authentication(
72
            os.environ.get('PLEXTOKEN')
73
        )
74
75
        Plex.configuration.defaults.client(
76
            identifier=client_id,
77
78
            product='trakt (for Plex)',
79
            version=PLUGIN_VERSION
80
        )
81
82
        # plex.activity.py
83
        path = os.path.join(LOG_HANDLER.baseFilename, '..', '..', 'Plex Media Server.log')
84
        path = os.path.abspath(path)
85
86
        Activity['logging'].add_hint(path)
87
88
        # plex.metadata.py
89
        Metadata.configure(
90
            client=Plex.client
91
        )
92
93
    @classmethod
94
    def init_trakt(cls):
95
        config = Configuration.advanced['trakt']
96
97
        # Build timeout value
98
        timeout = (
99
            config.get_float('connect_timeout', 6.05),
100
            config.get_float('read_timeout', 24)
101
        )
102
103
        # Client
104
        Trakt.configuration.defaults.client(
105
            id='c9ccd3684988a7862a8542ae0000535e0fbd2d1c0ca35583af7ea4e784650a61',
106
            secret='bf00575b1ad252b514f14b2c6171fe650d474091daad5eb6fa890ef24d581f65'
107
        )
108
109
        # Application
110
        Trakt.configuration.defaults.app(
111
            name='trakt (for Plex)',
112
            version=PLUGIN_VERSION
113
        )
114
115
        # Http
116
        Trakt.base_url = (
117
            config.get('protocol', 'https') + '://' +
118
            config.get('hostname', 'api.trakt.tv')
119
        )
120
121
        Trakt.configuration.defaults.http(
122
            timeout=timeout
123
        )
124
125
        # Configure keep-alive
126
        Trakt.http.keep_alive = config.get_boolean('keep_alive', True)
127
128
        # Configure requests adapter
129
        Trakt.http.adapter_kwargs = {
130
            'pool_connections': config.get_int('pool_connections', 10),
131
            'pool_maxsize': config.get_int('pool_size', 10),
132
            'max_retries': Retry(
133
                total=config.get_int('connect_retries', 3),
134
                read=0
135
            )
136
        }
137
138
        Trakt.http.rebuild()
139
140
        # Bind to events
141
        Trakt.on('oauth.refresh', cls.on_trakt_refresh)
142
        Trakt.on('oauth.refresh.rejected', cls.on_trakt_refresh_rejected)
143
144
        log.info(
145
            'Configured trakt.py (timeout=%r, base_url=%r, keep_alive=%r, adapter_kwargs=%r)',
146
            timeout,
147
            Trakt.base_url,
148
            Trakt.http.keep_alive,
149
            Trakt.http.adapter_kwargs,
150
        )
151
152
    @classmethod
153
    def on_trakt_refresh(cls, username, authorization):
154
        log.debug('[Trakt.tv] Token has been refreshed for %r', username)
155
156
        # Retrieve trakt account matching this `authorization`
157
        with Trakt.configuration.http(retry=True).oauth(token=authorization.get('access_token')):
158
            settings = Trakt['users/settings'].get(validate_token=False)
159
160
        if not settings:
161
            log.warn('[Trakt.tv] Unable to retrieve account details for token')
162
            return False
163
164
        # Retrieve trakt account username from `settings`
165
        s_username = settings.get('user', {}).get('username')
166
167
        if not s_username:
168
            log.warn('[Trakt.tv] Unable to retrieve username for token')
169
            return False
170
171
        if s_username != username:
172
            log.warn('[Trakt.tv] Token mismatch (%r != %r)', s_username, username)
173
            return False
174
175
        # Find matching trakt account
176
        trakt_account = (TraktAccount
177
            .select()
178
            .where(
179
                TraktAccount.username == username
180
            )
181
        ).first()
182
183
        if not trakt_account:
184
            log.warn('[Trakt.tv] Unable to find account with the username: %r', username)
185
            return False
186
187
        # Update OAuth credential
188
        TraktAccountManager.update.from_dict(
189
            trakt_account, {
190
                'authorization': {
191
                    'oauth': authorization
192
                }
193
            },
194
            settings=settings
195
        )
196
197
        log.info('[Trakt.tv] Token updated for %r', trakt_account)
198
        return True
199
200
    @classmethod
201
    def on_trakt_refresh_rejected(cls, username):
202
        log.debug('[Trakt.tv] Token refresh for %r has been rejected', username)
203
204
        # Find matching trakt account
205
        account = (TraktAccount
206
            .select()
207
            .where(
208
                TraktAccount.username == username
209
            )
210
        ).first()
211
212
        if not account:
213
            log.warn('[Trakt.tv] Unable to find account with the username: %r', username)
214
            return False
215
216
        # Delete OAuth credential
217
        TraktOAuthCredentialManager.delete(
218
            account=account.id
219
        )
220
221
        log.info('[Trakt.tv] Token cleared for %r', account)
222
        return True
223
224
    def start(self):
225
        # Construct main thread
226
        spawn(self.run, daemon=True, thread_name='main')
227
228
    def run(self):
229
        # Check for authentication token
230
        log.info('X-Plex-Token: %s', 'available' if os.environ.get('PLEXTOKEN') else 'unavailable')
231
232
        # Process server startup state
233
        self.process_server_state()
234
235
        # Start new-style modules
236
        module_start()
237
238
        # Start modules
239
        names = []
240
241
        for module in self.modules:
242
            if not hasattr(module, 'start'):
243
                continue
244
245
            names.append(get_class_name(module))
246
247
            module.start()
248
249
        log.info('Started %s modules: %s', len(names), ', '.join(names))
250
251
        ModuleManager.start()
252
253
        # Start plex.activity.py
254
        Activity.start(ACTIVITY_MODE.get(Preferences.get('activity.mode')))
255
256
    @classmethod
257
    def process_server_state(cls):
258
        # Check startup state
259
        server = Plex.detail()
260
261
        if server is None:
262
            log.info('Unable to check startup state, detail request failed')
263
            return
264
265
        # Check server startup state
266
        if server.start_state is None:
267
            return
268
269
        if server.start_state == 'startingPlugins':
270
            return cls.on_starting_plugins()
271
272
        log.error('Unhandled server start state %r', server.start_state)
273
274
    @staticmethod
275
    def on_starting_plugins():
276
        log.debug('on_starting_plugins')
277
278
        SessionPrefix.increment()
279
280
    @classmethod
281
    def on_configuration_changed(cls):
282
        # Update proxies (for requests)
283
        cls.update_proxies()
284
285
        # Refresh loggers
286
        LoggerManager.refresh()
287
288
    @staticmethod
289
    def update_proxies():
290
        # Retrieve proxy host
291
        host = Prefs['proxy_host']
0 ignored issues
show
Comprehensibility Best Practice introduced by
Undefined variable 'Prefs'
Loading history...
292
293
        if not host:
294
            if not Trakt.http.proxies and not os.environ.get('HTTP_PROXY') and not os.environ.get('HTTPS_PROXY'):
295
                return
296
297
            # Update trakt client
298
            Trakt.http.proxies = {}
299
300
            # Update environment variables
301
            if 'HTTP_PROXY' in os.environ:
302
                del os.environ['HTTP_PROXY']
303
304
            if 'HTTPS_PROXY' in os.environ:
305
                del os.environ['HTTPS_PROXY']
306
307
            log.info('HTTP Proxy has been disabled')
308
            return
309
310
        # Parse URL
311
        host_parsed = urlsplit(host)
312
313
        # Expand components
314
        scheme, netloc, path, query, fragment = host_parsed
315
316
        if not scheme:
317
            scheme = 'http'
318
319
        # Retrieve proxy credentials
320
        username = Prefs['proxy_username']
0 ignored issues
show
Comprehensibility Best Practice introduced by
Undefined variable 'Prefs'
Loading history...
321
        password = Prefs['proxy_password']
0 ignored issues
show
Comprehensibility Best Practice introduced by
Undefined variable 'Prefs'
Loading history...
322
323
        # Build URL
324
        if username and password and '@' not in netloc:
325
            netloc = '%s:%s@%s' % (
326
                quote_plus(username),
327
                quote_plus(password),
328
                netloc
329
            )
330
331
        url = urlunsplit((scheme, netloc, path, query, fragment))
332
333
        # Update trakt client
334
        Trakt.http.proxies = {
335
            'http': url,
336
            'https': url
337
        }
338
339
        # Update environment variables
340
        os.environ.update({
341
            'HTTP_PROXY': url,
342
            'HTTPS_PROXY': url
343
        })
344
345
        # Display message in log file
346
        if not host_parsed.username and not host_parsed.password:
347
            log.info('HTTP Proxy has been enabled (host: %r)', host)
348
        else:
349
            log.info('HTTP Proxy has been enabled (host: <sensitive>)')
350