We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Error: SSLError(SSLError("bad handshake: SysCallError(-1, 'Unexpected EOF')",),)
```
2017-08-22 17:13:36 ERROR    SHOWQUEUE-ADD :: [49e9fc3] 289590 Error while loading information from indexer TVDBv2. Error: SSLError(SSLError("bad handshake: SysCallError(-1, 'Unexpected EOF')",),)
Traceback (most recent call last):
  File "/app/medusa/medusa/show_queue.py", line 419, in run
    check_existing_shows(s, self.indexer)
  File "/app/medusa/medusa/helpers/externals.py", line 131, in check_existing_shows
    new_show_externals = get_externals(indexer=indexer, indexed_show=indexed_show)
  File "/app/medusa/medusa/helpers/externals.py", line 105, in get_externals
    new_show_externals.update(t.get_id_by_external(**new_show_externals))
  File "/app/medusa/medusa/indexers/tmdb/tmdb.py", line 593, in get_id_by_external
    result = self.tmdb.Find(kwargs.get(external_id)).info(**{'external_source': external_id})
  File "/app/medusa/lib/tmdbsimple/find.py", line 50, in info
    response = self._GET(path, kwargs)
  File "/app/medusa/lib/tmdbsimple/base.py", line 87, in _GET
    return self._request('GET', path, params=params)
  File "/app/medusa/lib/tmdbsimple/base.py", line 80, in _request
    headers=self.headers)
  File "/app/medusa/lib/requests/sessions.py", line 475, in request
    resp = self.send(prep, **send_kwargs)
  File "/app/medusa/lib/requests/sessions.py", line 585, in send
    r = adapter.send(request, **kwargs)
  File "/app/medusa/lib/cachecontrol/adapter.py", line 46, in send
    resp = super(CacheControlAdapter, self).send(request, **kw)
  File "/app/medusa/lib/requests/adapters.py", line 477, in send
    raise SSLError(e, request=request)
SSLError: ("bad handshake: SysCallError(-1, 'Unexpected EOF')",)
```

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `medusa/helpers/externals.py`
Content:
```
1 # coding=utf-8
2 
3 """Externals helper functions."""
4 
5 import logging
6 
7 from medusa import app, db
8 from medusa.indexers.indexer_api import indexerApi
9 from medusa.indexers.indexer_config import indexerConfig, mappings
10 from medusa.indexers.indexer_exceptions import IndexerException, IndexerShowAllreadyInLibrary, IndexerUnavailable
11 from medusa.logger.adapters.style import BraceAdapter
12 
13 from traktor import AuthException, TokenExpiredException, TraktApi, TraktException
14 
15 log = BraceAdapter(logging.getLogger(__name__))
16 log.logger.addHandler(logging.NullHandler())
17 
18 
19 def get_trakt_externals(externals):
20     """Small trakt api wrapper, to request trakt externals using multiple external id's.
21 
22     :param externals: Dictionary of key/value pairs with external id's.
23     """
24     def trakt_request(api, trakt_url):
25         """Perform the request and handle possible token refresh."""
26         try:
27             trakt_result = api.request(trakt_url) or []
28             if api.access_token_refreshed:
29                 app.TRAKT_ACCESS_TOKEN = api.access_token
30                 app.TRAKT_REFRESH_TOKEN = api.refresh_token
31                 app.instance.save_config()
32         except (AuthException, TraktException, TokenExpiredException) as e:
33             log.info(u'Could not use Trakt to enrich with externals: {0}',
34                      e.message or e)
35             return []
36         else:
37             return trakt_result
38 
39     trakt_settings = {'trakt_api_key': app.TRAKT_API_KEY,
40                       'trakt_api_secret': app.TRAKT_API_SECRET,
41                       'trakt_access_token': app.TRAKT_ACCESS_TOKEN,
42                       'trakt_refresh_token': app.TRAKT_REFRESH_TOKEN}
43     trakt_api = TraktApi(app.SSL_VERIFY, app.TRAKT_TIMEOUT, **trakt_settings)
44 
45     id_lookup = '/search/{external_key}/{external_value}?type=show'
46     trakt_mapping = {'tvdb_id': 'tvdb', 'imdb_id': 'imdb', 'tmdb_id': 'tmdb', 'trakt_id': 'trakt'}
47     trakt_mapping_rev = {v: k for k, v in trakt_mapping.items()}
48 
49     for external_key in externals:
50         if not trakt_mapping.get(external_key) or not externals[external_key]:
51             continue
52 
53         url = id_lookup.format(external_key=trakt_mapping[external_key], external_value=externals[external_key])
54         log.debug(
55             u'Looking for externals using Trakt and {indexer} id {number}', {
56                 'indexer': trakt_mapping[external_key],
57                 'number': externals[external_key],
58             }
59         )
60         result = trakt_request(trakt_api, url)
61         if result and len(result) and result[0].get('show') and result[0]['show'].get('ids'):
62             ids = {trakt_mapping_rev[k]: v for k, v in result[0]['show'].get('ids').items()
63                    if v and trakt_mapping_rev.get(k)}
64             return ids
65     return {}
66 
67 
68 def get_externals(show=None, indexer=None, indexed_show=None):
69     """Use as much as possible sources to map known id's.
70 
71     Provide the external id's you have in a dictionary, and use as much available resources as possible to retrieve
72     external id's.
73     :param show: Series object.
74     :param indexer: Indexer id. For example 1 for tvdb or 4 for tmdb.
75     :param indexed_show: The result of a fully indexed shows. For example after an t['12345']
76     """
77     if show:
78         indexer = show.indexer
79         new_show_externals = show.externals
80     else:
81         if not indexer or not indexed_show:
82             raise Exception('Need a minimum of a show object or an indexer + indexer_api '
83                             '(Show searched through indexerApi.')
84         new_show_externals = getattr(indexed_show, 'externals', {})
85 
86     # For this show let's get all externals, and use them.
87     mappings = {indexer: indexerConfig[indexer]['mapped_to'] for indexer in indexerConfig}
88     other_indexers = [mapped_indexer for mapped_indexer in mappings if mapped_indexer != indexer]
89 
90     # We for example want to add through tmdb, but the show is already added through tvdb.
91     # If tmdb doesn't have a mapping to imdb, but tvmaze does, there is a small chance we can use that.
92 
93     for other_indexer in other_indexers:
94         lindexer_api_pararms = indexerApi(other_indexer).api_params.copy()
95         try:
96             t = indexerApi(other_indexer).indexer(**lindexer_api_pararms)
97         except IndexerUnavailable:
98             continue
99         if hasattr(t, 'get_id_by_external'):
100             log.debug(u"Trying other indexer: {indexer} get_id_by_external",
101                       {'indexer': indexerApi(other_indexer).name})
102             # Call the get_id_by_external and pass all the externals we have,
103             # except for the indexers own.
104             try:
105                 new_show_externals.update(t.get_id_by_external(**new_show_externals))
106             except IndexerException as error:
107                 log.warning(
108                     u'Error getting external ids for other'
109                     u' indexer {name}: {reason}',
110                     {'name': indexerApi(show.indexer).name, 'reason': error.message})
111 
112     # Try to update with the Trakt externals.
113     if app.USE_TRAKT:
114         new_show_externals.update(get_trakt_externals(new_show_externals))
115 
116     return new_show_externals
117 
118 
119 def check_existing_shows(indexed_show, indexer):
120     """Check if the searched show already exists in the current library.
121 
122     :param indexed_show: (Indexer Show object) The indexed show from -for example- tvdb. It might already have some
123     externals like imdb_id which can be used to search at tmdb, tvmaze or trakt.
124     :param indexer: (int) The indexer id, which has been used to search the indexed_show with.
125     :return: Raises the exception IndexerShowAllreadyInLibrary() when the show is already in your library.
126     """
127     # For this show let's get all externals, and use them.
128     mappings = {indexer: indexerConfig[indexer]['mapped_to'] for indexer in indexerConfig}
129     other_indexers = [mapped_indexer for mapped_indexer in mappings if mapped_indexer != indexer]
130 
131     new_show_externals = get_externals(indexer=indexer, indexed_show=indexed_show)
132 
133     # Iterate through all shows in library, and see if one of our externals matches it's indexer_id
134     # Or one of it's externals.
135     for show in app.showList:
136 
137         # Check if the new shows indexer id matches the external for the show
138         # in library
139         if show.externals.get(mappings[indexer]) and indexed_show['id'] == show.externals.get(mappings[indexer]):
140             log.debug(u'Show already in database. [{id}] {name}',
141                       {'name': show.name, 'id': indexed_show['id']})
142             raise IndexerShowAllreadyInLibrary('The show {0} has already been added by the indexer {1}. '
143                                                'Please remove the show, before you can add it through {2}.'
144                                                .format(show.name, indexerApi(show.indexer).name,
145                                                        indexerApi(indexer).name))
146 
147         for new_show_external_key in new_show_externals.keys():
148             if show.indexer not in other_indexers:
149                 continue
150 
151             # Check if one of the new shows externals matches one of the
152             # externals for the show in library.
153             if not new_show_externals.get(new_show_external_key) or not show.externals.get(new_show_external_key):
154                 continue
155 
156             if new_show_externals.get(new_show_external_key) == show.externals.get(new_show_external_key):
157                 log.debug(
158                     u'Show already in database under external ID ({existing})'
159                     u' for ({id}) {name}', {
160                         'name': show.name,
161                         'id': show.externals.get(new_show_external_key),
162                         'existing': new_show_external_key,
163                     }
164                 )
165                 raise IndexerShowAllreadyInLibrary('The show {0} has already been added by the indexer {1}. '
166                                                    'Please remove the show, before you can add it through {2}.'
167                                                    .format(show.name, indexerApi(show.indexer).name,
168                                                            indexerApi(indexer).name))
169 
170 
171 def load_externals_from_db(indexer=None, indexer_id=None):
172     """Load and recreate the indexers external id's.
173 
174     :param indexer: Optional pass indexer id, else use the current shows indexer.
175     :type indexer: int
176     :param indexer_id: Optional pass indexer id, else use the current shows indexer.
177     :type indexer_id: int
178     """
179     externals = {}
180 
181     main_db_con = db.DBConnection()
182     sql = (b'SELECT indexer, indexer_id, mindexer, mindexer_id '
183            b'FROM indexer_mapping '
184            b'WHERE (indexer = ? AND indexer_id = ?) '
185            b'OR (mindexer = ? AND mindexer_id = ?)')
186 
187     results = main_db_con.select(sql, [indexer, indexer_id, indexer, indexer_id])
188 
189     for result in results:
190         try:
191             if result[b'indexer'] == indexer:
192                 externals[mappings[result[b'mindexer']]] = result[b'mindexer_id']
193             else:
194                 externals[mappings[result[b'indexer']]] = result[b'indexer_id']
195         except KeyError as e:
196             log.error(u'Indexer not supported in current mappings: {id}', {'id': e.message})
197 
198     return externals
199
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/medusa/helpers/externals.py b/medusa/helpers/externals.py
--- a/medusa/helpers/externals.py
+++ b/medusa/helpers/externals.py
@@ -10,6 +10,7 @@
 from medusa.indexers.indexer_exceptions import IndexerException, IndexerShowAllreadyInLibrary, IndexerUnavailable
 from medusa.logger.adapters.style import BraceAdapter
 
+from requests.exceptions import RequestException
 from traktor import AuthException, TokenExpiredException, TraktApi, TraktException
 
 log = BraceAdapter(logging.getLogger(__name__))
@@ -103,7 +104,7 @@
             # except for the indexers own.
             try:
                 new_show_externals.update(t.get_id_by_external(**new_show_externals))
-            except IndexerException as error:
+            except (IndexerException, RequestException) as error:
                 log.warning(
                     u'Error getting external ids for other'
                     u' indexer {name}: {reason}',

{"golden_diff": "diff --git a/medusa/helpers/externals.py b/medusa/helpers/externals.py\n--- a/medusa/helpers/externals.py\n+++ b/medusa/helpers/externals.py\n@@ -10,6 +10,7 @@\n from medusa.indexers.indexer_exceptions import IndexerException, IndexerShowAllreadyInLibrary, IndexerUnavailable\n from medusa.logger.adapters.style import BraceAdapter\n \n+from requests.exceptions import RequestException\n from traktor import AuthException, TokenExpiredException, TraktApi, TraktException\n \n log = BraceAdapter(logging.getLogger(__name__))\n@@ -103,7 +104,7 @@\n             # except for the indexers own.\n             try:\n                 new_show_externals.update(t.get_id_by_external(**new_show_externals))\n-            except IndexerException as error:\n+            except (IndexerException, RequestException) as error:\n                 log.warning(\n                     u'Error getting external ids for other'\n                     u' indexer {name}: {reason}',\n", "issue": "Error: SSLError(SSLError(\"bad handshake: SysCallError(-1, 'Unexpected EOF')\",),)\n```\r\n2017-08-22 17:13:36 ERROR    SHOWQUEUE-ADD :: [49e9fc3] 289590 Error while loading information from indexer TVDBv2. Error: SSLError(SSLError(\"bad handshake: SysCallError(-1, 'Unexpected EOF')\",),)\r\nTraceback (most recent call last):\r\n  File \"/app/medusa/medusa/show_queue.py\", line 419, in run\r\n    check_existing_shows(s, self.indexer)\r\n  File \"/app/medusa/medusa/helpers/externals.py\", line 131, in check_existing_shows\r\n    new_show_externals = get_externals(indexer=indexer, indexed_show=indexed_show)\r\n  File \"/app/medusa/medusa/helpers/externals.py\", line 105, in get_externals\r\n    new_show_externals.update(t.get_id_by_external(**new_show_externals))\r\n  File \"/app/medusa/medusa/indexers/tmdb/tmdb.py\", line 593, in get_id_by_external\r\n    result = self.tmdb.Find(kwargs.get(external_id)).info(**{'external_source': external_id})\r\n  File \"/app/medusa/lib/tmdbsimple/find.py\", line 50, in info\r\n    response = self._GET(path, kwargs)\r\n  File \"/app/medusa/lib/tmdbsimple/base.py\", line 87, in _GET\r\n    return self._request('GET', path, params=params)\r\n  File \"/app/medusa/lib/tmdbsimple/base.py\", line 80, in _request\r\n    headers=self.headers)\r\n  File \"/app/medusa/lib/requests/sessions.py\", line 475, in request\r\n    resp = self.send(prep, **send_kwargs)\r\n  File \"/app/medusa/lib/requests/sessions.py\", line 585, in send\r\n    r = adapter.send(request, **kwargs)\r\n  File \"/app/medusa/lib/cachecontrol/adapter.py\", line 46, in send\r\n    resp = super(CacheControlAdapter, self).send(request, **kw)\r\n  File \"/app/medusa/lib/requests/adapters.py\", line 477, in send\r\n    raise SSLError(e, request=request)\r\nSSLError: (\"bad handshake: SysCallError(-1, 'Unexpected EOF')\",)\r\n```\n", "before_files": [{"content": "# coding=utf-8\n\n\"\"\"Externals helper functions.\"\"\"\n\nimport logging\n\nfrom medusa import app, db\nfrom medusa.indexers.indexer_api import indexerApi\nfrom medusa.indexers.indexer_config import indexerConfig, mappings\nfrom medusa.indexers.indexer_exceptions import IndexerException, IndexerShowAllreadyInLibrary, IndexerUnavailable\nfrom medusa.logger.adapters.style import BraceAdapter\n\nfrom traktor import AuthException, TokenExpiredException, TraktApi, TraktException\n\nlog = BraceAdapter(logging.getLogger(__name__))\nlog.logger.addHandler(logging.NullHandler())\n\n\ndef get_trakt_externals(externals):\n    \"\"\"Small trakt api wrapper, to request trakt externals using multiple external id's.\n\n    :param externals: Dictionary of key/value pairs with external id's.\n    \"\"\"\n    def trakt_request(api, trakt_url):\n        \"\"\"Perform the request and handle possible token refresh.\"\"\"\n        try:\n            trakt_result = api.request(trakt_url) or []\n            if api.access_token_refreshed:\n                app.TRAKT_ACCESS_TOKEN = api.access_token\n                app.TRAKT_REFRESH_TOKEN = api.refresh_token\n                app.instance.save_config()\n        except (AuthException, TraktException, TokenExpiredException) as e:\n            log.info(u'Could not use Trakt to enrich with externals: {0}',\n                     e.message or e)\n            return []\n        else:\n            return trakt_result\n\n    trakt_settings = {'trakt_api_key': app.TRAKT_API_KEY,\n                      'trakt_api_secret': app.TRAKT_API_SECRET,\n                      'trakt_access_token': app.TRAKT_ACCESS_TOKEN,\n                      'trakt_refresh_token': app.TRAKT_REFRESH_TOKEN}\n    trakt_api = TraktApi(app.SSL_VERIFY, app.TRAKT_TIMEOUT, **trakt_settings)\n\n    id_lookup = '/search/{external_key}/{external_value}?type=show'\n    trakt_mapping = {'tvdb_id': 'tvdb', 'imdb_id': 'imdb', 'tmdb_id': 'tmdb', 'trakt_id': 'trakt'}\n    trakt_mapping_rev = {v: k for k, v in trakt_mapping.items()}\n\n    for external_key in externals:\n        if not trakt_mapping.get(external_key) or not externals[external_key]:\n            continue\n\n        url = id_lookup.format(external_key=trakt_mapping[external_key], external_value=externals[external_key])\n        log.debug(\n            u'Looking for externals using Trakt and {indexer} id {number}', {\n                'indexer': trakt_mapping[external_key],\n                'number': externals[external_key],\n            }\n        )\n        result = trakt_request(trakt_api, url)\n        if result and len(result) and result[0].get('show') and result[0]['show'].get('ids'):\n            ids = {trakt_mapping_rev[k]: v for k, v in result[0]['show'].get('ids').items()\n                   if v and trakt_mapping_rev.get(k)}\n            return ids\n    return {}\n\n\ndef get_externals(show=None, indexer=None, indexed_show=None):\n    \"\"\"Use as much as possible sources to map known id's.\n\n    Provide the external id's you have in a dictionary, and use as much available resources as possible to retrieve\n    external id's.\n    :param show: Series object.\n    :param indexer: Indexer id. For example 1 for tvdb or 4 for tmdb.\n    :param indexed_show: The result of a fully indexed shows. For example after an t['12345']\n    \"\"\"\n    if show:\n        indexer = show.indexer\n        new_show_externals = show.externals\n    else:\n        if not indexer or not indexed_show:\n            raise Exception('Need a minimum of a show object or an indexer + indexer_api '\n                            '(Show searched through indexerApi.')\n        new_show_externals = getattr(indexed_show, 'externals', {})\n\n    # For this show let's get all externals, and use them.\n    mappings = {indexer: indexerConfig[indexer]['mapped_to'] for indexer in indexerConfig}\n    other_indexers = [mapped_indexer for mapped_indexer in mappings if mapped_indexer != indexer]\n\n    # We for example want to add through tmdb, but the show is already added through tvdb.\n    # If tmdb doesn't have a mapping to imdb, but tvmaze does, there is a small chance we can use that.\n\n    for other_indexer in other_indexers:\n        lindexer_api_pararms = indexerApi(other_indexer).api_params.copy()\n        try:\n            t = indexerApi(other_indexer).indexer(**lindexer_api_pararms)\n        except IndexerUnavailable:\n            continue\n        if hasattr(t, 'get_id_by_external'):\n            log.debug(u\"Trying other indexer: {indexer} get_id_by_external\",\n                      {'indexer': indexerApi(other_indexer).name})\n            # Call the get_id_by_external and pass all the externals we have,\n            # except for the indexers own.\n            try:\n                new_show_externals.update(t.get_id_by_external(**new_show_externals))\n            except IndexerException as error:\n                log.warning(\n                    u'Error getting external ids for other'\n                    u' indexer {name}: {reason}',\n                    {'name': indexerApi(show.indexer).name, 'reason': error.message})\n\n    # Try to update with the Trakt externals.\n    if app.USE_TRAKT:\n        new_show_externals.update(get_trakt_externals(new_show_externals))\n\n    return new_show_externals\n\n\ndef check_existing_shows(indexed_show, indexer):\n    \"\"\"Check if the searched show already exists in the current library.\n\n    :param indexed_show: (Indexer Show object) The indexed show from -for example- tvdb. It might already have some\n    externals like imdb_id which can be used to search at tmdb, tvmaze or trakt.\n    :param indexer: (int) The indexer id, which has been used to search the indexed_show with.\n    :return: Raises the exception IndexerShowAllreadyInLibrary() when the show is already in your library.\n    \"\"\"\n    # For this show let's get all externals, and use them.\n    mappings = {indexer: indexerConfig[indexer]['mapped_to'] for indexer in indexerConfig}\n    other_indexers = [mapped_indexer for mapped_indexer in mappings if mapped_indexer != indexer]\n\n    new_show_externals = get_externals(indexer=indexer, indexed_show=indexed_show)\n\n    # Iterate through all shows in library, and see if one of our externals matches it's indexer_id\n    # Or one of it's externals.\n    for show in app.showList:\n\n        # Check if the new shows indexer id matches the external for the show\n        # in library\n        if show.externals.get(mappings[indexer]) and indexed_show['id'] == show.externals.get(mappings[indexer]):\n            log.debug(u'Show already in database. [{id}] {name}',\n                      {'name': show.name, 'id': indexed_show['id']})\n            raise IndexerShowAllreadyInLibrary('The show {0} has already been added by the indexer {1}. '\n                                               'Please remove the show, before you can add it through {2}.'\n                                               .format(show.name, indexerApi(show.indexer).name,\n                                                       indexerApi(indexer).name))\n\n        for new_show_external_key in new_show_externals.keys():\n            if show.indexer not in other_indexers:\n                continue\n\n            # Check if one of the new shows externals matches one of the\n            # externals for the show in library.\n            if not new_show_externals.get(new_show_external_key) or not show.externals.get(new_show_external_key):\n                continue\n\n            if new_show_externals.get(new_show_external_key) == show.externals.get(new_show_external_key):\n                log.debug(\n                    u'Show already in database under external ID ({existing})'\n                    u' for ({id}) {name}', {\n                        'name': show.name,\n                        'id': show.externals.get(new_show_external_key),\n                        'existing': new_show_external_key,\n                    }\n                )\n                raise IndexerShowAllreadyInLibrary('The show {0} has already been added by the indexer {1}. '\n                                                   'Please remove the show, before you can add it through {2}.'\n                                                   .format(show.name, indexerApi(show.indexer).name,\n                                                           indexerApi(indexer).name))\n\n\ndef load_externals_from_db(indexer=None, indexer_id=None):\n    \"\"\"Load and recreate the indexers external id's.\n\n    :param indexer: Optional pass indexer id, else use the current shows indexer.\n    :type indexer: int\n    :param indexer_id: Optional pass indexer id, else use the current shows indexer.\n    :type indexer_id: int\n    \"\"\"\n    externals = {}\n\n    main_db_con = db.DBConnection()\n    sql = (b'SELECT indexer, indexer_id, mindexer, mindexer_id '\n           b'FROM indexer_mapping '\n           b'WHERE (indexer = ? AND indexer_id = ?) '\n           b'OR (mindexer = ? AND mindexer_id = ?)')\n\n    results = main_db_con.select(sql, [indexer, indexer_id, indexer, indexer_id])\n\n    for result in results:\n        try:\n            if result[b'indexer'] == indexer:\n                externals[mappings[result[b'mindexer']]] = result[b'mindexer_id']\n            else:\n                externals[mappings[result[b'indexer']]] = result[b'indexer_id']\n        except KeyError as e:\n            log.error(u'Indexer not supported in current mappings: {id}', {'id': e.message})\n\n    return externals\n", "path": "medusa/helpers/externals.py"}], "after_files": [{"content": "# coding=utf-8\n\n\"\"\"Externals helper functions.\"\"\"\n\nimport logging\n\nfrom medusa import app, db\nfrom medusa.indexers.indexer_api import indexerApi\nfrom medusa.indexers.indexer_config import indexerConfig, mappings\nfrom medusa.indexers.indexer_exceptions import IndexerException, IndexerShowAllreadyInLibrary, IndexerUnavailable\nfrom medusa.logger.adapters.style import BraceAdapter\n\nfrom requests.exceptions import RequestException\nfrom traktor import AuthException, TokenExpiredException, TraktApi, TraktException\n\nlog = BraceAdapter(logging.getLogger(__name__))\nlog.logger.addHandler(logging.NullHandler())\n\n\ndef get_trakt_externals(externals):\n    \"\"\"Small trakt api wrapper, to request trakt externals using multiple external id's.\n\n    :param externals: Dictionary of key/value pairs with external id's.\n    \"\"\"\n    def trakt_request(api, trakt_url):\n        \"\"\"Perform the request and handle possible token refresh.\"\"\"\n        try:\n            trakt_result = api.request(trakt_url) or []\n            if api.access_token_refreshed:\n                app.TRAKT_ACCESS_TOKEN = api.access_token\n                app.TRAKT_REFRESH_TOKEN = api.refresh_token\n                app.instance.save_config()\n        except (AuthException, TraktException, TokenExpiredException) as e:\n            log.info(u'Could not use Trakt to enrich with externals: {0}',\n                     e.message or e)\n            return []\n        else:\n            return trakt_result\n\n    trakt_settings = {'trakt_api_key': app.TRAKT_API_KEY,\n                      'trakt_api_secret': app.TRAKT_API_SECRET,\n                      'trakt_access_token': app.TRAKT_ACCESS_TOKEN,\n                      'trakt_refresh_token': app.TRAKT_REFRESH_TOKEN}\n    trakt_api = TraktApi(app.SSL_VERIFY, app.TRAKT_TIMEOUT, **trakt_settings)\n\n    id_lookup = '/search/{external_key}/{external_value}?type=show'\n    trakt_mapping = {'tvdb_id': 'tvdb', 'imdb_id': 'imdb', 'tmdb_id': 'tmdb', 'trakt_id': 'trakt'}\n    trakt_mapping_rev = {v: k for k, v in trakt_mapping.items()}\n\n    for external_key in externals:\n        if not trakt_mapping.get(external_key) or not externals[external_key]:\n            continue\n\n        url = id_lookup.format(external_key=trakt_mapping[external_key], external_value=externals[external_key])\n        log.debug(\n            u'Looking for externals using Trakt and {indexer} id {number}', {\n                'indexer': trakt_mapping[external_key],\n                'number': externals[external_key],\n            }\n        )\n        result = trakt_request(trakt_api, url)\n        if result and len(result) and result[0].get('show') and result[0]['show'].get('ids'):\n            ids = {trakt_mapping_rev[k]: v for k, v in result[0]['show'].get('ids').items()\n                   if v and trakt_mapping_rev.get(k)}\n            return ids\n    return {}\n\n\ndef get_externals(show=None, indexer=None, indexed_show=None):\n    \"\"\"Use as much as possible sources to map known id's.\n\n    Provide the external id's you have in a dictionary, and use as much available resources as possible to retrieve\n    external id's.\n    :param show: Series object.\n    :param indexer: Indexer id. For example 1 for tvdb or 4 for tmdb.\n    :param indexed_show: The result of a fully indexed shows. For example after an t['12345']\n    \"\"\"\n    if show:\n        indexer = show.indexer\n        new_show_externals = show.externals\n    else:\n        if not indexer or not indexed_show:\n            raise Exception('Need a minimum of a show object or an indexer + indexer_api '\n                            '(Show searched through indexerApi.')\n        new_show_externals = getattr(indexed_show, 'externals', {})\n\n    # For this show let's get all externals, and use them.\n    mappings = {indexer: indexerConfig[indexer]['mapped_to'] for indexer in indexerConfig}\n    other_indexers = [mapped_indexer for mapped_indexer in mappings if mapped_indexer != indexer]\n\n    # We for example want to add through tmdb, but the show is already added through tvdb.\n    # If tmdb doesn't have a mapping to imdb, but tvmaze does, there is a small chance we can use that.\n\n    for other_indexer in other_indexers:\n        lindexer_api_pararms = indexerApi(other_indexer).api_params.copy()\n        try:\n            t = indexerApi(other_indexer).indexer(**lindexer_api_pararms)\n        except IndexerUnavailable:\n            continue\n        if hasattr(t, 'get_id_by_external'):\n            log.debug(u\"Trying other indexer: {indexer} get_id_by_external\",\n                      {'indexer': indexerApi(other_indexer).name})\n            # Call the get_id_by_external and pass all the externals we have,\n            # except for the indexers own.\n            try:\n                new_show_externals.update(t.get_id_by_external(**new_show_externals))\n            except (IndexerException, RequestException) as error:\n                log.warning(\n                    u'Error getting external ids for other'\n                    u' indexer {name}: {reason}',\n                    {'name': indexerApi(show.indexer).name, 'reason': error.message})\n\n    # Try to update with the Trakt externals.\n    if app.USE_TRAKT:\n        new_show_externals.update(get_trakt_externals(new_show_externals))\n\n    return new_show_externals\n\n\ndef check_existing_shows(indexed_show, indexer):\n    \"\"\"Check if the searched show already exists in the current library.\n\n    :param indexed_show: (Indexer Show object) The indexed show from -for example- tvdb. It might already have some\n    externals like imdb_id which can be used to search at tmdb, tvmaze or trakt.\n    :param indexer: (int) The indexer id, which has been used to search the indexed_show with.\n    :return: Raises the exception IndexerShowAllreadyInLibrary() when the show is already in your library.\n    \"\"\"\n    # For this show let's get all externals, and use them.\n    mappings = {indexer: indexerConfig[indexer]['mapped_to'] for indexer in indexerConfig}\n    other_indexers = [mapped_indexer for mapped_indexer in mappings if mapped_indexer != indexer]\n\n    new_show_externals = get_externals(indexer=indexer, indexed_show=indexed_show)\n\n    # Iterate through all shows in library, and see if one of our externals matches it's indexer_id\n    # Or one of it's externals.\n    for show in app.showList:\n\n        # Check if the new shows indexer id matches the external for the show\n        # in library\n        if show.externals.get(mappings[indexer]) and indexed_show['id'] == show.externals.get(mappings[indexer]):\n            log.debug(u'Show already in database. [{id}] {name}',\n                      {'name': show.name, 'id': indexed_show['id']})\n            raise IndexerShowAllreadyInLibrary('The show {0} has already been added by the indexer {1}. '\n                                               'Please remove the show, before you can add it through {2}.'\n                                               .format(show.name, indexerApi(show.indexer).name,\n                                                       indexerApi(indexer).name))\n\n        for new_show_external_key in new_show_externals.keys():\n            if show.indexer not in other_indexers:\n                continue\n\n            # Check if one of the new shows externals matches one of the\n            # externals for the show in library.\n            if not new_show_externals.get(new_show_external_key) or not show.externals.get(new_show_external_key):\n                continue\n\n            if new_show_externals.get(new_show_external_key) == show.externals.get(new_show_external_key):\n                log.debug(\n                    u'Show already in database under external ID ({existing})'\n                    u' for ({id}) {name}', {\n                        'name': show.name,\n                        'id': show.externals.get(new_show_external_key),\n                        'existing': new_show_external_key,\n                    }\n                )\n                raise IndexerShowAllreadyInLibrary('The show {0} has already been added by the indexer {1}. '\n                                                   'Please remove the show, before you can add it through {2}.'\n                                                   .format(show.name, indexerApi(show.indexer).name,\n                                                           indexerApi(indexer).name))\n\n\ndef load_externals_from_db(indexer=None, indexer_id=None):\n    \"\"\"Load and recreate the indexers external id's.\n\n    :param indexer: Optional pass indexer id, else use the current shows indexer.\n    :type indexer: int\n    :param indexer_id: Optional pass indexer id, else use the current shows indexer.\n    :type indexer_id: int\n    \"\"\"\n    externals = {}\n\n    main_db_con = db.DBConnection()\n    sql = (b'SELECT indexer, indexer_id, mindexer, mindexer_id '\n           b'FROM indexer_mapping '\n           b'WHERE (indexer = ? AND indexer_id = ?) '\n           b'OR (mindexer = ? AND mindexer_id = ?)')\n\n    results = main_db_con.select(sql, [indexer, indexer_id, indexer, indexer_id])\n\n    for result in results:\n        try:\n            if result[b'indexer'] == indexer:\n                externals[mappings[result[b'mindexer']]] = result[b'mindexer_id']\n            else:\n                externals[mappings[result[b'indexer']]] = result[b'indexer_id']\n        except KeyError as e:\n            log.error(u'Indexer not supported in current mappings: {id}', {'id': e.message})\n\n    return externals\n", "path": "medusa/helpers/externals.py"}]}