def keyboard_tap_callback(proxy, type_, event, refcon): from AppKit import NSKeyUp, NSEvent if type_ < 0 or type_ > 0x7fffffff: LOG.error('Unkown mac event') run_event_loop() LOG.error('restart mac key board event loop') return event try: key_event = NSEvent.eventWithCGEvent_(event) except: LOG.info("mac event cast error") return event if key_event.subtype() == 8: key_code = (key_event.data1() & 0xFFFF0000) >> 16 key_state = (key_event.data1() & 0xFF00) >> 8 if key_code in (16, 19, 20): # 16 for play-pause, 19 for next, 20 for previous if key_state == NSKeyUp: if key_code is 19: ControllerApi.player.play_next() elif key_code is 20: ControllerApi.player.play_last() elif key_code is 16: ControllerApi.player.play_or_pause() return None return event
def load_user_infos(cls, data): avatar_url = data['avatar'] request = QNetworkRequest(QUrl(avatar_url)) cls.controller.network_manager.get(request) cls.controller.network_manager.network_queue.put(ViewOp.set_login_label_avatar) ViewOp.ui.MY_LIST_WIDGET.empty_layout() ViewOp.ui.COLLECTION_LIST_WIDGET.empty_layout() playlists = cls.controller.api.get_user_playlist() if not cls.controller.api.is_response_ok(playlists): return playlist_num = len(playlists) for playlist in playlists: pid = playlist['id'] w = PlaylistItem() w.set_playlist_item(playlist) w.signal_text_btn_clicked.connect(cls.on_playlist_btn_clicked) if cls.controller.api.is_playlist_mine(playlist): ViewOp.ui.MY_LIST_WIDGET.layout().addWidget(w) if pid == cls.controller.api.favorite_pid: favorite_playlist_detail = cls.controller.api.get_playlist_detail(pid) cls.controller.state["current_pid"] = pid ViewOp.ui.WEBVIEW.load_playlist(favorite_playlist_detail) else: if playlist_num <= 50: app_event_loop = asyncio.get_event_loop() app_event_loop.call_soon(cls.controller.api.get_playlist_detail, pid) else: ViewOp.ui.COLLECTION_LIST_WIDGET.layout().addWidget(w) LOG.info('load user infos finished')
def run_event_loop(): LOG.info("try to load mac hotkey event loop") import Quartz from AppKit import NSSystemDefined # Set up a tap, with type of tap, location, options and event mask tap = Quartz.CGEventTapCreate( Quartz.kCGSessionEventTap, # Session level is enough for our needs Quartz.kCGHeadInsertEventTap, # Insert wherever, we do not filter Quartz.kCGEventTapOptionDefault, # NSSystemDefined for media keys Quartz.CGEventMaskBit(NSSystemDefined), keyboard_tap_callback, None ) run_loop_source = Quartz.CFMachPortCreateRunLoopSource( None, tap, 0) Quartz.CFRunLoopAddSource( Quartz.CFRunLoopGetCurrent(), run_loop_source, Quartz.kCFRunLoopDefaultMode ) # Enable the tap Quartz.CGEventTapEnable(tap, True) # and run! This won't return until we exit or are terminated. Quartz.CFRunLoopRun() LOG.error('Mac hotkey event loop exit') return []
def save_cookies(self): try: write_json_into_file(self.cookies, DATA_PATH + self.cookies_filename) LOG.info("Save cookies successfully") except Exception as e: LOG.error(str(e)) LOG.error("Save cookies failed")
def http_request(self, method, action, query=None, urlencoded=None, callback=None, timeout=3): LOG.info('method=%s url=%s query=%s' % (method, action, query)) try: res = None if method == "GET": res = requests.get(action, headers=self.headers, cookies=self.cookies, timeout=timeout) elif method == "POST": res = requests.post(action, data=query, headers=self.headers, cookies=self.cookies, timeout=timeout) elif method == "POST_UPDATE": res = requests.post(action, data=query, headers=self.headers, cookies=self.cookies, timeout=timeout) self.cookies.update(res.cookies.get_dict()) content = show_requests_progress(res, self.signal_load_progress) content_str = content.decode('utf-8') content_dict = json.loads(content_str) return content_dict except Exception as e: LOG.error(str(e)) return {"code": 408}
def not_use_cache(*args, **kw): LOG.info("trying to update a playlist cache") data = func(*args, **kw) if data['code'] == 200: cache_data[args[1]] = data else: LOG.info("update playlist cache failed") return None
def __login(self): """登录 在用户登录成功时,会发射("login_success")信号 """ data = {} if self.is_need_captcha is True: captcha_text = str(self.captcha_lineedit.text()) flag, self.captcha_id = self.ne.confirm_captcha( self.captcha_id, captcha_text) if flag is not True: self.hint_label.setText(u'验证码错误') self.show_captcha() return phone_login = False # 0: 网易通行证, 1: 手机号登陆 username = str(self.username_widget.text()) # 包含中文会出错 password = str(self.password_widget.text()) # 2: checked, 1: partial checked is_remember = self.is_remember_chb.checkState() # judget if logining by using phone number if username.isdigit() and len(username) == 11: username = int(username) phone_login = True LOG.info(u"正在使用手机号进行登录") login_data = { 'username': username, 'password': password, 'is_remember': is_remember } if not self.is_autofill: data = self.ne.login(username, password, phone_login) else: data = self.ne.auto_login(username, password, phone_login) if data['code'] == 200: self.hint_label.setText(u'登录成功') self.signal_login_sucess.emit(data) self.close() self.save_login_info(login_data) elif data['code'] == 415: # 需要验证码 self.hint_label.setText(data['message']) LOG.info(u'本次登陆需要验证码') self.captcha_id = data['captchaId'] self.show_captcha() 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'密码错误') elif data['code'] == 509: self.hint_label.setText(u'你可能正在使用手机号登陆,密码错误几次之后,你可能需要等待几分钟再登录') else: self.hint_label.setText(u'未知错误')
def on_media_status_changed(self, state): if state == QMediaPlayer.EndOfMedia: self.finished.emit() self.stop() if (self._current_index == len(self._music_list) - 1) and (self.playback_mode == 2): self.signal_playlist_finished.emit() LOG.info("播放列表播放完毕") if not self._other_mode: self.play_next()
def __login(self): """登录 在用户登录成功时,会发射("login_success")信号 """ data = {} if self.is_need_captcha is True: captcha_text = str(self.captcha_lineedit.text()) flag, self.captcha_id = self.ne.confirm_captcha(self.captcha_id, captcha_text) if flag is not True: self.hint_label.setText(u'验证码错误') self.show_captcha() return phone_login = False # 0: 网易通行证, 1: 手机号登陆 username = str(self.username_widget.text()) # 包含中文会出错 password = str(self.password_widget.text()) # 2: checked, 1: partial checked is_remember = self.is_remember_chb.checkState() # judget if logining by using phone number if username.isdigit() and len(username) == 11: username = int(username) phone_login = True LOG.info(u"正在使用手机号进行登录") login_data = { 'username': username, 'password': password, 'is_remember': is_remember } if not self.is_autofill: data = self.ne.login(username, password, phone_login) else: data = self.ne.auto_login(username, password, phone_login) if data['code'] == 200: self.hint_label.setText(u'登录成功') self.signal_login_sucess.emit(data) self.close() self.save_login_info(login_data) elif data['code'] == 415: # 需要验证码 self.hint_label.setText(data['message']) LOG.info(u'本次登陆需要验证码') self.captcha_id = data['captchaId'] self.show_captcha() 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'密码错误') elif data['code'] == 509: self.hint_label.setText(u'你可能正在使用手机号登陆,密码错误几次之后,你可能需要等待几分钟再登录') else: self.hint_label.setText(u'未知错误')
def start(cls, port=12100): try: cls.sock.bind(('0.0.0.0', port)) cls.sock.listen(10) LOG.info("the cli server start at port %d" % port) cls.loop() except Exception as e: LOG.error(str(e)) port += 1 cls.start(port)
def get_song_detail(self, mid): data = self.ne.song_detail(mid) if not self.is_response_avaible(data): return data LOG.info("music id %d is available" % mid) songs = [] for each in data['songs']: song = self.access_music(each) songs.append(song) return songs
def play_specific_song_by_mid(cls, mid): songs = ControllerApi.api.get_song_detail(mid) if not ControllerApi.api.is_response_ok(songs): return False if len(songs) == 0: LOG.info("music id %d is unavailable" % mid) return False ControllerApi.player.play(songs[0]) return True
def use_cache(*args, **kw): if args[1] in cache_data: LOG.info('playlist: ' + cache_data[args[1]]['name'] + ' has been cached') else: data = func(*args, **kw) if data['code'] == 200: cache_data[args[1]] = data else: LOG.info("cache playlist failed") return None
def cache(this, *args, **kw): if len(args) > 1: if not args[1]: # 不使用缓存 cache_data[args[0]] = func(this, *args, **kw) else: if args[0] in cache_data: LOG.info('playlist: ' + cache_data[args[0]]['name'] + ' has been cached') else: cache_data[args[0]] = func(this, *args, **kw) return cache_data[args[0]]
def get_media_content_from_model(self, music_model): # if music_model['id'] in downloaded mid = music_model['id'] # 判断之前是否播放过,是否已经缓存下来,以后需要改变缓存的算法 for i, each in enumerate(self.__cache_list): if mid == each['id']: LOG.info(music_model['name'] + ' has been cached') return self.__cache_list[i]['content'] return self.cache_music(music_model)
def init(controller): """init plugin """ global CONTROLLER LOG.info("NetEase Plugin init") CONTROLLER = controller netease_normalize.ne.signal_load_progress.connect(CONTROLLER.on_web_load_progress) CONTROLLER.api = netease_normalize login_with_local_info()
def save_song(self, song_model, filepath): res = requests.get(song_model['url'], stream=True) if res.status_code == 200: print('status code 200') content = show_requests_progress(res, self.signal_download_progress) with open(filepath, 'wb') as f: f.write(content) LOG.info("save song %s successful" % song_model['name']) return True LOG.info("save song %s failed" % song_model['name']) return False
def play_next(self): index = self.get_next_song_index() if index is not None: if index == 0 and self.playback_mode == 2: self.signal_playlist_finished.emit() LOG.info("播放列表播放完毕") return music_model = self._music_list[index] self._current_index = index self.play(music_model) else: self.signal_playlist_is_empty.emit()
def get_playlist_detail(self, pid, cache=True): '''update playlist detail in sqlite if cache is false''' if cache is False: app_event_loop = asyncio.get_event_loop() app_event_loop.run_in_executor( None, partial(self.update_playlist_detail, pid)) if PlaylistDb.exists(pid): LOG.info("Read playlist %d info from sqlite" % (pid)) return PlaylistDb.get_data(pid) else: return self.update_playlist_detail(pid)
def run_event_loop(player): LOG.info("try to load mac hotkey event loop") import Quartz from AppKit import NSKeyUp, NSSystemDefined, NSEvent def keyboard_tap_callback(proxy, type_, event, refcon): if type_ < 0 or type_ > 0x7fffffff: LOG.error('Unkown mac event') Quartz.CFRunLoopRun() return run_event_loop(ControllerApi.player) try: key_event = NSEvent.eventWithCGEvent_(event) except: LOG.info("mac event cast error") return event if key_event.subtype() == 8: key_code = (key_event.data1() & 0xFFFF0000) >> 16 key_state = (key_event.data1() & 0xFF00) >> 8 if key_code is 16 or key_code is 19 or key_code is 20: # 16 for play-pause, 19 for next, 20 for previous if key_state == NSKeyUp: if key_code is 19: player.play_next() elif key_code is 20: player.play_last() elif key_code is 16: player.play_or_pause() return None return event # Set up a tap, with type of tap, location, options and event mask tap = Quartz.CGEventTapCreate( Quartz.kCGSessionEventTap, # Session level is enough for our needs Quartz.kCGHeadInsertEventTap, # Insert wherever, we do not filter Quartz.kCGEventTapOptionDefault, # NSSystemDefined for media keys Quartz.CGEventMaskBit(NSSystemDefined), keyboard_tap_callback, None ) run_loop_source = Quartz.CFMachPortCreateRunLoopSource( Quartz.kCFAllocatorDefault, tap, 0) Quartz.CFRunLoopAddSource( Quartz.CFRunLoopGetCurrent(), run_loop_source, Quartz.kCFRunLoopDefaultMode ) # Enable the tap Quartz.CGEventTapEnable(tap, True) # and run! This won't return until we exit or are terminated. Quartz.CFRunLoopRun() LOG.error('Mac hotkey exit event ')
def check_feeluown_release(cls): url = 'https://api.github.com/repos/cosven/FeelUOwn/releases' LOG.info('正在查找新版本...') res = requests.get(url) if res.status_code == 200: releases = res.json() for release in releases: if release['tag_name'] > cls.current_version: title = u'发现新版本 %s hoho' % release['tag_name'] LOG.info(title) content = release['name'] ControllerApi.notify_widget.show_message(title, content) ViewOp.ui.STATUS_BAR.showMessage(title, 5000) break
def play_next(self): index = self.get_next_song_index() if index is not None: if index == 0 and self.playback_mode == 2: self.signal_playlist_finished.emit() LOG.info("播放列表播放完毕") return music_model = self._music_list[index] self._current_index = index self.play(music_model) return True else: self.signal_playlist_is_empty.emit() return False
def wrapper(player, song_model=None): if song_model is None: return func(player) mid = song_model['id'] music_filename = str(mid) + '.mp3' music_filepath = os.path.join(SONGS_PATH, music_filename) if os.path.exists(music_filepath): LOG.info("play cached song %s" % song_model['name']) else: # async cache song app_event_loop = asyncio.get_event_loop() LOG.info("will download song %s " % song_model['name']) player.save_song(song_model, music_filepath) song_model['url'] = music_filepath return func(player, song_model)
def init(controller): LOG.info("NetEase Plugin init") ControllerApi.api = netease_normalize user = UserDb.get_last_login_user() if user is not None: netease_normalize.set_uid(user.uid) user_data = user.basic_info if user.cookies is not None: netease_normalize.ne.load_cookies(user.cookies) if netease_normalize.login_by_cookies(): ControllerApi.set_login() ViewOp.load_user_infos(user_data)
def http_request(self, method, action, query=None, urlencoded=None, callback=None, timeout=None): if method == 'GET': res = self.web.get(action) elif method == 'POST': res = self.web.post(action, query) try: # data = res.read() data = res.decode('utf-8') data = json.loads(data) except Exception as e: LOG.info(str(e)) data = res return data
def remove_music(self, mid): for i, music_model in enumerate(self.__music_list): if mid == music_model['id']: if self.__playlist.currentIndex() == i: self.__playlist.removeMedia(i) self.__music_list.pop(i) LOG.info(u'移除当前正在播放的歌曲') self.__playlist.next() break self.__playlist.removeMedia(i) self.__music_list.pop(i) break for cache in self.__cache_list: if mid == cache['id']: self.__cache_list.remove(cache) return True
def cache(this, *args, **kw): if len(args) > 1: if not args[1]: # 不使用缓存 data = func(this, *args, **kw) if data['code'] == 200: cache_data[args[0]] = data else: if args[0] in cache_data: LOG.debug('playlist: ' + cache_data[args[0]]['name'] + ' has been cached') else: data = func(this, *args, **kw) if data['code'] == 200: cache_data[args[0]] = data else: LOG.info("cache playlist failed") return cache_data[args[0]]
def on_set_favorite_btn_clicked(cls, checked=True): if not cls.controller.state["current_mid"]: return False data = cls.controller.api.set_music_to_favorite(cls.controller.state['current_mid'], checked) cls.controller.desktop_mini.content.is_song_like = checked if not cls.controller.api.is_response_ok(data): ViewOp.ui.LOVE_SONG_BTN.setChecked(not checked) cls.controller.desktop_mini.content.is_song_like = not checked return False playlist_detail = cls.controller.api.get_playlist_detail(cls.controller.api.favorite_pid, cache=False) if not cls.controller.api.is_response_ok(playlist_detail): ViewOp.ui.STATUS_BAR.showMessage("刷新 -喜欢列表- 失败") return False if cls.controller.state['current_pid'] == cls.controller.api.favorite_pid: LOG.info("喜欢列表的歌曲发生变化") ViewOp.ui.WEBVIEW.load_playlist(playlist_detail) return True
def on_set_favorite_btn_clicked(checked=True): if not ControllerApi.state["current_mid"]: return False data = ControllerApi.api.set_music_to_favorite(ControllerApi.state['current_mid'], checked) ControllerApi.desktop_mini.content.is_song_like = checked if not ControllerApi.api.is_response_ok(data): ViewOp.ui.LOVE_SONG_BTN.setChecked(not checked) ControllerApi.desktop_mini.content.is_song_like = not checked return False playlist_detail = ControllerApi.api.get_playlist_detail(ControllerApi.api.favorite_pid, cache=False) if not ControllerApi.api.is_response_ok(playlist_detail): ViewOp.ui.STATUS_BAR.showMessage("刷新 -喜欢列表- 失败") return False if ControllerApi.state['current_pid'] == ControllerApi.api.favorite_pid: LOG.info("喜欢列表的歌曲发生变化") ViewOp.ui.WEBVIEW.load_playlist(playlist_detail) return True
def show_progress(self, response): content = bytes() total_size = response.headers.get('content-length') if total_size is None: LOG.info(u'这个网络response没有Content-Length字段') content = response.content return content else: total_size = int(total_size) bytes_so_far = 0 for chunk in response.iter_content(): content += chunk bytes_so_far += len(chunk) progress = round(bytes_so_far * 1.0 / total_size * 100) self.signal_load_progress.emit(progress) return content
def set_favorite(self, checked=True): if not self.state["current_mid"]: return False data = self.api.set_music_to_favorite(self.state['current_mid'], checked) self.desktop_mini.content.is_song_like = checked if not self.is_response_ok(data): self.ui.LOVE_SONG_BTN.setChecked(not checked) self.desktop_mini.content.is_song_like = not checked return False playlist_detail = self.api.get_playlist_detail(self.api.favorite_pid, cache=False) if not self.is_response_ok(playlist_detail): self.ui.STATUS_BAR.showMessage("刷新 -喜欢列表- 失败") return False if self.state['current_pid'] == self.api.favorite_pid: LOG.info("喜欢列表的歌曲发生变化") self.ui.WEBVIEW.load_playlist(playlist_detail) return True
def run_func(func): data = dict() data['code'] = 404 try: result = eval(func) LOG.info("执行函数成功 %s " % func) data['code'] = 200 data['result'] = result except NameError: LOG.error("cli: %s command not found" % func) data['message'] = "command not found" except TypeError: LOG.error("cli: %s unknown arguement" % func) data['message'] = "arguments error" except Exception as e: LOG.error(str(e)) data['message'] = "unknown error" return data
def get_playlist_detail(self, pid, cache=True): """貌似这个请求会比较慢 :param pid: :return: """ # LOG.info(time.ctime()) data = self.ne.playlist_detail(pid) # 当列表内容多的时候,耗时久 # LOG.info(time.ctime()) data['uid'] = data['userId'] data['type'] = data['specialType'] for i, track in enumerate(data['tracks']): data['tracks'][i] = access_music(track) model = PlaylistModel(data).get_model() LOG.info('Update playlist cache finish: ' + model['name']) return model
def set_music_to_playlist(self, mid, pid, op): """ :param mid: :param pid: :param op: 'add' or 'del' :return: """ data = self.ne.addMusicToPlaylist(mid, pid, op) start_new_thread(self.get_playlist_detail, (pid, False, )) # self.get_playlist_detail(pid, False) if data['code'] == 200: LOG.info(op + ' ' + str(mid) + u' success') return True else: LOG.info(op + ' ' + str(mid) + u' failed') return False
def get_song_detail(self, mid): if SongDb.exists(mid): LOG.info("Read song %d from sqlite" % mid) return SongDb.get_data(mid) data = self.ne.song_detail(mid) if not self.is_response_avaible(data): return data songs = [] for each in data['songs']: song = self.access_music(each) songs.append(song) model = songs[0] song = SongDb(mid=mid, _data=pickle.dumps(model)) song.save() LOG.info('Save music %d info into sqlite' % mid) return model
def keyboard_tap_callback(proxy, type_, event, refcon): try: key_event = NSEvent.eventWithCGEvent_(event) except: LOG.info("mac event cast error") return event if key_event.subtype() == 8: key_code = (key_event.data1() & 0xFFFF0000) >> 16 key_state = (key_event.data1() & 0xFF00) >> 8 if key_code is 16 or key_code is 19 or key_code is 20: # 16 for play-pause, 19 for next, 20 for previous if key_state == NSKeyUp: if key_code is 19: player.play_next() elif key_code is 20: player.play_last() elif key_code is 16: player.play_or_pause() return None return event
def show_progress(self, response): content = bytes() try: total_size = response.getheader('Content-Length').strip() except: LOG.info(u'这个网络response没有Content-Length字段') return response.read() chunk_size = 8192 total_size = int(total_size) bytes_so_far = 0 while 1: chunk = response.read(chunk_size) content += chunk bytes_so_far += len(chunk) progress = round(bytes_so_far * 1.0 / total_size * 100) self.signal_load_progress.emit(progress) if not chunk: break return content