├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.yml │ ├── config.yml │ └── feature_request.yml ├── release-drafter.yml └── workflows │ ├── docker-build.yml │ ├── release-drafter.yml │ └── release-file.yml ├── LICENSE ├── Questions.md ├── README.md ├── alist ├── Dockerfile ├── README.md ├── clear.sh ├── fix_media.sh ├── service.sh ├── start.sh └── update_media_addr.sh ├── build.sh ├── docker-compose-alist.yml ├── docker-compose.yml ├── emby ├── Dockerfile ├── README.md ├── entrypoint.sh ├── service.sh └── update_alist_addr.sh ├── env ├── helmfile.yaml ├── install.sh ├── metadata ├── Dockerfile ├── README.md ├── emby.sh └── entrypoint.sh └── uninstall.sh /.github/ISSUE_TEMPLATE/bug_report.yml: -------------------------------------------------------------------------------- 1 | name: Bug 报告 2 | description: 反馈 Bug 来改进项目 3 | title: "[BUG]: " 4 | labels: ["bug"] 5 | assignees: 6 | - monlor 7 | 8 | body: 9 | - type: checkboxes 10 | id: checklist 11 | attributes: 12 | label: 提交检查 13 | description: 请确认以下内容 14 | options: 15 | - label: 我已经搜索过 Issues,没有找到类似问题 16 | - label: 我已经使用[百度](https://www.baidu.com)或[谷歌](https://www.google.com)搜索过,没有找到类似问题 17 | - label: 我已经检查过[常见问题](https://github.com/monlor/docker-xiaoya/blob/main/Questions.md),没有找到类似问题 18 | validations: 19 | required: true 20 | 21 | - type: textarea 22 | id: description 23 | attributes: 24 | label: 描述 bug 25 | description: 对 bug 的清晰简洁的描述。 26 | validations: 27 | required: true 28 | 29 | - type: textarea 30 | id: reproduction 31 | attributes: 32 | label: 重现步骤 33 | description: | 34 | 复现问题的步骤: 35 | 1. 进入 '...' 36 | 2. 点击 '....' 37 | 3. 滚动到 '....' 38 | 4. 出现错误 39 | validations: 40 | required: false 41 | 42 | - type: textarea 43 | id: expected 44 | attributes: 45 | label: 预期行为 46 | description: 对您期望发生的行为的清晰简洁的描述。 47 | validations: 48 | required: false 49 | 50 | - type: textarea 51 | id: screenshot 52 | attributes: 53 | label: 截图 54 | description: 如果适用,添加截图以帮助解释您的问题。 55 | 56 | - type: textarea 57 | id: logs 58 | attributes: 59 | label: 日志 60 | render: shell 61 | 62 | - type: dropdown 63 | id: device 64 | attributes: 65 | label: 设备 (请填写以下信息) 66 | options: 67 | - Linux 68 | - Windows 69 | - Mac 70 | - 群晖 71 | - 绿联 72 | - 其他(在其他信息中补充) 73 | validations: 74 | required: true 75 | 76 | - type: textarea 77 | id: additional 78 | attributes: 79 | label: 其他信息 80 | description: 在此添加有关问题的任何其他上下文。 81 | 82 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: 常见问题 4 | url: https://github.com/monlor/docker-xiaoya/blob/main/Questions.md 5 | about: 提问前先查看常见问题中是否有解决方案 -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.yml: -------------------------------------------------------------------------------- 1 | name: 功能建议 2 | description: 为这个项目提供建议 3 | title: "[FEATURE]: " 4 | labels: ["enhancement"] 5 | assignees: 6 | - monlor 7 | 8 | body: 9 | - type: textarea 10 | id: description 11 | attributes: 12 | label: 功能建议的描述 13 | description: 简洁地描述您的功能建议,包括它如何改善当前的产品或解决现有问题。 14 | validations: 15 | required: true 16 | 17 | - type: textarea 18 | id: user-story 19 | attributes: 20 | label: 功能背景 21 | description: 请描述一下这个功能的使用背景。 22 | validations: 23 | required: false 24 | 25 | - type: textarea 26 | id: benefit 27 | attributes: 28 | label: 预期的收益 29 | description: 解释一下您认为这个功能将带来哪些好处。 30 | validations: 31 | required: false 32 | 33 | - type: textarea 34 | id: alternatives 35 | attributes: 36 | label: 考虑过的替代方案 37 | description: 您是否考虑过其他的解决方案?如果有,请简单描述一下。 38 | validations: 39 | required: false 40 | 41 | - type: textarea 42 | id: implementation 43 | attributes: 44 | label: 实现建议 45 | description: 如果您有任何关于如何实现这个功能的想法,请分享出来。 46 | validations: 47 | required: false 48 | 49 | - type: dropdown 50 | id: priority 51 | attributes: 52 | label: 优先级 53 | description: 您认为这个功能建议的优先级有多高? 54 | options: 55 | - 低 56 | - 中 57 | - 高 58 | - 非常高 59 | validations: 60 | required: true 61 | 62 | - type: textarea 63 | id: additional 64 | attributes: 65 | label: 其他信息 66 | description: 您还有什么其他想补充的内容吗? -------------------------------------------------------------------------------- /.github/release-drafter.yml: -------------------------------------------------------------------------------- 1 | name-template: 'v$RESOLVED_VERSION 🌈' 2 | tag-template: 'v$RESOLVED_VERSION' 3 | categories: 4 | - title: '🚀 Features' 5 | labels: 6 | - 'feature' 7 | - 'enhancement' 8 | - 'feat' 9 | - title: '🐛 Bug Fixes' 10 | labels: 11 | - 'fix' 12 | - 'bugfix' 13 | - 'bug' 14 | - title: '🧰 Maintenance' 15 | label: 'chore' 16 | autolabeler: 17 | - label: 'chore' 18 | title: 19 | - '/docs:/i' 20 | - label: 'bug' 21 | title: 22 | - '/fix:/i' 23 | - label: 'feature' 24 | title: 25 | - '/feat:/i' 26 | change-template: '- $TITLE @$AUTHOR (#$NUMBER)' 27 | change-title-escapes: '\<*_&' # You can add # and @ to disable mentions, and add ` to disable code blocks. 28 | version-resolver: 29 | major: 30 | labels: 31 | - 'major' 32 | minor: 33 | labels: 34 | - 'minor' 35 | patch: 36 | labels: 37 | - 'patch' 38 | default: patch 39 | template: | 40 | ## Changes 41 | 42 | $CHANGES -------------------------------------------------------------------------------- /.github/workflows/docker-build.yml: -------------------------------------------------------------------------------- 1 | name: Docker Image 2 | 3 | on: 4 | schedule: 5 | - cron: '0 0 * * *' 6 | push: 7 | branches: 8 | - main 9 | - test 10 | tags: 11 | - '*' 12 | paths-ignore: 13 | - 'README.md' 14 | - 'install.sh' 15 | 16 | concurrency: 17 | group: ${{ github.ref }} 18 | cancel-in-progress: true 19 | 20 | jobs: 21 | prepare: 22 | runs-on: ubuntu-latest 23 | permissions: 24 | contents: read 25 | packages: write 26 | # This is used to complete the identity challenge 27 | # with sigstore/fulcio when running outside of PRs. 28 | id-token: write 29 | outputs: 30 | alist_tags: ${{ steps.vars.outputs.alist_tags }} 31 | metadata_tags: ${{ steps.vars.outputs.metadata_tags }} 32 | embyserver_tags: ${{ steps.vars.outputs.embyserver_tags }} 33 | embyserver-amilys_tags: ${{ steps.vars.outputs.embyserver-amilys_tags }} 34 | steps: 35 | - name: Checkout code 36 | uses: actions/checkout@v3 37 | 38 | - name: Determine version and tags 39 | id: vars 40 | run: | 41 | IMAGE_PREFIX="ghcr.io/${{ github.repository_owner }}" 42 | vars=( 43 | "alist" 44 | "metadata" 45 | "embyserver" 46 | "embyserver-amilys" 47 | ) 48 | if [[ $GITHUB_REF == refs/tags/* ]]; then 49 | VERSION=${GITHUB_REF#refs/tags/} 50 | for var in "${vars[@]}"; do 51 | echo "::set-output name=${var}_tags::${IMAGE_PREFIX}/xiaoya-${var}:latest,${IMAGE_PREFIX}/xiaoya-${var}:${VERSION}" 52 | done 53 | else 54 | LATEST_TAG=${GITHUB_REF#refs/heads/} 55 | for var in "${vars[@]}"; do 56 | echo "::set-output name=${var}_tags::${IMAGE_PREFIX}/xiaoya-${var}:${LATEST_TAG}" 57 | done 58 | fi 59 | 60 | - name: Set up Docker Buildx 61 | uses: docker/setup-buildx-action@v3 62 | 63 | - name: Login to GitHub Container Registry 64 | uses: docker/login-action@v2 65 | with: 66 | registry: ghcr.io 67 | username: ${{ github.actor }} 68 | password: ${{ secrets.GITHUB_TOKEN }} 69 | 70 | - name: Build base image 71 | run: | 72 | docker buildx imagetools create \ 73 | --tag ghcr.io/monlor/embyserver:latest \ 74 | emby/embyserver:latest \ 75 | emby/embyserver_arm64v8:latest \ 76 | emby/embyserver_arm32v7 77 | 78 | docker buildx imagetools create \ 79 | --tag ghcr.io/monlor/embyserver-amilys:latest \ 80 | amilys/embyserver:latest \ 81 | amilys/embyserver_arm64v8:latest 82 | 83 | build-alist: 84 | runs-on: ubuntu-latest 85 | permissions: 86 | contents: read 87 | packages: write 88 | # This is used to complete the identity challenge 89 | # with sigstore/fulcio when running outside of PRs. 90 | id-token: write 91 | needs: prepare 92 | steps: 93 | - name: Checkout code 94 | uses: actions/checkout@v3 95 | 96 | - name: Set up Docker Buildx 97 | uses: docker/setup-buildx-action@v3 98 | 99 | - name: Login to GitHub Container Registry 100 | uses: docker/login-action@v2 101 | with: 102 | registry: ghcr.io 103 | username: ${{ github.actor }} 104 | password: ${{ secrets.GITHUB_TOKEN }} 105 | 106 | - name: Cache Docker layers 107 | uses: actions/cache@v4 108 | with: 109 | path: /tmp/.buildx-cache 110 | key: ${{ github.job }}-${{ github.sha }} 111 | restore-keys: | 112 | ${{ github.job }}- 113 | 114 | - name: Build and push xiaoya-alist 115 | uses: docker/build-push-action@v4 116 | with: 117 | context: ./alist 118 | push: true 119 | tags: ${{ needs.prepare.outputs.alist_tags }} 120 | platforms: linux/amd64,linux/arm64,linux/arm/v7 121 | cache-from: type=local,src=/tmp/.buildx-cache 122 | cache-to: type=local,dest=/tmp/.buildx-cache-new 123 | 124 | - name: Move cache 125 | run: | 126 | rm -rf /tmp/.buildx-cache 127 | mv /tmp/.buildx-cache-new /tmp/.buildx-cache 128 | 129 | build-metadata: 130 | runs-on: ubuntu-latest 131 | permissions: 132 | contents: read 133 | packages: write 134 | # This is used to complete the identity challenge 135 | # with sigstore/fulcio when running outside of PRs. 136 | id-token: write 137 | needs: prepare 138 | steps: 139 | - name: Checkout code 140 | uses: actions/checkout@v3 141 | 142 | - name: Set up Docker Buildx 143 | uses: docker/setup-buildx-action@v3 144 | 145 | - name: Login to GitHub Container Registry 146 | uses: docker/login-action@v2 147 | with: 148 | registry: ghcr.io 149 | username: ${{ github.actor }} 150 | password: ${{ secrets.GITHUB_TOKEN }} 151 | 152 | - name: Cache Docker layers 153 | uses: actions/cache@v4 154 | with: 155 | path: /tmp/.buildx-cache 156 | key: ${{ github.job }}-${{ github.sha }} 157 | restore-keys: | 158 | ${{ github.job }}- 159 | 160 | - name: Build and push xiaoya-metadata 161 | uses: docker/build-push-action@v4 162 | with: 163 | context: ./metadata 164 | push: true 165 | tags: ${{ needs.prepare.outputs.metadata_tags }} 166 | platforms: linux/amd64,linux/arm64,linux/arm/v7 167 | cache-from: type=local,src=/tmp/.buildx-cache 168 | cache-to: type=local,dest=/tmp/.buildx-cache-new 169 | 170 | - name: Move cache 171 | run: | 172 | rm -rf /tmp/.buildx-cache 173 | mv /tmp/.buildx-cache-new /tmp/.buildx-cache 174 | 175 | build-emby: 176 | runs-on: ubuntu-latest 177 | permissions: 178 | contents: read 179 | packages: write 180 | # This is used to complete the identity challenge 181 | # with sigstore/fulcio when running outside of PRs. 182 | id-token: write 183 | needs: prepare 184 | steps: 185 | - name: Checkout code 186 | uses: actions/checkout@v3 187 | 188 | - name: Set up Docker Buildx 189 | uses: docker/setup-buildx-action@v3 190 | 191 | - name: Login to GitHub Container Registry 192 | uses: docker/login-action@v2 193 | with: 194 | registry: ghcr.io 195 | username: ${{ github.actor }} 196 | password: ${{ secrets.GITHUB_TOKEN }} 197 | 198 | - name: Cache Docker layers 199 | uses: actions/cache@v4 200 | with: 201 | path: /tmp/.buildx-cache 202 | key: ${{ github.job }}-${{ github.sha }} 203 | restore-keys: | 204 | ${{ github.job }}- 205 | 206 | - name: Build and push xiaoya-embyserver 207 | uses: docker/build-push-action@v4 208 | with: 209 | context: ./emby 210 | push: true 211 | tags: ${{ needs.prepare.outputs.embyserver_tags }} 212 | platforms: linux/amd64,linux/arm64,linux/arm/v7 213 | build-args: | 214 | BASE_IMAGE=ghcr.io/monlor/embyserver:latest 215 | cache-from: type=local,src=/tmp/.buildx-cache 216 | cache-to: type=local,dest=/tmp/.buildx-cache-new 217 | 218 | - name: Build and push xiaoya-embyserver-amilys 219 | uses: docker/build-push-action@v4 220 | with: 221 | context: ./emby 222 | push: true 223 | tags: ${{ needs.prepare.outputs.embyserver-amilys_tags }} 224 | platforms: linux/amd64,linux/arm64 225 | build-args: | 226 | BASE_IMAGE=ghcr.io/monlor/embyserver-amilys:latest 227 | cache-from: type=local,src=/tmp/.buildx-cache 228 | cache-to: type=local,dest=/tmp/.buildx-cache-new 229 | 230 | - name: Move cache 231 | run: | 232 | rm -rf /tmp/.buildx-cache 233 | mv /tmp/.buildx-cache-new /tmp/.buildx-cache 234 | -------------------------------------------------------------------------------- /.github/workflows/release-drafter.yml: -------------------------------------------------------------------------------- 1 | name: Release Drafter 2 | 3 | on: 4 | push: 5 | # branches to consider in the event; optional, defaults to all 6 | branches: 7 | - main 8 | pull_request_target: 9 | types: 10 | - closed 11 | 12 | permissions: 13 | contents: read 14 | 15 | jobs: 16 | update_release_draft: 17 | if: github.event.pull_request.merged 18 | permissions: 19 | # write permission is required to create a github release 20 | contents: write 21 | # write permission is required for autolabeler 22 | # otherwise, read permission is required at least 23 | pull-requests: write 24 | runs-on: ubuntu-latest 25 | steps: 26 | # (Optional) GitHub Enterprise requires GHE_HOST variable set 27 | #- name: Set GHE_HOST 28 | # run: | 29 | # echo "GHE_HOST=${GITHUB_SERVER_URL##https:\/\/}" >> $GITHUB_ENV 30 | 31 | # Drafts your next Release notes as Pull Requests are merged into "master" 32 | - uses: release-drafter/release-drafter@v6 33 | # (Optional) specify config name to use, relative to .github/. Default: release-drafter.yml 34 | # with: 35 | # config-name: my-config.yml 36 | # disable-autolabeler: true 37 | env: 38 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} -------------------------------------------------------------------------------- /.github/workflows/release-file.yml: -------------------------------------------------------------------------------- 1 | name: Release file 2 | 3 | on: 4 | release: 5 | types: [published] 6 | 7 | permissions: 8 | contents: write 9 | 10 | jobs: 11 | build: 12 | name: Publish files 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - uses: actions/checkout@v3 17 | - name: Upload binaries to release 18 | run: | 19 | gh release upload ${{github.event.release.tag_name}} docker-compose* env install.sh uninstall.sh 20 | env: 21 | GITHUB_TOKEN: ${{ github.TOKEN }} 22 | shell: bash -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Creative Commons Attribution-NonCommercial 4.0 International 2 | 3 | You are free to: 4 | 5 | - Share — copy and redistribute the material in any medium or format 6 | - Adapt — remix, transform, and build upon the material 7 | 8 | Under the following terms: 9 | 10 | - Attribution — You must give appropriate credit, provide a link to the license, and indicate if changes were made. You may do so in any reasonable manner, but not in any way that suggests the licensor endorses you or your use. 11 | - NonCommercial — You may not use the material for commercial purposes. 12 | 13 | No additional restrictions — You may not apply legal terms or technological measures that legally restrict others from doing anything the license permits. 14 | 15 | For more details, visit https://creativecommons.org/licenses/by-nc/4.0/ -------------------------------------------------------------------------------- /Questions.md: -------------------------------------------------------------------------------- 1 | ### 常见问题 2 | 3 | #### 获取file id失败 4 | 5 | 1. 请检查你的阿里云盘token,opentoken和folderid配置是否正确, 6 | 2. 阿里云盘空间是否饱和, 7 | 3. 多次尝试更换token和opentoken。 8 | 9 | #### 访问502 10 | 11 | 检查日志,查看服务是否正常启动 12 | 13 | #### 怎么更新服务,怎么修改阿里云盘和夸克云盘配置? 14 | 15 | 重新执行安装脚本可以更新服务配置 16 | 17 | #### 是否可以更新小雅的镜像? 18 | 19 | 1. 一般情况下没有必要同步更新小雅的镜像,除非有新功能上的变动,这种情况下给我提feature,我会同步更新 20 | 2. 小雅的alist数据会每天自动更新,不需要更新镜像 -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![docker-xiaoya](https://socialify.git.ci/monlor/docker-xiaoya/image?description=0&font=Rokkitt&forks=1&issues=1&language=1&logo=https%3A%2F%2Fcdn.monlor.com%2F2024%2F6%2F4%2F2024-06-04%252017.30.47.jpeg&name=1&owner=1&pattern=Circuit%20Board&pulls=1&stargazers=1&theme=Auto) 2 | 3 |
4 |

小雅全家桶部署

5 |

使用 Docker Compose 一键部署 Alist + Emby

6 |
7 | 8 |

9 | Build Status 10 | repo size 11 | GitHub release (latest by date) 12 | All Contributors 13 | buymeacoffee 14 |

15 | 16 | ## 功能特性 17 | 18 | ![](https://cdn.monlor.com/2024/6/4/SCR-20240603-kpvb.jpeg) 19 | 20 | 🚀 使用 Docker Compose 一键部署服务,兼容群晖,Linux,Windows,Mac,包含所有X86和Arm架构 21 | 22 | ✨ 部署alist+下载元数据+部署emby服务全流程自动,无需人工干预 23 | 24 | * 所有脚本集成到 Docker 镜像,避免污染系统环境 25 | * 合并emby的x86和arm镜像,部署时无需区分镜像名 26 | * 自动清理阿里云盘,默认每10分钟一次 27 | * 自动更新小雅alist中的云盘数据,默认每天一次 28 | * 自动更新emby服务配置,默认每周一次 29 | * 自动更新emby媒体数据,默认每天一次 30 | * 支持小雅夸克网盘资源,挂载自定义夸克网盘资源 31 | * 支持小雅PikPak网盘资源,挂载自定义PikPak资源 32 | * 支持小雅阿里云盘资源,挂载自定义阿里云盘资源 33 | * 支持WebDav,TvBox服务 34 | * [Beta]适配Armv7设备,包括alist, emby 35 | 36 | ## 提问规则 37 | 38 | 1. 提BUG和需求,在 [Issues](https://github.com/monlor/docker-xiaoya/issues) 里提 39 | 2. 相关问题讨论或其他内容,在 [Discussions](https://github.com/monlor/docker-xiaoya/discussions) 里提 40 | 41 | ## 一键部署 42 | 43 | ### 部署或更新脚本 44 | 45 | > 脚本支持重复执行,每天自动同步最新镜像(不需要同步请选择下面的稳定版本) 46 | 47 | ```bash 48 | export VERSION=main && bash -c "$(curl -fsSL ${GH_PROXY}https://raw.githubusercontent.com/monlor/docker-xiaoya/${VERSION:-main}/install.sh)" 49 | ``` 50 | 51 | **使用加速源** 52 | 53 | ```bash 54 | export VERSION=main GH_PROXY=https://gh.monlor.com/ IMAGE_PROXY=ghcr.monlor.com && bash -c "$(curl -fsSL ${GH_PROXY}https://raw.githubusercontent.com/monlor/docker-xiaoya/${VERSION:-main}/install.sh)" 55 | ``` 56 | 57 | **环境信息** 58 | 59 | | 类型 | 地址 | 默认用户密码 | 60 | | --- | --- | --- | 61 | | alist | http://ip:5678 | - | 62 | | webdav | http://ip:5678/dav | guest/guest_Api789 | 63 | | tvbox | http://ip:5678/tvbox/my_ext.json | - | 64 | | emby | http://ip:2345 | xiaoya/1234 | 65 | 66 | ### 卸载脚本 67 | 68 | ```bash 69 | bash -c "$(curl -fsSL https://raw.githubusercontent.com/monlor/docker-xiaoya/main/uninstall.sh)" 70 | ``` 71 | 72 | **使用加速源** 73 | 74 | ```bash 75 | export GH_PROXY=https://gh.monlor.com/ IMAGE_PROXY=ghcr.monlor.com && bash -c "$(curl -fsSL ${GH_PROXY}https://raw.githubusercontent.com/monlor/docker-xiaoya/main/uninstall.sh)" 76 | ``` 77 | 78 | ### 自定义配置 79 | 80 | 【**非必须,小白跳过这一步**】脚本没有计划支持硬解,在我看来这个功能没有必要。如果你需要修改硬解,端口,数据目录,环境变量,请自行修改docker-compose.yml和env文件,修改完成后执行下面的命令,使配置生效。**修改后注意**:执行更新脚本会覆盖docker-compose.yml,不会覆盖env文件。 81 | 82 | ```bash 83 | cd 你的安装目录 84 | docker-compose up --remove-orphans -d 85 | ``` 86 | 87 | ### 稳定版 88 | 89 | > release 版本 90 | 91 | ```bash 92 | bash -c "$(curl -fsSL https://raw.githubusercontent.com/monlor/docker-xiaoya/main/install.sh)" 93 | ``` 94 | 95 | ## 部署配置推荐 96 | 97 | | 部署方案 | CPU | 内存 | 硬盘 | 98 | | ----------------- | -------- | --------- | --------- | 99 | | Alist + Emby | 2核 | 4G | 140G | 100 | | 仅部署 Alist | 1核 | 512M | 512M | 101 | 102 | ## 配置示例 103 | 104 | * [只部署小雅alist](/docker-compose-alist.yml) 105 | * [部署小雅alist+emby](/docker-compose.yml) 106 | 107 | ## 服务组件介绍 108 | 109 | * [Alist](/alist): 提供资源在线播放,WebDav服务 110 | * [Metadata](/metadata): Emby的元数据管理 111 | * [Emby](/emby): 用家庭影视库的方式,可视化展示Alist中的资源 112 | 113 | ## 手动部署 114 | 115 | 仅展示小雅alist+emby的部署方式 116 | 117 | ### 使用Docker Compose 118 | 119 | 1. 创建compose文件夹 120 | 121 | ```bash 122 | mkdir /opt/xiaoya 123 | cd /opt/xiaoya 124 | ``` 125 | 126 | 2. 下载配置 127 | 128 | ```bash 129 | curl -#LO https://raw.githubusercontent.com/monlor/docker-xiaoya/main/docker-compose.yml 130 | curl -#LO https://raw.githubusercontent.com/monlor/docker-xiaoya/main/env 131 | ``` 132 | 133 | 3. 修改配置env里面的阿里云盘相关变量,启动服务 134 | 135 | ```bash 136 | docker compose up -d 137 | ``` 138 | 139 | 4. 查看日志 140 | 141 | ```bash 142 | docker compose logs 143 | ``` 144 | 145 | ### 部署在 Kubernetes 146 | 147 | 1. 安装helm 148 | 149 | ```bash 150 | curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash 151 | ``` 152 | 153 | 2. 安装helmfile 154 | 155 | ```bash 156 | ver=0.161.0 157 | curl -LO https://github.com/helmfile/helmfile/releases/download/v${ver}/helmfile_${ver}_linux_arm64.tar.gz 158 | tar zxvf helmfile_${ver}_linux_arm64.tar.gz -C helmfile 159 | mv helmfile/helmfile /usr/local/bin 160 | rm -rf helmfile helmfile_${ver}_linux_arm64.tar.gz 161 | helm plugin install https://github.com/databus23/helm-diff 162 | ``` 163 | 164 | 3. 下载helmfile配置 165 | 166 | ```bash 167 | curl -#LO https://raw.githubusercontent.com/monlor/docker-xiaoya/main/helmfile.yaml 168 | ``` 169 | 170 | 4. 修改helmfile的环境变量,环境变量含义看这里[alist](/alist) 171 | 172 | ```yaml 173 | env: 174 | ... 175 | WEBDAV_PASSWORD: 176 | ALIYUN_TOKEN: 177 | ALIYUN_OPEN_TOKEN: 178 | ALIYUN_FOLDER_ID: 179 | QUARK_COOKIE: 180 | PAN115_COOKIE: 181 | PIKPAK_USER: 182 | ... 183 | ``` 184 | 185 | 5. 部署helm服务 186 | 187 | ```bash 188 | helmfile sync -f helmfile.yaml 189 | ``` 190 | 191 | ### 使用docker部署【不推荐】 192 | 193 | 1. 创建volume 194 | 195 | ```bash 196 | docker volume create xiaoya 197 | docker volume create media 198 | docker volume create config 199 | docker volume create meta 200 | docker volume create cache 201 | ``` 202 | 203 | 2. 创建网络 204 | 205 | ```bash 206 | docker network create xiaoya 207 | ``` 208 | 209 | 3. 启动小雅alist,修改下面的阿里云盘配置,再执行命令 210 | 211 | ```bash 212 | docker run -d --name alist \ 213 | -v xiaoya:/data \ 214 | -p 5678:5678 -p 2345:2345 -p 2346:2346 \ 215 | -e TZ=Asia/Shanghai \ 216 | -e ALIYUN_TOKEN=阿里云盘TOKEN \ 217 | -e ALIYUN_OPEN_TOKEN=阿里云盘Open Token \ 218 | -e ALIYUN_FOLDER_ID=阿里云盘文件夹ID \ 219 | -e QUARK_COOKIE=夸克网盘cookie \ 220 | -e AUTO_CLEAR_ENABLED=true \ 221 | --network=xiaoya \ 222 | ghcr.io/monlor/xiaoya-alist 223 | ``` 224 | 225 | 4. 启动metadata用于元数据同步 226 | 227 | ```bash 228 | docker run -d --name metadata \ 229 | -e LANG=C.UTF-8 \ 230 | -e EMBY_ENABLED=true \ 231 | -e AUTO_UPDATE_EMBY_CONFIG_ENABLED=true \ 232 | -v xiaoya:/etc/xiaoya \ 233 | -v media:/media/xiaoya \ 234 | -v config:/media/config \ 235 | -v cache:/media/config/cache \ 236 | -v meta:/media/temp \ 237 | --network=xiaoya \ 238 | ghcr.io/monlor/xiaoya-metadata 239 | ``` 240 | 241 | 5. 启动emby服务 242 | 243 | ```bash 244 | docker run -d --name emby 245 | -e TZ=Asia/Shanghai \ 246 | -e GIDLIST=0 \ 247 | -e ALIST_ADDR=http://alist:5678 \ 248 | -v media:/media \ 249 | -v config:/config \ 250 | -v cache:/cache \ 251 | -p 6908:6908 \ 252 | --network=xiaoya \ 253 | ghcr.io/monlor/xiaoya-embyserver 254 | ``` 255 | 256 | 6. 查看日志 257 | 258 | ``` 259 | docker logs alist 260 | docker logs metadata 261 | docker logs emby 262 | ``` 263 | 264 | ## 安全建议 265 | 266 | * 开启alist的登陆,alist服务设置webdav的密码`WEBDAV_PASSWORD` 267 | * 在emby控制台修改ApiKey,这个key需要配置到metadata和alist服务,变量名:`EMBY_APIKEY` 268 | 269 | ## 赞助 270 | 271 | Buy Me A Coffee 272 | 273 | ## License 274 | 275 | This project is licensed under the [Creative Commons Attribution-NonCommercial 4.0 International License](https://creativecommons.org/licenses/by-nc/4.0/). 276 | 277 | ## 参考 278 | 279 | https://github.com/DDS-Derek/xiaoya-alist 280 | 281 | https://www.kdocs.cn/l/cvEe3cv6dGkH 282 | 283 | https://xiaoyaliu.notion.site/xiaoya-docker-69404af849504fa5bcf9f2dd5ecaa75f -------------------------------------------------------------------------------- /alist/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine as base 2 | 3 | RUN apk add dumb-init 4 | 5 | FROM xiaoyaliu/alist:hostmode 6 | 7 | LABEL MAINTAINER me@monlor.com 8 | 9 | ENV TZ=Asia/Shanghai 10 | 11 | EXPOSE 5678 2345 2346 12 | 13 | VOLUME /data 14 | 15 | ARG TARGETARCH 16 | 17 | RUN apk add --no-cache tzdata 18 | 19 | COPY --chmod=755 --from=base /usr/bin/dumb-init /dumb-init 20 | 21 | COPY --chmod=755 ./*.sh / 22 | 23 | RUN /service.sh download 24 | 25 | ENTRYPOINT [ "/dumb-init", "--", "/start.sh" ] 26 | 27 | CMD [] 28 | -------------------------------------------------------------------------------- /alist/README.md: -------------------------------------------------------------------------------- 1 | ## 小雅Alist 2 | 3 | alist访问端口:5678 4 | 5 | emby访问端口:2345 6 | 7 | tvbox访问地址:http://ip:5678/tvbox/my_ext.json 8 | 9 | ## 启动命令 10 | 11 | ``` 12 | docker run -d -p 5678:80 -p 2345:2345 -p 2346:2346 --restart=unless-stopped --name=xiaoya monlor/xiaoya-alist:latest 13 | ``` 14 | 15 | ## 环境变量 16 | 17 | `ALIYUN_TOKEN`: 阿里云token https://alist.nn.ci/zh/guide/drivers/aliyundrive.html 18 | 19 | `ALIYUN_OPEN_TOKEN`: 阿里云 open-token https://alist.nn.ci/zh/guide/drivers/aliyundrive_open.html 20 | 21 | `ALIYUN_FOLDER_ID`: 进入阿里云盘网页版,资源盘里面创建一个文件夹,点击文件夹,复制浏览器阿里云盘地址末尾的文件夹ID(最后一个斜杠/后面的一串字符串) 22 | 23 | `QUARK_COOKIE`: 夸克的cookie,登陆夸克网盘,F12找一个请求,查看请求中的Cookie信息 24 | 25 | `PAN115_COOKIE`: 115网盘的cookie,登陆115网盘,F12找一个请求,查看请求中的Cookie信息 26 | 27 | `ALIYUN_TO_115`: 是否将阿里云盘的文件自动迁移到115网盘,true/false,默认false 28 | 29 | `PAN115_FOLDER_ID`: 进入115网页版,创建一个文件夹,点击文件夹,复制浏览器115网盘地址中的cid,默认根目录为0 30 | 31 | `PIKPAK_USER`: pikpak 账号,用来观看小雅中pikpak分享给你的资源,格式:`qqq@qq.com:aaadds` 32 | 33 | `PIKPAK_LIST`: 挂载你自己 pikpak 账号,格式:`挂载名:qqq@qq.com:aaadds,aaa:+8613111111111:dasf`,密码中不支持符号,: 34 | 35 | `PIKPAK_SHARE_LIST`: 挂载自定义的pikpak分享内容,会覆盖小雅的分享,格式:`挂载名1:分享ID1:分享目录ID1,挂载名2:分享ID2:分享目录ID2` 36 | 37 | `ALI_SHARE_LIST`: 挂载额外的阿里云盘分享内容,格式:`挂载名1:分享ID1:文件夹ID1,挂载名2:分享ID2:文件夹ID2` 38 | 39 | `QUARK_SHARE_LIST`: 挂载额外的夸克网盘分享内容,格式:`挂载名1:分享ID1:文件夹ID1(不存在填root):提取码1(没有留空),挂载名2:分享ID2:文件夹ID2(不存在填root):提取码2` 40 | 41 | `PAN115_SHARE_LIST`: 挂载额外的115网盘分享内容,格式:`挂载名1:分享ID1:文件夹ID1(不存在填root):提取码1(没有留空),挂载名2:分享ID2:文件夹ID2(不存在填root):提取码2` 42 | 43 | `TVBOX_SECURITY`: 开启tvbox随机订阅地址,true/false,默认:false 44 | 45 | `PROXY`: 使用代理,支持http、https、socks5协议,格式:http://ip:7890 或 socks5://ip:7890 46 | 47 | `WEBDAV_PASSWORD`: webdav用户名为dav,设置密码。默认用户密码:guest/guest_Api789 48 | 49 | `EMBY_ADDR`: emby部署地址,默认http://emby:6908,容器内部使用地址,一般不用改 50 | 51 | `EMBY_APIKEY`: 填入一个emby的api key,用于在infuse中播放emby 52 | 53 | `AUTO_CLEAR_ENABLED`: 自动清理阿里云云盘的文件,true/false,默认false 54 | 55 | `AUTO_CLEAR_INTERVAL`: 自动清理间隔,单位分钟,范围0-60分钟,默认10分钟 56 | 57 | `AUTO_CLEAR_THRESHOLD`: 阿里云盘自动清理文件存在时间阈值,单位分钟,范围0-60分钟,默认10分钟 -------------------------------------------------------------------------------- /alist/clear.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | DATA_DIR=/data 6 | AUTO_CLEAR_THRESHOLD=${AUTO_CLEAR_THRESHOLD:-10} 7 | 8 | retry_command() { 9 | # 重试次数和最大重试次数 10 | retries=0 11 | max_retries=10 12 | local cmd="$1" 13 | local success=false 14 | local output="" 15 | 16 | while ! $success && [ $retries -lt $max_retries ]; do 17 | output=$(eval "$cmd" 2>&1) 18 | if [ $? -eq 0 ]; then 19 | success=true 20 | else 21 | retries=$(($retries + 1)) 22 | echo "#Failed to execute command \"$(echo "$cmd" | awk '{print $1}')\", retrying in 1 seconds (retry $retries of $max_retries)..." >&2 23 | sleep 1 24 | fi 25 | done 26 | 27 | if $success; then 28 | echo "$output" 29 | return 0 30 | else 31 | echo "#Failed to execute command after $max_retries retries: $cmd" >&2 32 | echo "#Command output: $output" >&2 33 | return 1 34 | fi 35 | } 36 | 37 | get_Header() { 38 | response=$(curl --connect-timeout 5 -m 5 -s -H "Content-Type: application/json" \ 39 | -d '{"grant_type":"refresh_token", "refresh_token":"'$refresh_token'"}' \ 40 | https://api.aliyundrive.com/v2/account/token) 41 | 42 | access_token=$(echo "$response" | sed -n 's/.*"access_token":"\([^"]*\).*/\1/p') 43 | 44 | HEADER="Authorization: Bearer $access_token" 45 | if [ -z "$HEADER" ]; then 46 | echo "获取access token失败" >&2 47 | return 1 48 | fi 49 | 50 | response="$(curl --connect-timeout 5 -m 5 -s -H "$HEADER" -H "Content-Type: application/json" -X POST -d '{}' "https://user.aliyundrive.com/v2/user/get")" 51 | 52 | lagacy_drive_id=$(echo "$response" | sed -n 's/.*"default_drive_id":"\([^"]*\).*/\1/p') 53 | 54 | drive_id=$(echo "$response" | sed -n 's/.*"resource_drive_id":"\([^"]*\).*/\1/p') 55 | 56 | if [ -z "$drive_id" ]; then 57 | drive_id=$lagacy_drive_id 58 | fi 59 | 60 | if [ "$folder_type"x = "b"x ]; then 61 | drive_id=$lagacy_drive_id 62 | fi 63 | 64 | if [ -z "$drive_id" ]; then 65 | echo "获取drive_id失败" >&2 66 | return 1 67 | fi 68 | 69 | echo "HEADER=\"$HEADER\"" 70 | echo "drive_id=\"$drive_id\"" 71 | return 0 72 | } 73 | 74 | get_rawList() { 75 | waittime=10 76 | if [ -n "$1" ]; then 77 | waittime="$1" 78 | fi 79 | _res=$(curl --connect-timeout 5 -m 5 -s -H "$HEADER" -H "Content-Type: application/json" -X POST -d '{"drive_id": "'$drive_id'","parent_file_id": "'$file_id'"}' "https://api.aliyundrive.com/adrive/v2/file/list") 80 | 81 | # 过滤掉最近的文件文件 82 | # 获取当前时间的 Unix 时间戳 83 | current_time=$(date +%s) 84 | # 定义时间间隔(30分钟)的秒数 85 | max_file_age_seconds=$((AUTO_CLEAR_THRESHOLD * 60)) 86 | # 使用 jq 解析和筛选 JSON 数据 87 | _res=$(echo "$_res" | jq -c --argjson now "$current_time" --argjson interval "$max_file_age_seconds" '.items |= map(select(($now - ( .created_at | sub("\\.[0-9]+Z$"; "Z") | fromdateiso8601)) > $interval))') 88 | 89 | if [ ! $? -eq 0 ] || [ -z "$(echo "$_res" | grep "items")" ]; then 90 | echo "获取文件列表失败:folder_id=$file_id,drive_id=$drive_id" >&2 91 | return 1 92 | fi 93 | echo "$_res" 94 | #简单规避小雅转存后还没来得及获取直链就被删除的问题,降低发生概率 95 | sleep "$waittime" 96 | return 0 97 | } 98 | 99 | get_Path() { 100 | _path="$(curl --connect-timeout 5 -m 5 -s -H "$HEADER" -H "Content-Type: application/json" -X POST -d "{\"drive_id\": \"$drive_id\", \"file_id\": \"$file_id\"}" "https://api.aliyundrive.com/adrive/v1/file/get_path" | grep -o "\"name\":\"[^\"]*\"" | cut -d':' -f2- | tr -d '"' | tr '\n' '/' | awk -F'/' '{for(i=NF-1;i>0;i--){printf("/%s",$i)}; printf("%s\n",$NF)}')" 101 | if [ -z "$_path" ]; then 102 | return 1 103 | fi 104 | echo "$_path" 105 | return 0 106 | } 107 | 108 | get_List() { 109 | _res=$raw_list 110 | 111 | #echo "$_res" | tr '{' '\n' | grep -v "folder" | grep -o "\"file_id\":\"[^\"]*\"" | cut -d':' -f2- | tr -d '"' 112 | echo "$_res" | tr '{' '\n' | grep -o "\"file_id\":\"[^\"]*\"" | cut -d':' -f2- | tr -d '"' 113 | return 0 114 | } 115 | 116 | delete_File() { 117 | _file_id=$1 118 | _name="$(echo "$raw_list" | grep -o "\"name\":\"[^\"]*\"" | cut -d':' -f2- | tr -d '"' | grep -n . | grep "^$(echo "$raw_list" | grep -o "\"file_id\":\"[^\"]*\"" | cut -d':' -f2- | tr -d '"' | grep -n . | grep "$_file_id" | awk -F: '{print $1}'):" | awk -F: '{print $2}')" 119 | 120 | _res=$(curl --connect-timeout 5 -m 5 -s -H "$HEADER" -H "Content-Type: application/json" -X POST -d '{ 121 | "requests": [ 122 | { 123 | "body": { 124 | "drive_id": "'$drive_id'", 125 | "file_id": "'$_file_id'" 126 | }, 127 | "headers": { 128 | "Content-Type": "application/json" 129 | }, 130 | "id": "'$_file_id'", 131 | "method": "POST", 132 | "url": "/file/delete" 133 | } 134 | ], 135 | "resource": "file" 136 | }' "https://api.aliyundrive.com/v3/batch" | grep "\"status\":204") 137 | if [ -z "$_res" ]; then 138 | return 1 139 | fi 140 | 141 | drive_root="资源盘" 142 | if [ "$folder_type"x = "b"x ]; then 143 | drive_root="备份盘" 144 | fi 145 | 146 | echo "彻底删除文件:/$drive_root$path/$_name" 147 | 148 | return 0 149 | } 150 | 151 | _clear_aliyun() { 152 | #eval "$(retry_command "get_Header")" 153 | raw_list=$(retry_command "get_rawList") 154 | path=$(retry_command "get_Path") 155 | _list="$(get_List)" 156 | echo "$_list" | sed '/^$/d' | while read line; do 157 | retry_command "delete_File \"$line\"" 158 | done 159 | return "$(echo "$_list" | sed '/^$/d' | wc -l)" 160 | } 161 | 162 | 163 | clear_aliyun() { 164 | eval "$(retry_command "get_Header")" 165 | raw_list=$(retry_command "get_rawList 0") 166 | _list="$(get_List)" 167 | if [ -z "$(echo "$_list" | sed '/^$/d')" ]; then 168 | return 0 169 | fi 170 | 171 | echo -e "\n[$(date '+%Y/%m/%d %H:%M:%S')]开始清理小雅$XIAOYA_NAME转存" 172 | 173 | _res=1 174 | _filenum=0 175 | while [ ! $_res -eq 0 ]; do 176 | _clear_aliyun 177 | _res=$? 178 | _filenum=$(($_filenum + $_res)) 179 | done 180 | 181 | echo "本次共清理小雅$XIAOYA_NAME转存文件$_filenum个" 182 | 183 | } 184 | 185 | init_para() { 186 | XIAOYA_NAME="alist" 187 | refresh_token="$(cat "${DATA_DIR}/mytoken.txt" | head -n1)" 188 | 189 | file_id=$(cat "${DATA_DIR}/temp_transfer_folder_id.txt") 190 | 191 | folder_type=$(cat "${DATA_DIR}/folder_type.txt") 192 | 193 | if [ -z "$refresh_token" ]; then 194 | echo "未找到refresh_token" 195 | return 1 196 | fi 197 | 198 | if [ -z "$file_id" ]; then 199 | echo "未找到file_id" 200 | return 1 201 | fi 202 | 203 | if [ -z "$folder_type" ]; then 204 | echo "未找到folder_type" 205 | return 1 206 | fi 207 | 208 | } 209 | 210 | init_para 211 | clear_aliyun -------------------------------------------------------------------------------- /alist/fix_media.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "等待emby.js创建完成..." 4 | while ! test -f /etc/nginx/http.d/emby.js; do 5 | sleep 2 6 | done 7 | 8 | echo "修复emby.js..." 9 | 10 | sed -i '/async function fetchXYApi/{:a;N;$!ba;d}' /etc/nginx/http.d/emby.js 11 | 12 | cat >> /etc/nginx/http.d/emby.js <<-\EOF 13 | async function fetchXYApi(xyurl) { 14 | xyurl = xyurl.replace(/ /g, '%20'); 15 | try { 16 | const res = await ngx.fetch(xyurl, { headers: { 'Content-Type': 'application/json;charset=utf-8', "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.6045.160 Safari/537.36" }, max_response_body_size: 365535} ); 17 | if (res.status == 302) { 18 | if (res.statusText == "Found") { 19 | return res.headers.Location; 20 | } 21 | return (`error: xy_api return 304 destination is null`); 22 | } 23 | else { 24 | return res.text(); 25 | } 26 | } 27 | catch (error) { 28 | return (`error: xy_api fetch aliyun direct link failed, ${error}`); 29 | } 30 | } 31 | 32 | export default { redirect2Pan }; 33 | EOF 34 | -------------------------------------------------------------------------------- /alist/service.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | DATA_DIR="/www/data" 4 | 5 | base_urls=( 6 | "https://raw.githubusercontent.com/xiaoyaliu00/data/main" 7 | "https://cdn.wygg.shop/https://raw.githubusercontent.com/xiaoyaliu00/data/main" 8 | "https://fastly.jsdelivr.net/gh/xiaoyaliu00/data@latest" 9 | "https://521github.com/extdomains/github.com/xiaoyaliu00/data/raw/main" 10 | "https://cors.zme.ink/https://raw.githubusercontent.com/xiaoyaliu00/data/main" 11 | ) 12 | 13 | files=( 14 | "tvbox.zip" 15 | "update.zip" 16 | "index.zip" 17 | "version.txt" 18 | ) 19 | 20 | if [ ! -d "$DATA_DIR" ]; then 21 | mkdir -p "$DATA_DIR" 22 | fi 23 | 24 | get_valid_url() { 25 | for url in "${base_urls[@]}"; do 26 | if curl -I -m 10 -s -f -o /dev/null "$url/version.txt"; then 27 | echo "$url" 28 | return 0 29 | fi 30 | done 31 | return 1 32 | } 33 | 34 | # 比较DATA_DIR本地version.txt版本号和远程version.txt版本号,不一样则下载文件 35 | download_files() { 36 | remote_url=$(get_valid_url) 37 | if [ -z "$remote_url" ]; then 38 | echo "Failed to get valid url" 39 | return 1 40 | fi 41 | echo "Remote url: $remote_url" 42 | remote_version=$(curl -s "${remote_url}/version.txt") 43 | if [ -z "$remote_version" ]; then 44 | echo "Failed to get remote version" 45 | return 1 46 | fi 47 | local_version="" 48 | if [ -f "${DATA_DIR}/version.txt" ]; then 49 | local_version=$(cat "${DATA_DIR}/version.txt") 50 | fi 51 | if [ "$remote_version" != "$local_version" ]; then 52 | for file in "${files[@]}"; do 53 | echo "Downloading file $file..." 54 | curl -s -o "${DATA_DIR}/${file}" "${remote_url}/${file}" 55 | done 56 | return 0 57 | else 58 | echo "No need to download files" 59 | return 1 60 | fi 61 | } 62 | 63 | start() { 64 | /entrypoint.sh /opt/alist/alist server --no-prefix & 65 | sleep 30 66 | echo "1" > /tmp/status 67 | } 68 | 69 | stop() { 70 | echo "0" > /tmp/status 71 | kill -15 $(pgrep -f 'nginx|httpd|alist') 72 | } 73 | 74 | restart() { 75 | stop 76 | sleep 10 77 | start 78 | } 79 | 80 | update() { 81 | download_files 82 | restart 83 | } 84 | 85 | # 进程守护函数 86 | daemon() { 87 | 88 | if [ -z "$(pgrep alist)" ] && [ "$(cat /tmp/status 2> /dev/null)" = "1" ]; then 89 | start 90 | fi 91 | 92 | } 93 | 94 | case "$1" in 95 | update) 96 | update 97 | ;; 98 | download) 99 | download_files 100 | ;; 101 | start) 102 | start 103 | ;; 104 | stop) 105 | stop 106 | ;; 107 | restart) 108 | restart 109 | ;; 110 | daemon) 111 | daemon 112 | ;; 113 | *) 114 | echo "Usage: $0 {update|download}" 115 | exit 1 116 | ;; 117 | esac 118 | -------------------------------------------------------------------------------- /alist/start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -eu 4 | 5 | echo "开始生成配置文件..." 6 | 7 | if [ ! -d "/data" ]; then 8 | mkdir /data 9 | fi 10 | 11 | # 设置端口 12 | local_ip=$(ip a | grep inet | grep -v '127.0.0.1|inet6' | awk '{print $2}' | cut -d/ -f1) 13 | echo "http://$local_ip:5678" > /data/docker_address.txt 14 | 15 | # 生成配置,阿里云token 16 | if [ ${#ALIYUN_TOKEN} -ne 32 ]; then 17 | echo "长度不对,阿里云盘 Token是32位长" 18 | echo -e "启动停止,请参考指南配置文件\nhttps://alist.nn.ci/zh/guide/drivers/aliyundrive.html \n" 19 | exit 1 20 | else 21 | echo "添加阿里云盘 Token..." 22 | echo "${ALIYUN_TOKEN}" > /data/mytoken.txt 23 | fi 24 | 25 | # 生成配置,阿里云open token 26 | if [[ ${#ALIYUN_OPEN_TOKEN} -le 334 ]]; then 27 | echo "长度不对,阿里云盘 Open Token是335位" 28 | echo -e "安装停止,请参考指南配置文件\nhttps://alist.nn.ci/zh/guide/drivers/aliyundrive_open.html \n" 29 | exit 1 30 | else 31 | echo "添加阿里云盘 Open Token..." 32 | echo "${ALIYUN_OPEN_TOKEN}" > /data/myopentoken.txt 33 | fi 34 | 35 | # 生成配置,阿里云转存目录folder_id 36 | if [ ${#ALIYUN_FOLDER_ID} -ne 40 ]; then 37 | echo "长度不对,阿里云盘 folder id是40位长" 38 | echo -e "安装停止,请转存以下目录到你的网盘,并获取该文件夹的folder_id\nhttps://www.aliyundrive.com/s/rP9gP3h9asE \n" 39 | exit 1 40 | else 41 | echo "添加阿里云盘 Folder ID..." 42 | echo "${ALIYUN_FOLDER_ID}" > /data/temp_transfer_folder_id.txt 43 | fi 44 | 45 | # 设置夸克网盘cookie 46 | if [ -n "${QUARK_COOKIE:-}" ]; then 47 | echo "添加夸克网盘 Cookie..." 48 | echo "${QUARK_COOKIE}" > /data/quark_cookie.txt 49 | else 50 | rm -rf /data/quark_cookie.txt 51 | fi 52 | 53 | # 设置115 cookie 54 | if [ -n "${PAN115_COOKIE:-}" ]; then 55 | echo "添加115网盘 Cookie..." 56 | echo "${PAN115_COOKIE}" > /data/115_cookie.txt 57 | if [ "${ALIYUN_TO_115}" = "true" ]; then 58 | cat > /data/ali2115.txt <<-EOF 59 | purge_ali_temp=true 60 | cookie="${PAN115_COOKIE}" 61 | purge_pan115_temp=true 62 | dir_id=${PAN115_FOLDER_ID:-0} 63 | EOF 64 | else 65 | rm -rf /data/ali2115.txt 66 | fi 67 | else 68 | rm -rf /data/115_cookie.txt 69 | fi 70 | 71 | # 设置pikpak用户密码观看pikpak资源 72 | if [ -n "${PIKPAK_USER:-}" ]; then 73 | echo "设置PIKPAK用户密码..." 74 | rm -rf /data/pikpak.txt 75 | user=$(echo "${PIKPAK_USER}" | cut -d':' -f1) 76 | pass=$(echo "${PIKPAK_USER}" | cut -d':' -f2-) 77 | echo "\"${user}\" \"${pass}\"" > /data/pikpak.txt 78 | else 79 | rm -rf /data/pikpak.txt 80 | fi 81 | 82 | # 挂载你自己 pikpak 账号 83 | if [ -n "${PIKPAK_LIST:-}" ]; then 84 | echo "挂载PIKPAK分享..." 85 | rm -rf /data/pikpak_list.txt 86 | echo "${PIKPAK_LIST}" | tr ',' '\n' | while read -r line; do 87 | name=$(echo "$line" | cut -d':' -f1) 88 | user=$(echo "$line" | cut -d':' -f2) 89 | pass=$(echo "$line" | cut -d':' -f3-) 90 | echo "${name} \"${user}\" \"${pass}\"" >> /data/pikpak_list.txt 91 | done 92 | else 93 | rm -rf /data/pikpak_list.txt 94 | fi 95 | 96 | # 挂载pikpak分享,覆盖小雅的分享 97 | if [ -n "${PIKPAK_SHARE_LIST:-}" ]; then 98 | echo "挂载额外的PIKPAK分享..." 99 | rm -rf /data/pikpakshare_list.txt 100 | echo "${PIKPAK_SHARE_LIST}" | tr ',' '\n' | while read -r line; do 101 | name=$(echo "$line" | cut -d':' -f1) 102 | share_id=$(echo "$line" | cut -d':' -f2) 103 | folder_id=$(echo "$line" | cut -d':' -f3) 104 | echo "${name} ${share_id} ${folder_id}" >> /data/pikpakshare_list.txt 105 | done 106 | else 107 | rm -rf /data/pikpakshare_list.txt 108 | fi 109 | 110 | # 挂载额外的阿里云盘分享 111 | if [ -n "${ALI_SHARE_LIST:-}" ]; then 112 | echo "挂载额外的PIKPAK分享..." 113 | rm -rf /data/alishare_list.txt 114 | echo "${ALI_SHARE_LIST}" | tr ',' '\n' | while read -r line; do 115 | name=$(echo "$line" | cut -d':' -f1) 116 | share_id=$(echo "$line" | cut -d':' -f2) 117 | folder_id=$(echo "$line" | cut -d':' -f3) 118 | echo "${name} ${share_id} ${folder_id}" >> /data/alishare_list.txt 119 | done 120 | else 121 | rm -rf /data/alishare_list.txt 122 | fi 123 | 124 | # 挂载额外的夸克网盘分享 125 | if [ -n "${QUARK_SHARE_LIST:-}" ]; then 126 | echo "挂载额外的夸克网盘分享..." 127 | rm -rf /data/quarkshare_list.txt 128 | echo "${QUARK_SHARE_LIST}" | tr ',' '\n' | while read -r line; do 129 | name=$(echo "$line" | cut -d':' -f1) 130 | share_id=$(echo "$line" | cut -d':' -f2) 131 | folder_id=$(echo "$line" | cut -d':' -f3) 132 | code=$(echo "$line" | cut -d':' -f4) 133 | echo "${name} ${share_id} ${folder_id} ${code}" >> /data/quarkshare_list.txt 134 | done 135 | else 136 | rm -rf /data/quarkshare_list.txt 137 | fi 138 | 139 | # 挂载额外的115网盘分享 140 | if [ -n "${PAN115_SHARE_LIST:-}" ]; then 141 | echo "挂载额外的115网盘分享..." 142 | rm -rf /data/115share_list.txt 143 | echo "${PAN115_SHARE_LIST}" | tr ',' '\n' | while read -r line; do 144 | name=$(echo "$line" | cut -d':' -f1) 145 | share_id=$(echo "$line" | cut -d':' -f2) 146 | folder_id=$(echo "$line" | cut -d':' -f3) 147 | code=$(echo "$line" | cut -d':' -f4) 148 | echo "${name} ${share_id} ${folder_id} ${code}" >> /data/115share_list.txt 149 | done 150 | else 151 | rm -rf /data/115share_list.txt 152 | fi 153 | 154 | # 开启tvbox随机订阅 155 | if [ "${TVBOX_SECURITY:=false}" = "true" ]; then 156 | echo "已开启TVBOX安全模式..." 157 | if [ ! -f /data/tvbox_security.txt ]; then 158 | touch /data/tvbox_security.txt 159 | fi 160 | else 161 | echo "已关闭TVBOX安全模式..." 162 | rm -rf /data/tvbox_security.txt 163 | fi 164 | 165 | # 开启代理 166 | if [ -n "${PROXY:-}" ]; then 167 | echo "已开启代理..." 168 | echo "${PROXY}" > /data/proxy.txt 169 | else 170 | rm -rf /data/proxy.txt 171 | fi 172 | 173 | # 设置webdav密码 174 | if [ -n "${WEBDAV_PASSWORD:-}" ]; then 175 | echo "设置webdav密码..." 176 | # 设置webdav密码后自动开启强制登陆 177 | echo "${WEBDAV_PASSWORD}" > /data/guestpass.txt 178 | if [ ! -f /data/guestlogin.txt ]; then 179 | touch /data/guestlogin.txt 180 | fi 181 | else 182 | rm -rf /data/guestpass.txt 183 | # 关闭强制登陆 184 | rm -rf /data/guestlogin.txt 185 | fi 186 | 187 | # 设置数据下载目录 188 | echo "http://127.0.0.1:5233/data" > /data/download_url.txt 189 | 190 | crontabs="" 191 | 192 | if [ "${AUTO_CLEAR_ENABLED:=true}" = "true" ]; then 193 | echo "启动定时清理定时任务..." 194 | AUTO_CLEAR_INTERVAL=${AUTO_CLEAR_INTERVAL:=10} 195 | HOURS=$(($AUTO_CLEAR_INTERVAL / 60)) 196 | REMAINING_MINUTES=$(($AUTO_CLEAR_INTERVAL % 60)) 197 | 198 | if [ $HOURS -gt 0 ]; then 199 | if [ $REMAINING_MINUTES -eq 0 ]; then 200 | CRONTAB_TIME="0 */$HOURS * * *" 201 | else 202 | CRONTAB_TIME="$REMAINING_MINUTES */$HOURS * * *" 203 | fi 204 | else 205 | CRONTAB_TIME="*/$REMAINING_MINUTES * * * *" 206 | fi 207 | crontabs="${crontabs}\n$CRONTAB_TIME /clear.sh" 208 | #crontabs="${crontabs}\n*/${AUTO_CLEAR_INTERVAL:=10} * * * * /clear.sh" 209 | fi 210 | 211 | # 添加后台守护 212 | crontabs="${crontabs}\n* * * * * /service.sh daemon" 213 | 214 | if [ -n "${crontabs}" ]; then 215 | echo -e "$crontabs" | crontab - 216 | fi 217 | 218 | # 设置本地变量 219 | echo "${EMBY_APIKEY:-e825ed6f7f8f44ffa0563cddaddce14d}" > /data/infuse_api_key.txt 220 | 221 | if [ "${AUTO_UPDATE_MEDIA_ADDR:=false}" = "true" ]; then 222 | echo "开始自动更新媒体服务地址..." 223 | /update_media_addr.sh &> /dev/null & 224 | fi 225 | 226 | /fix_media.sh & 227 | 228 | crond 229 | 230 | /entrypoint.sh /opt/alist/alist server --no-prefix 231 | 232 | -------------------------------------------------------------------------------- /alist/update_media_addr.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | EMBY_ADDR=${EMBY_ADDR:-http://emby:6908} 4 | UPDATE_MEDIA_ADDR_INTERVAL=${UPDATE_MEDIA_ADDR_INTERVAL:-60} 5 | 6 | # 如果是IP地址,直接写入文件,不需要更新 7 | EMBY_DOMAIN=$(echo "${EMBY_ADDR}" | sed -e 's#http://##' -e 's#https://##' -e 's#:[0-9]*##' -e 's#/$##') 8 | EMBY_END=0 9 | 10 | if [[ "${EMBY_DOMAIN}" =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then 11 | echo "Emby IP address: ${EMBY_DOMAIN}" 12 | echo "${EMBY_ADDR}" > /data/emby_server.txt 13 | EMBY_END=1 14 | fi 15 | 16 | if [ $EMBY_END -eq 1 ]; then 17 | exit 0 18 | fi 19 | 20 | # 解析域名为 IP 21 | get_addr() { 22 | ADDR=$1 23 | if [[ $ADDR =~ ^(https?)://([^:/]+)(:[0-9]+)?$ ]]; then 24 | PROTOCOL=${BASH_REMATCH[1]} 25 | DOMAIN_OR_IP=${BASH_REMATCH[2]} 26 | PORT=${BASH_REMATCH[3]} 27 | 28 | # 默认端口设置 29 | if [ -z "$PORT" ]; then 30 | if [ "$PROTOCOL" == "http" ]; then 31 | PORT=":80" 32 | elif [ "$PROTOCOL" == "https" ]; then 33 | PORT=":443" 34 | fi 35 | fi 36 | 37 | # 解析域名为 IP 38 | IP=$(ping -c 1 "$DOMAIN_OR_IP" | grep -o '\b([0-9]{1,3}\.){3}[0-9]{1,3}\b' | head -n 1) 39 | if [ -z "$IP" ]; then 40 | return 1 41 | fi 42 | echo "$PROTOCOL://$IP$PORT" 43 | return 0 44 | else 45 | echo 46 | return 1 47 | fi 48 | } 49 | 50 | echo "等待emby.js创建完成..." 51 | while ! test -f /etc/nginx/http.d/emby.js; do 52 | sleep 2 53 | done 54 | 55 | echo "开始更新媒体服务地址..." 56 | # 设置一个循环每1分钟检查一次emby的地址是否变化,如果变化就更新/data/emby_server.txt 57 | while true; do 58 | nginx_reload=0 59 | if [ "${EMBY_END}" -eq 0 ]; then 60 | NEW_EMBY_ADDR=$(get_addr "${EMBY_ADDR}") 61 | if [ -n "$NEW_EMBY_ADDR" ]; then 62 | if [ "$NEW_EMBY_ADDR" != "$OLD_EMBY_ADDR" ]; then 63 | echo "$NEW_EMBY_ADDR" > /data/emby_server.txt 64 | # 更新nginx配置 65 | sed -i "s#set \$emby .*#set \$emby ${NEW_EMBY_ADDR};#" /etc/nginx/http.d/emby.conf 66 | sed -i "s#const embyHost .*#const embyHost = '${NEW_EMBY_ADDR}';#" /etc/nginx/http.d/emby.js 67 | nginx_reload=1 68 | OLD_EMBY_ADDR=$NEW_EMBY_ADDR 69 | echo "Updated emby address to $NEW_EMBY_ADDR" 70 | fi 71 | else 72 | echo "Error: Failed to resolve IP address for ${EMBY_ADDR}" 73 | fi 74 | fi 75 | 76 | if [ "$nginx_reload" -eq 1 ]; then 77 | echo "Reloading nginx..." 78 | nginx -s reload 79 | fi 80 | 81 | echo "Waiting for $UPDATE_MEDIA_ADDR_INTERVAL seconds..." 82 | sleep "${UPDATE_MEDIA_ADDR_INTERVAL}" 83 | done 84 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | service="$1" 4 | services=( 5 | "alist" 6 | "emby" 7 | "metadata" 8 | ) 9 | 10 | if [ -z "$service" ]; then 11 | for service in "${services[@]}"; do 12 | docker build -t "monlor/xiaoya-$service" "$service" 13 | done 14 | else 15 | docker build -t "monlor/xiaoya-$service" "$service" 16 | fi 17 | 18 | -------------------------------------------------------------------------------- /docker-compose-alist.yml: -------------------------------------------------------------------------------- 1 | services: 2 | alist: 3 | image: ghcr.io/monlor/xiaoya-alist:latest 4 | volumes: 5 | - xiaoya:/data 6 | ports: 7 | - "5678:5678" 8 | - "2345:2345" 9 | - "2346:2346" 10 | env_file: 11 | - env 12 | restart: unless-stopped 13 | networks: 14 | - default 15 | 16 | networks: 17 | default: 18 | 19 | volumes: 20 | xiaoya: -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | alist: 3 | image: ghcr.io/monlor/xiaoya-alist:latest 4 | volumes: 5 | - xiaoya:/data 6 | ports: 7 | - "5678:5678" 8 | - "2345:2345" 9 | - "2346:2346" 10 | environment: 11 | - AUTO_UPDATE_MEDIA_ADDR=true 12 | env_file: 13 | - env 14 | restart: unless-stopped 15 | networks: 16 | - default 17 | metadata: 18 | image: ghcr.io/monlor/xiaoya-metadata:latest 19 | env_file: 20 | - env 21 | volumes: 22 | - xiaoya:/etc/xiaoya 23 | - media:/media/xiaoya 24 | - config:/media/config 25 | - cache:/media/config/cache 26 | - meta:/media/temp 27 | depends_on: 28 | - alist 29 | restart: unless-stopped 30 | networks: 31 | - default 32 | emby: 33 | image: ghcr.io/monlor/xiaoya-embyserver:latest 34 | env_file: 35 | - env 36 | depends_on: 37 | - metadata 38 | - alist 39 | volumes: 40 | - media:/media 41 | - config:/config 42 | - cache:/cache 43 | ports: 44 | - "6908:6908" 45 | restart: unless-stopped 46 | networks: 47 | - default 48 | 49 | networks: 50 | default: 51 | 52 | volumes: 53 | xiaoya: 54 | media: 55 | config: 56 | meta: 57 | cache: -------------------------------------------------------------------------------- /emby/Dockerfile: -------------------------------------------------------------------------------- 1 | ARG BASE_IMAGE=ghcr.io/monlor/embyserver:latest 2 | 3 | FROM alpine as base 4 | 5 | RUN apk add dumb-init 6 | 7 | FROM ${BASE_IMAGE} 8 | 9 | LABEL MAINTAINER me@monlor.com 10 | 11 | ENV TZ=Asia/Shanghai 12 | 13 | ARG TARGETARCH 14 | 15 | ENV PUID=0 PGID=0 16 | 17 | COPY --chmod=755 --from=base /usr/bin/dumb-init /dumb-init 18 | 19 | COPY --chmod=755 --from=msoap/shell2http:1.17.0 /app/shell2http /usr/bin/shell2http 20 | 21 | COPY --chmod=755 ./*.sh / 22 | 23 | ENTRYPOINT [ "/dumb-init", "/entrypoint.sh" ] -------------------------------------------------------------------------------- /emby/README.md: -------------------------------------------------------------------------------- 1 | ## Emby 小雅版本 2 | 3 | * 添加依赖服务检查,等待Alist和元数据处理完成 4 | * 解析小雅Alist的ip,自动更新 5 | 6 | ## 环境变量 7 | 8 | `ALIST_ADDR`: 小雅alist的地址,默认http://alist:5678,容器内部使用地址,一般不用改 9 | 10 | ## 其他镜像 11 | 12 | > 此镜像仅支持 x86,arm64 架构 13 | 14 | ``` 15 | ghcr.io/monlor/xiaoya-embyserver-amilys 16 | ``` -------------------------------------------------------------------------------- /emby/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | ALIST_ADDR=${ALIST_ADDR:-http://alist:5678} 4 | 5 | echo "检查alist连通性..." 6 | while ! wget -Y off -q -T 1 -O - "${ALIST_ADDR}/api/public/settings" 2> /dev/null | grep -q 200; do 7 | sleep 2 8 | done 9 | 10 | echo "等待配置数据下载完成..." 11 | while test ! -f /config/emby_meta_finished; do 12 | sleep 2 13 | done 14 | 15 | echo "等待媒体数据下载完成..." 16 | while test ! -f /media/emby_media_finished; do 17 | sleep 2 18 | done 19 | 20 | cat > /etc/nsswitch.conf <<-EOF 21 | hosts: files dns 22 | networks: files 23 | EOF 24 | 25 | echo "开始自动更新alist地址..." 26 | /update_alist_addr.sh > /dev/null 2>&1 & 27 | 28 | echo "启动emby服务..." 29 | /service.sh start 30 | sleep 2 31 | 32 | echo "启动进程守护..." 33 | /service.sh daemon & 34 | 35 | exec shell2http -port 8080 /stop "/service.sh stop" /start "/service.sh start" 36 | -------------------------------------------------------------------------------- /emby/service.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 1. 启动函数 4 | start() { 5 | 6 | LD_LIBRARY_PATH=/lib:/system /system/EmbyServer -programdata /config -ffdetect /bin/ffdetect -ffmpeg /bin/ffmpeg -ffprobe /bin/ffprobe -restartexitcode 3 & 7 | echo "1" > /tmp/embyserver_status 8 | 9 | } 10 | 11 | # 2. 停止函数 12 | stop() { 13 | 14 | killall -15 EmbyServer 15 | echo "0" > /tmp/embyserver_status 16 | 17 | } 18 | 19 | # 3. 进程守护函数 20 | daemon() { 21 | 22 | while true; do 23 | if [ -z "$(pgrep EmbyServer)" ] && [ "$(cat /tmp/embyserver_status)" = "1" ]; then 24 | start 25 | fi 26 | sleep 5 27 | done 28 | 29 | } 30 | 31 | case $1 in 32 | start) 33 | start 34 | ;; 35 | stop) 36 | stop 37 | ;; 38 | daemon) 39 | daemon 40 | ;; 41 | *) 42 | echo "Usage: $0 {start|stop|daemon}" 43 | exit 1 44 | ;; 45 | esac -------------------------------------------------------------------------------- /emby/update_alist_addr.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | ALIST_ADDR=${ALIST_ADDR:-http://alist:5678} 4 | UPDATE_ALIST_ADDR_INTERVAL=${UPDATE_ALIST_ADDR_INTERVAL:-60} 5 | 6 | # 解析ALIST_ADDR里面的域名或ip 7 | ALIST_DOMAIN=$(echo "${ALIST_ADDR}" | sed -e 's#http://##' -e 's#https://##' -e 's#:[0-9]*##' -e 's#/$##') 8 | 9 | add_hosts() { 10 | # 容器里不能使用sed -i,所以使用临时文件 11 | sed -e "/xiaoya.host/d" /etc/hosts > /tmp/hosts 12 | printf "%s\txiaoya.host\n" "$1" >> /tmp/hosts 13 | cat /tmp/hosts > /etc/hosts 14 | } 15 | 16 | if echo "${ALIST_DOMAIN}" | grep -E -q '^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$'; then 17 | IP="${ALIST_DOMAIN}" 18 | echo "Alist IP address: $IP" 19 | add_hosts "$IP" 20 | exit 0 21 | fi 22 | 23 | while true; do 24 | IP=$(ping -c 1 "${ALIST_DOMAIN}" | grep -Eo '\b([0-9]{1,3}\.){3}[0-9]{1,3}\b' | head -n 1) 25 | if [ -n "$IP" ]; then 26 | echo "Alist IP address: $IP" 27 | add_hosts "$IP" 28 | else 29 | echo "Failed to resolve Alist domain ${ALIST_DOMAIN}..." 30 | fi 31 | 32 | echo "Waiting for $UPDATE_ALIST_ADDR_INTERVAL seconds..." 33 | sleep "${UPDATE_ALIST_ADDR_INTERVAL}" 34 | done 35 | -------------------------------------------------------------------------------- /env: -------------------------------------------------------------------------------- 1 | TZ=Asia/Shanghai 2 | # 阿里云盘token 3 | ALIYUN_TOKEN= 4 | # 阿里云盘open token 5 | ALIYUN_OPEN_TOKEN= 6 | # 阿里云盘文件夹ID 7 | ALIYUN_FOLDER_ID= 8 | # 夸克网盘的cookie 9 | QUARK_COOKIE= 10 | # 115网盘的cookie 11 | PAN115_COOKIE= 12 | # 阿里云盘转存115播放 13 | ALIYUN_TO_115= 14 | # 115网盘文件夹ID 15 | PAN115_FOLDER_ID= 16 | # 自动清理阿里云盘 17 | AUTO_CLEAR_ENABLED= 18 | # 阿里云盘自动清理间隔,单位分钟,范围0-60分钟,默认10分钟 19 | AUTO_CLEAR_INTERVAL= 20 | # pikpak 账号,用来观看小雅中pikpak分享给你的资源,格式:`qqq@qq.com:aaadds` 21 | PIKPAK_USER= 22 | # 开启tvbox随机订阅地址,true/false,默认false 23 | TVBOX_SECURITY= 24 | # webdav用户名为dav,设置密码。默认用户密码:guest/guest_Api789 25 | WEBDAV_PASSWORD= 26 | 27 | # emby 地址,容器内部使用地址,一般不用改 28 | EMBY_ADDR=http://emby:6908 29 | # alist 地址,容器内部使用地址,一般不用改 30 | ALIST_ADDR=http://alist:5678 31 | 32 | # 是否启用emby 33 | EMBY_ENABLED=true 34 | # 自动更新emby的配置,依赖EMBY_APIKEY 35 | AUTO_UPDATE_EMBY_CONFIG_ENABLED=false 36 | # 自动更新emby配置,config.mp4间隔,单位天,默认7天 37 | AUTO_UPDATE_EMBY_INTERVAL= 38 | # 自动更新emby元数据 39 | AUTO_UPDATE_EMBY_METADATA_ENABLED=true 40 | # emby的apikey 41 | EMBY_APIKEY= 42 | # 下载解压完是否清除源文件,true/false,默认false 43 | CLEAR_TEMP= 44 | -------------------------------------------------------------------------------- /helmfile.yaml: -------------------------------------------------------------------------------- 1 | # 定义全局的repositories 2 | repositories: 3 | - name: monlor 4 | url: https://monlor.github.io/helm-charts 5 | 6 | releases: 7 | - name: alist 8 | namespace: media 9 | chart: monlor/quickchart 10 | version: 0.3.2 11 | values: 12 | - port: 5678 13 | extraPort: 14 | - port: 2345 15 | protocol: TCP 16 | - port: 2346 17 | protocol: TCP 18 | nodeSelector: 19 | kubernetes.io/hostname: nuc-1-node-1 20 | image: 21 | repository: ghcr.io 22 | name: monlor/xiaoya-alist 23 | tag: latest 24 | pullPolicy: Always 25 | env: 26 | TZ: Asia/Shanghai 27 | AUTO_CLEAR_ENABLED: true 28 | WEBDAV_PASSWORD: 29 | ALIYUN_TOKEN: 30 | ALIYUN_OPEN_TOKEN: 31 | ALIYUN_FOLDER_ID: 32 | QUARK_COOKIE: 33 | PAN115_COOKIE: 34 | PIKPAK_USER: 35 | volumes: 36 | - name: data 37 | mountPath: /data 38 | hostPath: 39 | path: /opt/xiaoya/data 40 | type: DirectoryOrCreate 41 | health: 42 | enabled: false 43 | strategy: 44 | type: Recreate 45 | resources: 46 | limits: 47 | cpu: 2 48 | memory: 4Gi 49 | requests: 50 | cpu: 100m 51 | memory: 1Gi 52 | ingress: 53 | enabled: true 54 | className: nginx 55 | clusterIssuer: cloudflare 56 | hosts: 57 | - host: alist.monlor.cn 58 | - host: emby.monlor.cn 59 | port: 2345 60 | 61 | - name: metadata 62 | namespace: media 63 | chart: monlor/quickchart 64 | version: 0.3.2 65 | values: 66 | - nodeSelector: 67 | kubernetes.io/hostname: nuc-1-node-1 68 | image: 69 | repository: ghcr.io 70 | name: monlor/xiaoya-metadata 71 | tag: latest 72 | pullPolicy: Always 73 | env: 74 | TZ: Asia/Shanghai 75 | # ALIST_ADDR: http://alist:5678 76 | EMBY_ENABLED: true 77 | AUTO_UPDATE_EMBY_CONFIG_ENABLED: true 78 | AUTO_UPDATE_EMBY_METADATA_ENABLED: true 79 | volumes: 80 | - name: data 81 | mountPath: /etc/xiaoya 82 | hostPath: 83 | path: /opt/xiaoya/data 84 | type: DirectoryOrCreate 85 | - name: media 86 | mountPath: /media/xiaoya 87 | hostPath: 88 | path: /opt/xiaoya/media 89 | type: DirectoryOrCreate 90 | - name: config 91 | mountPath: /media/config 92 | hostPath: 93 | path: /opt/xiaoya/config 94 | type: DirectoryOrCreate 95 | - name: meta 96 | mountPath: /media/temp 97 | hostPath: 98 | path: /opt/xiaoya/temp 99 | type: DirectoryOrCreate 100 | health: 101 | enabled: false 102 | strategy: 103 | type: Recreate 104 | resources: 105 | limits: 106 | cpu: 4 107 | memory: 8Gi 108 | requests: 109 | cpu: 100m 110 | memory: 512Mi 111 | 112 | - name: emby 113 | namespace: media 114 | chart: monlor/quickchart 115 | version: 0.3.2 116 | values: 117 | - port: 6908 118 | extraPort: 119 | - port: 8080 120 | protocol: TCP 121 | nodeSelector: 122 | kubernetes.io/hostname: nuc-1-node-1 123 | image: 124 | repository: ghcr.io 125 | name: monlor/xiaoya-embyserver 126 | tag: latest 127 | pullPolicy: Always 128 | env: 129 | TZ: Asia/Shanghai 130 | # ALIST_ADDR: http://alist:5678 131 | volumes: 132 | - name: media 133 | mountPath: /media 134 | hostPath: 135 | path: /opt/xiaoya/media 136 | type: DirectoryOrCreate 137 | - name: config 138 | mountPath: /config 139 | hostPath: 140 | path: /opt/xiaoya/config 141 | type: DirectoryOrCreate 142 | - name: cache 143 | mountPath: /cache 144 | hostPath: 145 | path: /opt/xiaoya/config/cache 146 | type: DirectoryOrCreate 147 | health: 148 | enabled: false 149 | strategy: 150 | type: Recreate 151 | resources: 152 | limits: 153 | cpu: 4 154 | memory: 8Gi 155 | requests: 156 | cpu: 500m 157 | memory: 1Gi 158 | # ingress: 159 | # enabled: true 160 | # className: nginx 161 | # clusterIssuer: cloudflare 162 | # hosts: 163 | # - host: emby.monlor.cn 164 | -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -eu 4 | 5 | sedsh() { 6 | if [[ "$(uname -o)" = "Darwin" ]]; then 7 | # macOS 8 | sed -i '' "$@" 9 | else 10 | # Linux 11 | sed -i "$@" 12 | fi 13 | } 14 | 15 | # 解除read输入长度限制 16 | if [[ "$(uname -o)" = "Darwin" ]]; then 17 | stty -icanon 18 | fi 19 | 20 | # 格式https://xxx.com/ 21 | GH_PROXY="${GH_PROXY:=}" 22 | # 格式xxx.com 23 | IMAGE_PROXY="${IMAGE_PROXY:=}" 24 | 25 | # 服务镜像 26 | IMAGE_TAG="${VERSION:-latest}" 27 | # 服务下载地址 28 | DOWNLOAD_URL="${GH_PROXY}https://raw.githubusercontent.com/monlor/docker-xiaoya/${VERSION:-main}" 29 | 30 | # 欢迎信息 31 | echo "欢迎使用xiaoya服务部署脚本" 32 | echo "项目地址:https://github.com/monlor/docker-xiaoya" 33 | echo "作者:monlor (https://link.monlor.com)" 34 | echo 35 | 36 | # 检查docker服务是否存在,不存在则询问用户是否安装,不安装退出脚本 37 | if ! command -v docker &> /dev/null; then 38 | if [ "$(uname -o)" = "Darwin" ]; then 39 | echo "Docker 未安装,请安装docker后再运行脚本,推荐OrbStack:https://orbstack.dev/" 40 | exit 1 41 | fi 42 | read -rp "Docker 未安装,是否安装?(y/n): " install 43 | if [ "$install" = "y" ]; then 44 | echo "安装docker..." 45 | curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun 46 | systemctl enable docker 47 | systemctl start docker 48 | else 49 | echo "退出安装" 50 | exit 1 51 | fi 52 | fi 53 | 54 | DOCKER_COMPOSE="docker compose" 55 | 56 | # 检查是否安装了compose插件,docker compose 命令 57 | if ! docker compose &> /dev/null && ! which docker-compose &> /dev/null; then 58 | read -rp "Docker Compose 未安装,是否安装?(y/n): " install 59 | if [ "$install" = "y" ]; then 60 | echo "安装docker compose..." 61 | # 判断系统是x86还是arm,arm有很多种类,都要判断 62 | if [ "$(uname -m)" = "aarch64" ]; then 63 | file=docker-compose-linux-aarch64 64 | elif [ "$(uname -m)" = "x86_64" ]; then 65 | file=docker-compose-linux-x86_64 66 | else 67 | echo "不支持的系统架构$(uname -m), 请自行安装docker compose(https://docs.docker.com/compose/install/linux/#install-using-the-repository)" 68 | exit 1 69 | fi 70 | curl -SL "${GH_PROXY}https://github.com/docker/compose/releases/download/v2.27.1/$file" -o /usr/local/bin/docker-compose 71 | chmod +x /usr/local/bin/docker-compose 72 | else 73 | echo "退出安装" 74 | exit 1 75 | fi 76 | fi 77 | 78 | if ! docker compose &> /dev/null; then 79 | DOCKER_COMPOSE="docker-compose" 80 | fi 81 | 82 | # 让用户输入服务部署目录,默认/opt/xiaoya 83 | read -rp "请输入服务部署目录(默认/opt/xiaoya): " install_path 84 | install_path=${install_path:=/opt/xiaoya} 85 | 86 | # 检查服务是否已经运行 87 | update=0 88 | if [ -f "$install_path/docker-compose.yml" ]; then 89 | # 询问用户是否要更新服务 90 | cat <<-EOF 91 | 92 | 更新方式: 93 | 1. 全部更新,会覆盖更新docker-compose.yml和env配置 94 | 2. 部分更新,仅覆盖更新docker-compose.yml 95 | 3. 退出脚本,不更新 96 | EOF 97 | read -rp "请选择更新方式(默认为1): " update 98 | update=${update:-1} 99 | case $update in 100 | 1|2) 101 | # 备份 102 | cp -rf "$install_path/env" "$install_path/env.bak" 103 | cp -rf "$install_path/docker-compose.yml" "$install_path/docker-compose.yml.bak" 104 | ;; 105 | *) 106 | echo "退出安装" 107 | exit 1 108 | ;; 109 | esac 110 | 111 | fi 112 | 113 | DOCKER_HOME="$(docker info | grep "Docker Root Dir" | awk -F ':' '{print$2}')" 114 | 115 | # 选择数据保存位置 116 | data_location=1 117 | if [ -d "$install_path/data" ]; then 118 | data_location=2 119 | fi 120 | cat <<-EOF 121 | 122 | 数据保存位置: 123 | 1. Docker卷(数据保存在: ${DOCKER_HOME}/volumes) 124 | 2. 服务部署目录(数据保存在: ${install_path}) 125 | EOF 126 | read -rp "请选择数据保存位置(默认为${data_location}): " res 127 | data_location=${res:-${data_location}} 128 | 129 | token="" 130 | open_token="" 131 | folder_id="" 132 | quark_cookie="" 133 | pan115_cookie="" 134 | aliyun_to_115="false" 135 | pan115_folder_id="" 136 | 137 | # 如果是更新服务,则从原有的compose配置中获取token等信息 138 | if [ "${update}" != "0" ]; then 139 | token=$(grep ALIYUN_TOKEN "$install_path/env" 2> /dev/null | cut -d '=' -f2-) 140 | open_token=$(grep ALIYUN_OPEN_TOKEN "$install_path/env" 2> /dev/null | cut -d '=' -f2-) 141 | folder_id=$(grep ALIYUN_FOLDER_ID "$install_path/env" 2> /dev/null | cut -d '=' -f2-) 142 | quark_cookie=$(grep QUARK_COOKIE "$install_path/env" 2> /dev/null | cut -d '=' -f2-) 143 | pan115_cookie=$(grep PAN115_COOKIE "$install_path/env" 2> /dev/null | cut -d '=' -f2-) 144 | aliyun_to_115=$(grep ALIYUN_TO_115 "$install_path/env" 2> /dev/null | cut -d '=' -f2-) 145 | pan115_folder_id=$(grep PAN115_FOLDER_ID "$install_path/env" 2> /dev/null | cut -d '=' -f2-) 146 | fi 147 | 148 | # 让用户输入阿里云盘TOKEN,token获取方式教程:https://alist.nn.ci/zh/guide/drivers/aliyundrive.html 149 | echo 150 | echo "阿里云盘token获取方式教程:https://alist.nn.ci/zh/guide/drivers/aliyundrive.html" 151 | read -rp "请输入阿里云盘TOKEN(默认为$token): " res 152 | token=${res:=$token} 153 | if [ ${#token} -ne 32 ]; then 154 | echo "长度不对,阿里云盘 Token是32位" 155 | exit 1 156 | fi 157 | 158 | # 让用户输入阿里云盘OpenTOKEN,token获取方式教程:https://alist.nn.ci/zh/guide/drivers/aliyundrive_open.html 159 | echo 160 | echo "阿里云盘Open token获取方式教程:https://alist.nn.ci/zh/guide/drivers/aliyundrive_open.html" 161 | read -rp "请输入阿里云盘Open TOKEN(默认为$open_token): " res 162 | open_token=${res:=$open_token} 163 | if [ ${#open_token} -le 334 ]; then 164 | echo "长度不对,阿里云盘 Open Token是335位" 165 | exit 1 166 | fi 167 | 168 | # 让用户输入阿里云盘转存目录folder_id,folder_id获取方式教程:https://www.aliyundrive.com/s/rP9gP3h9asE 169 | echo 170 | echo "进入阿里云盘网页版,资源盘里面创建一个文件夹,点击文件夹,复制浏览器阿里云盘地址末尾的文件夹ID(最后一个斜杠/后面的一串字符串)" 171 | read -rp "请输入阿里云盘缓存目录ID(默认为$folder_id): " res 172 | folder_id=${res:=$folder_id} 173 | if [ ${#folder_id} -ne 40 ]; then 174 | echo "长度不对,阿里云盘 folder id是40位" 175 | exit 1 176 | fi 177 | 178 | echo 179 | echo "登陆夸克网盘,浏览器F12,点击network,随便点一个请求,找到里面的Cookie值" 180 | read -rp "请输入夸克网盘Cookie值(默认为$quark_cookie): " res 181 | quark_cookie=${res:=$quark_cookie} 182 | 183 | echo 184 | echo "登陆115网盘,浏览器F12,点击network,随便点一个请求,找到里面的Cookie值" 185 | read -rp "请输入115网盘Cookie值(默认为$pan115_cookie): " res 186 | pan115_cookie=${res:=$pan115_cookie} 187 | 188 | if [ -n "${pan115_cookie}" ]; then 189 | read -rp "是否开启将阿里云盘转存到115播放?[y/n]: " res 190 | if [ "${res}" = "y" ]; then 191 | aliyun_to_115="true" 192 | read -rp "请输入115网盘文件夹ID(默认为$pan115_folder_id): " res 193 | pan115_folder_id=${res:=$pan115_folder_id} 194 | if [ -n "${pan115_folder_id}" ] && [ ${#pan115_folder_id} -ne 19 ] && [ "${pan115_folder_id}" != "0" ]; then 195 | echo "长度不对,115网盘 folder id是19位" 196 | exit 1 197 | fi 198 | else 199 | aliyun_to_115="false" 200 | fi 201 | fi 202 | 203 | # 选择部署服务类型,alist + emby (默认), alist 204 | echo 205 | echo "部署类型:" 206 | echo "1. alist + emby (默认)" 207 | echo "2. alist" 208 | read -rp "请选择部署服务类型: " service_type 209 | case $service_type in 210 | 1) 211 | service_type="" 212 | ;; 213 | 2) 214 | service_type="-alist" 215 | ;; 216 | *) 217 | service_type="" 218 | ;; 219 | esac 220 | 221 | 222 | # 检查目录是否存在,不存在则创建 223 | if [ ! -d "$install_path" ]; then 224 | mkdir -p "$install_path" 225 | fi 226 | 227 | cd "$install_path" 228 | 229 | echo "开始生成配置文件docker-compose${service_type}.yml..." 230 | curl -#Lo "$install_path/docker-compose.yml" "${DOWNLOAD_URL}/docker-compose${service_type}.yml" 231 | if [ "${update}" != "2" ]; then 232 | curl -#Lo "$install_path/env" "${DOWNLOAD_URL}/env" 233 | fi 234 | sedsh "s#ALIYUN_TOKEN=.*#ALIYUN_TOKEN=$token#g" env 235 | sedsh "s#ALIYUN_OPEN_TOKEN=.*#ALIYUN_OPEN_TOKEN=$open_token#g" env 236 | sedsh "s#ALIYUN_FOLDER_ID=.*#ALIYUN_FOLDER_ID=$folder_id#g" env 237 | sedsh "s#QUARK_COOKIE=.*#QUARK_COOKIE=$quark_cookie#g" env 238 | sedsh "s#PAN115_COOKIE=.*#PAN115_COOKIE=$pan115_cookie#g" env 239 | sedsh "s#ALIYUN_TO_115=.*#ALIYUN_TO_115=$aliyun_to_115#g" env 240 | sedsh "s#PAN115_FOLDER_ID=.*#PAN115_FOLDER_ID=$pan115_folder_id#g" env 241 | 242 | if [ -n "$IMAGE_PROXY" ]; then 243 | sedsh -E "s#image: [^/]+#image: ${IMAGE_PROXY}#g" docker-compose.yml 244 | fi 245 | 246 | # 修改镜像版本 247 | sedsh "s#:latest#:$IMAGE_TAG#g" docker-compose.yml 248 | 249 | # 修改数据保存位置 250 | if [ "$data_location" = "2" ]; then 251 | sed -n '/^volumes/,$p' ./docker-compose.yml | sed -e 's/://g' | grep -v volumes | while read -r volume; do 252 | if [ -z "${volume}" ]; then 253 | continue 254 | fi 255 | if [ ! -d "$install_path/data/$volume" ]; then 256 | mkdir -p "$install_path/data/$volume" 257 | fi 258 | sedsh "s#- $volume:#- $install_path/data/$volume:#g" docker-compose.yml 259 | done 260 | sedsh "/^volumes/,\$d" docker-compose.yml 261 | fi 262 | 263 | echo "开始部署服务..." 264 | $DOCKER_COMPOSE -f docker-compose.yml up --remove-orphans --pull=always -d 265 | 266 | echo "服务开始部署,如果部署emby,下载并解压60G元数据需要一段时间,请耐心等待..." 267 | echo "脚本执行完成不代表服务启动完成,请执行下面的命令查看日志来检查部署情况." 268 | 269 | echo 270 | echo "> 服务管理(请牢记以下命令)" 271 | # 提示用户compose如何查看日志,启动,重启,停止服务 272 | echo "查看日志:$install_path/manage.sh logs" 273 | # 更新服务 274 | echo "启动服务:$install_path/manage.sh start" 275 | echo "停止服务:$install_path/manage.sh stop" 276 | echo "重启服务:$install_path/manage.sh restart" 277 | echo "加载配置:$install_path/manage.sh reload" 278 | echo "更新服务:$install_path/manage.sh update" 279 | echo "高级用户自定义配置:$install_path/env" 280 | echo "修改env或者compose配置后,需要执行上面的加载配置reload命令生效!" 281 | 282 | # 获取当前服务器ip 283 | ip=$(curl -s ip.3322.net 2> /dev/null) 284 | # 内网ip 285 | local_ip="" 286 | if [[ "$(uname -o)" = "Darwin" ]]; then 287 | interface="$(route -n get default | grep interface | awk -F ':' '{print$2}' | awk '{$1=$1};1')" 288 | local_ip="$(ifconfig "${interface}" | grep 'inet ' | awk '{print$2}')" 289 | else 290 | interface="$(ip route | grep default | awk '{print$5}')" 291 | local_ip="$(ip -o -4 addr show "${interface}" | awk '{print $4}' | cut -d/ -f1)" 292 | fi 293 | 294 | echo 295 | echo "> 服务正在部署,请查看日志等待部署成功后,尝试访问下面的地址" 296 | echo "alist: http://$local_ip:5678, http://$ip:5678" 297 | echo "webdav: http://$local_ip:5678/dav, http://$ip:5678/dav, 默认用户密码: guest/guest_Api789" 298 | echo "tvbox: http://$local_ip:5678/tvbox/my_ext.json, http://$ip:5678/tvbox/my_ext.json" 299 | echo "emby: http://$local_ip:2345, http://$ip:2345, 默认用户密码: xiaoya/1234" 300 | 301 | echo 302 | echo "服务正在后台部署,执行这个命令查看日志:$install_path/manage.sh logs" 303 | echo "部署alist需要10分钟,emby需要1-24小时,请耐心等待..." 304 | # 添加管理脚本,启动,停止,查看日志 305 | cat > "$install_path/manage.sh" <<-EOF 306 | #!/bin/bash 307 | 308 | set -e 309 | 310 | case \$1 in 311 | start) 312 | $DOCKER_COMPOSE -f "$install_path/docker-compose.yml" start 313 | ;; 314 | stop) 315 | $DOCKER_COMPOSE -f "$install_path/docker-compose.yml" stop 316 | ;; 317 | restart) 318 | $DOCKER_COMPOSE -f "$install_path/docker-compose.yml" restart 319 | ;; 320 | reload) 321 | $DOCKER_COMPOSE -f "$install_path/docker-compose.yml" up --remove-orphans -d 322 | ;; 323 | logs) 324 | $DOCKER_COMPOSE -f "$install_path/docker-compose.yml" logs -f 325 | ;; 326 | update) 327 | $DOCKER_COMPOSE -f "$install_path/docker-compose.yml" up --remove-orphans --pull=always -d 328 | ;; 329 | *) 330 | echo "Usage: \$0 {start|stop|restart|reload|logs|update}" 331 | exit 1 332 | ;; 333 | esac 334 | EOF 335 | 336 | chmod +x "$install_path/manage.sh" 337 | -------------------------------------------------------------------------------- /metadata/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.12-slim 2 | 3 | LABEL MAINTAINER me@monlor.com 4 | 5 | ENV TZ=Asia/Shanghai 6 | 7 | WORKDIR /media 8 | 9 | # 设置非交互模式,以避免安装过程中提示用户输入 10 | ARG DEBIAN_FRONTEND=noninteractive 11 | 12 | ENV LANG=C.UTF-8 13 | 14 | RUN apt-get update && \ 15 | apt-get install -y cron locales fonts-wqy-zenhei busybox unzip curl fd-find gzip lsof sqlite3 httpie jq tzdata aria2 p7zip-full && \ 16 | locale-gen C.UTF-8 && \ 17 | ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \ 18 | apt-get autoremove -y && \ 19 | apt-get clean && \ 20 | rm -rf /var/lib/apt/lists/* /tmp/* 21 | 22 | # 安装solid 23 | RUN curl -#LO https://gh.monlor.com/https://github.com/Rik-F5/xiaoya_db/archive/refs/heads/main.zip && \ 24 | unzip main.zip && \ 25 | pip install -r xiaoya_db-main/requirements.txt && \ 26 | mv xiaoya_db-main/solid.py / && \ 27 | rm -rf main.zip xiaoya_db-main 28 | 29 | COPY --chmod=755 ./*.sh / 30 | 31 | ENTRYPOINT [ "/entrypoint.sh" ] 32 | -------------------------------------------------------------------------------- /metadata/README.md: -------------------------------------------------------------------------------- 1 | ## 元数据管理 2 | 3 | 下载更新Emby的元数据 4 | 5 | ## 环境变量 6 | 7 | `AUTO_UPDATE_EMBY_CONFIG_ENABLED`: 自动更新emby配置,下载config.mp4导入config,true/false,默认false 8 | 9 | `AUTO_UPDATE_EMBY_INTERVAL`: 自动更新emby配置间隔,默认7,单位天 10 | 11 | `AUTO_UPDATE_EMBY_METADATA_ENABLED`: 每天自动更新emby元数据,你们所说的爬虫,true/false,默认false 12 | 13 | `EMBY_APIKEY`: emby api 密钥,建议修改emby的api密钥,设置此变量,用于定期同步emby配置 14 | 15 | `EMBY_ADDR`: emby地址,默认:http://emby:6908 16 | 17 | `ALIST_ADDR`: alist地址,默认:http://alist:5678 18 | 19 | `DISK_CHECK_ENABLED`: 磁盘剩余空间检测开关,true/false,默认true 20 | 21 | `CLEAR_TEMP`: 下载解压完是否清除源文件,true/false,默认false 22 | 23 | ## emby数据管理 24 | 25 | > 进入容器执行 26 | 27 | 更新emby配置数据 28 | 29 | ``` 30 | # 更新emby配置 31 | /emby.sh update 32 | # 使用历史config.mp4重置emby数据,无法恢复 33 | /emby.sh reset 34 | # 下载emby配置,仅下载,不更新 35 | /emby.sh download 36 | ``` 37 | 38 | 更新每日元数据 39 | 40 | ``` 41 | python3 /solid.py --media /media/xiaoya 42 | ``` -------------------------------------------------------------------------------- /metadata/emby.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | EMBY_APIKEY=${EMBY_APIKEY:-e825ed6f7f8f44ffa0563cddaddce14d} 6 | EMBY_ADDR=${EMBY_ADDR:-http://emby:6908} 7 | ALIST_ADDR=${ALIST_ADDR:=http://alist:5678} 8 | 9 | EMBY_CONTROL_URL="${EMBY_ADDR/6908/8080}" 10 | 11 | MEDIA_DIR="/media" 12 | 13 | check_emby_status() { 14 | curl -s -f -m 5 "${EMBY_ADDR}" > /dev/null 15 | } 16 | 17 | save_user_policy() { 18 | 19 | if ! check_emby_status; then 20 | echo "Emby 服务未启动!" 21 | return 1 22 | fi 23 | 24 | echo "保留用户 Policy 中..." 25 | curl -s "${EMBY_ADDR}/Users?api_key=${EMBY_APIKEY}" > /tmp/emby.response 26 | } 27 | 28 | download() { 29 | if [ -f $MEDIA_DIR/temp/config.mp4 ]; then 30 | echo "备份旧数据..." 31 | mv -f $MEDIA_DIR/temp/config.mp4 $MEDIA_DIR/temp/config.mp4.bak 32 | fi 33 | echo "下载config.mp4数据..." 34 | cd $MEDIA_DIR/temp 35 | # 重试3次下载config.mp4,包含config.mp4.aria2则重试 36 | for i in {1..5}; do 37 | echo "下载config.mp4,尝试 $i..." 38 | aria2c -o config.mp4 --continue=true -x6 --conditional-get=true --allow-overwrite=true "${ALIST_ADDR}/d/元数据/config.mp4" 39 | if [ ! -f $MEDIA_DIR/temp/config.mp4.aria2 ]; then 40 | break 41 | fi 42 | done 43 | 44 | if [ -f $MEDIA_DIR/temp/config.mp4.aria2 ]; then 45 | echo "下载config.mp4失败,还原文件..." 46 | rm -f $MEDIA_DIR/temp/config.mp4.aria2 47 | rm -f $MEDIA_DIR/temp/config.mp4 48 | if [ -f $MEDIA_DIR/temp/config.mp4.bak ]; then 49 | mv -f $MEDIA_DIR/temp/config.mp4.bak $MEDIA_DIR/temp/config.mp4 50 | fi 51 | return 1 52 | fi 53 | 54 | rm -rf $MEDIA_DIR/temp/config.mp4.bak 55 | return 0 56 | 57 | } 58 | 59 | extract() { 60 | echo "解压config.mp4数据..." 61 | cd $MEDIA_DIR/temp 62 | 7z x -aoa -mmt=16 config.mp4 63 | #删除临时文件config.mp4 64 | if [ "${CLEAR_TEMP:=false}" = "true" ]; then 65 | rm -f $MEDIA_DIR/temp/config.mp4 66 | fi 67 | } 68 | 69 | update_data() { 70 | 71 | if check_emby_status; then 72 | echo "Emby 服务未停止!" 73 | return 1 74 | fi 75 | 76 | echo "备份数据中..." 77 | sqlite3 ${MEDIA_DIR}/config/data/library.db ".dump UserDatas" > /tmp/emby_user.sql 78 | sqlite3 ${MEDIA_DIR}/config/data/library.db ".dump ItemExtradata" > /tmp/emby_library_mediaconfig.sql 79 | 80 | files=( 81 | "library.db" 82 | "library.db-shm" 83 | "library.db-wal" 84 | ) 85 | for file in "${files[@]}"; do 86 | src_file="$MEDIA_DIR/config/data/$file" 87 | dest_file="$src_file.backup" 88 | if [ -f "$src_file" ]; then 89 | if [ -f "$dest_file" ]; then 90 | rm -f "$dest_file" 91 | fi 92 | mv -f "$src_file" "$dest_file" 93 | fi 94 | done 95 | 96 | echo "更新数据库中..." 97 | cp -rf $MEDIA_DIR/temp/config/data/library.db* $MEDIA_DIR/config/data/ 98 | sqlite3 $MEDIA_DIR/config/data/library.db "DROP TABLE IF EXISTS UserDatas;" 99 | sqlite3 $MEDIA_DIR/config/data/library.db ".read /tmp/emby_user.sql" 100 | sqlite3 $MEDIA_DIR/config/data/library.db "DROP TABLE IF EXISTS ItemExtradata;" 101 | sqlite3 $MEDIA_DIR/config/data/library.db ".read /tmp/emby_library_mediaconfig.sql" 102 | echo "保存用户信息完成" 103 | echo "文件复制中..." 104 | mkdir -p $MEDIA_DIR/config/cache 105 | mkdir -p $MEDIA_DIR/config/metadata 106 | cp -rf $MEDIA_DIR/temp/config/cache/* $MEDIA_DIR/config/cache/ 107 | cp -rf $MEDIA_DIR/temp/config/metadata/* $MEDIA_DIR/config/metadata/ 108 | rm -rf $MEDIA_DIR/temp/config/ 109 | echo "文件复制完成" 110 | chmod -R 777 $MEDIA_DIR/config/data $MEDIA_DIR/config/cache $MEDIA_DIR/config/metadata 111 | } 112 | 113 | wait_for_emby() { 114 | local MAX_WAIT="300" 115 | 116 | echo "Waiting for Emby service to start at $EMBY_ADDR..." 117 | 118 | while true; do 119 | local http_code 120 | http_code=$(curl -s -o /dev/null -w "%{http_code}" "${EMBY_ADDR}/Users") 121 | if [ "$http_code" -eq 401 ]; then 122 | echo "Emby service is up and running." 123 | return 0 124 | else 125 | echo "Emby service is not ready yet. HTTP status code: $http_code" 126 | if [ $MAX_WAIT -le 0 ]; then 127 | echo "Timeout waiting for Emby service to start." 128 | return 1 129 | fi 130 | sleep 2 131 | MAX_WAIT=$((MAX_WAIT - 2)) 132 | fi 133 | done 134 | } 135 | 136 | recover_user_policy() { 137 | echo "更新用户 Policy 中..." 138 | USER_COUNT=$(jq '.[].Name' /tmp/emby.response | wc -l) 139 | for ((i = 0; i < USER_COUNT; i++)); do 140 | id=$(jq -r ".[$i].Id" /tmp/emby.response) 141 | name=$(jq -r ".[$i].Name" /tmp/emby.response) 142 | policy=$(jq -r ".[$i].Policy | to_entries | from_entries | tojson" /tmp/emby.response) 143 | USER_URL_2="${EMBY_ADDR}/Users/$id/Policy?api_key=${EMBY_APIKEY}" 144 | status_code=$(curl -s -w "%{http_code}" -H "Content-Type: application/json" -X POST -d "$policy" "$USER_URL_2") 145 | if [ "$status_code" == "204" ]; then 146 | echo "成功更新 $name 用户Policy" 147 | else 148 | echo "返回错误代码 $status_code" 149 | return 1 150 | fi 151 | done 152 | } 153 | 154 | stop_emby() { 155 | echo "停止emby服务..." 156 | curl -s -f -m 5 "${EMBY_CONTROL_URL}/stop" 157 | sleep 10 158 | } 159 | 160 | start_emby() { 161 | echo "启动emby服务..." 162 | curl -s -f -m 5 "${EMBY_CONTROL_URL}/start" || true 163 | sleep 10 164 | } 165 | 166 | update() { 167 | # 储存用户策略,下载元数据,停止emby服务,更新数据库,启动emby服务,恢复用户策略 168 | save_user_policy 169 | download 170 | extract 171 | stop_emby 172 | update_data 173 | start_emby 174 | wait_for_emby 175 | recover_user_policy 176 | } 177 | 178 | reset() { 179 | stop_emby 180 | if [ ! -f "$MEDIA_DIR/temp/config.mp4" ]; then 181 | download 182 | fi 183 | # 不能删除config目录,该目录下有finished标记文件 184 | # rm -rf $MEDIA_DIR/config/* 185 | cd $MEDIA_DIR 186 | 7z x -aoa -mmt=16 temp/config.mp4 187 | #删除临时文件config.mp4 188 | if [ "${CLEAR_TEMP:=false}" = "true" ]; then 189 | rm -f $MEDIA_DIR/temp/config.mp4 190 | fi 191 | start_emby 192 | } 193 | 194 | case $1 in 195 | update) 196 | update 197 | ;; 198 | reset) 199 | reset 200 | ;; 201 | download) 202 | download 203 | ;; 204 | *) 205 | echo "Usage: $0 {update|reset|download}" 206 | exit 1 207 | ;; 208 | esac 209 | -------------------------------------------------------------------------------- /metadata/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | ALIST_ADDR=${ALIST_ADDR:-http://alist:5678} 6 | 7 | echo "检查alist连通性..." 8 | while ! curl -s --fail "${ALIST_ADDR}/api/public/settings" | grep -q 200; do 9 | sleep 2 10 | done 11 | 12 | echo "alist启动完成,等待10秒后开始下载元数据..." 13 | sleep 10 14 | 15 | MEDIA_DIR="/media" 16 | crontabs="" 17 | 18 | if [ ! -d "${MEDIA_DIR}/temp" ]; then 19 | mkdir -p "${MEDIA_DIR}/temp" 20 | fi 21 | if [ ! -d "${MEDIA_DIR}/xiaoya" ]; then 22 | mkdir -p "${MEDIA_DIR}/xiaoya" 23 | fi 24 | if [ ! -d "${MEDIA_DIR}/config" ]; then 25 | mkdir -p "${MEDIA_DIR}/config" 26 | fi 27 | 28 | echo "开始下载元数据,如果有问题无法解决,请删除目录 ${MEDIA_DIR}/temp 下的所有文件重新启动." 29 | 30 | disk_check() { 31 | if [ "${DISK_CHECK_ENABLED:-true}" = "false" ]; then 32 | return 33 | fi 34 | # 磁盘检测 35 | dir="$1" 36 | size="$2" 37 | free_size=$(df -P "${dir}" | tail -n1 | awk '{print $4}') 38 | free_size=$((free_size)) 39 | free_size_G=$((free_size / 1024 / 1024)) 40 | if [ $free_size_G -lt "${size}" ]; then 41 | echo "目录${dir}最少需要剩余空间:${size}G,目前仅剩:${free_size_G}G" 42 | exit 1 43 | fi 44 | } 45 | 46 | download_meta() { 47 | file=$1 48 | # 元数据路径 49 | path=${2:-} 50 | echo "Downloading ${file}..." 51 | # 检查历史文件,如果存在残留则删除 52 | if [ -f "${MEDIA_DIR}/temp/${file}.aria2" ]; then 53 | echo "Found ${file}.aria2, delete it." 54 | rm -rf "${MEDIA_DIR}/temp/${file}.aria2" 55 | rm -rf "${MEDIA_DIR}/temp/${file}" 56 | fi 57 | 58 | # 如果已经存在文件,则不下载 59 | if [ -f "${MEDIA_DIR}/temp/${file}" ]; then 60 | echo "Found ${file}, skip." 61 | return 62 | fi 63 | 64 | # 重试5次下载,包含.aria2则重试 65 | success=false 66 | for i in {1..5}; do 67 | echo "Downloading ${file}, try ${i}..." 68 | echo "Link is ${ALIST_ADDR}/d/元数据/${file}" 69 | if aria2c -o "${file}" --allow-overwrite=true --auto-file-renaming=false --enable-color=false -c -x6 "${ALIST_ADDR}/d/元数据/${path}${file}"; then 70 | # 下载的文件小于10M,下载失败,删除 71 | if [ "$(stat -c %s "${file}")" -lt 10000000 ]; then 72 | echo "Download ${file} failed, file size less than 10M, retry after 10 seconds." 73 | rm -rf "${file}" 74 | rm -rf "${file}.aria2" 75 | sleep 10 76 | continue 77 | fi 78 | # 下载成功 79 | success=true 80 | break 81 | fi 82 | done 83 | # 如果还存在aria2 84 | if [ "${success}" = "false" ]; then 85 | echo "Download ${file} failed." 86 | rm -rf "${file}" 87 | rm -rf "${file}.aria2" 88 | return 1 89 | fi 90 | } 91 | 92 | download_emby_config() { 93 | if [ -f ${MEDIA_DIR}/config/emby_meta_finished ]; then 94 | echo "Emby metadata has been downloaded. Delete the file ${MEDIA_DIR}/config/emby_meta_finished to re-extract." 95 | return 96 | fi 97 | 98 | disk_check ${MEDIA_DIR}/temp 5 99 | disk_check ${MEDIA_DIR}/config 5 100 | 101 | echo "Downloading Emby config..." 102 | 103 | cd "${MEDIA_DIR}/temp" 104 | download_meta config.mp4 105 | 106 | echo "Extracting Emby config..." 107 | 108 | cd ${MEDIA_DIR} 109 | 7z x -aoa -mmt=16 temp/config.mp4 110 | 111 | touch ${MEDIA_DIR}/config/emby_meta_finished 112 | 113 | #删除临时文件config.mp4 114 | if [ "${CLEAR_TEMP:=false}" = "true" ]; then 115 | rm -f $MEDIA_DIR/temp/config.mp4 116 | fi 117 | } 118 | 119 | download_emby_media() { 120 | if [ -f "${MEDIA_DIR}/xiaoya/emby_media_finished" ]; then 121 | echo "Emby media has been downloaded. Delete the file ${MEDIA_DIR}/xiaoya/emby_media_finished to re-extract." 122 | return 123 | fi 124 | 125 | echo "Cleaning up Emby media..." 126 | rm -rf ${MEDIA_DIR}/xiaoya/* 127 | 128 | disk_check ${MEDIA_DIR}/temp 60 129 | disk_check ${MEDIA_DIR}/xiaoya 70 130 | 131 | echo "Downloading Emby media..." 132 | 133 | cd "${MEDIA_DIR}/temp" 134 | download_meta all.mp4 135 | download_meta pikpak.mp4 136 | 137 | echo "Extracting Emby media..." 138 | 139 | cd ${MEDIA_DIR}/xiaoya 140 | 7z x -aoa -mmt=16 ${MEDIA_DIR}/temp/all.mp4 141 | 142 | cd ${MEDIA_DIR}/xiaoya 143 | 7z x -aoa -mmt=16 ${MEDIA_DIR}/temp/pikpak.mp4 144 | 145 | chmod -R 777 ${MEDIA_DIR}/xiaoya 146 | 147 | touch ${MEDIA_DIR}/xiaoya/emby_media_finished 148 | 149 | #删除临时文件all.mp4,pikpak.mo4 150 | if [ "${CLEAR_TEMP:=false}" = "true" ]; then 151 | rm -f $MEDIA_DIR/temp/all.mp4 152 | rm -f $MEDIA_DIR/temp/pikpak.mp4 153 | fi 154 | } 155 | 156 | # emby 157 | if [ "${EMBY_ENABLED:=false}" = "true" ]; then 158 | download_emby_config 159 | download_emby_media 160 | 161 | if [ "${AUTO_UPDATE_EMBY_CONFIG_ENABLED:=false}" = "true" ]; then 162 | echo "启动定时更新Emby配置任务..." 163 | # 随机生成一个时间,避免给服务器造成压力 164 | random_min=$(shuf -i 0-59 -n 1) 165 | random_hour=$(shuf -i 1-6 -n 1) 166 | crontabs="${crontabs}\n${random_min} ${random_hour} */${AUTO_UPDATE_EMBY_INTERVAL:=7} * * /emby.sh update" 167 | fi 168 | 169 | if [ "${AUTO_UPDATE_EMBY_METADATA_ENABLED:=false}" = "true" ]; then 170 | echo "启动定时更新Emby媒体数据任务..." 171 | # 随机生成一个时间,避免给服务器造成压力 172 | random_min=$(shuf -i 0-59 -n 1) 173 | random_hour=$(shuf -i 1-6 -n 1) 174 | crontabs="${crontabs}\n${random_min} ${random_hour} * * * /usr/local/bin/python3 /solid.py --media ${MEDIA_DIR}/xiaoya" 175 | fi 176 | fi 177 | 178 | # 添加定时任务 179 | if [ -n "${crontabs}" ]; then 180 | echo -e "$crontabs" | crontab - 181 | fi 182 | 183 | echo "Complete." 184 | 185 | cron -f 186 | -------------------------------------------------------------------------------- /uninstall.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -eu 4 | 5 | read -rp "请输入服务部署目录(默认/opt/xiaoya): " install_path 6 | install_path=${install_path:=/opt/xiaoya} 7 | 8 | if [ ! -d "$install_path" ]; then 9 | echo "目录不存在,退出卸载" 10 | exit 1 11 | fi 12 | 13 | DOCKER_COMPOSE="docker compose" 14 | 15 | if ! docker compose &> /dev/null; then 16 | DOCKER_COMPOSE="docker-compose" 17 | fi 18 | 19 | docker_params="" 20 | read -rp "是否删除数据卷?(y/n): " delete_volume 21 | if [ "$delete_volume" = "y" ]; then 22 | docker_params="--volumes" 23 | fi 24 | 25 | echo "停止服务..." 26 | $DOCKER_COMPOSE -f "$install_path/docker-compose.yml" down $docker_params 27 | 28 | if [ "$delete_volume" = "y" ]; then 29 | rm -rf "${install_path:?}"/* 30 | else 31 | find "${install_path:?}" -type f -not -path "${install_path}/data/*" -delete 32 | fi --------------------------------------------------------------------------------