Skip to content

Commit

Permalink
Emby 跳过简介/片头
Browse files Browse the repository at this point in the history
  • Loading branch information
kjtsune committed Jan 2, 2024
1 parent 44b7a49 commit 723b39c
Show file tree
Hide file tree
Showing 7 changed files with 95 additions and 40 deletions.
6 changes: 2 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@

> Windows
* 双击 `embyToLocalPlayer_debug.bat`
* 双击 `embyToLocalPlayer_debug.bat` (不要用管理员运行)
* 若无报错,按 1(窗口运行),可网页播放测试。(点击原来的播放按钮就可以)
* 按 2 则创建开机启动项并后台运行。
* 问题排查:
Expand Down Expand Up @@ -89,9 +89,8 @@ https://github.com/kjtsune/embyToLocalPlayer#faq
> 通用说明
* Python 最低支持版本为 3.8。
* 用鼠标手势软件关闭播放器体验更舒服一点。
* 同服务器同时开启多个浏览器标签页,会造成回传进度失败假象。手动刷新一下页面,或者只开一个标签。
* Plex 及部分域名有 dns 污染,若无法播放,修改系统 DNS 或使用代理。
* 部分域名及 Plex 域名有 dns 污染,若无法播放,修改系统 DNS 或使用代理。
* 反馈群组在频道置顶,提问前先把 FAQ 看一遍,不含敏感数据不私聊。
小更新会频道提醒,不过应该也没什么更新的了,反馈不需要关注频道。[https://t.me/embyToLocalPlayer](https://t.me/embyToLocalPlayer)

Expand Down Expand Up @@ -121,7 +120,6 @@ https://github.com/kjtsune/embyToLocalPlayer#faq
2. 换播放器及换视频文件测试是否复现。
3. 截图或复制 `.bat` 窗口中的日志(选中后回车即复制)。
4. 碰到什么问题及怎么复现。
5. [可选] 开启日志文件会隐藏报错里的用户名 `.ini` > `[dev]` > `log_file`

> 字幕相关
Expand Down
4 changes: 2 additions & 2 deletions embyToLocalPlayer.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ def start_play(data):
or (player_name == 'dandanplay' and mount_disk_mode):
player_manager = PlayerManager(data=data, player_name=player_name, player_path=player_path)
player_manager.start_player(cmd=cmd, start_sec=start_sec, sub_file=sub_file, media_title=media_title,
mount_disk_mode=mount_disk_mode)
mount_disk_mode=mount_disk_mode, data=data)
eps_data = eps_data_thread.join()
player_manager.playlist_add(eps_data=eps_data)
player_manager.update_playlist_time_loop()
Expand All @@ -108,7 +108,7 @@ def start_play(data):

player_function = player_start_func_dict[player_name]
stop_sec_kwargs = player_function(cmd=cmd, start_sec=start_sec, sub_file=sub_file, media_title=media_title,
mount_disk_mode=mount_disk_mode)
mount_disk_mode=mount_disk_mode, data=data)
stop_sec = stop_sec_function_dict[player_name](**stop_sec_kwargs)
logger.info('stop_sec', stop_sec)
if stop_sec is None:
Expand Down
8 changes: 6 additions & 2 deletions embyToLocalPlayer_debug.bat
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,12 @@ GOTO END
:TWO
echo you have pressed two
set startupVbs="%APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup\embyToLocalPlayer.vbs"
rem echo "%startupVbs%"
echo CreateObject("Wscript.Shell").Run """python"" ""%cd%\embyToLocalPlayer.py""" , 0, True > %startupVbs%
set startupCmd=CreateObject("Wscript.Shell").Run """python"" ""%cd%\embyToLocalPlayer.py""" , 0, True
echo startupCmd=%startupCmd%
echo startupVbs=%startupVbs%
echo %startupCmd% > %startupVbs%
echo writing startupCmd to startupVbs, save in startup folder.
timeout /nobreak /t 1 >nul
echo close this window manually
wscript.exe ""%startupVbs%""
GOTO END
Expand Down
21 changes: 6 additions & 15 deletions user_script/embyToLocalPlayer.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// @name:zh-CN embyToLocalPlayer
// @name:en embyToLocalPlayer
// @namespace https://github.com/kjtsune/embyToLocalPlayer
// @version 1.1.12.1
// @version 1.1.13
// @description 需要 Python。Emby/Jellyfin 调用外部本地播放器,并回传播放记录。适配 Plex。
// @description:zh-CN 需要 Python。Emby/Jellyfin 调用外部本地播放器,并回传播放记录。适配 Plex。
// @description:en Require Python. Play in an external player. Update watch history to Emby/Jellyfin server. Support Plex.
Expand All @@ -21,6 +21,11 @@
// ==/UserScript==
'use strict';
/*
2024-1-2:
1. 适配 Emby 跳过简介/片头。(限 mpv,且视频本身无章节,通过添加章节实现。)
* 版本间累积更新:
* mpv script-opts 被覆盖。@verygoodlee
* mpv 切回第一集时网络外挂字幕丢失。@verygoodlee
2023-12-11:
1. 美化 mpv pot 标题。
2. 改善版本筛选逻辑。
Expand All @@ -37,20 +42,6 @@
* 默认启用日志文件。
* pot 播放列表 未加载完成时可退出。
* 网络流:外挂 sup 支持(限 Emby v4.8.0.55 | mpv)。升级 Emby 记得备份,无法回退。
2023-10-04:
1. pot 和 vlc(Linux/macOS) 网络外挂字幕时,使用连播代替播放列表。`.ini` > playlist
2. 高版本 macOS 自启方案。@Eatsolx
* 版本间累积更新:
* 增加:bangumi.tv bgm.tv 单向同步支持。见 FAQ。
* Jellyfin: 适配 基础 URL。
* 可一键更新。见 FAQ。
* 可保存日志。ini > dev > log_file。
* Trakt:未启用播放列表时同步失效。
* Trakt:自启时误弹认证窗口。
* 播放列表:文件命名不规范时失效。
* 减少回传次数。**油猴脚本也需要更新**
* 播放网络流时:pot 播放列表:降低添加条目速度,减少异常。
* 播放网络流时:mpc 切换进度时 api 无响应,导致提前回传。
*/
(function () {
'use strict';
Expand Down
33 changes: 25 additions & 8 deletions utils/net_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -366,22 +366,37 @@ def version_filter(file_path, episodes_data):
logger.info(f'disable playlist, cuz version_filter: fail, {ini_re=}')
return [_ep_current]

def emby_title_ep_map():
def title_intro_index_map():
episodes_info = data.get('episodes_info')
_map = {}
_title_map = {}
_start_map = {}
_end_map = {}
for ep in episodes_info:
if 'ParentIndexNumber' not in ep or 'IndexNumber' not in ep:
logger.info('disable emby_title_ep_map, cuz season or ep index num error found')
logger.info('disable title_intro_index_map, cuz season or ep index num error found')
return {}
if 'IndexNumberEnd' in ep:
_t = f"{ep['SeriesName']} S{ep['ParentIndexNumber']}" \
f":E{ep['IndexNumber']}-{ep['IndexNumberEnd']} - {ep['Name']}"
else:
_t = f"{ep['SeriesName']} S{ep['ParentIndexNumber']}:E{ep['IndexNumber']} - {ep['Name']}"
_map[f"{ep['ParentIndexNumber']}-{ep['IndexNumber']}"] = _t
return _map
_key = f"{ep['ParentIndexNumber']}-{ep['IndexNumber']}"
_title_map[_key] = _t

emby_title_ep_data = emby_title_ep_map()
chapters = [i for i in ep['Chapters'][:5] if i.get('MarkerType')
and not str(i['StartPositionTicks']).endswith('000000000')
and not (i['StartPositionTicks'] == 0 and i['MarkerType'] == 'Chapter')]
if not chapters or len(chapters) > 2:
continue
for i in chapters:
if i['MarkerType'] == 'IntroStart':
_start_map[_key] = i['StartPositionTicks'] // (10 ** 7)
elif i['MarkerType'] == 'IntroEnd':
_end_map[_key] = i['StartPositionTicks'] // (10 ** 7)

return _title_map, _start_map, _end_map

title_data, start_data, end_data = title_intro_index_map()

def parse_item(item):
source_info = item['MediaSources'][0]
Expand All @@ -395,8 +410,8 @@ def parse_item(item):
media_path = translate_path_by_ini(file_path) if mount_disk_mode else stream_url
basename = os.path.basename(file_path)
index = item.get('IndexNumber', 0)
title_key = f"{item.get('ParentIndexNumber')}-{index}"
emby_title = emby_title_ep_data.get(title_key)
unique_key = f"{item.get('ParentIndexNumber')}-{index}"
emby_title = title_data.get(unique_key)
media_title = f'{emby_title} | {basename}' if emby_title else basename
media_basename = os.path.basename(media_path)
total_sec = int(source_info['RunTimeTicks']) // 10 ** 7
Expand Down Expand Up @@ -431,6 +446,8 @@ def parse_item(item):
index=index,
size=source_info['Size'],
media_title=media_title,
intro_start=start_data.get(unique_key),
intro_end=end_data.get(unique_key),
))
return result

Expand Down
45 changes: 36 additions & 9 deletions utils/players.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,9 @@ def init_player_instance(function, **kwargs):
return player


def mpv_player_start(cmd, start_sec=None, sub_file=None, media_title=None, get_stop_sec=True, mount_disk_mode=None):
def mpv_player_start(cmd, start_sec=None, sub_file=None, media_title=None, get_stop_sec=True, mount_disk_mode=None,
data=None):
intro_start, intro_end = data.get('intro_start'), data.get('intro_end')
is_darwin = True if platform.system() == 'Darwin' else False
is_iina = True if 'iina-cli' in cmd[0] else False
is_mpvnet = True if 'mpvnet' in cmd[0] else False
Expand Down Expand Up @@ -230,7 +232,9 @@ def mpv_player_start(cmd, start_sec=None, sub_file=None, media_title=None, get_s
if sub_file and is_mpvnet and mpv:
_cmd = ['sub-add', sub_file]
mpv.command(*_cmd)

if mpv and intro_end:
chapter_list = [{'title': 'intro', 'time': intro_start}, {'title': 'main', 'time': intro_end}]
mpv.command('set_property', 'chapter-list', chapter_list)
if not get_stop_sec:
return
if mpv:
Expand Down Expand Up @@ -268,11 +272,36 @@ def playlist_add_mpv(mpv: MPV, data, eps_data=None, limit=10):
else:
sub_cmd = f',sub-file={sub_file}'

if intro_end := ep.get('intro_end'):
# chapter_list = [{'title': 'intro', 'time': ep['intro_start']}, {'title': 'main', 'time': intro_end}]
# mpv.command('set_property', 'chapter-list', chapter_list)
chapters_text = f''';FFMETADATA1
[CHAPTER]
TIMEBASE=1/1
START={ep['intro_start']}
END={intro_end}
title=intro
[CHAPTER]
TIMEBASE=1/1
START={intro_end}
END=9999
title=main
'''
_tmp = os.path.join(configs.cwd, '.tmp')
chap_path = os.path.join(_tmp, f'{basename}-chapters.txt')
chap_cmd = f',chapters-file="{chap_path}"'
if not os.path.exists(_tmp):
os.mkdir(_tmp)
with open(chap_path, 'w', encoding='utf-8') as f:
f.write(chapters_text)
else:
chap_cmd = ''

try:
mpv.command(
'loadfile', ep['media_path'], 'append',
f'title="{media_title}",force-media-title="{media_title}",osd-playing-msg="{media_title}"'
f',start=0{sub_cmd}')
f',start=0{sub_cmd}{chap_cmd}')
except OSError:
logger.error('mpv exit: by playlist_add_mpv: except OSError')
return {}
Expand Down Expand Up @@ -302,8 +331,7 @@ def stop_sec_mpv(mpv, stop_sec_only=True, **_):
return stop_sec if stop_sec_only else name_stop_sec_dict


def vlc_player_start(cmd: list, start_sec=None, sub_file=None, media_title=None, get_stop_sec=True,
mount_disk_mode=None):
def vlc_player_start(cmd: list, start_sec=None, sub_file=None, get_stop_sec=True, mount_disk_mode=None, **_):
is_nt = True if os.name == 'nt' else False
port = get_pipe_or_port_str()
if mount_disk_mode:
Expand Down Expand Up @@ -435,7 +463,7 @@ def stop_sec_vlc(vlc: VLCHttpApi, stop_sec_only=True, **_):
time.sleep(0.2)


def mpc_player_start(cmd, start_sec=None, sub_file=None, media_title=None, get_stop_sec=True, mount_disk_mode=None):
def mpc_player_start(cmd, start_sec=None, sub_file=None, media_title=None, get_stop_sec=True, **_):
port = get_pipe_or_port_str()
if sub_file:
cmd += ['/sub', f'"{sub_file}"']
Expand Down Expand Up @@ -575,8 +603,7 @@ def stop_sec_mpc(mpc: MPCHttpApi, stop_sec_only=True, **_):
time.sleep(0.5)


def pot_player_start(cmd: list, start_sec=None, sub_file=None, media_title=None, get_stop_sec=True,
mount_disk_mode=None):
def pot_player_start(cmd: list, start_sec=None, sub_file=None, media_title=None, get_stop_sec=True, **_):
if sub_file:
cmd.append(f'/sub={sub_file}')
if start_sec is not None:
Expand Down Expand Up @@ -690,7 +717,7 @@ def for_each_window(hwnd, _):


def dandan_player_start(cmd: list, start_sec=None, sub_file=None, media_title=None, get_stop_sec=True,
mount_disk_mode=None):
mount_disk_mode=None, **_):
if sub_file:
pass
if not mount_disk_mode:
Expand Down
18 changes: 18 additions & 0 deletions utils/tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -249,12 +249,28 @@ def main_ep_to_title(main_ep_info):
f":E{main_ep_info['IndexNumber']}-{main_ep_info['IndexNumberEnd']} - {main_ep_info['Name']}"


def main_ep_intro_time(main_ep_info):
res = {}
chapters = [i for i in main_ep_info['Chapters'][:5] if i.get('MarkerType')
and not str(i['StartPositionTicks']).endswith('000000000')
and not (i['StartPositionTicks'] == 0 and i['MarkerType'] == 'Chapter')]
if not chapters or len(chapters) > 2:
return res
for i in chapters:
if i['MarkerType'] == 'IntroStart':
res['intro_start'] = i['StartPositionTicks'] // (10 ** 7)
elif i['MarkerType'] == 'IntroEnd':
res['intro_end'] = i['StartPositionTicks'] // (10 ** 7)
return res


def parse_received_data_emby(received_data):
extra_data = received_data['extraData']
main_ep_info = extra_data['mainEpInfo']
episodes_info = extra_data['episodesInfo']
playlist_info = extra_data['playlistInfo']
emby_title = main_ep_to_title(main_ep_info)
intro_time = main_ep_intro_time(main_ep_info)
api_client = received_data['ApiClient']
mount_disk_mode = True if received_data['mountDiskEnable'] == 'true' else False
url = urllib.parse.urlparse(received_data['playbackUrl'])
Expand Down Expand Up @@ -363,6 +379,8 @@ def parse_received_data_emby(received_data):
main_ep_info=main_ep_info,
episodes_info=episodes_info,
playlist_info=playlist_info,
intro_start=intro_time.get('intro_start'),
intro_end=intro_time.get('intro_end'),
)
return result

Expand Down

0 comments on commit 723b39c

Please sign in to comment.