Skip to content

Commit

Permalink
Added support for setting repeat mode on queue load
Browse files Browse the repository at this point in the history
  • Loading branch information
Tapanila committed Jul 20, 2024
1 parent 7535347 commit f29751a
Show file tree
Hide file tree
Showing 8 changed files with 215 additions and 10 deletions.
104 changes: 101 additions & 3 deletions Sharpcaster.Test/MediaChannelTester.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
using Sharpcaster.Channels;
using Sharpcaster.Interfaces;
using Sharpcaster.Interfaces;
using Sharpcaster.Models;
using Sharpcaster.Models.Media;
using Sharpcaster.Models.Queue;
using Sharpcaster.Test.helper;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
Expand Down Expand Up @@ -382,6 +380,106 @@ public async Task TestJoiningRunningMediaSessionAndPausingMedia(ChromecastReceiv
await client.GetChannel<IMediaChannel>().PauseAsync();
}

[Theory]
[MemberData(nameof(ChromecastReceiversFilter.GetChromecastUltra), MemberType = typeof(ChromecastReceiversFilter))]
public async Task TestRepeatingAllQueueMedia(ChromecastReceiver receiver)
{
var TestHelper = new TestHelper();
ChromecastClient client = await TestHelper.CreateConnectAndLoadAppClient(output, receiver);

var media = new Media
{
ContentUrl = "https://incompetech.com/music/royalty-free/mp3-royaltyfree/Loping%20Sting.mp3"
};

var queueItem = new QueueItem
{
Media = media,

};


await client.GetChannel<IMediaChannel>().QueueLoadAsync([queueItem], null, RepeatModeType.ALL);
var test = await client.GetChannel<IMediaChannel>().PlayAsync();

Assert.Equal(RepeatModeType.ALL, test.RepeatMode);
}

[Theory]
[MemberData(nameof(ChromecastReceiversFilter.GetChromecastUltra), MemberType = typeof(ChromecastReceiversFilter))]
public async Task TestRepeatingOffQueueMedia(ChromecastReceiver receiver)
{
var TestHelper = new TestHelper();
ChromecastClient client = await TestHelper.CreateConnectAndLoadAppClient(output, receiver);

var media = new Media
{
ContentUrl = "https://incompetech.com/music/royalty-free/mp3-royaltyfree/Loping%20Sting.mp3"
};

var queueItem = new QueueItem
{
Media = media,

};


await client.GetChannel<IMediaChannel>().QueueLoadAsync([queueItem], null, RepeatModeType.OFF);
var test = await client.GetChannel<IMediaChannel>().PlayAsync();

Assert.Equal(RepeatModeType.OFF, test.RepeatMode);
}

[Theory]
[MemberData(nameof(ChromecastReceiversFilter.GetChromecastUltra), MemberType = typeof(ChromecastReceiversFilter))]
public async Task TestRepeatingSingleQueueMedia(ChromecastReceiver receiver)
{
var TestHelper = new TestHelper();
ChromecastClient client = await TestHelper.CreateConnectAndLoadAppClient(output, receiver);

var media = new Media
{
ContentUrl = "https://incompetech.com/music/royalty-free/mp3-royaltyfree/Loping%20Sting.mp3"
};

var queueItem = new QueueItem
{
Media = media,

};


await client.GetChannel<IMediaChannel>().QueueLoadAsync([queueItem], null, RepeatModeType.SINGLE);
var test = await client.GetChannel<IMediaChannel>().PlayAsync();

Assert.Equal(RepeatModeType.SINGLE, test.RepeatMode);
}

[Theory]
[MemberData(nameof(ChromecastReceiversFilter.GetChromecastUltra), MemberType = typeof(ChromecastReceiversFilter))]
public async Task TestRepeatingAllAndShuffleQueueMedia(ChromecastReceiver receiver)
{
var TestHelper = new TestHelper();
ChromecastClient client = await TestHelper.CreateConnectAndLoadAppClient(output, receiver);

var media = new Media
{
ContentUrl = "https://incompetech.com/music/royalty-free/mp3-royaltyfree/Loping%20Sting.mp3"
};

var queueItem = new QueueItem
{
Media = media,

};


await client.GetChannel<IMediaChannel>().QueueLoadAsync([queueItem], null, RepeatModeType.ALL_AND_SHUFFLE);
var test = await client.GetChannel<IMediaChannel>().PlayAsync();

Assert.Equal(RepeatModeType.ALL_AND_SHUFFLE, test.RepeatMode);
}

}

}
4 changes: 2 additions & 2 deletions Sharpcaster/Channels/MediaChannel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -105,10 +105,10 @@ public async Task<MediaStatus> SeekAsync(double seconds)
return await SendAsync(new SeekMessage() { CurrentTime = seconds });
}

public async Task<MediaStatus> QueueLoadAsync(QueueItem[] items)
public async Task<MediaStatus> QueueLoadAsync(QueueItem[] items, int? currentTime = null, RepeatModeType repeatMode = RepeatModeType.OFF, int? startIndex = null)
{
var chromecastStatus = Client.GetChromecastStatus();
return (await SendAsync<MediaStatusMessage>(new QueueLoadMessage() { SessionId = chromecastStatus.Applications[0].SessionId, Items = items }, chromecastStatus.Applications[0].TransportId)).Status?.FirstOrDefault();
return (await SendAsync<MediaStatusMessage>(new QueueLoadMessage() { SessionId = chromecastStatus.Applications[0].SessionId, Items = items, CurrentTime = currentTime, RepeatMode = repeatMode, StartIndex = startIndex }, chromecastStatus.Applications[0].TransportId)).Status?.FirstOrDefault();
}

public async Task<MediaStatus> QueueNextAsync(long mediaSessionId)
Expand Down
61 changes: 61 additions & 0 deletions Sharpcaster/Converters/RepeatModeEnumConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
using Newtonsoft.Json;
using Sharpcaster.Models.Media;
using System;
using System.Collections.Generic;
using System.Text;

namespace Sharpcaster.Converters
{
public class RepeatModeEnumConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var repeatModeType = (RepeatModeType)value;
switch (repeatModeType)
{
case RepeatModeType.OFF:
writer.WriteValue("REPEAT_OFF");
break;
case RepeatModeType.ALL:
writer.WriteValue("REPEAT_ALL");
break;
case RepeatModeType.SINGLE:
writer.WriteValue("REPEAT_SINGLE");
break;
case RepeatModeType.ALL_AND_SHUFFLE:
writer.WriteValue("REPEAT_ALL_AND_SHUFFLE");
break;
default:
throw new ArgumentOutOfRangeException();
}
}

public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var enumString = (string)reader.Value;
RepeatModeType? repeatModeType = null;

switch (enumString)
{
case "REPEAT_OFF":
repeatModeType = RepeatModeType.OFF;
break;
case "REPEAT_ALL":
repeatModeType = RepeatModeType.ALL;
break;
case "REPEAT_SINGLE":
repeatModeType = RepeatModeType.SINGLE;
break;
case "REPEAT_ALL_AND_SHUFFLE":
repeatModeType = RepeatModeType.ALL_AND_SHUFFLE;
break;
}
return repeatModeType;
}

public override bool CanConvert(Type objectType)
{
return objectType == typeof(string);
}
}
}
2 changes: 1 addition & 1 deletion Sharpcaster/Interfaces/IMediaChannel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public interface IMediaChannel : IStatusChannel<IEnumerable<MediaStatus>>, IChro
/// <param name="seconds">time in seconds</param>
/// <returns>media status</returns>
Task<MediaStatus> SeekAsync(double seconds);
Task<MediaStatus> QueueLoadAsync(QueueItem[] items);
Task<MediaStatus> QueueLoadAsync(QueueItem[] items, int? currentTime = null, RepeatModeType repeatMode = RepeatModeType.OFF, int? startIndex = null);
Task<MediaStatus> QueueNextAsync(long mediaSessionId);
Task<MediaStatus> QueuePrevAsync(long mediaSessionId);
Task<QueueItem[]> QueueGetItemsAsync(long mediaSessionId, int[] ids = null);
Expand Down
34 changes: 33 additions & 1 deletion Sharpcaster/Messages/Queue/QueueLoadMessage.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
using Sharpcaster.Models.Queue;
using Newtonsoft.Json;
using Sharpcaster.Converters;
using Sharpcaster.Models.Media;
using Sharpcaster.Models.Queue;
using System.Runtime.Serialization;

namespace Sharpcaster.Messages.Queue
Expand All @@ -8,5 +11,34 @@ public class QueueLoadMessage : MessageWithSession
{
[DataMember(Name = "items")]
public QueueItem[] Items { get; set; }

/// <summary>
/// The index of the item in the items array that must be the first currentItem (the item that will be played first).
/// Note this is the index of the array (starts at 0) and not the itemId (as it is not known until the queue is created).
/// If repeatMode is REPEAT_OFF playback will end when the last item in the array is played (elements before the startIndex will not be played).
/// This may be useful for continuation scenarios where the user was already using the sender app and in the middle decides to cast.
/// In this way the sender app does not need to map between the local and remote queue positions or saves one extra QUEUE_UPDATE request.
/// </summary>
[DataMember(Name = "startIndex")]
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public int? StartIndex { get; set; }
/// <summary>
/// Behavior of the queue when all items have been played.
/// </summary>

[DataMember(Name = "repeatMode")]
[JsonConverter(typeof(RepeatModeEnumConverter))]
public RepeatModeType RepeatMode { get; set; }

/// <summary>
/// Seconds (since the beginning of content) to start playback of the first item to be played.
/// If provided, this value will take precedence over the startTime value provided at the QueueItem level but only the first time the item is played.
/// This is to cover the common case where the user casts the item that was playing locally so the currentTime does not apply to the item permanently like the QueueItem startTime does.
/// It avoids having to reset the startTime dynamically (that may not be possible if the phone has gone to sleep).
/// </summary>

[DataMember(Name = "currentTime")]
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public int? CurrentTime { get; set; }
}
}
3 changes: 2 additions & 1 deletion Sharpcaster/Models/Media/MediaStatus.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,8 @@ public class MediaStatus
/// Gets or sets the repeat mode
/// </summary>
[DataMember(Name = "repeatMode")]
public string RepeatMode { get; set; }
[JsonConverter(typeof(RepeatModeEnumConverter))]
public RepeatModeType RepeatMode { get; set; }

[DataMember(Name = "queueData")]
public QueueData QueueData { get; set; }
Expand Down
10 changes: 10 additions & 0 deletions Sharpcaster/Models/Media/RepeatModeType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace Sharpcaster.Models.Media
{
public enum RepeatModeType
{
OFF,
ALL,
SINGLE,
ALL_AND_SHUFFLE
}
}
7 changes: 5 additions & 2 deletions Sharpcaster/Models/Queue/QueueData.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using Sharpcaster.Models.Media;
using Newtonsoft.Json;
using Sharpcaster.Converters;
using Sharpcaster.Models.Media;
using System.Runtime.Serialization;

namespace Sharpcaster.Models.Queue
Expand Down Expand Up @@ -40,7 +42,8 @@ public class QueueData
/// The continuous playback behavior of the queue.
/// </summary>
[DataMember(Name = "repeatMode")]
public string RepeatMode { get; set; }
[JsonConverter(typeof(RepeatModeEnumConverter))]
public RepeatModeType RepeatMode { get; set; }
/// <summary>
/// True indicates that the queue is shuffled.
/// </summary>
Expand Down

0 comments on commit f29751a

Please sign in to comment.