def _get_playlist_list() -> List[Tuple[str, str]]: playlist_list_dir = get_playlist_dir(user_dir, 'playlist') playlist_list = [(os.path.join(playlist_list_dir, f), os.path.splitext(f)[0]) for f in os.listdir(playlist_list_dir) if os.path.isfile(os.path.join(playlist_list_dir, f)) and f.endswith('.playlist')] return playlist_list
def playlist_rename(args): user_dir = get_user_data_dir() old_play_list_file = get_playlist_file_path_by_name( get_playlist_dir(user_dir, 'playlist'), args.old_name) if not os.path.exists(old_play_list_file): print('播放列表[' + args.old_name + '][' + old_play_list_file + ']不存在') return new_play_list_file = get_playlist_file_path_by_name( get_playlist_dir(user_dir, 'playlist'), args.new_name) if os.path.exists(new_play_list_file): print('播放列表[' + args.new_name + '][' + new_play_list_file + ']已存在') return os.rename(old_play_list_file, new_play_list_file) if os.path.exists(new_play_list_file): print('播放列表重命名成功') else: print('播放列表重命名失败')
def playlist_list(args): user_dir = get_user_data_dir() play_list_dir = get_playlist_dir(user_dir, 'playlist') play_list = [ os.path.splitext(f)[0] for f in os.listdir(play_list_dir) if os.path.isfile(os.path.join(play_list_dir, f)) and f.endswith('.playlist') ] print('播放列表目录:', play_list_dir) if len(play_list) == 0: print('没有播放列表') for i, pl in enumerate(play_list): print('[' + str(i) + ']', pl)
def play(args): user_dir = get_user_data_dir() playlist_dir = get_playlist_dir(user_dir, 'playlist') playlist_path = get_playlist_file_path_by_name(playlist_dir, '临时列表') if os.path.exists(playlist_path): os.remove(playlist_path) temp_playlist = LocalTempFilePlaylist(playlist_path) temp_playlist.media_list = args.input temp_playlist.save_playlist(force=True) setattr(args, 'name', '临时列表') delattr(args, 'input') playlist_play(args)
def playlist_play(args): logger = get_logger() if is_in_prompt_mode(args): try: cmd = list() cwd = '.' if not is_in_nuitka(): cmd = [ 'python', os.path.join( os.path.split(os.path.abspath(__file__))[0], 'cli.py') ] cwd = str( Path( os.path.join( os.path.split( os.path.abspath(__file__))[0])).parent) else: cmd = [ str(get_abs_path('pysimpledlna.exe')), ] cwd = str(get_abs_path()) for arg in args.prompt_args: cmd.append(arg) print('启动目录:', cwd) print('启动命令:', ' '.join(cmd)) process = start_subprocess(cmd, cwd) print('新进程ID:' + str(process.pid)) except: traceback.print_exc() return from pysimpledlna.ui.playlist import (PlayListPlayer, PlayerModel, VideoPositionFormatter, VideoControlFormatter, VideoFileFormatter) from pysimpledlna.utils import format_time from prompt_toolkit_ext.widgets import RadioList from prompt_toolkit.shortcuts.progress_bar.formatters import Text from pysimpledlna.ui.terminal import PlayerStatus from prompt_toolkit.patch_stdout import patch_stdout from prompt_toolkit import HTML from prompt_toolkit_ext.event import KeyEvent dlna_server = _DLNA_SERVER user_dir = get_user_data_dir() play_list_dir = get_playlist_dir(user_dir, 'playlist') device: Device = None if args.auto_selected: for i, d in enumerate(dlna_server.get_devices(5)): device = d break else: url = args.url if url is None: if url is None: settings = Settings(get_setting_file_path()) default_device_urls: List[str] = settings.get_default_devices() for default_device_url in default_device_urls: device = dlna_server.find_device(default_device_url) if device is not None: default_device_urls.remove(default_device_url) default_device_urls.insert(0, default_device_url) settings.write() break else: device = dlna_server.parse_xml(url) if device is None: print('No Device available') return device = dlna_server.register_device(device) play_list = PlayListWrapper() play_list.start_play = True # 初始化播放列表 if not hasattr(args, 'name') or args.name is None: play_list.start_play = False all_playlist_path = os.listdir(play_list_dir) if len(all_playlist_path) == 0: logger.info('没有播放列表') return setattr(args, 'name', Path(all_playlist_path[0]).stem) play_list_file = get_playlist_file_path(args) if not os.path.exists(play_list_file): logger.info('播放列表[' + args.name + '][' + play_list_file + ']不存在') return play_list.playlist = Playlist.get_playlist(play_list_file) play_list.playlist.load_playlist() play_list.set_vo(play_list.playlist) device_list: DeviceList = DeviceList([device], 0) player = PlayListPlayer(play_list, [('', '')], device_list) player_model: PlayerModel = player.create_model() ac = ActionController(play_list, device, player_model) # 设置内部播放列表 def _setup_inner_playlist(new_play_list: Playlist): play_list.playlist = new_play_list ac.current_idx = play_list.playlist.current_index play_list.playlist_init_index = play_list.playlist.current_index play_list.playlist_init_position = play_list.playlist.current_pos # 设置视图播放列表 def _setup_view_playlist(new_play_list: Playlist): play_list.set_vo(new_play_list) player.update_playlist_part() # 设置播放列表 def _setup_playlist(new_play_list: Playlist): _setup_inner_playlist(new_play_list) _setup_view_playlist(new_play_list) _setup_playlist(play_list.playlist) # 获得所有播放列表的路径和名字 def _get_playlist_list() -> List[Tuple[str, str]]: playlist_list_dir = get_playlist_dir(user_dir, 'playlist') playlist_list = [(os.path.join(playlist_list_dir, f), os.path.splitext(f)[0]) for f in os.listdir(playlist_list_dir) if os.path.isfile(os.path.join(playlist_list_dir, f)) and f.endswith('.playlist')] return playlist_list def _setup_playlist_list(selected_name): playlist_list = _get_playlist_list() player.playlistlist_part.values = playlist_list for index, playlist in enumerate(playlist_list): playlist_name = playlist[1] if playlist_name == selected_name: player.playlistlist_part.set_checked_index(index) break _setup_playlist_list(args.name) def _get_playlist_filename_by_name(playlist_name): _play_list_file = get_playlist_file_path_by_name( play_list_dir, playlist_name) _play_list = LocalFilePlaylist(_play_list_file) _play_list.load_playlist() return _play_list # 切换播放列表 def _playlist_selected(old_value, new_value): old_file_path = old_value[0] old_file_name = old_value[1] new_file_path = new_value[0] new_file_name = new_value[1] def samefile(old_file_path, new_file_path): o_exists = os.path.exists(old_file_path) n_exists = os.path.exists(new_file_path) if not o_exists and n_exists: return False if not o_exists and not n_exists: return True if o_exists and not n_exists: return False return os.path.samefile(old_file_path, new_file_path) if samefile(old_file_path, new_file_path): return new_playlist = Playlist.get_playlist(new_file_path) _setup_playlist_list(new_playlist.get_playlist_name()) _setup_view_playlist(new_playlist) return # 切换视频 def _video_selected(old_value, new_value): if not play_list.start_play: play_list.start_play = True old_file_path = get_user_data_dir() old_file_name = Path(old_file_path).name else: old_file_path = old_value[0] old_file_name = old_value[1] new_file_path = new_value[0] new_file_name = new_value[1] # 视图显示的播放列表与内部播放列表不一致时 # 先将内部播放列表设置为视图播放列表 # 然后递归调用进行播放 if not play_list.is_sync(): current_file_path = play_list.playlist.media_list[ play_list.playlist.current_index] play_list.playlist.save_playlist(force=True) new_playlist = Playlist.get_playlist( play_list.get_view_playlist_path()) _setup_inner_playlist(new_playlist) return _video_selected( [current_file_path, Path(current_file_path).name], new_value) if os.path.samefile(old_file_path, new_file_path) \ and ac.player.player_status in [PlayerStatus.PLAY, PlayerStatus.PAUSE] \ and os.path.samefile(new_file_path, ac.local_file_path): if ac.player.player_status == PlayerStatus.PAUSE: ac.play() return True if not os.path.samefile(old_file_path, new_file_path) \ or not os.path.samefile(new_file_path, ac.local_file_path): selected_index = player.playlist_part.get_selected_index() ac.current_idx = selected_index ac.play() else: play_list.playlist.load_playlist() _setup_playlist(play_list.playlist) ac.play() # 初始化web ui from pysimpledlna.web import WebRoot, DLNAService web_root = WebRoot(ac, get_abs_path(Path('./webroot')), 0) dlna_service = DLNAService(ac, play_list=play_list, device_list=device_list, playlist_accessor=_get_playlist_list, select_playlist=_playlist_selected, select_video=_video_selected) dlna_server.app.route(**web_root.get_route_params()) dlna_server.app.route(**dlna_service.get_route_params()) player.webcontrol_url = web_root.get_player_page_url() player.create_key_bindings() player.create_ui() # 事件处理 # 刷新dlna设备列表 def _refresh_device_list(event: KeyEvent): import threading from threading import Timer def do_refresh(): if player.is_refresh_dlna_render: return player.is_refresh_dlna_render = True loading_char = ['▏', '▎', '▍', '▌', '▋', '▊', '▉'] def update_title(index): player.dlna_render_part.title = 'DLNA显示器 ' + loading_char[index % 7] if player.is_refresh_dlna_render: t = Timer(0.5, update_title, args=[(index + 1) % 7]) t.start() else: player.dlna_render_part.title = 'DLNA显示器' update_title(0) def find_device_by_location(devices: List[Device], location: str): for device in devices: if device.location == location: return device return None try: old_device_list = device_list.device_list result: List[Device] = list() for device in dlna_server.get_devices(60): known_device = dlna_server.register_device(device) result.append(known_device) # 根据已知的设备地址查询遗漏的设备 for known_device in dlna_server.known_devices.values(): d = find_device_by_location(result, known_device.location) if d is None: new_device = dlna_server.find_device( known_device.location) if new_device is not None: result.append(known_device) result = sorted(result, key=lambda dev: dev.location) device_list.device_list = result # 设置当前使用的设备 selected_device = old_device_list[device_list.selected_index] result = device_list.set_selected_index(selected_device) if result == -1: device_list.selected_index = 0 player.update_dlna_render_part() finally: player.is_refresh_dlna_render = False player.dlna_render_part.title = 'DLNA显示器' threading.Thread(target=do_refresh, daemon=True).start() # 切换dlna设备 def _dlna_device_selected(old_value, new_value): old_device_key = old_value[0] new_device_key = new_value[0] if old_device_key == new_device_key: return old_device = device_list.get_device_by_device_key(old_device_key) new_device = device_list.get_device_by_device_key(new_device_key) start_play = True if old_device.sync_thread is None or old_device.sync_thread.last_status is None \ or old_device.sync_thread.last_status.get('transport_info') is None: start_play = False else: current_transport_state = old_device.sync_thread.last_status[ 'transport_info']['CurrentTransportState'] if current_transport_state != 'PLAYING': start_play = False device_list.set_selected_index(new_device) positionhook, transportstatehook, exceptionhook = old_device.positionhook, old_device.transportstatehook, old_device.exceptionhook old_device.set_sync_hook(None, None, None) old_device.stop() old_device.stop_sync_remote_player_status() play_list.playlist.save_playlist(force=True) ac.init_ac(new_device) _setup_inner_playlist(play_list.playlist) if start_play: ac.play() new_device.set_sync_hook(positionhook, transportstatehook, exceptionhook) new_device.start_sync_remote_player_status() player.update_dlna_render_part() # 更新播放列表选中项位置,如果视图被切换则不进行更新视频列表内容 def _update_list_ui(current_index): if play_list.is_sync(): player.playlist_part.current_value = player.playlist_part.values[ current_index][0] # 视频进度向前 def _forward(event, times): target_position = ac.current_video_position + 10 * times if ac.current_video_duration == 0: time_str = format_time(target_position) ac.device.seek(time_str) return if target_position >= ac.get_max_video_position(): target_position = ac.get_max_video_position() - 10 time_str = format_time(target_position) ac.device.seek(time_str) # 视频进度向后 def _backward(event, times): target_position = ac.current_video_position - 10 * times if target_position <= 0: target_position = 1 time_str = format_time(target_position) ac.device.seek(time_str) # 播放下一个视频 def _next(event, times): ac.current_idx += times if ac.current_idx >= len(play_list.playlist.media_list): ac.current_idx = len(play_list.playlist.media_list) - 1 ac.play() # 播放上一个视频 def _last(event, times): ac.current_idx -= times if ac.current_idx < 0: ac.current_idx = 0 ac.play() # 设置播放列表中当前视频的索引 def _update_playlist_index(current_index): play_list.playlist.current_index = current_index play_list.playlist.save_playlist(force=True) # 更新播放列表当前文件长度 def _update_current_video_duration(o_position, n_position): if play_list.playlist.current_duration != ac.current_video_duration and ac.current_video_duration != 0: play_list.playlist.current_duration = ac.current_video_duration play_list.playlist.save_playlist(force=True) # 设置播放列表中当前视频的播放位置 def _update_playlist_video_position(o_position, n_position): play_list.playlist.current_pos = n_position play_list.playlist.save_playlist() # 使用新的索引和播放位置强制保存播放列表 def _save_playlist(current_index, current_position): play_list.playlist.current_index = current_index play_list.playlist.current_pos = current_position play_list.playlist.save_playlist(force=True) # 根据播放列表配置跳过片头 def _skip_head(current_index): logger.debug('==start==') logger.debug(f'cur index: {current_index}') logger.debug( f"position_in_playlist: {play_list.playlist_init_position}") logger.debug(f'current_video_position: {ac.current_video_position}') logger.debug(f'seek to {format_time(play_list.playlist.skip_head)}') logger.debug('== end ==') if play_list.playlist_init_position > 0 and play_list.playlist_init_index == ac.current_idx: if play_list.playlist_init_position > ac.current_video_position: resume_position = play_list.playlist_init_position - 5 if resume_position < 0: resume_position = 0 time_str = format_time(resume_position) ac.device.seek(time_str) play_list.playlist.current_pos = play_list.playlist_init_position play_list.playlist.save_playlist(force=True) play_list.playlist_init_position = -1 elif play_list.playlist.skip_head > 0: time.sleep(0.5) time_str = format_time(play_list.playlist.skip_head) ac.device.seek(time_str) play_list.playlist.current_pos = play_list.playlist.skip_head play_list.playlist.save_playlist(force=True) # 根据播放列表配置跳过片尾 def _skip_tail(o_position, n_position): if play_list.playlist.skip_tail == 0: return end = ac.get_max_video_position() - play_list.playlist.skip_tail if n_position >= end and end > 0: ac.play_next() # 退出 def _quit(): if ac.is_occupied: ac.device.stop_sync_remote_player_status() # 其他程序占用投屏时,退出时不发送结束投屏命令 #ac.device.stop() ac.end = True else: ac.stop_device() dlna_service.set_device_handler(_dlna_device_selected, _refresh_device_list) # 加入事件监听 player.playlist_part.check_event += _video_selected player.playlistlist_part.check_event += _playlist_selected player.dlna_render_radio.check_event += _dlna_device_selected # TODO 事件没有被调用,待查 player.player_events['quit'] += _quit player.player_events['refresh_dlna_render'] += _refresh_device_list player.controller_events['pause'] += \ lambda e: ac.resume() if player_model.player_status == PlayerStatus.PAUSE else ac.pause() player.controller_events['last'] += _last player.controller_events['next'] += _next player.controller_events['forward'] += _forward player.controller_events['backward'] += _backward ac.events['play'] += _skip_head ac.events['video_position'] += _skip_tail ac.events['play'] += _update_list_ui ac.events['play'] += _update_playlist_index ac.events['video_position'] += _update_current_video_duration ac.events['video_position'] += _update_playlist_video_position ac.events['stop'] += _save_playlist ac.events['resume'] += _update_list_ui ac.events['resume'] += _update_playlist_index device.set_sync_hook(positionhook=ac.hook, transportstatehook=ac.hook, exceptionhook=ac.excpetionhook) # 开始同步状态 device.start_sync_remote_player_status() with patch_stdout(): if play_list.start_play: ac.play() try: while True: if ac.end or not player.app.is_running: play_list.playlist.current_pos = ac.current_video_position play_list.playlist.current_index = ac.current_idx play_list.playlist.save_playlist(force=True) break time.sleep(0.5) finally: # 事件无法触发,暂时在这里调用 _quit() player.clear()
def get_playlist_file_path(args): user_dir = get_user_data_dir() play_list_dir = get_playlist_dir(user_dir, 'playlist') play_list_name = args.name play_list_file = os.path.join(play_list_dir, play_list_name + '.playlist') return play_list_file