diff options
author | Volpeon <git@volpeon.ink> | 2021-03-10 19:27:04 +0100 |
---|---|---|
committer | Volpeon <git@volpeon.ink> | 2021-03-10 19:27:04 +0100 |
commit | 61d37c1ed5818e7f5d53f358495e5349ae817702 (patch) | |
tree | 4f289beaed9386203204c28f142b6726721012c2 | |
download | livestream-irc-61d37c1ed5818e7f5d53f358495e5349ae817702.tar.gz livestream-irc-61d37c1ed5818e7f5d53f358495e5349ae817702.tar.bz2 livestream-irc-61d37c1ed5818e7f5d53f358495e5349ae817702.zip |
Init
-rw-r--r-- | index.html | 71 | ||||
-rw-r--r-- | robots.txt | 2 | ||||
-rw-r--r-- | script.js | 69 | ||||
-rw-r--r-- | style.css | 224 |
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 @@ | |||
1 | User-agent: * | ||
2 | Disallow: / | ||
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 | |||
31 | html { | ||
32 | height: 100%; | ||
33 | } | ||
34 | |||
35 | body { | ||
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 | |||
44 | a { | ||
45 | text-decoration: none; | ||
46 | } | ||
47 | |||
48 | p { | ||
49 | margin: 0; | ||
50 | } | ||
51 | |||
52 | iframe { | ||
53 | display: block; | ||
54 | width: 100%; | ||
55 | height: 100%; | ||
56 | } | ||
57 | |||
58 | input { | ||
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 | |||
67 | input:hover, | ||
68 | input:focus { | ||
69 | border-color: var(--color--yellow); | ||
70 | box-shadow: none; | ||
71 | outline: 0; | ||
72 | } | ||
73 | |||
74 | button { | ||
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 | |||
88 | button:hover, | ||
89 | button: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; | ||