weeb-cli 2.10.0__tar.gz → 2.11.1__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.
- {weeb_cli-2.10.0/weeb_cli.egg-info → weeb_cli-2.11.1}/PKG-INFO +3 -45
- {weeb_cli-2.10.0 → weeb_cli-2.11.1}/README.md +2 -44
- {weeb_cli-2.10.0 → weeb_cli-2.11.1}/pyproject.toml +1 -1
- weeb_cli-2.11.1/tests/test_aniworld_provider.py +256 -0
- {weeb_cli-2.10.0 → weeb_cli-2.11.1}/tests/test_providers.py +23 -23
- weeb_cli-2.11.1/weeb_cli/__init__.py +1 -0
- {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/commands/search/watch_flow.py +67 -20
- {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/commands/settings/settings_config.py +1 -1
- weeb_cli-2.11.1/weeb_cli/locales/de.json +385 -0
- {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/locales/en.json +7 -2
- {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/locales/tr.json +10 -7
- {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/main.py +14 -8
- weeb_cli-2.11.1/weeb_cli/providers/__init__.py +15 -0
- weeb_cli-2.11.1/weeb_cli/providers/de/aniworld.py +220 -0
- weeb_cli-2.11.1/weeb_cli/providers/extractors/__init__.py +0 -0
- weeb_cli-2.11.1/weeb_cli/providers/extractors/doodstream.py +12 -0
- weeb_cli-2.11.1/weeb_cli/providers/extractors/filemoon.py +31 -0
- weeb_cli-2.11.1/weeb_cli/providers/extractors/streamtape.py +8 -0
- weeb_cli-2.11.1/weeb_cli/providers/extractors/vidoza.py +7 -0
- weeb_cli-2.11.1/weeb_cli/providers/extractors/voe.py +38 -0
- {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/providers/registry.py +29 -4
- weeb_cli-2.11.1/weeb_cli/providers/tr/__init__.py +0 -0
- weeb_cli-2.11.1/weeb_cli/services/__init__.py +0 -0
- {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/services/database.py +17 -7
- {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/services/downloader.py +108 -46
- weeb_cli-2.11.1/weeb_cli/services/player.py +222 -0
- {weeb_cli-2.10.0 → weeb_cli-2.11.1/weeb_cli.egg-info}/PKG-INFO +3 -45
- {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli.egg-info/SOURCES.txt +17 -6
- weeb_cli-2.10.0/weeb_cli/__init__.py +0 -1
- weeb_cli-2.10.0/weeb_cli/providers/__init__.py +0 -24
- weeb_cli-2.10.0/weeb_cli/services/player.py +0 -69
- {weeb_cli-2.10.0 → weeb_cli-2.11.1}/LICENSE +0 -0
- {weeb_cli-2.10.0 → weeb_cli-2.11.1}/setup.cfg +0 -0
- {weeb_cli-2.10.0 → weeb_cli-2.11.1}/tests/test_anilist_tracker.py +0 -0
- {weeb_cli-2.10.0 → weeb_cli-2.11.1}/tests/test_api.py +0 -0
- {weeb_cli-2.10.0 → weeb_cli-2.11.1}/tests/test_cache.py +0 -0
- {weeb_cli-2.10.0 → weeb_cli-2.11.1}/tests/test_exceptions.py +0 -0
- {weeb_cli-2.10.0 → weeb_cli-2.11.1}/tests/test_kitsu_tracker.py +0 -0
- {weeb_cli-2.10.0 → weeb_cli-2.11.1}/tests/test_mal_tracker.py +0 -0
- {weeb_cli-2.10.0 → weeb_cli-2.11.1}/tests/test_sanitizer.py +0 -0
- {weeb_cli-2.10.0 → weeb_cli-2.11.1}/tests/test_sanitizer_security.py +0 -0
- {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/__main__.py +0 -0
- {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/commands/api.py +0 -0
- {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/commands/downloads.py +0 -0
- {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/commands/library.py +0 -0
- {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/commands/search/__init__.py +0 -0
- {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/commands/search/anime_details.py +0 -0
- {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/commands/search/download_flow.py +0 -0
- {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/commands/search/episode_utils.py +0 -0
- {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/commands/search/search_handlers.py +0 -0
- {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/commands/search/stream_utils.py +0 -0
- {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/commands/search.py +0 -0
- {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/commands/serve.py +0 -0
- {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/commands/settings/__init__.py +0 -0
- {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/commands/settings/settings_backup.py +0 -0
- {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/commands/settings/settings_cache.py +0 -0
- {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/commands/settings/settings_download.py +0 -0
- {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/commands/settings/settings_drives.py +0 -0
- {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/commands/settings/settings_menu.py +0 -0
- {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/commands/settings/settings_shortcuts.py +0 -0
- {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/commands/settings/settings_trackers.py +0 -0
- {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/commands/settings.py +0 -0
- {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/commands/setup.py +0 -0
- {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/commands/watchlist.py +0 -0
- {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/config.py +0 -0
- {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/exceptions.py +0 -0
- {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/i18n.py +0 -0
- {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/providers/base.py +0 -0
- {weeb_cli-2.10.0/weeb_cli/providers/extractors → weeb_cli-2.11.1/weeb_cli/providers/de}/__init__.py +0 -0
- {weeb_cli-2.10.0/weeb_cli/services → weeb_cli-2.11.1/weeb_cli/providers/en}/__init__.py +0 -0
- {weeb_cli-2.10.0/weeb_cli/providers → weeb_cli-2.11.1/weeb_cli/providers/en}/allanime.py +0 -0
- {weeb_cli-2.10.0/weeb_cli/providers → weeb_cli-2.11.1/weeb_cli/providers/en}/hianime.py +0 -0
- {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/providers/extractors/megacloud.py +0 -0
- {weeb_cli-2.10.0/weeb_cli/providers → weeb_cli-2.11.1/weeb_cli/providers/tr}/animecix.py +0 -0
- {weeb_cli-2.10.0/weeb_cli/providers → weeb_cli-2.11.1/weeb_cli/providers/tr}/anizle.py +0 -0
- {weeb_cli-2.10.0/weeb_cli/providers → weeb_cli-2.11.1/weeb_cli/providers/tr}/turkanime.py +0 -0
- {weeb_cli-2.10.0/weeb_cli/providers → weeb_cli-2.11.1/weeb_cli/providers/tr}/weeb.py +0 -0
- {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/services/_base.py +0 -0
- {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/services/_tracker_base.py +0 -0
- {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/services/cache.py +0 -0
- {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/services/dependency_manager.py +0 -0
- {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/services/details.py +0 -0
- {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/services/discord_rpc.py +0 -0
- {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/services/error_handler.py +0 -0
- {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/services/headless_downloader.py +0 -0
- {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/services/local_library.py +0 -0
- {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/services/logger.py +0 -0
- {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/services/notifier.py +0 -0
- {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/services/progress.py +0 -0
- {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/services/scraper.py +0 -0
- {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/services/search.py +0 -0
- {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/services/shortcuts.py +0 -0
- {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/services/stream_validator.py +0 -0
- {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/services/tracker.py +0 -0
- {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/services/updater.py +0 -0
- {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/services/watch.py +0 -0
- {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/templates/anilist_error.html +0 -0
- {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/templates/anilist_success.html +0 -0
- {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/templates/mal_error.html +0 -0
- {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/templates/mal_success.html +0 -0
- {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/ui/__init__.py +0 -0
- {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/ui/header.py +0 -0
- {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/ui/menu.py +0 -0
- {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/ui/prompt.py +0 -0
- {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/utils/__init__.py +0 -0
- {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/utils/sanitizer.py +0 -0
- {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli.egg-info/dependency_links.txt +0 -0
- {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli.egg-info/entry_points.txt +0 -0
- {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli.egg-info/requires.txt +0 -0
- {weeb_cli-2.10.0 → weeb_cli-2.11.1}/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.
|
|
3
|
+
Version: 2.11.1
|
|
4
4
|
Summary: Tarayıcı yok, reklam yok, dikkat dağıtıcı unsur yok. Sadece siz ve eşsiz bir anime izleme deneyimi.
|
|
5
5
|
Author-email: ewgsta <ewgst@proton.me>
|
|
6
6
|
License-Expression: GPL-3.0-only
|
|
@@ -221,6 +221,7 @@ CMD ["weeb-cli", "serve", "--port", "9876", "--watch-dir", "/downloads/watch", "
|
|
|
221
221
|
| Animecix | Turkish |
|
|
222
222
|
| Turkanime | Turkish |
|
|
223
223
|
| Anizle | Turkish |
|
|
224
|
+
| Weeb | Turkish |
|
|
224
225
|
| HiAnime | English |
|
|
225
226
|
| AllAnime | English |
|
|
226
227
|
|
|
@@ -388,50 +389,7 @@ weeb-cli/
|
|
|
388
389
|
|
|
389
390
|
---
|
|
390
391
|
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
### Core Technologies
|
|
394
|
-
- **Python 3.12+** - Main programming language
|
|
395
|
-
- **Typer** - CLI framework with rich terminal support
|
|
396
|
-
- **Rich** - Terminal formatting and styling
|
|
397
|
-
- **Questionary** - Interactive prompts and menus
|
|
398
|
-
- **SQLite** - Local database (WAL mode)
|
|
399
|
-
|
|
400
|
-
### Web & Networking
|
|
401
|
-
- **requests** - HTTP client
|
|
402
|
-
- **curl_cffi** - Advanced HTTP with browser impersonation
|
|
403
|
-
- **BeautifulSoup4** - HTML parsing
|
|
404
|
-
- **lxml** - Fast XML/HTML processing
|
|
405
|
-
|
|
406
|
-
### Media & Download
|
|
407
|
-
- **FFmpeg** - Video processing and conversion
|
|
408
|
-
- **MPV** - High-quality video player
|
|
409
|
-
- **Aria2** - Multi-connection downloader
|
|
410
|
-
- **yt-dlp** - Complex stream downloader (HLS, DASH)
|
|
411
|
-
|
|
412
|
-
### Encryption & Security
|
|
413
|
-
- **pycryptodome** - Encryption/decryption (Turkanime)
|
|
414
|
-
|
|
415
|
-
### Additional Features
|
|
416
|
-
- **pypresence** - Discord Rich Presence
|
|
417
|
-
- **py7zr** - 7z archive handling
|
|
418
|
-
- **winotify** - Windows notifications
|
|
419
|
-
- **pyfiglet** - ASCII art headers
|
|
420
|
-
- **packaging** - Version comparison
|
|
421
|
-
|
|
422
|
-
### Development & Testing
|
|
423
|
-
- **pytest** - Testing framework
|
|
424
|
-
- **pyinstaller** - Executable builder
|
|
425
|
-
- **build** - Python package builder
|
|
426
|
-
|
|
427
|
-
### Architecture Patterns
|
|
428
|
-
- **Provider Pattern** - Pluggable anime sources
|
|
429
|
-
- **Registry Pattern** - Dynamic provider registration
|
|
430
|
-
- **Service Locator** - Lazy-loaded services
|
|
431
|
-
- **Queue Pattern** - Thread-safe download queue
|
|
432
|
-
- **Decorator Pattern** - Caching decorator
|
|
433
|
-
- **Observer Pattern** - Progress tracking
|
|
434
|
-
- **Strategy Pattern** - Multiple download strategies
|
|
392
|
+
[](https://www.star-history.com/?repos=ewgsta%2Fweeb-cli&type=date&legend=top-left)
|
|
435
393
|
|
|
436
394
|
---
|
|
437
395
|
|
|
@@ -178,6 +178,7 @@ CMD ["weeb-cli", "serve", "--port", "9876", "--watch-dir", "/downloads/watch", "
|
|
|
178
178
|
| Animecix | Turkish |
|
|
179
179
|
| Turkanime | Turkish |
|
|
180
180
|
| Anizle | Turkish |
|
|
181
|
+
| Weeb | Turkish |
|
|
181
182
|
| HiAnime | English |
|
|
182
183
|
| AllAnime | English |
|
|
183
184
|
|
|
@@ -345,50 +346,7 @@ weeb-cli/
|
|
|
345
346
|
|
|
346
347
|
---
|
|
347
348
|
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
### Core Technologies
|
|
351
|
-
- **Python 3.12+** - Main programming language
|
|
352
|
-
- **Typer** - CLI framework with rich terminal support
|
|
353
|
-
- **Rich** - Terminal formatting and styling
|
|
354
|
-
- **Questionary** - Interactive prompts and menus
|
|
355
|
-
- **SQLite** - Local database (WAL mode)
|
|
356
|
-
|
|
357
|
-
### Web & Networking
|
|
358
|
-
- **requests** - HTTP client
|
|
359
|
-
- **curl_cffi** - Advanced HTTP with browser impersonation
|
|
360
|
-
- **BeautifulSoup4** - HTML parsing
|
|
361
|
-
- **lxml** - Fast XML/HTML processing
|
|
362
|
-
|
|
363
|
-
### Media & Download
|
|
364
|
-
- **FFmpeg** - Video processing and conversion
|
|
365
|
-
- **MPV** - High-quality video player
|
|
366
|
-
- **Aria2** - Multi-connection downloader
|
|
367
|
-
- **yt-dlp** - Complex stream downloader (HLS, DASH)
|
|
368
|
-
|
|
369
|
-
### Encryption & Security
|
|
370
|
-
- **pycryptodome** - Encryption/decryption (Turkanime)
|
|
371
|
-
|
|
372
|
-
### Additional Features
|
|
373
|
-
- **pypresence** - Discord Rich Presence
|
|
374
|
-
- **py7zr** - 7z archive handling
|
|
375
|
-
- **winotify** - Windows notifications
|
|
376
|
-
- **pyfiglet** - ASCII art headers
|
|
377
|
-
- **packaging** - Version comparison
|
|
378
|
-
|
|
379
|
-
### Development & Testing
|
|
380
|
-
- **pytest** - Testing framework
|
|
381
|
-
- **pyinstaller** - Executable builder
|
|
382
|
-
- **build** - Python package builder
|
|
383
|
-
|
|
384
|
-
### Architecture Patterns
|
|
385
|
-
- **Provider Pattern** - Pluggable anime sources
|
|
386
|
-
- **Registry Pattern** - Dynamic provider registration
|
|
387
|
-
- **Service Locator** - Lazy-loaded services
|
|
388
|
-
- **Queue Pattern** - Thread-safe download queue
|
|
389
|
-
- **Decorator Pattern** - Caching decorator
|
|
390
|
-
- **Observer Pattern** - Progress tracking
|
|
391
|
-
- **Strategy Pattern** - Multiple download strategies
|
|
349
|
+
[](https://www.star-history.com/?repos=ewgsta%2Fweeb-cli&type=date&legend=top-left)
|
|
392
350
|
|
|
393
351
|
---
|
|
394
352
|
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "weeb-cli"
|
|
7
|
-
version = "2.
|
|
7
|
+
version = "2.11.1"
|
|
8
8
|
description = "Tarayıcı yok, reklam yok, dikkat dağıtıcı unsur yok. Sadece siz ve eşsiz bir anime izleme deneyimi."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
authors = [{ name = "ewgsta", email = "ewgst@proton.me" }]
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
"""Tests for Aniworld provider"""
|
|
2
|
+
import pytest
|
|
3
|
+
from unittest.mock import Mock, patch, MagicMock
|
|
4
|
+
from weeb_cli.providers.de.aniworld import AniWorldProvider
|
|
5
|
+
from weeb_cli.providers.base import AnimeResult, Episode, StreamLink
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class TestAniWorldProvider:
|
|
9
|
+
"""Test suite for Aniworld provider"""
|
|
10
|
+
|
|
11
|
+
@pytest.fixture
|
|
12
|
+
def provider(self):
|
|
13
|
+
"""Create provider instance"""
|
|
14
|
+
return AniWorldProvider()
|
|
15
|
+
|
|
16
|
+
def test_provider_initialization(self, provider):
|
|
17
|
+
"""Test provider is initialized correctly"""
|
|
18
|
+
assert provider.name == "aniworld"
|
|
19
|
+
assert provider.lang == "de"
|
|
20
|
+
assert provider.region == "DE"
|
|
21
|
+
assert hasattr(provider, '_html_cache')
|
|
22
|
+
assert isinstance(provider._html_cache, dict)
|
|
23
|
+
|
|
24
|
+
def test_score_hoster_language_priority(self, provider):
|
|
25
|
+
"""Test hoster scoring prioritizes language correctly"""
|
|
26
|
+
# GerDub should score highest
|
|
27
|
+
score_gerdub = provider._score_hoster("GerDub", "720p", "Vidmoly")
|
|
28
|
+
score_gersub = provider._score_hoster("GerSub", "720p", "Vidmoly")
|
|
29
|
+
score_engsub = provider._score_hoster("EngSub", "720p", "Vidmoly")
|
|
30
|
+
|
|
31
|
+
assert score_gerdub > score_gersub > score_engsub
|
|
32
|
+
|
|
33
|
+
def test_score_hoster_quality_priority(self, provider):
|
|
34
|
+
"""Test hoster scoring prioritizes quality correctly"""
|
|
35
|
+
score_1080p = provider._score_hoster("GerDub", "1080p", "Vidmoly")
|
|
36
|
+
score_720p = provider._score_hoster("GerDub", "720p", "Vidmoly")
|
|
37
|
+
score_480p = provider._score_hoster("GerDub", "480p", "Vidmoly")
|
|
38
|
+
|
|
39
|
+
assert score_1080p > score_720p > score_480p
|
|
40
|
+
|
|
41
|
+
def test_score_hoster_preference(self, provider):
|
|
42
|
+
"""Test hoster scoring prioritizes preferred hosters"""
|
|
43
|
+
score_vidmoly = provider._score_hoster("GerDub", "720p", "Vidmoly")
|
|
44
|
+
score_voe = provider._score_hoster("GerDub", "720p", "VOE")
|
|
45
|
+
score_streamtape = provider._score_hoster("GerDub", "720p", "Streamtape")
|
|
46
|
+
|
|
47
|
+
assert score_vidmoly > score_voe > score_streamtape
|
|
48
|
+
|
|
49
|
+
def test_extract_quality_from_html(self, provider):
|
|
50
|
+
"""Test quality extraction from HTML"""
|
|
51
|
+
assert provider._extract_quality("1080p quality") == "1080p"
|
|
52
|
+
assert provider._extract_quality("720p HD") == "720p"
|
|
53
|
+
assert provider._extract_quality("480p stream") == "480p"
|
|
54
|
+
assert provider._extract_quality("HD quality") == "HD"
|
|
55
|
+
assert provider._extract_quality("no quality info") == "N/A"
|
|
56
|
+
|
|
57
|
+
@patch('weeb_cli.providers.de.aniworld.requests.Session.post')
|
|
58
|
+
def test_search_success(self, mock_post, provider):
|
|
59
|
+
"""Test search returns results"""
|
|
60
|
+
mock_response = Mock()
|
|
61
|
+
mock_response.text = '[{"title":"One Piece","link":"/anime/stream/one-piece"}]'
|
|
62
|
+
mock_post.return_value = mock_response
|
|
63
|
+
|
|
64
|
+
results = provider.search("One Piece")
|
|
65
|
+
|
|
66
|
+
assert len(results) == 1
|
|
67
|
+
assert results[0].title == "One Piece"
|
|
68
|
+
assert results[0].id == "one-piece"
|
|
69
|
+
assert results[0].type == "series"
|
|
70
|
+
|
|
71
|
+
@patch('weeb_cli.providers.de.aniworld.requests.Session.post')
|
|
72
|
+
def test_search_filters_non_anime(self, mock_post, provider):
|
|
73
|
+
"""Test search filters out non-anime results"""
|
|
74
|
+
mock_response = Mock()
|
|
75
|
+
mock_response.text = '''[
|
|
76
|
+
{"title":"One Piece","link":"/anime/stream/one-piece"},
|
|
77
|
+
{"title":"Manga","link":"/manga/one-piece"}
|
|
78
|
+
]'''
|
|
79
|
+
mock_post.return_value = mock_response
|
|
80
|
+
|
|
81
|
+
results = provider.search("One Piece")
|
|
82
|
+
|
|
83
|
+
assert len(results) == 1
|
|
84
|
+
assert results[0].id == "one-piece"
|
|
85
|
+
|
|
86
|
+
@patch('weeb_cli.providers.de.aniworld.requests.Session.post')
|
|
87
|
+
def test_search_empty_results(self, mock_post, provider):
|
|
88
|
+
"""Test search with no results"""
|
|
89
|
+
mock_response = Mock()
|
|
90
|
+
mock_response.text = '[]'
|
|
91
|
+
mock_post.return_value = mock_response
|
|
92
|
+
|
|
93
|
+
results = provider.search("NonExistentAnime")
|
|
94
|
+
|
|
95
|
+
assert len(results) == 0
|
|
96
|
+
|
|
97
|
+
@patch('weeb_cli.providers.de.aniworld.requests.Session.get')
|
|
98
|
+
def test_get_episodes_with_cache(self, mock_get, provider):
|
|
99
|
+
"""Test episode fetching uses cache"""
|
|
100
|
+
mock_response = Mock()
|
|
101
|
+
mock_response.text = '''
|
|
102
|
+
<html>
|
|
103
|
+
<a href="/anime/stream/test/staffel-1/episode-1">Episode 1</a>
|
|
104
|
+
<a href="/anime/stream/test/staffel-1/episode-2">Episode 2</a>
|
|
105
|
+
</html>
|
|
106
|
+
'''
|
|
107
|
+
mock_response.raise_for_status = Mock()
|
|
108
|
+
mock_get.return_value = mock_response
|
|
109
|
+
|
|
110
|
+
# First call
|
|
111
|
+
episodes1 = provider.get_episodes("test", season=1)
|
|
112
|
+
# Second call should use cache
|
|
113
|
+
episodes2 = provider.get_episodes("test", season=1)
|
|
114
|
+
|
|
115
|
+
assert len(episodes1) == 2
|
|
116
|
+
assert len(episodes2) == 2
|
|
117
|
+
# Should only call get once due to caching
|
|
118
|
+
assert mock_get.call_count == 1
|
|
119
|
+
|
|
120
|
+
def test_html_cache_functionality(self, provider):
|
|
121
|
+
"""Test HTML caching works correctly"""
|
|
122
|
+
with patch.object(provider, '_get', wraps=provider._get) as mock_get:
|
|
123
|
+
with patch('weeb_cli.providers.de.aniworld.requests.Session.get') as mock_session_get:
|
|
124
|
+
mock_response = Mock()
|
|
125
|
+
mock_response.text = "<html>test</html>"
|
|
126
|
+
mock_response.raise_for_status = Mock()
|
|
127
|
+
mock_session_get.return_value = mock_response
|
|
128
|
+
|
|
129
|
+
url = "https://aniworld.to/test"
|
|
130
|
+
|
|
131
|
+
# First call with cache
|
|
132
|
+
result1 = provider._get(url, use_cache=True)
|
|
133
|
+
# Second call should use cache
|
|
134
|
+
result2 = provider._get(url, use_cache=True)
|
|
135
|
+
|
|
136
|
+
assert result1 == result2
|
|
137
|
+
assert mock_session_get.call_count == 1
|
|
138
|
+
|
|
139
|
+
@patch('weeb_cli.providers.de.aniworld.requests.Session.get')
|
|
140
|
+
def test_extract_video_from_embed_vidmoly(self, mock_get, provider):
|
|
141
|
+
"""Test Vidmoly video extraction"""
|
|
142
|
+
mock_response = Mock()
|
|
143
|
+
mock_response.text = '''
|
|
144
|
+
<script>
|
|
145
|
+
sources: "https://example.com/video.m3u8"
|
|
146
|
+
</script>
|
|
147
|
+
'''
|
|
148
|
+
mock_response.raise_for_status = Mock()
|
|
149
|
+
mock_get.return_value = mock_response
|
|
150
|
+
|
|
151
|
+
url = provider._extract_video_from_embed("https://vidmoly.to/embed/test", "Vidmoly")
|
|
152
|
+
|
|
153
|
+
assert url is not None
|
|
154
|
+
assert ".m3u8" in url
|
|
155
|
+
|
|
156
|
+
def test_extract_video_from_embed_unknown_hoster(self, provider):
|
|
157
|
+
"""Test extraction returns None for unknown hoster"""
|
|
158
|
+
url = provider._extract_video_from_embed("https://unknown.com/embed", "UnknownHoster")
|
|
159
|
+
assert url is None
|
|
160
|
+
|
|
161
|
+
@patch('weeb_cli.providers.de.aniworld.requests.Session.get')
|
|
162
|
+
def test_get_streams_with_fallback(self, mock_get, provider):
|
|
163
|
+
"""Test stream extraction tries multiple hosters"""
|
|
164
|
+
# Mock episode page with multiple hosters
|
|
165
|
+
episode_html = '''
|
|
166
|
+
<html>
|
|
167
|
+
<li data-link-target="/redirect/123" data-lang-key="1">
|
|
168
|
+
<i class="icon Vidmoly"></i>
|
|
169
|
+
<h4>Vidmoly</h4>
|
|
170
|
+
</li>
|
|
171
|
+
<li data-link-target="/redirect/456" data-lang-key="1">
|
|
172
|
+
<i class="icon VOE"></i>
|
|
173
|
+
<h4>VOE</h4>
|
|
174
|
+
</li>
|
|
175
|
+
</html>
|
|
176
|
+
'''
|
|
177
|
+
|
|
178
|
+
# Mock redirect responses
|
|
179
|
+
def get_side_effect(url, *args, **kwargs):
|
|
180
|
+
response = Mock()
|
|
181
|
+
response.raise_for_status = Mock()
|
|
182
|
+
|
|
183
|
+
if "episode" in url:
|
|
184
|
+
response.text = episode_html
|
|
185
|
+
elif "redirect/123" in url:
|
|
186
|
+
response.url = "https://vidmoly.to/embed/test"
|
|
187
|
+
response.text = 'sources: "https://example.com/video1.m3u8"'
|
|
188
|
+
elif "redirect/456" in url:
|
|
189
|
+
response.url = "https://voe.sx/embed/test"
|
|
190
|
+
response.text = 'sources: "https://example.com/video2.m3u8"'
|
|
191
|
+
else:
|
|
192
|
+
response.text = ""
|
|
193
|
+
|
|
194
|
+
return response
|
|
195
|
+
|
|
196
|
+
mock_get.side_effect = get_side_effect
|
|
197
|
+
|
|
198
|
+
streams = provider.get_streams("test", "test/staffel-1/episode-1")
|
|
199
|
+
|
|
200
|
+
# Should find streams from both hosters
|
|
201
|
+
assert len(streams) >= 1
|
|
202
|
+
assert any("GerDub" in s.quality for s in streams)
|
|
203
|
+
|
|
204
|
+
@patch('weeb_cli.providers.de.aniworld.requests.Session.get')
|
|
205
|
+
def test_get_details_extracts_title_correctly(self, mock_get, provider):
|
|
206
|
+
"""Test anime details extraction with proper title parsing"""
|
|
207
|
+
mock_response = Mock()
|
|
208
|
+
mock_response.text = '''
|
|
209
|
+
<html>
|
|
210
|
+
<h1 itemprop="name"><span>One Piece</span></h1>
|
|
211
|
+
<a href="/anime/stream/one-piece/staffel-1/episode-1">Episode 1</a>
|
|
212
|
+
</html>
|
|
213
|
+
'''
|
|
214
|
+
mock_response.raise_for_status = Mock()
|
|
215
|
+
mock_get.return_value = mock_response
|
|
216
|
+
|
|
217
|
+
details = provider.get_details("one-piece")
|
|
218
|
+
|
|
219
|
+
assert details is not None
|
|
220
|
+
assert details.title == "One Piece"
|
|
221
|
+
assert details.id == "one-piece"
|
|
222
|
+
|
|
223
|
+
def test_provider_session_has_correct_headers(self, provider):
|
|
224
|
+
"""Test provider session is configured with correct headers"""
|
|
225
|
+
assert "User-Agent" in provider.headers
|
|
226
|
+
assert "Referer" in provider.headers
|
|
227
|
+
assert "aniworld.to" in provider.headers["Referer"]
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
class TestAniWorldProviderIntegration:
|
|
231
|
+
"""Integration tests for Aniworld provider (requires network)"""
|
|
232
|
+
|
|
233
|
+
@pytest.fixture
|
|
234
|
+
def provider(self):
|
|
235
|
+
return AniWorldProvider()
|
|
236
|
+
|
|
237
|
+
@pytest.mark.skip(reason="Network test - run manually")
|
|
238
|
+
def test_real_search(self, provider):
|
|
239
|
+
"""Test real search (manual test)"""
|
|
240
|
+
results = provider.search("One Piece")
|
|
241
|
+
assert len(results) > 0
|
|
242
|
+
assert any("One Piece" in r.title for r in results)
|
|
243
|
+
|
|
244
|
+
@pytest.mark.skip(reason="Network test - run manually")
|
|
245
|
+
def test_real_episodes(self, provider):
|
|
246
|
+
"""Test real episode fetching (manual test)"""
|
|
247
|
+
episodes = provider.get_episodes("one-piece", season=1)
|
|
248
|
+
assert len(episodes) > 0
|
|
249
|
+
assert all(isinstance(e, Episode) for e in episodes)
|
|
250
|
+
|
|
251
|
+
@pytest.mark.skip(reason="Network test - run manually")
|
|
252
|
+
def test_real_streams(self, provider):
|
|
253
|
+
"""Test real stream extraction (manual test)"""
|
|
254
|
+
streams = provider.get_streams("one-piece", "one-piece/staffel-1/episode-1")
|
|
255
|
+
assert len(streams) > 0
|
|
256
|
+
assert all(isinstance(s, StreamLink) for s in streams)
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import pytest
|
|
2
2
|
from unittest.mock import Mock, patch
|
|
3
|
-
from weeb_cli.providers.animecix import AnimeCixProvider
|
|
4
|
-
from weeb_cli.providers.turkanime import TurkAnimeProvider
|
|
5
|
-
from weeb_cli.providers.anizle import AnizleProvider
|
|
6
|
-
from weeb_cli.providers.hianime import HiAnimeProvider
|
|
7
|
-
from weeb_cli.providers.allanime import AllAnimeProvider
|
|
8
|
-
from weeb_cli.providers.weeb import WeebProvider
|
|
3
|
+
from weeb_cli.providers.tr.animecix import AnimeCixProvider
|
|
4
|
+
from weeb_cli.providers.tr.turkanime import TurkAnimeProvider
|
|
5
|
+
from weeb_cli.providers.tr.anizle import AnizleProvider
|
|
6
|
+
from weeb_cli.providers.en.hianime import HiAnimeProvider
|
|
7
|
+
from weeb_cli.providers.en.allanime import AllAnimeProvider
|
|
8
|
+
from weeb_cli.providers.tr.weeb import WeebProvider
|
|
9
9
|
from weeb_cli.providers.base import AnimeResult, AnimeDetails, Episode, StreamLink
|
|
10
10
|
import json
|
|
11
11
|
|
|
@@ -17,7 +17,7 @@ class TestAnimeCixProvider:
|
|
|
17
17
|
return AnimeCixProvider()
|
|
18
18
|
|
|
19
19
|
def test_search_angel_beats(self, provider):
|
|
20
|
-
with patch('weeb_cli.providers.animecix._get_json') as mock_get:
|
|
20
|
+
with patch('weeb_cli.providers.tr.animecix._get_json') as mock_get:
|
|
21
21
|
mock_get.return_value = {
|
|
22
22
|
"results": [
|
|
23
23
|
{
|
|
@@ -35,7 +35,7 @@ class TestAnimeCixProvider:
|
|
|
35
35
|
assert "angel beats" in results[0].title.lower()
|
|
36
36
|
|
|
37
37
|
def test_get_details_angel_beats(self, provider):
|
|
38
|
-
with patch('weeb_cli.providers.animecix._get_json') as mock_get:
|
|
38
|
+
with patch('weeb_cli.providers.tr.animecix._get_json') as mock_get:
|
|
39
39
|
mock_get.return_value = {
|
|
40
40
|
"videos": [
|
|
41
41
|
{
|
|
@@ -57,7 +57,7 @@ class TestAnimeCixProvider:
|
|
|
57
57
|
assert details.title == "Angel Beats!"
|
|
58
58
|
|
|
59
59
|
def test_get_episodes_angel_beats(self, provider):
|
|
60
|
-
with patch('weeb_cli.providers.animecix._get_json') as mock_get:
|
|
60
|
+
with patch('weeb_cli.providers.tr.animecix._get_json') as mock_get:
|
|
61
61
|
mock_get.side_effect = [
|
|
62
62
|
{"videos": [{"title": {"seasons": [1]}}]},
|
|
63
63
|
{
|
|
@@ -82,7 +82,7 @@ class TestTurkAnimeProvider:
|
|
|
82
82
|
return TurkAnimeProvider()
|
|
83
83
|
|
|
84
84
|
def test_search_angel_beats(self, provider):
|
|
85
|
-
with patch('weeb_cli.providers.turkanime._fetch') as mock_fetch:
|
|
85
|
+
with patch('weeb_cli.providers.tr.turkanime._fetch') as mock_fetch:
|
|
86
86
|
mock_fetch.return_value = '''
|
|
87
87
|
<a href="/anime/angel-beats"><div class="animeAdi">Angel Beats!</div></a>
|
|
88
88
|
'''
|
|
@@ -94,7 +94,7 @@ class TestTurkAnimeProvider:
|
|
|
94
94
|
assert "angel beats" in results[0].title.lower()
|
|
95
95
|
|
|
96
96
|
def test_get_details_angel_beats(self, provider):
|
|
97
|
-
with patch('weeb_cli.providers.turkanime._fetch') as mock_fetch:
|
|
97
|
+
with patch('weeb_cli.providers.tr.turkanime._fetch') as mock_fetch:
|
|
98
98
|
mock_fetch.return_value = '''
|
|
99
99
|
<title>Angel Beats!</title>
|
|
100
100
|
<meta property="twitter:image" content="test.jpg">
|
|
@@ -113,7 +113,7 @@ class TestTurkAnimeProvider:
|
|
|
113
113
|
assert "angel beats" in details.title.lower()
|
|
114
114
|
|
|
115
115
|
def test_get_episodes_angel_beats(self, provider):
|
|
116
|
-
with patch('weeb_cli.providers.turkanime._fetch') as mock_fetch:
|
|
116
|
+
with patch('weeb_cli.providers.tr.turkanime._fetch') as mock_fetch:
|
|
117
117
|
mock_fetch.side_effect = [
|
|
118
118
|
'<img src="/uploads/serilerb/12345.jpg">',
|
|
119
119
|
r'<a href=\"/video/angel-beats-1-bolum\" title=\"1. Bölüm\">'
|
|
@@ -133,7 +133,7 @@ class TestAnizleProvider:
|
|
|
133
133
|
return AnizleProvider()
|
|
134
134
|
|
|
135
135
|
def test_search_angel_beats(self, provider):
|
|
136
|
-
with patch('weeb_cli.providers.anizle._load_database') as mock_db:
|
|
136
|
+
with patch('weeb_cli.providers.tr.anizle._load_database') as mock_db:
|
|
137
137
|
mock_db.return_value = [
|
|
138
138
|
{
|
|
139
139
|
"info_slug": "angel-beats",
|
|
@@ -152,8 +152,8 @@ class TestAnizleProvider:
|
|
|
152
152
|
assert "angel beats" in results[0].title.lower()
|
|
153
153
|
|
|
154
154
|
def test_get_details_angel_beats(self, provider):
|
|
155
|
-
with patch('weeb_cli.providers.anizle._load_database') as mock_db, \
|
|
156
|
-
patch('weeb_cli.providers.anizle._http_get') as mock_http:
|
|
155
|
+
with patch('weeb_cli.providers.tr.anizle._load_database') as mock_db, \
|
|
156
|
+
patch('weeb_cli.providers.tr.anizle._http_get') as mock_http:
|
|
157
157
|
|
|
158
158
|
mock_db.return_value = [
|
|
159
159
|
{
|
|
@@ -178,7 +178,7 @@ class TestAnizleProvider:
|
|
|
178
178
|
assert details.title == "Angel Beats!"
|
|
179
179
|
|
|
180
180
|
def test_get_episodes_angel_beats(self, provider):
|
|
181
|
-
with patch('weeb_cli.providers.anizle._http_get') as mock_http:
|
|
181
|
+
with patch('weeb_cli.providers.tr.anizle._http_get') as mock_http:
|
|
182
182
|
mock_response = Mock()
|
|
183
183
|
mock_response.status_code = 200
|
|
184
184
|
mock_response.text = '''
|
|
@@ -201,7 +201,7 @@ class TestHiAnimeProvider:
|
|
|
201
201
|
return HiAnimeProvider()
|
|
202
202
|
|
|
203
203
|
def test_search_angel_beats(self, provider):
|
|
204
|
-
with patch('weeb_cli.providers.hianime._get_html') as mock_html:
|
|
204
|
+
with patch('weeb_cli.providers.en.hianime._get_html') as mock_html:
|
|
205
205
|
mock_html.return_value = '''
|
|
206
206
|
<div class="flw-item">
|
|
207
207
|
<div class="film-poster">
|
|
@@ -222,8 +222,8 @@ class TestHiAnimeProvider:
|
|
|
222
222
|
assert "angel beats" in results[0].title.lower()
|
|
223
223
|
|
|
224
224
|
def test_get_details_angel_beats(self, provider):
|
|
225
|
-
with patch('weeb_cli.providers.hianime._get_html') as mock_html, \
|
|
226
|
-
patch('weeb_cli.providers.hianime._get_json') as mock_json:
|
|
225
|
+
with patch('weeb_cli.providers.en.hianime._get_html') as mock_html, \
|
|
226
|
+
patch('weeb_cli.providers.en.hianime._get_json') as mock_json:
|
|
227
227
|
|
|
228
228
|
mock_html.return_value = '''
|
|
229
229
|
<div class="anisc-detail">
|
|
@@ -249,7 +249,7 @@ class TestHiAnimeProvider:
|
|
|
249
249
|
assert "angel beats" in details.title.lower()
|
|
250
250
|
|
|
251
251
|
def test_get_episodes_angel_beats(self, provider):
|
|
252
|
-
with patch('weeb_cli.providers.hianime._get_json') as mock_json:
|
|
252
|
+
with patch('weeb_cli.providers.en.hianime._get_json') as mock_json:
|
|
253
253
|
mock_json.return_value = {
|
|
254
254
|
"html": '''
|
|
255
255
|
<a class="ssl-item ep-item" href="/watch/angel-beats-123?ep=1" title="Episode 1"></a>
|
|
@@ -271,7 +271,7 @@ class TestAllAnimeProvider:
|
|
|
271
271
|
return AllAnimeProvider()
|
|
272
272
|
|
|
273
273
|
def test_search_angel_beats(self, provider):
|
|
274
|
-
with patch('weeb_cli.providers.allanime._graphql_request') as mock_gql:
|
|
274
|
+
with patch('weeb_cli.providers.en.allanime._graphql_request') as mock_gql:
|
|
275
275
|
mock_gql.return_value = {
|
|
276
276
|
"data": {
|
|
277
277
|
"shows": {
|
|
@@ -293,7 +293,7 @@ class TestAllAnimeProvider:
|
|
|
293
293
|
assert "angel beats" in results[0].title.lower()
|
|
294
294
|
|
|
295
295
|
def test_get_details_angel_beats(self, provider):
|
|
296
|
-
with patch('weeb_cli.providers.allanime._graphql_request') as mock_gql:
|
|
296
|
+
with patch('weeb_cli.providers.en.allanime._graphql_request') as mock_gql:
|
|
297
297
|
mock_gql.return_value = {
|
|
298
298
|
"data": {
|
|
299
299
|
"show": {
|
|
@@ -315,7 +315,7 @@ class TestAllAnimeProvider:
|
|
|
315
315
|
assert details.title == "Angel Beats!"
|
|
316
316
|
|
|
317
317
|
def test_get_episodes_angel_beats(self, provider):
|
|
318
|
-
with patch('weeb_cli.providers.allanime._graphql_request') as mock_gql:
|
|
318
|
+
with patch('weeb_cli.providers.en.allanime._graphql_request') as mock_gql:
|
|
319
319
|
mock_gql.return_value = {
|
|
320
320
|
"data": {
|
|
321
321
|
"show": {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "2.11.1"
|