Repository: EstrellaXD/Auto_Bangumi Branch: main Commit: 717ad11f7fad Files: 472 Total size: 1.7 MB Directory structure: gitextract_e3m8bq15/ ├── .dockerignore ├── .gitattributes ├── .github/ │ ├── ISSUE_TEMPLATE/ │ │ ├── bug_report.yml │ │ ├── config.yml │ │ ├── discussion.yml │ │ ├── feature_request.yml │ │ ├── parser_bug.yml │ │ ├── rename_bug.yml │ │ └── rfc.yml │ ├── PULL_REQUEST_TEMPLATE/ │ │ └── pull_request_template.md │ └── workflows/ │ └── build.yml ├── .gitignore ├── .vscode/ │ ├── extensions.json │ ├── launch.json │ └── settings.json ├── CHANGELOG.md ├── CLAUDE.md ├── CONTRIBUTING.md ├── Dockerfile ├── LICENSE ├── README.md ├── SECURITY.md ├── backend/ │ ├── .pre-commit-config.yaml │ ├── .vscode/ │ │ └── settings.json │ ├── dev.sh │ ├── pyproject.toml │ ├── scripts/ │ │ └── pip-lock-version.sh │ └── src/ │ ├── dev_server.py │ ├── main.py │ ├── module/ │ │ ├── __init__.py │ │ ├── ab_decorator/ │ │ │ ├── __init__.py │ │ │ └── timeout.py │ │ ├── api/ │ │ │ ├── __init__.py │ │ │ ├── auth.py │ │ │ ├── bangumi.py │ │ │ ├── config.py │ │ │ ├── downloader.py │ │ │ ├── log.py │ │ │ ├── notification.py │ │ │ ├── passkey.py │ │ │ ├── program.py │ │ │ ├── response.py │ │ │ ├── rss.py │ │ │ ├── search.py │ │ │ └── setup.py │ │ ├── checker/ │ │ │ ├── __init__.py │ │ │ └── checker.py │ │ ├── conf/ │ │ │ ├── __init__.py │ │ │ ├── config.py │ │ │ ├── const.py │ │ │ ├── log.py │ │ │ ├── parse.py │ │ │ ├── search_provider.py │ │ │ └── uvicorn_logging.py │ │ ├── core/ │ │ │ ├── __init__.py │ │ │ ├── offset_scanner.py │ │ │ ├── program.py │ │ │ ├── status.py │ │ │ └── sub_thread.py │ │ ├── database/ │ │ │ ├── __init__.py │ │ │ ├── bangumi.py │ │ │ ├── combine.py │ │ │ ├── engine.py │ │ │ ├── passkey.py │ │ │ ├── rss.py │ │ │ ├── torrent.py │ │ │ └── user.py │ │ ├── downloader/ │ │ │ ├── __init__.py │ │ │ ├── client/ │ │ │ │ ├── __init__.py │ │ │ │ ├── aria2_downloader.py │ │ │ │ ├── mock_downloader.py │ │ │ │ ├── qb_downloader.py │ │ │ │ └── tr_downloader.py │ │ │ ├── download_client.py │ │ │ ├── exceptions.py │ │ │ └── path.py │ │ ├── manager/ │ │ │ ├── __init__.py │ │ │ ├── collector.py │ │ │ ├── renamer.py │ │ │ └── torrent.py │ │ ├── mcp/ │ │ │ ├── __init__.py │ │ │ ├── resources.py │ │ │ ├── security.py │ │ │ ├── server.py │ │ │ └── tools.py │ │ ├── models/ │ │ │ ├── __init__.py │ │ │ ├── api.py │ │ │ ├── bangumi.py │ │ │ ├── config.py │ │ │ ├── passkey.py │ │ │ ├── response.py │ │ │ ├── rss.py │ │ │ ├── torrent.py │ │ │ └── user.py │ │ ├── network/ │ │ │ ├── __init__.py │ │ │ ├── request_contents.py │ │ │ ├── request_url.py │ │ │ └── site/ │ │ │ ├── __init__.py │ │ │ └── mikan.py │ │ ├── notification/ │ │ │ ├── __init__.py │ │ │ ├── base.py │ │ │ ├── manager.py │ │ │ ├── notification.py │ │ │ ├── plugin/ │ │ │ │ ├── __init__.py │ │ │ │ ├── bark.py │ │ │ │ ├── server_chan.py │ │ │ │ ├── slack.py │ │ │ │ ├── telegram.py │ │ │ │ └── wecom.py │ │ │ └── providers/ │ │ │ ├── __init__.py │ │ │ ├── bark.py │ │ │ ├── discord.py │ │ │ ├── gotify.py │ │ │ ├── pushover.py │ │ │ ├── server_chan.py │ │ │ ├── telegram.py │ │ │ ├── webhook.py │ │ │ └── wecom.py │ │ ├── parser/ │ │ │ ├── __init__.py │ │ │ ├── analyser/ │ │ │ │ ├── __init__.py │ │ │ │ ├── bgm_calendar.py │ │ │ │ ├── bgm_parser.py │ │ │ │ ├── mikan_parser.py │ │ │ │ ├── offset_detector.py │ │ │ │ ├── openai.py │ │ │ │ ├── raw_parser.py │ │ │ │ ├── tmdb_parser.py │ │ │ │ └── torrent_parser.py │ │ │ └── title_parser.py │ │ ├── rss/ │ │ │ ├── __init__.py │ │ │ ├── analyser.py │ │ │ └── engine.py │ │ ├── searcher/ │ │ │ ├── __init__.py │ │ │ ├── provider.py │ │ │ └── searcher.py │ │ ├── security/ │ │ │ ├── __init__.py │ │ │ ├── api.py │ │ │ ├── auth_strategy.py │ │ │ ├── jwt.py │ │ │ └── webauthn.py │ │ ├── update/ │ │ │ ├── __init__.py │ │ │ ├── cross_version.py │ │ │ ├── data_migration.py │ │ │ ├── rss.py │ │ │ ├── startup.py │ │ │ └── version_check.py │ │ └── utils/ │ │ ├── __init__.py │ │ ├── bangumi_data.py │ │ ├── cache_image.py │ │ └── json_config.py │ ├── test/ │ │ ├── __init__.py │ │ ├── conftest.py │ │ ├── e2e/ │ │ │ ├── Dockerfile.mock-rss │ │ │ ├── __init__.py │ │ │ ├── conftest.py │ │ │ ├── docker-compose.test.yml │ │ │ ├── fixtures/ │ │ │ │ └── mikan.xml │ │ │ ├── mock_rss_server.py │ │ │ └── test_e2e_workflow.py │ │ ├── factories.py │ │ ├── test_api_auth.py │ │ ├── test_api_bangumi.py │ │ ├── test_api_bangumi_extended.py │ │ ├── test_api_config.py │ │ ├── test_api_downloader.py │ │ ├── test_api_log.py │ │ ├── test_api_passkey.py │ │ ├── test_api_program.py │ │ ├── test_api_rss.py │ │ ├── test_api_search.py │ │ ├── test_auth.py │ │ ├── test_config.py │ │ ├── test_database.py │ │ ├── test_download_client.py │ │ ├── test_integration.py │ │ ├── test_issue_bugs.py │ │ ├── test_mcp_resources.py │ │ ├── test_mcp_security.py │ │ ├── test_mcp_tools.py │ │ ├── test_migration.py │ │ ├── test_mock_downloader.py │ │ ├── test_notification.py │ │ ├── test_openai.py │ │ ├── test_path.py │ │ ├── test_path_parser.py │ │ ├── test_qb_downloader.py │ │ ├── test_raw_parser.py │ │ ├── test_renamer.py │ │ ├── test_rss_engine.py │ │ ├── test_rss_engine_new.py │ │ ├── test_searcher.py │ │ ├── test_setup.py │ │ ├── test_title_parser.py │ │ ├── test_tmdb.py │ │ └── test_torrent_parser.py │ └── test_passkey_server.py ├── docs/ │ ├── .vitepress/ │ │ ├── config.ts │ │ └── theme/ │ │ ├── components/ │ │ │ └── HomePreviewWebUI.vue │ │ ├── index.ts │ │ └── style.css │ ├── api/ │ │ └── index.md │ ├── changelog/ │ │ ├── 2.6.md │ │ ├── 3.0.md │ │ ├── 3.1.md │ │ ├── 3.2-zh.md │ │ └── 3.2.md │ ├── config/ │ │ ├── downloader.md │ │ ├── experimental.md │ │ ├── manager.md │ │ ├── notifier.md │ │ ├── parser.md │ │ ├── program.md │ │ ├── proxy.md │ │ └── rss.md │ ├── deploy/ │ │ ├── docker-cli.md │ │ ├── docker-compose.md │ │ ├── dsm.md │ │ ├── local.md │ │ └── quick-start.md │ ├── dev/ │ │ ├── database.md │ │ ├── e2e-test-guide.md │ │ └── index.md │ ├── en/ │ │ ├── api/ │ │ │ └── index.md │ │ ├── changelog/ │ │ │ ├── 2.6.md │ │ │ ├── 3.0.md │ │ │ ├── 3.1.md │ │ │ └── 3.2.md │ │ ├── config/ │ │ │ ├── downloader.md │ │ │ ├── experimental.md │ │ │ ├── manager.md │ │ │ ├── notifier.md │ │ │ ├── parser.md │ │ │ ├── program.md │ │ │ ├── proxy.md │ │ │ └── rss.md │ │ ├── deploy/ │ │ │ ├── docker-cli.md │ │ │ ├── docker-compose.md │ │ │ ├── dsm.md │ │ │ ├── local.md │ │ │ └── quick-start.md │ │ ├── dev/ │ │ │ ├── database.md │ │ │ └── index.md │ │ ├── faq/ │ │ │ ├── index.md │ │ │ ├── network.md │ │ │ └── troubleshooting.md │ │ ├── feature/ │ │ │ ├── bangumi.md │ │ │ ├── calendar.md │ │ │ ├── rename.md │ │ │ ├── rss.md │ │ │ └── search.md │ │ ├── home/ │ │ │ ├── index.md │ │ │ └── pipline.md │ │ └── index.md │ ├── faq/ │ │ ├── index.md │ │ ├── network.md │ │ └── troubleshooting.md │ ├── feature/ │ │ ├── bangumi.md │ │ ├── calendar.md │ │ ├── rename.md │ │ ├── rss.md │ │ └── search.md │ ├── home/ │ │ ├── index.md │ │ └── pipline.md │ ├── index.md │ ├── ja/ │ │ ├── api/ │ │ │ └── index.md │ │ ├── changelog/ │ │ │ ├── 2.6.md │ │ │ ├── 3.0.md │ │ │ ├── 3.1.md │ │ │ └── 3.2.md │ │ ├── config/ │ │ │ ├── downloader.md │ │ │ ├── experimental.md │ │ │ ├── manager.md │ │ │ ├── notifier.md │ │ │ ├── parser.md │ │ │ ├── program.md │ │ │ ├── proxy.md │ │ │ └── rss.md │ │ ├── deploy/ │ │ │ ├── docker-cli.md │ │ │ ├── docker-compose.md │ │ │ ├── dsm.md │ │ │ ├── local.md │ │ │ └── quick-start.md │ │ ├── dev/ │ │ │ ├── database.md │ │ │ └── index.md │ │ ├── faq/ │ │ │ ├── index.md │ │ │ ├── network.md │ │ │ └── troubleshooting.md │ │ ├── feature/ │ │ │ ├── bangumi.md │ │ │ ├── calendar.md │ │ │ ├── rename.md │ │ │ ├── rss.md │ │ │ └── search.md │ │ ├── home/ │ │ │ ├── index.md │ │ │ └── pipline.md │ │ └── index.md │ ├── package.json │ ├── plans/ │ │ ├── 2026-01-25-search-panel-redesign.md │ │ └── 2026-02-23-calendar-drag-organize-design.md │ ├── resource/ │ │ ├── docker-compose/ │ │ │ ├── AutoBangumi/ │ │ │ │ └── docker-compose.yml │ │ │ └── qBittorrent+AutoBangumi/ │ │ │ └── docker-compose.yml │ │ └── unraid.xml │ ├── tsconfig.json │ └── vercel.json ├── entrypoint.sh ├── scripts/ │ └── generate-beta-notes.sh └── webui/ ├── .eslintignore ├── .eslintrc.json ├── .husky/ │ └── pre-commit ├── .neoconf.json ├── .npmrc ├── .prettierignore ├── .prettierrc.json ├── .storybook/ │ ├── main.ts │ └── preview.ts ├── .vscode/ │ ├── extensions.json │ └── settings.json ├── LICENSE ├── README.md ├── index.html ├── package.json ├── public/ │ └── robots.txt ├── src/ │ ├── App.vue │ ├── api/ │ │ ├── __tests__/ │ │ │ ├── auth.test.ts │ │ │ ├── bangumi.test.ts │ │ │ └── rss.test.ts │ │ ├── auth.ts │ │ ├── bangumi.ts │ │ ├── check.ts │ │ ├── config.ts │ │ ├── download.ts │ │ ├── downloader.ts │ │ ├── log.ts │ │ ├── notification.ts │ │ ├── passkey.ts │ │ ├── program.ts │ │ ├── rss.ts │ │ ├── search.ts │ │ └── setup.ts │ ├── components/ │ │ ├── ab-add-rss.vue │ │ ├── ab-bangumi-card.vue │ │ ├── ab-change-account.vue │ │ ├── ab-container.vue │ │ ├── ab-edit-rule.vue │ │ ├── ab-fold-panel.vue │ │ ├── ab-image.vue │ │ ├── ab-label.vue │ │ ├── ab-popup.vue │ │ ├── ab-rule.vue │ │ ├── ab-search-bar.vue │ │ ├── ab-setting.vue │ │ ├── ab-status-bar.vue │ │ ├── basic/ │ │ │ ├── __tests__/ │ │ │ │ ├── ab-button.test.ts │ │ │ │ └── ab-switch.test.ts │ │ │ ├── ab-adaptive-modal.vue │ │ │ ├── ab-add.stories.ts │ │ │ ├── ab-add.vue │ │ │ ├── ab-bottom-sheet.vue │ │ │ ├── ab-button-multi.stories.ts │ │ │ ├── ab-button-multi.vue │ │ │ ├── ab-button.stories.ts │ │ │ ├── ab-button.vue │ │ │ ├── ab-checkbox.stories.ts │ │ │ ├── ab-checkbox.vue │ │ │ ├── ab-data-list.vue │ │ │ ├── ab-offset-mismatch-dialog.vue │ │ │ ├── ab-page-title.stories.ts │ │ │ ├── ab-page-title.vue │ │ │ ├── ab-pull-refresh.vue │ │ │ ├── ab-search.stories.ts │ │ │ ├── ab-search.vue │ │ │ ├── ab-select.stories.ts │ │ │ ├── ab-select.vue │ │ │ ├── ab-status.stories.ts │ │ │ ├── ab-status.vue │ │ │ ├── ab-swipe-container.vue │ │ │ ├── ab-switch.stories.ts │ │ │ ├── ab-switch.vue │ │ │ ├── ab-tag.stories.ts │ │ │ └── ab-tag.vue │ │ ├── layout/ │ │ │ ├── ab-mobile-nav.vue │ │ │ ├── ab-sidebar.vue │ │ │ └── ab-topbar.vue │ │ ├── media-query.vue │ │ ├── search/ │ │ │ ├── ab-search-card.vue │ │ │ ├── ab-search-confirm.vue │ │ │ ├── ab-search-filters.vue │ │ │ └── ab-search-modal.vue │ │ ├── setting/ │ │ │ ├── config-download.vue │ │ │ ├── config-manage.vue │ │ │ ├── config-normal.vue │ │ │ ├── config-notification.vue │ │ │ ├── config-openai.vue │ │ │ ├── config-parser.vue │ │ │ ├── config-passkey.vue │ │ │ ├── config-player.vue │ │ │ ├── config-proxy.vue │ │ │ ├── config-search-provider.vue │ │ │ └── config-security.vue │ │ └── setup/ │ │ ├── wizard-container.vue │ │ ├── wizard-step-account.vue │ │ ├── wizard-step-downloader.vue │ │ ├── wizard-step-notification.vue │ │ ├── wizard-step-review.vue │ │ ├── wizard-step-rss.vue │ │ └── wizard-step-welcome.vue │ ├── hooks/ │ │ ├── __tests__/ │ │ │ ├── useApi.test.ts │ │ │ └── useAuth.test.ts │ │ ├── useAddRss.ts │ │ ├── useApi.ts │ │ ├── useAppInfo.ts │ │ ├── useAuth.ts │ │ ├── useBreakpointQuery.ts │ │ ├── useDarkMode.ts │ │ ├── useMessage.ts │ │ ├── useMyI18n.ts │ │ ├── usePasskey.ts │ │ └── useSafeArea.ts │ ├── i18n/ │ │ ├── en.json │ │ └── zh-CN.json │ ├── main.ts │ ├── pages/ │ │ ├── index/ │ │ │ ├── bangumi.vue │ │ │ ├── calendar.vue │ │ │ ├── config.vue │ │ │ ├── downloader.vue │ │ │ ├── log.vue │ │ │ ├── player.vue │ │ │ └── rss.vue │ │ ├── index.vue │ │ ├── login.vue │ │ └── setup.vue │ ├── router/ │ │ └── index.ts │ ├── services/ │ │ └── webauthn.ts │ ├── store/ │ │ ├── __tests__/ │ │ │ ├── bangumi.test.ts │ │ │ └── rss.test.ts │ │ ├── bangumi.ts │ │ ├── config.ts │ │ ├── downloader.ts │ │ ├── log.ts │ │ ├── player.ts │ │ ├── program.ts │ │ ├── rss.ts │ │ ├── search.ts │ │ └── setup.ts │ ├── style/ │ │ ├── global.scss │ │ ├── mixin.scss │ │ ├── transition.scss │ │ └── var.scss │ ├── test/ │ │ ├── mocks/ │ │ │ └── api.ts │ │ └── setup.ts │ └── utils/ │ ├── axios.ts │ └── poster.ts ├── tsconfig.json ├── tsconfig.node.json ├── types/ │ ├── api.ts │ ├── auth.ts │ ├── bangumi.ts │ ├── components.ts │ ├── config.ts │ ├── downloader.ts │ ├── dts/ │ │ ├── auto-imports.d.ts │ │ ├── components.d.ts │ │ ├── html.d.ts │ │ ├── router-type.d.ts │ │ └── vite-env.d.ts │ ├── passkey.ts │ ├── rss.ts │ ├── setup.ts │ ├── torrent.ts │ └── utils.ts ├── unocss.config.ts ├── vite.config.ts └── vitest.config.ts ================================================ FILE CONTENTS ================================================ ================================================ FILE: .dockerignore ================================================ __pycache__ *.pyc *.pyo *.pyd .Python env pip-log.txt pip-delete-this-directory.txt .tox .coverage .coverage.* .cache nosetests.xml coverage.xml *.cover *.log .git .mypy_cache ../.pytest_cache .hypothesis backend/src/module/tests backend/src/module/conf/const_dev.py config/bangumi.json/config/bangumi.json /docs /.github /.config /.data /.cache /LICENSE /README.md /setup.py dist.zip data config /backend/src/config /backend/src/data .pytest_cache test .env test.py ================================================ FILE: .gitattributes ================================================ # Don't allow people to merge changes to these generated files, because the result # may be invalid. You need to run "rush update" again. pnpm-lock.yaml merge=binary shrinkwrap.yaml merge=binary npm-shrinkwrap.json merge=binary yarn.lock merge=binary # Rush's JSON config files use JavaScript-style code comments. The rule below prevents pedantic # syntax highlighters such as GitHub's from highlighting these comments as errors. Your text editor # may also require a special configuration to allow comments in JSON. # # For more information, see this issue: https://github.com/microsoft/rushstack/issues/1088 # *.json linguist-language=JSON-with-Comments ================================================ FILE: .github/ISSUE_TEMPLATE/bug_report.yml ================================================ name: 问题反馈 description: File a bug report title: "[错误报告]请在此处简单描述你的问题" labels: ["bug"] body: - type: markdown attributes: value: | 描述问题前,请先更新到最新版本。 请确认以下信息,如果你没有完成以下检查,那么你的 issue 将会被直接关闭。 解析器问题请转到[专用模板](https://github.com/EstrellaXD/Auto_Bangumi/issues/new?assignees=&labels=bug&template=parser_bug.yml&title=%5B解析器错误%5D), 重命名问题请到[专用模板](https://github.com/EstrellaXD/Auto_Bangumi/issues/new?assignees=&labels=bug&template=rename_bug.yml&title=%5B重命名错误%5D) - type: checkboxes id: ensure attributes: label: 确认 description: 在提交 issue 之前,请确认你已经阅读并确认以下内容 options: - label: 我的版本是最新版本,我的版本号与 [version](https://github.com/EstrellaXD/Auto_Bangumi/releases/latest) 相同。 required: true - label: 我已经查阅了[已知问题](https://autobangumi.org/faq/),并确认我的问题不在其中。 required: true - label: 我已经 [issue](https://github.com/EstrellaXD/Auto_Bangumi/issues) 中搜索过,确认我的问题没有被提出过。 required: true - label: 我已经修改标题,将标题中的 **描述** 替换为我遇到的问题。 required: true - type: input id: version attributes: label: 当前程序版本 description: 遇到问题时程序所在的版本号 validations: required: true - type: dropdown id: type attributes: label: 问题类型 description: 你在以下哪个部分碰到了问题 options: - WebUI - 程序运行问题 - 其他问题 validations: required: true - type: textarea id: what-happened attributes: label: 问题描述 description: 请详细描述你碰到的问题 placeholder: "问题描述" validations: required: true - type: textarea id: logs attributes: label: 发生问题时系统日志 description: 问题出现时,程序运行日志请复制到这里。 render: bash ================================================ FILE: .github/ISSUE_TEMPLATE/config.yml ================================================ blank_issues_enabled: false contact_links: - name: 使用说明 url: https://github.com/EstrellaXD/Auto_Bangumi/wiki/Home about: 使用说明 - name: Twitter url: https://twitter.com/Estrella_Pan about: 推特联系我 ================================================ FILE: .github/ISSUE_TEMPLATE/discussion.yml ================================================ name: 项目讨论 description: discussion title: "[Discussion] " labels: ["discussion"] body: - type: markdown attributes: value: | [BUG](https://github.com/EstrellaXD/Auto_Bangumi/issues/new?assignees=&labels=bug&template=bug_report.yml&title=%5BBUG%5D%3A) 与 [Feature Request](https://github.com/EstrellaXD/Auto_Bangumi/issues/new?assignees=&labels=feature+request&template=feature_request.yml&title=%5BFeature+Request%5D%3A+) 请转到对应位置提交。 - type: textarea id: discussion attributes: label: 项目讨论 description: 请详细描述需要讨论的内容。 placeholder: "项目讨论" validations: required: true ================================================ FILE: .github/ISSUE_TEMPLATE/feature_request.yml ================================================ name: 功能改进 description: Feature Request title: "[Feature Request]" labels: ["feature request"] body: - type: markdown attributes: value: | 请说明你希望添加的功能。 - type: textarea id: feature-request attributes: label: 功能改进 description: 请详细描述需要改进或者添加的功能。 placeholder: "功能改进" validations: required: true ================================================ FILE: .github/ISSUE_TEMPLATE/parser_bug.yml ================================================ name: 解析器错误 description: Report parser bug title: "[解析器错误]" labels: ["bug"] body: - type: markdown attributes: value: | 描述问题前,请先更新到最新版本。 最新版本: [version](https://img.shields.io/docker/v/estrellaxd/auto_bangumi) 本模板仅限于解析器匹配错误。目前 AB 并不能解析类似 `[1-12]` 这样的合集 - type: input id: version attributes: label: 当前程序版本 description: 遇到问题时程序所在的版本号 validations: required: true - type: dropdown id: language attributes: label: 解析器语言设置 description: 你是用那种语言碰到了问题 options: - 默认:zh - en - jp validations: required: true - type: dropdown id: TMDB attributes: label: TMDB 解析 description: 是否开启 TMDB 解析 options: - 是 - 否 validations: required: true - type: input id: RawName attributes: label: 字幕组提供的名称 validations: required: true - type: input id: ErrorName attributes: label: 错误识别名称 description: 解析错误的名称,如果出现 `Not Matched` 确实非合集之类的无法解析的名称后再提交 issue validations: required: true ================================================ FILE: .github/ISSUE_TEMPLATE/rename_bug.yml ================================================ name: 重命名错误 description: Report parser bug title: "[重命名错误]" labels: ["bug"] body: - type: markdown attributes: value: | 描述问题前,请先更新到最新版本。 最新版本: [version](https://img.shields.io/docker/v/estrellaxd/auto_bangumi) 本模板仅限于重命名错误。目前 AB 并不能重命名合集,或者以文件夹形式下载的番剧,如果出现类似错误请等待版本更新! - type: input id: version attributes: label: 当前程序版本 description: 遇到问题时程序所在的版本号 validations: required: true - type: dropdown id: language attributes: label: 重命名设置 description: 你是用那重命名设置出现问题 options: - 默认:pn - normal - advance validations: required: true - type: input id: RawName attributes: label: 种子名称 description: 原本种子的名称 validations: required: true - type: input id: path attributes: label: 文件路径 description: 种子所在的目录,请以 AB 创建的文件夹为例子,如:`/Lycoris Recoil/Season 1/`,如果没有创建类似的文件夹请参考 [FAQ]() 中的排错指南。 validations: required: true - type: input id: ErrorName attributes: label: 错误命名 description: 重命名错误的名称 validations: required: true - type: textarea id: logs attributes: label: 发生问题时系统日志 description: 如果有条件,请打开 Debug 模式复制针对错误命名的日志。 render: shell ================================================ FILE: .github/ISSUE_TEMPLATE/rfc.yml ================================================ # https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-issue-forms name: 功能提案 description: Request for Comments title: "[RFC]" labels: ["RFC"] body: - type: markdown attributes: value: | 一份提案(RFC)定位为 **「在某功能/重构的具体开发前,用于开发者间 review 技术设计/方案的文档」**, 目的是让协作的开发者间清晰的知道「要做什么」和「具体会怎么做」,以及所有的开发者都能公开透明的参与讨论; 以便评估和讨论产生的影响 (遗漏的考虑、向后兼容性、与现有功能的冲突), 因此提案侧重在对解决问题的 **方案、设计、步骤** 的描述上。 如果仅希望讨论是否添加或改进某功能本身,请使用 -> [Issue: 功能改进](https://github.com/EstrellaXD/Auto_Bangumi/issues/new?labels=feature+request&template=feature_request.yml&title=%5BFeature+Request%5D+) - type: textarea id: background attributes: label: 背景 or 问题 description: 简单描述遇到的什么问题或需要改动什么。可以引用其他 issue、讨论、文档等。 validations: required: true - type: textarea id: goal attributes: label: "目标 & 方案简述" description: 简单描述提案此提案实现后,**预期的目标效果**,以及简单大致描述会采取的方案/步骤,可能会/不会产生什么影响。 validations: required: true - type: textarea id: design attributes: label: "方案设计 & 实现步骤" description: | 详细描述你设计的具体方案,可以考虑拆分列表或要点,一步步描述具体打算如何实现的步骤和相关细节。 这部份不需要一次性写完整,即使在创建完此提案 issue 后,依旧可以再次编辑修改。 validations: required: false - type: textarea id: alternative attributes: label: "替代方案 & 对比" description: | [可选] 为来实现目标效果,还考虑过什么其他方案,有什么对比? validations: required: false ================================================ FILE: .github/PULL_REQUEST_TEMPLATE/pull_request_template.md ================================================ ## New ## Change ## Fix ================================================ FILE: .github/workflows/build.yml ================================================ name: Build Docker on: pull_request: types: - opened - synchronize - closed branches: - main push: jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Set up Python 3.13 uses: actions/setup-python@v5 with: python-version: "3.13" - uses: astral-sh/setup-uv@v4 with: version: "latest" - name: Install dependencies run: cd backend && uv sync --group dev - name: Test run: | mkdir -p backend/config cd backend && uv run pytest src/test -v webui-test: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 - name: Install Node.js uses: actions/setup-node@v4 with: node-version: 20 - uses: pnpm/action-setup@v4 name: Install pnpm with: version: 9 run_install: false - name: Get pnpm store directory shell: bash run: | echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV - uses: actions/cache@v4 name: Setup pnpm cache with: path: ${{ env.STORE_PATH }} key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} restore-keys: | ${{ runner.os }}-pnpm-store- - name: Install dependencies run: cd webui && pnpm install - name: build test run: | cd webui && pnpm test:build version-info: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 - name: If release id: release run: | if [[ '${{ github.event_name }}' == 'pull_request' && '${{ github.event.pull_request.head.ref }}' == *'dev'* ]]; then if [ ${{ github.event.pull_request.merged }} == true ]; then echo "release=1" >> $GITHUB_OUTPUT else echo "release=0" >> $GITHUB_OUTPUT fi elif [[ '${{ github.event_name }}' == 'push' && (${{ github.ref }} == *'alpha'* || ${{ github.ref }} == *'beta'*) ]]; then echo "release=1" >> $GITHUB_OUTPUT else echo "release=0" >> $GITHUB_OUTPUT fi - name: If dev id: dev run: | if [[ '${{ github.event_name }}' == 'push' && (${{ github.ref }} == *'alpha'* || ${{ github.ref }} == *'beta'*) ]]; then echo "dev=1" >> $GITHUB_OUTPUT else echo "dev=0" >> $GITHUB_OUTPUT fi - name: Check version id: version run: | if [ '${{ github.event_name }}' == 'pull_request' ]; then if [ ${{ github.event.pull_request.merged }} == true ]; then # Extract version from PR title (handles "Release X.Y.Z", "vX.Y.Z", or "X.Y.Z") PR_TITLE="${{ github.event.pull_request.title }}" VERSION=$(echo "$PR_TITLE" | grep -oE '[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.]+)?' | head -1) if [ -n "$VERSION" ]; then echo "version=$VERSION" >> $GITHUB_OUTPUT else echo "version=$PR_TITLE" >> $GITHUB_OUTPUT fi fi elif [[ ${{ github.event_name }} == 'push' && (${{ github.ref }} == *'alpha'* || ${{ github.ref }} == *'beta'*) ]]; then echo "version=${{ github.ref_name }}" >> $GITHUB_OUTPUT else echo "version=Test" >> $GITHUB_OUTPUT fi - name: If build test id: build_test run: | if [[ '${{ github.event_name }}' == 'pull_request' && '${{ github.event.pull_request.merged }}' != 'true' && '${{ github.event.pull_request.head.ref }}' == *'dev'* ]]; then echo "build_test=1" >> $GITHUB_OUTPUT else echo "build_test=0" >> $GITHUB_OUTPUT fi - name: Check result run: | echo "release: ${{ steps.release.outputs.release }}" echo "dev: ${{ steps.dev.outputs.dev }}" echo "build_test: ${{ steps.build_test.outputs.build_test }}" echo "version: ${{ steps.version.outputs.version }}" outputs: release: ${{ steps.release.outputs.release }} dev: ${{ steps.dev.outputs.dev }} build_test: ${{ steps.build_test.outputs.build_test }} version: ${{ steps.version.outputs.version }} build-webui: runs-on: ubuntu-latest needs: [test, webui-test, version-info] if: ${{ needs.version-info.outputs.release == 1 || needs.version-info.outputs.dev == 1 || needs.version-info.outputs.build_test == 1 }} steps: - name: Checkout uses: actions/checkout@v4 - name: Install Node.js uses: actions/setup-node@v4 with: node-version: 20 - uses: pnpm/action-setup@v4 name: Install pnpm with: version: 9 run_install: false - name: Get pnpm store directory shell: bash run: | echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV - uses: actions/cache@v4 name: Setup pnpm cache with: path: ${{ env.STORE_PATH }} key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} restore-keys: | ${{ runner.os }}-pnpm-store- - name: Install dependencies run: cd webui && pnpm install - name: Build run: | cd webui && pnpm build - name: Upload artifact uses: actions/upload-artifact@v4 with: name: dist path: webui/dist build-docker: runs-on: ubuntu-latest needs: [build-webui, version-info] if: ${{ needs.version-info.outputs.release == 1 || needs.version-info.outputs.dev == 1 || needs.version-info.outputs.build_test == 1 }} steps: - name: Checkout uses: actions/checkout@v4 - name: Create Version info via tag working-directory: ./backend/src run: | echo ${{ needs.version-info.outputs.version }} echo "VERSION='${{ needs.version-info.outputs.version }}'" >> module/__version__.py - name: Set up QEMU uses: docker/setup-qemu-action@v2 - name: Set up Docker Buildx id: buildx uses: docker/setup-buildx-action@v2 - name: Docker metadata main if: ${{ needs.version-info.outputs.release == 1 && needs.version-info.outputs.dev != 1 }} id: meta uses: docker/metadata-action@v4 with: images: | estrellaxd/auto_bangumi ghcr.io/${{ github.repository }} tags: | type=raw,value=${{ needs.version-info.outputs.version }} type=raw,value=latest - name: Docker metadata dev if: ${{ needs.version-info.outputs.dev == 1 }} id: meta-dev uses: docker/metadata-action@v4 with: images: | estrellaxd/auto_bangumi ghcr.io/${{ github.repository }} tags: | type=raw,value=${{ needs.version-info.outputs.version }} type=raw,value=dev-latest - name: Login to DockerHub if: ${{ needs.version-info.outputs.release == 1 }} uses: docker/login-action@v2 with: username: ${{ secrets.DOCKER_HUB_USERNAME }} password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }} - name: Login to ghcr.io if: ${{ needs.version-info.outputs.release == 1 }} uses: docker/login-action@v2 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.ACCESS_TOKEN }} - name: Download artifact uses: actions/download-artifact@v4 with: name: dist path: backend/src/dist - name: Build and push if: ${{ needs.version-info.outputs.release == 1 && needs.version-info.outputs.dev != 1 }} uses: docker/build-push-action@v4 with: context: . builder: ${{ steps.buildx.output.name }} platforms: linux/amd64,linux/arm64 push: True tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} cache-from: type=gha, scope=${{ github.workflow }} cache-to: type=gha, scope=${{ github.workflow }} - name: Build and push dev if: ${{ needs.version-info.outputs.dev == 1 }} uses: docker/build-push-action@v4 with: context: . builder: ${{ steps.buildx.output.name }} platforms: linux/amd64,linux/arm64 push: ${{ github.event_name == 'push' }} tags: ${{ steps.meta-dev.outputs.tags }} labels: ${{ steps.meta-dev.outputs.labels }} cache-from: type=gha, scope=${{ github.workflow }} cache-to: type=gha, scope=${{ github.workflow }} - name: Build test if: ${{ needs.version-info.outputs.release == 0 }} uses: docker/build-push-action@v4 with: context: . builder: ${{ steps.buildx.output.name }} platforms: linux/amd64,linux/arm64 push: false tags: estrellaxd/auto_bangumi:test cache-from: type=gha, scope=${{ github.workflow }} cache-to: type=gha, scope=${{ github.workflow }} release: runs-on: ubuntu-latest needs: [build-docker, version-info] if: ${{ needs.version-info.outputs.release == 1 }} outputs: url: ${{ steps.release.outputs.url }} version: ${{ needs.version-info.outputs.version }} steps: - name: Checkout code uses: actions/checkout@v4 - name: Download artifact webui uses: actions/download-artifact@v4 with: name: dist path: webui/dist - name: Zip webui run: | cd webui && ls -al && tree && zip -r dist.zip dist - name: Download artifact app uses: actions/download-artifact@v4 with: name: dist path: backend/src/dist - name: Create Version info via tag working-directory: ./backend/src run: | echo ${{ needs.version-info.outputs.version }} echo "VERSION='${{ needs.version-info.outputs.version }}'" >> module/__version__.py - name: Zip app run: | cd backend && zip -r app-v${{ needs.version-info.outputs.version }}.zip src - name: Generate Release info id: release-info run: | if ${{ needs.version-info.outputs.dev == 1 }}; then echo "version=🌙${{ needs.version-info.outputs.version }}" >> $GITHUB_OUTPUT echo "pre_release=true" >> $GITHUB_OUTPUT else echo "version=🌟${{ needs.version-info.outputs.version }}" >> $GITHUB_OUTPUT echo "pre_release=false" >> $GITHUB_OUTPUT fi - name: Read changelog id: changelog run: | if [ -f docs/changelog/3.2.md ]; then echo "body<> $GITHUB_OUTPUT cat docs/changelog/3.2.md >> $GITHUB_OUTPUT echo "EOF" >> $GITHUB_OUTPUT fi - name: Release id: release uses: softprops/action-gh-release@v1 with: tag_name: ${{ needs.version-info.outputs.version }} name: ${{ steps.release-info.outputs.version }} body: ${{ github.event.pull_request.body || steps.changelog.outputs.body }} draft: false prerelease: ${{ steps.release-info.outputs.pre_release == 'true' }} files: | webui/dist.zip backend/app-v${{ needs.version-info.outputs.version }}.zip env: GITHUB_TOKEN: ${{ secrets.ACCESS_TOKEN }} telegram: runs-on: ubuntu-latest needs: [release] steps: - name: send telegram message on push uses: appleboy/telegram-action@master with: to: ${{ secrets.TELEGRAM_TO }} token: ${{ secrets.TELEGRAM_TOKEN }} message: | New release: ${{ needs.release.outputs.version }} Link: ${{ needs.release.outputs.url }} ================================================ FILE: .gitignore ================================================ # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] *$py.class # C extensions *.so # Distribution / packaging .Python build/ develop-eggs/ dist/ downloads/ eggs/ .eggs/ lib/ lib64/ parts/ sdist/ var/ wheels/ share/python-wheels/ *.egg-info/ .installed.cfg *.egg MANIFEST # PyInstaller # Usually these files are written by a python script from a template # before PyInstaller builds the exe, so as to inject date/other infos into it. *.manifest *.spec # Installer logs pip-log.txt pip-delete-this-directory.txt # Unit test / coverage reports htmlcov/ .tox/ .nox/ .coverage .coverage.* .cache nosetests.xml coverage.xml *.cover *.py,cover .hypothesis/ .pytest_cache/ cover/ # Translations *.mo *.pot # Django stuff: *.log local_settings.py db.sqlite3 db.sqlite3-journal # Flask stuff: instance/ .webassets-cache # Scrapy stuff: .scrapy # Sphinx documentation docs/_build/ # PyBuilder .pybuilder/ target/ # Jupyter Notebook .ipynb_checkpoints # IPython profile_default/ ipython_config.py # pyenv # For a library or package, you might want to ignore these files since the code is # intended to run in multiple environments; otherwise, check them in: # .python-version # pipenv # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. # However, in case of collaboration, if having platform-specific dependencies or dependencies # having no cross-platform support, pipenv may install dependencies that don't work, or not # install all needed dependencies. #Pipfile.lock # poetry # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. # This is especially recommended for binary packages to ensure reproducibility, and is more # commonly ignored for libraries. # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control #poetry.lock # pdm # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. #pdm.lock # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it # in version control. # https://pdm.fming.dev/#use-with-ide .pdm.toml # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm __pypackages__/ # Celery stuff celerybeat-schedule celerybeat.pid # SageMath parsed files *.sage.py # Environments .idea .env .venv env/ venv/ ENV/ env.bak/ venv.bak/ # Spyder project settings .spyderproject .spyproject # Rope project settings .ropeproject # mkdocs documentation /site # mypy .mypy_cache/ .dmypy.json dmypy.json # Pyre type checker .pyre/ # pytype static type analyzer .pytype/ # Cython debug symbols cython_debug/ # PyCharm # JetBrains specific template is maintained in a separate JetBrains.gitignore that can # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore # and can be added to the global gitignore or merged into this file. For a more nuclear # option (not recommended) you can uncomment the following to ignore the entire idea folder. #.idea/ # Custom # # backend /backend/src/test.py /backend/src/module/run_debug.sh /backend/src/module/debug_run.sh /backend/src/module/__version__.py /backend/src/data/ /src/module/conf/config_dev.ini .run /backend/src/templates/ /backend/src/config/ /src/debuger.py /backend/src/dist.zip /pyrightconfig.json # webui logs npm-debug.log* yarn-debug.log* yarn-error.log* pnpm-debug.log* lerna-debug.log* node_modules dist webui/.vite/deps/* dist.zip dist-ssr *.local dev-dist # Editor directories and files .vscode/* !.vscode/settings.json !.vscode/tasks.json !.vscode/launch.json !.vscode/extensions.json .DS_Store *.suo *.ntvs* *.njsproj *.sln *.sw? # vitepress /docs/.vitepress/cache/ # test file test.* # local config /backend/config/ .claude/settings.local.json ================================================ FILE: .vscode/extensions.json ================================================ { "recommendations": [ // https://marketplace.visualstudio.com/items?itemName=antfu.unocss "antfu.unocss", // https://marketplace.visualstudio.com/items?itemName=formulahendry.auto-rename-tag "formulahendry.auto-rename-tag", // https://marketplace.visualstudio.com/items?itemName=streetsidesoftware.code-spell-checker "streetsidesoftware.code-spell-checker", // https://marketplace.visualstudio.com/items?itemName=naumovs.color-highlight "naumovs.color-highlight", // https://marketplace.visualstudio.com/items?itemName=ms-python.vscode-pylance "ms-python.vscode-pylance", // https://marketplace.visualstudio.com/items?itemName=ms-python.python "ms-python.python", // https://marketplace.visualstudio.com/items?itemName=vue.volar "vue.volar" ] } ================================================ FILE: .vscode/launch.json ================================================ { // Use IntelliSense to learn about possible attributes. // Hover to view descriptions of existing attributes. // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ { "name": "Dev Backend", "type": "python", "request": "launch", "cwd": "${workspaceFolder}/backend/src", "program": "main.py", "env": { "HOST": "127.0.0.1", }, "console": "integratedTerminal", "justMyCode": true } ] } ================================================ FILE: .vscode/settings.json ================================================ { "files.associations": { "settings.json": "json5", "launch.json": "json5", "extensions.json": "json5", "tsconfig.json": "json5", "tsconfig.*.json": "json5", }, "[markdown]": { "editor.wordWrap": "off", }, "python.venvPath": "./backend/venv", "cSpell.words": [ "Bangumi", "fastapi", "mikan", "starlette" ], } ================================================ FILE: CHANGELOG.md ================================================ # [Unreleased] ## Backend ### Added - 新增 `Security` 配置模型,支持登录 IP 白名单、MCP IP 白名单和 Bearer Token 认证 - 新增登录端点 IP 白名单检查中间件 (`check_login_ip`) - MCP 安全中间件升级为可配置模式:支持 CIDR 白名单 + Bearer Token 双重认证 - 认证端点支持 `Authorization: Bearer` 令牌绕过 Cookie 登录 - 配置 API `_sanitize_dict` 修复:仅对字符串值进行脱敏,避免误处理非字符串字段 - 新增番剧放送日手动设置 API (`PATCH /api/v1/bangumi/{id}/weekday`),支持锁定放送日防止日历刷新覆盖 - 数据库迁移 v9:`bangumi` 表新增 `weekday_locked` 列 ### Fixed - 修复 qBittorrent 下载器 SSL 连接问题:解耦 HTTPS 协议选择与证书验证,自签名证书不再导致连接失败 (#923) - 修复 `torrents_rename_file` 重命名验证循环中 `continue` 应为 `break` 的逻辑错误 ### Changed - 重构认证模块:提取 `_issue_token` 公共方法,消除 3 处重复的 JWT 签发逻辑 - `get_current_user` 简化为三级认证(DEV 绕过 → Bearer Token → Cookie JWT) - `LocalNetworkMiddleware` 重命名为 `McpAccessMiddleware`,从硬编码 RFC 1918 改为读取配置 ### Tests - 新增 101 个单元测试覆盖安全、认证、配置、下载器和 MockDownloader 模块 ## Frontend ### Added - 新增日历拖拽排列功能:可将「未知」番剧拖入星期列,自动设置放送日并锁定 - 拖入后显示紫色图钉图标,鼠标悬停显示取消按钮 - 锁定的番剧在日历刷新时不会被覆盖 - 使用 vuedraggable 实现流畅拖拽动画 - 新增安全设置组件 (`config-security.vue`),支持在 WebUI 中配置 IP 白名单和 Token - 前端 `Security` 类型定义和初始化配置 --- # [3.2.3] - 2026-02-23 ## Backend ### Added - 新增 MCP (Model Context Protocol) 服务器,支持通过 Claude Desktop 等 LLM 工具管理番剧订阅 - SSE 传输层挂载在 `/mcp/sse`,支持 MCP 客户端连接 - 10 个工具:list_anime、get_anime、search_anime、subscribe_anime、unsubscribe_anime、list_downloads、list_rss_feeds、get_program_status、refresh_feeds、update_anime - 4 个资源:anime/list、anime/{id}、status、rss/feeds - 本地网络 IP 白名单安全中间件(RFC 1918 + 回环地址),无需 JWT 认证 - 新增通知系统重构,支持多通知渠道同时启用 - 支持 Telegram、Bark、Server 酱、企业微信、Discord、Gotify、Pushover、Webhook 八种渠道 - 新增通知管理 API:`GET/PUT /api/notification/providers` - 新增 E2E 集成测试套件,覆盖 RSS→下载→重命名全流程 ### Fixes - 修复第 0 集(SP/OVA)被错误重命名为第 1 集的问题 (#977) - Episode 0 现在免受集数偏移影响,不再覆盖正常集数文件 - 修复 RSS 过滤器包含特殊字符(如 `[字幕组`)时导致程序崩溃的问题 (#974) - 无效正则表达式自动降级为字面量匹配 - 修复聚合 RSS 解析时 `title_raw` 为空导致 `TypeError` 崩溃的问题 (#976) - 修复解析器处理无括号种子名称时 `IndexError` 崩溃的问题 (#973) - 修复删除番剧时未清理关联种子记录的问题 - 修复认证路由、JWT 刷新和 WebAuthn 注册流程的多个安全问题 - 修复程序生命周期管理和后台任务取消逻辑 - 修复数据库迁移在部分场景下未正确执行的问题 ### Performance - 优化日志系统:`RotatingFileHandler` 轮转(5 MB × 3)、`QueueHandler` 异步写入、`GET /api/log` 限读 512 KB - 优化重命名器:批量数据库查询,并发获取种子文件列表 - 所有 `logger.debug(f"...")` 转为惰性 `%s` 格式化(~80 处) ### Tests - 新增 26 个回归测试覆盖 #974、#976、#977、#986 - 扩展 raw_parser、torrent_parser、path_parser 测试覆盖率 ## Frontend ### Fixes - 修复认证路由守卫和 i18n 初始化顺序问题 - 修复通知设置组件与项目设计系统的对齐问题 - 修复组件生命周期管理问题 ## Docs - README 移除未实现的 Aria2 和 Transmission 下载器 (#987) --- # [3.2.0-beta.13] - 2026-01-26 ## Frontend ### Features - 重新设计搜索面板 - 新增筛选区域,支持按字幕组、分辨率、字幕类型、季度分类筛选 - 多选筛选器,智能禁用不兼容的选项(灰色显示) - 结果项标签改为非点击式彩色药丸样式 - 统一标签样式(药丸形状、12px 字体) - 标签值标准化(分辨率:FHD/HD/4K,字幕:简/繁/双语) - 筛选分类和结果变体支持展开/收起 - 海报高度自动匹配 4 行变体项(168px) - 点击弹窗外部自动关闭 --- # [3.2.0-beta.12] - 2026-01-26 ## Backend ### Features - 偏移检查面板新增建议值显示(解析的季度/集数和建议的偏移量) ### Fixes - 修复季度偏移未应用到下载文件夹路径的问题 - 设置季度偏移后,qBittorrent 保存路径会自动更新(如 `Season 2` → `Season 1`) - RSS 规则的保存路径也会同步更新 - 优化集数偏移建议逻辑 - 简单季度不匹配时不再建议集数偏移(仅虚拟季度需要) - 改进提示信息,明确说明是否需要调整集数 --- # [3.2.0-beta.11] - 2026-01-25 ## Backend ### Features - 新增季度/集数偏移自动检测功能 - 通过分析 TMDB 剧集播出日期检测「虚拟季度」(如芙莉莲第一季分两部分播出) - 当播出间隔超过6个月时自动识别为不同部分 - 自动计算集数偏移量(如 RSS 显示 S2E1 → TMDB S1E29) - 新增后台扫描线程,自动检测已有订阅的偏移问题 - 新增搜索源配置 API 端点: - `GET /search/provider/config` - 获取搜索源配置 - `PUT /search/provider/config` - 更新搜索源配置 - 新增 API 端点: - `POST /bangumi/detect-offset` - 检测季度/集数偏移 - `PATCH /bangumi/dismiss-review/{id}` - 忽略偏移检查提醒 - 数据库新增 `needs_review` 和 `needs_review_reason` 字段 ## Frontend ### Features - 新增搜索源设置面板 - 支持查看、添加、编辑、删除搜索源 - 默认搜索源(mikan、nyaa、dmhy)不可删除 - URL 模板验证,确保包含 `%s` 占位符 - 新增 iOS 风格通知角标系统 - 黄色角标 + 紫色边框显示需要检查的订阅 - 支持组合显示(如 `! | 2` 表示有警告且有多个规则) - 卡片黄色发光动画提示需要注意 - 编辑弹窗新增警告横幅,支持一键自动检测和忽略 - 规则选择弹窗高亮显示有警告的规则 - 首页空状态新增「添加 RSS 订阅」按钮,引导新用户快速上手 - 日历页面海报图片添加懒加载,提升性能 - 日历页面「未知播出日」独立为单独区块,优化视觉节奏 ### Fixes - 修复移动端设置页面水平溢出问题 - 输入框添加 `max-width: 100%` 防止超出容器 - 折叠面板添加宽度约束和溢出隐藏 - 设置栅格添加 `min-width: 0` 允许收缩 - 修复移动端顶栏布局 - 搜索按钮改为弹性布局,填充 Logo 和图标之间的空间 - 减小图标按钮尺寸和间距,优化紧凑型布局 - 添加「点击搜索」文字提示 - 修复移动端搜索弹窗关闭按钮被截断问题 - 减小弹窗头部内边距和元素尺寸 - 搜索源选择按钮缩小至适配移动端 - 修复设置页面保存/取消按钮缺少加载状态 - 修复侧边栏展开动画抖动(rotateY → rotate) - 移动端底部导航标签字号从 10px 增至 11px,提升可读性 - 登录页背景动画添加 `will-change: transform` 优化 GPU 性能 --- # [3.2.0-beta.8] - 2026-01-25 ## Backend ### Features - Passkey 登录支持无用户名模式(可发现凭证) ### Fixes - 修复搜索和订阅流程中的多个问题 - 改进种子获取可靠性和错误处理 ## Frontend ### Features - Passkey 登录支持无用户名模式(可发现凭证) --- # [3.2.0-beta.7] - 2026-01-25 ## Backend ### Features - 数据库迁移自动填充 NULL 值为模型默认值 ### Fixes - 修复下载器连接检查添加最大重试次数 - 修复添加种子时的网络瞬态错误,添加重试逻辑 ## Frontend ### Features - 重新设计搜索面板,新增模态框和过滤系统 - 重新设计登录面板,采用现代毛玻璃风格 - 日志页面新增日志级别过滤功能 ### Fixes - 修复日历页面未知列宽度问题 - 统一下载器页面操作栏按钮尺寸 --- # [3.2.0-beta.6] - 2026-01-25 ## Backend ### Features - 新增番剧归档功能:支持手动归档/取消归档,已完结番剧自动归档 ### Fixes - 修复 `add_all()` 方法缺少去重检查导致重复添加番剧规则的问题 - 去重逻辑基于 `(title_raw, group_name)` 组合,同时支持批量内部去重 - 新增剧集偏移自动检测:根据 TMDB 季度集数自动计算偏移量(如 S02E18 → S02E05) - TMDB 解析器新增 `series_status` 和 `season_episode_counts` 字段提取 - 新增数据库迁移 v4:为 `bangumi` 表添加 `archived` 字段 - 新增 API 端点: - `PATCH /bangumi/archive/{id}` - 归档番剧 - `PATCH /bangumi/unarchive/{id}` - 取消归档 - `GET /bangumi/refresh/metadata` - 刷新元数据并自动归档已完结番剧 - `GET /bangumi/suggest-offset/{id}` - 获取建议的剧集偏移量 - 重命名模块支持从数据库查询偏移量并应用到文件名 ## Frontend ### Features - 番剧列表页新增可折叠的「已归档」分区 - 日历页新增番剧分组功能:相同番剧的多个规则合并显示,点击可选择具体规则 - 番剧列表页新增骨架屏加载动画 ### Fixes - 修复弹窗 z-index 层级问题,新增 CSS 变量管理层级系统 - 改善无障碍体验:按钮最小触摸区域 44px、焦点状态可见、添加 aria-label - 规则编辑弹窗新增归档/取消归档按钮 - 规则编辑器新增剧集偏移字段和「自动检测」按钮 - 新增 i18n 翻译(中文/英文) - 优化规则编辑弹窗布局:统一表单字段对齐、统一按钮高度、修复移动端底部弹窗 z-index 层级问题 - 修复下载器页面仅显示季度文件夹名的问题,现在会显示「番剧名 / Season 1」格式 --- # [3.2.0-beta.5] - 2026-01-24 ## Backend ### Features - RSS 订阅源新增连接状态追踪:每次刷新后记录 `connection_status`(healthy/error)、`last_checked_at` 和 `last_error` - 新增数据库迁移 v2:为 `rssitem` 表添加连接状态相关字段 ### Performance - 新增共享 HTTP 客户端连接池,复用 TCP/SSL 连接,减少每次请求的握手开销 - RSS 刷新改为并发拉取所有订阅源(`asyncio.gather`),多源场景下速度提升约 10 倍 - 种子文件下载改为并发获取,下载多个种子时速度提升约 5 倍 - 重命名模块并发获取所有种子文件列表,速度提升约 20 倍 - 通知发送改为并发执行,移除 2 秒硬编码延迟 - 新增 TMDB 和 Mikan 解析结果的内存缓存,避免重复 API 调用 - 为 `Torrent.url`、`Torrent.rss_id`、`Bangumi.title_raw`、`Bangumi.deleted`、`RSSItem.url` 添加数据库索引 - RSS 批量启用/禁用改为单次事务操作,替代逐条提交 - 预编译正则表达式(种子名解析规则、过滤器匹配),避免运行时重复编译 - `SeasonCollector` 在循环外创建,复用单次认证 - `check_first_run` 缓存默认配置字典,避免每次创建新对象 - 通知模块中的同步数据库调用改为 `asyncio.to_thread`,避免阻塞事件循环 - RSS 解析去重从 O(n²) 列表查找改为 O(1) 集合查找 - 文件后缀判断使用 `frozenset` 替代列表,提升查找效率 - `Episode`/`SeasonInfo` 数据类添加 `__slots__`,减少内存占用 - RSS XML 解析返回元组列表,替代三个独立列表再 zip 的模式 - qBittorrent 规则设置改为并发执行 ## Frontend ### Features - RSS 管理页面新增连接状态标签:健康时显示绿色「已连接」,错误时显示红色「错误」并通过 tooltip 显示错误详情 ### Performance - 下载器 store 使用 `shallowRef` 替代 `ref`,避免大数组的深层响应式代理 - 表格列定义改为 `computed`,避免每次渲染重建 - RSS 表格列与数据分离,数据变化时不重建列配置 - 日历页移除重复的 `getAll()` 调用 - `ab-select` 的 `watchEffect` 改为 `watch`,消除挂载时的无效 emit - `useClipboard` 提升到 store 顶层,避免每次 `copy()` 创建新实例 - `setInterval` 替换为 `useIntervalFn`,自动生命周期管理,防止内存泄漏 - 共享 `ruleTemplate` 对象改为浅拷贝,避免意外的引用共变 - `ab-add-rss` 移除不必要的 `setTimeout` 延迟 ### Fixes - 修复 `ab-image.vue` 中 ` ================================================ FILE: docs/.vitepress/theme/index.ts ================================================ import { h, onMounted, watch, nextTick } from 'vue' import Theme from 'vitepress/theme' import { useRoute } from 'vitepress' import mediumZoom from 'medium-zoom' import HomePreviewWebUI from './components/HomePreviewWebUI.vue' import './style.css' export default { extends: Theme, Layout: () => { return h(Theme.Layout, null, { 'home-features-after': () => h(HomePreviewWebUI), }) }, setup() { const route = useRoute() const initZoom = () => { mediumZoom('[data-zoomable]', { background: 'var(--vp-c-bg)' }) } onMounted(() => { initZoom() }) watch( () => route.path, () => nextTick(initZoom), ) }, } ================================================ FILE: docs/.vitepress/theme/style.css ================================================ /** * Customize default theme styling by overriding CSS variables: * https://github.com/vuejs/vitepress/blob/main/src/client/theme-default/styles/vars.css */ /** * Colors * -------------------------------------------------------------------------- */ :root { --vp-c-brand-1: #7B65D6; --vp-c-brand-2: #7162AE; --vp-c-brand-3: #8D7FC2; --vp-c-brand-soft: rgba(100, 108, 255, 0.08); } /** * Component: Button * -------------------------------------------------------------------------- */ :root { --vp-button-brand-border: transparent; --vp-button-brand-text: var(--vp-c-white); --vp-button-brand-bg: var(--vp-c-brand-1); --vp-button-brand-hover-border: transparent; --vp-button-brand-hover-text: var(--vp-c-white); --vp-button-brand-hover-bg: var(--vp-c-brand-2); --vp-button-brand-active-border: transparent; --vp-button-brand-active-text: var(--vp-c-white); --vp-button-brand-active-bg: var(--vp-c-brand-1); } /** * Component: Home * -------------------------------------------------------------------------- */ :root { --vp-home-hero-name-color: transparent; --vp-home-hero-name-background: -webkit-linear-gradient( 120deg, #b42ff1 30%, #441bd9 ); --vp-home-hero-image-background-image: linear-gradient( -45deg, #b42ff1bb 50%, #4794ffbb 50% ); --vp-home-hero-image-filter: blur(44px); } @media (min-width: 640px) { :root { --vp-home-hero-image-filter: blur(56px); } } @media (min-width: 960px) { :root { --vp-home-hero-image-filter: blur(72px); } } /** * Component: Custom Block * -------------------------------------------------------------------------- */ :root { --vp-custom-block-tip-border: transparent; --vp-custom-block-tip-text: var(--vp-c-text-1); --vp-custom-block-tip-bg: var(--vp-c-brand-soft); --vp-custom-block-tip-code-bg: var(--vp-c-brand-soft); } /** * Component: medium-zoom * -------------------------------------------------------------------------- */ .medium-zoom--opened .medium-zoom-overlay { z-index: 20; } .medium-zoom--opened .medium-zoom-image { z-index: 21; } .vp-doc .ab-shadow-card { box-shadow: 0 10px 30px -10px rgba(0,0,0,0.2), 0 0 2px rgba(0,0,0,0.2), 0 20px 30px -20px rgba(0,0,0,0.4); border-radius: 10px; } ================================================ FILE: docs/api/index.md ================================================ # REST API 参考 AutoBangumi 在 `/api/v1` 路径下提供 REST API。除登录和设置向导外,所有端点都需要 JWT 认证。 **基础 URL:** `http://your-host:7892/api/v1` **认证:** 将 JWT 令牌作为 cookie 或 `Authorization: Bearer ` 请求头传入。 **交互式文档:** 在开发模式下运行时,可在 `http://your-host:7892/docs` 访问 Swagger UI。 --- ## 认证 ### 登录 ``` POST /auth/login ``` 使用用户名和密码进行认证。 **请求体:** ```json { "username": "string", "password": "string" } ``` **响应:** 设置包含 JWT 令牌的认证 cookie。 ### 刷新令牌 ``` GET /auth/refresh_token ``` 刷新当前的认证令牌。 ### 登出 ``` GET /auth/logout ``` 清除认证 cookie 并登出。 ### 更新凭据 ``` POST /auth/update ``` 更新用户名和/或密码。 **请求体:** ```json { "username": "string", "password": "string" } ``` --- ## Passkey / WebAuthn 使用 WebAuthn/FIDO2 Passkey 进行无密码认证。 ### 注册 Passkey ``` POST /passkey/register/options ``` 获取 WebAuthn 注册选项(质询、依赖方信息)。 ``` POST /passkey/register/verify ``` 验证并保存来自浏览器的 Passkey 注册响应。 ### 使用 Passkey 认证 ``` POST /passkey/auth/options ``` 获取 WebAuthn 认证质询选项。 ``` POST /passkey/auth/verify ``` 验证 Passkey 认证响应并签发 JWT 令牌。 ### 管理 Passkey ``` GET /passkey/list ``` 列出当前用户所有已注册的 Passkey。 ``` POST /passkey/delete ``` 通过凭据 ID 删除已注册的 Passkey。 --- ## 配置 ### 获取配置 ``` GET /config/get ``` 获取当前应用程序配置。 **响应:** 完整配置对象,包括 `program`、`downloader`、`rss_parser`、`bangumi_manager`、`notification`、`proxy` 和 `experimental_openai` 部分。 ### 更新配置 ``` PATCH /config/update ``` 部分更新应用程序配置。只需包含您想要更改的字段。 **请求体:** 部分配置对象。 --- ## 番剧(动画规则) ### 列出所有番剧 ``` GET /bangumi/get/all ``` 获取所有动画下载规则。 ### 通过 ID 获取番剧 ``` GET /bangumi/get/{bangumi_id} ``` 通过 ID 获取特定动画规则。 ### 更新番剧 ``` PATCH /bangumi/update/{bangumi_id} ``` 更新动画规则的元数据(标题、季度、集数偏移等)。 ### 删除番剧 ``` DELETE /bangumi/delete/{bangumi_id} ``` 删除单个动画规则及其关联的种子。 ``` DELETE /bangumi/delete/many/ ``` 批量删除多个动画规则。 **请求体:** ```json { "bangumi_ids": [1, 2, 3] } ``` ### 禁用/启用番剧 ``` DELETE /bangumi/disable/{bangumi_id} ``` 禁用动画规则(保留文件,停止下载)。 ``` DELETE /bangumi/disable/many/ ``` 批量禁用多个动画规则。 ``` GET /bangumi/enable/{bangumi_id} ``` 重新启用之前禁用的动画规则。 ### 刷新海报 ``` GET /bangumi/refresh/poster/all ``` 从 TMDB 刷新所有动画的海报图片。 ``` GET /bangumi/refresh/poster/{bangumi_id} ``` 刷新特定动画的海报图片。 ### 日历 ``` GET /bangumi/refresh/calendar ``` 从 Bangumi.tv 刷新动画放送日历数据。 ### 重置全部 ``` GET /bangumi/reset/all ``` 删除所有动画规则。请谨慎使用。 --- ## RSS 订阅源 ### 列出所有订阅源 ``` GET /rss ``` 获取所有已配置的 RSS 订阅源。 ### 添加订阅源 ``` POST /rss/add ``` 添加新的 RSS 订阅源。 **请求体:** ```json { "url": "string", "aggregate": true, "parser": "mikan" } ``` ### 启用/禁用订阅源 ``` POST /rss/enable/many ``` 启用多个 RSS 订阅源。 ``` PATCH /rss/disable/{rss_id} ``` 禁用单个 RSS 订阅源。 ``` POST /rss/disable/many ``` 批量禁用多个 RSS 订阅源。 ### 删除订阅源 ``` DELETE /rss/delete/{rss_id} ``` 删除单个 RSS 订阅源。 ``` POST /rss/delete/many ``` 批量删除多个 RSS 订阅源。 ### 更新订阅源 ``` PATCH /rss/update/{rss_id} ``` 更新 RSS 订阅源的配置。 ### 刷新订阅源 ``` GET /rss/refresh/all ``` 手动触发刷新所有 RSS 订阅源。 ``` GET /rss/refresh/{rss_id} ``` 刷新特定的 RSS 订阅源。 ### 获取订阅源中的种子 ``` GET /rss/torrent/{rss_id} ``` 获取从特定 RSS 订阅源解析的种子列表。 ### 分析与订阅 ``` POST /rss/analysis ``` 分析 RSS URL 并提取动画元数据,但不订阅。 **请求体:** ```json { "url": "string" } ``` ``` POST /rss/collect ``` 从 RSS 订阅源下载所有剧集(用于已完结动画)。 ``` POST /rss/subscribe ``` 订阅 RSS 订阅源以自动下载连载中的动画。 --- ## 搜索 ### 搜索番剧(Server-Sent Events) ``` GET /search/bangumi?keyword={keyword}&provider={provider} ``` 搜索动画种子。以 Server-Sent Events (SSE) 流的形式返回结果,提供实时更新。 **查询参数:** - `keyword` — 搜索关键词 - `provider` — 搜索提供者(例如 `mikan`、`nyaa`、`dmhy`) **响应:** 包含解析后搜索结果的 SSE 流。 ### 列出搜索提供者 ``` GET /search/provider ``` 获取可用搜索提供者的列表。 --- ## 程序控制 ### 获取状态 ``` GET /status ``` 获取程序状态,包括版本、运行状态和 first_run 标志。 **响应:** ```json { "status": "running", "version": "3.2.0", "first_run": false } ``` ### 启动程序 ``` GET /start ``` 启动主程序(RSS 检查、下载、重命名)。 ### 重启程序 ``` GET /restart ``` 重启主程序。 ### 停止程序 ``` GET /stop ``` 停止主程序(WebUI 仍可访问)。 ### 关闭 ``` GET /shutdown ``` 关闭整个应用程序(重启 Docker 容器)。 ### 检查下载器 ``` GET /check/downloader ``` 测试与已配置的下载器(qBittorrent)的连接。 --- ## 下载器管理 直接从 AutoBangumi 管理下载器中的种子。 ### 列出种子 ``` GET /downloader/torrents ``` 获取 Bangumi 分类中的所有种子。 ### 暂停种子 ``` POST /downloader/torrents/pause ``` 通过哈希暂停种子。 **请求体:** ```json { "hashes": ["hash1", "hash2"] } ``` ### 恢复种子 ``` POST /downloader/torrents/resume ``` 通过哈希恢复已暂停的种子。 **请求体:** ```json { "hashes": ["hash1", "hash2"] } ``` ### 删除种子 ``` POST /downloader/torrents/delete ``` 删除种子,可选择是否删除文件。 **请求体:** ```json { "hashes": ["hash1", "hash2"], "delete_files": false } ``` --- ## 设置向导 这些端点仅在首次运行设置期间可用(设置完成前)。它们**不**需要认证。设置完成后,所有端点返回 `403 Forbidden`。 ### 检查设置状态 ``` GET /setup/status ``` 检查是否需要设置向导(首次运行)。 **响应:** ```json { "need_setup": true } ``` ### 测试下载器连接 ``` POST /setup/test-downloader ``` 使用提供的凭据测试与下载器的连接。 **请求体:** ```json { "type": "qbittorrent", "host": "172.17.0.1:8080", "username": "admin", "password": "adminadmin", "ssl": false } ``` ### 测试 RSS 订阅源 ``` POST /setup/test-rss ``` 验证 RSS 订阅源 URL 是否可访问和可解析。 **请求体:** ```json { "url": "https://mikanime.tv/RSS/MyBangumi?token=xxx" } ``` ### 测试通知 ``` POST /setup/test-notification ``` 使用提供的设置发送测试通知。 **请求体:** ```json { "type": "telegram", "token": "bot_token", "chat_id": "chat_id" } ``` ### 完成设置 ``` POST /setup/complete ``` 保存所有配置并将设置标记为完成。创建标记文件 `config/.setup_complete`。 **请求体:** 完整配置对象。 --- ## 日志 ### 获取日志 ``` GET /log ``` 获取完整的应用程序日志文件。 ### 清除日志 ``` GET /log/clear ``` 清除日志文件。 --- ## 响应格式 所有 API 响应遵循统一格式: ```json { "msg_en": "Success message in English", "msg_zh": "成功消息(中文)", "status": true } ``` 错误响应包含适当的 HTTP 状态码(400、401、403、404、500)以及中英文错误消息。 ================================================ FILE: docs/changelog/2.6.md ================================================ # [2.6] 发布说明 ## 从旧版本升级说明 从 2.6 版本开始,AutoBangumi (AB) 配置已从环境变量迁移到 `config.json`。升级前请注意以下事项。 ### 环境变量迁移 升级到 2.6 后首次启动时,旧环境变量会自动转换为 `config.json`。生成的 `config.json` 放置在 `/app/config` 文件夹中。 一旦您映射了 `/app/config` 文件夹,旧环境变量将不再影响 AB 的运行。您可以删除 `config.json` 以从环境变量重新生成。 ### 容器卷映射 2.6 版本之后,需要映射以下文件夹: - `/app/config`:配置文件夹,包含 `config.json` - `/app/data`:数据文件夹,包含 `bangumi.json` 等 ### 数据文件 由于重大更新,我们不建议使用旧数据文件。AB 会在 `/app/data` 中自动生成新的 `bangumi.json`。 不用担心 — QB 不会重新下载之前已下载的动画。 ### 后续配置更改 AB 现在可以直接在 WebUI 中编辑配置。编辑后重启容器即可生效。 ## 如何升级 ### Docker Compose 您可以使用现有的 docker-compose.yml 文件进行升级: ```bash docker compose stop autobangumi docker compose pull autobangumi ``` 然后修改 docker-compose.yml 添加卷映射: ```yaml version: "3.8" services: autobangumi: image: estrellaxd/auto_bangumi:latest container_name: autobangumi restart: unless-stopped environment: - PUID=1000 - PGID=1000 - TZ=Asia/Shanghai volumes: - /path/to/config:/app/config - /path/to/data:/app/data networks: - bridge dns: - 8.8.8.8 ``` 然后启动 AB: ```bash docker compose up -d autobangumi ``` ### Portainer 在 Portainer 中,修改卷映射并点击 `Recreate` 完成升级。 ### 升级导致问题怎么办 由于配置可能各不相同,升级可能会导致程序失败。删除所有之前的数据和生成的配置文件,然后重启容器并在 WebUI 中重新配置。 ## 新功能 ### 配置方式变更 v2.6 之后,程序配置已从 Docker 环境变量迁移到 `config.json`。 新版 WebUI 还提供了基于 Web 的配置编辑器。访问 AB URL 并在侧边栏找到 `设置` 来修改配置。编辑后重启容器。 ### 自定义反向代理 URL 和 AB 作为代理中继 为了处理 [Mikan Project](https://mikanani.me) 无法访问的情况,AB 提供了三种方法: 1. HTTP 和 SOCKS 代理 此功能在旧版本中就存在。升级到 2.6 后,只需在 WebUI 中检查代理配置即可正常访问 Mikan Project。 但是,qBittorrent 仍然无法直接访问 Mikan 的 RSS 和种子 URL,因此您也需要在 qBittorrent 中添加代理。详见 #198。 2. 自定义反向代理 URL 2.6 版本添加了 `custom_url` 选项用于自定义反向代理 URL。 将其设置为您正确配置的反向代理 URL。AB 将使用此自定义 URL 访问 Mikan Project,QB 可以正常下载。 3. AB 作为代理中继 在 AB 中配置代理后,AB 可以作为本地代理中继(目前仅用于 RSS 相关功能)。 将 `custom_url` 设置为 `http://abhost:abport`,其中 `abhost` 是 AB 的 IP,`abport` 是 AB 的端口。 AB 将把自己的地址推送给 qBittorrent,qBittorrent 将使用 AB 作为代理来访问 Mikan Project。 注意:如果您没有使用 Nginx 或类似工具为 AB 设置反向代理,请包含 `http://` 以确保正常运行。 **重要说明** 如果 AB 和 QB 在同一个容器中,不要使用 `127.0.0.1` 或 `localhost`,它们无法通过这种方式通信。 如果在同一网络中,使用容器名称寻址,例如 `http://autobangumi:7892`。 您也可以使用 Docker 网关地址,例如 `http://172.17.0.1:7892`。 如果在不同主机上,使用主机的 IP 地址。 ### 合集和文件夹重命名 AB 现在可以重命名合集和文件夹中的文件,将媒体文件移回根目录。 请注意,AB 仍然依赖保存路径来确定季度和集数信息,因此请按照 AB 的标准放置合集文件。 **2.6.4** 版本之后,AB 可以重命名文件夹中的字幕(功能仍在完善中)。合集和字幕默认使用 `pn` 格式重命名;调整选项尚未提供。 **标准路径** ``` /downloads/Bangumi/Title/Season 1/xxx ``` ### 推送通知 AB 现在可以通过 `Telegram` 和 `ServerChan` 发送重命名完成通知。 在 WebUI 中启用推送通知并填写所需参数。 - Telegram 需要 Bot Token 和 Chat ID。获取方法请参考各种教程。 - ServerChan 需要 Token。获取方法请参考各种教程。 ================================================ FILE: docs/changelog/3.0.md ================================================ # [3.0] 发布说明 ### 新版 WebUI - 登录功能 — AB 现在支持用户名/密码认证。部分操作需要登录。 - 新海报墙 - 番剧管理功能 - 编辑动画季度信息和名称。更改会自动更新**下载规则** / **已下载文件路径**并触发重命名。 - 新链接解析器 — 解析链接后,您可以手动调整下载信息、选择下载季度或添加自动下载规则。 - 删除动画 — 一键删除动画及其种子文件。 - 每个动画的自定义下载规则,独立于全局规则。 - 新配置界面,更易于配置应用程序规则 - 添加首次启动引导的初始化页面 - 下载器连接检查器,检查 qBittorrent 连接性 - RSS URL 验证器,检查 RSS 订阅源是否有效 - 添加程序管理按钮,可在 WebUI 中启动/停止程序和重启容器 ### 解析器 - 新解析器,支持不同源类型以获取官方标题和海报 URL - 支持更换 RSS 订阅源而无需重新生成数据库 ### 通知模块 - 添加 `Bark` 通知模块 - 新通知格式 — 现在可以向 Telegram 推送海报、动画名称和更新的集数编号 ### 数据迁移 - 从旧版本升级时自动进行数据迁移 - 迁移的数据也会自动匹配海报 ## 修复 - 修复 Windows 路径可能导致的重命名 bug ## 变更 - 数据存储从 `json` 迁移到 `sqlite` - 从多进程迁移到多线程 - 重构主程序 - 改进启动/关闭时间 - 重构解析器模块 - 重构重命名模块 - 暂时移除 `normal` 模式 - 添加 `ghcr.io` 镜像仓库 ================================================ FILE: docs/changelog/3.1.md ================================================ # [3.1] - 2023-08 - 合并后端和前端仓库,优化项目目录结构 - 优化版本发布工作流程 - Wiki 迁移到 VitePress:https://autobangumi.org ## 后端 ### 功能 - 添加 `RSS Engine` 模块 — AB 现在可以独立更新和管理 RSS 订阅,并将种子发送到下载器 - 支持多个聚合 RSS 订阅源,通过 RSS Engine 模块管理 - 下载去重 — 重复订阅的种子不会重复下载 - 添加 RSS 订阅手动刷新 API - 添加 RSS 订阅管理 API - 添加 `Search Engine` 模块 — 按关键词搜索种子并将结果解析为收集或订阅任务 - 插件式搜索引擎,支持 `mikan`、`dmhy` 和 `nyaa` - 添加字幕组特定规则,用于单独配置各字幕组 - 添加 IPv6 监听支持(在环境变量中设置 `IPV6=1`) - 添加批量操作 API,用于批量管理规则和 RSS 订阅 ### 变更 - 数据库结构改为使用 `sqlmodel` 进行数据库管理 - 添加版本管理,实现软件数据无缝更新 - 统一 API 格式 - 添加 API 响应语言选项 - 添加数据库 mock 测试 - 代码优化 ### Bug 修复 - 修复各种小问题 - 引入了一些重大问题 ## 前端 ### 功能 - 添加 `i18n` 支持 — 目前支持 `zh-CN` 和 `en-US` - 添加 PWA 支持 - 添加 RSS 管理页面 - 添加搜索顶栏 ### 变更 - 调整各种 UI 细节 ================================================ FILE: docs/changelog/3.2-zh.md ================================================ # [3.2] - 2025-01 ## 后端 ### 新功能 - 新增 WebAuthn Passkey 无密码登录支持 - 支持注册、验证和管理 Passkey 凭证 - 多设备凭证备份检测(iCloud 钥匙串等) - 克隆攻击防护(sign_count 验证) - 认证策略模式统一密码和 Passkey 登录接口 - 支持无用户名登录(可发现凭证/resident keys) - 新增季度/集数偏移自动检测 - 通过分析 TMDB 剧集播出日期检测「虚拟季度」(如芙莉莲第一季分两部分播出) - 当播出间隔超过 6 个月时自动识别为不同部分 - 自动计算集数偏移量(如 RSS 显示 S2E1 → TMDB S1E29) - 后台扫描线程自动检测已有订阅的偏移问题 - 新增 API 端点:`POST /bangumi/detect-offset`、`PATCH /bangumi/dismiss-review/{id}` - 新增番剧归档功能 - 支持手动归档/取消归档 - 已完结番剧自动归档 - 新增 API 端点:`PATCH /bangumi/archive/{id}`、`PATCH /bangumi/unarchive/{id}`、`GET /bangumi/refresh/metadata` - 新增搜索源配置 API - `GET /search/provider/config` - 获取搜索源配置 - `PUT /search/provider/config` - 更新搜索源配置 - 偏移检查面板新增建议值显示(解析的季度/集数和建议的偏移量) - 修复季度偏移未应用到下载文件夹路径的问题 - 设置季度偏移后,qBittorrent 保存路径会自动更新(如 `Season 2` → `Season 1`) - RSS 规则的保存路径也会同步更新 - 优化集数偏移建议逻辑 - 简单季度不匹配时不再建议集数偏移(仅虚拟季度需要) - 改进提示信息,明确说明是否需要调整集数 - 新增 RSS 连接状态追踪 - 每次刷新后记录 `connection_status`(healthy/error)、`last_checked_at` 和 `last_error` - 新增首次运行设置向导 - 7 步引导配置:账户、下载器、RSS 源、媒体路径、通知 - 下载器连接测试、RSS 源验证 - 可选步骤可跳过,稍后在设置中配置 - 哨兵文件机制(`config/.setup_complete`)防止重复触发 - 未认证的设置 API(仅首次运行可用,完成后返回 403) - 新增日历视图,集成 Bangumi.tv 放送时间表 - 新增下载器 API 和管理界面 - 全面异步迁移 - 数据库层异步支持(aiosqlite),Passkey 操作非阻塞 I/O - `UserDatabase` 支持同步/异步双模式,向后兼容 - `Database` 上下文管理器支持 `with`(同步)和 `async with`(异步) - RSS 引擎、下载器、检查器、解析器全面转换为异步 - 网络请求从 `requests` 迁移到 `httpx`(AsyncClient) - 后端迁移到 `uv` 包管理器(pyproject.toml + uv.lock) - 服务器启动使用后台任务避免阻塞(修复 #891、#929) - 数据库迁移自动填充 NULL 值为模型默认值 - 数据库新增 `needs_review` 和 `needs_review_reason` 字段用于偏移检测 ### 性能优化 - 共享 HTTP 客户端连接池,复用 TCP/SSL 连接 - RSS 刷新改为并发拉取(`asyncio.gather`),多源场景下速度提升约 10 倍 - 种子文件下载改为并发获取,下载多个种子时速度提升约 5 倍 - 重命名模块并发获取文件列表,速度提升约 20 倍 - 通知发送改为并发执行,移除 2 秒硬编码延迟 - 新增 TMDB 和 Mikan 解析结果缓存,避免重复 API 调用 - 为 `Torrent.url`、`Torrent.rss_id`、`Bangumi.title_raw`、`Bangumi.deleted`、`RSSItem.url` 添加数据库索引 - RSS 批量启用/禁用改为单次事务操作,替代逐条提交 - 预编译正则表达式(种子名解析规则、过滤器匹配) - `SeasonCollector` 在循环外创建,复用单次认证 - RSS 解析去重从 O(n²) 列表查找改为 O(1) 集合查找 - `Episode`/`SeasonInfo` 数据类添加 `__slots__`,减少内存占用 ### 变更 - 升级 WebAuthn 依赖到 py_webauthn 2.7.0 - `_get_webauthn_from_request` 优先使用浏览器 Origin 头,修复跨端口开发环境验证问题 - `auth_user` 和 `update_user_info` 转换为异步函数 - `TitleParser.tmdb_parser` 转换为异步函数 - `RSSEngine` 方法全面异步化(`pull_rss`、`refresh_rss`、`download_bangumi`、`add_rss`) - `Checker.check_downloader` 转换为异步函数 - `ProgramStatus` 从 threading 迁移到 asyncio(Event、Lock) ### 问题修复 - 修复下载器连接检查添加最大重试次数 - 修复添加种子时的网络瞬态错误,添加重试逻辑 - 修复搜索和订阅流程中的多个问题 - 改进种子获取可靠性和错误处理 - 修复 `aaguid` 类型错误(py_webauthn 2.7.0 中现为 `str`,不再是 `bytes`) - 修复缺失的 `credential_backup_eligible` 字段(替换为 `credential_device_type`) - 修复 `verify_authentication_response` 接收无效 `credential_id` 参数导致 TypeError - 修复程序启动阻塞服务器(修复 #891、#929、#886、#917、#946) - 修复搜索接口导出与组件期望不匹配 - 修复海报端点路径检查错误拦截所有请求(修复 #933、#934) - 修复 OpenAI 解析器安全问题 - 修复数据库测试使用异步会话与同步代码不匹配 - 修复从 3.1.x 升级到 3.2 时配置字段冲突导致设置丢失(修复 #956) - `program.sleep_time` / `program.times` 自动迁移到 `rss_time` / `rename_time` - 移除废弃的 `rss_parser` 字段(`type`、`custom_url`、`token`、`enable_tmdb`) - 修复 `ENV_TO_ATTR` 环境变量映射指向不存在的模型字段 - 修复 `DEFAULT_SETTINGS` 与当前配置模型不一致 - 修复版本升级迁移逻辑错误(所有升级都调用 3.0→3.1 迁移) - 新增基于源版本的版本感知迁移分发 - 新增 `from_31_to_32()` 迁移函数处理数据库架构变更 ## 前端 ### 新功能 - 全新 UI 设计系统重构 - 统一设计令牌(颜色、字体、间距、阴影、动画) - 支持深色/浅色主题切换 - 全面的无障碍支持(ARIA、键盘导航、焦点管理) - 移动端响应式布局 - 新增首次运行设置向导页面 - 多步骤向导组件(进度条 + 步骤导航) - 路由守卫自动检测并重定向到设置页面 - 下载器/RSS/通知连接测试反馈 - 中英文 i18n 支持 - 新增 Passkey 管理面板(设置页面) - WebAuthn 浏览器支持检测 - 自动识别设备名称 - Passkey 列表显示和删除 - 登录页面新增 Passkey 指纹登录按钮(支持无用户名登录) - 新增日历视图页面 - 新增下载器管理页面 - 番剧卡片新增悬停遮罩层(显示标题和标签) - 新增 `resolvePosterUrl` 工具函数,统一处理外部 URL 和本地路径(修复 #934) - 重新设计搜索面板,新增模态框和过滤系统 - 新增筛选区域,支持按字幕组、分辨率、字幕类型、季度分类筛选 - 多选筛选器,智能禁用不兼容的选项(灰色显示) - 结果项标签改为非点击式彩色药丸样式 - 统一标签样式(药丸形状、12px 字体) - 标签值标准化(分辨率:FHD/HD/4K,字幕:简/繁/双语) - 筛选分类和结果变体支持展开/收起 - 海报高度自动匹配 4 行变体项(168px) - 点击弹窗外部自动关闭 - 重新设计登录面板,采用现代毛玻璃风格 - 日志页面新增日志级别过滤功能 - 重新设计 LLM 设置面板(修复 #938) - 重新设计设置、下载器、播放器、日志页面样式 - 新增搜索源设置面板 - 支持查看、添加、编辑、删除搜索源 - 默认搜索源(mikan、nyaa、dmhy)不可删除 - URL 模板验证,确保包含 `%s` 占位符 - 新增 iOS 风格通知角标系统 - 黄色角标 + 紫色边框显示需要检查的订阅 - 支持组合显示(如 `! | 2` 表示有警告且有多个规则) - 卡片黄色发光动画提示需要注意 - 编辑弹窗新增警告横幅,支持一键自动检测和忽略 - 规则选择弹窗高亮显示有警告的规则 - 日历页面番剧分组:相同番剧的多个规则合并显示,点击可选择具体规则 - 番剧列表页新增可折叠的「已归档」分区 - 番剧列表页新增骨架屏加载动画 - 规则编辑器新增剧集偏移字段和「自动检测」按钮 - 首页空状态新增「添加 RSS 订阅」按钮,引导新用户快速上手 - 日历页面海报图片添加懒加载,提升性能 - 日历页面「未知播出日」独立为单独区块,优化视觉节奏 - RSS 管理页面新增连接状态标签:健康时显示绿色「已连接」,错误时显示红色「错误」并通过 tooltip 显示错误详情 - 全新移动端优先响应式设计 - 三层断点系统:移动端(<640px)、平板(640-1023px)、桌面端(≥1024px) - 移动端底部导航栏(带图标和文字标签) - 平板迷你侧边栏(56px 图标导航) - 移动端弹窗自动切换为底部抽屉 - 支持下拉刷新 - 支持水平滑动容器 - 移动端卡片列表替代数据表格(RSS 页面) - CSS Grid 响应式布局(番剧卡片网格) - 移动端表单标签垂直堆叠,输入框全宽 - 触摸目标最小 44px,符合无障碍标准 - 安全区域支持(刘海屏设备) - `100dvh` 动态视口高度(修复移动端浏览器地址栏问题) - `viewport-fit=cover` 全屏设备支持 ### 新组件 - `ab-bottom-sheet` — 触摸驱动的底部抽屉组件(拖动关闭、最大高度限制) - `ab-adaptive-modal` — 自适应弹窗(移动端底部抽屉 / 桌面端居中对话框) - `ab-pull-refresh` — 下拉刷新包装组件 - `ab-swipe-container` — 水平滑动容器(CSS scroll-snap) - `ab-data-list` — 移动端友好的卡片列表(替代 NDataTable) - `ab-mobile-nav` — 增强版底部导航栏(图标 + 标签 + 激活指示器) - `useSafeArea` — 安全区域组合式函数 ### 性能优化 - 下载器 store 使用 `shallowRef` 替代 `ref`,避免大数组的深层响应式代理 - 表格列定义改为 `computed`,避免每次渲染重建 - RSS 表格列与数据分离,数据变化时不重建列配置 - 日历页移除重复的 `getAll()` 调用 - `ab-select` 的 `watchEffect` 改为 `watch`,消除挂载时的无效 emit - `useClipboard` 提升到 store 顶层,避免每次 `copy()` 创建新实例 - `setInterval` 替换为 `useIntervalFn`,自动生命周期管理 ### 变更 - 重构搜索逻辑,移除 rxjs 依赖 - 搜索 store 导出重构以匹配组件期望 - 升级前端依赖 - 断点系统从单一 1024px 扩展为 640px + 1024px 双层 - `useBreakpointQuery` 新增 `isTablet`、`isMobileOrTablet`、`isTabletOrPC` - `media-query.vue` 新增 `#tablet` 插槽(回退到 `#mobile`) - UnoCSS 新增 `sm: 640px` 断点 - `ab-input` 移动端全宽 + 增大触摸目标样式 - 布局使用 `dvh` 单位替代 `vh`,支持 safe-area-inset - 修复日历页面未知列宽度问题 - 统一下载器页面操作栏按钮尺寸 - 修复移动端设置页面水平溢出问题 - 输入框添加 `max-width: 100%` 防止超出容器 - 折叠面板添加宽度约束和溢出隐藏 - 设置栅格添加 `min-width: 0` 允许收缩 - 修复移动端顶栏布局 - 搜索按钮改为弹性布局,填充 Logo 和图标之间的空间 - 减小图标按钮尺寸和间距,优化紧凑型布局 - 添加「点击搜索」文字提示 - 修复移动端搜索弹窗关闭按钮被截断问题 - 减小弹窗头部内边距和元素尺寸 - 搜索源选择按钮缩小至适配移动端 - 修复设置页面保存/取消按钮缺少加载状态 - 修复侧边栏展开动画抖动(rotateY → rotate) - 移动端底部导航标签字号从 10px 增至 11px,提升可读性 - 登录页背景动画添加 `will-change: transform` 优化 GPU 性能 ## CI/基础设施 - CI 新增 PR 打开时的构建测试(dev 分支 PR 到 main 自动触发构建) - CI 升级 `actions/upload-artifact` 和 `actions/download-artifact` 到 v4 - Docker 构建移除 `linux/arm/v7` 平台(uv 镜像不支持) - 新增 CLAUDE.md 开发指南 ================================================ FILE: docs/changelog/3.2.md ================================================ # [3.2] - 2025-01 ## Backend ### Features - Added WebAuthn Passkey passwordless login support - Register, authenticate, and manage Passkey credentials - Multi-device credential backup detection (iCloud Keychain, etc.) - Clone attack protection (sign_count verification) - Authentication strategy pattern unifying password and Passkey login interfaces - Usernameless login support via discoverable credentials (resident keys) - Added season/episode offset auto-detection - Analyzes TMDB episode air dates to detect "virtual seasons" (e.g., Frieren S1 split into two parts) - Auto-identifies different parts when broadcast gap exceeds 6 months - Calculates episode offset (e.g., RSS shows S2E1 → TMDB S1E29) - Background scan thread automatically detects offset issues in existing subscriptions - New API endpoints: `POST /bangumi/detect-offset`, `PATCH /bangumi/dismiss-review/{id}` - Added bangumi archive functionality - Manual archive/unarchive support - Auto-archive completed series - New API endpoints: `PATCH /bangumi/archive/{id}`, `PATCH /bangumi/unarchive/{id}`, `GET /bangumi/refresh/metadata` - Added search provider configuration API - `GET /search/provider/config` - Get search provider config - `PUT /search/provider/config` - Update search provider config - Offset detection panel now shows suggested values (parsed season/episode and recommended offset) - Fixed season offset not being applied to download folder path - Setting season offset now auto-updates qBittorrent save path (e.g., `Season 2` → `Season 1`) - RSS rule save paths also sync the update - Optimized episode offset suggestion logic - Simple season mismatch no longer suggests episode offset (only virtual seasons need it) - Improved prompt messages to clarify whether episode adjustment is needed - Added RSS connection status tracking - Records `connection_status` (healthy/error), `last_checked_at`, and `last_error` after each refresh - Added first-run setup wizard - 7-step guided configuration: account, downloader, RSS source, media path, notifications - Downloader connection test, RSS source validation - Optional steps can be skipped and configured later in settings - Sentinel file mechanism (`config/.setup_complete`) prevents re-triggering - Unauthenticated setup API (only available on first run, returns 403 after completion) - Added calendar view with Bangumi.tv broadcast schedule integration - Added downloader API and management interface - Full async migration - Database layer async support (aiosqlite) for non-blocking I/O in Passkey operations - `UserDatabase` supports both sync/async modes for backward compatibility - `Database` context manager supports both `with` (sync) and `async with` (async) - RSS engine, downloader, checker, and parser fully converted to async - Network requests migrated from `requests` to `httpx` (AsyncClient) - Backend migrated to `uv` package manager (pyproject.toml + uv.lock) - Server startup uses background tasks to avoid blocking (fixes #891, #929) - Database migration auto-fills NULL values with model defaults - Database adds `needs_review` and `needs_review_reason` fields for offset detection ### Performance - Shared HTTP client connection pool, reuses TCP/SSL connections - RSS refresh now concurrent (`asyncio.gather`), ~10x faster with multiple sources - Torrent file download now concurrent, ~5x faster for multiple torrents - Rename module concurrent file list fetching, ~20x faster - Notification sending now concurrent, removed 2-second hardcoded delay - Added TMDB and Mikan parser result caching to avoid duplicate API calls - Database indexes added for `Torrent.url`, `Torrent.rss_id`, `Bangumi.title_raw`, `Bangumi.deleted`, `RSSItem.url` - RSS batch enable/disable uses single transaction instead of per-item commits - Pre-compiled regex patterns for torrent name parsing and filter matching - `SeasonCollector` created outside loops, reuses single authentication - RSS parsing deduplication changed from O(n²) list lookup to O(1) set lookup - `Episode`/`SeasonInfo` dataclasses use `__slots__` for reduced memory footprint ### Changes - Upgraded WebAuthn dependency to py_webauthn 2.7.0 - `_get_webauthn_from_request` prioritizes browser Origin header, fixing verification issues in cross-port development environments - `auth_user` and `update_user_info` converted to async functions - `TitleParser.tmdb_parser` converted to async function - `RSSEngine` methods fully async (`pull_rss`, `refresh_rss`, `download_bangumi`, `add_rss`) - `Checker.check_downloader` converted to async function - `ProgramStatus` migrated from threading to asyncio (Event, Lock) ### Bugfixes - Fixed downloader connection check with max retry limit - Fixed transient network errors when adding torrents with retry logic - Fixed multiple issues in search and subscription flow - Improved torrent fetch reliability and error handling - Fixed `aaguid` type error (now `str` in py_webauthn 2.7.0, no longer `bytes`) - Fixed missing `credential_backup_eligible` field (replaced with `credential_device_type`) - Fixed `verify_authentication_response` receiving invalid `credential_id` parameter causing TypeError - Fixed program startup blocking the server (fixes #891, #929, #886, #917, #946) - Fixed search interface export not matching component expectations - Fixed poster endpoint path check incorrectly intercepting all requests (fixes #933, #934) - Fixed OpenAI parser security issue - Fixed database tests using async sessions with sync code mismatch - Fixed config field conflicts when upgrading from 3.1.x to 3.2 causing settings loss (fixes #956) - `program.sleep_time` / `program.times` auto-migrated to `rss_time` / `rename_time` - Removed deprecated `rss_parser` fields (`type`, `custom_url`, `token`, `enable_tmdb`) - Fixed `ENV_TO_ATTR` environment variable mapping pointing to non-existent model fields - Fixed `DEFAULT_SETTINGS` inconsistency with current config model - Fixed version upgrade migration logic errors (all upgrades calling 3.0→3.1 migration) - Added version-aware migration dispatch based on source version - Added `from_31_to_32()` migration function for database schema changes ## Frontend ### Features - Complete UI design system redesign - Unified design tokens (colors, fonts, spacing, shadows, animations) - Light/dark theme toggle support - Comprehensive accessibility support (ARIA, keyboard navigation, focus management) - Responsive layout for mobile devices - Added first-run setup wizard page - Multi-step wizard component (progress bar + step navigation) - Route guard auto-detection and redirect to setup page - Downloader/RSS/notification connection test feedback - Chinese and English i18n support - Added Passkey management panel (settings page) - WebAuthn browser support detection - Automatic device name identification - Passkey list display and deletion - Added Passkey fingerprint login button on login page (supports usernameless login) - Added calendar view page - Added downloader management page - Added Bangumi card hover overlay (showing title and tags) - Added `resolvePosterUrl` utility function for unified external URL and local path handling (fixes #934) - Redesigned search panel with modal and filter system - Added filter section supporting fansub group, resolution, subtitle type, and season filtering - Multi-select filters with smart disable for incompatible options (grayed out) - Result item tags changed to non-clickable colored pill style - Unified tag styling (pill shape, 12px font) - Standardized tag values (resolution: FHD/HD/4K, subtitles: CHS/CHT/Dual) - Filter categories and result variants support expand/collapse - Poster height auto-matches 4 rows of variant items (168px) - Click outside modal to auto-close - Redesigned login panel with modern glassmorphism style - Added log level filter in log view - Redesigned LLM settings panel (fixes #938) - Redesigned settings, downloader, player, and log page styles - Added search provider settings panel - View, add, edit, delete search sources in UI - Default sources (mikan, nyaa, dmhy) cannot be deleted - URL template validation ensures `%s` placeholder - Added iOS-style notification badge system - Yellow badge + purple border for subscriptions needing review - Combined display support (e.g., `! | 2` for warning + multiple rules) - Yellow glow animation on cards needing attention - Edit modal warning banner with one-click auto-detect and dismiss - Rule selection modal highlights rules with warnings - Calendar page bangumi grouping: same anime with multiple rules merged, click to select specific rule - Bangumi list page collapsible "Archived" section - Bangumi list page skeleton loading animation - Rule editor episode offset field with "Auto Detect" button - Empty state on home page now includes "Add RSS Subscription" button to guide new users - Calendar page poster images now use lazy loading for better performance - Calendar page "Unknown Air Date" section separated into its own block for better visual rhythm - RSS management page connection status labels: green "Connected" when healthy, red "Error" with tooltip for details - New mobile-first responsive design - Three-tier breakpoint system: mobile (<640px), tablet (640-1023px), desktop (≥1024px) - Mobile bottom navigation bar (with icons and text labels) - Tablet mini sidebar (56px icon navigation) - Mobile popups automatically switch to bottom sheets - Pull-to-refresh support - Horizontal swipe container support - Mobile card list replacing data tables (RSS page) - CSS Grid responsive layout (Bangumi card grid) - Form labels stack vertically on mobile, full-width inputs - Touch targets minimum 44px, meeting accessibility standards - Safe area support (notched devices) - `100dvh` dynamic viewport height (fixes mobile browser address bar issue) - `viewport-fit=cover` for full-screen devices ### New Components - `ab-bottom-sheet` — Touch-driven bottom sheet component (drag to close, max height limit) - `ab-adaptive-modal` — Adaptive modal (bottom sheet on mobile / centered dialog on desktop) - `ab-pull-refresh` — Pull-to-refresh wrapper component - `ab-swipe-container` — Horizontal swipe container (CSS scroll-snap) - `ab-data-list` — Mobile-friendly card list (replacing NDataTable) - `ab-mobile-nav` — Enhanced bottom navigation bar (icon + label + active indicator) - `useSafeArea` — Safe area composable ### Performance - Downloader store uses `shallowRef` instead of `ref` to avoid deep reactive proxy on large arrays - Table column definitions moved to `computed` to avoid rebuilding on each render - RSS table columns separated from data, column config not rebuilt on data changes - Calendar page removed duplicate `getAll()` calls - `ab-select` `watchEffect` changed to `watch`, eliminates invalid emit on mount - `useClipboard` hoisted to store top level, avoids creating new instance on each `copy()` - `setInterval` replaced with `useIntervalFn` for automatic lifecycle management ### Changes - Refactored search logic, removed rxjs dependency - Search store export refactored to match component expectations - Upgraded frontend dependencies - Breakpoint system expanded from single 1024px to 640px + 1024px two-tier - `useBreakpointQuery` added `isTablet`, `isMobileOrTablet`, `isTabletOrPC` - `media-query.vue` added `#tablet` slot (falls back to `#mobile`) - UnoCSS added `sm: 640px` breakpoint - `ab-input` mobile full-width + increased touch target styling - Layout uses `dvh` units instead of `vh`, supports safe-area-inset - Fixed calendar page unknown column width - Unified action bar button sizes in downloader page - Fixed mobile settings page horizontal overflow - Added `max-width: 100%` to inputs to prevent container overflow - Added width constraint and overflow hidden to fold panels - Added `min-width: 0` to settings grid to allow shrinking - Fixed mobile top bar layout - Search button changed to flex layout, filling space between logo and icons - Reduced icon button size and spacing for compact layout - Added "Click to search" text hint - Fixed mobile search modal close button being cut off - Reduced modal header padding and element sizes - Search source selection buttons scaled down for mobile - Fixed settings page save/cancel buttons missing loading state - Fixed sidebar expand animation jitter (rotateY → rotate) - Mobile bottom navigation label font size increased from 10px to 11px for better readability - Login page background animation added `will-change: transform` for GPU performance ## CI/Infrastructure - CI added build test on PR open (dev branch PRs to main auto-trigger build) - CI upgraded `actions/upload-artifact` and `actions/download-artifact` to v4 - Docker build removed `linux/arm/v7` platform (uv image doesn't support it) - Added CLAUDE.md development guide ================================================ FILE: docs/config/downloader.md ================================================ # 下载器设置 ## WebUI 配置 ![downloader](/image/config/downloader.png){width=500}{class=ab-shadow-card}
- **下载器类型** 为下载器类型。目前仅支持 qBittorrent。 - **地址** 为下载器地址。[详见下方说明](#下载器地址) - **下载路径** 为下载器的映射下载路径。[详见下方说明](#下载路径问题) - **SSL** 启用下载器连接的 SSL。 ## 常见问题 ### 下载器地址 ::: warning 注意 请勿使用 127.0.0.1 或 localhost 作为下载器地址。 ::: 由于官方教程中 AB 在 Docker 的 **Bridge** 模式下运行,使用 127.0.0.1 或 localhost 会解析到 AB 自身,而非下载器。 - 如果 qBittorrent 也在 Docker 中运行,建议使用 Docker **网关地址:172.17.0.1**。 - 如果 qBittorrent 运行在宿主机上,请使用宿主机的 IP 地址。 如果 AB 以 **Host** 模式运行,则可以使用 127.0.0.1 代替 Docker 网关地址。 ::: warning 注意 Macvlan 会隔离容器网络。如果没有额外的网桥配置,容器无法访问其他容器或宿主机本身。 ::: ### 下载路径问题 AB 中配置的路径仅用于生成对应的番剧文件路径。AB 本身不会直接管理该路径下的文件。 **下载路径应该填什么?** 此参数只需与**下载器**的配置匹配: - Docker:如果 qB 使用 `/downloads`,则设置为 `/downloads/Bangumi`。`Bangumi` 可以改为任意名称。 - Linux/macOS:如果是 `/home/usr/downloads` 或 `/User/UserName/Downloads`,只需在末尾添加 `/Bangumi`。 - Windows:将 `D:\Media\` 改为 `D:\Media\Bangumi` ## `config.json` 配置选项 配置文件中的对应选项如下: 配置节:`downloader` | 参数 | 说明 | 类型 | WebUI 选项 | 默认值 | |----------|--------------|---------|-----------------|---------------------| | type | 下载器类型 | 字符串 | 下载器类型 | qbittorrent | | host | 下载器地址 | 字符串 | 下载器地址 | 172.17.0.1:8080 | | username | 下载器用户名 | 字符串 | 下载器用户名 | admin | | password | 下载器密码 | 字符串 | 下载器密码 | adminadmin | | path | 下载路径 | 字符串 | 下载路径 | /downloads/Bangumi | | ssl | 启用 SSL | 布尔值 | 启用 SSL | false | ================================================ FILE: docs/config/experimental.md ================================================ # 实验性功能 ::: warning 实验性功能仍在测试中。启用后可能会导致意外问题,且可能在未来版本中被移除。请谨慎使用! ::: ## OpenAI ChatGPT 使用 OpenAI ChatGPT 进行更好的结构化标题解析。例如: ``` input: "【喵萌奶茶屋】★04月新番★[夏日重现/Summer Time Rendering][11][1080p][繁日双语][招募翻译]" output: '{"group": "喵萌奶茶屋", "title_en": "Summer Time Rendering", "resolution": "1080p", "episode": 11, "season": 1, "title_zh": "夏日重现", "sub": "", "title_jp": "", "season_raw": "", "source": ""}' ``` ![experimental OpenAI](/image/config/experimental-openai.png){width=500}{class=ab-shadow-card} - **启用 OpenAI** 启用 OpenAI 并使用 ChatGPT 进行标题解析。 - **OpenAI API 类型** 默认为 OpenAI。 - **OpenAI API Key** 为您的 OpenAI 账户 API 密钥。 - **OpenAI API Base URL** 为 OpenAI 端点。默认为官方 OpenAI URL;您可以将其更改为兼容的第三方端点。 - **OpenAI Model** 为 ChatGPT 模型参数。目前提供 `gpt-3.5-turbo`,价格实惠且在正确的提示下效果出色。 ## Microsoft Azure OpenAI ![experimental Microsoft Azure OpenAI](/image/config/experimental-azure-openai.png){width=500}{class=ab-shadow-card} 除了标准 OpenAI 外,[版本 3.1.8](https://github.com/EstrellaXD/Auto_Bangumi/releases/tag/3.1.8) 添加了 Microsoft Azure OpenAI 支持。使用方式与标准 OpenAI 类似,共享部分参数,但请注意以下几点: - **启用 OpenAI** 启用 OpenAI 并使用 ChatGPT 进行标题解析。 - **OpenAI API 类型** — 选择 `azure` 以显示 Azure 专用选项。 - **OpenAI API Key** 为您的 Microsoft Azure OpenAI API 密钥。 - **OpenAI API Base URL** 对应 Microsoft Azure OpenAI 入口点。**需要手动填写**。 - **Azure OpenAI 版本** 为 API 版本。默认为 `2023-05-15`。查看[支持的版本](https://learn.microsoft.com/en-us/azure/ai-services/openai/reference#completions)。 - **Azure OpenAI Deployment ID** 为您的部署 ID,通常与模型名称相同。注意 Azure OpenAI 不支持 `_-` 以外的符号,因此 `gpt-3.5-turbo` 在 Azure 中变为 `gpt-35-turbo`。**需要手动填写**。 参考文档: - [快速入门:开始使用 Azure OpenAI 服务的 GPT-35-Turbo 和 GPT-4](https://learn.microsoft.com/en-us/azure/ai-services/openai/chatgpt-quickstart?tabs=command-line&pivots=programming-language-python) - [了解如何使用 GPT-35-Turbo 和 GPT-4 模型](https://learn.microsoft.com/en-us/azure/ai-services/openai/how-to/chatgpt?pivots=programming-language-chat-completions) ## `config.json` 配置选项 配置文件中的对应选项如下: 配置节:`experimental_openai` | 参数 | 说明 | 类型 | WebUI 选项 | 默认值 | |---------------|----------------------------|---------|----------------------------|---------------------------| | enable | 启用 OpenAI 解析器 | 布尔值 | 启用 OpenAI | false | | api_type | OpenAI API 类型 | 字符串 | API 类型 (`openai`/`azure`) | openai | | api_key | OpenAI API 密钥 | 字符串 | OpenAI API Key | | | api_base | API Base URL (Azure 入口点) | 字符串 | OpenAI API Base URL | https://api.openai.com/v1 | | model | OpenAI 模型 | 字符串 | OpenAI Model | gpt-3.5-turbo | | api_version | Azure OpenAI API 版本 | 字符串 | Azure API 版本 | 2023-05-15 | | deployment_id | Azure Deployment ID | 字符串 | Azure Deployment ID | | ================================================ FILE: docs/config/manager.md ================================================ # 番剧管理器设置 ## WebUI 配置 ![proxy](/image/config/manager.png){width=500}{class=ab-shadow-card}
- **启用** 启用番剧管理器。如果禁用,下方设置将不会生效。 - **重命名方式** 为重命名方法。目前支持: - `pn` — `种子标题 S0XE0X.mp4` 格式 - `advance` — `官方标题 S0XE0X.mp4` 格式 - `none` — 不重命名 - **剧集补全** 启用当季剧集补全。如果启用,将下载缺失的剧集。 - **添加字幕组标签** 为下载规则添加字幕组标签。 - **删除错误种子** 删除出错的种子。 - [关于文件路径][1] - [关于重命名][2] ## `config.json` 配置选项 配置文件中的对应选项如下: 配置节:`bangumi_manager` | 参数 | 说明 | 类型 | WebUI 选项 | 默认值 | |--------------------|------------------|---------|-----------------|--------| | enable | 启用番剧管理器 | 布尔值 | 启用管理器 | true | | eps_complete | 启用剧集补全 | 布尔值 | 剧集补全 | false | | rename_method | 重命名方式 | 字符串 | 重命名方式 | pn | | group_tag | 添加字幕组标签 | 布尔值 | 字幕组标签 | false | | remove_bad_torrent | 删除错误种子 | 布尔值 | 删除错误种子 | false | [1]: https://www.autobangumi.org/faq/#download-path [2]: https://www.autobangumi.org/faq/#file-renaming ================================================ FILE: docs/config/notifier.md ================================================ # 通知设置 ## WebUI 配置 ![notification](/image/config/notifier.png){width=500}{class=ab-shadow-card}
- **启用** 启用通知功能。如果禁用,下方设置将不会生效。 - **类型** 为通知类型。目前支持: - Telegram - Wecom - Bark - ServerChan - **Chat ID** 仅在使用 `telegram` 通知时需要填写。[如何获取 Telegram Bot Chat ID][1] - **Wecom**:在 Chat ID 字段填写自定义推送 URL,并在服务端添加[富文本消息][2]类型。[Wecom 配置指南][3] ## `config.json` 配置选项 配置文件中的对应选项如下: 配置节:`notification` | 参数 | 说明 | 类型 | WebUI 选项 | 默认值 | |---------|---------------|---------|-----------------|----------| | enable | 启用通知 | 布尔值 | 通知 | false | | type | 通知类型 | 字符串 | 通知类型 | telegram | | token | 通知 Token | 字符串 | 通知 Token | | | chat_id | 通知 Chat ID | 字符串 | 通知 Chat ID | | [1]: https://core.telegram.org/bots#6-botfather [2]: https://github.com/umbors/wecomchan-alifun [3]: https://github.com/easychen/wecomchan ================================================ FILE: docs/config/parser.md ================================================ # 解析器设置 AB 的解析器用于解析聚合 RSS 链接。当 RSS 订阅中出现新条目时,AB 会解析标题并生成自动下载规则。 ::: tip 从 v3.1 开始,解析器设置已移至各个 RSS 的单独设置中。要配置**解析器类型**,请参阅 [RSS 解析器设置][add_rss]。 ::: ## WebUI 中的解析器设置 ![parser](/image/config/parser.png){width=500}{class=ab-shadow-card}
- **启用**:是否启用 RSS 解析器。 - **语言** 为 RSS 解析器语言。目前支持 `zh`、`jp` 和 `en`。 - **过滤** 为全局 RSS 解析器过滤规则。可以输入字符串或正则表达式,AB 会在 RSS 解析时过滤掉匹配的条目。 ## `config.json` 配置选项 配置文件中的对应选项如下: 配置节:`rss_parser` | 参数 | 说明 | 类型 | WebUI 选项 | 默认值 | |----------|------------------|---------|-------------------|----------------| | enable | 启用 RSS 解析器 | 布尔值 | 启用 RSS 解析器 | true | | filter | RSS 解析器过滤 | 数组 | 过滤 | [720,\d+-\d+] | | language | RSS 解析器语言 | 字符串 | RSS 解析器语言 | zh | [rss_token]: rss [add_rss]: /feature/rss#parser-settings [reproxy]: proxy#reverse-proxy ================================================ FILE: docs/config/program.md ================================================ # 程序设置 ## WebUI 配置 ![program](/image/config/program.png){width=500}{class=ab-shadow-card}
- 时间间隔参数的单位为秒。如需设置分钟,请换算为秒。 - RSS 为 RSS 检查间隔,影响自动下载规则的生成频率。 - 重命名为重命名检查间隔,如需调整重命名检查频率可修改此项。 - WebUI 端口为端口号。注意:如果使用 Docker,更改端口后需要在 Docker 中重新映射端口。 ## `config.json` 配置选项 配置文件中的对应选项如下: 配置节:`program` | 参数 | 说明 | 类型 | WebUI 选项 | 默认值 | |-------------|----------------|---------------|-------------------|--------| | rss_time | RSS 检查间隔 | 整数(秒) | RSS 检查间隔 | 7200 | | rename_time | 重命名检查间隔 | 整数(秒) | 重命名检查间隔 | 60 | | webui_port | WebUI 端口 | 整数 | WebUI 端口 | 7892 | ================================================ FILE: docs/config/proxy.md ================================================ # 代理与反向代理 ## 代理 ![proxy](/image/config/proxy.png){width=500}{class=ab-shadow-card}
AB 支持 HTTP 和 SOCKS5 代理,以帮助解决网络问题。 - **启用**:是否启用代理。 - **类型** 为代理类型。 - **地址** 为代理地址。 - **端口** 为代理端口。 ::: tip 在 **SOCKS5** 模式下,需要填写用户名和密码。 ::: ## `config.json` 配置选项 配置文件中的对应选项如下: 配置节:`proxy` | 参数 | 说明 | 类型 | WebUI 选项 | 默认值 | |----------|------------|---------|--------------|--------| | enable | 启用代理 | 布尔值 | 代理 | false | | type | 代理类型 | 字符串 | 代理类型 | http | | host | 代理地址 | 字符串 | 代理地址 | | | port | 代理端口 | 整数 | 代理端口 | | | username | 代理用户名 | 字符串 | 代理用户名 | | | password | 代理密码 | 字符串 | 代理密码 | | ## 反向代理 - 使用 Mikan Project 备用域名 `mikanime.tv` 替换 RSS 订阅链接中的 `mikanani.me`。 - 使用 Cloudflare Worker 作为反向代理,替换 RSS 订阅中所有的 `mikanani.me` 域名。 ## Cloudflare Workers 参考绕过其他服务封锁的方法,您可以使用 Cloudflare Workers 搭建反向代理。如何注册域名并绑定到 Cloudflare 不在本指南范围内。在 Workers 中添加以下代码,即可使用自己的域名访问 Mikan Project 并从 RSS 链接下载种子: ```js const TELEGRAPH_URL = 'https://mikanani.me'; const MY_DOMAIN = 'https://yourdomain.com' addEventListener('fetch', event => { event.respondWith(handleRequest(event.request)) }) async function handleRequest(request) { const url = new URL(request.url); url.host = TELEGRAPH_URL.replace(/^https?:\/\//, ''); const modifiedRequest = new Request(url.toString(), { headers: request.headers, method: request.method, body: request.body, redirect: 'manual' }); const response = await fetch(modifiedRequest); const contentType = response.headers.get('Content-Type') || ''; // Only perform replacement if content type is RSS if (contentType.includes('application/xml')) { const text = await response.text(); const replacedText = text.replace(/https?:\/\/mikanani\.me/g, MY_DOMAIN); const modifiedResponse = new Response(replacedText, response); // Add CORS headers modifiedResponse.headers.set('Access-Control-Allow-Origin', '*'); return modifiedResponse; } else { const modifiedResponse = new Response(response.body, response); // Add CORS headers modifiedResponse.headers.set('Access-Control-Allow-Origin', '*'); return modifiedResponse; } } ``` ================================================ FILE: docs/config/rss.md ================================================ # RSS 订阅设置 AutoBangumi 可以自动解析聚合 RSS 订阅源,并根据字幕组和番剧名称生成下载规则,实现全自动追番。 以下以 [Mikan Project][mikan-site] 为例,说明如何获取 RSS 订阅链接。 请注意,Mikan Project 主站在部分地区可能被屏蔽。如果无法直接访问,请使用以下备用域名: [Mikan Project (备用)][mikan-cn-site] ## 获取订阅链接 本项目基于 Mikan Project 提供的 RSS 链接进行解析。要启用自动追番功能,您需要注册并获取 Mikan Project 的 RSS 链接: ![image](/image/rss/rss-token.png){data-zoomable} RSS 链接格式如下: ```txt https://mikanani.me/RSS/MyBangumi?token=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx # 或 https://mikanime.tv/RSS/MyBangumi?token=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ``` ## Mikan Project 订阅注意事项 由于 AutoBangumi 会解析接收到的所有 RSS 条目,订阅时请注意以下几点: ![image](/image/rss/advanced-subscription.png){data-zoomable} - 在个人设置中启用高级选项。 - 每部番剧只订阅一个字幕组。点击 Mikan Project 上的番剧海报打开子菜单,选择单个字幕组。 - 如果字幕组同时提供简体和繁体中文字幕,Mikan Project 通常会提供选择方式,请选择其中一种。 - 如果没有字幕类型选择选项,可以在 AutoBangumi 中设置 `filter` 进行过滤,或在规则生成后在 qBittorrent 中手动过滤。 - 目前不支持解析 OVA 和剧场版订阅。 [mikan-site]: https://mikanani.me/ [mikan-cn-site]: https://mikanime.tv/ ================================================ FILE: docs/deploy/docker-cli.md ================================================ # 使用 Docker CLI 部署 ## 新版本说明 从 AutoBangumi 2.6 开始,您可以直接在 WebUI 中配置所有设置。您可以先启动容器,然后在 WebUI 中进行配置。旧版本的环境变量配置将自动迁移。环境变量仍然有效,但仅在首次启动时生效。 ## 创建数据和配置目录 为确保 AB 的数据和配置在更新时持久化,我们建议使用 Docker 卷或绑定挂载。 ```shell # 使用绑定挂载 mkdir -p ${HOME}/AutoBangumi/{config,data} cd ${HOME}/AutoBangumi ``` 选择绑定挂载或 Docker 卷: ```shell # 使用 Docker 卷 docker volume create AutoBangumi_config docker volume create AutoBangumi_data ``` ## 使用 Docker CLI 部署 AutoBangumi 复制并运行以下命令。 请确保您的工作目录为 AutoBangumi。 ```shell docker run -d \ --name=AutoBangumi \ -v ${HOME}/AutoBangumi/config:/app/config \ -v ${HOME}/AutoBangumi/data:/app/data \ -p 7892:7892 \ -e TZ=Asia/Shanghai \ -e PUID=$(id -u) \ -e PGID=$(id -g) \ -e UMASK=022 \ --network=bridge \ --dns=8.8.8.8 \ --restart unless-stopped \ ghcr.io/estrellaxd/auto_bangumi:latest ``` 如果使用 Docker 卷,请相应替换绑定路径: ```shell -v AutoBangumi_config:/app/config \ -v AutoBangumi_data:/app/data \ ``` AB WebUI 将自动启动,但主程序处于暂停状态。访问 `http://abhost:7892` 进行配置。 AB 会自动将环境变量写入 `config.json` 并开始运行。 我们建议使用 _[Portainer](https://www.portainer.io)_ 或类似的 Docker 管理界面进行高级部署。 ================================================ FILE: docs/deploy/docker-compose.md ================================================ # 使用 Docker Compose 部署 使用 `docker-compose.yml` 文件一键部署 **AutoBangumi**。 ## 安装 Docker Compose Docker Compose 通常与 Docker 捆绑安装。使用以下命令检查: ```bash docker compose -v ``` 如果未安装,请使用以下命令安装: ```bash $ sudo apt-get update $ sudo apt-get install docker-compose-plugin ``` ## 部署 **AutoBangumi** ### 创建 AutoBangumi 和数据目录 ```bash mkdir -p ${HOME}/AutoBangumi/{config,data} cd ${HOME}/AutoBangumi ``` ### 方式一:自定义 Docker Compose 配置 ```yaml version: "3.8" services: AutoBangumi: image: "ghcr.io/estrellaxd/auto_bangumi:latest" container_name: AutoBangumi volumes: - ./config:/app/config - ./data:/app/data ports: - "7892:7892" restart: unless-stopped dns: - 8.8.8.8 network_mode: bridge environment: - TZ=Asia/Shanghai - PGID=$(id -g) - PUID=$(id -u) - UMASK=022 ``` 将以上内容复制到 `docker-compose.yml` 文件中。 ### 方式二:下载 Docker Compose 配置文件 如果您不想手动创建 `docker-compose.yml` 文件,项目提供了预制的配置: - 仅安装 **AutoBangumi**: ```bash wget https://raw.githubusercontent.com/EstrellaXD/Auto_Bangumi/main/docs/resource/docker-compose/AutoBangumi/docker-compose.yml ``` - 安装 **qBittorrent** 和 **AutoBangumi**: ```bash wget https://raw.githubusercontent.com/EstrellaXD/Auto_Bangumi/main/docs/resource/docker-compose/qBittorrent+AutoBangumi/docker-compose.yml ``` 选择您的安装方式并运行命令下载 `docker-compose.yml` 文件。如有需要,可以使用文本编辑器自定义参数。 ### 定义环境变量 如果您使用的是下载的 AB+QB Docker Compose 文件,需要定义以下环境变量: ```shell export \ QB_PORT= ``` - `QB_PORT`:输入您现有的 qBittorrent 端口或您想要的自定义端口,例如 `8080` ### 启动 Docker Compose ```bash docker compose up -d ``` ================================================ FILE: docs/deploy/dsm.md ================================================ # 群晖 NAS(DSM 7.2)部署(威联通类似) DSM 7.2 支持 Docker Compose,因此我们建议使用 Docker Compose 进行一键部署。 ## 创建配置和数据目录 在 `/volume1/docker/` 下创建 `AutoBangumi` 文件夹,然后在其中创建 `config` 和 `data` 子文件夹。 ## 安装 Container Manager(Docker)套件 打开套件中心,安装 Container Manager(Docker)套件。 ![install-docker](/image/dsm/install-docker.png){data-zoomable} ## 通过 Docker Compose 安装 AB 点击**项目**,然后点击**新增**,选择 **Docker Compose**。 ![new-compose](/image/dsm/new-compose.png){data-zoomable} 将以下内容复制粘贴到 **Docker Compose** 中: ```yaml version: "3.4" services: ab: image: "ghcr.io/estrellaxd/auto_bangumi:latest" container_name: "auto_bangumi" restart: unless-stopped ports: - "7892:7892" volumes: - "./config:/app/config" - "./data:/app/data" network_mode: bridge environment: - TZ=Asia/Shanghai - AB_METHOD=Advance - PGID=1000 - PUID=1000 - UMASK=022 ``` 点击**下一步**,然后点击**完成**。 ![create](/image/dsm/create.png){data-zoomable} 创建完成后,访问 `http://:7892` 进入 AB 并进行配置。 ## 通过 Docker Compose 安装 AB 和 qBittorrent 当您同时拥有代理和 IPv6 时,在群晖 NAS 的 Docker 中配置 IPv6 可能比较复杂。我们建议将 AB 和 qBittorrent 都安装在主机网络上以降低复杂性。 以下配置假设您已在 Docker 中部署了 Clash 代理,可通过本地 IP 的指定端口访问。 按照上一节的方法,调整并将以下内容粘贴到 **Docker Compose** 中: ```yaml qbittorrent: container_name: qbittorrent image: linuxserver/qbittorrent hostname: qbittorrent environment: - PGID=1000 # 根据需要修改 - PUID=1000 # 根据需要修改 - WEBUI_PORT=8989 - TZ=Asia/Shanghai volumes: - ./qb_config:/config - your_anime_path:/downloads # 修改为您的番剧存储目录。在 AB 中将下载路径设置为 /downloads networks: - host restart: unless-stopped auto_bangumi: container_name: AutoBangumi environment: - TZ=Asia/Shanghai - PGID=1000 # 根据需要修改 - PUID=1000 # 根据需要修改 - UMASK=022 - AB_DOWNLOADER_HOST=127.0.0.1:8989 # 根据需要修改端口 volumes: - /volume1/docker/ab/config:/app/config - /volume1/docker/ab/data:/app/data network_mode: host environment: - AB_METHOD=Advance dns: - 8.8.8.8 restart: unless-stopped image: "ghcr.io/estrellaxd/auto_bangumi:latest" depends_on: - qbittorrent ``` ## 附加说明 PGID 和 PUID 的值需要根据您的系统确定。对于较新的群晖 NAS 设备,通常为:`PUID=1026, PGID=100`。修改 qBittorrent 端口时,请确保在所有位置都进行更新。 代理设置请参阅:[代理设置](../config/proxy) 在性能较低的设备上,默认配置可能会大量占用 CPU,导致 AB 无法连接到 qB,qB WebUI 也无法访问。 对于 220+ 等设备,建议使用以下 qBittorrent 设置以降低 CPU 使用率: - 设置 -> 连接 -> 连接限制 - 全局最大连接数:300 - 每个种子最大连接数:60 - 全局上传槽位限制:15 - 每个种子上传槽位:4 - BitTorrent - 最大活动检查种子数:1 - 种子队列 - 最大活动下载数:3 - 最大活动上传数:5 - 最大活动种子数:10 - RSS - RSS 阅读器 - 每个订阅源最大文章数:50 ================================================ FILE: docs/deploy/local.md ================================================ # 本地部署 ::: warning 本地部署可能会导致意外问题。我们强烈建议使用 Docker 代替。 此文档可能存在更新延迟。如有问题,请在 [Issues](https://github.com/EstrellaXD/Auto_Bangumi/issues) 中提出。 ::: ## 下载最新版本 ```bash VERSION=$(curl -s "https://api.github.com/repos/EstrellaXD/Auto_Bangumi/releases/latest" | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/') curl -L -O "https://github.com/EstrellaXD/Auto_Bangumi/releases/download/$VERSION/app-v$VERSION.zip" ``` ## 解压压缩包 在 Unix/WSL 系统上,使用以下命令。在 Windows 上,请手动解压。 ```bash unzip app-v$VERSION.zip -d AutoBangumi cd AutoBangumi ``` ## 创建虚拟环境并安装依赖 确保本地已安装 Python 3.10+ 和 pip。 ```bash cd src python3 -m venv env python3 pip install -r requirements.txt ``` ## 创建配置和数据目录 ```bash mkdir config mkdir data ``` ## 运行 AutoBangumi ```bash python3 main.py ``` ## Windows 开机自启 可以使用 `nssm` 实现开机自启。使用 `nssm` 的示例: ```powershell nssm install AutoBangumi (Get-Command python).Source nssm set AutoBangumi AppParameters (Get-Item .\main.py).FullName nssm set AutoBangumi AppDirectory (Get-Item ..).FullName nssm set AutoBangumi Start SERVICE_DELAYED_AUTO_START ``` ================================================ FILE: docs/deploy/quick-start.md ================================================ # 快速开始 我们推荐使用 Docker 部署 AutoBangumi。 部署前,请确保已安装 [Docker Engine][docker-engine] 或 [Docker Desktop][docker-desktop]。 ## 创建数据和配置目录 为确保 AB 的数据和配置在更新时持久化,我们建议使用绑定挂载或 Docker 卷。 ```shell # 使用绑定挂载 mkdir -p ${HOME}/AutoBangumi/{config,data} cd ${HOME}/AutoBangumi ``` 选择绑定挂载或 Docker 卷: ```shell # 使用 Docker 卷 docker volume create AutoBangumi_config docker volume create AutoBangumi_data ``` ## 使用 Docker 部署 AutoBangumi 运行这些命令时,请确保您在 AutoBangumi 目录中。 ### 方式一:使用 Docker CLI 部署 复制并运行以下命令: ```shell docker run -d \ --name=AutoBangumi \ -v ${HOME}/AutoBangumi/config:/app/config \ -v ${HOME}/AutoBangumi/data:/app/data \ -p 7892:7892 \ -e TZ=Asia/Shanghai \ -e PUID=$(id -u) \ -e PGID=$(id -g) \ -e UMASK=022 \ --network=bridge \ --dns=8.8.8.8 \ --restart unless-stopped \ ghcr.io/estrellaxd/auto_bangumi:latest ``` ### 方式二:使用 Docker Compose 部署 将以下内容复制到 `docker-compose.yml` 文件中: ```yaml version: "3.8" services: AutoBangumi: image: "ghcr.io/estrellaxd/auto_bangumi:latest" container_name: AutoBangumi volumes: - ./config:/app/config - ./data:/app/data ports: - "7892:7892" network_mode: bridge restart: unless-stopped dns: - 8.8.8.8 environment: - TZ=Asia/Shanghai - PGID=$(id -g) - PUID=$(id -u) - UMASK=022 ``` 运行以下命令启动容器: ```shell docker compose up -d ``` ## 安装 qBittorrent 如果您尚未安装 qBittorrent,请先安装: - [在 Docker 中安装 qBittorrent][qbittorrent-docker] - [在 Windows/macOS 上安装 qBittorrent][qbittorrent-desktop] - [在 Linux 上安装 qBittorrent-nox][qbittorrent-nox] ## 获取聚合 RSS 链接(以 Mikan Project 为例) 访问 [Mikan Project][mikan-project],注册账号并登录,然后点击右下角的 **RSS** 按钮并复制链接。 ![mikan-rss](/image/rss/rss-token.png){data-zoomable} RSS 链接格式如下: ```txt https://mikanani.me/RSS/MyBangumi?token=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx # 或 https://mikanime.tv/RSS/MyBangumi?token=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ``` 详细步骤请参阅 [Mikan RSS 设置][config-rss]。 ## 配置 AutoBangumi 安装 AB 后,WebUI 将自动启动,但主程序处于暂停状态。您可以访问 `http://abhost:7892` 进行配置。 1. 打开网页。默认用户名为 `admin`,默认密码为 `adminadmin`。首次登录后请立即修改。 2. 输入下载器的地址、端口、用户名和密码。 ![ab-webui](/image/config/downloader.png){width=500}{class=ab-shadow-card} 3. 点击**应用**保存配置。AB 将重启,当右上角的圆点变为绿色时,表示 AB 正常运行。 4. 点击右上角的 **+** 按钮,勾选**聚合 RSS**,选择解析器类型,然后输入您的 Mikan RSS 链接。 ![ab-rss](/image/config/add-rss.png){width=500}{class=ab-shadow-card} 等待 AB 解析聚合 RSS。解析完成后,将自动添加番剧并管理下载。 [docker-engine]: https://docs.docker.com/engine/install/ [docker-desktop]: https://www.docker.com/products/docker-desktop [config-rss]: ../config/rss [mikan-project]: https://mikanani.me/ [qbittorrent-docker]: https://hub.docker.com/r/superng6/qbittorrent [qbittorrent-desktop]: https://www.qbittorrent.org/download [qbittorrent-nox]: https://www.qbittorrent.org/download-nox ================================================ FILE: docs/dev/database.md ================================================ # 数据库开发指南 本指南介绍 AutoBangumi 中的数据库架构、模型和操作。 ## 概述 AutoBangumi 使用 **SQLite** 作为数据库,使用 **SQLModel**(Pydantic + SQLAlchemy 混合)作为 ORM。数据库文件位于 `data/data.db`。 ### 架构 ``` module/database/ ├── engine.py # SQLAlchemy 引擎配置 ├── combine.py # Database 类、迁移、会话管理 ├── bangumi.py # 番剧(动画订阅)操作 ├── rss.py # RSS 订阅源操作 ├── torrent.py # 种子跟踪操作 └── user.py # 用户认证操作 ``` ## 核心组件 ### Database 类 `combine.py` 中的 `Database` 类是主入口点。它继承自 SQLModel 的 `Session`,并提供对所有子数据库的访问: ```python from module.database import Database with Database() as db: # 访问子数据库 bangumis = db.bangumi.search_all() rss_items = db.rss.search_active() torrents = db.torrent.search_all() ``` ### 子数据库类 | 类 | 模型 | 用途 | |-------|-------|---------| | `BangumiDatabase` | `Bangumi` | 动画订阅规则 | | `RSSDatabase` | `RSSItem` | RSS 订阅源 | | `TorrentDatabase` | `Torrent` | 已下载种子跟踪 | | `UserDatabase` | `User` | 认证 | ## 模型 ### Bangumi 模型 动画订阅的核心模型: ```python class Bangumi(SQLModel, table=True): id: int # 主键 official_title: str # 显示名称(如"无职转生") title_raw: str # 用于种子匹配的原始标题(有索引) season: int = 1 # 季度编号 episode_offset: int = 0 # 集数编号调整 season_offset: int = 0 # 季度编号调整 rss_link: str # 逗号分隔的 RSS 订阅源 URL filter: str # 排除过滤器(如 "720,\\d+-\\d+") poster_link: str # TMDB 海报 URL save_path: str # 下载目标路径 rule_name: str # qBittorrent RSS 规则名称 added: bool = False # 规则是否已添加到下载器 deleted: bool = False # 软删除标志(有索引) archived: bool = False # 用于已完结系列(有索引) needs_review: bool = False # 检测到偏移不匹配 needs_review_reason: str # 需要审核的原因 suggested_season_offset: int # 建议的季度偏移 suggested_episode_offset: int # 建议的集数偏移 air_weekday: int # 放送日(0=周日,6=周六) ``` ### RSSItem 模型 RSS 订阅源: ```python class RSSItem(SQLModel, table=True): id: int # 主键 name: str # 显示名称 url: str # 订阅源 URL(唯一,有索引) aggregate: bool = True # 是否解析种子 parser: str = "mikan" # 解析器类型:mikan、dmhy、nyaa enabled: bool = True # 启用标志 connection_status: str # "healthy" 或 "error" last_checked_at: str # ISO 时间戳 last_error: str # 最后一次错误消息 ``` ### Torrent 模型 跟踪已下载的种子: ```python class Torrent(SQLModel, table=True): id: int # 主键 name: str # 种子名称(有索引) url: str # 种子/磁力链接 URL(唯一,有索引) rss_id: int # 来源 RSS 订阅源 ID bangumi_id: int # 关联的番剧 ID(可为空) qb_hash: str # qBittorrent 信息哈希(有索引) downloaded: bool = False # 下载完成 ``` ## 常用操作 ### BangumiDatabase ```python with Database() as db: # 创建 db.bangumi.add(bangumi) # 单条插入 db.bangumi.add_all(bangumi_list) # 批量插入(去重) # 读取 db.bangumi.search_all() # 所有记录(缓存,5分钟 TTL) db.bangumi.search_id(123) # 按 ID 查询 db.bangumi.match_torrent("torrent name") # 按 title_raw 匹配查找 db.bangumi.not_complete() # 未完结系列 db.bangumi.get_needs_review() # 标记需要审核的 # 更新 db.bangumi.update(bangumi) # 更新单条记录 db.bangumi.update_all(bangumi_list) # 批量更新 # 删除 db.bangumi.delete_one(123) # 硬删除 db.bangumi.disable_rule(123) # 软删除(deleted=True) ``` ### RSSDatabase ```python with Database() as db: # 创建 db.rss.add(rss_item) # 单条插入 db.rss.add_all(rss_items) # 批量插入(去重) # 读取 db.rss.search_all() # 所有订阅源 db.rss.search_active() # 仅启用的订阅源 db.rss.search_aggregate() # 启用且 aggregate=True # 更新 db.rss.update(id, rss_update) # 部分更新 db.rss.enable(id) # 启用订阅源 db.rss.disable(id) # 禁用订阅源 db.rss.enable_batch([1, 2, 3]) # 批量启用 db.rss.disable_batch([1, 2, 3]) # 批量禁用 ``` ### TorrentDatabase ```python with Database() as db: # 创建 db.torrent.add(torrent) # 单条插入 db.torrent.add_all(torrents) # 批量插入 # 读取 db.torrent.search_all() # 所有种子 db.torrent.search_by_qb_hash(hash) # 按 qBittorrent 哈希查询 db.torrent.search_by_url(url) # 按 URL 查询 db.torrent.check_new(torrents) # 过滤掉已存在的 # 更新 db.torrent.update_qb_hash(id, hash) # 设置 qb_hash ``` ## 缓存 ### 番剧缓存 `search_all()` 的结果在模块级别缓存,TTL 为 5 分钟: ```python # bangumi.py 中的模块级缓存 _bangumi_cache: list[Bangumi] | None = None _bangumi_cache_time: float = 0 _BANGUMI_CACHE_TTL: float = 300.0 # 5 分钟 # 缓存失效 def _invalidate_bangumi_cache(): global _bangumi_cache, _bangumi_cache_time _bangumi_cache = None _bangumi_cache_time = 0 ``` **重要:** 缓存在以下操作时自动失效: - `add()`、`add_all()` - `update()`、`update_all()` - `delete_one()`、`delete_all()` - `archive_one()`、`unarchive_one()` - 任何 RSS 链接更新操作 ### 会话分离 缓存的对象会从会话中**分离**,以防止 `DetachedInstanceError`: ```python for b in bangumis: self.session.expunge(b) # 从会话中分离 ``` ## 迁移系统 ### Schema 版本控制 迁移通过 `schema_version` 表跟踪: ```python CURRENT_SCHEMA_VERSION = 7 # 每个迁移:(版本号, 描述, [SQL 语句]) MIGRATIONS = [ (1, "add air_weekday column", [...]), (2, "add connection status columns", [...]), (3, "create passkey table", [...]), (4, "add archived column", [...]), (5, "rename offset to episode_offset", [...]), (6, "add qb_hash column", [...]), (7, "add suggested offset columns", [...]), ] ``` ### 添加新迁移 1. 在 `combine.py` 中增加 `CURRENT_SCHEMA_VERSION` 2. 在 `MIGRATIONS` 列表中添加迁移元组: ```python MIGRATIONS = [ # ... 现有迁移 ... ( 8, "add my_new_column to bangumi", [ "ALTER TABLE bangumi ADD COLUMN my_new_column TEXT DEFAULT NULL", ], ), ] ``` 3. 在 `run_migrations()` 中添加幂等性检查: ```python if "bangumi" in tables and version == 8: columns = [col["name"] for col in inspector.get_columns("bangumi")] if "my_new_column" in columns: needs_run = False ``` 4. 更新 `module/models/` 中对应的 Pydantic 模型 ### 默认值回填 迁移后,`_fill_null_with_defaults()` 会根据模型默认值自动填充 NULL 值: ```python # 如果模型定义为: class Bangumi(SQLModel, table=True): my_field: bool = False # 那么现有记录中的 NULL 值将被更新为 False ``` ## 性能模式 ### 批量查询 `add_all()` 使用单个查询检查重复项,而不是 N 个查询: ```python # 高效:单个 SELECT keys_to_check = [(d.title_raw, d.group_name) for d in datas] conditions = [ and_(Bangumi.title_raw == tr, Bangumi.group_name == gn) for tr, gn in keys_to_check ] statement = select(Bangumi.title_raw, Bangumi.group_name).where(or_(*conditions)) ``` ### 正则表达式匹配 `match_list()` 为所有标题匹配编译单个正则表达式模式: ```python # 编译一次,匹配多次 sorted_titles = sorted(title_index.keys(), key=len, reverse=True) pattern = "|".join(re.escape(title) for title in sorted_titles) title_regex = re.compile(pattern) # 每个种子 O(1) 查找而不是 O(n) for torrent in torrent_list: match = title_regex.search(torrent.name) ``` ### 索引列 以下列具有索引以实现快速查找: | 表 | 列 | 索引类型 | |-------|--------|------------| | `bangumi` | `title_raw` | 普通 | | `bangumi` | `deleted` | 普通 | | `bangumi` | `archived` | 普通 | | `rssitem` | `url` | 唯一 | | `torrent` | `name` | 普通 | | `torrent` | `url` | 唯一 | | `torrent` | `qb_hash` | 普通 | ## 测试 ### 测试数据库设置 测试使用内存中的 SQLite 数据库: ```python # conftest.py @pytest.fixture def db_engine(): engine = create_engine("sqlite:///:memory:") SQLModel.metadata.create_all(engine) yield engine engine.dispose() @pytest.fixture def db_session(db_engine): with Session(db_engine) as session: yield session ``` ### 工厂函数 使用工厂函数创建测试数据: ```python from test.factories import make_bangumi, make_torrent, make_rss_item def test_bangumi_search(): bangumi = make_bangumi(title_raw="Test Title", season=2) # ... 测试逻辑 ``` ## 设计说明 ### 无外键 SQLite 外键强制默认是禁用的。关系(如 `Torrent.bangumi_id`)在应用程序逻辑中管理,而不是通过数据库约束。 ### 软删除 `Bangumi.deleted` 标志启用软删除。面向用户的数据查询应按 `deleted=False` 过滤: ```python statement = select(Bangumi).where(Bangumi.deleted == false()) ``` ### 种子标记 种子在 qBittorrent 中使用 `ab:{bangumi_id}` 标记,用于重命名操作时的偏移查找。这使得无需数据库查询即可快速识别番剧。 ## 常见问题 ### DetachedInstanceError 如果您从不同的会话访问缓存的对象: ```python # 错误:在新会话中访问缓存的对象 bangumis = db.bangumi.search_all() # 已缓存 with Database() as new_db: new_db.session.add(bangumis[0]) # 错误! # 正确:对象已分离,可独立工作 bangumis = db.bangumi.search_all() bangumis[0].title_raw = "New Title" # 可以,但不会持久化 ``` ### 缓存过期 如果手动 SQL 更新绕过了 ORM,请使缓存失效: ```python from module.database.bangumi import _invalidate_bangumi_cache with engine.connect() as conn: conn.execute(text("UPDATE bangumi SET ...")) conn.commit() _invalidate_bangumi_cache() # 重要! ``` ================================================ FILE: docs/dev/e2e-test-guide.md ================================================ # E2E Integration Test Guide End-to-end tests that exercise the full AutoBangumi workflow against real Docker services (qBittorrent + mock RSS server). ## Prerequisites - **Docker** with `docker compose` (v2) - **uv** for Python dependency management - Ports **7892**, **18080**, **18888** must be free ## Quick Start ```bash # 1. Build the mock RSS server image cd backend/src/test/e2e docker build -f Dockerfile.mock-rss -t ab-mock-rss . # 2. Start test infrastructure docker compose -f docker-compose.test.yml up -d --wait # 3. Verify services are healthy docker compose -f docker-compose.test.yml ps # 4. Run E2E tests cd backend && uv run pytest -m e2e -v --tb=long # 5. Cleanup docker compose -f backend/src/test/e2e/docker-compose.test.yml down -v ``` ## Architecture ``` Host machine ├── pytest (test runner) │ └── Drives HTTP requests to AutoBangumi at localhost:7892 ├── AutoBangumi subprocess │ ├── Isolated config/ and data/ in temp directory │ └── Uses mock downloader (no real qB coupling during setup) ├── qBittorrent container (localhost:18080) │ └── linuxserver/qbittorrent:latest └── Mock RSS server container (localhost:18888) └── Serves static XML fixtures from fixtures/ ``` ## Test Phases | Phase | Tests | What It Validates | |-------|-------|-------------------| | 1. Setup Wizard | `test_01` - `test_06` | First-run detection, mock downloader, setup completion, 403 guard | | 2. Authentication | `test_10` - `test_13` | Login, cookie-based JWT, token refresh, logout | | 3. Configuration | `test_20` - `test_22` | Config CRUD, password masking | | 4. RSS Management | `test_30` - `test_32` | Add, list, delete RSS feeds | | 5. Program Lifecycle | `test_40` - `test_41` | Status check, restart | | 6. Downloader | `test_50` - `test_51` | Mock downloader health, direct qB connectivity | | 7. Cleanup | `test_90` | Logout | ## Key Design Decisions ### Mock Downloader for Setup The setup wizard's `_validate_url()` blocks private/loopback IPs (SSRF protection). Since the Docker qBittorrent instance is on `localhost`, the setup wizard's "test downloader" endpoint would reject it. Instead: 1. Setup uses `downloader_type: "mock"` (bypasses URL validation) 2. Config can be updated to point to real qBittorrent after auth 3. Direct qBittorrent connectivity is tested independently (`test_51`) ### DEV_VERSION Auth Bypass When running from source, `VERSION == "DEV_VERSION"` which bypasses JWT validation (`get_current_user` returns `"dev_user"` unconditionally). Tests document this behavior: login/refresh/logout endpoints still work, but unauthenticated access is also allowed. In production builds, test_13 would expect HTTP 401. ### CWD-Based Isolation AutoBangumi resolves all paths relative to the working directory: - `config/` - config files, JWT secret, setup sentinel - `data/` - SQLite database, posters, logs The `ab_process` fixture creates a temp directory with these subdirs and runs `main.py` from there, ensuring complete isolation from any existing installation. ### qBittorrent Password Extraction Recent `linuxserver/qbittorrent` images generate a random temporary password on first start. The `qb_password` fixture polls `docker logs` until it finds the line: ``` A temporary password is provided for this session: XXXXXXXX ``` ## Debugging Failures ### AutoBangumi won't start ```bash # Check if port 7892 is in use lsof -i :7892 # Run manually to see startup logs cd /tmp/test-workdir && uv run python /path/to/backend/src/main.py ``` ### qBittorrent issues ```bash docker logs ab-test-qbittorrent docker exec ab-test-qbittorrent curl -s http://localhost:18080 ``` ### Mock RSS server issues ```bash docker logs ab-test-mock-rss curl http://localhost:18888/health curl http://localhost:18888/rss/mikan.xml ``` ### Test infrastructure stuck ```bash # Force cleanup docker compose -f backend/src/test/e2e/docker-compose.test.yml down -v --remove-orphans ``` ## Adding New Test Scenarios 1. Add new test methods to `TestE2EWorkflow` in definition order 2. Use `api_client` for HTTP requests (cookies persist across tests) 3. Use `e2e_state` dict to share data between tests 4. For new RSS fixtures, add XML files to `fixtures/` directory 5. Keep test names ordered: `test_XX_description` where XX reflects the phase ### Adding a new fixture feed 1. Create `backend/src/test/e2e/fixtures/your_feed.xml` 2. Access via `http://localhost:18888/rss/your_feed.xml` 3. Rebuild the mock RSS image: `docker compose ... build mock-rss` ================================================ FILE: docs/dev/index.md ================================================ # 贡献指南 我们欢迎贡献者帮助改进 AutoBangumi,更好地解决用户遇到的问题。 本指南将引导您了解如何向 AutoBangumi 贡献代码。请在提交 Pull Request 之前花几分钟阅读。 本文涵盖: - [项目路线图](#项目路线图) - [征求意见稿 (RFC)](#征求意见稿-rfc) - [Git 分支管理](#git-分支管理) - [版本号规则](#版本号规则) - [分支开发,主干发布](#分支开发主干发布) - [分支生命周期](#分支生命周期) - [Git 工作流程概述](#git-工作流程概述) - [Pull Request](#pull-request) - [发布流程](#发布流程) ## 项目路线图 AutoBangumi 开发团队使用 [GitHub Project](https://github.com/EstrellaXD/Auto_Bangumi/projects?query=is%3Aopen) 看板来管理计划中的开发、正在进行的修复及其进度。 这可以帮助您了解: - 开发团队正在做什么 - 哪些内容与您想要的贡献一致,以便您直接参与 - 哪些工作已经在进行中,避免重复工作 在 [Project](https://github.com/EstrellaXD/Auto_Bangumi/projects?query=is%3Aopen) 中,除了常见的 `[Feature Request]`、`[BUG]` 和小改进外,您还会看到 **`[RFC]`** 条目。 ### 征求意见稿 (RFC) > 通过 issues 中的 `RFC` 标签查找现有的 [AutoBangumi RFC](https://github.com/EstrellaXD/Auto_Bangumi/issues?q=is%3Aissue+label%3ARFC)。 对于小改进或 bug 修复,可以直接调整代码并提交 Pull Request。只需阅读[分支管理](#git-分支管理)部分以确保基于正确的分支进行工作,以及 [Pull Request](#pull-request) 部分了解 PR 如何被合并。
对于涉及范围较广的**较大**功能重构,请首先通过 [Issue: Feature Proposal](https://github.com/EstrellaXD/Auto_Bangumi/issues/new?assignees=&labels=RFC&projects=&template=rfc.yml&title=%5BRFC%5D%3A+) 编写 RFC 提案,简要描述您的方法并寻求开发者讨论和共识。 某些提案可能与开发团队已经做出的决定冲突,此步骤有助于避免浪费精力。 > 如果您只是想讨论是否添加或改进某个功能(而不是"如何实现"),请使用 -> [Issue: Feature Request](https://github.com/EstrellaXD/Auto_Bangumi/issues/new?labels=feature+request&template=feature_request.yml&title=%5BFeature+Request%5D+)
[RFC 提案](https://github.com/EstrellaXD/Auto_Bangumi/issues?q=is%3Aissue+is%3Aopen+label%3ARFC)是**"在功能/重构的具体开发之前,供开发者审查技术设计/方法的文档"**。 其目的是确保协作的开发者清楚地知道"要做什么"和"如何完成",所有开发者都可以参与公开讨论。 这有助于评估影响(被忽略的考虑因素、向后兼容性、与现有功能的冲突)。 因此,提案重点描述解决问题的**方法、设计和步骤**。 ## Git 分支管理 ### 版本号规则 AutoBangumi 项目中的 Git 分支与发布版本规则密切相关。 AutoBangumi 遵循[语义化版本控制 (SemVer)](https://semver.org/),使用 `..` 格式: - **Major**:主版本更新,可能包含不兼容的配置/API 变更 - **Minor**:向后兼容的新功能 - **Patch**:向后兼容的 bug 修复/小改进 ### 分支开发,主干发布 AutoBangumi 使用"分支开发,主干发布"模式。 [**`main`**](https://github.com/EstrellaXD/Auto_Bangumi/commits/main) 是稳定的**主干分支**,仅用于发布,不直接用于开发。 每个 Minor 版本都有对应的**开发分支**,用于新功能和发布后的维护。 开发分支命名为 `.-dev`,例如 `3.1-dev`、`3.0-dev`、`2.6-dev`。在[所有分支](https://github.com/EstrellaXD/Auto_Bangumi/branches/all?query=-dev)中查找它们。 ### 分支生命周期 当一个 Minor 开发分支(如 `3.1-dev`)完成功能开发并**首次**合并到 main 时: - 发布 Minor 版本(如 `3.1.0`) - 创建**下一个** Minor 开发分支(`3.2-dev`)用于下个版本的功能 - **上一个**版本的分支(`3.0-dev`)将被归档 - 当前 Minor 分支(`3.1-dev`)进入维护期——不再添加新功能/重构,只修复 bug - Bug 修复先合并到维护分支,然后合并到 main 进行 `Patch` 发布 对于选择 Git 分支的贡献者: - **Bug 修复** — 基于**当前已发布版本**的 Minor 分支,向该分支提交 PR - **新功能/重构** — 基于**下一个未发布版本**的 Minor 分支,向该分支提交 PR > "当前已发布版本"是 [[Releases 页面]](https://github.com/EstrellaXD/Auto_Bangumi/releases) 上的最新版本 ### Git 工作流程概述 > 提交时间线从左到右 ---> ![dev-branch](/image/dev/branch.png) ## Pull Request 请确保按照上述 Git 分支管理部分选择正确的 PR 目标分支: > - **Bug 修复** → 向**当前已发布版本**的 Minor 维护分支提交 PR > - **新功能/重构** → 向**下一版本**的 Minor 开发分支提交 PR
- 一个 PR 应该对应单一关注点,不要引入无关的更改。 将不同的关注点拆分为多个 PR,以帮助团队在每次审查中专注于一个问题。 - 在 PR 标题和描述中,简要说明更改内容,包括原因和意图。 在 PR 描述中链接相关的 issues 或 RFC。 这有助于团队在代码审查时快速了解上下文。 - 确保勾选"允许维护者编辑"。这允许直接进行小的编辑/重构,节省时间。 - 确保本地测试和代码检查通过。这些也会在 PR CI 中检查。 - 对于 bug 修复和新功能,团队可能会要求相应的单元测试覆盖。 开发团队将尽快审查贡献者的 PR,进行讨论或批准合并。 ## 发布流程 目前,发布是在开发团队手动合并特定的"发布 PR"后自动触发的。 Bug 修复 PR 通常会快速发布,一般在一周内。 新功能发布需要更长时间,时间不太可预测。查看 [GitHub Project](https://github.com/EstrellaXD/Auto_Bangumi/projects?query=is%3Aopen) 看板了解开发进度——当所有计划功能完成时发布版本。 ================================================ FILE: docs/en/api/index.md ================================================ # REST API Reference AutoBangumi exposes a REST API at `/api/v1`. All endpoints (except login and setup) require JWT authentication. **Base URL:** `http://your-host:7892/api/v1` **Authentication:** Include the JWT token as a cookie or `Authorization: Bearer ` header. **Interactive Docs:** When running in development mode, Swagger UI is available at `http://your-host:7892/docs`. --- ## Authentication ### Login ``` POST /auth/login ``` Authenticate with username and password. **Request Body:** ```json { "username": "string", "password": "string" } ``` **Response:** Sets authentication cookie with JWT token. ### Refresh Token ``` GET /auth/refresh_token ``` Refresh the current authentication token. ### Logout ``` GET /auth/logout ``` Clear authentication cookies and log out. ### Update Credentials ``` POST /auth/update ``` Update username and/or password. **Request Body:** ```json { "username": "string", "password": "string" } ``` --- ## Passkey / WebAuthn Passwordless authentication using WebAuthn/FIDO2 Passkeys. ### Register Passkey ``` POST /passkey/register/options ``` Get WebAuthn registration options (challenge, relying party info). ``` POST /passkey/register/verify ``` Verify and save the Passkey registration response from the browser. ### Authenticate with Passkey ``` POST /passkey/auth/options ``` Get WebAuthn authentication challenge options. ``` POST /passkey/auth/verify ``` Verify the Passkey authentication response and issue a JWT token. ### Manage Passkeys ``` GET /passkey/list ``` List all registered Passkeys for the current user. ``` POST /passkey/delete ``` Delete a registered Passkey by credential ID. --- ## Configuration ### Get Configuration ``` GET /config/get ``` Retrieve the current application configuration. **Response:** Full configuration object including `program`, `downloader`, `rss_parser`, `bangumi_manager`, `notification`, `proxy`, and `experimental_openai` sections. ### Update Configuration ``` PATCH /config/update ``` Partially update the application configuration. Only include fields you want to change. **Request Body:** Partial configuration object. --- ## Bangumi (Anime Rules) ### List All Bangumi ``` GET /bangumi/get/all ``` Get all anime download rules. ### Get Bangumi by ID ``` GET /bangumi/get/{bangumi_id} ``` Get a specific anime rule by ID. ### Update Bangumi ``` PATCH /bangumi/update/{bangumi_id} ``` Update an anime rule's metadata (title, season, episode offset, etc.). ### Delete Bangumi ``` DELETE /bangumi/delete/{bangumi_id} ``` Delete a single anime rule and its associated torrents. ``` DELETE /bangumi/delete/many/ ``` Batch delete multiple anime rules. **Request Body:** ```json { "bangumi_ids": [1, 2, 3] } ``` ### Disable / Enable Bangumi ``` DELETE /bangumi/disable/{bangumi_id} ``` Disable an anime rule (keeps files, stops downloading). ``` DELETE /bangumi/disable/many/ ``` Batch disable multiple anime rules. ``` GET /bangumi/enable/{bangumi_id} ``` Re-enable a previously disabled anime rule. ### Poster Refresh ``` GET /bangumi/refresh/poster/all ``` Refresh poster images for all anime from TMDB. ``` GET /bangumi/refresh/poster/{bangumi_id} ``` Refresh the poster image for a specific anime. ### Calendar ``` GET /bangumi/refresh/calendar ``` Refresh the anime broadcast calendar data from Bangumi.tv. ### Reset All ``` GET /bangumi/reset/all ``` Delete all anime rules. Use with caution. --- ## RSS Feeds ### List All Feeds ``` GET /rss ``` Get all configured RSS feeds. ### Add Feed ``` POST /rss/add ``` Add a new RSS feed subscription. **Request Body:** ```json { "url": "string", "aggregate": true, "parser": "mikan" } ``` ### Enable / Disable Feeds ``` POST /rss/enable/many ``` Enable multiple RSS feeds. ``` PATCH /rss/disable/{rss_id} ``` Disable a single RSS feed. ``` POST /rss/disable/many ``` Batch disable multiple RSS feeds. ### Delete Feeds ``` DELETE /rss/delete/{rss_id} ``` Delete a single RSS feed. ``` POST /rss/delete/many ``` Batch delete multiple RSS feeds. ### Update Feed ``` PATCH /rss/update/{rss_id} ``` Update an RSS feed's configuration. ### Refresh Feeds ``` GET /rss/refresh/all ``` Manually trigger a refresh of all RSS feeds. ``` GET /rss/refresh/{rss_id} ``` Refresh a specific RSS feed. ### Get Torrents from Feed ``` GET /rss/torrent/{rss_id} ``` Get the list of torrents parsed from a specific RSS feed. ### Analysis & Subscription ``` POST /rss/analysis ``` Analyze an RSS URL and extract anime metadata without subscribing. **Request Body:** ```json { "url": "string" } ``` ``` POST /rss/collect ``` Download all episodes from an RSS feed (for completed anime). ``` POST /rss/subscribe ``` Subscribe to an RSS feed for automatic ongoing downloads. --- ## Search ### Search Bangumi (Server-Sent Events) ``` GET /search/bangumi?keyword={keyword}&provider={provider} ``` Search for anime torrents. Returns results as a Server-Sent Events (SSE) stream for real-time updates. **Query Parameters:** - `keyword` — Search keyword - `provider` — Search provider (e.g., `mikan`, `nyaa`, `dmhy`) **Response:** SSE stream with parsed search results. ### List Search Providers ``` GET /search/provider ``` Get the list of available search providers. --- ## Program Control ### Get Status ``` GET /status ``` Get program status including version, running state, and first_run flag. **Response:** ```json { "status": "running", "version": "3.2.0", "first_run": false } ``` ### Start Program ``` GET /start ``` Start the main program (RSS checking, downloading, renaming). ### Restart Program ``` GET /restart ``` Restart the main program. ### Stop Program ``` GET /stop ``` Stop the main program (WebUI remains accessible). ### Shutdown ``` GET /shutdown ``` Shutdown the entire application (restarts the Docker container). ### Check Downloader ``` GET /check/downloader ``` Test connectivity to the configured downloader (qBittorrent). --- ## Downloader Management Manage torrents in the downloader directly from AutoBangumi. ### List Torrents ``` GET /downloader/torrents ``` Get all torrents in the Bangumi category. ### Pause Torrents ``` POST /downloader/torrents/pause ``` Pause torrents by hash. **Request Body:** ```json { "hashes": ["hash1", "hash2"] } ``` ### Resume Torrents ``` POST /downloader/torrents/resume ``` Resume paused torrents by hash. **Request Body:** ```json { "hashes": ["hash1", "hash2"] } ``` ### Delete Torrents ``` POST /downloader/torrents/delete ``` Delete torrents with optional file deletion. **Request Body:** ```json { "hashes": ["hash1", "hash2"], "delete_files": false } ``` --- ## Setup Wizard These endpoints are only available during first-run setup (before setup is complete). They do **not** require authentication. After setup completes, all endpoints return `403 Forbidden`. ### Check Setup Status ``` GET /setup/status ``` Check if setup wizard is needed (first run). **Response:** ```json { "need_setup": true } ``` ### Test Downloader Connection ``` POST /setup/test-downloader ``` Test connection to a downloader with provided credentials. **Request Body:** ```json { "type": "qbittorrent", "host": "172.17.0.1:8080", "username": "admin", "password": "adminadmin", "ssl": false } ``` ### Test RSS Feed ``` POST /setup/test-rss ``` Validate an RSS feed URL is accessible and parseable. **Request Body:** ```json { "url": "https://mikanime.tv/RSS/MyBangumi?token=xxx" } ``` ### Test Notification ``` POST /setup/test-notification ``` Send a test notification with provided settings. **Request Body:** ```json { "type": "telegram", "token": "bot_token", "chat_id": "chat_id" } ``` ### Complete Setup ``` POST /setup/complete ``` Save all configuration and mark setup as complete. Creates the sentinel file `config/.setup_complete`. **Request Body:** Full configuration object. --- ## Logs ### Get Logs ``` GET /log ``` Retrieve the full application log file. ### Clear Logs ``` GET /log/clear ``` Clear the log file. --- ## Response Format All API responses follow a consistent format: ```json { "msg_en": "Success message in English", "msg_zh": "Success message in Chinese", "status": true } ``` Error responses include appropriate HTTP status codes (400, 401, 403, 404, 500) with error messages in both languages. ================================================ FILE: docs/en/changelog/2.6.md ================================================ # [2.6] Release Notes ## Upgrade Notes from Older Versions Starting from version 2.6, AutoBangumi (AB) configuration has moved from environment variables to `config.json`. Note the following before upgrading. ### Environment Variable Migration Old environment variables are automatically converted to `config.json` on the first startup after upgrading to 2.6. The generated `config.json` is placed in the `/app/config` folder. Once you've mapped the `/app/config` folder, old environment variables no longer affect AB's operation. You can delete `config.json` to regenerate from environment variables. ### Container Volume Mapping After version 2.6, the following folders need to be mapped: - `/app/config`: Configuration folder containing `config.json` - `/app/data`: Data folder containing `bangumi.json`, etc. ### Data Files Due to major updates, we don't recommend using old data files. AB will automatically generate a new `bangumi.json` in `/app/data`. Don't worry — QB won't re-download previously downloaded anime. ### Subsequent Configuration Changes AB can now edit configuration directly in the WebUI. After editing, restart the container for changes to take effect. ## How to Upgrade ### Docker Compose You can use your existing docker-compose.yml file to upgrade: ```bash docker compose stop autobangumi docker compose pull autobangumi ``` Then modify docker-compose.yml to add volume mappings: ```yaml version: "3.8" services: autobangumi: image: estrellaxd/auto_bangumi:latest container_name: autobangumi restart: unless-stopped environment: - PUID=1000 - PGID=1000 - TZ=Asia/Shanghai volumes: - /path/to/config:/app/config - /path/to/data:/app/data networks: - bridge dns: - 8.8.8.8 ``` Then start AB: ```bash docker compose up -d autobangumi ``` ### Portainer In Portainer, modify the volume mappings and click `Recreate` to complete the upgrade. ### What to do if the upgrade causes issues Since configurations may vary, upgrades might cause the program to fail. Delete all previous data and generated configuration files, then restart the container and reconfigure in the WebUI. ## New Features ### Configuration Method Change After v2.6, program configuration has moved from Docker environment variables to `config.json`. The new WebUI also provides a web-based configuration editor. Access the AB URL and find `Settings` in the sidebar to modify configuration. Restart the container after editing. ### Custom Reverse Proxy URL and AB as Proxy Relay To handle situations where [Mikan Project](https://mikanani.me) is inaccessible, AB provides three approaches: 1. HTTP and SOCKS Proxy This feature existed in older versions. After upgrading to 2.6, just check the proxy configuration in the WebUI to access Mikan Project normally. However, qBittorrent still can't access Mikan's RSS and torrent URLs directly, so you need to add a proxy in qBittorrent as well. See #198 for details. 2. Custom Reverse Proxy URL Version 2.6 added a `custom_url` option for custom reverse proxy URLs. Set it to your properly configured reverse proxy URL. AB will use this custom URL to access Mikan Project, and QB can download normally. 3. AB as Proxy Relay After configuring a proxy in AB, AB can serve as a local proxy relay (currently only for RSS-related functions). Set `custom_url` to `http://abhost:abport` where `abhost` is AB's IP and `abport` is AB's port. AB will push its own address to qBittorrent, which will use AB as a proxy to access Mikan Project. Note: If you haven't set up a reverse proxy for AB with Nginx or similar, include `http://` to ensure proper operation. **Important Notes** If AB and QB are in the same container, don't use `127.0.0.1` or `localhost` as they can't communicate this way. If on the same network, use container name addressing, e.g., `http://autobangumi:7892`. You can also use the Docker gateway address, e.g., `http://172.17.0.1:7892`. If on different hosts, use the host machine's IP address. ### Collection and Folder Renaming AB can now rename files within collections and folders, moving media files back to the root directory. Note that AB still relies on the save path to determine season and episode information, so place collection files according to AB's standard. After version **2.6.4**, AB can rename subtitles within folders (feature still being refined). Collections and subtitles default to `pn` format renaming; adjustment options are not yet available. **Standard Path** ``` /downloads/Bangumi/Title/Season 1/xxx ``` ### Push Notifications AB can now send rename completion notifications via `Telegram` and `ServerChan`. In the WebUI, enable push notifications and fill in the required parameters. - Telegram requires Bot Token and Chat ID. Refer to various tutorials for obtaining these. - ServerChan requires a Token. Refer to various tutorials for obtaining this. ================================================ FILE: docs/en/changelog/3.0.md ================================================ # [3.0] Release Notes ### New WebUI - Login functionality — AB now supports username/password authentication. Some operations require login. - New poster wall - Bangumi management features - Edit anime season info and names. Changes automatically update **download rules** / **downloaded file paths** and trigger renaming. - New link parser — after parsing a link, you can manually adjust download info, select download season, or add automatic download rules. - Delete anime — one-click deletion of anime and its torrent files. - Custom download rules per anime, independent of global rules. - New configuration interface for easier application rule configuration - Added initialization page for first-time startup guidance - Downloader connection checker for qBittorrent connectivity - RSS URL validator to check if RSS feeds are valid - Added program management buttons for starting/stopping the program and restarting the container from the WebUI ### Parser - New parser with support for different source types to obtain official titles and poster URLs - Supports changing RSS subscription sources without regenerating the database ### Notification Module - Added `Bark` notification module - New notification format — can now push posters, anime names, and updated episode numbers to Telegram ### Data Migration - Automatic data migration when upgrading from older versions - Migrated data also automatically matches posters ## Fixes - Fixed renaming bugs that could occur with Windows paths ## Changes - Migrated from `json` to `sqlite` for data storage - Migrated from multiprocessing to multithreading - Refactored main program - Improved startup/shutdown time - Refactored parser module - Refactored renaming module - Temporarily removed `normal` mode - Added `ghcr.io` image registry ================================================ FILE: docs/en/changelog/3.1.md ================================================ # [3.1] - 2023-08 - Merged backend and frontend repositories, optimized project directory structure - Optimized version release workflow - Wiki migrated to VitePress at: https://autobangumi.org ## Backend ### Features - Added `RSS Engine` module — AB can now independently update and manage RSS subscriptions and send torrents to the downloader - Supports multiple aggregated RSS subscription sources, managed via the RSS Engine module - Download deduplication — duplicate subscribed torrents won't be re-downloaded - Added manual refresh API for RSS subscriptions - Added RSS subscription management API - Added `Search Engine` module — search torrents by keyword and parse results into collection or subscription tasks - Plugin-based search engine with support for `mikan`, `dmhy`, and `nyaa` - Added subtitle group-specific rules for individual group configurations - Added IPv6 listening support (set `IPV6=1` in environment variables) - Added batch operations API for bulk rule and RSS subscription management ### Changes - Database structure changed to `sqlmodel` for database management - Added version management for seamless software data updates - Unified API format - Added API response language options - Added database mock tests - Code optimizations ### Bugfixes - Fixed various minor issues - Introduced some major issues ## Frontend ### Features - Added `i18n` support — currently supports `zh-CN` and `en-US` - Added PWA support - Added RSS management page - Added search top bar ### Changes - Adjusted various UI details ================================================ FILE: docs/en/changelog/3.2.md ================================================ # [3.2] - 2025-01 ## Backend ### Features - Added WebAuthn Passkey passwordless login support - Register, authenticate, and manage Passkey credentials - Multi-device credential backup detection (iCloud Keychain, etc.) - Clone attack protection (sign_count verification) - Authentication strategy pattern unifying password and Passkey login interfaces - Usernameless login support via discoverable credentials (resident keys) - Added season/episode offset auto-detection - Analyzes TMDB episode air dates to detect "virtual seasons" (e.g., Frieren S1 split into two parts) - Auto-identifies different parts when broadcast gap exceeds 6 months - Calculates episode offset (e.g., RSS shows S2E1 → TMDB S1E29) - Background scan thread automatically detects offset issues in existing subscriptions - New API endpoints: `POST /bangumi/detect-offset`, `PATCH /bangumi/dismiss-review/{id}` - Added bangumi archive functionality - Manual archive/unarchive support - Auto-archive completed series - New API endpoints: `PATCH /bangumi/archive/{id}`, `PATCH /bangumi/unarchive/{id}`, `GET /bangumi/refresh/metadata` - Added search provider configuration API - `GET /search/provider/config` - Get search provider config - `PUT /search/provider/config` - Update search provider config - Added RSS connection status tracking - Records `connection_status` (healthy/error), `last_checked_at`, and `last_error` after each refresh - Added first-run setup wizard - 7-step guided configuration: account, downloader, RSS source, media path, notifications - Downloader connection test, RSS source validation - Optional steps can be skipped and configured later in settings - Sentinel file mechanism (`config/.setup_complete`) prevents re-triggering - Unauthenticated setup API (only available on first run, returns 403 after completion) - Added calendar view with Bangumi.tv broadcast schedule integration - Added downloader API and management interface - Full async migration - Database layer async support (aiosqlite) for non-blocking I/O in Passkey operations - `UserDatabase` supports both sync/async modes for backward compatibility - `Database` context manager supports both `with` (sync) and `async with` (async) - RSS engine, downloader, checker, and parser fully converted to async - Network requests migrated from `requests` to `httpx` (AsyncClient) - Backend migrated to `uv` package manager (pyproject.toml + uv.lock) - Server startup uses background tasks to avoid blocking (fixes #891, #929) - Database migration auto-fills NULL values with model defaults - Database adds `needs_review` and `needs_review_reason` fields for offset detection ### Performance - Shared HTTP client connection pool, reuses TCP/SSL connections - RSS refresh now concurrent (`asyncio.gather`), ~10x faster with multiple sources - Torrent file download now concurrent, ~5x faster for multiple torrents - Rename module concurrent file list fetching, ~20x faster - Notification sending now concurrent, removed 2-second hardcoded delay - Added TMDB and Mikan parser result caching to avoid duplicate API calls - Database indexes added for `Torrent.url`, `Torrent.rss_id`, `Bangumi.title_raw`, `Bangumi.deleted`, `RSSItem.url` - RSS batch enable/disable uses single transaction instead of per-item commits - Pre-compiled regex patterns for torrent name parsing and filter matching - `SeasonCollector` created outside loops, reuses single authentication - RSS parsing deduplication changed from O(n²) list lookup to O(1) set lookup - `Episode`/`SeasonInfo` dataclasses use `__slots__` for reduced memory footprint ### Changes - Upgraded WebAuthn dependency to py_webauthn 2.7.0 - `_get_webauthn_from_request` prioritizes browser Origin header, fixing verification issues in cross-port development environments - `auth_user` and `update_user_info` converted to async functions - `TitleParser.tmdb_parser` converted to async function - `RSSEngine` methods fully async (`pull_rss`, `refresh_rss`, `download_bangumi`, `add_rss`) - `Checker.check_downloader` converted to async function - `ProgramStatus` migrated from threading to asyncio (Event, Lock) ### Bugfixes - Fixed downloader connection check with max retry limit - Fixed transient network errors when adding torrents with retry logic - Fixed multiple issues in search and subscription flow - Improved torrent fetch reliability and error handling - Fixed `aaguid` type error (now `str` in py_webauthn 2.7.0, no longer `bytes`) - Fixed missing `credential_backup_eligible` field (replaced with `credential_device_type`) - Fixed `verify_authentication_response` receiving invalid `credential_id` parameter causing TypeError - Fixed program startup blocking the server (fixes #891, #929, #886, #917, #946) - Fixed search interface export not matching component expectations - Fixed poster endpoint path check incorrectly intercepting all requests (fixes #933, #934) - Fixed OpenAI parser security issue - Fixed database tests using async sessions with sync code mismatch - Fixed config field conflicts when upgrading from 3.1.x to 3.2 causing settings loss (fixes #956) - `program.sleep_time` / `program.times` auto-migrated to `rss_time` / `rename_time` - Removed deprecated `rss_parser` fields (`type`, `custom_url`, `token`, `enable_tmdb`) - Fixed `ENV_TO_ATTR` environment variable mapping pointing to non-existent model fields - Fixed `DEFAULT_SETTINGS` inconsistency with current config model - Fixed version upgrade migration logic errors (all upgrades calling 3.0→3.1 migration) - Added version-aware migration dispatch based on source version - Added `from_31_to_32()` migration function for database schema changes ## Frontend ### Features - Complete UI design system redesign - Unified design tokens (colors, fonts, spacing, shadows, animations) - Light/dark theme toggle support - Comprehensive accessibility support (ARIA, keyboard navigation, focus management) - Responsive layout for mobile devices - Added first-run setup wizard page - Multi-step wizard component (progress bar + step navigation) - Route guard auto-detection and redirect to setup page - Downloader/RSS/notification connection test feedback - Chinese and English i18n support - Added Passkey management panel (settings page) - WebAuthn browser support detection - Automatic device name identification - Passkey list display and deletion - Added Passkey fingerprint login button on login page (supports usernameless login) - Added calendar view page - Added downloader management page - Added Bangumi card hover overlay (showing title and tags) - Added `resolvePosterUrl` utility function for unified external URL and local path handling (fixes #934) - Redesigned search panel with modal and filter system - Redesigned login panel with modern glassmorphism style - Added log level filter in log view - Redesigned LLM settings panel (fixes #938) - Redesigned settings, downloader, player, and log page styles - Added search provider settings panel - View, add, edit, delete search sources in UI - Default sources (mikan, nyaa, dmhy) cannot be deleted - URL template validation ensures `%s` placeholder - Added iOS-style notification badge system - Yellow badge + purple border for subscriptions needing review - Combined display support (e.g., `! | 2` for warning + multiple rules) - Yellow glow animation on cards needing attention - Edit modal warning banner with one-click auto-detect and dismiss - Rule selection modal highlights rules with warnings - Calendar page bangumi grouping: same anime with multiple rules merged, click to select specific rule - Bangumi list page collapsible "Archived" section - Bangumi list page skeleton loading animation - Rule editor episode offset field with "Auto Detect" button - RSS management page connection status labels: green "Connected" when healthy, red "Error" with tooltip for details - New mobile-first responsive design - Three-tier breakpoint system: mobile (<640px), tablet (640-1023px), desktop (≥1024px) - Mobile bottom navigation bar (with icons and text labels) - Tablet mini sidebar (56px icon navigation) - Mobile popups automatically switch to bottom sheets - Pull-to-refresh support - Horizontal swipe container support - Mobile card list replacing data tables (RSS page) - CSS Grid responsive layout (Bangumi card grid) - Form labels stack vertically on mobile, full-width inputs - Touch targets minimum 44px, meeting accessibility standards - Safe area support (notched devices) - `100dvh` dynamic viewport height (fixes mobile browser address bar issue) - `viewport-fit=cover` for full-screen devices ### New Components - `ab-bottom-sheet` — Touch-driven bottom sheet component (drag to close, max height limit) - `ab-adaptive-modal` — Adaptive modal (bottom sheet on mobile / centered dialog on desktop) - `ab-pull-refresh` — Pull-to-refresh wrapper component - `ab-swipe-container` — Horizontal swipe container (CSS scroll-snap) - `ab-data-list` — Mobile-friendly card list (replacing NDataTable) - `ab-mobile-nav` — Enhanced bottom navigation bar (icon + label + active indicator) - `useSafeArea` — Safe area composable ### Performance - Downloader store uses `shallowRef` instead of `ref` to avoid deep reactive proxy on large arrays - Table column definitions moved to `computed` to avoid rebuilding on each render - RSS table columns separated from data, column config not rebuilt on data changes - Calendar page removed duplicate `getAll()` calls - `ab-select` `watchEffect` changed to `watch`, eliminates invalid emit on mount - `useClipboard` hoisted to store top level, avoids creating new instance on each `copy()` - `setInterval` replaced with `useIntervalFn` for automatic lifecycle management ### Changes - Refactored search logic, removed rxjs dependency - Search store export refactored to match component expectations - Upgraded frontend dependencies - Breakpoint system expanded from single 1024px to 640px + 1024px two-tier - `useBreakpointQuery` added `isTablet`, `isMobileOrTablet`, `isTabletOrPC` - `media-query.vue` added `#tablet` slot (falls back to `#mobile`) - UnoCSS added `sm: 640px` breakpoint - `ab-input` mobile full-width + increased touch target styling - Layout uses `dvh` units instead of `vh`, supports safe-area-inset - Fixed calendar page unknown column width - Unified action bar button sizes in downloader page ## CI/Infrastructure - CI added build test on PR open (dev branch PRs to main auto-trigger build) - CI upgraded `actions/upload-artifact` and `actions/download-artifact` to v4 - Docker build removed `linux/arm/v7` platform (uv image doesn't support it) - Added CLAUDE.md development guide ================================================ FILE: docs/en/config/downloader.md ================================================ # Downloader Settings ## WebUI Configuration ![downloader](/image/config/downloader.png){width=500}{class=ab-shadow-card}
- **Downloader Type** is the downloader type. Currently only qBittorrent is supported. - **Host** is the downloader address. [See below](#downloader-address) - **Download path** is the mapped download path for the downloader. [See below](#download-path-issues) - **SSL** enables SSL for the downloader connection. ## Common Issues ### Downloader Address ::: warning Note Do not use 127.0.0.1 or localhost as the downloader address. ::: Since AB runs in Docker with **Bridge** mode in the official tutorial, using 127.0.0.1 or localhost will resolve to AB itself, not the downloader. - If your qBittorrent also runs in Docker, we recommend using the Docker **gateway address: 172.17.0.1**. - If your qBittorrent runs on the host machine, use the host machine's IP address. If you run AB in **Host** mode, you can use 127.0.0.1 instead of the Docker gateway address. ::: warning Note Macvlan isolates container networks. Without additional bridge configuration, containers cannot access other containers or the host itself. ::: ### Download Path Issues The path configured in AB is only used to generate the corresponding anime file path. AB itself does not directly manage files at that path. **What should I put for the download path?** This parameter just needs to match your **downloader's** configuration: - Docker: If qB uses `/downloads`, then set `/downloads/Bangumi`. You can change `Bangumi` to anything. - Linux/macOS: If it's `/home/usr/downloads` or `/User/UserName/Downloads`, just append `/Bangumi` at the end. - Windows: Change `D:\Media\` to `D:\Media\Bangumi` ## `config.json` Configuration Options The corresponding options in the configuration file are: Configuration section: `downloader` | Parameter | Description | Type | WebUI Option | Default | |-----------|---------------------|---------|----------------------|---------------------| | type | Downloader type | String | Downloader type | qbittorrent | | host | Downloader address | String | Downloader address | 172.17.0.1:8080 | | username | Downloader username | String | Downloader username | admin | | password | Downloader password | String | Downloader password | adminadmin | | path | Download path | String | Download path | /downloads/Bangumi | | ssl | Enable SSL | Boolean | Enable SSL | false | ================================================ FILE: docs/en/config/experimental.md ================================================ # Experimental Features ::: warning Experimental features are still in testing. Enabling them may cause unexpected issues and they may be removed in future versions. Use with caution! ::: ## OpenAI ChatGPT Use OpenAI ChatGPT for better structured title parsing. For example: ``` input: "【喵萌奶茶屋】★04月新番★[夏日重现/Summer Time Rendering][11][1080p][繁日双语][招募翻译]" output: '{"group": "喵萌奶茶屋", "title_en": "Summer Time Rendering", "resolution": "1080p", "episode": 11, "season": 1, "title_zh": "夏日重现", "sub": "", "title_jp": "", "season_raw": "", "source": ""}' ``` ![experimental OpenAI](/image/config/experimental-openai.png){width=500}{class=ab-shadow-card} - **Enable OpenAI** enables OpenAI and uses ChatGPT for title parsing. - **OpenAI API Type** defaults to OpenAI. - **OpenAI API Key** is your OpenAI account API key. - **OpenAI API Base URL** is the OpenAI endpoint. Defaults to the official OpenAI URL; you can change it to a compatible third-party endpoint. - **OpenAI Model** is the ChatGPT model parameter. Currently provides `gpt-3.5-turbo`, which is affordable and produces excellent results with the right prompts. ## Microsoft Azure OpenAI ![experimental Microsoft Azure OpenAI](/image/config/experimental-azure-openai.png){width=500}{class=ab-shadow-card} In addition to standard OpenAI, [version 3.1.8](https://github.com/EstrellaXD/Auto_Bangumi/releases/tag/3.1.8) added Microsoft Azure OpenAI support. Usage is similar to standard OpenAI with some shared parameters, but note the following: - **Enable OpenAI** enables OpenAI and uses ChatGPT for title parsing. - **OpenAI API Type** — Select `azure` to show Azure-specific options. - **OpenAI API Key** is your Microsoft Azure OpenAI API key. - **OpenAI API Base URL** corresponds to the Microsoft Azure OpenAI Entrypoint. **Must be filled in manually**. - **Azure OpenAI Version** is the API version. Defaults to `2023-05-15`. See [supported versions](https://learn.microsoft.com/en-us/azure/ai-services/openai/reference#completions). - **Azure OpenAI Deployment ID** is your deployment ID, usually the same as the model name. Note that Azure OpenAI doesn't support symbols other than `_-`, so `gpt-3.5-turbo` becomes `gpt-35-turbo` in Azure. **Must be filled in manually**. Reference documentation: - [Quickstart: Get started using GPT-35-Turbo and GPT-4 with Azure OpenAI Service](https://learn.microsoft.com/en-us/azure/ai-services/openai/chatgpt-quickstart?tabs=command-line&pivots=programming-language-python) - [Learn how to work with the GPT-35-Turbo and GPT-4 models](https://learn.microsoft.com/en-us/azure/ai-services/openai/how-to/chatgpt?pivots=programming-language-chat-completions) ## `config.json` Configuration Options The corresponding options in the configuration file are: Configuration section: `experimental_openai` | Parameter | Description | Type | WebUI Option | Default | |---------------|--------------------------|---------|---------------------------|---------------------------| | enable | Enable OpenAI parser | Boolean | Enable OpenAI | false | | api_type | OpenAI API type | String | API type (`openai`/`azure`) | openai | | api_key | OpenAI API key | String | OpenAI API Key | | | api_base | API Base URL (Azure entrypoint) | String | OpenAI API Base URL | https://api.openai.com/v1 | | model | OpenAI model | String | OpenAI Model | gpt-3.5-turbo | | api_version | Azure OpenAI API version | String | Azure API Version | 2023-05-15 | | deployment_id | Azure Deployment ID | String | Azure Deployment ID | | ================================================ FILE: docs/en/config/manager.md ================================================ # Bangumi Manager Settings ## WebUI Configuration ![proxy](/image/config/manager.png){width=500}{class=ab-shadow-card}
- **Enable** enables the bangumi manager. If disabled, the settings below will not take effect. - **Rename method** is the renaming method. Currently supported: - `pn` — `Torrent Title S0XE0X.mp4` format - `advance` — `Official Title S0XE0X.mp4` format - `none` — No renaming - **Eps complete** enables episode completion for the current season. If enabled, missing episodes will be downloaded. - **Add group tag** adds subtitle group tags to download rules. - **Delete bad torrent** deletes errored torrents. - [About file paths][1] - [About renaming][2] ## `config.json` Configuration Options The corresponding options in the configuration file are: Configuration section: `bangumi_manager` | Parameter | Description | Type | WebUI Option | Default | |--------------------|------------------------------ |---------|-------------------|---------| | enable | Enable bangumi manager | Boolean | Enable manager | true | | eps_complete | Enable episode completion | Boolean | Episode completion | false | | rename_method | Rename method | String | Rename method | pn | | group_tag | Add subtitle group tag | Boolean | Group tag | false | | remove_bad_torrent | Delete bad torrents | Boolean | Remove bad torrent | false | [1]: https://www.autobangumi.org/faq/#download-path [2]: https://www.autobangumi.org/faq/#file-renaming ================================================ FILE: docs/en/config/notifier.md ================================================ # Notification Settings ## WebUI Configuration ![notification](/image/config/notifier.png){width=500}{class=ab-shadow-card}
- **Enable** enables notifications. If disabled, the settings below will not take effect. - **Type** is the notification type. Currently supported: - Telegram - Wecom - Bark - ServerChan - **Chat ID** only needs to be filled in when using `telegram` notifications. [How to get Telegram Bot Chat ID][1] - **Wecom**: Fill in the custom push URL in the Chat ID field, and add [Rich Text Message][2] type on the server side. [Wecom Configuration Guide][3] ## `config.json` Configuration Options The corresponding options in the configuration file are: Configuration section: `notification` | Parameter | Description | Type | WebUI Option | Default | |-----------|------------------|---------|-----------------|----------| | enable | Enable notifications | Boolean | Notifications | false | | type | Notification type | String | Notification type | telegram | | token | Notification token | String | Notification token | | | chat_id | Notification Chat ID | String | Notification Chat ID | | [1]: https://core.telegram.org/bots#6-botfather [2]: https://github.com/umbors/wecomchan-alifun [3]: https://github.com/easychen/wecomchan ================================================ FILE: docs/en/config/parser.md ================================================ # Parser Settings AB's parser is used to parse aggregated RSS links. When new entries appear in the RSS feed, AB will parse the titles and generate automatic download rules. ::: tip Since v3.1, parser settings have moved to individual RSS settings. To configure the **parser type**, see [Setting up parser for RSS][add_rss]. ::: ## Parser Settings in WebUI ![parser](/image/config/parser.png){width=500}{class=ab-shadow-card}
- **Enable**: Whether to enable the RSS parser. - **Language** is the RSS parser language. Currently supports `zh`, `jp`, and `en`. - **Exclude** is the global RSS parser filter. You can enter strings or regular expressions, and AB will filter out matching entries during RSS parsing. ## `config.json` Configuration Options The corresponding options in the configuration file are: Configuration section: `rss_parser` | Parameter | Description | Type | WebUI Option | Default | |-----------|-----------------------|---------|---------------------|----------------| | enable | Enable RSS parser | Boolean | Enable RSS parser | true | | filter | RSS parser filter | Array | Filter | [720,\d+-\d+] | | language | RSS parser language | String | RSS parser language | zh | [rss_token]: rss [add_rss]: /feature/rss#parser-settings [reproxy]: proxy#reverse-proxy ================================================ FILE: docs/en/config/program.md ================================================ # Program Settings ## WebUI Configuration ![program](/image/config/program.png){width=500}{class=ab-shadow-card}
- Interval Time parameters are in seconds. Convert to seconds if you need to set minutes. - RSS is the RSS check interval, which affects how often automatic download rules are generated. - Rename is the rename check interval. Modify this if you need to change how often renaming is checked. - WebUI Port is the port number. Note that if you're using Docker, you need to remap the port in Docker after changing it. ## `config.json` Configuration Options The corresponding options in the configuration file are: Configuration section: `program` | Parameter | Description | Type | WebUI Option | Default | |-------------|---------------------|-----------------|---------------------|---------| | rss_time | RSS check interval | Integer (seconds) | RSS check interval | 7200 | | rename_time | Rename check interval | Integer (seconds) | Rename check interval | 60 | | webui_port | WebUI port | Integer | WebUI port | 7892 | ================================================ FILE: docs/en/config/proxy.md ================================================ # Proxy and Reverse Proxy ## Proxy ![proxy](/image/config/proxy.png){width=500}{class=ab-shadow-card}
AB supports HTTP and SOCKS5 proxies to help resolve network issues. - **Enable**: Whether to enable the proxy. - **Type** is the proxy type. - **Host** is the proxy address. - **Port** is the proxy port. ::: tip In **SOCKS5** mode, username and password are required. ::: ## `config.json` Configuration Options The corresponding options in the configuration file are: Configuration section: `proxy` | Parameter | Description | Type | WebUI Option | Default | |-----------|---------------|---------|---------------|---------| | enable | Enable proxy | Boolean | Proxy | false | | type | Proxy type | String | Proxy type | http | | host | Proxy address | String | Proxy address | | | port | Proxy port | Integer | Proxy port | | | username | Proxy username | String | Proxy username | | | password | Proxy password | String | Proxy password | | ## Reverse Proxy - Use the Mikan Project alternative domain `mikanime.tv` to replace `mikanani.me` in your RSS subscription URL. - Use a Cloudflare Worker as a reverse proxy and replace all `mikanani.me` domains in the RSS feed. ## Cloudflare Workers Based on the approach used to bypass blocks on other services, you can set up a reverse proxy using Cloudflare Workers. How to register a domain and bind it to Cloudflare is beyond the scope of this guide. Add the following code in Workers to use your own domain to access Mikan Project and download torrents from RSS links: ```js const TELEGRAPH_URL = 'https://mikanani.me'; const MY_DOMAIN = 'https://yourdomain.com' addEventListener('fetch', event => { event.respondWith(handleRequest(event.request)) }) async function handleRequest(request) { const url = new URL(request.url); url.host = TELEGRAPH_URL.replace(/^https?:\/\//, ''); const modifiedRequest = new Request(url.toString(), { headers: request.headers, method: request.method, body: request.body, redirect: 'manual' }); const response = await fetch(modifiedRequest); const contentType = response.headers.get('Content-Type') || ''; // Only perform replacement if content type is RSS if (contentType.includes('application/xml')) { const text = await response.text(); const replacedText = text.replace(/https?:\/\/mikanani\.me/g, MY_DOMAIN); const modifiedResponse = new Response(replacedText, response); // Add CORS headers modifiedResponse.headers.set('Access-Control-Allow-Origin', '*'); return modifiedResponse; } else { const modifiedResponse = new Response(response.body, response); // Add CORS headers modifiedResponse.headers.set('Access-Control-Allow-Origin', '*'); return modifiedResponse; } } ``` ================================================ FILE: docs/en/config/rss.md ================================================ # RSS Feed Setup AutoBangumi can automatically parse aggregated anime RSS feeds and generate download rules based on subtitle groups and anime names, enabling fully automatic anime tracking. The following uses [Mikan Project][mikan-site] as an example to explain how to get an RSS subscription URL. Note that the main Mikan Project site may be blocked in some regions. If you cannot access it without a proxy, use the following alternative domain: [Mikan Project (Alternative)][mikan-cn-site] ## Get Subscription URL This project is based on parsing RSS URLs provided by Mikan Project. To enable automatic anime tracking, you need to register and obtain a Mikan Project RSS URL: ![image](/image/rss/rss-token.png){data-zoomable} The RSS URL will look like: ```txt https://mikanani.me/RSS/MyBangumi?token=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx # or https://mikanime.tv/RSS/MyBangumi?token=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ``` ## Mikan Project Subscription Tips Since AutoBangumi parses all RSS entries it receives, keep the following in mind when subscribing: ![image](/image/rss/advanced-subscription.png){data-zoomable} - Enable advanced settings in your profile settings. - Subscribe to only one subtitle group per anime. Click the anime poster on Mikan Project to open the submenu and select a single subtitle group. - If a subtitle group offers both Simplified and Traditional Chinese subtitles, Mikan Project usually provides a way to choose. Select one subtitle type. - If no subtitle type selection is available, you can set up a `filter` in AutoBangumi to filter them, or manually filter in qBittorrent after the rule is generated. - OVA and movie subscriptions are currently not supported for parsing. [mikan-site]: https://mikanani.me/ [mikan-cn-site]: https://mikanime.tv/ ================================================ FILE: docs/en/deploy/docker-cli.md ================================================ # Deploy with Docker CLI ## Note on New Versions Since AutoBangumi 2.6, you can configure everything directly in the WebUI. You can start the container first and then configure it in the WebUI. Environment variable configuration from older versions will be automatically migrated. Environment variables still work but only take effect on the first startup. ## Create Data and Configuration Directories To ensure AB's data and configuration persist across updates, we recommend using Docker volumes or bind mounts. ```shell # Using bind mount mkdir -p ${HOME}/AutoBangumi/{config,data} cd ${HOME}/AutoBangumi ``` Choose either bind mount or Docker volume: ```shell # Using Docker volume docker volume create AutoBangumi_config docker volume create AutoBangumi_data ``` ## Deploy AutoBangumi with Docker CLI Copy and run the following command. Make sure your working directory is AutoBangumi. ```shell docker run -d \ --name=AutoBangumi \ -v ${HOME}/AutoBangumi/config:/app/config \ -v ${HOME}/AutoBangumi/data:/app/data \ -p 7892:7892 \ -e TZ=Asia/Shanghai \ -e PUID=$(id -u) \ -e PGID=$(id -g) \ -e UMASK=022 \ --network=bridge \ --dns=8.8.8.8 \ --restart unless-stopped \ ghcr.io/estrellaxd/auto_bangumi:latest ``` If using Docker volumes, replace the bind paths accordingly: ```shell -v AutoBangumi_config:/app/config \ -v AutoBangumi_data:/app/data \ ``` The AB WebUI will start automatically, but the main program will be paused. Access `http://abhost:7892` to configure it. AB will automatically write environment variables to `config.json` and start running. We recommend using _[Portainer](https://www.portainer.io)_ or similar Docker management UIs for advanced deployment. ================================================ FILE: docs/en/deploy/docker-compose.md ================================================ # Deploy with Docker Compose A one-click deployment method for **AutoBangumi** using a `docker-compose.yml` file. ## Install Docker Compose Docker Compose usually comes bundled with Docker. Check with: ```bash docker compose -v ``` If not installed, install it with: ```bash $ sudo apt-get update $ sudo apt-get install docker-compose-plugin ``` ## Deploy **AutoBangumi** ### Create AutoBangumi and Data Directories ```bash mkdir -p ${HOME}/AutoBangumi/{config,data} cd ${HOME}/AutoBangumi ``` ### Option 1: Custom Docker Compose Configuration ```yaml version: "3.8" services: AutoBangumi: image: "ghcr.io/estrellaxd/auto_bangumi:latest" container_name: AutoBangumi volumes: - ./config:/app/config - ./data:/app/data ports: - "7892:7892" restart: unless-stopped dns: - 8.8.8.8 network_mode: bridge environment: - TZ=Asia/Shanghai - PGID=$(id -g) - PUID=$(id -u) - UMASK=022 ``` Copy the above content into a `docker-compose.yml` file. ### Option 2: Download Docker Compose Configuration File If you don't want to create the `docker-compose.yml` file manually, the project provides pre-made configurations: - Install **AutoBangumi** only: ```bash wget https://raw.githubusercontent.com/EstrellaXD/Auto_Bangumi/main/docs/resource/docker-compose/AutoBangumi/docker-compose.yml ``` - Install **qBittorrent** and **AutoBangumi**: ```bash wget https://raw.githubusercontent.com/EstrellaXD/Auto_Bangumi/main/docs/resource/docker-compose/qBittorrent+AutoBangumi/docker-compose.yml ``` Choose your installation method and run the command to download the `docker-compose.yml` file. You can customize parameters with a text editor if needed. ### Define Environment Variables If you're using the downloaded AB+QB Docker Compose file, you need to define the following environment variables: ```shell export \ QB_PORT= ``` - `QB_PORT`: Enter your existing qBittorrent port or your desired custom port, e.g., `8080` ### Start Docker Compose ```bash docker compose up -d ``` ================================================ FILE: docs/en/deploy/dsm.md ================================================ # Synology NAS (DSM 7.2) Deployment (QNAP Similar) DSM 7.2 supports Docker Compose, so we recommend using Docker Compose for one-click deployment. ## Create Configuration and Data Directories Create an `AutoBangumi` folder under `/volume1/docker/`, then create `config` and `data` subfolders inside it. ## Install Container Manager (Docker) Package Open Package Center and install the Container Manager (Docker) package. ![install-docker](/image/dsm/install-docker.png){data-zoomable} ## Install AB via Docker Compose Click **Project**, then click **Create**, and select **Docker Compose**. ![new-compose](/image/dsm/new-compose.png){data-zoomable} Copy and paste the following content into **Docker Compose**: ```yaml version: "3.4" services: ab: image: "ghcr.io/estrellaxd/auto_bangumi:latest" container_name: "auto_bangumi" restart: unless-stopped ports: - "7892:7892" volumes: - "./config:/app/config" - "./data:/app/data" network_mode: bridge environment: - TZ=Asia/Shanghai - AB_METHOD=Advance - PGID=1000 - PUID=1000 - UMASK=022 ``` Click **Next**, then click **Done**. ![create](/image/dsm/create.png){data-zoomable} After creation, access `http://:7892` to enter AB and configure it. ## Install AB and qBittorrent via Docker Compose When you have both a proxy and IPv6, configuring IPv6 in Docker on Synology NAS can be complex. We recommend installing both AB and qBittorrent on the host network to reduce complexity. The following configuration assumes you have a Clash proxy deployed in Docker that is accessible via a local IP at a specified port. Following the previous section, adjust and paste the following content into **Docker Compose**: ```yaml qbittorrent: container_name: qbittorrent image: linuxserver/qbittorrent hostname: qbittorrent environment: - PGID=1000 # Modify as needed - PUID=1000 # Modify as needed - WEBUI_PORT=8989 - TZ=Asia/Shanghai volumes: - ./qb_config:/config - your_anime_path:/downloads # Change this to your anime storage directory. Set download path in AB as /downloads networks: - host restart: unless-stopped auto_bangumi: container_name: AutoBangumi environment: - TZ=Asia/Shanghai - PGID=1000 # Modify as needed - PUID=1000 # Modify as needed - UMASK=022 - AB_DOWNLOADER_HOST=127.0.0.1:8989 # Modify port as needed volumes: - /volume1/docker/ab/config:/app/config - /volume1/docker/ab/data:/app/data network_mode: host environment: - AB_METHOD=Advance dns: - 8.8.8.8 restart: unless-stopped image: "ghcr.io/estrellaxd/auto_bangumi:latest" depends_on: - qbittorrent ``` ## Additional Notes The PGID and PUID values need to be determined for your system. For newer Synology NAS devices, they are typically: `PUID=1026, PGID=100`. When modifying the qBittorrent port, make sure to update it in all locations. For proxy setup, refer to: [Proxy Settings](../config/proxy) On lower-performance machines, the default configuration may heavily use the CPU, causing AB to fail connecting to qB and the qB WebUI to become inaccessible. For devices like the 220+, recommended qBittorrent settings to reduce CPU usage: - Settings -> Connections -> Connection Limits - Global maximum number of connections: 300 - Maximum number of connections per torrent: 60 - Global upload slots limit: 15 - Upload slots per torrent: 4 - BitTorrent - Maximum active checking torrents: 1 - Torrent Queueing - Maximum active downloads: 3 - Maximum active uploads: 5 - Maximum active torrents: 10 - RSS - RSS Reader - Maximum number of articles per feed: 50 ================================================ FILE: docs/en/deploy/local.md ================================================ # Local Deployment ::: warning Local deployment may cause unexpected issues. We strongly recommend using Docker instead. This documentation may have update delays. If you have questions, please raise them in [Issues](https://github.com/EstrellaXD/Auto_Bangumi/issues). ::: ## Download the Latest Release ```bash VERSION=$(curl -s "https://api.github.com/repos/EstrellaXD/Auto_Bangumi/releases/latest" | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/') curl -L -O "https://github.com/EstrellaXD/Auto_Bangumi/releases/download/$VERSION/app-v$VERSION.zip" ``` ## Extract the Archive On Unix/WSL systems, use the following command. On Windows, extract manually. ```bash unzip app-v$VERSION.zip -d AutoBangumi cd AutoBangumi ``` ## Create Virtual Environment and Install Dependencies Ensure you have Python 3.10+ and pip installed locally. ```bash cd src python3 -m venv env python3 pip install -r requirements.txt ``` ## Create Configuration and Data Directories ```bash mkdir config mkdir data ``` ## Run AutoBangumi ```bash python3 main.py ``` ## Windows Auto-Start on Boot You can use `nssm` for auto-start on boot. Example with `nssm`: ```powershell nssm install AutoBangumi (Get-Command python).Source nssm set AutoBangumi AppParameters (Get-Item .\main.py).FullName nssm set AutoBangumi AppDirectory (Get-Item ..).FullName nssm set AutoBangumi Start SERVICE_DELAYED_AUTO_START ``` ================================================ FILE: docs/en/deploy/quick-start.md ================================================ # Quick Start We recommend deploying AutoBangumi in Docker. Before deployment, make sure you have [Docker Engine][docker-engine] or [Docker Desktop][docker-desktop] installed. ## Create Data and Configuration Directories To ensure AB's data and configuration persist across updates, we recommend using bind mounts or Docker volumes. ```shell # Using bind mount mkdir -p ${HOME}/AutoBangumi/{config,data} cd ${HOME}/AutoBangumi ``` Choose either bind mount or Docker volume: ```shell # Using Docker volume docker volume create AutoBangumi_config docker volume create AutoBangumi_data ``` ## Deploy AutoBangumi with Docker Make sure you are in the AutoBangumi directory when running these commands. ### Option 1: Deploy with Docker CLI Copy and run the following command: ```shell docker run -d \ --name=AutoBangumi \ -v ${HOME}/AutoBangumi/config:/app/config \ -v ${HOME}/AutoBangumi/data:/app/data \ -p 7892:7892 \ -e TZ=Asia/Shanghai \ -e PUID=$(id -u) \ -e PGID=$(id -g) \ -e UMASK=022 \ --network=bridge \ --dns=8.8.8.8 \ --restart unless-stopped \ ghcr.io/estrellaxd/auto_bangumi:latest ``` ### Option 2: Deploy with Docker Compose Copy the following content into a `docker-compose.yml` file: ```yaml version: "3.8" services: AutoBangumi: image: "ghcr.io/estrellaxd/auto_bangumi:latest" container_name: AutoBangumi volumes: - ./config:/app/config - ./data:/app/data ports: - "7892:7892" network_mode: bridge restart: unless-stopped dns: - 8.8.8.8 environment: - TZ=Asia/Shanghai - PGID=$(id -g) - PUID=$(id -u) - UMASK=022 ``` Run the following command to start the container: ```shell docker compose up -d ``` ## Install qBittorrent If you haven't installed qBittorrent, please install it first: - [Install qBittorrent in Docker][qbittorrent-docker] - [Install qBittorrent on Windows/macOS][qbittorrent-desktop] - [Install qBittorrent-nox on Linux][qbittorrent-nox] ## Get an Aggregated RSS Link (Using Mikan Project as an Example) Visit [Mikan Project][mikan-project], register an account and log in, then click the **RSS** button in the bottom right corner and copy the link. ![mikan-rss](/image/rss/rss-token.png){data-zoomable} The RSS URL will look like: ```txt https://mikanani.me/RSS/MyBangumi?token=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx # or https://mikanime.tv/RSS/MyBangumi?token=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ``` For detailed steps, see [Mikan RSS Setup][config-rss]. ## Configure AutoBangumi After installing AB, the WebUI will start automatically, but the main program will be paused. You can access `http://abhost:7892` to configure it. 1. Open the webpage. The default username is `admin` and the default password is `adminadmin`. Change these immediately after first login. 2. Enter your downloader's address, port, username, and password. ![ab-webui](/image/config/downloader.png){width=500}{class=ab-shadow-card} 3. Click **Apply** to save the configuration. AB will restart, and when the dot in the upper right corner turns green, it indicates AB is running normally. 4. Click the **+** button in the upper right corner, check **Aggregated RSS**, select the parser type, and enter your Mikan RSS URL. ![ab-rss](/image/config/add-rss.png){width=500}{class=ab-shadow-card} Wait for AB to parse the aggregated RSS. Once parsing is complete, it will automatically add anime and manage downloads. [docker-engine]: https://docs.docker.com/engine/install/ [docker-desktop]: https://www.docker.com/products/docker-desktop [config-rss]: ../config/rss [mikan-project]: https://mikanani.me/ [qbittorrent-docker]: https://hub.docker.com/r/superng6/qbittorrent [qbittorrent-desktop]: https://www.qbittorrent.org/download [qbittorrent-nox]: https://www.qbittorrent.org/download-nox ================================================ FILE: docs/en/dev/database.md ================================================ # Database Developer Guide This guide covers the database architecture, models, and operations in AutoBangumi. ## Overview AutoBangumi uses **SQLite** as its database with **SQLModel** (Pydantic + SQLAlchemy hybrid) for ORM. The database file is located at `data/data.db`. ### Architecture ``` module/database/ ├── engine.py # SQLAlchemy engine configuration ├── combine.py # Database class, migrations, session management ├── bangumi.py # Bangumi (anime subscription) operations ├── rss.py # RSS feed operations ├── torrent.py # Torrent tracking operations └── user.py # User authentication operations ``` ## Core Components ### Database Class The `Database` class in `combine.py` is the main entry point. It inherits from SQLModel's `Session` and provides access to all sub-databases: ```python from module.database import Database with Database() as db: # Access sub-databases bangumis = db.bangumi.search_all() rss_items = db.rss.search_active() torrents = db.torrent.search_all() ``` ### Sub-Database Classes | Class | Model | Purpose | |-------|-------|---------| | `BangumiDatabase` | `Bangumi` | Anime subscription rules | | `RSSDatabase` | `RSSItem` | RSS feed sources | | `TorrentDatabase` | `Torrent` | Downloaded torrent tracking | | `UserDatabase` | `User` | Authentication | ## Models ### Bangumi Model Core model for anime subscriptions: ```python class Bangumi(SQLModel, table=True): id: int # Primary key official_title: str # Display name (e.g., "Mushoku Tensei") title_raw: str # Raw title for torrent matching (indexed) season: int = 1 # Season number episode_offset: int = 0 # Episode numbering adjustment season_offset: int = 0 # Season numbering adjustment rss_link: str # Comma-separated RSS feed URLs filter: str # Exclusion filter (e.g., "720,\\d+-\\d+") poster_link: str # TMDB poster URL save_path: str # Download destination path rule_name: str # qBittorrent RSS rule name added: bool = False # Whether rule is added to downloader deleted: bool = False # Soft delete flag (indexed) archived: bool = False # For completed series (indexed) needs_review: bool = False # Offset mismatch detected needs_review_reason: str # Reason for review suggested_season_offset: int # Suggested season offset suggested_episode_offset: int # Suggested episode offset air_weekday: int # Airing day (0=Sunday, 6=Saturday) ``` ### RSSItem Model RSS feed subscriptions: ```python class RSSItem(SQLModel, table=True): id: int # Primary key name: str # Display name url: str # Feed URL (unique, indexed) aggregate: bool = True # Whether to parse torrents parser: str = "mikan" # Parser type: mikan, dmhy, nyaa enabled: bool = True # Active flag connection_status: str # "healthy" or "error" last_checked_at: str # ISO timestamp last_error: str # Last error message ``` ### Torrent Model Tracks downloaded torrents: ```python class Torrent(SQLModel, table=True): id: int # Primary key name: str # Torrent name (indexed) url: str # Torrent/magnet URL (unique, indexed) rss_id: int # Source RSS feed ID bangumi_id: int # Linked Bangumi ID (nullable) qb_hash: str # qBittorrent info hash (indexed) downloaded: bool = False # Download completed ``` ## Common Operations ### BangumiDatabase ```python with Database() as db: # Create db.bangumi.add(bangumi) # Single insert db.bangumi.add_all(bangumi_list) # Batch insert (deduplicates) # Read db.bangumi.search_all() # All records (cached, 5min TTL) db.bangumi.search_id(123) # By ID db.bangumi.match_torrent("torrent name") # Find by title_raw match db.bangumi.not_complete() # Incomplete series db.bangumi.get_needs_review() # Flagged for review # Update db.bangumi.update(bangumi) # Update single record db.bangumi.update_all(bangumi_list) # Batch update # Delete db.bangumi.delete_one(123) # Hard delete db.bangumi.disable_rule(123) # Soft delete (deleted=True) ``` ### RSSDatabase ```python with Database() as db: # Create db.rss.add(rss_item) # Single insert db.rss.add_all(rss_items) # Batch insert (deduplicates) # Read db.rss.search_all() # All feeds db.rss.search_active() # Enabled feeds only db.rss.search_aggregate() # Enabled + aggregate=True # Update db.rss.update(id, rss_update) # Partial update db.rss.enable(id) # Enable feed db.rss.disable(id) # Disable feed db.rss.enable_batch([1, 2, 3]) # Batch enable db.rss.disable_batch([1, 2, 3]) # Batch disable ``` ### TorrentDatabase ```python with Database() as db: # Create db.torrent.add(torrent) # Single insert db.torrent.add_all(torrents) # Batch insert # Read db.torrent.search_all() # All torrents db.torrent.search_by_qb_hash(hash) # By qBittorrent hash db.torrent.search_by_url(url) # By URL db.torrent.check_new(torrents) # Filter out existing # Update db.torrent.update_qb_hash(id, hash) # Set qb_hash ``` ## Caching ### Bangumi Cache `search_all()` results are cached at the module level with a 5-minute TTL: ```python # Module-level cache in bangumi.py _bangumi_cache: list[Bangumi] | None = None _bangumi_cache_time: float = 0 _BANGUMI_CACHE_TTL: float = 300.0 # 5 minutes # Cache invalidation def _invalidate_bangumi_cache(): global _bangumi_cache, _bangumi_cache_time _bangumi_cache = None _bangumi_cache_time = 0 ``` **Important:** The cache is automatically invalidated on: - `add()`, `add_all()` - `update()`, `update_all()` - `delete_one()`, `delete_all()` - `archive_one()`, `unarchive_one()` - Any RSS link update operations ### Session Expunge Cached objects are **expunged** from the session to prevent `DetachedInstanceError`: ```python for b in bangumis: self.session.expunge(b) # Detach from session ``` ## Migration System ### Schema Versioning Migrations are tracked via a `schema_version` table: ```python CURRENT_SCHEMA_VERSION = 7 # Each migration: (version, description, [SQL statements]) MIGRATIONS = [ (1, "add air_weekday column", [...]), (2, "add connection status columns", [...]), (3, "create passkey table", [...]), (4, "add archived column", [...]), (5, "rename offset to episode_offset", [...]), (6, "add qb_hash column", [...]), (7, "add suggested offset columns", [...]), ] ``` ### Adding a New Migration 1. Increment `CURRENT_SCHEMA_VERSION` in `combine.py` 2. Add migration tuple to `MIGRATIONS` list: ```python MIGRATIONS = [ # ... existing migrations ... ( 8, "add my_new_column to bangumi", [ "ALTER TABLE bangumi ADD COLUMN my_new_column TEXT DEFAULT NULL", ], ), ] ``` 3. Add idempotency check in `run_migrations()`: ```python if "bangumi" in tables and version == 8: columns = [col["name"] for col in inspector.get_columns("bangumi")] if "my_new_column" in columns: needs_run = False ``` 4. Update the corresponding Pydantic model in `module/models/` ### Default Value Backfill After migrations, `_fill_null_with_defaults()` automatically fills NULL values based on model defaults: ```python # If model defines: class Bangumi(SQLModel, table=True): my_field: bool = False # Then existing rows with NULL will be updated to False ``` ## Performance Patterns ### Batch Queries `add_all()` uses a single query to check for duplicates instead of N queries: ```python # Efficient: single SELECT keys_to_check = [(d.title_raw, d.group_name) for d in datas] conditions = [ and_(Bangumi.title_raw == tr, Bangumi.group_name == gn) for tr, gn in keys_to_check ] statement = select(Bangumi.title_raw, Bangumi.group_name).where(or_(*conditions)) ``` ### Regex Matching `match_list()` compiles a single regex pattern for all title matches: ```python # Compile once, match many sorted_titles = sorted(title_index.keys(), key=len, reverse=True) pattern = "|".join(re.escape(title) for title in sorted_titles) title_regex = re.compile(pattern) # O(1) lookup per torrent instead of O(n) for torrent in torrent_list: match = title_regex.search(torrent.name) ``` ### Indexed Columns The following columns have indexes for fast lookups: | Table | Column | Index Type | |-------|--------|------------| | `bangumi` | `title_raw` | Regular | | `bangumi` | `deleted` | Regular | | `bangumi` | `archived` | Regular | | `rssitem` | `url` | Unique | | `torrent` | `name` | Regular | | `torrent` | `url` | Unique | | `torrent` | `qb_hash` | Regular | ## Testing ### Test Database Setup Tests use an in-memory SQLite database: ```python # conftest.py @pytest.fixture def db_engine(): engine = create_engine("sqlite:///:memory:") SQLModel.metadata.create_all(engine) yield engine engine.dispose() @pytest.fixture def db_session(db_engine): with Session(db_engine) as session: yield session ``` ### Factory Functions Use factory functions for creating test data: ```python from test.factories import make_bangumi, make_torrent, make_rss_item def test_bangumi_search(): bangumi = make_bangumi(title_raw="Test Title", season=2) # ... test logic ``` ## Design Notes ### No Foreign Keys SQLite foreign key enforcement is disabled by default. Relationships (like `Torrent.bangumi_id`) are managed in application logic rather than database constraints. ### Soft Deletes The `Bangumi.deleted` flag enables soft deletes. Queries should filter by `deleted=False` for user-facing data: ```python statement = select(Bangumi).where(Bangumi.deleted == false()) ``` ### Torrent Tagging Torrents are tagged in qBittorrent with `ab:{bangumi_id}` for offset lookup during rename operations. This enables fast bangumi identification without database queries. ## Common Issues ### DetachedInstanceError If you access cached objects from a different session: ```python # Wrong: accessing cached object in new session bangumis = db.bangumi.search_all() # Cached with Database() as new_db: new_db.session.add(bangumis[0]) # Error! # Right: objects are expunged, work independently bangumis = db.bangumi.search_all() bangumis[0].title_raw = "New Title" # OK, but won't persist ``` ### Cache Staleness If manual SQL updates bypass the ORM, invalidate the cache: ```python from module.database.bangumi import _invalidate_bangumi_cache with engine.connect() as conn: conn.execute(text("UPDATE bangumi SET ...")) conn.commit() _invalidate_bangumi_cache() # Important! ``` ================================================ FILE: docs/en/dev/index.md ================================================ # Contributing Guide We welcome contributors to help make AutoBangumi better at solving issues encountered by users. This guide will walk you through how to contribute code to AutoBangumi. Please take a few minutes to read through before submitting a Pull Request. This article covers: - [Project Roadmap](#project-roadmap) - [Request for Comments (RFC)](#request-for-comments-rfc) - [Git Branch Management](#git-branch-management) - [Version Numbering](#version-numbering) - [Branch Development, Trunk Release](#branch-development-trunk-release) - [Branch Lifecycle](#branch-lifecycle) - [Git Workflow Overview](#git-workflow-overview) - [Pull Request](#pull-request) - [Release Process](#release-process) ## Project Roadmap The AutoBangumi development team uses [GitHub Project](https://github.com/EstrellaXD/Auto_Bangumi/projects?query=is%3Aopen) boards to manage planned development, ongoing fixes, and their progress. This helps you understand: - What the development team is working on - What aligns with your intended contribution, so you can participate directly - What's already in progress, to avoid duplicate work In [Project](https://github.com/EstrellaXD/Auto_Bangumi/projects?query=is%3Aopen), beyond the usual `[Feature Request]`, `[BUG]`, and small improvements, you'll find **`[RFC]`** items. ### Request for Comments (RFC) > Find existing [AutoBangumi RFCs](https://github.com/EstrellaXD/Auto_Bangumi/issues?q=is%3Aissue+label%3ARFC) via the `RFC` label in issues. For small improvements or bug fixes, feel free to adjust the code and submit a Pull Request. Just read the [Branch Management](#git-branch-management) section to base your work on the correct branch, and the [Pull Request](#pull-request) section to understand how PRs are merged.
For **larger** feature refactors with broad scope, please first write an RFC proposal via [Issue: Feature Proposal](https://github.com/EstrellaXD/Auto_Bangumi/issues/new?assignees=&labels=RFC&projects=&template=rfc.yml&title=%5BRFC%5D%3A+) to briefly describe your approach and seek developer discussion and consensus. Some proposals may conflict with decisions the development team has already made, and this step helps avoid wasted effort. > If you only want to discuss whether to add or improve a feature (not "how to implement it"), use -> [Issue: Feature Request](https://github.com/EstrellaXD/Auto_Bangumi/issues/new?labels=feature+request&template=feature_request.yml&title=%5BFeature+Request%5D+)
An [RFC Proposal](https://github.com/EstrellaXD/Auto_Bangumi/issues?q=is%3Aissue+is%3Aopen+label%3ARFC) is **"a document for developers to review technical design/approach before concrete development of a feature/refactor"**. The purpose is to ensure collaborating developers clearly know "what to do" and "how it will be done", with all developers able to participate in open discussion. This helps evaluate impacts (overlooked considerations, backward compatibility, conflicts with existing features). Therefore, proposals focus on describing the **approach, design, and steps** for solving the problem. ## Git Branch Management ### Version Numbering Git branches in the AutoBangumi project are closely related to release version rules. AutoBangumi follows [Semantic Versioning (SemVer)](https://semver.org/) with a `..` format: - **Major**: Major version update, likely with incompatible configuration/API changes - **Minor**: Backward-compatible new functionality - **Patch**: Backward-compatible bug fixes / minor improvements ### Branch Development, Trunk Release AutoBangumi uses a "branch development, trunk release" model. [**`main`**](https://github.com/EstrellaXD/Auto_Bangumi/commits/main) is the stable **trunk branch**, used only for releases, not for direct development. Each Minor version has a corresponding **development branch** for new features and post-release maintenance. Development branches are named `.-dev`, e.g., `3.1-dev`, `3.0-dev`, `2.6-dev`. Find them in [All Branches](https://github.com/EstrellaXD/Auto_Bangumi/branches/all?query=-dev). ### Branch Lifecycle When a Minor development branch (e.g., `3.1-dev`) completes feature development and **first** merges into main: - Release the Minor version (e.g., `3.1.0`) - Create the **next** Minor development branch (`3.2-dev`) for next version features - The **previous** version's branch (`3.0-dev`) is archived - This Minor branch (`3.1-dev`) enters maintenance — no new features/refactors, only bug fixes - Bug fixes are merged to the maintenance branch, then to main for `Patch` releases For contributors choosing Git branches: - **Bug fixes** — base on the **current released version's** Minor branch, PR to that branch - **New features/refactors** — base on the **next unreleased version's** Minor branch, PR to that branch > "Current released version" is the latest version on the [[Releases page]](https://github.com/EstrellaXD/Auto_Bangumi/releases) ### Git Workflow Overview > Commit timeline goes from left to right ---> ![dev-branch](/image/dev/branch.png) ## Pull Request Ensure you've selected the correct PR target branch per the Git Branch Management section above: > - **Bug fixes** → PR to the **current released version's** Minor maintenance branch > - **New features/refactors** → PR to the **next version's** Minor development branch
- A PR should correspond to a single concern and not introduce unrelated changes. Split different concerns into multiple PRs to help the team focus on one issue per review. - In the PR title and description, briefly explain the changes including reasons and intent. Link related issues or RFCs in the PR description. This helps the team understand context quickly during code review. - Ensure "Allow edits from maintainers" is checked. This allows direct minor edits/refactors and saves time. - Ensure local tests and linting pass. These are also checked in PR CI. - For bug fixes and new features, the team may request corresponding unit test coverage. The development team will review contributor PRs and discuss or approve merging as soon as possible. ## Release Process Releases are currently triggered automatically after the development team manually merges a specific "release PR". Bug fix PRs are typically released quickly, usually within a week. New feature releases take longer and are less predictable. Check the [GitHub Project](https://github.com/EstrellaXD/Auto_Bangumi/projects?query=is%3Aopen) board for development progress — a version is released when all planned features are complete. ================================================ FILE: docs/en/faq/index.md ================================================ # Frequently Asked Questions ## WebUI ### WebUI Address The default port is 7892. For server deployments, access `http://serverhost:7892`. For local deployments, access `http://localhost:7892`. If you changed the port, remember to also update the Docker port mapping. ### Default Username and Password - Default username: `admin`, default password: `adminadmin`. - Please change your password after first login. ### Changing or Resetting Password - Change password: After logging in, click `···` in the upper right, click `Profile`, and modify your username and password. - There is currently no simple password reset method. If you forget your password, delete the `data/data.db` file and restart. ### Why don't my configuration changes take effect? - After changing configuration, click the **Apply** button, then click **Restart** in the `···` menu to restart the main process. - If Debug mode is enabled, click **Shutdown** in the `···` menu to restart the container. ### How to check if the program is running normally The new WebUI has a small dot in the upper right corner. Green means running normally, red means an error occurred and the program is paused. ### Poster wall not showing images - If your version is 3.0: AB uses `mikanani.me` addresses as poster image sources by default. If images aren't showing, your network cannot access these images. - If your version is 3.1 or later: - If posters show an error icon, the images are missing. Click the refresh poster button in the upper right menu to fetch TMDB posters. - If posters fail to load, clear your browser cache. - When using `mikanime.tv` as the RSS address, client-side proxies may prevent poster loading. Add a `direct` rule for it. ## How Does v3.0 Manage Bangumi After upgrading to v3.0, AB can manage anime torrents and download rules in the WebUI. It relies on the torrent download path and rule name. If you manually change torrent download paths in QB, you may encounter issues like notifications missing posters or failed torrent deletion. Please manage anime and torrents within AB as much as possible. ## Downloads and Keyword Filtering ### Download Path **What should I put for the download path?** - This parameter just needs to match your qBittorrent configuration: - Docker: If qB uses `/downloads`, then set `/downloads/Bangumi`. You can change `Bangumi` to anything. - Linux/macOS: If it's `/home/usr/downloads` or `/User/UserName/Downloads`, just append `/Bangumi` at the end. - Windows: Change `D:\Media\` to `D:\Media\Bangumi` ### Downloads not starting automatically Check AutoBangumi's logs for any torrent-related entries. - If none exist, check if your subscription is correct. ### Downloads not saved in the correct directory - Check if the [download path](#download-path) is correct. - Check qBittorrent's PGID and PUID configuration for folder creation permissions. Try manually downloading any torrent to a specified directory — if errors occur or the directory isn't created, it's a permissions issue. - Check qBittorrent's default settings: Saving Management should be set to Manual (Saving Management >> Default Torrent Management Mode >> Manual). ### Downloading many unsubscribed anime - Check if your Mikan subscription includes all subtitle groups for a single anime. Subscribe to only one group per anime, and enable advanced subscriptions. - Advanced subscriptions can be enabled in Mikan Project's user settings. - Regex filtering may be insufficient — see the next section for expanding regex. - If neither applies, report with logs at [Issues][ISSUE]. ### How to write filter keywords Filter keywords in AB are regular expressions, added only when rules are created. To expand rules after creation, use the WebUI (v3.0+) to configure each anime individually. - Filter keywords are regex — separate unwanted keywords with `|`. - The default `720|\d+-\d+` rule filters out all collections and 720P anime. Add filters before deploying AB; subsequent environment variable changes only affect new rules. - Common regex keywords (separated by `|`): - `720` — filters 720, 720P, 720p, etc. - `\d+-\d+` — filters collections like [1-12] - `[Bb]aha` — filters Baha releases - `[Bb]ilibili`, `[Bb]-Global` — filters Bilibili releases - `繁`, `CHT` — filters Traditional Chinese subtitles - To match specific keywords, add in QB's include field: `XXXXX+1080P\+` where `1080P\+` matches 1080P+ releases. ### First deployment downloaded unwanted anime 1. Delete extra automatic download rules and files in QB. 2. Check subscriptions and filter rules. 3. Visit the resetRule API in your browser: `http://localhost:7892/api/v1/resetRule` to reset rules. 4. Restart AB. ### AB identifies fewer RSS entries than subscribed In newer versions, AB's filter also filters all RSS entries by default. Don't add all filters at once. For fine-grained control, configure each anime individually in the WebUI. ### Filter keywords not working - Check if the **global filter** parameter is set correctly. - Check QB's RSS auto-download rules — you can see matched RSS on the right side, adjust download rules, and click save to identify which keyword is causing issues. ## Episode Completion ### Episode completion not working Check if the **Episode completion** parameter is correctly configured. ## File Renaming ### Parse error `Cannot parse XXX` - AB does not currently support parsing collections. - If it's not a collection, report the issue on GitHub Issues. ### `Rename failed` or renaming errors - Check file paths. Standard storage path should be `/title/Season/Episode.mp4`. Non-standard paths cause naming errors — check your qBittorrent configuration. - Check if the `download path` is filled in correctly. Incorrect paths prevent proper renaming. - For other issues, report on GitHub Issues. ### No automatic renaming - Check if the torrent category in QB is `Bangumi`. - AB only renames downloaded files. ### How to rename non-AB anime with AB - Simply change the torrent's category to `Bangumi`. - Note: The torrent must be stored in a `Title/Season X/` folder to trigger renaming. ### How to rename collections 1. Change the collection's category to `Bangumi`. 2. Change the collection's storage path to `Title/Season X/`. 3. Wait for the collection to finish downloading, and renaming will complete. ## Docker ### How to auto-update Run a `watchtower` daemon in Docker to automatically update your containers. [watchtower](https://containrrr.dev/watchtower) official documentation ### Updating with Docker Compose If your AB is deployed with Docker Compose, use `docker compose pull` to update. After pulling the new image, use `docker compose up -d` to restart. You can also add `pull_policy: always` to your `docker-compose.yml` to pull the latest image on every start. ### What to do if an upgrade causes issues Since configurations may vary, upgrades might cause the program to fail. In this case, delete all previous data and generated configuration files, then restart the container. Then reconfigure in the WebUI. If upgrading from an older version, first refer to the [upgrade guide](/changelog/2.6). If you encounter issues not covered above, report them at [Issues][ISSUE] using the bug template. [ISSUE]: https://github.com/EstrellaXD/Auto_Bangumi/issues ================================================ FILE: docs/en/faq/network.md ================================================ # Network Issues ## Cannot Connect to Mikan Project Since the main Mikan Project site (`https://mikanani.me`) may be blocked in some regions, AB may fail to connect. Use the following solutions: - [Use Mikan Project alternative domain](#mikan-project-alternative-domain) - [Use a proxy](#configuring-a-proxy) - [Use a Cloudflare Worker reverse proxy](#cloudflare-workers-reverse-proxy) ### Mikan Project Alternative Domain Mikan Project has a new domain `https://mikanime.tv`. Use this domain with AB without enabling a proxy. If you see: ``` DNS/Connect ERROR ``` - Check your network connection. If it's fine, check DNS resolution. - Add `dns=8.8.8.8` to AB. If using Host network mode, this can be ignored. If you're using a proxy, this error typically won't occur with correct configuration. ### Configuring a Proxy ::: tip In AB 3.1+, AB handles RSS updates and notifications itself, so you only need to configure the proxy in AB. ::: AB has built-in proxy configuration. To configure a proxy, follow the instructions in [Proxy Settings](../config/proxy) to set up HTTP or SOCKS proxy correctly. This resolves access issues. **For versions before 3.1, qBittorrent proxy configuration is also needed** Configure the proxy in QB as shown below (same approach for SOCKS): image ### Cloudflare Workers Reverse Proxy You can also use a reverse proxy approach via Cloudflare Workers. Setting up a domain and binding it to Cloudflare is beyond the scope of this guide. Add the following code in Workers to use your own domain to access Mikan Project and download torrents from RSS links: ```javascript const TELEGRAPH_URL = 'https://mikanani.me'; const MY_DOMAIN = 'https://yourdomain.com' addEventListener('fetch', event => { event.respondWith(handleRequest(event.request)) }) async function handleRequest(request) { const url = new URL(request.url); url.host = TELEGRAPH_URL.replace(/^https?:\/\//, ''); const modifiedRequest = new Request(url.toString(), { headers: request.headers, method: request.method, body: request.body, redirect: 'manual' }); const response = await fetch(modifiedRequest); const contentType = response.headers.get('Content-Type') || ''; // Only perform replacement if content type is RSS if (contentType.includes('application/xml')) { const text = await response.text(); const replacedText = text.replace(/https?:\/\/mikanani\.me/g, MY_DOMAIN); const modifiedResponse = new Response(replacedText, response); // Add CORS headers modifiedResponse.headers.set('Access-Control-Allow-Origin', '*'); return modifiedResponse; } else { const modifiedResponse = new Response(response.body, response); // Add CORS headers modifiedResponse.headers.set('Access-Control-Allow-Origin', '*'); return modifiedResponse; } } ``` After completing the configuration, replace `https://mikanani.me` with your domain when **adding RSS**. ## Cannot Connect to qBittorrent First, check if the **downloader address** parameter in AB is correct. - If AB and QB are on the same Docker network, try using the container name for addressing, e.g., `http://qbittorrent:8080`. - If AB and QB are on the same Docker server, try using the Docker gateway address, e.g., `http://172.17.0.1:8080`. - If AB's network mode is not `host`, do not use `127.0.0.1` to access QB. If containers in Docker cannot access each other, set up a network link between QB and AB in QB's network connection settings. If qBittorrent uses HTTPS, add the `https://` prefix to the **downloader address**. ================================================ FILE: docs/en/faq/troubleshooting.md ================================================ --- title: Troubleshooting --- ## General Troubleshooting Flow 1. If AB fails to start, check if the startup command is correct. If incorrect and you don't know how to fix it, try redeploying AB. 2. After deploying AB, check the logs first. If you see output like the following, AB is running normally and connected to QB: ``` [2022-07-09 21:55:19,164] INFO: _ ____ _ [2022-07-09 21:55:19,165] INFO: /\ | | | _ \ (_) [2022-07-09 21:55:19,166] INFO: / \ _ _| |_ ___ | |_) | __ _ _ __ __ _ _ _ _ __ ___ _ [2022-07-09 21:55:19,167] INFO: / /\ \| | | | __/ _ \| _ < / _` | '_ \ / _` | | | | '_ ` _ \| | [2022-07-09 21:55:19,167] INFO: / ____ \ |_| | || (_) | |_) | (_| | | | | (_| | |_| | | | | | | | [2022-07-09 21:55:19,168] INFO: /_/ \_\__,_|\__\___/|____/ \__,_|_| |_|\__, |\__,_|_| |_| |_|_| [2022-07-09 21:55:19,169] INFO: __/ | [2022-07-09 21:55:19,169] INFO: |___/ [2022-07-09 21:55:19,170] INFO: Version 3.0.1 Author: EstrellaXD Twitter: https://twitter.com/Estrella_Pan [2022-07-09 21:55:19,171] INFO: GitHub: https://github.com/EstrellaXD/Auto_Bangumi/ [2022-07-09 21:55:19,172] INFO: Starting AutoBangumi... [2022-07-09 21:55:20,717] INFO: Add RSS Feed successfully. [2022-07-09 21:55:21,761] INFO: Start collecting RSS info. [2022-07-09 21:55:23,431] INFO: Finished [2022-07-09 21:55:23,432] INFO: Running.... ``` 1. If you see this log, AB cannot connect to qBittorrent. Check if qBittorrent is running. If it is, go to the [Network Issues](/faq/network) section. ``` [2022-07-09 22:01:24,534] WARNING: Cannot connect to qBittorrent, wait 5min and retry ``` 2. If you see this log, AB cannot connect to Mikan RSS. Go to the [Network Issues](/faq/network) section. ``` [2022-07-09 21:55:21,761] INFO: Start collecting RSS info. [2022-07-09 22:01:24,534] WARNING: Connected Failed, please check DNS/Connection ``` 3. At this point, QB should have download tasks. 1. If downloads show path issues, check QB's "Saving Management" → "Default Torrent Management Mode" is set to "Manual". 2. If all downloads show exclamation marks or no category folders are created in the download path, check QB's permissions. 4. If none of the above resolves the issue, try redeploying a fresh qBittorrent. 5. If still unsuccessful, report with logs at [Issues](https://www.github.com/EstrellaXD/Auto_Bangumi/issues). ================================================ FILE: docs/en/feature/bangumi.md ================================================ # Bangumi Management Click an anime poster on the homepage to manage individual anime entries. ![Bangumi List](/image/feature/bangumi-list.png) If an anime has multiple download rules (e.g., different subtitle groups), a rule selection popup will appear: ![Rule Selection](/image/feature/rule-select.png) After selecting a rule, the edit modal opens: ![Edit Bangumi](/image/feature/bangumi-edit.png) ## Notification Badges Since v3.2, bangumi cards display iOS-style notification badges to indicate status: - **Yellow badge with `!`**: Subscription needs review (e.g., offset issues detected) - **Number badge**: Multiple rules exist for this anime - **Combined badge** (e.g., `! | 2`): Has warning and multiple rules Cards with warnings also display a yellow glow animation to draw attention. ## Episode Offset Auto-Detection Some anime have complex season structures that cause mismatches between RSS episode numbers and TMDB data. For example: - "Frieren: Beyond Journey's End" Season 1 was broadcast in two parts with a 6-month gap - RSS may show "S2E01" while TMDB considers it "S1E29" AB v3.2 can automatically detect these issues: 1. Click the **Auto Detect** button in the edit modal 2. AB analyzes TMDB episode air dates to identify "virtual seasons" 3. If a mismatch is found, AB suggests the correct offset values 4. Click **Apply** to save the offset The background scan thread also periodically checks existing subscriptions for offset issues and marks them for review. ## Archive / Unarchive Anime Since v3.2, you can archive completed or inactive anime to keep your list organized. ### Manual Archive 1. Click on an anime poster 2. In the edit modal, click the **Archive** button 3. The anime moves to the "Archived" section at the bottom of the list ### Automatic Archive AB can automatically archive anime when: - The series status on TMDB shows as "Ended" or "Canceled" - Use **Config** → refresh metadata to trigger auto-archive ### Viewing Archived Anime Archived anime appear in a collapsible "Archived" section at the bottom of the bangumi list. Click to expand and view archived items. ### Unarchive To restore an archived anime: 1. Expand the "Archived" section 2. Click on the anime poster 3. Click the **Unarchive** button ## Disable / Delete Anime Since AB continuously parses **aggregated RSS** feeds, for download rules from aggregated RSS that you no longer need: - Disable anime: The anime won't be downloaded or re-parsed - Remove the subscription from the aggregated RSS If you delete the anime entry, it will be recreated on the next parse cycle. ## Advanced Settings Click **Advanced Settings** in the edit modal to access additional options: ![Advanced Settings](/image/feature/bangumi-edit-advanced.png) - **Season Offset**: Adjust the season number offset - **Episode Offset**: Adjust the episode number offset - **Filter**: Custom regex filter for torrent matching ================================================ FILE: docs/en/feature/calendar.md ================================================ # Calendar View Since v3.2, AB includes a calendar view that shows your subscribed anime organized by broadcast day. ![Calendar](/image/feature/calendar.png) ## Features ### Weekly Schedule The calendar displays anime organized by their broadcast weekday (Monday through Sunday), plus an "Unknown" column for anime without broadcast schedule data. ### Bangumi.tv Integration AB fetches broadcast schedule data from Bangumi.tv to accurately display when each anime airs. Click the **Refresh schedule** button to update the broadcast data. ### Grouped Display Since v3.2, anime with multiple download rules are grouped together: - Same anime appears once, even with multiple subtitle group rules - Click on a grouped anime to see all available rules - Select a specific rule to edit This keeps the calendar clean while still providing access to all your rules. ## Navigation Click on any anime poster in the calendar to: - View anime details - Edit download rules - Access archive/disable options ## Tips ::: tip If an anime appears in the "Unknown" column, it may not have broadcast data on Bangumi.tv, or the anime title couldn't be matched. ::: ================================================ FILE: docs/en/feature/rename.md ================================================ # File Renaming AB currently provides three renaming methods: `pn`, `advance`, and `none`. ### pn Short for `pure name`. This method uses the torrent download name for renaming. Example: ``` [Lilith-Raws] 86 - Eighty Six - 01 [Baha][WEB-DL][1080p][AVC AAC][CHT][MKV].mkv >> 86 - Eighty Six S01E01.mkv ``` ### advance Advanced renaming. This method uses the parent folder name for renaming. ``` /downloads/Bangumi/86 - Eighty Six(2023)/Season 1/[Lilith-Raws] 86 - Eighty Six - 01 [Baha][WEB-DL][1080p][AVC AAC][CHT][MKV].mkv >> 86 - Eighty Six(2023) S01E01.mkv ``` ### none No renaming. Files are left as-is. ## Collection Renaming AB supports renaming collections. Collection renaming requires: - Episodes are in the collection's first-level directory - Episode numbers can be parsed from file names AB can also rename subtitle files in the first-level directory. After renaming, episodes and directories are placed in the `Season` folder. Renamed collections are moved and categorized under `BangumiCollection`. ## Episode Offset Since v3.2, AB supports episode offset for renaming. This is useful when: - RSS shows different episode numbers than expected (e.g., S2E01 should be S1E29) - Anime has "virtual seasons" due to broadcast gaps When an offset is configured for a bangumi, AB automatically applies it during renaming: ``` Original: S02E01.mkv With offset (season: -1, episode: +28): S01E29.mkv ``` To configure offset: 1. Click on the anime poster 2. Open Advanced Settings 3. Set Season Offset and/or Episode Offset values 4. Or use "Auto Detect" to let AB suggest the correct offset See [Bangumi Management](./bangumi.md#episode-offset-auto-detection) for more details on auto-detection. ================================================ FILE: docs/en/feature/rss.md ================================================ --- title: RSS Management --- # RSS Management ## RSS Manager Page The RSS Manager page displays all your RSS subscriptions with their connection status. ![RSS Manager](/image/feature/rss-manager.png) ### Connection Status Since v3.2, AB tracks the connection status of each RSS source: | Status | Description | |--------|-------------| | **Connected** (green) | RSS source is reachable and returning valid data | | **Error** (red) | RSS source failed to respond or returned invalid data | When a source shows an error, hover over the status label to see the error details in a tooltip. AB automatically updates the connection status on each RSS refresh cycle. ## Adding Collections AB provides two manual download methods: **Collect** and **Subscribe**. - **Collect** downloads all episodes at once, suitable for completed anime. - **Subscribe** adds an automatic download rule with the corresponding RSS link, suitable for ongoing anime. ### Parsing RSS Links AB supports parsing collection RSS links from all resource sites. Find the collection RSS for your desired anime on the corresponding site, click the **+** button in the upper right corner of AB, and paste the RSS link in the popup window. ### Adding Downloads If parsing succeeds, a window will appear showing the parsed anime information. Click **Collect** or **Subscribe** to add it to the download queue. ### Common Issues If a parsing error occurs, it may be due to an incorrect RSS link or an unsupported subtitle group naming format. ## Managing Bangumi Since v3.0, AB provides manual anime management in the WebUI, allowing you to manually adjust incorrectly parsed anime information. ### Editing Anime Information In the anime list, click the anime poster to enter the anime information page. After modifying the information, click **Apply**. AB will readjust the directory and automatically rename files based on your changes. ### Deleting Anime Since v3.0, you can manually delete anime. Click the anime poster, enter the information page, and click **Delete**. ::: warning After deleting anime, if the RSS subscription hasn't been cancelled, AB will still re-parse it. To disable the download rule, use [Disable Anime](#disabling-anime). ::: ### Disabling Anime Since v3.0, you can manually disable anime. Click the anime poster, enter the information page, and click **Disable**. Once disabled, the anime poster will be grayed out and sorted to the end. To re-enable the download rule, click **Enable**. ================================================ FILE: docs/en/feature/search.md ================================================ # Torrent Search Since v3.1, AB includes a search feature for quickly finding anime. ## Using the Search Feature ::: warning The search feature relies on the main program's parser. The current version does not support parsing collections. A `warning` when parsing collections is normal behavior. ::: The search bar is located in the AB top bar. Click to open the search panel. ![Search Panel](/image/feature/search-panel.png) Select the source site, enter keywords, and AB will automatically parse and display search results. To add an anime, click the add button on the right side of the card. ::: tip When the source is **Mikan**, AB uses the `mikan` parser by default. For other sources, the TMDB parser is used. ::: ## Managing Search Sources Since v3.2, you can manage search sources directly in the Settings page without editing JSON files. ### Search Provider Settings Panel Navigate to **Config** → **Search Provider** to access the settings panel. ![Search Provider Settings](/image/feature/search-provider.png) From here you can: - **View** all configured search sources - **Add** new search sources with the "Add Provider" button - **Edit** existing source URLs - **Delete** custom sources (default sources mikan, nyaa, dmhy cannot be deleted) ### URL Template Format When adding a custom source, the URL must contain `%s` as a placeholder for the search keyword. Example: ``` https://example.com/rss/search?q=%s ``` The `%s` will be replaced with the user's search query. ### Default Sources The following sources are built-in and cannot be deleted: | Source | URL Template | |--------|--------------| | mikan | `https://mikanani.me/RSS/Search?searchstr=%s` | | nyaa | `https://nyaa.si/?page=rss&q=%s&c=0_0&f=0` | | dmhy | `http://dmhy.org/topics/rss/rss.xml?keyword=%s` | ### Adding Sources via Config File You can also manually add sources by editing `config/search_provider.json`: ```json { "mikan": "https://mikanani.me/RSS/Search?searchstr=%s", "nyaa": "https://nyaa.si/?page=rss&q=%s&c=0_0&f=0", "dmhy": "http://dmhy.org/topics/rss/rss.xml?keyword=%s", "bangumi.moe": "https://bangumi.moe/rss/search/%s" } ``` ================================================ FILE: docs/en/home/index.md ================================================ --- title: About ---

## About AutoBangumi

AutoBangumi WebUI

**`AutoBangumi`** is a fully automated anime downloading and organizing tool based on RSS feeds. Simply subscribe to anime on [Mikan Project][mikan] or similar sites, and it will automatically track and download new episodes. The organized file names and directory structure are directly compatible with [Plex][plex], [Jellyfin][jellyfin], and other media library software without requiring additional metadata scraping. ## Features - Simple one-time configuration for continuous use - Hands-free RSS parser that extracts anime information and automatically generates download rules - Anime file organization: ``` Bangumi ├── bangumi_A_title │ ├── Season 1 │ │ ├── A S01E01.mp4 │ │ ├── A S01E02.mp4 │ │ ├── A S01E03.mp4 │ │ └── A S01E04.mp4 │ └── Season 2 │ ├── A S02E01.mp4 │ ├── A S02E02.mp4 │ ├── A S02E03.mp4 │ └── A S02E04.mp4 ├── bangumi_B_title │ └─── Season 1 ``` - Fully automatic renaming — over 99% of anime files can be directly scraped by media library software after renaming ``` [Lilith-Raws] Kakkou no Iinazuke - 07 [Baha][WEB-DL][1080p][AVC AAC][CHT][MP4].mp4 >> Kakkou no Iinazuke S01E07.mp4 ``` - Custom renaming based on parent folder names for all child files - Mid-season catch-up to fill in all missed episodes of the current season - Highly customizable options that can be fine-tuned for different media library software - Zero maintenance, completely transparent operation - Built-in TMDB parser for generating complete TMDB-formatted files and anime metadata - Reverse proxy support for Mikan RSS feeds ## Community - Update notifications: [Telegram Channel](https://t.me/autobangumi_update) - Bug reports: [Telegram](https://t.me/+yNisOnDGaX5jMTM9) ## Acknowledgments Thanks to [Sean](https://github.com/findix) for extensive help with the project. ## Contributing Issues and Pull Requests are welcome! ## Disclaimer Since AutoBangumi obtains anime through unofficial copyright channels: - **Do not** use AutoBangumi for commercial purposes. - **Do not** create video content featuring AutoBangumi for distribution on domestic video platforms (copyright stakeholders). - **Do not** use AutoBangumi for any activity that violates laws or regulations. AutoBangumi is for educational and personal use only. ## License [MIT License](https://github.com/EstrellaXD/Auto_Bangumi/blob/main/LICENSE) [mikan]: https://mikanani.me [plex]: https://plex.tv [jellyfin]: https://jellyfin.org ================================================ FILE: docs/en/home/pipline.md ================================================ # How AutoBangumi Works AutoBangumi (AB for short) is essentially an RSS parser. It parses RSS feeds from anime torrent sites, extracts metadata from torrent titles, generates download rules, and sends them to qBittorrent for downloading. After downloading, it organizes files into a standard media library directory structure. ## Pipeline Overview 1. **RSS Parsing** — AB periodically fetches and parses your subscribed RSS feeds 2. **Title Analysis** — Torrent titles are parsed to extract anime name, episode number, season, subtitle group, and resolution 3. **Rule Generation** — Download rules are created in qBittorrent based on the parsed information 4. **Download Management** — qBittorrent handles the actual downloading of torrents 5. **File Organization** — Downloaded files are renamed and moved into a standardized directory structure 6. **Media Library Ready** — The organized files can be directly recognized by Plex, Jellyfin, and other media servers ================================================ FILE: docs/en/index.md ================================================ --- # https://vitepress.dev/reference/default-theme-home-page layout: home title: AutoBangumi titleTemplate: 全自动追番,解放双手! hero: name: AutoBangumi text: 全自动追番,解放双手! tagline: 全自动 RSS 订阅解析、下载管理和文件整理 actions: - theme: brand text: 快速开始 link: /deploy/quick-start - theme: alt text: 关于 link: /home/ - theme: alt text: 更新日志 link: /changelog/3.2 features: - icon: src: /image/icons/rss.png title: RSS 订阅解析 details: 自动识别并解析番剧 RSS 订阅源。无需手动输入,只需订阅即可自动完成解析、下载和整理。 - icon: src: /image/icons/qbittorrent-logo.svg title: qBittorrent 下载器 details: 使用 qBittorrent 下载番剧资源。在 AutoBangumi 中即可管理现有番剧、下载往期番剧以及删除条目。 - icon: src: /image/icons/tmdb-icon.png title: TMDB 元数据匹配 details: 通过 TMDB 匹配番剧信息以获取准确的元数据,确保即使在多个字幕组之间也能正确解析。 - icon: src: /image/icons/plex-icon.png title: Plex / Jellyfin / Infuse ... details: 根据匹配结果自动整理文件名和目录结构,确保媒体库软件能够高成功率地刮削元数据。 ---
## 鸣谢 ### 致谢 感谢 - [Mikan Project](https://mikanani.me) 提供了如此优秀的番剧资源。 - [VitePress](https://vitepress.dev) 提供了优秀的文档框架。 - [qBittorrent](https://www.qbittorrent.org) 提供了优秀的下载器。 - [Plex](https://www.plex.tv) / [Jellyfin](https://jellyfin.org) 提供了优秀的自托管媒体库。 - [Infuse](https://firecore.com/infuse) 提供了优雅的视频播放器。 - [弹弹 Play](https://www.dandanplay.com) 提供了优秀的弹幕播放器。 - 每一个番剧制作组 / 字幕组 / 爱好者。 ### 贡献者 [ ![](https://contrib.rocks/image?repo=EstrellaXD/Auto_Bangumi){class=contributors-avatar} ](https://github.com/EstrellaXD/Auto_Bangumi/graphs/contributors) ## 免责声明 由于 AutoBangumi 通过非官方版权渠道获取番剧: - **请勿**将 AutoBangumi 用于商业用途。 - **请勿**制作包含 AutoBangumi 的视频内容并在国内视频平台(版权相关方)上发布。 - **请勿**将 AutoBangumi 用于任何违反法律法规的活动。
================================================ FILE: docs/faq/index.md ================================================ # 常见问题 ## WebUI ### WebUI 地址 默认端口为 7892。服务器部署时访问 `http://serverhost:7892`,本地部署时访问 `http://localhost:7892`。如果修改了端口,请记得同时修改 Docker 端口映射。 ### 默认用户名和密码 - 默认用户名:`admin`,默认密码:`adminadmin`。 - 请在首次登录后修改密码。 ### 修改或重置密码 - 修改密码:登录后,点击右上角的 `···`,点击 `个人资料`,即可修改用户名和密码。 - 目前没有简便的密码重置方法。如果忘记密码,请删除 `data/data.db` 文件后重启。 ### 为什么配置修改不生效? - 修改配置后,点击 **应用** 按钮,然后在 `···` 菜单中点击 **重启** 来重启主程序。 - 如果启用了调试模式,请在 `···` 菜单中点击 **关闭** 来重启容器。 ### 如何检查程序是否正常运行 新版 WebUI 右上角有一个小圆点。绿色表示程序正常运行,红色表示发生错误且程序已暂停。 ### 海报墙不显示图片 - 如果您的版本是 3.0: AB 默认使用 `mikanani.me` 地址作为海报图片来源。如果图片不显示,说明您的网络无法访问这些图片。 - 如果您的版本是 3.1 或更高版本: - 如果海报显示错误图标,说明图片缺失。请点击右上角菜单中的刷新海报按钮来获取 TMDB 海报。 - 如果海报加载失败,请清除浏览器缓存。 - 使用 `mikanime.tv` 作为 RSS 地址时,客户端代理可能会阻止海报加载。请为其添加 `direct` 规则。 ## v3.0 如何管理番剧 升级到 v3.0 后,AB 可以在 WebUI 中管理番剧种子和下载规则。这依赖于种子下载路径和规则名称。 如果您在 QB 中手动修改了种子下载路径,可能会遇到通知缺少海报或种子删除失败等问题。 请尽量在 AB 内管理番剧和种子。 ## 下载与关键词过滤 ### 下载路径 **下载路径应该填什么?** - 这个参数只需要与您的 qBittorrent 配置匹配即可: - Docker:如果 qB 使用 `/downloads`,则设置为 `/downloads/Bangumi`。您可以将 `Bangumi` 改为任何名称。 - Linux/macOS:如果是 `/home/usr/downloads` 或 `/User/UserName/Downloads`,只需在末尾添加 `/Bangumi`。 - Windows:将 `D:\Media\` 改为 `D:\Media\Bangumi` ### 下载没有自动开始 检查 AutoBangumi 的日志中是否有种子相关的条目。 - 如果没有,请检查您的订阅是否正确。 ### 下载没有保存到正确的目录 - 检查[下载路径](#下载路径)是否正确。 - 检查 qBittorrent 的 PGID 和 PUID 配置是否有创建文件夹的权限。尝试手动下载任意种子到指定目录——如果出错或目录未创建,则是权限问题。 - 检查 qBittorrent 的默认设置:保存管理应设置为手动(保存管理 >> 默认 Torrent 管理模式 >> 手动)。 ### 下载了很多未订阅的番剧 - 检查您的 Mikan 订阅是否包含了同一番剧的所有字幕组。每个番剧只订阅一个字幕组,并启用高级订阅。 - 高级订阅可以在 Mikan Project 的用户设置中启用。 - 正则表达式过滤可能不够——请参阅下一节来扩展正则表达式。 - 如果以上都不适用,请携带日志在 [Issues][ISSUE] 报告。 ### 如何编写过滤关键词 AB 中的过滤关键词是正则表达式,仅在创建规则时添加。要在创建后扩展规则,请使用 WebUI(v3.0+)单独配置每个番剧。 - 过滤关键词是正则表达式——用 `|` 分隔不需要的关键词。 - 默认的 `720|\d+-\d+` 规则会过滤掉所有合集和 720P 番剧。在部署 AB 前添加过滤器;之后的环境变量修改只影响新规则。 - 常用正则表达式关键词(用 `|` 分隔): - `720` — 过滤 720、720P、720p 等。 - `\d+-\d+` — 过滤 [1-12] 等合集 - `[Bb]aha` — 过滤 Baha 发布 - `[Bb]ilibili`、`[Bb]-Global` — 过滤 Bilibili 发布 - `繁`、`CHT` — 过滤繁体中文字幕 - 要匹配特定关键词,请在 QB 的包含字段中添加:`XXXXX+1080P\+`,其中 `1080P\+` 匹配 1080P+ 发布。 ### 首次部署时下载了不想要的番剧 1. 删除 QB 中多余的自动下载规则和文件。 2. 检查订阅和过滤规则。 3. 在浏览器中访问 resetRule API:`http://localhost:7892/api/v1/resetRule` 来重置规则。 4. 重启 AB。 ### AB 识别的 RSS 条目比订阅的少 在较新版本中,AB 的过滤器默认也会过滤所有 RSS 条目。不要一次添加所有过滤器。要进行细粒度控制,请在 WebUI 中单独配置每个番剧。 ### 过滤关键词不生效 - 检查 **全局过滤器** 参数是否设置正确。 - 检查 QB 的 RSS 自动下载规则——您可以在右侧看到匹配的 RSS,调整下载规则并点击保存来确定是哪个关键词导致问题。 ## 剧集补全 ### 剧集补全不生效 检查 **剧集补全** 参数是否配置正确。 ## 文件重命名 ### 解析错误 `Cannot parse XXX` - AB 目前不支持解析合集。 - 如果不是合集,请在 GitHub Issues 上报告问题。 ### `Rename failed` 或重命名错误 - 检查文件路径。标准存储路径应为 `/title/Season/Episode.mp4`。非标准路径会导致命名错误——请检查您的 qBittorrent 配置。 - 检查 `下载路径` 是否填写正确。路径不正确会导致无法正确重命名。 - 其他问题请在 GitHub Issues 上报告。 ### 没有自动重命名 - 检查 QB 中的种子分类是否为 `Bangumi`。 - AB 只会重命名已下载的文件。 ### 如何使用 AB 重命名非 AB 下载的番剧 - 只需将种子的分类改为 `Bangumi`。 - 注意:种子必须存储在 `Title/Season X/` 文件夹中才能触发重命名。 ### 如何重命名合集 1. 将合集的分类改为 `Bangumi`。 2. 将合集的存储路径改为 `Title/Season X/`。 3. 等待合集下载完成,重命名将自动完成。 ## Docker ### 如何自动更新 在 Docker 中运行 `watchtower` 守护进程来自动更新您的容器。 [watchtower](https://containrrr.dev/watchtower) 官方文档 ### 使用 Docker Compose 更新 如果您的 AB 是使用 Docker Compose 部署的,请使用 `docker compose pull` 来更新。 拉取新镜像后,使用 `docker compose up -d` 来重启。 您也可以在 `docker-compose.yml` 中添加 `pull_policy: always` 来在每次启动时拉取最新镜像。 ### 升级导致问题怎么办 由于配置可能各不相同,升级可能会导致程序失败。在这种情况下,删除所有之前的数据和生成的配置文件,然后重启容器。 然后在 WebUI 中重新配置。 如果从旧版本升级,请先参阅[升级指南](/changelog/2.6)。 如果您遇到上述未涵盖的问题,请使用 bug 模板在 [Issues][ISSUE] 报告。 [ISSUE]: https://github.com/EstrellaXD/Auto_Bangumi/issues ================================================ FILE: docs/faq/network.md ================================================ # 网络问题 ## 无法连接到 Mikan Project 由于 Mikan Project 主站(`https://mikanani.me`)在某些地区可能被屏蔽,AB 可能无法连接。请使用以下解决方案: - [使用 Mikan Project 备用域名](#mikan-project-备用域名) - [使用代理](#配置代理) - [使用 Cloudflare Worker 反向代理](#cloudflare-workers-反向代理) ### Mikan Project 备用域名 Mikan Project 有一个新域名 `https://mikanime.tv`。使用此域名配合 AB,无需启用代理。 如果您看到: ``` DNS/Connect ERROR ``` - 请检查您的网络连接。如果网络正常,请检查 DNS 解析。 - 在 AB 中添加 `dns=8.8.8.8`。如果使用 Host 网络模式,可以忽略此项。 如果您使用代理,正确配置后通常不会出现此错误。 ### 配置代理 ::: tip 在 AB 3.1+ 中,AB 自己处理 RSS 更新和通知,因此您只需要在 AB 中配置代理。 ::: AB 有内置的代理配置。要配置代理,请按照[代理设置](../config/proxy)中的说明正确设置 HTTP 或 SOCKS 代理。这可以解决访问问题。 **对于 3.1 之前的版本,还需要配置 qBittorrent 代理** 如下图所示在 QB 中配置代理(SOCKS 方法相同): image ### Cloudflare Workers 反向代理 您也可以通过 Cloudflare Workers 使用反向代理方式。设置域名并绑定到 Cloudflare 超出了本指南的范围。 在 Workers 中添加以下代码,即可使用您自己的域名访问 Mikan Project 并从 RSS 链接下载种子: ```javascript const TELEGRAPH_URL = 'https://mikanani.me'; const MY_DOMAIN = 'https://yourdomain.com' addEventListener('fetch', event => { event.respondWith(handleRequest(event.request)) }) async function handleRequest(request) { const url = new URL(request.url); url.host = TELEGRAPH_URL.replace(/^https?:\/\//, ''); const modifiedRequest = new Request(url.toString(), { headers: request.headers, method: request.method, body: request.body, redirect: 'manual' }); const response = await fetch(modifiedRequest); const contentType = response.headers.get('Content-Type') || ''; // Only perform replacement if content type is RSS if (contentType.includes('application/xml')) { const text = await response.text(); const replacedText = text.replace(/https?:\/\/mikanani\.me/g, MY_DOMAIN); const modifiedResponse = new Response(replacedText, response); // Add CORS headers modifiedResponse.headers.set('Access-Control-Allow-Origin', '*'); return modifiedResponse; } else { const modifiedResponse = new Response(response.body, response); // Add CORS headers modifiedResponse.headers.set('Access-Control-Allow-Origin', '*'); return modifiedResponse; } } ``` 完成配置后,**添加 RSS** 时将 `https://mikanani.me` 替换为您的域名。 ## 无法连接到 qBittorrent 首先,检查 AB 中的 **下载器地址** 参数是否正确。 - 如果 AB 和 QB 在同一个 Docker 网络中,请尝试使用容器名称进行寻址,例如 `http://qbittorrent:8080`。 - 如果 AB 和 QB 在同一台 Docker 服务器上,请尝试使用 Docker 网关地址,例如 `http://172.17.0.1:8080`。 - 如果 AB 的网络模式不是 `host`,请不要使用 `127.0.0.1` 访问 QB。 如果 Docker 中的容器无法相互访问,请在 QB 的网络连接设置中设置 QB 和 AB 之间的网络链接。如果 qBittorrent 使用 HTTPS,请在 **下载器地址** 前添加 `https://` 前缀。 ================================================ FILE: docs/faq/troubleshooting.md ================================================ --- title: 故障排除 --- ## 一般故障排除流程 1. 如果 AB 无法启动,请检查启动命令是否正确。如果不正确且您不知道如何修复,请尝试重新部署 AB。 2. 部署 AB 后,首先检查日志。如果您看到类似以下的输出,说明 AB 正常运行并已连接到 QB: ``` [2022-07-09 21:55:19,164] INFO: _ ____ _ [2022-07-09 21:55:19,165] INFO: /\ | | | _ \ (_) [2022-07-09 21:55:19,166] INFO: / \ _ _| |_ ___ | |_) | __ _ _ __ __ _ _ _ _ __ ___ _ [2022-07-09 21:55:19,167] INFO: / /\ \| | | | __/ _ \| _ < / _` | '_ \ / _` | | | | '_ ` _ \| | [2022-07-09 21:55:19,167] INFO: / ____ \ |_| | || (_) | |_) | (_| | | | | (_| | |_| | | | | | | | [2022-07-09 21:55:19,168] INFO: /_/ \_\__,_|\__\___/|____/ \__,_|_| |_|\__, |\__,_|_| |_| |_|_| [2022-07-09 21:55:19,169] INFO: __/ | [2022-07-09 21:55:19,169] INFO: |___/ [2022-07-09 21:55:19,170] INFO: Version 3.0.1 Author: EstrellaXD Twitter: https://twitter.com/Estrella_Pan [2022-07-09 21:55:19,171] INFO: GitHub: https://github.com/EstrellaXD/Auto_Bangumi/ [2022-07-09 21:55:19,172] INFO: Starting AutoBangumi... [2022-07-09 21:55:20,717] INFO: Add RSS Feed successfully. [2022-07-09 21:55:21,761] INFO: Start collecting RSS info. [2022-07-09 21:55:23,431] INFO: Finished [2022-07-09 21:55:23,432] INFO: Running.... ``` 1. 如果您看到此日志,说明 AB 无法连接到 qBittorrent。请检查 qBittorrent 是否正在运行。如果正在运行,请前往[网络问题](/faq/network)部分。 ``` [2022-07-09 22:01:24,534] WARNING: Cannot connect to qBittorrent, wait 5min and retry ``` 2. 如果您看到此日志,说明 AB 无法连接到 Mikan RSS。请前往[网络问题](/faq/network)部分。 ``` [2022-07-09 21:55:21,761] INFO: Start collecting RSS info. [2022-07-09 22:01:24,534] WARNING: Connected Failed, please check DNS/Connection ``` 3. 此时,QB 应该有下载任务了。 1. 如果下载显示路径问题,请检查 QB 的"保存管理"→"默认 Torrent 管理模式"是否设置为"手动"。 2. 如果所有下载都显示感叹号或下载路径中没有创建分类文件夹,请检查 QB 的权限。 4. 如果以上都无法解决问题,请尝试重新部署一个全新的 qBittorrent。 5. 如果仍然不成功,请携带日志在 [Issues](https://www.github.com/EstrellaXD/Auto_Bangumi/issues) 报告。 ================================================ FILE: docs/feature/bangumi.md ================================================ # 番剧管理 点击首页的番剧海报可管理单个番剧条目。 ![Bangumi List](/image/feature/bangumi-list.png) 如果一部番剧有多个下载规则(例如不同字幕组),将出现规则选择弹窗: ![Rule Selection](/image/feature/rule-select.png) 选择规则后,编辑弹窗打开: ![Edit Bangumi](/image/feature/bangumi-edit.png) ## 通知徽章 从 v3.2 开始,番剧卡片显示 iOS 风格的通知徽章来指示状态: - **黄色徽章带 `!`**:订阅需要审核(例如检测到偏移问题) - **数字徽章**:该番剧存在多个规则 - **组合徽章**(例如 `! | 2`):有警告且有多个规则 带有警告的卡片还会显示黄色发光动画以引起注意。 ## 剧集偏移自动检测 某些番剧具有复杂的季度结构,导致 RSS 剧集编号与 TMDB 数据不匹配。例如: - "葬送的芙莉莲" 第一季分两部分播出,中间隔了 6 个月 - RSS 可能显示 "S2E01",而 TMDB 认为是 "S1E29" AB v3.2 可以自动检测这些问题: 1. 点击编辑弹窗中的 **自动检测** 按钮 2. AB 分析 TMDB 剧集播出日期以识别"虚拟季度" 3. 如果发现不匹配,AB 会建议正确的偏移值 4. 点击 **应用** 保存偏移 后台扫描线程还会定期检查现有订阅的偏移问题,并标记需要审核的项目。 ## 归档 / 取消归档番剧 从 v3.2 开始,您可以归档已完结或不活跃的番剧,以保持列表整洁。 ### 手动归档 1. 点击番剧海报 2. 在编辑弹窗中,点击 **归档** 按钮 3. 番剧移至列表底部的"已归档"区域 ### 自动归档 AB 可以在以下情况自动归档番剧: - TMDB 上的系列状态显示为"已完结"或"已取消" - 使用 **配置** → 刷新元数据触发自动归档 ### 查看已归档番剧 已归档番剧出现在番剧列表底部的可折叠"已归档"区域。点击展开查看已归档项目。 ### 取消归档 要恢复已归档番剧: 1. 展开"已归档"区域 2. 点击番剧海报 3. 点击 **取消归档** 按钮 ## 禁用 / 删除番剧 由于 AB 会持续解析**聚合 RSS** 订阅,对于来自聚合 RSS 中不再需要的下载规则: - 禁用番剧:番剧不会被下载或重新解析 - 从聚合 RSS 中移除订阅 如果您删除番剧条目,它将在下次解析周期被重新创建。 ## 高级设置 点击编辑弹窗中的 **高级设置** 访问更多选项: ![Advanced Settings](/image/feature/bangumi-edit-advanced.png) - **季度偏移**:调整季度编号偏移 - **剧集偏移**:调整剧集编号偏移 - **过滤**:用于种子匹配的自定义正则表达式过滤器 ================================================ FILE: docs/feature/calendar.md ================================================ # 日历视图 从 v3.2 开始,AB 包含日历视图,按播出日期显示您订阅的番剧。 ![Calendar](/image/feature/calendar.png) ## 功能 ### 每周时间表 日历按播出日期(周一至周日)组织显示番剧,另有"未知"列显示没有播出时间数据的番剧。 ### Bangumi.tv 集成 AB 从 Bangumi.tv 获取播出时间数据,以准确显示每部番剧的播出时间。 点击 **刷新时间表** 按钮更新播出数据。 ### 分组显示 从 v3.2 开始,具有多个下载规则的番剧会被分组显示: - 同一番剧只显示一次,即使有多个字幕组规则 - 点击分组番剧可查看所有可用规则 - 选择特定规则进行编辑 这样可以保持日历整洁,同时仍能访问所有规则。 ## 导航 点击日历中的任意番剧海报可: - 查看番剧详情 - 编辑下载规则 - 访问归档/禁用选项 ## 提示 ::: tip 如果番剧出现在"未知"列,可能是 Bangumi.tv 上没有播出数据,或番剧标题无法匹配。 ::: ================================================ FILE: docs/feature/rename.md ================================================ # 文件重命名 AB 目前提供三种重命名方式:`pn`、`advance` 和 `none`。 ### pn `pure name` 的缩写。此方法使用种子下载名称进行重命名。 示例: ``` [Lilith-Raws] 86 - Eighty Six - 01 [Baha][WEB-DL][1080p][AVC AAC][CHT][MKV].mkv >> 86 - Eighty Six S01E01.mkv ``` ### advance 高级重命名。此方法使用父文件夹名称进行重命名。 ``` /downloads/Bangumi/86 - Eighty Six(2023)/Season 1/[Lilith-Raws] 86 - Eighty Six - 01 [Baha][WEB-DL][1080p][AVC AAC][CHT][MKV].mkv >> 86 - Eighty Six(2023) S01E01.mkv ``` ### none 不重命名。文件保持原样。 ## 收藏重命名 AB 支持收藏重命名。收藏重命名需要: - 剧集位于收藏的一级目录中 - 可以从文件名解析出剧集编号 AB 还可以重命名一级目录中的字幕文件。 重命名后,剧集和目录将放置在 `Season` 文件夹中。 重命名后的收藏将被移动并归类到 `BangumiCollection` 下。 ## 剧集偏移 从 v3.2 开始,AB 支持重命名时的剧集偏移。在以下情况下很有用: - RSS 显示的剧集编号与预期不同(例如 S2E01 应该是 S1E29) - 番剧因播出间隔而产生"虚拟季度" 当为番剧配置偏移后,AB 会在重命名时自动应用: ``` 原始:S02E01.mkv 应用偏移(季度:-1,剧集:+28):S01E29.mkv ``` 配置偏移: 1. 点击番剧海报 2. 打开高级设置 3. 设置季度偏移和/或剧集偏移值 4. 或使用"自动检测"让 AB 建议正确的偏移 有关自动检测的更多详情,请参阅[番剧管理](./bangumi.md#episode-offset-auto-detection)。 ================================================ FILE: docs/feature/rss.md ================================================ --- title: RSS 管理 --- # RSS 管理 ## RSS 管理页面 RSS 管理页面显示您所有的 RSS 订阅及其连接状态。 ![RSS Manager](/image/feature/rss-manager.png) ### 连接状态 从 v3.2 开始,AB 会跟踪每个 RSS 源的连接状态: | 状态 | 说明 | |------|------| | **已连接**(绿色) | RSS 源可访问且返回有效数据 | | **错误**(红色) | RSS 源无法响应或返回无效数据 | 当源显示错误时,将鼠标悬停在状态标签上可在提示框中查看错误详情。 AB 会在每次 RSS 刷新周期自动更新连接状态。 ## 添加收藏 AB 提供两种手动下载方式: **收藏** 和 **订阅**。 - **收藏** 一次性下载所有剧集,适用于已完结番剧。 - **订阅** 添加带有对应 RSS 链接的自动下载规则,适用于连载番剧。 ### 解析 RSS 链接 AB 支持解析所有资源站的收藏 RSS 链接。在对应网站找到您想要的番剧收藏 RSS,点击 AB 右上角的 **+** 按钮,在弹出窗口中粘贴 RSS 链接。 ### 添加下载 如果解析成功,将出现一个窗口显示解析后的番剧信息。点击 **收藏** 或 **订阅** 将其添加到下载队列。 ### 常见问题 如果出现解析错误,可能是由于 RSS 链接不正确或字幕组命名格式不支持。 ## 管理番剧 从 v3.0 开始,AB 在 WebUI 中提供手动番剧管理功能,允许您手动调整解析错误的番剧信息。 ### 编辑番剧信息 在番剧列表中,点击番剧海报进入番剧信息页面。 修改信息后,点击 **应用**。 AB 将根据您的更改重新调整目录并自动重命名文件。 ### 删除番剧 从 v3.0 开始,您可以手动删除番剧。点击番剧海报,进入信息页面,点击 **删除**。 ::: warning 删除番剧后,如果 RSS 订阅未取消,AB 仍会重新解析。要禁用下载规则,请使用[禁用番剧](#禁用番剧)。 ::: ### 禁用番剧 从 v3.0 开始,您可以手动禁用番剧。点击番剧海报,进入信息页面,点击 **禁用**。 禁用后,番剧海报将变灰并排序到末尾。要重新启用下载规则,点击 **启用**。 ================================================ FILE: docs/feature/search.md ================================================ # 种子搜索 从 v3.1 开始,AB 包含搜索功能,可快速查找番剧。 ## 使用搜索功能 ::: warning 搜索功能依赖主程序的解析器。当前版本不支持解析收藏。解析收藏时出现 `warning` 是正常现象。 ::: 搜索栏位于 AB 顶栏。点击打开搜索面板。 ![Search Panel](/image/feature/search-panel.png) 选择资源站,输入关键词,AB 将自动解析并显示搜索结果。要添加番剧,点击卡片右侧的添加按钮。 ::: tip 当资源站为 **Mikan** 时,AB 默认使用 `mikan` 解析器。其他资源站使用 TMDB 解析器。 ::: ## 管理搜索源 从 v3.2 开始,您可以直接在设置页面管理搜索源,无需编辑 JSON 文件。 ### 搜索源设置面板 导航至 **配置** → **搜索源** 访问设置面板。 ![Search Provider Settings](/image/feature/search-provider.png) 在这里您可以: - **查看** 所有已配置的搜索源 - **添加** 新搜索源,使用"添加源"按钮 - **编辑** 现有源的 URL - **删除** 自定义源(默认源 mikan、nyaa、dmhy 不能删除) ### URL 模板格式 添加自定义源时,URL 必须包含 `%s` 作为搜索关键词的占位符。 示例: ``` https://example.com/rss/search?q=%s ``` `%s` 将被替换为用户的搜索查询。 ### 默认源 以下源为内置源,不能删除: | 源 | URL 模板 | |----|----------| | mikan | `https://mikanani.me/RSS/Search?searchstr=%s` | | nyaa | `https://nyaa.si/?page=rss&q=%s&c=0_0&f=0` | | dmhy | `http://dmhy.org/topics/rss/rss.xml?keyword=%s` | ### 通过配置文件添加源 您也可以通过编辑 `config/search_provider.json` 手动添加源: ```json { "mikan": "https://mikanani.me/RSS/Search?searchstr=%s", "nyaa": "https://nyaa.si/?page=rss&q=%s&c=0_0&f=0", "dmhy": "http://dmhy.org/topics/rss/rss.xml?keyword=%s", "bangumi.moe": "https://bangumi.moe/rss/search/%s" } ``` ================================================ FILE: docs/home/index.md ================================================ --- title: 关于 ---

## 关于 AutoBangumi

AutoBangumi WebUI

**`AutoBangumi`** 是一款基于 RSS 订阅的全自动番剧下载和整理工具。只需在 [Mikan Project][mikan] 或类似网站订阅番剧,即可自动追踪和下载最新剧集。 整理后的文件名和目录结构可直接兼容 [Plex][plex]、[Jellyfin][jellyfin] 等媒体库软件,无需额外的元数据刮削。 ## 功能特性 - 简单的一次性配置,持续使用 - 无需操心的 RSS 解析器,可提取番剧信息并自动生成下载规则 - 番剧文件整理: ``` Bangumi ├── bangumi_A_title │ ├── Season 1 │ │ ├── A S01E01.mp4 │ │ ├── A S01E02.mp4 │ │ ├── A S01E03.mp4 │ │ └── A S01E04.mp4 │ └── Season 2 │ ├── A S02E01.mp4 │ ├── A S02E02.mp4 │ ├── A S02E03.mp4 │ └── A S02E04.mp4 ├── bangumi_B_title │ └─── Season 1 ``` - 全自动重命名——超过 99% 的番剧文件在重命名后可被媒体库软件直接刮削 ``` [Lilith-Raws] Kakkou no Iinazuke - 07 [Baha][WEB-DL][1080p][AVC AAC][CHT][MP4].mp4 >> Kakkou no Iinazuke S01E07.mp4 ``` - 基于父文件夹名称自定义重命名所有子文件 - 季中追番补全当季所有缺失剧集 - 高度自定义选项,可针对不同媒体库软件进行微调 - 零维护,完全透明运行 - 内置 TMDB 解析器,可生成完整的 TMDB 格式文件和番剧元数据 - 支持 Mikan RSS 订阅的反向代理 ## 社区 - 更新通知:[Telegram 频道](https://t.me/autobangumi_update) - Bug 反馈:[Telegram](https://t.me/+yNisOnDGaX5jMTM9) ## 致谢 感谢 [Sean](https://github.com/findix) 对项目的大力帮助。 ## 参与贡献 欢迎提交 Issues 和 Pull Requests! ## 免责声明 由于 AutoBangumi 通过非官方版权渠道获取番剧: - **请勿**将 AutoBangumi 用于商业用途。 - **请勿**制作包含 AutoBangumi 的视频内容并在国内视频平台(版权相关方)上发布。 - **请勿**将 AutoBangumi 用于任何违反法律法规的活动。 AutoBangumi 仅供学习和个人使用。 ## 许可证 [MIT License](https://github.com/EstrellaXD/Auto_Bangumi/blob/main/LICENSE) [mikan]: https://mikanani.me [plex]: https://plex.tv [jellyfin]: https://jellyfin.org ================================================ FILE: docs/home/pipline.md ================================================ # AutoBangumi 工作原理 AutoBangumi(简称 AB)本质上是一个 RSS 解析器。它从番剧种子站点解析 RSS 订阅源,从种子标题中提取元数据,生成下载规则并发送给 qBittorrent 进行下载。下载完成后,将文件整理成标准的媒体库目录结构。 ## 流程概览 1. **RSS 解析** — AB 定期获取并解析您订阅的 RSS 源 2. **标题分析** — 解析种子标题以提取番剧名称、集数、季度、字幕组和分辨率 3. **规则生成** — 根据解析的信息在 qBittorrent 中创建下载规则 4. **下载管理** — qBittorrent 负责实际的种子下载 5. **文件整理** — 下载的文件被重命名并移动到标准化的目录结构中 6. **媒体库就绪** — 整理后的文件可直接被 Plex、Jellyfin 等媒体服务器识别 ================================================ FILE: docs/index.md ================================================ --- # https://vitepress.dev/reference/default-theme-home-page layout: home title: AutoBangumi titleTemplate: 全自动追番,解放双手! hero: name: AutoBangumi text: 全自动追番,解放双手! tagline: 全自动 RSS 订阅解析、下载管理和文件整理 actions: - theme: brand text: 快速开始 link: /deploy/quick-start - theme: alt text: 关于 link: /home/ - theme: alt text: 更新日志 link: /changelog/3.2 features: - icon: src: /image/icons/rss.png title: RSS 订阅解析 details: 自动识别并解析番剧 RSS 订阅源。无需手动输入,只需订阅即可自动完成解析、下载和整理。 - icon: src: /image/icons/qbittorrent-logo.svg title: qBittorrent 下载器 details: 使用 qBittorrent 下载番剧资源。在 AutoBangumi 中即可管理现有番剧、下载往期番剧以及删除条目。 - icon: src: /image/icons/tmdb-icon.png title: TMDB 元数据匹配 details: 通过 TMDB 匹配番剧信息以获取准确的元数据,确保即使在多个字幕组之间也能正确解析。 - icon: src: /image/icons/plex-icon.png title: Plex / Jellyfin / Infuse ... details: 根据匹配结果自动整理文件名和目录结构,确保媒体库软件能够高成功率地刮削元数据。 ---
## 鸣谢 ### 致谢 感谢 - [Mikan Project](https://mikanani.me) 提供了如此优秀的番剧资源。 - [VitePress](https://vitepress.dev) 提供了优秀的文档框架。 - [qBittorrent](https://www.qbittorrent.org) 提供了优秀的下载器。 - [Plex](https://www.plex.tv) / [Jellyfin](https://jellyfin.org) 提供了优秀的自托管媒体库。 - [Infuse](https://firecore.com/infuse) 提供了优雅的视频播放器。 - [弹弹 Play](https://www.dandanplay.com) 提供了优秀的弹幕播放器。 - 每一个番剧制作组 / 字幕组 / 爱好者。 ### 贡献者 [ ![](https://contrib.rocks/image?repo=EstrellaXD/Auto_Bangumi){class=contributors-avatar} ](https://github.com/EstrellaXD/Auto_Bangumi/graphs/contributors) ## 免责声明 由于 AutoBangumi 通过非官方版权渠道获取番剧: - **请勿**将 AutoBangumi 用于商业用途。 - **请勿**制作包含 AutoBangumi 的视频内容并在国内视频平台(版权相关方)上发布。 - **请勿**将 AutoBangumi 用于任何违反法律法规的活动。
================================================ FILE: docs/ja/api/index.md ================================================ # REST APIリファレンス AutoBangumiは`/api/v1`でREST APIを公開しています。すべてのエンドポイント(ログインとセットアップを除く)はJWT認証が必要です。 **ベースURL:** `http://your-host:7892/api/v1` **認証:** JWTトークンをCookieまたは`Authorization: Bearer `ヘッダーとして含めてください。 **インタラクティブドキュメント:** 開発モードで実行している場合、Swagger UIは`http://your-host:7892/docs`で利用可能です。 --- ## 認証 ### ログイン ``` POST /auth/login ``` ユーザー名とパスワードで認証します。 **リクエストボディ:** ```json { "username": "string", "password": "string" } ``` **レスポンス:** JWTトークン付きの認証Cookieを設定します。 ### トークンのリフレッシュ ``` GET /auth/refresh_token ``` 現在の認証トークンをリフレッシュします。 ### ログアウト ``` GET /auth/logout ``` 認証Cookieをクリアしてログアウトします。 ### 認証情報の更新 ``` POST /auth/update ``` ユーザー名および/またはパスワードを更新します。 **リクエストボディ:** ```json { "username": "string", "password": "string" } ``` --- ## Passkey / WebAuthn WebAuthn/FIDO2 Passkeysを使用したパスワードレス認証。 ### Passkeyの登録 ``` POST /passkey/register/options ``` WebAuthn登録オプション(チャレンジ、リライングパーティ情報)を取得します。 ``` POST /passkey/register/verify ``` ブラウザからのPasskey登録レスポンスを検証して保存します。 ### Passkeyで認証 ``` POST /passkey/auth/options ``` WebAuthn認証チャレンジオプションを取得します。 ``` POST /passkey/auth/verify ``` Passkey認証レスポンスを検証し、JWTトークンを発行します。 ### Passkeyの管理 ``` GET /passkey/list ``` 現在のユーザーの登録済みPasskeyをすべてリストします。 ``` POST /passkey/delete ``` クレデンシャルIDで登録済みPasskeyを削除します。 --- ## 設定 ### 設定の取得 ``` GET /config/get ``` 現在のアプリケーション設定を取得します。 **レスポンス:** `program`、`downloader`、`rss_parser`、`bangumi_manager`、`notification`、`proxy`、`experimental_openai`セクションを含む完全な設定オブジェクト。 ### 設定の更新 ``` PATCH /config/update ``` アプリケーション設定を部分的に更新します。変更したいフィールドのみを含めてください。 **リクエストボディ:** 部分的な設定オブジェクト。 --- ## 番組(アニメルール) ### すべての番組をリスト ``` GET /bangumi/get/all ``` すべてのアニメダウンロードルールを取得します。 ### IDで番組を取得 ``` GET /bangumi/get/{bangumi_id} ``` IDで特定のアニメルールを取得します。 ### 番組の更新 ``` PATCH /bangumi/update/{bangumi_id} ``` アニメルールのメタデータ(タイトル、シーズン、エピソードオフセットなど)を更新します。 ### 番組の削除 ``` DELETE /bangumi/delete/{bangumi_id} ``` 単一のアニメルールと関連するトレントを削除します。 ``` DELETE /bangumi/delete/many/ ``` 複数のアニメルールを一括削除します。 **リクエストボディ:** ```json { "bangumi_ids": [1, 2, 3] } ``` ### 番組の無効化 / 有効化 ``` DELETE /bangumi/disable/{bangumi_id} ``` アニメルールを無効化します(ファイルは保持、ダウンロード停止)。 ``` DELETE /bangumi/disable/many/ ``` 複数のアニメルールを一括無効化します。 ``` GET /bangumi/enable/{bangumi_id} ``` 以前に無効化されたアニメルールを再有効化します。 ### ポスターのリフレッシュ ``` GET /bangumi/refresh/poster/all ``` すべてのアニメのポスター画像をTMDBからリフレッシュします。 ``` GET /bangumi/refresh/poster/{bangumi_id} ``` 特定のアニメのポスター画像をリフレッシュします。 ### カレンダー ``` GET /bangumi/refresh/calendar ``` Bangumi.tvからアニメ放送カレンダーデータをリフレッシュします。 ### すべてリセット ``` GET /bangumi/reset/all ``` すべてのアニメルールを削除します。注意して使用してください。 --- ## RSSフィード ### すべてのフィードをリスト ``` GET /rss ``` 設定されたすべてのRSSフィードを取得します。 ### フィードの追加 ``` POST /rss/add ``` 新しいRSSフィード購読を追加します。 **リクエストボディ:** ```json { "url": "string", "aggregate": true, "parser": "mikan" } ``` ### フィードの有効化 / 無効化 ``` POST /rss/enable/many ``` 複数のRSSフィードを有効化します。 ``` PATCH /rss/disable/{rss_id} ``` 単一のRSSフィードを無効化します。 ``` POST /rss/disable/many ``` 複数のRSSフィードを一括無効化します。 ### フィードの削除 ``` DELETE /rss/delete/{rss_id} ``` 単一のRSSフィードを削除します。 ``` POST /rss/delete/many ``` 複数のRSSフィードを一括削除します。 ### フィードの更新 ``` PATCH /rss/update/{rss_id} ``` RSSフィードの設定を更新します。 ### フィードのリフレッシュ ``` GET /rss/refresh/all ``` すべてのRSSフィードのリフレッシュを手動でトリガーします。 ``` GET /rss/refresh/{rss_id} ``` 特定のRSSフィードをリフレッシュします。 ### フィードからトレントを取得 ``` GET /rss/torrent/{rss_id} ``` 特定のRSSフィードから解析されたトレントのリストを取得します。 ### 分析と購読 ``` POST /rss/analysis ``` RSS URLを分析し、購読せずにアニメのメタデータを抽出します。 **リクエストボディ:** ```json { "url": "string" } ``` ``` POST /rss/collect ``` RSSフィードからすべてのエピソードをダウンロードします(完結したアニメ用)。 ``` POST /rss/subscribe ``` 自動継続ダウンロード用にRSSフィードを購読します。 --- ## 検索 ### 番組検索(Server-Sent Events) ``` GET /search/bangumi?keyword={keyword}&provider={provider} ``` アニメのトレントを検索します。リアルタイム更新のためにServer-Sent Events(SSE)ストリームとして結果を返します。 **クエリパラメータ:** - `keyword` — 検索キーワード - `provider` — 検索プロバイダー(例:`mikan`、`nyaa`、`dmhy`) **レスポンス:** 解析された検索結果を含むSSEストリーム。 ### 検索プロバイダーのリスト ``` GET /search/provider ``` 利用可能な検索プロバイダーのリストを取得します。 --- ## プログラム制御 ### ステータスの取得 ``` GET /status ``` バージョン、実行状態、first_runフラグを含むプログラムステータスを取得します。 **レスポンス:** ```json { "status": "running", "version": "3.2.0", "first_run": false } ``` ### プログラムの開始 ``` GET /start ``` メインプログラム(RSSチェック、ダウンロード、リネーム)を開始します。 ### プログラムの再起動 ``` GET /restart ``` メインプログラムを再起動します。 ### プログラムの停止 ``` GET /stop ``` メインプログラムを停止します(WebUIはアクセス可能なまま)。 ### シャットダウン ``` GET /shutdown ``` アプリケーション全体をシャットダウンします(Dockerコンテナを再起動します)。 ### ダウンローダーのチェック ``` GET /check/downloader ``` 設定されたダウンローダー(qBittorrent)への接続をテストします。 --- ## ダウンローダー管理 AutoBangumiから直接ダウンローダー内のトレントを管理します。 ### トレントのリスト ``` GET /downloader/torrents ``` Bangumiカテゴリ内のすべてのトレントを取得します。 ### トレントの一時停止 ``` POST /downloader/torrents/pause ``` ハッシュでトレントを一時停止します。 **リクエストボディ:** ```json { "hashes": ["hash1", "hash2"] } ``` ### トレントの再開 ``` POST /downloader/torrents/resume ``` ハッシュで一時停止したトレントを再開します。 **リクエストボディ:** ```json { "hashes": ["hash1", "hash2"] } ``` ### トレントの削除 ``` POST /downloader/torrents/delete ``` オプションでファイル削除を伴うトレントを削除します。 **リクエストボディ:** ```json { "hashes": ["hash1", "hash2"], "delete_files": false } ``` --- ## セットアップウィザード これらのエンドポイントは、初回実行セットアップ中(セットアップ完了前)にのみ利用可能です。認証は**不要**です。セットアップ完了後、すべてのエンドポイントは`403 Forbidden`を返します。 ### セットアップステータスの確認 ``` GET /setup/status ``` セットアップウィザードが必要かどうか(初回実行)を確認します。 **レスポンス:** ```json { "need_setup": true } ``` ### ダウンローダー接続のテスト ``` POST /setup/test-downloader ``` 提供された認証情報でダウンローダーへの接続をテストします。 **リクエストボディ:** ```json { "type": "qbittorrent", "host": "172.17.0.1:8080", "username": "admin", "password": "adminadmin", "ssl": false } ``` ### RSSフィードのテスト ``` POST /setup/test-rss ``` RSS URLがアクセス可能で解析可能か検証します。 **リクエストボディ:** ```json { "url": "https://mikanime.tv/RSS/MyBangumi?token=xxx" } ``` ### 通知のテスト ``` POST /setup/test-notification ``` 提供された設定でテスト通知を送信します。 **リクエストボディ:** ```json { "type": "telegram", "token": "bot_token", "chat_id": "chat_id" } ``` ### セットアップの完了 ``` POST /setup/complete ``` すべての設定を保存し、セットアップを完了としてマークします。センチネルファイル`config/.setup_complete`を作成します。 **リクエストボディ:** 完全な設定オブジェクト。 --- ## ログ ### ログの取得 ``` GET /log ``` 完全なアプリケーションログファイルを取得します。 ### ログのクリア ``` GET /log/clear ``` ログファイルをクリアします。 --- ## レスポンス形式 すべてのAPIレスポンスは一貫した形式に従います: ```json { "msg_en": "Success message in English", "msg_zh": "Success message in Chinese", "status": true } ``` エラーレスポンスには、両方の言語でのエラーメッセージとともに適切なHTTPステータスコード(400、401、403、404、500)が含まれます。 ================================================ FILE: docs/ja/changelog/2.6.md ================================================ # [2.6] リリースノート ## 古いバージョンからのアップグレードノート バージョン2.6以降、AutoBangumi(AB)の設定は環境変数から`config.json`に移動しました。アップグレード前に以下の点に注意してください。 ### 環境変数の移行 古い環境変数は、2.6へのアップグレード後の初回起動時に自動的に`config.json`に変換されます。生成された`config.json`は`/app/config`フォルダに配置されます。 `/app/config`フォルダをマッピングすると、古い環境変数はABの動作に影響しなくなります。`config.json`を削除すると、環境変数から再生成できます。 ### コンテナボリュームマッピング バージョン2.6以降、以下のフォルダをマッピングする必要があります: - `/app/config`:`config.json`を含む設定フォルダ - `/app/data`:`bangumi.json`などを含むデータフォルダ ### データファイル 大きな更新があるため、古いデータファイルの使用は推奨しません。ABは自動的に`/app/data`に新しい`bangumi.json`を生成します。 心配しないでください — QBは以前にダウンロードしたアニメを再ダウンロードしません。 ### 以降の設定変更 ABはWebUIで直接設定を編集できるようになりました。編集後、変更を有効にするにはコンテナを再起動してください。 ## アップグレード方法 ### Docker Compose 既存のdocker-compose.ymlファイルを使用してアップグレードできます: ```bash docker compose stop autobangumi docker compose pull autobangumi ``` 次に、docker-compose.ymlを変更してボリュームマッピングを追加します: ```yaml version: "3.8" services: autobangumi: image: estrellaxd/auto_bangumi:latest container_name: autobangumi restart: unless-stopped environment: - PUID=1000 - PGID=1000 - TZ=Asia/Shanghai volumes: - /path/to/config:/app/config - /path/to/data:/app/data networks: - bridge dns: - 8.8.8.8 ``` その後ABを起動します: ```bash docker compose up -d autobangumi ``` ### Portainer Portainerで、ボリュームマッピングを変更し、`Recreate`をクリックしてアップグレードを完了します。 ### アップグレードで問題が発生した場合 設定は様々であるため、アップグレードによりプログラムが失敗する場合があります。以前のすべてのデータと生成された設定ファイルを削除し、コンテナを再起動してWebUIで再設定してください。 ## 新機能 ### 設定方法の変更 v2.6以降、プログラム設定はDocker環境変数から`config.json`に移動しました。 新しいWebUIはWebベースの設定エディターも提供します。AB URLにアクセスし、サイドバーで`設定`を見つけて設定を変更してください。編集後はコンテナを再起動してください。 ### カスタムリバースプロキシURLとABのプロキシリレー [Mikan Project](https://mikanani.me)にアクセスできない状況に対処するため、ABは3つのアプローチを提供します: 1. HTTPとSOCKSプロキシ この機能は古いバージョンに存在しました。2.6にアップグレードした後、WebUIでプロキシ設定を確認するだけでMikan Projectに正常にアクセスできます。 ただし、qBittorrentはまだMikanのRSSとトレントURLに直接アクセスできないため、qBittorrentにもプロキシを追加する必要があります。詳細は#198を参照してください。 2. カスタムリバースプロキシURL バージョン2.6ではカスタムリバースプロキシURL用の`custom_url`オプションが追加されました。 適切に設定されたリバースプロキシURLに設定してください。ABはこのカスタムURLを使用してMikan Projectにアクセスし、QBは正常にダウンロードできます。 3. ABのプロキシリレー ABでプロキシを設定した後、ABはローカルプロキシリレーとして機能できます(現在はRSS関連機能のみ)。 `custom_url`を`http://abhost:abport`に設定します。ここで`abhost`はABのIP、`abport`はABのポートです。 ABは自分のアドレスをqBittorrentにプッシュし、qBittorrentはABをプロキシとして使用してMikan Projectにアクセスします。 注意:NginxなどでABのリバースプロキシを設定していない場合は、適切な動作を確保するために`http://`を含めてください。 **重要な注意事項** ABとQBが同じコンテナにある場合、`127.0.0.1`や`localhost`を使用しないでください。この方法では通信できません。 同じネットワーク上にある場合は、コンテナ名アドレッシングを使用してください。例:`http://autobangumi:7892`。 Dockerゲートウェイアドレスも使用できます。例:`http://172.17.0.1:7892`。 異なるホストにある場合は、ホストマシンのIPアドレスを使用してください。 ### コレクションとフォルダのリネーム ABはコレクションとフォルダ内のファイルをリネームし、メディアファイルをルートディレクトリに戻すことができるようになりました。 ABは保存パスに依存してシーズンとエピソード情報を決定するため、ABの標準に従ってコレクションファイルを配置してください。 バージョン**2.6.4**以降、ABはフォルダ内の字幕をリネームできます(機能はまだ改善中)。コレクションと字幕はデフォルトで`pn`形式リネームです。調整オプションはまだ利用できません。 **標準パス** ``` /downloads/Bangumi/Title/Season 1/xxx ``` ### プッシュ通知 ABは`Telegram`と`ServerChan`を介してリネーム完了通知を送信できるようになりました。 WebUIで、プッシュ通知を有効にし、必要なパラメータを入力してください。 - Telegramには Bot TokenとChat IDが必要です。取得方法については様々なチュートリアルを参照してください。 - ServerChanにはTokenが必要です。取得方法については様々なチュートリアルを参照してください。 ================================================ FILE: docs/ja/changelog/3.0.md ================================================ # [3.0] リリースノート ### 新しいWebUI - ログイン機能 — ABがユーザー名/パスワード認証をサポート。一部の操作にはログインが必要。 - 新しいポスターウォール - 番組管理機能 - アニメシーズン情報と名前を編集。変更は自動的に**ダウンロードルール** / **ダウンロード済みファイルパス**を更新し、リネームをトリガー。 - 新しいリンクパーサー — リンクを解析した後、ダウンロード情報を手動で調整、ダウンロードシーズンを選択、または自動ダウンロードルールを追加可能。 - アニメ削除 — アニメとそのトレントファイルをワンクリックで削除。 - アニメごとのカスタムダウンロードルール、グローバルルールから独立。 - より簡単なアプリケーションルール設定のための新しい設定インターフェース - 初回起動ガイダンス用の初期化ページを追加 - qBittorrent接続用のダウンローダー接続チェッカー - RSSフィードが有効かどうかをチェックするRSS URLバリデーター - WebUIからプログラムの開始/停止とコンテナの再起動のためのプログラム管理ボタンを追加 ### パーサー - 公式タイトルとポスターURLを取得するための異なるソースタイプをサポートする新しいパーサー - データベースを再生成せずにRSS購読ソースの変更をサポート ### 通知モジュール - `Bark`通知モジュールを追加 - 新しい通知形式 — ポスター、アニメ名、更新されたエピソード番号をTelegramにプッシュ可能に ### データ移行 - 古いバージョンからアップグレード時の自動データ移行 - 移行されたデータも自動的にポスターをマッチング ## 修正 - Windowsパスで発生する可能性のあるリネームバグを修正 ## 変更 - データストレージを`json`から`sqlite`に移行 - マルチプロセッシングからマルチスレッドに移行 - メインプログラムのリファクタリング - 起動/シャットダウン時間の改善 - パーサーモジュールのリファクタリング - リネームモジュールのリファクタリング - 一時的に`normal`モードを削除 - `ghcr.io`イメージレジストリを追加 ================================================ FILE: docs/ja/changelog/3.1.md ================================================ # [3.1] - 2023-08 - バックエンドとフロントエンドリポジトリを統合、プロジェクトディレクトリ構造を最適化 - バージョンリリースワークフローを最適化 - WikiをVitePressに移行:https://autobangumi.org ## バックエンド ### 機能 - `RSS Engine`モジュールを追加 — ABが独立してRSS購読を更新・管理し、トレントをダウンローダーに送信可能に - RSS Engineモジュールで管理される複数の集約RSS購読ソースをサポート - ダウンロード重複排除 — 重複して購読されたトレントは再ダウンロードされない - RSS購読の手動リフレッシュAPIを追加 - RSS購読管理APIを追加 - `Search Engine`モジュールを追加 — キーワードでトレントを検索し、結果をコレクションまたは購読タスクとして解析 - `mikan`、`dmhy`、`nyaa`をサポートするプラグインベースの検索エンジン - 個別グループ設定用の字幕グループ固有ルールを追加 - IPv6リスニングサポートを追加(環境変数で`IPV6=1`を設定) - ルールとRSS購読の一括管理用バッチ操作APIを追加 ### 変更 - データベース構造を`sqlmodel`に変更してデータベース管理 - シームレスなソフトウェアデータ更新のためにバージョン管理を追加 - API形式を統一 - APIレスポンス言語オプションを追加 - データベースモックテストを追加 - コード最適化 ### バグ修正 - 様々な小さな問題を修正 - いくつかの大きな問題を導入 ## フロントエンド ### 機能 - `i18n`サポートを追加 — 現在`zh-CN`と`en-US`をサポート - PWAサポートを追加 - RSS管理ページを追加 - 検索トップバーを追加 ### 変更 - 様々なUI詳細を調整 ================================================ FILE: docs/ja/changelog/3.2.md ================================================ # [3.2] - 2025-01 ## バックエンド ### 機能 - WebAuthn Passkeyパスワードレスログインサポートを追加 - Passkeyクレデンシャルの登録、認証、管理 - マルチデバイスクレデンシャルバックアップ検出(iCloud Keychainなど) - クローン攻撃保護(sign_count検証) - パスワードとPasskeyログインインターフェースを統合する認証戦略パターン - 検出可能なクレデンシャル(レジデントキー)によるユーザー名なしログインサポート - シーズン/エピソードオフセットの自動検出を追加 - TMDBエピソード放送日を分析して「仮想シーズン」を検出(例:フリーレンS1が2つのパートに分割) - 放送ギャップが6ヶ月を超える場合に異なるパートを自動識別 - エピソードオフセットを計算(例:RSSがS2E1を表示 → TMDB S1E29) - バックグラウンドスキャンスレッドが既存の購読のオフセット問題を自動検出 - 新しいAPIエンドポイント:`POST /bangumi/detect-offset`、`PATCH /bangumi/dismiss-review/{id}` - 番組アーカイブ機能を追加 - 手動アーカイブ/アーカイブ解除サポート - 完結シリーズの自動アーカイブ - 新しいAPIエンドポイント:`PATCH /bangumi/archive/{id}`、`PATCH /bangumi/unarchive/{id}`、`GET /bangumi/refresh/metadata` - 検索プロバイダー設定APIを追加 - `GET /search/provider/config` - 検索プロバイダー設定を取得 - `PUT /search/provider/config` - 検索プロバイダー設定を更新 - RSS接続ステータス追跡を追加 - 各リフレッシュ後に`connection_status`(healthy/error)、`last_checked_at`、`last_error`を記録 - 初回実行セットアップウィザードを追加 - 7ステップのガイド付き設定:アカウント、ダウンローダー、RSSソース、メディアパス、通知 - ダウンローダー接続テスト、RSSソース検証 - オプションのステップはスキップして後で設定で構成可能 - センチネルファイルメカニズム(`config/.setup_complete`)で再トリガーを防止 - 認証不要のセットアップAPI(初回実行時のみ利用可能、完了後は403を返す) - Bangumi.tv放送スケジュール連携付きカレンダービューを追加 - ダウンローダーAPIと管理インターフェースを追加 - 完全な非同期移行 - データベースレイヤーの非同期サポート(aiosqlite)でPasskey操作のノンブロッキングI/O - `UserDatabase`は後方互換性のためsync/asyncの両モードをサポート - `Database`コンテキストマネージャーは`with`(sync)と`async with`(async)の両方をサポート - RSSエンジン、ダウンローダー、チェッカー、パーサーを完全に非同期に変換 - ネットワークリクエストを`requests`から`httpx`(AsyncClient)に移行 - バックエンドを`uv`パッケージマネージャーに移行(pyproject.toml + uv.lock) - サーバー起動がバックグラウンドタスクを使用してブロッキングを回避(#891、#929を修正) - データベースマイグレーションがNULL値をモデルデフォルトで自動埋め - データベースにオフセット検出用の`needs_review`と`needs_review_reason`フィールドを追加 ### パフォーマンス - 共有HTTPクライアント接続プール、TCP/SSL接続を再利用 - RSSリフレッシュが並行(`asyncio.gather`)、複数ソースで約10倍高速化 - トレントファイルダウンロードが並行、複数トレントで約5倍高速化 - リネームモジュールのファイルリスト取得が並行、約20倍高速化 - 通知送信が並行、2秒のハードコードされた遅延を削除 - TMDBとMikanパーサー結果のキャッシュを追加して重複API呼び出しを回避 - `Torrent.url`、`Torrent.rss_id`、`Bangumi.title_raw`、`Bangumi.deleted`、`RSSItem.url`にデータベースインデックスを追加 - RSSの一括有効化/無効化がアイテムごとのコミットではなく単一トランザクションを使用 - トレント名解析とフィルターマッチング用にプリコンパイルされた正規表現パターン - `SeasonCollector`がループ外で作成され、単一の認証を再利用 - RSS解析の重複排除がO(n²)のリストルックアップからO(1)のセットルックアップに変更 - `Episode`/`SeasonInfo`データクラスがメモリフットプリント削減のため`__slots__`を使用 ### 変更 - WebAuthn依存関係をpy_webauthn 2.7.0にアップグレード - `_get_webauthn_from_request`がブラウザのOriginヘッダーを優先、クロスポート開発環境での検証問題を修正 - `auth_user`と`update_user_info`を非同期関数に変換 - `TitleParser.tmdb_parser`を非同期関数に変換 - `RSSEngine`メソッドを完全に非同期化(`pull_rss`、`refresh_rss`、`download_bangumi`、`add_rss`) - `Checker.check_downloader`を非同期関数に変換 - `ProgramStatus`がthreadingからasyncioに移行(Event、Lock) ### バグ修正 - 最大リトライ制限付きダウンローダー接続チェックを修正 - トレント追加時の一時的なネットワークエラーをリトライロジックで修正 - 検索と購読フローの複数の問題を修正 - トレント取得の信頼性とエラー処理を改善 - `aaguid`型エラーを修正(py_webauthn 2.7.0では`str`、`bytes`ではない) - 欠落していた`credential_backup_eligible`フィールドを修正(`credential_device_type`に置換) - `verify_authentication_response`が無効な`credential_id`パラメータを受け取りTypeErrorを引き起こす問題を修正 - プログラム起動がサーバーをブロックする問題を修正(#891、#929、#886、#917、#946を修正) - 検索インターフェースのエクスポートがコンポーネントの期待と一致しない問題を修正 - ポスターエンドポイントのパスチェックがすべてのリクエストを誤ってインターセプトする問題を修正(#933、#934を修正) - OpenAIパーサーのセキュリティ問題を修正 - 非同期セッションを使用するデータベーステストと同期コードの不一致を修正 - 3.1.xから3.2へのアップグレード時の設定フィールドの競合による設定喪失を修正(#956を修正) - `program.sleep_time` / `program.times`が`rss_time` / `rename_time`に自動移行 - 非推奨の`rss_parser`フィールド(`type`、`custom_url`、`token`、`enable_tmdb`)を削除 - `ENV_TO_ATTR`環境変数マッピングが存在しないモデルフィールドを指す問題を修正 - `DEFAULT_SETTINGS`と現在の設定モデルの不整合を修正 - バージョンアップグレードマイグレーションロジックエラーを修正(すべてのアップグレードが3.0→3.1マイグレーションを呼び出していた) - ソースバージョンに基づくバージョン対応マイグレーションディスパッチを追加 - データベーススキーマ変更用の`from_31_to_32()`マイグレーション関数を追加 ## フロントエンド ### 機能 - 完全なUIデザインシステムの再設計 - 統一されたデザイントークン(色、フォント、間隔、シャドウ、アニメーション) - ライト/ダークテーマ切り替えサポート - 包括的なアクセシビリティサポート(ARIA、キーボードナビゲーション、フォーカス管理) - モバイルデバイス用レスポンシブレイアウト - 初回実行セットアップウィザードページを追加 - マルチステップウィザードコンポーネント(プログレスバー + ステップナビゲーション) - ルートガードの自動検出とセットアップページへのリダイレクト - ダウンローダー/RSS/通知接続テストフィードバック - 中国語と英語のi18nサポート - Passkey管理パネルを追加(設定ページ) - WebAuthnブラウザサポート検出 - 自動デバイス名識別 - Passkeyリスト表示と削除 - ログインページにPasskey指紋ログインボタンを追加(ユーザー名なしログインをサポート) - カレンダービューページを追加 - ダウンローダー管理ページを追加 - 番組カードホバーオーバーレイを追加(タイトルとタグを表示) - 外部URLとローカルパス処理を統一する`resolvePosterUrl`ユーティリティ関数を追加(#934を修正) - モーダルとフィルターシステムで検索パネルを再設計 - モダンなグラスモーフィズムスタイルでログインパネルを再設計 - ログビューにログレベルフィルターを追加 - LLM設定パネルを再設計(#938を修正) - 設定、ダウンローダー、プレーヤー、ログページのスタイルを再設計 - 検索プロバイダー設定パネルを追加 - UIで検索ソースの表示、追加、編集、削除 - デフォルトソース(mikan、nyaa、dmhy)は削除不可 - URLテンプレート検証で`%s`プレースホルダーを確認 - iOSスタイルの通知バッジシステムを追加 - レビューが必要な購読に黄色バッジ + 紫の枠線 - 組み合わせ表示サポート(例:警告 + 複数ルールに`! | 2`) - 注意が必要なカードに黄色のグローアニメーション - ワンクリック自動検出と却下機能付きの編集モーダル警告バナー - ルール選択モーダルで警告のあるルールをハイライト - カレンダーページの番組グループ化:複数ルールの同じアニメをマージ、クリックで特定のルールを選択 - 番組リストページに折りたたみ可能な「アーカイブ済み」セクション - 番組リストページにスケルトンローディングアニメーション - ルールエディターのエピソードオフセットフィールドに「自動検出」ボタン - RSS管理ページの接続ステータスラベル:正常時は緑の「接続済み」、エラー時は赤で詳細ツールチップ - 新しいモバイルファーストレスポンシブデザイン - 3層ブレークポイントシステム:モバイル(<640px)、タブレット(640-1023px)、デスクトップ(≥1024px) - モバイル下部ナビゲーションバー(アイコンとテキストラベル付き) - タブレットミニサイドバー(56pxアイコンナビゲーション) - モバイルポップアップが自動的にボトムシートに切り替わる - プルトゥリフレッシュサポート - 水平スワイプコンテナサポート - モバイルカードリストがデータテーブルを置き換え(RSSページ) - CSSグリッドレスポンシブレイアウト(番組カードグリッド) - モバイルでフォームラベルが垂直にスタック、入力がフル幅 - タッチターゲット最小44px、アクセシビリティ標準を満たす - セーフエリアサポート(ノッチデバイス) - `100dvh`動的ビューポート高さ(モバイルブラウザアドレスバー問題を修正) - フルスクリーンデバイス用`viewport-fit=cover` ### 新しいコンポーネント - `ab-bottom-sheet` — タッチ駆動のボトムシートコンポーネント(ドラッグで閉じる、最大高さ制限) - `ab-adaptive-modal` — アダプティブモーダル(モバイルでボトムシート / デスクトップで中央ダイアログ) - `ab-pull-refresh` — プルトゥリフレッシュラッパーコンポーネント - `ab-swipe-container` — 水平スワイプコンテナ(CSSスクロールスナップ) - `ab-data-list` — モバイルフレンドリーなカードリスト(NDataTableを置き換え) - `ab-mobile-nav` — 強化された下部ナビゲーションバー(アイコン + ラベル + アクティブインジケーター) - `useSafeArea` — セーフエリアコンポーザブル ### パフォーマンス - ダウンローダーストアが大規模配列でのディープリアクティブプロキシを回避するため`ref`の代わりに`shallowRef`を使用 - テーブル列定義を`computed`に移動して各レンダリングでの再構築を回避 - RSSテーブル列をデータから分離、データ変更時に列設定を再構築しない - カレンダーページの重複した`getAll()`呼び出しを削除 - `ab-select`の`watchEffect`を`watch`に変更、マウント時の無効なemitを排除 - `useClipboard`をストアトップレベルに引き上げ、各`copy()`での新しいインスタンス作成を回避 - `setInterval`を自動ライフサイクル管理のため`useIntervalFn`に置換 ### 変更 - 検索ロジックをリファクタリング、rxjs依存関係を削除 - 検索ストアエクスポートをコンポーネントの期待に一致するようリファクタリング - フロントエンド依存関係をアップグレード - ブレークポイントシステムを単一の1024pxから640px + 1024pxの2層に拡張 - `useBreakpointQuery`に`isTablet`、`isMobileOrTablet`、`isTabletOrPC`を追加 - `media-query.vue`に`#tablet`スロットを追加(`#mobile`にフォールバック) - UnoCSSに`sm: 640px`ブレークポイントを追加 - `ab-input`のモバイルフル幅 + タッチターゲット増加スタイリング - レイアウトが`vh`単位の代わりに`dvh`単位を使用、safe-area-insetをサポート - カレンダーページの不明な列幅を修正 - ダウンローダーページのアクションバーボタンサイズを統一 ## CI/インフラ - CIがPRオープン時にビルドテストを追加(devブランチからmainへのPRが自動的にビルドをトリガー) - CIが`actions/upload-artifact`と`actions/download-artifact`をv4にアップグレード - Dockerビルドから`linux/arm/v7`プラットフォームを削除(uvイメージがサポートしていない) - CLAUDE.md開発ガイドを追加 ================================================ FILE: docs/ja/config/downloader.md ================================================ # ダウンローダー設定 ## WebUI設定 ![downloader](/image/config/downloader.png){width=500}{class=ab-shadow-card}
- **ダウンローダータイプ**はダウンローダーの種類です。現在はqBittorrentのみサポートされています。 - **ホスト**はダウンローダーのアドレスです。[下記参照](#ダウンローダーアドレス) - **ダウンロードパス**はダウンローダーのマッピングされたダウンロードパスです。[下記参照](#ダウンロードパスの問題) - **SSL**はダウンローダー接続のSSLを有効にします。 ## よくある問題 ### ダウンローダーアドレス ::: warning 注意 ダウンローダーアドレスに127.0.0.1またはlocalhostを使用しないでください。 ::: 公式チュートリアルではABは**Bridge**モードのDockerで実行されるため、127.0.0.1またはlocalhostを使用するとダウンローダーではなくAB自体に解決されます。 - qBittorrentもDockerで実行している場合は、Dockerの**ゲートウェイアドレス:172.17.0.1**の使用を推奨します。 - qBittorrentがホストマシンで実行されている場合は、ホストマシンのIPアドレスを使用してください。 ABを**Host**モードで実行している場合は、Dockerゲートウェイアドレスの代わりに127.0.0.1を使用できます。 ::: warning 注意 Macvlanはコンテナネットワークを分離します。追加のブリッジ設定なしでは、コンテナは他のコンテナやホスト自体にアクセスできません。 ::: ### ダウンロードパスの問題 ABで設定されたパスは、対応するアニメファイルパスを生成するためにのみ使用されます。AB自体はそのパスのファイルを直接管理しません。 **ダウンロードパスには何を入力すればよいですか?** このパラメータは**ダウンローダー**の設定と一致させるだけです: - Docker:qBが`/downloads`を使用している場合は、`/downloads/Bangumi`に設定します。`Bangumi`は任意の名前に変更できます。 - Linux/macOS:`/home/usr/downloads`または`/User/UserName/Downloads`の場合は、末尾に`/Bangumi`を追加するだけです。 - Windows:`D:\Media\`を`D:\Media\Bangumi`に変更します ## `config.json`設定オプション 設定ファイルの対応するオプションは以下のとおりです: 設定セクション:`downloader` | パラメータ | 説明 | タイプ | WebUIオプション | デフォルト | |-----------|-------------------|---------|----------------------|---------------------| | type | ダウンローダータイプ | 文字列 | ダウンローダータイプ | qbittorrent | | host | ダウンローダーアドレス | 文字列 | ダウンローダーアドレス | 172.17.0.1:8080 | | username | ダウンローダーユーザー名 | 文字列 | ダウンローダーユーザー名 | admin | | password | ダウンローダーパスワード | 文字列 | ダウンローダーパスワード | adminadmin | | path | ダウンロードパス | 文字列 | ダウンロードパス | /downloads/Bangumi | | ssl | SSL有効 | ブール値 | SSL有効 | false | ================================================ FILE: docs/ja/config/experimental.md ================================================ # 実験的機能 ::: warning 実験的機能はまだテスト中です。有効にすると予期しない問題が発生する可能性があり、将来のバージョンで削除される可能性があります。注意して使用してください! ::: ## OpenAI ChatGPT より良い構造化タイトル解析のためにOpenAI ChatGPTを使用します。例: ``` input: "【喵萌奶茶屋】★04月新番★[夏日重现/Summer Time Rendering][11][1080p][繁日双语][招募翻译]" output: '{"group": "喵萌奶茶屋", "title_en": "Summer Time Rendering", "resolution": "1080p", "episode": 11, "season": 1, "title_zh": "夏日重现", "sub": "", "title_jp": "", "season_raw": "", "source": ""}' ``` ![experimental OpenAI](/image/config/experimental-openai.png){width=500}{class=ab-shadow-card} - **OpenAI有効**はOpenAIを有効にし、タイトル解析にChatGPTを使用します。 - **OpenAI APIタイプ**はデフォルトでOpenAIです。 - **OpenAI APIキー**はOpenAIアカウントのAPIキーです。 - **OpenAI APIベースURL**はOpenAIエンドポイントです。デフォルトでは公式OpenAI URLですが、互換性のあるサードパーティエンドポイントに変更できます。 - **OpenAIモデル**はChatGPTモデルパラメータです。現在`gpt-3.5-turbo`を提供しており、適切なプロンプトで手頃な価格で優れた結果を生成します。 ## Microsoft Azure OpenAI ![experimental Microsoft Azure OpenAI](/image/config/experimental-azure-openai.png){width=500}{class=ab-shadow-card} 標準のOpenAIに加えて、[バージョン3.1.8](https://github.com/EstrellaXD/Auto_Bangumi/releases/tag/3.1.8)でMicrosoft Azure OpenAIサポートが追加されました。使用方法は標準のOpenAIと同様で、一部の共有パラメータがありますが、以下の点に注意してください: - **OpenAI有効**はOpenAIを有効にし、タイトル解析にChatGPTを使用します。 - **OpenAI APIタイプ** — Azure固有のオプションを表示するには`azure`を選択します。 - **OpenAI APIキー**はMicrosoft Azure OpenAI APIキーです。 - **OpenAI APIベースURL**はMicrosoft Azure OpenAIエントリーポイントに対応します。**手動で入力する必要があります**。 - **Azure OpenAIバージョン**はAPIバージョンです。デフォルトは`2023-05-15`です。[サポートされているバージョン](https://learn.microsoft.com/ja-jp/azure/ai-services/openai/reference#completions)を参照してください。 - **Azure OpenAIデプロイメントID**はデプロイメントIDで、通常はモデル名と同じです。Azure OpenAIは`_-`以外の記号をサポートしていないため、`gpt-3.5-turbo`はAzureでは`gpt-35-turbo`になることに注意してください。**手動で入力する必要があります**。 参考ドキュメント: - [クイックスタート:Azure OpenAI ServiceでGPT-35-TurboとGPT-4の使用を開始する](https://learn.microsoft.com/ja-jp/azure/ai-services/openai/chatgpt-quickstart?tabs=command-line&pivots=programming-language-python) - [GPT-35-TurboとGPT-4モデルの操作方法を学ぶ](https://learn.microsoft.com/ja-jp/azure/ai-services/openai/how-to/chatgpt?pivots=programming-language-chat-completions) ## `config.json`設定オプション 設定ファイルの対応するオプションは以下のとおりです: 設定セクション:`experimental_openai` | パラメータ | 説明 | タイプ | WebUIオプション | デフォルト | |---------------|---------------------------|---------|----------------------------|------------------------------| | enable | OpenAIパーサー有効 | ブール値 | OpenAI有効 | false | | api_type | OpenAI APIタイプ | 文字列 | APIタイプ (`openai`/`azure`) | openai | | api_key | OpenAI APIキー | 文字列 | OpenAI APIキー | | | api_base | APIベースURL(Azureエントリーポイント) | 文字列 | OpenAI APIベースURL | https://api.openai.com/v1 | | model | OpenAIモデル | 文字列 | OpenAIモデル | gpt-3.5-turbo | | api_version | Azure OpenAI APIバージョン | 文字列 | Azure APIバージョン | 2023-05-15 | | deployment_id | AzureデプロイメントID | 文字列 | AzureデプロイメントID | | ================================================ FILE: docs/ja/config/manager.md ================================================ # 番組マネージャー設定 ## WebUI設定 ![proxy](/image/config/manager.png){width=500}{class=ab-shadow-card}
- **有効**は番組マネージャーを有効にします。無効にすると、以下の設定は効果がありません。 - **リネーム方法**はリネーム方法です。現在サポートされているもの: - `pn` — `Torrentタイトル S0XE0X.mp4` 形式 - `advance` — `公式タイトル S0XE0X.mp4` 形式 - `none` — リネームなし - **エピソード補完**は現在のシーズンのエピソード補完を有効にします。有効にすると、不足しているエピソードがダウンロードされます。 - **グループタグ追加**はダウンロードルールに字幕グループタグを追加します。 - **不良トレント削除**はエラーのあるトレントを削除します。 - [ファイルパスについて][1] - [リネームについて][2] ## `config.json`設定オプション 設定ファイルの対応するオプションは以下のとおりです: 設定セクション:`bangumi_manager` | パラメータ | 説明 | タイプ | WebUIオプション | デフォルト | |--------------------|------------------------|---------|-------------------|-----------| | enable | 番組マネージャー有効 | ブール値 | マネージャー有効 | true | | eps_complete | エピソード補完有効 | ブール値 | エピソード補完 | false | | rename_method | リネーム方法 | 文字列 | リネーム方法 | pn | | group_tag | 字幕グループタグ追加 | ブール値 | グループタグ | false | | remove_bad_torrent | 不良トレント削除 | ブール値 | 不良トレント削除 | false | [1]: https://www.autobangumi.org/faq/#download-path [2]: https://www.autobangumi.org/faq/#file-renaming ================================================ FILE: docs/ja/config/notifier.md ================================================ # 通知設定 ## WebUI設定 ![notification](/image/config/notifier.png){width=500}{class=ab-shadow-card}
- **有効**は通知を有効にします。無効にすると、以下の設定は効果がありません。 - **タイプ**は通知タイプです。現在サポートされているもの: - Telegram - Wecom - Bark - ServerChan - **Chat ID**は`telegram`通知を使用する場合にのみ入力が必要です。[Telegram Bot Chat IDの取得方法][1] - **Wecom**:Chat IDフィールドにカスタムプッシュURLを入力し、サーバー側で[リッチテキストメッセージ][2]タイプを追加します。[Wecom設定ガイド][3] ## `config.json`設定オプション 設定ファイルの対応するオプションは以下のとおりです: 設定セクション:`notification` | パラメータ | 説明 | タイプ | WebUIオプション | デフォルト | |-----------|-----------------|---------|------------------|----------| | enable | 通知有効 | ブール値 | 通知 | false | | type | 通知タイプ | 文字列 | 通知タイプ | telegram | | token | 通知トークン | 文字列 | 通知トークン | | | chat_id | 通知Chat ID | 文字列 | 通知Chat ID | | [1]: https://core.telegram.org/bots#6-botfather [2]: https://github.com/umbors/wecomchan-alifun [3]: https://github.com/easychen/wecomchan ================================================ FILE: docs/ja/config/parser.md ================================================ # パーサー設定 ABのパーサーは集約されたRSSリンクを解析するために使用されます。RSSフィードに新しいエントリが表示されると、ABはタイトルを解析して自動ダウンロードルールを生成します。 ::: tip v3.1以降、パーサー設定は個別のRSS設定に移動しました。**パーサータイプ**を設定するには、[RSSのパーサー設定][add_rss]を参照してください。 ::: ## WebUIでのパーサー設定 ![parser](/image/config/parser.png){width=500}{class=ab-shadow-card}
- **有効**:RSSパーサーを有効にするかどうか。 - **言語**はRSSパーサーの言語です。現在`zh`、`jp`、`en`をサポートしています。 - **除外**はグローバルRSSパーサーフィルターです。文字列または正規表現を入力でき、ABはRSS解析時に一致するエントリをフィルタリングします。 ## `config.json`設定オプション 設定ファイルの対応するオプションは以下のとおりです: 設定セクション:`rss_parser` | パラメータ | 説明 | タイプ | WebUIオプション | デフォルト | |-----------|-------------------|---------|---------------------|----------------| | enable | RSSパーサー有効 | ブール値 | RSSパーサー有効 | true | | filter | RSSパーサーフィルター | 配列 | フィルター | [720,\d+-\d+] | | language | RSSパーサー言語 | 文字列 | RSSパーサー言語 | zh | [rss_token]: rss [add_rss]: /ja/feature/rss#パーサー設定 [reproxy]: /ja/config/proxy#リバースプロキシ ================================================ FILE: docs/ja/config/program.md ================================================ # プログラム設定 ## WebUI設定 ![program](/image/config/program.png){width=500}{class=ab-shadow-card}
- インターバル時間パラメータは秒単位です。分単位で設定する場合は秒に変換してください。 - RSSはRSSチェック間隔で、自動ダウンロードルールの生成頻度に影響します。 - リネームはリネームチェック間隔です。リネームのチェック頻度を変更する必要がある場合に修正してください。 - WebUIポートはポート番号です。Dockerを使用している場合、変更後にDockerでポートを再マッピングする必要があることに注意してください。 ## `config.json`設定オプション 設定ファイルの対応するオプションは以下のとおりです: 設定セクション:`program` | パラメータ | 説明 | タイプ | WebUIオプション | デフォルト | |-------------|-------------------|-----------------|---------------------|-----------| | rss_time | RSSチェック間隔 | 整数(秒) | RSSチェック間隔 | 7200 | | rename_time | リネームチェック間隔 | 整数(秒) | リネームチェック間隔 | 60 | | webui_port | WebUIポート | 整数 | WebUIポート | 7892 | ================================================ FILE: docs/ja/config/proxy.md ================================================ # プロキシとリバースプロキシ ## プロキシ ![proxy](/image/config/proxy.png){width=500}{class=ab-shadow-card}
ABはネットワーク問題を解決するためにHTTPおよびSOCKS5プロキシをサポートしています。 - **有効**:プロキシを有効にするかどうか。 - **タイプ**はプロキシタイプです。 - **ホスト**はプロキシアドレスです。 - **ポート**はプロキシポートです。 ::: tip **SOCKS5**モードでは、ユーザー名とパスワードが必要です。 ::: ## `config.json`設定オプション 設定ファイルの対応するオプションは以下のとおりです: 設定セクション:`proxy` | パラメータ | 説明 | タイプ | WebUIオプション | デフォルト | |-----------|-----------------|---------|-----------------|-----------| | enable | プロキシ有効 | ブール値 | プロキシ | false | | type | プロキシタイプ | 文字列 | プロキシタイプ | http | | host | プロキシアドレス | 文字列 | プロキシアドレス | | | port | プロキシポート | 整数 | プロキシポート | | | username | プロキシユーザー名 | 文字列 | プロキシユーザー名 | | | password | プロキシパスワード | 文字列 | プロキシパスワード | | ## リバースプロキシ - Mikan Projectの代替ドメイン`mikanime.tv`を使用して、RSS購読URLの`mikanani.me`を置き換えます。 - Cloudflare Workerをリバースプロキシとして使用し、RSSフィード内のすべての`mikanani.me`ドメインを置き換えます。 ## Cloudflare Workers 他のサービスのブロックをバイパスするために使用されるアプローチに基づいて、Cloudflare Workersを使用してリバースプロキシを設定できます。ドメインの登録とCloudflareへのバインド方法は、このガイドの範囲外です。Workersに以下のコードを追加して、独自のドメインを使用してMikan Projectにアクセスし、RSSリンクからトレントをダウンロードします: ```js const TELEGRAPH_URL = 'https://mikanani.me'; const MY_DOMAIN = 'https://yourdomain.com' addEventListener('fetch', event => { event.respondWith(handleRequest(event.request)) }) async function handleRequest(request) { const url = new URL(request.url); url.host = TELEGRAPH_URL.replace(/^https?:\/\//, ''); const modifiedRequest = new Request(url.toString(), { headers: request.headers, method: request.method, body: request.body, redirect: 'manual' }); const response = await fetch(modifiedRequest); const contentType = response.headers.get('Content-Type') || ''; // コンテンツタイプがRSSの場合のみ置換を実行 if (contentType.includes('application/xml')) { const text = await response.text(); const replacedText = text.replace(/https?:\/\/mikanani\.me/g, MY_DOMAIN); const modifiedResponse = new Response(replacedText, response); // CORSヘッダーを追加 modifiedResponse.headers.set('Access-Control-Allow-Origin', '*'); return modifiedResponse; } else { const modifiedResponse = new Response(response.body, response); // CORSヘッダーを追加 modifiedResponse.headers.set('Access-Control-Allow-Origin', '*'); return modifiedResponse; } } ``` ================================================ FILE: docs/ja/config/rss.md ================================================ # RSS購読設定 AutoBangumiは集約されたアニメRSSフィードを自動的に解析し、字幕グループとアニメ名に基づいてダウンロードルールを生成して、完全自動のアニメ追跡を可能にします。 以下では、[Mikan Project][mikan-site]を例として、RSS購読URLを取得する方法を説明します。 Mikan Projectのメインサイトは一部の地域でブロックされている場合があります。プロキシなしでアクセスできない場合は、以下の代替ドメインを使用してください: [Mikan Project (代替)][mikan-cn-site] ## 購読URLの取得 このプロジェクトはMikan Projectが提供するRSS URLの解析に基づいています。自動アニメ追跡を有効にするには、Mikan ProjectのRSS URLを登録して取得する必要があります: ![image](/image/rss/rss-token.png){data-zoomable} RSS URLは以下のようになります: ```txt https://mikanani.me/RSS/MyBangumi?token=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx # または https://mikanime.tv/RSS/MyBangumi?token=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ``` ## Mikan Project購読のヒント AutoBangumiは受信したすべてのRSSエントリを解析するため、購読時に以下の点に注意してください: ![image](/image/rss/advanced-subscription.png){data-zoomable} - プロファイル設定で詳細設定を有効にしてください。 - アニメごとに1つの字幕グループのみを購読してください。Mikan Projectでアニメのポスターをクリックしてサブメニューを開き、単一の字幕グループを選択します。 - 字幕グループが簡体字中国語と繁体字中国語の両方の字幕を提供している場合、Mikan Projectは通常選択方法を提供します。1つの字幕タイプを選択してください。 - 字幕タイプの選択がない場合は、AutoBangumiで`filter`を設定してフィルタリングするか、ルール生成後にqBittorrentで手動でフィルタリングできます。 - OVAと映画の購読は現在解析がサポートされていません。 [mikan-site]: https://mikanani.me/ [mikan-cn-site]: https://mikanime.tv/ ================================================ FILE: docs/ja/deploy/docker-cli.md ================================================ # Docker CLIでデプロイ ## 新バージョンに関する注意 AutoBangumi 2.6以降、WebUIで直接すべてを設定できます。まずコンテナを起動してから、WebUIで設定できます。以前のバージョンの環境変数設定は自動的に移行されます。環境変数は引き続き機能しますが、初回起動時にのみ有効です。 ## データと設定ディレクトリの作成 更新後もABのデータと設定が保持されるように、Dockerボリュームまたはバインドマウントの使用を推奨します。 ```shell # バインドマウントを使用 mkdir -p ${HOME}/AutoBangumi/{config,data} cd ${HOME}/AutoBangumi ``` バインドマウントまたはDockerボリュームのいずれかを選択してください: ```shell # Dockerボリュームを使用 docker volume create AutoBangumi_config docker volume create AutoBangumi_data ``` ## Docker CLIでAutoBangumiをデプロイ 以下のコマンドをコピーして実行してください。 作業ディレクトリがAutoBangumiであることを確認してください。 ```shell docker run -d \ --name=AutoBangumi \ -v ${HOME}/AutoBangumi/config:/app/config \ -v ${HOME}/AutoBangumi/data:/app/data \ -p 7892:7892 \ -e TZ=Asia/Shanghai \ -e PUID=$(id -u) \ -e PGID=$(id -g) \ -e UMASK=022 \ --network=bridge \ --dns=8.8.8.8 \ --restart unless-stopped \ ghcr.io/estrellaxd/auto_bangumi:latest ``` Dockerボリュームを使用する場合は、バインドパスを適宜置き換えてください: ```shell -v AutoBangumi_config:/app/config \ -v AutoBangumi_data:/app/data \ ``` AB WebUIは自動的に起動しますが、メインプログラムは一時停止状態です。`http://abhost:7892`にアクセスして設定してください。 ABは自動的に環境変数を`config.json`に書き込み、実行を開始します。 高度なデプロイには_[Portainer](https://www.portainer.io)_または同様のDocker管理UIの使用を推奨します。 ================================================ FILE: docs/ja/deploy/docker-compose.md ================================================ # Docker Composeでデプロイ `docker-compose.yml`ファイルを使用した**AutoBangumi**のワンクリックデプロイ方法です。 ## Docker Composeのインストール Docker Composeは通常Dockerにバンドルされています。以下で確認してください: ```bash docker compose -v ``` インストールされていない場合は、以下でインストールしてください: ```bash $ sudo apt-get update $ sudo apt-get install docker-compose-plugin ``` ## **AutoBangumi**のデプロイ ### AutoBangumiとデータディレクトリの作成 ```bash mkdir -p ${HOME}/AutoBangumi/{config,data} cd ${HOME}/AutoBangumi ``` ### オプション1:カスタムDocker Compose設定 ```yaml version: "3.8" services: AutoBangumi: image: "ghcr.io/estrellaxd/auto_bangumi:latest" container_name: AutoBangumi volumes: - ./config:/app/config - ./data:/app/data ports: - "7892:7892" restart: unless-stopped dns: - 8.8.8.8 network_mode: bridge environment: - TZ=Asia/Shanghai - PGID=$(id -g) - PUID=$(id -u) - UMASK=022 ``` 上記の内容を`docker-compose.yml`ファイルにコピーしてください。 ### オプション2:Docker Compose設定ファイルのダウンロード `docker-compose.yml`ファイルを手動で作成したくない場合、プロジェクトでは事前に作成された設定を提供しています: - **AutoBangumi**のみをインストール: ```bash wget https://raw.githubusercontent.com/EstrellaXD/Auto_Bangumi/main/docs/resource/docker-compose/AutoBangumi/docker-compose.yml ``` - **qBittorrent**と**AutoBangumi**をインストール: ```bash wget https://raw.githubusercontent.com/EstrellaXD/Auto_Bangumi/main/docs/resource/docker-compose/qBittorrent+AutoBangumi/docker-compose.yml ``` インストール方法を選択し、コマンドを実行して`docker-compose.yml`ファイルをダウンロードしてください。必要に応じてテキストエディタでパラメータをカスタマイズできます。 ### 環境変数の定義 ダウンロードしたAB+QB Docker Composeファイルを使用している場合は、以下の環境変数を定義する必要があります: ```shell export \ QB_PORT= ``` - `QB_PORT`:既存のqBittorrentポートまたは希望するカスタムポートを入力します。例:`8080` ### Docker Composeの起動 ```bash docker compose up -d ``` ================================================ FILE: docs/ja/deploy/dsm.md ================================================ # Synology NAS(DSM 7.2)デプロイ(QNAPも同様) DSM 7.2はDocker Composeをサポートしているため、ワンクリックデプロイにDocker Composeの使用を推奨します。 ## 設定とデータディレクトリの作成 `/volume1/docker/`の下に`AutoBangumi`フォルダを作成し、その中に`config`と`data`サブフォルダを作成します。 ## Container Manager(Docker)パッケージのインストール パッケージセンターを開き、Container Manager(Docker)パッケージをインストールします。 ![install-docker](/image/dsm/install-docker.png){data-zoomable} ## Docker Compose経由でABをインストール **プロジェクト**をクリックし、**作成**をクリックして、**Docker Compose**を選択します。 ![new-compose](/image/dsm/new-compose.png){data-zoomable} 以下の内容をコピーして**Docker Compose**に貼り付けます: ```yaml version: "3.4" services: ab: image: "ghcr.io/estrellaxd/auto_bangumi:latest" container_name: "auto_bangumi" restart: unless-stopped ports: - "7892:7892" volumes: - "./config:/app/config" - "./data:/app/data" network_mode: bridge environment: - TZ=Asia/Shanghai - AB_METHOD=Advance - PGID=1000 - PUID=1000 - UMASK=022 ``` **次へ**をクリックし、**完了**をクリックします。 ![create](/image/dsm/create.png){data-zoomable} 作成後、`http://:7892`にアクセスしてABに入り、設定を行います。 ## Docker Compose経由でABとqBittorrentをインストール プロキシとIPv6の両方がある場合、Synology NASのDockerでIPv6を設定するのは複雑です。複雑さを軽減するために、ABとqBittorrentの両方をホストネットワークにインストールすることを推奨します。 以下の設定は、DockerにデプロイされたローカルIPの指定ポートでアクセス可能なClashプロキシがあることを前提としています。 前のセクションに従って、以下の内容を調整して**Docker Compose**に貼り付けます: ```yaml qbittorrent: container_name: qbittorrent image: linuxserver/qbittorrent hostname: qbittorrent environment: - PGID=1000 # 必要に応じて変更 - PUID=1000 # 必要に応じて変更 - WEBUI_PORT=8989 - TZ=Asia/Shanghai volumes: - ./qb_config:/config - your_anime_path:/downloads # アニメ保存ディレクトリに変更してください。ABでダウンロードパスを/downloadsに設定 networks: - host restart: unless-stopped auto_bangumi: container_name: AutoBangumi environment: - TZ=Asia/Shanghai - PGID=1000 # 必要に応じて変更 - PUID=1000 # 必要に応じて変更 - UMASK=022 - AB_DOWNLOADER_HOST=127.0.0.1:8989 # 必要に応じてポートを変更 volumes: - /volume1/docker/ab/config:/app/config - /volume1/docker/ab/data:/app/data network_mode: host environment: - AB_METHOD=Advance dns: - 8.8.8.8 restart: unless-stopped image: "ghcr.io/estrellaxd/auto_bangumi:latest" depends_on: - qbittorrent ``` ## 追加の注意事項 PGIDとPUIDの値はシステムに応じて決定する必要があります。新しいSynology NASデバイスでは、通常`PUID=1026, PGID=100`です。qBittorrentのポートを変更する場合は、すべての場所で更新してください。 プロキシ設定については、[プロキシ設定](/ja/config/proxy)を参照してください。 低性能マシンでは、デフォルト設定がCPUを大量に使用し、ABがqBに接続できなくなり、qB WebUIにアクセスできなくなる可能性があります。 220+などのデバイスでは、CPU使用率を下げるための推奨qBittorrent設定: - 設定 -> 接続 -> 接続制限 - グローバル最大接続数:300 - Torrentあたりの最大接続数:60 - グローバルアップロードスロット制限:15 - Torrentあたりのアップロードスロット:4 - BitTorrent - 最大アクティブチェックTorrent数:1 - Torrentキューイング - 最大アクティブダウンロード数:3 - 最大アクティブアップロード数:5 - 最大アクティブTorrent数:10 - RSS - RSSリーダー - フィードあたりの最大記事数:50 ================================================ FILE: docs/ja/deploy/local.md ================================================ # ローカルデプロイ ::: warning ローカルデプロイは予期しない問題を引き起こす可能性があります。代わりにDockerの使用を強く推奨します。 このドキュメントには更新の遅れがある可能性があります。質問がある場合は、[Issues](https://github.com/EstrellaXD/Auto_Bangumi/issues)で提起してください。 ::: ## 最新リリースのダウンロード ```bash VERSION=$(curl -s "https://api.github.com/repos/EstrellaXD/Auto_Bangumi/releases/latest" | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/') curl -L -O "https://github.com/EstrellaXD/Auto_Bangumi/releases/download/$VERSION/app-v$VERSION.zip" ``` ## アーカイブの展開 Unix/WSLシステムでは、以下のコマンドを使用します。Windowsでは手動で展開してください。 ```bash unzip app-v$VERSION.zip -d AutoBangumi cd AutoBangumi ``` ## 仮想環境の作成と依存関係のインストール ローカルにPython 3.10以上とpipがインストールされていることを確認してください。 ```bash cd src python3 -m venv env python3 pip install -r requirements.txt ``` ## 設定とデータディレクトリの作成 ```bash mkdir config mkdir data ``` ## AutoBangumiの実行 ```bash python3 main.py ``` ## Windows起動時の自動起動 `nssm`を使用して起動時の自動起動を設定できます。`nssm`を使用した例: ```powershell nssm install AutoBangumi (Get-Command python).Source nssm set AutoBangumi AppParameters (Get-Item .\main.py).FullName nssm set AutoBangumi AppDirectory (Get-Item ..).FullName nssm set AutoBangumi Start SERVICE_DELAYED_AUTO_START ``` ================================================ FILE: docs/ja/deploy/quick-start.md ================================================ # クイックスタート AutoBangumiはDockerでのデプロイを推奨しています。 デプロイ前に、[Docker Engine][docker-engine]または[Docker Desktop][docker-desktop]がインストールされていることを確認してください。 ## データと設定ディレクトリの作成 ABのデータと設定を更新時に永続化するために、バインドマウントまたはDockerボリュームの使用を推奨します。 ```shell # バインドマウントを使用 mkdir -p ${HOME}/AutoBangumi/{config,data} cd ${HOME}/AutoBangumi ``` バインドマウントまたはDockerボリュームのいずれかを選択: ```shell # Dockerボリュームを使用 docker volume create AutoBangumi_config docker volume create AutoBangumi_data ``` ## DockerでAutoBangumiをデプロイ これらのコマンドを実行する際は、AutoBangumiディレクトリにいることを確認してください。 ### オプション1:Docker CLIでデプロイ 以下のコマンドをコピーして実行: ```shell docker run -d \ --name=AutoBangumi \ -v ${HOME}/AutoBangumi/config:/app/config \ -v ${HOME}/AutoBangumi/data:/app/data \ -p 7892:7892 \ -e TZ=Asia/Tokyo \ -e PUID=$(id -u) \ -e PGID=$(id -g) \ -e UMASK=022 \ --network=bridge \ --dns=8.8.8.8 \ --restart unless-stopped \ ghcr.io/estrellaxd/auto_bangumi:latest ``` ### オプション2:Docker Composeでデプロイ 以下の内容を`docker-compose.yml`ファイルにコピー: ```yaml version: "3.8" services: AutoBangumi: image: "ghcr.io/estrellaxd/auto_bangumi:latest" container_name: AutoBangumi volumes: - ./config:/app/config - ./data:/app/data ports: - "7892:7892" network_mode: bridge restart: unless-stopped dns: - 8.8.8.8 environment: - TZ=Asia/Tokyo - PGID=$(id -g) - PUID=$(id -u) - UMASK=022 ``` 以下のコマンドでコンテナを起動: ```shell docker compose up -d ``` ## qBittorrentのインストール qBittorrentをまだインストールしていない場合は、最初にインストールしてください: - [DockerでqBittorrentをインストール][qbittorrent-docker] - [Windows/macOSでqBittorrentをインストール][qbittorrent-desktop] - [Linuxでqbittorrent-noxをインストール][qbittorrent-nox] ## 集約RSSリンクの取得(Mikan Projectを例として) [Mikan Project][mikan-project]にアクセスし、アカウントを登録してログインし、右下の**RSS**ボタンをクリックしてリンクをコピーします。 ![mikan-rss](/image/rss/rss-token.png){data-zoomable} RSS URLは以下のようになります: ```txt https://mikanani.me/RSS/MyBangumi?token=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx # または https://mikanime.tv/RSS/MyBangumi?token=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ``` 詳細な手順については、[Mikan RSS設定][config-rss]を参照してください。 ## AutoBangumiの設定 ABをインストール後、WebUIは自動的に起動しますが、メインプログラムは一時停止状態です。`http://abhost:7892`にアクセスして設定できます。 1. Webページを開きます。デフォルトのユーザー名は`admin`、デフォルトのパスワードは`adminadmin`です。初回ログイン後すぐに変更してください。 2. ダウンローダーのアドレス、ポート、ユーザー名、パスワードを入力します。 ![ab-webui](/image/config/downloader.png){width=500}{class=ab-shadow-card} 3. **適用**をクリックして設定を保存します。ABが再起動し、右上のドットが緑色になると、ABが正常に動作していることを示します。 4. 右上の**+**ボタンをクリックし、**集約RSS**にチェックを入れ、パーサータイプを選択し、Mikan RSS URLを入力します。 ![ab-rss](/image/config/add-rss.png){width=500}{class=ab-shadow-card} ABが集約RSSを解析するのを待ちます。解析が完了すると、自動的にアニメを追加し、ダウンロードを管理します。 [docker-engine]: https://docs.docker.com/engine/install/ [docker-desktop]: https://www.docker.com/products/docker-desktop [config-rss]: ../config/rss [mikan-project]: https://mikanani.me/ [qbittorrent-docker]: https://hub.docker.com/r/superng6/qbittorrent [qbittorrent-desktop]: https://www.qbittorrent.org/download [qbittorrent-nox]: https://www.qbittorrent.org/download-nox ================================================ FILE: docs/ja/dev/database.md ================================================ # データベース開発者ガイド このガイドでは、AutoBangumiのデータベースアーキテクチャ、モデル、および操作について説明します。 ## 概要 AutoBangumiはデータベースとして**SQLite**を使用し、ORMには**SQLModel**(Pydantic + SQLAlchemyハイブリッド)を使用しています。データベースファイルは`data/data.db`にあります。 ### アーキテクチャ ``` module/database/ ├── engine.py # SQLAlchemyエンジン設定 ├── combine.py # Databaseクラス、マイグレーション、セッション管理 ├── bangumi.py # Bangumi(アニメ購読)操作 ├── rss.py # RSSフィード操作 ├── torrent.py # トレント追跡操作 └── user.py # ユーザー認証操作 ``` ## コアコンポーネント ### Databaseクラス `combine.py`の`Database`クラスがメインエントリーポイントです。SQLModelの`Session`を継承し、すべてのサブデータベースへのアクセスを提供します: ```python from module.database import Database with Database() as db: # サブデータベースへのアクセス bangumis = db.bangumi.search_all() rss_items = db.rss.search_active() torrents = db.torrent.search_all() ``` ### サブデータベースクラス | クラス | モデル | 目的 | |-------|-------|------| | `BangumiDatabase` | `Bangumi` | アニメ購読ルール | | `RSSDatabase` | `RSSItem` | RSSフィードソース | | `TorrentDatabase` | `Torrent` | ダウンロードしたトレントの追跡 | | `UserDatabase` | `User` | 認証 | ## モデル ### Bangumiモデル アニメ購読のコアモデル: ```python class Bangumi(SQLModel, table=True): id: int # 主キー official_title: str # 表示名(例:"無職転生") title_raw: str # トレントマッチング用の生タイトル(インデックス付き) season: int = 1 # シーズン番号 episode_offset: int = 0 # エピソード番号調整 season_offset: int = 0 # シーズン番号調整 rss_link: str # カンマ区切りRSSフィードURL filter: str # 除外フィルター(例:"720,\\d+-\\d+") poster_link: str # TMDBポスターURL save_path: str # ダウンロード先パス rule_name: str # qBittorrent RSSルール名 added: bool = False # ルールがダウンローダーに追加されたかどうか deleted: bool = False # ソフト削除フラグ(インデックス付き) archived: bool = False # 完結シリーズ用(インデックス付き) needs_review: bool = False # オフセット不一致検出 needs_review_reason: str # レビューの理由 suggested_season_offset: int # 提案されたシーズンオフセット suggested_episode_offset: int # 提案されたエピソードオフセット air_weekday: int # 放送日(0=日曜日、6=土曜日) ``` ### RSSItemモデル RSSフィード購読: ```python class RSSItem(SQLModel, table=True): id: int # 主キー name: str # 表示名 url: str # フィードURL(ユニーク、インデックス付き) aggregate: bool = True # トレントを解析するかどうか parser: str = "mikan" # パーサータイプ:mikan、dmhy、nyaa enabled: bool = True # アクティブフラグ connection_status: str # "healthy"または"error" last_checked_at: str # ISOタイムスタンプ last_error: str # 最後のエラーメッセージ ``` ### Torrentモデル ダウンロードしたトレントを追跡: ```python class Torrent(SQLModel, table=True): id: int # 主キー name: str # トレント名(インデックス付き) url: str # トレント/マグネットURL(ユニーク、インデックス付き) rss_id: int # ソースRSSフィードID bangumi_id: int # リンクされたBangumi ID(nullable) qb_hash: str # qBittorrentインフォハッシュ(インデックス付き) downloaded: bool = False # ダウンロード完了 ``` ## 一般的な操作 ### BangumiDatabase ```python with Database() as db: # 作成 db.bangumi.add(bangumi) # 単一挿入 db.bangumi.add_all(bangumi_list) # バッチ挿入(重複排除) # 読み取り db.bangumi.search_all() # 全レコード(キャッシュ、5分TTL) db.bangumi.search_id(123) # IDで検索 db.bangumi.match_torrent("torrent name") # title_rawマッチで検索 db.bangumi.not_complete() # 未完了シリーズ db.bangumi.get_needs_review() # レビューフラグ付き # 更新 db.bangumi.update(bangumi) # 単一レコード更新 db.bangumi.update_all(bangumi_list) # バッチ更新 # 削除 db.bangumi.delete_one(123) # ハード削除 db.bangumi.disable_rule(123) # ソフト削除(deleted=True) ``` ### RSSDatabase ```python with Database() as db: # 作成 db.rss.add(rss_item) # 単一挿入 db.rss.add_all(rss_items) # バッチ挿入(重複排除) # 読み取り db.rss.search_all() # 全フィード db.rss.search_active() # 有効なフィードのみ db.rss.search_aggregate() # 有効 + aggregate=True # 更新 db.rss.update(id, rss_update) # 部分更新 db.rss.enable(id) # フィード有効化 db.rss.disable(id) # フィード無効化 db.rss.enable_batch([1, 2, 3]) # バッチ有効化 db.rss.disable_batch([1, 2, 3]) # バッチ無効化 ``` ### TorrentDatabase ```python with Database() as db: # 作成 db.torrent.add(torrent) # 単一挿入 db.torrent.add_all(torrents) # バッチ挿入 # 読み取り db.torrent.search_all() # 全トレント db.torrent.search_by_qb_hash(hash) # qBittorrentハッシュで検索 db.torrent.search_by_url(url) # URLで検索 db.torrent.check_new(torrents) # 既存のものをフィルター # 更新 db.torrent.update_qb_hash(id, hash) # qb_hashを設定 ``` ## キャッシング ### Bangumiキャッシュ `search_all()`の結果はモジュールレベルで5分のTTLでキャッシュされます: ```python # bangumi.pyのモジュールレベルキャッシュ _bangumi_cache: list[Bangumi] | None = None _bangumi_cache_time: float = 0 _BANGUMI_CACHE_TTL: float = 300.0 # 5分 # キャッシュ無効化 def _invalidate_bangumi_cache(): global _bangumi_cache, _bangumi_cache_time _bangumi_cache = None _bangumi_cache_time = 0 ``` **重要:** キャッシュは以下で自動的に無効化されます: - `add()`、`add_all()` - `update()`、`update_all()` - `delete_one()`、`delete_all()` - `archive_one()`、`unarchive_one()` - 任意のRSSリンク更新操作 ### セッションExpunge キャッシュされたオブジェクトは`DetachedInstanceError`を防ぐためにセッションから**expunge**されます: ```python for b in bangumis: self.session.expunge(b) # セッションから切り離す ``` ## マイグレーションシステム ### スキーマバージョニング マイグレーションは`schema_version`テーブルを介して追跡されます: ```python CURRENT_SCHEMA_VERSION = 7 # 各マイグレーション:(バージョン、説明、[SQLステートメント]) MIGRATIONS = [ (1, "add air_weekday column", [...]), (2, "add connection status columns", [...]), (3, "create passkey table", [...]), (4, "add archived column", [...]), (5, "rename offset to episode_offset", [...]), (6, "add qb_hash column", [...]), (7, "add suggested offset columns", [...]), ] ``` ### 新しいマイグレーションの追加 1. `combine.py`の`CURRENT_SCHEMA_VERSION`をインクリメント 2. `MIGRATIONS`リストにマイグレーションタプルを追加: ```python MIGRATIONS = [ # ... 既存のマイグレーション ... ( 8, "add my_new_column to bangumi", [ "ALTER TABLE bangumi ADD COLUMN my_new_column TEXT DEFAULT NULL", ], ), ] ``` 3. `run_migrations()`に冪等性チェックを追加: ```python if "bangumi" in tables and version == 8: columns = [col["name"] for col in inspector.get_columns("bangumi")] if "my_new_column" in columns: needs_run = False ``` 4. `module/models/`の対応するPydanticモデルを更新 ### デフォルト値のバックフィル マイグレーション後、`_fill_null_with_defaults()`がモデルのデフォルトに基づいてNULL値を自動的に埋めます: ```python # モデルが定義している場合: class Bangumi(SQLModel, table=True): my_field: bool = False # NULLの既存行はFalseに更新されます ``` ## パフォーマンスパターン ### バッチクエリ `add_all()`は、Nクエリの代わりに単一のクエリを使用して重複をチェックします: ```python # 効率的:単一SELECT keys_to_check = [(d.title_raw, d.group_name) for d in datas] conditions = [ and_(Bangumi.title_raw == tr, Bangumi.group_name == gn) for tr, gn in keys_to_check ] statement = select(Bangumi.title_raw, Bangumi.group_name).where(or_(*conditions)) ``` ### 正規表現マッチング `match_list()`は、すべてのタイトルマッチ用に単一の正規表現パターンをコンパイルします: ```python # 一度コンパイル、多くマッチ sorted_titles = sorted(title_index.keys(), key=len, reverse=True) pattern = "|".join(re.escape(title) for title in sorted_titles) title_regex = re.compile(pattern) # トレントごとにO(n)ではなくO(1)ルックアップ for torrent in torrent_list: match = title_regex.search(torrent.name) ``` ### インデックス付きカラム 以下のカラムには高速ルックアップ用のインデックスがあります: | テーブル | カラム | インデックスタイプ | |---------|--------|------------------| | `bangumi` | `title_raw` | 通常 | | `bangumi` | `deleted` | 通常 | | `bangumi` | `archived` | 通常 | | `rssitem` | `url` | ユニーク | | `torrent` | `name` | 通常 | | `torrent` | `url` | ユニーク | | `torrent` | `qb_hash` | 通常 | ## テスト ### テストデータベースセットアップ テストはインメモリSQLiteデータベースを使用します: ```python # conftest.py @pytest.fixture def db_engine(): engine = create_engine("sqlite:///:memory:") SQLModel.metadata.create_all(engine) yield engine engine.dispose() @pytest.fixture def db_session(db_engine): with Session(db_engine) as session: yield session ``` ### ファクトリ関数 テストデータ作成にはファクトリ関数を使用: ```python from test.factories import make_bangumi, make_torrent, make_rss_item def test_bangumi_search(): bangumi = make_bangumi(title_raw="Test Title", season=2) # ... テストロジック ``` ## 設計ノート ### 外部キーなし SQLite外部キー強制はデフォルトで無効になっています。リレーションシップ(`Torrent.bangumi_id`など)はデータベース制約ではなくアプリケーションロジックで管理されます。 ### ソフト削除 `Bangumi.deleted`フラグはソフト削除を可能にします。クエリはユーザー向けデータには`deleted=False`でフィルターする必要があります: ```python statement = select(Bangumi).where(Bangumi.deleted == false()) ``` ### トレントタグ付け トレントはリネーム操作中のオフセットルックアップ用にqBittorrentで`ab:{bangumi_id}`でタグ付けされます。これにより、データベースクエリなしで高速な番組識別が可能になります。 ## 一般的な問題 ### DetachedInstanceError キャッシュされたオブジェクトを別のセッションからアクセスする場合: ```python # 間違い:新しいセッションでキャッシュされたオブジェクトにアクセス bangumis = db.bangumi.search_all() # キャッシュ済み with Database() as new_db: new_db.session.add(bangumis[0]) # エラー! # 正しい:オブジェクトはexpungeされ、独立して動作 bangumis = db.bangumi.search_all() bangumis[0].title_raw = "New Title" # OK、ただし永続化されない ``` ### キャッシュの古さ 手動SQLアップデートがORMをバイパスする場合、キャッシュを無効化: ```python from module.database.bangumi import _invalidate_bangumi_cache with engine.connect() as conn: conn.execute(text("UPDATE bangumi SET ...")) conn.commit() _invalidate_bangumi_cache() # 重要! ``` ================================================ FILE: docs/ja/dev/index.md ================================================ # コントリビューションガイド AutoBangumiをより良くするためにユーザーが遭遇する問題を解決する手助けをしていただけるコントリビューターを歓迎します。 このガイドでは、AutoBangumiにコードをコントリビュートする方法を説明します。Pull Requestを提出する前に数分間お読みください。 この記事では以下を扱います: - [プロジェクトロードマップ](#プロジェクトロードマップ) - [Request for Comments (RFC)](#request-for-comments-rfc) - [Gitブランチ管理](#gitブランチ管理) - [バージョン番号](#バージョン番号) - [ブランチ開発、トランクリリース](#ブランチ開発トランクリリース) - [ブランチライフサイクル](#ブランチライフサイクル) - [Gitワークフローの概要](#gitワークフローの概要) - [Pull Request](#pull-request) - [リリースプロセス](#リリースプロセス) ## プロジェクトロードマップ AutoBangumi開発チームは[GitHub Project](https://github.com/EstrellaXD/Auto_Bangumi/projects?query=is%3Aopen)ボードを使用して、計画された開発、進行中の修正、およびその進捗を管理しています。 これにより以下を理解できます: - 開発チームが取り組んでいること - あなたの意図するコントリビューションに合致するものがあり、直接参加できること - すでに進行中のものがあり、重複作業を避けられること [Project](https://github.com/EstrellaXD/Auto_Bangumi/projects?query=is%3Aopen)では、通常の`[Feature Request]`、`[BUG]`、小さな改善に加えて、**`[RFC]`**アイテムがあります。 ### Request for Comments (RFC) > issuesの`RFC`ラベルを介して既存の[AutoBangumi RFC](https://github.com/EstrellaXD/Auto_Bangumi/issues?q=is%3Aissue+label%3ARFC)を見つけてください。 小さな改善やバグ修正については、コードを調整してPull Requestを提出してください。[ブランチ管理](#gitブランチ管理)セクションを読んで正しいブランチに基づいて作業し、[Pull Request](#pull-request)セクションでPRがどのようにマージされるかを理解してください。
広範囲にわたる**大きな**機能リファクタリングについては、まず[Issue: Feature Proposal](https://github.com/EstrellaXD/Auto_Bangumi/issues/new?assignees=&labels=RFC&projects=&template=rfc.yml&title=%5BRFC%5D%3A+)を介してRFC提案を書き、アプローチを簡潔に説明し、開発者の議論とコンセンサスを求めてください。 一部の提案は開発チームがすでに行った決定と競合する可能性があり、このステップは無駄な努力を避けるのに役立ちます。 > 機能を追加または改善するかどうか(「実装方法」ではなく)について議論したい場合は -> [Issue: Feature Request](https://github.com/EstrellaXD/Auto_Bangumi/issues/new?labels=feature+request&template=feature_request.yml&title=%5BFeature+Request%5D+)を使用してください
[RFC提案](https://github.com/EstrellaXD/Auto_Bangumi/issues?q=is%3Aissue+is%3Aopen+label%3ARFC)は**「機能/リファクタリングの具体的な開発前に開発者が技術設計/アプローチをレビューするためのドキュメント」**です。 目的は、協力する開発者が「何をするか」と「どのように行われるか」を明確に知り、すべての開発者がオープンな議論に参加できるようにすることです。 これにより、影響(見落とされた考慮事項、後方互換性、既存機能との競合)を評価できます。 したがって、提案は問題を解決するための**アプローチ、設計、ステップ**の説明に焦点を当てます。 ## Gitブランチ管理 ### バージョン番号 AutoBangumiプロジェクトのGitブランチは、リリースバージョンルールと密接に関連しています。 AutoBangumiは[セマンティックバージョニング(SemVer)](https://semver.org/)に従い、`..`形式を使用します: - **Major**:メジャーバージョン更新、互換性のない設定/API変更がある可能性 - **Minor**:後方互換性のある新機能 - **Patch**:後方互換性のあるバグ修正 / 小さな改善 ### ブランチ開発、トランクリリース AutoBangumiは「ブランチ開発、トランクリリース」モデルを使用しています。 [**`main`**](https://github.com/EstrellaXD/Auto_Bangumi/commits/main)は安定した**トランクブランチ**で、リリースにのみ使用され、直接開発には使用されません。 各Minorバージョンには、新機能とリリース後のメンテナンス用の対応する**開発ブランチ**があります。 開発ブランチは`.-dev`という名前で、例:`3.1-dev`、`3.0-dev`、`2.6-dev`。[All Branches](https://github.com/EstrellaXD/Auto_Bangumi/branches/all?query=-dev)で見つけてください。 ### ブランチライフサイクル Minor開発ブランチ(例:`3.1-dev`)が機能開発を完了し、**最初に**mainにマージするとき: - Minorバージョン(例:`3.1.0`)をリリース - **次の**Minor開発ブランチ(`3.2-dev`)を次バージョンの機能用に作成 - **前の**バージョンのブランチ(`3.0-dev`)はアーカイブ - このMinorブランチ(`3.1-dev`)はメンテナンスに入る — 新機能/リファクタリングなし、バグ修正のみ - バグ修正はメンテナンスブランチにマージされ、その後`Patch`リリースのためにmainへ コントリビューターのGitブランチ選択: - **バグ修正** — **現在リリースされているバージョンの**Minorブランチに基づき、そのブランチにPR - **新機能/リファクタリング** — **次の未リリースバージョンの**Minorブランチに基づき、そのブランチにPR > 「現在リリースされているバージョン」は[[Releases page]](https://github.com/EstrellaXD/Auto_Bangumi/releases)の最新バージョンです ### Gitワークフローの概要 > コミットタイムラインは左から右へ ---> ![dev-branch](/image/dev/branch.png) ## Pull Request 上記のGitブランチ管理セクションに従って、正しいPRターゲットブランチを選択していることを確認してください: > - **バグ修正** → **現在リリースされているバージョンの**Minorメンテナンスブランチに PR > - **新機能/リファクタリング** → **次バージョンの**Minor開発ブランチに PR
- PRは単一の関心事に対応し、無関係な変更を導入しないでください。 異なる関心事を複数のPRに分割して、チームがレビューごとに1つの問題に集中できるようにしてください。 - PRタイトルと説明で、理由と意図を含めて変更を簡潔に説明してください。 PR説明に関連するissuesやRFCをリンクしてください。 これにより、コードレビュー中にチームがコンテキストを素早く理解できます。 - 「メンテナーからの編集を許可」がチェックされていることを確認してください。これにより、小さな編集/リファクタリングを直接行うことができ、時間を節約できます。 - ローカルテストとリンティングがパスすることを確認してください。これらはPR CIでもチェックされます。 - バグ修正と新機能については、チームが対応するユニットテストカバレッジを要求する場合があります。 開発チームはコントリビューターのPRをレビューし、できるだけ早く議論またはマージを承認します。 ## リリースプロセス リリースは現在、開発チームが特定の「リリースPR」を手動でマージした後に自動的にトリガーされます。 バグ修正PRは通常、迅速にリリースされ、通常は1週間以内です。 新機能リリースはより長くかかり、予測が難しいです。開発進捗については[GitHub Project](https://github.com/EstrellaXD/Auto_Bangumi/projects?query=is%3Aopen)ボードを確認してください — すべての計画された機能が完了するとバージョンがリリースされます。 ================================================ FILE: docs/ja/faq/index.md ================================================ # よくある質問 ## WebUI ### WebUIアドレス デフォルトポートは7892です。サーバーデプロイメントの場合は`http://serverhost:7892`に、ローカルデプロイメントの場合は`http://localhost:7892`にアクセスします。ポートを変更した場合は、Dockerのポートマッピングも更新することを忘れないでください。 ### デフォルトのユーザー名とパスワード - デフォルトのユーザー名:`admin`、デフォルトのパスワード:`adminadmin` - 初回ログイン後にパスワードを変更してください。 ### パスワードの変更またはリセット - パスワード変更:ログイン後、右上の`···`をクリックし、`プロファイル`をクリックして、ユーザー名とパスワードを変更します。 - 現在、簡単なパスワードリセット方法はありません。パスワードを忘れた場合は、`data/data.db`ファイルを削除して再起動してください。 ### 設定変更が反映されないのはなぜですか? - 設定変更後、**適用**ボタンをクリックし、次に`···`メニューの**再起動**をクリックしてメインプロセスを再起動します。 - デバッグモードが有効な場合は、`···`メニューの**シャットダウン**をクリックしてコンテナを再起動します。 ### プログラムが正常に動作しているか確認する方法 新しいWebUIには右上に小さなドットがあります。緑は正常に動作中、赤はエラーが発生してプログラムが一時停止中を意味します。 ### ポスターウォールに画像が表示されない - バージョン3.0の場合: ABはデフォルトで`mikanani.me`アドレスをポスター画像ソースとして使用します。画像が表示されない場合、ネットワークがこれらの画像にアクセスできません。 - バージョン3.1以降の場合: - ポスターにエラーアイコンが表示される場合、画像がありません。右上メニューのポスター更新ボタンをクリックしてTMDBポスターを取得してください。 - ポスターの読み込みに失敗する場合は、ブラウザのキャッシュをクリアしてください。 - RSSアドレスとして`mikanime.tv`を使用している場合、クライアント側のプロキシがポスターの読み込みを妨げる可能性があります。`direct`ルールを追加してください。 ## v3.0はどのようにアニメを管理しますか v3.0にアップグレード後、ABはWebUIでアニメトレントとダウンロードルールを管理できます。トレントのダウンロードパスとルール名に依存します。 QBでトレントのダウンロードパスを手動で変更すると、通知にポスターがない、トレントの削除に失敗するなどの問題が発生する可能性があります。 できるだけAB内でアニメとトレントを管理してください。 ## ダウンロードとキーワードフィルタリング ### ダウンロードパス **ダウンロードパスには何を入力すればよいですか?** - このパラメータはqBittorrentの設定と一致させるだけです: - Docker:qBが`/downloads`を使用している場合は、`/downloads/Bangumi`に設定します。`Bangumi`は任意の名前に変更できます。 - Linux/macOS:`/home/usr/downloads`または`/User/UserName/Downloads`の場合は、末尾に`/Bangumi`を追加するだけです。 - Windows:`D:\Media\`を`D:\Media\Bangumi`に変更します ### ダウンロードが自動的に開始されない AutoBangumiのログでトレント関連のエントリを確認してください。 - 存在しない場合は、購読が正しいか確認してください。 ### ダウンロードが正しいディレクトリに保存されない - [ダウンロードパス](#ダウンロードパス)が正しいか確認してください。 - qBittorrentのPGIDとPUID設定でフォルダ作成権限を確認してください。任意のトレントを指定ディレクトリに手動でダウンロードしてみてください — エラーが発生するかディレクトリが作成されない場合は、権限の問題です。 - qBittorrentのデフォルト設定を確認してください:保存管理は手動に設定する必要があります(保存管理 >> デフォルトトレント管理モード >> 手動)。 ### 購読していないアニメが多数ダウンロードされる - Mikan購読が単一のアニメのすべての字幕グループを含んでいないか確認してください。アニメごとに1つのグループのみを購読し、詳細購読を有効にしてください。 - 詳細購読はMikan Projectのユーザー設定で有効にできます。 - 正規表現フィルタリングが不十分な場合があります — 正規表現の拡張については次のセクションを参照してください。 - どちらにも該当しない場合は、ログとともに[Issues][ISSUE]で報告してください。 ### フィルターキーワードの書き方 ABのフィルターキーワードは正規表現で、ルール作成時にのみ追加されます。作成後にルールを拡張するには、WebUI(v3.0以降)を使用して各アニメを個別に設定します。 - フィルターキーワードは正規表現です — 不要なキーワードは`|`で区切ります。 - デフォルトの`720|\d+-\d+`ルールは、すべてのコレクションと720Pアニメをフィルタリングします。ABをデプロイする前にフィルターを追加してください。以降の環境変数の変更は新しいルールにのみ影響します。 - 一般的な正規表現キーワード(`|`で区切り): - `720` — 720、720P、720pなどをフィルタリング - `\d+-\d+` — [1-12]のようなコレクションをフィルタリング - `[Bb]aha` — Bahaリリースをフィルタリング - `[Bb]ilibili`、`[Bb]-Global` — Bilibiliリリースをフィルタリング - `繁`、`CHT` — 繁体字中国語字幕をフィルタリング - 特定のキーワードに一致させるには、QBのincludeフィールドに追加:`XXXXX+1080P\+`ここで`1080P\+`は1080P+リリースに一致します。 ### 初回デプロイ時に不要なアニメがダウンロードされた 1. QBで余分な自動ダウンロードルールとファイルを削除します。 2. 購読とフィルタールールを確認します。 3. ブラウザでresetRule APIにアクセス:`http://localhost:7892/api/v1/resetRule`でルールをリセットします。 4. ABを再起動します。 ### ABが購読より少ないRSSエントリを識別する 新しいバージョンでは、ABのフィルターもデフォルトですべてのRSSエントリをフィルタリングします。すべてのフィルターを一度に追加しないでください。細かい制御には、WebUIで各アニメを個別に設定してください。 ### フィルターキーワードが機能しない - **グローバルフィルター**パラメータが正しく設定されているか確認してください。 - QBのRSS自動ダウンロードルールを確認してください — 右側で一致したRSSを確認でき、ダウンロードルールを調整して保存をクリックすると、どのキーワードが問題を引き起こしているかを特定できます。 ## エピソード補完 ### エピソード補完が機能しない **エピソード補完**パラメータが正しく設定されているか確認してください。 ## ファイルリネーム ### 解析エラー `Cannot parse XXX` - ABは現在コレクションの解析をサポートしていません。 - コレクションでない場合は、GitHub Issuesで問題を報告してください。 ### `Rename failed`またはリネームエラー - ファイルパスを確認してください。標準的な保存パスは`/title/Season/Episode.mp4`である必要があります。非標準のパスは名前付けエラーの原因となります — qBittorrentの設定を確認してください。 - `ダウンロードパス`が正しく入力されているか確認してください。パスが正しくないと適切なリネームができません。 - その他の問題については、GitHub Issuesで報告してください。 ### 自動リネームされない - QBのトレントカテゴリが`Bangumi`であるか確認してください。 - ABはダウンロード済みのファイルのみをリネームします。 ### AB以外のアニメをABでリネームする方法 - トレントのカテゴリを`Bangumi`に変更するだけです。 - 注意:リネームをトリガーするには、トレントが`Title/Season X/`フォルダに保存されている必要があります。 ### コレクションのリネーム方法 1. コレクションのカテゴリを`Bangumi`に変更します。 2. コレクションの保存パスを`Title/Season X/`に変更します。 3. コレクションのダウンロードが完了するのを待つと、リネームが完了します。 ## Docker ### 自動更新の方法 Dockerで`watchtower`デーモンを実行して、コンテナを自動的に更新します。 [watchtower](https://containrrr.dev/watchtower) 公式ドキュメント ### Docker Composeでの更新 ABがDocker Composeでデプロイされている場合は、`docker compose pull`で更新します。 新しいイメージをpullした後、`docker compose up -d`で再起動します。 `docker-compose.yml`に`pull_policy: always`を追加すると、起動するたびに最新のイメージをpullできます。 ### アップグレードで問題が発生した場合の対処法 設定が異なる場合があるため、アップグレードによりプログラムが失敗する可能性があります。この場合、以前のデータと生成された設定ファイルをすべて削除してから、コンテナを再起動してください。 その後、WebUIで再設定します。 古いバージョンからアップグレードする場合は、まず[アップグレードガイド](/ja/changelog/2.6)を参照してください。 上記でカバーされていない問題が発生した場合は、バグテンプレートを使用して[Issues][ISSUE]で報告してください。 [ISSUE]: https://github.com/EstrellaXD/Auto_Bangumi/issues ================================================ FILE: docs/ja/faq/network.md ================================================ # ネットワーク問題 ## Mikan Projectに接続できない Mikan Projectのメインサイト(`https://mikanani.me`)は一部の地域でブロックされている可能性があるため、ABは接続に失敗することがあります。以下の解決策を使用してください: - [Mikan Projectの代替ドメインを使用](#mikan-projectの代替ドメイン) - [プロキシを使用](#プロキシの設定) - [Cloudflare Workerリバースプロキシを使用](#cloudflare-workersリバースプロキシ) ### Mikan Projectの代替ドメイン Mikan Projectには新しいドメイン`https://mikanime.tv`があります。プロキシを有効にせずに、このドメインをABで使用してください。 以下が表示される場合: ``` DNS/Connect ERROR ``` - ネットワーク接続を確認してください。問題がない場合は、DNS解決を確認してください。 - ABに`dns=8.8.8.8`を追加してください。Hostネットワークモードを使用している場合、これは無視できます。 プロキシを使用している場合、正しい設定であればこのエラーは通常発生しません。 ### プロキシの設定 ::: tip AB 3.1以降、ABはRSS更新と通知を自分で処理するため、ABでプロキシを設定するだけで十分です。 ::: ABにはプロキシ設定が組み込まれています。プロキシを設定するには、[プロキシ設定](/ja/config/proxy)の指示に従ってHTTPまたはSOCKSプロキシを正しく設定してください。これでアクセス問題が解決されます。 **3.1より前のバージョンでは、qBittorrentのプロキシ設定も必要です** QBで以下のようにプロキシを設定してください(SOCKSも同様のアプローチ): image ### Cloudflare Workersリバースプロキシ Cloudflare Workersを介したリバースプロキシアプローチも使用できます。ドメインの設定とCloudflareへのバインドは、このガイドの範囲外です。 Workersに以下のコードを追加して、独自のドメインを使用してMikan Projectにアクセスし、RSSリンクからトレントをダウンロードします: ```javascript const TELEGRAPH_URL = 'https://mikanani.me'; const MY_DOMAIN = 'https://yourdomain.com' addEventListener('fetch', event => { event.respondWith(handleRequest(event.request)) }) async function handleRequest(request) { const url = new URL(request.url); url.host = TELEGRAPH_URL.replace(/^https?:\/\//, ''); const modifiedRequest = new Request(url.toString(), { headers: request.headers, method: request.method, body: request.body, redirect: 'manual' }); const response = await fetch(modifiedRequest); const contentType = response.headers.get('Content-Type') || ''; // コンテンツタイプがRSSの場合のみ置換を実行 if (contentType.includes('application/xml')) { const text = await response.text(); const replacedText = text.replace(/https?:\/\/mikanani\.me/g, MY_DOMAIN); const modifiedResponse = new Response(replacedText, response); // CORSヘッダーを追加 modifiedResponse.headers.set('Access-Control-Allow-Origin', '*'); return modifiedResponse; } else { const modifiedResponse = new Response(response.body, response); // CORSヘッダーを追加 modifiedResponse.headers.set('Access-Control-Allow-Origin', '*'); return modifiedResponse; } } ``` 設定が完了したら、**RSSを追加する**際に`https://mikanani.me`をあなたのドメインに置き換えてください。 ## qBittorrentに接続できない まず、ABの**ダウンローダーアドレス**パラメータが正しいか確認してください。 - ABとQBが同じDockerネットワーク上にある場合、コンテナ名を使用したアドレス指定を試してください。例:`http://qbittorrent:8080`。 - ABとQBが同じDockerサーバー上にある場合、Dockerゲートウェイアドレスを使用してみてください。例:`http://172.17.0.1:8080`。 - ABのネットワークモードが`host`でない場合、QBへのアクセスに`127.0.0.1`を使用しないでください。 Docker内のコンテナが相互にアクセスできない場合は、QBのネットワーク接続設定でQBとAB間のネットワークリンクを設定してください。qBittorrentがHTTPSを使用する場合は、**ダウンローダーアドレス**に`https://`プレフィックスを追加してください。 ================================================ FILE: docs/ja/faq/troubleshooting.md ================================================ --- title: トラブルシューティング --- ## 一般的なトラブルシューティングフロー 1. ABが起動に失敗した場合、起動コマンドが正しいか確認してください。正しくなく、修正方法がわからない場合は、ABを再デプロイしてみてください。 2. ABをデプロイした後、まずログを確認してください。以下のような出力が表示されれば、ABは正常に動作しており、QBに接続されています: ``` [2022-07-09 21:55:19,164] INFO: _ ____ _ [2022-07-09 21:55:19,165] INFO: /\ | | | _ \ (_) [2022-07-09 21:55:19,166] INFO: / \ _ _| |_ ___ | |_) | __ _ _ __ __ _ _ _ _ __ ___ _ [2022-07-09 21:55:19,167] INFO: / /\ \| | | | __/ _ \| _ < / _` | '_ \ / _` | | | | '_ ` _ \| | [2022-07-09 21:55:19,167] INFO: / ____ \ |_| | || (_) | |_) | (_| | | | | (_| | |_| | | | | | | | [2022-07-09 21:55:19,168] INFO: /_/ \_\__,_|\__\___/|____/ \__,_|_| |_|\__, |\__,_|_| |_| |_|_| [2022-07-09 21:55:19,169] INFO: __/ | [2022-07-09 21:55:19,169] INFO: |___/ [2022-07-09 21:55:19,170] INFO: Version 3.0.1 Author: EstrellaXD Twitter: https://twitter.com/Estrella_Pan [2022-07-09 21:55:19,171] INFO: GitHub: https://github.com/EstrellaXD/Auto_Bangumi/ [2022-07-09 21:55:19,172] INFO: Starting AutoBangumi... [2022-07-09 21:55:20,717] INFO: Add RSS Feed successfully. [2022-07-09 21:55:21,761] INFO: Start collecting RSS info. [2022-07-09 21:55:23,431] INFO: Finished [2022-07-09 21:55:23,432] INFO: Running.... ``` 1. このログが表示される場合、ABはqBittorrentに接続できません。qBittorrentが実行されているか確認してください。実行されている場合は、[ネットワーク問題](/ja/faq/network)セクションに進んでください。 ``` [2022-07-09 22:01:24,534] WARNING: Cannot connect to qBittorrent, wait 5min and retry ``` 2. このログが表示される場合、ABはMikan RSSに接続できません。[ネットワーク問題](/ja/faq/network)セクションに進んでください。 ``` [2022-07-09 21:55:21,761] INFO: Start collecting RSS info. [2022-07-09 22:01:24,534] WARNING: Connected Failed, please check DNS/Connection ``` 3. この時点で、QBにはダウンロードタスクがあるはずです。 1. ダウンロードでパスの問題が表示される場合、QBの「保存管理」→「デフォルトTorrent管理モード」が「手動」に設定されているか確認してください。 2. すべてのダウンロードに感嘆符が表示されるか、ダウンロードパスにカテゴリフォルダが作成されない場合は、QBの権限を確認してください。 4. 上記のいずれでも問題が解決しない場合は、新しいqBittorrentを再デプロイしてみてください。 5. それでも成功しない場合は、ログを添えて[Issues](https://www.github.com/EstrellaXD/Auto_Bangumi/issues)で報告してください。 ================================================ FILE: docs/ja/feature/bangumi.md ================================================ # 番組管理 ホームページでアニメのポスターをクリックして、個別のアニメエントリを管理します。 ![Bangumi List](/image/feature/bangumi-list.png) アニメに複数のダウンロードルールがある場合(例:異なる字幕グループ)、ルール選択ポップアップが表示されます: ![Rule Selection](/image/feature/rule-select.png) ルールを選択すると、編集モーダルが開きます: ![Edit Bangumi](/image/feature/bangumi-edit.png) ## 通知バッジ v3.2以降、番組カードにはステータスを示すiOSスタイルの通知バッジが表示されます: - **`!`が付いた黄色バッジ**:購読にレビューが必要(例:オフセットの問題が検出された) - **数字バッジ**:このアニメに複数のルールが存在 - **組み合わせバッジ**(例:`! | 2`):警告と複数のルールがある 警告のあるカードは、注意を引くために黄色のグローアニメーションも表示されます。 ## エピソードオフセットの自動検出 一部のアニメには、RSSエピソード番号とTMDBデータの不一致を引き起こす複雑なシーズン構造があります。例: - 「葬送のフリーレン」シーズン1は6ヶ月の間隔を置いて2つのパートで放送されました - RSSは「S2E01」と表示しますが、TMDBは「S1E29」と見なします AB v3.2はこれらの問題を自動的に検出できます: 1. 編集モーダルで**自動検出**ボタンをクリック 2. ABはTMDBエピソード放送日を分析して「仮想シーズン」を特定 3. 不一致が見つかった場合、ABは正しいオフセット値を提案 4. **適用**をクリックしてオフセットを保存 バックグラウンドスキャンスレッドも定期的に既存の購読のオフセット問題をチェックし、レビュー対象としてマークします。 ## アニメのアーカイブ / アーカイブ解除 v3.2以降、完了したまたは非アクティブなアニメをアーカイブしてリストを整理できます。 ### 手動アーカイブ 1. アニメのポスターをクリック 2. 編集モーダルで**アーカイブ**ボタンをクリック 3. アニメはリストの下部にある「アーカイブ済み」セクションに移動 ### 自動アーカイブ ABは以下の場合にアニメを自動的にアーカイブできます: - TMDBのシリーズステータスが「終了」または「キャンセル」と表示 - **設定** → メタデータを更新を使用して自動アーカイブをトリガー ### アーカイブされたアニメの表示 アーカイブされたアニメは、番組リストの下部にある折りたたみ可能な「アーカイブ済み」セクションに表示されます。クリックして展開し、アーカイブされたアイテムを表示します。 ### アーカイブ解除 アーカイブされたアニメを復元するには: 1. 「アーカイブ済み」セクションを展開 2. アニメのポスターをクリック 3. **アーカイブ解除**ボタンをクリック ## アニメの無効化 / 削除 ABは継続的に**集約RSS**フィードを解析するため、不要になった集約RSSからのダウンロードルールについては: - アニメを無効化:アニメはダウンロードも再解析もされません - 集約RSSから購読を削除 アニメエントリを削除すると、次の解析サイクルで再作成されます。 ## 詳細設定 編集モーダルで**詳細設定**をクリックして、追加のオプションにアクセスします: ![Advanced Settings](/image/feature/bangumi-edit-advanced.png) - **シーズンオフセット**:シーズン番号オフセットを調整 - **エピソードオフセット**:エピソード番号オフセットを調整 - **フィルター**:トレントマッチング用のカスタム正規表現フィルター ================================================ FILE: docs/ja/feature/calendar.md ================================================ # カレンダービュー v3.2以降、ABには購読しているアニメを放送日ごとに整理して表示するカレンダービューが含まれています。 ![Calendar](/image/feature/calendar.png) ## 機能 ### 週間スケジュール カレンダーは、放送曜日(月曜日から日曜日)ごとに整理されたアニメを表示し、放送スケジュールデータのないアニメ用の「不明」列もあります。 ### Bangumi.tv連携 ABはBangumi.tvから放送スケジュールデータを取得して、各アニメの放送時間を正確に表示します。 **スケジュールを更新**ボタンをクリックして、放送データを更新します。 ### グループ表示 v3.2以降、複数のダウンロードルールを持つアニメはグループ化されます: - 複数の字幕グループルールがあっても、同じアニメは1回だけ表示されます - グループ化されたアニメをクリックすると、利用可能なすべてのルールが表示されます - 特定のルールを選択して編集 これにより、すべてのルールへのアクセスを提供しながら、カレンダーをすっきり保ちます。 ## ナビゲーション カレンダー内の任意のアニメポスターをクリックして: - アニメの詳細を表示 - ダウンロードルールを編集 - アーカイブ/無効化オプションにアクセス ## ヒント ::: tip アニメが「不明」列に表示される場合、Bangumi.tvに放送データがないか、アニメタイトルがマッチングできなかった可能性があります。 ::: ================================================ FILE: docs/ja/feature/rename.md ================================================ # ファイルリネーム ABは現在3つのリネーム方法を提供しています:`pn`、`advance`、`none`。 ### pn `pure name`の略です。この方法はトレントダウンロード名を使用してリネームします。 例: ``` [Lilith-Raws] 86 - Eighty Six - 01 [Baha][WEB-DL][1080p][AVC AAC][CHT][MKV].mkv >> 86 - Eighty Six S01E01.mkv ``` ### advance 高度なリネーム。この方法は親フォルダ名を使用してリネームします。 ``` /downloads/Bangumi/86 - Eighty Six(2023)/Season 1/[Lilith-Raws] 86 - Eighty Six - 01 [Baha][WEB-DL][1080p][AVC AAC][CHT][MKV].mkv >> 86 - Eighty Six(2023) S01E01.mkv ``` ### none リネームなし。ファイルはそのまま残されます。 ## コレクションのリネーム ABはコレクションのリネームをサポートしています。コレクションのリネームには以下が必要です: - エピソードがコレクションの第1レベルディレクトリにある - ファイル名からエピソード番号を解析できる ABはまた、第1レベルディレクトリ内の字幕ファイルもリネームできます。 リネーム後、エピソードとディレクトリは`Season`フォルダに配置されます。 リネームされたコレクションは`BangumiCollection`の下に移動され、カテゴリ分けされます。 ## エピソードオフセット v3.2以降、ABはリネーム用のエピソードオフセットをサポートしています。これは以下の場合に便利です: - RSSが期待と異なるエピソード番号を表示(例:S2E01がS1E29であるべき) - 放送ギャップによるアニメの「仮想シーズン」がある 番組にオフセットが設定されている場合、ABはリネーム中に自動的に適用します: ``` 元:S02E01.mkv オフセット適用後(シーズン:-1、エピソード:+28):S01E29.mkv ``` オフセットを設定するには: 1. アニメのポスターをクリック 2. 詳細設定を開く 3. シーズンオフセットおよび/またはエピソードオフセットの値を設定 4. または「自動検出」を使用してABに正しいオフセットを提案させる 自動検出の詳細については、[番組管理](/ja/feature/bangumi#エピソードオフセットの自動検出)を参照してください。 ================================================ FILE: docs/ja/feature/rss.md ================================================ --- title: RSS管理 --- # RSS管理 ## RSSマネージャーページ RSSマネージャーページには、すべてのRSS購読と接続ステータスが表示されます。 ![RSS Manager](/image/feature/rss-manager.png) ### 接続ステータス v3.2以降、ABは各RSSソースの接続ステータスを追跡します: | ステータス | 説明 | |----------|------| | **接続済み**(緑) | RSSソースに到達可能で、有効なデータを返しています | | **エラー**(赤) | RSSソースが応答しないか、無効なデータを返しました | ソースがエラーを表示している場合、ステータスラベルにマウスを合わせるとツールチップでエラーの詳細が表示されます。 ABは各RSSリフレッシュサイクルで接続ステータスを自動的に更新します。 ## コレクションの追加 ABは2つの手動ダウンロード方法を提供しています: **コレクト**と**サブスクライブ**。 - **コレクト**はすべてのエピソードを一度にダウンロードし、完結したアニメに適しています。 - **サブスクライブ**は対応するRSSリンクで自動ダウンロードルールを追加し、放送中のアニメに適しています。 ### RSSリンクの解析 ABはすべてのリソースサイトからのコレクションRSSリンクの解析をサポートしています。対応するサイトで希望するアニメのコレクションRSSを見つけ、ABの右上隅にある**+**ボタンをクリックし、ポップアップウィンドウにRSSリンクを貼り付けます。 ### ダウンロードの追加 解析が成功すると、解析されたアニメ情報を表示するウィンドウが表示されます。**コレクト**または**サブスクライブ**をクリックしてダウンロードキューに追加します。 ### よくある問題 解析エラーが発生した場合、RSSリンクが正しくないか、サポートされていない字幕グループの命名形式が原因である可能性があります。 ## 番組の管理 v3.0以降、ABはWebUIで手動のアニメ管理を提供し、誤って解析されたアニメ情報を手動で調整できます。 ### アニメ情報の編集 アニメリストで、アニメのポスターをクリックしてアニメ情報ページに入ります。 情報を変更した後、**適用**をクリックします。 ABは変更に基づいてディレクトリを再調整し、ファイルを自動的にリネームします。 ### アニメの削除 v3.0以降、アニメを手動で削除できます。アニメのポスターをクリックし、情報ページに入り、**削除**をクリックします。 ::: warning アニメを削除した後、RSS購読がキャンセルされていない場合、ABは引き続き再解析します。ダウンロードルールを無効にするには、[アニメの無効化](#アニメの無効化)を使用してください。 ::: ### アニメの無効化 v3.0以降、アニメを手動で無効にできます。アニメのポスターをクリックし、情報ページに入り、**無効**をクリックします。 無効化されると、アニメのポスターはグレーアウトされ、最後にソートされます。ダウンロードルールを再度有効にするには、**有効**をクリックします。 ================================================ FILE: docs/ja/feature/search.md ================================================ # トレント検索 v3.1以降、ABにはアニメを素早く見つけるための検索機能が含まれています。 ## 検索機能の使用 ::: warning 検索機能はメインプログラムのパーサーに依存しています。現在のバージョンはコレクションの解析をサポートしていません。コレクションを解析する際の`warning`は正常な動作です。 ::: 検索バーはABのトップバーにあります。クリックして検索パネルを開きます。 ![Search Panel](/image/feature/search-panel.png) ソースサイトを選択し、キーワードを入力すると、ABが自動的に解析して検索結果を表示します。アニメを追加するには、カードの右側にある追加ボタンをクリックします。 ::: tip ソースが**Mikan**の場合、ABはデフォルトで`mikan`パーサーを使用します。他のソースの場合、TMDBパーサーが使用されます。 ::: ## 検索ソースの管理 v3.2以降、JSONファイルを編集せずに、設定ページで直接検索ソースを管理できます。 ### 検索プロバイダー設定パネル **設定** → **検索プロバイダー**に移動して、設定パネルにアクセスします。 ![Search Provider Settings](/image/feature/search-provider.png) ここから以下が可能です: - すべての設定済み検索ソースを**表示** - 「プロバイダーを追加」ボタンで新しい検索ソースを**追加** - 既存のソースURLを**編集** - カスタムソースを**削除**(デフォルトソースmikan、nyaa、dmhyは削除できません) ### URLテンプレート形式 カスタムソースを追加する場合、URLには検索キーワードのプレースホルダーとして`%s`を含める必要があります。 例: ``` https://example.com/rss/search?q=%s ``` `%s`はユーザーの検索クエリに置き換えられます。 ### デフォルトソース 以下のソースは組み込みで、削除できません: | ソース | URLテンプレート | |--------|---------------| | mikan | `https://mikanani.me/RSS/Search?searchstr=%s` | | nyaa | `https://nyaa.si/?page=rss&q=%s&c=0_0&f=0` | | dmhy | `http://dmhy.org/topics/rss/rss.xml?keyword=%s` | ### 設定ファイル経由でソースを追加 `config/search_provider.json`を編集してソースを手動で追加することもできます: ```json { "mikan": "https://mikanani.me/RSS/Search?searchstr=%s", "nyaa": "https://nyaa.si/?page=rss&q=%s&c=0_0&f=0", "dmhy": "http://dmhy.org/topics/rss/rss.xml?keyword=%s", "bangumi.moe": "https://bangumi.moe/rss/search/%s" } ``` ================================================ FILE: docs/ja/home/index.md ================================================ --- title: 概要 ---

## AutoBangumiについて

AutoBangumi WebUI

**`AutoBangumi`** は、RSSフィードに基づく全自動アニメダウンロード・整理ツールです。[Mikan Project][mikan]などのサイトでアニメを購読するだけで、新しいエピソードを自動的に追跡・ダウンロードします。 整理されたファイル名とディレクトリ構造は、追加のメタデータスクレイピングなしで[Plex][plex]、[Jellyfin][jellyfin]などのメディアライブラリソフトウェアと直接互換性があります。 ## 機能 - シンプルな一回限りの設定で継続的に使用可能 - アニメ情報を抽出し、自動的にダウンロードルールを生成する手間いらずのRSSパーサー - アニメファイルの整理: ``` Bangumi ├── bangumi_A_title │ ├── Season 1 │ │ ├── A S01E01.mp4 │ │ ├── A S01E02.mp4 │ │ ├── A S01E03.mp4 │ │ └── A S01E04.mp4 │ └── Season 2 │ ├── A S02E01.mp4 │ ├── A S02E02.mp4 │ ├── A S02E03.mp4 │ └── A S02E04.mp4 ├── bangumi_B_title │ └─── Season 1 ``` - 完全自動リネーム — リネーム後、99%以上のアニメファイルがメディアライブラリソフトウェアで直接スクレイピング可能 ``` [Lilith-Raws] Kakkou no Iinazuke - 07 [Baha][WEB-DL][1080p][AVC AAC][CHT][MP4].mp4 >> Kakkou no Iinazuke S01E07.mp4 ``` - 親フォルダ名に基づくすべての子ファイルのカスタムリネーム - シーズン途中からの追跡で現在のシーズンの見逃したエピソードをすべて補完 - 異なるメディアライブラリソフトウェアに合わせて微調整できる高度にカスタマイズ可能なオプション - メンテナンス不要、完全に透明な動作 - 完全なTMDB形式のファイルとアニメメタデータを生成する内蔵TMDBパーサー - Mikan RSSフィードのリバースプロキシサポート ## コミュニティ - 更新通知:[Telegramチャンネル](https://t.me/autobangumi_update) - バグ報告:[Telegram](https://t.me/+yNisOnDGaX5jMTM9) ## 謝辞 [Sean](https://github.com/findix)氏のプロジェクトへの多大なご協力に感謝いたします。 ## コントリビュート IssuesとPull Requestsを歓迎します! ## 免責事項 AutoBangumiは非公式の著作権チャンネルを通じてアニメを取得するため: - AutoBangumiを**商業目的で使用しないでください**。 - AutoBangumiを含むビデオコンテンツを作成し、国内のビデオプラットフォーム(著作権関係者)で**配信しないでください**。 - AutoBangumiを法律や規制に違反する活動に**使用しないでください**。 AutoBangumiは教育目的および個人使用のみを目的としています。 ## ライセンス [MIT License](https://github.com/EstrellaXD/Auto_Bangumi/blob/main/LICENSE) [mikan]: https://mikanani.me [plex]: https://plex.tv [jellyfin]: https://jellyfin.org ================================================ FILE: docs/ja/home/pipline.md ================================================ # AutoBangumiの仕組み AutoBangumi(略称AB)は基本的にRSSパーサーです。アニメトレントサイトからのRSSフィードを解析し、トレントタイトルからメタデータを抽出し、ダウンロードルールを生成してqBittorrentに送信してダウンロードします。ダウンロード後、ファイルを標準的なメディアライブラリのディレクトリ構造に整理します。 ## パイプライン概要 1. **RSS解析** — ABは定期的に購読しているRSSフィードを取得して解析します 2. **タイトル分析** — トレントタイトルを解析してアニメ名、エピソード番号、シーズン、字幕グループ、解像度を抽出します 3. **ルール生成** — 解析された情報に基づいてqBittorrentにダウンロードルールを作成します 4. **ダウンロード管理** — qBittorrentが実際のトレントダウンロードを処理します 5. **ファイル整理** — ダウンロードされたファイルはリネームされ、標準化されたディレクトリ構造に移動されます 6. **メディアライブラリ対応** — 整理されたファイルはPlex、Jellyfin、その他のメディアサーバーで直接認識できます ================================================ FILE: docs/ja/index.md ================================================ --- # https://vitepress.dev/reference/default-theme-home-page layout: home title: AutoBangumi titleTemplate: 全自動アニメ追跡、手間いらず! hero: name: AutoBangumi text: 全自動アニメ追跡、手間いらず! tagline: RSS購読の自動解析、ダウンロード管理、ファイル整理 actions: - theme: brand text: クイックスタート link: /ja/deploy/quick-start - theme: alt text: 概要 link: /ja/home/ - theme: alt text: 更新履歴 link: /ja/changelog/3.2 features: - icon: src: /image/icons/rss.png title: RSS購読解析 details: アニメのRSS購読を自動的に認識・解析。手動入力不要で、購読するだけで自動的に解析、ダウンロード、整理を完了します。 - icon: src: /image/icons/qbittorrent-logo.svg title: qBittorrentダウンローダー details: qBittorrentを使用してアニメをダウンロード。AutoBangumiで既存のアニメ管理、過去のエピソードのダウンロード、エントリの削除が可能です。 - icon: src: /image/icons/tmdb-icon.png title: TMDBメタデータマッチング details: TMDBを通じてアニメ情報をマッチングし、正確なメタデータを取得。複数の字幕グループ間でも正しく解析できます。 - icon: src: /image/icons/plex-icon.png title: Plex / Jellyfin / Infuse ... details: マッチング結果に基づいてファイル名とディレクトリ構造を自動整理。メディアライブラリソフトウェアが高い成功率でメタデータをスクレイピングできるようにします。 ---
## 謝辞 ### 感謝 - [Mikan Project](https://mikanani.me) - 優れたアニメリソースを提供していただきありがとうございます。 - [VitePress](https://vitepress.dev) - 優れたドキュメントフレームワークを提供していただきありがとうございます。 - [qBittorrent](https://www.qbittorrent.org) - 優れたダウンローダーを提供していただきありがとうございます。 - [Plex](https://www.plex.tv) / [Jellyfin](https://jellyfin.org) - 優れたセルフホストメディアライブラリを提供していただきありがとうございます。 - [Infuse](https://firecore.com/infuse) - エレガントなビデオプレーヤーを提供していただきありがとうございます。 - [弾弾 Play](https://www.dandanplay.com) - 優れたコメント付きプレーヤーを提供していただきありがとうございます。 - すべてのアニメ制作チーム / 字幕グループ / ファンの皆様。 ### コントリビューター [ ![](https://contrib.rocks/image?repo=EstrellaXD/Auto_Bangumi){class=contributors-avatar} ](https://github.com/EstrellaXD/Auto_Bangumi/graphs/contributors) ## 免責事項 AutoBangumiは非公式の著作権チャンネルを通じてアニメを取得するため: - AutoBangumiを**商業目的で使用しないでください**。 - AutoBangumiを含むビデオコンテンツを作成し、国内のビデオプラットフォーム(著作権関係者)で**公開しないでください**。 - AutoBangumiを法律や規制に違反する活動に**使用しないでください**。
================================================ FILE: docs/package.json ================================================ { "private": true, "scripts": { "docs:dev": "vitepress dev", "docs:build": "vitepress build", "docs:preview": "vitepress preview" }, "devDependencies": { "@vue/tsconfig": "^0.5.1", "medium-zoom": "^1.1.0", "typescript": "~5.6.0", "vitepress": "1.6.4", "vue": "^3.5.0" }, "type": "module", "engines": { "node": ">=20" } } ================================================ FILE: docs/plans/2026-01-25-search-panel-redesign.md ================================================ # Search Panel Redesign ## Overview Redesign the search bar component from a dropdown list to a full modal-based search experience with advanced filtering capabilities. ## Problems with Current Implementation 1. **Click-outside clears everything** - `v-on-click-outside="clearSearch"` causes accidental loss of results 2. **Limited result display** - Absolute positioned list with no scroll container 3. **No explicit close control** - User has no intentional way to dismiss except clicking outside 4. **No filtering** - Results often contain same anime with different subtitle groups/seasons, hard to find the right one ## Design Goals - Prevent accidental dismissal of search results - Support displaying many results with proper scrolling - Enable filtering by subtitle group, resolution, subtitle type, and season - Provide confirmation step before subscribing --- ## Search Panel Structure ### Trigger & Toggle Behavior - Clicking the search input opens the search modal (if closed) or closes it (if open) - Pressing `Escape` closes the modal - A visible `×` close button in the modal header provides explicit dismissal - Clicking the backdrop does NOT close (prevents accidental loss of results) ### Modal Layout ``` ┌─────────────────────────────────────────────────────────┐ │ 🔍 [Search input] [provider ▼] [×] │ ├─────────────────────────────────────────────────────────┤ │ 字幕组: [喵萌奶茶屋] [ANi] [桜都] [LoliHouse] [+3] │ │ 分辨率: [1080p] [720p] [4K] │ │ 字幕语言: [简中 CHS] [繁中 CHT] [双语] [内嵌] [外挂] │ │ 季度: [S1] [S2] [剧场版] │ │ [清除筛选] 8/24 结果 │ ├─────────────────────────────────────────────────────────┤ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │ │ Result │ │ Result │ │ Result │ │ │ │ Card │ │ Card │ │ Card │ │ │ └─────────┘ └─────────┘ └─────────┘ │ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │ │ Result │ │ Result │ │ Result │ │ │ │ Card │ │ Card │ │ Card │ │ │ └─────────┘ └─────────┘ └─────────┘ │ │ (scrollable grid) │ └─────────────────────────────────────────────────────────┘ ``` - Modal centered on screen with backdrop overlay - Fixed header with search input, provider selector, close button - Filter chips section below header (sticky when scrolling) - Scrollable grid of result cards (3 columns on desktop, responsive) --- ## Filter Chip System ### Four Filter Dimensions 1. **字幕组 (Subtitle Group)** - Fansub team name 2. **分辨率 (Resolution)** - 720p, 1080p, 4K/2160p 3. **字幕语言 (Subtitle Type)** - CHS (简中), CHT (繁中), 双语 (Dual), 内嵌 (Hardcoded), 外挂 (External ASS/SRT) 4. **季度 (Season)** - S1, S2, Movie/剧场版, OVA ### Auto-generated Filters - As results stream in, extract unique values for each dimension - Chips appear dynamically as new filter values are discovered - Parsing logic extracts metadata from torrent titles ### Filter Behavior - Chips are toggleable - click to activate (highlighted), click again to deactivate - Multiple filters can be active simultaneously - Logic: AND within same category, OR across categories - Active filters show with filled background, inactive with outline style - "清除筛选" (Clear all) button appears when any filter is active - Result count updates dynamically: "显示 8 / 24 个结果" ### Overflow Handling - If a category has >5 options, show first 4 + `[+N more]` chip that expands - Each category row can collapse/expand independently - Collapsed state shows badge count for active filters --- ## Result Cards ### Card Design (Compact Grid Item) ``` ┌──────────────────────────┐ │ ┌──────┐ 葬送的芙莉莲 │ │ │poster│ Frieren │ │ │ │ ───────────── │ │ └──────┘ 喵萌奶茶屋 │ │ 1080p · 简中 │ │ S1 · 全28集 │ └──────────────────────────┘ ``` ### Card Content - Thumbnail/poster (if available from API, placeholder if not) - Title (Chinese + Romaji/English) - Subtitle group badge - Resolution + Subtitle type tags - Season + Episode count ### Streaming Animation - Cards fade in with slight upward slide (`opacity: 0 → 1`, `translateY: 8px → 0`) - Staggered delay: each card delays 50ms after previous - Grid re-flows smoothly as new cards appear - Filter changes use same fade animation for showing/hiding ### Empty & Loading States | State | Display | |-------|---------| | Initial | "输入关键词开始搜索" | | Searching | Spinner in search input, cards stream in | | No results | "未找到相关结果,试试其他关键词" | | Filtered to zero | "没有符合筛选条件的结果" + "清除筛选" button | --- ## Confirmation Modal When user clicks a result card, a nested modal appears for review before subscribing. ### Layout ``` ┌─────────────────────────────────────────────────────┐ │ 添加订阅 [×] │ ├─────────────────────────────────────────────────────┤ │ ┌────────┐ │ │ │ │ 葬送的芙莉莲 │ │ │ poster │ Sousou no Frieren │ │ │ │ ★ 9.2 · 2023年秋 · 全28集 │ │ └────────┘ │ ├─────────────────────────────────────────────────────┤ │ RSS 源: [当前选择的RSS链接] [复制] │ │ 字幕组: 喵萌奶茶屋 │ │ 分辨率: 1080p │ │ 字幕类型: 简体中文 (内嵌) │ ├─────────────────────────────────────────────────────┤ │ 高级设置 ▼ │ │ ┌─────────────────────────────────────────────┐ │ │ │ 过滤规则: [自动生成的filter] │ │ │ │ 保存路径: /media/anime/葬送的芙莉莲/ │ │ │ │ 重命名: ☑ 启用自动重命名 │ │ │ └─────────────────────────────────────────────┘ │ ├─────────────────────────────────────────────────────┤ │ [取消] [确认订阅 ✓] │ └─────────────────────────────────────────────────────┘ ``` ### Behavior - Shows parsed metadata for user review - Advanced settings collapsed by default - "确认订阅" adds to subscriptions and closes both modals - "取消" returns to search results (results preserved) - Escape key returns to search results (not full close) --- ## Keyboard Navigation | Key | Action | |-----|--------| | `Enter` (in search input) | Trigger search | | `Escape` | Close confirmation modal (if open) → close search modal | | `Tab` | Navigate through filter chips, then result cards | | `Enter` (on focused card) | Open confirmation modal | | Arrow keys | Navigate grid (optional enhancement) | --- ## Responsive Design ### Breakpoints | Viewport | Grid | Modal Width | Behavior | |----------|------|-------------|----------| | Desktop (>1024px) | 3 columns | 800px centered | Full experience | | Tablet (768-1024px) | 2 columns | 90% width | Filters collapse to single row | | Mobile (<768px) | 1 column | Full screen | Bottom sheet style | ### Mobile Adaptations - Modal becomes full-screen bottom sheet - Filter chips horizontally scrollable (single row) - Cards stack vertically (single column) - Swipe down to close (in addition to × button) --- ## Component Structure ``` ab-search-modal.vue (new - main modal container) ├── ab-search-header.vue (search input + provider + close) ├── ab-search-filters.vue (new - filter chips) ├── ab-search-results.vue (new - scrollable grid) │ └── ab-search-card.vue (new - individual result card) └── ab-search-confirm.vue (new - confirmation modal) ``` ### State Management Extend `useSearchStore` with: - `filters` - active filter state per dimension - `filteredResults` - computed results after filter application - `showModal` - modal open/close state - `selectedResult` - currently selected result for confirmation --- ## Implementation Notes ### Filter Parsing Need to extract metadata from torrent titles. Common patterns: - Subtitle group: `[喵萌奶茶屋]`, `【ANi】` - Resolution: `1080p`, `720p`, `2160p`, `4K` - Subtitle type: `简体`, `繁體`, `CHS`, `CHT`, `简日双语`, `内嵌`, `外挂` - Season: `S01`, `Season 1`, `第一季`, `剧场版`, `Movie`, `OVA` ### SSE Streaming Keep existing SSE implementation but: - Parse metadata from each result as it arrives - Update filter options incrementally - Apply active filters to new results immediately ### Animation Use Vue's `` with staggered delays: ```css .card-enter-active { transition: all 0.3s ease; transition-delay: calc(var(--index) * 50ms); } .card-enter-from { opacity: 0; transform: translateY(8px); } ``` ================================================ FILE: docs/plans/2026-02-23-calendar-drag-organize-design.md ================================================ # Calendar Drag-to-Organize Unknown Bangumi — Implementation Plan > **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. **Goal:** Allow users to drag bangumi cards from the "Unknown" section into weekday columns in the calendar view, with a locked flag to prevent calendar refresh from overwriting manual assignments. **Architecture:** Add a `weekday_locked` boolean field to the Bangumi model. A new API endpoint sets weekday + locks it. The `refresh_calendar()` method skips locked items. Frontend uses vuedraggable for smooth drag-and-drop from Unknown to weekday columns, with a reset/unlock button on manually-pinned cards. **Tech Stack:** Python/FastAPI (backend), SQLModel/SQLite (data), Vue 3 + TypeScript + vuedraggable (frontend) --- ### Task 1: Add `weekday_locked` field to data model + migration **Files:** - Modify: `backend/src/module/models/bangumi.py:36-38` - Modify: `backend/src/module/database/combine.py:26,99-105` **Step 1: Add `weekday_locked` field to `Bangumi` model** In `backend/src/module/models/bangumi.py`, after the `air_weekday` field (line 38), add: ```python weekday_locked: bool = Field( default=False, alias="weekday_locked", title="放送星期锁定" ) ``` **Step 2: Add `weekday_locked` to `BangumiUpdate` model** In the same file, after `air_weekday` in `BangumiUpdate` (line 79), add: ```python weekday_locked: bool = Field( default=False, alias="weekday_locked", title="放送星期锁定" ) ``` **Step 3: Add database migration** In `backend/src/module/database/combine.py`: 1. Increment `CURRENT_SCHEMA_VERSION` from `8` to `9` 2. Add migration entry after the existing migration 8: ```python ( 9, "add weekday_locked column to bangumi", [ "ALTER TABLE bangumi ADD COLUMN weekday_locked BOOLEAN DEFAULT 0", ], ), ``` 3. Add skip-check in `run_migrations()` after the version 8 check: ```python if "bangumi" in tables and version == 9: columns = [col["name"] for col in inspector.get_columns("bangumi")] if "weekday_locked" in columns: needs_run = False ``` **Step 4: Run backend tests to verify migration** Run: `cd backend && uv run pytest src/test/ -v -k "not test_mcp"` Expected: All pass (new column has default, so no breaking changes) **Step 5: Commit** ```bash git add backend/src/module/models/bangumi.py backend/src/module/database/combine.py git commit -m "feat(model): add weekday_locked field to bangumi for manual calendar assignment" ``` --- ### Task 2: Add backend API endpoint for setting weekday **Files:** - Modify: `backend/src/module/api/bangumi.py` - Modify: `backend/src/module/database/bangumi.py` **Step 1: Add `set_weekday` database method** In `backend/src/module/database/bangumi.py`, add method to `BangumiDatabase`: ```python def set_weekday(self, _id: int, weekday: int | None) -> bool: """Set air_weekday and weekday_locked for manual calendar assignment.""" bangumi = self.session.get(Bangumi, _id) if not bangumi: return False if weekday is not None: bangumi.air_weekday = weekday bangumi.weekday_locked = True else: bangumi.air_weekday = None bangumi.weekday_locked = False self.session.add(bangumi) self.session.commit() _invalidate_bangumi_cache() logger.debug( "[Database] Set weekday=%s, locked=%s for bangumi id %s", weekday, bangumi.weekday_locked, _id, ) return True ``` **Step 2: Add API endpoint** In `backend/src/module/api/bangumi.py`, add request model and endpoint: ```python class SetWeekdayRequest(BaseModel): weekday: Optional[int] = None # 0-6 for Mon-Sun, None to reset @router.patch( path="/{bangumi_id}/weekday", response_model=APIResponse, dependencies=[Depends(get_current_user)], ) async def set_weekday(bangumi_id: int, request: SetWeekdayRequest): """Manually set the broadcast weekday for a bangumi.""" if request.weekday is not None and not (0 <= request.weekday <= 6): return JSONResponse( status_code=400, content={ "status": False, "msg_en": "Weekday must be 0-6 (Mon-Sun) or null.", "msg_zh": "星期必须是 0-6(周一至周日)或空。", }, ) with Database() as db: success = db.bangumi.set_weekday(bangumi_id, request.weekday) if success: action = f"weekday {request.weekday}" if request.weekday is not None else "unknown" return JSONResponse( status_code=200, content={ "status": True, "msg_en": f"Set bangumi to {action}.", "msg_zh": f"已设置放送日为 {action}。", }, ) return JSONResponse( status_code=404, content={ "status": False, "msg_en": f"Bangumi {bangumi_id} not found.", "msg_zh": f"未找到番剧 {bangumi_id}。", }, ) ``` **Step 3: Modify `refresh_calendar()` to skip locked items** In `backend/src/module/manager/torrent.py`, in `refresh_calendar()` method (line 212-213), change: ```python # Before: if bangumi.deleted: continue # After: if bangumi.deleted or bangumi.weekday_locked: continue ``` **Step 4: Run tests** Run: `cd backend && uv run pytest src/test/ -v -k "not test_mcp"` Expected: All pass **Step 5: Commit** ```bash git add backend/src/module/api/bangumi.py backend/src/module/database/bangumi.py backend/src/module/manager/torrent.py git commit -m "feat(api): add PATCH /bangumi/{id}/weekday endpoint and skip locked items in calendar refresh" ``` --- ### Task 3: Add frontend TypeScript types and API client **Files:** - Modify: `webui/types/bangumi.ts` - Modify: `webui/src/api/bangumi.ts` **Step 1: Add `weekday_locked` to TypeScript types** In `webui/types/bangumi.ts`, add to `BangumiRule` interface (after `air_weekday` line 26): ```typescript weekday_locked: boolean; ``` Add to `ruleTemplate` (after `air_weekday: null` line 65): ```typescript weekday_locked: false, ``` **Step 2: Add API method for setting weekday** In `webui/src/api/bangumi.ts`, add method to `apiBangumi`: ```typescript /** * 手动设置番剧的放送星期 * @param bangumiId - bangumi 的 id * @param weekday - 0-6 for Mon-Sun, null to reset */ async setWeekday(bangumiId: number, weekday: number | null) { const { data } = await axios.patch( `api/v1/bangumi/${bangumiId}/weekday`, { weekday } ); return data; }, ``` **Step 3: Commit** ```bash git add webui/types/bangumi.ts webui/src/api/bangumi.ts git commit -m "feat(webui): add weekday_locked type and setWeekday API client" ``` --- ### Task 4: Install vuedraggable **Files:** - Modify: `webui/package.json` **Step 1: Install vuedraggable** ```bash cd webui && pnpm add vuedraggable@next ``` **Step 2: Commit** ```bash git add webui/package.json webui/pnpm-lock.yaml git commit -m "chore(webui): add vuedraggable dependency for calendar drag-and-drop" ``` --- ### Task 5: Add i18n strings for drag-and-drop **Files:** - Modify: `webui/src/i18n/en.json` - Modify: `webui/src/i18n/zh-CN.json` **Step 1: Add English i18n strings** In `webui/src/i18n/en.json`, inside the `"calendar"` object (before the closing `}`), add: ```json "drag_hint": "Drag to assign weekday", "pinned": "Manually assigned", "unpin": "Reset to unknown", "drop_here": "Drop here" ``` **Step 2: Add Chinese i18n strings** In `webui/src/i18n/zh-CN.json`, inside the `"calendar"` object, add: ```json "drag_hint": "拖拽以设置放送日", "pinned": "手动设置", "unpin": "重置为未知", "drop_here": "拖放到此处" ``` **Step 3: Commit** ```bash git add webui/src/i18n/en.json webui/src/i18n/zh-CN.json git commit -m "feat(i18n): add calendar drag-and-drop strings" ``` --- ### Task 6: Implement drag-and-drop in calendar.vue (Desktop) **Files:** - Modify: `webui/src/pages/index/calendar.vue` This is the main implementation task. The calendar.vue file needs: 1. **Import vuedraggable** and add drag-and-drop functionality 2. **Wrap Unknown section cards** in a `` component as the drag source 3. **Wrap each weekday column** in a `` component as drop targets 4. **Handle the `onChange` event** to call the API when a card is dropped 5. **Add reset/unpin button** on cards with `weekday_locked === true` 6. **Add visual pin indicator** on locked cards 7. **Add drop-zone highlighting** CSS for when dragging over a weekday column Key implementation details: - vuedraggable uses `group` option to allow cross-list dragging - Unknown list: `group: { name: 'calendar', pull: true, put: false }` (can pull from, cannot put into) - Weekday lists: `group: { name: 'calendar', pull: false, put: true }` (can put into, cannot pull from) - On `change` event with `added` property, extract the bangumi group's primary ID and target day index, then call `apiBangumi.setWeekday(id, dayIndex)` - After API success, update the local bangumi store to reflect the change - The reset button calls `apiBangumi.setWeekday(id, null)` and refreshes store CSS additions: - `.calendar-column--drop-active`: highlight border when dragging over - `.calendar-card--pinned`: pin icon overlay - `.calendar-unpin-btn`: reset button style - `.sortable-ghost`: semi-transparent placeholder during drag - `.sortable-drag`: shadow on the card being dragged **Step 1: Implement the full calendar.vue changes** (See implementation — this is a substantial template + script change) **Step 2: Test manually in dev server** ```bash cd webui && pnpm dev ``` Verify: - Unknown cards can be dragged to weekday columns - Weekday column highlights on dragover - Dropped cards show pin icon - Pin icon has working reset button - Cards with weekday_locked show pin in weekday columns - Mobile view still works (no drag on mobile — touch has different UX) **Step 3: Commit** ```bash git add webui/src/pages/index/calendar.vue git commit -m "feat(calendar): add drag-and-drop from Unknown to weekday columns with pin/reset" ``` --- ### Task 7: Update bangumi store to handle weekday_locked **Files:** - Modify: `webui/src/store/bangumi.ts` (if needed for reactive updates after setWeekday) **Step 1: Add `setWeekday` action to store** Add a store action that calls the API and updates the local bangumi array reactively: ```typescript async function setWeekday(bangumiId: number, weekday: number | null) { await apiBangumi.setWeekday(bangumiId, weekday); // Update local state const item = bangumi.value?.find((b) => b.id === bangumiId); if (item) { item.air_weekday = weekday; item.weekday_locked = weekday !== null; } } ``` **Step 2: Commit** ```bash git add webui/src/store/bangumi.ts git commit -m "feat(store): add setWeekday action for calendar drag-and-drop" ``` --- ### Task 8: Final integration test and type check **Step 1: Run type check** ```bash cd webui && pnpm test:build ``` **Step 2: Run backend tests** ```bash cd backend && uv run pytest src/test/ -v -k "not test_mcp" ``` **Step 3: Run lint** ```bash cd webui && pnpm lint cd backend && uv run ruff check src ``` **Step 4: Fix any issues and commit** ```bash git add -A git commit -m "fix: resolve type and lint issues from calendar drag-and-drop feature" ``` ================================================ FILE: docs/resource/docker-compose/AutoBangumi/docker-compose.yml ================================================ version: "3.4" services: AutoBangumi: image: "ghcr.io/estrellaxd/auto_bangumi:latest" container_name: AutoBangumi volumes: - ./config:/app/config - ./data:/app/data network_mode: bridge ports: - "7892:7892" restart: unless-stopped dns: - 223.5.5.5 environment: - TZ=Asia/Shanghai - PGID=$(id -g) - PUID=$(id -u) - UMASK=022 ================================================ FILE: docs/resource/docker-compose/qBittorrent+AutoBangumi/docker-compose.yml ================================================ version: "3.4" services: qbittorrent: container_name: qbittorrent image: linuxserver/qbittorrent hostname: qbittorrent environment: - PGID=$(id -g) - PUID=$(id -u) - TZ=Asia/Shanghai volumes: - ./qb_config:/config - :/downloads # 注意 修改此处为自己存放动漫的目录,ab 内下载路径填写downloads network_mode: bridge restart: unless-stopped AutoBangumi: image: "ghcr.io/estrellaxd/auto_bangumi:latest" container_name: AutoBangumi depends_on: - qbittorrent volumes: - ./config:/app/config - ./data:/app/data network_mode: bridge dns: - 223.5.5.5 restart: unless-stopped environment: - TZ=Asia/Shanghai - PGID=$(id -g) - PUID=$(id -u) - UMASK=022 ================================================ FILE: docs/resource/unraid.xml ================================================ AutoBangumi estrellaxd/auto_bangumi:latest https://registry.hub.docker.com/r/estrellaxd/auto_bangumi bridge MediaServer:Video Tools Productivity sh false https://github.com/EstrellaXD/Auto_Bangumi/issues AutoBangumi 是基于 Mikan Project、qBittorrent 的全自动追番整理下载工具。只需要在 Mikan Project 上订阅番剧,就可以全自动追番。并且整理完成的名称和目录可以直接被 Plex、Jellyfin 等媒体库软件识别,无需二次刮削。 http://[IP]:[PORT:7892]/ https://quantil.jsdelivr.net/gh/9bingyin/static@main/images/AutoBangumi.png --dns 8.8.8.8 --dns 223.5.5.5 7892 /mnt/user/appdata/AutoBangumi/config /mnt/user/appdata/AutoBangumi/data ================================================ FILE: docs/tsconfig.json ================================================ { "extends": "@vue/tsconfig/tsconfig.json", "compilerOptions": { "module": "ESNext", "moduleResolution": "bundler" } } ================================================ FILE: docs/vercel.json ================================================ { "buildCommand": "pnpm docs:build", "outputDirectory": ".vitepress/dist", "installCommand": "pnpm install", "framework": "vitepress" } ================================================ FILE: entrypoint.sh ================================================ #!/bin/bash # shellcheck shell=bash umask ${UMASK} if [ -f /config/bangumi.json ]; then mv /config/bangumi.json /app/data/bangumi.json fi groupmod -o -g "${PGID}" ab usermod -o -u "${PUID}" ab chown ab:ab -R /app /home/ab exec su-exec "${PUID}:${PGID}" python main.py ================================================ FILE: scripts/generate-beta-notes.sh ================================================ #!/bin/bash # Generate release notes for beta versions # Usage: ./generate-beta-notes.sh [previous-tag] set -e NEW_TAG="${1:?Usage: $0 [previous-tag]}" REPO_URL="https://github.com/EstrellaXD/Auto_Bangumi" # Auto-detect previous beta tag if not provided if [ -z "$2" ]; then PREV_TAG=$(git tag --sort=-v:refname | grep -E "^${NEW_TAG%-*}" | grep beta | head -2 | tail -1) else PREV_TAG="$2" fi if [ -z "$PREV_TAG" ] || [ "$PREV_TAG" = "$NEW_TAG" ]; then echo "## Initial Beta Release" echo "" echo "First beta for this version series." exit 0 fi echo "## Changes since ${PREV_TAG#v}" echo "" # Get commits and categorize FEATS=$(git log --oneline "$PREV_TAG..$NEW_TAG" --no-merges --grep="^feat" --format="- %s" 2>/dev/null || true) FIXES=$(git log --oneline "$PREV_TAG..$NEW_TAG" --no-merges --grep="^fix" --format="- %s" 2>/dev/null || true) PERFS=$(git log --oneline "$PREV_TAG..$NEW_TAG" --no-merges --grep="^perf" --format="- %s" 2>/dev/null || true) DOCS=$(git log --oneline "$PREV_TAG..$NEW_TAG" --no-merges --grep="^docs" --format="- %s" 2>/dev/null || true) OTHERS=$(git log --oneline "$PREV_TAG..$NEW_TAG" --no-merges --grep="^chore\|^refactor\|^test\|^ci" --format="- %s" 2>/dev/null || true) if [ -n "$FEATS" ]; then echo "### Features" echo "$FEATS" echo "" fi if [ -n "$FIXES" ]; then echo "### Fixes" echo "$FIXES" echo "" fi if [ -n "$PERFS" ]; then echo "### Performance" echo "$PERFS" echo "" fi if [ -n "$DOCS" ]; then echo "### Documentation" echo "$DOCS" echo "" fi # Don't show chore/refactor/test in release notes (too noisy) echo "---" echo "**Full Changelog**: ${REPO_URL}/compare/${PREV_TAG}...${NEW_TAG}" ================================================ FILE: webui/.eslintignore ================================================ /build /dist /dev-dist ================================================ FILE: webui/.eslintrc.json ================================================ { "extends": ["@antfu", "prettier", "plugin:storybook/recommended"], "rules": { "antfu/if-newline": ["off"], "no-console": ["off"], "vue/custom-event-name-casing": ["off"] } } ================================================ FILE: webui/.husky/pre-commit ================================================ #!/usr/bin/env sh . "$(dirname -- "$0")/_/husky.sh" cd ./webui && pnpm run lint:fix && pnpm run format ================================================ FILE: webui/.neoconf.json ================================================ { "volar": { "enable": true } } ================================================ FILE: webui/.npmrc ================================================ public-hoist-pattern[]=@vue/runtime-core public-hoist-pattern[]=*eslint* public-hoist-pattern[]=*prettier* ================================================ FILE: webui/.prettierignore ================================================ /build /dist /dev-dist /pnpm-lock.yaml auto-imports.d.ts components.d.ts router-type.d.ts ================================================ FILE: webui/.prettierrc.json ================================================ { "singleQuote": true } ================================================ FILE: webui/.storybook/main.ts ================================================ import type { StorybookConfig } from '@storybook/vue3-vite'; import Unocss from 'unocss/vite'; const config: StorybookConfig = { stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'], addons: [ '@storybook/addon-links', '@storybook/addon-essentials', '@storybook/addon-interactions', ], framework: { name: '@storybook/vue3-vite', options: {}, }, docs: { autodocs: 'tag', }, viteFinal(config) { config.plugins?.push(Unocss()); // Add other configuration here depending on your use case return config; }, }; export default config; ================================================ FILE: webui/.storybook/preview.ts ================================================ import type { Preview } from '@storybook/vue3'; import '@unocss/reset/tailwind-compat.css'; import 'uno.css'; const preview: Preview = { parameters: { actions: { argTypesRegex: '^on[A-Z].*' }, controls: { matchers: { color: /(background|color)$/i, date: /Date$/, }, }, }, }; export default preview; ================================================ FILE: webui/.vscode/extensions.json ================================================ { "recommendations": ["Vue.volar"] } ================================================ FILE: webui/.vscode/settings.json ================================================ { "i18n-ally.localesPaths": ["src/i18n"], "commentTranslate.targetLanguage": "zh-CN", "i18n-ally.sourceLanguage": "zh-CN", "typescript.tsdk": "node_modules/typescript/lib", "i18n-ally.keystyle": "nested" } ================================================ FILE: webui/LICENSE ================================================ MIT License Copyright (c) 2022 Rewrite0 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: webui/README.md ================================================ # Auto_Bangumi_WebUI 使用 Vue3 + TypeScript 构建的 [Auto_Bangumi](https://github.com/EstrellaXD/Auto_Bangumi) 的 WebUI ================================================ FILE: webui/index.html ================================================ AutoBangumi
================================================ FILE: webui/package.json ================================================ { "name": "ab-webui", "type": "module", "version": "0.0.0", "private": true, "packageManager": "pnpm@9.11.0+sha512.0a203ffaed5a3f63242cd064c8fb5892366c103e328079318f78062f24ea8c9d50bc6a47aa3567cabefd824d170e78fa2745ed1f16b132e16436146b7688f19b", "scripts": { "prepare": "cd .. && husky install ./webui/.husky", "test:build": "vue-tsc --noEmit", "build": "vite build", "dev": "vite", "format": "prettier --write .", "format:check": "prettier --check .", "lint": "eslint .", "lint:fix": "eslint . --fix", "preview": "vite preview", "test": "vitest", "storybook": "storybook dev -p 6006", "build-storybook": "storybook build", "generate-pwa-assets": "pwa-assets-generator --preset minimal public/images/logo.svg" }, "dependencies": { "@headlessui/vue": "^1.7.23", "@vueuse/components": "^10.11.1", "@vueuse/core": "^10.11.1", "axios": "^0.27.2", "naive-ui": "^2.39.0", "pinia": "^2.2.2", "vue": "^3.5.8", "vue-i18n": "^9.14.0", "vue-inline-svg": "^3.1.4", "vue-router": "^4.4.5", "vuedraggable": "^4.1.0" }, "devDependencies": { "@antfu/eslint-config": "^0.38.6", "@icon-park/vue-next": "^1.4.2", "@intlify/unplugin-vue-i18n": "^0.11.0", "@storybook/addon-essentials": "^7.6.20", "@storybook/addon-interactions": "^7.6.20", "@storybook/addon-links": "^7.6.20", "@storybook/blocks": "^7.6.20", "@storybook/testing-library": "0.0.14-next.2", "@storybook/vue3": "^7.6.20", "@storybook/vue3-vite": "^7.6.20", "@types/node": "^18.19.50", "@unocss/preset-attributify": "^0.55.7", "@unocss/preset-rem-to-px": "^0.51.13", "@unocss/reset": "^0.51.13", "@vitejs/plugin-vue": "^4.6.2", "@vitejs/plugin-vue-jsx": "^3.1.0", "@vue/runtime-dom": "^3.5.8", "@vue/test-utils": "^2.4.6", "eslint": "^8.57.1", "eslint-config-prettier": "^8.10.0", "eslint-plugin-storybook": "^0.6.15", "happy-dom": "^12.10.3", "husky": "^8.0.3", "prettier": "^2.8.8", "radash": "^12.1.0", "sass-embedded": "^1.79.3", "storybook": "^7.6.20", "typescript": "^4.9.5", "unocss": "^0.51.13", "unplugin-auto-import": "^0.10.3", "unplugin-vue-components": "^0.24.1", "unplugin-vue-router": "^0.6.4", "vite": "^4.5.5", "vite-plugin-pwa": "^0.16.7", "vitest": "^0.30.1", "vue-tsc": "^1.8.27" } } ================================================ FILE: webui/public/robots.txt ================================================ # robots.txt generated at http://tool.chinaz.com/robots/ User-agent: Baiduspider Disallow: / User-agent: Sosospider Disallow: / User-agent: sogou spider Disallow: / User-agent: YodaoBot Disallow: / User-agent: Googlebot Disallow: / User-agent: Bingbot Disallow: / User-agent: Slurp Disallow: / User-agent: Teoma Disallow: / User-agent: ia_archiver Disallow: / User-agent: twiceler Disallow: / User-agent: MSNBot Disallow: / User-agent: Scrubby Disallow: / User-agent: Robozilla Disallow: / User-agent: Gigabot Disallow: / User-agent: googlebot-image Disallow: / User-agent: googlebot-mobile Disallow: / User-agent: yahoo-mmcrawler Disallow: / User-agent: yahoo-blogs/v3.9 Disallow: / User-agent: psbot Disallow: / ================================================ FILE: webui/src/App.vue ================================================ ================================================ FILE: webui/src/api/__tests__/auth.test.ts ================================================ /** * Tests for Auth API logic * Note: These tests focus on the data structures and transformations */ import { describe, it, expect } from 'vitest'; import { mockLoginSuccess } from '@/test/mocks/api'; describe('Auth API Data Structures', () => { describe('login response', () => { it('should have access_token and token_type', () => { expect(mockLoginSuccess.access_token).toBeDefined(); expect(mockLoginSuccess.token_type).toBe('bearer'); }); it('should have string access_token', () => { expect(typeof mockLoginSuccess.access_token).toBe('string'); expect(mockLoginSuccess.access_token.length).toBeGreaterThan(0); }); }); describe('login request formation', () => { it('should create URLSearchParams with username and password', () => { const username = 'testuser'; const password = 'testpassword'; const formData = new URLSearchParams({ username, password, }); expect(formData.toString()).toContain('username=testuser'); expect(formData.toString()).toContain('password=testpassword'); }); it('should properly encode special characters in credentials', () => { const username = 'test@user.com'; const password = 'pass&word=123'; const formData = new URLSearchParams({ username, password, }); expect(formData.get('username')).toBe('test@user.com'); expect(formData.get('password')).toBe('pass&word=123'); }); }); describe('update request formation', () => { it('should create update payload with username and password', () => { const username = 'newuser'; const password = 'newpassword123'; const payload = { username, password, }; expect(payload.username).toBe('newuser'); expect(payload.password).toBe('newpassword123'); }); }); describe('API endpoint paths', () => { const AUTH_ENDPOINTS = { login: 'api/v1/auth/login', logout: 'api/v1/auth/logout', refresh: 'api/v1/auth/refresh_token', update: 'api/v1/auth/update', }; it('should have correct login endpoint', () => { expect(AUTH_ENDPOINTS.login).toBe('api/v1/auth/login'); }); it('should have correct logout endpoint', () => { expect(AUTH_ENDPOINTS.logout).toBe('api/v1/auth/logout'); }); it('should have correct refresh endpoint', () => { expect(AUTH_ENDPOINTS.refresh).toBe('api/v1/auth/refresh_token'); }); it('should have correct update endpoint', () => { expect(AUTH_ENDPOINTS.update).toBe('api/v1/auth/update'); }); }); }); ================================================ FILE: webui/src/api/__tests__/bangumi.test.ts ================================================ /** * Tests for Bangumi API data transformation logic * Note: These tests focus on the filter/rss_link string<->array transformations */ import { describe, it, expect } from 'vitest'; import { mockBangumiAPI, mockBangumiRule, } from '@/test/mocks/api'; describe('Bangumi API Logic', () => { describe('getAll transformation (string to array)', () => { // This transformation happens when receiving data from API const transformApiResponse = (item: T) => ({ ...item, filter: item.filter.split(','), rss_link: item.rss_link.split(','), }); it('should transform filter string to array', () => { const apiData = { ...mockBangumiAPI, filter: '720', rss_link: 'url1' }; const result = transformApiResponse(apiData); expect(Array.isArray(result.filter)).toBe(true); expect(result.filter).toEqual(['720']); }); it('should handle empty filter string', () => { const apiData = { ...mockBangumiAPI, filter: '', rss_link: '' }; const result = transformApiResponse(apiData); expect(result.filter).toEqual(['']); expect(result.rss_link).toEqual(['']); }); it('should handle multiple comma-separated values', () => { const apiData = { ...mockBangumiAPI, filter: '720,1080,480', rss_link: 'url1,url2,url3', }; const result = transformApiResponse(apiData); expect(result.filter).toEqual(['720', '1080', '480']); expect(result.rss_link).toEqual(['url1', 'url2', 'url3']); }); it('should preserve other fields during transformation', () => { const apiData = { ...mockBangumiAPI, id: 42, title_raw: 'Test Title', filter: '720', rss_link: 'url1', }; const result = transformApiResponse(apiData); expect(result.id).toBe(42); expect(result.title_raw).toBe('Test Title'); }); }); describe('updateRule transformation (array to string)', () => { // This transformation happens when sending data to API const transformForUpdate = (rule: { id: number; filter: string[]; rss_link: string[] }) => { const { id, ...rest } = rule; return { ...rest, filter: rule.filter.join(','), rss_link: rule.rss_link.join(','), }; }; it('should transform filter array to string', () => { const rule = { ...mockBangumiRule, filter: ['720'], rss_link: ['url1'] }; const result = transformForUpdate(rule); expect(typeof result.filter).toBe('string'); expect(result.filter).toBe('720'); }); it('should join multiple filter values with commas', () => { const rule = { ...mockBangumiRule, filter: ['720', '1080', '480'], rss_link: ['url1', 'url2'], }; const result = transformForUpdate(rule); expect(result.filter).toBe('720,1080,480'); expect(result.rss_link).toBe('url1,url2'); }); it('should omit id from update payload', () => { const rule = { ...mockBangumiRule, id: 123 }; const result = transformForUpdate(rule); expect(result).not.toHaveProperty('id'); }); it('should handle empty arrays', () => { const rule = { ...mockBangumiRule, filter: [], rss_link: [] }; const result = transformForUpdate(rule); expect(result.filter).toBe(''); expect(result.rss_link).toBe(''); }); }); describe('deleteRule logic', () => { it('should use single endpoint for single ID', () => { const id = 1; const isArray = Array.isArray(id); expect(isArray).toBe(false); // Single ID should use: `api/v1/bangumi/delete/${id}` }); it('should use many endpoint for array of IDs', () => { const ids = [1, 2, 3]; const isArray = Array.isArray(ids); expect(isArray).toBe(true); // Array should use: `api/v1/bangumi/delete/many` }); }); describe('API endpoint paths', () => { const BANGUMI_ENDPOINTS = { getAll: 'api/v1/bangumi/get/all', getOne: (id: number) => `api/v1/bangumi/get/${id}`, update: (id: number) => `api/v1/bangumi/update/${id}`, delete: (id: number) => `api/v1/bangumi/delete/${id}`, deleteMany: 'api/v1/bangumi/delete/many', disable: (id: number) => `api/v1/bangumi/disable/${id}`, disableMany: 'api/v1/bangumi/disable/many', enable: (id: number) => `api/v1/bangumi/enable/${id}`, archive: (id: number) => `api/v1/bangumi/archive/${id}`, unarchive: (id: number) => `api/v1/bangumi/unarchive/${id}`, resetAll: 'api/v1/bangumi/reset/all', detectOffset: 'api/v1/bangumi/detect-offset', needsReview: 'api/v1/bangumi/needs-review', }; it('should generate correct getOne endpoint', () => { expect(BANGUMI_ENDPOINTS.getOne(42)).toBe('api/v1/bangumi/get/42'); }); it('should generate correct update endpoint', () => { expect(BANGUMI_ENDPOINTS.update(42)).toBe('api/v1/bangumi/update/42'); }); it('should have correct static endpoints', () => { expect(BANGUMI_ENDPOINTS.getAll).toBe('api/v1/bangumi/get/all'); expect(BANGUMI_ENDPOINTS.deleteMany).toBe('api/v1/bangumi/delete/many'); expect(BANGUMI_ENDPOINTS.needsReview).toBe('api/v1/bangumi/needs-review'); }); }); }); ================================================ FILE: webui/src/api/__tests__/rss.test.ts ================================================ /** * Tests for RSS API logic * Note: These tests focus on data structures and endpoint paths */ import { describe, it, expect } from 'vitest'; import { mockRSSItem, mockRSSList, } from '@/test/mocks/api'; describe('RSS API Logic', () => { describe('RSS data structure', () => { it('should have required RSS fields', () => { expect(mockRSSItem).toHaveProperty('id'); expect(mockRSSItem).toHaveProperty('name'); expect(mockRSSItem).toHaveProperty('url'); expect(mockRSSItem).toHaveProperty('enabled'); }); it('should have correct field types', () => { expect(typeof mockRSSItem.id).toBe('number'); expect(typeof mockRSSItem.name).toBe('string'); expect(typeof mockRSSItem.url).toBe('string'); expect(typeof mockRSSItem.enabled).toBe('boolean'); }); }); describe('RSS list operations', () => { it('should handle empty list', () => { const emptyList: typeof mockRSSList = []; expect(emptyList.length).toBe(0); }); it('should be able to filter enabled feeds', () => { const enabled = mockRSSList.filter((rss) => rss.enabled); expect(enabled.length).toBeGreaterThanOrEqual(0); }); it('should be able to filter disabled feeds', () => { const disabled = mockRSSList.filter((rss) => !rss.enabled); expect(disabled.length).toBeGreaterThanOrEqual(0); }); }); describe('batch operations data format', () => { it('should format deleteMany as array of IDs', () => { const idsToDelete = [1, 2, 3]; expect(Array.isArray(idsToDelete)).toBe(true); expect(idsToDelete).toEqual([1, 2, 3]); }); it('should format disableMany as array of IDs', () => { const idsToDisable = [1, 2]; expect(Array.isArray(idsToDisable)).toBe(true); expect(idsToDisable).toEqual([1, 2]); }); it('should format enableMany as array of IDs', () => { const idsToEnable = [1, 2, 3]; expect(Array.isArray(idsToEnable)).toBe(true); expect(idsToEnable).toEqual([1, 2, 3]); }); }); describe('API endpoint paths', () => { const RSS_ENDPOINTS = { get: 'api/v1/rss', add: 'api/v1/rss/add', delete: (id: number) => `api/v1/rss/delete/${id}`, deleteMany: 'api/v1/rss/delete/many', disable: (id: number) => `api/v1/rss/disable/${id}`, disableMany: 'api/v1/rss/disable/many', update: (id: number) => `api/v1/rss/update/${id}`, enableMany: 'api/v1/rss/enable/many', refreshAll: 'api/v1/rss/refresh/all', refresh: (id: number) => `api/v1/rss/refresh/${id}`, getTorrent: (id: number) => `api/v1/rss/torrent/${id}`, }; it('should have correct base RSS endpoint', () => { expect(RSS_ENDPOINTS.get).toBe('api/v1/rss'); }); it('should have correct add endpoint', () => { expect(RSS_ENDPOINTS.add).toBe('api/v1/rss/add'); }); it('should generate correct delete endpoint for ID', () => { expect(RSS_ENDPOINTS.delete(1)).toBe('api/v1/rss/delete/1'); expect(RSS_ENDPOINTS.delete(42)).toBe('api/v1/rss/delete/42'); }); it('should have correct deleteMany endpoint', () => { expect(RSS_ENDPOINTS.deleteMany).toBe('api/v1/rss/delete/many'); }); it('should generate correct disable endpoint for ID', () => { expect(RSS_ENDPOINTS.disable(1)).toBe('api/v1/rss/disable/1'); }); it('should have correct batch operation endpoints', () => { expect(RSS_ENDPOINTS.disableMany).toBe('api/v1/rss/disable/many'); expect(RSS_ENDPOINTS.enableMany).toBe('api/v1/rss/enable/many'); }); it('should generate correct update endpoint for ID', () => { expect(RSS_ENDPOINTS.update(1)).toBe('api/v1/rss/update/1'); }); it('should have correct refresh endpoints', () => { expect(RSS_ENDPOINTS.refreshAll).toBe('api/v1/rss/refresh/all'); expect(RSS_ENDPOINTS.refresh(1)).toBe('api/v1/rss/refresh/1'); }); it('should generate correct getTorrent endpoint for ID', () => { expect(RSS_ENDPOINTS.getTorrent(1)).toBe('api/v1/rss/torrent/1'); }); }); describe('update payload', () => { it('should include all RSS fields in update', () => { const updatedRSS = { ...mockRSSItem, name: 'Updated Feed' }; expect(updatedRSS.id).toBe(mockRSSItem.id); expect(updatedRSS.name).toBe('Updated Feed'); expect(updatedRSS.url).toBe(mockRSSItem.url); expect(updatedRSS.enabled).toBe(mockRSSItem.enabled); }); }); }); ================================================ FILE: webui/src/api/auth.ts ================================================ import type { LoginSuccess, Update } from '#/auth'; import type { ApiSuccess } from '#/api'; export const apiAuth = { async login(username: string, password: string) { const formData = new URLSearchParams({ username, password, }); const { data } = await axios.post( 'api/v1/auth/login', formData, { headers: { 'Content-Type': 'application/x-www-form-urlencoded', }, } ); return data; }, async refresh() { const { data } = await axios.get('api/v1/auth/refresh_token'); return data; }, async logout() { const { data } = await axios.get('api/v1/auth/logout'); return data; }, async update(username: string, password: string) { const { data } = await axios.post('api/v1/auth/update', { username, password, }); return data; }, }; ================================================ FILE: webui/src/api/bangumi.ts ================================================ import { omit } from 'radash'; import type { BangumiAPI, BangumiRule, DetectOffsetRequest, DetectOffsetResponse, OffsetSuggestion, } from '#/bangumi'; import type { ApiSuccess } from '#/api'; export const apiBangumi = { /** * 获取所有 bangumi 数据 * @returns 所有 bangumi 数据 */ async getAll() { const { data } = await axios.get('api/v1/bangumi/get/all'); const result: BangumiRule[] = data.map((bangumi) => ({ ...bangumi, filter: bangumi.filter.split(','), rss_link: bangumi.rss_link.split(','), air_weekday: bangumi.air_weekday ?? null, })); return result; }, /** * 获取指定 bangumiId 的规则 * @param bangumiId bangumi id * @returns 指定 bangumi 的规则 */ async getRule(bangumiId: number) { const { data } = await axios.get( `api/v1/bangumi/get/${bangumiId}` ); const result: BangumiRule = { ...data, filter: data.filter.split(','), rss_link: data.rss_link.split(','), air_weekday: data.air_weekday ?? null, }; return result; }, /** * 更新指定 bangumiId 的规则 * @param bangumiId - 需要更新的 bangumi 的 id * @param bangumiRule * @returns axios 请求返回的数据 */ async updateRule(bangumiId: number, bangumiRule: BangumiRule) { const rule: BangumiAPI = { ...bangumiRule, filter: bangumiRule.filter.join(','), rss_link: bangumiRule.rss_link.join(','), }; const post = omit(rule, ['id']); const { data } = await axios.patch( `api/v1/bangumi/update/${bangumiId}`, post ); return data; }, /** * 删除指定 bangumiId 的数据库规则,会在重新匹配到后重建 * @param bangumiId - 需要删除的 bangumi 的 id * @param file - 是否同时删除关联文件。 * @returns axios 请求返回的数据 */ async deleteRule(bangumiId: number | number[], file: boolean) { let url = 'api/v1/bangumi/delete'; let ids: undefined | number[]; if (typeof bangumiId === 'number') { url = `${url}/${bangumiId}`; } else { url = `${url}/many`; ids = bangumiId; } const { data } = await axios.delete(url, { data: ids, params: { file, }, }); return data; }, /** * 删除指定 bangumiId 的规则。如果 file 为 true,则同时删除关联文件。 * @param bangumiId - 需要删除规则的 bangumi 的 id。 * @param file - 是否同时删除关联文件。 * @returns axios 请求返回的数据 */ async disableRule(bangumiId: number | number[], file: boolean) { let url = 'api/v1/bangumi/disable'; let ids: undefined | number[]; if (typeof bangumiId === 'number') { url = `${url}/${bangumiId}`; } else { url = `${url}/many`; ids = bangumiId; } const { data } = await axios.delete(url, { data: ids, params: { file, }, }); return data; }, /** * 启用指定 bangumiId 的规则 * @param bangumiId - 需要启用的 bangumi 的 id */ async enableRule(bangumiId: number) { const { data } = await axios.get( `api/v1/bangumi/enable/${bangumiId}` ); return data; }, /** * 重置所有 bangumi 数据 */ async resetAll() { const { data } = await axios.get('api/v1/bangumi/reset/all'); return data; }, /** * 刷新所有没有海报的 bangumi 海报 */ async refreshPoster() { const { data } = await axios.get( 'api/v1/bangumi/refresh/poster/all' ); return data; }, /** * 从 Bangumi.tv 刷新放送日历数据 */ async refreshCalendar() { const { data } = await axios.get( 'api/v1/bangumi/refresh/calendar' ); return data; }, /** * 归档指定 bangumi * @param bangumiId - 需要归档的 bangumi 的 id */ async archiveRule(bangumiId: number) { const { data } = await axios.patch( `api/v1/bangumi/archive/${bangumiId}` ); return data; }, /** * 取消归档指定 bangumi * @param bangumiId - 需要取消归档的 bangumi 的 id */ async unarchiveRule(bangumiId: number) { const { data } = await axios.patch( `api/v1/bangumi/unarchive/${bangumiId}` ); return data; }, /** * 刷新 TMDB 元数据并自动归档已完结番剧 */ async refreshMetadata() { const { data } = await axios.get( 'api/v1/bangumi/refresh/metadata' ); return data; }, /** * 获取自动检测的剧集偏移量建议 * @param bangumiId - bangumi 的 id */ async suggestOffset(bangumiId: number) { const { data } = await axios.get( `api/v1/bangumi/suggest-offset/${bangumiId}` ); return data; }, /** * 检测季度/集数与 TMDB 的不匹配 * @param request - 包含标题、解析的季度和集数 */ async detectOffset(request: DetectOffsetRequest) { const { data } = await axios.post( 'api/v1/bangumi/detect-offset', request ); return data; }, /** * 清除 bangumi 的需要检查标记 * @param bangumiId - bangumi 的 id */ async dismissReview(bangumiId: number) { const { data } = await axios.post( `api/v1/bangumi/dismiss-review/${bangumiId}` ); return data; }, /** * 手动设置番剧的放送星期 * @param bangumiId - bangumi 的 id * @param weekday - 0-6 for Mon-Sun, null to reset */ async setWeekday(bangumiId: number, weekday: number | null) { const { data } = await axios.patch( `api/v1/bangumi/${bangumiId}/weekday`, { weekday } ); return data; }, /** * 获取所有需要检查偏移量的 bangumi */ async getNeedsReview() { const { data } = await axios.get( 'api/v1/bangumi/needs-review' ); return data.map((bangumi) => ({ ...bangumi, filter: bangumi.filter.split(','), rss_link: bangumi.rss_link.split(','), air_weekday: bangumi.air_weekday ?? null, })) as BangumiRule[]; }, }; ================================================ FILE: webui/src/api/check.ts ================================================ export const apiCheck = { /** * 检测下载器 */ async downloader() { const { data } = await axios.get('api/v1/check/downloader'); return data; }, }; ================================================ FILE: webui/src/api/config.ts ================================================ import type { Config } from '#/config'; import type { ApiSuccess } from '#/api'; export const apiConfig = { /** * 获取 config 数据 */ async getConfig() { const { data } = await axios.get('api/v1/config/get'); return data; }, /** * 更新 config 数据 * @param newConfig - 需要更新的 config */ async updateConfig(newConfig: Config) { const { data } = await axios.patch( 'api/v1/config/update', newConfig ); return data; }, }; ================================================ FILE: webui/src/api/download.ts ================================================ import type { BangumiAPI, BangumiRule } from '#/bangumi'; import type { RSS } from '#/rss'; import type { ApiSuccess } from '#/api'; export const apiDownload = { /** * 解析 RSS 链接 * @param rss_item - RSS 链接 */ async analysis(rss_item: RSS) { const { data } = await axios.post( 'api/v1/rss/analysis', rss_item ); const result: BangumiRule = { ...data, filter: data.filter.split(','), rss_link: data.rss_link.split(','), }; return result; }, /** * 旧番 * @param bangumiData - Bangumi 数据 */ async collection(bangumiData: BangumiRule) { const postData: BangumiAPI = { ...bangumiData, filter: bangumiData.filter.join(','), rss_link: bangumiData.rss_link.join(','), }; const { data } = await axios.post( 'api/v1/rss/collect', postData ); return data; }, /** * 新番 * @param bangumiData - Bangumi 数据 */ async subscribe(bangumiData: BangumiRule, rss: RSS) { const bangumi: BangumiAPI = { ...bangumiData, filter: bangumiData.filter.join(','), rss_link: bangumiData.rss_link.join(','), }; const postData = { data: bangumi, rss, }; const { data } = await axios.post( 'api/v1/rss/subscribe', postData ); return data; }, }; ================================================ FILE: webui/src/api/downloader.ts ================================================ import type { QbTorrentInfo } from '#/downloader'; import type { ApiSuccess } from '#/api'; export const apiDownloader = { async getTorrents() { const { data } = await axios.get( 'api/v1/downloader/torrents' ); return data!; }, async pause(hashes: string[]) { const { data } = await axios.post( 'api/v1/downloader/torrents/pause', { hashes } ); return data!; }, async resume(hashes: string[]) { const { data } = await axios.post( 'api/v1/downloader/torrents/resume', { hashes } ); return data!; }, async deleteTorrents(hashes: string[], deleteFiles = false) { const { data } = await axios.post( 'api/v1/downloader/torrents/delete', { hashes, delete_files: deleteFiles } ); return data!; }, }; ================================================ FILE: webui/src/api/log.ts ================================================ import type { ApiSuccess } from '#/api'; export const apiLog = { async getLog() { const { data } = await axios.get('api/v1/log'); return data; }, async clearLog() { const { data } = await axios.get('api/v1/log/clear'); return data; }, }; ================================================ FILE: webui/src/api/notification.ts ================================================ import type { NotificationProviderConfig, NotificationType } from '#/config'; import type { TupleToUnion } from '#/utils'; export interface TestProviderRequest { provider_index: number; } export interface TestProviderConfigRequest { type: TupleToUnion; enabled?: boolean; token?: string; chat_id?: string; webhook_url?: string; server_url?: string; device_key?: string; user_key?: string; api_token?: string; template?: string; url?: string; } export interface TestResponse { success: boolean; message: string; message_zh: string; message_en: string; } export const apiNotification = { /** * Test a configured provider by index */ async testProvider(request: TestProviderRequest) { const { data } = await axios.post( 'api/v1/notification/test', request ); return { data }; }, /** * Test an unsaved provider configuration */ async testProviderConfig(request: TestProviderConfigRequest) { const { data } = await axios.post( 'api/v1/notification/test-config', request ); return { data }; }, }; ================================================ FILE: webui/src/api/passkey.ts ================================================ import type { ApiSuccess } from '#/api'; import type { LoginSuccess } from '#/auth'; import type { AuthenticationOptions, PasskeyAuthFinishRequest, PasskeyAuthStartRequest, PasskeyCreateRequest, PasskeyDeleteRequest, PasskeyItem, RegistrationOptions, } from '#/passkey'; /** * Passkey API 客户端 */ export const apiPasskey = { // ============ 注册流程 ============ /** * 获取注册选项(步骤 1) */ async getRegistrationOptions(): Promise { const { data } = await axios.post( 'api/v1/passkey/register/options' ); return data; }, /** * 提交注册结果(步骤 2) */ async verifyRegistration(request: PasskeyCreateRequest): Promise { const { data } = await axios.post( 'api/v1/passkey/register/verify', request ); return data; }, // ============ 认证流程 ============ /** * 获取登录选项(步骤 1) */ async getLoginOptions( request: PasskeyAuthStartRequest ): Promise { const { data } = await axios.post( 'api/v1/passkey/auth/options', request ); return data; }, /** * 提交认证结果(步骤 2) */ async loginWithPasskey( request: PasskeyAuthFinishRequest ): Promise { const { data } = await axios.post( 'api/v1/passkey/auth/verify', request ); return data; }, // ============ 管理 ============ /** * 获取 Passkey 列表 */ async list(): Promise { const { data } = await axios.get('api/v1/passkey/list'); return data; }, /** * 删除 Passkey */ async delete(request: PasskeyDeleteRequest): Promise { const { data } = await axios.post( 'api/v1/passkey/delete', request ); return data; }, }; ================================================ FILE: webui/src/api/program.ts ================================================ import type { ApiSuccess } from '#/api'; export const apiProgram = { /** * 重启 */ async restart() { const { data } = await axios.get('api/v1/restart'); return data; }, /** * 启动 */ async start() { const { data } = await axios.get('api/v1/start'); return data; }, /** * 停止 */ async stop() { const { data } = await axios.get('api/v1/stop'); return data; }, /** * 状态 */ async status() { const { data } = await axios.get<{ status: boolean; version: string }>( 'api/v1/status' ); return data!; }, /** * 终止 */ async shutdown() { const { data } = await axios.get('api/v1/shutdown'); return data; }, }; ================================================ FILE: webui/src/api/rss.ts ================================================ import type { RSS } from '#/rss'; import type { Torrent } from '#/torrent'; import type { ApiSuccess } from '#/api'; export const apiRSS = { async get() { const { data } = await axios.get('api/v1/rss'); return data!; }, async add(rss: RSS) { const { data } = await axios.post('api/v1/rss/add', rss); return data; }, async delete(rss_id: number) { const { data } = await axios.delete( `api/v1/rss/delete/${rss_id}` ); return data!; }, async deleteMany(rss_list: number[]) { const { data } = await axios.post( `api/v1/rss/delete/many`, rss_list ); return data!; }, async disable(rss_id: number) { const { data } = await axios.patch( `api/v1/rss/disable/${rss_id}` ); return data!; }, async disableMany(rss_list: number[]) { const { data } = await axios.post( `api/v1/rss/disable/many`, rss_list ); return data!; }, async update(rss_id: number, rss: RSS) { const { data } = await axios.patch( `api/v1/rss/update/${rss_id}`, rss ); return data!; }, async enableMany(rss_list: number[]) { const { data } = await axios.post( `api/v1/rss/enable/many`, rss_list ); return data!; }, async refreshAll() { const { data } = await axios.get('api/v1/rss/refresh/all'); return data!; }, async refresh(rss_id: number) { const { data } = await axios.get( `api/v1/rss/refresh/${rss_id}` ); return data!; }, async getTorrent(rss_id: number) { const { data } = await axios.get(`api/v1/rss/torrent/${rss_id}`); return data!; }, }; ================================================ FILE: webui/src/api/search.ts ================================================ import type { Ref } from 'vue'; import { omit } from 'radash'; import type { BangumiAPI, BangumiRule } from '#/bangumi'; type EventSourceStatus = 'OPEN' | 'CONNECTING' | 'CLOSED'; export const apiSearch = { get() { const eventSource = ref(null) as Ref; const status = ref('CLOSED'); const data = ref([]); const keyword = ref(''); const provider = ref(''); const close = () => { if (eventSource.value) { eventSource.value.close(); eventSource.value = null; status.value = 'CLOSED'; } }; const _init = () => { status.value = 'CONNECTING'; const url = `api/v1/search/bangumi?site=${ provider.value }&keywords=${encodeURIComponent(keyword.value)}`; const es = new EventSource(url, { withCredentials: true }); eventSource.value = es; es.onopen = () => { status.value = 'OPEN'; }; es.onmessage = (e) => { const _data = JSON.parse(e.data) as BangumiAPI; const newData: BangumiRule = { ...omit(_data, ['filter', 'rss_link']), filter: _data.filter.split(','), rss_link: _data.rss_link.split(','), }; data.value = [...data.value, newData]; }; es.onerror = (err) => { console.error('EventSource error:', err); close(); }; }; const open = () => { data.value = []; _init(); }; return { keyword, provider, status, data, open, close, }; }, async getProvider() { const { data } = await axios.get('api/v1/search/provider'); return data; }, }; ================================================ FILE: webui/src/api/setup.ts ================================================ import type { SetupCompleteRequest, SetupStatus, TestDownloaderRequest, TestNotificationRequest, TestResult, } from '#/setup'; import type { ApiSuccess } from '#/api'; export const apiSetup = { async getStatus() { const { data } = await axios.get('api/v1/setup/status'); return data; }, async testDownloader(config: TestDownloaderRequest) { const { data } = await axios.post( 'api/v1/setup/test-downloader', config ); return data; }, async testRSS(url: string) { const { data } = await axios.post('api/v1/setup/test-rss', { url, }); return data; }, async testNotification(config: TestNotificationRequest) { const { data } = await axios.post( 'api/v1/setup/test-notification', config ); return data; }, async complete(config: SetupCompleteRequest) { const { data } = await axios.post( 'api/v1/setup/complete', config ); return data; }, }; ================================================ FILE: webui/src/components/ab-add-rss.vue ================================================ ================================================ FILE: webui/src/components/ab-bangumi-card.vue ================================================ ================================================ FILE: webui/src/components/ab-change-account.vue ================================================ ================================================ FILE: webui/src/components/ab-container.vue ================================================ ================================================ FILE: webui/src/components/ab-edit-rule.vue ================================================ ================================================ FILE: webui/src/components/ab-fold-panel.vue ================================================ ================================================ FILE: webui/src/components/ab-image.vue ================================================ ================================================ FILE: webui/src/components/ab-label.vue ================================================ ================================================ FILE: webui/src/components/ab-popup.vue ================================================ ================================================ FILE: webui/src/components/ab-rule.vue ================================================ ================================================ FILE: webui/src/components/ab-search-bar.vue ================================================ ================================================ FILE: webui/src/components/ab-setting.vue ================================================ ================================================ FILE: webui/src/components/ab-status-bar.vue ================================================ ================================================ FILE: webui/src/components/basic/__tests__/ab-button.test.ts ================================================ /** * Tests for AbButton component */ import { describe, it, expect, vi } from 'vitest'; import { mount } from '@vue/test-utils'; import { h, defineComponent } from 'vue'; import AbButton from '../ab-button.vue'; // Mock naive-ui NSpin component vi.mock('naive-ui', () => ({ NSpin: defineComponent({ props: ['show', 'size'], setup(props, { slots }) { return () => h('div', { class: 'n-spin-mock' }, slots.default?.()); }, }), })); describe('AbButton', () => { describe('rendering', () => { it('should render as button by default', () => { const wrapper = mount(AbButton, { slots: { default: 'Click me', }, }); expect(wrapper.element.tagName).toBe('BUTTON'); expect(wrapper.text()).toContain('Click me'); }); it('should render as anchor when link is provided', () => { const wrapper = mount(AbButton, { props: { link: 'https://example.com', }, slots: { default: 'Click me', }, }); expect(wrapper.element.tagName).toBe('A'); expect(wrapper.attributes('href')).toBe('https://example.com'); }); it('should render slot content', () => { const wrapper = mount(AbButton, { slots: { default: 'Custom Content', }, }); expect(wrapper.html()).toContain('Custom Content'); }); }); describe('props', () => { describe('type', () => { it('should have primary type by default', () => { const wrapper = mount(AbButton); expect(wrapper.classes()).toContain('btn--primary'); }); it('should apply secondary type class', () => { const wrapper = mount(AbButton, { props: { type: 'secondary' }, }); expect(wrapper.classes()).toContain('btn--secondary'); }); it('should apply warn type class', () => { const wrapper = mount(AbButton, { props: { type: 'warn' }, }); expect(wrapper.classes()).toContain('btn--warn'); }); }); describe('size', () => { it('should have normal size by default', () => { const wrapper = mount(AbButton); expect(wrapper.classes()).toContain('btn--normal'); }); it('should apply big size class', () => { const wrapper = mount(AbButton, { props: { size: 'big' }, }); expect(wrapper.classes()).toContain('btn--big'); }); it('should apply small size class', () => { const wrapper = mount(AbButton, { props: { size: 'small' }, }); expect(wrapper.classes()).toContain('btn--small'); }); }); describe('loading', () => { it('should be false by default', () => { const wrapper = mount(AbButton); // Verify component renders with default loading=false expect(wrapper.vm.$props.loading).toBe(false); }); }); }); describe('events', () => { it('should emit click event when clicked', async () => { const wrapper = mount(AbButton); await wrapper.trigger('click'); expect(wrapper.emitted('click')).toBeTruthy(); expect(wrapper.emitted('click')?.length).toBe(1); }); it('should emit click event multiple times', async () => { const wrapper = mount(AbButton); await wrapper.trigger('click'); await wrapper.trigger('click'); await wrapper.trigger('click'); expect(wrapper.emitted('click')?.length).toBe(3); }); }); describe('accessibility', () => { it('should have btn class for styling', () => { const wrapper = mount(AbButton); expect(wrapper.classes()).toContain('btn'); }); }); describe('combined props', () => { it('should apply multiple props correctly', () => { const wrapper = mount(AbButton, { props: { type: 'warn', size: 'big', }, slots: { default: 'Delete', }, }); expect(wrapper.classes()).toContain('btn--warn'); expect(wrapper.classes()).toContain('btn--big'); expect(wrapper.text()).toContain('Delete'); }); }); }); ================================================ FILE: webui/src/components/basic/__tests__/ab-switch.test.ts ================================================ /** * Tests for AbSwitch component */ import { describe, it, expect, vi } from 'vitest'; import { mount } from '@vue/test-utils'; import { h, defineComponent, ref } from 'vue'; import AbSwitch from '../ab-switch.vue'; // Mock @headlessui/vue Switch component vi.mock('@headlessui/vue', () => ({ Switch: defineComponent({ props: ['modelValue', 'as'], emits: ['update:modelValue'], setup(props, { emit, slots }) { const toggle = () => { emit('update:modelValue', !props.modelValue); }; return () => h( 'div', { class: 'headlessui-switch-mock', onClick: toggle, 'data-checked': props.modelValue, }, slots.default?.() ); }, }), })); describe('AbSwitch', () => { describe('rendering', () => { it('should render switch track', () => { const wrapper = mount(AbSwitch); expect(wrapper.find('.switch-track').exists()).toBe(true); }); it('should render switch thumb', () => { const wrapper = mount(AbSwitch); expect(wrapper.find('.switch-thumb').exists()).toBe(true); }); }); describe('checked state', () => { it('should be unchecked by default', () => { const wrapper = mount(AbSwitch); const track = wrapper.find('.switch-track'); const thumb = wrapper.find('.switch-thumb'); expect(track.classes()).not.toContain('switch-track--checked'); expect(thumb.classes()).not.toContain('switch-thumb--checked'); }); it('should apply checked classes when checked is true', async () => { const wrapper = mount(AbSwitch, { props: { checked: true, 'onUpdate:checked': (e: boolean) => wrapper.setProps({ checked: e }), }, }); const track = wrapper.find('.switch-track'); const thumb = wrapper.find('.switch-thumb'); expect(track.classes()).toContain('switch-track--checked'); expect(thumb.classes()).toContain('switch-thumb--checked'); }); it('should not have checked classes when checked is false', () => { const wrapper = mount(AbSwitch, { props: { checked: false, 'onUpdate:checked': () => {}, }, }); const track = wrapper.find('.switch-track'); const thumb = wrapper.find('.switch-thumb'); expect(track.classes()).not.toContain('switch-track--checked'); expect(thumb.classes()).not.toContain('switch-thumb--checked'); }); }); describe('v-model', () => { it('should emit update:checked when toggled', async () => { const wrapper = mount(AbSwitch, { props: { checked: false, 'onUpdate:checked': (e: boolean) => wrapper.setProps({ checked: e }), }, }); // Find the HeadlessUI Switch mock and click it const switchMock = wrapper.find('.headlessui-switch-mock'); await switchMock.trigger('click'); expect(wrapper.emitted('update:checked')).toBeTruthy(); }); it('should toggle from false to true', async () => { let checked = false; const wrapper = mount(AbSwitch, { props: { checked, 'onUpdate:checked': (e: boolean) => { checked = e; wrapper.setProps({ checked: e }); }, }, }); const switchMock = wrapper.find('.headlessui-switch-mock'); await switchMock.trigger('click'); expect(checked).toBe(true); }); it('should toggle from true to false', async () => { let checked = true; const wrapper = mount(AbSwitch, { props: { checked, 'onUpdate:checked': (e: boolean) => { checked = e; wrapper.setProps({ checked: e }); }, }, }); const switchMock = wrapper.find('.headlessui-switch-mock'); await switchMock.trigger('click'); expect(checked).toBe(false); }); }); describe('accessibility', () => { it('should use HeadlessUI Switch component', () => { const wrapper = mount(AbSwitch); // The mock creates a div with this class expect(wrapper.find('.headlessui-switch-mock').exists()).toBe(true); }); }); describe('styling', () => { it('should have correct track dimensions via CSS class', () => { const wrapper = mount(AbSwitch); const track = wrapper.find('.switch-track'); expect(track.exists()).toBe(true); expect(track.classes()).toContain('switch-track'); }); it('should have correct thumb styling via CSS class', () => { const wrapper = mount(AbSwitch); const thumb = wrapper.find('.switch-thumb'); expect(thumb.exists()).toBe(true); expect(thumb.classes()).toContain('switch-thumb'); }); }); }); ================================================ FILE: webui/src/components/basic/ab-adaptive-modal.vue ================================================ ================================================ FILE: webui/src/components/basic/ab-add.stories.ts ================================================ import type { Meta, StoryObj } from '@storybook/vue3'; import AbAdd from './ab-add.vue'; const meta: Meta = { title: 'basic/ab-add', component: AbAdd, tags: ['autodocs'], }; export default meta; type Story = StoryObj; export const Template: Story = { render: (args) => ({ components: { AbAdd }, setup() { return { args }; }, template: '', }), }; ================================================ FILE: webui/src/components/basic/ab-add.vue ================================================ ================================================ FILE: webui/src/components/basic/ab-bottom-sheet.vue ================================================ ================================================ FILE: webui/src/components/basic/ab-button-multi.stories.ts ================================================ import type {Meta, StoryObj} from '@storybook/vue3'; import AbButtonMulti from './ab-button-multi.vue'; const meta: Meta = { title: 'basic/ab-button-multi', component: AbButtonMulti, tags: ['autodocs'], argTypes: { type: { control: {type: 'select'}, options: ['primary', 'warn'], }, size: { control: {type: 'select'}, options: ['big', 'normal', 'small'], }, }, }; export default meta; type Story = StoryObj; export const Template: Story = { render: (args) => ({ components: {AbButtonMulti}, setup() { return {args}; }, template: 'button', }), }; ================================================ FILE: webui/src/components/basic/ab-button-multi.vue ================================================ ================================================ FILE: webui/src/components/basic/ab-button.stories.ts ================================================ import type { Meta, StoryObj } from '@storybook/vue3'; import AbButton from './ab-button.vue'; const meta: Meta = { title: 'basic/ab-button', component: AbButton, tags: ['autodocs'], argTypes: { type: { control: { type: 'select' }, options: ['primary', 'warn'], }, size: { control: { type: 'select' }, options: ['big', 'normal', 'small'], }, }, }; export default meta; type Story = StoryObj; export const Template: Story = { render: (args) => ({ components: { AbButton }, setup() { return { args }; }, template: 'button', }), }; ================================================ FILE: webui/src/components/basic/ab-button.vue ================================================ ================================================ FILE: webui/src/components/basic/ab-checkbox.stories.ts ================================================ import type { Meta, StoryObj } from '@storybook/vue3'; import AbCheckbox from './ab-checkbox.vue'; const meta: Meta = { title: 'basic/ab-checkbox', component: AbCheckbox, tags: ['autodocs'], }; export default meta; type Story = StoryObj; export const Template: Story = { render: (args) => ({ components: { AbCheckbox }, setup() { return { args }; }, template: '', }), }; ================================================ FILE: webui/src/components/basic/ab-checkbox.vue ================================================ ================================================ FILE: webui/src/components/basic/ab-data-list.vue ================================================ ================================================ FILE: webui/src/components/basic/ab-offset-mismatch-dialog.vue ================================================ ================================================ FILE: webui/src/components/basic/ab-page-title.stories.ts ================================================ import type { Meta, StoryObj } from '@storybook/vue3'; import AbPageTitle from './ab-page-title.vue'; const meta: Meta = { title: 'basic/ab-PageTitle', component: AbPageTitle, tags: ['autodocs'], }; export default meta; type Story = StoryObj; export const Template: Story = { render: (args) => ({ components: { AbPageTitle }, setup() { return { args }; }, template: '', }), }; ================================================ FILE: webui/src/components/basic/ab-page-title.vue ================================================ ================================================ FILE: webui/src/components/basic/ab-pull-refresh.vue ================================================ ================================================ FILE: webui/src/components/basic/ab-search.stories.ts ================================================ import type { Meta, StoryObj } from '@storybook/vue3'; import AbSearch from './ab-search.vue'; const meta: Meta = { title: 'basic/ab-search', component: AbSearch, tags: ['autodocs'], }; export default meta; type Story = StoryObj; export const Template: Story = { render: (args) => ({ components: { AbSearch }, setup() { return { args }; }, template: '', }), }; ================================================ FILE: webui/src/components/basic/ab-search.vue ================================================ ================================================ FILE: webui/src/components/basic/ab-select.stories.ts ================================================ import type { Meta, StoryObj } from '@storybook/vue3'; import AbSelect from './ab-select.vue'; const meta: Meta = { title: 'basic/ab-select', component: AbSelect, tags: ['autodocs'], }; export default meta; type Story = StoryObj; export const Template: Story = { render: (args) => ({ components: { AbSelect }, setup() { return { args }; }, template: '', }), }; ================================================ FILE: webui/src/components/basic/ab-select.vue ================================================ ================================================ FILE: webui/src/components/basic/ab-status.stories.ts ================================================ import type { Meta, StoryObj } from '@storybook/vue3'; import AbStatus from './ab-status.vue'; const meta: Meta = { title: 'basic/ab-status', component: AbStatus, tags: ['autodocs'], }; export default meta; type Story = StoryObj; export const Template: Story = { render: (args) => ({ components: { AbStatus }, setup() { return { args }; }, template: '', }), }; ================================================ FILE: webui/src/components/basic/ab-status.vue ================================================ ================================================ FILE: webui/src/components/basic/ab-swipe-container.vue ================================================ ================================================ FILE: webui/src/components/basic/ab-switch.stories.ts ================================================ import type { Meta, StoryObj } from '@storybook/vue3'; import AbSwitch from './ab-switch.vue'; const meta: Meta = { title: 'basic/ab-switch', component: AbSwitch, tags: ['autodocs'], }; export default meta; type Story = StoryObj; export const Template: Story = { render: (args) => ({ components: { AbSwitch }, setup() { return { args }; }, template: '', }), }; ================================================ FILE: webui/src/components/basic/ab-switch.vue ================================================ ================================================ FILE: webui/src/components/basic/ab-tag.stories.ts ================================================ import type { Meta, StoryObj } from '@storybook/vue3'; import AbTag from './ab-tag.vue'; const meta: Meta = { title: 'basic/ab-tag', component: AbTag, tags: ['autodocs'], }; export default meta; type Story = StoryObj; export const Template: Story = { render: (args) => ({ components: { AbTag }, setup() { return { args }; }, template: '', }), }; ================================================ FILE: webui/src/components/basic/ab-tag.vue ================================================ ================================================ FILE: webui/src/components/layout/ab-mobile-nav.vue ================================================ ================================================ FILE: webui/src/components/layout/ab-sidebar.vue ================================================ ================================================ FILE: webui/src/components/layout/ab-topbar.vue ================================================ ================================================ FILE: webui/src/components/media-query.vue ================================================ ================================================ FILE: webui/src/components/search/ab-search-card.vue ================================================ ================================================ FILE: webui/src/components/search/ab-search-confirm.vue ================================================ ================================================ FILE: webui/src/components/search/ab-search-filters.vue ================================================ ================================================ FILE: webui/src/components/search/ab-search-modal.vue ================================================ ================================================ FILE: webui/src/components/setting/config-download.vue ================================================ ================================================ FILE: webui/src/components/setting/config-manage.vue ================================================ ================================================ FILE: webui/src/components/setting/config-normal.vue ================================================ ================================================ FILE: webui/src/components/setting/config-notification.vue ================================================