parse static method

Lrc parse(
  1. String parsed
)

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,
  );
}