コード例 #1
0
ファイル: cli.py プロジェクト: zqbxx/pysimpledlna
 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
コード例 #2
0
ファイル: cli.py プロジェクト: zqbxx/pysimpledlna
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('播放列表重命名失败')
コード例 #3
0
ファイル: cli.py プロジェクト: zqbxx/pysimpledlna
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)
コード例 #4
0
ファイル: cli.py プロジェクト: zqbxx/pysimpledlna
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)
コード例 #5
0
ファイル: cli.py プロジェクト: zqbxx/pysimpledlna
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()
コード例 #6
0
ファイル: cli.py プロジェクト: zqbxx/pysimpledlna
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