diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8e62500..0b7e8ad 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -3,13 +3,13 @@ name: Release on: push: tags: - - 'v*' + - "v*" workflow_dispatch: inputs: version: - description: 'Version tag (e.g., v1.0.0)' + description: "Version tag (e.g., v1.0.0)" required: true - default: 'v1.0.0' + default: "v1.0.0" jobs: # Get version first (quick job) @@ -28,7 +28,7 @@ jobs: VERSION="${GITHUB_REF#refs/tags/}" fi echo "version=$VERSION" >> $GITHUB_OUTPUT - + # Check if version contains -preview, -beta, -rc, or -alpha (NOT -hotfix) VERSION_LOWER=$(echo "$VERSION" | tr '[:upper:]' '[:lower:]') if [[ "$VERSION_LOWER" == *"-preview"* ]] || [[ "$VERSION_LOWER" == *"-beta"* ]] || [[ "$VERSION_LOWER" == *"-rc"* ]] || [[ "$VERSION_LOWER" == *"-alpha"* ]]; then @@ -43,7 +43,7 @@ jobs: build-android: runs-on: ubuntu-latest needs: get-version - + steps: - name: Free disk space run: | @@ -65,13 +65,13 @@ jobs: - name: Setup Java uses: actions/setup-java@v4 with: - distribution: 'temurin' - java-version: '17' + distribution: "temurin" + java-version: "17" - name: Setup Go uses: actions/setup-go@v5 with: - go-version: '1.21' + go-version: "1.21" cache-dependency-path: go_backend/go.sum # Cache Gradle for faster builds @@ -89,13 +89,13 @@ jobs: # Use pre-installed Android SDK on GitHub runners echo "ANDROID_HOME=$ANDROID_HOME" echo "ANDROID_SDK_ROOT=$ANDROID_SDK_ROOT" - + # Accept licenses yes | $ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager --licenses || true - + # Install NDK (required for gomobile) $ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager "ndk;25.2.9519653" "platforms;android-34" "build-tools;34.0.0" - + # Set NDK path echo "ANDROID_NDK_HOME=$ANDROID_HOME/ndk/25.2.9519653" >> $GITHUB_ENV @@ -115,7 +115,7 @@ jobs: - name: Setup Flutter uses: subosito/flutter-action@v2 with: - channel: 'stable' + channel: "stable" cache: true - name: Get Flutter dependencies @@ -164,8 +164,8 @@ jobs: build-ios: runs-on: macos-latest - needs: get-version # Only depends on version, NOT android build! - + needs: get-version # Only depends on version, NOT android build! + steps: - name: Checkout repository uses: actions/checkout@v4 @@ -173,7 +173,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v5 with: - go-version: '1.21' + go-version: "1.21" cache-dependency-path: go_backend/go.sum # Cache CocoaPods @@ -201,51 +201,51 @@ jobs: run: | ls -la ios/Frameworks/ ls -la ios/Frameworks/Gobackend.xcframework/ || (echo "ERROR: XCFramework not found!" && exit 1) - + - name: Add XCFramework to Xcode project run: | # Install xcodeproj gem for modifying Xcode project sudo gem install xcodeproj - + # Create Ruby script to add framework cat > add_framework.rb << 'EOF' require 'xcodeproj' - + project_path = 'ios/Runner.xcodeproj' project = Xcodeproj::Project.open(project_path) - + # Get the main target target = project.targets.find { |t| t.name == 'Runner' } - + # Get or create Frameworks group frameworks_group = project.main_group.find_subpath('Frameworks', true) frameworks_group ||= project.main_group.new_group('Frameworks') - + # Add XCFramework reference framework_path = 'Frameworks/Gobackend.xcframework' framework_ref = frameworks_group.new_file(framework_path, :project) - + # Add to frameworks build phase frameworks_build_phase = target.frameworks_build_phase frameworks_build_phase.add_file_reference(framework_ref) - + # Add to embed frameworks build phase embed_phase = target.build_phases.find { |p| p.is_a?(Xcodeproj::Project::Object::PBXCopyFilesBuildPhase) && p.name == 'Embed Frameworks' } if embed_phase build_file = embed_phase.add_file_reference(framework_ref) build_file.settings = { 'ATTRIBUTES' => ['CodeSignOnCopy', 'RemoveHeadersOnCopy'] } end - + project.save puts "Successfully added Gobackend.xcframework to Xcode project" EOF - + ruby add_framework.rb - name: Setup Flutter uses: subosito/flutter-action@v2 with: - channel: 'stable' + channel: "stable" cache: true # Swap pubspec for iOS build (includes ffmpeg_kit_flutter) @@ -275,7 +275,7 @@ jobs: run: | # Build Flutter iOS without codesigning flutter build ios --release --no-codesign --config-only - + # Use xcodebuild with code signing disabled cd ios xcodebuild -workspace Runner.xcworkspace \ @@ -300,7 +300,7 @@ jobs: # Use absolute path to avoid relative path issues zip -r $GITHUB_WORKSPACE/build/ios/ipa/SpotiFLAC-${VERSION}-ios-unsigned.ipa Payload rm -rf Payload - + - name: Verify IPA created run: | ls -la build/ios/ipa/ @@ -321,7 +321,7 @@ jobs: needs: [get-version, build-android, build-ios] permissions: contents: write - + steps: - name: Checkout repository uses: actions/checkout@v4 @@ -331,13 +331,13 @@ jobs: run: | VERSION=${{ needs.get-version.outputs.version }} VERSION_NUM=${VERSION#v} # Remove 'v' prefix - + echo "Looking for version: $VERSION_NUM" - + # Extract changelog section for this version using sed # Find the line with version, then print until next version header or end CHANGELOG=$(sed -n "/^## \[$VERSION_NUM\]/,/^## \[/{ /^## \[$VERSION_NUM\]/d; /^## \[/d; p; }" CHANGELOG.md) - + # If no changelog found, use default message if [ -z "$CHANGELOG" ]; then echo "No changelog found for version $VERSION_NUM" @@ -345,7 +345,7 @@ jobs: else echo "Found changelog content" fi - + # Save to file for multiline support echo "$CHANGELOG" > /tmp/changelog.txt echo "Extracted changelog:" @@ -367,32 +367,37 @@ jobs: run: | VERSION=${{ needs.get-version.outputs.version }} cat > /tmp/release_body.txt << 'HEADER' - ## SpotiFLAC $VERSION - - Download Spotify tracks in FLAC quality from Tidal, Qobuz & Amazon Music. - ### What's New HEADER - - # Replace $VERSION in header - sed -i "s/\$VERSION/$VERSION/g" /tmp/release_body.txt - + cat /tmp/changelog.txt >> /tmp/release_body.txt + + REPO_OWNER="${{ github.repository_owner }}" + REPO_NAME="${{ github.event.repository.name }}" cat >> /tmp/release_body.txt << FOOTER - + --- - + ### Downloads - - **Android (arm64)**: \`SpotiFLAC-${VERSION}-arm64.apk\` (recommended) - - **Android (arm32)**: \`SpotiFLAC-${VERSION}-arm32.apk\` (older devices) + + #### Android + ![Android arm64 Downloads](https://img.shields.io/github/downloads/${REPO_OWNER}/${REPO_NAME}/${VERSION}/SpotiFLAC-${VERSION}-arm64.apk?style=for-the-badge&logo=android&label=arm64&color=3DDC84) + ![Android arm32 Downloads](https://img.shields.io/github/downloads/${REPO_OWNER}/${REPO_NAME}/${VERSION}/SpotiFLAC-${VERSION}-arm32.apk?style=for-the-badge&logo=android&label=arm32&color=3DDC84) + + - **arm64**: \`SpotiFLAC-${VERSION}-arm64.apk\` (recommended for modern devices) + - **arm32**: \`SpotiFLAC-${VERSION}-arm32.apk\` (older devices) + + #### iOS + ![iOS Downloads](https://img.shields.io/github/downloads/${REPO_OWNER}/${REPO_NAME}/${VERSION}/SpotiFLAC-${VERSION}-ios-unsigned.ipa?style=for-the-badge&logo=apple&label=iOS&color=0078D6) + - **iOS**: \`SpotiFLAC-${VERSION}-ios-unsigned.ipa\` (sideload required) - + ### Installation **Android**: Enable "Install from unknown sources" and install the APK **iOS**: Use AltStore, Sideloadly, or similar tools to sideload the IPA FOOTER - + echo "Release body:" cat /tmp/release_body.txt diff --git a/ios/Runner/AppDelegate.swift b/ios/Runner/AppDelegate.swift index 88359b7..0b7ebb1 100644 --- a/ios/Runner/AppDelegate.swift +++ b/ios/Runner/AppDelegate.swift @@ -181,6 +181,66 @@ import Gobackend // Import Go framework GobackendCleanupConnections() return nil + case "searchDeezerAll": + let args = call.arguments as! [String: Any] + let query = args["query"] as! String + let trackLimit = args["track_limit"] as? Int ?? 15 + let artistLimit = args["artist_limit"] as? Int ?? 3 + let response = GobackendSearchDeezerAll(query, Int(trackLimit), Int(artistLimit), &error) + if let error = error { throw error } + return response + + case "getDeezerMetadata": + let args = call.arguments as! [String: Any] + let resourceType = args["resource_type"] as! String + let resourceId = args["resource_id"] as! String + let response = GobackendGetDeezerMetadata(resourceType, resourceId, &error) + if let error = error { throw error } + return response + + case "parseDeezerUrl": + let args = call.arguments as! [String: Any] + let url = args["url"] as! String + let response = GobackendParseDeezerUrl(url, &error) + if let error = error { throw error } + return response + + case "searchDeezerByISRC": + let args = call.arguments as! [String: Any] + let isrc = args["isrc"] as! String + let response = GobackendSearchDeezerByISRC(isrc, &error) + if let error = error { throw error } + return response + + case "convertSpotifyToDeezer": + let args = call.arguments as! [String: Any] + let resourceType = args["resource_type"] as! String + let spotifyId = args["spotify_id"] as! String + let response = GobackendConvertSpotifyToDeezer(resourceType, spotifyId, &error) + if let error = error { throw error } + return response + + case "getSpotifyMetadataWithFallback": + let args = call.arguments as! [String: Any] + let url = args["url"] as! String + let response = GobackendGetSpotifyMetadataWithFallback(url, &error) + if let error = error { throw error } + return response + + case "preWarmTrackCache": + let args = call.arguments as! [String: Any] + let tracksJson = args["tracks"] as! String + GobackendPreWarmTrackCache(tracksJson) + return nil + + case "getTrackCacheSize": + let response = GobackendGetTrackCacheSize() + return response + + case "clearTrackCache": + GobackendClearTrackCache() + return nil + default: throw NSError( domain: "SpotiFLAC",