Initial commit
This commit is contained in:
153
lib/api/commands.dart
Normal file
153
lib/api/commands.dart
Normal file
@@ -0,0 +1,153 @@
|
||||
import 'dart:convert';
|
||||
|
||||
sealed class ConnectionEvent {}
|
||||
|
||||
class Connect extends ConnectionEvent {
|
||||
final String uri;
|
||||
|
||||
Connect(this.uri);
|
||||
}
|
||||
|
||||
class Disconnect extends ConnectionEvent {}
|
||||
|
||||
class Command extends ConnectionEvent {
|
||||
final String type;
|
||||
final Map<String, dynamic> value;
|
||||
|
||||
Command({
|
||||
required this.type,
|
||||
required this.value,
|
||||
});
|
||||
|
||||
factory Command.fromJson(Map<String, dynamic> json) {
|
||||
return Command(
|
||||
type: json['type'],
|
||||
value: json['value'],
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> result = Map.from(value);
|
||||
result['type'] = type;
|
||||
return result;
|
||||
}
|
||||
|
||||
String toJsonString() {
|
||||
return jsonEncode(toJson());
|
||||
}
|
||||
|
||||
factory Command.subscribe(String property) {
|
||||
return Command(
|
||||
type: 'subscribe',
|
||||
value: {
|
||||
'property': property,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
factory Command.unsubscribeAll() {
|
||||
return Command(
|
||||
type: 'unsubscribe_all',
|
||||
value: {},
|
||||
);
|
||||
}
|
||||
|
||||
factory Command.load(String url) {
|
||||
return Command(
|
||||
type: 'load',
|
||||
value: {
|
||||
'url': url,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
factory Command.togglePlayback() {
|
||||
return Command(
|
||||
type: 'toggle_playback',
|
||||
value: {},
|
||||
);
|
||||
}
|
||||
|
||||
factory Command.volume(double volume) {
|
||||
return Command(
|
||||
type: 'volume',
|
||||
value: {
|
||||
'volume': volume,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
factory Command.time(double time) {
|
||||
return Command(
|
||||
type: 'time',
|
||||
value: {
|
||||
'time': time,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
factory Command.playlistNext() {
|
||||
return Command(
|
||||
type: 'playlist_next',
|
||||
value: {},
|
||||
);
|
||||
}
|
||||
|
||||
factory Command.playlistPrevious() {
|
||||
return Command(
|
||||
type: 'playlist_previous',
|
||||
value: {},
|
||||
);
|
||||
}
|
||||
|
||||
factory Command.playlistGoto(int position) {
|
||||
return Command(
|
||||
type: 'playlist_goto',
|
||||
value: {
|
||||
'position': position,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
factory Command.playlistClear() {
|
||||
return Command(
|
||||
type: 'playlist_clear',
|
||||
value: {},
|
||||
);
|
||||
}
|
||||
|
||||
factory Command.playlistRemove(int position) {
|
||||
return Command(
|
||||
type: 'playlist_remove',
|
||||
value: {
|
||||
'position': position,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
factory Command.playlistMove(int from, int to) {
|
||||
return Command(
|
||||
type: 'playlist_move',
|
||||
value: {
|
||||
'from': from,
|
||||
'to': to,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
factory Command.shuffle() {
|
||||
return Command(
|
||||
type: 'shuffle',
|
||||
value: {},
|
||||
);
|
||||
}
|
||||
|
||||
factory Command.setLooping(bool value) {
|
||||
return Command(
|
||||
type: 'set_looping',
|
||||
value: {
|
||||
'value': value,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
206
lib/api/events.dart
Normal file
206
lib/api/events.dart
Normal file
@@ -0,0 +1,206 @@
|
||||
// TODO: handle typing and deserialization of events
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
import '../state/player_state.dart';
|
||||
|
||||
// NOTE: see DEFAULT_PROPERTY_SUBSCRIPTIONS in the websocket API source for greg-ng.
|
||||
|
||||
@immutable
|
||||
sealed class Event {
|
||||
const Event();
|
||||
}
|
||||
|
||||
@immutable
|
||||
class ClearPlayerState extends Event {
|
||||
const ClearPlayerState() : super();
|
||||
}
|
||||
|
||||
@immutable
|
||||
class InitialPlayerState extends Event {
|
||||
final PlayerState playerState;
|
||||
|
||||
const InitialPlayerState(this.playerState) : super();
|
||||
|
||||
factory InitialPlayerState.fromJson(dynamic json) {
|
||||
return InitialPlayerState(PlayerState.fromJson(json));
|
||||
}
|
||||
}
|
||||
|
||||
@immutable
|
||||
sealed class PropertyChangedEvent extends Event {
|
||||
final bool local;
|
||||
|
||||
const PropertyChangedEvent({this.local = false}) : super();
|
||||
}
|
||||
|
||||
@immutable
|
||||
class PlaylistChange extends PropertyChangedEvent {
|
||||
final Playlist playlist;
|
||||
|
||||
const PlaylistChange(this.playlist, { super.local }) : super();
|
||||
|
||||
factory PlaylistChange.fromJson(dynamic json) {
|
||||
return PlaylistChange(
|
||||
(json as List).map((e) => PlaylistItem.fromJson(e)).toList(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@immutable
|
||||
class LoopPlaylistChange extends PropertyChangedEvent {
|
||||
final bool isLooping;
|
||||
|
||||
const LoopPlaylistChange(this.isLooping, { super.local }) : super();
|
||||
|
||||
factory LoopPlaylistChange.fromJson(dynamic json) {
|
||||
return LoopPlaylistChange(json == "inf");
|
||||
}
|
||||
}
|
||||
|
||||
@immutable
|
||||
class PercentPositionChange extends PropertyChangedEvent {
|
||||
final double currentPercentPosition;
|
||||
|
||||
const PercentPositionChange(this.currentPercentPosition, { super.local }) : super();
|
||||
|
||||
factory PercentPositionChange.fromJson(dynamic json) {
|
||||
return PercentPositionChange(json ?? 0.0);
|
||||
}
|
||||
}
|
||||
|
||||
@immutable
|
||||
class VolumeChange extends PropertyChangedEvent {
|
||||
final double volume;
|
||||
|
||||
const VolumeChange(this.volume, { super.local }) : super();
|
||||
|
||||
factory VolumeChange.fromJson(dynamic json) {
|
||||
return VolumeChange(json);
|
||||
}
|
||||
}
|
||||
|
||||
@immutable
|
||||
class DurationChange extends PropertyChangedEvent {
|
||||
final Duration duration;
|
||||
|
||||
const DurationChange(this.duration, { super.local }) : super();
|
||||
|
||||
factory DurationChange.fromJson(dynamic json) {
|
||||
return DurationChange(Duration(milliseconds: ((json ?? 0.0) * 1000).round()));
|
||||
}
|
||||
}
|
||||
|
||||
@immutable
|
||||
class PauseChange extends PropertyChangedEvent {
|
||||
final bool isPaused;
|
||||
|
||||
const PauseChange(this.isPaused, { super.local }) : super();
|
||||
|
||||
factory PauseChange.fromJson(dynamic json) {
|
||||
return PauseChange(json as bool);
|
||||
}
|
||||
}
|
||||
|
||||
@immutable
|
||||
class MuteChange extends PropertyChangedEvent {
|
||||
final bool isMuted;
|
||||
|
||||
const MuteChange(this.isMuted, { super.local }) : super();
|
||||
|
||||
factory MuteChange.fromJson(dynamic json) {
|
||||
return MuteChange(json as bool);
|
||||
}
|
||||
}
|
||||
|
||||
@immutable
|
||||
class TrackListChange extends PropertyChangedEvent {
|
||||
final List<SubtitleTrack> tracks;
|
||||
|
||||
const TrackListChange(this.tracks, { super.local }) : super();
|
||||
|
||||
factory TrackListChange.fromJson(dynamic json) {
|
||||
final trackList = json as List;
|
||||
trackList.retainWhere((e) => e is Map && e['type'] == 'sub');
|
||||
return TrackListChange(
|
||||
trackList.map((e) => SubtitleTrack.fromJson(e)).toList(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@immutable
|
||||
class DemuxerCacheStateChange extends PropertyChangedEvent {
|
||||
final double cachedTimestamp;
|
||||
|
||||
const DemuxerCacheStateChange(this.cachedTimestamp, { super.local }) : super();
|
||||
|
||||
factory DemuxerCacheStateChange.fromJson(dynamic json) {
|
||||
final demuxerCacheState = json as Map?;
|
||||
final cachedTimestamp =
|
||||
demuxerCacheState != null ? demuxerCacheState['cache-end'] ?? 0.0 : 0.0;
|
||||
return DemuxerCacheStateChange(cachedTimestamp);
|
||||
}
|
||||
}
|
||||
|
||||
@immutable
|
||||
class PausedForCacheChange extends PropertyChangedEvent {
|
||||
final bool isPausedForCache;
|
||||
|
||||
const PausedForCacheChange(this.isPausedForCache, { super.local }) : super();
|
||||
|
||||
factory PausedForCacheChange.fromJson(dynamic json) {
|
||||
return PausedForCacheChange(json as bool? ?? false);
|
||||
}
|
||||
}
|
||||
|
||||
// @immutable
|
||||
// class ChapterListChange extends PropertyChangedEvent {
|
||||
// final List<Chapter> chapters;
|
||||
|
||||
// ChapterListChange(this.chapters);
|
||||
|
||||
// factory ChapterListChange.fromJson(dynamic json) {
|
||||
// return ChapterListChange(
|
||||
// (json as List).map((e) => Chapter.fromJson(e)).toList(),
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
|
||||
Event? parseEvent(dynamic value) {
|
||||
if (value is String) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (value is Map && value.containsKey('property-change')) {
|
||||
final propertyChange = value['property-change'];
|
||||
switch (propertyChange['name']) {
|
||||
case 'playlist':
|
||||
return PlaylistChange.fromJson(propertyChange['data']);
|
||||
case 'loop-playlist':
|
||||
return LoopPlaylistChange.fromJson(propertyChange['data']);
|
||||
case 'percent-pos':
|
||||
return PercentPositionChange.fromJson(propertyChange['data']);
|
||||
case 'volume':
|
||||
return VolumeChange.fromJson(propertyChange['data']);
|
||||
case 'duration':
|
||||
return DurationChange.fromJson(propertyChange['data']);
|
||||
case 'pause':
|
||||
return PauseChange.fromJson(propertyChange['data']);
|
||||
case 'mute':
|
||||
return MuteChange.fromJson(propertyChange['data']);
|
||||
case 'track-list':
|
||||
return TrackListChange.fromJson(propertyChange['data']);
|
||||
case 'demuxer-cache-state':
|
||||
return DemuxerCacheStateChange.fromJson(propertyChange['data']);
|
||||
case 'paused-for-cache':
|
||||
return PausedForCacheChange.fromJson(propertyChange['data']);
|
||||
|
||||
// "chapter-list",
|
||||
// "paused-for-cache",
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
Reference in New Issue
Block a user