mirror of
https://github.com/yuukiwww/taiko-web.git
synced 2024-10-22 17:05:49 +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 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 )
|
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.
|
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 = []
|
songs_out = []
|
||||||
for song in songs:
|
for song in songs:
|
||||||
song_id = song[0]
|
song_id = song[0]
|
||||||
song_type = song[10]
|
song_type = song[12]
|
||||||
preview = get_preview(song_id, song_type)
|
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({
|
songs_out.append({
|
||||||
'id': song_id,
|
'id': song_id,
|
||||||
'title': song[1],
|
'title': song[1],
|
||||||
'title_en': song[2],
|
'title_en': song[2],
|
||||||
|
'subtitle': song[3],
|
||||||
|
'subtitle_en': song[4],
|
||||||
'stars': [
|
'stars': [
|
||||||
song[3], song[4], song[5], song[6], song[7]
|
song[5], song[6], song[7], song[8], song[9]
|
||||||
],
|
],
|
||||||
'preview': preview,
|
'preview': preview,
|
||||||
'category': category_out['title'],
|
'category': category_out['title'],
|
||||||
'category_en': category_out['title_en'],
|
'category_en': category_out['title_en'],
|
||||||
'type': song_type,
|
'type': song_type,
|
||||||
'offset': song[11]
|
'offset': song[13]
|
||||||
})
|
})
|
||||||
|
|
||||||
return jsonify(songs_out)
|
return jsonify(songs_out)
|
||||||
|
@ -50,8 +50,9 @@
|
|||||||
comma: /[,.]/,
|
comma: /[,.]/,
|
||||||
ideographicComma: /[、。]/,
|
ideographicComma: /[、。]/,
|
||||||
apostrophe: /['']/,
|
apostrophe: /['']/,
|
||||||
|
degree: /[゚°]/,
|
||||||
brackets: /[\((\))「」『』]/,
|
brackets: /[\((\))「」『』]/,
|
||||||
tilde: /[\--~~]/,
|
tilde: /[\--~~〜]/,
|
||||||
tall: /[bbddffh-lh-ltt0-90-9♪]/,
|
tall: /[bbddffh-lh-ltt0-90-9♪]/,
|
||||||
uppercase: /[A-ZA-Z]/,
|
uppercase: /[A-ZA-Z]/,
|
||||||
lowercase: /[a-za-z・]/,
|
lowercase: /[a-za-z・]/,
|
||||||
@ -283,15 +284,20 @@
|
|||||||
}else if(symbol === "ー"){
|
}else if(symbol === "ー"){
|
||||||
// Long-vowel mark
|
// Long-vowel mark
|
||||||
drawn.push({realText: symbol, svg: this.longVowelMark, x: -4, y: 5, h: 33, scale: [mul, mul]})
|
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)){
|
}else if(r.comma.test(symbol)){
|
||||||
// Comma, full stop
|
// 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)){
|
}else if(r.ideographicComma.test(symbol)){
|
||||||
// Ideographic comma, full stop
|
// Ideographic comma, full stop
|
||||||
drawn.push({text: symbol, x: 16, y: -16, h: 18})
|
drawn.push({text: symbol, x: 16, y: -16, h: 18})
|
||||||
}else if(r.apostrophe.test(symbol)){
|
}else if(r.apostrophe.test(symbol)){
|
||||||
// Apostrophe
|
// Apostrophe
|
||||||
drawn.push({realText: symbol, text: ",", x: 20, y: -39, h: 0, scale: [1.2, 0.7]})
|
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)){
|
}else if(r.brackets.test(symbol)){
|
||||||
// Rotated brackets
|
// Rotated brackets
|
||||||
drawn.push({text: symbol, x: 0, y: -5, h: 25, rotate: true})
|
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
|
var drawnHeight = 0
|
||||||
for(let symbol of drawn){
|
for(let symbol of drawn){
|
||||||
if(config.letterSpacing){
|
if(config.letterSpacing){
|
||||||
@ -382,9 +392,17 @@
|
|||||||
style.transform = ""
|
style.transform = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var scaling = 1
|
||||||
if(config.height && drawnHeight > config.height){
|
if(config.height && drawnHeight > config.height){
|
||||||
var scaling = config.height / drawnHeight
|
if(config.align === "bottom"){
|
||||||
ctx.scale(1, scaling)
|
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){
|
if(config.selectable){
|
||||||
style.transform = "scale(1, " + scaling + ")"
|
style.transform = "scale(1, " + scaling + ")"
|
||||||
style.top = (config.y + (config.height - drawnHeight) / 2 - 15 / 2 * scaling) * scale + "px"
|
style.top = (config.y + (config.height - drawnHeight) / 2 - 15 / 2 * scaling) * scale + "px"
|
||||||
@ -407,12 +425,19 @@
|
|||||||
if(action === "stroke"){
|
if(action === "stroke"){
|
||||||
ctx.strokeStyle = config.outline
|
ctx.strokeStyle = config.outline
|
||||||
ctx.lineWidth = config.outlineSize * mul
|
ctx.lineWidth = config.outlineSize * mul
|
||||||
|
if(config.align === "bottom"){
|
||||||
|
ctx.lineWidth /= scaling
|
||||||
|
}
|
||||||
ctx.lineJoin = "round"
|
ctx.lineJoin = "round"
|
||||||
ctx.miterLimit = 1
|
ctx.miterLimit = 1
|
||||||
}else if(action === "fill"){
|
}else if(action === "fill"){
|
||||||
ctx.fillStyle = config.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){
|
for(let symbol of drawn){
|
||||||
var saved = false
|
var saved = false
|
||||||
@ -421,7 +446,10 @@
|
|||||||
currentX += 20 * mul
|
currentX += 20 * mul
|
||||||
}
|
}
|
||||||
var currentY = offsetY + symbol.y * 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"){
|
if(action === "selectable"){
|
||||||
let div = document.createElement("div")
|
let div = document.createElement("div")
|
||||||
div.classList.add("stroke-sub")
|
div.classList.add("stroke-sub")
|
||||||
|
@ -85,7 +85,8 @@ pageEvents.add(versionDiv, ["click", "touchend"], () => {
|
|||||||
resizeRoot()
|
resizeRoot()
|
||||||
setInterval(resizeRoot, 100)
|
setInterval(resizeRoot, 100)
|
||||||
pageEvents.keyAdd(debugObj, "all", "down", event => {
|
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"){
|
if(debugObj.state === "open"){
|
||||||
debugObj.debug.minimise()
|
debugObj.debug.minimise()
|
||||||
}else if(debugObj.state === "minimised"){
|
}else if(debugObj.state === "minimised"){
|
||||||
@ -95,6 +96,7 @@ pageEvents.keyAdd(debugObj, "all", "down", event => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(event.keyCode === 82 && debugObj.debug && debugObj.controller){
|
if(event.keyCode === 82 && debugObj.debug && debugObj.controller){
|
||||||
|
// R
|
||||||
debugObj.controller.restartSong()
|
debugObj.controller.restartSong()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -91,6 +91,7 @@ class SongSelect{
|
|||||||
this.songs.push({
|
this.songs.push({
|
||||||
id: song.id,
|
id: song.id,
|
||||||
title: song.title,
|
title: song.title,
|
||||||
|
subtitle: song.subtitle,
|
||||||
skin: song.category in this.songSkin ? this.songSkin[song.category] : this.songSkin.default,
|
skin: song.category in this.songSkin ? this.songSkin[song.category] : this.songSkin.default,
|
||||||
stars: song.stars,
|
stars: song.stars,
|
||||||
category: song.category,
|
category: song.category,
|
||||||
@ -218,6 +219,8 @@ class SongSelect{
|
|||||||
this.songSelect.style.backgroundImage = "url('" + assets.image["bg_genre_" + sort].src + "')"
|
this.songSelect.style.backgroundImage = "url('" + assets.image["bg_genre_" + sort].src + "')"
|
||||||
|
|
||||||
this.previewId = 0
|
this.previewId = 0
|
||||||
|
this.previewList = Array(5)
|
||||||
|
|
||||||
var skipStart = fromTutorial || p2.session
|
var skipStart = fromTutorial || p2.session
|
||||||
this.state = {
|
this.state = {
|
||||||
screen: fadeIn ? "titleFadeIn" : (skipStart ? "song" : "title"),
|
screen: fadeIn ? "titleFadeIn" : (skipStart ? "song" : "title"),
|
||||||
@ -708,7 +711,11 @@ class SongSelect{
|
|||||||
this.canvas.style.height = (winH / this.pixelRatio) + "px"
|
this.canvas.style.height = (winH / this.pixelRatio) + "px"
|
||||||
|
|
||||||
var borders = (this.songAsset.border + this.songAsset.innerBorder) * 2
|
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)
|
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)
|
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]){
|
if(!songSel && currentSong.stars[4]){
|
||||||
var fade = ((ms - this.state.screenMS) % 1200) / 1200
|
var fade = ((ms - this.state.screenMS) % 1200) / 1200
|
||||||
var _x = x + 402 + 4 * 100 + fade * 25
|
var _x = x + 402 + 4 * 100 + fade * 25
|
||||||
var _y = y + 258
|
var _y = y + 258
|
||||||
ctx.save()
|
ctx.fillStyle = "rgba(0, 0, 0, " + 0.2 * this.draw.easeInOut(1 - fade) + ")"
|
||||||
ctx.globalAlpha = this.draw.easeInOut(1 - fade)
|
|
||||||
ctx.fillStyle = "#e0be28"
|
|
||||||
ctx.beginPath()
|
ctx.beginPath()
|
||||||
ctx.moveTo(_x - 35, _y - 25)
|
ctx.moveTo(_x - 35, _y - 25)
|
||||||
ctx.lineTo(_x - 10, _y)
|
ctx.lineTo(_x - 10, _y)
|
||||||
ctx.lineTo(_x - 35, _y + 25)
|
ctx.lineTo(_x - 35, _y + 25)
|
||||||
ctx.fill()
|
ctx.fill()
|
||||||
ctx.restore()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.globalAlpha = 1 - Math.max(0, opened - 0.5) * 2
|
ctx.globalAlpha = 1 - Math.max(0, opened - 0.5) * 2
|
||||||
ctx.fillStyle = selectedSkin.background
|
ctx.fillStyle = selectedSkin.background
|
||||||
ctx.fillRect(x, y, w, h)
|
ctx.fillRect(x, y, w, h)
|
||||||
ctx.globalAlpha = 1
|
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({
|
this.songTitleCache.get({
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
x: x + textX,
|
x: x + textX,
|
||||||
@ -1551,15 +1583,17 @@ class SongSelect{
|
|||||||
var currentId = this.previewId
|
var currentId = this.previewId
|
||||||
this.previewing = this.selectedSong
|
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){
|
if(!loadOnly){
|
||||||
this.preview = songObj.preview_sound
|
this.preview = songObj.preview_sound
|
||||||
this.preview.gain = snd.previewGain
|
this.preview.gain = snd.previewGain
|
||||||
this.previewLoaded(startLoad, songObj.preview_time)
|
this.previewLoaded(startLoad, songObj.preview_time)
|
||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
|
songObj = {id: id}
|
||||||
|
|
||||||
var previewFilename = prvTime > 0.1 ? "/preview.mp3" : "/main.mp3"
|
var previewFilename = prvTime > 0.1 ? "/preview.mp3" : "/main.mp3"
|
||||||
|
|
||||||
var loadPreview = previewFilename => {
|
var loadPreview = previewFilename => {
|
||||||
@ -1575,6 +1609,14 @@ class SongSelect{
|
|||||||
songObj.preview_sound = sound
|
songObj.preview_sound = sound
|
||||||
this.preview = sound
|
this.preview = sound
|
||||||
this.previewLoaded(startLoad, songObj.preview_time)
|
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.redrawRunning = false
|
||||||
this.endPreview()
|
this.endPreview()
|
||||||
|
this.previewList.forEach(song => {
|
||||||
|
if(song){
|
||||||
|
song.preview_sound.clean()
|
||||||
|
}
|
||||||
|
})
|
||||||
pageEvents.keyRemove(this, "all")
|
pageEvents.keyRemove(this, "all")
|
||||||
pageEvents.remove(loader.screen, ["mousemove", "mouseleave", "mousedown", "touchstart"])
|
pageEvents.remove(loader.screen, ["mousemove", "mouseleave", "mousedown", "touchstart"])
|
||||||
pageEvents.remove(p2, "message")
|
pageEvents.remove(p2, "message")
|
||||||
|
@ -206,4 +206,7 @@ class Sound{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
clean(){
|
||||||
|
delete this.buffer
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user