Пример #1
0
    def to_events(cls, session, info):
        # Validate `state`
        state = info.get('state')

        if not state:
            log.warn('Event has an invalid state %r', state)
            return []

        if state in IGNORED_EVENTS:
            log.debug('Ignored "%s" event: %r', state, info)
            return []

        # Validate `view_offset`
        view_offset = to_integer(info.get('viewOffset'))

        if view_offset is None:
            log.info('Event has an invalid view offset %r', view_offset)
            return []

        # Check for session `view_offset` jump
        if cls.session_jumped(session, info.get('viewOffset')):
            return []

        # Build event
        return [(state, {
            'rating_key': to_integer(info.get('ratingKey')),
            'view_offset': view_offset
        })]
Пример #2
0
    def to_events(cls, session, info):
        # Validate `state`
        state = info.get('state')

        if not state:
            log.warn('Event has an invalid state %r', state)
            return []

        if state in IGNORED_EVENTS:
            log.debug('Ignored "%s" event: %r', state, info)
            return []

        # Check for session `view_offset` jump
        if cls.session_jumped(session, info.get('viewOffset')):
            return []

        # Retrieve event parameters
        view_offset = to_integer(info.get('viewOffset'))

        # Calculate current part number
        # TODO handle null values from session?
        part, _ = UpdateSession.get_part(session.duration, view_offset,
                                         session.part_count)

        # Build event
        return [(state, {
            'part': part,
            'rating_key': to_integer(info.get('ratingKey')),
            'view_offset': view_offset
        })]
Пример #3
0
    def to_events(cls, session, info):
        # Validate `state`
        state = info.get('state')

        if not state:
            log.warn('Event has an invalid state %r', state)
            return []

        if state in IGNORED_EVENTS:
            log.debug('Ignored "%s" event: %r', state, info)
            return []

        # Check for session `view_offset` jump
        if cls.session_jumped(session, info.get('viewOffset')):
            return []

        # Retrieve event parameters
        view_offset = to_integer(info.get('viewOffset'))

        # Calculate current part number
        # TODO handle null values from session?
        part, _ = UpdateSession.get_part(session.duration, view_offset, session.part_count)

        # Build event
        return [
            (state, {
                'part': part,
                'rating_key': to_integer(info.get('ratingKey')),
                'view_offset': view_offset
            })
        ]
Пример #4
0
    def to_events(cls, session, info):
        # Validate `state`
        state = info.get('state')

        if not state:
            log.warn('Event has an invalid state %r', state)
            return []

        if state in IGNORED_EVENTS:
            log.debug('Ignored "%s" event: %r', state, info)
            return []

        # Validate `view_offset`
        view_offset = to_integer(info.get('viewOffset'))

        if view_offset is None:
            log.info('Event has an invalid view offset %r', view_offset)
            return []

        # Check for session `view_offset` jump
        if cls.session_jumped(session, info.get('viewOffset')):
            return []

        # Build event
        return [
            (state, {
                'rating_key': to_integer(info.get('ratingKey')),
                'view_offset': view_offset
            })
        ]
Пример #5
0
    def to_dict(self, obj, info, fetch=False):
        view_offset = to_integer(info.get('time'))
        rating_key = info.get('ratingKey')

        result = {
            'view_offset': view_offset,

            'updated_at': datetime.utcnow()
        }

        if not fetch:
            # Return simple update
            return merge(result, {
                'progress': self.get_progress(obj.duration, view_offset)
            })

        # Retrieve session
        # Retrieve metadata and guid
        p_metadata, guid = self.get_metadata(rating_key)

        if not p_metadata:
            log.warn('Unable to retrieve metadata for rating_key %r', rating_key)
            return result

        if not guid or not guid.valid:
            return merge(result, {
                'duration': p_metadata.duration,
                'progress': self.get_progress(p_metadata.duration, view_offset)
            })

        try:
            # Create/Retrieve `Client` for session
            result['client'] = ClientManager.get.or_create({
                'key': info.get('machineIdentifier'),
                'title': info.get('client')
            }, fetch=True)

            # Create/Retrieve `User` for session
            result['user'] = UserManager.get.or_create({
                'key': to_integer(info.get('user_id')),
                'title': info.get('user_name')
            }, fetch=True)

            # Pick account from `client` or `user` objects
            result['account'] = self.get_account(result)
        except FilteredException:
            log.debug('Activity has been filtered')

            result['client'] = None
            result['user'] = None

            result['account'] = None

        return merge(result, {
            'duration': p_metadata.duration,
            'progress': self.get_progress(p_metadata.duration, view_offset)
        })
Пример #6
0
    def on_playing(self, info):
        if InterfaceMessages.critical:
            return

        # Create or retrieve existing session
        session = WSessionManager.get.or_create(info, fetch=True)

        # Validate session
        if session.updated_at is None or (
                datetime.utcnow() - session.updated_at) > timedelta(minutes=5):
            log.info('Updating session, last update was over 5 minutes ago')
            WSessionManager.update(session, info, fetch=True)
            return

        if session.duration is None or session.view_offset is None:
            # Update session
            WSessionManager.update(
                session,
                info,
                fetch=lambda s, i: (s.rating_key != to_integer(
                    i.get('ratingKey')) or s.duration is None))
            return

        # Parse `info` to events
        events = self.to_events(session, info)

        if not events:
            return

        # Check for changed media
        media_changed = session.rating_key != to_integer(info.get('ratingKey'))

        # Parse `events`
        actions = self.engine.process(session, events)

        for action, payload in actions:
            # Build request for the event
            request = self.build_request(
                session,
                part=payload.get('part', 1),
                rating_key=payload.get('rating_key'),
                view_offset=payload.get('view_offset'))

            if not request:
                log.info('No request returned for action %r (payload: %r)',
                         action, payload)
                continue

            # Queue request to be sent
            ActionManager.queue('/'.join(['scrobble', action]), request,
                                session)

        # Update session
        WSessionManager.update(session, info, fetch=media_changed)
Пример #7
0
    def on_playing(self, info):
        if InterfaceMessages.critical:
            return

        # Create or retrieve existing session
        session = WSessionManager.get.or_create(info, fetch=True)

        # Validate session
        if session.updated_at is None or (datetime.utcnow() - session.updated_at) > timedelta(minutes=5):
            log.info('Updating session, last update was over 5 minutes ago')
            WSessionManager.update(session, info, fetch=True)
            return

        if session.duration is None or session.view_offset is None:
            # Update session
            WSessionManager.update(session, info, fetch=lambda s, i: (
                s.rating_key != to_integer(i.get('ratingKey')) or
                s.duration is None
            ))
            return

        # Parse `info` to events
        events = self.to_events(session, info)

        if not events:
            return

        # Check for changed media
        media_changed = session.rating_key != to_integer(info.get('ratingKey'))

        # Parse `events`
        actions = self.engine.process(session, events)

        for action, payload in actions:
            # Build request for the event
            request = self.build_request(
                session,
                part=payload.get('part', 1),
                rating_key=payload.get('rating_key'),
                view_offset=payload.get('view_offset')
            )

            if not request:
                log.info('No request returned for action %r (payload: %r)', action, payload)
                continue

            # Queue request to be sent
            ActionManager.queue('/'.join(['scrobble', action]), request, session)

        # Update session
        WSessionManager.update(session, info, fetch=media_changed)
Пример #8
0
    def or_create(self,
                  user,
                  fetch=False,
                  match=False,
                  filtered_exception=False):
        user = self.manager.parse_user(user)

        if not user:
            return None

        try:
            # Create new user
            obj = self.manager.create(key=to_integer(user['key']))

            # Update newly created object
            self.manager.update(obj,
                                user,
                                fetch=fetch,
                                match=match,
                                filtered_exception=filtered_exception)

            return obj
        except (apsw.ConstraintError, peewee.IntegrityError):
            # Return existing user
            obj = self(user)

            if fetch or match:
                # Update existing `User`
                self.manager.update(obj,
                                    user,
                                    fetch=fetch,
                                    match=match,
                                    filtered_exception=filtered_exception)

            return obj
Пример #9
0
    def or_create(self, info, fetch=False):
        machine_identifier = info.get('machineIdentifier')

        if not machine_identifier:
            log.info(
                'No machine identifier available, unable to create session')
            return None

        try:
            # Create new session
            obj = self.manager.create(rating_key=to_integer(
                info.get('ratingKey')),
                                      session_key=machine_identifier,
                                      state='create')

            # Update newly created object
            self.manager.update(obj, info, fetch)

            # Update active sessions
            ModuleManager['sessions'].on_created(obj)

            return obj
        except (apsw.ConstraintError, peewee.IntegrityError):
            # Return existing object
            return self(info)
Пример #10
0
    def or_create(self, info, fetch=False):
        machine_identifier = info.get('machineIdentifier')

        if not machine_identifier:
            log.info('No machine identifier available, unable to create session')
            return None

        try:
            # Create new session
            obj = self.manager.create(
                rating_key=to_integer(info.get('ratingKey')),
                session_key=machine_identifier,

                state='create'
            )

            # Update newly created object
            self.manager.update(obj, info, fetch)

            # Update active sessions
            ModuleManager['sessions'].on_created(obj)

            return obj
        except (apsw.ConstraintError, peewee.IntegrityError):
            # Return existing object
            return self(info)
Пример #11
0
    def __call__(self, user):
        user = self.manager.parse_user(user)

        if not user:
            return None

        return super(GetUser,
                     self).__call__(User.key == to_integer(user['key']))
Пример #12
0
    def __call__(self, user):
        user = self.manager.parse_user(user)

        if not user:
            return None

        return super(GetUser, self).__call__(
            User.key == to_integer(user['key'])
        )
Пример #13
0
    def to_events(cls, session, info):
        # Validate `state`
        state = info.get('state')

        if not state:
            log.warn('Event has an invalid state %r', state)
            return []

        # Check for session `view_offset` jump
        if cls.session_jumped(session, info.get('viewOffset')):
            return []

        # Build event
        return [
            (state, {
                'rating_key': to_integer(info.get('ratingKey')),
                'view_offset': to_integer(info.get('viewOffset'))
            })
        ]
Пример #14
0
    def or_create(self, info, fetch=False):
        session_key = to_integer(info.get('sessionKey'))

        try:
            # Create new session
            obj = self.manager.create(
                rating_key=to_integer(info.get('ratingKey')),
                session_key=self.build_session_key(session_key),
                state='create')

            # Update newly created object
            self.manager.update(obj, info, fetch)

            # Update active sessions
            ModuleManager['sessions'].on_created(obj)

            return obj
        except (apsw.ConstraintError, peewee.IntegrityError):
            # Return existing object
            return self(info)
Пример #15
0
    def or_create(self, info, fetch=False):
        session_key = to_integer(info.get('sessionKey'))

        try:
            # Create new session
            obj = self.manager.create(
                rating_key=to_integer(info.get('ratingKey')),
                session_key=self.build_session_key(session_key),

                state='create'
            )

            # Update newly created object
            self.manager.update(obj, info, fetch)

            # Update active sessions
            ModuleManager['sessions'].on_created(obj)

            return obj
        except (apsw.ConstraintError, peewee.IntegrityError):
            # Return existing object
            return self(info)
Пример #16
0
    def or_create(self, user, fetch=False, match=False, filtered_exception=False):
        user = self.manager.parse_user(user)

        if not user:
            return None

        try:
            # Create new user
            obj = self.manager.create(
                key=to_integer(user['key'])
            )

            # Update newly created object
            self.manager.update(
                obj, user,

                fetch=fetch,
                match=match,
                filtered_exception=filtered_exception
            )

            return obj
        except (apsw.ConstraintError, peewee.IntegrityError):
            # Return existing user
            obj = self(user)

            if fetch or match:
                # Update existing `User`
                self.manager.update(
                    obj, user,

                    fetch=fetch,
                    match=match,
                    filtered_exception=filtered_exception
                )

            return obj
Пример #17
0
    def __call__(self, info):
        session_key = to_integer(info.get('sessionKey'))

        return super(GetWSession, self).__call__(
            Session.session_key == self.build_session_key(session_key))
Пример #18
0
    def to_dict(self, obj, info, fetch=False):
        fetch = resolve(fetch, obj, info)

        view_offset = to_integer(info.get('viewOffset'))

        result = {
            'rating_key': to_integer(info.get('ratingKey')),
            'view_offset': view_offset,

            'updated_at': datetime.utcnow()
        }

        if not fetch:
            # Return simple update
            return merge(result, {
                'progress': self.get_progress(
                    obj.duration, view_offset,
                    obj.part, obj.part_count, obj.part_duration
                )
            })

        # Retrieve session key
        session_key = to_integer(info.get('sessionKey'))

        if not session_key:
            log.info('Missing session key, unable to fetch session details')
            return result

        # Retrieve active sessions
        log.debug('Fetching details for session #%s', session_key)

        p_sessions = Plex['status'].sessions()

        if not p_sessions:
            log.info('Unable to retrieve active sessions')
            return result

        # Find session matching `session_key`
        p_item = p_sessions.get(session_key)

        if not p_item:
            log.info('Unable to find session with key %r', session_key)
            return result

        # Retrieve metadata and guid
        p_metadata, guid = self.get_metadata(p_item.rating_key)

        if not p_metadata:
            log.info('Unable to retrieve metadata for rating_key %r', p_item.rating_key)
            return result

        if not guid or not guid.valid:
            return merge(result, {
                'duration': p_metadata.duration,
                'progress': self.get_progress(p_metadata.duration, view_offset)
            })

        # Retrieve media parts
        part, part_count, part_duration = self.match_parts(p_metadata, guid, view_offset)

        log.debug('Part: %s (part_count: %s, part_duration: %s)', part, part_count, part_duration)

        # Find matching client + user for session
        try:
            # Create/Retrieve `Client` for session
            result['client'] = ClientManager.get.or_create(
                p_item.session.player,

                fetch=True,
                match=True,
                filtered_exception=True
            )

            # Create/Retrieve `User` for session
            result['user'] = UserManager.get.or_create(
                p_item.session.user,

                fetch=True,
                match=True,
                filtered_exception=True
            )

            # Pick account from `client` or `user` objects
            result['account'] = self.get_account(result)
        except FilteredException:
            log.debug('Activity has been filtered')

            result['client'] = None
            result['user'] = None

            result['account'] = None

        return merge(result, {
            'part': part,
            'part_count': part_count,
            'part_duration': part_duration,

            'duration': p_metadata.duration,
            'progress': self.get_progress(
                p_metadata.duration, view_offset,
                part, part_count, part_duration
            )
        })
Пример #19
0
    def to_dict(self, obj, info, fetch=False):
        fetch = resolve(fetch, obj, info)

        view_offset = to_integer(info.get('viewOffset'))

        result = {
            'rating_key': to_integer(info.get('ratingKey')),
            'view_offset': view_offset,
            'updated_at': datetime.utcnow()
        }

        if not fetch:
            # Return simple update
            return merge(
                result,
                {'progress': self.get_progress(obj.duration, view_offset)})

        # Retrieve session key
        session_key = to_integer(info.get('sessionKey'))

        if not session_key:
            log.info('Missing session key, unable to fetch session details')
            return result

        # Retrieve active sessions
        log.debug('Fetching details for session #%s', session_key)

        p_sessions = Plex['status'].sessions()

        if not p_sessions:
            log.info('Unable to retrieve active sessions')
            return result

        # Find session matching `session_key`
        p_item = p_sessions.get(session_key)

        if not p_item:
            log.info('Unable to find session with key %r', session_key)
            return result

        # Retrieve metadata and guid
        p_metadata, guid = self.get_metadata(p_item.rating_key)

        if not p_metadata:
            log.info('Unable to retrieve metadata for rating_key %r',
                     p_item.rating_key)
            return result

        if not guid:
            return merge(
                result, {
                    'duration': p_metadata.duration,
                    'progress': self.get_progress(p_metadata.duration,
                                                  view_offset)
                })

        try:
            # Create/Retrieve `Client` for session
            result['client'] = ClientManager.get.or_create(
                p_item.session.player,
                fetch=True,
                match=True,
                filtered_exception=True)

            # Create/Retrieve `User` for session
            result['user'] = UserManager.get.or_create(p_item.session.user,
                                                       fetch=True,
                                                       match=True,
                                                       filtered_exception=True)

            # Pick account from `client` or `user` objects
            result['account'] = self.get_account(result)
        except FilteredException:
            log.debug('Activity has been filtered')

            result['client'] = None
            result['user'] = None

            result['account'] = None

        return merge(
            result, {
                'duration': p_metadata.duration,
                'progress': self.get_progress(p_metadata.duration, view_offset)
            })
Пример #20
0
    def __call__(self, info):
        session_key = to_integer(info.get('sessionKey'))

        return super(GetWSession, self).__call__(
            Session.session_key == self.build_session_key(session_key)
        )