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.
- {weeb_cli-2.6.0/weeb_cli.egg-info → weeb_cli-2.6.1}/PKG-INFO +1 -1
- {weeb_cli-2.6.0 → weeb_cli-2.6.1}/pyproject.toml +1 -1
- weeb_cli-2.6.1/weeb_cli/__init__.py +1 -0
- {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/providers/allanime.py +47 -4
- {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/services/database.py +20 -1
- {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/services/downloader.py +73 -2
- {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/services/scraper.py +13 -1
- {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/services/watch.py +8 -1
- {weeb_cli-2.6.0 → weeb_cli-2.6.1/weeb_cli.egg-info}/PKG-INFO +1 -1
- weeb_cli-2.6.0/weeb_cli/__init__.py +0 -1
- {weeb_cli-2.6.0 → weeb_cli-2.6.1}/LICENSE +0 -0
- {weeb_cli-2.6.0 → weeb_cli-2.6.1}/README.md +0 -0
- {weeb_cli-2.6.0 → weeb_cli-2.6.1}/setup.cfg +0 -0
- {weeb_cli-2.6.0 → weeb_cli-2.6.1}/tests/test_cache.py +0 -0
- {weeb_cli-2.6.0 → weeb_cli-2.6.1}/tests/test_exceptions.py +0 -0
- {weeb_cli-2.6.0 → weeb_cli-2.6.1}/tests/test_sanitizer.py +0 -0
- {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/__main__.py +0 -0
- {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/commands/downloads.py +0 -0
- {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/commands/search.py +0 -0
- {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/commands/settings.py +0 -0
- {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/commands/setup.py +0 -0
- {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/commands/watchlist.py +0 -0
- {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/config.py +0 -0
- {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/exceptions.py +0 -0
- {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/i18n.py +0 -0
- {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/locales/en.json +0 -0
- {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/locales/tr.json +0 -0
- {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/main.py +0 -0
- {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/providers/__init__.py +0 -0
- {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/providers/animecix.py +0 -0
- {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/providers/anizle.py +0 -0
- {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/providers/base.py +0 -0
- {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/providers/extractors/__init__.py +0 -0
- {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/providers/extractors/megacloud.py +0 -0
- {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/providers/hianime.py +0 -0
- {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/providers/registry.py +0 -0
- {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/providers/turkanime.py +0 -0
- {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/services/__init__.py +0 -0
- {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/services/cache.py +0 -0
- {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/services/dependency_manager.py +0 -0
- {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/services/details.py +0 -0
- {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/services/discord_rpc.py +0 -0
- {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/services/error_handler.py +0 -0
- {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/services/local_library.py +0 -0
- {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/services/logger.py +0 -0
- {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/services/notifier.py +0 -0
- {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/services/player.py +0 -0
- {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/services/progress.py +0 -0
- {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/services/search.py +0 -0
- {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/services/shortcuts.py +0 -0
- {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/services/tracker.py +0 -0
- {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/services/updater.py +0 -0
- {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/ui/__init__.py +0 -0
- {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/ui/header.py +0 -0
- {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/ui/menu.py +0 -0
- {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/ui/prompt.py +0 -0
- {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/utils/__init__.py +0 -0
- {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli/utils/sanitizer.py +0 -0
- {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli.egg-info/SOURCES.txt +0 -0
- {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli.egg-info/dependency_links.txt +0 -0
- {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli.egg-info/entry_points.txt +0 -0
- {weeb_cli-2.6.0 → weeb_cli-2.6.1}/weeb_cli.egg-info/requires.txt +0 -0
- {weeb_cli-2.6.0 → weeb_cli-2.6.1}/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.6.
|
|
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=
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 +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
|
|
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
|