| 1 | 1 |  | from plugin.core.constants import GUID_SERVICES | 
            
                                                                                                            
                            
            
                                    
            
            
                | 2 | 1 |  | from plugin.core.helpers.variable import dict_path | 
            
                                                                                                            
                            
            
                                    
            
            
                | 3 | 1 |  | from plugin.models import * | 
            
                                                                                                            
                            
            
                                    
            
            
                | 4 | 1 |  | from plugin.preferences import Preferences | 
            
                                                                                                            
                            
            
                                    
            
            
                | 5 | 1 |  | from plugin.sync.core.enums import SyncActionMode, SyncData | 
            
                                                                                                            
                            
            
                                    
            
            
                | 6 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 7 | 1 |  | from datetime import datetime, timedelta | 
            
                                                                                                            
                            
            
                                    
            
            
                | 8 | 1 |  | from trakt import Trakt | 
            
                                                                                                            
                            
            
                                    
            
            
                | 9 | 1 |  | from trakt_sync.cache.main import Cache | 
            
                                                                                                            
                            
            
                                    
            
            
                | 10 | 1 |  | import elapsed | 
            
                                                                                                            
                            
            
                                    
            
            
                | 11 | 1 |  | import logging | 
            
                                                                                                            
                            
            
                                    
            
            
                | 12 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 13 | 1 |  | log = logging.getLogger(__name__) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 14 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 15 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 16 | 1 |  | class SyncArtifacts(object): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 17 | 1 |  |     def __init__(self, task): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 18 | 1 |  |         self.task = task | 
            
                                                                                                            
                            
            
                                    
            
            
                | 19 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 20 | 1 |  |         self.artifacts = {} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 21 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 22 |  |  |     # | 
            
                                                                                                            
                            
            
                                    
            
            
                | 23 |  |  |     # Log/Send artifacts | 
            
                                                                                                            
                            
            
                                    
            
            
                | 24 |  |  |     # | 
            
                                                                                                            
                            
            
                                    
            
            
                | 25 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 26 | 1 |  |     def send(self): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 27 |  |  |         action_mode = self.task.configuration['sync.action.mode'] | 
            
                                                                                                            
                            
            
                                    
            
            
                | 28 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 29 |  |  |         if action_mode == SyncActionMode.Update: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 30 |  |  |             self.send_actions() | 
            
                                                                                                            
                            
            
                                    
            
            
                | 31 |  |  |             return True | 
            
                                                                                                            
                            
            
                                    
            
            
                | 32 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 33 |  |  |         if action_mode == SyncActionMode.Log: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 34 |  |  |             self.log_actions() | 
            
                                                                                                            
                            
            
                                    
            
            
                | 35 |  |  |             return True | 
            
                                                                                                            
                            
            
                                    
            
            
                | 36 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 37 |  |  |         raise NotImplementedError('Unable to send artifacts to trakt, action mode %r not supported', action_mode) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 38 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 39 | 1 |  |     @elapsed.clock | 
            
                                                                                                            
                            
            
                                    
            
            
                | 40 |  |  |     def send_actions(self): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 41 |  |  |         changes = False | 
            
                                                                                                            
                            
            
                                    
            
            
                | 42 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 43 |  |  |         for data, action, request in self.flatten(): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 44 |  |  |             changes = True | 
            
                                                                                                            
                            
            
                                    
            
            
                | 45 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 46 |  |  |             # Send artifact to trakt.tv | 
            
                                                                                                            
                            
            
                                    
            
            
                | 47 |  |  |             self.send_action(data, action, **request) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 48 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 49 |  |  |             # Invalidate cache to ensure actions aren't resent | 
            
                                                                                                            
                            
            
                                    
            
            
                | 50 |  |  |             for key, value in request.items(): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 51 |  |  |                 if not value: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 52 |  |  |                     # Empty media request | 
            
                                                                                                            
                            
            
                                    
            
            
                | 53 |  |  |                     continue | 
            
                                                                                                            
                            
            
                                    
            
            
                | 54 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 55 |  |  |                 if key == 'shows': | 
            
                                                                                                            
                            
            
                                    
            
            
                | 56 |  |  |                     media = Cache.Media.Shows | 
            
                                                                                                            
                            
            
                                    
            
            
                | 57 |  |  |                 elif key == 'movies': | 
            
                                                                                                            
                            
            
                                    
            
            
                | 58 |  |  |                     media = Cache.Media.Movies | 
            
                                                                                                            
                            
            
                                    
            
            
                | 59 |  |  |                 else: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 60 |  |  |                     # Unknown media type | 
            
                                                                                                            
                            
            
                                    
            
            
                | 61 |  |  |                     continue | 
            
                                                                                                            
                            
            
                                    
            
            
                | 62 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 63 |  |  |                 self.task.state.trakt.invalidate( | 
            
                                                                                                            
                            
            
                                    
            
            
                | 64 |  |  |                     Cache.Media.get(media), | 
            
                                                                                                            
                            
            
                                    
            
            
                | 65 |  |  |                     Cache.Data.get(data) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 66 |  |  |                 ) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 67 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 68 |  |  |             # Task checkpoint | 
            
                                                                                                            
                            
            
                                    
            
            
                | 69 |  |  |             self.task.checkpoint() | 
            
                                                                                                            
                            
            
                                    
            
            
                | 70 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 71 |  |  |         if not changes: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 72 |  |  |             log.info('trakt.tv profile is up-to-date') | 
            
                                                                                                            
                            
            
                                    
            
            
                | 73 |  |  |             return | 
            
                                                                                                            
                            
            
                                    
            
            
                | 74 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 75 |  |  |         log.info('trakt.tv profile has been updated') | 
            
                                                                                                            
                                                                
            
                                    
            
            
                | 76 |  |  |  | 
            
                                                                        
                            
            
                                    
            
            
                | 77 | 1 |  |     @classmethod | 
            
                                                                        
                            
            
                                    
            
            
                | 78 |  |  |     def send_action(cls, data, action, **kwargs): | 
            
                                                                        
                            
            
                                    
            
            
                | 79 |  |  |         # Ensure items exist in `kwargs` | 
            
                                                                        
                            
            
                                    
            
            
                | 80 |  |  |         if not kwargs: | 
            
                                                                        
                            
            
                                    
            
            
                | 81 |  |  |             return False | 
            
                                                                        
                            
            
                                    
            
            
                | 82 |  |  |  | 
            
                                                                        
                            
            
                                    
            
            
                | 83 |  |  |         if not kwargs.get('movies') and not kwargs.get('shows'): | 
            
                                                                        
                            
            
                                    
            
            
                | 84 |  |  |             return False | 
            
                                                                        
                            
            
                                    
            
            
                | 85 |  |  |  | 
            
                                                                        
                            
            
                                    
            
            
                | 86 |  |  |         # Try retrieve interface for `data` | 
            
                                                                        
                            
            
                                    
            
            
                | 87 |  |  |         interface = cls._get_interface(data) | 
            
                                                                        
                            
            
                                    
            
            
                | 88 |  |  |  | 
            
                                                                        
                            
            
                                    
            
            
                | 89 |  |  |         if interface is None: | 
            
                                                                        
                            
            
                                    
            
            
                | 90 |  |  |             log.warn('[%s](%s) Unknown data type', data, action) | 
            
                                                                        
                            
            
                                    
            
            
                | 91 |  |  |             return False | 
            
                                                                        
                            
            
                                    
            
            
                | 92 |  |  |  | 
            
                                                                        
                            
            
                                    
            
            
                | 93 |  |  |         # Try retrieve method for `action` | 
            
                                                                        
                            
            
                                    
            
            
                | 94 |  |  |         func = getattr(Trakt[interface], action, None) | 
            
                                                                        
                            
            
                                    
            
            
                | 95 |  |  |  | 
            
                                                                        
                            
            
                                    
            
            
                | 96 |  |  |         if func is None: | 
            
                                                                        
                            
            
                                    
            
            
                | 97 |  |  |             log.warn('[%s](%s) Unable find action in interface', data, action) | 
            
                                                                        
                            
            
                                    
            
            
                | 98 |  |  |             return False | 
            
                                                                        
                            
            
                                    
            
            
                | 99 |  |  |  | 
            
                                                                        
                            
            
                                    
            
            
                | 100 |  |  |         # Send request to trakt.tv | 
            
                                                                        
                            
            
                                    
            
            
                | 101 |  |  |         response = func(kwargs) | 
            
                                                                        
                            
            
                                    
            
            
                | 102 |  |  |  | 
            
                                                                        
                            
            
                                    
            
            
                | 103 |  |  |         if response is None: | 
            
                                                                        
                            
            
                                    
            
            
                | 104 |  |  |             return False | 
            
                                                                        
                            
            
                                    
            
            
                | 105 |  |  |  | 
            
                                                                        
                            
            
                                    
            
            
                | 106 |  |  |         log.debug('[%s](%s) Response: %r', data, action, response) | 
            
                                                                        
                            
            
                                    
            
            
                | 107 |  |  |         return True | 
            
                                                                                                            
                            
            
                                    
            
            
                | 108 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 109 | 1 |  |     def log_actions(self): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 110 |  |  |         for data, action, request in self.flatten(): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 111 |  |  |             # Try retrieve interface for `data` | 
            
                                                                                                            
                            
            
                                    
            
            
                | 112 |  |  |             interface = self._get_interface(data) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 113 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 114 |  |  |             if interface is None: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 115 |  |  |                 log.warn('[%s](%s) Unknown data type', data, action) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 116 |  |  |                 continue | 
            
                                                                                                            
                            
            
                                    
            
            
                | 117 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 118 |  |  |             # Log request items | 
            
                                                                                                            
                            
            
                                    
            
            
                | 119 |  |  |             for media, items in request.items(): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 120 |  |  |                 self.log_items(interface, action, media, items) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 121 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 122 | 1 |  |     def log_items(self, interface, action, media, items): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 123 |  |  |         if not items: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 124 |  |  |             return | 
            
                                                                                                            
                            
            
                                    
            
            
                | 125 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 126 |  |  |             # Log each item | 
            
                                                                                                            
                            
            
                                    
            
            
                | 127 |  |  |         for item in items: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 128 |  |  |             if not item: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 129 |  |  |                 continue | 
            
                                                                                                            
                            
            
                                    
            
            
                | 130 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 131 |  |  |             log.info('[%s:%s](%s) %r (%r)', interface, action, media, item.get('title'), item.get('year')) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 132 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 133 |  |  |             if media == 'shows': | 
            
                                                                                                            
                            
            
                                    
            
            
                | 134 |  |  |                 # Log each episode | 
            
                                                                                                            
                            
            
                                    
            
            
                | 135 |  |  |                 self.log_episodes(item) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 136 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 137 | 1 |  |     def log_episodes(self, item): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 138 |  |  |         for season in item.get('seasons', []): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 139 |  |  |             episodes = season.get('episodes') | 
            
                                                                                                            
                            
            
                                    
            
            
                | 140 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 141 |  |  |             if episodes is None: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 142 |  |  |                 log.info('    S%02d', season.get('number')) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 143 |  |  |                 continue | 
            
                                                                                                            
                            
            
                                    
            
            
                | 144 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 145 |  |  |             for episode in episodes: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 146 |  |  |                 log.info('    S%02dE%02d', season.get('number'), episode.get('number')) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 147 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 148 | 1 |  |     @staticmethod | 
            
                                                                                                            
                            
            
                                    
            
            
                | 149 |  |  |     def _get_interface(data): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 150 |  |  |         # Try retrieve interface for `data` | 
            
                                                                                                            
                            
            
                                    
            
            
                | 151 |  |  |         interface = Cache.Data.get_interface(data) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 152 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 153 |  |  |         if interface == 'sync/watched': | 
            
                                                                                                            
                            
            
                                    
            
            
                | 154 |  |  |             # Watched add/remove functions are on the "sync/history" interface | 
            
                                                                                                            
                            
            
                                    
            
            
                | 155 |  |  |             return 'sync/history' | 
            
                                                                                                            
                            
            
                                    
            
            
                | 156 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 157 |  |  |         return interface | 
            
                                                                                                            
                            
            
                                    
            
            
                | 158 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 159 |  |  |     # | 
            
                                                                                                            
                            
            
                                    
            
            
                | 160 |  |  |     # Artifact storage | 
            
                                                                                                            
                            
            
                                    
            
            
                | 161 |  |  |     # | 
            
                                                                                                            
                            
            
                                    
            
            
                | 162 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 163 | 1 |  |     def store_show(self, data, action, guid, p_show=None, **kwargs): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 164 |  |  |         key = (guid.service, guid.id) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 165 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 166 |  |  |         shows = dict_path(self.artifacts, [ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 167 |  |  |             data, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 168 |  |  |             action, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 169 |  |  |             'shows' | 
            
                                                                                                            
                            
            
                                    
            
            
                | 170 |  |  |         ]) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 171 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 172 |  |  |         # Build show | 
            
                                                                                                            
                            
            
                                    
            
            
                | 173 |  |  |         if key in shows: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 174 |  |  |             show = shows[key] | 
            
                                                                                                            
                            
            
                                    
            
            
                | 175 |  |  |         else: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 176 |  |  |             show = self._build_request(guid, p_show, **kwargs) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 177 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 178 |  |  |             if show is None: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 179 |  |  |                 return False | 
            
                                                                                                            
                            
            
                                    
            
            
                | 180 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 181 |  |  |             # Store `show` in artifacts | 
            
                                                                                                            
                            
            
                                    
            
            
                | 182 |  |  |             shows[key] = show | 
            
                                                                                                            
                            
            
                                    
            
            
                | 183 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 184 |  |  |         # Set `kwargs` on `show` | 
            
                                                                                                            
                            
            
                                    
            
            
                | 185 |  |  |         self._set_kwargs(show, kwargs) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 186 |  |  |         return True | 
            
                                                                                                            
                            
            
                                    
            
            
                | 187 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 188 | 1 |  |     def store_episode(self, data, action, guid, identifier, p_key=None, p_show=None, p_episode=None, **kwargs): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 189 | 1 |  |         key = (guid.service, guid.id) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 190 | 1 |  |         season_num, episode_num = identifier | 
            
                                                                                                            
                            
            
                                    
            
            
                | 191 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 192 | 1 |  |         shows = dict_path(self.artifacts, [ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 193 |  |  |             data, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 194 |  |  |             action, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 195 |  |  |             'shows' | 
            
                                                                                                            
                            
            
                                    
            
            
                | 196 |  |  |         ]) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 197 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 198 |  |  |         # Check for duplicate history addition | 
            
                                                                                                            
                            
            
                                    
            
            
                | 199 | 1 |  |         if self._is_duplicate(data, action, p_key): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 200 |  |  |             return False | 
            
                                                                                                            
                            
            
                                    
            
            
                | 201 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 202 |  |  |         # Build show | 
            
                                                                                                            
                            
            
                                    
            
            
                | 203 | 1 |  |         if key in shows: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 204 |  |  |             show = shows[key] | 
            
                                                                                                            
                            
            
                                    
            
            
                | 205 |  |  |         else: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 206 | 1 |  |             show = self._build_request(guid, p_show) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 207 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 208 | 1 |  |             if show is None: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 209 |  |  |                 return False | 
            
                                                                                                            
                            
            
                                    
            
            
                | 210 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 211 | 1 |  |             shows[key] = show | 
            
                                                                                                            
                            
            
                                    
            
            
                | 212 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 213 |  |  |         # Ensure 'seasons' attribute exists | 
            
                                                                                                            
                            
            
                                    
            
            
                | 214 | 1 |  |         if 'seasons' not in show: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 215 | 1 |  |             show['seasons'] = {} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 216 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 217 |  |  |         # Build season | 
            
                                                                                                            
                            
            
                                    
            
            
                | 218 | 1 |  |         if season_num in show['seasons']: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 219 |  |  |             season = show['seasons'][season_num] | 
            
                                                                                                            
                            
            
                                    
            
            
                | 220 |  |  |         else: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 221 | 1 |  |             season = show['seasons'][season_num] = {'number': season_num} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 222 | 1 |  |             season['episodes'] = {} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 223 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 224 |  |  |         # Build episode | 
            
                                                                                                            
                            
            
                                    
            
            
                | 225 | 1 |  |         if episode_num in season['episodes']: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 226 |  |  |             episode = season['episodes'][episode_num] | 
            
                                                                                                            
                            
            
                                    
            
            
                | 227 |  |  |         else: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 228 | 1 |  |             episode = season['episodes'][episode_num] = {'number': episode_num} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 229 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 230 |  |  |         # Set `kwargs` on `episode` | 
            
                                                                                                            
                            
            
                                    
            
            
                | 231 | 1 |  |         self._set_kwargs(episode, kwargs) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 232 | 1 |  |         return True | 
            
                                                                                                            
                            
            
                                    
            
            
                | 233 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 234 | 1 |  |     def store_movie(self, data, action, guid, p_key=None, p_movie=None, **kwargs): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 235 | 1 |  |         key = (guid.service, guid.id) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 236 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 237 | 1 |  |         movies = dict_path(self.artifacts, [ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 238 |  |  |             data, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 239 |  |  |             action, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 240 |  |  |             'movies' | 
            
                                                                                                            
                            
            
                                    
            
            
                | 241 |  |  |         ]) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 242 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 243 |  |  |         # Check for duplicate history addition | 
            
                                                                                                            
                            
            
                                    
            
            
                | 244 | 1 |  |         if self._is_duplicate(data, action, p_key): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 245 |  |  |             return False | 
            
                                                                                                            
                            
            
                                    
            
            
                | 246 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 247 |  |  |         # Build movie | 
            
                                                                                                            
                            
            
                                    
            
            
                | 248 | 1 |  |         if key in movies: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 249 |  |  |             movie = movies[key] | 
            
                                                                                                            
                            
            
                                    
            
            
                | 250 |  |  |         else: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 251 | 1 |  |             movie = self._build_request(guid, p_movie, **kwargs) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 252 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 253 | 1 |  |             if movie is None: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 254 |  |  |                 return False | 
            
                                                                                                            
                            
            
                                    
            
            
                | 255 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 256 |  |  |             # Store `movie` in artifacts | 
            
                                                                                                            
                            
            
                                    
            
            
                | 257 | 1 |  |             movies[key] = movie | 
            
                                                                                                            
                            
            
                                    
            
            
                | 258 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 259 |  |  |         # Set `kwargs` on `movie` | 
            
                                                                                                            
                            
            
                                    
            
            
                | 260 | 1 |  |         self._set_kwargs(movie, kwargs) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 261 | 1 |  |         return True | 
            
                                                                                                            
                            
            
                                    
            
            
                | 262 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 263 | 1 |  |     @classmethod | 
            
                                                                                                            
                            
            
                                    
            
            
                | 264 |  |  |     def _build_request(cls, guid, p_item, **kwargs): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 265 |  |  |         # Validate request | 
            
                                                                                                            
                            
            
                                    
            
            
                | 266 | 1 |  |         if not cls._validate_request(guid, p_item): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 267 |  |  |             return None | 
            
                                                                                                            
                            
            
                                    
            
            
                | 268 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 269 |  |  |         # Build request | 
            
                                                                                                            
                            
            
                                    
            
            
                | 270 | 1 |  |         request = { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 271 |  |  |             'ids': {} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 272 |  |  |         } | 
            
                                                                                                            
                            
            
                                    
            
            
                | 273 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 274 |  |  |         # Set identifier | 
            
                                                                                                            
                            
            
                                    
            
            
                | 275 | 1 |  |         request['ids'][guid.service] = guid.id | 
            
                                                                                                            
                            
            
                                    
            
            
                | 276 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 277 |  |  |         # Set extra attributes | 
            
                                                                                                            
                            
            
                                    
            
            
                | 278 | 1 |  |         cls._set_kwargs(request, kwargs) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 279 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 280 | 1 |  |         return request | 
            
                                                                                                            
                            
            
                                    
            
            
                | 281 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 282 | 1 |  |     def _is_duplicate(self, data, action, p_key): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 283 | 1 |  |         if data != SyncData.Watched or action != 'add': | 
            
                                                                                                            
                            
            
                                    
            
            
                | 284 | 1 |  |             return False | 
            
                                                                                                            
                            
            
                                    
            
            
                | 285 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 286 |  |  |         # Retrieve scrobble duplication period | 
            
                                                                                                            
                            
            
                                    
            
            
                | 287 |  |  |         duplication_period = Preferences.get('scrobble.duplication_period') | 
            
                                                                                                            
                            
            
                                    
            
            
                | 288 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 289 |  |  |         if duplication_period is None: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 290 |  |  |             return False | 
            
                                                                                                            
                            
            
                                    
            
            
                | 291 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 292 |  |  |         # Check for duplicate scrobbles in `duplication_period` | 
            
                                                                                                            
                            
            
                                    
            
            
                | 293 |  |  |         scrobbled = ActionHistory.has_scrobbled( | 
            
                                                                                                            
                            
            
                                    
            
            
                | 294 |  |  |             self.task.account, p_key, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 295 |  |  |             after=datetime.utcnow() - timedelta(minutes=duplication_period) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 296 |  |  |         ) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 297 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 298 |  |  |         if scrobbled: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 299 |  |  |             log.info( | 
            
                                                                                                            
                            
            
                                    
            
            
                | 300 |  |  |                 'Ignoring duplicate history addition, scrobble already performed in the last %d minutes', | 
            
                                                                                                            
                            
            
                                    
            
            
                | 301 |  |  |                 duplication_period | 
            
                                                                                                            
                            
            
                                    
            
            
                | 302 |  |  |             ) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 303 |  |  |             return True | 
            
                                                                                                            
                            
            
                                    
            
            
                | 304 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 305 |  |  |         return False | 
            
                                                                                                            
                            
            
                                    
            
            
                | 306 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 307 | 1 |  |     @classmethod | 
            
                                                                                                            
                            
            
                                    
            
            
                | 308 |  |  |     def _validate_request(cls, guid, p_item): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 309 |  |  |         # Build item identifier | 
            
                                                                                                            
                            
            
                                    
            
            
                | 310 | 1 |  |         if p_item: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 311 | 1 |  |             identifier = '<%r (%r)>' % (p_item.get('title'), p_item.get('year')) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 312 |  |  |         else: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 313 |  |  |             identifier = repr(guid) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 314 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 315 |  |  |         # Validate parameters | 
            
                                                                                                            
                            
            
                                    
            
            
                | 316 | 1 |  |         if p_item is not None and (not p_item.get('title') or not p_item.get('year')): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 317 |  |  |             log.info('Invalid "title" or "year" attribute on %s', identifier) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 318 |  |  |             return False | 
            
                                                                                                            
                            
            
                                    
            
            
                | 319 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 320 | 1 |  |         if not guid: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 321 |  |  |             log.warn('Invalid GUID attribute on %s', identifier) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 322 |  |  |             return False | 
            
                                                                                                            
                            
            
                                    
            
            
                | 323 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 324 | 1 |  |         if not guid or guid.service not in GUID_SERVICES: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 325 |  |  |             log.warn('GUID service %r is not supported on %s', guid.service if guid else None, identifier) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 326 |  |  |             return False | 
            
                                                                                                            
                            
            
                                    
            
            
                | 327 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 328 | 1 |  |         return True | 
            
                                                                                                            
                            
            
                                    
            
            
                | 329 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 330 | 1 |  |     @staticmethod | 
            
                                                                                                            
                            
            
                                    
            
            
                | 331 |  |  |     def _set_kwargs(request, kwargs): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 332 | 1 |  |         for key, value in kwargs.items(): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 333 | 1 |  |             if type(value) is datetime: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 334 | 1 |  |                 try: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 335 |  |  |                     # Convert `datetime` object to string | 
            
                                                                                                            
                            
            
                                    
            
            
                | 336 | 1 |  |                     value = value.strftime('%Y-%m-%dT%H:%M:%S') + '.000-00:00' | 
            
                                                                                                            
                            
            
                                    
            
            
                | 337 |  |  |                 except Exception, ex: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 338 |  |  |                     log.warn('Unable to convert %r to string', value) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 339 |  |  |                     return False | 
            
                                                                                                            
                            
            
                                    
            
            
                | 340 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 341 | 1 |  |             request[key] = value | 
            
                                                                                                            
                            
            
                                    
            
            
                | 342 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 343 | 1 |  |         return True | 
            
                                                                                                            
                            
            
                                    
            
            
                | 344 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 345 |  |  |     # | 
            
                                                                                                            
                            
            
                                    
            
            
                | 346 |  |  |     # Flatten | 
            
                                                                                                            
                            
            
                                    
            
            
                | 347 |  |  |     # | 
            
                                                                                                            
                            
            
                                    
            
            
                | 348 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 349 | 1 |  |     def flatten(self): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 350 |  |  |         for data, actions in self.artifacts.items(): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 351 |  |  |             for action, request in actions.items(): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 352 |  |  |                 if 'shows' in request: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 353 |  |  |                     request['shows'] = list(self.flatten_shows(request['shows'])) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 354 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 355 |  |  |                 if 'movies' in request: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 356 |  |  |                     request['movies'] = request['movies'].values() | 
            
                                                                                                            
                            
            
                                    
            
            
                | 357 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 358 |  |  |                 yield data, action, request | 
            
                                                                                                            
                            
            
                                    
            
            
                | 359 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 360 | 1 |  |     @staticmethod | 
            
                                                                                                            
                            
            
                                    
            
            
                | 361 |  |  |     def flatten_shows(shows): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 362 |  |  |         for show in shows.itervalues(): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 363 |  |  |             if 'seasons' not in show: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 364 |  |  |                 yield show | 
            
                                                                                                            
                            
            
                                    
            
            
                | 365 |  |  |                 continue | 
            
                                                                                                            
                            
            
                                    
            
            
                | 366 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 367 |  |  |             show['seasons'] = show['seasons'].values() | 
            
                                                                                                            
                            
            
                                    
            
            
                | 368 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 369 |  |  |             for season in show['seasons']: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 370 |  |  |                 if 'episodes' not in season: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 371 |  |  |                     continue | 
            
                                                                                                            
                            
            
                                    
            
            
                | 372 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 373 |  |  |                 season['episodes'] = season['episodes'].values() | 
            
                                                                                                            
                            
            
                                    
            
            
                | 374 |  |  |  | 
            
                                                                                                            
                                                                
            
                                    
            
            
                | 375 |  |  |             yield show | 
            
                                                        
            
                                    
            
            
                | 376 |  |  |  |