def _should_skip(p_name: str, links: Candidates) -> bool: key = str(sorted(links)) skip_dir = Path(get_user_cache_dir('music_manager/disambiguation_skip')) skip_path = skip_dir.joinpath(f'{p_name}.json') if skip_path.exists(): with skip_path.open('r') as f: try: return json.load(f)[key] except KeyError: return False return False
def get_clean_paths(max_wait: int, arg_paths): from os import getpid from selectors import DefaultSelector, EVENT_READ from socket import socket from time import monotonic from filelock import FileLock from psutil import Process, NoSuchProcess from ds_tools.fs.paths import get_user_cache_dir cache_dir = Path(get_user_cache_dir('music_manager')) with FileLock(cache_dir.joinpath('init.lock').as_posix()): pid = getpid() active_path = cache_dir.joinpath('active_pid_port.txt') try: with active_path.open('r') as f: active_pid, port = map(int, f.read().split(',')) except OSError: active = True else: try: active = not Process(active_pid).is_running() except NoSuchProcess: active = True sock = socket() if active: sock.bind(('localhost', 0)) sock.listen(100) sock.setblocking(False) port = sock.getsockname()[1] log.info(f'Primary instance with {pid=} {port=}') with active_path.open('w') as f: f.write(f'{pid},{port}') else: log.info(f'Follower instance with {pid=} {port=}') if active: paths = list(arg_paths) selector = DefaultSelector() def accept(sock, mask): conn, addr = sock.accept() conn.setblocking(False) selector.register(conn, EVENT_READ, read) def read(conn, mask): if data := conn.recv(2000): paths.append(data.decode('utf-8')) # log.debug(f'Received path={data!r} from other instance') else: selector.unregister(conn) conn.close()
def _always_skip(p_name: str, links: Candidates): key = str(sorted(links)) skip_dir = Path(get_user_cache_dir('music_manager/disambiguation_skip')) skip_path = skip_dir.joinpath(f'{p_name}.json') if skip_path.exists(): with skip_path.open('r') as f: data = json.load(f) else: data = {} data[key] = True with skip_path.open('w') as f: json.dump(data, f, sort_keys=True, indent=4)
def get_wiki_cover_choice(self) -> Optional[Path]: from ..popups.choose_image import choose_image if images := self.get_wiki_cover_images(): if title := choose_image(images): cover_dir = Path(get_user_cache_dir('music_manager/cover_art')) name = title.split( ':', 1)[1] if title.lower().startswith('file:') else title path = cover_dir.joinpath(name) if not path.is_file(): img_data = self.wiki_client.get_image(title) with path.open('wb') as f: f.write(img_data) return path
def init_sym_spell(): from pathlib import Path from symspellpy import SymSpell from ds_tools.fs.paths import get_user_cache_dir sym_spell = SymSpell(max_dictionary_edit_distance=0, prefix_length=1) dict_path_pkl = Path( get_user_cache_dir('music_manager')).joinpath('words.pkl.gz') if dict_path_pkl.exists(): log.debug(f'Loading pickled spellcheck dictionary: {dict_path_pkl}') sym_spell.load_pickle(dict_path_pkl) else: import lzma import pkg_resources dict_path = pkg_resources.resource_filename( 'symspellpy', 'frequency_dictionary_en_82_765.txt') sym_spell.load_dictionary(dict_path, 0, 1) word_list_path_xz = Path( pkg_resources.resource_filename( 'music', '../../etc/scowl/words.xz')).resolve() log.debug( f'Loading default dictionary + word list from {word_list_path_xz}') with lzma.open(word_list_path_xz, 'rt', encoding='utf-8') as f: word_list = f.read().splitlines() loaded = sym_spell._words min_count = min(loaded.values()) add_word = sym_spell.create_dictionary_entry for word in word_list: try: loaded[word] except KeyError: add_word(word, min_count) fmt = 'Saving pickled spellcheck dictionary (this is a one-time action that may take about 15 seconds): {}' log.info(fmt.format(dict_path_pkl)) sym_spell.save_pickle(dict_path_pkl) return sym_spell
def __init__( self, email=None, serial=None, no_store_prompt=False, update_password=False, config_path=None, reauth=False ): """ :param str email: The email address to be used for login :param str serial: The serial number of the thermostat to be managed by this client :param bool no_store_prompt: Do not prompt to store the password securely :param bool update_password: Prompt to update the stored password, even if one already exists :param str config_path: The config path to use :param bool reauth: Force reauth, even if a cached session exists """ super().__init__(NEST_URL, user_agent_fmt=USER_AGENT_CHROME, headers={'Referer': NEST_URL}) self._reauth = reauth self._lock = RLock() self._cache_dir = Path(get_user_cache_dir('nest')) self._config_path = Path(config_path or DEFAULT_CONFIG_PATH).expanduser() self._no_store_pw = no_store_prompt self._update_pw = update_password self._email = self._get_config('credentials', 'email', 'email address', email, required=True) self._session_info = None self._session_expiry = None self._user_id = None self._nest_host_port = ('home.nest.com', None) self._serial = self._get_config('device', 'serial', 'thermostat serial number', serial)
def process_lyrics(self, song, title=None, size=12, ignore_len=False, output_dir=None, extra_linebreaks=None, extra_lines=None, replace_lb=False, **kwargs): """ Process lyrics from the given song and write them to an html file :param str|None song: Song endpoint for lyrics :param str title: Title to use (overrides automatically-extracted title is specified) :param int size: Font size to use for output :param bool ignore_len: Ignore stanza length mismatches :param output_dir: :param extra_linebreaks: :param english_extra: :param korean_extra: :param replace_lb: :param kwargs: Keyword arguments to pass to :func:`LyricFetcher.get_lyrics` """ if output_dir and (os.path.exists(output_dir) and not os.path.isdir(output_dir)): raise ValueError( 'Invalid output dir - it exists but is not a directory: {}'. format(output_dir)) lyrics = self.get_lyrics(song, title, **kwargs) discovered_title = lyrics.pop('title', None) stanzas = normalize_lyrics(lyrics, extra_linebreaks, extra_lines, replace_lb=replace_lb, ignore_len=ignore_len) stanzas['Translation'] = stanzas.pop('English') max_stanzas = max( len(lang_stanzas) for lang_stanzas in stanzas.values()) for lang, lang_stanzas in stanzas.items(): add_stanzas = max_stanzas - len(lang_stanzas) if add_stanzas: for i in range(add_stanzas): lang_stanzas.append([]) final_title = title or discovered_title or song render_vars = { 'title': final_title, 'lyrics': stanzas, 'lang_order': ['Korean', 'Translation'], 'stanza_count': max_stanzas, 'url_for': url_for_file } prettified = self.song_tmpl.render(**render_vars) output_dir = output_dir or get_user_cache_dir('lyric_fetcher/lyrics') validate_or_make_dir(output_dir) output_filename = 'lyrics_{}.html'.format( final_title.replace(' ', '_').replace('?', '')) output_path = os.path.join(output_dir, output_filename) log.info('Saving lyrics to {}'.format(output_path)) with open(output_path, 'w', encoding='utf-8') as f: f.write(prettified)
page = self.edition.page return MediaWikiClient(page.site) def get_album_cover_urls(self) -> Optional[dict[str, str]]: page = self.edition.page if image_titles := self._edition_client.get_page_image_titles(page.title)[page.title]: return self._edition_client.get_image_urls(image_titles) return None def get_album_cover(self) -> Optional[str]: if not self.update_cover: return None elif not (urls := self.get_album_cover_urls()): return None cover_dir = Path(get_user_cache_dir('music_manager/cover_art')) tmp_dir = TemporaryDirectory() _tmp_dir = Path(tmp_dir.name) tmp_html = _tmp_dir.joinpath('options.html') try: song_file = next(iter(self.album_dir)) # type: SongFile cover_data, ext = song_file.get_cover_data() except Exception as e: log.error(f'Unable to extract current album art: {e}') tmp_img = None else: tmp_img = _tmp_dir.joinpath(f'current.{ext}') with tmp_img.open('wb') as f: f.write(cover_data) with tmp_html.open('w', encoding='utf-8', newline='\n') as f: