Інтеграція Whisper API для транскрибації у мобільному додатку

TRUETECH займається розробкою, підтримкою та обслуговуванням мобільних додатків iOS, Android, PWA. Маємо великий досвід та експертизу для публікації мобільних додатків до популярних маркетів Google Play, App Store, Amazon, AppGallery та інші.

Розробка та підтримка будь-яких видів мобільних додатків:

Інформаційні та розважальні мобільні програми
Новинки, ігри, довідники, онлайн-каталоги, погодні, фітнес та здоров'я, туристичні, освітні, соціальні мережі та месенджери, квіз, блоги та подкасти, форуми, агрегатори
Мобільні програми електронної комерції
Інтернет-магазини, B2B-додатки, маркетплейси, онлайн-обмінники, кешбек-сервіси, біржі, дропшиппінг-платформи, програми лояльності, доставка їжі та товарів, платіжні системи
Мобільні програми для управління бізнес-процесами
CRM-системи, ERP-системи, управління проектами, інструменти для команди продажів, облік фінансів, управління виробництвом, логістика та доставка, управління персоналом, системи моніторингу даних
Мобільні програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, платформи надання електронних послуг, платформи кешбеку, відеохостинги, тематичні портали, платформи онлайн-бронювання та запису, платформи онлайн-торгівлі

Це лише деякі з типів мобільних додатків, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Послуги, які ми пропонуємо
Показано 1 з 1Усі 1735 послуг
Інтеграція Whisper API для транскрибації у мобільному додатку
Простий
від 1 дня до 3 днів
Часті запитання

Наші компетенції:

Етапи розробки

Останні роботи

  • image_mobile-applications_feedme_467_0.webp
    Розробка мобільного додатка для компанії FEEDME
    792
  • image_mobile-applications_xoomer_471_0.webp
    Розробка мобільного додатку для компанії XOOMER
    671
  • image_mobile-applications_rhl_428_0.webp
    Розробка мобільного додатку для компанії RHL
    1097
  • image_mobile-applications_zippy_411_0.webp
    Розробка мобільного додатку для компанії ZIPPY
    969
  • image_mobile-applications_affhome_429_0.webp
    Розробка мобільного додатку для компанії Affhome
    914
  • image_mobile-applications_flavors_409_0.webp
    Розробка мобільного додатку для компанії FLAVORS
    495

Інтеграція Whisper API для трансгрипції в мобільних додатках

Whisper — це не просто «відправи аудіо, отримай текст». API має конкретні обмеження, які потребують підготовки на стороні клієнта: 25 МБ на файл, підтримка тільки певних кодеків, відсутність стрімінгу, синхронна відповідь. Якщо не врахувати ці деталі на етапі архітектури, інтеграція перетворюється на серію гарячих фіксів.

Ліміти та як з ними жити

25 МБ — жорсткий ліміт endpoint'а POST /v1/audio/transcriptions. Хвилина MP3 в 128 кbps займає ~1 МБ, значить ліміт — приблизно 25 хвилин. Для більшості голосових заміток хватає, для записів зустрічей — ні.

Рішення: нарізка на клієнті. На iOS — AVAssetExportSession з часовим діапазоном через AVAssetExportSession.timeRange. На Android — MediaExtractor + MediaMuxer для точної нарізки без перекодування, якщо вихідний кодек вже сумісний (AAC в MP4 — зазвичай так).

Кодек. API приймає mp3, mp4, mpeg, mpga, m4a, wav, webm. Важливо: контейнер, а не кодек всередину. .m4a з AAC — проходить. .m4a з ALAC — ні, отримаєш 400. На iOS після AVAssetExportSession з пресетом AVAssetExportPresetAppleM4A завжди буде AAC. На Android безпечніше конвертувати через MediaCodec в PCM → WAV, якщо не впевнений у джерелі.

Мова. Параметр language у форматі ISO-639-1 (ru, en, uk) прискорює трансгрипцію та зменшує помилки. Без нього Whisper витрачає час на детекцію мови та іноді помиляється на коротких фрагментах.

Реалізація на iOS (Swift)

struct WhisperService {
    private let apiKey: String
    private let session = URLSession.shared

    func transcribe(audioURL: URL, language: String = "ru") async throws -> String {
        var request = URLRequest(url: URL(string: "https://api.openai.com/v1/audio/transcriptions")!)
        request.httpMethod = "POST"
        request.setValue("Bearer \(apiKey)", forHTTPHeaderField: "Authorization")

        let boundary = UUID().uuidString
        request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")

        var body = Data()
        // Додаємо файл
        body.append("--\(boundary)\r\n".data(using: .utf8)!)
        body.append("Content-Disposition: form-data; name=\"file\"; filename=\"audio.m4a\"\r\n".data(using: .utf8)!)
        body.append("Content-Type: audio/m4a\r\n\r\n".data(using: .utf8)!)
        body.append(try Data(contentsOf: audioURL))
        body.append("\r\n".data(using: .utf8)!)
        // Додаємо модель та мову
        body.append("--\(boundary)\r\n".data(using: .utf8)!)
        body.append("Content-Disposition: form-data; name=\"model\"\r\n\r\nwhisper-1\r\n".data(using: .utf8)!)
        body.append("--\(boundary)\r\n".data(using: .utf8)!)
        body.append("Content-Disposition: form-data; name=\"language\"\r\n\r\n\(language)\r\n".data(using: .utf8)!)
        body.append("--\(boundary)--\r\n".data(using: .utf8)!)

        request.httpBody = body
        let (data, _) = try await session.data(for: request)
        let response = try JSONDecoder().decode(TranscriptionResponse.self, from: data)
        return response.text
    }
}

Для файлів більше 25 МБ — перед викликом transcribe запускаємо AudioChunker.split(url:maxBytes:), отримуємо масив URL, запускаємо transcribe паралельно через TaskGroup, мержимо за порядком індексів.

Реалізація на Android (Kotlin)

suspend fun transcribe(file: File, language: String = "ru"): String {
    val client = OkHttpClient.Builder()
        .readTimeout(120, TimeUnit.SECONDS)
        .build()

    val requestBody = MultipartBody.Builder()
        .setType(MultipartBody.FORM)
        .addFormDataPart("file", file.name, file.asRequestBody("audio/mp4".toMediaType()))
        .addFormDataPart("model", "whisper-1")
        .addFormDataPart("language", language)
        .build()

    val request = Request.Builder()
        .url("https://api.openai.com/v1/audio/transcriptions")
        .header("Authorization", "Bearer $apiKey")
        .post(requestBody)
        .build()

    return withContext(Dispatchers.IO) {
        client.newCall(request).execute().use { response ->
            val json = response.body!!.string()
            JSONObject(json).getString("text")
        }
    }
}

Зверни увагу: readTimeout — 120 секунд мінімум. Whisper на довгому файлі відповідає повільно. Дефолтні 10 секунд OkHttp гарантійно дадуть SocketTimeoutException.

Параметри, які часто ігнорують

response_format: verbose_json — повертає не просто текст, а сегменти з start, end, text. Це потрібно для синхронізації аудіо з текстом, пошуку по часу, субтитрів.

prompt — до 224 токенів контексту, який підказує моделі стиль та спеціальні слова. Передавай термінологію предметної області: «ТЗ, MVP, бекелог, Jira» для IT-мітингів, «ЕКГ, АД, анамнез» для медицини. Це реально знижує кількість помилок у спеціальних термінах.

temperature: 0 — детерміністичний вивід. Для продакшену краще, ніж дефолт.

Типові помилки при інтеграції

Завантаження Data(contentsOf:) цілком в пам'ять перед відправкою — на 100 МБ файлі це OOM на бюджетному Android. Використовуй file.asRequestBody() в OkHttp або InputStream-based upload в iOS через URLSession.uploadTask(withStreamedRequest:).

Відсутність retry-логіки. Whisper API періодично повертає 503 Service Unavailable при навантаженні. Експоненціальний backoff з 3 спробами закриває 99% випадків.

Зберігання API-ключа в коді або BuildConfig. Ключ повинен йти через бекенд — мобільний клієнт не повинен мати прямого доступу до OpenAI API в продакшені.

Сроки та процес

Базова інтеграція Whisper API (запис → трансгрипція → вивід тексту) на одній платформі — 3–5 днів. Додавання чанкування, verbose_json з мітками часу, retry-логіки, фонової обробки через WorkManager/BackgroundTasks — ще 5–8 днів. Мультиязичність та UI синхронізації тексту з аудіо — окремий етап.