import 'package:halkyon_learn_flutter/src/feature/card/model/card_content.dart'; import 'package:halkyon_learn_flutter/src/feature/dictionary/model/dictionary.dart'; import 'package:halkyon_learn_flutter/src/feature/jellyfin/model/jellyfin.dart'; import 'fake_dictionary_gateway.dart'; import 'fake_jellyfin.dart'; /// The show of the sample library. const sampleShow = JellyfinItem( id: 'show-1', name: 'Sample Journey', kind: JellyfinItemKind.series, year: 2026, ); /// Its first episode. const sampleEpisode = JellyfinItem( id: 'episode-1', name: 'The Station', kind: JellyfinItemKind.episode, seriesName: 'Sample Journey', seasonNumber: 1, episodeNumber: 1, runTime: Duration(minutes: 24), ); /// The episode's playback: a Japanese track and an English one. const sampleEpisodePlayback = JellyfinPlayback( itemId: 'episode-1', mediaSourceId: 'episode-1', playSessionId: 'session-1', runTime: Duration(minutes: 24), subtitles: [ JellyfinSubtitleTrack( index: 2, title: '日本語 - Japanese', language: 'jpn', isDefault: true, ), JellyfinSubtitleTrack(index: 3, title: 'English', language: 'eng'), ], ); /// The episode's Japanese subtitles, as Jellyfin converts them. const sampleJapanese = ''' WEBVTT 00:00:01.000 --> 00:00:05.000 昨日、駅で友達に会いました。 00:00:06.000 --> 00:00:10.000 まだ読んでいないけど、面白そうだね。 00:00:11.000 --> 00:00:15.000 一緒に旅を続けよう。 00:00:16.000 --> 00:00:20.000 駅で友達を待っている。 '''; /// Its English subtitles. const sampleEnglish = ''' WEBVTT 00:00:01.000 --> 00:00:05.000 Yesterday I met a friend at the station. 00:00:06.000 --> 00:00:10.000 Not yet, but it looks interesting. 00:00:11.000 --> 00:00:15.000 Let's continue the journey together. 00:00:16.000 --> 00:00:20.000 I'm waiting for a friend at the station. '''; DictionaryWord _word( String id, String headword, { String? reading, required List glosses, List pos = const ['n'], List misc = const [], List labels = const ['common'], List examples = const [], }) => DictionaryWord( ref: (source: 'jmdict', id: id), headword: headword, reading: reading, labels: labels, senses: [VocabularySense(glosses: glosses, pos: pos, misc: misc)], examples: examples, ); /// The JMdict entries of the sample's words, as the instance gives them. final sampleWords = [ _word( '1', '昨日', reading: 'きのう', glosses: ['yesterday'], labels: ['common', 'jlpt:n5'], ), _word( '2', '駅', reading: 'えき', glosses: ['station'], labels: ['common', 'jlpt:n5'], ), _word('3', 'で', glosses: ['at', 'in'], pos: ['prt']), _word( '4', '友達', reading: 'ともだち', glosses: ['friend'], labels: ['common', 'jlpt:n5'], ), _word('5', 'に', glosses: ['to', 'at'], pos: ['prt']), _word( '6', '会う', reading: 'あう', glosses: ['to meet'], pos: ['v5u', 'vi'], labels: ['common', 'jlpt:n5'], // As Tatoeba's checked sentences come through JMdict. examples: [ ( form: '会いましょう', sentence: 'また会いましょう。', translation: "Let's meet again.", ), ], ), _word( '7', '未だ', reading: 'まだ', glosses: ['still', 'not yet'], pos: ['adv'], misc: ['uk'], labels: ['common', 'jlpt:n5'], ), _word( '8', '読む', reading: 'よむ', glosses: ['to read'], pos: ['v5m', 'vt'], labels: ['common', 'jlpt:n5'], ), _word('9', 'けど', glosses: ['but', 'however'], pos: ['conj']), _word( '10', '面白い', reading: 'おもしろい', glosses: ['interesting', 'amusing'], pos: ['adj-i'], labels: ['common', 'jlpt:n5'], ), _word('11', 'だ', glosses: ['be', 'is'], pos: ['cop']), _word('12', 'ね', glosses: ["isn't it?"], pos: ['prt']), _word( '13', '一緒に', reading: 'いっしょに', glosses: ['together'], pos: ['adv'], labels: ['common', 'jlpt:n4'], ), _word( '14', '旅', reading: 'たび', glosses: ['travel', 'trip', 'journey'], labels: ['common', 'jlpt:n3'], ), _word('15', 'を', glosses: ['indicates direct object'], pos: ['prt']), _word( '16', '続ける', reading: 'つづける', glosses: ['to continue'], pos: ['v1', 'vt'], labels: ['common', 'jlpt:n4'], ), _word( '17', '待つ', reading: 'まつ', glosses: ['to wait'], pos: ['v5t', 'vt'], labels: ['common', 'jlpt:n5'], ), ]; /// An instance with JMdict holding the sample's words. FakeDictionaryGateway sampleDictionary() => FakeDictionaryGateway(words: sampleWords); /// The sample library: Anime, holding the show, its season and episode. FakeJellyfinGateway sampleJellyfin({ JellyfinPlayback playback = sampleEpisodePlayback, Map<(String, int), String> vtt = const { ('episode-1', 2): sampleJapanese, ('episode-1', 3): sampleEnglish, }, }) => FakeJellyfinGateway( tree: { '': [ const JellyfinItem( id: 'library-1', name: 'Anime', kind: JellyfinItemKind.library, ), ], 'library-1': [sampleShow], 'show-1': [ const JellyfinItem( id: 'season-1', name: 'Season 1', kind: JellyfinItemKind.season, seriesName: 'Sample Journey', seasonNumber: 1, ), ], 'season-1': [sampleEpisode], }, playbacks: {'episode-1': playback}, vtt: vtt, ); /// The device signed in as `mika` on the sample server. MemoryJellyfinStore signedInJellyfin() => MemoryJellyfinStore( kept: JellyfinState( deviceId: 'device-1', account: JellyfinAccount( server: fakeJellyfinServer, userId: 'user-1', userName: 'mika', token: 'token-1', ), ), ); /// A sample song: its album, by Sample Artist, holds it with lyrics timed /// line by line, and an instrumental without. const sampleSong = JellyfinItem( id: 'song-1', name: 'Sample Song', kind: JellyfinItemKind.audio, artist: 'Sample Artist', hasLyrics: true, hasImage: true, runTime: Duration(minutes: 3), ); /// A music library of one album: [sampleSong] and an instrumental. FakeJellyfinGateway sampleMusic() => FakeJellyfinGateway( tree: { '': [ const JellyfinItem( id: 'music-1', name: 'Music', kind: JellyfinItemKind.library, ), ], 'music-1': [ const JellyfinItem( id: 'album-1', name: 'Sample Album', kind: JellyfinItemKind.album, artist: 'Sample Artist', ), ], 'album-1': [ sampleSong, const JellyfinItem( id: 'song-2', name: 'Sample Song (Instrumental)', kind: JellyfinItemKind.audio, artist: 'Sample Artist', runTime: Duration(minutes: 3), ), ], }, playbacks: { 'song-1': const JellyfinPlayback( itemId: 'song-1', mediaSourceId: 'song-1', playSessionId: 'session-2', subtitles: [], runTime: Duration(minutes: 3), audioOnly: true, ), }, lyricsOf: { 'song-1': const [ (text: '昨日、駅で友達に会いました。', start: Duration(seconds: 1)), (text: '', start: Duration(seconds: 5)), (text: 'まだ読んでいないけど、面白そうだね。', start: Duration(seconds: 6)), ], }, );