Skip to content

Commit

Permalink
feat: linux support (miru-project#251)
Browse files Browse the repository at this point in the history
* init linux support

* support loading multiple extension

fix async handling

* fix:android issue

* update register setting

add ./vcode to .gitnore

* Update prbuild.yml

* Update prbuild.yml

* Update prbuild.yml

* chnage `flutter_js` dependencies

* downgrade `flutter` to `3.16.8 `

* feat: linux btserver support

* add external playing option for linux

-mpv( with header and subtitle support)
-vlc

* simplify code

* add some comment

* change back to btserver address

-fix typo

* Delete settings.json

* Delete lib/.vscode/launch.json

* Delete extension.js
  • Loading branch information
appdevelpo authored Apr 16, 2024
1 parent e4bd46c commit 8599d0d
Show file tree
Hide file tree
Showing 12 changed files with 614 additions and 175 deletions.
28 changes: 26 additions & 2 deletions .github/workflows/prbuild.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:
- name: Flutter action
uses: subosito/flutter-action@v2
with:
flutter-version: 3.16.8
flutter-version: 3.16.8
channel: stable
- name: Decode keystore
run: |
Expand Down Expand Up @@ -64,7 +64,7 @@ jobs:
- uses: actions/checkout@v3
- uses: subosito/[email protected]
with:
flutter-version: 3.16.8
flutter-version: 3.16.8
channel: stable
- name: Install project dependencies
run: flutter pub get
Expand All @@ -79,3 +79,27 @@ jobs:
with:
path: "build/windows/x64/runner/Miru-App"
name: Miru-pr-${{ github.event.pull_request.number }}-windows.zip

build-and-release-linux:
runs-on: ubuntu-latest
steps:
- name: Install Dependencies
run: sudo apt-get install ninja-build build-essential libgtk-3-dev libmpv-dev mpv
- uses: actions/checkout@v3
- uses: subosito/[email protected]
with:
flutter-version: 3.16.8
channel: stable
- name: Install project dependencies
run: flutter pub get
- name: Build artifacts
run: flutter build linux --release
- name: Rename Release Directory Name to Miru-App # 为了解压缩后更好看一点
run: |
mv build/linux/x64/release/bundle build/linux/x64/release/Miru-App
# 发布安装包
- name: Upload Artifact
uses: actions/upload-artifact@v3
with:
path: "build/linux/x64/release/Miru-App"
name: Miru-pr-${{ github.event.pull_request.number }}-linux.zip
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ migrate_working_dir/
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/
.vscode/

# Flutter/Dart/Pub related
**/doc/api/
Expand Down
3 changes: 2 additions & 1 deletion lib/controllers/detail_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -382,7 +382,8 @@ class DetailPageController extends GetxController {
await launchMobileExternalPlayer(watchData.url, player);
return;
}
await launchDesktopExternalPlayer(watchData.url, player);
await launchDesktopExternalPlayer(watchData.url, player,
watchData.headers ?? {}, watchData.subtitles ?? []);
return;
} catch (e) {
showPlatformSnackbar(
Expand Down
136 changes: 136 additions & 0 deletions lib/data/services/extension_jscore_plugin.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
import 'dart:async';
import 'dart:convert';

import 'package:flutter_js/flutter_js.dart';
import 'package:miru_app/utils/log.dart';

const dartBridgeMessageName = 'DART_BRIDGE_MESSAGE_NAME';

class JsBridge {
final JavascriptRuntime jsRuntime;
int _messageCounter = 0;
static final Map<int, Completer> _pendingRequests = {};
final Object? Function(Object? value)? toEncodable;
static final Map<String, Future<dynamic> Function(dynamic message)>
_handlers = {};
JsBridge({
required this.jsRuntime,
this.toEncodable,
}) {
final bridgeScriptEvalResult = jsRuntime.evaluate(jsBridgeJs);
if (bridgeScriptEvalResult.isError) {
logger.info('Error eval bridge script');
}
final windowEvalResult =
jsRuntime.evaluate('var window = global = globalThis;');
if (windowEvalResult.isError) {
logger.info('Error eval window script');
}
jsRuntime.onMessage(dartBridgeMessageName, (message) {
_onMessage(message);
});
}

_onMessage(dynamic message) async {
if (message['isRequest']) {
final handler = _handlers[message['name']];
if (handler == null) {
logger.info('Error: no handlers for message $message');
} else {
final result = await handler(message['args']);
final jsResult = jsRuntime.evaluate(
'onMessageFromDart(false, ${message['callId']}, "${message['name']}",${jsonEncode(result, toEncodable: toEncodable)})');
if (jsResult.isError) {
logger.info('Error sending message to JS: $jsResult');
}
}
} else {
final completer = _pendingRequests.remove(message['callId']);
if (completer == null) {
logger.info('Error: no completer for response for message $message');
} else {
completer.complete(message['result']);
}
}
}

sendMessage(String name, dynamic message) async {
if (_messageCounter > 999999999) {
_messageCounter = 0;
}
_messageCounter += 1;
final completer = Completer();
_pendingRequests[_messageCounter] = completer;
final jsResult = jsRuntime.evaluate(
'window.onMessageFromDart(true, $_messageCounter, "$name",${jsonEncode(message, toEncodable: toEncodable)})');
if (jsResult.isError) {
logger.info('Error sending message to JS: $jsResult');
}

return completer.future;
}

// final _handlers = {};

setHandler(String name, Future<dynamic> Function(dynamic message) handler) {
_handlers[name] = handler;
}
}

const jsBridgeJs = '''
globalThis.DartBridge = (() => {
let callId = 0;
const DART_BRIDGE_MESSAGE_NAME = '$dartBridgeMessageName';
globalThis.onMessageFromDart = async (isRequest, callId, name, args) => {
if (isRequest) {
if (handlers[name]) {
sendMessage(DART_BRIDGE_MESSAGE_NAME, JSON.stringify({
isRequest: false,
callId,
name,
result: await handlers[name](args),
}));
}
}
else {
const pendingResolve = pendingRequests[callId];
delete pendingRequests[callId];
if (pendingResolve) {
pendingResolve(args);
}
}
return null;
};
const handlers = {};
const pendingRequests = {};
return {
sendMessage: async (name, args) => {
if (callId > 999999999) {
callId = 0;
}
callId += 1;
sendMessage(DART_BRIDGE_MESSAGE_NAME, JSON.stringify({
isRequest: true,
callId,
name,
args,
}),call=((res)=>{}));
return new Promise((resolve) => {
pendingRequests[callId] = resolve;
call(resolve)
});
},
setHandler: (name, handler) => {
handlers[name] = handler;
},
resolveRequest: (callId, result) => {
sendMessage(DART_BRIDGE_MESSAGE_NAME, JSON.stringify({
isRequest: false,
callId,
result,
}));
},
};
})();
global = globalThis;
''';
Loading

0 comments on commit 8599d0d

Please sign in to comment.