feat(lyrics): show songwriter and provider credits after lyrics

This commit is contained in:
zarzet committed 2026-09-25 12:01:43 +07:00
1 parent abb8dc4a42
commit 8103c313d7
6 files changed
+238 -11

No files matched your search

+59
View File
@@ -7,6 +7,65 @@ String _tag(String kind, int time, String text) =>
'[x-$kind:$time:${base64.encode(utf8.encode(text))}]';
void main() {
test(
'writer tags and stored provider attribution remain separate from lyric rows',
() {
final lyrics = LyricsParser.parse('''
[au:Example Writer, Another Writer]
[by:SpotiFLAC-Mobile via Example Lyrics API (source: upstream)]
[00:01.00]First line
[00:04.00]Last line
''');
expect(lyrics.writers, 'Example Writer, Another Writer');
expect(lyrics.provider, 'Example Lyrics');
expect(lyrics.lines, hasLength(2));
expect(lyrics.lines.last.time, const Duration(seconds: 4));
},
);
test(
'an LRC uploader or performing artist is not invented as the songwriter or provider',
() {
final lyrics = LyricsParser.parse(
'[ar:Performer]\n[by:Uploader]\n[00:01.00]Line',
);
expect(lyrics.writers, isNull);
expect(lyrics.provider, isNull);
expect(
LyricsParser.parse(
'[by:SpotiFLAC-Mobile (source: extension:example.lyrics)]\n[00:01]Line',
).provider,
'example.lyrics',
);
},
);
test(
'explicit ending credits become a footer rather than a seekable lyric',
() {
final lyrics = LyricsParser.parse(
'[00:01]Last lyric\n[00:04]Written By: Example Writer',
);
expect(lyrics.lines.single.text, 'Last lyric');
expect(lyrics.writers, 'Example Writer');
expect(lyrics.plainText, 'Last lyric');
},
);
test(
'TTML songwriter metadata is retained without treating performers as writers',
() {
final lyrics = LyricsParser.parse('''
<tt xmlns="http://www.w3.org/ns/ttml" xmlns:meta="urn:example:metadata">
<head><metadata><meta:songwriters><meta:songwriter>Example Writer</meta:songwriter>
<meta:songwriter>Second Writer</meta:songwriter></meta:songwriters></metadata></head>
<body><div><p begin="00:01.00">Lyric</p></div></body></tt>
''');
expect(lyrics.writers, 'Example Writer, Second Writer');
expect(lyrics.lines.single.text, 'Lyric');
},
);
test('retains all three texts, word ends and millisecond timing', () {
final lyrics = LyricsParser.parse('''
${_tag('romaji', 1009, 'Romanized')}
@@ -1641,6 +1641,38 @@ void main() {
},
);
testWidgets(
'lyrics end with writer and provider credits and clear them on track change',
(tester) async {
metadataOverrides['composer'] = 'Example Composer';
metadataOverrides['lyrics'] =
'[by:SpotiFLAC-Mobile via Example Lyrics API (source: upstream)]\n[00:01]Last lyric';
await pumpNowPlaying(
tester,
theme: MornyeTheme.build(Brightness.dark),
size: const Size(390, 844),
);
mediaItems.add(item('first'));
await tester.pumpAndSettle();
await tester.tap(find.byIcon(CupertinoIcons.quote_bubble));
await tester.pumpAndSettle();
final credit = find.text(
'Written by: Example Composer\nLyrics: Example Lyrics',
);
expect(credit, findsOneWidget);
expect(
tester.getTopLeft(credit).dy,
greaterThan(tester.getBottomLeft(find.text('Last lyric')).dy),
);
metadataOverrides.clear();
mediaItems.add(item('second'));
await tester.pumpAndSettle();
expect(credit, findsNothing);
expect(find.text('Second lyric'), findsOneWidget);
expect(tester.takeException(), isNull);
},
);
for (final supplement in ['none', 'pronunciation', 'translation']) {
testWidgets('language menu only offers available $supplement', (
tester,