2018-08-26 18:14:56 +02:00
|
|
|
#!/usr/bin/env python2
|
|
|
|
|
2018-10-17 23:16:53 +02:00
|
|
|
from __future__ import division
|
|
|
|
|
2018-10-10 22:57:29 +02:00
|
|
|
import json
|
2018-08-26 18:14:56 +02:00
|
|
|
import sqlite3
|
2019-02-14 11:31:23 +01:00
|
|
|
import tempfile
|
2018-08-27 14:28:30 +02:00
|
|
|
import re
|
|
|
|
import os
|
2018-10-17 23:16:53 +02:00
|
|
|
from flask import Flask, g, jsonify, render_template, request, abort, redirect
|
2019-02-14 11:31:23 +01:00
|
|
|
from flask_caching import Cache
|
2018-10-17 23:16:53 +02:00
|
|
|
from ffmpy import FFmpeg
|
2018-08-26 18:14:56 +02:00
|
|
|
|
|
|
|
app = Flask(__name__)
|
2019-02-14 11:31:23 +01:00
|
|
|
try:
|
|
|
|
app.cache = Cache(app, config={'CACHE_TYPE': 'redis'})
|
|
|
|
except RuntimeError:
|
|
|
|
app.cache = Cache(app, config={'CACHE_TYPE': 'filesystem', 'CACHE_DIR': tempfile.gettempdir()})
|
|
|
|
|
2018-08-26 18:14:56 +02:00
|
|
|
DATABASE = 'taiko.db'
|
2019-02-06 19:49:25 +01:00
|
|
|
DEFAULT_URL = 'https://github.com/bui/taiko-web/'
|
2018-08-26 18:14:56 +02:00
|
|
|
|
|
|
|
|
|
|
|
def get_db():
|
|
|
|
db = getattr(g, '_database', None)
|
|
|
|
if db is None:
|
|
|
|
db = g._database = sqlite3.connect(DATABASE)
|
|
|
|
return db
|
|
|
|
|
|
|
|
|
|
|
|
def query_db(query, args=(), one=False):
|
|
|
|
cur = get_db().execute(query, args)
|
|
|
|
rv = cur.fetchall()
|
|
|
|
cur.close()
|
|
|
|
return (rv[0] if rv else None) if one else rv
|
|
|
|
|
|
|
|
|
2018-10-28 10:59:49 +01:00
|
|
|
def get_config():
|
|
|
|
if os.path.isfile('config.json'):
|
|
|
|
try:
|
|
|
|
config = json.load(open('config.json', 'r'))
|
|
|
|
except ValueError:
|
|
|
|
print 'WARNING: Invalid config.json, using default values'
|
|
|
|
config = {}
|
|
|
|
else:
|
|
|
|
print 'WARNING: No config.json found, using default values'
|
|
|
|
config = {}
|
|
|
|
|
|
|
|
if not config.get('songs_baseurl'):
|
|
|
|
config['songs_baseurl'] = ''.join([request.host_url, 'songs']) + '/'
|
|
|
|
if not config.get('assets_baseurl'):
|
|
|
|
config['assets_baseurl'] = ''.join([request.host_url, 'assets']) + '/'
|
|
|
|
|
2018-12-05 22:55:28 +01:00
|
|
|
config['_version'] = get_version()
|
2018-10-28 10:59:49 +01:00
|
|
|
return config
|
|
|
|
|
|
|
|
|
2018-08-27 14:28:30 +02:00
|
|
|
def parse_osu(osu):
|
|
|
|
osu_lines = open(osu, 'r').read().replace('\x00', '').split('\n')
|
|
|
|
sections = {}
|
|
|
|
current_section = (None, [])
|
|
|
|
|
|
|
|
for line in osu_lines:
|
|
|
|
line = line.strip()
|
|
|
|
secm = re.match('^\[(\w+)\]$', line)
|
|
|
|
if secm:
|
|
|
|
if current_section:
|
|
|
|
sections[current_section[0]] = current_section[1]
|
|
|
|
current_section = (secm.group(1), [])
|
|
|
|
else:
|
|
|
|
if current_section:
|
|
|
|
current_section[1].append(line)
|
|
|
|
else:
|
|
|
|
current_section = ('Default', [line])
|
|
|
|
|
|
|
|
if current_section:
|
|
|
|
sections[current_section[0]] = current_section[1]
|
|
|
|
|
|
|
|
return sections
|
|
|
|
|
|
|
|
|
|
|
|
def get_osu_key(osu, section, key, default=None):
|
|
|
|
sec = osu[section]
|
|
|
|
for line in sec:
|
|
|
|
ok = line.split(':', 1)[0].strip()
|
|
|
|
ov = line.split(':', 1)[1].strip()
|
|
|
|
|
|
|
|
if ok.lower() == key.lower():
|
|
|
|
return ov
|
|
|
|
|
|
|
|
return default
|
|
|
|
|
|
|
|
|
2018-10-17 23:16:53 +02:00
|
|
|
def get_preview(song_id, song_type):
|
|
|
|
preview = 0
|
|
|
|
|
|
|
|
if song_type == "tja":
|
|
|
|
if os.path.isfile('public/songs/%s/main.tja' % song_id):
|
|
|
|
preview = get_tja_preview('public/songs/%s/main.tja' % song_id)
|
|
|
|
else:
|
|
|
|
osus = [osu for osu in os.listdir('public/songs/%s' % song_id) if osu in ['easy.osu', 'normal.osu', 'hard.osu', 'oni.osu']]
|
|
|
|
if osus:
|
|
|
|
osud = parse_osu('public/songs/%s/%s' % (song_id, osus[0]))
|
|
|
|
preview = int(get_osu_key(osud, 'General', 'PreviewTime', 0))
|
|
|
|
|
|
|
|
return preview
|
|
|
|
|
|
|
|
|
2018-10-11 01:59:17 +02:00
|
|
|
def get_tja_preview(tja):
|
|
|
|
tja_lines = open(tja, 'r').read().replace('\x00', '').split('\n')
|
|
|
|
|
|
|
|
for line in tja_lines:
|
|
|
|
line = line.strip()
|
|
|
|
if ':' in line:
|
|
|
|
name, value = line.split(':', 1)
|
|
|
|
if name.lower() == 'demostart':
|
|
|
|
value = value.strip()
|
|
|
|
try:
|
|
|
|
value = float(value)
|
|
|
|
except ValueError:
|
|
|
|
pass
|
|
|
|
else:
|
|
|
|
return int(value * 1000)
|
|
|
|
elif line.lower() == '#start':
|
|
|
|
break
|
|
|
|
return 0
|
|
|
|
|
|
|
|
|
2018-12-05 22:47:35 +01:00
|
|
|
def get_version():
|
2019-02-06 19:49:25 +01:00
|
|
|
version = {'commit': None, 'commit_short': '', 'version': None, 'url': DEFAULT_URL}
|
2018-12-05 22:47:35 +01:00
|
|
|
if os.path.isfile('version.json'):
|
2019-02-06 19:49:25 +01:00
|
|
|
try:
|
|
|
|
ver = json.load(open('version.json', 'r'))
|
|
|
|
except ValueError:
|
|
|
|
print('Invalid version.json file')
|
|
|
|
return version
|
|
|
|
|
|
|
|
for key in version.keys():
|
|
|
|
if ver.get(key):
|
|
|
|
version[key] = ver.get(key)
|
2018-12-05 22:47:35 +01:00
|
|
|
|
|
|
|
return version
|
|
|
|
|
|
|
|
|
2018-08-26 18:14:56 +02:00
|
|
|
@app.teardown_appcontext
|
|
|
|
def close_connection(exception):
|
|
|
|
db = getattr(g, '_database', None)
|
|
|
|
if db is not None:
|
|
|
|
db.close()
|
|
|
|
|
|
|
|
|
2018-10-10 22:57:29 +02:00
|
|
|
@app.route('/')
|
|
|
|
def route_index():
|
2018-12-05 22:47:35 +01:00
|
|
|
version = get_version()
|
2018-10-28 10:59:49 +01:00
|
|
|
return render_template('index.html', version=version, config=get_config())
|
2018-10-10 22:57:29 +02:00
|
|
|
|
|
|
|
|
2018-10-17 23:16:53 +02:00
|
|
|
@app.route('/api/preview')
|
2019-02-14 11:31:23 +01:00
|
|
|
@app.cache.cached(timeout=15)
|
2018-10-17 23:16:53 +02:00
|
|
|
def route_api_preview():
|
|
|
|
song_id = request.args.get('id', None)
|
|
|
|
if not song_id or not re.match('^[0-9]+$', song_id):
|
|
|
|
abort(400)
|
|
|
|
|
|
|
|
song_row = query_db('select * from songs where id = ? and enabled = 1', (song_id,))
|
|
|
|
if not song_row:
|
|
|
|
abort(400)
|
|
|
|
|
2018-11-18 20:53:37 +01:00
|
|
|
song_type = song_row[0][12]
|
2018-10-17 23:16:53 +02:00
|
|
|
prev_path = make_preview(song_id, song_type)
|
|
|
|
if not prev_path:
|
2019-02-02 08:41:06 +01:00
|
|
|
return redirect(get_config()['songs_baseurl'] + '%s/main.mp3' % song_id)
|
2018-10-17 23:16:53 +02:00
|
|
|
|
2019-02-02 08:41:06 +01:00
|
|
|
return redirect(get_config()['songs_baseurl'] + '%s/preview.mp3' % song_id)
|
2018-10-17 23:16:53 +02:00
|
|
|
|
|
|
|
|
2018-08-26 18:14:56 +02:00
|
|
|
@app.route('/api/songs')
|
2019-02-14 11:31:23 +01:00
|
|
|
@app.cache.cached(timeout=15)
|
2018-08-26 18:14:56 +02:00
|
|
|
def route_api_songs():
|
2018-08-27 14:28:30 +02:00
|
|
|
songs = query_db('select * from songs where enabled = 1')
|
2018-11-25 23:42:24 +01:00
|
|
|
|
2018-09-26 20:30:57 +02:00
|
|
|
raw_categories = query_db('select * from categories')
|
|
|
|
categories = {}
|
|
|
|
def_category = {'title': None, 'title_en': None}
|
|
|
|
for cat in raw_categories:
|
2019-01-25 02:42:05 +01:00
|
|
|
categories[cat[0]] = cat[1]
|
2018-11-25 23:42:24 +01:00
|
|
|
|
|
|
|
raw_song_skins = query_db('select * from song_skins')
|
|
|
|
song_skins = {}
|
|
|
|
for skin in raw_song_skins:
|
2018-12-02 16:25:42 +01:00
|
|
|
song_skins[skin[0]] = {'name': skin[1], 'song': skin[2], 'stage': skin[3], 'don': skin[4]}
|
2018-11-25 23:42:24 +01:00
|
|
|
|
2018-08-27 14:28:30 +02:00
|
|
|
songs_out = []
|
|
|
|
for song in songs:
|
2018-10-13 00:09:42 +02:00
|
|
|
song_id = song[0]
|
2018-11-10 20:12:29 +01:00
|
|
|
song_type = song[12]
|
2018-10-17 23:16:53 +02:00
|
|
|
preview = get_preview(song_id, song_type)
|
2018-11-25 23:42:24 +01:00
|
|
|
|
2018-11-10 20:12:29 +01:00
|
|
|
category_out = categories[song[11]] if song[11] in categories else def_category
|
2018-12-02 16:25:42 +01:00
|
|
|
song_skin_out = song_skins[song[14]] if song[14] in song_skins else None
|
2018-09-26 20:30:57 +02:00
|
|
|
|
|
|
|
songs_out.append({
|
2018-10-13 00:09:42 +02:00
|
|
|
'id': song_id,
|
2018-09-26 20:30:57 +02:00
|
|
|
'title': song[1],
|
2019-01-25 02:42:05 +01:00
|
|
|
'title_lang': song[2],
|
2018-11-10 20:12:29 +01:00
|
|
|
'subtitle': song[3],
|
2019-01-25 02:42:05 +01:00
|
|
|
'subtitle_lang': song[4],
|
2018-09-26 20:30:57 +02:00
|
|
|
'stars': [
|
2018-11-10 20:12:29 +01:00
|
|
|
song[5], song[6], song[7], song[8], song[9]
|
2018-09-26 20:30:57 +02:00
|
|
|
],
|
|
|
|
'preview': preview,
|
2019-01-25 02:42:05 +01:00
|
|
|
'category': category_out,
|
2018-10-13 00:09:42 +02:00
|
|
|
'type': song_type,
|
2018-11-25 23:42:24 +01:00
|
|
|
'offset': song[13],
|
|
|
|
'song_skin': song_skin_out
|
2018-09-26 20:30:57 +02:00
|
|
|
})
|
2018-08-26 18:14:56 +02:00
|
|
|
|
2018-08-27 14:28:30 +02:00
|
|
|
return jsonify(songs_out)
|
2018-08-26 18:14:56 +02:00
|
|
|
|
|
|
|
|
2018-10-27 23:42:28 +02:00
|
|
|
@app.route('/api/config')
|
2019-02-14 11:31:23 +01:00
|
|
|
@app.cache.cached(timeout=15)
|
2018-10-27 23:42:28 +02:00
|
|
|
def route_api_config():
|
2018-10-28 10:59:49 +01:00
|
|
|
config = get_config()
|
2018-10-27 23:42:28 +02:00
|
|
|
return jsonify(config)
|
|
|
|
|
|
|
|
|
2018-10-17 23:16:53 +02:00
|
|
|
def make_preview(song_id, song_type):
|
|
|
|
song_path = 'public/songs/%s/main.mp3' % song_id
|
|
|
|
prev_path = 'public/songs/%s/preview.mp3' % song_id
|
|
|
|
|
|
|
|
if os.path.isfile(song_path) and not os.path.isfile(prev_path):
|
|
|
|
preview = get_preview(song_id, song_type) / 1000
|
|
|
|
if not preview or preview <= 0.1:
|
2019-02-06 19:49:25 +01:00
|
|
|
print('Skipping #%s due to no preview' % song_id)
|
2018-10-17 23:16:53 +02:00
|
|
|
return False
|
|
|
|
|
2019-02-06 19:49:25 +01:00
|
|
|
print('Making preview.mp3 for song #%s' % song_id)
|
2018-10-17 23:16:53 +02:00
|
|
|
ff = FFmpeg(inputs={song_path: '-ss %s' % preview},
|
2018-10-18 01:11:14 +02:00
|
|
|
outputs={prev_path: '-codec:a libmp3lame -ar 32000 -b:a 92k -y -loglevel panic'})
|
2018-10-17 23:16:53 +02:00
|
|
|
ff.run()
|
|
|
|
|
|
|
|
return prev_path
|
|
|
|
|
|
|
|
|
2018-08-26 18:14:56 +02:00
|
|
|
if __name__ == '__main__':
|
2018-08-27 14:28:30 +02:00
|
|
|
app.run(port=34801)
|