-
Notifications
You must be signed in to change notification settings - Fork 2k
Fixes to Replace Plugin #6215
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Fixes to Replace Plugin #6215
Changes from all commits
8a97fba
eaec52b
5586a16
651da61
2d0c487
d460139
99c7820
53efa03
29e22f9
a6c78b5
56750ff
3717c29
c06ec26
f28a9f0
4dd66ee
15f3be8
28322ca
f23abda
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,9 +7,12 @@ | |
| import mediafile | ||
|
|
||
| from beets import ui, util | ||
| from beets.library import Item, Library | ||
| from beets.plugins import BeetsPlugin | ||
|
|
||
| if TYPE_CHECKING: | ||
| import optparse | ||
|
|
||
| from beets.library import Item, Library | ||
|
|
||
|
|
||
|
|
@@ -21,7 +24,9 @@ def commands(self): | |
| cmd.func = self.run | ||
| return [cmd] | ||
|
|
||
| def run(self, lib: Library, args: list[str]) -> None: | ||
| def run( | ||
| self, lib: Library, _opts: optparse.Values, args: list[str] | ||
| ) -> None: | ||
| if len(args) < 2: | ||
| raise ui.UserError("Usage: beet replace <query> <new_file_path>") | ||
|
|
||
|
|
@@ -59,32 +64,27 @@ def file_check(self, filepath: Path) -> None: | |
| except mediafile.FileTypeError as fte: | ||
| raise ui.UserError(fte) | ||
|
|
||
| def select_song(self, items: list[Item]): | ||
| def select_song(self, items: list[Item]) -> Item | None: | ||
| """Present a menu of matching songs and get user selection.""" | ||
| ui.print_("\nMatching songs:") | ||
| ui.print_("Matching songs:") | ||
| for i, item in enumerate(items, 1): | ||
| ui.print_(f"{i}. {util.displayable_path(item)}") | ||
|
|
||
| while True: | ||
| try: | ||
| index = int( | ||
| input( | ||
| f"Which song would you like to replace? " | ||
| f"[1-{len(items)}] (0 to cancel): " | ||
| ) | ||
| ) | ||
| if index == 0: | ||
| return None | ||
| if 1 <= index <= len(items): | ||
| return items[index - 1] | ||
| ui.print_( | ||
| f"Invalid choice. Please enter a number " | ||
| f"between 1 and {len(items)}." | ||
| ) | ||
| except ValueError: | ||
| ui.print_("Invalid input. Please type in a number.") | ||
|
|
||
| def confirm_replacement(self, new_file_path: Path, song: Item): | ||
| index = ui.input_options( | ||
| [], | ||
| require=True, | ||
| prompt=( | ||
| f"Which song would you like to replace? " | ||
| f"[1-{len(items)}] (0 to cancel):" | ||
| ), | ||
| numrange=(0, len(items)), | ||
| ) | ||
|
|
||
| if index == 0: | ||
| return None | ||
| return items[index - 1] | ||
|
|
||
| def confirm_replacement(self, new_file_path: Path, song: Item) -> bool: | ||
| """Get user confirmation for the replacement.""" | ||
| original_file_path: Path = Path(song.path.decode()) | ||
|
|
||
|
|
@@ -95,12 +95,10 @@ def confirm_replacement(self, new_file_path: Path, song: Item): | |
| f"\nReplacing: {util.displayable_path(new_file_path)} " | ||
| f"-> {util.displayable_path(original_file_path)}" | ||
| ) | ||
| decision: str = ( | ||
| input("Are you sure you want to replace this track? (y/N): ") | ||
| .strip() | ||
| .casefold() | ||
|
|
||
| return ui.input_yn( | ||
| "Are you sure you want to replace this track (y/n)?", require=True | ||
| ) | ||
| return decision in {"yes", "y"} | ||
|
|
||
| def replace_file(self, new_file_path: Path, song: Item) -> None: | ||
| """Replace the existing file with the new one.""" | ||
|
|
@@ -109,7 +107,7 @@ def replace_file(self, new_file_path: Path, song: Item) -> None: | |
|
|
||
| try: | ||
| shutil.move(util.syspath(new_file_path), util.syspath(dest)) | ||
| except Exception as e: | ||
| except OSError as e: | ||
| raise ui.UserError(f"Error replacing file: {e}") | ||
|
Comment on lines
108
to
111
|
||
|
|
||
| if ( | ||
|
|
@@ -118,10 +116,13 @@ def replace_file(self, new_file_path: Path, song: Item) -> None: | |
| ): | ||
| try: | ||
| original_file_path.unlink() | ||
| except Exception as e: | ||
| except OSError as e: | ||
| raise ui.UserError(f"Could not delete original file: {e}") | ||
|
|
||
| song.path = str(dest).encode() | ||
| song.store() | ||
| # Update the path to point to the new file. | ||
| song.path = util.bytestring_path(dest) | ||
|
|
||
| ui.print_("Replacement successful.") | ||
| # Synchronise the new file with the database. This copies metadata from the | ||
| # Item to the new file (i.e. title, artist, album, etc.), | ||
| # and then from the Item to the database (i.e. path and mtime). | ||
| song.try_sync(write=True, move=False) | ||
Uh oh!
There was an error while loading. Please reload this page.