def get_user_dir(): """Place where QL saves its state, database, config etc.""" if os.name == "nt": USERDIR = os.path.join(windows.get_appdate_dir(), "Quod Libet") else: USERDIR = os.path.join(os.path.expanduser("~"), ".quodlibet") if not PY2: USERDIR += "_py3" if 'QUODLIBET_USERDIR' in environ: USERDIR = environ['QUODLIBET_USERDIR'] # XXX: Exec conf.py in this directory, used to override const globals # e.g. for setting USERDIR for the Windows portable version # Note: execfile doesn't handle unicode paths on windows, so encode. # (this doesn't use the old win api in case of str compared to os.*) _CONF_PATH = os.path.join( os.path.dirname(os.path.realpath(__file__)), "conf.py") if PY2: # FIXME: PY3PORT try: execfile(_CONF_PATH) except IOError: pass # XXX: users shouldn't assume the dir is there, but we currently do in # some places mkdir(USERDIR, 0o750) return USERDIR
def init(icon=None, proc_title=None, name=None): global quodlibet print_d("Entering quodlibet.init") _gtk_init() _gtk_icons_init(get_image_dir(), icon) _gst_init() _dbus_init() _init_debug() from gi.repository import GLib if proc_title: GLib.set_prgname(proc_title) set_process_title(proc_title) # Issue 736 - set after main loop has started (gtk seems to reset it) GLib.idle_add(set_process_title, proc_title) if name: GLib.set_application_name(name) mkdir(get_user_dir(), 0750) print_d("Finished initialization.")
def get_user_dir(): """Place where QL saves its state, database, config etc.""" if os.name == "nt": USERDIR = os.path.join(windows.get_appdata_dir(), "Quod Libet") elif is_osx(): USERDIR = os.path.join(os.path.expanduser("~"), ".quodlibet") else: USERDIR = os.path.join(xdg_get_config_home(), "quodlibet") if not os.path.exists(USERDIR): tmp = os.path.join(os.path.expanduser("~"), ".quodlibet") if os.path.exists(tmp): USERDIR = tmp if 'QUODLIBET_USERDIR' in environ: USERDIR = environ['QUODLIBET_USERDIR'] if build.BUILD_TYPE == u"windows-portable": USERDIR = os.path.normpath(os.path.join( os.path.dirname(path2fsn(sys.executable)), "..", "..", "config")) # XXX: users shouldn't assume the dir is there, but we currently do in # some places mkdir(USERDIR, 0o750) return USERDIR
def dump_to_disk(dump_dir, exc_info): """Writes a new error log file into 'dump_dir' Args: dump_dir (path-like) exc_info (tuple): sys.exc_info() result tuple """ try: mkdir(dump_dir) except EnvironmentError: print_exc() return time_ = time.localtime() dump_path = os.path.join( dump_dir, time.strftime("Dump_%Y%m%d_%H%M%S.txt", time_)) header = format_dump_header(exc_info).encode("utf-8") log = format_dump_log().encode("utf-8") try: with open(dump_path, "wb") as dump: dump.write(header) dump.write(log) except EnvironmentError: print_exc()
def write(self, filename): """Write config to filename. Can raise EnvironmentError """ assert isinstance(filename, fsnative) mkdir(os.path.dirname(filename)) # temporary set the new version for saving if self._version is not None: self.add_section("__config__") self.set("__config__", "version", self._version) try: with atomic_save(filename, "wb") as fileobj: if PY2: self._config.write(fileobj) else: temp = StringIO() self._config.write(temp) data = temp.getvalue().encode("utf-8", "surrogateescape") fileobj.write(data) finally: if self._loaded_version is not None: self.set("__config__", "version", self._loaded_version)
def setUp(self): # Testing locally is VERY dangerous without this... self.assertTrue(_TEMP_DIR in PLAYLISTS or os.name == "nt", msg="Failing, don't want to delete %s" % PLAYLISTS) try: shutil.rmtree(PLAYLISTS) except OSError: pass mkdir(PLAYLISTS) self.lib = quodlibet.browsers.playlists.library = SongLibrary() self.lib.librarian = SongLibrarian() all_songs = SONGS + [self.ANOTHER_SONG] for af in all_songs: af.sanitize() self.lib.add(all_songs) self.big = pl = FileBackedPlaylist.new(PLAYLISTS, "Big", self.lib) pl.extend(SONGS) pl.write() self.small = pl = FileBackedPlaylist.new(PLAYLISTS, "Small", self.lib) pl.extend([self.ANOTHER_SONG]) pl.write() PlaylistsBrowser.init(self.lib) self.bar = PlaylistsBrowser(self.lib) self.bar.connect('songs-selected', self._expected) self.bar._select_playlist(self.bar.playlists()[0]) self.expected = None
def setUp(self): try: shutil.rmtree(PLAYLISTS) except OSError: pass mkdir(PLAYLISTS) self.lib = quodlibet.browsers.playlists.library = SongLibrary() self.lib.librarian = SongLibrarian() all_songs = SONGS + [self.ANOTHER_SONG] for af in all_songs: af.sanitize() self.lib.add(all_songs) pl = Playlist.new(PLAYLISTS, "Big", self.lib) pl.extend(SONGS) pl.write() pl = Playlist.new(PLAYLISTS, "Small", self.lib) pl.extend([self.ANOTHER_SONG]) pl.write() PlaylistsBrowser.init(self.lib) self.bar = PlaylistsBrowser(self.lib) self.bar.connect('songs-selected', self._expected) self.bar._select_playlist(self.bar.playlists()[0]) self.expected = None
def test_manydeep(self): self.failUnless(not os.path.isdir("nonext")) mkdir("nonext/test/test2/test3") try: self.failUnless(os.path.isdir("nonext/test/test2/test3")) finally: os.rmdir("nonext/test/test2/test3") os.rmdir("nonext/test/test2") os.rmdir("nonext/test") os.rmdir("nonext")
def get_cache_dir(): """The directory to store things into which can be deleted at any time""" if os.name == "nt" and build.BUILD_TYPE == u"windows-portable": # avoid writing things to the host system for the portable build path = os.path.join(get_user_dir(), "cache") else: path = os.path.join(xdg_get_cache_home(), "quodlibet") mkdir(path, 0o700) return path
def test_lyric_filename_search_builtin_default(self): """test built-in default""" with self.lyric_filename_test_setup(no_config=True) as ts: fp = os.path.join(ts.root, ts["artist"], ts["title"] + ".lyric") p = os.path.dirname(fp) mkdir(p) with io.open(fp, "w", encoding='utf-8') as f: f.write(u"") search = unquote(ts.lyric_filename) os.remove(fp) os.rmdir(p) self.assertEqual(search, fp)
def test_lyrics_from_file(self): with temp_filename() as filename: af = AudioFile(artist='Motörhead', title='this: again') af.sanitize(filename) lyrics = "blah!\nblasé 😬\n" lyrics_dir = os.path.dirname(af.lyric_filename) mkdir(lyrics_dir) with io.open(af.lyric_filename, "w", encoding='utf-8') as lf: lf.write(text_type(lyrics)) self.failUnlessEqual(af("~lyrics").splitlines(), lyrics.splitlines()) os.remove(af.lyric_filename) os.rmdir(lyrics_dir)
def dump_to_disk(self, type_, value, traceback): """Writes the dump files to DUMDIR""" mkdir(self.DUMPDIR) header = format_dump_header(type_, value, traceback).encode("utf-8") log = format_dump_log().encode("utf-8") print(self.dump_path) with open(self.dump_path, "wb") as dump: with open(self.minidump_path, "wb") as minidump: minidump.write(header) dump.write(header) dump.write(log)
def from_except(Kind, *args): mkdir(Kind.DUMPDIR) dump = os.path.join( Kind.DUMPDIR, time.strftime("Dump_%Y%m%d_%H%M%S.txt")) minidump = os.path.join( Kind.DUMPDIR, time.strftime("MiniDump_%Y%m%d_%H%M%S.txt")) full_args = list(args) + [dump, minidump] Kind.__dump(*full_args) # Don't get in a recursive exception handler loop. if not Kind.running: Kind.running = True Kind.instance = Kind(*full_args) return Kind.instance
def rename(self, newname): """Rename a file. Errors are not handled. This shouldn't be used directly; use library.rename instead.""" if os.path.isabs(newname): mkdir(os.path.dirname(newname)) else: newname = os.path.join(self('~dirname'), newname) if not os.path.exists(newname): shutil.move(self['~filename'], newname) elif normalize_path(newname, canonicalise=True) != self['~filename']: raise ValueError self.sanitize(newname)
def test_manydeep(self): self.failUnless(not os.path.isdir("nonext")) t = mkdtemp() path = os.path.join(t, "nonext", "test", "test2", "test3") mkdir(path) try: self.failUnless(os.path.isdir(path)) finally: os.rmdir(path) path = os.path.dirname(path) os.rmdir(path) path = os.path.dirname(path) os.rmdir(path) path = os.path.dirname(path) os.rmdir(path) os.rmdir(t)
def _open(self, *args): from quodlibet import qltk self._id = None try: if not os.path.exists(self._path): mkdir(os.path.dirname(self._path)) os.mkfifo(self._path, 0600) fifo = os.open(self._path, os.O_NONBLOCK) f = os.fdopen(fifo, "r", 4096) self._id = qltk.io_add_watch( f, GLib.PRIORITY_DEFAULT, GLib.IO_IN | GLib.IO_ERR | GLib.IO_HUP, self._process, *args) except (EnvironmentError, AttributeError): pass
def setUp(self): self.success = False # Testing locally is VERY dangerous without this... self.assertTrue(_TEMP_DIR in _DEFAULT_PLAYLIST_DIR or os.name == "nt", msg="Failing, don't want to delete %s" % _DEFAULT_PLAYLIST_DIR) try: shutil.rmtree(_DEFAULT_PLAYLIST_DIR) except OSError: pass mkdir(_DEFAULT_PLAYLIST_DIR) init_fake_app() self.lib = quodlibet.browsers.playlists.library = SongFileLibrary() self.lib.librarian = SongLibrarian() for af in self.ALL_SONGS: af.sanitize() self.lib.add(self.ALL_SONGS) self.big = pl = FileBackedPlaylist.new(_DEFAULT_PLAYLIST_DIR, "Big", self.lib, self.lib.playlists) pl.extend(SONGS) pl.write() self.small = pl = XSPFBackedPlaylist.new(_DEFAULT_PLAYLIST_DIR, "Small", self.lib, self.lib.playlists) pl.extend([self.ANOTHER_SONG]) pl.write() PlaylistsBrowser.init(self.lib) self.bar = PlaylistsBrowser(self.lib, self.MockConfirmerAccepting) self.bar.connect('songs-selected', self._expected) self.bar._select_playlist(self.bar.playlists()[0]) self.expected = None # Uses the declining confirmer. self.bar_decline = PlaylistsBrowser(self.lib, self.MockConfirmerDeclining) self.bar_decline.connect('songs-selected', self._expected_decline) self.bar_decline._select_playlist(self.bar_decline.playlists()[0]) # Note that _do() uses self.expected, but _do() is not called by the # testcase for declining the prompt. Tests fail with a shared expected. self.expected_decline = None
def dump_items(filename, items): """Pickle items to disk. Doesn't handle exceptions. """ dirname = os.path.dirname(filename) mkdir(dirname) with atomic_save(filename, "wb") as fileobj: # While protocol 2 is usually faster it uses __setitem__ # for unpickle and we override it to clear the sort cache. # This roundtrip makes it much slower, so we use protocol 1 # unpickle numbers (py2.7): # 2: 0.66s / 2 + __set_item__: 1.18s / 1 + __set_item__: 0.72s # see: http://bugs.python.org/issue826897 pickle.dump(items, fileobj, 1)
def dump_items(filename, items): """Pickle items to disk. Doesn't handle exceptions. """ dirname = os.path.dirname(filename) mkdir(dirname) with util.atomic_save(filename, ".tmp", "wb") as fileobj: # While protocol 2 is usually faster it uses __setitem__ # for unpickle and we override it to clear the sort cache. # This roundtrip makes it much slower, so we use protocol 1 # unpickle numbers (py2.7): # 2: 0.66s / 2 + __set_item__: 1.18s / 1 + __set_item__: 0.72s # see: http://bugs.python.org/issue826897 pickle.dump(items, fileobj, 1)
def save(self, filename=None): """Save the library to the given filename, or the default if `None`""" if filename is None: filename = self.filename print_d("Saving contents to %r." % filename, self) try: dirname = os.path.dirname(filename) mkdir(dirname) with atomic_save(filename, "wb") as fileobj: fileobj.write(dump_audio_files(self.get_content())) # unhandled SerializationError, shouldn't happen -> better # not replace the library file with nothing except EnvironmentError: print_w("Couldn't save library to path: %r" % filename) else: self.dirty = False
def open(self, ignore_lock=False): """Create the FIFO and listen to it. Raises: FIFOError in case another process is already using it. """ from quodlibet import qltk mkdir(os.path.dirname(self._path)) try: os.mkfifo(self._path, 0o600) except OSError: # maybe exists, we'll fail below otherwise pass try: fifo = os.open(self._path, os.O_NONBLOCK) except OSError: return while True: try: fcntl.flock(fifo, fcntl.LOCK_EX | fcntl.LOCK_NB) except InterruptedError: # EINTR continue except BlockingIOError: # EWOULDBLOCK if not ignore_lock: raise FIFOError("fifo already locked") except OSError as e: print_d(f"fifo locking failed: {e!r}") break try: f = os.fdopen(fifo, "rb", 4096) except OSError as e: print_e(f"Couldn't open FIFO ({e!r})") else: self._source_id = qltk.io_add_watch( f, GLib.PRIORITY_DEFAULT, GLib.IO_IN | GLib.IO_ERR | GLib.IO_HUP, self._process, )
def test_lyric_filename_search_order_priority(self): """test custom lyrics order priority""" with self.lyric_filename_test_setup() as ts: root2 = os.path.join(get_home_dir(), ".lyrics") # built-in default fp2 = os.path.join(root2, ts["artist"] + " - " + ts["title"] + ".lyric") p2 = os.path.dirname(fp2) mkdir(p2) with io.open(fp2, "w", encoding='utf-8') as f: f.write(u"") fp = os.path.join(ts.root, ts["artist"] + " - " + ts["title"] + ".lyric") with io.open(fp, "w", encoding='utf-8') as f: f.write(u"") mkdir(p2) search = ts.lyric_filename os.remove(fp2) os.rmdir(p2) os.remove(fp) self.assertEqual(search, fp)
def test_invalid_glob_path(self): config.set("albumart", "force_filename", str(True)) config.set("albumart", "filename", "*.jpg") # Make a dir which contains an invalid glob path = os.path.join(self.full_path("[a-2]"), "cover.jpg") mkdir(os.path.dirname(path)) f = self.add_file(path) # Change the song's path to contain the invalid glob old_song_path = self.song['~filename'] new_song_path = os.path.join(os.path.dirname(path), os.path.basename(old_song_path)) self.song['~filename'] = new_song_path # The glob in the dirname should be ignored, while the # glob in the filename/basename is honored assert path_equal(os.path.abspath(self._find_cover(self.song).name), f) self.song['~filename'] = old_song_path
def write(self, filename): """Write config to filename. Can raise EnvironmentError """ assert is_fsnative(filename) mkdir(os.path.dirname(filename)) # temporary set the new version for saving if self._version is not None: self.add_section("__config__") self.set("__config__", "version", self._version) try: with atomic_save(filename, ".tmp", "wb") as fileobj: self._config.write(fileobj) finally: if self._loaded_version is not None: self.set("__config__", "version", self._loaded_version)
def _open(self, ignore_lock, *args): from quodlibet import qltk self._id = None mkdir(os.path.dirname(self._path)) try: os.mkfifo(self._path, 0600) except OSError: # maybe exists, we'll fail below otherwise pass try: fifo = os.open(self._path, os.O_NONBLOCK) except OSError: return while True: try: fcntl.flock(fifo, fcntl.LOCK_EX | fcntl.LOCK_NB) except IOError as e: # EINTR on linux if e.errno == errno.EINTR: continue if ignore_lock: break # OSX doesn't support fifo locking, so check errno if e.errno == errno.EWOULDBLOCK: raise FIFOError("fifo already locked") else: print_d("fifo locking failed: %r" % e) break try: f = os.fdopen(fifo, "r", 4096) except OSError: pass self._id = qltk.io_add_watch( f, GLib.PRIORITY_DEFAULT, GLib.IO_IN | GLib.IO_ERR | GLib.IO_HUP, self._process, *args)
def test_lyric_filename_search_special_characters_across_path(self): """test '<' and/or '>' in name across path separator (not parsed (transparent to test))""" with self.lyric_filename_test_setup(no_config=True) as ts: # test '<' and '>' in name across path # (not parsed (transparent to test)) ts['artist'] = "a < b" ts['title'] = "b > a" parts = [ts.root, ts["artist"], ts["title"] + ".lyric"] rpf = RootPathFile(ts.root, os.path.sep.join(parts)) rootp = ts.root rmdirs = [] # ensure valid dir existence for p in rpf.end.split(os.path.sep)[:-1]: rootp = os.path.sep.join([ts.root, p]) if not RootPathFile(ts.root, rootp).valid: rootp = os.path.sep.join([ts.root, escape_filename(p)]) self.assertTrue( RootPathFile(ts.root, rootp).valid, "even escaped target dir part is not valid!") if not os.path.exists(rootp): mkdir(rootp) rmdirs.append(rootp) if not rpf.valid: rpf = RootPathFile(rpf.root, rpf.pathfile_escaped) with io.open(rpf.pathfile, "w", encoding='utf-8') as f: f.write(u"") # search for lyric file search = ts.lyric_filename # clean up test lyric file / path os.remove(rpf.pathfile) for p in rmdirs: os.rmdir(p) # test whether the 'found' file is the test lyric file fp = rpf.pathfile if is_windows(): fp = fp.lower() # account for 'os.path.normcase' santisatation search = search.lower() # compensate for the above self.assertEqual(search, fp)
def get_user_dir(): """Place where QL saves its state, database, config etc.""" if os.name == "nt": USERDIR = os.path.join(windows.get_appdate_dir(), "Quod Libet") else: USERDIR = os.path.join(os.path.expanduser("~"), ".quodlibet") if 'QUODLIBET_USERDIR' in environ: USERDIR = environ['QUODLIBET_USERDIR'] if build.BUILD_TYPE == u"windows-portable": USERDIR = os.path.normpath( os.path.join(os.path.dirname(path2fsn(sys.executable)), "..", "..", "config")) # XXX: users shouldn't assume the dir is there, but we currently do in # some places mkdir(USERDIR, 0o750) return USERDIR
def test_invalid_glob_path(self): config.set("albumart", "force_filename", str(True)) config.set("albumart", "filename", "*.jpg") # Make a dir which contains an invalid glob path = os.path.join(self.full_path("[a-2]"), "cover.jpg") mkdir(os.path.dirname(path)) f = self.add_file(path) # Change the song's path to contain the invalid glob old_song_path = self.song['~filename'] new_song_path = os.path.join(os.path.dirname(path), os.path.basename(old_song_path)) self.song['~filename'] = new_song_path # The glob in the dirname should be ignored, while the # glob in the filename/basename is honored assert path_equal( os.path.abspath(self._find_cover(self.song).name), f) self.song['~filename'] = old_song_path
def test_lyric_filename_search_special_characters_across_path(self): """test '<' and/or '>' in name across path separator (not parsed (transparent to test))""" with self.lyric_filename_test_setup(no_config=True) as ts: # test '<' and '>' in name across path # (not parsed (transparent to test)) ts['artist'] = "a < b" ts['title'] = "b > a" parts = [ts.root, ts["artist"], ts["title"] + ".lyric"] rpf = RootPathFile(ts.root, os.path.sep.join(parts)) rootp = ts.root rmdirs = [] # ensure valid dir existence for p in rpf.end.split(os.path.sep)[:-1]: rootp = os.path.sep.join([ts.root, p]) if not RootPathFile(ts.root, rootp).valid: rootp = os.path.sep.join([ts.root, escape_filename(p)]) self.assertTrue(RootPathFile(ts.root, rootp).valid, "even escaped target dir part is not valid!") if not os.path.exists(rootp): mkdir(rootp) rmdirs.append(rootp) if not rpf.valid: rpf = RootPathFile(rpf.root, rpf.pathfile_escaped) with io.open(rpf.pathfile, "w", encoding='utf-8') as f: f.write(u"") # search for lyric file search = ts.lyric_filename # clean up test lyric file / path os.remove(rpf.pathfile) for p in rmdirs: os.rmdir(p) # test whether the 'found' file is the test lyric file fp = rpf.pathfile if is_windows(): fp = fp.lower() # account for 'os.path.normcase' santisatation search = search.lower() # compensate for the above self.assertEqual(search, fp)
def _open(self, ignore_lock, *args): from quodlibet import qltk self._id = None mkdir(os.path.dirname(self._path)) try: os.mkfifo(self._path, 0o600) except OSError: # maybe exists, we'll fail below otherwise pass try: fifo = os.open(self._path, os.O_NONBLOCK) except OSError: return while True: try: fcntl.flock(fifo, fcntl.LOCK_EX | fcntl.LOCK_NB) except IOError as e: # EINTR on linux if e.errno == errno.EINTR: continue if ignore_lock: break # OSX doesn't support fifo locking, so check errno if e.errno == errno.EWOULDBLOCK: raise FIFOError("fifo already locked") else: print_d("fifo locking failed: %r" % e) break try: f = os.fdopen(fifo, "r", 4096) except OSError: pass self._id = qltk.io_add_watch(f, GLib.PRIORITY_DEFAULT, GLib.IO_IN | GLib.IO_ERR | GLib.IO_HUP, self._process, *args)
def get_user_dir(): """Place where QL saves its state, database, config etc.""" if os.name == "nt": USERDIR = os.path.join(windows.get_appdate_dir(), "Quod Libet") else: USERDIR = os.path.join(os.path.expanduser("~"), ".quodlibet") if not PY2: USERDIR += "_py3" if 'QUODLIBET_USERDIR' in environ: USERDIR = environ['QUODLIBET_USERDIR'] if build.BUILD_TYPE == u"windows-portable": USERDIR = os.path.join(get_base_dir(), "..", "..", "..", "config") # XXX: users shouldn't assume the dir is there, but we currently do in # some places mkdir(USERDIR, 0o750) return USERDIR
def save(self, filename=None): """Save the library to the given filename, or the default if `None`""" if filename is None: filename = self.filename print_d("Saving contents to %r." % filename, self) try: dirname = os.path.dirname(filename) mkdir(dirname) with atomic_save(filename, "wb") as fileobj: fileobj.write(dump_audio_files(self.get_content())) except SerializationError: # Can happen when we try to pickle while the library is being # modified, like in the periodic 15min save. # Ignore, as it should try again later or on program exit. util.print_exc() except EnvironmentError: print_w("Couldn't save library to path: %r" % filename) else: self.dirty = False
def write(self, filename): """Write config to filename. Can raise EnvironmentError """ assert isinstance(filename, fsnative) mkdir(os.path.dirname(filename)) # temporary set the new version for saving if self._version is not None: self.add_section("__config__") self.set("__config__", "version", self._version) try: with atomic_save(filename, "wb") as fileobj: temp = StringIO() self._config.write(temp) data = temp.getvalue().encode("utf-8", "surrogateescape") fileobj.write(data) finally: if self._loaded_version is not None: self.set("__config__", "version", self._loaded_version)
def get_user_dir(): """Place where QL saves its state, database, config etc.""" if os.name == "nt": USERDIR = os.path.join(windows.get_appdate_dir(), "Quod Libet") else: USERDIR = os.path.join(os.path.expanduser("~"), ".quodlibet") if not PY2: USERDIR += "_py3" if 'QUODLIBET_USERDIR' in environ: USERDIR = environ['QUODLIBET_USERDIR'] # XXX: Exec conf.py in this directory, used to override const globals # e.g. for setting USERDIR for the Windows portable version # Note: execfile doesn't handle unicode paths on windows, so encode. # (this doesn't use the old win api in case of str compared to os.*) _CONF_PATH = os.path.join( os.path.dirname(os.path.realpath(__file__)), "conf.py") if PY2: locals_ = {} # FIXME: PY3PORT try: execfile(_CONF_PATH, globals(), locals_) except IOError: pass else: USERDIR = locals_["USERDIR"] # XXX: users shouldn't assume the dir is there, but we currently do in # some places mkdir(USERDIR, 0o750) return USERDIR
def get_thumbnail(path, boundary): """Get a thumbnail pixbuf of an image at `path`. Will create/use a thumbnail in the user's thumbnail directory if possible. Follows the Free Desktop specification: http://specifications.freedesktop.org/thumbnail-spec/ Can raise GLib.GError. Thread-safe. """ width, height = boundary new_from_file_at_size = GdkPixbuf.Pixbuf.new_from_file_at_size # larger than thumbnails, load directly if width > ThumbSize.LARGEST or height > ThumbSize.LARGEST: return new_from_file_at_size(path, width, height) path_mtime = mtime(path) if path_mtime == 0: return new_from_file_at_size(path, width, height) # embedded thumbnails come from /tmp/ # FIXME: move this to another layer if path.startswith(tempfile.gettempdir()): return new_from_file_at_size(path, width, height) thumb_path, thumb_size = get_cache_info(path, boundary) cache_dir = os.path.dirname(thumb_path) try: mkdir(cache_dir, 0o700) except OSError: return new_from_file_at_size(path, width, height) try: pb = new_from_file_at_size(thumb_path, width, height) except GLib.GError: # in case it fails to load, we recreate it pass else: meta_mtime = pb.get_option("tEXt::Thumb::MTime") if meta_mtime is not None: try: meta_mtime = int(meta_mtime) except ValueError: pass else: if meta_mtime == int(path_mtime): return pb info, pw, ph = GdkPixbuf.Pixbuf.get_file_info(path) # Too small picture, no thumbnail needed if pw < thumb_size and ph < thumb_size: return new_from_file_at_size(path, width, height) thumb_pb = new_from_file_at_size(path, thumb_size, thumb_size) uri = "file://" + pathname2url(path) mime = info.get_mime_types()[0] options = { "tEXt::Thumb::Image::Width": str(pw), "tEXt::Thumb::Image::Height": str(ph), "tEXt::Thumb::URI": uri, "tEXt::Thumb::MTime": str(int(path_mtime)), "tEXt::Thumb::Size": str(os.path.getsize(path)), "tEXt::Thumb::Mimetype": mime, "tEXt::Software": "QuodLibet" } thumb_pb.savev(thumb_path, "png", options.keys(), options.values()) try: os.chmod(thumb_path, 0o600) except OSError: pass return scale(thumb_pb, boundary)
def test_exists(self): mkdir(".")
import quodlibet from quodlibet import formats, qltk from quodlibet.qltk.wlw import WaitLoadWindow from quodlibet.qltk.getstring import GetStringDialog from quodlibet.util import escape from quodlibet.util.collection import Playlist from quodlibet.util.path import mkdir, fsdecode, is_fsnative # Directory for playlist files from quodlibet.util.uri import URI PLAYLISTS = os.path.join(quodlibet.get_user_dir(), "playlists") assert is_fsnative(PLAYLISTS) if not os.path.isdir(PLAYLISTS): mkdir(PLAYLISTS) class ConfirmRemovePlaylistDialog(qltk.Message): def __init__(self, parent, playlist): title = _("Are you sure you want to delete the playlist '%s'?") % escape(playlist.name) description = _("All information about the selected playlist " "will be deleted and can not be restored.") super(ConfirmRemovePlaylistDialog, self).__init__( Gtk.MessageType.WARNING, parent, title, description, Gtk.ButtonsType.NONE ) self.add_buttons(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_DELETE, Gtk.ResponseType.YES) class GetPlaylistName(GetStringDialog):
from gi.repository import Gtk from quodlibet import formats, qltk from quodlibet.qltk.wlw import WaitLoadWindow from quodlibet.qltk.getstring import GetStringDialog from quodlibet.util import escape from quodlibet.util.collection import Playlist from quodlibet.util.path import mkdir, fsdecode, is_fsnative from quodlibet import const # Directory for playlist files from quodlibet.util.uri import URI PLAYLISTS = os.path.join(const.USERDIR, "playlists") assert is_fsnative(PLAYLISTS) if not os.path.isdir(PLAYLISTS): mkdir(PLAYLISTS) class ConfirmRemovePlaylistDialog(qltk.Message): def __init__(self, parent, playlist): title = (_("Are you sure you want to delete the playlist '%s'?") % escape(playlist.name)) description = (_("All information about the selected playlist " "will be deleted and can not be restored.")) super(ConfirmRemovePlaylistDialog, self).__init__(Gtk.MessageType.WARNING, parent, title, description, Gtk.ButtonsType.NONE) self.add_buttons(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_DELETE, Gtk.ResponseType.YES)