Repository: pymedusa/Medusa

Issue:
[APP SUBMITTED]: TypeError: normalize() argument 2 must be str, not None

### INFO
**Python Version**: `3.7.3 (default, Jul 25 2020, 13:03:44) [GCC 8.3.0]`
**Operating System**: `Linux-5.4.83-v7l+-armv7l-with-debian-10.7`
**Locale**: `UTF-8`
**Branch**: [master](../tree/master)
**Database**: `44.17`
**Commit**: pymedusa/Medusa@698ca1db9903a9f716d9ed1910972cbf70ea50e8
**Link to Log**: https://gist.github.com/0cee566bd5ceb2f70fd08f1be14bfa7d
### ERROR
<pre>
2021-01-12 20:29:41 ERROR    SNATCHQUEUE-SNATCH-375921 :: [698ca1d] Snatch failed! For result: Lupin.S01E01.Chapter.1.720p.NF.WEB-DL.DDP5.1.x264-3cTWeB
Traceback (most recent call last):
  File "/opt/medusa/<a href="../blob/698ca1db9903a9f716d9ed1910972cbf70ea50e8/medusa/search/queue.py#L516">medusa/search/queue.py</a>", line 516, in run
    self.success = snatch_episode(result)
  File "/opt/medusa/<a href="../blob/698ca1db9903a9f716d9ed1910972cbf70ea50e8/medusa/search/core.py#L235">medusa/search/core.py</a>", line 235, in snatch_episode
    notifiers.trakt_notifier.add_episode_to_watchlist(episode)
  File "/opt/medusa/<a href="../blob/698ca1db9903a9f716d9ed1910972cbf70ea50e8/medusa/notifiers/trakt.py#L141">medusa/notifiers/trakt.py</a>", line 141, in add_episode_to_watchlist
    tv_show = tv.TVShow(show_id)
  File "/opt/medusa/<a href="../blob/698ca1db9903a9f716d9ed1910972cbf70ea50e8/ext/trakt/tv.py#L211">ext/trakt/tv.py</a>", line 211, in __init__
    self.slug = slug or slugify(self.title)
  File "/opt/medusa/<a href="../blob/698ca1db9903a9f716d9ed1910972cbf70ea50e8/ext/trakt/utils.py#L21">ext/trakt/utils.py</a>", line 21, in slugify
    nfkd_form = unicodedata.normalize('NFKD', value)
TypeError: normalize() argument 2 must be str, not None
</pre>
---
_STAFF NOTIFIED_: @pymedusa/support @pymedusa/moderators



Discussion:

Resolving patch:
diff --git a/ext/trakt/core.py b/ext/trakt/core.py
--- a/ext/trakt/core.py
+++ b/ext/trakt/core.py
@@ -413,6 +413,30 @@ def _refresh_token(s):
         raise s.error_map[response.status_code]()
 
 
+def load_config():
+    """Manually load config from json config file."""
+    global CLIENT_ID, CLIENT_SECRET, OAUTH_TOKEN, OAUTH_EXPIRES_AT
+    global OAUTH_REFRESH, APPLICATION_ID, CONFIG_PATH
+    if (CLIENT_ID is None or CLIENT_SECRET is None) and \
+            os.path.exists(CONFIG_PATH):
+        # Load in trakt API auth data from CONFIG_PATH
+        with open(CONFIG_PATH) as config_file:
+            config_data = json.load(config_file)
+
+        if CLIENT_ID is None:
+            CLIENT_ID = config_data.get('CLIENT_ID', None)
+        if CLIENT_SECRET is None:
+            CLIENT_SECRET = config_data.get('CLIENT_SECRET', None)
+        if OAUTH_TOKEN is None:
+            OAUTH_TOKEN = config_data.get('OAUTH_TOKEN', None)
+        if OAUTH_EXPIRES_AT is None:
+            OAUTH_EXPIRES_AT = config_data.get('OAUTH_EXPIRES_AT', None)
+        if OAUTH_REFRESH is None:
+            OAUTH_REFRESH = config_data.get('OAUTH_REFRESH', None)
+        if APPLICATION_ID is None:
+            APPLICATION_ID = config_data.get('APPLICATION_ID', None)
+
+
 def _bootstrapped(f):
     """Bootstrap your authentication environment when authentication is needed
     and if a file at `CONFIG_PATH` exists. The process is completed by setting
@@ -420,34 +444,18 @@ def _bootstrapped(f):
     """
     @wraps(f)
     def inner(*args, **kwargs):
-        global CLIENT_ID, CLIENT_SECRET, OAUTH_TOKEN, OAUTH_EXPIRES_AT
-        global OAUTH_REFRESH, APPLICATION_ID
-        if (CLIENT_ID is None or CLIENT_SECRET is None) and \
-                os.path.exists(CONFIG_PATH):
-            # Load in trakt API auth data from CONFIG_PATH
-            with open(CONFIG_PATH) as config_file:
-                config_data = json.load(config_file)
-
-            if CLIENT_ID is None:
-                CLIENT_ID = config_data.get('CLIENT_ID', None)
-            if CLIENT_SECRET is None:
-                CLIENT_SECRET = config_data.get('CLIENT_SECRET', None)
-            if OAUTH_TOKEN is None:
-                OAUTH_TOKEN = config_data.get('OAUTH_TOKEN', None)
-            if OAUTH_EXPIRES_AT is None:
-                OAUTH_EXPIRES_AT = config_data.get('OAUTH_EXPIRES_AT', None)
-            if OAUTH_REFRESH is None:
-                OAUTH_REFRESH = config_data.get('OAUTH_REFRESH', None)
-            if APPLICATION_ID is None:
-                APPLICATION_ID = config_data.get('APPLICATION_ID', None)
-
-            # Check token validity and refresh token if needed
-            if (not OAUTH_TOKEN_VALID and OAUTH_EXPIRES_AT is not None and
-                    OAUTH_REFRESH is not None):
-                _validate_token(args[0])
-            # For backwards compatability with trakt<=2.3.0
-            if api_key is not None and OAUTH_TOKEN is None:
-                OAUTH_TOKEN = api_key
+        global OAUTH_TOKEN_VALID, OAUTH_EXPIRES_AT
+        global OAUTH_REFRESH, OAUTH_TOKEN
+
+        load_config()
+        # Check token validity and refresh token if needed
+        if (not OAUTH_TOKEN_VALID and OAUTH_EXPIRES_AT is not None
+                and OAUTH_REFRESH is not None):
+            _validate_token(args[0])
+        # For backwards compatability with trakt<=2.3.0
+        if api_key is not None and OAUTH_TOKEN is None:
+            OAUTH_TOKEN = api_key
+
         return f(*args, **kwargs)
     return inner
 
diff --git a/medusa/__main__.py b/medusa/__main__.py
--- a/medusa/__main__.py
+++ b/medusa/__main__.py
@@ -1019,6 +1019,7 @@ def initialize(self, console_logging=True):
 
             # Initialize trakt config path.
             trakt.core.CONFIG_PATH = os.path.join(app.CACHE_DIR, '.pytrakt.json')
+            trakt.core.load_config()
 
             # reconfigure the logger
             app_logger.reconfigure()
diff --git a/medusa/notifiers/trakt.py b/medusa/notifiers/trakt.py
--- a/medusa/notifiers/trakt.py
+++ b/medusa/notifiers/trakt.py
@@ -135,11 +135,10 @@ def add_episode_to_watchlist(episode):
 
         :params episode: Episode Object.
         """
-        show_id = None
+        show_id = str(episode.series.externals.get('trakt_id', episode.series.name))
 
         try:
-            tv_show = tv.TVShow(show_id)
-            tv_episode = tv.TVEpisode(tv_show, episode.season, episode.episode)
+            tv_episode = tv.TVEpisode(show_id, episode.season, episode.episode)
             tv_episode.add_to_watchlist()
         except TraktException as error:
             log.warning('Unable to add episode to watchlist: {error!r}', {'error': error})


Test patch: