Skip to content

Commit a888489

Browse files
committed
Enhance media casting functionality in MediaController
Updated `CastMediaAsync` to include sample media options ("Sample Video", "Sample Audio", "Custom URL") and improved media type detection. Added error handling for invalid URL choices and ensured correct media receiver launch. Removed references to `ClearApplicationState` in multiple classes to streamline application state management, enhancing overall user experience and robustness of the feature.
1 parent fa4f359 commit a888489

File tree

6 files changed

+112
-111
lines changed

6 files changed

+112
-111
lines changed

SharpCaster.Console/Controllers/MediaController.cs

Lines changed: 81 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -28,76 +28,66 @@ public async Task CastMediaAsync()
2828
if (!await _deviceService.EnsureConnectedAsync())
2929
return;
3030

31-
var mediaTypes = new[]
31+
var urlOptions = new[]
3232
{
33-
"Video (MP4/WebM/etc)",
34-
"Audio (MP3/AAC/etc)",
35-
"Image (JPG/PNG/etc)"
33+
"Sample Video (Designing for Google Cast)",
34+
"Sample Audio (Arcane - Kevin MacLeod)",
35+
"Custom URL"
3636
};
3737

38-
var mediaType = AnsiConsole.Prompt(
38+
var urlChoice = AnsiConsole.Prompt(
3939
new SelectionPrompt<string>()
40-
.Title("[yellow]What type of media would you like to cast?[/]")
41-
.AddChoices(mediaTypes)
42-
.UseConverter(type => type switch
40+
.Title("[yellow]Select media to cast:[/]")
41+
.AddChoices(urlOptions)
42+
.UseConverter(choice => choice switch
4343
{
44-
"Video (MP4/WebM/etc)" => "🎬 Video (MP4/WebM/etc)",
45-
"Audio (MP3/AAC/etc)" => "🎵 Audio (MP3/AAC/etc)",
46-
"Image (JPG/PNG/etc)" => "🖼️ Image (JPG/PNG/etc)",
47-
_ => type
44+
"Sample Video (Designing for Google Cast)" => "🎬 Sample Video (Designing for Google Cast)",
45+
"Sample Audio (Arcane - Kevin MacLeod)" => "🎵 Sample Audio (Arcane - Kevin MacLeod)",
46+
"Custom URL" => "🔗 Custom URL",
47+
_ => choice
4848
}));
49-
50-
var url = AnsiConsole.Prompt(
51-
new TextPrompt<string>("[yellow]Enter media URL:[/]")
52-
.PromptStyle("green")
53-
.ValidationErrorMessage("[red]Please enter a valid URL[/]")
54-
.Validate(url => Uri.TryCreate(url, UriKind.Absolute, out _)));
5549

56-
var title = AnsiConsole.Prompt(
57-
new TextPrompt<string>("[yellow]Enter media title (optional):[/]")
58-
.PromptStyle("green")
59-
.AllowEmpty());
50+
string url;
51+
string title;
6052

61-
if (string.IsNullOrWhiteSpace(title))
62-
title = "Untitled Media";
53+
switch (urlChoice)
54+
{
55+
case "Sample Video (Designing for Google Cast)":
56+
url = "https://commondatastorage.googleapis.com/gtv-videos-bucket/CastVideos/mp4/DesigningForGoogleCast.mp4";
57+
title = "Designing for Google Cast";
58+
break;
59+
case "Sample Audio (Arcane - Kevin MacLeod)":
60+
url = "https://incompetech.com/music/royalty-free/mp3-royaltyfree/Arcane.mp3";
61+
title = "Arcane";
62+
break;
63+
case "Custom URL":
64+
url = AnsiConsole.Prompt(
65+
new TextPrompt<string>("[yellow]Enter media URL:[/]")
66+
.PromptStyle("green")
67+
.ValidationErrorMessage("[red]Please enter a valid URL[/]")
68+
.Validate(url => Uri.TryCreate(url, UriKind.Absolute, out _)));
69+
title = "Custom Media";
70+
break;
71+
default:
72+
throw new InvalidOperationException("Invalid URL choice");
73+
}
6374

6475
try
6576
{
6677
await AnsiConsole.Status()
6778
.Spinner(Spinner.Known.Star2)
6879
.SpinnerStyle(Style.Parse("yellow"))
69-
.StartAsync("Launching media receiver and loading content...", async ctx =>
80+
.StartAsync("Loading content...", async ctx =>
7081
{
71-
const string defaultMediaReceiver = "B3419EF5"; // Default Media Receiver
72-
73-
// Only launch application if we haven't already launched it or if it's different
74-
if (!_state.HasLaunchedApplication || _state.CurrentApplicationId != defaultMediaReceiver)
75-
{
76-
ctx.Status("Launching Default Media Receiver...");
77-
var receiver = await _state.Client!.LaunchApplicationAsync(defaultMediaReceiver, false);
78-
_state.SetApplicationLaunched(defaultMediaReceiver);
79-
}
80-
else
81-
{
82-
ctx.Status("Using already launched Default Media Receiver...");
83-
var receiver = await _state.Client!.LaunchApplicationAsync(defaultMediaReceiver);
84-
_state.SetApplicationLaunched(defaultMediaReceiver);
85-
}
86-
82+
var contentType = DetectMediaType(url);
8783
var media = new Media
8884
{
8985
ContentId = url,
90-
ContentType = mediaType switch
91-
{
92-
"Video (MP4/WebM/etc)" => "video/mp4",
93-
"Audio (MP3/AAC/etc)" => "audio/mpeg",
94-
"Image (JPG/PNG/etc)" => "image/jpeg",
95-
_ => "video/mp4"
96-
},
86+
ContentType = contentType,
9787
StreamType = StreamType.Buffered,
9888
Metadata = new MediaMetadata
9989
{
100-
MetadataType = MetadataType.Default,
90+
MetadataType = GetMetadataType(contentType),
10191
Title = title
10292
}
10393
};
@@ -152,17 +142,27 @@ await AnsiConsole.Status()
152142
.StartAsync("Loading website on Chromecast...", async ctx =>
153143
{
154144
const string dashboardReceiver = "F7FD2183";
155-
156-
// Only launch application if we haven't already launched it or if it's different
157-
if (!_state.HasLaunchedApplication || _state.CurrentApplicationId != dashboardReceiver)
145+
146+
if (_state.Client!.ChromecastStatus.Application?.AppId == dashboardReceiver)
158147
{
159-
ctx.Status("Launching dashboard receiver...");
160-
await _state.Client!.LaunchApplicationAsync(dashboardReceiver);
161-
_state.SetApplicationLaunched(dashboardReceiver);
148+
ctx.Status("Dashboard receiver already running");
162149
}
163150
else
164151
{
165-
ctx.Status("Using already launched dashboard receiver...");
152+
if (_state.Client.ChromecastStatus.Application != null && _state.Client.ChromecastStatus.Application.AppId != "E8C28D3C")
153+
{
154+
ctx.Status("Stopping previous application");
155+
await _state.Client.ReceiverChannel.StopApplication();
156+
await Task.Delay(300); // Wait for stop to complete
157+
await _state.Client.DisconnectAsync();
158+
await Task.Delay(500); // Wait disconnect to complete
159+
await _state.Client.ConnectChromecast(_state.SelectedDevice);
160+
}
161+
162+
ctx.Status("Launching dashboard receiver...");
163+
var status = await _state.Client.LaunchApplicationAsync(dashboardReceiver);
164+
if (status == null)
165+
throw new Exception("Failed to launch dashboard receiver");
166166
}
167167

168168
var req = new WebMessage
@@ -213,7 +213,6 @@ await AnsiConsole.Status()
213213
var app = receiverStatus.Applications.First();
214214
ctx.Status($"Stopping '{app.DisplayName}'...");
215215
await _state.Client.ReceiverChannel.StopApplication();
216-
_state.ClearApplicationState(); // Clear application state after stopping
217216
}
218217
else
219218
{
@@ -472,4 +471,29 @@ public async Task ShowDeviceStatusAsync()
472471
}
473472
}
474473
}
474+
475+
private static string DetectMediaType(string url)
476+
{
477+
var uri = new Uri(url);
478+
var extension = Path.GetExtension(uri.AbsolutePath).ToLowerInvariant();
479+
480+
return extension switch
481+
{
482+
".mp4" or ".webm" or ".avi" or ".mkv" or ".mov" => "video/mp4",
483+
".mp3" or ".aac" or ".wav" or ".flac" or ".ogg" => "audio/mpeg",
484+
".jpg" or ".jpeg" or ".png" or ".gif" or ".webp" => "image/jpeg",
485+
_ => "video/mp4" // Default to video
486+
};
487+
}
488+
489+
private static MetadataType GetMetadataType(string contentType)
490+
{
491+
return contentType switch
492+
{
493+
var ct when ct.StartsWith("video/") => MetadataType.Movie,
494+
var ct when ct.StartsWith("audio/") => MetadataType.Music,
495+
var ct when ct.StartsWith("image/") => MetadataType.Photo,
496+
_ => MetadataType.Default
497+
};
498+
}
475499
}

SharpCaster.Console/Controllers/QueueController.cs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -190,9 +190,6 @@ await AnsiConsole.Status()
190190
.SpinnerStyle(Style.Parse("green"))
191191
.StartAsync("Loading queue...", async ctx =>
192192
{
193-
ctx.Status("Launching Default Media Receiver...");
194-
await _state.Client!.LaunchApplicationAsync("CC1AD845"); // Default Media Receiver
195-
196193
ctx.Status($"Loading queue with {queueItems.Count} items...");
197194
await mediaChannel.QueueLoadAsync(queueItems.ToArray(), RepeatModeType.OFF, 0);
198195
});

SharpCaster.Console/Flows/ApplicationFlows.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,6 @@ public async Task MainApplicationFlowAsync()
261261
_state.Client?.Dispose();
262262
_state.Client = null;
263263
_state.SelectedDevice = null;
264-
_state.ClearApplicationState();
265264
_ui.AddSeparator("Switching to different device...");
266265
await DeviceSelectionFlowAsync();
267266
if (_state.IsConnected)
@@ -279,7 +278,6 @@ public async Task MainApplicationFlowAsync()
279278
_state.Client?.Dispose();
280279
_state.Client = null;
281280
_state.SelectedDevice = null;
282-
_state.ClearApplicationState();
283281
_ui.AddSeparator("Searching for new devices...");
284282
await InitialDiscoveryFlowAsync();
285283
await DeviceSelectionFlowAsync();

SharpCaster.Console/Models/ApplicationState.cs

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,4 @@ public class ApplicationState
1414
public ILogger? Logger { get; set; }
1515
public MdnsChromecastLocator? Locator { get; set; }
1616

17-
// Application state tracking
18-
public string? CurrentApplicationId { get; set; }
19-
public bool HasLaunchedApplication { get; set; }
20-
21-
public void SetApplicationLaunched(string applicationId)
22-
{
23-
CurrentApplicationId = applicationId;
24-
HasLaunchedApplication = true;
25-
}
26-
27-
public void ClearApplicationState()
28-
{
29-
CurrentApplicationId = null;
30-
HasLaunchedApplication = false;
31-
}
3217
}

SharpCaster.Console/Services/CommandExecutor.cs

Lines changed: 2 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,6 @@ public async Task<int> ExecuteCommandAsync(CommandLineArgs args)
161161
// Cleanup
162162
_state.Client?.Dispose();
163163
_state.Locator?.Dispose();
164-
_state.ClearApplicationState();
165164
}
166165
}
167166

@@ -226,7 +225,6 @@ private async Task ConnectToDeviceQuietAsync()
226225
_state.Client?.Dispose();
227226
_state.Client = null;
228227
_state.IsConnected = false;
229-
_state.ClearApplicationState();
230228
throw new Exception($"Connection failed: {ex.Message}");
231229
}
232230
}
@@ -335,22 +333,6 @@ private async Task<int> PlayMediaAsync(string url, string? title)
335333
{
336334
System.Console.WriteLine($"Casting media: {url}");
337335

338-
const string defaultMediaReceiver = "B3419EF5";
339-
340-
// Only launch application if we haven't already launched it or if it's different
341-
if (!_state.HasLaunchedApplication || _state.CurrentApplicationId != defaultMediaReceiver)
342-
{
343-
System.Console.WriteLine("Launching Default Media Receiver...");
344-
await _state.Client!.LaunchApplicationAsync(defaultMediaReceiver, false);
345-
_state.SetApplicationLaunched(defaultMediaReceiver);
346-
}
347-
else
348-
{
349-
System.Console.WriteLine("Using already launched Default Media Receiver...");
350-
await _state.Client!.LaunchApplicationAsync(defaultMediaReceiver);
351-
_state.SetApplicationLaunched(defaultMediaReceiver);
352-
}
353-
354336
var mediaType = DetectMediaType(url);
355337
var media = new Media
356338
{
@@ -425,7 +407,6 @@ private async Task<int> StopApplicationAsync()
425407
{
426408
var app = receiverStatus.Applications.First();
427409
await _state.Client.ReceiverChannel.StopApplication();
428-
_state.ClearApplicationState(); // Clear application state after stopping
429410
System.Console.WriteLine($"Application '{app.DisplayName}' stopped");
430411
}
431412
else
@@ -557,17 +538,8 @@ private async Task<int> StartWebsiteAsync(string url)
557538
{
558539
const string dashboardReceiver = "F7FD2183";
559540

560-
// Only launch application if we haven't already launched it or if it's different
561-
if (!_state.HasLaunchedApplication || _state.CurrentApplicationId != dashboardReceiver)
562-
{
563-
System.Console.WriteLine("Launching Dashboard Receiver...");
564-
await _state.Client!.LaunchApplicationAsync(dashboardReceiver, false);
565-
_state.SetApplicationLaunched(dashboardReceiver);
566-
}
567-
else
568-
{
569-
System.Console.WriteLine("Using already launched Dashboard Receiver...");
570-
}
541+
System.Console.WriteLine("Launching Dashboard Receiver...");
542+
await _state.Client!.LaunchApplicationAsync(dashboardReceiver, false);
571543

572544

573545
var req = new WebMessage

SharpCaster.Console/Services/DeviceService.cs

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,6 @@ await AnsiConsole.Status()
106106
_state.Client?.Dispose();
107107
_state.Client = null;
108108
_state.IsConnected = false;
109-
_state.ClearApplicationState();
110109
}
111110
}
112111

@@ -190,9 +189,21 @@ private async Task CheckAndJoinExistingApplicationAsync()
190189
var runningApp = receiverStatus.Applications.FirstOrDefault();
191190
if (runningApp != null && !string.IsNullOrEmpty(runningApp.DisplayName))
192191
{
193-
// Skip joining prompt for Backdrop (default screensaver app)
194-
if (runningApp.AppId == "E8C28D3C")
192+
// Skip joining prompt for Backdrop (default screensaver app) and Web Receiver (default web app)
193+
if (runningApp.AppId == "E8C28D3C" || runningApp.AppId == "F7FD2183")
195194
{
195+
AnsiConsole.MarkupLine("[dim]Skipping existing application. Launching Default Media Receiver...[/]");
196+
197+
await AnsiConsole.Status()
198+
.Spinner(Spinner.Known.Star)
199+
.SpinnerStyle(Style.Parse("blue"))
200+
.StartAsync("Launching Default Media Receiver...", async ctx =>
201+
{
202+
const string defaultMediaReceiver = "B3419EF5";
203+
await _state.Client.LaunchApplicationAsync(defaultMediaReceiver, false);
204+
});
205+
206+
AnsiConsole.MarkupLine("[green]✅ Default Media Receiver launched successfully![/]");
196207
return;
197208
}
198209

@@ -242,8 +253,22 @@ await AnsiConsole.Status()
242253
}
243254
else
244255
{
245-
AnsiConsole.MarkupLine("[dim]Skipping existing application. You can cast new media or control the device normally.[/]");
256+
AnsiConsole.MarkupLine("[dim]Skipping existing application. Launching Default Media Receiver...[/]");
257+
258+
await AnsiConsole.Status()
259+
.Spinner(Spinner.Known.Star)
260+
.SpinnerStyle(Style.Parse("blue"))
261+
.StartAsync("Launching Default Media Receiver...", async ctx =>
262+
{
263+
const string defaultMediaReceiver = "B3419EF5";
264+
await _state.Client.LaunchApplicationAsync(defaultMediaReceiver, false);
265+
});
266+
267+
AnsiConsole.MarkupLine("[green]✅ Default Media Receiver launched successfully![/]");
246268
}
269+
} else
270+
{
271+
var i = 0;
247272
}
248273
}
249274
}

0 commit comments

Comments
 (0)