anipy-cli 3.3.2__tar.gz → 3.3.3__tar.gz

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.

Files changed (26) hide show
  1. {anipy_cli-3.3.2 → anipy_cli-3.3.3}/PKG-INFO +2 -2
  2. {anipy_cli-3.3.2 → anipy_cli-3.3.3}/pyproject.toml +2 -2
  3. {anipy_cli-3.3.2 → anipy_cli-3.3.3}/src/anipy_cli/__init__.py +1 -1
  4. {anipy_cli-3.3.2 → anipy_cli-3.3.3}/src/anipy_cli/clis/download_cli.py +1 -1
  5. {anipy_cli-3.3.2 → anipy_cli-3.3.3}/src/anipy_cli/config.py +41 -1
  6. {anipy_cli-3.3.2 → anipy_cli-3.3.3}/src/anipy_cli/download_component.py +5 -3
  7. {anipy_cli-3.3.2 → anipy_cli-3.3.3}/src/anipy_cli/menus/mal_menu.py +1 -1
  8. {anipy_cli-3.3.2 → anipy_cli-3.3.3}/src/anipy_cli/menus/menu.py +2 -1
  9. {anipy_cli-3.3.2 → anipy_cli-3.3.3}/src/anipy_cli/menus/seasonal_menu.py +1 -1
  10. {anipy_cli-3.3.2 → anipy_cli-3.3.3}/src/anipy_cli/util.py +19 -1
  11. {anipy_cli-3.3.2 → anipy_cli-3.3.3}/README.md +0 -0
  12. {anipy_cli-3.3.2 → anipy_cli-3.3.3}/src/anipy_cli/arg_parser.py +0 -0
  13. {anipy_cli-3.3.2 → anipy_cli-3.3.3}/src/anipy_cli/cli.py +0 -0
  14. {anipy_cli-3.3.2 → anipy_cli-3.3.3}/src/anipy_cli/clis/__init__.py +0 -0
  15. {anipy_cli-3.3.2 → anipy_cli-3.3.3}/src/anipy_cli/clis/base_cli.py +0 -0
  16. {anipy_cli-3.3.2 → anipy_cli-3.3.3}/src/anipy_cli/clis/binge_cli.py +0 -0
  17. {anipy_cli-3.3.2 → anipy_cli-3.3.3}/src/anipy_cli/clis/default_cli.py +0 -0
  18. {anipy_cli-3.3.2 → anipy_cli-3.3.3}/src/anipy_cli/clis/history_cli.py +0 -0
  19. {anipy_cli-3.3.2 → anipy_cli-3.3.3}/src/anipy_cli/clis/mal_cli.py +0 -0
  20. {anipy_cli-3.3.2 → anipy_cli-3.3.3}/src/anipy_cli/clis/seasonal_cli.py +0 -0
  21. {anipy_cli-3.3.2 → anipy_cli-3.3.3}/src/anipy_cli/colors.py +0 -0
  22. {anipy_cli-3.3.2 → anipy_cli-3.3.3}/src/anipy_cli/discord.py +0 -0
  23. {anipy_cli-3.3.2 → anipy_cli-3.3.3}/src/anipy_cli/mal_proxy.py +0 -0
  24. {anipy_cli-3.3.2 → anipy_cli-3.3.3}/src/anipy_cli/menus/__init__.py +0 -0
  25. {anipy_cli-3.3.2 → anipy_cli-3.3.3}/src/anipy_cli/menus/base_menu.py +0 -0
  26. {anipy_cli-3.3.2 → anipy_cli-3.3.3}/src/anipy_cli/prompts.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: anipy-cli
3
- Version: 3.3.2
3
+ Version: 3.3.3
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.2,<4.0.0)
18
+ Requires-Dist: anipy-api (>=3.3.3,<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,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "anipy-cli"
3
- version = "3.3.2"
3
+ version = "3.3.3"
4
4
  description = "Watch and Download anime from the comfort of your Terminal"
5
5
  authors = ["sdaqo <sdaqo.dev@protonmail.com>"]
6
6
  license = "GPL-3.0"
@@ -20,7 +20,7 @@ yaspin = "^3.0.2"
20
20
  inquirerpy = "^0.3.4"
21
21
  appdirs = "^1.4.4"
22
22
  pypresence = "^4.3.0"
23
- anipy-api = "^3.3.2"
23
+ anipy-api = "^3.3.3"
24
24
 
25
25
  [tool.poetry.scripts]
26
26
  anipy-cli = "anipy_cli.cli:run_cli"
@@ -1,2 +1,2 @@
1
1
  __appname__ = "anipy-cli"
2
- __version__ = "3.3.2"
2
+ __version__ = "3.3.3"
@@ -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)
@@ -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."""
@@ -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, cliArgs: CliArgs, dl_path: Path) -> None:
35
- self.options = cliArgs
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)
145
147
  )
146
148
 
147
149
  @staticmethod
@@ -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)
@@ -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)
236
237
  )
237
238
 
238
239
  if Config().auto_open_dl_defaultcli:
@@ -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
 
@@ -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,22 @@ 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") -> 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
+ arguments = [
131
+ str(path), anime.name,
132
+ str(stream.episode), anime.provider.NAME,
133
+ str(stream.resolution), stream.language.name
134
+ ]
135
+ for s in scripts:
136
+ sub_proc = sp.Popen([s, *arguments])
137
+ sub_proc.wait(timeout) # type: ignore
138
+
139
+ return hook
122
140
 
123
141
  def parse_episode_ranges(ranges: str, episodes: List["Episode"]) -> List["Episode"]:
124
142
  picked = set()
File without changes