improve tv stream
parent
fa46838ce6
commit
4fcec2105b
|
@ -72,12 +72,13 @@ final class Tv(
|
|||
}
|
||||
|
||||
def feed =
|
||||
Action.async {
|
||||
Action.async { req =>
|
||||
import makeTimeout.short
|
||||
import akka.pattern.ask
|
||||
import lila.round.TvBroadcast
|
||||
import play.api.libs.EventSource
|
||||
env.round.tvBroadcast ? TvBroadcast.Connect mapTo
|
||||
val fromLichess = getBool("bc", req)
|
||||
env.round.tvBroadcast ? TvBroadcast.Connect(fromLichess) mapTo
|
||||
manifest[TvBroadcast.SourceType] map { source =>
|
||||
Ok.chunked(source via EventSource.flow).as(ContentTypes.EVENT_STREAM) pipe noProxyBuffer
|
||||
}
|
||||
|
|
|
@ -8,14 +8,14 @@ import lila.app.ui.ScalatagsTemplate._
|
|||
|
||||
object embed {
|
||||
|
||||
private val dataStreamUrl = attr("data-stream-url")
|
||||
private val dataStreamUrl = attr("data-stream-url") := "/tv/feed?bc=1"
|
||||
|
||||
def apply(pov: lila.game.Pov)(implicit config: EmbedConfig) =
|
||||
views.html.base.embed(
|
||||
title = "lichess.org chess TV",
|
||||
cssModule = "tv.embed"
|
||||
)(
|
||||
dataStreamUrl := routes.Tv.feed(),
|
||||
dataStreamUrl,
|
||||
div(id := "featured-game", cls := "embedded", title := "lichess.org TV")(
|
||||
views.html.game.mini.noCtx(pov, tv = true, blank = true)
|
||||
),
|
||||
|
|
|
@ -187,11 +187,6 @@ package timeline {
|
|||
}
|
||||
}
|
||||
|
||||
package game {
|
||||
case class ChangeFeatured(id: String, msg: JsObject)
|
||||
case object Count
|
||||
}
|
||||
|
||||
package tv {
|
||||
case class TvSelect(gameId: String, speed: chess.Speed, data: JsObject)
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@ import scala.concurrent.duration._
|
|||
import scala.concurrent.Promise
|
||||
|
||||
import lila.game.Pov
|
||||
import lila.hub.actorApi.game.ChangeFeatured
|
||||
import lila.hub.actorApi.timeline._
|
||||
import lila.hub.Trouper
|
||||
import lila.i18n.defaultLang
|
||||
|
@ -15,6 +14,7 @@ import lila.rating.RatingRange
|
|||
import lila.socket.RemoteSocket.{ Protocol => P, _ }
|
||||
import lila.socket.Socket.{ makeMessage, Sri, Sris }
|
||||
import lila.user.User
|
||||
import lila.round.ChangeFeatured
|
||||
|
||||
case class LobbyCounters(members: Int, rounds: Int)
|
||||
|
||||
|
|
|
@ -50,6 +50,7 @@ final class Env(
|
|||
evalCache: lila.evalCache.EvalCacheApi,
|
||||
remoteSocketApi: lila.socket.RemoteSocket,
|
||||
isBotSync: lila.common.LightUser.IsBotSync,
|
||||
lightUserSync: lila.common.LightUser.GetterSync,
|
||||
slackApi: lila.slack.SlackApi,
|
||||
ratingFactors: () => lila.rating.RatingFactors,
|
||||
shutdown: akka.actor.CoordinatedShutdown
|
||||
|
@ -179,7 +180,7 @@ final class Env(
|
|||
|
||||
val playing = wire[PlayingUsers]
|
||||
|
||||
val tvBroadcast = system.actorOf(Props(classOf[TvBroadcast]))
|
||||
val tvBroadcast = system.actorOf(Props(wire[TvBroadcast]))
|
||||
|
||||
def resign(pov: Pov): Unit =
|
||||
if (pov.game.abortable) tellRound(pov.gameId, Abort(pov.playerId))
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
package lila.round
|
||||
|
||||
import scala.math
|
||||
|
||||
import actorApi.SocketStatus
|
||||
import chess.format.{ FEN, Forsyth }
|
||||
import chess.{ Clock, Color }
|
||||
import play.api.libs.json._
|
||||
import scala.math
|
||||
|
||||
import lila.common.ApiVersion
|
||||
import lila.game.JsonView._
|
||||
|
@ -10,11 +12,6 @@ import lila.game.{ Pov, Game, Player => GamePlayer }
|
|||
import lila.pref.Pref
|
||||
import lila.user.{ User, UserRepo }
|
||||
|
||||
import chess.format.{ FEN, Forsyth }
|
||||
import chess.{ Clock, Color }
|
||||
|
||||
import actorApi.SocketStatus
|
||||
|
||||
final class JsonView(
|
||||
userRepo: UserRepo,
|
||||
userJsonView: lila.user.JsonView,
|
||||
|
@ -38,9 +35,7 @@ final class JsonView(
|
|||
|
||||
private def commonPlayerJson(g: Game, p: GamePlayer, user: Option[User], withFlags: WithFlags): JsObject =
|
||||
Json
|
||||
.obj(
|
||||
"color" -> p.color.name
|
||||
)
|
||||
.obj("color" -> p.color.name)
|
||||
.add("user" -> user.map { userJsonView.minimal(_, g.perfType) })
|
||||
.add("rating" -> p.rating)
|
||||
.add("ratingDiff" -> p.ratingDiff)
|
||||
|
|
|
@ -2,20 +2,27 @@ package lila.round
|
|||
|
||||
import akka.actor._
|
||||
import akka.stream.scaladsl._
|
||||
import lila.game.actorApi.MoveGameEvent
|
||||
import lila.hub.actorApi.game.ChangeFeatured
|
||||
import lila.socket.Socket.makeMessage
|
||||
import chess.format.FEN
|
||||
import chess.format.Forsyth
|
||||
import play.api.libs.json._
|
||||
|
||||
import lila.common.Bus
|
||||
import lila.common.LightUser
|
||||
import lila.game.actorApi.MoveGameEvent
|
||||
import lila.game.Game
|
||||
import lila.socket.Socket
|
||||
import lila.socket.Socket.makeMessage
|
||||
|
||||
final private class TvBroadcast extends Actor {
|
||||
final private class TvBroadcast(
|
||||
userJsonView: lila.user.JsonView,
|
||||
lightUserSync: LightUser.GetterSync
|
||||
) extends Actor {
|
||||
|
||||
import TvBroadcast._
|
||||
|
||||
private var queues = Set.empty[Queue]
|
||||
private var clients = Set.empty[Client]
|
||||
|
||||
private var featuredId = none[String]
|
||||
private var featured = none[Featured]
|
||||
|
||||
Bus.subscribe(self, "changeFeaturedGame")
|
||||
|
||||
|
@ -28,26 +35,51 @@ final private class TvBroadcast extends Actor {
|
|||
|
||||
def receive = {
|
||||
|
||||
case TvBroadcast.Connect =>
|
||||
case TvBroadcast.Connect(compat) =>
|
||||
sender() ! Source
|
||||
.queue[JsValue](8, akka.stream.OverflowStrategy.dropHead)
|
||||
.mapMaterializedValue { queue =>
|
||||
self ! Add(queue)
|
||||
val client = Client(queue, compat)
|
||||
self ! Add(client)
|
||||
queue.watchCompletion().foreach { _ =>
|
||||
self ! Remove(queue)
|
||||
self ! Remove(client)
|
||||
}
|
||||
featured ifFalse compat foreach { f =>
|
||||
client.queue.offer(Socket.makeMessage("featured", f.dataWithFen))
|
||||
}
|
||||
}
|
||||
|
||||
case Add(queue) => queues = queues + queue
|
||||
case Remove(queue) => queues = queues - queue
|
||||
case Add(client) => clients = clients + client
|
||||
case Remove(client) => clients = clients - client
|
||||
|
||||
case ChangeFeatured(id, msg) =>
|
||||
case ChangeFeatured(pov, msg) =>
|
||||
unsubscribeFromFeaturedId()
|
||||
Bus.subscribe(self, MoveGameEvent makeChan id)
|
||||
featuredId = id.some
|
||||
queues.foreach(_ offer msg)
|
||||
Bus.subscribe(self, MoveGameEvent makeChan pov.gameId)
|
||||
val feat = Featured(
|
||||
pov.gameId,
|
||||
Json.obj(
|
||||
"id" -> pov.gameId,
|
||||
"orientation" -> pov.color.name,
|
||||
"players" -> pov.game.players.map { p =>
|
||||
val user = p.userId.flatMap(lightUserSync)
|
||||
Json
|
||||
.obj("color" -> p.color.name)
|
||||
.add("user" -> user.map(LightUser.lightUserWrites.writes))
|
||||
.add("ai" -> p.aiLevel)
|
||||
.add("rating" -> p.rating)
|
||||
}
|
||||
),
|
||||
fen = Forsyth exportBoard pov.game.chess.board
|
||||
)
|
||||
clients.foreach { client =>
|
||||
client.queue offer {
|
||||
if (client.fromLichess) msg
|
||||
else feat.socketMsg
|
||||
}
|
||||
}
|
||||
featured = feat.some
|
||||
|
||||
case MoveGameEvent(game, fen, move) if queues.nonEmpty =>
|
||||
case MoveGameEvent(game, fen, move) =>
|
||||
val msg = makeMessage(
|
||||
"fen",
|
||||
Json
|
||||
|
@ -58,12 +90,15 @@ final private class TvBroadcast extends Actor {
|
|||
.add("wc" -> game.clock.map(_.remainingTime(chess.White).roundSeconds))
|
||||
.add("bc" -> game.clock.map(_.remainingTime(chess.Black).roundSeconds))
|
||||
)
|
||||
queues.foreach(_ offer msg)
|
||||
clients.foreach(_.queue offer msg)
|
||||
featured foreach { f =>
|
||||
featured = f.copy(fen = fen).some
|
||||
}
|
||||
}
|
||||
|
||||
def unsubscribeFromFeaturedId() =
|
||||
featuredId foreach { previous =>
|
||||
Bus.unsubscribe(self, MoveGameEvent makeChan previous)
|
||||
featured foreach { previous =>
|
||||
Bus.unsubscribe(self, MoveGameEvent makeChan previous.id)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -72,8 +107,14 @@ object TvBroadcast {
|
|||
type SourceType = Source[JsValue, _]
|
||||
type Queue = SourceQueueWithComplete[JsValue]
|
||||
|
||||
case object Connect
|
||||
case class Featured(id: Game.ID, data: JsObject, fen: String) {
|
||||
def dataWithFen = data ++ Json.obj("fen" -> fen)
|
||||
def socketMsg = Socket.makeMessage("featured", dataWithFen)
|
||||
}
|
||||
|
||||
case class Add(q: Queue)
|
||||
case class Remove(q: Queue)
|
||||
case class Connect(fromLichess: Boolean)
|
||||
case class Client(queue: Queue, fromLichess: Boolean)
|
||||
|
||||
case class Add(c: Client)
|
||||
case class Remove(c: Client)
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import scala.concurrent.duration.FiniteDuration
|
|||
|
||||
import lila.game.{ Game, Pov }
|
||||
import lila.user.User
|
||||
import play.api.libs.json.JsObject
|
||||
|
||||
private case class MoretimeDuration(value: FiniteDuration) extends AnyVal
|
||||
private case class AnimationDuration(value: FiniteDuration) extends AnyVal
|
||||
|
@ -27,3 +28,5 @@ final private class ScheduleExpiration(f: Game => Unit) extends (Game => Unit) {
|
|||
final class IsOfferingRematch(f: Pov => Boolean) extends (Pov => Boolean) {
|
||||
def apply(p: Pov) = f(p)
|
||||
}
|
||||
|
||||
case class ChangeFeatured(pov: Pov, mgs: JsObject)
|
||||
|
|
|
@ -6,7 +6,7 @@ import scala.concurrent.duration._
|
|||
import scala.concurrent.Promise
|
||||
|
||||
import lila.common.{ Bus, LightUser }
|
||||
import lila.game.Game
|
||||
import lila.game.{ Game, Pov }
|
||||
import lila.hub.Trouper
|
||||
|
||||
final private[tv] class TvTrouper(
|
||||
|
@ -79,13 +79,14 @@ final private[tv] class TvTrouper(
|
|||
if (channel == Tv.Channel.Best) {
|
||||
implicit def timeout = makeTimeout(100 millis)
|
||||
actorAsk(renderer.actor, actorApi.RenderFeaturedJs(game)) foreach { case html: String =>
|
||||
val event = lila.hub.actorApi.game.ChangeFeatured(
|
||||
game.id,
|
||||
val pov = Pov naturalOrientation game
|
||||
val event = lila.round.ChangeFeatured(
|
||||
pov,
|
||||
makeMessage(
|
||||
"featured",
|
||||
Json.obj(
|
||||
"html" -> html,
|
||||
"color" -> game.naturalOrientation.name,
|
||||
"color" -> pov.color.name,
|
||||
"id" -> game.id
|
||||
)
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue