anipy-cli 3.1.11__py3-none-any.whl → 3.2.1__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.

Potentially problematic release.


This version of anipy-cli might be problematic. Click here for more details.

anipy_cli/__init__.py CHANGED
@@ -1,2 +1,2 @@
1
1
  __appname__ = "anipy-cli"
2
- __version__ = "3.1.11"
2
+ __version__ = "3.2.1"
@@ -1,6 +1,6 @@
1
1
  from typing import TYPE_CHECKING, Optional, List
2
2
 
3
- from anipy_api.download import Downloader
3
+ from anipy_cli.download_component import DownloadComponent
4
4
 
5
5
  from anipy_cli.clis.base_cli import CliBase
6
6
  from anipy_cli.colors import colors, cprint
@@ -12,10 +12,6 @@ from anipy_cli.prompts import (
12
12
  lang_prompt,
13
13
  parse_auto_search,
14
14
  )
15
- from anipy_cli.util import (
16
- DotSpinner,
17
- get_download_path,
18
- )
19
15
 
20
16
  if TYPE_CHECKING:
21
17
  from anipy_api.anime import Anime
@@ -73,45 +69,10 @@ class DownloadCli(CliBase):
73
69
  assert self.anime is not None
74
70
  assert self.lang is not None
75
71
 
76
- config = Config()
77
- with DotSpinner("Starting Download...") as s:
78
-
79
- def progress_indicator(percentage: float):
80
- s.set_text(f"Progress: {percentage:.1f}%")
81
-
82
- def info_display(message: str):
83
- s.write(f"> {message}")
84
-
85
- downloader = Downloader(progress_indicator, info_display)
86
-
87
- for e in self.episodes:
88
- s.set_text(
89
- "Extracting streams for ",
90
- colors.BLUE,
91
- f"{self.anime.name} ({self.lang})",
92
- colors.END,
93
- " Episode ",
94
- e,
95
- "...",
96
- )
97
-
98
- stream = self.anime.get_video(
99
- e, self.lang, preferred_quality=self.options.quality
100
- )
101
-
102
- info_display(
103
- f"Downloading Episode {stream.episode} of {self.anime.name} ({self.lang})"
104
- )
105
- s.set_text("Downloading...")
106
-
107
- downloader.download(
108
- stream,
109
- get_download_path(
110
- self.anime, stream, parent_directory=self.dl_path
111
- ),
112
- container=config.remux_to,
113
- ffmpeg=self.options.ffmpeg or config.ffmpeg_hls,
114
- )
72
+ errors = DownloadComponent(self.options, self.dl_path).download_anime(
73
+ [(self.anime, self.lang, self.episodes)], only_skip_ep_on_err=True
74
+ )
75
+ DownloadComponent.serve_download_errors(errors, only_skip_ep_on_err=True)
115
76
 
116
77
  def show(self):
117
78
  pass
@@ -0,0 +1,166 @@
1
+ from pathlib import Path
2
+ from typing import List, Protocol, Tuple
3
+ from anipy_cli.arg_parser import CliArgs
4
+ from anipy_cli.colors import color, colors
5
+ from anipy_cli.config import Config
6
+ from anipy_cli.util import DotSpinner, get_download_path
7
+
8
+ from anipy_api.anime import Anime
9
+ from anipy_api.download import Downloader
10
+ from anipy_api.provider.base import Episode, LanguageTypeEnum
11
+
12
+
13
+ class SuccessfulEpDownload(Protocol):
14
+ """
15
+ Callback for when an episode successfully downloads
16
+ """
17
+
18
+ def __call__(self, anime: Anime, ep: Episode, lang: LanguageTypeEnum):
19
+ """
20
+ Args:
21
+ anime: The relevant anime
22
+ ep: An int/float for the episode
23
+ lang: The language that downloaded
24
+ """
25
+ ...
26
+
27
+
28
+ class DownloadComponent:
29
+ """
30
+ A component used to download anime for
31
+ the ani-py CLI.
32
+ """
33
+
34
+ def __init__(self, cliArgs: CliArgs, dl_path: Path) -> None:
35
+ self.options = cliArgs
36
+ self.dl_path = dl_path
37
+
38
+ def download_anime(
39
+ self,
40
+ picked: List[Tuple[Anime, LanguageTypeEnum, List[Episode]]],
41
+ after_success_ep: SuccessfulEpDownload = lambda anime, ep, lang: None,
42
+ only_skip_ep_on_err: bool = False,
43
+ ) -> List[Tuple[Anime, Episode]]:
44
+ """
45
+ Attributes:
46
+ picked: The chosen animes to download
47
+ after_success_ep: The code to run when an anime successful downloads
48
+ only_skip_ep_on_err: If we should skip the specific episode on an error. If false, we skip the entire anime.
49
+ """
50
+ with DotSpinner("Starting download...") as s:
51
+
52
+ def progress_indicator(percentage: float):
53
+ s.set_text(f"Progress: {percentage:.1f}%")
54
+
55
+ def info_display(message: str):
56
+ s.write(f"> {message}")
57
+
58
+ def error_display(message: str):
59
+ s.write(color(colors.RED, "! ", message))
60
+
61
+ downloader = Downloader(progress_indicator, info_display, error_display)
62
+
63
+ failed: List[Tuple[Anime, Episode]] = []
64
+
65
+ for anime, lang, eps in picked:
66
+ failed = self.download_episodes(
67
+ s,
68
+ downloader,
69
+ anime,
70
+ lang,
71
+ eps,
72
+ after_success_ep,
73
+ only_skip_ep_on_err,
74
+ )
75
+
76
+ return failed
77
+
78
+ def download_episodes(
79
+ self,
80
+ spinner: DotSpinner,
81
+ downloader: Downloader,
82
+ anime: Anime,
83
+ lang: LanguageTypeEnum,
84
+ eps: List[Episode],
85
+ after_success_ep: SuccessfulEpDownload = lambda anime, ep, lang: None,
86
+ only_skip_ep_on_err: bool = False,
87
+ ) -> List[Tuple[Anime, Episode]]:
88
+ fails = []
89
+ for ep in eps:
90
+ try:
91
+ self.download_ep(spinner, downloader, anime, lang, ep)
92
+ except Exception as e:
93
+ if only_skip_ep_on_err:
94
+ error_msg = f"! Issues downloading episode {ep} of {anime.name}. Skipping..."
95
+ else:
96
+ error_msg = f"! Issues occurred while downloading the series ${anime.name}. Skipping..."
97
+ spinner.write(
98
+ color(
99
+ colors.RED,
100
+ f"! Error: {e}\n",
101
+ error_msg,
102
+ )
103
+ )
104
+ fails.append((anime, ep))
105
+ if only_skip_ep_on_err:
106
+ continue
107
+ return fails
108
+
109
+ after_success_ep(anime, ep, lang)
110
+ return fails
111
+
112
+ def download_ep(
113
+ self,
114
+ spinner: DotSpinner,
115
+ downloader: Downloader,
116
+ anime: Anime,
117
+ lang: LanguageTypeEnum,
118
+ ep: Episode,
119
+ ):
120
+ config = Config()
121
+
122
+ spinner.set_text(
123
+ "Extracting streams for ",
124
+ colors.BLUE,
125
+ f"{anime.name} ({lang})",
126
+ colors.END,
127
+ " Episode ",
128
+ ep,
129
+ "...",
130
+ )
131
+
132
+ stream = anime.get_video(ep, lang, preferred_quality=self.options.quality)
133
+
134
+ spinner.write(
135
+ f"> Downloading Episode {stream.episode} of {anime.name} ({lang})"
136
+ )
137
+
138
+ spinner.set_text("Downloading...")
139
+
140
+ downloader.download(
141
+ stream,
142
+ get_download_path(anime, stream, parent_directory=self.dl_path),
143
+ container=config.remux_to,
144
+ ffmpeg=self.options.ffmpeg or config.ffmpeg_hls,
145
+ )
146
+
147
+ @staticmethod
148
+ def serve_download_errors(
149
+ failures: List[Tuple[Anime, Episode]],
150
+ only_skip_ep_on_err: bool = False,
151
+ ):
152
+ if not failures:
153
+ return
154
+ text = ", ".join([f"\n\tEpisode {i[1]} of {i[0].name}" for i in failures])
155
+ extra_info = (
156
+ " (and the remaining episodes in that series)"
157
+ if not only_skip_ep_on_err
158
+ else ""
159
+ )
160
+
161
+ print(
162
+ color(
163
+ colors.RED,
164
+ f"! Unable to download the following episodes{extra_info}: {text}",
165
+ )
166
+ )
@@ -2,8 +2,9 @@ import sys
2
2
  from concurrent.futures import ThreadPoolExecutor, as_completed
3
3
  from typing import Dict, List, Tuple
4
4
 
5
+ from anipy_cli.download_component import DownloadComponent
6
+
5
7
  from anipy_api.anime import Anime
6
- from anipy_api.download import Downloader
7
8
  from anipy_api.mal import MALAnime, MALMyListStatusEnum, MyAnimeList
8
9
  from anipy_api.provider import LanguageTypeEnum
9
10
  from anipy_api.provider.base import Episode
@@ -23,7 +24,6 @@ from anipy_cli.util import (
23
24
  error,
24
25
  find_closest,
25
26
  get_configured_player,
26
- get_download_path,
27
27
  migrate_locallist,
28
28
  )
29
29
  from anipy_cli.prompts import search_show_prompt
@@ -249,7 +249,6 @@ class MALMenu(MenuBase):
249
249
 
250
250
  def download(self, all: bool = False):
251
251
  picked = self._choose_latest(all=all)
252
- config = Config()
253
252
  total_eps = sum([len(e) for a, m, lang, e in picked])
254
253
  if total_eps == 0:
255
254
  print("Nothing to download, returning...")
@@ -257,49 +256,26 @@ class MALMenu(MenuBase):
257
256
  else:
258
257
  print(f"Downloading a total of {total_eps} episode(s)")
259
258
 
260
- with DotSpinner("Starting Download...") as s:
261
-
262
- def progress_indicator(percentage: float):
263
- s.set_text(f"Progress: {percentage:.1f}%")
264
-
265
- def info_display(message: str):
266
- s.write(f"> {message}")
267
-
268
- downloader = Downloader(progress_indicator, info_display)
269
-
270
- for anime, mal_anime, lang, eps in picked:
271
- for ep in eps:
272
- s.set_text(
273
- "Extracting streams for ",
274
- colors.BLUE,
275
- f"{anime.name} ({lang})",
276
- colors.END,
277
- " Episode ",
278
- ep,
279
- "...",
280
- )
259
+ convert: Dict[Anime, MALAnime] = {d[0]: d[1] for d in picked}
260
+ new_picked = [
261
+ (anime_info[0], anime_info[2], anime_info[3]) for anime_info in picked
262
+ ]
281
263
 
282
- stream = anime.get_video(
283
- ep, lang, preferred_quality=self.options.quality
284
- )
264
+ def on_successful_download(anime: Anime, ep: Episode, lang: LanguageTypeEnum):
265
+ if all:
266
+ return
267
+ self.mal_proxy.update_show(
268
+ convert[anime],
269
+ status=MALMyListStatusEnum.WATCHING,
270
+ episode=int(ep),
271
+ )
285
272
 
286
- info_display(
287
- f"Downloading Episode {stream.episode} of {anime.name} ({lang})"
288
- )
289
- s.set_text("Downloading...")
273
+ errors = DownloadComponent(self.options, self.dl_path).download_anime(
274
+ new_picked, on_successful_download
275
+ )
276
+ DownloadComponent.serve_download_errors(errors)
290
277
 
291
- downloader.download(
292
- stream,
293
- get_download_path(anime, stream, parent_directory=self.dl_path),
294
- container=config.remux_to,
295
- ffmpeg=self.options.ffmpeg or config.ffmpeg_hls,
296
- )
297
- if not all:
298
- self.mal_proxy.update_show(
299
- mal_anime,
300
- status=MALMyListStatusEnum.WATCHING,
301
- episode=int(ep),
302
- )
278
+ self.print_options(clear_screen=len(errors) == 0)
303
279
 
304
280
  def binge_latest(self):
305
281
  picked = self._choose_latest()
@@ -417,6 +393,10 @@ class MALMenu(MenuBase):
417
393
  if e.my_list_status.num_episodes_watched == e.num_episodes:
418
394
  mylist.pop(i)
419
395
 
396
+ if not mylist:
397
+ error("MAL is empty", False)
398
+ return []
399
+
420
400
  if not (all or self.options.auto_update):
421
401
  choices = inquirer.fuzzy( # type: ignore
422
402
  message="Select shows to catch up to:",
anipy_cli/menus/menu.py CHANGED
@@ -211,7 +211,10 @@ class Menu(MenuBase):
211
211
  def info_display(message: str):
212
212
  s.write(f"> {message}")
213
213
 
214
- downloader = Downloader(progress_indicator, info_display)
214
+ def error_display(message: str):
215
+ s.write(f"{colors.RED}! {message}{colors.END}")
216
+
217
+ downloader = Downloader(progress_indicator, info_display, error_display)
215
218
 
216
219
  s.set_text(
217
220
  "Extracting streams for ",
@@ -1,8 +1,9 @@
1
1
  import sys
2
2
  from typing import TYPE_CHECKING, List, Tuple
3
3
 
4
+ from anipy_cli.download_component import DownloadComponent
5
+
4
6
  from anipy_api.anime import Anime
5
- from anipy_api.download import Downloader
6
7
  from anipy_api.provider import LanguageTypeEnum
7
8
  from anipy_api.provider.base import Episode
8
9
  from anipy_api.locallist import LocalList, LocalListEntry
@@ -17,7 +18,6 @@ from anipy_cli.util import (
17
18
  DotSpinner,
18
19
  error,
19
20
  get_configured_player,
20
- get_download_path,
21
21
  migrate_locallist,
22
22
  )
23
23
  from anipy_cli.prompts import pick_episode_prompt, search_show_prompt, lang_prompt
@@ -75,6 +75,9 @@ class SeasonalMenu(MenuBase):
75
75
  if self.options.auto_update:
76
76
  return [ch.value for ch in choices]
77
77
 
78
+ if not choices:
79
+ return []
80
+
78
81
  choices = inquirer.fuzzy( # type: ignore
79
82
  message="Select Seasonals to catch up to:",
80
83
  choices=choices,
@@ -86,6 +89,7 @@ class SeasonalMenu(MenuBase):
86
89
  {"long_instruction": "fg:#5FAFFF bg:#222"}, style_override=False
87
90
  ),
88
91
  ).execute()
92
+
89
93
  return choices or []
90
94
 
91
95
  def add_anime(self):
@@ -180,65 +184,39 @@ class SeasonalMenu(MenuBase):
180
184
  print(f"> {new_lang} is for {e.name} not available")
181
185
 
182
186
  def list_animes(self):
183
- for i in self.seasonal_list.get_all():
187
+ all_seasonals = self.seasonal_list.get_all()
188
+ if not all_seasonals:
189
+ error("No seasonals configured.")
190
+ return
191
+
192
+ for i in all_seasonals:
184
193
  print(i)
185
194
 
186
195
  def download_latest(self):
187
196
  picked = self._choose_latest()
188
- config = Config()
189
197
  total_eps = sum([len(e) for _a, _d, e in picked])
190
198
  if total_eps == 0:
191
- print("Nothing to download, returning...")
199
+ print("All up to date on downloads.")
192
200
  return
193
201
  else:
194
202
  print(f"Downloading a total of {total_eps} episode(s)")
195
- with DotSpinner("Starting Download...") as s:
196
-
197
- def progress_indicator(percentage: float):
198
- s.set_text(f"Progress: {percentage:.1f}%")
199
-
200
- def info_display(message: str):
201
- s.write(f"> {message}")
202
-
203
- downloader = Downloader(progress_indicator, info_display)
204
-
205
- for anime, lang, eps in picked:
206
- for ep in eps:
207
- s.set_text(
208
- "Extracting streams for ",
209
- colors.BLUE,
210
- f"{anime.name} ({lang})",
211
- colors.END,
212
- " Episode ",
213
- ep,
214
- "...",
215
- )
216
203
 
217
- stream = anime.get_video(
218
- ep, lang, preferred_quality=self.options.quality
219
- )
204
+ def on_successful_download(anime: Anime, ep: Episode, lang: LanguageTypeEnum):
205
+ self.seasonal_list.update(anime, episode=ep, language=lang)
220
206
 
221
- info_display(
222
- f"Downloading Episode {stream.episode} of {anime.name}"
223
- )
224
- s.set_text("Downloading...")
225
-
226
- downloader.download(
227
- stream,
228
- get_download_path(anime, stream, parent_directory=self.dl_path),
229
- container=config.remux_to,
230
- ffmpeg=self.options.ffmpeg or config.ffmpeg_hls,
231
- )
232
- self.seasonal_list.update(anime, episode=ep, language=lang)
207
+ failed_series = DownloadComponent(self.options, self.dl_path).download_anime(
208
+ picked, on_successful_download
209
+ )
233
210
 
234
211
  if not self.options.auto_update:
235
- self.print_options(clear_screen=True)
212
+ # Clear screen only if there were no issues
213
+ self.print_options(clear_screen=len(failed_series) == 0)
236
214
 
237
215
  def binge_latest(self):
238
216
  picked = self._choose_latest()
239
217
  total_eps = sum([len(e) for _a, _d, e in picked])
240
218
  if total_eps == 0:
241
- print("Nothing to watch, returning...")
219
+ print("Up to date on binge.")
242
220
  return
243
221
  else:
244
222
  print(f"Playing a total of {total_eps} episode(s)")
anipy_cli/util.py CHANGED
@@ -59,16 +59,17 @@ def error(error: str, fatal: bool = False) -> Union[NoReturn, None]:
59
59
  sys.stderr.write(
60
60
  color(colors.RED, "anipy-cli: error: ", colors.END, f"{error}\n")
61
61
  )
62
- else:
63
- sys.stderr.write(
64
- color(
65
- colors.RED,
66
- "anipy-cli: fatal error: ",
67
- colors.END,
68
- f"{error}, exiting\n",
69
- )
62
+ return
63
+
64
+ sys.stderr.write(
65
+ color(
66
+ colors.RED,
67
+ "anipy-cli: fatal error: ",
68
+ colors.END,
69
+ f"{error}, exiting\n",
70
70
  )
71
- sys.exit(1)
71
+ )
72
+ sys.exit(1)
72
73
 
73
74
 
74
75
  def get_prefered_providers(mode: str) -> Iterator["BaseProvider"]:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: anipy-cli
3
- Version: 3.1.11
3
+ Version: 3.2.1
4
4
  Summary: Watch and Download anime from the comfort of your Terminal
5
5
  Home-page: https://sdaqo.github.io/anipy-cli
6
6
  License: GPL-3.0
@@ -14,7 +14,7 @@ Classifier: Programming Language :: Python :: 3.9
14
14
  Classifier: Programming Language :: Python :: 3.10
15
15
  Classifier: Programming Language :: Python :: 3.11
16
16
  Classifier: Programming Language :: Python :: 3.12
17
- Requires-Dist: anipy-api (>=3.1.11,<4.0.0)
17
+ Requires-Dist: anipy-api (>=3.2.1,<4.0.0)
18
18
  Requires-Dist: appdirs (>=1.4.4,<2.0.0)
19
19
  Requires-Dist: inquirerpy (>=0.3.4,<0.4.0)
20
20
  Requires-Dist: pypresence (>=4.3.0,<5.0.0)
@@ -1,26 +1,27 @@
1
- anipy_cli/__init__.py,sha256=xL2cL5tD8ctPxuAeiOgWyxhCG9lQxrLxvHWQ4ZS3GIU,49
1
+ anipy_cli/__init__.py,sha256=WPYElS2k94EZHwDcAzNe-BpROcnukfOvamZVzV0HCD8,48
2
2
  anipy_cli/arg_parser.py,sha256=Y_p5Sh7LR0yeqfhFVXkyAbZMjDR1-NIoY_TVuPwUnwk,6009
3
3
  anipy_cli/cli.py,sha256=Ah5BE-u_SjcuzU5dQ5cSHtUloVRApCWwTK5i49yQtH8,1922
4
4
  anipy_cli/clis/__init__.py,sha256=Y00uiPWiMvvRImxJMvfLA55BOkMUOrrx5vJUNvquNsY,411
5
5
  anipy_cli/clis/base_cli.py,sha256=xPr_J8hKs7LkDLvmK6zyL1ZTZRpyC2IuFss8KsaDstU,817
6
6
  anipy_cli/clis/binge_cli.py,sha256=ioZ-V0WfGYBqETFkd8epGrT9dPHwsRJ1qvIdqf4waIs,2551
7
7
  anipy_cli/clis/default_cli.py,sha256=aJrJwtwdD7l-Z3dMjSHlvMvgTVnwA3_OXwS-9DZQIy8,3078
8
- anipy_cli/clis/download_cli.py,sha256=XO6_B2Sm2P_u34MOvvXungFZWBDPGaO937GxNidpvdE,3460
8
+ anipy_cli/clis/download_cli.py,sha256=ku4_zVMeiV7VOxEx2Bq0mhxQJfS5sculzewgBfYuuFE,2308
9
9
  anipy_cli/clis/history_cli.py,sha256=2ccv6BpQQpUhY4K-KM7lO9qxVLXBrmCY5lec6czipSE,2863
10
10
  anipy_cli/clis/mal_cli.py,sha256=_tSLgDUOa6GOZNyCncSSzaVj088y5GAKkHVRSndLLxk,2258
11
11
  anipy_cli/clis/seasonal_cli.py,sha256=GV2TQNm9UotG1cxfYbrFFgg7Jmy8SFa7w_GlFtPdRVE,616
12
12
  anipy_cli/colors.py,sha256=voXC7z1Fs9tHg4zzNTNMIrt9k-EVgJ3_xEf5KiW2xgo,916
13
13
  anipy_cli/config.py,sha256=R26KvF-mUDQ9uTqViHJn1gsrCk9TCj2ndVXW9jmQH9c,15955
14
14
  anipy_cli/discord.py,sha256=c6mdqnEdblzZBYs3cGP66oDeS4ySm59OfTRP-R-Duls,1160
15
+ anipy_cli/download_component.py,sha256=6N-7ItCj1hiqFu7falu6gvLp5KNYtfISMqlazHFPiAg,5098
15
16
  anipy_cli/mal_proxy.py,sha256=wIsku2_dl8vKD2K99L63OlzA3L5fl-VmyeiXC9VrxI4,6981
16
17
  anipy_cli/menus/__init__.py,sha256=aIzbphxAW-QGfZwR1DIegFZuTJp1O3tSUnai0f0f4lY,185
17
18
  anipy_cli/menus/base_menu.py,sha256=g5b9Z7SpvCxcq_vqObcPzxLwcXeGPltLgSwa0sEzyfk,1140
18
- anipy_cli/menus/mal_menu.py,sha256=tJYq5J3k89_0BKFiWavn9Gqh5Z7uXtoUFqJaa3fT4o4,24105
19
- anipy_cli/menus/menu.py,sha256=UQJ1hpyDT0i03ecIjBbFRp4PFh6FTNHDhSwSBSAhQEI,7860
20
- anipy_cli/menus/seasonal_menu.py,sha256=Y64dRs554n-O51L5Cf-Jwtmdt2w1MM2XAErqBwsNgDM,9176
19
+ anipy_cli/menus/mal_menu.py,sha256=r9HEu73l0G-mq6xubvJO9XtP2xFNMg1PX0rFl_Uid2o,23261
20
+ anipy_cli/menus/menu.py,sha256=iSEko3ckcLMOccwB8qJ2LE4j15Qgy0RMLWKY9jnAJrY,7985
21
+ anipy_cli/menus/seasonal_menu.py,sha256=ZB0i37xdR5tERqPNDIdYS9cFLh2swFFQdvOdCD_CdkY,8245
21
22
  anipy_cli/prompts.py,sha256=lQDrb8IlhsQhOpLG4sflamprTSn-PlCy41tGpXIowdo,10156
22
- anipy_cli/util.py,sha256=YiiWaX8O-yxTT53kBA1wlTJesLudjeto6E3ZdtDMgAw,8022
23
- anipy_cli-3.1.11.dist-info/METADATA,sha256=lgs9MvJT6GAxVqi8mxNEkPZsNKR6qIeC0xrmlAxTn6Q,3420
24
- anipy_cli-3.1.11.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
25
- anipy_cli-3.1.11.dist-info/entry_points.txt,sha256=86iXpcm_ECFndrt0JAI2mqYfXC2Ar7mGi0iOaxCrNP0,51
26
- anipy_cli-3.1.11.dist-info/RECORD,,
23
+ anipy_cli/util.py,sha256=YPCYMmsXUhcikYvT9407Asie5qd20pvhqnK949JcirE,7992
24
+ anipy_cli-3.2.1.dist-info/METADATA,sha256=oNt88B48gvqiu2cuPB9peM5J-J5LXq5TAZrfjFeVvA0,3418
25
+ anipy_cli-3.2.1.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
26
+ anipy_cli-3.2.1.dist-info/entry_points.txt,sha256=86iXpcm_ECFndrt0JAI2mqYfXC2Ar7mGi0iOaxCrNP0,51
27
+ anipy_cli-3.2.1.dist-info/RECORD,,