mirror of
https://github.com/ikun0014/lx-music-mobile.git
synced 2025-05-23 22:37:41 +08:00
优化同步逻辑
This commit is contained in:
parent
c80d388dc0
commit
acb756710a
@ -31,6 +31,8 @@
|
|||||||
|
|
||||||
目前本项目的原始发布地址只有**GitHub**及**蓝奏网盘**,其他渠道均为第三方转载发布,与本项目无关!
|
目前本项目的原始发布地址只有**GitHub**及**蓝奏网盘**,其他渠道均为第三方转载发布,与本项目无关!
|
||||||
|
|
||||||
|
为了提高使用门槛,本软件内的默认设置、UI操作不以新手友好为目标,所以使用前建议先根据你的喜好浏览调整一遍软件设置,阅读一遍[音乐播放列表机制](https://lyswhut.github.io/lx-music-doc/desktop/faq/playlist)
|
||||||
|
|
||||||
#### 数据同步服务
|
#### 数据同步服务
|
||||||
|
|
||||||
从v1.0.0起,我们发布了一个独立版的[数据同步服务](https://github.com/lyswhut/lx-music-sync-server#readme),如果你有服务器,可以将其部署到服务器上作为私人多端同步服务使用,详情看该项目说明
|
从v1.0.0起,我们发布了一个独立版的[数据同步服务](https://github.com/lyswhut/lx-music-sync-server#readme),如果你有服务器,可以将其部署到服务器上作为私人多端同步服务使用,详情看该项目说明
|
||||||
|
@ -18,10 +18,10 @@
|
|||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:roundIcon="@mipmap/ic_launcher_round"
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
android:allowBackup="false"
|
android:allowBackup="false"
|
||||||
android:theme="@style/AppTheme">
|
android:theme="@style/AppTheme"
|
||||||
|
tools:targetApi="n">
|
||||||
<activity
|
<activity
|
||||||
android:name=".MainActivity"
|
android:name=".MainActivity"
|
||||||
android:label="@string/app_name"
|
|
||||||
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode"
|
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode"
|
||||||
android:launchMode="singleTask"
|
android:launchMode="singleTask"
|
||||||
android:windowSoftInputMode="adjustResize"
|
android:windowSoftInputMode="adjustResize"
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package cn.toside.music.mobile.gzip;
|
package cn.toside.music.mobile.gzip;
|
||||||
|
|
||||||
|
import android.util.Base64;
|
||||||
|
|
||||||
import com.facebook.common.internal.Throwables;
|
import com.facebook.common.internal.Throwables;
|
||||||
import com.facebook.react.bridge.Promise;
|
import com.facebook.react.bridge.Promise;
|
||||||
import com.facebook.react.bridge.ReactApplicationContext;
|
import com.facebook.react.bridge.ReactApplicationContext;
|
||||||
@ -18,6 +20,7 @@ import cn.toside.music.mobile.utils.TaskRunner;
|
|||||||
|
|
||||||
// https://github.com/FWC1994/react-native-gzip/blob/main/android/src/main/java/com/reactlibrary/GzipModule.java
|
// https://github.com/FWC1994/react-native-gzip/blob/main/android/src/main/java/com/reactlibrary/GzipModule.java
|
||||||
// https://www.digitalocean.com/community/tutorials/java-gzip-example-compress-decompress-file
|
// https://www.digitalocean.com/community/tutorials/java-gzip-example-compress-decompress-file
|
||||||
|
// https://github.com/ammarahm-ed/react-native-gzip/blob/master/android/src/main/java/com/gzip/GzipModule.java
|
||||||
public class GzipModule extends ReactContextBaseJavaModule {
|
public class GzipModule extends ReactContextBaseJavaModule {
|
||||||
private final ReactApplicationContext reactContext;
|
private final ReactApplicationContext reactContext;
|
||||||
|
|
||||||
@ -31,104 +34,31 @@ public class GzipModule extends ReactContextBaseJavaModule {
|
|||||||
return "GzipModule";
|
return "GzipModule";
|
||||||
}
|
}
|
||||||
|
|
||||||
static class UnGzip implements Callable<String> {
|
@ReactMethod
|
||||||
private final String source;
|
public void unGzipFromBase64(String base64, Promise promise) {
|
||||||
private final String target;
|
TaskRunner taskRunner = new TaskRunner();
|
||||||
private final Boolean force;
|
try {
|
||||||
|
taskRunner.executeAsync(new Utils.UnGzip(base64), promise::resolve);
|
||||||
public UnGzip(String source, String target, Boolean force) {
|
} catch (RuntimeException err) {
|
||||||
this.source = source;
|
promise.reject("-2", err.getMessage());
|
||||||
this.target = target;
|
|
||||||
this.force = force;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String call() {
|
|
||||||
// Log.d("Gzip", "source: " + source + ", target: " + target);
|
|
||||||
File sourceFile = new File(source);
|
|
||||||
File targetFile = new File(target);
|
|
||||||
if(!Utils.checkDir(sourceFile, targetFile, force)){
|
|
||||||
return "error";
|
|
||||||
}
|
|
||||||
|
|
||||||
FileInputStream fileInputStream;
|
|
||||||
FileOutputStream fileOutputStream;
|
|
||||||
|
|
||||||
try{
|
|
||||||
fileInputStream = new FileInputStream(sourceFile);
|
|
||||||
fileOutputStream = new FileOutputStream(targetFile);
|
|
||||||
final GZIPInputStream gzipInputStream = new GZIPInputStream(fileInputStream);
|
|
||||||
|
|
||||||
final byte[] buffer = new byte[4096];
|
|
||||||
int len;
|
|
||||||
while((len = gzipInputStream.read(buffer)) != -1){
|
|
||||||
fileOutputStream.write(buffer, 0, len);
|
|
||||||
}
|
|
||||||
//close resources
|
|
||||||
fileOutputStream.close();
|
|
||||||
gzipInputStream.close();
|
|
||||||
|
|
||||||
return "";
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
|
|
||||||
return "unGzip error: " + Throwables.getStackTraceAsString(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static class Gzip implements Callable<String> {
|
|
||||||
private final String source;
|
|
||||||
private final String target;
|
|
||||||
private final Boolean force;
|
|
||||||
|
|
||||||
public Gzip(String source, String target, Boolean force) {
|
|
||||||
this.source = source;
|
|
||||||
this.target = target;
|
|
||||||
this.force = force;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String call() {
|
|
||||||
// Log.d("Gzip", "source: " + source + ", target: " + target);
|
|
||||||
File sourceFile = new File(source);
|
|
||||||
File targetFile = new File(target);
|
|
||||||
// Log.d("Gzip", "sourceFile: " + sourceFile.getAbsolutePath() + ", targetFile: " + targetFile.getAbsolutePath());
|
|
||||||
if(!Utils.checkFile(sourceFile, targetFile, force)){
|
|
||||||
return "error";
|
|
||||||
}
|
|
||||||
|
|
||||||
FileInputStream fileInputStream;
|
|
||||||
FileOutputStream fileOutputStream;
|
|
||||||
|
|
||||||
try{
|
|
||||||
fileInputStream = new FileInputStream(sourceFile);
|
|
||||||
fileOutputStream = new FileOutputStream(targetFile);
|
|
||||||
|
|
||||||
GZIPOutputStream gzipOutputStream = new GZIPOutputStream(fileOutputStream);
|
|
||||||
final byte[] buffer = new byte[4096];
|
|
||||||
int len;
|
|
||||||
while((len= fileInputStream.read(buffer)) != -1){
|
|
||||||
gzipOutputStream.write(buffer, 0, len);
|
|
||||||
}
|
|
||||||
//close resources
|
|
||||||
gzipOutputStream.close();
|
|
||||||
gzipOutputStream.close();
|
|
||||||
fileInputStream.close();
|
|
||||||
|
|
||||||
return "";
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
return "gzip error: " + source.length() + "\nstack: " + Throwables.getStackTraceAsString(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ReactMethod
|
@ReactMethod
|
||||||
public void unGzip(String source, String target, Boolean force, Promise promise) {
|
public void gzipStringToBase64(String data, Promise promise) {
|
||||||
TaskRunner taskRunner = new TaskRunner();
|
TaskRunner taskRunner = new TaskRunner();
|
||||||
try {
|
try {
|
||||||
taskRunner.executeAsync(new UnGzip(source, target, force), (String errMessage) -> {
|
taskRunner.executeAsync(new Utils.Gzip(data), promise::resolve);
|
||||||
|
} catch (RuntimeException err) {
|
||||||
|
promise.reject("-2", err.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ReactMethod
|
||||||
|
public void unGzipFile(String source, String target, Boolean force, Promise promise) {
|
||||||
|
TaskRunner taskRunner = new TaskRunner();
|
||||||
|
try {
|
||||||
|
taskRunner.executeAsync(new Utils.UnGzipFile(source, target, force), (String errMessage) -> {
|
||||||
if ("".equals(errMessage)) {
|
if ("".equals(errMessage)) {
|
||||||
promise.resolve(null);
|
promise.resolve(null);
|
||||||
} else promise.reject("-2", errMessage);
|
} else promise.reject("-2", errMessage);
|
||||||
@ -139,10 +69,10 @@ public class GzipModule extends ReactContextBaseJavaModule {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@ReactMethod
|
@ReactMethod
|
||||||
public void gzip(String source, String target, Boolean force, Promise promise) {
|
public void gzipFile(String source, String target, Boolean force, Promise promise) {
|
||||||
TaskRunner taskRunner = new TaskRunner();
|
TaskRunner taskRunner = new TaskRunner();
|
||||||
try {
|
try {
|
||||||
taskRunner.executeAsync(new Gzip(source, target, force), (String errMessage) -> {
|
taskRunner.executeAsync(new Utils.GzipFile(source, target, force), (String errMessage) -> {
|
||||||
if ("".equals(errMessage)) {
|
if ("".equals(errMessage)) {
|
||||||
promise.resolve(null);
|
promise.resolve(null);
|
||||||
} else promise.reject("-2", errMessage);
|
} else promise.reject("-2", errMessage);
|
||||||
|
@ -3,7 +3,23 @@ package cn.toside.music.mobile.gzip;
|
|||||||
|
|
||||||
import static cn.toside.music.mobile.utils.Utils.deletePath;
|
import static cn.toside.music.mobile.utils.Utils.deletePath;
|
||||||
|
|
||||||
|
import android.util.Base64;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.facebook.common.internal.Throwables;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
|
import java.util.zip.GZIPInputStream;
|
||||||
|
import java.util.zip.GZIPOutputStream;
|
||||||
|
|
||||||
// https://github.com/FWC1994/react-native-gzip/blob/main/android/src/main/java/com/reactlibrary/GzipModule.java
|
// https://github.com/FWC1994/react-native-gzip/blob/main/android/src/main/java/com/reactlibrary/GzipModule.java
|
||||||
public class Utils {
|
public class Utils {
|
||||||
@ -37,4 +53,143 @@ public class Utils {
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static class UnGzipFile implements Callable<String> {
|
||||||
|
private final String source;
|
||||||
|
private final String target;
|
||||||
|
private final Boolean force;
|
||||||
|
|
||||||
|
public UnGzipFile(String source, String target, Boolean force) {
|
||||||
|
this.source = source;
|
||||||
|
this.target = target;
|
||||||
|
this.force = force;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String call() {
|
||||||
|
// Log.d("Gzip", "source: " + source + ", target: " + target);
|
||||||
|
File sourceFile = new File(source);
|
||||||
|
File targetFile = new File(target);
|
||||||
|
if(!Utils.checkDir(sourceFile, targetFile, force)){
|
||||||
|
return "error";
|
||||||
|
}
|
||||||
|
|
||||||
|
FileInputStream fileInputStream;
|
||||||
|
FileOutputStream fileOutputStream;
|
||||||
|
|
||||||
|
try{
|
||||||
|
fileInputStream = new FileInputStream(sourceFile);
|
||||||
|
fileOutputStream = new FileOutputStream(targetFile);
|
||||||
|
final GZIPInputStream gzipInputStream = new GZIPInputStream(fileInputStream);
|
||||||
|
|
||||||
|
final byte[] buffer = new byte[4096];
|
||||||
|
int len;
|
||||||
|
while((len = gzipInputStream.read(buffer)) != -1){
|
||||||
|
fileOutputStream.write(buffer, 0, len);
|
||||||
|
}
|
||||||
|
//close resources
|
||||||
|
fileOutputStream.close();
|
||||||
|
gzipInputStream.close();
|
||||||
|
fileInputStream.close();
|
||||||
|
|
||||||
|
return "";
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
|
||||||
|
return "unGzip error: " + Throwables.getStackTraceAsString(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class GzipFile implements Callable<String> {
|
||||||
|
private final String source;
|
||||||
|
private final String target;
|
||||||
|
private final Boolean force;
|
||||||
|
|
||||||
|
public GzipFile(String source, String target, Boolean force) {
|
||||||
|
this.source = source;
|
||||||
|
this.target = target;
|
||||||
|
this.force = force;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String call() {
|
||||||
|
// Log.d("Gzip", "source: " + source + ", target: " + target);
|
||||||
|
File sourceFile = new File(source);
|
||||||
|
File targetFile = new File(target);
|
||||||
|
// Log.d("Gzip", "sourceFile: " + sourceFile.getAbsolutePath() + ", targetFile: " + targetFile.getAbsolutePath());
|
||||||
|
if(!Utils.checkFile(sourceFile, targetFile, force)){
|
||||||
|
return "error";
|
||||||
|
}
|
||||||
|
|
||||||
|
FileInputStream fileInputStream;
|
||||||
|
FileOutputStream fileOutputStream;
|
||||||
|
|
||||||
|
try{
|
||||||
|
fileInputStream = new FileInputStream(sourceFile);
|
||||||
|
fileOutputStream = new FileOutputStream(targetFile);
|
||||||
|
|
||||||
|
GZIPOutputStream gzipOutputStream = new GZIPOutputStream(fileOutputStream);
|
||||||
|
final byte[] buffer = new byte[4096];
|
||||||
|
int len;
|
||||||
|
while((len= fileInputStream.read(buffer)) != -1){
|
||||||
|
gzipOutputStream.write(buffer, 0, len);
|
||||||
|
}
|
||||||
|
//close resources
|
||||||
|
gzipOutputStream.close();
|
||||||
|
fileInputStream.close();
|
||||||
|
fileOutputStream.close();
|
||||||
|
|
||||||
|
return "";
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return "gzip error: " + source.length() + "\nstack: " + Throwables.getStackTraceAsString(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class UnGzip implements Callable<String> {
|
||||||
|
private final byte[] data;
|
||||||
|
|
||||||
|
public UnGzip(String data) {
|
||||||
|
this.data = Base64.decode(data, Base64.DEFAULT);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String call() throws IOException {
|
||||||
|
// Log.d("Gzip", "source: " + source + ", target: " + target);
|
||||||
|
final int BUFFER_SIZE = 1024;
|
||||||
|
ByteArrayInputStream is = new ByteArrayInputStream(data);
|
||||||
|
GZIPInputStream gis = new GZIPInputStream(is, BUFFER_SIZE);
|
||||||
|
BufferedReader bf = new BufferedReader(new InputStreamReader(gis, StandardCharsets.UTF_8));
|
||||||
|
final StringBuilder outStr = new StringBuilder();
|
||||||
|
String line;
|
||||||
|
while ((line = bf.readLine()) != null) {
|
||||||
|
outStr.append(line);
|
||||||
|
}
|
||||||
|
gis.close();
|
||||||
|
is.close();
|
||||||
|
bf.close();
|
||||||
|
return outStr.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class Gzip implements Callable<String> {
|
||||||
|
private final String data;
|
||||||
|
|
||||||
|
public Gzip(String data) {
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String call() throws IOException {
|
||||||
|
ByteArrayOutputStream os = new ByteArrayOutputStream(data.length());
|
||||||
|
GZIPOutputStream gos = new GZIPOutputStream(os);
|
||||||
|
gos.write(data.getBytes(StandardCharsets.UTF_8));
|
||||||
|
gos.close();
|
||||||
|
byte[] compressed = os.toByteArray();
|
||||||
|
os.close();
|
||||||
|
return Base64.encodeToString(compressed, Base64.NO_WRAP);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
1736
package-lock.json
generated
1736
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
17
package.json
17
package.json
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "lx-music-mobile",
|
"name": "lx-music-mobile",
|
||||||
"version": "1.0.7-beta.9",
|
"version": "1.0.7-beta.10",
|
||||||
"versionCode": 60,
|
"versionCode": 60,
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@ -43,11 +43,12 @@
|
|||||||
"homepage": "https://github.com/lyswhut/lx-music-mobile#readme",
|
"homepage": "https://github.com/lyswhut/lx-music-mobile#readme",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@craftzdog/react-native-buffer": "^6.0.5",
|
"@craftzdog/react-native-buffer": "^6.0.5",
|
||||||
"@react-native-async-storage/async-storage": "^1.19.1",
|
"@react-native-async-storage/async-storage": "^1.19.2",
|
||||||
"@react-native-clipboard/clipboard": "^1.11.2",
|
"@react-native-clipboard/clipboard": "^1.11.2",
|
||||||
"@react-native-community/slider": "^4.4.2",
|
"@react-native-community/slider": "^4.4.2",
|
||||||
"iconv-lite": "^0.6.3",
|
"iconv-lite": "^0.6.3",
|
||||||
"lrc-file-parser": "^2.4.1",
|
"lrc-file-parser": "^2.4.1",
|
||||||
|
"message2call": "^0.1.0",
|
||||||
"pako": "^2.1.0",
|
"pako": "^2.1.0",
|
||||||
"react": "18.2.0",
|
"react": "18.2.0",
|
||||||
"react-native": "0.72.3",
|
"react-native": "0.72.3",
|
||||||
@ -62,13 +63,13 @@
|
|||||||
"react-native-vector-icons": "^10.0.0"
|
"react-native-vector-icons": "^10.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.22.9",
|
"@babel/core": "^7.22.10",
|
||||||
"@babel/plugin-proposal-export-namespace-from": "^7.18.9",
|
"@babel/plugin-proposal-export-namespace-from": "^7.18.9",
|
||||||
"@babel/preset-env": "^7.22.9",
|
"@babel/preset-env": "^7.22.10",
|
||||||
"@babel/runtime": "^7.22.6",
|
"@babel/runtime": "^7.22.10",
|
||||||
"@react-native/metro-config": "^0.72.9",
|
"@react-native/metro-config": "^0.72.11",
|
||||||
"@tsconfig/react-native": "^3.0.2",
|
"@tsconfig/react-native": "^3.0.2",
|
||||||
"@types/react": "^18.2.18",
|
"@types/react": "^18.2.20",
|
||||||
"@types/react-native": "^0.70.14",
|
"@types/react-native": "^0.70.14",
|
||||||
"@types/react-native-background-timer": "^2.0.0",
|
"@types/react-native-background-timer": "^2.0.0",
|
||||||
"@types/react-native-vector-icons": "^6.4.13",
|
"@types/react-native-vector-icons": "^6.4.13",
|
||||||
@ -77,7 +78,7 @@
|
|||||||
"eslint-config-standard-with-typescript": "^37.0.0",
|
"eslint-config-standard-with-typescript": "^37.0.0",
|
||||||
"eslint-plugin-react": "^7.33.1",
|
"eslint-plugin-react": "^7.33.1",
|
||||||
"eslint-plugin-react-hooks": "^4.6.0",
|
"eslint-plugin-react-hooks": "^4.6.0",
|
||||||
"metro-react-native-babel-preset": "0.76.7",
|
"metro-react-native-babel-preset": "0.76.8",
|
||||||
"typescript": "^5.1.6"
|
"typescript": "^5.1.6"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,11 +4,16 @@
|
|||||||
若在升级新版本时提示签名不一致,则表明你手机上的旧版本或者将要安装的新版本中有一方是第三方修改版。
|
若在升级新版本时提示签名不一致,则表明你手机上的旧版本或者将要安装的新版本中有一方是第三方修改版。
|
||||||
若在升级新版本时提示无法降级安装,则表明你使用的是universal(通用)版安装包(安装包大小20M+),尝试使用arm64-v8a版安装包或者到GitHub下载其他版本安装包。
|
若在升级新版本时提示无法降级安装,则表明你使用的是universal(通用)版安装包(安装包大小20M+),尝试使用arm64-v8a版安装包或者到GitHub下载其他版本安装包。
|
||||||
|
|
||||||
|
### 不兼容性变更
|
||||||
|
|
||||||
|
该版本修改了同步协议逻辑,需要PC端v2.4.0或移动端v1.7.0版本才能连接使用。
|
||||||
|
|
||||||
### 优化
|
### 优化
|
||||||
|
|
||||||
- 优化歌单列表歌单封面大小计算方式
|
- 优化歌单列表歌单封面大小计算方式
|
||||||
- 调整竖屏下的排行榜布局
|
- 调整竖屏下的排行榜布局
|
||||||
- 调整桌面歌词主题配色,增强歌词字体阴影(#276)
|
- 调整桌面歌词主题配色,增强歌词字体阴影(#276)
|
||||||
|
- 优化数据传输逻辑,列表同步指令使用队列机制,保证列表同步操作的顺序
|
||||||
|
|
||||||
### 修复
|
### 修复
|
||||||
|
|
||||||
@ -19,6 +24,7 @@
|
|||||||
- 修复wy源热搜词失效的问题(@Folltoshe)
|
- 修复wy源热搜词失效的问题(@Folltoshe)
|
||||||
- 修复mg歌单搜索歌单播放数量显示问题
|
- 修复mg歌单搜索歌单播放数量显示问题
|
||||||
- 修复搜索提示功能失效的问题(@Folltoshe)
|
- 修复搜索提示功能失效的问题(@Folltoshe)
|
||||||
|
- 修复潜在导致列表数据不同步的问题
|
||||||
|
|
||||||
### 其他
|
### 其他
|
||||||
|
|
||||||
|
17
src/app.ts
17
src/app.ts
@ -64,3 +64,20 @@ initNavigation(async() => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// const createProxy = () => {
|
||||||
|
// return new Proxy(function() {}, {
|
||||||
|
// get: (_target, prop, receiver) => {
|
||||||
|
// let propName = prop.toString()
|
||||||
|
// console.log('proxy get', propName)
|
||||||
|
// return createProxy()
|
||||||
|
// },
|
||||||
|
// // eslint-disable-next-line @typescript-eslint/promise-function-async
|
||||||
|
// apply: (target, thisArg, argumentsList) => {
|
||||||
|
// console.log('proxy apply')
|
||||||
|
// return '56'
|
||||||
|
// },
|
||||||
|
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
// const proxy = createProxy()
|
||||||
|
// console.log(proxy.aaa())
|
||||||
|
@ -137,7 +137,7 @@ export const DEFAULT_SETTING = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const SYNC_CODE = {
|
export const SYNC_CODE = {
|
||||||
helloMsg: 'Hello~::^-^::~v3~',
|
helloMsg: 'Hello~::^-^::~v4~',
|
||||||
idPrefix: 'OjppZDo6',
|
idPrefix: 'OjppZDo6',
|
||||||
authMsg: 'lx-music auth::',
|
authMsg: 'lx-music auth::',
|
||||||
authFailed: 'Auth failed',
|
authFailed: 'Auth failed',
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import TrackPlayer, { Capability, Event, State } from 'react-native-track-player'
|
import TrackPlayer, { Capability, Event, RepeatMode, State } from 'react-native-track-player'
|
||||||
import BackgroundTimer from 'react-native-background-timer'
|
import BackgroundTimer from 'react-native-background-timer'
|
||||||
import { playMusic as handlePlayMusic } from './playList'
|
import { playMusic as handlePlayMusic } from './playList'
|
||||||
// import { PlayerMusicInfo } from '@/store/modules/player/playInfo'
|
// import { PlayerMusicInfo } from '@/store/modules/player/playInfo'
|
||||||
@ -159,6 +159,8 @@ export const setStop = async() => {
|
|||||||
await TrackPlayer.stop()
|
await TrackPlayer.stop()
|
||||||
if (!isEmpty()) await TrackPlayer.skipToNext()
|
if (!isEmpty()) await TrackPlayer.skipToNext()
|
||||||
}
|
}
|
||||||
|
export const setLoop = async(loop: boolean) => TrackPlayer.setRepeatMode(loop ? RepeatMode.Off : RepeatMode.Track)
|
||||||
|
|
||||||
export const setPause = async() => TrackPlayer.pause()
|
export const setPause = async() => TrackPlayer.pause()
|
||||||
// export const skipToNext = () => TrackPlayer.skipToNext()
|
// export const skipToNext = () => TrackPlayer.skipToNext()
|
||||||
export const setCurrentTime = async(time: number) => TrackPlayer.seekTo(time)
|
export const setCurrentTime = async(time: number) => TrackPlayer.seekTo(time)
|
||||||
|
@ -4,6 +4,7 @@ import { SYNC_CODE } from '@/config/constant'
|
|||||||
import log from '../log'
|
import log from '../log'
|
||||||
import { aesDecrypt, aesEncrypt, rsaDecrypt } from '../utils'
|
import { aesDecrypt, aesEncrypt, rsaDecrypt } from '../utils'
|
||||||
import { getDeviceName } from '@/utils/nativeModules/utils'
|
import { getDeviceName } from '@/utils/nativeModules/utils'
|
||||||
|
import { toMD5 } from '@/utils/tools'
|
||||||
|
|
||||||
const hello = async(urlInfo: LX.Sync.UrlInfo) => request(`${urlInfo.httpProtocol}//${urlInfo.hostPath}/hello`)
|
const hello = async(urlInfo: LX.Sync.UrlInfo) => request(`${urlInfo.httpProtocol}//${urlInfo.hostPath}/hello`)
|
||||||
.then(({ text }) => text == SYNC_CODE.helloMsg)
|
.then(({ text }) => text == SYNC_CODE.helloMsg)
|
||||||
@ -25,7 +26,7 @@ const getServerId = async(urlInfo: LX.Sync.UrlInfo) => request(`${urlInfo.httpPr
|
|||||||
})
|
})
|
||||||
|
|
||||||
const codeAuth = async(urlInfo: LX.Sync.UrlInfo, serverId: string, authCode: string) => {
|
const codeAuth = async(urlInfo: LX.Sync.UrlInfo, serverId: string, authCode: string) => {
|
||||||
let key = ''.padStart(16, Buffer.from(authCode).toString('hex'))
|
let key = toMD5(authCode).substring(0, 16)
|
||||||
// const iv = Buffer.from(key.split('').reverse().join('')).toString('base64')
|
// const iv = Buffer.from(key.split('').reverse().join('')).toString('base64')
|
||||||
key = Buffer.from(key).toString('base64')
|
key = Buffer.from(key).toString('base64')
|
||||||
let { publicKey, privateKey } = await generateRsaKey()
|
let { publicKey, privateKey } = await generateRsaKey()
|
||||||
|
@ -1,13 +1,15 @@
|
|||||||
import { encryptMsg, decryptMsg } from './utils'
|
import { encryptMsg, decryptMsg } from './utils'
|
||||||
import * as modules from './modules'
|
import { modules, callObj } from './modules'
|
||||||
// import { action as commonAction } from '@/store/modules/common'
|
// import { action as commonAction } from '@/store/modules/common'
|
||||||
// import { getStore } from '@/store'
|
// import { getStore } from '@/store'
|
||||||
import registerSyncListHandler from './syncList'
|
// import registerSyncListHandler from './syncList'
|
||||||
import log from '../log'
|
import log from '../log'
|
||||||
import { SYNC_CLOSE_CODE, SYNC_CODE } from '@/config/constant'
|
import { SYNC_CLOSE_CODE, SYNC_CODE } from '@/config/constant'
|
||||||
import { aesEncrypt } from '../utils'
|
import { aesEncrypt } from '../utils'
|
||||||
import { setSyncStatus } from '@/core/sync'
|
import { setSyncStatus } from '@/core/sync'
|
||||||
import { dateFormat } from '@/utils/common'
|
import { dateFormat } from '@/utils/common'
|
||||||
|
import { createMsg2call } from 'message2call'
|
||||||
|
import { toast } from '@/utils/tools'
|
||||||
|
|
||||||
let status: LX.Sync.Status = {
|
let status: LX.Sync.Status = {
|
||||||
status: false,
|
status: false,
|
||||||
@ -26,8 +28,14 @@ export const sendSyncMessage = (message: string) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const handleConnection = (socket: LX.Sync.Socket) => {
|
const handleConnection = (socket: LX.Sync.Socket) => {
|
||||||
for (const moduleInit of Object.values(modules)) {
|
for (const { registerEvent } of Object.values(modules)) {
|
||||||
moduleInit(socket)
|
registerEvent(socket)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleDisconnection = () => {
|
||||||
|
for (const { unregisterEvent } of Object.values(modules)) {
|
||||||
|
unregisterEvent()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -118,47 +126,75 @@ const heartbeatTools = {
|
|||||||
let client: LX.Sync.Socket | null
|
let client: LX.Sync.Socket | null
|
||||||
// let listSyncPromise: Promise<void>
|
// let listSyncPromise: Promise<void>
|
||||||
export const connect = (urlInfo: LX.Sync.UrlInfo, keyInfo: LX.Sync.KeyInfo) => {
|
export const connect = (urlInfo: LX.Sync.UrlInfo, keyInfo: LX.Sync.KeyInfo) => {
|
||||||
client = new WebSocket(`${urlInfo.wsProtocol}//${urlInfo.hostPath}?i=${encodeURIComponent(keyInfo.clientId)}&t=${encodeURIComponent(aesEncrypt(SYNC_CODE.msgConnect, keyInfo.key))}`) as LX.Sync.Socket
|
client = new WebSocket(`${urlInfo.wsProtocol}//${urlInfo.hostPath}/socket?i=${encodeURIComponent(keyInfo.clientId)}&t=${encodeURIComponent(aesEncrypt(SYNC_CODE.msgConnect, keyInfo.key))}`) as LX.Sync.Socket
|
||||||
client.data = {
|
client.data = {
|
||||||
keyInfo,
|
keyInfo,
|
||||||
urlInfo,
|
urlInfo,
|
||||||
}
|
}
|
||||||
heartbeatTools.connect(client)
|
heartbeatTools.connect(client)
|
||||||
|
|
||||||
// listSyncPromise = registerSyncListHandler(socket)
|
|
||||||
let events: Partial<{ [K in keyof LX.Sync.ActionSyncSendType]: Array<(data: LX.Sync.ActionSyncSendType[K]) => (void | Promise<void>)> }> = {}
|
|
||||||
let closeEvents: Array<(err: Error) => (void | Promise<void>)> = []
|
let closeEvents: Array<(err: Error) => (void | Promise<void>)> = []
|
||||||
|
|
||||||
|
const message2read = createMsg2call({
|
||||||
|
funcsObj: {
|
||||||
|
...callObj,
|
||||||
|
list_sync_finished() {
|
||||||
|
log.info('sync list success')
|
||||||
|
toast('Sync successfully')
|
||||||
|
client!.isReady = true
|
||||||
|
handleConnection(client as LX.Sync.Socket)
|
||||||
|
sendSyncStatus({
|
||||||
|
status: true,
|
||||||
|
message: '',
|
||||||
|
})
|
||||||
|
heartbeatTools.failedNum = 0
|
||||||
|
},
|
||||||
|
},
|
||||||
|
timeout: 120 * 1000,
|
||||||
|
sendMessage(data) {
|
||||||
|
void encryptMsg(keyInfo, JSON.stringify(data)).then((data) => {
|
||||||
|
client?.send(data)
|
||||||
|
}).catch((err) => {
|
||||||
|
log.error('encrypt msg error: ', err)
|
||||||
|
client?.close(SYNC_CLOSE_CODE.failed)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
onCallBeforeParams(rawArgs) {
|
||||||
|
return [client, ...rawArgs]
|
||||||
|
},
|
||||||
|
onError(error, path, groupName) {
|
||||||
|
const name = groupName ?? ''
|
||||||
|
log.error(`sync call ${name} ${path.join('.')} error:`, error)
|
||||||
|
if (groupName == null) return
|
||||||
|
client?.close(SYNC_CLOSE_CODE.failed)
|
||||||
|
sendSyncStatus({
|
||||||
|
status: false,
|
||||||
|
message: error.message,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
client.remoteSyncList = message2read.createSyncRemote('list')
|
||||||
|
|
||||||
client.addEventListener('message', ({ data }) => {
|
client.addEventListener('message', ({ data }) => {
|
||||||
if (data == 'ping') return
|
if (data == 'ping') return
|
||||||
if (typeof data === 'string') {
|
if (typeof data === 'string') {
|
||||||
let syncData: LX.Sync.ActionSync
|
void decryptMsg(keyInfo, data).then((data) => {
|
||||||
|
let syncData: LX.Sync.ServerActions
|
||||||
try {
|
try {
|
||||||
syncData = JSON.parse(decryptMsg(keyInfo, data))
|
syncData = JSON.parse(data)
|
||||||
} catch {
|
} catch (err) {
|
||||||
|
log.error('parse msg error: ', err)
|
||||||
|
client?.close(SYNC_CLOSE_CODE.failed)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const handlers = events[syncData.action]
|
message2read.onMessage(syncData)
|
||||||
if (handlers) {
|
}).catch((error) => {
|
||||||
// @ts-expect-error
|
log.error('decrypt msg error: ', error)
|
||||||
for (const handler of handlers) void handler(syncData.data)
|
client?.close(SYNC_CLOSE_CODE.failed)
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
client.onRemoteEvent = function(eventName, handler) {
|
|
||||||
let eventArr = events[eventName]
|
|
||||||
if (!eventArr) events[eventName] = eventArr = []
|
|
||||||
// let eventArr = events.get(eventName)
|
|
||||||
// if (!eventArr) events.set(eventName, eventArr = [])
|
|
||||||
eventArr.push(handler)
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
eventArr!.splice(eventArr!.indexOf(handler), 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
client.sendData = function(eventName, data, callback) {
|
|
||||||
client?.send(encryptMsg(keyInfo, JSON.stringify({ action: eventName, data })))
|
|
||||||
callback?.()
|
|
||||||
}
|
|
||||||
client.onClose = function(handler: typeof closeEvents[number]) {
|
client.onClose = function(handler: typeof closeEvents[number]) {
|
||||||
closeEvents.push(handler)
|
closeEvents.push(handler)
|
||||||
return () => {
|
return () => {
|
||||||
@ -166,6 +202,7 @@ export const connect = (urlInfo: LX.Sync.UrlInfo, keyInfo: LX.Sync.KeyInfo) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const initMessage = 'Wait syncing...'
|
||||||
client.addEventListener('open', () => {
|
client.addEventListener('open', () => {
|
||||||
log.info('connect')
|
log.info('connect')
|
||||||
// const store = getStore()
|
// const store = getStore()
|
||||||
@ -173,39 +210,14 @@ export const connect = (urlInfo: LX.Sync.UrlInfo, keyInfo: LX.Sync.KeyInfo) => {
|
|||||||
client!.isReady = false
|
client!.isReady = false
|
||||||
sendSyncStatus({
|
sendSyncStatus({
|
||||||
status: false,
|
status: false,
|
||||||
message: 'Wait syncing...',
|
message: initMessage,
|
||||||
})
|
|
||||||
void registerSyncListHandler(client as LX.Sync.Socket).then(() => {
|
|
||||||
log.info('sync list success')
|
|
||||||
handleConnection(client as LX.Sync.Socket)
|
|
||||||
log.info('register list sync service success')
|
|
||||||
client!.isReady = true
|
|
||||||
sendSyncStatus({
|
|
||||||
status: true,
|
|
||||||
message: '',
|
|
||||||
})
|
|
||||||
heartbeatTools.failedNum = 0
|
|
||||||
}).catch(err => {
|
|
||||||
if (err.message == 'closed') {
|
|
||||||
sendSyncStatus({
|
|
||||||
status: false,
|
|
||||||
message: '',
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
console.log(err)
|
|
||||||
log.r_error(err.stack)
|
|
||||||
sendSyncStatus({
|
|
||||||
status: false,
|
|
||||||
message: err.message,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
client.addEventListener('close', ({ code }) => {
|
client.addEventListener('close', ({ code }) => {
|
||||||
const err = new Error('closed')
|
const err = new Error('closed')
|
||||||
for (const handler of closeEvents) void handler(err)
|
for (const handler of closeEvents) void handler(err)
|
||||||
|
handleDisconnection()
|
||||||
closeEvents = []
|
closeEvents = []
|
||||||
events = {}
|
|
||||||
switch (code) {
|
switch (code) {
|
||||||
case SYNC_CLOSE_CODE.normal:
|
case SYNC_CLOSE_CODE.normal:
|
||||||
// case SYNC_CLOSE_CODE.failed:
|
// case SYNC_CLOSE_CODE.failed:
|
||||||
@ -213,6 +225,15 @@ export const connect = (urlInfo: LX.Sync.UrlInfo, keyInfo: LX.Sync.KeyInfo) => {
|
|||||||
status: false,
|
status: false,
|
||||||
message: '',
|
message: '',
|
||||||
})
|
})
|
||||||
|
break
|
||||||
|
case SYNC_CLOSE_CODE.failed:
|
||||||
|
if (!status.message || status.message == initMessage) {
|
||||||
|
sendSyncStatus({
|
||||||
|
status: false,
|
||||||
|
message: 'failed',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
break
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
client.addEventListener('error', ({ message }) => {
|
client.addEventListener('error', ({ message }) => {
|
||||||
|
@ -1 +1,10 @@
|
|||||||
export { default as list } from './list'
|
import * as list from './list'
|
||||||
|
// export * as theme from './theme'
|
||||||
|
|
||||||
|
|
||||||
|
export const callObj = Object.assign({}, list.handler)
|
||||||
|
|
||||||
|
|
||||||
|
export const modules = {
|
||||||
|
list,
|
||||||
|
}
|
||||||
|
95
src/plugins/sync/client/modules/list/handler.ts
Normal file
95
src/plugins/sync/client/modules/list/handler.ts
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
import { handleRemoteListAction, getLocalListData, setLocalListData } from '../../../utils'
|
||||||
|
import log from '../../../log'
|
||||||
|
// import { SYNC_CLOSE_CODE } from '@/config/constant'
|
||||||
|
import { removeSyncModeEvent, selectSyncMode } from '@/core/sync'
|
||||||
|
import { toMD5 } from '@/utils/tools'
|
||||||
|
|
||||||
|
const logInfo = (eventName: string, success = false) => {
|
||||||
|
log.info(`[${eventName}]${eventName.replace('list:sync:list_sync_', '').replace(/_/g, ' ')}${success ? ' success' : ''}`)
|
||||||
|
}
|
||||||
|
// const logError = (eventName: string, err: Error) => {
|
||||||
|
// log.error(`[${eventName}]${eventName.replace('list:sync:list_sync_', '').replace(/_/g, ' ')} error: ${err.message}`)
|
||||||
|
// }
|
||||||
|
|
||||||
|
export const onListSyncAction = async(socket: LX.Sync.Socket, action: LX.Sync.ActionList) => {
|
||||||
|
if (!socket.isReady) return
|
||||||
|
await handleRemoteListAction(action)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const list_sync_get_md5 = async(socket: LX.Sync.Socket) => {
|
||||||
|
logInfo('list:sync:list_sync_get_md5')
|
||||||
|
return toMD5(JSON.stringify(await getLocalListData()))
|
||||||
|
}
|
||||||
|
|
||||||
|
export const list_sync_get_sync_mode = async(socket: LX.Sync.Socket) => {
|
||||||
|
logInfo('list:sync:list_sync_get_sync_mode')
|
||||||
|
const unsubscribe = socket.onClose(() => {
|
||||||
|
removeSyncModeEvent()
|
||||||
|
})
|
||||||
|
return selectSyncMode(socket.data.keyInfo.serverName).finally(unsubscribe)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const list_sync_get_list_data = async(socket: LX.Sync.Socket) => {
|
||||||
|
logInfo('list:sync:list_sync_get_list_data')
|
||||||
|
return getLocalListData()
|
||||||
|
}
|
||||||
|
|
||||||
|
export const list_sync_set_list_data = async(socket: LX.Sync.Socket, data: LX.Sync.ListData) => {
|
||||||
|
logInfo('list:sync:list_sync_set_list_data')
|
||||||
|
await setLocalListData(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// export default async(socket: LX.Sync.Socket) => new Promise<void>((resolve, reject) => {
|
||||||
|
// let listenEvents: Array<() => void> = []
|
||||||
|
// const unregisterEvents = () => {
|
||||||
|
// while (listenEvents.length) listenEvents.shift()?.()
|
||||||
|
// }
|
||||||
|
|
||||||
|
// socket.onClose(() => {
|
||||||
|
// unregisterEvents()
|
||||||
|
// removeSyncModeEvent()
|
||||||
|
// reject(new Error('closed'))
|
||||||
|
// })
|
||||||
|
// listenEvents.push(socket.onRemoteEvent('list:sync:list_sync_get_list_data', async() => {
|
||||||
|
// logInfo('list:sync:list_sync_get_list_data')
|
||||||
|
// socket?.sendData('list:sync:list_sync_get_list_data', await getLocalListData(), (err) => {
|
||||||
|
// if (err) {
|
||||||
|
// logError('list:sync:list_sync_get_list_data', err)
|
||||||
|
// socket.close(SYNC_CLOSE_CODE.failed)
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// logInfo('list:sync:list_sync_get_list_data', true)
|
||||||
|
// })
|
||||||
|
// }))
|
||||||
|
// listenEvents.push(socket.onRemoteEvent('list:sync:list_sync_get_sync_mode', async() => {
|
||||||
|
// logInfo('list:sync:list_sync_get_sync_mode')
|
||||||
|
// let mode: LX.Sync.Mode
|
||||||
|
// try {
|
||||||
|
// mode = await selectSyncMode(socket.data.keyInfo.serverName)
|
||||||
|
// } catch (err: unknown) {
|
||||||
|
// logError('list:sync:list_sync_get_sync_mode', err as Error)
|
||||||
|
// socket.close(SYNC_CLOSE_CODE.normal)
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// socket?.sendData('list:sync:list_sync_get_sync_mode', mode, (err) => {
|
||||||
|
// if (err) {
|
||||||
|
// logError('list:sync:list_sync_get_sync_mode', err)
|
||||||
|
// socket.close(SYNC_CLOSE_CODE.failed)
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// logInfo('list:sync:list_sync_get_sync_mode', true)
|
||||||
|
// })
|
||||||
|
// }))
|
||||||
|
// listenEvents.push(socket.onRemoteEvent('list:sync:list_sync_set_data', async(data) => {
|
||||||
|
// logInfo('list:sync:list_sync_set_data')
|
||||||
|
// await setLocalListData(data)
|
||||||
|
// logInfo('list:sync:list_sync_set_data', true)
|
||||||
|
// }))
|
||||||
|
// listenEvents.push(socket.onRemoteEvent('list:sync:finished', async() => {
|
||||||
|
// unregisterEvents()
|
||||||
|
// resolve()
|
||||||
|
// logInfo('list:sync:finished', true)
|
||||||
|
// toast('Sync successfully')
|
||||||
|
// }))
|
||||||
|
// })
|
@ -1,7 +1,4 @@
|
|||||||
import initOn from './on'
|
|
||||||
import initSend from './send'
|
|
||||||
|
|
||||||
export default (socket: LX.Sync.Socket) => {
|
export * as handler from './handler'
|
||||||
initOn(socket)
|
|
||||||
initSend(socket)
|
export * from './localEvent'
|
||||||
}
|
|
||||||
|
20
src/plugins/sync/client/modules/list/localEvent.ts
Normal file
20
src/plugins/sync/client/modules/list/localEvent.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import { registerListActionEvent } from '../../../utils'
|
||||||
|
|
||||||
|
let unregisterLocalListAction: (() => void) | null
|
||||||
|
|
||||||
|
export const registerEvent = (socket: LX.Sync.Socket) => {
|
||||||
|
// socket = _socket
|
||||||
|
// socket.onClose(() => {
|
||||||
|
// unregisterLocalListAction?.()
|
||||||
|
// unregisterLocalListAction = null
|
||||||
|
// })
|
||||||
|
unregisterLocalListAction = registerListActionEvent((action) => {
|
||||||
|
if (!socket?.isReady) return
|
||||||
|
void socket.remoteSyncList.onListSyncAction(action)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const unregisterEvent = () => {
|
||||||
|
unregisterLocalListAction?.()
|
||||||
|
unregisterLocalListAction = null
|
||||||
|
}
|
@ -1,9 +0,0 @@
|
|||||||
import { handleRemoteListAction } from '../../../utils'
|
|
||||||
|
|
||||||
export default (socket: LX.Sync.Socket) => {
|
|
||||||
socket.onRemoteEvent('list:sync:action', (action) => {
|
|
||||||
if (!socket.isReady) return
|
|
||||||
void handleRemoteListAction(action)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
@ -1,21 +0,0 @@
|
|||||||
import { registerListActionEvent } from '../../../utils'
|
|
||||||
|
|
||||||
let socket: LX.Sync.Socket | null
|
|
||||||
let unregisterLocalListAction: (() => void) | null
|
|
||||||
|
|
||||||
|
|
||||||
const sendListAction = (action: LX.Sync.ActionList) => {
|
|
||||||
// console.log('sendListAction', action.action)
|
|
||||||
if (!socket?.isReady) return
|
|
||||||
socket.sendData('list:sync:action', action)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default (_socket: LX.Sync.Socket) => {
|
|
||||||
socket = _socket
|
|
||||||
socket.onClose(() => {
|
|
||||||
socket = null
|
|
||||||
unregisterLocalListAction?.()
|
|
||||||
unregisterLocalListAction = null
|
|
||||||
})
|
|
||||||
unregisterLocalListAction = registerListActionEvent(sendListAction)
|
|
||||||
}
|
|
@ -1,79 +0,0 @@
|
|||||||
import log from '../log'
|
|
||||||
import { getLocalListData, setLocalListData } from '../utils'
|
|
||||||
// import { removeSelectModeListener, sendCloseSelectMode, sendSelectMode } from '@main/modules/winMain'
|
|
||||||
import { SYNC_CLOSE_CODE } from '@/config/constant'
|
|
||||||
import { removeSyncModeEvent, selectSyncMode } from '@/core/sync'
|
|
||||||
import { toast, toMD5 } from '@/utils/tools'
|
|
||||||
|
|
||||||
const logInfo = (eventName: keyof LX.Sync.ActionSyncSendType, success = false) => {
|
|
||||||
log.info(`[${eventName as string}]${eventName.replace('list:sync:list_sync_', '').replace(/_/g, ' ')}${success ? ' success' : ''}`)
|
|
||||||
}
|
|
||||||
const logError = (eventName: keyof LX.Sync.ActionSyncSendType, err: Error) => {
|
|
||||||
log.error(`[${eventName as string}]${eventName.replace('list:sync:list_sync_', '').replace(/_/g, ' ')} error: ${err.message}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default async(socket: LX.Sync.Socket) => new Promise<void>((resolve, reject) => {
|
|
||||||
let listenEvents: Array<() => void> = []
|
|
||||||
const unregisterEvents = () => {
|
|
||||||
while (listenEvents.length) listenEvents.shift()?.()
|
|
||||||
}
|
|
||||||
|
|
||||||
socket.onClose(() => {
|
|
||||||
unregisterEvents()
|
|
||||||
removeSyncModeEvent()
|
|
||||||
reject(new Error('closed'))
|
|
||||||
})
|
|
||||||
listenEvents.push(socket.onRemoteEvent('list:sync:list_sync_get_md5', async() => {
|
|
||||||
logInfo('list:sync:list_sync_get_md5')
|
|
||||||
const md5 = toMD5(JSON.stringify(await getLocalListData()))
|
|
||||||
socket?.sendData('list:sync:list_sync_get_md5', md5, (err) => {
|
|
||||||
if (err) {
|
|
||||||
logError('list:sync:list_sync_get_md5', err)
|
|
||||||
socket.close(SYNC_CLOSE_CODE.failed)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
logInfo('list:sync:list_sync_get_md5', true)
|
|
||||||
})
|
|
||||||
}))
|
|
||||||
listenEvents.push(socket.onRemoteEvent('list:sync:list_sync_get_list_data', async() => {
|
|
||||||
logInfo('list:sync:list_sync_get_list_data')
|
|
||||||
socket?.sendData('list:sync:list_sync_get_list_data', await getLocalListData(), (err) => {
|
|
||||||
if (err) {
|
|
||||||
logError('list:sync:list_sync_get_list_data', err)
|
|
||||||
socket.close(SYNC_CLOSE_CODE.failed)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
logInfo('list:sync:list_sync_get_list_data', true)
|
|
||||||
})
|
|
||||||
}))
|
|
||||||
listenEvents.push(socket.onRemoteEvent('list:sync:list_sync_get_sync_mode', async() => {
|
|
||||||
logInfo('list:sync:list_sync_get_sync_mode')
|
|
||||||
let mode: LX.Sync.Mode
|
|
||||||
try {
|
|
||||||
mode = await selectSyncMode(socket.data.keyInfo.serverName)
|
|
||||||
} catch (err: unknown) {
|
|
||||||
logError('list:sync:list_sync_get_sync_mode', err as Error)
|
|
||||||
socket.close(SYNC_CLOSE_CODE.normal)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
socket?.sendData('list:sync:list_sync_get_sync_mode', mode, (err) => {
|
|
||||||
if (err) {
|
|
||||||
logError('list:sync:list_sync_get_sync_mode', err)
|
|
||||||
socket.close(SYNC_CLOSE_CODE.failed)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
logInfo('list:sync:list_sync_get_sync_mode', true)
|
|
||||||
})
|
|
||||||
}))
|
|
||||||
listenEvents.push(socket.onRemoteEvent('list:sync:list_sync_set_data', async(data) => {
|
|
||||||
logInfo('list:sync:list_sync_set_data')
|
|
||||||
await setLocalListData(data)
|
|
||||||
logInfo('list:sync:list_sync_set_data', true)
|
|
||||||
}))
|
|
||||||
listenEvents.push(socket.onRemoteEvent('list:sync:finished', async() => {
|
|
||||||
unregisterEvents()
|
|
||||||
resolve()
|
|
||||||
logInfo('list:sync:finished', true)
|
|
||||||
toast('Sync successfully')
|
|
||||||
}))
|
|
||||||
})
|
|
@ -1,4 +1,5 @@
|
|||||||
// import { generateKeyPair } from 'crypto'
|
// import { generateKeyPair } from 'crypto'
|
||||||
|
import { gzipStringToBase64, unGzipFromBase64 } from '@/utils/nativeModules/gzip'
|
||||||
import BackgroundTimer from 'react-native-background-timer'
|
import BackgroundTimer from 'react-native-background-timer'
|
||||||
|
|
||||||
export const request = async(url: string, { timeout = 10000, ...options }: RequestInit & { timeout?: number } = {}) => {
|
export const request = async(url: string, { timeout = 10000, ...options }: RequestInit & { timeout?: number } = {}) => {
|
||||||
@ -68,14 +69,18 @@ export { generateRsaKey } from '@/utils/nativeModules/crypto'
|
|||||||
// })
|
// })
|
||||||
|
|
||||||
|
|
||||||
export const encryptMsg = (keyInfo: LX.Sync.KeyInfo, msg: string): string => {
|
export const encryptMsg = async(keyInfo: LX.Sync.KeyInfo, msg: string): Promise<string> => {
|
||||||
return msg
|
return msg.length > 1024
|
||||||
|
? 'cg_' + await gzipStringToBase64(msg)
|
||||||
|
: msg
|
||||||
// if (!keyInfo) return ''
|
// if (!keyInfo) return ''
|
||||||
// return aesEncrypt(msg, keyInfo.key, keyInfo.iv)
|
// return aesEncrypt(msg, keyInfo.key, keyInfo.iv)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const decryptMsg = (keyInfo: LX.Sync.KeyInfo, enMsg: string): string => {
|
export const decryptMsg = async(keyInfo: LX.Sync.KeyInfo, enMsg: string): Promise<string> => {
|
||||||
return enMsg
|
return enMsg.substring(0, 3) == 'cg_'
|
||||||
|
? unGzipFromBase64(enMsg.replace('cg_', ''))
|
||||||
|
: enMsg
|
||||||
// if (!keyInfo) return ''
|
// if (!keyInfo) return ''
|
||||||
// let msg = ''
|
// let msg = ''
|
||||||
// try {
|
// try {
|
||||||
|
70
src/types/sync.d.ts
vendored
70
src/types/sync.d.ts
vendored
@ -1,65 +1,6 @@
|
|||||||
declare global {
|
declare global {
|
||||||
namespace LX {
|
namespace LX {
|
||||||
namespace Sync {
|
namespace Sync {
|
||||||
|
|
||||||
interface Enable {
|
|
||||||
enable: boolean
|
|
||||||
port: string
|
|
||||||
}
|
|
||||||
|
|
||||||
interface SyncActionBase <A> {
|
|
||||||
action: A
|
|
||||||
}
|
|
||||||
interface SyncActionData<A, D> extends SyncActionBase<A> {
|
|
||||||
data: D
|
|
||||||
}
|
|
||||||
type SyncAction<A, D = undefined> = D extends undefined ? SyncActionBase<A> : SyncActionData<A, D>
|
|
||||||
|
|
||||||
// type SyncMainWindowActions = SyncAction<'select_mode', KeyInfo>
|
|
||||||
// | SyncAction<'close_select_mode'>
|
|
||||||
// | SyncAction<'status', Status>
|
|
||||||
|
|
||||||
// type SyncServiceActions = SyncAction<'select_mode', Mode>
|
|
||||||
// | SyncAction<'get_status'>
|
|
||||||
// | SyncAction<'generate_code'>
|
|
||||||
// | SyncAction<'enable', Enable>
|
|
||||||
|
|
||||||
type ActionList = SyncAction<'list_data_overwrite', LX.List.ListActionDataOverwrite>
|
|
||||||
| SyncAction<'list_create', LX.List.ListActionAdd>
|
|
||||||
| SyncAction<'list_remove', LX.List.ListActionRemove>
|
|
||||||
| SyncAction<'list_update', LX.List.ListActionUpdate>
|
|
||||||
| SyncAction<'list_update_position', LX.List.ListActionUpdatePosition>
|
|
||||||
| SyncAction<'list_music_add', LX.List.ListActionMusicAdd>
|
|
||||||
| SyncAction<'list_music_move', LX.List.ListActionMusicMove>
|
|
||||||
| SyncAction<'list_music_remove', LX.List.ListActionMusicRemove>
|
|
||||||
| SyncAction<'list_music_update', LX.List.ListActionMusicUpdate>
|
|
||||||
| SyncAction<'list_music_update_position', LX.List.ListActionMusicUpdatePosition>
|
|
||||||
| SyncAction<'list_music_overwrite', LX.List.ListActionMusicOverwrite>
|
|
||||||
| SyncAction<'list_music_clear', LX.List.ListActionMusicClear>
|
|
||||||
|
|
||||||
type ActionSync = SyncAction<'list:sync:list_sync_get_md5', string>
|
|
||||||
| SyncAction<'list:sync:list_sync_get_list_data', ListData>
|
|
||||||
| SyncAction<'list:sync:list_sync_get_sync_mode', Mode>
|
|
||||||
| SyncAction<'list:sync:action', ActionList>
|
|
||||||
// | SyncAction<'finished'>
|
|
||||||
|
|
||||||
type ActionSyncType = Actions<ActionSync>
|
|
||||||
|
|
||||||
type ActionSyncSend = SyncAction<'list:sync:list_sync_get_md5'>
|
|
||||||
| SyncAction<'list:sync:list_sync_get_list_data'>
|
|
||||||
| SyncAction<'list:sync:list_sync_get_sync_mode'>
|
|
||||||
| SyncAction<'list:sync:list_sync_set_data', LX.Sync.ListData>
|
|
||||||
| SyncAction<'list:sync:action', ActionList>
|
|
||||||
| SyncAction<'list:sync:finished'>
|
|
||||||
|
|
||||||
type ActionSyncSendType = Actions<ActionSyncSend>
|
|
||||||
|
|
||||||
|
|
||||||
interface List {
|
|
||||||
action: string
|
|
||||||
data: any
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Status {
|
interface Status {
|
||||||
status: boolean
|
status: boolean
|
||||||
message: string
|
message: string
|
||||||
@ -89,18 +30,9 @@ declare global {
|
|||||||
urlInfo: UrlInfo
|
urlInfo: UrlInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
onRemoteEvent: <T extends keyof LX.Sync.ActionSyncSendType>(
|
|
||||||
eventName: T,
|
|
||||||
handler: (data: LX.Sync.ActionSyncSendType[T]) => (void | Promise<void>)
|
|
||||||
) => () => void
|
|
||||||
|
|
||||||
onClose: (handler: (err: Error) => (void | Promise<void>)) => () => void
|
onClose: (handler: (err: Error) => (void | Promise<void>)) => () => void
|
||||||
|
|
||||||
sendData: <T extends keyof LX.Sync.ActionSyncType>(
|
remoteSyncList: LX.Sync.ServerActions
|
||||||
eventName: T,
|
|
||||||
data?: LX.Sync.ActionSyncType[T],
|
|
||||||
callback?: (err?: Error) => void
|
|
||||||
) => void
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
31
src/types/sync_common.d.ts
vendored
Normal file
31
src/types/sync_common.d.ts
vendored
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
declare namespace LX {
|
||||||
|
namespace Sync {
|
||||||
|
type ActionList = SyncAction<'list_data_overwrite', LX.List.ListActionDataOverwrite>
|
||||||
|
| SyncAction<'list_create', LX.List.ListActionAdd>
|
||||||
|
| SyncAction<'list_remove', LX.List.ListActionRemove>
|
||||||
|
| SyncAction<'list_update', LX.List.ListActionUpdate>
|
||||||
|
| SyncAction<'list_update_position', LX.List.ListActionUpdatePosition>
|
||||||
|
| SyncAction<'list_music_add', LX.List.ListActionMusicAdd>
|
||||||
|
| SyncAction<'list_music_move', LX.List.ListActionMusicMove>
|
||||||
|
| SyncAction<'list_music_remove', LX.List.ListActionMusicRemove>
|
||||||
|
| SyncAction<'list_music_update', LX.List.ListActionMusicUpdate>
|
||||||
|
| SyncAction<'list_music_update_position', LX.List.ListActionMusicUpdatePosition>
|
||||||
|
| SyncAction<'list_music_overwrite', LX.List.ListActionMusicOverwrite>
|
||||||
|
| SyncAction<'list_music_clear', LX.List.ListActionMusicClear>
|
||||||
|
|
||||||
|
type ServerActions = WarpPromiseRecord<{
|
||||||
|
onListSyncAction: (action: LX.Sync.ActionList) => void
|
||||||
|
}>
|
||||||
|
|
||||||
|
type ClientActions = WarpPromiseRecord<{
|
||||||
|
onListSyncAction: (action: LX.Sync.ActionList) => void
|
||||||
|
list_sync_get_md5: () => string
|
||||||
|
list_sync_get_sync_mode: () => Mode
|
||||||
|
list_sync_get_list_data: () => ListData
|
||||||
|
list_sync_set_list_data: (data: ListData) => void
|
||||||
|
list_sync_finished: () => void
|
||||||
|
}>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
10
src/types/utils.d.ts
vendored
10
src/types/utils.d.ts
vendored
@ -14,3 +14,13 @@ type ForwardRefFn<R> = <P = {}>(p: React.PropsWithChildren<P> & React.RefAttribu
|
|||||||
type Actions<T extends { action: string, data?: any }> = {
|
type Actions<T extends { action: string, data?: any }> = {
|
||||||
[U in T as U['action']]: 'data' extends keyof U ? U['data'] : undefined
|
[U in T as U['action']]: 'data' extends keyof U ? U['data'] : undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type WarpPromiseValue<T> = T extends ((...args: infer P) => Promise<infer R>)
|
||||||
|
? ((...args: P) => Promise<R>)
|
||||||
|
: T extends ((...args: infer P2) => infer R2)
|
||||||
|
? ((...args: P2) => Promise<R2>)
|
||||||
|
: Promise<T>
|
||||||
|
|
||||||
|
type WarpPromiseRecord<T extends Record<string, any>> = {
|
||||||
|
[K in keyof T]: WarpPromiseValue<T[K]>
|
||||||
|
}
|
||||||
|
@ -2,11 +2,19 @@ import { NativeModules } from 'react-native'
|
|||||||
|
|
||||||
const { GzipModule } = NativeModules
|
const { GzipModule } = NativeModules
|
||||||
|
|
||||||
export const gzip = (sourceFilePath: string, targetFilePath: string) => {
|
export const gzipFile = async(sourceFilePath: string, targetFilePath: string): Promise<string> => {
|
||||||
console.log(sourceFilePath, targetFilePath)
|
console.log(sourceFilePath, targetFilePath)
|
||||||
return GzipModule.gzip(sourceFilePath, targetFilePath, true)
|
return GzipModule.gzipFile(sourceFilePath, targetFilePath, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ungzip = (sourceFilePath: string, targetFilePath: string) => {
|
export const unGzipFile = async(sourceFilePath: string, targetFilePath: string): Promise<string> => {
|
||||||
return GzipModule.unGzip(sourceFilePath, targetFilePath, true)
|
return GzipModule.unGzipFile(sourceFilePath, targetFilePath, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const gzipStringToBase64 = async(data: string): Promise<string> => {
|
||||||
|
return GzipModule.gzipStringToBase64(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const unGzipFromBase64 = async(data: string): Promise<string> => {
|
||||||
|
return GzipModule.unGzipFromBase64(data)
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ import { Platform, ToastAndroid, BackHandler, Linking, Dimensions, Alert, Appear
|
|||||||
// import ExtraDimensions from 'react-native-extra-dimensions-android'
|
// import ExtraDimensions from 'react-native-extra-dimensions-android'
|
||||||
import Clipboard from '@react-native-clipboard/clipboard'
|
import Clipboard from '@react-native-clipboard/clipboard'
|
||||||
import { storageDataPrefix } from '@/config/constant'
|
import { storageDataPrefix } from '@/config/constant'
|
||||||
import { gzip, ungzip } from '@/utils/nativeModules/gzip'
|
import { gzipFile, unGzipFile } from '@/utils/nativeModules/gzip'
|
||||||
import { temporaryDirectoryPath, unlink } from '@/utils/fs'
|
import { temporaryDirectoryPath, unlink } from '@/utils/fs'
|
||||||
import { getSystemLocales, isNotificationsEnabled, openNotificationPermissionActivity, readFile, shareText, writeFile } from '@/utils/nativeModules/utils'
|
import { getSystemLocales, isNotificationsEnabled, openNotificationPermissionActivity, readFile, shareText, writeFile } from '@/utils/nativeModules/utils'
|
||||||
import musicSdk from '@/utils/musicSdk'
|
import musicSdk from '@/utils/musicSdk'
|
||||||
@ -146,7 +146,7 @@ export const handleSaveFile = async(path: string, data: any) => {
|
|||||||
// const buffer = gzip(data)
|
// const buffer = gzip(data)
|
||||||
const tempFilePath = `${temporaryDirectoryPath}/tempFile.json`
|
const tempFilePath = `${temporaryDirectoryPath}/tempFile.json`
|
||||||
await writeFile(tempFilePath, JSON.stringify(data))
|
await writeFile(tempFilePath, JSON.stringify(data))
|
||||||
await gzip(tempFilePath, path)
|
await gzipFile(tempFilePath, path)
|
||||||
await unlink(tempFilePath)
|
await unlink(tempFilePath)
|
||||||
}
|
}
|
||||||
export const handleReadFile = async<T = unknown>(path: string): Promise<T> => {
|
export const handleReadFile = async<T = unknown>(path: string): Promise<T> => {
|
||||||
@ -156,7 +156,7 @@ export const handleReadFile = async<T = unknown>(path: string): Promise<T> => {
|
|||||||
data = await readFile(path)
|
data = await readFile(path)
|
||||||
} else {
|
} else {
|
||||||
const tempFilePath = `${temporaryDirectoryPath}/tempFile.json`
|
const tempFilePath = `${temporaryDirectoryPath}/tempFile.json`
|
||||||
await ungzip(path, tempFilePath)
|
await unGzipFile(path, tempFilePath)
|
||||||
data = await readFile(tempFilePath)
|
data = await readFile(tempFilePath)
|
||||||
await unlink(tempFilePath)
|
await unlink(tempFilePath)
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user