We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
TorrentDay search result empty or malformed
### Expected behaviour:

Search result
![image](https://user-images.githubusercontent.com/3293865/94644622-67c71500-031c-11eb-86d9-a9cd983dee1f.png)

### Actual behaviour:

Search result empty or malformed.

### Steps to reproduce:
- Step 1: run task

#### Config:
```yaml
  download-childrenshow-discover:
    template:
      - cartoon-metainfo
      - reject
      - children_show
      - notifyViaEmail
    discover:
      what:
        - next_series_seasons:
            from_start: yes
        - next_series_episodes:
            backfill: true
            from_start: yes
      from:
        - torrentday:
            uid: "'{? torrentday.uid ?}'"
            passkey: "{? torrentday.passkey ?}"
            cfduid: "{? torrentday.cfduid ?}"
            rss_key: "{? torrentday.rss_key ?}"
```
  
#### Log:
<details>
 <summary>(click to expand)</summary>

2020-09-30 12:39:49 VERBOSE  discover      download-childrenshow-discover Searching for `Vampirina S02` with plugin `torrentday` (1 of 7)
2020-09-30 12:39:49 DEBUG    utils.requests download-childrenshow-discover GETing URL https://www.torrentday.com/t with args () and kwargs {'params': {'cata': 'yes', 'c7': 1, 'clear-new': 1, 'q': 'Vampirina S02'}, 'cookies': {'uid': '...', 'pass': '...', '__cfduid': '...'}, 'allow_redirects': True, 'timeout': 30}
2020-09-30 12:39:50 ERROR    discover      download-childrenshow-discover Error searching with torrentday: Search returned by torrentday appears to be empty or malformed.
2020-09-30 12:39:50 VERBOSE  discover      download-childrenshow-discover No search results for `Vampirina S02`

</details>

### Additional information:

- FlexGet version: 3.1.71
- Python version: 3.5.1
- Installation method: pip install
- Using daemon (yes/no): yes
- OS and version: Synology DSM 6

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `flexget/components/sites/sites/torrentday.py`
Content:
```
1 import re
2 
3 from loguru import logger
4 from requests.exceptions import RequestException
5 
6 from flexget import plugin
7 from flexget.components.sites.urlrewriting import UrlRewritingError
8 from flexget.components.sites.utils import normalize_unicode, torrent_availability
9 from flexget.entry import Entry
10 from flexget.event import event
11 from flexget.plugin import PluginError
12 from flexget.utils import requests
13 from flexget.utils.soup import get_soup
14 from flexget.utils.tools import parse_filesize
15 
16 logger = logger.bind(name='torrentday')
17 
18 CATEGORIES = {
19     'all': 0,
20     # Movies
21     'mov480p': 25,
22     'movHD': 11,
23     'movBD': 5,
24     'movDVD': 3,
25     'movMP4': 21,
26     'movNonEnglish': 22,
27     'movPACKS': 13,
28     'movSDx264': 44,
29     'movX265': 48,
30     'movXVID': 1,
31     # TV
32     'tv480p': 24,
33     'tvBRD': 32,
34     'tvDVD': 31,
35     'tvDVDrip': 33,
36     'tvMOBILE': 46,
37     'tvPACKS': 14,
38     'tvSDx264': 26,
39     'tvHDx264': 7,
40     'tvX265': 34,
41     'tvXVID': 2,
42 }
43 
44 
45 class UrlRewriteTorrentday:
46     """
47         Torrentday urlrewriter and search plugin.
48 
49         torrentday:
50           uid: xxxxxxxxxxxxx  (required)    NOT YOUR LOGIN. find this in your browser's cookies
51           passkey: xxxxxxxxx  (required)    NOT YOUR PASSWORD. see previous
52           cfduid: xxxxxxxxxx  (required)    AGAIN IN THE COOKIES
53           rss_key: xxxxxxxxx  (required)    get this from your profile page
54           category: xxxxxxxx
55 
56           Category can be one of 
57             ID from browsing site OR 'name'
58             movies:
59               mov480p, movHD, movBD, movDVD,
60               movMP4, movNonEnglish, movPACKS,
61               movSDx264, movX265, movXVID
62             tv:
63               tv480p, tvBRD, tvDVD, tvDVDrip,
64               tvMOBILE, tvPACKS, tvSDx264, 
65               tvHDx264, tvX265, tvXVID
66     """
67 
68     schema = {
69         'type': 'object',
70         'properties': {
71             'rss_key': {'type': 'string'},
72             'uid': {'type': 'string'},
73             'passkey': {'type': 'string'},
74             'cfduid': {'type': 'string'},
75             'category': {
76                 'oneOf': [{'type': 'integer'}, {'type': 'string', 'enum': list(CATEGORIES)}]
77             },
78         },
79         'required': ['rss_key', 'uid', 'passkey', 'cfduid'],
80         'additionalProperties': False,
81     }
82 
83     # urlrewriter API
84     def url_rewritable(self, task, entry):
85         url = entry['url']
86         if url.find('.torrent'):
87             return False
88         if url.startswith('https://www.torrentday.com'):
89             return True
90         return False
91 
92     # urlrewriter API
93     def url_rewrite(self, task, entry):
94         if 'url' not in entry:
95             logger.error('Didn\'t actually get a URL...')
96         else:
97             logger.debug('Got the URL: {}', entry['url'])
98         if entry['url'].startswith('https://www.torrentday.com/browse'):
99             # use search
100             results = self.search(task, entry)
101             if not results:
102                 raise UrlRewritingError('No search results found')
103             entry['url'] = results[0]['url']
104 
105     @plugin.internet(logger)
106     def search(self, task, entry, config=None):
107         """
108         Search for name from torrentday.
109         """
110 
111         categories = config.get('category', 'all')
112         # Make sure categories is a list
113         if not isinstance(categories, list):
114             categories = [categories]
115         # If there are any text categories, turn them into their id number
116         categories = [c if isinstance(c, int) else CATEGORIES[c] for c in categories]
117         params = {
118             'cata': 'yes',
119             'c{}'.format(','.join(str(c) for c in categories)): 1,
120             'clear-new': 1,
121         }
122         entries = set()
123         for search_string in entry.get('search_strings', [entry['title']]):
124 
125             url = 'https://www.torrentday.com/t'
126             params['q'] = normalize_unicode(search_string).replace(':', '')
127             cookies = {
128                 'uid': config['uid'],
129                 'pass': config['passkey'],
130                 '__cfduid': config['cfduid'],
131             }
132 
133             try:
134                 page = requests.get(url, params=params, cookies=cookies).content
135             except RequestException as e:
136                 raise PluginError('Could not connect to torrentday: {}'.format(e))
137 
138             # the following should avoid table being None due to a malformed
139             # html in td search results
140             soup = get_soup(page).contents[1].contents[1].next.next.nextSibling
141             table = soup.find('table', {'id': 'torrentTable'})
142             if table is None:
143                 raise PluginError(
144                     'Search returned by torrentday appears to be empty or malformed.'
145                 )
146 
147             # the first row is the header so skip it
148             for tr in table.find_all('tr')[1:]:
149                 entry = Entry()
150                 # find the torrent names
151                 td = tr.find('td', {'class': 'torrentNameInfo'})
152                 if not td:
153                     logger.warning('Could not find entry torrentNameInfo for {}.', search_string)
154                     continue
155                 title = td.find('a')
156                 if not title:
157                     logger.warning('Could not determine title for {}.', search_string)
158                     continue
159                 entry['title'] = title.contents[0]
160                 logger.debug('title: {}', title.contents[0])
161 
162                 # find download link
163                 torrent_url = tr.find('td', {'class': 'ac'})
164                 if not torrent_url:
165                     logger.warning('Could not determine download link for {}.', search_string)
166                     continue
167                 torrent_url = torrent_url.find('a').get('href')
168 
169                 # construct download URL
170                 torrent_url = (
171                     'https://www.torrentday.com/'
172                     + torrent_url
173                     + '?torrent_pass='
174                     + config['rss_key']
175                 )
176                 logger.debug('RSS-ified download link: {}', torrent_url)
177                 entry['url'] = torrent_url
178 
179                 # us tr object for seeders/leechers
180                 seeders = tr.find('td', {'class': 'ac seedersInfo'})
181                 leechers = tr.find('td', {'class': 'ac leechersInfo'})
182                 entry['torrent_seeds'] = int(seeders.contents[0].replace(',', ''))
183                 entry['torrent_leeches'] = int(leechers.contents[0].replace(',', ''))
184                 entry['torrent_availability'] = torrent_availability(
185                     entry['torrent_seeds'], entry['torrent_leeches']
186                 )
187 
188                 # use tr object for size
189                 size = tr.find('td', text=re.compile(r'([\.\d]+) ([TGMKk]?)B')).contents[0]
190                 size = re.search(r'([\.\d]+) ([TGMKk]?)B', str(size))
191 
192                 entry['content_size'] = parse_filesize(size.group(0))
193 
194                 entries.add(entry)
195 
196         return sorted(entries, reverse=True, key=lambda x: x.get('torrent_availability'))
197 
198 
199 @event('plugin.register')
200 def register_plugin():
201     plugin.register(
202         UrlRewriteTorrentday, 'torrentday', interfaces=['urlrewriter', 'search'], api_ver=2
203     )
204
```
--- 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/flexget/components/sites/sites/torrentday.py b/flexget/components/sites/sites/torrentday.py
--- a/flexget/components/sites/sites/torrentday.py
+++ b/flexget/components/sites/sites/torrentday.py
@@ -18,6 +18,7 @@
 CATEGORIES = {
     'all': 0,
     # Movies
+    'mov4k': 96,
     'mov480p': 25,
     'movHD': 11,
     'movBD': 5,
@@ -34,6 +35,7 @@
     'tvDVD': 31,
     'tvDVDrip': 33,
     'tvMOBILE': 46,
+    'tvNonEnglish': 82,
     'tvPACKS': 14,
     'tvSDx264': 26,
     'tvHDx264': 7,
@@ -53,16 +55,16 @@
           rss_key: xxxxxxxxx  (required)    get this from your profile page
           category: xxxxxxxx
 
-          Category can be one of 
+          Category can be one of
             ID from browsing site OR 'name'
             movies:
-              mov480p, movHD, movBD, movDVD,
+              mov4k, mov480p, movHD, movBD, movDVD,
               movMP4, movNonEnglish, movPACKS,
               movSDx264, movX265, movXVID
             tv:
               tv480p, tvBRD, tvDVD, tvDVDrip,
-              tvMOBILE, tvPACKS, tvSDx264, 
-              tvHDx264, tvX265, tvXVID
+              tvMOBILE, tvNonEnglish, tvPACKS,
+              tvSDx264, tvHDx264, tvX265, tvXVID
     """
 
     schema = {
@@ -114,11 +116,9 @@
             categories = [categories]
         # If there are any text categories, turn them into their id number
         categories = [c if isinstance(c, int) else CATEGORIES[c] for c in categories]
-        params = {
-            'cata': 'yes',
-            'c{}'.format(','.join(str(c) for c in categories)): 1,
-            'clear-new': 1,
-        }
+        params = {'cata': 'yes', 'clear-new': 1}
+        params.update({str(c): 1 for c in categories})
+
         entries = set()
         for search_string in entry.get('search_strings', [entry['title']]):
 
@@ -137,7 +137,7 @@
 
             # the following should avoid table being None due to a malformed
             # html in td search results
-            soup = get_soup(page).contents[1].contents[1].next.next.nextSibling
+            soup = get_soup(page).contents[1].contents[1].contents[1].next.nextSibling
             table = soup.find('table', {'id': 'torrentTable'})
             if table is None:
                 raise PluginError(

{"golden_diff": "diff --git a/flexget/components/sites/sites/torrentday.py b/flexget/components/sites/sites/torrentday.py\n--- a/flexget/components/sites/sites/torrentday.py\n+++ b/flexget/components/sites/sites/torrentday.py\n@@ -18,6 +18,7 @@\n CATEGORIES = {\n     'all': 0,\n     # Movies\n+    'mov4k': 96,\n     'mov480p': 25,\n     'movHD': 11,\n     'movBD': 5,\n@@ -34,6 +35,7 @@\n     'tvDVD': 31,\n     'tvDVDrip': 33,\n     'tvMOBILE': 46,\n+    'tvNonEnglish': 82,\n     'tvPACKS': 14,\n     'tvSDx264': 26,\n     'tvHDx264': 7,\n@@ -53,16 +55,16 @@\n           rss_key: xxxxxxxxx  (required)    get this from your profile page\n           category: xxxxxxxx\n \n-          Category can be one of \n+          Category can be one of\n             ID from browsing site OR 'name'\n             movies:\n-              mov480p, movHD, movBD, movDVD,\n+              mov4k, mov480p, movHD, movBD, movDVD,\n               movMP4, movNonEnglish, movPACKS,\n               movSDx264, movX265, movXVID\n             tv:\n               tv480p, tvBRD, tvDVD, tvDVDrip,\n-              tvMOBILE, tvPACKS, tvSDx264, \n-              tvHDx264, tvX265, tvXVID\n+              tvMOBILE, tvNonEnglish, tvPACKS,\n+              tvSDx264, tvHDx264, tvX265, tvXVID\n     \"\"\"\n \n     schema = {\n@@ -114,11 +116,9 @@\n             categories = [categories]\n         # If there are any text categories, turn them into their id number\n         categories = [c if isinstance(c, int) else CATEGORIES[c] for c in categories]\n-        params = {\n-            'cata': 'yes',\n-            'c{}'.format(','.join(str(c) for c in categories)): 1,\n-            'clear-new': 1,\n-        }\n+        params = {'cata': 'yes', 'clear-new': 1}\n+        params.update({str(c): 1 for c in categories})\n+\n         entries = set()\n         for search_string in entry.get('search_strings', [entry['title']]):\n \n@@ -137,7 +137,7 @@\n \n             # the following should avoid table being None due to a malformed\n             # html in td search results\n-            soup = get_soup(page).contents[1].contents[1].next.next.nextSibling\n+            soup = get_soup(page).contents[1].contents[1].contents[1].next.nextSibling\n             table = soup.find('table', {'id': 'torrentTable'})\n             if table is None:\n                 raise PluginError(\n", "issue": "TorrentDay search result empty or malformed\n### Expected behaviour:\r\n\r\nSearch result\r\n![image](https://user-images.githubusercontent.com/3293865/94644622-67c71500-031c-11eb-86d9-a9cd983dee1f.png)\r\n\r\n### Actual behaviour:\r\n\r\nSearch result empty or malformed.\r\n\r\n### Steps to reproduce:\r\n- Step 1: run task\r\n\r\n#### Config:\r\n```yaml\r\n  download-childrenshow-discover:\r\n    template:\r\n      - cartoon-metainfo\r\n      - reject\r\n      - children_show\r\n      - notifyViaEmail\r\n    discover:\r\n      what:\r\n        - next_series_seasons:\r\n            from_start: yes\r\n        - next_series_episodes:\r\n            backfill: true\r\n            from_start: yes\r\n      from:\r\n        - torrentday:\r\n            uid: \"'{? torrentday.uid ?}'\"\r\n            passkey: \"{? torrentday.passkey ?}\"\r\n            cfduid: \"{? torrentday.cfduid ?}\"\r\n            rss_key: \"{? torrentday.rss_key ?}\"\r\n```\r\n  \r\n#### Log:\r\n<details>\r\n <summary>(click to expand)</summary>\r\n\r\n2020-09-30 12:39:49 VERBOSE  discover      download-childrenshow-discover Searching for `Vampirina S02` with plugin `torrentday` (1 of 7)\r\n2020-09-30 12:39:49 DEBUG    utils.requests download-childrenshow-discover GETing URL https://www.torrentday.com/t with args () and kwargs {'params': {'cata': 'yes', 'c7': 1, 'clear-new': 1, 'q': 'Vampirina S02'}, 'cookies': {'uid': '...', 'pass': '...', '__cfduid': '...'}, 'allow_redirects': True, 'timeout': 30}\r\n2020-09-30 12:39:50 ERROR    discover      download-childrenshow-discover Error searching with torrentday: Search returned by torrentday appears to be empty or malformed.\r\n2020-09-30 12:39:50 VERBOSE  discover      download-childrenshow-discover No search results for `Vampirina S02`\r\n\r\n</details>\r\n\r\n### Additional information:\r\n\r\n- FlexGet version: 3.1.71\r\n- Python version: 3.5.1\r\n- Installation method: pip install\r\n- Using daemon (yes/no): yes\r\n- OS and version: Synology DSM 6\n", "before_files": [{"content": "import re\n\nfrom loguru import logger\nfrom requests.exceptions import RequestException\n\nfrom flexget import plugin\nfrom flexget.components.sites.urlrewriting import UrlRewritingError\nfrom flexget.components.sites.utils import normalize_unicode, torrent_availability\nfrom flexget.entry import Entry\nfrom flexget.event import event\nfrom flexget.plugin import PluginError\nfrom flexget.utils import requests\nfrom flexget.utils.soup import get_soup\nfrom flexget.utils.tools import parse_filesize\n\nlogger = logger.bind(name='torrentday')\n\nCATEGORIES = {\n    'all': 0,\n    # Movies\n    'mov480p': 25,\n    'movHD': 11,\n    'movBD': 5,\n    'movDVD': 3,\n    'movMP4': 21,\n    'movNonEnglish': 22,\n    'movPACKS': 13,\n    'movSDx264': 44,\n    'movX265': 48,\n    'movXVID': 1,\n    # TV\n    'tv480p': 24,\n    'tvBRD': 32,\n    'tvDVD': 31,\n    'tvDVDrip': 33,\n    'tvMOBILE': 46,\n    'tvPACKS': 14,\n    'tvSDx264': 26,\n    'tvHDx264': 7,\n    'tvX265': 34,\n    'tvXVID': 2,\n}\n\n\nclass UrlRewriteTorrentday:\n    \"\"\"\n        Torrentday urlrewriter and search plugin.\n\n        torrentday:\n          uid: xxxxxxxxxxxxx  (required)    NOT YOUR LOGIN. find this in your browser's cookies\n          passkey: xxxxxxxxx  (required)    NOT YOUR PASSWORD. see previous\n          cfduid: xxxxxxxxxx  (required)    AGAIN IN THE COOKIES\n          rss_key: xxxxxxxxx  (required)    get this from your profile page\n          category: xxxxxxxx\n\n          Category can be one of \n            ID from browsing site OR 'name'\n            movies:\n              mov480p, movHD, movBD, movDVD,\n              movMP4, movNonEnglish, movPACKS,\n              movSDx264, movX265, movXVID\n            tv:\n              tv480p, tvBRD, tvDVD, tvDVDrip,\n              tvMOBILE, tvPACKS, tvSDx264, \n              tvHDx264, tvX265, tvXVID\n    \"\"\"\n\n    schema = {\n        'type': 'object',\n        'properties': {\n            'rss_key': {'type': 'string'},\n            'uid': {'type': 'string'},\n            'passkey': {'type': 'string'},\n            'cfduid': {'type': 'string'},\n            'category': {\n                'oneOf': [{'type': 'integer'}, {'type': 'string', 'enum': list(CATEGORIES)}]\n            },\n        },\n        'required': ['rss_key', 'uid', 'passkey', 'cfduid'],\n        'additionalProperties': False,\n    }\n\n    # urlrewriter API\n    def url_rewritable(self, task, entry):\n        url = entry['url']\n        if url.find('.torrent'):\n            return False\n        if url.startswith('https://www.torrentday.com'):\n            return True\n        return False\n\n    # urlrewriter API\n    def url_rewrite(self, task, entry):\n        if 'url' not in entry:\n            logger.error('Didn\\'t actually get a URL...')\n        else:\n            logger.debug('Got the URL: {}', entry['url'])\n        if entry['url'].startswith('https://www.torrentday.com/browse'):\n            # use search\n            results = self.search(task, entry)\n            if not results:\n                raise UrlRewritingError('No search results found')\n            entry['url'] = results[0]['url']\n\n    @plugin.internet(logger)\n    def search(self, task, entry, config=None):\n        \"\"\"\n        Search for name from torrentday.\n        \"\"\"\n\n        categories = config.get('category', 'all')\n        # Make sure categories is a list\n        if not isinstance(categories, list):\n            categories = [categories]\n        # If there are any text categories, turn them into their id number\n        categories = [c if isinstance(c, int) else CATEGORIES[c] for c in categories]\n        params = {\n            'cata': 'yes',\n            'c{}'.format(','.join(str(c) for c in categories)): 1,\n            'clear-new': 1,\n        }\n        entries = set()\n        for search_string in entry.get('search_strings', [entry['title']]):\n\n            url = 'https://www.torrentday.com/t'\n            params['q'] = normalize_unicode(search_string).replace(':', '')\n            cookies = {\n                'uid': config['uid'],\n                'pass': config['passkey'],\n                '__cfduid': config['cfduid'],\n            }\n\n            try:\n                page = requests.get(url, params=params, cookies=cookies).content\n            except RequestException as e:\n                raise PluginError('Could not connect to torrentday: {}'.format(e))\n\n            # the following should avoid table being None due to a malformed\n            # html in td search results\n            soup = get_soup(page).contents[1].contents[1].next.next.nextSibling\n            table = soup.find('table', {'id': 'torrentTable'})\n            if table is None:\n                raise PluginError(\n                    'Search returned by torrentday appears to be empty or malformed.'\n                )\n\n            # the first row is the header so skip it\n            for tr in table.find_all('tr')[1:]:\n                entry = Entry()\n                # find the torrent names\n                td = tr.find('td', {'class': 'torrentNameInfo'})\n                if not td:\n                    logger.warning('Could not find entry torrentNameInfo for {}.', search_string)\n                    continue\n                title = td.find('a')\n                if not title:\n                    logger.warning('Could not determine title for {}.', search_string)\n                    continue\n                entry['title'] = title.contents[0]\n                logger.debug('title: {}', title.contents[0])\n\n                # find download link\n                torrent_url = tr.find('td', {'class': 'ac'})\n                if not torrent_url:\n                    logger.warning('Could not determine download link for {}.', search_string)\n                    continue\n                torrent_url = torrent_url.find('a').get('href')\n\n                # construct download URL\n                torrent_url = (\n                    'https://www.torrentday.com/'\n                    + torrent_url\n                    + '?torrent_pass='\n                    + config['rss_key']\n                )\n                logger.debug('RSS-ified download link: {}', torrent_url)\n                entry['url'] = torrent_url\n\n                # us tr object for seeders/leechers\n                seeders = tr.find('td', {'class': 'ac seedersInfo'})\n                leechers = tr.find('td', {'class': 'ac leechersInfo'})\n                entry['torrent_seeds'] = int(seeders.contents[0].replace(',', ''))\n                entry['torrent_leeches'] = int(leechers.contents[0].replace(',', ''))\n                entry['torrent_availability'] = torrent_availability(\n                    entry['torrent_seeds'], entry['torrent_leeches']\n                )\n\n                # use tr object for size\n                size = tr.find('td', text=re.compile(r'([\\.\\d]+) ([TGMKk]?)B')).contents[0]\n                size = re.search(r'([\\.\\d]+) ([TGMKk]?)B', str(size))\n\n                entry['content_size'] = parse_filesize(size.group(0))\n\n                entries.add(entry)\n\n        return sorted(entries, reverse=True, key=lambda x: x.get('torrent_availability'))\n\n\n@event('plugin.register')\ndef register_plugin():\n    plugin.register(\n        UrlRewriteTorrentday, 'torrentday', interfaces=['urlrewriter', 'search'], api_ver=2\n    )\n", "path": "flexget/components/sites/sites/torrentday.py"}], "after_files": [{"content": "import re\n\nfrom loguru import logger\nfrom requests.exceptions import RequestException\n\nfrom flexget import plugin\nfrom flexget.components.sites.urlrewriting import UrlRewritingError\nfrom flexget.components.sites.utils import normalize_unicode, torrent_availability\nfrom flexget.entry import Entry\nfrom flexget.event import event\nfrom flexget.plugin import PluginError\nfrom flexget.utils import requests\nfrom flexget.utils.soup import get_soup\nfrom flexget.utils.tools import parse_filesize\n\nlogger = logger.bind(name='torrentday')\n\nCATEGORIES = {\n    'all': 0,\n    # Movies\n    'mov4k': 96,\n    'mov480p': 25,\n    'movHD': 11,\n    'movBD': 5,\n    'movDVD': 3,\n    'movMP4': 21,\n    'movNonEnglish': 22,\n    'movPACKS': 13,\n    'movSDx264': 44,\n    'movX265': 48,\n    'movXVID': 1,\n    # TV\n    'tv480p': 24,\n    'tvBRD': 32,\n    'tvDVD': 31,\n    'tvDVDrip': 33,\n    'tvMOBILE': 46,\n    'tvNonEnglish': 82,\n    'tvPACKS': 14,\n    'tvSDx264': 26,\n    'tvHDx264': 7,\n    'tvX265': 34,\n    'tvXVID': 2,\n}\n\n\nclass UrlRewriteTorrentday:\n    \"\"\"\n        Torrentday urlrewriter and search plugin.\n\n        torrentday:\n          uid: xxxxxxxxxxxxx  (required)    NOT YOUR LOGIN. find this in your browser's cookies\n          passkey: xxxxxxxxx  (required)    NOT YOUR PASSWORD. see previous\n          cfduid: xxxxxxxxxx  (required)    AGAIN IN THE COOKIES\n          rss_key: xxxxxxxxx  (required)    get this from your profile page\n          category: xxxxxxxx\n\n          Category can be one of\n            ID from browsing site OR 'name'\n            movies:\n              mov4k, mov480p, movHD, movBD, movDVD,\n              movMP4, movNonEnglish, movPACKS,\n              movSDx264, movX265, movXVID\n            tv:\n              tv480p, tvBRD, tvDVD, tvDVDrip,\n              tvMOBILE, tvNonEnglish, tvPACKS,\n              tvSDx264, tvHDx264, tvX265, tvXVID\n    \"\"\"\n\n    schema = {\n        'type': 'object',\n        'properties': {\n            'rss_key': {'type': 'string'},\n            'uid': {'type': 'string'},\n            'passkey': {'type': 'string'},\n            'cfduid': {'type': 'string'},\n            'category': {\n                'oneOf': [{'type': 'integer'}, {'type': 'string', 'enum': list(CATEGORIES)}]\n            },\n        },\n        'required': ['rss_key', 'uid', 'passkey', 'cfduid'],\n        'additionalProperties': False,\n    }\n\n    # urlrewriter API\n    def url_rewritable(self, task, entry):\n        url = entry['url']\n        if url.find('.torrent'):\n            return False\n        if url.startswith('https://www.torrentday.com'):\n            return True\n        return False\n\n    # urlrewriter API\n    def url_rewrite(self, task, entry):\n        if 'url' not in entry:\n            logger.error('Didn\\'t actually get a URL...')\n        else:\n            logger.debug('Got the URL: {}', entry['url'])\n        if entry['url'].startswith('https://www.torrentday.com/browse'):\n            # use search\n            results = self.search(task, entry)\n            if not results:\n                raise UrlRewritingError('No search results found')\n            entry['url'] = results[0]['url']\n\n    @plugin.internet(logger)\n    def search(self, task, entry, config=None):\n        \"\"\"\n        Search for name from torrentday.\n        \"\"\"\n\n        categories = config.get('category', 'all')\n        # Make sure categories is a list\n        if not isinstance(categories, list):\n            categories = [categories]\n        # If there are any text categories, turn them into their id number\n        categories = [c if isinstance(c, int) else CATEGORIES[c] for c in categories]\n        params = {'cata': 'yes', 'clear-new': 1}\n        params.update({str(c): 1 for c in categories})\n\n        entries = set()\n        for search_string in entry.get('search_strings', [entry['title']]):\n\n            url = 'https://www.torrentday.com/t'\n            params['q'] = normalize_unicode(search_string).replace(':', '')\n            cookies = {\n                'uid': config['uid'],\n                'pass': config['passkey'],\n                '__cfduid': config['cfduid'],\n            }\n\n            try:\n                page = requests.get(url, params=params, cookies=cookies).content\n            except RequestException as e:\n                raise PluginError('Could not connect to torrentday: {}'.format(e))\n\n            # the following should avoid table being None due to a malformed\n            # html in td search results\n            soup = get_soup(page).contents[1].contents[1].contents[1].next.nextSibling\n            table = soup.find('table', {'id': 'torrentTable'})\n            if table is None:\n                raise PluginError(\n                    'Search returned by torrentday appears to be empty or malformed.'\n                )\n\n            # the first row is the header so skip it\n            for tr in table.find_all('tr')[1:]:\n                entry = Entry()\n                # find the torrent names\n                td = tr.find('td', {'class': 'torrentNameInfo'})\n                if not td:\n                    logger.warning('Could not find entry torrentNameInfo for {}.', search_string)\n                    continue\n                title = td.find('a')\n                if not title:\n                    logger.warning('Could not determine title for {}.', search_string)\n                    continue\n                entry['title'] = title.contents[0]\n                logger.debug('title: {}', title.contents[0])\n\n                # find download link\n                torrent_url = tr.find('td', {'class': 'ac'})\n                if not torrent_url:\n                    logger.warning('Could not determine download link for {}.', search_string)\n                    continue\n                torrent_url = torrent_url.find('a').get('href')\n\n                # construct download URL\n                torrent_url = (\n                    'https://www.torrentday.com/'\n                    + torrent_url\n                    + '?torrent_pass='\n                    + config['rss_key']\n                )\n                logger.debug('RSS-ified download link: {}', torrent_url)\n                entry['url'] = torrent_url\n\n                # us tr object for seeders/leechers\n                seeders = tr.find('td', {'class': 'ac seedersInfo'})\n                leechers = tr.find('td', {'class': 'ac leechersInfo'})\n                entry['torrent_seeds'] = int(seeders.contents[0].replace(',', ''))\n                entry['torrent_leeches'] = int(leechers.contents[0].replace(',', ''))\n                entry['torrent_availability'] = torrent_availability(\n                    entry['torrent_seeds'], entry['torrent_leeches']\n                )\n\n                # use tr object for size\n                size = tr.find('td', text=re.compile(r'([\\.\\d]+) ([TGMKk]?)B')).contents[0]\n                size = re.search(r'([\\.\\d]+) ([TGMKk]?)B', str(size))\n\n                entry['content_size'] = parse_filesize(size.group(0))\n\n                entries.add(entry)\n\n        return sorted(entries, reverse=True, key=lambda x: x.get('torrent_availability'))\n\n\n@event('plugin.register')\ndef register_plugin():\n    plugin.register(\n        UrlRewriteTorrentday, 'torrentday', interfaces=['urlrewriter', 'search'], api_ver=2\n    )\n", "path": "flexget/components/sites/sites/torrentday.py"}]}