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(): def generate_output():
# 设置环境变量
process_env = os.environ.copy()
process_env["PYTHONIOENCODING"] = "utf-8"
process = subprocess.Popen( process = subprocess.Popen(
command, command,
stdout=subprocess.PIPE, stdout=subprocess.PIPE,
stderr=subprocess.STDOUT, stderr=subprocess.STDOUT,
universal_newlines=True, universal_newlines=True,
encoding="utf-8",
errors="replace",
bufsize=1, bufsize=1,
env=process_env,
) )
try: try:
for line in iter(process.stdout.readline, ""): for line in iter(process.stdout.readline, ""):

View File

@ -82,19 +82,24 @@
<div class="row title"> <div class="row title">
<div class="col"> <div class="col">
<h2>Emby</h2> <h2>媒体库</h2>
</div> </div>
</div> </div>
<div class="form-group row"> <div v-for="(server, serverName) in formData.media_servers" :key="serverName" class="task mb-3">
<label class="col-sm-2 col-form-label">Emby URL</label> <div class="form-group row" style="display:flex; align-items:center">
<div class="col-sm-10"> <div class="col-9" data-toggle="collapse" :data-target="'#collapse_'+serverName" aria-expanded="true" :aria-controls="'collapse_'+serverName">
<input type="text" v-model="formData.emby.url" class="form-control" placeholder="可选"> <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> <div class="collapse" :id="'collapse_'+serverName" style="padding-left:2em">
<div class="form-group row"> <div v-for="(value, key) in server" :key="key" class="form-group row">
<label class="col-sm-2 col-form-label">Emby API Key</label> <label class="col-sm-2 col-form-label">{{ key }}</label>
<div class="col-sm-10"> <div class="col-sm-10">
<input type="text" v-model="formData.emby.apikey" class="form-control" placeholder="可选"> <input type="text" v-model="formData.media_servers[serverName][key]" class="form-control" :placeholder="key === 'url' ? 'URL' : 'API Key/Token'">
</div>
</div>
</div> </div>
</div> </div>
@ -256,9 +261,9 @@
</div> </div>
</div> </div>
<div class="form-group row"> <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"> <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> </div>
</div> </div>
@ -365,10 +370,7 @@
formData: { formData: {
cookie: [], cookie: [],
push_config: {}, push_config: {},
emby: { media_servers: {},
url: "",
apikey: ""
},
tasklist: [], tasklist: [],
magic_regex: {} magic_regex: {}
}, },
@ -379,7 +381,7 @@
pattern: "", pattern: "",
replace: "", replace: "",
enddate: "", enddate: "",
emby_id: "", media_id: "",
ignore_extension: false, ignore_extension: false,
runweek: [1, 2, 3, 4, 5, 6, 7] 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 # !/usr/bin/env python3
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Modify: 2024-04-03 # Modify: 2024-11-13
# Repo: https://github.com/Cp0204/quark_auto_save # Repo: https://github.com/Cp0204/quark_auto_save
# ConfigFile: quark_config.json # ConfigFile: quark_config.json
""" """
@ -14,6 +14,7 @@ import json
import time import time
import random import random
import requests import requests
import importlib
from datetime import datetime from datetime import datetime
# 兼容青龙 # 兼容青龙
@ -686,79 +687,24 @@ class Quark:
return is_rename_count > 0 return is_rename_count > 0
class Emby: def load_media_servers(media_servers_config, media_servers_dir="media_servers"):
def __init__(self, emby_url, emby_apikey): media_servers = {}
self.is_active = False available_modules = [
if emby_url and emby_apikey: f.replace(".py", "") for f in os.listdir(media_servers_dir) if f.endswith(".py")
self.emby_url = emby_url ]
self.emby_apikey = emby_apikey for module_name in available_modules:
if self.get_info(): try:
self.is_active = True module = importlib.import_module(f"{media_servers_dir}.{module_name}")
ServerClass = getattr(module, module_name.capitalize())
def get_info(self): # 检查配置中是否存在该模块的配置
url = f"{self.emby_url}/emby/System/Info" if module_name in media_servers_config:
headers = {"X-Emby-Token": self.emby_apikey} server_config = media_servers_config[module_name]
querystring = {} media_servers[module_name] = ServerClass(**server_config)
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
else: else:
print(f"🎞 刷新Emby媒体库{response.text}") media_servers_config[module_name] = ServerClass().default_config
return False except (ImportError, AttributeError):
print(f"加载模块 {module_name} 失败")
def search(self, media_name): return media_servers
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
def verify_account(account): def verify_account(account):
@ -818,10 +764,7 @@ def do_sign(account):
def do_save(account, tasklist=[]): def do_save(account, tasklist=[]):
emby = Emby( media_servers = load_media_servers(CONFIG_DATA.get("media_servers", {}))
CONFIG_DATA.get("emby", {}).get("url", ""),
CONFIG_DATA.get("emby", {}).get("apikey", ""),
)
print(f"转存账号: {account.nickname}") print(f"转存账号: {account.nickname}")
# 获取全部保存目录fid # 获取全部保存目录fid
account.update_savepath_fid(tasklist) account.update_savepath_fid(tasklist)
@ -862,17 +805,33 @@ def do_save(account, tasklist=[]):
is_new = account.do_save_task(task) is_new = account.do_save_task(task)
is_rename = account.do_rename_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": for server_name, media_server in media_servers.items():
if task.get("emby_id"): if (
emby.refresh(task["emby_id"]) media_server.is_active
else: and (is_new or is_rename)
match_emby_id = emby.search(task["taskname"]) and task.get("media_id") != "0"
if match_emby_id: ):
task["emby_id"] = match_emby_id if task.get("media_id"):
emby.refresh(match_emby_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() 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(): def main():
global CONFIG_DATA global CONFIG_DATA
start_time = datetime.now() start_time = datetime.now()
@ -900,6 +859,7 @@ def main():
print(f"⚙️ 正从 {config_path} 文件中读取配置") print(f"⚙️ 正从 {config_path} 文件中读取配置")
with open(config_path, "r", encoding="utf-8") as file: with open(config_path, "r", encoding="utf-8") as file:
CONFIG_DATA = json.load(file) CONFIG_DATA = json.load(file)
reaking_change_update()
cookie_val = CONFIG_DATA.get("cookie") cookie_val = CONFIG_DATA.get("cookie")
if not CONFIG_DATA.get("magic_regex"): if not CONFIG_DATA.get("magic_regex"):
CONFIG_DATA["magic_regex"] = MAGIC_REGEX CONFIG_DATA["magic_regex"] = MAGIC_REGEX