Commit 2050703e authored by Andreas Heimann's avatar Andreas Heimann

added downloading and songdetail section

parent 8eadd3a2
......@@ -12,7 +12,9 @@
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;700&display=swap" />
<link rel="stylesheet" href="https://cdn.materialdesignicons.com/5.0.45/css/materialdesignicons.min.css" />
<link rel="stylesheet" href="assets/css/main.css" />
<link rel="stylesheet" href="assets/css/download.css" />
<link rel="stylesheet" href="assets/css/startup.css" />
<link rel="stylesheet" href="assets/css/songdetail.css" />
</head>
<body>
<main>
......@@ -163,44 +165,54 @@
EXPLICIT? YES/NO
</section>
<section class="section-song-detail">
SONG DETAIL
<div class="song-detail-background">
<div class="song-detail-dim">
<div class="song-detail">
<div class="song-cover"></div>
<div class="song-meta-data">
<div class="song-title">Lorem Ipsum</div>
<div class="song-artist">Lorem Ipsum</div>
<div class="song-tags">
<div class="tag">Test</div>
<div class="tag">Demo</div>
<div class="tag">Debug</div>
<div class="tag">API</div>
</div>
<div class="song-charter">
<div class="song-charter-user">
<i class="mdi mdi-account-circle"></i>
<span></span>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="song-detail-actions">
<button class="button-download button button-primary" onclick="SongDetailDownload()">Download</button>
<button class="button-preview button" onclick="SongDetailTogglePreview()">Play Preview</button>
<button class="button-copylink button" onclick="SongDetailCopyLink()">Copy Link</button>
<button class="button-report button" onclick="SongDetailReport()">Report</button>
</div>
</section>
</main>
<!--
<header>
<div class="title">CustomSpeens</div>
</header>
<section class="section-startup active">
<button class="open-backup-button" onclick="UIOpenBackup()">Open Backup</button><br /><br />
<input type="number" class="external-backup-id" placeholder="External Backup ID" />
<button class="external-backup-button" onclick="UIDownloadBackup()">Download</button>
<span class="external-backup-progress"></span>
</section>
<section class="section-trackinfo">
<div class="cover ui-song-cover"></div>
<div class="metadata">
<div class="ui-song-title">Title is here!</div>
<div class="ui-song-subtitle">Subtitle is here!</div>
<div class="ui-song-artist">Song by Artist</div>
<div class="ui-song-author">Chart by Author</div>
</div>
<div class="actions">
<button class="copy-backup-button" onclick="UICopyBackup()">Copy to Game</button>
<div class="download-overlay">
<div class="download-content">
<div class="download-output">Output</div>
<div class="download-actions">
<button class="button" onclick="CloseDownloadOverlay()">Close</button>
</div>
</div>
</section>
<section class="section-result"></section> -->
</div>
<!-- Scripts -->
<script src="./assets/js/init.js"></script>
<script src="./assets/js/system.js"></script>
<script src="./assets/js/navigation.js"></script>
<script src="./assets/js/section.startup.js"></script>
<script src="./assets/js/section.songdetail.js"></script>
<script src="./assets/js/download.js"></script>
<!-- <script src="./assets/js/app.js"></script> -->
</body>
</html>
\ No newline at end of file
.download-overlay {
position: absolute;
z-index: 100;
top: 0px;
left: 0px;
right: 0px;
bottom: 0px;
background: rgba(0, 0, 0, 0.75);
display: none;
justify-content: center;
align-items: center;
}
.download-overlay .download-content {
width: 500px;
background: #212629;
border-radius: 6px;
}
.download-overlay .download-content .download-output {
text-align: center;
padding: 25px;
}
.download-overlay .download-content .download-actions {
display: none;
justify-content: flex-end;
padding: 25px;
background: rgba(0, 0, 0, 0.4);
}
.download-overlay .download-content .download-actions.active {
display: flex;
}
.download-overlay.active {
display: flex;
}
.download-overlay {
position: absolute;
z-index: 100;
top: 0px;
left: 0px;
right: 0px;
bottom: 0px;
background: rgba(0,0,0,0.75);
display: none;
justify-content: center;
align-items: center;
& .download-content {
width: 500px;
background: #212629;
border-radius: 6px;
& .download-output {
text-align: center;
padding: 25px;
}
& .download-actions {
display: none;
justify-content: flex-end;
padding: 25px;
background: rgba(0,0,0,0.4);
&.active {
display: flex;
}
}
}
&.active {
display: flex;
}
}
\ No newline at end of file
.section-song-detail .song-detail-background {
background-size: cover;
background-position: center;
}
.section-song-detail .song-detail-background .song-detail-dim {
backdrop-filter: blur(10px);
background: linear-gradient(180deg, rgba(0, 0, 0, 0.4), #212629);
}
.section-song-detail .song-detail-background .song-detail-dim .song-detail {
padding: 50px;
display: none;
grid-template-columns: 200px 1fr;
grid-gap: 25px;
}
.section-song-detail .song-detail-background .song-detail-dim .song-detail .song-cover {
width: 200px;
height: 200px;
align-self: center;
background: #eee;
border-radius: 6px;
background-size: cover;
}
.section-song-detail .song-detail-background .song-detail-dim .song-detail .song-meta-data .song-title {
font-weight: bold;
font-size: 48px;
}
.section-song-detail .song-detail-background .song-detail-dim .song-detail .song-meta-data .song-artist {
font-size: 20px;
opacity: 0.6;
}
.section-song-detail .song-detail-background .song-detail-dim .song-detail .song-meta-data .song-tags {
display: flex;
margin-top: 15px;
}
.section-song-detail .song-detail-background .song-detail-dim .song-detail .song-meta-data .song-tags .tag {
font-size: 12px;
font-weight: bold;
color: #222;
background: #fff;
padding: 5px 20px;
border-radius: 50px;
margin-right: 10px;
transition: 0.2s ease-in-out all;
}
.section-song-detail .song-detail-background .song-detail-dim .song-detail .song-meta-data .song-tags .tag:hover {
opacity: 0.6;
cursor: pointer;
}
.section-song-detail .song-detail-background .song-detail-dim .song-detail .song-meta-data .song-charter {
margin-top: 15px;
display: flex;
}
.section-song-detail .song-detail-background .song-detail-dim .song-detail .song-meta-data .song-charter .song-charter-user {
justify-self: left;
background: #222;
padding: 10px 15px;
border-radius: 6px;
display: flex;
align-items: center;
transition: 0.2s ease-in-out all;
}
.section-song-detail .song-detail-background .song-detail-dim .song-detail .song-meta-data .song-charter .song-charter-user .mdi {
font-size: 22px;
}
.section-song-detail .song-detail-background .song-detail-dim .song-detail .song-meta-data .song-charter .song-charter-user span {
margin-left: 15px;
}
.section-song-detail .song-detail-background .song-detail-dim .song-detail .song-meta-data .song-charter .song-charter-user:hover {
opacity: 0.6;
cursor: pointer;
}
.section-song-detail .song-detail-background .song-detail-dim .song-detail.active {
display: grid;
}
.section-song-detail .song-detail-actions {
padding: 50px;
padding-top: 0px;
display: none;
grid-template-columns: 1fr 1fr 1fr 1fr;
grid-gap: 25px;
}
.section-song-detail .song-detail-actions .button {
padding: 15px 0px;
font-size: 16px;
transition: 0.2s ease-in-out all, 0.1s ease-in-out transform;
}
.section-song-detail .song-detail-actions .button.button-primary {
background: #fff;
color: #222;
}
.section-song-detail .song-detail-actions .button.button-primary:hover {
background: #fff;
color: #222;
}
.section-song-detail .song-detail-actions .button:hover {
background: rgba(255, 255, 255, 0.2);
color: #fff;
opacity: 0.6;
transform: translateY(-4px);
}
.section-song-detail .song-detail-actions .button:active {
transform: translateY(-2px);
}
.section-song-detail .song-detail-actions.active {
display: grid;
}
.section-song-detail {
& .song-detail-background {
background-size: cover;
background-position: center;
& .song-detail-dim {
backdrop-filter: blur(10px);
background: linear-gradient(180deg, rgba(0,0,0,0.4), #212629);
& .song-detail {
padding: 50px;
display: none;
grid-template-columns: 200px 1fr;
grid-gap: 25px;
& .song-cover {
width: 200px;
height: 200px;
align-self: center;
background: #eee;
border-radius: 6px;
background-size: cover;
}
& .song-meta-data {
& .song-title {
font-weight: bold;
font-size: 48px;
}
& .song-artist {
font-size: 20px;
opacity: 0.6;
}
& .song-tags {
display: flex;
margin-top: 15px;
& .tag {
font-size: 12px;
font-weight: bold;
color: #222;
background: #fff;
padding: 5px 20px;
border-radius: 50px;
margin-right: 10px;
transition: 0.2s ease-in-out all;
&:hover {
opacity: 0.6;
cursor: pointer;
}
}
}
& .song-charter {
margin-top: 15px;
display: flex;
& .song-charter-user {
justify-self: left;
background: #222;
padding: 10px 15px;
border-radius: 6px;
display: flex;
align-items: center;
transition: 0.2s ease-in-out all;
& .mdi {
font-size: 22px;
}
& span {
margin-left: 15px;
}
&:hover {
opacity: 0.6;
cursor: pointer;
}
}
}
}
&.active {
display: grid;
}
}
}
}
& .song-detail-actions {
padding: 50px;
padding-top: 0px;
display: none;
grid-template-columns: 1fr 1fr 1fr 1fr;
grid-gap: 25px;
& .button {
padding: 15px 0px;
font-size: 16px;
transition: 0.2s ease-in-out all, 0.1s ease-in-out transform;
&.button-primary {
background: #fff;
color: #222;
&:hover {
background: #fff;
color: #222;
}
}
&:hover {
background: rgba(255,255,255,0.2);
color: #fff;
opacity: 0.6;
transform: translateY(-4px);
}
&:active {
transform: translateY(-2px);
}
}
&.active {
display: grid;
}
}
}
\ No newline at end of file
......@@ -69,6 +69,22 @@ class SHAPI {
throw new Error(error);
});
}
async getSongDetail(_songId) {
let apiPath = this.apiBase + "song/" + _songId;
let supportedVersion = this.supportedVersion;
return axios.get(apiPath)
.then(function(response) {
if(response.data.version !== supportedVersion) {
throw new Error("Client is outdated!");
}
return response.data.data;
}).catch(function(error) {
throw new Error(error);
});
}
}
module.exports = SHAPI;
\ No newline at end of file
const { ipcRenderer } = require('electron');
const { dialog, shell, app } = require('electron').remote;
const fs = require('fs');
const path = require('path');
const rimraf = require('rimraf');
const ncp = require('ncp');
const http = require('http');
const unzipper = require('unzipper');
// System References
let systemOS = "";
let tempDirLocation = "";
let gameDirLocation = "";
// Backup Data
let currentBackupLocation = "";
let currentSRTBLocation = "";
......@@ -19,35 +5,6 @@ let currentSongLocation = "";
let currentSongTrackInfo = {};
// UI Referrences
let DOMSectionStartup = document.querySelector(".section-startup");
let DOMSectionTrackinfo = document.querySelector(".section-trackinfo");
let DOMSectionResult = document.querySelector(".section-result");
let DOMExternalBackupID = document.querySelector(".external-backup-id");
let DOMExternalBackupProgress = document.querySelector(".external-backup-progress");
let DOMUISongCover = document.querySelector(".ui-song-cover");
let DOMUISongTitle = document.querySelector(".ui-song-title");
let DOMUISongSubtitle = document.querySelector(".ui-song-subtitle");
let DOMUISongArtist = document.querySelector(".ui-song-artist");
let DOMUISongAuthor = document.querySelector(".ui-song-author");
// Init System References
Init();
function Init() {
console.log("Initializing System.");
systemOS = process.platform;
tempDirLocation = app.getPath('temp');
// TODO: Mac/Linux Support
if(process.platform == "win32") {
gameDirLocation = path.join(app.getPath("userData"), "../..", "LocalLow", "Super Spin Digital", "Spin Rhythm XD", "Custom");
}
}
function UIOpenBackup() {
dialog.showOpenDialog({ title: "Open Backup", properties: ['openFile'], filters: [{"name": "Backup Archive", "extensions": ["zip"]}] }).then(result => {
if(!result.canceled) {
......@@ -108,99 +65,4 @@ function UIDownloadBackup() {
}
});
});
}
async function loadBackup(filePath, fileName) {
if(currentBackupLocation != "") {
console.log("Unload previous Backup.");
unloadBackup();
}
console.log("Extracting Backup.");
currentBackupLocation = path.join(tempDirLocation, "extract-" + fileName);
console.info(currentBackupLocation);
// Unzip to temp/CustomSpeens/Song
await fs.createReadStream(filePath).pipe(unzipper.Extract({ path: currentBackupLocation })).promise();
console.log("Loading Backup.");
// Find SRTB & OGG files
let srtbFilesInBackupLocation = getFilesFromPath(currentBackupLocation, ".srtb");
if(srtbFilesInBackupLocation.length < 1) {
console.error("No SRTB file found in backup.");
return false;
}
// Load SRTB file
currentSRTBLocation = path.join(currentBackupLocation, srtbFilesInBackupLocation[0]);
let srtbFile = JSON.parse( fs.readFileSync(currentSRTBLocation) );
currentSongTrackInfo = JSON.parse( srtbFile.largeStringValuesContainer.values[0].val );
// Load OGG file
let oggFilesInBackupLocation = getFilesFromPath(path.join(currentBackupLocation, "AudioClips"), ".ogg");
currentSongLocation = path.join(currentBackupLocation, oggFilesInBackupLocation[0]);
// TODO: Backup Validation
return true;
}
function copyBackup() {
// Copy temp folder to game dir
ncp(currentBackupLocation, gameDirLocation, function(error) {
DOMSectionResult.classList.add("active");
if(error) {
console.error(error);
DOMSectionResult.innerHTML = error;
return false;
}
DOMSectionResult.innerHTML = "Copied Backup Successfully!";
return true;
})
}
function unloadBackup() {
// Remove temp files
rimraf(currentBackupLocation, function() { console.log("Removed backup folder."); });
// Reset vars
currentBackupLocation = "";
currentSRTBLocation = "";
currentSongLocation = "";
currentSongTrackInfo = {};
}
// Used to find files by file extension
// by https://stackoverflow.com/a/52024318
function getFilesFromPath(path, extension) {
let dir = fs.readdirSync( path );
return dir.filter( elm => elm.match(new RegExp(`.*\.(${extension})$`, 'ig')));
}
function getSongCover(extension) {
let dir = fs.readdirSync( path.join(currentBackupLocation, "AlbumArt") );
let fileExtension = dir.filter( elm => elm.match(new RegExp(`(${extension}).*\.$`, 'ig')));
let finalPath = path.join(currentBackupLocation, "AlbumArt", fileExtension[0]);
let base64Data = "data:image/jpg;base64," + fs.readFileSync(finalPath, { encoding: 'base64' });
return base64Data;
}
ipcRenderer.on("download-complete", (event, info) => {
loadBackup(info, path.basename(info)).then(function(result) {
if(result) {
DOMExternalBackupProgress.innerHTML = "";
UIUpdateMetadata();
} else {
console.error("Backup could not be loaded!");
}
});
});
}
\ No newline at end of file
let DOMDownloadOverlay = document.querySelector(".download-overlay");
let DOMDownloadOutput = DOMDownloadOverlay.querySelector(".download-content .download-output");
let DOMDownloadActions = DOMDownloadOverlay.querySelector(".download-content .download-actions");
function DownloadSong(songData) {
DOMDownloadOverlay.classList.add("active");
DOMDownloadActions.classList.remove("active");
DOMDownloadOutput.innerText = "Downloading...";
// Send download command to main.js
ipcRenderer.send("download", {
url: songData.paths.zip,
properties: { directory: tempDirLocation }
});
console.log(songData);
}
function CloseDownloadOverlay() {
DOMDownloadOverlay.classList.remove("active");
DOMDownloadActions.classList.remove("active");
DOMDownloadOutput.innerText = "Downloading...";
}
function UnloadBackup() {
// Remove temp files
rimraf(currentBackupLocation, function() { console.log("Removed backup folder."); });
// Reset vars
currentBackupLocation = "";
currentSRTBLocation = "";
currentSongLocation = "";
currentSongTrackInfo = {};
}
ipcRenderer.on("download-complete", (event, downloadPath) => {
DOMDownloadOutput.innerText = "Extracing...";
extractBackup(downloadPath, path.basename(downloadPath)).then(function(extractResult) {
DOMDownloadOutput.innerText = "Installing...";
if(extractResult) {
installBackup(extractResult);
} else {
console.error("Backup could not be loaded!");
}
});
});
// TODO: Refactor these to dynamic returns
let currentBackupLocation = "";
let currentSRTBLocation = "";
let currentSongLocation = "";
let currentSongTrackInfo = {};
// Extract a local backup folder
async function extractBackup(filePath, fileName) {
if(currentBackupLocation != "") {
console.log("Unload previous Backup.");
UnloadBackup();
}
console.log("Extracting Backup.");
currentBackupLocation = path.join(tempDirLocation, "extract-" + fileName);
console.info(currentBackupLocation);
// Unzip to temp/CustomSpeens/Song
await fs.createReadStream(filePath).pipe(unzipper.Extract({ path: currentBackupLocation })).promise();
console.log("Loading Backup.");
// Find SRTB & OGG files
let srtbFilesInBackupLocation = getFilesFromPath(currentBackupLocation, ".srtb");
if(srtbFilesInBackupLocation.length < 1) {
console.error("No SRTB file found in backup.");
return false;
}
// Load SRTB file
currentSRTBLocation = path.join(currentBackupLocation, srtbFilesInBackupLocation[0]);
let srtbFile = JSON.parse( fs.readFileSync(currentSRTBLocation) );
currentSongTrackInfo = JSON.parse( srtbFile.largeStringValuesContainer.values[0].val );
// Load OGG file
let oggFilesInBackupLocation = getFilesFromPath(path.join(currentBackupLocation, "AudioClips"), ".ogg");
currentSongLocation = path.join(currentBackupLocation, oggFilesInBackupLocation[0]);
// TODO: Backup Validation
return currentBackupLocation;
}
// Install local backup folder
async function installBackup(backupLocation) {
// Copy temp folder to game dir
await ncp(backupLocation, gameDirLocation, function(error) {
if(error) {
console.error(error);
console.error("Couldn't copy backup!");
DOMDownloadActions.classList.add("active");
DOMDownloadOutput.innerText = "Error!";
}
console.log("Successfully copied backup!");
DOMDownloadActions.classList.add("active");
DOMDownloadOutput.innerText = "Done!";
});
}
// Used to find files by file extension
// by https://stackoverflow.com/a/52024318
function getFilesFromPath(path, extension) {
let dir = fs.readdirSync( path );
return dir.filter( elm => elm.match(new RegExp(`.*\.(${extension})$`, 'ig')));
}
function getSongCover(extension) {
let dir = fs.readdirSync( path.join(currentBackupLocation, "AlbumArt") );
let fileExtension = dir.filter( elm => elm.match(new RegExp(`(${extension}).*\.$`, 'ig')));
let finalPath = path.join(currentBackupLocation, "AlbumArt", fileExtension[0]);
let base64Data = "data:image/jpg;base64," + fs.readFileSync(finalPath, { encoding: 'base64' });
return base64Data;
}
\ No newline at end of file
const path = require('path');
const SHAPI = require( path.resolve(__dirname, './assets/js/api.js') );
let api = new SHAPI();
let DOMNavigationItems = document.querySelectorAll("aside .item");
......@@ -10,7 +7,15 @@ let DOMSectionSongDetail = document.querySelector(".section-song-detail");
let currentSection = 0;
let currentSongId = 0;
let currentSongData = {};
let currentPreviewAudio;
function NavigateToSection(sectionIndex) {
// Stop audio if playing
if(currentPreviewAudio)
currentPreviewAudio.pause();
// Navigation
DOMNavigationItems.forEach(function(DOMNavigation) {
DOMNavigation.classList.remove("active");
......@@ -29,6 +34,10 @@ function NavigateToSection(sectionIndex) {
NavigateToSection(0);
function NavigateToSongDetail(songId) {
// Stop audio if playing
if(currentPreviewAudio)
currentPreviewAudio.pause();
// Navigation
DOMNavigationItems.forEach(function(DOMNavigation) {
DOMNavigation.classList.remove("active");
......@@ -42,5 +51,5 @@ function NavigateToSongDetail(songId) {
// Load Detail
DOMSectionSongDetail.classList.add("active");
LoadSongDetail(songId);
SongDetailLoad(songId);
}
\ No newline at end of file
function LoadSongDetail(songId) {
console.log(songId);
const DOMSongDetailBackground = document.querySelector(".song-detail-background");
const DOMSongDetailActions = document.querySelector(".song-detail-actions");
const DOMSongDetail = document.querySelector(".section-song-detail .song-detail");
const DOMSongDetailCover = document.querySelector(".section-song-detail .song-cover");
const DOMButtonPreview = DOMSongDetailActions.querySelector(".button-preview");
const DOMSongTitle = DOMSongDetail.querySelector(".song-title");
const DOMSongArtist = DOMSongDetail.querySelector(".song-artist");
const DOMSongCharter = DOMSongDetail.querySelector(".song-charter span");
const DOMSongTags = DOMSongDetail.querySelector(".song-tags");
function SongDetailLoad(songId) {
currentSongId = 0;
currentSongData = {};
DOMSongDetail.classList.remove("active");
DOMSongDetailActions.classList.remove("active");
api.getSongDetail(songId).then(function(songData) {
DOMSongDetail.classList.add("active");
DOMSongDetailActions.classList.add("active");
DOMSongDetailBackground.style.backgroundImage = "url('" + songData.paths.cover + "')";
DOMSongDetailCover.style.backgroundImage = "url('" + songData.paths.cover + "')";
DOMSongTitle.innerText = songData.title;
DOMSongArtist.innerText = songData.artist;
DOMSongCharter.innerText = songData.charter;
DOMButtonPreview.innerText = "PLAY PREVIEW";
DOMSongTags.innerHTML = "";
songData.tags.forEach(function(tag) {
if(tag != "") {
let newTag = document.createElement("div");
newTag.classList.add("tag");
newTag.innerText = tag;
DOMSongTags.appendChild(newTag);
}
});
currentSongId = songId;
currentSongData = songData;
});
}
function SongDetailTogglePreview() {
if(currentPreviewAudio != undefined || currentPreviewAudio != null) {
currentPreviewAudio.pause();
currentPreviewAudio.currentTime = 0;
currentPreviewAudio = null;
DOMButtonPreview.innerText = "PLAY PREVIEW";
DOMButtonPreview.classList.remove("button-primary");
} else {
currentPreviewAudio = new Audio(currentSongData.paths.ogg);
currentPreviewAudio.play();
DOMButtonPreview.innerText = "PAUSE PREVIEW";
DOMButtonPreview.classList.add("button-primary");
}
}
function SongDetailDownload() {
DownloadSong(currentSongData);
}
function SongDetailCopyLink() {
}
function SongDetailReport() {
alert("Coming soon...");
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment