add game visualizer
parent
584aa856fe
commit
afa988ce3c
|
@ -40,3 +40,6 @@
|
||||||
[submodule "public/vendor/bar-rating"]
|
[submodule "public/vendor/bar-rating"]
|
||||||
path = public/vendor/bar-rating
|
path = public/vendor/bar-rating
|
||||||
url = https://github.com/antennaio/jquery-bar-rating
|
url = https://github.com/antennaio/jquery-bar-rating
|
||||||
|
[submodule "public/visualizer"]
|
||||||
|
path = public/visualizer
|
||||||
|
url = https://github.com/ornicar/chess_game_visualizer
|
||||||
|
|
|
@ -18,24 +18,33 @@ object Export extends LilaController {
|
||||||
def pgn(id: String) = Open { implicit ctx =>
|
def pgn(id: String) = Open { implicit ctx =>
|
||||||
OnlyHumans {
|
OnlyHumans {
|
||||||
lila.mon.export.pgn.game()
|
lila.mon.export.pgn.game()
|
||||||
OptionFuResult(GameRepo game id) {
|
OptionFuResult(GameRepo game id) { game =>
|
||||||
case game if game.playable => NotFound("Can't export PGN of game in progress").fuccess
|
gameToPgn(
|
||||||
case game => (game.pgnImport.ifTrue(get("as") contains "imported") match {
|
game,
|
||||||
case Some(i) => fuccess(i.pgn)
|
asImported = get("as") contains "imported",
|
||||||
case None => for {
|
asRaw = get("as").contains("raw")) map { content =>
|
||||||
initialFen <- GameRepo initialFen game
|
Ok(content).withHeaders(
|
||||||
pgn = Env.api.pgnDump(game, initialFen)
|
CONTENT_TYPE -> ContentTypes.TEXT,
|
||||||
analysis ← !get("as").contains("raw") ?? (Env.analyse.analyser get game.id)
|
CONTENT_DISPOSITION -> ("attachment; filename=" + (Env.api.pgnDump filename game)))
|
||||||
} yield Env.analyse.annotator(pgn, analysis, game.opening, game.winnerColor, game.status, game.clock).toString
|
} recover {
|
||||||
}) map { content =>
|
case err => NotFound(err.getMessage)
|
||||||
Ok(content).withHeaders(
|
}
|
||||||
CONTENT_TYPE -> ContentTypes.TEXT,
|
|
||||||
CONTENT_DISPOSITION -> ("attachment; filename=" + (Env.api.pgnDump filename game)))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private def gameToPgn(from: GameModel, asImported: Boolean, asRaw: Boolean): Fu[String] = from match {
|
||||||
|
case game if game.playable => fufail("Can't export PGN of game in progress")
|
||||||
|
case game => (game.pgnImport.ifTrue(asImported) match {
|
||||||
|
case Some(i) => fuccess(i.pgn)
|
||||||
|
case None => for {
|
||||||
|
initialFen <- GameRepo initialFen game
|
||||||
|
pgn = Env.api.pgnDump(game, initialFen)
|
||||||
|
analysis ← !asRaw ?? (Env.analyse.analyser get game.id)
|
||||||
|
} yield Env.analyse.annotator(pgn, analysis, game.opening, game.winnerColor, game.status, game.clock).toString
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
private val PdfRateLimitGlobal = new lila.memo.RateLimit(
|
private val PdfRateLimitGlobal = new lila.memo.RateLimit(
|
||||||
credits = 20,
|
credits = 20,
|
||||||
duration = 1 minute,
|
duration = 1 minute,
|
||||||
|
@ -74,6 +83,22 @@ object Export extends LilaController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def visualizer(id: String) = Open { implicit ctx =>
|
||||||
|
OptionFuResult(GameRepo game id) { game =>
|
||||||
|
gameToPgn(game, asImported = true, asRaw = true) map { pgn =>
|
||||||
|
lila.mon.export.visualizer()
|
||||||
|
Redirect {
|
||||||
|
import lila.api.Env.current.Net._
|
||||||
|
val base = s"$Protocol$AssetDomain/assets"
|
||||||
|
val encoded = java.net.URLEncoder.encode(pgn.toString, "UTF-8")
|
||||||
|
s"$base/visualizer/index_lichess.html?pgn=$encoded"
|
||||||
|
}
|
||||||
|
} recoverWith {
|
||||||
|
case _: Exception => notFound
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
def puzzlePng(id: Int) = Open { implicit ctx =>
|
def puzzlePng(id: Int) = Open { implicit ctx =>
|
||||||
OnlyHumansAndFacebook {
|
OnlyHumansAndFacebook {
|
||||||
PngRateLimitGlobal("-", msg = HTTPRequest lastRemoteAddress ctx.req) {
|
PngRateLimitGlobal("-", msg = HTTPRequest lastRemoteAddress ctx.req) {
|
||||||
|
|
|
@ -64,17 +64,19 @@ atom = atom.some) {
|
||||||
<div class="panel fen_pgn">
|
<div class="panel fen_pgn">
|
||||||
<p><strong>FEN</strong><input type="input" readonly="true" spellcheck="false" class="copyable autoselect fen" /></p>
|
<p><strong>FEN</strong><input type="input" readonly="true" spellcheck="false" class="copyable autoselect fen" /></p>
|
||||||
<p><strong>PGN</strong>
|
<p><strong>PGN</strong>
|
||||||
<a data-icon="x" rel="nofollow" href="@routes.Export.pgn(game.id)"> @trans.downloadAnnotated()</a>
|
<a data-icon="x" class="text" rel="nofollow" href="@routes.Export.pgn(game.id)">@trans.downloadAnnotated()</a>
|
||||||
@if(analysis.isDefined) {
|
@if(analysis.isDefined) {
|
||||||
/
|
/
|
||||||
<a data-icon="x" rel="nofollow" href="@routes.Export.pgn(game.id)?as=raw"> @trans.downloadRaw()</a>
|
<a data-icon="x" class="text" rel="nofollow" href="@routes.Export.pgn(game.id)?as=raw">@trans.downloadRaw()</a>
|
||||||
}
|
}
|
||||||
@if(game.isPgnImport) {
|
@if(game.isPgnImport) {
|
||||||
/
|
/
|
||||||
<a data-icon="x" rel="nofollow" href="@routes.Export.pgn(game.id)?as=imported"> @trans.downloadImported()</a>
|
<a data-icon="x" class="text" rel="nofollow" href="@routes.Export.pgn(game.id)?as=imported">@trans.downloadImported()</a>
|
||||||
}
|
}
|
||||||
/
|
/
|
||||||
<a data-icon="x" target="_blank" rel="nofollow" href="@cdnUrl(routes.Export.pdf(game.id).url)"> @trans.printFriendlyPDF()</a>
|
<a data-icon="x" class="text" target="_blank" rel="nofollow" href="@cdnUrl(routes.Export.pdf(game.id).url)">@trans.printFriendlyPDF()</a>
|
||||||
|
/
|
||||||
|
<a data-icon="x" class="text" target="_blank" href="@routes.Export.visualizer(game.id)">Generate images</a>
|
||||||
</p>
|
</p>
|
||||||
<div class="pgn">@Html(nl2br(escapeHtml(pgn)))</div>
|
<div class="pgn">@Html(nl2br(escapeHtml(pgn)))</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -256,6 +256,7 @@ POST /$gameId<\w{8}>/request-analysis controllers.Analyse.requestAnalysi
|
||||||
GET /game/export/$gameId<\w{8}>.pgn controllers.Export.pgn(gameId: String)
|
GET /game/export/$gameId<\w{8}>.pgn controllers.Export.pgn(gameId: String)
|
||||||
GET /game/export/pdf/$gameId<\w{8}>.pdf controllers.Export.pdf(gameId: String)
|
GET /game/export/pdf/$gameId<\w{8}>.pdf controllers.Export.pdf(gameId: String)
|
||||||
GET /game/export/png/$gameId<\w{8}>.png controllers.Export.png(gameId: String)
|
GET /game/export/png/$gameId<\w{8}>.png controllers.Export.png(gameId: String)
|
||||||
|
GET /game/visualizer/$gameId<\w{8}> controllers.Export.visualizer(gameId: String)
|
||||||
|
|
||||||
# Fishnet
|
# Fishnet
|
||||||
POST /fishnet/acquire controllers.Fishnet.acquire
|
POST /fishnet/acquire controllers.Fishnet.acquire
|
||||||
|
|
|
@ -379,6 +379,7 @@ object mon {
|
||||||
def puzzle = inc("export.png.puzzle")
|
def puzzle = inc("export.png.puzzle")
|
||||||
}
|
}
|
||||||
def pdf = inc("export.pdf.game")
|
def pdf = inc("export.pdf.game")
|
||||||
|
def visualizer = inc("export.visualizer.game")
|
||||||
}
|
}
|
||||||
|
|
||||||
def measure[A](path: RecPath)(op: => A) = {
|
def measure[A](path: RecPath)(op: => A) = {
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 64e15a1f0e95f6e79735e24f30dda9f9042f6053
|
Loading…
Reference in New Issue