Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: linux support #251

Merged
merged 17 commits into from
Apr 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading