├── .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 | 
2 |
3 |
4 |
小雅全家桶部署
5 |
使用 Docker Compose 一键部署 Alist + Emby
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | ## 功能特性
17 |
18 | 
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 |
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
--------------------------------------------------------------------------------