From 180ec58adbc093d46ac5e41453e94e3bdeccbba2 Mon Sep 17 00:00:00 2001 From: LoveEevee Date: Sat, 31 Oct 2020 14:47:42 +0300 Subject: [PATCH] Changed look of song loading, fix custom game assets and song skins, fix auth error - Change the way a selected song appears while it is loading the metadata - Fix custom taikowebskin - Fix importing custom game assets (local only) - Get the oauth token again on auth error --- public/src/js/about.js | 2 +- public/src/js/abstractfile.js | 39 ++++++++++++++++++++--------------- public/src/js/controller.js | 8 +++---- public/src/js/customsongs.js | 10 +++++---- public/src/js/gpicker.js | 26 +++++++++++++++++++---- public/src/js/importsongs.js | 30 ++++++++++++++++++++------- public/src/js/loader.js | 19 ++++++++++------- public/src/js/loadsong.js | 4 +++- public/src/js/songselect.js | 20 ++++++++---------- public/src/js/view.js | 4 ++-- 10 files changed, 103 insertions(+), 59 deletions(-) diff --git a/public/src/js/about.js b/public/src/js/about.js index a2712c7..5e3dc97 100644 --- a/public/src/js/about.js +++ b/public/src/js/about.js @@ -138,7 +138,7 @@ if(gamepads[i]){ var gamepadDiag = [] gamepadDiag.push(gamepads[i].id) - gamepadDiag.push("buttons: " + gamepads[i].buttons.length) + gamepadDiag.push("buttons: " + gamepads[i].buttons.length) gamepadDiag.push("axes: " + gamepads[i].axes.length) diag.push("Gamepad #" + (i + 1) + ": " + gamepadDiag.join(", ")) } diff --git a/public/src/js/abstractfile.js b/public/src/js/abstractfile.js index 982ac1c..07908d4 100644 --- a/public/src/js/abstractfile.js +++ b/public/src/js/abstractfile.js @@ -1,3 +1,9 @@ +function readFile(file, arrayBuffer, encoding){ + var reader = new FileReader() + var promise = pageEvents.load(reader).then(event => event.target.result) + reader[arrayBuffer ? "readAsArrayBuffer" : "readAsText"](file, encoding) + return promise +} class RemoteFile{ constructor(url){ this.url = url @@ -22,13 +28,14 @@ class RemoteFile{ } read(encoding){ if(encoding){ - return this.arrayBuffer().then(response => - new TextDecoder(encoding).decode(response) - ) + return this.blob().then(blob => readFile(blob, false, encoding)) }else{ return loader.ajax(this.url) } } + blob(){ + return this.arrayBuffer().then(response => new Blob([response])) + } } class LocalFile{ constructor(file){ @@ -38,16 +45,13 @@ class LocalFile{ this.name = file.name } arrayBuffer(){ - var reader = new FileReader() - var promise = pageEvents.load(reader).then(event => event.target.result) - reader.readAsArrayBuffer(this.file) - return promise + return readFile(this.file, true) } read(encoding){ - var reader = new FileReader() - var promise = pageEvents.load(reader).then(event => event.target.result) - reader.readAsText(this.file, encoding) - return promise + return readFile(this.file, false, encoding) + } + blob(){ + return Promise.resolve(this.file) } } class GdriveFile{ @@ -62,16 +66,14 @@ class GdriveFile{ } read(encoding){ if(encoding){ - return this.arrayBuffer().then(response => { - var reader = new FileReader() - var promise = pageEvents.load(reader).then(event => event.target.result) - reader.readAsText(new Blob([response]), encoding) - return promise - }) + return this.blob().then(blob => readFile(blob, false, encoding)) }else{ return gpicker.downloadFile(this.id) } } + blob(){ + return this.arrayBuffer().then(response => new Blob([response])) + } } class CachedFile{ constructor(contents, oldFile){ @@ -87,4 +89,7 @@ class CachedFile{ read(encoding){ return this.arrayBuffer() } + blob(){ + return this.arrayBuffer().then(response => new Blob([response])) + } } diff --git a/public/src/js/controller.js b/public/src/js/controller.js index 978b316..9294df9 100644 --- a/public/src/js/controller.js +++ b/public/src/js/controller.js @@ -40,12 +40,12 @@ class Controller{ this.parsedSongData = new ParseOsu(songData, selectedSong.difficulty, selectedSong.stars, selectedSong.offset) } this.offset = this.parsedSongData.soundOffset - + var maxCombo = this.parsedSongData.circles.filter(circle => ["don", "ka", "daiDon", "daiKa"].indexOf(circle.type) > -1 && (!circle.branch || circle.branch.name == "master")).length if (maxCombo >= 50) { - var comboVoices = ["v_combo_50"].concat([...Array(Math.floor(maxCombo/100)).keys()].map(i => "v_combo_" + (i + 1)*100)) + var comboVoices = ["v_combo_50"].concat(Array.from(Array(Math.min(50, Math.floor(maxCombo / 100))), (d, i) => "v_combo_" + ((i + 1) * 100))) var promises = [] - + comboVoices.forEach(name => { if (!assets.sounds[name + "_p1"]) { promises.push(loader.loadSound(name + ".wav", snd.sfxGain).then(sound => { @@ -54,7 +54,7 @@ class Controller{ })) } }) - + Promise.all(promises) } diff --git a/public/src/js/customsongs.js b/public/src/js/customsongs.js index b95c7d7..006661c 100644 --- a/public/src/js/customsongs.js +++ b/public/src/js/customsongs.js @@ -136,10 +136,12 @@ class CustomSongs{ } } songsLoaded(songs){ - var length = songs.length - assets.songs = songs - assets.customSongs = true - assets.customSelected = 0 + if(songs){ + var length = songs.length + assets.songs = songs + assets.customSongs = true + assets.customSelected = 0 + } assets.sounds["se_don"].play() this.clean() setTimeout(() => { diff --git a/public/src/js/gpicker.js b/public/src/js/gpicker.js index bcd94af..65eb2de 100644 --- a/public/src/js/gpicker.js +++ b/public/src/js/gpicker.js @@ -84,7 +84,7 @@ class Gpicker{ gapi.client.load("drive", "v3").then(resolve, reject) )) } - getToken(lockedCallback){ + getToken(lockedCallback=()=>{}){ if(this.oauthToken){ return Promise.resolve() } @@ -156,13 +156,31 @@ class Gpicker{ .build() .setVisible(true) } - downloadFile(id, arrayBuffer){ - return this.queue().then(() => - loader.ajax(this.filesUrl + id + "?alt=media", request => { + downloadFile(id, arrayBuffer, retry){ + var url = this.filesUrl + id + "?alt=media" + return this.queue().then(this.getToken.bind(this)).then(() => + loader.ajax(url, request => { if(arrayBuffer){ request.responseType = "arraybuffer" } request.setRequestHeader("Authorization", "Bearer " + this.oauthToken) + }, true).then(event => { + var request = event.target + var reject = () => Promise.reject(`${url} (${request.status})`) + if(request.status === 200){ + return request.response + }else if(request.status === 401 && !retry){ + return new Response(request.response).json().then(response => { + var e = response.error + if(e && e.errors[0].reason === "authError"){ + delete this.oauthToken + return this.downloadFile(id, arrayBuffer, true) + }else{ + return reject() + } + }, reject) + } + return reject() }) ) } diff --git a/public/src/js/importsongs.js b/public/src/js/importsongs.js index f076032..cc8c154 100644 --- a/public/src/js/importsongs.js +++ b/public/src/js/importsongs.js @@ -7,7 +7,7 @@ this.otherFiles = otherFiles || {} this.songs = [] this.stylesheet = [] - this.songTitle = {} + this.songTitle = this.otherFiles.songTitle || {} this.uraRegex = /\s*[\((]裏[\))]$/ this.courseTypes = { "easy": 0, @@ -42,6 +42,7 @@ "bg_stage_2": ".song-stage-2", "bg_stage_3": ".song-stage-3" } + this.comboVoices = ["v_combo_50"].concat(Array.from(Array(50), (d, i) => "v_combo_" + ((i + 1) * 100))) } load(files){ var extensionRegex = /\.[^\/]+$/ @@ -66,13 +67,13 @@ file: file, index: i }) - }else if(!this.limited && (name === "genre.ini" || name === "box.def" || name === "songtitle.txt")){ + }else if(!this.limited && (name === "genre.ini" || name === "box.def") || name === "songtitle.txt"){ var level = (file.path.match(/\//g) || []).length metaFiles.push({ file: file, level: (level * 2) + (name === "genre.ini" ? 1 : 0) }) - }else if(!this.limited && path.indexOf("/taiko-web assets/") !== -1){ + }else if(!this.limited && (path.indexOf("/taiko-web assets/") !== -1 || path.indexOf("taiko-web assets/") === 0)){ if(!(name in this.assetFiles)){ this.assetFiles[name] = file } @@ -401,12 +402,13 @@ for(let name in this.assetFiles){ let id = this.getFilename(name) var file = this.assetFiles[name] + var index = name.lastIndexOf(".") if(name === "vectors.json"){ - promises.push(file.read().then(() => response => { + promises.push(file.read().then(response => { vectors = JSON.parse(response) })) } - if(assets.img.indexOf(name) !== -1){ + if(name.endsWith(".png")){ let image = document.createElement("img") promises.push(pageEvents.load(image).then(() => { if(id in this.assetSelectors){ @@ -415,9 +417,12 @@ } })) image.id = name - image.src = URL.createObjectURL(file) + image.src = URL.createObjectURL(file.blob()) loader.assetsDiv.appendChild(image) - assets.image[id].parentNode.removeChild(assets.image[id]) + var oldImage = assets.image[id] + if(oldImage && oldImage.parentNode){ + oldImage.parentNode.removeChild(oldImage) + } assets.image[id] = image } if(assets.audioSfx.indexOf(name) !== -1){ @@ -440,6 +445,13 @@ assets.sounds[id].clean() promises.push(this.loadSound(file, name, snd.sfxLoudGain)) } + if(this.comboVoices.indexOf(id) !== -1){ + promises.push(snd.sfxGain.load(file).then(sound => { + assets.sounds[id] = sound + assets.sounds[id + "_p1"] = assets.sounds[id].copy(snd.sfxGainL) + assets.sounds[id + "_p2"] = assets.sounds[id].copy(snd.sfxGainR) + })) + } } return Promise.all(promises) } @@ -525,8 +537,11 @@ if(this.songs.length){ if(this.limited){ assets.otherFiles = this.otherFiles + assets.otherFiles.songTitle = this.songTitle } return Promise.resolve(this.songs) + }else if(Object.keys(this.assetFiles).length){ + return Promise.resolve() }else{ return Promise.reject("cancel") } @@ -571,6 +586,7 @@ delete this.songs delete this.tjaFiles delete this.osuFiles + delete this.assetFiles delete this.otherFiles } } diff --git a/public/src/js/loader.js b/public/src/js/loader.js index 8119243..d3a277f 100644 --- a/public/src/js/loader.js +++ b/public/src/js/loader.js @@ -433,16 +433,19 @@ class Loader{ this.screen.innerHTML = assets.pages[name] this.screen.classList[patternBg ? "add" : "remove"]("pattern-bg") } - ajax(url, customRequest){ + ajax(url, customRequest, customResponse){ var request = new XMLHttpRequest() request.open("GET", url) - var promise = pageEvents.load(request).then(() => { - if(request.status === 200){ - return request.response - }else{ - return Promise.reject(`${url} (${request.status})`) - } - }) + var promise = pageEvents.load(request) + if(!customResponse){ + promise = promise.then(() => { + if(request.status === 200){ + return request.response + }else{ + return Promise.reject(`${url} (${request.status})`) + } + }) + } if(customRequest){ customRequest(request) } diff --git a/public/src/js/loadsong.js b/public/src/js/loadsong.js index aad65d7..66b9080 100644 --- a/public/src/js/loadsong.js +++ b/public/src/js/loadsong.js @@ -108,7 +108,9 @@ class LoadSong{ return this.scaleImg(img, filename, prefix, force) }), songObj.custom ? filename + ".png" : skinBase + filename + ".png") if(songObj.custom){ - img.src = URL.createObjectURL(song.songSkin[filename + ".png"]) + this.addPromise(song.songSkin[filename + ".png"].blob().then(blob => { + img.src = URL.createObjectURL(blob) + })) }else{ img.src = skinBase + filename + ".png" } diff --git a/public/src/js/songselect.js b/public/src/js/songselect.js index 0e68284..af8e038 100644 --- a/public/src/js/songselect.js +++ b/public/src/js/songselect.js @@ -1090,7 +1090,7 @@ class SongSelect{ } if(screen === "song"){ - if(this.songs[this.selectedSong].courses){ + if(this.songs[this.selectedSong].courses && !this.songs[this.selectedSong].unloaded){ selectedWidth = this.songAsset.selectedWidth } @@ -1101,11 +1101,11 @@ class SongSelect{ var resize2 = changeSpeed - resize var scroll = resize2 - resize - scrollDelay * 2 var elapsed = ms - this.state.moveMS - + if(this.state.catJump || (this.state.move && ms > this.state.moveMS + resize2 - scrollDelay)){ var isJump = this.state.catJump var previousSelectedSong = this.selectedSong - + if(!isJump){ this.playSound("se_ka", 0, this.lastMoveBy) this.selectedSong = this.mod(this.songs.length, this.selectedSong + this.state.move) @@ -1165,7 +1165,7 @@ class SongSelect{ if(this.songs[this.selectedSong].action !== "back"){ var cat = this.songs[this.selectedSong].originalCategory - this.drawBackground(cat) + this.drawBackground(cat) } } if(this.state.moveMS && ms < this.state.moveMS + changeSpeed){ @@ -1291,7 +1291,7 @@ class SongSelect{ highlight = 1 } var selectedSkin = this.songSkin.selected - if(screen === "title" || screen === "titleFadeIn" || this.state.locked === 3){ + if(screen === "title" || screen === "titleFadeIn" || this.state.locked === 3 || currentSong.unloaded){ selectedSkin = currentSong.skin highlight = 2 }else if(songSelMoving){ @@ -2278,7 +2278,7 @@ class SongSelect{ }else{ this.songSelect.style.backgroundImage = "url('" + assets.image["bg_genre_def"].src + "')" } - } + } drawClosedSong(config){ var ctx = config.ctx @@ -2476,20 +2476,18 @@ class SongSelect{ currentSong.chart = new CachedFile(data, file) return importSongs[currentSong.type === "tja" ? "addTja" : "addOsu"]({ file: currentSong.chart, - index: 0 + index: currentSong.id }) }).then(() => { - var imported = importSongs.songs[0] + var imported = importSongs.songs[currentSong.id] importSongs.clean() - imported.id = currentSong.id - imported.order = currentSong.order - delete imported.song_skin songObj.preview_time = imported.preview var index = assets.songs.findIndex(song => song.id === currentSong.id) if(index !== -1){ assets.songs[index] = imported } this.songs[selectedSong] = this.addSong(imported) + this.state.moveMS = this.getMS() - this.songSelecting.speed * this.songSelecting.resize if(imported.music && currentId === this.previewId){ return snd.previewGain.load(imported.music).then(sound => { imported.sound = sound diff --git a/public/src/js/view.js b/public/src/js/view.js index 5c5711f..686bdc0 100644 --- a/public/src/js/view.js +++ b/public/src/js/view.js @@ -308,8 +308,8 @@ w: _w, h: _h, radius: 11 }) - ctx.fill() - + ctx.fill() + this.draw.layeredText({ ctx: ctx, text: selectedSong.category,