anipy-cli 2.7.17__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/__init__.py +2 -20
- anipy_cli/anilist_proxy.py +229 -0
- anipy_cli/arg_parser.py +109 -21
- anipy_cli/cli.py +98 -0
- anipy_cli/clis/__init__.py +17 -0
- anipy_cli/clis/anilist_cli.py +62 -0
- anipy_cli/clis/base_cli.py +34 -0
- anipy_cli/clis/binge_cli.py +96 -0
- anipy_cli/clis/default_cli.py +115 -0
- anipy_cli/clis/download_cli.py +85 -0
- anipy_cli/clis/history_cli.py +96 -0
- anipy_cli/clis/mal_cli.py +71 -0
- anipy_cli/{cli/clis → clis}/seasonal_cli.py +9 -6
- anipy_cli/colors.py +14 -8
- anipy_cli/config.py +387 -90
- anipy_cli/discord.py +34 -0
- anipy_cli/download_component.py +194 -0
- anipy_cli/logger.py +200 -0
- anipy_cli/mal_proxy.py +228 -0
- anipy_cli/menus/__init__.py +6 -0
- anipy_cli/menus/anilist_menu.py +671 -0
- anipy_cli/{cli/menus → menus}/base_menu.py +9 -14
- anipy_cli/menus/mal_menu.py +657 -0
- anipy_cli/menus/menu.py +265 -0
- anipy_cli/menus/seasonal_menu.py +270 -0
- anipy_cli/prompts.py +387 -0
- anipy_cli/util.py +268 -0
- anipy_cli-3.8.2.dist-info/METADATA +71 -0
- anipy_cli-3.8.2.dist-info/RECORD +31 -0
- {anipy_cli-2.7.17.dist-info → anipy_cli-3.8.2.dist-info}/WHEEL +1 -2
- anipy_cli-3.8.2.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 -102
- anipy_cli/cli/menus/seasonal_menu.py +0 -174
- anipy_cli/cli/util.py +0 -118
- anipy_cli/download.py +0 -454
- anipy_cli/history.py +0 -83
- anipy_cli/mal.py +0 -645
- anipy_cli/misc.py +0 -227
- anipy_cli/player/__init__.py +0 -1
- anipy_cli/player/player.py +0 -33
- anipy_cli/player/players/__init__.py +0 -3
- anipy_cli/player/players/base.py +0 -106
- anipy_cli/player/players/mpv.py +0 -19
- anipy_cli/player/players/mpv_contrl.py +0 -37
- anipy_cli/player/players/syncplay.py +0 -19
- anipy_cli/player/players/vlc.py +0 -18
- anipy_cli/query.py +0 -92
- anipy_cli/run_anipy_cli.py +0 -14
- anipy_cli/seasonal.py +0 -106
- anipy_cli/url_handler.py +0 -442
- anipy_cli/version.py +0 -1
- anipy_cli-2.7.17.dist-info/LICENSE +0 -674
- anipy_cli-2.7.17.dist-info/METADATA +0 -159
- anipy_cli-2.7.17.dist-info/RECORD +0 -43
- anipy_cli-2.7.17.dist-info/entry_points.txt +0 -2
- anipy_cli-2.7.17.dist-info/top_level.txt +0 -1
anipy_cli/misc.py
DELETED
|
@@ -1,227 +0,0 @@
|
|
|
1
|
-
import os
|
|
2
|
-
import requests
|
|
3
|
-
import sys
|
|
4
|
-
import json
|
|
5
|
-
import time
|
|
6
|
-
from pypresence import Presence
|
|
7
|
-
from pypresence.exceptions import DiscordNotFound
|
|
8
|
-
from bs4 import BeautifulSoup
|
|
9
|
-
from dataclasses import dataclass
|
|
10
|
-
from typing import Union
|
|
11
|
-
|
|
12
|
-
from anipy_cli.config import Config
|
|
13
|
-
from anipy_cli.colors import colors, color, cprint
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
@dataclass
|
|
17
|
-
class Entry:
|
|
18
|
-
"""
|
|
19
|
-
This is the class that saves
|
|
20
|
-
metadata about a show.
|
|
21
|
-
"""
|
|
22
|
-
|
|
23
|
-
show_name: str = ""
|
|
24
|
-
category_url: str = ""
|
|
25
|
-
ep_url: str = ""
|
|
26
|
-
embed_url: str = ""
|
|
27
|
-
stream_url: str = ""
|
|
28
|
-
ep: Union[int, float] = 0
|
|
29
|
-
latest_ep: Union[int, float] = 0
|
|
30
|
-
quality: str = ""
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
def clear_console():
|
|
34
|
-
os.system("cls" if os.name == "nt" else "clear")
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
def error(error: str) -> None:
|
|
38
|
-
"""
|
|
39
|
-
Error function for better error handling,
|
|
40
|
-
that takes an error and prints it to stderr.
|
|
41
|
-
"""
|
|
42
|
-
sys.stderr.write(color(colors.ERROR, "anipy-cli: error: " + error) + "\n")
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
def response_err(req, link) -> None:
|
|
46
|
-
"""
|
|
47
|
-
Function that checks if a request
|
|
48
|
-
was succesfull.
|
|
49
|
-
"""
|
|
50
|
-
if req.ok:
|
|
51
|
-
pass
|
|
52
|
-
else:
|
|
53
|
-
error(
|
|
54
|
-
f"requested url not available/blocked: {link}: response-code: {req.status_code}"
|
|
55
|
-
)
|
|
56
|
-
sys.exit()
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
def loc_err(soup, link: str, element: str) -> None:
|
|
60
|
-
"""
|
|
61
|
-
Function that checks if beautifulsoup
|
|
62
|
-
could locate an element.
|
|
63
|
-
"""
|
|
64
|
-
if soup == None:
|
|
65
|
-
error(f"could not locate {element}: {link}")
|
|
66
|
-
sys.exit()
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
def keyboard_inter() -> None:
|
|
70
|
-
cprint(colors.ERROR, "\nanipy-cli: error: interrupted")
|
|
71
|
-
sys.exit()
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
def parsenum(n: str):
|
|
75
|
-
"""
|
|
76
|
-
Parse String to either
|
|
77
|
-
int or float.
|
|
78
|
-
"""
|
|
79
|
-
|
|
80
|
-
try:
|
|
81
|
-
return int(n)
|
|
82
|
-
except ValueError:
|
|
83
|
-
return float(n)
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
def read_json(path):
|
|
87
|
-
"""
|
|
88
|
-
Read a json file, if
|
|
89
|
-
it doesn't exist create it,
|
|
90
|
-
along with user_files folder.
|
|
91
|
-
"""
|
|
92
|
-
while True:
|
|
93
|
-
try:
|
|
94
|
-
with open(path, "r") as data:
|
|
95
|
-
json_data = json.load(data)
|
|
96
|
-
break
|
|
97
|
-
|
|
98
|
-
except FileNotFoundError:
|
|
99
|
-
try:
|
|
100
|
-
Config().user_files_path.mkdir(exist_ok=True, parents=True)
|
|
101
|
-
path.touch(exist_ok=True)
|
|
102
|
-
# avoids error on empty json file
|
|
103
|
-
with path.open("a") as f:
|
|
104
|
-
f.write("{}")
|
|
105
|
-
continue
|
|
106
|
-
|
|
107
|
-
except PermissionError:
|
|
108
|
-
error(f"Unable to create {path} due to permissions.")
|
|
109
|
-
sys.exit()
|
|
110
|
-
|
|
111
|
-
return json_data
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
def print_names(names):
|
|
115
|
-
"""
|
|
116
|
-
Cli function that takes a
|
|
117
|
-
list of names and prints
|
|
118
|
-
them to the terminal.
|
|
119
|
-
"""
|
|
120
|
-
for number, value in enumerate(names, 1):
|
|
121
|
-
value_color = colors.END
|
|
122
|
-
if number % 2 == 0:
|
|
123
|
-
value_color = colors.YELLOW
|
|
124
|
-
|
|
125
|
-
cprint(colors.GREEN, f"[{number}] ", value_color, value)
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
def get_anime_info(category_url: str) -> dict:
|
|
129
|
-
"""
|
|
130
|
-
Get metadata about an anime.
|
|
131
|
-
"""
|
|
132
|
-
r = requests.get(category_url)
|
|
133
|
-
soup = BeautifulSoup(r.text, "html.parser")
|
|
134
|
-
info_body = soup.find("div", {"class": "anime_info_body_bg"})
|
|
135
|
-
image_url = info_body.find("img")["src"]
|
|
136
|
-
other_info = info_body.find_all("p", {"class": "type"})
|
|
137
|
-
info_dict = {
|
|
138
|
-
"image_url": image_url,
|
|
139
|
-
"type": other_info[0].text.replace("\n", "").replace("Type: ", ""),
|
|
140
|
-
"synopsis": other_info[1].text.replace("\n", ""),
|
|
141
|
-
"genres": [
|
|
142
|
-
x["title"]
|
|
143
|
-
for x in BeautifulSoup(str(other_info[2]), "html.parser").find_all("a")
|
|
144
|
-
],
|
|
145
|
-
"release_year": other_info[3].text.replace("Released: ", ""),
|
|
146
|
-
"status": other_info[4].text.replace("\n", "").replace("Status: ", ""),
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
return info_dict
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
def search_in_season_on_gogo(s_year, s_name):
|
|
153
|
-
page = 1
|
|
154
|
-
content = True
|
|
155
|
-
gogo_anime_season_list = []
|
|
156
|
-
while content:
|
|
157
|
-
r = requests.get(
|
|
158
|
-
f"{Config().gogoanime_url}/sub-category/{s_name}-{s_year}-anime",
|
|
159
|
-
params={"page": page},
|
|
160
|
-
)
|
|
161
|
-
soup = BeautifulSoup(r.content, "html.parser")
|
|
162
|
-
wrapper_div = soup.find("div", attrs={"class": "last_episodes"})
|
|
163
|
-
try:
|
|
164
|
-
anime_items = wrapper_div.findAll("li")
|
|
165
|
-
for link in anime_items:
|
|
166
|
-
link_a = link.find("p", attrs={"class": "name"}).find("a")
|
|
167
|
-
name = link_a.get("title")
|
|
168
|
-
gogo_anime_season_list.append(
|
|
169
|
-
{
|
|
170
|
-
"name": name,
|
|
171
|
-
"category_url": "{}{}".format(
|
|
172
|
-
Config().gogoanime_url, link_a.get("href")
|
|
173
|
-
),
|
|
174
|
-
}
|
|
175
|
-
)
|
|
176
|
-
|
|
177
|
-
except AttributeError:
|
|
178
|
-
content = False
|
|
179
|
-
|
|
180
|
-
page += 1
|
|
181
|
-
filtered_list = filter_anime_list_dub_sub(gogo_anime_season_list)
|
|
182
|
-
|
|
183
|
-
return filtered_list
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
def filter_anime_list_dub_sub(gogo_anime_season_list):
|
|
187
|
-
if "sub" not in Config().anime_types and "dub" in Config().anime_types:
|
|
188
|
-
filtered_list = [
|
|
189
|
-
x for x in gogo_anime_season_list if "(dub)" in x["name"].lower()
|
|
190
|
-
]
|
|
191
|
-
|
|
192
|
-
elif "dub" not in Config().anime_types and "sub" in Config().anime_types:
|
|
193
|
-
filtered_list = [
|
|
194
|
-
x for x in gogo_anime_season_list if "(dub)" not in x["name"].lower()
|
|
195
|
-
]
|
|
196
|
-
|
|
197
|
-
else:
|
|
198
|
-
filtered_list = gogo_anime_season_list
|
|
199
|
-
return filtered_list
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
def dc_presence_connect():
|
|
203
|
-
CLIENT_ID = 966365883691855942
|
|
204
|
-
rpc_client = None
|
|
205
|
-
try:
|
|
206
|
-
rpc_client = Presence(CLIENT_ID)
|
|
207
|
-
rpc_client.connect()
|
|
208
|
-
cprint(colors.GREEN, "Initialized Discord Presence Client")
|
|
209
|
-
except DiscordNotFound:
|
|
210
|
-
rpc_client = None
|
|
211
|
-
cprint(colors.RED, "Discord is not opened, can't initialize Discord Presence")
|
|
212
|
-
except ConnectionError:
|
|
213
|
-
rpc_client = None
|
|
214
|
-
cprint(colors.RED, "Can't Connect to discord.")
|
|
215
|
-
|
|
216
|
-
return rpc_client
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
def dc_presence(media_title, category_url, rpc_client):
|
|
220
|
-
info = get_anime_info(category_url)
|
|
221
|
-
rpc_client.update(
|
|
222
|
-
details="Watching anime via anipy-cli",
|
|
223
|
-
state=media_title,
|
|
224
|
-
large_image=info["image_url"],
|
|
225
|
-
small_image="https://github.com/Dankni95/ulauncher-anime/raw/master/images/icon.png",
|
|
226
|
-
start=int(time.time()),
|
|
227
|
-
)
|
anipy_cli/player/__init__.py
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
from anipy_cli.player.player import get_player, PlayerBaseType
|
anipy_cli/player/player.py
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import sys
|
|
2
|
-
from typing import TypeVar
|
|
3
|
-
|
|
4
|
-
from anipy_cli.config import Config
|
|
5
|
-
from anipy_cli.misc import error
|
|
6
|
-
from anipy_cli.player.players import Mpv, Vlc, Syncplay
|
|
7
|
-
from anipy_cli.player.players.base import PlayerBase
|
|
8
|
-
|
|
9
|
-
PlayerBaseType = TypeVar("PlayerBaseType", bound=PlayerBase)
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
def get_player(rpc_client=None, player_override=None) -> PlayerBaseType:
|
|
13
|
-
cfg = Config()
|
|
14
|
-
|
|
15
|
-
player = player_override
|
|
16
|
-
|
|
17
|
-
if not player_override:
|
|
18
|
-
player = cfg.player_path
|
|
19
|
-
|
|
20
|
-
if player == "mpv" and cfg.reuse_mpv_window:
|
|
21
|
-
from anipy_cli.player.players.mpv_contrl import MpvControllable
|
|
22
|
-
|
|
23
|
-
return MpvControllable(rpc_client=rpc_client)
|
|
24
|
-
|
|
25
|
-
if player in ("mpv", "mpvnet"):
|
|
26
|
-
return Mpv(rpc_client=rpc_client, mpv_exec_name=player)
|
|
27
|
-
elif player == "vlc":
|
|
28
|
-
return Vlc(rpc_client=rpc_client)
|
|
29
|
-
elif player == "syncplay":
|
|
30
|
-
return Syncplay(rpc_client=rpc_client)
|
|
31
|
-
else:
|
|
32
|
-
error(f"Specified player `{player}` is unknown")
|
|
33
|
-
sys.exit()
|
anipy_cli/player/players/base.py
DELETED
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
import os
|
|
2
|
-
import subprocess as sp
|
|
3
|
-
import sys
|
|
4
|
-
from typing import List
|
|
5
|
-
from abc import ABC, abstractmethod
|
|
6
|
-
|
|
7
|
-
from anipy_cli.colors import cprint, colors
|
|
8
|
-
from anipy_cli.history import history
|
|
9
|
-
from anipy_cli.misc import dc_presence, Entry
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
class PlayerBase(ABC):
|
|
13
|
-
@property
|
|
14
|
-
@abstractmethod
|
|
15
|
-
def rpc_client(self):
|
|
16
|
-
pass
|
|
17
|
-
|
|
18
|
-
@abstractmethod
|
|
19
|
-
def play_title(self, entry: Entry):
|
|
20
|
-
pass
|
|
21
|
-
|
|
22
|
-
@abstractmethod
|
|
23
|
-
def play_file(self, path: str):
|
|
24
|
-
pass
|
|
25
|
-
|
|
26
|
-
@abstractmethod
|
|
27
|
-
def wait(self):
|
|
28
|
-
pass
|
|
29
|
-
|
|
30
|
-
@abstractmethod
|
|
31
|
-
def kill_player(self):
|
|
32
|
-
pass
|
|
33
|
-
|
|
34
|
-
def _start_dc_presence(self, entry: Entry):
|
|
35
|
-
if self.rpc_client:
|
|
36
|
-
dc_media_title = f"{entry.show_name} | {entry.ep}/{entry.latest_ep}"
|
|
37
|
-
dc_presence(dc_media_title, entry.category_url, self.rpc_client)
|
|
38
|
-
|
|
39
|
-
@staticmethod
|
|
40
|
-
def _write_hist(entry: Entry):
|
|
41
|
-
history(entry).write_hist()
|
|
42
|
-
|
|
43
|
-
@staticmethod
|
|
44
|
-
def _get_media_title(entry: Entry):
|
|
45
|
-
return (
|
|
46
|
-
entry.show_name
|
|
47
|
-
+ " - Episode: "
|
|
48
|
-
+ str(entry.ep)
|
|
49
|
-
+ " - "
|
|
50
|
-
+ str(entry.quality)
|
|
51
|
-
)
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
class SubProcessPlayerBase(PlayerBase):
|
|
55
|
-
def __init__(
|
|
56
|
-
self, player_args_template: List[str], player_exec: str, rpc_client=None
|
|
57
|
-
):
|
|
58
|
-
self._rpc_client = rpc_client
|
|
59
|
-
self._sub_proc = None
|
|
60
|
-
self._player_args_template = player_args_template
|
|
61
|
-
self._player_exec = player_exec
|
|
62
|
-
|
|
63
|
-
@property
|
|
64
|
-
def rpc_client(self):
|
|
65
|
-
return self._rpc_client
|
|
66
|
-
|
|
67
|
-
def play_title(self, entry):
|
|
68
|
-
player_cmd = [
|
|
69
|
-
i.format(media_title=self._get_media_title(entry), **vars(entry))
|
|
70
|
-
for i in self._player_args_template
|
|
71
|
-
]
|
|
72
|
-
player_cmd.insert(0, self._player_exec)
|
|
73
|
-
|
|
74
|
-
if isinstance(self._sub_proc, sp.Popen):
|
|
75
|
-
self.kill_player()
|
|
76
|
-
|
|
77
|
-
self._sub_proc = self._open_sproc(player_cmd)
|
|
78
|
-
|
|
79
|
-
self._write_hist(entry)
|
|
80
|
-
self._start_dc_presence(entry)
|
|
81
|
-
|
|
82
|
-
def play_file(self, path):
|
|
83
|
-
if isinstance(self._sub_proc, sp.Popen):
|
|
84
|
-
self.kill_player()
|
|
85
|
-
|
|
86
|
-
player_cmd = [self._player_exec, path]
|
|
87
|
-
self._sub_proc = self._open_sproc(player_cmd)
|
|
88
|
-
|
|
89
|
-
def wait(self):
|
|
90
|
-
self._sub_proc.wait()
|
|
91
|
-
|
|
92
|
-
def kill_player(self):
|
|
93
|
-
self._sub_proc.kill()
|
|
94
|
-
|
|
95
|
-
@staticmethod
|
|
96
|
-
def _open_sproc(player_command: List[str]) -> sp.Popen:
|
|
97
|
-
try:
|
|
98
|
-
if os.name in ("nt", "dos"):
|
|
99
|
-
sub_proc = sp.Popen(player_command)
|
|
100
|
-
else:
|
|
101
|
-
sub_proc = sp.Popen(player_command, stdout=sp.PIPE, stderr=sp.DEVNULL)
|
|
102
|
-
except FileNotFoundError as e:
|
|
103
|
-
cprint(colors.RED, "Error:" + str(e))
|
|
104
|
-
sys.exit()
|
|
105
|
-
|
|
106
|
-
return sub_proc
|
anipy_cli/player/players/mpv.py
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
from anipy_cli.player.players.base import SubProcessPlayerBase
|
|
2
|
-
from anipy_cli.config import Config
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
class Mpv(SubProcessPlayerBase):
|
|
6
|
-
def __init__(self, rpc_client=None, mpv_exec_name: str = "mpv"):
|
|
7
|
-
player_args_template = [
|
|
8
|
-
"{stream_url}",
|
|
9
|
-
"--force-media-title={media_title}",
|
|
10
|
-
"--referrer={embed_url}",
|
|
11
|
-
"--force-window=immediate",
|
|
12
|
-
*Config().mpv_commandline_options,
|
|
13
|
-
]
|
|
14
|
-
|
|
15
|
-
super().__init__(
|
|
16
|
-
player_args_template=player_args_template,
|
|
17
|
-
rpc_client=rpc_client,
|
|
18
|
-
player_exec=mpv_exec_name,
|
|
19
|
-
)
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import mpv
|
|
2
|
-
|
|
3
|
-
from anipy_cli.player.players.base import PlayerBase
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
class MpvControllable(mpv.MPV, PlayerBase):
|
|
7
|
-
def __init__(self, rpc_client=None):
|
|
8
|
-
super().__init__(
|
|
9
|
-
input_default_bindings=True,
|
|
10
|
-
input_vo_keyboard=True,
|
|
11
|
-
force_window="immediate",
|
|
12
|
-
title="MPV - Receiving Links from anipy-cli",
|
|
13
|
-
osc=True,
|
|
14
|
-
)
|
|
15
|
-
self._rpc_client = rpc_client
|
|
16
|
-
|
|
17
|
-
@property
|
|
18
|
-
def rpc_client(self):
|
|
19
|
-
return self._rpc_client
|
|
20
|
-
|
|
21
|
-
def play_title(self, entry):
|
|
22
|
-
self.force_media_title = self._get_media_title(entry)
|
|
23
|
-
|
|
24
|
-
self.referrer = entry.embed_url
|
|
25
|
-
self.play(entry.stream_url)
|
|
26
|
-
|
|
27
|
-
self._write_hist(entry)
|
|
28
|
-
self._start_dc_presence(entry)
|
|
29
|
-
|
|
30
|
-
def play_file(self, path: str):
|
|
31
|
-
self.play(path)
|
|
32
|
-
|
|
33
|
-
def wait(self):
|
|
34
|
-
self.wait_for_playback()
|
|
35
|
-
|
|
36
|
-
def kill_player(self):
|
|
37
|
-
self.terminate()
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
from anipy_cli.player.players.base import SubProcessPlayerBase
|
|
2
|
-
from anipy_cli.config import Config
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
class Syncplay(SubProcessPlayerBase):
|
|
6
|
-
def __init__(self, rpc_client=None):
|
|
7
|
-
self.player_exec = "syncplay"
|
|
8
|
-
player_args_template = [
|
|
9
|
-
"--" "--http-referrer='{embed_url}'",
|
|
10
|
-
"--meta-title='{media_title}'",
|
|
11
|
-
"{stream_url}",
|
|
12
|
-
*Config().mpv_commandline_options,
|
|
13
|
-
]
|
|
14
|
-
|
|
15
|
-
super().__init__(
|
|
16
|
-
rpc_client=rpc_client,
|
|
17
|
-
player_exec=self.player_exec,
|
|
18
|
-
player_args_template=player_args_template,
|
|
19
|
-
)
|
anipy_cli/player/players/vlc.py
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
from anipy_cli.player.players.base import SubProcessPlayerBase
|
|
2
|
-
from anipy_cli.config import Config
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
class Vlc(SubProcessPlayerBase):
|
|
6
|
-
def __init__(self, rpc_client=None):
|
|
7
|
-
player_args_template = [
|
|
8
|
-
"--http-referrer='{embed_url}'",
|
|
9
|
-
"--meta-title='{media_title}'",
|
|
10
|
-
"{stream_url}",
|
|
11
|
-
*Config().vlc_commandline_options,
|
|
12
|
-
]
|
|
13
|
-
|
|
14
|
-
super().__init__(
|
|
15
|
-
rpc_client=rpc_client,
|
|
16
|
-
player_args_template=player_args_template,
|
|
17
|
-
player_exec="vlc",
|
|
18
|
-
)
|
anipy_cli/query.py
DELETED
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
import requests
|
|
2
|
-
import re
|
|
3
|
-
from bs4 import BeautifulSoup
|
|
4
|
-
|
|
5
|
-
from anipy_cli.misc import loc_err, response_err, error, print_names
|
|
6
|
-
from anipy_cli.colors import colors, cinput
|
|
7
|
-
from anipy_cli.config import Config
|
|
8
|
-
|
|
9
|
-
base_url = Config().gogoanime_url
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
class query:
|
|
13
|
-
"""
|
|
14
|
-
Class to get query results
|
|
15
|
-
from a search parameter.
|
|
16
|
-
Takes an empty entry or one
|
|
17
|
-
with fields filled.
|
|
18
|
-
"""
|
|
19
|
-
|
|
20
|
-
def __init__(self, search_param, entry) -> None:
|
|
21
|
-
self.entry = entry
|
|
22
|
-
self.search_url = base_url + f"/search.html?keyword={search_param}"
|
|
23
|
-
r = requests.get(self.search_url)
|
|
24
|
-
response_err(r, self.search_url)
|
|
25
|
-
self.soup = BeautifulSoup(r.content, "html.parser")
|
|
26
|
-
|
|
27
|
-
def get_pages(self):
|
|
28
|
-
"""Get count of result pages of query"""
|
|
29
|
-
pages = self.soup.find_all(
|
|
30
|
-
"a", attrs={"data-page": re.compile(r"^ *\d[\d ]*$")}
|
|
31
|
-
)
|
|
32
|
-
loc_err(pages, self.search_url, "page-count")
|
|
33
|
-
pages = [x.get("data-page") for x in pages]
|
|
34
|
-
try:
|
|
35
|
-
self.pages = int(pages[-1])
|
|
36
|
-
except:
|
|
37
|
-
self.pages = 1
|
|
38
|
-
|
|
39
|
-
def get_links(self):
|
|
40
|
-
"""
|
|
41
|
-
Get all category links and names of a query
|
|
42
|
-
and returns them.
|
|
43
|
-
"""
|
|
44
|
-
self.get_pages()
|
|
45
|
-
self.links = []
|
|
46
|
-
self.names = []
|
|
47
|
-
for i in range(self.pages):
|
|
48
|
-
req_link = self.search_url + f"&page={i + 1}"
|
|
49
|
-
r = requests.get(req_link)
|
|
50
|
-
response_err(r, req_link)
|
|
51
|
-
self.soup = BeautifulSoup(r.content, "html.parser")
|
|
52
|
-
|
|
53
|
-
for link in self.soup.find_all("p", attrs={"class": "name"}):
|
|
54
|
-
name_lower = link.text.lower()
|
|
55
|
-
if len(Config().anime_types) == 1:
|
|
56
|
-
if "sub" in Config().anime_types and "(dub)" in name_lower:
|
|
57
|
-
continue
|
|
58
|
-
elif "dub" in Config().anime_types and "(dub)" not in name_lower:
|
|
59
|
-
continue
|
|
60
|
-
|
|
61
|
-
loc_err(link, req_link, "query results")
|
|
62
|
-
self.names.append(link.text.replace("\n", ""))
|
|
63
|
-
a_tag = link.findChildren("a", recursive=False)
|
|
64
|
-
self.links.append(a_tag[0].get("href"))
|
|
65
|
-
|
|
66
|
-
if not self.links:
|
|
67
|
-
return 0
|
|
68
|
-
else:
|
|
69
|
-
return self.links, self.names
|
|
70
|
-
|
|
71
|
-
def pick_show(self, cancelable=False):
|
|
72
|
-
"""
|
|
73
|
-
Cli Function that
|
|
74
|
-
Lets you pick a show from
|
|
75
|
-
yout query, filles the category_url
|
|
76
|
-
field from the entry and returns it.
|
|
77
|
-
"""
|
|
78
|
-
print_names(self.names)
|
|
79
|
-
if cancelable:
|
|
80
|
-
print(f"{colors.GREEN}[C] {colors.YELLOW} Cancel.")
|
|
81
|
-
while True:
|
|
82
|
-
inp = cinput("Enter Number: ", colors.CYAN)
|
|
83
|
-
try:
|
|
84
|
-
self.entry.category_url = base_url + self.links[int(inp) - 1]
|
|
85
|
-
self.entry.show_name = self.names[int(inp) - 1]
|
|
86
|
-
break
|
|
87
|
-
except:
|
|
88
|
-
if cancelable and inp.lower() == "c":
|
|
89
|
-
return False
|
|
90
|
-
error("Invalid Input")
|
|
91
|
-
|
|
92
|
-
return self.entry
|
anipy_cli/run_anipy_cli.py
DELETED
anipy_cli/seasonal.py
DELETED
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
import json
|
|
2
|
-
import sys
|
|
3
|
-
|
|
4
|
-
from anipy_cli.config import Config
|
|
5
|
-
from anipy_cli.url_handler import epHandler
|
|
6
|
-
from anipy_cli.misc import Entry, error, read_json
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
class Seasonal:
|
|
10
|
-
def __init__(self):
|
|
11
|
-
self.entry = Entry()
|
|
12
|
-
|
|
13
|
-
def latest_eps(self):
|
|
14
|
-
"""
|
|
15
|
-
returns a dict like so:
|
|
16
|
-
{"name": {
|
|
17
|
-
"ep_list": [[ep, ep-link], [], ...],
|
|
18
|
-
"category_url": "https://"
|
|
19
|
-
},
|
|
20
|
-
"another anime": {
|
|
21
|
-
...
|
|
22
|
-
},
|
|
23
|
-
}
|
|
24
|
-
"""
|
|
25
|
-
|
|
26
|
-
self.read_save_data()
|
|
27
|
-
names = [i for i in self.json]
|
|
28
|
-
categ_urls = []
|
|
29
|
-
user_eps = []
|
|
30
|
-
for i in names:
|
|
31
|
-
categ_urls.append(self.json[i]["category_url"])
|
|
32
|
-
user_eps.append(self.json[i]["ep"])
|
|
33
|
-
|
|
34
|
-
latest_urls = {}
|
|
35
|
-
for i, e, n in zip(categ_urls, user_eps, names):
|
|
36
|
-
self.entry.category_url = i
|
|
37
|
-
ep_class = epHandler(self.entry)
|
|
38
|
-
latest = ep_class.get_latest()
|
|
39
|
-
eps_range = list(range(e + 1, latest + 1))
|
|
40
|
-
ep_urls = []
|
|
41
|
-
for j in eps_range:
|
|
42
|
-
self.entry.ep = j
|
|
43
|
-
ep_class = epHandler(self.entry)
|
|
44
|
-
entry = ep_class.gen_eplink()
|
|
45
|
-
ep_urls.append([j, entry.ep_url])
|
|
46
|
-
|
|
47
|
-
latest_urls.update({n: {"ep_list": ep_urls, "category_url": i}})
|
|
48
|
-
|
|
49
|
-
return latest_urls
|
|
50
|
-
|
|
51
|
-
def read_save_data(self):
|
|
52
|
-
self.json = read_json(Config().seasonal_file_path)
|
|
53
|
-
|
|
54
|
-
def write_seasonals(self):
|
|
55
|
-
try:
|
|
56
|
-
with Config().seasonal_file_path.open("w") as f:
|
|
57
|
-
json.dump(self.json, f, indent=4)
|
|
58
|
-
|
|
59
|
-
except PermissionError:
|
|
60
|
-
error("Unable to write to history file due permissions.")
|
|
61
|
-
sys.exit()
|
|
62
|
-
|
|
63
|
-
def update_show(self, name, categ_url, ep=None):
|
|
64
|
-
self.read_save_data()
|
|
65
|
-
|
|
66
|
-
if name not in [x for x in self.json]:
|
|
67
|
-
return 0
|
|
68
|
-
|
|
69
|
-
self.entry.category_url = categ_url
|
|
70
|
-
if ep is not None:
|
|
71
|
-
self.json[name]["ep"] = ep
|
|
72
|
-
|
|
73
|
-
else:
|
|
74
|
-
self.json[name]["ep"] = epHandler(self.entry).get_latest()
|
|
75
|
-
self.write_seasonals()
|
|
76
|
-
|
|
77
|
-
def add_show(self, name, categ_url, start_ep):
|
|
78
|
-
self.read_save_data()
|
|
79
|
-
|
|
80
|
-
if name in [x for x in self.json]:
|
|
81
|
-
return 0
|
|
82
|
-
|
|
83
|
-
dic = {name: {"ep": start_ep, "category_url": categ_url}}
|
|
84
|
-
|
|
85
|
-
self.json.update(dic)
|
|
86
|
-
self.write_seasonals()
|
|
87
|
-
|
|
88
|
-
def del_show(self, name):
|
|
89
|
-
self.read_save_data()
|
|
90
|
-
|
|
91
|
-
if name not in [x for x in self.json]:
|
|
92
|
-
return 0
|
|
93
|
-
|
|
94
|
-
self.json.pop(name)
|
|
95
|
-
self.write_seasonals()
|
|
96
|
-
|
|
97
|
-
def list_seasonals(self):
|
|
98
|
-
self.read_save_data()
|
|
99
|
-
|
|
100
|
-
return [[i, self.json[i]["ep"]] for i in self.json]
|
|
101
|
-
|
|
102
|
-
def export_seasonals(self):
|
|
103
|
-
self.read_save_data()
|
|
104
|
-
return [
|
|
105
|
-
[i, self.json[i]["category_url"], self.json[i]["ep"]] for i in self.json
|
|
106
|
-
]
|