Flipnote Subtitle (.fsrt)
Subtitles timed to Flipnote pages, rather than seconds.
1
1 --> 12
Hello!
2
18 --> 30
Thanks for watching.
See you next time!
FSRT uses the same numbered blocks as a basic SRT file, with page ranges in
place of timestamps. Pages start at 1, and both ends are included:
12 --> 12 displays a subtitle for one page. A gap between ranges has no text.
Changing playback speed doesn't change which drawings have subtitles.
Save files as UTF-8 with the .fsrt extension. Each file is one language track.
Names such as my-flipnote.en.fsrt and my-flipnote.ar.fsrt help people tell
tracks apart, but language codes are not part of the file format.
An FSRT contains text and page ranges. It contains no animation, audio, account details or author signature, and it doesn't change the original Flipnote. It can be used with PPM or KWZ animations.
JavaScript
No dependencies are needed. Use Node.js 18 or later:
const fs = require('node:fs');
const {parse, stringify} = require('./fsrt.js');
const cues = parse(fs.readFileSync('examples/english.fsrt', 'utf8'));
fs.writeFileSync('copy.fsrt', stringify(cues));
For browser use, load fsrt.js with a script tag:
<script src="fsrt.js"></script>
<script>
const cues = FlipnoteSubtitle.parse('1\n1 --> 12\nHello!\n');
console.log(cues[0]); // {start: 1, end: 12, text: 'Hello!'}
</script>
Both functions accept optional {maxPage: 999, maxCues: 200} limits. Catch
FsrtError to show a readable message for an invalid file or range.
Python
Use Python 3.9 or later. Copy fsrt.py into your project:
from pathlib import Path
from fsrt import parse, stringify
cues = parse(Path('examples/english.fsrt').read_text(encoding='utf-8-sig'))
Path('copy.fsrt').write_text(stringify(cues), encoding='utf-8')
The optional keyword arguments are max_page and max_cues. Parser and
validation errors raise FsrtError, a subclass of ValueError.
Playing a track
Each parser returns objects or dictionaries with start, end and text
fields, sorted by starting page. If your player reports zero-based frame
indices, add 1 before comparing them with a cue's inclusive range:
const page = player.currentFrame + 1;
const cue = cues.find(cue => cue.start <= page && page <= cue.end);
caption.textContent = cue ? cue.text : '';
Render the text literally, including line breaks. Do not put it into
innerHTML: <b> and similar strings are subtitle text, not formatting.
Fonts, placement and language selection are the player's responsibility.
Readers reject overlapping cues, reversed or out-of-range pages, blank text and invalid control characters. The reference code accepts UTF-8 BOMs and common line endings, and limits files to 1 MiB and 10,000 cues by default. The specification defines the format; individual applications may apply stricter limits.
Using it on RexiMemo
See the RexiMemo instructions for importing and exporting tracks through Manage Flipnote. Those application limits do not restrict the FSRT format itself.
Tests
node --test tests/fsrt.test.js
python -m unittest discover -s tests
The two implementations use the same conformance cases. The examples include English, Japanese and Arabic tracks.
Licence
MIT. The format and reference code are maintained by RexiMemo.
