Repository: beetbox/beets

Issue:
lyrics: UnicodeEncodeError may happen when google backend activated
```
fetching Elephanz Stereo
Traceback (most recent call last):
  File "/Users/flap/bin/beet", line 20, in <module>
    beets.ui.main()
  File "/Users/flap/Dev/beets/beets/ui/__init__.py", line 937, in main
    _raw_main(args)
  File "/Users/flap/Dev/beets/beets/ui/__init__.py", line 927, in _raw_main
    subcommand.func(lib, suboptions, subargs)
  File "/Users/flap/Dev/beets/beets/ui/commands.py", line 872, in import_func
    import_files(lib, paths, query)
  File "/Users/flap/Dev/beets/beets/ui/commands.py", line 844, in import_files
    session.run()
  File "/Users/flap/Dev/beets/beets/importer.py", line 300, in run
    pl.run_parallel(QUEUE_SIZE)
  File "/Users/flap/Dev/beets/beets/util/pipeline.py", line 299, in run
    out = self.coro.send(msg)
  File "/Users/flap/Dev/beets/beets/util/pipeline.py", line 181, in coro
    func(*(args + (task,)))
  File "/Users/flap/Dev/beets/beets/importer.py", line 1263, in plugin_stage
    func(session, task)
  File "/Users/flap/Dev/beets/beetsplug/lyrics.py", line 457, in imported
    False, False)
  File "/Users/flap/Dev/beets/beetsplug/lyrics.py", line 473, in fetch_item_lyrics
    lyrics = [self.get_lyrics(artist, title) for title in titles]
  File "/Users/flap/Dev/beets/beetsplug/lyrics.py", line 503, in get_lyrics
    lyrics = backend(artist, title)
  File "/Users/flap/Dev/beets/beetsplug/lyrics.py", line 405, in fetch_google
    if is_lyrics(lyrics, artist):
  File "/Users/flap/Dev/beets/beetsplug/lyrics.py", line 302, in is_lyrics
    text.decode('utf8')))
  File "/usr/local/Cellar/python/2.7.8_1/Frameworks/Python.framework/Versions/2.7/lib/python2.7/encodings/utf_8.py", line 16, in decode
    return codecs.utf_8_decode(input, errors, True)
UnicodeEncodeError: 'ascii' codec can't encode character u'\xe9' in position 61: ordinal not in range(128)
```



Discussion:

Resolving patch:
diff --git a/beetsplug/lyrics.py b/beetsplug/lyrics.py
--- a/beetsplug/lyrics.py
+++ b/beetsplug/lyrics.py
@@ -18,9 +18,10 @@
 
 import re
 import logging
-import urllib
+import requests
 import json
 import unicodedata
+import urllib
 import difflib
 import itertools
 from HTMLParser import HTMLParseError
@@ -60,11 +61,12 @@ def fetch_url(url):
     """Retrieve the content at a given URL, or return None if the source
     is unreachable.
     """
-    try:
-        return urllib.urlopen(url).read()
-    except IOError as exc:
-        log.debug(u'failed to fetch: {0} ({1})'.format(url, unicode(exc)))
-        return None
+    r = requests.get(url)
+    if r.status_code == requests.codes.ok:
+        return r.text
+    else:
+        log.debug(u'failed to fetch: {0} ({1})'.format(url, r.status_code))
+    return None
 
 
 def unescape(text):
@@ -298,8 +300,7 @@ def is_lyrics(text, artist=None):
     badTriggersOcc = []
     nbLines = text.count('\n')
     if nbLines <= 1:
-        log.debug(u"Ignoring too short lyrics '{0}'".format(
-                  text.decode('utf8')))
+        log.debug(u"Ignoring too short lyrics '{0}'".format(text))
         return 0
     elif nbLines < 5:
         badTriggersOcc.append('too_short')
@@ -368,9 +369,7 @@ def is_text_notcode(text):
                              parse_only=SoupStrainer(text=is_text_notcode))
     except HTMLParseError:
         return None
-
     soup = sorted(soup.stripped_strings, key=len)[-1]
-
     return soup
 
 
@@ -396,7 +395,6 @@ def fetch_google(artist, title):
             urlTitle = item.get('title', u'')
             if not is_page_candidate(urlLink, urlTitle, title, artist):
                 continue
-
             html = fetch_url(urlLink)
             lyrics = scrape_lyrics_from_html(html)
             if not lyrics:
@@ -501,8 +499,6 @@ def get_lyrics(self, artist, title):
         for backend in self.backends:
             lyrics = backend(artist, title)
             if lyrics:
-                if isinstance(lyrics, str):
-                    lyrics = lyrics.decode('utf8', 'ignore')
                 log.debug(u'got lyrics from backend: {0}'
                           .format(backend.__name__))
                 return lyrics.strip()


Test patch: