Skip to content

Commit

Permalink
mpv bangumi trakt 独立同步脚本
Browse files Browse the repository at this point in the history
  • Loading branch information
kjtsune committed Jun 6, 2024
1 parent 0f4b5a5 commit 102da74
Show file tree
Hide file tree
Showing 6 changed files with 166 additions and 18 deletions.
25 changes: 21 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,10 +128,9 @@ https://github.com/kjtsune/embyToLocalPlayer#faq
* **没按要求反馈会忽略。**

1. 运行 `debug.bat` 选1。( macOS 或 Linux 运行 `.command`)
* 若启动不成功,命令行输入 `python --version` 检查 Python 是否安装成功及版本。(便携版用户可跳过)
* Python 低于 3.8.10 的先升级试试看。(便携版用户可跳过)
* 参考`如何更新`,更新到最新版后测试。(所有用户均**不可跳过**
2. 换播放器及换视频文件测试是否复现。(Windows 用户更换包含 mpv 的便携版测试)
* 若启动不成功,Windows 用户换便携版测试。
* 参考`如何更新`,更新到最新版后测试。
2. 换播放器及换视频文件测试是否复现。(Windows 用户换含 mpv 的便携版测试)
3. 截图或复制 `.bat` 窗口中的日志(选中后回车即复制)。
4. 碰到什么问题及怎么复现。
5. [可选] 关闭模糊日志。 `.ini` > `[dev]` > `mix_log = no`
Expand Down Expand Up @@ -375,6 +374,24 @@ https://github.com/kjtsune/embyToLocalPlayer#faq

### 隐藏功能(无支持):

<details>
<summary>mpv bangumi trakt 独立同步脚本</summary>

> mpv bangumi trakt 独立同步脚本
* 使用情景:使用其他工具或客户端调用 mpv 播放 Emby/Jellyfin 视频,需要标记 bangumi trakt 中对应条目为已观看。
* 条件:mpv 播放器,播放网络视频流,播放进度超过 90% 时同步。
* 使用方法:
1. 下载 `etlp-python-embed-win32.zip` 并解压到任意文件夹。
2. 将 lua: `刚才解压的文件夹\utils\others\etlp_sync_bgm_trakt.lua` 移动至 mpv 的脚本文件夹。
例如:`mpv.exe 所在目录 > portable_config > scripts > etlp_sync_bgm_trakt.lua`
3. 修改 `etlp_sync_bgm_trakt.lua` 内 etlp 的保存目录(刚才解压的文件夹路径)
4. 参考上方 `FAQ > 观看记录存储服务相关` 修改配置文件:`embyToLocalPlayer_config.ini`
5. 播放一个视频,进度拖到 90% 以上,查看 etlp 日志:`刚才解压的文件夹 > log.txt`。或者查看 mpv 日志。
* 排错方法:使用本项目浏览器调用播放测试。

</details>

<details>
<summary>播放列表预读取下一集</summary>

Expand Down
34 changes: 23 additions & 11 deletions utils/bangumi_sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import os
import re
import sys
import time
import urllib.parse

try:
Expand Down Expand Up @@ -179,13 +180,12 @@ def bangumi_sync_main(bangumi=None, eps_data: list = None, test=False, use_ini=F
return bgm


def bgm_sync_via_stream_url(url, get_emby=False):
from utils.bangumi_api import BangumiApiEmbyVer
def api_client_via_stream_url(url):
from utils.emby_api import EmbyApi
url = urllib.parse.urlparse(url)
netloc, path_spit = url.netloc, url.path.split('/')
parsed_url = urllib.parse.urlparse(url)
netloc, path_spit = parsed_url.netloc, parsed_url.path.split('/')
item_id = str(path_spit[-2])
query = dict(urllib.parse.parse_qsl(url.query))
query = dict(urllib.parse.parse_qsl(parsed_url.query))
query: dict

plex_token = query.get('X-Plex-Token')
Expand All @@ -195,28 +195,40 @@ def bgm_sync_via_stream_url(url, get_emby=False):

if is_plex:
# 没找到好的媒体文件 key 反查条目的方法。
# plex = PlexApi(host=f"{url.scheme}://{netloc}",
# plex = PlexApi(host=f"{parsed_url.scheme}://{netloc}",
# api_key=api_key)
# media_key = 'library/parts/3814/1687966436/file.mp4'
logger.error('bgm_sync_via_stream_url: not support plex')
return
emby = EmbyApi(host=f"{url.scheme}://{netloc}{jelly_sp}",
logger.error('third_party_sync_via_stream_url: not support plex')
return None, None, None
emby = EmbyApi(host=f"{parsed_url.scheme}://{netloc}{jelly_sp}",
api_key=api_key,
user_id=None,
http_proxy=configs.script_proxy,
cert_verify=(not configs.raw.getboolean('dev', 'skip_certificate_verify', fallback=False)), )
if get_emby:
return emby
return emby, item_id, parsed_url


def bgm_sync_via_stream_url(url):
from utils.bangumi_api import BangumiApiEmbyVer
emby, item_id, parsed_url = api_client_via_stream_url(url)
if not emby:
time.sleep(1)
return
if not configs.check_str_match(parsed_url.netloc, 'bangumi', 'enable_host', log=True):
time.sleep(1)
return
bgm = BangumiApiEmbyVer(
username=configs.raw.get('bangumi', 'username', fallback=''),
private=configs.raw.getboolean('bangumi', 'private', fallback=True),
access_token=configs.raw.get('bangumi', 'access_token', fallback=''),
http_proxy=configs.script_proxy)
bangumi_sync_emby(emby=emby, bgm=bgm, emby_ids=[item_id])
time.sleep(1)


def run_via_console():
argv = sys.argv
logger.info(f'{argv=}')
if len(argv) == 2:
bgm_sync_via_stream_url(url=argv[1])

Expand Down
1 change: 1 addition & 0 deletions utils/emby_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ def get_items(self, genre='', types='Movie,Series,Video', fields: typing.Union[l
ids=None, limit=50, parent_id=None,
sort_by='DateCreated,SortName',
recursive=True, ext_params: dict = None):
# 注意默认不包含 Episode。同时 Episode 需要 ext_params={'HasTmdbId': None}。
fields = fields or self._default_fields
fields = fields if isinstance(fields, str) else ','.join(fields)
params = {
Expand Down
64 changes: 64 additions & 0 deletions utils/others/etlp_sync_bgm_trakt.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
-- ############### 以下为可配置项 ###############

-- etlp 的保存目录,注意末尾要斜杠,并且所有斜杠都是 "/"。
local etlp_root_dir = "C:/Green/etlp-python-embed-win32/"

-- 若不用 pyton_embed,修改为自定义的 python 绝对路径,并安装 requests 依赖。
local etlp_python = etlp_root_dir .. "python_embed/python.exe"

-- 以下三行不要动。
local etlp_util_dir = etlp_root_dir .. "utils/"
local etlp_bgm_sync_py = etlp_util_dir .. "bangumi_sync.py"
local etlp_trakt_sync_py = etlp_util_dir .. "trakt_sync.py"

-- 启用的脚本列表,个别不启用就删掉,删掉的话记得逗号也删掉。
-- 记得修改 etlp 的 ini 配置文件,按域名启用,故默认都启用。
local enable_srcipts = { etlp_bgm_sync_py, etlp_trakt_sync_py }

-- ############### 以上为可配置项 ###############

local script_run = false
local is_url = false
local my_unpack = table.unpack or unpack

local function sleep(secs)
local t0 = mp.get_time()
while mp.get_time() - t0 < secs do end
end

local function check_is_url(path)
return string.match(path, "^https?://") ~= nil
end

local function check_progress_and_domain()
local percent_pos = mp.get_property_number("percent-pos", 0)

if is_url and not script_run and percent_pos >= 90 then
local path = mp.get_property("path", "")
local title = mp.get_property("media-title", "")
mp.msg.info(etlp_python)
for _, script_path in ipairs(enable_srcipts) do
local run_command = { etlp_python, script_path, path }
mp.msg.info(script_path .. " --> " .. title)
mp.command_native_async({"run", my_unpack(run_command)}, function(success, result, error)
if not success then
mp.msg.error("Failed to run Python script: " ..
script_path .. ", error: " .. (error or "unknown error") .. "\n" .. table.concat(run_command, " "))
end
end)
sleep(0.1)
end
script_run = true
end
end

local function on_start_file()
local path = mp.get_property("path", "")
is_url = check_is_url(path)
script_run = false
mp.msg.info("Starting new file, resetting script run status.")
end

mp.register_event("start-file", on_start_file)

mp.add_periodic_timer(1, check_progress_and_domain)
2 changes: 1 addition & 1 deletion utils/tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ def main_ep_intro_time(main_ep_info):


def show_version_info(extra_data):
py_script_version = '2024.03.27'
py_script_version = '2024.06.06'
gm_info = extra_data.get('gmInfo')
user_agent = extra_data.get('userAgent')
if not gm_info:
Expand Down
58 changes: 56 additions & 2 deletions utils/trakt_sync.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
import os.path
import sys
import time
import typing

try:
sys.path.insert(0, os.path.dirname(os.path.dirname(__file__)))
except Exception:
pass

from utils.configs import configs, MyLogger
from utils.bangumi_sync import api_client_via_stream_url

logger = MyLogger()

Expand Down Expand Up @@ -120,7 +129,7 @@ def sync_ep_or_movie_to_trakt(trakt, eps_data):
return res


def trakt_sync_main(trakt=None, eps_data=None, test=False):
def trakt_api_client():
from utils.trakt_api import TraktApi
user_id = configs.raw.get('trakt', 'user_name', fallback='')
client_id = configs.raw.get('trakt', 'client_id', fallback='')
Expand All @@ -129,17 +138,62 @@ def trakt_sync_main(trakt=None, eps_data=None, test=False):
oauth_code = oauth_code[1] if len(oauth_code) == 2 else oauth_code[0]
if not all([user_id, client_id, client_secret]):
raise ValueError('trakt: require user_name, client_id, client_secret')
trakt = trakt or TraktApi(
trakt = TraktApi(
user_id=user_id,
client_id=client_id,
client_secret=client_secret,
oauth_code=oauth_code,
token_file=os.path.join(configs.cwd, 'trakt_token.json'),
http_proxy=configs.script_proxy)
return trakt


def trakt_sync_main(trakt=None, eps_data=None, test=False):
trakt = trakt or trakt_api_client()
if test:
trakt.test()
return trakt
else:
res = sync_ep_or_movie_to_trakt(trakt=trakt, eps_data=eps_data)
res and logger.info('trakt:', res)
return trakt


def emby_eps_data_generator(emby, item_id: typing.Union[str, list]):
from utils.emby_api import EmbyApi
emby: EmbyApi
item_ids = [item_id] if isinstance(item_id, str) else item_id
eps_data = emby.get_items(ids=item_ids,
types='Movie,Series,Video,Episode',
ext_params={'HasTmdbId': None}
)['Items']
for ep in eps_data:
ep['server'] = 'emby'
ep['basename'] = os.path.basename(ep['Path'])
return eps_data


def trakt_sync_via_stream_url(url):
emby, item_id, parsed_url = api_client_via_stream_url(url)
if not emby:
time.sleep(1)
return
if not configs.check_str_match(parsed_url.netloc, 'trakt', 'enable_host', log=True):
time.sleep(1)
return
eps_data = emby_eps_data_generator(emby=emby, item_id=item_id)
trakt = trakt_api_client()
trakt_sync_main(trakt=trakt, eps_data=eps_data, test=False)
time.sleep(1)


def run_via_console():
argv = sys.argv
logger.info(f'{argv=}')
if len(argv) == 2:
trakt_sync_via_stream_url(url=argv[1])


if __name__ == '__main__':
os.chdir(configs.cwd)
run_via_console()

0 comments on commit 102da74

Please sign in to comment.