Recently Written · git

subplz-web

git clone https://github.com/equwal/subplz-web

Log | Files | Refs


frontend/index.html (11690 bytes)

1 <!DOCTYPE html>
2 <html lang="en">
3 <head>
4 <meta charset="utf-8">
5 <meta name="viewport" content="width=device-width, initial-scale=1">
6 <title>SubRead — read along with your audiobook</title>
7 <meta name="description" content="Line an audiobook up with its ebook, sentence by sentence. Get an SRT for read-along, or a video with subtitles.">
8 <meta property="og:type" content="website">
9 <meta property="og:site_name" content="SubRead">
10 <meta property="og:title" content="SubRead — read along with your audiobook">
11 <meta property="og:description" content="Line an audiobook up with its ebook, sentence by sentence. Get an SRT for read-along, or a video with subtitles.">
12 <meta property="og:url" content="https://subread.space/">
13 <meta name="twitter:card" content="summary">
14 <meta name="theme-color" content="#4f46e5">
15 <link rel="stylesheet" href="/style.css?v=12">
16 <link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🎧</text></svg>">
17 </head>
18 <body>
19 <header class="topbar">
20   <div class="brand">
21     <span class="logo" aria-hidden="true">🎧</span>
22     <div>
23       <h1>SubRead</h1>
24       <p class="tagline">Line an audiobook up with its ebook, sentence by sentence.</p>
25     </div>
26   </div>
27   <div class="account" id="account">
28     <span id="quota" class="quota" hidden></span>
29     <span id="who" class="who" hidden></span>
30     <button type="button" id="buy-btn" class="ghost compact" hidden>Buy credits</button>
31     <button type="button" id="portal-btn" class="ghost compact" hidden>Manage plan</button>
32     <button type="button" id="signin-btn" class="ghost compact" hidden>Sign in</button>
33     <button type="button" id="signout-btn" class="ghost compact" hidden>Sign out</button>
34   </div>
35 </header>
36 
37 <main>
38   <section class="pitch">
39     <p>
40       Drop in an audiobook and the ebook it was read from. You get back subtitles
41       timed to the narration, with the wording taken from your own book — not
42       from a machine's guess at what it heard. It all happens in this tab:
43       <strong>your files are never uploaded</strong>.
44     </p>
45     <ul class="uses">
46       <li><strong>HoshiReader read-along</strong> — the .srt timing file, to match
47         against your epub. <span class="tag free">free</span></li>
48       <li><strong>Read-along book</strong> — your epub with the narration inside it
49         (EPUB 3 Media Overlays). Thorium, Storyteller and other EPUB 3 readers
50         highlight each line as it is read. <span class="tag free">free</span></li>
51       <li><strong>Local video</strong> — an .mkv with the subtitles built in, for
52         MPV or VLC. <span class="tag free">free</span></li>
53       <li><strong>YouTube video</strong> — a clean .mp4, plus the .srt to upload as
54         its captions. <span class="tag free">free</span></li>
55     </ul>
56     <p class="muted small" id="freetier">Free in your browser, without limit.</p>
57   </section>
58 
59   <!-- step 1: drop -->
60   <section id="dropzone" class="dropzone" tabindex="0" role="button"
61            aria-label="Drop an audio file and an ebook, or click to choose files">
62     <input type="file" id="filepicker" multiple hidden
63            accept=".jpg,.jpeg,.png,.webp,.bmp,.m4b,.m4a,.mp3,.opus,.ogg,.oga,.flac,.wav,.aac,.wma,.mka,.mkv,.mp4,.webm,.avi,.mov,.epub,.txt,.srt,.vtt,.ass,.fb2,.zip,.mobi,.azw,.azw3,.prc">
64     <div class="dz-inner">
65       <div class="dz-icon" aria-hidden="true">
66         <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"
67              stroke-linecap="round" stroke-linejoin="round">
68           <path d="M12 16V4m0 0L7.5 8.5M12 4l4.5 4.5"/>
69           <path d="M4 15v3a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-3"/>
70         </svg>
71       </div>
72       <p class="dz-title" id="dz-title">Drop your audiobook <em>and</em> ebook here</p>
73       <p class="dz-sub" id="dz-sub">One at a time or both at once — order doesn't matter.
74         A folder of per-chapter files works too.
75         Or <button type="button" id="browse" class="linkbtn">browse</button>.</p>
76       <p class="dz-formats">Audio: m4b · mp3 · m4a · opus · flac · wav · mkv
77         &nbsp;|&nbsp; Book: epub · fb2 · mobi · azw3 · txt</p>
78       <p class="dz-formats" id="cover-hint">Optional: drop a jpg or png to use as the
79         video background — <strong>sign in required</strong></p>
80     </div>
81   </section>
82 
83   <!-- what has been dropped so far -->
84   <section id="staged" class="panel staged" hidden>
85     <div class="slot" id="slot-audio">
86       <span class="slot-tick" aria-hidden="true"></span>
87       <div class="slot-body">
88         <div class="slot-label">Audiobook</div>
89         <div class="slot-value">Waiting for an audio file</div>
90       </div>
91       <button type="button" class="slot-x" data-slot="audio" title="Remove">×</button>
92     </div>
93     <div class="slot" id="slot-cover" hidden>
94       <span class="slot-tick" aria-hidden="true"></span>
95       <div class="slot-body">
96         <div class="slot-label">Cover image</div>
97         <div class="slot-value">Waiting for an image</div>
98       </div>
99       <button type="button" class="slot-x" data-slot="cover" title="Remove">×</button>
100     </div>
101     <div class="slot" id="slot-text">
102       <span class="slot-tick" aria-hidden="true"></span>
103       <div class="slot-body">
104         <div class="slot-label">Book</div>
105         <div class="slot-value">Waiting for an ebook</div>
106       </div>
107       <button type="button" class="slot-x" data-slot="text" title="Remove">×</button>
108     </div>
109   </section>
110 
111   <div id="uploading" class="panel uploading" hidden>
112     <div class="spinner" aria-hidden="true"></div>
113     <div class="up-body">
114       <p class="up-title">Getting ready…</p>
115       <div class="bar"><div id="upbar" class="bar-fill"></div></div>
116       <p id="uptext" class="muted small">0%</p>
117     </div>
118   </div>
119 
120   <div id="error" class="panel error" hidden role="alert"></div>
121 
122   <!-- step 2: confirm -->
123   <section id="confirm" class="panel confirm" hidden>
124     <h2>Ready to align</h2>
125     <dl class="pairing">
126       <div><dt>Audiobook</dt><dd id="c-audio"></dd></div>
127       <div><dt>Book</dt><dd id="c-text"></dd></div>
128     </dl>
129 
130     <div class="langrow">
131       <label for="language">Language</label>
132       <select id="language"></select>
133       <span id="detected" class="detected"></span>
134     </div>
135     <p id="splitnote" class="muted small"></p>
136 
137     <div id="match" class="match" hidden>
138       <div class="match-head">
139         <span id="match-badge" class="match-badge"></span>
140         <span id="match-summary"></span>
141       </div>
142       <ul id="match-warnings" class="match-warnings"></ul>
143     </div>
144 
145     <div class="mode">
146       <label class="switch">
147         <input type="checkbox" id="in-browser" checked>
148         <span>Convert in this browser (JavaScript). Off: our server does it.</span>
149       </label>
150     </div>
151 
152     <div class="actions">
153       <button id="start" class="primary">Start alignment</button>
154       <button id="discard" class="ghost">Discard</button>
155     </div>
156     <p class="muted small" id="eta"></p>
157   </section>
158 
159   <!-- step 3: the job, running in this tab -->
160   <section id="working" class="panel working" hidden>
161     <p class="up-title" id="work-title">Starting</p>
162     <div class="bar"><div id="work-bar" class="bar-fill"></div></div>
163     <p id="work-meta" class="muted small"></p>
164     <div class="actions">
165       <button id="stop" class="ghost">Stop (progress is kept)</button>
166     </div>
167     <div id="results" hidden>
168       <div id="result-files" class="job-files"></div>
169       <p class="muted small">Videos are made here when you ask for them, in seconds.
170         They are not kept anywhere: make them before closing this tab.</p>
171       <p class="muted small" id="chip-in" hidden>SubRead is free, and it has no ads.
172         <a href="https://ko-fi.com/truex" target="_blank" rel="noopener">Chip in for the server on Ko-fi</a>, if it helped.</p>
173       <div class="actions"><button id="another" class="ghost">Convert another book</button></div>
174     </div>
175   </section>
176 
177   <!-- step 4: history -->
178   <section id="jobs-section" hidden>
179     <h2 class="section-title">Your conversions</h2>
180     <ul id="jobs" class="jobs"></ul>
181   </section>
182 </main>
183 
184 <footer>
185   <p class="muted small">
186     Alignment method from <a href="https://github.com/kanjieater/SubPlz" target="_blank" rel="noopener">SubPlz</a>;
187     speech model <a href="https://github.com/openai/whisper" target="_blank" rel="noopener">Whisper</a>.
188     Every line of text comes from the book you upload, so names and spelling stay right.
189   </p>
190   <p class="muted small">
191     <a href="/terms.html">Terms, privacy and refunds</a> ·
192     <a href="https://github.com/equwal/subplz-web" target="_blank" rel="noopener">Source code</a> ·
193     <a href="https://github.com/equwal/subread-android/releases/latest" target="_blank" rel="noopener">Android app</a> ·
194     <a href="https://ko-fi.com/truex" target="_blank" rel="noopener">Support on Ko-fi</a>
195   </p>
196 </footer>
197 
198 <dialog id="signin-dialog" class="modal">
199   <form method="dialog"><button class="modal-x" aria-label="Close">×</button></form>
200   <h2>Sign in</h2>
201   <p class="muted">We email you a one-time link. There is no password.</p>
202   <form id="signin-form" class="signin-form">
203     <input type="email" id="signin-email" required autocomplete="email"
204            placeholder="you@example.com" aria-label="Email address">
205     <button class="primary" id="signin-send">Email me a link</button>
206   </form>
207   <p id="signin-note" class="muted small" hidden></p>
208 </dialog>
209 
210 <dialog id="share-dialog" class="modal">
211   <form method="dialog"><button class="modal-x" aria-label="Close">×</button></form>
212   <h2>Share on honjimaku</h2>
213   <p class="muted"><a href="https://honjimaku.com" target="_blank" rel="noopener">honjimaku.com</a> is a free
214     library of subtitles for Japanese audiobooks. Other learners can then read along with this book
215     and do not have to make the subtitles again.</p>
216   <form id="share-form" class="share-form" autocomplete="off">
217     <label for="share-title">Title of the book, as the audiobook shop writes it</label>
218     <input id="share-title" required maxlength="200">
219     <label for="share-book-id">Audiobook ID, if there is one (Audible ASIN, audiobook.jp number)</label>
220     <input id="share-book-id" maxlength="40" pattern="[A-Za-z0-9 ._\-]*" placeholder="B0BPXSSWVF">
221     <label for="share-key">Your honjimaku API key
222       (<a href="https://honjimaku.com/account" target="_blank" rel="noopener">make an account, then "Generate API key"</a>)</label>
223     <input id="share-key" type="password" required autocomplete="off">
224     <p class="muted small">The key stays in this browser. The subtitles go from this tab to honjimaku.com and to
225       nowhere else. They hold the words of the book: share them only if you may.</p>
226     <button class="primary" id="share-send">Share the subtitles</button>
227   </form>
228   <p id="share-status" class="muted small" hidden></p>
229 </dialog>
230 
231 <dialog id="pricing-dialog" class="modal wide">
232   <form method="dialog"><button class="modal-x" aria-label="Close">×</button></form>
233   <h2>Credits for our server</h2>
234   <p id="pricing-why" class="muted"></p>
235   <div id="plans" class="plans"></div>
236   <p class="muted small" id="pricing-foot">
237     Paid through Stripe; we never see your card. One credit converts one book
238     on our server, with every output. Credits never expire, and a job that
239     fails gives its credit back. In this tab a conversion is always free. Paying also creates your account,
240     under the email you give Stripe.
241   </p>
242 </dialog>
243 
244 <div id="toast" class="toast" hidden role="status"></div>
245 
246 <script type="module" src="/app.js?v=12"></script>
247 </body>
248 </html>