rip out media_kit, use better_player and audioplayers

This also let us rip out cocoapods stuff.

Needs testing on Windows, MacOS, and ideally, iOS.
This commit is contained in:
Henry Hiles 2026-11-11 23:25:28 -05:00
commit 37a2c150f3
Signed by: Henry-Hiles
SSH key fingerprint: SHA256:VKQUdS31Q90KvX7EkKMHMBpUspcmItAh86a+v7PGiIs
28 changed files with 375 additions and 604 deletions

View file

@ -45,6 +45,17 @@ jobs:
flutter pub run build_runner build flutter pub run build_runner build
flutter build windows --dart-define=GIT_COMMIT=${{ github.sha }} flutter build windows --dart-define=GIT_COMMIT=${{ github.sha }}
# better_player plays videos with libmpv, but doesn't bundle it
- name: Download libmpv
shell: powershell
run: |
$archive = "$env:RUNNER_TEMP\mpv.7z"
Invoke-WebRequest "https://github.com/shinchiro/mpv-winbuild-cmake/releases/download/20261021/mpv-dev-x86_64-20261021-git-e471496c56.7z" -OutFile $archive
if ((Get-FileHash $archive -Algorithm SHA256).Hash -ne "efc719eff3dc5954b4a5efc800632808bcec01cc9034940dd14e5eb7fe9ea633") {
throw "libmpv checksum mismatch"
}
7z e $archive libmpv-2.dll -obuild/windows/x64/runner/Release
- name: Copy MinGW runtime DLLs - name: Copy MinGW runtime DLLs
shell: msys2 {0} shell: msys2 {0}
run: | run: |

View file

@ -75,7 +75,7 @@ This installs Nexus system-wide. With `enableNotifications`, it also installs KU
#### Linux #### Linux
- On NixOS: Either use direnv and `direnv allow`, or `nix develop` - On NixOS: Either use direnv and `direnv allow`, or `nix develop`
- On other distros: Install Flutter, Go, Git, Libclang, Libass, MPV, and Glibc. Do not use any Snap packages, they cause various compilation issues. - On other distros: Install Flutter, Go, Git, Libclang, MPV, GStreamer (with its base, good and libav plugins), and Glibc. Do not use any Snap packages, they cause various compilation issues.
#### Windows #### Windows
@ -83,12 +83,11 @@ You will need:
- Flutter - Flutter
- Git - Git
- Libass
- MPV
- Go - Go
- Visual Studio 2022 (Desktop development with C++) - Visual Studio 2022 (Desktop development with C++)
- [MSYS2/MinGW-w64 GCC](https://www.msys2.org/) (for CGO) - [MSYS2/MinGW-w64 GCC](https://www.msys2.org/) (for CGO)
- [LLVM/Clang + libclang](https://clang.llvm.org/get_started.html) (for `ffigen`) - [LLVM/Clang + libclang](https://clang.llvm.org/get_started.html) (for `ffigen`)
- `libmpv-2.dll`, for playing videos, next to `nexus.exe` (CI uses a build from [mpv-winbuild-cmake](https://github.com/shinchiro/mpv-winbuild-cmake/releases))
On Windows, make sure these are available in your shell `PATH`: On Windows, make sure these are available in your shell `PATH`:

View file

@ -1,2 +1 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
#include "Generated.xcconfig" #include "Generated.xcconfig"

View file

@ -1,2 +1 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
#include "Generated.xcconfig" #include "Generated.xcconfig"

View file

@ -1,43 +0,0 @@
# Uncomment this line to define a global platform for your project
# platform :ios, '13.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
project 'Runner', {
'Debug' => :debug,
'Profile' => :release,
'Release' => :release,
}
def flutter_root
generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
unless File.exist?(generated_xcode_build_settings_path)
raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
end
File.foreach(generated_xcode_build_settings_path) do |line|
matches = line.match(/FLUTTER_ROOT\=(.*)/)
return matches[1].strip if matches
end
raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
end
require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
flutter_ios_podfile_setup
target 'Runner' do
use_frameworks!
flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
target 'RunnerTests' do
inherit! :search_paths
end
end
post_install do |installer|
installer.pods_project.targets.each do |target|
flutter_additional_ios_build_settings(target)
end
end

View file

@ -1,28 +0,0 @@
PODS:
- Flutter (1.0.0)
- media_kit_libs_ios_video (1.0.4):
- Flutter
- media_kit_video (0.0.1):
- Flutter
DEPENDENCIES:
- Flutter (from `Flutter`)
- media_kit_libs_ios_video (from `.symlinks/plugins/media_kit_libs_ios_video/ios`)
- media_kit_video (from `.symlinks/plugins/media_kit_video/ios`)
EXTERNAL SOURCES:
Flutter:
:path: Flutter
media_kit_libs_ios_video:
:path: ".symlinks/plugins/media_kit_libs_ios_video/ios"
media_kit_video:
:path: ".symlinks/plugins/media_kit_video/ios"
SPEC CHECKSUMS:
Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
media_kit_libs_ios_video: 5a18affdb97d1f5d466dc79988b13eff6c5e2854
media_kit_video: 1746e198cb697d1ffb734b1d05ec429d1fcd1474
PODFILE CHECKSUM: 3c63482e143d1b91d2d2560aee9fb04ecc74ac7e
COCOAPODS: 1.17.0

View file

@ -8,13 +8,11 @@
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
2ADFEE6C745FFB2100CC9AFD /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8156A45F510382D558AED93A /* Pods_RunnerTests.framework */; };
331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; }; 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; };
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
7884E8682EC3CC0700C636F2 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7884E8672EC3CC0400C636F2 /* SceneDelegate.swift */; }; 7884E8682EC3CC0700C636F2 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7884E8672EC3CC0400C636F2 /* SceneDelegate.swift */; };
78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */ = {isa = PBXBuildFile; productRef = 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */; }; 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */ = {isa = PBXBuildFile; productRef = 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */; };
88D55A5F1C7625122EBEE29D /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 82F9A3759658343C81A0EA4C /* Pods_Runner.framework */; };
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
@ -46,19 +44,14 @@
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; }; 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
22DE4310DAFBE211CDF700B1 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; }; 331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; };
331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
363A89F6FA09BCCBDED440CE /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
63516CF65A79A5DDDE64B0D1 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = "<group>"; };
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; }; 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; }; 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
7884E8672EC3CC0400C636F2 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; }; 7884E8672EC3CC0400C636F2 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; };
78E0A7A72DC9AD7400C4905E /* FlutterGeneratedPluginSwiftPackage */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = FlutterGeneratedPluginSwiftPackage; path = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; sourceTree = "<group>"; }; 78E0A7A72DC9AD7400C4905E /* FlutterGeneratedPluginSwiftPackage */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = FlutterGeneratedPluginSwiftPackage; path = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; sourceTree = "<group>"; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
8156A45F510382D558AED93A /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
82F9A3759658343C81A0EA4C /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
@ -66,9 +59,6 @@
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; }; 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; }; 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
AEB96D8976BD46D6860C7041 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
B8B599FDB72E4713FB7B5A44 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = "<group>"; };
FE5E4162842555A83F8A7FD7 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */ /* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */ /* Begin PBXFrameworksBuildPhase section */
@ -76,7 +66,6 @@
isa = PBXFrameworksBuildPhase; isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
2ADFEE6C745FFB2100CC9AFD /* Pods_RunnerTests.framework in Frameworks */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@ -85,27 +74,12 @@
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */, 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */,
88D55A5F1C7625122EBEE29D /* Pods_Runner.framework in Frameworks */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
/* End PBXFrameworksBuildPhase section */ /* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */ /* Begin PBXGroup section */
1CFFAE23B502BCCEB2B191A3 /* Pods */ = {
isa = PBXGroup;
children = (
AEB96D8976BD46D6860C7041 /* Pods-Runner.debug.xcconfig */,
363A89F6FA09BCCBDED440CE /* Pods-Runner.release.xcconfig */,
22DE4310DAFBE211CDF700B1 /* Pods-Runner.profile.xcconfig */,
B8B599FDB72E4713FB7B5A44 /* Pods-RunnerTests.debug.xcconfig */,
FE5E4162842555A83F8A7FD7 /* Pods-RunnerTests.release.xcconfig */,
63516CF65A79A5DDDE64B0D1 /* Pods-RunnerTests.profile.xcconfig */,
);
name = Pods;
path = Pods;
sourceTree = "<group>";
};
331C8082294A63A400263BE5 /* RunnerTests */ = { 331C8082294A63A400263BE5 /* RunnerTests */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@ -114,15 +88,6 @@
path = RunnerTests; path = RunnerTests;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
594EB1F65336CF9C7958F6FA /* Frameworks */ = {
isa = PBXGroup;
children = (
82F9A3759658343C81A0EA4C /* Pods_Runner.framework */,
8156A45F510382D558AED93A /* Pods_RunnerTests.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
9740EEB11CF90186004384FC /* Flutter */ = { 9740EEB11CF90186004384FC /* Flutter */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@ -142,8 +107,6 @@
97C146F01CF9000F007C117D /* Runner */, 97C146F01CF9000F007C117D /* Runner */,
97C146EF1CF9000F007C117D /* Products */, 97C146EF1CF9000F007C117D /* Products */,
331C8082294A63A400263BE5 /* RunnerTests */, 331C8082294A63A400263BE5 /* RunnerTests */,
1CFFAE23B502BCCEB2B191A3 /* Pods */,
594EB1F65336CF9C7958F6FA /* Frameworks */,
); );
sourceTree = "<group>"; sourceTree = "<group>";
}; };
@ -179,7 +142,6 @@
isa = PBXNativeTarget; isa = PBXNativeTarget;
buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
buildPhases = ( buildPhases = (
EE82EC529EB6C0F1FA480659 /* [CP] Check Pods Manifest.lock */,
331C807D294A63A400263BE5 /* Sources */, 331C807D294A63A400263BE5 /* Sources */,
331C807F294A63A400263BE5 /* Resources */, 331C807F294A63A400263BE5 /* Resources */,
8C0A36760DA8B873D8F6DDCE /* Frameworks */, 8C0A36760DA8B873D8F6DDCE /* Frameworks */,
@ -198,14 +160,12 @@
isa = PBXNativeTarget; isa = PBXNativeTarget;
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = ( buildPhases = (
0830C6758DA3ECA81FB12386 /* [CP] Check Pods Manifest.lock */,
9740EEB61CF901F6004384FC /* Run Script */, 9740EEB61CF901F6004384FC /* Run Script */,
97C146EA1CF9000F007C117D /* Sources */, 97C146EA1CF9000F007C117D /* Sources */,
97C146EB1CF9000F007C117D /* Frameworks */, 97C146EB1CF9000F007C117D /* Frameworks */,
97C146EC1CF9000F007C117D /* Resources */, 97C146EC1CF9000F007C117D /* Resources */,
9705A1C41CF9048500538489 /* Embed Frameworks */, 9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */,
1D165FDFB9D0D939B041966D /* [CP] Embed Pods Frameworks */,
); );
buildRules = ( buildRules = (
); );
@ -283,45 +243,6 @@
/* End PBXResourcesBuildPhase section */ /* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */
0830C6758DA3ECA81FB12386 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
1D165FDFB9D0D939B041966D /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
isa = PBXShellScriptBuildPhase; isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1; alwaysOutOfDate = 1;
@ -353,28 +274,6 @@
shellPath = /bin/sh; shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
}; };
EE82EC529EB6C0F1FA480659 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */ /* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */
@ -502,7 +401,6 @@
}; };
331C8088294A63A400263BE5 /* Debug */ = { 331C8088294A63A400263BE5 /* Debug */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
baseConfigurationReference = B8B599FDB72E4713FB7B5A44 /* Pods-RunnerTests.debug.xcconfig */;
buildSettings = { buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)"; BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
@ -520,7 +418,6 @@
}; };
331C8089294A63A400263BE5 /* Release */ = { 331C8089294A63A400263BE5 /* Release */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
baseConfigurationReference = FE5E4162842555A83F8A7FD7 /* Pods-RunnerTests.release.xcconfig */;
buildSettings = { buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)"; BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
@ -536,7 +433,6 @@
}; };
331C808A294A63A400263BE5 /* Profile */ = { 331C808A294A63A400263BE5 /* Profile */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
baseConfigurationReference = 63516CF65A79A5DDDE64B0D1 /* Pods-RunnerTests.profile.xcconfig */;
buildSettings = { buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)"; BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;

View file

@ -4,7 +4,4 @@
<FileRef <FileRef
location = "group:Runner.xcodeproj"> location = "group:Runner.xcodeproj">
</FileRef> </FileRef>
<FileRef
location = "group:Pods/Pods.xcodeproj">
</FileRef>
</Workspace> </Workspace>

View file

@ -4,7 +4,6 @@ import "package:dynamic_color/dynamic_color.dart";
import "package:flutter/foundation.dart"; import "package:flutter/foundation.dart";
import "package:flutter_riverpod/flutter_riverpod.dart"; import "package:flutter_riverpod/flutter_riverpod.dart";
import "package:material_emoji_picker/material_emoji_picker.dart"; import "package:material_emoji_picker/material_emoji_picker.dart";
import "package:media_kit/media_kit.dart";
import "package:nexus/controllers/active_account.dart"; import "package:nexus/controllers/active_account.dart";
import "package:nexus/controllers/client.dart"; import "package:nexus/controllers/client.dart";
import "package:nexus/controllers/client_state.dart"; import "package:nexus/controllers/client_state.dart";
@ -65,7 +64,6 @@ void showError(Object error, [StackTrace? stackTrace]) {
void main(List<String> args) async { void main(List<String> args) async {
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
MediaKit.ensureInitialized();
LicenseRegistry.addLicense(() => .fromIterable(fontLicenses)); LicenseRegistry.addLicense(() => .fromIterable(fontLicenses));

View file

@ -1,9 +1,9 @@
import "dart:async"; import "dart:async";
import "package:audioplayers/audioplayers.dart" as audioplayers;
import "package:material_ui/material_ui.dart"; import "package:material_ui/material_ui.dart";
import "package:flutter_hooks/flutter_hooks.dart"; import "package:flutter_hooks/flutter_hooks.dart";
import "package:hooks_riverpod/hooks_riverpod.dart"; import "package:hooks_riverpod/hooks_riverpod.dart";
import "package:media_kit/media_kit.dart";
import "package:nexus/controllers/client.dart"; import "package:nexus/controllers/client.dart";
import "package:nexus/helpers/extensions/save_attachment.dart"; import "package:nexus/helpers/extensions/save_attachment.dart";
import "package:nexus/models/info/audio.dart"; import "package:nexus/models/info/audio.dart";
@ -18,9 +18,7 @@ class const AudioPlayer(
}) extends HookConsumerWidget { }) extends HookConsumerWidget {
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
final player = useMemoized( final player = useMemoized(audioplayers.AudioPlayer.new);
() => Player(configuration: .new(bufferSize: 128 * 1024 * 1024)),
);
final loaded = useState(false); final loaded = useState(false);
useEffect(() { useEffect(() {
@ -29,19 +27,39 @@ class const AudioPlayer(
.read(ClientController.provider.notifier) .read(ClientController.provider.notifier)
.downloadMedia(.new(mxc: uri, encrypted: encrypted)); .downloadMedia(.new(mxc: uri, encrypted: encrypted));
if (!context.mounted) return; if (!context.mounted) return;
await player.open(Media(audio.path), play: false); // Keeps the audio loaded after it finishes, so it can be replayed
await player.setReleaseMode(.stop);
await player.setSource(audioplayers.DeviceFileSource(audio.path));
if (context.mounted) loaded.value = true; if (context.mounted) loaded.value = true;
}); });
return player.dispose; return player.dispose;
}, []); }, []);
final state = useStream(
player.onPlayerStateChanged,
initialData: player.state,
).requireData;
final position = useStream(
player.onPositionChanged,
initialData: Duration.zero,
).requireData;
final duration = useStream(
player.onDurationChanged,
initialData: Duration.zero,
).requireData;
final playing = state == .playing;
return Card( return Card(
color: Theme.of(context).colorScheme.surfaceContainerHigh, color: Theme.of(context).colorScheme.surfaceContainerHigh,
child: Padding( child: Padding(
padding: .symmetric(horizontal: 8), padding: .symmetric(horizontal: 8),
child: PlayerControls( child: PlayerControls(
player, playing: playing,
position: position,
duration: duration,
onPlayPause: playing ? player.pause : player.resume,
onSeek: player.seek,
loading: !loaded.value, loading: !loaded.value,
size: info?.size, size: info?.size,
actions: .new([ actions: .new([

View file

@ -1,39 +1,25 @@
import "dart:math"; import "dart:math";
import "package:fast_immutable_collections/fast_immutable_collections.dart"; import "package:fast_immutable_collections/fast_immutable_collections.dart";
import "package:flutter_hooks/flutter_hooks.dart";
import "package:material_ui/material_ui.dart"; import "package:material_ui/material_ui.dart";
import "package:media_kit/media_kit.dart";
import "package:nexus/helpers/extensions/size_to_string.dart"; import "package:nexus/helpers/extensions/size_to_string.dart";
/// Play/pause, position and seeking controls for a media_kit [Player]. /// Play/pause, position and seeking controls, for any player.
class const PlayerControls( class const PlayerControls({
final Player player, { required final bool playing,
required final Duration position,
required final Duration duration,
required final VoidCallback onPlayPause,
required final ValueChanged<Duration> onSeek,
final bool loading = false, final bool loading = false,
final bool buffering = false,
final Color? color, final Color? color,
final int? size, final int? size,
final IList<Widget> actions = const .empty(), final IList<Widget> actions = const .empty(),
super.key, super.key,
}) extends HookWidget { }) extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final playing = useStream(
player.stream.playing,
initialData: player.state.playing,
).requireData;
final buffering = useStream(
player.stream.buffering,
initialData: player.state.buffering,
).requireData;
final position = useStream(
player.stream.position,
initialData: player.state.position,
).requireData;
final duration = useStream(
player.stream.duration,
initialData: player.state.duration,
).requireData;
String format(Duration duration) { String format(Duration duration) {
final minutes = duration.inMinutes final minutes = duration.inMinutes
.remainder(60) .remainder(60)
@ -61,7 +47,7 @@ class const PlayerControls(
child: CircularProgressIndicator(padding: .all(12), color: color), child: CircularProgressIndicator(padding: .all(12), color: color),
) )
: IconButton( : IconButton(
onPressed: player.playOrPause, onPressed: onPlayPause,
color: color, color: color,
tooltip: playing ? "Pause" : "Play", tooltip: playing ? "Pause" : "Play",
icon: Icon(playing ? Icons.pause_circle : Icons.play_circle), icon: Icon(playing ? Icons.pause_circle : Icons.play_circle),
@ -73,7 +59,7 @@ class const PlayerControls(
value: min(position.inMilliseconds.toDouble(), max), value: min(position.inMilliseconds.toDouble(), max),
onChanged: loading onChanged: loading
? null ? null
: (value) => player.seek(.new(milliseconds: value.toInt())), : (value) => onSeek(.new(milliseconds: value.toInt())),
); );
return LayoutBuilder( return LayoutBuilder(

View file

@ -1,13 +1,12 @@
import "dart:async"; import "dart:async";
import "package:better_player/better_player.dart";
import "package:material_ui/material_ui.dart"; import "package:material_ui/material_ui.dart";
import "package:hooks_riverpod/hooks_riverpod.dart"; import "package:hooks_riverpod/hooks_riverpod.dart";
import "package:nexus/controllers/client.dart"; import "package:nexus/controllers/client.dart";
import "package:nexus/helpers/extensions/save_attachment.dart"; import "package:nexus/helpers/extensions/save_attachment.dart";
import "package:nexus/models/info/video.dart"; import "package:nexus/models/info/video.dart";
import "package:flutter_hooks/flutter_hooks.dart"; import "package:flutter_hooks/flutter_hooks.dart";
import "package:media_kit/media_kit.dart";
import "package:media_kit_video/media_kit_video.dart";
import "package:nexus/widgets/players/player_controls.dart"; import "package:nexus/widgets/players/player_controls.dart";
class const VideoPlayer( class const VideoPlayer(
@ -19,38 +18,45 @@ class const VideoPlayer(
}) extends HookConsumerWidget { }) extends HookConsumerWidget {
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
final player = useMemoized( final aspectRatio = switch (info) {
() => Player(configuration: .new(bufferSize: 128 * 1024 * 1024)), VideoInfo(:final width?, :final height?) when width > 0 && height > 0 =>
); width / height,
final controller = useMemoized(() => VideoController(player)); _ => 16 / 9,
};
final loaded = useState(false); final loaded = useState(false);
final controller = useMemoized(
() => BetterPlayerController(
PlayerConfiguration(
aspectRatio: aspectRatio,
fit: .contain,
controlsConfiguration: PlayerControlsConfiguration(
playerTheme: .custom,
customControlsBuilder: (controller, _) => HookBuilder(
builder: (context) {
final isLoaded = useValueListenable(loaded);
final value = useState(controller.videoPlayerValue);
useEffect(() { useEffect(() {
scheduleMicrotask(() async { void listener() => value.value = controller.videoPlayerValue;
final video = await ref controller.addVideoListener(listener);
.read(ClientController.provider.notifier) return () => controller.removeVideoListener(listener);
.downloadMedia(.new(mxc: uri, encrypted: encrypted)); }, [controller]);
if (!context.mounted) return;
await player.open(Media(video.path), play: false);
if (context.mounted) loaded.value = true;
});
return player.dispose; final playing = value.value?.isPlaying ?? false;
}, []);
final controls = useCallback( // better_player doesn't use material_ui, so this Material is needed.
(VideoState _) => return Material(
// Until media_kit moves to material_ui, this Material is needed.
Material(
type: .transparency, type: .transparency,
child: ValueListenableBuilder( child: Stack(
valueListenable: loaded,
builder: (_, loaded, _) => Stack(
children: [ children: [
Positioned.fill( Positioned.fill(
child: GestureDetector( child: GestureDetector(
behavior: .opaque, behavior: .opaque,
onTap: loaded ? player.playOrPause : null, onTap: isLoaded
? playing
? controller.pause
: controller.play
: null,
), ),
), ),
Positioned( Positioned(
@ -86,28 +92,29 @@ class const VideoPlayer(
child: Padding( child: Padding(
padding: const .only(left: 4, right: 4, top: 16), padding: const .only(left: 4, right: 4, top: 16),
child: PlayerControls( child: PlayerControls(
player, playing: playing,
loading: !loaded, buffering: value.value?.isBuffering ?? false,
position: value.value?.position ?? .zero,
duration: value.value?.duration ?? .zero,
onPlayPause: playing
? controller.pause
: controller.play,
onSeek: controller.seekTo,
loading: !isLoaded,
size: info?.size, size: info?.size,
color: Colors.white, color: Colors.white,
actions: .new([ actions: .new([
Builder( IconButton(
builder: (context) { onPressed: controller.toggleFullScreen,
final fullscreen = isFullscreen(context);
return IconButton(
onPressed: () => toggleFullscreen(context),
color: Colors.white, color: Colors.white,
tooltip: fullscreen tooltip: controller.isFullScreen
? "Exit fullscreen" ? "Exit fullscreen"
: "Fullscreen", : "Fullscreen",
icon: Icon( icon: Icon(
fullscreen controller.isFullScreen
? Icons.fullscreen_exit ? Icons.fullscreen_exit
: Icons.fullscreen, : Icons.fullscreen,
), ),
);
},
), ),
]), ]),
), ),
@ -116,16 +123,26 @@ class const VideoPlayer(
), ),
], ],
), ),
);
},
),
),
), ),
), ),
[uri, encrypted, filename, info],
); );
final aspectRatio = switch (info) { useEffect(() {
VideoInfo(:final width?, :final height?) when width > 0 && height > 0 => scheduleMicrotask(() async {
width / height, final video = await ref
_ => 16 / 9, .read(ClientController.provider.notifier)
}; .downloadMedia(.new(mxc: uri, encrypted: encrypted));
if (!context.mounted) return;
await controller.setupDataSource(.file(video.path));
if (context.mounted) loaded.value = true;
});
return null;
}, []);
return ConstrainedBox( return ConstrainedBox(
constraints: const .new(maxHeight: 300), constraints: const .new(maxHeight: 300),
@ -133,7 +150,7 @@ class const VideoPlayer(
aspectRatio: aspectRatio, aspectRatio: aspectRatio,
child: ClipRRect( child: ClipRRect(
borderRadius: .circular(12), borderRadius: .circular(12),
child: Video(controller: controller, controls: controls), child: BetterPlayer(controller: controller),
), ),
), ),
); );

View file

@ -7,11 +7,11 @@
#include "generated_plugin_registrant.h" #include "generated_plugin_registrant.h"
#include <app_links_linux/app_links_plugin_linux.h> #include <app_links_linux/app_links_plugin_linux.h>
#include <audioplayers_linux/audioplayers_linux_plugin.h>
#include <better_player_linux/better_player_linux_plugin.h>
#include <dynamic_color/dynamic_color_plugin.h> #include <dynamic_color/dynamic_color_plugin.h>
#include <file_selector_linux/file_selector_plugin.h> #include <file_selector_linux/file_selector_plugin.h>
#include <location/location_plugin.h> #include <location/location_plugin.h>
#include <media_kit_libs_linux/media_kit_libs_linux_plugin.h>
#include <media_kit_video/media_kit_video_plugin.h>
#include <pasteboard/pasteboard_plugin.h> #include <pasteboard/pasteboard_plugin.h>
#include <url_launcher_linux/url_launcher_plugin.h> #include <url_launcher_linux/url_launcher_plugin.h>
@ -19,6 +19,12 @@ void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) app_links_linux_registrar = g_autoptr(FlPluginRegistrar) app_links_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "AppLinksPluginLinux"); fl_plugin_registry_get_registrar_for_plugin(registry, "AppLinksPluginLinux");
app_links_plugin_linux_register_with_registrar(app_links_linux_registrar); app_links_plugin_linux_register_with_registrar(app_links_linux_registrar);
g_autoptr(FlPluginRegistrar) audioplayers_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "AudioplayersLinuxPlugin");
audioplayers_linux_plugin_register_with_registrar(audioplayers_linux_registrar);
g_autoptr(FlPluginRegistrar) better_player_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "BetterPlayerLinuxPlugin");
better_player_linux_plugin_register_with_registrar(better_player_linux_registrar);
g_autoptr(FlPluginRegistrar) dynamic_color_registrar = g_autoptr(FlPluginRegistrar) dynamic_color_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "DynamicColorPlugin"); fl_plugin_registry_get_registrar_for_plugin(registry, "DynamicColorPlugin");
dynamic_color_plugin_register_with_registrar(dynamic_color_registrar); dynamic_color_plugin_register_with_registrar(dynamic_color_registrar);
@ -28,12 +34,6 @@ void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) location_registrar = g_autoptr(FlPluginRegistrar) location_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "LocationPlugin"); fl_plugin_registry_get_registrar_for_plugin(registry, "LocationPlugin");
location_plugin_register_with_registrar(location_registrar); location_plugin_register_with_registrar(location_registrar);
g_autoptr(FlPluginRegistrar) media_kit_libs_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "MediaKitLibsLinuxPlugin");
media_kit_libs_linux_plugin_register_with_registrar(media_kit_libs_linux_registrar);
g_autoptr(FlPluginRegistrar) media_kit_video_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "MediaKitVideoPlugin");
media_kit_video_plugin_register_with_registrar(media_kit_video_registrar);
g_autoptr(FlPluginRegistrar) pasteboard_registrar = g_autoptr(FlPluginRegistrar) pasteboard_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "PasteboardPlugin"); fl_plugin_registry_get_registrar_for_plugin(registry, "PasteboardPlugin");
pasteboard_plugin_register_with_registrar(pasteboard_registrar); pasteboard_plugin_register_with_registrar(pasteboard_registrar);

View file

@ -4,11 +4,11 @@
list(APPEND FLUTTER_PLUGIN_LIST list(APPEND FLUTTER_PLUGIN_LIST
app_links_linux app_links_linux
audioplayers_linux
better_player_linux
dynamic_color dynamic_color
file_selector_linux file_selector_linux
location location
media_kit_libs_linux
media_kit_video
pasteboard pasteboard
url_launcher_linux url_launcher_linux
) )

View file

@ -7,12 +7,14 @@
stdenv.cc stdenv.cc
go go
git git
ffmpeg-headless
libGL libGL
(flutter.override { (flutter.override {
# audioplayers plays audio through GStreamer
extraPkgConfigPackages = [ extraPkgConfigPackages = [
mpv-unwrapped gst_all_1.gstreamer
libass gst_all_1.gst-plugins-base
]; ];
}) })
] ]
@ -25,6 +27,27 @@
]; ];
env = lib.optionalAttrs pkgs.stdenv.isLinux { env = lib.optionalAttrs pkgs.stdenv.isLinux {
LD_LIBRARY_PATH = "./build/native_assets/linux:${lib.makeLibraryPath [ pkgs.zlib ]}"; # better_player loads libmpv at runtime to play videos
LD_LIBRARY_PATH = "./build/native_assets/linux:${
lib.makeLibraryPath [
pkgs.zlib
pkgs.mpv-unwrapped
]
}";
# audioplayers links GStreamer by name only, without its library directory
LIBRARY_PATH = lib.makeLibraryPath [
pkgs.gst_all_1.gstreamer
pkgs.gst_all_1.gst-plugins-base
pkgs.glib
];
GST_PLUGIN_SYSTEM_PATH_1_0 = lib.makeSearchPathOutput "lib" "lib/gstreamer-1.0" (
with pkgs.gst_all_1;
[
gstreamer
gst-plugins-base
gst-plugins-good
gst-libav
]
);
}; };
} }

View file

@ -2,7 +2,8 @@
lib, lib,
callPackage, callPackage,
mpv-unwrapped, mpv-unwrapped,
libass, gst_all_1,
ffmpeg-headless,
libxext, libxext,
libxfixes, libxfixes,
libclang, libclang,
@ -39,8 +40,9 @@ flutter.buildFlutterApplication {
nativeBuildInputs = [ (callPackage ./hook_pkg_config.nix { }) ]; nativeBuildInputs = [ (callPackage ./hook_pkg_config.nix { }) ];
buildInputs = [ buildInputs = [
mpv-unwrapped # audioplayers plays audio through GStreamer
libass gst_all_1.gstreamer
gst_all_1.gst-plugins-base
# nativeapi includes XInput2.h, which needs these, but xi.pc only lists them privately # nativeapi includes XInput2.h, which needs these, but xi.pc only lists them privately
libxext libxext
libxfixes libxfixes
@ -54,6 +56,7 @@ flutter.buildFlutterApplication {
gitHashes = { gitHashes = {
linkify = "sha256-TpMD6+0zyY6i9l+6d8ErnVufmepCv362rCtnbOht/z4="; linkify = "sha256-TpMD6+0zyY6i9l+6d8ErnVufmepCv362rCtnbOht/z4=";
location = "sha256-zWnffXiK1LD4Ln23coPozBjYfTlhZmBhC5Z8mCdAgwQ=";
fab_m3e = "sha256-5V9m6yY0jimx2gh7lQHYg4HDKQ4Wrl0ATmBhw5DkQMI="; fab_m3e = "sha256-5V9m6yY0jimx2gh7lQHYg4HDKQ4Wrl0ATmBhw5DkQMI=";
fluttertagger = "sha256-tHUNU2+fBPkq5UK5YRi3SWs1zF2PIPz6RgSbwignU/I="; fluttertagger = "sha256-tHUNU2+fBPkq5UK5YRi3SWs1zF2PIPz6RgSbwignU/I=";
m3e_design = "sha256-5V9m6yY0jimx2gh7lQHYg4HDKQ4Wrl0ATmBhw5DkQMI="; m3e_design = "sha256-5V9m6yY0jimx2gh7lQHYg4HDKQ4Wrl0ATmBhw5DkQMI=";
@ -69,8 +72,24 @@ flutter.buildFlutterApplication {
install -D assets/bundled/icon.svg $out/share/icons/hicolor/scalable/apps/nexus.svg install -D assets/bundled/icon.svg $out/share/icons/hicolor/scalable/apps/nexus.svg
install -Dm755 linux/*.desktop -t $out/share/applications install -Dm755 linux/*.desktop -t $out/share/applications
install -Dm644 linux/*.service -t $out/share/dbus-1/services install -Dm644 linux/*.service -t $out/share/dbus-1/services
# better_player loads libmpv at runtime to play videos, and audioplayers
# needs GStreamer's plugins to decode audio
# gomuks uses ffmpeg and ffprobe for video thumbnails and metadata
wrapProgram $out/bin/nexus \ wrapProgram $out/bin/nexus \
--suffix LD_LIBRARY_PATH : $out/app/nexus/lib --suffix LD_LIBRARY_PATH : $out/app/nexus/lib \
--prefix LD_LIBRARY_PATH : ${lib.makeLibraryPath [ mpv-unwrapped ]} \
--prefix PATH : ${lib.makeBinPath [ ffmpeg-headless ]} \
--prefix GST_PLUGIN_SYSTEM_PATH_1_0 : ${
lib.makeSearchPathOutput "lib" "lib/gstreamer-1.0" (
with gst_all_1;
[
gstreamer
gst-plugins-base
gst-plugins-good
gst-libav
]
)
}
''; '';
meta = { meta = {

View file

@ -1,5 +1,6 @@
#include "my_application.h" #include "my_application.h"
#include <clocale>
#include <cstdlib> #include <cstdlib>
#include <flutter_linux/flutter_linux.h> #include <flutter_linux/flutter_linux.h>
@ -301,6 +302,9 @@ static void my_application_startup(
G_APPLICATION_CLASS( G_APPLICATION_CLASS(
my_application_parent_class) my_application_parent_class)
->startup(application); ->startup(application);
// mpv hates normal locales
setlocale(LC_NUMERIC, "C");
} }
static void my_application_shutdown( static void my_application_shutdown(

View file

@ -1,2 +1 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
#include "ephemeral/Flutter-Generated.xcconfig" #include "ephemeral/Flutter-Generated.xcconfig"

View file

@ -1,2 +1 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
#include "ephemeral/Flutter-Generated.xcconfig" #include "ephemeral/Flutter-Generated.xcconfig"

View file

@ -6,12 +6,12 @@ import FlutterMacOS
import Foundation import Foundation
import app_links import app_links
import audioplayers_darwin
import better_player_macos
import dynamic_color import dynamic_color
import file_selector_macos import file_selector_macos
import flutter_local_notifications import flutter_local_notifications
import location import location
import media_kit_libs_macos_video
import media_kit_video
import package_info_plus import package_info_plus
import pasteboard import pasteboard
import shared_preferences_foundation import shared_preferences_foundation
@ -20,12 +20,12 @@ import wakelock_plus
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
AppLinksMacosPlugin.register(with: registry.registrar(forPlugin: "AppLinksMacosPlugin")) AppLinksMacosPlugin.register(with: registry.registrar(forPlugin: "AppLinksMacosPlugin"))
AudioplayersDarwinPlugin.register(with: registry.registrar(forPlugin: "AudioplayersDarwinPlugin"))
BetterPlayerPlugin.register(with: registry.registrar(forPlugin: "BetterPlayerPlugin"))
DynamicColorPlugin.register(with: registry.registrar(forPlugin: "DynamicColorPlugin")) DynamicColorPlugin.register(with: registry.registrar(forPlugin: "DynamicColorPlugin"))
FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin")) FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin"))
FlutterLocalNotificationsPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalNotificationsPlugin")) FlutterLocalNotificationsPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalNotificationsPlugin"))
LocationPlugin.register(with: registry.registrar(forPlugin: "LocationPlugin")) LocationPlugin.register(with: registry.registrar(forPlugin: "LocationPlugin"))
MediaKitLibsMacosVideoPlugin.register(with: registry.registrar(forPlugin: "MediaKitLibsMacosVideoPlugin"))
MediaKitVideoPlugin.register(with: registry.registrar(forPlugin: "MediaKitVideoPlugin"))
FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin")) FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin"))
PasteboardPlugin.register(with: registry.registrar(forPlugin: "PasteboardPlugin")) PasteboardPlugin.register(with: registry.registrar(forPlugin: "PasteboardPlugin"))
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))

Some files were not shown because too many files have changed in this diff Show more