mirror of
				https://gitlab.com/foxixus/neomovies_mobile.git
				synced 2025-10-29 02:38:49 +05:00 
			
		
		
		
	Compare commits
	
		
			5 Commits
		
	
	
		
			143a5cf8a5
			...
			54a533f267
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 54a533f267 | |||
|  | e4e56d76af | ||
|  | 4306a9038a | ||
|  | 275c8122a2 | ||
|  | 2f191dd302 | 
							
								
								
									
										267
									
								
								.gitlab-ci.yml
									
									
									
									
									
								
							
							
						
						
									
										267
									
								
								.gitlab-ci.yml
									
									
									
									
									
								
							| @@ -1,202 +1,201 @@ | ||||
| # GitLab CI/CD Configuration for NeoMovies Mobile | ||||
| # Автоматическая сборка APK и TorrentEngine модуля | ||||
|  | ||||
| stages: | ||||
|   - build | ||||
|   - test | ||||
|   - deploy | ||||
|  | ||||
| variables: | ||||
|   # Flutter версия | ||||
|   FLUTTER_VERSION: "3.35.5" | ||||
|   # Flutter путь для CI | ||||
|   FLUTTER_ROOT: "/opt/flutter" | ||||
|   # Android SDK (стандартный путь в mingc/android-build-box) | ||||
|   ANDROID_SDK_ROOT: "/opt/android-sdk" | ||||
|   ANDROID_HOME: "/opt/android-sdk" | ||||
|   # Gradle настройки для CI (меньше RAM) | ||||
|   GRADLE_OPTS: "-Dorg.gradle.daemon=false -Dorg.gradle.jvmargs='-Xmx1536m -XX:MaxMetaspaceSize=512m' -Dorg.gradle.parallel=true -Dorg.gradle.caching=true" | ||||
|   # Кэш | ||||
|   GRADLE_OPTS: "-Dorg.gradle.daemon=false -Dorg.gradle.jvmargs='-Xmx2048m' -Dorg.gradle.parallel=true" | ||||
|   GRADLE_USER_HOME: "${CI_PROJECT_DIR}/.gradle" | ||||
|   PUB_CACHE: "${CI_PROJECT_DIR}/.pub-cache" | ||||
|  | ||||
| # Кэширование для ускорения сборки | ||||
| cache: | ||||
|   key: ${CI_COMMIT_REF_SLUG} | ||||
|   paths: | ||||
|     - .gradle/ | ||||
|     - .pub-cache/ | ||||
|     - android/.gradle/ | ||||
|     - android/build/ | ||||
|     - build/ | ||||
|  | ||||
| # ============================================ | ||||
| # Сборка только TorrentEngine модуля | ||||
| # ============================================ | ||||
| build:torrent-engine: | ||||
|   stage: build | ||||
|   image: mingc/android-build-box:latest | ||||
|   tags: | ||||
|     - saas-linux-medium-amd64  # GitLab Instance Runner (4GB RAM, 2 cores) | ||||
|   before_script: | ||||
|     - echo "Detecting Android SDK location..." | ||||
|     - export ANDROID_SDK_ROOT=${ANDROID_SDK_ROOT:-${ANDROID_HOME:-/opt/android-sdk}} | ||||
|     - echo "Android SDK: ${ANDROID_SDK_ROOT}" | ||||
|     - echo "Creating local.properties for Flutter..." | ||||
|     - echo "flutter.sdk=${FLUTTER_ROOT}" > android/local.properties | ||||
|     - echo "sdk.dir=${ANDROID_SDK_ROOT}" >> android/local.properties | ||||
|     - cat android/local.properties | ||||
|   script: | ||||
|     - echo "Building TorrentEngine library module..." | ||||
|     - cd android | ||||
|     # Собираем только модуль torrentengine | ||||
|     - ./gradlew :torrentengine:assembleRelease --no-daemon --parallel --build-cache | ||||
|     - ls -lah torrentengine/build/outputs/aar/ | ||||
|     - chmod +x gradlew | ||||
|     - ./gradlew :torrentengine:assembleRelease --no-daemon --stacktrace | ||||
|   artifacts: | ||||
|     name: "torrentengine-${CI_COMMIT_SHORT_SHA}" | ||||
|     paths: | ||||
|       - android/torrentengine/build/outputs/aar/*.aar | ||||
|     expire_in: 1 week | ||||
|   only: | ||||
|     - dev | ||||
|     - feature/torrent-engine-integration | ||||
|     - merge_requests | ||||
|   when: on_success | ||||
|     expire_in: 30 days | ||||
|   rules: | ||||
|     - if: $CI_COMMIT_BRANCH == "dev" | ||||
|     - if: $CI_COMMIT_BRANCH == "main" | ||||
|     - if: $CI_COMMIT_BRANCH =~ /^feature\// | ||||
|     - if: $CI_COMMIT_TAG | ||||
|     - if: $CI_PIPELINE_SOURCE == "merge_request_event" | ||||
|  | ||||
| # ============================================ | ||||
| # Сборка Debug APK | ||||
| # ============================================ | ||||
| build:apk-debug: | ||||
|   stage: build | ||||
|   image: mingc/android-build-box:latest | ||||
|   tags: | ||||
|     - docker | ||||
|   before_script: | ||||
|     - echo "Installing Flutter ${FLUTTER_VERSION}..." | ||||
|     - git clone --depth 1 --branch ${FLUTTER_VERSION} https://github.com/flutter/flutter.git /opt/flutter | ||||
|     - export PATH="/opt/flutter/bin:$PATH" | ||||
|     - flutter --version | ||||
|     - flutter doctor -v | ||||
|     - flutter pub get | ||||
|   image: ghcr.io/cirruslabs/flutter:stable | ||||
|   script: | ||||
|     - echo "Building Debug APK..." | ||||
|     - flutter build apk --debug --target-platform android-arm64 | ||||
|     - ls -lah build/app/outputs/flutter-apk/ | ||||
|     - flutter pub get | ||||
|     - flutter build apk --debug | ||||
|   artifacts: | ||||
|     name: "neomovies-debug-${CI_COMMIT_SHORT_SHA}" | ||||
|     paths: | ||||
|       - build/app/outputs/flutter-apk/app-debug.apk | ||||
|     expire_in: 1 week | ||||
|   only: | ||||
|     - dev | ||||
|     - feature/torrent-engine-integration | ||||
|     - merge_requests | ||||
|   when: on_success | ||||
|   rules: | ||||
|     - if: $CI_COMMIT_BRANCH == "dev" | ||||
|     - if: $CI_COMMIT_BRANCH =~ /^feature\// | ||||
|     - if: $CI_PIPELINE_SOURCE == "merge_request_event" | ||||
|   allow_failure: true | ||||
|  | ||||
| # ============================================ | ||||
| # Сборка Release APK (только для dev) | ||||
| # ============================================ | ||||
| build:apk-release: | ||||
|   stage: build | ||||
|   image: mingc/android-build-box:latest | ||||
|   tags: | ||||
|     - docker | ||||
|   before_script: | ||||
|     - echo "Installing Flutter ${FLUTTER_VERSION}..." | ||||
|     - git clone --depth 1 --branch ${FLUTTER_VERSION} https://github.com/flutter/flutter.git /opt/flutter | ||||
|     - export PATH="/opt/flutter/bin:$PATH" | ||||
|     - flutter --version | ||||
|     - flutter doctor -v | ||||
|     - flutter pub get | ||||
|   image: ghcr.io/cirruslabs/flutter:stable | ||||
|   script: | ||||
|     - echo "Building Release APK..." | ||||
|     # Сборка с split-per-abi для уменьшения размера | ||||
|     - flutter build apk --release --split-per-abi --target-platform android-arm64 | ||||
|     - ls -lah build/app/outputs/flutter-apk/ | ||||
|     - flutter pub get | ||||
|     - flutter build apk --release --split-per-abi | ||||
|   artifacts: | ||||
|     name: "neomovies-release-${CI_COMMIT_SHORT_SHA}" | ||||
|     paths: | ||||
|       - build/app/outputs/flutter-apk/app-arm64-v8a-release.apk | ||||
|       - build/app/outputs/flutter-apk/app-armeabi-v7a-release.apk | ||||
|       - build/app/outputs/flutter-apk/app-x86_64-release.apk | ||||
|     expire_in: 30 days | ||||
|   only: | ||||
|     - dev | ||||
|   when: on_success | ||||
|   rules: | ||||
|     - if: $CI_COMMIT_BRANCH == "dev" | ||||
|     - if: $CI_COMMIT_BRANCH == "main" | ||||
|     - if: $CI_COMMIT_BRANCH =~ /^feature\// | ||||
|     - if: $CI_COMMIT_TAG | ||||
|   allow_failure: true | ||||
|  | ||||
| # ============================================ | ||||
| # Анализ кода Flutter | ||||
| # ============================================ | ||||
| test:flutter-analyze: | ||||
|   stage: test | ||||
|   image: mingc/android-build-box:latest | ||||
|   tags: | ||||
|     - docker | ||||
|   before_script: | ||||
|     - git clone --depth 1 --branch ${FLUTTER_VERSION} https://github.com/flutter/flutter.git /opt/flutter | ||||
|     - export PATH="/opt/flutter/bin:$PATH" | ||||
|     - flutter pub get | ||||
|   image: ghcr.io/cirruslabs/flutter:stable | ||||
|   script: | ||||
|     - echo "Running Flutter analyze..." | ||||
|     - flutter analyze --no-fatal-infos || true | ||||
|   only: | ||||
|     - dev | ||||
|     - merge_requests | ||||
|     - flutter pub get | ||||
|     - flutter analyze | ||||
|   rules: | ||||
|     - if: $CI_COMMIT_BRANCH == "dev" | ||||
|     - if: $CI_PIPELINE_SOURCE == "merge_request_event" | ||||
|   allow_failure: true | ||||
|  | ||||
| # ============================================ | ||||
| # Kotlin/Android lint | ||||
| # ============================================ | ||||
| test:android-lint: | ||||
|   stage: test | ||||
|   image: mingc/android-build-box:latest | ||||
|   tags: | ||||
|     - docker | ||||
|   before_script: | ||||
|     - echo "Creating local.properties for Flutter..." | ||||
|     - echo "flutter.sdk=${FLUTTER_ROOT}" > android/local.properties | ||||
|     - echo "sdk.dir=${ANDROID_SDK_ROOT}" >> android/local.properties | ||||
|   script: | ||||
|     - echo "Running Android Lint..." | ||||
|     - cd android | ||||
|     - ./gradlew lint --no-daemon || true | ||||
|     - chmod +x gradlew | ||||
|     - ./gradlew lint --no-daemon | ||||
|   artifacts: | ||||
|     name: "lint-reports-${CI_COMMIT_SHORT_SHA}" | ||||
|     paths: | ||||
|       - android/app/build/reports/lint-results*.html | ||||
|       - android/torrentengine/build/reports/lint-results*.html | ||||
|       - android/app/build/reports/lint-*.html | ||||
|       - android/torrentengine/build/reports/lint-*.html | ||||
|     expire_in: 1 week | ||||
|   only: | ||||
|     - dev | ||||
|     - merge_requests | ||||
|     when: always | ||||
|   rules: | ||||
|     - if: $CI_COMMIT_BRANCH == "dev" | ||||
|     - if: $CI_PIPELINE_SOURCE == "merge_request_event" | ||||
|   allow_failure: true | ||||
|  | ||||
| # ============================================ | ||||
| # Deploy к релизам (опционально) | ||||
| # ============================================ | ||||
| deploy:release: | ||||
|   stage: deploy | ||||
|   image: alpine:latest | ||||
|   tags: | ||||
|     - docker | ||||
|   needs: | ||||
|     - build:apk-release | ||||
|     - build:torrent-engine | ||||
|   before_script: | ||||
|     - apk add --no-cache curl jq | ||||
|   script: | ||||
|     - echo "Creating GitLab Release..." | ||||
|     - | | ||||
|       if [ -f "build/app/outputs/flutter-apk/app-arm64-v8a-release.apk" ]; then | ||||
|         echo "Release APK found" | ||||
|         # Здесь можно добавить публикацию в GitLab Releases или другой deployment | ||||
|       # Определяем версию релиза | ||||
|       if [ -n "$CI_COMMIT_TAG" ]; then | ||||
|         VERSION="$CI_COMMIT_TAG" | ||||
|       else | ||||
|         # Автоматическая версия из коммита | ||||
|         VERSION="v0.0.${CI_PIPELINE_ID}" | ||||
|       fi | ||||
|   only: | ||||
|     - tags | ||||
|   when: manual | ||||
|        | ||||
| # ============================================ | ||||
| # Уведомление об успешной сборке | ||||
| # ============================================ | ||||
| .notify_success: | ||||
|   after_script: | ||||
|     - echo "✅ Build completed successfully!" | ||||
|     - echo "📦 Artifacts are available in the pipeline artifacts" | ||||
|     - echo "🔗 Download URL: ${CI_JOB_URL}/artifacts/download" | ||||
|       echo "📦 Creating GitLab Release: $VERSION" | ||||
|       echo "📝 Commit: ${CI_COMMIT_SHORT_SHA}" | ||||
|       echo "🔗 Branch: ${CI_COMMIT_BRANCH}" | ||||
|        | ||||
|       # Проверяем наличие APK файлов | ||||
|       APK_ARM64="build/app/outputs/flutter-apk/app-arm64-v8a-release.apk" | ||||
|       APK_ARM32="build/app/outputs/flutter-apk/app-armeabi-v7a-release.apk" | ||||
|       APK_X86="build/app/outputs/flutter-apk/app-x86_64-release.apk" | ||||
|       AAR_TORRENT="android/torrentengine/build/outputs/aar/torrentengine-release.aar" | ||||
|        | ||||
|       # Создаем описание релиза | ||||
|       RELEASE_DESCRIPTION="## NeoMovies Mobile ${VERSION} | ||||
|        | ||||
|       **Build Info:** | ||||
|       - Commit: \`${CI_COMMIT_SHORT_SHA}\` | ||||
|       - Branch: \`${CI_COMMIT_BRANCH}\` | ||||
|       - Pipeline: [#${CI_PIPELINE_ID}](${CI_PIPELINE_URL}) | ||||
|        | ||||
|       **Downloads:** | ||||
|       " | ||||
|        | ||||
|       # Подсчитываем файлы | ||||
|       FILE_COUNT=0 | ||||
|       [ -f "$APK_ARM64" ] && FILE_COUNT=$((FILE_COUNT+1)) && RELEASE_DESCRIPTION="${RELEASE_DESCRIPTION}\n- ARM64 APK: \`app-arm64-v8a-release.apk\`" | ||||
|       [ -f "$APK_ARM32" ] && FILE_COUNT=$((FILE_COUNT+1)) && RELEASE_DESCRIPTION="${RELEASE_DESCRIPTION}\n- ARM32 APK: \`app-armeabi-v7a-release.apk\`" | ||||
|       [ -f "$APK_X86" ] && FILE_COUNT=$((FILE_COUNT+1)) && RELEASE_DESCRIPTION="${RELEASE_DESCRIPTION}\n- x86_64 APK: \`app-x86_64-release.apk\`" | ||||
|       [ -f "$AAR_TORRENT" ] && FILE_COUNT=$((FILE_COUNT+1)) && RELEASE_DESCRIPTION="${RELEASE_DESCRIPTION}\n- TorrentEngine Library: \`torrentengine-release.aar\`" | ||||
|        | ||||
|       if [ $FILE_COUNT -eq 0 ]; then | ||||
|         echo "❌ No release artifacts found!" | ||||
|         exit 1 | ||||
|       fi | ||||
|        | ||||
|       echo "✅ Found $FILE_COUNT artifact(s) to release" | ||||
|        | ||||
|       # Создаем релиз через GitLab API | ||||
|       RELEASE_PAYLOAD=$(cat <<EOF | ||||
|       { | ||||
|         "name": "NeoMovies ${VERSION}", | ||||
|         "tag_name": "${VERSION}", | ||||
|         "description": "${RELEASE_DESCRIPTION}", | ||||
|         "ref": "${CI_COMMIT_SHA}", | ||||
|         "assets": { | ||||
|           "links": [] | ||||
|         } | ||||
|       } | ||||
|       EOF | ||||
|       ) | ||||
|        | ||||
|       echo "🚀 Creating release via GitLab API..." | ||||
|        | ||||
|       RESPONSE=$(curl --fail-with-body -s -X POST \ | ||||
|         "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/releases" \ | ||||
|         --header "PRIVATE-TOKEN: ${CI_JOB_TOKEN}" \ | ||||
|         --header "Content-Type: application/json" \ | ||||
|         --data "${RELEASE_PAYLOAD}" || echo "FAILED") | ||||
|        | ||||
|       if [ "$RESPONSE" = "FAILED" ]; then | ||||
|         echo "⚠️  Release API call failed, trying alternative method..." | ||||
|         # Если релиз уже существует, пробуем обновить | ||||
|         curl -s -X PUT \ | ||||
|           "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/releases/${VERSION}" \ | ||||
|           --header "PRIVATE-TOKEN: ${CI_JOB_TOKEN}" \ | ||||
|           --header "Content-Type: application/json" \ | ||||
|           --data "${RELEASE_PAYLOAD}" | ||||
|       fi | ||||
|        | ||||
|       echo "" | ||||
|       echo "✅ Release created successfully!" | ||||
|       echo "🔗 View release: ${CI_PROJECT_URL}/-/releases/${VERSION}" | ||||
|       echo "📦 Pipeline artifacts: ${CI_JOB_URL}/artifacts/browse" | ||||
|   artifacts: | ||||
|     paths: | ||||
|       - build/app/outputs/flutter-apk/*.apk | ||||
|       - android/torrentengine/build/outputs/aar/*.aar | ||||
|     expire_in: 90 days | ||||
|   rules: | ||||
|     - if: $CI_COMMIT_TAG | ||||
|       when: always | ||||
|     - if: $CI_COMMIT_BRANCH == "dev" | ||||
|       when: on_success | ||||
|     - if: $CI_COMMIT_BRANCH == "main" | ||||
|       when: on_success | ||||
|   | ||||
| @@ -16,5 +16,5 @@ kotlin.incremental=true | ||||
| kotlin.incremental.usePreciseJavaTracking=true | ||||
|  | ||||
| # Build optimization | ||||
| android.enableBuildCache=true | ||||
| # android.enableBuildCache=true  # Deprecated in AGP 7.0+, use org.gradle.caching instead | ||||
| org.gradle.vfs.watch=false | ||||
|   | ||||
| @@ -28,7 +28,7 @@ plugins { | ||||
|     id("dev.flutter.flutter-plugin-loader") version "1.0.0" | ||||
|     id("com.android.application") version "8.7.3" apply false | ||||
|     id("com.android.library") version "8.7.3" apply false | ||||
|     id("org.jetbrains.kotlin.android") version "2.1.0" apply false | ||||
|     id("org.jetbrains.kotlin.android") version "1.9.24" apply false | ||||
| } | ||||
|  | ||||
| include(":app") | ||||
|   | ||||
| @@ -62,6 +62,7 @@ dependencies { | ||||
|     implementation("com.google.code.gson:gson:2.11.0") | ||||
|  | ||||
|     // LibTorrent4j - Java bindings for libtorrent | ||||
|     // Using main package which includes native libraries | ||||
|     implementation("org.libtorrent4j:libtorrent4j:2.1.0-28") | ||||
|     implementation("org.libtorrent4j:libtorrent4j-android-arm64:2.1.0-28") | ||||
|     implementation("org.libtorrent4j:libtorrent4j-android-arm:2.1.0-28") | ||||
|   | ||||
| @@ -1,5 +1,6 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <manifest xmlns:android="http://schemas.android.com/apk/res/android"> | ||||
| <manifest xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     xmlns:tools="http://schemas.android.com/tools"> | ||||
|  | ||||
|     <!-- Permissions for torrent engine --> | ||||
|     <uses-permission android:name="android.permission.INTERNET" /> | ||||
|   | ||||
| @@ -11,6 +11,7 @@ import kotlinx.coroutines.* | ||||
| import kotlinx.coroutines.flow.* | ||||
| import org.libtorrent4j.* | ||||
| import org.libtorrent4j.alerts.* | ||||
| import org.libtorrent4j.TorrentInfo as LibTorrentInfo | ||||
| import java.io.File | ||||
|  | ||||
| /** | ||||
| @@ -41,13 +42,17 @@ class TorrentEngine private constructor(private val context: Context) { | ||||
|     private val torrentHandles = mutableMapOf<String, TorrentHandle>() | ||||
|      | ||||
|     // Settings | ||||
|     private val settings = SettingsPack().apply { | ||||
|         setInteger(SettingsPack.Key.ALERT_MASK.value(), Alert.Category.ALL.swig()) | ||||
|         setBoolean(SettingsPack.Key.ENABLE_DHT.value(), true) | ||||
|         setBoolean(SettingsPack.Key.ENABLE_LSD.value(), true) | ||||
|         setString(SettingsPack.Key.USER_AGENT.value(), "NeoMovies/1.0 libtorrent4j/2.1.0") | ||||
|     private val settingsPack = SettingsPack().apply { | ||||
|         // Enable DHT for magnet links | ||||
|         setEnableDht(true) | ||||
|         // Enable Local Service Discovery | ||||
|         setEnableLsd(true) | ||||
|         // User agent | ||||
|         setString(org.libtorrent4j.swig.settings_pack.string_types.user_agent.swigValue(), "NeoMovies/1.0 libtorrent4j/2.1.0") | ||||
|     } | ||||
|      | ||||
|     private val sessionParams = SessionParams(settingsPack) | ||||
|  | ||||
|     init { | ||||
|         startSession() | ||||
|         restoreTorrents() | ||||
| @@ -60,7 +65,7 @@ class TorrentEngine private constructor(private val context: Context) { | ||||
|     private fun startSession() { | ||||
|         try { | ||||
|             session = SessionManager() | ||||
|             session?.start(settings) | ||||
|             session?.start(sessionParams) | ||||
|             isSessionStarted = true | ||||
|             Log.d(TAG, "LibTorrent session started") | ||||
|         } catch (e: Exception) { | ||||
| @@ -93,21 +98,21 @@ class TorrentEngine private constructor(private val context: Context) { | ||||
|      * Start alert listener for torrent events | ||||
|      */ | ||||
|     private fun startAlertListener() { | ||||
|         scope.launch { | ||||
|             while (isActive && isSessionStarted) { | ||||
|                 try { | ||||
|                     session?.let { sess -> | ||||
|                         val alerts = sess.popAlerts() | ||||
|                         for (alert in alerts) { | ||||
|                             handleAlert(alert) | ||||
|                         } | ||||
|                     } | ||||
|                     delay(1000) // Check every second | ||||
|                 } catch (e: Exception) { | ||||
|                     Log.e(TAG, "Error in alert listener", e) | ||||
|                 } | ||||
|         session?.addListener(object : AlertListener { | ||||
|             override fun types(): IntArray { | ||||
|                 return intArrayOf( | ||||
|                     AlertType.METADATA_RECEIVED.swig(), | ||||
|                     AlertType.TORRENT_FINISHED.swig(), | ||||
|                     AlertType.TORRENT_ERROR.swig(), | ||||
|                     AlertType.STATE_CHANGED.swig(), | ||||
|                     AlertType.TORRENT_CHECKED.swig() | ||||
|                 ) | ||||
|             } | ||||
|         } | ||||
|              | ||||
|             override fun alert(alert: Alert<*>) { | ||||
|                 handleAlert(alert) | ||||
|             } | ||||
|         }) | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -191,7 +196,8 @@ class TorrentEngine private constructor(private val context: Context) { | ||||
|         scope.launch { | ||||
|             val handle = alert.handle() | ||||
|             val infoHash = handle.infoHash().toHex() | ||||
|             val error = alert.error().message() | ||||
|             // message is a property in Kotlin | ||||
|             val error = alert.error().message | ||||
|              | ||||
|             Log.e(TAG, "Torrent error: $infoHash - $error") | ||||
|             torrentDao.setTorrentError(infoHash, error) | ||||
| @@ -205,7 +211,8 @@ class TorrentEngine private constructor(private val context: Context) { | ||||
|         scope.launch { | ||||
|             val handle = alert.handle() | ||||
|             val infoHash = handle.infoHash().toHex() | ||||
|             val state = when (alert.state()) { | ||||
|             val status = handle.status() | ||||
|             val state = when (status.state()) { | ||||
|                 TorrentStatus.State.CHECKING_FILES -> TorrentState.CHECKING | ||||
|                 TorrentStatus.State.DOWNLOADING_METADATA -> TorrentState.METADATA_DOWNLOADING | ||||
|                 TorrentStatus.State.DOWNLOADING -> TorrentState.DOWNLOADING | ||||
| @@ -251,15 +258,11 @@ class TorrentEngine private constructor(private val context: Context) { | ||||
|     ): String { | ||||
|         return withContext(Dispatchers.IO) { | ||||
|             try { | ||||
|                 // Parse magnet URI | ||||
|                 val error = ErrorCode() | ||||
|                 val params = SessionHandle.parseMagnetUri(magnetUri, error) | ||||
|                 // Parse magnet URI using new API | ||||
|                 val params = AddTorrentParams.parseMagnetUri(magnetUri) | ||||
|                  | ||||
|                 if (error.value() != 0) { | ||||
|                     throw Exception("Invalid magnet URI: ${error.message()}") | ||||
|                 } | ||||
|                  | ||||
|                 val infoHash = params.infoHash().toHex() | ||||
|                 // Get info hash from parsed params - best is a property | ||||
|                 val infoHash = params.infoHashes.best.toHex() | ||||
|                  | ||||
|                 // Check if already exists | ||||
|                 val existing = existingTorrent ?: torrentDao.getTorrent(infoHash) | ||||
| @@ -268,22 +271,16 @@ class TorrentEngine private constructor(private val context: Context) { | ||||
|                     return@withContext infoHash | ||||
|                 } | ||||
|                  | ||||
|                 // Set save path | ||||
|                 // Set save path and apply to params | ||||
|                 val saveDir = File(savePath) | ||||
|                 if (!saveDir.exists()) { | ||||
|                     saveDir.mkdirs() | ||||
|                 } | ||||
|                 params.savePath(saveDir.absolutePath) | ||||
|                 params.swig().setSave_path(saveDir.absolutePath) | ||||
|                  | ||||
|                 // Add to session | ||||
|                 val handle = session?.swig()?.addTorrent(params, error) | ||||
|                     ?: throw Exception("Session not initialized") | ||||
|                  | ||||
|                 if (error.value() != 0) { | ||||
|                     throw Exception("Failed to add torrent: ${error.message()}") | ||||
|                 } | ||||
|                  | ||||
|                 torrentHandles[infoHash] = TorrentHandle(handle) | ||||
|                 // Add to session using async API | ||||
|                 // Handle will be received asynchronously via ADD_TORRENT alert | ||||
|                 session?.swig()?.async_add_torrent(params.swig()) ?: throw Exception("Session not initialized") | ||||
|                  | ||||
|                 // Save to database | ||||
|                 val torrentInfo = TorrentInfo( | ||||
| @@ -334,9 +331,11 @@ class TorrentEngine private constructor(private val context: Context) { | ||||
|                 Log.d(TAG, "Torrent paused: $infoHash") | ||||
|                  | ||||
|                 // Stop service if no active torrents | ||||
|                 if (torrentDao.getActiveTorrents().isEmpty()) { | ||||
|                 val activeTorrents = torrentDao.getActiveTorrents() | ||||
|                 if (activeTorrents.isEmpty()) { | ||||
|                     stopService() | ||||
|                 } | ||||
|                 Unit // Explicitly return Unit | ||||
|             } catch (e: Exception) { | ||||
|                 Log.e(TAG, "Failed to pause torrent", e) | ||||
|             } | ||||
| @@ -372,9 +371,11 @@ class TorrentEngine private constructor(private val context: Context) { | ||||
|                 Log.d(TAG, "Torrent removed: $infoHash") | ||||
|                  | ||||
|                 // Stop service if no active torrents | ||||
|                 if (torrentDao.getActiveTorrents().isEmpty()) { | ||||
|                 val activeTorrents = torrentDao.getActiveTorrents() | ||||
|                 if (activeTorrents.isEmpty()) { | ||||
|                     stopService() | ||||
|                 } | ||||
|                 Unit // Explicitly return Unit | ||||
|             } catch (e: Exception) { | ||||
|                 Log.e(TAG, "Failed to remove torrent", e) | ||||
|             } | ||||
| @@ -393,7 +394,15 @@ class TorrentEngine private constructor(private val context: Context) { | ||||
|         withContext(Dispatchers.IO) { | ||||
|             try { | ||||
|                 val handle = torrentHandles[infoHash] ?: return@withContext | ||||
|                 handle.filePriority(fileIndex, Priority.getValue(priority.value)) | ||||
|                 // Convert FilePriority to LibTorrent Priority | ||||
|                 val libPriority = when (priority) { | ||||
|                     FilePriority.DONT_DOWNLOAD -> Priority.IGNORE | ||||
|                     FilePriority.LOW -> Priority.LOW | ||||
|                     FilePriority.NORMAL -> Priority.DEFAULT | ||||
|                     FilePriority.HIGH -> Priority.TOP_PRIORITY | ||||
|                     else -> Priority.DEFAULT // Default | ||||
|                 } | ||||
|                 handle.filePriority(fileIndex, libPriority) | ||||
|                  | ||||
|                 // Update database | ||||
|                 val torrent = torrentDao.getTorrent(infoHash) ?: return@withContext | ||||
| @@ -418,7 +427,14 @@ class TorrentEngine private constructor(private val context: Context) { | ||||
|                 val handle = torrentHandles[infoHash] ?: return@withContext | ||||
|                  | ||||
|                 priorities.forEach { (fileIndex, priority) -> | ||||
|                     handle.filePriority(fileIndex, Priority.getValue(priority.value)) | ||||
|                     val libPriority = when (priority) { | ||||
|                         FilePriority.DONT_DOWNLOAD -> Priority.IGNORE | ||||
|                         FilePriority.LOW -> Priority.LOW | ||||
|                         FilePriority.NORMAL -> Priority.DEFAULT | ||||
|                         FilePriority.HIGH -> Priority.TOP_PRIORITY | ||||
|                         else -> Priority.DEFAULT // Default | ||||
|                     } | ||||
|                     handle.filePriority(fileIndex, libPriority) | ||||
|                 } | ||||
|                  | ||||
|                 // Update database | ||||
|   | ||||
		Reference in New Issue
	
	Block a user