summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--index.html71
-rw-r--r--robots.txt2
-rw-r--r--script.js69
-rw-r--r--style.css224
4 files changed, 366 insertions, 0 deletions
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..01e48f8
--- /dev/null
+++ b/index.html
@@ -0,0 +1,71 @@
1<!DOCTYPE html>
2<html lang="en">
3
4<head>
5 <meta charset="utf-8" />
6 <meta name="viewport" content="width=device-width, initial-scale=1" />
7 <meta name="robots" content="noindex" />
8
9 <title>Livestream IRC</title>
10
11 <link rel="stylesheet" href="style.css" />
12</head>
13
14<body>
15 <section class="c-section c-section--create">
16 <form class="c-form" action="index.html" method="GET">
17 <label class="c-form-field">
18 <div class="c-form-field__label">Title</div>
19 <input class="c-form-field__input" name="title" />
20 <div class="c-form-field__help">
21 Optional title for your stream page.
22 </div>
23 </label>
24 <label class="c-form-field">
25 <div class="c-form-field__label">Stream embed URL</div>
26 <input class="c-form-field__input" name="stream" />
27 <div class="c-form-field__help">
28 The URL of the stream you want to embed.
29 Make sure you use an embed URL so only the player is displayed instead of the whole website.
30 In case of PeerTube, replace the "/watch/" part in the URL with "/embed/".
31 </div>
32 </label>
33 <label class="c-form-field">
34 <div class="c-form-field__label">IRC channel URL</div>
35 <input class="c-form-field__input" name="irc" />
36 <div class="c-form-field__help">
37 The URL pointing to a channel on an IRC server.
38 It has the format "ircs://chat.server/channel", where "chat.server" is the IRC server
39 and "channel" the name of the channel without the leading #.
40 Use "irc://..." if the server does not support TLS.
41 </div>
42 </label>
43 <button class="c-form-field">Create</button>
44 </form>
45 </section>
46
47 <section class="c-section c-section--main u-hidden">
48 <div class="c-msg">
49 <p class="c-msg__content">
50 <a class="c-irc-link" href="ircs://irc.vulpes.one:6697/livestream">Join #livestream on irc.vulpes.one</a>
51 </p>
52 <button class="c-msg__close">
53 <div class="c-close-icon"></div>
54 </button>
55 </div>
56
57 <div class="c-frame c-frame--pt">
58 <iframe width="560" height="315" sandbox="allow-same-origin allow-scripts allow-popups" frameborder="0"
59 allowfullscreen></iframe>
60 </div>
61
62 <div class="c-frame c-frame--irc">
63 <iframe width="560" height="315" sandbox="allow-same-origin allow-scripts allow-popups allow-forms"
64 frameborder="0"></iframe>
65 </div>
66 </section>
67
68 <script type=text/javascript src="script.js"></script>
69</body>
70
71</html>
diff --git a/robots.txt b/robots.txt
new file mode 100644
index 0000000..1f53798
--- /dev/null
+++ b/robots.txt
@@ -0,0 +1,2 @@
1User-agent: *
2Disallow: /
diff --git a/script.js b/script.js
new file mode 100644
index 0000000..e2c8f36
--- /dev/null
+++ b/script.js
@@ -0,0 +1,69 @@
1(() => {
2 const queryString = window.location.search;
3 const urlParams = new URLSearchParams(queryString);
4
5 const title = urlParams.get("title");
6 const streamUrl = urlParams.get("stream");
7 const ircUrl = urlParams.get("irc");
8
9 if (streamUrl === null || ircUrl === null) {
10 return;
11 }
12
13 /** @type HTMLInputElement */
14 const titleEl = document.querySelector(".c-form-field__input[name=title]");
15 /** @type HTMLInputElement */
16 const streamEl = document.querySelector(".c-form-field__input[name=stream]");
17 /** @type HTMLInputElement */
18 const ircEl = document.querySelector(".c-form-field__input[name=irc]");
19
20 titleEl.value = title;
21 streamEl.value = streamUrl;
22 ircEl.value = ircUrl;
23
24 if (streamUrl.trim().length === 0 || ircUrl.trim().length === 0) {
25 return;
26 }
27
28 const normalizedircUrl = ircUrl.startsWith("irc://")
29 ? ircUrl.substring(6)
30 : ircUrl.startsWith("ircs://")
31 ? ircUrl.substring(7)
32 : ircUrl;
33 const ircUrlComponents = normalizedircUrl.split("/");
34
35 if (ircUrlComponents.length !== 2) {
36 return;
37 }
38
39 if (title !== null && title.trim().length !== 0) {
40 document.querySelector("title").textContent = title;
41 }
42
43 const ircChan = ircUrlComponents[0];
44 const ircHost = ircUrlComponents[1];
45
46 document.querySelector(".c-section--create").classList.add("u-hidden");
47 document.querySelector(".c-section--main").classList.remove("u-hidden");
48
49 /** @type HTMLIFrameElement */
50 const streamFrameEl = document.querySelector(".c-frame--pt iframe");
51 /** @type HTMLIFrameElement */
52 const ircFrameEl = document.querySelector(".c-frame--irc iframe");
53
54 streamFrameEl.src = streamUrl;
55 ircFrameEl.src = "https://irc.vulpes.one/?uri=" + ircUrl;
56
57 /** @type HTMLAnchorElement */
58 const ircLink = document.querySelector(".c-irc-link");
59 ircLink.href = ircUrl;
60 ircLink.textContent = `Join #${ircChan} on ${ircHost}`;
61
62 document.querySelector(".c-msg__close").addEventListener("click", (e) => {
63 e.preventDefault();
64
65 /** @type HTMLElement */
66 const el = e.currentTarget;
67 el.parentElement.remove();
68 });
69})();
diff --git a/style.css b/style.css
new file mode 100644
index 0000000..6be7ed3
--- /dev/null
+++ b/style.css
@@ -0,0 +1,224 @@
1
2
3:root {
4 --gray0: hsl(270, 0%, 7%);
5 --gray1: hsl(270, 0%, 10%);
6 --gray2: hsl(270, 1%, 16%);
7 --gray3: hsl(270, 1%, 24%);
8 --gray4: hsl(270, 1%, 35%);
9 --gray5: hsl(270, 2%, 54%);
10 --gray6: hsl(270, 2%, 73%);
11 --gray7: hsl(270, 2%, 100%);
12
13 /* * */
14
15 --bg-hi: var(--gray0);
16 --bg: var(--gray1);
17
18 --obj-hi: var(--gray2);
19 --obj: var(--gray3);
20 --obj-lo: var(--gray4);
21
22 --fg-hi: var(--gray5);
23 --fg: var(--gray6);
24 --fg-lo: var(--gray7);
25
26 /* * */
27
28 --color--yellow: hsl(38, 100%, 76%);
29}
30
31html {
32 height: 100%;
33}
34
35body {
36 height: 100%;
37 margin: 0;
38 font-family: IBM Plex Sans, -apple-system, system-ui, Segoe UI, Roboto, Ubuntu, Cantarell, Noto Sans, sans-serif, BlinkMacSystemFont, Helvetica Neue, Arial;
39 line-height: 1.5;
40 background-color: var(--bg);
41 color: var(--fg);
42}
43
44a {
45 text-decoration: none;
46}
47
48p {
49 margin: 0;
50}
51
52iframe {
53 display: block;
54 width: 100%;
55 height: 100%;
56}
57
58input {
59 padding: .5em .7em;
60 background: var(--obj);
61 color: var(--fg-lo);
62 border: 2px solid transparent;
63 border-radius: 3px;
64 transition: border-color .2s;
65}
66
67input:hover,
68input:focus {
69 border-color: var(--color--yellow);
70 box-shadow: none;
71 outline: 0;
72}
73
74button {
75 padding: .6em 1.3em;
76 font: inherit;
77 font-size: 12px;
78 font-weight: bold;
79 text-transform: uppercase;
80 letter-spacing: .2em;
81 background: transparent;
82 color: var(--color--yellow);
83 border: 2px solid var(--color--yellow);
84 border-radius: 3px;
85 transition: background-color .2s, color .2s;
86}
87
88button:hover,
89button:focus {
90 background-color: var(--color--yellow);
91 color: #000;
92}
93
94:link,
95:visited {
96 color: #850000;
97}
98
99.c-section {
100 height: 100%;
101}
102
103.c-section--main {
104 display: grid;
105 grid-template-areas: "video msg" "video chat";
106 grid-template-rows: auto 1fr;
107 grid-template-columns: 1fr 22em;
108}
109
110.c-form {
111 display: block;
112 padding: 2rem;
113 margin: 0 auto;
114 max-width: 600px;
115}
116
117.c-form-msg:empty {
118 display: none;
119}
120
121.c-form-field {
122 display: block;
123 margin-top: 2rem;
124}
125
126.c-form-field__label {
127 font-size: 15px;
128 font-weight: bold;
129 color: var(--fg-lo);
130}
131
132.c-form-field__help {
133 display: block;
134 margin-top: .5rem;
135 color: var(--fg-hi);
136 font-size: 15px;
137}
138
139.c-form-field__input {
140 box-sizing: border-box;
141 display: block;
142 width: 100%;
143 margin-top: .5rem;
144}
145
146.c-frame--pt {
147 grid-area: video;
148}
149
150.c-frame--irc {
151 grid-area: chat;
152}
153
154.c-msg {
155 grid-area: msg;
156 display: flex;
157 color: #850000;
158 background-color: #fcc;
159 font-size: 14px;
160 border: 1px solid #ffb8b8;
161}
162
163.c-msg__content {
164 display: block;
165 width: 100%;
166 padding: .75rem 1.25rem;
167}
168
169.c-close-icon {
170 width: 1.1em;
171 height: 2px;
172 position: relative;
173}
174
175.c-close-icon::before,
176.c-close-icon::after {
177 position: absolute;
178 left: 0;
179 display: block;
180 height: 2px;
181 content: '';
182 background-color: currentColor;
183 transform-origin: 50% 50%;
184}
185
186.c-close-icon::before {
187 top: 0;
188 width: 100%;
189 transform: rotate(45deg);
190 transition: background-color .3s ease;
191}
192
193.c-close-icon::after {
194 bottom: 0;
195 transform: rotate(-45deg);
196 width: 100%;
197 transition: background-color .3s ease;
198}
199
200.c-msg__close {
201 display: block;
202 padding: .75rem 1.25rem;
203 margin-left: auto;
204 background: transparent;
205 color: currentColor;
206 border: 0;
207}
208
209.c-msg__close:hover {
210 background: transparent;
211 color: currentColor;
212}
213
214.u-hidden {
215 display: none;
216}
217
218@media (max-width: 1024px) {
219 .c-section--main {
220 grid-template-areas: "msg" "video" "chat";
221 grid-template-rows: auto 1fr 22em;
222 grid-template-columns: 1fr;
223 }
224}