performance: server simplified replay pages to web crawlers
This commit is contained in:
parent
89bba41886
commit
c7434ebafa
|
@ -52,15 +52,13 @@ object Analyse extends LilaController {
|
|||
}
|
||||
|
||||
def replay(pov: Pov, userTv: Option[lila.user.User])(implicit ctx: Context) =
|
||||
GameRepo initialFen pov.game.id flatMap { initialFen =>
|
||||
if (HTTPRequest isBot ctx.req) replayBot(pov)
|
||||
else GameRepo initialFen pov.game.id flatMap { initialFen =>
|
||||
(env.analyser get pov.game.id) zip
|
||||
(pov.game.tournamentId ?? lila.tournament.TournamentRepo.byId) zip
|
||||
(pov.game.simulId ?? Env.simul.repo.find) zip
|
||||
Env.game.crosstableApi(pov.game) flatMap {
|
||||
case (((analysis, tour), simul), crosstable) =>
|
||||
val division =
|
||||
if (HTTPRequest.isBot(ctx.req)) divider.empty
|
||||
else divider(pov.game, initialFen)
|
||||
val pgn = Env.game.pgnDump(pov.game, initialFen)
|
||||
Env.api.roundApi.watcher(pov, lila.api.Mobile.Api.currentVersion,
|
||||
tv = none,
|
||||
|
@ -71,6 +69,7 @@ object Analyse extends LilaController {
|
|||
Ok(html.analyse.replay(
|
||||
pov,
|
||||
data,
|
||||
initialFen,
|
||||
Env.analyse.annotator(pgn, analysis, pov.game.opening, pov.game.winnerColor, pov.game.status, pov.game.clock).toString,
|
||||
analysis,
|
||||
analysis filter (_.done) map { a => AdvantageChart(a.infoAdvices, pov.game.pgnMoves) },
|
||||
|
@ -79,8 +78,27 @@ object Analyse extends LilaController {
|
|||
new TimeChart(pov.game, pov.game.pgnMoves),
|
||||
crosstable,
|
||||
userTv,
|
||||
division))
|
||||
divider(pov.game, initialFen)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private def replayBot(pov: Pov)(implicit ctx: Context) =
|
||||
GameRepo initialFen pov.game.id flatMap { initialFen =>
|
||||
(env.analyser get pov.game.id) zip
|
||||
(pov.game.tournamentId ?? lila.tournament.TournamentRepo.byId) zip
|
||||
(pov.game.simulId ?? Env.simul.repo.find) zip
|
||||
Env.game.crosstableApi(pov.game) map {
|
||||
case (((analysis, tour), simul), crosstable) =>
|
||||
val pgn = Env.game.pgnDump(pov.game, initialFen)
|
||||
Ok(html.analyse.replayBot(
|
||||
pov,
|
||||
initialFen,
|
||||
Env.analyse.annotator(pgn, analysis, pov.game.opening, pov.game.winnerColor, pov.game.status, pov.game.clock).toString,
|
||||
analysis,
|
||||
tour,
|
||||
simul,
|
||||
crosstable))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ import play.twirl.api.Html
|
|||
|
||||
import lila.api.Context
|
||||
import lila.app._
|
||||
import lila.common.HTTPRequest
|
||||
import lila.game.{ Pov, PlayerRef, GameRepo, Game => GameModel }
|
||||
import lila.hub.actorApi.map.Tell
|
||||
import lila.round.actorApi.round._
|
||||
|
@ -141,7 +142,7 @@ object Round extends LilaController with TheftPrevention {
|
|||
else if (pov.game.joinable) join(pov)
|
||||
else ctx.userId.flatMap(pov.game.playerByUserId) ifTrue pov.game.playable match {
|
||||
case Some(player) => renderPlayer(pov withColor player.color)
|
||||
case None =>
|
||||
case None if HTTPRequest.isHuman(ctx.req) =>
|
||||
(pov.game.tournamentId ?? TournamentRepo.byId) zip
|
||||
(pov.game.simulId ?? Env.simul.repo.find) zip
|
||||
Env.game.crosstableApi(pov.game) zip
|
||||
|
@ -149,6 +150,13 @@ object Round extends LilaController with TheftPrevention {
|
|||
case (((tour, simul), crosstable), data) =>
|
||||
Ok(html.round.watcher(pov, data, tour, simul, crosstable, userTv = userTv))
|
||||
}
|
||||
case _ => // web crawlers don't need the full thing
|
||||
GameRepo.initialFen(pov.game.id) zip
|
||||
Env.game.crosstableApi(pov.game) map {
|
||||
case (initialFen, crosstable) =>
|
||||
val pgn = Env.game.pgnDump(pov.game, initialFen)
|
||||
Ok(html.round.watcherBot(pov, initialFen, pgn, crosstable))
|
||||
}
|
||||
},
|
||||
api = apiVersion => Env.api.roundApi.watcher(pov, apiVersion, tv = none) map { Ok(_) }
|
||||
)
|
||||
|
|
|
@ -19,17 +19,21 @@ trait GameHelper { self: I18nHelper with UserHelper with AiHelper with StringHel
|
|||
def mandatorySecondsToMove = lila.game.Env.current.MandatorySecondsToMove
|
||||
|
||||
def povOpenGraph(pov: Pov) = {
|
||||
val speed = chess.Speed(pov.game.clock).name
|
||||
val variant = pov.game.variant.exotic ?? s" ${pov.game.variant.name}"
|
||||
Map(
|
||||
'type -> "website",
|
||||
'image -> cdnUrl(routes.Export.png(pov.game.id).url),
|
||||
'title -> s"$speed$variant Chess • ${playerText(pov.game.whitePlayer)} vs ${playerText(pov.game.blackPlayer)}",
|
||||
'title -> titlePov(pov),
|
||||
'site_name -> "lichess.org",
|
||||
'url -> s"$netBaseUrl${routes.Round.watcher(pov.game.id, pov.color.name).url}",
|
||||
'description -> describePov(pov))
|
||||
}
|
||||
|
||||
def titlePov(pov: Pov) = {
|
||||
val speed = chess.Speed(pov.game.clock).name
|
||||
val variant = pov.game.variant.exotic ?? s" ${pov.game.variant.name}"
|
||||
s"$speed$variant Chess • ${playerText(pov.game.whitePlayer)} vs ${playerText(pov.game.blackPlayer)}"
|
||||
}
|
||||
|
||||
def describePov(pov: Pov) = {
|
||||
import pov._
|
||||
val p1 = playerText(player, withRating = true)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
@(title: String, side: Option[Html] = None, chat: Option[Html] = None, underchat: Option[Html] = None, moreCss: Html = Html(""), moreJs: Html = Html(""), openGraph: Map[Symbol, String] = Map.empty)(body: Html)(implicit ctx: Context)
|
||||
@(title: String, side: Option[Html] = None, chat: Option[Html] = None, underchat: Option[Html] = None, moreCss: Html = Html(""), moreJs: Html = Html(""), openGraph: Map[Symbol, String] = Map.empty, chessground: Boolean)(body: Html)(implicit ctx: Context)
|
||||
|
||||
@base.layout(
|
||||
title = title,
|
||||
|
@ -8,4 +8,4 @@ underchat = underchat,
|
|||
moreCss = moreCss,
|
||||
moreJs = moreJs,
|
||||
openGraph = openGraph,
|
||||
chessground = false)(body)
|
||||
chessground = chessground)(body)
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
@import lila.evaluation.PlayerAssessments
|
||||
|
||||
@(pov: Pov, data: play.api.libs.json.JsObject, pgn: String, analysis: Option[lila.analyse.Analysis], advantageChart: Option[String], tour: Option[lila.tournament.Tournament], simul: Option[lila.simul.Simul], timeChart: lila.analyse.TimeChart, cross: Option[lila.game.Crosstable], userTv: Option[User], division: chess.Division)(implicit ctx: Context)
|
||||
@(pov: Pov, data: play.api.libs.json.JsObject, initialFen: Option[String], pgn: String, analysis: Option[lila.analyse.Analysis], advantageChart: Option[String], tour: Option[lila.tournament.Tournament], simul: Option[lila.simul.Simul], timeChart: lila.analyse.TimeChart, cross: Option[lila.game.Crosstable], userTv: Option[User], division: chess.Division)(implicit ctx: Context)
|
||||
|
||||
@import pov._
|
||||
|
||||
|
@ -47,12 +45,13 @@ userId: @Html(ctx.userId.fold("null")(id => s""""$id""""))
|
|||
|
||||
@analyse.layout(
|
||||
title = title,
|
||||
side = views.html.game.side(pov, (data\"game"\"initialFen").asOpt[String], tour, withTourStanding = false, simul = simul, userTv = userTv).some,
|
||||
side = views.html.game.side(pov, initialFen, tour, withTourStanding = false, simul = simul, userTv = userTv).some,
|
||||
chat = base.chatDom(trans.spectatorRoom.str(), ctx.isAuth).some,
|
||||
underchat = underchat.some,
|
||||
moreCss = moreCss,
|
||||
moreJs = moreJs,
|
||||
openGraph = povOpenGraph(pov)) {
|
||||
openGraph = povOpenGraph(pov),
|
||||
chessground = false) {
|
||||
<div class="analyse cg-512">@miniBoardContent</div>
|
||||
<div class="advice_summary" style="display:none">
|
||||
@analysis.filter(_.done).map { a =>
|
||||
|
|
127
app/views/analyse/replayBot.scala.html
Normal file
127
app/views/analyse/replayBot.scala.html
Normal file
|
@ -0,0 +1,127 @@
|
|||
@(pov: Pov, initialFen: Option[String], pgn: String, analysis: Option[lila.analyse.Analysis], tour: Option[lila.tournament.Tournament], simul: Option[lila.simul.Simul], cross: Option[lila.game.Crosstable])(implicit ctx: Context)
|
||||
|
||||
@import pov._
|
||||
|
||||
@title = @{ s"${playerText(pov.player)} vs ${playerText(pov.opponent)} in $gameId : ${game.opening.fold(trans.analysis.str())(_.fullName)}" }
|
||||
|
||||
@moreJs = {
|
||||
@embedJs {
|
||||
Chessground(document.querySelector('#lichess .analyse .lichess_board'), {
|
||||
viewOnly: true,
|
||||
fen: "@{chess.format.Forsyth.>>(pov.game.toChess)}",
|
||||
orientation: "@pov.color.name"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@moreCss = {
|
||||
@cssTag("analyse.css")
|
||||
}
|
||||
|
||||
@underchat = {
|
||||
@views.html.game.watchers()
|
||||
<div class="shortcuts">
|
||||
<p class="title text" data-icon="u">Keyboard Shortcuts</p>
|
||||
<div class="inner">
|
||||
<ul>
|
||||
<li><strong>h</strong>/<strong>l</strong> or <strong>←</strong>/<strong>→</strong> move backward/forward</li>
|
||||
<li><strong>j</strong>/<strong>k</strong> or <strong>↑</strong>/<strong>↓</strong> go to start/end</li>
|
||||
<li><strong>c</strong> show/hide comments</li>
|
||||
<li><strong>shift</strong> + <strong>h</strong>/<strong>l</strong> or <strong>←</strong>/<strong>→</strong> enter/exit variation</li>
|
||||
</ul>
|
||||
Press shift+click or right-click to draw circles and arrows on the board!<br />
|
||||
You can also scroll over the board to move in the game.
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
@analyse.layout(
|
||||
title = title,
|
||||
side = views.html.game.side(pov, initialFen, tour, withTourStanding = false, simul = simul).some,
|
||||
chat = base.chatDom(trans.spectatorRoom.str(), ctx.isAuth).some,
|
||||
underchat = underchat.some,
|
||||
moreJs = moreJs,
|
||||
moreCss = moreCss,
|
||||
openGraph = povOpenGraph(pov),
|
||||
chessground = true) {
|
||||
<div class="analyse cg-512">
|
||||
<div class="top">
|
||||
<div class="lichess_game">
|
||||
<div class="lichess_board_wrap">
|
||||
<div class="lichess_board">
|
||||
<div class="cg-board-wrap">
|
||||
<div class="cg-board"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="lichess_ground for_bot">
|
||||
<h1>@titlePov(pov)</h1>
|
||||
<p>@describePov(pov)</p>
|
||||
<p>@pov.game.opening.map(_.fullName)</p>
|
||||
<div class="pgn">@Html(nl2br(escape(pgn.toString)))</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="advice_summary" style="display:none">
|
||||
@analysis.filter(_.done).map { a =>
|
||||
<table>
|
||||
@for((color, pairs) <- a.summary) {
|
||||
<thead>
|
||||
<tr>
|
||||
<td>
|
||||
<span class="is color-icon @color"></span>
|
||||
</td>
|
||||
<th>@playerLink(pov.game.player(color), withOnline = false)</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@for((nag, nb) <- pairs) {
|
||||
<tr>
|
||||
<td><strong>@nb</strong></td>
|
||||
<th>@nagName(nag)</th>
|
||||
</tr>
|
||||
}
|
||||
<tr>
|
||||
<td><strong>@lila.analyse.Accuracy(pov.withColor(color), a)</strong></td>
|
||||
<th>Average centipawn loss</th>
|
||||
</tr>
|
||||
<tr><td class="spacerlol" colspan=2></td></tr>
|
||||
</tbody>
|
||||
}
|
||||
</table>
|
||||
}
|
||||
</div>
|
||||
<div class="underboard_content" style="display:none">
|
||||
<div class="analysis_panels">
|
||||
<div class="panel fen_pgn">
|
||||
<p><strong>FEN</strong><input type="input" readonly="true" spellcheck="false" class="copyable fen" /></p>
|
||||
<p><strong>PGN</strong>
|
||||
<a data-icon="x" rel="nofollow" href="@routes.Export.pgn(game.id)"> Download annotated</a>
|
||||
@if(analysis.isDefined) {
|
||||
/
|
||||
<a data-icon="x" rel="nofollow" href="@routes.Export.pgn(game.id)?as=raw"> Download raw</a>
|
||||
}
|
||||
@if(game.isPgnImport) {
|
||||
/
|
||||
<a data-icon="x" rel="nofollow" href="@routes.Export.pgn(game.id)?as=imported"> Download imported</a>
|
||||
}
|
||||
/
|
||||
<a data-icon="x" target="_blank" rel="nofollow" href="@cdnUrl(routes.Export.pdf(game.id).url)"> Print-friendly PDF</a>
|
||||
</p>
|
||||
<div class="pgn">@Html(nl2br(escape(pgn)))</div>
|
||||
</div>
|
||||
@cross.map { c =>
|
||||
<div class="panel crosstable">
|
||||
@views.html.game.crosstable(pov.player.userId.fold(c)(c.fromPov), pov.gameId.some)
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<div class="analysis_menu">
|
||||
@if(cross.isDefined) {
|
||||
<a data-panel="crosstable" class="crosstable">Crosstable</a>
|
||||
}
|
||||
<a data-panel="fen_pgn" class="fen_pgn">FEN & PGN</a>
|
||||
</div>
|
||||
</div>
|
||||
}
|
45
app/views/round/watcherBot.scala.html
Normal file
45
app/views/round/watcherBot.scala.html
Normal file
|
@ -0,0 +1,45 @@
|
|||
@(pov: Pov, initialFen: Option[String], pgn: chess.format.pgn.Pgn, cross: Option[lila.game.Crosstable])(implicit ctx: Context)
|
||||
|
||||
@title = @{ s"${playerText(pov.player)} vs ${playerText(pov.opponent)} in ${pov.gameId}" }
|
||||
|
||||
@moreJs = {
|
||||
@embedJs {
|
||||
Chessground(document.querySelector('#lichess .round .lichess_board'), {
|
||||
viewOnly: true,
|
||||
fen: "@{chess.format.Forsyth.>>(pov.game.toChess)}",
|
||||
orientation: "@pov.color.name"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@round.layout(
|
||||
title = title,
|
||||
side = views.html.game.side(pov, initialFen, none, withTourStanding = false, simul = none, userTv = none),
|
||||
chat = base.chatDom(trans.spectatorRoom.str()).some,
|
||||
underchat = views.html.game.watchers().some,
|
||||
moreJs = moreJs,
|
||||
openGraph = povOpenGraph(pov)) {
|
||||
<div class="round cg-512">
|
||||
<div class="top">
|
||||
<div class="lichess_game">
|
||||
<div class="lichess_board_wrap">
|
||||
<div class="lichess_board">
|
||||
<div class="cg-board-wrap">
|
||||
<div class="cg-board"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="lichess_ground for_bot">
|
||||
<h1>@titlePov(pov)</h1>
|
||||
<p>@describePov(pov)</p>
|
||||
<div class="pgn">@Html(nl2br(escape(pgn.toString)))</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="crosstable" style="display:none">
|
||||
@cross.map { c =>
|
||||
@views.html.game.crosstable(c, pov.gameId.some)
|
||||
}
|
||||
</div>
|
||||
}
|
|
@ -1 +1 @@
|
|||
Subproject commit c472c3449d8fe9b7f288b6c52ea8808703975a87
|
||||
Subproject commit f2af8bcb5dc2105ed383932a128d948f53e0fdc0
|
|
@ -35,6 +35,8 @@ object HTTPRequest {
|
|||
isBotPattern.matcher(ua).matches
|
||||
}
|
||||
|
||||
def isHuman(req: RequestHeader) = !isBot(req)
|
||||
|
||||
def isFacebookBot(req: RequestHeader) = userAgent(req) ?? (_ contains "facebookexternalhit")
|
||||
|
||||
private val fileExtensionPattern = """.+\.[a-z0-9]{2,4}$""".r.pattern
|
||||
|
|
|
@ -214,6 +214,12 @@ div.lichess_game div.lichess_ground {
|
|||
height: 512px;
|
||||
width: 242px;
|
||||
}
|
||||
div.lichess_game div.lichess_ground.for_bot h1 {
|
||||
font-size: 2em;
|
||||
}
|
||||
div.lichess_game div.lichess_ground.for_bot p {
|
||||
margin: 1em 0;
|
||||
}
|
||||
div.lichess_board {
|
||||
position: relative;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue