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.
Files changed (110) hide show
  1. {weeb_cli-2.10.0/weeb_cli.egg-info → weeb_cli-2.11.1}/PKG-INFO +3 -45
  2. {weeb_cli-2.10.0 → weeb_cli-2.11.1}/README.md +2 -44
  3. {weeb_cli-2.10.0 → weeb_cli-2.11.1}/pyproject.toml +1 -1
  4. weeb_cli-2.11.1/tests/test_aniworld_provider.py +256 -0
  5. {weeb_cli-2.10.0 → weeb_cli-2.11.1}/tests/test_providers.py +23 -23
  6. weeb_cli-2.11.1/weeb_cli/__init__.py +1 -0
  7. {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/commands/search/watch_flow.py +67 -20
  8. {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/commands/settings/settings_config.py +1 -1
  9. weeb_cli-2.11.1/weeb_cli/locales/de.json +385 -0
  10. {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/locales/en.json +7 -2
  11. {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/locales/tr.json +10 -7
  12. {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/main.py +14 -8
  13. weeb_cli-2.11.1/weeb_cli/providers/__init__.py +15 -0
  14. weeb_cli-2.11.1/weeb_cli/providers/de/aniworld.py +220 -0
  15. weeb_cli-2.11.1/weeb_cli/providers/extractors/__init__.py +0 -0
  16. weeb_cli-2.11.1/weeb_cli/providers/extractors/doodstream.py +12 -0
  17. weeb_cli-2.11.1/weeb_cli/providers/extractors/filemoon.py +31 -0
  18. weeb_cli-2.11.1/weeb_cli/providers/extractors/streamtape.py +8 -0
  19. weeb_cli-2.11.1/weeb_cli/providers/extractors/vidoza.py +7 -0
  20. weeb_cli-2.11.1/weeb_cli/providers/extractors/voe.py +38 -0
  21. {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/providers/registry.py +29 -4
  22. weeb_cli-2.11.1/weeb_cli/providers/tr/__init__.py +0 -0
  23. weeb_cli-2.11.1/weeb_cli/services/__init__.py +0 -0
  24. {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/services/database.py +17 -7
  25. {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/services/downloader.py +108 -46
  26. weeb_cli-2.11.1/weeb_cli/services/player.py +222 -0
  27. {weeb_cli-2.10.0 → weeb_cli-2.11.1/weeb_cli.egg-info}/PKG-INFO +3 -45
  28. {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli.egg-info/SOURCES.txt +17 -6
  29. weeb_cli-2.10.0/weeb_cli/__init__.py +0 -1
  30. weeb_cli-2.10.0/weeb_cli/providers/__init__.py +0 -24
  31. weeb_cli-2.10.0/weeb_cli/services/player.py +0 -69
  32. {weeb_cli-2.10.0 → weeb_cli-2.11.1}/LICENSE +0 -0
  33. {weeb_cli-2.10.0 → weeb_cli-2.11.1}/setup.cfg +0 -0
  34. {weeb_cli-2.10.0 → weeb_cli-2.11.1}/tests/test_anilist_tracker.py +0 -0
  35. {weeb_cli-2.10.0 → weeb_cli-2.11.1}/tests/test_api.py +0 -0
  36. {weeb_cli-2.10.0 → weeb_cli-2.11.1}/tests/test_cache.py +0 -0
  37. {weeb_cli-2.10.0 → weeb_cli-2.11.1}/tests/test_exceptions.py +0 -0
  38. {weeb_cli-2.10.0 → weeb_cli-2.11.1}/tests/test_kitsu_tracker.py +0 -0
  39. {weeb_cli-2.10.0 → weeb_cli-2.11.1}/tests/test_mal_tracker.py +0 -0
  40. {weeb_cli-2.10.0 → weeb_cli-2.11.1}/tests/test_sanitizer.py +0 -0
  41. {weeb_cli-2.10.0 → weeb_cli-2.11.1}/tests/test_sanitizer_security.py +0 -0
  42. {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/__main__.py +0 -0
  43. {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/commands/api.py +0 -0
  44. {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/commands/downloads.py +0 -0
  45. {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/commands/library.py +0 -0
  46. {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/commands/search/__init__.py +0 -0
  47. {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/commands/search/anime_details.py +0 -0
  48. {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/commands/search/download_flow.py +0 -0
  49. {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/commands/search/episode_utils.py +0 -0
  50. {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/commands/search/search_handlers.py +0 -0
  51. {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/commands/search/stream_utils.py +0 -0
  52. {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/commands/search.py +0 -0
  53. {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/commands/serve.py +0 -0
  54. {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/commands/settings/__init__.py +0 -0
  55. {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/commands/settings/settings_backup.py +0 -0
  56. {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/commands/settings/settings_cache.py +0 -0
  57. {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/commands/settings/settings_download.py +0 -0
  58. {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/commands/settings/settings_drives.py +0 -0
  59. {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/commands/settings/settings_menu.py +0 -0
  60. {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/commands/settings/settings_shortcuts.py +0 -0
  61. {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/commands/settings/settings_trackers.py +0 -0
  62. {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/commands/settings.py +0 -0
  63. {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/commands/setup.py +0 -0
  64. {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/commands/watchlist.py +0 -0
  65. {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/config.py +0 -0
  66. {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/exceptions.py +0 -0
  67. {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/i18n.py +0 -0
  68. {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/providers/base.py +0 -0
  69. {weeb_cli-2.10.0/weeb_cli/providers/extractors → weeb_cli-2.11.1/weeb_cli/providers/de}/__init__.py +0 -0
  70. {weeb_cli-2.10.0/weeb_cli/services → weeb_cli-2.11.1/weeb_cli/providers/en}/__init__.py +0 -0
  71. {weeb_cli-2.10.0/weeb_cli/providers → weeb_cli-2.11.1/weeb_cli/providers/en}/allanime.py +0 -0
  72. {weeb_cli-2.10.0/weeb_cli/providers → weeb_cli-2.11.1/weeb_cli/providers/en}/hianime.py +0 -0
  73. {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/providers/extractors/megacloud.py +0 -0
  74. {weeb_cli-2.10.0/weeb_cli/providers → weeb_cli-2.11.1/weeb_cli/providers/tr}/animecix.py +0 -0
  75. {weeb_cli-2.10.0/weeb_cli/providers → weeb_cli-2.11.1/weeb_cli/providers/tr}/anizle.py +0 -0
  76. {weeb_cli-2.10.0/weeb_cli/providers → weeb_cli-2.11.1/weeb_cli/providers/tr}/turkanime.py +0 -0
  77. {weeb_cli-2.10.0/weeb_cli/providers → weeb_cli-2.11.1/weeb_cli/providers/tr}/weeb.py +0 -0
  78. {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/services/_base.py +0 -0
  79. {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/services/_tracker_base.py +0 -0
  80. {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/services/cache.py +0 -0
  81. {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/services/dependency_manager.py +0 -0
  82. {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/services/details.py +0 -0
  83. {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/services/discord_rpc.py +0 -0
  84. {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/services/error_handler.py +0 -0
  85. {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/services/headless_downloader.py +0 -0
  86. {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/services/local_library.py +0 -0
  87. {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/services/logger.py +0 -0
  88. {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/services/notifier.py +0 -0
  89. {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/services/progress.py +0 -0
  90. {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/services/scraper.py +0 -0
  91. {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/services/search.py +0 -0
  92. {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/services/shortcuts.py +0 -0
  93. {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/services/stream_validator.py +0 -0
  94. {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/services/tracker.py +0 -0
  95. {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/services/updater.py +0 -0
  96. {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/services/watch.py +0 -0
  97. {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/templates/anilist_error.html +0 -0
  98. {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/templates/anilist_success.html +0 -0
  99. {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/templates/mal_error.html +0 -0
  100. {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/templates/mal_success.html +0 -0
  101. {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/ui/__init__.py +0 -0
  102. {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/ui/header.py +0 -0
  103. {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/ui/menu.py +0 -0
  104. {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/ui/prompt.py +0 -0
  105. {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/utils/__init__.py +0 -0
  106. {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli/utils/sanitizer.py +0 -0
  107. {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli.egg-info/dependency_links.txt +0 -0
  108. {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli.egg-info/entry_points.txt +0 -0
  109. {weeb_cli-2.10.0 → weeb_cli-2.11.1}/weeb_cli.egg-info/requires.txt +0 -0
  110. {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.10.0
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
- ## Tech Stack
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
+ [![Star History Chart](https://api.star-history.com/image?repos=ewgsta/weeb-cli&type=date&legend=top-left)](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
- ## Tech Stack
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
+ [![Star History Chart](https://api.star-history.com/image?repos=ewgsta/weeb-cli&type=date&legend=top-left)](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.10.0"
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"