Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

aaarmstark's pinned streamer image link #16623

Merged
merged 8 commits into from
Dec 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 9 additions & 8 deletions app/controllers/RelayRound.scala
Original file line number Diff line number Diff line change
Expand Up @@ -268,20 +268,21 @@ final class RelayRound(
isSubscribed <- ctx.me.soFu: me =>
env.relay.api.isSubscribed(rt.tour.id, me.userId)
videoUrls <- embed match
case VideoEmbed.Auto =>
fuccess:
rt.tour.pinnedStream
.ifFalse(rt.round.isFinished)
.flatMap(_.upstream)
.map(_.urls(netDomain).toPair)
case VideoEmbed.No => fuccess(none)
case VideoEmbed.Stream(userId) =>
env.streamer.api
.find(userId)
.flatMapz(s => env.streamer.liveStreamApi.of(s).dmap(some))
.map:
_.flatMap(_.stream).map(_.urls(netDomain).toPair)
crossSiteIsolation = videoUrls.isEmpty
case VideoEmbed.PinnedStream =>
fuccess:
rt.tour.pinnedStream
.ifFalse(rt.round.isFinished)
.flatMap(_.upstream)
.map(_.urls(netDomain).toPair)
case _ => fuccess(none)
crossSiteIsolation = videoUrls.isEmpty || (rt.tour.pinnedStream.isDefined && crossOriginPolicy
.supportsCredentiallessIFrames(ctx.req))
data = env.relay.jsonView.makeData(
rt.tour.withRounds(rounds.map(_.round)),
rt.round.id,
Expand Down
8 changes: 6 additions & 2 deletions modules/relay/src/main/JsonView.scala
Original file line number Diff line number Diff line change
Expand Up @@ -128,8 +128,12 @@ final class JsonView(baseUrl: BaseUrl, markup: RelayMarkup, picfitUrl: PicfitUrl
.add("lcc", trs.rounds.find(_.id == currentRoundId).map(_.sync.upstream.exists(_.hasLcc)))
.add("isSubscribed" -> isSubscribed)
.add("videoUrls" -> videoUrls)
.add("pinnedStream" -> pinned)
.add("note" -> trs.tour.note.ifTrue(canContribute)),
.add("note" -> trs.tour.note.ifTrue(canContribute))
.add("pinned" -> pinned.map: p =>
Json
.obj("name" -> p.name)
.add("redirect" -> p.upstream.map(_.urls(lila.core.config.NetDomain("")).redirect))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NetDomain("") 😒

.add("text" -> p.text)),
study = studyData.study,
analysis = studyData.analysis,
group = group.map(_.group.name)
Expand Down
19 changes: 8 additions & 11 deletions modules/relay/src/main/RelayPinnedStream.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,19 @@ import io.mola.galimatias.URL
import scala.jdk.CollectionConverters.*
import lila.core.config.NetDomain

case class RelayPinnedStream(name: String, url: URL):
case class RelayPinnedStream(name: String, url: URL, text: Option[String]):

import RelayPinnedStream.*

def upstream: Option[RelayPinnedStream.Upstream] =
parseYoutube.orElse(parseTwitch)

// https://www.youtube.com/live/Lg0askmGqvo
// https://www.youtube.com/live/Lg0askmGqvo?si=KKOexnmA2xPcyStZ
def parseYoutube: Option[YouTube] =
url.host.toString
.endsWith("youtube.com")
.so:
url.pathSegments.asScala.toList match
case List("live", id) => YouTube(id).some
case _ => none
if List("www.youtube.com", "youtube.com", "youtu.be").contains(url.host.toString) then
url.pathSegments.asScala.toList match
case List("live", id) => Some(YouTube(id))
case _ => Option(url.queryParameter("v")).map(YouTube.apply)
else None

// https://www.twitch.tv/tcec_chess_tv
def parseTwitch: Option[Twitch] =
Expand All @@ -37,11 +34,11 @@ object RelayPinnedStream:
def urls(parent: NetDomain): Urls
case class YouTube(id: String) extends Upstream:
def urls(parent: NetDomain) = Urls(
s"https://www.youtube.com/embed/${id}?disablekb=1&modestbranding=1",
s"https://www.youtube.com/embed/${id}?disablekb=1&modestbranding=1&autoplay=1",
s"https://www.youtube.com/watch?v=${id}"
)
case class Twitch(id: String) extends Upstream:
def urls(parent: NetDomain) = Urls(
s"https://player.twitch.tv/?channel=${id}&parent=${parent}",
s"https://player.twitch.tv/?channel=${id}&parent=${parent}&autoplay=true",
s"https://www.twitch.tv/${id}"
)
4 changes: 3 additions & 1 deletion modules/relay/src/main/RelayTourForm.scala
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,9 @@ final class RelayTourForm(langList: lila.core.i18n.LangList):

private val pinnedStreamMapping = mapping(
"name" -> cleanNonEmptyText(maxLength = 100),
"url" -> url.field.verifying("Invalid stream URL", url => RelayPinnedStream("", url).upstream.isDefined)
"url" -> url.field
.verifying("Invalid stream URL", url => RelayPinnedStream("", url, None).upstream.isDefined),
"text" -> optional(cleanText(maxLength = 100))
)(RelayPinnedStream.apply)(unapply)

private given Formatter[RelayTour.Tier] =
Expand Down
10 changes: 5 additions & 5 deletions modules/relay/src/main/RelayVideoEmbed.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ import play.api.mvc.Result
enum RelayVideoEmbed:
case No
case Auto
case PinnedStream
case Stream(userId: UserId)
override def toString = this match
case No => "no"
case Auto => ""
case Stream(u) => u.toString
case No => "no"
case _ => ""

final class RelayVideoEmbedStore(baker: LilaCookie):

Expand All @@ -21,11 +21,11 @@ final class RelayVideoEmbedStore(baker: LilaCookie):
def read(using req: RequestHeader): RelayVideoEmbed =
def fromCookie = req.cookies.get(cookieName).map(_.value).filter(_.nonEmpty) match
case Some("no") => No
case Some("ps") => PinnedStream
case _ => Auto
req.queryString.get("embed") match
case Some(Nil) => fromCookie
case Some(Seq("")) => Auto
case Some(Seq("no")) => No
case Some(Seq("ps")) => PinnedStream
case Some(Seq(name)) => UserStr.read(name).fold(Auto)(u => Stream(u.id))
case _ => fromCookie

Expand Down
11 changes: 11 additions & 0 deletions modules/relay/src/main/ui/RelayFormUi.scala
Original file line number Diff line number Diff line change
Expand Up @@ -645,6 +645,17 @@ Team Dogs ; Scooby Doo"""),
"Stream name",
half = true
)(form3.input(_))
),
form3.split(
form3.group(
form("pinnedStream.text"),
"Stream link label",
help = frag(
"Optional. Show a label on the image link to your live stream.",
br,
"Example: 'Watch us live on YouTube!'"
).some
)(form3.input(_))
)
)
)
Expand Down
2 changes: 1 addition & 1 deletion ui/analyse/css/study/relay/_layout.scss
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ body {
main.is-relay {
.relay-tour {
grid-area: relay-tour;
overflow: visible;
&__side {
grid-area: side;
}
Expand All @@ -22,7 +23,6 @@ main.is-relay {
}

@include mq-at-least-col3 {
#video-player-placeholder,
button.streamer-show {
display: block;
}
Expand Down
5 changes: 4 additions & 1 deletion ui/analyse/css/study/relay/_tour.scss
Original file line number Diff line number Diff line change
Expand Up @@ -117,10 +117,13 @@ $hover-bg: $m-primary_bg--mix-30;
flex: 0 0 50%;
}
line-height: 0;
img {
> img {
width: 100%;
@include broken-img(2 / 1);
}
.video-player-close {
display: none;
}
text-align: center;
}
&__image-upload {
Expand Down
91 changes: 87 additions & 4 deletions ui/analyse/css/study/relay/_video-player.scss
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,99 @@

#video-player-placeholder {
aspect-ratio: 16/9;
position: relative;
width: 100%;
}

img.video-player-close {
.video-player-close {
z-index: $z-video-player-controls-101;
position: absolute;
height: 20px;
width: 20px;
pointer-events: auto;
top: 6px;
right: 6px;
height: 24px;
width: 24px;
padding: 2px;
border-radius: 50%;
background-color: black;
cursor: pointer;
&:hover {
filter: brightness(10);
filter: brightness(3);
}
}

#video-player-placeholder.link {
cursor: pointer;
overflow: hidden;
outline-offset: -3px;
outline: 3px solid $m-bad--alpha-50;

.image {
position: absolute;
background: center / cover;
overflow: hidden;
inset: 0;
filter: blur(4px) brightness(0.7);
}

.play-button {
position: absolute;
pointer-events: none;
transform: translate(-50%, -50%);
top: 50%;
left: 50%;
width: 18%;
opacity: 0.6;
fill: white;
filter: drop-shadow(0 0 12px #0000004f);

circle {
filter: drop-shadow(0 0 8px #840000);
paint-order: stroke fill;
stroke: #fff9;
stroke-width: 3px;
fill: $c-bad;
}
}

&:has(.text-box) .play-button {
top: 56%;
}

.text-box {
@extend %flex-column;
position: absolute;
pointer-events: none;
justify-content: center;
align-items: center;
top: 10%;
left: 10%;
right: 10%;
}

.text-box div {
margin: auto;
pointer-events: none;
border-radius: 5px;
border: 1px solid #8888;
padding: 5px 8px;
text-align: center;
line-height: normal;
color: #ddde;
background-color: #333d;
font-family: 'Noto Sans';
font-size: 1.2em;
}

&:hover:not(:has(.video-player-close:hover)) {
box-shadow: 0 0 12px $c-bad;

.play-button {
opacity: 1;
}

.image {
filter: blur(4px) brightness(0.6);
}
}
}
5 changes: 2 additions & 3 deletions ui/analyse/src/analyse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,8 @@ import { wsConnect } from 'common/socket';

export { patch };

export const start = makeStart(patch);

export const boot = makeBoot(start);
const start = makeStart(patch);
const boot = makeBoot(start);

export function initModule({ mode, cfg }: { mode: 'userAnalysis' | 'replay'; cfg: any }) {
if (mode === 'replay') boot(cfg);
Expand Down
2 changes: 2 additions & 0 deletions ui/analyse/src/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,8 @@ export interface AnalyseOpts {
inlinePgn?: string;
externalEngineEndpoint: string;
embed?: boolean;
socketUrl?: string;
socketVersion?: number;
}

export interface JustCaptured extends Piece {
Expand Down
12 changes: 6 additions & 6 deletions ui/analyse/src/plugins/analyse.study.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import { patch } from '../view/util';
import makeBoot from '../boot';
import makeStart from '../start';
import type { AnalyseOpts } from '../interfaces';
import type { AnalyseSocketSend } from '../socket';
import * as studyDeps from '../study/studyDeps';
import { wsConnect } from 'common/socket';

export { patch };

export const start = makeStart(patch, studyDeps);
export const boot = makeBoot(start);
const start = makeStart(patch, studyDeps);

export function initModule(cfg: any) {
cfg.socketSend = wsConnect(cfg.socketUrl || '/analysis/socket/v5', cfg.socketVersion, {
export function initModule(cfg: AnalyseOpts) {
cfg.socketSend = wsConnect(cfg.socketUrl || '/analysis/socket/v5', cfg.socketVersion ?? false, {
receive: (t: string, d: any) => analyse.socketReceive(t, d),
...(cfg.embed ? { params: { flag: 'embed' } } : {}),
}).send;
}).send as AnalyseSocketSend;
const analyse = start(cfg);
}
2 changes: 1 addition & 1 deletion ui/analyse/src/study/relay/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export interface RelayData {
group?: RelayGroup;
isSubscribed?: boolean; // undefined if anon
videoUrls?: [string, string];
pinnedStream?: { name: string; youtube?: string; twitch?: string };
pinned?: { name: string; redirect: string; text?: string };
note?: string;
lcc?: boolean;
}
Expand Down
36 changes: 25 additions & 11 deletions ui/analyse/src/study/relay/relayCtrl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { RelayData, LogEvent, RelaySync, RelayRound, RoundId } from './inte
import type { BothClocks, ChapterId, ChapterSelect, Federations, ServerClockMsg } from '../interfaces';
import type { StudyMemberCtrl } from '../studyMembers';
import type { AnalyseSocketSend } from '../../socket';
import { type Prop, type Toggle, defined, myUserId, notNull, prop, toggle } from 'common';
import { type Prop, type Toggle, myUserId, notNull, prop, toggle } from 'common';
import RelayTeams from './relayTeams';
import RelayPlayers from './relayPlayers';
import type { StudyChapters } from '../studyChapters';
Expand Down Expand Up @@ -59,19 +59,27 @@ export default class RelayCtrl {
redraw,
);
this.stats = new RelayStats(this.currentRound(), redraw);
this.videoPlayer = this.data.videoUrls?.[0] ? new VideoPlayer(this.data.videoUrls[0], redraw) : undefined;
setInterval(() => this.redraw(true), 1000);

const pinned = data.pinnedStream;
if (pinned && this.pinStreamer()) this.streams.push(['', pinned.name]);
if (data.videoUrls?.[0] || this.isPinnedStreamOngoing())
this.videoPlayer = new VideoPlayer(
{
embed: this.data.videoUrls?.[0] || false,
redirect: this.data.videoUrls?.[1] || this.data.pinned?.redirect,
image: this.data.tour.image,
text: this.data.pinned?.text,
},
redraw,
);
const pinnedName = this.isPinnedStreamOngoing() && data.pinned?.name;
if (pinnedName) this.streams.push(['ps', pinnedName]);

pubsub.on('socket.in.crowd', d => {
const s = (d.streams as [string, string][]) ?? [];
if (pinned && this.pinStreamer()) s.unshift(['', pinned.name]);
if (pinnedName) s.unshift(['ps', pinnedName]);
if (this.streams.length === s.length && this.streams.every(([id], i) => id === s[i][0])) return;
this.streams = s;
this.redraw();
});
setInterval(() => this.redraw(true), 1000);
}

openTab = (t: RelayTab) => {
Expand Down Expand Up @@ -131,10 +139,16 @@ export default class RelayCtrl {

isStreamer = () => this.streams.some(([id]) => id === myUserId());

pinStreamer = () =>
defined(this.data.pinnedStream) &&
!this.currentRound().finished &&
Date.now() > this.currentRound().startsAt! - 1000 * 3600;
isPinnedStreamOngoing = () => {
if (!this.data.pinned) return false;
if (this.currentRound().finished) return false;
if (Date.now() < this.currentRound().startsAt! - 1000 * 3600) return false;
return true;
};

noEmbed() {
return document.cookie.includes('relayVideo=no');
}

private socketHandlers = {
relayData: (d: RelayData) => {
Expand Down
Loading
Loading