Как использовать Python в нативном Android приложении. Мой опыт

Привет, Хабр! В этой статье я хочу поделиться опытом разработки самого популярного загрузчика видео в RuStore и рассказать, как я использовал потрясающую библиотеку для интеграции Python-кода в свой проект.

Загрузчик видео доступен в RuStore

Загрузчик видео доступен в RuStore

К моему удивлению, я не нашел ни одного русскоязычного материала про Chaquopy, о котором сегодня пойдет речь.

Chaquopy — это библиотека, которая предоставляет всё необходимое для использования компонентов Python в Android приложение, включая:

  1. Полную интеграцию со стандартной системой сборки Gradle в Android Studio.

  2. Простые API для вызова Python-кода из Java/Kotlin и наоборот.

  3. Широкий спектр сторонних пакетов Python, включая SciPy, OpenCV, TensorFlow и многое другое.

Примеры приложений, использующих Chaquopy, включают Electron Cash и SummaryYou

Предыстория

В начале 2023 года я начал разработку своего загрузчика видео для Android.
За основу была взята библиотека yt-dlp, которая является швейцарским ножом в мире скачивания видео и используется практически всеми онлайн-загрузчиками видео.
Эта библиотека поддерживает более 1 тысячи ресурсов.
Самой большой проблемой оказалось найти работающую обертку на Java/Kotlin, реализующую весь функционал данной библиотеки. Спойлер: я её так и не нашел.
Но благодаря Chaquopy мне удалось интегрировать yt-dlp в свой проект, и это чертовски круто!

Забегая немного вперед, уточню, что были попытки перенести всю логику скачивания на сервер. Мне удалось реализовать склеивание видео и аудиодорожки для YouTube, что позволило пользователям скачивать видео в высоком качестве 1080p и выше.

Однако однажды подписчик решил потроллить меня, скачивая 24-часовые видео журчания Ниагарского водопада в 4K. У этого парня определенно есть чувство юмора:)
Тогда мне стало ясно, почему у многих онлайн-загрузчиков видео с YouTube нет функции выбора такого высокого качества, а лишь разрешения 360–720p.
Как говорится, если на стене висит ружьё, оно обязательно выстрелит.
Вдобавок к этому, YouTube очень быстро ввел лимиты на наш IP, из-за чего при скачивании возникали ошибки.

Было принято решение вернуться к клиентской обработке и скачиванию видео.
Как оказалось, современные смартфоны на Android довольно шустро справляются со склеиванием видео в высоком качестве. Да и как известно, лучшее — враг хорошего.

Начинаем работу с Chaquopy

В первой части мы подключим библиотеку и напишем простой Hello World, а во второй части я продемонстрирую пример использования библиотеки в своем рабочем проекте.
Весь код базового проекта я выложил на GitFlic.


// build.gradle.kts (Project:ChaquopyHelloWorld)
plugins {
    alias(libs.plugins.androidApplication) apply false
    alias(libs.plugins.jetbrainsKotlinAndroid) apply false
    id("com.chaquo.python") version "15.0.1" apply false
}

// build.gradle.kts (Module:app)
plugins {
    alias(libs.plugins.androidApplication)
    alias(libs.plugins.jetbrainsKotlinAndroid)
    id("com.chaquo.python")
}

android {
    
    defaultConfig {

        ndk {
            abiFilters += listOf("x86", "x86_64", "armeabi-v7a", "arm64-v8a")
        }
    }
    //// ....
}

chaquopy {
    defaultConfig {
        pip {
            // Здесь подключаем библиотеки, например yt-dlp
            // install("yt-dlp")
        }
    }
}

dependencies {

    ///....
}

Вот так выглядит весь код для подключения Chaquopy.
Стоит отметить, что необходимо указать abiFilters для поддержки всех архитектур процессора.
В блоке конфигурации Pip мы можем указать Python библиотеки, которые хотим подключить.

После установки зависимостей в файловой структуре проекта появится папка python. Именно туда мы и будем писать наш Python-код.

f39158cecb95d7711c8e9bdc4a16d834.png

Давайте напишим простейшую hello world функцию:

#helloworld.py

def get_text(name : str):
    return "Привет, " + name

Инициализируем Chaquopy в onCreate Application класса и вызываем в MainActivity функцию get_text в для получения строчки и отображаем её на экране:

class App : Application() {

    override fun onCreate() {
        super.onCreate()
        if (!Python.isStarted()) {
            Python.start(AndroidPlatform(this))
        }
    }

}



class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState) 
        val python = Python.getInstance()
        val helloText = python.getModule("helloworld")
            .callAttr("get_text", "Хабр").toString()

        setContent {
            ChaquopyHelloWorldTheme {
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colorScheme.background
                ) {
                    Text(text = helloText)
                }
            }
        }
    }
}

Усложняем

Теперь давайте перейдем к конкретному кейсу использования библиотеки в продакшн-проекте для загрузки видео с YouTube в высоком качестве 1080p+.

Кратко пробежимся по коду: я передаю в метод download ссылку на видео, формат видео (в yt-dlp это называется идентификатор качества), путь для сохранения (локальная папка приложения) и расширение конечного файла.
Сначала скачиваются аудио- и видеодорожки, затем с помощью FFmpegKit они склеиваются в единый видеофайл, а исходники удаляются.

Кстати, как вы могли заметить, Kotlin и Python код можно смешивать в одном файле.

val pythonFile = python.getModule("download_hq_youtube_vid")
                        pythonFile.callAttr(
                            "download",
                            url,
                            path,
                            filename,
                            format,
                            ext
                        )
import os
import yt_dlp
from com.arthenica.ffmpegkit import FFmpegKit


def download(url: str, path: str, title: str, format: str, ext: str):
    os.chdir(path)
    audio_filename = title + "_audio.webm"
    video_filename = title + "_video." + ext
    hq_options = {
        'format': format,
        'outtmpl': video_filename
    }
    yt_dlp.YoutubeDL(hq_options).download([url])
    video_output = title + "." + ext
    audio_options = {
        'format': 'bestaudio',
        'outtmpl': audio_filename
    }
    yt_dlp.YoutubeDL(audio_options).download([url])
    command = "-i " + video_filename + " -i " + audio_filename + " -c:v copy -c:a copy " + video_output
    FFmpegKit.execute(command)
    os.remove(audio_filename)
    os.remove(video_filename)

Надеюсь, что статья была вам полезной. Спасибо за внимание!

Habrahabr.ru прочитано 3754 раза