Merge branch 'master' into volume
This commit is contained in:
commit
fb38b43f93
78
mumbleBot.py
78
mumbleBot.py
@ -18,6 +18,7 @@ import variables as var
|
|||||||
import logging
|
import logging
|
||||||
import logging.handlers
|
import logging.handlers
|
||||||
import traceback
|
import traceback
|
||||||
|
import struct
|
||||||
from packaging import version
|
from packaging import version
|
||||||
|
|
||||||
import util
|
import util
|
||||||
@ -49,21 +50,27 @@ class MumbleBot:
|
|||||||
var.user = args.user
|
var.user = args.user
|
||||||
var.is_proxified = var.config.getboolean(
|
var.is_proxified = var.config.getboolean(
|
||||||
"webinterface", "is_web_proxified")
|
"webinterface", "is_web_proxified")
|
||||||
|
|
||||||
|
# Flags to indicate the bot is exiting (Ctrl-C, or !kill)
|
||||||
self.exit = False
|
self.exit = False
|
||||||
self.nb_exit = 0
|
self.nb_exit = 0
|
||||||
|
|
||||||
|
# Related to ffmpeg thread
|
||||||
self.thread = None
|
self.thread = None
|
||||||
self.thread_stderr = 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.read_pcm_size = 0
|
||||||
self.pcm_buffer_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.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:
|
if args.host:
|
||||||
host = args.host
|
host = args.host
|
||||||
@ -364,9 +371,6 @@ class MumbleBot:
|
|||||||
# =======================
|
# =======================
|
||||||
|
|
||||||
def launch_music(self, music_wrapper, start_from=0):
|
def launch_music(self, music_wrapper, start_from=0):
|
||||||
self.on_killing.acquire()
|
|
||||||
self.on_killing.release()
|
|
||||||
|
|
||||||
assert music_wrapper.is_ready()
|
assert music_wrapper.is_ready()
|
||||||
|
|
||||||
uri = music_wrapper.uri()
|
uri = music_wrapper.uri()
|
||||||
@ -454,12 +458,12 @@ class MumbleBot:
|
|||||||
raw_music = ""
|
raw_music = ""
|
||||||
while not self.exit and self.mumble.is_alive():
|
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
|
# 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}'
|
self._loop_status = f'Wait for buffer {self.mumble.sound_output.get_buffer_size():.3f}'
|
||||||
time.sleep(0.01)
|
time.sleep(0.01)
|
||||||
|
|
||||||
if self.thread:
|
if self.is_ffmpeg_running():
|
||||||
# I get raw from ffmpeg thread
|
# I get raw from ffmpeg thread
|
||||||
# move playhead forward
|
# move playhead forward
|
||||||
self._loop_status = 'Reading raw'
|
self._loop_status = 'Reading raw'
|
||||||
@ -481,14 +485,22 @@ class MumbleBot:
|
|||||||
if raw_music:
|
if raw_music:
|
||||||
# Adjust the volume and send it to mumble
|
# Adjust the volume and send it to mumble
|
||||||
self.volume_cycle()
|
self.volume_cycle()
|
||||||
|
|
||||||
|
if not self.on_interrupting:
|
||||||
self.mumble.sound_output.add_sound(
|
self.mumble.sound_output.add_sound(
|
||||||
audioop.mul(raw_music, 2, self.volume_helper.real_volume))
|
audioop.mul(raw_music, 2, self.volume_helper.real_volume))
|
||||||
|
else:
|
||||||
|
self.mumble.sound_output.add_sound(
|
||||||
|
audioop.mul(self._fadeout(raw_music, self.stereo), 2, self.volume_helper.real_volume))
|
||||||
|
self.thread.kill()
|
||||||
|
time.sleep(0.1)
|
||||||
|
self.on_interrupting = False
|
||||||
else:
|
else:
|
||||||
time.sleep(0.1)
|
time.sleep(0.1)
|
||||||
else:
|
else:
|
||||||
time.sleep(0.1)
|
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.
|
# 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.
|
# 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 \
|
if self.read_pcm_size < self.pcm_buffer_size and len(var.playlist) > 0 \
|
||||||
@ -507,6 +519,7 @@ class MumbleBot:
|
|||||||
if not self.wait_for_ready: # if wait_for_ready flag is not true, move to the next song.
|
if not self.wait_for_ready: # if wait_for_ready flag is not true, move to the next song.
|
||||||
if var.playlist.next():
|
if var.playlist.next():
|
||||||
current = var.playlist.current_item()
|
current = var.playlist.current_item()
|
||||||
|
self.log.debug(f"bot: next into the song: {current.format_debug_string()}")
|
||||||
try:
|
try:
|
||||||
self.validate_and_start_download(current)
|
self.validate_and_start_download(current)
|
||||||
self.wait_for_ready = True
|
self.wait_for_ready = True
|
||||||
@ -586,10 +599,30 @@ class MumbleBot:
|
|||||||
self.on_ducking = True
|
self.on_ducking = True
|
||||||
self.ducking_release = time.time() + 1 # ducking release after 1s
|
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("<h",
|
||||||
|
round(struct.unpack("<h", pcm_data[4 * i:4 * i + 2])[0] * mask[i]))
|
||||||
|
pcm_data[4 * i + 2:4 * i + 4] = struct.pack("<h", round(
|
||||||
|
struct.unpack("<h", pcm_data[4 * i + 2:4 * i + 4])[0] * mask[i]))
|
||||||
|
else:
|
||||||
|
mask = [math.exp(-x/60) for x in range(0, int(len(pcm_data) / 2))]
|
||||||
|
for i in range(int(len(pcm_data) / 2)):
|
||||||
|
pcm_data[2 * i:2 * i + 2] = struct.pack("<h",
|
||||||
|
round(struct.unpack("<h", pcm_data[2 * i:2 * i + 2])[0] * mask[i]))
|
||||||
|
|
||||||
|
return bytes(pcm_data) + bytes(len(pcm_data))
|
||||||
|
|
||||||
# =======================
|
# =======================
|
||||||
# Play Control
|
# Play Control
|
||||||
# =======================
|
# =======================
|
||||||
|
|
||||||
|
def is_ffmpeg_running(self):
|
||||||
|
return self.thread and self.thread.poll() is None
|
||||||
|
|
||||||
def play(self, index=-1, start_at=0):
|
def play(self, index=-1, start_at=0):
|
||||||
if not self.is_pause:
|
if not self.is_pause:
|
||||||
self.interrupt()
|
self.interrupt()
|
||||||
@ -620,19 +653,8 @@ class MumbleBot:
|
|||||||
|
|
||||||
def interrupt(self):
|
def interrupt(self):
|
||||||
# Kill the ffmpeg thread
|
# Kill the ffmpeg thread
|
||||||
if not self.on_killing.locked():
|
if self.is_ffmpeg_running():
|
||||||
self.on_killing.acquire()
|
self.on_interrupting = True
|
||||||
if self.thread:
|
|
||||||
volume_set = self.volume_helper.plain_volume_set
|
|
||||||
self.volume_helper.plain_volume_set = 0
|
|
||||||
|
|
||||||
while self.volume_helper.real_volume > 0.01 and self.thread: # Waiting for volume_cycle to gradually tune volume to 0.
|
|
||||||
time.sleep(0.01)
|
|
||||||
|
|
||||||
self.thread.kill()
|
|
||||||
self.thread = None
|
|
||||||
self.volume_helper.plain_volume_set = volume_set
|
|
||||||
self.on_killing.release()
|
|
||||||
|
|
||||||
self.song_start_at = -1
|
self.song_start_at = -1
|
||||||
self.read_pcm_size = 0
|
self.read_pcm_size = 0
|
||||||
@ -787,7 +809,7 @@ if __name__ == '__main__':
|
|||||||
var.tmp_folder = util.solve_filepath(var.config.get('bot', 'tmp_folder'))
|
var.tmp_folder = util.solve_filepath(var.config.get('bot', 'tmp_folder'))
|
||||||
var.cache = MusicCache(var.music_db)
|
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()
|
var.cache.build_dir_cache()
|
||||||
|
|
||||||
# ======================
|
# ======================
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
opuslib==2.0.0
|
|
||||||
protobuf==3.4.0
|
|
||||||
flask
|
flask
|
||||||
youtube-dl
|
youtube-dl
|
||||||
python-magic
|
python-magic
|
||||||
|
@ -989,6 +989,7 @@ function playerSetIdle(){
|
|||||||
playerTitle.textContent = '-- IDLE --';
|
playerTitle.textContent = '-- IDLE --';
|
||||||
playerArtist.textContent = '';
|
playerArtist.textContent = '';
|
||||||
setProgressBar(playerBar, 0);
|
setProgressBar(playerBar, 0);
|
||||||
|
clearInterval(playhead_timer);
|
||||||
}
|
}
|
||||||
|
|
||||||
function updatePlayerInfo(item){
|
function updatePlayerInfo(item){
|
||||||
|
Loading…
x
Reference in New Issue
Block a user