mirror of
https://gitlab.com/foxixus/neomovies_mobile.git
synced 2025-10-28 01:58:50 +05:00
Merge branch 'feature/torrent-engine-integration' into 'main'
fix(build): resolve Gradle and manifest issues for TorrentEngine See merge request foxixus/neomovies_mobile!2
This commit is contained in:
269
.gitlab-ci.yml
269
.gitlab-ci.yml
@@ -1,202 +1,201 @@
|
|||||||
# GitLab CI/CD Configuration for NeoMovies Mobile
|
|
||||||
# Автоматическая сборка APK и TorrentEngine модуля
|
|
||||||
|
|
||||||
stages:
|
stages:
|
||||||
- build
|
- build
|
||||||
- test
|
- test
|
||||||
- deploy
|
- deploy
|
||||||
|
|
||||||
variables:
|
variables:
|
||||||
# Flutter версия
|
GRADLE_OPTS: "-Dorg.gradle.daemon=false -Dorg.gradle.jvmargs='-Xmx2048m' -Dorg.gradle.parallel=true"
|
||||||
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_USER_HOME: "${CI_PROJECT_DIR}/.gradle"
|
GRADLE_USER_HOME: "${CI_PROJECT_DIR}/.gradle"
|
||||||
PUB_CACHE: "${CI_PROJECT_DIR}/.pub-cache"
|
PUB_CACHE: "${CI_PROJECT_DIR}/.pub-cache"
|
||||||
|
|
||||||
# Кэширование для ускорения сборки
|
|
||||||
cache:
|
cache:
|
||||||
key: ${CI_COMMIT_REF_SLUG}
|
key: ${CI_COMMIT_REF_SLUG}
|
||||||
paths:
|
paths:
|
||||||
- .gradle/
|
- .gradle/
|
||||||
- .pub-cache/
|
- .pub-cache/
|
||||||
- android/.gradle/
|
- android/.gradle/
|
||||||
- android/build/
|
|
||||||
- build/
|
- build/
|
||||||
|
|
||||||
# ============================================
|
|
||||||
# Сборка только TorrentEngine модуля
|
|
||||||
# ============================================
|
|
||||||
build:torrent-engine:
|
build:torrent-engine:
|
||||||
stage: build
|
stage: build
|
||||||
image: mingc/android-build-box:latest
|
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:
|
script:
|
||||||
- echo "Building TorrentEngine library module..."
|
|
||||||
- cd android
|
- cd android
|
||||||
# Собираем только модуль torrentengine
|
- chmod +x gradlew
|
||||||
- ./gradlew :torrentengine:assembleRelease --no-daemon --parallel --build-cache
|
- ./gradlew :torrentengine:assembleRelease --no-daemon --stacktrace
|
||||||
- ls -lah torrentengine/build/outputs/aar/
|
|
||||||
artifacts:
|
artifacts:
|
||||||
name: "torrentengine-${CI_COMMIT_SHORT_SHA}"
|
|
||||||
paths:
|
paths:
|
||||||
- android/torrentengine/build/outputs/aar/*.aar
|
- android/torrentengine/build/outputs/aar/*.aar
|
||||||
expire_in: 1 week
|
expire_in: 30 days
|
||||||
only:
|
rules:
|
||||||
- dev
|
- if: $CI_COMMIT_BRANCH == "dev"
|
||||||
- feature/torrent-engine-integration
|
- if: $CI_COMMIT_BRANCH == "main"
|
||||||
- merge_requests
|
- if: $CI_COMMIT_BRANCH =~ /^feature\//
|
||||||
when: on_success
|
- if: $CI_COMMIT_TAG
|
||||||
|
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
|
||||||
|
|
||||||
# ============================================
|
|
||||||
# Сборка Debug APK
|
|
||||||
# ============================================
|
|
||||||
build:apk-debug:
|
build:apk-debug:
|
||||||
stage: build
|
stage: build
|
||||||
image: mingc/android-build-box:latest
|
image: ghcr.io/cirruslabs/flutter:stable
|
||||||
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
|
|
||||||
script:
|
script:
|
||||||
- echo "Building Debug APK..."
|
- flutter pub get
|
||||||
- flutter build apk --debug --target-platform android-arm64
|
- flutter build apk --debug
|
||||||
- ls -lah build/app/outputs/flutter-apk/
|
|
||||||
artifacts:
|
artifacts:
|
||||||
name: "neomovies-debug-${CI_COMMIT_SHORT_SHA}"
|
|
||||||
paths:
|
paths:
|
||||||
- build/app/outputs/flutter-apk/app-debug.apk
|
- build/app/outputs/flutter-apk/app-debug.apk
|
||||||
expire_in: 1 week
|
expire_in: 1 week
|
||||||
only:
|
rules:
|
||||||
- dev
|
- if: $CI_COMMIT_BRANCH == "dev"
|
||||||
- feature/torrent-engine-integration
|
- if: $CI_COMMIT_BRANCH =~ /^feature\//
|
||||||
- merge_requests
|
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
|
||||||
when: on_success
|
|
||||||
allow_failure: true
|
allow_failure: true
|
||||||
|
|
||||||
# ============================================
|
|
||||||
# Сборка Release APK (только для dev)
|
|
||||||
# ============================================
|
|
||||||
build:apk-release:
|
build:apk-release:
|
||||||
stage: build
|
stage: build
|
||||||
image: mingc/android-build-box:latest
|
image: ghcr.io/cirruslabs/flutter:stable
|
||||||
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
|
|
||||||
script:
|
script:
|
||||||
- echo "Building Release APK..."
|
- flutter pub get
|
||||||
# Сборка с split-per-abi для уменьшения размера
|
- flutter build apk --release --split-per-abi
|
||||||
- flutter build apk --release --split-per-abi --target-platform android-arm64
|
|
||||||
- ls -lah build/app/outputs/flutter-apk/
|
|
||||||
artifacts:
|
artifacts:
|
||||||
name: "neomovies-release-${CI_COMMIT_SHORT_SHA}"
|
|
||||||
paths:
|
paths:
|
||||||
- build/app/outputs/flutter-apk/app-arm64-v8a-release.apk
|
- 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
|
expire_in: 30 days
|
||||||
only:
|
rules:
|
||||||
- dev
|
- if: $CI_COMMIT_BRANCH == "dev"
|
||||||
when: on_success
|
- if: $CI_COMMIT_BRANCH == "main"
|
||||||
|
- if: $CI_COMMIT_BRANCH =~ /^feature\//
|
||||||
|
- if: $CI_COMMIT_TAG
|
||||||
allow_failure: true
|
allow_failure: true
|
||||||
|
|
||||||
# ============================================
|
|
||||||
# Анализ кода Flutter
|
|
||||||
# ============================================
|
|
||||||
test:flutter-analyze:
|
test:flutter-analyze:
|
||||||
stage: test
|
stage: test
|
||||||
image: mingc/android-build-box:latest
|
image: ghcr.io/cirruslabs/flutter:stable
|
||||||
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
|
|
||||||
script:
|
script:
|
||||||
- echo "Running Flutter analyze..."
|
- flutter pub get
|
||||||
- flutter analyze --no-fatal-infos || true
|
- flutter analyze
|
||||||
only:
|
rules:
|
||||||
- dev
|
- if: $CI_COMMIT_BRANCH == "dev"
|
||||||
- merge_requests
|
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
|
||||||
allow_failure: true
|
allow_failure: true
|
||||||
|
|
||||||
# ============================================
|
|
||||||
# Kotlin/Android lint
|
|
||||||
# ============================================
|
|
||||||
test:android-lint:
|
test:android-lint:
|
||||||
stage: test
|
stage: test
|
||||||
image: mingc/android-build-box:latest
|
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:
|
script:
|
||||||
- echo "Running Android Lint..."
|
|
||||||
- cd android
|
- cd android
|
||||||
- ./gradlew lint --no-daemon || true
|
- chmod +x gradlew
|
||||||
|
- ./gradlew lint --no-daemon
|
||||||
artifacts:
|
artifacts:
|
||||||
name: "lint-reports-${CI_COMMIT_SHORT_SHA}"
|
|
||||||
paths:
|
paths:
|
||||||
- android/app/build/reports/lint-results*.html
|
- android/app/build/reports/lint-*.html
|
||||||
- android/torrentengine/build/reports/lint-results*.html
|
- android/torrentengine/build/reports/lint-*.html
|
||||||
expire_in: 1 week
|
expire_in: 1 week
|
||||||
only:
|
when: always
|
||||||
- dev
|
rules:
|
||||||
- merge_requests
|
- if: $CI_COMMIT_BRANCH == "dev"
|
||||||
|
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
|
||||||
allow_failure: true
|
allow_failure: true
|
||||||
|
|
||||||
# ============================================
|
|
||||||
# Deploy к релизам (опционально)
|
|
||||||
# ============================================
|
|
||||||
deploy:release:
|
deploy:release:
|
||||||
stage: deploy
|
stage: deploy
|
||||||
image: alpine:latest
|
image: alpine:latest
|
||||||
tags:
|
needs:
|
||||||
- docker
|
- build:apk-release
|
||||||
|
- build:torrent-engine
|
||||||
before_script:
|
before_script:
|
||||||
- apk add --no-cache curl jq
|
- apk add --no-cache curl jq
|
||||||
script:
|
script:
|
||||||
- echo "Creating GitLab Release..."
|
|
||||||
- |
|
- |
|
||||||
if [ -f "build/app/outputs/flutter-apk/app-arm64-v8a-release.apk" ]; then
|
# Определяем версию релиза
|
||||||
echo "Release APK found"
|
if [ -n "$CI_COMMIT_TAG" ]; then
|
||||||
# Здесь можно добавить публикацию в GitLab Releases или другой deployment
|
VERSION="$CI_COMMIT_TAG"
|
||||||
|
else
|
||||||
|
# Автоматическая версия из коммита
|
||||||
|
VERSION="v0.0.${CI_PIPELINE_ID}"
|
||||||
fi
|
fi
|
||||||
only:
|
|
||||||
- tags
|
echo "📦 Creating GitLab Release: $VERSION"
|
||||||
when: manual
|
echo "📝 Commit: ${CI_COMMIT_SHORT_SHA}"
|
||||||
|
echo "🔗 Branch: ${CI_COMMIT_BRANCH}"
|
||||||
# ============================================
|
|
||||||
# Уведомление об успешной сборке
|
# Проверяем наличие APK файлов
|
||||||
# ============================================
|
APK_ARM64="build/app/outputs/flutter-apk/app-arm64-v8a-release.apk"
|
||||||
.notify_success:
|
APK_ARM32="build/app/outputs/flutter-apk/app-armeabi-v7a-release.apk"
|
||||||
after_script:
|
APK_X86="build/app/outputs/flutter-apk/app-x86_64-release.apk"
|
||||||
- echo "✅ Build completed successfully!"
|
AAR_TORRENT="android/torrentengine/build/outputs/aar/torrentengine-release.aar"
|
||||||
- echo "📦 Artifacts are available in the pipeline artifacts"
|
|
||||||
- echo "🔗 Download URL: ${CI_JOB_URL}/artifacts/download"
|
# Создаем описание релиза
|
||||||
|
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
|
kotlin.incremental.usePreciseJavaTracking=true
|
||||||
|
|
||||||
# Build optimization
|
# Build optimization
|
||||||
android.enableBuildCache=true
|
# android.enableBuildCache=true # Deprecated in AGP 7.0+, use org.gradle.caching instead
|
||||||
org.gradle.vfs.watch=false
|
org.gradle.vfs.watch=false
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ plugins {
|
|||||||
id("dev.flutter.flutter-plugin-loader") version "1.0.0"
|
id("dev.flutter.flutter-plugin-loader") version "1.0.0"
|
||||||
id("com.android.application") version "8.7.3" apply false
|
id("com.android.application") version "8.7.3" apply false
|
||||||
id("com.android.library") 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")
|
include(":app")
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ dependencies {
|
|||||||
implementation("com.google.code.gson:gson:2.11.0")
|
implementation("com.google.code.gson:gson:2.11.0")
|
||||||
|
|
||||||
// LibTorrent4j - Java bindings for libtorrent
|
// LibTorrent4j - Java bindings for libtorrent
|
||||||
|
// Using main package which includes native libraries
|
||||||
implementation("org.libtorrent4j:libtorrent4j:2.1.0-28")
|
implementation("org.libtorrent4j:libtorrent4j:2.1.0-28")
|
||||||
implementation("org.libtorrent4j:libtorrent4j-android-arm64:2.1.0-28")
|
implementation("org.libtorrent4j:libtorrent4j-android-arm64:2.1.0-28")
|
||||||
implementation("org.libtorrent4j:libtorrent4j-android-arm: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"?>
|
<?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 -->
|
<!-- Permissions for torrent engine -->
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import kotlinx.coroutines.*
|
|||||||
import kotlinx.coroutines.flow.*
|
import kotlinx.coroutines.flow.*
|
||||||
import org.libtorrent4j.*
|
import org.libtorrent4j.*
|
||||||
import org.libtorrent4j.alerts.*
|
import org.libtorrent4j.alerts.*
|
||||||
|
import org.libtorrent4j.TorrentInfo as LibTorrentInfo
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -41,12 +42,16 @@ class TorrentEngine private constructor(private val context: Context) {
|
|||||||
private val torrentHandles = mutableMapOf<String, TorrentHandle>()
|
private val torrentHandles = mutableMapOf<String, TorrentHandle>()
|
||||||
|
|
||||||
// Settings
|
// Settings
|
||||||
private val settings = SettingsPack().apply {
|
private val settingsPack = SettingsPack().apply {
|
||||||
setInteger(SettingsPack.Key.ALERT_MASK.value(), Alert.Category.ALL.swig())
|
// Enable DHT for magnet links
|
||||||
setBoolean(SettingsPack.Key.ENABLE_DHT.value(), true)
|
setEnableDht(true)
|
||||||
setBoolean(SettingsPack.Key.ENABLE_LSD.value(), true)
|
// Enable Local Service Discovery
|
||||||
setString(SettingsPack.Key.USER_AGENT.value(), "NeoMovies/1.0 libtorrent4j/2.1.0")
|
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 {
|
init {
|
||||||
startSession()
|
startSession()
|
||||||
@@ -60,7 +65,7 @@ class TorrentEngine private constructor(private val context: Context) {
|
|||||||
private fun startSession() {
|
private fun startSession() {
|
||||||
try {
|
try {
|
||||||
session = SessionManager()
|
session = SessionManager()
|
||||||
session?.start(settings)
|
session?.start(sessionParams)
|
||||||
isSessionStarted = true
|
isSessionStarted = true
|
||||||
Log.d(TAG, "LibTorrent session started")
|
Log.d(TAG, "LibTorrent session started")
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
@@ -93,21 +98,21 @@ class TorrentEngine private constructor(private val context: Context) {
|
|||||||
* Start alert listener for torrent events
|
* Start alert listener for torrent events
|
||||||
*/
|
*/
|
||||||
private fun startAlertListener() {
|
private fun startAlertListener() {
|
||||||
scope.launch {
|
session?.addListener(object : AlertListener {
|
||||||
while (isActive && isSessionStarted) {
|
override fun types(): IntArray {
|
||||||
try {
|
return intArrayOf(
|
||||||
session?.let { sess ->
|
AlertType.METADATA_RECEIVED.swig(),
|
||||||
val alerts = sess.popAlerts()
|
AlertType.TORRENT_FINISHED.swig(),
|
||||||
for (alert in alerts) {
|
AlertType.TORRENT_ERROR.swig(),
|
||||||
handleAlert(alert)
|
AlertType.STATE_CHANGED.swig(),
|
||||||
}
|
AlertType.TORRENT_CHECKED.swig()
|
||||||
}
|
)
|
||||||
delay(1000) // Check every second
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Log.e(TAG, "Error in alert listener", e)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
override fun alert(alert: Alert<*>) {
|
||||||
|
handleAlert(alert)
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -191,7 +196,8 @@ class TorrentEngine private constructor(private val context: Context) {
|
|||||||
scope.launch {
|
scope.launch {
|
||||||
val handle = alert.handle()
|
val handle = alert.handle()
|
||||||
val infoHash = handle.infoHash().toHex()
|
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")
|
Log.e(TAG, "Torrent error: $infoHash - $error")
|
||||||
torrentDao.setTorrentError(infoHash, error)
|
torrentDao.setTorrentError(infoHash, error)
|
||||||
@@ -205,7 +211,8 @@ class TorrentEngine private constructor(private val context: Context) {
|
|||||||
scope.launch {
|
scope.launch {
|
||||||
val handle = alert.handle()
|
val handle = alert.handle()
|
||||||
val infoHash = handle.infoHash().toHex()
|
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.CHECKING_FILES -> TorrentState.CHECKING
|
||||||
TorrentStatus.State.DOWNLOADING_METADATA -> TorrentState.METADATA_DOWNLOADING
|
TorrentStatus.State.DOWNLOADING_METADATA -> TorrentState.METADATA_DOWNLOADING
|
||||||
TorrentStatus.State.DOWNLOADING -> TorrentState.DOWNLOADING
|
TorrentStatus.State.DOWNLOADING -> TorrentState.DOWNLOADING
|
||||||
@@ -251,15 +258,11 @@ class TorrentEngine private constructor(private val context: Context) {
|
|||||||
): String {
|
): String {
|
||||||
return withContext(Dispatchers.IO) {
|
return withContext(Dispatchers.IO) {
|
||||||
try {
|
try {
|
||||||
// Parse magnet URI
|
// Parse magnet URI using new API
|
||||||
val error = ErrorCode()
|
val params = AddTorrentParams.parseMagnetUri(magnetUri)
|
||||||
val params = SessionHandle.parseMagnetUri(magnetUri, error)
|
|
||||||
|
|
||||||
if (error.value() != 0) {
|
// Get info hash from parsed params - best is a property
|
||||||
throw Exception("Invalid magnet URI: ${error.message()}")
|
val infoHash = params.infoHashes.best.toHex()
|
||||||
}
|
|
||||||
|
|
||||||
val infoHash = params.infoHash().toHex()
|
|
||||||
|
|
||||||
// Check if already exists
|
// Check if already exists
|
||||||
val existing = existingTorrent ?: torrentDao.getTorrent(infoHash)
|
val existing = existingTorrent ?: torrentDao.getTorrent(infoHash)
|
||||||
@@ -268,22 +271,16 @@ class TorrentEngine private constructor(private val context: Context) {
|
|||||||
return@withContext infoHash
|
return@withContext infoHash
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set save path
|
// Set save path and apply to params
|
||||||
val saveDir = File(savePath)
|
val saveDir = File(savePath)
|
||||||
if (!saveDir.exists()) {
|
if (!saveDir.exists()) {
|
||||||
saveDir.mkdirs()
|
saveDir.mkdirs()
|
||||||
}
|
}
|
||||||
params.savePath(saveDir.absolutePath)
|
params.swig().setSave_path(saveDir.absolutePath)
|
||||||
|
|
||||||
// Add to session
|
// Add to session using async API
|
||||||
val handle = session?.swig()?.addTorrent(params, error)
|
// Handle will be received asynchronously via ADD_TORRENT alert
|
||||||
?: throw Exception("Session not initialized")
|
session?.swig()?.async_add_torrent(params.swig()) ?: throw Exception("Session not initialized")
|
||||||
|
|
||||||
if (error.value() != 0) {
|
|
||||||
throw Exception("Failed to add torrent: ${error.message()}")
|
|
||||||
}
|
|
||||||
|
|
||||||
torrentHandles[infoHash] = TorrentHandle(handle)
|
|
||||||
|
|
||||||
// Save to database
|
// Save to database
|
||||||
val torrentInfo = TorrentInfo(
|
val torrentInfo = TorrentInfo(
|
||||||
@@ -334,9 +331,11 @@ class TorrentEngine private constructor(private val context: Context) {
|
|||||||
Log.d(TAG, "Torrent paused: $infoHash")
|
Log.d(TAG, "Torrent paused: $infoHash")
|
||||||
|
|
||||||
// Stop service if no active torrents
|
// Stop service if no active torrents
|
||||||
if (torrentDao.getActiveTorrents().isEmpty()) {
|
val activeTorrents = torrentDao.getActiveTorrents()
|
||||||
|
if (activeTorrents.isEmpty()) {
|
||||||
stopService()
|
stopService()
|
||||||
}
|
}
|
||||||
|
Unit // Explicitly return Unit
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.e(TAG, "Failed to pause torrent", e)
|
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")
|
Log.d(TAG, "Torrent removed: $infoHash")
|
||||||
|
|
||||||
// Stop service if no active torrents
|
// Stop service if no active torrents
|
||||||
if (torrentDao.getActiveTorrents().isEmpty()) {
|
val activeTorrents = torrentDao.getActiveTorrents()
|
||||||
|
if (activeTorrents.isEmpty()) {
|
||||||
stopService()
|
stopService()
|
||||||
}
|
}
|
||||||
|
Unit // Explicitly return Unit
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.e(TAG, "Failed to remove torrent", e)
|
Log.e(TAG, "Failed to remove torrent", e)
|
||||||
}
|
}
|
||||||
@@ -393,7 +394,15 @@ class TorrentEngine private constructor(private val context: Context) {
|
|||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
try {
|
try {
|
||||||
val handle = torrentHandles[infoHash] ?: return@withContext
|
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
|
// Update database
|
||||||
val torrent = torrentDao.getTorrent(infoHash) ?: return@withContext
|
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
|
val handle = torrentHandles[infoHash] ?: return@withContext
|
||||||
|
|
||||||
priorities.forEach { (fileIndex, priority) ->
|
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
|
// Update database
|
||||||
|
|||||||
Reference in New Issue
Block a user