warp-beacon 2.7.11__tar.gz → 2.7.13__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.
- {warp_beacon-2.7.11/warp_beacon.egg-info → warp_beacon-2.7.13}/PKG-INFO +1 -1
- warp_beacon-2.7.13/warp_beacon/__version__.py +2 -0
- {warp_beacon-2.7.11 → warp_beacon-2.7.13}/warp_beacon/scraper/instagram/instagram.py +1 -1
- {warp_beacon-2.7.11 → warp_beacon-2.7.13}/warp_beacon/scraper/youtube/abstract.py +30 -2
- warp_beacon-2.7.13/warp_beacon/scraper/youtube/shorts.py +124 -0
- {warp_beacon-2.7.11 → warp_beacon-2.7.13}/warp_beacon/scraper/youtube/youtube.py +0 -28
- {warp_beacon-2.7.11 → warp_beacon-2.7.13}/warp_beacon/telegram/progress_bar.py +1 -1
- {warp_beacon-2.7.11 → warp_beacon-2.7.13/warp_beacon.egg-info}/PKG-INFO +1 -1
- warp_beacon-2.7.11/warp_beacon/__version__.py +0 -2
- warp_beacon-2.7.11/warp_beacon/scraper/youtube/shorts.py +0 -61
- {warp_beacon-2.7.11 → warp_beacon-2.7.13}/LICENSE +0 -0
- {warp_beacon-2.7.11 → warp_beacon-2.7.13}/MANIFEST.in +0 -0
- {warp_beacon-2.7.11 → warp_beacon-2.7.13}/README.md +0 -0
- {warp_beacon-2.7.11 → warp_beacon-2.7.13}/assets/cc-group-black.png +0 -0
- {warp_beacon-2.7.11 → warp_beacon-2.7.13}/assets/placeholder.gif +0 -0
- {warp_beacon-2.7.11 → warp_beacon-2.7.13}/etc/.gitignore +0 -0
- {warp_beacon-2.7.11 → warp_beacon-2.7.13}/etc/accounts.json +0 -0
- {warp_beacon-2.7.11 → warp_beacon-2.7.13}/etc/proxies.json +0 -0
- {warp_beacon-2.7.11 → warp_beacon-2.7.13}/etc/warp_beacon.conf +0 -0
- {warp_beacon-2.7.11 → warp_beacon-2.7.13}/etc/warp_beacon.service +0 -0
- {warp_beacon-2.7.11 → warp_beacon-2.7.13}/pyproject.toml +0 -0
- {warp_beacon-2.7.11 → warp_beacon-2.7.13}/setup.cfg +0 -0
- {warp_beacon-2.7.11 → warp_beacon-2.7.13}/setup.py +0 -0
- {warp_beacon-2.7.11 → warp_beacon-2.7.13}/warp_beacon/__init__.py +0 -0
- {warp_beacon-2.7.11 → warp_beacon-2.7.13}/warp_beacon/compress/__init__.py +0 -0
- {warp_beacon-2.7.11 → warp_beacon-2.7.13}/warp_beacon/compress/video.py +0 -0
- {warp_beacon-2.7.11 → warp_beacon-2.7.13}/warp_beacon/jobs/__init__.py +0 -0
- {warp_beacon-2.7.11 → warp_beacon-2.7.13}/warp_beacon/jobs/abstract.py +0 -0
- {warp_beacon-2.7.11 → warp_beacon-2.7.13}/warp_beacon/jobs/download_job.py +0 -0
- {warp_beacon-2.7.11 → warp_beacon-2.7.13}/warp_beacon/jobs/types.py +0 -0
- {warp_beacon-2.7.11 → warp_beacon-2.7.13}/warp_beacon/jobs/upload_job.py +0 -0
- {warp_beacon-2.7.11 → warp_beacon-2.7.13}/warp_beacon/mediainfo/__init__.py +0 -0
- {warp_beacon-2.7.11 → warp_beacon-2.7.13}/warp_beacon/mediainfo/abstract.py +0 -0
- {warp_beacon-2.7.11 → warp_beacon-2.7.13}/warp_beacon/mediainfo/audio.py +0 -0
- {warp_beacon-2.7.11 → warp_beacon-2.7.13}/warp_beacon/mediainfo/silencer.py +0 -0
- {warp_beacon-2.7.11 → warp_beacon-2.7.13}/warp_beacon/mediainfo/video.py +0 -0
- {warp_beacon-2.7.11 → warp_beacon-2.7.13}/warp_beacon/scheduler/__init__.py +0 -0
- {warp_beacon-2.7.11 → warp_beacon-2.7.13}/warp_beacon/scheduler/instagram_human.py +0 -0
- {warp_beacon-2.7.11 → warp_beacon-2.7.13}/warp_beacon/scheduler/scheduler.py +0 -0
- {warp_beacon-2.7.11 → warp_beacon-2.7.13}/warp_beacon/scraper/__init__.py +0 -0
- {warp_beacon-2.7.11 → warp_beacon-2.7.13}/warp_beacon/scraper/abstract.py +0 -0
- {warp_beacon-2.7.11 → warp_beacon-2.7.13}/warp_beacon/scraper/account_selector.py +0 -0
- {warp_beacon-2.7.11 → warp_beacon-2.7.13}/warp_beacon/scraper/exceptions.py +0 -0
- {warp_beacon-2.7.11 → warp_beacon-2.7.13}/warp_beacon/scraper/fail_handler.py +0 -0
- {warp_beacon-2.7.11 → warp_beacon-2.7.13}/warp_beacon/scraper/instagram/__init__.py +0 -0
- {warp_beacon-2.7.11 → warp_beacon-2.7.13}/warp_beacon/scraper/instagram/captcha.py +0 -0
- {warp_beacon-2.7.11 → warp_beacon-2.7.13}/warp_beacon/scraper/instagram/wb_instagrapi.py +0 -0
- {warp_beacon-2.7.11 → warp_beacon-2.7.13}/warp_beacon/scraper/link_resolver.py +0 -0
- {warp_beacon-2.7.11 → warp_beacon-2.7.13}/warp_beacon/scraper/utils.py +0 -0
- {warp_beacon-2.7.11 → warp_beacon-2.7.13}/warp_beacon/scraper/youtube/__init__.py +0 -0
- {warp_beacon-2.7.11 → warp_beacon-2.7.13}/warp_beacon/scraper/youtube/music.py +0 -0
- {warp_beacon-2.7.11 → warp_beacon-2.7.13}/warp_beacon/storage/__init__.py +0 -0
- {warp_beacon-2.7.11 → warp_beacon-2.7.13}/warp_beacon/storage/mongo.py +0 -0
- {warp_beacon-2.7.11 → warp_beacon-2.7.13}/warp_beacon/telegram/__init__.py +0 -0
- {warp_beacon-2.7.11 → warp_beacon-2.7.13}/warp_beacon/telegram/bot.py +0 -0
- {warp_beacon-2.7.11 → warp_beacon-2.7.13}/warp_beacon/telegram/caption_shortener.py +0 -0
- {warp_beacon-2.7.11 → warp_beacon-2.7.13}/warp_beacon/telegram/download_status.py +0 -0
- {warp_beacon-2.7.11 → warp_beacon-2.7.13}/warp_beacon/telegram/edit_message.py +0 -0
- {warp_beacon-2.7.11 → warp_beacon-2.7.13}/warp_beacon/telegram/handlers.py +0 -0
- {warp_beacon-2.7.11 → warp_beacon-2.7.13}/warp_beacon/telegram/placeholder_message.py +0 -0
- {warp_beacon-2.7.11 → warp_beacon-2.7.13}/warp_beacon/telegram/progress_file_reader.py +0 -0
- {warp_beacon-2.7.11 → warp_beacon-2.7.13}/warp_beacon/telegram/types.py +0 -0
- {warp_beacon-2.7.11 → warp_beacon-2.7.13}/warp_beacon/telegram/utils.py +0 -0
- {warp_beacon-2.7.11 → warp_beacon-2.7.13}/warp_beacon/uploader/__init__.py +0 -0
- {warp_beacon-2.7.11 → warp_beacon-2.7.13}/warp_beacon/warp_beacon.py +0 -0
- {warp_beacon-2.7.11 → warp_beacon-2.7.13}/warp_beacon/yt_auth.py +0 -0
- {warp_beacon-2.7.11 → warp_beacon-2.7.13}/warp_beacon.egg-info/SOURCES.txt +0 -0
- {warp_beacon-2.7.11 → warp_beacon-2.7.13}/warp_beacon.egg-info/dependency_links.txt +0 -0
- {warp_beacon-2.7.11 → warp_beacon-2.7.13}/warp_beacon.egg-info/entry_points.txt +0 -0
- {warp_beacon-2.7.11 → warp_beacon-2.7.13}/warp_beacon.egg-info/requires.txt +0 -0
- {warp_beacon-2.7.11 → warp_beacon-2.7.13}/warp_beacon.egg-info/top_level.txt +0 -0
@@ -475,7 +475,7 @@ class InstagramScraper(ScraperAbstract):
|
|
475
475
|
"report_type": ReportType.PROGRESS
|
476
476
|
}
|
477
477
|
self.status_pipe.send(msg)
|
478
|
-
self._download_progress_threshold +=
|
478
|
+
self._download_progress_threshold += 20
|
479
479
|
|
480
480
|
def report_seen(self, items: dict) -> None:
|
481
481
|
try:
|
@@ -13,6 +13,7 @@ from typing import Callable, Optional, Union
|
|
13
13
|
from urllib.parse import parse_qs, urlparse
|
14
14
|
|
15
15
|
import numpy as np
|
16
|
+
import av
|
16
17
|
import pytubefix
|
17
18
|
import pytubefix.exceptions
|
18
19
|
import requests
|
@@ -245,7 +246,7 @@ class YoutubeAbstract(ScraperAbstract):
|
|
245
246
|
self.status_pipe.send(msg)
|
246
247
|
logging.debug("[Download worker] Downloaded %d%%", percentage_of_completion)
|
247
248
|
if total_size > 0:
|
248
|
-
self._download_progress_threshold +=
|
249
|
+
self._download_progress_threshold += 20
|
249
250
|
|
250
251
|
def build_proxies(self, proxy_dsn: str) -> dict:
|
251
252
|
if not proxy_dsn:
|
@@ -310,7 +311,7 @@ class YoutubeAbstract(ScraperAbstract):
|
|
310
311
|
self.status_pipe.send(msg)
|
311
312
|
logging.debug("[Download worker][yt_dlp] Downloaded %d%%", percentage_of_completion)
|
312
313
|
if total_size > 0:
|
313
|
-
self._download_progress_threshold +=
|
314
|
+
self._download_progress_threshold += 20
|
314
315
|
|
315
316
|
def build_yt_dlp(self, timeout: int = 60) -> yt_dlp.YoutubeDL:
|
316
317
|
auth_data = {}
|
@@ -387,3 +388,30 @@ class YoutubeAbstract(ScraperAbstract):
|
|
387
388
|
raise Unavailable("Сontent unvailable")
|
388
389
|
|
389
390
|
return ret
|
391
|
+
|
392
|
+
def mux_raw_copy(self, video_path: str, audio_path: str, output_path: str) -> str:
|
393
|
+
try:
|
394
|
+
with av.open(video_path) as input_video, av.open(audio_path) as input_audio, av.open(output_path, mode='w') as output:
|
395
|
+
in_video_stream = input_video.streams.video[0]
|
396
|
+
in_audio_stream = input_audio.streams.audio[0]
|
397
|
+
|
398
|
+
video_stream_map = output.add_stream(template=in_video_stream)
|
399
|
+
audio_stream_map = output.add_stream(template=in_audio_stream)
|
400
|
+
|
401
|
+
for packet in input_video.demux(in_video_stream):
|
402
|
+
if packet.dts is None:
|
403
|
+
continue
|
404
|
+
packet.stream = video_stream_map
|
405
|
+
output.mux(packet)
|
406
|
+
|
407
|
+
for packet in input_audio.demux(in_audio_stream):
|
408
|
+
if packet.dts is None:
|
409
|
+
continue
|
410
|
+
packet.stream = audio_stream_map
|
411
|
+
output.mux(packet)
|
412
|
+
except Exception as e:
|
413
|
+
logging.error("Failed to mux audio and video!")
|
414
|
+
logging.exception(e)
|
415
|
+
return ''
|
416
|
+
|
417
|
+
return output_path
|
@@ -0,0 +1,124 @@
|
|
1
|
+
import os
|
2
|
+
import io
|
3
|
+
from typing import Optional
|
4
|
+
import time
|
5
|
+
|
6
|
+
import logging
|
7
|
+
|
8
|
+
from pytubefix.exceptions import AgeRestrictedError
|
9
|
+
|
10
|
+
from warp_beacon.jobs.types import JobType
|
11
|
+
from warp_beacon.scraper.youtube.abstract import YoutubeAbstract
|
12
|
+
|
13
|
+
from warp_beacon.scraper.exceptions import NotFound, YotubeAgeRestrictedError
|
14
|
+
|
15
|
+
class YoutubeShortsScraper(YoutubeAbstract):
|
16
|
+
YT_MAX_RETRIES_DEFAULT = 8
|
17
|
+
YT_PAUSE_BEFORE_RETRY_DEFAULT = 3
|
18
|
+
YT_TIMEOUT_DEFAULT = 2
|
19
|
+
YT_TIMEOUT_INCREMENT_DEFAULT = 60
|
20
|
+
|
21
|
+
def _download(self, url: str, session: bool = True, thumbnail: Optional[io.BytesIO] = None, timeout: int = 60) -> list:
|
22
|
+
res = self._download_pytubefix_max_res(url=url, session=session, thumbnail=thumbnail, timeout=timeout)
|
23
|
+
if not res:
|
24
|
+
res = self._download_pytube_dash(url=url, session=session, thumbnail=thumbnail, timeout=timeout)
|
25
|
+
|
26
|
+
return res
|
27
|
+
|
28
|
+
def _download_pytube_dash(self, url: str, session: bool = True, thumbnail: Optional[io.BytesIO] = None, timeout: int = 0) -> list:
|
29
|
+
res = []
|
30
|
+
yt = self.build_yt(url, session=session)
|
31
|
+
stream = yt.streams.get_highest_resolution()
|
32
|
+
|
33
|
+
if not stream:
|
34
|
+
raise NotFound("No suitable video stream found")
|
35
|
+
|
36
|
+
local_file = stream.download(
|
37
|
+
output_path=self.DOWNLOAD_DIR,
|
38
|
+
max_retries=0,
|
39
|
+
timeout=timeout,
|
40
|
+
skip_existing=False,
|
41
|
+
filename_prefix="yt_download_"
|
42
|
+
)
|
43
|
+
|
44
|
+
local_file = self.rename_local_file(local_file)
|
45
|
+
|
46
|
+
logging.debug("Temp filename: '%s'", local_file)
|
47
|
+
res.append({
|
48
|
+
"local_media_path": local_file,
|
49
|
+
"performer": yt.author,
|
50
|
+
"thumb": thumbnail,
|
51
|
+
"canonical_name": stream.title,
|
52
|
+
"media_type": JobType.VIDEO
|
53
|
+
})
|
54
|
+
|
55
|
+
return res
|
56
|
+
|
57
|
+
def _download_yt_dlp(self, url: str, thumbnail: Optional[io.BytesIO] = None, timeout: int = 60) -> list:
|
58
|
+
res = []
|
59
|
+
with self.build_yt_dlp(timeout) as ydl:
|
60
|
+
info = ydl.extract_info(url, download=True)
|
61
|
+
local_file = ydl.prepare_filename(info)
|
62
|
+
logging.debug("Temp filename: '%s'", local_file)
|
63
|
+
res.append({
|
64
|
+
"local_media_path": local_file,
|
65
|
+
"performer": info.get("uploader", "Unknown"),
|
66
|
+
"thumb": thumbnail,
|
67
|
+
"canonical_name": info.get("title", ''),
|
68
|
+
"media_type": JobType.VIDEO
|
69
|
+
})
|
70
|
+
|
71
|
+
return res
|
72
|
+
|
73
|
+
def _download_pytubefix_max_res(self, url: str, session: bool = True, thumbnail: Optional[io.BytesIO] = None, timeout: int = 60) -> list:
|
74
|
+
res = []
|
75
|
+
local_video_file, local_audio_file = '', ''
|
76
|
+
try:
|
77
|
+
yt = self.build_yt(url, session=session)
|
78
|
+
|
79
|
+
video_stream = yt.streams.filter(adaptive=True, file_extension='mp4', only_video=True).order_by('resolution').desc().first()
|
80
|
+
audio_stream = yt.streams.filter(adaptive=True, file_extension='mp4', only_audio=True).order_by('abr').desc().first()
|
81
|
+
|
82
|
+
local_video_file = video_stream.download(
|
83
|
+
output_path=self.DOWNLOAD_DIR,
|
84
|
+
max_retries=3,
|
85
|
+
timeout=timeout,
|
86
|
+
skip_existing=False,
|
87
|
+
filename_prefix="yt_download_video_"
|
88
|
+
)
|
89
|
+
local_video_file = self.rename_local_file(local_video_file)
|
90
|
+
logging.debug("Temp video filename: '%s'", local_video_file)
|
91
|
+
local_audio_file = audio_stream.download(
|
92
|
+
output_path=self.DOWNLOAD_DIR,
|
93
|
+
max_retries=3,
|
94
|
+
timeout=timeout,
|
95
|
+
skip_existing=False,
|
96
|
+
filename_prefix="yt_download_audio_"
|
97
|
+
)
|
98
|
+
local_audio_file = self.rename_local_file(local_audio_file)
|
99
|
+
logging.debug("Temp audio filename: '%s'", local_audio_file)
|
100
|
+
|
101
|
+
muxed_video = self.mux_raw_copy(
|
102
|
+
video_path=local_video_file,
|
103
|
+
audio_path=local_audio_file,
|
104
|
+
output_path=f"{self.DOWNLOAD_DIR}/yt_muxed_video_{int(time.time())}.mp4")
|
105
|
+
if muxed_video:
|
106
|
+
muxed_video = self.rename_local_file(muxed_video)
|
107
|
+
logging.debug("Temp muxed filename: '%s'", muxed_video)
|
108
|
+
|
109
|
+
res.append({
|
110
|
+
"local_media_path": muxed_video,
|
111
|
+
"performer": yt.author,
|
112
|
+
"thumb": thumbnail,
|
113
|
+
"canonical_name": yt.title,
|
114
|
+
"media_type": JobType.VIDEO
|
115
|
+
})
|
116
|
+
except AgeRestrictedError as e:
|
117
|
+
raise YotubeAgeRestrictedError("Youtube Age Restricted error")
|
118
|
+
finally:
|
119
|
+
if os.path.exists(local_video_file):
|
120
|
+
os.unlink(local_video_file)
|
121
|
+
if os.path.exists(local_audio_file):
|
122
|
+
os.unlink(local_audio_file)
|
123
|
+
|
124
|
+
return res
|
@@ -4,7 +4,6 @@ import io
|
|
4
4
|
from typing import Optional
|
5
5
|
import logging
|
6
6
|
|
7
|
-
import av
|
8
7
|
from pytubefix.exceptions import AgeRestrictedError
|
9
8
|
|
10
9
|
from warp_beacon.jobs.types import JobType
|
@@ -39,33 +38,6 @@ class YoutubeScraper(YoutubeAbstract):
|
|
39
38
|
res = self._download_pytube_dash(url=url, session=session, thumbnail=thumbnail, timeout=timeout)
|
40
39
|
|
41
40
|
return res
|
42
|
-
|
43
|
-
def mux_raw_copy(self, video_path: str, audio_path: str, output_path: str) -> str:
|
44
|
-
try:
|
45
|
-
with av.open(video_path) as input_video, av.open(audio_path) as input_audio, av.open(output_path, mode='w') as output:
|
46
|
-
in_video_stream = input_video.streams.video[0]
|
47
|
-
in_audio_stream = input_audio.streams.audio[0]
|
48
|
-
|
49
|
-
video_stream_map = output.add_stream(template=in_video_stream)
|
50
|
-
audio_stream_map = output.add_stream(template=in_audio_stream)
|
51
|
-
|
52
|
-
for packet in input_video.demux(in_video_stream):
|
53
|
-
if packet.dts is None:
|
54
|
-
continue
|
55
|
-
packet.stream = video_stream_map
|
56
|
-
output.mux(packet)
|
57
|
-
|
58
|
-
for packet in input_audio.demux(in_audio_stream):
|
59
|
-
if packet.dts is None:
|
60
|
-
continue
|
61
|
-
packet.stream = audio_stream_map
|
62
|
-
output.mux(packet)
|
63
|
-
except Exception as e:
|
64
|
-
logging.error("Failed to mux audio and video!")
|
65
|
-
logging.exception(e)
|
66
|
-
return ''
|
67
|
-
|
68
|
-
return output_path
|
69
41
|
|
70
42
|
def _download_pytubefix_max_res(self, url: str, session: bool = True, thumbnail: Optional[io.BytesIO] = None, timeout: int = 60) -> list:
|
71
43
|
res = []
|
@@ -125,7 +125,7 @@ class ProgressBar(object):
|
|
125
125
|
logging.warning("An error occurred while setup task to update progress bar")
|
126
126
|
logging.exception(e)
|
127
127
|
if total > 0 and percent != 0:
|
128
|
-
self._next_threshold +=
|
128
|
+
self._next_threshold += 20
|
129
129
|
|
130
130
|
@staticmethod
|
131
131
|
def make_hash(chat_id: str | int, message_id: int, algorithm: str = 'sha256') -> str:
|
@@ -1,61 +0,0 @@
|
|
1
|
-
import io
|
2
|
-
from typing import Optional
|
3
|
-
|
4
|
-
import logging
|
5
|
-
|
6
|
-
from warp_beacon.jobs.types import JobType
|
7
|
-
from warp_beacon.scraper.youtube.abstract import YoutubeAbstract
|
8
|
-
from warp_beacon.scraper.exceptions import NotFound
|
9
|
-
|
10
|
-
from warp_beacon.mediainfo.video import VideoInfo
|
11
|
-
|
12
|
-
class YoutubeShortsScraper(YoutubeAbstract):
|
13
|
-
YT_MAX_RETRIES_DEFAULT = 8
|
14
|
-
YT_PAUSE_BEFORE_RETRY_DEFAULT = 3
|
15
|
-
YT_TIMEOUT_DEFAULT = 2
|
16
|
-
YT_TIMEOUT_INCREMENT_DEFAULT = 60
|
17
|
-
|
18
|
-
def _download(self, url: str, session: bool = True, thumbnail: Optional[io.BytesIO] = None, timeout: int = 0) -> list:
|
19
|
-
res = []
|
20
|
-
yt = self.build_yt(url, session=session)
|
21
|
-
stream = yt.streams.get_highest_resolution()
|
22
|
-
|
23
|
-
if not stream:
|
24
|
-
raise NotFound("No suitable video stream found")
|
25
|
-
|
26
|
-
local_file = stream.download(
|
27
|
-
output_path=self.DOWNLOAD_DIR,
|
28
|
-
max_retries=0,
|
29
|
-
timeout=timeout,
|
30
|
-
skip_existing=False,
|
31
|
-
filename_prefix="yt_download_"
|
32
|
-
)
|
33
|
-
|
34
|
-
local_file = self.rename_local_file(local_file)
|
35
|
-
|
36
|
-
logging.debug("Temp filename: '%s'", local_file)
|
37
|
-
res.append({
|
38
|
-
"local_media_path": local_file,
|
39
|
-
"performer": yt.author,
|
40
|
-
"thumb": thumbnail,
|
41
|
-
"canonical_name": stream.title,
|
42
|
-
"media_type": JobType.VIDEO
|
43
|
-
})
|
44
|
-
|
45
|
-
return res
|
46
|
-
|
47
|
-
def _download_yt_dlp(self, url: str, thumbnail: Optional[io.BytesIO] = None, timeout: int = 60) -> list:
|
48
|
-
res = []
|
49
|
-
with self.build_yt_dlp(timeout) as ydl:
|
50
|
-
info = ydl.extract_info(url, download=True)
|
51
|
-
local_file = ydl.prepare_filename(info)
|
52
|
-
logging.debug("Temp filename: '%s'", local_file)
|
53
|
-
res.append({
|
54
|
-
"local_media_path": local_file,
|
55
|
-
"performer": info.get("uploader", "Unknown"),
|
56
|
-
"thumb": thumbnail,
|
57
|
-
"canonical_name": info.get("title", ''),
|
58
|
-
"media_type": JobType.VIDEO
|
59
|
-
})
|
60
|
-
|
61
|
-
return res
|
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
|