Skip to content
Draft
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
66 changes: 66 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,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 @@ -458,6 +499,7 @@ jobs:
- fetch-info
- build-android
- build-windows
- build-windows-arm64
- build-ios
- build-macos
- build-linux
Expand Down Expand Up @@ -582,6 +624,28 @@ 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 @@ -659,6 +723,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 @@ -172,8 +172,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;