diff options
author | polwex <polwex@sortug.com> | 2025-06-27 22:53:52 +0700 |
---|---|---|
committer | polwex <polwex@sortug.com> | 2025-06-27 22:53:52 +0700 |
commit | 328ebe85135912678bdacd3381126ffd66ef2761 (patch) | |
tree | 365962bf45302f2a440f766a4f3c9e0a962dbe47 /desk/web/tv |
init
Diffstat (limited to 'desk/web/tv')
-rw-r--r-- | desk/web/tv/router.hoon | 39 | ||||
-rw-r--r-- | desk/web/tv/tv.hoon | 285 |
2 files changed, 324 insertions, 0 deletions
diff --git a/desk/web/tv/router.hoon b/desk/web/tv/router.hoon new file mode 100644 index 0000000..6f4df21 --- /dev/null +++ b/desk/web/tv/router.hoon @@ -0,0 +1,39 @@ +/- boke, tp=trill-post, cnt=contact +/+ kaji, fetch-lib=fetch, plib=trill-utils, const=constants, sr=sortug, lib=boke +/= index /web/index +/= tvp /web/tv/tv +/= subscribe /web/subscribe +|_ [rl=req-line:kaji s=state:boke =bowl:gall] ++* fetch ~(. fetch-lib [s bowl]) + tv ~(. tvp [s bowl]) +++ eyre-bail (error-response:kaji 404) +++ manx-bail (error-page:kaji 404) +:: +++ $ ^- eyre-res:kaji + =/ p pat.rl ::?. mob.rl pat.rl [%m pat.rl] + :: ?. (is-subscribed:lib src.bowl) nudge + ?+ p eyre-bail + ~ main + [%f *] fragment + == +++ main + :- %page + =/ ut (~(get by here.tv.s) 'spandrell-tv') + ?~ ut manx-bail + =/ sta (~(get by schedule.u.ut) started.current.u.ut) + ?~ sta manx-bail :: TODO old station sorry + =/ page (main:tv ['spandrell-tv' %our 'Spandrell TV' ~docteg-mothep u.sta]) + (index ~[page] bowl) + +++ nudge + :- %page + =/ sub ~(. subscribe src.bowl) + =/ nudgep (nudge:sub "TV") + (index ~[nudgep] bowl) +++ fragment + :- %html + ?+ pat.rl manx-bail + [%f %s ~] ;div ;* tv-list:tv == + [%f %u ~] ;div ;* radio-list:tv == + == +-- 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) + == +== +-- |