We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
No posters for trakt show recommendations
### Before submitting your issue:

Enable debug logging in Medusa settings, reproduce the error (be sure to disable after the bug is fixed)

**Branch/Commit:**
**OS:**
**What you did:**
**What happened:**
**What you expected:**
**Logs:**
```
2017-06-27 05:58:49 DEBUG    Thread-38 :: [b26de9c] Missing poster on TheTVDB, cause: WindowsError(3, 'The system cannot find the path specified')```


--- 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/show/recommendations/trakt.py`
Content:
```
1 # coding=utf-8
2 
3 from __future__ import unicode_literals
4 
5 import logging
6 import os
7 import time
8 
9 from medusa import app
10 from medusa.helper.common import try_int
11 from medusa.helper.exceptions import MultipleShowObjectsException
12 from medusa.indexers.indexer_api import indexerApi
13 from medusa.indexers.indexer_config import INDEXER_TVDBV2
14 from medusa.logger.adapters.style import BraceAdapter
15 from medusa.show.recommendations.recommended import RecommendedShow
16 
17 import requests
18 from simpleanidb import Anidb
19 from traktor import (TokenExpiredException, TraktApi, TraktException)
20 from tvdbapiv2.exceptions import ApiException
21 
22 log = BraceAdapter(logging.getLogger(__name__))
23 log.logger.addHandler(logging.NullHandler())
24 
25 
26 class MissingPosterList(list):
27     """Smart custom list, with a cache expiration.
28 
29     A list used to store the trakt shows that do not have a poster on tvdb. This will prevent searches for posters
30     that have recently been searched using the tvdb's api, and resulted in a 404.
31     """
32 
33     def __init__(self, items=None, cache_timeout=3600, implicit_clean=False):
34         """Initialize the MissingPosterList.
35 
36         :param items: Provide the initial list.
37         :param cache_timeout: Timeout after which the item expires.
38         :param implicit_clean: If enabled, run the clean() method, to check for expired items. Else you'll have to run
39         this periodically.
40         """
41         list.__init__(self, items or [])
42         self.cache_timeout = cache_timeout
43         self.implicit_clean = implicit_clean
44 
45     def append(self, item):
46         """Add new items to the list."""
47         if self.implicit_clean:
48             self.clean()
49         super(MissingPosterList, self).append((int(time.time()), item))
50 
51     def clean(self):
52         """Use the cache_timeout to remove expired items."""
53         new_list = [_ for _ in self if _[0] + self.cache_timeout > int(time.time())]
54         self.__init__(new_list, self.cache_timeout, self.implicit_clean)
55 
56     def has(self, value):
57         """Check if the value is in the list.
58 
59         We need a smarter method to check if an item is already in the list. This will return a list with items that
60         match the value.
61         :param value: The value to check for.
62         :return: A list of tuples with matches. For example: (141234234, '12342').
63         """
64         if self.implicit_clean:
65             self.clean()
66         return [_ for _ in self if _[1] == value]
67 
68 
69 missing_posters = MissingPosterList(cache_timeout=3600 * 24 * 3)  # Cache 3 days
70 
71 
72 class TraktPopular(object):
73     """This class retrieves a speficed recommended show list from Trakt.
74 
75     The list of returned shows is mapped to a RecommendedShow object
76     """
77 
78     def __init__(self):
79         """Initialize the trakt recommended list object."""
80         self.cache_subfolder = __name__.split('.')[-1] if '.' in __name__ else __name__
81         self.session = requests.Session()
82         self.recommender = "Trakt Popular"
83         self.default_img_src = 'trakt-default.png'
84         self.anidb = Anidb(cache_dir=app.CACHE_DIR)
85         self.tvdb_api_v2 = indexerApi(INDEXER_TVDBV2).indexer()
86 
87     def _create_recommended_show(self, show_obj):
88         """Create the RecommendedShow object from the returned showobj."""
89         rec_show = RecommendedShow(self,
90                                    show_obj['show']['ids'], show_obj['show']['title'],
91                                    INDEXER_TVDBV2,  # indexer
92                                    show_obj['show']['ids']['tvdb'],
93                                    **{'rating': show_obj['show']['rating'],
94                                       'votes': try_int(show_obj['show']['votes'], '0'),
95                                       'image_href': 'http://www.trakt.tv/shows/{0}'.format(show_obj['show']['ids']['slug']),
96                                       # Adds like: {'tmdb': 62126, 'tvdb': 304219, 'trakt': 79382, 'imdb': 'tt3322314',
97                                       # 'tvrage': None, 'slug': 'marvel-s-luke-cage'}
98                                       'ids': show_obj['show']['ids']
99                                       }
100                                    )
101 
102         use_default = None
103         image = None
104         try:
105             if not missing_posters.has(show_obj['show']['ids']['tvdb']):
106                 image = self.check_cache_for_poster(show_obj['show']['ids']['tvdb']) or \
107                     self.tvdb_api_v2.series_api.series_id_images_query_get(show_obj['show']['ids']['tvdb'], key_type='poster').data[0].file_name
108             else:
109                 log.info('CACHE: Missing poster on TVDB for show {0}', show_obj['show']['title'])
110                 use_default = self.default_img_src
111         except ApiException as error:
112             use_default = self.default_img_src
113             if getattr(error, 'status', None) == 404:
114                 log.info('Missing poster on TheTVDB for show {0}', show_obj['show']['title'])
115                 missing_posters.append(show_obj['show']['ids']['tvdb'])
116         except Exception as error:
117             use_default = self.default_img_src
118             log.debug('Missing poster on TheTVDB, cause: {0!r}', error)
119 
120         rec_show.cache_image('http://thetvdb.com/banners/{0}'.format(image), default=use_default)
121         # As the method below requires allot of resources, i've only enabled it when
122         # the shows language or country is 'jp' (japanese). Looks a litle bit akward,
123         # but alternative is allot of resource used
124         if 'jp' in [show_obj['show']['country'], show_obj['show']['language']]:
125             rec_show.check_if_anime(self.anidb, show_obj['show']['ids']['tvdb'])
126 
127         return rec_show
128 
129     @staticmethod
130     def fetch_and_refresh_token(trakt_api, path):
131         """Fetch shows from trakt and store the refresh token when needed."""
132         try:
133             library_shows = trakt_api.request(path) or []
134             if trakt_api.access_token_refreshed:
135                 app.TRAKT_ACCESS_TOKEN = trakt_api.access_token
136                 app.TRAKT_REFRESH_TOKEN = trakt_api.refresh_token
137                 app.instance.save_config()
138         except TokenExpiredException:
139             app.TRAKT_ACCESS_TOKEN = ''
140             raise
141 
142         return library_shows
143 
144     def fetch_popular_shows(self, page_url=None, trakt_list=None):  # pylint: disable=too-many-nested-blocks,too-many-branches
145         """Get a list of popular shows from different Trakt lists based on a provided trakt_list.
146 
147         :param page_url: the page url opened to the base api url, for retreiving a specific list
148         :param trakt_list: a description of the trakt list
149         :return: A list of RecommendedShow objects, an empty list of none returned
150         :throw: ``Exception`` if an Exception is thrown not handled by the libtrats exceptions
151         """
152         trending_shows = []
153         removed_from_medusa = []
154 
155         # Create a trakt settings dict
156         trakt_settings = {'trakt_api_secret': app.TRAKT_API_SECRET,
157                           'trakt_api_key': app.TRAKT_API_KEY,
158                           'trakt_access_token': app.TRAKT_ACCESS_TOKEN,
159                           'trakt_refresh_token': app.TRAKT_REFRESH_TOKEN}
160 
161         trakt_api = TraktApi(timeout=app.TRAKT_TIMEOUT, ssl_verify=app.SSL_VERIFY, **trakt_settings)
162 
163         try:  # pylint: disable=too-many-nested-blocks
164             not_liked_show = ''
165             if app.TRAKT_ACCESS_TOKEN != '':
166                 library_shows = self.fetch_and_refresh_token(trakt_api, 'sync/watched/shows?extended=noseasons') + \
167                     self.fetch_and_refresh_token(trakt_api, 'sync/collection/shows?extended=full')
168 
169                 medusa_shows = [show.indexerid for show in app.showList if show.indexerid]
170                 removed_from_medusa = [lshow['show']['ids']['tvdb'] for lshow in library_shows if lshow['show']['ids']['tvdb'] not in medusa_shows]
171 
172                 if app.TRAKT_BLACKLIST_NAME is not None and app.TRAKT_BLACKLIST_NAME:
173                     not_liked_show = trakt_api.request('users/' + app.TRAKT_USERNAME + '/lists/' +
174                                                        app.TRAKT_BLACKLIST_NAME + '/items') or []
175                 else:
176                     log.debug('Trakt blacklist name is empty')
177 
178             if trakt_list not in ['recommended', 'newshow', 'newseason']:
179                 limit_show = '?limit=' + str(100 + len(not_liked_show)) + '&'
180             else:
181                 limit_show = '?'
182 
183             shows = self.fetch_and_refresh_token(trakt_api, page_url + limit_show + 'extended=full,images') or []
184 
185             # Let's trigger a cache cleanup.
186             missing_posters.clean()
187 
188             for show in shows:
189                 try:
190                     if 'show' not in show:
191                         show['show'] = show
192 
193                     if not_liked_show:
194                         if show['show']['ids']['tvdb'] not in (show['show']['ids']['tvdb']
195                                                                for show in not_liked_show if show['type'] == 'show'):
196                             trending_shows.append(self._create_recommended_show(show))
197                     else:
198                         trending_shows.append(self._create_recommended_show(show))
199 
200                 except MultipleShowObjectsException:
201                     continue
202 
203             blacklist = app.TRAKT_BLACKLIST_NAME not in ''
204 
205         except TraktException as error:
206             log.warning('Could not connect to Trakt service: {0}', error)
207             raise
208 
209         return blacklist, trending_shows, removed_from_medusa
210 
211     def check_cache_for_poster(self, tvdb_id):
212         """Verify if we already have a poster downloaded for this show."""
213         for image_file_name in os.listdir(os.path.abspath(os.path.join(app.CACHE_DIR, 'images', self.cache_subfolder))):
214             if os.path.isfile(os.path.abspath(os.path.join(app.CACHE_DIR, 'images', self.cache_subfolder, image_file_name))):
215                 if str(tvdb_id) == image_file_name.split('-')[0]:
216                     return image_file_name
217         return False
218
```
--- 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/show/recommendations/trakt.py b/medusa/show/recommendations/trakt.py
--- a/medusa/show/recommendations/trakt.py
+++ b/medusa/show/recommendations/trakt.py
@@ -104,7 +104,8 @@
         try:
             if not missing_posters.has(show_obj['show']['ids']['tvdb']):
                 image = self.check_cache_for_poster(show_obj['show']['ids']['tvdb']) or \
-                    self.tvdb_api_v2.series_api.series_id_images_query_get(show_obj['show']['ids']['tvdb'], key_type='poster').data[0].file_name
+                    self.tvdb_api_v2.config['session'].series_api.series_id_images_query_get(show_obj['show']['ids']['tvdb'],
+                                                                                             key_type='poster').data[0].file_name
             else:
                 log.info('CACHE: Missing poster on TVDB for show {0}', show_obj['show']['title'])
                 use_default = self.default_img_src
@@ -117,7 +118,11 @@
             use_default = self.default_img_src
             log.debug('Missing poster on TheTVDB, cause: {0!r}', error)
 
-        rec_show.cache_image('http://thetvdb.com/banners/{0}'.format(image), default=use_default)
+        if image:
+            rec_show.cache_image('http://thetvdb.com/banners/{0}'.format(image), default=use_default)
+        else:
+            rec_show.cache_image('', default=use_default)
+
         # As the method below requires allot of resources, i've only enabled it when
         # the shows language or country is 'jp' (japanese). Looks a litle bit akward,
         # but alternative is allot of resource used
@@ -210,6 +215,9 @@
 
     def check_cache_for_poster(self, tvdb_id):
         """Verify if we already have a poster downloaded for this show."""
+        if not os.path.exists(os.path.join(app.CACHE_DIR, 'images', self.cache_subfolder)):
+            os.makedirs(os.path.join(app.CACHE_DIR, 'images', self.cache_subfolder))
+
         for image_file_name in os.listdir(os.path.abspath(os.path.join(app.CACHE_DIR, 'images', self.cache_subfolder))):
             if os.path.isfile(os.path.abspath(os.path.join(app.CACHE_DIR, 'images', self.cache_subfolder, image_file_name))):
                 if str(tvdb_id) == image_file_name.split('-')[0]:

{"golden_diff": "diff --git a/medusa/show/recommendations/trakt.py b/medusa/show/recommendations/trakt.py\n--- a/medusa/show/recommendations/trakt.py\n+++ b/medusa/show/recommendations/trakt.py\n@@ -104,7 +104,8 @@\n         try:\n             if not missing_posters.has(show_obj['show']['ids']['tvdb']):\n                 image = self.check_cache_for_poster(show_obj['show']['ids']['tvdb']) or \\\n-                    self.tvdb_api_v2.series_api.series_id_images_query_get(show_obj['show']['ids']['tvdb'], key_type='poster').data[0].file_name\n+                    self.tvdb_api_v2.config['session'].series_api.series_id_images_query_get(show_obj['show']['ids']['tvdb'],\n+                                                                                             key_type='poster').data[0].file_name\n             else:\n                 log.info('CACHE: Missing poster on TVDB for show {0}', show_obj['show']['title'])\n                 use_default = self.default_img_src\n@@ -117,7 +118,11 @@\n             use_default = self.default_img_src\n             log.debug('Missing poster on TheTVDB, cause: {0!r}', error)\n \n-        rec_show.cache_image('http://thetvdb.com/banners/{0}'.format(image), default=use_default)\n+        if image:\n+            rec_show.cache_image('http://thetvdb.com/banners/{0}'.format(image), default=use_default)\n+        else:\n+            rec_show.cache_image('', default=use_default)\n+\n         # As the method below requires allot of resources, i've only enabled it when\n         # the shows language or country is 'jp' (japanese). Looks a litle bit akward,\n         # but alternative is allot of resource used\n@@ -210,6 +215,9 @@\n \n     def check_cache_for_poster(self, tvdb_id):\n         \"\"\"Verify if we already have a poster downloaded for this show.\"\"\"\n+        if not os.path.exists(os.path.join(app.CACHE_DIR, 'images', self.cache_subfolder)):\n+            os.makedirs(os.path.join(app.CACHE_DIR, 'images', self.cache_subfolder))\n+\n         for image_file_name in os.listdir(os.path.abspath(os.path.join(app.CACHE_DIR, 'images', self.cache_subfolder))):\n             if os.path.isfile(os.path.abspath(os.path.join(app.CACHE_DIR, 'images', self.cache_subfolder, image_file_name))):\n                 if str(tvdb_id) == image_file_name.split('-')[0]:\n", "issue": "No posters for trakt show recommendations\n### Before submitting your issue:\r\n\r\nEnable debug logging in Medusa settings, reproduce the error (be sure to disable after the bug is fixed)\r\n\r\n**Branch/Commit:**\r\n**OS:**\r\n**What you did:**\r\n**What happened:**\r\n**What you expected:**\r\n**Logs:**\r\n```\r\n2017-06-27 05:58:49 DEBUG    Thread-38 :: [b26de9c] Missing poster on TheTVDB, cause: WindowsError(3, 'The system cannot find the path specified')```\r\n\n", "before_files": [{"content": "# coding=utf-8\n\nfrom __future__ import unicode_literals\n\nimport logging\nimport os\nimport time\n\nfrom medusa import app\nfrom medusa.helper.common import try_int\nfrom medusa.helper.exceptions import MultipleShowObjectsException\nfrom medusa.indexers.indexer_api import indexerApi\nfrom medusa.indexers.indexer_config import INDEXER_TVDBV2\nfrom medusa.logger.adapters.style import BraceAdapter\nfrom medusa.show.recommendations.recommended import RecommendedShow\n\nimport requests\nfrom simpleanidb import Anidb\nfrom traktor import (TokenExpiredException, TraktApi, TraktException)\nfrom tvdbapiv2.exceptions import ApiException\n\nlog = BraceAdapter(logging.getLogger(__name__))\nlog.logger.addHandler(logging.NullHandler())\n\n\nclass MissingPosterList(list):\n    \"\"\"Smart custom list, with a cache expiration.\n\n    A list used to store the trakt shows that do not have a poster on tvdb. This will prevent searches for posters\n    that have recently been searched using the tvdb's api, and resulted in a 404.\n    \"\"\"\n\n    def __init__(self, items=None, cache_timeout=3600, implicit_clean=False):\n        \"\"\"Initialize the MissingPosterList.\n\n        :param items: Provide the initial list.\n        :param cache_timeout: Timeout after which the item expires.\n        :param implicit_clean: If enabled, run the clean() method, to check for expired items. Else you'll have to run\n        this periodically.\n        \"\"\"\n        list.__init__(self, items or [])\n        self.cache_timeout = cache_timeout\n        self.implicit_clean = implicit_clean\n\n    def append(self, item):\n        \"\"\"Add new items to the list.\"\"\"\n        if self.implicit_clean:\n            self.clean()\n        super(MissingPosterList, self).append((int(time.time()), item))\n\n    def clean(self):\n        \"\"\"Use the cache_timeout to remove expired items.\"\"\"\n        new_list = [_ for _ in self if _[0] + self.cache_timeout > int(time.time())]\n        self.__init__(new_list, self.cache_timeout, self.implicit_clean)\n\n    def has(self, value):\n        \"\"\"Check if the value is in the list.\n\n        We need a smarter method to check if an item is already in the list. This will return a list with items that\n        match the value.\n        :param value: The value to check for.\n        :return: A list of tuples with matches. For example: (141234234, '12342').\n        \"\"\"\n        if self.implicit_clean:\n            self.clean()\n        return [_ for _ in self if _[1] == value]\n\n\nmissing_posters = MissingPosterList(cache_timeout=3600 * 24 * 3)  # Cache 3 days\n\n\nclass TraktPopular(object):\n    \"\"\"This class retrieves a speficed recommended show list from Trakt.\n\n    The list of returned shows is mapped to a RecommendedShow object\n    \"\"\"\n\n    def __init__(self):\n        \"\"\"Initialize the trakt recommended list object.\"\"\"\n        self.cache_subfolder = __name__.split('.')[-1] if '.' in __name__ else __name__\n        self.session = requests.Session()\n        self.recommender = \"Trakt Popular\"\n        self.default_img_src = 'trakt-default.png'\n        self.anidb = Anidb(cache_dir=app.CACHE_DIR)\n        self.tvdb_api_v2 = indexerApi(INDEXER_TVDBV2).indexer()\n\n    def _create_recommended_show(self, show_obj):\n        \"\"\"Create the RecommendedShow object from the returned showobj.\"\"\"\n        rec_show = RecommendedShow(self,\n                                   show_obj['show']['ids'], show_obj['show']['title'],\n                                   INDEXER_TVDBV2,  # indexer\n                                   show_obj['show']['ids']['tvdb'],\n                                   **{'rating': show_obj['show']['rating'],\n                                      'votes': try_int(show_obj['show']['votes'], '0'),\n                                      'image_href': 'http://www.trakt.tv/shows/{0}'.format(show_obj['show']['ids']['slug']),\n                                      # Adds like: {'tmdb': 62126, 'tvdb': 304219, 'trakt': 79382, 'imdb': 'tt3322314',\n                                      # 'tvrage': None, 'slug': 'marvel-s-luke-cage'}\n                                      'ids': show_obj['show']['ids']\n                                      }\n                                   )\n\n        use_default = None\n        image = None\n        try:\n            if not missing_posters.has(show_obj['show']['ids']['tvdb']):\n                image = self.check_cache_for_poster(show_obj['show']['ids']['tvdb']) or \\\n                    self.tvdb_api_v2.series_api.series_id_images_query_get(show_obj['show']['ids']['tvdb'], key_type='poster').data[0].file_name\n            else:\n                log.info('CACHE: Missing poster on TVDB for show {0}', show_obj['show']['title'])\n                use_default = self.default_img_src\n        except ApiException as error:\n            use_default = self.default_img_src\n            if getattr(error, 'status', None) == 404:\n                log.info('Missing poster on TheTVDB for show {0}', show_obj['show']['title'])\n                missing_posters.append(show_obj['show']['ids']['tvdb'])\n        except Exception as error:\n            use_default = self.default_img_src\n            log.debug('Missing poster on TheTVDB, cause: {0!r}', error)\n\n        rec_show.cache_image('http://thetvdb.com/banners/{0}'.format(image), default=use_default)\n        # As the method below requires allot of resources, i've only enabled it when\n        # the shows language or country is 'jp' (japanese). Looks a litle bit akward,\n        # but alternative is allot of resource used\n        if 'jp' in [show_obj['show']['country'], show_obj['show']['language']]:\n            rec_show.check_if_anime(self.anidb, show_obj['show']['ids']['tvdb'])\n\n        return rec_show\n\n    @staticmethod\n    def fetch_and_refresh_token(trakt_api, path):\n        \"\"\"Fetch shows from trakt and store the refresh token when needed.\"\"\"\n        try:\n            library_shows = trakt_api.request(path) or []\n            if trakt_api.access_token_refreshed:\n                app.TRAKT_ACCESS_TOKEN = trakt_api.access_token\n                app.TRAKT_REFRESH_TOKEN = trakt_api.refresh_token\n                app.instance.save_config()\n        except TokenExpiredException:\n            app.TRAKT_ACCESS_TOKEN = ''\n            raise\n\n        return library_shows\n\n    def fetch_popular_shows(self, page_url=None, trakt_list=None):  # pylint: disable=too-many-nested-blocks,too-many-branches\n        \"\"\"Get a list of popular shows from different Trakt lists based on a provided trakt_list.\n\n        :param page_url: the page url opened to the base api url, for retreiving a specific list\n        :param trakt_list: a description of the trakt list\n        :return: A list of RecommendedShow objects, an empty list of none returned\n        :throw: ``Exception`` if an Exception is thrown not handled by the libtrats exceptions\n        \"\"\"\n        trending_shows = []\n        removed_from_medusa = []\n\n        # Create a trakt settings dict\n        trakt_settings = {'trakt_api_secret': app.TRAKT_API_SECRET,\n                          'trakt_api_key': app.TRAKT_API_KEY,\n                          'trakt_access_token': app.TRAKT_ACCESS_TOKEN,\n                          'trakt_refresh_token': app.TRAKT_REFRESH_TOKEN}\n\n        trakt_api = TraktApi(timeout=app.TRAKT_TIMEOUT, ssl_verify=app.SSL_VERIFY, **trakt_settings)\n\n        try:  # pylint: disable=too-many-nested-blocks\n            not_liked_show = ''\n            if app.TRAKT_ACCESS_TOKEN != '':\n                library_shows = self.fetch_and_refresh_token(trakt_api, 'sync/watched/shows?extended=noseasons') + \\\n                    self.fetch_and_refresh_token(trakt_api, 'sync/collection/shows?extended=full')\n\n                medusa_shows = [show.indexerid for show in app.showList if show.indexerid]\n                removed_from_medusa = [lshow['show']['ids']['tvdb'] for lshow in library_shows if lshow['show']['ids']['tvdb'] not in medusa_shows]\n\n                if app.TRAKT_BLACKLIST_NAME is not None and app.TRAKT_BLACKLIST_NAME:\n                    not_liked_show = trakt_api.request('users/' + app.TRAKT_USERNAME + '/lists/' +\n                                                       app.TRAKT_BLACKLIST_NAME + '/items') or []\n                else:\n                    log.debug('Trakt blacklist name is empty')\n\n            if trakt_list not in ['recommended', 'newshow', 'newseason']:\n                limit_show = '?limit=' + str(100 + len(not_liked_show)) + '&'\n            else:\n                limit_show = '?'\n\n            shows = self.fetch_and_refresh_token(trakt_api, page_url + limit_show + 'extended=full,images') or []\n\n            # Let's trigger a cache cleanup.\n            missing_posters.clean()\n\n            for show in shows:\n                try:\n                    if 'show' not in show:\n                        show['show'] = show\n\n                    if not_liked_show:\n                        if show['show']['ids']['tvdb'] not in (show['show']['ids']['tvdb']\n                                                               for show in not_liked_show if show['type'] == 'show'):\n                            trending_shows.append(self._create_recommended_show(show))\n                    else:\n                        trending_shows.append(self._create_recommended_show(show))\n\n                except MultipleShowObjectsException:\n                    continue\n\n            blacklist = app.TRAKT_BLACKLIST_NAME not in ''\n\n        except TraktException as error:\n            log.warning('Could not connect to Trakt service: {0}', error)\n            raise\n\n        return blacklist, trending_shows, removed_from_medusa\n\n    def check_cache_for_poster(self, tvdb_id):\n        \"\"\"Verify if we already have a poster downloaded for this show.\"\"\"\n        for image_file_name in os.listdir(os.path.abspath(os.path.join(app.CACHE_DIR, 'images', self.cache_subfolder))):\n            if os.path.isfile(os.path.abspath(os.path.join(app.CACHE_DIR, 'images', self.cache_subfolder, image_file_name))):\n                if str(tvdb_id) == image_file_name.split('-')[0]:\n                    return image_file_name\n        return False\n", "path": "medusa/show/recommendations/trakt.py"}], "after_files": [{"content": "# coding=utf-8\n\nfrom __future__ import unicode_literals\n\nimport logging\nimport os\nimport time\n\nfrom medusa import app\nfrom medusa.helper.common import try_int\nfrom medusa.helper.exceptions import MultipleShowObjectsException\nfrom medusa.indexers.indexer_api import indexerApi\nfrom medusa.indexers.indexer_config import INDEXER_TVDBV2\nfrom medusa.logger.adapters.style import BraceAdapter\nfrom medusa.show.recommendations.recommended import RecommendedShow\n\nimport requests\nfrom simpleanidb import Anidb\nfrom traktor import (TokenExpiredException, TraktApi, TraktException)\nfrom tvdbapiv2.exceptions import ApiException\n\nlog = BraceAdapter(logging.getLogger(__name__))\nlog.logger.addHandler(logging.NullHandler())\n\n\nclass MissingPosterList(list):\n    \"\"\"Smart custom list, with a cache expiration.\n\n    A list used to store the trakt shows that do not have a poster on tvdb. This will prevent searches for posters\n    that have recently been searched using the tvdb's api, and resulted in a 404.\n    \"\"\"\n\n    def __init__(self, items=None, cache_timeout=3600, implicit_clean=False):\n        \"\"\"Initialize the MissingPosterList.\n\n        :param items: Provide the initial list.\n        :param cache_timeout: Timeout after which the item expires.\n        :param implicit_clean: If enabled, run the clean() method, to check for expired items. Else you'll have to run\n        this periodically.\n        \"\"\"\n        list.__init__(self, items or [])\n        self.cache_timeout = cache_timeout\n        self.implicit_clean = implicit_clean\n\n    def append(self, item):\n        \"\"\"Add new items to the list.\"\"\"\n        if self.implicit_clean:\n            self.clean()\n        super(MissingPosterList, self).append((int(time.time()), item))\n\n    def clean(self):\n        \"\"\"Use the cache_timeout to remove expired items.\"\"\"\n        new_list = [_ for _ in self if _[0] + self.cache_timeout > int(time.time())]\n        self.__init__(new_list, self.cache_timeout, self.implicit_clean)\n\n    def has(self, value):\n        \"\"\"Check if the value is in the list.\n\n        We need a smarter method to check if an item is already in the list. This will return a list with items that\n        match the value.\n        :param value: The value to check for.\n        :return: A list of tuples with matches. For example: (141234234, '12342').\n        \"\"\"\n        if self.implicit_clean:\n            self.clean()\n        return [_ for _ in self if _[1] == value]\n\n\nmissing_posters = MissingPosterList(cache_timeout=3600 * 24 * 3)  # Cache 3 days\n\n\nclass TraktPopular(object):\n    \"\"\"This class retrieves a speficed recommended show list from Trakt.\n\n    The list of returned shows is mapped to a RecommendedShow object\n    \"\"\"\n\n    def __init__(self):\n        \"\"\"Initialize the trakt recommended list object.\"\"\"\n        self.cache_subfolder = __name__.split('.')[-1] if '.' in __name__ else __name__\n        self.session = requests.Session()\n        self.recommender = \"Trakt Popular\"\n        self.default_img_src = 'trakt-default.png'\n        self.anidb = Anidb(cache_dir=app.CACHE_DIR)\n        self.tvdb_api_v2 = indexerApi(INDEXER_TVDBV2).indexer()\n\n    def _create_recommended_show(self, show_obj):\n        \"\"\"Create the RecommendedShow object from the returned showobj.\"\"\"\n        rec_show = RecommendedShow(self,\n                                   show_obj['show']['ids'], show_obj['show']['title'],\n                                   INDEXER_TVDBV2,  # indexer\n                                   show_obj['show']['ids']['tvdb'],\n                                   **{'rating': show_obj['show']['rating'],\n                                      'votes': try_int(show_obj['show']['votes'], '0'),\n                                      'image_href': 'http://www.trakt.tv/shows/{0}'.format(show_obj['show']['ids']['slug']),\n                                      # Adds like: {'tmdb': 62126, 'tvdb': 304219, 'trakt': 79382, 'imdb': 'tt3322314',\n                                      # 'tvrage': None, 'slug': 'marvel-s-luke-cage'}\n                                      'ids': show_obj['show']['ids']\n                                      }\n                                   )\n\n        use_default = None\n        image = None\n        try:\n            if not missing_posters.has(show_obj['show']['ids']['tvdb']):\n                image = self.check_cache_for_poster(show_obj['show']['ids']['tvdb']) or \\\n                    self.tvdb_api_v2.config['session'].series_api.series_id_images_query_get(show_obj['show']['ids']['tvdb'],\n                                                                                             key_type='poster').data[0].file_name\n            else:\n                log.info('CACHE: Missing poster on TVDB for show {0}', show_obj['show']['title'])\n                use_default = self.default_img_src\n        except ApiException as error:\n            use_default = self.default_img_src\n            if getattr(error, 'status', None) == 404:\n                log.info('Missing poster on TheTVDB for show {0}', show_obj['show']['title'])\n                missing_posters.append(show_obj['show']['ids']['tvdb'])\n        except Exception as error:\n            use_default = self.default_img_src\n            log.debug('Missing poster on TheTVDB, cause: {0!r}', error)\n\n        if image:\n            rec_show.cache_image('http://thetvdb.com/banners/{0}'.format(image), default=use_default)\n        else:\n            rec_show.cache_image('', default=use_default)\n\n        # As the method below requires allot of resources, i've only enabled it when\n        # the shows language or country is 'jp' (japanese). Looks a litle bit akward,\n        # but alternative is allot of resource used\n        if 'jp' in [show_obj['show']['country'], show_obj['show']['language']]:\n            rec_show.check_if_anime(self.anidb, show_obj['show']['ids']['tvdb'])\n\n        return rec_show\n\n    @staticmethod\n    def fetch_and_refresh_token(trakt_api, path):\n        \"\"\"Fetch shows from trakt and store the refresh token when needed.\"\"\"\n        try:\n            library_shows = trakt_api.request(path) or []\n            if trakt_api.access_token_refreshed:\n                app.TRAKT_ACCESS_TOKEN = trakt_api.access_token\n                app.TRAKT_REFRESH_TOKEN = trakt_api.refresh_token\n                app.instance.save_config()\n        except TokenExpiredException:\n            app.TRAKT_ACCESS_TOKEN = ''\n            raise\n\n        return library_shows\n\n    def fetch_popular_shows(self, page_url=None, trakt_list=None):  # pylint: disable=too-many-nested-blocks,too-many-branches\n        \"\"\"Get a list of popular shows from different Trakt lists based on a provided trakt_list.\n\n        :param page_url: the page url opened to the base api url, for retreiving a specific list\n        :param trakt_list: a description of the trakt list\n        :return: A list of RecommendedShow objects, an empty list of none returned\n        :throw: ``Exception`` if an Exception is thrown not handled by the libtrats exceptions\n        \"\"\"\n        trending_shows = []\n        removed_from_medusa = []\n\n        # Create a trakt settings dict\n        trakt_settings = {'trakt_api_secret': app.TRAKT_API_SECRET,\n                          'trakt_api_key': app.TRAKT_API_KEY,\n                          'trakt_access_token': app.TRAKT_ACCESS_TOKEN,\n                          'trakt_refresh_token': app.TRAKT_REFRESH_TOKEN}\n\n        trakt_api = TraktApi(timeout=app.TRAKT_TIMEOUT, ssl_verify=app.SSL_VERIFY, **trakt_settings)\n\n        try:  # pylint: disable=too-many-nested-blocks\n            not_liked_show = ''\n            if app.TRAKT_ACCESS_TOKEN != '':\n                library_shows = self.fetch_and_refresh_token(trakt_api, 'sync/watched/shows?extended=noseasons') + \\\n                    self.fetch_and_refresh_token(trakt_api, 'sync/collection/shows?extended=full')\n\n                medusa_shows = [show.indexerid for show in app.showList if show.indexerid]\n                removed_from_medusa = [lshow['show']['ids']['tvdb'] for lshow in library_shows if lshow['show']['ids']['tvdb'] not in medusa_shows]\n\n                if app.TRAKT_BLACKLIST_NAME is not None and app.TRAKT_BLACKLIST_NAME:\n                    not_liked_show = trakt_api.request('users/' + app.TRAKT_USERNAME + '/lists/' +\n                                                       app.TRAKT_BLACKLIST_NAME + '/items') or []\n                else:\n                    log.debug('Trakt blacklist name is empty')\n\n            if trakt_list not in ['recommended', 'newshow', 'newseason']:\n                limit_show = '?limit=' + str(100 + len(not_liked_show)) + '&'\n            else:\n                limit_show = '?'\n\n            shows = self.fetch_and_refresh_token(trakt_api, page_url + limit_show + 'extended=full,images') or []\n\n            # Let's trigger a cache cleanup.\n            missing_posters.clean()\n\n            for show in shows:\n                try:\n                    if 'show' not in show:\n                        show['show'] = show\n\n                    if not_liked_show:\n                        if show['show']['ids']['tvdb'] not in (show['show']['ids']['tvdb']\n                                                               for show in not_liked_show if show['type'] == 'show'):\n                            trending_shows.append(self._create_recommended_show(show))\n                    else:\n                        trending_shows.append(self._create_recommended_show(show))\n\n                except MultipleShowObjectsException:\n                    continue\n\n            blacklist = app.TRAKT_BLACKLIST_NAME not in ''\n\n        except TraktException as error:\n            log.warning('Could not connect to Trakt service: {0}', error)\n            raise\n\n        return blacklist, trending_shows, removed_from_medusa\n\n    def check_cache_for_poster(self, tvdb_id):\n        \"\"\"Verify if we already have a poster downloaded for this show.\"\"\"\n        if not os.path.exists(os.path.join(app.CACHE_DIR, 'images', self.cache_subfolder)):\n            os.makedirs(os.path.join(app.CACHE_DIR, 'images', self.cache_subfolder))\n\n        for image_file_name in os.listdir(os.path.abspath(os.path.join(app.CACHE_DIR, 'images', self.cache_subfolder))):\n            if os.path.isfile(os.path.abspath(os.path.join(app.CACHE_DIR, 'images', self.cache_subfolder, image_file_name))):\n                if str(tvdb_id) == image_file_name.split('-')[0]:\n                    return image_file_name\n        return False\n", "path": "medusa/show/recommendations/trakt.py"}]}