From 22b9caa6dd21b46a276ce231d4db67575ad96451 Mon Sep 17 00:00:00 2001 From: Azlux Date: Sun, 7 Jun 2020 01:11:40 +0200 Subject: [PATCH 1/5] remove explicite lib depending of pymumble --- requirements.txt | 2 -- 1 file changed, 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index b0800bf..2be02cb 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,3 @@ -opuslib==2.0.0 -protobuf==3.4.0 flask youtube-dl python-magic From e133788ba1f3aec86fe8ff3e19bae90bb4fe1182 Mon Sep 17 00:00:00 2001 From: Terry Geng Date: Tue, 9 Jun 2020 19:58:08 +0800 Subject: [PATCH 2/5] fix: playbar keeps extending when playlist is empty. --- static/js/custom.js | 1 + 1 file changed, 1 insertion(+) diff --git a/static/js/custom.js b/static/js/custom.js index 1c29fd6..710682d 100644 --- a/static/js/custom.js +++ b/static/js/custom.js @@ -989,6 +989,7 @@ function playerSetIdle(){ playerTitle.textContent = '-- IDLE --'; playerArtist.textContent = ''; setProgressBar(playerBar, 0); + clearInterval(playhead_timer); } function updatePlayerInfo(item){ From 3c87c33ddd2838b06756aa07f6fccc32ea62ce6e Mon Sep 17 00:00:00 2001 From: Terry Geng Date: Tue, 9 Jun 2020 21:21:23 +0800 Subject: [PATCH 3/5] fix: typo that renders refresh_cache_on_startup = False useless, #163 --- mumbleBot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mumbleBot.py b/mumbleBot.py index 84eeb11..ab7346a 100644 --- a/mumbleBot.py +++ b/mumbleBot.py @@ -778,7 +778,7 @@ if __name__ == '__main__': var.tmp_folder = util.solve_filepath(var.config.get('bot', 'tmp_folder')) var.cache = MusicCache(var.music_db) - if var.config.get("bot", "refresh_cache_on_startup", fallback=True): + if var.config.getboolean("bot", "refresh_cache_on_startup", fallback=True): var.cache.build_dir_cache() # ====================== From 7f45ab0506d21a0992a438ebb1465c5fe0d721c5 Mon Sep 17 00:00:00 2001 From: Terry Geng Date: Thu, 11 Jun 2020 09:17:48 +0800 Subject: [PATCH 4/5] fix: bot failed to detect if the ffmpeg thread is dead, fixed #168. --- mumbleBot.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/mumbleBot.py b/mumbleBot.py index ab7346a..12c5a1b 100644 --- a/mumbleBot.py +++ b/mumbleBot.py @@ -446,12 +446,12 @@ class MumbleBot: raw_music = "" while not self.exit and self.mumble.is_alive(): - while self.thread and self.mumble.sound_output.get_buffer_size() > 0.5 and not self.exit: + while self.is_ffmpeg_running() and self.mumble.sound_output.get_buffer_size() > 0.5 and not self.exit: # If the buffer isn't empty, I cannot send new music part, so I wait self._loop_status = f'Wait for buffer {self.mumble.sound_output.get_buffer_size():.3f}' time.sleep(0.01) - if self.thread: + if self.is_ffmpeg_running(): # I get raw from ffmpeg thread # move playhead forward self._loop_status = 'Reading raw' @@ -480,7 +480,7 @@ class MumbleBot: else: time.sleep(0.1) - if not self.is_pause and (self.thread is None or self.thread.poll() is not None): + if not self.is_pause and not self.is_ffmpeg_running(): # bot is not paused, but ffmpeg thread has gone. # indicate that last song has finished, or the bot just resumed from pause, or something is wrong. if self.read_pcm_size < self.pcm_buffer_size and len(var.playlist) > 0 \ @@ -499,6 +499,7 @@ class MumbleBot: if not self.wait_for_ready: # if wait_for_ready flag is not true, move to the next song. if var.playlist.next(): current = var.playlist.current_item() + self.log.debug(f"bot: next into the song: {current.format_debug_string()}") try: self.validate_and_start_download(current) self.wait_for_ready = True @@ -581,6 +582,9 @@ class MumbleBot: # Play Control # ======================= + def is_ffmpeg_running(self): + return self.thread and self.thread.poll() is None + def play(self, index=-1, start_at=0): if not self.is_pause: self.interrupt() @@ -613,15 +617,14 @@ class MumbleBot: # Kill the ffmpeg thread if not self.on_killing.locked(): self.on_killing.acquire() - if self.thread: + if self.is_ffmpeg_running(): volume_set = self.volume_set self.volume_set = 0 - while self.volume > 0.01 and self.thread: # Waiting for volume_cycle to gradually tune volume to 0. + while self.volume > 0.01 and self.is_ffmpeg_running(): # Waiting for volume_cycle to gradually tune volume to 0. time.sleep(0.01) self.thread.kill() - self.thread = None self.volume_set = volume_set self.on_killing.release() From 762bb337b5ef0bd1bb6c602246d2b756dc9236d8 Mon Sep 17 00:00:00 2001 From: Terry Geng Date: Thu, 11 Jun 2020 10:40:19 +0800 Subject: [PATCH 5/5] refactor: optimized the non-glitchy interrupt logic. --- mumbleBot.py | 71 ++++++++++++++++++++++++++++++++-------------------- 1 file changed, 44 insertions(+), 27 deletions(-) diff --git a/mumbleBot.py b/mumbleBot.py index 12c5a1b..21361ed 100644 --- a/mumbleBot.py +++ b/mumbleBot.py @@ -18,6 +18,7 @@ import variables as var import logging import logging.handlers import traceback +import struct from packaging import version import util @@ -53,21 +54,27 @@ class MumbleBot: var.user = args.user var.is_proxified = var.config.getboolean( "webinterface", "is_web_proxified") + + # Flags to indicate the bot is exiting (Ctrl-C, or !kill) self.exit = False self.nb_exit = 0 + + # Related to ffmpeg thread self.thread = None self.thread_stderr = None - self.is_pause = False - self.pause_at_id = "" - self.playhead = -1 - self.song_start_at = -1 - self.last_ffmpeg_err = "" self.read_pcm_size = 0 self.pcm_buffer_size = 0 - # self.download_threads = [] + self.last_ffmpeg_err = "" + + # Play/pause status + self.is_pause = False + self.pause_at_id = "" + self.playhead = -1 # current position in a song. + self.song_start_at = -1 self.wait_for_ready = False # flag for the loop are waiting for download to complete in the other thread - self.on_killing = threading.Lock() # lock to acquire when killing ffmpeg thread is asked but ffmpeg is not - # killed yet + + # + self.on_interrupting = False if args.host: host = args.host @@ -356,9 +363,6 @@ class MumbleBot: # ======================= def launch_music(self, music_wrapper, start_from=0): - self.on_killing.acquire() - self.on_killing.release() - assert music_wrapper.is_ready() uri = music_wrapper.uri() @@ -473,8 +477,16 @@ class MumbleBot: if raw_music: # Adjust the volume and send it to mumble self.volume_cycle() - self.mumble.sound_output.add_sound( - audioop.mul(raw_music, 2, self.volume)) + + if not self.on_interrupting: + self.mumble.sound_output.add_sound( + audioop.mul(raw_music, 2, self.volume)) + else: + self.mumble.sound_output.add_sound( + audioop.mul(self._fadeout(raw_music, self.stereo), 2, self.volume)) + self.thread.kill() + time.sleep(0.1) + self.on_interrupting = False else: time.sleep(0.1) else: @@ -555,8 +567,6 @@ class MumbleBot: if delta > 0.001: if self.is_ducking and self.on_ducking: self.volume = (self.volume - self.ducking_volume) * math.exp(- delta / 0.2) + self.ducking_volume - elif self.on_killing.locked(): - self.volume = self.volume_set - (self.volume_set - self.volume) * math.exp(- delta / 0.05) else: self.volume = self.volume_set - (self.volume_set - self.volume) * math.exp(- delta / 0.5) @@ -578,6 +588,23 @@ class MumbleBot: self.on_ducking = True self.ducking_release = time.time() + 1 # ducking release after 1s + def _fadeout(self, _pcm_data, stereo=False): + pcm_data = bytearray(_pcm_data) + if stereo: + mask = [math.exp(-x/60) for x in range(0, int(len(pcm_data) / 4))] + for i in range(int(len(pcm_data) / 4)): + pcm_data[4 * i:4 * i + 2] = struct.pack(" 0.01 and self.is_ffmpeg_running(): # Waiting for volume_cycle to gradually tune volume to 0. - time.sleep(0.01) - - self.thread.kill() - self.volume_set = volume_set - self.on_killing.release() + if self.is_ffmpeg_running(): + self.on_interrupting = True self.song_start_at = -1 self.read_pcm_size = 0