weeb-cli 2.6.0__tar.gz → 2.6.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 (63) hide show
  1. {weeb_cli-2.6.0/weeb_cli.egg-info → weeb_cli-2.6.1}/PKG-INFO +1 -1
  2. {weeb_cli-2.6.0 → weeb_cli-2.6.1}/pyproject.toml +1 -1
  3. weeb_cli-2.6.1/weeb_cli/__init__.py +1 -0
  4. {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/providers/allanime.py +47 -4
  5. {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/services/database.py +20 -1
  6. {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/services/downloader.py +73 -2
  7. {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/services/scraper.py +13 -1
  8. {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/services/watch.py +8 -1
  9. {weeb_cli-2.6.0 → weeb_cli-2.6.1/weeb_cli.egg-info}/PKG-INFO +1 -1
  10. weeb_cli-2.6.0/weeb_cli/__init__.py +0 -1
  11. {weeb_cli-2.6.0 → weeb_cli-2.6.1}/LICENSE +0 -0
  12. {weeb_cli-2.6.0 → weeb_cli-2.6.1}/README.md +0 -0
  13. {weeb_cli-2.6.0 → weeb_cli-2.6.1}/setup.cfg +0 -0
  14. {weeb_cli-2.6.0 → weeb_cli-2.6.1}/tests/test_cache.py +0 -0
  15. {weeb_cli-2.6.0 → weeb_cli-2.6.1}/tests/test_exceptions.py +0 -0
  16. {weeb_cli-2.6.0 → weeb_cli-2.6.1}/tests/test_sanitizer.py +0 -0
  17. {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/__main__.py +0 -0
  18. {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/commands/downloads.py +0 -0
  19. {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/commands/search.py +0 -0
  20. {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/commands/settings.py +0 -0
  21. {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/commands/setup.py +0 -0
  22. {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/commands/watchlist.py +0 -0
  23. {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/config.py +0 -0
  24. {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/exceptions.py +0 -0
  25. {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/i18n.py +0 -0
  26. {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/locales/en.json +0 -0
  27. {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/locales/tr.json +0 -0
  28. {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/main.py +0 -0
  29. {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/providers/__init__.py +0 -0
  30. {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/providers/animecix.py +0 -0
  31. {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/providers/anizle.py +0 -0
  32. {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/providers/base.py +0 -0
  33. {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/providers/extractors/__init__.py +0 -0
  34. {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/providers/extractors/megacloud.py +0 -0
  35. {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/providers/hianime.py +0 -0
  36. {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/providers/registry.py +0 -0
  37. {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/providers/turkanime.py +0 -0
  38. {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/services/__init__.py +0 -0
  39. {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/services/cache.py +0 -0
  40. {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/services/dependency_manager.py +0 -0
  41. {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/services/details.py +0 -0
  42. {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/services/discord_rpc.py +0 -0
  43. {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/services/error_handler.py +0 -0
  44. {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/services/local_library.py +0 -0
  45. {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/services/logger.py +0 -0
  46. {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/services/notifier.py +0 -0
  47. {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/services/player.py +0 -0
  48. {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/services/progress.py +0 -0
  49. {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/services/search.py +0 -0
  50. {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/services/shortcuts.py +0 -0
  51. {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/services/tracker.py +0 -0
  52. {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/services/updater.py +0 -0
  53. {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/ui/__init__.py +0 -0
  54. {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/ui/header.py +0 -0
  55. {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/ui/menu.py +0 -0
  56. {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/ui/prompt.py +0 -0
  57. {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/utils/__init__.py +0 -0
  58. {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/utils/sanitizer.py +0 -0
  59. {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli.egg-info/SOURCES.txt +0 -0
  60. {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli.egg-info/dependency_links.txt +0 -0
  61. {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli.egg-info/entry_points.txt +0 -0
  62. {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli.egg-info/requires.txt +0 -0
  63. {weeb_cli-2.6.0 → weeb_cli-2.6.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.6.0
3
+ Version: 2.6.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: CC-BY-NC-ND-4.0
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "weeb-cli"
7
- version = "2.6.0"
7
+ version = "2.6.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 @@
1
+ __version__ = "2.6.1"
@@ -130,23 +130,54 @@ class AllAnimeProvider(BaseProvider):
130
130
 
131
131
  results.append(AnimeResult(
132
132
  id=anime_id,
133
- title=f"{name} ({ep_count} episodes)",
133
+ title=name,
134
134
  type="series"
135
135
  ))
136
136
 
137
137
  return results
138
138
 
139
139
  def get_details(self, anime_id: str) -> Optional[AnimeDetails]:
140
- episodes = self.get_episodes(anime_id)
140
+ gql = '''query ($showId: String!) {
141
+ show(_id: $showId) {
142
+ _id
143
+ name
144
+ description
145
+ thumbnail
146
+ availableEpisodesDetail
147
+ }
148
+ }'''
149
+
150
+ variables = {"showId": anime_id}
151
+ data = _graphql_request(gql, variables)
152
+
153
+ if not data or 'data' not in data:
154
+ return None
141
155
 
142
- if not episodes:
156
+ show = data.get('data', {}).get('show', {})
157
+
158
+ if not show:
143
159
  return None
144
160
 
145
- title = anime_id.replace('-', ' ').title()
161
+ title = show.get('name', anime_id.replace('-', ' ').title())
162
+ description = show.get('description')
163
+ thumbnail = show.get('thumbnail')
164
+
165
+ ep_detail = show.get('availableEpisodesDetail', {})
166
+ ep_list = ep_detail.get(self.mode, [])
167
+
168
+ episodes = []
169
+ for i, ep_num in enumerate(sorted(ep_list, key=lambda x: float(x) if x.replace('.', '').isdigit() else 0)):
170
+ episodes.append(Episode(
171
+ id=f"{anime_id}::ep={ep_num}",
172
+ number=i + 1,
173
+ title=f"Episode {ep_num}"
174
+ ))
146
175
 
147
176
  return AnimeDetails(
148
177
  id=anime_id,
149
178
  title=title,
179
+ description=description,
180
+ cover=thumbnail,
150
181
  episodes=episodes,
151
182
  total_episodes=len(episodes)
152
183
  )
@@ -180,6 +211,10 @@ class AllAnimeProvider(BaseProvider):
180
211
  return episodes
181
212
 
182
213
  def get_streams(self, anime_id: str, episode_id: str) -> List[StreamLink]:
214
+ from weeb_cli.services.logger import debug, error
215
+
216
+ debug(f"[ALLANIME] get_streams: anime_id={anime_id}, episode_id={episode_id}")
217
+
183
218
  if '::ep=' in episode_id:
184
219
  parts = episode_id.split('::ep=')
185
220
  show_id = parts[0]
@@ -188,6 +223,8 @@ class AllAnimeProvider(BaseProvider):
188
223
  show_id = anime_id
189
224
  ep_no = episode_id
190
225
 
226
+ debug(f"[ALLANIME] Parsed: show_id={show_id}, ep_no={ep_no}, mode={self.mode}")
227
+
191
228
  gql = '''query ($showId: String!, $translationType: VaildTranslationTypeEnumType!, $episodeString: String!) {
192
229
  episode(showId: $showId translationType: $translationType episodeString: $episodeString) {
193
230
  episodeString
@@ -201,8 +238,14 @@ class AllAnimeProvider(BaseProvider):
201
238
  "episodeString": ep_no
202
239
  }
203
240
 
241
+ debug(f"[ALLANIME] GraphQL variables: {variables}")
242
+
204
243
  data = _graphql_request(gql, variables)
244
+
245
+ debug(f"[ALLANIME] GraphQL response: {data}")
246
+
205
247
  if not data or 'data' not in data:
248
+ error(f"[ALLANIME] No data in response")
206
249
  return []
207
250
 
208
251
  episode = data.get('data', {}).get('episode', {})
@@ -26,6 +26,8 @@ class Database:
26
26
 
27
27
  def _init_db(self):
28
28
  with self._conn() as conn:
29
+ conn.execute('PRAGMA journal_mode=WAL')
30
+
29
31
  conn.executescript('''
30
32
  CREATE TABLE IF NOT EXISTS config (
31
33
  key TEXT PRIMARY KEY,
@@ -58,7 +60,9 @@ class Database:
58
60
  progress INTEGER DEFAULT 0,
59
61
  eta TEXT DEFAULT '?',
60
62
  error TEXT,
61
- added_at REAL
63
+ added_at REAL,
64
+ retry_count INTEGER DEFAULT 0,
65
+ speed TEXT
62
66
  );
63
67
 
64
68
  CREATE TABLE IF NOT EXISTS external_drives (
@@ -83,6 +87,21 @@ class Database:
83
87
  CREATE INDEX IF NOT EXISTS idx_anime_title ON anime_index(title);
84
88
  CREATE INDEX IF NOT EXISTS idx_anime_source ON anime_index(source_path);
85
89
  ''')
90
+
91
+ self._migrate_columns()
92
+
93
+ def _migrate_columns(self):
94
+ """Add missing columns to existing tables."""
95
+ with self._conn() as conn:
96
+ try:
97
+ conn.execute('SELECT retry_count FROM download_queue LIMIT 1')
98
+ except:
99
+ conn.execute('ALTER TABLE download_queue ADD COLUMN retry_count INTEGER DEFAULT 0')
100
+
101
+ try:
102
+ conn.execute('SELECT speed FROM download_queue LIMIT 1')
103
+ except:
104
+ conn.execute('ALTER TABLE download_queue ADD COLUMN speed TEXT')
86
105
 
87
106
  def _migrate_from_json(self):
88
107
  config_dir = Path.home() / ".weeb-cli"
@@ -206,8 +206,6 @@ class QueueManager:
206
206
  filename = f"{safe_title} - S{season}B{ep_num}.mp4"
207
207
  output_path = anime_dir / filename
208
208
 
209
- debug(f"Getting streams for {item['slug']} - {item['episode_id']}")
210
-
211
209
  stream_data = get_streams(item["slug"], item["episode_id"])
212
210
 
213
211
  if not stream_data:
@@ -217,14 +215,87 @@ class QueueManager:
217
215
  log_error(f"Download failed - {err_msg}")
218
216
  raise DownloadError(err_msg, code="NO_STREAM_DATA")
219
217
 
218
+ if isinstance(stream_data, dict) and "data" in stream_data and "links" in stream_data["data"]:
219
+ links = stream_data["data"]["links"]
220
+ if not links:
221
+ raise DownloadError("Stream links boş", code="EMPTY_STREAM_LINKS")
222
+
223
+ debug(f"Found {len(links)} stream sources, trying each one...")
224
+
225
+ last_error = None
226
+ for idx, link in enumerate(links):
227
+ stream_url = link.get("url")
228
+ server_name = link.get("server", "unknown")
229
+
230
+ if not stream_url:
231
+ continue
232
+
233
+ debug(f"Trying source {idx + 1}/{len(links)}: {server_name} - {stream_url[:80]}...")
234
+
235
+ try:
236
+ self._try_download(stream_url, output_path, item)
237
+ debug(f"Download successful with source: {server_name}")
238
+ return
239
+
240
+ except Exception as e:
241
+ last_error = str(e)
242
+ log_error(f"Source {server_name} failed: {e}")
243
+ if idx < len(links) - 1:
244
+ debug(f"Trying next source...")
245
+ continue
246
+
247
+ raise DownloadError(f"Tüm kaynaklar başarısız. Son hata: {last_error}", code="ALL_SOURCES_FAILED")
248
+
220
249
  stream_url = self._extract_url(stream_data)
221
250
 
251
+ debug(f"Extracted stream URL: {stream_url}")
252
+
222
253
  if not stream_url:
223
254
  log_error(f"Download failed - Stream URL bulunamadı. Data: {stream_data}")
224
255
  raise DownloadError("Stream URL bulunamadı", code="NO_STREAM_URL")
225
256
 
226
257
  debug(f"Stream URL found: {stream_url[:80]}...")
227
258
 
259
+ self._try_download(stream_url, output_path, item)
260
+
261
+ def _extract_all_urls(self, data):
262
+ """Extract all available stream URLs with their server names."""
263
+ PRIORITY = ["ALUCARD", "AMATERASU", "SIBNET", "MP4UPLOAD", "UQLOAD"]
264
+
265
+ results = []
266
+
267
+ if isinstance(data, dict):
268
+ node = data
269
+ for _ in range(3):
270
+ if "data" in node and isinstance(node["data"], (dict, list)):
271
+ node = node["data"]
272
+ else:
273
+ break
274
+
275
+ sources = node if isinstance(node, list) else node.get("links") or node.get("sources")
276
+ if sources and isinstance(sources, list) and len(sources) > 0:
277
+ def get_priority(s):
278
+ server = (s.get("server") or "").upper()
279
+ for i, p in enumerate(PRIORITY):
280
+ if p in server:
281
+ return i
282
+ return 999
283
+
284
+ sorted_sources = sorted(sources, key=get_priority)
285
+
286
+ for src in sorted_sources:
287
+ url = src.get("url")
288
+ server = src.get("server", "unknown")
289
+ if url:
290
+ results.append((url, server))
291
+
292
+ elif isinstance(node, dict) and "url" in node:
293
+ results.append((node["url"], node.get("server", "unknown")))
294
+
295
+ return results
296
+
297
+ def _try_download(self, stream_url, output_path, item):
298
+ """Try to download from a stream URL."""
228
299
  is_hls = ".m3u8" in stream_url
229
300
 
230
301
  if is_hls:
@@ -68,16 +68,28 @@ class Scraper:
68
68
  return []
69
69
 
70
70
  def get_streams(self, anime_id: str, episode_id: str) -> List[StreamLink]:
71
+ from weeb_cli.services.logger import debug, error
72
+
71
73
  self.last_error = None
74
+ debug(f"[SCRAPER] get_streams called: anime_id={anime_id}, episode_id={episode_id}")
75
+ debug(f"[SCRAPER] Current provider: {self._provider_name}")
76
+
72
77
  if not self.provider:
78
+ error("[SCRAPER] No provider available!")
73
79
  return []
80
+
74
81
  try:
75
- return self.provider.get_streams(anime_id, episode_id)
82
+ debug(f"[SCRAPER] Calling provider.get_streams()")
83
+ result = self.provider.get_streams(anime_id, episode_id)
84
+ debug(f"[SCRAPER] Provider returned {len(result) if result else 0} streams")
85
+ return result
76
86
  except ProviderError as e:
77
87
  self.last_error = e.code
88
+ error(f"[SCRAPER] ProviderError: {e.code} - {e.message}")
78
89
  return []
79
90
  except Exception as e:
80
91
  self.last_error = str(e)
92
+ error(f"[SCRAPER] Exception: {str(e)}")
81
93
  return []
82
94
 
83
95
  def get_available_sources(self) -> List[dict]:
@@ -1,11 +1,16 @@
1
1
  from weeb_cli.services.scraper import scraper
2
+ from weeb_cli.services.logger import debug
2
3
 
3
4
  def get_streams(anime_id, episode_id):
5
+ debug(f"[WATCH] Getting streams for {anime_id} - {episode_id}")
4
6
  streams = scraper.get_streams(anime_id, episode_id)
7
+ debug(f"[WATCH] Scraper returned {len(streams) if streams else 0} streams")
8
+
5
9
  if not streams:
10
+ debug(f"[WATCH] No streams found, last_error: {scraper.last_error}")
6
11
  return None
7
12
 
8
- return {
13
+ result = {
9
14
  "data": {
10
15
  "links": [
11
16
  {
@@ -17,3 +22,5 @@ def get_streams(anime_id, episode_id):
17
22
  ]
18
23
  }
19
24
  }
25
+ debug(f"[WATCH] Returning {len(result['data']['links'])} links")
26
+ return result
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: weeb-cli
3
- Version: 2.6.0
3
+ Version: 2.6.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: CC-BY-NC-ND-4.0
@@ -1 +0,0 @@
1
- __version__ = "2.6.0"
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