anipy-cli 3.3.2__py3-none-any.whl → 3.3.4__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/clis/download_cli.py +1 -1
- anipy_cli/config.py +41 -1
- anipy_cli/download_component.py +5 -3
- anipy_cli/menus/mal_menu.py +1 -1
- anipy_cli/menus/menu.py +2 -1
- anipy_cli/menus/seasonal_menu.py +1 -1
- anipy_cli/util.py +21 -1
- {anipy_cli-3.3.2.dist-info → anipy_cli-3.3.4.dist-info}/METADATA +2 -2
- {anipy_cli-3.3.2.dist-info → anipy_cli-3.3.4.dist-info}/RECORD +12 -12
- {anipy_cli-3.3.2.dist-info → anipy_cli-3.3.4.dist-info}/WHEEL +0 -0
- {anipy_cli-3.3.2.dist-info → anipy_cli-3.3.4.dist-info}/entry_points.txt +0 -0
anipy_cli/__init__.py
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
__appname__ = "anipy-cli"
|
|
2
|
-
__version__ = "3.3.
|
|
2
|
+
__version__ = "3.3.4"
|
anipy_cli/clis/download_cli.py
CHANGED
|
@@ -69,7 +69,7 @@ class DownloadCli(CliBase):
|
|
|
69
69
|
assert self.anime is not None
|
|
70
70
|
assert self.lang is not None
|
|
71
71
|
|
|
72
|
-
errors = DownloadComponent(self.options, self.dl_path).download_anime(
|
|
72
|
+
errors = DownloadComponent(self.options, self.dl_path, "download").download_anime(
|
|
73
73
|
[(self.anime, self.lang, self.episodes)], only_skip_ep_on_err=True
|
|
74
74
|
)
|
|
75
75
|
DownloadComponent.serve_download_errors(errors, only_skip_ep_on_err=True)
|
anipy_cli/config.py
CHANGED
|
@@ -85,7 +85,6 @@ class Config:
|
|
|
85
85
|
defaults = {
|
|
86
86
|
"default": ["anivibe"],
|
|
87
87
|
"download": ["anivibe"],
|
|
88
|
-
"history": ["anivibe"],
|
|
89
88
|
"seasonal": ["anivibe"],
|
|
90
89
|
"binge": ["anivibe"],
|
|
91
90
|
"mal": ["anivibe"],
|
|
@@ -223,6 +222,47 @@ class Config:
|
|
|
223
222
|
)
|
|
224
223
|
return str(Path(value).with_suffix(""))
|
|
225
224
|
|
|
225
|
+
@property
|
|
226
|
+
def post_download_scripts(self) -> Dict[str, List[str]]:
|
|
227
|
+
"""With this option you can define scripts that run after a file
|
|
228
|
+
has been downloaded. As with the 'providers' option, you can configure
|
|
229
|
+
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.
|
|
233
|
+
You can also define a timeout (in seconds), after which a script will be terminated,
|
|
234
|
+
if set to null there will be no timeout and any script will run forever.
|
|
235
|
+
|
|
236
|
+
A "script" is a path to an executable file which accepts following parameters (in this order):
|
|
237
|
+
1. Path to the file
|
|
238
|
+
2. Name of series
|
|
239
|
+
3. Episode
|
|
240
|
+
4. Provider
|
|
241
|
+
5. Quality
|
|
242
|
+
6. Language profile
|
|
243
|
+
|
|
244
|
+
Examples:
|
|
245
|
+
post_download_scripts:
|
|
246
|
+
default: [] # used in default mode and for the history
|
|
247
|
+
download: ["/scripts/send_notification.sh", "/scripts/move_and_rename.sh"]
|
|
248
|
+
seasonal: ["link_to_jellyfin.bat", "jellyfin_library_update.exe"] # All executable files should work, including windows specific
|
|
249
|
+
mal: ["hard_link_to_shoko"]
|
|
250
|
+
timeout: 60 # terminate any script after running for 60 seconds
|
|
251
|
+
"""
|
|
252
|
+
defaults = {
|
|
253
|
+
"default": [],
|
|
254
|
+
"download": [],
|
|
255
|
+
"seasonal": [],
|
|
256
|
+
"mal": [],
|
|
257
|
+
"timeout": None
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
value = self._get_value("post_download_scripts", defaults, dict)
|
|
261
|
+
|
|
262
|
+
# Merge Dicts
|
|
263
|
+
defaults.update(value)
|
|
264
|
+
return defaults
|
|
265
|
+
|
|
226
266
|
@property
|
|
227
267
|
def dc_presence(self) -> bool:
|
|
228
268
|
"""Activate discord presence, only works with discord open."""
|
anipy_cli/download_component.py
CHANGED
|
@@ -3,7 +3,7 @@ from typing import List, Protocol, Tuple
|
|
|
3
3
|
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
|
-
from anipy_cli.util import DotSpinner, get_download_path
|
|
6
|
+
from anipy_cli.util import DotSpinner, get_download_path, get_post_download_scripts_hook
|
|
7
7
|
|
|
8
8
|
from anipy_api.anime import Anime
|
|
9
9
|
from anipy_api.download import Downloader
|
|
@@ -31,9 +31,10 @@ class DownloadComponent:
|
|
|
31
31
|
the ani-py CLI.
|
|
32
32
|
"""
|
|
33
33
|
|
|
34
|
-
def __init__(self,
|
|
35
|
-
self.options =
|
|
34
|
+
def __init__(self, options: CliArgs, dl_path: Path, mode: str) -> None:
|
|
35
|
+
self.options = options
|
|
36
36
|
self.dl_path = dl_path
|
|
37
|
+
self.mode = mode
|
|
37
38
|
|
|
38
39
|
def download_anime(
|
|
39
40
|
self,
|
|
@@ -142,6 +143,7 @@ class DownloadComponent:
|
|
|
142
143
|
get_download_path(anime, stream, parent_directory=self.dl_path),
|
|
143
144
|
container=config.remux_to,
|
|
144
145
|
ffmpeg=self.options.ffmpeg or config.ffmpeg_hls,
|
|
146
|
+
post_dl_cb=get_post_download_scripts_hook(self.mode, anime, spinner)
|
|
145
147
|
)
|
|
146
148
|
|
|
147
149
|
@staticmethod
|
anipy_cli/menus/mal_menu.py
CHANGED
|
@@ -270,7 +270,7 @@ class MALMenu(MenuBase):
|
|
|
270
270
|
episode=int(ep),
|
|
271
271
|
)
|
|
272
272
|
|
|
273
|
-
errors = DownloadComponent(self.options, self.dl_path).download_anime(
|
|
273
|
+
errors = DownloadComponent(self.options, self.dl_path, "mal").download_anime(
|
|
274
274
|
new_picked, on_successful_download
|
|
275
275
|
)
|
|
276
276
|
DownloadComponent.serve_download_errors(errors)
|
anipy_cli/menus/menu.py
CHANGED
|
@@ -10,7 +10,7 @@ from anipy_api.locallist import LocalList
|
|
|
10
10
|
from anipy_cli.colors import colors, cprint
|
|
11
11
|
from anipy_cli.config import Config
|
|
12
12
|
from anipy_cli.menus.base_menu import MenuBase, MenuOption
|
|
13
|
-
from anipy_cli.util import DotSpinner, error, get_download_path, migrate_locallist
|
|
13
|
+
from anipy_cli.util import DotSpinner, error, get_download_path, get_post_download_scripts_hook, migrate_locallist
|
|
14
14
|
from anipy_cli.prompts import pick_episode_prompt, search_show_prompt
|
|
15
15
|
|
|
16
16
|
|
|
@@ -233,6 +233,7 @@ class Menu(MenuBase):
|
|
|
233
233
|
get_download_path(self.anime, self.stream),
|
|
234
234
|
container=config.remux_to,
|
|
235
235
|
ffmpeg=self.options.ffmpeg or config.ffmpeg_hls,
|
|
236
|
+
post_dl_cb=get_post_download_scripts_hook("default", self.anime, s)
|
|
236
237
|
)
|
|
237
238
|
|
|
238
239
|
if Config().auto_open_dl_defaultcli:
|
anipy_cli/menus/seasonal_menu.py
CHANGED
|
@@ -270,7 +270,7 @@ class SeasonalMenu(MenuBase):
|
|
|
270
270
|
def on_successful_download(anime: Anime, ep: Episode, lang: LanguageTypeEnum):
|
|
271
271
|
self.seasonal_list.update(anime, episode=ep, language=lang)
|
|
272
272
|
|
|
273
|
-
failed_series = DownloadComponent(self.options, self.dl_path).download_anime(
|
|
273
|
+
failed_series = DownloadComponent(self.options, self.dl_path, "seasonal").download_anime(
|
|
274
274
|
picked, on_successful_download
|
|
275
275
|
)
|
|
276
276
|
|
anipy_cli/util.py
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import sys
|
|
2
2
|
import time
|
|
3
|
+
import subprocess as sp
|
|
3
4
|
from pathlib import Path
|
|
4
5
|
from typing import (
|
|
5
6
|
TYPE_CHECKING,
|
|
7
|
+
Callable,
|
|
6
8
|
Iterator,
|
|
7
9
|
List,
|
|
8
10
|
Literal,
|
|
@@ -13,7 +15,7 @@ from typing import (
|
|
|
13
15
|
)
|
|
14
16
|
|
|
15
17
|
from anipy_api.anime import Anime
|
|
16
|
-
from anipy_api.download import Downloader
|
|
18
|
+
from anipy_api.download import Downloader, PostDownloadCallback
|
|
17
19
|
from anipy_api.error import LangTypeNotAvailableError
|
|
18
20
|
from anipy_api.locallist import LocalListData, LocalListEntry
|
|
19
21
|
from anipy_api.player import get_player
|
|
@@ -119,6 +121,24 @@ def get_download_path(
|
|
|
119
121
|
|
|
120
122
|
return download_folder / anime_name / filename
|
|
121
123
|
|
|
124
|
+
def get_post_download_scripts_hook(mode: str, anime: "Anime", spinner: DotSpinner) -> PostDownloadCallback:
|
|
125
|
+
config = Config()
|
|
126
|
+
scripts = config.post_download_scripts[mode]
|
|
127
|
+
timeout = config.post_download_scripts["timeout"]
|
|
128
|
+
|
|
129
|
+
def hook(path: Path, stream: "ProviderStream"):
|
|
130
|
+
spinner.hide()
|
|
131
|
+
arguments = [
|
|
132
|
+
str(path), anime.name,
|
|
133
|
+
str(stream.episode), anime.provider.NAME,
|
|
134
|
+
str(stream.resolution), stream.language.name
|
|
135
|
+
]
|
|
136
|
+
for s in scripts:
|
|
137
|
+
sub_proc = sp.Popen([s, *arguments])
|
|
138
|
+
sub_proc.wait(timeout) # type: ignore
|
|
139
|
+
spinner.show()
|
|
140
|
+
|
|
141
|
+
return hook
|
|
122
142
|
|
|
123
143
|
def parse_episode_ranges(ranges: str, episodes: List["Episode"]) -> List["Episode"]:
|
|
124
144
|
picked = set()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: anipy-cli
|
|
3
|
-
Version: 3.3.
|
|
3
|
+
Version: 3.3.4
|
|
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.3.
|
|
18
|
+
Requires-Dist: anipy-api (>=3.3.4,<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,27 +1,27 @@
|
|
|
1
|
-
anipy_cli/__init__.py,sha256=
|
|
1
|
+
anipy_cli/__init__.py,sha256=S3uHlqaqwDVDsjRxtpyGtKztgf9jrVf3CtG8pj149ZU,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=
|
|
8
|
+
anipy_cli/clis/download_cli.py,sha256=K8hqkxTeQ5Ao5LR2rimxX099IWf2I_KDGySIZzuIIVI,2320
|
|
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
|
-
anipy_cli/config.py,sha256=
|
|
13
|
+
anipy_cli/config.py,sha256=mbR7D1472Z6lLLw1_Vx_H0cH7fQVJJBPpIVXA6SMvy0,17751
|
|
14
14
|
anipy_cli/discord.py,sha256=c6mdqnEdblzZBYs3cGP66oDeS4ySm59OfTRP-R-Duls,1160
|
|
15
|
-
anipy_cli/download_component.py,sha256=
|
|
15
|
+
anipy_cli/download_component.py,sha256=3GCPoWIEjhN1rBtOt8NQg9lqBTI4OHV3gG2RZ3NMzcA,5248
|
|
16
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
|
-
anipy_cli/menus/mal_menu.py,sha256=
|
|
20
|
-
anipy_cli/menus/menu.py,sha256=
|
|
21
|
-
anipy_cli/menus/seasonal_menu.py,sha256=
|
|
19
|
+
anipy_cli/menus/mal_menu.py,sha256=jAVJh7K5d0BCnoT4qUIZ7CavrnrctXj-eg9k8E90ulE,23697
|
|
20
|
+
anipy_cli/menus/menu.py,sha256=ejTOGVG8fFJhTT7t23kA_uBbF5pkNcdvj4cGAIPvdt8,8101
|
|
21
|
+
anipy_cli/menus/seasonal_menu.py,sha256=diABjK7LIDlXUoMydhDyLG9ba7J4UFmeD5AyGO3GPAI,10648
|
|
22
22
|
anipy_cli/prompts.py,sha256=lQDrb8IlhsQhOpLG4sflamprTSn-PlCy41tGpXIowdo,10156
|
|
23
|
-
anipy_cli/util.py,sha256=
|
|
24
|
-
anipy_cli-3.3.
|
|
25
|
-
anipy_cli-3.3.
|
|
26
|
-
anipy_cli-3.3.
|
|
27
|
-
anipy_cli-3.3.
|
|
23
|
+
anipy_cli/util.py,sha256=PxUPRxKCCTig1IHwmzH4m4Jofr4r-bg9lHyID3l5CIA,8944
|
|
24
|
+
anipy_cli-3.3.4.dist-info/METADATA,sha256=WzPCCp1Uy-mTM6Gu2D1JlLD6qVKX8VlcTOFNtllkrO4,3469
|
|
25
|
+
anipy_cli-3.3.4.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
|
|
26
|
+
anipy_cli-3.3.4.dist-info/entry_points.txt,sha256=86iXpcm_ECFndrt0JAI2mqYfXC2Ar7mGi0iOaxCrNP0,51
|
|
27
|
+
anipy_cli-3.3.4.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|