beetbox / beets

music library manager and MusicBrainz tagger
http://beets.io/
MIT License
12.67k stars 1.81k forks source link

lyrics: Crash when BeautifulSoup is not installed #4027

Closed j-peeters closed 2 years ago

j-peeters commented 2 years ago

Problem

The Lyrics plugin throws an error/crashes when ran without BeautifulSoup installed.

Terminal output

I currently can't run a verbose command because Beets is dragging itself through finding lyrics for many many albums.

The following error is displayed in Terminal when running:

$ beets lyrics artist song
/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/beets/util/confit.py:21: UserWarning: beets.util.confit is deprecated; use confuse instead
  warnings.warn("beets.util.confit is deprecated; use confuse instead")
Traceback (most recent call last):
  File "/Library/Frameworks/Python.framework/Versions/3.7/bin/beet", line 11, in <module>
    load_entry_point('beets==1.5.0', 'console_scripts', 'beet')()
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/beets/ui/__init__.py", line 1291, in main
    _raw_main(args)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/beets/ui/__init__.py", line 1274, in _raw_main
    subcommands, plugins, lib = _setup(options, lib)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/beets/ui/__init__.py", line 1153, in _setup
    plugins = _load_plugins(options, config)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/beets/ui/__init__.py", line 1139, in _load_plugins
    plugins.send("pluginload")
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/beets/plugins.py", line 497, in send
    for handler in event_handlers()[event]:
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/beets/plugins.py", line 480, in event_handlers
    for plugin in find_plugins():
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/beets/plugins.py", line 315, in find_plugins
    _instances[cls] = cls()
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/beetsplug/lyrics.py", line 736, in __init__
    sources = self.sanitize_bs_sources(sources)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/beetsplug/lyrics.py", line 763, in sanitize_bs_sources
    if source.REQUIRES_BS:
AttributeError: 'str' object has no attribute 'REQUIRES_BS'

After installing BeautifulSoup the output is

/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/beets/util/confit.py:21: UserWarning: beets.util.confit is deprecated; use confuse instead
  warnings.warn("beets.util.confit is deprecated; use confuse instead")

Andrew writes:

We're supposed to silently fix up the configuration when BeautifulSoup is not installed; not crash with a traceback. Glancing at the code, I do see the mistake.

Setup

My configuration (output of beet config) is:

library: ~/Music/Beets/musiclibrary.db
directory: /Volumes/Tunez

import:
    write: yes
    copy: yes
    move: no
    link: no
    hardlink: no
    delete: no
    resume: ask
    incremental: no
    incremental_skip_later: no
    from_scratch: no
    quiet_fallback: skip
    none_rec_action: ask
    timid: no
    log:
    autotag: yes
    quiet: no
    singletons: no
    default_action: apply
    languages: []
    detail: no
    flat: no
    group_albums: no
    pretend: false
    search_ids: []
    duplicate_action: remove
    bell: no
    set_fields: {}

clutter: ["Thumbs.DB", ".DS_Store"]
ignore: [".*", "*~", "System Volume Information", "lost+found"]
ignore_hidden: yes

replace:
    '[\\/]': _
    '^\.': _
    '[\x00-\x1f]': _
    '[<>:"\?\*\|]': ''
    '\.$': _
    '\s+$': ''
    '^\s+': ''
    '^-': _
path_sep_replace: _
asciify_paths: false
art_filename: cover
max_filename_length: 0

aunique:
    keys: albumartist album
    disambiguators: albumtype year samplerate label catalognum albumdisambig releasegroupdisambig
    bracket: '()'

plugins: 
    - inline
    - the
    - lastgenre
    - discogs
    - chroma
    - acousticbrainz
    - fromfilename
    - fetchart
    - embedart
    - ftintitle
    - edit
    - info
    - missing
    - types
    - convert
    - plexupdate
    - web
    - unimported
    - keyfinder
    - fuzzy
    - spotify
    - describe
    - bpmanalyser
    - lyrics
#    - smartplaylist
#    - goingrunning
pluginpath: 
    - /Users/xxxxxxxx/Documents/BeetsPluginDescribe/beetsplug
threaded: yes
timeout: 5.0
per_disc_numbering: no
verbose: 0
terminal_encoding:
original_date: no
artist_credit: no
id3v23: no
va_name: "Various Artists"

zero:
    fields: comments
    update_database: true

# Inline plugin template
item_fields:
  multidisc: 1 if disctotal > 1 else 0
  my_samplerate: round(samplerate / 1000)
  is_flac: 1 if format == "FLAC" else 0
album_fields:  
  format: |
       formatList = []
       for item in items:
           formatList.append(item.format)
       return formatList
  av_bitrate: |
       total = 0
       for item in items:
           total += item.bitrate
       return round(total / len(items) / 1000)
  album_bitdepth:  |
       total = 0
       for item in items:
           total += item.bitdepth
       return round(total / len(items))
  album_samplerate:  |
       total = 0
       for item in items:
           total += item.samplerate
       return round(total / len(items) / 1000)
  is_1644: |
       bd = 0
       sr = 0
       br = 0
       for item in items:
           bd += item.bitdepth
           sr += item.samplerate
           br += item.bitrate
       bd = round(bd / len(items))
       sr = round(sr / len(items) / 1000)
       br = round(br / len(items) / 1000)
       return 1 if bd == 16 and sr == 44 and br > 320 else 0

ui:
    terminal_width: 80
    length_diff_thresh: 10.0
    color: yes
    colors:
        text_success: green
        text_warning: yellow
        text_error: red
        text_highlight: red
        text_highlight_minor: lightgray
        action_default: turquoise
        action: blue

format_item: $artist - $album - $title
format_album: $albumartist - $album
time_format: '%Y-%m-%d %H:%M:%S'
format_raw_length: no

sort_album: albumartist+ album+
sort_item: artist+ album+ disc+ track+
sort_case_insensitive: yes

paths:
#  default: 'Albums/%the{$albumartist}/$album (%if{$original_year,$original_year}) %aunique{albumartist album year, albumdisambig}%if{$albumdisambig,($albumdisambig $year) }%if{$is_1644,,%if{$is_flac,($format $bitdepth-$album_samplerate),($format $av_bitrate)}}/%if{$multidisc,$disc-}$track - $title'
  default: 'Albums/%the{$albumartist}/%if{$original_year,$original_year} - $album %aunique{albumartist album year, albumdisambig}%if{$albumdisambig,($albumdisambig $year) }%if{$is_1644,,%if{$is_flac,($format $album_bitdepth-$album_samplerate),($format $av_bitrate)}}/%if{$multidisc,$disc-}$track - $title'
  dancetrack:1: 'Singles Dance/$artist - $title (%if{$original_year,$original_year}) %aunique{albumartist album year, albumtype label catalognum albumdisambig}%if{$albumdisambig,($albumdisambig$ $year)}($bpm bpm)'
  artistsingles:1: 'Albums/%the{$albumartist}/$album/$title'
  custcomp:1: 'Compilations/$album/$track - $artist - $title'
  custcompmin:1: 'Compilations/$album/$artist - $title'
  singleton: 'Singles/$artist - $title %aunique{albumartist album year, albumtype label catalognum albumdisambig}%if{$albumdisambig,($albumdisambig$ $year)}'
  comp: 'Albums/%the{$albumartist}/$album (%if{$original_year,$original_year}) %aunique{albumartist album year, albumtype label catalognum albumdisambig}%if{$albumdisambig,($albumdisambig $year) }%if{$is_1644,,%if{$is_flac,($format $bitdepth-$album_samplerate),($format $av_bitrate)}}/%if{$multidisc,$disc-}$track - $artist - $title'

statefile: state.pickle

musicbrainz:
    host: musicbrainz.org
    ratelimit: 1
    ratelimit_interval: 1.0
    searchlimit: 5

match:
    strong_rec_thresh: 0.04
    medium_rec_thresh: 0.25
    rec_gap_thresh: 0.25
    max_rec:
        missing_tracks: medium
        unmatched_tracks: medium
    distance_weights:
        source: 2.0
        artist: 3.0
        album: 3.0
        media: 1.0
        mediums: 1.0
        year: 1.0
        country: 0.5
        label: 0.5
        catalognum: 0.5
        albumdisambig: 0.5
        album_id: 5.0
        tracks: 2.0
        missing_tracks: 0.9
        unmatched_tracks: 0.6
        track_title: 3.0
        track_artist: 2.0
        track_index: 1.0
        track_length: 2.0
        track_id: 5.0
    preferred:
        countries: []
        media: ['Digital Media|File' , 'CD']
        original_year: no
    ignored: []
    required: []
    ignored_media: []
    ignore_data_tracks: yes
    ignore_video_tracks: yes
    track_length_grace: 10
    track_length_max: 30

chroma:
    auto: no

fetchart:
    auto: yes
    cautious: no
    cover_names: cover front art album folder
    minwidth: 290
    maxwidth: 1050
    sources: itunes amazon albumart lastfm fanart filesystem coverart 
    fanarttv_key: xxxxxxxxxxxxx
    lastfm_key: xxxxxxxxxxxx

acoustid: 
    apikey: xxxxxxxxx

embedart:
    auto: yes
    ifempty: no   

lastgenre:
    auto: yes
    canonical: no
    count: 1
    force: yes
    source: album
    whitelist: yes
    min_weight: 10
    fallback:
    separator: ', '
    prefer_specific: no

types:
    av_bitrate: int
    my_samplerate: int
    bitdepth: int
    album_bitdepth: int
    album_samplerate: int
    rating: int

convert:
    dest: ~/Music/BeetExports
    command: ffmpeg -i $source -y -vn -aq 0 $dest    
    extension: mp3
    format: mp3
    never_convert_lossy_files: yes

plex:
    host: localhost
    port: 32400
    token: xxxxxxxxxxxxxx

lastfm:
    user: xxxxxxxxxx

smartplaylist:
    playlist_dir: /Users/Shared/Music/Playlists
    auto: no
    playlists:
        - name: 'favorites.m3u' 
          query: rating:4..5
        - name: 'danceable.m3u'
          query: 
            - danceable:0.8..
        - name: 'acoustic.m3u'
          query: 
            - mood_acoustic:0.8..
        - name: 'aggresive.m3u'
          query: 
            - mood_aggresive:0.8..
        - name: 'electronic.m3u'
          query: 
            - mood_electronic:0.8..
        - name: 'danceable.m3u'
          query: 
            - danceable:0.8..
        - name: 'happy.m3u'
          query: 
            - mood_happy:0.8..           
        - name: 'party.m3u'
          query: 
            - mood_party:0.8.. 
        - name: 'relaxed.m3u'
          query: 
            - mood_relaxed:0.8..                   
        - name: 'sad.m3u'
          query: 
            - mood_sad:0.8..

spotify:
    mode: list
    show_failures: on
    tiebreak: first

bpmanalyser:
    auto: no
    dry-run: no
    write: yes
    force: no
    quiet: no

goingrunning:
  targets:
    subsonic_m3u:
      device_root: /Users/Shared/Music/Playlists
      device_path: 
      clean_target: yes
      generate_playlist: yes
      copy_files: no
  trainings:
    10K:
      use_flavours: [intensity_low, intensity_high, running]
      duration: 180
      target: subsonic_m3u
  flavours:
    intensity_low:
      bpm: 87..88
    intensity_high:
      bpm: 175..177
    running:
      genre: [electronic, hip hop, funk, trip hop, house]

web:
    host: 0.0.0.0
    cors: 'http://localhost:3000'

lyrics:
    auto: yes
    google_API_key: xxxxxxxxxxxxxxxxx
    google_engine_ID: 009217259823014548361:lndtuqkycfu

unimported:
    ignore_extensions: mid wav
sampsyo commented 2 years ago

Thanks! This looks like a recently-introduced bug. The root cause is a function that expects a Source object but is actually receiving a string.

ctrueden commented 2 years ago

The commit that introduced this bug is 316b79f72fd5c4f39e4d14b417c7bb5fd8864e4a by @wisp3rwind.

j-peeters commented 2 years ago

Thanks for fixing this! I'm a total newbie with branches, masters, fixes etc. If I want to use this fix, how do I update Beets to include this new fix? I'm currently on the 1.5 version.

sampsyo commented 2 years ago

The FAQ has instructions: https://beets.readthedocs.io/en/stable/faq.html#run-the-latest-source-version-of-beets

ctrueden commented 2 years ago

@sampsyo Side question about that: I notice the docs don't mention the pattern:

git clone git://github.com/beetbox/beets
cd beets
pip install -e .

to install beets from source in editable mode. Should we add it? The advantage is that you can then hack on your beets working copy, and the changes are immediately reflected in your environment without needing to run python setup.py install every time. This is how I've been developing my beets plugins, and I find it very convenient. Or is there some downside to pip install -e that I don't know (I'm not super expert at python)?

Edit: Reading more carefully, I see that pip install -e is the third option listed... but using the "automatic source checkout" approach. So I guess the distinction here is merely whether you clone it manually yourself, and therefore get to decide where the sources go? *shrug*

Edit 2: I filed #4084 suggesting an addition to the FAQ about pip install -e .. Please feel free to close if it's not to your liking.