def change_user(): #file = open('163.md') file = open('account.txt') try: alist = file.read() finally: file.close() account_list = alist.splitlines() account = random.choice(account_list) user = account.split(",")[0] passwd = md5(account.split(",")[1]) joker = NetEase() login_info = joker.login(user, passwd) print login_info return login_info
class User(object): def __init__(self): self.netease = NetEase() self.is_login = False self.uid = str() # '' def login(self, username, password): data = self.netease.login(username, password) code = data['code'] if code is 200: self.uid = data['profile']['userId'] self.is_login = True return True else: # 501 return False def get_favorite_playlist_id(self): """ login required success: return playlist id fail: return empty string '' """ if self.is_login: playlist = self.netease.user_playlist(self.uid) for each in playlist: if each['specialType'] is 5: # favorite playlist return each['id'] # the favorite playlist id return '' return '' def get_music_title_and_url(self, pid): """ :param pid: playlist id :return re: return list re """ playlist = self.netease.playlist_detail(pid) re = list() if playlist is not []: for music in playlist: tmp = dict() tmp['title'] = music['name'] tmp['url'] = music['mp3Url'] re.append(tmp) return re
class Menu: def __init__(self): reload(sys) sys.setdefaultencoding('UTF-8') self.config = Config() self.datatype = 'main' self.title = '网易云音乐' self.datalist = ['排行榜', '艺术家', '新碟上架', '精选歌单', '我的歌单', 'DJ节目', '每日推荐', '私人FM', '搜索', '帮助'] self.offset = 0 self.index = 0 self.storage = Storage() self.storage.load() self.collection = self.storage.database['collections'][0] self.player = Player() self.player.playing_song_changed_callback = self.song_changed_callback self.cache = Cache() self.ui = Ui() self.netease = NetEase() self.screen = curses.initscr() self.screen.keypad(1) self.step = 10 self.stack = [] self.djstack = [] self.userid = self.storage.database["user"]["user_id"] self.username = self.storage.database["user"]["nickname"] self.resume_play = True self.at_playing_list = False signal.signal(signal.SIGWINCH, self.change_term) signal.signal(signal.SIGINT, self.send_kill) self.START = time.time() def change_term(self, signum, frame): self.ui.screen.clear() self.ui.screen.refresh() def send_kill(self, signum, fram): self.player.stop() self.cache.quit() self.storage.save() curses.endwin() sys.exit() def update_alert(self, version): latest = Menu().check_version() if latest != version and latest != 0: if platform.system() == 'Darwin': os.system('/usr/bin/osascript -e \'display notification "MusicBox Update is available"sound name "/System/Library/Sounds/Ping.aiff"\'') time.sleep(0.5) os.system('/usr/bin/osascript -e \'display notification "NetEase-MusicBox installed version:' + version + '\nNetEase-MusicBox latest version:' + latest + '"\'') else: os.system('/usr/bin/notify-send "MusicBox Update is available"') def signin_alert(self, type): if type == 0: if platform.system() == 'Darwin': os.system('/usr/bin/osascript -e \'display notification "Mobile signin success"sound name "/System/Library/Sounds/Ping.aiff"\'') else: os.system('/usr/bin/notify-send "Mobile signin success"') else: if platform.system() == 'Darwin': os.system('/usr/bin/osascript -e \'display notification "PC signin success"sound name "/System/Library/Sounds/Ping.aiff"\'') else: os.system('/usr/bin/notify-send "PC signin success"') def check_version(self): # 检查更新 && 签到 try: mobilesignin = self.netease.daily_signin(0) if mobilesignin != -1 and mobilesignin['code'] != -2: self.signin_alert(0) time.sleep(0.5) pcsignin = self.netease.daily_signin(1) if pcsignin != -1 and pcsignin['code'] != -2: self.signin_alert(1) tree = ET.ElementTree(ET.fromstring(str(self.netease.get_version()))) root = tree.getroot() return root[0][4][0][0].text except: return 0 def start_fork(self, version): pid = os.fork() if pid == 0: Menu().update_alert(version) else: Menu().start() def play_pause(self): if len(self.storage.database["player_info"]["player_list"]) == 0: return if self.player.pause_flag: self.player.resume() else: self.player.pause() time.sleep(0.1) def next_song(self): if len(self.storage.database["player_info"]["player_list"]) == 0: return self.player.next() time.sleep(0.1) def previous_song(self): if len(self.storage.database["player_info"]["player_list"]) == 0: return self.player.prev() time.sleep(0.1) def start(self): self.START = time.time() // 1 self.ui.build_menu(self.datatype, self.title, self.datalist, self.offset, self.index, self.step, self.START) self.ui.build_process_bar(self.player.process_location, self.player.process_length, self.player.playing_flag, self.player.pause_flag, self.storage.database['player_info']['playing_mode']) self.stack.append([self.datatype, self.title, self.datalist, self.offset, self.index]) if bind_global: keybinder.bind(self.config.get_item("global_play_pause"), self.play_pause) keybinder.bind(self.config.get_item("global_next"), self.next_song) keybinder.bind(self.config.get_item("global_previous"), self.previous_song) while True: datatype = self.datatype title = self.title datalist = self.datalist offset = self.offset idx = index = self.index step = self.step stack = self.stack djstack = self.djstack self.screen.timeout(500) key = self.screen.getch() if bind_global: keybinder.gtk.main_iteration(False) self.ui.screen.refresh() # term resize if key == -1: self.ui.update_size() self.player.update_size() # 退出 if key == ord('q'): break # 退出并清除用户信息 if key == ord('w'): self.storage.database['user'] = { "username": "", "password": "", "user_id": "", "nickname": "", } try: os.remove(self.storage.cookie_path) except: break break # 上移 elif key == ord('k'): # turn page if at beginning if idx == offset: if offset == 0: continue self.offset -= step # 移动光标到最后一列 self.index = offset - 1 else: self.index = carousel(offset, min( len(datalist), offset + step) - 1, idx - 1) self.START = time.time() # 下移 elif key == ord('j'): # turn page if at end if idx == min( len(datalist), offset + step) - 1: if offset + step >= len( datalist ): continue self.offset += step # 移动光标到第一列 self.index = offset + step else: self.index = carousel(offset, min( len(datalist), offset + step) - 1, idx + 1) self.START = time.time() # 数字快捷键 elif ord('0') <= key <= ord('9'): if self.datatype == 'songs' or self.datatype == 'djchannels' or self.datatype == 'help': continue idx = key - ord('0') self.ui.build_menu(self.datatype, self.title, self.datalist, self.offset, idx, self.step, self.START) self.ui.build_loading() self.dispatch_enter(idx) self.index = 0 self.offset = 0 # 向上翻页 elif key == ord('u'): if offset == 0: continue self.START = time.time() self.offset -= step # e.g. 23 - 10 = 13 --> 10 self.index = (index - step) // step * step # 向下翻页 elif key == ord('d'): if offset + step >= len(datalist): continue self.START = time.time() self.offset += step # e.g. 23 + 10 = 33 --> 30 self.index = (index + step) // step * step # 前进 elif key == ord('l') or key == 10: if self.datatype == 'songs' or self.datatype == 'djchannels' or self.datatype == 'help' or len(self.datalist) <= 0: continue self.START = time.time() self.ui.build_loading() self.dispatch_enter(idx) self.index = 0 self.offset = 0 # 回退 elif key == ord('h'): # if not main menu if len(self.stack) == 1: continue self.START = time.time() up = stack.pop() self.datatype = up[0] self.title = up[1] self.datalist = up[2] self.offset = up[3] self.index = up[4] self.at_playing_list = False # 搜索 elif key == ord('f'): # 8 is the 'search' menu self.dispatch_enter(8) # 播放下一曲 elif key == ord(']'): self.next_song() # 播放上一曲 elif key == ord('['): self.previous_song() # 增加音量 elif key == ord('='): self.player.volume_up() # 减少音量 elif key == ord('-'): self.player.volume_down() # 随机播放 elif key == ord('?'): if len(self.storage.database["player_info"]["player_list"]) == 0: continue self.player.shuffle() time.sleep(0.1) # 喜爱 elif key == ord(','): return_data = self.request_api(self.netease.fm_like, self.player.get_playing_id()) if return_data != -1: if platform.system() == 'Darwin': os.system('/usr/bin/osascript -e \'display notification "Added successfully"\'') else: os.system('/usr/bin/notify-send "Added successfully"') # 删除FM elif key == ord('.'): if self.datatype == 'fmsongs': if len(self.storage.database["player_info"]["player_list"]) == 0: continue self.player.next() return_data = self.request_api(self.netease.fm_trash, self.player.get_playing_id()) if return_data != -1: if platform.system() == 'Darwin': os.system('/usr/bin/osascript -e \'display notification "Deleted successfully"\'') else: os.system('/usr/bin/notify-send "Deleted successfully"') time.sleep(0.1) # 下一FM elif key == ord('/'): if self.datatype == 'fmsongs': if len(self.storage.database["player_info"]["player_list"]) == 0: continue self.player.next() time.sleep(0.1) # 播放、暂停 elif key == ord(' '): # If not open a new playing list, just play and pause. try: if self.datalist[idx]['song_id'] == self.player.playing_id: self.player.play_and_pause(self.storage.database['player_info']['idx']) time.sleep(0.1) continue except: pass # If change to a new playing list. Add playing list and play. if datatype == 'songs': self.resume_play = False self.player.new_player_list('songs', self.title, self.datalist, -1) self.player.end_callback = None self.player.play_and_pause(idx) self.at_playing_list = True elif datatype == 'djchannels': self.resume_play = False self.player.new_player_list('djchannels', self.title, self.datalist, -1) self.player.end_callback = None self.player.play_and_pause(idx) self.at_playing_list = True elif datatype == 'fmsongs': self.resume_play = False self.storage.database['player_info']['playing_mode'] = 0 self.player.new_player_list('fmsongs', self.title, self.datalist, -1) self.player.end_callback = self.fm_callback self.player.play_and_pause(idx) self.at_playing_list = True else: self.player.play_and_pause(self.storage.database['player_info']['idx']) time.sleep(0.1) # 加载当前播放列表 elif key == ord('p'): self.show_playing_song() # 播放模式切换 elif key == ord('P'): self.storage.database['player_info']['playing_mode'] = \ (self.storage.database['player_info']['playing_mode'] + 1) % 5 # 添加到打碟歌单 elif key == ord('a'): if datatype == 'songs' and len(datalist) != 0: self.djstack.append(datalist[idx]) elif datatype == 'artists': pass # 加载打碟歌单 elif key == ord('z'): self.stack.append([datatype, title, datalist, offset, index]) self.datatype = 'songs' self.title = '网易云音乐 > 打碟' self.datalist = self.djstack self.offset = 0 self.index = 0 # 添加到收藏歌曲 elif key == ord('s'): if (datatype == 'songs' or datatype == 'djchannels') and len(datalist) != 0: self.collection.append(datalist[idx]) if platform.system() == 'Darwin': os.system('/usr/bin/osascript -e \'display notification "Added successfully"\'') else: os.system('/usr/bin/notify-send "Added successfully"') # 加载收藏歌曲 elif key == ord('c'): self.stack.append([datatype, title, datalist, offset, index]) self.datatype = 'songs' self.title = '网易云音乐 > 收藏' self.datalist = self.collection self.offset = 0 self.index = 0 # 从当前列表移除 elif key == ord('r'): if (datatype == 'songs' or datatype == 'djchannels') and len(datalist) != 0: self.datalist.pop(idx) self.index = carousel(offset, min(len(datalist), offset + step) - 1, idx) # 当前项目下移 elif key == ord("J"): if datatype != 'main' and len(datalist) != 0 and idx + 1 != len(self.datalist): self.START = time.time() song = self.datalist.pop(idx) self.datalist.insert(idx + 1, song) self.index = idx + 1 # 翻页 if self.index >= offset + step: self.offset = offset + step # 当前项目上移 elif key == ord("K"): if datatype != 'main' and len(datalist) != 0 and idx != 0: self.START = time.time() song = self.datalist.pop(idx) self.datalist.insert(idx - 1, song) self.index = idx - 1 # 翻页 if self.index < offset: self.offset = offset - step elif key == ord('m'): if datatype != 'main': self.stack.append([datatype, title, datalist, offset, index]) self.datatype = self.stack[0][0] self.title = self.stack[0][1] self.datalist = self.stack[0][2] self.offset = 0 self.index = 0 elif key == ord('g'): if datatype == 'help': webbrowser.open_new_tab('https://github.com/darknessomi/musicbox') # 开始下载 elif key == ord("C"): s = self.datalist[idx] cache_thread = threading.Thread(target=self.player.cacheSong1time, args=( s['song_id'], s['song_name'], s['artist'], s['mp3_url'])) cache_thread.start() elif key == ord('i'): if self.player.playing_id != -1: webbrowser.open_new_tab('http://music.163.com/#/song?id=' + str(self.player.playing_id)) self.ui.build_process_bar(self.player.process_location, self.player.process_length, self.player.playing_flag, self.player.pause_flag, self.storage.database['player_info']['playing_mode']) self.ui.build_menu(self.datatype, self.title, self.datalist, self.offset, self.index, self.step, self.START) self.player.stop() self.cache.quit() self.storage.save() curses.endwin() def dispatch_enter(self, idx): # The end of stack netease = self.netease datatype = self.datatype title = self.title datalist = self.datalist offset = self.offset index = self.index self.stack.append([datatype, title, datalist, offset, index]) if idx > len(self.datalist): return False if datatype == 'main': self.choice_channel(idx) # 该艺术家的热门歌曲 elif datatype == 'artists': artist_id = datalist[idx]['artist_id'] songs = netease.artists(artist_id) self.datatype = 'songs' self.datalist = netease.dig_info(songs, 'songs') self.title += ' > ' + datalist[idx]['artists_name'] # 该专辑包含的歌曲 elif datatype == 'albums': album_id = datalist[idx]['album_id'] songs = netease.album(album_id) self.datatype = 'songs' self.datalist = netease.dig_info(songs, 'songs') self.title += ' > ' + datalist[idx]['albums_name'] # 精选歌单选项 elif datatype == 'playlists': data = self.datalist[idx] self.datatype = data['datatype'] self.datalist = netease.dig_info(data['callback'](), self.datatype) self.title += ' > ' + data['title'] # 全站置顶歌单包含的歌曲 elif datatype == 'top_playlists': log.debug(datalist) playlist_id = datalist[idx]['playlist_id'] songs = netease.playlist_detail(playlist_id) self.datatype = 'songs' self.datalist = netease.dig_info(songs, 'songs') self.title += ' > ' + datalist[idx]['playlists_name'] # 分类精选 elif datatype == 'playlist_classes': # 分类名称 data = self.datalist[idx] self.datatype = 'playlist_class_detail' self.datalist = netease.dig_info(data, self.datatype) self.title += ' > ' + data log.debug(self.datalist) # 某一分类的详情 elif datatype == 'playlist_class_detail': # 子类别 data = self.datalist[idx] self.datatype = 'top_playlists' self.datalist = netease.dig_info(netease.top_playlists(data), self.datatype) log.debug(self.datalist) self.title += ' > ' + data # 歌曲榜单 elif datatype == 'toplists': songs = netease.top_songlist(idx) self.title += ' > ' + self.datalist[idx] self.datalist = netease.dig_info(songs, 'songs') self.datatype = 'songs' # 搜索菜单 elif datatype == 'search': ui = self.ui # no need to do stack.append, Otherwise there will be a bug when you input key 'h' to return # if idx in range(1, 5): # self.stack.append([self.datatype, self.title, self.datalist, self.offset, self.index]) self.index = 0 self.offset = 0 if idx == 0: # 搜索结果可以用top_playlists处理 self.datatype = 'top_playlists' self.datalist = ui.build_search('search_playlist') self.title = '精选歌单搜索列表' elif idx == 1: self.datatype = 'songs' self.datalist = ui.build_search('songs') self.title = '歌曲搜索列表' elif idx == 2: self.datatype = 'artists' self.datalist = ui.build_search('artists') self.title = '艺术家搜索列表' elif idx == 3: self.datatype = 'albums' self.datalist = ui.build_search('albums') self.title = '专辑搜索列表' def show_playing_song(self): if len(self.storage.database['player_info']['player_list']) == 0: return if not self.at_playing_list: self.stack.append([self.datatype, self.title, self.datalist, self.offset, self.index]) self.at_playing_list = True self.datatype = self.storage.database['player_info']['player_list_type'] self.title = self.storage.database['player_info']['player_list_title'] self.datalist = [] for i in self.storage.database['player_info']['player_list']: self.datalist.append(self.storage.database['songs'][i]) self.index = self.storage.database['player_info']['idx'] self.offset = self.storage.database['player_info']['idx'] / self.step * self.step if self.resume_play: if self.datatype == "fmsongs": self.player.end_callback = self.fm_callback else: self.player.end_callback = None self.storage.database['player_info']['idx'] = -1 self.player.play_and_pause(self.index) self.resume_play = False def song_changed_callback(self): if self.at_playing_list: self.show_playing_song() def fm_callback(self): log.debug("FM CallBack.") data = self.get_new_fm() self.player.append_songs(data) if self.datatype == 'fmsongs': if len(self.storage.database['player_info']['player_list']) == 0: return self.datatype = self.storage.database['player_info']['player_list_type'] self.title = self.storage.database['player_info']['player_list_title'] self.datalist = [] for i in self.storage.database['player_info']['player_list']: self.datalist.append(self.storage.database['songs'][i]) self.index = self.storage.database['player_info']['idx'] self.offset = self.storage.database['player_info']['idx'] / self.step * self.step def request_api(self, func, *args): if self.storage.database['user']['user_id'] != "": result = func(*args) if result != -1: return result log.debug("Re Login.") user_info = {} if self.storage.database['user']['username'] != "": user_info = self.netease.login(self.storage.database['user']['username'], self.storage.database['user']['password']) if self.storage.database['user']['username'] == "" or user_info['code'] != 200: data = self.ui.build_login() # 取消登录 if data == -1: return -1 user_info = data[0] self.storage.database['user']['username'] = data[1][0] self.storage.database['user']['password'] = data[1][1] self.storage.database['user']['user_id'] = user_info['account']['id'] self.storage.database['user']['nickname'] = user_info['profile']['nickname'] self.userid = self.storage.database["user"]["user_id"] self.username = self.storage.database["user"]["nickname"] return func(*args) def get_new_fm(self): myplaylist = [] for count in range(0, 1): data = self.request_api(self.netease.personal_fm) if data == -1: break myplaylist += data time.sleep(0.2) return self.netease.dig_info(myplaylist, "fmsongs") def choice_channel(self, idx): # 排行榜 netease = self.netease if idx == 0: self.datalist = netease.return_toplists() self.title += ' > 排行榜' self.datatype = 'toplists' # 艺术家 elif idx == 1: artists = netease.top_artists() self.datalist = netease.dig_info(artists, 'artists') self.title += ' > 艺术家' self.datatype = 'artists' # 新碟上架 elif idx == 2: albums = netease.new_albums() self.datalist = netease.dig_info(albums, 'albums') self.title += ' > 新碟上架' self.datatype = 'albums' # 精选歌单 elif idx == 3: self.datalist = [ { 'title': '全站置顶', 'datatype': 'top_playlists', 'callback': netease.top_playlists }, { 'title': '分类精选', 'datatype': 'playlist_classes', 'callback': netease.playlist_classes } ] self.title += ' > 精选歌单' self.datatype = 'playlists' # 我的歌单 elif idx == 4: myplaylist = self.request_api(self.netease.user_playlist, self.userid) if myplaylist == -1: return self.datatype = 'top_playlists' self.datalist = netease.dig_info(myplaylist, self.datatype) self.title += ' > ' + self.username + ' 的歌单' # DJ节目 elif idx == 5: self.datatype = 'djchannels' self.title += ' > DJ节目' self.datalist = netease.djchannels() # 每日推荐 elif idx == 6: self.datatype = 'songs' self.title += ' > 每日推荐' myplaylist = self.request_api(self.netease.recommend_playlist) if myplaylist == -1: return self.datalist = self.netease.dig_info(myplaylist, self.datatype) # 私人FM elif idx == 7: self.datatype = 'fmsongs' self.title += ' > 私人FM' self.datalist = self.get_new_fm() # 搜索 elif idx == 8: self.datatype = 'search' self.title += ' > 搜索' self.datalist = ['歌曲', '艺术家', '专辑', '网易精选集'] # 帮助 elif idx == 9: self.datatype = 'help' self.title += ' > 帮助' self.datalist = shortcut self.offset = 0 self.index = 0
class Ui: def __init__(self): self.screen = curses.initscr() # charactor break buffer curses.cbreak() self.screen.keypad(1) self.netease = NetEase() curses.start_color() curses.init_pair(1, curses.COLOR_GREEN, curses.COLOR_BLACK) curses.init_pair(2, curses.COLOR_CYAN, curses.COLOR_BLACK) curses.init_pair(3, curses.COLOR_RED, curses.COLOR_BLACK) curses.init_pair(4, curses.COLOR_YELLOW, curses.COLOR_BLACK) def build_playinfo(self, song_name, artist, album_name, pause=False): curses.noecho() # refresh top 2 line self.screen.move(1,1) self.screen.clrtoeol() self.screen.move(2,1) self.screen.clrtoeol() if pause: self.screen.addstr(1, 6, '_ _ z Z Z', curses.color_pair(3)) else: self.screen.addstr(1, 6, '♫ ♪ ♫ ♪', curses.color_pair(3)) self.screen.addstr(1, 19, song_name + ' - ' + artist + ' < ' + album_name + ' >', curses.color_pair(4)) self.screen.refresh() def build_loading(self): self.screen.addstr(6, 19, '享受高品质音乐,loading...', curses.color_pair(1)) self.screen.refresh() def build_menu(self, datatype, title, datalist, offset, index, step): # keep playing info in line 1 curses.noecho() self.screen.move(4,1) self.screen.clrtobot() self.screen.addstr(4, 19, title, curses.color_pair(1)) if len(datalist) == 0: self.screen.addstr(8, 19, '这里什么都没有 -,-') else: if datatype == 'main': for i in range( offset, min( len(datalist), offset+step) ): if i == index: self.screen.addstr(i - offset +8, 16, '-> ' + str(i) + '. ' + datalist[i], curses.color_pair(2)) else: self.screen.addstr(i - offset +8, 19, str(i) + '. ' + datalist[i]) elif datatype == 'songs': for i in range(offset, min( len(datalist), offset+step) ): # this item is focus if i == index: self.screen.addstr(i - offset +8, 16, '-> ' + str(i) + '. ' + datalist[i]['song_name'] + ' - ' + datalist[i]['artist'] + ' < ' + datalist[i]['album_name'] + ' >', curses.color_pair(2)) else: self.screen.addstr(i - offset +8, 19, str(i) + '. ' + datalist[i]['song_name'] + ' - ' + datalist[i]['artist'] + ' < ' + datalist[i]['album_name'] + ' >') elif datatype == 'artists': for i in range(offset, min( len(datalist), offset+step) ): if i == index: self.screen.addstr(i - offset +8, 16, '-> ' + str(i) + '. ' + datalist[i]['artists_name'] + ' - ' + str(datalist[i]['alias']), curses.color_pair(2)) else: self.screen.addstr(i - offset +8, 19, str(i) + '. ' + datalist[i]['artists_name'] + ' - ' + datalist[i]['alias']) elif datatype == 'albums': for i in range(offset, min( len(datalist), offset+step) ): if i == index: self.screen.addstr(i - offset +8, 16, '-> ' + str(i) + '. ' + datalist[i]['albums_name'] + ' - ' + datalist[i]['artists_name'], curses.color_pair(2)) else: self.screen.addstr(i - offset +8, 19, str(i) + '. ' + datalist[i]['albums_name'] + ' - ' + datalist[i]['artists_name']) elif datatype == 'playlists': for i in range(offset, min( len(datalist), offset+step) ): if i == index: self.screen.addstr(i - offset +8, 16, '-> ' + str(i) + '. ' + datalist[i]['title'], curses.color_pair(2)) else: self.screen.addstr(i - offset +8, 19, str(i) + '. ' + datalist[i]['title']) elif datatype == 'top_playlists': for i in range(offset, min( len(datalist), offset+step) ): if i == index: self.screen.addstr(i - offset +8, 16, '-> ' + str(i) + '. ' + datalist[i]['playlists_name'] + ' - ' + datalist[i]['creator_name'], curses.color_pair(2)) else: self.screen.addstr(i - offset +8, 19, str(i) + '. ' + datalist[i]['playlists_name'] + ' - ' + datalist[i]['creator_name']) elif datatype == 'playlist_classes' or datatype == 'playlist_class_detail': for i in range(offset, min( len(datalist), offset+step) ): if i == index: self.screen.addstr(i - offset +8, 16, '-> ' + str(i) + '. ' + datalist[i], curses.color_pair(2)) else: self.screen.addstr(i - offset +8, 19, str(i) + '. ' + datalist[i]) elif datatype == 'djchannels': for i in range(offset, min( len(datalist), offset+step) ): if i == index: self.screen.addstr(i - offset +8, 16, '-> ' + str(i) + '. ' + datalist[i]['song_name'], curses.color_pair(2)) else: self.screen.addstr(i - offset +8, 19, str(i) + '. ' + datalist[i]['song_name']) elif datatype == 'help': for i in range(offset, min( len(datalist), offset+step) ): if i == index: self.screen.addstr(i - offset +8, 16, '-> ' + str(i) + '. \'' + datalist[i][0].upper() + '\' ' + datalist[i][1] + ' ' + datalist[i][2], curses.color_pair(2)) else: self.screen.addstr(i - offset +8, 19, str(i) + '. \'' + datalist[i][0].upper() + '\' ' + datalist[i][1] + ' ' + datalist[i][2]) self.screen.addstr(20, 6, 'NetEase-MusicBox 基于Python,所有版权音乐来源于网易,本地不做任何保存') self.screen.addstr(21, 10, '按 [G] 到 Github 了解更多信息,帮助改进,或者Star表示支持~~') self.screen.addstr(22, 19, 'Build with love to music by omi') self.screen.refresh() def build_search(self, stype): netease = self.netease if stype == 'songs': song_name = self.get_param('搜索歌曲:') try: data = netease.search(song_name, stype=1) song_ids = [] if 'songs' in data['result']: if 'mp3Url' in data['result']['songs']: songs = data['result']['songs'] # if search song result do not has mp3Url # send ids to get mp3Url else: for i in range(0, len(data['result']['songs']) ): song_ids.append( data['result']['songs'][i]['id'] ) songs = netease.songs_detail(song_ids) return netease.dig_info(songs, 'songs') except: return [] elif stype == 'artists': artist_name = self.get_param('搜索艺术家:') try: data = netease.search(artist_name, stype=100) if 'artists' in data['result']: artists = data['result']['artists'] return netease.dig_info(artists, 'artists') except: return [] elif stype == 'albums': artist_name = self.get_param('搜索专辑:') try: data = netease.search(artist_name, stype=10) if 'albums' in data['result']: albums = data['result']['albums'] return netease.dig_info(albums, 'albums') except: return [] elif stype == 'search_playlist': artist_name = self.get_param('搜索网易精选集:') try: data = netease.search(artist_name, stype=1000) if 'playlists' in data['result']: playlists = data['result']['playlists'] return netease.dig_info(playlists, 'top_playlists') except: return [] return [] def build_search_menu(self): self.screen.move(4,1) self.screen.clrtobot() self.screen.addstr(8, 19, '选择搜索类型:', curses.color_pair(1)) self.screen.addstr(10,19, '[1] 歌曲') self.screen.addstr(11,19, '[2] 艺术家') self.screen.addstr(12,19, '[3] 专辑') self.screen.addstr(13,19, '[4] 网易精选集') self.screen.addstr(16,19, '请键入对应数字:', curses.color_pair(2)) self.screen.refresh() x = self.screen.getch() return x def build_login(self): curses.noecho() info = self.get_param('请输入登录信息, e.g: [email protected] 123456') account = info.split(' ') if len(account) != 2: return self.build_login() login_info = self.netease.login(account[0], account[1]) if login_info['code'] != 200: x = self.build_login_error() if x == ord('1'): return self.build_login() else: return -1 else: return [login_info, account] def build_login_error(self): self.screen.move(4,1) self.screen.clrtobot() self.screen.addstr(8, 19, '艾玛,登录信息好像不对呢 (O_O)#', curses.color_pair(1)) self.screen.addstr(10,19, '[1] 再试一次') self.screen.addstr(11,19, '[2] 稍后再试') self.screen.addstr(14,19, '请键入对应数字:', curses.color_pair(2)) self.screen.refresh() x = self.screen.getch() return x def get_param(self, prompt_string): # keep playing info in line 1 curses.echo() self.screen.move(4,1) self.screen.clrtobot() self.screen.addstr(5, 19, prompt_string, curses.color_pair(1)) self.screen.refresh() info = self.screen.getstr(10, 19, 60) if info.strip() is '': return self.get_param(prompt_string) else: return info
pool = ThreadPool(10) # 考虑到歌单最多10000首歌,故采用多个歌单 wait_song_list = [644264533, 644752348, 644765343, 644738922, 644776005, 644749639, 644756477, 644818427, 644835023, 644810557, 644806753, 644811524, 644800922, 644793786] # wait_song_list = [644810557, 644806753, 644811524, 644800922, 644793786] back_song_list = [583517654, 431743699] joker = NetEase() user_info = {} local_account = '*****@*****.**' local_password = '******' #local_account = '*****@*****.**' #local_password = '******' login_info = joker.login(local_account, local_password) print login_info def track_log(i): song_info = joker.song_info(i) print song_info if song_info: #print song_info try: play_time = song_info[0]['bMusic']['playTime']/1000 except IndexError: play_time = 60 print 'finish %s %s' % (i, joker.track_log(i, play_time)) #time.sleep(0.5) else: print 'no song %s' % (i)
class Ui: def __init__(self): self.screen = curses.initscr() self.screen.timeout(100) # the screen refresh every 100ms # charactor break buffer curses.cbreak() self.screen.keypad(1) self.netease = NetEase() curses.start_color() curses.init_pair(1, curses.COLOR_GREEN, curses.COLOR_BLACK) curses.init_pair(2, curses.COLOR_CYAN, curses.COLOR_BLACK) curses.init_pair(3, curses.COLOR_RED, curses.COLOR_BLACK) curses.init_pair(4, curses.COLOR_YELLOW, curses.COLOR_BLACK) # term resize handling size = terminalsize.get_terminal_size() self.x = max(size[0], 10) self.y = max(size[1], 25) self.startcol = int(float(self.x) / 5) self.indented_startcol = max(self.startcol - 3, 0) self.update_space() self.lyric = "" self.now_lyric = "" self.tlyric = "" self.storage = Storage() self.config = Config() self.newversion = False def notify(self, summary, song, album, artist): if summary != "disable": cmd = "" body = "%s\nin %s by %s" % (song, album, artist) if platform.system() == "Darwin": content = escape_quote(summary + ': ' + body) cmd = '/usr/bin/osascript -e $\'display notification "' + content + '"\'' else: cmd = '/usr/bin/notify-send -a NetEase-MusicBox "%s" "%s"' % (summary, body) os.system(cmd) def build_playinfo(self, song_name, artist, album_name, quality, start, pause=False): curses.noecho() # refresh top 2 line self.screen.move(1, 1) self.screen.clrtoeol() self.screen.move(2, 1) self.screen.clrtoeol() if pause: self.screen.addstr(1, self.indented_startcol, '_ _ z Z Z ' + quality, curses.color_pair(3)) else: self.screen.addstr(1, self.indented_startcol, '♫ ♪ ♫ ♪ ' + quality, curses.color_pair(3)) self.screen.addstr(1, min(self.indented_startcol + 18, self.x - 1), song_name + self.space + artist + ' < ' + album_name + ' >', curses.color_pair(4)) # The following script doesn't work. It is intended to scroll the playinfo # Scrollstring works by determining how long since it is created, but # playinfo is created everytime the screen refreshes (every 500ms), unlike # the menu. Is there a workaround? # name = song_name + self.space + artist + ' < ' + album_name + ' >' # decides whether to scoll # if truelen(name) <= self.x - self.indented_startcol - 18: # self.screen.addstr(1, min(self.indented_startcol + 18, self.x-1), # name, # curses.color_pair(4)) # else: # name = scrollstring(name + ' ', start) # self.screen.addstr(1, min(self.indented_startcol + 18, self.x-1), # str(name), # curses.color_pair(4)) self.screen.refresh() def build_process_bar(self, now_playing, total_length, playing_flag, pause_flag, playing_mode): if (self.storage.database["player_info"]["idx"] >= len(self.storage.database["player_info"]["player_list"])): return curses.noecho() self.screen.move(3, 1) self.screen.clrtoeol() self.screen.move(4, 1) self.screen.clrtoeol() if not playing_flag: return if total_length <= 0: total_length = 1 if now_playing > total_length or now_playing <= 0: now_playing = 0 process = "[" for i in range(0, 33): if i < now_playing / total_length * 33: if (i + 1) > now_playing / total_length * 33: if not pause_flag: process += ">" continue process += "=" else: process += " " process += "] " now_minute = int(now_playing / 60) if now_minute > 9: now_minute = str(now_minute) else: now_minute = "0" + str(now_minute) now_second = int(now_playing - int(now_playing / 60) * 60) if now_second > 9: now_second = str(now_second) else: now_second = "0" + str(now_second) total_minute = int(total_length / 60) if total_minute > 9: total_minute = str(total_minute) else: total_minute = "0" + str(total_minute) total_second = int(total_length - int(total_length / 60) * 60) if total_second > 9: total_second = str(total_second) else: total_second = "0" + str(total_second) process += "(" + now_minute + ":" + now_second + "/" + total_minute + ":" + total_second + ")" if playing_mode == 0: process = "顺序播放 " + process elif playing_mode == 1: process = "顺序循环 " + process elif playing_mode == 2: process = "单曲循环 " + process elif playing_mode == 3: process = "随机播放 " + process elif playing_mode == 4: process = "随机循环 " + process else: pass self.screen.addstr(3, self.startcol - 2, process, curses.color_pair(1)) song = self.storage.database["songs"][ self.storage.database["player_info"]["player_list"][self.storage.database["player_info"]["idx"]] ] if 'lyric' not in song.keys() or len(song["lyric"]) <= 0: self.now_lyric = "[00:00.00]暂无歌词 ~>_<~ \n" else: key = now_minute + ":" + now_second for line in song["lyric"]: if key in line: if 'tlyric' not in song.keys() or len(song["tlyric"]) <= 0: self.now_lyric = line else: self.now_lyric = line for tline in song["tlyric"]: if key in tline and self.config.get_item("translation"): self.now_lyric = tline + " || " + self.now_lyric self.now_lyric = re.sub('\[.*?\]', "", self.now_lyric) self.screen.addstr(4, self.startcol - 2, str(self.now_lyric), curses.color_pair(3)) self.screen.refresh() def build_loading(self): self.screen.addstr(7, self.startcol, '享受高品质音乐,loading...', curses.color_pair(1)) self.screen.refresh() # start is the timestamp of this function being called def build_menu(self, datatype, title, datalist, offset, index, step, start): # keep playing info in line 1 curses.noecho() self.screen.move(5, 1) self.screen.clrtobot() self.screen.addstr(5, self.startcol, title, curses.color_pair(1)) if len(datalist) == 0: self.screen.addstr(8, self.startcol, '这里什么都没有 -,-') else: if datatype == 'main': for i in range(offset, min(len(datalist), offset + step)): if i == index: self.screen.addstr(i - offset + 9, self.indented_startcol, '-> ' + str(i) + '. ' + datalist[i], curses.color_pair(2)) else: self.screen.addstr(i - offset + 9, self.startcol, str(i) + '. ' + datalist[i]) elif datatype == 'songs' or datatype == 'fmsongs': iter_range = min(len(datalist), offset + step) for i in range(offset, iter_range): # this item is focus if i == index: self.screen.addstr(i - offset + 8, 0, ' ' * self.startcol) lead = '-> ' + str(i) + '. ' self.screen.addstr(i - offset + 8, self.indented_startcol, lead, curses.color_pair(2)) name = str(datalist[i]['song_name'] + self.space + datalist[i][ 'artist'] + ' < ' + datalist[i]['album_name'] + ' >') # the length decides whether to scoll if truelen(name) < self.x - self.startcol - 1: self.screen.addstr(i - offset + 8, self.indented_startcol + len(lead), name, curses.color_pair(2)) else: name = scrollstring(name + ' ', start) self.screen.addstr(i - offset + 8, self.indented_startcol + len(lead), str(name), curses.color_pair(2)) else: self.screen.addstr(i - offset + 8, 0, ' ' * self.startcol) self.screen.addstr(i - offset + 8, self.startcol, str(str(i) + '. ' + datalist[i]['song_name'] + self.space + datalist[i][ 'artist'] + ' < ' + datalist[i]['album_name'] + ' >')[:int(self.x * 2)]) self.screen.addstr(iter_range - offset + 9, 0, ' ' * self.x) elif datatype == 'artists': for i in range(offset, min(len(datalist), offset + step)): if i == index: self.screen.addstr(i - offset + 9, self.indented_startcol, '-> ' + str(i) + '. ' + datalist[i]['artists_name'] + self.space + str( datalist[i]['alias']), curses.color_pair(2)) else: self.screen.addstr(i - offset + 9, self.startcol, str(i) + '. ' + datalist[i]['artists_name'] + self.space + datalist[i][ 'alias']) elif datatype == 'albums': for i in range(offset, min(len(datalist), offset + step)): if i == index: self.screen.addstr(i - offset + 9, self.indented_startcol, '-> ' + str(i) + '. ' + datalist[i]['albums_name'] + self.space + datalist[i][ 'artists_name'], curses.color_pair(2)) else: self.screen.addstr(i - offset + 9, self.startcol, str(i) + '. ' + datalist[i]['albums_name'] + self.space + datalist[i][ 'artists_name']) elif datatype == 'playlists': for i in range(offset, min(len(datalist), offset + step)): if i == index: self.screen.addstr(i - offset + 9, self.indented_startcol, '-> ' + str(i) + '. ' + datalist[i]['title'], curses.color_pair(2)) else: self.screen.addstr(i - offset + 9, self.startcol, str(i) + '. ' + datalist[i]['title']) elif datatype == 'top_playlists': for i in range(offset, min(len(datalist), offset + step)): if i == index: self.screen.addstr(i - offset + 9, self.indented_startcol, '-> ' + str(i) + '. ' + datalist[i]['playlists_name'] + self.space + datalist[i]['creator_name'], curses.color_pair(2)) else: self.screen.addstr(i - offset + 9, self.startcol, str(i) + '. ' + datalist[i]['playlists_name'] + self.space + datalist[i][ 'creator_name']) elif datatype == 'toplists': for i in range(offset, min(len(datalist), offset + step)): if i == index: self.screen.addstr(i - offset + 9, self.indented_startcol, '-> ' + str(i) + '. ' + datalist[i], curses.color_pair(2)) else: self.screen.addstr(i - offset + 9, self.startcol, str(i) + '. ' + datalist[i]) elif datatype == 'playlist_classes' or datatype == 'playlist_class_detail': for i in range(offset, min(len(datalist), offset + step)): if i == index: self.screen.addstr(i - offset + 9, self.indented_startcol, '-> ' + str(i) + '. ' + datalist[i], curses.color_pair(2)) else: self.screen.addstr(i - offset + 9, self.startcol, str(i) + '. ' + datalist[i]) elif datatype == 'djchannels': for i in range(offset, min(len(datalist), offset + step)): if i == index: self.screen.addstr(i - offset + 8, self.indented_startcol, '-> ' + str(i) + '. ' + datalist[i]['song_name'], curses.color_pair(2)) else: self.screen.addstr(i - offset + 8, self.startcol, str(i) + '. ' + datalist[i]['song_name']) elif datatype == 'search': self.screen.move(6, 1) self.screen.clrtobot() self.screen.timeout(-1) self.screen.addstr(8, self.startcol, '选择搜索类型:', curses.color_pair(1)) for i in range(offset, min(len(datalist), offset + step)): if i == index: self.screen.addstr(i - offset + 10, self.indented_startcol, '-> ' + str(i) + '.' + datalist[i - 1], curses.color_pair(2)) else: self.screen.addstr(i - offset + 10, self.startcol, str(i) + '.' + datalist[i - 1]) self.screen.timeout(100) elif datatype == 'help': for i in range(offset, min(len(datalist), offset + step)): if i == index: self.screen.addstr(i - offset + 9, self.indented_startcol, '-> ' + str(i) + '. \'' + (datalist[i][0].upper() + '\'').ljust(11) + datalist[i][ 1] + ' ' + datalist[i][2], curses.color_pair(2)) else: self.screen.addstr(i - offset + 9, self.startcol, str(i) + '. \'' + (datalist[i][0].upper() + '\'').ljust(11) + datalist[i][ 1] + ' ' + datalist[i][2]) self.screen.addstr(20, 6, 'NetEase-MusicBox 基于Python,所有版权音乐来源于网易,本地不做任何保存') self.screen.addstr(21, 10, '按 [G] 到 Github 了解更多信息,帮助改进,或者Star表示支持~~') self.screen.addstr(22, self.startcol, 'Build with love to music by omi') self.screen.refresh() def build_search(self, stype): self.screen.timeout(-1) netease = self.netease if stype == 'songs': song_name = self.get_param('搜索歌曲:') if song_name == '/return': return [] else: try: data = netease.search(song_name, stype=1) song_ids = [] if 'songs' in data['result']: if 'mp3Url' in data['result']['songs']: songs = data['result']['songs'] # if search song result do not has mp3Url # send ids to get mp3Url else: for i in range(0, len(data['result']['songs'])): song_ids.append(data['result']['songs'][i]['id']) songs = netease.songs_detail(song_ids) return netease.dig_info(songs, 'songs') except: return [] elif stype == 'artists': artist_name = self.get_param('搜索艺术家:') if artist_name == '/return': return [] else: try: data = netease.search(artist_name, stype=100) if 'artists' in data['result']: artists = data['result']['artists'] return netease.dig_info(artists, 'artists') except: return [] elif stype == 'albums': albums_name = self.get_param('搜索专辑:') if albums_name == '/return': return [] else: try: data = netease.search(albums_name, stype=10) if 'albums' in data['result']: albums = data['result']['albums'] return netease.dig_info(albums, 'albums') except: return [] elif stype == 'search_playlist': search_playlist = self.get_param('搜索网易精选集:') if search_playlist == '/return': return [] else: try: data = netease.search(search_playlist, stype=1000) if 'playlists' in data['result']: playlists = data['result']['playlists'] return netease.dig_info(playlists, 'top_playlists') except: return [] return [] def build_login(self): self.build_login_bar() local_account = self.get_account() local_password = hashlib.md5(self.get_password()).hexdigest() login_info = self.netease.login(local_account, local_password) account = [local_account, local_password] if login_info['code'] != 200: x = self.build_login_error() if x == ord('1'): return self.build_login() else: return -1 else: return [login_info, account] def build_login_bar(self): curses.noecho() self.screen.move(4, 1) self.screen.clrtobot() self.screen.addstr(5, self.startcol, '请输入登录信息(支持手机登陆)', curses.color_pair(1)) self.screen.addstr(8, self.startcol, "账号:", curses.color_pair(1)) self.screen.addstr(9, self.startcol, "密码:", curses.color_pair(1)) self.screen.move(8, 24) self.screen.refresh() def build_login_error(self): self.screen.move(4, 1) self.screen.timeout(-1) # disable the screen timeout self.screen.clrtobot() self.screen.addstr(8, self.startcol, '艾玛,登录信息好像不对呢 (O_O)#', curses.color_pair(1)) self.screen.addstr(10, self.startcol, '[1] 再试一次') self.screen.addstr(11, self.startcol, '[2] 稍后再试') self.screen.addstr(14, self.startcol, '请键入对应数字:', curses.color_pair(2)) self.screen.refresh() x = self.screen.getch() self.screen.timeout(100) # restore the screen timeout return x def get_account(self): self.screen.timeout(-1) # disable the screen timeout curses.echo() account = self.screen.getstr(8, self.startcol + 6, 60) self.screen.timeout(100) # restore the screen timeout return account def get_password(self): self.screen.timeout(-1) # disable the screen timeout curses.noecho() password = self.screen.getstr(9, self.startcol + 6, 60) self.screen.timeout(100) # restore the screen timeout return password def get_param(self, prompt_string): # keep playing info in line 1 curses.echo() self.screen.move(4, 1) self.screen.clrtobot() self.screen.addstr(5, self.startcol, prompt_string, curses.color_pair(1)) self.screen.refresh() info = self.screen.getstr(10, self.startcol, 60) if info == '': return '/return' elif info.strip() is '': return self.get_param(prompt_string) else: return info def update_size(self): # get terminal size size = terminalsize.get_terminal_size() self.x = max(size[0], 10) self.y = max(size[1], 25) # update intendations curses.resizeterm(self.y, self.x) self.startcol = int(float(self.x) / 5) self.indented_startcol = max(self.startcol - 3, 0) self.update_space() self.screen.clear() self.screen.refresh() def update_space(self): if self.x > 140: self.space = " - " elif self.x > 80: self.space = " - " else: self.space = " - " self.screen.refresh()
class Ui: def __init__(self): self.screen = curses.initscr() # charactor break buffer curses.cbreak() self.screen.keypad(1) self.netease = NetEase() curses.start_color() curses.init_pair(1, curses.COLOR_GREEN, curses.COLOR_BLACK) curses.init_pair(2, curses.COLOR_CYAN, curses.COLOR_BLACK) curses.init_pair(3, curses.COLOR_RED, curses.COLOR_BLACK) curses.init_pair(4, curses.COLOR_YELLOW, curses.COLOR_BLACK) def build_playinfo(self, song_name, artist, album_name, pause=False): # refresh top 2 line self.screen.move(1, 1) self.screen.clrtoeol() self.screen.move(2, 1) self.screen.clrtoeol() if pause: self.screen.addstr(1, 6, '_ _ z Z Z', curses.color_pair(3)) else: self.screen.addstr(1, 6, '♫ ♪ ♫ ♪', curses.color_pair(3)) self.screen.addstr(1, 19, song_name + ' - ' + artist + ' < ' + album_name + ' >', curses.color_pair(4)) self.screen.refresh() def build_loading(self): self.screen.addstr(6, 19, 'Waiting for you,loading...', curses.color_pair(1)) self.screen.refresh() def build_menu(self, datatype, title, datalist, offset, index, step): # keep playing info in line 1 self.screen.move(4, 1) self.screen.clrtobot() self.screen.addstr(4, 19, title, curses.color_pair(1)) if len(datalist) == 0: self.screen.addstr(8, 19, 'Nothing -,-') else: if datatype == 'main': for i in range(offset, min(len(datalist), offset + step)): if i == index: self.screen.addstr(i - offset + 8, 16, '-> ' + str(i) + '. ' + datalist[i], curses.color_pair(2)) else: self.screen.addstr(i - offset + 8, 19, str(i) + '. ' + datalist[i]) elif datatype == 'songs': for i in range(offset, min(len(datalist), offset + step)): # this item is focus if i == index: self.screen.addstr(i - offset + 8, 16, '-> ' + str(i) + '. ' + datalist[i]['song_name'] + ' - ' + datalist[i][ 'artist'] + ' < ' + datalist[i]['album_name'] + ' >', curses.color_pair(2)) else: self.screen.addstr(i - offset + 8, 19, str(i) + '. ' + datalist[i]['song_name'] + ' - ' + datalist[i][ 'artist'] + ' < ' + datalist[i]['album_name'] + ' >') elif datatype == 'artists': for i in range(offset, min(len(datalist), offset + step)): if i == index: self.screen.addstr(i - offset + 8, 16, '-> ' + str(i) + '. ' + datalist[i]['artists_name'] + ' - ' + str( datalist[i]['alias']), curses.color_pair(2)) else: self.screen.addstr(i - offset + 8, 19, str(i) + '. ' + datalist[i]['artists_name'] + ' - ' + datalist[i][ 'alias']) elif datatype == 'albums': for i in range(offset, min(len(datalist), offset + step)): if i == index: self.screen.addstr(i - offset + 8, 16, '-> ' + str(i) + '. ' + datalist[i]['albums_name'] + ' - ' + datalist[i][ 'artists_name'], curses.color_pair(2)) else: self.screen.addstr(i - offset + 8, 19, str(i) + '. ' + datalist[i]['albums_name'] + ' - ' + datalist[i][ 'artists_name']) elif datatype == 'playlists': for i in range(offset, min(len(datalist), offset + step)): if i == index: self.screen.addstr(i - offset + 8, 16, '-> ' + str(i) + '. ' + datalist[i]['playlists_name'] + ' - ' + datalist[i]['creator_name'], curses.color_pair(2)) else: self.screen.addstr(i - offset + 8, 19, str(i) + '. ' + datalist[i]['playlists_name'] + ' - ' + datalist[i][ 'creator_name']) elif datatype == 'djchannels': for i in range(offset, min(len(datalist), offset + step)): if i == index: self.screen.addstr(i - offset + 8, 16, '-> ' + str(i) + '. ' + datalist[i]['song_name'], curses.color_pair(2)) else: self.screen.addstr(i - offset + 8, 19, str(i) + '. ' + datalist[i]['song_name']) elif datatype == 'help': for i in range(offset, min(len(datalist), offset + step)): if i == index: self.screen.addstr(i - offset + 8, 16, '-> ' + str(i) + '. \'' + datalist[i][0].upper() + '\' ' + datalist[i][ 1] + ' ' + datalist[i][2], curses.color_pair(2)) else: self.screen.addstr(i - offset + 8, 19, str(i) + '. \'' + datalist[i][0].upper() + '\' ' + datalist[i][1] + ' ' + datalist[i][2]) self.screen.addstr(20, 6, 'NetEase-MusicBox Based on Python, all rights revesred by NetEase') self.screen.addstr(21, 10, 'Input [G] to GitHub') self.screen.addstr(22, 19, 'Build with love to music by @vellow') self.screen.refresh() def build_search(self, stype): netease = self.netease if stype == 'songs': song_name = self.get_param('Search by Songs:') try: data = netease.search(song_name, stype=1) song_ids = [] if 'songs' in data['result']: if 'mp3Url' in data['result']['songs']: songs = data['result']['songs'] # if search song result do not has mp3Url # send ids to get mp3Url else: for i in range(0, len(data['result']['songs'])): song_ids.append(data['result']['songs'][i]['id']) songs = netease.songs_detail(song_ids) return netease.dig_info(songs, 'songs') except: return [] elif stype == 'artists': artist_name = self.get_param('Search by Artists:') try: data = netease.search(artist_name, stype=100) if 'artists' in data['result']: artists = data['result']['artists'] return netease.dig_info(artists, 'artists') except: return [] elif stype == 'albums': artist_name = self.get_param('Search by Artists:') try: data = netease.search(artist_name, stype=10) if 'albums' in data['result']: albums = data['result']['albums'] return netease.dig_info(albums, 'albums') except: return [] elif stype == 'playlists': artist_name = self.get_param('Search by Playlists:') try: data = netease.search(artist_name, stype=1000) if 'playlists' in data['result']: playlists = data['result']['playlists'] return netease.dig_info(playlists, 'playlists') except: return [] return [] def build_search_menu(self): self.screen.move(4, 1) self.screen.clrtobot() self.screen.addstr(8, 19, 'Search Type:', curses.color_pair(1)) self.screen.addstr(10, 19, '[1] Songs') self.screen.addstr(11, 19, '[2] Artists') self.screen.addstr(12, 19, '[3] Albums') self.screen.addstr(13, 19, '[4] Playist') self.screen.addstr(16, 19, 'Please input the option:', curses.color_pair(2)) self.screen.refresh() x = self.screen.getch() return x def build_login(self): info = self.get_param('Log In, e.g: [email protected] 123456') account = info.split(' ') if len(account) != 2: return self.build_login() login_info = self.netease.login(account[0], account[1]) if login_info['code'] != 200: x = self.build_login_error() if x == ord('1'): return self.build_login() else: return -1 else: return [login_info, account] def build_login_error(self): self.screen.move(4, 1) self.screen.clrtobot() self.screen.addstr(8, 19, 'Something wrong(O_O)#', curses.color_pair(1)) self.screen.addstr(10, 19, '[1] Try Again') self.screen.addstr(11, 19, '[2] Later') self.screen.addstr(14, 19, 'Please input the option', curses.color_pair(2)) self.screen.refresh() x = self.screen.getch() return x def get_param(self, prompt_string): # keep playing info in line 1 self.screen.move(4, 1) self.screen.clrtobot() self.screen.addstr(5, 19, prompt_string, curses.color_pair(1)) self.screen.refresh() info = self.screen.getstr(10, 19, 60) if info.strip() is '': return self.get_param(prompt_string) else: return info
class Ui: def __init__(self): self.screen = curses.initscr() # charactor break buffer curses.cbreak() #cbreak模式,按键事件互动 self.screen.keypad(1) self.netease = NetEase() curses.start_color() curses.init_pair(1, curses.COLOR_GREEN, curses.COLOR_BLACK) curses.init_pair(2, curses.COLOR_CYAN, curses.COLOR_BLACK) curses.init_pair(3, curses.COLOR_RED, curses.COLOR_BLACK) curses.init_pair(4, curses.COLOR_YELLOW, curses.COLOR_BLACK) # term resize handling size = terminalsize.get_terminal_size() self.x = max(size[0], 10) self.y = max(size[1], 25) self.startcol = int(float(self.x)/5) self.indented_startcol = max(self.startcol - 3, 0) self.update_space() def build_playinfo(self, song_name, artist, album_name, quality, pause=False): curses.noecho() # refresh top 2 line self.screen.move(1, 1) self.screen.clrtoeol() self.screen.move(2, 1) self.screen.clrtoeol() if pause: self.screen.addstr(1, self.indented_startcol, '_ _ z Z Z ' + quality, curses.color_pair(3)) else: self.screen.addstr(1, self.indented_startcol, '♫ ♪ ♫ ♪ ' + quality, curses.color_pair(3)) self.screen.addstr(1, min(self.indented_startcol + 18, self.x-1), song_name + self.space + artist + ' < ' + album_name + ' >', curses.color_pair(4)) self.screen.refresh() def build_loading(self): self.screen.addstr(6, self.startcol, '享受高品质音乐,loading...', curses.color_pair(1)) self.screen.refresh() def build_menu(self, datatype, title, datalist, offset, index, step): # keep playing info in line 1 curses.noecho() self.screen.move(4, 1) self.screen.clrtobot() self.screen.addstr(4, self.startcol, title, curses.color_pair(1)) if len(datalist) == 0: self.screen.addstr(8, self.startcol, '这里什么都没有 -,-') else: if datatype == 'main': for i in range(offset, min(len(datalist), offset + step)): if i == index: self.screen.addstr(i - offset + 8, self.indented_startcol, '-> ' + str(i) + '. ' + datalist[i], curses.color_pair(2)) else: self.screen.addstr(i - offset + 8, self.startcol, str(i) + '. ' + datalist[i]) elif datatype == 'songs': iter_range = min(len(datalist), offset + step) for i in range(offset, iter_range): # this item is focus if i == index: self.screen.addstr(i - offset + 8, 0, ' ' * self.startcol) self.screen.addstr(i - offset + 8, self.indented_startcol, str('-> ' + str(i) + '. ' + datalist[i]['song_name'] + self.space + datalist[i][ 'artist'] + ' < ' + datalist[i]['album_name'] + ' >')[:int(self.x*2)], curses.color_pair(2)) else: self.screen.addstr(i - offset + 8, 0, ' ' * self.startcol) self.screen.addstr(i - offset + 8, self.startcol, str(str(i) + '. ' + datalist[i]['song_name'] + self.space + datalist[i][ 'artist'] + ' < ' + datalist[i]['album_name'] + ' >')[:int(self.x*2)]) self.screen.addstr(iter_range - offset + 8, 0, ' ' * self.x) elif datatype == 'artists': for i in range(offset, min(len(datalist), offset + step)): if i == index: self.screen.addstr(i - offset + 8, self.indented_startcol, '-> ' + str(i) + '. ' + datalist[i]['artists_name'] + self.space + str( datalist[i]['alias']), curses.color_pair(2)) else: self.screen.addstr(i - offset + 8, self.startcol, str(i) + '. ' + datalist[i]['artists_name'] + self.space + datalist[i][ 'alias']) elif datatype == 'albums': for i in range(offset, min(len(datalist), offset + step)): if i == index: self.screen.addstr(i - offset + 8, self.indented_startcol, '-> ' + str(i) + '. ' + datalist[i]['albums_name'] + self.space + datalist[i][ 'artists_name'], curses.color_pair(2)) else: self.screen.addstr(i - offset + 8, self.startcol, str(i) + '. ' + datalist[i]['albums_name'] + self.space + datalist[i][ 'artists_name']) elif datatype == 'playlists': for i in range(offset, min(len(datalist), offset + step)): if i == index: self.screen.addstr(i - offset + 8, self.indented_startcol, '-> ' + str(i) + '. ' + datalist[i]['title'], curses.color_pair(2)) else: self.screen.addstr(i - offset + 8, self.startcol, str(i) + '. ' + datalist[i]['title']) elif datatype == 'top_playlists': for i in range(offset, min(len(datalist), offset + step)): if i == index: self.screen.addstr(i - offset + 8, self.indented_startcol, '-> ' + str(i) + '. ' + datalist[i]['playlists_name'] + self.space + datalist[i]['creator_name'], curses.color_pair(2)) else: self.screen.addstr(i - offset + 8, self.startcol, str(i) + '. ' + datalist[i]['playlists_name'] + self.space + datalist[i][ 'creator_name']) elif datatype == 'toplists': for i in range(offset, min(len(datalist), offset + step)): if i == index: self.screen.addstr(i - offset + 8, self.indented_startcol, '-> ' + str(i) + '. ' + datalist[i], curses.color_pair(2)) else: self.screen.addstr(i - offset + 8, self.startcol, str(i) + '. ' + datalist[i]) elif datatype == 'playlist_classes' or datatype == 'playlist_class_detail': for i in range(offset, min(len(datalist), offset + step)): if i == index: self.screen.addstr(i - offset + 8, self.indented_startcol, '-> ' + str(i) + '. ' + datalist[i], curses.color_pair(2)) else: self.screen.addstr(i - offset + 8, self.startcol, str(i) + '. ' + datalist[i]) elif datatype == 'djchannels': for i in range(offset, min(len(datalist), offset + step)): if i == index: self.screen.addstr(i - offset + 8, self.indented_startcol, '-> ' + str(i) + '. ' + datalist[i]['song_name'], curses.color_pair(2)) else: self.screen.addstr(i - offset + 8, self.startcol, str(i) + '. ' + datalist[i]['song_name']) elif datatype == 'search': self.screen.move(4, 1) self.screen.clrtobot() self.screen.addstr(8, self.startcol, '选择搜索类型:', curses.color_pair(1)) for i in range(offset, min(len(datalist), offset + step)): if i == index: self.screen.addstr(i - offset + 10, self.indented_startcol, '-> ' + str(i) + '.' + datalist[i - 1], curses.color_pair(2)) else: self.screen.addstr(i - offset + 10, self.startcol, str(i) + '.' + datalist[i - 1]) elif datatype == 'help': for i in range(offset, min(len(datalist), offset + step)): if i == index: self.screen.addstr(i - offset + 8, self.indented_startcol, '-> ' + str(i) + '. \'' + (datalist[i][0].upper() + '\'').ljust(11) + datalist[i][ 1] + ' ' + datalist[i][2], curses.color_pair(2)) else: self.screen.addstr(i - offset + 8, self.startcol, str(i) + '. \'' + (datalist[i][0].upper() + '\'').ljust(11) + datalist[i][1] + ' ' + datalist[i][2]) self.screen.addstr(20, 6, 'NetEase-MusicBox 基于Python,所有版权音乐来源于网易,本地不做任何保存') self.screen.addstr(21, 10, '按 [G] 到 Github 了解更多信息,帮助改进,或者Star表示支持~~') self.screen.addstr(22, self.startcol, 'Build with love to music by omi') self.screen.refresh() def build_search(self, stype): netease = self.netease if stype == 'songs': song_name = self.get_param('搜索歌曲:') try: data = netease.search(song_name, stype=1) song_ids = [] if 'songs' in data['result']: if 'mp3Url' in data['result']['songs']: songs = data['result']['songs'] # if search song result do not has mp3Url # send ids to get mp3Url else: for i in range(0, len(data['result']['songs'])): song_ids.append(data['result']['songs'][i]['id']) songs = netease.songs_detail(song_ids) return netease.dig_info(songs, 'songs') except: return [] elif stype == 'artists': artist_name = self.get_param('搜索艺术家:') try: data = netease.search(artist_name, stype=100) if 'artists' in data['result']: artists = data['result']['artists'] return netease.dig_info(artists, 'artists') except: return [] elif stype == 'albums': artist_name = self.get_param('搜索专辑:') try: data = netease.search(artist_name, stype=10) if 'albums' in data['result']: albums = data['result']['albums'] return netease.dig_info(albums, 'albums') except: return [] elif stype == 'search_playlist': artist_name = self.get_param('搜索网易精选集:') try: data = netease.search(artist_name, stype=1000) if 'playlists' in data['result']: playlists = data['result']['playlists'] return netease.dig_info(playlists, 'top_playlists') except: return [] return [] def build_login(self): self.build_login_bar() local_account = self.get_account() local_password = hashlib.md5(self.get_password()).hexdigest() login_info = self.netease.login(local_account, local_password) account = [local_account,local_password] if login_info['code'] != 200: x = self.build_login_error() if x == ord('1'): return self.build_login() else: return -1 else: return [login_info, account] def build_login_bar(self): curses.noecho() self.screen.move(4, 1) self.screen.clrtobot() self.screen.addstr(5, self.startcol, '请输入登录信息(支持手机登陆)',curses.color_pair(1)) self.screen.addstr(8, self.startcol, "账号:", curses.color_pair(1)) self.screen.addstr(9, self.startcol, "密码:", curses.color_pair(1)) self.screen.move(8,24) self.screen.refresh() def build_login_error(self): self.screen.move(4, 1) self.screen.clrtobot() self.screen.addstr(8, self.startcol, '艾玛,登录信息好像不对呢 (O_O)#', curses.color_pair(1)) self.screen.addstr(10, self.startcol, '[1] 再试一次') self.screen.addstr(11, self.startcol, '[2] 稍后再试') self.screen.addstr(14, self.startcol, '请键入对应数字:', curses.color_pair(2)) self.screen.refresh() x = self.screen.getch() return x def get_account(self): curses.echo() account = self.screen.getstr(8, self.startcol+6,60) return account def get_password(self): curses.noecho() password = self.screen.getstr(9, self.startcol+6,60) return password def get_param(self, prompt_string): # keep playing info in line 1 curses.echo() self.screen.move(4, 1) self.screen.clrtobot() self.screen.addstr(5, self.startcol, prompt_string, curses.color_pair(1)) self.screen.refresh() info = self.screen.getstr(10, self.startcol, 60) if info.strip() is '': return self.get_param(prompt_string) else: return info def update_size(self): # get terminal size size = terminalsize.get_terminal_size() self.x = max(size[0], 10) self.y = max(size[1], 25) # update intendations curses.resizeterm(self.y, self.x) self.startcol = int(float(self.x)/5) self.indented_startcol = max(self.startcol - 3, 0) self.update_space() self.screen.clear() self.screen.refresh() def update_space(self): if self.x > 140: self.space = " - " elif self.x > 80: self.space = " - " else: self.space = " - " self.screen.refresh()
class Ui(object): def __init__(self): self.screen = curses.initscr() self.screen.timeout(100) # the screen refresh every 100ms # charactor break buffer curses.cbreak() self.screen.keypad(1) self.netease = NetEase() curses.start_color() curses.init_pair(1, curses.COLOR_GREEN, curses.COLOR_BLACK) curses.init_pair(2, curses.COLOR_CYAN, curses.COLOR_BLACK) curses.init_pair(3, curses.COLOR_RED, curses.COLOR_BLACK) curses.init_pair(4, curses.COLOR_YELLOW, curses.COLOR_BLACK) # term resize handling size = terminalsize.get_terminal_size() self.x = max(size[0], 10) self.y = max(size[1], 25) self.startcol = int(float(self.x) / 5) self.indented_startcol = max(self.startcol - 3, 0) self.update_space() self.lyric = '' self.now_lyric = '' self.tlyric = '' self.storage = Storage() self.config = Config() self.newversion = False def notify(self, summary, song, album, artist): if summary != 'disable': body = '%s\nin %s by %s' % (song, album, artist) content = escape_quote(summary + ': ' + body) notify(content) def build_playinfo(self, song_name, artist, album_name, quality, start, pause=False): curses.noecho() # refresh top 2 line self.screen.move(1, 1) self.screen.clrtoeol() self.screen.move(2, 1) self.screen.clrtoeol() if pause: self.screen.addstr(1, self.indented_startcol, '_ _ z Z Z ' + quality, curses.color_pair(3)) else: self.screen.addstr(1, self.indented_startcol, '♫ ♪ ♫ ♪ ' + quality, curses.color_pair(3)) self.screen.addstr( 1, min(self.indented_startcol + 18, self.x - 1), song_name + self.space + artist + ' < ' + album_name + ' >', curses.color_pair(4)) self.screen.refresh() def build_process_bar(self, now_playing, total_length, playing_flag, pause_flag, playing_mode): if (self.storage.database['player_info']['idx'] >= len(self.storage.database['player_info']['player_list'])): return curses.noecho() self.screen.move(3, 1) self.screen.clrtoeol() self.screen.move(4, 1) self.screen.clrtoeol() if not playing_flag: return if total_length <= 0: total_length = 1 if now_playing > total_length or now_playing <= 0: now_playing = 0 process = '[' for i in range(0, 33): if i < now_playing / total_length * 33: if (i + 1) > now_playing / total_length * 33: if not pause_flag: process += '>' continue process += '=' else: process += ' ' process += '] ' now_minute = int(now_playing / 60) if now_minute > 9: now_minute = str(now_minute) else: now_minute = '0' + str(now_minute) now_second = int(now_playing - int(now_playing / 60) * 60) if now_second > 9: now_second = str(now_second) else: now_second = '0' + str(now_second) total_minute = int(total_length / 60) if total_minute > 9: total_minute = str(total_minute) else: total_minute = '0' + str(total_minute) total_second = int(total_length - int(total_length / 60) * 60) if total_second > 9: total_second = str(total_second) else: total_second = '0' + str(total_second) process += '(' + now_minute + ':' + now_second + '/' + total_minute + ':' + total_second + ')' # NOQA if playing_mode == 0: process = 'No Repeat ' + process elif playing_mode == 1: process = 'Repeat All ' + process elif playing_mode == 2: process = 'Repeat Song ' + process elif playing_mode == 3: process = 'Shuffle ' + process elif playing_mode == 4: process = 'Random ' + process else: pass self.screen.addstr(3, self.startcol - 2, process, curses.color_pair(1)) song = self.storage.database['songs'][ self.storage.database['player_info']['player_list'][ self.storage.database['player_info']['idx']]] if 'lyric' not in song.keys() or len(song['lyric']) <= 0: self.now_lyric = 'No Lyrics ~>_<~ \n' if dbus_activity and self.config.get_item('osdlyrics'): self.now_playing = song['song_name'] + ' - ' + song[ 'artist'] + '\n' else: key = now_minute + ':' + now_second for line in song['lyric']: if key in line: if 'tlyric' not in song.keys() or len(song['tlyric']) <= 0: self.now_lyric = line else: self.now_lyric = line for tline in song['tlyric']: if key in tline and self.config.get_item( 'translation'): self.now_lyric = tline + ' || ' + self.now_lyric # NOQA self.now_lyric = re.sub('\[.*?\]', '', self.now_lyric) if dbus_activity and self.config.get_item('osdlyrics'): try: bus = dbus.SessionBus().get_object('org.musicbox.Bus', '/') if self.now_lyric == 'No Lyrics ~>_<~ \n': bus.refresh_lyrics(self.now_playing, dbus_interface='local.musicbox.Lyrics') else: bus.refresh_lyrics(self.now_lyric, dbus_interface='local.musicbox.Lyrics') except Exception as e: log.error(e) pass self.screen.addstr(4, self.startcol - 2, str(self.now_lyric), curses.color_pair(3)) self.screen.refresh() def build_loading(self): self.screen.addstr(7, self.startcol, 'Enjoy Music,loading...', curses.color_pair(1)) self.screen.refresh() # start is the timestamp of this function being called def build_menu(self, datatype, title, datalist, offset, index, step, start): # keep playing info in line 1 curses.noecho() self.screen.move(5, 1) self.screen.clrtobot() self.screen.addstr(5, self.startcol, title, curses.color_pair(1)) if len(datalist) == 0: self.screen.addstr(8, self.startcol, 'Nothing Here! -,-') else: if datatype == 'main': for i in range(offset, min(len(datalist), offset + step)): if i == index: self.screen.addstr(i - offset + 9, self.indented_startcol, '-> ' + str(i) + '. ' + datalist[i], curses.color_pair(2)) else: self.screen.addstr(i - offset + 9, self.startcol, str(i) + '. ' + datalist[i]) elif datatype == 'songs' or datatype == 'fmsongs': iter_range = min(len(datalist), offset + step) for i in range(offset, iter_range): # this item is focus if i == index: self.screen.addstr(i - offset + 8, 0, ' ' * self.startcol) lead = '-> ' + str(i) + '. ' self.screen.addstr(i - offset + 8, self.indented_startcol, lead, curses.color_pair(2)) name = '{}{}{} < {} >'.format( datalist[i]['song_name'], self.space, datalist[i]['artist'], datalist[i]['album_name']) # the length decides whether to scoll if truelen(name) < self.x - self.startcol - 1: self.screen.addstr( i - offset + 8, self.indented_startcol + len(lead), name, curses.color_pair(2)) else: name = scrollstring(name + ' ', start) self.screen.addstr( i - offset + 8, self.indented_startcol + len(lead), str(name), curses.color_pair(2)) else: self.screen.addstr(i - offset + 8, 0, ' ' * self.startcol) self.screen.addstr( i - offset + 8, self.startcol, '{}. {}{}{} < {} >'.format( i, datalist[i]['song_name'], self.space, datalist[i]['artist'], datalist[i]['album_name'])[:int(self.x * 2)]) self.screen.addstr(iter_range - offset + 8, 0, ' ' * self.x) elif datatype == 'artists': for i in range(offset, min(len(datalist), offset + step)): if i == index: self.screen.addstr( i - offset + 9, self.indented_startcol, '-> ' + str(i) + '. ' + datalist[i]['artists_name'] + self.space + str(datalist[i]['alias']), curses.color_pair(2)) else: self.screen.addstr( i - offset + 9, self.startcol, str(i) + '. ' + datalist[i]['artists_name'] + self.space + datalist[i][ 'alias']) elif datatype == 'albums': for i in range(offset, min(len(datalist), offset + step)): if i == index: self.screen.addstr( i - offset + 9, self.indented_startcol, '-> ' + str(i) + '. ' + datalist[i]['albums_name'] + self.space + datalist[i][ 'artists_name'], curses.color_pair(2)) else: self.screen.addstr( i - offset + 9, self.startcol, str(i) + '. ' + datalist[i]['albums_name'] + self.space + datalist[i][ 'artists_name']) elif datatype == 'playlists': for i in range(offset, min(len(datalist), offset + step)): if i == index: self.screen.addstr( i - offset + 9, self.indented_startcol, '-> ' + str(i) + '. ' + datalist[i]['title'], curses.color_pair(2)) else: self.screen.addstr( i - offset + 9, self.startcol, str(i) + '. ' + datalist[i]['title']) elif datatype == 'top_playlists': for i in range(offset, min(len(datalist), offset + step)): if i == index: self.screen.addstr( i - offset + 9, self.indented_startcol, '-> ' + str(i) + '. ' + datalist[i]['playlists_name'] + self.space + datalist[i]['creator_name'], curses.color_pair(2)) else: self.screen.addstr( i - offset + 9, self.startcol, str(i) + '. ' + datalist[i]['playlists_name'] + self.space + datalist[i][ 'creator_name']) elif datatype == 'toplists': for i in range(offset, min(len(datalist), offset + step)): if i == index: self.screen.addstr(i - offset + 9, self.indented_startcol, '-> ' + str(i) + '. ' + datalist[i], curses.color_pair(2)) else: self.screen.addstr(i - offset + 9, self.startcol, str(i) + '. ' + datalist[i]) elif datatype in ('playlist_classes', 'playlist_class_detail'): for i in range(offset, min(len(datalist), offset + step)): if i == index: self.screen.addstr(i - offset + 9, self.indented_startcol, '-> ' + str(i) + '. ' + datalist[i], curses.color_pair(2)) else: self.screen.addstr(i - offset + 9, self.startcol, str(i) + '. ' + datalist[i]) elif datatype == 'djchannels': for i in range(offset, min(len(datalist), offset + step)): if i == index: self.screen.addstr( i - offset + 8, self.indented_startcol, '-> ' + str(i) + '. ' + datalist[i]['song_name'], curses.color_pair(2)) else: self.screen.addstr( i - offset + 8, self.startcol, str(i) + '. ' + datalist[i]['song_name']) elif datatype == 'search': self.screen.move(6, 1) self.screen.clrtobot() self.screen.timeout(-1) self.screen.addstr(8, self.startcol, 'Select Search Type:', curses.color_pair(1)) for i in range(offset, min(len(datalist), offset + step)): if i == index: self.screen.addstr( i - offset + 10, self.indented_startcol, '-> ' + str(i) + '.' + datalist[i - 1], curses.color_pair(2)) else: self.screen.addstr(i - offset + 10, self.startcol, str(i) + '.' + datalist[i - 1]) self.screen.timeout(100) elif datatype == 'help': for i in range(offset, min(len(datalist), offset + step)): if i == index: self.screen.addstr( i - offset + 9, self.indented_startcol, '-> {}. \'{}{} {}'.format( i, (datalist[i][0].upper() + '\'').ljust(11), datalist[i][1], datalist[i][2]), curses.color_pair(2)) else: self.screen.addstr( i - offset + 9, self.startcol, '{}. \'{}{} {}'.format( i, (datalist[i][0].upper() + '\'').ljust(11), datalist[i][1], datalist[i][2])) self.screen.addstr( 20, 6, 'NetEase-MusicBox is written in Python. All copyrighted content is is not hosted by me!') self.screen.addstr(21, 10, 'Press [G] tp go to Github fot more information and to support the project~~') self.screen.addstr(22, self.startcol, 'Built with love to music by omi ENGLISH BY IVAN') self.screen.refresh() def build_search(self, stype): self.screen.timeout(-1) netease = self.netease if stype == 'songs': song_name = self.get_param('Search Songs :') if song_name == '/return': return [] else: try: data = netease.search(song_name, stype=1) song_ids = [] if 'songs' in data['result']: if 'mp3Url' in data['result']['songs']: songs = data['result']['songs'] # if search song result do not has mp3Url # send ids to get mp3Url else: for i in range(0, len(data['result']['songs'])): song_ids.append(data['result']['songs'][i][ 'id']) songs = netease.songs_detail(song_ids) return netease.dig_info(songs, 'songs') except Exception as e: log.error(e) return [] elif stype == 'artists': artist_name = self.get_param('Search Artists :') if artist_name == '/return': return [] else: try: data = netease.search(artist_name, stype=100) if 'artists' in data['result']: artists = data['result']['artists'] return netease.dig_info(artists, 'artists') except Exception as e: log.error(e) return [] elif stype == 'albums': albums_name = self.get_param('Search Albums :') if albums_name == '/return': return [] else: try: data = netease.search(albums_name, stype=10) if 'albums' in data['result']: albums = data['result']['albums'] return netease.dig_info(albums, 'albums') except Exception as e: log.error(e) return [] elif stype == 'search_playlist': search_playlist = self.get_param('Search Playlists :') if search_playlist == '/return': return [] else: try: data = netease.search(search_playlist, stype=1000) if 'playlists' in data['result']: playlists = data['result']['playlists'] return netease.dig_info(playlists, 'top_playlists') except Exception as e: log.error(e) return [] return [] def build_login(self): self.build_login_bar() local_account = self.get_account() local_password = hashlib.md5(self.get_password()).hexdigest() login_info = self.netease.login(local_account, local_password) account = [local_account, local_password] if login_info['code'] != 200: x = self.build_login_error() if x == ord('1'): return self.build_login() else: return -1 else: return [login_info, account] def build_login_bar(self): curses.noecho() self.screen.move(4, 1) self.screen.clrtobot() self.screen.addstr(5, self.startcol, 'Enter your login information(支持手机登陆)', curses.color_pair(1)) self.screen.addstr(8, self.startcol, 'Username :'******'Password :'******'Sorry, the login information was invalid (O_O)#', curses.color_pair(1)) self.screen.addstr(10, self.startcol, '[1] Try Again') self.screen.addstr(11, self.startcol, '[2] Later') self.screen.addstr(14, self.startcol, 'Enter the Captcha :', curses.color_pair(2)) self.screen.refresh() x = self.screen.getch() self.screen.timeout(100) # restore the screen timeout return x def get_account(self): self.screen.timeout(-1) # disable the screen timeout curses.echo() account = self.screen.getstr(8, self.startcol + 6, 60) self.screen.timeout(100) # restore the screen timeout return account def get_password(self): self.screen.timeout(-1) # disable the screen timeout curses.noecho() password = self.screen.getstr(9, self.startcol + 6, 60) self.screen.timeout(100) # restore the screen timeout return password def get_param(self, prompt_string): # keep playing info in line 1 curses.echo() self.screen.move(4, 1) self.screen.clrtobot() self.screen.addstr(5, self.startcol, prompt_string, curses.color_pair(1)) self.screen.refresh() info = self.screen.getstr(10, self.startcol, 60) if info == '': return '/return' elif info.strip() is '': return self.get_param(prompt_string) else: return info def update_size(self): # get terminal size size = terminalsize.get_terminal_size() self.x = max(size[0], 10) self.y = max(size[1], 25) # update intendations curses.resizeterm(self.y, self.x) self.startcol = int(float(self.x) / 5) self.indented_startcol = max(self.startcol - 3, 0) self.update_space() self.screen.clear() self.screen.refresh() def update_space(self): if self.x > 140: self.space = ' - ' elif self.x > 80: self.space = ' - ' else: self.space = ' - ' self.screen.refresh()
class Menu: def __init__(self): reload(sys) sys.setdefaultencoding('UTF-8') self.config = Config() self.datatype = 'main' self.title = '网易云音乐' self.datalist = [ '排行榜', '艺术家', '新碟上架', '精选歌单', '我的歌单', '主播电台', '每日推荐', '私人FM', '搜索', '帮助' ] self.offset = 0 self.index = 0 self.storage = Storage() self.storage.load() self.collection = self.storage.database['collections'][0] self.player = Player() self.player.playing_song_changed_callback = self.song_changed_callback self.cache = Cache() self.ui = Ui() self.netease = NetEase() self.screen = curses.initscr() self.screen.keypad(1) self.step = 10 self.stack = [] self.djstack = [] self.userid = self.storage.database['user']['user_id'] self.username = self.storage.database['user']['nickname'] self.resume_play = True self.at_playing_list = False signal.signal(signal.SIGWINCH, self.change_term) signal.signal(signal.SIGINT, self.send_kill) self.START = time.time() def change_term(self, signum, frame): self.ui.screen.clear() self.ui.screen.refresh() def send_kill(self, signum, fram): self.player.stop() self.cache.quit() self.storage.save() curses.endwin() sys.exit() def update_alert(self, version): latest = Menu().check_version() if latest != version and latest != 0: notify('MusicBox Update is available', 1) time.sleep(0.5) notify( 'NetEase-MusicBox installed version:' + version + '\nNetEase-MusicBox latest version:' + latest, 0) def check_version(self): # 检查更新 && 签到 try: mobilesignin = self.netease.daily_signin(0) if mobilesignin != -1 and mobilesignin['code'] != -2: notify('Mobile signin success', 1) time.sleep(0.5) pcsignin = self.netease.daily_signin(1) if pcsignin != -1 and pcsignin['code'] != -2: notify('PC signin success', 1) tree = ET.ElementTree( ET.fromstring(str(self.netease.get_version()))) root = tree.getroot() return root[0][4][0][0].text except TypeError as e: log.error(e) return 0 def start_fork(self, version): pid = os.fork() if pid == 0: Menu().update_alert(version) else: Menu().start() def play_pause(self): if len(self.storage.database['player_info']['player_list']) == 0: return if self.player.pause_flag: self.player.resume() else: self.player.pause() time.sleep(0.1) def next_song(self): if len(self.storage.database['player_info']['player_list']) == 0: return self.player.next() time.sleep(0.1) def previous_song(self): if len(self.storage.database['player_info']['player_list']) == 0: return self.player.prev() time.sleep(0.1) # 全局加星歌曲 def star_song(self): return_data = self.request_api(self.netease.fm_like, self.player.get_playing_id()) if return_data != -1: notify("Added successfully!", 0) else: notify("Existing song!", 0) def bind_keys(self): if bind_global: keybinder.bind(self.config.get_item('global_play_pause'), self.play_pause) keybinder.bind(self.config.get_item('global_next'), self.next_song) keybinder.bind(self.config.get_item('global_previous'), self.previous_song) keybinder.bind(self.config.get_item('global_star'), self.star_song) def unbind_keys(self): if bind_global: keybinder.unbind(self.config.get_item('global_play_pause')) keybinder.unbind(self.config.get_item('global_next')) keybinder.unbind(self.config.get_item('global_previous')) keybinder.unbind(self.config.get_item('global_star')) def start(self): self.START = time.time() // 1 self.ui.build_menu(self.datatype, self.title, self.datalist, self.offset, self.index, self.step, self.START) self.ui.build_process_bar( self.player.process_location, self.player.process_length, self.player.playing_flag, self.player.pause_flag, self.storage.database['player_info']['playing_mode']) self.stack.append([ self.datatype, self.title, self.datalist, self.offset, self.index ]) if bind_global: try: self.bind_keys() except KeyError as e: log.warning(e) show_lyrics_new_process() while True: datatype = self.datatype title = self.title datalist = self.datalist offset = self.offset idx = index = self.index step = self.step stack = self.stack self.screen.timeout(500) key = self.screen.getch() if bind_global: keybinder.gtk.main_iteration(False) self.ui.screen.refresh() # term resize if key == -1: self.ui.update_size() self.player.update_size() # 退出 if key == ord('q'): try: self.unbind_keys() except KeyError as e: log.warning(e) break # 退出并清除用户信息 if key == ord('w'): self.storage.database['user'] = { 'username': '', 'password': '', 'user_id': '', 'nickname': '', } try: os.remove(self.storage.cookie_path) except OSError as e: log.error(e) break break # 上移 elif key == ord('k'): # turn page if at beginning if idx == offset: if offset == 0: continue self.offset -= step # 移动光标到最后一列 self.index = offset - 1 else: self.index = carousel( offset, min(len(datalist), offset + step) - 1, idx - 1) self.START = time.time() # 下移 elif key == ord('j'): # turn page if at end if idx == min(len(datalist), offset + step) - 1: if offset + step >= len(datalist): continue self.offset += step # 移动光标到第一列 self.index = offset + step else: self.index = carousel( offset, min(len(datalist), offset + step) - 1, idx + 1) self.START = time.time() # 数字快捷键 elif ord('0') <= key <= ord('9'): if self.datatype == ('songs', 'djchannels', 'help'): continue idx = key - ord('0') self.ui.build_menu(self.datatype, self.title, self.datalist, self.offset, idx, self.step, self.START) self.ui.build_loading() self.dispatch_enter(idx) self.index = 0 self.offset = 0 # 向上翻页 elif key == ord('u'): if offset == 0: continue self.START = time.time() self.offset -= step # e.g. 23 - 10 = 13 --> 10 self.index = (index - step) // step * step # 向下翻页 elif key == ord('d'): if offset + step >= len(datalist): continue self.START = time.time() self.offset += step # e.g. 23 + 10 = 33 --> 30 self.index = (index + step) // step * step # 前进 elif key == ord('l') or key == 10: if self.datatype == ('songs', 'djchannels', 'help') or len(self.datalist) <= 0: continue self.START = time.time() self.ui.build_loading() self.dispatch_enter(idx) self.index = 0 self.offset = 0 # 回退 elif key == ord('h'): # if not main menu if len(self.stack) == 1: continue self.START = time.time() up = stack.pop() self.datatype = up[0] self.title = up[1] self.datalist = up[2] self.offset = up[3] self.index = up[4] self.at_playing_list = False # 搜索 elif key == ord('f'): # 8 is the 'search' menu self.dispatch_enter(8) # 播放下一曲 elif key == ord(']'): self.next_song() # 播放上一曲 elif key == ord('['): self.previous_song() # 增加音量 elif key == ord('='): self.player.volume_up() # 减少音量 elif key == ord('-'): self.player.volume_down() # 随机播放 elif key == ord('?'): if len(self.storage.database['player_info'] ['player_list']) == 0: continue self.player.shuffle() time.sleep(0.1) # 喜爱 elif key == ord(','): return_data = self.request_api(self.netease.fm_like, self.player.get_playing_id()) if return_data != -1: notify('Added successfully!', 0) else: notify('Existing song!', 0) # 删除FM elif key == ord('.'): if self.datatype == 'fmsongs': if len(self.storage.database['player_info'] ['player_list']) == 0: continue self.player.next() return_data = self.request_api( self.netease.fm_trash, self.player.get_playing_id()) if return_data != -1: notify('Deleted successfully!', 0) time.sleep(0.1) # 下一FM elif key == ord('/'): if self.datatype == 'fmsongs': if len(self.storage.database['player_info'] ['player_list']) == 0: continue self.player.next() time.sleep(0.1) # 播放、暂停 elif key == ord(' '): # If not open a new playing list, just play and pause. try: if self.datalist[idx]['song_id'] == self.player.playing_id: self.player.play_and_pause( self.storage.database['player_info']['idx']) time.sleep(0.1) continue except (TypeError, KeyError) as e: log.error(e) pass # If change to a new playing list. Add playing list and play. if datatype == 'songs': self.resume_play = False self.player.new_player_list('songs', self.title, self.datalist, -1) self.player.end_callback = None self.player.play_and_pause(idx) self.at_playing_list = True elif datatype == 'djchannels': self.resume_play = False self.player.new_player_list('djchannels', self.title, self.datalist, -1) self.player.end_callback = None self.player.play_and_pause(idx) self.at_playing_list = True elif datatype == 'fmsongs': self.resume_play = False self.storage.database['player_info']['playing_mode'] = 0 self.player.new_player_list('fmsongs', self.title, self.datalist, -1) self.player.end_callback = self.fm_callback self.player.play_and_pause(idx) self.at_playing_list = True else: self.player.play_and_pause( self.storage.database['player_info']['idx']) time.sleep(0.1) # 加载当前播放列表 elif key == ord('p'): self.show_playing_song() # 播放模式切换 elif key == ord('P'): self.storage.database['player_info']['playing_mode'] = ( self.storage.database['player_info']['playing_mode'] + 1) % 5 # 添加到打碟歌单 elif key == ord('a'): if datatype == 'songs' and len(datalist) != 0: self.djstack.append(datalist[idx]) elif datatype == 'artists': pass # 加载打碟歌单 elif key == ord('z'): self.stack.append([datatype, title, datalist, offset, index]) self.datatype = 'songs' self.title = '网易云音乐 > 打碟' self.datalist = self.djstack self.offset = 0 self.index = 0 # 添加到本地收藏 elif key == ord('s'): if (datatype == 'songs' or datatype == 'djchannels') and len(datalist) != 0: self.collection.append(datalist[idx]) notify('Added successfully', 0) # 加载本地收藏 elif key == ord('c'): self.stack.append([datatype, title, datalist, offset, index]) self.datatype = 'songs' self.title = '网易云音乐 > 本地收藏' self.datalist = self.collection self.offset = 0 self.index = 0 # 从当前列表移除 elif key == ord('r'): if (datatype == 'songs' or datatype == 'djchannels') and len(datalist) != 0: self.datalist.pop(idx) self.index = carousel( offset, min(len(datalist), offset + step) - 1, idx) # 当前项目下移 elif key == ord('J'): if datatype != 'main' and len( datalist) != 0 and idx + 1 != len(self.datalist): self.START = time.time() song = self.datalist.pop(idx) self.datalist.insert(idx + 1, song) self.index = idx + 1 # 翻页 if self.index >= offset + step: self.offset = offset + step # 当前项目上移 elif key == ord('K'): if datatype != 'main' and len(datalist) != 0 and idx != 0: self.START = time.time() song = self.datalist.pop(idx) self.datalist.insert(idx - 1, song) self.index = idx - 1 # 翻页 if self.index < offset: self.offset = offset - step elif key == ord('m'): if datatype != 'main': self.stack.append( [datatype, title, datalist, offset, index]) self.datatype = self.stack[0][0] self.title = self.stack[0][1] self.datalist = self.stack[0][2] self.offset = 0 self.index = 0 elif key == ord('g'): if datatype == 'help': webbrowser.open_new_tab( 'https://github.com/darknessomi/musicbox') # 开始下载 elif key == ord('C'): s = self.datalist[idx] cache_thread = threading.Thread( target=self.player.cacheSong1time, args=(s['song_id'], s['song_name'], s['artist'], s['mp3_url'])) cache_thread.start() elif key == ord('i'): if self.player.playing_id != -1: webbrowser.open_new_tab('http://music.163.com/#/song?id=' + str(self.player.playing_id)) self.ui.build_process_bar( self.player.process_location, self.player.process_length, self.player.playing_flag, self.player.pause_flag, self.storage.database['player_info']['playing_mode']) self.ui.build_menu(self.datatype, self.title, self.datalist, self.offset, self.index, self.step, self.START) self.player.stop() self.cache.quit() self.storage.save() curses.endwin() def dispatch_enter(self, idx): # The end of stack netease = self.netease datatype = self.datatype title = self.title datalist = self.datalist offset = self.offset index = self.index self.stack.append([datatype, title, datalist, offset, index]) if idx > len(self.datalist): return False if datatype == 'main': self.choice_channel(idx) # 该艺术家的热门歌曲 elif datatype == 'artists': artist_id = datalist[idx]['artist_id'] songs = netease.artists(artist_id) self.datatype = 'songs' self.datalist = netease.dig_info(songs, 'songs') self.title += ' > ' + datalist[idx]['artists_name'] # 该专辑包含的歌曲 elif datatype == 'albums': album_id = datalist[idx]['album_id'] songs = netease.album(album_id) self.datatype = 'songs' self.datalist = netease.dig_info(songs, 'songs') self.title += ' > ' + datalist[idx]['albums_name'] # 精选歌单选项 elif datatype == 'playlists': data = self.datalist[idx] self.datatype = data['datatype'] self.datalist = netease.dig_info(data['callback'](), self.datatype) self.title += ' > ' + data['title'] # 全站置顶歌单包含的歌曲 elif datatype == 'top_playlists': playlist_id = datalist[idx]['playlist_id'] songs = netease.playlist_detail(playlist_id) self.datatype = 'songs' self.datalist = netease.dig_info(songs, 'songs') self.title += ' > ' + datalist[idx]['playlists_name'] # 分类精选 elif datatype == 'playlist_classes': # 分类名称 data = self.datalist[idx] self.datatype = 'playlist_class_detail' self.datalist = netease.dig_info(data, self.datatype) self.title += ' > ' + data # 某一分类的详情 elif datatype == 'playlist_class_detail': # 子类别 data = self.datalist[idx] self.datatype = 'top_playlists' self.datalist = netease.dig_info(netease.top_playlists(data), self.datatype) self.title += ' > ' + data # 歌曲榜单 elif datatype == 'toplists': songs = netease.top_songlist(idx) self.title += ' > ' + self.datalist[idx] self.datalist = netease.dig_info(songs, 'songs') self.datatype = 'songs' # 搜索菜单 elif datatype == 'search': ui = self.ui self.index = 0 self.offset = 0 if idx == 0: # 搜索结果可以用top_playlists处理 self.datatype = 'top_playlists' self.datalist = ui.build_search('search_playlist') self.title = '精选歌单搜索列表' elif idx == 1: self.datatype = 'songs' self.datalist = ui.build_search('songs') self.title = '歌曲搜索列表' elif idx == 2: self.datatype = 'artists' self.datalist = ui.build_search('artists') self.title = '艺术家搜索列表' elif idx == 3: self.datatype = 'albums' self.datalist = ui.build_search('albums') self.title = '专辑搜索列表' def show_playing_song(self): if len(self.storage.database['player_info']['player_list']) == 0: return if not self.at_playing_list: self.stack.append([ self.datatype, self.title, self.datalist, self.offset, self.index ]) self.at_playing_list = True self.datatype = self.storage.database['player_info'][ 'player_list_type'] self.title = self.storage.database['player_info']['player_list_title'] self.datalist = [] for i in self.storage.database['player_info']['player_list']: self.datalist.append(self.storage.database['songs'][i]) self.index = self.storage.database['player_info']['idx'] self.offset = self.storage.database['player_info'][ 'idx'] / self.step * self.step if self.resume_play: if self.datatype == 'fmsongs': self.player.end_callback = self.fm_callback else: self.player.end_callback = None self.storage.database['player_info']['idx'] = -1 self.player.play_and_pause(self.index) self.resume_play = False def song_changed_callback(self): if self.at_playing_list: self.show_playing_song() def fm_callback(self): log.debug('FM CallBack.') data = self.get_new_fm() self.player.append_songs(data) if self.datatype == 'fmsongs': if len(self.storage.database['player_info']['player_list']) == 0: return self.datatype = self.storage.database['player_info'][ 'player_list_type'] self.title = self.storage.database['player_info'][ 'player_list_title'] self.datalist = [] for i in self.storage.database['player_info']['player_list']: self.datalist.append(self.storage.database['songs'][i]) self.index = self.storage.database['player_info']['idx'] self.offset = self.storage.database['player_info'][ 'idx'] / self.step * self.step def request_api(self, func, *args): if self.storage.database['user']['user_id'] != '': result = func(*args) if result != -1: return result log.debug('Re Login.') user_info = {} if self.storage.database['user']['username'] != '': user_info = self.netease.login( self.storage.database['user']['username'], self.storage.database['user']['password']) if self.storage.database['user'][ 'username'] == '' or user_info['code'] != 200: data = self.ui.build_login() # 取消登录 if data == -1: return -1 user_info = data[0] self.storage.database['user']['username'] = data[1][0] self.storage.database['user']['password'] = data[1][1] self.storage.database['user']['user_id'] = user_info['account'][ 'id'] self.storage.database['user']['nickname'] = user_info['profile'][ 'nickname'] self.userid = self.storage.database['user']['user_id'] self.username = self.storage.database['user']['nickname'] return func(*args) def get_new_fm(self): myplaylist = [] for count in range(0, 1): data = self.request_api(self.netease.personal_fm) if data == -1: break myplaylist += data time.sleep(0.2) return self.netease.dig_info(myplaylist, 'fmsongs') def choice_channel(self, idx): # 排行榜 netease = self.netease if idx == 0: self.datalist = netease.return_toplists() self.title += ' > 排行榜' self.datatype = 'toplists' # 艺术家 elif idx == 1: artists = netease.top_artists() self.datalist = netease.dig_info(artists, 'artists') self.title += ' > 艺术家' self.datatype = 'artists' # 新碟上架 elif idx == 2: albums = netease.new_albums() self.datalist = netease.dig_info(albums, 'albums') self.title += ' > 新碟上架' self.datatype = 'albums' # 精选歌单 elif idx == 3: self.datalist = [{ 'title': '全站置顶', 'datatype': 'top_playlists', 'callback': netease.top_playlists }, { 'title': '分类精选', 'datatype': 'playlist_classes', 'callback': netease.playlist_classes }] self.title += ' > 精选歌单' self.datatype = 'playlists' # 我的歌单 elif idx == 4: myplaylist = self.request_api(self.netease.user_playlist, self.userid) if myplaylist == -1: return self.datatype = 'top_playlists' self.datalist = netease.dig_info(myplaylist, self.datatype) self.title += ' > ' + self.username + ' 的歌单' # 主播电台 elif idx == 5: self.datatype = 'djchannels' self.title += ' > 主播电台' self.datalist = netease.djchannels() # 每日推荐 elif idx == 6: self.datatype = 'songs' self.title += ' > 每日推荐' myplaylist = self.request_api(self.netease.recommend_playlist) if myplaylist == -1: return self.datalist = self.netease.dig_info(myplaylist, self.datatype) # 私人FM elif idx == 7: self.datatype = 'fmsongs' self.title += ' > 私人FM' self.datalist = self.get_new_fm() # 搜索 elif idx == 8: self.datatype = 'search' self.title += ' > 搜索' self.datalist = ['歌曲', '艺术家', '专辑', '网易精选集'] # 帮助 elif idx == 9: self.datatype = 'help' self.title += ' > 帮助' self.datalist = shortcut self.offset = 0 self.index = 0
class Menu: def __init__(self): reload(sys) sys.setdefaultencoding('UTF-8') self.config = Config() self.datatype = 'main' self.title = '网易云音乐' self.datalist = ['排行榜', '艺术家', '新碟上架', '精选歌单', '我的歌单', 'DJ节目', '每日推荐', '私人FM', '搜索', '帮助'] self.offset = 0 self.index = 0 self.storage = Storage() self.storage.load() self.collection = self.storage.database['collections'][0] self.player = Player() self.cache = Cache() self.ui = Ui() self.netease = NetEase() self.screen = curses.initscr() self.screen.keypad(1) self.step = 10 self.stack = [] self.djstack = [] self.userid = None self.username = None self.resume_play = True self.at_playing_list = False signal.signal(signal.SIGWINCH, self.change_term) signal.signal(signal.SIGINT, self.send_kill) self.START = time.time() def change_term(self, signum, frame): self.ui.screen.clear() self.ui.screen.refresh() def send_kill(self, signum, fram): self.player.stop() self.cache.quit() self.storage.save() curses.endwin() sys.exit() def start(self): self.START = time.time() // 1 self.ui.build_menu(self.datatype, self.title, self.datalist, self.offset, self.index, self.step, self.START) self.ui.build_process_bar(self.player.process_location, self.player.process_length, self.player.playing_flag, self.player.pause_flag, self.storage.database['player_info']['playing_mode']) self.stack.append([self.datatype, self.title, self.datalist, self.offset, self.index]) while True: datatype = self.datatype title = self.title datalist = self.datalist offset = self.offset idx = index = self.index step = self.step stack = self.stack djstack = self.djstack self.screen.timeout(500) key = self.screen.getch() self.ui.screen.refresh() # term resize if key == -1: self.ui.update_size() self.player.update_size() # 退出 if key == ord('q'): break # 退出并清除用户信息 if key == ord('w'): self.storage.database['user'] = { "username": "", "password": "", "user_id": "", "nickname": "", } os.remove(self.storage.cookie_path) break # 上移 elif key == ord('k'): self.index = carousel(offset, min(len(datalist), offset + step) - 1, idx - 1) self.START = time.time() # 下移 elif key == ord('j'): self.index = carousel(offset, min(len(datalist), offset + step) - 1, idx + 1) self.START = time.time() # 数字快捷键 elif ord('0') <= key <= ord('9'): if self.datatype == 'songs' or self.datatype == 'djchannels' or self.datatype == 'help': continue idx = key - ord('0') self.ui.build_menu(self.datatype, self.title, self.datalist, self.offset, idx, self.step, self.START) self.ui.build_loading() self.dispatch_enter(idx) self.index = 0 self.offset = 0 # 向上翻页 elif key == ord('u'): if offset == 0: continue self.START = time.time() self.offset -= step # e.g. 23 - 10 = 13 --> 10 self.index = (index - step) // step * step # 向下翻页 elif key == ord('d'): if offset + step >= len(datalist): continue self.START = time.time() self.offset += step # e.g. 23 + 10 = 33 --> 30 self.index = (index + step) // step * step # 前进 elif key == ord('l') or key == 10: if self.datatype == 'songs' or self.datatype == 'djchannels' or self.datatype == 'help': continue self.START = time.time() self.ui.build_loading() self.dispatch_enter(idx) self.index = 0 self.offset = 0 # 回退 elif key == ord('h'): # if not main menu if len(self.stack) == 1: continue self.START = time.time() up = stack.pop() self.datatype = up[0] self.title = up[1] self.datalist = up[2] self.offset = up[3] self.index = up[4] self.at_playing_list = False; # 搜索 elif key == ord('f'): # 8 is the 'search' menu self.dispatch_enter(8) # 播放下一曲 elif key == ord(']'): if len(self.storage.database["player_info"]["player_list"]) == 0: continue self.player.next() time.sleep(0.1) # 播放上一曲 elif key == ord('['): if len(self.storage.database["player_info"]["player_list"]) == 0: continue self.player.prev() time.sleep(0.1) # 增加音量 elif key == ord('='): self.player.volume_up() # 减少音量 elif key == ord('-'): self.player.volume_down() # 随机播放 elif key == ord('?'): if len(self.storage.database["player_info"]["player_list"]) == 0: continue self.player.shuffle() time.sleep(0.1) # 喜爱 elif key == ord(','): self.netease.fm_like(self.player.get_playing_id()) # 删除FM elif key == ord('.'): if self.datatype == 'fmsongs': if len(self.storage.database["player_info"]["player_list"]) == 0: continue self.player.next() self.netease.fm_trash(self.player.get_playing_id()) time.sleep(0.1) # 下一FM elif key == ord('/'): if self.datatype == 'fmsongs': if len(self.storage.database["player_info"]["player_list"]) == 0: continue self.player.next() time.sleep(0.1) # 播放、暂停 elif key == ord(' '): # If not open a new playing list, just play and pause. try: if self.datalist[idx] == self.storage.database["songs"][str(self.player.playing_id)]: self.player.play_and_pause(self.storage.database['player_info']['idx']) time.sleep(0.1) continue except: pass # If change to a new playing list. Add playing list and play. if datatype == 'songs': self.resume_play = False self.player.new_player_list('songs', self.title, self.datalist, -1) self.player.end_callback = None self.player.play_and_pause(idx) self.at_playing_list = True elif datatype == 'djchannels': self.resume_play = False self.player.new_player_list('djchannels', self.title, self.datalist, -1) self.player.end_callback = None self.player.play_and_pause(idx) self.at_playing_list = True elif datatype == 'fmsongs': self.resume_play = False self.storage.database['player_info']['playing_mode'] = 0 self.player.new_player_list('fmsongs', self.title, self.datalist, -1) self.player.end_callback = self.fm_callback self.player.play_and_pause(idx) self.at_playing_list = True else: self.player.play_and_pause(self.storage.database['player_info']['idx']) time.sleep(0.1) # 加载当前播放列表 elif key == ord('p'): if len(self.storage.database['player_info']['player_list']) == 0: continue if not self.at_playing_list: self.stack.append([self.datatype, self.title, self.datalist, self.offset, self.index]) self.at_playing_list = True self.datatype = self.storage.database['player_info']['player_list_type'] self.title = self.storage.database['player_info']['player_list_title'] self.datalist = [] for i in self.storage.database['player_info']['player_list']: self.datalist.append(self.storage.database['songs'][i]) self.index = self.storage.database['player_info']['idx'] self.offset = self.storage.database['player_info']['idx'] / self.step * self.step if self.resume_play: if self.datatype == "fmsongs": self.player.end_callback = self.fm_callback else: self.player.end_callback = None self.storage.database['player_info']['idx'] = -1 self.player.play_and_pause(self.index) self.resume_play = False # 播放模式切换 elif key == ord('P'): self.storage.database['player_info']['playing_mode'] = \ (self.storage.database['player_info']['playing_mode'] + 1) % 5 # 添加到打碟歌单 elif key == ord('a'): if datatype == 'songs' and len(datalist) != 0: self.djstack.append(datalist[idx]) elif datatype == 'artists': pass # 加载打碟歌单 elif key == ord('z'): self.stack.append([datatype, title, datalist, offset, index]) self.datatype = 'songs' self.title = '网易云音乐 > 打碟' self.datalist = self.djstack self.offset = 0 self.index = 0 # 添加到收藏歌曲 elif key == ord('s'): if (datatype == 'songs' or datatype == 'djchannels') and len(datalist) != 0: self.collection.append(datalist[idx]) # 加载收藏歌曲 elif key == ord('c'): self.stack.append([datatype, title, datalist, offset, index]) self.datatype = 'songs' self.title = '网易云音乐 > 收藏' self.datalist = self.collection self.offset = 0 self.index = 0 # 从当前列表移除 elif key == ord('r'): if datatype != 'main' and len(datalist) != 0: self.datalist.pop(idx) self.index = carousel(offset, min(len(datalist), offset + step) - 1, idx) # 当前项目下移 elif key == ord("J"): if datatype != 'main' and len(datalist) != 0 and idx + 1 != len(self.datalist): self.START = time.time() song = self.datalist.pop(idx) self.datalist.insert(idx + 1, song) self.index = idx + 1 # 翻页 if self.index >= offset + step: self.offset = offset + step # 当前项目上移 elif key == ord("K"): if datatype != 'main' and len(datalist) != 0 and idx != 0: self.START = time.time() song = self.datalist.pop(idx) self.datalist.insert(idx - 1, song) self.index = idx - 1 # 翻页 if self.index < offset: self.offset = offset - step elif key == ord('m'): if datatype != 'main': self.stack.append([datatype, title, datalist, offset, index]) self.datatype = self.stack[0][0] self.title = self.stack[0][1] self.datalist = self.stack[0][2] self.offset = 0 self.index = 0 elif key == ord('g'): if datatype == 'help': webbrowser.open_new_tab('https://github.com/darknessomi/musicbox') self.ui.build_process_bar(self.player.process_location, self.player.process_length, self.player.playing_flag, self.player.pause_flag, self.storage.database['player_info']['playing_mode']) self.ui.build_menu(self.datatype, self.title, self.datalist, self.offset, self.index, self.step, self.START) self.player.stop() self.cache.quit() self.storage.save() curses.endwin() def dispatch_enter(self, idx): # The end of stack netease = self.netease datatype = self.datatype title = self.title datalist = self.datalist offset = self.offset index = self.index self.stack.append([datatype, title, datalist, offset, index]) if datatype == 'main': self.choice_channel(idx) # 该艺术家的热门歌曲 elif datatype == 'artists': artist_id = datalist[idx]['artist_id'] songs = netease.artists(artist_id) self.datatype = 'songs' self.datalist = netease.dig_info(songs, 'songs') self.title += ' > ' + datalist[idx]['artists_name'] # 该专辑包含的歌曲 elif datatype == 'albums': album_id = datalist[idx]['album_id'] songs = netease.album(album_id) self.datatype = 'songs' self.datalist = netease.dig_info(songs, 'songs') self.title += ' > ' + datalist[idx]['albums_name'] # 精选歌单选项 elif datatype == 'playlists': data = self.datalist[idx] self.datatype = data['datatype'] self.datalist = netease.dig_info(data['callback'](), self.datatype) self.title += ' > ' + data['title'] # 全站置顶歌单包含的歌曲 elif datatype == 'top_playlists': log.debug(datalist) playlist_id = datalist[idx]['playlist_id'] songs = netease.playlist_detail(playlist_id) self.datatype = 'songs' self.datalist = netease.dig_info(songs, 'songs') self.title += ' > ' + datalist[idx]['playlists_name'] # 分类精选 elif datatype == 'playlist_classes': # 分类名称 data = self.datalist[idx] self.datatype = 'playlist_class_detail' self.datalist = netease.dig_info(data, self.datatype) self.title += ' > ' + data log.debug(self.datalist) # 某一分类的详情 elif datatype == 'playlist_class_detail': # 子类别 data = self.datalist[idx] self.datatype = 'top_playlists' self.datalist = netease.dig_info(netease.top_playlists(data), self.datatype) log.debug(self.datalist) self.title += ' > ' + data # 歌曲榜单 elif datatype == 'toplists': songs = netease.top_songlist(idx) self.title += ' > ' + self.datalist[idx] self.datalist = netease.dig_info(songs, 'songs') self.datatype = 'songs' # 搜索菜单 elif datatype == 'search': ui = self.ui # no need to do stack.append, Otherwise there will be a bug when you input key 'h' to return # if idx in range(1, 5): # self.stack.append([self.datatype, self.title, self.datalist, self.offset, self.index]) self.index = 0 self.offset = 0 if idx == 0: # 搜索结果可以用top_playlists处理 self.datatype = 'top_playlists' self.datalist = ui.build_search('search_playlist') self.title = '精选歌单搜索列表' elif idx == 1: self.datatype = 'songs' self.datalist = ui.build_search('songs') self.title = '歌曲搜索列表' elif idx == 2: self.datatype = 'artists' self.datalist = ui.build_search('artists') self.title = '艺术家搜索列表' elif idx == 3: self.datatype = 'albums' self.datalist = ui.build_search('albums') self.title = '专辑搜索列表' def fm_callback(self): log.debug("FM CallBack.") data = self.get_new_fm() self.player.append_songs(data) if self.datatype == 'fmsongs': if len(self.storage.database['player_info']['player_list']) == 0: return self.datatype = self.storage.database['player_info']['player_list_type'] self.title = self.storage.database['player_info']['player_list_title'] self.datalist = [] for i in self.storage.database['player_info']['player_list']: self.datalist.append(self.storage.database['songs'][i]) self.index = self.storage.database['player_info']['idx'] self.offset = self.storage.database['player_info']['idx'] / self.step * self.step def get_new_fm(self): if self.userid is None: if self.storage.database['user']['user_id'] == "": # 使用本地存储了账户登录 if self.storage.database['user']['username'] != "": user_info = self.netease.login(self.storage.database['user']['username'], self.storage.database['user']['password']) # 本地没有存储账户,或本地账户失效,则引导录入 if self.storage.database['user']['username'] == "" or user_info['code'] != 200: data = self.ui.build_login() # 取消登录 if data == -1: return user_info = data[0] self.storage.database['user']['username'] = data[1][0] self.storage.database['user']['password'] = data[1][1] self.storage.database['user']['user_id'] = user_info['account']['id'] self.storage.database['user']['nickname'] = user_info['profile']['nickname'] self.username = user_info['profile']['nickname'] self.userid = user_info['account']['id'] else: self.userid = self.storage.database['user']['user_id'] myplaylist = [] for count in range(0, 1): myplaylist += self.netease.personal_fm() time.sleep(0.2) return self.netease.dig_info(myplaylist, "fmsongs") def choice_channel(self, idx): # 排行榜 netease = self.netease if idx == 0: self.datalist = netease.return_toplists() self.title += ' > 排行榜' self.datatype = 'toplists' # 艺术家 elif idx == 1: artists = netease.top_artists() self.datalist = netease.dig_info(artists, 'artists') self.title += ' > 艺术家' self.datatype = 'artists' # 新碟上架 elif idx == 2: albums = netease.new_albums() self.datalist = netease.dig_info(albums, 'albums') self.title += ' > 新碟上架' self.datatype = 'albums' # 精选歌单 elif idx == 3: self.datalist = [ { 'title': '全站置顶', 'datatype': 'top_playlists', 'callback': netease.top_playlists }, { 'title': '分类精选', 'datatype': 'playlist_classes', 'callback': netease.playlist_classes } ] self.title += ' > 精选歌单' self.datatype = 'playlists' # 我的歌单 elif idx == 4: # 未登录 if self.userid is None: if self.storage.database['user']['user_id'] == "": # 使用本地存储了账户登录 if self.storage.database['user']['username'] != "": user_info = netease.login(self.storage.database['user']['username'], self.storage.database['user']['password']) # 本地没有存储账户,或本地账户失效,则引导录入 if self.storage.database['user']['username'] == "" or user_info['code'] != 200: data = self.ui.build_login() # 取消登录 if data == -1: return user_info = data[0] self.storage.database['user']['username'] = data[1][0] self.storage.database['user']['password'] = data[1][1] self.storage.database['user']['user_id'] = user_info['account']['id'] self.storage.database['user']['nickname'] = user_info['profile']['nickname'] self.username = user_info['profile']['nickname'] self.userid = user_info['account']['id'] else: self.userid = self.storage.database['user']['user_id'] # 读取登录之后的用户歌单 self.username = self.storage.database['user']['nickname'] myplaylist = netease.user_playlist(self.userid) self.datatype = 'top_playlists' self.datalist = netease.dig_info(myplaylist, self.datatype) self.title += ' > ' + self.username + ' 的歌单' # DJ节目 elif idx == 5: self.datatype = 'djchannels' self.title += ' > DJ节目' self.datalist = netease.djchannels() # 每日推荐 elif idx == 6: self.datatype = 'songs' self.title += ' > 每日推荐' if self.userid is None: if self.storage.database['user']['user_id'] == "": # 使用本地存储了账户登录 if self.storage.database['user']['username'] != "": user_info = netease.login(self.storage.database['user']['username'], self.storage.database['user']['password']) # 本地没有存储账户,或本地账户失效,则引导录入 if self.storage.database['user']['username'] == "" or user_info['code'] != 200: data = self.ui.build_login() # 取消登录 if data == -1: return user_info = data[0] self.storage.database['user']['username'] = data[1][0] self.storage.database['user']['password'] = data[1][1] self.storage.database['user']['user_id'] = user_info['account']['id'] self.storage.database['user']['nickname'] = user_info['profile']['nickname'] self.username = user_info['profile']['nickname'] self.userid = user_info['account']['id'] else: self.userid = self.storage.database['user']['user_id'] # myplaylist = self.netease.recommend_playlist() self.datalist = self.netease.dig_info(myplaylist, self.datatype) # 私人FM elif idx == 7: self.datatype = 'fmsongs' self.title += ' > 私人FM' self.datalist = self.get_new_fm() # 搜索 elif idx == 8: self.datatype = 'search' self.title += ' > 搜索' self.datalist = ['歌曲', '艺术家', '专辑', '网易精选集'] # 帮助 elif idx == 9: self.datatype = 'help' self.title += ' > 帮助' self.datalist = shortcut self.offset = 0 self.index = 0
class Menu(object): def __init__(self): reload(sys) sys.setdefaultencoding('UTF-8') self.config = Config() self.datatype = 'main' self.title = 'NetEase Music' self.datalist = ['Charts', 'Artists', 'New Songs', 'Songs', 'My Songs', 'Radio Stations', 'Recommended', 'Personal Radio', 'Search', 'Help'] self.offset = 0 self.index = 0 self.storage = Storage() self.storage.load() self.collection = self.storage.database['collections'][0] self.player = Player() self.player.playing_song_changed_callback = self.song_changed_callback self.cache = Cache() self.ui = Ui() self.netease = NetEase() self.screen = curses.initscr() self.screen.keypad(1) self.step = 10 self.stack = [] self.djstack = [] self.userid = self.storage.database['user']['user_id'] self.username = self.storage.database['user']['nickname'] self.resume_play = True self.at_playing_list = False signal.signal(signal.SIGWINCH, self.change_term) signal.signal(signal.SIGINT, self.send_kill) self.START = time.time() def change_term(self, signum, frame): self.ui.screen.clear() self.ui.screen.refresh() def send_kill(self, signum, fram): self.player.stop() self.cache.quit() self.storage.save() curses.endwin() sys.exit() def update_alert(self, version): latest = Menu().check_version() if latest != version and latest != 0: notify('MusicBox Update is available', 1) time.sleep(0.5) notify('NetEase-MusicBox installed version:' + version + '\nNetEase-MusicBox latest version:' + latest, 0) def check_version(self): # 检查更新 && 签到 try: mobilesignin = self.netease.daily_signin(0) if mobilesignin != -1 and mobilesignin['code'] != -2: notify('Mobile signin success', 1) time.sleep(0.5) pcsignin = self.netease.daily_signin(1) if pcsignin != -1 and pcsignin['code'] != -2: notify('PC signin success', 1) tree = ET.ElementTree(ET.fromstring(str(self.netease.get_version( )))) root = tree.getroot() return root[0][4][0][0].text except TypeError as e: log.error(e) return 0 def start_fork(self, version): pid = os.fork() if pid == 0: Menu().update_alert(version) else: Menu().start() def _is_playlist_empty(self): return len(self.storage.database['player_info']['player_list']) == 0 def play_pause(self): if self._is_playlist_empty(): return if self.player.pause_flag: self.player.resume() else: self.player.pause() time.sleep(0.1) def next_song(self): if self._is_playlist_empty(): return self.player.next() time.sleep(0.5) def previous_song(self): if self._is_playlist_empty(): return self.player.prev() time.sleep(0.5) def bind_keys(self): if BINDABLE: keybinder.bind( self.config.get_item('global_play_pause'), self.play_pause) keybinder.bind(self.config.get_item('global_next'), self.next_song) keybinder.bind( self.config.get_item('global_previous'), self.previous_song) def unbind_keys(self): if BINDABLE: keybinder.unbind(self.config.get_item('global_play_pause')) keybinder.unbind(self.config.get_item('global_next')) keybinder.unbind(self.config.get_item('global_previous')) def start(self): self.START = time.time() // 1 self.ui.build_menu(self.datatype, self.title, self.datalist, self.offset, self.index, self.step, self.START) self.ui.build_process_bar( self.player.process_location, self.player.process_length, self.player.playing_flag, self.player.pause_flag, self.storage.database['player_info']['playing_mode']) self.stack.append([self.datatype, self.title, self.datalist, self.offset, self.index]) try: self.bind_keys() except KeyError as e: log.warning(e) show_lyrics_new_process() while True: datatype = self.datatype title = self.title datalist = self.datalist offset = self.offset idx = index = self.index step = self.step stack = self.stack self.screen.timeout(500) key = self.screen.getch() if BINDABLE: keybinder.gtk.main_iteration(False) self.ui.screen.refresh() # term resize if key == -1: self.ui.update_size() self.player.update_size() # 退出 if key == ord('q'): try: self.unbind_keys() except KeyError as e: log.warning(e) break # 退出并清除用户信息 if key == ord('w'): self.storage.database['user'] = { 'username': '', 'password': '', 'user_id': '', 'nickname': '', } try: os.remove(self.storage.cookie_path) except OSError as e: log.error(e) break break # 上移 elif key == ord('k'): # turn page if at beginning if idx == offset: if offset == 0: continue self.offset -= step # 移动光标到最后一列 self.index = offset - 1 else: self.index = carousel(offset, min( len(datalist), offset + step) - 1, idx - 1) self.START = time.time() # 下移 elif key == ord('j'): # turn page if at end if idx == min(len(datalist), offset + step) - 1: if offset + step >= len(datalist): continue self.offset += step # 移动光标到第一列 self.index = offset + step else: self.index = carousel(offset, min( len(datalist), offset + step) - 1, idx + 1) self.START = time.time() # 数字快捷键 elif ord('0') <= key <= ord('9'): if self.datatype == ('songs', 'djchannels', 'help'): continue idx = key - ord('0') self.ui.build_menu(self.datatype, self.title, self.datalist, self.offset, idx, self.step, self.START) self.ui.build_loading() self.dispatch_enter(idx) self.index = 0 self.offset = 0 # 向上翻页 elif key == ord('u'): if offset == 0: continue self.START = time.time() self.offset -= step # e.g. 23 - 10 = 13 --> 10 self.index = (index - step) // step * step # 向下翻页 elif key == ord('d'): if offset + step >= len(datalist): continue self.START = time.time() self.offset += step # e.g. 23 + 10 = 33 --> 30 self.index = (index + step) // step * step # 前进 elif key == ord('l') or key == 10: if self.datatype == ('songs', 'djchannels', 'help') or len(self.datalist) <= 0: continue self.START = time.time() self.ui.build_loading() self.dispatch_enter(idx) self.index = 0 self.offset = 0 # 回退 elif key == ord('h'): # if not main menu if len(self.stack) == 1: continue self.START = time.time() up = stack.pop() self.datatype = up[0] self.title = up[1] self.datalist = up[2] self.offset = up[3] self.index = up[4] self.at_playing_list = False # 搜索 elif key == ord('f'): # 8 is the 'search' menu self.dispatch_enter(8) # 播放下一曲 elif key == ord(']'): self.next_song() # 播放上一曲 elif key == ord('['): self.previous_song() # 增加音量 elif key == ord('='): self.player.volume_up() # 减少音量 elif key == ord('-'): self.player.volume_down() # 随机播放 elif key == ord('?'): if len(self.storage.database['player_info'][ 'player_list']) == 0: continue self.player.shuffle() time.sleep(0.1) # 喜爱 elif key == ord(','): return_data = self.request_api(self.netease.fm_like, self.player.get_playing_id()) if return_data != -1: notify('Added successfully!', 0) else: notify('Existing song!', 0) # 删除FM elif key == ord('.'): if self.datatype == 'fmsongs': if len(self.storage.database['player_info'][ 'player_list']) == 0: continue self.player.next() return_data = self.request_api( self.netease.fm_trash, self.player.get_playing_id()) if return_data != -1: notify('Deleted successfully!', 0) time.sleep(0.1) # 下一FM elif key == ord('/'): if self.datatype == 'fmsongs': if len(self.storage.database['player_info'][ 'player_list']) == 0: continue if self.player.end_callback: self.player.end_callback() time.sleep(0.1) # 播放、暂停 elif key == ord(' '): # If not open a new playing list, just play and pause. try: if self.datalist[idx]['song_id'] == self.player.playing_id: self.player.play_and_pause(self.storage.database[ 'player_info']['idx']) time.sleep(0.1) continue except (TypeError, KeyError) as e: log.error(e) pass # If change to a new playing list. Add playing list and play. if datatype == 'songs': self.resume_play = False self.player.new_player_list('songs', self.title, self.datalist, -1) self.player.end_callback = None self.player.play_and_pause(idx) self.at_playing_list = True elif datatype == 'djchannels': self.resume_play = False self.player.new_player_list('djchannels', self.title, self.datalist, -1) self.player.end_callback = None self.player.play_and_pause(idx) self.at_playing_list = True elif datatype == 'fmsongs': self.resume_play = False self.storage.database['player_info']['playing_mode'] = 0 self.player.new_player_list('fmsongs', self.title, self.datalist, -1) self.player.end_callback = self.fm_callback self.player.play_and_pause(idx) self.at_playing_list = True else: self.player.play_and_pause(self.storage.database[ 'player_info']['idx']) time.sleep(0.1) # 加载当前播放列表 elif key == ord('p'): self.show_playing_song() # 播放模式切换 elif key == ord('P'): self.storage.database['player_info']['playing_mode'] = ( self.storage.database['player_info']['playing_mode'] + 1) % 5 # 添加到打碟歌单 elif key == ord('a'): if datatype == 'songs' and len(datalist) != 0: self.djstack.append(datalist[idx]) elif datatype == 'artists': pass # 加载打碟歌单 elif key == ord('z'): self.stack.append([datatype, title, datalist, offset, index]) self.datatype = 'songs' self.title = '网易云音乐 > 打碟' self.datalist = self.djstack self.offset = 0 self.index = 0 # 添加到本地收藏 elif key == ord('s'): if (datatype == 'songs' or datatype == 'djchannels') and len(datalist) != 0: self.collection.append(datalist[idx]) notify('Added successfully', 0) # 加载本地收藏 elif key == ord('c'): self.stack.append([datatype, title, datalist, offset, index]) self.datatype = 'songs' self.title = 'NetEase Music > Local Music' self.datalist = self.collection self.offset = 0 self.index = 0 # 从当前列表移除 elif key == ord('r'): if (datatype == 'songs' or datatype == 'djchannels') and len(datalist) != 0: self.datalist.pop(idx) self.index = carousel(offset, min( len(datalist), offset + step) - 1, idx) # 当前项目下移 elif key == ord('J'): if datatype != 'main' and len( datalist) != 0 and idx + 1 != len(self.datalist): self.START = time.time() song = self.datalist.pop(idx) self.datalist.insert(idx + 1, song) self.index = idx + 1 # 翻页 if self.index >= offset + step: self.offset = offset + step # 当前项目上移 elif key == ord('K'): if datatype != 'main' and len(datalist) != 0 and idx != 0: self.START = time.time() song = self.datalist.pop(idx) self.datalist.insert(idx - 1, song) self.index = idx - 1 # 翻页 if self.index < offset: self.offset = offset - step elif key == ord('m'): if datatype != 'main': self.stack.append([datatype, title, datalist, offset, index ]) self.datatype = self.stack[0][0] self.title = self.stack[0][1] self.datalist = self.stack[0][2] self.offset = 0 self.index = 0 elif key == ord('g'): if datatype == 'help': webbrowser.open_new_tab( 'https://github.com/darknessomi/musicbox') # 开始下载 elif key == ord('C'): s = self.datalist[idx] cache_thread = threading.Thread( target=self.player.cacheSong1time, args=(s['song_id'], s['song_name'], s['artist'], s[ 'mp3_url'])) cache_thread.start() elif key == ord('i'): if self.player.playing_id != -1: webbrowser.open_new_tab('http://music.163.com/#/song?id=' + str(self.player.playing_id)) self.ui.build_process_bar( self.player.process_location, self.player.process_length, self.player.playing_flag, self.player.pause_flag, self.storage.database['player_info']['playing_mode']) self.ui.build_menu(self.datatype, self.title, self.datalist, self.offset, self.index, self.step, self.START) self.player.stop() self.cache.quit() self.storage.save() curses.endwin() def dispatch_enter(self, idx): # The end of stack netease = self.netease datatype = self.datatype title = self.title datalist = self.datalist offset = self.offset index = self.index self.stack.append([datatype, title, datalist, offset, index]) if idx > len(self.datalist): return False if datatype == 'main': self.choice_channel(idx) # 该艺术家的热门歌曲 elif datatype == 'artists': artist_id = datalist[idx]['artist_id'] songs = netease.artists(artist_id) self.datatype = 'songs' self.datalist = netease.dig_info(songs, 'songs') self.title += ' > ' + datalist[idx]['artists_name'] # 该专辑包含的歌曲 elif datatype == 'albums': album_id = datalist[idx]['album_id'] songs = netease.album(album_id) self.datatype = 'songs' self.datalist = netease.dig_info(songs, 'songs') self.title += ' > ' + datalist[idx]['albums_name'] # 精选歌单选项 elif datatype == 'playlists': data = self.datalist[idx] self.datatype = data['datatype'] self.datalist = netease.dig_info(data['callback'](), self.datatype) self.title += ' > ' + data['title'] # 全站置顶歌单包含的歌曲 elif datatype == 'top_playlists': playlist_id = datalist[idx]['playlist_id'] songs = netease.playlist_detail(playlist_id) self.datatype = 'songs' self.datalist = netease.dig_info(songs, 'songs') self.title += ' > ' + datalist[idx]['playlists_name'] # 分类精选 elif datatype == 'playlist_classes': # 分类名称 data = self.datalist[idx] self.datatype = 'playlist_class_detail' self.datalist = netease.dig_info(data, self.datatype) self.title += ' > ' + data # 某一分类的详情 elif datatype == 'playlist_class_detail': # 子类别 data = self.datalist[idx] self.datatype = 'top_playlists' self.datalist = netease.dig_info( netease.top_playlists(data), self.datatype) self.title += ' > ' + data # 歌曲榜单 elif datatype == 'toplists': songs = netease.top_songlist(idx) self.title += ' > ' + self.datalist[idx] self.datalist = netease.dig_info(songs, 'songs') self.datatype = 'songs' # 搜索菜单 elif datatype == 'search': ui = self.ui self.index = 0 self.offset = 0 if idx == 0: # 搜索结果可以用top_playlists处理 self.datatype = 'top_playlists' self.datalist = ui.build_search('search_playlist') self.title = 'Playlist Search' elif idx == 1: self.datatype = 'songs' self.datalist = ui.build_search('songs') self.title = 'Song Search' elif idx == 2: self.datatype = 'artists' self.datalist = ui.build_search('artists') self.title = 'Artist Search' elif idx == 3: self.datatype = 'albums' self.datalist = ui.build_search('albums') self.title = 'Album Search' def show_playing_song(self): if self._is_playlist_empty(): return if not self.at_playing_list: self.stack.append([self.datatype, self.title, self.datalist, self.offset, self.index]) self.at_playing_list = True self.datatype = self.storage.database['player_info'][ 'player_list_type'] self.title = self.storage.database['player_info']['player_list_title'] self.datalist = [] for i in self.storage.database['player_info']['player_list']: self.datalist.append(self.storage.database['songs'][i]) self.index = self.storage.database['player_info']['idx'] self.offset = self.storage.database['player_info'][ 'idx'] / self.step * self.step if self.resume_play: if self.datatype == 'fmsongs': self.player.end_callback = self.fm_callback else: self.player.end_callback = None self.storage.database['player_info']['idx'] = -1 self.player.play_and_pause(self.index) self.resume_play = False def song_changed_callback(self): if self.at_playing_list: self.show_playing_song() def fm_callback(self): log.debug('FM CallBack.') data = self.get_new_fm() self.player.append_songs(data) if self.datatype == 'fmsongs': if self._is_playlist_empty(): return self.datatype = self.storage.database['player_info'][ 'player_list_type'] self.title = self.storage.database['player_info'][ 'player_list_title'] self.datalist = [] for i in self.storage.database['player_info']['player_list']: self.datalist.append(self.storage.database['songs'][i]) self.index = self.storage.database['player_info']['idx'] self.offset = self.storage.database['player_info'][ 'idx'] / self.step * self.step def request_api(self, func, *args): if self.storage.database['user']['user_id'] != '': result = func(*args) if result != -1: return result log.debug('Re Login.') user_info = {} if self.storage.database['user']['username'] != '': user_info = self.netease.login( self.storage.database['user']['username'], self.storage.database['user']['password']) if self.storage.database['user']['username'] == '' or user_info[ 'code'] != 200: data = self.ui.build_login() # 取消登录 if data == -1: return -1 user_info = data[0] self.storage.database['user']['username'] = data[1][0] self.storage.database['user']['password'] = data[1][1] self.storage.database['user']['user_id'] = user_info['account'][ 'id'] self.storage.database['user']['nickname'] = user_info['profile'][ 'nickname'] self.userid = self.storage.database['user']['user_id'] self.username = self.storage.database['user']['nickname'] return func(*args) def get_new_fm(self): myplaylist = [] for count in range(0, 1): data = self.request_api(self.netease.personal_fm) if data == -1: break myplaylist += data time.sleep(0.2) return self.netease.dig_info(myplaylist, 'fmsongs') def choice_channel(self, idx): # 排行榜 netease = self.netease if idx == 0: self.datalist = netease.return_toplists() self.title += ' > Charts' self.datatype = 'toplists' # 艺术家 elif idx == 1: artists = netease.top_artists() self.datalist = netease.dig_info(artists, 'artists') self.title += ' > Artists' self.datatype = 'artists' # 新碟上架 elif idx == 2: albums = netease.new_albums() self.datalist = netease.dig_info(albums, 'albums') self.title += ' > Albums' self.datatype = 'albums' # 精选歌单 elif idx == 3: self.datalist = [ { 'title': 'Top Playlists', 'datatype': 'top_playlists', 'callback': netease.top_playlists }, { 'title': 'Playlists (By Category)', 'datatype': 'playlist_classes', 'callback': netease.playlist_classes } ] self.title += ' > Playlists' self.datatype = 'playlists' # 我的歌单 elif idx == 4: myplaylist = self.request_api(self.netease.user_playlist, self.userid) if myplaylist == -1: return self.datatype = 'top_playlists' self.datalist = netease.dig_info(myplaylist, self.datatype) self.title += ' > ' + self.username + ' 的歌单' # 主播电台 elif idx == 5: self.datatype = 'djchannels' self.title += ' > Radio Stations' self.datalist = netease.djchannels() # 每日推荐 elif idx == 6: self.datatype = 'songs' self.title += ' > Songs' myplaylist = self.request_api(self.netease.recommend_playlist) if myplaylist == -1: return self.datalist = self.netease.dig_info(myplaylist, self.datatype) # 私人FM elif idx == 7: self.datatype = 'fmsongs' self.title += ' > Personal Radio' self.datalist = self.get_new_fm() # 搜索 elif idx == 8: self.datatype = 'search' self.title += ' > Search' self.datalist = ['Songs', 'Artists', 'Albums', 'Playlists'] # 帮助 elif idx == 9: self.datatype = 'help' self.title += ' > Help' self.datalist = shortcut self.offset = 0 self.index = 0
class Ui: def __init__(self): self.screen = curses.initscr() self.screen.timeout(500) # the screen refresh every 500ms # charactor break buffer curses.cbreak() self.screen.keypad(1) self.netease = NetEase() curses.start_color() curses.init_pair(1, curses.COLOR_GREEN, curses.COLOR_BLACK) curses.init_pair(2, curses.COLOR_CYAN, curses.COLOR_BLACK) curses.init_pair(3, curses.COLOR_RED, curses.COLOR_BLACK) curses.init_pair(4, curses.COLOR_YELLOW, curses.COLOR_BLACK) # term resize handling size = terminalsize.get_terminal_size() self.x = max(size[0], 10) self.y = max(size[1], 25) self.startcol = int(float(self.x) / 5) self.indented_startcol = max(self.startcol - 3, 0) self.update_space() def build_playinfo(self, song_name, artist, album_name, quality, start, pause=False): curses.noecho() # refresh top 2 line self.screen.move(1, 1) self.screen.clrtoeol() self.screen.move(2, 1) self.screen.clrtoeol() if pause: self.screen.addstr(1, self.indented_startcol, '_ _ z Z Z ' + quality, curses.color_pair(3)) else: self.screen.addstr(1, self.indented_startcol, '♫ ♪ ♫ ♪ ' + quality, curses.color_pair(3)) self.screen.addstr( 1, min(self.indented_startcol + 18, self.x - 1), song_name + self.space + artist + ' < ' + album_name + ' >', curses.color_pair(4)) # The following script doesn't work. It is intended to scroll the playinfo # Scrollstring works by determining how long since it is created, but # playinfo is created everytime the screen refreshes (every 500ms), unlike # the menu. Is there a workaround? # name = song_name + self.space + artist + ' < ' + album_name + ' >' # decides whether to scoll # if truelen(name) <= self.x - self.indented_startcol - 18: # self.screen.addstr(1, min(self.indented_startcol + 18, self.x-1), # name, # curses.color_pair(4)) # else: # name = scrollstring(name + ' ', start) # self.screen.addstr(1, min(self.indented_startcol + 18, self.x-1), # str(name), # curses.color_pair(4)) self.screen.refresh() def build_loading(self): self.screen.addstr(6, self.startcol, '享受高品质音乐,loading...', curses.color_pair(1)) self.screen.refresh() # start is the timestamp of this function being called def build_menu(self, datatype, title, datalist, offset, index, step, start): # keep playing info in line 1 curses.noecho() self.screen.move(4, 1) self.screen.clrtobot() self.screen.addstr(4, self.startcol, title, curses.color_pair(1)) if len(datalist) == 0: self.screen.addstr(8, self.startcol, '这里什么都没有 -,-') else: if datatype == 'main': for i in range(offset, min(len(datalist), offset + step)): if i == index: self.screen.addstr(i - offset + 8, self.indented_startcol, '-> ' + str(i) + '. ' + datalist[i], curses.color_pair(2)) else: self.screen.addstr(i - offset + 8, self.startcol, str(i) + '. ' + datalist[i]) elif datatype == 'songs': iter_range = min(len(datalist), offset + step) for i in range(offset, iter_range): # this item is focus if i == index: self.screen.addstr(i - offset + 8, 0, ' ' * self.startcol) lead = '-> ' + str(i) + '. ' self.screen.addstr(i - offset + 8, self.indented_startcol, lead, curses.color_pair(2)) name = str(datalist[i]['song_name'] + self.space + datalist[i]['artist'] + ' < ' + datalist[i]['album_name'] + ' >') # the length decides whether to scoll if truelen(name) < self.x - self.startcol - 1: self.screen.addstr( i - offset + 8, self.indented_startcol + len(lead), name, curses.color_pair(2)) else: name = scrollstring(name + ' ', start) self.screen.addstr( i - offset + 8, self.indented_startcol + len(lead), str(name), curses.color_pair(2)) else: self.screen.addstr(i - offset + 8, 0, ' ' * self.startcol) self.screen.addstr( i - offset + 8, self.startcol, str( str(i) + '. ' + datalist[i]['song_name'] + self.space + datalist[i]['artist'] + ' < ' + datalist[i]['album_name'] + ' >')[:int(self.x * 2)]) self.screen.addstr(iter_range - offset + 8, 0, ' ' * self.x) elif datatype == 'artists': for i in range(offset, min(len(datalist), offset + step)): if i == index: self.screen.addstr( i - offset + 8, self.indented_startcol, '-> ' + str(i) + '. ' + datalist[i]['artists_name'] + self.space + str(datalist[i]['alias']), curses.color_pair(2)) else: self.screen.addstr( i - offset + 8, self.startcol, str(i) + '. ' + datalist[i]['artists_name'] + self.space + datalist[i]['alias']) elif datatype == 'albums': for i in range(offset, min(len(datalist), offset + step)): if i == index: self.screen.addstr( i - offset + 8, self.indented_startcol, '-> ' + str(i) + '. ' + datalist[i]['albums_name'] + self.space + datalist[i]['artists_name'], curses.color_pair(2)) else: self.screen.addstr( i - offset + 8, self.startcol, str(i) + '. ' + datalist[i]['albums_name'] + self.space + datalist[i]['artists_name']) elif datatype == 'playlists': for i in range(offset, min(len(datalist), offset + step)): if i == index: self.screen.addstr( i - offset + 8, self.indented_startcol, '-> ' + str(i) + '. ' + datalist[i]['title'], curses.color_pair(2)) else: self.screen.addstr( i - offset + 8, self.startcol, str(i) + '. ' + datalist[i]['title']) elif datatype == 'top_playlists': for i in range(offset, min(len(datalist), offset + step)): if i == index: self.screen.addstr( i - offset + 8, self.indented_startcol, '-> ' + str(i) + '. ' + datalist[i]['playlists_name'] + self.space + datalist[i]['creator_name'], curses.color_pair(2)) else: self.screen.addstr( i - offset + 8, self.startcol, str(i) + '. ' + datalist[i]['playlists_name'] + self.space + datalist[i]['creator_name']) elif datatype == 'toplists': for i in range(offset, min(len(datalist), offset + step)): if i == index: self.screen.addstr(i - offset + 8, self.indented_startcol, '-> ' + str(i) + '. ' + datalist[i], curses.color_pair(2)) else: self.screen.addstr(i - offset + 8, self.startcol, str(i) + '. ' + datalist[i]) elif datatype == 'playlist_classes' or datatype == 'playlist_class_detail': for i in range(offset, min(len(datalist), offset + step)): if i == index: self.screen.addstr(i - offset + 8, self.indented_startcol, '-> ' + str(i) + '. ' + datalist[i], curses.color_pair(2)) else: self.screen.addstr(i - offset + 8, self.startcol, str(i) + '. ' + datalist[i]) elif datatype == 'djchannels': for i in range(offset, min(len(datalist), offset + step)): if i == index: self.screen.addstr( i - offset + 8, self.indented_startcol, '-> ' + str(i) + '. ' + datalist[i]['song_name'], curses.color_pair(2)) else: self.screen.addstr( i - offset + 8, self.startcol, str(i) + '. ' + datalist[i]['song_name']) elif datatype == 'search': self.screen.move(4, 1) self.screen.clrtobot() self.screen.timeout(-1) self.screen.addstr(8, self.startcol, '选择搜索类型:', curses.color_pair(1)) for i in range(offset, min(len(datalist), offset + step)): if i == index: self.screen.addstr( i - offset + 10, self.indented_startcol, '-> ' + str(i) + '.' + datalist[i - 1], curses.color_pair(2)) else: self.screen.addstr(i - offset + 10, self.startcol, str(i) + '.' + datalist[i - 1]) self.screen.timeout(500) elif datatype == 'help': for i in range(offset, min(len(datalist), offset + step)): if i == index: self.screen.addstr( i - offset + 8, self.indented_startcol, '-> ' + str(i) + '. \'' + (datalist[i][0].upper() + '\'').ljust(11) + datalist[i][1] + ' ' + datalist[i][2], curses.color_pair(2)) else: self.screen.addstr( i - offset + 8, self.startcol, str(i) + '. \'' + (datalist[i][0].upper() + '\'').ljust(11) + datalist[i][1] + ' ' + datalist[i][2]) self.screen.addstr( 20, 6, 'NetEase-MusicBox 基于Python,所有版权音乐来源于网易,本地不做任何保存') self.screen.addstr(21, 10, '按 [G] 到 Github 了解更多信息,帮助改进,或者Star表示支持~~') self.screen.addstr(22, self.startcol, 'Build with love to music by omi') self.screen.refresh() def build_search(self, stype): self.screen.timeout(-1) netease = self.netease if stype == 'songs': song_name = self.get_param('搜索歌曲:') try: data = netease.search(song_name, stype=1) song_ids = [] if 'songs' in data['result']: if 'mp3Url' in data['result']['songs']: songs = data['result']['songs'] # if search song result do not has mp3Url # send ids to get mp3Url else: for i in range(0, len(data['result']['songs'])): song_ids.append(data['result']['songs'][i]['id']) songs = netease.songs_detail(song_ids) return netease.dig_info(songs, 'songs') except: return [] elif stype == 'artists': artist_name = self.get_param('搜索艺术家:') try: data = netease.search(artist_name, stype=100) if 'artists' in data['result']: artists = data['result']['artists'] return netease.dig_info(artists, 'artists') except: return [] elif stype == 'albums': artist_name = self.get_param('搜索专辑:') try: data = netease.search(artist_name, stype=10) if 'albums' in data['result']: albums = data['result']['albums'] return netease.dig_info(albums, 'albums') except: return [] elif stype == 'search_playlist': artist_name = self.get_param('搜索网易精选集:') try: data = netease.search(artist_name, stype=1000) if 'playlists' in data['result']: playlists = data['result']['playlists'] return netease.dig_info(playlists, 'top_playlists') except: return [] return [] def build_login(self): self.build_login_bar() local_account = self.get_account() local_password = hashlib.md5(self.get_password()).hexdigest() login_info = self.netease.login(local_account, local_password) account = [local_account, local_password] if login_info['code'] != 200: x = self.build_login_error() if x == ord('1'): return self.build_login() else: return -1 else: return [login_info, account] def build_login_bar(self): curses.noecho() self.screen.move(4, 1) self.screen.clrtobot() self.screen.addstr(5, self.startcol, '请输入登录信息(支持手机登陆)', curses.color_pair(1)) self.screen.addstr(8, self.startcol, "账号:", curses.color_pair(1)) self.screen.addstr(9, self.startcol, "密码:", curses.color_pair(1)) self.screen.move(8, 24) self.screen.refresh() def build_login_error(self): self.screen.move(4, 1) self.screen.timeout(-1) # disable the screen timeout self.screen.clrtobot() self.screen.addstr(8, self.startcol, '艾玛,登录信息好像不对呢 (O_O)#', curses.color_pair(1)) self.screen.addstr(10, self.startcol, '[1] 再试一次') self.screen.addstr(11, self.startcol, '[2] 稍后再试') self.screen.addstr(14, self.startcol, '请键入对应数字:', curses.color_pair(2)) self.screen.refresh() x = self.screen.getch() self.screen.timeout(500) # restore the screen timeout return x def get_account(self): self.screen.timeout(-1) # disable the screen timeout curses.echo() account = self.screen.getstr(8, self.startcol + 6, 60) self.screen.timeout(500) # restore the screen timeout return account def get_password(self): self.screen.timeout(-1) # disable the screen timeout curses.noecho() password = self.screen.getstr(9, self.startcol + 6, 60) self.screen.timeout(500) # restore the screen timeout return password def get_param(self, prompt_string): # keep playing info in line 1 curses.echo() self.screen.move(4, 1) self.screen.clrtobot() self.screen.addstr(5, self.startcol, prompt_string, curses.color_pair(1)) self.screen.refresh() info = self.screen.getstr(10, self.startcol, 60) if info.strip() is '': return self.get_param(prompt_string) else: return info def update_size(self): # get terminal size size = terminalsize.get_terminal_size() self.x = max(size[0], 10) self.y = max(size[1], 25) # update intendations curses.resizeterm(self.y, self.x) self.startcol = int(float(self.x) / 5) self.indented_startcol = max(self.startcol - 3, 0) self.update_space() self.screen.clear() self.screen.refresh() def update_space(self): if self.x > 140: self.space = " - " elif self.x > 80: self.space = " - " else: self.space = " - " self.screen.refresh()
class Menu(object): def __init__(self): self.config = Config() self.datatype = "main" self.title = "网易云音乐" self.datalist = [ "排行榜", "艺术家", "新碟上架", "精选歌单", "我的歌单", "主播电台", "每日推荐歌曲", "每日推荐歌单", "私人FM", "搜索", "帮助", ] self.offset = 0 self.index = 0 self.storage = Storage() self.storage.load() self.collection = self.storage.database["collections"] self.player = Player() self.player.playing_song_changed_callback = self.song_changed_callback self.cache = Cache() # self.ui = Ui() self.api = NetEase() # self.screen = curses.initscr() # self.screen.keypad(1) self.step = 10 self.stack = [] self.djstack = [] self.at_playing_list = False self.enter_flag = True # signal.signal(signal.SIGWINCH, self.change_term) # signal.signal(signal.SIGINT, self.send_kill) self.menu_starts = time.time() self.countdown_start = time.time() self.countdown = -1 self.is_in_countdown = False self.keyword = '' @property def user(self): return self.storage.database["user"] @property def account(self): return self.user["username"] @property def md5pass(self): return self.user["password"] @property def userid(self): return self.user["user_id"] @property def username(self): return self.user["nickname"] def login(self): if self.account and self.md5pass: account, md5pass = self.account, self.md5pass else: #modified account = str(input('name:')) password = str(input('password:'******'that is right........') return True else: self.storage.logout() # x = self.ui.build_login_error() # if x != ord("1"): # return False return self.login() def to_login(self, username, passwd): if self.account and self.md5pass: account, md5pass = self.account, self.md5pass else: # modified # account = str(input('name:')) # password = str(input('password:'******'that is right........') return True else: self.storage.logout() # x = self.ui.build_login_error() # if x != ord("1"): # return False # return self.login() return False def search(self, category): # self.ui.screen.timeout(-1) SearchArg = namedtuple("SearchArg", ["prompt", "api_type", "post_process"]) category_map = { "songs": SearchArg("搜索歌曲:", 1, lambda datalist: datalist), "albums": SearchArg("搜索专辑:", 10, lambda datalist: datalist), "artists": SearchArg("搜索艺术家:", 100, lambda datalist: datalist), "playlists": SearchArg("搜索网易精选集:", 1000, lambda datalist: datalist), } prompt, api_type, post_process = category_map[category] # keyword = self.ui.get_param(prompt) # keyword = str(input('Input the song\'s name:')) keyword = self.keyword if not keyword: return [] data = self.api.search(keyword, api_type) if not data: return data datalist = post_process(data.get(category, [])) return self.api.dig_info(datalist, category) def change_term(self, signum, frame): self.ui.screen.clear() self.ui.screen.refresh() def send_kill(self, signum, fram): self.player.stop() self.cache.quit() self.storage.save() # curses.endwin() sys.exit() def update_alert(self, version): latest = Menu().check_version() if latest != version and latest != 0: notify("MusicBox Update is available", 1) time.sleep(0.5) notify( "NetEase-MusicBox installed version:" + version + "\nNetEase-MusicBox latest version:" + latest, 0, ) def check_version(self): # 检查更新 && 签到 try: mobile = self.api.daily_task(is_mobile=True) pc = self.api.daily_task(is_mobile=False) if mobile["code"] == 200: notify("移动端签到成功", 1) if pc["code"] == 200: notify("PC端签到成功", 1) data = self.api.get_version() return data["info"]["version"] except KeyError as e: return 0 def start_fork(self, version): pid = os.fork() if pid == 0: Menu().update_alert(version) else: Menu().start() def play_pause(self): if self.player.is_empty: return if not self.player.playing_flag: self.player.resume() else: self.player.pause() def next_song(self): if self.player.is_empty: return self.player.next() def previous_song(self): if self.player.is_empty: return self.player.prev() def start(self): # while True: # print('input 1:login,2:search,100:break') # num = int(input('Please input your choice:')) # print('you input {}'.format(num)) # if (num == 1): # print('username before: {}'.format(self.user)) # myplaylist = self.request_api(self.api.user_playlist, self.userid) # print(myplaylist) # print('username: {}'.format(self.user)) # elif num == 2: # datalist = self.search('songs') # print('search result:') # for idxx,val in enumerate(datalist): # print('{}:{}-{}'.format(idxx,val['song_name'],val['artist'])) # if idxx > 10: # break; # elif num == 100: # break #############################################################afer: def print_info(): print('----------------------------') print('1:清空信息并退出') print('2:上移') print('3:下移') print('4:搜索') print('5:播放') print('6:登录') print('7:个人歌单') print('100:直接退出') print('----------------------------') while True: datatype = self.datatype title = self.title datalist = self.datalist offset = self.offset idx = self.index step = self.step print_info() key = int(input('请输入你的选择:')) if key == 100: print('正在退出....') self.player.stop() self.storage.save() break elif key == 1: self.api.logout() print('正在退出....') self.player.stop() break elif key == 2: if idx == offset: if offset == 0: continue self.offset -= step # 移动光标到最后一列 self.index = offset - 1 else: self.index = carousel( offset, min(len(datalist), offset + step) - 1, idx - 1) self.menu_starts = time.time() elif key == 3: if idx == min(len(datalist), offset + step) - 1: if offset + step >= len(datalist): continue self.offset += step # 移动光标到第一列 self.index = offset + step else: self.index = carousel( offset, min(len(datalist), offset + step) - 1, idx + 1) self.menu_starts = time.time() elif key == 4: self.index = 0 self.offset = 0 idx = 1 SearchCategory = namedtuple("SearchCategory", ["type", "title"]) idx_map = { 0: SearchCategory("playlists", "精选歌单搜索列表"), 1: SearchCategory("songs", "歌曲搜索列表"), 2: SearchCategory("artists", "艺术家搜索列表"), 3: SearchCategory("albums", "专辑搜索列表"), } self.datatype, self.title = idx_map[idx] self.datalist = self.search(self.datatype) print('search result:') for idxx, val in enumerate(self.datalist): print('{}:{}-{}'.format(idxx, val['song_name'], val['artist'])) if idxx > 10: break which_one = int(input('输入想要播放的序号:')) while which_one > 10 or which_one < 0: which_one = int(input('序号不合理,重新输入:')) self.player.new_player_list('songs', self.title, self.datalist, -1) self.idx = which_one self.player.play_or_pause(self.idx, self.at_playing_list) elif key == 5: print('当前的歌单:') cnt = 0 for key in self.player.songs.keys(): print('{}.{}----{}'.format( cnt, self.player.songs[key]['song_name'], self.player.songs[key]['artist'])) cnt += 1 if cnt > 10: break which_one = int(input('输入想要播放的序号:')) while which_one > 10 or which_one < 0: which_one = int(input('序号不合理,重新输入:')) self.idx = which_one self.player.play_or_pause(self.idx, self.at_playing_list) elif key == 6: myplaylist = self.request_api(self.api.user_playlist, self.userid) self.datatype = 'top_playlists' myplaylist = self.api.dig_info(myplaylist, self.datatype) notify('登录成功') elif key == 7: myplaylist = self.request_api(self.api.user_playlist, self.userid) self.datatype = 'top_playlists' myplaylist = self.api.dig_info(myplaylist, self.datatype) print('{}的歌单:'.format(self.username)) for x, y in enumerate(myplaylist): print('{}.{}'.format(x, y['playlist_name'])) def get_songs_info(self, search_info, choice): self.keyword = search_info if choice < 0 or choice > 3: notify('选择有误') return idx = choice SearchCategory = namedtuple("SearchCategory", ["type", "title"]) idx_map = { 0: SearchCategory("playlists", "精选歌单搜索列表"), 1: SearchCategory("songs", "歌曲搜索列表"), 2: SearchCategory("artists", "艺术家搜索列表"), 3: SearchCategory("albums", "专辑搜索列表"), } self.datatype, self.title = idx_map[idx] self.datalist = self.search(self.datatype) res = [] if choice == 1: for idxx, val in enumerate(self.datalist): res.append('{}(歌曲名)-{}(艺术家))'.format(val['song_name'], val['artist'])) # if idxx > 10: # break; elif choice == 2: for idxx, val in enumerate(self.datalist): res.append('艺术家:{}'.format(val['artists_name'])) # if idxx > 10: # break; elif choice == 3: # print(self.datalist) for idxx, val in enumerate(self.datalist): res.append('{}(专辑)-{}(艺术家)'.format(val['albums_name'], val['artists_name'])) # if idxx > 10: # break; else: pass return res def play_which_song(self, which): # self.player.new_player_list('songs', self.title, self.datalist, -1) # # self.idx = which # self.player.play_or_pause(self.idx, self.at_playing_list) # print('self.at...',self.at_playing_list) self.player.new_player_list("songs", self.title, self.datalist, -1) # self.player.end_callback = None self.player.play_or_pause(which, self.at_playing_list) # self.at_playing_list = True def now_total_time(self): return self.player.process_location, self.player.process_length def dispatch_enter(self, idx): # The end of stack netease = self.api datatype = self.datatype title = self.title datalist = self.datalist offset = self.offset index = self.index self.stack.append([datatype, title, datalist, offset, index]) if idx >= len(self.datalist): return False if datatype == "main": self.choice_channel(idx) # 该艺术家的热门歌曲 elif datatype == "artists": artist_name = datalist[idx]["artists_name"] artist_id = datalist[idx]["artist_id"] self.datatype = "artist_info" self.title += " > " + artist_name self.datalist = [ { "item": "{}的热门歌曲".format(artist_name), "id": artist_id }, { "item": "{}的所有专辑".format(artist_name), "id": artist_id }, ] elif datatype == "artist_info": self.title += " > " + datalist[idx]["item"] artist_id = datalist[0]["id"] if idx == 0: self.datatype = "songs" songs = netease.artists(artist_id) self.datalist = netease.dig_info(songs, "songs") elif idx == 1: albums = netease.get_artist_album(artist_id) self.datatype = "albums" self.datalist = netease.dig_info(albums, "albums") elif datatype == "djchannels": radio_id = datalist[idx]["id"] programs = netease.djprograms(radio_id) self.title += " > " + datalist[idx]["name"] self.datatype = "songs" self.datalist = netease.dig_info(programs, "songs") # 该专辑包含的歌曲 elif datatype == "albums": album_id = datalist[idx]["album_id"] songs = netease.album(album_id) self.datatype = "songs" self.datalist = netease.dig_info(songs, "songs") self.title += " > " + datalist[idx]["albums_name"] # 精选歌单选项 elif datatype == "recommend_lists": data = self.datalist[idx] self.datatype = data["datatype"] self.datalist = netease.dig_info(data["callback"](), self.datatype) self.title += " > " + data["title"] # 全站置顶歌单包含的歌曲 elif datatype in ["top_playlists", "playlists"]: playlist_id = datalist[idx]["playlist_id"] songs = netease.playlist_detail(playlist_id) self.datatype = "songs" self.datalist = netease.dig_info(songs, "songs") self.title += " > " + datalist[idx]["playlist_name"] # 分类精选 elif datatype == "playlist_classes": # 分类名称 data = self.datalist[idx] self.datatype = "playlist_class_detail" self.datalist = netease.dig_info(data, self.datatype) self.title += " > " + data # 某一分类的详情 elif datatype == "playlist_class_detail": # 子类别 data = self.datalist[idx] self.datatype = "top_playlists" log.error(data) self.datalist = netease.dig_info(netease.top_playlists(data), self.datatype) self.title += " > " + data # 歌曲评论 elif datatype in ["songs", "fmsongs"]: song_id = datalist[idx]["song_id"] comments = self.api.song_comments(song_id, limit=100) try: hotcomments = comments["hotComments"] comcomments = comments["comments"] except KeyError: hotcomments = comcomments = [] self.datalist = [] for one_comment in hotcomments: self.datalist.append("(热评 %s❤️ ️)%s:%s" % ( one_comment["likedCount"], one_comment["user"]["nickname"], one_comment["content"], )) for one_comment in comcomments: self.datalist.append(one_comment["content"]) self.datatype = "comments" self.title = "网易云音乐 > 评论:%s" % datalist[idx]["song_name"] self.offset = 0 self.index = 0 # 歌曲榜单 elif datatype == "toplists": songs = netease.top_songlist(idx) self.title += " > " + self.datalist[idx] self.datalist = netease.dig_info(songs, "songs") self.datatype = "songs" # 搜索菜单 elif datatype == "search": self.index = 0 self.offset = 0 SearchCategory = namedtuple("SearchCategory", ["type", "title"]) idx_map = { 0: SearchCategory("playlists", "精选歌单搜索列表"), 1: SearchCategory("songs", "歌曲搜索列表"), 2: SearchCategory("artists", "艺术家搜索列表"), 3: SearchCategory("albums", "专辑搜索列表"), } self.datatype, self.title = idx_map[idx] self.datalist = self.search(self.datatype) else: self.enter_flag = False def show_playing_song(self): if self.player.is_empty: return if not self.at_playing_list: self.stack.append([ self.datatype, self.title, self.datalist, self.offset, self.index ]) self.at_playing_list = True self.datatype = self.player.info["player_list_type"] self.title = self.player.info["player_list_title"] self.datalist = [ self.player.songs[i] for i in self.player.info["player_list"] ] self.index = self.player.info["idx"] self.offset = self.index // self.step * self.step def song_changed_callback(self): if self.at_playing_list: self.show_playing_song() def fm_callback(self): # log.debug('FM CallBack.') data = self.get_new_fm() self.player.append_songs(data) if self.datatype == "fmsongs": if self.player.is_empty: return self.datatype = self.player.info["player_list_type"] self.title = self.player.info["player_list_title"] self.datalist = [] for i in self.player.info["player_list"]: self.datalist.append(self.player.songs[i]) self.index = self.player.info["idx"] self.offset = self.index // self.step * self.step if not self.player.playing_flag: switch_flag = False self.player.play_or_pause(self.index, switch_flag) def request_api(self, func, *args): result = func(*args) if result: return result if not self.login(): print('you really need to login') notify("You need to log in") return False return func(*args) def get_new_fm(self): data = self.request_api(self.api.personal_fm) if not data: return [] return self.api.dig_info(data, "fmsongs") def choice_channel(self, idx): self.offset = 0 self.index = 0 if idx == 0: self.datalist = self.api.toplists self.title += " > 排行榜" self.datatype = "toplists" elif idx == 1: artists = self.api.top_artists() self.datalist = self.api.dig_info(artists, "artists") self.title += " > 艺术家" self.datatype = "artists" elif idx == 2: albums = self.api.new_albums() self.datalist = self.api.dig_info(albums, "albums") self.title += " > 新碟上架" self.datatype = "albums" elif idx == 3: self.datalist = [ { "title": "全站置顶", "datatype": "top_playlists", "callback": self.api.top_playlists, }, { "title": "分类精选", "datatype": "playlist_classes", "callback": lambda: [], }, ] self.title += " > 精选歌单" self.datatype = "recommend_lists" elif idx == 4: myplaylist = self.request_api(self.api.user_playlist, self.userid) self.datatype = "top_playlists" self.datalist = self.api.dig_info(myplaylist, self.datatype) self.title += " > " + self.username + " 的歌单" elif idx == 5: self.datatype = "djchannels" self.title += " > 主播电台" self.datalist = self.api.djchannels() elif idx == 6: self.datatype = "songs" self.title += " > 每日推荐歌曲" myplaylist = self.request_api(self.api.recommend_playlist) if myplaylist == -1: return self.datalist = self.api.dig_info(myplaylist, self.datatype) elif idx == 7: myplaylist = self.request_api(self.api.recommend_resource) self.datatype = "top_playlists" self.title += " > 每日推荐歌单" self.datalist = self.api.dig_info(myplaylist, self.datatype) elif idx == 8: self.datatype = "fmsongs" self.title += " > 私人FM" self.datalist = self.get_new_fm() elif idx == 9: self.datatype = "search" self.title += " > 搜索" self.datalist = ["歌曲", "艺术家", "专辑", "网易精选集"] elif idx == 10: self.datatype = "help" self.title += " > 帮助" self.datalist = shortcut
class Ui: def __init__(self): curses.setupterm() #初始化终端 self.screen = curses.initscr() self.LINES=curses.tigetnum('lines') #终端行数 self.COLS=curses.tigetnum('cols') #列数 self.show_begin_line=int((int(self.LINES)-22)/2-1) # charactor break buffer curses.cbreak() self.screen.keypad(1) self.netease = NetEase() curses.start_color() curses.init_pair(1, curses.COLOR_GREEN, curses.COLOR_BLACK) curses.init_pair(2, curses.COLOR_CYAN, curses.COLOR_BLACK) curses.init_pair(3, curses.COLOR_RED, curses.COLOR_BLACK) curses.init_pair(4, curses.COLOR_YELLOW, curses.COLOR_BLACK) def build_playinfo(self, song_name, artist, album_name, pause=False): curses.noecho() # refresh top 2 line self.screen.move(1+self.show_begin_line,1) self.screen.clrtoeol() self.screen.move(2+self.show_begin_line,1) self.screen.clrtoeol() if pause: self.screen.addstr(1+self.show_begin_line, 6, '_ _ z Z Z', curses.color_pair(3)) else: self.screen.addstr(1+self.show_begin_line, 6, '♫ ♪ ♫ ♪', curses.color_pair(3)) self.screen.addstr(1+self.show_begin_line, 17, song_name + ' - ' + artist + ' < ' + album_name + ' >', curses.color_pair(4)) self.screen.refresh() def build_loading(self,i=1): if i==1: self.screen.addstr(6+self.show_begin_line, 16, '享受高品质音乐,loading...', curses.color_pair(1)) elif i==2: self.screen.addstr(6+self.show_begin_line, 16, 'loading...', curses.color_pair(3)) self.screen.refresh() def build_menu(self, datatype, title, datalist, offset, index, step,album_name_P=False): # keep playing info in line 1 curses.noecho() self.screen.move(4+self.show_begin_line,1) self.screen.clrtobot() if datatype=='main': #调整标题位置 self.screen.addstr(4+self.show_begin_line, 19, title, curses.color_pair(1)) else: self.screen.addstr(4+self.show_begin_line, 9, title, curses.color_pair(1)) if len(datalist) == 0: self.screen.addstr(8+self.show_begin_line, 19, '这里什么都没有 -,-') else: if datatype == 'main': for i in range( offset, min( len(datalist), offset+step) ): if i == index: self.screen.addstr(i - offset +8+self.show_begin_line, 16, '-> ' + str(i) + '. ' + datalist[i], curses.color_pair(2)) else: self.screen.addstr(i - offset +8+self.show_begin_line, 19, str(i) + '. ' + datalist[i]) elif datatype == 'songs': for i in range(offset, min( len(datalist), offset+step) ): # this item is focus if album_name_P: #显示专辑名 if i == index: self.screen.addstr(i - offset +8+self.show_begin_line, 6, '-> ' + str(i) + '. ' + datalist[i]['song_name'] + ' - ' + datalist[i]['artist'] + ' < ' + datalist[i]['album_name'] + ' >', curses.color_pair(2)) else: self.screen.addstr(i - offset +8+self.show_begin_line, 9, str(i) + '. ' + datalist[i]['song_name'] + ' - ' + datalist[i]['artist'] + ' < ' + datalist[i]['album_name'] + ' >') else: #不显示专辑名 if i==index: self.screen.addstr(i - offset +8+self.show_begin_line, 6, '-> ' + str(i) + '. ' + datalist[i]['song_name'] + ' - ' + datalist[i]['artist'], curses.color_pair(2)) else: self.screen.addstr(i - offset +8+self.show_begin_line, 9, str(i) + '. ' + datalist[i]['song_name'] + ' - ' + datalist[i]['artist'] ) elif datatype == 'artists': for i in range(offset, min( len(datalist), offset+step) ): if i == index: self.screen.addstr(i - offset +8+self.show_begin_line, 16, '-> ' + str(i) + '. ' + datalist[i]['artists_name'] + ' - ' + str(datalist[i]['alias']), curses.color_pair(2)) else: self.screen.addstr(i - offset +8+self.show_begin_line, 19, str(i) + '. ' + datalist[i]['artists_name'] + ' - ' + datalist[i]['alias']) elif datatype == 'albums': for i in range(offset, min( len(datalist), offset+step) ): if i == index: self.screen.addstr(i - offset +8+self.show_begin_line, 6, '-> ' + str(i) + '. ' + datalist[i]['albums_name'] + ' - ' + datalist[i]['artists_name'], curses.color_pair(2)) else: self.screen.addstr(i - offset +8+self.show_begin_line, 9, str(i) + '. ' + datalist[i]['albums_name'] + ' - ' + datalist[i]['artists_name']) elif datatype == 'playlists': for i in range(offset, min( len(datalist), offset+step) ): if i == index: self.screen.addstr(i - offset +8+self.show_begin_line, 6, '-> ' + str(i) + '. ' + datalist[i]['title'], curses.color_pair(2)) else: self.screen.addstr(i - offset +8+self.show_begin_line, 9, str(i) + '. ' + datalist[i]['title']) elif datatype == 'top_playlists': for i in range(offset, min( len(datalist), offset+step) ): if i == index: self.screen.addstr(i - offset +8+self.show_begin_line, 6, '-> ' + str(i) + '. ' + datalist[i]['playlists_name'] + ' - ' + datalist[i]['creator_name'], curses.color_pair(2)) else: self.screen.addstr(i - offset +8+self.show_begin_line, 9, str(i) + '. ' + datalist[i]['playlists_name'] + ' - ' + datalist[i]['creator_name']) elif datatype == 'toplists': #排行榜榜名 for i in range(offset, min(len(datalist),offset+step)): if i == index: self.screen.addstr(i-offset+8+self.show_begin_line, 7, '->' + str(i) + '. ' + datalist[i], curses.color_pair(2)) else: self.screen.addstr(i - offset + 8 + self.show_begin_line, 9 ,str(i) + '. ' + datalist[i]) elif datatype == 'playlist_classes' or datatype == 'playlist_class_detail': for i in range(offset, min( len(datalist), offset+step) ): if i == index: self.screen.addstr(i - offset +8+self.show_begin_line, 6, '-> ' + str(i) + '. ' + datalist[i], curses.color_pair(2)) else: self.screen.addstr(i - offset +8+self.show_begin_line, 9, str(i) + '. ' + datalist[i]) elif datatype == 'djchannels': for i in range(offset, min( len(datalist), offset+step) ): if i == index: self.screen.addstr(i - offset +8+self.show_begin_line, 6, '-> ' + str(i) + '. ' + datalist[i]['song_name'], curses.color_pair(2)) else: self.screen.addstr(i - offset +8+self.show_begin_line, 9, str(i) + '. ' + datalist[i]['song_name']) elif datatype == 'help': for i in range(offset, min( len(datalist), offset+step) ): if i == index: self.screen.addstr(i - offset +8+self.show_begin_line, 16, '-> ' + str(i) + '. \'' + datalist[i][0].upper() + '\' ' + datalist[i][1] + ' ' + datalist[i][2], curses.color_pair(2)) else: self.screen.addstr(i - offset +8+self.show_begin_line, 19, str(i) + '. \'' + datalist[i][0].upper() + '\' ' + datalist[i][1] + ' ' + datalist[i][2]) self.screen.addstr(18+self.show_begin_line, 6, 'MusicBox 基于Python,所有版权音乐来源于网易,本地不做任何保存') self.screen.addstr(21+self.show_begin_line, 6, '按 [G] 到 Github 了解更多信息,帮助改进,或者Star表示支持~~') self.screen.refresh() def build_search(self, stype): netease = self.netease if stype == 'songs': song_name = self.get_param('搜索歌曲:') try: data = netease.search(song_name, stype=1) song_ids = [] if 'songs' in data['result']: if 'mp3Url' in data['result']['songs']: songs = data['result']['songs'] # if search song result do not has mp3Url # send ids to get mp3Url else: for i in range(0, len(data['result']['songs']) ): song_ids.append( data['result']['songs'][i]['id'] ) songs = netease.songs_detail(song_ids) return netease.dig_info(songs, 'songs') except: return [] elif stype == 'artists': artist_name = self.get_param('搜索艺术家:') try: data = netease.search(artist_name, stype=100) if 'artists' in data['result']: artists = data['result']['artists'] return netease.dig_info(artists, 'artists') except: return [] elif stype == 'albums': artist_name = self.get_param('搜索专辑:') try: data = netease.search(artist_name, stype=10) if 'albums' in data['result']: albums = data['result']['albums'] return netease.dig_info(albums, 'albums') except: return [] elif stype == 'search_playlist': artist_name = self.get_param('搜索网易精选集:') try: data = netease.search(artist_name, stype=1000) if 'playlists' in data['result']: playlists = data['result']['playlists'] return netease.dig_info(playlists, 'top_playlists') except: return [] return [] def build_search_menu(self): self.screen.move(4+self.show_begin_line,1) self.screen.clrtobot() self.screen.addstr(8+self.show_begin_line, 19, '选择搜索类型:', curses.color_pair(1)) self.screen.addstr(10+self.show_begin_line,19, '[1] 歌曲') self.screen.addstr(11+self.show_begin_line,19, '[2] 艺术家') self.screen.addstr(12+self.show_begin_line,19, '[3] 专辑') self.screen.addstr(13+self.show_begin_line,19, '[4] 网易精选集') self.screen.addstr(16+self.show_begin_line,19, '请键入对应数字:', curses.color_pair(2)) self.screen.refresh() x = self.screen.getch() return x def build_login(self): self.screen.move(7+self.show_begin_line,1) self.screen.clrtobot() curses.noecho() info = self.get_param('请输入登录信息(q or Q 退出登陆), e.g: [email protected] 123456') if str(info) in ('q','Q'): return -1 account = info.split(' ') if len(account) != 2: return self.build_login() login_info = self.netease.login(account[0], account[1]) if login_info['code'] != 200: x = self.build_login_error() if x == ord('1'): return self.build_login() else: return -1 else: return [login_info, account] def build_login_error(self): self.screen.move(4+self.show_begin_line,1) self.screen.clrtobot() self.screen.addstr(8+self.show_begin_line, 19, '艾玛,登录信息好像不对呢 (O_O)#', curses.color_pair(1)) self.screen.addstr(10+self.show_begin_line,19, '[1] 再试一次') self.screen.addstr(11+self.show_begin_line,19, '[2] 稍后再试') self.screen.addstr(14+self.show_begin_line,19, '请键入对应数字:', curses.color_pair(2)) self.screen.refresh() x = self.screen.getch() return x def get_param(self, prompt_string): # keep playing info in line 1 curses.echo() self.screen.move(4+self.show_begin_line,1) self.screen.clrtobot() self.screen.addstr(5+self.show_begin_line, 3, prompt_string, curses.color_pair(1)) self.screen.refresh() info = self.screen.getstr(10, 19, 60) if info.strip() is '': return self.get_param(prompt_string) else: return info def progress_bar(self,time_now,length): #进度条 cols=int(self.COLS)-18 time_now=int(ceil(float(time_now))) length=int(ceil(float(length))) if time_now>length: time_now=length surplus=length-time_now if length!=0 and length!=0: t_now="%2d:%02d" % (int(time_now//60),int(time_now%60)) t_surplus="%2d:%02d" % (int(surplus//60),int(surplus%60)) dutyfactor_1=int(ceil(float(time_now)/length*cols)) line=t_now+' '+'*'*dutyfactor_1+'>'+'-'*(cols-dutyfactor_1)+' '+t_surplus self.screen.addstr(20+self.show_begin_line,2,line,curses.color_pair(3)) self.screen.refresh() def notifySend(self,name,artist,other=''): #发送桌面通知 s=other+name+' -- <'+artist+'>' subprocess.call(['notify-send',s])
class Ui: def __init__(self): self.screen = curses.initscr() self.screen.timeout(100) # the screen refresh every 100ms # charactor break buffer curses.cbreak() self.screen.keypad(1) self.netease = NetEase() curses.start_color() curses.init_pair(1, curses.COLOR_GREEN, curses.COLOR_BLACK) curses.init_pair(2, curses.COLOR_CYAN, curses.COLOR_BLACK) curses.init_pair(3, curses.COLOR_RED, curses.COLOR_BLACK) curses.init_pair(4, curses.COLOR_YELLOW, curses.COLOR_BLACK) # term resize handling size = terminalsize.get_terminal_size() self.x = max(size[0], 10) self.y = max(size[1], 25) self.startcol = int(float(self.x) / 5) self.indented_startcol = max(self.startcol - 3, 0) self.update_space() self.lyric = "" self.now_lyric = "" self.storage = Storage() self.newversion = False def notify(self, summary, song, album, artist): if platform.system() == "Darwin": os.system('/usr/bin/osascript -e \'display notification "' + summary + ' ' + song + '\nin ' + album + ' by ' + artist +'"\'') else: os.system('/usr/bin/notify-send "' + summary + song + ' in ' + album + ' by ' + artist + '"') def build_playinfo(self, song_name, artist, album_name, quality, start, pause=False): curses.noecho() # refresh top 2 line self.screen.move(1, 1) self.screen.clrtoeol() self.screen.move(2, 1) self.screen.clrtoeol() if pause: self.screen.addstr(1, self.indented_startcol, '_ _ z Z Z ' + quality, curses.color_pair(3)) else: self.screen.addstr(1, self.indented_startcol, '♫ ♪ ♫ ♪ ' + quality, curses.color_pair(3)) self.screen.addstr(1, min(self.indented_startcol + 18, self.x - 1), song_name + self.space + artist + ' < ' + album_name + ' >', curses.color_pair(4)) # The following script doesn't work. It is intended to scroll the playinfo # Scrollstring works by determining how long since it is created, but # playinfo is created everytime the screen refreshes (every 500ms), unlike # the menu. Is there a workaround? # name = song_name + self.space + artist + ' < ' + album_name + ' >' # decides whether to scoll # if truelen(name) <= self.x - self.indented_startcol - 18: # self.screen.addstr(1, min(self.indented_startcol + 18, self.x-1), # name, # curses.color_pair(4)) # else: # name = scrollstring(name + ' ', start) # self.screen.addstr(1, min(self.indented_startcol + 18, self.x-1), # str(name), # curses.color_pair(4)) self.screen.refresh() def build_process_bar(self, now_playing, total_length, playing_flag, pause_flag, playing_mode): if (self.storage.database["player_info"]["idx"] >= len(self.storage.database["player_info"]["player_list"])): return curses.noecho() self.screen.move(3, 1) self.screen.clrtoeol() self.screen.move(4, 1) self.screen.clrtoeol() if not playing_flag: return if total_length <= 0: total_length = 1 if now_playing > total_length or now_playing <= 0: now_playing = 0 process = "[" for i in range(0, 33): if i < now_playing / total_length * 33: if (i + 1) > now_playing / total_length * 33: if not pause_flag: process += ">" continue process += "=" else: process += " " process += "] " now_minute = int(now_playing / 60) if now_minute > 9: now_minute = str(now_minute) else: now_minute = "0" + str(now_minute) now_second = int(now_playing - int(now_playing / 60) * 60) if now_second > 9: now_second = str(now_second) else: now_second = "0" + str(now_second) total_minute = int(total_length / 60) if total_minute > 9: total_minute = str(total_minute) else: total_minute = "0" + str(total_minute) total_second = int(total_length - int(total_length / 60) * 60) if total_second > 9: total_second = str(total_second) else: total_second = "0" + str(total_second) process += "(" + now_minute + ":" + now_second + "/" + total_minute + ":" + total_second + ")" if playing_mode == 0: process = "顺序播放 " + process elif playing_mode == 1: process = "顺序循环 " + process elif playing_mode == 2: process = "单曲循环 " + process elif playing_mode == 3: process = "随机播放 " + process elif playing_mode == 4: process = "随机循环 " + process else: pass self.screen.addstr(3, self.startcol - 2, process, curses.color_pair(1)) song = self.storage.database["songs"][ self.storage.database["player_info"]["player_list"][self.storage.database["player_info"]["idx"]] ] if 'lyric' not in song.keys() or len(song["lyric"]) <= 0: self.now_lyric = "[00:00.00]暂无歌词 ~>_<~ \n" else: key = now_minute + ":" + now_second for line in song["lyric"]: if key in line: self.now_lyric = line self.now_lyric = re.sub('\[.*?\]', "", self.now_lyric) self.screen.addstr(4, self.startcol - 2, str(self.now_lyric), curses.color_pair(3)) self.screen.refresh() def build_loading(self): self.screen.addstr(7, self.startcol, '享受高品质音乐,loading...', curses.color_pair(1)) self.screen.refresh() # start is the timestamp of this function being called def build_menu(self, datatype, title, datalist, offset, index, step, start): # keep playing info in line 1 curses.noecho() self.screen.move(5, 1) self.screen.clrtobot() self.screen.addstr(5, self.startcol, title, curses.color_pair(1)) if len(datalist) == 0: self.screen.addstr(8, self.startcol, '这里什么都没有 -,-') else: if datatype == 'main': for i in range(offset, min(len(datalist), offset + step)): if i == index: self.screen.addstr(i - offset + 9, self.indented_startcol, '-> ' + str(i) + '. ' + datalist[i], curses.color_pair(2)) else: self.screen.addstr(i - offset + 9, self.startcol, str(i) + '. ' + datalist[i]) elif datatype == 'songs' or datatype == 'fmsongs': iter_range = min(len(datalist), offset + step) for i in range(offset, iter_range): # this item is focus if i == index: self.screen.addstr(i - offset + 8, 0, ' ' * self.startcol) lead = '-> ' + str(i) + '. ' self.screen.addstr(i - offset + 8, self.indented_startcol, lead, curses.color_pair(2)) name = str(datalist[i]['song_name'] + self.space + datalist[i][ 'artist'] + ' < ' + datalist[i]['album_name'] + ' >') # the length decides whether to scoll if truelen(name) < self.x - self.startcol - 1: self.screen.addstr(i - offset + 8, self.indented_startcol + len(lead), name, curses.color_pair(2)) else: name = scrollstring(name + ' ', start) self.screen.addstr(i - offset + 8, self.indented_startcol + len(lead), str(name), curses.color_pair(2)) else: self.screen.addstr(i - offset + 8, 0, ' ' * self.startcol) self.screen.addstr(i - offset + 8, self.startcol, str(str(i) + '. ' + datalist[i]['song_name'] + self.space + datalist[i][ 'artist'] + ' < ' + datalist[i]['album_name'] + ' >')[:int(self.x * 2)]) self.screen.addstr(iter_range - offset + 9, 0, ' ' * self.x) elif datatype == 'artists': for i in range(offset, min(len(datalist), offset + step)): if i == index: self.screen.addstr(i - offset + 9, self.indented_startcol, '-> ' + str(i) + '. ' + datalist[i]['artists_name'] + self.space + str( datalist[i]['alias']), curses.color_pair(2)) else: self.screen.addstr(i - offset + 9, self.startcol, str(i) + '. ' + datalist[i]['artists_name'] + self.space + datalist[i][ 'alias']) elif datatype == 'albums': for i in range(offset, min(len(datalist), offset + step)): if i == index: self.screen.addstr(i - offset + 9, self.indented_startcol, '-> ' + str(i) + '. ' + datalist[i]['albums_name'] + self.space + datalist[i][ 'artists_name'], curses.color_pair(2)) else: self.screen.addstr(i - offset + 9, self.startcol, str(i) + '. ' + datalist[i]['albums_name'] + self.space + datalist[i][ 'artists_name']) elif datatype == 'playlists': for i in range(offset, min(len(datalist), offset + step)): if i == index: self.screen.addstr(i - offset + 9, self.indented_startcol, '-> ' + str(i) + '. ' + datalist[i]['title'], curses.color_pair(2)) else: self.screen.addstr(i - offset + 9, self.startcol, str(i) + '. ' + datalist[i]['title']) elif datatype == 'top_playlists': for i in range(offset, min(len(datalist), offset + step)): if i == index: self.screen.addstr(i - offset + 9, self.indented_startcol, '-> ' + str(i) + '. ' + datalist[i]['playlists_name'] + self.space + datalist[i]['creator_name'], curses.color_pair(2)) else: self.screen.addstr(i - offset + 9, self.startcol, str(i) + '. ' + datalist[i]['playlists_name'] + self.space + datalist[i][ 'creator_name']) elif datatype == 'toplists': for i in range(offset, min(len(datalist), offset + step)): if i == index: self.screen.addstr(i - offset + 9, self.indented_startcol, '-> ' + str(i) + '. ' + datalist[i], curses.color_pair(2)) else: self.screen.addstr(i - offset + 9, self.startcol, str(i) + '. ' + datalist[i]) elif datatype == 'playlist_classes' or datatype == 'playlist_class_detail': for i in range(offset, min(len(datalist), offset + step)): if i == index: self.screen.addstr(i - offset + 9, self.indented_startcol, '-> ' + str(i) + '. ' + datalist[i], curses.color_pair(2)) else: self.screen.addstr(i - offset + 9, self.startcol, str(i) + '. ' + datalist[i]) elif datatype == 'djchannels': for i in range(offset, min(len(datalist), offset + step)): if i == index: self.screen.addstr(i - offset + 8, self.indented_startcol, '-> ' + str(i) + '. ' + datalist[i]['song_name'], curses.color_pair(2)) else: self.screen.addstr(i - offset + 8, self.startcol, str(i) + '. ' + datalist[i]['song_name']) elif datatype == 'search': self.screen.move(4, 1) self.screen.clrtobot() self.screen.timeout(-1) self.screen.addstr(8, self.startcol, '选择搜索类型:', curses.color_pair(1)) for i in range(offset, min(len(datalist), offset + step)): if i == index: self.screen.addstr(i - offset + 10, self.indented_startcol, '-> ' + str(i) + '.' + datalist[i - 1], curses.color_pair(2)) else: self.screen.addstr(i - offset + 10, self.startcol, str(i) + '.' + datalist[i - 1]) self.screen.timeout(100) elif datatype == 'help': for i in range(offset, min(len(datalist), offset + step)): if i == index: self.screen.addstr(i - offset + 9, self.indented_startcol, '-> ' + str(i) + '. \'' + (datalist[i][0].upper() + '\'').ljust(11) + datalist[i][ 1] + ' ' + datalist[i][2], curses.color_pair(2)) else: self.screen.addstr(i - offset + 9, self.startcol, str(i) + '. \'' + (datalist[i][0].upper() + '\'').ljust(11) + datalist[i][ 1] + ' ' + datalist[i][2]) self.screen.addstr(20, 6, 'NetEase-MusicBox 基于Python,所有版权音乐来源于网易,本地不做任何保存') self.screen.addstr(21, 10, '按 [G] 到 Github 了解更多信息,帮助改进,或者Star表示支持~~') self.screen.addstr(22, self.startcol, 'Build with love to music by omi') self.screen.refresh() def build_search(self, stype): self.screen.timeout(-1) netease = self.netease if stype == 'songs': song_name = self.get_param('搜索歌曲:') if song_name == '/return': return [] else: try: data = netease.search(song_name, stype=1) song_ids = [] if 'songs' in data['result']: if 'mp3Url' in data['result']['songs']: songs = data['result']['songs'] # if search song result do not has mp3Url # send ids to get mp3Url else: for i in range(0, len(data['result']['songs'])): song_ids.append(data['result']['songs'][i]['id']) songs = netease.songs_detail(song_ids) return netease.dig_info(songs, 'songs') except: return [] elif stype == 'artists': artist_name = self.get_param('搜索艺术家:') if artist_name == '/return': return [] else: try: data = netease.search(artist_name, stype=100) if 'artists' in data['result']: artists = data['result']['artists'] return netease.dig_info(artists, 'artists') except: return [] elif stype == 'albums': albums_name = self.get_param('搜索专辑:') if albums_name == '/return': return [] else: try: data = netease.search(albums_name, stype=10) if 'albums' in data['result']: albums = data['result']['albums'] return netease.dig_info(albums, 'albums') except: return [] elif stype == 'search_playlist': search_playlist = self.get_param('搜索网易精选集:') if search_playlist == '/return': return [] else: try: data = netease.search(search_playlist, stype=1000) if 'playlists' in data['result']: playlists = data['result']['playlists'] return netease.dig_info(playlists, 'top_playlists') except: return [] return [] def build_login(self): self.build_login_bar() local_account = self.get_account() local_password = hashlib.md5(self.get_password()).hexdigest() login_info = self.netease.login(local_account, local_password) account = [local_account, local_password] if login_info['code'] != 200: x = self.build_login_error() if x == ord('1'): return self.build_login() else: return -1 else: return [login_info, account] def build_login_bar(self): curses.noecho() self.screen.move(4, 1) self.screen.clrtobot() self.screen.addstr(5, self.startcol, '请输入登录信息(支持手机登陆)', curses.color_pair(1)) self.screen.addstr(8, self.startcol, "账号:", curses.color_pair(1)) self.screen.addstr(9, self.startcol, "密码:", curses.color_pair(1)) self.screen.move(8, 24) self.screen.refresh() def build_login_error(self): self.screen.move(4, 1) self.screen.timeout(-1) # disable the screen timeout self.screen.clrtobot() self.screen.addstr(8, self.startcol, '艾玛,登录信息好像不对呢 (O_O)#', curses.color_pair(1)) self.screen.addstr(10, self.startcol, '[1] 再试一次') self.screen.addstr(11, self.startcol, '[2] 稍后再试') self.screen.addstr(14, self.startcol, '请键入对应数字:', curses.color_pair(2)) self.screen.refresh() x = self.screen.getch() self.screen.timeout(100) # restore the screen timeout return x def get_account(self): self.screen.timeout(-1) # disable the screen timeout curses.echo() account = self.screen.getstr(8, self.startcol + 6, 60) self.screen.timeout(100) # restore the screen timeout return account def get_password(self): self.screen.timeout(-1) # disable the screen timeout curses.noecho() password = self.screen.getstr(9, self.startcol + 6, 60) self.screen.timeout(100) # restore the screen timeout return password def get_param(self, prompt_string): # keep playing info in line 1 curses.echo() self.screen.move(4, 1) self.screen.clrtobot() self.screen.addstr(5, self.startcol, prompt_string, curses.color_pair(1)) self.screen.refresh() info = self.screen.getstr(10, self.startcol, 60) if info == '': return '/return' elif info.strip() is '': return self.get_param(prompt_string) else: return info def update_size(self): # get terminal size size = terminalsize.get_terminal_size() self.x = max(size[0], 10) self.y = max(size[1], 25) # update intendations curses.resizeterm(self.y, self.x) self.startcol = int(float(self.x) / 5) self.indented_startcol = max(self.startcol - 3, 0) self.update_space() self.screen.clear() self.screen.refresh() def update_space(self): if self.x > 140: self.space = " - " elif self.x > 80: self.space = " - " else: self.space = " - " self.screen.refresh()
class Menu: def __init__(self): reload(sys) sys.setdefaultencoding('UTF-8') self.config = Config() self.datatype = 'main' self.title = '网易云音乐' self.datalist = [ '排行榜', '艺术家', '新碟上架', '精选歌单', '我的歌单', 'DJ节目', '每日推荐', '私人FM', '搜索', '帮助' ] self.offset = 0 self.index = 0 self.storage = Storage() self.storage.load() self.collection = self.storage.database['collections'][0] self.player = Player() self.cache = Cache() self.ui = Ui() self.netease = NetEase() self.screen = curses.initscr() self.screen.keypad(1) self.step = 10 self.stack = [] self.djstack = [] self.userid = self.storage.database["user"]["user_id"] self.username = self.storage.database["user"]["nickname"] self.resume_play = True self.at_playing_list = False signal.signal(signal.SIGWINCH, self.change_term) signal.signal(signal.SIGINT, self.send_kill) self.START = time.time() def change_term(self, signum, frame): self.ui.screen.clear() self.ui.screen.refresh() def send_kill(self, signum, fram): self.player.stop() self.cache.quit() self.storage.save() curses.endwin() sys.exit() def alert(self, version): latest = Menu().check_version() if latest != version: if platform.system() == 'Darwin': os.system( '/usr/bin/osascript -e \'display notification "MusicBox Update is available"sound name "/System/Library/Sounds/Ping.aiff"\'' ) time.sleep(0.5) os.system( '/usr/bin/osascript -e \'display notification "NetEase-MusicBox installed version:' + version + '\nNetEase-MusicBox latest version:' + latest + '"\'') else: os.system( '/usr/bin/notify-send "MusicBox Update is available"') def check_version(self): # 检查更新 tree = ET.ElementTree(ET.fromstring(str(self.netease.get_version()))) root = tree.getroot() return root[0][4][0][0].text def start_fork(self, version): pid = os.fork() if pid == 0: Menu().alert(version) else: Menu().start() def play_pause(self): if len(self.storage.database["player_info"]["player_list"]) == 0: return if self.player.pause_flag: self.player.resume() else: self.player.pause() time.sleep(0.1) def next_song(self): if len(self.storage.database["player_info"]["player_list"]) == 0: return self.player.next() time.sleep(0.1) def previous_song(self): if len(self.storage.database["player_info"]["player_list"]) == 0: return self.player.prev() time.sleep(0.1) def start(self): self.START = time.time() // 1 self.ui.build_menu(self.datatype, self.title, self.datalist, self.offset, self.index, self.step, self.START) self.ui.build_process_bar( self.player.process_location, self.player.process_length, self.player.playing_flag, self.player.pause_flag, self.storage.database['player_info']['playing_mode']) self.stack.append([ self.datatype, self.title, self.datalist, self.offset, self.index ]) if bind_global: keybinder.bind(self.config.get_item("global_play_pause"), self.play_pause) keybinder.bind(self.config.get_item("global_next"), self.next_song) keybinder.bind(self.config.get_item("global_previous"), self.previous_song) while True: datatype = self.datatype title = self.title datalist = self.datalist offset = self.offset idx = index = self.index step = self.step stack = self.stack djstack = self.djstack self.screen.timeout(500) key = self.screen.getch() if bind_global: keybinder.gtk.main_iteration(False) self.ui.screen.refresh() # term resize if key == -1: self.ui.update_size() self.player.update_size() # 退出 if key == ord('q'): break # 退出并清除用户信息 if key == ord('w'): self.storage.database['user'] = { "username": "", "password": "", "user_id": "", "nickname": "", } try: os.remove(self.storage.cookie_path) except: break break # 上移 elif key == ord('k'): self.index = carousel(offset, min(len(datalist), offset + step) - 1, idx - 1) self.START = time.time() # 下移 elif key == ord('j'): self.index = carousel(offset, min(len(datalist), offset + step) - 1, idx + 1) self.START = time.time() # 数字快捷键 elif ord('0') <= key <= ord('9'): if self.datatype == 'songs' or self.datatype == 'djchannels' or self.datatype == 'help': continue idx = key - ord('0') self.ui.build_menu(self.datatype, self.title, self.datalist, self.offset, idx, self.step, self.START) self.ui.build_loading() self.dispatch_enter(idx) self.index = 0 self.offset = 0 # 向上翻页 elif key == ord('u'): if offset == 0: continue self.START = time.time() self.offset -= step # e.g. 23 - 10 = 13 --> 10 self.index = (index - step) // step * step # 向下翻页 elif key == ord('d'): if offset + step >= len(datalist): continue self.START = time.time() self.offset += step # e.g. 23 + 10 = 33 --> 30 self.index = (index + step) // step * step # 前进 elif key == ord('l') or key == 10: if self.datatype == 'songs' or self.datatype == 'djchannels' or self.datatype == 'help': continue self.START = time.time() self.ui.build_loading() self.dispatch_enter(idx) self.index = 0 self.offset = 0 # 回退 elif key == ord('h'): # if not main menu if len(self.stack) == 1: continue self.START = time.time() up = stack.pop() self.datatype = up[0] self.title = up[1] self.datalist = up[2] self.offset = up[3] self.index = up[4] self.at_playing_list = False # 搜索 elif key == ord('f'): # 8 is the 'search' menu self.dispatch_enter(8) # 播放下一曲 elif key == ord(']'): self.next_song() # 播放上一曲 elif key == ord('['): self.previous_song() # 增加音量 elif key == ord('='): self.player.volume_up() # 减少音量 elif key == ord('-'): self.player.volume_down() # 随机播放 elif key == ord('?'): if len(self.storage.database["player_info"] ["player_list"]) == 0: continue self.player.shuffle() time.sleep(0.1) # 喜爱 elif key == ord(','): self.request_api(self.netease.fm_like, self.player.get_playing_id()) # 删除FM elif key == ord('.'): if self.datatype == 'fmsongs': if len(self.storage.database["player_info"] ["player_list"]) == 0: continue self.player.next() self.request_api(self.netease.fm_trash, self.player.get_playing_id()) time.sleep(0.1) # 下一FM elif key == ord('/'): if self.datatype == 'fmsongs': if len(self.storage.database["player_info"] ["player_list"]) == 0: continue self.player.next() time.sleep(0.1) # 播放、暂停 elif key == ord(' '): # If not open a new playing list, just play and pause. try: if self.datalist[idx]['song_id'] == self.player.playing_id: self.player.play_and_pause( self.storage.database['player_info']['idx']) time.sleep(0.1) continue except: pass # If change to a new playing list. Add playing list and play. if datatype == 'songs': self.resume_play = False self.player.new_player_list('songs', self.title, self.datalist, -1) self.player.end_callback = None self.player.play_and_pause(idx) self.at_playing_list = True elif datatype == 'djchannels': self.resume_play = False self.player.new_player_list('djchannels', self.title, self.datalist, -1) self.player.end_callback = None self.player.play_and_pause(idx) self.at_playing_list = True elif datatype == 'fmsongs': self.resume_play = False self.storage.database['player_info']['playing_mode'] = 0 self.player.new_player_list('fmsongs', self.title, self.datalist, -1) self.player.end_callback = self.fm_callback self.player.play_and_pause(idx) self.at_playing_list = True else: self.player.play_and_pause( self.storage.database['player_info']['idx']) time.sleep(0.1) # 加载当前播放列表 elif key == ord('p'): if len(self.storage.database['player_info'] ['player_list']) == 0: continue if not self.at_playing_list: self.stack.append([ self.datatype, self.title, self.datalist, self.offset, self.index ]) self.at_playing_list = True self.datatype = self.storage.database['player_info'][ 'player_list_type'] self.title = self.storage.database['player_info'][ 'player_list_title'] self.datalist = [] for i in self.storage.database['player_info']['player_list']: self.datalist.append(self.storage.database['songs'][i]) self.index = self.storage.database['player_info']['idx'] self.offset = self.storage.database['player_info'][ 'idx'] / self.step * self.step if self.resume_play: if self.datatype == "fmsongs": self.player.end_callback = self.fm_callback else: self.player.end_callback = None self.storage.database['player_info']['idx'] = -1 self.player.play_and_pause(self.index) self.resume_play = False # 播放模式切换 elif key == ord('P'): self.storage.database['player_info']['playing_mode'] = \ (self.storage.database['player_info']['playing_mode'] + 1) % 5 # 添加到打碟歌单 elif key == ord('a'): if datatype == 'songs' and len(datalist) != 0: self.djstack.append(datalist[idx]) elif datatype == 'artists': pass # 加载打碟歌单 elif key == ord('z'): self.stack.append([datatype, title, datalist, offset, index]) self.datatype = 'songs' self.title = '网易云音乐 > 打碟' self.datalist = self.djstack self.offset = 0 self.index = 0 # 添加到收藏歌曲 elif key == ord('s'): if (datatype == 'songs' or datatype == 'djchannels') and len(datalist) != 0: self.collection.append(datalist[idx]) # 加载收藏歌曲 elif key == ord('c'): self.stack.append([datatype, title, datalist, offset, index]) self.datatype = 'songs' self.title = '网易云音乐 > 收藏' self.datalist = self.collection self.offset = 0 self.index = 0 # 从当前列表移除 elif key == ord('r'): if (datatype == 'songs' or datatype == 'djchannels') and len(datalist) != 0: self.datalist.pop(idx) self.index = carousel( offset, min(len(datalist), offset + step) - 1, idx) # 当前项目下移 elif key == ord("J"): if datatype != 'main' and len( datalist) != 0 and idx + 1 != len(self.datalist): self.START = time.time() song = self.datalist.pop(idx) self.datalist.insert(idx + 1, song) self.index = idx + 1 # 翻页 if self.index >= offset + step: self.offset = offset + step # 当前项目上移 elif key == ord("K"): if datatype != 'main' and len(datalist) != 0 and idx != 0: self.START = time.time() song = self.datalist.pop(idx) self.datalist.insert(idx - 1, song) self.index = idx - 1 # 翻页 if self.index < offset: self.offset = offset - step elif key == ord('m'): if datatype != 'main': self.stack.append( [datatype, title, datalist, offset, index]) self.datatype = self.stack[0][0] self.title = self.stack[0][1] self.datalist = self.stack[0][2] self.offset = 0 self.index = 0 elif key == ord('g'): if datatype == 'help': webbrowser.open_new_tab( 'https://github.com/darknessomi/musicbox') self.ui.build_process_bar( self.player.process_location, self.player.process_length, self.player.playing_flag, self.player.pause_flag, self.storage.database['player_info']['playing_mode']) self.ui.build_menu(self.datatype, self.title, self.datalist, self.offset, self.index, self.step, self.START) self.player.stop() self.cache.quit() self.storage.save() curses.endwin() def dispatch_enter(self, idx): # The end of stack netease = self.netease datatype = self.datatype title = self.title datalist = self.datalist offset = self.offset index = self.index self.stack.append([datatype, title, datalist, offset, index]) if datatype == 'main': self.choice_channel(idx) # 该艺术家的热门歌曲 elif datatype == 'artists': artist_id = datalist[idx]['artist_id'] songs = netease.artists(artist_id) self.datatype = 'songs' self.datalist = netease.dig_info(songs, 'songs') self.title += ' > ' + datalist[idx]['artists_name'] # 该专辑包含的歌曲 elif datatype == 'albums': album_id = datalist[idx]['album_id'] songs = netease.album(album_id) self.datatype = 'songs' self.datalist = netease.dig_info(songs, 'songs') self.title += ' > ' + datalist[idx]['albums_name'] # 精选歌单选项 elif datatype == 'playlists': data = self.datalist[idx] self.datatype = data['datatype'] self.datalist = netease.dig_info(data['callback'](), self.datatype) self.title += ' > ' + data['title'] # 全站置顶歌单包含的歌曲 elif datatype == 'top_playlists': log.debug(datalist) playlist_id = datalist[idx]['playlist_id'] songs = netease.playlist_detail(playlist_id) self.datatype = 'songs' self.datalist = netease.dig_info(songs, 'songs') self.title += ' > ' + datalist[idx]['playlists_name'] # 分类精选 elif datatype == 'playlist_classes': # 分类名称 data = self.datalist[idx] self.datatype = 'playlist_class_detail' self.datalist = netease.dig_info(data, self.datatype) self.title += ' > ' + data log.debug(self.datalist) # 某一分类的详情 elif datatype == 'playlist_class_detail': # 子类别 data = self.datalist[idx] self.datatype = 'top_playlists' self.datalist = netease.dig_info(netease.top_playlists(data), self.datatype) log.debug(self.datalist) self.title += ' > ' + data # 歌曲榜单 elif datatype == 'toplists': songs = netease.top_songlist(idx) self.title += ' > ' + self.datalist[idx] self.datalist = netease.dig_info(songs, 'songs') self.datatype = 'songs' # 搜索菜单 elif datatype == 'search': ui = self.ui # no need to do stack.append, Otherwise there will be a bug when you input key 'h' to return # if idx in range(1, 5): # self.stack.append([self.datatype, self.title, self.datalist, self.offset, self.index]) self.index = 0 self.offset = 0 if idx == 0: # 搜索结果可以用top_playlists处理 self.datatype = 'top_playlists' self.datalist = ui.build_search('search_playlist') self.title = '精选歌单搜索列表' elif idx == 1: self.datatype = 'songs' self.datalist = ui.build_search('songs') self.title = '歌曲搜索列表' elif idx == 2: self.datatype = 'artists' self.datalist = ui.build_search('artists') self.title = '艺术家搜索列表' elif idx == 3: self.datatype = 'albums' self.datalist = ui.build_search('albums') self.title = '专辑搜索列表' def fm_callback(self): log.debug("FM CallBack.") data = self.get_new_fm() self.player.append_songs(data) if self.datatype == 'fmsongs': if len(self.storage.database['player_info']['player_list']) == 0: return self.datatype = self.storage.database['player_info'][ 'player_list_type'] self.title = self.storage.database['player_info'][ 'player_list_title'] self.datalist = [] for i in self.storage.database['player_info']['player_list']: self.datalist.append(self.storage.database['songs'][i]) self.index = self.storage.database['player_info']['idx'] self.offset = self.storage.database['player_info'][ 'idx'] / self.step * self.step def request_api(self, func, *args): if self.storage.database['user']['user_id'] != "": result = func(*args) if result != -1: return result log.debug("Re Login.") user_info = {} if self.storage.database['user']['username'] != "": user_info = self.netease.login( self.storage.database['user']['username'], self.storage.database['user']['password']) if self.storage.database['user'][ 'username'] == "" or user_info['code'] != 200: data = self.ui.build_login() # 取消登录 if data == -1: return -1 user_info = data[0] self.storage.database['user']['username'] = data[1][0] self.storage.database['user']['password'] = data[1][1] self.storage.database['user']['user_id'] = user_info['account'][ 'id'] self.storage.database['user']['nickname'] = user_info['profile'][ 'nickname'] self.userid = self.storage.database["user"]["user_id"] self.username = self.storage.database["user"]["nickname"] return func(*args) def get_new_fm(self): myplaylist = [] for count in range(0, 1): data = self.request_api(self.netease.personal_fm) if data == -1: break myplaylist += data time.sleep(0.2) return self.netease.dig_info(myplaylist, "fmsongs") def choice_channel(self, idx): # 排行榜 netease = self.netease if idx == 0: self.datalist = netease.return_toplists() self.title += ' > 排行榜' self.datatype = 'toplists' # 艺术家 elif idx == 1: artists = netease.top_artists() self.datalist = netease.dig_info(artists, 'artists') self.title += ' > 艺术家' self.datatype = 'artists' # 新碟上架 elif idx == 2: albums = netease.new_albums() self.datalist = netease.dig_info(albums, 'albums') self.title += ' > 新碟上架' self.datatype = 'albums' # 精选歌单 elif idx == 3: self.datalist = [{ 'title': '全站置顶', 'datatype': 'top_playlists', 'callback': netease.top_playlists }, { 'title': '分类精选', 'datatype': 'playlist_classes', 'callback': netease.playlist_classes }] self.title += ' > 精选歌单' self.datatype = 'playlists' # 我的歌单 elif idx == 4: myplaylist = self.request_api(self.netease.user_playlist, self.userid) if myplaylist == -1: return self.datatype = 'top_playlists' self.datalist = netease.dig_info(myplaylist, self.datatype) self.title += ' > ' + self.username + ' 的歌单' # DJ节目 elif idx == 5: self.datatype = 'djchannels' self.title += ' > DJ节目' self.datalist = netease.djchannels() # 每日推荐 elif idx == 6: self.datatype = 'songs' self.title += ' > 每日推荐' myplaylist = self.request_api(self.netease.recommend_playlist) if myplaylist == -1: return self.datalist = self.netease.dig_info(myplaylist, self.datatype) # 私人FM elif idx == 7: self.datatype = 'fmsongs' self.title += ' > 私人FM' self.datalist = self.get_new_fm() # 搜索 elif idx == 8: self.datatype = 'search' self.title += ' > 搜索' self.datalist = ['歌曲', '艺术家', '专辑', '网易精选集'] # 帮助 elif idx == 9: self.datatype = 'help' self.title += ' > 帮助' self.datalist = shortcut self.offset = 0 self.index = 0
class Ui: def __init__(self): # Initializing curses # initialize window object to curses.initscr() self.screen = curses.initscr() # character break buffer, to read in any key without the need to press 'Enter'. curses.cbreak() # make those navigation keys to normal key that can be processed in program. self.screen.keypad(1) # initialize the some color pair curses.start_color() curses.init_pair(1, curses.COLOR_GREEN, curses.COLOR_BLACK) curses.init_pair(2, curses.COLOR_CYAN, curses.COLOR_BLACK) curses.init_pair(3, curses.COLOR_RED, curses.COLOR_BLACK) curses.init_pair(4, curses.COLOR_YELLOW, curses.COLOR_BLACK) # Initializing NetEase object to communicate with NetEase server. self.netease = NetEase() def build_playinfo(self, song_name, artist, album_name, pause=False, play_mode='normal'): curses.noecho() # refresh top 2 line self.screen.move(1, 1) self.screen.clrtoeol() self.screen.move(2, 1) self.screen.clrtoeol() if pause: self.screen.addstr(1, 0, play_mode + ' ' + '_ _ z Z Z', curses.color_pair(3)) else: self.screen.addstr(1, 0, play_mode + ' ' + '♫ ♪ ♫ ♪', curses.color_pair(3)) self.screen.addstr(1, 19, song_name + ' - ' + artist + ' < ' + album_name + ' >', curses.color_pair(4)) self.screen.refresh() def build_loading(self): self.screen.addstr(6, 19, '享受高品质音乐,loading...', curses.color_pair(1)) self.screen.refresh() # draw the all types of menu according to data_type def build_menu(self, data_type, title, data_list, offset, index, step): # keep playing info in line 1 # do not echo the input key-code curses.noecho() # clear the content after (4,1) # move cursor to (4,1) self.screen.move(4, 1) # clear the content under line-4, erase from cursor to the end of the window: all lines below the cursor are deleted, # and then the equivalent of clrtoeol() is performed. self.screen.clrtobot() # draw the title with color pair 1 (GREEN, BLACK) self.screen.addstr(4, 19, title, curses.color_pair(1)) # If there's nothing in the data_list if len(data_list) == 0: self.screen.addstr(8, 19, '这里什么都没有 -,-') else: if data_type == 'main': for i in range(offset, min(len(data_list), offset + step)): content = str(i) + '. ' + data_list[i] self.print_menu_line(i, offset, content, i == index) elif data_type == 'songs': for i in range(offset, min(len(data_list), offset + step)): content = str(i) + '. ' + data_list[i]['song_name'] \ + ' - ' + data_list[i]['artist'] + ' < ' + data_list[i]['album_name'] + ' >' self.print_menu_line(i, offset, content, i == index) elif data_type == 'artists': for i in range(offset, min(len(data_list), offset + step)): content = str(i) + '. ' + data_list[i]['artists_name'] + ' - ' + str(data_list[i]['alias']) self.print_menu_line(i, offset, content, i == index) elif data_type == 'albums': for i in range(offset, min(len(data_list), offset + step)): content = str(i) + '. ' + data_list[i]['albums_name'] + ' - ' + data_list[i]['artists_name'] self.print_menu_line(i, offset, content, i == index) elif data_type == 'playlists': for i in range(offset, min(len(data_list), offset + step)): content = str(i) + '. ' + data_list[i]['title'] self.print_menu_line(i, offset, content, i == index) elif data_type == 'top_playlists': for i in range(offset, min(len(data_list), offset + step)): content = str(i) + '. ' + data_list[i]['playlists_name'] + ' - ' + data_list[i]['creator_name'] self.print_menu_line(i, offset, content, i == index) elif data_type == 'playlist_classes' or data_type == 'playlist_class_detail': for i in range(offset, min(len(data_list), offset + step)): content = str(i) + '. ' + data_list[i] self.print_menu_line(i, offset, content, i == index) elif data_type == 'djchannels': for i in range(offset, min(len(data_list), offset + step)): content = str(i) + '. ' + data_list[i]['song_name'] self.print_menu_line(i, offset, content, i == index) elif data_type == 'help': for i in range(offset, min(len(data_list), offset + step)): content = str(i) + '. \'' + data_list[i][0].upper() + '\' ' + data_list[i][1] + ' ' + \ data_list[i][2] self.print_menu_line(i, offset, content, i == index) self.screen.addstr(20, 6, 'NetEase-MusicBox 基于Python,所有版权音乐来源于网易,本地不做任何保存') self.screen.addstr(21, 10, '按 [G] 到 Git-hub 了解更多信息,帮助改进,或者Star表示支持~~') self.screen.addstr(22, 19, 'Build with love to music by omi') self.screen.refresh() # print the specified line at certain place def print_menu_line(self, i, offset, content, chosen=False): y = i - offset + 8 if chosen == True: x = 16 color = curses.color_pair(2) self.screen.addstr(y, x, '-> ' + content, color) else: x = 19 self.screen.addstr(y, x, content) def build_search(self, stype): netease = self.netease if stype == 'songs': song_name = self.get_param('搜索歌曲:') try: data = netease.search(song_name, stype=1) song_ids = [] if 'songs' in data['result']: if 'mp3Url' in data['result']['songs']: songs = data['result']['songs'] # if search song result do not has mp3Url # send ids to get mp3Url else: for i in range(0, len(data['result']['songs'])): song_ids.append(data['result']['songs'][i]['id']) songs = netease.songs_detail(song_ids) return netease.dig_info(songs, 'songs') except: return [] elif stype == 'artists': artist_name = self.get_param('搜索艺术家:') try: data = netease.search(artist_name, stype=100) if 'artists' in data['result']: artists = data['result']['artists'] return netease.dig_info(artists, 'artists') except: return [] elif stype == 'albums': artist_name = self.get_param('搜索专辑:') try: data = netease.search(artist_name, stype=10) if 'albums' in data['result']: albums = data['result']['albums'] return netease.dig_info(albums, 'albums') except: return [] elif stype == 'search_playlist': artist_name = self.get_param('搜索网易精选集:') try: data = netease.search(artist_name, stype=1000) if 'playlists' in data['result']: playlists = data['result']['playlists'] return netease.dig_info(playlists, 'top_playlists') except: return [] return [] def build_search_menu(self): self.screen.move(4, 1) self.screen.clrtobot() self.screen.addstr(8, 19, '选择搜索类型:', curses.color_pair(1)) self.screen.addstr(10, 19, '[1] 歌曲') self.screen.addstr(11, 19, '[2] 艺术家') self.screen.addstr(12, 19, '[3] 专辑') self.screen.addstr(13, 19, '[4] 网易精选集') self.screen.addstr(16, 19, '请键入对应数字:', curses.color_pair(2)) self.screen.refresh() x = self.screen.getch() return x # build login interface and login def build_login(self): curses.noecho() info = self.get_param('请输入登录信息, e.g: [email protected] 123456') account = info.split(' ') # if both user-name and password is got. if len(account) != 2: return self.build_login() # login login_info = self.netease.login(account[0], account[1]) self.screen.refresh() # if login successfully if login_info['code'] != 200: x = self.build_login_error() if x == ord('1'): return self.build_login() else: return -1 else: return [login_info, account] def build_login_error(self): self.screen.move(4, 1) self.screen.clrtobot() self.screen.addstr(8, 19, '艾玛,登录信息好像不对呢 (O_O)#', curses.color_pair(1)) self.screen.addstr(10, 19, '[1] 再试一次') self.screen.addstr(11, 19, '[2] 稍后再试') self.screen.addstr(14, 19, '请键入对应数字:', curses.color_pair(2)) self.screen.refresh() x = self.screen.getch() return x def get_param(self, prompt_string): # When getting the user-name and password, we need to echo the letter user typed in. curses.echo() # clear the content after the 4th line self.screen.move(4, 1) self.screen.clrtobot() # print and display the prompt message to input user-name self.screen.addstr(5, 19, prompt_string, curses.color_pair(1)) self.screen.addstr(9, 19, "Please input the user-name: ") self.screen.refresh() info = self.screen.getstr(10, 19, 60) # print the prompt to enter password self.screen.move(11, 19) # close curses mode to get password without display it self.end_curses() info = info + " " + getpass.getpass(' Password:'******'': return self.get_param(prompt_string) else: return info def start_curses(self): curses.noecho() curses.cbreak() self.screen.keypad(1) def end_curses(self): curses.nocbreak() self.screen.keypad(0) curses.echo() def redraw(self): self.screen.clear() self.screen.addstr(4, 19, '网易云音乐', curses.color_pair(1)) self.screen.refresh()
class Ui: def __init__(self): self.screen = curses.initscr() # charactor break buffer curses.cbreak() self.screen.keypad(1) self.netease = NetEase() curses.start_color() curses.init_pair(1, curses.COLOR_GREEN, curses.COLOR_BLACK) curses.init_pair(2, curses.COLOR_CYAN, curses.COLOR_BLACK) curses.init_pair(3, curses.COLOR_RED, curses.COLOR_BLACK) curses.init_pair(4, curses.COLOR_YELLOW, curses.COLOR_BLACK) def build_playinfo(self, song_name, artist, album_name, pause=False): # refresh top 2 line self.screen.move(1,1) self.screen.clrtoeol() self.screen.move(2,1) self.screen.clrtoeol() if pause: self.screen.addstr(1, 6, '_ _ z Z Z', curses.color_pair(3)) else: self.screen.addstr(1, 6, '♫ ♪ ♫ ♪', curses.color_pair(3)) self.screen.addstr(1, 19, song_name + ' - ' + artist + ' < ' + album_name + ' >', curses.color_pair(4)) self.screen.refresh() def build_loading(self): self.screen.addstr(6, 19, '享受高品质音乐,loading...', curses.color_pair(1)) self.screen.refresh() def build_menu(self, datatype, title, datalist, offset, index, step): # keep playing info in line 1 self.screen.move(4,1) self.screen.clrtobot() self.screen.addstr(4, 19, title, curses.color_pair(1)) if len(datalist) == 0: self.screen.addstr(8, 19, '这里什么都没有 -,-') else: if datatype == 'main': for i in range( offset, min( len(datalist), offset+step) ): if i == index: self.screen.addstr(i - offset +8, 16, '-> ' + str(i) + '. ' + datalist[i], curses.color_pair(2)) else: self.screen.addstr(i - offset +8, 19, str(i) + '. ' + datalist[i]) elif datatype == 'songs': for i in range(offset, min( len(datalist), offset+step) ): # this item is focus if i == index: self.screen.addstr(i - offset +8, 16, '-> ' + str(i) + '. ' + datalist[i]['song_name'] + ' - ' + datalist[i]['artist'] + ' < ' + datalist[i]['album_name'] + ' >', curses.color_pair(2)) else: self.screen.addstr(i - offset +8, 19, str(i) + '. ' + datalist[i]['song_name'] + ' - ' + datalist[i]['artist'] + ' < ' + datalist[i]['album_name'] + ' >') elif datatype == 'artists': for i in range(offset, min( len(datalist), offset+step) ): if i == index: self.screen.addstr(i - offset +8, 16, '-> ' + str(i) + '. ' + datalist[i]['artists_name'] + ' - ' + str(datalist[i]['alias']), curses.color_pair(2)) else: self.screen.addstr(i - offset +8, 19, str(i) + '. ' + datalist[i]['artists_name'] + ' - ' + datalist[i]['alias']) elif datatype == 'albums': for i in range(offset, min( len(datalist), offset+step) ): if i == index: self.screen.addstr(i - offset +8, 16, '-> ' + str(i) + '. ' + datalist[i]['albums_name'] + ' - ' + datalist[i]['artists_name'], curses.color_pair(2)) else: self.screen.addstr(i - offset +8, 19, str(i) + '. ' + datalist[i]['albums_name'] + ' - ' + datalist[i]['artists_name']) elif datatype == 'playlists': for i in range(offset, min( len(datalist), offset+step) ): if i == index: self.screen.addstr(i - offset +8, 16, '-> ' + str(i) + '. ' + datalist[i]['playlists_name'] + ' - ' + datalist[i]['creator_name'], curses.color_pair(2)) else: self.screen.addstr(i - offset +8, 19, str(i) + '. ' + datalist[i]['playlists_name'] + ' - ' + datalist[i]['creator_name']) elif datatype == 'djchannels': for i in range(offset, min( len(datalist), offset+step) ): if i == index: self.screen.addstr(i - offset +8, 16, '-> ' + str(i) + '. ' + datalist[i]['song_name'], curses.color_pair(2)) else: self.screen.addstr(i - offset +8, 19, str(i) + '. ' + datalist[i]['song_name']) elif datatype == 'help': for i in range(offset, min( len(datalist), offset+step) ): if i == index: self.screen.addstr(i - offset +8, 16, '-> ' + str(i) + '. \'' + datalist[i][0].upper() + '\' ' + datalist[i][1] + ' ' + datalist[i][2], curses.color_pair(2)) else: self.screen.addstr(i - offset +8, 19, str(i) + '. \'' + datalist[i][0].upper() + '\' ' + datalist[i][1] + ' ' + datalist[i][2]) self.screen.addstr(20, 6, 'NetEase-MusicBox 基于Python,所有版权音乐来源于网易,本地不做任何保存') self.screen.addstr(21, 10, '按 [G] 到 Github 了解更多信息,帮助改进,或者Star表示支持~~') self.screen.addstr(22, 19, 'Build with love to music by @vellow') self.screen.refresh() def build_search(self, stype): netease = self.netease if stype == 'songs': song_name = self.get_param('搜索歌曲:') try: data = netease.search(song_name, stype=1) song_ids = [] if 'songs' in data['result']: if 'mp3Url' in data['result']['songs']: songs = data['result']['songs'] # if search song result do not has mp3Url # send ids to get mp3Url else: for i in range(0, len(data['result']['songs']) ): song_ids.append( data['result']['songs'][i]['id'] ) songs = netease.songs_detail(song_ids) return netease.dig_info(songs, 'songs') except: return [] elif stype == 'artists': artist_name = self.get_param('搜索艺术家:') try: data = netease.search(artist_name, stype=100) if 'artists' in data['result']: artists = data['result']['artists'] return netease.dig_info(artists, 'artists') except: return [] elif stype == 'albums': artist_name = self.get_param('搜索专辑:') try: data = netease.search(artist_name, stype=10) if 'albums' in data['result']: albums = data['result']['albums'] return netease.dig_info(albums, 'albums') except: return [] elif stype == 'playlists': artist_name = self.get_param('搜索网易精选集:') try: data = netease.search(artist_name, stype=1000) if 'playlists' in data['result']: playlists = data['result']['playlists'] return netease.dig_info(playlists, 'playlists') except: return [] return [] def build_search_menu(self): self.screen.move(4,1) self.screen.clrtobot() self.screen.addstr(8, 19, '选择搜索类型:', curses.color_pair(1)) self.screen.addstr(10,19, '[1] 歌曲') self.screen.addstr(11,19, '[2] 艺术家') self.screen.addstr(12,19, '[3] 专辑') self.screen.addstr(13,19, '[4] 网易精选集') self.screen.addstr(16,19, '请键入对应数字:', curses.color_pair(2)) self.screen.refresh() x = self.screen.getch() return x def build_login(self): info = self.get_param('请输入登录信息, e.g: [email protected] 123456') account = info.split(' ') if len(account) != 2: return self.build_login() login_info = self.netease.login(account[0], account[1]) if login_info['code'] != 200: x = self.build_login_error() if x == ord('1'): return self.build_login() else: return -1 else: return [login_info, account] def build_login_error(self): self.screen.move(4,1) self.screen.clrtobot() self.screen.addstr(8, 19, '艾玛,登录信息好像不对呢 (O_O)#', curses.color_pair(1)) self.screen.addstr(10,19, '[1] 再试一次') self.screen.addstr(11,19, '[2] 稍后再试') self.screen.addstr(14,19, '请键入对应数字:', curses.color_pair(2)) self.screen.refresh() x = self.screen.getch() return x def get_param(self, prompt_string): # keep playing info in line 1 self.screen.move(4,1) self.screen.clrtobot() self.screen.addstr(5, 19, prompt_string, curses.color_pair(1)) self.screen.refresh() info = self.screen.getstr(10, 19, 60) if info.strip() is '': return self.get_param(prompt_string) else: return info
class Menu: def __init__(self): reload(sys) sys.setdefaultencoding("UTF-8") self.config = Config() self.datatype = "main" self.title = "网易云音乐" self.datalist = ["排行榜", "艺术家", "新碟上架", "精选歌单", "我的歌单", "DJ节目", "每日推荐", "私人FM", "搜索", "帮助"] self.offset = 0 self.index = 0 self.storage = Storage() self.storage.load() self.collection = self.storage.database["collections"][0] self.player = Player() self.player.playing_song_changed_callback = self.song_changed_callback self.cache = Cache() self.ui = Ui() self.netease = NetEase() self.screen = curses.initscr() self.screen.keypad(1) self.step = 10 self.stack = [] self.djstack = [] self.userid = self.storage.database["user"]["user_id"] self.username = self.storage.database["user"]["nickname"] self.resume_play = True self.at_playing_list = False signal.signal(signal.SIGWINCH, self.change_term) signal.signal(signal.SIGINT, self.send_kill) self.START = time.time() def change_term(self, signum, frame): self.ui.screen.clear() self.ui.screen.refresh() def send_kill(self, signum, fram): self.player.stop() self.cache.quit() self.storage.save() curses.endwin() sys.exit() def alert(self, version): latest = Menu().check_version() if latest != version: if platform.system() == "Darwin": os.system( '/usr/bin/osascript -e \'display notification "MusicBox Update is available"sound name "/System/Library/Sounds/Ping.aiff"\'' ) time.sleep(0.5) os.system( "/usr/bin/osascript -e 'display notification \"NetEase-MusicBox installed version:" + version + "\nNetEase-MusicBox latest version:" + latest + "\"'" ) else: os.system('/usr/bin/notify-send "MusicBox Update is available"') def check_version(self): # 检查更新 tree = ET.ElementTree(ET.fromstring(str(self.netease.get_version()))) root = tree.getroot() return root[0][4][0][0].text def start_fork(self, version): # Hong, Fork a child process. # Return 0 in the child and the child’s process id in the parent. pid = os.fork() if pid == 0: Menu().alert(version) else: Menu().start() def play_pause(self): if len(self.storage.database["player_info"]["player_list"]) == 0: return if self.player.pause_flag: self.player.resume() else: self.player.pause() time.sleep(0.1) def next_song(self): if len(self.storage.database["player_info"]["player_list"]) == 0: return self.player.next() time.sleep(0.1) def previous_song(self): if len(self.storage.database["player_info"]["player_list"]) == 0: return self.player.prev() time.sleep(0.1) def start(self): # Hong, Return the time in seconds since the epoch as a floating point number. self.START = time.time() // 1 self.ui.build_menu(self.datatype, self.title, self.datalist, self.offset, self.index, self.step, self.START) self.ui.build_process_bar( self.player.process_location, self.player.process_length, self.player.playing_flag, self.player.pause_flag, self.storage.database["player_info"]["playing_mode"], ) self.stack.append([self.datatype, self.title, self.datalist, self.offset, self.index]) if bind_global: keybinder.bind(self.config.get_item("global_play_pause"), self.play_pause) keybinder.bind(self.config.get_item("global_next"), self.next_song) keybinder.bind(self.config.get_item("global_previous"), self.previous_song) while True: datatype = self.datatype title = self.title datalist = self.datalist offset = self.offset idx = index = self.index step = self.step stack = self.stack djstack = self.djstack self.screen.timeout(500) key = self.screen.getch() if bind_global: keybinder.gtk.main_iteration(False) self.ui.screen.refresh() # term resize if key == -1: self.ui.update_size() self.player.update_size() # 退出 # Hong, ord(), Given a string of length one, return an integer representing the Unicode code point of the character when the argument is a unicode object, # or the value of the byte when the argument is an 8-bit string. if key == ord("q"): break # 退出并清除用户信息 if key == ord("w"): self.storage.database["user"] = {"username": "", "password": "", "user_id": "", "nickname": ""} try: os.remove(self.storage.cookie_path) except: break break # 上移 elif key == ord("k"): # turn page if at beginning if idx == offset: if offset == 0: continue self.offset -= step # 移动光标到最后一列 self.index = offset - 1 else: self.index = carousel(offset, min(len(datalist), offset + step) - 1, idx - 1) self.START = time.time() # 下移 elif key == ord("j"): # turn page if at end if idx == min(len(datalist), offset + step) - 1: if offset + step >= len(datalist): continue self.offset += step # 移动光标到第一列 self.index = offset + step else: self.index = carousel(offset, min(len(datalist), offset + step) - 1, idx + 1) self.START = time.time() # 数字快捷键 elif ord("0") <= key <= ord("9"): if self.datatype == "songs" or self.datatype == "djchannels" or self.datatype == "help": continue idx = key - ord("0") self.ui.build_menu(self.datatype, self.title, self.datalist, self.offset, idx, self.step, self.START) self.ui.build_loading() self.dispatch_enter(idx) self.index = 0 self.offset = 0 # 向上翻页 elif key == ord("u"): if offset == 0: continue self.START = time.time() self.offset -= step # e.g. 23 - 10 = 13 --> 10 self.index = (index - step) // step * step # 向下翻页 elif key == ord("d"): if offset + step >= len(datalist): continue self.START = time.time() self.offset += step # e.g. 23 + 10 = 33 --> 30 self.index = (index + step) // step * step # 前进 elif key == ord("l") or key == 10: if ( self.datatype == "songs" or self.datatype == "djchannels" or self.datatype == "help" or len(self.datalist) <= 0 ): continue self.START = time.time() self.ui.build_loading() self.dispatch_enter(idx) self.index = 0 self.offset = 0 # 回退 elif key == ord("h"): # if not main menu if len(self.stack) == 1: continue self.START = time.time() up = stack.pop() self.datatype = up[0] self.title = up[1] self.datalist = up[2] self.offset = up[3] self.index = up[4] self.at_playing_list = False # 搜索 elif key == ord("f"): # 8 is the 'search' menu self.dispatch_enter(8) # 播放下一曲 elif key == ord("]"): self.next_song() # 播放上一曲 elif key == ord("["): self.previous_song() # 增加音量 elif key == ord("="): self.player.volume_up() # 减少音量 elif key == ord("-"): self.player.volume_down() # 随机播放 elif key == ord("?"): if len(self.storage.database["player_info"]["player_list"]) == 0: continue self.player.shuffle() time.sleep(0.1) # 喜爱 elif key == ord(","): return_data = self.request_api(self.netease.fm_like, self.player.get_playing_id()) if return_data != -1: if platform.system() == "Darwin": os.system("/usr/bin/osascript -e 'display notification \"Added successfully\"'") else: os.system('/usr/bin/notify-send "Added successfully"') # 删除FM elif key == ord("."): if self.datatype == "fmsongs": if len(self.storage.database["player_info"]["player_list"]) == 0: continue self.player.next() return_data = self.request_api(self.netease.fm_trash, self.player.get_playing_id()) if return_data != -1: if platform.system() == "Darwin": os.system("/usr/bin/osascript -e 'display notification \"Deleted successfully\"'") else: os.system('/usr/bin/notify-send "Deleted successfully"') time.sleep(0.1) # 下一FM elif key == ord("/"): if self.datatype == "fmsongs": if len(self.storage.database["player_info"]["player_list"]) == 0: continue self.player.next() time.sleep(0.1) # 播放、暂停 elif key == ord(" "): # If not open a new playing list, just play and pause. try: if self.datalist[idx]["song_id"] == self.player.playing_id: self.player.play_and_pause(self.storage.database["player_info"]["idx"]) time.sleep(0.1) continue except: pass # If change to a new playing list. Add playing list and play. if datatype == "songs": self.resume_play = False self.player.new_player_list("songs", self.title, self.datalist, -1) self.player.end_callback = None self.player.play_and_pause(idx) self.at_playing_list = True elif datatype == "djchannels": self.resume_play = False self.player.new_player_list("djchannels", self.title, self.datalist, -1) self.player.end_callback = None self.player.play_and_pause(idx) self.at_playing_list = True elif datatype == "fmsongs": self.resume_play = False self.storage.database["player_info"]["playing_mode"] = 0 self.player.new_player_list("fmsongs", self.title, self.datalist, -1) self.player.end_callback = self.fm_callback self.player.play_and_pause(idx) self.at_playing_list = True else: self.player.play_and_pause(self.storage.database["player_info"]["idx"]) time.sleep(0.1) # 加载当前播放列表 elif key == ord("p"): self.show_playing_song() # 播放模式切换 elif key == ord("P"): self.storage.database["player_info"]["playing_mode"] = ( self.storage.database["player_info"]["playing_mode"] + 1 ) % 5 # 添加到打碟歌单 elif key == ord("a"): if datatype == "songs" and len(datalist) != 0: self.djstack.append(datalist[idx]) elif datatype == "artists": pass # 加载打碟歌单 elif key == ord("z"): self.stack.append([datatype, title, datalist, offset, index]) self.datatype = "songs" self.title = "网易云音乐 > 打碟" self.datalist = self.djstack self.offset = 0 self.index = 0 # 添加到收藏歌曲 elif key == ord("s"): if (datatype == "songs" or datatype == "djchannels") and len(datalist) != 0: self.collection.append(datalist[idx]) if platform.system() == "Darwin": os.system("/usr/bin/osascript -e 'display notification \"Added successfully\"'") else: os.system('/usr/bin/notify-send "Added successfully"') # 加载收藏歌曲 elif key == ord("c"): self.stack.append([datatype, title, datalist, offset, index]) self.datatype = "songs" self.title = "网易云音乐 > 收藏" self.datalist = self.collection self.offset = 0 self.index = 0 # 从当前列表移除 elif key == ord("r"): if (datatype == "songs" or datatype == "djchannels") and len(datalist) != 0: self.datalist.pop(idx) self.index = carousel(offset, min(len(datalist), offset + step) - 1, idx) # 当前项目下移 elif key == ord("J"): if datatype != "main" and len(datalist) != 0 and idx + 1 != len(self.datalist): self.START = time.time() song = self.datalist.pop(idx) self.datalist.insert(idx + 1, song) self.index = idx + 1 # 翻页 if self.index >= offset + step: self.offset = offset + step # 当前项目上移 elif key == ord("K"): if datatype != "main" and len(datalist) != 0 and idx != 0: self.START = time.time() song = self.datalist.pop(idx) self.datalist.insert(idx - 1, song) self.index = idx - 1 # 翻页 if self.index < offset: self.offset = offset - step elif key == ord("m"): if datatype != "main": self.stack.append([datatype, title, datalist, offset, index]) self.datatype = self.stack[0][0] self.title = self.stack[0][1] self.datalist = self.stack[0][2] self.offset = 0 self.index = 0 elif key == ord("g"): if datatype == "help": webbrowser.open_new_tab("https://github.com/darknessomi/musicbox") # 开始下载 elif key == ord("C"): s = self.datalist[idx] cache_thread = threading.Thread( target=self.player.cacheSong1time, args=(s["song_id"], s["song_name"], s["artist"], s["mp3_url"]) ) cache_thread.start() elif key == ord("i"): if self.player.playing_id != -1: webbrowser.open_new_tab("http://music.163.com/#/song?id=" + str(self.player.playing_id)) self.ui.build_process_bar( self.player.process_location, self.player.process_length, self.player.playing_flag, self.player.pause_flag, self.storage.database["player_info"]["playing_mode"], ) self.ui.build_menu(self.datatype, self.title, self.datalist, self.offset, self.index, self.step, self.START) self.player.stop() self.cache.quit() self.storage.save() curses.endwin() def dispatch_enter(self, idx): # The end of stack netease = self.netease datatype = self.datatype title = self.title datalist = self.datalist offset = self.offset index = self.index self.stack.append([datatype, title, datalist, offset, index]) if datatype == "main": self.choice_channel(idx) # 该艺术家的热门歌曲 elif datatype == "artists": artist_id = datalist[idx]["artist_id"] songs = netease.artists(artist_id) self.datatype = "songs" self.datalist = netease.dig_info(songs, "songs") self.title += " > " + datalist[idx]["artists_name"] # 该专辑包含的歌曲 elif datatype == "albums": album_id = datalist[idx]["album_id"] songs = netease.album(album_id) self.datatype = "songs" self.datalist = netease.dig_info(songs, "songs") self.title += " > " + datalist[idx]["albums_name"] # 精选歌单选项 elif datatype == "playlists": data = self.datalist[idx] self.datatype = data["datatype"] self.datalist = netease.dig_info(data["callback"](), self.datatype) self.title += " > " + data["title"] # 全站置顶歌单包含的歌曲 elif datatype == "top_playlists": log.debug(datalist) playlist_id = datalist[idx]["playlist_id"] songs = netease.playlist_detail(playlist_id) self.datatype = "songs" self.datalist = netease.dig_info(songs, "songs") self.title += " > " + datalist[idx]["playlists_name"] # 分类精选 elif datatype == "playlist_classes": # 分类名称 data = self.datalist[idx] self.datatype = "playlist_class_detail" self.datalist = netease.dig_info(data, self.datatype) self.title += " > " + data log.debug(self.datalist) # 某一分类的详情 elif datatype == "playlist_class_detail": # 子类别 data = self.datalist[idx] self.datatype = "top_playlists" self.datalist = netease.dig_info(netease.top_playlists(data), self.datatype) log.debug(self.datalist) self.title += " > " + data # 歌曲榜单 elif datatype == "toplists": songs = netease.top_songlist(idx) self.title += " > " + self.datalist[idx] self.datalist = netease.dig_info(songs, "songs") self.datatype = "songs" # 搜索菜单 elif datatype == "search": ui = self.ui # no need to do stack.append, Otherwise there will be a bug when you input key 'h' to return # if idx in range(1, 5): # self.stack.append([self.datatype, self.title, self.datalist, self.offset, self.index]) self.index = 0 self.offset = 0 if idx == 0: # 搜索结果可以用top_playlists处理 self.datatype = "top_playlists" self.datalist = ui.build_search("search_playlist") self.title = "精选歌单搜索列表" elif idx == 1: self.datatype = "songs" self.datalist = ui.build_search("songs") self.title = "歌曲搜索列表" elif idx == 2: self.datatype = "artists" self.datalist = ui.build_search("artists") self.title = "艺术家搜索列表" elif idx == 3: self.datatype = "albums" self.datalist = ui.build_search("albums") self.title = "专辑搜索列表" def show_playing_song(self): if len(self.storage.database["player_info"]["player_list"]) == 0: return if not self.at_playing_list: self.stack.append([self.datatype, self.title, self.datalist, self.offset, self.index]) self.at_playing_list = True self.datatype = self.storage.database["player_info"]["player_list_type"] self.title = self.storage.database["player_info"]["player_list_title"] self.datalist = [] for i in self.storage.database["player_info"]["player_list"]: self.datalist.append(self.storage.database["songs"][i]) self.index = self.storage.database["player_info"]["idx"] self.offset = self.storage.database["player_info"]["idx"] / self.step * self.step if self.resume_play: if self.datatype == "fmsongs": self.player.end_callback = self.fm_callback else: self.player.end_callback = None self.storage.database["player_info"]["idx"] = -1 self.player.play_and_pause(self.index) self.resume_play = False def song_changed_callback(self): if self.at_playing_list: self.show_playing_song() def fm_callback(self): log.debug("FM CallBack.") data = self.get_new_fm() self.player.append_songs(data) if self.datatype == "fmsongs": if len(self.storage.database["player_info"]["player_list"]) == 0: return self.datatype = self.storage.database["player_info"]["player_list_type"] self.title = self.storage.database["player_info"]["player_list_title"] self.datalist = [] for i in self.storage.database["player_info"]["player_list"]: self.datalist.append(self.storage.database["songs"][i]) self.index = self.storage.database["player_info"]["idx"] self.offset = self.storage.database["player_info"]["idx"] / self.step * self.step def request_api(self, func, *args): if self.storage.database["user"]["user_id"] != "": result = func(*args) if result != -1: return result log.debug("Re Login.") user_info = {} if self.storage.database["user"]["username"] != "": user_info = self.netease.login( self.storage.database["user"]["username"], self.storage.database["user"]["password"] ) if self.storage.database["user"]["username"] == "" or user_info["code"] != 200: data = self.ui.build_login() # 取消登录 if data == -1: return -1 user_info = data[0] self.storage.database["user"]["username"] = data[1][0] self.storage.database["user"]["password"] = data[1][1] self.storage.database["user"]["user_id"] = user_info["account"]["id"] self.storage.database["user"]["nickname"] = user_info["profile"]["nickname"] self.userid = self.storage.database["user"]["user_id"] self.username = self.storage.database["user"]["nickname"] return func(*args) def get_new_fm(self): myplaylist = [] for count in range(0, 1): data = self.request_api(self.netease.personal_fm) if data == -1: break myplaylist += data time.sleep(0.2) return self.netease.dig_info(myplaylist, "fmsongs") def choice_channel(self, idx): # 排行榜 netease = self.netease if idx == 0: self.datalist = netease.return_toplists() self.title += " > 排行榜" self.datatype = "toplists" # 艺术家 elif idx == 1: artists = netease.top_artists() self.datalist = netease.dig_info(artists, "artists") self.title += " > 艺术家" self.datatype = "artists" # 新碟上架 elif idx == 2: albums = netease.new_albums() self.datalist = netease.dig_info(albums, "albums") self.title += " > 新碟上架" self.datatype = "albums" # 精选歌单 elif idx == 3: self.datalist = [ {"title": "全站置顶", "datatype": "top_playlists", "callback": netease.top_playlists}, {"title": "分类精选", "datatype": "playlist_classes", "callback": netease.playlist_classes}, ] self.title += " > 精选歌单" self.datatype = "playlists" # 我的歌单 elif idx == 4: myplaylist = self.request_api(self.netease.user_playlist, self.userid) if myplaylist == -1: return self.datatype = "top_playlists" self.datalist = netease.dig_info(myplaylist, self.datatype) self.title += " > " + self.username + " 的歌单" # DJ节目 elif idx == 5: self.datatype = "djchannels" self.title += " > DJ节目" self.datalist = netease.djchannels() # 每日推荐 elif idx == 6: self.datatype = "songs" self.title += " > 每日推荐" myplaylist = self.request_api(self.netease.recommend_playlist) if myplaylist == -1: return self.datalist = self.netease.dig_info(myplaylist, self.datatype) # 私人FM elif idx == 7: self.datatype = "fmsongs" self.title += " > 私人FM" self.datalist = self.get_new_fm() # 搜索 elif idx == 8: self.datatype = "search" self.title += " > 搜索" self.datalist = ["歌曲", "艺术家", "专辑", "网易精选集"] # 帮助 elif idx == 9: self.datatype = "help" self.title += " > 帮助" self.datalist = shortcut self.offset = 0 self.index = 0
class Ui: def __init__(self): self.screen = curses.initscr() self.screen.timeout(100) # the screen refresh every 100ms # charactor break buffer curses.cbreak() self.screen.keypad(1) self.netease = NetEase() curses.start_color() curses.init_pair(1, curses.COLOR_GREEN, curses.COLOR_BLACK) curses.init_pair(2, curses.COLOR_CYAN, curses.COLOR_BLACK) curses.init_pair(3, curses.COLOR_RED, curses.COLOR_BLACK) curses.init_pair(4, curses.COLOR_YELLOW, curses.COLOR_BLACK) # term resize handling size = terminalsize.get_terminal_size() self.x = max(size[0], 10) self.y = max(size[1], 25) self.startcol = int(float(self.x) / 5) self.indented_startcol = max(self.startcol - 3, 0) self.update_space() self.lyric = '' self.now_lyric = '' self.tlyric = '' self.storage = Storage() self.config = Config() self.newversion = False def notify(self, summary, song, album, artist): if summary != 'disable': body = '%s\nin %s by %s' % (song, album, artist) content = escape_quote(summary + ': ' + body) notify(content) def build_playinfo(self, song_name, artist, album_name, quality, start, pause=False): curses.noecho() # refresh top 2 line self.screen.move(1, 1) self.screen.clrtoeol() self.screen.move(2, 1) self.screen.clrtoeol() if pause: self.screen.addstr(1, self.indented_startcol, '_ _ z Z Z ' + quality, curses.color_pair(3)) else: self.screen.addstr(1, self.indented_startcol, '♫ ♪ ♫ ♪ ' + quality, curses.color_pair(3)) self.screen.addstr( 1, min(self.indented_startcol + 18, self.x - 1), song_name + self.space + artist + ' < ' + album_name + ' >', curses.color_pair(4)) self.screen.refresh() def build_process_bar(self, now_playing, total_length, playing_flag, pause_flag, playing_mode): if (self.storage.database['player_info']['idx'] >= len( self.storage.database['player_info']['player_list'])): return curses.noecho() self.screen.move(3, 1) self.screen.clrtoeol() self.screen.move(4, 1) self.screen.clrtoeol() if not playing_flag: return if total_length <= 0: total_length = 1 if now_playing > total_length or now_playing <= 0: now_playing = 0 process = '[' for i in range(0, 33): if i < now_playing / total_length * 33: if (i + 1) > now_playing / total_length * 33: if not pause_flag: process += '>' continue process += '=' else: process += ' ' process += '] ' now_minute = int(now_playing / 60) if now_minute > 9: now_minute = str(now_minute) else: now_minute = '0' + str(now_minute) now_second = int(now_playing - int(now_playing / 60) * 60) if now_second > 9: now_second = str(now_second) else: now_second = '0' + str(now_second) total_minute = int(total_length / 60) if total_minute > 9: total_minute = str(total_minute) else: total_minute = '0' + str(total_minute) total_second = int(total_length - int(total_length / 60) * 60) if total_second > 9: total_second = str(total_second) else: total_second = '0' + str(total_second) process += '(' + now_minute + ':' + now_second + '/' + total_minute + ':' + total_second + ')' # NOQA if playing_mode == 0: process = '顺序播放 ' + process elif playing_mode == 1: process = '顺序循环 ' + process elif playing_mode == 2: process = '单曲循环 ' + process elif playing_mode == 3: process = '随机播放 ' + process elif playing_mode == 4: process = '随机循环 ' + process else: pass self.screen.addstr(3, self.startcol - 2, process, curses.color_pair(1)) song = self.storage.database['songs'][ self.storage.database['player_info']['player_list'][ self.storage.database['player_info']['idx']]] if 'lyric' not in song.keys() or len(song['lyric']) <= 0: self.now_lyric = '暂无歌词 ~>_<~ \n' if dbus_activity and self.config.get_item('osdlyrics'): self.now_playing = song['song_name'] + ' - ' + song[ 'artist'] + '\n' else: key = now_minute + ':' + now_second for line in song['lyric']: if key in line: if 'tlyric' not in song.keys() or len(song['tlyric']) <= 0: self.now_lyric = line else: self.now_lyric = line for tline in song['tlyric']: if key in tline and self.config.get_item( 'translation'): self.now_lyric = tline + ' || ' + self.now_lyric # NOQA self.now_lyric = re.sub('\[.*?\]', '', self.now_lyric) if dbus_activity and self.config.get_item('osdlyrics'): try: bus = dbus.SessionBus().get_object('org.musicbox.Bus', '/') if self.now_lyric == '暂无歌词 ~>_<~ \n': bus.refresh_lyrics(self.now_playing, dbus_interface='local.musicbox.Lyrics') else: bus.refresh_lyrics(self.now_lyric, dbus_interface='local.musicbox.Lyrics') except Exception as e: log.error(e) pass self.screen.addstr(4, self.startcol - 2, str(self.now_lyric), curses.color_pair(3)) self.screen.refresh() def build_loading(self): self.screen.addstr(7, self.startcol, '享受高品质音乐,loading...', curses.color_pair(1)) self.screen.refresh() # start is the timestamp of this function being called def build_menu(self, datatype, title, datalist, offset, index, step, start): # keep playing info in line 1 curses.noecho() self.screen.move(5, 1) self.screen.clrtobot() self.screen.addstr(5, self.startcol, title, curses.color_pair(1)) if len(datalist) == 0: self.screen.addstr(8, self.startcol, '这里什么都没有 -,-') else: if datatype == 'main': for i in range(offset, min(len(datalist), offset + step)): if i == index: self.screen.addstr(i - offset + 9, self.indented_startcol, '-> ' + str(i) + '. ' + datalist[i], curses.color_pair(2)) else: self.screen.addstr(i - offset + 9, self.startcol, str(i) + '. ' + datalist[i]) elif datatype == 'songs' or datatype == 'fmsongs': iter_range = min(len(datalist), offset + step) for i in range(offset, iter_range): # this item is focus if i == index: self.screen.addstr(i - offset + 8, 0, ' ' * self.startcol) lead = '-> ' + str(i) + '. ' self.screen.addstr(i - offset + 8, self.indented_startcol, lead, curses.color_pair(2)) name = '{}{}{} < {} >'.format( datalist[i]['song_name'], self.space, datalist[i]['artist'], datalist[i]['album_name']) # the length decides whether to scoll if truelen(name) < self.x - self.startcol - 1: self.screen.addstr( i - offset + 8, self.indented_startcol + len(lead), name, curses.color_pair(2)) else: name = scrollstring(name + ' ', start) self.screen.addstr( i - offset + 8, self.indented_startcol + len(lead), str(name), curses.color_pair(2)) else: self.screen.addstr(i - offset + 8, 0, ' ' * self.startcol) self.screen.addstr( i - offset + 8, self.startcol, str( str(i) + '. ' + datalist[i]['song_name'] + self.space + datalist[i]['artist'] + ' < ' + datalist[i]['album_name'] + ' >')[:int(self.x * 2)]) self.screen.addstr(iter_range - offset + 9, 0, ' ' * self.x) elif datatype == 'artists': for i in range(offset, min(len(datalist), offset + step)): if i == index: self.screen.addstr( i - offset + 9, self.indented_startcol, '-> ' + str(i) + '. ' + datalist[i]['artists_name'] + self.space + str(datalist[i]['alias']), curses.color_pair(2)) else: self.screen.addstr( i - offset + 9, self.startcol, str(i) + '. ' + datalist[i]['artists_name'] + self.space + datalist[i]['alias']) elif datatype == 'albums': for i in range(offset, min(len(datalist), offset + step)): if i == index: self.screen.addstr( i - offset + 9, self.indented_startcol, '-> ' + str(i) + '. ' + datalist[i]['albums_name'] + self.space + datalist[i]['artists_name'], curses.color_pair(2)) else: self.screen.addstr( i - offset + 9, self.startcol, str(i) + '. ' + datalist[i]['albums_name'] + self.space + datalist[i]['artists_name']) elif datatype == 'playlists': for i in range(offset, min(len(datalist), offset + step)): if i == index: self.screen.addstr( i - offset + 9, self.indented_startcol, '-> ' + str(i) + '. ' + datalist[i]['title'], curses.color_pair(2)) else: self.screen.addstr( i - offset + 9, self.startcol, str(i) + '. ' + datalist[i]['title']) elif datatype == 'top_playlists': for i in range(offset, min(len(datalist), offset + step)): if i == index: self.screen.addstr( i - offset + 9, self.indented_startcol, '-> ' + str(i) + '. ' + datalist[i]['playlists_name'] + self.space + datalist[i]['creator_name'], curses.color_pair(2)) else: self.screen.addstr( i - offset + 9, self.startcol, str(i) + '. ' + datalist[i]['playlists_name'] + self.space + datalist[i]['creator_name']) elif datatype == 'toplists': for i in range(offset, min(len(datalist), offset + step)): if i == index: self.screen.addstr(i - offset + 9, self.indented_startcol, '-> ' + str(i) + '. ' + datalist[i], curses.color_pair(2)) else: self.screen.addstr(i - offset + 9, self.startcol, str(i) + '. ' + datalist[i]) elif datatype in ('playlist_classes', 'playlist_class_detail'): for i in range(offset, min(len(datalist), offset + step)): if i == index: self.screen.addstr(i - offset + 9, self.indented_startcol, '-> ' + str(i) + '. ' + datalist[i], curses.color_pair(2)) else: self.screen.addstr(i - offset + 9, self.startcol, str(i) + '. ' + datalist[i]) elif datatype == 'djchannels': for i in range(offset, min(len(datalist), offset + step)): if i == index: self.screen.addstr( i - offset + 8, self.indented_startcol, '-> ' + str(i) + '. ' + datalist[i]['song_name'], curses.color_pair(2)) else: self.screen.addstr( i - offset + 8, self.startcol, str(i) + '. ' + datalist[i]['song_name']) elif datatype == 'search': self.screen.move(6, 1) self.screen.clrtobot() self.screen.timeout(-1) self.screen.addstr(8, self.startcol, '选择搜索类型:', curses.color_pair(1)) for i in range(offset, min(len(datalist), offset + step)): if i == index: self.screen.addstr( i - offset + 10, self.indented_startcol, '-> ' + str(i) + '.' + datalist[i - 1], curses.color_pair(2)) else: self.screen.addstr(i - offset + 10, self.startcol, str(i) + '.' + datalist[i - 1]) self.screen.timeout(100) elif datatype == 'help': for i in range(offset, min(len(datalist), offset + step)): if i == index: self.screen.addstr( i - offset + 9, self.indented_startcol, '-> ' + str(i) + '. \'' + (datalist[i][0].upper() + '\'').ljust(11) + datalist[i][1] + ' ' + datalist[i][2], curses.color_pair(2)) else: self.screen.addstr( i - offset + 9, self.startcol, str(i) + '. \'' + (datalist[i][0].upper() + '\'').ljust(11) + datalist[i][1] + ' ' + datalist[i][2]) self.screen.addstr( 20, 6, 'NetEase-MusicBox 基于Python,所有版权音乐来源于网易,本地不做任何保存') self.screen.addstr(21, 10, '按 [G] 到 Github 了解更多信息,帮助改进,或者Star表示支持~~') self.screen.addstr(22, self.startcol, 'Build with love to music by omi') self.screen.refresh() def build_search(self, stype): self.screen.timeout(-1) netease = self.netease if stype == 'songs': song_name = self.get_param('搜索歌曲:') if song_name == '/return': return [] else: try: data = netease.search(song_name, stype=1) song_ids = [] if 'songs' in data['result']: if 'mp3Url' in data['result']['songs']: songs = data['result']['songs'] # if search song result do not has mp3Url # send ids to get mp3Url else: for i in range(0, len(data['result']['songs'])): song_ids.append( data['result']['songs'][i]['id']) songs = netease.songs_detail(song_ids) return netease.dig_info(songs, 'songs') except Exception as e: log.error(e) return [] elif stype == 'artists': artist_name = self.get_param('搜索艺术家:') if artist_name == '/return': return [] else: try: data = netease.search(artist_name, stype=100) if 'artists' in data['result']: artists = data['result']['artists'] return netease.dig_info(artists, 'artists') except Exception as e: log.error(e) return [] elif stype == 'albums': albums_name = self.get_param('搜索专辑:') if albums_name == '/return': return [] else: try: data = netease.search(albums_name, stype=10) if 'albums' in data['result']: albums = data['result']['albums'] return netease.dig_info(albums, 'albums') except Exception as e: log.error(e) return [] elif stype == 'search_playlist': search_playlist = self.get_param('搜索网易精选集:') if search_playlist == '/return': return [] else: try: data = netease.search(search_playlist, stype=1000) if 'playlists' in data['result']: playlists = data['result']['playlists'] return netease.dig_info(playlists, 'top_playlists') except Exception as e: log.error(e) return [] return [] def build_login(self): self.build_login_bar() local_account = self.get_account() local_password = hashlib.md5(self.get_password()).hexdigest() login_info = self.netease.login(local_account, local_password) account = [local_account, local_password] if login_info['code'] != 200: x = self.build_login_error() if x == ord('1'): return self.build_login() else: return -1 else: return [login_info, account] def build_login_bar(self): curses.noecho() self.screen.move(4, 1) self.screen.clrtobot() self.screen.addstr(5, self.startcol, '请输入登录信息(支持手机登陆)', curses.color_pair(1)) self.screen.addstr(8, self.startcol, '账号:', curses.color_pair(1)) self.screen.addstr(9, self.startcol, '密码:', curses.color_pair(1)) self.screen.move(8, 24) self.screen.refresh() def build_login_error(self): self.screen.move(4, 1) self.screen.timeout(-1) # disable the screen timeout self.screen.clrtobot() self.screen.addstr(8, self.startcol, '艾玛,登录信息好像不对呢 (O_O)#', curses.color_pair(1)) self.screen.addstr(10, self.startcol, '[1] 再试一次') self.screen.addstr(11, self.startcol, '[2] 稍后再试') self.screen.addstr(14, self.startcol, '请键入对应数字:', curses.color_pair(2)) self.screen.refresh() x = self.screen.getch() self.screen.timeout(100) # restore the screen timeout return x def get_account(self): self.screen.timeout(-1) # disable the screen timeout curses.echo() account = self.screen.getstr(8, self.startcol + 6, 60) self.screen.timeout(100) # restore the screen timeout return account def get_password(self): self.screen.timeout(-1) # disable the screen timeout curses.noecho() password = self.screen.getstr(9, self.startcol + 6, 60) self.screen.timeout(100) # restore the screen timeout return password def get_param(self, prompt_string): # keep playing info in line 1 curses.echo() self.screen.move(4, 1) self.screen.clrtobot() self.screen.addstr(5, self.startcol, prompt_string, curses.color_pair(1)) self.screen.refresh() info = self.screen.getstr(10, self.startcol, 60) if info == '': return '/return' elif info.strip() is '': return self.get_param(prompt_string) else: return info def update_size(self): # get terminal size size = terminalsize.get_terminal_size() self.x = max(size[0], 10) self.y = max(size[1], 25) # update intendations curses.resizeterm(self.y, self.x) self.startcol = int(float(self.x) / 5) self.indented_startcol = max(self.startcol - 3, 0) self.update_space() self.screen.clear() self.screen.refresh() def update_space(self): if self.x > 140: self.space = ' - ' elif self.x > 80: self.space = ' - ' else: self.space = ' - ' self.screen.refresh()
class LoginDialog(QDialog): """登录对话框 描述: 弹出登录对话框,用户输入用户名和密码,点击登录按钮调用login函数。 登录成功则发射("loginsuccess")信号,失败则显示相关提示信息 调用: 1. 在用户登录成功时,会发射("loginsuccess")信号 """ def __init__(self, parent=None): super(LoginDialog, self).__init__(parent) self.username_lable = QLabel() self.password_lable = QLabel() self.hint_label = QLabel() self.username_widget = QLineEdit() self.password_widget = QLineEdit() self.login_btn = QPushButton() self.layout = QVBoxLayout() self.ne = NetEase() self.__set_signal_binding() self.__set_widgets_prop() self.__set_layouts_prop() self.__set_me() def __set_signal_binding(self): self.login_btn.clicked.connect(self.__login) def __login(self): """登录 在用户登录成功时,会发射("loginsuccess")信号 """ phone_login = False # 0: 网易通行证, 1: 手机号登陆 username = str(self.username_widget.text()) # 包含中文会出错 password = str(self.password_widget.text()) # judget if logining by using phone number try: int(username) phone_login = True except ValueError: pass data = self.ne.login(username, password, phone_login) # judge if __login successfully # if not, why print data['code'], type(data['code']) if data['code'] == 200: self.hint_label.setText(u'登陆成功') self.emit(SIGNAL('loginsuccess'), data) self.close() elif data['code'] == 408: self.hint_label.setText(u'网络连接超时') elif data['code'] == 501: self.hint_label.setText(u'用户名错误') elif data['code'] == 502: self.hint_label.setText(u'密码错误') else: self.hint_label.setText(u'未知错误') def __set_me(self): self.setObjectName('login_dialog') self.setLayout(self.layout) def __set_widgets_prop(self): self.login_btn.setText(u'登陆') self.username_lable.setText(u'网易邮箱或者手机号') self.password_lable.setText(u'密码') self.username_widget.setPlaceholderText(u'请输入用户名') self.password_widget.setPlaceholderText(u'请输入密码') self.password_widget.setEchoMode(QLineEdit.Password) def __set_layouts_prop(self): self.layout.addWidget(self.username_lable) self.layout.addWidget(self.username_widget) self.layout.addWidget(self.password_lable) self.layout.addWidget(self.password_widget) self.layout.addWidget(self.hint_label) self.layout.addWidget(self.login_btn) self.layout.addStretch(1)