diff --git a/.github/workflows/frontend-test.yml b/.github/workflows/frontend-test.yml index 88e1c50c..ad7689fd 100644 --- a/.github/workflows/frontend-test.yml +++ b/.github/workflows/frontend-test.yml @@ -92,9 +92,10 @@ jobs: - name: Build distributable run: yarn build - - name: Verify committed distributable + - name: Verify distributable run: | - if [ -n "$(git status --porcelain -- dist)" ]; then - git status --short -- dist + test -s dist/assets/remoteEntry.js + if [ -n "$(git status --porcelain --untracked-files=all -- dist)" ]; then + git status --short --untracked-files=all -- dist exit 1 fi diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 73203b64..163761c0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -22,6 +22,11 @@ jobs: with: fetch-depth: 0 + - name: Setup Node + uses: actions/setup-node@v7 + with: + node-version: '24' + - name: Install dependencies run: | sudo apt-get update @@ -39,6 +44,36 @@ jobs: # Track processed tags to avoid duplicates across package files. : > processed_tags.txt + build_frontend() { + local plugin_dir="$1" + local frontend_dir="$plugin_dir/frontend" + [ -f "$frontend_dir/package.json" ] || return 0 + + if [ ! -f "$frontend_dir/yarn.lock" ]; then + echo "Frontend lockfile missing: $frontend_dir/yarn.lock" >&2 + exit 1 + fi + + echo "Building frontend in $frontend_dir" + (cd "$frontend_dir" && yarn --frozen-lockfile && yarn build) + + if [ ! -s "$frontend_dir/dist/assets/remoteEntry.js" ]; then + echo "Frontend federation entry missing: $frontend_dir/dist/assets/remoteEntry.js" >&2 + exit 1 + fi + + if git check-ignore -q "$frontend_dir/dist/assets/remoteEntry.js"; then + echo "Frontend distributable is ignored by Git: $frontend_dir/dist" >&2 + exit 1 + fi + + if [ -n "$(git status --porcelain --untracked-files=all -- "$frontend_dir/dist")" ]; then + git status --short --untracked-files=all -- "$frontend_dir/dist" + echo "Frontend distributable does not match committed files: $frontend_dir/dist" >&2 + exit 1 + fi + } + process_package() { local pkg_file="$1" [ -f "$pkg_file" ] || return 0 @@ -115,8 +150,9 @@ jobs: fi echo "Packing $plugin_id from $plugin_dir -> $asset" + build_frontend "$plugin_dir" rm -f "$asset" - (cd "$(dirname "$plugin_dir")" && zip -r "$GITHUB_WORKSPACE/$asset" "$(basename "$plugin_dir")" -x "*/__pycache__/*" -x "*.pyc") >/dev/null + (cd "$(dirname "$plugin_dir")" && zip -r "$GITHUB_WORKSPACE/$asset" "$(basename "$plugin_dir")" -x "*/__pycache__/*" -x "*.pyc" -x "*/node_modules/*") >/dev/null # Re-running a version replaces the release asset for that plugin. if gh release view "$tag" >/dev/null 2>&1; then diff --git a/.gitignore b/.gitignore index 8248ce75..6bf7972f 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,10 @@ __pycache__/ build/ develop-eggs/ dist/ +!plugins.v[23]/*/frontend/dist/ +plugins.v[23]/*/frontend/dist/* +!plugins.v[23]/*/frontend/dist/assets/ +!plugins.v[23]/*/frontend/dist/assets/** downloads/ eggs/ .eggs/ @@ -168,8 +172,3 @@ cython_debug/ # option (not recommended) you can uncomment the following to ignore the entire idea folder. .idea/ .vscode/ - -!plugins.v2/subscribeassistantenhanced/frontend/dist/ -plugins.v2/subscribeassistantenhanced/frontend/dist/* -!plugins.v2/subscribeassistantenhanced/frontend/dist/assets/ -!plugins.v2/subscribeassistantenhanced/frontend/dist/assets/** diff --git a/package.v3.json b/package.v3.json index 5c7129ea..441faa84 100644 --- a/package.v3.json +++ b/package.v3.json @@ -3,13 +3,13 @@ "name": "订阅助手(增强版)", "description": "多场景管理订阅,实现订阅全生命周期管理。", "labels": "订阅", - "version": "0.7.1", + "version": "0.7.2", "icon": "https://raw.githubusercontent.com/InfinityPacer/MoviePilot-Plugins/main/icons/subscribeassistantenhanced.png", "author": "InfinityPacer", "level": 1, "system_version": ">=3.0.0", "history": { - "v0.7.1": "适配 MoviePilot V3 统一响应封装,避免打开配置页时提示未知错误" + "v0.7.2": "修正 V3 发布包缺少前端构建产物的问题,确保配置组件可正常加载" }, "release": true }, diff --git a/plugins.v3/subscribeassistantenhanced/README.md b/plugins.v3/subscribeassistantenhanced/README.md index d188fb86..6f7afa46 100644 --- a/plugins.v3/subscribeassistantenhanced/README.md +++ b/plugins.v3/subscribeassistantenhanced/README.md @@ -16,6 +16,7 @@ ## 版本更新日志 +- v0.7.2:修正 V3 发布包缺少前端构建产物的问题,确保配置组件可正常加载。 - v0.7.1:适配 MoviePilot V3 统一响应封装,避免打开配置页时提示未知错误。 - v0.7:MoviePilot V3 版本订阅助手(增强版)插件 - v0.6.12:配置页滚动条跟随主程序样式,并恢复移动端修改项计数。 diff --git a/plugins.v3/subscribeassistantenhanced/__init__.py b/plugins.v3/subscribeassistantenhanced/__init__.py index 9ea45f15..383a2ad0 100644 --- a/plugins.v3/subscribeassistantenhanced/__init__.py +++ b/plugins.v3/subscribeassistantenhanced/__init__.py @@ -90,7 +90,7 @@ class SubscribeAssistantEnhanced(_PluginBase): # 插件图标 plugin_icon = "https://raw.githubusercontent.com/InfinityPacer/MoviePilot-Plugins/main/icons/subscribeassistantenhanced.png" # 插件版本 - plugin_version = "0.7.1" + plugin_version = "0.7.2" _site_cache_candidate_helper_warned = False # 插件作者 plugin_author = "InfinityPacer" diff --git a/plugins.v3/subscribeassistantenhanced/frontend/dist/assets/__federation_expose_Config-COZtkKfx.css b/plugins.v3/subscribeassistantenhanced/frontend/dist/assets/__federation_expose_Config-COZtkKfx.css new file mode 100644 index 00000000..06e001bb --- /dev/null +++ b/plugins.v3/subscribeassistantenhanced/frontend/dist/assets/__federation_expose_Config-COZtkKfx.css @@ -0,0 +1,833 @@ + +.sae-config[data-v-59866d47] { + container-type: inline-size; + min-inline-size: 0; + color: rgb(var(--v-theme-on-surface)); + letter-spacing: 0; +} +.sae-config[data-v-59866d47], +.sae-config[data-v-59866d47] * { + box-sizing: border-box; +} +.sae-config__form[data-v-59866d47] { + min-inline-size: 0; +} +.sae-config-header-sentinel[data-v-59866d47] { + block-size: 1px; + margin-block-end: -1px; + pointer-events: none; +} +.sae-field-section[data-v-59866d47], +.sae-impact-preview[data-v-59866d47] { + border: var(--app-surface-border); + border-radius: var(--app-surface-radius); + backdrop-filter: var(--app-grouped-list-backdrop-filter); + background: var(--app-grouped-list-background); + box-shadow: var(--app-surface-shadow); +} +.sae-config-header[data-v-59866d47] { + position: sticky; + z-index: 20; + inset-block-start: 0; + display: flex; + align-items: center; + justify-content: space-between; + min-inline-size: 0; + min-block-size: 72px; + padding: 10px 16px; + border-block-end: 1px solid rgba(var(--v-theme-on-surface), 0.1); + background: transparent; + backdrop-filter: none; + box-shadow: none; + gap: 16px; +} +.sae-config-header--scrolled[data-v-59866d47] { + --sae-header-background: var(--app-grouped-list-background); + --sae-header-backdrop-filter: var(--app-grouped-list-backdrop-filter); + + background: var(--sae-header-background); + backdrop-filter: var(--sae-header-backdrop-filter); + box-shadow: var(--app-surface-shadow); +} +html[data-theme='transparent'] .sae-config-header--scrolled { + --sae-header-background: rgba(var(--v-theme-surface), 0.72); + --sae-header-backdrop-filter: blur(24px); +} +html[data-theme='transparent'].transparent-blur-disabled .sae-config-header--scrolled { + --sae-header-background: rgba(var(--v-theme-surface), 0.92); + --sae-header-backdrop-filter: none; +} +.sae-config-header__brand[data-v-59866d47] { + display: flex; + flex: 1 1 auto; + align-items: center; + min-inline-size: 0; + gap: 10px; +} +.sae-config-header__logo[data-v-59866d47] { + display: block; + flex: 0 0 40px; + block-size: 40px; + inline-size: 40px; + object-fit: contain; +} +.sae-config-header__identity[data-v-59866d47] { + min-inline-size: 0; +} +.sae-config-header__crumbs[data-v-59866d47], +.sae-config-header__title-row[data-v-59866d47] { + display: flex; + align-items: center; + min-inline-size: 0; +} +.sae-config-header__crumbs[data-v-59866d47] { + margin-block-end: 3px; + color: rgba(var(--v-theme-on-surface), 0.55); + font-size: 0.6875rem; + line-height: 1rem; + gap: 2px; +} +.sae-config-header__title-row[data-v-59866d47] { + gap: 8px; +} +.sae-config-header__title[data-v-59866d47] { + margin: 0; + overflow-wrap: anywhere; + font-size: 1.0625rem; + font-weight: 700; + letter-spacing: 0; + line-height: 1.4rem; +} +.sae-config-header__actions[data-v-59866d47] { + display: flex; + flex: 0 0 auto; + align-items: center; + gap: 8px; +} +.sae-config-header__run[data-v-59866d47] { + min-inline-size: 112px; + font-weight: 600; +} +.sae-config-header__save[data-v-59866d47] { + min-inline-size: 120px; + font-weight: 600; +} +.sae-config-header__close-action[data-v-59866d47] { + min-inline-size: 96px; + color: rgba(var(--v-theme-on-surface), 0.78); + font-weight: 600; + transition: + background-color 180ms ease, + color 180ms ease; +} +@media (hover: hover) and (pointer: fine) { +.sae-config-header__close-action[data-v-59866d47]:hover { + color: rgb(var(--v-theme-on-surface)); +} +} +.sae-config-header__close-action[data-v-59866d47]:focus-visible { + color: rgb(var(--v-theme-on-surface)); + outline: 2px solid rgba(var(--v-theme-primary), 0.58); + outline-offset: 2px; +} +.sae-config-header__close-action[data-v-59866d47]:active { + background-color: rgba(var(--v-theme-on-surface), 0.04); + color: rgb(var(--v-theme-on-surface)); +} +@media (prefers-reduced-motion: reduce) { +.sae-config-header__close-action[data-v-59866d47], + .sae-config-header__close-action[data-v-59866d47] .v-btn__overlay { + transition: none; +} +.sae-config-header__close-action[data-v-59866d47] .v-ripple__container { + display: none; +} +} +.sae-config-header__close-icon[data-v-59866d47] { + flex: 0 0 40px; + block-size: 40px; + inline-size: 40px; +} +.sae-config__body[data-v-59866d47] { + min-inline-size: 0; + padding: 12px; +} +.sae-config-layout[data-v-59866d47] { + display: grid; + min-inline-size: 0; + margin-block-start: 12px; + gap: 12px; + grid-template-areas: + 'content' + 'preview'; + grid-template-columns: minmax(0, 1fr); +} +.sae-group-nav[data-v-59866d47] { + display: none; + min-inline-size: 0; + grid-area: navigation; +} +.sae-group-nav__heading[data-v-59866d47] { + padding: 6px 10px 10px; + color: rgba(var(--v-theme-on-surface), 0.54); + font-size: 0.75rem; + font-weight: 600; +} +.sae-group-nav > .sae-group-nav__list.v-list[data-v-59866d47] { + flex: 1 1 auto; + min-block-size: 0; + overflow-y: auto; + padding: 0 4px; + backdrop-filter: none; + background: transparent; + background-color: transparent; +} +.sae-group-nav__list[data-v-59866d47] .v-list-item { + position: relative; + min-block-size: 50px; + padding-inline: 12px; + margin-block: 4px; +} +.sae-group-nav__list[data-v-59866d47] .v-list-item-title { + overflow-wrap: anywhere; + font-size: 0.875rem; + font-weight: 600; + letter-spacing: 0; + line-height: 1.2rem; +} +.sae-group-nav__list[data-v-59866d47] .v-list-item__prepend > .v-icon { + font-size: 1.25rem; +} +.sae-group-nav__list[data-v-59866d47] .v-list-item--active { + background: rgba(var(--v-theme-primary), 0.09); + color: rgb(var(--v-theme-primary)); +} +.sae-group-nav__list[data-v-59866d47] .v-list-item--active::before { + position: absolute; + inset-block: 8px; + inset-inline-start: 0; + inline-size: 3px; + border-radius: 0 3px 3px 0; + background: rgb(var(--v-theme-primary)); + content: ''; +} +.sae-group-nav__help[data-v-59866d47] { + flex: 0 0 auto; + padding: 12px; + margin-block-start: 10px; + border: 1px solid rgba(var(--v-theme-on-surface), 0.12); + border-radius: var(--app-control-radius); + background: rgba(var(--v-theme-on-surface), 0.025); +} +.sae-group-nav__help-title[data-v-59866d47] { + display: block; + font-size: 0.8125rem; + line-height: 1.1rem; +} +.sae-group-nav__help p[data-v-59866d47] { + margin: 6px 0 0; + color: rgba(var(--v-theme-on-surface), 0.56); + font-size: 0.6875rem; + line-height: 1rem; +} +.sae-group-nav__help-link[data-v-59866d47] { + min-inline-size: 0; + min-block-size: 28px; + padding-inline: 0; + margin-block-start: 7px; + font-size: 0.75rem; +} +.sae-field-surface[data-v-59866d47] { + min-inline-size: 0; + grid-area: content; +} +.sae-field-surface__heading[data-v-59866d47] { + display: flex; + align-items: flex-start; + justify-content: space-between; + min-inline-size: 0; + padding: 2px 2px 12px; + gap: 12px; +} +.sae-field-surface__heading-copy[data-v-59866d47] { + display: flex; + align-items: flex-start; + min-inline-size: 0; + gap: 9px; +} +.sae-field-surface__mobile-actions[data-v-59866d47] { + display: flex; + flex: 0 0 auto; + align-items: center; + gap: 4px; +} +.sae-mobile-group-action[data-v-59866d47], +.sae-mobile-help[data-v-59866d47] { + block-size: 36px; + inline-size: 36px; +} +.sae-field-surface h2[data-v-59866d47], +.sae-impact-preview h2[data-v-59866d47] { + margin: 0; + font-size: 1rem; + font-weight: 700; + letter-spacing: 0; + line-height: 1.25rem; +} +.sae-field-surface__heading p[data-v-59866d47], +.sae-impact-preview p[data-v-59866d47] { + margin: 3px 0 0; + overflow-wrap: anywhere; + color: rgba(var(--v-theme-on-surface), 0.62); + font-size: 0.75rem; + letter-spacing: 0; + line-height: 1.05rem; +} +.sae-field-section[data-v-59866d47] { + overflow: hidden; + min-inline-size: 0; +} +.sae-field-section + .sae-field-section[data-v-59866d47] { + margin-block-start: 12px; +} +.sae-field-section > h3[data-v-59866d47] { + padding: 14px 16px 10px; + margin: 0; + font-size: 0.9375rem; + font-weight: 700; + letter-spacing: 0; + line-height: 1.25rem; +} +.sae-field-section__rows[data-v-59866d47] { + padding-inline: 16px; +} +.sae-field-row[data-v-59866d47] { + display: grid; + align-items: start; + min-inline-size: 0; + padding-block: 13px; + border-block-start: 1px solid rgba(var(--v-theme-on-surface), 0.08); + gap: 18px; + grid-template-columns: minmax(200px, 1.45fr) minmax(180px, 0.75fr); +} +.sae-field-row--switch[data-v-59866d47] { + align-items: center; +} +.sae-field-row__copy[data-v-59866d47] { + min-inline-size: 0; +} +.sae-field-row__label[data-v-59866d47] { + display: flex; + align-items: center; + color: rgb(var(--v-theme-on-surface)); + font-size: 0.8125rem; + font-weight: 600; + line-height: 1.15rem; + gap: 6px; +} +.sae-field-row__copy p[data-v-59866d47] { + margin: 4px 0 0; + color: rgba(var(--v-theme-on-surface), 0.57); + font-size: 0.6875rem; + line-height: 1rem; +} +.sae-field-control[data-v-59866d47], +.sae-field-control[data-v-59866d47] .v-input { + min-inline-size: 0; + max-inline-size: 100%; +} +.sae-field-control[data-v-59866d47] .v-select__selection { + justify-content: flex-end; + margin-inline-start: auto; + text-align: end; +} +.sae-text-control[data-v-59866d47] input { + text-align: end; +} +.sae-select-summary__primary[data-v-59866d47] { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +.sae-select-summary__count[data-v-59866d47] { + flex: 0 0 auto; + color: rgba(var(--v-theme-on-surface), 0.58); + margin-inline-start: 6px; + white-space: nowrap; +} +.sae-number-stepper[data-v-59866d47] { + display: grid; + align-items: center; + min-block-size: 40px; + inline-size: 100%; + overflow: hidden; + border: 1px solid rgba(var(--v-theme-on-surface), 0.18); + border-radius: var(--app-control-radius); + grid-template-columns: 40px minmax(54px, 1fr) 40px auto; +} +.sae-number-stepper[data-v-59866d47] .v-btn { + min-inline-size: 40px; + block-size: 40px; + border-radius: 0; +} +.sae-number-stepper[data-v-59866d47] .v-field__input { + min-block-size: 40px; + padding: 0 6px; + text-align: center; +} +.sae-number-stepper[data-v-59866d47] input { + text-align: center; +} +.sae-number-stepper__unit[data-v-59866d47] { + min-inline-size: 38px; + padding-inline: 8px; + border-inline-start: 1px solid rgba(var(--v-theme-on-surface), 0.12); + color: rgba(var(--v-theme-on-surface), 0.6); + font-size: 0.6875rem; + text-align: center; + white-space: nowrap; +} +.sae-field-row--switch .sae-field-control[data-v-59866d47] { + display: flex; + justify-content: flex-end; +} +.sae-tracker-entry[data-v-59866d47] { + display: flex; + align-items: center; + justify-content: space-between; + min-inline-size: 0; + padding: 16px; + gap: 12px; +} +.sae-tracker-entry__copy[data-v-59866d47] { + display: flex; + align-items: flex-start; + min-inline-size: 0; + gap: 9px; +} +.sae-tracker-entry__copy > div[data-v-59866d47] { + min-inline-size: 0; +} +.sae-tracker-entry strong[data-v-59866d47] { + display: block; + overflow-wrap: anywhere; + font-size: 0.875rem; + letter-spacing: 0; + line-height: 1.2rem; +} +.sae-tracker-entry p[data-v-59866d47] { + margin: 3px 0 0; + overflow-wrap: anywhere; + color: rgba(var(--v-theme-on-surface), 0.62); + font-size: 0.75rem; + letter-spacing: 0; + line-height: 1.05rem; +} +.sae-tracker-entry[data-v-59866d47] .v-btn { + flex: 0 1 auto; + min-inline-size: 0; + block-size: auto; + min-block-size: 36px; + padding-block: 7px; +} +.sae-tracker-entry[data-v-59866d47] .v-btn__content { + white-space: normal; + overflow-wrap: anywhere; +} +.sae-impact-preview[data-v-59866d47] { + min-inline-size: 0; + align-self: start; + padding: 16px; + grid-area: preview; +} +.sae-impact-preview__title[data-v-59866d47] { + display: flex; + align-items: center; + min-inline-size: 0; + gap: 8px; +} +.sae-impact-preview strong[data-v-59866d47] { + display: block; + overflow-wrap: anywhere; + font-size: 0.875rem; + letter-spacing: 0; + line-height: 1.25rem; +} +.sae-impact-preview__list[data-v-59866d47] { + padding: 0; + margin: 10px 0 0; + list-style: none; +} +.sae-impact-preview__item[data-v-59866d47], +.sae-runtime-summary__row[data-v-59866d47], +.sae-runtime-summary__state[data-v-59866d47], +.sae-runtime-summary__title[data-v-59866d47] { + display: grid; + align-items: start; + min-inline-size: 0; + gap: 10px; + grid-template-columns: 28px minmax(0, 1fr); +} +.sae-impact-preview__item[data-v-59866d47] { + align-items: center; + padding-block: 10px; + color: rgba(var(--v-theme-on-surface), 0.72); + font-size: 0.875rem; + line-height: 1.25rem; + grid-template-columns: 28px minmax(0, 1fr) minmax(0, auto); +} +.sae-impact-preview__item > .v-icon[data-v-59866d47], +.sae-runtime-summary__row > .v-icon[data-v-59866d47] { + justify-self: center; + color: rgba(var(--v-theme-on-surface), 0.54); +} +.sae-impact-preview__item > span[data-v-59866d47], +.sae-impact-preview__item > strong[data-v-59866d47] { + min-inline-size: 0; + overflow-wrap: anywhere; +} +.sae-impact-preview__item > strong[data-v-59866d47] { + text-align: end; +} +.sae-runtime-summary[data-v-59866d47], +.sae-change-summary[data-v-59866d47] { + padding-block-start: 16px; + margin-block-start: 16px; + border-block-start: 1px solid rgba(var(--v-theme-on-surface), 0.1); +} +.sae-summary-section__title[data-v-59866d47] { + display: grid; + align-items: center; + gap: 10px; + grid-template-columns: 28px minmax(0, 1fr); +} +.sae-summary-section__title > .v-icon[data-v-59866d47] { + block-size: 28px; + inline-size: 28px; + border-radius: var(--app-control-radius); + background: rgba(var(--v-theme-primary), 0.1); +} +.sae-change-summary .sae-summary-section__title > .v-icon[data-v-59866d47] { + background: rgba(var(--v-theme-warning), 0.12); +} +.sae-summary-section__title h3[data-v-59866d47] { + margin: 0; + font-size: 1rem; + font-weight: 600; + letter-spacing: 0; + line-height: 1.25rem; +} +.sae-runtime-summary__state[data-v-59866d47], +.sae-runtime-summary__row[data-v-59866d47] { + align-items: center; + padding-block: 7px; + color: rgba(var(--v-theme-on-surface), 0.7); + font-size: 0.875rem; + letter-spacing: 0; + line-height: 1.25rem; +} +.sae-runtime-summary__state[data-v-59866d47] { + margin-block-start: 6px; +} +.sae-runtime-summary__metrics[data-v-59866d47] { + margin-block-start: 10px; +} +.sae-runtime-summary__row[data-v-59866d47] { + grid-template-columns: 28px minmax(0, 1fr) minmax(0, auto); +} +.sae-runtime-summary__row span[data-v-59866d47], +.sae-runtime-summary__row strong[data-v-59866d47] { + min-inline-size: 0; + overflow-wrap: anywhere; +} +.sae-runtime-summary__row strong[data-v-59866d47] { + color: rgb(var(--v-theme-on-surface)); + font-weight: 600; + text-align: end; +} +.sae-runtime-summary__unavailable[data-v-59866d47] { + margin-block-start: 9px; +} +.sae-change-summary ul[data-v-59866d47] { + padding: 0; + margin: 8px 0 0; + list-style: none; +} +.sae-change-summary li[data-v-59866d47] { + display: grid; + align-items: center; + min-inline-size: 0; + padding-block: 6px; + color: rgba(var(--v-theme-on-surface), 0.72); + font-size: 0.875rem; + line-height: 1.25rem; + gap: 8px; + grid-template-columns: 12px minmax(0, 1fr); +} +.sae-change-summary li span[data-v-59866d47] { + min-inline-size: 0; + overflow-wrap: anywhere; +} +.sae-change-summary > p[data-v-59866d47] { + margin: 4px 0 0 20px; + color: rgb(var(--v-theme-warning)); + font-size: 0.75rem; +} +.sae-tracker-dialog__title[data-v-59866d47] { + display: flex; + align-items: center; + justify-content: space-between; + min-inline-size: 0; + gap: 12px; +} +.sae-tracker-dialog__title > span[data-v-59866d47] { + min-inline-size: 0; + overflow-wrap: anywhere; + white-space: normal; +} +.sae-tracker-dialog__actions[data-v-59866d47] { + flex-wrap: wrap; +} +.sae-mobile-save-dock[data-v-59866d47] { + position: sticky; + z-index: 20; + inset-block-end: calc(12px + env(safe-area-inset-bottom)); + display: flex; + align-items: center; + justify-content: space-between; + min-block-size: 64px; + padding-block: 10px; + padding-inline: 14px; + margin-inline: 12px; + border: 1px solid rgba(var(--v-theme-on-surface), 0.12); + border-radius: var(--app-surface-radius); + background: rgba(var(--v-theme-surface), 0.94); + backdrop-filter: blur(20px); + box-shadow: 0 -8px 24px rgba(var(--v-theme-on-surface), 0.06); + gap: 12px; +} +.sae-mobile-save-dock__state[data-v-59866d47] { + display: inline-flex; + align-items: center; + color: rgb(var(--v-theme-warning)); + font-size: 0.8125rem; + font-weight: 600; + white-space: nowrap; + gap: 8px; +} +.sae-mobile-save-dock__save[data-v-59866d47] { + min-inline-size: 128px; + font-weight: 600; +} +html[data-theme='transparent'] .sae-mobile-save-dock { + background: rgba(var(--v-theme-surface), 0.92); + backdrop-filter: blur(24px); +} +html[data-theme='transparent'].transparent-blur-disabled .sae-mobile-save-dock { + background: rgba(var(--v-theme-surface), 0.98); + backdrop-filter: none; +} +.sae-yaml-dialog__content[data-v-59866d47] { + min-block-size: min(60dvh, 560px); + padding: 0 !important; +} +.sae-yaml-editor[data-v-59866d47] { + block-size: min(60dvh, 560px); + inline-size: 100%; +} +.sae-mobile-group-sheet[data-v-59866d47] .v-bottom-sheet__content { + max-block-size: min(82dvh, 680px); +} +@container (width <= 480px) { +.sae-config-header[data-v-59866d47] { + min-block-size: 64px; + padding-inline: 10px; + gap: 8px; +} +.sae-config-header__logo[data-v-59866d47] { + flex-basis: 34px; + block-size: 34px; + inline-size: 34px; +} +.sae-config-header__crumbs[data-v-59866d47] { + display: none; +} +.sae-config-header__title[data-v-59866d47] { + font-size: 0.875rem; + line-height: 1.1rem; +} +.sae-config-header__title-row[data-v-59866d47] { + gap: 5px; +} +.sae-tracker-entry[data-v-59866d47] { + align-items: stretch; + flex-direction: column; +} +.sae-tracker-entry[data-v-59866d47] .v-btn { + inline-size: 100%; +} +} +@container (width < 720px) { +.sae-config-header__run[data-v-59866d47], + .sae-config-header__save[data-v-59866d47], + .sae-config-header__close-action[data-v-59866d47], + .sae-change-summary[data-v-59866d47] { + display: none; +} +.sae-field-row[data-v-59866d47] { + align-items: center; + gap: 12px; + grid-template-columns: minmax(0, 1fr) minmax(180px, 0.9fr); +} +.sae-field-row--switch[data-v-59866d47] { + grid-template-columns: minmax(0, 1fr) auto; +} +.sae-field-row--switch .sae-field-control[data-v-59866d47] { + align-self: center; +} +} +@container (width <= 30rem) { +.sae-field-row[data-v-59866d47] { + align-items: center; + padding-block: 0.625rem; + column-gap: 0.75rem; + row-gap: 0; + grid-template-columns: minmax(0, 1fr) minmax(8.5rem, 10rem); +} +.sae-field-row__copy[data-v-59866d47] { + display: contents; +} +.sae-field-row__label[data-v-59866d47] { + grid-column: 1; + grid-row: 1; +} +.sae-field-row__copy p[data-v-59866d47] { + grid-column: 1 / -1; + grid-row: 2; + margin-block-start: 1px; + line-height: 0.9375rem; +} +.sae-field-control[data-v-59866d47] { + grid-column: 2; + grid-row: 1; + justify-self: end; + inline-size: 100%; +} +.sae-field-row--switch .sae-field-control[data-v-59866d47] { + justify-self: end; + inline-size: auto; +} +.sae-field-control[data-v-59866d47] .v-field { + min-block-size: 1.75rem; + border-radius: 0; + background: transparent; + box-shadow: none; +} +.sae-field-control[data-v-59866d47] .v-field__outline { + display: none; +} +.sae-field-control[data-v-59866d47] .v-field__input { + min-block-size: 1.75rem; + padding-block: 0; + padding-inline: 0; +} +.sae-field-control[data-v-59866d47] .v-field__append-inner { + padding-block-start: 0.25rem; + padding-inline-start: 0.25rem; +} +.sae-field-control[data-v-59866d47] .v-select__selection { + justify-content: flex-end; +} +.sae-number-stepper[data-v-59866d47] { + min-block-size: 1.75rem; + border: 0; + border-radius: 0; + grid-template-columns: minmax(2.625rem, 1fr) auto; +} +.sae-number-stepper[data-v-59866d47] .v-btn { + display: none; +} +.sae-number-stepper[data-v-59866d47] .v-field__input, + .sae-number-stepper[data-v-59866d47] input { + min-block-size: 1.75rem; + text-align: end; +} +.sae-number-stepper__unit[data-v-59866d47] { + min-inline-size: auto; + padding-inline-end: 0; + border-inline-start: 0; +} +} +@container (width >= 720px) { +.sae-config__body[data-v-59866d47] { + padding: 14px; +} +.sae-config-layout[data-v-59866d47] { + padding-block-end: 14px; + gap: 14px; + grid-template-areas: + 'navigation content' + 'navigation preview'; + grid-template-columns: 168px minmax(0, 1fr); +} +.sae-config-header__close-icon[data-v-59866d47], + .sae-field-row--mobile-only[data-v-59866d47], + .sae-field-surface__mobile-actions[data-v-59866d47], + .sae-mobile-save-dock[data-v-59866d47] { + display: none; +} +.sae-group-nav[data-v-59866d47] { + position: sticky; + inset-block-start: 86px; + display: flex; + align-self: start; + flex-direction: column; + block-size: clamp(480px, calc(100dvh - 150px), 760px); + padding-block-end: 2px; + border-inline-end: 1px solid rgba(var(--v-theme-on-surface), 0.1); + padding-inline-end: 10px; +} +.sae-impact-preview[data-v-59866d47] { + align-self: start; +} +} +@container (width >= 880px) { +.sae-config__form[data-v-59866d47] { + display: grid; + overflow: hidden; + block-size: min(90dvh, 820px); + grid-template-rows: 1px auto minmax(0, 1fr); +} +.sae-config__body[data-v-59866d47], + .sae-config-layout[data-v-59866d47] { + min-block-size: 0; + block-size: 100%; +} +.sae-config__body[data-v-59866d47] { + overflow: hidden; +} +.sae-config-layout[data-v-59866d47] { + align-items: stretch; + grid-template-areas: 'navigation content preview'; + grid-template-columns: 168px minmax(0, 1fr) 232px; +} +.sae-group-nav[data-v-59866d47] { + position: static; + overflow: hidden; + block-size: 100%; +} +.sae-impact-preview[data-v-59866d47] { + position: static; + overflow: hidden; + block-size: 100%; +} +.sae-field-surface[data-v-59866d47] { + overflow-x: hidden; + overflow-y: auto; + min-block-size: 0; + padding-inline-end: 4px; +} +.sae-impact-preview[data-v-59866d47] { + align-self: stretch; +} +} diff --git a/plugins.v3/subscribeassistantenhanced/frontend/dist/assets/__federation_expose_Config-CaQEeIYR.js b/plugins.v3/subscribeassistantenhanced/frontend/dist/assets/__federation_expose_Config-CaQEeIYR.js new file mode 100644 index 00000000..9f56f5f1 --- /dev/null +++ b/plugins.v3/subscribeassistantenhanced/frontend/dist/assets/__federation_expose_Config-CaQEeIYR.js @@ -0,0 +1,2886 @@ +import { importShared } from './__federation_fn_import-JrT3xvdd.js'; + +const saeLogo = "data:image/png;base64,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"; + +async function loadSummary(api) { + if (!api) return null; + try { + const response = await api.get("plugin/SubscribeAssistantEnhanced/summary"); + return response.data; + } catch { + console.warn("[SubscribeAssistantEnhanced] summary unavailable"); + return null; + } +} + +const configDefaults = { + "enabled": false, + "notify": true, + "onlyonce": false, + "reset_task": false, + "auto_check_interval_minutes": 30, + "download_check_interval_minutes": 10, + "meta_check_interval_hours": 3, + "best_version_cron": "0 15 * * *", + "download_monitor_enabled": true, + "manual_delete_listen": true, + "tracker_response_listen": true, + "auto_search_when_delete": true, + "skip_deletion": true, + "download_timeout_minutes": 120, + "download_progress_threshold": 10, + "download_queue_grace_multiplier": 2, + "download_retry_limit": 3, + "delete_exclude_tags": "H&R", + "default_tracker_response": "torrent not registered with this tracker\ntorrent banned", + "delete_record_retention_hours": 24, + "subscription_cleanup_history_type": "no", + "subscription_cleanup_history_scenes": [], + "recognition_guard_mode": "off", + "recognition_guard_notify": "off", + "recognition_guard_notify_interval": 3600, + "recognition_guard_tmdb_recheck_mode": "balanced_strict", + "recognition_guard_cache_maxsize": 1e5, + "recognition_guard_custom_config": "####### 配置说明 BEGIN #######\n# 1. 本配置只控制识别增强的策略覆盖和关键词,不控制通知、二次识别触发或缓存大小。\n# 2. 未配置或保持注释的项目均继承 recognition_guard_mode 当前模板。\n# 3. actions 的值可选:inherit / observe / soft_block / block:\n# - inherit:继承当前 recognition_guard_mode 模板,不单独覆盖。\n# - observe:只记录审计和可选通知,不移除候选,下载选择不受影响。\n# - soft_block:先从候选池移除;如果整轮候选被清空,且 empty_pool 策略允许,该候选可降级为 observe 恢复。\n# - block:从候选池移除,集合级保护也不得恢复;用于用户明确不想下载的风险。\n# 4. allow 只能抵消非 hard veto 风险;不能覆盖显式 ID 错配、明确类型/形态互串、目标范围完全不覆盖等 hard veto。\n# 5. block 是普通黑名单风险,动作由 mode 或 actions.user_block 决定;hard_block 才是一律强拦截。\n# 6. 正则使用 Python re 语法;非法正则会跳过对应条目并记录配置告警,不影响其他规则。\n# 7. keywords 下的内置证据词分组如果取消注释配置,表示替换该分组;未配置的分组继续使用内置默认。\n####### 配置说明 END #######\n\nactions:\n # 候选缺少年份。多站点用户可改为 block,少站点用户建议 inherit 或 observe。\n # missing_year: block\n\n # 候选全集范围明显大于目标窗口,例如目标缺 E08-E19,候选是全 60 集。\n # target_range_oversized: block\n\n # 命中 keywords.block 时的动作。\n # user_block: soft_block\n\n # 二次识别结果与订阅目标不一致。\n # secondary_identity_conflict: block\n\nempty_pool:\n # 整轮候选被识别增强清空时的恢复策略:recover_soft_block / never_recover。\n # policy: recover_soft_block\n\n # 即使动作是 soft_block,也不允许因整轮候选清空而恢复的原因码。\n # non_recoverable_codes:\n # - target_range_oversized\n # - missing_year\n\nkeywords:\n # 白名单:只抵消非 hard veto 风险。\n # allow:\n # - 官方合集\n\n # 普通黑名单:动作由 mode 或 actions.user_block 决定。\n # block:\n # - 低可信风险词\n\n # 强黑名单:所有启用模式下 hard veto;audit 只记录 would block。\n # hard_block:\n # - 强制错误词\n\n # 以下是内置证据词分组;如需覆盖某一组,取消注释并完整写出该组。\n # live_action:\n # - 真人版\n # - 电视剧版\n # - 实拍版\n # - 真人剧\n # animation:\n # - 动画\n # - 动漫\n # - 国漫\n # - 番剧\n # movie:\n # - 电影版\n # - 剧场版\n # - 劇場版\n # - '\\bMovie\\b'\n # tv:\n # - '\\bS\\d{1,3}(?:E\\d{1,4})?\\b'\n # - '第\\s*\\d+\\s*[集季]'\n # - '全\\s*\\d+\\s*集'\n", + "pending_enhanced_enabled": true, + "pending_download_enabled": true, + "auto_tv_pending_days": 0, + "auto_tv_pending_episodes": 1, + "pending_use_volatility": false, + "pause_enhanced_enabled": false, + "auto_pause_users": "", + "airing_pause_days": 30, + "movie_air_pause_days": 7, + "tv_air_pause_days": 14, + "movie_no_download_days": 365, + "tv_no_download_days": 180, + "no_download_actions": [], + "site_total_probe_enabled": false, + "paused_probe_reasons": ["no_download"], + "paused_probe_min_pause_days": 14, + "paused_probe_interval_hours": 72, + "best_version_type": "no", + "best_version_movie_remaining_days": 0, + "best_version_tv_remaining_days": 0, + "best_version_episode_to_full": false, + "best_version_backfill_enabled": false, + "backfill_best_version_now": false, + "completion_guard_mode": "balanced", + "site_completion_evidence_enabled": true, + "volatility_enabled": true, + "volatility_window_days": 3, + "cadence_enabled": true, + "cadence_multiplier": 2.5, + "cadence_min_window_days": 7, + "cadence_min_episodes": 3, + "season_cooldown_days": 14, + "verify_enabled": false, + "verify_interval_hours": 12, + "verify_retention_days": 180, + "timeout_release_days": 7, + "timeout_cadence_acceleration": true +}; + +function normalizeFiniteNumber(current, incoming) { + if (incoming === null || incoming === void 0) return current; + if (typeof incoming === "string" && !incoming.trim()) return current; + const parsed = typeof incoming === "number" ? incoming : Number(incoming); + return Number.isFinite(parsed) ? parsed : current; +} +function normalizeBoolean(defaultValue, incoming) { + if (incoming === null || incoming === void 0) return defaultValue; + if (typeof incoming === "boolean") return incoming; + if (typeof incoming === "string") { + return ["true", "on", "yes", "1", "guard"].includes(incoming.trim().toLowerCase()); + } + if (typeof incoming === "number") return incoming !== 0; + if (Array.isArray(incoming)) return incoming.length > 0; + if (typeof incoming === "object") return Object.keys(incoming).length > 0; + return Boolean(incoming); +} +function normalizeNumber(defaultValue, incoming) { + if (incoming === null || incoming === void 0) return defaultValue; + if (typeof incoming === "string" && !incoming.trim()) return defaultValue; + if (typeof incoming !== "number" && typeof incoming !== "string") return defaultValue; + const parsed = Number(incoming); + return Number.isFinite(parsed) ? parsed : defaultValue; +} +function normalizeString(defaultValue, incoming) { + return incoming === null || incoming === void 0 ? defaultValue : String(incoming); +} +function normalizeStringArray(defaultValue, incoming) { + if (Array.isArray(incoming)) { + return incoming.map((value) => String(value).trim()).filter(Boolean); + } + if (typeof incoming === "string") { + return incoming.split(",").map((value) => value.trim()).filter(Boolean); + } + return [...defaultValue]; +} +function normalizeSaeConfig(input) { + const source = input !== null && typeof input === "object" && !Array.isArray(input) ? input : {}; + const entries = Object.keys(configDefaults).map((key) => { + const defaultValue = configDefaults[key]; + const incoming = source[key]; + if (Array.isArray(defaultValue)) { + return [key, normalizeStringArray(defaultValue, incoming)]; + } + if (typeof defaultValue === "boolean") { + return [key, normalizeBoolean(defaultValue, incoming)]; + } + if (typeof defaultValue === "number") { + return [key, normalizeNumber(defaultValue, incoming)]; + } + return [key, normalizeString(defaultValue, incoming)]; + }); + return Object.fromEntries(entries); +} + +const {computed: computed$1,reactive} = await importShared('vue'); +function useConfigDraft(initialConfig) { + const initialSnapshot = normalizeSaeConfig(initialConfig); + const draft = reactive(structuredClone(initialSnapshot)); + const configKeys = Object.keys(initialSnapshot); + const changedKeys = computed$1( + () => configKeys.filter((key) => JSON.stringify(draft[key]) !== JSON.stringify(initialSnapshot[key])) + ); + const changedCount = computed$1(() => changedKeys.value.length); + function buildSavePayload() { + return normalizeSaeConfig(draft); + } + return { draft, changedCount, changedKeys, buildSavePayload }; +} + +const groups = [ + { key: "global", title: "全局运行", icon: "mdi-tune-variant", summary: "插件开关、通知、一次性动作与公共周期" }, + { + key: "cleanup", + title: "订阅清理", + icon: "mdi-delete-sweep-outline", + summary: "下载监控、删种、Tracker 与整理记录清理", + highRisk: true + }, + { key: "pending", title: "订阅待定", icon: "mdi-timer-sand", summary: "下载中与剧集目标未稳定时保持待定" }, + { + key: "pause", + title: "订阅暂停", + icon: "mdi-pause-circle-outline", + summary: "按用户、上映播出窗口和无下载策略暂停订阅" + }, + { key: "completion", title: "订阅补全", icon: "mdi-radar", summary: "站点集数探测与暂停订阅补搜" }, + { + key: "bestVersion", + title: "订阅洗版", + icon: "mdi-auto-fix", + summary: "洗版范围、时限、回填和分集转全集", + highRisk: true + }, + { + key: "guard", + title: "完结信号", + icon: "mdi-shield-check-outline", + summary: "完结守卫、站点证据、波动节奏和自动纠错" + }, + { + key: "recognition", + title: "识别增强", + icon: "mdi-account-search-outline", + summary: "候选准入、通知、二次识别和自定义策略" + } +]; +const fields = [ + { + "key": "enabled", + "label": "启用插件", + "group": "global", + "kind": "switch", + "hint": "开启后插件将处于激活状态" + }, + { + "key": "notify", + "label": "发送通知", + "group": "global", + "kind": "switch", + "hint": "是否在特定事件发生时发送通知" + }, + { + "key": "onlyonce", + "label": "立即运行一次", + "group": "global", + "kind": "switch", + "hint": "保存后立即运行一次全量巡检,执行后自动复位" + }, + { + "key": "reset_task", + "label": "重置数据", + "group": "global", + "kind": "switch", + "hint": "保存后将重置所有待定/暂停/监控等任务数据,执行后自动复位", + "risk": "danger" + }, + { + "key": "auto_check_interval_minutes", + "label": "通用巡检周期(分钟)", + "group": "global", + "kind": "select", + "hint": "站点采样、待定释放、无下载处理和清理周期", + "options": [ + { + "title": "10分钟", + "value": 10 + }, + { + "title": "20分钟", + "value": 20 + }, + { + "title": "30分钟", + "value": 30 + }, + { + "title": "60分钟", + "value": 60 + }, + { + "title": "120分钟", + "value": 120 + }, + { + "title": "240分钟", + "value": 240 + } + ], + "advanced": true + }, + { + "key": "download_check_interval_minutes", + "label": "下载检查周期(分钟)", + "group": "global", + "kind": "select", + "hint": "下载检查的周期,定时检查下载任务状态", + "options": [ + { + "title": "5分钟", + "value": 5 + }, + { + "title": "10分钟", + "value": 10 + }, + { + "title": "15分钟", + "value": 15 + }, + { + "title": "30分钟", + "value": 30 + }, + { + "title": "60分钟", + "value": 60 + }, + { + "title": "120分钟", + "value": 120 + } + ], + "advanced": true + }, + { + "key": "meta_check_interval_hours", + "label": "元数据检查周期(小时)", + "group": "global", + "kind": "select", + "hint": "元数据检查的周期,定时复核订阅元数据状态", + "options": [ + { + "title": "1小时", + "value": 1 + }, + { + "title": "3小时", + "value": 3 + }, + { + "title": "6小时", + "value": 6 + }, + { + "title": "12小时", + "value": 12 + }, + { + "title": "24小时", + "value": 24 + } + ], + "advanced": true + }, + { + "key": "best_version_cron", + "label": "洗版检查周期", + "group": "global", + "kind": "cron", + "hint": "洗版检查的周期,如 0 15 * * *" + }, + { + "key": "download_monitor_enabled", + "label": "下载超时自动删除", + "group": "cleanup", + "kind": "switch", + "hint": "订阅下载超时将自动删除种子", + "risk": "danger" + }, + { + "key": "manual_delete_listen", + "label": "监听手动删除种子", + "group": "cleanup", + "kind": "switch", + "hint": "监听用户手动删除的种子记录", + "risk": "danger" + }, + { + "key": "tracker_response_listen", + "label": "监听Tracker响应关键字", + "group": "cleanup", + "kind": "switch", + "hint": "命中Tracker响应关键字时将自动删除种子", + "risk": "danger" + }, + { + "key": "auto_search_when_delete", + "label": "删除后触发搜索补全", + "group": "cleanup", + "kind": "switch", + "hint": "删种后将自动触发搜索补全" + }, + { + "key": "skip_deletion", + "label": "跳过近期删除资源", + "group": "cleanup", + "kind": "switch", + "hint": "跳过最近删除的种子,避免再次下载" + }, + { + "key": "download_timeout_minutes", + "label": "下载超时时间(分钟)", + "group": "cleanup", + "kind": "number", + "hint": "作为下载进度观察窗口,窗口内进度增长低于阈值时视为超时", + "advanced": true + }, + { + "key": "download_progress_threshold", + "label": "下载超时进度阈值", + "group": "cleanup", + "kind": "number", + "hint": "超时窗口内下载进度增长低于N%时才删除", + "advanced": true + }, + { + "key": "download_queue_grace_multiplier", + "label": "下载排队宽限倍数", + "group": "cleanup", + "kind": "number", + "hint": "排队状态额外宽限N个超时窗口,0表示不宽限" + }, + { + "key": "download_retry_limit", + "label": "下载连续超时重试次数", + "group": "cleanup", + "kind": "number", + "hint": "连续低进度超时N次后保留种子并通知", + "advanced": true + }, + { + "key": "delete_exclude_tags", + "label": "排除标签", + "group": "cleanup", + "kind": "text", + "hint": "需要排除的标签,多个标签用逗号分隔" + }, + { + "key": "default_tracker_response", + "label": "Tracker响应关键字", + "group": "cleanup", + "kind": "textarea", + "hint": "每一行一个关键字,忽略大小写,支持正则表达式匹配", + "dialogOnly": true, + "advanced": true + }, + { + "key": "delete_record_retention_hours", + "label": "删除记录保留(小时)", + "group": "cleanup", + "kind": "number", + "hint": "定时清理N小时前的删除记录", + "advanced": true + }, + { + "key": "subscription_cleanup_history_type", + "label": "清理整理记录范围", + "group": "cleanup", + "kind": "select", + "hint": "订阅下载前清理旧整理记录、源文件和入库前目标文件的媒体类型范围(破坏性)", + "options": [ + { + "title": "关闭", + "value": "no" + }, + { + "title": "全部", + "value": "all" + }, + { + "title": "电影", + "value": "movie" + }, + { + "title": "剧集", + "value": "tv" + } + ], + "risk": "danger" + }, + { + "key": "subscription_cleanup_history_scenes", + "label": "清理整理记录场景", + "group": "cleanup", + "kind": "multi-select", + "hint": "选择普通订阅、洗版订阅或分集洗版下载时触发订阅清理", + "options": [ + { + "title": "普通订阅", + "value": "normal" + }, + { + "title": "洗版订阅", + "value": "best_version" + }, + { + "title": "分集洗版", + "value": "best_version_episode" + } + ], + "risk": "danger" + }, + { + "key": "recognition_guard_mode", + "label": "识别增强模式", + "group": "recognition", + "kind": "select", + "hint": "在自动下载前复核订阅候选是否像当前订阅目标", + "options": [ + { + "title": "关闭", + "value": "off" + }, + { + "title": "审计", + "value": "audit" + }, + { + "title": "宽松", + "value": "loose" + }, + { + "title": "平衡", + "value": "balanced" + }, + { + "title": "严格", + "value": "strict" + } + ], + "risk": "danger" + }, + { + "key": "recognition_guard_notify", + "label": "识别增强通知", + "group": "recognition", + "kind": "select", + "hint": "控制识别增强消息推送,不影响审计日志", + "options": [ + { + "title": "关闭", + "value": "off" + }, + { + "title": "摘要", + "value": "summary" + }, + { + "title": "明细", + "value": "detail" + }, + { + "title": "全部", + "value": "all" + } + ] + }, + { + "key": "recognition_guard_notify_interval", + "label": "识别增强通知限频(秒)", + "group": "recognition", + "kind": "number", + "hint": "同订阅同动作同原因的通知限频秒数", + "advanced": true + }, + { + "key": "recognition_guard_tmdb_recheck_mode", + "label": "识别增强二次识别", + "group": "recognition", + "kind": "select", + "hint": "控制二次识别触发范围", + "options": [ + { + "title": "关闭", + "value": "off" + }, + { + "title": "全部", + "value": "all" + }, + { + "title": "严格", + "value": "strict" + }, + { + "title": "平衡和严格", + "value": "balanced_strict" + } + ] + }, + { + "key": "recognition_guard_cache_maxsize", + "label": "识别增强缓存大小", + "group": "recognition", + "kind": "number", + "hint": "缓存二次识别结果,避免重复识别", + "advanced": true + }, + { + "key": "recognition_guard_custom_config", + "label": "自定义识别规则", + "group": "recognition", + "kind": "textarea", + "hint": "仅在内置规则无法满足时编辑,留空则继承当前模式", + "risk": "danger" + }, + { + "key": "pending_enhanced_enabled", + "label": "自动待定剧集订阅", + "group": "pending", + "kind": "switch", + "hint": "自动标记订阅剧集为待定状态,避免提前完成订阅" + }, + { + "key": "pending_download_enabled", + "label": "自动待定下载中订阅", + "group": "pending", + "kind": "switch", + "hint": "存在进行中下载时自动标记待定,避免提前完成订阅" + }, + { + "key": "auto_tv_pending_days", + "label": "剧集待定天数", + "group": "pending", + "kind": "number", + "hint": "当前日期小于上映日期加N天,则视为待定,为0时不处理", + "advanced": true + }, + { + "key": "auto_tv_pending_episodes", + "label": "剧集待定集数", + "group": "pending", + "kind": "number", + "hint": "剧集数小于等于设置的集数,则视为待定,为0时不处理" + }, + { + "key": "pending_use_volatility", + "label": "待定参考变更速率", + "group": "pending", + "kind": "switch", + "hint": "接近完结且总集数变化时提前待定" + }, + { + "key": "pause_enhanced_enabled", + "label": "自动暂停订阅", + "group": "pause", + "kind": "switch", + "hint": "自动标记订阅为暂停状态,避免无意义的请求" + }, + { + "key": "auto_pause_users", + "label": "自动暂停新增订阅的用户(逗号分隔)", + "group": "pause", + "kind": "text", + "hint": "名单内用户新增订阅时将自动暂停,多个用户用逗号分隔,为空时不启用" + }, + { + "key": "airing_pause_days", + "label": "即将播出暂停天数", + "group": "pause", + "kind": "number", + "hint": "已存在最新播出集,且下集距当前日期大于N天,则视为暂停,为0时不处理", + "advanced": true + }, + { + "key": "movie_air_pause_days", + "label": "电影上映暂停天数", + "group": "pause", + "kind": "number", + "hint": "当前日期小于上映日期减N天,则视为暂停,为0时不处理", + "advanced": true + }, + { + "key": "tv_air_pause_days", + "label": "剧集上映暂停天数", + "group": "pause", + "kind": "number", + "hint": "当前日期小于开播日期减N天,则视为暂停,为0时不处理", + "advanced": true + }, + { + "key": "movie_no_download_days", + "label": "电影无下载处理天数", + "group": "pause", + "kind": "number", + "hint": "电影上映后N天内无新的订阅下载,则按策略处理,为0时不处理", + "advanced": true + }, + { + "key": "tv_no_download_days", + "label": "剧集无下载处理天数", + "group": "pause", + "kind": "number", + "hint": "剧集上映后N天内无新的订阅下载,则按策略处理,为0时不处理", + "advanced": true + }, + { + "key": "no_download_actions", + "label": "无下载处理策略", + "group": "pause", + "kind": "multi-select", + "hint": "选择无下载时的处理策略", + "options": [ + { + "title": "暂停电影订阅", + "value": "pause_movie" + }, + { + "title": "暂停剧集订阅", + "value": "pause_tv" + }, + { + "title": "完成电影订阅", + "value": "complete_movie" + }, + { + "title": "完成剧集订阅", + "value": "complete_tv" + }, + { + "title": "删除电影订阅", + "value": "delete_movie" + }, + { + "title": "删除剧集订阅", + "value": "delete_tv" + } + ], + "risk": "danger" + }, + { + "key": "site_total_probe_enabled", + "label": "站点集数探测", + "group": "completion", + "kind": "switch", + "hint": "用站点缓存资源辅助发现目标集数不足" + }, + { + "key": "paused_probe_reasons", + "label": "暂停订阅补搜场景", + "group": "completion", + "kind": "multi-select", + "hint": "选择允许低频补搜的暂停原因", + "options": [ + { + "title": "无下载", + "value": "no_download" + }, + { + "title": "上映/开播", + "value": "pre_air" + }, + { + "title": "播出间隔", + "value": "airing_gap" + }, + { + "title": "用户名", + "value": "auto_user" + }, + { + "title": "外部暂停", + "value": "external" + }, + { + "title": "全部", + "value": "all" + } + ] + }, + { + "key": "paused_probe_min_pause_days", + "label": "暂停满N天后补搜", + "group": "completion", + "kind": "number", + "hint": "暂停达到天数后开始补搜,0 表示不处理", + "advanced": true + }, + { + "key": "paused_probe_interval_hours", + "label": "补搜间隔(小时)", + "group": "completion", + "kind": "select", + "hint": "同一订阅两次补搜的最小间隔", + "options": [ + { + "title": "24", + "value": 24 + }, + { + "title": "48", + "value": 48 + }, + { + "title": "72", + "value": 72 + }, + { + "title": "96", + "value": 96 + }, + { + "title": "120", + "value": 120 + }, + { + "title": "144", + "value": 144 + } + ], + "advanced": true + }, + { + "key": "best_version_type", + "label": "洗版类型", + "group": "bestVersion", + "kind": "select", + "hint": "选择需要自动洗版的类型,关闭时不自动创建和巡检洗版订阅", + "options": [ + { + "title": "关闭", + "value": "no" + }, + { + "title": "全部", + "value": "all" + }, + { + "title": "电影", + "value": "movie" + }, + { + "title": "剧集", + "value": "tv" + }, + { + "title": "剧集(分集下载)", + "value": "tv_episode" + } + ], + "risk": "danger" + }, + { + "key": "best_version_movie_remaining_days", + "label": "电影洗版时限(天)", + "group": "bestVersion", + "kind": "number", + "hint": "电影洗版订阅达到指定天数后自动终止,有下载则按最新时间计算,为0时不限", + "advanced": true + }, + { + "key": "best_version_tv_remaining_days", + "label": "剧集洗版时限(天)", + "group": "bestVersion", + "kind": "number", + "hint": "剧集洗版订阅达到指定天数后自动终止,有下载则按最新时间计算,为0时不限", + "advanced": true + }, + { + "key": "best_version_episode_to_full", + "label": "分集转全集", + "group": "bestVersion", + "kind": "switch", + "hint": "订阅目标集数满足时,从分集洗版切换为全集洗版", + "risk": "danger" + }, + { + "key": "best_version_backfill_enabled", + "label": "回填已存在集", + "group": "bestVersion", + "kind": "switch", + "hint": "新建或转分集洗版时回填媒体库已有集,避免重复下载" + }, + { + "key": "backfill_best_version_now", + "label": "立即扫描存量并回填", + "group": "bestVersion", + "kind": "switch", + "hint": "保存后对存量分集洗版订阅执行一次回填,执行后自动复位", + "risk": "danger" + }, + { + "key": "completion_guard_mode", + "label": "完结守卫模式", + "group": "guard", + "kind": "select", + "hint": "选择完成前复核强度,默认使用平衡策略", + "options": [ + { + "title": "关闭", + "value": "off" + }, + { + "title": "严格", + "value": "strict" + }, + { + "title": "平衡", + "value": "balanced" + }, + { + "title": "宽松", + "value": "loose" + } + ] + }, + { + "key": "site_completion_evidence_enabled", + "label": "站点完结信号", + "group": "guard", + "kind": "switch", + "hint": "使用站点资源标题佐证完结信号" + }, + { + "key": "volatility_enabled", + "label": "变更速率信号", + "group": "guard", + "kind": "switch", + "hint": "总集数近期变化时视为不稳定" + }, + { + "key": "volatility_window_days", + "label": "变更速率窗口(天)", + "group": "guard", + "kind": "number", + "hint": "统计总集数变化的天数,越长越保守", + "advanced": true + }, + { + "key": "cadence_enabled", + "label": "播出节奏信号", + "group": "guard", + "kind": "switch", + "hint": "按已播间隔判断等待期,不会直接判定完结" + }, + { + "key": "cadence_multiplier", + "label": "节奏窗口系数", + "group": "guard", + "kind": "number", + "hint": "放大预计等待时间,数值越大等待越久" + }, + { + "key": "cadence_min_window_days", + "label": "节奏窗口下限(天)", + "group": "guard", + "kind": "number", + "hint": "预计等待时间不得少于设置天数", + "advanced": true + }, + { + "key": "cadence_min_episodes", + "label": "节奏参与最少集数", + "group": "guard", + "kind": "number", + "hint": "已播集数达到设置值后才计算播出间隔" + }, + { + "key": "season_cooldown_days", + "label": "季冷却期(天)", + "group": "guard", + "kind": "number", + "hint": "最后一集播出后继续观察的天数", + "advanced": true + }, + { + "key": "verify_enabled", + "label": "自动纠错", + "group": "guard", + "kind": "switch", + "hint": "完成后检查集数,增加时自动重建订阅" + }, + { + "key": "verify_interval_hours", + "label": "自动纠错间隔(小时)", + "group": "guard", + "kind": "number", + "hint": "完成后重新检查集数的间隔", + "advanced": true + }, + { + "key": "verify_retention_days", + "label": "快照保留(天)", + "group": "guard", + "kind": "number", + "hint": "完成快照按设置天数保留并自动清理,默认180天", + "advanced": true + }, + { + "key": "timeout_release_days", + "label": "完成前观察天数", + "group": "guard", + "kind": "number", + "hint": "完成前观察允许保留的最长天数", + "advanced": true + }, + { + "key": "timeout_cadence_acceleration", + "label": "按节奏加速释放", + "group": "guard", + "kind": "switch", + "hint": "等待期结束时缩短观察期限" + } +]; + +const supportedLocales = /* @__PURE__ */ new Set(["zh-CN", "zh-TW", "en-US"]); +function normalizeLocale(source) { + let current = source; + const visited = /* @__PURE__ */ new Set(); + while (current && typeof current === "object" && "value" in current) { + if (visited.has(current)) return "zh-CN"; + visited.add(current); + current = current.value; + } + if (typeof current !== "string") return "zh-CN"; + const normalized = current.trim().replace("_", "-").toLowerCase(); + const locale = normalized === "zh-cn" ? "zh-CN" : normalized === "zh-tw" ? "zh-TW" : normalized === "en-us" ? "en-US" : "zh-CN"; + return supportedLocales.has(locale) ? locale : "zh-CN"; +} +const messages = { + "zh-CN": { + "config.changedCount": "{count} 项待保存", + "config.changes": "本次修改", + "config.moreChanges": "另有 {count} 项", + "config.runOnce": "运行一次", + "config.save": "保存修改", + "config.close": "关闭", + "config.cadence": "运行节奏", + "config.generalInspection": "通用巡检", + "config.downloadInspection": "下载检查", + "config.metadataInspection": "元数据检查", + "config.bestVersionInspection": "洗版检查", + "config.everyMinutes": "每 {value} 分钟", + "config.everyHours": "每 {value} 小时", + "config.notScheduled": "未设置", + "config.activeDomains": "已启用能力", + "config.help": "插件帮助", + "config.aboutPlugin": "关于插件", + "config.aboutDescription": "多场景管理订阅,实现订阅全生命周期管理", + "config.viewDocs": "查看文档", + "config.plugin": "插件", + "config.settings": "插件设置", + "config.selectGroup": "选择配置分组", + "config.done": "完成", + "config.edit": "编辑", + "config.decrease": "减小{label}", + "config.increase": "增大{label}", + "config.editLabel": "编辑{label}", + "config.yamlTitle": "自定义识别规则", + "config.runtime": "运行概况", + "config.runtimeLoading": "正在读取运行概况", + "config.runtimeUnavailable": "运行概况暂不可用", + "config.pendingCount": "待定订阅", + "config.monitoredCount": "下载任务", + "config.enabled": "启用", + "config.off": "关闭", + "config.cronPlaceholder": "5 位 CRON 表达式", + "config.title": "订阅助手(增强版)", + "domain.completionGuard": "完结守卫模式", + "domain.pending": "待定增强", + "domain.pause": "暂停优化", + "domain.bestVersion": "自动洗版", + "domain.download": "下载管理", + "domain.verify": "完成后验证", + "domain.siteTotal": "站点集数探测", + "domain.siteCompletion": "站点完结信号", + "domain.recognition": "识别增强", + "section.running": "运行状态", + "section.oneTime": "一次性动作", + "section.schedule": "公共周期", + "section.download": "下载任务处理", + "section.timeout": "超时与重试", + "section.cleanup": "订阅记录清理", + "section.pending": "待定策略", + "section.tvDecision": "剧集判定", + "section.autoPause": "自动暂停", + "section.airing": "上映与播出窗口", + "section.noDownload": "无下载处理", + "section.siteProbe": "站点集数探测", + "section.pausedProbe": "暂停订阅补搜", + "section.bestVersionScope": "洗版范围", + "section.backfill": "转换与回填", + "section.guard": "守卫信号", + "section.cadence": "播出节奏", + "section.correction": "纠错与释放", + "section.recognition": "识别策略", + "section.custom": "自定义规则" + }, + "zh-TW": { + "config.changedCount": "{count} 項待儲存", + "config.changes": "本次修改", + "config.moreChanges": "另有 {count} 項", + "config.runOnce": "執行一次", + "config.save": "儲存修改", + "config.close": "關閉", + "config.cadence": "執行節奏", + "config.generalInspection": "通用巡檢", + "config.downloadInspection": "下載檢查", + "config.metadataInspection": "元資料檢查", + "config.bestVersionInspection": "洗版檢查", + "config.everyMinutes": "每 {value} 分鐘", + "config.everyHours": "每 {value} 小時", + "config.notScheduled": "未設定", + "config.activeDomains": "已啟用能力", + "config.help": "外掛說明", + "config.aboutPlugin": "關於外掛", + "config.aboutDescription": "多場景管理訂閱,實現訂閱全生命週期管理", + "config.viewDocs": "查看文件", + "config.plugin": "外掛", + "config.settings": "外掛設定", + "config.selectGroup": "選擇設定分組", + "config.done": "完成", + "config.edit": "編輯", + "config.decrease": "減少{label}", + "config.increase": "增加{label}", + "config.editLabel": "編輯{label}", + "config.yamlTitle": "自訂識別規則", + "config.runtime": "執行概況", + "config.runtimeLoading": "正在讀取執行概況", + "config.runtimeUnavailable": "執行概況暫不可用", + "config.pendingCount": "待定訂閱", + "config.monitoredCount": "下載任務", + "config.enabled": "啟用", + "config.off": "關閉", + "config.cronPlaceholder": "5 位 CRON 表示式", + "config.title": "訂閱助手(增強版)", + "domain.completionGuard": "完結守衛模式", + "domain.pending": "待定增強", + "domain.pause": "暫停最佳化", + "domain.bestVersion": "自動洗版", + "domain.download": "下載管理", + "domain.verify": "完成後驗證", + "domain.siteTotal": "站點集數探測", + "domain.siteCompletion": "站點完結訊號", + "domain.recognition": "識別增強", + "section.running": "執行狀態", + "section.oneTime": "單次操作", + "section.schedule": "共用週期", + "section.download": "下載任務處理", + "section.timeout": "逾時與重試", + "section.cleanup": "訂閱記錄清理", + "section.pending": "待定策略", + "section.tvDecision": "影集判定", + "section.autoPause": "自動暫停", + "section.airing": "上映與播出窗口", + "section.noDownload": "無下載處理", + "section.siteProbe": "站點集數探測", + "section.pausedProbe": "暫停訂閱補搜", + "section.bestVersionScope": "洗版範圍", + "section.backfill": "轉換與回填", + "section.guard": "守衛訊號", + "section.cadence": "播出節奏", + "section.correction": "修正與釋放", + "section.recognition": "識別策略", + "section.custom": "自訂規則" + }, + "en-US": { + "config.changedCount": "{count} to save", + "config.changes": "Changes", + "config.moreChanges": "{count} more", + "config.runOnce": "Run once", + "config.save": "Save changes", + "config.close": "Close", + "config.cadence": "Run cadence", + "config.generalInspection": "General inspection", + "config.downloadInspection": "Download checks", + "config.metadataInspection": "Metadata checks", + "config.bestVersionInspection": "Best-version checks", + "config.everyMinutes": "Every {value} min", + "config.everyHours": "Every {value} hr", + "config.notScheduled": "Not set", + "config.activeDomains": "Active capabilities", + "config.help": "Plugin help", + "config.aboutPlugin": "About plugin", + "config.aboutDescription": "Manage subscriptions across scenarios and throughout their lifecycle", + "config.viewDocs": "View docs", + "config.plugin": "Plugins", + "config.settings": "Plugin settings", + "config.selectGroup": "Select settings group", + "config.done": "Done", + "config.edit": "Edit", + "config.decrease": "Decrease {label}", + "config.increase": "Increase {label}", + "config.editLabel": "Edit {label}", + "config.yamlTitle": "Custom recognition rules", + "config.runtime": "Runtime summary", + "config.runtimeLoading": "Loading runtime summary", + "config.runtimeUnavailable": "Runtime summary unavailable", + "config.pendingCount": "Pending subscriptions", + "config.monitoredCount": "Downloads", + "config.enabled": "Enabled", + "config.off": "Off", + "config.cronPlaceholder": "5-field CRON expression", + "config.title": "Subscribe Assistant (Enhanced)", + "domain.completionGuard": "Completion guard mode", + "domain.pending": "Pending enhancement", + "domain.pause": "Pause optimization", + "domain.bestVersion": "Automatic upgrades", + "domain.download": "Download management", + "domain.verify": "Post-completion verification", + "domain.siteTotal": "Site episode probe", + "domain.siteCompletion": "Site completion signal", + "domain.recognition": "Recognition", + "section.running": "Runtime state", + "section.oneTime": "One-time actions", + "section.schedule": "Shared schedules", + "section.download": "Download handling", + "section.timeout": "Timeouts and retries", + "section.cleanup": "Subscription cleanup", + "section.pending": "Pending policy", + "section.tvDecision": "TV decisions", + "section.autoPause": "Automatic pause", + "section.airing": "Release and airing windows", + "section.noDownload": "No-download handling", + "section.siteProbe": "Site episode probe", + "section.pausedProbe": "Paused subscription search", + "section.bestVersionScope": "Best-version scope", + "section.backfill": "Conversion and backfill", + "section.guard": "Guard signals", + "section.cadence": "Airing cadence", + "section.correction": "Correction and release", + "section.recognition": "Recognition policy", + "section.custom": "Custom rules" + } +}; +function t(localeSource, key, params = {}) { + const locale = normalizeLocale(localeSource); + const template = messages[locale][key] ?? messages["zh-CN"][key]; + if (!template) throw new Error(`Missing translation key: ${key}`); + return template.replace( + /\{(\w+)\}/g, + (match, name) => Object.prototype.hasOwnProperty.call(params, name) ? String(params[name]) : match + ); +} +const groupTranslations = { + global: { + tw: ["全域執行", "外掛開關、通知、單次操作與共用週期"], + en: ["General", "Plugin state, notifications, one-time actions, and shared schedules"] + }, + cleanup: { + tw: ["訂閱清理", "下載監控、刪除種子、Tracker 與整理記錄清理"], + en: ["Cleanup", "Download monitoring, torrent removal, Tracker rules, and history cleanup"] + }, + pending: { + tw: ["訂閱待定", "下載中或集數目標尚未穩定時保持待定"], + en: ["Pending", "Keep subscriptions pending while downloads or episode targets are unsettled"] + }, + pause: { + tw: ["訂閱暫停", "依使用者、播出窗口與無下載策略暫停訂閱"], + en: ["Pause", "Pause subscriptions by user, release window, or no-download policy"] + }, + completion: { + tw: ["訂閱補全", "站點集數探測與暫停訂閱補搜"], + en: ["Completion", "Site episode probes and paused subscription searches"] + }, + bestVersion: { + tw: ["訂閱洗版", "洗版範圍、時限、回填與分集轉全集"], + en: ["Best version", "Upgrade scope, time limits, backfill, and episode-to-season conversion"] + }, + guard: { + tw: ["完結訊號", "完結守衛、站點證據、波動節奏與自動修正"], + en: ["Completion guard", "Completion checks, site evidence, cadence, and automatic correction"] + }, + recognition: { + tw: ["識別增強", "候選准入、通知、二次識別與自訂策略"], + en: ["Recognition", "Candidate checks, notifications, re-identification, and custom policies"] + } +}; +const englishFields = { + enabled: ["Enable plugin", "Activate the plugin and register its scheduled tasks"], + notify: ["Send notifications", "Send notifications when relevant events occur"], + onlyonce: ["Run once now", "Run a full inspection after saving, then reset automatically"], + reset_task: [ + "Reset data", + "Reset all pending, paused, and monitored task data after saving, then reset automatically" + ], + auto_check_interval_minutes: [ + "General check interval (minutes)", + "Interval for site sampling, pending release, no-download handling, and cleanup" + ], + download_check_interval_minutes: ["Download check interval (minutes)", "How often download task status is checked"], + meta_check_interval_hours: ["Metadata check interval (hours)", "How often subscription metadata is reviewed"], + best_version_cron: ["Best-version schedule", "CRON schedule for best-version checks, for example 0 15 * * *"], + download_monitor_enabled: ["Remove stalled downloads", "Automatically remove subscription torrents that time out"], + manual_delete_listen: ["Watch manual torrent removal", "Record torrents manually removed by the user"], + tracker_response_listen: [ + "Watch Tracker response keywords", + "Remove torrents when a configured Tracker response keyword matches" + ], + auto_search_when_delete: ["Search after removal", "Trigger a completion search after removing a torrent"], + skip_deletion: ["Skip recently removed releases", "Avoid downloading recently removed torrents again"], + download_timeout_minutes: [ + "Download timeout (minutes)", + "Observation window used to detect downloads with insufficient progress" + ], + download_progress_threshold: [ + "Download progress threshold", + "Remove only when progress increases by less than N% during the timeout window" + ], + download_queue_grace_multiplier: [ + "Download queue grace multiplier", + "Allow N additional timeout windows while the downloader explicitly reports the task as queued; 0 disables the grace period" + ], + download_retry_limit: [ + "Consecutive timeout limit", + "Keep the torrent and notify after N consecutive low-progress timeouts" + ], + delete_exclude_tags: ["Excluded tags", "Comma-separated tags that must not be processed"], + default_tracker_response: [ + "Tracker response keywords", + "One keyword per line; case-insensitive regular expressions are supported" + ], + delete_record_retention_hours: [ + "Removal history retention (hours)", + "Periodically remove deletion records older than N hours" + ], + subscription_cleanup_history_type: [ + "Cleanup media scope", + "Media types whose old transfer records and files are removed before download" + ], + subscription_cleanup_history_scenes: [ + "Cleanup trigger scenarios", + "Choose which subscription download scenarios trigger cleanup" + ], + recognition_guard_mode: [ + "Recognition mode", + "Review whether a candidate matches the subscription target before automatic download" + ], + recognition_guard_notify: ["Recognition notifications", "Control recognition messages without affecting audit logs"], + recognition_guard_notify_interval: [ + "Notification rate limit (seconds)", + "Minimum interval for the same subscription, action, and reason" + ], + recognition_guard_tmdb_recheck_mode: ["Secondary recognition", "Control when secondary recognition is performed"], + recognition_guard_cache_maxsize: [ + "Recognition cache size", + "Cache secondary recognition results to avoid duplicate requests" + ], + recognition_guard_custom_config: [ + "Custom recognition rules", + "Edit only when built-in rules are insufficient; leave empty to inherit the current mode" + ], + pending_enhanced_enabled: [ + "Automatically pend TV subscriptions", + "Mark TV subscriptions pending to avoid completing them too early" + ], + pending_download_enabled: ["Pend active downloads", "Keep subscriptions pending while downloads are in progress"], + auto_tv_pending_days: ["TV pending days", "Keep pending before the release date plus N days; 0 disables this rule"], + auto_tv_pending_episodes: [ + "TV pending episode count", + "Keep pending when the episode count is at or below this value; 0 disables this rule" + ], + pending_use_volatility: [ + "Use change rate for pending", + "Pend early when the total episode count changes near completion" + ], + pause_enhanced_enabled: ["Automatically pause subscriptions", "Pause subscriptions to avoid unnecessary requests"], + auto_pause_users: [ + "Auto-pause users (comma-separated)", + "Pause new subscriptions from listed users; leave empty to disable" + ], + airing_pause_days: [ + "Upcoming episode pause days", + "Pause when the next episode is more than N days away; 0 disables this rule" + ], + movie_air_pause_days: [ + "Movie release pause days", + "Pause until N days before the movie release date; 0 disables this rule" + ], + tv_air_pause_days: ["TV premiere pause days", "Pause until N days before the TV premiere date; 0 disables this rule"], + movie_no_download_days: [ + "Movie no-download days", + "Apply the selected policy when no movie download occurs within N days; 0 disables it" + ], + tv_no_download_days: [ + "TV no-download days", + "Apply the selected policy when no TV download occurs within N days; 0 disables it" + ], + no_download_actions: ["No-download actions", "Choose the actions to apply when no download is found"], + site_total_probe_enabled: [ + "Probe site episode totals", + "Use cached site releases to detect an incomplete episode target" + ], + paused_probe_reasons: ["Paused search scenarios", "Choose pause reasons that allow low-frequency searches"], + paused_probe_min_pause_days: [ + "Search after N paused days", + "Start searching after this many paused days; 0 disables it" + ], + paused_probe_interval_hours: [ + "Search interval (hours)", + "Minimum interval between two searches for the same subscription" + ], + best_version_type: [ + "Best-version type", + "Select media types for automatic upgrades; Off disables creation and checks" + ], + best_version_movie_remaining_days: [ + "Movie upgrade time limit (days)", + "Stop movie upgrade subscriptions after this period; 0 means unlimited" + ], + best_version_tv_remaining_days: [ + "TV upgrade time limit (days)", + "Stop TV upgrade subscriptions after this period; 0 means unlimited" + ], + best_version_episode_to_full: [ + "Convert episodes to full season", + "Switch from episode upgrades to a full-season upgrade when the target is met" + ], + best_version_backfill_enabled: [ + "Backfill existing episodes", + "Backfill library episodes when creating or converting an episode upgrade" + ], + backfill_best_version_now: [ + "Scan and backfill now", + "Backfill existing episode-upgrade subscriptions after saving, then reset automatically" + ], + completion_guard_mode: [ + "Completion guard mode", + "Choose the review strength used before completion; Balanced is the default" + ], + site_completion_evidence_enabled: [ + "Use site completion evidence", + "Use site release titles as supporting completion evidence" + ], + volatility_enabled: ["Episode-count change signal", "Treat recent total episode count changes as unstable"], + volatility_window_days: ["Change-rate window (days)", "Number of days used to measure total episode count changes"], + cadence_enabled: [ + "Airing cadence signal", + "Estimate the waiting period from airing intervals without directly marking completion" + ], + cadence_multiplier: ["Cadence window multiplier", "Increase the estimated waiting period; higher values wait longer"], + cadence_min_window_days: [ + "Minimum cadence window (days)", + "The estimated waiting period cannot be shorter than this value" + ], + cadence_min_episodes: [ + "Minimum episodes for cadence", + "Calculate airing intervals only after this many episodes have aired" + ], + season_cooldown_days: ["Season cooldown (days)", "Continue observing for this many days after the last episode airs"], + verify_enabled: [ + "Automatic correction", + "Recheck completed episode counts and rebuild subscriptions when the count increases" + ], + verify_interval_hours: ["Correction interval (hours)", "Interval for rechecking episode counts after completion"], + verify_retention_days: [ + "Snapshot retention (days)", + "Retain completion snapshots for this many days; default is 180" + ], + timeout_release_days: [ + "Pre-completion observation days", + "Maximum number of days allowed for pre-completion observation" + ], + timeout_cadence_acceleration: [ + "Accelerate release by cadence", + "Shorten the observation period after the cadence waiting window ends" + ] +}; +const traditionalPhrases = [ + ["插件", "外掛"], + ["启用", "啟用"], + ["发送", "傳送"], + ["通知", "通知"], + ["运行", "執行"], + ["重置", "重設"], + ["数据", "資料"], + ["检查", "檢查"], + ["周期", "週期"], + ["下载", "下載"], + ["订阅", "訂閱"], + ["删除", "刪除"], + ["记录", "記錄"], + ["监听", "監聽"], + ["关键字", "關鍵字"], + ["进度", "進度"], + ["连续", "連續"], + ["时", "時"], + ["分钟", "分鐘"], + ["小时", "小時"], + ["自动", "自動"], + ["状态", "狀態"], + ["配置", "設定"], + ["识别", "識別"], + ["增强", "增強"], + ["自定义", "自訂"], + ["剧集", "影集"], + ["电影", "電影"], + ["上映", "上映"], + ["暂停", "暫停"], + ["用户", "使用者"], + ["选择", "選擇"], + ["范围", "範圍"], + ["场景", "情境"], + ["关闭", "關閉"], + ["全部", "全部"], + ["严格", "嚴格"], + ["宽松", "寬鬆"], + ["平衡", "平衡"], + ["仅", "僅"], + ["完结", "完結"], + ["信号", "訊號"], + ["纠错", "修正"], + ["变更", "變更"], + ["节奏", "節奏"], + ["间隔", "間隔"], + ["默认", "預設"], + ["目标", "目標"], + ["满足", "符合"], + ["转换", "轉換"], + ["转", "轉"], + ["扫描", "掃描"], + ["存量", "既有"], + ["回填", "回填"], + ["媒体库", "媒體庫"], + ["整理", "整理"], + ["文件", "檔案"], + ["多个", "多個"], + ["为空", "留空"], + ["表示", "表示"], + ["开启", "開啟"], + ["发生", "發生"], + ["复核", "複核"], + ["触发", "觸發"], + ["清理", "清理"], + ["待定", "待定"], + ["完成", "完成"], + ["总集数", "總集數"], + ["集数", "集數"], + ["天数", "天數"], + ["策略", "策略"], + ["模式", "模式"], + ["缓存", "快取"], + ["大小", "大小"], + ["支持", "支援"], + ["处于激活状态", "處於啟用狀態"], + ["正则表达式", "正規表示式"], + ["媒体类型", "媒體類型"], + ["审计", "稽核"], + ["消息推送", "訊息推送"], + ["站点", "站點"], + ["搜索", "搜尋"], + ["补搜", "補搜"], + ["巡检", "巡檢"], + ["种子", "種子"], + ["任务", "任務"], + ["标签", "標籤"], + ["请求", "請求"], + ["名单", "名單"], + ["候选", "候選"], + ["标题", "標題"], + ["诊断", "診斷"], + ["类型", "類型"], + ["创建", "建立"], + ["终止", "終止"], + ["守卫", "守衛"], + ["统计", "統計"], + ["判断", "判斷"], + ["预计", "預計"], + ["参与", "參與"], + ["观察", "觀察"], + ["释放", "釋放"], + ["结果", "結果"], + ["动作", "動作"], + ["原因", "原因"], + ["频", "頻"], + ["秒数", "秒數"], + ["资源", "資源"], + ["辅助", "輔助"], + ["不足", "不足"], + ["允许", "允許"], + ["达到", "達到"], + ["两次", "兩次"], + ["轮数", "輪數"], + ["提醒", "提醒"], + ["强度", "強度"], + ["佐证", "佐證"], + ["稳定", "穩定"], + ["增加", "增加"], + ["重新", "重新"], + ["最后", "最後"], + ["继续", "繼續"], + ["结束", "結束"], + ["缩短", "縮短"], + ["保存", "儲存"], + ["采样", "取樣"], + ["补全", "補全"], + ["手动", "手動"], + ["跳过", "略過"], + ["作为", "作為"], + ["低于", "低於"], + ["视为", "視為"], + ["一个", "一個"], + ["大小写", "大小寫"], + ["精准", "精準"], + ["入库", "入庫"], + ["日志", "日誌"], + ["明细", "明細"], + ["覆盖", "覆蓋"], + ["进行", "進行"], + ["设置", "設定"], + ["等于", "等於"], + ["参考", "參考"], + ["意义", "意義"], + ["逗号", "逗號"], + ["探测", "探測"], + ["多少轮", "多少輪"], + ["计算", "計算"], + ["新建", "建立"], + ["切换", "切換"], + ["于", "於"], + ["视", "視"], + ["采", "採"], + ["补", "補"], + ["删", "刪"], + ["轮", "輪"], + ["算", "算"], + ["后", "後"], + ["会", "會"], + ["将", "將"], + ["处", "處"], + ["为", "為"], + ["与", "與"], + ["发", "發"], + ["过", "過"], + ["这", "這"], + ["则", "則"], + ["无", "無"], + ["设", "設"], + ["选", "選"], + ["线", "線"], + ["响", "響"], + ["应", "應"], + ["种", "種"], + ["从", "從"], + ["开", "開"], + ["进", "進"], + ["间", "間"], + ["数", "數"], + ["长", "長"], + ["现", "現"], + ["还", "還"], + ["较", "較"], + ["达", "達"], + ["实", "實"], + ["复", "複"], + ["对", "對"], + ["内", "內"], + ["样", "樣"], + ["并", "並"], + ["当", "當"], + ["监", "監"], + ["执", "執"], + ["检", "檢"], + ["动", "動"], + ["试", "試"], + ["阈", "閾"], + ["值", "值"], + ["写", "寫"], + ["号", "號"], + ["旧", "舊"], + ["库", "庫"], + ["坏", "壞"], + ["记", "記"], + ["覆", "覆"], + ["标", "標"], + ["变化", "變化"], + ["减", "減"], + ["满", "滿"], + ["少", "少"], + ["低", "低"], + ["冷却", "冷卻"], + ["换", "換"], + ["别", "別"] +]; +function toTraditional(text) { + return traditionalPhrases.reduce((result, [source, target]) => result.replaceAll(source, target), text); +} +const englishOptionTitles = { + no: "Off", + off: "Off", + all: "All", + movie: "Movies", + tv: "TV shows", + tv_episode: "TV shows (individual episodes)", + normal: "Standard subscriptions", + best_version: "Best-version subscriptions", + best_version_episode: "Episode upgrades", + audit: "Audit", + loose: "Relaxed", + balanced: "Balanced", + strict: "Strict", + summary: "Summary", + detail: "Details", + balanced_strict: "Balanced and strict", + pause_movie: "Pause movie subscriptions", + pause_tv: "Pause TV subscriptions", + complete_movie: "Complete movie subscriptions", + complete_tv: "Complete TV subscriptions", + delete_movie: "Delete movie subscriptions", + delete_tv: "Delete TV subscriptions", + no_download: "No downloads", + pre_air: "Before release", + airing_gap: "Airing gap", + auto_user: "User rule", + external: "External pause", + notify: "Notify only" +}; +function localizedOptionTitle(locale, field, value, source) { + if (locale === "zh-CN") return source; + if (locale === "zh-TW") return toTraditional(source); + if (typeof value === "number") { + if (field.key === "auto_check_interval_minutes" || field.key === "download_check_interval_minutes") + return `${value} minutes`; + if (field.key === "meta_check_interval_hours") return `${value} hours`; + return String(value); + } + const translated = englishOptionTitles[value]; + if (!translated) throw new Error(`Missing option translation: ${field.key}.${value}`); + return translated; +} +function localizeGroups(localeSource, source = groups) { + const locale = normalizeLocale(localeSource); + return source.map((group) => { + const translation = groupTranslations[group.key]; + if (!translation) throw new Error(`Missing group translation: ${group.key}`); + const [title, summary] = locale === "zh-CN" ? [group.title, group.summary] : locale === "zh-TW" ? translation.tw : translation.en; + return { ...group, title, summary }; + }); +} +function localizeFields(localeSource, source = fields) { + const locale = normalizeLocale(localeSource); + return source.map((field) => { + const english = englishFields[field.key]; + if (!english) throw new Error(`Missing field translation: ${field.key}`); + const label = locale === "zh-CN" ? field.label : locale === "zh-TW" ? toTraditional(field.label) : english[0]; + const hint = field.hint ? locale === "zh-CN" ? field.hint : locale === "zh-TW" ? field.key === "recognition_guard_custom_config" ? "僅在內建規則無法滿足時編輯,留空則繼承目前模式" : toTraditional(field.hint) : english[1] : void 0; + if (!label.trim() || field.hint && !hint?.trim()) throw new Error(`Empty field translation: ${field.key}`); + return { + ...field, + label, + hint, + options: field.options?.map((option) => ({ + ...option, + title: localizedOptionTitle(locale, field, option.value, option.title) + })) + }; + }); +} + +const unitLabels = { + "zh-CN": { + count: "次", + day: "天", + episode: "集", + hour: "小时", + item: "条", + minute: "分钟", + multiplier: "倍", + percent: "%", + round: "轮", + second: "秒" + }, + "zh-TW": { + count: "次", + day: "天", + episode: "集", + hour: "小時", + item: "條", + minute: "分鐘", + multiplier: "倍", + percent: "%", + round: "輪", + second: "秒" + }, + "en-US": { + count: "x", + day: "d", + episode: "ep", + hour: "hr", + item: "items", + minute: "min", + multiplier: "x", + percent: "%", + round: "rounds", + second: "sec" + } +}; +function numberFieldUnit(key, locale = "zh-CN") { + const units = unitLabels[locale]; + if (key === "cadence_min_episodes") return units.episode; + if (key === "cadence_multiplier") return units.multiplier; + if (key === "download_progress_threshold") return units.percent; + if (key === "download_queue_grace_multiplier") return units.multiplier; + if (key === "download_retry_limit") return units.count; + if (key === "recognition_guard_cache_maxsize") return units.item; + if (key === "recognition_guard_notify_interval") return units.second; + if (key.endsWith("_minutes")) return units.minute; + if (key.endsWith("_hours")) return units.hour; + if (key.endsWith("_days")) return units.day; + if (key.endsWith("_episodes")) return units.episode; + return void 0; +} +function displayFieldLabel(field) { + if (field.kind !== "number") return field.label; + let label = field.label; + for (const [opening, closing] of [ + ["(", ")"], + ["(", ")"] + ]) { + let start = label.indexOf(opening); + while (start >= 0) { + const end = label.indexOf(closing, start + opening.length); + if (end < 0) break; + label = `${label.slice(0, start)}${label.slice(end + closing.length)}`; + start = label.indexOf(opening); + } + } + return label.trim(); +} + +const {defineComponent:_defineComponent} = await importShared('vue'); + +const {createElementVNode:_createElementVNode,unref:_unref,resolveComponent:_resolveComponent,createVNode:_createVNode,toDisplayString:_toDisplayString,createTextVNode:_createTextVNode,withCtx:_withCtx,normalizeClass:_normalizeClass,renderList:_renderList,Fragment:_Fragment,openBlock:_openBlock,createElementBlock:_createElementBlock,createBlock:_createBlock,createCommentVNode:_createCommentVNode,createSlots:_createSlots,withModifiers:_withModifiers} = await importShared('vue'); + +const _hoisted_1 = { class: "sae-config" }; +const _hoisted_2 = { class: "sae-config-header__brand" }; +const _hoisted_3 = ["src"]; +const _hoisted_4 = { class: "sae-config-header__identity" }; +const _hoisted_5 = { class: "sae-config-header__crumbs" }; +const _hoisted_6 = { class: "sae-config-header__title-row" }; +const _hoisted_7 = { class: "sae-config-header__title" }; +const _hoisted_8 = { class: "sae-config-header__actions" }; +const _hoisted_9 = { class: "sae-config__body" }; +const _hoisted_10 = { class: "sae-config-layout" }; +const _hoisted_11 = ["aria-label"]; +const _hoisted_12 = { class: "sae-group-nav__heading" }; +const _hoisted_13 = { class: "sae-group-nav__help" }; +const _hoisted_14 = { class: "sae-group-nav__help-title" }; +const _hoisted_15 = { class: "sae-field-surface" }; +const _hoisted_16 = { class: "sae-field-surface__heading" }; +const _hoisted_17 = { class: "sae-field-surface__heading-copy" }; +const _hoisted_18 = { class: "sae-field-surface__mobile-actions" }; +const _hoisted_19 = { class: "sae-field-section__rows" }; +const _hoisted_20 = { class: "sae-field-row__copy" }; +const _hoisted_21 = { class: "sae-field-row__label" }; +const _hoisted_22 = { key: 0 }; +const _hoisted_23 = { class: "sae-field-control" }; +const _hoisted_24 = { + key: 0, + class: "sae-select-summary__primary" +}; +const _hoisted_25 = { + key: 1, + class: "sae-select-summary__count" +}; +const _hoisted_26 = { + key: 3, + class: "sae-number-stepper" +}; +const _hoisted_27 = { + key: 0, + class: "sae-number-stepper__unit" +}; +const _hoisted_28 = { + key: 0, + class: "sae-field-section sae-tracker-entry" +}; +const _hoisted_29 = { class: "sae-tracker-entry__copy" }; +const _hoisted_30 = { + key: 1, + class: "sae-field-section sae-tracker-entry" +}; +const _hoisted_31 = { class: "sae-tracker-entry__copy" }; +const _hoisted_32 = { class: "sae-impact-preview" }; +const _hoisted_33 = { class: "sae-impact-preview__title sae-summary-section__title" }; +const _hoisted_34 = { class: "sae-impact-preview__list" }; +const _hoisted_35 = { + key: 0, + class: "sae-change-summary" +}; +const _hoisted_36 = { class: "sae-change-summary__title sae-summary-section__title" }; +const _hoisted_37 = { key: 0 }; +const _hoisted_38 = ["aria-label"]; +const _hoisted_39 = { class: "sae-runtime-summary__title sae-summary-section__title" }; +const _hoisted_40 = { + key: 0, + class: "sae-runtime-summary__state" +}; +const _hoisted_41 = { + key: 1, + class: "sae-runtime-summary__metrics" +}; +const _hoisted_42 = { class: "sae-runtime-summary__row" }; +const _hoisted_43 = { class: "sae-runtime-summary__row" }; +const _hoisted_44 = { class: "sae-runtime-summary__row" }; +const _hoisted_45 = { + key: 2, + class: "sae-runtime-summary__unavailable" +}; +const _hoisted_46 = { + key: 0, + class: "sae-mobile-save-dock" +}; +const _hoisted_47 = { + "aria-live": "polite", + class: "sae-mobile-save-dock__state" +}; +const {computed,getCurrentInstance,onBeforeUnmount,onMounted,ref} = await importShared('vue'); + +const {useTheme} = await importShared('vuetify'); +const README_URL = "https://github.com/InfinityPacer/MoviePilot-Plugins/blob/main/plugins.v3/subscribeassistantenhanced/README.md"; +const _sfc_main = /* @__PURE__ */ _defineComponent({ + __name: "Config", + props: { + initialConfig: {}, + api: {} + }, + emits: ["save", "close", "layout"], + setup(__props, { emit: __emit }) { + const props = __props; + const emit = __emit; + emit("layout", { maxWidth: "68rem" }); + const { draft, changedCount, changedKeys, buildSavePayload } = useConfigDraft(props.initialConfig); + const instance = getCurrentInstance(); + const locale = computed(() => normalizeLocale(instance?.appContext.config.globalProperties.$i18n?.locale)); + const localizedGroups = computed(() => localizeGroups(locale.value, groups)); + const localizedFields = computed(() => localizeFields(locale.value, fields)); + const fieldsByKey = computed( + () => new Map( + localizedFields.value.filter((field) => !field.legacyUiKey && !field.dialogOnly).map((field) => [field.key, field]) + ) + ); + const trackerField = computed( + () => localizedFields.value.find((field) => field.key === "default_tracker_response" && field.dialogOnly) + ); + const yamlField = computed(() => localizedFields.value.find((field) => field.key === "recognition_guard_custom_config")); + const changedItems = computed( + () => changedKeys.value.slice(0, 3).map((key) => localizedFields.value.find((field) => field.key === key)).filter((field) => Boolean(field)) + ); + const hiddenChangedCount = computed(() => Math.max(0, changedKeys.value.length - changedItems.value.length)); + const activeGroup = ref("global"); + const runtimeSummary = ref(null); + const summaryState = ref("loading"); + const trackerDialogOpen = ref(false); + const yamlDialogOpen = ref(false); + const mobileGroupSheet = ref(false); + const configHeaderSentinel = ref(null); + const headerScrolled = ref(false); + const theme = useTheme(); + const aceTheme = computed(() => theme.current.value.dark ? "github_dark" : "github"); + let headerObserver; + const sectionDefinitions = { + global: [ + { titleKey: "section.running", keys: ["enabled", "notify"] }, + { + titleKey: "section.schedule", + keys: [ + "auto_check_interval_minutes", + "download_check_interval_minutes", + "meta_check_interval_hours", + "best_version_cron" + ] + }, + { titleKey: "section.oneTime", keys: ["onlyonce", "reset_task"] } + ], + cleanup: [ + { + titleKey: "section.download", + keys: [ + "download_monitor_enabled", + "manual_delete_listen", + "tracker_response_listen", + "auto_search_when_delete", + "skip_deletion" + ] + }, + { + titleKey: "section.timeout", + keys: [ + "download_timeout_minutes", + "download_progress_threshold", + "download_queue_grace_multiplier", + "download_retry_limit", + "delete_exclude_tags", + "delete_record_retention_hours" + ] + }, + { + titleKey: "section.cleanup", + keys: ["subscription_cleanup_history_type", "subscription_cleanup_history_scenes"] + } + ], + pending: [ + { + titleKey: "section.pending", + keys: ["pending_enhanced_enabled", "pending_download_enabled"] + }, + { + titleKey: "section.tvDecision", + keys: ["auto_tv_pending_days", "auto_tv_pending_episodes", "pending_use_volatility"] + } + ], + pause: [ + { + titleKey: "section.autoPause", + keys: ["pause_enhanced_enabled", "auto_pause_users"] + }, + { + titleKey: "section.airing", + keys: ["airing_pause_days", "movie_air_pause_days", "tv_air_pause_days"] + }, + { + titleKey: "section.noDownload", + keys: ["movie_no_download_days", "tv_no_download_days", "no_download_actions"] + } + ], + completion: [ + { titleKey: "section.siteProbe", keys: ["site_total_probe_enabled"] }, + { + titleKey: "section.pausedProbe", + keys: ["paused_probe_reasons", "paused_probe_min_pause_days", "paused_probe_interval_hours"] + } + ], + bestVersion: [ + { + titleKey: "section.bestVersionScope", + keys: ["best_version_type", "best_version_movie_remaining_days", "best_version_tv_remaining_days"] + }, + { + titleKey: "section.backfill", + keys: ["best_version_episode_to_full", "best_version_backfill_enabled", "backfill_best_version_now"] + } + ], + guard: [ + { + titleKey: "section.guard", + keys: [ + "completion_guard_mode", + "site_completion_evidence_enabled", + "volatility_enabled", + "volatility_window_days" + ] + }, + { + titleKey: "section.cadence", + keys: [ + "cadence_enabled", + "cadence_multiplier", + "cadence_min_window_days", + "cadence_min_episodes", + "season_cooldown_days" + ] + }, + { + titleKey: "section.correction", + keys: [ + "verify_enabled", + "verify_interval_hours", + "verify_retention_days", + "timeout_release_days", + "timeout_cadence_acceleration" + ] + } + ], + recognition: [ + { + titleKey: "section.recognition", + keys: [ + "recognition_guard_mode", + "recognition_guard_notify", + "recognition_guard_notify_interval", + "recognition_guard_tmdb_recheck_mode", + "recognition_guard_cache_maxsize" + ] + }, + { titleKey: "section.custom", keys: ["recognition_guard_custom_config"] } + ] + }; + const activeGroupMeta = computed( + () => localizedGroups.value.find((group) => group.key === activeGroup.value) ?? localizedGroups.value[0] + ); + const activeSections = computed( + () => sectionDefinitions[activeGroup.value].map((section) => ({ + ...section, + title: t(locale.value, section.titleKey), + fields: section.keys.map((key) => fieldsByKey.value.get(key)).filter((field) => Boolean(field) && field?.kind !== "textarea") + })).filter((section) => section.fields.length > 0) + ); + const cadenceSummary = computed(() => [ + { + icon: "mdi-radar", + title: t(locale.value, "config.generalInspection"), + value: t(locale.value, "config.everyMinutes", { value: draft.auto_check_interval_minutes }) + }, + { + icon: "mdi-download-network-outline", + title: t(locale.value, "config.downloadInspection"), + value: t(locale.value, "config.everyMinutes", { value: draft.download_check_interval_minutes }) + }, + { + icon: "mdi-database-search-outline", + title: t(locale.value, "config.metadataInspection"), + value: t(locale.value, "config.everyHours", { value: draft.meta_check_interval_hours }) + }, + { + icon: "mdi-auto-fix", + title: t(locale.value, "config.bestVersionInspection"), + value: draft.best_version_cron || t(locale.value, "config.notScheduled") + } + ]); + const activeDomainCount = computed(() => { + const values = Object.values(runtimeSummary.value?.domains ?? {}); + return { + active: values.filter((value) => value === true || typeof value === "string" && !["off", "no"].includes(value)).length, + total: values.length + }; + }); + onMounted(() => { + void loadSummary(props.api).then((payload) => { + runtimeSummary.value = payload; + summaryState.value = payload ? "available" : "unavailable"; + }); + const scrollRoot = configHeaderSentinel.value?.closest(".v-card-text") ?? null; + headerObserver = new IntersectionObserver( + ([entry]) => { + headerScrolled.value = !entry?.isIntersecting; + }, + { root: scrollRoot, threshold: 1 } + ); + if (configHeaderSentinel.value) headerObserver.observe(configHeaderSentinel.value); + }); + onBeforeUnmount(() => { + headerObserver?.disconnect(); + }); + function updateNumber(key, incoming) { + draft[key] = normalizeFiniteNumber(draft[key], incoming); + } + function numberStep(key) { + return key === "cadence_multiplier" ? 0.5 : 1; + } + function stepNumber(key, direction) { + updateNumber(key, draft[key] + numberStep(key) * direction); + } + function fieldUnit(field) { + return numberFieldUnit(field.key, locale.value); + } + function selectionOverflowCount(key) { + const value = draft[key]; + return Array.isArray(value) ? Math.max(0, value.length - 1) : 0; + } + function selectMobileGroup(group) { + activeGroup.value = group; + mobileGroupSheet.value = false; + } + function saveConfig() { + emit("save", buildSavePayload()); + } + function handleOneTimeAction(key, enabled) { + if (enabled !== true) return; + if (key === "onlyonce") draft.reset_task = false; + if (key === "reset_task") draft.onlyonce = false; + } + function runOnce() { + handleOneTimeAction("onlyonce", true); + const payload = buildSavePayload(); + payload.onlyonce = true; + emit("save", payload); + } + return (_ctx, _cache) => { + const _component_VIcon = _resolveComponent("VIcon"); + const _component_VChip = _resolveComponent("VChip"); + const _component_VBtn = _resolveComponent("VBtn"); + const _component_VListItem = _resolveComponent("VListItem"); + const _component_VList = _resolveComponent("VList"); + const _component_VTooltip = _resolveComponent("VTooltip"); + const _component_VCheckbox = _resolveComponent("VCheckbox"); + const _component_VSwitch = _resolveComponent("VSwitch"); + const _component_VSelect = _resolveComponent("VSelect"); + const _component_VTextField = _resolveComponent("VTextField"); + const _component_VCronField = _resolveComponent("VCronField"); + const _component_VProgressCircular = _resolveComponent("VProgressCircular"); + const _component_VSpacer = _resolveComponent("VSpacer"); + const _component_VCardTitle = _resolveComponent("VCardTitle"); + const _component_VTextarea = _resolveComponent("VTextarea"); + const _component_VCardText = _resolveComponent("VCardText"); + const _component_VCardActions = _resolveComponent("VCardActions"); + const _component_VCard = _resolveComponent("VCard"); + const _component_VDialog = _resolveComponent("VDialog"); + const _component_VBottomSheet = _resolveComponent("VBottomSheet"); + const _component_VAceEditor = _resolveComponent("VAceEditor"); + return _openBlock(), _createElementBlock("section", _hoisted_1, [ + _createElementVNode("form", { + class: "sae-config__form", + onSubmit: _withModifiers(saveConfig, ["prevent"]) + }, [ + _createElementVNode("div", { + ref_key: "configHeaderSentinel", + ref: configHeaderSentinel, + class: "sae-config-header-sentinel", + "aria-hidden": "true" + }, null, 512), + _createElementVNode("header", { + class: _normalizeClass(["sae-config-header", { "sae-config-header--scrolled": headerScrolled.value }]) + }, [ + _createElementVNode("div", _hoisted_2, [ + _createElementVNode("img", { + src: _unref(saeLogo), + alt: "", + class: "sae-config-header__logo" + }, null, 8, _hoisted_3), + _createElementVNode("div", _hoisted_4, [ + _createElementVNode("div", _hoisted_5, [ + _cache[16] || (_cache[16] = _createElementVNode("span", null, "MoviePilot", -1)), + _createVNode(_component_VIcon, { + icon: "mdi-chevron-right", + size: "14" + }), + _createElementVNode("span", null, _toDisplayString(_unref(t)(locale.value, "config.plugin")), 1), + _createVNode(_component_VIcon, { + icon: "mdi-chevron-right", + size: "14" + }) + ]), + _createElementVNode("div", _hoisted_6, [ + _createElementVNode("h1", _hoisted_7, _toDisplayString(_unref(t)(locale.value, "config.title")), 1), + _createVNode(_component_VChip, { + color: "primary", + size: "x-small", + variant: "tonal" + }, { + default: _withCtx(() => [..._cache[17] || (_cache[17] = [ + _createTextVNode("BETA", -1) + ])]), + _: 1 + }) + ]) + ]) + ]), + _createElementVNode("div", _hoisted_8, [ + _createVNode(_component_VBtn, { + class: "sae-config-header__run", + color: "primary", + type: "button", + variant: "tonal", + onClick: runOnce + }, { + default: _withCtx(() => [ + _createVNode(_component_VIcon, { + icon: "mdi-play", + start: "" + }), + _createTextVNode(" " + _toDisplayString(_unref(t)(locale.value, "config.runOnce")), 1) + ]), + _: 1 + }), + _createVNode(_component_VBtn, { + class: "sae-config-header__save", + color: "primary", + disabled: _unref(changedCount) === 0, + type: "submit", + variant: "flat" + }, { + default: _withCtx(() => [ + _createVNode(_component_VIcon, { + icon: "mdi-content-save", + start: "" + }), + _createTextVNode(" " + _toDisplayString(_unref(t)(locale.value, "config.save")), 1) + ]), + _: 1 + }, 8, ["disabled"]), + _createVNode(_component_VBtn, { + class: "sae-config-header__close-action", + color: "default", + type: "button", + variant: "outlined", + onClick: _cache[0] || (_cache[0] = ($event) => emit("close")) + }, { + default: _withCtx(() => [ + _createVNode(_component_VIcon, { + icon: "mdi-close", + start: "" + }), + _createTextVNode(" " + _toDisplayString(_unref(t)(locale.value, "config.close")), 1) + ]), + _: 1 + }), + _createVNode(_component_VBtn, { + "aria-label": _unref(t)(locale.value, "config.close"), + class: "sae-config-header__close-icon", + icon: "", + size: "small", + type: "button", + variant: "text", + onClick: _cache[1] || (_cache[1] = ($event) => emit("close")) + }, { + default: _withCtx(() => [ + _createVNode(_component_VIcon, { icon: "mdi-close" }) + ]), + _: 1 + }, 8, ["aria-label"]) + ]) + ], 2), + _createElementVNode("div", _hoisted_9, [ + _createElementVNode("div", _hoisted_10, [ + _createElementVNode("nav", { + class: "sae-group-nav", + "aria-label": _unref(t)(locale.value, "config.selectGroup") + }, [ + _createElementVNode("div", _hoisted_12, _toDisplayString(_unref(t)(locale.value, "config.settings")), 1), + _createVNode(_component_VList, { + class: "sae-group-nav__list", + density: "compact", + nav: "" + }, { + default: _withCtx(() => [ + (_openBlock(true), _createElementBlock(_Fragment, null, _renderList(localizedGroups.value, (group) => { + return _openBlock(), _createBlock(_component_VListItem, { + key: group.key, + active: activeGroup.value === group.key, + "prepend-icon": group.icon, + title: group.title, + color: "primary", + rounded: "lg", + onClick: ($event) => activeGroup.value = group.key + }, null, 8, ["active", "prepend-icon", "title", "onClick"]); + }), 128)) + ]), + _: 1 + }), + _createElementVNode("section", _hoisted_13, [ + _createElementVNode("strong", _hoisted_14, _toDisplayString(_unref(t)(locale.value, "config.aboutPlugin")), 1), + _createElementVNode("p", null, _toDisplayString(_unref(t)(locale.value, "config.aboutDescription")), 1), + _createVNode(_component_VBtn, { + href: README_URL, + "append-icon": "mdi-open-in-new", + class: "sae-group-nav__help-link", + color: "primary", + rel: "noopener noreferrer", + size: "small", + target: "_blank", + variant: "text" + }, { + default: _withCtx(() => [ + _createTextVNode(_toDisplayString(_unref(t)(locale.value, "config.viewDocs")), 1) + ]), + _: 1 + }) + ]) + ], 8, _hoisted_11), + _createElementVNode("main", _hoisted_15, [ + _createElementVNode("div", _hoisted_16, [ + _createElementVNode("div", _hoisted_17, [ + _createVNode(_component_VIcon, { + icon: activeGroupMeta.value.icon, + color: "primary", + size: "22" + }, null, 8, ["icon"]), + _createElementVNode("div", null, [ + _createElementVNode("h2", null, _toDisplayString(activeGroupMeta.value.title), 1), + _createElementVNode("p", null, _toDisplayString(activeGroupMeta.value.summary), 1) + ]) + ]), + _createElementVNode("div", _hoisted_18, [ + _createVNode(_component_VBtn, { + "aria-expanded": mobileGroupSheet.value, + "aria-label": _unref(t)(locale.value, "config.selectGroup"), + "aria-haspopup": "dialog", + class: "sae-mobile-group-action", + icon: "", + size: "small", + type: "button", + variant: "tonal", + onClick: _cache[2] || (_cache[2] = ($event) => mobileGroupSheet.value = true) + }, { + default: _withCtx(() => [ + _createVNode(_component_VIcon, { + icon: "mdi-view-list-outline", + size: "18" + }), + _createVNode(_component_VTooltip, { + activator: "parent", + text: _unref(t)(locale.value, "config.selectGroup") + }, null, 8, ["text"]) + ]), + _: 1 + }, 8, ["aria-expanded", "aria-label"]), + _createVNode(_component_VBtn, { + href: README_URL, + "aria-label": _unref(t)(locale.value, "config.help"), + class: "sae-mobile-help", + icon: "", + rel: "noopener noreferrer", + size: "small", + target: "_blank", + variant: "text" + }, { + default: _withCtx(() => [ + _createVNode(_component_VIcon, { + icon: "mdi-help-circle-outline", + size: "18" + }), + _createVNode(_component_VTooltip, { + activator: "parent", + text: _unref(t)(locale.value, "config.help") + }, null, 8, ["text"]) + ]), + _: 1 + }, 8, ["aria-label"]) + ]) + ]), + (_openBlock(true), _createElementBlock(_Fragment, null, _renderList(activeSections.value, (section, sectionIndex) => { + return _openBlock(), _createElementBlock("section", { + key: section.title, + class: "sae-field-section" + }, [ + _createElementVNode("h3", null, _toDisplayString(sectionIndex + 1) + ". " + _toDisplayString(section.title), 1), + _createElementVNode("div", _hoisted_19, [ + (_openBlock(true), _createElementBlock(_Fragment, null, _renderList(section.fields, (field) => { + return _openBlock(), _createElementBlock("div", { + key: field.key, + class: _normalizeClass([ + "sae-field-row", + { + "sae-field-row--mobile-only": field.key === "onlyonce", + "sae-field-row--switch": field.kind === "switch" + } + ]) + }, [ + _createElementVNode("div", _hoisted_20, [ + _createElementVNode("div", _hoisted_21, [ + field.key === "reset_task" ? (_openBlock(), _createBlock(_component_VIcon, { + key: 0, + color: "error", + icon: "mdi-alert-outline", + size: "16" + })) : _createCommentVNode("", true), + _createElementVNode("span", null, _toDisplayString(_unref(displayFieldLabel)(field)), 1) + ]), + field.hint ? (_openBlock(), _createElementBlock("p", _hoisted_22, _toDisplayString(field.hint), 1)) : _createCommentVNode("", true) + ]), + _createElementVNode("div", _hoisted_23, [ + field.key === "reset_task" ? (_openBlock(), _createBlock(_component_VCheckbox, { + key: 0, + id: "sae-field-reset_task", + modelValue: _unref(draft).reset_task, + "onUpdate:modelValue": [ + _cache[3] || (_cache[3] = ($event) => _unref(draft).reset_task = $event), + _cache[4] || (_cache[4] = ($event) => handleOneTimeAction("reset_task", $event)) + ], + "aria-label": field.label, + density: "compact", + "hide-details": "" + }, null, 8, ["modelValue", "aria-label"])) : field.kind === "switch" ? (_openBlock(), _createBlock(_component_VSwitch, { + key: 1, + id: `sae-field-${field.key}`, + modelValue: _unref(draft)[field.key], + "onUpdate:modelValue": [($event) => _unref(draft)[field.key] = $event, ($event) => handleOneTimeAction(field.key, $event)], + "aria-label": field.label, + color: "primary", + density: "compact", + "hide-details": "" + }, null, 8, ["id", "modelValue", "onUpdate:modelValue", "aria-label"])) : field.kind === "select" || field.kind === "multi-select" ? (_openBlock(), _createBlock(_component_VSelect, { + key: 2, + modelValue: _unref(draft)[field.key], + "onUpdate:modelValue": ($event) => _unref(draft)[field.key] = $event, + "aria-label": field.label, + density: "compact", + "hide-details": "", + "item-title": "title", + "item-value": "value", + items: field.options, + multiple: field.kind === "multi-select", + variant: "outlined" + }, _createSlots({ _: 2 }, [ + field.kind === "multi-select" ? { + name: "selection", + fn: _withCtx(({ item, index }) => [ + index === 0 ? (_openBlock(), _createElementBlock("span", _hoisted_24, _toDisplayString(item.title), 1)) : index === 1 ? (_openBlock(), _createElementBlock("span", _hoisted_25, " +" + _toDisplayString(selectionOverflowCount(field.key)), 1)) : _createCommentVNode("", true) + ]), + key: "0" + } : void 0 + ]), 1032, ["modelValue", "onUpdate:modelValue", "aria-label", "items", "multiple"])) : field.kind === "number" ? (_openBlock(), _createElementBlock("div", _hoisted_26, [ + _createVNode(_component_VBtn, { + "aria-label": _unref(t)(locale.value, "config.decrease", { label: field.label }), + icon: "", + type: "button", + variant: "text", + onClick: ($event) => stepNumber(field.key, -1) + }, { + default: _withCtx(() => [ + _createVNode(_component_VIcon, { icon: "mdi-minus" }) + ]), + _: 1 + }, 8, ["aria-label", "onClick"]), + _createVNode(_component_VTextField, { + id: `sae-field-${field.key}`, + "aria-label": field.label, + density: "compact", + "hide-details": "", + "model-value": _unref(draft)[field.key], + step: numberStep(field.key), + type: "number", + variant: "plain", + "onUpdate:modelValue": ($event) => updateNumber(field.key, $event) + }, null, 8, ["id", "aria-label", "model-value", "step", "onUpdate:modelValue"]), + _createVNode(_component_VBtn, { + "aria-label": _unref(t)(locale.value, "config.increase", { label: field.label }), + icon: "", + type: "button", + variant: "text", + onClick: ($event) => stepNumber(field.key, 1) + }, { + default: _withCtx(() => [ + _createVNode(_component_VIcon, { icon: "mdi-plus" }) + ]), + _: 1 + }, 8, ["aria-label", "onClick"]), + fieldUnit(field) ? (_openBlock(), _createElementBlock("span", _hoisted_27, _toDisplayString(fieldUnit(field)), 1)) : _createCommentVNode("", true) + ])) : field.kind === "cron" ? (_openBlock(), _createBlock(_component_VCronField, { + key: 4, + modelValue: _unref(draft)[field.key], + "onUpdate:modelValue": ($event) => _unref(draft)[field.key] = $event, + "aria-label": field.label, + class: "sae-text-control", + clearable: false, + density: "compact", + "hide-details": "", + placeholder: _unref(t)(locale.value, "config.cronPlaceholder"), + variant: "outlined" + }, null, 8, ["modelValue", "onUpdate:modelValue", "aria-label", "placeholder"])) : field.kind === "text" ? (_openBlock(), _createBlock(_component_VTextField, { + key: 5, + id: `sae-field-${field.key}`, + modelValue: _unref(draft)[field.key], + "onUpdate:modelValue": ($event) => _unref(draft)[field.key] = $event, + "aria-label": field.label, + class: "sae-text-control", + density: "compact", + "hide-details": "", + variant: "outlined" + }, null, 8, ["id", "modelValue", "onUpdate:modelValue", "aria-label"])) : _createCommentVNode("", true) + ]) + ], 2); + }), 128)) + ]) + ]); + }), 128)), + activeGroup.value === "cleanup" ? (_openBlock(), _createElementBlock("section", _hoisted_28, [ + _createElementVNode("div", _hoisted_29, [ + _createVNode(_component_VIcon, { + color: "primary", + icon: "mdi-message-text-outline", + size: "22" + }), + _createElementVNode("div", null, [ + _createElementVNode("strong", null, _toDisplayString(trackerField.value.label), 1), + _createElementVNode("p", null, _toDisplayString(trackerField.value.hint), 1) + ]) + ]), + _createVNode(_component_VBtn, { + "aria-label": _unref(t)(locale.value, "config.editLabel", { label: trackerField.value.label }), + color: "primary", + "prepend-icon": "mdi-pencil-outline", + type: "button", + variant: "tonal", + onClick: _cache[5] || (_cache[5] = ($event) => trackerDialogOpen.value = true) + }, { + default: _withCtx(() => [ + _createTextVNode(_toDisplayString(_unref(t)(locale.value, "config.edit")), 1) + ]), + _: 1 + }, 8, ["aria-label"]) + ])) : _createCommentVNode("", true), + activeGroup.value === "recognition" ? (_openBlock(), _createElementBlock("section", _hoisted_30, [ + _createElementVNode("div", _hoisted_31, [ + _createVNode(_component_VIcon, { + color: "primary", + icon: "mdi-code-braces", + size: "22" + }), + _createElementVNode("div", null, [ + _createElementVNode("strong", null, _toDisplayString(yamlField.value.label), 1), + _createElementVNode("p", null, _toDisplayString(yamlField.value.hint), 1) + ]) + ]), + _createVNode(_component_VBtn, { + "aria-label": _unref(t)(locale.value, "config.editLabel", { label: yamlField.value.label }), + color: "primary", + "prepend-icon": "mdi-pencil-outline", + type: "button", + variant: "tonal", + onClick: _cache[6] || (_cache[6] = ($event) => yamlDialogOpen.value = true) + }, { + default: _withCtx(() => [ + _createTextVNode(_toDisplayString(_unref(t)(locale.value, "config.edit")), 1) + ]), + _: 1 + }, 8, ["aria-label"]) + ])) : _createCommentVNode("", true) + ]), + _createElementVNode("aside", _hoisted_32, [ + _createElementVNode("div", _hoisted_33, [ + _createVNode(_component_VIcon, { + color: "primary", + icon: "mdi-clock-outline", + size: "20" + }), + _createElementVNode("h2", null, _toDisplayString(_unref(t)(locale.value, "config.cadence")), 1) + ]), + _createElementVNode("ul", _hoisted_34, [ + (_openBlock(true), _createElementBlock(_Fragment, null, _renderList(cadenceSummary.value, (item) => { + return _openBlock(), _createElementBlock("li", { + key: item.title, + class: "sae-impact-preview__item" + }, [ + _createVNode(_component_VIcon, { + icon: item.icon, + size: "18" + }, null, 8, ["icon"]), + _createElementVNode("span", null, _toDisplayString(item.title), 1), + _createElementVNode("strong", null, _toDisplayString(item.value), 1) + ]); + }), 128)) + ]), + changedItems.value.length ? (_openBlock(), _createElementBlock("section", _hoisted_35, [ + _createElementVNode("div", _hoisted_36, [ + _createVNode(_component_VIcon, { + color: "warning", + icon: "mdi-format-list-checks", + size: "19" + }), + _createElementVNode("h3", null, _toDisplayString(_unref(t)(locale.value, "config.changes")), 1) + ]), + _createElementVNode("ul", null, [ + (_openBlock(true), _createElementBlock(_Fragment, null, _renderList(changedItems.value, (item) => { + return _openBlock(), _createElementBlock("li", { + key: item.key + }, [ + _createVNode(_component_VIcon, { + color: "warning", + icon: "mdi-circle", + size: "6" + }), + _createElementVNode("span", null, _toDisplayString(_unref(displayFieldLabel)(item)), 1) + ]); + }), 128)) + ]), + hiddenChangedCount.value > 0 ? (_openBlock(), _createElementBlock("p", _hoisted_37, _toDisplayString(_unref(t)(locale.value, "config.moreChanges", { count: hiddenChangedCount.value })), 1)) : _createCommentVNode("", true) + ])) : _createCommentVNode("", true), + _createElementVNode("section", { + "aria-label": _unref(t)(locale.value, "config.runtime"), + class: "sae-runtime-summary" + }, [ + _createElementVNode("div", _hoisted_39, [ + _createVNode(_component_VIcon, { + color: "primary", + icon: "mdi-chart-box-outline", + size: "19" + }), + _createElementVNode("h3", null, _toDisplayString(_unref(t)(locale.value, "config.runtime")), 1) + ]), + summaryState.value === "loading" ? (_openBlock(), _createElementBlock("div", _hoisted_40, [ + _createVNode(_component_VProgressCircular, { + color: "primary", + indeterminate: "", + size: "18", + width: "2" + }), + _createElementVNode("span", null, _toDisplayString(_unref(t)(locale.value, "config.runtimeLoading")), 1) + ])) : summaryState.value === "available" && runtimeSummary.value ? (_openBlock(), _createElementBlock("div", _hoisted_41, [ + _createElementVNode("div", _hoisted_42, [ + _createVNode(_component_VIcon, { + icon: "mdi-timer-sand", + size: "18" + }), + _createElementVNode("span", null, _toDisplayString(_unref(t)(locale.value, "config.pendingCount")), 1), + _createElementVNode("strong", null, _toDisplayString(runtimeSummary.value.pending_count), 1) + ]), + _createElementVNode("div", _hoisted_43, [ + _createVNode(_component_VIcon, { + icon: "mdi-download-network-outline", + size: "18" + }), + _createElementVNode("span", null, _toDisplayString(_unref(t)(locale.value, "config.monitoredCount")), 1), + _createElementVNode("strong", null, _toDisplayString(runtimeSummary.value.monitored_torrents), 1) + ]), + _createElementVNode("div", _hoisted_44, [ + _createVNode(_component_VIcon, { + icon: "mdi-toggle-switch-outline", + size: "18" + }), + _createElementVNode("span", null, _toDisplayString(_unref(t)(locale.value, "config.activeDomains")), 1), + _createElementVNode("strong", null, _toDisplayString(activeDomainCount.value.active) + " / " + _toDisplayString(activeDomainCount.value.total), 1) + ]) + ])) : (_openBlock(), _createElementBlock("p", _hoisted_45, _toDisplayString(_unref(t)(locale.value, "config.runtimeUnavailable")), 1)) + ], 8, _hoisted_38) + ]) + ]) + ]), + _unref(changedCount) > 0 ? (_openBlock(), _createElementBlock("div", _hoisted_46, [ + _createElementVNode("span", _hoisted_47, [ + _createVNode(_component_VIcon, { + color: "warning", + icon: "mdi-circle", + size: "8" + }), + _createTextVNode(" " + _toDisplayString(_unref(t)(locale.value, "config.changedCount", { count: _unref(changedCount) })), 1) + ]), + _createVNode(_component_VSpacer), + _createVNode(_component_VBtn, { + class: "sae-mobile-save-dock__save", + color: "primary", + disabled: _unref(changedCount) === 0, + type: "submit", + variant: "flat" + }, { + default: _withCtx(() => [ + _createVNode(_component_VIcon, { + icon: "mdi-content-save", + start: "" + }), + _createTextVNode(" " + _toDisplayString(_unref(t)(locale.value, "config.save")), 1) + ]), + _: 1 + }, 8, ["disabled"]) + ])) : _createCommentVNode("", true) + ], 32), + _createVNode(_component_VDialog, { + modelValue: trackerDialogOpen.value, + "onUpdate:modelValue": _cache[10] || (_cache[10] = ($event) => trackerDialogOpen.value = $event), + "max-width": "720", + scrollable: "", + width: "calc(100% - 24px)" + }, { + default: _withCtx(() => [ + _createVNode(_component_VCard, null, { + default: _withCtx(() => [ + _createVNode(_component_VCardTitle, { class: "sae-tracker-dialog__title" }, { + default: _withCtx(() => [ + _createElementVNode("span", null, _toDisplayString(trackerField.value.label), 1), + _createVNode(_component_VBtn, { + "aria-label": `${_unref(t)(locale.value, "config.close")} ${trackerField.value.label}`, + icon: "", + size: "small", + variant: "text", + onClick: _cache[7] || (_cache[7] = ($event) => trackerDialogOpen.value = false) + }, { + default: _withCtx(() => [ + _createVNode(_component_VIcon, { icon: "mdi-close" }), + _createVNode(_component_VTooltip, { + activator: "parent", + text: _unref(t)(locale.value, "config.close") + }, null, 8, ["text"]) + ]), + _: 1 + }, 8, ["aria-label"]) + ]), + _: 1 + }), + _createVNode(_component_VCardText, null, { + default: _withCtx(() => [ + _createVNode(_component_VTextarea, { + modelValue: _unref(draft).default_tracker_response, + "onUpdate:modelValue": _cache[8] || (_cache[8] = ($event) => _unref(draft).default_tracker_response = $event), + "aria-label": trackerField.value.label, + hint: trackerField.value.hint, + label: trackerField.value.label, + "persistent-hint": "", + rows: "10", + variant: "outlined" + }, null, 8, ["modelValue", "aria-label", "hint", "label"]) + ]), + _: 1 + }), + _createVNode(_component_VCardActions, { class: "sae-tracker-dialog__actions" }, { + default: _withCtx(() => [ + _createVNode(_component_VSpacer), + _createVNode(_component_VBtn, { + color: "primary", + "prepend-icon": "mdi-check", + onClick: _cache[9] || (_cache[9] = ($event) => trackerDialogOpen.value = false) + }, { + default: _withCtx(() => [ + _createTextVNode(_toDisplayString(_unref(t)(locale.value, "config.done")), 1) + ]), + _: 1 + }) + ]), + _: 1 + }) + ]), + _: 1 + }) + ]), + _: 1 + }, 8, ["modelValue"]), + _createVNode(_component_VBottomSheet, { + modelValue: mobileGroupSheet.value, + "onUpdate:modelValue": _cache[11] || (_cache[11] = ($event) => mobileGroupSheet.value = $event), + class: "sae-mobile-group-sheet" + }, { + default: _withCtx(() => [ + _createVNode(_component_VCard, null, { + default: _withCtx(() => [ + _createVNode(_component_VCardTitle, null, { + default: _withCtx(() => [ + _createTextVNode(_toDisplayString(_unref(t)(locale.value, "config.selectGroup")), 1) + ]), + _: 1 + }), + _createVNode(_component_VList, { + lines: "two", + nav: "" + }, { + default: _withCtx(() => [ + (_openBlock(true), _createElementBlock(_Fragment, null, _renderList(localizedGroups.value, (group) => { + return _openBlock(), _createBlock(_component_VListItem, { + key: group.key, + active: activeGroup.value === group.key, + "prepend-icon": group.icon, + subtitle: group.summary, + title: group.title, + color: "primary", + onClick: ($event) => selectMobileGroup(group.key) + }, { + append: _withCtx(() => [ + activeGroup.value === group.key ? (_openBlock(), _createBlock(_component_VIcon, { + key: 0, + icon: "mdi-check" + })) : _createCommentVNode("", true) + ]), + _: 2 + }, 1032, ["active", "prepend-icon", "subtitle", "title", "onClick"]); + }), 128)) + ]), + _: 1 + }) + ]), + _: 1 + }) + ]), + _: 1 + }, 8, ["modelValue"]), + _createVNode(_component_VDialog, { + modelValue: yamlDialogOpen.value, + "onUpdate:modelValue": _cache[15] || (_cache[15] = ($event) => yamlDialogOpen.value = $event), + "max-width": "900", + scrollable: "", + width: "calc(100% - 24px)" + }, { + default: _withCtx(() => [ + _createVNode(_component_VCard, null, { + default: _withCtx(() => [ + _createVNode(_component_VCardTitle, { class: "sae-tracker-dialog__title" }, { + default: _withCtx(() => [ + _createElementVNode("span", null, _toDisplayString(_unref(t)(locale.value, "config.yamlTitle")), 1), + _createVNode(_component_VBtn, { + "aria-label": _unref(t)(locale.value, "config.close"), + icon: "", + size: "small", + variant: "text", + onClick: _cache[12] || (_cache[12] = ($event) => yamlDialogOpen.value = false) + }, { + default: _withCtx(() => [ + _createVNode(_component_VIcon, { icon: "mdi-close" }) + ]), + _: 1 + }, 8, ["aria-label"]) + ]), + _: 1 + }), + _createVNode(_component_VCardText, { class: "sae-yaml-dialog__content" }, { + default: _withCtx(() => [ + _createVNode(_component_VAceEditor, { + value: _unref(draft).recognition_guard_custom_config, + "onUpdate:value": _cache[13] || (_cache[13] = ($event) => _unref(draft).recognition_guard_custom_config = $event), + theme: aceTheme.value, + lang: "yaml", + options: { fontSize: 14, showPrintMargin: false, tabSize: 2, useSoftTabs: true }, + class: "sae-yaml-editor" + }, null, 8, ["value", "theme"]) + ]), + _: 1 + }), + _createVNode(_component_VCardActions, null, { + default: _withCtx(() => [ + _createVNode(_component_VSpacer), + _createVNode(_component_VBtn, { + color: "primary", + "prepend-icon": "mdi-check", + onClick: _cache[14] || (_cache[14] = ($event) => yamlDialogOpen.value = false) + }, { + default: _withCtx(() => [ + _createTextVNode(_toDisplayString(_unref(t)(locale.value, "config.done")), 1) + ]), + _: 1 + }) + ]), + _: 1 + }) + ]), + _: 1 + }) + ]), + _: 1 + }, 8, ["modelValue"]) + ]); + }; + } +}); + +const _export_sfc = (sfc, props) => { + const target = sfc.__vccOpts || sfc; + for (const [key, val] of props) { + target[key] = val; + } + return target; +}; + +const Config = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-59866d47"]]); + +export { Config as default }; diff --git a/plugins.v3/subscribeassistantenhanced/frontend/dist/assets/__federation_fn_import-JrT3xvdd.js b/plugins.v3/subscribeassistantenhanced/frontend/dist/assets/__federation_fn_import-JrT3xvdd.js new file mode 100644 index 00000000..ed562f5a --- /dev/null +++ b/plugins.v3/subscribeassistantenhanced/frontend/dist/assets/__federation_fn_import-JrT3xvdd.js @@ -0,0 +1,418 @@ +const buildIdentifier = "[0-9A-Za-z-]+"; +const build = `(?:\\+(${buildIdentifier}(?:\\.${buildIdentifier})*))`; +const numericIdentifier = "0|[1-9]\\d*"; +const numericIdentifierLoose = "[0-9]+"; +const nonNumericIdentifier = "\\d*[a-zA-Z-][a-zA-Z0-9-]*"; +const preReleaseIdentifierLoose = `(?:${numericIdentifierLoose}|${nonNumericIdentifier})`; +const preReleaseLoose = `(?:-?(${preReleaseIdentifierLoose}(?:\\.${preReleaseIdentifierLoose})*))`; +const preReleaseIdentifier = `(?:${numericIdentifier}|${nonNumericIdentifier})`; +const preRelease = `(?:-(${preReleaseIdentifier}(?:\\.${preReleaseIdentifier})*))`; +const xRangeIdentifier = `${numericIdentifier}|x|X|\\*`; +const xRangePlain = `[v=\\s]*(${xRangeIdentifier})(?:\\.(${xRangeIdentifier})(?:\\.(${xRangeIdentifier})(?:${preRelease})?${build}?)?)?`; +const hyphenRange = `^\\s*(${xRangePlain})\\s+-\\s+(${xRangePlain})\\s*$`; +const mainVersionLoose = `(${numericIdentifierLoose})\\.(${numericIdentifierLoose})\\.(${numericIdentifierLoose})`; +const loosePlain = `[v=\\s]*${mainVersionLoose}${preReleaseLoose}?${build}?`; +const gtlt = "((?:<|>)?=?)"; +const comparatorTrim = `(\\s*)${gtlt}\\s*(${loosePlain}|${xRangePlain})`; +const loneTilde = "(?:~>?)"; +const tildeTrim = `(\\s*)${loneTilde}\\s+`; +const loneCaret = "(?:\\^)"; +const caretTrim = `(\\s*)${loneCaret}\\s+`; +const star = "(<|>)?=?\\s*\\*"; +const caret = `^${loneCaret}${xRangePlain}$`; +const mainVersion = `(${numericIdentifier})\\.(${numericIdentifier})\\.(${numericIdentifier})`; +const fullPlain = `v?${mainVersion}${preRelease}?${build}?`; +const tilde = `^${loneTilde}${xRangePlain}$`; +const xRange = `^${gtlt}\\s*${xRangePlain}$`; +const comparator = `^${gtlt}\\s*(${fullPlain})$|^$`; +const gte0 = "^\\s*>=\\s*0.0.0\\s*$"; +function parseRegex(source) { + return new RegExp(source); +} +function isXVersion(version) { + return !version || version.toLowerCase() === "x" || version === "*"; +} +function pipe(...fns) { + return (x) => { + return fns.reduce((v, f) => f(v), x); + }; +} +function extractComparator(comparatorString) { + return comparatorString.match(parseRegex(comparator)); +} +function combineVersion(major, minor, patch, preRelease2) { + const mainVersion2 = `${major}.${minor}.${patch}`; + if (preRelease2) { + return `${mainVersion2}-${preRelease2}`; + } + return mainVersion2; +} +function parseHyphen(range) { + return range.replace( + parseRegex(hyphenRange), + (_range, from, fromMajor, fromMinor, fromPatch, _fromPreRelease, _fromBuild, to, toMajor, toMinor, toPatch, toPreRelease) => { + if (isXVersion(fromMajor)) { + from = ""; + } else if (isXVersion(fromMinor)) { + from = `>=${fromMajor}.0.0`; + } else if (isXVersion(fromPatch)) { + from = `>=${fromMajor}.${fromMinor}.0`; + } else { + from = `>=${from}`; + } + if (isXVersion(toMajor)) { + to = ""; + } else if (isXVersion(toMinor)) { + to = `<${+toMajor + 1}.0.0-0`; + } else if (isXVersion(toPatch)) { + to = `<${toMajor}.${+toMinor + 1}.0-0`; + } else if (toPreRelease) { + to = `<=${toMajor}.${toMinor}.${toPatch}-${toPreRelease}`; + } else { + to = `<=${to}`; + } + return `${from} ${to}`.trim(); + } + ); +} +function parseComparatorTrim(range) { + return range.replace(parseRegex(comparatorTrim), "$1$2$3"); +} +function parseTildeTrim(range) { + return range.replace(parseRegex(tildeTrim), "$1~"); +} +function parseCaretTrim(range) { + return range.replace(parseRegex(caretTrim), "$1^"); +} +function parseCarets(range) { + return range.trim().split(/\s+/).map((rangeVersion) => { + return rangeVersion.replace( + parseRegex(caret), + (_, major, minor, patch, preRelease2) => { + if (isXVersion(major)) { + return ""; + } else if (isXVersion(minor)) { + return `>=${major}.0.0 <${+major + 1}.0.0-0`; + } else if (isXVersion(patch)) { + if (major === "0") { + return `>=${major}.${minor}.0 <${major}.${+minor + 1}.0-0`; + } else { + return `>=${major}.${minor}.0 <${+major + 1}.0.0-0`; + } + } else if (preRelease2) { + if (major === "0") { + if (minor === "0") { + return `>=${major}.${minor}.${patch}-${preRelease2} <${major}.${minor}.${+patch + 1}-0`; + } else { + return `>=${major}.${minor}.${patch}-${preRelease2} <${major}.${+minor + 1}.0-0`; + } + } else { + return `>=${major}.${minor}.${patch}-${preRelease2} <${+major + 1}.0.0-0`; + } + } else { + if (major === "0") { + if (minor === "0") { + return `>=${major}.${minor}.${patch} <${major}.${minor}.${+patch + 1}-0`; + } else { + return `>=${major}.${minor}.${patch} <${major}.${+minor + 1}.0-0`; + } + } + return `>=${major}.${minor}.${patch} <${+major + 1}.0.0-0`; + } + } + ); + }).join(" "); +} +function parseTildes(range) { + return range.trim().split(/\s+/).map((rangeVersion) => { + return rangeVersion.replace( + parseRegex(tilde), + (_, major, minor, patch, preRelease2) => { + if (isXVersion(major)) { + return ""; + } else if (isXVersion(minor)) { + return `>=${major}.0.0 <${+major + 1}.0.0-0`; + } else if (isXVersion(patch)) { + return `>=${major}.${minor}.0 <${major}.${+minor + 1}.0-0`; + } else if (preRelease2) { + return `>=${major}.${minor}.${patch}-${preRelease2} <${major}.${+minor + 1}.0-0`; + } + return `>=${major}.${minor}.${patch} <${major}.${+minor + 1}.0-0`; + } + ); + }).join(" "); +} +function parseXRanges(range) { + return range.split(/\s+/).map((rangeVersion) => { + return rangeVersion.trim().replace( + parseRegex(xRange), + (ret, gtlt2, major, minor, patch, preRelease2) => { + const isXMajor = isXVersion(major); + const isXMinor = isXMajor || isXVersion(minor); + const isXPatch = isXMinor || isXVersion(patch); + if (gtlt2 === "=" && isXPatch) { + gtlt2 = ""; + } + preRelease2 = ""; + if (isXMajor) { + if (gtlt2 === ">" || gtlt2 === "<") { + return "<0.0.0-0"; + } else { + return "*"; + } + } else if (gtlt2 && isXPatch) { + if (isXMinor) { + minor = 0; + } + patch = 0; + if (gtlt2 === ">") { + gtlt2 = ">="; + if (isXMinor) { + major = +major + 1; + minor = 0; + patch = 0; + } else { + minor = +minor + 1; + patch = 0; + } + } else if (gtlt2 === "<=") { + gtlt2 = "<"; + if (isXMinor) { + major = +major + 1; + } else { + minor = +minor + 1; + } + } + if (gtlt2 === "<") { + preRelease2 = "-0"; + } + return `${gtlt2 + major}.${minor}.${patch}${preRelease2}`; + } else if (isXMinor) { + return `>=${major}.0.0${preRelease2} <${+major + 1}.0.0-0`; + } else if (isXPatch) { + return `>=${major}.${minor}.0${preRelease2} <${major}.${+minor + 1}.0-0`; + } + return ret; + } + ); + }).join(" "); +} +function parseStar(range) { + return range.trim().replace(parseRegex(star), ""); +} +function parseGTE0(comparatorString) { + return comparatorString.trim().replace(parseRegex(gte0), ""); +} +function compareAtom(rangeAtom, versionAtom) { + rangeAtom = +rangeAtom || rangeAtom; + versionAtom = +versionAtom || versionAtom; + if (rangeAtom > versionAtom) { + return 1; + } + if (rangeAtom === versionAtom) { + return 0; + } + return -1; +} +function comparePreRelease(rangeAtom, versionAtom) { + const { preRelease: rangePreRelease } = rangeAtom; + const { preRelease: versionPreRelease } = versionAtom; + if (rangePreRelease === void 0 && !!versionPreRelease) { + return 1; + } + if (!!rangePreRelease && versionPreRelease === void 0) { + return -1; + } + if (rangePreRelease === void 0 && versionPreRelease === void 0) { + return 0; + } + for (let i = 0, n = rangePreRelease.length; i <= n; i++) { + const rangeElement = rangePreRelease[i]; + const versionElement = versionPreRelease[i]; + if (rangeElement === versionElement) { + continue; + } + if (rangeElement === void 0 && versionElement === void 0) { + return 0; + } + if (!rangeElement) { + return 1; + } + if (!versionElement) { + return -1; + } + return compareAtom(rangeElement, versionElement); + } + return 0; +} +function compareVersion(rangeAtom, versionAtom) { + return compareAtom(rangeAtom.major, versionAtom.major) || compareAtom(rangeAtom.minor, versionAtom.minor) || compareAtom(rangeAtom.patch, versionAtom.patch) || comparePreRelease(rangeAtom, versionAtom); +} +function eq(rangeAtom, versionAtom) { + return rangeAtom.version === versionAtom.version; +} +function compare(rangeAtom, versionAtom) { + switch (rangeAtom.operator) { + case "": + case "=": + return eq(rangeAtom, versionAtom); + case ">": + return compareVersion(rangeAtom, versionAtom) < 0; + case ">=": + return eq(rangeAtom, versionAtom) || compareVersion(rangeAtom, versionAtom) < 0; + case "<": + return compareVersion(rangeAtom, versionAtom) > 0; + case "<=": + return eq(rangeAtom, versionAtom) || compareVersion(rangeAtom, versionAtom) > 0; + case void 0: { + return true; + } + default: + return false; + } +} +function parseComparatorString(range) { + return pipe( + parseCarets, + parseTildes, + parseXRanges, + parseStar + )(range); +} +function parseRange(range) { + return pipe( + parseHyphen, + parseComparatorTrim, + parseTildeTrim, + parseCaretTrim + )(range.trim()).split(/\s+/).join(" "); +} +function satisfy(version, range) { + if (!version) { + return false; + } + const parsedRange = parseRange(range); + const parsedComparator = parsedRange.split(" ").map((rangeVersion) => parseComparatorString(rangeVersion)).join(" "); + const comparators = parsedComparator.split(/\s+/).map((comparator2) => parseGTE0(comparator2)); + const extractedVersion = extractComparator(version); + if (!extractedVersion) { + return false; + } + const [ + , + versionOperator, + , + versionMajor, + versionMinor, + versionPatch, + versionPreRelease + ] = extractedVersion; + const versionAtom = { + version: combineVersion( + versionMajor, + versionMinor, + versionPatch, + versionPreRelease + ), + major: versionMajor, + minor: versionMinor, + patch: versionPatch, + preRelease: versionPreRelease == null ? void 0 : versionPreRelease.split(".") + }; + for (const comparator2 of comparators) { + const extractedComparator = extractComparator(comparator2); + if (!extractedComparator) { + return false; + } + const [ + , + rangeOperator, + , + rangeMajor, + rangeMinor, + rangePatch, + rangePreRelease + ] = extractedComparator; + const rangeAtom = { + operator: rangeOperator, + version: combineVersion( + rangeMajor, + rangeMinor, + rangePatch, + rangePreRelease + ), + major: rangeMajor, + minor: rangeMinor, + patch: rangePatch, + preRelease: rangePreRelease == null ? void 0 : rangePreRelease.split(".") + }; + if (!compare(rangeAtom, versionAtom)) { + return false; + } + } + return true; +} + +// eslint-disable-next-line no-undef +const moduleMap = {}; +const moduleCache = Object.create(null); +async function importShared(name, shareScope = 'default') { + return moduleCache[name] + ? new Promise((r) => r(moduleCache[name])) + : (await getSharedFromRuntime(name, shareScope)) || getSharedFromLocal(name) +} +async function getSharedFromRuntime(name, shareScope) { + let module = null; + if (globalThis?.__federation_shared__?.[shareScope]?.[name]) { + const versionObj = globalThis.__federation_shared__[shareScope][name]; + const requiredVersion = moduleMap[name]?.requiredVersion; + const hasRequiredVersion = !!requiredVersion; + if (hasRequiredVersion) { + const versionKey = Object.keys(versionObj).find((version) => + satisfy(version, requiredVersion) + ); + if (versionKey) { + const versionValue = versionObj[versionKey]; + module = await (await versionValue.get())(); + } else { + console.log( + `provider support ${name}(${versionKey}) is not satisfied requiredVersion(\${moduleMap[name].requiredVersion})` + ); + } + } else { + const versionKey = Object.keys(versionObj)[0]; + const versionValue = versionObj[versionKey]; + module = await (await versionValue.get())(); + } + } + if (module) { + return flattenModule(module, name) + } +} +async function getSharedFromLocal(name) { + if (moduleMap[name]?.import) { + let module = await (await moduleMap[name].get())(); + return flattenModule(module, name) + } else { + console.error( + `consumer config import=false,so cant use callback shared module` + ); + } +} +function flattenModule(module, name) { + // use a shared module which export default a function will getting error 'TypeError: xxx is not a function' + if (typeof module.default === 'function') { + Object.keys(module).forEach((key) => { + if (key !== 'default') { + module.default[key] = module[key]; + } + }); + moduleCache[name] = module.default; + return module.default + } + if (module.default) module = Object.assign({}, module.default, module); + moduleCache[name] = module; + return module +} + +export { importShared, getSharedFromLocal as importSharedLocal, getSharedFromRuntime as importSharedRuntime }; diff --git a/plugins.v3/subscribeassistantenhanced/frontend/dist/assets/index-Z2CmsQc7.js b/plugins.v3/subscribeassistantenhanced/frontend/dist/assets/index-Z2CmsQc7.js new file mode 100644 index 00000000..1955a026 --- /dev/null +++ b/plugins.v3/subscribeassistantenhanced/frontend/dist/assets/index-Z2CmsQc7.js @@ -0,0 +1 @@ +export { default as Config } from './__federation_expose_Config-CaQEeIYR.js'; diff --git a/plugins.v3/subscribeassistantenhanced/frontend/dist/assets/remoteEntry.js b/plugins.v3/subscribeassistantenhanced/frontend/dist/assets/remoteEntry.js new file mode 100644 index 00000000..eca71837 --- /dev/null +++ b/plugins.v3/subscribeassistantenhanced/frontend/dist/assets/remoteEntry.js @@ -0,0 +1,81 @@ +const currentImports = {}; + const exportSet = new Set(['Module', '__esModule', 'default', '_export_sfc']); + let moduleMap = { +"./Config":()=>{ + dynamicLoadingCss(["__federation_expose_Config-COZtkKfx.css"], false, './Config'); + return __federation_import('./__federation_expose_Config-CaQEeIYR.js').then(module =>Object.keys(module).every(item => exportSet.has(item)) ? () => module.default : () => module)},}; + const seen = {}; + const dynamicLoadingCss = (cssFilePaths, dontAppendStylesToHead, exposeItemName) => { + const metaUrl = import.meta.url; + if (typeof metaUrl === 'undefined') { + console.warn('The remote style takes effect only when the build.target option in the vite.config.ts file is higher than that of "es2020".'); + return; + } + + const curUrl = metaUrl.substring(0, metaUrl.lastIndexOf('remoteEntry.js')); + const base = '/'; + 'assets'; + + cssFilePaths.forEach(cssPath => { + let href = ''; + const baseUrl = base || curUrl; + if (baseUrl) { + const trimmer = { + trailing: (path) => (path.endsWith('/') ? path.slice(0, -1) : path), + leading: (path) => (path.startsWith('/') ? path.slice(1) : path) + }; + const isAbsoluteUrl = (url) => url.startsWith('http') || url.startsWith('//'); + + const cleanBaseUrl = trimmer.trailing(baseUrl); + const cleanCssPath = trimmer.leading(cssPath); + const cleanCurUrl = trimmer.trailing(curUrl); + + if (isAbsoluteUrl(baseUrl)) { + href = [cleanBaseUrl, cleanCssPath].filter(Boolean).join('/'); + } else { + if (cleanCurUrl.includes(cleanBaseUrl)) { + href = [cleanCurUrl, cleanCssPath].filter(Boolean).join('/'); + } else { + href = [cleanCurUrl + cleanBaseUrl, cleanCssPath].filter(Boolean).join('/'); + } + } + } else { + href = cssPath; + } + + if (dontAppendStylesToHead) { + const key = 'css__SubscribeAssistantEnhanced__' + exposeItemName; + window[key] = window[key] || []; + window[key].push(href); + return; + } + + if (href in seen) return; + seen[href] = true; + + const element = document.createElement('link'); + element.rel = 'stylesheet'; + element.href = href; + document.head.appendChild(element); + }); + }; + async function __federation_import(name) { + currentImports[name] ??= import(name); + return currentImports[name] + } const get =(module) => { + if(!moduleMap[module]) throw new Error('Can not find remote module ' + module) + return moduleMap[module](); + }; + const init =(shareScope) => { + globalThis.__federation_shared__= globalThis.__federation_shared__|| {}; + Object.entries(shareScope).forEach(([key, value]) => { + for (const [versionKey, versionValue] of Object.entries(value)) { + const scope = versionValue.scope || 'default'; + globalThis.__federation_shared__[scope] = globalThis.__federation_shared__[scope] || {}; + const shared= globalThis.__federation_shared__[scope]; + (shared[key] = shared[key]||{})[versionKey] = versionValue; + } + }); + }; + +export { dynamicLoadingCss, get, init }; diff --git a/tests/ci/test_plugin_release_directory.py b/tests/ci/test_plugin_release_directory.py index 6817716a..daeba038 100644 --- a/tests/ci/test_plugin_release_directory.py +++ b/tests/ci/test_plugin_release_directory.py @@ -65,3 +65,38 @@ def test_release_workflow_uses_generation_aware_directory_selector() -> None: assert 'git rev-parse -q --verify "refs/tags/$tag"' in workflow assert 'prev_tag="$tag"' in workflow assert 'if [ -d "$dir2" ]; then plugin_dir="$dir2"; fi' not in workflow + + +def test_release_workflow_builds_frontend_before_packaging() -> None: + """带前端工程的插件必须构建并校验联邦入口后才能打包。""" + workflow = WORKFLOW.read_text(encoding="utf-8") + + assert "uses: actions/setup-node@v7" in workflow + assert "build_frontend \"$plugin_dir\"" in workflow + assert "yarn --frozen-lockfile && yarn build" in workflow + assert '[ ! -s "$frontend_dir/dist/assets/remoteEntry.js" ]' in workflow + assert 'git check-ignore -q "$frontend_dir/dist/assets/remoteEntry.js"' in workflow + assert 'git status --porcelain --untracked-files=all -- "$frontend_dir/dist"' in workflow + assert '-x "*/node_modules/*"' in workflow + + +def test_frontend_federation_entries_are_tracked() -> None: + """Release 下载失败时会回退 main,V2/V3 联邦入口必须存在于 Git 文件列表。""" + missing = [] + for plugin_generation in ("plugins.v2", "plugins.v3"): + for package_path in sorted( + REPO_ROOT.glob(f"{plugin_generation}/*/frontend/package.json") + ): + remote_entry = package_path.parent / "dist/assets/remoteEntry.js" + relative_entry = remote_entry.relative_to(REPO_ROOT) + result = subprocess.run( + ["git", "ls-files", "--error-unmatch", str(relative_entry)], + cwd=REPO_ROOT, + capture_output=True, + text=True, + check=False, + ) + if result.returncode != 0: + missing.append(str(relative_entry)) + + assert missing == []