diff --git a/.gitignore b/.gitignore index 65c34dc..6cadb61 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,6 @@ build/ # Omit committing pubspec.lock for library packages; see # https://dart.dev/guides/libraries/private-files#pubspeclock. pubspec.lock + + +test/files/extra \ No newline at end of file diff --git a/lib/lrc.dart b/lib/lrc.dart index 0ff3b73..b2e1450 100644 --- a/lib/lrc.dart +++ b/lib/lrc.dart @@ -1,4 +1,20 @@ /// The main library for support for LRCs. library lrc; -export 'src/lrc_main.dart'; +import 'dart:convert'; +import 'dart:io'; + +import 'package:charset/charset.dart'; +import 'package:html_unescape/html_unescape.dart'; +import 'package:kana_kit/kana_kit.dart'; +import 'package:xml/xml.dart'; + +part 'src/core/enums.dart'; +part 'src/core/extensions.dart'; +part 'src/models/lrc.dart'; +part 'src/models/lrc_line.dart'; +part 'src/models/lrc_line_part.dart'; +part 'src/models/lrc_stream.dart'; +part 'src/parsers/lrc_parser.dart'; +part 'src/parsers/multi_timestamp_parser.dart'; +part 'src/parsers/ttml_parser.dart'; diff --git a/lib/src/core/enums.dart b/lib/src/core/enums.dart new file mode 100644 index 0000000..c663abf --- /dev/null +++ b/lib/src/core/enums.dart @@ -0,0 +1,16 @@ +part of lrc; + +///The types of LRC +enum LrcTypes { + ///A simple LRC, with no extra formatting, etc + simple, + + ///LRC with modifiers at the start in the form `A: foo` + extended, + + ///LRC with additional timestamps per line in the form `<00:00.00> foo` + enhanced, + + ///LRC that some lines are extended and some are enhanced + extended_enhanced +} diff --git a/lib/src/core/extensions.dart b/lib/src/core/extensions.dart new file mode 100644 index 0000000..ee2a6f7 --- /dev/null +++ b/lib/src/core/extensions.dart @@ -0,0 +1,235 @@ +part of lrc; + +/// Handy extensions on lists of LrcLine +extension LrcLineExtensions on List { + /// Creates a stream for each lyric using their durations + Stream toStream() async* { + for (var i = 0; i < length; i++) { + var lineCurrent = this[i]; + var lineNext = (i + 1 < length) ? this[i + 1] : null; + var durationToNext = (lineNext != null) + ? Duration( + milliseconds: lineNext.timestamp.inMilliseconds - + lineCurrent.timestamp.inMilliseconds) + : null; + yield LrcStream( + duration: durationToNext, + previous: (i != 0) ? this[i - 1] : null, + current: lineCurrent, + next: lineNext, + position: i, + length: length - 1); + if (durationToNext != null) { + await Future.delayed(durationToNext); + } + } + } +} + +extension LrcExtensions on Lrc { + ({ + List uiLyricsLines, + Map> highlightTimestampsMap, + }) forUiDisplay( + double multiplier, { + Duration durationDifferenceToInsertEmptyLine = const Duration(seconds: 1), + bool romanize = false, + }) { + final originalLyrics = lyrics; + final offset = this.offset ?? 0; + final uiLyricsLines = []; + final highlightTimestampsMap = + >{}; // timestamp: [index] + var indexExtra = 0; + for (var index = 0; index < originalLyrics.length; index++) { + final ogItem = originalLyrics[index]; + + final lineTimeStamp = ogItem.timestamp - Duration(milliseconds: offset); + final calculatedForSpedUpVersions = + multiplier == 0 ? lineTimeStamp : (lineTimeStamp * multiplier); + + final newLrcLine = ogItem.withTimeStamp( + newTimestamp: calculatedForSpedUpVersions, + parts: _mergeParts(ogItem.parts), + ); + final indicesList = + highlightTimestampsMap[calculatedForSpedUpVersions] ??= []; + indicesList.add(index + indexExtra); + uiLyricsLines.add(newLrcLine); + final newParts = newLrcLine.parts; + if (newParts != null) { + try { + final nextLine = index == originalLyrics.length - 1 + ? null + : originalLyrics[index + 1]; + if (nextLine != null) { + final partEndTimestamp = newParts.last.endTimestamp; + if ((nextLine.timestamp - partEndTimestamp) > + durationDifferenceToInsertEmptyLine) { + // -- insert empty line to allow dynamic lrc view to hide lrc during long transitions + indexExtra++; + final emptyLineIndex = index + indexExtra; + + uiLyricsLines.add( + LrcLine( + timestamp: partEndTimestamp, + originalIndex: emptyLineIndex + 0.1, + lyrics: '', + readableText: '', + type: LrcTypes.simple, + parts: const [], + person: null, + ), + ); + final indicesList = + highlightTimestampsMap[partEndTimestamp] ??= []; + indicesList.add(emptyLineIndex); + } + } + } catch (_) {} + } + if (romanize) { + final romanized = + _romanized(newLrcLine, index, calculatedForSpedUpVersions); + if (romanized != null) { + uiLyricsLines.add(romanized); + } + } + } + return ( + uiLyricsLines: uiLyricsLines, + highlightTimestampsMap: highlightTimestampsMap, + ); + } +} + +List? _mergeParts( + List? parts, { + int minDurationMs = 250, +}) { + if (parts == null || parts.isEmpty) return parts; + + final merged = []; + var current = parts[0]; + + for (var i = 1; i < parts.length; i++) { + final next = parts[i]; + final duration = current.endTimestamp.inMilliseconds - + current.startTimestamp.inMilliseconds; + + if (duration < minDurationMs) { + current = LrcLinePart( + startTimestamp: current.startTimestamp, + endTimestamp: next.endTimestamp, + lyrics: current.lyrics + next.lyrics, + ); + } else { + merged.add(current); + current = next; + } + } + merged.add(current); + return merged; +} + +const _kanaKit = KanaKit( + config: KanaKitConfig( + passRomaji: true, + passKanji: false, + upcaseKatakana: true, + ), +); + +String? _toRomajiOrNull(String txt) { + if (txt.isNotEmpty) { + return _kanaKit.toRomaji(txt); + } + return null; +} + +String _toRomajiOrOriginal(String txt) { + return _toRomajiOrNull(txt) ?? txt; +} + +LrcLine? _romanized(LrcLine line, int index, Duration lineTimestamp) { + LrcLine? romanizedLine; + final parts = line.parts; + if (parts != null && parts.isNotEmpty) { + var hasRomajiPart = false; + final romanizedParts = []; + for (final p in parts) { + final romanizedPart = _toRomajiOrNull(p.lyrics); + if (romanizedPart != null && romanizedPart != p.lyrics) { + hasRomajiPart = true; + } + // -- always add to retain order + romanizedParts.add( + LrcLinePart( + startTimestamp: p.startTimestamp, + endTimestamp: p.endTimestamp, + lyrics: romanizedPart ?? p.lyrics, + ), + ); + } + if (hasRomajiPart) { + romanizedLine = LrcLine( + timestamp: lineTimestamp, + originalIndex: index - 0.1, + lyrics: _toRomajiOrOriginal(line.lyrics), + readableText: romanizedParts.map((e) => e.lyrics).join(), + type: line.type, + parts: romanizedParts, + person: line.person, + ); + } + } else { + final txt = line.lyrics; + final romanized = _toRomajiOrNull(txt); + if (romanized != null && romanized != txt) { + romanizedLine = LrcLine( + timestamp: lineTimestamp, + originalIndex: index - 0.1, + lyrics: romanized, + readableText: romanized, + type: line.type, + parts: parts, + person: line.person, + ); + } + } + + return romanizedLine; +} + +/// Handy extensions on strings +extension StringExtensions on String { + /// Handy extension method that parses the string to an [Lrc] + Lrc toLrc() => LrcParser.parse(this); + + /// Handy extension getter if the given string is a valid LRC + bool get isValidLrc => LrcParser.isValid(this); +} + +extension LrcFileExtensions on File { + String readLrcStringSync() { + try { + return readAsStringSync(encoding: utf8); + } on FileSystemException catch (_) { + try { + return readAsStringSync(encoding: utf16); + } catch (_) {} + } + return ''; + } + + Future readLrcString() async { + try { + return await readAsString(encoding: utf8); + } on FileSystemException catch (_) { + try { + return await readAsString(encoding: utf16); + } catch (_) {} + } + return ''; + } +} diff --git a/lib/src/lrc_main.dart b/lib/src/lrc_main.dart deleted file mode 100644 index 2d864a3..0000000 --- a/lib/src/lrc_main.dart +++ /dev/null @@ -1,401 +0,0 @@ -/// The parsed LRC class. -/// -/// You can instantiate this class directly -/// or parse a string using `Lrc.parse()`. -class Lrc { - /// The overall type of LRC for this object - LrcTypes type; - - /// The name of the artist of the song (optional) - /// - /// This corresponds to the ID tag `[ar:]`. - String? artist; - - /// The name of the album of the song (optional) - /// - /// This corresponds to the ID tag `[al:]`. - String? album; - - /// The title of the song (optional) - /// - /// This corresponds to the ID tag `[ti:]`. - String? title; - - /// The name of the author of the lyrics (optional) - /// - /// This corresponds to the ID tag `[au:]`. - String? author; - - /// The name of the creator of the LRC file (optional) - /// - /// This corresponds to the ID tag `[by:]`. - String? creator; - - /// The name of the program that created the LRC file (optional) - /// - /// This corresponds to the ID tag `[re:]`. - String? program; - - /// The version of the program that created the LRC file (optional) - /// - /// This corresponds to the ID tag `[ve:]`. - String? version; - - /// The length of the song (optional) - /// - /// This corresponds to the ID tag `[length:]`. - String? length; - - /// The language of the song, using an IETF BCP 47 language tag (optional) - /// - /// This corresponds to the ID tag `[la:]`. - String? language; - - /// Offset of time in milliseconds, can be positive [shifts time up] - /// or negative [shifts time down] (optional) - /// - /// This corresponds to the ID tag `[offset:]`. - int? offset; - - /// The list of lyric lines - List lyrics; - - /// Handy parameter to get a stream of the lyrics. - /// See `List.toStream()`. - Stream get stream => lyrics.toStream(); - - /// Use this constructor if you want to manually create an LRC from scratch. - /// Otherwise, parse an LRC string using [Lrc.parse]. - Lrc({ - this.type = LrcTypes.simple, - required this.lyrics, - this.artist, - this.album, - this.title, - this.creator, - this.author, - this.program, - this.version, - this.length, - this.offset, - this.language, - }); - - /// Format the lrc to a readable string that can then be - /// outputted to an LRC file. - String format() { - var output = ''; - - output += (artist != null) ? '[ar:$artist]\n' : ''; - output += (album != null) ? '[al:$album]\n' : ''; - output += (title != null) ? '[ti:$title]\n' : ''; - output += (length != null) ? '[length:$length]\n' : ''; - output += (creator != null) ? '[by:$creator]\n' : ''; - output += (author != null) ? '[au:$author]\n' : ''; - output += (offset != null) ? '[offset:${offset.toString()}]\n' : ''; - output += (program != null) ? '[re:$program]\n' : ''; - output += (version != null) ? '[ve:$version]\n' : ''; - output += (language != null) ? '[la:$language]\n' : ''; - - for (var lyric in lyrics) { - output += lyric.formattedLine + '\n'; - } - - return output; - } - - /// Parses an LRC from a string. Throws a `FormatExeption` - /// if the inputted string is not valid. - 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 = []; - 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 = []; - - 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? 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 - () => { - // 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, - ); - } - - /// Checks if the string [input] is a valid LRC using Regex. - static bool isValid(String input) => RegExp( - r'^([\r\n]*\[((ti)|(a[rlu])|(by)|([rv]e)|(length)|(offset)|(la)):.+\][\r\n]*)*([\r\n]*\[\d\d:\d\d\.\d\d\].*){2,}[\r\n]*$') - .hasMatch(input.trim()); - - @override - String toString() { - var lyrics = this.lyrics.join('\n'); - - return ''' - Type: '$type' - Artist: '$artist' - Album: '$album' - Title: '$title' - Author: '$author' - Creator: '$creator' - Program: '$program' - Length: '$length' - Language: '$language' - Offset: '$offset' - Lyrics: '$lyrics' - '''; - } -} - -///The types of LRC -enum LrcTypes { - ///A simple LRC, with no extra formatting, etc - simple, - - ///LRC with modifiers at the start in the form `A: foo` - extended, - - ///LRC with additional timestamps per line in the form `<00:00.00> foo` - enhanced, - - ///LRC that some lines are extended and some are enhanced - extended_enhanced -} - -///A line of lyrics, with its defined duration and raw lyrics -class LrcLine { - ///timestamp for the lyrics wherein it'll be displayed - Duration timestamp; - - ///the raw lyrics for the line - String lyrics; - - ///the additional arguments for other lrc types - Map? args; - - ///the type of lrc for this line - LrcTypes type; - - LrcLine({ - required this.timestamp, - required this.lyrics, - required this.type, - this.args, - }); - - ///get the string for a formatted line - String get formattedLine { - ///function to add leading zeros - String f(int x) => x.toString().padLeft(2, '0'); - - // LRC format doesn't accept hours. - final minutes = timestamp.inMinutes % 60, - seconds = timestamp.inSeconds % 60, - hundreds = timestamp.inMilliseconds % 1000 ~/ 10; - - return '[${f(minutes)}:${f(seconds)}.${f(hundreds)}]$lyrics'; - } - - @override - String toString() { - return ''' - Timestamp: '$timestamp' - Lyrics: '$lyrics' - Args: '$args' - '''; - } -} - -/// A data class to store each yielding of the stream -class LrcStream { - /// The previous line. Is null if the current line is the fist position. - LrcLine? previous; - - /// Tthe current line - LrcLine current; - - /// The next line. Is null if the current line is the last position. - LrcLine? next; - - /// The duration from the current to the next. Is null if the current line is the last position. - Duration? duration; - - /// The position of the current line - int position; - - /// The total number of lines in the stream - int length; - - /// The main constructor for a LrcStream - LrcStream( - {this.previous, - required this.current, - this.next, - this.duration, - required this.position, - required this.length}) - //position should be greater than or equal to 0 - : assert(position >= 0), - //the length should be greater than or equal to the position - assert(length >= position), - //previous is null only if position is 0 - assert((previous == null) ? position == 0 : true), - //next is null only if position is the last - assert((next == null) ? position == length : true); -} - -/// Handy extensions on lists of LrcLine -extension LrcLineExtensions on List { - /// Creates a stream for each lyric using their durations - Stream toStream() async* { - for (var i = 0; i < length; i++) { - var lineCurrent = this[i]; - var lineNext = (i + 1 < length) ? this[i + 1] : null; - var durationToNext = (lineNext != null) - ? Duration( - milliseconds: lineNext.timestamp.inMilliseconds - - lineCurrent.timestamp.inMilliseconds) - : null; - yield LrcStream( - duration: durationToNext, - previous: (i != 0) ? this[i - 1] : null, - current: lineCurrent, - next: lineNext, - position: i, - length: length - 1); - if (durationToNext != null) { - await Future.delayed(durationToNext); - } - } - } -} - -/// Handy extensions on strings -extension StringExtensions on String { - /// Handy extension method that parses the string to an [Lrc] - Lrc toLrc() => Lrc.parse(this); - - /// Handy extension getter if the given string is a valid LRC - bool get isValidLrc => Lrc.isValid(this); -} diff --git a/lib/src/models/lrc.dart b/lib/src/models/lrc.dart new file mode 100644 index 0000000..fcb9408 --- /dev/null +++ b/lib/src/models/lrc.dart @@ -0,0 +1,133 @@ +part of lrc; + +/// The parsed LRC class. +/// +/// You can instantiate this class directly +/// or parse a string using `Lrc.parse()`. +class Lrc { + /// The overall type of LRC for this object + final LrcTypes type; + + /// The name of the artist of the song (optional) + /// + /// This corresponds to the ID tag `[ar:]`. + final String? artist; + + /// The name of the album of the song (optional) + /// + /// This corresponds to the ID tag `[al:]`. + final String? album; + + /// The title of the song (optional) + /// + /// This corresponds to the ID tag `[ti:]`. + final String? title; + + /// The name of the author of the lyrics (optional) + /// + /// This corresponds to the ID tag `[au:]`. + final String? author; + + /// The name of the creator of the LRC file (optional) + /// + /// This corresponds to the ID tag `[by:]`. + final String? creator; + + /// The name of the program that created the LRC file (optional) + /// + /// This corresponds to the ID tag `[re:]`. + final String? program; + + /// The version of the program that created the LRC file (optional) + /// + /// This corresponds to the ID tag `[ve:]`. + final String? version; + + /// The length of the song (optional) + /// + /// This corresponds to the ID tag `[length:]`. + final String? length; + + /// The language of the song, using an IETF BCP 47 language tag (optional) + /// + /// This corresponds to the ID tag `[la:]`. + final String? language; + + /// Offset of time in milliseconds, can be positive [shifts time up] + /// or negative [shifts time down] (optional) + /// + /// This corresponds to the ID tag `[offset:]`. + final int? offset; + + /// The list of lyric lines + final List lyrics; + + final int personCount; + + /// Handy parameter to get a stream of the lyrics. + /// See `List.toStream()`. + Stream get stream => lyrics.toStream(); + + /// Use this constructor if you want to manually create an LRC from scratch. + /// Otherwise, parse an LRC string using [Lrc.parse]. + const Lrc({ + this.type = LrcTypes.simple, + required this.lyrics, + this.artist, + this.album, + this.title, + this.creator, + this.author, + this.program, + this.version, + this.length, + this.offset, + this.language, + this.personCount = 1, + }); + + static Lrc parse(String text) => LrcParser.parse(text); + + /// Format the lrc to a readable string that can then be + /// outputted to an LRC file. + String format() { + var buffer = StringBuffer(); + + if (artist != null) buffer.writeln('[ar:$artist]'); + if (album != null) buffer.writeln('[al:$album]'); + if (title != null) buffer.writeln('[ti:$title]'); + if (length != null) buffer.writeln('[length:$length]'); + if (creator != null) buffer.writeln('[by:$creator]'); + if (author != null) buffer.writeln('[au:$author]'); + if (offset != null) buffer.writeln('[offset:${offset.toString()}]'); + if (program != null) buffer.writeln('[re:$program]'); + if (version != null) buffer.writeln('[ve:$version]'); + if (language != null) buffer.writeln('[la:$language]'); + + final lrcLength = lyrics.length; + for (var i = 0; i < lrcLength; i++) { + buffer.writeln(lyrics[i].formattedLine); + } + + return buffer.toString(); + } + + @override + String toString() { + var lyrics = this.lyrics.join('\n'); + + return ''' + Type: '$type' + Artist: '$artist' + Album: '$album' + Title: '$title' + Author: '$author' + Creator: '$creator' + Program: '$program' + Length: '$length' + Language: '$language' + Offset: '$offset' + Lyrics: $lyrics + '''; + } +} diff --git a/lib/src/models/lrc_line.dart b/lib/src/models/lrc_line.dart new file mode 100644 index 0000000..c8f42c7 --- /dev/null +++ b/lib/src/models/lrc_line.dart @@ -0,0 +1,72 @@ +part of lrc; + +///A line of lyrics, with its defined duration and raw lyrics +class LrcLine { + ///timestamp for the lyrics wherein it'll be displayed + final Duration timestamp; + + final num originalIndex; + + ///the raw lyrics for the line + final String lyrics; + + final String readableText; + + final List? parts; + + ///the type of lrc for this line + final LrcTypes type; + + final int? person; + + bool get isBGLyrics => person == 0; + + const LrcLine({ + required this.timestamp, + required this.originalIndex, + required this.lyrics, + required this.readableText, + required this.type, + required this.parts, + required this.person, + }); + + LrcLine withTimeStamp({ + required Duration newTimestamp, + List? parts, + }) { + return LrcLine( + timestamp: newTimestamp, + originalIndex: originalIndex, + lyrics: lyrics, + readableText: readableText, + type: type, + person: person, + parts: parts ?? this.parts, + ); + } + + ///get the string for a formatted line + String get formattedLine { + ///function to add leading zeros + String f(int x) => x.toString().padLeft(2, '0'); + + // LRC format doesn't accept hours. + final minutes = timestamp.inMinutes % 60, + seconds = timestamp.inSeconds % 60, + hundreds = timestamp.inMilliseconds % 1000 ~/ 10; + + return '[${f(minutes)}:${f(seconds)}.${f(hundreds)}]$lyrics'; + } + + @override + String toString() { + return ''' + Timestamp: '$timestamp' + OriginalIndex: '$originalIndex' + Lyrics: '$lyrics' + Parts: '$parts' + Person: '$person' + '''; + } +} diff --git a/lib/src/models/lrc_line_part.dart b/lib/src/models/lrc_line_part.dart new file mode 100644 index 0000000..9baccfb --- /dev/null +++ b/lib/src/models/lrc_line_part.dart @@ -0,0 +1,22 @@ +part of lrc; + +class LrcLinePart { + final Duration startTimestamp; + final Duration endTimestamp; + final String lyrics; + + const LrcLinePart({ + required this.startTimestamp, + required this.endTimestamp, + required this.lyrics, + }); + + @override + String toString() { + return ''' + startTimestamp: '$startTimestamp' + endTimestamp: '$endTimestamp' + Lyrics: '$lyrics' + '''; + } +} diff --git a/lib/src/models/lrc_stream.dart b/lib/src/models/lrc_stream.dart new file mode 100644 index 0000000..a7cc759 --- /dev/null +++ b/lib/src/models/lrc_stream.dart @@ -0,0 +1,39 @@ +part of lrc; + +/// A data class to store each yielding of the stream +class LrcStream { + /// The previous line. Is null if the current line is the fist position. + LrcLine? previous; + + /// Tthe current line + LrcLine current; + + /// The next line. Is null if the current line is the last position. + LrcLine? next; + + /// The duration from the current to the next. Is null if the current line is the last position. + Duration? duration; + + /// The position of the current line + int position; + + /// The total number of lines in the stream + int length; + + /// The main constructor for a LrcStream + LrcStream( + {this.previous, + required this.current, + this.next, + this.duration, + required this.position, + required this.length}) + //position should be greater than or equal to 0 + : assert(position >= 0), + //the length should be greater than or equal to the position + assert(length >= position), + //previous is null only if position is 0 + assert((previous == null) ? position == 0 : true), + //next is null only if position is the last + assert((next == null) ? position == length : true); +} diff --git a/lib/src/parsers/lrc_parser.dart b/lib/src/parsers/lrc_parser.dart new file mode 100644 index 0000000..5e0e2ad --- /dev/null +++ b/lib/src/parsers/lrc_parser.dart @@ -0,0 +1,280 @@ +part of lrc; + +class LrcParser { + static List _splitLines(String data) { + var lines = []; + 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; + } + + static List splitMultiLanguageLine(String lyric) { + return lyric.split(RegExp(r'(\s{2,}|\|)(?=\S)')); + } + + static Iterable extractTimeStampPartFromLine(String line, + {Duration? startTimeStamp}) sync* { + var isFirst = true; + var latestTimeStamp = startTimeStamp; + for (var j in RegExp(r'([^<\]]*)<(([0-9]{1,}):([0-9]{1,})\.([0-9]{1,})?)>') + .allMatches(line)) { + final lyrics = j.group(1) ?? ''; + final endTimestamp = Duration( + minutes: int.tryParse(j.group(3)!) ?? 0, + seconds: int.tryParse(j.group(4)!) ?? 0, + milliseconds: (int.tryParse(j.group(5)!) ?? 0), + ); + final startTimestamp = latestTimeStamp; + latestTimeStamp = endTimestamp; + if (startTimestamp == null) { + // we set the start for the next part + continue; + } + if (isFirst) { + // -- skip first part that has `v1:` etc + if (lyrics.length < 5 && + lyrics.startsWith('v') && + (lyrics.endsWith(':') || lyrics.endsWith(': '))) { + continue; + } + } + yield LrcLinePart( + startTimestamp: startTimestamp, + endTimestamp: endTimestamp, + lyrics: lyrics, + ); + isFirst = false; + } + } + + /// Parses an LRC from a string. Throws a `FormatExeption` + /// if the inputted string is not valid. + static Lrc parse(String content) { + if (!isValid(content)) { + throw FormatException('The inputted string is not a valid LRC file'); + } + + // split string into lines, code from Linesplitter().convert(data) + var lines = _splitLines(content); + + // temporary storer variables + String? artist, + album, + title, + length, + author, + creator, + offset, + program, + version, + language; + LrcTypes? type; + var lyrics = []; + + final personToIndex = {}; + var registeredTimestamps = {}; + var shouldSortLyrics = false; + + 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 = 0; i < lines.length; i++) { + var l = lines[i]; + artist ??= setIfMatchTag(l, 'ar'); + album ??= setIfMatchTag(l, 'al'); + title ??= setIfMatchTag(l, 'ti'); + author ??= setIfMatchTag(l, 'au'); + length ??= setIfMatchTag(l, 'length'); + creator ??= setIfMatchTag(l, 'by'); + offset ??= setIfMatchTag(l, 'offset'); + program ??= setIfMatchTag(l, 're'); + version ??= setIfMatchTag(l, 've'); + language ??= setIfMatchTag(l, 'la'); + + if (_LRCMultiTimestampParser._durRegex.hasMatch(l)) { + var lineType = LrcTypes.simple; + List? parts; + + final lrclineDetails = _LRCMultiTimestampParser.parseLine(l); + var lyric = lrclineDetails.lineText; + final timestamps = lrclineDetails.timestamps; + + if (shouldSortLyrics == false) { + if (timestamps.length > 1) { + // -- means it has multi timestamps + shouldSortLyrics = true; + } + if (timestamps + .any((element) => registeredTimestamps.contains(element))) { + // -- means it has multi language in the end of the file + shouldSortLyrics = true; + } + } + + int? person; + + // checkers for different types of LRCs + if (lyric.contains(RegExp(r'^\w:'))) { + //if extended + type = (type == LrcTypes.enhanced) + ? LrcTypes.extended_enhanced + : LrcTypes.extended; + + final personText = lyric[0]; + person = personToIndex[personText] ??= personToIndex.length + 1; + + parts = []; + parts.add(LrcLinePart( + startTimestamp: timestamps.first, + endTimestamp: timestamps.first, + lyrics: lyric.substring(2), + )); + + lineType = LrcTypes.extended; + } else if (lyric + .contains(RegExp(r'<[0-9]{1,}:[0-9]{1,}(\.[0-9]{1,})?>'))) { + // if enhanced + type = (type == LrcTypes.extended) + ? LrcTypes.extended_enhanced + : LrcTypes.enhanced; + parts = []; + lineType = LrcTypes.enhanced; + + final indexOfLT = lyric.indexOf('<'); + final personText = indexOfLT < 0 ? '' : lyric.substring(0, indexOfLT); + if (personText.contains(':')) { + person = personToIndex[personText] ??= personText.startsWith('v1:') + ? 1 + : personText.startsWith('v2:') + ? 2 + : personText.startsWith('v3:') + ? 3 + : personToIndex.length + 1; + } + + if (!lyric.endsWith('>')) { + try { + for (var start = i + 1; i < lines.length; i++) { + final nextLine = lines[start]; + final nextTimestamp = + _LRCMultiTimestampParser.extractMainTimestamp(nextLine); + if (nextTimestamp != null) { + lyric = '$lyric<$nextTimestamp>'; + break; + } + } + } catch (_) {} + } + parts.addAll( + extractTimeStampPartFromLine( + lyric, + startTimeStamp: timestamps.first, + ), + ); + } + + final lyricSplit = parts != null && parts.length > 1 + ? [lyric] + : splitMultiLanguageLine(lyric); + for (var i = 0; i < lyricSplit.length; i++) { + var lyric = lyricSplit[i]; + if (lyric.length < 5 && lyric.startsWith('v3:')) lyric = ''; + final readableText = parts != null && parts.isNotEmpty + ? parts.map((e) => e.lyrics).join() + : lyric; + for (final linetimestamp in timestamps) { + registeredTimestamps.add(linetimestamp); + lyrics.add(LrcLine( + timestamp: linetimestamp, + originalIndex: lyrics.length, + lyrics: lyric, + readableText: readableText.isNotEmpty ? readableText : lyric, + type: lineType, + parts: parts, + person: person, + )); + } + } + } + } + + if (type == LrcTypes.enhanced || type == LrcTypes.extended_enhanced) { + // extract bg tags + for (final m in RegExp(r'\[bg:(.*)\]').allMatches(content)) { + final text = m.group(1); + if (text == null) continue; + final parts = extractTimeStampPartFromLine(text).toList(); + if (parts.isEmpty) continue; + lyrics.add(LrcLine( + timestamp: parts[0].startTimestamp, + originalIndex: lyrics.length, + lyrics: text, + readableText: parts.map((e) => e.lyrics).join(), + type: type ?? LrcTypes.enhanced, + parts: parts, + person: 0, + )); + shouldSortLyrics = true; + } + } + + if (shouldSortLyrics) { + lyrics.sort((a, b) { + final res = + a.timestamp.inMicroseconds.compareTo(b.timestamp.inMicroseconds); + if (res == 0) { + return a.originalIndex.compareTo(b.originalIndex); + } + return res; + }); + } + + var personCount = + personToIndex.values.where((element) => element > 0).length; + if (personCount <= 0) personCount = 1; + + 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, + personCount: personCount, + ); + } + + /// Checks if the string [input] is a valid LRC using Regex. + static bool isValid(String input) => + RegExp(r'\[(\d{1,}).+\]').hasMatch(input); + + static String cleanPlainLyrics(String input) { + final regex = RegExp( + r'([\r\n]*\[((ti)|(a[rlu])|(by)|([rv]e)|(length)|(offset)|(la)):.+\][\r\n]*)'); + return input.replaceAll(regex, ''); + } +} diff --git a/lib/src/parsers/multi_timestamp_parser.dart b/lib/src/parsers/multi_timestamp_parser.dart new file mode 100644 index 0000000..8ffd8bd --- /dev/null +++ b/lib/src/parsers/multi_timestamp_parser.dart @@ -0,0 +1,85 @@ +part of lrc; + +/// Parses Multi-timestamped lyrics lines effectively. +class _LRCMultiTimestampParser { + const _LRCMultiTimestampParser(); + + /// supports 2-digit minutes, 2-digit seconds & optional 1-2-3-digit hundreds. + static final _durRegex = + RegExp(r'\[([0-9]{1,}):(\d{1,})(\.[0-9]{1,})?\]\s*(.*)?'); + + static String? extractMainTimestamp(String line) { + final m = _durRegex.firstMatch(line); + if (m == null) return null; + try { + final minutes = m.group(1); + if (minutes != null) { + final seconds = m.group(2) ?? '00'; + final hundreds = m.group(3) ?? '00'; + return '$minutes:$seconds$hundreds'; + } + } catch (_) {} + return null; + } + + /// Converts multi-timestamped lyrics lines into a pair of `lineText` & `timestamps` list + static _MultiTimeStampDetails parseLine(String line) { + var lineText = line; + final timestamps = []; + + while (true) { + final m = _durRegex.firstMatch(lineText); + if (m != null && m.group(1) != null) { + final timestamp = _durationFromStrings( + minutes: m.group(1), + seconds: m.group(2), + hundreds: m.group(3)?.substring(1), + ); + timestamps.add(timestamp); + lineText = m.group(4) ?? ''; + if (lineText.isEmpty) break; + } else { + break; + } + } + + return _MultiTimeStampDetails( + lineText: lineText, + timestamps: timestamps, + ); + } + + static Duration _durationFromStrings({ + required String? minutes, + required String? seconds, + required String? hundreds, + }) { + if (hundreds != null) { + var zerosToAdd = 6 - hundreds.length; + while (zerosToAdd > 0) { + hundreds = hundreds! + '0'; + zerosToAdd--; + } + } + + final m = minutes == null ? null : int.tryParse(minutes); + final s = seconds == null ? null : int.tryParse(seconds); + final micros = hundreds == null ? null : int.tryParse(hundreds); + + return Duration( + minutes: m ?? 0, + seconds: s ?? 0, + microseconds: micros ?? 0, + ); + } +} + +class _MultiTimeStampDetails { + final String lineText; + final List timestamps; + + const _MultiTimeStampDetails({ + required this.lineText, + required this.timestamps, + }); +} diff --git a/lib/src/parsers/ttml_parser.dart b/lib/src/parsers/ttml_parser.dart new file mode 100644 index 0000000..bce5846 --- /dev/null +++ b/lib/src/parsers/ttml_parser.dart @@ -0,0 +1,234 @@ +part of lrc; + +class TtmlParser { + static final _extractors = <_TtmlLineExtractorBase>[ + _TtmlLineExtractorRegex(), + _TtmlLineExtractorXml(), + ]; + + static bool isValid(String content) => + _extractors.any((extr) => extr.isValid(content)); + + static Lrc parse(String content) { + Lrc? lrc; + for (final extr in _extractors) { + lrc = _parseInternal(content, extr); + if (lrc.lyrics.isNotEmpty) return lrc; + } + return lrc!; + } + + static Lrc _parseInternal(String content, _TtmlLineExtractorBase extractor) { + final lrcLines = []; + final personToIndex = {}; + final htmlUnescape = HtmlUnescape(); + var type = LrcTypes.simple; + + for (final m in extractor.allMatches(content)) { + int? startMS; + // int? endMS; + try { + startMS = extractor.extractStartMS(m); + // endMS = (double.parse(m.group(2)!) * 1000).round(); + } catch (_) {} + if (startMS != null) { + final textOriginal = extractor.extractText(m); + String textNormalized; + if (textOriginal != null && textOriginal.isNotEmpty) { + textNormalized = htmlUnescape.convert(textOriginal); + } else { + textNormalized = ''; + } + final indexOfPartStart = textNormalized.indexOf('<'); + final personText = indexOfPartStart > 0 + ? textNormalized.substring(0, indexOfPartStart) + : ''; + final person = personToIndex[personText] ??= personToIndex.length + 1; + var parts = + LrcParser.extractTimeStampPartFromLine(textNormalized).toList(); + final lineType = parts.isNotEmpty ? LrcTypes.enhanced : LrcTypes.simple; + if (lineType == LrcTypes.enhanced) type = LrcTypes.enhanced; + lrcLines.add(LrcLine( + timestamp: Duration(milliseconds: startMS), + originalIndex: lrcLines.length, + lyrics: textNormalized, + readableText: parts.isNotEmpty + ? parts.map((e) => e.lyrics).join() + : textNormalized, + type: lineType, + parts: parts, + person: person, + )); + } + } + + return Lrc( + lyrics: lrcLines, + type: type, + ); + } +} + +class _TtmlLineExtractorXml extends _TtmlLineExtractorBase { + @override + bool isValid(String content) => content.contains('

+ s.getAttribute('ttm:role') != 'x-bg' && + s.getAttribute('begin') != null) + .toList(); + + if (spans.isEmpty) return p.innerText.trim(); + if (spans.length == 1) return spans.first.innerText.trim(); + + final buffer = StringBuffer(); + for (final span in spans) { + final beginMs = _timestampToMilliseconds(span.getAttribute('begin')!); + buffer.write('<${_msToLrcTimestamp(beginMs)}>${span.innerText.trim()} '); + } + final end = p.getAttribute('end'); + if (end != null) { + final endMs = _timestampToMilliseconds(end); + buffer.write('<${_msToLrcTimestamp(endMs)}>'); + } + return buffer.isEmpty ? null : buffer.toString(); + } + + static int _timestampToMilliseconds(String timestamp) { + final colonParts = timestamp.split(':'); + + var hours = 0, minutes = 0; + String secondsRaw; + + if (colonParts.length == 3) { + hours = int.tryParse(colonParts[0]) ?? 0; + minutes = int.tryParse(colonParts[1]) ?? 0; + secondsRaw = colonParts[2]; + } else if (colonParts.length == 2) { + minutes = int.tryParse(colonParts[0]) ?? 0; + secondsRaw = colonParts[1]; + } else { + secondsRaw = colonParts[0]; + } + + final secondsParts = secondsRaw.split('.'); + final seconds = int.tryParse(secondsParts[0]) ?? 0; + var milliseconds = 0; + if (secondsParts.length > 1) { + final msString = secondsParts[1].padRight(3, '0').substring(0, 3); + milliseconds = int.tryParse(msString) ?? 0; + } + + return (hours * 3_600_000) + + (minutes * 60_000) + + (seconds * 1_000) + + milliseconds; + } +} + +class _TtmlLineExtractorRegex extends _TtmlLineExtractorBase { + static final _regexp = RegExp( + r']*?begin="([\d:.]+)s?"[^>]*?end="([\d:.]+)s?"[^>]*?>(.*?)<\/p>', + multiLine: true, + dotAll: true, + ); + + @override + bool isValid(String content) => _regexp.hasMatch(content); + + @override + Iterable allMatches(String content) { + return _regexp.allMatches(content); + } + + @override + int? extractStartMS(RegExpMatch m) { + try { + final raw = m.group(1)!; + return _TtmlLineExtractorXml._timestampToMilliseconds(raw); + } catch (_) {} + return null; + } + + @override + int? extractEndMS(RegExpMatch m) { + try { + final raw = m.group(2)!; + return _TtmlLineExtractorXml._timestampToMilliseconds(raw); + } catch (_) {} + return null; + } + + @override + String? extractText(RegExpMatch m) { + final raw = m.group(3) ?? ''; + final spanRegex = RegExp(r']*begin="([^"]+)"[^>]*>([^<]*)<\/span>'); + final spans = spanRegex.allMatches(raw).toList(); + + if (spans.isEmpty) return raw; + if (spans.length == 1) return spans.first.group(2)?.trim(); + + final buffer = StringBuffer(); + for (final span in spans) { + final beginMs = + _TtmlLineExtractorXml._timestampToMilliseconds(span.group(1)!); + buffer.write( + '<${_TtmlLineExtractorXml._msToLrcTimestamp(beginMs)}>${span.group(2)?.trim()} '); + } + final end = m.group(2); + if (end != null) { + final endMs = _TtmlLineExtractorXml._timestampToMilliseconds(end); + buffer.write('<${_TtmlLineExtractorXml._msToLrcTimestamp(endMs)}>'); + } + return buffer.isEmpty ? null : buffer.toString(); + } +} + +abstract class _TtmlLineExtractorBase { + bool isValid(String content); + Iterable allMatches(String content); + int? extractStartMS(T item); + int? extractEndMS(T item); + String? extractText(T item); +} diff --git a/pubspec.yaml b/pubspec.yaml index c44b72f..09f0d4e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,11 +1,16 @@ name: lrc description: A Dart-only package that creates, parses, and handles LRC, which is a format that stores song lyrics. -version: 1.0.2 +version: 1.7.4 repository: https://github.com/Yivan000/lrc environment: - sdk: '>=2.12.0 <3.0.0' + sdk: ">=3.6.0 <4.0.0" +dependencies: + html_unescape: ^2.0.0 + xml: ^6.6.1 + charset: ^2.0.1 + kana_kit: ^2.1.1 dev_dependencies: pedantic: ^1.10.0 diff --git a/test/files/timed_lrc.lrc b/test/files/timed_lrc.lrc new file mode 100644 index 0000000..3540afe --- /dev/null +++ b/test/files/timed_lrc.lrc @@ -0,0 +1,92 @@ +[ti:Runaway (feat. Pusha T)] +[ar:Kanye West] +[by:Generated using SongSync] +[00:40.363]v1: <00:40.363>Look <00:40.595>at <00:40.916>ya, <00:41.100>look <00:41.314>at <00:41.624>ya, <00:41.796>look <00:42.022>at <00:42.314>ya, <00:42.504>look <00:42.724>at <00:43.028>ya <00:43.235> +[00:44.515]v1: <00:44.515>Look <00:44.842>at <00:45.139>ya, <00:45.336>look <00:45.556>at <00:45.859>ya, <00:46.032>look <00:46.258>at <00:46.567>ya, <00:46.757>look <00:46.870>at <00:46.970>ya <00:47.363> +[00:51.414]v1: <00:51.414>Look <00:51.711>at <00:52.026>ya, <00:52.205>look <00:52.425>at <00:52.734>ya, <00:52.919>look <00:53.139>at <00:53.424>ya, <00:53.621>look <00:53.793>at <00:53.893>ya <00:54.250> +[00:56.948]v1: <00:56.948>Look <00:57.193>at <00:57.526>ya, <00:57.726>look <00:57.910>at <00:58.243>ya, <00:58.409>look <00:58.593>at <00:58.910>ya, <00:59.091>look <00:59.275>at <00:59.593>ya, <00:59.777>look <00:59.977>at <01:00.259>ya <01:00.440> +[bg: <00:56.955>Ladies <00:57.283>and <00:57.467>gentlemen, <00:58.334>ladies, <00:58.819>ladies <00:59.086>and <00:59.286>gentlemen <01:00.440>] +[01:02.297]v2: <01:02.297>And <01:02.564>I <01:02.784>always <01:03.480>find, <01:03.843>yeah, <01:03.968>I <01:04.182>always <01:04.676>find <01:04.980>something <01:05.563>wrong <01:06.247> +[01:07.926]v2: <01:07.926>You <01:08.217>been <01:08.407>puttin' <01:08.764>up <01:08.984>with <01:09.270>my <01:09.478>shit <01:09.984>just <01:10.299>way <01:10.823>too <01:11.049>long <01:11.633> +[01:13.334]v2: <01:13.334>I'm <01:13.434>so <01:13.839>gifted <01:14.202>at <01:14.301>finding <01:15.077>what <01:15.261>I <01:15.630>don't <01:15.939>like <01:16.308>the <01:16.487>most <01:17.162> +[01:18.899]v2: <01:18.899>So <01:19.327>I <01:19.499>think <01:19.749>it's <01:20.041>time <01:20.564>for <01:20.725>us <01:21.271>to <01:21.408>have <01:21.956>a <01:22.105>toast <01:22.793> +[01:22.947]v2: <01:22.947>Let's <01:23.184>have <01:23.393>a <01:23.530>toast <01:23.881>for <01:24.071>the <01:24.238>douche<01:24.580>bags <01:25.515> +[01:25.699]v2: <01:25.699>Let's <01:26.002>have <01:26.210>a <01:26.347>toast <01:26.704>for <01:26.906>the <01:27.055>assholes <01:28.275> +[01:26.699]v3: <01:25.699>Let's <01:26.002>have <01:26.210>a <01:26.347>toast <01:26.704>for <01:26.906>the <01:27.055>assholes <01:28.275> +[01:28.472]v2: <01:28.472>Let's <01:28.632>have <01:28.846>a <01:28.971>toast <01:29.322>for <01:29.501>the <01:29.601>scum<01:30.128>bags <01:31.048> +[01:31.379]v2: <01:31.379>Every <01:31.807>one <01:32.015>of <01:32.146>them <01:32.360>that <01:32.515>I <01:32.800>know <01:33.808> +[01:33.991]v2: <01:33.991>Let's <01:34.241>have <01:34.442>a <01:34.594>toast <01:34.941>for <01:35.125>the <01:35.274>jerk-<01:35.626>offs <01:36.554> +[01:36.890]v2: <01:36.890>That'll <01:37.259>never <01:37.598>take <01:37.961>work <01:38.348>off <01:39.336> +[01:39.336]v2: <01:39.336>Baby, <01:39.764>I <01:40.091>got <01:40.496>a <01:40.769>plan <01:41.151> +[01:41.402]v2: <01:41.402>Run <01:41.676>away <01:42.087>fast <01:42.586>as <01:42.914>you <01:43.580>can <01:44.931> +[01:45.020]v2: <01:45.020>She <01:45.192>find <01:45.549>pictures <01:45.930>in <01:46.084>my <01:46.257>email <01:47.276> +[01:47.809]v2: <01:47.809>I <01:47.926>sent <01:48.201>this <01:48.374>bitch <01:48.576>a <01:48.719>picture <01:49.064>of <01:49.241>my <01:49.456>dick <01:50.203> +[01:50.537]v2: <01:50.537>I <01:50.822>don't <01:51.024>know <01:51.215>what <01:51.411>it <01:51.536>is <01:51.726>with <01:51.828>females <01:53.049> +[01:53.463]v2: <01:53.463>But <01:53.683>I'm <01:53.837>not <01:54.057>too <01:54.212>good <01:54.420>at <01:54.587>that <01:54.825>shit <01:55.831> +[01:56.078]v2: <01:56.078>See, <01:56.339>I <01:56.530>could <01:56.678>have <01:57.006>me <01:57.196>a <01:57.369>good <01:57.714>girl <01:58.509> +[01:58.513]v2: <01:58.513>And <01:58.727>still <01:59.179>be <01:59.357>addicted <01:59.842>to <02:00.024>them <02:00.178>hood<02:00.574>rats <02:01.418> +[02:01.652]v2: <02:01.652>And <02:01.875>I <02:02.057>just <02:02.222>blame <02:02.523>everything <02:03.057>on <02:03.222>you <02:04.081> +[02:04.081]v2: <02:04.081>At <02:04.286>least <02:04.467>you <02:04.619>know <02:04.966>that's <02:05.299>what <02:05.499>I'm <02:05.654>good <02:05.982>at <02:06.934> +[02:07.129]v2: <02:07.129>And <02:07.396>I <02:07.587>always <02:08.300>find, <02:08.699>yeah, <02:08.799>I <02:08.899>always <02:09.520>find <02:09.785> +[02:09.918]v2: <02:09.918>Yeah, <02:10.018>I <02:10.233>always <02:10.905>find <02:11.187>something <02:11.815>wrong <02:12.462> +[02:14.158]v2: <02:14.158>You <02:14.419>been <02:14.603>puttin' <02:14.936>up <02:15.171>with <02:15.469>my <02:15.803>shit <02:16.136>just <02:16.469>way <02:16.971>too <02:17.187>long <02:17.953> +[02:19.489]v2: <02:19.489>I'm <02:19.649>so <02:20.095>gifted <02:20.339>at <02:20.542>finding <02:21.327>what <02:21.523>I <02:21.874>don't <02:22.201>like <02:22.558>the <02:22.749>most <02:23.416> +[02:25.062]v2: <02:25.062>So <02:25.377>I <02:25.555>think <02:25.817>it's <02:26.085>time <02:26.805>for <02:26.953>us <02:27.554>to <02:27.727>have <02:28.215>a <02:28.357>toast <02:29.077> +[02:29.190]v2: <02:29.190>Let's <02:29.422>have <02:29.636>a <02:29.773>toast <02:30.118>for <02:30.308>the <02:30.475>douche<02:30.808>bags <02:31.591> +[02:31.958]v2: <02:31.958>Let's <02:32.255>have <02:32.445>a <02:32.588>toast <02:32.892>for <02:33.082>the <02:33.182>ass<02:33.648>holes <02:34.534> +[02:34.683]v2: <02:34.683>Let's <02:34.873>have <02:35.093>a <02:35.230>toast <02:35.581>for <02:35.747>the <02:35.884>scum<02:36.233>bags <02:37.238> +[02:37.588]v2: <02:37.588>Every <02:38.035>one <02:38.235>of <02:38.403>them <02:38.585>that <02:38.753>I <02:39.070>know <02:39.863> +[02:40.247]v2: <02:40.247>Let's <02:40.492>have <02:40.654>a <02:40.809>toast <02:41.121>for <02:41.337>the <02:41.508>jerk-<02:41.838>offs <02:42.683> +[02:43.100]v2: <02:43.100>That'll <02:43.510>never <02:43.849>take <02:44.206>work <02:44.599>off <02:45.535> +[02:45.537]v2: <02:45.537>Baby, <02:46.012>I <02:46.340>got <02:46.744>a <02:47.012>plan <02:47.357> +[02:47.639]v2: <02:47.639>Run <02:47.936>away <02:48.341>fast <02:48.840>as <02:49.007>you <02:49.632>can <02:51.071> +[02:51.239]v2: <02:51.239>Run <02:51.465>away <02:52.006>from <02:52.274>me, <02:52.494>baby <02:53.174> +[02:54.204]v2: <02:54.204>Ah, <02:55.316>run <02:55.524>away <02:55.959> +[02:56.761]v2: <02:56.761>Run <02:57.112>away <02:57.463>from <02:57.784>me, <02:57.974>baby <02:58.946> +[03:00.734]v2: <03:00.734>Run <03:00.983>away <03:01.722> +[03:02.257]v2: <03:02.257>When <03:02.530>it <03:02.726>starts <03:03.066>to <03:03.262>get <03:03.565>crazy <03:04.663> +[03:05.894]v2: <03:05.894>Then <03:06.155>run <03:06.435>away <03:07.106> +[03:07.582]v2: <03:07.582>Babe, <03:08.072>I <03:08.405>got <03:08.723>a <03:08.923>plan, <03:09.789>run <03:09.955>away <03:10.323>as <03:10.477>fast <03:10.824>as <03:11.173>you <03:11.805>can <03:12.490> +[03:13.342]v2: <03:13.342>Run <03:13.484>away <03:13.853>from <03:14.305>me, <03:14.531>baby <03:15.267> +[03:17.281]v2: <03:17.281>Run <03:17.524>away <03:18.071> +[03:18.838]v2: <03:18.838>Run <03:19.135>away <03:19.670>from <03:20.010>me, <03:20.200>baby <03:20.973> +[03:22.790]v2: <03:22.790>Run <03:23.004>away <03:23.708> +[03:24.339]v2: <03:24.339>When <03:24.547>it <03:24.737>starts <03:25.082>to <03:25.267>get <03:25.576>crazy <03:26.265> +[03:26.764]v2: <03:26.764>Why <03:27.009>can't <03:27.361>she <03:27.710>just <03:28.443>run <03:28.609>away? <03:29.014> +[03:29.710]v2: <03:29.710>Baby, <03:30.037>I <03:30.376>got <03:30.768>a <03:31.042>plan <03:31.460> +[03:31.763]v2: <03:31.763>Run <03:31.983>away <03:32.286>as <03:32.482>fast <03:32.905>as <03:33.214>you <03:33.892>can <03:34.528> +[03:34.526]v2: <03:34.526>Twenty-four <03:35.293>seven, <03:35.822>three <03:36.197>sixty-five, <03:37.292>pussy <03:37.583>stays <03:38.071>on <03:38.291>my <03:38.595>mind <03:39.107> +[03:39.259]v2: <03:39.259>I-<03:39.688>I-<03:40.021>I-<03:40.353>I <03:40.488>did <03:40.653>it, <03:41.021>alright, <03:41.338>alright, <03:41.738>I <03:42.037>admit <03:42.353>it <03:42.523> +[03:42.672]v2: <03:42.672>Now <03:42.904>pick <03:43.112>your <03:43.212>next <03:43.552>move, <03:43.855>you <03:44.028>could <03:44.242>leave <03:44.522>or <03:44.766>live <03:45.140>with <03:45.295>it <03:45.395> +[03:45.619]v2: <03:45.619>Ichabod <03:46.315>Crane <03:46.666>with <03:46.791>that <03:46.963>motherfuckin' <03:47.689>top <03:48.046>off <03:48.328> +[03:48.632]v2: <03:48.632>Split <03:48.941>and <03:49.096>go <03:49.351>where, <03:49.821>back <03:50.030>to <03:50.155>wearing <03:50.458>knockoffs? <03:50.944> +[03:50.947]v2: <03:50.947>Haha, <03:51.509>knock <03:51.709>it <03:51.893>off, <03:52.426>Neiman's, <03:52.877>shop <03:53.093>it <03:53.261>off <03:53.547> +[03:53.734]v2: <03:53.734>Let's <03:53.888>talk <03:54.203>over <03:54.501>mai <03:54.792>tais, <03:55.138>waitress, <03:55.614>top <03:55.881>it <03:55.982>off <03:56.299> +[03:56.638]v2: <03:56.638>Hoes <03:57.078>like <03:57.328>vultures, <03:57.768>wanna <03:57.988>fly <03:58.178>in <03:58.589>your <03:58.689>Freddy <03:59.106>loafers <03:59.428> +[03:59.575]v2: <03:59.575>You <03:59.822>can't <04:00.108>blame <04:00.289>'em, <04:00.473>they <04:00.673>ain't <04:00.854>never <04:01.140>seen <04:01.374>Versace <04:01.841>sofas <04:02.285> +[04:02.527]v2: <04:02.527>Every <04:02.824>bag, <04:03.187>every <04:03.526>blouse, <04:03.990>every <04:04.258>bracelet <04:04.945> +[04:05.226]v2: <04:05.226>Comes <04:05.707>with <04:05.826>a <04:06.029>price <04:06.374>tag, <04:06.713>baby, <04:07.135>face <04:07.558>it <04:07.730> +[04:08.015]v2: <04:08.015>You <04:08.247>should <04:08.520>leave <04:08.847>if <04:09.002>you <04:09.181>can't <04:09.365>accept <04:09.645>the <04:09.746>basics <04:10.463> +[04:10.762]v2: <04:10.762>Plenty <04:11.215>hoes <04:11.548>in <04:11.700>the <04:11.881>baller-<04:12.183>**** <04:12.548>matrix <04:13.222> +[04:13.339]v2: <04:13.339>Invisibly <04:14.023>set, <04:14.451>the <04:14.653>Rolex <04:15.171>is <04:15.403>faceless <04:15.949> +[04:16.184]v2: <04:16.184>I'm <04:16.487>just <04:16.654>young, <04:17.021>rich, <04:17.204>and <04:17.404>tasteless, <04:18.373>P <04:18.745> +[04:19.531]v2: <04:19.531>Never <04:19.893>was <04:20.077>much <04:20.286>of <04:20.435>a <04:20.624>romantic <04:21.403> +[04:22.267]v2: <04:22.267>I <04:22.541>could <04:22.725>never <04:23.008>take <04:23.242>the <04:23.410>intimacy <04:24.523> +[04:25.188]v2: <04:25.188>And <04:25.462>I <04:25.630>know <04:25.795>I <04:25.979>did <04:26.297>damage <04:27.168> +[04:27.601]v2: <04:27.601>'Cause <04:27.821>the <04:27.987>look <04:28.189>in <04:28.320>your <04:28.511>eyes <04:28.874>is <04:29.094>killing <04:29.433>me <04:29.941> +[04:30.556]v2: <04:30.556>I <04:30.811>guess <04:31.168>you <04:31.268>knew <04:31.377>of <04:31.519>that <04:31.704>advantage <04:32.789> +[04:32.953]v2: <04:32.953>'Cause <04:33.243>you <04:33.411>could <04:33.582>blame <04:33.909>me <04:34.246>for <04:34.595>every<04:35.046>thing <04:35.998> +[04:36.123]v2: <04:36.123>And <04:36.295>I <04:36.462>don't <04:36.610>know <04:36.819>how <04:36.955>I'ma <04:37.324>manage <04:38.265> +[04:38.614]v2: <04:38.614>If <04:39.088>one <04:39.421>day, <04:39.771>you <04:39.955>just <04:40.120>up <04:40.320>and <04:40.475>leave <04:41.494> +[04:43.009]v2: <04:43.009>And <04:43.145>I <04:43.348>always <04:44.056>find, <04:44.448>yeah, <04:44.548>I <04:44.764>always <04:45.454>find <04:45.757>something <04:46.340>wrong <04:46.919> +[04:48.654]v2: <04:48.654>You <04:48.987>been <04:49.183>puttin' <04:49.534>up <04:49.634>with <04:49.837>my <04:50.052>shit <04:50.557>just <04:50.873>way <04:51.396>too <04:51.622>long <04:52.438> +[04:54.003]v2: <04:54.003>I'm <04:54.157>so <04:54.621>gifted <04:54.972>at <04:55.072>finding <04:55.859>what <04:56.031>I <04:56.406>don't <04:56.709>like <04:57.084>the <04:57.263>most <04:57.886> +[04:59.534]v2: <04:59.534>So <04:59.902>I <05:00.069>think <05:00.319>it's <05:00.616>time <05:01.181>for <05:01.497>us <05:02.044>to <05:02.217>have <05:02.728>a <05:02.883>toast <05:03.494> +[05:03.665]v2: <05:03.665>Let's <05:03.915>have <05:04.115>a <05:04.267>toast <05:04.582>for <05:04.798>the <05:04.947>douche<05:05.306>bags <05:06.150> +[05:06.401]v2: <05:06.401>Let's <05:06.573>have <05:06.775>a <05:06.912>toast <05:07.275>for <05:07.477>the <05:07.626>ass<05:08.065>holes <05:08.998> +[05:09.177]v2: <05:09.177>Let's <05:09.409>have <05:09.623>a <05:09.748>toast <05:10.099>for <05:10.277>the <05:10.377>scum<05:10.775>bags <05:11.718> +[05:12.054]v2: <05:12.054>Every <05:12.533>one <05:12.699>of <05:12.885>them <05:13.067>that <05:13.235>I <05:13.549>know <05:14.420> +[05:14.685]v2: <05:14.685>Let's <05:14.919>have <05:15.135>a <05:15.300>toast <05:15.618>for <05:15.834>the <05:15.967>jerk-<05:16.303>offs <05:17.150> +[05:17.600]v2: <05:17.600>That'll <05:18.034>never <05:18.373>take <05:18.730>work <05:19.123>off <05:20.020> +[05:20.021]v2: <05:20.021>Baby, <05:20.482>I <05:20.815>got <05:21.148>a <05:21.498>plan <05:21.826> +[05:22.091]v2: <05:22.091>Run <05:22.378>away <05:22.861>fast <05:23.194>as <05:23.562>you <05:24.210>can <05:25.499> \ No newline at end of file diff --git a/test/files/timed_lrc_2.lrc b/test/files/timed_lrc_2.lrc new file mode 100644 index 0000000..c54275b --- /dev/null +++ b/test/files/timed_lrc_2.lrc @@ -0,0 +1,61 @@ +[ti:Brooklyn Baby] +[ar:Lana Del Rey] +[offset:+0] +[by:Generated using SongSync] +[00:18.653]v1:<00:18.653>They <00:18.900>say <00:19.352>I'm <00:19.588>too <00:20.298>young <00:20.845>to <00:21.184>love <00:21.648>you <00:22.543> +[00:23.256]v1:<00:23.256>I <00:23.544>don't <00:24.092>know <00:24.556>what <00:24.942>I <00:25.603>need <00:27.213> +[00:27.790]v1:<00:27.790>They <00:28.013>think <00:28.298>I <00:28.554>don't <00:28.997>understand <00:31.076> +[00:31.145]v1:<00:31.145>The <00:31.392>freedom <00:32.201>land <00:32.960>of <00:33.168>the <00:33.403>sev<00:34.053>en<00:34.686>ties <00:36.506> +[00:36.556]v1:<00:36.556>I <00:36.697>think <00:37.076>I'm <00:37.385>too <00:37.950>cool <00:38.563>to <00:38.800>know <00:39.252>ya <00:40.174> +[00:40.738]v1:<00:40.738>You <00:40.928>say <00:41.212>I'm <00:41.539>like <00:41.777>the <00:42.122>ice <00:42.746>I <00:43.282>freeze <00:44.918> +[00:45.362]v1:<00:45.362>I'm <00:45.496>churning <00:46.192>out <00:46.709>novels <00:47.584>like <00:48.383> +[00:48.293]v1:<00:48.293>Beat <00:48.804>poetry <00:50.081><00:50.422>on <00:50.810>am<00:51.066>phe<00:51.433>ta<00:52.022>mines <00:55.180> +[00:55.966]v1:<00:55.966>I <00:56.403>say <01:00.424> +[01:04.696]v1:<01:04.696>I <01:05.062>say <01:09.047> +[01:10.260]v1:<01:10.260>Well, <01:10.525>my <01:10.870>boyfriend's <01:12.155>in <01:12.458>a <01:12.732>band <01:13.611> +[01:14.214]v1:<01:14.214>He <01:14.474>plays <01:14.930>guitar <01:15.632>while <01:16.049>I <01:16.274>sing <01:16.501>Lou <01:17.003>Reed <01:18.122> +[01:19.056]v1:<01:19.056>I've <01:19.362>got <01:19.576>feathers <01:20.582>in <01:21.082>my <01:21.433>hair <01:22.449> +[01:23.453]v1:<01:23.453>I <01:23.622>get <01:23.932>down <01:24.388>to <01:24.672>beat <01:25.050>poetry <01:26.763> +[01:27.804]v1:<01:27.804>And <01:28.128>my <01:28.420>jazz <01:28.878>collection's <01:30.264>rare <01:31.794> +[01:32.152]v1:<01:32.152>I <01:32.320>can <01:32.616>play <01:33.313>most <01:33.968>anything <01:35.705> +[01:36.648]v1:<01:36.648>I'm <01:37.054>a <01:37.311>Brooklyn <01:38.229>baby <01:40.161> +[01:41.029]v1:<01:41.029>I'm <01:41.353>a <01:41.656>Brooklyn <01:42.757>baby <01:44.788> +[01:55.700]v1:<01:55.700>They <01:55.920>say <01:56.275>I'm <01:56.566>too <01:57.197>young <01:57.733>to <01:58.113>love <01:58.548>you <01:59.523> +[01:59.977]v1:<01:59.977>They <02:00.256>say <02:00.665>I'm <02:00.839>too <02:01.440>dumb <02:01.926>to <02:02.219>see <02:03.994> +[02:04.538]v1:<02:04.538>They <02:04.719>judge <02:05.118>me <02:05.386>like <02:05.683>a <02:05.992>picture <02:06.790>book <02:07.511> +[02:07.548]v1:<02:07.548>By <02:07.836>the <02:08.098>colors, <02:08.872>like <02:09.508>they <02:09.716>forgot <02:10.722>to <02:11.049>read <02:12.902> +[02:13.332]v1:<02:13.332>I <02:13.567>think <02:13.858>we're <02:14.120>like <02:14.709>fire <02:15.600>and <02:15.863>water <02:17.032> +[02:17.652]v1:<02:17.652>I <02:17.839>think <02:18.125>we're <02:18.387>like <02:18.678>the <02:18.999>wind <02:19.588>and <02:19.933>sea <02:21.715> +[02:22.070]v1:<02:22.070>You're <02:22.323>burning <02:22.876>up, <02:23.132>I'm <02:23.429>cooling <02:24.280>down <02:25.134> +[02:25.227]v1:<02:25.227>You're <02:25.390>up, <02:25.694>I'm <02:26.216>down <02:26.969> +[02:27.357]v1:<02:27.357>You're <02:27.550>blind, <02:28.145>I <02:28.603>see <02:31.641> +[02:32.630]v1:<02:32.630>But <02:32.770>I'm <02:33.138>free <02:36.187> +[02:41.383]v1:<02:41.383>I'm <02:41.796>free <02:45.916> +[02:46.816]v1:<02:46.816>Well, <02:47.027>my <02:47.341>boyfriend's <02:48.627>in <02:48.883>a <02:49.199>band <02:50.102> +[02:50.738]v1:<02:50.738>He <02:51.042>plays <02:51.385>guitar <02:52.188>while <02:52.611>I <02:52.711>sing <02:53.152>Lou <02:53.545>Reed <02:54.574> +[02:55.513]v1:<02:55.513>I've <02:55.825>got <02:56.039>feathers <02:57.223>in <02:57.533>my <02:57.925>hair <02:58.909> +[02:59.900]v1:<02:59.900>I <03:00.081>get <03:00.397>down <03:01.003>to <03:01.265>beat <03:01.515>poetry <03:02.977> +[03:04.349]v1:<03:04.349>And <03:04.590>my <03:04.899>jazz <03:05.345>collection's <03:06.732>rare <03:07.933> +[03:08.664]v1:<03:08.664>I <03:08.887>can <03:09.083>play <03:09.773>most <03:10.356>anything <03:12.185> +[03:13.138]v1:<03:13.138>I'm <03:13.409>a <03:13.792>Brooklyn <03:14.615>baby <03:17.026> +[03:17.553]v1:<03:17.553>I'm <03:17.818>a <03:18.127>Brooklyn <03:19.222>baby <03:21.316> +[03:23.468]v1:<03:23.468>I'm <03:23.568>talking <03:24.143>about <03:24.411>my <03:24.702>generation <03:27.222> +[03:27.978]v1:<03:27.978>Talking <03:28.386>about <03:28.753>that <03:29.143>newer <03:29.969>nation <03:31.491> +[03:32.111]v1:<03:32.111>And <03:32.399>if <03:32.709>you <03:32.982>don't <03:33.559>like <03:34.154>it <03:34.698> +[03:34.708]v1:<03:34.708>You <03:35.074>can <03:35.675>beat <03:36.793>it <03:37.621> +[03:37.884]v1:<03:37.884>Beat <03:38.137>it, <03:38.619>baby <03:40.228> +[03:40.844]v1:<03:40.844>You <03:41.097>never <03:41.579>liked <03:41.858>the <03:42.168>way <03:42.566>I <03:42.870>said <03:43.494>it <03:44.535> +[03:44.905]v1:<03:44.905>If <03:45.128>you <03:45.433>don't <03:45.638>get <03:45.814>it, <03:46.309>then <03:46.808>forget <03:48.051>it <03:48.770> +[03:49.502]v1:<03:49.502>'Cause <03:49.897>I <03:49.997>don't <03:50.195>have <03:50.469>to <03:50.599>fucking <03:51.676>explain <03:53.937>it <03:55.377> +[03:56.761]v1:<03:56.761>And <03:57.014>my <03:57.347>boyfriend's <03:58.561>in <03:59.078>a <03:59.334>band <04:00.206> +[04:00.941]v1:<04:00.941>He <04:01.185>plays <04:01.444>guitar <04:02.187>while <04:02.598>I <04:02.698>sing <04:03.145>Lou <04:03.603>Reed <04:04.680> +[04:05.520]v1:<04:05.520>I've <04:05.856>got <04:06.082>feathers <04:07.314>in <04:07.661>my <04:07.928>hair <04:08.975> +[04:10.047]v1:<04:10.047>I <04:10.151>get <04:10.454>high <04:10.788>on <04:11.031>hydroponic <04:12.477>weed <04:13.575> +[04:14.530]v1:<04:14.530>And <04:14.759>my <04:15.128>jazz <04:15.717>collection's <04:17.144>rare <04:18.492> +[04:18.992]v1:<04:18.992>I <04:19.202>get <04:19.483>down <04:20.005>to <04:20.286>beat <04:20.582>poetry <04:22.868> +[04:23.393]v1:<04:23.393>I'm <04:23.664>a <04:23.991>Brooklyn <04:25.097>baby <04:26.788> +[04:27.736]v1:<04:27.736>I'm <04:28.036>a <04:28.363>Brooklyn <04:29.440>baby <04:31.619> +[04:50.175]v1:<04:50.175>Yeah, <04:50.275>my <04:50.493>boyfriend's <04:51.766>pretty <04:52.569>cool <04:53.980> +[04:54.491]v1:<04:54.491>But <04:54.821>he's <04:55.172>not <04:55.630>as <04:56.219>cool <04:56.814>as <04:57.219>me <04:58.437> +[04:58.599]v1:<04:58.599>'Cause <04:58.911>I'm <04:59.197>a <04:59.641>Brooklyn <05:00.595>baby <05:02.807> +[05:03.417]v1:<05:03.417>I'm <05:03.765>a <05:04.080>Brooklyn <05:05.514>baby <05:07.710> \ No newline at end of file diff --git a/test/files/timed_lrc_3.lrc b/test/files/timed_lrc_3.lrc new file mode 100644 index 0000000..6e80a6f --- /dev/null +++ b/test/files/timed_lrc_3.lrc @@ -0,0 +1,75 @@ +[ti:KONTINUUM] +[ar:SennaRin] +[offset:+0] +[by:Generated using SongSync] +[00:00.000]v1:<00:00.000>KONTINUUM <00:00.087><00:00.087>- <00:00.174><00:00.174>SennaRin<00:00.261><00:00.261> +[00:02.110]v1:<00:02.110>Deeper <00:04.756><00:04.756>cover <00:07.620><00:07.620>Spotlight <00:10.339><00:10.339>lover<00:12.308><00:12.308> +[00:12.308]v1:<00:12.308>The <00:12.507><00:12.507>stars <00:13.235><00:13.235>circle <00:14.314><00:14.314>around <00:14.907><00:14.907>your <00:15.499><00:15.499>head<00:16.786><00:16.786> +[00:16.786]v1:<00:16.786>Nations <00:17.131><00:17.131>stoop <00:17.464><00:17.464>to <00:17.722><00:17.722>kiss <00:18.128><00:18.128>the <00:18.408><00:18.408>ground <00:18.751><00:18.751>you <00:19.016><00:19.016>walk <00:19.906><00:19.906>on<00:21.178><00:21.178> +[00:22.361]v1:<00:22.361>Shiller<00:23.221><00:23.221> +[00:23.221]v1:<00:23.221>Better <00:23.388><00:23.388>a <00:23.556><00:23.556>lie?<00:23.876><00:23.876> +[00:23.876]v1:<00:23.876>Better <00:24.060><00:24.060>I <00:24.259><00:24.259>die?<00:24.707><00:24.707> +[00:24.707]v1:<00:24.707>Better <00:24.924><00:24.924>I <00:25.126><00:25.126>make <00:25.299><00:25.299>it <00:25.492><00:25.492>stop?<00:26.051><00:26.051> +[00:26.051]v1:<00:26.051>Gravity <00:26.343><00:26.343>shift<00:26.692><00:26.692> +[00:26.692]v1:<00:26.692>In <00:26.875><00:26.875>the <00:27.059><00:27.059>abyss<00:27.419><00:27.419> +[00:27.419]v1:<00:27.419>Show <00:27.596><00:27.596>me <00:27.755><00:27.755>the <00:27.908><00:27.908>shoe <00:28.076><00:28.076>will <00:28.259><00:28.259>drop<00:28.778><00:28.778> +[00:28.778]v1:<00:28.778>Could <00:29.074><00:29.074>be <00:29.367><00:29.367>I'm <00:29.735><00:29.735>just <00:30.110><00:30.110>apassing <00:30.934><00:30.934>trend<00:31.704><00:31.704> +[00:31.704]v1:<00:31.704>You <00:31.958><00:31.958>have <00:32.278><00:32.278>to <00:32.518><00:32.518>tune <00:33.206><00:33.206>in <00:33.709><00:33.709>cuz <00:34.285><00:34.285>it's <00:35.049><00:35.049>not <00:35.721><00:35.721>the <00:36.320><00:36.320>end<00:37.457><00:37.457> +[00:38.273]v1:<00:38.273>Kill <00:38.968><00:38.968>this <00:39.585><00:39.585>destiny <00:41.428><00:41.428>that's <00:41.757><00:41.757>tryna <00:42.093><00:42.093>to <00:42.445><00:42.445>tell <00:42.764><00:42.764>you <00:43.474><00:43.474>what <00:44.090><00:44.090>you <00:44.713><00:44.713>should <00:44.924><00:44.924>be<00:45.457><00:45.457> +[00:45.457]v1:<00:45.457>Into <00:45.920><00:45.920>the <00:46.533><00:46.533>void <00:46.893><00:46.893>I <00:47.606><00:47.606>scream<00:48.271><00:48.271> +[00:48.271]v1:<00:48.271>Someone <00:48.524><00:48.524>try <00:48.910><00:48.910>and <00:49.414><00:49.414>stop <00:49.869><00:49.869>me<00:50.563><00:50.563> +[00:50.563]v1:<00:50.563>Check <00:50.842><00:50.842>out <00:51.178><00:51.178>my <00:51.453><00:51.453>face<00:52.193><00:52.193> +[00:52.193]v1:<00:52.193>I <00:52.505><00:52.505>got <00:53.258><00:53.258>the <00:53.609><00:53.609>look<00:54.041><00:54.041> +[00:54.041]v1:<00:54.041>Not <00:54.288><00:54.288>gonna <00:54.920><00:54.920>play <00:55.336><00:55.336>by <00:55.991><00:55.991>the <00:56.263><00:56.263>book<00:56.711><00:56.711> +[00:56.711]v1:<00:56.711>This <00:57.040><00:57.040>mic <00:57.400><00:57.400>just <00:57.792><00:57.792>gotturned <00:58.751><00:58.751>on<00:59.476><00:59.476> +[00:59.476]v1:<00:59.476>Make <00:59.757><00:59.757>your <01:00.172><01:00.172>decision<01:02.127><01:02.127> +[01:02.127]v1:<01:02.127>Dare <01:02.452><01:02.452>to <01:02.700><01:02.700>believe<01:04.004><01:04.004> +[01:04.004]v1:<01:04.004>To <01:04.181><01:04.181>become<01:05.124><01:05.124> +[01:05.124]v1:<01:05.124>To <01:05.412><01:05.412>become<01:06.885><01:06.885> +[01:06.885]v1:<01:06.885>Spotlight <01:08.308><01:08.308>lover<01:09.735><01:09.735> +[01:09.735]v1:<01:09.735>Deepest <01:11.053><01:11.053>cover<01:12.213><01:12.213> +[01:12.213]v1:<01:12.213>If <01:12.421><01:12.421>you <01:12.830><01:12.830>can <01:13.135><01:13.135>dare <01:13.544><01:13.544>to <01:13.823><01:13.823>believe<01:15.028><01:15.028> +[01:15.028]v1:<01:15.028>To <01:15.254><01:15.254>become<01:16.223><01:16.223> +[01:16.223]v1:<01:16.223>To <01:16.436><01:16.436>become<01:18.045><01:18.045> +[01:18.045]v1:<01:18.045>Spotlight <01:19.390><01:19.390>lover<01:20.839><01:20.839> +[01:20.839]v1:<01:20.839>Deepest <01:22.190><01:22.190>cover<01:23.303><01:23.303> +[01:23.757]v1:<01:23.757>Spotlight<01:26.184><01:26.184> +[01:27.647]v1:<01:27.647>This <01:27.846><01:27.846>star's <01:28.502><01:28.502>future <01:29.046><01:29.046>hangs <01:29.478><01:29.478>by <01:29.854><01:29.854>a <01:30.453><01:30.453>thread<01:31.807><01:31.807> +[01:32.416]v1:<01:32.416>Am <01:32.624><01:32.624>I <01:32.784><01:32.784>strong <01:33.194><01:33.194>enough?<01:33.556><01:33.556> +[01:33.556]v1:<01:33.556>To <01:33.742><01:33.742>be <01:34.130><01:34.130>continued<01:36.698><01:36.698> +[01:37.538]v1:<01:37.538>Chiller<01:38.510><01:38.510> +[01:38.510]v1:<01:38.510>Follow <01:38.670><01:38.670>a <01:38.838><01:38.838>key<01:39.175><01:39.175> +[01:39.175]v1:<01:39.175>Follow <01:39.366><01:39.366>a <01:39.574><01:39.574>face<01:39.897><01:39.897> +[01:39.897]v1:<01:39.897>Follow <01:40.102><01:40.102>a <01:40.325><01:40.325>clue <01:40.582><01:40.582>from <01:40.806><01:40.806>space<01:41.227><01:41.227> +[01:41.227]v1:<01:41.227>Evil <01:41.403><01:41.403>'n' <01:41.579><01:41.579>good<01:41.955><01:41.955> +[01:41.955]v1:<01:41.955>Deep <01:42.130><01:42.130>in <01:42.298><01:42.298>the <01:42.443><01:42.443>wood<01:42.748><01:42.748> +[01:42.748]v1:<01:42.748>Nobody <01:42.907><01:42.907>left <01:43.093><01:43.093>a <01:43.307><01:43.307>trace<01:44.098><01:44.098> +[01:44.098]v1:<01:44.098>They <01:44.379><01:44.379>left <01:44.715><01:44.715>me <01:45.018><01:45.018>on <01:45.426><01:45.426>this <01:45.770><01:45.770>cliff <01:46.093><01:46.093>to <01:46.357><01:46.357>die<01:46.937><01:46.937> +[01:46.937]v1:<01:46.937>My <01:47.237><01:47.237>fingers <01:48.014><01:48.014>slipping<01:49.204><01:49.204> +[01:49.204]v1:<01:49.204>It's <01:49.686><01:49.686>my <01:50.389><01:50.389>last <01:51.045><01:51.045>goodbye<01:53.110><01:53.110> +[01:53.541]v1:<01:53.541>Kill <01:54.238><01:54.238>this <01:55.037><01:55.037>destiny <01:56.615><01:56.615>that's <01:56.965><01:56.965>tryna <01:57.470><01:57.470>to <01:57.757><01:57.757>tell <01:58.127><01:58.127>you <01:58.740><01:58.740>what <01:59.348><01:59.348>you <02:00.084><02:00.084>should <02:00.519><02:00.519>be<02:00.880><02:00.880> +[02:00.880]v1:<02:00.880>Into <02:01.510><02:01.510>the <02:01.822><02:01.822>void <02:02.244><02:02.244>I <02:02.852><02:02.852>scream<02:03.572><02:03.572> +[02:03.572]v1:<02:03.572>Someone <02:03.905><02:03.905>try <02:04.247><02:04.247>and <02:04.616><02:04.616>stop <02:04.912><02:04.912>me<02:05.746><02:05.746> +[02:05.746]v1:<02:05.746>Check <02:05.969><02:05.969>out <02:06.320><02:06.320>my <02:06.690><02:06.690>face<02:07.416><02:07.416> +[02:07.416]v1:<02:07.416>I <02:07.736><02:07.736>got <02:08.426><02:08.426>the <02:08.701><02:08.701>look<02:09.294><02:09.294> +[02:09.294]v1:<02:09.294>Not <02:09.621><02:09.621>gonna <02:10.221><02:10.221>play <02:10.621><02:10.621>by <02:11.220><02:11.220>the <02:11.434><02:11.434>book<02:12.003><02:12.003> +[02:12.003]v1:<02:12.003>This <02:12.299><02:12.299>mic <02:12.666><02:12.666>just <02:13.049><02:13.049>got <02:13.442><02:13.442>turned <02:14.058><02:14.058>on<02:15.617><02:15.617> +[02:15.914]v1:<02:15.914>Kill <02:16.537><02:16.537>this <02:17.330><02:17.330>destiny <02:18.956><02:18.956>that's <02:19.273><02:19.273>tryna <02:19.668><02:19.668>to <02:20.041><02:20.041>tell <02:20.377><02:20.377>you <02:21.113><02:21.113>what <02:21.764><02:21.764>you <02:22.458><02:22.458>should <02:22.793><02:22.793>be<02:23.195><02:23.195> +[02:23.195]v1:<02:23.195>Into <02:23.872><02:23.872>the <02:24.201><02:24.201>void <02:24.705><02:24.705>I <02:25.263><02:25.263>scream<02:25.895><02:25.895> +[02:25.895]v1:<02:25.895>Someone <02:26.249><02:26.249>try <02:26.593><02:26.593>and <02:27.057><02:27.057>stop <02:27.525><02:27.525>me<02:28.185><02:28.185> +[02:28.185]v1:<02:28.185>Check <02:28.442><02:28.442>out <02:28.914><02:28.914>my <02:29.214><02:29.214>face<02:29.743><02:29.743> +[02:29.743]v1:<02:29.743>I <02:30.087><02:30.087>got <02:30.810><02:30.810>the <02:31.073><02:31.073>look<02:31.627><02:31.627> +[02:31.627]v1:<02:31.627>Not <02:31.907><02:31.907>gonna <02:32.523><02:32.523>play <02:32.948><02:32.948>by <02:33.628><02:33.628>the <02:33.859><02:33.859>book<02:34.322><02:34.322> +[02:34.322]v1:<02:34.322>This <02:34.651><02:34.651>mic <02:35.018><02:35.018>just <02:35.355><02:35.355>got <02:35.747><02:35.747>turned <02:36.236><02:36.236>on<02:37.091><02:37.091> +[02:37.091]v1:<02:37.091>Make <02:37.419><02:37.419>your <02:37.939><02:37.939>decision<02:39.763><02:39.763> +[02:39.763]v1:<02:39.763>Dare <02:40.035><02:40.035>to <02:40.283><02:40.283>believe<02:41.547><02:41.547> +[02:41.547]v1:<02:41.547>To <02:41.788><02:41.788>become<02:42.751><02:42.751> +[02:42.751]v1:<02:42.751>To <02:43.003><02:43.003>become<02:44.459><02:44.459> +[02:44.459]v1:<02:44.459>Spotlight <02:45.947><02:45.947>lover<02:47.308><02:47.308> +[02:47.308]v1:<02:47.308>Deepest <02:48.656><02:48.656>cover<02:49.711><02:49.711> +[02:49.711]v1:<02:49.711>If <02:49.999><02:49.999>you <02:50.408><02:50.408>can <02:50.777><02:50.777>dare <02:51.135><02:51.135>to <02:51.463><02:51.463>believe<02:52.680><02:52.680> +[02:52.680]v1:<02:52.680>To <02:52.879><02:52.879>become<02:53.943><02:53.943> +[02:53.943]v1:<02:53.943>To <02:54.183><02:54.183>become<02:55.666><02:55.666> +[02:55.666]v1:<02:55.666>Spotlight <02:57.461><02:57.461>lover<02:58.462><02:58.462> +[02:58.462]v1:<02:58.462>Deepest <02:59.814><02:59.814>cover<03:01.157><03:01.157> +[03:01.157]v1:<03:01.157>Spotlight<03:03.710><03:03.710> \ No newline at end of file diff --git a/test/files/timed_lrc_4.lrc b/test/files/timed_lrc_4.lrc new file mode 100644 index 0000000..f15839d --- /dev/null +++ b/test/files/timed_lrc_4.lrc @@ -0,0 +1,155 @@ +[ti:We Cry Together] +[ar:Kendrick Lamar & Taylour Paige] +[offset:+0] +[by:Generated using SongSync] +[00:03.151]v2:<00:03.151>Oh-<00:03.716>ooh-<00:04.135>oh-<00:04.583>ooh, <00:04.999>whoa <00:07.861> +[00:09.870]v2:<00:09.870>Oh-<00:10.353>ooh-<00:10.769>oh-<00:11.153>ooh, <00:11.569>whoa-<00:14.315>oh <00:15.595> +[00:16.441]v2:<00:16.441>Hold <00:19.454>on<00:20.185>to <00:20.618>each <00:21.050>o<00:21.254>ther <00:22.270> +[00:22.990]v2:<00:22.990>Hold <00:25.691>on<00:26.691>to <00:27.107>each <00:27.526>o<00:27.779>ther <00:28.955> +[00:31.088]v2:<00:31.088>This <00:31.256>is <00:31.421>what <00:31.584>the <00:31.723>world <00:32.005>sounds <00:32.272>like <00:32.595> +[00:31.767]v1:<00:31.767>Nah, <00:32.250>fuck <00:32.601>you, <00:32.818>****! <00:33.330> +[00:33.079]v2:<00:33.079>Fuck <00:33.514>you, <00:33.730>bitch! <00:34.109> +[00:38.799]v1:<00:38.799>You <00:38.999>got <00:39.215>me <00:39.399>fucked <00:39.748>up <00:40.130> +[00:41.950]v1:<00:41.950>Fuck <00:42.283>you, <00:42.858><00:43.771>fuck <00:44.343><00:44.670>you <00:45.220> +[00:45.645]v2:<00:45.645>I <00:45.845>swear <00:46.061>I'm <00:46.261>tired <00:46.534>of <00:46.634>these <00:46.978>emotional <00:47.527>ass, <00:47.861>ungrateful <00:48.343>ass <00:48.694>bitches <00:49.123> +[00:48.540]v1:<00:48.540>Shut <00:48.890>the <00:49.050>fuck <00:49.224>up <00:49.350> +[00:49.360]v2:<00:49.360>Un<00:49.608>stable <00:49.960>ass, <00:50.349><00:50.394>confrontational <00:51.226>ass, <00:51.617><00:51.775>dumb <00:52.092>bitches <00:52.533> +[00:52.570]v2:<00:52.570>You <00:52.786>wanna <00:53.030>bring <00:53.219>a <00:53.319>**** <00:53.701>down, <00:54.089><00:54.218>even <00:54.488>when <00:54.650>I'm <00:54.837>tryna <00:55.021>do <00:55.205>right <00:55.423> +[00:55.669]v2:<00:55.669>We <00:55.818>could <00:55.986>go <00:56.135>our <00:56.303>separate <00:56.618>ways <00:56.885>right <00:57.170>now, <00:57.429><00:57.556>you <00:57.742>could <00:57.921>move <00:58.089>on <00:58.356>with <00:58.521>your <00:58.654>life, <00:58.858><00:58.982>I <00:59.250>swear <00:59.583>to <00:59.751>God <01:00.080> +[00:58.929]v1:<00:58.929>Fuck <00:59.212>you, <00:59.446>****, <00:59.780>you <01:00.012>love <01:00.329>a <01:00.529>pity <01:00.846>party, <01:01.212>I <01:01.452>won't <01:01.671>show <01:01.921>up <01:02.292> +[01:02.396]v1:<01:02.396>Always <01:02.729>act <01:03.012>like <01:03.180>your <01:03.360>shit <01:03.663>don't <01:03.980>stink, <01:04.345>motherfucka, <01:05.144>grow <01:05.428>up <01:05.679> +[01:05.767]v1:<01:05.767>Forever <01:06.167>late <01:06.500>for <01:06.700>shit, <01:06.908><01:06.983>won't <01:07.250>buy <01:07.447>shit, <01:07.768><01:07.889>sit <01:08.121>around <01:08.474>and <01:08.574>deny <01:09.089>shit <01:09.460> +[01:08.960]v2:<01:08.960>Man <01:10.018> +[01:09.488]v1:<01:09.488>Fuck <01:09.760>around <01:10.106>on <01:10.288>a <01:10.472>side <01:10.786>bitch, <01:11.066>then <01:11.249>come <01:11.484>fuckin' <01:11.922>up <01:12.189>my <01:12.483>shit? <01:12.770> +[01:12.447]v2:<01:12.447>What? <01:12.781>Fuckin' <01:13.130>up <01:13.413>yo' <01:13.647>shit? <01:13.944><01:14.204>You <01:14.404>must <01:14.583>be <01:14.788>bleedin' <01:15.102>and <01:15.327>some <01:15.558>more <01:15.755>shit <01:16.084> +[01:16.181]v2:<01:16.181>Bitch, <01:16.455>I <01:16.609>don't <01:16.760>know <01:16.981>shit, <01:17.226><01:17.240>fuck <01:17.565>yo' <01:17.848>feelings <01:18.254> +[01:18.264]v1:<01:18.264>You <01:18.480>on <01:18.664>some <01:18.997>ho <01:19.280>shit <01:19.549> +[01:19.603]v1:<01:19.603>See, <01:19.819>I <01:19.982>don't <01:20.146>know <01:20.336>why <01:20.551>you <01:20.771>like <01:21.003>playin' <01:21.320>mind <01:21.670>games <01:21.952>with <01:22.136>me <01:22.414> +[01:22.245]v2:<01:22.245>Mind <01:22.578>games? <01:22.852>Okay <01:23.269> +[01:22.517]v1:<01:22.517>Bitch, <01:22.834>I <01:23.002>ain't <01:23.250>slow, <01:23.481>nor <01:23.749>ditsy, <01:24.162><01:24.250>I <01:24.424>know <01:24.679>when <01:24.901>you <01:25.117>bein' <01:25.349>distant <01:25.815> +[01:25.825]v1:<01:25.825>I <01:26.116>know <01:26.300>when <01:26.513>you <01:26.748>fake <01:27.014>busy, <01:27.393><01:27.497>get <01:27.748>out <01:27.908>yo' <01:28.168>feelings <01:28.497>and <01:28.699>miss <01:28.985>me <01:29.126>with <01:29.315>that <01:29.473>re<01:29.714>verse <01:30.085>psycholo<01:30.749>gy <01:31.271> +[01:30.679]v2:<01:30.679>Man, <01:31.047>bitch, <01:31.279>you <01:31.479>trippin', <01:31.879>who <01:32.113>got <01:32.345>you <01:32.529>that <01:32.713>Rollie <01:33.063>chain? <01:33.543> +[01:33.335]v1:<01:33.335>And <01:33.503>who <01:33.684>put <01:33.902>that <01:34.170>car <01:34.436>in <01:34.618>my <01:34.868>name? <01:35.215> +[01:34.941]v2:<01:34.941>What, <01:35.120>you <01:35.341>think <01:35.547>I'ma <01:35.845><01:35.940>kiss <01:36.197>yo' <01:36.448>ass? <01:36.771> +[01:36.782]v1:<01:36.782>Nah, <01:37.115>****, <01:37.382>you <01:37.715>fuckin' <01:38.098>lame <01:38.502> +[01:38.252]v2:<01:38.252>You <01:38.463>know <01:38.650>what? <01:38.917>Fuck <01:39.268>you, <01:39.535>bitch <01:39.668> +[01:39.768]v1:<01:39.768>Fuck <01:40.031>you, <01:40.211>****! <01:40.584> +[01:40.594]v2:<01:40.594>Nah, <01:41.078><01:41.140>fuck <01:41.520>you, <01:41.752>bitch! <01:42.181> +[01:41.866]v1:<01:41.866>Nah, <01:42.118>f<01:42.334>uck <01:42.667>you, <01:42.866>****! <01:43.238> +[01:43.045]v2:<01:43.045>Fuck <01:43.446>you, <01:43.728>bitch! <01:44.035> +[01:44.045]v1:<01:44.045>Nah, <01:44.277>f<01:44.513>uck <01:44.686>you, <01:44.920>****! <01:45.438> +[01:45.170]v2:<01:45.170>Nah, <01:45.618><01:45.687>fuck <01:46.003>you, <01:46.235>bitch! <01:46.542> +[01:46.558]v1:<01:46.558>Fuck <01:46.843>you, <01:47.049>****! <01:47.441>Fuck <01:47.774>you, <01:47.929>****! <01:48.313> +[01:46.606]v2:<01:46.606>Fuck <01:46.889>you, <01:47.206>bitch! <01:47.542><01:47.881>Fuck <01:48.163>you, <01:48.419><01:48.497>fuck <01:48.798>you, <01:49.035><01:49.065>fuck <01:49.347>you, <01:49.561><01:49.579>fuck <01:49.913>you, <01:50.167>bitch! <01:50.462> +[01:49.862]v1:<01:49.862>Fuck <01:50.198>you, <01:50.497>****! <01:50.942> +[01:51.161]v2:<01:51.161>Fuck <01:51.509>you, <01:51.761>bitch! <01:52.013> +[01:52.023]v1:<01:52.023>Nah, <01:52.258>f<01:52.407>uck <01:52.706>you! <01:53.100> +[01:53.212]v1:<01:53.212>Wastin' <01:53.530>my <01:53.828>time <01:54.095>and <01:54.330>energy <01:54.762>tryna <01:55.079>be <01:55.330>good <01:55.644>to <01:55.863>you <01:56.140> +[01:56.221]v1:<01:56.221>Lost <01:56.573>friends, <01:57.021>family, <01:57.405>gained <01:57.821>more <01:58.122>enemies <01:58.688>'cause <01:58.955>of <01:59.170>you <01:59.413> +[01:59.482]v1:<01:59.482>Bitches <01:59.866>starin' <02:00.216>at <02:00.482>me <02:00.749>in <02:00.930>Zara, <02:01.355><02:01.501>hoes <02:01.919>scratchin' <02:02.367>my <02:02.653>cars <02:02.885>up <02:03.130> +[02:03.242]v1:<02:03.242>Shoulda <02:03.709>followed <02:04.010>my <02:04.274>mind <02:04.525>in <02:04.709>'09 <02:05.138>and <02:05.303>just <02:05.570>moved <02:05.809>to <02:05.909>Georgia <02:06.524> +[02:05.966]v2:<02:05.966>Oh, <02:06.284>what <02:06.534>that's <02:06.766>my <02:07.033>fault <02:07.284>now? <02:07.553> +[02:07.786]v2:<02:07.786>Bitch, <02:08.103>you <02:08.287>power <02:08.651>trippin' <02:08.970>or <02:09.169>guilt <02:09.487>trippin', <02:09.820>I <02:10.036>held <02:10.303>yo' <02:10.519>ass <02:10.786>down <02:11.266> +[02:10.936]v1:<02:10.936>You <02:11.219>just <02:11.438>kept <02:11.670>me <02:11.846>down, <02:12.168>that's <02:12.387>a <02:12.536>big <02:12.853>difference <02:13.450> +[02:13.460]v1:<02:13.460>Stressin' <02:13.913>myself, <02:14.425>tryna <02:14.793>figure <02:15.209>why <02:15.492>I'm <02:15.708>not <02:15.950>good <02:16.292>enough <02:16.673> +[02:16.846]v1:<02:16.846>Goin' <02:17.179>to <02:17.364>church, <02:17.795>prayin' <02:18.179>for <02:18.411>you, <02:18.653><02:18.713>searchin' <02:19.137>for <02:19.353>good <02:19.585>in <02:19.737>us <02:20.116> +[02:20.214]v1:<02:20.214>Lil' <02:20.614>dick-<02:20.978>ass <02:21.296>**** <02:21.696>that's <02:21.947>tryna <02:22.262>go <02:22.528>big <02:22.944> +[02:22.947]v2:<02:22.947>But <02:23.130>you <02:23.332>were <02:23.518>s<02:23.618>uckin' <02:23.998>this <02:24.198>dick, <02:24.451>though <02:24.799> +[02:24.578]v1:<02:24.578>Well, <02:24.797>shit, <02:25.045>I <02:25.260>shoulda <02:25.647>sucked <02:26.045>his <02:26.541> +[02:26.341]v2:<02:26.341>What <02:26.509>you <02:26.609>say? <02:27.029> +[02:27.029]v1:<02:27.029>I <02:27.277>shoulda <02:27.643>found <02:28.011>a <02:28.195>bigger <02:28.576>dick! <02:29.001> +[02:28.636]v2:<02:28.636>Bitch, <02:28.953>get <02:29.137>the <02:29.319>fuck <02:29.569>out <02:29.769>my <02:29.969>face! <02:30.353> +[02:30.419]v1:<02:30.419>Oh, <02:30.600>what, <02:30.901>you <02:31.072>mad? <02:31.394> +[02:31.309]v2:<02:31.309>Shut <02:31.560>up, <02:31.858>bitch! <02:32.155>You <02:32.372>got <02:32.535>me <02:32.699>fucked <02:33.035>up <02:33.293>today, <02:33.671>on <02:33.903>God! <02:34.531> +[02:33.841]v1:<02:33.841>Ah-<02:34.142>ha, <02:34.388>you <02:34.616>mad, <02:34.924>lil' <02:35.276>feelings <02:35.677>is <02:35.892>shot <02:36.249> +[02:36.385]v1:<02:36.385>Go <02:36.603>text <02:36.887>that <02:37.137>raggedy <02:37.569>bitch <02:37.918>and <02:38.031>tell <02:38.245>her <02:38.436>you <02:38.688>all <02:38.848>that <02:38.991>she <02:39.337>got <02:39.585> +[02:39.405]v2:<02:39.405>Man, <02:39.755>what <02:40.123>bitch? <02:40.517> +[02:39.751]v1:<02:39.751>Let <02:39.966>her <02:40.098>know <02:40.316>you <02:40.442>packin' <02:40.884>yo' <02:41.084>shit <02:41.426><02:41.495>and <02:41.676>gotta <02:41.925>move <02:42.191>by <02:42.380>the <02:42.584>first <02:42.911> +[02:42.817]v2:<02:42.817>Man, <02:43.053>give <02:43.194>me <02:43.293>these <02:43.442>fuckin' <02:43.751>keys, <02:44.050>dawg <02:44.191> +[02:44.201]v1:<02:44.201>Give <02:44.367>me <02:44.551>my <02:44.751>keys, <02:45.178>I'ma <02:45.433>be <02:45.700>late <02:45.951>for <02:46.151>work <02:46.430> +[02:46.225]v2:<02:46.225>Fuck <02:46.546>yo' <02:46.758>job, <02:47.126>today <02:47.535>gon' <02:47.852>be <02:48.002>the <02:48.151>day <02:48.402>you <02:48.634>walk <02:48.918>to <02:49.084>that <02:49.349>bitch <02:49.692> +[02:49.746]v1:<02:49.746>Give <02:49.962>me <02:50.112>my <02:50.312>fuckin' <02:50.813>keys <02:51.357> +[02:51.024]v2:<02:51.024>Nah, <02:51.291>I <02:51.608>like <02:51.875>you <02:52.109>parked <02:52.370>in <02:52.586>that <02:52.817>bitch <02:53.200> +[02:53.214]v1:<02:53.214>Give <02:53.397>me <02:53.579>my <02:53.744>keys, <02:54.289>bro <02:54.726> +[02:54.321]v2:<02:54.321>On <02:54.771>God, <02:55.144>you <02:55.321>ain't <02:55.521>gettin' <02:55.755>these <02:56.041>keys <02:56.475> +[02:56.497]v1:<02:56.497>Give <02:56.697>me <02:56.881>my <02:57.110>fuckin' <02:57.630>keys! <02:58.225> +[02:57.894]v2:<02:57.894>Ha, <02:58.380>now <02:58.713>you <02:58.932>mad <02:59.214>at <02:59.446>me, <02:59.841>I <03:00.028>got <03:00.246>you <03:00.510>hollerin' <03:00.860>for <03:01.073>nothin' <03:01.481> +[03:01.494]v1:<03:01.494>I <03:01.678>do <03:01.894>the <03:02.094>same <03:02.377>when <03:02.577>we <03:02.811>fuckin' <03:03.355> +[03:03.127]v2:<03:03.127>Act <03:03.432>like <03:03.624>that <03:03.828>pussy <03:04.289>ain't <03:04.594>loose <03:04.925> +[03:04.808]v1:<03:04.808>I'd <03:05.008>rather <03:05.408>act <03:05.675>like <03:05.881>I'm <03:06.072>cummin' <03:06.637> +[03:06.345]v2:<03:06.345>I'd <03:06.652>rather <03:07.116>fuck <03:07.428>off <03:07.663>the <03:07.780>juice <03:08.108> +[03:08.118]v1:<03:08.118>I'd <03:08.392>rather <03:08.818>fuck <03:09.200>on <03:09.304>yo' <03:09.465>cousin <03:09.976> +[03:09.891]v2:<03:09.891>Bitch, <03:10.129>you <03:10.260>said <03:10.491>you <03:10.679>gon' <03:10.860>fuck <03:11.101>who? <03:11.492> +[03:11.494]v1:<03:11.494>You <03:11.694>heard <03:11.878>me, <03:12.176>****, <03:12.579>it's <03:12.846>nothin' <03:13.417> +[03:12.340]v2:<03:12.340>A'ight <03:12.870> +[03:13.219]v2:<03:13.219>You <03:13.385>know <03:13.548>what? <03:13.822><03:13.912>Fuck <03:14.174>you, <03:14.406>bitch! <03:14.808> +[03:13.785]v1:<03:13.785>Fuck <03:14.036>you, <03:14.302>****! <03:14.742> +[03:15.111]v2:<03:15.111>Fuck <03:15.429>you, <03:15.711>bitch! <03:16.122> +[03:15.790]v1:<03:15.790>Nah, <03:16.209>fuck <03:16.542>you, <03:16.792>****! <03:17.337> +[03:16.716]v2:<03:16.716>No, <03:17.065><03:17.268>fuck <03:17.516>you, <03:17.860>bitch! <03:18.156> +[03:18.156]v1:<03:18.156>Nah, <03:18.556>fuck <03:18.873>you, <03:19.063>****! <03:19.505> +[03:19.228]v2:<03:19.228>Fuck <03:19.561>you, <03:19.844>bitch! <03:20.172> +[03:19.694]v1:<03:19.694>Fuck <03:19.977>you! <03:20.267>I'm <03:20.451>sick <03:20.619>of <03:20.803>this <03:20.969>**** <03:21.233> +[03:20.406]v2:<03:20.406>Fuck <03:20.704>you, <03:21.022>bitch! <03:21.227> +[03:21.237]v1:<03:21.237>Fuck <03:21.539>you! <03:21.856>Fuck <03:22.103>you! <03:22.667><03:22.819>Fuck <03:23.136>you! <03:23.421> +[03:21.719]v2:<03:21.719>Fuck <03:22.001>you, <03:22.268>fuck <03:22.503>you, <03:22.699>fuck <03:22.942>you, <03:23.150>fuck <03:23.397>you, <03:23.594>bi- <03:23.852> +[03:23.989]v2:<03:23.989>Fuck <03:24.204>you, <03:24.472>bitch! <03:24.888> +[03:25.225]v2:<03:25.225>Stupid <03:25.807>ass <03:26.276>bitch <03:26.657> +[03:26.719]v2:<03:26.719>I <03:26.938>don't <03:27.154>even <03:27.453>know <03:27.703>why <03:27.903>I <03:28.119>fuck <03:28.322>with <03:28.535>you <03:28.736> +[03:28.746]v1:<03:28.746>I'll <03:29.042>be <03:29.242>damned <03:29.541>if <03:29.709>I <03:29.893>stuck <03:30.242>with <03:30.426>you <03:30.738> +[03:30.279]v2:<03:30.279>Changed <03:30.746>my <03:30.994>number, <03:31.383>I'm <03:31.628>duckin' <03:32.012>you, <03:32.279>bitch <03:32.623> +[03:32.105]v1:<03:32.105>Bitch, <03:32.505>whatever <03:32.953>is <03:33.121>comfortable <03:33.878> +[03:33.878]v2:<03:33.878>This <03:34.062>the <03:34.195>type <03:34.454>of <03:34.554>shit <03:34.837>couples <03:35.278>do? <03:35.547>Shoulda <03:35.846>thought <03:36.081>about <03:36.441><03:36.493>cuffin' <03:36.928>you, <03:37.144>bitch <03:37.642> +[03:37.168]v1:<03:37.168>****, <03:37.466>you <03:37.769>dirty <03:38.086>and <03:38.369>you <03:38.686>broke <03:39.172> +[03:38.768]v2:<03:38.768>Ho, <03:39.066>you <03:39.317>goofy <03:39.733>and <03:40.000>gullible, <03:40.538><03:40.611>fuck <03:40.877>you <03:41.144>talkin' <03:41.411>'bout? <03:41.840> +[03:40.086]v1:<03:40.086>Mmm, <03:40.436>the <03:40.665>insecurities <03:41.746>you <03:41.918>got <03:42.178>won't <03:42.451>mind-<03:42.750>fuck <03:43.051>me <03:43.358> +[03:43.469]v1:<03:43.469>Womanizer, <03:44.285>got <03:44.536>no <03:44.802>affection <03:45.250>from <03:45.485>yo' <03:45.752>momma, <03:46.191>I <03:46.425>see <03:46.724> +[03:46.482]v2:<03:46.482>Don't <03:46.691>speak <03:46.949>on <03:47.094>my <03:47.265>momma, <03:47.623>the <03:47.789>fuck <03:48.106>is <03:48.271>yo' <03:48.454>problem? <03:48.868> +[03:48.878]v1:<03:48.878>That <03:49.129>bitch <03:49.396>don't <03:49.579>like <03:49.827>me <03:50.039>anyways <03:50.732> +[03:50.289]v2:<03:50.289>Bitch, <03:50.543>she <03:50.800>gave <03:51.061>you <03:51.197>the <03:51.358>Hon<03:51.558>da <03:51.783> +[03:51.825]v1:<03:51.825>And <03:52.041>used <03:52.241>that <03:52.524>shit <03:52.758>to <03:52.958>throw <03:53.124>it <03:53.292>in <03:53.441>my <03:53.692>face <03:54.121> +[03:53.783]v2:<03:53.783>Man, <03:54.119>hol' <03:54.300>on, <03:54.583>bro <03:55.103> +[03:54.153]v1:<03:54.153>Find <03:54.385>it <03:54.585>funny <03:54.918>you <03:55.201>just <03:55.452>can't <03:55.719>apologize <03:56.731> +[03:56.741]v2:<03:56.741>Bitch, <03:57.049>dawg <03:57.380> +[03:57.024]v1:<03:57.024>Egotistic, <03:57.875>narcissistic, <03:58.707>love <03:59.008>your <03:59.256>own <03:59.624>lies <03:59.954> +[04:00.032]v2:<04:00.032>Bro <04:00.303> +[04:00.313]v1:<04:00.313>See <04:00.548>you <04:00.732>the <04:00.881>reason <04:01.249>why <04:01.548>strong <04:01.814>women <04:02.081>fucked <04:02.398>up! <04:02.753> +[04:02.863]v1:<04:02.863>Why <04:03.066>they <04:03.282>say <04:03.466>it's <04:03.615>a <04:03.815>man's <04:04.164>world, <04:04.506><04:04.615>see <04:04.831>you <04:04.999>the <04:05.164>reason <04:05.450>for <04:05.666>Trump <04:05.994> +[04:05.786]v2:<04:05.786>You <04:06.002>crying, <04:06.292>bro? <04:06.749> +[04:06.031]v1:<04:06.031>You <04:06.316>the <04:06.516>reason, <04:06.922><04:06.999>we <04:07.226>overlooked, <04:07.900><04:08.015>underpaid, <04:08.756><04:08.898>under<04:09.266>booked, <04:09.642><04:09.764>under <04:10.082>shame <04:10.509> +[04:10.632]v1:<04:10.632>If <04:10.784>you <04:10.984>look, <04:11.274><04:11.412>I <04:11.512>don't <04:11.689>speak, <04:12.080><04:12.277>then <04:12.429>I'm <04:12.587>called <04:12.939><04:13.120>out <04:13.261>my <04:13.429>name <04:13.821> +[04:13.953]v1:<04:13.953>I <04:14.171>am <04:14.353>flawed, <04:14.745><04:14.870>I <04:15.054>am <04:15.164>pained, <04:15.590><04:15.689>never <04:15.934>yours, <04:16.462><04:16.522>I <04:16.701>remained <04:17.244> +[04:17.353]v1:<04:17.353>You <04:17.540>the <04:17.665>reason <04:18.071>bitches <04:18.380>start <04:18.755>fuckin' <04:19.048>with <04:19.281>bitches <04:19.665>when <04:19.916>they <04:20.132>change <04:20.495> +[04:20.593]v1:<04:20.593>You <04:20.793>the <04:21.009>reason <04:21.358>bitches <04:21.755>start <04:22.094>callin' <04:22.387>y'all <04:22.694>bitches, <04:23.010>'cause <04:23.273>y'all <04:23.461>useless <04:23.934> +[04:23.977]v1:<04:23.977>You <04:24.259>the <04:24.429>reason <04:24.764>Harvey <04:25.119>Weinstein <04:25.774>had <04:25.974>to <04:26.193>see <04:26.425>his <04:26.570>conclusion <04:27.303> +[04:27.312]v1:<04:27.312>You <04:27.496>the <04:27.731>reason <04:28.096>R. <04:28.363>Kelly <04:28.691>can't <04:28.928>recognize <04:29.505>that <04:29.744>he's <04:29.963>abusive <04:30.650> +[04:30.421]v2:<04:30.421>Man, <04:30.662>shut <04:30.923>the <04:31.139>fuck <04:31.424>up, <04:31.630>we <04:31.872>all <04:32.056>know <04:32.323>you <04:32.507>still <04:32.872>playin' <04:33.291>his <04:33.482>music <04:34.000> +[04:34.014]v2:<04:34.014>Said, <04:34.271>I'm <04:34.414>tired <04:34.716>of <04:34.865>these <04:35.121>emotional <04:35.670>ass, <04:36.121>ungrateful <04:36.622>ass <04:36.921>bitches <04:37.492> +[04:37.554]v2:<04:37.554>Fake <04:37.855>innocent, <04:38.508><04:38.663>fake <04:39.031>feminist, <04:39.783><04:39.817>stop <04:40.151>pretendin' <04:40.761> +[04:40.818]v2:<04:40.818>Y'all <04:41.148>sentiments <04:41.847><04:42.053>ain't <04:42.437>realer <04:42.821>than <04:43.068><04:43.149>what <04:43.349>you <04:43.533>defendin' <04:44.143> +[04:43.566]v1:<04:43.566>Here <04:43.848>he <04:44.064>go, <04:44.766>shut <04:45.082>the <04:45.232>fuck <04:45.476>up, <04:45.812><04:45.816>look <04:46.080>at <04:46.262>you, <04:46.569><04:46.696>look <04:46.944>at <04:47.211>you! <04:47.692> +[04:44.235]v2:<04:44.235>It's <04:44.370>a <04:44.470>split <04:44.676>decision, <04:45.350><04:45.483>broads <04:45.734>like <04:46.017>you <04:46.406><04:46.457>and <04:46.739>real <04:47.022>victims <04:47.585> +[04:47.582]v2:<04:47.582>Let's <04:47.881>talk <04:48.113>the <04:48.223>truth, <04:48.659><04:48.859>women <04:49.236>in <04:49.391>general <04:49.821>just <04:50.137>can't <04:50.470>get <04:50.718>along <04:51.217> +[04:48.574]v1:<04:48.574>Okay, <04:49.107><04:50.898>explain, <04:51.531>**** <04:52.009> +[04:51.641]v2:<04:51.641>Hmm, <04:52.041>when <04:52.241>Tash <04:52.524>got <04:52.692>a <04:52.841>man, <04:53.092><04:53.201>you <04:53.385>didn't <04:53.668>pick <04:53.902>up <04:54.068>the <04:54.217>phone <04:54.513> +[04:54.298]v1:<04:54.298>Explain, <04:54.898>****? <04:55.332> +[04:54.837]v2:<04:54.837>Uh-<04:55.085>huh, <04:55.437>when <04:55.634>Nate <04:55.869>got <04:56.018>a <04:56.170>job, <04:56.392><04:56.519>you <04:56.748>said <04:56.922>you <04:57.133>stayin' <04:57.430>home <04:57.700> +[04:57.710]v1:<04:57.710>Explain <04:58.466> +[04:58.287]v2:<04:58.287>Why <04:58.503>R&B <04:58.956>bitches <04:59.351>don't <04:59.636>feature <05:00.020>on <05:00.236>each <05:00.436>other <05:00.669>songs, <05:01.105>then? <05:01.484> +[05:00.864]v1:<05:00.864>What <05:01.066>the <05:01.298>fuck <05:01.549>is <05:01.749>you <05:01.965>talkin' <05:02.349>'bout? <05:02.649> +[05:02.659]v2:<05:02.659>Nevermind, <05:03.360>bitch, <05:03.627>I'm <05:03.787>walkin' <05:04.160>out <05:04.371> +[05:04.381]v1:<05:04.381>Whatever, <05:04.848>****, <05:05.283>I'm <05:05.499>off <05:05.781>you <05:05.960>now <05:06.413> +[05:05.850]v2:<05:05.850>Yo' <05:06.183>evil <05:06.516>ass <05:06.799>kept <05:07.034>me <05:07.250>well <05:07.482>in <05:07.700>doubt <05:08.060> +[05:07.730]v1:<05:07.730>Pussy <05:08.162>****, <05:08.546>best <05:08.781>watch <05:09.029>your <05:09.191>mouth <05:09.485> +[05:09.495]v2:<05:09.495>Pussy <05:09.879>and <05:10.042>mouth <05:10.378>is <05:10.562>all <05:10.794>you <05:10.978>got <05:11.322> +[05:11.132]v1:<05:11.132>Lay <05:11.449>this <05:11.676>pussy <05:12.065>back <05:12.348>on <05:12.548>the <05:12.732>couch <05:13.521> +[05:12.729]v2:<05:12.729>Doggie <05:13.096>style, <05:13.526>then <05:13.780>you <05:13.980>get <05:14.145>on <05:14.257>top <05:14.617> +[05:14.627]v1:<05:14.627>Fuck <05:14.894>me, <05:15.184>**** <05:15.569> +[05:15.579]v2:<05:15.579>I'ma <05:15.913>fuck <05:16.230>you, <05:16.448>bitch <05:16.832> +[05:16.843]v1:<05:16.843>Nah, <05:17.185>fuck <05:17.500>me, <05:17.832>****, <05:18.115>fuck <05:18.472>me <05:19.228> +[05:18.889]v1:<05:18.889>I'ma <05:19.270>fuck <05:19.604>you, <05:19.870>bitch <05:20.425> +[05:19.906]v1:<05:19.906>Nah, <05:20.239>fuck <05:20.506>you, <05:20.738>fuck <05:21.013>me <05:21.402> +[05:21.412]v2:<05:21.412>You <05:21.713>playin', <05:21.960>man <05:22.367> +[05:22.450]v1:<05:22.450>Fuck <05:22.866>me <05:23.511> +[05:23.980]v1:<05:23.980>Nah, <05:24.313>you <05:24.580>playin' <05:25.057> +[05:38.801]v2:<05:38.801>Stop <05:39.185>tap <05:39.503>dancing <05:39.969>around <05:40.284>the <05:40.432>conversation <05:41.274> \ No newline at end of file diff --git a/test/files/timed_lrc_5.lrc b/test/files/timed_lrc_5.lrc new file mode 100644 index 0000000..a02a161 --- /dev/null +++ b/test/files/timed_lrc_5.lrc @@ -0,0 +1,115 @@ +[by:Mercury-xly] +[00:00.00]作词 : Khoa Pham +[00:01.00]作曲 : Khoa Pham +[00:20.89]I'm +[00:20.89]我 +[00:24.74]Mesmerized +[00:24.74]已然沉醉 +[00:29.91]By you +[00:29.91]为你 +[00:35.16]Your eyes +[00:35.16]彻底沦陷 +[00:40.38]Now I can't stop +[00:40.38]如今我已无法自拔 +[00:44.16]Dreaming of you +[00:44.16]沉溺于与你同在的梦境 +[00:50.90]But it hurts to know +[00:50.90]然而,痛彻心扉的,却是 +[00:54.47]I'll wake up without you +[00:54.47]我终将醒来,身边却没有你 +[01:00.08]And this dissonance is killing me +[01:00.08]这份失衡感将我吞噬 +[01:11.73]Drowning in a sea of broken dreams +[01:11.73]沉沦于逐渐消散幻梦的深渊 +[01:22.19]This pain I buried is surfacing +[01:22.19]我深埋的痛楚正破土而出 +[01:32.50]A ghost of who I used to be +[01:32.50]如同昔日的幽魂 +[01:42.74]I'm falling +[01:42.74]我正坠入 +[01:46.96]Apart +[01:46.96]逐渐消散 +[01:53.09]From chasing +[01:53.09]为了追逐 +[01:57.33]Your heart +[01:57.33]你的心 +[02:03.67]I'm falling +[02:03.67]我正坠入 +[02:07.88]Apart +[02:07.88]逐渐消散 +[02:13.92]From chasing +[02:13.92]为了追逐 +[02:18.21]Your heart +[02:18.21]你的心 +[02:44.46]And this dissonance is killing me +[02:44.46]这份失衡感将我吞噬 +[02:56.04]Drowning in a sea of broken dreams +[02:56.04]沉沦于逐渐消散幻梦的深渊 +[03:06.46]This pain I buried is surfacing +[03:06.46]我深埋的痛楚正破土而出 +[03:16.84]A ghost of who I used to be +[03:16.84]如同昔日的幽魂 +[03:26.93]I'm falling +[03:26.93]我正坠入 +[03:31.38]Apart +[03:31.38]逐渐消散 +[03:37.16]From chasing +[03:37.16]为了追逐 +[03:41.77]Your heart +[03:41.77]你的心 +[03:49.61]You +[03:49.61]你 +[03:53.47]Were the song +[03:53.47]曾是那首歌 +[03:58.73]That kept +[03:58.73]予我 +[04:03.94]Me strong +[04:03.94]力量 +[04:08.74]You saw the storm that I +[04:08.74]你看到我那场 +[04:12.52]Was hiding deep inside +[04:12.52]深藏内心的风暴 +[04:19.17]I cursed the world yet you +[04:19.17]尽管我诅咒世界,可你 +[04:23.03]Would stay by my side +[04:23.03]却依然在我身边 +[04:28.66]Was it cruel how our lives entwined? +[04:28.66]命运的纠缠,是否太过于残酷? +[04:40.43]Just to leave deeper scars behind +[04:40.43]只为留下,更深的刻骨伤痕 +[04:50.75]All the happiness was a fleeting dream +[04:50.75]所有幸福,不过是南柯一梦 +[05:01.25]All the promises that we failed to keep +[05:01.25]所有诺言,终究都未能兑现 +[05:11.41]I'm falling +[05:11.41]我正坠入 +[05:15.70]Apart +[05:15.70]逐渐消散 +[05:21.75]From chasing +[05:21.75]为了追逐 +[05:26.17]Your heart +[05:26.17]你的心 +[05:52.26]And this dissonance is killing me +[05:52.26]这份失衡感将我吞噬 +[06:03.88]Drowning in a sea of broken dreams +[06:03.88]沉沦于逐渐消散幻梦的深渊 +[06:14.35]This pain I buried is surfacing +[06:14.35]我深埋的痛楚正破土而出 +[06:24.76]A ghost of who I used to be +[06:24.76]如同昔日的幽魂 +[06:34.94]I'm falling +[06:34.94]我正坠入 +[06:39.24]Apart +[06:39.24]逐渐消散 +[06:45.34]From chasing +[06:45.34]为了追逐 +[06:49.60]Your heart +[06:49.60]你的心 +[07:16.65]I'm falling +[07:16.65]我正坠入 +[07:20.94]Apart +[07:20.94]逐渐消散 +[07:26.95]From chasing +[07:26.95]为了追逐 +[07:31.35]Your heart +[07:31.35]你的心 \ No newline at end of file diff --git a/test/files/timed_lrc_6.lrc b/test/files/timed_lrc_6.lrc new file mode 100644 index 0000000..b54b2c8 --- /dev/null +++ b/test/files/timed_lrc_6.lrc @@ -0,0 +1,46 @@ +[ti:Cigarettes out the Window (1)] +[ar:TV Girl] +[al:Who Really Cares] +[la:EN] +[re:LRCgenerator.com] +[ve:4.00] + +[00:08.50]Cigarettes, <00:10.71>cigarettes <00:11.90>out <00:12.36>the <00:12.80>window +[00:17.03]Cigarettes, <00:19.14>cigarettes <00:20.30>out <00:20.88>the <00:21.34>window +[00:25.50]My <00:25.96>girl <00:26.48>Liddy <00:26.96>used <00:27.48>to <00:27.76>always <00:28.15>smoke +[00:29.61]Cigarettes <00:30.60>when <00:31.09>she <00:31.66>couldn't <00:32.87>sleep +[00:33.83]She'd <00:34.11>disappear <00:35.42>for <00:35.70>an <00:36.13>hour <00:36.60>and <00:36.82>a half +[00:37.79]And <00:38.05>when <00:38.72>she'd <00:39.05>come <00:39.63>back, <00:40.11>she'd <00:40.51>brush <00:40.95>her <00:41.35>teeth +[00:42.13]But <00:42.40>I could <00:42.70>still <00:43.15>smell <00:43.50>it <00:44.02>on <00:44.25>her <00:44.50>raggedy <00:44.69>tee +[00:45.74]And <00:46.03>I could <00:46.44>taste <00:47.21>it <00:47.49>on <00:47.77>her <00:48.00>lips <00:48.32>when <00:48.52>we <00:48.85>kissed +[00:50.85]Poor <00:51.37>little <00:51.96>Liddy <00:52.48>used <00:52.77>to <00:53.06>always <00:54.00>quit +[00:54.70]But <00:54.97>she <00:55.24>never <00:55.53>really <00:55.92>quit, <00:56.52>she'd <00:56.81>just <00:57.27>say <00:57.67>she <00:57.95>did +[00:59.36]Cigarettes, <01:01.38>cigarettes <01:03.18>out <01:03.44>the <01:03.71>window (oh-oh) +[01:03.93]Cigarettes, <01:08.04>cigarettes <01:11.07>out <01:11.90>the <01:12.21>window (oh-oh) +[01:16.28]Cigarettes, <01:18.63>cigarettes <01:19.64>out <01:20.06>the <01:20.46>window (oh-oh) +[01:24.72]Cigarettes, <01:26.98>cigarettes <01:27.94>out <01:28.45>the <01:28.87>window (oh-oh) +[01:33.14]My <01:33.68>girl <01:34.20>Liddy <01:34.74>used <01:35.04>to <01:35.42>always <01:36.35>smoke +[01:37.35]Cigarettes <01:38.32>when <01:38.80>she <01:39.40>couldn't <01:40.54>sleep +[01:41.36]I <01:41.63>wonder <01:41.93>what <01:42.42>she <01:42.71>did <01:43.20>when <01:43.68>she <01:44.22>got <01:44.70>done +[01:45.45]I <01:45.88>guess <01:46.32>she'd <01:46.56>just <01:47.26>flick <01:47.64>them <01:48.02>out <01:48.42>in <01:48.62>the <01:48.80>street +[01:49.72]Poor <01:50.33>little <01:50.68>Liddy, <01:51.76>she <01:52.13>wishes <01:52.56>it <01:52.92>was <01:53.31>dark +[01:53.88]But <01:54.13>it's <01:54.38>never <01:54.71>really <01:55.05>dark <01:55.41>in <01:55.78>L.A +[01:58.49]The <01:58.69>light <01:59.11>from <01:59.30>the <01:59.65>billboard <02:00.15>always <02:01.55>shines +[02:02.46]But <02:02.75>it <02:03.00>changed <02:03.52>12 <02:03.88>times <02:04.34>since <02:04.63>you <02:04.95>went <02:05.41>away +[02:07.01]Cigarettes, <02:09.16>cigarettes <02:10.28>out <02:10.83>the <02:11.22>window (oh-oh) +[02:15.45]Cigarettes, <02:17.60>cigarettes <02:18.57>out <02:19.07>the <02:19.57>window (oh-oh) +[02:23.89]Cigarettes, <02:26.04>cigarettes <02:28.16>out <02:28.40>the <02:28.73>window (oh-oh) +[02:28.96]Cigarettes, <02:34.43>cigarettes <02:35.60>out <02:36.04>the <02:36.49>window (oh-oh) +[02:41.06]We'll <02:41.38>find <02:41.67>moonlit <02:42.06>nights <02:43.23>strangely <02:43.62>empty +[02:45.02]Because <02:45.24>when <02:45.71>you <02:45.95>call <02:46.22>my <02:46.44>name <02:46.63>through <02:46.87>them, <02:47.08>there'll <02:47.32>be <02:47.53>no <02:47.74>answer +[02:48.17]Rather <02:48.33>melodramatic, <02:48.52>aren't <02:48.93>you? +[02:49.14]We'll <02:49.80>find <02:50.07>moonlit <02:50.31>nights <02:51.25>strangely <02:51.71>empty +[02:53.03]Because <02:53.21>when <02:53.47>you <02:53.79>call <02:53.97>my <02:54.18>name <02:54.46>through <02:54.75>them, <02:55.12>there'll <02:55.30>be <02:55.48>no <02:55.71>answer +[02:55.89]You <02:56.53>mind <02:56.74>if <02:56.93>I smoke? +[02:57.16]We'll <02:58.08>find <02:58.35>moonlit <02:59.00>nights <02:59.21>strangely <02:59.38>empty +[03:01.18]Because <03:01.91>when <03:02.13>you <03:02.40>call <03:02.57>my <03:02.72>name <03:02.92>through <03:03.22>them, <03:03.71>there'll <03:03.99>be <03:04.23>no <03:04.46>answer (oh-oh) +[03:04.63]I <03:06.49>always <03:08.37>smoke <03:08.52>in <03:08.69>the <03:08.87>theatre +[03:09.05]We'll <03:09.23>find <03:09.40>moonlit <03:09.60>nights <03:09.76>strangely <03:09.94>empty +[03:10.14]Because <03:10.89>when <03:11.08>you <03:11.28>call <03:11.45>my <03:11.71>name <03:12.04>through <03:14.05>them, <03:14.26>there'll <03:14.45>be <03:14.67>no <03:14.86>answer (oh-oh) + +[03:15.33] \ No newline at end of file diff --git a/test/files/timed_lrc_7_utf16_le_bom.lrc b/test/files/timed_lrc_7_utf16_le_bom.lrc new file mode 100644 index 0000000..ce270b3 Binary files /dev/null and b/test/files/timed_lrc_7_utf16_le_bom.lrc differ diff --git a/test/files/ttml_lrc.xml b/test/files/ttml_lrc.xml new file mode 100644 index 0000000..a936c60 --- /dev/null +++ b/test/files/ttml_lrc.xml @@ -0,0 +1,84 @@ + + + + + + + +