package com.android.music.lrc;
import com.android.music.lrc.LRC.Offset;
import android.text.TextUtils;
import android.util.Log;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* LRCParser
*
* @author lisc
*/
public class LRCParser {
private static final String TAG = "LRCParser";
private LRCParser() {
}
/**
* parseFromFile
*
* @param path
* @return LRC
*/
public static LRC parseFromFile(String path) {
// by zhangjb add
File file = new File(path);
isValidLRCFile(file);
FileInputStream in = null;
InputStreamReader read = null;
BufferedReader reader = null;
File destFile = null;
try {
byte b[]=new byte[(int)file.length()];
in = new FileInputStream(file);
in.read(b);
read = new InputStreamReader(new FileInputStream(file),
LyricConstants.TEXT_UTF_ENCODING);
reader = new BufferedReader(read);
String line;
String type = "";
while ((line = reader.readLine()) != null) {
if (LyricConstants.DOWNLOAD_LRC_FLAY.equals(line)) {
type = LyricConstants.TEXT_UTF_ENCODING;
return parseFromFile(file);
} else {
type = getCharEncoding(line,b);
}
break;
}
destFile = toUtf(type, file);
} catch (IOException e) {
Log.e(TAG, "PARSE FROM FILE ERROR:" + e.getMessage());
} catch (OutOfMemoryError e) {
Log.e(TAG, "FILE OUT OF SIZE" + e.getMessage());
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
}
}
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
Log.e(TAG, "CLOSE BUFFERED READER ERROR:" + e);
}
}
if (read != null) {
try {
read.close();
} catch (IOException e) {
Log.e(TAG, "CLOSE INPUT STREAM READER ERROR:" + e);
}
}
}
return parseFromFile(destFile);
}
/**
* parseFromFile
*
* @param path
* @return LRC
*/
public static LRC parseFromFile(File path) {
isValidLRCFile(path);
Pattern pattern = Pattern.compile(LyricConstants.REGEX);
BufferedReader reader = null;
String s = null;
LRC lrc = new LRC();
try {
reader = new BufferedReader(new FileReader(path));
Matcher matcher = null;
while ((s = reader.readLine()) != null) {
matcher = pattern.matcher(s);
while (matcher.find()) {
String g = matcher.group(1);
if (g != null) {
extractHead(lrc, g);
} else {
g = matcher.group(2);
extractBody(lrc, g);
}
}
}
} catch (Exception e) {
Log.e(TAG, e.getMessage());
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
Log.e(TAG, e.getMessage());
}
}
}
lrc.sortOffsets();
return lrc;
}
/**
* isValidLRCFile
*
* @param path
*/
public static void isValidLRCFile(File path) {
if (path == null || !path.exists()) {
throw new IllegalArgumentException("lrc file [" + path.getPath() + "] is invalid.");
}
if (!path.canRead()) {
throw new IllegalArgumentException("lrc file [" + path.getPath() + "] can not be read.");
}
if (!path.getName().toLowerCase().endsWith(LyricConstants.EXTENSION)) {
throw new IllegalArgumentException("[" + path.getName() + "] is not a valid lrc file.");
}
}
/**
* toUtf
*
* @param type
* @param sourceFile
* @return File
* @throws IOException
*/
public static File toUtf(String type, File sourceFile) throws IOException {
if (type == LyricConstants.TEXT_UTF_ENCODING) {
return sourceFile;
} else {
StringBuffer utfContent = new StringBuffer();
BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(
sourceFile), type));
String singleline = null;
while ((singleline = br.readLine()) != null) {
utfContent.append(singleline + System.getProperty("line.separator"));
}
br.close();
File dir = new File(StringConstant.CURRENT_PATH, StringConstant.LRC_DIRECTORY
+ File.separator);
if (!dir.exists()) {
dir.mkdirs();
}
File tempFile = new File(dir, LyricConstants.TEMP_LRC_FILE);
Writer write = new OutputStreamWriter(new FileOutputStream(tempFile),
LyricConstants.TEXT_UTF_ENCODING);
write.write(new String(utfContent.toString().getBytes(LyricConstants.TEXT_UTF_ENCODING)));
write.close();
return tempFile;
}
}
/**
* getCharEncoding
*
* @param sourceString
* @return String
*/
private static String getCharEncoding(String sourceString,byte[] b) {
StringBuffer testString = new StringBuffer();
String type = "";
for (int i = 0; i < sourceString.length(); i++) {
char sc = sourceString.charAt(i);
testString.append(Integer.toHexString(sc).toUpperCase());
}
String destString = testString.toString();
Log.d(TAG, "destString="+destString);
if (destString.startsWith("FEFF") || destString.startsWith("FFFE")) {
type = LyricConstants.TEXT_UTF_ENCODING;
} else if (destString.startsWith("FFFD")) {
type = LyricConstants.TEXT_UNICODE_ENCODING;
} else {
if(IsUTF8Bytes(b)){
type = LyricConstants.TEXT_UTF_ENCODING;
}else{
type = LyricConstants.TEXT_GB_ENCODING;
}
}
return type;
}
/**
* extractHead
*
* @param lrc
* @param head
*/
private static void extractHead(LRC lrc, String head) {
try {
int l = head.indexOf('[');
int r = head.indexOf(']');
int m = head.indexOf(':');
String key = head.substring(l + 1, m);
String value = head.substring(m + 1, r);
lrc.setBasicInfo(key, value);
} catch (Exception e) {
throw new LRCFormatException("The lrc file has a bad format.", e);
}
}
/**
* extractBody
*
* @param lrc
* @param body
*/
private static void extractBody(LRC lrc, String body) {
try {
List<Long> times = new ArrayList<Long>();
while (true) {
int l = body.indexOf('[');
if (l < 0) {
break;
}
int r = body.indexOf(']');
int m = body.indexOf(':');
int d = body.indexOf('.');
int min = Integer.valueOf(body.substring(l + 1, m));
String tmpSec = body.substring(m + 1, d > 0 && d < r ? d : r);
boolean hasNeg = tmpSec.indexOf('-') >= 0;
int sec = Integer.valueOf(tmpSec);
int mil = d > 0 && d < r ? Integer.valueOf(body.substring(d + 1, r)) : 0;
mil = Math.abs(mil);
if (mil < 100) {
mil *= 10;
}
long time = min * 60 * 1000 + sec * 1000 + (hasNeg ? -1 : 1) * mil;
times.add(time);
body = body.substring(r + 1);
}
int ind = lrc.addLyric(body);
for (long t : times) {
lrc.addOffset(t, ind);
}
} catch (Exception e) {
throw new LRCFormatException("The lrc file has a bad format.", e);
}
}
// ==========================================================
/**
* formatKeyValue
*/
private static String formatKeyValue(String key, String value) {
return LyricConstants.BORL + key + LyricConstants.SEP1 + value + LyricConstants.BORR;
}
/**
* formatOffset
*
* @param t
* @return String
*/
private static String formatOffset(long t) {
int min = (int) (t / 60000);
t -= min * 60000;
int sec = (int) (t / 1000);
t -= sec * 1000;
int mil = (int) t;
boolean negMin = min < 0;
boolean negSec = sec < 0;
boolean negMil = mil < 0;
min = Math.abs(min);
sec = Math.abs(sec);
mil = Math.abs(mil);
mil /= 10;
return "" + LyricConstants.BORL + (negMin ? "-" : "") + (min < 10 ? "0" + min : min)
+ LyricConstants.SEP1 + (negSec || (sec == 0 && negMil) ? "-" : "")
+ (sec < 10 ? "0" + sec : sec) + LyricConstants.SEP2 + (mil < 10 ? "0" : "") + mil
+ LyricConstants.BORR;
}
private static String[] populateLyrics(LRC lrc) {
ArrayList<String> lyrics = new ArrayList<String>();
lyrics.add(LyricConstants.DOWNLOAD_LRC_FLAY);
if (!TextUtils.isEmpty(lrc.artist)) {
lyrics.add(formatKeyValue(LyricConstants.KEY_AR, lrc.artist));
}
if (!TextUtils.isEmpty(lrc.title)) {
lyrics.add(formatKeyValue(LyricConstants.KEY_TI, lrc.title));
}
if (!TextUtils.isEmpty(lrc.album)) {
lyrics.add(formatKeyValue(LyricConstants.KEY_AL, lrc.album));
}
if (!TextUtils.isEmpty(lrc.by)) {
lyrics.add(formatKeyValue(LyricConstants.KEY_BY, lrc.by));
}
if (!TextUtils.isEmpty(lrc.offset + "")) {
lyrics.add(formatKeyValue(LyricConstants.KEY_OFFSET, lrc.offset + ""));
}
if (!TextUtils.isEmpty(lrc.key)) {
lyrics.add(formatKeyValue(LyricConstants.KEY_KEY, lrc.key));
}
lyrics.add("");
StringBuilder sb = new StringBuilder();
List<Offset> offsets = lrc.getOffsets();
boolean[] isRemoved = new boolean[offsets.size()];
Offset o = null;
int len = offsets.size();
for (int i = 0; i < len; i++) {
if (isRemoved[i]) {
continue;
}
o = offsets.get(i);
sb.append(formatOffset(o.time));
int ind = o.lrcInd;
isRemoved[i] = true;
for (int j = i + 1; j < len; j++) {
if (isRemoved[j]) {
continue;
}
o = offsets.get(j);
if (o.lrcInd == ind) {
sb.append(formatOffset(o.time));
isRemoved[j] = true;
}
}
sb.append(lrc.getLyrics().get(ind));
lyrics.add(sb.toString());
sb.delete(0, sb.length());
}
return lyrics.toArray(new String[lyrics.size()]);
}
/**
* saveToFile
*
* @param path
* @param lrc
* @return boolean
*/
public static boolean saveToFile(String path, LRC lrc) {
if (path == null || lrc == null) {
Log.v(TAG, "saveToFile failed. Because path = " + path + " | lrc = " + lrc);
return false;
}
String newPath = path + LyricConstants.TMP_FILE_EXTENSION;
File newLrc = new File(newPath);
PrintWriter writer = null;
try {
if (newLrc.exists()) {
newLrc.delete();
}
newLrc.createNewFile();
String[] lyrics = populateLyrics(lrc);
if (lyrics == null) {
return false;
}
writer = new PrintWriter(newLrc);
for (String l : lyrics) {
writer.println(l);
}
writer.flush();
File old = new File(path);
if (old.delete()) {
newLrc.renameTo(old);
} else {
Log.e(TAG, "saveToFile failed. Because delete old file failed.");
return false;
}
} catch (IOException e) {
Log.e(TAG, "saveToFile failed. Because\n" + e.getMessage());
newLrc.delete();
return false;
} finally {
if (writer != null) {
writer.close();
writer = null;
}
}
return true;
}
public static boolean IsUTF8Bytes(byte[] data) {
int i = 0;
int size = data.length;
while(i < size)
{
int step = 0;
if((data[i] & 0x80) == 0x00)
{
step = 1;
}
else if((data[i] & 0xe0) == 0xc0)
{
if(i + 1 >= size) return false;
if((data[i + 1] & 0xc0) != 0x80) return false;
step = 2;
}
else if((data[i] & 0xf0) == 0xe0)
{
if(i + 2 >= size) return false;
if((data[i + 1] & 0xc0) != 0x80) return false;
if((data[i + 2] & 0xc0) != 0x80) return false;
step = 3;
}
else
{
return false;
}
i += step;
}
if(i == size) return true;
return false;
}
}