parse static method
Parses an LRC from a string. Throws a FormatExeption
if the inputted string is not valid.
Implementation
static Lrc parse(String parsed) {
parsed = parsed.trim();
if (!isValid(parsed)) {
throw FormatException('The inputted string is not a valid LRC file');
}
// split string into lines, code from Linesplitter().convert(data)
var lines = ((data) {
var lines = <String>[];
var end = data.length;
var sliceStart = 0;
var char = 0;
for (var i = 0; i < end; i++) {
var previousChar = char;
char = data.codeUnitAt(i);
if (char != 13) {
if (char != 10) continue;
if (previousChar == 13) {
sliceStart = i + 1;
continue;
}
}
lines.add(data.substring(sliceStart, i));
sliceStart = i + 1;
}
if (sliceStart < end) lines.add(data.substring(sliceStart, end));
return lines;
})(parsed);
// temporary storer variables
String? artist,
album,
title,
length,
author,
creator,
offset,
program,
version,
language;
LrcTypes? type;
var lyrics = <LrcLine>[];
String? setIfMatchTag(String toMatch, String tag) =>
(RegExp(r'^\[' + tag + r':.*\]$').hasMatch(toMatch))
? toMatch.substring(tag.length + 2, toMatch.length - 1).trim()
: null;
// loop thru each lines
for (var i in lines) {
artist = artist ?? setIfMatchTag(i, 'ar');
album = album ?? setIfMatchTag(i, 'al');
title = title ?? setIfMatchTag(i, 'ti');
author = author ?? setIfMatchTag(i, 'au');
length = length ?? setIfMatchTag(i, 'length');
creator = creator ?? setIfMatchTag(i, 'by');
offset = offset ?? setIfMatchTag(i, 'offset');
program = program ?? setIfMatchTag(i, 're');
version = version ?? setIfMatchTag(i, 've');
language = language ?? setIfMatchTag(i, 'la');
if (RegExp(r'^\[\d\d:\d\d\.\d\d\].*$').hasMatch(i)) {
var lyric = i.substring(10).trim();
var lineType = LrcTypes.simple;
Map<String, Object>? args;
// checkers for different types of LRCs
if (lyric.contains(RegExp(r'^\w:'))) {
//if extended
type = (type == LrcTypes.enhanced)
? LrcTypes.extended_enhanced
: LrcTypes.extended;
args = {
'letter': lyric[0], // get the letter of the type of person
'lyrics': lyric.substring(2) // get the rest of the lyrics
};
lineType = LrcTypes.extended;
} else if (lyric.contains(RegExp(r'<\d\d:\d\d\.\d\d>'))) {
// if enhanced
type = (type == LrcTypes.extended)
? LrcTypes.extended_enhanced
: LrcTypes.enhanced;
args = {};
lineType = LrcTypes.enhanced;
// for each timestamp in the line, regex has capturing
// groups to make this easier
for (var j in RegExp(r'<((\d\d):(\d\d)\.(\d\d))>([^<]+)')
.allMatches(lyric)) {
// puts each timestamp+lyrics in the args, no duplicates
args.putIfAbsent(
j.group(1)!, //the key is the <mm:ss.xx>
() => <String, Object>{
// the value is another map with the duration and lyrics
'duration': Duration(
minutes: int.parse(j.group(2)!),
seconds: int.parse(j.group(3)!),
milliseconds: int.parse(j.group(4)!) * 10,
),
'lyrics': j.group(5)!.trim()
},
);
}
}
final minutes = int.parse(i.substring(1, 3));
final seconds = int.parse(i.substring(4, 6));
final hundreds = int.parse(i.substring(7, 9));
lyrics.add(LrcLine(
timestamp: Duration(
minutes: minutes,
seconds: seconds,
milliseconds: hundreds * 10,
),
lyrics: lyric,
type: lineType,
args: args,
));
}
}
return Lrc(
type: type ?? LrcTypes.simple,
artist: artist,
album: album,
title: title,
author: author,
length: length,
creator: creator,
offset: (offset != null) ? int.tryParse(offset) : null,
program: program,
version: version,
lyrics: lyrics,
language: language,
);
}