upload streamer images to picfit
parent
d095ecd1df
commit
4a95e0e3ee
|
@ -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)
|
||||
|
|
|
@ -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")(
|
||||
|
|
|
@ -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")(
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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 =>
|
||||
|
|
|
@ -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())
|
||||
)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 =
|
||||
|
|
|
@ -59,10 +59,6 @@ $mq-picture: $mq-medium;
|
|||
.picture {
|
||||
flex: 0 0 300px;
|
||||
|
||||
&.default {
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
display: none;
|
||||
|
||||
@include breakpoint($mq-picture) {
|
||||
|
|
Loading…
Reference in New Issue