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