Skip to content

Commit

Permalink
throttle and monitor youtube stream api calls
Browse files Browse the repository at this point in the history
  • Loading branch information
ornicar committed Mar 8, 2020
1 parent dc1eead commit fef7c3d
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 37 deletions.
12 changes: 7 additions & 5 deletions app/controllers/Game.scala
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,13 @@ final class Game(
lila.mon.export.pgn.game.increment()
env.api.gameApiV2.exportOne(game, config) flatMap { content =>
env.api.gameApiV2.filename(game, config.format) map { filename =>
Ok(content).withHeaders(
CONTENT_DISPOSITION -> s"attachment; filename=$filename"
).withHeaders(
lila.app.http.ResponseHeaders.headersForApiOrApp(ctx.req): _*
) as gameContentType(config)
Ok(content)
.withHeaders(
CONTENT_DISPOSITION -> s"attachment; filename=$filename"
)
.withHeaders(
lila.app.http.ResponseHeaders.headersForApiOrApp(ctx.req): _*
) as gameContentType(config)
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions modules/common/src/main/mon.scala
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,8 @@ object mon {
object tv {
object streamer {
def present(n: String) = gauge("tv.streamer.present").withTag("name", n)
def youTube = future("tv.streamer.youtube")
def twitch = future("tv.streamer.twitch")
}
}
object crosstable {
Expand Down
3 changes: 3 additions & 0 deletions modules/streamer/src/main/Stream.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package lila.streamer

import play.api.libs.json._
import org.joda.time.DateTime

import lila.user.User
import lila.common.String.html.unescapeHtml
Expand Down Expand Up @@ -81,5 +82,7 @@ object Stream {
implicit private val youtubeItemReads = Json.reads[Item]
implicit val youtubeResultReads = Json.reads[Result]
}

case class StreamsFetched(list: List[YouTube.Stream], at: DateTime)
}
}
82 changes: 50 additions & 32 deletions modules/streamer/src/main/Streaming.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package lila.streamer

import akka.actor._
import org.joda.time.DateTime
import play.api.libs.json._
import play.api.libs.ws.WSClient
import scala.concurrent.duration._
Expand Down Expand Up @@ -116,45 +117,62 @@ final private class Streaming(
.withHttpHeaders(
"Client-ID" -> twitchClientId.value
)
url.get().map { res =>
res.json.validate[Twitch.Result](twitchResultReads) match {
case JsSuccess(data, _) =>
data.streams(
keyword,
streamers,
alwaysFeatured().value.map(_.toLowerCase)
)
case JsError(err) =>
logger.warn(s"twitch ${res.status} $err ${~res.body.linesIterator.toList.headOption}")
Nil
url
.get()
.map { res =>
res.json.validate[Twitch.Result](twitchResultReads) match {
case JsSuccess(data, _) =>
data.streams(
keyword,
streamers,
alwaysFeatured().value.map(_.toLowerCase)
)
case JsError(err) =>
logger.warn(s"twitch ${res.status} $err ${~res.body.linesIterator.toList.headOption}")
Nil
}
}
}
.monSuccess(_.tv.streamer.twitch)
}
}
}

def fetchYouTubeStreams(streamers: List[Streamer]): Fu[List[YouTube.Stream]] =
googleApiKey.value.nonEmpty ?? {
val youtubeStreamers = streamers.filter(_.youTube.isDefined)
youtubeStreamers.nonEmpty ?? ws
.url("https://www.googleapis.com/youtube/v3/search")
.withQueryStringParameters(
"part" -> "snippet",
"type" -> "video",
"eventType" -> "live",
"q" -> keyword.value,
"key" -> googleApiKey.value
)
.get()
.map { res =>
res.json.validate[YouTube.Result](youtubeResultReads) match {
case JsSuccess(data, _) => data.streams(keyword, youtubeStreamers)
case JsError(err) =>
logger.warn(s"youtube ${res.status} $err ${res.body.take(500)}")
Nil
}
private var prevYouTubeStreams = YouTube.StreamsFetched(Nil, DateTime.now)

def fetchYouTubeStreams(streamers: List[Streamer]): Fu[List[YouTube.Stream]] = {
val youtubeStreamers = streamers.filter(_.youTube.isDefined)
(youtubeStreamers.nonEmpty && googleApiKey.value.nonEmpty) ?? {
val now = DateTime.now
val res =
if (prevYouTubeStreams.list.isEmpty && prevYouTubeStreams.at.isAfter(now minusMinutes 2))
fuccess(prevYouTubeStreams)
else {
ws.url("https://www.googleapis.com/youtube/v3/search")
.withQueryStringParameters(
"part" -> "snippet",
"type" -> "video",
"eventType" -> "live",
"q" -> keyword.value,
"key" -> googleApiKey.value
)
.get()
.map { res =>
res.json.validate[YouTube.Result](youtubeResultReads) match {
case JsSuccess(data, _) =>
YouTube.StreamsFetched(data.streams(keyword, youtubeStreamers), now)
case JsError(err) =>
logger.warn(s"youtube ${res.status} $err ${res.body.take(500)}")
YouTube.StreamsFetched(Nil, now)
}
}
.monSuccess(_.tv.streamer.youTube)
}
res dmap { r =>
prevYouTubeStreams = r
r.list
}
}
}

def dedupStreamers(streams: List[Stream]): List[Stream] =
streams
Expand Down

0 comments on commit fef7c3d

Please sign in to comment.