We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
AniList crashing with custom lists
### Expected behaviour:
Parse AniList by status, ignore custom lists since they are duplicate entries from status lists.

### Actual behaviour:
Trying to parse custom lists cause a crash
### Steps to reproduce:
- Step 1: Try to parse an AniList account with a custom list.

#### Config:
```yaml
Paste FULL config and remove any personal info if config is too long, attach the file to the ticket.
If issue is with a single task, you can get get resulting configuration by running:
  flexget execute --task <NAME> --dump-config
Make sure to redact any personal information (passwords, api keys, etc) !
```
  
#### Log:
<details>
 <summary>(click to expand)</summary>

```
2020-03-18 10:22:25 CRITICAL task_queue                    BUG: Unhandled exception during task queue run loop.
2020-03-18 10:22:25 DEBUG    scheduler                     task finished executing: fill-anime-movies-list
Traceback (most recent call last):
  File "github\flexget\flexget\task_queue.py", line 46, in run
    self.current_task.execute()
  File "github\flexget\flexget\task.py", line 80, in wrapper
    return func(self, *args, **kw)
  File "github\flexget\flexget\task.py", line 697, in execute
    self._execute()
  File "github\flexget\flexget\task.py", line 663, in _execute
    self.__run_task_phase(phase)
  File "github\flexget\flexget\task.py", line 492, in __run_task_phase
    for e in response:
  File "github\flexget\flexget\utils\cached_input.py", line 217, in __iter__
    for item in self.iterable:
  File "github\flexget\flexget\plugins\input\anilist.py", line 133, in on_task_input
    entry['al_list_status'] = list_status['status'].capitalize()
AttributeError: 'NoneType' object has no attribute 'capitalize'
```
</details>

### Additional information:

- FlexGet version: 3.1.34
- Python version: 3.7
- Installation method: git
- Using daemon (yes/no): yes
- OS and version: Win10
- Link to crash log: N/A


--- 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/plugins/input/anilist.py`
Content:
```
1 from loguru import logger
2 
3 from flexget import plugin
4 from flexget.config_schema import one_or_more
5 from flexget.entry import Entry
6 from flexget.event import event
7 from flexget.utils.cached_input import cached
8 from flexget.utils.requests import RequestException
9 
10 logger = logger.bind(name='anilist')
11 
12 LIST_STATUS = ['current', 'planning', 'completed', 'dropped', 'paused', 'repeating']
13 
14 RELEASE_STATUS = ['finished', 'releasing', 'not_yet_released', 'cancelled', 'all']
15 
16 ANIME_FORMAT = ['tv', 'tv_short', 'movie', 'special', 'ova', 'ona', 'all']
17 
18 TRAILER_SOURCE = {
19     'youtube': 'https://www.youtube.com/embed/',
20     'dailymotion': 'https://www.dailymotion.com/embed/video/',
21 }
22 
23 
24 class AniList(object):
25     """" Creates entries for series and movies from your AniList list
26 
27     Syntax:
28     anilist:
29       username: <value>
30       status:
31         - <current|planning|completed|dropped|paused|repeating>
32         - <current|planning|completed|dropped|paused|repeating>
33         ...
34       release_status:
35         - <all|finished|releasing|not_yet_released|cancelled>
36         - <finished|releasing|not_yet_released|cancelled>
37         ...
38       format:
39         - <all|tv|tv_short|movie|special|ova|ona>
40         - <tv|tv_short|movie|special|ova|ona>
41         ...
42     """
43 
44     schema = {
45         'oneOf': [
46             {'type': 'string'},
47             {
48                 'type': 'object',
49                 'properties': {
50                     'username': {'type': 'string'},
51                     'status': one_or_more(
52                         {'type': 'string', 'enum': LIST_STATUS}, unique_items=True
53                     ),
54                     'release_status': one_or_more(
55                         {'type': 'string', 'enum': RELEASE_STATUS}, unique_items=True
56                     ),
57                     'format': one_or_more(
58                         {'type': 'string', 'enum': ANIME_FORMAT}, unique_items=True
59                     ),
60                 },
61                 'required': ['username'],
62                 'additionalProperties': False,
63             },
64         ]
65     }
66 
67     @cached('anilist', persist='2 hours')
68     def on_task_input(self, task, config):
69         if isinstance(config, str):
70             config = {'username': config}
71         selected_list_status = config['status'] if 'status' in config else ['current', 'planning']
72         selected_release_status = (
73             config['release_status'] if 'release_status' in config else ['all']
74         )
75         selected_formats = config['format'] if 'format' in config else ['all']
76 
77         if not isinstance(selected_list_status, list):
78             selected_list_status = [selected_list_status]
79 
80         if not isinstance(selected_release_status, list):
81             selected_release_status = [selected_release_status]
82 
83         if not isinstance(selected_formats, list):
84             selected_formats = [selected_formats]
85 
86         logger.debug('Selected List Status: {}', selected_list_status)
87         logger.debug('Selected Release Status: {}', selected_release_status)
88         logger.debug('Selected Formats: {}', selected_formats)
89 
90         req_variables = {'user': config['username']}
91         req_chunk = 1
92         req_fields = (
93             'status, title{ romaji, english }, synonyms, siteUrl, idMal, format, episodes, '
94             'trailer{ site, id }, coverImage{ large }, bannerImage, genres, tags{ name }, '
95             'externalLinks{ site, url }'
96         )
97         while req_chunk:
98             req_query = (
99                 f'query ($user: String){{ collection: MediaListCollection(userName: $user, '
100                 f'type: ANIME, perChunk: 500, chunk: {req_chunk}, status_in: '
101                 f'[{", ".join([s.upper() for s in selected_list_status])}]) {{ hasNextChunk, '
102                 f'statuses: lists{{ status, list: entries{{ anime: media{{ {req_fields} }}}}}}}}}}'
103             )
104 
105             try:
106                 list_response = task.requests.post(
107                     'https://graphql.anilist.co',
108                     json={'query': req_query, 'variables': req_variables},
109                 )
110             except RequestException as e:
111                 raise plugin.PluginError('Error reading list - {url}'.format(url=e))
112 
113             try:
114                 list_response = list_response.json()['data']
115                 logger.debug('JSON output: {}', list_response)
116                 for list_status in list_response['collection']['statuses']:
117                     for anime in list_status['list']:
118                         anime = anime['anime']
119                         has_selected_release_status = (
120                             anime['status'].lower() in selected_release_status
121                             or 'all' in selected_release_status
122                         )
123                         has_selected_type = (
124                             anime['format'].lower() in selected_formats
125                             or 'all' in selected_formats
126                         )
127                         if has_selected_type and has_selected_release_status:
128                             entry = Entry()
129                             entry['title'] = anime['title']['romaji']
130                             entry['al_title'] = anime['title']
131                             entry['al_format'] = anime['format']
132                             entry['al_release_status'] = anime['status'].capitalize()
133                             entry['al_list_status'] = list_status['status'].capitalize()
134                             entry['alternate_name'] = anime.get('synonyms', [])
135                             if (
136                                 anime['title'].get('english')
137                                 and anime['title'].get('english') != anime['title']['romaji']
138                                 and anime['title'].get('english') not in entry['alternate_name']
139                             ):
140                                 entry['alternate_name'].insert(0, anime['title']['english'])
141                             entry['url'] = anime['siteUrl']
142                             entry['al_idMal'] = anime['idMal']
143                             entry['al_episodes'] = anime['episodes']
144                             entry['al_trailer'] = (
145                                 TRAILER_SOURCE[anime['trailer']['site']] + anime['trailer']['id']
146                                 if anime['trailer']
147                                 else ''
148                             )
149                             entry['al_cover'] = anime['coverImage']['large']
150                             entry['al_banner'] = anime['bannerImage']
151                             entry['al_genres'] = anime['genres']
152                             entry['al_tags'] = [t['name'] for t in anime['tags']]
153                             entry['al_links'] = anime['externalLinks']
154                             if entry.isvalid():
155                                 yield entry
156                 req_chunk = req_chunk + 1 if list_response['collection']['hasNextChunk'] else False
157 
158             except ValueError:
159                 raise plugin.PluginError('Invalid JSON response')
160 
161 
162 @event('plugin.register')
163 def register_plugin():
164     plugin.register(AniList, 'anilist', api_ver=2)
165
```
--- 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/plugins/input/anilist.py b/flexget/plugins/input/anilist.py
--- a/flexget/plugins/input/anilist.py
+++ b/flexget/plugins/input/anilist.py
@@ -26,7 +26,7 @@
 
     Syntax:
     anilist:
-      username: <value>
+      username: <string>
       status:
         - <current|planning|completed|dropped|paused|repeating>
         - <current|planning|completed|dropped|paused|repeating>
@@ -39,6 +39,10 @@
         - <all|tv|tv_short|movie|special|ova|ona>
         - <tv|tv_short|movie|special|ova|ona>
         ...
+      list:
+        - <string>
+        - <string>
+        ...
     """
 
     schema = {
@@ -57,6 +61,7 @@
                     'format': one_or_more(
                         {'type': 'string', 'enum': ANIME_FORMAT}, unique_items=True
                     ),
+                    'list': one_or_more({'type': 'string'}),
                 },
                 'required': ['username'],
                 'additionalProperties': False,
@@ -68,11 +73,10 @@
     def on_task_input(self, task, config):
         if isinstance(config, str):
             config = {'username': config}
-        selected_list_status = config['status'] if 'status' in config else ['current', 'planning']
-        selected_release_status = (
-            config['release_status'] if 'release_status' in config else ['all']
-        )
-        selected_formats = config['format'] if 'format' in config else ['all']
+        selected_list_status = config.get('status', ['current', 'planning'])
+        selected_release_status = config.get('release_status', ['all'])
+        selected_formats = config.get('format', ['all'])
+        selected_list_name = config.get('list', [])
 
         if not isinstance(selected_list_status, list):
             selected_list_status = [selected_list_status]
@@ -83,6 +87,10 @@
         if not isinstance(selected_formats, list):
             selected_formats = [selected_formats]
 
+        if not isinstance(selected_list_name, list):
+            selected_list_name = [selected_list_name]
+        selected_list_name = [i.lower() for i in selected_list_name]
+
         logger.debug('Selected List Status: {}', selected_list_status)
         logger.debug('Selected Release Status: {}', selected_release_status)
         logger.debug('Selected Formats: {}', selected_formats)
@@ -99,7 +107,8 @@
                 f'query ($user: String){{ collection: MediaListCollection(userName: $user, '
                 f'type: ANIME, perChunk: 500, chunk: {req_chunk}, status_in: '
                 f'[{", ".join([s.upper() for s in selected_list_status])}]) {{ hasNextChunk, '
-                f'statuses: lists{{ status, list: entries{{ anime: media{{ {req_fields} }}}}}}}}}}'
+                f'statuses: lists{{ status, name, list: entries{{ anime: media{{ {req_fields}'
+                f' }}}}}}}}}}'
             )
 
             try:
@@ -114,6 +123,11 @@
                 list_response = list_response.json()['data']
                 logger.debug('JSON output: {}', list_response)
                 for list_status in list_response['collection']['statuses']:
+                    if (
+                        selected_list_name
+                        and list_status['name'].lower() not in selected_list_name
+                    ):
+                        continue
                     for anime in list_status['list']:
                         anime = anime['anime']
                         has_selected_release_status = (
@@ -130,7 +144,12 @@
                             entry['al_title'] = anime['title']
                             entry['al_format'] = anime['format']
                             entry['al_release_status'] = anime['status'].capitalize()
-                            entry['al_list_status'] = list_status['status'].capitalize()
+                            entry['al_list'] = list_status['name']
+                            entry['al_list_status'] = (
+                                list_status['status'].capitalize()
+                                if list_status.get('status')
+                                else ''
+                            )
                             entry['alternate_name'] = anime.get('synonyms', [])
                             if (
                                 anime['title'].get('english')

{"golden_diff": "diff --git a/flexget/plugins/input/anilist.py b/flexget/plugins/input/anilist.py\n--- a/flexget/plugins/input/anilist.py\n+++ b/flexget/plugins/input/anilist.py\n@@ -26,7 +26,7 @@\n \n     Syntax:\n     anilist:\n-      username: <value>\n+      username: <string>\n       status:\n         - <current|planning|completed|dropped|paused|repeating>\n         - <current|planning|completed|dropped|paused|repeating>\n@@ -39,6 +39,10 @@\n         - <all|tv|tv_short|movie|special|ova|ona>\n         - <tv|tv_short|movie|special|ova|ona>\n         ...\n+      list:\n+        - <string>\n+        - <string>\n+        ...\n     \"\"\"\n \n     schema = {\n@@ -57,6 +61,7 @@\n                     'format': one_or_more(\n                         {'type': 'string', 'enum': ANIME_FORMAT}, unique_items=True\n                     ),\n+                    'list': one_or_more({'type': 'string'}),\n                 },\n                 'required': ['username'],\n                 'additionalProperties': False,\n@@ -68,11 +73,10 @@\n     def on_task_input(self, task, config):\n         if isinstance(config, str):\n             config = {'username': config}\n-        selected_list_status = config['status'] if 'status' in config else ['current', 'planning']\n-        selected_release_status = (\n-            config['release_status'] if 'release_status' in config else ['all']\n-        )\n-        selected_formats = config['format'] if 'format' in config else ['all']\n+        selected_list_status = config.get('status', ['current', 'planning'])\n+        selected_release_status = config.get('release_status', ['all'])\n+        selected_formats = config.get('format', ['all'])\n+        selected_list_name = config.get('list', [])\n \n         if not isinstance(selected_list_status, list):\n             selected_list_status = [selected_list_status]\n@@ -83,6 +87,10 @@\n         if not isinstance(selected_formats, list):\n             selected_formats = [selected_formats]\n \n+        if not isinstance(selected_list_name, list):\n+            selected_list_name = [selected_list_name]\n+        selected_list_name = [i.lower() for i in selected_list_name]\n+\n         logger.debug('Selected List Status: {}', selected_list_status)\n         logger.debug('Selected Release Status: {}', selected_release_status)\n         logger.debug('Selected Formats: {}', selected_formats)\n@@ -99,7 +107,8 @@\n                 f'query ($user: String){{ collection: MediaListCollection(userName: $user, '\n                 f'type: ANIME, perChunk: 500, chunk: {req_chunk}, status_in: '\n                 f'[{\", \".join([s.upper() for s in selected_list_status])}]) {{ hasNextChunk, '\n-                f'statuses: lists{{ status, list: entries{{ anime: media{{ {req_fields} }}}}}}}}}}'\n+                f'statuses: lists{{ status, name, list: entries{{ anime: media{{ {req_fields}'\n+                f' }}}}}}}}}}'\n             )\n \n             try:\n@@ -114,6 +123,11 @@\n                 list_response = list_response.json()['data']\n                 logger.debug('JSON output: {}', list_response)\n                 for list_status in list_response['collection']['statuses']:\n+                    if (\n+                        selected_list_name\n+                        and list_status['name'].lower() not in selected_list_name\n+                    ):\n+                        continue\n                     for anime in list_status['list']:\n                         anime = anime['anime']\n                         has_selected_release_status = (\n@@ -130,7 +144,12 @@\n                             entry['al_title'] = anime['title']\n                             entry['al_format'] = anime['format']\n                             entry['al_release_status'] = anime['status'].capitalize()\n-                            entry['al_list_status'] = list_status['status'].capitalize()\n+                            entry['al_list'] = list_status['name']\n+                            entry['al_list_status'] = (\n+                                list_status['status'].capitalize()\n+                                if list_status.get('status')\n+                                else ''\n+                            )\n                             entry['alternate_name'] = anime.get('synonyms', [])\n                             if (\n                                 anime['title'].get('english')\n", "issue": "AniList crashing with custom lists\n### Expected behaviour:\r\nParse AniList by status, ignore custom lists since they are duplicate entries from status lists.\r\n\r\n### Actual behaviour:\r\nTrying to parse custom lists cause a crash\r\n### Steps to reproduce:\r\n- Step 1: Try to parse an AniList account with a custom list.\r\n\r\n#### Config:\r\n```yaml\r\nPaste FULL config and remove any personal info if config is too long, attach the file to the ticket.\r\nIf issue is with a single task, you can get get resulting configuration by running:\r\n  flexget execute --task <NAME> --dump-config\r\nMake sure to redact any personal information (passwords, api keys, etc) !\r\n```\r\n  \r\n#### Log:\r\n<details>\r\n <summary>(click to expand)</summary>\r\n\r\n```\r\n2020-03-18 10:22:25 CRITICAL task_queue                    BUG: Unhandled exception during task queue run loop.\r\n2020-03-18 10:22:25 DEBUG    scheduler                     task finished executing: fill-anime-movies-list\r\nTraceback (most recent call last):\r\n  File \"github\\flexget\\flexget\\task_queue.py\", line 46, in run\r\n    self.current_task.execute()\r\n  File \"github\\flexget\\flexget\\task.py\", line 80, in wrapper\r\n    return func(self, *args, **kw)\r\n  File \"github\\flexget\\flexget\\task.py\", line 697, in execute\r\n    self._execute()\r\n  File \"github\\flexget\\flexget\\task.py\", line 663, in _execute\r\n    self.__run_task_phase(phase)\r\n  File \"github\\flexget\\flexget\\task.py\", line 492, in __run_task_phase\r\n    for e in response:\r\n  File \"github\\flexget\\flexget\\utils\\cached_input.py\", line 217, in __iter__\r\n    for item in self.iterable:\r\n  File \"github\\flexget\\flexget\\plugins\\input\\anilist.py\", line 133, in on_task_input\r\n    entry['al_list_status'] = list_status['status'].capitalize()\r\nAttributeError: 'NoneType' object has no attribute 'capitalize'\r\n```\r\n</details>\r\n\r\n### Additional information:\r\n\r\n- FlexGet version: 3.1.34\r\n- Python version: 3.7\r\n- Installation method: git\r\n- Using daemon (yes/no): yes\r\n- OS and version: Win10\r\n- Link to crash log: N/A\r\n\n", "before_files": [{"content": "from loguru import logger\n\nfrom flexget import plugin\nfrom flexget.config_schema import one_or_more\nfrom flexget.entry import Entry\nfrom flexget.event import event\nfrom flexget.utils.cached_input import cached\nfrom flexget.utils.requests import RequestException\n\nlogger = logger.bind(name='anilist')\n\nLIST_STATUS = ['current', 'planning', 'completed', 'dropped', 'paused', 'repeating']\n\nRELEASE_STATUS = ['finished', 'releasing', 'not_yet_released', 'cancelled', 'all']\n\nANIME_FORMAT = ['tv', 'tv_short', 'movie', 'special', 'ova', 'ona', 'all']\n\nTRAILER_SOURCE = {\n    'youtube': 'https://www.youtube.com/embed/',\n    'dailymotion': 'https://www.dailymotion.com/embed/video/',\n}\n\n\nclass AniList(object):\n    \"\"\"\" Creates entries for series and movies from your AniList list\n\n    Syntax:\n    anilist:\n      username: <value>\n      status:\n        - <current|planning|completed|dropped|paused|repeating>\n        - <current|planning|completed|dropped|paused|repeating>\n        ...\n      release_status:\n        - <all|finished|releasing|not_yet_released|cancelled>\n        - <finished|releasing|not_yet_released|cancelled>\n        ...\n      format:\n        - <all|tv|tv_short|movie|special|ova|ona>\n        - <tv|tv_short|movie|special|ova|ona>\n        ...\n    \"\"\"\n\n    schema = {\n        'oneOf': [\n            {'type': 'string'},\n            {\n                'type': 'object',\n                'properties': {\n                    'username': {'type': 'string'},\n                    'status': one_or_more(\n                        {'type': 'string', 'enum': LIST_STATUS}, unique_items=True\n                    ),\n                    'release_status': one_or_more(\n                        {'type': 'string', 'enum': RELEASE_STATUS}, unique_items=True\n                    ),\n                    'format': one_or_more(\n                        {'type': 'string', 'enum': ANIME_FORMAT}, unique_items=True\n                    ),\n                },\n                'required': ['username'],\n                'additionalProperties': False,\n            },\n        ]\n    }\n\n    @cached('anilist', persist='2 hours')\n    def on_task_input(self, task, config):\n        if isinstance(config, str):\n            config = {'username': config}\n        selected_list_status = config['status'] if 'status' in config else ['current', 'planning']\n        selected_release_status = (\n            config['release_status'] if 'release_status' in config else ['all']\n        )\n        selected_formats = config['format'] if 'format' in config else ['all']\n\n        if not isinstance(selected_list_status, list):\n            selected_list_status = [selected_list_status]\n\n        if not isinstance(selected_release_status, list):\n            selected_release_status = [selected_release_status]\n\n        if not isinstance(selected_formats, list):\n            selected_formats = [selected_formats]\n\n        logger.debug('Selected List Status: {}', selected_list_status)\n        logger.debug('Selected Release Status: {}', selected_release_status)\n        logger.debug('Selected Formats: {}', selected_formats)\n\n        req_variables = {'user': config['username']}\n        req_chunk = 1\n        req_fields = (\n            'status, title{ romaji, english }, synonyms, siteUrl, idMal, format, episodes, '\n            'trailer{ site, id }, coverImage{ large }, bannerImage, genres, tags{ name }, '\n            'externalLinks{ site, url }'\n        )\n        while req_chunk:\n            req_query = (\n                f'query ($user: String){{ collection: MediaListCollection(userName: $user, '\n                f'type: ANIME, perChunk: 500, chunk: {req_chunk}, status_in: '\n                f'[{\", \".join([s.upper() for s in selected_list_status])}]) {{ hasNextChunk, '\n                f'statuses: lists{{ status, list: entries{{ anime: media{{ {req_fields} }}}}}}}}}}'\n            )\n\n            try:\n                list_response = task.requests.post(\n                    'https://graphql.anilist.co',\n                    json={'query': req_query, 'variables': req_variables},\n                )\n            except RequestException as e:\n                raise plugin.PluginError('Error reading list - {url}'.format(url=e))\n\n            try:\n                list_response = list_response.json()['data']\n                logger.debug('JSON output: {}', list_response)\n                for list_status in list_response['collection']['statuses']:\n                    for anime in list_status['list']:\n                        anime = anime['anime']\n                        has_selected_release_status = (\n                            anime['status'].lower() in selected_release_status\n                            or 'all' in selected_release_status\n                        )\n                        has_selected_type = (\n                            anime['format'].lower() in selected_formats\n                            or 'all' in selected_formats\n                        )\n                        if has_selected_type and has_selected_release_status:\n                            entry = Entry()\n                            entry['title'] = anime['title']['romaji']\n                            entry['al_title'] = anime['title']\n                            entry['al_format'] = anime['format']\n                            entry['al_release_status'] = anime['status'].capitalize()\n                            entry['al_list_status'] = list_status['status'].capitalize()\n                            entry['alternate_name'] = anime.get('synonyms', [])\n                            if (\n                                anime['title'].get('english')\n                                and anime['title'].get('english') != anime['title']['romaji']\n                                and anime['title'].get('english') not in entry['alternate_name']\n                            ):\n                                entry['alternate_name'].insert(0, anime['title']['english'])\n                            entry['url'] = anime['siteUrl']\n                            entry['al_idMal'] = anime['idMal']\n                            entry['al_episodes'] = anime['episodes']\n                            entry['al_trailer'] = (\n                                TRAILER_SOURCE[anime['trailer']['site']] + anime['trailer']['id']\n                                if anime['trailer']\n                                else ''\n                            )\n                            entry['al_cover'] = anime['coverImage']['large']\n                            entry['al_banner'] = anime['bannerImage']\n                            entry['al_genres'] = anime['genres']\n                            entry['al_tags'] = [t['name'] for t in anime['tags']]\n                            entry['al_links'] = anime['externalLinks']\n                            if entry.isvalid():\n                                yield entry\n                req_chunk = req_chunk + 1 if list_response['collection']['hasNextChunk'] else False\n\n            except ValueError:\n                raise plugin.PluginError('Invalid JSON response')\n\n\n@event('plugin.register')\ndef register_plugin():\n    plugin.register(AniList, 'anilist', api_ver=2)\n", "path": "flexget/plugins/input/anilist.py"}], "after_files": [{"content": "from loguru import logger\n\nfrom flexget import plugin\nfrom flexget.config_schema import one_or_more\nfrom flexget.entry import Entry\nfrom flexget.event import event\nfrom flexget.utils.cached_input import cached\nfrom flexget.utils.requests import RequestException\n\nlogger = logger.bind(name='anilist')\n\nLIST_STATUS = ['current', 'planning', 'completed', 'dropped', 'paused', 'repeating']\n\nRELEASE_STATUS = ['finished', 'releasing', 'not_yet_released', 'cancelled', 'all']\n\nANIME_FORMAT = ['tv', 'tv_short', 'movie', 'special', 'ova', 'ona', 'all']\n\nTRAILER_SOURCE = {\n    'youtube': 'https://www.youtube.com/embed/',\n    'dailymotion': 'https://www.dailymotion.com/embed/video/',\n}\n\n\nclass AniList(object):\n    \"\"\"\" Creates entries for series and movies from your AniList list\n\n    Syntax:\n    anilist:\n      username: <string>\n      status:\n        - <current|planning|completed|dropped|paused|repeating>\n        - <current|planning|completed|dropped|paused|repeating>\n        ...\n      release_status:\n        - <all|finished|releasing|not_yet_released|cancelled>\n        - <finished|releasing|not_yet_released|cancelled>\n        ...\n      format:\n        - <all|tv|tv_short|movie|special|ova|ona>\n        - <tv|tv_short|movie|special|ova|ona>\n        ...\n      list:\n        - <string>\n        - <string>\n        ...\n    \"\"\"\n\n    schema = {\n        'oneOf': [\n            {'type': 'string'},\n            {\n                'type': 'object',\n                'properties': {\n                    'username': {'type': 'string'},\n                    'status': one_or_more(\n                        {'type': 'string', 'enum': LIST_STATUS}, unique_items=True\n                    ),\n                    'release_status': one_or_more(\n                        {'type': 'string', 'enum': RELEASE_STATUS}, unique_items=True\n                    ),\n                    'format': one_or_more(\n                        {'type': 'string', 'enum': ANIME_FORMAT}, unique_items=True\n                    ),\n                    'list': one_or_more({'type': 'string'}),\n                },\n                'required': ['username'],\n                'additionalProperties': False,\n            },\n        ]\n    }\n\n    @cached('anilist', persist='2 hours')\n    def on_task_input(self, task, config):\n        if isinstance(config, str):\n            config = {'username': config}\n        selected_list_status = config.get('status', ['current', 'planning'])\n        selected_release_status = config.get('release_status', ['all'])\n        selected_formats = config.get('format', ['all'])\n        selected_list_name = config.get('list', [])\n\n        if not isinstance(selected_list_status, list):\n            selected_list_status = [selected_list_status]\n\n        if not isinstance(selected_release_status, list):\n            selected_release_status = [selected_release_status]\n\n        if not isinstance(selected_formats, list):\n            selected_formats = [selected_formats]\n\n        if not isinstance(selected_list_name, list):\n            selected_list_name = [selected_list_name]\n        selected_list_name = [i.lower() for i in selected_list_name]\n\n        logger.debug('Selected List Status: {}', selected_list_status)\n        logger.debug('Selected Release Status: {}', selected_release_status)\n        logger.debug('Selected Formats: {}', selected_formats)\n\n        req_variables = {'user': config['username']}\n        req_chunk = 1\n        req_fields = (\n            'status, title{ romaji, english }, synonyms, siteUrl, idMal, format, episodes, '\n            'trailer{ site, id }, coverImage{ large }, bannerImage, genres, tags{ name }, '\n            'externalLinks{ site, url }'\n        )\n        while req_chunk:\n            req_query = (\n                f'query ($user: String){{ collection: MediaListCollection(userName: $user, '\n                f'type: ANIME, perChunk: 500, chunk: {req_chunk}, status_in: '\n                f'[{\", \".join([s.upper() for s in selected_list_status])}]) {{ hasNextChunk, '\n                f'statuses: lists{{ status, name, list: entries{{ anime: media{{ {req_fields}'\n                f' }}}}}}}}}}'\n            )\n\n            try:\n                list_response = task.requests.post(\n                    'https://graphql.anilist.co',\n                    json={'query': req_query, 'variables': req_variables},\n                )\n            except RequestException as e:\n                raise plugin.PluginError('Error reading list - {url}'.format(url=e))\n\n            try:\n                list_response = list_response.json()['data']\n                logger.debug('JSON output: {}', list_response)\n                for list_status in list_response['collection']['statuses']:\n                    if (\n                        selected_list_name\n                        and list_status['name'].lower() not in selected_list_name\n                    ):\n                        continue\n                    for anime in list_status['list']:\n                        anime = anime['anime']\n                        has_selected_release_status = (\n                            anime['status'].lower() in selected_release_status\n                            or 'all' in selected_release_status\n                        )\n                        has_selected_type = (\n                            anime['format'].lower() in selected_formats\n                            or 'all' in selected_formats\n                        )\n                        if has_selected_type and has_selected_release_status:\n                            entry = Entry()\n                            entry['title'] = anime['title']['romaji']\n                            entry['al_title'] = anime['title']\n                            entry['al_format'] = anime['format']\n                            entry['al_release_status'] = anime['status'].capitalize()\n                            entry['al_list'] = list_status['name']\n                            entry['al_list_status'] = (\n                                list_status['status'].capitalize()\n                                if list_status.get('status')\n                                else ''\n                            )\n                            entry['alternate_name'] = anime.get('synonyms', [])\n                            if (\n                                anime['title'].get('english')\n                                and anime['title'].get('english') != anime['title']['romaji']\n                                and anime['title'].get('english') not in entry['alternate_name']\n                            ):\n                                entry['alternate_name'].insert(0, anime['title']['english'])\n                            entry['url'] = anime['siteUrl']\n                            entry['al_idMal'] = anime['idMal']\n                            entry['al_episodes'] = anime['episodes']\n                            entry['al_trailer'] = (\n                                TRAILER_SOURCE[anime['trailer']['site']] + anime['trailer']['id']\n                                if anime['trailer']\n                                else ''\n                            )\n                            entry['al_cover'] = anime['coverImage']['large']\n                            entry['al_banner'] = anime['bannerImage']\n                            entry['al_genres'] = anime['genres']\n                            entry['al_tags'] = [t['name'] for t in anime['tags']]\n                            entry['al_links'] = anime['externalLinks']\n                            if entry.isvalid():\n                                yield entry\n                req_chunk = req_chunk + 1 if list_response['collection']['hasNextChunk'] else False\n\n            except ValueError:\n                raise plugin.PluginError('Invalid JSON response')\n\n\n@event('plugin.register')\ndef register_plugin():\n    plugin.register(AniList, 'anilist', api_ver=2)\n", "path": "flexget/plugins/input/anilist.py"}]}