@@ -28,76 +28,66 @@ public async Task CastMediaAsync()
28
28
if ( ! await _deviceService . EnsureConnectedAsync ( ) )
29
29
return ;
30
30
31
- var mediaTypes = new [ ]
31
+ var urlOptions = new [ ]
32
32
{
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 "
36
36
} ;
37
37
38
- var mediaType = AnsiConsole . Prompt (
38
+ var urlChoice = AnsiConsole . Prompt (
39
39
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
43
43
{
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
48
48
} ) ) ;
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 _ ) ) ) ;
55
49
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 ;
60
52
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
+ }
63
74
64
75
try
65
76
{
66
77
await AnsiConsole . Status ( )
67
78
. Spinner ( Spinner . Known . Star2 )
68
79
. SpinnerStyle ( Style . Parse ( "yellow" ) )
69
- . StartAsync ( "Launching media receiver and loading content..." , async ctx =>
80
+ . StartAsync ( "Loading content..." , async ctx =>
70
81
{
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 ) ;
87
83
var media = new Media
88
84
{
89
85
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 ,
97
87
StreamType = StreamType . Buffered ,
98
88
Metadata = new MediaMetadata
99
89
{
100
- MetadataType = MetadataType . Default ,
90
+ MetadataType = GetMetadataType ( contentType ) ,
101
91
Title = title
102
92
}
103
93
} ;
@@ -152,17 +142,27 @@ await AnsiConsole.Status()
152
142
. StartAsync ( "Loading website on Chromecast..." , async ctx =>
153
143
{
154
144
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 )
158
147
{
159
- ctx . Status ( "Launching dashboard receiver..." ) ;
160
- await _state . Client ! . LaunchApplicationAsync ( dashboardReceiver ) ;
161
- _state . SetApplicationLaunched ( dashboardReceiver ) ;
148
+ ctx . Status ( "Dashboard receiver already running" ) ;
162
149
}
163
150
else
164
151
{
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" ) ;
166
166
}
167
167
168
168
var req = new WebMessage
@@ -213,7 +213,6 @@ await AnsiConsole.Status()
213
213
var app = receiverStatus . Applications . First ( ) ;
214
214
ctx . Status ( $ "Stopping '{ app . DisplayName } '...") ;
215
215
await _state . Client . ReceiverChannel . StopApplication ( ) ;
216
- _state . ClearApplicationState ( ) ; // Clear application state after stopping
217
216
}
218
217
else
219
218
{
@@ -472,4 +471,29 @@ public async Task ShowDeviceStatusAsync()
472
471
}
473
472
}
474
473
}
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
+ }
475
499
}
0 commit comments