Repository: beetbox/beets

Issue:
Beet update when volume not mounted
Hi

Maybe I'm not using the correct config, but if you `beet update` when your music files volume is not mounted, your library gets all deleted without further prompting.

Is that the expected behaviour or is there a way to avoid it?
Thank you



Discussion:
Yep, it's rather unfortunate but expected—the files don't exist from the OS's perspective, so beets removes them from the database.

hi @sampsyo 

Thank you for your prompt answer. I understand that you don't have the feature (check if volume is mounted / available). But does closing the issue mean that you do not want the feature to be implemented? If it is an unfortunate bug, which we agree it is, would you consider it in the roadmap?

Thank you

Well, here's the thing: from the perspective of the OS, there is no way to tell why a file is unavailable. A deleted file looks exactly the same as an unmounted file, and unmounting a filesystem leaves behind no "evidence" that it was unmounted. So I'm not sure a fix is possible, alas.

Yes @sampsyo thank you again for your reply.

I understand this might be an edge case but I think it makes sense. 

I am not saying to check if "a file" is available or not, nor why. That would be useless. But if  `directory` path (as per yaml config) is unavailable then we can assume all files inside the library are unavailable, so a `beet update` is not safe without further asking for confirmation. 

I know that would be easy with bash and we cannot assume bash, but doing it in python should not be impossible at all. I never worked with python but looks like `import os` (which you are already using) would give you the basic tools. 

I did not ask _you_ to fix it, I am just surprised this was closed without further commenting if it is doable or not, approaches, and so on. 

Thank you again for such a great tool.

I think checking whether the library directory itself is available is a good idea, as it's unlikely someone would delete the library folder itself and run an update on purpose.

Maybe we can introduce a config option like `library_removable: true/false` which covers this case and prints an error message to the user if the library path cannot be found

Yes, unless all the music happens to reside outside of the library directory---for example, if the user imported everything with `move: no`, intending to leave everything in place, and didn't bother to change the `directory` configuration.

But yes, perhaps a confirmation wouldn't hurt?

Hi again people,

Thank you for the support on the issue. I deleted all my library by being relaxed, so I think it can happen to more people. 

What I had in mind is a confirmation if that scenario (library volume unavailable) is in place, but a `library_removable: true/false` (default: false) would work as well. 

Do you accept PR's? How do you prepare these kind of changes (how do you run your copy of beets not the pip one)? Also, do you run tests? 

@zigotica I also store my music library on a removable usb hard drive. I use 'autofs' to auto-mount/umount the volume. You can find some information here https://gist.github.com/daks/8030543 if needed.

Hi @daks 

I have my library (database) in my hard drive, but the music files in the NAS. For security reasons I don't have my NAS volumes always mounted, so I just mount them (manually) when I want to do something specifically. 

Thank you for the suggestion anyway, USB might be the case for other people. 

We certainly do accept PRs, @zigotica. Take a look at [How do I run the latest source version of beets?](https://beets.readthedocs.org/en/v1.3.17/faq.html#run-the-latest-source-version-of-beets), it should help you get set up with a modifiable install of beets.

I find the best way to work on the source is a combination of a [virtualenv](https://virtualenv.pypa.io) and the third way of doing it (`pip install -e git+https://github.com/beetbox/beets#egg=beets`).

Here's an example of my workflow (assuming you're on *nix):

```
$ virtualenv venv
New python executable in venv/bin/python2.7
Also creating executable in venv/bin/python
Installing setuptools, pip, wheel...done.

$ source venv/bin/activate

$ pip install -e git+https://github.com/beetbox/beets#egg=beets
Obtaining beets from git+https://github.com/beetbox/beets#egg=beets
  Cloning https://github.com/beetbox/beets to ./venv/src/beets
Collecting enum34>=1.0.4 (from beets)
Collecting mutagen>=1.27 (from beets)
Collecting munkres (from beets)
Collecting unidecode (from beets)
Collecting musicbrainzngs>=0.4 (from beets)
Collecting pyyaml (from beets)
Collecting jellyfish (from beets)
  Using cached jellyfish-0.5.3-cp27-none-macosx_10_10_x86_64.whl
Installing collected packages: enum34, mutagen, munkres, unidecode, musicbrainzngs, pyyaml, jellyfish, beets
  Running setup.py develop for beets
Successfully installed beets enum34-1.1.2 jellyfish-0.5.3 munkres-1.0.7 musicbrainzngs-0.6 mutagen-1.31 pyyaml-3.11 unidecode-0.4.19

$ cd venv/src/beets
# You are now in the source directory
```

I'd suggest [forking](https://help.github.com/articles/fork-a-repo/) the repository first and replacing `git+https://github.com/beetbox/beets#egg=beets` with `git+https://github.com/your_name/beets#egg=beets`.

You can then make a new branch, make your changes and make a PR back to this repository.

Hi @jackwilsdon 

Thank you for re-opening the issue and for the detailed description of your environment. 

I will certainly try to fix this soon-ish.

@zigotica i got the same setup: beets db on HD and music files on USB HD.

Thanks for looking into it! If you do put together a PR, can I encourage you to first try _not_ adding a configuration option? Wherever it's possible to do something reasonable by default, we should do that—especially in this case, when we're protecting against a surprising case that could be called "user error."

@sampsyo yes, when i look into it, i will try to use a confirmation in first place. if i end up using a configuration option (ie. `library_removable_when_unmounted_source: true/false`) i would use false by default, otherwise most users would get the same error as now.

does it make sense to you?

Yep, sounds good.


Resolving patch:
diff --git a/beets/ui/commands.py b/beets/ui/commands.py
--- a/beets/ui/commands.py
+++ b/beets/ui/commands.py
@@ -1185,6 +1185,12 @@ def update_items(lib, query, album, move, pretend, fields):
 
 
 def update_func(lib, opts, args):
+    # Verify that the library folder exists to prevent accidental wipes.
+    if not os.path.isdir(lib.directory):
+        ui.print_("Library path is unavailable or does not exist.")
+        ui.print_(lib.directory)
+        if not ui.input_yn("Are you sure you want to continue (y/n)?", True):
+            return
     update_items(lib, decargs(args), opts.album, ui.should_move(opts.move),
                  opts.pretend, opts.fields)
 


Test patch: