def _load_more(self): ''' load more when scroll bar is scrolled to the bottom only when searching music and in qq music ''' if self.display_mode == 'local' or self.parent.header.music_source != 'qq': return # scroll bar at end if self.music_list.verticalScrollBar().value( ) == self.load_more_threshold: keywords = self.parent.header.search_line.text() api = QQMusicApi() search_result = api.search(self.load_page, keywords) for item in search_result: qt_item = QTreeWidgetItem() qt_item.setText(0, item['song_name']) singers = '' for singer in item['singer_list']: singers += singer singers += '/' singers = singers.strip('/') qt_item.setText(1, singers) qt_item.setText(2, item['album_name']) qt_item.setText(3, convert_interval(item['interval'])) qt_item.setText(4, str(item['song_mid'])) qt_item.setText(5, item['image_url']) self.parent.main_list.music_list.addTopLevelItem(qt_item) self.load_page += 1 self.load_more_threshold += 10
def _show_search(self): ''' show first 10 search result from `list` self.search_result ''' print('{}{}{}{}{}'.format( self._rpad('', 4), self._rpad('歌曲', 30), self._rpad('歌手', 20), self._rpad('专辑', 30), self._rpad('时长', 10))) print('-'*100) index = 1 for result in self.search_result['content']: if self.search_result['source'] != 'migu': singers = convert_singerlist(result['singer_list']) else: singers = result['singer_list'] print('{}{}{}{}{}'.format( self._rpad(str(index)+'.', 4), self._rpad(result['song_name'], 30), self._rpad(singers, 20), self._rpad(result['album_name'], 30), self._rpad(convert_interval(result['interval']), 10))) index += 1 if index == 11: # show only ten results break print('-'*100)
def __slider_moved(self): ''' moving slider ''' ratio = self.slider.value() / 1000 music_time = (self.player.duration() // 1000) * ratio # ms -> s music_time_str = convert_interval(int(music_time)) total_time = self.time.text().split('/')[-1] self.time.setText(music_time_str + '/' + total_time)
def _player_pos_change(self): ''' when player position is changing, change slider and time info ''' # current music total time (ms) if self.player.duration() == 0: return current_time = self.player.position() // 1000 # ms -> s api = QQMusicApi() total_time = self.time.text().split('/')[-1] # set time_info and slider self.time.setText(convert_interval(current_time) + '/' + total_time) self.slider.setValue(current_time / (self.player.duration() // 1000) * 1000) if self.player.duration() == self.player.position(): self.music_end_signal.emit()
def do_i(self, arg): ''' print current playing song's info ''' if self.current_song == None: print('No song is playing.') return # print(self.current_song) try: # print(self.player._get_property('time-pos')) if self.current_song['source'] == 'migu': total_time = '' else: total_time = '/' + self.current_song['interval'] print('Playing {} - {} {}{}'.format( self.current_song['song_name'], self.current_song['singers'], convert_interval(int(self.player._get_property('time-pos'))), total_time)) except: print('No song is playing.')
def local_music(self): ''' display local music in main_list ''' self.navigation_list.setCurrentItem(None) self.play_list.setCurrentItem(None) self.parent.main_list.music_list.clear() self.parent.main_list.display_mode = 'local' for file_path in os.listdir('userdata/music'): if '.m4a' not in file_path and '.mp3' not in file_path and '.flac' not in file_path: continue path = os.path.join('userdata/music', file_path) media_info = json.loads( MediaInfo.parse(path).to_json())['tracks'][0] # song_name = media_info['title'] if 'title' in media_info else file_path.split('_')[0] song_name = file_path.split('_')[0] singers = media_info[ 'performer'] if 'performer' in media_info else '' album_name = media_info['album'] if 'album' in media_info else '' api = QQMusicApi() duration = convert_interval( math.floor(media_info['duration'] / 1000)) if 'duration' in media_info else '' song_mid = file_path.split('.')[0].split('_')[1] qt_item = QTreeWidgetItem() qt_item.setText(0, song_name) qt_item.setText(1, singers) qt_item.setText(2, album_name) qt_item.setText(3, duration) qt_item.setText(4, song_mid) self.parent.main_list.music_list.addTopLevelItem(qt_item)
def next_song(self, direction=None): ''' when current music play end, continue to next song ''' if self.current_music == None: return index = self.play_list.index(self.current_music) # next song mid if direction == 'next' or direction is None: if index == len(self.play_list) - 1: next_song_dict = self.play_list[0] else: next_song_dict = self.play_list[index + 1] elif direction == 'prev': if index == 0: next_song_dict = self.play_list[len(self.play_list) - 1] else: next_song_dict = self.play_list[index - 1] else: raise valueError('direction error.') self.pause() self.slider.setValue(0) # if local exists for music_file in os.listdir('userdata/music'): if next_song_dict['song_mid'] in music_file: next_path = os.path.join('userdata/music', music_file) self.player.setMedia( QMediaContent(QUrl.fromLocalFile(next_path))) self.play() # get true interval media_info = json.loads( MediaInfo.parse(next_path).to_json())['tracks'][0] interval = convert_interval( math.floor(media_info['duration'] / 1000)) # set song name, singer, interval display if next_song_dict['singers'] != '': self.song_info.setText(next_song_dict['song_name'] + '-' + next_song_dict['singers']) else: self.song_info.setText(next_song_dict['song_name']) self.time.setText(self.time.text().split('/')[0] + '/' + interval) self.current_music = next_song_dict iterator = QTreeWidgetItemIterator( self.parent.main_list.music_list) while iterator.value(): item = iterator.value() if item.text(4) == next_song_dict['song_mid']: self.parent.main_list.music_list.setCurrentItem(item) break iterator.__iadd__(1) return # local not exists, download according to song mid if next_song_dict['source'] == 'migu': url = next_song_dict['url'] elif next_song_dict['source'] == 'qq': url = QQMusicApi().get_url(next_song_dict['song_mid']) elif next_song_didct['source'] == 'netease': url = NeteaseCloudMusicAPI().get_url(next_song_dict['song_mid']) next_path = 'userdata/music/{}_{}.m4a'.format( next_song_dict['song_name'], next_song_dict['song_mid']) urllib.request.urlretrieve(url, next_path) self.player.setMedia(QMediaContent(QUrl.fromLocalFile(next_path))) self.play() # get true interval media_info = json.loads( MediaInfo.parse(next_path).to_json())['tracks'][0] interval = convert_interval(math.floor(media_info['duration'] / 1000)) # set song name, singer, interval display if next_song_dict['singers'] != '': self.song_info.setText(next_song_dict['song_name'] + '-' + next_song_dict['singers']) else: self.song_info.setText(next_song_dict['song_name']) self.time.setText(self.time.text().split('/')[0] + '/' + interval) self.current_music = next_song_dict iterator = QTreeWidgetItemIterator(self.parent.main_list.music_list) while iterator.value(): item = iterator.value() if item.text(4) == next_song_dict['song_mid']: self.parent.main_list.music_list.setCurrentItem(item) break iterator.__iadd__(1)
def play_from_main_list(self, qtree_item, music_path, mode): ''' Get signal from main list and play music qtree_item - `QTreeWidgetItem` music_path - local path mode - 'global', 'local', 'list' ''' self.play_mode = mode # button self.slider.setValue(0) self.play_button.hide() self.pause_button.show() # play list clear self.play_list.clear() # set media for QMediaPlayer q_path = QUrl.fromLocalFile(music_path) self.player.setMedia(QMediaContent(q_path)) media_info = json.loads(MediaInfo.parse( music_path).to_json())['tracks'][0] # to get song interval item_dict = { 'song_name': qtree_item.text(0), 'singers': qtree_item.text(1), 'album_name': qtree_item.text(2), 'interval': '', 'song_mid': qtree_item.text(4), 'url': qtree_item.text(5), 'source': qtree_item.text(6) } self.current_music = item_dict # qtreewidgetiem # set play_list according to the mode if mode == 'global': self.play_list.append(self.current_music) elif mode == 'list' or mode == 'local': iterator = QTreeWidgetItemIterator( self.parent.main_list.music_list) while iterator.value(): item = iterator.value() item_dict = { 'song_name': item.text(0), 'singers': item.text(1), 'album_name': item.text(2), 'interval': '', 'song_mid': item.text(4), 'url': item.text(5), 'source': item.text(6) } self.play_list.append(item_dict) iterator.__iadd__(1) self.order_play_list = self.play_list[:] # play self.play() # set song_info if self.current_music['singers'] != '': self.song_info.setText(self.current_music['song_name'] + '-' + self.current_music['singers']) else: self.song_info.setText(self.current_music['song_name']) # set total time media_info = json.loads( MediaInfo.parse(music_path).to_json())['tracks'][0] interval = convert_interval(math.floor(media_info['duration'] / 1000)) self.time.setText(self.time.text().split('/')[0] + '/' + interval)
def search(self): self.parent.main_list.music_list.clear() self.parent.navigation.local_list.setCurrentItem(None) self.parent.navigation.play_list.setCurrentItem(None) self.parent.main_list.display_mode = 'global' keywords = self.search_line.text() if keywords == '': return if self.music_source == 'qq': self.parent.navigation.navigation_list.setCurrentRow(0) api = QQMusicApi() elif self.music_source == 'netease': self.parent.navigation.navigation_list.setCurrentRow(1) api = NeteaseCloudMusicAPI() elif self.music_source == 'migu': self.parent.navigation.navigation_list.setCurrentRow(2) api = MiguMusicAPI() else: raise ValueError( 'music sources only contain qq, netease and migu.') search_result = [] if api.name == 'qq': for page in [1, 2]: search_result += api.search(page, keywords) elif api.name == 'netease': search_result += api.search(keywords, 1) elif api.name == 'migu': search_result += api.search(keywords) else: raise ValueError('api.name not in qq, netease and migu') for item in search_result: qt_item = QTreeWidgetItem() qt_item.setText(0, item['song_name']) if api.name == 'migu': singers = item['singer_list'] else: singers = '' for singer in item['singer_list']: singers += singer singers += '/' singers = singers.strip('/') qt_item.setText(1, singers) qt_item.setText(2, item['album_name']) if api.name == 'migu': qt_item.setText(3, '') else: qt_item.setText(3, convert_interval(item['interval'])) qt_item.setText(4, str(item['song_mid'])) if api.name == 'migu': qt_item.setText(5, item['url']) else: qt_item.setText(5, '') qt_item.setText(6, api.name) self.parent.main_list.music_list.addTopLevelItem(qt_item)
def do_play(self, arg): ''' play songs > play # default play from playlist 1 > play -pl 2 # play from playlist 2 > play -s 2 # play the 2nd song in the last search result ''' # update playlist self._get_playlist_info() # play from playlist 1 by default if arg == '': if len(self.playlist) == 0: print('No playlist found, use `cpl` to create one.') return pl_name = self.playlist[0] pl_path = os.path.join(self.LIST_DIR, pl_name + '.json') with open(pl_path, 'r') as f: song_list = json.load(f) if len(song_list) == 0: print('No song found in playlist {}.'.format(pl_name)) return play_pl_thread = threading.Thread(target=self._play_from_playlist, args=(song_list, )) play_pl_thread.start() # play from playlist or search else: # args number args = arg.split() if len(args) < 2: print( 'Not enough arguments, use `help play` for more information.' ) return if len(args) > 2: print( 'Too much arguments, use `help play` for more information.' ) return # check mode mode = args[0] if mode not in ['-pl', '-s']: print('Arguments error, use `help play` for more information.') return # play from playlist if mode == '-pl': if len(self.playlist) == 0: print('No playlist found, use `cpl` to create one.') return try: pl_index = int(args[1]) assert pl_index >= 1 and pl_index <= len(self.playlist) except: print('playlist index should be in [1, {}].'.format( len(self.playlist))) return pl_name = self.playlist[pl_index - 1] pl_path = os.path.join(self.LIST_DIR, pl_name + '.json') with open(pl_path, 'r') as f: song_list = json.load(f) if len(song_list) == 0: print('No song found in playlist {}.'.format(pl_name)) return play_pl_thread = threading.Thread( target=self._play_from_playlist, args=(song_list, )) play_pl_thread.start() # play from search result elif mode == '-s': if len(self.search_result['content']) == 0: print('No search result, do search first.') return s_index_upper_limit = len( self.search_result['content']) if len( self.search_result['content']) <= 10 else 10 try: s_index = int(args[1]) assert s_index >= 1 and s_index <= s_index_upper_limit except: print('The index in search result must be in [1, {}].'. format(s_index_upper_limit)) # song info play_song = copy.deepcopy( self.search_result['content'][s_index - 1]) play_song['source'] = self.search_result['source'] play_song['singers'] = play_song['singer_list'] if play_song[ 'source'] == 'migu' else convert_singerlist( play_song['singer_list']) play_song['interval'] = '' if play_song[ 'source'] == 'migu' else convert_interval( play_song['interval']) play_s_thread = threading.Thread(target=self._play_from_search, args=(play_song, )) play_s_thread.start()
def do_add(self, arg): ''' add songs in last search result to playlist > add 10 1 # 10 is index in search result, 1 is index of playlist ''' # check search result if len(self.search_result['content']) == 0: print('No search result, do search first.') return # check playlist exists if len(self.playlist) == 0: print('No playlist found, use `cpl` to create one.') return # check arguments format arg_list = arg.split() # check number of arguments if len(arg_list) < 2: print('Not enough arguments, the number of arguments should be 2.') return elif len(arg_list) > 2: print('Too much arguments, the number of arguments should be 2.') return try: # check if arguments are number s_index = int(arg_list[0]) # index in search result pl_index = int(arg_list[1]) # index in playlist except: print( 'Arguments should be [the index in search result] [the index of playlist].' ) return # check if index is out of range s_index_upper_limit = len(self.search_result['content']) if len( self.search_result['content']) <= 10 else 10 if s_index < 1 or s_index > s_index_upper_limit: print('The index in search result must be in [1, {}].'.format( s_index_upper_limit)) return if pl_index < 1 or pl_index > len(self.playlist): print('The index of playlist must be in [1, {}].'.format( len(self.playlist))) return # add song to playlist add_song = self.search_result['content'][s_index - 1] source = self.search_result['source'] pl_name = self.playlist[pl_index - 1] # if playlist name exists locally if os.path.exists('{}/{}.json'.format(self.LIST_DIR, pl_name)): full_path = '{}/{}.json'.format(self.LIST_DIR, pl_name) else: print('playlist {} dosen\'t exist.'.format(pl_name)) return # generate stored info music_info = dict() music_info['source'] = source music_info['song_name'] = add_song['song_name'] music_info['singers'] = add_song[ 'singer_list'] if source == 'migu' else convert_singerlist( add_song['singer_list']) music_info['album_name'] = add_song['album_name'] music_info['interval'] = '' if source == 'migu' else convert_interval( add_song['interval']) music_info['song_mid'] = add_song['song_mid'] music_info['url'] = add_song['url'] if source == 'migu' else '' # add to playlist with open(full_path, 'r') as f: song_list = json.load(f) song_list.append(music_info) with open(full_path, 'w') as f: json.dump(song_list, f, indent=1) print('Add {} to playlist {} successfully.'.format( add_song['song_name'], pl_name))