upload streamer images to picfit

pull/9751/head
Thibault Duplessis 2021-09-07 19:56:02 +02:00
parent d095ecd1df
commit 4a95e0e3ee
12 changed files with 50 additions and 55 deletions

View File

@ -1,10 +1,11 @@
package controllers
import play.api.libs.json._
import play.api.mvc._
import scala.concurrent.duration._
import views._
import lila.api.Context
import play.api.libs.json._
import lila.app._
import lila.streamer.{ Streamer => StreamerModel, StreamerForm }
@ -167,26 +168,28 @@ final class Streamer(env: Env, apiC: => Api) extends LilaController(env) {
}
}
private val ImageRateLimitPerIp = lila.memo.RateLimit.composite[lila.common.IpAddress](
key = "streamer.image.ip"
)(
("fast", 10, 2.minutes),
("slow", 30, 1.day)
)
def pictureApply =
AuthBody(parse.multipartFormData) { implicit ctx => me =>
AsStreamer { s =>
ctx.body.body.file("picture") match {
case Some(pic) =>
api.uploadPicture(s.streamer, pic, me) recover { case e: Exception =>
BadRequest(html.streamer.picture(s, e.getMessage.some))
} inject Redirect(routes.Streamer.edit)
case None => fuccess(Redirect(routes.Streamer.edit))
ImageRateLimitPerIp(ctx.ip) {
api.uploadPicture(s.streamer, pic, me) recover { case e: Exception =>
BadRequest(html.streamer.picture(s, e.getMessage.some))
} inject Redirect(routes.Streamer.edit)
}(rateLimitedFu)
case None => Redirect(routes.Streamer.edit).flashFailure.fuccess
}
}
}
def pictureDelete =
Auth { implicit ctx => _ =>
AsStreamer { s =>
api.deletePicture(s.streamer) inject Redirect(routes.Streamer.edit)
}
}
private def AsStreamer(f: StreamerModel.WithUser => Fu[Result])(implicit ctx: Context) =
ctx.me.fold(notFound) { me =>
if (StreamerModel canApply me)

View File

@ -31,25 +31,22 @@ object bits extends Context.ToLang {
)
)
def pic(s: lila.streamer.Streamer, u: User, size: Int = 300) =
s.picturePath match {
case Some(path) =>
img(
width := size,
height := size,
cls := "picture",
src := dbImageUrl(path.value),
alt := s"${u.titleUsername} Lichess streamer picture"
)
case _ =>
img(
width := size,
height := size,
cls := "default picture",
src := assetUrl("images/placeholder.png"),
alt := "Default Lichess streamer picture"
)
}
object thumbnail {
val size = 300
def apply(s: lila.streamer.Streamer, u: User) =
img(
width := size,
height := size,
cls := "picture",
src := url(s),
alt := s"${u.titleUsername} Lichess streamer picture"
)
def url(s: lila.streamer.Streamer) =
s.picture match {
case Some(image) => picfitUrl.thumbnail(image, size, size)
case _ => assetUrl("images/placeholder.png")
}
}
def menu(active: String, s: Option[lila.streamer.Streamer.WithUser])(implicit ctx: Context) =
st.nav(cls := "subnav")(

View File

@ -34,7 +34,7 @@ object edit extends Context.ToLang {
href := routes.Streamer.picture,
title := changePicture.txt()
)(
bits.pic(s.streamer, s.user)
bits.thumbnail(s.streamer, s.user)
)
else
div(cls := "picture-create")(

View File

@ -12,7 +12,7 @@ object header {
def apply(s: lila.streamer.Streamer.WithUserAndStream)(implicit ctx: Context) =
div(cls := "streamer-header")(
bits.pic(s.streamer, s.user),
bits.thumbnail(s.streamer, s.user),
div(cls := "overview")(
h1(dataIcon := "")(
titleTag(s.user.title),

View File

@ -27,7 +27,7 @@ object index {
else
bits.redirectLink(s.user.username, stream.isDefined.some)(cls := "overlay"),
stream.isDefined option span(cls := "ribbon")(span(trans.streamer.live())),
bits.pic(s.streamer, s.user),
bits.thumbnail(s.streamer, s.user),
div(cls := "overview")(
h1(dataIcon := "")(titleTag(s.user.title), s.streamer.name),
s.streamer.headline.map(_.value).map { d =>

View File

@ -22,7 +22,7 @@ $('.streamer-picture form.upload input[type=file]').on('change', function() {
) {
main(cls := "streamer-picture small-page box")(
h1(xStreamerPicture(userLink(s.user))),
div(cls := "picture_wrap")(bits.pic(s.streamer, s.user, 250)),
div(cls := "picture_wrap")(bits.thumbnail(s.streamer, s.user)),
div(cls := "forms")(
error.map { badTag(_) },
postForm(
@ -30,14 +30,10 @@ $('.streamer-picture form.upload input[type=file]').on('change', function() {
enctype := "multipart/form-data",
cls := "upload"
)(
p(maxSize(s"${lila.db.Photographer.uploadMaxMb}MB.")),
p(maxSize(s"${lila.memo.PicfitApi.uploadMaxMb}MB.")),
form3.file.image("picture"),
submitButton(cls := "button")(uploadPicture())
),
s.streamer.hasPicture option
postForm(action := routes.Streamer.pictureDelete, cls := "delete")(
submitButton(cls := "button button-red")(deletePicture())
),
div(cls := "cancel")(
a(href := routes.Streamer.edit, cls := "text", dataIcon := "")(trans.cancel())
)

View File

@ -25,7 +25,7 @@ object show {
shorten(~(s.streamer.headline.map(_.value) orElse s.streamer.description.map(_.value)), 152),
url = s"$netBaseUrl${routes.Streamer.show(s.user.username)}",
`type` = "video",
image = s.streamer.picturePath.map(p => dbImageUrl(p.value))
image = s.streamer.hasPicture option bits.thumbnail.url(s.streamer)
)
.some,
csp = defaultCsp.finalizeWithTwitch.some

View File

@ -248,7 +248,6 @@ POST /streamer/edit controllers.Streamer.editApply
POST /streamer/approval/request controllers.Streamer.approvalRequest
GET /streamer/picture/edit controllers.Streamer.picture
POST /upload/image/streamer controllers.Streamer.pictureApply
POST /streamer/picture/delete controllers.Streamer.pictureDelete
GET /streamer/:username controllers.Streamer.show(username: String)
GET /streamer/:username/redirect controllers.Streamer.redirect(username: String)

View File

@ -25,6 +25,7 @@ final class Env(
settingStore: lila.memo.SettingStore.Builder,
isOnline: lila.socket.IsOnline,
cacheApi: lila.memo.CacheApi,
picfitApi: lila.memo.PicfitApi,
notifyApi: lila.notify.NotifyApi,
userRepo: lila.user.UserRepo,
timeline: lila.hub.actors.Timeline,

View File

@ -2,13 +2,14 @@ package lila.streamer
import org.joda.time.DateTime
import lila.memo.{ PicfitImage, PicfitUrl }
import lila.user.User
case class Streamer(
_id: Streamer.Id, // user ID
listed: Streamer.Listed,
approval: Streamer.Approval,
picturePath: Option[Streamer.PicturePath],
picture: Option[PicfitImage.Id],
name: Streamer.Name,
headline: Option[Streamer.Headline],
description: Option[Streamer.Description],
@ -26,7 +27,7 @@ case class Streamer(
def is(user: User) = userId == user.id
def hasPicture = picturePath.isDefined
def hasPicture = picture.isDefined
def isListed = listed.value && approval.granted
@ -49,7 +50,7 @@ object Streamer {
chatEnabled = true,
lastGrantedAt = none
),
picturePath = none,
picture = none,
name = Name(user.realNameOrUsername),
headline = none,
description = none,

View File

@ -7,13 +7,14 @@ import scala.concurrent.duration._
import lila.db.dsl._
import lila.db.Photographer
import lila.memo.CacheApi._
import lila.memo.PicfitApi
import lila.user.{ User, UserRepo }
final class StreamerApi(
coll: Coll,
userRepo: UserRepo,
cacheApi: lila.memo.CacheApi,
photographer: Photographer,
picfitApi: PicfitApi,
notifyApi: lila.notify.NotifyApi
)(implicit ec: scala.concurrent.ExecutionContext) {
@ -116,13 +117,14 @@ final class StreamerApi(
def isActualStreamer(user: User): Fu[Boolean] =
isPotentialStreamer(user) >>& !isCandidateStreamer(user)
def uploadPicture(s: Streamer, picture: Photographer.Uploaded, by: User): Funit =
photographer(s.id.value, picture, createdBy = by.id).flatMap { pic =>
coll.update.one($id(s.id), $set("picturePath" -> pic.path)).void
def uploadPicture(s: Streamer, picture: PicfitApi.Uploaded, by: User): Funit = {
picfitApi
.upload(imageRel(by), picture, userId = by.id) flatMap { pic =>
coll.update.one($id(s.id), $set("picture" -> pic.id)).void
}
}.logFailure(logger branch "upload")
def deletePicture(s: Streamer): Funit =
coll.update.one($id(s.id), $unset("picturePath")).void
private def imageRel(user: User) = s"streamer:${user.id}"
// unapprove after a week if you never streamed
def autoDemoteFakes: Funit =

View File

@ -59,10 +59,6 @@ $mq-picture: $mq-medium;
.picture {
flex: 0 0 300px;
&.default {
opacity: 0.3;
}
display: none;
@include breakpoint($mq-picture) {