Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
fc01d85
Add Windows ARM 64 build support
jgershgorin Feb 20, 2026
9b3978e
Stick Flutter SDK in master channel to older version to fix breaking …
jgershgorin Feb 20, 2026
e78e50d
Merge branch 'develop' into WINARM64
talynone Feb 21, 2026
e457b7f
Merge branch 'develop' into WINARM64
talynone Feb 22, 2026
79e96ea
Merge branch 'develop' into WINARM64
talynone Feb 25, 2026
0efeac0
Merge branch 'develop' into WINARM64
talynone Feb 25, 2026
8cd66a2
Merge branch 'develop' into WINARM64
talynone Mar 6, 2026
3b8983a
Merge branch 'develop' into WINARM64
talynone Mar 9, 2026
1fe632c
Merge branch 'develop' into WINARM64
talynone Mar 11, 2026
4478488
Pin Master flutter version
talynone Mar 13, 2026
a5ecbf6
Merge branch 'WINARM64' of https://github.com/talynone/Fladder into W…
talynone Mar 13, 2026
c98a88b
Pin new master flutter version
talynone Mar 13, 2026
7affffc
Merge branch 'develop' into WINARM64
talynone Mar 14, 2026
fee5c1c
Merge branch 'develop' into WINARM64
talynone Mar 16, 2026
07b3b9f
Merge branch 'develop' into WINARM64
talynone Mar 16, 2026
afea03b
Merge branch 'develop' into WINARM64
talynone Mar 17, 2026
98ee719
Merge branch 'develop' into WINARM64
talynone Mar 18, 2026
0970909
Merge branch 'develop' into WINARM64
talynone Mar 19, 2026
21676de
Merge branch 'develop' into WINARM64
talynone Mar 29, 2026
6403f23
Merge branch 'develop' into WINARM64
talynone Apr 7, 2026
d5c34cd
Merge branch 'develop' into WINARM64
talynone Apr 8, 2026
23e00d0
Merge branch 'develop' into WINARM64
talynone Apr 11, 2026
5418ea9
Merge branch 'develop' into WINARM64
talynone Apr 13, 2026
92c30c3
Merge branch 'develop' into WINARM64
talynone Apr 13, 2026
a6c7086
Merge branch 'develop' into WINARM64
talynone Apr 24, 2026
02d0f0d
Merge branch 'develop' into WINARM64
talynone May 8, 2026
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
64 changes: 64 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,47 @@ jobs:
name: fladder-windows-installer
path: windows\Output\fladder_setup.exe

build-windows-arm64:
runs-on: windows-11-arm
needs: [fetch-info]

steps:
- name: Checkout repository
uses: actions/checkout@v4.1.1

- name: Set up Flutter
uses: subosito/flutter-action@v2.19.0
with:
channel: "master"
flutter-version: "010cd4f383" # Newer version may having breaking change with page_transition / CupertinoPageTransitionsBuilder
cache: true
cache-key: "flutter-:os:-:channel:-:version:-:arch:-:hash:"
cache-path: "${{ runner.tool_cache }}/flutter/:channel:-:version:-:arch:"

- name: Get dependencies
run: flutter pub get

- name: Build Windows ARM64 EXE
run: flutter build windows --build-number=${{ github.run_number }}

- name: Compile Inno Setup installer for ARM64
uses: Minionguyjpro/Inno-Setup-Action@v1.2.2
with:
path: windows/windows_setup_arm64.iss
options: /O+ /DFLADDER_VERSION="${{ needs.fetch-info.outputs.version_name }}"

- name: Archive Windows ARM64 portable artifact
uses: actions/upload-artifact@v4.0.0
with:
name: fladder-windows-arm64-portable
path: build\windows\arm64\runner\Release\

- name: Archive Windows ARM64 installer artifact
uses: actions/upload-artifact@v4.0.0
with:
name: fladder-windows-arm64-installer
path: windows\Output\fladder_setup_arm64.exe

build-ios:
runs-on: macos-latest
needs: [fetch-info]
Expand Down Expand Up @@ -465,6 +506,7 @@ jobs:
- fetch-info
- build-android
- build-windows
- build-windows-arm64
- build-ios
- build-macos
- build-linux
Expand Down Expand Up @@ -591,6 +633,26 @@ jobs:
- name: Rename Windows installer
run: mv fladder-windows-installer/fladder_setup.exe Fladder-Windows-${{ steps.version.outputs.version }}-Setup.exe

- name: Download Windows ARM64 portable artifact
uses: actions/download-artifact@v4
with:
name: fladder-windows-arm64-portable
path: fladder-windows-arm64-portable

- name: Compress Windows ARM64
run: |
cd fladder-windows-arm64-portable
zip -r ../Fladder-Windows-ARM64-${{ steps.version.outputs.version }}.zip .

- name: Download Windows ARM64 installer artifact
uses: actions/download-artifact@v4
with:
name: fladder-windows-arm64-installer
path: fladder-windows-arm64-installer

- name: Rename Windows ARM64 installer
run: mv fladder-windows-arm64-installer/fladder_setup_arm64.exe Fladder-Windows-ARM64-${{ steps.version.outputs.version }}-Setup.exe

- name: Download Artifacts iOS
uses: actions/download-artifact@v4
with:
Expand Down Expand Up @@ -670,6 +732,8 @@ jobs:
Fladder-Android-${{ steps.version.outputs.version }}.aab
Fladder-Windows-${{ steps.version.outputs.version }}-Setup.exe
Fladder-Windows-${{ steps.version.outputs.version }}.zip
Fladder-Windows-ARM64-${{ steps.version.outputs.version }}-Setup.exe
Fladder-Windows-ARM64-${{ steps.version.outputs.version }}.zip
Fladder-iOS-${{ steps.version.outputs.version }}.ipa
Fladder-macOS-${{ steps.version.outputs.version }}.dmg
Fladder-Web-${{ steps.version.outputs.version }}.zip
Expand Down
23 changes: 12 additions & 11 deletions lib/models/playback/direct_playback_model.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import 'package:flutter/widgets.dart';
import 'package:collection/collection.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

import 'package:fladder/jellyfin/jellyfin_open_api.swagger.dart';
import 'package:fladder/jellyfin/jellyfin_open_api.swagger.dart' as jellyfin;
import 'package:fladder/models/item_base_model.dart';
import 'package:fladder/models/items/chapters_model.dart';
import 'package:fladder/models/items/item_shared_models.dart';
Expand Down Expand Up @@ -33,8 +33,9 @@ class DirectPlaybackModel extends PlaybackModel {
@override
List<SubStreamModel> get subStreams => [SubStreamModel.no(), ...mediaStreams?.subStreams ?? []];

List<QueueItem> get itemsInQueue =>
queue.mapIndexed((index, element) => QueueItem(id: element.id, playlistItemId: "playlistItem$index")).toList();
List<jellyfin.QueueItem> get itemsInQueue => queue
.mapIndexed((index, element) => jellyfin.QueueItem(id: element.id, playlistItemId: "playlistItem$index"))
.toList();

@override
Future<DirectPlaybackModel> setSubtitle(SubStreamModel? model, MediaControlsWrapper player) async {
Expand All @@ -59,7 +60,7 @@ class DirectPlaybackModel extends PlaybackModel {
@override
Future<PlaybackModel?> playbackStarted(Duration position, Ref ref) async {
await ref.read(jellyApiProvider).sessionsPlayingPost(
body: PlaybackStartInfo(
body: jellyfin.PlaybackStartInfo(
canSeek: true,
itemId: item.id,
mediaSourceId: item.id,
Expand All @@ -68,10 +69,10 @@ class DirectPlaybackModel extends PlaybackModel {
audioStreamIndex: item.streamModel?.defaultAudioStreamIndex,
volumeLevel: 100,
playbackStartTimeTicks: position.toRuntimeTicks,
playMethod: PlayMethod.directplay,
playMethod: jellyfin.PlayMethod.directplay,
isMuted: false,
isPaused: false,
repeatMode: RepeatMode.repeatall,
repeatMode: jellyfin.RepeatMode.repeatall,
),
);
return null;
Expand All @@ -82,7 +83,7 @@ class DirectPlaybackModel extends PlaybackModel {
ref.read(playBackModel.notifier).update((state) => null);

await ref.read(jellyApiProvider).sessionsPlayingStoppedPost(
body: PlaybackStopInfo(
body: jellyfin.PlaybackStopInfo(
itemId: item.id,
mediaSourceId: item.id,
playSessionId: playbackInfo?.playSessionId,
Expand All @@ -97,19 +98,19 @@ class DirectPlaybackModel extends PlaybackModel {
Future<PlaybackModel?> updatePlaybackPosition(Duration position, bool isPlaying, Ref ref) async {
final api = ref.read(jellyApiProvider);
await api.sessionsPlayingProgressPost(
body: PlaybackProgressInfo(
body: jellyfin.PlaybackProgressInfo(
canSeek: true,
itemId: item.id,
mediaSourceId: item.id,
playSessionId: playbackInfo?.playSessionId,
subtitleStreamIndex: item.streamModel?.defaultSubStreamIndex,
audioStreamIndex: item.streamModel?.defaultAudioStreamIndex,
volumeLevel: 100,
playMethod: PlayMethod.directplay,
playMethod: jellyfin.PlayMethod.directplay,
isPaused: !isPlaying,
isMuted: false,
positionTicks: position.toRuntimeTicks,
repeatMode: RepeatMode.repeatall,
repeatMode: jellyfin.RepeatMode.repeatall,
),
);

Expand All @@ -133,7 +134,7 @@ class DirectPlaybackModel extends PlaybackModel {
ItemBaseModel? item,
ValueGetter<Media?>? media,
ValueGetter<Duration>? lastPosition,
PlaybackInfoResponse? playbackInfo,
jellyfin.PlaybackInfoResponse? playbackInfo,
ValueGetter<MediaStreamsModel?>? mediaStreams,
ValueGetter<MediaSegmentsModel?>? mediaSegments,
ValueGetter<List<Chapter>?>? chapters,
Expand Down
23 changes: 12 additions & 11 deletions lib/models/playback/transcode_playback_model.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import 'package:flutter/widgets.dart';
import 'package:collection/collection.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

import 'package:fladder/jellyfin/jellyfin_open_api.swagger.dart';
import 'package:fladder/jellyfin/jellyfin_open_api.swagger.dart' as jellyfin;
import 'package:fladder/models/item_base_model.dart';
import 'package:fladder/models/items/chapters_model.dart';
import 'package:fladder/models/items/item_shared_models.dart';
Expand Down Expand Up @@ -33,8 +33,9 @@ class TranscodePlaybackModel extends PlaybackModel {
@override
List<SubStreamModel> get subStreams => [SubStreamModel.no(), ...mediaStreams?.subStreams ?? []];

List<QueueItem> get itemsInQueue =>
queue.mapIndexed((index, element) => QueueItem(id: element.id, playlistItemId: "playlistItem$index")).toList();
List<jellyfin.QueueItem> get itemsInQueue => queue
.mapIndexed((index, element) => jellyfin.QueueItem(id: element.id, playlistItemId: "playlistItem$index"))
.toList();

@override
Future<TranscodePlaybackModel> setSubtitle(SubStreamModel? model, MediaControlsWrapper player) async {
Expand All @@ -57,7 +58,7 @@ class TranscodePlaybackModel extends PlaybackModel {
@override
Future<PlaybackModel?> playbackStarted(Duration position, Ref ref) async {
await ref.read(jellyApiProvider).sessionsPlayingPost(
body: PlaybackStartInfo(
body: jellyfin.PlaybackStartInfo(
canSeek: true,
itemId: item.id,
mediaSourceId: item.id,
Expand All @@ -67,10 +68,10 @@ class TranscodePlaybackModel extends PlaybackModel {
audioStreamIndex: item.streamModel?.defaultAudioStreamIndex,
volumeLevel: 100,
playbackStartTimeTicks: position.toRuntimeTicks,
playMethod: PlayMethod.transcode,
playMethod: jellyfin.PlayMethod.transcode,
isMuted: false,
isPaused: false,
repeatMode: RepeatMode.repeatall,
repeatMode: jellyfin.RepeatMode.repeatall,
),
);
return null;
Expand All @@ -81,7 +82,7 @@ class TranscodePlaybackModel extends PlaybackModel {
ref.read(playBackModel.notifier).update((state) => null);

await ref.read(jellyApiProvider).sessionsPlayingStoppedPost(
body: PlaybackStopInfo(
body: jellyfin.PlaybackStopInfo(
itemId: item.id,
mediaSourceId: item.id,
playSessionId: playbackInfo?.playSessionId,
Expand All @@ -96,7 +97,7 @@ class TranscodePlaybackModel extends PlaybackModel {
Future<PlaybackModel?> updatePlaybackPosition(Duration position, bool isPlaying, Ref ref) async {
final api = ref.read(jellyApiProvider);
await api.sessionsPlayingProgressPost(
body: PlaybackProgressInfo(
body: jellyfin.PlaybackProgressInfo(
canSeek: true,
itemId: item.id,
mediaSourceId: item.id,
Expand All @@ -106,10 +107,10 @@ class TranscodePlaybackModel extends PlaybackModel {
audioStreamIndex: item.streamModel?.defaultAudioStreamIndex,
volumeLevel: 100,
positionTicks: position.toRuntimeTicks,
playMethod: PlayMethod.transcode,
playMethod: jellyfin.PlayMethod.transcode,
isPaused: !isPlaying,
isMuted: false,
repeatMode: RepeatMode.repeatall,
repeatMode: jellyfin.RepeatMode.repeatall,
),
);
return this;
Expand All @@ -132,7 +133,7 @@ class TranscodePlaybackModel extends PlaybackModel {
ItemBaseModel? item,
ValueGetter<Media?>? media,
ValueGetter<Duration>? lastPosition,
PlaybackInfoResponse? playbackInfo,
jellyfin.PlaybackInfoResponse? playbackInfo,
ValueGetter<MediaStreamsModel?>? mediaStreams,
ValueGetter<MediaSegmentsModel?>? mediaSegments,
ValueGetter<List<Chapter>?>? chapters,
Expand Down
3 changes: 2 additions & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -175,8 +175,9 @@ dependency_overrides:
path: libs/macos/media_kit_libs_macos_video
media_kit_libs_windows_video:
git:
url: https://github.com/DonutWare/media-kit.git
url: https://github.com/talynone/media-kit.git
path: libs/windows/media_kit_libs_windows_video
ref: WINARM64
media_kit_libs_linux:
git:
url: https://github.com/DonutWare/media-kit.git
Expand Down
65 changes: 65 additions & 0 deletions windows/windows_setup_arm64.iss
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#define SourcePath ".."

#ifndef FLADDER_VERSION
#define FLADDER_VERSION "latest"
#endif

[Setup]
AppId={{D573EDD5-117A-47AD-88AC-62C8EBD11DC8}
AppName="Fladder"
AppVersion={#FLADDER_VERSION}
AppPublisher="DonutWare"
AppPublisherURL="https://github.com/DonutWare/Fladder"
AppSupportURL="https://github.com/DonutWare/Fladder"
AppUpdatesURL="https://github.com/DonutWare/Fladder"
DefaultDirName={localappdata}\Programs\Fladder
ArchitecturesAllowed=arm64
ArchitecturesInstallIn64BitMode=arm64
DisableProgramGroupPage=yes
PrivilegesRequired=lowest
PrivilegesRequiredOverridesAllowed=dialog
OutputBaseFilename=fladder_setup_arm64
Compression=lzma
SolidCompression=yes
WizardStyle=modern

SetupLogging=yes
UninstallLogging=yes
UninstallDisplayName="Fladder (ARM64)"
UninstallDisplayIcon={app}\fladder.exe
SetupIconFile="{#SourcePath}\icons\production\fladder_icon.ico"
LicenseFile="{#SourcePath}\LICENSE"
WizardImageFile={#SourcePath}\assets\windows-installer\fladder-installer-100.bmp,{#SourcePath}\assets\windows-installer\fladder-installer-125.bmp,{#SourcePath}\assets\windows-installer\fladder-installer-150.bmp

[Languages]
Name: "english"; MessagesFile: "compiler:Default.isl"

[Tasks]
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked

[Files]
Source: "{#SourcePath}\build\windows\arm64\runner\Release\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs

[Icons]
Name: "{autoprograms}\Fladder"; Filename: "{app}\fladder.exe"
Name: "{autodesktop}\Fladder"; Filename: "{app}\fladder.exe"; Tasks: desktopicon

[Run]
Filename: "{app}\fladder.exe"; Description: "{cm:LaunchProgram,Fladder}"; Flags: nowait postinstall skipifsilent

[Code]
procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);
begin
case CurUninstallStep of
usUninstall:
begin
if MsgBox('Would you like to delete the application''s data? This action cannot be undone. Synced files will remain unaffected.', mbConfirmation, MB_YESNO) = IDYES then
begin
if DelTree(ExpandConstant('{localappdata}\DonutWare'), True, True, True) = False then
begin
Log(ExpandConstant('{localappdata}\DonutWare could not be deleted. Skipping...'));
end;
end;
end;
end;
end;
Loading