-
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathindex.html
341 lines (317 loc) · 17.3 KB
/
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description"
content="Control the volume and manage streaming apps on your Chromecast-enabled devices directly from your browser.">
<meta name="author" content="@SilviuStroe">
<meta name="keywords" content="Google Cast, Chromecast, Volume Control, Streaming, Media Control">
<meta name="google-site-verification" content="wByruZ2Gy-r5VEMOgQZdlt5XtB-3W1Oo1EiV3aFIviI"/>
<title>Chromecast Volume Control</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="manifest" href="manifest.json">
<link rel="apple-touch-icon" sizes="180x180" href="favicons/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="favicons/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="favicons/favicon-16x16.png">
<script type="application/ld+json">
{
"@context": "http://schema.org",
"@graph": [
{
"@type": "WebSite",
"name": "Chromecast Volume Control",
"url": "https://castvolumecontrol.pages.dev",
"author": {
"@type": "Person",
"name": "@SilviuStroe"
},
"description": "Control the volume and manage streaming apps on your Chromecast-enabled devices directly from your browser."
},
{
"@type": "WebPage",
"name": "Chromecast Volume Control",
"description": "Manage the volume of your Chromecast-enabled devices and switch between your favorite streaming apps with ease.",
"url": "https://castvolumecontrol.pages.dev"
},
{
"@type": "SoftwareApplication",
"name": "Chromecast Volume Control",
"applicationCategory": "Multimedia",
"operatingSystem": "Web",
"offers": {
"@type": "Offer",
"price": "0",
"priceCurrency": "USD"
},
"featureList": "Volume control for Chromecast devices, Streaming app management, Easy-to-use interface, No installation required",
"description": "A web-based tool to control the volume of Chromecast-enabled devices. Easily manage streaming apps and adjust volume settings directly from your browser."
}
]
}
</script>
</head>
<body class="font-sans bg-gray-100 dark:bg-gray-900 text-gray-800 dark:text-gray-200 m-0 flex flex-col justify-center items-center p-2 md:p-4 lg:p-6 min-h-screen">
<div class="control-panel bg-white dark:bg-gray-800 p-4 md:p-6 lg:p-10 rounded-lg shadow-md w-full md:w-4/5 max-w-xl mx-auto overflow-auto">
<h1 class="text-xl md:text-3xl font-bold text-gray-800 dark:text-gray-100 mb-6">Chromecast Volume Control</h1>
<p class="text-base text-gray-600 dark:text-gray-300 mb-6 leading-relaxed">
Manage the volume of your Chromecast-enabled devices and switch between your favorite streaming apps with ease.
</p>
<div class="app-select-container mb-6">
<label for="appSelect" class="block mb-2 font-bold dark:text-gray-100">Choose the app playing on your
device:</label>
<select id="appSelect" required
class="w-full p-2 rounded border border-gray-300 dark:border-gray-600 mt-1 bg-gray-100 dark:bg-gray-700 text-gray-800 dark:text-gray-200">
<option value="">Select an app</option>
<option value="CC32E753">Spotify</option>
<option value="2DB7CC49">YouTube Music</option>
<option value="B143C57E">SoundCloud</option>
<option value="CA5E8412">Netflix</option>
<option value="9AC194DC">Plex</option>
<option value="233637DE">YouTube</option>
<option value="C3DE6BC2">Disney+</option>
<option value="10AAD887">All 4</option>
<option value="B88B034A">Dailymotion</option>
<option value="B3DCF968">Twitch</option>
<option value="2BA92214">BBC iPlayer</option>
<option value="5E645BCC">BubbleUPnP</option>
<option value="12F05308">TuneIn Free</option>
<option value="211CD751">Pandora</option>
<option value="0F5096E8">Chrome Mirroring</option>
<option value="85CDB22F">Chrome Audio Mirroring</option>
<option value="CC1AD845">Default Media Receiver</option>
</select>
</div>
<div class="flex justify-between items-center mb-6">
<button id="castButton"
class="bg-blue-600 dark:bg-blue-700 text-white py-2 px-4 rounded cursor-pointer text-lg transition-colors duration-300 w-1/2 mr-2 hover:bg-blue-700 dark:hover:bg-blue-800">
Connect to Device
</button>
<button id="stopButton" style="display: none"
class="bg-red-600 dark:bg-red-700 text-white py-2 px-4 rounded cursor-pointer text-lg transition-colors duration-300 w-1/2 ml-2 hover:bg-red-700 dark:hover:bg-red-800">
Stop Media
</button>
</div>
<div class="volume-container mb-6">
<label for="volumeControl" class="block text-lg text-gray-600 dark:text-gray-300 mb-2">Volume:</label>
<div class="slider-container flex items-center">
<input type="range" id="volumeControl" min="0" max="100" step="1" disabled class="flex-grow mr-2">
<span id="volumeValue" class="volume-value text-lg text-gray-800 dark:text-gray-200">0</span>
</div>
</div>
<div class="media-info">
<h3 id="mediaTitle" class="text-lg font-bold text-gray-700 dark:text-white mb-2"></h3>
<img id="mediaImage" src="#" alt="Media Thumbnail" class="hidden rounded-lg w-full md:w-48 h-auto object-cover">
</div>
</div>
<div class="use-cases bg-gray-200 dark:bg-gray-800 p-5 mt-8 rounded-lg shadow-md w-full md:w-4/5 max-w-xl mx-auto">
<h2 class="text-2xl font-semibold text-gray-700 dark:text-gray-100">How Chromecast Volume Control Enhances Your
Experience</h2>
<p>Discover how users from various backgrounds use our app to streamline their media experience:</p>
<ul class="list-disc ml-5">
<li class="dark:text-gray-300"><strong>For Work and Play:</strong> Seamlessly adjust the volume during
work-from-home sessions or while enjoying a movie night, all from your browser.
</li>
<li class="dark:text-gray-300"><strong>Music Lovers:</strong> Effortlessly control the volume of your favorite
Spotify playlists or YouTube Music tracks on your Chromecast-enabled devices.
</li>
<li class="dark:text-gray-300"><strong>Party Hosts:</strong> Keep the party going by controlling the music
volume on your Chromecast-enabled TV or speakers without missing a beat.
</li>
<li class="dark:text-gray-300"><strong>Desktop Users:</strong> Desktop users can effortlessly control their
Google Nest or other Chromecast-enabled devices directly from their browser.
</li>
<li class="dark:text-gray-300"><strong>Accessibility:</strong> Those with mobility challenges can easily adjust
device settings without needing to physically access them.
</li>
<li class="dark:text-gray-300"><strong>Privacy First:</strong> This app operates without any backend servers and
requires no installations, ensuring your data stays with you.
</li>
</ul>
</div>
<footer class="bg-gray-100 dark:bg-gray-900 text-gray-800 dark:text-gray-200 my-2">
<div class="container mx-auto px-4 py-5 text-center">
<p class="mb-4">© 2023 Chromecast Volume Control. All Rights Reserved.</p>
<div class="flex justify-center space-x-4">
<a rel="noopener noreferrer" class="github-button" href="https://github.com/BrainicHQ/CastVolumeControl"
data-color-scheme="no-preference: light; light: light; dark: dark;" data-show-count="true"
aria-label="Star BrainicHQ/CastVolumeControl on GitHub">Star on GitHub</a>
<a href="https://www.buymeacoffee.com/silviu" target="_blank" rel="noopener noreferrer"
aria-label="Support me on Buy Me a Coffee" class="text-blue-500 dark:text-blue-400 hover:underline">☕️
Support this project</a>
<a class="github-button" href="https://github.com/BrainicHQ/CastVolumeControl/issues"
data-color-scheme="no-preference: light; light: light; dark: dark;" data-icon="octicon-issue-opened"
data-show-count="true" aria-label="Issue BrainicHQ/CastVolumeControl on GitHub">Report Issue 🐛</a>
</div>
</div>
</footer>
<script async defer src="https://buttons.github.io/buttons.js"></script>
<script>
// Import cast framework
if (window.chrome && !window.chrome.cast) {
const script = document.createElement('script');
script.src = 'https://www.gstatic.com/cv/js/sender/v1/cast_sender.js?loadCastFramework=1';
script.onload = function () {
window['__onGCastApiAvailable'] = function (isAvailable) {
if (isAvailable) {
initializeCastApi();
} else {
console.error('Google Cast SDK not available.');
}
};
};
document.head.appendChild(script);
} else {
console.error('Chromecast not supported in this browser.');
displayCastSupportError();
}
function displayCastSupportError() {
// Display an error message to the user
alert("Sorry, Chromecast is not supported in your browser.");
// Optionally, disable or hide Chromecast-related UI elements
document.getElementById('castButton').disabled = true;
document.getElementById('volumeControl').disabled = true;
}
function initializeCastApi() {
const castContext = cast.framework.CastContext.getInstance();
const appSelect = document.getElementById('appSelect');
const castButton = document.getElementById('castButton');
// Load the saved app selection from localStorage
const savedAppId = localStorage.getItem('selectedAppId');
if (savedAppId) {
appSelect.value = savedAppId;
}
// Check for existing session immediately after API initialization
const existingSession = castContext.getCurrentSession();
if (existingSession) {
console.log('Existing session found, attaching to it');
} else {
// No existing session, set options for a new session
castContext.setOptions({
receiverApplicationId: savedAppId || chrome.cast.media.DEFAULT_MEDIA_RECEIVER_APP_ID,
autoJoinPolicy: chrome.cast.AutoJoinPolicy.ORIGIN_SCOPED,
resumeSavedSession: true
});
}
castButton.addEventListener('click', () => {
const buttonText = castButton.textContent.trim();
if (buttonText === 'Connect to Device') {
// Logic for connecting to a device
const selectedAppId = appSelect.value;
if (selectedAppId) {
localStorage.setItem('selectedAppId', selectedAppId);
castContext.setOptions({
receiverApplicationId: selectedAppId,
autoJoinPolicy: chrome.cast.AutoJoinPolicy.ORIGIN_SCOPED,
resumeSavedSession: true
});
castContext.requestSession().then(
() => console.log('Cast session started with app ID:', selectedAppId),
(error) => console.error('Error starting Cast session:', error)
);
} else {
alert("Please select an app that is playing on your Chromecast-enabled device.");
// Focus the dropdown for the user to select an app
appSelect.focus();
}
} else if (buttonText === 'Disconnect') {
// Logic for disconnecting from a device
castContext.endCurrentSession(false);
console.log('Disconnected from the session');
}
});
castContext.addEventListener(
cast.framework.CastContextEventType.SESSION_STATE_CHANGED,
function (event) {
const stopButton = document.getElementById('stopButton');
switch (event.sessionState) {
case cast.framework.SessionState.SESSION_STARTED:
case cast.framework.SessionState.SESSION_RESUMED:
console.log('Session started or resumed');
castButton.textContent = 'Disconnect';
stopButton.style.display = 'block'; // Ensure the button is displayed when a session starts or resumes
const castSession = cast.framework.CastContext.getInstance().getCurrentSession();
if (castSession) {
updateMediaInformation(castSession.getMediaSession());
const currentVolume = castSession.getVolume() * 100;
document.getElementById('volumeControl').value = currentVolume;
document.getElementById('volumeValue').textContent = currentVolume.toFixed(0);
document.getElementById('volumeControl').disabled = false;
} else {
console.log('No active media session found');
alert("No active media session found. Please start playing media on your Chromecast device and try again.");
castButton.textContent = 'Connect to Device';
document.getElementById('volumeControl').disabled = true;
stopButton.style.display = 'none'; // Hide if no session is active
}
break;
case cast.framework.SessionState.SESSION_ENDED:
case cast.framework.SessionState.NO_SESSION:
console.log('Session ended or no session available');
castButton.textContent = 'Connect to Device';
document.getElementById('volumeControl').disabled = true;
stopButton.style.display = 'none'; // Hide when the session ends
break;
}
}
);
}
function updateMediaInformation(mediaSession) {
const mediaTitle = document.getElementById('mediaTitle');
const mediaImage = document.getElementById('mediaImage');
if (mediaSession && mediaSession.media && mediaSession.media.metadata) {
mediaTitle.textContent = mediaSession.media.metadata.title || 'Unknown Title';
if (mediaSession.media.metadata.images && mediaSession.media.metadata.images.length > 0) {
mediaImage.src = mediaSession.media.metadata.images[0].url;
mediaImage.classList.remove('hidden'); // Remove hidden class to show image
} else {
mediaImage.classList.add('hidden'); // Add hidden class to hide image
}
} else {
mediaTitle.textContent = 'No media information';
mediaImage.classList.add('hidden');
}
}
function adjustVolume(volumeLevel) {
const castSession = cast.framework.CastContext.getInstance().getCurrentSession();
if (castSession) {
// Normalize volume level to a value between 0.0 and 1.0
let normalizedVolume = volumeLevel / 100;
castSession.setVolume(normalizedVolume).then(
() => console.log('Volume set to ' + normalizedVolume),
(error) => console.error('Error setting volume:', error)
);
} else {
console.log('No active Cast session');
}
}
document.getElementById('volumeControl').addEventListener('input', function (event) {
adjustVolume(event.target.value);
document.getElementById('volumeValue').textContent = event.target.value;
});
// Event listener for stop button click
document.getElementById('stopButton').addEventListener('click', stopMedia);
function stopMedia() {
const castSession = cast.framework.CastContext.getInstance().getCurrentSession();
if (castSession) {
castSession.endSession(true);
document.getElementById('stopButton').style.display = 'none';
console.log('Media stopped');
} else {
console.log('No active Cast session');
}
}
</script>
<script>
if ('serviceWorker' in navigator) {
window.addEventListener('load', function () {
navigator.serviceWorker.register('/service-worker.js').then(function (registration) {
console.log('ServiceWorker registration successful with scope: ', registration.scope);
}, function (err) {
console.log('ServiceWorker registration failed: ', err);
});
});
}
</script>
</body>
</html>