class _Operation(object): def __init__(self): self.danmu = Danmu() self.log = Log('Danmu Service') self.downloader = NeteaseMusic() def order_song(self, danmu): """点歌台""" # 如果命令全为数字,跳转到id点歌 if danmu['command'].isdigit(): song = self._order_song_id(danmu) # 否则按照歌名点歌 else: song = self._order_song_name(danmu) if song: self.danmu.send('%s 点歌成功' % song['name']) DownloadQueue.put({ 'type': 'music', 'id': song['id'], 'name': song['name'], 'singer': song['ar'][0]['name'], 'username': danmu['name'] }) else: self.danmu.send('找不到%s' % danmu['command']) self.log.info('找不到%s' % danmu['command']) def _order_song_name(self, danmu): """通过歌名点歌""" self.log.info('%s 点歌 [%s]' % (danmu['name'], danmu['command'])) detail = danmu['command'].split('-') if len(detail) == 1: # 按歌曲名点歌 song = self.downloader.search_single(danmu['command'].strip()) elif len(detail) == 2: # 按歌曲名-歌手点歌 song = self.downloader.search_single(detail[0].strip(), detail[1].strip()) else: # 无效命令 song = {} return song def _order_song_id(self, danmu): """通过id点歌""" self.log.info('%s id点歌 [%s]' % (danmu['name'], danmu['command'])) song = self.downloader.get_info(danmu['command'].strip()) return song
class DownloadService(Service): def __init__(self): self.danmu = Danmu() self.log = Log('Download Service') self.musicDownloader = NeteaseMusic() # 获取下载队列 分发至下载函数 def run(self): try: # 判断队列是否为空 if DownloadQueue.empty(): return # 获取新的下载任务 task = DownloadQueue.get() if task and 'type' in task: if task['type'] == 'music': self.musicDownload(task) elif task['type'] == 'vedio': pass except Exception as e: self.log.error(e) pass def musicDownload(self, song): # 搜索歌曲并下载 self.danmu.send('正在下载%s' % song['name']) filename = self.musicDownloader.download(song['id']) if filename: self.log.info('歌曲下载完毕 %s - %s' % (song['name'], song['singer'])) # 加入播放队列 PlayQueue.put({ 'type': 'music', 'filename': filename, 'name': song['name'], 'singer': song['singer'], 'username': song['username'] }) else: pass
class DanmuService(Service): def __init__(self): self.danmu = Danmu() self.musicDownloader = NeteaseMusic() self.log = Log('Danmu Service') self.commandMap = { '点歌': 'selectSongAction', 'id': 'selectSongByIdAction' } pass def run(self): try: self.parseDanmu() time.sleep(1.5) except Exception as e: self.log.error(e) # 解析弹幕 def parseDanmu(self): danmuList = self.danmu.get() if danmuList: for danmu in danmuList: self.log.debug('%s: %s' % (danmu['name'], danmu['text'])) self.danmuStateMachine(danmu) # 将对应的指令映射到对应的Action上 def danmuStateMachine(self, danmu): text = danmu['text'] commandAction = '' for key in self.commandMap: # 遍历查询comand是否存在 若存在则反射到对应的Action if text.find(key) == 0 and hasattr(self, self.commandMap[key]): danmu['command'] = danmu['text'][len(key) : len(danmu['text'])] getattr(self, self.commandMap[key])(danmu) break pass # 歌曲名点歌 def selectSongAction(self, danmu): self.log.info('%s 点歌 [%s]' % (danmu['name'], danmu['command'])) command = danmu['command'] song = [] # 按歌曲名-歌手点歌 if command.find('-') != -1: detail = command.split('-') if len(detail) == 2: song = self.musicDownloader.searchSingle(detail[0], detail[1]) else: # 查询失败 song = {} pass # 直接按歌曲名点歌 else: song = self.musicDownloader.searchSingle(danmu['command']) if song: self.danmu.send('%s点歌成功' % song['name']) DownloadQueue.put({ 'type': 'music', 'id': song['id'], 'name': song['name'], 'singer': song['singer'], 'username': danmu['name'] }) else: # 未找到歌曲 self.danmu.send('找不到%s' % danmu['command']) self.log.info('找不到%s' % danmu['command']) pass # 通过Id点歌 def selectSongByIdAction(self, danmu): command = danmu['command'] try: song = self.musicDownloader.getInfo(command) if song: self.danmu.send('%s点歌成功' % song['name']) DownloadQueue.put({ 'type': 'music', 'id': song['id'], 'name': song['name'], 'singer': song['singer'], 'username': danmu['name'] }) else: # 未找到歌曲 raise Exception('未找到歌曲') except Exception as e: self.danmu.send('找不到%s' % danmu['command']) self.log.info('找不到%s' % danmu['command'])
class MediaService(Service): def __init__(self): self.danmu = Danmu() self.log = Log('Media Service') self.config = Config() def run(self): try: # 判断队列是否为空 if PlayQueue.empty(): # 获取随机文件,播放 musicPath = './resource/music/' randomMusic = self.getRandomFile(musicPath) musicName = os.path.basename(randomMusic) musicName = musicName.replace(os.path.splitext(randomMusic)[1], '') self.playMusic({ 'username': '******', 'name': musicName, 'filename': musicPath + randomMusic }, True) return # 获取新的下载任务 task = PlayQueue.get() if task and 'type' in task: if task['type'] == 'music': self.playMusic(task) elif task['type'] == 'vedio': pass except Exception as e: self.log.error(e) # 播放音乐 def playMusic(self, music, autoPlay=False): imagePath = './resource/img/' randomImage = imagePath + self.getRandomFile(imagePath) self.log.info('[Music] 开始播放[%s]点播的[%s]' % (music['username'], music['name'])) self.danmu.send('正在播放 %s' % music['name']) # 获取歌词 assPath = './resource/lrc/default.ass' if 'lrc' in music: assPath = music['lrc'] # 开始播放 command = ffmpeg().getMusic(music=music['filename'], output=self.getRTMPUrl(), image=randomImage, ass=assPath) command = "%s 2>> ./log/ffmpeg.log" % command self.log.debug(command) process = subprocess.Popen(args=command, cwd=os.getcwd(), shell=True) process.wait() # 播放完毕 if not autoPlay: os.remove(path=music['filename']) self.log.info('[Music] [%s]播放结束' % music['name']) # 获取推流地址 def getRTMPUrl(self): url = self.config.get(module='rtmp', key='url') code = self.config.get(module='rtmp', key='code') return url + code # 获取随机文件 def getRandomFile(self, path): fileList = os.listdir(path) if len(fileList) == 0: raise Exception('无法获取随机文件,%s为空' % path) index = random.randint(0, len(fileList) - 1) return fileList[index]
class MediaService(Service): def __init__(self): self.danmu = Danmu() self.log = Log('Media Service') self.config = Config() self.ass = AssMaker() def run(self): try: # 判断队列是否为空 if PlayQueue.empty(): time.sleep(3) # 获取随机文件,播放 musicPath = './resource/music/' musicName = self.getRandomFile(musicPath, '.mp3') # musicName = os.path.basename(musicName) musicName = os.path.splitext(musicName)[0] task = {} # 存在详情文件 if os.path.isfile('%s%s.mp3.json' % (musicPath, musicName)): f = open('%s%s.mp3.json' % (musicPath, musicName), 'rt') task = json.loads(f.read()) f.close() else: pass self.playMusic(task) else: # 获取新的下载任务 task = PlayQueue.get() if task and 'type' in task: if task['type'] == 'id': self.playMusic(task) elif task['type'] == 'mv': self.playVedio(task) pass except Exception as e: self.log.error(e) # 播放音乐 def playMusic(self, music): self.log.info('[Music] 开始播放[%s]点播的[%s]' % (music['username'], music['info']['name'])) self.danmu.send('正在播放%s' % music['info']['name']) # 生成背景字幕 self.ass.make_ass(music, './resource/bak.ass') # 处理图片 imagePath = './resource/img/' randomImage = imagePath + self.getRandomFile(imagePath) command = ffmpeg().getImage(image=randomImage, output='./resource/bak.jpg', ass='./resource/bak.ass') command = "%s 2>> ./log/ffmpeg_img.log" % command self.log.debug(command) process = subprocess.Popen(args=command, cwd=os.getcwd(), shell=True) process.wait() # 获取歌词 assPath = '' if 'lrc' in music['info']: assPath = './resource/music/%s.mp3.ass' % music['info']['id'] # 开始播放 mp3Path = './resource/music/%s.mp3' % music['info']['id'] command = ffmpeg().getMusic(music=mp3Path, output=self.getRTMPUrl(), image='./resource/bak.jpg', ass=assPath) command = "%s 2>> ./log/ffmpeg.log" % command self.log.debug(command) process = subprocess.Popen(args=command, cwd=os.getcwd(), shell=True) process.wait() self.log.info('[Music] [%s]播放结束' % music['info']['name']) # 播放视频 def playVedio(self, music): self.log.info('[Music] 开始播放[%s]点播的[%s]' % (music['username'], music['info']['name'])) self.danmu.send('正在播放%s' % music['info']['name']) # 开始播放 vedioPath = './resource/video/%s_mv.flv' % music['info']['id'] command = ffmpeg().getVedio(vedio=vedioPath, output=self.getRTMPUrl()) command = "%s 2>> ./log/ffmpeg.log" % command self.log.debug(command) process = subprocess.Popen(args=command, cwd=os.getcwd(), shell=True) process.wait() self.log.info('[Music] [%s]播放结束' % music['info']['name']) # 获取推流地址 def getRTMPUrl(self): url = self.config.get(module='rtmp', key='url') code = self.config.get(module='rtmp', key='code') return url + code # 获取随机文件 def getRandomFile(self, path, type=None): fileList = [] if type: for filename in os.listdir(path): if os.path.splitext(filename)[1] == type: fileList.append(filename) else: fileList = os.listdir(path) if len(fileList) == 0: raise Exception('无法获取随机文件,%s为空' % path) index = random.randint(0, len(fileList) - 1) return fileList[index]
class DownloadService(Service): def __init__(self): self.danmu = Danmu() self.log = Log('Download Service') self.musicDownloader = NeteaseMusic() self.kuwoDownloader = KuwoDownloader() # 获取下载队列 分发至下载函数 def run(self): try: # 判断队列是否为空 if DownloadQueue.empty(): return # 获取新的下载任务 task = DownloadQueue.get() if task and 'type' in task: if task['type'] == 'music': self.musicDownload(task) elif task['type'] == 'vedio': pass #酷我音乐 elif task['type'] == 'kuwo': self.kuwoMusicDownload(task) except Exception as e: self.log.error(e) traceback.print_exc() pass def musicDownload(self, song): # 搜索歌曲并下载 #发送弹幕 self.danmu.send('正在下载%s' % song['name']) filename = self.musicDownloader.download(song['id']) headers = { 'authority': 'music.163.com', 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.135 Safari/537.36', 'content-type': 'application/x-www-form-urlencoded', 'accept': '*/*', 'origin': 'https://music.163.com', 'sec-fetch-site': 'same-origin', 'sec-fetch-mode': 'cors', 'sec-fetch-dest': 'empty', 'referer': 'https://music.163.com/search/', 'accept-language': 'zh-CN,zh;q=0.9', } url = 'http://music.163.com/api/song/lyric?' + 'id=' + str( song['id']) + '&lv=1&kv=1&tv=-1' resp = requests.get(url, headers=headers) resjson = json.loads(resp.text) #左下角info信息 info = '当前网易云id:' + str( song['id'] ) + "\\N当前播放的歌曲:" + song['name'] + "\\N点播人:" + song['username'] #生成ass文件 lrc = "" tlrc = "" #如果是纯音乐不传歌词 if not "nolyric" in resjson: lrc = resjson["lrc"]["lyric"] tlrc = resjson["tlyric"]["lyric"] else: lrc = '[00:00.000] 纯音乐,请您欣赏\n[10:00.000] ' self.musicDownloader.make_ass(song['name'], info, lrc_to_ass(lrc), tlrc_to_ass(tlrc)) if filename: self.log.info('歌曲下载完毕 %s - %s' % (song['name'], song['singer'])) # 加入播放队列 PlayQueue.put({ 'type': 'music', 'filename': filename, 'name': song['name'], 'singer': song['singer'], 'username': song['username'], 'lrc': './resource/lrc/' + song['name'] + '.ass' }) else: pass def kuwoMusicDownload(self, song): # 搜索歌曲并下载 #发送弹幕 self.danmu.send('正在下载%s' % song['name']) filename = self.kuwoDownloader.download(musicrid=song['id']) self.log.info("下载成功,文件 " + filename) #左下角info信息 info = '当前酷我歌曲id:' + str( song['id'] ) + "\\N当前播放的歌曲:" + song['name'] + "\\N点播人:" + song['username'] #生成ass文件 lrc = self.kuwoDownloader.getLyric(song['id']) tlrc = "" self.musicDownloader.make_ass(song['name'], info, lrc_to_ass(lrc), tlrc_to_ass(tlrc)) if filename: self.log.info('歌曲下载完毕 %s - %s' % (song['name'], song['singer'])) # 加入播放队列 PlayQueue.put({ 'type': 'music', 'filename': filename, 'name': song['name'], 'singer': song['singer'], 'username': song['username'], 'lrc': './resource/lrc/' + song['name'] + '.ass' }) else: pass
class DownloadService(Service): def __init__(self): self.danmu = Danmu() self.ass = AssMaker() self.log = Log('Download Service') self.neteaseMusic = NeteaseMusic() # 获取下载队列 分发至下载函数 def run(self): try: # 判断队列是否为空 if DownloadQueue.empty(): return # 获取新的下载任务 task = DownloadQueue.get() if task and 'type' in task: self.download(task) PlayQueue.put(task) self.log.info('播放列表+1') except Exception as e: self.log.error(e) # 下载媒体 def download(self, info, filename=None, callback=None): self.danmu.send('正在下载%s' % info['info']['name']) # 名称处理 if not filename: filename = info['info']['id'] if info['type'] == 'id': songId = info['info']['id'] # 本地不存在此歌曲 if (str(songId) + '.mp3') not in os.listdir('./resource/music/'): # 下载歌曲 musicUrl = self.neteaseMusic.getSingleUrl(songId) mp3_filename = './resource/music/%s.mp3' % filename Request.download(musicUrl, mp3_filename, callback) # 获取歌词文件 lyric = self.neteaseMusic.getLyric(songId) if lyric: ass_filename = '%s.ass' % mp3_filename self.ass.make_lrc_ass(ass_filename, lyric['lyric'], lyric['tlyric']) info['info']['lrc'] = True else: info['info']['lrc'] = False # 保存点歌信息 file = open('%s.json' % mp3_filename, 'w') file.write(json.dumps(info, ensure_ascii=False)) file.close() self.log.info('歌曲下载完毕 %s - %s' % (info['info']['name'], info['info']['singer'])) else: # 更新点歌信息 pass return filename elif info['type'] == 'mv': mvId = info['info']['id'] # 本地不存在此mv if (str(mvId) + '_mv.flv') not in os.listdir('./resource/video/'): # 下载mv mvUrl = '' if '720' in info['info']['brs']: mvUrl = info['info']['brs']['720'] elif '480' in info['info']['brs']: mvUrl = info['info']['brs']['480'] else: return None downPath = './resource/video/%s_mv.down' % filename Request.download(mvUrl, downPath, callback) self.log.info('MV下载完毕 %s - %s' % (info['info']['name'], info['info']['singer'])) # 生成背景字幕 self.ass.make_ass(info, './resource/video/bak.ass') # 渲染MV renderPath = './resource/video/%s_mv.render' % filename command = ffmpeg().renderVedio(vedio=downPath, output=renderPath, ass='./resource/video/bak.ass') command = "%s 2>> ./log/ffmpeg.log" % command self.log.debug(command) process = subprocess.Popen(args=command, cwd=os.getcwd(), shell=True) process.wait() # 渲染完成,修改文件名 os.remove(downPath) mp4Path = './resource/video/%s_mv.flv' % filename os.rename(renderPath, mp4Path) # 保存点歌信息 file = open('%s.json' % mp4Path, 'w') file.write(json.dumps(info, ensure_ascii=False)) file.close() self.log.info('MV渲染完毕 %s - %s' % (info['info']['name'], info['info']['singer'])) else: # 更新点歌信息 pass return filename
class DanmuService(Service): def __init__(self): self.danmu = Danmu() self.config = Config() self.neteaseMusic = NeteaseMusic() self.log = Log('Danmu Service') self.commandMap = { '点歌=': 'selectSongAction', 'id=': 'selectSongByIdAction', 'mv=': 'selectMvByIdAction', '切歌': 'DebugAction' } pass def run(self): try: self.parseDanmu() time.sleep(1.5) except Exception as e: self.log.error(e) # 解析弹幕 def parseDanmu(self): danmuList = self.danmu.get() if danmuList: for danmu in danmuList: self.log.debug('%s: %s' % (danmu['name'], danmu['text'])) if danmu['name'] != self.config.get('miaoUser'): # 不响应弹幕姬的弹幕 danmu['text'] = danmu['text'].replace(' ', '') # 删除空格防和谐 self.danmuStateMachine(danmu) pass # 将对应的指令映射到对应的Action上 def danmuStateMachine(self, danmu): text = danmu['text'] commandAction = '' for key in self.commandMap: # 遍历查询comand是否存在 若存在则反射到对应的Action if text.find(key) == 0 and hasattr(self, self.commandMap[key]): danmu['command'] = danmu['text'][len(key):len(danmu['text'])] getattr(self, self.commandMap[key])(danmu) break pass # 歌曲名点歌 def selectSongAction(self, danmu): self.log.info('%s 点歌 [%s]' % (danmu['name'], danmu['command'])) command = danmu['command'] song = [] # 按歌曲名-歌手点歌 if command.find('-') != -1: detail = command.split('-') if len(detail) == 2: song = self.neteaseMusic.searchSingle(detail[0], detail[1]) else: # 查询失败 song = {} # 直接按歌曲名点歌 else: song = self.neteaseMusic.searchSingle(danmu['command']) if song: self.danmu.send('%s点歌成功' % song['name']) DownloadQueue.put({ 'type': 'id', 'info': song, 'username': danmu['name'], 'time': danmu['time'] }) else: # 未找到歌曲 self.danmu.send('找不到%s' % danmu['command']) self.log.info('找不到%s' % danmu['command']) # 通过Id点歌 def selectSongByIdAction(self, danmu): self.log.info('%s ID [%s]' % (danmu['name'], danmu['command'])) command = danmu['command'] try: song = self.neteaseMusic.getInfo(command) if song: self.danmu.send('%s点歌成功' % song['name']) DownloadQueue.put({ 'type': 'id', 'info': song, 'username': danmu['name'], 'time': danmu['time'] }) else: # 未找到歌曲 raise Exception('未找到歌曲') except Exception as e: self.danmu.send('找不到%s' % danmu['command']) self.log.info('找不到%s' % danmu['command']) # 通过Id点Mv def selectMvByIdAction(self, danmu): self.log.info('%s MV [%s]' % (danmu['name'], danmu['command'])) command = danmu['command'] try: mv = self.neteaseMusic.getMv(command) if mv: self.danmu.send('%s点播成功' % mv['name']) DownloadQueue.put({ 'type': 'mv', 'info': mv, 'username': danmu['name'], 'time': danmu['time'] }) else: # 未找到歌曲 raise Exception('未找到MV') except Exception as e: self.danmu.send('找不到%s' % danmu['command']) self.log.info('找不到%s' % danmu['command']) def DebugAction(self, danmu): if danmu['name'] in self.config.get('adminUser'): if danmu['text'] == '切歌': os.system( "kill `ps a|grep 'ffmpeg -re'|grep -v 'sh'|grep -v 'grep'|awk '{print $1}'`" ) self.danmu.send('切歌成功')