Skip to content

Commit

Permalink
新增实验性的添加本地歌曲到我的收藏支持
Browse files Browse the repository at this point in the history
  • Loading branch information
lyswhut committed Dec 10, 2023
1 parent 30609c6 commit f7793cf
Show file tree
Hide file tree
Showing 540 changed files with 94,805 additions and 23 deletions.
5 changes: 4 additions & 1 deletion android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,10 @@ dependencies {
// implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3")
implementation group: 'wang.harlon.quickjs', name: 'wrapper-android', version: '1.0.0'

if (hermesEnabled.toBoolean()) {
// https://mvnrepository.com/artifact/com.googlecode.juniversalchardet/juniversalchardet
implementation group: 'com.googlecode.juniversalchardet', name: 'juniversalchardet', version: '1.0.3'

if (hermesEnabled.toBoolean()) {
implementation("com.facebook.react:hermes-android")
} else {
implementation jscFlavor
Expand Down
3 changes: 3 additions & 0 deletions android/app/proguard-rules.pro
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,6 @@

-keep class com.reactnativenavigation.views.element.animators.** { *; }
# -keepclassmembers class com.reactnativenavigation.views.element.animators.** { *; }


-keep class org.jaudiotagger.tag.** { *; }
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import cn.toside.music.mobile.cache.CachePackage;
import cn.toside.music.mobile.crypto.CryptoPackage;
import cn.toside.music.mobile.gzip.GzipPackage;
import cn.toside.music.mobile.localMedia.LocalMediaPackage;
import cn.toside.music.mobile.lyric.LyricPackage;
import cn.toside.music.mobile.userApi.UserApiPackage;
import cn.toside.music.mobile.utils.UtilsPackage;
Expand All @@ -40,6 +41,7 @@ protected List<ReactPackage> getPackages() {
packages.add(new UtilsPackage());
packages.add(new CryptoPackage());
packages.add(new UserApiPackage());
packages.add(new LocalMediaPackage());
return packages;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package cn.toside.music.mobile.localMedia;

import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;

import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableMap;

import cn.toside.music.mobile.gzip.Utils;
import cn.toside.music.mobile.utils.TaskRunner;

public class LocalMediaModule extends ReactContextBaseJavaModule {
private final ReactApplicationContext reactContext;

private int listenerCount = 0;

LocalMediaModule(ReactApplicationContext reactContext) {
super(reactContext);
this.reactContext = reactContext;
}

@Override
public String getName() {
return "LocalMediaModule";
}

@ReactMethod
public void addListener(String eventName) {
if (listenerCount == 0) {
// Set up any upstream listeners or background tasks as necessary
}

listenerCount += 1;
}

@ReactMethod
public void removeListeners(Integer count) {
listenerCount -= count;
if (listenerCount == 0) {
// Remove upstream listeners, stop unnecessary background tasks
}
}

@ReactMethod
public void scanAudioFiles(String dirPath, Promise promise) {
TaskRunner taskRunner = new TaskRunner();
try {
taskRunner.executeAsync(new MetadataCallable.ScanAudioFiles(reactContext, dirPath), promise::resolve);
} catch (Exception err) {
promise.reject("-1", err.getMessage());
}
}

@ReactMethod
public void readMetadata(String filePath, Promise promise) {
TaskRunner taskRunner = new TaskRunner();
try {
taskRunner.executeAsync(new MetadataCallable.ReadMetadata(filePath), promise::resolve);
} catch (Exception err) {
promise.reject("-1", err.getMessage());
}
}

@ReactMethod
public void readPic(String filePath, Promise promise) {
TaskRunner taskRunner = new TaskRunner();
try {
taskRunner.executeAsync(new MetadataCallable.ReadPic(filePath), promise::resolve);
} catch (Exception err) {
promise.reject("-1", err.getMessage());
}
}

@ReactMethod
public void readLyric(String filePath, Promise promise) {
TaskRunner taskRunner = new TaskRunner();
try {
taskRunner.executeAsync(new MetadataCallable.ReadLyric(filePath), promise::resolve);
} catch (Exception err) {
promise.reject("-1", err.getMessage());
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package cn.toside.music.mobile.localMedia;

import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public class LocalMediaPackage implements ReactPackage {
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Collections.emptyList();
}

@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
return Arrays.asList(new LocalMediaModule(reactContext));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package cn.toside.music.mobile.localMedia;

import android.util.Base64;

import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.WritableMap;

import org.jaudiotagger.audio.AudioFile;
import org.jaudiotagger.audio.AudioFileIO;
import org.jaudiotagger.audio.AudioHeader;
import org.jaudiotagger.tag.FieldKey;
import org.jaudiotagger.tag.Tag;
import org.jaudiotagger.tag.images.Artwork;
import org.mozilla.universalchardet.UniversalDetector;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.nio.charset.StandardCharsets;

public class Metadata {
private static String getFileName(File file) {
String fileName = file.getName();
int dotIndex = fileName.lastIndexOf(".");
if (dotIndex != -1) {
return fileName.substring(0, dotIndex);
} else {
return fileName;
}
}
private static String getFileExtension(String fileName) {
int dotIndex = fileName.lastIndexOf(".");
if (dotIndex != -1 && dotIndex < fileName.length() - 1) {
return fileName.substring(dotIndex + 1);
} else {
return "";
}
}
private static WritableMap buildMetadata(File file, AudioHeader audioHeader, Tag tag) {
WritableMap params = Arguments.createMap();
String name = tag.getFirst(FieldKey.TITLE);
if ("".equals(name)) name = getFileName(file);
params.putString("name", name);
params.putString("singer", tag.getFirst(FieldKey.ARTIST).replaceAll("\\u0000", "、"));
params.putString("albumName", tag.getFirst(FieldKey.ALBUM));
params.putDouble("interval", audioHeader.getTrackLength());
params.putString("bitrate", audioHeader.getBitRate());
params.putString("type", audioHeader.getEncodingType());
params.putString("ext", getFileExtension(file.getName()));
params.putDouble("size", file.length());

return params;
}

static public WritableMap readMetadata(String filePath) throws Exception {
File file = new File(filePath);
AudioFile audioFile = AudioFileIO.read(file);
return buildMetadata(file, audioFile.getAudioHeader(), audioFile.getTag());
}

private static String encodeBase64(byte[] data) {
return new String(Base64.encode(data, Base64.NO_WRAP), StandardCharsets.UTF_8);
}
public static String readPic(String filePath) throws Exception {
File file = new File(filePath);
AudioFile audioFile = AudioFileIO.read(file);
Artwork artwork = audioFile.getTag().getFirstArtwork();
if (artwork.isLinked()) return artwork.getImageUrl();

return "data:" + artwork.getMimeType() +
";base64," + encodeBase64(artwork.getBinaryData());
}

public static String decodeString(byte[] data) {
UniversalDetector detector = new UniversalDetector(null);
detector.handleData(data, 0, data.length);
detector.dataEnd();
String detectedCharset = detector.getDetectedCharset();
detector.reset();
try {
return new String(data, detectedCharset);
} catch (Exception e) {
return "";
}
}
public static String readLyricFile(File lrcFile) {
try {
FileInputStream fileInputStream = new FileInputStream(lrcFile);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = fileInputStream.read(buffer)) != -1) {
byteArrayOutputStream.write(buffer, 0, bytesRead);
}
fileInputStream.close();
return decodeString(byteArrayOutputStream.toByteArray());
} catch (Exception e) {
return "";
}
}
public static String readLyric(String filePath) throws Exception {
File file = new File(filePath);
File lrcFile = new File(file.getParent() + "/" + getFileName(file) + ".lrc");
if (lrcFile.exists()) {
String lrc = readLyricFile(lrcFile);
if (!"".equals(lrc)) return lrc;
}

AudioFile audioFile = AudioFileIO.read(file);
Tag tag = audioFile.getTag();
return tag.getFirst(FieldKey.LYRICS);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package cn.toside.music.mobile.localMedia;

import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
import android.util.Base64;
import android.util.Log;

import androidx.annotation.NonNull;

import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.WritableArray;
import com.facebook.react.bridge.WritableMap;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.zip.GZIPInputStream;

public class MetadataCallable {
static class ReadMetadata implements Callable<WritableMap> {
private final String filePath;
public ReadMetadata(String filePath) {
this.filePath = filePath;
}
@Override
public WritableMap call() {
try {
return Metadata.readMetadata(this.filePath);
} catch (Exception err) {
Log.e("ReadMetadata", "Read Metadata Error: " + err.getMessage());
return null;
}
}
}

static class ReadPic implements Callable<String> {
private final String filePath;
public ReadPic(String filePath) {
this.filePath = filePath;
}
@Override
public String call() {
try {
return Metadata.readPic(this.filePath);
} catch (Exception err) {
Log.e("ReadMetadata", "Read Pic Error: " + err.getMessage());
return "";
}
}
}

static class ReadLyric implements Callable<String> {
private final String filePath;
public ReadLyric(String filePath) {
this.filePath = filePath;
}
@Override
public String call() {
try {
return Metadata.readLyric(this.filePath);
} catch (Exception err) {
Log.e("ReadMetadata", "Read Lyric Error: " + err.getMessage());
return "";
}
}
}

static class ScanAudioFiles implements Callable<WritableArray> {
private final ReactApplicationContext context;
private final String dirPath;
public ScanAudioFiles(ReactApplicationContext context, String dirPath) {
this.context = context;
this.dirPath = dirPath;
}

@Override
public WritableArray call() {
try {
return Utils.scanAudioFiles(this.context, this.dirPath);
} catch (Exception err) {
return Arguments.createArray();
}
}
}
}
Loading

0 comments on commit f7793cf

Please sign in to comment.