weeb-cli 2.9.2__tar.gz → 2.10.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.
- {weeb_cli-2.9.2/weeb_cli.egg-info → weeb_cli-2.10.0}/PKG-INFO +1 -1
- {weeb_cli-2.9.2 → weeb_cli-2.10.0}/pyproject.toml +1 -1
- {weeb_cli-2.9.2 → weeb_cli-2.10.0}/tests/test_providers.py +94 -0
- weeb_cli-2.10.0/weeb_cli/__init__.py +1 -0
- {weeb_cli-2.9.2 → weeb_cli-2.10.0}/weeb_cli/commands/search/download_flow.py +37 -2
- {weeb_cli-2.9.2 → weeb_cli-2.10.0}/weeb_cli/commands/search/watch_flow.py +6 -7
- {weeb_cli-2.9.2 → weeb_cli-2.10.0}/weeb_cli/commands/settings/settings_cache.py +15 -15
- {weeb_cli-2.9.2 → weeb_cli-2.10.0}/weeb_cli/commands/settings/settings_download.py +2 -2
- {weeb_cli-2.9.2 → weeb_cli-2.10.0}/weeb_cli/locales/en.json +21 -1
- {weeb_cli-2.9.2 → weeb_cli-2.10.0}/weeb_cli/locales/tr.json +21 -1
- {weeb_cli-2.9.2 → weeb_cli-2.10.0}/weeb_cli/providers/__init__.py +1 -0
- weeb_cli-2.10.0/weeb_cli/providers/weeb.py +113 -0
- {weeb_cli-2.9.2 → weeb_cli-2.10.0}/weeb_cli/services/updater.py +1 -1
- {weeb_cli-2.9.2 → weeb_cli-2.10.0/weeb_cli.egg-info}/PKG-INFO +1 -1
- {weeb_cli-2.9.2 → weeb_cli-2.10.0}/weeb_cli.egg-info/SOURCES.txt +1 -0
- weeb_cli-2.9.2/weeb_cli/__init__.py +0 -1
- {weeb_cli-2.9.2 → weeb_cli-2.10.0}/LICENSE +0 -0
- {weeb_cli-2.9.2 → weeb_cli-2.10.0}/README.md +0 -0
- {weeb_cli-2.9.2 → weeb_cli-2.10.0}/setup.cfg +0 -0
- {weeb_cli-2.9.2 → weeb_cli-2.10.0}/tests/test_anilist_tracker.py +0 -0
- {weeb_cli-2.9.2 → weeb_cli-2.10.0}/tests/test_api.py +0 -0
- {weeb_cli-2.9.2 → weeb_cli-2.10.0}/tests/test_cache.py +0 -0
- {weeb_cli-2.9.2 → weeb_cli-2.10.0}/tests/test_exceptions.py +0 -0
- {weeb_cli-2.9.2 → weeb_cli-2.10.0}/tests/test_kitsu_tracker.py +0 -0
- {weeb_cli-2.9.2 → weeb_cli-2.10.0}/tests/test_mal_tracker.py +0 -0
- {weeb_cli-2.9.2 → weeb_cli-2.10.0}/tests/test_sanitizer.py +0 -0
- {weeb_cli-2.9.2 → weeb_cli-2.10.0}/tests/test_sanitizer_security.py +0 -0
- {weeb_cli-2.9.2 → weeb_cli-2.10.0}/weeb_cli/__main__.py +0 -0
- {weeb_cli-2.9.2 → weeb_cli-2.10.0}/weeb_cli/commands/api.py +0 -0
- {weeb_cli-2.9.2 → weeb_cli-2.10.0}/weeb_cli/commands/downloads.py +0 -0
- {weeb_cli-2.9.2 → weeb_cli-2.10.0}/weeb_cli/commands/library.py +0 -0
- {weeb_cli-2.9.2 → weeb_cli-2.10.0}/weeb_cli/commands/search/__init__.py +0 -0
- {weeb_cli-2.9.2 → weeb_cli-2.10.0}/weeb_cli/commands/search/anime_details.py +0 -0
- {weeb_cli-2.9.2 → weeb_cli-2.10.0}/weeb_cli/commands/search/episode_utils.py +0 -0
- {weeb_cli-2.9.2 → weeb_cli-2.10.0}/weeb_cli/commands/search/search_handlers.py +0 -0
- {weeb_cli-2.9.2 → weeb_cli-2.10.0}/weeb_cli/commands/search/stream_utils.py +0 -0
- {weeb_cli-2.9.2 → weeb_cli-2.10.0}/weeb_cli/commands/search.py +0 -0
- {weeb_cli-2.9.2 → weeb_cli-2.10.0}/weeb_cli/commands/serve.py +0 -0
- {weeb_cli-2.9.2 → weeb_cli-2.10.0}/weeb_cli/commands/settings/__init__.py +0 -0
- {weeb_cli-2.9.2 → weeb_cli-2.10.0}/weeb_cli/commands/settings/settings_backup.py +0 -0
- {weeb_cli-2.9.2 → weeb_cli-2.10.0}/weeb_cli/commands/settings/settings_config.py +0 -0
- {weeb_cli-2.9.2 → weeb_cli-2.10.0}/weeb_cli/commands/settings/settings_drives.py +0 -0
- {weeb_cli-2.9.2 → weeb_cli-2.10.0}/weeb_cli/commands/settings/settings_menu.py +0 -0
- {weeb_cli-2.9.2 → weeb_cli-2.10.0}/weeb_cli/commands/settings/settings_shortcuts.py +0 -0
- {weeb_cli-2.9.2 → weeb_cli-2.10.0}/weeb_cli/commands/settings/settings_trackers.py +0 -0
- {weeb_cli-2.9.2 → weeb_cli-2.10.0}/weeb_cli/commands/settings.py +0 -0
- {weeb_cli-2.9.2 → weeb_cli-2.10.0}/weeb_cli/commands/setup.py +0 -0
- {weeb_cli-2.9.2 → weeb_cli-2.10.0}/weeb_cli/commands/watchlist.py +0 -0
- {weeb_cli-2.9.2 → weeb_cli-2.10.0}/weeb_cli/config.py +0 -0
- {weeb_cli-2.9.2 → weeb_cli-2.10.0}/weeb_cli/exceptions.py +0 -0
- {weeb_cli-2.9.2 → weeb_cli-2.10.0}/weeb_cli/i18n.py +0 -0
- {weeb_cli-2.9.2 → weeb_cli-2.10.0}/weeb_cli/main.py +0 -0
- {weeb_cli-2.9.2 → weeb_cli-2.10.0}/weeb_cli/providers/allanime.py +0 -0
- {weeb_cli-2.9.2 → weeb_cli-2.10.0}/weeb_cli/providers/animecix.py +0 -0
- {weeb_cli-2.9.2 → weeb_cli-2.10.0}/weeb_cli/providers/anizle.py +0 -0
- {weeb_cli-2.9.2 → weeb_cli-2.10.0}/weeb_cli/providers/base.py +0 -0
- {weeb_cli-2.9.2 → weeb_cli-2.10.0}/weeb_cli/providers/extractors/__init__.py +0 -0
- {weeb_cli-2.9.2 → weeb_cli-2.10.0}/weeb_cli/providers/extractors/megacloud.py +0 -0
- {weeb_cli-2.9.2 → weeb_cli-2.10.0}/weeb_cli/providers/hianime.py +0 -0
- {weeb_cli-2.9.2 → weeb_cli-2.10.0}/weeb_cli/providers/registry.py +0 -0
- {weeb_cli-2.9.2 → weeb_cli-2.10.0}/weeb_cli/providers/turkanime.py +0 -0
- {weeb_cli-2.9.2 → weeb_cli-2.10.0}/weeb_cli/services/__init__.py +0 -0
- {weeb_cli-2.9.2 → weeb_cli-2.10.0}/weeb_cli/services/_base.py +0 -0
- {weeb_cli-2.9.2 → weeb_cli-2.10.0}/weeb_cli/services/_tracker_base.py +0 -0
- {weeb_cli-2.9.2 → weeb_cli-2.10.0}/weeb_cli/services/cache.py +0 -0
- {weeb_cli-2.9.2 → weeb_cli-2.10.0}/weeb_cli/services/database.py +0 -0
- {weeb_cli-2.9.2 → weeb_cli-2.10.0}/weeb_cli/services/dependency_manager.py +0 -0
- {weeb_cli-2.9.2 → weeb_cli-2.10.0}/weeb_cli/services/details.py +0 -0
- {weeb_cli-2.9.2 → weeb_cli-2.10.0}/weeb_cli/services/discord_rpc.py +0 -0
- {weeb_cli-2.9.2 → weeb_cli-2.10.0}/weeb_cli/services/downloader.py +0 -0
- {weeb_cli-2.9.2 → weeb_cli-2.10.0}/weeb_cli/services/error_handler.py +0 -0
- {weeb_cli-2.9.2 → weeb_cli-2.10.0}/weeb_cli/services/headless_downloader.py +0 -0
- {weeb_cli-2.9.2 → weeb_cli-2.10.0}/weeb_cli/services/local_library.py +0 -0
- {weeb_cli-2.9.2 → weeb_cli-2.10.0}/weeb_cli/services/logger.py +0 -0
- {weeb_cli-2.9.2 → weeb_cli-2.10.0}/weeb_cli/services/notifier.py +0 -0
- {weeb_cli-2.9.2 → weeb_cli-2.10.0}/weeb_cli/services/player.py +0 -0
- {weeb_cli-2.9.2 → weeb_cli-2.10.0}/weeb_cli/services/progress.py +0 -0
- {weeb_cli-2.9.2 → weeb_cli-2.10.0}/weeb_cli/services/scraper.py +0 -0
- {weeb_cli-2.9.2 → weeb_cli-2.10.0}/weeb_cli/services/search.py +0 -0
- {weeb_cli-2.9.2 → weeb_cli-2.10.0}/weeb_cli/services/shortcuts.py +0 -0
- {weeb_cli-2.9.2 → weeb_cli-2.10.0}/weeb_cli/services/stream_validator.py +0 -0
- {weeb_cli-2.9.2 → weeb_cli-2.10.0}/weeb_cli/services/tracker.py +0 -0
- {weeb_cli-2.9.2 → weeb_cli-2.10.0}/weeb_cli/services/watch.py +0 -0
- {weeb_cli-2.9.2 → weeb_cli-2.10.0}/weeb_cli/templates/anilist_error.html +0 -0
- {weeb_cli-2.9.2 → weeb_cli-2.10.0}/weeb_cli/templates/anilist_success.html +0 -0
- {weeb_cli-2.9.2 → weeb_cli-2.10.0}/weeb_cli/templates/mal_error.html +0 -0
- {weeb_cli-2.9.2 → weeb_cli-2.10.0}/weeb_cli/templates/mal_success.html +0 -0
- {weeb_cli-2.9.2 → weeb_cli-2.10.0}/weeb_cli/ui/__init__.py +0 -0
- {weeb_cli-2.9.2 → weeb_cli-2.10.0}/weeb_cli/ui/header.py +0 -0
- {weeb_cli-2.9.2 → weeb_cli-2.10.0}/weeb_cli/ui/menu.py +0 -0
- {weeb_cli-2.9.2 → weeb_cli-2.10.0}/weeb_cli/ui/prompt.py +0 -0
- {weeb_cli-2.9.2 → weeb_cli-2.10.0}/weeb_cli/utils/__init__.py +0 -0
- {weeb_cli-2.9.2 → weeb_cli-2.10.0}/weeb_cli/utils/sanitizer.py +0 -0
- {weeb_cli-2.9.2 → weeb_cli-2.10.0}/weeb_cli.egg-info/dependency_links.txt +0 -0
- {weeb_cli-2.9.2 → weeb_cli-2.10.0}/weeb_cli.egg-info/entry_points.txt +0 -0
- {weeb_cli-2.9.2 → weeb_cli-2.10.0}/weeb_cli.egg-info/requires.txt +0 -0
- {weeb_cli-2.9.2 → weeb_cli-2.10.0}/weeb_cli.egg-info/top_level.txt +0 -0
|
@@ -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.10.0"
|
|
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" }]
|
|
@@ -5,7 +5,9 @@ from weeb_cli.providers.turkanime import TurkAnimeProvider
|
|
|
5
5
|
from weeb_cli.providers.anizle import AnizleProvider
|
|
6
6
|
from weeb_cli.providers.hianime import HiAnimeProvider
|
|
7
7
|
from weeb_cli.providers.allanime import AllAnimeProvider
|
|
8
|
+
from weeb_cli.providers.weeb import WeebProvider
|
|
8
9
|
from weeb_cli.providers.base import AnimeResult, AnimeDetails, Episode, StreamLink
|
|
10
|
+
import json
|
|
9
11
|
|
|
10
12
|
|
|
11
13
|
class TestAnimeCixProvider:
|
|
@@ -331,3 +333,95 @@ class TestAllAnimeProvider:
|
|
|
331
333
|
if episodes:
|
|
332
334
|
assert isinstance(episodes[0], Episode)
|
|
333
335
|
assert episodes[0].number == 1
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
class TestWeebProvider:
|
|
339
|
+
|
|
340
|
+
@pytest.fixture
|
|
341
|
+
def provider(self):
|
|
342
|
+
return WeebProvider()
|
|
343
|
+
|
|
344
|
+
def test_search(self, provider):
|
|
345
|
+
with patch.object(WeebProvider, '_request') as mock_request:
|
|
346
|
+
mock_request.return_value = {
|
|
347
|
+
"data": [
|
|
348
|
+
{
|
|
349
|
+
"id": "1",
|
|
350
|
+
"name": "Ubel Blatt",
|
|
351
|
+
"slug": "ubel-blatt",
|
|
352
|
+
"first_image": "https://test.com/image.jpg"
|
|
353
|
+
}
|
|
354
|
+
]
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
results = provider.search("Ubel Blatt")
|
|
358
|
+
|
|
359
|
+
assert len(results) == 1
|
|
360
|
+
assert isinstance(results[0], AnimeResult)
|
|
361
|
+
assert results[0].title == "Ubel Blatt"
|
|
362
|
+
assert results[0].id == "ubel-blatt"
|
|
363
|
+
assert results[0].cover == "https://test.com/image.jpg"
|
|
364
|
+
|
|
365
|
+
def test_get_details(self, provider):
|
|
366
|
+
with patch.object(WeebProvider, '_request') as mock_request:
|
|
367
|
+
mock_request.return_value = {
|
|
368
|
+
"data": {
|
|
369
|
+
"id": "1",
|
|
370
|
+
"name": "Ubel Blatt",
|
|
371
|
+
"slug": "ubel-blatt",
|
|
372
|
+
"description": "Test description",
|
|
373
|
+
"categories": ["Aksiyon", "Macera"],
|
|
374
|
+
"season_number": 1,
|
|
375
|
+
"episodes": [
|
|
376
|
+
{
|
|
377
|
+
"episode_number": 1,
|
|
378
|
+
"sources": [
|
|
379
|
+
{"label": "Primary", "watch_url": "/watch/token1"}
|
|
380
|
+
]
|
|
381
|
+
}
|
|
382
|
+
]
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
details = provider.get_details("ubel-blatt")
|
|
387
|
+
|
|
388
|
+
assert details is not None
|
|
389
|
+
assert isinstance(details, AnimeDetails)
|
|
390
|
+
assert details.title == "Ubel Blatt"
|
|
391
|
+
assert len(details.episodes) == 1
|
|
392
|
+
assert details.episodes[0].number == 1
|
|
393
|
+
|
|
394
|
+
sources = json.loads(details.episodes[0].id)
|
|
395
|
+
assert sources[0]["label"] == "Primary"
|
|
396
|
+
|
|
397
|
+
def test_get_streams_from_json_id(self, provider):
|
|
398
|
+
sources = [{"label": "Primary", "watch_url": "/watch/token1"}]
|
|
399
|
+
episode_id = json.dumps(sources)
|
|
400
|
+
|
|
401
|
+
streams = provider.get_streams("ubel-blatt", episode_id)
|
|
402
|
+
|
|
403
|
+
assert len(streams) == 1
|
|
404
|
+
assert isinstance(streams[0], StreamLink)
|
|
405
|
+
assert streams[0].url == "https://anime-api.ewgsta.workers.dev/watch/token1"
|
|
406
|
+
assert streams[0].server == "Primary"
|
|
407
|
+
|
|
408
|
+
def test_get_streams_fallback(self, provider):
|
|
409
|
+
with patch.object(WeebProvider, '_request') as mock_request:
|
|
410
|
+
mock_request.return_value = {
|
|
411
|
+
"data": {
|
|
412
|
+
"episodes": [
|
|
413
|
+
{
|
|
414
|
+
"episode_number": 1,
|
|
415
|
+
"sources": [
|
|
416
|
+
{"label": "Primary", "watch_url": "/watch/token1"}
|
|
417
|
+
]
|
|
418
|
+
}
|
|
419
|
+
]
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
streams = provider.get_streams("ubel-blatt", "1")
|
|
424
|
+
|
|
425
|
+
assert len(streams) == 1
|
|
426
|
+
assert streams[0].url == "https://anime-api.ewgsta.workers.dev/watch/token1"
|
|
427
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "2.10.0"
|
|
@@ -3,7 +3,7 @@ import questionary
|
|
|
3
3
|
from rich.console import Console
|
|
4
4
|
from weeb_cli.i18n import i18n
|
|
5
5
|
from weeb_cli.services.downloader import queue_manager
|
|
6
|
-
from .episode_utils import get_episodes_safe
|
|
6
|
+
from .episode_utils import get_episodes_safe, group_episodes_by_season
|
|
7
7
|
|
|
8
8
|
console = Console()
|
|
9
9
|
|
|
@@ -14,8 +14,23 @@ def handle_download_flow(slug, details):
|
|
|
14
14
|
time.sleep(1.5)
|
|
15
15
|
return
|
|
16
16
|
|
|
17
|
+
seasons = group_episodes_by_season(episodes)
|
|
18
|
+
season_numbers = sorted(seasons.keys())
|
|
19
|
+
|
|
17
20
|
try:
|
|
18
|
-
|
|
21
|
+
if len(season_numbers) > 1:
|
|
22
|
+
selected_season = _select_season_for_download(seasons, season_numbers)
|
|
23
|
+
if selected_season is None:
|
|
24
|
+
return
|
|
25
|
+
|
|
26
|
+
if selected_season == "all":
|
|
27
|
+
target_episodes = episodes
|
|
28
|
+
else:
|
|
29
|
+
target_episodes = seasons[selected_season]
|
|
30
|
+
else:
|
|
31
|
+
target_episodes = episodes
|
|
32
|
+
|
|
33
|
+
selected_eps = _select_episodes_to_download(target_episodes)
|
|
19
34
|
if not selected_eps:
|
|
20
35
|
return
|
|
21
36
|
|
|
@@ -24,6 +39,26 @@ def handle_download_flow(slug, details):
|
|
|
24
39
|
except KeyboardInterrupt:
|
|
25
40
|
return
|
|
26
41
|
|
|
42
|
+
def _select_season_for_download(seasons, season_numbers):
|
|
43
|
+
season_choices = []
|
|
44
|
+
|
|
45
|
+
all_label = i18n.t("details.all_seasons", "Tüm Sezonlar")
|
|
46
|
+
season_choices.append(questionary.Choice(all_label, value="all"))
|
|
47
|
+
|
|
48
|
+
for s_num in season_numbers:
|
|
49
|
+
ep_count = len(seasons[s_num])
|
|
50
|
+
label = f"{i18n.t('details.season', 'Sezon')} {s_num} ({ep_count} {i18n.t('details.episode', 'Bölüm')})"
|
|
51
|
+
season_choices.append(questionary.Choice(label, value=s_num))
|
|
52
|
+
|
|
53
|
+
selected = questionary.select(
|
|
54
|
+
i18n.t("details.select_season", "Sezon Seçin") + ":",
|
|
55
|
+
choices=season_choices,
|
|
56
|
+
pointer=">",
|
|
57
|
+
use_shortcuts=False
|
|
58
|
+
).ask()
|
|
59
|
+
|
|
60
|
+
return selected
|
|
61
|
+
|
|
27
62
|
def _select_episodes_to_download(episodes):
|
|
28
63
|
opt_all = i18n.t("details.download_options.all")
|
|
29
64
|
opt_manual = i18n.t("details.download_options.manual")
|
|
@@ -166,22 +166,21 @@ def _play_episode(slug, selected_ep, details, season, episodes):
|
|
|
166
166
|
return False
|
|
167
167
|
|
|
168
168
|
from weeb_cli.services.stream_validator import stream_validator
|
|
169
|
-
|
|
170
|
-
console.print(f"[dim]{i18n.t('details.validating_streams'
|
|
169
|
+
|
|
170
|
+
console.print(f"[dim]{i18n.t('details.validating_streams')}...[/dim]")
|
|
171
171
|
valid_streams = []
|
|
172
172
|
for stream in streams_list:
|
|
173
173
|
is_valid, error = stream_validator.validate_url(stream.get("url"), timeout=3)
|
|
174
174
|
if is_valid:
|
|
175
175
|
valid_streams.append(stream)
|
|
176
|
-
|
|
176
|
+
|
|
177
177
|
if not valid_streams:
|
|
178
|
-
console.print(f"[red]{i18n.t('details.no_valid_streams'
|
|
178
|
+
console.print(f"[red]{i18n.t('details.no_valid_streams')}[/red]")
|
|
179
179
|
time.sleep(1.5)
|
|
180
180
|
return False
|
|
181
|
-
|
|
181
|
+
|
|
182
182
|
if len(valid_streams) < len(streams_list):
|
|
183
|
-
console.print(f"[dim]{len(valid_streams)}/{len(streams_list)} {i18n.t('details.streams_valid'
|
|
184
|
-
|
|
183
|
+
console.print(f"[dim]{len(valid_streams)}/{len(streams_list)} {i18n.t('details.streams_valid')}[/dim]")
|
|
185
184
|
streams_list = sort_streams(valid_streams)
|
|
186
185
|
|
|
187
186
|
selected_stream = _select_stream(streams_list)
|
|
@@ -17,24 +17,24 @@ SELECT_STYLE = questionary.Style([
|
|
|
17
17
|
def cache_settings_menu():
|
|
18
18
|
while True:
|
|
19
19
|
console.clear()
|
|
20
|
-
show_header(i18n.t("settings.cache.title"
|
|
20
|
+
show_header(i18n.t("settings.cache.title"))
|
|
21
21
|
|
|
22
22
|
cache = get_cache()
|
|
23
23
|
stats = cache.get_stats()
|
|
24
24
|
|
|
25
|
-
console.print(f"[dim]{i18n.t('settings.cache.memory_entries'
|
|
26
|
-
console.print(f"[dim]{i18n.t('settings.cache.file_entries'
|
|
27
|
-
console.print(f"[dim]{i18n.t('settings.cache.total_size'
|
|
25
|
+
console.print(f"[dim]{i18n.t('settings.cache.memory_entries')}: {stats['memory_entries']}[/dim]")
|
|
26
|
+
console.print(f"[dim]{i18n.t('settings.cache.file_entries')}: {stats['file_entries']}[/dim]")
|
|
27
|
+
console.print(f"[dim]{i18n.t('settings.cache.total_size')}: {stats['total_size_mb']} MB[/dim]\n")
|
|
28
28
|
|
|
29
29
|
choices = [
|
|
30
|
-
i18n.t("settings.cache.clear_all"
|
|
31
|
-
i18n.t("settings.cache.clear_provider"
|
|
32
|
-
i18n.t("settings.cache.cleanup_old"
|
|
30
|
+
i18n.t("settings.cache.clear_all"),
|
|
31
|
+
i18n.t("settings.cache.clear_provider"),
|
|
32
|
+
i18n.t("settings.cache.cleanup_old"),
|
|
33
33
|
]
|
|
34
34
|
|
|
35
35
|
try:
|
|
36
36
|
answer = questionary.select(
|
|
37
|
-
i18n.t("settings.cache.prompt"
|
|
37
|
+
i18n.t("settings.cache.prompt"),
|
|
38
38
|
choices=choices,
|
|
39
39
|
pointer=">",
|
|
40
40
|
use_shortcuts=False,
|
|
@@ -46,24 +46,24 @@ def cache_settings_menu():
|
|
|
46
46
|
if answer is None:
|
|
47
47
|
return
|
|
48
48
|
|
|
49
|
-
if answer == i18n.t("settings.cache.clear_all"
|
|
49
|
+
if answer == i18n.t("settings.cache.clear_all"):
|
|
50
50
|
confirm = questionary.confirm(
|
|
51
|
-
i18n.t("settings.cache.confirm_clear_all"
|
|
51
|
+
i18n.t("settings.cache.confirm_clear_all"),
|
|
52
52
|
default=False
|
|
53
53
|
).ask()
|
|
54
54
|
|
|
55
55
|
if confirm:
|
|
56
56
|
cache.clear()
|
|
57
|
-
console.print(f"[green]{i18n.t('settings.cache.cleared'
|
|
57
|
+
console.print(f"[green]{i18n.t('settings.cache.cleared')}[/green]")
|
|
58
58
|
time.sleep(1)
|
|
59
59
|
|
|
60
|
-
elif answer == i18n.t("settings.cache.clear_provider"
|
|
60
|
+
elif answer == i18n.t("settings.cache.clear_provider"):
|
|
61
61
|
provider = config.get("scraping_source", "None")
|
|
62
62
|
removed = cache.invalidate_provider(provider)
|
|
63
|
-
console.print(f"[green]{i18n.t('settings.cache.provider_cleared'
|
|
63
|
+
console.print(f"[green]{i18n.t('settings.cache.provider_cleared')}: {removed} {i18n.t('common.items')}[/green]")
|
|
64
64
|
time.sleep(1)
|
|
65
65
|
|
|
66
|
-
elif answer == i18n.t("settings.cache.cleanup_old"
|
|
66
|
+
elif answer == i18n.t("settings.cache.cleanup_old"):
|
|
67
67
|
removed = cache.cleanup(max_age=86400)
|
|
68
|
-
console.print(f"[green]{i18n.t('settings.cache.cleaned'
|
|
68
|
+
console.print(f"[green]{i18n.t('settings.cache.cleaned')}: {removed} {i18n.t('common.items')}[/green]")
|
|
69
69
|
time.sleep(1)
|
|
@@ -40,7 +40,7 @@ def download_settings_menu():
|
|
|
40
40
|
show_header(i18n.t("settings.download_settings"))
|
|
41
41
|
|
|
42
42
|
curr_dir = config.get("download_dir")
|
|
43
|
-
console.print(f"[dim]
|
|
43
|
+
console.print(f"[dim]{i18n.t('settings.current_dir', dir=curr_dir)}[/dim]\n", justify="left")
|
|
44
44
|
|
|
45
45
|
curr_concurrent = config.get("max_concurrent_downloads", 3)
|
|
46
46
|
curr_retries = config.get("download_max_retries", 3)
|
|
@@ -110,7 +110,7 @@ def _change_retry_delay(curr_delay):
|
|
|
110
110
|
if 0 <= n <= 300:
|
|
111
111
|
config.set("download_retry_delay", n)
|
|
112
112
|
else:
|
|
113
|
-
console.print("[red]
|
|
113
|
+
console.print(f"[red]{i18n.t('settings.retry_delay_error')}[/red]")
|
|
114
114
|
time.sleep(1.5)
|
|
115
115
|
|
|
116
116
|
def aria2_settings_menu():
|
|
@@ -19,7 +19,8 @@
|
|
|
19
19
|
"updating_pip": "Updating via pip",
|
|
20
20
|
"no_asset": "Download file not found",
|
|
21
21
|
"download_url": "Download URL",
|
|
22
|
-
"manual_update": "Manual update required"
|
|
22
|
+
"manual_update": "Manual update required",
|
|
23
|
+
"pip_command": "pip install --upgrade weeb-cli"
|
|
23
24
|
},
|
|
24
25
|
"menu": {
|
|
25
26
|
"title": "Main Menu",
|
|
@@ -78,6 +79,22 @@
|
|
|
78
79
|
"confirm_remove": "Are you sure you want to remove this drive?",
|
|
79
80
|
"drive_renamed": "Drive renamed.",
|
|
80
81
|
"drive_removed": "Drive removed.",
|
|
82
|
+
"current_dir": "Current: {dir}",
|
|
83
|
+
"retry_delay_error": "Retry delay must be between 0 and 300 seconds.",
|
|
84
|
+
"cache": {
|
|
85
|
+
"title": "Cache Management",
|
|
86
|
+
"memory_entries": "Memory entries",
|
|
87
|
+
"file_entries": "File entries",
|
|
88
|
+
"total_size": "Total size",
|
|
89
|
+
"clear_all": "Clear all cache",
|
|
90
|
+
"clear_provider": "Clear current provider cache",
|
|
91
|
+
"cleanup_old": "Cleanup old cache (>24h)",
|
|
92
|
+
"cleared": "Cache cleared",
|
|
93
|
+
"provider_cleared": "Provider cache cleared",
|
|
94
|
+
"cleaned": "Cleaned",
|
|
95
|
+
"prompt": "Select action",
|
|
96
|
+
"confirm_clear_all": "Clear all cache?"
|
|
97
|
+
},
|
|
81
98
|
"anilist": "AniList",
|
|
82
99
|
"anilist_connected": "Connected: {user}",
|
|
83
100
|
"anilist_not_connected": "Connect your AniList account to sync your watch history.",
|
|
@@ -229,6 +246,9 @@
|
|
|
229
246
|
"removed_from_library": "Removed from library.",
|
|
230
247
|
"mark_watched": "Mark as watched?",
|
|
231
248
|
"marked_watched": "Marked as watched",
|
|
249
|
+
"validating_streams": "Validating streams...",
|
|
250
|
+
"no_valid_streams": "No valid streams found.",
|
|
251
|
+
"streams_valid": "streams valid",
|
|
232
252
|
"sync_to_trackers": "Sync to trackers as well?",
|
|
233
253
|
"action_prompt": "Select Action",
|
|
234
254
|
"download_options": {
|
|
@@ -19,7 +19,8 @@
|
|
|
19
19
|
"updating_pip": "pip ile güncelleniyor",
|
|
20
20
|
"no_asset": "İndirme dosyası bulunamadı",
|
|
21
21
|
"download_url": "İndirme URL'si",
|
|
22
|
-
"manual_update": "Manuel güncelleme gerekli"
|
|
22
|
+
"manual_update": "Manuel güncelleme gerekli",
|
|
23
|
+
"pip_command": "pip install --upgrade weeb-cli"
|
|
23
24
|
},
|
|
24
25
|
"menu": {
|
|
25
26
|
"title": "Ana Menü",
|
|
@@ -78,6 +79,22 @@
|
|
|
78
79
|
"confirm_remove": "Bu diski kaldırmak istediğinize emin misiniz?",
|
|
79
80
|
"drive_renamed": "Disk ismi değiştirildi.",
|
|
80
81
|
"drive_removed": "Disk kaldırıldı.",
|
|
82
|
+
"current_dir": "Mevcut: {dir}",
|
|
83
|
+
"retry_delay_error": "Yeniden deneme aralığı 0 ile 300 saniye arasında olmalıdır.",
|
|
84
|
+
"cache": {
|
|
85
|
+
"title": "Önbellek Yönetimi",
|
|
86
|
+
"memory_entries": "Bellek kayıtları",
|
|
87
|
+
"file_entries": "Dosya kayıtları",
|
|
88
|
+
"total_size": "Toplam boyut",
|
|
89
|
+
"clear_all": "Tüm önbelleği temizle",
|
|
90
|
+
"clear_provider": "Mevcut kaynak önbelleğini temizle",
|
|
91
|
+
"cleanup_old": "Eski önbelleği temizle (>24s)",
|
|
92
|
+
"cleared": "Önbellek temizlendi",
|
|
93
|
+
"provider_cleared": "Kaynak önbelleği temizlendi",
|
|
94
|
+
"cleaned": "Temizlendi",
|
|
95
|
+
"prompt": "İşlem seçin",
|
|
96
|
+
"confirm_clear_all": "Tüm önbellek temizlensin mi?"
|
|
97
|
+
},
|
|
81
98
|
"anilist": "AniList",
|
|
82
99
|
"anilist_connected": "Bağlı: {user}",
|
|
83
100
|
"anilist_not_connected": "AniList hesabınıza bağlanarak izleme geçmişinizi senkronize edebilirsiniz.",
|
|
@@ -229,6 +246,9 @@
|
|
|
229
246
|
"removed_from_library": "Kütüphaneden çıkarıldı.",
|
|
230
247
|
"mark_watched": "İzlendi olarak işaretlensin mi?",
|
|
231
248
|
"marked_watched": "İzlendi olarak işaretlendi",
|
|
249
|
+
"validating_streams": "Yayınlar doğrulanıyor...",
|
|
250
|
+
"no_valid_streams": "Geçerli yayın bulunamadı.",
|
|
251
|
+
"streams_valid": "yayın geçerli",
|
|
232
252
|
"sync_to_trackers": "Tracker'lara da eklensin mi?",
|
|
233
253
|
"action_prompt": "İşlem Seçin",
|
|
234
254
|
"download_options": {
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from typing import List, Optional
|
|
3
|
+
|
|
4
|
+
from weeb_cli.providers.base import (
|
|
5
|
+
AnimeDetails,
|
|
6
|
+
AnimeResult,
|
|
7
|
+
BaseProvider,
|
|
8
|
+
Episode,
|
|
9
|
+
StreamLink,
|
|
10
|
+
)
|
|
11
|
+
from weeb_cli.providers.registry import register_provider
|
|
12
|
+
|
|
13
|
+
BASE_URL = "https://anime-api.ewgsta.workers.dev"
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@register_provider("weeb", lang="tr", region="TR")
|
|
17
|
+
class WeebProvider(BaseProvider):
|
|
18
|
+
def __init__(self):
|
|
19
|
+
super().__init__()
|
|
20
|
+
self.headers.update({"Accept": "application/json"})
|
|
21
|
+
|
|
22
|
+
def search(self, query: str) -> List[AnimeResult]:
|
|
23
|
+
if not query or len(query) < 2:
|
|
24
|
+
return []
|
|
25
|
+
|
|
26
|
+
data = self._request(f"{BASE_URL}/search", params={"q": query})
|
|
27
|
+
if not data or "data" not in data:
|
|
28
|
+
return []
|
|
29
|
+
|
|
30
|
+
results = []
|
|
31
|
+
for item in data["data"]:
|
|
32
|
+
results.append(
|
|
33
|
+
AnimeResult(
|
|
34
|
+
id=item["slug"], title=item["name"], cover=item.get("first_image")
|
|
35
|
+
)
|
|
36
|
+
)
|
|
37
|
+
return results
|
|
38
|
+
|
|
39
|
+
def get_details(self, anime_id: str) -> Optional[AnimeDetails]:
|
|
40
|
+
data = self._request(f"{BASE_URL}/animes/{anime_id}")
|
|
41
|
+
if not data or "data" not in data:
|
|
42
|
+
return None
|
|
43
|
+
|
|
44
|
+
anime_data = data["data"]
|
|
45
|
+
episodes = []
|
|
46
|
+
|
|
47
|
+
for ep in anime_data.get("episodes", []):
|
|
48
|
+
sources_json = json.dumps(ep.get("sources", []))
|
|
49
|
+
ep_num = ep.get("episode_number", 0)
|
|
50
|
+
episodes.append(
|
|
51
|
+
Episode(
|
|
52
|
+
id=sources_json,
|
|
53
|
+
number=ep_num,
|
|
54
|
+
title=f"Bölüm {ep_num}",
|
|
55
|
+
season=anime_data.get("season_number", 1),
|
|
56
|
+
)
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
return AnimeDetails(
|
|
60
|
+
id=anime_id,
|
|
61
|
+
title=anime_data.get("name", anime_id),
|
|
62
|
+
description=anime_data.get("description"),
|
|
63
|
+
cover=anime_data.get("first_image"),
|
|
64
|
+
genres=anime_data.get("categories", []),
|
|
65
|
+
episodes=episodes,
|
|
66
|
+
total_episodes=len(episodes),
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
def get_episodes(self, anime_id: str) -> List[Episode]:
|
|
70
|
+
details = self.get_details(anime_id)
|
|
71
|
+
return details.episodes if details else []
|
|
72
|
+
|
|
73
|
+
def get_streams(self, anime_id: str, episode_id: str) -> List[StreamLink]:
|
|
74
|
+
try:
|
|
75
|
+
sources = json.loads(episode_id)
|
|
76
|
+
if isinstance(sources, list):
|
|
77
|
+
streams = []
|
|
78
|
+
for src in sources:
|
|
79
|
+
watch_url = src.get("watch_url", "")
|
|
80
|
+
if watch_url:
|
|
81
|
+
streams.append(
|
|
82
|
+
StreamLink(
|
|
83
|
+
url=f"{BASE_URL}{watch_url}",
|
|
84
|
+
quality="auto",
|
|
85
|
+
server=src.get("label", "default"),
|
|
86
|
+
)
|
|
87
|
+
)
|
|
88
|
+
if streams:
|
|
89
|
+
return streams
|
|
90
|
+
except (json.JSONDecodeError, ValueError, TypeError):
|
|
91
|
+
pass
|
|
92
|
+
|
|
93
|
+
data = self._request(f"{BASE_URL}/animes/{anime_id}")
|
|
94
|
+
if not data or "data" not in data:
|
|
95
|
+
return []
|
|
96
|
+
|
|
97
|
+
anime_data = data["data"]
|
|
98
|
+
for ep in anime_data.get("episodes", []):
|
|
99
|
+
if str(ep.get("episode_number")) == str(episode_id):
|
|
100
|
+
streams = []
|
|
101
|
+
for src in ep.get("sources", []):
|
|
102
|
+
watch_url = src.get("watch_url", "")
|
|
103
|
+
if watch_url:
|
|
104
|
+
streams.append(
|
|
105
|
+
StreamLink(
|
|
106
|
+
url=f"{BASE_URL}{watch_url}",
|
|
107
|
+
quality="auto",
|
|
108
|
+
server=src.get("label", "default"),
|
|
109
|
+
)
|
|
110
|
+
)
|
|
111
|
+
return streams
|
|
112
|
+
|
|
113
|
+
return []
|
|
@@ -205,4 +205,4 @@ def update_prompt():
|
|
|
205
205
|
console.print(f"[blue]{asset_url}[/blue]")
|
|
206
206
|
else:
|
|
207
207
|
console.print(f"[yellow]{i18n.t('update.manual_update')}[/yellow]")
|
|
208
|
-
console.print("[blue]
|
|
208
|
+
console.print(f"[blue]{i18n.t('update.pip_command')}[/blue]")
|
|
@@ -56,6 +56,7 @@ weeb_cli/providers/base.py
|
|
|
56
56
|
weeb_cli/providers/hianime.py
|
|
57
57
|
weeb_cli/providers/registry.py
|
|
58
58
|
weeb_cli/providers/turkanime.py
|
|
59
|
+
weeb_cli/providers/weeb.py
|
|
59
60
|
weeb_cli/providers/extractors/__init__.py
|
|
60
61
|
weeb_cli/providers/extractors/megacloud.py
|
|
61
62
|
weeb_cli/services/__init__.py
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "2.9.2"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|