diff --git a/.gitmodules b/.gitmodules index 66b7eadf20..f6c2c412fc 100644 --- a/.gitmodules +++ b/.gitmodules @@ -40,3 +40,6 @@ [submodule "public/vendor/bar-rating"] path = public/vendor/bar-rating url = https://github.com/antennaio/jquery-bar-rating +[submodule "public/visualizer"] + path = public/visualizer + url = https://github.com/ornicar/chess_game_visualizer diff --git a/app/controllers/Export.scala b/app/controllers/Export.scala index 736e2ddbce..9f078e19be 100644 --- a/app/controllers/Export.scala +++ b/app/controllers/Export.scala @@ -18,24 +18,33 @@ object Export extends LilaController { def pgn(id: String) = Open { implicit ctx => OnlyHumans { lila.mon.export.pgn.game() - OptionFuResult(GameRepo game id) { - case game if game.playable => NotFound("Can't export PGN of game in progress").fuccess - case game => (game.pgnImport.ifTrue(get("as") contains "imported") match { - case Some(i) => fuccess(i.pgn) - case None => for { - initialFen <- GameRepo initialFen game - pgn = Env.api.pgnDump(game, initialFen) - analysis ← !get("as").contains("raw") ?? (Env.analyse.analyser get game.id) - } yield Env.analyse.annotator(pgn, analysis, game.opening, game.winnerColor, game.status, game.clock).toString - }) map { content => - Ok(content).withHeaders( - CONTENT_TYPE -> ContentTypes.TEXT, - CONTENT_DISPOSITION -> ("attachment; filename=" + (Env.api.pgnDump filename game))) - } + OptionFuResult(GameRepo game id) { game => + gameToPgn( + game, + asImported = get("as") contains "imported", + asRaw = get("as").contains("raw")) map { content => + Ok(content).withHeaders( + CONTENT_TYPE -> ContentTypes.TEXT, + CONTENT_DISPOSITION -> ("attachment; filename=" + (Env.api.pgnDump filename game))) + } recover { + case err => NotFound(err.getMessage) + } } } } + 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( credits = 20, 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 => OnlyHumansAndFacebook { PngRateLimitGlobal("-", msg = HTTPRequest lastRemoteAddress ctx.req) { diff --git a/app/views/analyse/replay.scala.html b/app/views/analyse/replay.scala.html index f590ae6ddb..53e440a288 100644 --- a/app/views/analyse/replay.scala.html +++ b/app/views/analyse/replay.scala.html @@ -64,17 +64,19 @@ atom = atom.some) {

FEN

PGN - @trans.downloadAnnotated() + @trans.downloadAnnotated() @if(analysis.isDefined) { / - @trans.downloadRaw() + @trans.downloadRaw() } @if(game.isPgnImport) { / - @trans.downloadImported() + @trans.downloadImported() } / - @trans.printFriendlyPDF() + @trans.printFriendlyPDF() + / + Generate images

@Html(nl2br(escapeHtml(pgn)))
diff --git a/conf/routes b/conf/routes index f45730e7ba..b6ffc1b9cd 100644 --- a/conf/routes +++ b/conf/routes @@ -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/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/visualizer/$gameId<\w{8}> controllers.Export.visualizer(gameId: String) # Fishnet POST /fishnet/acquire controllers.Fishnet.acquire diff --git a/modules/common/src/main/mon.scala b/modules/common/src/main/mon.scala index d99371a950..521204da26 100644 --- a/modules/common/src/main/mon.scala +++ b/modules/common/src/main/mon.scala @@ -379,6 +379,7 @@ object mon { def puzzle = inc("export.png.puzzle") } def pdf = inc("export.pdf.game") + def visualizer = inc("export.visualizer.game") } def measure[A](path: RecPath)(op: => A) = { diff --git a/public/visualizer b/public/visualizer new file mode 160000 index 0000000000..64e15a1f0e --- /dev/null +++ b/public/visualizer @@ -0,0 +1 @@ +Subproject commit 64e15a1f0e95f6e79735e24f30dda9f9042f6053