anipy-cli 3.2.4__py3-none-any.whl → 3.3.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 +1 -1
- anipy_cli/config.py +7 -7
- anipy_cli/mal_proxy.py +9 -3
- anipy_cli/menus/seasonal_menu.py +68 -2
- anipy_cli/util.py +13 -3
- {anipy_cli-3.2.4.dist-info → anipy_cli-3.3.1.dist-info}/METADATA +2 -2
- {anipy_cli-3.2.4.dist-info → anipy_cli-3.3.1.dist-info}/RECORD +9 -9
- {anipy_cli-3.2.4.dist-info → anipy_cli-3.3.1.dist-info}/WHEEL +0 -0
- {anipy_cli-3.2.4.dist-info → anipy_cli-3.3.1.dist-info}/entry_points.txt +0 -0
anipy_cli/__init__.py
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
__appname__ = "anipy-cli"
|
|
2
|
-
__version__ = "3.
|
|
2
|
+
__version__ = "3.3.1"
|
anipy_cli/config.py
CHANGED
|
@@ -72,7 +72,7 @@ class Config:
|
|
|
72
72
|
|
|
73
73
|
For an updated list of providers look here: https://sdaqo.github.io/anipy-cli/availabilty
|
|
74
74
|
|
|
75
|
-
Supported providers (as of $version): gogoanime,
|
|
75
|
+
Supported providers (as of $version): gogoanime, anivibe
|
|
76
76
|
|
|
77
77
|
Examples:
|
|
78
78
|
providers:
|
|
@@ -83,12 +83,12 @@ class Config:
|
|
|
83
83
|
mal: ["provider2", "provider3"]
|
|
84
84
|
"""
|
|
85
85
|
defaults = {
|
|
86
|
-
"default": ["
|
|
87
|
-
"download": ["
|
|
88
|
-
"history": ["
|
|
89
|
-
"seasonal": ["
|
|
90
|
-
"binge": ["
|
|
91
|
-
"mal": ["
|
|
86
|
+
"default": ["anivibe"],
|
|
87
|
+
"download": ["anivibe"],
|
|
88
|
+
"history": ["anivibe"],
|
|
89
|
+
"seasonal": ["anivibe"],
|
|
90
|
+
"binge": ["anivibe"],
|
|
91
|
+
"mal": ["anivibe"],
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
value = self._get_value("providers", defaults, dict)
|
anipy_cli/mal_proxy.py
CHANGED
|
@@ -167,9 +167,15 @@ class MyAnimeListProxy:
|
|
|
167
167
|
return mapping
|
|
168
168
|
|
|
169
169
|
if self.local_list.mappings[anime.id].mappings:
|
|
170
|
-
map
|
|
171
|
-
|
|
172
|
-
|
|
170
|
+
for map in self.local_list.mappings[anime.id].mappings.values():
|
|
171
|
+
provider = next(
|
|
172
|
+
filter(lambda x: x.NAME == map.provider, list_providers()), None
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
if provider is None:
|
|
176
|
+
continue
|
|
177
|
+
|
|
178
|
+
return Anime(provider(), map.name, map.identifier, map.languages)
|
|
173
179
|
|
|
174
180
|
config = Config()
|
|
175
181
|
result = None
|
anipy_cli/menus/seasonal_menu.py
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
import sys
|
|
2
2
|
from typing import TYPE_CHECKING, List, Tuple
|
|
3
3
|
|
|
4
|
+
from anipy_api.mal import MyAnimeListAdapter
|
|
5
|
+
|
|
4
6
|
from anipy_cli.download_component import DownloadComponent
|
|
5
7
|
|
|
6
8
|
from anipy_api.anime import Anime
|
|
7
9
|
from anipy_api.provider import LanguageTypeEnum
|
|
8
10
|
from anipy_api.provider.base import Episode
|
|
9
11
|
from anipy_api.locallist import LocalList, LocalListEntry
|
|
12
|
+
from anipy_api.error import ProviderNotAvailable
|
|
10
13
|
from InquirerPy import inquirer
|
|
11
14
|
from InquirerPy.base.control import Choice
|
|
12
15
|
from InquirerPy.utils import get_style
|
|
@@ -17,11 +20,14 @@ from anipy_cli.menus.base_menu import MenuBase, MenuOption
|
|
|
17
20
|
from anipy_cli.util import (
|
|
18
21
|
DotSpinner,
|
|
19
22
|
error,
|
|
23
|
+
find_closest,
|
|
20
24
|
get_configured_player,
|
|
25
|
+
get_prefered_providers,
|
|
21
26
|
migrate_locallist,
|
|
22
27
|
)
|
|
23
28
|
from anipy_cli.prompts import pick_episode_prompt, search_show_prompt, lang_prompt
|
|
24
29
|
|
|
30
|
+
|
|
25
31
|
if TYPE_CHECKING:
|
|
26
32
|
from anipy_cli.arg_parser import CliArgs
|
|
27
33
|
|
|
@@ -43,6 +49,7 @@ class SeasonalMenu(MenuBase):
|
|
|
43
49
|
MenuOption("Add Anime", self.add_anime, "a"),
|
|
44
50
|
MenuOption("Delete one anime from seasonals", self.del_anime, "e"),
|
|
45
51
|
MenuOption("List anime in seasonals", self.list_animes, "l"),
|
|
52
|
+
MenuOption("Migrate to current provider", self.migrate_provider, "m"),
|
|
46
53
|
MenuOption("Change dub/sub of anime in seasonals", self.change_lang, "c"),
|
|
47
54
|
MenuOption("Download newest episodes", self.download_latest, "d"),
|
|
48
55
|
MenuOption("Binge watch newest episodes", self.binge_latest, "w"),
|
|
@@ -56,7 +63,16 @@ class SeasonalMenu(MenuBase):
|
|
|
56
63
|
with DotSpinner("Fetching status of shows in seasonals..."):
|
|
57
64
|
choices = []
|
|
58
65
|
for s in self.seasonal_list.get_all():
|
|
59
|
-
|
|
66
|
+
try:
|
|
67
|
+
anime = Anime.from_local_list_entry(s)
|
|
68
|
+
except ProviderNotAvailable:
|
|
69
|
+
error(
|
|
70
|
+
f"Can not load '{s.name}' because the configured provider"
|
|
71
|
+
f" '{s.provider}' was not found, maybe try to migrate"
|
|
72
|
+
" providers with 'm'."
|
|
73
|
+
)
|
|
74
|
+
continue
|
|
75
|
+
|
|
60
76
|
lang = s.language
|
|
61
77
|
episodes = anime.get_episodes(lang)
|
|
62
78
|
|
|
@@ -93,7 +109,7 @@ class SeasonalMenu(MenuBase):
|
|
|
93
109
|
return choices or []
|
|
94
110
|
|
|
95
111
|
def add_anime(self):
|
|
96
|
-
anime = search_show_prompt("
|
|
112
|
+
anime = search_show_prompt("seasonal")
|
|
97
113
|
|
|
98
114
|
if anime is None:
|
|
99
115
|
return
|
|
@@ -192,6 +208,56 @@ class SeasonalMenu(MenuBase):
|
|
|
192
208
|
for i in all_seasonals:
|
|
193
209
|
print(i)
|
|
194
210
|
|
|
211
|
+
def migrate_provider(self):
|
|
212
|
+
config = Config()
|
|
213
|
+
all_seasonals = self.seasonal_list.get_all()
|
|
214
|
+
current_providers = list(get_prefered_providers("seasonal"))
|
|
215
|
+
print(
|
|
216
|
+
"Migrating to configured providers:",
|
|
217
|
+
", ".join([p.NAME for p in current_providers]),
|
|
218
|
+
)
|
|
219
|
+
for s in all_seasonals:
|
|
220
|
+
if s.provider in [p.NAME for p in current_providers]:
|
|
221
|
+
continue
|
|
222
|
+
|
|
223
|
+
print(f"Mapping: {s.name}")
|
|
224
|
+
|
|
225
|
+
search_results: List[Anime] = []
|
|
226
|
+
for p in current_providers:
|
|
227
|
+
search_results.extend(
|
|
228
|
+
[Anime.from_search_result(p, r) for r in p.get_search(s.name)]
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
best_anime = None
|
|
232
|
+
best_ratio = 0
|
|
233
|
+
for r in search_results:
|
|
234
|
+
titles = {r.name}
|
|
235
|
+
titles |= set(r.get_info().alternative_names or [])
|
|
236
|
+
ratio = MyAnimeListAdapter._find_best_ratio(titles, {s.name})
|
|
237
|
+
|
|
238
|
+
if ratio > best_ratio:
|
|
239
|
+
best_ratio = ratio
|
|
240
|
+
best_anime = r
|
|
241
|
+
|
|
242
|
+
if best_ratio == 1:
|
|
243
|
+
break
|
|
244
|
+
|
|
245
|
+
if best_anime is None:
|
|
246
|
+
continue
|
|
247
|
+
|
|
248
|
+
if best_ratio >= config.mal_mapping_min_similarity:
|
|
249
|
+
self.seasonal_list.delete(s)
|
|
250
|
+
else:
|
|
251
|
+
print(f"Could not autmatically map {s.name}, you can map it manually.")
|
|
252
|
+
best_anime = search_show_prompt("seasonal", skip_season_search=True)
|
|
253
|
+
if best_anime is None:
|
|
254
|
+
continue
|
|
255
|
+
|
|
256
|
+
episode = find_closest(best_anime.get_episodes(s.language), s.episode)
|
|
257
|
+
self.seasonal_list.update(best_anime, language=s.language, episode=episode)
|
|
258
|
+
|
|
259
|
+
self.print_options(clear_screen=True)
|
|
260
|
+
|
|
195
261
|
def download_latest(self):
|
|
196
262
|
picked = self._choose_latest()
|
|
197
263
|
total_eps = sum([len(e) for _a, _d, e in picked])
|
anipy_cli/util.py
CHANGED
|
@@ -78,14 +78,24 @@ def get_prefered_providers(mode: str) -> Iterator["BaseProvider"]:
|
|
|
78
78
|
|
|
79
79
|
if not preferred_providers:
|
|
80
80
|
error(
|
|
81
|
-
f"you have no providers set for {mode} mode, look into your config",
|
|
81
|
+
f"you have no providers set for '{mode}' mode, look into your config",
|
|
82
82
|
fatal=True,
|
|
83
83
|
)
|
|
84
84
|
|
|
85
|
+
providers = []
|
|
85
86
|
for i in list_providers():
|
|
86
87
|
if i.NAME in preferred_providers:
|
|
87
88
|
url_override = config.provider_urls.get(i.NAME, None)
|
|
88
|
-
|
|
89
|
+
providers.append(i(url_override))
|
|
90
|
+
|
|
91
|
+
if not providers:
|
|
92
|
+
error(
|
|
93
|
+
f"there are no working providers for '{mode}' mode, look into your config",
|
|
94
|
+
fatal=True,
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
for p in providers:
|
|
98
|
+
yield p
|
|
89
99
|
|
|
90
100
|
|
|
91
101
|
def get_download_path(
|
|
@@ -135,7 +145,7 @@ def parsenum(n: str):
|
|
|
135
145
|
return float(n)
|
|
136
146
|
|
|
137
147
|
|
|
138
|
-
def find_closest(episodes: List["Episode"], target:
|
|
148
|
+
def find_closest(episodes: List["Episode"], target: "Episode") -> "Episode":
|
|
139
149
|
left, right = 0, len(episodes) - 1
|
|
140
150
|
while left < right:
|
|
141
151
|
if abs(episodes[left] - target) <= abs(episodes[right] - target):
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: anipy-cli
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.3.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
|
|
@@ -15,7 +15,7 @@ Classifier: Programming Language :: Python :: 3.10
|
|
|
15
15
|
Classifier: Programming Language :: Python :: 3.11
|
|
16
16
|
Classifier: Programming Language :: Python :: 3.12
|
|
17
17
|
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
-
Requires-Dist: anipy-api (>=3.
|
|
18
|
+
Requires-Dist: anipy-api (>=3.3.1,<4.0.0)
|
|
19
19
|
Requires-Dist: appdirs (>=1.4.4,<2.0.0)
|
|
20
20
|
Requires-Dist: inquirerpy (>=0.3.4,<0.4.0)
|
|
21
21
|
Requires-Dist: pypresence (>=4.3.0,<5.0.0)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
anipy_cli/__init__.py,sha256=
|
|
1
|
+
anipy_cli/__init__.py,sha256=VIX0DpDFvN9R1K0maY6aDEB5ofODHoiovUNhSW-ac0E,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
|
|
@@ -10,18 +10,18 @@ anipy_cli/clis/history_cli.py,sha256=2ccv6BpQQpUhY4K-KM7lO9qxVLXBrmCY5lec6czipSE
|
|
|
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
|
-
anipy_cli/config.py,sha256=
|
|
13
|
+
anipy_cli/config.py,sha256=tVZk51QuX4V4BOIzyDYdQ6y6QKU69NkagAYXhcikHXY,15934
|
|
14
14
|
anipy_cli/discord.py,sha256=c6mdqnEdblzZBYs3cGP66oDeS4ySm59OfTRP-R-Duls,1160
|
|
15
15
|
anipy_cli/download_component.py,sha256=6N-7ItCj1hiqFu7falu6gvLp5KNYtfISMqlazHFPiAg,5098
|
|
16
|
-
anipy_cli/mal_proxy.py,sha256=
|
|
16
|
+
anipy_cli/mal_proxy.py,sha256=me2ESB442pYeNEpHY8mqrOEb477UA0uAg2LprKcp8sM,7098
|
|
17
17
|
anipy_cli/menus/__init__.py,sha256=aIzbphxAW-QGfZwR1DIegFZuTJp1O3tSUnai0f0f4lY,185
|
|
18
18
|
anipy_cli/menus/base_menu.py,sha256=g5b9Z7SpvCxcq_vqObcPzxLwcXeGPltLgSwa0sEzyfk,1140
|
|
19
19
|
anipy_cli/menus/mal_menu.py,sha256=RBq80HG-rKUeJaDuT9ZCCZuujebb2Jc0xwo2Pedo3VQ,23690
|
|
20
20
|
anipy_cli/menus/menu.py,sha256=iSEko3ckcLMOccwB8qJ2LE4j15Qgy0RMLWKY9jnAJrY,7985
|
|
21
|
-
anipy_cli/menus/seasonal_menu.py,sha256=
|
|
21
|
+
anipy_cli/menus/seasonal_menu.py,sha256=t4LJMtempxf3OaqtOvGJoRJ1IhmxgFjsIgeSceKKnR4,10636
|
|
22
22
|
anipy_cli/prompts.py,sha256=lQDrb8IlhsQhOpLG4sflamprTSn-PlCy41tGpXIowdo,10156
|
|
23
|
-
anipy_cli/util.py,sha256=
|
|
24
|
-
anipy_cli-3.
|
|
25
|
-
anipy_cli-3.
|
|
26
|
-
anipy_cli-3.
|
|
27
|
-
anipy_cli-3.
|
|
23
|
+
anipy_cli/util.py,sha256=Y_kPyb3D0-lVipWIJFL3GNcnunSlQw4kNTzCzgRXOQg,8232
|
|
24
|
+
anipy_cli-3.3.1.dist-info/METADATA,sha256=IIYyezuJb1Y6f5gQ_0Ff8EjCwJLeQolLXMtEJbrNFAE,3469
|
|
25
|
+
anipy_cli-3.3.1.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
|
|
26
|
+
anipy_cli-3.3.1.dist-info/entry_points.txt,sha256=86iXpcm_ECFndrt0JAI2mqYfXC2Ar7mGi0iOaxCrNP0,51
|
|
27
|
+
anipy_cli-3.3.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|