Passed
Push — beta ( 91a9ed...2462f8 )
by Dean
02:58
created

Mode.process_guid_episode()   C

Complexity

Conditions 7

Size

Total Lines 30

Duplication

Lines 30
Ratio 100 %

Code Coverage

Tests 1
CRAP Score 48.2727

Importance

Changes 0
Metric Value
dl 30
loc 30
ccs 1
cts 18
cp 0.0556
rs 5.5
c 0
b 0
f 0
cc 7
crap 48.2727
1 1
from plugin.core.constants import GUID_SERVICES
2 1
from plugin.core.filters import Filters
3 1
from plugin.core.helpers.variable import try_convert
4 1
from plugin.modules.core.manager import ModuleManager
5 1
from plugin.sync import SyncMedia, SyncData, SyncMode
6
7 1
from plex import Plex
8 1
from plex_metadata import Guid
9 1
import elapsed
10 1
import itertools
11 1
import logging
12
13 1
log = logging.getLogger(__name__)
14
15 1
TRAKT_DATA_MAP = {
16
    SyncMedia.Movies: [
17
        SyncData.Collection,
18
        SyncData.Playback,
19
        SyncData.Ratings,
20
        SyncData.Watched,
21
        # SyncData.Watchlist
22
    ],
23
    SyncMedia.Shows: [
24
        SyncData.Ratings
25
    ],
26
    SyncMedia.Seasons: [
27
        SyncData.Ratings
28
    ],
29
    SyncMedia.Episodes: [
30
        SyncData.Collection,
31
        SyncData.Playback,
32
        SyncData.Ratings,
33
        SyncData.Watched,
34
        # SyncData.Watchlist
35
    ]
36
}
37
38 1
DATA_PREFERENCE_MAP = {
39
    SyncData.Collection:    'sync.collection.mode',
40
    SyncData.Playback:      'sync.playback.mode',
41
    SyncData.Ratings:       'sync.ratings.mode',
42
    SyncData.Watched:       'sync.watched.mode',
43
44
    # Lists
45
    SyncData.Liked:         'sync.lists.liked.mode',
46
    SyncData.Personal:      'sync.lists.personal.mode',
47
    SyncData.Watchlist:     'sync.lists.watchlist.mode',
48
}
49
50
51 1
class Mode(object):
52 1
    data = None
53 1
    mode = None
54
55 1
    children = []
56
57 1
    def __init__(self, task):
58
        self.__task = task
59
60
        self.children = [c(task) for c in self.children]
61
62
        # Retrieve enabled data
63
        self.enabled_data = self.get_enabled_data()
64
65
        # Determine if mode should be enabled
66
        self.enabled = len(self.enabled_data) > 0
67
68
        if not self.enabled:
69
            log.debug('Mode %r disabled on: %r', self.mode, self)
70
71 1
    @property
72
    def current(self):
73
        return self.__task
74
75 1
    @property
76
    def configuration(self):
77
        return self.__task.configuration
78
79 1
    @property
80
    def handlers(self):
81
        return self.__task.handlers
82
83 1
    @property
84
    def modes(self):
85
        return self.__task.modes
86
87 1
    @property
88
    def plex(self):
89
        if not self.current or not self.current.state:
90
            return None
91
92
        return self.current.state.plex
93
94 1
    @property
95
    def trakt(self):
96
        if not self.current or not self.current.state:
97
            return None
98
99
        return self.current.state.trakt
100
101 1
    def construct(self):
102
        pass
103
104 1
    def start(self):
105
        pass
106
107 1
    def run(self):
108
        raise NotImplementedError
109
110 1
    def stop(self):
111
        pass
112
113 1
    def checkpoint(self):
114
        if self.current is None:
115
            return
116
117
        self.current.checkpoint()
118
119 1
    def execute_children(self, name, force=None):
120
        # Run method on children
121
        for c in self.children:
122
            if not force and not c.enabled:
123
                log.debug('Ignoring %s() call on child: %r', name, c)
124
                continue
125
126
            # Find method `name` in child
127
            log.info('Executing %s() on child: %r', name, c)
128
129
            func = getattr(c, name, None)
130
131
            if not func:
132
                log.warn('Unknown method: %r', name)
133
                continue
134
135
            # Run method on child
136
            func()
137
138 1
    @elapsed.clock
139
    def execute_handlers(self, media, data, *args, **kwargs):
140
        if type(media) is not list:
141
            media = [media]
142
143
        if type(data) is not list:
144
            data = [data]
145
146
        for m, d in itertools.product(media, data):
147
            if d not in self.handlers:
148
                log.debug('Unable to find handler for data: %r', d)
149
                continue
150
151
            try:
152
                self.handlers[d].run(m, self.mode, *args, **kwargs)
153
            except Exception, ex:
154
                log.warn('Exception raised in handlers[%r].run(%r, ...): %s', d, m, ex, exc_info=True)
155
156 1
    def get_enabled_data(self):
157
        config = self.configuration
158
159
        # Determine accepted modes
160
        modes = [SyncMode.Full]
161
162
        if self.mode == SyncMode.Full:
163
            modes.extend([
164
                SyncMode.FastPull,
165
                SyncMode.Pull,
166
                SyncMode.Push
167
            ])
168
        elif self.mode == SyncMode.FastPull:
169
            modes.extend([
170
                self.mode,
171
                SyncMode.Pull
172
            ])
173
        else:
174
            modes.append(self.mode)
175
176
        # Retrieve enabled data
177
        result = []
178
179
        if config['sync.watched.mode'] in modes:
180
            result.append(SyncData.Watched)
181
182
        if config['sync.ratings.mode'] in modes:
183
            result.append(SyncData.Ratings)
184
185
        if config['sync.playback.mode'] in modes:
186
            result.append(SyncData.Playback)
187
188
        if config['sync.collection.mode'] in modes:
189
            result.append(SyncData.Collection)
190
191
        # Lists
192
        if config['sync.lists.watchlist.mode'] in modes:
193
            result.append(SyncData.Watchlist)
194
195
        if config['sync.lists.liked.mode'] in modes:
196
            result.append(SyncData.Liked)
197
198
        if config['sync.lists.personal.mode'] in modes:
199
            result.append(SyncData.Personal)
200
201
        # Filter `result` to data provided by this mode
202
        if self.data is None:
203
            log.warn('No "data" property defined on %r', self)
204
            return result
205
206
        if self.data == SyncData.All:
207
            return result
208
209
        return [
210
            data for data in result
211
            if data in self.data
212
        ]
213
214 1
    def get_data(self, media):
215
        for data in TRAKT_DATA_MAP[media]:
216
            if not self.is_data_enabled(data):
217
                continue
218
219
            yield data
220
221 1
    @elapsed.clock
222
    def is_data_enabled(self, data):
223
        return data in self.enabled_data
224
225 1 View Code Duplication
    @elapsed.clock
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
226
    def process_guid(self, guid):
227
        if not guid:
228
            return False, guid
229
230
        if guid.service not in GUID_SERVICES:
231
            # Try map show to a supported service (via OEM)
232
            supported, item = ModuleManager['mapper'].match(guid.service, guid.id)
233
234
            if not supported:
235
                return False, guid
236
237
            if item and item.identifiers:
238
                log.debug('[%s/%s] - Mapped to: %r', guid.service, guid.id, item)
239
240
                # Retrieve mapped show identifier
241
                service = item.identifiers.keys()[0]
242
                key = try_convert(item.identifiers[service], int, item.identifiers[service])
243
244
                # Return mapped show result
245
                return True, Guid.construct(service, key)
246
247
            log.debug('Unable to find mapping for %r', guid)
248
            return False, guid
249
250
        return True, guid
251
252 1 View Code Duplication
    @elapsed.clock
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
253
    def process_guid_episode(self, guid, season_num, episode_num):
254
        if not guid:
255
            return False, guid, season_num, episode_num
256
257
        if guid.service not in GUID_SERVICES:
258
            # Try map episode to a supported service (via OEM)
259
            supported, match = ModuleManager['mapper'].map_episode(guid, season_num, episode_num)
260
261
            if not supported:
262
                return False, guid, season_num, episode_num
263
264
            if match and match.identifiers:
265
                if match.absolute_num is not None:
266
                    log.info('Episode mappings with absolute numbers are not supported yet')
267
                    return False, guid, season_num, episode_num
268
269
                log.debug('[%s/%s] (S%02dE%02d) - Mapped to: %r', guid.service, guid.id, season_num, episode_num, match)
270
271
                # Retrieve mapped show identifier
272
                service = match.identifiers.keys()[0]
273
                key = try_convert(match.identifiers[service], int, match.identifiers[service])
274
275
                # Return mapped episode result
276
                return True, Guid.construct(service, key), match.season_num, match.episode_num
277
278
            log.debug('Unable to find mapping for %r S%02dE%02d', guid, season_num, episode_num)
279
            return False, guid, season_num, episode_num
280
281
        return True, guid, season_num, episode_num
282
283 1
    def sections(self, section_type=None):
284
        # Retrieve "section" for current task
285
        section_key = self.current.kwargs.get('section', None)
286
287
        # Fetch sections from server
288
        p_sections = Plex['library'].sections()
289
290
        if p_sections is None:
291
            return None
292
293
        # Filter sections, map to dictionary
294
        result = {}
295
296
        for section in p_sections.filter(section_type, section_key):
297
            # Apply section name filter
298
            if not Filters.is_valid_section_name(section.title):
299
                continue
300
301
            try:
302
                key = int(section.key)
303
            except Exception, ex:
304
                log.warn('Unable to cast section key %r to integer: %s', section.key, ex, exc_info=True)
305
                continue
306
307
            result[key] = section.uuid
308
309
        return [(key, ) for key in result.keys()], result
310