Example #1
0
    def test_types(self):
        from quodlibet.util.path import normalize_path

        assert isinstance(normalize_path(fsnative(u"foo"), False), fsnative)
        assert isinstance(normalize_path("foo", False), fsnative)
        assert isinstance(normalize_path(fsnative(u"foo"), True), fsnative)
        assert isinstance(normalize_path("foo", True), fsnative)
Example #2
0
 def remove_roots(self,
                  old_roots: Iterable[str]) -> Generator[None, None, None]:
     """Remove library roots (scandirs) entirely, and all their songs"""
     old_paths = [
         Path(normalize_path(root, canonicalise=True)).expanduser()
         for root in old_roots
     ]
     total = len(self)
     removed = set()
     print_d(
         f"Removing library roots {old_roots} from {self._name} library")
     yield
     with Task(_("Library"), _("Removing library files")) as task:
         for i, song in enumerate(list(self.values())):
             task.update(i / total)
             key = normalize_path(song.key)
             song_path = Path(key)
             if any(path in song_path.parents for path in old_paths):
                 removed.add(song)
             if not i % 100:
                 yield
     if removed:
         self.remove(removed)
     else:
         print_d(f"No tracks in {old_roots} to remove from {self._name}")
Example #3
0
    def test_types(self):
        from quodlibet.util.path import normalize_path

        assert isinstance(normalize_path(fsnative(u"foo"), False), fsnative)
        assert isinstance(normalize_path("foo", False), fsnative)
        assert isinstance(normalize_path(fsnative(u"foo"), True), fsnative)
        assert isinstance(normalize_path("foo", True), fsnative)
Example #4
0
    def move_root(self,
                  old_root: str,
                  new_root: fsnative,
                  write_files: bool = True) -> Generator[None, None, None]:
        """
        Move the root for all songs in a given (scan) directory.

        We avoid dereferencing the destination, to allow users things like:
          1. Symlink new_path -> old_root
          2. Move QL root to new_path
          3. Remove symlink
          4. Move audio files: old_root -> new_path

        """
        # TODO: only move primary library
        old_path = Path(normalize_path(old_root, canonicalise=True)).expanduser()
        new_path = Path(normalize_path(new_root)).expanduser()
        if not old_path.is_dir():
            print_w(f"Source dir {str(old_path)!r} doesn't exist, assuming that's OK",
                    self._name)
        if not new_path.is_dir():
            raise ValueError(f"Destination {new_path!r} is not a directory")
        print_d(f"Checking entire library for {str(old_path)!r}", self._name)
        missing: Set[AudioFile] = set()
        changed = set()
        total = len(self)
        if not total:
            return
        with Task(_("Library"), _("Moving library files")) as task:
            yield
            for i, song in enumerate(list(self.values())):
                task.update(i / total)
                key = normalize_path(song.key)
                path = Path(key)
                if old_path in path.parents:
                    # TODO: more Pathlib-friendly dir replacement...
                    new_key = key.replace(str(old_path), str(new_path), 1)
                    new_key = normalize_path(new_key, canonicalise=False)
                    if new_key == key:
                        print_w(f"Substitution failed for {key!r}", self._name)
                    # We need to update ~filename and ~mountpoint
                    song.sanitize()
                    if write_files:
                        song.write()
                    if self.move_song(song, new_key):
                        changed.add(song)
                    else:
                        missing.add(song)
                elif not (i % 1000):
                    print_d(f"Not moved, for example: {key!r}", self._name)
                if not i % 100:
                    yield
            self.changed(changed)
            if missing:
                print_w(f"Couldn't find {len(list(missing))} files: {missing}",
                        self._name)
        yield
        self.save()
        print_d(f"Done moving {len(changed)} track(s) (of {total}) "
                f"to {str(new_path)!r}.", self._name)
Example #5
0
    def test_move_root(self):
        # TODO: mountpoint tests too
        self.library.filename = "moving"
        root = Path(normalize_path(mkdtemp(), True))
        other_root = Path(normalize_path(mkdtemp(), True))
        new_root = Path(normalize_path(mkdtemp(), True))
        in_song = FakeAudioFile(str(root / "in file.mp3"))
        in_song.sanitize()
        out_song = FakeAudioFile(str(other_root / "out file.mp3"))
        # Make sure they exists
        in_song.sanitize()
        out_song.sanitize()
        assert Path(in_song("~dirname")) == root, "test setup wrong"
        assert Path(out_song("~dirname")) == other_root, "test setup wrong"
        self.library.add([out_song, in_song])

        # Run it by draining the generator
        list(self.library.move_root(root, str(new_root)))
        msg = f"Dir wasn't updated in {root!r} -> {new_root!r} for {in_song.key}"
        assert Path(in_song("~dirname")) == new_root, msg
        assert Path(in_song("~filename")) == (new_root / "in file.mp3")
        assert Path(out_song(
            "~dirname")) == other_root, f"{out_song} was wrongly moved"
        assert in_song._written, "Song wasn't written to disk"
        assert not out_song._written, "Excluded songs was written!"
Example #6
0
    def test_move_root_gone_source_dir(self):
        # See #3967
        self.library.filename = "moving"
        gone_root = Path(normalize_path("/gone", True))
        new_root = Path(normalize_path(mkdtemp(), True))
        song = FakeAudioFile(str(gone_root / "in file.mp3"))
        assert Path(song("~dirname")) == gone_root, "test setup wrong"
        self.library.add([song])

        # Run it by draining the generator
        list(self.library.move_root(gone_root, str(new_root)))
        assert Path(song("~dirname")) == new_root
        assert song._written, "Song wasn't written to disk"
Example #7
0
    def __popup_menu(self, view, fs):
        # get all songs for the selection
        filenames = [
            normalize_path(f, canonicalise=True)
            for f in fs.get_selected_paths()
        ]
        maybe_songs = [self.__library.get(f) for f in filenames]
        songs = [s for s in maybe_songs if s]

        if songs:
            menu = self.pm.Menu(self.__library, songs)
            if menu is None:
                menu = Gtk.Menu()
            else:
                menu.prepend(SeparatorMenuItem())
        else:
            menu = Gtk.Menu()

        b = TrashMenuItem()
        b.connect('activate', self.__delete, filenames, fs)
        menu.prepend(b)

        def selection_done_cb(menu):
            menu.destroy()

        menu.connect('selection-done', selection_done_cb)
        menu.show_all()
        return view.popup_menu(menu, 0, Gtk.get_current_event_time())
Example #8
0
def temp_filename(*args, as_path=False, **kwargs):
    """
    Creates an empty file, returning the normalized path to it,
    and removes it when done.

        with temp_filename() as filename:
            with open(filename, 'w') as h:
                h.write("foo")
            do_stuff(filename)
    """

    from tests import mkstemp
    try:
        del kwargs["as_path"]
    except KeyError:
        pass
    fd, filename = mkstemp(*args, **kwargs)
    os.close(fd)
    normalized = normalize_path(filename)
    yield Path(normalized) if as_path else normalized

    try:
        os.remove(filename)
    except OSError as e:
        if e.errno != errno.ENOENT:
            raise
Example #9
0
    def __popup_menu(self, view, fs):
        # get all songs for the selection
        filenames = [normalize_path(f, canonicalise=True)
                     for f in fs.get_selected_paths()]
        maybe_songs = [self.__library.get(f) for f in filenames]
        songs = [s for s in maybe_songs if s]

        if songs:
            menu = self.pm.Menu(self.__library, songs)
            if menu is None:
                menu = Gtk.Menu()
            else:
                menu.prepend(SeparatorMenuItem())
        else:
            menu = Gtk.Menu()

        b = TrashMenuItem()
        b.connect('activate', self.__delete, filenames, fs)
        menu.prepend(b)

        def selection_done_cb(menu):
            menu.destroy()

        menu.connect('selection-done', selection_done_cb)
        menu.show_all()
        return view.popup_menu(menu, 0, Gtk.get_current_event_time())
Example #10
0
def show_files_win32(path, files):
    """Takes a path to a directory and a list of filenames in that directory
    to display.

    Returns True on success.
    """

    assert os.name == "nt"

    import pywintypes
    from win32com.shell import shell

    assert is_fsnative(path)
    assert all(is_fsnative(f) for f in files)

    normalized_files = map(normalize_path, files)

    try:
        folder_pidl = shell.SHILCreateFromPath(path, 0)[0]
        desktop = shell.SHGetDesktopFolder()
        shell_folder = desktop.BindToObject(
            folder_pidl, None, shell.IID_IShellFolder)
        items = []
        for item in shell_folder:
            name = desktop.GetDisplayNameOf(item, 0)
            if normalize_path(name) in normalized_files:
                items.append(item)
        shell.SHOpenFolderAndSelectItems(folder_pidl, items, 0)
    except pywintypes.com_error:
        return False
    else:
        return True
Example #11
0
def show_files_win32(path, files):
    """Takes a path to a directory and a list of filenames in that directory
    to display.

    Returns True on success.
    """

    assert os.name == "nt"

    import pywintypes
    from win32com.shell import shell

    assert is_fsnative(path)
    assert all(is_fsnative(f) for f in files)

    normalized_files = map(normalize_path, files)

    try:
        folder_pidl = shell.SHILCreateFromPath(path, 0)[0]
        desktop = shell.SHGetDesktopFolder()
        shell_folder = desktop.BindToObject(
            folder_pidl, None, shell.IID_IShellFolder)
        items = []
        for item in shell_folder:
            name = desktop.GetDisplayNameOf(item, 0)
            if normalize_path(name) in normalized_files:
                items.append(item)
        shell.SHOpenFolderAndSelectItems(folder_pidl, items, 0)
    except pywintypes.com_error:
        return False
    else:
        return True
Example #12
0
 def __find_songs(self, selection):
     model, rows = selection.get_selected_rows()
     dirs = [model[row][0] for row in rows]
     songs = []
     to_add = []
     for dir in dirs:
         try:
             for file in filter(formats.filter,
                                sorted(os.listdir(dir))):
                 raw_path = os.path.join(dir, file)
                 fn = normalize_path(raw_path, canonicalise=True)
                 if fn in self.__glibrary:
                     songs.append(self.__glibrary[fn])
                 elif fn not in self.__library:
                     song = formats.MusicFile(fn)
                     if song:
                         to_add.append(song)
                         songs.append(song)
                         yield songs
                 if fn in self.__library:
                     song = self.__library[fn]
                     if not song.valid():
                         self.__library.reload(song)
                     if song in self.__library:
                         songs.append(song)
         except OSError:
             pass
     self.__library.add(to_add)
     yield songs
Example #13
0
 def __find_songs(self, selection):
     model, rows = selection.get_selected_rows()
     dirs = [model[row][0] for row in rows]
     songs = []
     to_add = []
     for dir in dirs:
         try:
             for file in filter(formats.filter, sorted(os.listdir(dir))):
                 raw_path = os.path.join(dir, file)
                 fn = normalize_path(raw_path, canonicalise=True)
                 if fn in self.__glibrary:
                     songs.append(self.__glibrary[fn])
                 elif fn not in self.__library:
                     song = formats.MusicFile(fn)
                     if song:
                         to_add.append(song)
                         songs.append(song)
                         yield songs
                 if fn in self.__library:
                     song = self.__library[fn]
                     if not song.valid():
                         self.__library.reload(song)
                     if song in self.__library:
                         songs.append(song)
         except OSError:
             pass
     self.__library.add(to_add)
     yield songs
Example #14
0
    def test_file_encoding(self):
        if os.name == "nt":
            return

        f = self.add_file(bytes2fsn(b"\xff\xff\xff\xff - cover.jpg", None))
        self.assertTrue(isinstance(self.song("album"), text_type))
        h = self._find_cover(self.song)
        self.assertEqual(h.name, normalize_path(f))
Example #15
0
    def test_file_encoding(self):
        if os.name == "nt":
            return

        f = self.add_file("\xff\xff\xff\xff - cover.jpg")
        self.assertTrue(isinstance(self.song("album"), unicode))
        h = self._find_cover(self.song)
        self.assertEqual(h.name, normalize_path(f))
    def test_file_encoding(self):
        if os.name == "nt":
            return

        f = self.add_file("\xff\xff\xff\xff - cover.jpg")
        self.assertTrue(isinstance(quux("album"), unicode))
        h = self._find_cover(quux)
        self.assertEqual(h.name, normalize_path(f))
Example #17
0
 def setUp(self):
     config.RATINGS = config.HardCodedRatingsPrefs()
     fd, filename = mkstemp()
     os.close(fd)
     self.quux = AudioFile({
         "~filename": normalize_path(filename, True),
         "album": u"Quuxly"
     })
Example #18
0
    def test_file_encoding(self):
        if os.name == "nt":
            return

        f = self.add_file(bytes2fsn(b"\xff\xff\xff\xff - cover.jpg", None))
        self.assertTrue(isinstance(self.song("album"), text_type))
        h = self._find_cover(self.song)
        self.assertEqual(h.name, normalize_path(f))
Example #19
0
 def _file_changed(self, _monitor, main_file: Gio.File,
                   other_file: Optional[Gio.File],
                   event_type: Gio.FileMonitorEvent) -> None:
     file_path = main_file.get_path()
     other_path = (Path(normalize_path(other_file.get_path(), True))
                   if other_file else None)
     print_d(f"Got event {event_type} on {file_path}->{other_path}")
     self.changed.append((event_type, file_path))
Example #20
0
 def setUp(self):
     config.RATINGS = config.HardCodedRatingsPrefs()
     fd, filename = mkstemp()
     os.close(fd)
     self.quux = AudioFile({
         "~filename": normalize_path(filename, True),
         "album": u"Quuxly"
     })
Example #21
0
    def sanitize(self, filename=None):
        """Fill in metadata defaults. Find ~mountpoint, ~#mtime, ~#filesize
        and ~#added. Check for null bytes in tags.

        Does not raise.
        """

        # Replace nulls with newlines, trimming zero-length segments
        for key, val in listitems(self):
            self[key] = val
            if isinstance(val, string_types) and '\0' in val:
                self[key] = '\n'.join(filter(lambda s: s, val.split('\0')))
            # Remove unnecessary defaults
            if key in NUMERIC_ZERO_DEFAULT and val == 0:
                del self[key]

        if filename:
            self["~filename"] = filename
        elif "~filename" not in self:
            raise ValueError("Unknown filename!")

        assert isinstance(self["~filename"], fsnative)

        if self.is_file:
            self["~filename"] = normalize_path(
                self["~filename"], canonicalise=True)
            # Find mount point (terminating at "/" if necessary)
            head = self["~filename"]
            while "~mountpoint" not in self:
                head, tail = os.path.split(head)
                # Prevent infinite loop without a fully-qualified filename
                # (the unit tests use these).
                head = head or fsnative(u"/")
                if ismount(head):
                    self["~mountpoint"] = head
        else:
            self["~mountpoint"] = fsnative(u"/")

        # Fill in necessary values.
        self.setdefault("~#added", int(time.time()))

        # For efficiency, do a single stat here. See Issue 504
        try:
            stat = os.stat(self['~filename'])
            self["~#mtime"] = stat.st_mtime
            self["~#filesize"] = stat.st_size

            # Issue 342. This is a horrible approximation (due to headers) but
            # on FLACs, the most common case, this should be close enough
            if "~#bitrate" not in self:
                try:
                    # kbps = bytes * 8 / seconds / 1000
                    self["~#bitrate"] = int(stat.st_size /
                                            (self["~#length"] * (1000 / 8)))
                except (KeyError, ZeroDivisionError):
                    pass
        except OSError:
            self["~#mtime"] = 0
Example #22
0
    def sanitize(self, filename=None):
        """Fill in metadata defaults. Find ~mountpoint, ~#mtime, ~#filesize
        and ~#added. Check for null bytes in tags.

        Does not raise.
        """

        # Replace nulls with newlines, trimming zero-length segments
        for key, val in list(self.items()):
            self[key] = val
            if isinstance(val, str) and '\0' in val:
                self[key] = '\n'.join(filter(lambda s: s, val.split('\0')))
            # Remove unnecessary defaults
            if key in NUMERIC_ZERO_DEFAULT and val == 0:
                del self[key]

        if filename:
            self["~filename"] = filename
        elif "~filename" not in self:
            raise ValueError("Unknown filename!")

        assert isinstance(self["~filename"], fsnative)

        if self.is_file:
            self["~filename"] = normalize_path(self["~filename"],
                                               canonicalise=True)
            # Find mount point (terminating at "/" if necessary)
            head = self["~filename"]
            while "~mountpoint" not in self:
                head, tail = os.path.split(head)
                # Prevent infinite loop without a fully-qualified filename
                # (the unit tests use these).
                head = head or fsnative(u"/")
                if ismount(head):
                    self["~mountpoint"] = head
        else:
            self["~mountpoint"] = fsnative(u"/")

        # Fill in necessary values.
        self.setdefault("~#added", int(time.time()))

        # For efficiency, do a single stat here. See Issue 504
        try:
            stat = os.stat(self['~filename'])
            self["~#mtime"] = stat.st_mtime
            self["~#filesize"] = stat.st_size

            # Issue 342. This is a horrible approximation (due to headers) but
            # on FLACs, the most common case, this should be close enough
            if "~#bitrate" not in self:
                try:
                    # kbps = bytes * 8 / seconds / 1000
                    self["~#bitrate"] = int(stat.st_size / (self["~#length"] *
                                                            (1000 / 8)))
                except (KeyError, ZeroDivisionError):
                    pass
        except OSError:
            self["~#mtime"] = 0
Example #23
0
    def test_remove_roots(self):
        self.library.filename = "removing"
        root = Path(normalize_path(mkdtemp(), True))
        other_root = Path(normalize_path(mkdtemp(), True))
        out_song = FakeAudioFile(str(other_root / "out file.mp3"))
        in_song = FakeAudioFile(str(root / "in file.mp3"))
        in_song.sanitize()
        out_song.sanitize()
        self.library.add([in_song, out_song])
        assert in_song in self.library, "test seems broken"

        # Run it by draining the generator
        list(self.library.remove_roots([root]))

        assert in_song not in self.library
        assert out_song in self.library, "removed too many files"
        assert self.removed == [in_song], "didn't signal the song removal"
        assert not self.changed, "shouldn't have changed any tracks"
Example #24
0
    def read(self, db):
        """Iterate through the database and import data for songs found in
        the library
        """

        # use the Row class for extracting rows
        db.row_factory = sqlite3.Row

        # iterate over all songs in the database
        # throws sqlite3.OperationalError if CoreTracks is not found
        for row in db.execute("SELECT * FROM CoreTracks"):
            try:
                filename = uri2fsn(row["Uri"])
            except ValueError:
                continue

            song = self._library.get(normalize_path(filename))
            if not song:
                continue

            has_changed = False

            # rating is stored as integer from 0 to 5
            b_rating = row["Rating"] / 5.0
            if b_rating != song("~#rating"):
                song["~#rating"] = b_rating
                has_changed = True

            # play count is stored as integer from 0
            if row["PlayCount"] != song("~#playcount"):
                # summing play counts would break on multiple imports
                song["~#playcount"] = row["PlayCount"]
                has_changed = True

            # skip count is stored as integer from 0
            if row["SkipCount"] != song("~#skipcount"):
                song["~#skipcount"] = row["SkipCount"]
                has_changed = True

            # timestamp is stored as integer or None
            if row["LastPlayedStamp"] is not None:
                value = row["LastPlayedStamp"]
                # keep timestamp if it is newer than what we had
                if value > song("~#lastplayed", 0):
                    song["~#lastplayed"] = value
                    has_changed = True

            if row["DateAddedStamp"] is not None:
                value = row["DateAddedStamp"]
                # keep timestamp if it is older than what we had
                if value < song("~#added", 0):
                    song["~#added"] = value
                    has_changed = True

            if has_changed:
                self._changed_songs.append(song)
Example #25
0
def a_dummy_song():
    """Looks like the real thing"""
    fd, filename = mkstemp()
    os.close(fd)
    return AudioFile({
        '~#length': 234, '~filename': normalize_path(filename, True),
        'artist': AN_ARTIST, 'album': 'An Example Album',
        'title': A_TITLE, 'tracknumber': 1,
        'date': '2010-12-31',
    })
Example #26
0
    def test_file_encoding(self):
        if os.name == "nt":
            return

        f = self.full_path("\xff\xff\xff\xff - cover.jpg")
        file(f, "w").close()
        self.files.append(f)
        self.assertTrue(isinstance(quux("album"), unicode))
        h = self._find_cover(quux)
        self.assertEqual(h.name, normalize_path(f))
Example #27
0
 def setUp(self):
     # Need the playlists library now
     init_fake_app()
     config.RATINGS = config.HardCodedRatingsPrefs()
     fd, filename = mkstemp()
     os.close(fd)
     self.quux = AudioFile({
         "~filename": normalize_path(filename, True),
         "album": u"Quuxly"
     })
Example #28
0
 def watching_producer():
     # TODO: integrate this better with scanning.
     for fullpath in paths:
         desc = _("Adding watches for %s") % (fsn2text(unexpand(fullpath)))
         with Task(_("Library"), desc) as task:
             normalised = Path(normalize_path(fullpath, True)).expanduser()
             if any(Path(exclude) in normalised.parents
                    for exclude in exclude_dirs):
                 continue
             unpulsed = 0
             self.monitor_dir(normalised)
             for path, dirs, files in os.walk(normalised):
                 normalised = Path(normalize_path(path, True))
                 for d in dirs:
                     self.monitor_dir(normalised / d)
                 unpulsed += len(dirs)
                 if unpulsed > 50:
                     task.pulse()
                     unpulsed = 0
                 yield
Example #29
0
    def endElement(self, name):
        self._tag = None
        if name == "entry" and self._current is not None:
            current = self._current
            self._current = None
            if len(current) > 1:
                uri = current.pop("location", "")
                try:
                    filename = uri2fsn(uri)
                except ValueError:
                    return

                self._process_song(normalize_path(filename), current)
Example #30
0
    def endElement(self, name):
        self._tag = None
        if name == "entry" and self._current is not None:
            current = self._current
            self._current = None
            if len(current) > 1:
                uri = current.pop("location", "")
                try:
                    filename = uri2fsn(uri)
                except ValueError:
                    return

                self._process_song(normalize_path(filename), current)
Example #31
0
    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)
Example #32
0
    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)
Example #33
0
        def select(model, path, iter_, paths_):
            (paths, first) = paths_
            value = model.get_value(iter_)
            if value is None:
                return not bool(paths)
            value = normalize_path(value)

            if value in paths:
                self.get_child().get_selection().select_path(path)
                paths.remove(value)
                if not first:
                    self.get_child().set_cursor(path)
                    # copy treepath, gets invalid after the callback
                    first.append(path.copy())
            else:
                for fpath in paths:
                    if fpath.startswith(value):
                        self.get_child().expand_row(path, False)
            return not bool(paths)
Example #34
0
        def select(model, path, iter_, paths_):
            (paths, first) = paths_
            value = model.get_value(iter_)
            if value is None:
                return not bool(paths)
            value = normalize_path(value)

            if value in paths:
                self.get_child().get_selection().select_path(path)
                paths.remove(value)
                if not first:
                    self.get_child().set_cursor(path)
                    # copy treepath, gets invalid after the callback
                    first.append(path.copy())
            else:
                for fpath in paths:
                    if fpath.startswith(value):
                        self.get_child().expand_row(path, False)
            return not bool(paths)
Example #35
0
    def __select_paths(self, paths):
        # AudioFile uses normalized paths, DirectoryTree doesn't

        paths = map(normalize_path, paths)

        def select(model, path, iter_, (paths, first)):
            value = model.get_value(iter_)
            if value is None:
                return not bool(paths)
            value = normalize_path(value)

            if value in paths:
                self.get_child().get_selection().select_path(path)
                paths.remove(value)
                if not first:
                    self.get_child().set_cursor(path)
                    # copy treepath, gets invalid after the callback
                    first.append(path.copy())
            else:
                for fpath in paths:
                    if fpath.startswith(value):
                        self.get_child().expand_row(path, False)
            return not bool(paths)
Example #36
0
    def __select_paths(self, paths):
        # AudioFile uses normalized paths, DirectoryTree doesn't

        paths = map(normalize_path, paths)

        def select(model, path, iter_, (paths, first)):
            value = model.get_value(iter_)
            if value is None:
                return not bool(paths)
            value = normalize_path(value)

            if value in paths:
                self.get_child().get_selection().select_path(path)
                paths.remove(value)
                if not first:
                    self.get_child().set_cursor(path)
                    # copy treepath, gets invalid after the callback
                    first.append(path.copy())
            else:
                for fpath in paths:
                    if fpath.startswith(value):
                        self.get_child().expand_row(path, False)
            return not bool(paths)
Example #37
0
    def test_watched_moving_song(self):
        with temp_filename(dir=self.temp_path, suffix=".flac", as_path=True) as path:
            shutil.copy(Path(get_data_path("silence-44-s.flac")), path)
            sleep(0.2)
            assert path.exists()
            run_gtk_loop()
            assert str(path) in self.library, f"New path {path!s} didn't get added"
            assert len(self.added) == 1
            assert self.added[0]("~basename") == path.name
            self.added.clear()

            # Now move it...
            new_path = path.parent / f"moved-{path.name}"
            path.rename(new_path)
            sleep(0.2)
            assert not path.exists(), "test should have removed old file"
            assert new_path.exists(), "test should have renamed file"
            print_d(f"New test file at {new_path}")
            run_gtk_loop()
            p = normalize_path(str(new_path), True)
            assert p in self.library, f"New path {new_path} not in library [{self.fns}]"
            msg = "Inconsistent events: should be (added and removed) or nothing at all"
            assert not (bool(self.added) ^ bool(self.removed)), msg
Example #38
0
    def add_filename(self, filename, add=True):
        """Add a song to the library based on filename.

        If 'add' is true, the song will be added and the 'added' signal
        may be fired.

        Example (add=False):
            load many songs and call Library.add(songs) to add all in one go.

        The song is returned if it is in the library after this call.
        Otherwise, None is returned.
        """

        key = normalize_path(filename, True)
        song = None
        if key not in self._contents:
            song = MusicFile(filename)
            if song and add:
                self.add([song])
        else:
            print_d("Already got file %r." % filename)
            song = self._contents[key]

        return song
Example #39
0
    def add_filename(self, filename, add=True):
        """Add a song to the library based on filename.

        If 'add' is true, the song will be added and the 'added' signal
        may be fired.

        Example (add=False):
            load many songs and call Library.add(songs) to add all in one go.

        The song is returned if it is in the library after this call.
        Otherwise, None is returned.
        """

        key = normalize_path(filename, True)
        song = None
        if key not in self._contents:
            song = MusicFile(filename)
            if song and add:
                self.add([song])
        else:
            print_d("Already got file %r." % filename)
            song = self._contents[key]

        return song
Example #40
0
 def test_get_link_target(self):
     path = get_data_path("test.lnk")
     d = windows.get_link_target(path)
     self.assertTrue(isinstance(d, text_type))
     self.assertEqual(
         normalize_path(d), normalize_path(u"C:\\Windows\\explorer.exe"))
Example #41
0
def dummy_path(path):
    path = fsnative(path)
    if os.name == "nt":
        return normalize_path(u"z:\\" + path.replace(u"/", u"\\"))
    return path
Example #42
0
 def get_filename(self, filename):
     key = normalize_path(filename, True)
     return self._contents.get(key)
Example #43
0
 def contains_filename(self, filename):
     key = normalize_path(filename, True)
     return key in self._contents
Example #44
0
 def test_get_link_target(self):
     path = os.path.join(DATA_DIR, "test.lnk")
     d = windows.get_link_target(path)
     self.assertTrue(isinstance(d, unicode))
     self.assertEqual(
         normalize_path(d), normalize_path(u"C:\Windows\explorer.exe"))
Example #45
0
 def get_filename(self, filename):
     key = normalize_path(filename, True)
     return self._contents.get(key)
Example #46
0
 def test_file_encoding(self):
     f = self.add_file(fsnative(u"öäü - cover.jpg"))
     self.assertTrue(isinstance(self.song("album"), text_type))
     h = self._find_cover(self.song)
     self.assertEqual(normalize_path(h.name), normalize_path(f))
Example #47
0
def dummy_path(path):
    path = fsnative(path)
    if os.name == "nt":
        return normalize_path(u"z:\\" + path.replace(u"/", u"\\"))
    return path
Example #48
0
 def contains_filename(self, filename):
     key = normalize_path(filename, True)
     return key in self._contents
Example #49
0
 def test_get_link_target(self):
     path = get_data_path("test.lnk")
     d = windows.get_link_target(path)
     self.assertTrue(isinstance(d, text_type))
     self.assertEqual(normalize_path(d),
                      normalize_path(u"C:\Windows\explorer.exe"))