docs/intent-api.md (3507 bytes)
1 # Make subtitles from another app 2 3 A reader or an audiobook player can ask SubRead to make the `.srt` for a book, 4 and get the file back. The other app needs no code of SubRead and no speech 5 model of its own. SubRead shows its usual screen with the progress, so the user 6 sees that a long job runs and can stop it. 7 8 ## Ask 9 10 ```kotlin 11 val ask = Intent("space.subread.app.action.ALIGN").apply { 12 setPackage("space.subread.app") 13 putExtra("space.subread.extra.AUDIO", audioUri) // Uri: m4b, m4a, mp3, opus, ogg, flac, wav 14 putExtra("space.subread.extra.BOOK", bookUri) // Uri: epub, txt, Aozora zip 15 putExtra("space.subread.extra.LANGUAGE", "ja") // optional: a Whisper language code, or "auto" (the default) 16 // SubRead must be able to read both files: 17 clipData = ClipData.newRawUri("audio", audioUri).apply { addItem(ClipData.Item(bookUri)) } 18 addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) 19 } 20 startActivityForResult(ask, REQUEST_SUBTITLES) // or an ActivityResultLauncher 21 ``` 22 23 Both Uris must be `content://` Uris that the calling app may grant (its own 24 `FileProvider`, or a document Uri for which it holds a persisted permission). 25 SubRead refuses every other Uri, a `file://` Uri included: SubRead would read 26 its own private files for the caller. 27 28 One audio file for one book. A book in many audio files is not handled yet. 29 30 Declare that the app looks for SubRead (Android 11 and later hide other apps 31 without this), in the manifest of the calling app: 32 33 ```xml 34 <queries> 35 <package android:name="space.subread.app" /> 36 </queries> 37 ``` 38 39 `ask.resolveActivity(packageManager) == null` means that SubRead is not 40 installed, or is older than 0.9.0. Offer the download then: 41 <https://github.com/equwal/subread-android/releases/latest> (also on F-Droid 42 and Google Play when those listings are up). 43 44 ## Answer 45 46 `RESULT_OK`: 47 48 | Where | What | 49 |---|---| 50 | `data` | a `content://` Uri of the `.srt` (UTF-8), with `FLAG_GRANT_READ_URI_PERMISSION`. Copy the file; the grant ends with the calling activity | 51 | `space.subread.extra.CUES` | `Int`: the number of subtitle lines | 52 | `space.subread.extra.MATCH_RATE` | `Double`, 0 to 1: the share of lines whose words were found in the book. Under 0.8 usually means another edition, a translation, or the wrong language | 53 | `space.subread.extra.LANGUAGE` | `String`: the language that was used | 54 55 `RESULT_CANCELED`: the user went back, or the job failed. 56 `space.subread.extra.ERROR` (`String`) says why when it failed. 57 58 SubRead makes one set of subtitles at a time. An ask that arrives while a job 59 runs is refused with `RESULT_CANCELED` and an `ERROR`; the job that runs is not 60 touched. An ask with a file that is missing or is not a `content://` Uri is 61 refused the same way, before any work starts. 62 63 The job takes about a third of the length of the audio on a mid-range device. 64 SubRead keeps what it has transcribed, so asking again for the same audio file 65 continues and does not start over. 66 67 When the subtitles are ready in less than 3 seconds (SubRead did this audiobook 68 before), SubRead does not close by itself. The screen says why the job was so 69 fast, and a button returns to the calling app with the same `RESULT_OK` answer. 70 A screen that opens and closes at once looks like a fault. 71 72 ## The subtitles 73 74 SubRip, UTF-8, one cue for each phrase the narrator says, times on the clock of 75 the audio file. The words are the book's own. A line that starts with `*` is 76 what the speech model heard where no text of the book could be matched.