mirror of
https://github.com/immich-app/immich
synced 2025-06-08 04:51:01 +00:00
code review changes from @shenlong-tanwen
This commit is contained in:
parent
9b42e1b561
commit
f999c77079
@ -5,8 +5,6 @@ abstract interface class ICastDestinationService {
|
|||||||
Future<bool> initialize();
|
Future<bool> initialize();
|
||||||
CastDestinationType getType();
|
CastDestinationType getType();
|
||||||
|
|
||||||
bool isAvailable();
|
|
||||||
|
|
||||||
void Function(bool)? onConnectionState;
|
void Function(bool)? onConnectionState;
|
||||||
|
|
||||||
void Function(Duration)? onCurrentTime;
|
void Function(Duration)? onCurrentTime;
|
||||||
@ -15,12 +13,14 @@ abstract interface class ICastDestinationService {
|
|||||||
void Function(String)? onReceiverName;
|
void Function(String)? onReceiverName;
|
||||||
void Function(CastState)? onCastState;
|
void Function(CastState)? onCastState;
|
||||||
|
|
||||||
|
Future<void> connect(dynamic device);
|
||||||
|
|
||||||
void loadMedia(Asset asset, bool reload);
|
void loadMedia(Asset asset, bool reload);
|
||||||
|
|
||||||
void play();
|
void play();
|
||||||
void pause();
|
void pause();
|
||||||
void seekTo(Duration position);
|
void seekTo(Duration position);
|
||||||
void disconnect();
|
Future<void> disconnect();
|
||||||
|
|
||||||
Future<List<(String, CastDestinationType, dynamic)>> getDevices();
|
Future<List<(String, CastDestinationType, dynamic)>> getDevices();
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@ class CastManagerState {
|
|||||||
final Duration currentTime;
|
final Duration currentTime;
|
||||||
final Duration duration;
|
final Duration duration;
|
||||||
|
|
||||||
CastManagerState({
|
const CastManagerState({
|
||||||
required this.isCasting,
|
required this.isCasting,
|
||||||
required this.receiverName,
|
required this.receiverName,
|
||||||
required this.castState,
|
required this.castState,
|
||||||
@ -53,7 +53,8 @@ class CastManagerState {
|
|||||||
receiverName: map['receiverName'] ?? '',
|
receiverName: map['receiverName'] ?? '',
|
||||||
castState: map['castState'] ?? CastState.idle,
|
castState: map['castState'] ?? CastState.idle,
|
||||||
currentTime: Duration(seconds: map['currentTime']?.toInt() ?? 0),
|
currentTime: Duration(seconds: map['currentTime']?.toInt() ?? 0),
|
||||||
duration: Duration(seconds: map['duration']?.toInt() ?? 0),);
|
duration: Duration(seconds: map['duration']?.toInt() ?? 0),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
String toJson() => json.encode(toMap());
|
String toJson() => json.encode(toMap());
|
||||||
|
@ -8,7 +8,7 @@ class SessionCreateResponse {
|
|||||||
final String token;
|
final String token;
|
||||||
final String updatedAt;
|
final String updatedAt;
|
||||||
|
|
||||||
SessionCreateResponse({
|
const SessionCreateResponse({
|
||||||
required this.createdAt,
|
required this.createdAt,
|
||||||
required this.current,
|
required this.current,
|
||||||
required this.deviceOS,
|
required this.deviceOS,
|
||||||
|
@ -61,7 +61,7 @@ class NativeVideoViewerPage extends HookConsumerWidget {
|
|||||||
|
|
||||||
final log = Logger('NativeVideoViewerPage');
|
final log = Logger('NativeVideoViewerPage');
|
||||||
|
|
||||||
final cast = ref.watch(castProvider);
|
final isCasting = ref.watch(castProvider.select((c) => c.isCasting));
|
||||||
|
|
||||||
Future<VideoSource?> createSource() async {
|
Future<VideoSource?> createSource() async {
|
||||||
if (!context.mounted) {
|
if (!context.mounted) {
|
||||||
@ -394,7 +394,7 @@ class NativeVideoViewerPage extends HookConsumerWidget {
|
|||||||
// This remains under the video to avoid flickering
|
// This remains under the video to avoid flickering
|
||||||
// For motion videos, this is the image portion of the asset
|
// For motion videos, this is the image portion of the asset
|
||||||
Center(key: ValueKey(asset.id), child: image),
|
Center(key: ValueKey(asset.id), child: image),
|
||||||
if (aspectRatio.value != null && !cast.isCasting)
|
if (aspectRatio.value != null && !isCasting)
|
||||||
Visibility.maintain(
|
Visibility.maintain(
|
||||||
key: ValueKey(asset),
|
key: ValueKey(asset),
|
||||||
visible: isVisible.value,
|
visible: isVisible.value,
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:immich_mobile/entities/asset.entity.dart';
|
import 'package:immich_mobile/entities/asset.entity.dart';
|
||||||
|
import 'package:immich_mobile/interfaces/cast_destination_service.interface.dart';
|
||||||
import 'package:immich_mobile/models/cast/cast_manager_state.dart';
|
import 'package:immich_mobile/models/cast/cast_manager_state.dart';
|
||||||
import 'package:immich_mobile/services/gcast.service.dart';
|
import 'package:immich_mobile/services/gcast.service.dart';
|
||||||
|
|
||||||
@ -8,13 +9,14 @@ final castProvider = StateNotifierProvider<CastNotifier, CastManagerState>(
|
|||||||
);
|
);
|
||||||
|
|
||||||
class CastNotifier extends StateNotifier<CastManagerState> {
|
class CastNotifier extends StateNotifier<CastManagerState> {
|
||||||
final GCastService _gCastService;
|
// more cast providers can be added here (ie Fcast)
|
||||||
|
final ICastDestinationService _gCastService;
|
||||||
|
|
||||||
List<(String, CastDestinationType, dynamic)> discovered = List.empty();
|
List<(String, CastDestinationType, dynamic)> discovered = List.empty();
|
||||||
|
|
||||||
CastNotifier(this._gCastService)
|
CastNotifier(this._gCastService)
|
||||||
: super(
|
: super(
|
||||||
CastManagerState(
|
const CastManagerState(
|
||||||
isCasting: false,
|
isCasting: false,
|
||||||
currentTime: Duration.zero,
|
currentTime: Duration.zero,
|
||||||
duration: Duration.zero,
|
duration: Duration.zero,
|
||||||
|
@ -14,6 +14,7 @@ import 'package:immich_mobile/repositories/gcast.repository.dart';
|
|||||||
import 'package:immich_mobile/repositories/sessions_api.repository.dart';
|
import 'package:immich_mobile/repositories/sessions_api.repository.dart';
|
||||||
import 'package:immich_mobile/utils/image_url_builder.dart';
|
import 'package:immich_mobile/utils/image_url_builder.dart';
|
||||||
import 'package:immich_mobile/utils/url_helper.dart';
|
import 'package:immich_mobile/utils/url_helper.dart';
|
||||||
|
// ignore: import_rule_openapi, we are only using the AssetMediaSize enum
|
||||||
import 'package:openapi/api.dart';
|
import 'package:openapi/api.dart';
|
||||||
|
|
||||||
final gCastServiceProvider = Provider(
|
final gCastServiceProvider = Provider(
|
||||||
@ -34,7 +35,6 @@ class GCastService implements ICastDestinationService {
|
|||||||
bool isConnected = false;
|
bool isConnected = false;
|
||||||
int? _sessionId;
|
int? _sessionId;
|
||||||
Timer? _mediaStatusPollingTimer;
|
Timer? _mediaStatusPollingTimer;
|
||||||
CastState? castState;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void Function(bool)? onConnectionState;
|
void Function(bool)? onConnectionState;
|
||||||
@ -67,12 +67,16 @@ class GCastService implements ICastDestinationService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _onCastMessageCallback(Map<String, dynamic> message) {
|
void _onCastMessageCallback(Map<String, dynamic> message) {
|
||||||
final msgType = message['type'];
|
switch (message['type']) {
|
||||||
|
case "MEDIA_STATUS":
|
||||||
|
_handleMediaStatus(message);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (msgType == "MEDIA_STATUS") {
|
void _handleMediaStatus(Map<String, dynamic> message) {
|
||||||
final statusList = (message['status'] as List)
|
final statusList =
|
||||||
.whereType<Map<String, dynamic>>()
|
(message['status'] as List).whereType<Map<String, dynamic>>().toList();
|
||||||
.toList();
|
|
||||||
|
|
||||||
if (statusList.isEmpty) {
|
if (statusList.isEmpty) {
|
||||||
return;
|
return;
|
||||||
@ -117,7 +121,6 @@ class GCastService implements ICastDestinationService {
|
|||||||
onCurrentTime?.call(currentTime);
|
onCurrentTime?.call(currentTime);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> connect(CastDevice device) async {
|
Future<void> connect(CastDevice device) async {
|
||||||
await _gCastRepository.connect(device);
|
await _gCastRepository.connect(device);
|
||||||
@ -139,20 +142,11 @@ class GCastService implements ICastDestinationService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void disconnect() {
|
Future<void> disconnect() async {
|
||||||
_gCastRepository.disconnect();
|
await _gCastRepository.disconnect();
|
||||||
|
|
||||||
onReceiverName?.call("");
|
onReceiverName?.call("");
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
bool isAvailable() {
|
|
||||||
// check if server URL is https
|
|
||||||
final serverUrl = punycodeDecodeUrl(Store.tryGet(StoreKey.serverEndpoint));
|
|
||||||
|
|
||||||
return serverUrl?.startsWith("https://") ?? false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isSessionValid() {
|
bool isSessionValid() {
|
||||||
// check if we already have a session token
|
// check if we already have a session token
|
||||||
// we should always have a expiration date
|
// we should always have a expiration date
|
||||||
@ -220,6 +214,8 @@ class GCastService implements ICastDestinationService {
|
|||||||
"autoplay": true,
|
"autoplay": true,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
currentAssetId = asset.remoteId;
|
||||||
|
|
||||||
// we need to poll for media status since the cast device does not
|
// we need to poll for media status since the cast device does not
|
||||||
// send a message when the media is loaded for whatever reason
|
// send a message when the media is loaded for whatever reason
|
||||||
// only do this on videos
|
// only do this on videos
|
||||||
|
@ -78,7 +78,9 @@ class CustomVideoPlayerControls extends HookConsumerWidget {
|
|||||||
}
|
}
|
||||||
ref.read(castProvider.notifier).loadMedia(asset, true);
|
ref.read(castProvider.notifier).loadMedia(asset, true);
|
||||||
}
|
}
|
||||||
} else {
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (state == VideoPlaybackState.playing) {
|
if (state == VideoPlaybackState.playing) {
|
||||||
ref.read(videoPlayerControlsProvider.notifier).pause();
|
ref.read(videoPlayerControlsProvider.notifier).pause();
|
||||||
} else if (state == VideoPlaybackState.completed) {
|
} else if (state == VideoPlaybackState.completed) {
|
||||||
@ -87,7 +89,6 @@ class CustomVideoPlayerControls extends HookConsumerWidget {
|
|||||||
ref.read(videoPlayerControlsProvider.notifier).play();
|
ref.read(videoPlayerControlsProvider.notifier).play();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
behavior: HitTestBehavior.opaque,
|
behavior: HitTestBehavior.opaque,
|
||||||
|
@ -46,7 +46,7 @@ class TopControlAppBar extends HookConsumerWidget {
|
|||||||
const double iconSize = 22.0;
|
const double iconSize = 22.0;
|
||||||
final a = ref.watch(assetWatcher(asset)).value ?? asset;
|
final a = ref.watch(assetWatcher(asset)).value ?? asset;
|
||||||
final album = ref.watch(currentAlbumProvider);
|
final album = ref.watch(currentAlbumProvider);
|
||||||
final castManager = ref.watch(castProvider);
|
final isCasting = ref.watch(castProvider.select((c) => c.isCasting));
|
||||||
final comments = album != null &&
|
final comments = album != null &&
|
||||||
album.remoteId != null &&
|
album.remoteId != null &&
|
||||||
asset.remoteId != null
|
asset.remoteId != null
|
||||||
@ -181,9 +181,7 @@ class TopControlAppBar extends HookConsumerWidget {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
castManager.isCasting
|
isCasting ? Icons.cast_connected_rounded : Icons.cast_rounded,
|
||||||
? Icons.cast_connected_rounded
|
|
||||||
: Icons.cast_rounded,
|
|
||||||
size: 20.0,
|
size: 20.0,
|
||||||
color: Colors.grey[200],
|
color: Colors.grey[200],
|
||||||
),
|
),
|
||||||
|
@ -71,16 +71,16 @@ class VideoPosition extends HookConsumerWidget {
|
|||||||
ref
|
ref
|
||||||
.read(castProvider.notifier)
|
.read(castProvider.notifier)
|
||||||
.seekTo(seekToDuration);
|
.seekTo(seekToDuration);
|
||||||
} else {
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
ref
|
ref
|
||||||
.read(videoPlayerControlsProvider.notifier)
|
.read(videoPlayerControlsProvider.notifier)
|
||||||
.position = seekToDuration.inSeconds.toDouble();
|
.position = seekToDuration.inSeconds.toDouble();
|
||||||
|
|
||||||
// This immediately updates the slider position without waiting for the video to update
|
// This immediately updates the slider position without waiting for the video to update
|
||||||
ref
|
ref.read(videoPlaybackValueProvider.notifier).position =
|
||||||
.read(videoPlaybackValueProvider.notifier)
|
seekToDuration;
|
||||||
.position = seekToDuration;
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user