Repository: beetbox/beets

Issue:
fetchart: Handle exceptions in Google backend
With a big library if you force a fetchart there is a good chance that a connection is reset and unfortunately, this case isn't handled with some basic retrying:

``` python
fetchart: The Chemical Brothers - The Private Psychedelic Reel: found album art
fetchart: The Chemical Brothers - We Are the Night: found album art
Traceback (most recent call last):
  File "/usr/local/bin/beet", line 9, in <module>
    load_entry_point('beets==1.3.15', 'console_scripts', 'beet')()
  File "/usr/local/Cellar/python/2.7.9/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/beets-1.3.15-py2.7.egg/beets/ui/__init__.py", line 1161, in main
    _raw_main(args)
  File "/usr/local/Cellar/python/2.7.9/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/beets-1.3.15-py2.7.egg/beets/ui/__init__.py", line 1151, in _raw_main
    subcommand.func(lib, suboptions, subargs)
  File "/usr/local/Cellar/python/2.7.9/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/beets-1.3.15-py2.7.egg/beetsplug/fetchart.py", line 412, in func
    self.batch_fetch_art(lib, lib.albums(ui.decargs(args)), opts.force)
  File "/usr/local/Cellar/python/2.7.9/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/beets-1.3.15-py2.7.egg/beetsplug/fetchart.py", line 513, in batch_fetch_art
    path = self.art_for_album(album, local_paths)
  File "/usr/local/Cellar/python/2.7.9/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/beets-1.3.15-py2.7.egg/beetsplug/fetchart.py", line 487, in art_for_album
    for url in self._source_urls(album):
  File "/usr/local/Cellar/python/2.7.9/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/beets-1.3.15-py2.7.egg/beetsplug/fetchart.py", line 532, in _source_urls
    for url in urls:
  File "/usr/local/Cellar/python/2.7.9/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/beets-1.3.15-py2.7.egg/beetsplug/fetchart.py", line 125, in get
    'start': '0',
  File "/Library/Python/2.7/site-packages/requests/sessions.py", line 473, in get
    return self.request('GET', url, **kwargs)
  File "/Library/Python/2.7/site-packages/requests/sessions.py", line 461, in request
    resp = self.send(prep, **send_kwargs)
  File "/Library/Python/2.7/site-packages/requests/sessions.py", line 573, in send
    r = adapter.send(request, **kwargs)
  File "/Library/Python/2.7/site-packages/requests/adapters.py", line 415, in send
    raise ConnectionError(err, request=request)
requests.exceptions.ConnectionError: ('Connection aborted.', error(54, 'Connection reset by peer'))
```



Discussion:
Thanks. Looks like a bug specifically in the Google backend, which doesn't catch any exceptions for its search request:
https://github.com/sampsyo/beets/blob/master/beetsplug/fetchart.py#L122

We do have error handling for most cases:
https://github.com/sampsyo/beets/blob/master/beetsplug/fetchart.py#L439


Resolving patch:
diff --git a/beetsplug/fetchart.py b/beetsplug/fetchart.py
--- a/beetsplug/fetchart.py
+++ b/beetsplug/fetchart.py
@@ -365,12 +365,17 @@ def get(self, album, plugin, paths):
         if not (album.albumartist and album.album):
             return
         search_string = (album.albumartist + ',' + album.album).encode('utf-8')
-        response = self.request(self.URL, params={
-            'key': self.key,
-            'cx': self.cx,
-            'q': search_string,
-            'searchType': 'image'
-        })
+
+        try:
+            response = self.request(self.URL, params={
+                'key': self.key,
+                'cx': self.cx,
+                'q': search_string,
+                'searchType': 'image'
+            })
+        except requests.RequestException:
+            self._log.debug(u'google: error receiving response')
+            return
 
         # Get results using JSON.
         try:
@@ -406,10 +411,14 @@ def get(self, album, plugin, paths):
         if not album.mb_releasegroupid:
             return
 
-        response = self.request(
-            self.API_ALBUMS + album.mb_releasegroupid,
-            headers={'api-key': self.PROJECT_KEY,
-                     'client-key': self.client_key})
+        try:
+            response = self.request(
+                self.API_ALBUMS + album.mb_releasegroupid,
+                headers={'api-key': self.PROJECT_KEY,
+                         'client-key': self.client_key})
+        except requests.RequestException:
+            self._log.debug(u'fanart.tv: error receiving response')
+            return
 
         try:
             data = response.json()
@@ -545,16 +554,22 @@ def get(self, album, plugin, paths):
 
         # Find the name of the cover art filename on DBpedia
         cover_filename, page_id = None, None
-        dbpedia_response = self.request(
-            self.DBPEDIA_URL,
-            params={
-                'format': 'application/sparql-results+json',
-                'timeout': 2500,
-                'query': self.SPARQL_QUERY.format(
-                    artist=album.albumartist.title(), album=album.album)
-            },
-            headers={'content-type': 'application/json'},
-        )
+
+        try:
+            dbpedia_response = self.request(
+                self.DBPEDIA_URL,
+                params={
+                    'format': 'application/sparql-results+json',
+                    'timeout': 2500,
+                    'query': self.SPARQL_QUERY.format(
+                        artist=album.albumartist.title(), album=album.album)
+                },
+                headers={'content-type': 'application/json'},
+            )
+        except requests.RequestException:
+            self._log.debug(u'dbpedia: error receiving response')
+            return
+
         try:
             data = dbpedia_response.json()
             results = data['results']['bindings']
@@ -584,17 +599,21 @@ def get(self, album, plugin, paths):
             lpart, rpart = cover_filename.rsplit(' .', 1)
 
             # Query all the images in the page
-            wikipedia_response = self.request(
-                self.WIKIPEDIA_URL,
-                params={
-                    'format': 'json',
-                    'action': 'query',
-                    'continue': '',
-                    'prop': 'images',
-                    'pageids': page_id,
-                },
-                headers={'content-type': 'application/json'},
-            )
+            try:
+                wikipedia_response = self.request(
+                    self.WIKIPEDIA_URL,
+                    params={
+                        'format': 'json',
+                        'action': 'query',
+                        'continue': '',
+                        'prop': 'images',
+                        'pageids': page_id,
+                    },
+                    headers={'content-type': 'application/json'},
+                )
+            except requests.RequestException:
+                self._log.debug(u'wikipedia: error receiving response')
+                return
 
             # Try to see if one of the images on the pages matches our
             # incomplete cover_filename
@@ -613,18 +632,22 @@ def get(self, album, plugin, paths):
                 return
 
         # Find the absolute url of the cover art on Wikipedia
-        wikipedia_response = self.request(
-            self.WIKIPEDIA_URL,
-            params={
-                'format': 'json',
-                'action': 'query',
-                'continue': '',
-                'prop': 'imageinfo',
-                'iiprop': 'url',
-                'titles': cover_filename.encode('utf-8'),
-            },
-            headers={'content-type': 'application/json'},
-        )
+        try:
+            wikipedia_response = self.request(
+                self.WIKIPEDIA_URL,
+                params={
+                    'format': 'json',
+                    'action': 'query',
+                    'continue': '',
+                    'prop': 'imageinfo',
+                    'iiprop': 'url',
+                    'titles': cover_filename.encode('utf-8'),
+                },
+                headers={'content-type': 'application/json'},
+            )
+        except requests.RequestException:
+            self._log.debug(u'wikipedia: error receiving response')
+            return
 
         try:
             data = wikipedia_response.json()


Test patch: