mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-05-11 18:10:15 +08:00
Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ab73dbb3cd | ||
|
|
cb042dbe68 | ||
|
|
bba0d363d7 | ||
|
|
8635d8c53f | ||
|
|
dae6894e8b | ||
|
|
b76991a027 | ||
|
|
de61c43db4 | ||
|
|
890afc2a72 | ||
|
|
8d4e1f3af6 | ||
|
|
85507a4fff | ||
|
|
6d395f9866 | ||
|
|
c589f42181 | ||
|
|
87bb121060 | ||
|
|
42cd35ab3c | ||
|
|
669da0d882 | ||
|
|
9ac1346f80 |
@@ -444,7 +444,8 @@ class MediaChain(ChainBase, metaclass=Singleton):
|
|||||||
for file in files:
|
for file in files:
|
||||||
self.scrape_metadata(fileitem=file,
|
self.scrape_metadata(fileitem=file,
|
||||||
meta=meta, mediainfo=mediainfo,
|
meta=meta, mediainfo=mediainfo,
|
||||||
init_folder=False, parent=fileitem)
|
init_folder=False, parent=fileitem,
|
||||||
|
overwrite=overwrite)
|
||||||
# 生成目录内图片文件
|
# 生成目录内图片文件
|
||||||
if init_folder:
|
if init_folder:
|
||||||
# 图片
|
# 图片
|
||||||
@@ -515,7 +516,8 @@ class MediaChain(ChainBase, metaclass=Singleton):
|
|||||||
self.scrape_metadata(fileitem=file,
|
self.scrape_metadata(fileitem=file,
|
||||||
meta=meta, mediainfo=mediainfo,
|
meta=meta, mediainfo=mediainfo,
|
||||||
parent=fileitem if file.type == "file" else None,
|
parent=fileitem if file.type == "file" else None,
|
||||||
init_folder=True if file.type == "dir" else False)
|
init_folder=True if file.type == "dir" else False,
|
||||||
|
overwrite=overwrite)
|
||||||
# 生成目录的nfo和图片
|
# 生成目录的nfo和图片
|
||||||
if init_folder:
|
if init_folder:
|
||||||
# 识别文件夹名称
|
# 识别文件夹名称
|
||||||
|
|||||||
@@ -295,6 +295,8 @@ class MessageChain(ChainBase):
|
|||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
best_version = True
|
best_version = True
|
||||||
|
# 转换用户名
|
||||||
|
mp_name = self.useroper.get_name(**{f"{channel.name.lower()}_userid": userid}) if channel else None
|
||||||
# 添加订阅,状态为N
|
# 添加订阅,状态为N
|
||||||
self.subscribechain.add(title=mediainfo.title,
|
self.subscribechain.add(title=mediainfo.title,
|
||||||
year=mediainfo.year,
|
year=mediainfo.year,
|
||||||
@@ -304,7 +306,7 @@ class MessageChain(ChainBase):
|
|||||||
channel=channel,
|
channel=channel,
|
||||||
source=source,
|
source=source,
|
||||||
userid=userid,
|
userid=userid,
|
||||||
username=username,
|
username=mp_name or username,
|
||||||
best_version=best_version)
|
best_version=best_version)
|
||||||
elif cache_type == "Torrent":
|
elif cache_type == "Torrent":
|
||||||
if int(text) == 0:
|
if int(text) == 0:
|
||||||
@@ -505,6 +507,8 @@ class MessageChain(ChainBase):
|
|||||||
note = downloaded
|
note = downloaded
|
||||||
else:
|
else:
|
||||||
note = None
|
note = None
|
||||||
|
# 转换用户名
|
||||||
|
mp_name = self.useroper.get_name(**{f"{channel.name.lower()}_userid": userid}) if channel else None
|
||||||
# 添加订阅,状态为R
|
# 添加订阅,状态为R
|
||||||
self.subscribechain.add(title=_current_media.title,
|
self.subscribechain.add(title=_current_media.title,
|
||||||
year=_current_media.year,
|
year=_current_media.year,
|
||||||
@@ -514,7 +518,7 @@ class MessageChain(ChainBase):
|
|||||||
channel=channel,
|
channel=channel,
|
||||||
source=source,
|
source=source,
|
||||||
userid=userid,
|
userid=userid,
|
||||||
username=username,
|
username=mp_name or username,
|
||||||
state="R",
|
state="R",
|
||||||
note=note)
|
note=note)
|
||||||
|
|
||||||
|
|||||||
@@ -165,7 +165,9 @@ class SubscribeChain(ChainBase, metaclass=Singleton):
|
|||||||
'downloader': self.__get_default_subscribe_config(mediainfo.type, "downloader") if not kwargs.get(
|
'downloader': self.__get_default_subscribe_config(mediainfo.type, "downloader") if not kwargs.get(
|
||||||
"downloader") else kwargs.get("downloader"),
|
"downloader") else kwargs.get("downloader"),
|
||||||
'save_path': self.__get_default_subscribe_config(mediainfo.type, "save_path") if not kwargs.get(
|
'save_path': self.__get_default_subscribe_config(mediainfo.type, "save_path") if not kwargs.get(
|
||||||
"save_path") else kwargs.get("save_path")
|
"save_path") else kwargs.get("save_path"),
|
||||||
|
'filter_groups': self.__get_default_subscribe_config(mediainfo.type, "filter_groups") if not kwargs.get(
|
||||||
|
"filter_groups") else kwargs.get("filter_groups"),
|
||||||
})
|
})
|
||||||
sid, err_msg = self.subscribeoper.add(mediainfo=mediainfo, season=season, username=username, **kwargs)
|
sid, err_msg = self.subscribeoper.add(mediainfo=mediainfo, season=season, username=username, **kwargs)
|
||||||
if not sid:
|
if not sid:
|
||||||
|
|||||||
@@ -663,22 +663,22 @@ class TransferChain(ChainBase, metaclass=Singleton):
|
|||||||
if transfer_history:
|
if transfer_history:
|
||||||
mediainfo.title = transfer_history.title
|
mediainfo.title = transfer_history.title
|
||||||
|
|
||||||
# 获取集数据
|
|
||||||
if not task.episodes_info and mediainfo.type == MediaType.TV:
|
|
||||||
if task.meta.begin_season is None:
|
|
||||||
task.meta.begin_season = 1
|
|
||||||
mediainfo.season = mediainfo.season or task.meta.begin_season
|
|
||||||
task.episodes_info = self.tmdbchain.tmdb_episodes(
|
|
||||||
tmdbid=mediainfo.tmdb_id,
|
|
||||||
season=mediainfo.season
|
|
||||||
)
|
|
||||||
|
|
||||||
# 更新任务信息
|
# 更新任务信息
|
||||||
task.mediainfo = mediainfo
|
task.mediainfo = mediainfo
|
||||||
# 更新队列任务
|
# 更新队列任务
|
||||||
curr_task = self.jobview.remove_task(task.fileitem)
|
curr_task = self.jobview.remove_task(task.fileitem)
|
||||||
self.jobview.add_task(task, state=curr_task.state if curr_task else "waiting")
|
self.jobview.add_task(task, state=curr_task.state if curr_task else "waiting")
|
||||||
|
|
||||||
|
# 获取集数据
|
||||||
|
if not task.episodes_info and task.mediainfo.type == MediaType.TV:
|
||||||
|
if task.meta.begin_season is None:
|
||||||
|
task.meta.begin_season = 1
|
||||||
|
task.mediainfo.season = task.mediainfo.season or task.meta.begin_season
|
||||||
|
task.episodes_info = self.tmdbchain.tmdb_episodes(
|
||||||
|
tmdbid=task.mediainfo.tmdb_id,
|
||||||
|
season=task.mediainfo.season
|
||||||
|
)
|
||||||
|
|
||||||
# 查询整理目标目录
|
# 查询整理目标目录
|
||||||
if not task.target_directory:
|
if not task.target_directory:
|
||||||
if task.target_path:
|
if task.target_path:
|
||||||
|
|||||||
@@ -262,6 +262,8 @@ class MediaInfo:
|
|||||||
runtime: int = None
|
runtime: int = None
|
||||||
# 下一集
|
# 下一集
|
||||||
next_episode_to_air: dict = field(default_factory=dict)
|
next_episode_to_air: dict = field(default_factory=dict)
|
||||||
|
# 内容分级
|
||||||
|
content_rating: str = None
|
||||||
|
|
||||||
def __post_init__(self):
|
def __post_init__(self):
|
||||||
# 设置媒体信息
|
# 设置媒体信息
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
from typing import Optional
|
from typing import Optional, List
|
||||||
|
|
||||||
from fastapi import Depends, HTTPException
|
from fastapi import Depends, HTTPException
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
@@ -51,6 +51,12 @@ class UserOper(DbOper):
|
|||||||
用户管理
|
用户管理
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
def list(self) -> List[User]:
|
||||||
|
"""
|
||||||
|
获取用户列表
|
||||||
|
"""
|
||||||
|
return User.list(self._db)
|
||||||
|
|
||||||
def add(self, **kwargs):
|
def add(self, **kwargs):
|
||||||
"""
|
"""
|
||||||
新增用户
|
新增用户
|
||||||
@@ -90,3 +96,16 @@ class UserOper(DbOper):
|
|||||||
if settings:
|
if settings:
|
||||||
return settings.get(key)
|
return settings.get(key)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def get_name(self, **kwargs) -> Optional[str]:
|
||||||
|
"""
|
||||||
|
根据绑定账号获取用户名称
|
||||||
|
"""
|
||||||
|
users = self.list()
|
||||||
|
for user in users:
|
||||||
|
user_setting = user.settings
|
||||||
|
if user_setting:
|
||||||
|
for k, v in kwargs.items():
|
||||||
|
if user_setting.get(k) == str(v):
|
||||||
|
return user.name
|
||||||
|
return None
|
||||||
|
|||||||
@@ -16,7 +16,8 @@ from app.helper.module import ModuleHelper
|
|||||||
from app.log import logger
|
from app.log import logger
|
||||||
from app.modules import _ModuleBase
|
from app.modules import _ModuleBase
|
||||||
from app.modules.filemanager.storages import StorageBase
|
from app.modules.filemanager.storages import StorageBase
|
||||||
from app.schemas import TransferInfo, ExistMediaInfo, TmdbEpisode, TransferDirectoryConf, FileItem, StorageUsage, TransferRenameEventData
|
from app.schemas import TransferInfo, ExistMediaInfo, TmdbEpisode, TransferDirectoryConf, FileItem, StorageUsage, \
|
||||||
|
TransferRenameEventData, TransferInterceptEventData
|
||||||
from app.schemas.types import MediaType, ModuleType, ChainEventType, OtherModulesType
|
from app.schemas.types import MediaType, ModuleType, ChainEventType, OtherModulesType
|
||||||
from app.utils.system import SystemUtils
|
from app.utils.system import SystemUtils
|
||||||
|
|
||||||
@@ -763,6 +764,21 @@ class FileManagerModule(_ModuleBase):
|
|||||||
target_item = target_oper.get_folder(target_path)
|
target_item = target_oper.get_folder(target_path)
|
||||||
if not target_item:
|
if not target_item:
|
||||||
return None, f"获取目标目录失败:{target_path}"
|
return None, f"获取目标目录失败:{target_path}"
|
||||||
|
event_data = TransferInterceptEventData(
|
||||||
|
fileitem=fileitem,
|
||||||
|
target_storage=target_storage,
|
||||||
|
target_path=target_path,
|
||||||
|
transfer_type=transfer_type
|
||||||
|
)
|
||||||
|
event = eventmanager.send_event(ChainEventType.TransferRename, event_data)
|
||||||
|
if event and event.event_data:
|
||||||
|
event_data = event.event_data
|
||||||
|
# 如果事件被取消,跳过文件整理
|
||||||
|
if event_data.cancel:
|
||||||
|
logger.debug(
|
||||||
|
f"Transfer dir canceled by event: {event_data.source},"
|
||||||
|
f"Reason: {event_data.reason}")
|
||||||
|
return None, event_data.reason
|
||||||
# 处理所有文件
|
# 处理所有文件
|
||||||
state, errmsg = self.__transfer_dir_files(fileitem=fileitem,
|
state, errmsg = self.__transfer_dir_files(fileitem=fileitem,
|
||||||
target_storage=target_storage,
|
target_storage=target_storage,
|
||||||
@@ -830,6 +846,24 @@ class FileManagerModule(_ModuleBase):
|
|||||||
target_file.unlink()
|
target_file.unlink()
|
||||||
logger.info(f"正在整理文件:【{fileitem.storage}】{fileitem.path} 到 【{target_storage}】{target_file},"
|
logger.info(f"正在整理文件:【{fileitem.storage}】{fileitem.path} 到 【{target_storage}】{target_file},"
|
||||||
f"操作类型:{transfer_type}")
|
f"操作类型:{transfer_type}")
|
||||||
|
event_data = TransferInterceptEventData(
|
||||||
|
fileitem=fileitem,
|
||||||
|
target_storage=target_storage,
|
||||||
|
target_path=target_file,
|
||||||
|
transfer_type=transfer_type,
|
||||||
|
options={
|
||||||
|
"over_flag": over_flag
|
||||||
|
}
|
||||||
|
)
|
||||||
|
event = eventmanager.send_event(ChainEventType.TransferRename, event_data)
|
||||||
|
if event and event.event_data:
|
||||||
|
event_data = event.event_data
|
||||||
|
# 如果事件被取消,跳过文件整理
|
||||||
|
if event_data.cancel:
|
||||||
|
logger.debug(
|
||||||
|
f"Transfer file canceled by event: {event_data.source},"
|
||||||
|
f"Reason: {event_data.reason}")
|
||||||
|
return None, event_data.reason
|
||||||
new_item, errmsg = self.__transfer_command(fileitem=fileitem,
|
new_item, errmsg = self.__transfer_command(fileitem=fileitem,
|
||||||
target_storage=target_storage,
|
target_storage=target_storage,
|
||||||
target_file=target_file,
|
target_file=target_file,
|
||||||
@@ -1127,7 +1161,7 @@ class FileManagerModule(_ModuleBase):
|
|||||||
if episode.episode_number == meta.begin_episode:
|
if episode.episode_number == meta.begin_episode:
|
||||||
episode_date = episode.air_date
|
episode_date = episode.air_date
|
||||||
break
|
break
|
||||||
|
|
||||||
return {
|
return {
|
||||||
# 标题
|
# 标题
|
||||||
"title": __convert_invalid_characters(mediainfo.title),
|
"title": __convert_invalid_characters(mediainfo.title),
|
||||||
|
|||||||
@@ -399,7 +399,7 @@ class TheMovieDbModule(_ModuleBase):
|
|||||||
"with_watch_providers": with_watch_providers,
|
"with_watch_providers": with_watch_providers,
|
||||||
"vote_average.gte": vote_average,
|
"vote_average.gte": vote_average,
|
||||||
"vote_count.gte": vote_count,
|
"vote_count.gte": vote_count,
|
||||||
"release_date.gte": release_date,
|
"first_air_date.gte": release_date,
|
||||||
"page": page
|
"page": page
|
||||||
})
|
})
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -170,6 +170,9 @@ class TmdbScraper:
|
|||||||
DomUtils.add_node(doc, root, "genre", genre.get("name") or "")
|
DomUtils.add_node(doc, root, "genre", genre.get("name") or "")
|
||||||
# 评分
|
# 评分
|
||||||
DomUtils.add_node(doc, root, "rating", mediainfo.vote_average or "0")
|
DomUtils.add_node(doc, root, "rating", mediainfo.vote_average or "0")
|
||||||
|
# 内容分级
|
||||||
|
if content_rating := mediainfo.content_rating:
|
||||||
|
DomUtils.add_node(doc, root, "mpaa", content_rating)
|
||||||
|
|
||||||
return doc
|
return doc
|
||||||
|
|
||||||
|
|||||||
@@ -601,6 +601,8 @@ class TmdbApi:
|
|||||||
tmdb_info['genre_ids'] = __get_genre_ids(tmdb_info.get('genres'))
|
tmdb_info['genre_ids'] = __get_genre_ids(tmdb_info.get('genres'))
|
||||||
# 别名和译名
|
# 别名和译名
|
||||||
tmdb_info['names'] = self.__get_names(tmdb_info)
|
tmdb_info['names'] = self.__get_names(tmdb_info)
|
||||||
|
# 内容分级
|
||||||
|
tmdb_info['content_rating'] = self.__get_content_rating(tmdb_info)
|
||||||
# 转换多语种标题
|
# 转换多语种标题
|
||||||
self.__update_tmdbinfo_extra_title(tmdb_info)
|
self.__update_tmdbinfo_extra_title(tmdb_info)
|
||||||
# 转换中文标题
|
# 转换中文标题
|
||||||
@@ -608,6 +610,68 @@ class TmdbApi:
|
|||||||
|
|
||||||
return tmdb_info
|
return tmdb_info
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def __get_content_rating(tmdb_info: dict) -> Optional[str]:
|
||||||
|
"""
|
||||||
|
获得tmdb中的内容评级
|
||||||
|
:param tmdb_info: TMDB信息
|
||||||
|
:return: 内容评级
|
||||||
|
"""
|
||||||
|
if not tmdb_info:
|
||||||
|
return None
|
||||||
|
# dict[地区:分级]
|
||||||
|
ratings = {}
|
||||||
|
if results := (tmdb_info.get("release_dates") or {}).get("results"):
|
||||||
|
"""
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"iso_3166_1": "AR",
|
||||||
|
"release_dates": [
|
||||||
|
{
|
||||||
|
"certification": "+13",
|
||||||
|
"descriptors": [],
|
||||||
|
"iso_639_1": "",
|
||||||
|
"note": "",
|
||||||
|
"release_date": "2025-01-23T00:00:00.000Z",
|
||||||
|
"type": 3
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
"""
|
||||||
|
for item in results:
|
||||||
|
iso_3166_1 = item.get("iso_3166_1")
|
||||||
|
if not iso_3166_1:
|
||||||
|
continue
|
||||||
|
dates = item.get("release_dates")
|
||||||
|
if not dates:
|
||||||
|
continue
|
||||||
|
certification = dates[0].get("certification")
|
||||||
|
if not certification:
|
||||||
|
continue
|
||||||
|
ratings[iso_3166_1] = certification
|
||||||
|
elif results := (tmdb_info.get("content_ratings") or {}).get("results"):
|
||||||
|
"""
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"descriptors": [],
|
||||||
|
"iso_3166_1": "US",
|
||||||
|
"rating": "TV-MA"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
"""
|
||||||
|
for item in results:
|
||||||
|
iso_3166_1 = item.get("iso_3166_1")
|
||||||
|
if not iso_3166_1:
|
||||||
|
continue
|
||||||
|
rating = item.get("rating")
|
||||||
|
if not rating:
|
||||||
|
continue
|
||||||
|
ratings[iso_3166_1] = rating
|
||||||
|
if not ratings:
|
||||||
|
return None
|
||||||
|
return ratings.get("CN") or ratings.get("US")
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def __update_tmdbinfo_cn_title(tmdb_info: dict):
|
def __update_tmdbinfo_cn_title(tmdb_info: dict):
|
||||||
"""
|
"""
|
||||||
@@ -700,6 +764,7 @@ class TmdbApi:
|
|||||||
"credits,"
|
"credits,"
|
||||||
"alternative_titles,"
|
"alternative_titles,"
|
||||||
"translations,"
|
"translations,"
|
||||||
|
"release_dates,"
|
||||||
"external_ids") -> Optional[dict]:
|
"external_ids") -> Optional[dict]:
|
||||||
"""
|
"""
|
||||||
获取电影的详情
|
获取电影的详情
|
||||||
@@ -812,6 +877,7 @@ class TmdbApi:
|
|||||||
"credits,"
|
"credits,"
|
||||||
"alternative_titles,"
|
"alternative_titles,"
|
||||||
"translations,"
|
"translations,"
|
||||||
|
"content_ratings,"
|
||||||
"external_ids") -> Optional[dict]:
|
"external_ids") -> Optional[dict]:
|
||||||
"""
|
"""
|
||||||
获取电视剧的详情
|
获取电视剧的详情
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ from typing import Optional, Dict, Any, List, Set
|
|||||||
|
|
||||||
from pydantic import BaseModel, Field, root_validator
|
from pydantic import BaseModel, Field, root_validator
|
||||||
|
|
||||||
from app.schemas import MessageChannel
|
from app.schemas import MessageChannel, FileItem
|
||||||
|
|
||||||
|
|
||||||
class BaseEventData(BaseModel):
|
class BaseEventData(BaseModel):
|
||||||
@@ -50,7 +50,7 @@ class AuthCredentials(ChainEventData):
|
|||||||
service: Optional[str] = Field(default=None, description="服务名称")
|
service: Optional[str] = Field(default=None, description="服务名称")
|
||||||
|
|
||||||
@root_validator(pre=True)
|
@root_validator(pre=True)
|
||||||
def check_fields_based_on_grant_type(cls, values): # noqa
|
def check_fields_based_on_grant_type(cls, values): # noqa
|
||||||
grant_type = values.get("grant_type")
|
grant_type = values.get("grant_type")
|
||||||
if not grant_type:
|
if not grant_type:
|
||||||
values["grant_type"] = "password"
|
values["grant_type"] = "password"
|
||||||
@@ -202,3 +202,33 @@ class ResourceDownloadEventData(ChainEventData):
|
|||||||
cancel: bool = Field(default=False, description="是否取消下载")
|
cancel: bool = Field(default=False, description="是否取消下载")
|
||||||
source: str = Field(default="未知拦截源", description="拦截源")
|
source: str = Field(default="未知拦截源", description="拦截源")
|
||||||
reason: str = Field(default="", description="拦截原因")
|
reason: str = Field(default="", description="拦截原因")
|
||||||
|
|
||||||
|
|
||||||
|
class TransferInterceptEventData(ChainEventData):
|
||||||
|
"""
|
||||||
|
TransferIntercept 事件的数据模型
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
# 输入参数
|
||||||
|
fileitem (FileItem): 源文件
|
||||||
|
target_storage (str): 目标存储
|
||||||
|
target_path (Path): 目标路径
|
||||||
|
transfer_type (str): 整理方式(copy、move、link、softlink等)
|
||||||
|
options (dict): 其他参数
|
||||||
|
|
||||||
|
# 输出参数
|
||||||
|
cancel (bool): 是否取消下载,默认值为 False
|
||||||
|
source (str): 拦截源,默认值为 "未知拦截源"
|
||||||
|
reason (str): 拦截原因,描述拦截的具体原因
|
||||||
|
"""
|
||||||
|
# 输入参数
|
||||||
|
fileitem: FileItem = Field(..., description="源文件")
|
||||||
|
target_storage: str = Field(..., description="目标存储")
|
||||||
|
target_path: Path = Field(..., description="目标路径")
|
||||||
|
transfer_type: str = Field(..., description="整理方式")
|
||||||
|
options: Optional[dict] = Field(None, description="其他参数")
|
||||||
|
|
||||||
|
# 输出参数
|
||||||
|
cancel: bool = Field(default=False, description="是否取消整理")
|
||||||
|
source: str = Field(default="未知拦截源", description="拦截源")
|
||||||
|
reason: str = Field(default="", description="拦截原因")
|
||||||
|
|||||||
@@ -75,6 +75,8 @@ class ChainEventType(Enum):
|
|||||||
CommandRegister = "command.register"
|
CommandRegister = "command.register"
|
||||||
# 整理重命名
|
# 整理重命名
|
||||||
TransferRename = "transfer.rename"
|
TransferRename = "transfer.rename"
|
||||||
|
# 整理拦截
|
||||||
|
TransferIntercept = "transfer.intercept"
|
||||||
# 资源选择
|
# 资源选择
|
||||||
ResourceSelection = "resource.selection"
|
ResourceSelection = "resource.selection"
|
||||||
# 资源下载
|
# 资源下载
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
APP_VERSION = 'v2.2.5'
|
APP_VERSION = 'v2.2.6'
|
||||||
FRONTEND_VERSION = 'v2.2.5'
|
FRONTEND_VERSION = 'v2.2.6'
|
||||||
|
|||||||
Reference in New Issue
Block a user