spotube/lib/provider/connect/clients.dart
Kingkor Roy Tirtho 22a49e56a2
refactor: use tcp server based track matcher (#1386)
* refactor: remove SourcedTrack based audio player and utilize mediakit playback system

* feat: implement local (loopback) server to resolve stream source and leverage the media_kit playback API

* feat: add source change support and re-add prefetching tracks

* fix: assign lastId when track fetch completes regardless of error

* chore: remove print statements

* fix: remote queue not working

* fix: increase mpv network timeout to reduce auto-skipping

* fix: do not pre-fetch local tracks

* fix(proxy-playlist): reset collections on load

* chore: fix lint warnings

* fix(mobile): player overlay should not be visible when the player is not playing

* chore: fix typo in turkish translation

* cd: checkout PR branch

* cd: upgrade flutter version

* chore: fix lint errors
2024-04-11 17:56:41 +06:00

112 lines
3.1 KiB
Dart

import 'package:bonsoir/bonsoir.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:spotube/services/device_info/device_info.dart';
class ConnectClientsState {
final List<BonsoirService> services;
final ResolvedBonsoirService? resolvedService;
final BonsoirDiscovery discovery;
ConnectClientsState({
required this.services,
required this.discovery,
this.resolvedService,
});
ConnectClientsState copyWith({
List<BonsoirService>? services,
BonsoirDiscovery? discovery,
ResolvedBonsoirService? resolvedService,
}) {
return ConnectClientsState(
services: services ?? this.services,
discovery: discovery ?? this.discovery,
resolvedService: resolvedService ?? this.resolvedService,
);
}
}
class ConnectClientsNotifier extends AsyncNotifier<ConnectClientsState> {
ConnectClientsNotifier();
@override
build() async {
final discovery = BonsoirDiscovery(type: '_spotube._tcp');
final deviceId = await DeviceInfoService.instance.deviceId();
await discovery.ready;
final subscription = discovery.eventStream?.listen((event) {
// ignore device itself
if (event.service?.attributes["deviceId"] == deviceId) {
return;
}
switch (event.type) {
case BonsoirDiscoveryEventType.discoveryServiceFound:
state = AsyncData(state.value!.copyWith(
services: [
...?state.value?.services,
event.service!,
],
));
break;
case BonsoirDiscoveryEventType.discoveryServiceResolved:
state = AsyncData(
state.value!.copyWith(
resolvedService: event.service as ResolvedBonsoirService,
),
);
break;
case BonsoirDiscoveryEventType.discoveryServiceLost:
state = AsyncData(
ConnectClientsState(
services: state.value!.services
.where((s) => s.name != event.service!.name)
.toList(),
discovery: state.value!.discovery,
resolvedService: state.value?.resolvedService != null &&
event.service?.name == state.value?.resolvedService?.name
? null
: state.value!.resolvedService,
),
);
break;
default:
break;
}
});
ref.onDispose(() {
subscription?.cancel();
discovery.stop();
});
await discovery.start();
return ConnectClientsState(
services: [],
discovery: discovery,
);
}
Future<void> resolveService(BonsoirService service) async {
if (state.value == null) return;
await service.resolve(state.value!.discovery.serviceResolver);
}
Future<void> clearResolvedService() async {
if (state.value == null) return;
state = AsyncData(
ConnectClientsState(
services: state.value!.services,
discovery: state.value!.discovery,
),
);
}
}
final connectClientsProvider =
AsyncNotifierProvider<ConnectClientsNotifier, ConnectClientsState>(
() => ConnectClientsNotifier(),
);