Forráskód Böngészése

Updated Music Embedded player

Toby Chui 3 éve
szülő
commit
d8b0c426d4

+ 2 - 0
mod/iot/handlerManager.go

@@ -2,6 +2,7 @@ package iot
 
 import (
 	"encoding/json"
+	"fmt"
 	"log"
 	"net/http"
 
@@ -87,6 +88,7 @@ func (m *Manager) HandleIconLoad(w http.ResponseWriter, r *http.Request) {
 	iconName := targetDevice.Handler.Icon(targetDevice)
 
 	iconFilePath := "./web/SystemAO/iot/hub/img/devices/" + iconName + ".png"
+	fmt.Println(iconFilePath)
 	if fileExists(iconFilePath) {
 		http.ServeFile(w, r, iconFilePath)
 	} else {

+ 2 - 0
mod/iot/hdsv2/hdsv2.go

@@ -117,6 +117,8 @@ func (h *Handler) Icon(device *iot.Device) string {
 		return "switch"
 	} else if devModel == "Test Unit" {
 		return "test"
+	} else if devModel == "Display" {
+		return "display"
 	} else {
 		return "unknown"
 	}

+ 168 - 42
web/Music/embedded.html

@@ -5,21 +5,22 @@
 <head>
 	<meta charset="UTF-8">
 	<meta name="theme-color" content="#232324">
-    <link rel="stylesheet" href="../script/tocas/tocas.css">
+    <link rel="stylesheet" href="../script/semantic/semantic.min.css">
 	<link rel="icon" type="image/png" href="./img/small_icon.png">
-	<script src="../script/tocas/tocas.js"></script>
 	<script src="../script/jquery.min.js"></script>
+	<script src="../script/semantic/semantic.min.js"></script>
 	<script src="../script/ao_module.js"></script>
 	<style>
 	body{
 		padding:0px !important;
 		overflow: hidden;
 		height:100%;
+		background-color: transparent !important;
 	}
 
 	.coloredBackground{
-		background: rgb(58,58,58);
-		background: linear-gradient(180deg, rgba(58,58,58,1) 2%, rgba(34,34,34,1) 57%, rgba(0,0,0,1) 100%); 
+		background: rgb(253,255,254);
+		background: linear-gradient(321deg, rgba(253,255,254,1) 29%, rgba(240,250,255,1) 100%); 
 	}
 
 	html, body { height: 100%; width: 100%; margin: 0; }
@@ -33,6 +34,10 @@
 	    margin-left:0px !important;
 		margin-right:0px !important;
 		width:100% !important;
+		position: relative;
+		color: #404147;
+		background-color: #f9f9f9 !important;
+		overflow: hidden;
 	}
 	.parkLeft{
 	    position:absolute !important;
@@ -52,12 +57,25 @@
 	    box-shadow: 2px 2px 2px 2px #bfbfbf;
 	}
 	.playBtn{
-	    color:#4b75ff !important;
+		color: white !important;
+		transition: opacity 0.1s;
+	}
+
+	#playpauseBtn{
+		width: 66px;
+		height: 66px;
+	}
+
+	.playerControlWrapper .sidebuttons{
+		background-color: white !important;
+	}
+	.playerControlWrapper .white.icon.button:hover{
+		opacity: 0.6;
 	}
 	.playerControlWrapper{
 	    text-align: center;
 	    position:absolute;
-	    bottom:50px;
+	    margin-top: 3rem;
 	    width:100% !important;
 	}
 	.rightPaddedOprButtons{
@@ -66,9 +84,9 @@
 	    top:3px;
 	}
 	.modeEnabled{
-	    background-color:#4b75ff !important;
+		background-color: #186ed2 !important;
 	    color:white !important;
-	    border: 1px solid #3d5fd1 !important;
+	    border: 0px solid transparent !important;
 	}
 	#volControlOverlay{
 	    background-color: rgba(255,255,255,0.01);
@@ -85,12 +103,13 @@
 		display:inline-box;
 		left:5px;
 		top:5px;
-		color:white;
+		color:#77767b;
 		height:22px;
 		text-overflow: ellipsis;
 		overflow: hidden; 
 		width: 100%; 
 		white-space: nowrap;
+		font-weight: bold;
 	}
 	.durationDisplay{
 		position:absolute;
@@ -99,22 +118,25 @@
 		padding-right:5px;
 		padding-bottom:5px;
 		margin: 0px !important;
+		color: #77767b;
 	}
 	.bottomBackground{
-		background-color: rgba(48,48,48,0.95);
+		background-color: white;
+		position:relative;
 		height:58px;
 		width:100%;
 	}
 	.sameDirFileList{
 		width:100%;
 		height:calc(100% - 220px) !important;
-		background-color:rgba(52,52,52,0.9);
+		background-color:rgba(243, 243, 243, 0.8);
 		overflow-y:auto;
 		overflow-x:hidden;
 	}
+	
 	@supports ((-webkit-backdrop-filter: blur(2em)) or (backdrop-filter: blur(2em))) {
 		.sameDirFileList {
-			background-color:rgba(52,52,52,0.5);
+			background-color:rgba(240, 240, 240, 0.5);
 			-webkit-backdrop-filter: blur(2em);
 			backdrop-filter: blur(2em);
 		}
@@ -123,50 +145,55 @@
 	.listitem{
 		border-bottom: 1px solid #404040;
 		padding: 12px;
-		color:white;
+		color:rgb(43, 43, 43);
 		font-size:120%;
 		cursor:pointer;
 	}
 	.listitem:hover{
-		background-color:#545454;
+		background-color:#eff1f3;
 	}
 	.listitem.selected{
-		background-color:#5e5e5e;
+		background-color:#eff1f3;
+	}
+	.noradius{
+		border-radius: 0px !important;
 	}
 	</style>
 </head>
 <body>
-	<div id="playerUI" class="playerInterface ts container">
+	<div id="playerUI" class="playerInterface ui container">
+		<img id="Albumnart" class="ui fluid image" src="img/nothumb.png" style="position: absolute; top: -75px; opacity: 0.3; pointer-events: none; user-select: none;">
+		
 	    <div class="parkLeft">
-	        <div class="ts primary small progress rotateNinetyDegree" style="width:120px !important;">
-                <div id="volControl" class="bar" style="width: 60%;background-color:#ff4b4b;"></div>
+	        <div class="ui primary small progress rotateNinetyDegree" style="width:120px !important; background-color: white;">
+                <div id="volControl" class="bar" style="min-width: 0%; width: 60%;background-color:#4576c5;"></div>
             </div>
             
 	    </div>
 	    <div id="volControlOverlay"></div>
 		<div class="playerControlWrapper" align="center">
-		    <button class="ts huge icon button roundbtn playlistStepBtn" onClick="lastSong();"><i class="step backward icon"></i></button>
-		    <button class="ts massive icon button roundbtn playBtn" onClick="togglePlay(this);"><i class="pause icon"></i></button>
-		    <button class="ts huge icon button roundbtn playlistStepBtn" onClick="nextSong();"><i class="step forward icon"></i></button>
+		    <button class="ui white huge icon button sidebuttons roundbtn playlistStepBtn" onClick="lastSong();"><i class="step backward icon"></i></button>
+		    <button id="playpauseBtn" style="background-color:#186ed2;" class="ui white massive icon button roundbtn playBtn" onClick="togglePlay(this);"><i class="pause icon"></i></button>
+		    <button class="ui huge white icon button sidebuttons roundbtn playlistStepBtn" onClick="nextSong();"><i class="step forward icon"></i></button>
 		</div>
 		<div class="rightPaddedOprButtons">
-		    <button id="repeatModeBtn" class="ts icon button" style="margin-bottom:5px;" onClick="toggleRepeat(this);"><i class="retweet icon"></i></button><br>
-		    <button id="showListBtn" class="ts icon button" onClick="showRelatedFileList();"><i class="content icon"></i></button>
-			<!-- <button class="ts icon button"><i class="exchange icon"></i></button> -->
+		    <button id="repeatModeBtn" class="ui icon button" style="margin-bottom:5px;" onClick="toggleRepeat(this);"><i class="retweet icon"></i></button><br>
+		    <button id="showListBtn" class="ui icon button" onClick="showRelatedFileList();"><i class="content icon"></i></button>
+			<!-- <button class="ui icon button"><i class="exchange icon"></i></button> -->
 		</div>
 		<!-- Adding in some labels for the progress bars and song information-->
-		<p class="rotateNinetyDegree" style="position:absolute;top:55px;left:5px;"><i class="minus icon"></i> Volume <i class="plus icon"></i></p>
+		<p class="rotateNinetyDegree" style="position:absolute;top:65px;left:8px; font-weight: lighter;"><i class="small minus icon"></i> Volume <i class="small plus icon"></i></p>
 		<p class="durationDisplay">0:00 / 0:00</p>
 	</div>
 	
-	<div id="progressControl" class="ts attached small progress">
-		<div id="playbackProgress" class="bar" style="width: 0%;background-color: #4b75ff;"></div>
+	<div id="progressControl" class="ui attached small progress" style="height: 0.8rem; background: rgba(255,255,255,0.8);">
+		<div id="playbackProgress" class="bar" style="min-width: 0%; width: 0%;background-color: #4576c5; "></div>
 	</div>
-	<div class="bottomBackground">
+	<div class="bottomBackground" >
 		<!-- Song Title at the bottom black area-->
 		<div id="songtitle" class="songlabel">Loading Song Information...</div>
-		<div id="fileSize" style="position:absolute;bottom:5px;left:5px;color:white;">N/A</div>
-		<div style="position:absolute;right:3px;bottom:3px;color:white;"><i class='leaf icon'></i>AirMusic</div>
+		<div id="fileSize" style="position:absolute;bottom:5px;left:5px;color:#77767b;">N/A</div>
+		<div style="position:absolute;right:3px;bottom:3px;color:#77767b;"><i class='leaf icon'></i>AirMusic</div>
 	</div>
 	<!-- The sections below are for mobile interfaces -->
 	<div id="nearbyFilelist" class="sameDirFileList">
@@ -216,6 +243,8 @@
 					player.src = "../media?file=" + encodeURIComponent(data[i][1]);
 					//Update window title
 					ao_module_setWindowTitle(data[i][0]);
+
+					updatePlayerThemeAndBackground(data[i][1]);
 				}
 			}
 
@@ -419,16 +448,41 @@
 			updatePlayingSongSelection();
 		}
 		
+
+		function uuidv4() {
+			return ([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g, c =>
+				(c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16)
+			);
+		}
+
 		function createPlayList(){
 			$(".sameDirFileList").html("");
-			var template = '<div class="listitem" filename="{rawname}" onClick="playThis(this);"><i class="file audio outline icon"></i> {songname} ({filesize})</div>';
 			for (var i = 0; i < songList.length; i++){
-				var box  = template;
-				box = box.split("{rawname}").join(songList[i][0]);
-				box = box.split("{songname}").join(ao_module_codec.decodeUmFilename(songList[i][0]));
-				box = box.split("{filesize}").join(songList[i][3]);
 				//console.log(box);
-				$(".sameDirFileList").append(box);
+				let thisFileItemID = "thumb_" + uuidv4();
+				$(".sameDirFileList").append(`<div class="listitem" filename="${songList[i][0]}" onClick="playThis(this);">
+					<div class="ui unstackable grid">
+						<div class="three wide column" align="right">
+							<img id="${thisFileItemID}" class="ui image noradius" style="height: 2rem;" src="img/eq.svg"> 
+						</div>
+  						<div class="thirteen wide column">
+							${ao_module_codec.decodeUmFilename(songList[i][0])} (${songList[i][3]})
+						</div>
+					</div>
+					
+					
+					</div>`);
+
+				ao_module_agirun("Music/functions/getThumbnail.js", {
+					file: songList[i][1],
+				}, function(data){
+					if (data.error !== undefined){
+
+					}else{
+						$("#" + thisFileItemID).attr("src","data:image/jpg;base64," + data);
+					}
+				});
+				
 			}
 		
 		}
@@ -498,7 +552,7 @@
 
 		function loadSongFromSongInfo(playSongInfo){
 			var songName = ao_module_codec.decodeUmFilename(playSongInfo[0]);
-			var songPath = filterExternalStoragePath(playSongInfo[1]);
+			var songPath = playSongInfo[1];
 			var fileSize = playSongInfo[3];
 
 			$(player).attr('src',"/media?file=" + encodeURIComponent(songPath));
@@ -506,6 +560,37 @@
 			ao_module_setWindowTitle(songName);
 			songInfo = playSongInfo;
 			updateDisplayInformation(ao_module_codec.decodeUmFilename(songInfo[0]),songInfo[3]);
+			updatePlayerThemeAndBackground(songPath);
+		}
+
+		//Update the player background and theme color
+		function updatePlayerThemeAndBackground(songPath){
+			ao_module_agirun("Music/functions/getThumbnail.js", {
+					file: songPath,
+				}, function(data){
+					if (data.error == undefined){
+						let imageSrc = "data:image/jpg;base64," + data;
+						$("#Albumnart").attr("src",imageSrc);
+						//Get theme color and update the player theme color
+						let themeColor = getAverageRGB($("#Albumnart")[0]);
+						updateThemeColor(themeColor);
+					}else{
+						//No thumbnail. Restore to default theme color
+						updateThemeColor({r:24, g: 110, b: 210});
+						$("#Albumnart").attr("src","img/nothumb.png");
+					}
+				}
+			);
+		}
+
+		//Update the theme color, require something like { r: 231, g: 159, b: 140 }
+		function updateThemeColor(newRGBColor){
+			let colorText = `rgb(${newRGBColor.r} ,${newRGBColor.g} ,${newRGBColor.b})`
+			$("#playbackProgress").css("background-color", colorText);
+			$("#playpauseBtn").css("background-color", colorText);
+			$("#volControl").css("background-color", colorText);
+			$(".modeEnabled")[0].style.setProperty( "background-color", colorText, 'important' );
+			//$(".sameDirFileList").css("background-color", `rgba(${newRGBColor.r}, ${newRGBColor.g}, ${newRGBColor.b}, 0.1)`);
 		}
 		
 		function updateDisplayInformation(songname, filesize){
@@ -513,14 +598,10 @@
 			$("#fileSize").html("<i class='file outline icon'></i> " + filesize);
 		}
 		
-		function filterExternalStoragePath(filepath){
-			return filepath;
-		}
-		
 		function updateDurationDisplay(pos,dur){
 			pos = secondsToHMS(pos);
 			dur = secondsToHMS(dur);
-			$(".durationDisplay").html('<i class="clock icon"></i>' + pos.trim() + " / " + dur.trim());
+			$(".durationDisplay").html(pos.trim() + " / " + dur.trim());
 		}
 		
 		function secondsToHMS(sec){
@@ -611,6 +692,51 @@
 			});
 			return result;
 		}
+
+		function getAverageRGB(imgEl) {
+			var blockSize = 5, 
+				defaultRGB = {r:0,g:0,b:0}, 
+				canvas = document.createElement('canvas'),
+				context = canvas.getContext && canvas.getContext('2d'),
+				data, width, height,
+				i = -4,
+				length,
+				rgb = {r:0,g:0,b:0},
+				count = 0;
+
+			if (!context) {
+				return defaultRGB;
+			}
+
+			height = canvas.height = imgEl.naturalHeight || imgEl.offsetHeight || imgEl.height;
+			width = canvas.width = imgEl.naturalWidth || imgEl.offsetWidth || imgEl.width;
+
+			context.drawImage(imgEl, 0, 0);
+
+			try {
+				data = context.getImageData(0, 0, width, height);
+			} catch(e) {
+				/* security error, img on diff domain */
+				return defaultRGB;
+			}
+
+			length = data.data.length;
+
+			while ( (i += blockSize * 4) < length ) {
+				++count;
+				rgb.r += data.data[i];
+				rgb.g += data.data[i+1];
+				rgb.b += data.data[i+2];
+			}
+
+			// ~~ used to floor values
+			rgb.r = ~~(rgb.r/count);
+			rgb.g = ~~(rgb.g/count);
+			rgb.b = ~~(rgb.b/count);
+
+			return rgb;
+
+			}
 		
 	</script>
 </body>

BIN
web/Music/img/eq.png


BIN
web/Music/img/nothumb.png


BIN
web/Music/img/nothumb.psd


BIN
web/SystemAO/iot/hub/img/devices/cast_FILL0_wght400_GRAD0_opsz48.png


BIN
web/SystemAO/iot/hub/img/devices/display.png


+ 1 - 0
web/SystemAO/iot/hub/img/menu_icon.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" height="48" width="48"><path d="M10.55 37q2.35 0 3.925-1.575Q16.05 33.85 16.05 31.5q0-1.3-.6-2.55-.6-1.25-1.8-2.05l-1.6-1.05V12.5q0-.7-.4-1.1-.4-.4-1.1-.4-.65 0-1.075.4-.425.4-.425 1.1v13.35l-1.6 1.05q-1.2.85-1.8 2.05-.6 1.2-.6 2.55 0 2.3 1.6 3.9t3.9 1.6Zm0 3q-3.6 0-6.05-2.45-2.45-2.45-2.45-6.05 0-2.25 1.1-4.1 1.1-1.85 2.9-3.05V12.5q0-1.9 1.3-3.2Q8.65 8 10.55 8q1.9 0 3.2 1.3 1.3 1.3 1.3 3.2v11.85q1.8 1.2 2.9 3.05 1.1 1.85 1.1 4.1 0 3.55-2.475 6.025Q14.1 40 10.55 40Zm17-10q-2.25-1.6-3.9-4.175Q22 23.25 22 20q0-4.95 3.525-8.475Q29.05 8 34 8q5.05 0 8.525 3.5Q46 15 46 20q0 3.25-1.65 5.825T40.45 30Zm1.05-3h10.8q1.5-1.15 2.55-3.125Q43 21.9 43 20q0-3.75-2.6-6.375T34 11q-3.7 0-6.35 2.65Q25 16.3 25 20q0 1.9 1.05 3.875T28.6 27ZM34 40q-.9 0-1.575-.575-.675-.575-.875-1.425h4.9q-.2.85-.875 1.425Q34.9 40 34 40Zm-5.5-4v-3h11v3Zm-17.95-4.5ZM34 19Z"/></svg>

+ 1 - 1
web/SystemAO/iot/hub/index.html

@@ -83,7 +83,7 @@
 	</div>
 	<div class="pusher">
 	   <div class="ui menu">
-		  <a class="item noborder" href="index.html"><img class="ui ultrasmall circular image" src="img/main_icon.png"> IoT Hub</a>
+		  <a class="item noborder" href="index.html"><img class="ui ultrasmall circular image" src="img/menu_icon.svg"> IoT Hub</a>
 		  <a class="right item" onClick="toggleSideMenu();"><i class="content icon"></i></a>
 	   </div>
 	   <div id="devList" class="ui container">