12 import variables as var
13 from media.file import FileItem
16 log = logging.getLogger("bot")
19 class URLItem(FileItem):
20 def __init__(self, bot, url, from_dict=None):
21 self.validating_lock = threading.Lock()
26 self.ready = 'pending'
27 super().__init__(bot, "")
28 self.id = hashlib.md5(url.encode()).hexdigest()
29 path = var.tmp_folder + self.id + ".mp3"
31 if os.path.isfile(path):
32 self.log.info("url: file existed for url %s " % self.url)
35 self._get_info_from_tag()
37 # self._get_info_from_url()
40 super().__init__(bot, "", from_dict)
41 self.url = from_dict['url']
42 self.duration = from_dict['duration']
44 self.downloading = False
51 if self.downloading or self.ready != 'yes':
53 if self.ready == 'yes' and not os.path.exists(self.path):
55 "url: music file missed for %s" % self.format_debug_string())
56 self.ready = 'validated'
62 if self.ready in ['yes', 'validated']:
65 if os.path.exists(self.path):
69 # avoid multiple process validating in the meantime
70 self.validating_lock.acquire()
71 info = self._get_info_from_url()
72 self.validating_lock.release()
74 if self.duration == 0 and not info:
77 if self.duration > var.config.getint('bot', 'max_track_duration') != 0:
78 # Check the length, useful in case of playlist, it wasn't checked before)
80 "url: " + self.url + " has a duration of " + str(self.duration) + " min -- too long")
81 self.send_client_message(constants.strings('too_long'))
84 self.ready = "validated"
87 # Run in a other thread
89 if not self.downloading:
90 assert self.ready == 'validated'
91 return self._download()
93 assert self.ready == 'yes'
96 def _get_info_from_url(self):
97 self.log.info("url: fetching metadata of url %s " % self.url)
102 with youtube_dl.YoutubeDL(ydl_opts) as ydl:
103 attempts = var.config.getint('bot', 'download_attempts', fallback=2)
104 for i in range(attempts):
106 info = ydl.extract_info(self.url, download=False)
107 self.duration = info['duration'] / 60
108 self.title = info['title']
111 except youtube_dl.utils.DownloadError:
115 self.ready = 'failed'
116 self.log.error("url: error while fetching info from the URL")
117 self.send_client_message(constants.strings('unable_download'))
121 media.system.clear_tmp_folder(var.tmp_folder, var.config.getint('bot', 'tmp_folder_max_size'))
123 self.downloading = True
124 base_path = var.tmp_folder + self.id
125 save_path = base_path + ".%(ext)s"
126 mp3_path = base_path + ".mp3"
128 # Download only if music is not existed
129 self.ready = "preparing"
131 self.log.info("bot: downloading url (%s) %s " % (self.title, self.url))
135 'format': 'bestaudio/best',
136 'outtmpl': save_path,
138 'writethumbnail': True,
141 'key': 'FFmpegExtractAudio',
142 'preferredcodec': 'mp3',
143 'preferredquality': '192'},
144 {'key': 'FFmpegMetadata'}]
147 with youtube_dl.YoutubeDL(ydl_opts) as ydl:
148 attempts = var.config.getint('bot', 'download_attempts', fallback=2)
149 download_succeed = False
150 for i in range(attempts):
151 self.log.info("bot: download attempts %d / %d" % (i+1, attempts))
153 info = ydl.extract_info(self.url)
154 download_succeed = True
157 error_traceback = traceback.format_exc().split("During")[0]
158 error = error_traceback.rstrip().split("\n")[-1]
159 self.log.error("bot: download failed with error:\n %s" % error)
165 "bot: finished downloading url (%s) %s, saved to %s." % (self.title, self.url, self.path))
166 self.downloading = False
167 self._read_thumbnail_from_file(base_path + ".jpg")
170 for f in glob.glob(base_path + "*"):
172 self.send_client_message(constants.strings('unable_download'))
173 self.ready = "failed"
174 self.downloading = False
177 def _read_thumbnail_from_file(self, path_thumbnail):
178 if os.path.isfile(path_thumbnail):
179 im = Image.open(path_thumbnail)
180 self.thumbnail = self._prepare_thumbnail(im)
183 dict = super().to_dict()
185 dict['url'] = self.url
186 dict['duration'] = self.duration
191 def format_debug_string(self):
192 return "[url] {title} ({url})".format(
197 def format_song_string(self, user):
198 return constants.strings("url_item",
203 def format_current_playing(self, user):
204 display = constants.strings("now_playing", item=self.format_song_string(user))
207 thumbnail_html = '<img width="80" src="data:image/jpge;base64,' + \
208 self.thumbnail + '"/>'
209 display += "<br />" + thumbnail_html
213 def format_short_string(self):
214 return self.title if self.title else self.url
216 def display_type(self):
217 return constants.strings("url")