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