def getLyric(): if 'lyric' not in self.songs[str(self.playing_id)].keys(): self.songs[str(self.playing_id)]["lyric"] = [] if len(self.songs[str(self.playing_id)]["lyric"]) > 0: return netease = NetEase() lyric = netease.song_lyric(self.playing_id) if (not lyric == []) or lyric == '未找到歌词': lyric = lyric.split('\n') self.songs[str(self.playing_id)]["lyric"] = lyric return
def getLyric(): if "lyric" not in self.songs[str(self.playing_id)].keys(): self.songs[str(self.playing_id)]["lyric"] = [] if len(self.songs[str(self.playing_id)]["lyric"]) > 0: return netease = NetEase() lyric = netease.song_lyric(self.playing_id) if lyric == [] or lyric == "未找到歌词": return lyric = lyric.split("\n") self.songs[str(self.playing_id)]["lyric"] = lyric return
class Player(object): MODE_ORDERED = 0 MODE_ORDERED_LOOP = 1 MODE_SINGLE_LOOP = 2 MODE_RANDOM = 3 MODE_RANDOM_LOOP = 4 def __init__(self): self.config = Config() # self.ui = Ui() self.popen_handler = None # flag stop, prevent thread start self.playing_flag = False self.refrese_url_flag = False self.process_length = 0 self.process_location = 0 self.storage = Storage() self.cache = Cache() self.end_callback = None self.playing_song_changed_callback = None self.api = NetEase() @property def info(self): return self.storage.database["player_info"] @property def songs(self): return self.storage.database["songs"] @property def index(self): return self.info["idx"] @property def list(self): return self.info["player_list"] @property def order(self): return self.info["playing_order"] @property def mode(self): return self.info["playing_mode"] @property def is_ordered_mode(self): return self.mode == Player.MODE_ORDERED @property def is_ordered_loop_mode(self): return self.mode == Player.MODE_ORDERED_LOOP @property def is_single_loop_mode(self): return self.mode == Player.MODE_SINGLE_LOOP @property def is_random_mode(self): return self.mode == Player.MODE_RANDOM @property def is_random_loop_mode(self): return self.mode == Player.MODE_RANDOM_LOOP @property def config_notifier(self): return self.config.get("notifier") @property def config_mpg123(self): return self.config.get("mpg123_parameters") @property def current_song(self): if not self.songs: return {} if not self.is_index_valid: return {} song_id = self.list[self.index] return self.songs.get(song_id, {}) @property def playing_id(self): return self.current_song["song_id"] @property def playing_name(self): return self.current_song["song_name"] @property def is_empty(self): return len(self.list) == 0 @property def is_index_valid(self): return 0 <= self.index < len(self.list) def notify_playing(self): if not self.current_song: return if not self.config_notifier: return song = self.current_song notify("正在播放: {}\n{}-{}".format(song["song_name"], song["artist"], song["album_name"])) def notify_copyright_issue(self): log.warning("Song {} is unavailable due to copyright issue.".format( self.playing_id)) notify("版权限制,无法播放此歌曲") def change_mode(self, step=1): self.info["playing_mode"] = (self.info["playing_mode"] + step) % 5 def build_playinfo(self): if not self.current_song: return # self.ui.build_playinfo( # self.current_song["song_name"], # self.current_song["artist"], # self.current_song["album_name"], # self.current_song["quality"], # time.time(), # pause=not self.playing_flag, # ) def add_songs(self, songs): for song in songs: song_id = str(song["song_id"]) self.info["player_list"].append(song_id) if song_id in self.songs: self.songs[song_id].update(song) else: self.songs[song_id] = song def refresh_urls(self): songs = self.api.dig_info(self.list, "refresh_urls") if songs: for song in songs: song_id = str(song["song_id"]) if song_id in self.songs: self.songs[song_id]["mp3_url"] = song["mp3_url"] self.songs[song_id]["expires"] = song["expires"] self.songs[song_id]["get_time"] = song["get_time"] else: self.songs[song_id] = song self.refrese_url_flag = True def stop(self): if not self.popen_handler: return self.playing_flag = False self.popen_handler.stdin.write(b"Q\n") self.popen_handler.stdin.flush() self.popen_handler.kill() self.popen_handler = None # wait process to be killed time.sleep(0.01) def tune_volume(self, up=0): if not self.popen_handler: return new_volume = self.info["playing_volume"] + up if new_volume > 100: new_volume = 100 elif new_volume < 0: new_volume = 0 self.info["playing_volume"] = new_volume self.popen_handler.stdin.write("V {}\n".format( self.info["playing_volume"]).encode()) self.popen_handler.stdin.flush() def switch(self): if not self.popen_handler: return self.playing_flag = not self.playing_flag self.popen_handler.stdin.write(b"P\n") self.popen_handler.stdin.flush() self.build_playinfo() def run_mpg123(self, on_exit, url, expires=-1, get_time=-1): para = ["mpg123", "-R"] + self.config_mpg123 self.popen_handler = subprocess.Popen(para, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) self.tune_volume() self.popen_handler.stdin.write(b"L " + url.encode("utf-8") + b"\n") self.popen_handler.stdin.flush() endless_loop_cnt = 0 while True: if not self.popen_handler: break strout = self.popen_handler.stdout.readline().decode( "utf-8").strip() if strout[:2] == "@F": # playing, update progress out = strout.split(" ") self.process_location = int(float(out[3])) self.process_length = int(float(out[3]) + float(out[4])) elif strout[:2] == "@E": self.playing_flag = True if (expires >= 0 and get_time >= 0 and time.time() - expires - get_time >= 0): # 刷新URL self.refresh_urls() else: # error, stop song and move to next self.notify_copyright_issue() break elif strout == "@P 0": # end, moving to next self.playing_flag = True break elif strout == "": endless_loop_cnt += 1 # 有播放后没有退出,mpg123一直在发送空消息的情况,此处直接终止处理 if endless_loop_cnt > 100: log.warning( "mpg123 error, halt, endless loop and high cpu use, then we kill it" ) break if self.playing_flag: if self.refrese_url_flag: self.stop() self.replay() self.refrese_url_flag = False else: self.next() else: self.stop() def download_lyric(self, is_transalted=False): key = "lyric" if not is_transalted else "tlyric" if key not in self.songs[str(self.playing_id)]: self.songs[str(self.playing_id)][key] = [] if len(self.songs[str(self.playing_id)][key]) > 0: return if not is_transalted: lyric = self.api.song_lyric(self.playing_id) else: lyric = self.api.song_tlyric(self.playing_id) self.songs[str(self.playing_id)][key] = lyric def download_song(self, song_id, song_name, artist, url): def write_path(song_id, path): self.songs[str(song_id)]["cache"] = path self.cache.add(song_id, song_name, artist, url, write_path) self.cache.start_download() def start_playing(self, on_exit, args): """ Runs the given args in subprocess.Popen, and then calls the function on_exit when the subprocess completes. on_exit is a callable object, and args is a lists/tuple of args that would give to subprocess.Popen. """ # log.debug("%s,%s,%s" % (args['song_id'], args['song_name'], args['mp3_url'])) if "cache" in args.keys() and os.path.isfile(args["cache"]): thread = threading.Thread(target=self.run_mpg123, args=(on_exit, args["cache"])) else: new_url = NetEase().songs_url([args["song_id"]])[0]["url"] #使用新地址 if not new_url: #如果没有获得新地址 new_url = args["mp3_url"] #使用老地址传给mpg123 thread = threading.Thread( target=self.run_mpg123, args=(on_exit, new_url, args["expires"], args["get_time"]), ) cache_thread = threading.Thread( target=self.download_song, args=( args["song_id"], args["song_name"], args["artist"], args["mp3_url"], ), ) cache_thread.start() thread.start() lyric_download_thread = threading.Thread(target=self.download_lyric) lyric_download_thread.start() tlyric_download_thread = threading.Thread(target=self.download_lyric, args=(True, )) tlyric_download_thread.start() # returns immediately after the thread starts return thread def replay(self): if not self.is_index_valid: self.stop() if self.end_callback: log.debug("Callback") self.end_callback() return if not self.current_song: return self.stop() self.playing_flag = True self.build_playinfo() self.notify_playing() self.start_playing(lambda: 0, self.current_song) def shuffle_order(self): del self.order[:] self.order.extend(list(range(0, len(self.list)))) random.shuffle(self.order) self.info["random_index"] = 0 def new_player_list(self, type, title, datalist, offset): self.info["player_list_type"] = type self.info["player_list_title"] = title # self.info['idx'] = offset self.info["player_list"] = [] self.info["playing_order"] = [] self.info["random_index"] = 0 self.songs.clear() self.add_songs(datalist) def append_songs(self, datalist): self.add_songs(datalist) # switch_flag为true表示: # 在播放列表中 || 当前所在列表类型不在"songs"、"djchannels"、"fmsongs"中 def play_or_pause(self, idx, switch_flag): if self.is_empty: return # print('flag:',switch_flag) # if same "list index" and "playing index" --> same song :: pause/resume it if self.index == idx and switch_flag: if not self.popen_handler: # print('aaaaaa') self.stop() self.replay() else: # print('bbbbbb') self.switch() else: # print('cccccccc') self.info["idx"] = idx self.stop() self.replay() def _swap_song(self): now_songs = self.order.index(self.index) self.order[0], self.order[now_songs] = self.order[ now_songs], self.order[0] def _need_to_shuffle(self): playing_order = self.order random_index = self.info["random_index"] if (random_index >= len(playing_order) or playing_order[random_index] != self.index): return True else: return False def next_idx(self): if not self.is_index_valid: return self.stop() playlist_len = len(self.list) if self.mode == Player.MODE_ORDERED: # make sure self.index will not over if self.info["idx"] < playlist_len: self.info["idx"] += 1 elif self.mode == Player.MODE_ORDERED_LOOP: self.info["idx"] = (self.index + 1) % playlist_len elif self.mode == Player.MODE_SINGLE_LOOP: self.info["idx"] = self.info["idx"] else: playing_order_len = len(self.order) if self._need_to_shuffle(): self.shuffle_order() # When you regenerate playing list # you should keep previous song same. self._swap_song() playing_order_len = len(self.order) self.info["random_index"] += 1 # Out of border if self.mode == Player.MODE_RANDOM_LOOP: self.info["random_index"] %= playing_order_len # Random but not loop, out of border, stop playing. if self.info["random_index"] >= playing_order_len: self.info["idx"] = playlist_len else: self.info["idx"] = self.order[self.info["random_index"]] if self.playing_song_changed_callback is not None: self.playing_song_changed_callback() def next(self): self.stop() self.next_idx() self.replay() def prev_idx(self): if not self.is_index_valid: self.stop() return playlist_len = len(self.list) if self.mode == Player.MODE_ORDERED: if self.info["idx"] > 0: self.info["idx"] -= 1 elif self.mode == Player.MODE_ORDERED_LOOP: self.info["idx"] = (self.info["idx"] - 1) % playlist_len elif self.mode == Player.MODE_SINGLE_LOOP: self.info["idx"] = self.info["idx"] else: playing_order_len = len(self.order) if self._need_to_shuffle(): self.shuffle_order() playing_order_len = len(self.order) self.info["random_index"] -= 1 if self.info["random_index"] < 0: if self.mode == Player.MODE_RANDOM: self.info["random_index"] = 0 else: self.info["random_index"] %= playing_order_len self.info["idx"] = self.order[self.info["random_index"]] if self.playing_song_changed_callback is not None: self.playing_song_changed_callback() def prev(self): self.stop() self.prev_idx() self.replay() def shuffle(self): self.stop() self.info["playing_mode"] = Player.MODE_RANDOM self.shuffle_order() self.info["idx"] = self.info["playing_order"][ self.info["random_index"]] self.replay() def volume_up(self): self.tune_volume(5) def volume_down(self): self.tune_volume(-5) def update_size(self): self.ui.update_size() self.build_playinfo() def cache_song(self, song_id, song_name, artist, song_url): def on_exit(song_id, path): self.songs[str(song_id)]["cache"] = path self.cache.enable = False self.cache.enable = True self.cache.add(song_id, song_name, artist, song_url, on_exit) self.cache.start_download()
def on_get(self, req, resp): song_id = req.get_param('id', True) ne = NetEase() resp.body = json.dumps(ne.song_lyric(song_id))