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:
2025-10-02 14:30:46 +00:00
6 changed files with 200 additions and 183 deletions

View File

@@ -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
when: manual
# ============================================ echo "📦 Creating GitLab Release: $VERSION"
# Уведомление об успешной сборке echo "📝 Commit: ${CI_COMMIT_SHORT_SHA}"
# ============================================ echo "🔗 Branch: ${CI_COMMIT_BRANCH}"
.notify_success:
after_script: # Проверяем наличие APK файлов
- echo "✅ Build completed successfully!" APK_ARM64="build/app/outputs/flutter-apk/app-arm64-v8a-release.apk"
- echo "📦 Artifacts are available in the pipeline artifacts" APK_ARM32="build/app/outputs/flutter-apk/app-armeabi-v7a-release.apk"
- echo "🔗 Download URL: ${CI_JOB_URL}/artifacts/download" 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

View File

@@ -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

View File

@@ -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")

View File

@@ -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")

View File

@@ -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" />

View File

@@ -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,13 +42,17 @@ 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()
restoreTorrents() restoreTorrents()
@@ -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