Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .cargo/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[env]
FFMPEG_DIR_AARCH64_APPLE_IOS = { value = "vendor/ffmpeg_ios", relative = true }
FFMPEG_DIR_aarch64_apple_ios = { value = "vendor/ffmpeg_ios", relative = true }
186 changes: 184 additions & 2 deletions .github/workflows/build-player.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ jobs:
target/**/release/amll-player

build-tauri-android:
name: 构建 Tauri 安卓移动版本
name: 构建 Tauri 安卓版本
needs: clean-pre-release
runs-on: ubuntu-latest

Expand Down Expand Up @@ -335,7 +335,7 @@ jobs:
run: |
node ./packages/player/scripts/gen-dev-version.mjs

- name: 构建 AMLL Player 安卓移动版程序
- name: 构建 AMLL Player 安卓版本
env:
CARGO_NDK_ANDROID_PLATFORM: 26
run: |
Expand All @@ -350,3 +350,185 @@ jobs:
path: |
packages/player/src-tauri/gen/android/app/build/outputs/**/*.apk
packages/player/src-tauri/gen/android/app/build/outputs/**/*.aab

build-tauri-ios:
name: 构建 Tauri iOS 版本
needs: clean-pre-release
runs-on: macos-14

steps:
- uses: actions/checkout@v4
name: 克隆仓库
with:
fetch-depth: 0

- name: 选择 Xcode 16 版本
uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: '16'

- name: 安装 PNPM
uses: pnpm/action-setup@v4

- name: 安装 Node.js
uses: actions/setup-node@v4
with:
node-version: lts/*
cache: pnpm

- name: 安装 Rust 工具链与 iOS 构建目标
uses: dtolnay/rust-toolchain@v1
with:
toolchain: stable
targets: "aarch64-apple-ios"

- name: 配置 Rust 缓存
uses: Swatinem/rust-cache@v2
with:
workspaces: |
. -> target
packages/player/src-tauri -> target
key: ios-aarch64-only

- name: 拉取并链接 iOS 预编译 FFmpeg 静态库
shell: bash
env:
BUILDER_REPO: apoint123/ffmpeg-builder
FFMPEG_VERSION_TAG: "ffmpeg-8.0.1-cc5ef4262cfdcf5eba1e9d8b6d4020e9998c5680"
run: |
PACKAGE_NAME="ffmpeg-8.0.1-ios-arm64.tar.gz"
DOWNLOAD_URL="https://github.com/${BUILDER_REPO}/releases/download/${FFMPEG_VERSION_TAG}/${PACKAGE_NAME}"
EXTRACT_DIR="${{ github.workspace }}/vendor/ffmpeg_ios"

echo "Downloading iOS FFmpeg static libraries from $DOWNLOAD_URL..."
curl -f -sL $DOWNLOAD_URL -o $PACKAGE_NAME

mkdir -p $EXTRACT_DIR
echo "Extracting FFmpeg to $EXTRACT_DIR..."
tar -xzf $PACKAGE_NAME -C $EXTRACT_DIR
rm $PACKAGE_NAME

# 设置链接编译所需的 FFMPEG_DIR 环境变量,映射真机目标
echo "FFMPEG_DIR=$EXTRACT_DIR" >> $GITHUB_ENV
echo "FFMPEG_DIR_AARCH64_APPLE_IOS=$EXTRACT_DIR" >> $GITHUB_ENV
echo "FFMPEG_DIR_aarch64_apple_ios=$EXTRACT_DIR" >> $GITHUB_ENV
echo "FFmpeg iOS environment set up successfully."


- name: 安装 pnpm 依赖
run: |
pnpm i --frozen-lockfile --ignore-scripts
env:
AMLL_GITHUB_IS_ACTION: true

- name: 生成开发构建版本号
run: |
node ./packages/player/scripts/gen-dev-version.mjs
env:
AMLL_IOS_BUILD: "true"

- name: 初始化 iOS 工程并配置图标
run: |
pnpm -F player tauri ios init
# 从高分辨率主图生成符合 iOS 规范的无切边专属图标集
pnpm -F player tauri icon src-tauri/icons/512x512@2x.png
env:
AMLL_GITHUB_IS_ACTION: true

- name: 禁用 Xcode 工程自动签名 (免证书构建)
shell: bash
run: |
# 修改自动签名配置为手动,强制关闭代码签名校验
PBXPROJ="packages/player/src-tauri/gen/apple/amll-player.xcodeproj/project.pbxproj"

node -e '
const fs = require("fs");
const path = process.argv[1];
let content = fs.readFileSync(path, "utf8");

content = content.replace(/buildSettings = \{([\s\S]*?)\};/g, (match, body) => {
let newBody = body
.replace(/CODE_SIGN_STYLE = [^;]+;/g, "")
.replace(/DEVELOPMENT_TEAM = [^;]+;/g, "")
.replace(/CODE_SIGN_IDENTITY = [^;]+;/g, "")
.replace(/CODE_SIGNING_ALLOWED = [^;]+;/g, "")
.replace(/CODE_SIGNING_REQUIRED = [^;]+;/g, "")
.replace(/PROVISIONING_PROFILE_SPECIFIER = [^;]+;/g, "")
.split("\n")
.filter(line => line.trim() !== "")
.join("\n");

return `buildSettings = {\n${newBody}\n\t\t\t\tCODE_SIGNING_ALLOWED = NO;\n\t\t\t\tCODE_SIGNING_REQUIRED = NO;\n\t\t\t\tCODE_SIGN_IDENTITY = "";\n\t\t\t\tCODE_SIGN_STYLE = Manual;\n\t\t\t\tDEVELOPMENT_TEAM = "";\n\t\t\t};`;
});

fs.writeFileSync(path, content, "utf8");
console.log("Successfully rewrote all buildSettings in project.pbxproj to disable signing.");
' "$PBXPROJ"

- name: 构建前端资源
run: pnpm -F player build
env:
AMLL_GITHUB_IS_ACTION: true

- name: 注入 iOS 后台音频播放权限 (Info.plist)
shell: bash
run: |
# 在 Xcode 工程生成后,通过 PlistBuddy 写入 UIBackgroundModes=audio 权限,支持后台切歌与播放
PLIST_PATH="packages/player/src-tauri/gen/apple/amll-player_iOS/Info.plist"
/usr/libexec/PlistBuddy -c "Add :UIBackgroundModes array" "$PLIST_PATH" || true
/usr/libexec/PlistBuddy -c "Add :UIBackgroundModes:0 string audio" "$PLIST_PATH" || true
echo "Info.plist background audio modes injected."

- name: 使用 Tauri CLI 构建无签名 iOS 应用
shell: bash
run: |
# 使用 Tauri CLI 编译应用。无签名构建在导出归档时会提示报错(code 70),
# 若底层的 *.app 已经成功生成,我们捕获该状态并作为构建成功处理。
pnpm -F player tauri ios build || {
echo "==== 已编译的 iOS 应用包 (.app) 路径列表 ===="
find packages/player/src-tauri/gen/apple -name "*.app" 2>/dev/null || true
find $HOME/Library/Developer/Xcode -name "*.app" 2>/dev/null || true
echo "=========================================="

APP_PATH=$(find packages/player/src-tauri/gen/apple -name "*.app" 2>/dev/null | head -1)
if [ -z "$APP_PATH" ]; then
APP_PATH=$(find $HOME/Library/Developer/Xcode/DerivedData $HOME/Library/Developer/Xcode/Archives -name "*.app" 2>/dev/null | head -1)
fi
if [ -n "$APP_PATH" ] && [ -d "$APP_PATH" ]; then
echo "Tauri CLI exited with signing/export warning, but iOS app was compiled successfully!"
echo "Found compiled app at: $APP_PATH"
echo "Proceeding with manual unsigned packaging..."
else
echo "ERROR: Tauri CLI build failed and no compiled .app was found."
exit 1
fi
}
env:
AMLL_GITHUB_IS_ACTION: true
SKIP_FRONTEND_BUILD: true

- name: 压包并生成无签名版 .ipa
run: |
# 动态查找编译生成的 .app 目录并打包为无签名 .ipa 产物
APP_PATH=$(find packages/player/src-tauri/gen/apple -name "*.app" 2>/dev/null | head -1)
if [ -z "$APP_PATH" ]; then
APP_PATH=$(find $HOME/Library/Developer/Xcode/DerivedData $HOME/Library/Developer/Xcode/Archives -name "*.app" 2>/dev/null | head -1)
fi
if [ -z "$APP_PATH" ]; then
echo "ERROR: Compiled .app not found. Listing possible locations:"
find . -name "*.app" 2>/dev/null
exit 1
fi
echo "Found: $APP_PATH"

mkdir -p build/Payload
cp -r "$APP_PATH" build/Payload/
cd build && zip -r amll-player-unsigned.ipa Payload
echo "IPA created:"
ls -la amll-player-unsigned.ipa

- name: 上传无签名 ipa 包至 GitHub Action Artifacts
uses: actions/upload-artifact@v4
with:
name: AMLL Player iOS Unsigned IPA
path: build/amll-player-unsigned.ipa
3 changes: 2 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 15 additions & 5 deletions packages/player/scripts/gen-dev-version.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,20 @@ if (!/^[0-9]+\.[0-9]+\.[0-9]+$/.test(baseVersion)) {
// const branch = execSync('git rev-parse --abbrev-ref HEAD').toString().trim();
const commitCount = execSync("git rev-list --count HEAD").toString().trim();

const devVersion = `${baseVersion}+${commitCount}`;

tauriConf.version = devVersion;

console.log(`Generated dev version: ${baseVersion} -> ${devVersion}`);
const isIos = process.env.AMLL_IOS_BUILD === "true";

if (isIos) {
// iOS:版本号严格保持 3 位,构建号单独写入 bundle.iOS.bundleVersion
tauriConf.version = baseVersion;
if (!tauriConf.bundle) tauriConf.bundle = {};
if (!tauriConf.bundle.iOS) tauriConf.bundle.iOS = {};
tauriConf.bundle.iOS.bundleVersion = commitCount;
console.log(`Generated iOS dev version: ${baseVersion} (${commitCount})`);
} else {
// 其它平台:保留原有逻辑
const devVersion = `${baseVersion}+${commitCount}`;
tauriConf.version = devVersion;
console.log(`Generated dev version: ${baseVersion} -> ${devVersion}`);
}

writeFileSync(tauriConfPath, JSON.stringify(tauriConf, null, "\t"));
5 changes: 5 additions & 0 deletions packages/player/src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,11 @@ objc2 = "^0.6"
objc2-avf-audio = "^0.3"
tauri-plugin-macos-fps = "0.1.0"

[target.'cfg(target_os = "ios")'.dependencies]
objc2 = "^0.6"
objc2-avf-audio = "^0.3"
objc2-foundation = "^0.3"

[target.'cfg(target_os = "windows")'.dependencies]
webview2-com = "0.38"
taskbar-lyric = { git = "https://github.com/apoint123/taskbar-lyric.git", features = [
Expand Down
17 changes: 17 additions & 0 deletions packages/player/src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -527,6 +527,23 @@ pub fn run() {
theme_watcher::get_system_theme
])
.setup(|app| {
#[cfg(target_os = "ios")]
{
use objc2::msg_send;
use objc2_avf_audio::AVAudioSession;
use objc2_foundation::ns_string;

info!("Initializing iOS AVAudioSession Category to Playback...");
unsafe {
let session = AVAudioSession::sharedInstance();
let category = ns_string!("AVAudioSessionCategoryPlayback");

let _: () = msg_send![&session, setCategory: category, error: std::ptr::null_mut::<*mut *mut objc2_foundation::NSError>()];

let _: bool = msg_send![&session, setActive: true, error: std::ptr::null_mut::<*mut *mut objc2_foundation::NSError>()];
}
info!("iOS AVAudioSession Category set to Playback successfully!");
}
#[cfg(target_os = "android")]
{
if let Some(webview) = app.get_webview_window("main") {
Expand Down
1 change: 0 additions & 1 deletion packages/player/src-tauri/tauri.conf.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@
"version": "0.0.1",
"identifier": "net.stevexmh.amllplayer",
"plugins": {
"macos-fps": {},
"updater": {
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDVBMzY2M0I2Qjc4MjBBNTUKUldSVkNvSzN0bU0yV2k0bUhxcm9jczBNYk9lTllYaTFyWHVEZ2xaSloxaVhpSnQ1RHZFSUtnS2IK",
"endpoints": [
Expand Down
5 changes: 5 additions & 0 deletions packages/player/src-tauri/tauri.macos.conf.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"plugins": {
"macos-fps": {}
}
}