示例#1
0
def get_media_info(file_path: str):
    logger.debug(f"Raw filepath {file_path!r}")
    file_path = cleanup_encoding(file_path)
    parsed = urlsplit(file_path)
    file_is_url = False
    guessit_path = file_path
    if is_url(parsed):
        file_is_url = True
        # remove the query and fragment from the url, keeping only important parts
        scheme, netloc, path, _, _ = parsed
        path = unquote(path)  # quoting should only be applied to the path
        file_path = urlunsplit((scheme, netloc, path, "", ""))
        logger.debug(f"Converted to url {file_path!r}")
        # only use the actual path for guessit, skipping other parts
        guessit_path = path
        logger.debug(f"Guessit url {guessit_path!r}")

    if not whitelist_file(file_path, file_is_url):
        logger.info("File path not in whitelist.")
        return None
    if exclude_file(file_path):
        logger.info("Ignoring file.")
        return None
    guess = use_regex and custom_regex(file_path) or use_guessit(guessit_path)
    logger.debug(f"Guess: {guess}")
    return cleanup_guess(guess)
示例#2
0
def get_media_info(file_path):
    logger.debug(f"Filepath '{file_path}'")
    file_path = Path(file_path)
    if not whitelist_file(file_path):
        logger.info("File path not in whitelist.")
        return None
    guess = custom_regex(file_path) or use_guessit(file_path)

    if any(key not in guess for key in ('title', 'type')) or \
       (guess['type'] == 'episode' and 'episode' not in guess):
        logger.warning('Failed to parse filename for episode/movie info. '
                       'Consider renaming/using custom regex.')
        return None

    if isinstance(guess['title'], list):
        guess['title'] = " ".join(guess['title'])

    req_keys = ['type', 'title']
    if guess['type'] == 'episode':
        season = guess.get('season', 1)
        if isinstance(season, list):
            logger.warning(
                f"Multiple probable seasons found: ({','.join(season)}). "
                "Consider renaming the folder.")
            return None
        guess['season'] = int(season)
        req_keys += ['season', 'episode']

    return {key: guess[key] for key in req_keys}
示例#3
0
    def refresh_token(self):
        if self._refresh_retries == self._REFRESH_RETRIES_LIMIT:
            self.token_data = {}
            self._refresh_retries = 0

            logger.critical("Too many failed refreshes. Clearing token.")
            notify("Trakt token expired. Couldn't auto-refresh token.", stdout=True)
            self.device_auth()
            return

        exchange_params = {
            "url": API_URL + '/oauth/token',
            "headers": {"Content-Type": "application/json"},
            "json": {
                "refresh_token": self.token_data['refresh_token'],
                "client_id": self.CLIENT_ID,
                "client_secret": self.CLIENT_SECRET,
                "redirect_uri": "urn:ietf:wg:oauth:2.0:oob",
                "grant_type": "refresh_token"
            }
        }
        self._refresh_retries += 1
        exchange_resp = safe_request('post', exchange_params)

        if exchange_resp and exchange_resp.status_code == 200:
            self.token_data = exchange_resp.json()
            self._refresh_retries = 0
            logger.info('Refreshed access token.')
        else:
            logger.error("Error refreshing token.")
示例#4
0
 def __init__(self, scrobble_queue):
     super().__init__()
     logger.info('Started monitor for ' + self.name)
     self.scrobble_queue = scrobble_queue
     self.is_running = False
     self.status = {}
     self.prev_state = {}
     self.skip_interval = self.config.get('skip_interval', 5)
示例#5
0
def get_media_info(file_path):
    logger.debug(f"Filepath '{file_path}'")
    file_path = Path(file_path)
    if not whitelist_file(file_path):
        logger.info("File path not in whitelist.")
        return None
    guess = use_regex and custom_regex(file_path) or use_guessit(file_path)
    logger.debug(f"Guess: {guess}")
    return cleanup_guess(guess)
示例#6
0
def get_token(**kwargs):
    token = kwargs.get("token") or read_json(PLEX_TOKEN_PATH).get("token")
    if not token:
        logger.info("Retrieving plex token")
        token = plex_token_auth(kwargs["login"], kwargs["password"])
        if token:
            write_json({"token": token}, PLEX_TOKEN_PATH)
            logger.info(f"Saved plex token to {PLEX_TOKEN_PATH}")
    return token
示例#7
0
 def handle_successful_scrobble(self, verb, data, resp):
     if 'movie' in resp:
         name = resp['movie']['title']
     else:
         name = (resp['show']['title'] +
                 " S{season:02}E{number:02}".format(**resp['episode']))
     category = self._determine_category(verb, data['media_info'], resp['action'])
     msg = f"Scrobble {category} successful for {name} at {resp['progress']:.2f}%"
     logger.info(msg)
     notify(msg, category=f"scrobble.{category}")
     self.backlog_cleaner.clear()
示例#8
0
 def run(self):
     while True:
         if self.can_connect():
             self.update_vars()
             self.conn_loop()
             if self.poll_timer:
                 self.poll_timer.cancel()
             time.sleep(1)
         else:
             logger.info('Unable to connect to MPV. Check ipc path.')
             time.sleep(10)
示例#9
0
 def run(self):
     while True:
         try:
             self.update_status()
         except requests.ConnectionError:
             logger.info(f'Unable to connect to {self.name}. Ensure that '
                         'the web interface is running.')
             self.status = {}
         if not self.status.get("filepath") and not self.status.get("media_info"):
             self.status = {}
         self.handle_status_update()
         time.sleep(self.poll_interval)
示例#10
0
    def handle_successful_scrobble(self, verb, data, resp):
        if 'movie' in resp:
            name = "{title} ({year:04})".format(**resp['movie'])
        else:
            name = (
                "{title} ({year:04})".format(**resp['show']) +
                " [{season}x{number:02}] {title}".format(**resp['episode']))
        os.environ['TRAKTTITLE'] = name

        category = self._determine_category(verb, data['media_info'],
                                            resp['action'])
        msg = f"Scrobble {category} successful {name} at {resp['progress']:.2f}%"
        logger.info(msg)
        notify(title=name, body=msg, category=f"scrobble.{category}")
        self.backlog_cleaner.clear()
示例#11
0
 def get_token(self):
     logger.info("Retrieving plex token")
     login = self.ask("Plex login ID:")
     pwd = self.secret("Plex password:"******"user"]["authToken"]
     elif resp is not None:
         err_msg = resp.json().get("error", resp.text)
         self.line(err_msg, "error")
         logger.error(err_msg)
         return None
     else:
         logger.error("Unable to get access token")
         return None
示例#12
0
 def run(self):
     while True:
         if self.can_connect():
             self.update_vars()
             self.conn_loop()
             if self.vars.get('state', 0) != 0:
                 # create a 'stop' event in case the player didn't send 'end-file'
                 self.vars['state'] = 0
                 self.update_status()
             self.vars = {}
             if self.poll_timer:
                 self.poll_timer.cancel()
             time.sleep(self.restart_delay)
         else:
             logger.info('Unable to connect to MPV. Check ipc path.')
             time.sleep(self.poll_interval)
示例#13
0
 def get_access_token(self):
     if not self.token_data:
         logger.info("Access token not found. Initiating device authentication.")
         self.device_auth()
     elif self.is_token_expired():
         logger.info("Trakt access token expired. Refreshing.")
         notify("Trakt access token expired. Refreshing.", category="trakt")
         self.refresh_token()
     if not self.token_data or self.is_token_expired():
         # either device_auth or refresh_token failed to get token
         logger.critical("Unable to get access token.")
         notify("Failed to authorize application with Trakt. "
                "Run 'trakts auth' manually to retry.",
                stdout=True, category="trakt")
     else:
         return self.token_data['access_token']
示例#14
0
    def clear(self):
        self.remove_expired()

        failed = []
        for item in self.backlog:
            logger.debug(f'Adding item to history {item}')
            if trakt.add_to_history(**item):
                logger.info("Successfully added media to history.")
            else:
                failed.append(item)

        self.backlog = failed
        self.save_backlog()

        if self.timer_enabled:
            self.timer.cancel()
            self._make_timer()
示例#15
0
 def scrobble(self, verb, data):
     resp = trakt.scrobble(verb, **data)
     if resp:
         if 'movie' in resp:
             name = resp['movie']['title']
         else:
             name = (resp['show']['title'] +
                     " S{season:02}E{number:02}".format(**resp['episode']))
         msg = f"Scrobble {verb} successful for {name}"
         logger.info(msg)
         notify(msg)
         self.backlog_cleaner.clear()
     elif resp is False and verb == 'stop' and data['progress'] > 80:
         logger.warning('Scrobble unsuccessful. Will try again later.')
         self.backlog_cleaner.add(data)
     else:
         logger.warning('Scrobble unsuccessful.')
示例#16
0
def refresh_token(token_data):
    exchange_params = {
        "url": API_URL + '/oauth/token',
        "headers": {"Content-Type": "application/json"},
        "json": {
            "refresh_token": token_data['refresh_token'],
            "client_id": CLIENT_ID,
            "client_secret": CLIENT_SECRET,
            "redirect_uri": "urn:ietf:wg:oauth:2.0:oob",
            "grant_type": "refresh_token"
        }
    }
    exchange_resp = safe_request('post', exchange_params)
    if exchange_resp and exchange_resp.status_code == 200:
        logger.info('Refreshed access token.')
        return exchange_resp.json()
    else:
        logger.info("Error refreshing token.")
示例#17
0
    def run(self):
        while True:
            try:
                self.update_status()
            except requests.ConnectionError:
                logger.info(f'Unable to connect to {self.name}. Ensure that '
                            'the web interface is running.')
                self.status = {}
            except requests.HTTPError as e:
                logger.error(f"Error while getting data from {self.name}: {e}")
                break
            if not self.status.get("filepath") and not self.status.get(
                    "media_info"):
                self.status = {}
            self.handle_status_update()
            time.sleep(self.poll_interval)

        logger.warning(f"{self.name} monitor stopped")
示例#18
0
 def __init__(self, scrobble_queue):
     super().__init__()
     logger.info('Started monitor for ' + self.name)
     self.scrobble_queue = scrobble_queue
     self.skip_interval = self.config['skip_interval']
     self.preview_threshold = self.config['preview_threshold']
     self.preview_duration = self.config['preview_duration']
     self.fast_pause_threshold = self.config['fast_pause_threshold']
     self.fast_pause_duration = self.config['fast_pause_duration']
     self.is_running = False
     self.status = {}
     self.prev_state = {}
     self.preview = False
     self.fast_pause = False
     self.scrobble_buf = None
     self.lock = Lock()
     self.preview_timer: ResumableTimer = None
     self.fast_pause_timer: ResumableTimer = None
示例#19
0
def get_access_token():
    global token_data
    if not token_data:
        logger.info("Access token not found in config. "
                    "Initiating device authentication.")
        token_data = device_auth()
        write_json(token_data, TRAKT_TOKEN_PATH)
    elif token_data['created_at'] + token_data['expires_in'] - \
            time.time() < 86400:
        logger.info("Access token about to expire. Refreshing.")
        token_data = refresh_token(token_data)
        write_json(token_data, TRAKT_TOKEN_PATH)
    if not token_data:
        logger.error("Unable to get access token. "
                     f"Try deleting {TRAKT_TOKEN_PATH!s} and retry.")
        notify("Failed to authorize application.", stdout=True)
        sys.exit(1)
    return token_data['access_token']
示例#20
0
def get_token():
    global token_data
    if not token_data:
        logger.info("Retrieving plex token")
        login = input("Plex login ID: ")
        pwd = getpass()
        resp = plex_token_auth(login, pwd)
        if resp.ok:
            token_data = {"token": resp.json()["user"]["authToken"]}
            write_json(token_data, PLEX_TOKEN_PATH)
            logger.info(f"Saved plex token to {PLEX_TOKEN_PATH}")
        elif resp is not None:
            print(resp.json().get("error", resp.text))
            return None
        else:
            logger.error("Unable to get access token. "
                         f"Try deleting {PLEX_TOKEN_PATH!s} and retry.")
            return None
    return token_data['token']
示例#21
0
 def scrobble(self, verb, data):
     logger.debug(f"Scrobbling {verb} at {data['progress']:.2f}% for "
                  f"{data['media_info']['title']}")
     resp = trakt.scrobble(verb, **data)
     if resp:
         if 'movie' in resp:
             name = resp['movie']['title']
         else:
             name = (resp['show']['title'] +
                     " S{season:02}E{number:02}".format(**resp['episode']))
         category = 'resume' if self.is_resume(verb, data) else verb
         msg = f"Scrobble {category} successful for {name}"
         logger.info(msg)
         notify(msg, category=f"scrobble.{category}")
         self.backlog_cleaner.clear()
     elif resp is False and verb == 'stop' and data['progress'] > 80:
         logger.warning('Scrobble unsuccessful. Will try again later.')
         self.backlog_cleaner.add(data)
     else:
         logger.warning('Scrobble unsuccessful.')
     self.prev_scrobble = (verb, data)
示例#22
0
def device_auth():
    code_data = get_device_code()
    if not code_data:
        logger.error('Failed device auth.')
        sys.exit(1)

    logger.info(f"Verification URL: {code_data['verification_url']}")
    logger.info(f"User Code: {code_data['user_code']}")
    notify("Open {verification_url} in your browser and enter this code: "
           "{user_code}".format(**code_data),
           timeout=60,
           stdout=True)
    webbrowser.open(code_data['verification_url'])

    start = time.time()
    while time.time() - start < code_data['expires_in']:
        token_data = get_device_token(code_data['device_code'])
        if not token_data:
            logger.debug('Waiting for user to authorize the app.')
            time.sleep(int(code_data['interval']))
        else:
            notify('App authorized successfully.', stdout=True)
            logger.info('Device auth successful.')
            break
    else:
        logger.error('Timed out during auth.')
    return token_data
示例#23
0
    def device_auth(self):
        code_data = self.get_device_code()
        if not code_data:
            logger.error("Could not get device code.")
            return

        logger.info(f"Verification URL: {code_data['verification_url']}")
        logger.info(f"User Code: {code_data['user_code']}")
        notify("Open {verification_url} in your browser and enter this code: "
               "{user_code}".format(**code_data),
               timeout=30,
               stdout=True,
               category="trakt")
        webbrowser.open(code_data['verification_url'])

        start = time.time()
        while time.time() - start < code_data['expires_in']:
            if self.get_device_token(code_data['device_code']):
                notify('App authorized successfully.',
                       stdout=True,
                       category="trakt")
                logger.info('App authorized successfully.')
                break
            logger.debug('Waiting for user to authorize the app.')
            time.sleep(int(code_data['interval']))
        else:
            logger.error('Timed out during auth.')
示例#24
0
    def device_auth(self):
        code_data = self.get_device_code()
        if not code_data:
            logger.error("Could not get device code.")
            return

        logger.info(f"Verification URL: {code_data['verification_url']}")
        logger.info(f"User Code: {code_data['user_code']}")
        notify(
            "Open {verification_url} in your browser and enter this code: "
            "{user_code}".format(**code_data), timeout=30, stdout=True,
            category="trakt")

        # automatically open the url in the default browser
        # but we don't want to use terminal-based browsers - most likely not
        # what the user wants
        term_bak = os.environ.pop("TERM", None)
        webbrowser.open(code_data['verification_url'])
        if term_bak is not None:
            os.environ["TERM"] = term_bak

        start = time.time()
        while time.time() - start < code_data['expires_in']:
            if self.get_device_token(code_data['device_code']):
                notify('App authorized successfully.',
                       stdout=True, category="trakt")
                logger.info('App authorized successfully.')
                break
            logger.debug('Waiting for user to authorize the app.')
            time.sleep(int(code_data['interval']))
        else:
            logger.error('Timed out during auth.')
示例#25
0
    def handle(self):
        from trakt_scrobbler.player_monitors.plex import token

        if self.option("force"):
            del token.data
            self.line("Forcing plex authentication")

        token_data = self.option("token")
        if token_data is not None:
            if token_data == 'null':
                token_data = self.ask("Enter token:")
            # TODO: Verify that token is valid
            token.data = token_data
        elif not token:
            token_data = self.get_token()
            if token_data:
                token.data = token_data
                logger.info("Saved plex token")

        if token:
            self.line("Plex token is saved.")
        else:
            self.line("Failed to retrieve plex token.", "error")
            return 1
示例#26
0
 def __init__(self, scrobble_queue, backlog_cleaner):
     super().__init__(name='scrobbler')
     logger.info('Started scrobbler thread.')
     self.scrobble_queue = scrobble_queue
     self.backlog_cleaner = backlog_cleaner