mirror of
https://github.com/jiojciojsioe3/a3cjroijsiojiorj.git
synced 2024-11-15 15:31:51 +08:00
Merge pull request #369 from bui/customsongs-filesystem
CustomSongs: Restore custom song list after reload
This commit is contained in:
commit
8fbb45db4f
@ -4,6 +4,21 @@ function readFile(file, arrayBuffer, encoding){
|
||||
reader[arrayBuffer ? "readAsArrayBuffer" : "readAsText"](file, encoding)
|
||||
return promise
|
||||
}
|
||||
function filePermission(file){
|
||||
return file.queryPermission().then(response => {
|
||||
if(response === "granted"){
|
||||
return file
|
||||
}else{
|
||||
return file.requestPermission().then(response => {
|
||||
if(response === "granted"){
|
||||
return file
|
||||
}else{
|
||||
return Promise.reject(file)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
class RemoteFile{
|
||||
constructor(url){
|
||||
this.url = url
|
||||
@ -54,6 +69,23 @@ class LocalFile{
|
||||
return Promise.resolve(this.file)
|
||||
}
|
||||
}
|
||||
class FilesystemFile{
|
||||
constructor(file, path){
|
||||
this.file = file
|
||||
this.path = path
|
||||
this.url = this.path
|
||||
this.name = file.name
|
||||
}
|
||||
arrayBuffer(){
|
||||
return this.blob().then(blob => blob.arrayBuffer())
|
||||
}
|
||||
read(encoding){
|
||||
return this.blob().then(blob => readFile(blob, false, encoding))
|
||||
}
|
||||
blob(){
|
||||
return filePermission(this.file).then(file => file.getFile())
|
||||
}
|
||||
}
|
||||
class GdriveFile{
|
||||
constructor(fileObj){
|
||||
this.path = fileObj.path
|
||||
|
@ -35,7 +35,8 @@ var assets = {
|
||||
"account.js",
|
||||
"lyrics.js",
|
||||
"customsongs.js",
|
||||
"abstractfile.js"
|
||||
"abstractfile.js",
|
||||
"idb.js"
|
||||
],
|
||||
"css": [
|
||||
"main.css",
|
||||
|
@ -1,12 +1,23 @@
|
||||
class CustomSongs{
|
||||
constructor(touchEnabled){
|
||||
constructor(touchEnabled, noPage){
|
||||
this.loaderDiv = document.createElement("div")
|
||||
this.loaderDiv.innerHTML = assets.pages["loadsong"]
|
||||
var loadingText = this.loaderDiv.querySelector("#loading-text")
|
||||
this.setAltText(loadingText, strings.loading)
|
||||
|
||||
this.locked = false
|
||||
this.mode = "main"
|
||||
|
||||
if(noPage){
|
||||
this.noPage = true
|
||||
return
|
||||
}
|
||||
|
||||
this.touchEnabled = touchEnabled
|
||||
loader.changePage("customsongs", true)
|
||||
if(touchEnabled){
|
||||
this.getElement("view-outer").classList.add("touch-enabled")
|
||||
}
|
||||
this.locked = false
|
||||
this.mode = "main"
|
||||
|
||||
var tutorialTitle = this.getElement("view-title")
|
||||
this.setAltText(tutorialTitle, strings.customSongs.title)
|
||||
@ -19,7 +30,7 @@ class CustomSongs{
|
||||
|
||||
this.items = []
|
||||
this.linkLocalFolder = document.getElementById("link-localfolder")
|
||||
this.hasLocal = "webkitdirectory" in HTMLInputElement.prototype && !(/Android|iPhone|iPad/.test(navigator.userAgent))
|
||||
this.hasLocal = (typeof showDirectoryPicker === "function" || "webkitdirectory" in HTMLInputElement.prototype) && !(/Android|iPhone|iPad/.test(navigator.userAgent))
|
||||
this.selected = -1
|
||||
|
||||
if(this.hasLocal){
|
||||
@ -68,12 +79,8 @@ class CustomSongs{
|
||||
this.selected = this.items.length - 1
|
||||
}
|
||||
|
||||
this.loaderDiv = document.createElement("div")
|
||||
this.loaderDiv.innerHTML = assets.pages["loadsong"]
|
||||
var loadingText = this.loaderDiv.querySelector("#loading-text")
|
||||
this.setAltText(loadingText, strings.loading)
|
||||
|
||||
if(DataTransferItem.prototype.webkitGetAsEntry){
|
||||
this.fileSystem = location.protocol === "https:" && DataTransferItem.prototype.getAsFileSystemHandle
|
||||
if(this.fileSystem || DataTransferItem.prototype.webkitGetAsEntry){
|
||||
this.dropzone = document.getElementById("dropzone")
|
||||
var dropContent = this.dropzone.getElementsByClassName("view-content")[0]
|
||||
dropContent.innerText = strings.customSongs.dropzone
|
||||
@ -142,7 +149,19 @@ class CustomSongs{
|
||||
return
|
||||
}
|
||||
this.changeSelected(this.linkLocalFolder)
|
||||
this.browse.click()
|
||||
if(typeof showDirectoryPicker === "function"){
|
||||
return showDirectoryPicker().then(file => {
|
||||
this.walkFilesystem(file).then(files => this.importLocal(files)).then(e => {
|
||||
db.setItem("customFolder", [file])
|
||||
}).catch(e => {
|
||||
if(e !== "cancel"){
|
||||
return Promise.reject(e)
|
||||
}
|
||||
})
|
||||
}, () => {})
|
||||
}else{
|
||||
this.browse.click()
|
||||
}
|
||||
}
|
||||
browseChange(event){
|
||||
var files = []
|
||||
@ -151,6 +170,23 @@ class CustomSongs{
|
||||
}
|
||||
this.importLocal(files)
|
||||
}
|
||||
walkFilesystem(file, path="", output=[]){
|
||||
if(file.kind === "directory"){
|
||||
return filePermission(file).then(file => {
|
||||
var values = file.values()
|
||||
var walkValues = () => values.next().then(generator => {
|
||||
if(generator.done){
|
||||
return output
|
||||
}
|
||||
return this.walkFilesystem(generator.value, path + file.name + "/", output).then(() => walkValues())
|
||||
})
|
||||
return walkValues()
|
||||
}, () => Promise.resolve())
|
||||
}else{
|
||||
output.push(new FilesystemFile(file, path + file.name))
|
||||
return Promise.resolve(output)
|
||||
}
|
||||
}
|
||||
filesDropped(event){
|
||||
event.preventDefault()
|
||||
this.dropzone.classList.remove("dragover")
|
||||
@ -158,46 +194,69 @@ class CustomSongs{
|
||||
if(this.locked){
|
||||
return
|
||||
}
|
||||
var files = []
|
||||
var walk = (entry, path="") => {
|
||||
return new Promise(resolve => {
|
||||
if(entry.isFile){
|
||||
entry.file(file => {
|
||||
files.push(new LocalFile(file, path + file.name))
|
||||
return resolve()
|
||||
}, resolve)
|
||||
}else if(entry.isDirectory){
|
||||
var dirReader = entry.createReader()
|
||||
dirReader.readEntries(entries => {
|
||||
var dirPromises = []
|
||||
for(var i = 0; i < entries.length; i++){
|
||||
dirPromises.push(walk(entries[i], path + entry.name + "/"))
|
||||
}
|
||||
return Promise.all(dirPromises).then(resolve)
|
||||
}, resolve)
|
||||
}else{
|
||||
return resolve()
|
||||
}
|
||||
})
|
||||
}
|
||||
var allFiles = []
|
||||
var dropPromises = []
|
||||
var dbItems = []
|
||||
for(var i = 0; i < event.dataTransfer.items.length; i++){
|
||||
var entry = event.dataTransfer.items[i].webkitGetAsEntry()
|
||||
if(entry){
|
||||
dropPromises.push(walk(entry))
|
||||
var item = event.dataTransfer.items[i]
|
||||
let promise
|
||||
if(this.fileSystem){
|
||||
promise = item.getAsFileSystemHandle().then(file => {
|
||||
dbItems.push(file)
|
||||
return this.walkFilesystem(file)
|
||||
})
|
||||
}else{
|
||||
var entry = item.webkitGetAsEntry()
|
||||
if(entry){
|
||||
promise = this.walkEntry(entry)
|
||||
}
|
||||
}
|
||||
if(promise){
|
||||
dropPromises.push(promise.then(files => {
|
||||
allFiles = allFiles.concat(files)
|
||||
}))
|
||||
}
|
||||
}
|
||||
Promise.all(dropPromises).then(() => this.importLocal(files))
|
||||
Promise.all(dropPromises).then(() => this.importLocal(allFiles)).then(() => {
|
||||
if(dbItems.length){
|
||||
db.setItem("customFolder", dbItems)
|
||||
}
|
||||
})
|
||||
}
|
||||
walkEntry(entry, path="", output=[]){
|
||||
return new Promise(resolve => {
|
||||
if(entry.isFile){
|
||||
entry.file(file => {
|
||||
output.push(new LocalFile(file, path + file.name))
|
||||
return resolve()
|
||||
}, resolve)
|
||||
}else if(entry.isDirectory){
|
||||
var dirReader = entry.createReader()
|
||||
dirReader.readEntries(entries => {
|
||||
var dirPromises = []
|
||||
for(var i = 0; i < entries.length; i++){
|
||||
dirPromises.push(this.walkEntry(entries[i], path + entry.name + "/", output))
|
||||
}
|
||||
return Promise.all(dirPromises).then(resolve)
|
||||
}, resolve)
|
||||
}else{
|
||||
return resolve()
|
||||
}
|
||||
}).then(() => output)
|
||||
}
|
||||
importLocal(files){
|
||||
if(!files.length){
|
||||
return
|
||||
if(this.noPage){
|
||||
return Promise.reject("cancel")
|
||||
}else{
|
||||
return Promise.resolve("cancel")
|
||||
}
|
||||
}
|
||||
this.locked = true
|
||||
this.loading(true)
|
||||
|
||||
var importSongs = new ImportSongs()
|
||||
importSongs.load(files).then(this.songsLoaded.bind(this), e => {
|
||||
return importSongs.load(files).then(this.songsLoaded.bind(this), e => {
|
||||
this.browse.parentNode.reset()
|
||||
this.locked = false
|
||||
this.loading(false)
|
||||
@ -304,6 +363,9 @@ class CustomSongs{
|
||||
open("privacy")
|
||||
}
|
||||
loading(show){
|
||||
if(this.noPage){
|
||||
return
|
||||
}
|
||||
if(show){
|
||||
loader.screen.appendChild(this.loaderDiv)
|
||||
}else if(this.loaderDiv.parentNode){
|
||||
@ -315,9 +377,11 @@ class CustomSongs{
|
||||
var length = songs.length
|
||||
assets.songs = songs
|
||||
assets.customSongs = true
|
||||
assets.customSelected = 0
|
||||
assets.customSelected = this.noPage ? +localStorage.getItem("customSelected") : 0
|
||||
}
|
||||
if(!this.noPage){
|
||||
assets.sounds["se_don"].play()
|
||||
}
|
||||
assets.sounds["se_don"].play()
|
||||
this.clean()
|
||||
setTimeout(() => {
|
||||
new SongSelect("customSongs", false, this.touchEnabled)
|
||||
@ -393,15 +457,18 @@ class CustomSongs{
|
||||
touched = this.touchEnabled
|
||||
}
|
||||
this.clean()
|
||||
assets.sounds[confirm ? "se_don" : "se_cancel"].play()
|
||||
setTimeout(() => {
|
||||
if(!this.noPage){
|
||||
assets.sounds[confirm ? "se_don" : "se_cancel"].play()
|
||||
}
|
||||
return new Promise(resolve => setTimeout(() => {
|
||||
new SongSelect("customSongs", false, touched)
|
||||
}, 500)
|
||||
resolve()
|
||||
}, 500))
|
||||
}
|
||||
showError(text){
|
||||
this.locked = false
|
||||
this.loading(false)
|
||||
if(this.mode === "error"){
|
||||
if(this.noPage || this.mode === "error"){
|
||||
return
|
||||
}
|
||||
this.mode = "error"
|
||||
@ -418,6 +485,10 @@ class CustomSongs{
|
||||
assets.sounds[confirm ? "se_don" : "se_cancel"].play()
|
||||
}
|
||||
clean(){
|
||||
delete this.loaderDiv
|
||||
if(this.noPage){
|
||||
return
|
||||
}
|
||||
this.keyboard.clean()
|
||||
this.gamepad.clean()
|
||||
pageEvents.remove(this.browse, "change")
|
||||
@ -443,7 +514,6 @@ class CustomSongs{
|
||||
delete this.linkPrivacy
|
||||
delete this.endButton
|
||||
delete this.items
|
||||
delete this.loaderDiv
|
||||
delete this.errorDiv
|
||||
delete this.errorContent
|
||||
delete this.errorEnd
|
||||
|
51
public/src/js/idb.js
Normal file
51
public/src/js/idb.js
Normal file
@ -0,0 +1,51 @@
|
||||
class IDB{
|
||||
constructor(name, store){
|
||||
this.name = name
|
||||
this.store = store
|
||||
}
|
||||
init(){
|
||||
if(this.db){
|
||||
return Promise.resolve(this.db)
|
||||
}
|
||||
var request = indexedDB.open(this.name)
|
||||
request.onupgradeneeded = event => {
|
||||
var db = event.target.result
|
||||
db.createObjectStore(this.store)
|
||||
}
|
||||
return this.promise(request).then(result => {
|
||||
this.db = result
|
||||
return this.db
|
||||
}, target =>
|
||||
console.warn("DB error", target)
|
||||
)
|
||||
}
|
||||
promise(request){
|
||||
return new Promise((resolve, reject) => {
|
||||
return pageEvents.race(request, "success", "error").then(response => {
|
||||
if(response.type === "success"){
|
||||
return resolve(event.target.result)
|
||||
}else{
|
||||
return reject(event.target)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
transaction(method, ...args){
|
||||
return this.init().then(db =>
|
||||
db.transaction(this.store, "readwrite").objectStore(this.store)[method](...args)
|
||||
).then(this.promise.bind(this))
|
||||
}
|
||||
getItem(name){
|
||||
return this.transaction("get", name)
|
||||
}
|
||||
setItem(name, value){
|
||||
return this.transaction("put", value, name)
|
||||
}
|
||||
removeItem(name){
|
||||
return this.transaction("delete", name)
|
||||
}
|
||||
removeDB(){
|
||||
delete this.db
|
||||
return indexedDB.deleteDatabase(this.name)
|
||||
}
|
||||
}
|
@ -252,6 +252,7 @@ class Loader{
|
||||
settings = new Settings()
|
||||
pageEvents.setKbd()
|
||||
scoreStorage = new ScoreStorage()
|
||||
db = new IDB("taiko", "store")
|
||||
|
||||
Promise.all(this.promises).then(() => {
|
||||
if(this.error){
|
||||
|
@ -90,6 +90,7 @@ var settings
|
||||
var scoreStorage
|
||||
var account = {}
|
||||
var gpicker
|
||||
var db
|
||||
|
||||
pageEvents.add(root, ["touchstart", "touchmove", "touchend"], event => {
|
||||
if(event.cancelable && cancelTouch && event.target.tagName !== "SELECT"){
|
||||
|
@ -210,7 +210,7 @@ class SongSelect{
|
||||
if(!assets.customSongs && !fromTutorial && !("selectedSong" in localStorage) && !songId){
|
||||
fromTutorial = touchEnabled ? "about" : "tutorial"
|
||||
}
|
||||
if(p2.session){
|
||||
if(p2.session || assets.customSongs && "customSelected" in localStorage){
|
||||
fromTutorial = false
|
||||
}
|
||||
|
||||
@ -231,7 +231,7 @@ class SongSelect{
|
||||
if(songIdIndex !== -1){
|
||||
this.selectedSong = songIdIndex
|
||||
}else if(assets.customSongs){
|
||||
this.selectedSong = assets.customSelected
|
||||
this.selectedSong = Math.min(Math.max(0, assets.customSelected), this.songs.length - 1)
|
||||
}else if((!p2.session || fadeIn) && "selectedSong" in localStorage){
|
||||
this.selectedSong = Math.min(Math.max(0, localStorage["selectedSong"] |0), this.songs.length - 1)
|
||||
}
|
||||
@ -508,7 +508,7 @@ class SongSelect{
|
||||
moveTo = "account"
|
||||
}else if(p2.session && 438 < mouse.x && mouse.x < 834 && mouse.y > 603){
|
||||
moveTo = "session"
|
||||
}else if(!p2.session && mouse.x > 641 && mouse.y > 603 && p2.socket.readyState === 1 && !assets.customSongs){
|
||||
}else if(!p2.session && mouse.x > 641 && mouse.y > 603 && p2.socket && p2.socket.readyState === 1 && !assets.customSongs){
|
||||
moveTo = "session"
|
||||
}else{
|
||||
var moveTo = this.songSelMouse(mouse.x, mouse.y)
|
||||
@ -739,6 +739,7 @@ class SongSelect{
|
||||
try{
|
||||
if(assets.customSongs){
|
||||
assets.customSelected = this.selectedSong
|
||||
localStorage["customSelected"] = this.selectedSong
|
||||
}else{
|
||||
localStorage["selectedSong"] = this.selectedSong
|
||||
}
|
||||
@ -756,7 +757,7 @@ class SongSelect{
|
||||
autoplay = true
|
||||
}else if(shift){
|
||||
autoplay = shift
|
||||
}else if(p2.socket.readyState === 1 && !assets.customSongs){
|
||||
}else if(p2.socket && p2.socket.readyState === 1 && !assets.customSongs){
|
||||
multiplayer = ctrl
|
||||
}
|
||||
var diff = this.difficultyId[difficulty]
|
||||
@ -850,6 +851,8 @@ class SongSelect{
|
||||
setTimeout(() => {
|
||||
new SongSelect("customSongs", false, this.touchEnabled)
|
||||
}, 500)
|
||||
localStorage.removeItem("customSelected")
|
||||
db.removeItem("customFolder")
|
||||
pageEvents.send("import-songs-default")
|
||||
}else{
|
||||
localStorage["selectedSong"] = this.selectedSong
|
||||
@ -1174,6 +1177,7 @@ class SongSelect{
|
||||
this.state.locked = 2
|
||||
if(assets.customSongs){
|
||||
assets.customSelected = this.selectedSong
|
||||
localStorage["customSelected"] = this.selectedSong
|
||||
}else if(!p2.session){
|
||||
try{
|
||||
localStorage["selectedSong"] = this.selectedSong
|
||||
@ -2097,7 +2101,7 @@ class SongSelect{
|
||||
ctx.lineTo(x + 4, y + 4)
|
||||
ctx.lineTo(x + 4, y + h)
|
||||
ctx.fill()
|
||||
if(screen !== "difficulty" && p2.socket.readyState === 1 && !assets.customSongs){
|
||||
if(screen !== "difficulty" && p2.socket && p2.socket.readyState === 1 && !assets.customSongs){
|
||||
var elapsed = (ms - this.state.screenMS) % 3100
|
||||
var fade = 1
|
||||
if(!p2.session && screen === "song"){
|
||||
|
@ -1,6 +1,7 @@
|
||||
class Titlescreen{
|
||||
constructor(songId){
|
||||
this.songId = songId
|
||||
db.getItem("customFolder").then(folder => this.customFolder = folder)
|
||||
|
||||
if(!songId){
|
||||
loader.changePage("titlescreen", false)
|
||||
@ -50,30 +51,58 @@ class Titlescreen{
|
||||
|
||||
onPressed(pressed, name){
|
||||
if(pressed){
|
||||
if(name === "gamepadConfirm" && snd.buffer.context.state === "suspended"){
|
||||
if(name === "gamepadConfirm" && (snd.buffer.context.state === "suspended" || this.customFolder)){
|
||||
return
|
||||
}
|
||||
this.titleScreen.style.cursor = "auto"
|
||||
this.clean()
|
||||
assets.sounds["se_don"].play()
|
||||
if(!this.customFolder || assets.customSongs){
|
||||
assets.sounds["se_don"].play()
|
||||
}
|
||||
this.goNext()
|
||||
}
|
||||
}
|
||||
goNext(fromP2){
|
||||
if(p2.session && !fromP2){
|
||||
p2.send("songsel")
|
||||
}else if(fromP2 || localStorage.getItem("tutorial") === "true"){
|
||||
if(this.touched){
|
||||
localStorage.setItem("tutorial", "true")
|
||||
}
|
||||
pageEvents.remove(p2, "message")
|
||||
setTimeout(() => {
|
||||
new SongSelect(false, false, this.touched, this.songId)
|
||||
}, 500)
|
||||
}else{
|
||||
setTimeout(() => {
|
||||
new SettingsView(this.touched, true, this.songId)
|
||||
}, 500)
|
||||
if(fromP2 || this.customFolder || localStorage.getItem("tutorial") === "true"){
|
||||
if(this.touched){
|
||||
localStorage.setItem("tutorial", "true")
|
||||
}
|
||||
pageEvents.remove(p2, "message")
|
||||
if(this.customFolder && !fromP2 && !assets.customSongs){
|
||||
var customSongs = new CustomSongs(this.touched, true)
|
||||
var soundPlayed = false
|
||||
var promises = []
|
||||
var allFiles = []
|
||||
this.customFolder.forEach(file => {
|
||||
promises.push(customSongs.walkFilesystem(file, undefined, allFiles))
|
||||
})
|
||||
Promise.all(promises).then(() => {
|
||||
assets.sounds["se_don"].play()
|
||||
soundPlayed = true
|
||||
return customSongs.importLocal(allFiles)
|
||||
}).catch(() => {
|
||||
localStorage.removeItem("customSelected")
|
||||
db.removeItem("customFolder")
|
||||
if(!soundPlayed){
|
||||
assets.sounds["se_don"].play()
|
||||
}
|
||||
setTimeout(() => {
|
||||
new SongSelect(false, false, this.touched, this.songId)
|
||||
}, 500)
|
||||
})
|
||||
}else{
|
||||
setTimeout(() => {
|
||||
new SongSelect(false, false, this.touched, this.songId)
|
||||
}, 500)
|
||||
}
|
||||
}else{
|
||||
setTimeout(() => {
|
||||
new SettingsView(this.touched, true, this.songId)
|
||||
}, 500)
|
||||
}
|
||||
}
|
||||
}
|
||||
setLang(lang, noEvent){
|
||||
|
Loading…
Reference in New Issue
Block a user