Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions src/SystemMedia/Core/MediaTime.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace SystemMedia.Core;

// Players report lengths they do not know as huge numbers (Chromium sends Int64.MaxValue), beyond what TimeSpan holds.
internal static class MediaTime
{
private const double MaxMicroseconds = long.MaxValue / TimeSpan.TicksPerMicrosecond;

internal static TimeSpan? FromMicroseconds(double micros) =>
micros is >= 0 and < MaxMicroseconds ? TimeSpan.FromMicroseconds(micros) : null;

internal static TimeSpan? FromSeconds(double seconds) => FromMicroseconds(seconds * 1_000_000);
}
7 changes: 5 additions & 2 deletions src/SystemMedia/Platform/Linux/MprisPlayer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,13 +75,16 @@ internal MediaSnapshot ToSnapshot(DateTimeOffset readAt)
_ => null,
},
StartTime = TimeSpan.Zero,
EndTime = length is > 0 ? TimeSpan.FromMicroseconds(length.Value) : TimeSpan.Zero,
Position = PositionMicros is > 0 ? TimeSpan.FromMicroseconds(PositionMicros.Value) : TimeSpan.Zero,
EndTime = Duration(length),
Position = Duration(PositionMicros),
// Position is fetched on every read, so it is current as of now.
LastUpdatedTime = readAt,
};
}

private static TimeSpan Duration(double? micros) =>
micros is { } value ? MediaTime.FromMicroseconds(value) ?? TimeSpan.Zero : TimeSpan.Zero;

private IReadOnlyDictionary<string, object?> Metadata =>
Properties.GetValueOrDefault("Metadata") as IReadOnlyDictionary<string, object?> ?? _empty;

Expand Down
4 changes: 2 additions & 2 deletions src/SystemMedia/Platform/MacOS/NowPlayingState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@ internal sealed record NowPlayingInfo
_ => null,
},
StartTime = TimeSpan.Zero,
EndTime = DurationMicros is { } duration ? TimeSpan.FromMicroseconds(duration) : TimeSpan.Zero,
Position = ElapsedMicros is { } elapsed ? TimeSpan.FromMicroseconds(elapsed) : TimeSpan.Zero,
EndTime = DurationMicros is { } duration ? MediaTime.FromMicroseconds(duration) ?? TimeSpan.Zero : TimeSpan.Zero,
Position = ElapsedMicros is { } elapsed ? MediaTime.FromMicroseconds(elapsed) ?? TimeSpan.Zero : TimeSpan.Zero,
LastUpdatedTime = TimestampEpochMicros is { } stamp
? DateTimeOffset.FromUnixTimeMilliseconds(stamp / 1000)
: DateTimeOffset.MinValue,
Expand Down
4 changes: 1 addition & 3 deletions src/SystemMedia/Players/MpvPlayer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -233,9 +233,7 @@ private static bool HasThumbnail(JsonElement? video, TimeSpan? position, TimeSpa
}

private static TimeSpan? Seconds(JsonElement? value) =>
value is { ValueKind: JsonValueKind.Number } number && number.GetDouble() is var seconds && seconds >= 0 && double.IsFinite(seconds)
? TimeSpan.FromSeconds(seconds)
: null;
value is { ValueKind: JsonValueKind.Number } number ? MediaTime.FromSeconds(number.GetDouble()) : null;

private static void TryDelete(string file)
{
Expand Down
13 changes: 13 additions & 0 deletions tests/SystemMedia.Tests/Linux/MprisPlayerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,19 @@ public void A_length_sent_as_a_double_still_counts()
Assert.That(snapshot.EndTime, Is.EqualTo(TimeSpan.FromMinutes(1)));
}

[Test]
public void A_length_too_long_for_a_timespan_counts_as_none()
{
var snapshot = Player(new()
{
["Metadata"] = new Dictionary<string, object?> { ["mpris:length"] = long.MaxValue },
["Position"] = long.MaxValue,
}).ToSnapshot(_readAt);

Assert.That(snapshot.HasDuration, Is.False);
Assert.That(snapshot.Position, Is.EqualTo(TimeSpan.Zero));
}

[Test]
public void The_no_track_id_is_not_a_track_to_seek_in()
{
Expand Down
27 changes: 27 additions & 0 deletions tests/SystemMedia.Tests/MediaTimeTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using NUnit.Framework;
using SystemMedia.Core;

namespace SystemMedia.Tests;

[TestFixture]
public sealed class MediaTimeTests
{
[Test]
public void A_reported_time_converts()
{
Assert.That(MediaTime.FromMicroseconds(60_000_000), Is.EqualTo(TimeSpan.FromMinutes(1)));
Assert.That(MediaTime.FromSeconds(90.5), Is.EqualTo(TimeSpan.FromSeconds(90.5)));
Assert.That(MediaTime.FromSeconds(0), Is.EqualTo(TimeSpan.Zero));
}

[TestCase(long.MaxValue)]
[TestCase(1e300)]
[TestCase(-1)]
[TestCase(double.NaN)]
[TestCase(double.PositiveInfinity)]
public void A_time_beyond_what_a_timespan_holds_is_unknown(double value)
{
Assert.That(MediaTime.FromMicroseconds(value), Is.Null);
Assert.That(MediaTime.FromSeconds(value), Is.Null);
}
}
Loading