mirror of
https://github.com/yuukiwww/taiko-web.git
synced 2024-08-26 12:35:48 +02:00
Merge pull request #69 from LoveEevee/songselect-add-sub-titles
SongSelect: Add sub-titles
This commit is contained in:
commit
76108d47d6
@ -10,7 +10,7 @@ Still in developement. Works best with Chrome.
|
||||
|
||||
Create a SQLite databse named `taiko.db` with the following schema:
|
||||
|
||||
CREATE TABLE "songs" ( `id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, `title` TEXT NOT NULL, `title_en` TEXT, `easy` INTEGER, `normal` INTEGER, `hard` INTEGER, `oni` INTEGER, `ura` INTEGER, `enabled` INTEGER NOT NULL, `category` INTEGER, `type` TEXT , `offset` REAL NOT NULL )
|
||||
CREATE TABLE "songs" ( `id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, `title` TEXT NOT NULL, `title_en` TEXT, `subtitle` TEXT, `subtitle_en` TEXT, `easy` INTEGER, `normal` INTEGER, `hard` INTEGER, `oni` INTEGER, `ura` INTEGER, `enabled` INTEGER NOT NULL, `category` INTEGER, `type` TEXT , `offset` REAL NOT NULL )
|
||||
CREATE TABLE "categories" ( `id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, `title` TEXT NOT NULL, `title_en` TEXT NOT NULL )
|
||||
|
||||
When inserting song rows, leave any difficulty columns as NULL if you don't intend to add notecharts for them.
|
||||
|
10
app.py
10
app.py
@ -161,22 +161,24 @@ def route_api_songs():
|
||||
songs_out = []
|
||||
for song in songs:
|
||||
song_id = song[0]
|
||||
song_type = song[10]
|
||||
song_type = song[12]
|
||||
preview = get_preview(song_id, song_type)
|
||||
category_out = categories[song[9]] if song[9] in categories else def_category
|
||||
category_out = categories[song[11]] if song[11] in categories else def_category
|
||||
|
||||
songs_out.append({
|
||||
'id': song_id,
|
||||
'title': song[1],
|
||||
'title_en': song[2],
|
||||
'subtitle': song[3],
|
||||
'subtitle_en': song[4],
|
||||
'stars': [
|
||||
song[3], song[4], song[5], song[6], song[7]
|
||||
song[5], song[6], song[7], song[8], song[9]
|
||||
],
|
||||
'preview': preview,
|
||||
'category': category_out['title'],
|
||||
'category_en': category_out['title_en'],
|
||||
'type': song_type,
|
||||
'offset': song[11]
|
||||
'offset': song[13]
|
||||
})
|
||||
|
||||
return jsonify(songs_out)
|
||||
|
@ -50,8 +50,9 @@
|
||||
comma: /[,.]/,
|
||||
ideographicComma: /[、。]/,
|
||||
apostrophe: /['']/,
|
||||
degree: /[゚°]/,
|
||||
brackets: /[\((\))「」『』]/,
|
||||
tilde: /[\--~~]/,
|
||||
tilde: /[\--~~〜]/,
|
||||
tall: /[bbddffh-lh-ltt0-90-9♪]/,
|
||||
uppercase: /[A-ZA-Z]/,
|
||||
lowercase: /[a-za-z・]/,
|
||||
@ -283,15 +284,20 @@
|
||||
}else if(symbol === "ー"){
|
||||
// Long-vowel mark
|
||||
drawn.push({realText: symbol, svg: this.longVowelMark, x: -4, y: 5, h: 33, scale: [mul, mul]})
|
||||
}else if(symbol === "∀"){
|
||||
drawn.push({text: symbol, x: 0, y: 3, h: 39, rotate: true})
|
||||
}else if(r.comma.test(symbol)){
|
||||
// Comma, full stop
|
||||
drawn.push({text: symbol, x: 16, y: -7, h: 0, scale: [1.2, 0.7]})
|
||||
drawn.push({text: symbol, x: 13, y: -9, h: 13, scale: [1.2, 0.7]})
|
||||
}else if(r.ideographicComma.test(symbol)){
|
||||
// Ideographic comma, full stop
|
||||
drawn.push({text: symbol, x: 16, y: -16, h: 18})
|
||||
}else if(r.apostrophe.test(symbol)){
|
||||
// Apostrophe
|
||||
drawn.push({realText: symbol, text: ",", x: 20, y: -39, h: 0, scale: [1.2, 0.7]})
|
||||
}else if(r.degree.test(symbol)){
|
||||
// Degree
|
||||
drawn.push({text: symbol, x: 16, y: 3, h: 18})
|
||||
}else if(r.brackets.test(symbol)){
|
||||
// Rotated brackets
|
||||
drawn.push({text: symbol, x: 0, y: -5, h: 25, rotate: true})
|
||||
@ -359,6 +365,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
if(config.align === "bottom"){
|
||||
drawn.reverse()
|
||||
}
|
||||
|
||||
var drawnHeight = 0
|
||||
for(let symbol of drawn){
|
||||
if(config.letterSpacing){
|
||||
@ -382,9 +392,17 @@
|
||||
style.transform = ""
|
||||
}
|
||||
|
||||
var scaling = 1
|
||||
if(config.height && drawnHeight > config.height){
|
||||
var scaling = config.height / drawnHeight
|
||||
ctx.scale(1, scaling)
|
||||
if(config.align === "bottom"){
|
||||
scaling = Math.max(0.6, config.height / drawnHeight)
|
||||
ctx.translate(40 * mul, 0)
|
||||
ctx.scale(scaling, config.height / drawnHeight)
|
||||
ctx.translate(-40 * mul, 0)
|
||||
}else{
|
||||
scaling = config.height / drawnHeight
|
||||
ctx.scale(1, scaling)
|
||||
}
|
||||
if(config.selectable){
|
||||
style.transform = "scale(1, " + scaling + ")"
|
||||
style.top = (config.y + (config.height - drawnHeight) / 2 - 15 / 2 * scaling) * scale + "px"
|
||||
@ -407,12 +425,19 @@
|
||||
if(action === "stroke"){
|
||||
ctx.strokeStyle = config.outline
|
||||
ctx.lineWidth = config.outlineSize * mul
|
||||
if(config.align === "bottom"){
|
||||
ctx.lineWidth /= scaling
|
||||
}
|
||||
ctx.lineJoin = "round"
|
||||
ctx.miterLimit = 1
|
||||
}else if(action === "fill"){
|
||||
ctx.fillStyle = config.fill
|
||||
}
|
||||
var offsetY = 0
|
||||
if(config.align === "bottom"){
|
||||
var offsetY = drawnHeight > config.height ? drawnHeight : config.height
|
||||
}else{
|
||||
var offsetY = 0
|
||||
}
|
||||
|
||||
for(let symbol of drawn){
|
||||
var saved = false
|
||||
@ -421,7 +446,10 @@
|
||||
currentX += 20 * mul
|
||||
}
|
||||
var currentY = offsetY + symbol.y * mul
|
||||
offsetY += symbol.h * mul
|
||||
if(config.align === "bottom"){
|
||||
currentY -= symbol.h * mul
|
||||
}
|
||||
offsetY = offsetY + symbol.h * mul * (config.align === "bottom" ? -1 : 1)
|
||||
if(action === "selectable"){
|
||||
let div = document.createElement("div")
|
||||
div.classList.add("stroke-sub")
|
||||
|
@ -85,7 +85,8 @@ pageEvents.add(versionDiv, ["click", "touchend"], () => {
|
||||
resizeRoot()
|
||||
setInterval(resizeRoot, 100)
|
||||
pageEvents.keyAdd(debugObj, "all", "down", event => {
|
||||
if(event.keyCode === 186 && event.ctrlKey && event.shiftKey && !event.altKey){
|
||||
if((event.keyCode === 186 || event.keyCode === 59) && event.ctrlKey && event.shiftKey && !event.altKey){
|
||||
// Semicolon
|
||||
if(debugObj.state === "open"){
|
||||
debugObj.debug.minimise()
|
||||
}else if(debugObj.state === "minimised"){
|
||||
@ -95,6 +96,7 @@ pageEvents.keyAdd(debugObj, "all", "down", event => {
|
||||
}
|
||||
}
|
||||
if(event.keyCode === 82 && debugObj.debug && debugObj.controller){
|
||||
// R
|
||||
debugObj.controller.restartSong()
|
||||
}
|
||||
})
|
||||
|
@ -91,6 +91,7 @@ class SongSelect{
|
||||
this.songs.push({
|
||||
id: song.id,
|
||||
title: song.title,
|
||||
subtitle: song.subtitle,
|
||||
skin: song.category in this.songSkin ? this.songSkin[song.category] : this.songSkin.default,
|
||||
stars: song.stars,
|
||||
category: song.category,
|
||||
@ -218,6 +219,8 @@ class SongSelect{
|
||||
this.songSelect.style.backgroundImage = "url('" + assets.image["bg_genre_" + sort].src + "')"
|
||||
|
||||
this.previewId = 0
|
||||
this.previewList = Array(5)
|
||||
|
||||
var skipStart = fromTutorial || p2.session
|
||||
this.state = {
|
||||
screen: fadeIn ? "titleFadeIn" : (skipStart ? "song" : "title"),
|
||||
@ -708,7 +711,11 @@ class SongSelect{
|
||||
this.canvas.style.height = (winH / this.pixelRatio) + "px"
|
||||
|
||||
var borders = (this.songAsset.border + this.songAsset.innerBorder) * 2
|
||||
this.songTitleCache.resize((this.songAsset.width - borders + 1) * this.songs.length, (this.songAsset.height - borders + 1) * 2, ratio + 0.5)
|
||||
this.songTitleCache.resize(
|
||||
(this.songAsset.width - borders + 1) * this.songs.length,
|
||||
(this.songAsset.height - borders + 1) * 3,
|
||||
ratio + 0.5
|
||||
)
|
||||
|
||||
this.selectTextCache.resize((280 + 53 + 60 + 1) * 2, this.songAsset.marginTop + 15, ratio + 0.5)
|
||||
|
||||
@ -1285,30 +1292,55 @@ class SongSelect{
|
||||
drawDifficulty(ctx, i, currentUra)
|
||||
}
|
||||
}
|
||||
|
||||
var borders = (this.songAsset.border + this.songAsset.innerBorder) * 2
|
||||
var textW = this.songAsset.width - borders
|
||||
var textH = this.songAsset.height - borders
|
||||
var textX = Math.max(w - 37 - textW / 2, w / 2 - textW / 2)
|
||||
var textY = opened * 12 + (1 - opened) * 7
|
||||
|
||||
if(currentSong.subtitle){
|
||||
this.songTitleCache.get({
|
||||
ctx: ctx,
|
||||
x: x + textX - textW,
|
||||
y: y + textY,
|
||||
w: textW,
|
||||
h: textH,
|
||||
id: currentSong.subtitle,
|
||||
}, ctx => {
|
||||
this.draw.verticalText({
|
||||
ctx: ctx,
|
||||
text: currentSong.subtitle,
|
||||
x: textW / 2,
|
||||
y: 7,
|
||||
width: textW,
|
||||
height: textH - 35,
|
||||
fill: "#fff",
|
||||
outline: "#000",
|
||||
outlineSize: 14,
|
||||
fontSize: 28,
|
||||
fontFamily: this.font,
|
||||
align: "bottom"
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
if(!songSel && currentSong.stars[4]){
|
||||
var fade = ((ms - this.state.screenMS) % 1200) / 1200
|
||||
var _x = x + 402 + 4 * 100 + fade * 25
|
||||
var _y = y + 258
|
||||
ctx.save()
|
||||
ctx.globalAlpha = this.draw.easeInOut(1 - fade)
|
||||
ctx.fillStyle = "#e0be28"
|
||||
ctx.fillStyle = "rgba(0, 0, 0, " + 0.2 * this.draw.easeInOut(1 - fade) + ")"
|
||||
ctx.beginPath()
|
||||
ctx.moveTo(_x - 35, _y - 25)
|
||||
ctx.lineTo(_x - 10, _y)
|
||||
ctx.lineTo(_x - 35, _y + 25)
|
||||
ctx.fill()
|
||||
ctx.restore()
|
||||
}
|
||||
|
||||
ctx.globalAlpha = 1 - Math.max(0, opened - 0.5) * 2
|
||||
ctx.fillStyle = selectedSkin.background
|
||||
ctx.fillRect(x, y, w, h)
|
||||
ctx.globalAlpha = 1
|
||||
var borders = (this.songAsset.border + this.songAsset.innerBorder) * 2
|
||||
var textW = this.songAsset.width - borders
|
||||
var textH = this.songAsset.height - borders
|
||||
var textX = Math.max(w - 37 - textW / 2, w / 2 - textW / 2)
|
||||
var textY = opened * 12 + (1 - opened) * 7
|
||||
this.songTitleCache.get({
|
||||
ctx: ctx,
|
||||
x: x + textX,
|
||||
@ -1551,15 +1583,17 @@ class SongSelect{
|
||||
var currentId = this.previewId
|
||||
this.previewing = this.selectedSong
|
||||
}
|
||||
var songObj = assets.songs.find(song => song.id == id)
|
||||
var songObj = this.previewList.find(song => song && song.id === id)
|
||||
|
||||
if(songObj.preview_sound){
|
||||
if(songObj){
|
||||
if(!loadOnly){
|
||||
this.preview = songObj.preview_sound
|
||||
this.preview.gain = snd.previewGain
|
||||
this.previewLoaded(startLoad, songObj.preview_time)
|
||||
}
|
||||
}else{
|
||||
songObj = {id: id}
|
||||
|
||||
var previewFilename = prvTime > 0.1 ? "/preview.mp3" : "/main.mp3"
|
||||
|
||||
var loadPreview = previewFilename => {
|
||||
@ -1575,6 +1609,14 @@ class SongSelect{
|
||||
songObj.preview_sound = sound
|
||||
this.preview = sound
|
||||
this.previewLoaded(startLoad, songObj.preview_time)
|
||||
|
||||
var oldPreview = this.previewList.shift()
|
||||
if(oldPreview){
|
||||
oldPreview.preview_sound.clean()
|
||||
}
|
||||
this.previewList.push(songObj)
|
||||
}else{
|
||||
sound.clean()
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -1714,6 +1756,11 @@ class SongSelect{
|
||||
}
|
||||
this.redrawRunning = false
|
||||
this.endPreview()
|
||||
this.previewList.forEach(song => {
|
||||
if(song){
|
||||
song.preview_sound.clean()
|
||||
}
|
||||
})
|
||||
pageEvents.keyRemove(this, "all")
|
||||
pageEvents.remove(loader.screen, ["mousemove", "mouseleave", "mousedown", "touchstart"])
|
||||
pageEvents.remove(p2, "message")
|
||||
|
@ -206,4 +206,7 @@ class Sound{
|
||||
}
|
||||
}
|
||||
}
|
||||
clean(){
|
||||
delete this.buffer
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user