Recently Written · git

subread-overlay

Shows the lines of an .srt over any Android media player, in time with it. Tap a word to look it up.

git clone https://github.com/equwal/subread-overlay

Log | Files | Refs


README.md (5645 bytes)

1 # SubRead Overlay
2 
3 Shows the lines of a subtitle file (`.srt`) over the app that plays your
4 audiobook or video. The line follows the position of the player. Tap a word to
5 select it, and send it to a dictionary.
6 
7 It works with each Android player that publishes a media session: Voice, VLC,
8 mpv-android, YouTube, Smart AudioBook Player, podcast apps. No player needs a
9 change, and the app has no network permission.
10 
11 No `.srt` for your audiobook? [SubRead](https://subread.space) makes one from
12 the audiobook and its ebook, in the browser or on
13 [Android](https://github.com/equwal/subread-android).
14 
15 ## How to use it
16 
17 1. Allow "Show over other apps".
18 2. Allow notification access. Android gives the position of other apps only to
19    an app with this access. The app uses it for the media position, the speed,
20    and play or pause. It does not read, keep or send a notification: the
21    listener has no `onNotificationPosted`.
22 3. Choose the `.srt` file.
23 4. Choose the dictionary for "Look up", or let Android ask each time. Each app
24    that has an entry in the text selection menu is in the list (Takoboto,
25    Aedict, AnkiDroid, a translator).
26 5. Press "Show the subtitles", then start the player.
27 
28 Two settings change the panel: how much of the player shows through it, and
29 whether it shows the line before and the line after the line of now.
30 
31 On the panel:
32 
33 - `≡` moves the panel.
34 - A tap selects the word under the finger. A drag selects more words.
35   "Look up" pauses the player and sends the selection to the dictionary.
36   "Share" opens the share sheet of Android, for an app without an entry in
37   the text selection menu. "Copy" copies it.
38 - `▶` starts the player again after a lookup. `⏸` pauses it.
39 - `⋯` opens the timing row. `−0.5 s` and `+0.5 s` shift the subtitles.
40   `◀ line` and `line ▶` make the line before, or the next line, the line of
41   now. Use them when the media has an intro that the subtitle file does not
42   have.
43 - `✕` closes the panel.
44 
45 The line stays on the panel until the next line starts, also in a silence, so
46 that there is time to look a word up.
47 
48 ## How the timing works
49 
50 A media session reports a position, the time of that report, and the speed.
51 `PlayClock` computes the position of now from these three. `Follower` sleeps
52 until the next line starts and wakes on each report of the player (pause,
53 seek, speed). It does not poll.
54 
55 A player that reports no position: Android counts from zero when that player
56 starts to play, and the panel holds its line in a pause. Set the timing with
57 `◀ line` and `line ▶`.
58 
59 The app does not use an accessibility service, and will not. Players that
60 hide their position (Netflix, some DRM players) are not supported.
61 
62 A book in many audio files: the player reports the position in the current
63 file, and the subtitle file has one clock for the whole book. Shift the
64 subtitles with the timing row at the start of each file. One `.m4b` for one
65 book has no such problem.
66 
67 ## For reader apps
68 
69 A reader app has no notification access, so it cannot see the position of
70 the player. This app answers for it, with a content provider at
71 `content://space.subread.overlay.player/state`. A query returns one row with
72 the column `state`:
73 
74 ```
75 playing=1;position=96153;speed=1.0;package=de.ph1b.audiobook
76 ```
77 
78 The position is in milliseconds, for the moment of the query. A problem is
79 `error=no_notification_access` or `error=no_player`. `call` with the method
80 `play`, `pause` or `seek` (the argument is the position in milliseconds)
81 controls the player. The panel does not need to be on the screen.
82 
83 The [SubRead plugin for KOReader](https://github.com/equwal/subread.koplugin)
84 uses this to turn the pages with the audiobook.
85 
86 ## For caption apps
87 
88 An app that makes captions from live audio, for example speech recognition of
89 the sound of a video, can show its lines on the panel. The user then taps the
90 words and looks them up, the same as with a subtitle file. The app calls the
91 same content provider:
92 
93 ```kotlin
94 val panel = Uri.parse("content://space.subread.overlay.player")
95 contentResolver.call(panel, "line", "It was a dark", bundleOf("partial" to true))
96 contentResolver.call(panel, "line", "It was a dark night.", null)
97 contentResolver.call(panel, "end", null, null)
98 ```
99 
100 `line` shows the text. The extra `partial` is true while the sentence goes on:
101 the panel adds `…` to the line, and the next line replaces it. A final line
102 (no `partial`) stays as the line before, when the user shows three lines. `end`
103 gives the panel back to the subtitle file. Without `end`, live lines end ten
104 minutes after the last one.
105 
106 The answer is in the bundle key `live`: `ok`, or the reason the panel cannot
107 show the line. `no_notification_access` and `no_overlay_permission`: the user
108 must allow steps 1 and 2. `panel_hidden`: the user must press "Show the
109 subtitles", or closed the panel with `✕`. The panel does not come back on its
110 own for a line, so that a close stays a close.
111 
112 While a word is selected, the panel holds the line, so that the lookup has
113 time. The newest line comes when the selection goes.
114 
115 From a shell, for a test:
116 
117 ```
118 adb shell content call --uri content://space.subread.overlay.player --method line --arg "It was a dark" --extra partial:b:true
119 ```
120 
121 ## Build
122 
123 ```
124 ./gradlew :core:test :app:assembleDebug
125 ./gradlew :app:connectedDebugAndroidTest   # word selection and the follower, on a device
126 ```
127 
128 `:core` is plain Kotlin: the subtitle reader, the line for a position, the
129 clock. It has property tests. `:app` has the panel and the listener.
130 
131 `-PplayStore=true` leaves the Ko-fi link out of the build for Google Play.
132 
133 ## Licence
134 
135 AGPL-3.0. See `LICENSE`.