summaryrefslogtreecommitdiff
path: root/desk/web/tv/tv.hoon
diff options
context:
space:
mode:
Diffstat (limited to 'desk/web/tv/tv.hoon')
-rw-r--r--desk/web/tv/tv.hoon285
1 files changed, 285 insertions, 0 deletions
diff --git a/desk/web/tv/tv.hoon b/desk/web/tv/tv.hoon
new file mode 100644
index 0000000..5a3360a
--- /dev/null
+++ b/desk/web/tv/tv.hoon
@@ -0,0 +1,285 @@
+/- *boke
+/+ kaji, sr=sortug, lib=boke
+/= post-text /web/components/post-text
+|_ [s=state =bowl:gall]
+++ css ^~ %- trip
+'''
+#tv{
+ padding: 1rem;
+
+ & main{
+ margin-top: 0.5rem;
+ height: 50vw;
+ & #screen{
+ }
+ & #chat{
+ border: 1px solid var(--text-color);
+ h3{
+ height: 1.5rem;
+ margin: 0;
+ }
+
+ & #chat-container{
+ overflow-y: scroll;
+ max-height: calc(100% - 3.5rem);
+ height: calc(100% - 3.5rem);
+ .chat-msg{
+ padding: 0.5rem;
+ & .author{
+ opacity: 0.8;
+ font-family: Inter;
+ }
+ & .chat-content{
+ padding: 0.3rem 1rem;
+ & p{
+ margin: 0;
+ }
+ }
+ }
+ }
+ & form{
+ display: flex;
+ margin-bottom: 0;
+ border-top: 1px solid black;
+
+ & input{
+ border: none;
+ border-radius: 0;
+ }
+
+ & input[type=text]{
+ width: 100%;
+ height: 2rem;
+ }
+ & input[type=submit]{
+ padding: 0.2rem;
+ }
+ }
+ }
+ }
+ & #radio-list{
+ overflow-y: scroll;
+ max-height: 70vh;
+ & .entry{
+ cursor: pointer;
+ }
+ }
+}
+#video{
+ width: 100%;
+ background-color: black;
+}
+.tabs{
+ display: flex;
+ gap: 1rem;
+ & .tab{
+ cursor: pointer;
+ }
+}
+@media (min-width: 1200px){
+#tv main{
+ display: flex;
+ align-items: stretch;
+ gap: 1rem;
+
+}
+#screen{
+ width: 75%;
+}
+#chat{
+ width: 25%;
+}
+@media (max-width: 1200px){
+#screen{
+ width: 100%;
+}
+#chat{
+ width: 25%;
+}
+
+}
+.movie-name{
+ font-size: 1.2rem;
+ font-weight: 500;
+}
+'''
+++ script
+|= sub-path=tape
+^~ %+ weld %- trip
+'''
+function initVideo(videoSrc){
+ const video = document.getElementById('video');
+ video.autoplay = true;
+ console.log(videoSrc, "starting video")
+
+ if (Hls.isSupported()) {
+ var hls = new Hls();
+ hls.loadSource(videoSrc);
+ hls.attachMedia(video);
+ hls.on(Hls.Events.MANIFEST_LOADED, function (event, data) {
+ console.log("manifest loaded", [event, data])
+ });
+ hls.on(Hls.Events.MANIFEST_LOADED, function (event, data) {
+ console.log("manifest loaded", [event, data])
+ });
+ hls.on(Hls.Events.FRAG_LOADED, function (event, data) {
+ console.log("frag loaded", [event, data])
+ });
+ hls.on(Hls.Events.LEVEL_LOADED, function (event, data) {
+ console.log("level loaded", [event, data])
+ });
+ hls.on(Hls.Events.SUBTITLE_TRACK_SWITCH, function (event, data) {
+ console.log("sub switch", [event, data])
+ });
+ hls.on(Hls.Events.SUBTITLE_TRACK_LOADED, function (event, data) {
+ console.log("sub loaded", [event, data])
+ });
+ hls.on(Hls.Events.MANIFEST_PARSED, function (event, data) {
+ console.log("manifest parsed", [event, data])
+ video.play();
+ });
+ }
+ // HLS.js is not supported on platforms that natively support HLS
+ else if (video.canPlayType('application/vnd.apple.mpegurl')) {
+ video.src = videoSrc;
+ video.addEventListener('loadedmetadata', function () {
+ video.play();
+ });
+ }
+
+}
+document.addEventListener('DOMContentLoaded', () => {
+ const src = 'https://hydrogen.finnem.net/hls/live.m3u8';
+ //const src = 'https://hls.urbit.cam/output.m3u8';
+ initVideo(src);
+ const room = document.getElementById("chat-container");
+ if (room) room.scrollTop = room.scrollHeight
+});
+
+document.addEventListener('kaji-fact', (event) => {
+ console.log(event.detail, "kaji fact")
+ if (event.detail.event === "scroll"){
+ const room = document.getElementById("chat-container");
+ if (room) setTimeout(() => room.scrollTop = room.scrollHeight, 50)
+ }
+ else if ("change-radio" in event.detail)
+ initVideo(event.detail["change-radio"]);
+});
+
+'''
+"""
+subscribe('{sub-path}');
+
+"""
+++ main
+|= [symbol=@tas =twr] ^- manx
+ =/ author (scow %p owner.twr)
+ =/ name (trip symbol)
+ =/ sub-path "/tv/{(trip -.twr)}/{name}"
+ ~& sub-path=sub-path
+;div#tv
+ ;style: {css}
+ ;main
+ ;div#screen
+ ;video#video
+ =controls ""
+ ;
+ ==
+ ;+ (broadcast-metadata twr)
+ ==
+ ;+ (chat-box symbol twr)
+ ==
+ :: ;+ rl
+ ;script: {(script sub-path)}
+ ;script@"https://cdn.jsdelivr.net/npm/hls.js@latest";
+==
+++ broadcast-metadata
+|= =twr ^- manx
+ =/ author (scow %p owner.twr)
+ =/ stat-name (trip name.twr)
+ ?: ?=(%our -.twr)
+ =/ desc (trip description.tv.twr)
+ ;div.meta
+ ;div.movie-name:"{desc}"
+ ;div.movie-from:"from {stat-name} by {author}"
+ ==
+ ::
+ =/ desc (trip description.p.twr)
+ ;div.meta
+ ;div:"{desc}"
+ ;div:"by {author}"
+ ==
+
+++ rl ^- manx
+ ;div#radio-list
+ ;div.tabs
+ =kaji "scry"
+ =swap "swap"
+ =targ "#rlist"
+ ;div.tab
+ =path "/tv/f/s"
+ ; Spandrell TV
+ ==
+ ;div.tab
+ =path "/tv/f/u"
+ ; Urbit Radio
+ ==
+ ==
+ ;div#rlist
+ ;* tv-list
+ ==
+ ==
+++ radio-list ^- marl
+ %+ turn ~(tap by urbit.tv.s) |= [p=@p q=urbit-radio]
+ ;div.entry
+ =kaji "poke"
+ =action "change-radio"
+ =payload (enc:kaji p)
+ ;p:"{(trip description.q)}"
+ ;p:"by {(scow %p p)}"
+ ==
+++ tv-list ^- marl
+ %+ turn ~(tap by here.tv.s) |= [p=@t q=bstv]
+ ;div.entry
+ =kaji "poke"
+ =action "change-nradio"
+ =payload (trip p)
+ ;p:"{(trip playing.current.q)}"
+ ;p:"by {(scow %p owner.q)}"
+ ==
+
+++ chat-box
+|= [symbol=@tas =twr]
+?: ?=(%urb -.twr) ;div;
+;div#chat
+ ;h3.tc.bb:"Chat"
+ ;div#chat-container
+ ;* %+ turn (flop chat.tv.twr) chat-msg
+ ==
+ ;form
+ =id "chat-composer"
+ =kaji "poke"
+ =action "add-radio-chat"
+ =wipe "yes"
+ ;input#text-input(type "text", name "input", placeholder "Nice", autocomplete "off");
+ ;input(type "hidden", name "type", value (trip -.twr));
+ ;input(type "hidden", name "owner", value (scow %p owner.twr));
+ ;input(type "hidden", name "name", value (trip symbol));
+ ;input(type "submit", value "⇛");
+ ==
+
+==
+++ chat-msg
+|= p=post:tp
+:: =/ usr (user author.p whoms 32)
+ =/ author ?: (is-anon:lib author.p) "anon" (scow %p author.p)
+;div.chat-msg
+ ;div.f1
+ ;div.author:"{author}:"
+ ;div.time:"{(time-to-tape:string:sr id.p)}"
+ ==
+ ;div.chat-content
+ ;* (content:post-text contents.p)
+ ==
+==
+--