Compare commits

...

4 Commits

Author SHA1 Message Date
Cp0204
cdbd2882f3 🎨 适配媒体库配置界面及数据结构
Some checks are pending
Docker Publish / build-and-push (push) Waiting to run
2024-11-13 03:44:29 +08:00
Cp0204
dd42197b27 ♻️ 优化Emby模块配置及加载逻辑 2024-11-13 03:39:18 +08:00
Cp0204
9a1ebe0894 ♻️ 重构媒体库代码,模块化以便扩展
- 通过load_media_servers函数动态加载媒体库模块
- 动态加载媒体库配置,提高扩展性
- 修改部分配置字段,并添加兼容升级代码
2024-11-13 01:28:37 +08:00
Cp0204
abc5ef4e40 🩹 添加运行脚本环境变量设置
- 确保输出编码为utf-8,避免编码问题
2024-11-12 11:39:22 +08:00
4 changed files with 153 additions and 102 deletions

View File

@ -179,12 +179,18 @@ def run_script_now():
)
def generate_output():
# 设置环境变量
process_env = os.environ.copy()
process_env["PYTHONIOENCODING"] = "utf-8"
process = subprocess.Popen(
command,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
universal_newlines=True,
encoding="utf-8",
errors="replace",
bufsize=1,
env=process_env,
)
try:
for line in iter(process.stdout.readline, ""):

View File

@ -82,19 +82,24 @@
<div class="row title">
<div class="col">
<h2>Emby</h2>
<h2>媒体库</h2>
</div>
</div>
<div class="form-group row">
<label class="col-sm-2 col-form-label">Emby URL</label>
<div class="col-sm-10">
<input type="text" v-model="formData.emby.url" class="form-control" placeholder="可选">
<div v-for="(server, serverName) in formData.media_servers" :key="serverName" class="task mb-3">
<div class="form-group row" style="display:flex; align-items:center">
<div class="col-9" data-toggle="collapse" :data-target="'#collapse_'+serverName" aria-expanded="true" :aria-controls="'collapse_'+serverName">
<div class="btn btn-block text-left">
<i class="bi bi-caret-right-fill"></i> <span v-html="`${serverName}`"></span>
</div>
</div>
</div>
</div>
<div class="form-group row">
<label class="col-sm-2 col-form-label">Emby API Key</label>
<div class="col-sm-10">
<input type="text" v-model="formData.emby.apikey" class="form-control" placeholder="可选">
<div class="collapse" :id="'collapse_'+serverName" style="padding-left:2em">
<div v-for="(value, key) in server" :key="key" class="form-group row">
<label class="col-sm-2 col-form-label">{{ key }}</label>
<div class="col-sm-10">
<input type="text" v-model="formData.media_servers[serverName][key]" class="form-control" :placeholder="key === 'url' ? 'URL' : 'API Key/Token'">
</div>
</div>
</div>
</div>
@ -256,9 +261,9 @@
</div>
</div>
<div class="form-group row">
<label class="col-sm-2 col-form-label">Emby ID</label>
<label class="col-sm-2 col-form-label">媒体库ID</label>
<div class="col-sm-10">
<input type="number" name="emby_id[]" class="form-control" v-model="task.emby_id" placeholder="可选">
<input type="number" name="media_id[]" class="form-control" v-model="task.media_id" placeholder="可选">
</div>
</div>
</div>
@ -365,10 +370,7 @@
formData: {
cookie: [],
push_config: {},
emby: {
url: "",
apikey: ""
},
media_servers: {},
tasklist: [],
magic_regex: {}
},
@ -379,7 +381,7 @@
pattern: "",
replace: "",
enddate: "",
emby_id: "",
media_id: "",
ignore_extension: false,
runweek: [1, 2, 3, 4, 5, 6, 7]
},

83
media_servers/emby.py Normal file
View File

@ -0,0 +1,83 @@
import requests
class Emby:
default_config = {"url": "", "apikey": ""}
def __init__(self, **kwargs):
self.is_active = False
if kwargs:
for key, value in self.default_config.items():
if key in kwargs:
setattr(self, key, kwargs[key])
else:
print(f"{self.__class__.__name__} 模块缺少必要参数: {key}")
if self.url and self.apikey:
if self.get_info():
self.is_active = True
def get_info(self):
url = f"{self.url}/emby/System/Info"
headers = {"X-Emby-Token": self.apikey}
querystring = {}
response = requests.request("GET", url, headers=headers, params=querystring)
if "application/json" in response.headers["Content-Type"]:
response = response.json()
print(
f"Emby媒体库: {response.get('ServerName','')} v{response.get('Version','')}"
)
return True
else:
print(f"Emby媒体库: 连接失败❌ {response.text}")
return False
def refresh(self, emby_id):
if emby_id:
url = f"{self.url}/emby/Items/{emby_id}/Refresh"
headers = {"X-Emby-Token": self.apikey}
querystring = {
"Recursive": "true",
"MetadataRefreshMode": "FullRefresh",
"ImageRefreshMode": "FullRefresh",
"ReplaceAllMetadata": "false",
"ReplaceAllImages": "false",
}
response = requests.request(
"POST", url, headers=headers, params=querystring
)
if response.text == "":
print(f"🎞 刷新Emby媒体库成功✅")
return True
else:
print(f"🎞 刷新Emby媒体库{response.text}")
return False
def search(self, media_name):
if media_name:
url = f"{self.url}/emby/Items"
headers = {"X-Emby-Token": self.apikey}
querystring = {
"IncludeItemTypes": "Series",
"StartIndex": 0,
"SortBy": "SortName",
"SortOrder": "Ascending",
"ImageTypeLimit": 0,
"Recursive": "true",
"SearchTerm": media_name,
"Limit": 10,
"IncludeSearchTypes": "false",
}
response = requests.request("GET", url, headers=headers, params=querystring)
if "application/json" in response.headers["Content-Type"]:
response = response.json()
if response.get("Items"):
for item in response["Items"]:
if item["IsFolder"]:
print(
f"🎞 《{item['Name']}》匹配到Emby媒体库ID{item['Id']}"
)
return item["Id"]
else:
print(f"🎞 搜索Emby媒体库{response.text}")
return False

View File

@ -1,6 +1,6 @@
# !/usr/bin/env python3
# -*- coding: utf-8 -*-
# Modify: 2024-04-03
# Modify: 2024-11-13
# Repo: https://github.com/Cp0204/quark_auto_save
# ConfigFile: quark_config.json
"""
@ -14,6 +14,7 @@ import json
import time
import random
import requests
import importlib
from datetime import datetime
# 兼容青龙
@ -686,79 +687,24 @@ class Quark:
return is_rename_count > 0
class Emby:
def __init__(self, emby_url, emby_apikey):
self.is_active = False
if emby_url and emby_apikey:
self.emby_url = emby_url
self.emby_apikey = emby_apikey
if self.get_info():
self.is_active = True
def get_info(self):
url = f"{self.emby_url}/emby/System/Info"
headers = {"X-Emby-Token": self.emby_apikey}
querystring = {}
response = requests.request("GET", url, headers=headers, params=querystring)
if "application/json" in response.headers["Content-Type"]:
response = response.json()
print(
f"Emby媒体库: {response.get('ServerName','')} v{response.get('Version','')}"
)
return True
else:
print(f"Emby媒体库: 连接失败❌ {response.text}")
return False
def refresh(self, emby_id):
if emby_id:
url = f"{self.emby_url}/emby/Items/{emby_id}/Refresh"
headers = {"X-Emby-Token": self.emby_apikey}
querystring = {
"Recursive": "true",
"MetadataRefreshMode": "FullRefresh",
"ImageRefreshMode": "FullRefresh",
"ReplaceAllMetadata": "false",
"ReplaceAllImages": "false",
}
response = requests.request(
"POST", url, headers=headers, params=querystring
)
if response.text == "":
print(f"🎞 刷新Emby媒体库成功✅")
return True
def load_media_servers(media_servers_config, media_servers_dir="media_servers"):
media_servers = {}
available_modules = [
f.replace(".py", "") for f in os.listdir(media_servers_dir) if f.endswith(".py")
]
for module_name in available_modules:
try:
module = importlib.import_module(f"{media_servers_dir}.{module_name}")
ServerClass = getattr(module, module_name.capitalize())
# 检查配置中是否存在该模块的配置
if module_name in media_servers_config:
server_config = media_servers_config[module_name]
media_servers[module_name] = ServerClass(**server_config)
else:
print(f"🎞 刷新Emby媒体库{response.text}")
return False
def search(self, media_name):
if media_name:
url = f"{self.emby_url}/emby/Items"
headers = {"X-Emby-Token": self.emby_apikey}
querystring = {
"IncludeItemTypes": "Series",
"StartIndex": 0,
"SortBy": "SortName",
"SortOrder": "Ascending",
"ImageTypeLimit": 0,
"Recursive": "true",
"SearchTerm": media_name,
"Limit": 10,
"IncludeSearchTypes": "false",
}
response = requests.request("GET", url, headers=headers, params=querystring)
if "application/json" in response.headers["Content-Type"]:
response = response.json()
if response.get("Items"):
for item in response["Items"]:
if item["IsFolder"]:
print(
f"🎞 《{item['Name']}》匹配到Emby媒体库ID{item['Id']}"
)
return item["Id"]
else:
print(f"🎞 搜索Emby媒体库{response.text}")
return False
media_servers_config[module_name] = ServerClass().default_config
except (ImportError, AttributeError):
print(f"加载模块 {module_name} 失败")
return media_servers
def verify_account(account):
@ -818,10 +764,7 @@ def do_sign(account):
def do_save(account, tasklist=[]):
emby = Emby(
CONFIG_DATA.get("emby", {}).get("url", ""),
CONFIG_DATA.get("emby", {}).get("apikey", ""),
)
media_servers = load_media_servers(CONFIG_DATA.get("media_servers", {}))
print(f"转存账号: {account.nickname}")
# 获取全部保存目录fid
account.update_savepath_fid(tasklist)
@ -862,17 +805,33 @@ def do_save(account, tasklist=[]):
is_new = account.do_save_task(task)
is_rename = account.do_rename_task(task)
# 刷新媒体库
if emby.is_active and (is_new or is_rename) and task.get("emby_id") != "0":
if task.get("emby_id"):
emby.refresh(task["emby_id"])
else:
match_emby_id = emby.search(task["taskname"])
if match_emby_id:
task["emby_id"] = match_emby_id
emby.refresh(match_emby_id)
for server_name, media_server in media_servers.items():
if (
media_server.is_active
and (is_new or is_rename)
and task.get("media_id") != "0"
):
if task.get("media_id"):
media_server.refresh(task["media_id"])
else:
match_media_id = media_server.search(task["taskname"])
if match_media_id:
task["media_id"] = match_media_id
media_server.refresh(match_media_id)
print()
def reaking_change_update():
global CONFIG_DATA
# print("Update config v0.3.6.1 to 0.3.7")
if CONFIG_DATA.get("emby"):
CONFIG_DATA.setdefault("media_servers", {})["emby"] = CONFIG_DATA["emby"]
del CONFIG_DATA["emby"]
for task in CONFIG_DATA.get("tasklist", {}):
task["media_id"] = task.get("emby_id", "")
del task["emby_id"]
def main():
global CONFIG_DATA
start_time = datetime.now()
@ -900,6 +859,7 @@ def main():
print(f"⚙️ 正从 {config_path} 文件中读取配置")
with open(config_path, "r", encoding="utf-8") as file:
CONFIG_DATA = json.load(file)
reaking_change_update()
cookie_val = CONFIG_DATA.get("cookie")
if not CONFIG_DATA.get("magic_regex"):
CONFIG_DATA["magic_regex"] = MAGIC_REGEX