anipy-cli 2.7.31__py3-none-any.whl → 3.0.0__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 +2 -20
- anipy_cli/arg_parser.py +30 -20
- anipy_cli/cli.py +66 -0
- anipy_cli/clis/__init__.py +15 -0
- anipy_cli/clis/base_cli.py +32 -0
- anipy_cli/clis/binge_cli.py +83 -0
- anipy_cli/clis/default_cli.py +104 -0
- anipy_cli/clis/download_cli.py +111 -0
- anipy_cli/clis/history_cli.py +93 -0
- anipy_cli/clis/mal_cli.py +71 -0
- anipy_cli/{cli/clis → clis}/seasonal_cli.py +9 -6
- anipy_cli/colors.py +4 -4
- anipy_cli/config.py +308 -87
- anipy_cli/discord.py +34 -0
- anipy_cli/mal_proxy.py +216 -0
- anipy_cli/menus/__init__.py +5 -0
- anipy_cli/{cli/menus → menus}/base_menu.py +8 -12
- anipy_cli/menus/mal_menu.py +660 -0
- anipy_cli/menus/menu.py +194 -0
- anipy_cli/menus/seasonal_menu.py +263 -0
- anipy_cli/prompts.py +231 -0
- anipy_cli/util.py +262 -0
- anipy_cli-3.0.0.dist-info/METADATA +67 -0
- anipy_cli-3.0.0.dist-info/RECORD +26 -0
- {anipy_cli-2.7.31.dist-info → anipy_cli-3.0.0.dist-info}/WHEEL +1 -2
- anipy_cli-3.0.0.dist-info/entry_points.txt +3 -0
- anipy_cli/cli/__init__.py +0 -1
- anipy_cli/cli/cli.py +0 -37
- anipy_cli/cli/clis/__init__.py +0 -6
- anipy_cli/cli/clis/base_cli.py +0 -43
- anipy_cli/cli/clis/binge_cli.py +0 -54
- anipy_cli/cli/clis/default_cli.py +0 -46
- anipy_cli/cli/clis/download_cli.py +0 -92
- anipy_cli/cli/clis/history_cli.py +0 -64
- anipy_cli/cli/clis/mal_cli.py +0 -27
- anipy_cli/cli/menus/__init__.py +0 -3
- anipy_cli/cli/menus/mal_menu.py +0 -411
- anipy_cli/cli/menus/menu.py +0 -108
- anipy_cli/cli/menus/seasonal_menu.py +0 -177
- anipy_cli/cli/util.py +0 -125
- anipy_cli/download.py +0 -467
- anipy_cli/history.py +0 -83
- anipy_cli/mal.py +0 -651
- anipy_cli/misc.py +0 -227
- anipy_cli/player/__init__.py +0 -1
- anipy_cli/player/player.py +0 -35
- anipy_cli/player/players/__init__.py +0 -3
- anipy_cli/player/players/base.py +0 -107
- anipy_cli/player/players/mpv.py +0 -19
- anipy_cli/player/players/mpv_control.py +0 -37
- anipy_cli/player/players/syncplay.py +0 -19
- anipy_cli/player/players/vlc.py +0 -18
- anipy_cli/query.py +0 -100
- anipy_cli/run_anipy_cli.py +0 -14
- anipy_cli/seasonal.py +0 -112
- anipy_cli/url_handler.py +0 -470
- anipy_cli/version.py +0 -1
- anipy_cli-2.7.31.dist-info/LICENSE +0 -674
- anipy_cli-2.7.31.dist-info/METADATA +0 -162
- anipy_cli-2.7.31.dist-info/RECORD +0 -43
- anipy_cli-2.7.31.dist-info/entry_points.txt +0 -2
- anipy_cli-2.7.31.dist-info/top_level.txt +0 -1
anipy_cli/mal_proxy.py
ADDED
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
from dataclasses import dataclass, field
|
|
2
|
+
from typing import Dict, List, Optional, Set
|
|
3
|
+
|
|
4
|
+
from anipy_api.anime import Anime
|
|
5
|
+
from anipy_api.mal import (
|
|
6
|
+
MALAnime,
|
|
7
|
+
MALMyListStatus,
|
|
8
|
+
MALMyListStatusEnum,
|
|
9
|
+
MyAnimeList,
|
|
10
|
+
MyAnimeListAdapter,
|
|
11
|
+
)
|
|
12
|
+
from anipy_api.provider import LanguageTypeEnum, list_providers
|
|
13
|
+
from dataclasses_json import DataClassJsonMixin, config
|
|
14
|
+
from InquirerPy import inquirer
|
|
15
|
+
|
|
16
|
+
from anipy_cli.config import Config
|
|
17
|
+
from anipy_cli.util import error, get_prefered_providers
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@dataclass
|
|
21
|
+
class ProviderMapping(DataClassJsonMixin):
|
|
22
|
+
provider: str = field(metadata=config(field_name="pv"))
|
|
23
|
+
name: str = field(metadata=config(field_name="na"))
|
|
24
|
+
identifier: str = field(metadata=config(field_name="id"))
|
|
25
|
+
languages: Set[LanguageTypeEnum] = field(metadata=config(field_name="la"))
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@dataclass
|
|
29
|
+
class MALProviderMapping(DataClassJsonMixin):
|
|
30
|
+
mal_anime: MALAnime
|
|
31
|
+
mappings: Dict[str, ProviderMapping]
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@dataclass
|
|
35
|
+
class MALLocalList(DataClassJsonMixin):
|
|
36
|
+
mappings: Dict[int, MALProviderMapping]
|
|
37
|
+
|
|
38
|
+
def write(self, user_id: int):
|
|
39
|
+
config = Config()
|
|
40
|
+
local_list = config._mal_local_user_list_path.with_stem(
|
|
41
|
+
f"{config._mal_local_user_list_path.stem}_{user_id}"
|
|
42
|
+
)
|
|
43
|
+
local_list.write_text(self.to_json())
|
|
44
|
+
|
|
45
|
+
@staticmethod
|
|
46
|
+
def read(user_id: int) -> "MALLocalList":
|
|
47
|
+
config = Config()
|
|
48
|
+
local_list = config._mal_local_user_list_path.with_stem(
|
|
49
|
+
f"{config._mal_local_user_list_path.stem}_{user_id}"
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
if not local_list.is_file():
|
|
53
|
+
local_list.parent.mkdir(exist_ok=True, parents=True)
|
|
54
|
+
local_list.touch()
|
|
55
|
+
mylist = MALLocalList({})
|
|
56
|
+
mylist.write(user_id)
|
|
57
|
+
return mylist
|
|
58
|
+
|
|
59
|
+
try:
|
|
60
|
+
mylist: MALLocalList = MALLocalList.from_json(local_list.read_text())
|
|
61
|
+
except KeyError:
|
|
62
|
+
choice = inquirer.confirm(
|
|
63
|
+
message=f"Your local MyAnimeList ({str(local_list)}) is not in the correct format, should it be deleted?",
|
|
64
|
+
default=False,
|
|
65
|
+
).execute()
|
|
66
|
+
if choice:
|
|
67
|
+
local_list.unlink()
|
|
68
|
+
return MALLocalList.read(user_id)
|
|
69
|
+
else:
|
|
70
|
+
error("could not read your MyAnimeList", fatal=True)
|
|
71
|
+
|
|
72
|
+
return mylist
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class MyAnimeListProxy:
|
|
76
|
+
def __init__(self, mal: MyAnimeList):
|
|
77
|
+
self.mal = mal
|
|
78
|
+
self.user_id = mal.get_user().id
|
|
79
|
+
self.local_list = MALLocalList.read(self.user_id)
|
|
80
|
+
|
|
81
|
+
def _cache_list(self, mylist: List[MALAnime]):
|
|
82
|
+
config = Config()
|
|
83
|
+
for e in mylist:
|
|
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:
|
|
86
|
+
self.local_list.mappings.pop(e.id)
|
|
87
|
+
else:
|
|
88
|
+
self.local_list.mappings[e.id].mal_anime = e
|
|
89
|
+
else:
|
|
90
|
+
self.local_list.mappings[e.id] = MALProviderMapping(e, {})
|
|
91
|
+
|
|
92
|
+
self.local_list.write(self.user_id)
|
|
93
|
+
|
|
94
|
+
def _write_mapping(self, mal_anime: MALAnime, mapping: Anime):
|
|
95
|
+
self._cache_list([mal_anime])
|
|
96
|
+
|
|
97
|
+
self.local_list.mappings[mal_anime.id].mappings[
|
|
98
|
+
f"{mapping.provider.NAME}:{mapping.identifier}"
|
|
99
|
+
] = ProviderMapping(
|
|
100
|
+
mapping.provider.NAME, mapping.name, mapping.identifier, mapping.languages
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
self.local_list.write(self.user_id)
|
|
104
|
+
|
|
105
|
+
def get_list(
|
|
106
|
+
self, status_catagories: Optional[Set[MALMyListStatusEnum]] = None
|
|
107
|
+
) -> List[MALAnime]:
|
|
108
|
+
config = Config()
|
|
109
|
+
mylist: List[MALAnime] = []
|
|
110
|
+
|
|
111
|
+
catagories = (
|
|
112
|
+
status_catagories
|
|
113
|
+
if status_catagories is not None
|
|
114
|
+
else set(
|
|
115
|
+
[MALMyListStatusEnum[s.upper()] for s in config.mal_status_categories]
|
|
116
|
+
)
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
for c in catagories:
|
|
120
|
+
mylist.extend(
|
|
121
|
+
filter(
|
|
122
|
+
lambda e: (
|
|
123
|
+
config.mal_ignore_tag not in e.my_list_status.tags
|
|
124
|
+
if e.my_list_status
|
|
125
|
+
else True
|
|
126
|
+
),
|
|
127
|
+
self.mal.get_anime_list(c),
|
|
128
|
+
)
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
self._cache_list(mylist)
|
|
132
|
+
filtered_list = filter(
|
|
133
|
+
lambda x: (
|
|
134
|
+
x.my_list_status.status in catagories if x.my_list_status else False
|
|
135
|
+
),
|
|
136
|
+
mylist,
|
|
137
|
+
)
|
|
138
|
+
return list(filtered_list)
|
|
139
|
+
|
|
140
|
+
def update_show(
|
|
141
|
+
self,
|
|
142
|
+
anime: MALAnime,
|
|
143
|
+
status: Optional[MALMyListStatusEnum] = None,
|
|
144
|
+
episode: Optional[int] = None,
|
|
145
|
+
tags: Set[str] = set(),
|
|
146
|
+
) -> MALMyListStatus:
|
|
147
|
+
config = Config()
|
|
148
|
+
tags |= set(config.mal_tags)
|
|
149
|
+
result = self.mal.update_anime_list(
|
|
150
|
+
anime.id, status=status, watched_episodes=episode, tags=list(tags)
|
|
151
|
+
)
|
|
152
|
+
anime.my_list_status = result
|
|
153
|
+
self._cache_list([anime])
|
|
154
|
+
return result
|
|
155
|
+
|
|
156
|
+
def delete_show(self, anime: MALAnime) -> None:
|
|
157
|
+
self.local_list.mappings.pop(anime.id)
|
|
158
|
+
self.local_list.write(self.user_id)
|
|
159
|
+
|
|
160
|
+
self.mal.remove_from_anime_list(anime.id)
|
|
161
|
+
|
|
162
|
+
def map_from_mal(
|
|
163
|
+
self, anime: MALAnime, mapping: Optional[Anime] = None
|
|
164
|
+
) -> Optional[Anime]:
|
|
165
|
+
if mapping is not None:
|
|
166
|
+
self._write_mapping(anime, mapping)
|
|
167
|
+
return mapping
|
|
168
|
+
|
|
169
|
+
if self.local_list.mappings[anime.id].mappings:
|
|
170
|
+
map = list(self.local_list.mappings[anime.id].mappings.values())[0]
|
|
171
|
+
provider = next(filter(lambda x: x.NAME == map.provider, list_providers()))
|
|
172
|
+
return Anime(provider(), map.name, map.identifier, map.languages)
|
|
173
|
+
|
|
174
|
+
config = Config()
|
|
175
|
+
result = None
|
|
176
|
+
for p in get_prefered_providers("mal"):
|
|
177
|
+
adapter = MyAnimeListAdapter(self.mal, p)
|
|
178
|
+
result = adapter.from_myanimelist(
|
|
179
|
+
anime,
|
|
180
|
+
config.mal_mapping_min_similarity,
|
|
181
|
+
config.mal_mapping_use_filters,
|
|
182
|
+
config.mal_mapping_use_alternatives,
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
if result is not None:
|
|
186
|
+
break
|
|
187
|
+
|
|
188
|
+
if result:
|
|
189
|
+
self._write_mapping(anime, result)
|
|
190
|
+
|
|
191
|
+
return result
|
|
192
|
+
|
|
193
|
+
def map_from_provider(
|
|
194
|
+
self, anime: Anime, mapping: Optional[MALAnime] = None
|
|
195
|
+
) -> Optional[MALAnime]:
|
|
196
|
+
if mapping is not None:
|
|
197
|
+
self._write_mapping(mapping, anime)
|
|
198
|
+
return mapping
|
|
199
|
+
|
|
200
|
+
for _, m in self.local_list.mappings.items():
|
|
201
|
+
existing = m.mappings.get(f"{anime.provider.NAME}:{anime.identifier}", None)
|
|
202
|
+
if existing:
|
|
203
|
+
return m.mal_anime
|
|
204
|
+
|
|
205
|
+
config = Config()
|
|
206
|
+
adapter = MyAnimeListAdapter(self.mal, anime.provider)
|
|
207
|
+
result = adapter.from_provider(
|
|
208
|
+
anime,
|
|
209
|
+
config.mal_mapping_min_similarity,
|
|
210
|
+
config.mal_mapping_use_alternatives,
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
if result:
|
|
214
|
+
self._write_mapping(result, anime)
|
|
215
|
+
|
|
216
|
+
return result
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import
|
|
1
|
+
import os
|
|
2
|
+
from abc import ABC, abstractmethod
|
|
2
3
|
from dataclasses import dataclass
|
|
3
4
|
from typing import Callable, List
|
|
4
|
-
from abc import ABC, abstractmethod
|
|
5
5
|
|
|
6
|
-
from anipy_cli.
|
|
7
|
-
from anipy_cli.
|
|
6
|
+
from anipy_cli.colors import color, colors
|
|
7
|
+
from anipy_cli.util import error
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
@dataclass(frozen=True)
|
|
@@ -20,12 +20,10 @@ class MenuOption:
|
|
|
20
20
|
class MenuBase(ABC):
|
|
21
21
|
@property
|
|
22
22
|
@abstractmethod
|
|
23
|
-
def menu_options(self) -> List[MenuOption]:
|
|
24
|
-
pass
|
|
23
|
+
def menu_options(self) -> List[MenuOption]: ...
|
|
25
24
|
|
|
26
25
|
@abstractmethod
|
|
27
|
-
def print_header(self):
|
|
28
|
-
pass
|
|
26
|
+
def print_header(self): ...
|
|
29
27
|
|
|
30
28
|
def run(self):
|
|
31
29
|
self.print_options()
|
|
@@ -44,10 +42,8 @@ class MenuBase(ABC):
|
|
|
44
42
|
|
|
45
43
|
def print_options(self, clear_screen=True):
|
|
46
44
|
if clear_screen:
|
|
47
|
-
|
|
45
|
+
os.system("cls" if os.name == "nt" else "clear")
|
|
46
|
+
|
|
48
47
|
self.print_header()
|
|
49
48
|
for op in self.menu_options:
|
|
50
49
|
print(op)
|
|
51
|
-
|
|
52
|
-
def quit(self):
|
|
53
|
-
sys.exit()
|