Skip to content

Commit 0f37f09

Browse files
Add support for streaming updates (#299)
* Add support for streaming updates * PR * PR * PR * PR * Implement streaming in entity events * PR * PR * PR * PR * Clarify that playback info is only for oppo * PR * Fix regression * Fix bug with streaming response for play/pause and remove "retry" * PR * PR
1 parent dd323b6 commit 0f37f09

16 files changed

Lines changed: 2059 additions & 416 deletions

src/Oppo/Enums.cs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -123,9 +123,11 @@ public enum PlaybackStatus : sbyte
123123
[EnumExtensions(MetadataSource = MetadataSource.DisplayAttribute)]
124124
public enum DiscType : sbyte
125125
{
126+
Unknown = 1,
127+
126128
// ReSharper disable InconsistentNaming
127129
[Display(Name = "Blu-Ray Movie")]
128-
BlueRayMovie = 1,
130+
BlueRayMovie,
129131

130132
[Display(Name = "DVD Video")]
131133
DVDVideo,
@@ -151,7 +153,9 @@ public enum DiscType : sbyte
151153
UnknownDisc,
152154

153155
// Pre 20X models
154-
HDCD
156+
HDCD,
157+
VCD2,
158+
SVCD
155159
// ReSharper restore InconsistentNaming
156160
}
157161

@@ -292,6 +296,9 @@ public enum HDMIResolution : sbyte
292296
[Display(Name = "1080i 60Hz")]
293297
R1080i60,
294298

299+
[Display(Name = "1080p 23.97Hz")]
300+
R1080p23,
301+
295302
[Display(Name = "1080p 24Hz")]
296303
R1080p24,
297304

@@ -319,7 +326,9 @@ public enum HDMIResolution : sbyte
319326
Auto,
320327

321328
[Display(Name = "Source Direct")]
322-
SourceDirect
329+
SourceDirect,
330+
331+
Other
323332
// ReSharper restore InconsistentNaming
324333
}
325334

src/Oppo/IOppoClient.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,16 @@ public interface IOppoClient : IDisposable
425425
/// </summary>
426426
ValueTask<OppoResult<VerboseMode>> SetVerboseMode(VerboseMode verboseMode, CancellationToken cancellationToken = default);
427427

428+
/// <summary>
429+
/// <see langword="true"/> when this client supports unsolicited streaming updates.
430+
/// </summary>
431+
bool SupportsStreamingUpdates { get; }
432+
433+
/// <summary>
434+
/// Subscribe to streaming updates emitted by the player.
435+
/// </summary>
436+
IAsyncEnumerable<OppoStreamingEvent> SubscribeStreamingUpdates(CancellationToken cancellationToken = default);
437+
428438
/// <summary>
429439
/// Check if the client is connected.
430440
/// </summary>

src/Oppo/MagnetarClient.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -399,6 +399,11 @@ public ValueTask<OppoResult<string>> QueryTrackPerformerAsync(CancellationToken
399399
public ValueTask<OppoResult<VerboseMode>> SetVerboseMode(VerboseMode verboseMode, CancellationToken cancellationToken = default)
400400
=> ValueTask.FromResult(new OppoResult<VerboseMode> { Success = false });
401401

402+
public bool SupportsStreamingUpdates => false;
403+
404+
public IAsyncEnumerable<OppoStreamingEvent> SubscribeStreamingUpdates(CancellationToken cancellationToken = default)
405+
=> AsyncEnumerable.Empty<OppoStreamingEvent>();
406+
402407
public ValueTask<bool> IsConnectedAsync(TimeSpan? timeout = null)
403408
=> ConnectHelper.IsConnectedAsync(_tcpClient, _hostName, Port, _semaphore, _logger, timeout);
404409

src/Oppo/OppoClient.cs

Lines changed: 853 additions & 154 deletions
Large diffs are not rendered by default.

src/Oppo/OppoClientFactory.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,10 @@ public class OppoClientFactory(ILoggerFactory loggerFactory, ILogger<OppoClientF
4242
{
4343
_oppoClientLogger ??= _loggerFactory.CreateLogger<OppoClient>();
4444
client = new OppoClient(oppoClientKey.HostName, oppoClientKey.Model, _oppoClientLogger);
45+
await client.IsConnectedAsync();
46+
await client.SetVerboseMode(
47+
client.SupportsStreamingUpdates && oppoClientKey.UseStreamingEvents ? VerboseMode.DetailedStatus : VerboseMode.Off,
48+
cancellationToken);
4549
}
4650

4751
_clients[clientKeyHash] = client;

src/Oppo/OppoClientKey.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
namespace Oppo;
22

3-
public record struct OppoClientKey(string HostName, in OppoModel Model, in bool UseMediaEvents, in bool UseChapterLengthForMovies,
3+
public record struct OppoClientKey(string HostName, in OppoModel Model, in bool UseMediaEvents, in bool UseStreamingEvents, in bool UseChapterLengthForMovies,
44
string EntityId, string? DeviceId, string? MacAddress);

src/Oppo/OppoLogger.cs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,4 +81,40 @@ public static void FailedToSendCommandException(this ILogger logger, Exception e
8181
[LoggerMessage(EventId = 14, EventName = nameof(MissingMacAddress), Level = LogLevel.Error,
8282
Message = "MAC address is missing for entity ID '{EntityId}'")]
8383
public static partial void MissingMacAddress(this ILogger logger, string entityId);
84+
85+
[LoggerMessage(EventId = 15, EventName = nameof(ReceivedWireFrameRaw), Level = LogLevel.Trace,
86+
Message = "Received wire frame '{Frame}'")]
87+
public static partial void ReceivedWireFrameRaw(this ILogger logger, string? frame);
88+
89+
[LoggerMessage(EventId = 16, EventName = nameof(DroppedUnsubscribedStreamingFrame), Level = LogLevel.Trace,
90+
Message = "Dropped unsolicited streaming frame because there is no active subscriber: '{Frame}'")]
91+
public static partial void DroppedUnsubscribedStreamingFrame(this ILogger logger, string frame);
92+
93+
[LoggerMessage(EventId = 17, EventName = nameof(DroppedStreamingFrameBecauseChannelRejectedWrite), Level = LogLevel.Trace,
94+
Message = "Dropped unsolicited streaming frame because the subscriber channel rejected the write while still active: '{Frame}'")]
95+
public static partial void DroppedStreamingFrameBecauseChannelRejectedWrite(this ILogger logger, string frame);
96+
97+
[LoggerMessage(EventId = 18, EventName = nameof(DroppedStreamingFrameBecauseChannelCompleted), Level = LogLevel.Trace,
98+
Message = "Dropped unsolicited streaming frame because the subscriber channel was completed or replaced: '{Frame}'")]
99+
public static partial void DroppedStreamingFrameBecauseChannelCompleted(this ILogger logger, string frame);
100+
101+
[LoggerMessage(EventId = 19, EventName = nameof(ReplacingStreamingSubscriber), Level = LogLevel.Trace,
102+
Message = "Replacing existing streaming subscriber")]
103+
public static partial void ReplacingStreamingSubscriber(this ILogger logger);
104+
105+
[LoggerMessage(EventId = 20, EventName = nameof(UnknownStreamingStatusCode), Level = LogLevel.Debug,
106+
Message = "Received unknown streaming event. Frame '{Frame}'")]
107+
public static partial void UnknownStreamingStatusCode(this ILogger logger, string frame);
108+
109+
[LoggerMessage(EventId = 21, EventName = nameof(ReaderLoopCompletedWithPendingCommand), Level = LogLevel.Warning,
110+
Message = "Reader loop completed because the remote endpoint closed the stream while a command response was pending.")]
111+
public static partial void ReaderLoopCompletedWithPendingCommand(this ILogger logger);
112+
113+
[LoggerMessage(EventId = 22, EventName = nameof(ReaderLoopCanceledWithPendingCommand), Level = LogLevel.Debug,
114+
Message = "Reader loop canceled while a command response was pending.")]
115+
public static partial void ReaderLoopCanceledWithPendingCommand(this ILogger logger);
116+
117+
[LoggerMessage(EventId = 23, EventName = nameof(ReaderLoopFailedWithPendingCommand), Level = LogLevel.Warning,
118+
Message = "Reader loop failed while a command response was pending.")]
119+
public static partial void ReaderLoopFailedWithPendingCommand(this ILogger logger);
84120
}

src/Oppo/OppoResultCore.cs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,7 @@ internal readonly record struct OppoResultCore(
1111
[MemberNotNullWhen(true, nameof(Response))]
1212
public bool Success { get; } = Success;
1313

14-
public bool InvalidVerboseLevel { get; private init; }
15-
1614
public static readonly OppoResultCore FalseResult = new(false, null);
17-
public static readonly OppoResultCore InvalidVerboseLevelResult = new(false, null) { InvalidVerboseLevel = true };
1815

1916
public static OppoResultCore SuccessResult(string response) => new(true, response);
2017
}

src/Oppo/OppoStreamingEvent.cs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
namespace Oppo;
2+
3+
public enum OppoTimeCodeType : sbyte
4+
{
5+
Unknown = 1,
6+
TotalElapsed,
7+
TotalRemaining,
8+
TitleElapsed,
9+
TitleRemaining,
10+
ChapterElapsed,
11+
ChapterRemaining
12+
}
13+
14+
public abstract record OppoStreamingEvent;
15+
16+
public sealed record OppoUnknownStreamingEvent
17+
: OppoStreamingEvent;
18+
19+
public sealed record OppoPowerStateStreamingEvent(in PowerState PowerState)
20+
: OppoStreamingEvent;
21+
22+
public sealed record OppoPlaybackStatusStreamingEvent(in PlaybackStatus PlaybackStatus)
23+
: OppoStreamingEvent;
24+
25+
public sealed record OppoVolumeStreamingEvent(in VolumeInfo VolumeInfo)
26+
: OppoStreamingEvent;
27+
28+
// ReSharper disable once NotAccessedPositionalProperty.Global
29+
public sealed record OppoDiscTypeStreamingEvent(in DiscType DiscType)
30+
: OppoStreamingEvent;
31+
32+
// ReSharper disable once NotAccessedPositionalProperty.Global
33+
public sealed record OppoInputSourceStreamingEvent(in InputSource InputSource)
34+
: OppoStreamingEvent;
35+
36+
public sealed record OppoVideoResolutionStreamingEvent(in HDMIResolution Resolution)
37+
: OppoStreamingEvent;
38+
39+
public sealed record OppoAudioTypeStreamingEvent(string AudioType)
40+
: OppoStreamingEvent;
41+
42+
public sealed record OppoSubtitleTypeStreamingEvent(string SubtitleType)
43+
: OppoStreamingEvent;
44+
45+
public sealed record OppoThreeDStatusStreamingEvent(in bool Is3D)
46+
: OppoStreamingEvent;
47+
48+
public sealed record OppoAspectRatioStreamingEvent(in AspectRatio AspectRatio)
49+
: OppoStreamingEvent;
50+
51+
public sealed record OppoPlaybackProgressStreamingEvent(
52+
in ushort Title,
53+
in ushort Chapter,
54+
in OppoTimeCodeType TimeCodeType,
55+
in uint Seconds)
56+
: OppoStreamingEvent;
57+

src/UnfoldedCircle.OppoBluRay/AlbumCover/AlbumCoverService.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ namespace UnfoldedCircle.OppoBluRay.AlbumCover;
99

1010
public interface IAlbumCoverService
1111
{
12-
Task<Uri?> GetAlbumCoverAsync(string artist, string? album, string? track, CancellationToken cancellationToken = default);
12+
ValueTask<Uri?> GetAlbumCoverAsync(string artist, string? album, string? track, CancellationToken cancellationToken = default);
1313
}
1414

1515
internal sealed class AlbumCoverService(
@@ -22,7 +22,7 @@ internal sealed class AlbumCoverService(
2222
private readonly ILogger<AlbumCoverService> _logger = logger;
2323
private static readonly TimeSpan CacheDuration = TimeSpan.FromHours(3);
2424

25-
public async Task<Uri?> GetAlbumCoverAsync(string artist, string? album, string? track, CancellationToken cancellationToken = default)
25+
public async ValueTask<Uri?> GetAlbumCoverAsync(string artist, string? album, string? track, CancellationToken cancellationToken = default)
2626
{
2727
if (string.IsNullOrWhiteSpace(album))
2828
{

0 commit comments

Comments
 (0)