Repository: hanxi/xiaomusic Branch: main Commit: e5a56933d834 Files: 195 Total size: 7.8 MB Directory structure: gitextract_prp6ch69/ ├── .gitattributes ├── .github/ │ ├── FUNDING.yml │ └── workflows/ │ ├── ci.yml │ ├── dockerhub-description.yml │ ├── fmt.yml │ └── static.yml ├── .gitignore ├── .gitmodules ├── .pre-commit-config.yaml ├── CHANGELOG.md ├── Dockerfile ├── LICENSE ├── README.md ├── check_plugins.py ├── config-example.json ├── docs/ │ ├── .vitepress/ │ │ ├── config.mts │ │ └── vitepress-plugin-github-issues.mts │ ├── CNAME │ ├── index.md │ ├── issues/ │ │ ├── 101.md │ │ ├── 105.md │ │ ├── 182.md │ │ ├── 19.md │ │ ├── 210.md │ │ ├── 211.md │ │ ├── 212.md │ │ ├── 235.md │ │ ├── 269.md │ │ ├── 285.md │ │ ├── 294.md │ │ ├── 297.md │ │ ├── 312.md │ │ ├── 333.md │ │ ├── 350.md │ │ ├── 360.md │ │ ├── 365.md │ │ ├── 366.md │ │ ├── 389.md │ │ ├── 398.md │ │ ├── 417.md │ │ ├── 520.md │ │ ├── 533.md │ │ ├── 595.md │ │ ├── 600.md │ │ ├── 637.md │ │ ├── 688.md │ │ ├── 764.md │ │ ├── 767.md │ │ ├── 78.md │ │ ├── 86.md │ │ ├── 88.md │ │ ├── 94.md │ │ ├── 96.md │ │ ├── 99.md │ │ ├── changelog.md │ │ └── index.md │ └── package.json ├── get_release.py ├── holiday/ │ ├── 2007.json │ ├── 2008.json │ ├── 2009.json │ ├── 2010.json │ ├── 2011.json │ ├── 2012.json │ ├── 2013.json │ ├── 2014.json │ ├── 2015.json │ ├── 2016.json │ ├── 2017.json │ ├── 2018.json │ ├── 2019.json │ ├── 2020.json │ ├── 2021.json │ ├── 2022.json │ ├── 2023.json │ ├── 2024.json │ ├── 2025.json │ ├── 2026.json │ ├── 2027.json │ ├── renovate.json │ └── schema.json ├── install_dependencies.sh ├── newpatch.sh ├── newversion.sh ├── package.json ├── plugins/ │ ├── __init__.py │ ├── code1.py │ ├── httpget.py │ └── httppost.py ├── pyproject.toml ├── test/ │ ├── test_difflib.py │ ├── test_music_duration.py │ ├── test_music_tags.py │ ├── test_remove_common_prefix.py │ └── test_update.py ├── update-holiday.sh ├── update-static-version.py ├── xiaomusic/ │ ├── __init__.py │ ├── analytics.py │ ├── api/ │ │ ├── __init__.py │ │ ├── app.py │ │ ├── dependencies.py │ │ ├── models.py │ │ ├── routers/ │ │ │ ├── __init__.py │ │ │ ├── device.py │ │ │ ├── file.py │ │ │ ├── music.py │ │ │ ├── playlist.py │ │ │ ├── plugin.py │ │ │ └── system.py │ │ └── websocket.py │ ├── auth.py │ ├── cli.py │ ├── command_handler.py │ ├── config.py │ ├── config_manager.py │ ├── const.py │ ├── conversation.py │ ├── crontab.py │ ├── device_manager.py │ ├── device_player.py │ ├── events.py │ ├── file_watcher.py │ ├── holiday.py │ ├── js_adapter.py │ ├── js_plugin_manager.py │ ├── js_plugin_runner.js │ ├── music_library.py │ ├── online_music.py │ ├── plugin.py │ ├── plugins-config-example.json │ ├── qrcode_login.py │ ├── static/ │ │ ├── default/ │ │ │ ├── debug.html │ │ │ ├── downloadtool.html │ │ │ ├── index.html │ │ │ ├── m3u.html │ │ │ ├── main.css │ │ │ ├── md.js │ │ │ ├── merge/ │ │ │ │ ├── index.html │ │ │ │ ├── main.js │ │ │ │ └── tailwind.css │ │ │ ├── setting.css │ │ │ ├── setting.html │ │ │ └── setting.js │ │ ├── index.html │ │ ├── iwebplayer/ │ │ │ └── iwebplayer.html │ │ ├── manifest.json │ │ ├── onlineSearch/ │ │ │ ├── config.js │ │ │ ├── index.html │ │ │ └── setting.html │ │ ├── pure/ │ │ │ ├── assets/ │ │ │ │ ├── DownloadTool-BWMSO0_N.css │ │ │ │ ├── DownloadTool-bty5M9I6.js │ │ │ │ ├── M3u2Json-ButJ7G_D.css │ │ │ │ ├── M3u2Json-DeAtFyPF.js │ │ │ │ ├── index-BAPaOAUA.js │ │ │ │ └── index-CfMOqlRg.css │ │ │ └── index.html │ │ ├── soundSpace/ │ │ │ ├── assets/ │ │ │ │ ├── features-animation-DOC4MC0a.js │ │ │ │ ├── index-KGjtlaO8.js │ │ │ │ ├── index-ckWJnWZz.js │ │ │ │ ├── index-fie2kaim.js │ │ │ │ ├── index-qfFWjqIn.css │ │ │ │ └── src-UW24ZMRV-DgU5LBZm.js │ │ │ └── index.html │ │ ├── sw.js │ │ ├── tailwind/ │ │ │ ├── api.js │ │ │ ├── debug.html │ │ │ ├── downloadtool.html │ │ │ ├── index.html │ │ │ ├── libs/ │ │ │ │ ├── daisyui@4.12.23.css │ │ │ │ ├── jquery-3.6.0.js │ │ │ │ ├── tailwind.js │ │ │ │ └── vue@3.5.13.js │ │ │ ├── m3u.html │ │ │ ├── main.css │ │ │ ├── md.js │ │ │ ├── now_playing.html │ │ │ ├── now_playing.js │ │ │ ├── setting.html │ │ │ ├── setting.js │ │ │ ├── tailwind.js │ │ │ └── theme.js │ │ ├── weapp/ │ │ │ └── qrcode.html │ │ └── xplayer/ │ │ ├── assets/ │ │ │ ├── index-2Kb1oK2G.css │ │ │ └── index-ESKkJcHu.js │ │ └── index.html │ ├── utils/ │ │ ├── __init__.py │ │ ├── file_utils.py │ │ ├── music_utils.py │ │ ├── network_utils.py │ │ ├── openai_utils.py │ │ ├── system_utils.py │ │ └── text_utils.py │ └── xiaomusic.py └── xiaomusic.py ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitattributes ================================================ # 指定 Python 为主要语言 *.py linguist-detectable=true # 将 HTML 和 JavaScript 归类为文档或前端资源,降低权重 *.html linguist-vendored *.js linguist-vendored *.css linguist-vendored # 如果有前端框架(如 Vue 或 React),也可以添加: *.vue linguist-vendored *.tsx linguist-vendored *.jsx linguist-vendored ================================================ FILE: .github/FUNDING.yml ================================================ github: [hanxi] custom: ['https://afdian.com/a/imhanxi'] ================================================ FILE: .github/workflows/ci.yml ================================================ name: CI Workflow on: push: branches: - "*" # 所有分支触发 tags: - "v*" workflow_dispatch: env: VERSION_TAG: ${{ secrets.DOCKERHUB_USERNAME }}/xiaomusic:${{ github.ref_name }} LATEST_TAG: ${{ secrets.DOCKERHUB_USERNAME }}/xiaomusic:latest STABLE_TAG: ${{ secrets.DOCKERHUB_USERNAME }}/xiaomusic:stable permissions: contents: write id-token: write jobs: # 构建多架构 Docker 镜像并在所有平台上运行基本测试 build: runs-on: ubuntu-latest strategy: matrix: platform: [amd64, arm64, armv7] include: - platform: amd64 arch: amd64 - platform: arm64 arch: arm64 - platform: armv7 arch: arm/v7 steps: - uses: actions/checkout@v4 with: fetch-depth: 0 submodules: true - name: Set up QEMU uses: docker/setup-qemu-action@v3 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Login to Docker Hub uses: docker/login-action@v3 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Build Docker image for ${{ matrix.platform }} uses: docker/build-push-action@v6 with: platforms: linux/${{ matrix.arch }} context: . push: false load: true tags: ${{ env.VERSION_TAG }} cache-from: type=gha,scope=${{ matrix.platform }} cache-to: type=gha,mode=max,scope=${{ matrix.platform }} - name: Test Docker image for ${{ matrix.platform }} run: | docker run --rm ${{ env.VERSION_TAG }} /app/.venv/bin/python3 /app/xiaomusic.py -h - name: Save ${{ matrix.platform }} image to tar run: | docker save ${{ env.VERSION_TAG }} -o xiaomusic-${{ github.ref_name }}-${{ matrix.platform }}.tar - name: Upload Docker images as artifacts uses: actions/upload-artifact@v4 with: name: docker-images-${{ matrix.platform }} path: xiaomusic-${{ github.ref_name }}-${{ matrix.platform }}.tar retention-days: 1 # 推送多架构 Docker 镜像到 Docker Hub push-docker: runs-on: ubuntu-latest needs: build if: github.ref_name == 'main' || startsWith(github.ref, 'refs/tags/v') steps: - uses: actions/checkout@v4 - name: Set up QEMU uses: docker/setup-qemu-action@v3 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Login to Docker Hub uses: docker/login-action@v3 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Publish to Docker Hub if: github.ref_name == 'main' uses: docker/build-push-action@v6 with: platforms: linux/amd64,linux/arm64,linux/arm/v7 context: . push: true tags: ${{ env.VERSION_TAG }} cache-from: | type=gha,scope=amd64 type=gha,scope=arm64 type=gha,scope=armv7 - name: Publish to Docker Hub latest and stable if: startsWith(github.ref, 'refs/tags/v') uses: docker/build-push-action@v6 with: platforms: linux/amd64,linux/arm64,linux/arm/v7 context: . push: true tags: | ${{ env.VERSION_TAG }} ${{ env.LATEST_TAG }} ${{ env.STABLE_TAG }} cache-from: | type=gha,scope=amd64 type=gha,scope=arm64 type=gha,scope=armv7 # 推送镜像到 GitHub Release publish-release: runs-on: ubuntu-latest needs: build if: github.ref_name == 'main' || startsWith(github.ref, 'refs/tags/v') steps: - uses: actions/checkout@v4 - name: Download all platform artifacts uses: actions/download-artifact@v4 with: pattern: docker-images-* merge-multiple: true - name: Install GitHub CLI run: | sudo apt update sudo apt install -y gh - name: Create or update Release tag env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | RELEASE_NAME=${{ github.ref_name }} RELEASE_BODY="This release is automatically updated from the ${RELEASE_NAME} branch." EXISTING_RELEASE=$(gh release view "${RELEASE_NAME}" --json id --jq .id || echo "") if [[ -n "${EXISTING_RELEASE}" ]]; then echo "release exist" else gh release create "${RELEASE_NAME}" \ --prerelease=false \ --title "${RELEASE_NAME}" \ --notes "${RELEASE_BODY}" fi - name: Upload assets to Release tag env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | RELEASE_NAME=${{ github.ref_name }} FILES=$(find . -type f -name "xiaomusic-*.tar") for FILE in ${FILES}; do echo "type upload ${FILE}" gh release upload "${RELEASE_NAME}" "${FILE}" --clobber done # 推送 PyPI 包 publish-pypi: runs-on: ubuntu-latest needs: build if: startsWith(github.ref, 'refs/tags/v') steps: - uses: actions/checkout@v4 with: fetch-depth: 0 submodules: true - uses: actions/setup-node@v4 with: registry-url: https://registry.npmjs.org/ node-version: lts/* - name: Generate changelog run: npx changelogithub continue-on-error: true env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - uses: pdm-project/setup-pdm@v3 - name: Publish package distributions to PyPI run: pdm publish continue-on-error: true ================================================ FILE: .github/workflows/dockerhub-description.yml ================================================ name: Update Docker Hub Description permissions: contents: read on: push: branches: - main paths: - 'README.md' - '.github/workflows/dockerhub-description.yml' workflow_dispatch: jobs: update-description: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Docker Hub Description uses: peter-evans/dockerhub-description@v4 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} repository: hanxi/xiaomusic ================================================ FILE: .github/workflows/fmt.yml ================================================ name: fmt on: push: branches: - "*" workflow_dispatch: permissions: contents: read jobs: format: runs-on: ubuntu-latest permissions: contents: write steps: - uses: actions/checkout@v4 with: fetch-depth: 0 submodules: true - name: Setup PDM uses: pdm-project/setup-pdm@v4 - name: install ruff run: pip install ruff - name: Format code run: pdm lintfmt - name: Check for changes id: check_changes run: | if [ -n "$(git diff)" ]; then echo "changed=true" >> $GITHUB_OUTPUT else echo "changed=false" >> $GITHUB_OUTPUT fi continue-on-error: true # Optionally, customize the user name and commit message, and can add an email as well such as Github Actions' email - name: Set up Git and Commit Changes run: | if [ "${{ steps.check_changes.outputs.changed }}" == "true" ]; then git config --local user.name "Formatter [BOT]" git add . git commit -m "Auto-format code 🧹🌟🤖" git push fi ================================================ FILE: .github/workflows/static.yml ================================================ # Simple workflow for deploying static content to GitHub Pages name: Deploy static content to Pages on: # Runs on pushes targeting the default branch push: branches: ["main"] paths: - 'docs/**' - 'README.md' - 'CHANGELOG.md' - '.github/workflows/static.yml' # Runs on issue events issues: types: [opened, edited, reopened] # Specify events you're interested in release: types: - uploaded workflow_run: workflows: - CI Workflow types: - completed # Allows you to run this workflow manually from the Actions tab workflow_dispatch: # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages permissions: contents: write pages: write id-token: write # Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. # However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. concurrency: group: "pages" cancel-in-progress: false jobs: # Single deploy job since we're just deploying deploy: environment: name: github-pages url: ${{ steps.deployment.outputs.page_url }} runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 with: fetch-depth: 0 submodules: true - name: Set up Node.js uses: actions/setup-node@v2 with: node-version: '20' - name: Install dependencies working-directory: ./docs # 指定工作目录为 docs run: | npm install - name: Build VitePress env: VITE_GITHUB_ISSUES_TOKEN: ${{ secrets.VITE_GITHUB_ISSUES_TOKEN }} working-directory: ./docs # 指定工作目录为 docs run: | # 有点小问题,得执行2次 npm run docs:build npm run docs:build - uses: pdm-project/setup-pdm@v3 - name: pdm run: pdm install --prod --frozen-lockfile - name: generate versions.json env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: pdm run get_release.py - name: Check for changes id: check_changes run: | if [ -n "$(git diff docs)" ]; then echo "changed=true" >> $GITHUB_OUTPUT else echo "changed=false" >> $GITHUB_OUTPUT fi continue-on-error: true # Optionally, customize the user name and commit message, and can add an email as well such as Github Actions' email - name: Set up Git and Commit Changes run: | if [ "${{ steps.check_changes.outputs.changed }}" == "true" ]; then git config --local user.name "Issues Docs [BOT]" git config --local user.email "github-actions[bot]@users.noreply.github.com" git add . git commit -m "Auto-Generate docs 🤖" git push fi - name: Setup Pages uses: actions/configure-pages@v5 - name: Upload artifact uses: actions/upload-pages-artifact@v3 with: # Upload entire repository path: './docs/.vitepress/dist' - name: Deploy to GitHub Pages id: deployment uses: actions/deploy-pages@v4 ================================================ 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 *_bak/ # 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 .pdm-python .pdm-build/ # 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 .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/ ffmpeg music test.sh conf setting.json .DS_Store cache tmp/ xiaomusic.log.txt* node_modules reference/ .aone_copilot/ .idea/ ================================================ FILE: .gitmodules ================================================ ================================================ FILE: .pre-commit-config.yaml ================================================ repos: - hooks: - id: commitizen - id: commitizen-branch stages: - push repo: https://github.com/commitizen-tools/commitizen rev: v3.27.0 ================================================ FILE: CHANGELOG.md ================================================ ## v0.4.26 (2026-03-20) ### Fix - proxy handler CDN safeguard & content-type based FFmpeg routing (#791) ## v0.4.25 (2026-03-19) ### Fix - proxy url too long for xiaomi speaker (#786) - 无法上传音乐 close #783 ## v0.4.24 (2026-03-13) ### Feat - 新增适配iPhone主题 (#775) - Add 肉肉音乐TV link to index.html (#773) ### Fix - 修复无法收藏歌曲的问题 ## v0.4.23 (2026-02-17) ### Feat - **api**: support playlist_name in downloadonemusic (#756) - **api**: support downloadonemusic dirname under music_path (#751) - 二维码登录功能 (#750) - add dirname parameter to /downloadonemusic API (#749) - add /getplayerstatus API to get full player status (#747) ### Fix - 漏提交base64 (#761) - **playlist**: avoid custom/folder name collisions (#752) - 点击 本地端口-自动填功能 报错 #742 ## v0.4.22 (2026-01-26) ### Feat - 新增 HMusic 客户端 ### Fix - 修复播放歌曲口令问题 close #731 - 修复nodejs进程无限重启的问题 see #728 ## v0.4.21 (2026-01-24) ### Feat - 优化首页 ### Fix - 修复定时任务播放临时歌单报错问题 ## v0.4.20 (2026-01-23) ### Feat - 优化默认主题的无障碍使用 - 完善默认主题本机播放功能 - 优化默认主题界面布局 - 添加测试口令入口 ## v0.4.19 (2026-01-23) ### Feat - 默认主题支持无障碍使用 ### Fix - 修复收藏歌曲报错 - 修复 playlist相关接口报错问题 ## v0.4.18 (2026-01-21) ### Feat - 默认口令都允许唤醒 - 优化默认主题设置页面 ### Fix - 修复插件获取最后一条命令的问题 - 修复 /proxy 接口问题 ## v0.4.17 (2026-01-20) ### Fix - 支持代理 m3u8 格式链接 close #711 - 兼容旧配置 hostname 报错 close #713 - 修复下载歌曲报错 close #709 - 修复报错 close #708 ## v0.4.16 (2026-01-18) ### Feat - 新增【风花雪乐】手机APP入口 - 优化获取时长卡顿问题 - 删除怀旧主题 ### Fix - 只有一单音乐,同时不在播放列表中时更新播放列表 (#703) ## v0.4.15 (2026-01-17) ### Feat - TTS默认值修改 ## v0.4.14 (2026-01-16) ### Fix - 修复报错 close #696 ## v0.4.13 (2026-01-16) ### Feat - 登录支持仅填 cookie #688 - 网络歌单插件功能更新 (#690) - 编译出 arm/v7 架构的镜像 ### Fix - 修复网络歌曲 proxy 处理问题 ## v0.4.12 (2026-01-15) ### Fix - 修复启动报错 ## v0.4.11 (2026-01-15) ### Fix - 修复报错 close #684 - 修复登陆报错问题 - 修复保存报错 ## v0.4.10 (2026-01-15) ### Feat - 主页新增获取对话记录开关 ### Fix - 设置token到account,解决登陆问题 (#680) - 把cookie到.mi.token,解决登录报错问题 (#676) - 修复ws连接错误 ## v0.4.9 (2026-01-15) ### Feat - 新增清空临时目录接口,优化tts接口 ### Fix - 修复刷新列表问题 close #621 - 修复定时器关闭问题 ### Refactor - 修改api接口 ## v0.4.8 (2026-01-13) ### Feat - 支持配置 cookie - 接入 edge-tts 解决文字转语音的问题 close #642 ### Fix - 修复关机慢的问题,关机改完接口 close #479 - 修复一直循环播放一首歌曲的问题 close #519 ## v0.4.7 (2026-01-12) ### Feat - 默认关闭语音口令 - 修复同一个时间不能执行多个定时任务的问题 - 打包docker镜像并上传github - 重构播放时长获取规则 see #668 ### Fix - 修改cache目录,修复播放时长问题 - 修复异常未加载插件无法禁用bug (#666) ## v0.4.6 (2026-01-12) ### Feat - 定时任务支持选多个歌曲组成一个歌单播放 ### Fix - 修复【启用】网络歌曲过代理后,在线歌曲播放链接被base编码两次bug (#664) - 修复报错 #660 - 修复在线推送发出设置成循环播放回复的问题 ## v0.4.5 (2026-01-10) ### Feat - 在线播放功能优化,新增AI智能提取用户口令 (#654) ### Fix - 修复重构后的问题 - 修复部分网络歌曲无法播放的问题 - 解决在线歌曲链接转换问题 - 修复重构引起的问题 - 修复重构后遇到的问题 ## v0.4.4 (2026-01-07) ### Fix - 修复无法关闭的问题 - 修复无法关闭的问题 - 修复关闭不了的问题 - self.log.inf缺少o ### Refactor - lint code ## v0.4.3 (2026-01-07) ### Fix - 定时播放报错 ## v0.4.2 (2026-01-06) ### Fix - 修复不能播放的问题 ## v0.4.1 (2026-01-06) ### Fix - 修复不能播放的问题 ## v0.4.0 (2026-01-06) ### Feat - 移除第三方设备播放功能 - 新增定时任务刷新网络歌单 #616 - add last_cmd to store latest query (#635) - 增加musicfree插件集成功能 ### Fix - 修复 python3.14 中无法运行的问题 - 修复顺序播放上一首失效的问题 - 修复本地歌曲无法播放的问题 ### Refactor - 重构 xiaomusic.py 模块 - 重构 xiaomusic.py 模块 - 重构 xiaomusic.py 模块 - 优化日志 - 优化日志 - 重构拆分 httpserver 文件 - 重构拆分 utils 文件 - 优化日志输出 - 清理 httpserver.py 中的无效代码 ## v0.3.102 (2025-12-21) ### Feat - 支持记录每个播放列表上次播放的歌曲 close #613 ## v0.3.101 (2025-12-01) ### Fix - hostname 不允许填 127.0.0.1 和 localhost - 修复自动填端口为0的问题 - SoundSpace随机播放模式失效 #578 (#585) - 下线失效的统计服务 close #579 ## v0.3.100 (2025-11-09) ### Feat - soundSpace兼容移动端 (#577) ## v0.3.99 (2025-11-02) ### Feat - 新增 SoundScape 主题 - add path aliases and base URL configuration in Vite(SoundSpace) (#569) - SoundSpace Theme (#568) ## v0.3.98 (2025-11-02) ## v0.3.97 (2025-11-01) ### Feat - 升级 miservice ### Fix - 优化登录问题 ## v0.3.96 (2025-10-28) ### Fix - tts问题临时处理 ## v0.3.95 (2025-10-26) ### Fix - 修复型号M01不能播放问题 ## v0.3.94 (2025-10-25) ### Fix - 尝试修复登录问题 ## v0.3.93 (2025-10-25) ### Fix - 尝试修复登录问题 ## v0.3.92 (2025-10-25) ### Fix - 尝试解决登录失败问题 ## v0.3.91 (2025-10-24) ### Feat - 删掉没用的网页更新功能 - 获取对话记录接口修改 ### Fix - 尝试修复登录问题 ## v0.3.90 (2025-10-22) ### Fix - 修复登录问题 see #547 ## v0.3.89 (2025-10-21) ### Fix - 修复缺失库报错 ## v0.3.88 (2025-09-16) ### Feat - 新增歌单合并工具 - 兼容 X6A 型号 ## v0.3.87 (2025-09-11) ### Feat - 新增 websocket 接口获取当前播放状态 ### Fix - 修复本地播放失败问题 ## v0.3.86 (2025-09-08) ### Feat - LX音源支持http_proxy - 支持LX歌单 - 代理播放模式使用原始地址获取歌曲时长 - 网络歌曲支持使用代理的方式播放 - 新增代理播放链接功能 see: #525 ## v0.3.85 (2025-08-08) ### Fix - 修复延迟关机按钮失效问题 ## v0.3.84 (2025-08-03) ### Feat - 下一首歌延迟播放秒数支持负数,用于解决播放下一首时会播放上一首的开头几秒的问题 ### Fix - 修复谷歌字体问题 - 文件监控: 忽略非文件创建、删除和移动事件 (#514) - 修复中文定时关机无法识别的BUG (#510) - 修复日志文件删除失败的问题 ## v0.3.83 (2025-06-12) ### Feat - 新增开关控制是否开始谷歌统计 see #473 - 支持b站合集和收藏下载 (#487) ### Fix - 修复安全问题 - 修复安全问题 ## v0.3.82 (2025-05-30) ### Fix - 修复节假日文件没有打包到 docker 镜像里的问题 ## v0.3.81 (2025-05-28) ### Feat - 定时任务支持工作日和休息日 see #182 ### Fix - 动态小程序码生成接口 (#478) - 指定日志编码,避免中文乱码 (#475) ## v0.3.80 (2025-05-18) ### Feat - 新增 OH2 型号的支持 - 支持配置最大搜索歌曲数量 see #462 ### Fix - 修复获取在线歌曲长度 (#469) ## v0.3.79 (2025-04-29) ### Fix - 修复型号LX05不能播放问题 ## v0.3.78 (2025-04-09) ### Feat - 优化获取时长的方式 ### Fix - 修复部分型号单曲播放功能无效的问题 - 监控文件夹仅对音乐格式生效,减少不必要的刷新 (#441) - 修复型号X4B不能播放问题 ## v0.3.77 (2025-03-13) ### Feat - OH2P 型号支持打断说话 ### Fix - 修复首页看不到设备的问题 ## v0.3.76 (2025-03-11) ### Feat - 整理第三方播放设备的代码 - 删除歌曲口令默认关闭 - 新增删除歌曲口令 see #402 - 增加音量控制for -3thplay.html by socketio.emit (#404) - 增加大声,音量,继续的指令,用于3thplay (#401) - 不用再手动配置 tts command, 优先使用已知的 tts command - 新增重新初始的定时任务 #182 - 新增定时任务用于开启和关闭拉取对话记录 #182 - 加入了遥控网页播放,用于实现电视上使用 (#395) ### Fix - tailwind播放页面有报错,暂时禁用 - tailwind 主题问题 - 修复下载后不自动播放的问题 - 修复每次打开页面都是随机播放的问题 ## v0.3.75 (2025-02-18) ### Feat - 监测音乐文件夹变化更新歌曲列表 (#394) - 添加正在播放页面 (#386) - 更新tailwind主题 (#383) ### Fix - 未开启模糊匹配播放错误问题 (#393) - 修复tailwind主题样式问题 (#384) ## v0.3.74 (2025-01-21) ### Feat - 新增 Tailwind 主题 - 修改设置页面文档链接 ### Fix - 修复下载歌单重命名问题 ## v0.3.73 (2025-01-16) ### Fix - 当前歌曲不在列表中时才切换列表 close #359 - 修复默认主题播放进度时间问题 - 尝试修复获取对话记录失败的问题 close #362 ## v0.3.72 (2025-01-11) ### Feat - 新增播放文字功能 ### Fix - 修复默认主题没有单曲循环的问题 see #355 ## v0.3.71 (2025-01-07) ### Feat - 支持自动填 ip 和端口 ### Fix - 搜索歌曲窗口不自动关闭 see #351 - 解决歌词信息写入失败的问题 - 修复一些小问题 - 非播放中也返回歌曲时长 see #340 ## v0.3.70 (2025-01-04) ### Fix - 尝试修复部分设备无法启动的问题 - 解决首页提示翻译英文问题 - 尝试解决 supervisor 启动报错 ## v0.3.69 (2025-01-01) ### Feat - 支持关闭获取对话记录功能 ### Fix - 尝试解决网络卡顿问题 ## v0.3.68 (2024-12-31) ### Feat - umami 脚本改为异步加载 - 支持 python3.13 版本 - 增加均衡歌曲响度(可选) (#338) ### Fix - 修复保存设置时可能出现报错的情况 ## v0.3.67 (2024-12-29) ### Feat - 简化设置,不允许修改监听端口 ### Fix - 修复默认主题搜索问题 ## v0.3.66 (2024-12-26) ### Fix - 修复歌曲批量重命名的问题 - 修复自定义歌单删除后没刷新歌单列表 - 尝试修复更新失败问题 ## v0.3.65 (2024-12-24) ### Fix - 处理图像报错 - 修改歌单名字漏更新歌单列表 - 修复获取自定义歌单接口报错 ## v0.3.64 (2024-12-22) ### Fix - 使用自己架设的 sentry 服务,解决 Cloudflare 额度超量问题 ## v0.3.63 (2024-12-22) ### Perf - 只监控报错信息 ## v0.3.62 (2024-12-21) ### Fix - 修复首次配置时,默认主题只有一个设备的问题。 - 修复一些报错问题 ## v0.3.61 (2024-12-19) ### Fix - 尝试修复更新问题 ### Refactor - 重构更新流程 ## v0.3.60 (2024-12-19) ## v0.3.59 (2024-12-19) ### Feat - 新增更多的歌单编辑相关接口 - 一键更新功能 - 接入 sentry 平台 - 实现更新接口 - 下载完成之后修改文件权限为755 close #316 ### Fix - 解决飞牛平台报错问题 ### Refactor - 优化代码日志级别 - 更新等消息推送到客户端再重启 - 更新接口修改 ## v0.3.58 (2024-12-15) ### Fix - 尝试解决七牛设备问题 - 修复 umami 统计函数报错,解决七牛环境问题 ## v0.3.57 (2024-12-14) ### Feat - 优化批量下载工具命名和下载高码率音频 - 新增搜索播放口令用于生成临时播放列表 - 新增设置项 ignore_tag_dirs 用于忽略读取目录下的标签信息,解决挂载 alist 目录的问题 - 新主题 Material (#299) ### Fix - 默认主题刷新时选中当前播放歌曲 - 修复当前播放列表没更新的问题 - 修复搜索时不显示保存输入框问题 - 收藏 (#301) - 修复收藏歌曲失败的问题 - 小屏幕设备主页显示问题 (#300) ### Refactor - 修改默认主题 - 后端也加入 umami 统计 - 新增自托管 umami 统计 - XIAOMUSIC_HOSTNAME 携带端口号友好提醒并处理 (#303) - 修改标题 - 冲突解决错误 ## v0.3.56 (2024-12-07) ### Feat - tag 信息支持写入到歌曲文件 see #266 - 开启gzip压缩 ### Fix - 播放失败设置重试次数10次,解决设备失联后无限重试的问题 - 修复最近新增歌单问题 - 小程序码移动端布局兼容 (#293) ## v0.3.55 (2024-12-04) ### Fix - 修复播放接口报错问题 ## v0.3.54 (2024-12-04) ### Feat - 新增最近新增歌单 close #273 ### Fix - 安卓低版本webview对audio的src为空值的报错 (#289) - 修复M01语音播放问题,X08C X08E X8F 型号默认采用型号兼容模式 see #30 ## v0.3.53 (2024-12-03) ### Fix - 解决播放接口修改后播放失败的问题 ## v0.3.52 (2024-12-03) ### Fix - 修复播放接口参数错误的问题 ## v0.3.51 (2024-12-03) ### Fix - 修复空配置启动失败问题 close #284 ## v0.3.50 (2024-12-03) ### Feat - 修改日志文件的默认值 - 新增修改tag缓存信息的接口 close #266 - 新增专用的播放歌曲和播放歌单接口,解决默认口令提示词被修改了导致后台失效的问题 - 统计设备型号 - 页面与设置中的HOST不一致时弹窗提醒 (#281) - 未发现小爱设备时给予提示 (#278) - 优化设置页面提示 ### Fix - 更新 yt-dlp ,解决 B 站下载问题 close #279 - 网页播放audio获取到错误url无法播放时提醒用户 (#280) - input标签自闭合 ### Refactor - 调整设置页面 ## v0.3.49 (2024-11-28) ### Feat - 临时文件目录支持配置 #99 - 新增单曲播放和顺序播放功能 close #277 - 设置播放类型支持配置语音提示词,定时任务支持设置播放类型 ### Fix - 修复中文数字转换函数对'十、十一'等数字的处理 (#275) ## v0.3.48 (2024-11-20) ### Feat - 支持替换默认口令,而不是追加 close #259 - 新增自定义个歌单接口 #242 ### Fix - 锁定 PWA 应用旋转方向 ## v0.3.47 (2024-11-14) ### Feat - 支持 PWA 应用安装 - 新增模糊匹配测试用例 ### Fix - 修复 PWA 应用有密码时报错的问题 - 修复播放顺序没有按数字排序的问题 close #249 ## v0.3.46 (2024-11-08) ### Feat - 升级依赖库 ### Fix - 添加依赖库 requests ### Refactor - 依赖库已经支持分段获取静态文件,重构代码 ## v0.3.45 (2024-11-08) ### Feat - 定时任务支持设置音量 - 播放歌单口令支持配置 ### Fix - 修复定时任务报错问题 ## v0.3.44 (2024-11-01) ### Feat - 日志时间里加上日期 ### Fix - 修复搜索失败的问题 ## v0.3.43 (2024-10-30) ### Feat - 播放列表可以删除当前歌曲(!危险操作,请在设置中心开启相关功能) (#250) - 插件自定义口令支持获取语音输入内容 #105 ### Fix - 修复谷歌统计导致的卡顿问题 - 解决挂载网盘卡死的问题 ## v0.3.42 (2024-10-24) ### Fix - 尝试修复缺少 libtiff.so.6 文件的问题 #244 - 修复默认主题播放歌曲输入框空的情况 - 尝试修复停止后自动播放的问题 ## v0.3.41 (2024-10-17) ### Feat - 设置默认时区为东八区 closed #236 ### Fix - 修复获取标签信息报错问题 - remove_id3_tags return None if no id3 tag (#238) - bug in del_music (#237) ## v0.3.40 (2024-10-16) ### Feat - 默认主题的播放列表上显示歌曲数量 ### Fix - 修复播放卡顿问题(谷歌统计地址无法访问的情况) ## v0.3.39 (2024-10-15) ### Feat - 固定的播放列表全部初始化 - 生产环境与开发环境接口分离、关于页面增加返回到主页的链接 update: 支持https页面未及时更新的问题 ### Fix - pure主题 当前设备与远程设备未正确区分的问题 (#234) - static和doc添加basic auth (#231) ### Refactor - 修改默认UI播放提示词 (#233) ## v0.3.38 (2024-10-14) ### Feat - 播放状态接口返回当前播放列表 (#229) - 新增口令收藏歌曲用来收藏当前播放的歌曲 - 默认UI搜索框动态显示 (#228) - 文件转换逻辑延迟到读取文件的时候 see #218 - 重写播放组件,现在支持歌词显示了 - 使用 /cmdstatus 接口来判断异步任务是否完成 - 新增接口 /cmdstatus 用于查询异步任务是否执行完毕 - XMusicPlayer播放器主题优化 (#216) - XMusicPlayer播放器主题 (#214) - 新增 yt-dlp cookies 文件参数支持 - 新增批量下载歌曲工具 - 新增后台网站图标 - 加密音乐和图片访问链接 (#200) - 歌曲信息中的图片改为url #190 - 新增更新提醒 - 定时任务新增刷新播放列表接口 - 后台设置名称优化 - 新增按钮刷新 tag 信息 - 新增 musicinfos 接口用于批量查询歌曲信息 - 增加 tags 缓存 (#193) - 使用 opencc 将歌曲名转化为简体 (#192) - 搜索的歌曲存成列表供前端显示,实现额外索引 (#188) - 搜索多个结果,并更新“当前”播放列表 (#185) - musicinfo接口新增musictag参数,用于返回歌曲额外信息 - 新增口令【播放列表第几个+列表名】来播放列表里的第几个 #158 - 新增定时任务功能 #182 - hostname can take protocol,域名支持 https 格式 (#181) ### Fix - xplayer 收藏歌曲、取消收藏 (#230) - 修复型号M01获取对话记录时间戳的问题 - 修复型号M01无法获取到对话记录的问题 - 使用小爱设备播放时组件异常的问题 (#217) - 修复图片获取失败的问题 - 修复 yt-dlp-cookies 报错 - 修复自定义口令末尾多余逗号的情况 - 修复windows下路径问题 - 解决 L05C 无提示音问题 support MiIOService tts (#198) - 解决歌曲信息乱码问题 - 修复搜索补全不生效的问题 - 修复默认主题没有选中上次播放列表的问题 - ffmpeg only output audio (#184) ### Refactor - 新增清理缓存按钮 - 优化默认UI的搜索框#226 (#227) - 修复告警 - 体验优化,音乐列表缓存 (#222) - 修改为播放选中歌曲 - 更新静态文件 ### Perf - 对歌曲信息中的图片缩小到300 #190 ## v0.3.37 (2024-09-20) ### Feat - Pure主题更新 设置中心新增主题音乐列表样式选择、夜间模式、其他多项优化 (#180) ## v0.3.36 (2024-09-19) ### Feat - Pure 主题更新 (#178) - 支持配置获取对话记录间隔时间 #169 - 允许在后台设置监听端口 ### Fix - 修复开启继续播放时歌曲播放不完整问题 (#177) ## v0.3.35 (2024-09-18) ### Feat - 允许跨域访问 #172 ### Fix - 修复 Pure 主题白屏无法打开的问题 (#176) ## v0.3.34 (2024-09-18) ### Feat - 新增 pure 主题 vue + elementUI (#172) ### Fix - 主页适配移动端 - 修复网页播放点击后没有关闭旧声音的问题 #166 - 修复单曲循环的情况下歌曲不在当前播放列表时失效的情况 ### Refactor - 优化代码:输入框处理抖动问题,网页播放修改实现方式 see #166 ## v0.3.33 (2024-09-15) ### Feat - 调整页面布局 - 支持继续播放 (#171) ### Fix - #168 安全优化: 设置数据接口密码隐藏处理 - 修复谷歌统计报错问题 ### Refactor - 优化谷歌统计 ## v0.3.32 (2024-09-14) ### Feat - 新增谷歌统计 - 增加播放进度 (#160) ### Fix - 优化audio_id查询方式 (#165) - 播放链接接口支持复杂的链接 ## v0.3.31 (2024-09-10) ### Feat - 新增播放上一首歌曲功能 #90 - 新增所有歌曲列表 - 触屏版显示歌曲名称 (#156) ### Fix - 修复插件示例报错 #105 - 修复当前播放歌曲没保存的问题 #90 ## v0.3.30 (2024-09-07) ### Feat - 修改设置按钮位置 - 新增网页播放接口 #138 ## v0.3.29 (2024-09-06) ### Feat - 设置页面新增接口文档入口 ### Fix - 修复网页开启秘密验证无法播歌的问题 #149 ## v0.3.28 (2024-09-03) ### Feat - 新增歌曲收藏功能 #87 ### Fix - docker下minetypes无法判断m4a ### Refactor - ffmpeg_location 从配置里读取 ## v0.3.27 (2024-09-02) ### Feat - Add feature as requested in issue #143 ### Fix - 默认下载目录修改 ### Refactor - 处理 code review 问题' ## v0.3.26 (2024-08-17) ### Feat - 删除网关模式 ## v0.3.25 (2024-08-16) ### Feat - 设置页面支持配置 use_music_api 选项 ## v0.3.24 (2024-08-01) ### Fix - #131 修复多设备切换时播放模式显示错误问题 ## v0.3.23 (2024-08-01) ### Fix - 修复部分文件获取不到播放时长问题 - 处理安全问题 ## v0.3.22 (2024-08-01) ### Feat - 网关模式支持配置,默认关闭 ### Fix - 继续优化延迟问题 ## v0.3.21 (2024-07-30) ### Feat - 尝试加个网关在前面处理静态文件来加速文件获取 ### Fix - 使用前置网关处理静态文件来加速,尝试解决延迟的问题 - 播放前先立即暂停之前的音乐 ## v0.3.20 (2024-07-30) ### Fix - 尝试修复延迟问题,修复播放停止不了的问题 ## v0.3.19 (2024-07-30) ### Fix - 调整配置,优化获取歌曲时长接口 ## v0.3.18 (2024-07-29) ### Fix - #135 修复获取不到播放时长时只播放3秒的问题 ## v0.3.17 (2024-07-28) ### Fix - 优化日志输出,尝试排查延迟播放的问题 ## v0.3.16 (2024-07-28) ## v0.3.15 (2024-07-28) ### Fix - 修复自定义口令重复的问题 - 修复日志输出问题 - 修复退出异常问题 ## v0.3.14 (2024-07-28) ### Feat - 优化播放延迟问题,并新增配置下一首播放的延迟秒数 ## v0.3.13 (2024-07-24) ### Fix - 解决 docker 镜像问题 ## v0.3.12 (2024-07-24) ### Feat - 优化获取文件播放时长接口,尝试解决播放延迟和操作面板卡顿的问题 ## v0.3.11 (2024-07-22) ### Feat - Add remove mp3 id3 tag function ### Fix - #130 单曲循环的模式下,播放列表的指令不生效 ### Refactor - 优化代码 ## v0.3.10 (2024-07-19) ### Feat - 支持软连接的接口直接用os.walk即可 ### Fix - 修复软连接目录不能播放的问题 - 修复自定义语音口令设置不生效的问题 ## v0.3.9 (2024-07-17) ### Feat - #119 音乐目录支持软连接 ### Fix - 修复日志下载报错问题 - 兼容旧的setting.json文件中conf_path为空的情况 - 修复设置页面可能打不开的问题 ## v0.3.8 (2024-07-16) ### Fix - 修复播放url接口问题 ## v0.3.7 (2024-07-16) ### Feat - 播放链接按钮对应给个默认的链接用于测试 - Uvicorn 的日志信息合并到 xiaomusic 日志里显示 ## v0.3.6 (2024-07-15) ### Fix - #126 修复pip安装时主页打不开的问题 ## v0.3.5 (2024-07-15) ### Fix - #116 播放失败自动切下首歌 ## v0.3.4 (2024-07-15) ### Fix - #125 修复本地英文歌曲匹大小写字母配不到的问题 ## v0.3.3 (2024-07-15) ### Fix - 尝试修复播放卡顿问题 see #124 ## v0.3.2 (2024-07-15) ### Fix - #122 pip安装方式下,static目录找不到报错 - 版本更新时更新页面缓存 ## v0.3.1 (2024-07-15) ### Fix - 修复主页选择设备不生效的问题 see #120 ## v0.3.0 (2024-07-14) ### Feat - 建议音乐目录和配置目录分开不同目录 - 优化后台网络设置,同时支持ipv4和ipv6 - 使用fastapi替换flask,解决多线程问题 - #106 网页上显示音箱当前状态(播放中or空闲中)以及当前的播放模式 - 优化首页加载慢的问题 - 优化设置页面布局,方便配置必须项 - 优化配置界面,支持配置分组 - 支持多设备分开播放 see #65 ### Fix - #114 修复部分 mp3 文件长度识别错误 - 删除 armv6 的支持 - 修复编译问题 - 修复音乐路径设置后找不到音乐的问题 - 修复启动报错的问题 - 修复CI警告问题 ## v0.2.0 (2024-07-09) ### Feat - 触屏版可以不用设置 XIAOMUSIC_USE_MUSIC_API - 升级依赖库 - 唤醒口令配置支持配语音词,简化自定义口令配置 see #105 ## v0.1.101 (2024-07-07) ### Fix - #81 修复播放列表时,当前歌曲不在列表没有更换歌曲的问题 - #110 修复配置加载问题 ## v0.1.100 (2024-07-07) ### Fix - 日志代码写错 ## v0.1.99 (2024-07-07) ### Fix - #81 修复播放列表没有继续播放上次播放的歌曲,并把随机播放,全部循环,单曲循环状态落地 ## v0.1.98 (2024-07-07) ### Fix - 修复多设备获取不到对话记录的问题 see #65 - #93 修复目录深度设置后导致目录下的歌曲无法加到播放列表里的问题 ## v0.1.97 (2024-07-06) ### Fix - 修复网页控制台设置页面保存报错 ## v0.1.96 (2024-07-06) ### Feat - 使用commitizen管理版本号 - 页面版本号链接到CHANGELOG页面 - 规范版本管理 ## v0.1.95 (2024-07-06) ## v0.1.94 (2024-07-06) ### Feat - 优化多设备接口执行效果,尽量做到同时执行 ### Fix - 新增参数配置强制打断小爱说话 - 修复多设备获取对话记录的问题 - 修复windows下路径分隔符被视为转移符导致音箱无法播放音乐的问题 - 修复播放链接报错 - 修复配置页面默认配置被置空的问题 ## v0.1.93 (2024-07-05) ### Feat - 访问账号密码默认为空 - 支持下载的目录与本地音乐目录分开 see #98 - 新增m4a文件格式支持 - 设置页面支持配置多设备 - 默认用空的后台账号和密码 - 支持多个设备同时播放 see #65 - 新增自定义口令功能 #105 ### Fix - 修复设置页面没成功初始化设置问题 - 修复镜像缺少文件问题 - 尝试解决插件路径问题 - 设置页面日志路径写错了 - 修复口令导致异常关闭的问题 ## v0.1.92 (2024-07-04) ### Feat - 启动参数新增 --port 配置监听端口 - 外网访问端口可独立配置 - 优化设置页面,新增更多配置项 - 首次保存设置后不需要重启容器 ### Fix - 日志文件配置的环境变量写错了 ## v0.1.91 (2024-07-03) ### Fix - 尝试解决触屏版不能播放的问题 ## v0.1.90 (2024-07-02) ### Feat - 优化触屏版播放页面显示歌曲 ## v0.1.89 (2024-07-02) ### Feat - 尝试解决触屏版无法播放的问题 ### Fix - 播放歌曲写成固定的了 - 播放歌曲时被其他指令打断后没有继续播放 ## v0.1.88 (2024-07-02) ### Feat - 日志里不要输出敏感信息 - 优化下载 ffmpeg 脚本,尝试解决 armv7 环境问题 - 优化日志输出信息 - 尝试解决触屏版无法播放的问题 ### Fix - 是否下载中判断错误导致播放无法自动重新开始播放 - 升级yt-dlp到2024.07.01 - 修复部分型号关机失败的问题 ## v0.1.87 (2024-07-01) ### Fix - 修复XIAOMUSIC_USE_MUSIC_API=true时播放不了的问题 ## v0.1.86 (2024-07-01) ### Feat - 优化 ffmpeg 安装脚本 - 新增调试工具用来调试 player_play_music 接口 - 升级依赖库 MiService ### Fix - 尝试修复 armv7 的 ffmpeg 问题 - 尝试修复关机失败的问题 - 修复口令不能播放的问题 ## v0.1.85 (2024-06-30) ### Feat - 版本号链接到github的release页面,方便查看版本更新日志 ### Fix - 修复电台删除后没有从电台列表中删除的问题 ## v0.1.84 (2024-06-30) ### Feat - config.json 支持更多配置选项 - 新增 XIAOMUSIC_STOP_TTS_MSG 配置关机提示音 ## v0.1.83 (2024-06-30) ## v0.1.82 (2024-06-30) ### Feat - 优化指令匹配规则 ## v0.1.81 (2024-06-30) ## v0.1.80 (2024-06-30) ### Fix - #91 修复下载歌曲报错 ## v0.1.79 (2024-06-29) ## v0.1.77 (2024-06-29) ### Fix - #52 支持配置模糊匹配本地歌曲 ## v0.1.76 (2024-06-28) ## v0.1.75 (2024-06-28) ## v0.1.74 (2024-06-28) ## v0.1.73 (2024-06-28) ## v0.1.72 (2024-06-28) ## v0.1.71 (2024-06-28) ### Fix - #83 ## v0.1.70 (2024-06-27) ## v0.1.69 (2024-06-26) ## v0.1.67 (2024-06-26) ## v0.1.66 (2024-06-26) ## v0.1.65 (2024-06-26) ## v0.1.64 (2024-06-26) ## v0.1.62 (2024-06-25) ## v0.1.61 (2024-06-25) ## v0.1.60 (2024-06-25) ## v0.1.58 (2024-06-25) ### Fix - 登陆失败不阻塞启动 ## v0.1.57 (2024-06-24) ## v0.1.56 (2024-06-24) ## v0.1.55 (2024-06-23) ### Fix - #47 支持配置基础的BaseAuth登录 ## v0.1.54 (2024-06-23) ### Fix - #76 新增XIAOMUSIC_MUSIC_PATH_DEPTH配置生成播放列表的目录深度,默认10 - #74 配置目录可以和下载目录分开配置, 新增XIAOMUSIC_CONF_PATH用来设置配置目录,不配置时使用下载目录 ## v0.1.53 (2024-06-23) ## v0.1.52 (2024-06-21) ## v0.1.51 (2024-06-20) ## v0.1.49 (2024-06-20) ## v0.1.48 (2024-06-16) ## v0.1.47 (2024-06-16) ## v0.1.46 (2024-06-15) ## v0.1.45 (2024-06-15) ## v0.1.44 (2024-06-14) ## v0.1.43 (2024-06-14) ## v0.1.41 (2024-06-14) ## v0.1.40 (2024-06-12) ## v0.1.39 (2024-06-12) ## v0.1.38 (2024-06-12) ### Fix - #70 下一首歌曲不存在时从播放列表中删除并继续找下一首 ## v0.1.37 (2024-06-04) ## v0.1.36 (2024-05-30) ## v0.1.35 (2024-05-30) ### Fix - #67 没配置did时也允许启动 http 服务 ## v0.1.34 (2024-05-19) ## v0.1.33 (2024-05-19) ### Fix - #50 新增配置页面 - #62 ## v0.1.32 (2024-05-17) ## v0.1.31 (2024-05-16) ## v0.1.30 (2024-05-16) ### Fix - 控制台显示版本号 #59 ## v0.1.29 (2024-05-16) ### Fix - #57 #55 ## v0.1.28 (2024-05-16) ## v0.1.27 (2024-05-16) ## v0.1.26 (2024-05-08) ## v0.1.25 (2024-05-06) ## v0.1.24 (2024-04-30) ## v0.1.23 (2024-04-30) ## v0.1.22 (2024-04-30) ## v0.1.21 (2024-04-08) ## v0.1.20 (2024-04-08) ## v0.1.19 (2024-04-04) ## v0.1.18 (2024-02-24) ## v0.1.16 (2024-02-24) ## v0.1.15 (2024-02-03) ## v0.1.14 (2024-02-03) ## v0.1.13 (2024-02-02) ## v0.1.12 (2024-01-30) ### Fix - set volume failed ## v0.1.11 (2024-01-29) ## v0.1.10 (2024-01-29) ## v0.1.9 (2024-01-28) ### Fix - arg1 漏修改 ## v0.1.8 (2024-01-28) ### Fix - http server listen host ## v0.1.7 (2024-01-28) ## v0.1.6 (2024-01-28) ## v0.1.5 (2024-01-27) ## v0.1.4 (2024-01-27) ### Fix - error when play next ## v0.1.3 (2023-10-15) ## v0.1.2 (2023-10-15) ## v0.1.1 (2023-10-14) ================================================ FILE: Dockerfile ================================================ # 定义构建参数,用于指定架构和基础镜像 ARG PYTHON_VERSION=3.14 # 根据不同架构选择对应的基础镜像 FROM python:${PYTHON_VERSION}-alpine AS base-linux-amd64 FROM python:${PYTHON_VERSION}-alpine AS base-linux-arm64 FROM python:${PYTHON_VERSION}-bookworm AS base-linux-arm-v7 FROM python:${PYTHON_VERSION}-alpine AS run-linux-amd64 FROM python:${PYTHON_VERSION}-alpine AS run-linux-arm64 FROM python:${PYTHON_VERSION}-bookworm AS run-linux-arm-v7 FROM --platform=$BUILDPLATFORM alpine AS shelf # 这里的逻辑是关键:接收标准的 TARGETPLATFORM (如 linux/amd64) # 并将其转换为我们定义的别名格式 (如 linux-amd64) ARG TARGETPLATFORM RUN echo ${TARGETPLATFORM//\//-} > /platform_id # 根据TARGETPLATFORM自动选择对应的builder阶段 ARG TARGETPLATFORM FROM base-${TARGETPLATFORM//\//-} AS builder # 安装构建依赖(根据基础镜像类型区分) RUN if [ -f /etc/alpine-release ]; then \ # Alpine系统依赖 apk add --no-cache \ build-base \ nodejs \ npm \ zlib-dev \ jpeg-dev \ freetype-dev \ lcms2-dev \ openjpeg-dev \ tiff-dev \ libwebp-dev; \ else \ # Debian系统依赖 apt-get update && apt-get install -y --no-install-recommends \ build-essential \ nodejs \ npm \ zlib1g-dev \ libjpeg-dev \ libfreetype6-dev \ liblcms2-dev \ libopenjp2-7-dev \ libtiff5-dev \ libwebp-dev \ && rm -rf /var/lib/apt/lists/*; \ fi # 安装PDM RUN pip install -U pdm ENV PDM_CHECK_UPDATE=false WORKDIR /app COPY pyproject.toml README.md package.json ./ # 安装Python和Node.js依赖 RUN pdm install --prod --no-editable -v RUN npm install --loglevel=verbose # 复制应用代码 COPY xiaomusic/ ./xiaomusic/ COPY plugins/ ./plugins/ COPY holiday/ ./holiday/ COPY xiaomusic.py . # -------------------------- 运行阶段 -------------------------- # 根据TARGETPLATFORM自动选择对应的runner阶段 ARG TARGETPLATFORM FROM run-${TARGETPLATFORM//\//-} AS runner # 安装运行时依赖(区分Alpine和Debian) RUN if [ -f /etc/alpine-release ]; then \ # Alpine运行时依赖 apk add --no-cache \ ffmpeg \ nodejs \ npm; \ else \ # Debian运行时依赖 apt-get update && apt-get install -y --no-install-recommends \ ffmpeg \ nodejs \ npm \ && rm -rf /var/lib/apt/lists/*; \ fi # 设置工作目录 WORKDIR /app # 从构建阶段复制产物 COPY --from=builder /app/.venv ./.venv COPY --from=builder /app/node_modules ./node_modules/ COPY --from=builder /app/xiaomusic/ ./xiaomusic/ COPY --from=builder /app/plugins/ ./plugins/ COPY --from=builder /app/holiday/ ./holiday/ COPY --from=builder /app/xiaomusic.py . COPY --from=builder /app/xiaomusic/__init__.py /base_version.py COPY --from=builder /app/package.json . # 创建FFmpeg软链接目录(兼容不同系统的ffmpeg路径) RUN mkdir -p /app/ffmpeg/bin \ && ln -s $(which ffmpeg) /app/ffmpeg/bin/ffmpeg \ && ln -s $(which ffprobe) /app/ffmpeg/bin/ffprobe RUN touch /app/.dockerenv # 设置卷和暴露端口 VOLUME /app/conf VOLUME /app/music EXPOSE 8090 # 设置环境变量 ENV TZ=Asia/Shanghai ENV PATH=/app/.venv/bin:/usr/local/bin:$PATH # 直接启动xiaomusic应用 CMD ["/app/.venv/bin/python3", "/app/xiaomusic.py"] ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2023 涵曦 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: README.md ================================================ # XiaoMusic: 无限听歌,解放小爱音箱 [](https://github.com/hanxi/xiaomusic) [](https://hub.docker.com/r/hanxi/xiaomusic) [](https://hub.docker.com/r/hanxi/xiaomusic) [](https://pypi.org/project/xiaomusic/) [](https://pypi.org/project/xiaomusic/) [](https://pypi.org/project/xiaomusic/) [](https://github.com/hanxi/xiaomusic/releases) [](https://visitorbadge.io/status?path=hanxi%2Fxiaomusic) [](https://visitorbadge.io/status?path=hanxi%2Fxiaomusic) ---
🎵 使用小爱音箱播放音乐,音乐使用 yt-dlp 下载
🏠 GitHub • 📖 文档 • 💬 FAQ • 💭 讨论区
--- > [!TIP] > **新手指南**:初次安装遇到问题请查阅 [💬 FAQ问题集合](https://github.com/hanxi/xiaomusic/issues/99),一般遇到的问题都已经有解决办法。 ## 👋 快速入门指南 已经支持在 web 设置页面配置其他参数,不再需要设置环境变量, docker compose 配置如下(选一个即可): ```yaml services: xiaomusic: image: hanxi/xiaomusic container_name: xiaomusic restart: always ports: - 58090:8090 volumes: - /xiaomusic_music:/app/music - /xiaomusic_conf:/app/conf ``` 🔥 国内: ```yaml services: xiaomusic: image: docker.hanxi.cc/hanxi/xiaomusic container_name: xiaomusic restart: always ports: - 58090:8090 volumes: - /xiaomusic_music:/app/music - /xiaomusic_conf:/app/conf ``` 测试版: ```yaml services: xiaomusic: image: hanxi/xiaomusic:main container_name: xiaomusic restart: always ports: - 58090:8090 volumes: - /xiaomusic_music:/app/music - /xiaomusic_conf:/app/conf ``` 对应的 docker 启动命令如下: ```bash docker run -p 58090:8090 -v /xiaomusic_music:/app/music -v /xiaomusic_conf:/app/conf hanxi/xiaomusic ``` 🔥 国内: ```bash docker run -p 58090:8090 -v /xiaomusic_music:/app/music -v /xiaomusic_conf:/app/conf docker.hanxi.cc/hanxi/xiaomusic ``` 测试版: ``` docker run -p 58090:8090 -v /xiaomusic_music:/app/music -v /xiaomusic_conf:/app/conf hanxi/xiaomusic:main ``` - 其中 conf 目录为配置文件存放目录,music 目录为音乐存放目录,建议分开配置为不同的目录。 - /xiaomusic_music 和 /xiaomusic_conf 是 docker 所在的主机的目录,可以修改为其他目录。如果报错找不到 /xiaomusic_music 目录,可以先执行 `mkdir -p /xiaomusic_{music,conf}` 命令新建目录。 - /app/music 和 /app/conf 是 docker 容器里的目录,不要去修改。 - 58090 是 NAS 本地端口的。8090 是容器端口,不要去修改。 - 后台访问地址为: http://NAS_IP:58090 > [!NOTE] > docker 和 docker compose 二选一即可,启动成功后,在 web 页面可以配置其他参数,带有 `*` 号的配置是必须要配置的,其他的用不上时不用修改。初次配置时需要在页面上输入小米账号和密码保存后才能获取到设备列表。 > [!TIP] > 目前安装步骤已经是最简化了,如果还是嫌安装麻烦,可以微信或者 QQ 约我远程安装,我一般周末和晚上才有时间,需要赞助个辛苦费 :moneybag: 50 元一次。 遇到问题可以去 web 设置页面底部点击【下载日志文件】按钮,然后搜索一下日志文件内容确保里面没有账号密码信息后(有就删除这些敏感信息),然后在提 issues 反馈问题时把下载的日志文件带上。 > [!TIP] > 作者新写了一个更简洁的个人音乐服务器,支持更强的插件扩展
> - 不知道选哪个套餐可以直接买这个最便宜的
## 🎤 功能特性
### 🤐 支持语音口令
#### 基础播放控制
- **播放歌曲** - 播放本地的歌曲
- **播放歌曲+歌名** - 例如:播放歌曲周杰伦晴天
- **上一首** / **下一首** - 切换歌曲
- **关机** / **停止播放** - 停止播放
#### 播放模式
- **单曲循环** - 重复播放当前歌曲
- **全部循环** - 循环播放所有歌曲
- **随机播放** - 随机顺序播放
#### 歌单管理
- **播放歌单+目录名** - 例如:播放歌单其他
- **播放歌单第几个+列表名** - 详见 [#158](https://github.com/hanxi/xiaomusic/issues/158)
- **播放歌单收藏** - 播放收藏歌单
#### 收藏功能
- **加入收藏** - 将当前播放的歌曲加入收藏歌单
- **取消收藏** - 将当前播放的歌曲从收藏歌单移除
> [!TIP]
> **隐藏玩法**:对小爱同学说"播放歌曲小猪佩奇的故事",会先下载小猪佩奇的故事,然后再播放。
## 📦 安装方式
### 方式一:Docker Compose(推荐)
详见 [👋 快速入门指南](#-快速入门指南)
### 方式二:Pip 安装
```shell
# 安装
pip install -U xiaomusic
# 查看帮助
xiaomusic --help
# 启动(使用配置文件)
xiaomusic --config config.json
# 启动(使用默认端口 8090)
xiaomusic
```
> [!NOTE]
> `config.json` 文件可以参考 `config-example.json` 文件配置。详见 [#94](https://github.com/hanxi/xiaomusic/issues/94)
## 👨💻 开发指南
### 🔩 开发环境运行
1. **下载依赖**
```shell
./install_dependencies.sh
```
2. **安装环境**
```shell
pdm install
```
3. **启动服务**
```shell
pdm run xiaomusic.py
```
默认监听端口 8090,使用其他端口请自行修改。
4. **查看 API 文档**
访问 💬 GitHub Issues • 🎮 QQ频道 • 👥 QQ交流群 • 💬 微信群
### 🤝 如何贡献 我们欢迎所有形式的贡献,包括但不限于: - 🐛 **报告 Bug**:在 [Issues](https://github.com/hanxi/xiaomusic/issues) 中提交问题 - 💡 **功能建议**:分享你的想法和建议 - 📝 **改进文档**:帮助完善文档和教程 - 🎨 **前端美化**:优化 Web 控制台界面 - 🔧 **代码贡献**:提交 Pull Request > [!TIP] > 提交代码前请确保运行 `pdm lintfmt` 检查代码规范 ## 📚 相关资源 ### 👉 更多教程 更多功能见 [📝 文档汇总](https://github.com/hanxi/xiaomusic/issues/211) ### 🎨 第三方主题 - [pure 主题 xiaomusicUI](https://github.com/52fisher/xiaomusicUI) - [移动端的播放器主题](https://github.com/52fisher/XMusicPlayer) - [Tailwind主题](https://github.com/clarencejh/xiaomusic) - [SoundScape主题](https://github.com/jhao0413/SoundScape) - [第三方主题](https://github.com/DarrenWen/xiaomusicui) ### 📱 配套应用 - [微信小程序: 卯卯音乐](https://github.com/F-loat/xiaoplayer) - [手机APP: 风花雪乐](https://github.com/jokezc/mi_music) - [JS在线播放插件](https://github.com/boluofan/xiaomusic-online) - [手机APP: HMusic](https://github.com/hpcll/HMusic) - [安卓TV: 肉肉音乐TV](https://github.com/GanHuaLin/rouroumusic-tv) ### ❤️ 致谢 **核心依赖** - [xiaomi](https://www.mi.com/) - 小米智能设备 - [xiaogpt](https://github.com/yihong0618/xiaogpt) - 项目灵感来源 - [MiService](https://github.com/yihong0618/MiService) - 小米服务接口 - [yt-dlp](https://github.com/yt-dlp/yt-dlp) - 音乐下载工具 **开发工具** - [PDM](https://pdm.fming.dev/latest/) - Python 包管理 - [FastAPI](https://fastapi.tiangolo.com/) - Web 框架 - [Umami](https://github.com/umami-software/umami) - 统计分析 - [Sentry](https://github.com/getsentry/sentry) - 报错监控 **参考资料** - [实现原理](https://github.com/yihong0618/gitblog/issues/258) - [awesome-xiaoai](https://github.com/zzz6519003/awesome-xiaoai) **特别感谢** - 所有帮忙调试和测试的朋友 - 所有反馈问题和建议的朋友 - 所有贡献代码和文档的开发者 ## 🚨 免责声明 本项目仅供学习和研究目的,不得用于任何商业活动。用户在使用本项目时应遵守所在地区的法律法规,对于违法使用所导致的后果,本项目及作者不承担任何责任。 本项目可能存在未知的缺陷和风险(包括但不限于设备损坏和账号封禁等),使用者应自行承担使用本项目所产生的所有风险及责任。 作者不保证本项目的准确性、完整性、及时性、可靠性,也不承担任何因使用本项目而产生的任何损失或损害责任。 使用本项目即表示您已阅读并同意本免责声明的全部内容。 ## Star History [](https://star-history.com/#hanxi/xiaomusic&Date) ## 💖 支持项目 如果这个项目对你有帮助,欢迎通过以下方式支持: ### ⭐ Star 项目 点击右上角的 ⭐ Star 按钮,让更多人发现这个项目 ### 💰 赞赏支持 - [💝 爱发电](https://afdian.com/a/imhanxi) - 持续支持项目发展 - 扫码请作者喝杯奶茶 ☕
感谢你的支持!❤️
## License [MIT](https://github.com/hanxi/xiaomusic/blob/main/LICENSE) License © 2024 涵曦 ================================================ FILE: check_plugins.py ================================================ #!/usr/bin/env python3 """ 检查所有插件的加载状态 """ import sys sys.path.append(".") from xiaomusic.config import Config from xiaomusic.js_plugin_manager import JSPluginManager def check_all_plugins(): print("=== 检查所有插件加载状态 ===\n") config = Config() config.verbose = True class SimpleLogger: def info(self, msg): print(f"[INFO] {msg}") def error(self, msg): print(f"[ERROR] {msg}") def debug(self, msg): print(f"[DEBUG] {msg}") print("1. 创建插件管理器...") manager = JSPluginManager(None) manager.config = config manager.log = SimpleLogger() import time time.sleep(3) # 等待插件加载 print("\n2. 获取所有插件状态...") plugins = manager.refresh_plugin_list() print(f" 总共找到 {len(plugins)} 个插件") # 分类插件状态 working_plugins = [] failed_plugins = [] for plugin in plugins: if plugin.get("loaded", False) and plugin.get("enabled", False): working_plugins.append(plugin) else: failed_plugins.append(plugin) print(f"\n 正常工作的插件 ({len(working_plugins)} 个):") for plugin in working_plugins: print(f" ✓ {plugin['name']}") print(f"\n 失败的插件 ({len(failed_plugins)} 个):") for plugin in failed_plugins: print(f" ✗ {plugin['name']}: {plugin.get('error', 'Unknown error')}") # 清理 if hasattr(manager, "node_process") and manager.node_process: manager.node_process.terminate() if __name__ == "__main__": check_all_plugins() ================================================ FILE: config-example.json ================================================ { "account": "", "password": "", "mi_did": "", "cookie": "", "verbose": false, "music_path": "music", "temp_path": "music/tmp", "download_path": "music/download", "conf_path": "conf", "cache_dir": "music/cache", "hostname": "http://192.168.2.5", "port": 8090, "public_port": 58090, "proxy": null, "loudnorm": null, "search_prefix": "bilisearch:", "ffmpeg_location": "./ffmpeg/bin", "get_duration_type": "ffprobe", "active_cmd": "play,set_play_type_rnd,playlocal,play_music_list,play_music_list_index,stop_after_minute,stop,play_next,play_prev,set_play_type_one,set_play_type_all,set_play_type_sin,set_play_type_seq,gen_music_list,add_to_favorites,del_from_favorites,cmd_del_music,online_play,singer_play", "exclude_dirs": "@eaDir,tmp", "ignore_tag_dirs": "", "music_path_depth": 10, "disable_httpauth": true, "httpauth_username": "", "httpauth_password": "", "music_list_url": "", "music_list_json": "", "custom_play_list_json": "", "disable_download": false, "key_word_dict": { "下一首": "play_next", "上一首": "play_prev", "单曲循环": "set_play_type_one", "全部循环": "set_play_type_all", "随机播放": "set_play_type_rnd", "单曲播放": "set_play_type_sin", "顺序播放": "set_play_type_seq", "分钟后关机": "stop_after_minute", "刷新列表": "gen_music_list", "加入收藏": "add_to_favorites", "收藏歌曲": "add_to_favorites", "取消收藏": "del_from_favorites", "播放列表第": "play_music_list_index", "删除歌曲": "cmd_del_music", "播放本地歌曲": "playlocal", "本地播放歌曲": "playlocal", "播放歌曲": "play", "放歌曲": "play", "在线播放": "online_play", "播放歌手": "singer_play", "关机": "stop", "暂停": "stop", "停止": "stop", "停止播放": "stop", "播放列表": "play_music_list", "播放歌单": "play_music_list", "测试自定义口令": "exec#code1(\"hello\")", "测试链接": "exec#httpget(\"https://github.com/hanxi/xiaomusic\")" }, "key_match_order": [ "分钟后关机", "下一首", "上一首", "单曲循环", "全部循环", "随机播放", "单曲播放", "顺序播放", "关机", "刷新列表", "播放列表第", "播放列表", "加入收藏", "收藏歌曲", "取消收藏", "删除歌曲", "播放本地歌曲", "本地播放歌曲", "播放歌曲", "放歌曲", "在线播放", "播放歌手", "暂停", "停止", "停止播放", "播放歌单", "测试自定义口令", "测试链接" ], "use_music_api": false, "use_music_audio_id": "1582971365183456177", "use_music_id": "355454500", "log_file": "xiaomusic.log.txt", "fuzzy_match_cutoff": 0.6, "enable_fuzzy_match": true, "stop_tts_msg": "收到,再见", "enable_config_example": true, "keywords_playlocal": "播放本地歌曲,本地播放歌曲", "keywords_play": "播放歌曲,放歌曲", "keywords_online_play": "在线播放", "keywords_singer_play": "播放歌手", "keywords_stop": "关机,暂停,停止,停止播放", "keywords_playlist": "播放列表,播放歌单", "user_key_word_dict": { "测试自定义口令": "exec#code1(\"hello\")", "测试链接": "exec#httpget(\"https://github.com/hanxi/xiaomusic\")" }, "enable_force_stop": false, "devices": {}, "group_list": "", "remove_id3tag": false, "convert_to_mp3": false, "delay_sec": 0, "continue_play": false, "enable_file_watch": false, "file_watch_debounce": 10, "pull_ask_sec": 1, "enable_pull_ask": false, "crontab_json": "", "enable_yt_dlp_cookies": false, "enable_save_tag": false, "enable_analytics": true, "get_ask_by_mina": false, "play_type_one_tts_msg": "已经设置为单曲循环", "play_type_all_tts_msg": "已经设置为全部循环", "play_type_rnd_tts_msg": "已经设置为随机播放", "play_type_sin_tts_msg": "已经设置为单曲播放", "play_type_seq_tts_msg": "已经设置为顺序播放", "recently_added_playlist_len": 50, "enable_cmd_del_music": false, "web_music_proxy": true, "edge_tts_voice": "zh-CN-XiaoyiNeural", "enable_auto_clean_temp": true } ================================================ FILE: docs/.vitepress/config.mts ================================================ import { loadEnv, defineConfig } from 'vitepress' import AutoSidebar from 'vite-plugin-vitepress-auto-sidebar'; import GitHubIssuesPlugin from './vitepress-plugin-github-issues.mts'; export default async ({ mode }) => { const env = loadEnv(mode || '', process.cwd()) return defineConfig({ title: "XiaoMusic", description: "XiaoMusic doc", themeConfig: { // https://vitepress.dev/reference/default-theme-config nav: [ { text: 'Guide', link: '/issues' }, { text: 'Admin', link: 'https://x.hanxi.cc' }, ], socialLinks: [ { icon: 'github', link: 'https://github.com/hanxi/xiaomusic' } ], footer: { message: '基于 MIT 许可发布', copyright: `版权所有 © 2023-${new Date().getFullYear()} 涵曦` }, }, sitemap: { hostname: 'https://xdocs.hanxi.cc' }, head: [ ['script', { defer: true, src: 'https://umami.hanxi.cc/script.js', 'data-website-id': '29cca3f5-e420-432b-adc7-8a1325d31c68' }] ], lastUpdated: true, markdown: { lineNumbers: false, // 关闭代码块行号显示 // 自定义 markdown-it 插件 config: (md) => { md.renderer.rules.link_open = (tokens, idx, options, env, self) => { const aIndex = tokens[idx].attrIndex('target'); if (aIndex < 0) { tokens[idx].attrPush(['target', '_self']); // 将默认行为改为不使用 _blank } else { tokens[idx].attrs![aIndex][1] = '_self'; // 替换 _blank 为 _self } return self.renderToken(tokens, idx, options); }; }, }, logLevel: 'warn', vite: { plugins: [ AutoSidebar({ path: '.', collapsed: true, titleFromFile: true, }), GitHubIssuesPlugin({ repo: 'hanxi/xiaomusic', token: env.VITE_GITHUB_ISSUES_TOKEN, replaceRules: [ { baseUrl: 'https://github.com/hanxi/xiaomusic/issues', targetUrl: '/issues', }, ], githubProxy: 'https://gproxy.hanxi.cc/proxy', }), ], } }) } ================================================ FILE: docs/.vitepress/vitepress-plugin-github-issues.mts ================================================ import axios from 'axios'; import fs from 'fs'; import path from 'path'; import type { Plugin } from 'vitepress'; interface ReplaceRule { baseUrl: string; targetUrl: string; } interface GitHubIssuesPluginOptions { repo: string; token: string; replaceRules: ReplaceRule[]; githubProxy: string; } // 增强超时 + 重试 axios.defaults.timeout = 15000; async function fetchAllIssues(repo: string, token: string): Promise
an'zh 无法使用语音播放歌曲,小爱s12a。极空间z4pro。 1. 按照教程,点击播放本地歌曲,提示hostname和设置的端口映射不匹配。映射5678,容器端口8090. 2.网络host模式,能够本地播放,无法使用语音控制,提示“下载app”。日志已上传
既然你映射的5678,为什么你又在那把监听端口改成11999? 我的教程里面全程没有写要修改监听端口
---
### 评论 3 - xiaohuobanhahaha
我没讲清楚。我试了两种极空间的桥接和host模式。桥接模式。我按照教程走的。报错如图
>
> 提到的第二个问题和日志,是我将网络模式改为host的情况,能连上音箱,但是没法使用语音控制。
把外网访问端口改成5678
---
### 评论 5 - xiaohuobanhahaha
可以连接上网页控制了,但是语音控制仍然不行。已经按照FAQ问题集合 #99 重启docker。
这是日志,
[xiaomusic.txt](https://github.com/user-attachments/files/18012369/xiaomusic.txt)
---
### 评论 6 - 52fisher
> 可以连接上网页控制了,但是语音控制仍然不行。已经按照FAQ问题集合 #99 重启docker。 这是日志, [xiaomusic.txt](https://github.com/user-attachments/files/18012369/xiaomusic.txt)
你的设置 hostname='192.168.31.165', port=8090, public_port=5678,
后台的ip 192.168.31.143 检查一下你的地址 有可能是你的ip地址改变了
---
### 评论 7 - xiaohuobanhahaha
> > 可以连接上网页控制了,但是语音控制仍然不行。已经按照FAQ问题集合 #99 重启docker。 这是日志, [xiaomusic.txt](https://github.com/user-attachments/files/18012369/xiaomusic.txt)
>
> 你的设置 hostname='192.168.31.165', port=8090, public_port=5678, 后台的ip 192.168.31.143 检查一下你的地址 有可能是你的ip地址改变了
确实是变了。192.168.31.143是我电脑的ip。 hostname='192.168.31.165'是极空间的。小爱是192.168.31.77。现在我的网络结构是电脑连nas上的istoreos旁路由。nas直连主路由,小爱直连主路由。主路由dhcp都绑定了。 大佬,这种情况该怎么解决呢。所有设置都是默认,没修改哈。
---
### 评论 8 - 52fisher
> 确实是变了。192.168.31.143是我电脑的ip。 hostname='192.168.31.165'是极空间的。小爱是192.168.31.77。现在我的网络结构是电脑连nas上的istoreos旁路由。nas直连主路由,小爱直连主路由。主路由dhcp都绑定了。 大佬,这种情况该怎么解决呢。所有设置都是默认,没修改哈。
容器的网络模式改成bridge试试呢 没解决的话你加群明天再详聊吧
---
### 评论 9 - xiaohuobanhahaha
>
> > > 确实是变了。192.168.31.143是我电脑的ip。 hostname='192.168.31.165'是极空间的。小爱是192.168.31.77。现在我的网络结构是电脑连nas上的istoreos旁路由。nas直连主路由,小爱直连主路由。主路由dhcp都绑定了。 大佬,这种情况该怎么解决呢。所有设置都是默认,没修改哈。
> >
> >
> > 容器的网络模式改成bridge试试呢 没解决的话你加群明天再详聊吧
>
> 辛苦了,这么晚还在回复。我一直用的bridge。大佬,群号多少,不行我明天群里问吧。
[readme](https://github.com/hanxi/xiaomusic?tab=readme-ov-file#-%E8%AE%A8%E8%AE%BA%E5%8C%BA)
---
### 评论 11 - xiaohuobanhahaha
> >
> > > > 确实是变了。192.168.31.143是我电脑的ip。 hostname='192.168.31.165'是极空间的。小爱是192.168.31.77。现在我的网络结构是电脑连nas上的istoreos旁路由。nas直连主路由,小爱直连主路由。主路由dhcp都绑定了。 大佬,这种情况该怎么解决呢。所有设置都是默认,没修改哈。
> > >
> > >
> > > 容器的网络模式改成bridge试试呢 没解决的话你加群明天再详聊吧
> >
> >
> > 辛苦了,这么晚还在回复。我一直用的bridge。大佬,群号多少,不行我明天群里问吧。
>
> [readme](https://github.com/hanxi/xiaomusic?tab=readme-ov-file#-%E8%AE%A8%E8%AE%BA%E5%8C%BA)
已自查解决。问题是账号问题。绑定设备的一定是创建者,不能是管理员。
---
### 评论 12 - McCree2020
这个主题目录不能设置吧?没人遇到这个issue?我原来用的张大妈平台的教程设置的,能用,后来看到这个教程后就修改了后台的路径映射,但是dockers启动正常,网页不能打开提示internal sever error,后来ssh进docker看了日志文件 提示static那个路径有问题,下边的index什么的文件找不到, 删除主题映射以后,重启docker后,网页正常显示了
---
### 评论 13 - 52fisher
> 这个主题目录不能设置吧?没人遇到这个issue?我原来用的张大妈平台的教程设置的,能用,后来看到这个教程后就修改了后台的路径映射,但是dockers启动正常,网页不能打开提示internal sever error,后来ssh进docker看了日志文件 提示static那个路径有问题,下边的index什么的文件找不到, 删除主题映射以后,重启docker后,网页正常显示了
要注意看提示:
装载路径中的 配置文件目录 和 音乐目录 必须进行配置。
其他的路径非必要不要配置,主题目录路径是方便开发调试的时候用的,普通用户不要映射主题目录。我已经把这个提示更新到文档中了
---
### 评论 14 - zxhans
就不能让xiaomusic支持服务器部署吗?服务器部署为啥设备不能读取呢?home assistant 通过xiaomi home assistant都可以读取呀
---
### 评论 15 - hanxi
> 就不能让xiaomusic支持服务器部署吗?服务器部署为啥设备不能读取呢?home assistant 通过xiaomi home assistant都可以读取呀
支持服务器部署的,你需要在服务器上装个浏览器登陆过风控。
---
### 评论 16 - Lunder-R
> > >
> > > > > 确实是变了。192.168.31.143是我电脑的ip。hostname='192.168.31.165'是极空间的。小爱是192.168.31.77。现在我的网络结构是电脑连nas上的istoreos旁路由。nas直连主路由,小爱直连主路由。主dhcp都绑定了。大佬,这种情况该怎么解决呢。所有设置都是默认,没修改哈。
> > > >
> > > >
> > > > 容器的网络模式改成桥试用呢没解决的话你加群明天再详聊吧
> > >
> > >
> > > 辛苦了,今天晚上还在回复。我一直用的桥。大佬,群号多少,不行我明天群里问吧。
> >
> >
> > [自述](https://github.com/hanxi/xiaomusic?tab=readme-ov-file#-%E8%AE%A8%E8%AE%BA%E5%8C%BA)
>
> 自查解决。问题是已账号问题。绑定设备的一定是创建者,不能是管理员。
能不能分享一下解决大概过程,我也是这种情况,谢谢了
---
[Issue 链接](https://github.com/hanxi/xiaomusic/issues/297)
================================================
FILE: docs/issues/312.md
================================================
---
title: 同步网易云歌单
---
# 同步网易云歌单
推广一波 [netease-playlist](https://github.com/qiujie8092916/netease-playlist)
通过网易云音乐歌单 ID,下载歌曲或[生成歌单配置](/issues/269.html)
本程序为独立模块,可 docker 部署,可作为不用[插件](/issues/105.html)的另外实现方式
## 评论
### 评论 1 - Ghost-Sam1222
什么时候可以同步apple music歌单就真好了
---
### 评论 2 - fxchby
试了一下,似乎用不了
---
### 评论 3 - qiujie8092916
> 试了一下,似乎用不了
哪里有问题
---
### 评论 4 - xcysy32
这能实时同步吗🌹
---
[Issue 链接](https://github.com/hanxi/xiaomusic/issues/312)
================================================
FILE: docs/issues/333.md
================================================
---
title: 设置项功能介绍
---
# 设置项功能介绍
- XIAOMUSIC_ACTIVE_CMD 环境变量,对应后台的 【允许唤醒的命令】,用于唤醒口令,配置成'play,random_play',在非播放状态下,只有这两个指令(播放歌曲和随机播放)可以触发,触发后,xiaomusic进入playing状态,其他指令则可以正常触发。具体见 🎵 使用小爱音箱播放音乐,音乐使用 yt-dlp 下载
🏠 GitHub • 📖 文档 • 💬 FAQ • 💭 讨论区
--- > [!TIP] > **新手指南**:初次安装遇到问题请查阅 [💬 FAQ问题集合](https://github.com/hanxi/xiaomusic/issues/99),一般遇到的问题都已经有解决办法。 ## 👋 快速入门指南 已经支持在 web 设置页面配置其他参数,不再需要设置环境变量, docker compose 配置如下(选一个即可): ```yaml services: xiaomusic: image: hanxi/xiaomusic container_name: xiaomusic restart: always ports: - 58090:8090 volumes: - /xiaomusic_music:/app/music - /xiaomusic_conf:/app/conf ``` 🔥 国内: ```yaml services: xiaomusic: image: docker.hanxi.cc/hanxi/xiaomusic container_name: xiaomusic restart: always ports: - 58090:8090 volumes: - /xiaomusic_music:/app/music - /xiaomusic_conf:/app/conf ``` 测试版: ```yaml services: xiaomusic: image: hanxi/xiaomusic:main container_name: xiaomusic restart: always ports: - 58090:8090 volumes: - /xiaomusic_music:/app/music - /xiaomusic_conf:/app/conf ``` 对应的 docker 启动命令如下: ```bash docker run -p 58090:8090 -v /xiaomusic_music:/app/music -v /xiaomusic_conf:/app/conf hanxi/xiaomusic ``` 🔥 国内: ```bash docker run -p 58090:8090 -v /xiaomusic_music:/app/music -v /xiaomusic_conf:/app/conf docker.hanxi.cc/hanxi/xiaomusic ``` 测试版: ``` docker run -p 58090:8090 -v /xiaomusic_music:/app/music -v /xiaomusic_conf:/app/conf hanxi/xiaomusic:main ``` - 其中 conf 目录为配置文件存放目录,music 目录为音乐存放目录,建议分开配置为不同的目录。 - /xiaomusic_music 和 /xiaomusic_conf 是 docker 所在的主机的目录,可以修改为其他目录。如果报错找不到 /xiaomusic_music 目录,可以先执行 `mkdir -p /xiaomusic_{music,conf}` 命令新建目录。 - /app/music 和 /app/conf 是 docker 容器里的目录,不要去修改。 - 58090 是 NAS 本地端口的。8090 是容器端口,不要去修改。 - 后台访问地址为: http://NAS_IP:58090 > [!NOTE] > docker 和 docker compose 二选一即可,启动成功后,在 web 页面可以配置其他参数,带有 `*` 号的配置是必须要配置的,其他的用不上时不用修改。初次配置时需要在页面上输入小米账号和密码保存后才能获取到设备列表。 > [!TIP] > 目前安装步骤已经是最简化了,如果还是嫌安装麻烦,可以微信或者 QQ 约我远程安装,我一般周末和晚上才有时间,需要赞助个辛苦费 :moneybag: 50 元一次。 遇到问题可以去 web 设置页面底部点击【下载日志文件】按钮,然后搜索一下日志文件内容确保里面没有账号密码信息后(有就删除这些敏感信息),然后在提 issues 反馈问题时把下载的日志文件带上。 > [!TIP] > 作者新写了一个更简洁的个人音乐服务器,支持更强的插件扩展
> - 不知道选哪个套餐可以直接买这个最便宜的
## 🎤 功能特性
### 🤐 支持语音口令
#### 基础播放控制
- **播放歌曲** - 播放本地的歌曲
- **播放歌曲+歌名** - 例如:播放歌曲周杰伦晴天
- **上一首** / **下一首** - 切换歌曲
- **关机** / **停止播放** - 停止播放
#### 播放模式
- **单曲循环** - 重复播放当前歌曲
- **全部循环** - 循环播放所有歌曲
- **随机播放** - 随机顺序播放
#### 歌单管理
- **播放歌单+目录名** - 例如:播放歌单其他
- **播放歌单第几个+列表名** - 详见 [#158](https://github.com/hanxi/xiaomusic/issues/158)
- **播放歌单收藏** - 播放收藏歌单
#### 收藏功能
- **加入收藏** - 将当前播放的歌曲加入收藏歌单
- **取消收藏** - 将当前播放的歌曲从收藏歌单移除
> [!TIP]
> **隐藏玩法**:对小爱同学说"播放歌曲小猪佩奇的故事",会先下载小猪佩奇的故事,然后再播放。
## 📦 安装方式
### 方式一:Docker Compose(推荐)
详见 [👋 快速入门指南](#-快速入门指南)
### 方式二:Pip 安装
```shell
# 安装
pip install -U xiaomusic
# 查看帮助
xiaomusic --help
# 启动(使用配置文件)
xiaomusic --config config.json
# 启动(使用默认端口 8090)
xiaomusic
```
> [!NOTE]
> `config.json` 文件可以参考 `config-example.json` 文件配置。详见 [#94](https://github.com/hanxi/xiaomusic/issues/94)
## 👨💻 开发指南
### 🔩 开发环境运行
1. **下载依赖**
```shell
./install_dependencies.sh
```
2. **安装环境**
```shell
pdm install
```
3. **启动服务**
```shell
pdm run xiaomusic.py
```
默认监听端口 8090,使用其他端口请自行修改。
4. **查看 API 文档**
访问 💬 GitHub Issues • 🎮 QQ频道 • 👥 QQ交流群 • 💬 微信群
### 🤝 如何贡献 我们欢迎所有形式的贡献,包括但不限于: - 🐛 **报告 Bug**:在 [Issues](https://github.com/hanxi/xiaomusic/issues) 中提交问题 - 💡 **功能建议**:分享你的想法和建议 - 📝 **改进文档**:帮助完善文档和教程 - 🎨 **前端美化**:优化 Web 控制台界面 - 🔧 **代码贡献**:提交 Pull Request > [!TIP] > 提交代码前请确保运行 `pdm lintfmt` 检查代码规范 ## 📚 相关资源 ### 👉 更多教程 更多功能见 [📝 文档汇总](https://github.com/hanxi/xiaomusic/issues/211) ### 🎨 第三方主题 - [pure 主题 xiaomusicUI](https://github.com/52fisher/xiaomusicUI) - [移动端的播放器主题](https://github.com/52fisher/XMusicPlayer) - [Tailwind主题](https://github.com/clarencejh/xiaomusic) - [SoundScape主题](https://github.com/jhao0413/SoundScape) - [第三方主题](https://github.com/DarrenWen/xiaomusicui) ### 📱 配套应用 - [微信小程序: 卯卯音乐](https://github.com/F-loat/xiaoplayer) - [手机APP: 风花雪乐](https://github.com/jokezc/mi_music) - [JS在线播放插件](https://github.com/boluofan/xiaomusic-online) - [手机APP: HMusic](https://github.com/hpcll/HMusic) - [安卓TV: 肉肉音乐TV](https://github.com/GanHuaLin/rouroumusic-tv) ### ❤️ 致谢 **核心依赖** - [xiaomi](https://www.mi.com/) - 小米智能设备 - [xiaogpt](https://github.com/yihong0618/xiaogpt) - 项目灵感来源 - [MiService](https://github.com/yihong0618/MiService) - 小米服务接口 - [yt-dlp](https://github.com/yt-dlp/yt-dlp) - 音乐下载工具 **开发工具** - [PDM](https://pdm.fming.dev/latest/) - Python 包管理 - [FastAPI](https://fastapi.tiangolo.com/) - Web 框架 - [Umami](https://github.com/umami-software/umami) - 统计分析 - [Sentry](https://github.com/getsentry/sentry) - 报错监控 **参考资料** - [实现原理](https://github.com/yihong0618/gitblog/issues/258) - [awesome-xiaoai](https://github.com/zzz6519003/awesome-xiaoai) **特别感谢** - 所有帮忙调试和测试的朋友 - 所有反馈问题和建议的朋友 - 所有贡献代码和文档的开发者 ## 🚨 免责声明 本项目仅供学习和研究目的,不得用于任何商业活动。用户在使用本项目时应遵守所在地区的法律法规,对于违法使用所导致的后果,本项目及作者不承担任何责任。 本项目可能存在未知的缺陷和风险(包括但不限于设备损坏和账号封禁等),使用者应自行承担使用本项目所产生的所有风险及责任。 作者不保证本项目的准确性、完整性、及时性、可靠性,也不承担任何因使用本项目而产生的任何损失或损害责任。 使用本项目即表示您已阅读并同意本免责声明的全部内容。 ## Star History [](https://star-history.com/#hanxi/xiaomusic&Date) ## 💖 支持项目 如果这个项目对你有帮助,欢迎通过以下方式支持: ### ⭐ Star 项目 点击右上角的 ⭐ Star 按钮,让更多人发现这个项目 ### 💰 赞赏支持 - [💝 爱发电](https://afdian.com/a/imhanxi) - 持续支持项目发展 - 扫码请作者喝杯奶茶 ☕
感谢你的支持!❤️
## License [MIT](https://github.com/hanxi/xiaomusic/blob/main/LICENSE) License © 2024 涵曦 ================================================ FILE: docs/package.json ================================================ { "devDependencies": { "vitepress": "^1.6.4" }, "scripts": { "docs:dev": "vitepress dev --host=0.0.0.0 --port=3030", "docs:build": "vitepress build", "docs:preview": "vitepress preview" }, "dependencies": { "axios": "^1.7.9", "vite-plugin-vitepress-auto-sidebar": "^1.7.0" } } ================================================ FILE: get_release.py ================================================ import json import os import requests # 替换为你的 GitHub 仓库信息 GITHUB_OWNER = "hanxi" GITHUB_REPO = "xiaomusic" GITHUB_API_URL = f"https://api.github.com/repos/{GITHUB_OWNER}/{GITHUB_REPO}/releases" def fetch_releases(): headers = {} github_token = os.getenv("GITHUB_TOKEN") if github_token: headers["Authorization"] = f"token {github_token}" try: response = requests.get(GITHUB_API_URL, headers=headers) response.raise_for_status() return response.json() except requests.RequestException as e: print(f"请求 GitHub API 失败: {e}") return [] def extract_tar_gz_files(releases): versions = [] for release in releases: version = release.get("tag_name") if not version: continue files = [] for asset in release.get("assets", []): if asset.get("name", "").endswith(".tar.gz"): files.append(asset["name"]) if files: versions.append({"version": version, "files": files}) return versions def save_to_json(data, filename="versions.json"): try: with open(filename, "w") as f: json.dump(data, f, indent=4) print(f"数据已保存到 {filename}") except OSError as e: print(f"保存文件失败: {e}") def main(): releases = fetch_releases() if not releases: print("未获取到任何 release 数据") return versions = extract_tar_gz_files(releases) save_to_json(versions, "docs/.vitepress/dist/versions.json") if __name__ == "__main__": main() ================================================ FILE: holiday/2007.json ================================================ { "$schema": "https://raw.githubusercontent.com/NateScarlet/holiday-cn/master/schema.json", "$id": "https://raw.githubusercontent.com/NateScarlet/holiday-cn/master/2007.json", "year": 2007, "papers": [ "http://www.gov.cn/zhengce/zhengceku/2008-03/28/content_1761.htm" ], "days": [ { "name": "元旦", "date": "2006-12-30", "isOffDay": false }, { "name": "元旦", "date": "2006-12-31", "isOffDay": false }, { "name": "元旦", "date": "2007-01-01", "isOffDay": true }, { "name": "元旦", "date": "2007-01-02", "isOffDay": true }, { "name": "元旦", "date": "2007-01-03", "isOffDay": true }, { "name": "春节", "date": "2007-02-17", "isOffDay": false }, { "name": "春节", "date": "2007-02-18", "isOffDay": true }, { "name": "春节", "date": "2007-02-19", "isOffDay": true }, { "name": "春节", "date": "2007-02-20", "isOffDay": true }, { "name": "春节", "date": "2007-02-21", "isOffDay": true }, { "name": "春节", "date": "2007-02-22", "isOffDay": true }, { "name": "春节", "date": "2007-02-23", "isOffDay": true }, { "name": "春节", "date": "2007-02-24", "isOffDay": true }, { "name": "春节", "date": "2007-02-25", "isOffDay": false }, { "name": "“五一”", "date": "2007-04-28", "isOffDay": false }, { "name": "“五一”", "date": "2007-04-29", "isOffDay": false }, { "name": "“五一”", "date": "2007-05-01", "isOffDay": true }, { "name": "“五一”", "date": "2007-05-02", "isOffDay": true }, { "name": "“五一”", "date": "2007-05-03", "isOffDay": true }, { "name": "“五一”", "date": "2007-05-04", "isOffDay": true }, { "name": "“五一”", "date": "2007-05-05", "isOffDay": true }, { "name": "“五一”", "date": "2007-05-06", "isOffDay": true }, { "name": "“五一”", "date": "2007-05-07", "isOffDay": true }, { "name": "“十一”", "date": "2007-09-29", "isOffDay": false }, { "name": "“十一”", "date": "2007-09-30", "isOffDay": false }, { "name": "“十一”", "date": "2007-10-01", "isOffDay": true }, { "name": "“十一”", "date": "2007-10-02", "isOffDay": true }, { "name": "“十一”", "date": "2007-10-03", "isOffDay": true }, { "name": "“十一”", "date": "2007-10-04", "isOffDay": true }, { "name": "“十一”", "date": "2007-10-05", "isOffDay": true }, { "name": "“十一”", "date": "2007-10-06", "isOffDay": true }, { "name": "“十一”", "date": "2007-10-07", "isOffDay": true } ] } ================================================ FILE: holiday/2008.json ================================================ { "$schema": "https://raw.githubusercontent.com/NateScarlet/holiday-cn/master/schema.json", "$id": "https://raw.githubusercontent.com/NateScarlet/holiday-cn/master/2008.json", "year": 2008, "papers": [ "http://www.gov.cn/zhengce/zhengceku/2008-03/28/content_1645.htm" ], "days": [ { "name": "元旦", "date": "2007-12-29", "isOffDay": false }, { "name": "元旦", "date": "2007-12-30", "isOffDay": true }, { "name": "元旦", "date": "2007-12-31", "isOffDay": true }, { "name": "元旦", "date": "2008-01-01", "isOffDay": true }, { "name": "春节", "date": "2008-02-02", "isOffDay": false }, { "name": "春节", "date": "2008-02-03", "isOffDay": false }, { "name": "春节", "date": "2008-02-06", "isOffDay": true }, { "name": "春节", "date": "2008-02-07", "isOffDay": true }, { "name": "春节", "date": "2008-02-08", "isOffDay": true }, { "name": "春节", "date": "2008-02-09", "isOffDay": true }, { "name": "春节", "date": "2008-02-10", "isOffDay": true }, { "name": "春节", "date": "2008-02-11", "isOffDay": true }, { "name": "春节", "date": "2008-02-12", "isOffDay": true }, { "name": "清明节", "date": "2008-04-04", "isOffDay": true }, { "name": "清明节", "date": "2008-04-05", "isOffDay": true }, { "name": "清明节", "date": "2008-04-06", "isOffDay": true }, { "name": "“五一”国际劳动节", "date": "2008-05-01", "isOffDay": true }, { "name": "“五一”国际劳动节", "date": "2008-05-02", "isOffDay": true }, { "name": "“五一”国际劳动节", "date": "2008-05-03", "isOffDay": true }, { "name": "“五一”国际劳动节", "date": "2008-05-04", "isOffDay": false }, { "name": "端午节", "date": "2008-06-07", "isOffDay": true }, { "name": "端午节", "date": "2008-06-08", "isOffDay": true }, { "name": "端午节", "date": "2008-06-09", "isOffDay": true }, { "name": "中秋节", "date": "2008-09-13", "isOffDay": true }, { "name": "中秋节", "date": "2008-09-14", "isOffDay": true }, { "name": "中秋节", "date": "2008-09-15", "isOffDay": true }, { "name": "国庆节", "date": "2008-09-27", "isOffDay": false }, { "name": "国庆节", "date": "2008-09-28", "isOffDay": false }, { "name": "国庆节", "date": "2008-09-29", "isOffDay": true }, { "name": "国庆节", "date": "2008-09-30", "isOffDay": true }, { "name": "国庆节", "date": "2008-10-01", "isOffDay": true }, { "name": "国庆节", "date": "2008-10-02", "isOffDay": true }, { "name": "国庆节", "date": "2008-10-03", "isOffDay": true }, { "name": "国庆节", "date": "2008-10-04", "isOffDay": true }, { "name": "国庆节", "date": "2008-10-05", "isOffDay": true } ] } ================================================ FILE: holiday/2009.json ================================================ { "$schema": "https://raw.githubusercontent.com/NateScarlet/holiday-cn/master/schema.json", "$id": "https://raw.githubusercontent.com/NateScarlet/holiday-cn/master/2009.json", "year": 2009, "papers": [ "http://www.gov.cn/zhengce/zhengceku/2008-12/10/content_1572.htm" ], "days": [ { "name": "元旦", "date": "2009-01-01", "isOffDay": true }, { "name": "元旦", "date": "2009-01-02", "isOffDay": true }, { "name": "元旦", "date": "2009-01-03", "isOffDay": true }, { "name": "元旦", "date": "2009-01-04", "isOffDay": false }, { "name": "春节", "date": "2009-01-24", "isOffDay": false }, { "name": "春节", "date": "2009-01-25", "isOffDay": true }, { "name": "春节", "date": "2009-01-26", "isOffDay": true }, { "name": "春节", "date": "2009-01-27", "isOffDay": true }, { "name": "春节", "date": "2009-01-28", "isOffDay": true }, { "name": "春节", "date": "2009-01-29", "isOffDay": true }, { "name": "春节", "date": "2009-01-30", "isOffDay": true }, { "name": "春节", "date": "2009-01-31", "isOffDay": true }, { "name": "春节", "date": "2009-02-01", "isOffDay": false }, { "name": "清明节", "date": "2009-04-04", "isOffDay": true }, { "name": "清明节", "date": "2009-04-05", "isOffDay": true }, { "name": "清明节", "date": "2009-04-06", "isOffDay": true }, { "name": "劳动节", "date": "2009-05-01", "isOffDay": true }, { "name": "劳动节", "date": "2009-05-02", "isOffDay": true }, { "name": "劳动节", "date": "2009-05-03", "isOffDay": true }, { "name": "端午节", "date": "2009-05-28", "isOffDay": true }, { "name": "端午节", "date": "2009-05-29", "isOffDay": true }, { "name": "端午节", "date": "2009-05-30", "isOffDay": true }, { "name": "端午节", "date": "2009-05-31", "isOffDay": false }, { "name": "国庆节、中秋节", "date": "2009-09-27", "isOffDay": false }, { "name": "国庆节、中秋节", "date": "2009-10-01", "isOffDay": true }, { "name": "国庆节、中秋节", "date": "2009-10-02", "isOffDay": true }, { "name": "国庆节、中秋节", "date": "2009-10-03", "isOffDay": true }, { "name": "国庆节、中秋节", "date": "2009-10-04", "isOffDay": true }, { "name": "国庆节、中秋节", "date": "2009-10-05", "isOffDay": true }, { "name": "国庆节、中秋节", "date": "2009-10-06", "isOffDay": true }, { "name": "国庆节、中秋节", "date": "2009-10-07", "isOffDay": true }, { "name": "国庆节、中秋节", "date": "2009-10-08", "isOffDay": true }, { "name": "国庆节、中秋节", "date": "2009-10-10", "isOffDay": false } ] } ================================================ FILE: holiday/2010.json ================================================ { "$schema": "https://raw.githubusercontent.com/NateScarlet/holiday-cn/master/schema.json", "$id": "https://raw.githubusercontent.com/NateScarlet/holiday-cn/master/2010.json", "year": 2010, "papers": [ "http://www.gov.cn/zhengce/zhengceku/2009-12/08/content_1476.htm" ], "days": [ { "name": "元旦", "date": "2010-01-01", "isOffDay": true }, { "name": "元旦", "date": "2010-01-02", "isOffDay": true }, { "name": "元旦", "date": "2010-01-03", "isOffDay": true }, { "name": "春节", "date": "2010-02-13", "isOffDay": true }, { "name": "春节", "date": "2010-02-14", "isOffDay": true }, { "name": "春节", "date": "2010-02-15", "isOffDay": true }, { "name": "春节", "date": "2010-02-16", "isOffDay": true }, { "name": "春节", "date": "2010-02-17", "isOffDay": true }, { "name": "春节", "date": "2010-02-18", "isOffDay": true }, { "name": "春节", "date": "2010-02-19", "isOffDay": true }, { "name": "春节", "date": "2010-02-20", "isOffDay": false }, { "name": "春节", "date": "2010-02-21", "isOffDay": false }, { "name": "清明节", "date": "2010-04-03", "isOffDay": true }, { "name": "清明节", "date": "2010-04-04", "isOffDay": true }, { "name": "清明节", "date": "2010-04-05", "isOffDay": true }, { "name": "劳动节", "date": "2010-05-01", "isOffDay": true }, { "name": "劳动节", "date": "2010-05-02", "isOffDay": true }, { "name": "劳动节", "date": "2010-05-03", "isOffDay": true }, { "name": "端午节", "date": "2010-06-12", "isOffDay": false }, { "name": "端午节", "date": "2010-06-13", "isOffDay": false }, { "name": "端午节", "date": "2010-06-14", "isOffDay": true }, { "name": "端午节", "date": "2010-06-15", "isOffDay": true }, { "name": "端午节", "date": "2010-06-16", "isOffDay": true }, { "name": "中秋节", "date": "2010-09-19", "isOffDay": false }, { "name": "中秋节", "date": "2010-09-22", "isOffDay": true }, { "name": "中秋节", "date": "2010-09-23", "isOffDay": true }, { "name": "中秋节", "date": "2010-09-24", "isOffDay": true }, { "name": "中秋节", "date": "2010-09-25", "isOffDay": false }, { "name": "国庆节", "date": "2010-09-26", "isOffDay": false }, { "name": "国庆节", "date": "2010-10-01", "isOffDay": true }, { "name": "国庆节", "date": "2010-10-02", "isOffDay": true }, { "name": "国庆节", "date": "2010-10-03", "isOffDay": true }, { "name": "国庆节", "date": "2010-10-04", "isOffDay": true }, { "name": "国庆节", "date": "2010-10-05", "isOffDay": true }, { "name": "国庆节", "date": "2010-10-06", "isOffDay": true }, { "name": "国庆节", "date": "2010-10-07", "isOffDay": true }, { "name": "国庆节", "date": "2010-10-09", "isOffDay": false } ] } ================================================ FILE: holiday/2011.json ================================================ { "$schema": "https://raw.githubusercontent.com/NateScarlet/holiday-cn/master/schema.json", "$id": "https://raw.githubusercontent.com/NateScarlet/holiday-cn/master/2011.json", "year": 2011, "papers": [ "http://www.gov.cn/zhengce/zhengceku/2010-12/10/content_1423.htm" ], "days": [ { "name": "元旦", "date": "2011-01-01", "isOffDay": true }, { "name": "元旦", "date": "2011-01-02", "isOffDay": true }, { "name": "元旦", "date": "2011-01-03", "isOffDay": true }, { "name": "春节", "date": "2011-01-30", "isOffDay": false }, { "name": "春节", "date": "2011-02-02", "isOffDay": true }, { "name": "春节", "date": "2011-02-03", "isOffDay": true }, { "name": "春节", "date": "2011-02-04", "isOffDay": true }, { "name": "春节", "date": "2011-02-05", "isOffDay": true }, { "name": "春节", "date": "2011-02-06", "isOffDay": true }, { "name": "春节", "date": "2011-02-07", "isOffDay": true }, { "name": "春节", "date": "2011-02-08", "isOffDay": true }, { "name": "春节", "date": "2011-02-12", "isOffDay": false }, { "name": "清明节", "date": "2011-04-02", "isOffDay": false }, { "name": "清明节", "date": "2011-04-03", "isOffDay": true }, { "name": "清明节", "date": "2011-04-04", "isOffDay": true }, { "name": "清明节", "date": "2011-04-05", "isOffDay": true }, { "name": "劳动节", "date": "2011-04-30", "isOffDay": true }, { "name": "劳动节", "date": "2011-05-01", "isOffDay": true }, { "name": "劳动节", "date": "2011-05-02", "isOffDay": true }, { "name": "端午节", "date": "2011-06-04", "isOffDay": true }, { "name": "端午节", "date": "2011-06-05", "isOffDay": true }, { "name": "端午节", "date": "2011-06-06", "isOffDay": true }, { "name": "中秋节", "date": "2011-09-10", "isOffDay": true }, { "name": "中秋节", "date": "2011-09-11", "isOffDay": true }, { "name": "中秋节", "date": "2011-09-12", "isOffDay": true }, { "name": "国庆节", "date": "2011-10-01", "isOffDay": true }, { "name": "国庆节", "date": "2011-10-02", "isOffDay": true }, { "name": "国庆节", "date": "2011-10-03", "isOffDay": true }, { "name": "国庆节", "date": "2011-10-04", "isOffDay": true }, { "name": "国庆节", "date": "2011-10-05", "isOffDay": true }, { "name": "国庆节", "date": "2011-10-06", "isOffDay": true }, { "name": "国庆节", "date": "2011-10-07", "isOffDay": true }, { "name": "国庆节", "date": "2011-10-08", "isOffDay": false }, { "name": "国庆节", "date": "2011-10-09", "isOffDay": false } ] } ================================================ FILE: holiday/2012.json ================================================ { "$schema": "https://raw.githubusercontent.com/NateScarlet/holiday-cn/master/schema.json", "$id": "https://raw.githubusercontent.com/NateScarlet/holiday-cn/master/2012.json", "year": 2012, "papers": [ "http://www.gov.cn/zhengce/zhengceku/2011-12/06/content_1411.htm" ], "days": [ { "name": "元旦", "date": "2011-12-31", "isOffDay": false }, { "name": "元旦", "date": "2012-01-01", "isOffDay": true }, { "name": "元旦", "date": "2012-01-02", "isOffDay": true }, { "name": "元旦", "date": "2012-01-03", "isOffDay": true }, { "name": "春节", "date": "2012-01-21", "isOffDay": false }, { "name": "春节", "date": "2012-01-22", "isOffDay": true }, { "name": "春节", "date": "2012-01-23", "isOffDay": true }, { "name": "春节", "date": "2012-01-24", "isOffDay": true }, { "name": "春节", "date": "2012-01-25", "isOffDay": true }, { "name": "春节", "date": "2012-01-26", "isOffDay": true }, { "name": "春节", "date": "2012-01-27", "isOffDay": true }, { "name": "春节", "date": "2012-01-28", "isOffDay": true }, { "name": "春节", "date": "2012-01-29", "isOffDay": false }, { "name": "清明节", "date": "2012-03-31", "isOffDay": false }, { "name": "清明节", "date": "2012-04-01", "isOffDay": false }, { "name": "清明节", "date": "2012-04-02", "isOffDay": true }, { "name": "清明节", "date": "2012-04-03", "isOffDay": true }, { "name": "清明节", "date": "2012-04-04", "isOffDay": true }, { "name": "劳动节", "date": "2012-04-28", "isOffDay": false }, { "name": "劳动节", "date": "2012-04-29", "isOffDay": true }, { "name": "劳动节", "date": "2012-04-30", "isOffDay": true }, { "name": "劳动节", "date": "2012-05-01", "isOffDay": true }, { "name": "端午节", "date": "2012-06-22", "isOffDay": true }, { "name": "端午节", "date": "2012-06-23", "isOffDay": true }, { "name": "端午节", "date": "2012-06-24", "isOffDay": true }, { "name": "中秋节、国庆节", "date": "2012-09-29", "isOffDay": false }, { "name": "中秋节、国庆节", "date": "2012-09-30", "isOffDay": true }, { "name": "中秋节、国庆节", "date": "2012-10-01", "isOffDay": true }, { "name": "中秋节、国庆节", "date": "2012-10-02", "isOffDay": true }, { "name": "中秋节、国庆节", "date": "2012-10-03", "isOffDay": true }, { "name": "中秋节、国庆节", "date": "2012-10-04", "isOffDay": true }, { "name": "中秋节、国庆节", "date": "2012-10-05", "isOffDay": true }, { "name": "中秋节、国庆节", "date": "2012-10-06", "isOffDay": true }, { "name": "中秋节、国庆节", "date": "2012-10-07", "isOffDay": true } ] } ================================================ FILE: holiday/2013.json ================================================ { "$schema": "https://raw.githubusercontent.com/NateScarlet/holiday-cn/master/schema.json", "$id": "https://raw.githubusercontent.com/NateScarlet/holiday-cn/master/2013.json", "year": 2013, "papers": [ "http://www.gov.cn/zhengce/zhengceku/2012-12/10/content_1353.htm" ], "days": [ { "name": "元旦", "date": "2013-01-01", "isOffDay": true }, { "name": "元旦", "date": "2013-01-02", "isOffDay": true }, { "name": "元旦", "date": "2013-01-03", "isOffDay": true }, { "name": "元旦", "date": "2013-01-05", "isOffDay": false }, { "name": "元旦", "date": "2013-01-06", "isOffDay": false }, { "name": "春节", "date": "2013-02-09", "isOffDay": true }, { "name": "春节", "date": "2013-02-10", "isOffDay": true }, { "name": "春节", "date": "2013-02-11", "isOffDay": true }, { "name": "春节", "date": "2013-02-12", "isOffDay": true }, { "name": "春节", "date": "2013-02-13", "isOffDay": true }, { "name": "春节", "date": "2013-02-14", "isOffDay": true }, { "name": "春节", "date": "2013-02-15", "isOffDay": true }, { "name": "春节", "date": "2013-02-16", "isOffDay": false }, { "name": "春节", "date": "2013-02-17", "isOffDay": false }, { "name": "清明节", "date": "2013-04-04", "isOffDay": true }, { "name": "清明节", "date": "2013-04-05", "isOffDay": true }, { "name": "清明节", "date": "2013-04-06", "isOffDay": true }, { "name": "清明节", "date": "2013-04-07", "isOffDay": false }, { "name": "劳动节", "date": "2013-04-27", "isOffDay": false }, { "name": "劳动节", "date": "2013-04-28", "isOffDay": false }, { "name": "劳动节", "date": "2013-04-29", "isOffDay": true }, { "name": "劳动节", "date": "2013-04-30", "isOffDay": true }, { "name": "劳动节", "date": "2013-05-01", "isOffDay": true }, { "name": "端午节", "date": "2013-06-08", "isOffDay": false }, { "name": "端午节", "date": "2013-06-09", "isOffDay": false }, { "name": "端午节", "date": "2013-06-10", "isOffDay": true }, { "name": "端午节", "date": "2013-06-11", "isOffDay": true }, { "name": "端午节", "date": "2013-06-12", "isOffDay": true }, { "name": "中秋节", "date": "2013-09-19", "isOffDay": true }, { "name": "中秋节", "date": "2013-09-20", "isOffDay": true }, { "name": "中秋节", "date": "2013-09-21", "isOffDay": true }, { "name": "中秋节", "date": "2013-09-22", "isOffDay": false }, { "name": "国庆节", "date": "2013-09-29", "isOffDay": false }, { "name": "国庆节", "date": "2013-10-01", "isOffDay": true }, { "name": "国庆节", "date": "2013-10-02", "isOffDay": true }, { "name": "国庆节", "date": "2013-10-03", "isOffDay": true }, { "name": "国庆节", "date": "2013-10-04", "isOffDay": true }, { "name": "国庆节", "date": "2013-10-05", "isOffDay": true }, { "name": "国庆节", "date": "2013-10-06", "isOffDay": true }, { "name": "国庆节", "date": "2013-10-07", "isOffDay": true }, { "name": "国庆节", "date": "2013-10-12", "isOffDay": false } ] } ================================================ FILE: holiday/2014.json ================================================ { "$schema": "https://raw.githubusercontent.com/NateScarlet/holiday-cn/master/schema.json", "$id": "https://raw.githubusercontent.com/NateScarlet/holiday-cn/master/2014.json", "year": 2014, "papers": [ "http://www.gov.cn/zhengce/zhengceku/2014-01/02/content_1194.htm" ], "days": [ { "name": "元旦", "date": "2014-01-01", "isOffDay": true }, { "name": "春节", "date": "2014-01-26", "isOffDay": false }, { "name": "春节", "date": "2014-01-31", "isOffDay": true }, { "name": "春节", "date": "2014-02-01", "isOffDay": true }, { "name": "春节", "date": "2014-02-02", "isOffDay": true }, { "name": "春节", "date": "2014-02-03", "isOffDay": true }, { "name": "春节", "date": "2014-02-04", "isOffDay": true }, { "name": "春节", "date": "2014-02-05", "isOffDay": true }, { "name": "春节", "date": "2014-02-06", "isOffDay": true }, { "name": "春节", "date": "2014-02-08", "isOffDay": false }, { "name": "清明节", "date": "2014-04-05", "isOffDay": true }, { "name": "清明节", "date": "2014-04-07", "isOffDay": true }, { "name": "劳动节", "date": "2014-05-01", "isOffDay": true }, { "name": "劳动节", "date": "2014-05-02", "isOffDay": true }, { "name": "劳动节", "date": "2014-05-03", "isOffDay": true }, { "name": "劳动节", "date": "2014-05-04", "isOffDay": false }, { "name": "端午节", "date": "2014-06-02", "isOffDay": true }, { "name": "中秋节", "date": "2014-09-08", "isOffDay": true }, { "name": "国庆节", "date": "2014-09-28", "isOffDay": false }, { "name": "国庆节", "date": "2014-10-01", "isOffDay": true }, { "name": "国庆节", "date": "2014-10-02", "isOffDay": true }, { "name": "国庆节", "date": "2014-10-03", "isOffDay": true }, { "name": "国庆节", "date": "2014-10-04", "isOffDay": true }, { "name": "国庆节", "date": "2014-10-05", "isOffDay": true }, { "name": "国庆节", "date": "2014-10-06", "isOffDay": true }, { "name": "国庆节", "date": "2014-10-07", "isOffDay": true }, { "name": "国庆节", "date": "2014-10-11", "isOffDay": false } ] } ================================================ FILE: holiday/2015.json ================================================ { "$schema": "https://raw.githubusercontent.com/NateScarlet/holiday-cn/master/schema.json", "$id": "https://raw.githubusercontent.com/NateScarlet/holiday-cn/master/2015.json", "year": 2015, "papers": [ "http://www.gov.cn/zhengce/zhengceku/2014-12/16/content_9302.htm", "http://www.gov.cn/zhengce/zhengceku/2015-05/13/content_9742.htm" ], "days": [ { "name": "元旦", "date": "2015-01-01", "isOffDay": true }, { "name": "元旦", "date": "2015-01-02", "isOffDay": true }, { "name": "元旦", "date": "2015-01-03", "isOffDay": true }, { "name": "元旦", "date": "2015-01-04", "isOffDay": false }, { "name": "春节", "date": "2015-02-15", "isOffDay": false }, { "name": "春节", "date": "2015-02-18", "isOffDay": true }, { "name": "春节", "date": "2015-02-19", "isOffDay": true }, { "name": "春节", "date": "2015-02-20", "isOffDay": true }, { "name": "春节", "date": "2015-02-21", "isOffDay": true }, { "name": "春节", "date": "2015-02-22", "isOffDay": true }, { "name": "春节", "date": "2015-02-23", "isOffDay": true }, { "name": "春节", "date": "2015-02-24", "isOffDay": true }, { "name": "春节", "date": "2015-02-28", "isOffDay": false }, { "name": "清明节", "date": "2015-04-05", "isOffDay": true }, { "name": "清明节", "date": "2015-04-06", "isOffDay": true }, { "name": "劳动节", "date": "2015-05-01", "isOffDay": true }, { "name": "端午节", "date": "2015-06-20", "isOffDay": true }, { "name": "端午节", "date": "2015-06-22", "isOffDay": true }, { "name": "抗日战争暨世界反法西斯战争胜利70周年纪念日", "date": "2015-09-03", "isOffDay": true }, { "name": "抗日战争暨世界反法西斯战争胜利70周年纪念日", "date": "2015-09-04", "isOffDay": true }, { "name": "抗日战争暨世界反法西斯战争胜利70周年纪念日", "date": "2015-09-05", "isOffDay": true }, { "name": "抗日战争暨世界反法西斯战争胜利70周年纪念日", "date": "2015-09-06", "isOffDay": false }, { "name": "中秋节", "date": "2015-09-27", "isOffDay": true }, { "name": "国庆节", "date": "2015-10-01", "isOffDay": true }, { "name": "国庆节", "date": "2015-10-02", "isOffDay": true }, { "name": "国庆节", "date": "2015-10-03", "isOffDay": true }, { "name": "国庆节", "date": "2015-10-04", "isOffDay": true }, { "name": "国庆节", "date": "2015-10-05", "isOffDay": true }, { "name": "国庆节", "date": "2015-10-06", "isOffDay": true }, { "name": "国庆节", "date": "2015-10-07", "isOffDay": true }, { "name": "国庆节", "date": "2015-10-10", "isOffDay": false } ] } ================================================ FILE: holiday/2016.json ================================================ { "$schema": "https://raw.githubusercontent.com/NateScarlet/holiday-cn/master/schema.json", "$id": "https://raw.githubusercontent.com/NateScarlet/holiday-cn/master/2016.json", "year": 2016, "papers": [ "http://www.gov.cn/zhengce/zhengceku/2015-12/10/content_10394.htm" ], "days": [ { "name": "元旦", "date": "2016-01-01", "isOffDay": true }, { "name": "春节", "date": "2016-02-06", "isOffDay": false }, { "name": "春节", "date": "2016-02-07", "isOffDay": true }, { "name": "春节", "date": "2016-02-08", "isOffDay": true }, { "name": "春节", "date": "2016-02-09", "isOffDay": true }, { "name": "春节", "date": "2016-02-10", "isOffDay": true }, { "name": "春节", "date": "2016-02-11", "isOffDay": true }, { "name": "春节", "date": "2016-02-12", "isOffDay": true }, { "name": "春节", "date": "2016-02-13", "isOffDay": true }, { "name": "春节", "date": "2016-02-14", "isOffDay": false }, { "name": "清明节", "date": "2016-04-04", "isOffDay": true }, { "name": "劳动节", "date": "2016-05-01", "isOffDay": true }, { "name": "劳动节", "date": "2016-05-02", "isOffDay": true }, { "name": "端午节", "date": "2016-06-09", "isOffDay": true }, { "name": "端午节", "date": "2016-06-10", "isOffDay": true }, { "name": "端午节", "date": "2016-06-11", "isOffDay": true }, { "name": "端午节", "date": "2016-06-12", "isOffDay": false }, { "name": "中秋节", "date": "2016-09-15", "isOffDay": true }, { "name": "中秋节", "date": "2016-09-16", "isOffDay": true }, { "name": "中秋节", "date": "2016-09-17", "isOffDay": true }, { "name": "中秋节", "date": "2016-09-18", "isOffDay": false }, { "name": "国庆节", "date": "2016-10-01", "isOffDay": true }, { "name": "国庆节", "date": "2016-10-02", "isOffDay": true }, { "name": "国庆节", "date": "2016-10-03", "isOffDay": true }, { "name": "国庆节", "date": "2016-10-04", "isOffDay": true }, { "name": "国庆节", "date": "2016-10-05", "isOffDay": true }, { "name": "国庆节", "date": "2016-10-06", "isOffDay": true }, { "name": "国庆节", "date": "2016-10-07", "isOffDay": true }, { "name": "国庆节", "date": "2016-10-08", "isOffDay": false }, { "name": "国庆节", "date": "2016-10-09", "isOffDay": false } ] } ================================================ FILE: holiday/2017.json ================================================ { "$schema": "https://raw.githubusercontent.com/NateScarlet/holiday-cn/master/schema.json", "$id": "https://raw.githubusercontent.com/NateScarlet/holiday-cn/master/2017.json", "year": 2017, "papers": [ "http://www.gov.cn/zhengce/zhengceku/2016-12/01/content_5141603.htm" ], "days": [ { "name": "元旦", "date": "2017-01-01", "isOffDay": true }, { "name": "元旦", "date": "2017-01-02", "isOffDay": true }, { "name": "春节", "date": "2017-01-22", "isOffDay": false }, { "name": "春节", "date": "2017-01-27", "isOffDay": true }, { "name": "春节", "date": "2017-01-28", "isOffDay": true }, { "name": "春节", "date": "2017-01-29", "isOffDay": true }, { "name": "春节", "date": "2017-01-30", "isOffDay": true }, { "name": "春节", "date": "2017-01-31", "isOffDay": true }, { "name": "春节", "date": "2017-02-01", "isOffDay": true }, { "name": "春节", "date": "2017-02-02", "isOffDay": true }, { "name": "春节", "date": "2017-02-04", "isOffDay": false }, { "name": "清明节", "date": "2017-04-01", "isOffDay": false }, { "name": "清明节", "date": "2017-04-02", "isOffDay": true }, { "name": "清明节", "date": "2017-04-03", "isOffDay": true }, { "name": "清明节", "date": "2017-04-04", "isOffDay": true }, { "name": "劳动节", "date": "2017-05-01", "isOffDay": true }, { "name": "端午节", "date": "2017-05-27", "isOffDay": false }, { "name": "端午节", "date": "2017-05-28", "isOffDay": true }, { "name": "端午节", "date": "2017-05-29", "isOffDay": true }, { "name": "端午节", "date": "2017-05-30", "isOffDay": true }, { "name": "中秋节、国庆节", "date": "2017-09-30", "isOffDay": false }, { "name": "中秋节、国庆节", "date": "2017-10-01", "isOffDay": true }, { "name": "中秋节、国庆节", "date": "2017-10-02", "isOffDay": true }, { "name": "中秋节、国庆节", "date": "2017-10-03", "isOffDay": true }, { "name": "中秋节、国庆节", "date": "2017-10-04", "isOffDay": true }, { "name": "中秋节、国庆节", "date": "2017-10-05", "isOffDay": true }, { "name": "中秋节、国庆节", "date": "2017-10-06", "isOffDay": true }, { "name": "中秋节、国庆节", "date": "2017-10-07", "isOffDay": true }, { "name": "中秋节、国庆节", "date": "2017-10-08", "isOffDay": true } ] } ================================================ FILE: holiday/2018.json ================================================ { "$schema": "https://raw.githubusercontent.com/NateScarlet/holiday-cn/master/schema.json", "$id": "https://raw.githubusercontent.com/NateScarlet/holiday-cn/master/2018.json", "year": 2018, "papers": [ "http://www.gov.cn/zhengce/zhengceku/2017-11/30/content_5243579.htm" ], "days": [ { "name": "元旦", "date": "2018-01-01", "isOffDay": true }, { "name": "春节", "date": "2018-02-11", "isOffDay": false }, { "name": "春节", "date": "2018-02-15", "isOffDay": true }, { "name": "春节", "date": "2018-02-16", "isOffDay": true }, { "name": "春节", "date": "2018-02-17", "isOffDay": true }, { "name": "春节", "date": "2018-02-18", "isOffDay": true }, { "name": "春节", "date": "2018-02-19", "isOffDay": true }, { "name": "春节", "date": "2018-02-20", "isOffDay": true }, { "name": "春节", "date": "2018-02-21", "isOffDay": true }, { "name": "春节", "date": "2018-02-24", "isOffDay": false }, { "name": "清明节", "date": "2018-04-05", "isOffDay": true }, { "name": "清明节", "date": "2018-04-06", "isOffDay": true }, { "name": "清明节", "date": "2018-04-07", "isOffDay": true }, { "name": "清明节", "date": "2018-04-08", "isOffDay": false }, { "name": "劳动节", "date": "2018-04-28", "isOffDay": false }, { "name": "劳动节", "date": "2018-04-29", "isOffDay": true }, { "name": "劳动节", "date": "2018-04-30", "isOffDay": true }, { "name": "劳动节", "date": "2018-05-01", "isOffDay": true }, { "name": "端午节", "date": "2018-06-18", "isOffDay": true }, { "name": "中秋节", "date": "2018-09-24", "isOffDay": true }, { "name": "国庆节", "date": "2018-09-29", "isOffDay": false }, { "name": "国庆节", "date": "2018-09-30", "isOffDay": false }, { "name": "国庆节", "date": "2018-10-01", "isOffDay": true }, { "name": "国庆节", "date": "2018-10-02", "isOffDay": true }, { "name": "国庆节", "date": "2018-10-03", "isOffDay": true }, { "name": "国庆节", "date": "2018-10-04", "isOffDay": true }, { "name": "国庆节", "date": "2018-10-05", "isOffDay": true }, { "name": "国庆节", "date": "2018-10-06", "isOffDay": true }, { "name": "国庆节", "date": "2018-10-07", "isOffDay": true } ] } ================================================ FILE: holiday/2019.json ================================================ { "$schema": "https://raw.githubusercontent.com/NateScarlet/holiday-cn/master/schema.json", "$id": "https://raw.githubusercontent.com/NateScarlet/holiday-cn/master/2019.json", "year": 2019, "papers": [ "http://www.gov.cn/zhengce/zhengceku/2018-12/06/content_5346276.htm", "http://www.gov.cn/zhengce/zhengceku/2019-03/22/content_5375877.htm" ], "days": [ { "name": "元旦", "date": "2018-12-29", "isOffDay": false }, { "name": "元旦", "date": "2018-12-30", "isOffDay": true }, { "name": "元旦", "date": "2018-12-31", "isOffDay": true }, { "name": "元旦", "date": "2019-01-01", "isOffDay": true }, { "name": "春节", "date": "2019-02-02", "isOffDay": false }, { "name": "春节", "date": "2019-02-03", "isOffDay": false }, { "name": "春节", "date": "2019-02-04", "isOffDay": true }, { "name": "春节", "date": "2019-02-05", "isOffDay": true }, { "name": "春节", "date": "2019-02-06", "isOffDay": true }, { "name": "春节", "date": "2019-02-07", "isOffDay": true }, { "name": "春节", "date": "2019-02-08", "isOffDay": true }, { "name": "春节", "date": "2019-02-09", "isOffDay": true }, { "name": "春节", "date": "2019-02-10", "isOffDay": true }, { "name": "清明节", "date": "2019-04-05", "isOffDay": true }, { "name": "劳动节", "date": "2019-04-28", "isOffDay": false }, { "name": "劳动节", "date": "2019-05-01", "isOffDay": true }, { "name": "劳动节", "date": "2019-05-02", "isOffDay": true }, { "name": "劳动节", "date": "2019-05-03", "isOffDay": true }, { "name": "劳动节", "date": "2019-05-04", "isOffDay": true }, { "name": "劳动节", "date": "2019-05-05", "isOffDay": false }, { "name": "端午节", "date": "2019-06-07", "isOffDay": true }, { "name": "中秋节", "date": "2019-09-13", "isOffDay": true }, { "name": "国庆节", "date": "2019-09-29", "isOffDay": false }, { "name": "国庆节", "date": "2019-10-01", "isOffDay": true }, { "name": "国庆节", "date": "2019-10-02", "isOffDay": true }, { "name": "国庆节", "date": "2019-10-03", "isOffDay": true }, { "name": "国庆节", "date": "2019-10-04", "isOffDay": true }, { "name": "国庆节", "date": "2019-10-05", "isOffDay": true }, { "name": "国庆节", "date": "2019-10-06", "isOffDay": true }, { "name": "国庆节", "date": "2019-10-07", "isOffDay": true }, { "name": "国庆节", "date": "2019-10-12", "isOffDay": false } ] } ================================================ FILE: holiday/2020.json ================================================ { "$schema": "https://raw.githubusercontent.com/NateScarlet/holiday-cn/master/schema.json", "$id": "https://raw.githubusercontent.com/NateScarlet/holiday-cn/master/2020.json", "year": 2020, "papers": [ "http://www.gov.cn/zhengce/zhengceku/2019-11/21/content_5454164.htm", "http://www.gov.cn/zhengce/zhengceku/2020-01/27/content_5472352.htm" ], "days": [ { "name": "元旦", "date": "2020-01-01", "isOffDay": true }, { "name": "春节", "date": "2020-01-19", "isOffDay": false }, { "name": "春节", "date": "2020-01-24", "isOffDay": true }, { "name": "春节", "date": "2020-01-25", "isOffDay": true }, { "name": "春节", "date": "2020-01-26", "isOffDay": true }, { "name": "春节", "date": "2020-01-27", "isOffDay": true }, { "name": "春节", "date": "2020-01-28", "isOffDay": true }, { "name": "春节", "date": "2020-01-29", "isOffDay": true }, { "name": "春节", "date": "2020-01-30", "isOffDay": true }, { "name": "春节", "date": "2020-01-31", "isOffDay": true }, { "name": "春节", "date": "2020-02-01", "isOffDay": true }, { "name": "春节", "date": "2020-02-02", "isOffDay": true }, { "name": "春节", "date": "2020-02-03", "isOffDay": false }, { "name": "清明节", "date": "2020-04-04", "isOffDay": true }, { "name": "清明节", "date": "2020-04-05", "isOffDay": true }, { "name": "清明节", "date": "2020-04-06", "isOffDay": true }, { "name": "劳动节", "date": "2020-04-26", "isOffDay": false }, { "name": "劳动节", "date": "2020-05-01", "isOffDay": true }, { "name": "劳动节", "date": "2020-05-02", "isOffDay": true }, { "name": "劳动节", "date": "2020-05-03", "isOffDay": true }, { "name": "劳动节", "date": "2020-05-04", "isOffDay": true }, { "name": "劳动节", "date": "2020-05-05", "isOffDay": true }, { "name": "劳动节", "date": "2020-05-09", "isOffDay": false }, { "name": "端午节", "date": "2020-06-25", "isOffDay": true }, { "name": "端午节", "date": "2020-06-26", "isOffDay": true }, { "name": "端午节", "date": "2020-06-27", "isOffDay": true }, { "name": "端午节", "date": "2020-06-28", "isOffDay": false }, { "name": "国庆节、中秋节", "date": "2020-09-27", "isOffDay": false }, { "name": "国庆节、中秋节", "date": "2020-10-01", "isOffDay": true }, { "name": "国庆节、中秋节", "date": "2020-10-02", "isOffDay": true }, { "name": "国庆节、中秋节", "date": "2020-10-03", "isOffDay": true }, { "name": "国庆节、中秋节", "date": "2020-10-04", "isOffDay": true }, { "name": "国庆节、中秋节", "date": "2020-10-05", "isOffDay": true }, { "name": "国庆节、中秋节", "date": "2020-10-06", "isOffDay": true }, { "name": "国庆节、中秋节", "date": "2020-10-07", "isOffDay": true }, { "name": "国庆节、中秋节", "date": "2020-10-08", "isOffDay": true }, { "name": "国庆节、中秋节", "date": "2020-10-10", "isOffDay": false } ] } ================================================ FILE: holiday/2021.json ================================================ { "$schema": "https://raw.githubusercontent.com/NateScarlet/holiday-cn/master/schema.json", "$id": "https://raw.githubusercontent.com/NateScarlet/holiday-cn/master/2021.json", "year": 2021, "papers": [ "http://www.gov.cn/zhengce/zhengceku/2020-11/25/content_5564127.htm" ], "days": [ { "name": "元旦", "date": "2021-01-01", "isOffDay": true }, { "name": "元旦", "date": "2021-01-02", "isOffDay": true }, { "name": "元旦", "date": "2021-01-03", "isOffDay": true }, { "name": "春节", "date": "2021-02-07", "isOffDay": false }, { "name": "春节", "date": "2021-02-11", "isOffDay": true }, { "name": "春节", "date": "2021-02-12", "isOffDay": true }, { "name": "春节", "date": "2021-02-13", "isOffDay": true }, { "name": "春节", "date": "2021-02-14", "isOffDay": true }, { "name": "春节", "date": "2021-02-15", "isOffDay": true }, { "name": "春节", "date": "2021-02-16", "isOffDay": true }, { "name": "春节", "date": "2021-02-17", "isOffDay": true }, { "name": "春节", "date": "2021-02-20", "isOffDay": false }, { "name": "清明节", "date": "2021-04-03", "isOffDay": true }, { "name": "清明节", "date": "2021-04-04", "isOffDay": true }, { "name": "清明节", "date": "2021-04-05", "isOffDay": true }, { "name": "劳动节", "date": "2021-04-25", "isOffDay": false }, { "name": "劳动节", "date": "2021-05-01", "isOffDay": true }, { "name": "劳动节", "date": "2021-05-02", "isOffDay": true }, { "name": "劳动节", "date": "2021-05-03", "isOffDay": true }, { "name": "劳动节", "date": "2021-05-04", "isOffDay": true }, { "name": "劳动节", "date": "2021-05-05", "isOffDay": true }, { "name": "劳动节", "date": "2021-05-08", "isOffDay": false }, { "name": "端午节", "date": "2021-06-12", "isOffDay": true }, { "name": "端午节", "date": "2021-06-13", "isOffDay": true }, { "name": "端午节", "date": "2021-06-14", "isOffDay": true }, { "name": "中秋节", "date": "2021-09-18", "isOffDay": false }, { "name": "中秋节", "date": "2021-09-19", "isOffDay": true }, { "name": "中秋节", "date": "2021-09-20", "isOffDay": true }, { "name": "中秋节", "date": "2021-09-21", "isOffDay": true }, { "name": "国庆节", "date": "2021-09-26", "isOffDay": false }, { "name": "国庆节", "date": "2021-10-01", "isOffDay": true }, { "name": "国庆节", "date": "2021-10-02", "isOffDay": true }, { "name": "国庆节", "date": "2021-10-03", "isOffDay": true }, { "name": "国庆节", "date": "2021-10-04", "isOffDay": true }, { "name": "国庆节", "date": "2021-10-05", "isOffDay": true }, { "name": "国庆节", "date": "2021-10-06", "isOffDay": true }, { "name": "国庆节", "date": "2021-10-07", "isOffDay": true }, { "name": "国庆节", "date": "2021-10-09", "isOffDay": false } ] } ================================================ FILE: holiday/2022.json ================================================ { "$schema": "https://raw.githubusercontent.com/NateScarlet/holiday-cn/master/schema.json", "$id": "https://raw.githubusercontent.com/NateScarlet/holiday-cn/master/2022.json", "year": 2022, "papers": [ "http://www.gov.cn/zhengce/zhengceku/2021-10/25/content_5644835.htm" ], "days": [ { "name": "元旦", "date": "2022-01-01", "isOffDay": true }, { "name": "元旦", "date": "2022-01-02", "isOffDay": true }, { "name": "元旦", "date": "2022-01-03", "isOffDay": true }, { "name": "春节", "date": "2022-01-29", "isOffDay": false }, { "name": "春节", "date": "2022-01-30", "isOffDay": false }, { "name": "春节", "date": "2022-01-31", "isOffDay": true }, { "name": "春节", "date": "2022-02-01", "isOffDay": true }, { "name": "春节", "date": "2022-02-02", "isOffDay": true }, { "name": "春节", "date": "2022-02-03", "isOffDay": true }, { "name": "春节", "date": "2022-02-04", "isOffDay": true }, { "name": "春节", "date": "2022-02-05", "isOffDay": true }, { "name": "春节", "date": "2022-02-06", "isOffDay": true }, { "name": "清明节", "date": "2022-04-02", "isOffDay": false }, { "name": "清明节", "date": "2022-04-03", "isOffDay": true }, { "name": "清明节", "date": "2022-04-04", "isOffDay": true }, { "name": "清明节", "date": "2022-04-05", "isOffDay": true }, { "name": "劳动节", "date": "2022-04-24", "isOffDay": false }, { "name": "劳动节", "date": "2022-04-30", "isOffDay": true }, { "name": "劳动节", "date": "2022-05-01", "isOffDay": true }, { "name": "劳动节", "date": "2022-05-02", "isOffDay": true }, { "name": "劳动节", "date": "2022-05-03", "isOffDay": true }, { "name": "劳动节", "date": "2022-05-04", "isOffDay": true }, { "name": "劳动节", "date": "2022-05-07", "isOffDay": false }, { "name": "端午节", "date": "2022-06-03", "isOffDay": true }, { "name": "端午节", "date": "2022-06-04", "isOffDay": true }, { "name": "端午节", "date": "2022-06-05", "isOffDay": true }, { "name": "中秋节", "date": "2022-09-10", "isOffDay": true }, { "name": "中秋节", "date": "2022-09-11", "isOffDay": true }, { "name": "中秋节", "date": "2022-09-12", "isOffDay": true }, { "name": "国庆节", "date": "2022-10-01", "isOffDay": true }, { "name": "国庆节", "date": "2022-10-02", "isOffDay": true }, { "name": "国庆节", "date": "2022-10-03", "isOffDay": true }, { "name": "国庆节", "date": "2022-10-04", "isOffDay": true }, { "name": "国庆节", "date": "2022-10-05", "isOffDay": true }, { "name": "国庆节", "date": "2022-10-06", "isOffDay": true }, { "name": "国庆节", "date": "2022-10-07", "isOffDay": true }, { "name": "国庆节", "date": "2022-10-08", "isOffDay": false }, { "name": "国庆节", "date": "2022-10-09", "isOffDay": false } ] } ================================================ FILE: holiday/2023.json ================================================ { "$schema": "https://raw.githubusercontent.com/NateScarlet/holiday-cn/master/schema.json", "$id": "https://raw.githubusercontent.com/NateScarlet/holiday-cn/master/2023.json", "year": 2023, "papers": [ "http://www.gov.cn/zhengce/zhengceku/2022-12/08/content_5730844.htm" ], "days": [ { "name": "元旦", "date": "2022-12-31", "isOffDay": true }, { "name": "元旦", "date": "2023-01-01", "isOffDay": true }, { "name": "元旦", "date": "2023-01-02", "isOffDay": true }, { "name": "春节", "date": "2023-01-21", "isOffDay": true }, { "name": "春节", "date": "2023-01-22", "isOffDay": true }, { "name": "春节", "date": "2023-01-23", "isOffDay": true }, { "name": "春节", "date": "2023-01-24", "isOffDay": true }, { "name": "春节", "date": "2023-01-25", "isOffDay": true }, { "name": "春节", "date": "2023-01-26", "isOffDay": true }, { "name": "春节", "date": "2023-01-27", "isOffDay": true }, { "name": "春节", "date": "2023-01-28", "isOffDay": false }, { "name": "春节", "date": "2023-01-29", "isOffDay": false }, { "name": "清明节", "date": "2023-04-05", "isOffDay": true }, { "name": "劳动节", "date": "2023-04-23", "isOffDay": false }, { "name": "劳动节", "date": "2023-04-29", "isOffDay": true }, { "name": "劳动节", "date": "2023-04-30", "isOffDay": true }, { "name": "劳动节", "date": "2023-05-01", "isOffDay": true }, { "name": "劳动节", "date": "2023-05-02", "isOffDay": true }, { "name": "劳动节", "date": "2023-05-03", "isOffDay": true }, { "name": "劳动节", "date": "2023-05-06", "isOffDay": false }, { "name": "端午节", "date": "2023-06-22", "isOffDay": true }, { "name": "端午节", "date": "2023-06-23", "isOffDay": true }, { "name": "端午节", "date": "2023-06-24", "isOffDay": true }, { "name": "端午节", "date": "2023-06-25", "isOffDay": false }, { "name": "中秋节、国庆节", "date": "2023-09-29", "isOffDay": true }, { "name": "中秋节、国庆节", "date": "2023-09-30", "isOffDay": true }, { "name": "中秋节、国庆节", "date": "2023-10-01", "isOffDay": true }, { "name": "中秋节、国庆节", "date": "2023-10-02", "isOffDay": true }, { "name": "中秋节、国庆节", "date": "2023-10-03", "isOffDay": true }, { "name": "中秋节、国庆节", "date": "2023-10-04", "isOffDay": true }, { "name": "中秋节、国庆节", "date": "2023-10-05", "isOffDay": true }, { "name": "中秋节、国庆节", "date": "2023-10-06", "isOffDay": true }, { "name": "中秋节、国庆节", "date": "2023-10-07", "isOffDay": false }, { "name": "中秋节、国庆节", "date": "2023-10-08", "isOffDay": false } ] } ================================================ FILE: holiday/2024.json ================================================ { "$schema": "https://raw.githubusercontent.com/NateScarlet/holiday-cn/master/schema.json", "$id": "https://raw.githubusercontent.com/NateScarlet/holiday-cn/master/2024.json", "year": 2024, "papers": [ "https://www.gov.cn/zhengce/zhengceku/202310/content_6911528.htm" ], "days": [ { "name": "元旦", "date": "2024-01-01", "isOffDay": true }, { "name": "春节", "date": "2024-02-04", "isOffDay": false }, { "name": "春节", "date": "2024-02-10", "isOffDay": true }, { "name": "春节", "date": "2024-02-11", "isOffDay": true }, { "name": "春节", "date": "2024-02-12", "isOffDay": true }, { "name": "春节", "date": "2024-02-13", "isOffDay": true }, { "name": "春节", "date": "2024-02-14", "isOffDay": true }, { "name": "春节", "date": "2024-02-15", "isOffDay": true }, { "name": "春节", "date": "2024-02-16", "isOffDay": true }, { "name": "春节", "date": "2024-02-17", "isOffDay": true }, { "name": "春节", "date": "2024-02-18", "isOffDay": false }, { "name": "清明节", "date": "2024-04-04", "isOffDay": true }, { "name": "清明节", "date": "2024-04-05", "isOffDay": true }, { "name": "清明节", "date": "2024-04-06", "isOffDay": true }, { "name": "清明节", "date": "2024-04-07", "isOffDay": false }, { "name": "劳动节", "date": "2024-04-28", "isOffDay": false }, { "name": "劳动节", "date": "2024-05-01", "isOffDay": true }, { "name": "劳动节", "date": "2024-05-02", "isOffDay": true }, { "name": "劳动节", "date": "2024-05-03", "isOffDay": true }, { "name": "劳动节", "date": "2024-05-04", "isOffDay": true }, { "name": "劳动节", "date": "2024-05-05", "isOffDay": true }, { "name": "劳动节", "date": "2024-05-11", "isOffDay": false }, { "name": "端午节", "date": "2024-06-10", "isOffDay": true }, { "name": "中秋节", "date": "2024-09-14", "isOffDay": false }, { "name": "中秋节", "date": "2024-09-15", "isOffDay": true }, { "name": "中秋节", "date": "2024-09-16", "isOffDay": true }, { "name": "中秋节", "date": "2024-09-17", "isOffDay": true }, { "name": "国庆节", "date": "2024-09-29", "isOffDay": false }, { "name": "国庆节", "date": "2024-10-01", "isOffDay": true }, { "name": "国庆节", "date": "2024-10-02", "isOffDay": true }, { "name": "国庆节", "date": "2024-10-03", "isOffDay": true }, { "name": "国庆节", "date": "2024-10-04", "isOffDay": true }, { "name": "国庆节", "date": "2024-10-05", "isOffDay": true }, { "name": "国庆节", "date": "2024-10-06", "isOffDay": true }, { "name": "国庆节", "date": "2024-10-07", "isOffDay": true }, { "name": "国庆节", "date": "2024-10-12", "isOffDay": false } ] } ================================================ FILE: holiday/2025.json ================================================ { "$schema": "https://raw.githubusercontent.com/NateScarlet/holiday-cn/master/schema.json", "$id": "https://raw.githubusercontent.com/NateScarlet/holiday-cn/master/2025.json", "year": 2025, "papers": [ "https://www.gov.cn/zhengce/zhengceku/202411/content_6986383.htm" ], "days": [ { "name": "元旦", "date": "2025-01-01", "isOffDay": true }, { "name": "春节", "date": "2025-01-26", "isOffDay": false }, { "name": "春节", "date": "2025-01-28", "isOffDay": true }, { "name": "春节", "date": "2025-01-29", "isOffDay": true }, { "name": "春节", "date": "2025-01-30", "isOffDay": true }, { "name": "春节", "date": "2025-01-31", "isOffDay": true }, { "name": "春节", "date": "2025-02-01", "isOffDay": true }, { "name": "春节", "date": "2025-02-02", "isOffDay": true }, { "name": "春节", "date": "2025-02-03", "isOffDay": true }, { "name": "春节", "date": "2025-02-04", "isOffDay": true }, { "name": "春节", "date": "2025-02-08", "isOffDay": false }, { "name": "清明节", "date": "2025-04-04", "isOffDay": true }, { "name": "清明节", "date": "2025-04-05", "isOffDay": true }, { "name": "清明节", "date": "2025-04-06", "isOffDay": true }, { "name": "劳动节", "date": "2025-04-27", "isOffDay": false }, { "name": "劳动节", "date": "2025-05-01", "isOffDay": true }, { "name": "劳动节", "date": "2025-05-02", "isOffDay": true }, { "name": "劳动节", "date": "2025-05-03", "isOffDay": true }, { "name": "劳动节", "date": "2025-05-04", "isOffDay": true }, { "name": "劳动节", "date": "2025-05-05", "isOffDay": true }, { "name": "端午节", "date": "2025-05-31", "isOffDay": true }, { "name": "端午节", "date": "2025-06-01", "isOffDay": true }, { "name": "端午节", "date": "2025-06-02", "isOffDay": true }, { "name": "国庆节、中秋节", "date": "2025-09-28", "isOffDay": false }, { "name": "国庆节、中秋节", "date": "2025-10-01", "isOffDay": true }, { "name": "国庆节、中秋节", "date": "2025-10-02", "isOffDay": true }, { "name": "国庆节、中秋节", "date": "2025-10-03", "isOffDay": true }, { "name": "国庆节、中秋节", "date": "2025-10-04", "isOffDay": true }, { "name": "国庆节、中秋节", "date": "2025-10-05", "isOffDay": true }, { "name": "国庆节、中秋节", "date": "2025-10-06", "isOffDay": true }, { "name": "国庆节、中秋节", "date": "2025-10-07", "isOffDay": true }, { "name": "国庆节、中秋节", "date": "2025-10-08", "isOffDay": true }, { "name": "国庆节、中秋节", "date": "2025-10-11", "isOffDay": false } ] } ================================================ FILE: holiday/2026.json ================================================ { "$schema": "https://raw.githubusercontent.com/NateScarlet/holiday-cn/master/schema.json", "$id": "https://raw.githubusercontent.com/NateScarlet/holiday-cn/master/2026.json", "year": 2026, "papers": [ "https://www.gov.cn/zhengce/zhengceku/202511/content_7047091.htm" ], "days": [ { "name": "元旦", "date": "2026-01-01", "isOffDay": true }, { "name": "元旦", "date": "2026-01-02", "isOffDay": true }, { "name": "元旦", "date": "2026-01-03", "isOffDay": true }, { "name": "元旦", "date": "2026-01-04", "isOffDay": false }, { "name": "春节", "date": "2026-02-14", "isOffDay": false }, { "name": "春节", "date": "2026-02-15", "isOffDay": true }, { "name": "春节", "date": "2026-02-16", "isOffDay": true }, { "name": "春节", "date": "2026-02-17", "isOffDay": true }, { "name": "春节", "date": "2026-02-18", "isOffDay": true }, { "name": "春节", "date": "2026-02-19", "isOffDay": true }, { "name": "春节", "date": "2026-02-20", "isOffDay": true }, { "name": "春节", "date": "2026-02-21", "isOffDay": true }, { "name": "春节", "date": "2026-02-22", "isOffDay": true }, { "name": "春节", "date": "2026-02-23", "isOffDay": true }, { "name": "春节", "date": "2026-02-28", "isOffDay": false }, { "name": "清明节", "date": "2026-04-04", "isOffDay": true }, { "name": "清明节", "date": "2026-04-05", "isOffDay": true }, { "name": "清明节", "date": "2026-04-06", "isOffDay": true }, { "name": "劳动节", "date": "2026-05-01", "isOffDay": true }, { "name": "劳动节", "date": "2026-05-02", "isOffDay": true }, { "name": "劳动节", "date": "2026-05-03", "isOffDay": true }, { "name": "劳动节", "date": "2026-05-04", "isOffDay": true }, { "name": "劳动节", "date": "2026-05-05", "isOffDay": true }, { "name": "劳动节", "date": "2026-05-09", "isOffDay": false }, { "name": "端午节", "date": "2026-06-19", "isOffDay": true }, { "name": "端午节", "date": "2026-06-20", "isOffDay": true }, { "name": "端午节", "date": "2026-06-21", "isOffDay": true }, { "name": "国庆节", "date": "2026-09-20", "isOffDay": false }, { "name": "中秋节", "date": "2026-09-25", "isOffDay": true }, { "name": "中秋节", "date": "2026-09-26", "isOffDay": true }, { "name": "中秋节", "date": "2026-09-27", "isOffDay": true }, { "name": "国庆节", "date": "2026-10-01", "isOffDay": true }, { "name": "国庆节", "date": "2026-10-02", "isOffDay": true }, { "name": "国庆节", "date": "2026-10-03", "isOffDay": true }, { "name": "国庆节", "date": "2026-10-04", "isOffDay": true }, { "name": "国庆节", "date": "2026-10-05", "isOffDay": true }, { "name": "国庆节", "date": "2026-10-06", "isOffDay": true }, { "name": "国庆节", "date": "2026-10-07", "isOffDay": true }, { "name": "国庆节", "date": "2026-10-10", "isOffDay": false } ] } ================================================ FILE: holiday/2027.json ================================================ { "$schema": "https://raw.githubusercontent.com/NateScarlet/holiday-cn/master/schema.json", "$id": "https://raw.githubusercontent.com/NateScarlet/holiday-cn/master/2027.json", "year": 2027, "papers": [], "days": [] } ================================================ FILE: holiday/renovate.json ================================================ { "extends": [ "config:best-practices", ":automergeMinor", ":disableDependencyDashboard" ] } ================================================ FILE: holiday/schema.json ================================================ { "$schema": "http://json-schema.org/draft-07/schema#", "$id": "https://raw.githubusercontent.com/NateScarlet/holiday-cn/master/schema.json", "type": "object", "properties": { "year": { "type": "number", "description": "年份" }, "papers": { "type": "array", "items": { "type": "string" }, "description": "所用国务院文件网址列表" }, "days": { "type": "array", "items": { "type": "object", "properties": { "name": { "type": "string", "description": "节日名称" }, "date": { "type": "string", "description": "ISO 8601 日期" }, "isOffDay": { "type": "boolean", "description": "是否为休息日" } }, "required": ["name", "date", "isOffDay"] } } }, "required": ["year", "papers", "days"] } ================================================ FILE: install_dependencies.sh ================================================ #!/bin/bash # yt-dlp 依赖 ffmpeg # https://github.com/yt-dlp/yt-dlp#dependencies # 判断系统架构 arch=$(uname -m) # 输出架构信息 echo "当前系统架构是:$arch" install_from_github() { pkg=$1 wget https://github.com/yt-dlp/FFmpeg-Builds/releases/download/latest/$pkg.tar.xz tar -xvJf $pkg.tar.xz mkdir -p ffmpeg/bin mv $pkg/bin/ffmpeg ffmpeg/bin/ mv $pkg/bin/ffprobe ffmpeg/bin/ rm -rf $pkg $pkg.tar.xz } install_from_ffmpeg() { pkg=$1 wget https://johnvansickle.com/ffmpeg/builds/$pkg.tar.xz mkdir -p $pkg tar -xvJf $pkg.tar.xz -C $pkg mkdir -p ffmpeg/bin mv $pkg/*/ffmpeg ffmpeg/bin/ mv $pkg/*/ffprobe ffmpeg/bin/ rm -rf $pkg $pkg.tar.xz } # 基于架构执行不同的操作 case "$arch" in x86_64) echo "64位 x86 架构" install_from_github ffmpeg-master-latest-linux64-gpl #install_from_ffmpeg ffmpeg-git-amd64-static ;; arm64 | aarch64) echo "64位 ARM 架构" install_from_github ffmpeg-master-latest-linuxarm64-gpl #install_from_ffmpeg ffmpeg-git-arm64-static ;; armv7l) echo "armv7l 架构" install_from_ffmpeg ffmpeg-git-armhf-static ;; *) echo "未知架构 $arch" ;; esac ================================================ FILE: newpatch.sh ================================================ #!/bin/bash ./update-static-version.py ./update-holiday.sh git add xiaomusic/static git commit -m 'build: update static version' git pull --rebase cz bump --check-consistency --increment patch git push git push --tags ================================================ FILE: newversion.sh ================================================ #!/bin/bash ./update-static-version.py ./update-holiday.sh git add xiaomusic/static git commit -m 'build: update static version' git pull --rebase cz bump --check-consistency git push git push --tags ================================================ FILE: package.json ================================================ { "name": "xiaomusic-js-plugins", "version": "1.0.0", "description": "JS plugins for xiaomusic", "main": "xiaomusic/js_plugin_runner.js", "dependencies": { "axios": "^1.6.0", "big-integer": "^1.6.52", "cheerio": "^1.0.0-rc.12", "crypto-js": "^4.2.0", "dayjs": "^1.11.10", "he": "^1.2.0", "qs": "^6.14.0" } } ================================================ FILE: plugins/__init__.py ================================================ ================================================ FILE: plugins/code1.py ================================================ async def code1(arg1): global log, xiaomusic log.info(f"code1:{arg1}") did = xiaomusic.get_cur_did() await xiaomusic.do_tts(did, "你好,我是自定义的测试口令") query = xiaomusic.command_handler.last_cmd.strip() await xiaomusic.do_tts(did, f"你说的是: {query}") ================================================ FILE: plugins/httpget.py ================================================ import requests def httpget(url): global log # 发起请求 response = requests.get(url, timeout=5) # 增加超时以避免长时间挂起 response.raise_for_status() # 如果响应不是200,引发HTTPError异常 log.info(f"httpget url:{url} response:{response.text}") ================================================ FILE: plugins/httppost.py ================================================ import requests target = "HTTP://192.168.1.10:58091/items/" def httppost(data, url=target): global log # 发起请求, with requests.post( url, json=data, timeout=5 ) as response: # 增加超时以避免长时间挂起 response.raise_for_status() # 如果响应不是200,引发HTTPError异常 log.info(f"httppost url:{url} data :{data} response:{response.text}") ================================================ FILE: pyproject.toml ================================================ [project] name = "xiaomusic" version = "0.4.26" description = "Play Music with xiaomi AI speaker" authors = [ {name = "涵曦", email = "im.hanxi@gmail.com"}, ] dependencies = [ "aiohttp>=3.8.6", "watchdog>=6.0.0", "mutagen>=1.47.0", "yt-dlp[default]>=2024.12.1.232904.dev0", "uvicorn>=0.30.1", "fastapi>=0.115.4", "starlette>=0.37.2", "aiofiles>=24.1.0", "ga4mp>=2.0.4", "apscheduler>=3.10.4", "opencc-python-reimplemented==0.1.7", "pillow>=10.4.0", "python-multipart>=0.0.12", "requests>=2.32.3", "sentry-sdk[fastapi]==1.45.1", "pyjwt>=2.10.1", "fake-useragent>=2.2.0", "miservice-fork", "edge-tts>=7.2.3", "psutil>=5.9.0", "pycryptodome>=3.23.0", "qrcode>=8.2" ] requires-python = ">=3.10" readme = "README.md" license = {text = "MIT"} [project.urls] Homepage = "https://github.com/hanxi/xiaomusic" [project.scripts] xiaomusic = "xiaomusic.cli:main" [build-system] requires = ["pdm-backend"] build-backend = "pdm.backend" [tool.pdm] [tool.pdm.dev-dependencies] lint = [ "ruff>=0.4.8", ] dev = [ "commitizen>=3.27.0", ] [tool.ruff] lint.select = [ "B", # flake8-bugbear "C4", # flake8-comprehensions "E", # pycodestyle - Error "F", # Pyflakes "I", # isort "W", # pycodestyle - Warning "UP", # pyupgrade ] lint.ignore = [ "E501", # line-too-long "W191", # tab-indentation ] include = ["**/*.py", "**/*.pyi", "**/pyproject.toml"] [tool.ruff.lint.pydocstyle] convention = "google" [tool.ruff.lint.flake8-bugbear] extend-immutable-calls = [ "fastapi.Depends", "fastapi.params.Depends", "fastapi.Query", "fastapi.params.Query", "fastapi.File" ] [tool.pdm.scripts] lint = "ruff check . --exclude MiService" fmt = "ruff format . --exclude MiService" lintfmt = {composite = ["ruff check --fix . --exclude MiService", "ruff format . --exclude MiService"]} [tool.commitizen] name = "cz_conventional_commits" tag_format = "v$version" version_scheme = "pep440" version_provider = "pep621" update_changelog_on_bump = true major_version_zero = true version_files = [ "xiaomusic/__init__.py", ] ================================================ FILE: test/test_difflib.py ================================================ import difflib from xiaomusic.utils.text_utils import find_best_match, keyword_detection if __name__ == "__main__": user_input = "八年的爱" s1 = "冰冰超人 - 八年的爱新版" s2 = "冰冰超人 - 八年的爱" r1 = difflib.SequenceMatcher(None, s1, user_input).ratio() r2 = difflib.SequenceMatcher(None, s2, user_input).ratio() print(s1, r1) print(s2, r2) s3 = "其他" str_list = [s2, s1, s3] matches, remains = keyword_detection(user_input, str_list, n=10) print(matches, remains) extra_search_index = {} extra_search_index["1"] = s1 extra_search_index["2"] = s2 extra_search_index["3"] = s3 real_names = find_best_match( user_input, str_list, cutoff=0.4, n=100, extra_search_index=extra_search_index, ) print(real_names) ================================================ FILE: test/test_music_duration.py ================================================ import math from xiaomusic.const import SUPPORT_MUSIC_TYPE from xiaomusic.utils.file_utils import traverse_music_directory from xiaomusic.utils.music_utils import get_local_music_duration async def test_one_music(filename): # 获取播放时长 duration = await get_local_music_duration(filename) sec = math.ceil(duration) print(f"本地歌曲 : {filename} 的时长 {duration} {sec} 秒") async def main(directory): # 获取所有歌曲文件 local_musics = traverse_music_directory(directory, 10, [], SUPPORT_MUSIC_TYPE) print(local_musics) for _, files in local_musics.items(): for file in files: await test_one_music(file) if __name__ == "__main__": import asyncio directory = "./music" # 替换为你的音乐目录路径 asyncio.run(main(directory)) ================================================ FILE: test/test_music_tags.py ================================================ import traceback from xiaomusic.const import SUPPORT_MUSIC_TYPE from xiaomusic.utils.file_utils import traverse_music_directory from xiaomusic.utils.music_utils import extract_audio_metadata # title 标题 # artist 艺术家 # album 影集 # year 年 # genre 性 # picture 图片 # lyrics 歌词 async def test_one_music(filename): # 获取播放时长 try: metadata = extract_audio_metadata(filename, "cache/picture_cache") print(metadata) except Exception as e: print(f"歌曲 : {filename} no tag {e}") traceback.print_exc() async def main(directory): # 获取所有歌曲文件 local_musics = traverse_music_directory(directory, 10, [], SUPPORT_MUSIC_TYPE) for _, files in local_musics.items(): for file in files: print(file) # await test_one_music(file) pass await test_one_music("music/4 In Love - 一千零一个愿望.mp3") # await test_one_music("./music/程响-人间烟火.flac") if __name__ == "__main__": import asyncio directory = "./music" # 替换为你的音乐目录路径 asyncio.run(main(directory)) ================================================ FILE: test/test_remove_common_prefix.py ================================================ import re def removepre(filename): match = re.search(r"^[pP]?(\d+)\s+\d*(.+?)\.(.*$)", filename.strip()) new_filename = filename if match: num = match.group(1) name = match.group(2).replace(".", " ").strip() suffix = match.group(3) # print(name) # print(num) # print(suffix) new_filename = f"{num}.{name}.{suffix}" print(filename, "=>", new_filename) if __name__ == "__main__": removepre(" 17 《白色风车》.mp3") removepre(" 17 《白色风车》.mp3") removepre(" 17 17 《白色风车》.mp3") removepre(" 17 17 《白色风车》.mp3") removepre(" 18 风车.mp3") removepre(" 18 色风车.mp3") removepre(" 18 18 你好.mp3") removepre(" 18 18 我好.mp3") removepre("p09 009. 梁静茹-亲亲.mp3") ================================================ FILE: test/test_update.py ================================================ from xiaomusic.utils.system_utils import download_and_extract if __name__ == "__main__": import asyncio url = "https://github.hanxi.cc/proxy/hanxi/xiaomusic/releases/download/main/app-amd64-lite.tar.gz" target_directory = "./tmp/app" asyncio.run(download_and_extract(url, target_directory)) ================================================ FILE: update-holiday.sh ================================================ #!/bin/bash rm -rf holiday-cn git clone https://github.com/NateScarlet/holiday-cn.git mkdir -p holiday cp holiday-cn/*.json holiday/ rm -rf holiday-cn ================================================ FILE: update-static-version.py ================================================ #!/usr/bin/env python3 import re from pathlib import Path def get_html_files(directory): """ 获取指定目录下所有HTML文件的列表。 :param directory: 搜索HTML文件的目录。 :return: 搜索到的HTML文件的路径列表。 """ return list(Path(directory).rglob("*.html")) def update_html_version(html_files, version): """ 更新HTML文件中所有以 ./ 开头的CSS和JS文件引用的版本号。 :param html_files: 需要更新的HTML文件路径的列表。 :param version: 新的版本号字符串。 """ pattern = re.compile(r'(\./.*(css|js))\?version=[^"]*"') for html_file in html_files: if not html_file.exists(): print(f"文件 {html_file} 不存在。") continue html_content = html_file.read_text() # 更新CSS和JS版本号 html_content = pattern.sub(rf'\g<1>?version={version}"', html_content) # html_content = pattern.sub(fr'\g<1>"', html_content) # 保存更改到HTML文件 html_file.write_text(html_content) print(f"文件 {html_file} 已更新为使用新的版本号: {version}") # 使用案例 if __name__ == "__main__": import time t = str(int(time.time())) # 指定目录 html_directory = "xiaomusic/static/default" # 修改为实际的HTML文件目录路径 # 获取HTML文件列表 html_files_to_update = get_html_files(html_directory) # 执行更新 update_html_version(html_files_to_update, t) ================================================ FILE: xiaomusic/__init__.py ================================================ __version__ = "0.4.26" ================================================ FILE: xiaomusic/analytics.py ================================================ import asyncio import copy import platform import traceback from datetime import datetime import aiohttp from ga4mp import GtagMP from xiaomusic import __version__ MAX_PARAM_LENGTH = 100 class Analytics: def __init__(self, log, config): self.gtag = None self.current_date = None self.log = log self.config = config self.init() def init(self): if self.gtag is not None: return gtag = GtagMP( api_secret="sVRsf3T9StuWc-ZiWZxDVA", measurement_id="G-Z09NC1K7ZW", client_id="", ) gtag.client_id = gtag.random_client_id() gtag.store.set_user_property(name="version", value=__version__) self.gtag = gtag self.log.info("analytics init ok") async def send_startup_event(self): event = self.gtag.create_new_event(name="startup") event.set_event_param(name="version", value=__version__) await self._send(event) async def send_daily_event(self): current_date = datetime.now().strftime("%Y-%m-%d") if self.current_date == current_date: return event = self.gtag.create_new_event(name="daily_active_user") event.set_event_param(name="version", value=__version__) event.set_event_param(name="date", value=current_date) await self._send(event) self.current_date = current_date async def send_play_event(self, name, sec, hardware): event = self.gtag.create_new_event(name="play") event.set_event_param(name="version", value=__version__) truncated_name = name[:MAX_PARAM_LENGTH] event.set_event_param(name="music", value=truncated_name) event.set_event_param(name="sec", value=sec) event.set_event_param(name="hardware", value=hardware) await self._send(event) async def _send(self, event): if self.config.enable_analytics: # asyncio.create_task(self.post_to_umami(event)) await self.run_with_cancel(self._google_send, [event]) else: self.log.info("analytics is disabled, skip sending event") def _google_send(self, events): try: self.gtag.send(events) except Exception as e: self.log.warning(f"google analytics run_with_cancel failed {e}") async def run_with_cancel(self, func, *args, **kwargs): try: asyncio.create_task(asyncio.to_thread(func, *args, **kwargs)) self.log.info("analytics run_with_cancel success") except Exception as e: self.log.warning(f"analytics run_with_cancel failed {e}") return None async def post_to_umami(self, event): try: url = "https://umami.hanxi.cc/api/send" user_agent = self._get_user_agent() params = copy.copy(event.get_event_params()) params["useragent"] = user_agent data = { "payload": { "hostname": self.config.hostname, "language": "zh-CN", "referrer": "", "screen": "430x932", "title": "后端统计", "url": "/backend", "website": "7bfb0890-4115-4260-8892-b391513e7e99", "name": event.get_event_name(), "data": params, }, "type": "event", } self.log.info(f"umami data: {data}") async with aiohttp.ClientSession() as session: headers = { "User-Agent": user_agent, } # self.log.info(f"headers {headers}, {data}") async with session.post(url, json=data, headers=headers) as response: self.log.info(f"umami Status: {response.status}") await response.text() except Exception as e: self.log.exception(f"Execption {e}") def _get_user_agent(self): try: # 获取系统信息 os_name = platform.system() # 操作系统名称,如 'Windows', 'Linux', 'Darwin' os_version = platform.version() # 操作系统版本号 architecture = "unknow" try: architecture = platform.architecture()[0] # '32bit' or '64bit' except Exception as e: architecture = f"Error {e}" pass machine = platform.machine() # 机器类型,如 'x86_64', 'arm64' # 获取 Python 版本信息 python_version = platform.python_version() # Python 版本 # 组合 User-Agent 字符串 user_agent = ( f"XiaoMusic/{__version__} " f"({os_name} {os_version}; {architecture}; {machine}) " f"Python/{python_version}" ) except Exception as e: # 获取报错的堆栈信息 error_info = traceback.format_exc() user_agent = f"Error: {e} {error_info}" return user_agent ================================================ FILE: xiaomusic/api/__init__.py ================================================ """API 模块统一入口""" from xiaomusic.api.app import ( HttpInit, app, ) __all__ = ["app", "HttpInit"] ================================================ FILE: xiaomusic/api/app.py ================================================ """FastAPI 应用实例和中间件配置""" import asyncio import os from contextlib import asynccontextmanager from typing import TYPE_CHECKING from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from starlette.middleware.gzip import GZipMiddleware from xiaomusic import __version__ from xiaomusic.api.dependencies import ( AuthStaticFiles, reset_http_server, ) if TYPE_CHECKING: from xiaomusic.xiaomusic import XiaoMusic # 导入内部状态管理器 from xiaomusic.api.dependencies import _state @asynccontextmanager async def app_lifespan(app): """应用生命周期管理""" task = None if _state.is_initialized(): task = asyncio.create_task(_state._xiaomusic.run_forever()) try: yield except asyncio.CancelledError: # 正常关闭时的取消,不需要记录 pass finally: # 关闭时取消后台任务 if task is not None and not task.done(): task.cancel() try: await task except asyncio.CancelledError: if _state.is_initialized(): _state._log.info("Background task cleanup: CancelledError") except Exception as e: if _state.is_initialized(): _state._log.error(f"Background task cleanup error: {e}") # 创建 FastAPI 应用实例 app = FastAPI( lifespan=app_lifespan, version=__version__, docs_url=None, redoc_url=None, openapi_url=None, ) # 添加 CORS 中间件 app.add_middleware( CORSMiddleware, allow_origins=["*"], # 允许访问的源 allow_credentials=False, # 支持 cookie allow_methods=["*"], # 允许使用的请求方法 allow_headers=["*"], # 允许携带的 Headers ) # 添加 GZip 中间件 app.add_middleware(GZipMiddleware, minimum_size=500) def HttpInit(_xiaomusic: "XiaoMusic"): """初始化 HTTP 服务器 Args: _xiaomusic: XiaoMusic 实例 """ # 初始化应用状态 _state.initialize(_xiaomusic) # 挂载静态文件 folder = os.path.dirname(os.path.dirname(__file__)) # xiaomusic 目录 app.mount("/static", AuthStaticFiles(directory=f"{folder}/static"), name="static") # 注册所有路由 from xiaomusic.api.routers import register_routers register_routers(app) # 重置 HTTP 服务器配置 reset_http_server(app) ================================================ FILE: xiaomusic/api/dependencies.py ================================================ """依赖注入和认证相关功能""" import hashlib import secrets from typing import ( TYPE_CHECKING, Annotated, ) from fastapi import ( Depends, HTTPException, Request, status, ) from fastapi.security import ( HTTPBasic, HTTPBasicCredentials, ) from fastapi.staticfiles import StaticFiles if TYPE_CHECKING: import logging from xiaomusic.config import Config from xiaomusic.xiaomusic import XiaoMusic security = HTTPBasic() class _AppStateProxy: """应用状态代理类 提供类似全局变量的访问方式,但实际上是动态获取的。 这样既保持了代码的简洁性,又避免了真正的全局变量。 """ def __init__(self): self._xiaomusic: XiaoMusic | None = None self._config: Config | None = None self._log: logging.Logger | None = None def initialize(self, xiaomusic_instance: "XiaoMusic"): """初始化应用状态 Args: xiaomusic_instance: XiaoMusic 实例 """ self._xiaomusic = xiaomusic_instance self._config = xiaomusic_instance.config self._log = xiaomusic_instance.log def is_initialized(self) -> bool: """检查是否已初始化""" return self._xiaomusic is not None # 创建内部状态管理器 _state = _AppStateProxy() class _LazyProxy: """延迟代理类,用于模拟全局变量""" def __init__(self, attr_name: str): self._attr_name = attr_name def __getattr__(self, name): """代理所有属性访问""" obj = getattr(_state, self._attr_name) if obj is None: raise RuntimeError( f"{self._attr_name} not initialized. Call initialize() first." ) return getattr(obj, name) def __call__(self, *args, **kwargs): """代理函数调用""" obj = getattr(_state, self._attr_name) if obj is None: raise RuntimeError( f"{self._attr_name} not initialized. Call initialize() first." ) return obj(*args, **kwargs) def __bool__(self): """支持布尔判断""" obj = getattr(_state, self._attr_name) return obj is not None and bool(obj) def __repr__(self): obj = getattr(_state, self._attr_name) return repr(obj) if obj is not None else f"你确定要删除歌曲 吗?
注意:该操作会永久删除该歌曲且不可撤销
当前页面的HOST与设置中的HOST不一致,请检查是否设置错误
当前HOST:
设置中的HOST: