mirror of
https://github.com/Cp0204/quark-auto-save.git
synced 2026-01-13 07:40:45 +08:00
✨ 媒体库模块 改称为 插件
- 媒体库模块改称为插件,更好地反映功能 - 更新相关文档和代码中的所有引用 - 修改变量名以反映插件的概念 - 确保代码一致性和可读性
This commit is contained in:
parent
9c5ade608e
commit
c3c4ad6c00
@ -82,25 +82,25 @@
|
||||
|
||||
<div class="row title">
|
||||
<div class="col">
|
||||
<h2 style="display: inline-block;">媒体库</h2>
|
||||
<h2 style="display: inline-block;">插件</h2>
|
||||
<span class="badge badge-pill badge-light">
|
||||
<a href="https://github.com/Cp0204/quark-auto-save/wiki/媒体库模块配置" target="_blank" title="媒体库模块配置">?</a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-for="(server, serverName) in formData.media_servers" :key="serverName" class="task mb-3">
|
||||
<div v-for="(plugin, pluginName) in formData.plugins" :key="pluginName" 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="col-9" data-toggle="collapse" :data-target="'#collapse_'+pluginName" aria-expanded="true" :aria-controls="'collapse_'+pluginName">
|
||||
<div class="btn btn-block text-left">
|
||||
<i class="bi bi-caret-right-fill"></i> <span v-html="`${serverName}`"></span>
|
||||
<i class="bi bi-caret-right-fill"></i> <span v-html="`${pluginName}`"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="collapse" :id="'collapse_'+serverName" style="padding-left:2em">
|
||||
<div v-for="(value, key) in server" :key="key" class="form-group row">
|
||||
<div class="collapse" :id="'collapse_'+pluginName" style="padding-left:2em">
|
||||
<div v-for="(value, key) in plugin" :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">
|
||||
<input type="text" v-model="formData.plugins[pluginName][key]" class="form-control">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -267,7 +267,7 @@
|
||||
<label class="col-sm-2 col-form-label">插件配置</label>
|
||||
<div class="col-sm-10">
|
||||
<!-- <input type="text" name="addition[]" class="form-control" v-model="task.addition" placeholder="可选"> -->
|
||||
<v-jsoneditor v-model="task.addition" :options="{mode:'tree'}" :plus="false" height="150px"></v-jsoneditor>
|
||||
<v-jsoneditor v-model="task.addition" :options="{mode:'tree'}" :plus="false" height="200px"></v-jsoneditor>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1,21 +1,21 @@
|
||||
# 媒体库模块开发指南
|
||||
# 插件开发指南
|
||||
|
||||
本指南介绍如何开发自定义媒体库模块,你可以通过添加新的媒体库模块来扩展项目功能。
|
||||
本指南介绍如何开发自定义插件,你可以通过添加新的插件来扩展项目功能。
|
||||
|
||||
## 基本结构
|
||||
|
||||
* 模块位于 `media_servers` 目录下.
|
||||
* 每个模块是一个 `.py` 文件 (例如 `emby.py`, `plex.py`),文件名小写。
|
||||
* 每个模块文件包含一个与文件名对应的首字母大写命名类(例如 `emby.py` 中的 `Emby` 类)。
|
||||
* 插件位于 `media_servers` 目录下.
|
||||
* 每个插件是一个 `.py` 文件 (例如 `emby.py`, `plex.py`),文件名小写。
|
||||
* 每个插件文件包含一个与文件名对应的首字母大写命名类(例如 `emby.py` 中的 `Emby` 类)。
|
||||
|
||||
## 模块要求
|
||||
## 插件要求
|
||||
|
||||
每个模块类必须包含以下内容:
|
||||
每个插件类必须包含以下内容:
|
||||
|
||||
* **`default_config`**:字典,包含模块所需参数及其默认值。例如:
|
||||
* **`default_config`**:字典,包含插件所需参数及其默认值。例如:
|
||||
|
||||
```python
|
||||
# 该模块必须配置的键,值可留空
|
||||
# 该插件必须配置的键,值可留空
|
||||
default_config = {"url": "", "token": ""}
|
||||
```
|
||||
|
||||
@ -25,11 +25,11 @@
|
||||
1. 检查 `kwargs` 是否包含所有 `default_config` 中的参数,缺少参数则打印警告。
|
||||
2. 若参数完整,尝试连接服务器并验证配置,成功则设置 `self.is_active = True`。
|
||||
|
||||
* **`run(self, task)`**:整个模块入口函数,处理模块逻辑。
|
||||
* **`run(self, task, **kwargs)`**:整个插件入口函数,处理插件逻辑。
|
||||
* `task` 是一个字典,包含任务信息。如果需要修改任务参数,返回修改后的 `task` 字典;
|
||||
* 无修改则不返回或返回 `None`。
|
||||
|
||||
## 模块示例
|
||||
## 插件示例
|
||||
|
||||
参考 [emby.py](emby.py)
|
||||
|
||||
@ -43,7 +43,7 @@
|
||||
|
||||
### 最佳实践
|
||||
|
||||
requests 部分使用 try-except 块,以防模块请求出错中断整个转存任务。
|
||||
requests 部分使用 try-except 块,以防插件请求出错中断整个转存任务。
|
||||
|
||||
```python
|
||||
try:
|
||||
@ -56,7 +56,7 @@ except requests.exceptions.RequestException as e:
|
||||
return False
|
||||
```
|
||||
|
||||
## 使用自定义模块
|
||||
## 使用自定义插件
|
||||
|
||||
放到 `/media_servers` 目录即可识别,如果你使用 docker 运行:
|
||||
|
||||
@ -67,11 +67,11 @@ docker run -d \
|
||||
# ...
|
||||
```
|
||||
|
||||
如果你有写自定义模块的能力,相信你也知道如何挂载自定义模块,算我啰嗦。🙃
|
||||
如果你有写自定义插件的能力,相信你也知道如何挂载自定义插件,算我啰嗦。🙃
|
||||
|
||||
## 配置文件
|
||||
|
||||
在 `quark_config.json` 的 `media_servers` 中配置模块参数:
|
||||
在 `quark_config.json` 的 `media_servers` 中配置插件参数:
|
||||
|
||||
```json
|
||||
{
|
||||
@ -84,10 +84,11 @@ docker run -d \
|
||||
}
|
||||
```
|
||||
|
||||
当模块代码正确赋值 `default_config` 时,首次运行会自动补充缺失的键。
|
||||
当插件代码正确赋值 `default_config` 时,首次运行会自动补充缺失的键。
|
||||
|
||||
## 🤝 贡献者
|
||||
|
||||
| 模块 | 说明 | 贡献者 |
|
||||
| 插件 | 说明 | 贡献者 |
|
||||
| ------- | -------------------- | --------------------------------------- |
|
||||
| plex.py | 自动刷新 Plex 媒体库 | [zhazhayu](https://github.com/zhazhayu) |
|
||||
| plex.py | 自动刷新 Plex 媒体库 | [zhazhayu](https://github.com/zhazhayu) |
|
||||
| alist_strm_gen.py | 自动生成strm | [xiaoQQya](https://github.com/xiaoQQya) |
|
||||
@ -40,7 +40,7 @@ class Alist_strm_gen:
|
||||
if key in kwargs:
|
||||
setattr(self, key, kwargs[key])
|
||||
else:
|
||||
print(f"{self.__class__.__name__} 模块缺少必要参数: {key}")
|
||||
print(f"{self.plugin_name} 模块缺少必要参数: {key}")
|
||||
if self.url and self.token and self.storage_id:
|
||||
success, result = self.storage_id_to_path(self.storage_id)
|
||||
if success:
|
||||
@ -16,11 +16,11 @@ class Emby:
|
||||
def __init__(self, **kwargs):
|
||||
self.plugin_name = self.__class__.__name__.lower()
|
||||
if kwargs:
|
||||
for key, value in self.default_config.items():
|
||||
for key, _ in self.default_config.items():
|
||||
if key in kwargs:
|
||||
setattr(self, key, kwargs[key])
|
||||
else:
|
||||
print(f"{self.__class__.__name__} 模块缺少必要参数: {key}")
|
||||
print(f"{self.plugin_name} 模块缺少必要参数: {key}")
|
||||
if self.url and self.token:
|
||||
if self.get_info():
|
||||
self.is_active = True
|
||||
@ -714,13 +714,13 @@ class Quark:
|
||||
return is_rename_count > 0
|
||||
|
||||
|
||||
def load_media_servers(media_servers_config, media_servers_dir="media_servers"):
|
||||
media_servers = {}
|
||||
def load_plugins(plugins_config, plugins_dir="plugins"):
|
||||
plugins = {}
|
||||
all_modules = [
|
||||
f.replace(".py", "") for f in os.listdir(media_servers_dir) if f.endswith(".py")
|
||||
f.replace(".py", "") for f in os.listdir(plugins_dir) if f.endswith(".py")
|
||||
]
|
||||
# 调整模块优先级
|
||||
priority_path = os.path.join(media_servers_dir, "_priority.json")
|
||||
priority_path = os.path.join(plugins_dir, "_priority.json")
|
||||
try:
|
||||
with open(priority_path, encoding="utf-8") as f:
|
||||
priority_modules = json.load(f)
|
||||
@ -730,21 +730,21 @@ def load_media_servers(media_servers_config, media_servers_dir="media_servers"):
|
||||
] + [module for module in all_modules if module not in priority_modules]
|
||||
except (FileNotFoundError, json.JSONDecodeError):
|
||||
priority_modules = []
|
||||
print(f"🧩 载入媒体库模块")
|
||||
print(f"🧩 载入插件")
|
||||
for module_name in all_modules:
|
||||
try:
|
||||
module = importlib.import_module(f"{media_servers_dir}.{module_name}")
|
||||
module = importlib.import_module(f"{plugins_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)
|
||||
if module_name in plugins_config:
|
||||
server_config = plugins_config[module_name]
|
||||
plugins[module_name] = ServerClass(**server_config)
|
||||
else:
|
||||
media_servers_config[module_name] = ServerClass().default_config
|
||||
plugins_config[module_name] = ServerClass().default_config
|
||||
except (ImportError, AttributeError) as e:
|
||||
print(f"载入模块 {module_name} 失败: {e}")
|
||||
print()
|
||||
return media_servers
|
||||
return plugins
|
||||
|
||||
|
||||
def verify_account(account):
|
||||
@ -804,7 +804,7 @@ def do_sign(account):
|
||||
|
||||
|
||||
def do_save(account, tasklist=[]):
|
||||
media_servers = load_media_servers(CONFIG_DATA.get("media_servers", {}))
|
||||
plugins = load_plugins(CONFIG_DATA.get("plugins", {}))
|
||||
print(f"转存账号: {account.nickname}")
|
||||
# 获取全部保存目录fid
|
||||
account.update_savepath_fid(tasklist)
|
||||
@ -835,30 +835,28 @@ def do_save(account, tasklist=[]):
|
||||
print(f"正则替换: {task['replace']}")
|
||||
if task.get("enddate"):
|
||||
print(f"任务截止: {task['enddate']}")
|
||||
if task.get("media_id"):
|
||||
print(f"刷媒体库: {task['media_id']}")
|
||||
if task.get("ignore_extension"):
|
||||
print(f"忽略后缀: {task['ignore_extension']}")
|
||||
if task.get("update_subdir"):
|
||||
print(f"更子目录: {task['update_subdir']}")
|
||||
print()
|
||||
is_new = account.do_save_task(task)
|
||||
is_new_tree = account.do_save_task(task)
|
||||
is_rename = account.do_rename_task(task)
|
||||
# 调用媒体库模块
|
||||
print(f"🧩 调用媒体库模块")
|
||||
for server_name, media_server in media_servers.items():
|
||||
if hasattr(media_server, "default_task_config") and not task.get(
|
||||
# 调用插件
|
||||
print(f"🧩 调用插件")
|
||||
for plugin_name, plugin in plugins.items():
|
||||
if hasattr(plugin, "default_task_config") and not task.get(
|
||||
"addition", {}
|
||||
).get(server_name):
|
||||
).get(plugin_name):
|
||||
task.setdefault("addition", {})[
|
||||
server_name
|
||||
] = media_server.default_task_config
|
||||
if media_server.is_active and (is_new or is_rename):
|
||||
task = media_server.run(task, account=account, tree=is_new) or task
|
||||
plugin_name
|
||||
] = plugin.default_task_config
|
||||
if plugin.is_active and (is_new_tree or is_rename):
|
||||
task = plugin.run(task, account=account, tree=is_new_tree) or task
|
||||
print()
|
||||
|
||||
|
||||
def reaking_change_update():
|
||||
def breaking_change_update():
|
||||
global CONFIG_DATA
|
||||
# print("Update config v0.3.6.1 to 0.3.7")
|
||||
if CONFIG_DATA.get("emby"):
|
||||
@ -900,7 +898,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()
|
||||
breaking_change_update()
|
||||
cookie_val = CONFIG_DATA.get("cookie")
|
||||
if not CONFIG_DATA.get("magic_regex"):
|
||||
CONFIG_DATA["magic_regex"] = MAGIC_REGEX
|
||||
|
||||
Loading…
Reference in New Issue
Block a user