diff options
-rw-r--r-- | dist/script.js | 184 | ||||
-rw-r--r-- | dist/seattle/index.html | 2 | ||||
-rw-r--r-- | dist/seattle/script.js | 229 | ||||
-rw-r--r-- | dist/seattle/sources.js | 1 | ||||
-rw-r--r-- | dist/utah/index.html | 2 | ||||
-rw-r--r-- | dist/utah/script.js | 166 | ||||
-rw-r--r-- | dist/utah/sources.js | 1 |
7 files changed, 108 insertions, 477 deletions
diff --git a/dist/script.js b/dist/script.js index 63415da..5c70acf 100644 --- a/dist/script.js +++ b/dist/script.js @@ -1,4 +1,4 @@ -/* global Hls, dragula, CAMERAS */ +/* global Hls, dragula, CAMERAS, DEFAULTS */ const STATE = loadState(); @@ -7,7 +7,6 @@ let HLSInstances = {}; function loadState() { let hash = document.location.hash; if (hash.length === 0) { - const DEFAULTS = ["CMR-0039", "CMR-0176", "CMR-0223", "CMR-0088", "CMR-0089", "CMR-0309", "CMR-0257"]; history.replaceState(null, "", "#" + DEFAULTS.join(",")); return DEFAULTS; } @@ -19,13 +18,14 @@ function loadState() { } function handleCheckbox(evt) { + const id = evt.target.dataset.id; if (evt.target.checked) { - if (!STATE.includes(evt.target.dataset.id)) { - appendCamera(evt.target.dataset.id); + if (!STATE.includes(id)) { + appendCamera(id); } } else { - if (STATE.includes(evt.target.dataset.id)) { - removeCamera(evt.target.dataset.id); + if (STATE.includes(id)) { + removeCamera(id); } } } @@ -49,7 +49,7 @@ function loadData() { const boxes = document.createElement("ul"); section.append(boxes); - for (let { id, stream, name } of CAMERAS[neighborhood]) { + for (let { id, name } of CAMERAS[neighborhood]) { const label = document.createElement("label"); const checkbox = document.createElement("input"); checkbox.type = "checkbox"; @@ -69,17 +69,17 @@ function loadData() { } for (let id of STATE) { - let { stream, name } = findCamera(id); - document.querySelector("main").append(makeCameraStream(id, stream, name)); + let camera = findCamera(id); + document.querySelector("main").append(makeCameraStream(camera)); } } -function makeCameraStream(id, stream, title) { - var section = document.createElement("section"); - section.dataset.id = id; - var header = document.createElement("h2"); - header.innerText = title; - var close = document.createElement("button"); +function makeCameraStream(camera) { + const section = document.createElement("section"); + section.dataset.id = camera.id; + const header = document.createElement("h2"); + header.innerText = camera.name; + const close = document.createElement("button"); close.className = 'close'; close.type = "button"; close.innerText = "X"; @@ -88,77 +88,85 @@ function makeCameraStream(id, stream, title) { removeCamera(evt.target.parentNode.parentNode.dataset.id); }); section.append(header); - var video = document.createElement("video"); - video.controls = true; - section.append(video); - if (Hls.isSupported()) { - var hls = new Hls({manifestLoadingTimeOut: 60000}); - hls.loadSource(stream); - hls.attachMedia(video); - hls.on(Hls.Events.MANIFEST_PARSED, function() { - video.play(); - }); - hls.on(Hls.Events.ERROR, function(event, data) { - if (data.fatal) { - switch (data.type) { - case Hls.ErrorTypes.NETWORK_ERROR: - // try to recover network error - if (data.response.code === 404) { + let video; + if (camera.stream !== undefined) { + video = document.createElement("video"); + video.controls = true; + if (Hls.isSupported()) { + const hls = new Hls({manifestLoadingTimeOut: 60000}); + hls.loadSource(camera.stream); + hls.attachMedia(video); + hls.on(Hls.Events.MANIFEST_PARSED, function () { + video.play(); + }); + hls.on(Hls.Events.ERROR, function (event, data) { + if (data.fatal) { + switch (data.type) { + case Hls.ErrorTypes.NETWORK_ERROR: + // try to recover network error + if (data.response.code === 404) { + hls.destroy(); + let errorMessage = document.createTextNode("Stream not found (probably doesn't exist after all)"); + video.parentNode.append(errorMessage); + video.remove(); + break; + } + console.log("fatal network error encountered, try to recover"); + console.log(data); + hls.startLoad(); + break; + case Hls.ErrorTypes.MEDIA_ERROR: + console.log("fatal media error encountered, try to recover"); + hls.recoverMediaError(); + break; + default: + // cannot recover hls.destroy(); - let errorMessage = document.createTextNode("Stream not found (probably doesn't exist after all)"); + let errorMessage = document.createTextNode( + "Error: " + JSON.stringify([event, data]) + ); video.parentNode.append(errorMessage); video.remove(); break; - } - console.log("fatal network error encountered, try to recover"); - console.log(data); - hls.startLoad(); - break; - case Hls.ErrorTypes.MEDIA_ERROR: - console.log("fatal media error encountered, try to recover"); - hls.recoverMediaError(); - break; - default: - // cannot recover - hls.destroy(); - let errorMessage = document.createTextNode( - "Error: " + JSON.stringify([event, data]) - ); - video.parentNode.append(errorMessage); - video.remove(); - break; + } } - } - }); - HLSInstances[id] = hls; - } - // hls.js is not supported on platforms that do not have Media Source - // Extensions (MSE) enabled. - // - // When the browser has built-in HLS support (check using `canPlayType`), - // we can provide an HLS manifest (i.e. .m3u8 URL) directly to the video - // element through the `src` property. This is using the built-in support - // of the plain video element, without using hls.js. - // - // Note: it would be more normal to wait on the 'canplay' event below however - // on Safari (where you are most likely to find built-in HLS support) the - // video.src URL must be on the user-driven white-list before a 'canplay' - // event will be emitted; the last video event that can be reliably - // listened-for when the URL is not on the white-list is 'loadedmetadata'. - else if (video.canPlayType("application/vnd.apple.mpegurl")) { - video.src = stream; - video.addEventListener("loadedmetadata", function() { - video.play(); - }); + }); + HLSInstances[camera.id] = hls; + } + // hls.js is not supported on platforms that do not have Media Source + // Extensions (MSE) enabled. + // + // When the browser has built-in HLS support (check using `canPlayType`), + // we can provide an HLS manifest (i.e. .m3u8 URL) directly to the video + // element through the `src` property. This is using the built-in support + // of the plain video element, without using hls.js. + // + // Note: it would be more normal to wait on the 'canplay' event below however + // on Safari (where you are most likely to find built-in HLS support) the + // video.src URL must be on the user-driven white-list before a 'canplay' + // event will be emitted; the last video event that can be reliably + // listened-for when the URL is not on the white-list is 'loadedmetadata'. + else if (video.canPlayType("application/vnd.apple.mpegurl")) { + video.src = camera.stream; + video.addEventListener("loadedmetadata", function () { + video.play(); + }); + } + } else { + video = document.createElement("img"); + video.className = 'reload'; + video.src = camera.url; + setTimeout(pokeImages, 1); } + section.append(video); return section; } function appendCamera(id) { STATE.push(id); history.replaceState(null, "", "#" + STATE.join(",")); - let { stream, name } = findCamera(id); - document.querySelector("main").append(makeCameraStream(id, stream, name)); + let camera = findCamera(id); + document.querySelector("main").append(makeCameraStream(camera)); } function removeCamera(id) { const oldIndex = STATE.indexOf(id); @@ -182,12 +190,15 @@ function removeCamera(id) { } } -document.getElementById("playall").addEventListener("click", e => { - for (let k of document.querySelectorAll("video")) { - k.play(); - k.currentTime += 10000; - } -}); +const playall = document.getElementById("playall"); +if (playall) { + playall.addEventListener("click", e => { + for (let k of document.querySelectorAll("video")) { + k.play(); + k.currentTime += 10000; + } + }); +} document.getElementById("vidwidth").addEventListener("input", e => { document.body.style.setProperty("--video-width", e.target.value + "vw"); }); @@ -227,3 +238,16 @@ dragHandler.on("drop", (el, target, source, sibling) => { } history.replaceState(null, "", "#" + STATE.join(",")); }); + +let pokeTimeout = undefined; +function pokeImages() { + for (let img of document.querySelectorAll('img.reload')) { + img.src = img.src.replace(/(\?0\.\d+)?$/, '?' + Math.random()); + img.addEventListener('load', e => { + if (pokeTimeout !== undefined) { + clearTimeout(pokeTimeout); + } + pokeTimeout = setTimeout(pokeImages, 1000); + }); + } +} diff --git a/dist/seattle/index.html b/dist/seattle/index.html index 50aa0a8..b076f7c 100644 --- a/dist/seattle/index.html +++ b/dist/seattle/index.html @@ -43,7 +43,7 @@ </footer> <script src="sources.js"></script> - <script src="script.js" defer></script> + <script src="/script.js" defer></script> <script src="/hls.min.js"></script> <script src="/dragula.min.js" integrity="sha256-ug4bHfqHFAj2B5MESRxbLd3R3wdVMQzug2KHZqFEmFI=" crossorigin="anonymous"></script> </body> diff --git a/dist/seattle/script.js b/dist/seattle/script.js deleted file mode 100644 index 63415da..0000000 --- a/dist/seattle/script.js +++ /dev/null @@ -1,229 +0,0 @@ -/* global Hls, dragula, CAMERAS */ - -const STATE = loadState(); - -let HLSInstances = {}; - -function loadState() { - let hash = document.location.hash; - if (hash.length === 0) { - const DEFAULTS = ["CMR-0039", "CMR-0176", "CMR-0223", "CMR-0088", "CMR-0089", "CMR-0309", "CMR-0257"]; - history.replaceState(null, "", "#" + DEFAULTS.join(",")); - return DEFAULTS; - } - hash = hash.replace(/^#/, ""); - if (hash.length === 0) { - return []; - } - return hash.split(","); -} - -function handleCheckbox(evt) { - if (evt.target.checked) { - if (!STATE.includes(evt.target.dataset.id)) { - appendCamera(evt.target.dataset.id); - } - } else { - if (STATE.includes(evt.target.dataset.id)) { - removeCamera(evt.target.dataset.id); - } - } -} - -function findCamera(id) { - for (let cameras of Object.values(CAMERAS)) { - let found = cameras.find(x => x.id === id); - if (found !== undefined) { - return found; - } - } -} - -function loadData() { - const nav = document.querySelector("nav"); - for (let neighborhood of Object.keys(CAMERAS)) { - const section = document.createElement("section"); - const h2 = document.createElement("h2"); - h2.innerText = neighborhood; - section.append(h2); - - const boxes = document.createElement("ul"); - section.append(boxes); - for (let { id, stream, name } of CAMERAS[neighborhood]) { - const label = document.createElement("label"); - const checkbox = document.createElement("input"); - checkbox.type = "checkbox"; - checkbox.dataset.id = id; - checkbox.addEventListener("input", handleCheckbox); - checkbox.checked = STATE.includes(id); - label.append(checkbox); - const text = document.createTextNode(name); - label.append(text); - - const li = document.createElement("li"); - li.append(label); - boxes.append(li); - } - - nav.append(section); - } - - for (let id of STATE) { - let { stream, name } = findCamera(id); - document.querySelector("main").append(makeCameraStream(id, stream, name)); - } -} - -function makeCameraStream(id, stream, title) { - var section = document.createElement("section"); - section.dataset.id = id; - var header = document.createElement("h2"); - header.innerText = title; - var close = document.createElement("button"); - close.className = 'close'; - close.type = "button"; - close.innerText = "X"; - header.append(close); - close.addEventListener('click', evt => { - removeCamera(evt.target.parentNode.parentNode.dataset.id); - }); - section.append(header); - var video = document.createElement("video"); - video.controls = true; - section.append(video); - if (Hls.isSupported()) { - var hls = new Hls({manifestLoadingTimeOut: 60000}); - hls.loadSource(stream); - hls.attachMedia(video); - hls.on(Hls.Events.MANIFEST_PARSED, function() { - video.play(); - }); - hls.on(Hls.Events.ERROR, function(event, data) { - if (data.fatal) { - switch (data.type) { - case Hls.ErrorTypes.NETWORK_ERROR: - // try to recover network error - if (data.response.code === 404) { - hls.destroy(); - let errorMessage = document.createTextNode("Stream not found (probably doesn't exist after all)"); - video.parentNode.append(errorMessage); - video.remove(); - break; - } - console.log("fatal network error encountered, try to recover"); - console.log(data); - hls.startLoad(); - break; - case Hls.ErrorTypes.MEDIA_ERROR: - console.log("fatal media error encountered, try to recover"); - hls.recoverMediaError(); - break; - default: - // cannot recover - hls.destroy(); - let errorMessage = document.createTextNode( - "Error: " + JSON.stringify([event, data]) - ); - video.parentNode.append(errorMessage); - video.remove(); - break; - } - } - }); - HLSInstances[id] = hls; - } - // hls.js is not supported on platforms that do not have Media Source - // Extensions (MSE) enabled. - // - // When the browser has built-in HLS support (check using `canPlayType`), - // we can provide an HLS manifest (i.e. .m3u8 URL) directly to the video - // element through the `src` property. This is using the built-in support - // of the plain video element, without using hls.js. - // - // Note: it would be more normal to wait on the 'canplay' event below however - // on Safari (where you are most likely to find built-in HLS support) the - // video.src URL must be on the user-driven white-list before a 'canplay' - // event will be emitted; the last video event that can be reliably - // listened-for when the URL is not on the white-list is 'loadedmetadata'. - else if (video.canPlayType("application/vnd.apple.mpegurl")) { - video.src = stream; - video.addEventListener("loadedmetadata", function() { - video.play(); - }); - } - return section; -} - -function appendCamera(id) { - STATE.push(id); - history.replaceState(null, "", "#" + STATE.join(",")); - let { stream, name } = findCamera(id); - document.querySelector("main").append(makeCameraStream(id, stream, name)); -} -function removeCamera(id) { - const oldIndex = STATE.indexOf(id); - if (oldIndex > -1) { - STATE.splice(oldIndex, 1); - } - history.replaceState(null, "", "#" + STATE.join(",")); - for (let video of document.querySelectorAll("main section")) { - if (video.dataset.id === id) { - video.remove(); - } - } - for (let checkbox of document.querySelectorAll('nav input')) { - if (checkbox.dataset.id === id) { - checkbox.checked = false; - } - } - if (HLSInstances[id] !== undefined) { - HLSInstances[id].destroy(); - HLSInstances[id] = undefined; - } -} - -document.getElementById("playall").addEventListener("click", e => { - for (let k of document.querySelectorAll("video")) { - k.play(); - k.currentTime += 10000; - } -}); -document.getElementById("vidwidth").addEventListener("input", e => { - document.body.style.setProperty("--video-width", e.target.value + "vw"); -}); -document.getElementById("filter").addEventListener("input", e => { - let filter = e.target.value; - for (let neighborhood of document.querySelectorAll("nav section")) { - neighborhood.hidden = true; - for (let box of neighborhood.querySelectorAll("li")) { - const matches = box.innerText - .toLowerCase() - .includes(filter.toLowerCase()); - box.hidden = !matches; - neighborhood.hidden = neighborhood.hidden && !matches; - } - } -}); - -loadData(); - -const dragHandler = dragula([document.querySelector("main")], { - moves(el, container, handle) { - return handle.tagName.toLowerCase() === "h2"; - } -}); -dragHandler.on("drop", (el, target, source, sibling) => { - const myID = el.dataset.id; - const neighborID = sibling.dataset.id; - const oldIndex = STATE.indexOf(myID); - if (oldIndex > -1) { - STATE.splice(oldIndex, 1); - } - const newIndex = STATE.indexOf(neighborID); - if (newIndex > -1) { - STATE.splice(newIndex, 0, myID); - } else { - console.log("uhhhhh fuck"); - } - history.replaceState(null, "", "#" + STATE.join(",")); -}); diff --git a/dist/seattle/sources.js b/dist/seattle/sources.js index a7d77f5..b8991b0 100644 --- a/dist/seattle/sources.js +++ b/dist/seattle/sources.js @@ -1,3 +1,4 @@ +const DEFAULTS = ["CMR-0039", "CMR-0176", "CMR-0223", "CMR-0088", "CMR-0089", "CMR-0309", "CMR-0257"]; const CAMERAS = { Ballard: [ { diff --git a/dist/utah/index.html b/dist/utah/index.html index 52876bb..8853b1f 100644 --- a/dist/utah/index.html +++ b/dist/utah/index.html @@ -40,7 +40,7 @@ </footer> <script src="sources.js"></script> - <script src="script.js" defer></script> + <script src="/script.js" defer></script> <script src="/dragula.min.js" integrity="sha256-ug4bHfqHFAj2B5MESRxbLd3R3wdVMQzug2KHZqFEmFI=" crossorigin="anonymous"></script> </body> </html> diff --git a/dist/utah/script.js b/dist/utah/script.js deleted file mode 100644 index 1701a3a..0000000 --- a/dist/utah/script.js +++ /dev/null @@ -1,166 +0,0 @@ -/* global dragula, CAMERAS */ - -const STATE = loadState(); - -function loadState() { - let hash = document.location.hash; - if (hash.length === 0) { - const DEFAULTS = [139]; - history.replaceState(null, "", "#" + DEFAULTS.join(",")); - return DEFAULTS; - } - hash = hash.replace(/^#/, ""); - if (hash.length === 0) { - return []; - } - return hash.split(",").map(x => parseInt(x)); -} - -function handleCheckbox(evt) { - const id = parseInt(evt.target.dataset.id); - if (evt.target.checked) { - if (!STATE.includes(id)) { - appendCamera(id); - } - } else { - if (STATE.includes(id)) { - removeCamera(id); - } - } -} - -function findCamera(id) { - for (let cameras of Object.values(CAMERAS)) { - let found = cameras.find(x => x.id === id); - if (found !== undefined) { - return found; - } - } -} - -function loadData() { - const nav = document.querySelector("nav"); - for (let neighborhood of Object.keys(CAMERAS)) { - const section = document.createElement("section"); - const h2 = document.createElement("h2"); - h2.innerText = neighborhood; - section.append(h2); - - const boxes = document.createElement("ul"); - section.append(boxes); - for (let { id, stream, name } of CAMERAS[neighborhood]) { - const label = document.createElement("label"); - const checkbox = document.createElement("input"); - checkbox.type = "checkbox"; - checkbox.dataset.id = id; - checkbox.addEventListener("input", handleCheckbox); - checkbox.checked = STATE.includes(id); - label.append(checkbox); - const text = document.createTextNode(name); - label.append(text); - - const li = document.createElement("li"); - li.append(label); - boxes.append(li); - } - - nav.append(section); - } - - for (let id of STATE) { - let { url, name } = findCamera(id); - document.querySelector("main").append(makeCameraStream(id, url, name)); - } -} - -function makeCameraStream(id, url, title) { - var section = document.createElement("section"); - section.dataset.id = id; - var header = document.createElement("h2"); - header.innerText = title; - var close = document.createElement("button"); - close.className = 'close'; - close.type = "button"; - close.innerText = "X"; - header.append(close); - close.addEventListener('click', evt => { - removeCamera(parseInt(evt.target.parentNode.parentNode.dataset.id)); - }); - section.append(header); - var img = document.createElement("img"); - img.className = 'reload'; - img.src = url; - section.append(img); - return section; -} - -function appendCamera(id) { - STATE.push(id); - history.replaceState(null, "", "#" + STATE.join(",")); - let { url, name } = findCamera(id); - document.querySelector("main").append(makeCameraStream(id, url, name)); -} -function removeCamera(id) { - const oldIndex = STATE.indexOf(id); - if (oldIndex > -1) { - STATE.splice(oldIndex, 1); - } - history.replaceState(null, "", "#" + STATE.join(",")); - for (let video of document.querySelectorAll("main section")) { - if (video.dataset.id === id) { - video.remove(); - } - } - for (let checkbox of document.querySelectorAll('nav input')) { - if (checkbox.dataset.id === id) { - checkbox.checked = false; - } - } -} - -document.getElementById("vidwidth").addEventListener("input", e => { - document.body.style.setProperty("--video-width", e.target.value + "vw"); -}); -document.getElementById("filter").addEventListener("input", e => { - let filter = e.target.value; - for (let neighborhood of document.querySelectorAll("nav section")) { - neighborhood.hidden = true; - for (let box of neighborhood.querySelectorAll("li")) { - const matches = box.innerText - .toLowerCase() - .includes(filter.toLowerCase()); - box.hidden = !matches; - neighborhood.hidden = neighborhood.hidden && !matches; - } - } -}); - -loadData(); - -const dragHandler = dragula([document.querySelector("main")], { - moves(el, container, handle) { - return handle.tagName.toLowerCase() === "h2"; - } -}); -dragHandler.on("drop", (el, target, source, sibling) => { - const myID = parseInt(el.dataset.id); - const neighborID = parseInt(sibling.dataset.id); - const oldIndex = STATE.indexOf(myID); - if (oldIndex > -1) { - STATE.splice(oldIndex, 1); - } - const newIndex = STATE.indexOf(neighborID); - if (newIndex > -1) { - STATE.splice(newIndex, 0, myID); - } else { - console.log("uhhhhh fuck"); - } - history.replaceState(null, "", "#" + STATE.join(",")); -}); - -function pokeImages() { - for (let img of document.querySelectorAll('img.reload')) { - img.src = img.src.replace(/(\?0\.\d+)?$/, '?' + Math.random()); - } -} -setInterval(pokeImages, 1000); diff --git a/dist/utah/sources.js b/dist/utah/sources.js index 712f4e4..d48603f 100644 --- a/dist/utah/sources.js +++ b/dist/utah/sources.js @@ -1,3 +1,4 @@ +const DEFAULTS = ["139"]; const CAMERAS = { Richfield: [ { |