GuidParser.parse_episode()   F
last analyzed

Complexity

Conditions 11

Size

Total Lines 104

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 121.9122

Importance

Changes 0
Metric Value
dl 0
loc 104
ccs 1
cts 35
cp 0.0286
rs 3.1764
c 0
b 0
f 0
cc 11
crap 121.9122

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 GuidParser.parse_episode() 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 1
from oem.media.movie import MovieMatch
2 1
from plugin.core.constants import GUID_SERVICES
3 1
from plugin.core.helpers.variable import try_convert
4 1
from plugin.modules.core.manager import ModuleManager
5 1
from plugin.sync.core.guid.match import GuidMatch
6
7 1
from oem.media.show import EpisodeMatch
8 1
from plex_metadata import Guid
9 1
import logging
10
11 1
log = logging.getLogger(__name__)
12
13
14 1
class GuidParser(object):
15 1
    @classmethod
16 1
    def parse(cls, guid, episode=None):
17
        media = (
18
            GuidMatch.Media.Episode
19
            if episode else GuidMatch.Media.Movie
20
        )
21
22
        # Ensure guid is valid
23
        if not guid or not guid.valid:
24
            return GuidMatch(
25
                media, guid,
26
                invalid=True
27
            )
28
29
        # Process guid episode identifier overrides
30
        if episode and len(episode) == 2:
31
            season_num, episode_num = episode
32
33
            if guid.season is not None:
34
                episode = guid.season, episode_num
35
36
        # Process natively supported guid services
37
        if guid.service in GUID_SERVICES:
38
            episodes = None
39
40
            if episode and len(episode) == 2:
41
                episodes = [episode]
42
43
            return GuidMatch(
44
                media, guid,
45
                episodes=episodes,
46
                supported=True,
47
                found=True
48
            )
49
50
        # Process episode
51
        if episode:
52
            return cls.parse_episode(guid, episode)
53
54
        # Process shows + movies
55
        supported, (service, key) = ModuleManager['mapper'].id(
56
            guid.service, guid.id,
57
            resolve_mappings=False
58
        )
59
60
        # Validate match
61
        if not supported:
62
            return GuidMatch(media, guid)
63
64
        if not service or not key:
65
            return GuidMatch(media, guid, supported=True)
66
67
        # Validate identifier
68
        if type(key) is list:
69
            log.info('[%s/%s] - List keys are not supported', guid.service, guid.id)
70
            return GuidMatch(media, guid, supported=True)
71
72
        if type(key) not in [int, str]:
73
            log.info('[%s/%s] - Unsupported key: %r', guid.service, guid.id, key)
74
            return GuidMatch(media, guid, supported=True)
75
76
        log.debug('[%s/%s] - Mapped to: %s/%s', guid.service, guid.id, service, key)
77
78
        # Return movie/show match
79
        return GuidMatch(
80
            media, Guid.construct(service, key, matched=True),
81
            supported=True,
82
            found=True
83
        )
84
85 1
    @classmethod
86
    def parse_episode(cls, guid, (season_num, episode_num)):
87
        episodes = [(season_num, episode_num)]
88
89
        # Map episode to a supported service (via OEM)
90
        supported, match = ModuleManager['mapper'].map_episode(
91
            guid, season_num, episode_num,
92
            resolve_mappings=False
93
        )
94
95
        # Validate match
96
        if not supported:
97
            return GuidMatch(
98
                GuidMatch.Media.Episode, guid,
99
                episodes=episodes
100
            )
101
102
        if not match or not match.identifiers:
103
            log.debug('Unable to find mapping for %r S%02dE%02d', guid, season_num, episode_num)
104
            return GuidMatch(
105
                GuidMatch.Media.Episode, guid,
106
                episodes=episodes,
107
                supported=True
108
            )
109
110
        # Retrieve identifier
111
        service = match.identifiers.keys()[0]
112
        key = match.identifiers[service]
113
114
        if type(key) is list:
115
            log.info('[%s/%s] - List keys are not supported', guid.service, guid.id)
116
            return GuidMatch(
117
                GuidMatch.Media.Episode, guid,
118
                episodes=episodes,
119
                supported=True
120
            )
121
122
        # Cast `key` numbers to integers
123
        key = try_convert(key, int, key)
124
125
        # Validate show identifier
126
        if type(key) not in [int, str]:
127
            log.info('[%s/%s] - Unsupported key: %r', guid.service, guid.id, key)
128
            return GuidMatch(
129
                GuidMatch.Media.Episode, guid,
130
                episodes=episodes,
131
                supported=True
132
            )
133
134
        # Process episode matches
135
        if isinstance(match, EpisodeMatch):
136
            # Ensure match doesn't include an absolute number
137
            if match.absolute_num is not None:
138
                log.info('[%s/%s] - Episode mappings with absolute numbers are not supported', guid.service, guid.id)
139
                return GuidMatch(
140
                    GuidMatch.Media.Episode, guid,
141
                    episodes=episodes,
142
                    supported=True
143
                )
144
145
            # Update `episodes` list
146
            if match.mappings:
147
                # Use match mappings
148
                episodes = []
149
150
                for mapping in match.mappings:
151
                    log.debug('[%s/%s] (S%02dE%02d) - Mapped to: %r', guid.service, guid.id, season_num, episode_num, mapping)
152
                    episodes.append((
153
                        int(mapping.season),
154
                        int(mapping.number)
155
                    ))
156
            else:
157
                # Use match identifier
158
                log.debug('[%s/%s] (S%02dE%02d) - Mapped to: %r', guid.service, guid.id, season_num, episode_num, match)
159
                episodes = [(
160
                    int(match.season_num),
161
                    int(match.episode_num)
162
                )]
163
164
            # Return episode match
165
            return GuidMatch(
166
                GuidMatch.Media.Episode, Guid.construct(service, key, matched=True),
167
                episodes=episodes,
168
                supported=True,
169
                found=True
170
            )
171
172
        # Process movie matches
173
        if isinstance(match, MovieMatch):
174
            log.debug('[%s/%s] (S%02dE%02d) - Mapped to: %r', guid.service, guid.id, season_num, episode_num, match)
175
176
            # Return movie match
177
            return GuidMatch(
178
                GuidMatch.Media.Movie, Guid.construct(service, key, matched=True),
179
                supported=True,
180
                found=True
181
            )
182
183
        # Unknown value for `match` returned
184
        log.warn('Unknown match returned: %r', match)
185
        return GuidMatch(
186
            GuidMatch.Media.Episode, guid,
187
            episodes=episodes,
188
            supported=True
189
        )
190