weeb-cli 2.14.0__tar.gz → 2.15.0__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.
Files changed (116) hide show
  1. {weeb_cli-2.14.0/weeb_cli.egg-info → weeb_cli-2.15.0}/PKG-INFO +69 -10
  2. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/README.md +68 -9
  3. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/pyproject.toml +1 -1
  4. weeb_cli-2.15.0/tests/test_sdk.py +291 -0
  5. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli/__init__.py +21 -2
  6. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli/commands/downloads.py +3 -2
  7. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli/commands/search/watch_flow.py +38 -24
  8. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli/config.py +4 -3
  9. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli/i18n.py +6 -2
  10. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli/locales/de.json +1 -14
  11. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli/locales/en.json +1 -14
  12. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli/locales/pl.json +3 -14
  13. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli/locales/tr.json +4 -15
  14. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli/main.py +19 -19
  15. weeb_cli-2.15.0/weeb_cli/sdk.py +480 -0
  16. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli/services/cache.py +0 -9
  17. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli/services/database.py +29 -19
  18. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli/services/downloader.py +2 -29
  19. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli/services/local_library.py +3 -2
  20. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli/services/logger.py +2 -2
  21. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli/services/tracker.py +110 -272
  22. {weeb_cli-2.14.0 → weeb_cli-2.15.0/weeb_cli.egg-info}/PKG-INFO +69 -10
  23. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli.egg-info/SOURCES.txt +2 -0
  24. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/LICENSE +0 -0
  25. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/setup.cfg +0 -0
  26. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/tests/test_anilist_tracker.py +0 -0
  27. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/tests/test_aniworld_provider.py +0 -0
  28. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/tests/test_api.py +0 -0
  29. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/tests/test_cache.py +0 -0
  30. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/tests/test_exceptions.py +0 -0
  31. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/tests/test_kitsu_tracker.py +0 -0
  32. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/tests/test_mal_tracker.py +0 -0
  33. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/tests/test_providers.py +0 -0
  34. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/tests/test_restful_api.py +0 -0
  35. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/tests/test_sanitizer.py +0 -0
  36. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/tests/test_sanitizer_security.py +0 -0
  37. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli/__main__.py +0 -0
  38. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli/commands/__init__.py +0 -0
  39. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli/commands/api.py +0 -0
  40. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli/commands/library.py +0 -0
  41. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli/commands/search/__init__.py +0 -0
  42. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli/commands/search/anime_details.py +0 -0
  43. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli/commands/search/download_flow.py +0 -0
  44. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli/commands/search/episode_utils.py +0 -0
  45. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli/commands/search/search_handlers.py +0 -0
  46. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli/commands/search/stream_utils.py +0 -0
  47. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli/commands/search.py +0 -0
  48. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli/commands/serve.py +0 -0
  49. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli/commands/serve_restful.py +0 -0
  50. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli/commands/settings/__init__.py +0 -0
  51. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli/commands/settings/settings_backup.py +0 -0
  52. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli/commands/settings/settings_cache.py +0 -0
  53. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli/commands/settings/settings_config.py +0 -0
  54. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli/commands/settings/settings_download.py +0 -0
  55. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli/commands/settings/settings_drives.py +0 -0
  56. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli/commands/settings/settings_menu.py +0 -0
  57. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli/commands/settings/settings_shortcuts.py +0 -0
  58. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli/commands/settings/settings_trackers.py +0 -0
  59. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli/commands/settings.py +0 -0
  60. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli/commands/setup.py +0 -0
  61. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli/commands/watchlist.py +0 -0
  62. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli/exceptions.py +0 -0
  63. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli/providers/__init__.py +0 -0
  64. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli/providers/base.py +0 -0
  65. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli/providers/de/__init__.py +0 -0
  66. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli/providers/de/aniworld.py +0 -0
  67. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli/providers/en/__init__.py +0 -0
  68. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli/providers/en/allanime.py +0 -0
  69. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli/providers/en/hianime.py +0 -0
  70. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli/providers/extractors/__init__.py +0 -0
  71. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli/providers/extractors/doodstream.py +0 -0
  72. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli/providers/extractors/filemoon.py +0 -0
  73. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli/providers/extractors/megacloud.py +0 -0
  74. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli/providers/extractors/streamtape.py +0 -0
  75. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli/providers/extractors/vidoza.py +0 -0
  76. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli/providers/extractors/voe.py +0 -0
  77. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli/providers/pl/__init__.py +0 -0
  78. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli/providers/pl/docchi.py +0 -0
  79. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli/providers/registry.py +0 -0
  80. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli/providers/tr/__init__.py +0 -0
  81. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli/providers/tr/animecix.py +0 -0
  82. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli/providers/tr/anizle.py +0 -0
  83. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli/providers/tr/turkanime.py +0 -0
  84. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli/providers/tr/weeb.py +0 -0
  85. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli/services/__init__.py +0 -0
  86. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli/services/_base.py +0 -0
  87. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli/services/_tracker_base.py +0 -0
  88. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli/services/aniskip.py +0 -0
  89. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli/services/dependency_manager.py +0 -0
  90. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli/services/details.py +0 -0
  91. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli/services/discord_rpc.py +0 -0
  92. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli/services/error_handler.py +0 -0
  93. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli/services/headless_downloader.py +0 -0
  94. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli/services/notifier.py +0 -0
  95. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli/services/player.py +0 -0
  96. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli/services/progress.py +0 -0
  97. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli/services/scraper.py +0 -0
  98. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli/services/search.py +0 -0
  99. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli/services/shortcuts.py +0 -0
  100. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli/services/stream_validator.py +0 -0
  101. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli/services/updater.py +0 -0
  102. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli/services/watch.py +0 -0
  103. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli/templates/anilist_error.html +0 -0
  104. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli/templates/anilist_success.html +0 -0
  105. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli/templates/mal_error.html +0 -0
  106. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli/templates/mal_success.html +0 -0
  107. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli/ui/__init__.py +0 -0
  108. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli/ui/header.py +0 -0
  109. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli/ui/menu.py +0 -0
  110. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli/ui/prompt.py +0 -0
  111. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli/utils/__init__.py +0 -0
  112. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli/utils/sanitizer.py +0 -0
  113. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli.egg-info/dependency_links.txt +0 -0
  114. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli.egg-info/entry_points.txt +0 -0
  115. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli.egg-info/requires.txt +0 -0
  116. {weeb_cli-2.14.0 → weeb_cli-2.15.0}/weeb_cli.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: weeb-cli
3
- Version: 2.14.0
3
+ Version: 2.15.0
4
4
  Summary: No browser, no ads, no distractions. Just you and an unparalleled anime viewing experience.
5
5
  Author-email: ewgsta <ewgst@proton.me>
6
6
  License-Expression: GPL-3.0-only
@@ -55,7 +55,7 @@ Dynamic: license-file
55
55
  </p>
56
56
 
57
57
  <div align="center">
58
- <a href="README.md">English</a> | <a href="md/tr/README.md">Türkçe</a> | <a href="md/de/README.md">Deutsch</a> | <a href="md/pl/README.md">Polski</a>
58
+ <a href="README.md">English</a> | <a href="docs/md/tr/README.md">Türkçe</a> | <a href="docs/md/de/README.md">Deutsch</a> | <a href="docs/md/pl/README.md">Polski</a>
59
59
  </div>
60
60
  <br>
61
61
 
@@ -164,24 +164,77 @@ For scripts, automation, and AI agents, weeb-cli provides JSON API commands that
164
164
  weeb-cli api providers
165
165
 
166
166
  # Search for anime (returns IDs)
167
- weeb-cli api search "Angel Beats"
167
+ weeb-cli api search "Angel Beats" --provider hianime
168
168
  # Returns: [{"id": "12345", "title": "Angel Beats!", ...}]
169
169
 
170
170
  # List episodes (use ID from search)
171
- weeb-cli api episodes 12345 --season 1
171
+ weeb-cli api episodes 12345 --season 1 --provider hianime
172
172
 
173
173
  # Get stream URLs for an episode
174
- weeb-cli api streams 12345 --season 1 --episode 1
174
+ weeb-cli api streams 12345 --season 1 --episode 1 --provider hianime
175
175
 
176
176
  # Get anime details
177
- weeb-cli api details 12345
177
+ weeb-cli api details 12345 --provider hianime
178
178
 
179
179
  # Download an episode
180
- weeb-cli api download 12345 --season 1 --episode 1 --output ./downloads
180
+ weeb-cli api download 12345 --season 1 --episode 1 --provider hianime --output ./downloads
181
181
  ```
182
182
 
183
+ **Provider Selection:**
184
+ All API commands support the `--provider` (or `-p`) option to select which anime source to use:
185
+ - Turkish: `animecix`, `turkanime`, `anizle`, `weeb`
186
+ - English: `hianime`, `allanime`
187
+ - German: `aniworld`
188
+ - Polish: `docchi`
189
+
190
+ Default provider is `animecix` if not specified. Use `weeb-cli api providers` to see all available providers with their language and region information.
191
+
183
192
  All API commands output JSON to stdout.
184
193
 
194
+ ### Python SDK (Programmatic Access)
195
+
196
+ For Python applications, weeb-cli provides a native SDK that doesn't require spawning processes:
197
+
198
+ ```python
199
+ from weeb_cli import WeebSDK
200
+
201
+ # Initialize SDK
202
+ sdk = WeebSDK(default_provider="hianime")
203
+
204
+ # Search for anime
205
+ results = sdk.search("One Piece")
206
+ for anime in results:
207
+ print(f"{anime.title} ({anime.year})")
208
+
209
+ # Get episodes
210
+ episodes = sdk.get_episodes(results[0].id, season=1)
211
+
212
+ # Get stream URLs
213
+ streams = sdk.get_streams(
214
+ anime_id=results[0].id,
215
+ episode_id=episodes[0].id
216
+ )
217
+
218
+ # Download episode
219
+ path = sdk.download_episode(
220
+ anime_id=results[0].id,
221
+ season=1,
222
+ episode=1,
223
+ output_dir="./downloads"
224
+ )
225
+ print(f"Downloaded to: {path}")
226
+ ```
227
+
228
+ **SDK Features:**
229
+ - No subprocess overhead - direct Python API
230
+ - Type-safe with full type hints
231
+ - Automatic provider discovery
232
+ - Same caching as CLI mode
233
+ - Thread-safe operations
234
+ - Headless mode (no database/TUI dependencies)
235
+
236
+ See [SDK Documentation](https://ewgsta.github.io/weeb-cli/api/sdk/) for complete API reference.
237
+
185
238
  ### Sonarr/*arr Integration (Serve Mode)
186
239
 
187
240
  weeb-cli can run as a Torznab-compatible server for Sonarr and other *arr applications:
@@ -193,12 +246,18 @@ weeb-cli serve --port 9876 \
193
246
  --watch-dir /downloads/watch \
194
247
  --completed-dir /downloads/completed \
195
248
  --sonarr-url http://sonarr:8989 \
196
- --sonarr-api-key YOUR_KEY \
197
- --providers animecix,anizle,turkanime
249
+ --sonarr-api-key YOUR_KEY
198
250
  ```
199
251
 
200
252
  Then add `http://weeb-cli-host:9876` as a Torznab indexer in Sonarr with category 5070 (TV/Anime). The server includes a blackhole download worker that automatically processes grabbed episodes.
201
253
 
254
+ **Docker Support:**
255
+ ```bash
256
+ docker-compose -f docs/docker-compose.torznab.yml up -d
257
+ ```
258
+
259
+ See [Torznab Server Documentation](https://ewgsta.github.io/weeb-cli/cli/serve-mode/) for full details.
260
+
202
261
  ### RESTful API Server
203
262
 
204
263
  For web/mobile applications and custom integrations, weeb-cli provides a RESTful API server:
@@ -221,7 +280,7 @@ All available providers are loaded automatically. Select which provider to use v
221
280
 
222
281
  **Docker Support:**
223
282
  ```bash
224
- docker-compose -f docker-compose.restful.yml up -d
283
+ docker-compose -f docs/docker-compose.restful.yml up -d
225
284
  ```
226
285
 
227
286
  See [RESTful API Documentation](https://ewgsta.github.io/weeb-cli/cli/restful-api/) for full details.
@@ -9,7 +9,7 @@
9
9
  </p>
10
10
 
11
11
  <div align="center">
12
- <a href="README.md">English</a> | <a href="md/tr/README.md">Türkçe</a> | <a href="md/de/README.md">Deutsch</a> | <a href="md/pl/README.md">Polski</a>
12
+ <a href="README.md">English</a> | <a href="docs/md/tr/README.md">Türkçe</a> | <a href="docs/md/de/README.md">Deutsch</a> | <a href="docs/md/pl/README.md">Polski</a>
13
13
  </div>
14
14
  <br>
15
15
 
@@ -118,24 +118,77 @@ For scripts, automation, and AI agents, weeb-cli provides JSON API commands that
118
118
  weeb-cli api providers
119
119
 
120
120
  # Search for anime (returns IDs)
121
- weeb-cli api search "Angel Beats"
121
+ weeb-cli api search "Angel Beats" --provider hianime
122
122
  # Returns: [{"id": "12345", "title": "Angel Beats!", ...}]
123
123
 
124
124
  # List episodes (use ID from search)
125
- weeb-cli api episodes 12345 --season 1
125
+ weeb-cli api episodes 12345 --season 1 --provider hianime
126
126
 
127
127
  # Get stream URLs for an episode
128
- weeb-cli api streams 12345 --season 1 --episode 1
128
+ weeb-cli api streams 12345 --season 1 --episode 1 --provider hianime
129
129
 
130
130
  # Get anime details
131
- weeb-cli api details 12345
131
+ weeb-cli api details 12345 --provider hianime
132
132
 
133
133
  # Download an episode
134
- weeb-cli api download 12345 --season 1 --episode 1 --output ./downloads
134
+ weeb-cli api download 12345 --season 1 --episode 1 --provider hianime --output ./downloads
135
135
  ```
136
136
 
137
+ **Provider Selection:**
138
+ All API commands support the `--provider` (or `-p`) option to select which anime source to use:
139
+ - Turkish: `animecix`, `turkanime`, `anizle`, `weeb`
140
+ - English: `hianime`, `allanime`
141
+ - German: `aniworld`
142
+ - Polish: `docchi`
143
+
144
+ Default provider is `animecix` if not specified. Use `weeb-cli api providers` to see all available providers with their language and region information.
145
+
137
146
  All API commands output JSON to stdout.
138
147
 
148
+ ### Python SDK (Programmatic Access)
149
+
150
+ For Python applications, weeb-cli provides a native SDK that doesn't require spawning processes:
151
+
152
+ ```python
153
+ from weeb_cli import WeebSDK
154
+
155
+ # Initialize SDK
156
+ sdk = WeebSDK(default_provider="hianime")
157
+
158
+ # Search for anime
159
+ results = sdk.search("One Piece")
160
+ for anime in results:
161
+ print(f"{anime.title} ({anime.year})")
162
+
163
+ # Get episodes
164
+ episodes = sdk.get_episodes(results[0].id, season=1)
165
+
166
+ # Get stream URLs
167
+ streams = sdk.get_streams(
168
+ anime_id=results[0].id,
169
+ episode_id=episodes[0].id
170
+ )
171
+
172
+ # Download episode
173
+ path = sdk.download_episode(
174
+ anime_id=results[0].id,
175
+ season=1,
176
+ episode=1,
177
+ output_dir="./downloads"
178
+ )
179
+ print(f"Downloaded to: {path}")
180
+ ```
181
+
182
+ **SDK Features:**
183
+ - No subprocess overhead - direct Python API
184
+ - Type-safe with full type hints
185
+ - Automatic provider discovery
186
+ - Same caching as CLI mode
187
+ - Thread-safe operations
188
+ - Headless mode (no database/TUI dependencies)
189
+
190
+ See [SDK Documentation](https://ewgsta.github.io/weeb-cli/api/sdk/) for complete API reference.
191
+
139
192
  ### Sonarr/*arr Integration (Serve Mode)
140
193
 
141
194
  weeb-cli can run as a Torznab-compatible server for Sonarr and other *arr applications:
@@ -147,12 +200,18 @@ weeb-cli serve --port 9876 \
147
200
  --watch-dir /downloads/watch \
148
201
  --completed-dir /downloads/completed \
149
202
  --sonarr-url http://sonarr:8989 \
150
- --sonarr-api-key YOUR_KEY \
151
- --providers animecix,anizle,turkanime
203
+ --sonarr-api-key YOUR_KEY
152
204
  ```
153
205
 
154
206
  Then add `http://weeb-cli-host:9876` as a Torznab indexer in Sonarr with category 5070 (TV/Anime). The server includes a blackhole download worker that automatically processes grabbed episodes.
155
207
 
208
+ **Docker Support:**
209
+ ```bash
210
+ docker-compose -f docs/docker-compose.torznab.yml up -d
211
+ ```
212
+
213
+ See [Torznab Server Documentation](https://ewgsta.github.io/weeb-cli/cli/serve-mode/) for full details.
214
+
156
215
  ### RESTful API Server
157
216
 
158
217
  For web/mobile applications and custom integrations, weeb-cli provides a RESTful API server:
@@ -175,7 +234,7 @@ All available providers are loaded automatically. Select which provider to use v
175
234
 
176
235
  **Docker Support:**
177
236
  ```bash
178
- docker-compose -f docker-compose.restful.yml up -d
237
+ docker-compose -f docs/docker-compose.restful.yml up -d
179
238
  ```
180
239
 
181
240
  See [RESTful API Documentation](https://ewgsta.github.io/weeb-cli/cli/restful-api/) for full details.
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "weeb-cli"
7
- version = "2.14.0"
7
+ version = "2.15.0"
8
8
  description = "No browser, no ads, no distractions. Just you and an unparalleled anime viewing experience."
9
9
  readme = "README.md"
10
10
  authors = [{ name = "ewgsta", email = "ewgst@proton.me" }]
@@ -0,0 +1,291 @@
1
+ """Tests for Weeb CLI SDK."""
2
+
3
+ import pytest
4
+ from unittest.mock import Mock, patch, MagicMock
5
+
6
+ from weeb_cli.sdk import WeebSDK, list_providers, get_provider_info
7
+ from weeb_cli.providers.base import AnimeResult, Episode, StreamLink, AnimeDetails
8
+ from weeb_cli.exceptions import ProviderError, WeebCLIError
9
+
10
+
11
+ class TestWeebSDK:
12
+ """Test suite for WeebSDK class."""
13
+
14
+ def test_init_default(self):
15
+ """Test SDK initialization with defaults."""
16
+ sdk = WeebSDK()
17
+ assert sdk.headless is True
18
+ assert sdk.default_provider == "animecix"
19
+
20
+ def test_init_custom_provider(self):
21
+ """Test SDK initialization with custom provider."""
22
+ sdk = WeebSDK(default_provider="hianime")
23
+ assert sdk.default_provider == "hianime"
24
+
25
+ def test_init_non_headless(self):
26
+ """Test SDK initialization in non-headless mode."""
27
+ sdk = WeebSDK(headless=False)
28
+ assert sdk.headless is False
29
+
30
+ @patch('weeb_cli.sdk._list_providers')
31
+ def test_list_providers(self, mock_list):
32
+ """Test listing providers."""
33
+ mock_list.return_value = [
34
+ {"name": "animecix", "lang": "tr", "region": "TR"},
35
+ {"name": "hianime", "lang": "en", "region": "US"}
36
+ ]
37
+
38
+ sdk = WeebSDK()
39
+ providers = sdk.list_providers()
40
+
41
+ assert len(providers) == 2
42
+ assert providers[0]["name"] == "animecix"
43
+ assert providers[1]["name"] == "hianime"
44
+
45
+ @patch('weeb_cli.sdk._list_providers')
46
+ def test_get_provider_info(self, mock_list):
47
+ """Test getting provider info."""
48
+ mock_list.return_value = [
49
+ {"name": "animecix", "lang": "tr", "region": "TR"},
50
+ {"name": "hianime", "lang": "en", "region": "US"}
51
+ ]
52
+
53
+ sdk = WeebSDK()
54
+ info = sdk.get_provider_info("hianime")
55
+
56
+ assert info is not None
57
+ assert info["name"] == "hianime"
58
+ assert info["lang"] == "en"
59
+
60
+ @patch('weeb_cli.sdk._list_providers')
61
+ def test_get_provider_info_not_found(self, mock_list):
62
+ """Test getting info for non-existent provider."""
63
+ mock_list.return_value = [
64
+ {"name": "animecix", "lang": "tr", "region": "TR"}
65
+ ]
66
+
67
+ sdk = WeebSDK()
68
+ info = sdk.get_provider_info("invalid")
69
+
70
+ assert info is None
71
+
72
+ @patch('weeb_cli.sdk.get_provider')
73
+ def test_search_success(self, mock_get_provider):
74
+ """Test successful anime search."""
75
+ mock_provider = Mock()
76
+ mock_provider.search.return_value = [
77
+ AnimeResult(id="1", title="Anime 1", year=2020),
78
+ AnimeResult(id="2", title="Anime 2", year=2021)
79
+ ]
80
+ mock_get_provider.return_value = mock_provider
81
+
82
+ sdk = WeebSDK()
83
+ results = sdk.search("test", provider="hianime")
84
+
85
+ assert len(results) == 2
86
+ assert results[0].title == "Anime 1"
87
+ mock_provider.search.assert_called_once_with("test")
88
+
89
+ @patch('weeb_cli.sdk.get_provider')
90
+ def test_search_provider_not_found(self, mock_get_provider):
91
+ """Test search with invalid provider."""
92
+ mock_get_provider.return_value = None
93
+
94
+ sdk = WeebSDK()
95
+
96
+ with pytest.raises(ProviderError) as exc_info:
97
+ sdk.search("test", provider="invalid")
98
+
99
+ assert "Provider not found or disabled" in str(exc_info.value)
100
+
101
+ @patch('weeb_cli.sdk.get_provider')
102
+ def test_search_uses_default_provider(self, mock_get_provider):
103
+ """Test search uses default provider when not specified."""
104
+ mock_provider = Mock()
105
+ mock_provider.search.return_value = []
106
+ mock_get_provider.return_value = mock_provider
107
+
108
+ sdk = WeebSDK(default_provider="hianime")
109
+ sdk.search("test")
110
+
111
+ mock_get_provider.assert_called_with("hianime")
112
+
113
+ @patch('weeb_cli.sdk.get_provider')
114
+ def test_get_details_success(self, mock_get_provider):
115
+ """Test getting anime details."""
116
+ mock_provider = Mock()
117
+ mock_provider.get_details.return_value = AnimeDetails(
118
+ id="1",
119
+ title="Test Anime",
120
+ description="Test description",
121
+ genres=["Action", "Adventure"]
122
+ )
123
+ mock_get_provider.return_value = mock_provider
124
+
125
+ sdk = WeebSDK()
126
+ details = sdk.get_details("1", provider="hianime")
127
+
128
+ assert details is not None
129
+ assert details.title == "Test Anime"
130
+ assert len(details.genres) == 2
131
+
132
+ @patch('weeb_cli.sdk.get_provider')
133
+ def test_get_episodes_success(self, mock_get_provider):
134
+ """Test getting episode list."""
135
+ mock_provider = Mock()
136
+ mock_provider.get_episodes.return_value = [
137
+ Episode(id="e1", number=1, season=1),
138
+ Episode(id="e2", number=2, season=1),
139
+ Episode(id="e3", number=1, season=2)
140
+ ]
141
+ mock_get_provider.return_value = mock_provider
142
+
143
+ sdk = WeebSDK()
144
+ episodes = sdk.get_episodes("anime-id", provider="hianime")
145
+
146
+ assert len(episodes) == 3
147
+
148
+ @patch('weeb_cli.sdk.get_provider')
149
+ def test_get_episodes_filter_by_season(self, mock_get_provider):
150
+ """Test filtering episodes by season."""
151
+ mock_provider = Mock()
152
+ mock_provider.get_episodes.return_value = [
153
+ Episode(id="e1", number=1, season=1),
154
+ Episode(id="e2", number=2, season=1),
155
+ Episode(id="e3", number=1, season=2)
156
+ ]
157
+ mock_get_provider.return_value = mock_provider
158
+
159
+ sdk = WeebSDK()
160
+ episodes = sdk.get_episodes("anime-id", season=1, provider="hianime")
161
+
162
+ assert len(episodes) == 2
163
+ assert all(ep.season == 1 for ep in episodes)
164
+
165
+ @patch('weeb_cli.sdk.get_provider')
166
+ def test_get_streams_success(self, mock_get_provider):
167
+ """Test getting stream URLs."""
168
+ mock_provider = Mock()
169
+ mock_provider.get_streams.return_value = [
170
+ StreamLink(url="http://stream1.com", quality="1080p"),
171
+ StreamLink(url="http://stream2.com", quality="720p")
172
+ ]
173
+ mock_get_provider.return_value = mock_provider
174
+
175
+ sdk = WeebSDK()
176
+ streams = sdk.get_streams("anime-id", "ep-id", provider="hianime")
177
+
178
+ assert len(streams) == 2
179
+ assert streams[0].quality == "1080p"
180
+
181
+ @patch('weeb_cli.services.headless_downloader.download_episode')
182
+ @patch('weeb_cli.sdk.get_provider')
183
+ def test_download_episode_success(self, mock_get_provider, mock_download):
184
+ """Test downloading an episode."""
185
+ mock_provider = Mock()
186
+ mock_provider.get_episodes.return_value = [
187
+ Episode(id="e1", number=1, season=1)
188
+ ]
189
+ mock_provider.get_streams.return_value = [
190
+ StreamLink(url="http://stream.com", quality="1080p")
191
+ ]
192
+ mock_provider.get_details.return_value = AnimeDetails(
193
+ id="1", title="Test Anime"
194
+ )
195
+ mock_get_provider.return_value = mock_provider
196
+ mock_download.return_value = "/path/to/file.mp4"
197
+
198
+ sdk = WeebSDK()
199
+ path = sdk.download_episode(
200
+ anime_id="1",
201
+ season=1,
202
+ episode=1,
203
+ provider="hianime"
204
+ )
205
+
206
+ assert path == "/path/to/file.mp4"
207
+ mock_download.assert_called_once()
208
+
209
+ @patch('weeb_cli.sdk.get_provider')
210
+ def test_download_episode_not_found(self, mock_get_provider):
211
+ """Test downloading non-existent episode."""
212
+ mock_provider = Mock()
213
+ mock_provider.get_episodes.return_value = [
214
+ Episode(id="e1", number=1, season=1)
215
+ ]
216
+ mock_get_provider.return_value = mock_provider
217
+
218
+ sdk = WeebSDK()
219
+
220
+ with pytest.raises(WeebCLIError) as exc_info:
221
+ sdk.download_episode(
222
+ anime_id="1",
223
+ season=2,
224
+ episode=1,
225
+ provider="hianime"
226
+ )
227
+
228
+ assert "not found" in str(exc_info.value)
229
+
230
+ @patch('weeb_cli.sdk.get_provider')
231
+ def test_download_episode_no_streams(self, mock_get_provider):
232
+ """Test downloading when no streams available."""
233
+ mock_provider = Mock()
234
+ mock_provider.get_episodes.return_value = [
235
+ Episode(id="e1", number=1, season=1)
236
+ ]
237
+ mock_provider.get_streams.return_value = []
238
+ mock_get_provider.return_value = mock_provider
239
+
240
+ sdk = WeebSDK()
241
+
242
+ with pytest.raises(WeebCLIError) as exc_info:
243
+ sdk.download_episode(
244
+ anime_id="1",
245
+ season=1,
246
+ episode=1,
247
+ provider="hianime"
248
+ )
249
+
250
+ assert "No streams available" in str(exc_info.value)
251
+
252
+ @patch('weeb_cli.services.headless_downloader.download_episode')
253
+ def test_download_url_success(self, mock_download):
254
+ """Test downloading from direct URL."""
255
+ mock_download.return_value = "/path/to/file.mp4"
256
+
257
+ sdk = WeebSDK()
258
+ path = sdk.download_url(
259
+ stream_url="http://stream.com/video.m3u8",
260
+ title="Test Anime",
261
+ season=1,
262
+ episode=1
263
+ )
264
+
265
+ assert path == "/path/to/file.mp4"
266
+ mock_download.assert_called_once()
267
+
268
+
269
+ class TestConvenienceFunctions:
270
+ """Test suite for convenience functions."""
271
+
272
+ @patch('weeb_cli.sdk._list_providers')
273
+ def test_list_providers_function(self, mock_list):
274
+ """Test list_providers convenience function."""
275
+ mock_list.return_value = [
276
+ {"name": "animecix", "lang": "tr"}
277
+ ]
278
+
279
+ providers = list_providers()
280
+ assert len(providers) == 1
281
+
282
+ @patch('weeb_cli.sdk.list_providers')
283
+ def test_get_provider_info_function(self, mock_list):
284
+ """Test get_provider_info convenience function."""
285
+ mock_list.return_value = [
286
+ {"name": "animecix", "lang": "tr"},
287
+ {"name": "hianime", "lang": "en"}
288
+ ]
289
+
290
+ info = get_provider_info("hianime")
291
+ assert info["name"] == "hianime"
@@ -10,16 +10,35 @@ Features:
10
10
  - Advanced download queue with Aria2 and yt-dlp
11
11
  - Discord Rich Presence integration
12
12
  - Multi-language support (TR, EN, DE, PL)
13
+ - Python SDK for programmatic access
13
14
 
14
15
  Example:
15
- Basic usage::
16
+ CLI usage::
16
17
 
17
18
  $ weeb-cli start
18
19
  $ weeb-cli api search "anime name"
19
20
  $ weeb-cli serve --port 8080
21
+
22
+ SDK usage::
23
+
24
+ from weeb_cli import WeebSDK
25
+
26
+ sdk = WeebSDK(default_provider="hianime")
27
+ results = sdk.search("One Piece")
28
+ episodes = sdk.get_episodes(results[0].id)
20
29
 
21
30
  Attributes:
22
31
  __version__ (str): Current version of Weeb CLI.
23
32
  """
24
33
 
25
- __version__ = "2.14.0"
34
+ __version__ = "2.15.0"
35
+
36
+ # SDK exports for easy import
37
+ from weeb_cli.sdk import WeebSDK, list_providers, get_provider_info
38
+
39
+ __all__ = [
40
+ "__version__",
41
+ "WeebSDK",
42
+ "list_providers",
43
+ "get_provider_info",
44
+ ]
@@ -416,8 +416,9 @@ def play_local_episode(anime, episode):
416
416
 
417
417
  except KeyboardInterrupt:
418
418
  pass
419
- except Exception:
420
- pass
419
+ except Exception as e:
420
+ from weeb_cli.services.logger import debug
421
+ debug(f"[Downloads] Tracker sync display error: {e}")
421
422
 
422
423
  def manage_queue():
423
424
  while True: