mirror of
https://github.com/beetbox/beets.git
synced 2025-12-09 10:05:35 +01:00
Add badfiles plugin as-is
This commit is contained in:
parent
315fe78f0c
commit
82484be232
4 changed files with 150 additions and 3 deletions
93
beetsplug/badfiles.py
Normal file
93
beetsplug/badfiles.py
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
#!/usr/bin/python
|
||||
# coding=utf-8
|
||||
|
||||
# Base Python File (badfiles.py)
|
||||
# Created: Tue 11 Aug 2015 10:46:34 PM CEST
|
||||
# Version: 1.0
|
||||
#
|
||||
# This Python script was developped by François-Xavier Thomas.
|
||||
# You are free to copy, adapt or modify it.
|
||||
# If you do so, however, leave my name somewhere in the credits, I'd appreciate it ;)
|
||||
#
|
||||
# (ɔ) François-Xavier Thomas <fx.thomas@gmail.com>
|
||||
|
||||
from beets.plugins import BeetsPlugin
|
||||
from beets.ui import Subcommand
|
||||
from beets.util import displayable_path
|
||||
from beets import ui
|
||||
from subprocess import check_output, CalledProcessError, list2cmdline, STDOUT
|
||||
import shlex
|
||||
import os
|
||||
import errno
|
||||
import sys
|
||||
|
||||
class BadFiles(BeetsPlugin):
|
||||
|
||||
def run_command(self, cmd):
|
||||
self._log.debug(u"running command: %s" % displayable_path(list2cmdline(cmd)))
|
||||
try:
|
||||
output = check_output(cmd, stderr=STDOUT)
|
||||
return 0, [line for line in output.split("\n") if line]
|
||||
except CalledProcessError as e:
|
||||
return 1, [line for line in e.output.split("\n") if line]
|
||||
except OSError as e:
|
||||
if e.errno == errno.ENOENT:
|
||||
ui.print_("Command '%s' does not exit. Is it installed?" % cmd[0])
|
||||
sys.exit(1)
|
||||
else:
|
||||
raise
|
||||
|
||||
def check_mp3val(self, path):
|
||||
errors, output = self.run_command(["mp3val", path])
|
||||
if errors == 0:
|
||||
output = [line for line in output if line.startswith("WARNING:")]
|
||||
errors = sum(1 for line in output if line.startswith("WARNING:"))
|
||||
return errors, output
|
||||
|
||||
def check_flac(self, path):
|
||||
return self.run_command(["flac", "-wst", path])
|
||||
|
||||
def check_custom(self, command):
|
||||
def checker(path):
|
||||
cmd = shlex.split(command)
|
||||
cmd.append(path)
|
||||
return self.run_command(cmd)
|
||||
return checker
|
||||
|
||||
def get_checker(self, ext):
|
||||
ext = ext.lower()
|
||||
command = self.config['commands'].get().get(ext)
|
||||
if command:
|
||||
return self.check_custom(command)
|
||||
elif ext == "mp3":
|
||||
return self.check_mp3val
|
||||
elif ext == "flac":
|
||||
return self.check_flac
|
||||
|
||||
def check_bad(self, lib, opts, args):
|
||||
for item in lib.items(args):
|
||||
|
||||
# First check if the path exists. If not, should run 'beets update'
|
||||
# to cleanup your library.
|
||||
dpath = displayable_path(item.path)
|
||||
self._log.debug(u"checking path: %s" % dpath)
|
||||
if not os.path.exists(item.path):
|
||||
ui.print_(u"%s: file does not exist" % dpath)
|
||||
|
||||
# Run the checker against the file if one is found
|
||||
ext = os.path.splitext(item.path)[1][1:]
|
||||
checker = self.get_checker(ext)
|
||||
if not checker:
|
||||
continue
|
||||
errors, output = checker(item.path)
|
||||
if errors == 0:
|
||||
ui.print_(u"%s: ok" % dpath)
|
||||
else:
|
||||
ui.print_(u"%s: checker found %d errors or warnings" % (dpath, errors))
|
||||
for line in output:
|
||||
ui.print_(u" %s" % displayable_path(line))
|
||||
|
||||
def commands(self):
|
||||
bad_command = Subcommand('bad', help='check for corrupt or missing files')
|
||||
bad_command.func = self.check_bad
|
||||
return [bad_command]
|
||||
|
|
@ -10,6 +10,8 @@ The new features:
|
|||
magenta). Thanks to :user:`mathstuf`. :bug:`1548`
|
||||
* :doc:`/plugins/play`: A new ``--args`` option lets you specify options for
|
||||
the player command. :bug:`1532`
|
||||
* A new :doc:`/plugins/badfiles` helps you scan for corruption in your music
|
||||
collection.
|
||||
|
||||
Fixes:
|
||||
|
||||
|
|
|
|||
53
docs/plugins/badfiles.rst
Normal file
53
docs/plugins/badfiles.rst
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
Bad Files Plugin
|
||||
================
|
||||
|
||||
Adds a `beet bad` command to check for missing, and optionally corrupt files.
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
Here is a very basic configuration that uses the default commands for MP3 and
|
||||
FLAC files, requiring the `mp3val`_ and
|
||||
packages to be installed::
|
||||
|
||||
badfiles:
|
||||
commands: {}
|
||||
plugins: ... badfiles
|
||||
|
||||
Note that the *mp3val* checker is a bit verbose and can output a lot of "stream
|
||||
error" messages, even for files that play perfectly well. Generally if more
|
||||
than one stream error happens, or if a stream error happens in the middle of a
|
||||
file, this is a bad sign.
|
||||
|
||||
.. _mp3val: http://mp3val.sourceforge.net/
|
||||
.. _flac: https://xiph.org/flac/
|
||||
|
||||
You can also add custom commands for a specific extension, e.g.::
|
||||
|
||||
badfiles:
|
||||
commands:
|
||||
ogg: myoggchecker --opt1 --opt2
|
||||
flac: flac --test --warnings-as-errors --silent
|
||||
plugins: ... badfiles
|
||||
|
||||
Running
|
||||
-------
|
||||
|
||||
To run Badfiles, just use the ``beet bad`` command with Beets' usual query syntax.
|
||||
|
||||
For instance, this will run a check on all songs containing the word "wolf"::
|
||||
|
||||
beet bad wolf
|
||||
|
||||
This one will run checks on a specific album::
|
||||
|
||||
beet bad album_id:1234
|
||||
|
||||
Here is an example from my library where the FLAC decoder was signaling a
|
||||
corrupt file::
|
||||
|
||||
beet bad title::^$
|
||||
/tank/Music/__/00.flac: command exited with status 1
|
||||
00.flac: *** Got error code 2:FLAC__STREAM_DECODER_ERROR_STATUS_FRAME_CRC_MISMATCH
|
||||
00.flac: ERROR while decoding data
|
||||
state = FLAC__STREAM_DECODER_READ_FRAME
|
||||
|
|
@ -31,6 +31,7 @@ Each plugin has its own set of options that can be defined in a section bearing
|
|||
.. toctree::
|
||||
:hidden:
|
||||
|
||||
badfiles
|
||||
bpd
|
||||
bpm
|
||||
bucket
|
||||
|
|
@ -139,6 +140,7 @@ Interoperability
|
|||
changes.
|
||||
* :doc:`smartplaylist`: Generate smart playlists based on beets queries.
|
||||
* :doc:`thumbnails`: Get thumbnails with the cover art on your album folders.
|
||||
* :doc:`badfiles`: Check audio file integrity.
|
||||
|
||||
|
||||
.. _Plex: http://plex.tv
|
||||
|
|
@ -211,8 +213,6 @@ Here are a few of the plugins written by the beets community:
|
|||
|
||||
* `beets-noimport`_ adds and removes directories from the incremental import skip list.
|
||||
|
||||
* `beets-badfiles`_ helps you identify broken audio files.
|
||||
|
||||
.. _beets-check: https://github.com/geigerzaehler/beets-check
|
||||
.. _copyartifacts: https://github.com/sbarakat/beets-copyartifacts
|
||||
.. _dsedivec: https://github.com/dsedivec/beets-plugins
|
||||
|
|
@ -228,4 +228,3 @@ Here are a few of the plugins written by the beets community:
|
|||
.. _beets-follow: https://github.com/nolsto/beets-follow
|
||||
.. _beets-setlister: https://github.com/tomjaspers/beets-setlister
|
||||
.. _beets-noimport: https://github.com/ttsda/beets-noimport
|
||||
.. _beets-badfiles: https://github.com/fxthomas/beets-badfiles
|
||||
|
|
|
|||
Loading…
Reference in a new issue