warp-beacon 1.1.1__tar.gz → 1.1.2__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-1.1.1/warp_beacon.egg-info → warp_beacon-1.1.2}/PKG-INFO +1 -1
- warp_beacon-1.1.2/warp_beacon/__version__.py +2 -0
- warp_beacon-1.1.2/warp_beacon/mediainfo/abstract.py +28 -0
- warp_beacon-1.1.2/warp_beacon/mediainfo/audio.py +19 -0
- warp_beacon-1.1.2/warp_beacon/scraper/youtube/music.py +110 -0
- {warp_beacon-1.1.1 → warp_beacon-1.1.2/warp_beacon.egg-info}/PKG-INFO +1 -1
- {warp_beacon-1.1.1 → warp_beacon-1.1.2}/warp_beacon.egg-info/SOURCES.txt +3 -0
- warp_beacon-1.1.1/warp_beacon/__version__.py +0 -2
- {warp_beacon-1.1.1 → warp_beacon-1.1.2}/LICENSE +0 -0
- {warp_beacon-1.1.1 → warp_beacon-1.1.2}/MANIFEST.in +0 -0
- {warp_beacon-1.1.1 → warp_beacon-1.1.2}/README.md +0 -0
- {warp_beacon-1.1.1 → warp_beacon-1.1.2}/assets/placeholder.gif +0 -0
- {warp_beacon-1.1.1 → warp_beacon-1.1.2}/etc/.gitignore +0 -0
- {warp_beacon-1.1.1 → warp_beacon-1.1.2}/etc/warp_beacon.conf +0 -0
- {warp_beacon-1.1.1 → warp_beacon-1.1.2}/etc/warp_beacon.service +0 -0
- {warp_beacon-1.1.1 → warp_beacon-1.1.2}/pyproject.toml +0 -0
- {warp_beacon-1.1.1 → warp_beacon-1.1.2}/setup.cfg +0 -0
- {warp_beacon-1.1.1 → warp_beacon-1.1.2}/setup.py +0 -0
- {warp_beacon-1.1.1 → warp_beacon-1.1.2}/warp_beacon/__init__.py +0 -0
- {warp_beacon-1.1.1 → warp_beacon-1.1.2}/warp_beacon/compress/__init__.py +0 -0
- {warp_beacon-1.1.1 → warp_beacon-1.1.2}/warp_beacon/compress/video.py +0 -0
- {warp_beacon-1.1.1 → warp_beacon-1.1.2}/warp_beacon/jobs/__init__.py +0 -0
- {warp_beacon-1.1.1 → warp_beacon-1.1.2}/warp_beacon/jobs/abstract.py +0 -0
- {warp_beacon-1.1.1 → warp_beacon-1.1.2}/warp_beacon/jobs/download_job.py +0 -0
- {warp_beacon-1.1.1 → warp_beacon-1.1.2}/warp_beacon/jobs/upload_job.py +0 -0
- {warp_beacon-1.1.1 → warp_beacon-1.1.2}/warp_beacon/mediainfo/__init__.py +0 -0
- {warp_beacon-1.1.1 → warp_beacon-1.1.2}/warp_beacon/mediainfo/video.py +0 -0
- {warp_beacon-1.1.1 → warp_beacon-1.1.2}/warp_beacon/scraper/__init__.py +0 -0
- {warp_beacon-1.1.1 → warp_beacon-1.1.2}/warp_beacon/scraper/abstract.py +0 -0
- {warp_beacon-1.1.1 → warp_beacon-1.1.2}/warp_beacon/scraper/exceptions.py +0 -0
- {warp_beacon-1.1.1 → warp_beacon-1.1.2}/warp_beacon/scraper/instagram.py +0 -0
- {warp_beacon-1.1.1 → warp_beacon-1.1.2}/warp_beacon/scraper/youtube/__init__.py +0 -0
- {warp_beacon-1.1.1 → warp_beacon-1.1.2}/warp_beacon/scraper/youtube/shorts.py +0 -0
- {warp_beacon-1.1.1 → warp_beacon-1.1.2}/warp_beacon/storage/__init__.py +0 -0
- {warp_beacon-1.1.1 → warp_beacon-1.1.2}/warp_beacon/uploader/__init__.py +0 -0
- {warp_beacon-1.1.1 → warp_beacon-1.1.2}/warp_beacon/warp_beacon.py +0 -0
- {warp_beacon-1.1.1 → warp_beacon-1.1.2}/warp_beacon.egg-info/dependency_links.txt +0 -0
- {warp_beacon-1.1.1 → warp_beacon-1.1.2}/warp_beacon.egg-info/entry_points.txt +0 -0
- {warp_beacon-1.1.1 → warp_beacon-1.1.2}/warp_beacon.egg-info/requires.txt +0 -0
- {warp_beacon-1.1.1 → warp_beacon-1.1.2}/warp_beacon.egg-info/top_level.txt +0 -0
@@ -0,0 +1,28 @@
|
|
1
|
+
import os
|
2
|
+
from abc import ABC, abstractmethod
|
3
|
+
|
4
|
+
import av
|
5
|
+
|
6
|
+
class MediaInfoAbstract(ABC):
|
7
|
+
filename = ""
|
8
|
+
container = None
|
9
|
+
duration = 0.0
|
10
|
+
|
11
|
+
def __init__(self, filename: str) -> None:
|
12
|
+
self.filename = filename
|
13
|
+
self.container = av.open(file=self.filename, mode='r')
|
14
|
+
|
15
|
+
def __del__(self) -> None:
|
16
|
+
if self.container:
|
17
|
+
self.container.close()
|
18
|
+
|
19
|
+
def get_duration(self) -> float:
|
20
|
+
return self.duration
|
21
|
+
|
22
|
+
@staticmethod
|
23
|
+
def get_filesize(filename: str) -> float:
|
24
|
+
return os.stat(filename).st_size
|
25
|
+
|
26
|
+
@abstractmethod
|
27
|
+
def get_finfo(cls, except_info: tuple=()) -> dict:
|
28
|
+
raise NotImplementedError
|
@@ -0,0 +1,19 @@
|
|
1
|
+
from warp_beacon.mediainfo.abstract import MediaInfoAbstract
|
2
|
+
|
3
|
+
class AudioInfo(MediaInfoAbstract):
|
4
|
+
def __init__(self, filename: str) -> None:
|
5
|
+
super(AudioInfo, self).__init__(filename)
|
6
|
+
if self.container:
|
7
|
+
stream_list = self.container.streams.get(audio=0)
|
8
|
+
if stream_list:
|
9
|
+
stream = stream_list[0]
|
10
|
+
time_base = stream.time_base
|
11
|
+
self.duration = float(stream.duration * time_base)
|
12
|
+
|
13
|
+
def get_finfo(self, except_info: tuple=()) -> dict:
|
14
|
+
res = {}
|
15
|
+
if "duration" not in except_info:
|
16
|
+
res["duration"] = round(self.get_duration())
|
17
|
+
if "filesize" not in except_info:
|
18
|
+
res["filesize"] = AudioInfo.get_filesize(self.filename)
|
19
|
+
return res
|
@@ -0,0 +1,110 @@
|
|
1
|
+
import os
|
2
|
+
import pathlib
|
3
|
+
import time
|
4
|
+
|
5
|
+
import socket
|
6
|
+
import ssl
|
7
|
+
|
8
|
+
from typing import Callable, Union
|
9
|
+
|
10
|
+
import requests
|
11
|
+
import urllib
|
12
|
+
import http.client
|
13
|
+
|
14
|
+
from pytubefix import YouTube
|
15
|
+
from pytubefix.exceptions import VideoUnavailable, VideoPrivate, MaxRetriesExceeded
|
16
|
+
|
17
|
+
from warp_beacon.scraper.exceptions import NotFound, UnknownError, TimeOut, Unavailable, extract_exception_message
|
18
|
+
from warp_beacon.scraper.abstract import ScraperAbstract
|
19
|
+
|
20
|
+
import logging
|
21
|
+
|
22
|
+
DOWNLOAD_DIR = "/tmp"
|
23
|
+
|
24
|
+
class YoutubeMusicScraper(ScraperAbstract):
|
25
|
+
|
26
|
+
def __init__(self) -> None:
|
27
|
+
pass
|
28
|
+
|
29
|
+
def __del__(self) -> None:
|
30
|
+
pass
|
31
|
+
|
32
|
+
def remove_tmp_files(self) -> None:
|
33
|
+
for i in os.listdir(DOWNLOAD_DIR):
|
34
|
+
if "yt_download_" in i:
|
35
|
+
os.unlink("%s/%s" % (DOWNLOAD_DIR, i))
|
36
|
+
|
37
|
+
def _download_hndlr(self, func: Callable, *args: tuple[str], **kwargs: dict[str]) -> Union[str, dict]:
|
38
|
+
ret_val = ''
|
39
|
+
max_retries = int(os.environ.get("YT_MUSIC_MAX_RETRIES", default=6))
|
40
|
+
pause_secs = int(os.environ.get("YT_MUSIC_PAUSE_BEFORE_RETRY", default=3))
|
41
|
+
timeout = int(os.environ.get("YT_MUSIC_TIMEOUT", default=60))
|
42
|
+
timeout_increment = int(os.environ.get("YT_MUSIC_TIMEOUT_INCREMENT", default=60))
|
43
|
+
retries = 0
|
44
|
+
while max_retries >= retries:
|
45
|
+
try:
|
46
|
+
kwargs["timeout"] = timeout
|
47
|
+
ret_val = func(*args, **kwargs)
|
48
|
+
break
|
49
|
+
except MaxRetriesExceeded:
|
50
|
+
# do noting, not interested
|
51
|
+
pass
|
52
|
+
#except http.client.IncompleteRead as e:
|
53
|
+
except (socket.timeout,
|
54
|
+
ssl.SSLError,
|
55
|
+
http.client.IncompleteRead,
|
56
|
+
http.client.HTTPException,
|
57
|
+
requests.RequestException,
|
58
|
+
urllib.error.URLError,
|
59
|
+
urllib.error.HTTPError) as e:
|
60
|
+
if hasattr(e, "code") and int(e.code) == 403:
|
61
|
+
raise Unavailable(extract_exception_message(e))
|
62
|
+
logging.warning("Youtube read timeout! Retrying in %d seconds ...", pause_secs)
|
63
|
+
logging.info("Your `YT_MUSIC_MAX_RETRIES` values is %d", max_retries)
|
64
|
+
logging.exception(extract_exception_message(e))
|
65
|
+
if max_retries <= retries:
|
66
|
+
self.remove_tmp_files()
|
67
|
+
raise TimeOut(extract_exception_message(e))
|
68
|
+
retries += 1
|
69
|
+
timeout += timeout_increment
|
70
|
+
time.sleep(pause_secs)
|
71
|
+
except (VideoUnavailable, VideoPrivate) as e:
|
72
|
+
raise Unavailable(extract_exception_message(e))
|
73
|
+
|
74
|
+
return ret_val
|
75
|
+
|
76
|
+
def rename_local_file(self, filename: str) -> str:
|
77
|
+
if not os.path.exists(filename):
|
78
|
+
raise NameError("No file provided")
|
79
|
+
path_info = pathlib.Path(filename)
|
80
|
+
ext = path_info.suffix
|
81
|
+
old_filename = path_info.stem
|
82
|
+
time_name = str(time.time()).replace('.', '_')
|
83
|
+
new_filename = "%s%s" % (time_name, ext)
|
84
|
+
new_filepath = "%s/%s" % (os.path.dirname(filename), new_filename)
|
85
|
+
|
86
|
+
os.rename(filename, new_filepath)
|
87
|
+
|
88
|
+
return new_filepath
|
89
|
+
|
90
|
+
def _download(self, url: str, timeout: int = 0) -> list:
|
91
|
+
res = []
|
92
|
+
yt = YouTube(url)
|
93
|
+
stream = yt.streams.get_audio_only()
|
94
|
+
if stream:
|
95
|
+
logging.info("Operation timeout is '%d'", timeout)
|
96
|
+
local_file = stream.download(
|
97
|
+
output_path=DOWNLOAD_DIR,
|
98
|
+
max_retries=0,
|
99
|
+
timeout=timeout,
|
100
|
+
skip_existing=False,
|
101
|
+
filename_prefix='yt_download_',
|
102
|
+
mp3=True
|
103
|
+
)
|
104
|
+
logging.info("Temp filename: '%s'", local_file)
|
105
|
+
res.append({"local_media_path": self.rename_local_file(local_file), "canonical_name": stream.title, "media_type": "audio"})
|
106
|
+
|
107
|
+
return res
|
108
|
+
|
109
|
+
def download(self, url: str) -> list:
|
110
|
+
return self._download_hndlr(self._download, url)
|
@@ -24,12 +24,15 @@ warp_beacon/jobs/abstract.py
|
|
24
24
|
warp_beacon/jobs/download_job.py
|
25
25
|
warp_beacon/jobs/upload_job.py
|
26
26
|
warp_beacon/mediainfo/__init__.py
|
27
|
+
warp_beacon/mediainfo/abstract.py
|
28
|
+
warp_beacon/mediainfo/audio.py
|
27
29
|
warp_beacon/mediainfo/video.py
|
28
30
|
warp_beacon/scraper/__init__.py
|
29
31
|
warp_beacon/scraper/abstract.py
|
30
32
|
warp_beacon/scraper/exceptions.py
|
31
33
|
warp_beacon/scraper/instagram.py
|
32
34
|
warp_beacon/scraper/youtube/__init__.py
|
35
|
+
warp_beacon/scraper/youtube/music.py
|
33
36
|
warp_beacon/scraper/youtube/shorts.py
|
34
37
|
warp_beacon/storage/__init__.py
|
35
38
|
warp_beacon/uploader/__init__.py
|
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
|