parent
03bbdcf160
commit
3c1c4e48e2
@ -0,0 +1,18 @@ |
||||
package com.frank.ffmpeg.model |
||||
|
||||
import java.util.ArrayList |
||||
|
||||
|
||||
class LrcInfo { |
||||
|
||||
var title: String? = null |
||||
var album: String? = null |
||||
var artist: String? = null |
||||
var author: String? = null |
||||
var creator: String? = null |
||||
var encoder: String? = null |
||||
var version: String? = null |
||||
var offset: Int = 0 |
||||
|
||||
var lrcLineList: ArrayList<LrcLine>? = null |
||||
} |
@ -0,0 +1,152 @@ |
||||
package com.frank.ffmpeg.tool |
||||
|
||||
import com.frank.ffmpeg.model.LrcInfo |
||||
import com.frank.ffmpeg.model.LrcLine |
||||
import com.frank.ffmpeg.util.TimeUtil |
||||
|
||||
import java.io.BufferedInputStream |
||||
import java.io.BufferedReader |
||||
import java.io.FileInputStream |
||||
import java.io.IOException |
||||
import java.io.InputStream |
||||
import java.io.InputStreamReader |
||||
import java.util.* |
||||
import java.util.regex.Pattern |
||||
|
||||
/** |
||||
* Parse lrc format of lyrics |
||||
*/ |
||||
class LrcParser { |
||||
|
||||
private val lrcInfo = LrcInfo() |
||||
|
||||
private val lrcLineList = ArrayList<LrcLine>() |
||||
|
||||
fun readLrc(path: String): LrcInfo? { |
||||
var inputStream: InputStream? = null |
||||
var inputStreamReader: InputStreamReader? = null |
||||
try { |
||||
var charsetName: String? = getCharsetName(path) |
||||
inputStream = FileInputStream(path) |
||||
if (charsetName!!.toLowerCase(Locale.getDefault()) == "utf-8") { |
||||
inputStream = UnicodeInputStream(inputStream, charsetName) |
||||
charsetName = inputStream.getEncoding() |
||||
} |
||||
inputStreamReader = if (!charsetName.isNullOrEmpty()) { |
||||
InputStreamReader(inputStream, charsetName) |
||||
} else { |
||||
InputStreamReader(inputStream) |
||||
} |
||||
val reader = BufferedReader(inputStreamReader) |
||||
while (true) { |
||||
val str: String? = reader.readLine() ?: break |
||||
if (!str.isNullOrEmpty()) { |
||||
decodeLine(str) |
||||
} |
||||
} |
||||
lrcInfo.lrcLineList = lrcLineList |
||||
return lrcInfo |
||||
} catch (e: IOException) { |
||||
return null |
||||
} finally { |
||||
try { |
||||
inputStreamReader?.close() |
||||
inputStream?.close() |
||||
} catch (e: IOException) { |
||||
e.printStackTrace() |
||||
} |
||||
|
||||
} |
||||
} |
||||
|
||||
private fun getCharsetName(path: String): String { |
||||
var code = "GBK" |
||||
var bin: BufferedInputStream? = null |
||||
try { |
||||
bin = BufferedInputStream(FileInputStream(path)) |
||||
when ((bin.read() shl 8) + bin.read()) { |
||||
0xefbb -> code = "UTF-8" |
||||
0xfffe -> code = "Unicode" |
||||
0xfeff -> code = "UTF-16BE" |
||||
else -> { |
||||
} |
||||
} |
||||
} catch (e: Exception) { |
||||
e.printStackTrace() |
||||
} finally { |
||||
if (bin != null) { |
||||
try { |
||||
bin.close() |
||||
} catch (e: IOException) { |
||||
e.printStackTrace() |
||||
} |
||||
|
||||
} |
||||
} |
||||
return code |
||||
} |
||||
|
||||
private fun match(line: String): Boolean { |
||||
return line.length > 4 && line.lastIndexOf("]") > 4 |
||||
} |
||||
|
||||
private fun decodeLine(str: String) { |
||||
if (str.startsWith("[ti:")) { |
||||
if (match(str)) |
||||
lrcInfo.title = str.substring(4, str.lastIndexOf("]")) |
||||
} else if (str.startsWith("[ar:")) { |
||||
if (match(str)) |
||||
lrcInfo.artist = str.substring(4, str.lastIndexOf("]")) |
||||
} else if (str.startsWith("[al:")) { |
||||
if (match(str)) |
||||
lrcInfo.album = str.substring(4, str.lastIndexOf("]")) |
||||
} else if (str.startsWith("[au:")) { |
||||
if (match(str)) |
||||
lrcInfo.author = str.substring(4, str.lastIndexOf("]")) |
||||
} else if (str.startsWith("[by:")) { |
||||
if (match(str)) |
||||
lrcInfo.creator = str.substring(4, str.lastIndexOf("]")) |
||||
} else if (str.startsWith("[re:")) { |
||||
if (match(str)) |
||||
lrcInfo.encoder = str.substring(4, str.lastIndexOf("]")) |
||||
} else if (str.startsWith("[ve:")) { |
||||
if (match(str)) |
||||
lrcInfo.version = str.substring(4, str.lastIndexOf("]")) |
||||
} else if (str.startsWith("[offset:")) { |
||||
if (str.lastIndexOf("]") > 8) { |
||||
val offset = str.substring(8, str.lastIndexOf("]")) |
||||
try { |
||||
lrcInfo.offset = Integer.parseInt(offset) |
||||
} catch (e: NumberFormatException) { |
||||
e.printStackTrace() |
||||
} |
||||
|
||||
} |
||||
} else { |
||||
val timeExpress = "\\[(\\d{1,2}:\\d{1,2}\\.\\d{1,2})]|\\[(\\d{1,2}:\\d{1,2})]" |
||||
val pattern = Pattern.compile(timeExpress) |
||||
val matcher = pattern.matcher(str) |
||||
var currentTime: Long = 0 |
||||
while (matcher.find()) { |
||||
val groupCount = matcher.groupCount() |
||||
for (index in 0 until groupCount) { |
||||
val timeStr = matcher.group(index) |
||||
if (index == 0) { |
||||
currentTime = TimeUtil.timeStrToLong(timeStr.substring(1, timeStr.length - 1)) |
||||
} |
||||
} |
||||
val content = pattern.split(str) |
||||
var currentContent: String? = "" |
||||
if (content.isNotEmpty()) { |
||||
currentContent = content[content.size - 1] |
||||
} |
||||
val lrcLine = LrcLine() |
||||
lrcLine.startTime = currentTime |
||||
lrcLine.endTime = currentTime + 10 * 1000 |
||||
lrcLine.content = currentContent |
||||
lrcLineList.add(lrcLine) |
||||
} |
||||
} |
||||
} |
||||
|
||||
} |
@ -0,0 +1,85 @@ |
||||
package com.frank.ffmpeg.tool |
||||
|
||||
import java.io.IOException |
||||
import java.io.InputStream |
||||
import java.io.PushbackInputStream |
||||
|
||||
class UnicodeInputStream(`in`: InputStream, private val defaultEnc: String) : InputStream() { |
||||
|
||||
private var encoding: String? = null |
||||
private var isInitialed = false |
||||
private val internalIn: PushbackInputStream |
||||
|
||||
init { |
||||
internalIn = PushbackInputStream(`in`, BOM_SIZE) |
||||
} |
||||
|
||||
fun getEncoding(): String? { |
||||
if (!isInitialed) { |
||||
try { |
||||
init() |
||||
} catch (ex: IOException) { |
||||
throw IllegalStateException("getEncoding error=" + ex.message) |
||||
} |
||||
|
||||
} |
||||
return encoding |
||||
} |
||||
|
||||
/** |
||||
* Read-ahead four bytes and check for BOM marks. Extra bytes are unread |
||||
* back to the stream, only BOM bytes are skipped. |
||||
*/ |
||||
@Throws(IOException::class) |
||||
private fun init() { |
||||
if (isInitialed) |
||||
return |
||||
|
||||
val bom = ByteArray(BOM_SIZE) |
||||
val n: Int |
||||
val unread: Int |
||||
n = internalIn.read(bom, 0, bom.size) |
||||
|
||||
if (bom[0] == 0x00.toByte() && bom[1] == 0x00.toByte() |
||||
&& bom[2] == 0xFE.toByte() && bom[3] == 0xFF.toByte()) { |
||||
encoding = "UTF-32BE" |
||||
unread = n - 4 |
||||
} else if (bom[0] == 0xFF.toByte() && bom[1] == 0xFE.toByte() |
||||
&& bom[2] == 0x00.toByte() && bom[3] == 0x00.toByte()) { |
||||
encoding = "UTF-32LE" |
||||
unread = n - 4 |
||||
} else if (bom[0] == 0xEF.toByte() && bom[1] == 0xBB.toByte() |
||||
&& bom[2] == 0xBF.toByte()) { |
||||
encoding = "UTF-8" |
||||
unread = n - 3 |
||||
} else if (bom[0] == 0xFE.toByte() && bom[1] == 0xFF.toByte()) { |
||||
encoding = "UTF-16BE" |
||||
unread = n - 2 |
||||
} else if (bom[0] == 0xFF.toByte() && bom[1] == 0xFE.toByte()) { |
||||
encoding = "UTF-16LE" |
||||
unread = n - 2 |
||||
} else { |
||||
encoding = defaultEnc |
||||
unread = n |
||||
} |
||||
if (unread > 0) |
||||
internalIn.unread(bom, n - unread, unread) |
||||
isInitialed = true |
||||
} |
||||
|
||||
@Throws(IOException::class) |
||||
override fun close() { |
||||
isInitialed = false |
||||
internalIn.close() |
||||
} |
||||
|
||||
@Throws(IOException::class) |
||||
override fun read(): Int { |
||||
isInitialed = true |
||||
return internalIn.read() |
||||
} |
||||
|
||||
companion object { |
||||
private const val BOM_SIZE = 4 |
||||
} |
||||
} |
Loading…
Reference in new issue