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)
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}
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.")
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)
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)
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
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()
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)
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)
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()
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
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)
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']
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()
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.')
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.")
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")
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
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']
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']
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)
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
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.')
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.')
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
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