Skip to content

Commit

Permalink
Merge pull request #79 from geigerzaehler/update-all
Browse files Browse the repository at this point in the history
Add `update --all` flag to update all collections add once
  • Loading branch information
geigerzaehler authored Mar 24, 2024
2 parents 089886f + 73ae946 commit 378d1a4
Show file tree
Hide file tree
Showing 8 changed files with 209 additions and 147 deletions.
3 changes: 1 addition & 2 deletions .flake8
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
[flake8]
max-line-length = 88
extend-ignore = E203
extend-ignore = E203, E501
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
Change Log
==========

## Upcoming
* Add `--all` flag to update command which will update all configured
collections.

## v0.11.0 - 2023-06-06
* Use the convert’s plugin [`thread` configuration][convert-config] when
transcoding files. ([@johnyerhot](https://github.com/johnyerhot))
Expand Down
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,13 @@ The command accepts the following option.
command will ask you to confirm the creation of the external
collection. These options specify the answer as a cli option.

```plain
beet alt update [--create|--no-create] --all
```

Update all external collections defined in `alternatives` configuration.

```plain
beet alt list-tracks [--format=FORMAT] NAME
```

Expand Down
34 changes: 27 additions & 7 deletions beetsplug/alternatives.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,21 @@ def __init__(self):
def commands(self):
return [AlternativesCommand(self)]

def update(self, lib: Library, options):
try:
alt = self.alternative(options.name, lib)
except KeyError as e:
raise UserError("Alternative collection '{0}' not found.".format(e.args[0]))
alt.update(create=options.create)
def update(self, lib: Library, options: argparse.Namespace):
if options.name is None:
if not options.all:
raise UserError("Please specify a collection name or the --all flag")

for name in self.config.keys():
self.alternative(name, lib).update(create=options.create)
else:
try:
alt = self.alternative(options.name, lib)
except KeyError as e:
raise UserError(
"Alternative collection '{0}' not found.".format(e.args[0])
)
alt.update(create=options.create)

def list_tracks(self, lib, options):
if options.format is not None:
Expand Down Expand Up @@ -100,11 +109,22 @@ def __init__(self, plugin):

update = subparsers.add_parser("update")
update.set_defaults(func=plugin.update)
update.add_argument("name", metavar="NAME")
update.add_argument(
"name",
metavar="NAME",
nargs="?",
help="Name of the collection. Must be provided unless --all is given",
)
update.add_argument("--create", action="store_const", dest="create", const=True)
update.add_argument(
"--no-create", action="store_const", dest="create", const=False
)
update.add_argument(
"--all",
action="store_true",
default=False,
help="Update all alternative collections that are defined in the configuration",
)

list_tracks = subparsers.add_parser(
"list-tracks",
Expand Down
244 changes: 124 additions & 120 deletions poetry.lock

Large diffs are not rendered by default.

14 changes: 7 additions & 7 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,16 @@ python = "^3.8.1"
beets = "^1.6.0"

[tool.poetry.group.dev.dependencies]
pytest = "^7.3.1"
pytest-cov = "^4.0.0"
mock = "^5.0.2"
flake8 = "^6.0.0"
isort = "^5.12.0"
black = "^23.3.0"
black = "^24.2.0"
confuse = "^2.0.1"
flake8 = "^7.0.0"
isort = "^5.12.0"
mediafile = "^0.12.0"
typeguard = "^4.1.5"
mock = "^5.0.2"
pyright = "^1.1.340"
pytest = "^8.0.2"
pytest-cov = "^4.0.0"
typeguard = "^4.1.5"
typing-extensions = "^4.9.0"

[tool.pytest.ini_options]
Expand Down
39 changes: 37 additions & 2 deletions test/cli_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import shutil

from beets import util
from beets.ui import UserError
from beets.util import bytestring_path, syspath
from confuse import ConfigValueError
from helper import TestHelper, control_stdin
Expand Down Expand Up @@ -353,8 +354,9 @@ def test_remove_album(self):
self.assertIsNotFile(old_path)

def test_unkown_collection(self):
out = self.runcli("alt", "update", "unkown")
self.assertIn("Alternative collection 'unkown' not found.", out)
with self.assertRaises(UserError) as c:
self.runcli("alt", "update", "unkown")
assert str(c.exception) == "Alternative collection 'unkown' not found."

def test_embed_art(self):
"""Test that artwork is embedded and updated to match the source file.
Expand Down Expand Up @@ -413,6 +415,39 @@ def touch_art(item, image_path):
item = album.items().get()
self.assertHasEmbeddedArtwork(self.get_path(item), self.IMAGE_FIXTURE2)

def test_update_all(self):
dir_a = self.mkdtemp()
dir_b = self.mkdtemp()
self.config["alternatives"].get().clear() # type: ignore
self.config["alternatives"] = {
"a": {
"directory": dir_a,
"query": "myexternal:true",
},
"b": {
"directory": dir_b,
"query": "myexternal:true",
},
}

with self.assertRaises(UserError) as c:
self.runcli("alt", "update")
assert str(c.exception) == "Please specify a collection name or the --all flag"

item = self.add_track(title="a", myexternal="true")
self.runcli("alt", "update", "--all")
item.load()
path_a = self.get_path(item, path_key="alt.a")
assert path_a and dir_a in path_a.decode()
self.assertIsFile(path_a)

path_b = self.get_path(item, path_key="alt.b")
assert path_b and dir_b in path_b.decode()
self.assertIsFile(path_b)

# Don’t update files on second run
assert self.runcli("alt", "update", "--all") == ""


class ExternalConvertTest(TestHelper):
"""Test alternatives with non-empty ``format`` option, i.e. transcoding
Expand Down
12 changes: 3 additions & 9 deletions test/helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,9 +151,7 @@ def assertHasEmbeddedArtwork(self, path, compare_file=None):
self.assertIsNotNone(mediafile.art, msg="MediaFile has no embedded artwork")
if compare_file:
with open(syspath(compare_file), "rb") as compare_fh:
crc_is = crc32(
mediafile.art # pyright: ignore[reportGeneralTypeIssues]
)
crc_is = crc32(mediafile.art) # pyright: ignore[reportArgumentType]
crc_expected = crc32(compare_fh.read())
self.assertEqual(
crc_is,
Expand Down Expand Up @@ -229,7 +227,7 @@ def setup_beets(self):
self.libdir = bytestring_path(libdir)

self.lib = beets.library.Library(
":memory:", self.libdir # pyright: ignore[reportGeneralTypeIssues]
":memory:", self.libdir # pyright: ignore[reportArgumentType]
)
self.fixture_dir = os.path.join(
bytestring_path(os.path.dirname(__file__)), b"fixtures"
Expand Down Expand Up @@ -257,11 +255,7 @@ def unload_plugins(self):
def runcli(self, *args):
# TODO mock stdin
with capture_stdout() as out:
try:
ui._raw_main(list(args), self.lib)
except ui.UserError as u:
# TODO remove this and handle exceptions in tests
print(u.args[0])
ui._raw_main(list(args), self.lib)
return out.getvalue()

def lib_path(self, path):
Expand Down

0 comments on commit 378d1a4

Please sign in to comment.