We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Postprocessing exception on Prowl notifications
Since updating to the latest commit earlier today, postprocessing throws an exception once it gets to the point of sending Prowl notifications (error below). Actual postprocessing seems to go fine--e.g., episode shows up in the db properly, etc. Never had issues with Prowl notifications before today. Tested two different shows and got the same error.

ETA: I actually turned Prowl notifications off and got this same error postprocessing another ep so... maybe it's not related to Prowl? Was just assuming because it was throwing something in prowl.py. 

**To Reproduce**
Steps to reproduce the behavior:
1. Turn on Prowl notifications.
2. Postprocess an episode.
3. See error.

**Expected behavior**
I expect to get a Prowl notification that the download is complete and for Medusa not to throw any errors.


**Medusa (please complete the following information):**
 - OS: Linux-3.2.40-i686-with-glibc2.0 (on a Synology NAS)
 - Branch: master
 - Commit: db102f3561204d388c3f4cbf6e89c79282ecd12f
 - Python version: 3.7.10
 - Database version: 44.18

<details>

2021-07-06 22:48:45 ERROR    TORNADO :: [db102f3] API :: AttributeError("'list' object has no attribute 'series_id'")
Traceback (most recent call last):
  File "/volume1/@appstore/sickbeard-custom/var/SickBeard/medusa/server/api/v1/core.py", line 165, in get
    out_dict = _call_dispatcher(args, kwargs)
  File "/volume1/@appstore/sickbeard-custom/var/SickBeard/medusa/server/api/v1/core.py", line 235, in call_dispatcher
    cur_out_dict = func(cur_args, cur_kwargs).run()  # call function and get response
  File "/volume1/@appstore/sickbeard-custom/var/SickBeard/medusa/server/api/v1/core.py", line 1336, in run
    proc_type=self.type
  File "/volume1/@appstore/sickbeard-custom/var/SickBeard/medusa/process_tv.py", line 207, in run
    process_results.process(force=force, **kwargs)
  File "/volume1/@appstore/sickbeard-custom/var/SickBeard/medusa/process_tv.py", line 355, in process
    ignore_subs=ignore_subs)
  File "/volume1/@appstore/sickbeard-custom/var/SickBeard/medusa/process_tv.py", line 627, in process_files
    self.process_media(path, self.video_files, force, is_priority, ignore_subs)
  File "/volume1/@appstore/sickbeard-custom/var/SickBeard/medusa/process_tv.py", line 849, in process_media
    self.result = processor.process()
  File "/volume1/@appstore/sickbeard-custom/var/SickBeard/medusa/post_processor.py", line 1298, in process
    notifiers.notify_download(ep_obj)
  File "/volume1/@appstore/sickbeard-custom/var/SickBeard/medusa/notifiers/__init__.py", line 103, in notify_download
    n.notify_download(ep_obj)
  File "/volume1/@appstore/sickbeard-custom/var/SickBeard/medusa/notifiers/prowl.py", line 48, in notify_download
    recipients = self._generate_recipients(show)
  File "/volume1/@appstore/sickbeard-custom/var/SickBeard/medusa/notifiers/prowl.py", line 107, in _generate_recipients
    [show_obj.series_id, show_obj.indexer]
AttributeError: 'list' object has no attribute 'series_id'
</details>



--- 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/notifiers/prowl.py`
Content:
```
1 # coding=utf-8
2 
3 from __future__ import unicode_literals
4 
5 import json
6 import logging
7 import socket
8 from builtins import object
9 
10 from medusa import app, common, db
11 from medusa.logger.adapters.style import BraceAdapter
12 
13 from requests.compat import urlencode
14 
15 from six.moves.http_client import HTTPException, HTTPSConnection
16 
17 try:
18     # this only exists in 2.6
19     from ssl import SSLError
20 except ImportError:
21     # make a fake one since I don't know what it is supposed to be in 2.5
22     class SSLError(Exception):
23         pass
24 
25 log = BraceAdapter(logging.getLogger(__name__))
26 log.logger.addHandler(logging.NullHandler())
27 
28 
29 class Notifier(object):
30     def test_notify(self, prowl_api, prowl_priority):
31         return self._send_prowl(prowl_api, prowl_priority, event='Test', message='Testing Prowl settings from Medusa', force=True)
32 
33     def notify_snatch(self, title, message, ep_obj):
34         if app.PROWL_NOTIFY_ONSNATCH:
35             recipients = self._generate_recipients(ep_obj.series)
36             if not recipients:
37                 log.debug('Skipping prowl notify because there are no configured recipients')
38             else:
39                 for api in recipients:
40                     self._send_prowl(prowl_api=api, prowl_priority=None,
41                                      event=title,
42                                      message=message)
43 
44     def notify_download(self, ep_obj):
45         ep_name = ep_obj.pretty_name_with_quality()
46         if app.PROWL_NOTIFY_ONDOWNLOAD:
47             show = self._parse_episode(ep_name)
48             recipients = self._generate_recipients(show)
49             if not recipients:
50                 log.debug('Skipping prowl notify because there are no configured recipients')
51             else:
52                 for api in recipients:
53                     self._send_prowl(prowl_api=api, prowl_priority=None,
54                                      event=common.notifyStrings[common.NOTIFY_DOWNLOAD],
55                                      message=ep_name)
56 
57     def notify_subtitle_download(self, ep_obj, lang):
58         ep_name = ep_obj.pretty_name()
59         if app.PROWL_NOTIFY_ONSUBTITLEDOWNLOAD:
60             show = self._parse_episode(ep_name)
61             recipients = self._generate_recipients(show)
62             if not recipients:
63                 log.debug('Skipping prowl notify because there are no configured recipients')
64             else:
65                 for api in recipients:
66                     self._send_prowl(prowl_api=api, prowl_priority=None,
67                                      event=common.notifyStrings[common.NOTIFY_SUBTITLE_DOWNLOAD],
68                                      message=ep_name + ' [' + lang + ']')
69 
70     def notify_git_update(self, new_version='??'):
71         if app.USE_PROWL:
72             update_text = common.notifyStrings[common.NOTIFY_GIT_UPDATE_TEXT]
73             title = common.notifyStrings[common.NOTIFY_GIT_UPDATE]
74             self._send_prowl(prowl_api=None, prowl_priority=None,
75                              event=title, message=update_text + new_version)
76 
77     def notify_login(self, ipaddress=''):
78         if app.USE_PROWL:
79             update_text = common.notifyStrings[common.NOTIFY_LOGIN_TEXT]
80             title = common.notifyStrings[common.NOTIFY_LOGIN]
81             self._send_prowl(prowl_api=None, prowl_priority=None,
82                              event=title, message=update_text.format(ipaddress))
83 
84     @staticmethod
85     def _generate_recipients(show_obj=None):
86         """
87         Generate a list of prowl recipients (api keys) for a specific show.
88 
89         Search the tv_shows table for entries in the notify_list field.
90         :param show_obj: Show object.
91         """
92         apis = []
93         mydb = db.DBConnection()
94 
95         # Grab the global recipient(s)
96         if app.PROWL_API:
97             for api in app.PROWL_API:
98                 if api.strip():
99                     apis.append(api)
100 
101         # Grab the per-show-notification recipients
102         if show_obj is not None:
103             recipients = mydb.select(
104                 'SELECT notify_list '
105                 'FROM tv_shows '
106                 'WHERE indexer_id = ? AND indexer = ? ',
107                 [show_obj.series_id, show_obj.indexer]
108             )
109 
110             for subs in recipients:
111                 if subs['notify_list']:
112                     entries = json.loads(subs['notify_list'])
113                     if entries:
114                         for api in entries['prowlAPIs'].split(','):
115                             if api.strip():
116                                 apis.append(api)
117 
118         apis = set(apis)
119         return apis
120 
121     @staticmethod
122     def _send_prowl(prowl_api=None, prowl_priority=None, event=None, message=None, force=False):
123 
124         if not app.USE_PROWL and not force:
125             return False
126 
127         if prowl_api is None:
128             prowl_api = ','.join(app.PROWL_API)
129             if not prowl_api:
130                 return False
131 
132         if prowl_priority is None:
133             prowl_priority = app.PROWL_PRIORITY
134 
135         title = app.PROWL_MESSAGE_TITLE
136 
137         log.debug(u'PROWL: Sending notice with details: title="{0}" event="{1}", message="{2}", priority={3}, api={4}',
138                   title, event, message, prowl_priority, prowl_api)
139 
140         http_handler = HTTPSConnection('api.prowlapp.com')
141 
142         data = {'apikey': prowl_api,
143                 'application': title,
144                 'event': event,
145                 'description': message.encode('utf-8'),
146                 'priority': prowl_priority}
147 
148         try:
149             http_handler.request('POST',
150                                  '/publicapi/add',
151                                  headers={'Content-type': 'application/x-www-form-urlencoded'},
152                                  body=urlencode(data))
153         except (SSLError, HTTPException, socket.error):
154             log.error(u'Prowl notification failed.')
155             return False
156         response = http_handler.getresponse()
157         request_status = response.status
158 
159         if request_status == 200:
160             log.info(u'Prowl notifications sent.')
161             return True
162         elif request_status == 401:
163             log.error(u'Prowl auth failed: {0}', response.reason)
164             return False
165         else:
166             log.error(u'Prowl notification failed.')
167             return False
168 
169     @staticmethod
170     def _parse_episode(ep_name):
171         sep = ' - '
172         titles = ep_name.split(sep)
173         titles.sort(key=len, reverse=True)
174         log.debug('TITLES: {0}', titles)
175 
176         return titles
177
```
--- 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/notifiers/prowl.py b/medusa/notifiers/prowl.py
--- a/medusa/notifiers/prowl.py
+++ b/medusa/notifiers/prowl.py
@@ -42,30 +42,26 @@
                                      message=message)
 
     def notify_download(self, ep_obj):
-        ep_name = ep_obj.pretty_name_with_quality()
         if app.PROWL_NOTIFY_ONDOWNLOAD:
-            show = self._parse_episode(ep_name)
-            recipients = self._generate_recipients(show)
+            recipients = self._generate_recipients(ep_obj.series)
             if not recipients:
                 log.debug('Skipping prowl notify because there are no configured recipients')
             else:
                 for api in recipients:
                     self._send_prowl(prowl_api=api, prowl_priority=None,
                                      event=common.notifyStrings[common.NOTIFY_DOWNLOAD],
-                                     message=ep_name)
+                                     message=ep_obj.pretty_name_with_quality())
 
     def notify_subtitle_download(self, ep_obj, lang):
-        ep_name = ep_obj.pretty_name()
         if app.PROWL_NOTIFY_ONSUBTITLEDOWNLOAD:
-            show = self._parse_episode(ep_name)
-            recipients = self._generate_recipients(show)
+            recipients = self._generate_recipients(ep_obj.series)
             if not recipients:
                 log.debug('Skipping prowl notify because there are no configured recipients')
             else:
                 for api in recipients:
                     self._send_prowl(prowl_api=api, prowl_priority=None,
                                      event=common.notifyStrings[common.NOTIFY_SUBTITLE_DOWNLOAD],
-                                     message=ep_name + ' [' + lang + ']')
+                                     message=f'{ep_obj.pretty_name()} [{lang}]')
 
     def notify_git_update(self, new_version='??'):
         if app.USE_PROWL:

{"golden_diff": "diff --git a/medusa/notifiers/prowl.py b/medusa/notifiers/prowl.py\n--- a/medusa/notifiers/prowl.py\n+++ b/medusa/notifiers/prowl.py\n@@ -42,30 +42,26 @@\n                                      message=message)\n \n     def notify_download(self, ep_obj):\n-        ep_name = ep_obj.pretty_name_with_quality()\n         if app.PROWL_NOTIFY_ONDOWNLOAD:\n-            show = self._parse_episode(ep_name)\n-            recipients = self._generate_recipients(show)\n+            recipients = self._generate_recipients(ep_obj.series)\n             if not recipients:\n                 log.debug('Skipping prowl notify because there are no configured recipients')\n             else:\n                 for api in recipients:\n                     self._send_prowl(prowl_api=api, prowl_priority=None,\n                                      event=common.notifyStrings[common.NOTIFY_DOWNLOAD],\n-                                     message=ep_name)\n+                                     message=ep_obj.pretty_name_with_quality())\n \n     def notify_subtitle_download(self, ep_obj, lang):\n-        ep_name = ep_obj.pretty_name()\n         if app.PROWL_NOTIFY_ONSUBTITLEDOWNLOAD:\n-            show = self._parse_episode(ep_name)\n-            recipients = self._generate_recipients(show)\n+            recipients = self._generate_recipients(ep_obj.series)\n             if not recipients:\n                 log.debug('Skipping prowl notify because there are no configured recipients')\n             else:\n                 for api in recipients:\n                     self._send_prowl(prowl_api=api, prowl_priority=None,\n                                      event=common.notifyStrings[common.NOTIFY_SUBTITLE_DOWNLOAD],\n-                                     message=ep_name + ' [' + lang + ']')\n+                                     message=f'{ep_obj.pretty_name()} [{lang}]')\n \n     def notify_git_update(self, new_version='??'):\n         if app.USE_PROWL:\n", "issue": "Postprocessing exception on Prowl notifications\nSince updating to the latest commit earlier today, postprocessing throws an exception once it gets to the point of sending Prowl notifications (error below). Actual postprocessing seems to go fine--e.g., episode shows up in the db properly, etc. Never had issues with Prowl notifications before today. Tested two different shows and got the same error.\r\n\r\nETA: I actually turned Prowl notifications off and got this same error postprocessing another ep so... maybe it's not related to Prowl? Was just assuming because it was throwing something in prowl.py. \r\n\r\n**To Reproduce**\r\nSteps to reproduce the behavior:\r\n1. Turn on Prowl notifications.\r\n2. Postprocess an episode.\r\n3. See error.\r\n\r\n**Expected behavior**\r\nI expect to get a Prowl notification that the download is complete and for Medusa not to throw any errors.\r\n\r\n\r\n**Medusa (please complete the following information):**\r\n - OS: Linux-3.2.40-i686-with-glibc2.0 (on a Synology NAS)\r\n - Branch: master\r\n - Commit: db102f3561204d388c3f4cbf6e89c79282ecd12f\r\n - Python version: 3.7.10\r\n - Database version: 44.18\r\n\r\n<details>\r\n\r\n2021-07-06 22:48:45 ERROR    TORNADO :: [db102f3] API :: AttributeError(\"'list' object has no attribute 'series_id'\")\r\nTraceback (most recent call last):\r\n  File \"/volume1/@appstore/sickbeard-custom/var/SickBeard/medusa/server/api/v1/core.py\", line 165, in get\r\n    out_dict = _call_dispatcher(args, kwargs)\r\n  File \"/volume1/@appstore/sickbeard-custom/var/SickBeard/medusa/server/api/v1/core.py\", line 235, in call_dispatcher\r\n    cur_out_dict = func(cur_args, cur_kwargs).run()  # call function and get response\r\n  File \"/volume1/@appstore/sickbeard-custom/var/SickBeard/medusa/server/api/v1/core.py\", line 1336, in run\r\n    proc_type=self.type\r\n  File \"/volume1/@appstore/sickbeard-custom/var/SickBeard/medusa/process_tv.py\", line 207, in run\r\n    process_results.process(force=force, **kwargs)\r\n  File \"/volume1/@appstore/sickbeard-custom/var/SickBeard/medusa/process_tv.py\", line 355, in process\r\n    ignore_subs=ignore_subs)\r\n  File \"/volume1/@appstore/sickbeard-custom/var/SickBeard/medusa/process_tv.py\", line 627, in process_files\r\n    self.process_media(path, self.video_files, force, is_priority, ignore_subs)\r\n  File \"/volume1/@appstore/sickbeard-custom/var/SickBeard/medusa/process_tv.py\", line 849, in process_media\r\n    self.result = processor.process()\r\n  File \"/volume1/@appstore/sickbeard-custom/var/SickBeard/medusa/post_processor.py\", line 1298, in process\r\n    notifiers.notify_download(ep_obj)\r\n  File \"/volume1/@appstore/sickbeard-custom/var/SickBeard/medusa/notifiers/__init__.py\", line 103, in notify_download\r\n    n.notify_download(ep_obj)\r\n  File \"/volume1/@appstore/sickbeard-custom/var/SickBeard/medusa/notifiers/prowl.py\", line 48, in notify_download\r\n    recipients = self._generate_recipients(show)\r\n  File \"/volume1/@appstore/sickbeard-custom/var/SickBeard/medusa/notifiers/prowl.py\", line 107, in _generate_recipients\r\n    [show_obj.series_id, show_obj.indexer]\r\nAttributeError: 'list' object has no attribute 'series_id'\r\n</details>\r\n\r\n\n", "before_files": [{"content": "# coding=utf-8\n\nfrom __future__ import unicode_literals\n\nimport json\nimport logging\nimport socket\nfrom builtins import object\n\nfrom medusa import app, common, db\nfrom medusa.logger.adapters.style import BraceAdapter\n\nfrom requests.compat import urlencode\n\nfrom six.moves.http_client import HTTPException, HTTPSConnection\n\ntry:\n    # this only exists in 2.6\n    from ssl import SSLError\nexcept ImportError:\n    # make a fake one since I don't know what it is supposed to be in 2.5\n    class SSLError(Exception):\n        pass\n\nlog = BraceAdapter(logging.getLogger(__name__))\nlog.logger.addHandler(logging.NullHandler())\n\n\nclass Notifier(object):\n    def test_notify(self, prowl_api, prowl_priority):\n        return self._send_prowl(prowl_api, prowl_priority, event='Test', message='Testing Prowl settings from Medusa', force=True)\n\n    def notify_snatch(self, title, message, ep_obj):\n        if app.PROWL_NOTIFY_ONSNATCH:\n            recipients = self._generate_recipients(ep_obj.series)\n            if not recipients:\n                log.debug('Skipping prowl notify because there are no configured recipients')\n            else:\n                for api in recipients:\n                    self._send_prowl(prowl_api=api, prowl_priority=None,\n                                     event=title,\n                                     message=message)\n\n    def notify_download(self, ep_obj):\n        ep_name = ep_obj.pretty_name_with_quality()\n        if app.PROWL_NOTIFY_ONDOWNLOAD:\n            show = self._parse_episode(ep_name)\n            recipients = self._generate_recipients(show)\n            if not recipients:\n                log.debug('Skipping prowl notify because there are no configured recipients')\n            else:\n                for api in recipients:\n                    self._send_prowl(prowl_api=api, prowl_priority=None,\n                                     event=common.notifyStrings[common.NOTIFY_DOWNLOAD],\n                                     message=ep_name)\n\n    def notify_subtitle_download(self, ep_obj, lang):\n        ep_name = ep_obj.pretty_name()\n        if app.PROWL_NOTIFY_ONSUBTITLEDOWNLOAD:\n            show = self._parse_episode(ep_name)\n            recipients = self._generate_recipients(show)\n            if not recipients:\n                log.debug('Skipping prowl notify because there are no configured recipients')\n            else:\n                for api in recipients:\n                    self._send_prowl(prowl_api=api, prowl_priority=None,\n                                     event=common.notifyStrings[common.NOTIFY_SUBTITLE_DOWNLOAD],\n                                     message=ep_name + ' [' + lang + ']')\n\n    def notify_git_update(self, new_version='??'):\n        if app.USE_PROWL:\n            update_text = common.notifyStrings[common.NOTIFY_GIT_UPDATE_TEXT]\n            title = common.notifyStrings[common.NOTIFY_GIT_UPDATE]\n            self._send_prowl(prowl_api=None, prowl_priority=None,\n                             event=title, message=update_text + new_version)\n\n    def notify_login(self, ipaddress=''):\n        if app.USE_PROWL:\n            update_text = common.notifyStrings[common.NOTIFY_LOGIN_TEXT]\n            title = common.notifyStrings[common.NOTIFY_LOGIN]\n            self._send_prowl(prowl_api=None, prowl_priority=None,\n                             event=title, message=update_text.format(ipaddress))\n\n    @staticmethod\n    def _generate_recipients(show_obj=None):\n        \"\"\"\n        Generate a list of prowl recipients (api keys) for a specific show.\n\n        Search the tv_shows table for entries in the notify_list field.\n        :param show_obj: Show object.\n        \"\"\"\n        apis = []\n        mydb = db.DBConnection()\n\n        # Grab the global recipient(s)\n        if app.PROWL_API:\n            for api in app.PROWL_API:\n                if api.strip():\n                    apis.append(api)\n\n        # Grab the per-show-notification recipients\n        if show_obj is not None:\n            recipients = mydb.select(\n                'SELECT notify_list '\n                'FROM tv_shows '\n                'WHERE indexer_id = ? AND indexer = ? ',\n                [show_obj.series_id, show_obj.indexer]\n            )\n\n            for subs in recipients:\n                if subs['notify_list']:\n                    entries = json.loads(subs['notify_list'])\n                    if entries:\n                        for api in entries['prowlAPIs'].split(','):\n                            if api.strip():\n                                apis.append(api)\n\n        apis = set(apis)\n        return apis\n\n    @staticmethod\n    def _send_prowl(prowl_api=None, prowl_priority=None, event=None, message=None, force=False):\n\n        if not app.USE_PROWL and not force:\n            return False\n\n        if prowl_api is None:\n            prowl_api = ','.join(app.PROWL_API)\n            if not prowl_api:\n                return False\n\n        if prowl_priority is None:\n            prowl_priority = app.PROWL_PRIORITY\n\n        title = app.PROWL_MESSAGE_TITLE\n\n        log.debug(u'PROWL: Sending notice with details: title=\"{0}\" event=\"{1}\", message=\"{2}\", priority={3}, api={4}',\n                  title, event, message, prowl_priority, prowl_api)\n\n        http_handler = HTTPSConnection('api.prowlapp.com')\n\n        data = {'apikey': prowl_api,\n                'application': title,\n                'event': event,\n                'description': message.encode('utf-8'),\n                'priority': prowl_priority}\n\n        try:\n            http_handler.request('POST',\n                                 '/publicapi/add',\n                                 headers={'Content-type': 'application/x-www-form-urlencoded'},\n                                 body=urlencode(data))\n        except (SSLError, HTTPException, socket.error):\n            log.error(u'Prowl notification failed.')\n            return False\n        response = http_handler.getresponse()\n        request_status = response.status\n\n        if request_status == 200:\n            log.info(u'Prowl notifications sent.')\n            return True\n        elif request_status == 401:\n            log.error(u'Prowl auth failed: {0}', response.reason)\n            return False\n        else:\n            log.error(u'Prowl notification failed.')\n            return False\n\n    @staticmethod\n    def _parse_episode(ep_name):\n        sep = ' - '\n        titles = ep_name.split(sep)\n        titles.sort(key=len, reverse=True)\n        log.debug('TITLES: {0}', titles)\n\n        return titles\n", "path": "medusa/notifiers/prowl.py"}], "after_files": [{"content": "# coding=utf-8\n\nfrom __future__ import unicode_literals\n\nimport json\nimport logging\nimport socket\nfrom builtins import object\n\nfrom medusa import app, common, db\nfrom medusa.logger.adapters.style import BraceAdapter\n\nfrom requests.compat import urlencode\n\nfrom six.moves.http_client import HTTPException, HTTPSConnection\n\ntry:\n    # this only exists in 2.6\n    from ssl import SSLError\nexcept ImportError:\n    # make a fake one since I don't know what it is supposed to be in 2.5\n    class SSLError(Exception):\n        pass\n\nlog = BraceAdapter(logging.getLogger(__name__))\nlog.logger.addHandler(logging.NullHandler())\n\n\nclass Notifier(object):\n    def test_notify(self, prowl_api, prowl_priority):\n        return self._send_prowl(prowl_api, prowl_priority, event='Test', message='Testing Prowl settings from Medusa', force=True)\n\n    def notify_snatch(self, title, message, ep_obj):\n        if app.PROWL_NOTIFY_ONSNATCH:\n            recipients = self._generate_recipients(ep_obj.series)\n            if not recipients:\n                log.debug('Skipping prowl notify because there are no configured recipients')\n            else:\n                for api in recipients:\n                    self._send_prowl(prowl_api=api, prowl_priority=None,\n                                     event=title,\n                                     message=message)\n\n    def notify_download(self, ep_obj):\n        if app.PROWL_NOTIFY_ONDOWNLOAD:\n            recipients = self._generate_recipients(ep_obj.series)\n            if not recipients:\n                log.debug('Skipping prowl notify because there are no configured recipients')\n            else:\n                for api in recipients:\n                    self._send_prowl(prowl_api=api, prowl_priority=None,\n                                     event=common.notifyStrings[common.NOTIFY_DOWNLOAD],\n                                     message=ep_obj.pretty_name_with_quality())\n\n    def notify_subtitle_download(self, ep_obj, lang):\n        if app.PROWL_NOTIFY_ONSUBTITLEDOWNLOAD:\n            recipients = self._generate_recipients(ep_obj.series)\n            if not recipients:\n                log.debug('Skipping prowl notify because there are no configured recipients')\n            else:\n                for api in recipients:\n                    self._send_prowl(prowl_api=api, prowl_priority=None,\n                                     event=common.notifyStrings[common.NOTIFY_SUBTITLE_DOWNLOAD],\n                                     message=f'{ep_obj.pretty_name()} [{lang}]')\n\n    def notify_git_update(self, new_version='??'):\n        if app.USE_PROWL:\n            update_text = common.notifyStrings[common.NOTIFY_GIT_UPDATE_TEXT]\n            title = common.notifyStrings[common.NOTIFY_GIT_UPDATE]\n            self._send_prowl(prowl_api=None, prowl_priority=None,\n                             event=title, message=update_text + new_version)\n\n    def notify_login(self, ipaddress=''):\n        if app.USE_PROWL:\n            update_text = common.notifyStrings[common.NOTIFY_LOGIN_TEXT]\n            title = common.notifyStrings[common.NOTIFY_LOGIN]\n            self._send_prowl(prowl_api=None, prowl_priority=None,\n                             event=title, message=update_text.format(ipaddress))\n\n    @staticmethod\n    def _generate_recipients(show_obj=None):\n        \"\"\"\n        Generate a list of prowl recipients (api keys) for a specific show.\n\n        Search the tv_shows table for entries in the notify_list field.\n        :param show_obj: Show object.\n        \"\"\"\n        apis = []\n        mydb = db.DBConnection()\n\n        # Grab the global recipient(s)\n        if app.PROWL_API:\n            for api in app.PROWL_API:\n                if api.strip():\n                    apis.append(api)\n\n        # Grab the per-show-notification recipients\n        if show_obj is not None:\n            recipients = mydb.select(\n                'SELECT notify_list '\n                'FROM tv_shows '\n                'WHERE indexer_id = ? AND indexer = ? ',\n                [show_obj.series_id, show_obj.indexer]\n            )\n\n            for subs in recipients:\n                if subs['notify_list']:\n                    entries = json.loads(subs['notify_list'])\n                    if entries:\n                        for api in entries['prowlAPIs'].split(','):\n                            if api.strip():\n                                apis.append(api)\n\n        apis = set(apis)\n        return apis\n\n    @staticmethod\n    def _send_prowl(prowl_api=None, prowl_priority=None, event=None, message=None, force=False):\n\n        if not app.USE_PROWL and not force:\n            return False\n\n        if prowl_api is None:\n            prowl_api = ','.join(app.PROWL_API)\n            if not prowl_api:\n                return False\n\n        if prowl_priority is None:\n            prowl_priority = app.PROWL_PRIORITY\n\n        title = app.PROWL_MESSAGE_TITLE\n\n        log.debug(u'PROWL: Sending notice with details: title=\"{0}\" event=\"{1}\", message=\"{2}\", priority={3}, api={4}',\n                  title, event, message, prowl_priority, prowl_api)\n\n        http_handler = HTTPSConnection('api.prowlapp.com')\n\n        data = {'apikey': prowl_api,\n                'application': title,\n                'event': event,\n                'description': message.encode('utf-8'),\n                'priority': prowl_priority}\n\n        try:\n            http_handler.request('POST',\n                                 '/publicapi/add',\n                                 headers={'Content-type': 'application/x-www-form-urlencoded'},\n                                 body=urlencode(data))\n        except (SSLError, HTTPException, socket.error):\n            log.error(u'Prowl notification failed.')\n            return False\n        response = http_handler.getresponse()\n        request_status = response.status\n\n        if request_status == 200:\n            log.info(u'Prowl notifications sent.')\n            return True\n        elif request_status == 401:\n            log.error(u'Prowl auth failed: {0}', response.reason)\n            return False\n        else:\n            log.error(u'Prowl notification failed.')\n            return False\n\n    @staticmethod\n    def _parse_episode(ep_name):\n        sep = ' - '\n        titles = ep_name.split(sep)\n        titles.sort(key=len, reverse=True)\n        log.debug('TITLES: {0}', titles)\n\n        return titles\n", "path": "medusa/notifiers/prowl.py"}]}