Spaces:
Sleeping
Sleeping
Upload 12 files
Browse files- app.py +26 -3
- oauth_6a3a2795fb802155791e0630.json +1 -0
- requirements.txt +1 -0
- templates/index.html +90 -86
app.py
CHANGED
|
@@ -36,6 +36,7 @@ from ytmusicapi import YTMusic
|
|
| 36 |
from curl_cffi import requests as cffi_requests
|
| 37 |
import requests
|
| 38 |
import socket
|
|
|
|
| 39 |
import urllib3.util.connection as urllib3_cn
|
| 40 |
|
| 41 |
def allowed_gai_family():
|
|
@@ -305,6 +306,26 @@ def api_oauth_poll():
|
|
| 305 |
def api_status():
|
| 306 |
return jsonify(system_status)
|
| 307 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 308 |
@app.route('/api/frame', methods=['POST'])
|
| 309 |
def api_frame():
|
| 310 |
global start_closed_time, last_announced_state, smoothed_ear
|
|
@@ -540,12 +561,14 @@ def api_voice():
|
|
| 540 |
user = get_db().users.find_one({'_id': ObjectId(user_id)})
|
| 541 |
|
| 542 |
has_oauth = False
|
|
|
|
|
|
|
| 543 |
try:
|
| 544 |
if user and user.get('oauth_token'):
|
| 545 |
filepath = f"oauth_{user_id}.json"
|
| 546 |
with open(filepath, 'w') as f:
|
| 547 |
f.write(user['oauth_token'])
|
| 548 |
-
ytmusic = YTMusic(filepath)
|
| 549 |
has_oauth = True
|
| 550 |
# Read back in case of refresh
|
| 551 |
with open(filepath, 'r') as f:
|
|
@@ -553,9 +576,9 @@ def api_voice():
|
|
| 553 |
if refreshed != user['oauth_token']:
|
| 554 |
get_db().users.update_one({'_id': ObjectId(user_id)}, {'$set': {'oauth_token': refreshed}})
|
| 555 |
else:
|
| 556 |
-
ytmusic = YTMusic()
|
| 557 |
except Exception:
|
| 558 |
-
ytmusic = YTMusic()
|
| 559 |
|
| 560 |
is_personal = any(phrase in song.lower() for phrase in ["my liked", "my playlist", "my mix", "my supermix"])
|
| 561 |
|
|
|
|
| 36 |
from curl_cffi import requests as cffi_requests
|
| 37 |
import requests
|
| 38 |
import socket
|
| 39 |
+
from pytubefix import YouTube
|
| 40 |
import urllib3.util.connection as urllib3_cn
|
| 41 |
|
| 42 |
def allowed_gai_family():
|
|
|
|
| 306 |
def api_status():
|
| 307 |
return jsonify(system_status)
|
| 308 |
|
| 309 |
+
@app.route('/api/music_url', methods=['GET'])
|
| 310 |
+
def api_music_url():
|
| 311 |
+
video_id = request.args.get('video_id')
|
| 312 |
+
if not video_id:
|
| 313 |
+
return jsonify({"error": "No video_id provided"}), 400
|
| 314 |
+
try:
|
| 315 |
+
url = f"https://www.youtube.com/watch?v={video_id}"
|
| 316 |
+
yt = YouTube(url, use_oauth=False, allow_oauth_cache=False)
|
| 317 |
+
audio_stream = yt.streams.get_audio_only()
|
| 318 |
+
|
| 319 |
+
return jsonify({
|
| 320 |
+
"url": audio_stream.url,
|
| 321 |
+
"title": yt.title,
|
| 322 |
+
"artist": yt.author,
|
| 323 |
+
"thumbnail": yt.thumbnail_url
|
| 324 |
+
})
|
| 325 |
+
except Exception as e:
|
| 326 |
+
print(f"[pytubefix error] {repr(e)}")
|
| 327 |
+
return jsonify({"error": str(e)}), 500
|
| 328 |
+
|
| 329 |
@app.route('/api/frame', methods=['POST'])
|
| 330 |
def api_frame():
|
| 331 |
global start_closed_time, last_announced_state, smoothed_ear
|
|
|
|
| 561 |
user = get_db().users.find_one({'_id': ObjectId(user_id)})
|
| 562 |
|
| 563 |
has_oauth = False
|
| 564 |
+
# Inject a Chrome-impersonated session to bypass YouTube bot blocking on Hugging Face Spaces
|
| 565 |
+
custom_session = cffi_requests.Session(impersonate="chrome")
|
| 566 |
try:
|
| 567 |
if user and user.get('oauth_token'):
|
| 568 |
filepath = f"oauth_{user_id}.json"
|
| 569 |
with open(filepath, 'w') as f:
|
| 570 |
f.write(user['oauth_token'])
|
| 571 |
+
ytmusic = YTMusic(filepath, requests_session=custom_session)
|
| 572 |
has_oauth = True
|
| 573 |
# Read back in case of refresh
|
| 574 |
with open(filepath, 'r') as f:
|
|
|
|
| 576 |
if refreshed != user['oauth_token']:
|
| 577 |
get_db().users.update_one({'_id': ObjectId(user_id)}, {'$set': {'oauth_token': refreshed}})
|
| 578 |
else:
|
| 579 |
+
ytmusic = YTMusic(requests_session=custom_session)
|
| 580 |
except Exception:
|
| 581 |
+
ytmusic = YTMusic(requests_session=custom_session)
|
| 582 |
|
| 583 |
is_personal = any(phrase in song.lower() for phrase in ["my liked", "my playlist", "my mix", "my supermix"])
|
| 584 |
|
oauth_6a3a2795fb802155791e0630.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
{"access_token": "ya29.a0AT3oNZ-PuKzIRRXTb7bPX0SN0KYTcIu9lC__Wzknc4vgasQRkpIZiXGbgTOZICSwWlSoDm2P5yKY6O3j57SH9dy1ia0Km_I0QpjZo9hXm87VY0RITmBH9kSdjGke-q7Pup-dkA-Kac8mjR6Zfjvrp1RSJOypgDfN9OIezfzUH5lUOfvh8qXPA3Cga_0UBU9AHs4HfJMaCgYKAQkSARMSFQHGX2MiG9DepMZw2wSjU4GYDs3iqw0206", "expires_in": 3599, "refresh_token": "1//05IRxCRE98oyGCgYIARAAGAUSNwF-L9IrrajZ_3yapvGMF8TTsZ0rCuItI83YsyBp3o6ane8mMDxZopFh8FpuWNSKfDN-xtEscRI", "scope": "https://www.googleapis.com/auth/youtube", "token_type": "Bearer", "refresh_token_expires_in": 604799, "client_id": "491487047585-lho8et7iouct8o0r4f7hd6djlgnrroff.apps.googleusercontent.com", "client_secret": "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"}
|
requirements.txt
CHANGED
|
@@ -11,3 +11,4 @@ httpx<0.28.0
|
|
| 11 |
ytmusicapi
|
| 12 |
pymongo
|
| 13 |
requests
|
|
|
|
|
|
| 11 |
ytmusicapi
|
| 12 |
pymongo
|
| 13 |
requests
|
| 14 |
+
pytubefix
|
templates/index.html
CHANGED
|
@@ -793,7 +793,6 @@
|
|
| 793 |
}
|
| 794 |
}
|
| 795 |
</style>
|
| 796 |
-
<script src="https://www.youtube.com/iframe_api"></script>
|
| 797 |
</head>
|
| 798 |
|
| 799 |
<body>
|
|
@@ -885,8 +884,8 @@
|
|
| 885 |
<canvas id="canvasElement" style="display:none;"></canvas>
|
| 886 |
</div>
|
| 887 |
|
| 888 |
-
<!--
|
| 889 |
-
<
|
| 890 |
</main>
|
| 891 |
|
| 892 |
<!-- Right Sidebar for Copilot -->
|
|
@@ -1062,94 +1061,103 @@
|
|
| 1062 |
let recognition = null;
|
| 1063 |
|
| 1064 |
|
| 1065 |
-
// ---
|
| 1066 |
-
|
| 1067 |
let isMmpPlaying = false;
|
| 1068 |
-
let currentQueue = [];
|
| 1069 |
-
let currentQueueIndex = 0;
|
| 1070 |
|
| 1071 |
-
|
| 1072 |
-
|
| 1073 |
-
|
| 1074 |
-
|
| 1075 |
-
videoId: '',
|
| 1076 |
-
playerVars: {
|
| 1077 |
-
'autoplay': 1,
|
| 1078 |
-
'controls': 0,
|
| 1079 |
-
'disablekb': 1,
|
| 1080 |
-
'fs': 0,
|
| 1081 |
-
'modestbranding': 1,
|
| 1082 |
-
'rel': 0
|
| 1083 |
-
},
|
| 1084 |
-
events: {
|
| 1085 |
-
'onReady': onPlayerReady,
|
| 1086 |
-
'onStateChange': onPlayerStateChange,
|
| 1087 |
-
'onError': onPlayerError
|
| 1088 |
-
}
|
| 1089 |
-
});
|
| 1090 |
-
}
|
| 1091 |
|
| 1092 |
-
|
| 1093 |
-
|
| 1094 |
-
|
|
|
|
| 1095 |
|
| 1096 |
-
|
| 1097 |
-
|
| 1098 |
-
|
| 1099 |
-
|
| 1100 |
-
|
| 1101 |
-
|
| 1102 |
-
|
| 1103 |
-
|
| 1104 |
-
|
| 1105 |
-
|
| 1106 |
-
|
| 1107 |
-
|
| 1108 |
-
|
| 1109 |
-
|
| 1110 |
-
|
| 1111 |
-
|
| 1112 |
-
|
| 1113 |
-
|
| 1114 |
-
} else if (event.data == YT.PlayerState.ENDED) {
|
| 1115 |
-
document.getElementById('mmp-play-pause').innerHTML = '<i class="fa-solid fa-play"></i>';
|
| 1116 |
-
isMmpPlaying = false;
|
| 1117 |
-
if (currentQueue.length > currentQueueIndex + 1) {
|
| 1118 |
-
currentQueueIndex++;
|
| 1119 |
-
playNativeAudio(currentQueue[currentQueueIndex], "Up Next...");
|
| 1120 |
-
} else {
|
| 1121 |
-
document.getElementById('custom-media-player').style.display = 'none';
|
| 1122 |
-
}
|
| 1123 |
}
|
| 1124 |
-
}
|
| 1125 |
|
| 1126 |
-
|
| 1127 |
-
console.error("
|
| 1128 |
document.getElementById('mmp-title').innerText = "Playback Failed";
|
| 1129 |
document.getElementById('mmp-play-pause').innerHTML = '<i class="fa-solid fa-play"></i>';
|
| 1130 |
appendMessage('assistant', "Failed to load audio stream. Please try another song.");
|
| 1131 |
isMmpPlaying = false;
|
| 1132 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1133 |
|
| 1134 |
function toggleMmpPlay() {
|
| 1135 |
-
if (!ytPlayer) return;
|
| 1136 |
if (isMmpPlaying) {
|
| 1137 |
-
|
| 1138 |
} else {
|
| 1139 |
-
|
|
|
|
|
|
|
| 1140 |
}
|
| 1141 |
}
|
| 1142 |
|
| 1143 |
-
function playNativeAudio(videoId, titleGuess) {
|
| 1144 |
-
|
|
|
|
| 1145 |
|
| 1146 |
document.getElementById('custom-media-player').style.display = 'flex';
|
| 1147 |
document.getElementById('mmp-thumbnail').src = `https://img.youtube.com/vi/${videoId}/hqdefault.jpg`;
|
| 1148 |
-
document.getElementById('mmp-title').innerText = "
|
| 1149 |
document.getElementById('mmp-artist').innerText = titleGuess || "YouTube Music";
|
| 1150 |
document.getElementById('mmp-play-pause').innerHTML = '<i class="fa-solid fa-spinner fa-spin"></i>';
|
| 1151 |
|
| 1152 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1153 |
}
|
| 1154 |
|
| 1155 |
// Pre-load voices on page load
|
|
@@ -1167,8 +1175,8 @@
|
|
| 1167 |
if (!text) return;
|
| 1168 |
|
| 1169 |
// lower music volume
|
| 1170 |
-
if (isMmpPlaying
|
| 1171 |
-
|
| 1172 |
}
|
| 1173 |
|
| 1174 |
// Filter out markdown
|
|
@@ -1188,13 +1196,13 @@
|
|
| 1188 |
utterance.onend = () => {
|
| 1189 |
isSpeaking = false;
|
| 1190 |
// restore music volume
|
| 1191 |
-
if (isMmpPlaying
|
| 1192 |
-
|
| 1193 |
}
|
| 1194 |
};
|
| 1195 |
utterance.onerror = () => {
|
| 1196 |
isSpeaking = false;
|
| 1197 |
-
if (isMmpPlaying
|
| 1198 |
};
|
| 1199 |
|
| 1200 |
window.speechSynthesis.speak(utterance);
|
|
@@ -1234,11 +1242,11 @@
|
|
| 1234 |
currentQueueIndex = 0;
|
| 1235 |
playNativeAudio(currentQueue[currentQueueIndex], data.title);
|
| 1236 |
} else if (data.action === "pause_native") {
|
| 1237 |
-
|
| 1238 |
} else if (data.action === "resume_native") {
|
| 1239 |
-
|
| 1240 |
} else if (data.action === "stop_native") {
|
| 1241 |
-
|
| 1242 |
document.getElementById('custom-media-player').style.display = 'none';
|
| 1243 |
} else if (data.action === "next_native") {
|
| 1244 |
if (currentQueue.length > currentQueueIndex + 1) {
|
|
@@ -1255,19 +1263,15 @@
|
|
| 1255 |
appendMessage('assistant', "No previous song in queue.");
|
| 1256 |
}
|
| 1257 |
} else if (data.action === "vol_up_native") {
|
| 1258 |
-
|
| 1259 |
-
|
| 1260 |
-
ytPlayer.setVolume(Math.min(100, vol + 20));
|
| 1261 |
-
}
|
| 1262 |
} else if (data.action === "vol_down_native") {
|
| 1263 |
-
|
| 1264 |
-
|
| 1265 |
-
ytPlayer.setVolume(Math.max(0, vol - 20));
|
| 1266 |
-
}
|
| 1267 |
} else if (data.action === "mute_native") {
|
| 1268 |
-
|
| 1269 |
} else if (data.action === "unmute_native") {
|
| 1270 |
-
|
| 1271 |
} else if (data.action === "open_url" && data.url) {
|
| 1272 |
let newWin = window.open(data.url, "_blank");
|
| 1273 |
if (!newWin || newWin.closed || typeof newWin.closed == 'undefined') {
|
|
|
|
| 793 |
}
|
| 794 |
}
|
| 795 |
</style>
|
|
|
|
| 796 |
</head>
|
| 797 |
|
| 798 |
<body>
|
|
|
|
| 884 |
<canvas id="canvasElement" style="display:none;"></canvas>
|
| 885 |
</div>
|
| 886 |
|
| 887 |
+
<!-- NATIVE AUDIO PLAYER (Using Piped API) -->
|
| 888 |
+
<audio id="nativeAudio" style="display:none;"></audio>
|
| 889 |
</main>
|
| 890 |
|
| 891 |
<!-- Right Sidebar for Copilot -->
|
|
|
|
| 1061 |
let recognition = null;
|
| 1062 |
|
| 1063 |
|
| 1064 |
+
// --- NATIVE AUDIO PLAYER (Using Piped API) ---
|
| 1065 |
+
const nativeAudio = document.getElementById('nativeAudio') || new Audio();
|
| 1066 |
let isMmpPlaying = false;
|
|
|
|
|
|
|
| 1067 |
|
| 1068 |
+
nativeAudio.addEventListener('play', () => {
|
| 1069 |
+
document.getElementById('mmp-play-pause').innerHTML = '<i class="fa-solid fa-pause"></i>';
|
| 1070 |
+
isMmpPlaying = true;
|
| 1071 |
+
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1072 |
|
| 1073 |
+
nativeAudio.addEventListener('playing', () => {
|
| 1074 |
+
document.getElementById('mmp-play-pause').innerHTML = '<i class="fa-solid fa-pause"></i>';
|
| 1075 |
+
isMmpPlaying = true;
|
| 1076 |
+
});
|
| 1077 |
|
| 1078 |
+
nativeAudio.addEventListener('pause', () => {
|
| 1079 |
+
document.getElementById('mmp-play-pause').innerHTML = '<i class="fa-solid fa-play"></i>';
|
| 1080 |
+
isMmpPlaying = false;
|
| 1081 |
+
});
|
| 1082 |
+
|
| 1083 |
+
nativeAudio.addEventListener('waiting', () => {
|
| 1084 |
+
document.getElementById('mmp-play-pause').innerHTML = '<i class="fa-solid fa-spinner fa-spin"></i>';
|
| 1085 |
+
isMmpPlaying = false;
|
| 1086 |
+
});
|
| 1087 |
+
|
| 1088 |
+
nativeAudio.addEventListener('ended', () => {
|
| 1089 |
+
document.getElementById('mmp-play-pause').innerHTML = '<i class="fa-solid fa-play"></i>';
|
| 1090 |
+
isMmpPlaying = false;
|
| 1091 |
+
if (currentQueue.length > currentQueueIndex + 1) {
|
| 1092 |
+
currentQueueIndex++;
|
| 1093 |
+
playNativeAudio(currentQueue[currentQueueIndex], "Up Next...");
|
| 1094 |
+
} else {
|
| 1095 |
+
document.getElementById('custom-media-player').style.display = 'none';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1096 |
}
|
| 1097 |
+
});
|
| 1098 |
|
| 1099 |
+
nativeAudio.addEventListener('error', (e) => {
|
| 1100 |
+
console.error("Native Audio Player Error:", e);
|
| 1101 |
document.getElementById('mmp-title').innerText = "Playback Failed";
|
| 1102 |
document.getElementById('mmp-play-pause').innerHTML = '<i class="fa-solid fa-play"></i>';
|
| 1103 |
appendMessage('assistant', "Failed to load audio stream. Please try another song.");
|
| 1104 |
isMmpPlaying = false;
|
| 1105 |
+
});
|
| 1106 |
+
|
| 1107 |
+
let currentQueue = [];
|
| 1108 |
+
let currentQueueIndex = 0;
|
| 1109 |
+
let currentExtractionId = 0;
|
| 1110 |
|
| 1111 |
function toggleMmpPlay() {
|
|
|
|
| 1112 |
if (isMmpPlaying) {
|
| 1113 |
+
nativeAudio.pause();
|
| 1114 |
} else {
|
| 1115 |
+
if (nativeAudio.src) {
|
| 1116 |
+
nativeAudio.play().catch(e => console.error("Play error:", e));
|
| 1117 |
+
}
|
| 1118 |
}
|
| 1119 |
}
|
| 1120 |
|
| 1121 |
+
async function playNativeAudio(videoId, titleGuess) {
|
| 1122 |
+
const extractionId = ++currentExtractionId;
|
| 1123 |
+
nativeAudio.pause();
|
| 1124 |
|
| 1125 |
document.getElementById('custom-media-player').style.display = 'flex';
|
| 1126 |
document.getElementById('mmp-thumbnail').src = `https://img.youtube.com/vi/${videoId}/hqdefault.jpg`;
|
| 1127 |
+
document.getElementById('mmp-title').innerText = "Extracting Audio Stream...";
|
| 1128 |
document.getElementById('mmp-artist').innerText = titleGuess || "YouTube Music";
|
| 1129 |
document.getElementById('mmp-play-pause').innerHTML = '<i class="fa-solid fa-spinner fa-spin"></i>';
|
| 1130 |
|
| 1131 |
+
try {
|
| 1132 |
+
// Fetch direct audio stream URL from backend
|
| 1133 |
+
const response = await fetch(`/api/music_url?video_id=${videoId}`);
|
| 1134 |
+
const data = await response.json();
|
| 1135 |
+
|
| 1136 |
+
if (extractionId !== currentExtractionId) return; // Superceded by another play
|
| 1137 |
+
|
| 1138 |
+
if (data.url) {
|
| 1139 |
+
document.getElementById('mmp-title').innerText = data.title || titleGuess;
|
| 1140 |
+
document.getElementById('mmp-artist').innerText = data.artist || "YouTube Music";
|
| 1141 |
+
|
| 1142 |
+
nativeAudio.src = data.url;
|
| 1143 |
+
nativeAudio.volume = 1.0;
|
| 1144 |
+
nativeAudio.play().catch(e => {
|
| 1145 |
+
console.error("Autoplay prevented:", e);
|
| 1146 |
+
document.getElementById('mmp-title').innerText = data.title || titleGuess;
|
| 1147 |
+
document.getElementById('mmp-play-pause').innerHTML = '<i class="fa-solid fa-play"></i>';
|
| 1148 |
+
appendMessage('assistant', "Please click the play button manually to start the song.");
|
| 1149 |
+
});
|
| 1150 |
+
} else {
|
| 1151 |
+
throw new Error(data.error || "No URL returned");
|
| 1152 |
+
}
|
| 1153 |
+
} catch (err) {
|
| 1154 |
+
console.error("Extraction error:", err);
|
| 1155 |
+
if (extractionId === currentExtractionId) {
|
| 1156 |
+
document.getElementById('mmp-title').innerText = "Extraction Failed";
|
| 1157 |
+
document.getElementById('mmp-play-pause').innerHTML = '<i class="fa-solid fa-play"></i>';
|
| 1158 |
+
appendMessage('assistant', "Failed to extract audio stream for this song. Please try another one.");
|
| 1159 |
+
}
|
| 1160 |
+
}
|
| 1161 |
}
|
| 1162 |
|
| 1163 |
// Pre-load voices on page load
|
|
|
|
| 1175 |
if (!text) return;
|
| 1176 |
|
| 1177 |
// lower music volume
|
| 1178 |
+
if (isMmpPlaying) {
|
| 1179 |
+
nativeAudio.volume = 0.2;
|
| 1180 |
}
|
| 1181 |
|
| 1182 |
// Filter out markdown
|
|
|
|
| 1196 |
utterance.onend = () => {
|
| 1197 |
isSpeaking = false;
|
| 1198 |
// restore music volume
|
| 1199 |
+
if (isMmpPlaying) {
|
| 1200 |
+
nativeAudio.volume = 1.0;
|
| 1201 |
}
|
| 1202 |
};
|
| 1203 |
utterance.onerror = () => {
|
| 1204 |
isSpeaking = false;
|
| 1205 |
+
if (isMmpPlaying) nativeAudio.volume = 1.0;
|
| 1206 |
};
|
| 1207 |
|
| 1208 |
window.speechSynthesis.speak(utterance);
|
|
|
|
| 1242 |
currentQueueIndex = 0;
|
| 1243 |
playNativeAudio(currentQueue[currentQueueIndex], data.title);
|
| 1244 |
} else if (data.action === "pause_native") {
|
| 1245 |
+
nativeAudio.pause();
|
| 1246 |
} else if (data.action === "resume_native") {
|
| 1247 |
+
nativeAudio.play();
|
| 1248 |
} else if (data.action === "stop_native") {
|
| 1249 |
+
nativeAudio.pause();
|
| 1250 |
document.getElementById('custom-media-player').style.display = 'none';
|
| 1251 |
} else if (data.action === "next_native") {
|
| 1252 |
if (currentQueue.length > currentQueueIndex + 1) {
|
|
|
|
| 1263 |
appendMessage('assistant', "No previous song in queue.");
|
| 1264 |
}
|
| 1265 |
} else if (data.action === "vol_up_native") {
|
| 1266 |
+
let vol = nativeAudio.volume;
|
| 1267 |
+
nativeAudio.volume = Math.min(1.0, vol + 0.2);
|
|
|
|
|
|
|
| 1268 |
} else if (data.action === "vol_down_native") {
|
| 1269 |
+
let vol = nativeAudio.volume;
|
| 1270 |
+
nativeAudio.volume = Math.max(0.0, vol - 0.2);
|
|
|
|
|
|
|
| 1271 |
} else if (data.action === "mute_native") {
|
| 1272 |
+
nativeAudio.muted = true;
|
| 1273 |
} else if (data.action === "unmute_native") {
|
| 1274 |
+
nativeAudio.muted = false;
|
| 1275 |
} else if (data.action === "open_url" && data.url) {
|
| 1276 |
let newWin = window.open(data.url, "_blank");
|
| 1277 |
if (!newWin || newWin.closed || typeof newWin.closed == 'undefined') {
|