warp-beacon 2.6.51__py3-none-any.whl → 2.6.53__py3-none-any.whl

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.
@@ -1,2 +1,2 @@
1
- __version__ = "2.6.51"
1
+ __version__ = "2.6.53"
2
2
 
@@ -1,28 +1,31 @@
1
+ import logging
2
+ import multiprocessing
3
+ import multiprocessing.connection
1
4
  import os
2
-
3
5
  import time
4
- from typing import Optional
5
- import multiprocessing
6
6
  from multiprocessing.managers import Namespace
7
7
  from queue import Empty
8
+ from typing import Optional
8
9
 
9
- import logging
10
-
11
- from warp_beacon.scraper.exceptions import NotFound, UnknownError, TimeOut, Unavailable, FileTooBig, YoutubeLiveError, \
12
- YotubeAgeRestrictedError, IGRateLimitOccurred, CaptchaIssue, AllAccountsFailed, BadProxy
13
- from warp_beacon.mediainfo.video import VideoInfo
14
- from warp_beacon.mediainfo.audio import AudioInfo
15
- from warp_beacon.mediainfo.silencer import Silencer
16
10
  from warp_beacon.compress.video import VideoCompress
17
- from warp_beacon.uploader import AsyncUploader
18
11
  from warp_beacon.jobs import Origin
19
12
  from warp_beacon.jobs.download_job import DownloadJob
20
- from warp_beacon.jobs.upload_job import UploadJob
21
13
  from warp_beacon.jobs.types import JobType
14
+ from warp_beacon.jobs.upload_job import UploadJob
15
+ from warp_beacon.mediainfo.audio import AudioInfo
16
+ from warp_beacon.mediainfo.silencer import Silencer
17
+ from warp_beacon.mediainfo.video import VideoInfo
22
18
  from warp_beacon.scraper.account_selector import AccountSelector
23
- from warp_beacon.storage.mongo import DBClient
19
+ from warp_beacon.scraper.exceptions import (AllAccountsFailed, BadProxy,
20
+ CaptchaIssue, FileTooBig,
21
+ IGRateLimitOccurred, NotFound,
22
+ TimeOut, Unavailable, UnknownError,
23
+ YotubeAgeRestrictedError,
24
+ YoutubeLiveError)
24
25
  from warp_beacon.scraper.fail_handler import FailHandler
25
26
  from warp_beacon.scraper.link_resolver import LinkResolver
27
+ from warp_beacon.storage.mongo import DBClient
28
+ from warp_beacon.uploader import AsyncUploader
26
29
 
27
30
  ACC_FILE = os.environ.get("SERVICE_ACCOUNTS_FILE", default="/var/warp_beacon/accounts.json")
28
31
  PROXY_FILE = os.environ.get("PROXY_FILE", default="/var/warp_beacon/proxies.json")
@@ -41,8 +44,9 @@ class AsyncDownloader(object):
41
44
  scheduler = None
42
45
  scrolling_now = None
43
46
  process_context = None
47
+ status_pipe = None
44
48
 
45
- def __init__(self, uploader: AsyncUploader, workers_count: int) -> None:
49
+ def __init__(self, uploader: AsyncUploader, pipe_connection: multiprocessing.connection.Connection, workers_count: int) -> None:
46
50
  self.workers = []
47
51
  self.job_queue = multiprocessing.Queue()
48
52
  self.auth_event = multiprocessing.Event()
@@ -53,6 +57,7 @@ class AsyncDownloader(object):
53
57
  self.acc_selector = AccountSelector(self.manager, ACC_FILE, PROXY_FILE)
54
58
  self.uploader = uploader
55
59
  self.workers_count = workers_count
60
+ self.status_pipe = pipe_connection
56
61
  if os.environ.get("TG_PREMIUM", default="false") == "true":
57
62
  self.TG_FILE_LIMIT = 4294967296 # 4 GiB
58
63
 
@@ -154,6 +159,7 @@ class AsyncDownloader(object):
154
159
  actor.send_message_to_admin_func = self.send_message_to_admin
155
160
  actor.request_yt_auth = self.request_yt_auth
156
161
  actor.auth_event = self.auth_event
162
+ actor.status_pipe = self.status_pipe
157
163
  # job retry loop
158
164
  while self.allow_loop.value == 1:
159
165
  try:
@@ -4,6 +4,7 @@ from abc import ABC, abstractmethod
4
4
  from typing import Callable, Union
5
5
  import logging
6
6
  import multiprocessing
7
+ import multiprocessing.connection
7
8
  import socket
8
9
  import requests.packages.urllib3.util.connection as urllib3_cn
9
10
 
@@ -14,6 +15,7 @@ class ScraperAbstract(ABC):
14
15
  original_gai_family = None
15
16
  send_message_to_admin_func: Callable = lambda: None
16
17
  request_yt_auth: Callable = lambda: None
18
+ status_pipe: multiprocessing.connection.Connection = None
17
19
  auth_event = None
18
20
  account = None
19
21
  account_index = 0
@@ -1,39 +1,46 @@
1
- import os
1
+ import http.client
2
2
  import io
3
- import pathlib
4
- import time
3
+ import json
4
+ import logging
5
5
  import math
6
+ import os
7
+ import pathlib
6
8
  import socket
7
9
  import ssl
10
+ import time
11
+ import urllib
8
12
  #from abc import abstractmethod
9
- from typing import Callable, Union, Optional
10
- import logging
11
- import json
12
- import http.client
13
+ from typing import Callable, Optional, Union
14
+ from urllib.parse import parse_qs, urlparse
15
+
16
+ import numpy as np
17
+ import pytubefix
13
18
  import pytubefix.exceptions
14
19
  import requests
15
- from PIL import Image
16
- import numpy as np
17
- import urllib
18
20
  import urllib3
19
- from urllib.parse import urlparse, parse_qs
20
-
21
- import pytubefix
21
+ #from pytubefix.exceptions import VideoUnavailable, VideoPrivate, MaxRetriesExceeded
22
+ import yt_dlp
23
+ from PIL import Image
22
24
  from pytubefix import YouTube
23
25
  from pytubefix.innertube import _default_clients
24
26
  from pytubefix.streams import Stream
25
- #from pytubefix.exceptions import VideoUnavailable, VideoPrivate, MaxRetriesExceeded
26
- import yt_dlp
27
27
 
28
28
  from warp_beacon.jobs.download_job import DownloadJob
29
29
  from warp_beacon.scraper.abstract import ScraperAbstract
30
+ from warp_beacon.scraper.exceptions import (BadProxy, TimeOut, Unavailable,
31
+ extract_exception_message)
30
32
  from warp_beacon.yt_auth import YtAuth
31
- from warp_beacon.scraper.exceptions import TimeOut, Unavailable, BadProxy, extract_exception_message
32
33
 
33
34
  class YoutubeAbstract(ScraperAbstract):
34
35
  DOWNLOAD_DIR = "/tmp"
35
36
  YT_SESSION_FILE = '/var/warp_beacon/yt_session_%d.json'
36
37
 
38
+ job = None
39
+
40
+ def __init__(self, account: tuple, proxy: dict=None) -> None:
41
+ super().__init__(account, proxy)
42
+ self._download_progress_threshold = 20
43
+
37
44
  def validate_session(self) -> int:
38
45
  try:
39
46
  logging.info("Validating YT session(s) ...")
@@ -68,8 +75,8 @@ class YoutubeAbstract(ScraperAbstract):
68
75
  ext = path_info.suffix
69
76
  #old_filename = path_info.stem
70
77
  time_name = str(time.time()).replace('.', '_')
71
- new_filename = "%s%s" % (time_name, ext)
72
- new_filepath = "%s/%s" % (os.path.dirname(filename), new_filename)
78
+ new_filename = f"{time_name}{ext}"
79
+ new_filepath = f"{os.path.dirname(filename)}/{new_filename}"
73
80
 
74
81
  os.rename(filename, new_filepath)
75
82
 
@@ -83,7 +90,7 @@ class YoutubeAbstract(ScraperAbstract):
83
90
  def remove_tmp_files(self) -> None:
84
91
  for i in os.listdir(self.DOWNLOAD_DIR):
85
92
  if "yt_download_" in i:
86
- os.unlink("%s/%s" % (self.DOWNLOAD_DIR, i))
93
+ os.unlink(f"{self.DOWNLOAD_DIR}/{i}")
87
94
 
88
95
  def calculate_size_with_padding(self,
89
96
  image: Image,
@@ -234,8 +241,20 @@ class YoutubeAbstract(ScraperAbstract):
234
241
  def yt_on_progress(self, stream: Stream, chunk: bytes, bytes_remaining: int) -> None:
235
242
  total_size = stream.filesize or stream.filesize_approx
236
243
  bytes_downloaded = total_size - bytes_remaining
237
- percentage_of_completion = bytes_downloaded / total_size * 100
238
- logging.info("Downloaded %d%%", percentage_of_completion)
244
+ percentage_of_completion = bytes_downloaded / (total_size or 1) * 100
245
+ if total_size == 0 or percentage_of_completion >= self._download_progress_threshold:
246
+ msg = {
247
+ "action": "report_download_status",
248
+ "current": bytes_downloaded,
249
+ "total": total_size,
250
+ "message_id": self.job.placeholder_message_id,
251
+ "chat_id": self.job.chat_id,
252
+ "completed": percentage_of_completion >= 100
253
+ }
254
+ self.status_pipe.send(msg)
255
+ logging.debug("[Download worker] Downloaded %d%%", percentage_of_completion)
256
+ if total_size > 0:
257
+ self._download_progress_threshold += 20
239
258
 
240
259
  def build_proxies(self, proxy_dsn: str) -> dict:
241
260
  if not proxy_dsn:
@@ -309,6 +328,7 @@ class YoutubeAbstract(ScraperAbstract):
309
328
  raise NotImplementedError("You should to implement _download_yt_dlp method")
310
329
 
311
330
  def download(self, job: DownloadJob) -> list:
331
+ self.job = job
312
332
  ret = []
313
333
  thumbnail = None
314
334
  try:
@@ -323,6 +343,8 @@ class YoutubeAbstract(ScraperAbstract):
323
343
  logging.exception(e)
324
344
 
325
345
  try:
346
+ self.status_pipe.send({"action": "report_download_status", "current": 0, "total": 0,
347
+ "message_id": self.job.placeholder_message_id, "chat_id": self.job.chat_id})
326
348
  ret = self.download_hndlr(self._download, job.url, session=True, thumbnail=thumbnail)
327
349
  return ret
328
350
  except (Unavailable, TimeOut, KeyError) as e:
@@ -4,9 +4,7 @@ import asyncio
4
4
  from typing import Optional, Union
5
5
 
6
6
  import logging
7
-
8
7
  import html
9
-
10
8
  import uvloop
11
9
 
12
10
  from pyrogram import Client, filters
@@ -29,6 +27,7 @@ from warp_beacon.telegram.utils import Utils
29
27
  from warp_beacon.telegram.caption_shortener import CaptionShortner
30
28
  from warp_beacon.scheduler.scheduler import IGScheduler
31
29
  from warp_beacon.telegram.edit_message import EditMessage
30
+ from warp_beacon.telegram.download_status import DownloadStatus
32
31
 
33
32
  class Bot(object):
34
33
  should_exit = None
@@ -42,6 +41,7 @@ class Bot(object):
42
41
  scheduler = None
43
42
  me = None
44
43
  edit_message = None
44
+ download_status = None
45
45
 
46
46
  def __init__(self, tg_bot_name: str, tg_token: str, tg_api_id: str, tg_api_hash: str) -> None:
47
47
  # Enable logging
@@ -76,9 +76,11 @@ class Bot(object):
76
76
  pool_size=int(os.environ.get("UPLOAD_POOL_SIZE", default=workers_amount)),
77
77
  loop=self.client.loop
78
78
  )
79
+ self.download_status = DownloadStatus(self.client)
79
80
  self.downloader = warp_beacon.scraper.AsyncDownloader(
80
81
  workers_count=int(os.environ.get("WORKERS_POOL_SIZE", default=workers_amount)),
81
- uploader=self.uploader
82
+ uploader=self.uploader,
83
+ pipe_connection=self.download_status.child_conn
82
84
  )
83
85
 
84
86
  self.scheduler = IGScheduler(self.downloader)
@@ -111,6 +113,7 @@ class Bot(object):
111
113
  os.environ["TG_PREMIUM"] = "true"
112
114
  self.downloader.start()
113
115
  self.uploader.start()
116
+ loop.add_reader(self.download_status.status_pipe.fileno(), self.download_status.on_status)
114
117
  self.scheduler.start()
115
118
  logging.info("Warp Beacon version '%s' started", __version__)
116
119
  await self.should_exit.wait()
@@ -402,13 +405,6 @@ class Bot(object):
402
405
 
403
406
  return args
404
407
 
405
- async def progress_callback(self, current: int, total: int, chat_id: int | str, message_id: int, label: str) -> None:
406
- percents = int(current * 100 / total)
407
- if (percents % 40) > 0 or percents == 100:
408
- p = round(percents)
409
- await self.client.edit_message_caption(chat_id, message_id, f"<b>{p}% <code>{label}</code> uploaded</b>", ParseMode.HTML)
410
- logging.info("[%s] Uploaded to Telegram %d%%", label, p)
411
-
412
408
  async def upload_job(self, job: UploadJob) -> list[str]:
413
409
  tg_file_ids = []
414
410
  try:
@@ -0,0 +1,43 @@
1
+ import logging
2
+ from multiprocessing import Pipe
3
+ from pyrogram import Client
4
+ from warp_beacon.telegram.progress_bar import ProgressBar
5
+
6
+ class DownloadStatus(object):
7
+ status_pipe = None
8
+ child_conn = None
9
+ client = None
10
+ progress_bars = None
11
+
12
+ def __init__(self, client: Client) -> None:
13
+ self.progress_bars = {}
14
+ self.client = client
15
+ self.status_pipe, self.child_conn = Pipe()
16
+
17
+ async def handle_message(self, msg: dict, progress_bar: ProgressBar) -> None:
18
+ await progress_bar.progress_callback(
19
+ current=msg.get("current", 0),
20
+ total=msg.get("total", 0),
21
+ message_id=msg.get("message_id", 0),
22
+ chat_id=msg.get("chat_id", 0),
23
+ operation="Downloaded"
24
+ )
25
+
26
+ def on_status(self) -> None:
27
+ msg = self.status_pipe.recv()
28
+ if not msg:
29
+ logging.warning("Empty status message!")
30
+ return
31
+ logging.info("Received pipe message: %s", msg)
32
+ message_id = msg.get("message_id", 0)
33
+ chat_id = msg.get("chat_id", 0)
34
+ a_key = f"{message_id}:{chat_id}"
35
+ progress_bar = None
36
+ if a_key not in self.progress_bars:
37
+ progress_bar = ProgressBar(self.client)
38
+ self.progress_bars[a_key] = progress_bar
39
+ else:
40
+ progress_bar = self.progress_bars[a_key]
41
+ task = self.client.loop.create_task(self.handle_message(msg, progress_bar))
42
+ if msg.get("completed", False):
43
+ task.add_done_callback(lambda _: self.progress_bars.pop(a_key, None))
@@ -93,7 +93,8 @@ class EditMessage(object):
93
93
  file_name: str = None
94
94
  ) -> None:
95
95
  progress_bar = ProgressBar(self.client)
96
- raw_file = await self.client.save_file(path=media.media, progress=progress_bar.progress_callback, progress_args=(chat_id, message_id, file_name,))
96
+ await progress_bar.progress_callback(current=0, total=0, chat_id=chat_id, message_id=message_id, operation="Uploaded", label=file_name)
97
+ raw_file = await self.client.save_file(path=media.media, progress=progress_bar.progress_callback, progress_args=(chat_id, message_id, "Uploaded", file_name,))
97
98
 
98
99
  caption = media.caption
99
100
  parse_mode = media.parse_mode
@@ -106,17 +107,17 @@ class EditMessage(object):
106
107
  raw_media = None
107
108
  if isinstance(media, types.InputMediaVideo):
108
109
  progress_bar_thumb = ProgressBar(self.client)
109
- raw_file_thumb = await self.client.save_file(path=media.thumb, progress=progress_bar_thumb.progress_callback, progress_args=(chat_id, message_id, "thumbnail",))
110
+ raw_file_thumb = await self.client.save_file(path=media.thumb, progress=progress_bar_thumb.progress_callback, progress_args=(chat_id, message_id, "Uploaded", "thumbnail",))
110
111
  raw_media = self.get_wrapped_video(raw_file=raw_file, raw_thumb=raw_file_thumb, media=media, file_name=file_name)
111
112
  elif isinstance(media, types.InputMediaPhoto):
112
113
  raw_media = self.get_wrapped_photo(raw_file=raw_file, media=media)
113
114
  elif isinstance(media, types.InputMediaAudio):
114
115
  progress_bar_thumb = ProgressBar(self.client)
115
- raw_file_thumb = await self.client.save_file(path=media.thumb, progress=progress_bar_thumb.progress_callback, progress_args=(chat_id, message_id, "thumbnail",))
116
+ raw_file_thumb = await self.client.save_file(path=media.thumb, progress=progress_bar_thumb.progress_callback, progress_args=(chat_id, message_id, "Uploaded", "thumbnail",))
116
117
  raw_media = self.get_wrapped_audio(raw_file=raw_file, raw_thumb=raw_file_thumb, media=media, file_name=file_name)
117
118
  elif isinstance(media, types.InputMediaAnimation):
118
119
  progress_bar_thumb = ProgressBar(self.client)
119
- raw_file_thumb = await self.client.save_file(path=media.thumb, progress=progress_bar_thumb.progress_callback, progress_args=(chat_id, message_id, "thumbnail",))
120
+ raw_file_thumb = await self.client.save_file(path=media.thumb, progress=progress_bar_thumb.progress_callback, progress_args=(chat_id, message_id, "Uploaded", "thumbnail",))
120
121
  raw_media = self.get_wrapped_animation(raw_file=raw_file, raw_thumb=raw_file_thumb, media=media, file_name=file_name)
121
122
 
122
123
  peer = await self.client.resolve_peer(chat_id)
@@ -10,6 +10,10 @@ class ProgressBar(object):
10
10
  def __init__(self, client: Client) -> None:
11
11
  self._next_threshold = 20
12
12
  self.client = client
13
+ self.complete = False
14
+
15
+ def is_complete(self) -> bool:
16
+ return self.complete
13
17
 
14
18
  def make_progress_bar(self, current: int, total: int, length: int = 10) -> str:
15
19
  """
@@ -43,20 +47,23 @@ class ProgressBar(object):
43
47
  percent = frac * 100
44
48
  return f"[{pbar}] {round(percent)}%"
45
49
 
46
- async def progress_callback(self, current: int, total: int, chat_id: int | str, message_id: int, label: str) -> None:
47
- percent = current * 100 / total
48
- if percent >= self._next_threshold:
50
+ async def progress_callback(self, current: int, total: int, chat_id: int | str, message_id: int, operation: str, label: str = "") -> None:
51
+ percent = current * 100 / (total or 1)
52
+ if total == 0 or percent >= self._next_threshold:
49
53
  #pbar = self.make_progress_bar(percent, 100, 25)
50
54
  pbar = self.make_emoji_progress_bar(percent, 100, 14)
51
- logging.info("[%s] Uploaded to Telegram %d%%", label, percent)
55
+ logging.info("[%s] Uploaded to Telegram %d%%", label or operation, percent)
52
56
  try:
53
- await self.client.edit_message_caption(chat_id, message_id, f"{pbar} <b>Uploading</b> {label}", ParseMode.HTML)
57
+ await self.client.edit_message_caption(chat_id, message_id, f"{pbar} <b>{operation}</b> {label}", ParseMode.HTML)
54
58
  except MessageNotModified:
55
59
  logging.warning("bad_request_400.MessageNotModified")
56
60
  except Exception as e:
57
61
  logging.warning("An error occurred while updating progress bar")
58
62
  logging.exception(e)
59
- self._next_threshold += 20
63
+ if total > 0:
64
+ self._next_threshold += 20
65
+ if percent >= 100:
66
+ self.complete = True
60
67
 
61
68
  @staticmethod
62
69
  def make_hash(chat_id: str | int, message_id: int, algorithm: str = 'sha256') -> str:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: warp_beacon
3
- Version: 2.6.51
3
+ Version: 2.6.53
4
4
  Summary: Telegram bot for expanding external media links
5
5
  Home-page: https://github.com/sb0y/warp_beacon
6
6
  Author: Andrey Bagrintsev
@@ -4,7 +4,7 @@ var/warp_beacon/accounts.json,sha256=OsXdncs6h88xrF_AP6_WDCK1waGBn9SR-uYdIeK37GM
4
4
  var/warp_beacon/placeholder.gif,sha256=cE5CGJVaop4Sx21zx6j4AyoHU0ncmvQuS2o6hJfEH88,6064
5
5
  var/warp_beacon/proxies.json,sha256=VnjlQDXumOEq72ZFjbh6IqHS1TEHqn8HPYAZqWCeSIA,95
6
6
  warp_beacon/__init__.py,sha256=_rThNODmz0nDp_n4mWo_HKaNFE5jk1_7cRhHyYaencI,163
7
- warp_beacon/__version__.py,sha256=97aVkVvphE-RNmc19iil89QyOmg2AptM2VnjP0DOdho,24
7
+ warp_beacon/__version__.py,sha256=hcmauQ1R3h1hRfSDodv85yWUciNm6uQit0GyDrOnLDg,24
8
8
  warp_beacon/warp_beacon.py,sha256=ED43vNzdjDUJ_9qLCbri0bjWLWEJ69BENGj9i7G6AvM,342
9
9
  warp_beacon/yt_auth.py,sha256=GUTKqYr_tzDC-07Lx_ahWXSag8EyLxXBUnQbDBIkEmk,6022
10
10
  warp_beacon/compress/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -22,8 +22,8 @@ warp_beacon/mediainfo/video.py,sha256=UBZrhTN5IDI-aYu6tsJEILo9nFkjHhkldGVFmvV7tE
22
22
  warp_beacon/scheduler/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
23
23
  warp_beacon/scheduler/instagram_human.py,sha256=0LaRUu0MBBuEOQeFzuq22HIYfJL9pTK_7udsXfef0Fk,8204
24
24
  warp_beacon/scheduler/scheduler.py,sha256=9OCh7Ta4wY_aTHGAOOZmaKXg56Ftx1N_aV1g6E3ZLKA,4941
25
- warp_beacon/scraper/__init__.py,sha256=NAx81O85yP0vUP1uZ9VHZlXh-zZgN_LHm7gWcU5BV18,19982
26
- warp_beacon/scraper/abstract.py,sha256=lra97QidGcSvoXgQh2-aGAO-DAgQU_u-ORQJ3BY-un4,2950
25
+ warp_beacon/scraper/__init__.py,sha256=buXmGcJnwCi0qGZAh3srcSbww0XvKyTACp_DW11e86Q,20225
26
+ warp_beacon/scraper/abstract.py,sha256=wwmk2L1S4H9la8x74eN4svdG-BgOdE1tKrOv7jjFhXE,3043
27
27
  warp_beacon/scraper/account_selector.py,sha256=-tAbOIbM7sC9QafsgSe64RCWXewRO-RehEYsYSB3HGo,9727
28
28
  warp_beacon/scraper/exceptions.py,sha256=EKwoF0oH2xZWbNU-v8DOaWK5skKwa3s1yTIBdlcfMpc,1452
29
29
  warp_beacon/scraper/fail_handler.py,sha256=zcPK3ZVEsu6JmHYcWP7L3naTRK3gWFVRkpP84VBOtJs,964
@@ -33,25 +33,26 @@ warp_beacon/scraper/instagram/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5N
33
33
  warp_beacon/scraper/instagram/captcha.py,sha256=9UYziuqB3Tsat_ET6ex-cnZDbi6yCnsXHSpmE8MuUHk,4651
34
34
  warp_beacon/scraper/instagram/instagram.py,sha256=zbkF-1lU5lxJ_2m8i8mGRX0EQMLVSflsOafBM1FEC6s,15588
35
35
  warp_beacon/scraper/youtube/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
36
- warp_beacon/scraper/youtube/abstract.py,sha256=-7ha34z6bWc5rA7BZdA3x3Mw9ELghGYoHklDbZQYgTo,12689
36
+ warp_beacon/scraper/youtube/abstract.py,sha256=KjtMb_5RJrAyLGz_BIBv-RdBRey6PnJX6jnOaJKsbTk,13485
37
37
  warp_beacon/scraper/youtube/music.py,sha256=5AeSBQyUgVCJT2hoBCV2WvlyuV9US09SYJhmBG_P9F8,2755
38
38
  warp_beacon/scraper/youtube/shorts.py,sha256=1GtoYUlxAwcgSQcn80u5ehNJytH5AN5dPOicmX-XD8E,1705
39
39
  warp_beacon/scraper/youtube/youtube.py,sha256=x9v9p1coA9TvBhxjNAofGu4UBkAEdYPE2ePRnU-5tK0,7233
40
40
  warp_beacon/storage/__init__.py,sha256=0Vajd0oITKJfu2vmNx5uQSt3-L6vwIvUYWJo8HZCjco,3398
41
41
  warp_beacon/storage/mongo.py,sha256=qC4ZiO8XXvPnP0rJwz4CJx42pqFsyAjCiW10W5QdT6E,527
42
42
  warp_beacon/telegram/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
43
- warp_beacon/telegram/bot.py,sha256=GTxKEAyCzMXXMrAEw2INQED7Y4XPE0CjiTexPDNWrpA,19256
43
+ warp_beacon/telegram/bot.py,sha256=W13hyJD89ozZallLq0NIb9c3uh1Hov8Axi7ab7LkE3A,19128
44
44
  warp_beacon/telegram/caption_shortener.py,sha256=EnguNCF52ne7y4P-iJAbI6K3sqoJqJbND_dX5Fhwkv0,1549
45
- warp_beacon/telegram/edit_message.py,sha256=XDGBbQ3c6xzQsqwJ2ufVozgDZh3oWF-JQlk3wk78soo,5556
45
+ warp_beacon/telegram/download_status.py,sha256=dV0mRIOkA8dU0ECsmC_rZ33XQhVnkW2nZBNTxQ0Y6x4,1342
46
+ warp_beacon/telegram/edit_message.py,sha256=_452kXWn65qj9iPSvSmORbKXXTjJ0z6kBCxPcmKDrlM,5742
46
47
  warp_beacon/telegram/handlers.py,sha256=uvR6TPHSqdSxigp3wR-ewiE6t3TvVcbVLVcYGwkgD2s,9559
47
48
  warp_beacon/telegram/placeholder_message.py,sha256=wN9-BRiyrtHG-EvXtZkGJHt2CX71munQ57ITttjt0mw,6400
48
- warp_beacon/telegram/progress_bar.py,sha256=KcS-cswDo-sShgOKionqNEpexs-o9e4qqFAtPf45Rlw,2375
49
+ warp_beacon/telegram/progress_bar.py,sha256=TYvoGTmr44aqAYbZ6byU5sekdVoz7x_0YMDkLO6plYo,2575
49
50
  warp_beacon/telegram/progress_file_reader.py,sha256=e3equyNKlKs764AD-iE9QRsh3YDHTzP78Mx5tdvPPWs,969
50
51
  warp_beacon/telegram/utils.py,sha256=1Lq67aRylVJzbwSyvAgjPAGjJZFATkICvAj3TJGuJiM,4635
51
52
  warp_beacon/uploader/__init__.py,sha256=j3qcuKhpchseZLGzSsSiogqe6WdMbkK8d3I-ConhNRs,5687
52
- warp_beacon-2.6.51.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
53
- warp_beacon-2.6.51.dist-info/METADATA,sha256=vVqQnB1spafaejdnk_OxfPCvZyB8vj4zHHXBt6qFzWQ,22706
54
- warp_beacon-2.6.51.dist-info/WHEEL,sha256=0CuiUZ_p9E4cD6NyLD6UG80LBXYyiSYZOKDm5lp32xk,91
55
- warp_beacon-2.6.51.dist-info/entry_points.txt,sha256=eSB61Rb89d56WY0O-vEIQwkn18J-4CMrJcLA_R_8h3g,119
56
- warp_beacon-2.6.51.dist-info/top_level.txt,sha256=pYstFq5qRCQuQc4fOAAc1ti5UwsYldTREx7h2dNplI4,1297
57
- warp_beacon-2.6.51.dist-info/RECORD,,
53
+ warp_beacon-2.6.53.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
54
+ warp_beacon-2.6.53.dist-info/METADATA,sha256=Jg3gJ15i-ifchkeXPPek-KLoCByZrT0fqAyllygNDIk,22706
55
+ warp_beacon-2.6.53.dist-info/WHEEL,sha256=0CuiUZ_p9E4cD6NyLD6UG80LBXYyiSYZOKDm5lp32xk,91
56
+ warp_beacon-2.6.53.dist-info/entry_points.txt,sha256=eSB61Rb89d56WY0O-vEIQwkn18J-4CMrJcLA_R_8h3g,119
57
+ warp_beacon-2.6.53.dist-info/top_level.txt,sha256=2x4wBJ_HeeBt6Bsdu9C4tItXWqH8spX8jGSHpoL0IMw,1334
58
+ warp_beacon-2.6.53.dist-info/RECORD,,
@@ -34,6 +34,7 @@ warp_beacon/storage/mongo
34
34
  warp_beacon/telegram
35
35
  warp_beacon/telegram/bot
36
36
  warp_beacon/telegram/caption_shortener
37
+ warp_beacon/telegram/download_status
37
38
  warp_beacon/telegram/edit_message
38
39
  warp_beacon/telegram/handlers
39
40
  warp_beacon/telegram/placeholder_message