Passed
Push — develop ( b585db...289973 )
by Dean
03:02
created

Shows.execute_episode_action()   B

Complexity

Conditions 3

Size

Total Lines 35

Duplication

Lines 35
Ratio 100 %

Code Coverage

Tests 1
CRAP Score 9.762

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 35
loc 35
ccs 1
cts 11
cp 0.0909
rs 8.8571
cc 3
crap 9.762
1 1
from plugin.sync.core.enums import SyncData, SyncMedia, SyncMode
2 1
from plugin.sync.core.guid import GuidMatch, GuidParser
3 1
from plugin.sync.modes.core.base import log_unsupported, mark_unsupported
4 1
from plugin.sync.modes.fast_pull.base import Base
5
6 1
from plex_database.models import MetadataItem
7 1
import elapsed
8 1
import logging
9
10 1
log = logging.getLogger(__name__)
11
12
13 1
class Shows(Base):
14 1
    data = [
15
        SyncData.Collection,
16
        SyncData.Playback,
17
        SyncData.Ratings,
18
        SyncData.Watched
19
    ]
20
21 1
    def __init__(self, task):
22
        super(Shows, self).__init__(task)
23
24
        # Sections
25
        self.p_sections = None
26
        self.p_sections_map = None
27
28
        # Shows
29
        self.p_shows = None
30
        self.p_shows_count = None
31
        self.p_shows_unsupported = None
32
33
        # Seasons
34
        self.p_seasons = None
35
36
        # Episodes
37
        self.p_episodes = None
38
        self.p_episodes_count = None
39
40 1
    @elapsed.clock
41
    def construct(self):
42
        # Retrieve show sections
43
        self.p_sections, self.p_sections_map = self.sections('show')
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are trying to unpack a non-sequence, which was defined at line 303 of plugin.sync.modes.core.base.mode.
Loading history...
44
45
        # Determine number of shows that will be processed
46
        self.p_shows_count = self.plex.library.shows.count(
47
            self.p_sections
48
        )
49
50
        # Determine number of shows that will be processed
51
        self.p_episodes_count = self.plex.library.episodes.count_items(
52
            self.p_sections
53
        )
54
55
        # Increment progress steps total
56
        self.current.progress.group(Shows, 'shows').add(self.p_shows_count)
57
        self.current.progress.group(Shows, 'episodes').add(self.p_episodes_count)
58
59 1
    @elapsed.clock
60
    def start(self):
61
        # Fetch episodes with account settings
62
        self.p_shows, self.p_seasons, self.p_episodes = self.plex.library.episodes.mapped(
63
            self.p_sections, ([
64
                MetadataItem.library_section,
65
                MetadataItem.added_at
66
            ], [], [
67
                MetadataItem.added_at
68
            ]),
69
            account=self.current.account.plex.key,
70
            parse_guid=True
71
        )
72
73
        # Reset state
74
        self.p_shows_unsupported = {}
75
76
    #
77
    # Run
78
    #
79
80 1
    @elapsed.clock
81
    def run(self):
82
        # TODO process seasons
0 ignored issues
show
Coding Style introduced by
TODO and FIXME comments should generally be avoided.
Loading history...
83
        self.run_shows()
84
        self.run_episodes()
85
86 View Code Duplication
        # Log details
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
87
        log_unsupported(log, 'Found %d unsupported show(s)', self.p_shows_unsupported)
88
89 1
    def run_shows(self):
90
        # Iterate over plex shows
91
        for sh_id, guid, p_show in self.p_shows:
92
            # Increment one step
93
            self.current.progress.group(Shows, 'shows').step()
94
95
            # Parse guid
96
            match = GuidParser.parse(guid)
97
98
            if not match.supported:
99
                mark_unsupported(self.p_shows_unsupported, sh_id, guid)
100
                continue
101
102
            if not match.found:
103
                log.info('Unable to find identifier for: %s/%s (rating_key: %r)', guid.service, guid.id, sh_id)
104
                continue
105
106
            # Process show
107
            self.run_show(sh_id, match, p_show)
108
109
        # Stop progress group
110
        self.current.progress.group(Shows, 'shows').stop()
111
112 1
    def run_show(self, sh_id, match, p_show):
113
        key = (match.guid.service, match.guid.id)
114
115
        # Try retrieve `pk` for `key`
116
        pk = self.trakt.table('shows').get(key)
117
118
        # Store in item map
119
        self.current.map.add(p_show.get('library_section'), sh_id, [key, pk])
120
121
        if pk is None:
122
            # No `pk` found
123
            return
124
125
        # Run pull handlers if the item has been added recently
126 View Code Duplication
        if self.should_pull(sh_id, p_show.get('added_at')):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
127
            log.info('Show %r has been added recently, running pull sync instead', sh_id)
128
129
            # Execute handlers
130
            for data in self.get_data(SyncMedia.Shows):
131
                t_show = self.trakt[(SyncMedia.Shows, data)].get(pk)
132
133
                # Execute show handlers
134
                self.execute_handlers(
135
                    SyncMode.Pull, SyncMedia.Shows, data,
136
                    key=sh_id,
137
138
                    p_item=p_show,
139
                    t_item=t_show
140
                )
141
        else:
142
            # Execute handlers for changed data
143
            for data, action, t_show in self.iter_changes(SyncMedia.Shows, pk):
144
                # Execute show handlers
145
                self.execute_handlers(
146
                    self.mode, SyncMedia.Shows, data,
147
                    action=action,
148
149
                    key=sh_id,
150
151
                    p_item=p_show,
152
                    t_item=t_show
153
                )
154
155 1
    def run_episodes(self):
156
        # Iterate over plex episodes
157
        for ids, guid, (season_num, episode_num), p_show, p_season, p_episode in self.p_episodes:
0 ignored issues
show
Unused Code introduced by
The variable p_season seems to be unused.
Loading history...
158
            # Increment one step
159
            self.current.progress.group(Shows, 'episodes').step()
160
161
            # Process `p_guid` (map + validate)
162
            match = GuidParser.parse(guid, (season_num, episode_num))
163
164
            if not match.supported:
165
                mark_unsupported(self.p_shows_unsupported, ids['show'], guid)
166
                continue
167
168
            if not match.found:
169
                log.info('Unable to find identifier for: %s/%s (rating_key: %r)', guid.service, guid.id, ids['show'])
170
                continue
171
172
            # Process episode
173
            self.run_episode(ids, match, p_show, p_episode)
174
175
            # Task checkpoint
176
            self.checkpoint()
177
178
        # Stop progress group
179
        self.current.progress.group(Shows, 'episodes').stop()
180
181 1
    def run_episode(self, ids, match, p_show, p_episode):
182
        key = (match.guid.service, match.guid.id)
183
184
        # Determine media type
185
        if match.media == GuidMatch.Media.Movie:
186
            c_media = 'movies'
187
            s_media = SyncMedia.Movies
188
        elif match.media == GuidMatch.Media.Episode:
189
            c_media = 'shows'
190
            s_media = SyncMedia.Episodes
191
        else:
192
            raise ValueError('Unknown match media type: %r' % (match.media,))
193
194
        # Try retrieve `pk` for `key`
195
        pk = self.trakt.table(c_media).get(key)
196
197
        if pk is None:
198
            return
199
200
        if not ids.get('episode'):
201
            return
202
203
        # Run pull handlers if the item has been added recently
204
        if self.should_pull(ids['episode'], p_episode.get('added_at')):
205
            log.info('Episode %r has been added recently, running pull sync instead', ids['episode'])
206
207
            # Execute handlers
208
            for data in self.get_data(s_media):
209
                t_item = self.trakt[(s_media, data)].get(pk)
210
211
                if t_item is None:
212
                    continue
213
214
                self.run_episode_action(
215
                    SyncMode.Pull, data, ids, match,
216
                    p_show, p_episode, t_item
217
                )
218
        else:
219
            # Execute handlers for changed data
220
            for data, action, t_item in self.iter_changes(s_media, pk):
221
                self.run_episode_action(
222
                    self.mode, data, ids, match,
223
                    p_show, p_episode, t_item,
224
                    action=action
225
                )
226