mirror of
https://github.com/Cp0204/quark-auto-save.git
synced 2026-01-11 22:50:45 +08:00
Compare commits
9 Commits
80307a1540
...
4e45e37412
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4e45e37412 | ||
|
|
280f0ef060 | ||
|
|
63230d5c2b | ||
|
|
c9c83cb65a | ||
|
|
c3c4ad6c00 | ||
|
|
9c5ade608e | ||
|
|
9da63f12c7 | ||
|
|
2427a6d26b | ||
|
|
a0681e5e44 |
12
README.md
12
README.md
@ -40,7 +40,7 @@
|
||||
- 分享链接
|
||||
- [x] 支持分享链接的子目录
|
||||
- [x] 记录失效分享并跳过任务
|
||||
- [x] 支持需提取码的分享链接 <sup>[?](https://github.com/Cp0204/quark-auto-save/wiki/%E4%BD%BF%E7%94%A8%E6%8A%80%E5%B7%A7%E9%9B%86%E9%94%A6#%E6%94%AF%E6%8C%81%E9%9C%80%E6%8F%90%E5%8F%96%E7%A0%81%E7%9A%84%E5%88%86%E4%BA%AB%E9%93%BE%E6%8E%A5)</sup>
|
||||
- [x] 支持需提取码的分享链接 <sup>[?](https://github.com/Cp0204/quark-auto-save/wiki/使用技巧集锦#支持需提取码的分享链接)</sup>
|
||||
|
||||
- 文件管理
|
||||
- [x] 目标目录不存在时自动新建
|
||||
@ -57,11 +57,11 @@
|
||||
- 媒体库整合
|
||||
- [x] 根据任务名搜索 Emby 媒体库
|
||||
- [x] 追更或整理后自动刷新 Emby 媒体库
|
||||
- [x] **媒体库模块化,用户可很方便地[开发自己的媒体库hook模块](./media_servers)**
|
||||
- [x] **媒体库模块化,用户可很方便地[开发自己的媒体库hook模块](./plugins)**
|
||||
|
||||
- 其它
|
||||
- [x] 每日签到领空间 <sup>[?](https://github.com/Cp0204/quark-auto-save/wiki/%E4%BD%BF%E7%94%A8%E6%8A%80%E5%B7%A7%E9%9B%86%E9%94%A6#%E6%AF%8F%E6%97%A5%E7%AD%BE%E5%88%B0%E9%A2%86%E7%A9%BA%E9%97%B4)</sup>
|
||||
- [x] 支持多个通知推送渠道 <sup>[?](https://github.com/Cp0204/quark-auto-save/wiki/%E9%80%9A%E7%9F%A5%E6%8E%A8%E9%80%81%E6%9C%8D%E5%8A%A1%E9%85%8D%E7%BD%AE)</sup>
|
||||
- [x] 每日签到领空间 <sup>[?](https://github.com/Cp0204/quark-auto-save/wiki/使用技巧集锦#每日签到领空间)</sup>
|
||||
- [x] 支持多个通知推送渠道 <sup>[?](https://github.com/Cp0204/quark-auto-save/wiki/通知推送服务配置)</sup>
|
||||
- [x] 支持多账号(多账号签到,仅首账号转存)
|
||||
|
||||
## 部署
|
||||
@ -155,9 +155,9 @@ docker run --rm -v /var/run/docker.sock:/var/run/docker.sock containrrr/watchtow
|
||||
|
||||
### 刷新媒体库
|
||||
|
||||
在有新转存时,可触发完成相应功能,如自动刷新媒体库、生成 .strm 文件等。配置指南:[媒体库模块配置](https://github.com/Cp0204/quark-auto-save/wiki/媒体库模块配置)
|
||||
在有新转存时,可触发完成相应功能,如自动刷新媒体库、生成 .strm 文件等。配置指南:[插件配置](https://github.com/Cp0204/quark-auto-save/wiki/插件配置)
|
||||
|
||||
媒体库模块以模块化方式的集成,如果你有兴趣请参考[媒体库模块开发指南](https://github.com/Cp0204/quark-auto-save/tree/main/media_servers)。
|
||||
媒体库模块以插件的方式的集成,如果你有兴趣请参考[插件开发指南](https://github.com/Cp0204/quark-auto-save/tree/main/plugins)。
|
||||
|
||||
### 更多使用技巧
|
||||
|
||||
|
||||
32
app/static/js/v-jsoneditor.min.js
vendored
Normal file
32
app/static/js/v-jsoneditor.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@ -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>
|
||||
<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>
|
||||
@ -264,9 +264,10 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-2 col-form-label">媒体库ID</label>
|
||||
<label class="col-sm-2 col-form-label">插件配置</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="number" name="media_id[]" class="form-control" v-model="task.media_id" 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="180px"></v-jsoneditor>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -362,6 +363,7 @@
|
||||
<!-- 引入 Vue.js -->
|
||||
<script src="./static/js/vue@2.js"></script>
|
||||
<script src="./static/js/axios.min.js"></script>
|
||||
<script src="./static/js/v-jsoneditor.min.js"></script>
|
||||
|
||||
<script>
|
||||
var app = new Vue({
|
||||
@ -384,7 +386,7 @@
|
||||
pattern: "",
|
||||
replace: "",
|
||||
enddate: "",
|
||||
media_id: "",
|
||||
addition: {},
|
||||
ignore_extension: false,
|
||||
runweek: [1, 2, 3, 4, 5, 6, 7]
|
||||
},
|
||||
@ -460,16 +462,13 @@
|
||||
}
|
||||
return task;
|
||||
});
|
||||
// 添加emby预设
|
||||
if (!response.data.hasOwnProperty('emby')) {
|
||||
response.data.emby = { ...this.emby };
|
||||
}
|
||||
// 获取所有任务父目录
|
||||
response.data.tasklist.forEach(item => {
|
||||
parentDir = this.getParentDirectory(item.savepath)
|
||||
if (!this.taskDirs.includes(parentDir))
|
||||
this.taskDirs.push(parentDir);
|
||||
});
|
||||
this.newTask.addition = response.data.task_plugins_config;
|
||||
this.formData = response.data;
|
||||
})
|
||||
.catch(error => {
|
||||
|
||||
@ -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) |
|
||||
@ -2,6 +2,7 @@
|
||||
"alist",
|
||||
"alist_strm",
|
||||
"alist_strm_gen",
|
||||
"aria2",
|
||||
"emby",
|
||||
"plex"
|
||||
]
|
||||
@ -30,7 +30,7 @@ class Alist:
|
||||
self.storage_mount_path, self.quark_root_dir = result
|
||||
self.is_active = True
|
||||
|
||||
def run(self, task):
|
||||
def run(self, task, **kwargs):
|
||||
if task.get("savepath") and task.get("savepath").startswith(
|
||||
self.quark_root_dir
|
||||
):
|
||||
@ -12,7 +12,7 @@ class Alist_strm:
|
||||
default_config = {
|
||||
"url": "", # alist-strm服务器URL
|
||||
"cookie": "", # alist-strm的cookie,F12抓取,关键参数:session=ey***
|
||||
"config_id": "", # 要触发运行的配置ID
|
||||
"config_id": "", # 要触发运行的配置ID,支持多个,用逗号分隔
|
||||
}
|
||||
is_active = False
|
||||
|
||||
@ -27,7 +27,7 @@ class Alist_strm:
|
||||
if self.get_info(self.config_id):
|
||||
self.is_active = True
|
||||
|
||||
def run(self, task):
|
||||
def run(self, task, **kwargs):
|
||||
self.run_selected_configs(self.config_id)
|
||||
|
||||
def get_info(self, config_id_str):
|
||||
@ -24,6 +24,9 @@ class Alist_strm_gen:
|
||||
"strm_save_dir": "/media", # 生成的 strm 文件保存的路径
|
||||
"strm_replace_host": "", # strm 文件内链接的主机地址 (可选,缺省时=url)
|
||||
}
|
||||
default_task_config = {
|
||||
"auto_gen": True, # 是否自动生成 strm 文件
|
||||
}
|
||||
is_active = False
|
||||
# 缓存参数
|
||||
storage_mount_path = None
|
||||
@ -31,12 +34,13 @@ class Alist_strm_gen:
|
||||
strm_server = None
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self.plugin_name = self.__class__.__name__.lower()
|
||||
if kwargs:
|
||||
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 and self.storage_id:
|
||||
success, result = self.storage_id_to_path(self.storage_id)
|
||||
if success:
|
||||
@ -53,7 +57,10 @@ class Alist_strm_gen:
|
||||
else:
|
||||
self.strm_server = f"{self.url.strip()}/d"
|
||||
|
||||
def run(self, task):
|
||||
def run(self, task, **kwargs):
|
||||
if task_config := task.get("addition", {}).get(self.plugin_name, {}):
|
||||
if not task_config.get("auto_gen"):
|
||||
return
|
||||
if task.get("savepath") and task.get("savepath").startswith(
|
||||
self.quark_root_dir
|
||||
):
|
||||
90
plugins/aria2.py
Normal file
90
plugins/aria2.py
Normal file
@ -0,0 +1,90 @@
|
||||
import os
|
||||
import requests
|
||||
|
||||
|
||||
class Aria2:
|
||||
|
||||
default_config = {
|
||||
"host_port": "172.17.0.1:6800", # Aria2 RPC地址
|
||||
"secret": "", # Aria2 RPC 密钥
|
||||
"dir": "/Downloads", # 下载目录,需要Aria2有权限访问
|
||||
}
|
||||
default_task_config = {
|
||||
"auto_download": False, # 是否开启自动下载
|
||||
}
|
||||
is_active = False
|
||||
rpc_url = None
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self.plugin_name = self.__class__.__name__.lower()
|
||||
if kwargs:
|
||||
for key, _ in self.default_config.items():
|
||||
if key in kwargs:
|
||||
setattr(self, key, kwargs[key])
|
||||
else:
|
||||
print(f"{self.plugin_name} 模块缺少必要参数: {key}")
|
||||
if self.host_port and self.secret:
|
||||
self.rpc_url = f"http://{self.host_port}/jsonrpc"
|
||||
if self.get_version():
|
||||
self.is_active = True
|
||||
|
||||
def run(self, task, **kwargs):
|
||||
if task_config := task.get("addition", {}).get(self.plugin_name, {}):
|
||||
if not task_config.get("auto_download"):
|
||||
return
|
||||
if (tree := kwargs.get("tree")) and (account := kwargs.get("account")):
|
||||
for node in tree.all_nodes_itr():
|
||||
if not node.data.get("is_dir", True):
|
||||
quark_path = node.data.get("path")
|
||||
quark_fid = node.data.get("fid")
|
||||
save_path = f"{self.dir}{quark_path}"
|
||||
print(f"📥 Aria2下载: {quark_path}")
|
||||
download_return, cookie = account.download([quark_fid])
|
||||
download_url = [
|
||||
item["download_url"] for item in download_return["data"]
|
||||
]
|
||||
aria2_params = [
|
||||
download_url,
|
||||
{
|
||||
"header": [
|
||||
f"Cookie: {cookie}",
|
||||
f"User-Agent: {account.common_headers().get('user-agent')}",
|
||||
],
|
||||
"out": os.path.basename(save_path),
|
||||
"dir": os.path.dirname(save_path),
|
||||
},
|
||||
]
|
||||
self.add_uri(aria2_params)
|
||||
|
||||
def _make_rpc_request(self, method, params=None):
|
||||
"""发出 JSON-RPC 请求."""
|
||||
jsonrpc_data = {
|
||||
"jsonrpc": "2.0",
|
||||
"id": "quark-auto-save",
|
||||
"method": method,
|
||||
"params": params or [],
|
||||
}
|
||||
if self.secret:
|
||||
jsonrpc_data["params"].insert(0, f"token:{self.secret}")
|
||||
try:
|
||||
response = requests.post(self.rpc_url, json=jsonrpc_data)
|
||||
response.raise_for_status()
|
||||
return response.json()
|
||||
except requests.exceptions.RequestException as e:
|
||||
print(f"Aria2下载: 错误{e}")
|
||||
return None
|
||||
|
||||
def get_version(self):
|
||||
"""检查与 Aria2 的连接."""
|
||||
response = self._make_rpc_request("aria2.getVersion")
|
||||
if response.get("result"):
|
||||
print(f"Aria2下载: v{response['result']['version']}")
|
||||
return True
|
||||
else:
|
||||
print(f"Aria2下载: 连接失败{response.get('error')}")
|
||||
return False
|
||||
|
||||
def add_uri(self, params=None):
|
||||
"""添加 URI 下载任务."""
|
||||
response = self._make_rpc_request("aria2.addUri", params)
|
||||
return response.get("result") if response else None
|
||||
@ -3,30 +3,38 @@ import requests
|
||||
|
||||
class Emby:
|
||||
|
||||
default_config = {"url": "", "token": ""}
|
||||
default_config = {
|
||||
"url": "", # Emby服务器地址
|
||||
"token": "", # Emby服务器token
|
||||
}
|
||||
default_task_config = {
|
||||
"try_match": True, # 是否尝试匹配
|
||||
"media_id": "", # 媒体ID,当为0时不刷新
|
||||
}
|
||||
is_active = False
|
||||
|
||||
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
|
||||
|
||||
def run(self, task):
|
||||
if task.get("media_id"):
|
||||
if task["media_id"] != "0":
|
||||
self.refresh(task["media_id"])
|
||||
else:
|
||||
match_media_id = self.search(task["taskname"])
|
||||
if match_media_id:
|
||||
task["media_id"] = match_media_id
|
||||
self.refresh(match_media_id)
|
||||
return task
|
||||
def run(self, task, **kwargs):
|
||||
if task_config := task.get("addition", {}).get(self.plugin_name, {}):
|
||||
if media_id := task_config.get("media_id"):
|
||||
if media_id != "0":
|
||||
self.refresh(media_id)
|
||||
elif task_config.get("try_match"):
|
||||
if match_media_id := self.search(task["taskname"]):
|
||||
self.refresh(match_media_id)
|
||||
task["addition"][self.plugin_name]["media_id"] = match_media_id
|
||||
return task
|
||||
|
||||
def get_info(self):
|
||||
url = f"{self.url}/emby/System/Info"
|
||||
@ -23,7 +23,7 @@ class Plex:
|
||||
if self.get_info():
|
||||
self.is_active = True
|
||||
|
||||
def run(self, task):
|
||||
def run(self, task, **kwargs):
|
||||
if task.get("savepath"):
|
||||
# 检查是否已缓存库信息
|
||||
if self._libraries is None:
|
||||
@ -125,6 +125,7 @@ class Quark:
|
||||
headers = {
|
||||
"cookie": self.cookie,
|
||||
"content-type": "application/json",
|
||||
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) quark-cloud-drive/3.14.2 Chrome/112.0.5615.165 Electron/24.1.3.8 Safari/537.36 Channel/pckk_other_ch",
|
||||
}
|
||||
return headers
|
||||
|
||||
@ -329,6 +330,16 @@ class Quark:
|
||||
).json()
|
||||
return response
|
||||
|
||||
def download(self, fids):
|
||||
url = "https://drive-h.quark.cn/1/clouddrive/file/download"
|
||||
querystring = {"pr": "ucpro", "fr": "pc", "uc_param_str": ""}
|
||||
payload = {"fids": fids}
|
||||
headers = self.common_headers()
|
||||
response = requests.post(url, json=payload, headers=headers, params=querystring)
|
||||
set_cookie = response.cookies.get_dict()
|
||||
cookie_str = "; ".join([f"{key}={value}" for key, value in set_cookie.items()])
|
||||
return response.json(), cookie_str
|
||||
|
||||
def mkdir(self, dir_path):
|
||||
url = "https://drive-h.quark.cn/1/clouddrive/file"
|
||||
querystring = {"pr": "ucpro", "fr": "pc", "uc_param_str": ""}
|
||||
@ -487,14 +498,13 @@ class Quark:
|
||||
updated_tree = self.dir_check_and_save(task, pwd_id, stoken, pdir_fid)
|
||||
if updated_tree.size(1) > 0:
|
||||
add_notify(f"✅《{task['taskname']}》添加追更:\n{updated_tree}")
|
||||
return True
|
||||
return updated_tree
|
||||
else:
|
||||
print(f"任务结束:没有新的转存任务")
|
||||
return False
|
||||
|
||||
def dir_check_and_save(self, task, pwd_id, stoken, pdir_fid="", subdir_path=""):
|
||||
tree = Tree()
|
||||
tree.create_node(task["savepath"], pdir_fid)
|
||||
# 获取分享文件列表
|
||||
share_file_list = self.get_detail(pwd_id, stoken, pdir_fid)["list"]
|
||||
# print("share_file_list: ", share_file_list)
|
||||
@ -526,6 +536,14 @@ class Quark:
|
||||
dir_file_list = self.ls_dir(to_pdir_fid)
|
||||
# print("dir_file_list: ", dir_file_list)
|
||||
|
||||
tree.create_node(
|
||||
savepath,
|
||||
pdir_fid,
|
||||
data={
|
||||
"is_dir": True,
|
||||
},
|
||||
)
|
||||
|
||||
# 需保存的文件清单
|
||||
need_save_list = []
|
||||
# 添加符合的
|
||||
@ -580,6 +598,9 @@ class Quark:
|
||||
"📁" + share_file["file_name"],
|
||||
share_file["fid"],
|
||||
parent=pdir_fid,
|
||||
data={
|
||||
"is_dir": share_file["dir"],
|
||||
},
|
||||
)
|
||||
tree.merge(share_file["fid"], subdir_tree, deep=False)
|
||||
# 指定文件开始订阅/到达指定文件(含)结束历遍
|
||||
@ -588,7 +609,6 @@ class Quark:
|
||||
|
||||
fid_list = [item["fid"] for item in need_save_list]
|
||||
fid_token_list = [item["share_fid_token"] for item in need_save_list]
|
||||
save_name_list = [item["save_name"] for item in need_save_list]
|
||||
if fid_list:
|
||||
save_file_return = self.save_file(
|
||||
fid_list, fid_token_list, to_pdir_fid, pwd_id, stoken
|
||||
@ -598,16 +618,22 @@ class Quark:
|
||||
task_id = save_file_return["data"]["task_id"]
|
||||
query_task_return = self.query_task(task_id)
|
||||
if query_task_return["code"] == 0:
|
||||
save_name_list.sort()
|
||||
# 建立目录树
|
||||
for item in need_save_list:
|
||||
for index, item in enumerate(need_save_list):
|
||||
icon = (
|
||||
"📁"
|
||||
if item["dir"] == True
|
||||
else "🎞️" if item["obj_category"] == "video" else ""
|
||||
)
|
||||
tree.create_node(
|
||||
f"{icon}{item['save_name']}", item["fid"], parent=pdir_fid
|
||||
f"{icon}{item['save_name']}",
|
||||
item["fid"],
|
||||
parent=pdir_fid,
|
||||
data={
|
||||
"fid": f"{query_task_return['data']['save_as']['save_as_top_fids'][index]}",
|
||||
"path": f"{savepath}/{item['save_name']}",
|
||||
"is_dir": item["dir"],
|
||||
},
|
||||
)
|
||||
else:
|
||||
err_msg = query_task_return["message"]
|
||||
@ -688,13 +714,14 @@ 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_available = {}
|
||||
task_plugins_config = {}
|
||||
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)
|
||||
@ -704,21 +731,25 @@ 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:
|
||||
plugin = ServerClass(**plugins_config[module_name])
|
||||
plugins_available[module_name] = plugin
|
||||
else:
|
||||
media_servers_config[module_name] = ServerClass().default_config
|
||||
plugin = ServerClass()
|
||||
plugins_config[module_name] = plugin.default_config
|
||||
# 检查插件是否支持单独任务配置
|
||||
if hasattr(plugin, "default_task_config"):
|
||||
task_plugins_config[module_name] = plugin.default_task_config
|
||||
except (ImportError, AttributeError) as e:
|
||||
print(f"载入模块 {module_name} 失败: {e}")
|
||||
print()
|
||||
return media_servers
|
||||
return plugins_available, task_plugins_config
|
||||
|
||||
|
||||
def verify_account(account):
|
||||
@ -778,7 +809,9 @@ def do_sign(account):
|
||||
|
||||
|
||||
def do_save(account, tasklist=[]):
|
||||
media_servers = load_media_servers(CONFIG_DATA.get("media_servers", {}))
|
||||
plugins, CONFIG_DATA["task_plugins_config"] = load_plugins(
|
||||
CONFIG_DATA.get("plugins", {})
|
||||
)
|
||||
print(f"转存账号: {account.nickname}")
|
||||
# 获取全部保存目录fid
|
||||
account.update_savepath_fid(tasklist)
|
||||
@ -804,33 +837,48 @@ def do_save(account, tasklist=[]):
|
||||
print(f"#{index+1}------------------")
|
||||
print(f"任务名称: {task['taskname']}")
|
||||
print(f"分享链接: {task['shareurl']}")
|
||||
print(f"目标目录: {task['savepath']}")
|
||||
print(f"保存路径: {task['savepath']}")
|
||||
print(f"正则匹配: {task['pattern']}")
|
||||
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)
|
||||
# 调用媒体库模块
|
||||
if is_new or is_rename:
|
||||
print(f"🧩 调用媒体库模块")
|
||||
for server_name, media_server in media_servers.items():
|
||||
if media_server.is_active:
|
||||
task = media_server.run(task) or task
|
||||
|
||||
# 补充任务的插件配置
|
||||
def merge_dicts(a, b):
|
||||
result = a.copy()
|
||||
for key, value in b.items():
|
||||
if (
|
||||
key in result
|
||||
and isinstance(result[key], dict)
|
||||
and isinstance(value, dict)
|
||||
):
|
||||
result[key] = merge_dicts(result[key], value)
|
||||
elif key not in result:
|
||||
result[key] = value
|
||||
return result
|
||||
|
||||
task["addition"] = merge_dicts(
|
||||
task.get("addition", {}), CONFIG_DATA["task_plugins_config"]
|
||||
)
|
||||
# 调用插件
|
||||
print(f"🧩 调用插件")
|
||||
for plugin_name, plugin in plugins.items():
|
||||
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"):
|
||||
print("🔼 Update config v0.3.6.1 to 0.3.7")
|
||||
CONFIG_DATA.setdefault("media_servers", {})["emby"] = {
|
||||
"url": CONFIG_DATA["emby"]["url"],
|
||||
"token": CONFIG_DATA["emby"]["apikey"],
|
||||
@ -840,6 +888,18 @@ def reaking_change_update():
|
||||
task["media_id"] = task.get("emby_id", "")
|
||||
if task.get("emby_id"):
|
||||
del task["emby_id"]
|
||||
if CONFIG_DATA.get("media_servers"):
|
||||
print("🔼 Update config v0.3.8 to 0.3.9")
|
||||
CONFIG_DATA["plugins"] = CONFIG_DATA.get("media_servers")
|
||||
del CONFIG_DATA["media_servers"]
|
||||
for task in CONFIG_DATA.get("tasklist", {}):
|
||||
task["addition"] = {
|
||||
"emby": {
|
||||
"media_id": task.get("media_id", ""),
|
||||
}
|
||||
}
|
||||
if task.get("media_id"):
|
||||
del task["media_id"]
|
||||
|
||||
|
||||
def main():
|
||||
@ -869,7 +929,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
|
||||
|
||||
@ -20,7 +20,6 @@
|
||||
"pattern": "$TV",
|
||||
"replace": "",
|
||||
"enddate": "2099-01-30",
|
||||
"media_id": "",
|
||||
"update_subdir": "4k|1080p"
|
||||
},
|
||||
{
|
||||
|
||||
Loading…
Reference in New Issue
Block a user