Ejemplo n.º 1
0
    def set_identifier(cls, data, guid, strict=True):
        if not guid:
            return None

        if type(guid) is str:
            # Parse raw guid
            guid = Guid.parse(guid)

        if 'ids' not in data:
            data['ids'] = {}

        ids = data['ids']

        if guid.agent == 'imdb':
            ids['imdb'] = guid.sid
        elif guid.agent == 'tmdb':
            ids['tmdb'] = try_convert(guid.sid, int)
        elif guid.agent == 'tvdb':
            ids['tvdb'] = try_convert(guid.sid, int)
        elif not strict:
            log.info('Unknown Guid agent: "%s"', guid.agent)
        else:
            log.info('Unknown Guid agent: "%s" [strict]', guid.agent)
            return None

        return data
Ejemplo n.º 2
0
    def from_guid(cls, guid):
        if not guid:
            return None

        uri = urlparse(guid)
        agent = uri.scheme

        result = PlexParsedGuid(agent, uri.netloc, uri.query)

        # Nothing more to parse, return now
        if not uri.path:
            return result

        # Parse path component for agent-specific data
        path_fragments = uri.path.strip('/').split('/')

        if agent in SHOW_AGENTS:
            if len(path_fragments) >= 1:
                result.season = try_convert(path_fragments[0], int)

            if len(path_fragments) >= 2:
                result.episode = try_convert(path_fragments[1], int)
        else:
            log.warn('Unable to completely parse guid "%s"', guid)

        return result
def ListMessages(days=14, version='latest', viewed=False, *args, **kwargs):
    # Cast `viewed` to boolean
    if type(viewed) is str:
        if viewed == 'None':
            viewed = None
        else:
            viewed = viewed == 'True'

    # Retrieve messages
    messages = list(
        List(days=try_convert(days, int), version=version,
             viewed=viewed).order_by(Message.last_logged_at.desc()).limit(50))

    total_messages = List(
        days=try_convert(days, int),
        version=version,
    ).count()

    # Construct container
    oc = ObjectContainer(title2=_("Messages"))

    if viewed is False and len(messages) > 1:
        oc.add(
            DirectoryObject(key=Callback(DismissMessages),
                            title=pad_title(_("Dismiss all"))))

    for m in messages:
        if m.type is None or\
           m.summary is None:
            continue

        thumb = None

        if m.type == Message.Type.Exception:
            thumb = R("icon-exception-viewed.png") if m.viewed else R(
                "icon-exception.png")
        elif m.type == Message.Type.Info:
            thumb = R("icon-notification-viewed.png") if m.viewed else R(
                "icon-notification.png")
        elif m.type in ERROR_TYPES:
            thumb = R("icon-error-viewed.png") if m.viewed else R(
                "icon-error.png")

        oc.add(
            DirectoryObject(
                key=Callback(ViewMessage, error_id=m.id),
                title=pad_title('[%s] %s' %
                                (Message.Type.title(m.type), m.summary)),
                thumb=thumb))

    # Append "View All" button
    if len(messages) != 50 and len(messages) < total_messages:
        oc.add(
            DirectoryObject(key=Callback(ListMessages, days=None, viewed=None),
                            title=pad_title(_("View All"))))

    return oc
Ejemplo n.º 4
0
def pull_show(watched, rated, directory, tvdb_id):
    # Sync watched
    if Prefs['sync_watched'] is True:
        for show in [x for x in watched if x['tvdb_id'] == tvdb_id]:
            Log.Debug('We have a match for %s' % show['title'])

            episodes = PMS.get_metadata_leaves(directory.get('ratingKey'))
            if not episodes:
                Log.Warn('Unable to fetch episodes for show with id %s' % directory.get('ratingKey'))
                continue

            for episode in episodes.xpath('//Video'):
                season_num = try_convert(episode.get('parentIndex'), int)
                episode_num = try_convert(episode.get('index'), int)

                # Skip episodes with missing season or episode numbers
                if season_num is None or episode_num is None:
                    continue

                for season in matches(season_num, show['seasons'], lambda x: int(x['season'])):

                    if episode_num in season['episodes']:
                        Log.Debug('Marking %s episode %s with key: %s as seen.' % (
                            episode.get('grandparentTitle'), episode.get('title'), episode.get('ratingKey')
                        ))

                        if not PMS.scrobble(episode):
                            Log.Debug('The episode %s is already marked as seen in the library.' % episode.get('title'))

    # Sync ratings
    if Prefs['sync_ratings'] is True:
        for show in [x for x in rated if x['show']['tvdb_id'] == tvdb_id]:
            show_season = try_convert(show['episode']['season'], int)
            show_episode = try_convert(show['episode']['number'], int)

            # Skip episodes with missing season or episode numbers
            if show_season is None or show_episode is None:
                continue

            episodes = PMS.get_metadata_leaves(directory.get('ratingKey'))
            if not episodes:
                Log.Warn('Unable to fetch episodes for show with id %s' % directory.get('ratingKey'))
                continue

            for episode in episodes.xpath('//Video'):
                if show_season == int(episode.get('parentIndex')) and show_episode == int(episode.get('index')):
                    PMS.rate(episode, show['rating_advanced'])
Ejemplo n.º 5
0
    def fill(obj, container, video, parsed_guid=None):
        obj.type = video.get('type')

        obj.title = video.get('title')
        obj.year = try_convert(video.get('year'), int)

        obj.user_rating = try_convert(video.get('userRating'), float)

        if obj.user_rating:
            obj.user_rating = int(round(obj.user_rating, 0))

        obj.section_key = try_convert(container.get('librarySectionID'), int)
        obj.section_title = container.get('librarySectionTitle')

        if parsed_guid is not None:
            obj.agent = parsed_guid.agent
            obj.sid = parsed_guid.sid
Ejemplo n.º 6
0
    def set(cls, key, value, value_type=None):
        result = cls.request(':/prefs?%s=%s' %
                             (key, try_convert(value, value_type)),
                             'text',
                             method='PUT')
        if result is None:
            return False

        return True
Ejemplo n.º 7
0
    def scrobble_percentage(self, value):
        value = try_convert(value, int)

        if value is None:
            return None

        if 0 <= value <= 100:
            return value

        return None
Ejemplo n.º 8
0
    def scrobble_percentage(self, value):
        value = try_convert(value, int)

        if value is None:
            return None

        if 0 <= value <= 100:
            return value

        return None
Ejemplo n.º 9
0
    def get_identifier(cls, video):
        # Get plex identifier
        p_season = try_convert(video.get('parentIndex'), int)
        p_episode = try_convert(video.get('index'), int)

        # Ensure plex data is valid
        if p_season is None or p_episode is None:
            log.debug('Ignoring item with key "%s", invalid season or episode attribute', video.get('ratingKey'))
            return None, []

        # Find new episodes from identifiers
        c_episodes = [p_episode]

        # Add extended episodes
        c_episodes.extend(cls.get_extended(video, p_season, p_episode))

        # Remove any episode identifiers that are more than 1 away
        c_episodes = cls.remove_distant(c_episodes, p_episode)

        return p_season, c_episodes
    def update(self, info):
        # Ignore if scrobbling is disabled
        if not get_pref('scrobble'):
            return

        session_key = try_convert(info.get('sessionKey'), int)
        state = info.get('state')
        view_offset = info.get('viewOffset')

        ws = self.get_session(session_key, state, view_offset)
        if not ws:
            log.trace('Invalid or ignored session, nothing to do')
            return

        # Ignore sessions flagged as 'skip'
        if ws.skip:
            return

        # Validate session (check filters)
        if not self.valid(ws):
            return

        # Check if we are scrobbling a known media type
        if not ws.type:
            log.info('Playing unknown item, will not be scrobbled: "%s"' % ws.title)
            ws.skip = True
            return

        # Check if the view_offset has jumped (#131)
        if self.offset_jumped(ws, view_offset):
            log.info('View offset jump detected, ignoring the state update')
            ws.save()
            return

        ws.last_view_offset = view_offset

        # Calculate progress
        if not self.update_progress(ws, view_offset):
            log.warn('Error while updating session progress, queued session to be updated')
            ws.update_required = True
            ws.save()
            return

        action = self.get_action(ws, state)

        if action:
            self.handle_action(ws, action)
        else:
            log.debug(self.status_message(ws, state)('Nothing to do this time for %s'))
            ws.save()

        self.handle_state(ws, state)
Ejemplo n.º 11
0
    def match_identifier(cls, p_season, p_episode, c_identifier):
        # Season number retrieval/validation (only except exact matches to p_season)
        if 'season' not in c_identifier:
            return

        c_season = try_convert(c_identifier['season'], int)
        if c_season != p_season:
            return

        # Episode number retrieval/validation
        c_episodes = None

        # Single or repeat-style episodes
        if 'episode' in c_identifier:
            episodes = c_identifier['episode']

            if not isinstance(episodes, (list, set)):
                episodes = [episodes]

            c_episodes = [try_convert(x, int) for x in episodes]

        # Extend-style episodes
        if 'episode_from' in c_identifier and 'episode_to' in c_identifier:
            c_from = try_convert(c_identifier['episode_from'], int)
            c_to = try_convert(c_identifier['episode_to'], int)

            if c_from is None or c_to is None:
                return

            episodes = range(c_from, c_to + 1)

            # Ensure plex episode is inside identifier episode range
            if p_episode in episodes:
                c_episodes = episodes
            else:
                return

        return c_episodes
Ejemplo n.º 12
0
    def process_playing(item):
        session_key = item.get('sessionKey')
        state = item.get('state')
        view_offset = try_convert(item.get('viewOffset'), int)

        valid = all([x is not None for x in [session_key, state, view_offset]])

        if valid:
            EventManager.fire('notifications.playing', str(session_key),
                              str(state), view_offset)
            return True

        log.warn("'playing' notification doesn't look valid, ignoring: %s" %
                 item)
        return False
    def process_playing(item):
        session_key = item.get('sessionKey')
        state = item.get('state')
        view_offset = try_convert(item.get('viewOffset'), int)

        valid = all([
            x is not None
            for x in [session_key, state, view_offset]
        ])

        if valid:
            EventManager.fire('notifications.playing', str(session_key), str(state), view_offset)
            return True

        log.warn("'playing' notification doesn't look valid, ignoring: %s" % item)
        return False
Ejemplo n.º 14
0
    def from_event(cls, info):
        account_key = try_convert(info.get('account_key'), int)
        rating_key = info.get('rating_key')

        if account_key is None or rating_key is None:
            log.warn('Invalid action format: %s', info)
            return None

        if account_key != 1:
            log.debug('Ignoring action from shared account')
            return None

        if WatchSession.is_active(rating_key, lambda ws: not ws.update_required):
            log.debug('Ignoring action, item is currently being watched')
            return False

        metadata = Metadata.get(rating_key)

        if not metadata:
            log.debug('Ignoring action, unable to retrieve metadata')
            return False

        section = metadata.section.title.lower()

        f_allow, _ = get_filter('filter_sections')

        if f_allow is not None and section not in f_allow:
            log.debug('Ignoring action, section has been filtered')
            return False

        guid = Guid.parse(metadata.guid)

        request = {}

        if type(metadata) is Movie:
            request = cls.from_movie(metadata, guid)
        elif type(metadata) is Season:
            request = cls.from_season(metadata, guid)
        elif type(metadata) is Episode:
            request = cls.from_episode(metadata, guid)
        else:
            log.warn('Unsupported metadata type: %r', metadata)
            return None

        log.debug('request: %r', request)

        return request
Ejemplo n.º 15
0
    def process(self, opcode, data):
        if opcode not in self.opcode_data:
            return

        info = JSON.ObjectFromString(data)
        item = info['_children'][0]

        if info['type'] == "playing" and Dict["scrobble"]:
            session_key = str(item['sessionKey'])
            state = str(item['state'])
            view_offset = try_convert(item['viewOffset'], int)

            self.scrobbler.update(session_key, state, view_offset)

        if info['type'] == "timeline" and Dict['new_sync_collection']:
            if item['type'] not in [1, 4]:
                return

            if item['state'] == 0:
                Log.Info("New File added to Libray: " + item['title'] + ' - ' + str(item['itemID']))

                self.update_collection(item['itemID'], 'add')
Ejemplo n.º 16
0
    def process(self, opcode, data):
        if opcode not in self.opcode_data:
            return

        info = JSON.ObjectFromString(data)
        item = info['_children'][0]

        if info['type'] == "playing" and Dict["scrobble"]:
            session_key = str(item['sessionKey'])
            state = str(item['state'])
            view_offset = try_convert(item['viewOffset'], int)

            self.scrobbler.update(session_key, state, view_offset)

        if info['type'] == "timeline" and Dict['new_sync_collection']:
            if item['type'] not in [1, 4]:
                return

            if item['state'] == 0:
                Log.Info("New File added to Libray: " + item['title'] + ' - ' +
                         str(item['itemID']))

                self.update_collection(item['itemID'], 'add')
Ejemplo n.º 17
0
    def fill(obj, container, video, parsed_guid=None):
        PlexMedia.fill(obj, container, video, parsed_guid)

        obj.view_count = try_convert(video.get('viewCount'), int)
        obj.duration = try_convert(video.get('duration'), int, 0) / float(1000 * 60)  # Convert to minutes
Ejemplo n.º 18
0
def ListMessages(days=14, version='latest', viewed=False, *args, **kwargs):
    # Cast `viewed` to boolean
    if type(viewed) is str:
        if viewed == 'None':
            viewed = None
        else:
            viewed = viewed == 'True'

    # Retrieve messages
    messages = list(List(
        days=try_convert(days, int),
        version=version,
        viewed=viewed
    ).order_by(
        Message.last_logged_at.desc()
    ).limit(50))

    total_messages = List(
        days=try_convert(days, int),
        version=version,
    ).count()

    # Construct container
    oc = ObjectContainer(
        title2=_("Messages")
    )

    # Add "Dismiss All" button
    if viewed is False and len(messages) > 1:
        oc.add(DirectoryObject(
            key=Callback(DismissMessages),
            title=pad_title(_("Dismiss all"))
        ))

    # Add interface messages
    for record in InterfaceMessages.records:
        # Pick object thumb
        if record.level >= logging.WARNING:
            thumb = R("icon-error.png")
        else:
            thumb = R("icon-notification.png")

        # Add object
        oc.add(DirectoryObject(
            key=PLUGIN_PREFIX + '/messages/list',
            title=pad_title('[%s] %s' % (logging.getLevelName(record.level).capitalize(), record.message)),
            thumb=thumb
        ))

    # Add stored messages
    for m in messages:
        if m.type is None or\
           m.summary is None:
            continue

        # Pick thumb
        if m.type == Message.Type.Exception:
            thumb = R("icon-exception-viewed.png") if m.viewed else R("icon-exception.png")
        elif m.type == Message.Type.Info:
            thumb = R("icon-notification-viewed.png") if m.viewed else R("icon-notification.png")
        elif m.type in CONNECTION_TYPES:
            thumb = R("icon-connection-viewed.png") if m.viewed else R("icon-connection.png")
        else:
            thumb = R("icon-error-viewed.png") if m.viewed else R("icon-error.png")

        # Add object
        oc.add(DirectoryObject(
            key=Callback(ViewMessage, error_id=m.id),
            title=pad_title('[%s] %s' % (Message.Type.title(m.type), m.summary)),
            thumb=thumb
        ))

    # Append "View All" button
    if len(messages) != 50 and len(messages) < total_messages:
        oc.add(DirectoryObject(
            key=Callback(ListMessages, days=None, viewed=None),
            title=pad_title(_("View All"))
        ))

    return oc
    def set(cls, key, value, value_type=None):
        result = cls.request(':/prefs?%s=%s' % (key, try_convert(value, value_type)), 'text', method='PUT')
        if result is None:
            return False

        return True
Ejemplo n.º 20
0
def ListMessages(days=14, version='latest', viewed=False, *args, **kwargs):
    # Cast `viewed` to boolean
    if type(viewed) is str:
        if viewed == 'None':
            viewed = None
        else:
            viewed = viewed == 'True'

    # Retrieve messages
    messages = list(
        List(days=try_convert(days, int), version=version,
             viewed=viewed).order_by(Message.last_logged_at.desc()).limit(50))

    total_messages = List(
        days=try_convert(days, int),
        version=version,
    ).count()

    # Construct container
    oc = ObjectContainer(title2=_("Messages"))

    # Add "Dismiss All" button
    if viewed is False and len(messages) > 1:
        oc.add(
            DirectoryObject(key=Callback(DismissMessages),
                            title=pad_title(_("Dismiss all"))))

    # Add interface messages
    for record in InterfaceMessages.records:
        # Pick object thumb
        if record.level >= logging.WARNING:
            thumb = R("icon-error.png")
        else:
            thumb = R("icon-notification.png")

        # Add object
        oc.add(
            DirectoryObject(key=PLUGIN_PREFIX + '/messages/list',
                            title=pad_title('[%s] %s' % (logging.getLevelName(
                                record.level).capitalize(), record.message)),
                            thumb=thumb))

    # Add stored messages
    for m in messages:
        if m.type is None or\
           m.summary is None:
            continue

        # Pick thumb
        if m.type == Message.Type.Exception:
            thumb = R("icon-exception-viewed.png") if m.viewed else R(
                "icon-exception.png")
        elif m.type == Message.Type.Info:
            thumb = R("icon-notification-viewed.png") if m.viewed else R(
                "icon-notification.png")
        elif m.type in CONNECTION_TYPES:
            thumb = R("icon-connection-viewed.png") if m.viewed else R(
                "icon-connection.png")
        else:
            thumb = R("icon-error-viewed.png") if m.viewed else R(
                "icon-error.png")

        # Add object
        oc.add(
            DirectoryObject(
                key=Callback(ViewMessage, error_id=m.id),
                title=pad_title('[%s] %s' %
                                (Message.Type.title(m.type), m.summary)),
                thumb=thumb))

    # Append "View All" button
    if len(messages) != 50 and len(messages) < total_messages:
        oc.add(
            DirectoryObject(key=Callback(ListMessages, days=None, viewed=None),
                            title=pad_title(_("View All"))))

    return oc