anipy-cli 3.4.8__py3-none-any.whl → 3.8.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
anipy_cli/config.py CHANGED
@@ -42,6 +42,10 @@ class Config:
42
42
  def _mal_local_user_list_path(self) -> Path:
43
43
  return self.user_files_path / "mal_list.json"
44
44
 
45
+ @property
46
+ def _anilist_local_user_list_path(self) -> Path:
47
+ return self.user_files_path / "anilist_list.json"
48
+
45
49
  @property
46
50
  def download_folder_path(self) -> Path:
47
51
  """Path to your download folder/directory.
@@ -66,13 +70,13 @@ class Config:
66
70
  def providers(self) -> Dict[str, List[str]]:
67
71
  """A list of pairs defining which providers will search for anime
68
72
  in different parts of the program. Configurable areas are as follows:
69
- default (and history), download (-D), seasonal (-S), binge (-B) and mal
70
- (-M) The example will show you how it is done! Please note that for seasonal
73
+ default (and history), download (-D), seasonal (-S), binge (-B), anilist (-A)
74
+ and mal (-M) The example will show you how it is done! Please note that for seasonal
71
75
  search always the first provider that supports it is used.
72
76
 
73
77
  For an updated list of providers look here: https://sdaqo.github.io/anipy-cli/availabilty
74
78
 
75
- Supported providers (as of $version): animekai
79
+ Supported providers (as of $version): allanime, animekai (animekai is not functional for now), native (filesystem provider, set root in provider_urls config)
76
80
 
77
81
  Examples:
78
82
  providers:
@@ -81,13 +85,15 @@ class Config:
81
85
  seasonal: ["provider3"]
82
86
  binge: ["provider4"]
83
87
  mal: ["provider2", "provider3"]
88
+ anilist: ["provider1"]
84
89
  """
85
90
  defaults = {
86
- "default": ["animekai"],
87
- "download": ["animekai"],
88
- "seasonal": ["animekai"],
89
- "binge": ["animekai"],
90
- "mal": ["animekai"],
91
+ "default": ["allanime"],
92
+ "download": ["allanime"],
93
+ "seasonal": ["allanime"],
94
+ "binge": ["allanime"],
95
+ "mal": ["allanime"],
96
+ "anilist": ["allanime"],
91
97
  }
92
98
 
93
99
  value = self._get_value("providers", defaults, dict)
@@ -100,9 +106,12 @@ class Config:
100
106
  def provider_urls(self) -> Dict[str, str]:
101
107
  """A list of pairs to override the default urls that providers use.
102
108
 
109
+ Note: the native provider accepts a path instead of a url, it defaults to ~/Videos
110
+
103
111
  Examples:
104
112
  provider_urls:
105
113
  gogoanime: "https://gogoanime3.co"
114
+ native: "~/Videos"
106
115
  provider_urls: {} # do not override any urls
107
116
  """
108
117
 
@@ -227,9 +236,9 @@ class Config:
227
236
  """With this option you can define scripts that run after a file
228
237
  has been downloaded. As with the 'providers' option, you can configure
229
238
  different behaviour, depending on which part of anipy-cli the download occurs.
230
- Configurable areas are as follows: default (and history), download (-D), seasonal (-S)
231
- and mal (-M). The example will show you how it is done! Please note that if you define
232
- several scripts for one area, they will run in the order you put them in the list.
239
+ Configurable areas are as follows: default (and history), download (-D), seasonal (-S),
240
+ anilist (-A) and mal (-M). The example will show you how it is done! Please note that
241
+ if you define several scripts for one area, they will run in the order you put them in the list.
233
242
  You can also define a timeout (in seconds), after which a script will be terminated,
234
243
  if set to null there will be no timeout and any script will run forever.
235
244
 
@@ -254,7 +263,8 @@ class Config:
254
263
  "download": [],
255
264
  "seasonal": [],
256
265
  "mal": [],
257
- "timeout": None
266
+ "anilist": [],
267
+ "timeout": None,
258
268
  }
259
269
 
260
270
  value = self._get_value("post_download_scripts", defaults, dict)
@@ -279,6 +289,11 @@ class Config:
279
289
  """Your MyAnimeList username for MAL mode."""
280
290
  return self._get_value("mal_user", "", str)
281
291
 
292
+ @property
293
+ def anilist_token(self) -> str:
294
+ """Your AniList access token for AniList mode."""
295
+ return self._get_value("anilist_token", "", str)
296
+
282
297
  @property
283
298
  def mal_password(self) -> str:
284
299
  """Your MyAnimeList password for MAL mode.
@@ -289,55 +304,55 @@ class Config:
289
304
  return self._get_value("mal_password", "", str)
290
305
 
291
306
  @property
292
- def mal_ignore_tag(self) -> str:
307
+ def tracker_ignore_tag(self) -> str:
293
308
  """All anime in your MyAnimeList with this tag will be ignored by
294
309
  anipy-cli.
295
310
 
296
311
  Examples:
297
- mal_ignore_tag: ignore # all anime with ignore tag will be ignored
298
- mal_ignore_tag: "" # no anime will be ignored
312
+ tracker_ignore_tag: ignore # all anime with ignore tag will be ignored
313
+ tracker_ignore_tag: "" # no anime will be ignored
299
314
  """
300
- return self._get_value("mal_ignore_tag", "ignore", str)
315
+ return self._get_value("tracker_ignore_tag", "ignore", str)
301
316
 
302
317
  @property
303
- def mal_dub_tag(self) -> str:
304
- """All anime in your MyAnimeList with this tag will be switched over to
305
- dub in MAL mode, if the dub is available. If you do not specify a tag,
306
- anipy-cli will use `preferred_type` to choose dub or sub in MAL mode.
318
+ def tracker_dub_tag(self) -> str:
319
+ """All anime in your Anime Tracker with this tag will be switched over to
320
+ dub in tracker mode, if the dub is available. If you do not specify a tag,
321
+ anipy-cli will use `preferred_type` to choose dub or sub in tracker mode.
307
322
 
308
323
  Examples:
309
- mal_dub_tag: dub # all anime with this tag will be switched to dub
310
- mal_dub_tag: "" # no anime will be switched to dub, except you have preferred_type on dub
324
+ tracker_dub_tag: dub # all anime with this tag will be switched to dub
325
+ tracker_dub_tag: "" # no anime will be switched to dub, except you have preferred_type on dub
311
326
  """
312
- return self._get_value("mal_dub_tag", "dub", str)
327
+ return self._get_value("tracker_dub_tag", "dub", str)
313
328
 
314
329
  @property
315
- def mal_tags(self) -> List[str]:
316
- """Custom tags to tag all anime in your MyAnimeList that are
330
+ def tracker_tags(self) -> List[str]:
331
+ """Custom tags to tag all anime in your Anime Tracker that are
317
332
  altered/added by anipy-cli.
318
333
 
319
334
  Examples:
320
- mal_tags: ["anipy-cli"] # tag all anime with anipy-cli
321
- mal_tags: ["anipy-cli", "important"] # tag all anime with anipy-cli and important
322
- mal_tags: null or mal_tags: [] # Do not tag the anime
335
+ tracker_tags: ["anipy-cli"] # tag all anime with anipy-cli
336
+ tracker_tags: ["anipy-cli", "important"] # tag all anime with anipy-cli and important
337
+ tracker_tags: null or tracker_tags: [] # Do not tag the anime
323
338
  """
324
- return self._get_value("mal_tags", [], list)
339
+ return self._get_value("tracker_tags", [], list)
325
340
 
326
341
  @property
327
- def mal_status_categories(self) -> List[str]:
328
- """Status categories of your MyAnimeList that anipy-cli uses for
342
+ def tracker_status_categories(self) -> List[str]:
343
+ """Status categories of your Anime Tracker that anipy-cli uses for
329
344
  downloading/watching new episodes listing anime in your list and stuff
330
345
  like that. Normally the watching catagory should be enough as you would
331
346
  normally put anime you currently watch in the watching catagory.
332
347
 
333
348
  Valid values are: watching, completed, on_hold, dropped, plan_to_watch
334
349
  """
335
- return self._get_value("mal_status_categories", ["watching"], list)
350
+ return self._get_value("tracker_status_categories", ["watching"], list)
336
351
 
337
352
  @property
338
- def mal_mapping_min_similarity(self) -> float:
353
+ def tracker_mapping_min_similarity(self) -> float:
339
354
  """
340
- The minumum similarity between titles when mapping anime in MAL mode.
355
+ The minumum similarity between titles when mapping anime in tracker mode.
341
356
  This is a decimal number from 0 - 1, 1 meaning 100% match and 0 meaning all characters are different.
342
357
  If the similarity of a map is below the threshold you will be prompted for a manual map.
343
358
 
@@ -347,24 +362,24 @@ class Config:
347
362
 
348
363
  If you are interested, the algorithm being used here is this: https://en.wikipedia.org/wiki/Levenshtein_distance
349
364
  """
350
- return self._get_value("mal_mapping_min_similarity", 0.8, float)
365
+ return self._get_value("tracker_mapping_min_similarity", 0.8, float)
351
366
 
352
367
  @property
353
- def mal_mapping_use_alternatives(self) -> bool:
368
+ def tracker_mapping_use_alternatives(self) -> bool:
354
369
  """Check alternative names when mapping anime.
355
370
 
356
371
  If turned on this will slow down mapping but provide better
357
372
  chances of finding a match.
358
373
  """
359
- return self._get_value("mal_mapping_use_alternatives", True, bool)
374
+ return self._get_value("tracker_mapping_use_alternatives", True, bool)
360
375
 
361
376
  @property
362
- def mal_mapping_use_filters(self) -> bool:
377
+ def tracker_mapping_use_filters(self) -> bool:
363
378
  """Use filters (e.g. year, season etc.) of providers to narrow down the
364
379
  results, this will lead to more accurate mapping, but provide wrong
365
380
  results if the filters of the provider do not work properly or if anime
366
381
  are not correctly marked with the correct data."""
367
- return self._get_value("mal_mapping_use_filters", True, bool)
382
+ return self._get_value("tracker_mapping_use_filters", True, bool)
368
383
 
369
384
  @property
370
385
  def auto_sync_mal_to_seasonals(self) -> bool:
@@ -414,7 +429,7 @@ class Config:
414
429
  except RuntimeError:
415
430
  return fallback
416
431
 
417
- def _get_value(self, key: str, fallback, _type: Type) -> Any:
432
+ def _get_value(self, key: str, fallback: Any, _type: Type) -> Any:
418
433
  value = self._yaml_conf.get(key, fallback)
419
434
  if isinstance(value, _type):
420
435
  return value
@@ -4,6 +4,7 @@ from anipy_cli.arg_parser import CliArgs
4
4
  from anipy_cli.colors import color, colors
5
5
  from anipy_cli.config import Config
6
6
  from anipy_cli.util import DotSpinner, get_download_path, get_post_download_scripts_hook
7
+ import anipy_cli.logger as logger
7
8
 
8
9
  from anipy_api.anime import Anime
9
10
  from anipy_api.download import Downloader
@@ -32,7 +33,7 @@ class DownloadComponent:
32
33
  """
33
34
 
34
35
  def __init__(self, options: CliArgs, dl_path: Path, mode: str) -> None:
35
- self.options = options
36
+ self.options = options
36
37
  self.dl_path = dl_path
37
38
  self.mode = mode
38
39
 
@@ -41,6 +42,7 @@ class DownloadComponent:
41
42
  picked: List[Tuple[Anime, LanguageTypeEnum, List[Episode]]],
42
43
  after_success_ep: SuccessfulEpDownload = lambda anime, ep, lang: None,
43
44
  only_skip_ep_on_err: bool = False,
45
+ sub_only: bool = False,
44
46
  ) -> List[Tuple[Anime, Episode]]:
45
47
  """
46
48
  Attributes:
@@ -53,11 +55,13 @@ class DownloadComponent:
53
55
  def progress_indicator(percentage: float):
54
56
  s.set_text(f"Progress: {percentage:.1f}%")
55
57
 
56
- def info_display(message: str):
58
+ def info_display(message: str, exc_info: BaseException | None = None):
59
+ logger.info(message, exc_info, exc_info is not None)
57
60
  s.write(f"> {message}")
58
61
 
59
- def error_display(message: str):
60
- s.write(color(colors.RED, "! ", message))
62
+ def error_display(message: str, exc_info: BaseException | None = None):
63
+ logger.error(message, exc_info)
64
+ s.write(f"{colors.RED}! {message}{colors.END}")
61
65
 
62
66
  downloader = Downloader(progress_indicator, info_display, error_display)
63
67
 
@@ -72,6 +76,7 @@ class DownloadComponent:
72
76
  eps,
73
77
  after_success_ep,
74
78
  only_skip_ep_on_err,
79
+ sub_only,
75
80
  )
76
81
 
77
82
  return failed
@@ -85,12 +90,18 @@ class DownloadComponent:
85
90
  eps: List[Episode],
86
91
  after_success_ep: SuccessfulEpDownload = lambda anime, ep, lang: None,
87
92
  only_skip_ep_on_err: bool = False,
93
+ sub_only: bool = False,
88
94
  ) -> List[Tuple[Anime, Episode]]:
89
95
  fails = []
90
96
  for ep in eps:
91
97
  try:
92
- self.download_ep(spinner, downloader, anime, lang, ep)
93
- except Exception as e:
98
+ self.download_ep(spinner, downloader, anime, lang, ep, sub_only)
99
+ except Exception as anime_download_error:
100
+ # Log it first so we don't run into another error below
101
+ logger.error(
102
+ f"Error downloading episode {ep} of {anime.name}. Skipped.",
103
+ anime_download_error,
104
+ )
94
105
  if only_skip_ep_on_err:
95
106
  error_msg = f"! Issues downloading episode {ep} of {anime.name}. Skipping..."
96
107
  else:
@@ -98,7 +109,7 @@ class DownloadComponent:
98
109
  spinner.write(
99
110
  color(
100
111
  colors.RED,
101
- f"! Error: {e}\n",
112
+ f"! Error: {anime_download_error}\n",
102
113
  error_msg,
103
114
  )
104
115
  )
@@ -117,6 +128,7 @@ class DownloadComponent:
117
128
  anime: Anime,
118
129
  lang: LanguageTypeEnum,
119
130
  ep: Episode,
131
+ sub_only: bool = False,
120
132
  ):
121
133
  config = Config()
122
134
 
@@ -132,19 +144,33 @@ class DownloadComponent:
132
144
 
133
145
  stream = anime.get_video(ep, lang, preferred_quality=self.options.quality)
134
146
 
135
- spinner.write(
136
- f"> Downloading Episode {stream.episode} of {anime.name} ({lang})"
147
+ if stream is None:
148
+ logger.info("Could not find a stream")
149
+ DownloadComponent.serve_download_errors(
150
+ [(anime, ep)], only_skip_ep_on_err=True
151
+ )
152
+ return
153
+
154
+ download_message_update = (
155
+ f"Downloading Episode {stream.episode} of {anime.name} ({lang})"
137
156
  )
157
+ logger.info(download_message_update)
158
+ spinner.write(f"> {download_message_update}")
138
159
 
139
160
  spinner.set_text("Downloading...")
140
161
 
141
- downloader.download(
142
- stream,
143
- get_download_path(anime, stream, parent_directory=self.dl_path),
144
- container=config.remux_to,
145
- ffmpeg=self.options.ffmpeg or config.ffmpeg_hls,
146
- post_dl_cb=get_post_download_scripts_hook(self.mode, anime, spinner)
147
- )
162
+ if not sub_only:
163
+ downloader.download(
164
+ stream,
165
+ get_download_path(anime, stream, parent_directory=self.dl_path),
166
+ container=config.remux_to,
167
+ ffmpeg=self.options.ffmpeg or config.ffmpeg_hls,
168
+ post_dl_cb=get_post_download_scripts_hook(self.mode, anime, spinner),
169
+ )
170
+ else:
171
+ downloader.download_sub(
172
+ stream, get_download_path(anime, stream, parent_directory=self.dl_path)
173
+ )
148
174
 
149
175
  @staticmethod
150
176
  def serve_download_errors(
anipy_cli/logger.py ADDED
@@ -0,0 +1,200 @@
1
+ from __future__ import annotations
2
+
3
+ import logging
4
+ import logging.handlers
5
+ from pathlib import Path
6
+ import sys
7
+ from types import TracebackType
8
+ from typing import Protocol
9
+ import datetime
10
+
11
+ from anipy_cli.config import Config
12
+ from anipy_cli import __appname__
13
+ from appdirs import user_data_dir
14
+
15
+
16
+ class FatalHandler(Protocol):
17
+ def __call__(
18
+ self, exc_val: BaseException, exc_tb: TracebackType, logs_location: Path
19
+ ): ...
20
+
21
+
22
+ class FatalCatcher:
23
+ def __init__(
24
+ self,
25
+ logs_location: Path,
26
+ fatal_handler: FatalHandler | None = None,
27
+ ignore_system_exit: bool = True,
28
+ ):
29
+ self._fatal_handler = fatal_handler
30
+
31
+ self.logs_location = logs_location
32
+ self.ignore_system_exit = ignore_system_exit
33
+
34
+ def __enter__(self):
35
+ info("Initializing program...")
36
+ return self
37
+
38
+ def __exit__(
39
+ self,
40
+ exc_type: type[BaseException] | None,
41
+ exc_val: BaseException | None,
42
+ exc_tb: TracebackType | None,
43
+ ):
44
+ if (not exc_type) or (not exc_val) or (not exc_tb):
45
+ info("Program exited successfully...")
46
+ return True
47
+
48
+ if exc_type is SystemExit and self.ignore_system_exit:
49
+ return True
50
+
51
+ try:
52
+ # Attempt to let a handler know something is up
53
+ # so it can get to the user
54
+ if self._fatal_handler:
55
+ self._fatal_handler(exc_val, exc_tb, self.logs_location)
56
+ except Exception:
57
+ # If that fails, at least get something to the user
58
+ sys.stderr.write("An extra fatal error occurred...")
59
+
60
+ fatal(f"A fatal error has occurred - {','.join(exc_val.args)}", exc_val)
61
+ info("Program exited with fatal errors...")
62
+
63
+ return True # Return true because we have processed the error
64
+
65
+
66
+ LOGGER_NAME = "cli_logger"
67
+ MAX_LOGS = 5
68
+ DEFAULT_FILE_LOG_LEVEL = 10
69
+ DEFAULT_CONSOLE_LOG_LEVEL = 60
70
+
71
+
72
+ def get_logs_location():
73
+ user_file_path = Path()
74
+ try:
75
+ user_file_path = Config().user_files_path
76
+ except Exception:
77
+ user_file_path = Path(user_data_dir(__appname__, appauthor=False))
78
+ finally:
79
+ return user_file_path / "logs"
80
+
81
+
82
+ _logger = logging.getLogger(LOGGER_NAME)
83
+
84
+ _logger.setLevel(10)
85
+
86
+ file_formatter = logging.Formatter(
87
+ "{asctime} - {levelname} - {message}", style="{", datefmt=r"%Y-%m-%d %H:%M:%S"
88
+ )
89
+ console_formatter = logging.Formatter("{levelname} -> {message}", style="{")
90
+
91
+ console_handler = logging.StreamHandler()
92
+ console_handler.setFormatter(console_formatter)
93
+ console_handler.setLevel(DEFAULT_CONSOLE_LOG_LEVEL)
94
+ _logger.addHandler(console_handler)
95
+
96
+ log_dir = get_logs_location()
97
+ log_dir.mkdir(parents=True, exist_ok=True)
98
+
99
+ current_time = datetime.datetime.now()
100
+ file_handler = logging.handlers.RotatingFileHandler(
101
+ get_logs_location() / f"{current_time.isoformat().replace(':', '.')}.log",
102
+ backupCount=5,
103
+ encoding="utf-8",
104
+ )
105
+ file_handler.setFormatter(file_formatter)
106
+ file_handler.setLevel(DEFAULT_FILE_LOG_LEVEL)
107
+ _logger.addHandler(file_handler)
108
+
109
+
110
+ def get_console_log_level():
111
+ return console_handler.level
112
+
113
+
114
+ def set_console_log_level(value: logging._Level):
115
+ console_handler.setLevel(value)
116
+
117
+
118
+ def get_file_log_level():
119
+ return file_handler.level
120
+
121
+
122
+ def set_file_log_level(value: logging._Level):
123
+ file_handler.setLevel(value)
124
+
125
+
126
+ def set_cli_verbosity(level: int):
127
+ """
128
+ Set how extreme the error has to
129
+ be for it to be printed in the CLI.
130
+
131
+ Default is 0.
132
+
133
+ 0 = No Statements To CLI
134
+ 1 = Fatal
135
+ 2 = Warnings
136
+ 3 = Info
137
+ """
138
+ level_conversion = {
139
+ 0: 60,
140
+ 1: 50,
141
+ 2: 30,
142
+ 3: 20,
143
+ }
144
+ other = 10 # If anything else, default to debug.
145
+ console_handler.setLevel(level_conversion.get(level, other))
146
+
147
+
148
+ def safe(fatal_handler: FatalHandler | None = None):
149
+ return FatalCatcher(get_logs_location(), fatal_handler)
150
+
151
+
152
+ _stack_always = False
153
+
154
+
155
+ def set_stack_always(value: bool):
156
+ global _stack_always
157
+
158
+ _stack_always = value
159
+
160
+
161
+ def is_stack_always(passthrough: bool):
162
+ """
163
+ If _stack_always is true, return true.
164
+
165
+ Otherwise return passthrough.
166
+ """
167
+ return True if _stack_always else passthrough
168
+
169
+
170
+ def debug(
171
+ content: str, exc_info: logging._ExcInfoType = None, stack_info: bool = False
172
+ ):
173
+ _logger.debug(content, exc_info=exc_info, stack_info=is_stack_always(stack_info))
174
+
175
+
176
+ def info(content: str, exc_info: logging._ExcInfoType = None, stack_info: bool = False):
177
+ _logger.info(content, exc_info=exc_info, stack_info=is_stack_always(stack_info))
178
+
179
+
180
+ def warn(content: str, exc_info: logging._ExcInfoType = None, stack_info: bool = False):
181
+ _logger.warning(content, exc_info=exc_info, stack_info=is_stack_always(stack_info))
182
+
183
+
184
+ def error(content: str, exc_info: logging._ExcInfoType = None):
185
+ _logger.error(content, exc_info=exc_info, stack_info=True)
186
+
187
+
188
+ def fatal(content: str, exc_info: logging._ExcInfoType = None):
189
+ _logger.critical(content, exc_info=exc_info, stack_info=True)
190
+
191
+
192
+ def log(
193
+ level: int,
194
+ content: str,
195
+ exc_info: logging._ExcInfoType = None,
196
+ stack_info: bool = False,
197
+ ):
198
+ _logger.log(
199
+ level, content, exc_info=exc_info, stack_info=is_stack_always(stack_info)
200
+ )
anipy_cli/mal_proxy.py CHANGED
@@ -82,7 +82,10 @@ class MyAnimeListProxy:
82
82
  config = Config()
83
83
  for e in mylist:
84
84
  if self.local_list.mappings.get(e.id, None):
85
- if e.my_list_status and config.mal_ignore_tag in e.my_list_status.tags:
85
+ if (
86
+ e.my_list_status
87
+ and config.tracker_ignore_tag in e.my_list_status.tags
88
+ ):
86
89
  self.local_list.mappings.pop(e.id)
87
90
  else:
88
91
  self.local_list.mappings[e.id].mal_anime = e
@@ -112,7 +115,10 @@ class MyAnimeListProxy:
112
115
  status_catagories
113
116
  if status_catagories is not None
114
117
  else set(
115
- [MALMyListStatusEnum[s.upper()] for s in config.mal_status_categories]
118
+ [
119
+ MALMyListStatusEnum[s.upper()]
120
+ for s in config.tracker_status_categories
121
+ ]
116
122
  )
117
123
  )
118
124
 
@@ -120,7 +126,7 @@ class MyAnimeListProxy:
120
126
  mylist.extend(
121
127
  filter(
122
128
  lambda e: (
123
- config.mal_ignore_tag not in e.my_list_status.tags
129
+ config.tracker_ignore_tag not in e.my_list_status.tags
124
130
  if e.my_list_status
125
131
  else True
126
132
  ),
@@ -145,7 +151,7 @@ class MyAnimeListProxy:
145
151
  tags: Set[str] = set(),
146
152
  ) -> MALMyListStatus:
147
153
  config = Config()
148
- tags |= set(config.mal_tags)
154
+ tags |= set(config.tracker_tags)
149
155
  result = self.mal.update_anime_list(
150
156
  anime.id, status=status, watched_episodes=episode, tags=list(tags)
151
157
  )
@@ -183,9 +189,9 @@ class MyAnimeListProxy:
183
189
  adapter = MyAnimeListAdapter(self.mal, p)
184
190
  result = adapter.from_myanimelist(
185
191
  anime,
186
- config.mal_mapping_min_similarity,
187
- config.mal_mapping_use_filters,
188
- config.mal_mapping_use_alternatives,
192
+ config.tracker_mapping_min_similarity,
193
+ config.tracker_mapping_use_filters,
194
+ config.tracker_mapping_use_alternatives,
189
195
  )
190
196
 
191
197
  if result is not None:
@@ -212,8 +218,8 @@ class MyAnimeListProxy:
212
218
  adapter = MyAnimeListAdapter(self.mal, anime.provider)
213
219
  result = adapter.from_provider(
214
220
  anime,
215
- config.mal_mapping_min_similarity,
216
- config.mal_mapping_use_alternatives,
221
+ config.tracker_mapping_min_similarity,
222
+ config.tracker_mapping_use_alternatives,
217
223
  )
218
224
 
219
225
  if result:
@@ -1,5 +1,6 @@
1
1
  from anipy_cli.menus.menu import Menu
2
2
  from anipy_cli.menus.mal_menu import MALMenu
3
+ from anipy_cli.menus.anilist_menu import AniListMenu
3
4
  from anipy_cli.menus.seasonal_menu import SeasonalMenu
4
5
 
5
- __all__ = ["Menu", "MALMenu", "SeasonalMenu"]
6
+ __all__ = ["Menu", "MALMenu", "AniListMenu", "SeasonalMenu"]