3 Commits

Author SHA1 Message Date
4bf8715486 🐛 Fix timeout 2024-08-30 22:16:10 +08:00
fbb12ff801 🐛 Bug fixes 2024-08-30 22:06:24 +08:00
47d051dd44 🐛 Bug fixes 2024-08-30 21:53:40 +08:00
14 changed files with 142 additions and 44 deletions

View File

@ -5,23 +5,6 @@ on:
branches: [master] branches: [master]
jobs: jobs:
build-web:
runs-on: ubuntu-latest
steps:
- name: Clone repository
uses: actions/checkout@v4
- name: Set up Flutter
uses: subosito/flutter-action@v2
with:
channel: stable
cache: true
- run: flutter pub get
- run: flutter build web --release --base-href=/
- name: Archive production artifacts
uses: actions/upload-artifact@v4
with:
name: build-output-web
path: build/web
build-exe: build-exe:
runs-on: windows-latest runs-on: windows-latest
steps: steps:

View File

@ -44,6 +44,11 @@ Future<void> main(List<String> rawArgs) async {
if (PlatformInfo.isDesktop) { if (PlatformInfo.isDesktop) {
await windowManager.ensureInitialized(); await windowManager.ensureInitialized();
await windowManager.setPreventClose(true); await windowManager.setPreventClose(true);
if (PlatformInfo.isMacOS) {
await windowManager.setTitleBarStyle(TitleBarStyle.hidden);
} else {
await windowManager.setTitleBarStyle(TitleBarStyle.normal);
}
} }
if (PlatformInfo.isWindows) { if (PlatformInfo.isWindows) {
await SMTCWindows.initialize(); await SMTCWindows.initialize();

View File

@ -7,6 +7,9 @@ import 'package:media_kit/media_kit.dart' hide Track;
import 'package:rhythm_box/providers/database.dart'; import 'package:rhythm_box/providers/database.dart';
import 'package:rhythm_box/services/audio_player/state.dart'; import 'package:rhythm_box/services/audio_player/state.dart';
import 'package:rhythm_box/services/database/database.dart'; import 'package:rhythm_box/services/database/database.dart';
import 'package:rhythm_box/services/local_track.dart';
import 'package:rhythm_box/services/server/sourced_track.dart';
import 'package:rhythm_box/widgets/tracks/querying_track_info.dart';
import 'package:spotify/spotify.dart' hide Playlist; import 'package:spotify/spotify.dart' hide Playlist;
import 'package:rhythm_box/services/audio_player/audio_player.dart'; import 'package:rhythm_box/services/audio_player/audio_player.dart';
@ -248,11 +251,12 @@ class AudioPlayerProvider extends GetxController {
// Giving the initial track a boost so MediaKit won't skip // Giving the initial track a boost so MediaKit won't skip
// because of timeout // because of timeout
// final intendedActiveTrack = medias.elementAt(initialIndex); Get.find<QueryingTrackInfoProvider>().isQueryingTrackInfo.value = true;
// if (intendedActiveTrack.track is! LocalTrack) { final intendedActiveTrack = medias.elementAt(initialIndex);
// await Get.find<SourcedTrackProvider>() if (intendedActiveTrack.track is! LocalTrack) {
// .fetch(RhythmMedia(intendedActiveTrack.track)); await Get.find<SourcedTrackProvider>()
// } .fetch(RhythmMedia(intendedActiveTrack.track));
}
if (medias.isEmpty) return; if (medias.isEmpty) return;

View File

@ -11,9 +11,12 @@ class AboutScreen extends StatelessWidget {
const denseButtonStyle = const denseButtonStyle =
ButtonStyle(visualDensity: VisualDensity(vertical: -4)); ButtonStyle(visualDensity: VisualDensity(vertical: -4));
return Material( return Scaffold(
color: Theme.of(context).colorScheme.surface, backgroundColor: Theme.of(context).colorScheme.surface,
child: SizedBox( appBar: AppBar(
title: const Text('About'),
),
body: SizedBox(
width: double.infinity, width: double.infinity,
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,

View File

@ -132,12 +132,13 @@ class _MiniPlayerScreenState extends State<MiniPlayerScreen> {
await windowManager.setAlwaysOnTop( await windowManager.setAlwaysOnTop(
snapshot.data == true ? false : true, snapshot.data == true ? false : true,
); );
setState(() {});
}, },
); );
}, },
), ),
], ],
).paddingSymmetric(horizontal: 24), ).paddingSymmetric(horizontal: 14),
), ),
), ),
), ),

View File

@ -240,7 +240,7 @@ class _PlayerScreenState extends State<PlayerScreen> {
width: 56, width: 56,
height: 56, height: 56,
child: IconButton.filled( child: IconButton.filled(
icon: _isFetchingActiveTrack icon: (_isFetchingActiveTrack && _isPlaying)
? const SizedBox( ? const SizedBox(
height: 20, height: 20,
width: 20, width: 20,
@ -255,9 +255,7 @@ class _PlayerScreenState extends State<PlayerScreen> {
: Icons.pause, : Icons.pause,
size: 28, size: 28,
), ),
onPressed: _isFetchingActiveTrack onPressed: _togglePlayState,
? null
: _togglePlayState,
), ),
), ),
), ),

View File

@ -25,12 +25,12 @@ class ActiveSourcedTrackProvider extends GetxController {
try { try {
if (state.value == null) return; if (state.value == null) return;
await audioPlayer.pause();
await populateSibling(); await populateSibling();
final newTrack = await state.value!.swapWithSibling(sibling); final newTrack = await state.value!.swapWithSibling(sibling);
if (newTrack == null) return; if (newTrack == null) return;
state.value = newTrack; state.value = newTrack;
await audioPlayer.pause();
final playback = Get.find<AudioPlayerProvider>(); final playback = Get.find<AudioPlayerProvider>();
final oldActiveIndex = audioPlayer.currentIndex; final oldActiveIndex = audioPlayer.currentIndex;
@ -40,12 +40,11 @@ class ActiveSourcedTrackProvider extends GetxController {
await audioPlayer.removeTrack(oldActiveIndex); await audioPlayer.removeTrack(oldActiveIndex);
await playback.jumpToTrack(newTrack); await playback.jumpToTrack(newTrack);
await audioPlayer.resume();
} catch (e, stack) { } catch (e, stack) {
log('[Playback] Failed to swap with siblings. Error: $e; Trace:\n$stack'); log('[Playback] Failed to swap with siblings. Error: $e; Trace:\n$stack');
} finally { } finally {
query.isQueryingTrackInfo.value = false; query.isQueryingTrackInfo.value = false;
await audioPlayer.resume();
} }
} }
} }

View File

@ -9,7 +9,7 @@ class SystemShell extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
if (PlatformInfo.isDesktop) { if (PlatformInfo.isMacOS) {
return DragToMoveArea( return DragToMoveArea(
child: Column( child: Column(
children: [ children: [

View File

@ -10,6 +10,7 @@ import 'package:rhythm_box/providers/audio_player.dart';
import 'package:rhythm_box/services/audio_services/image.dart'; import 'package:rhythm_box/services/audio_services/image.dart';
import 'package:rhythm_box/widgets/auto_cache_image.dart'; import 'package:rhythm_box/widgets/auto_cache_image.dart';
import 'package:rhythm_box/widgets/player/controls.dart'; import 'package:rhythm_box/widgets/player/controls.dart';
import 'package:rhythm_box/widgets/player/devices.dart';
import 'package:rhythm_box/widgets/player/track_details.dart'; import 'package:rhythm_box/widgets/player/track_details.dart';
import 'package:rhythm_box/widgets/tracks/querying_track_info.dart'; import 'package:rhythm_box/widgets/tracks/querying_track_info.dart';
import 'package:rhythm_box/widgets/volume_slider.dart'; import 'package:rhythm_box/widgets/volume_slider.dart';
@ -163,6 +164,16 @@ class _BottomPlayerState extends State<BottomPlayer>
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.end, mainAxisAlignment: MainAxisAlignment.end,
children: [ children: [
IconButton(
icon: const Icon(Icons.speaker, size: 18),
onPressed: () {
showModalBottomSheet(
useRootNavigator: true,
context: context,
builder: (context) => const PlayerDevicePopup(),
);
},
),
if (!widget.isMiniPlayer && PlatformInfo.isDesktop) if (!widget.isMiniPlayer && PlatformInfo.isDesktop)
IconButton( IconButton(
icon: const Icon( icon: const Icon(

View File

@ -47,18 +47,19 @@ class _PlayerControlsState extends State<PlayerControls> {
onPressed: _isFetchingActiveTrack ? null : audioPlayer.skipToNext, onPressed: _isFetchingActiveTrack ? null : audioPlayer.skipToNext,
), ),
IconButton.filled( IconButton.filled(
icon: _isFetchingActiveTrack icon: (_isFetchingActiveTrack && _isPlaying)
? const SizedBox( ? const SizedBox(
height: 20, height: 20,
width: 20, width: 20,
child: CircularProgressIndicator( child: CircularProgressIndicator(
strokeWidth: 3, strokeWidth: 3,
color: Colors.white,
), ),
) )
: Icon( : Icon(
!_isPlaying ? Icons.play_arrow : Icons.pause, !_isPlaying ? Icons.play_arrow : Icons.pause,
), ),
onPressed: _isFetchingActiveTrack ? null : _togglePlayState, onPressed: _togglePlayState,
), ),
if (MediaQuery.of(context).size.width >= 720) if (MediaQuery.of(context).size.width >= 720)
IconButton( IconButton(

View File

@ -0,0 +1,85 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:media_kit/media_kit.dart';
import 'package:rhythm_box/services/audio_player/audio_player.dart';
class PlayerDevicePopup extends StatefulWidget {
const PlayerDevicePopup({super.key});
@override
State<PlayerDevicePopup> createState() => _PlayerDevicePopupState();
}
class _PlayerDevicePopupState extends State<PlayerDevicePopup> {
late Future<List<AudioDevice>> devicesFuture;
late Stream<List<AudioDevice>> devicesStream;
late Future<AudioDevice> selectedDeviceFuture;
late Stream<AudioDevice> selectedDeviceStream;
@override
void initState() {
super.initState();
devicesFuture = audioPlayer.devices;
devicesStream = audioPlayer.devicesStream;
selectedDeviceFuture = audioPlayer.selectedDevice;
selectedDeviceStream = audioPlayer.selectedDeviceStream;
}
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Devices',
style: Theme.of(context).textTheme.headlineSmall,
).paddingOnly(left: 24, right: 24, top: 32, bottom: 16),
Expanded(
child: StreamBuilder<List<AudioDevice>>(
stream: devicesStream,
builder: (context, devicesSnapshot) {
return FutureBuilder<List<AudioDevice>>(
future: devicesFuture,
builder: (context, devicesFutureSnapshot) {
final devices =
devicesSnapshot.data ?? devicesFutureSnapshot.data;
return StreamBuilder<AudioDevice>(
stream: selectedDeviceStream,
builder: (context, selectedDeviceSnapshot) {
return FutureBuilder<AudioDevice>(
future: selectedDeviceFuture,
builder: (context, selectedDeviceFutureSnapshot) {
final selectedDevice = selectedDeviceSnapshot.data ??
selectedDeviceFutureSnapshot.data;
if (devices == null || selectedDevice == null) {
return const CircularProgressIndicator();
}
return ListView.builder(
itemCount: devices.length,
itemBuilder: (context, idx) {
final device = devices[idx];
return ListTile(
leading: const Icon(Icons.speaker),
title: Text(device.description),
subtitle: Text(device.name),
selected: selectedDevice == device,
onTap: () => audioPlayer.setAudioDevice(device),
);
},
);
},
);
},
);
},
);
},
),
),
],
);
}
}

View File

@ -67,7 +67,7 @@ class VolumeSlider extends StatelessWidget {
} }
}, },
), ),
slider, SizedBox(width: 100, child: slider),
], ],
); );
}); });

View File

@ -1228,10 +1228,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: shelf_web_socket name: shelf_web_socket
sha256: "9ca081be41c60190ebcb4766b2486a7d50261db7bd0f5d9615f2d653637a84c1" sha256: "073c147238594ecd0d193f3456a5fe91c4b0abbcc68bf5cd95b36c4e194ac611"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.0.4" version: "2.0.0"
skeletonizer: skeletonizer:
dependency: "direct main" dependency: "direct main"
description: description:
@ -1522,10 +1522,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: vm_service name: vm_service
sha256: f652077d0bdf60abe4c1f6377448e8655008eef28f128bc023f7b5e8dfeb48fc sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "14.2.4" version: "14.2.5"
watcher: watcher:
dependency: transitive dependency: transitive
description: description:
@ -1542,14 +1542,22 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.5.1" version: "0.5.1"
web_socket:
dependency: transitive
description:
name: web_socket
sha256: "3c12d96c0c9a4eec095246debcea7b86c0324f22df69893d538fcc6f1b8cce83"
url: "https://pub.dev"
source: hosted
version: "0.1.6"
web_socket_channel: web_socket_channel:
dependency: transitive dependency: transitive
description: description:
name: web_socket_channel name: web_socket_channel
sha256: d88238e5eac9a42bb43ca4e721edba3c08c6354d4a53063afaa568516217621b sha256: "9f187088ed104edd8662ca07af4b124465893caf063ba29758f97af57e61da8f"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.4.0" version: "3.0.1"
win32: win32:
dependency: transitive dependency: transitive
description: description:

View File

@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# In Windows, build-name is used as the major, minor, and patch parts # In Windows, build-name is used as the major, minor, and patch parts
# of the product and file versions while build-number is used as the build suffix. # of the product and file versions while build-number is used as the build suffix.
version: 1.0.0+1 version: 1.0.0+3
environment: environment:
sdk: ^3.5.0 sdk: ^3.5.0