diff --git a/www/assets/app.js b/www/assets/app.js index c3fd8fa..5d86007 100644 --- a/www/assets/app.js +++ b/www/assets/app.js @@ -15,6 +15,19 @@ import './bootstrap'; import feather from 'feather-icons'; feather.replace(); +/** Update css variables --{header, footer}-height + * by querying elements real height */ +(function() { + let css_root = document.querySelector(':root'); + let header = document.getElementsByTagName('header')[0]; + let header_height = header.clientHeight; + css_root.style.setProperty('--header-height', header_height + 'px'); + let footer = document.getElementsByTagName('footer')[0]; + let footer_height = footer.clientHeight; + css_root.style.setProperty('--footer-height', footer_height + 'px'); + +})(); + try { document.getElementsByClassName('prevent').map( (e) => e.addEventListener('click', (e) => e.preventDefault()) diff --git a/www/assets/controllers/delete-record_controller.js b/www/assets/controllers/delete-record_controller.js new file mode 100644 index 0000000..3b09f02 --- /dev/null +++ b/www/assets/controllers/delete-record_controller.js @@ -0,0 +1,25 @@ +import { Controller } from '@hotwired/stimulus'; + +/* stimulusFetch: 'lazy' */ +export default class extends Controller { + static targets = ['filename'] + + delete() { + let filename = this.filenameTarget.value; + let url = `/records/delete/${filename}`; + fetch(url, { + method: 'POST' + }) + .then(response => { + if (response.ok) { + window.location.reload(); + } else { + console.log(response); + } + }) + .catch(error => { + console.log(error); + } + ); + } +} \ No newline at end of file diff --git a/www/assets/controllers/record-play_controller.js b/www/assets/controllers/record-play_controller.js new file mode 100644 index 0000000..3b09f02 --- /dev/null +++ b/www/assets/controllers/record-play_controller.js @@ -0,0 +1,25 @@ +import { Controller } from '@hotwired/stimulus'; + +/* stimulusFetch: 'lazy' */ +export default class extends Controller { + static targets = ['filename'] + + delete() { + let filename = this.filenameTarget.value; + let url = `/records/delete/${filename}`; + fetch(url, { + method: 'POST' + }) + .then(response => { + if (response.ok) { + window.location.reload(); + } else { + console.log(response); + } + }) + .catch(error => { + console.log(error); + } + ); + } +} \ No newline at end of file diff --git a/www/assets/controllers/record.js b/www/assets/controllers/record.js deleted file mode 100644 index 55782be..0000000 --- a/www/assets/controllers/record.js +++ /dev/null @@ -1,7 +0,0 @@ -(function() { - try { - let delete_buttons = document.getElementsByClassName("delete-button"); - } catch { - console.debug("no delete buttons found"); - } -})(); \ No newline at end of file diff --git a/www/assets/styles/app.css b/www/assets/styles/app.css index a9aa650..216b77b 100644 --- a/www/assets/styles/app.css +++ b/www/assets/styles/app.css @@ -1,9 +1,24 @@ :root { - --bg: lightgray; + --bg: white; + --font-family: 'Latin Modern Math'; + --font-size: 2em; } * { box-sizing: border-box; + font-family: var(--font-family); + font-size: large; +} + +html { + position: relative; + min-height: 100vh; +} + +a { + text-decoration: none; + color: inherit; + cursor: pointer; } .container { @@ -20,6 +35,18 @@ flex-direction: row; } +.grow { + flex-grow: 100; +} + +.end { + justify-self: flex-end; +} + +.item { + align-self: flex-end; +} + .grid { display: grid; } @@ -38,7 +65,7 @@ z-index: 1; } -.button, input { +.button, input[type=submit], input[type=button] { background-color: #f1f1f1; color: black; border-radius: 5px; @@ -129,23 +156,31 @@ body { header { padding: 1em; - display: flex; - flex-direction: row; /** Align text and center of image */ justify-content: center; align-items: baseline; } +header h1 { + font-size: 4rem; +} + +header:first-child { + justify-content: center; +} + header img.logo { - width: 100px; - height: 100px; + width: auto; + height: 10rem; + position: relative; + top: -2rem; + padding-top: 1em; } main { - min-height: 100vh; + min-height: calc(100vh - (var(--header-height, 4em) + var(--footer-height, 4em)) ); padding: 5em; z-index: 0; - position: relative; } footer { @@ -153,6 +188,7 @@ footer { background-color: black; padding: 2em; text-align: center; + overflow: hidden; } footer a { @@ -168,8 +204,6 @@ li, td { align-items: center; } -td - /* .dropdown-button:hover { background-color: #900; color: white diff --git a/www/assets/styles/menu.css b/www/assets/styles/menu.css index d934207..d433632 100644 --- a/www/assets/styles/menu.css +++ b/www/assets/styles/menu.css @@ -1,7 +1,8 @@ nav { --nav-width: 20em; - --nav-bg: white; - --burger-size: 2em; + --nav-bg: lightgrey; + --burger-size: 5em; + --burger-weight: 3px; position: fixed; top: 0; left: 0; @@ -40,7 +41,7 @@ nav { top: 0; left: 0; background: black; - height: 2px; + height: var(--burger-weight); width: 75%; transition: all 0.4s ease; color: #000; @@ -51,19 +52,19 @@ nav { .hamburger>div::after { content: ''; position: absolute; - top: -7px; + top: 10px; background: black; width: 100%; - height: 2px; + height: var(--burger-weight); transition: all 0.4s ease; } .hamburger>div::after { - top: 7px; + top: -10px; } .toggler:checked+.hamburger>div { - background: rgba(0, 0, 0, 0); + background-color: var(--nav-bg); } .toggler:checked+.hamburger>div::before { @@ -87,7 +88,7 @@ nav { .toggler:checked~.menu { width: fit-content; height: fit-content; - background-color: rgba(255, 255, 255, 1) !important; + background-color: var(--nav-bg) !important; } .menu>ul { @@ -95,12 +96,12 @@ nav { flex-direction: column; position: fixed; width: 0; - height: 100vmax; + height: calc(100vh - var(--burger-size)); padding-left: 1em; padding-right: 1em; margin-top: var(--burger-size); visibility: hidden; - background-color: white; + background-color: var(--nav-bg); z-index: 10; } @@ -112,7 +113,7 @@ nav { .menu>ul>li>a { color: black; text-decoration: none; - font-size: 2rem; + font-size: var(--font-size); } .toggler~.fill { @@ -120,11 +121,11 @@ nav { } .toggler:checked~.fill { - background: white; + background: var(--nav-bg); position: absolute; top: 0; left: 0; - height: 100vh; + height: var(--burger-size); width: var(--nav-width); display: block; z-index: 10; diff --git a/www/assets/utils/spectro.js b/www/assets/utils/spectro.js index 6785240..178a71a 100644 --- a/www/assets/utils/spectro.js +++ b/www/assets/utils/spectro.js @@ -3,8 +3,8 @@ * https://codepen.io/jakealbaugh/pen/jvQweW */ -// UPDATE: there is a problem in chrome with starting audio context -// before a user gesture. This fixes it. +const ICECAST_URL = '/stream'; + var started = false; try { var spectro_button = document.getElementById('spectro-button'); @@ -13,12 +13,13 @@ try { started = true; console.log("starting spectro"); initialize(); - }) + }) } catch { console.debug("spectro not found"); } function initialize() { + const AUDIO_ELEMENT = document.getElementById('player'); const CVS = document.getElementById('spectro-canvas'); const CTX = CVS.getContext('2d'); const W = CVS.width = window.innerWidth; @@ -27,14 +28,29 @@ function initialize() { const ACTX = new AudioContext(); const ANALYSER = ACTX.createAnalyser(); - ANALYSER.fftSize = 4096; - - navigator.mediaDevices - .getUserMedia({ audio: true }) - .then(process); + ANALYSER.fftSize = 4096; - function process(stream) { - const SOURCE = ACTX.createMediaStreamSource(stream); + // navigator.mediaDevices + // .getUserMedia({ audio: true }) + // .then(process); + + // Add icecast stream + // var audio = new Audio(ICECAST_URL); + let stream; + AUDIO_ELEMENT.src = ICECAST_URL; + AUDIO_ELEMENT.play(); + AUDIO_ELEMENT.onplay = function () { + if (navigator.userAgent.indexOf('Firefox') > -1) { + stream = AUDIO_ELEMENT.mozCaptureStream(); + } else { + console.debug('Not a firefox browser, defaults to `captureStream()`'); + stream = AUDIO_ELEMENT.captureStream(); + } + process(AUDIO_ELEMENT); + } + + function process(audio) { + const SOURCE = ACTX.createMediaElementSource(audio); SOURCE.connect(ANALYSER); const DATA = new Uint8Array(ANALYSER.frequencyBinCount); const LEN = DATA.length; diff --git a/www/src/Controller/HomeController.php b/www/src/Controller/HomeController.php index 150ad72..38f77f4 100644 --- a/www/src/Controller/HomeController.php +++ b/www/src/Controller/HomeController.php @@ -62,7 +62,7 @@ class HomeController extends AbstractController ORDER BY `date` DESC LIMIT 1"; $stmt = $this->connection->prepare($sql); $result = $stmt->executeQuery(); - return $result->fetchAllAssociative()[0]; + return $result->fetchAllAssociative(); } private function last_chart_generated() { diff --git a/www/src/Controller/RecordsController.php b/www/src/Controller/RecordsController.php index f7021a8..a827eaa 100644 --- a/www/src/Controller/RecordsController.php +++ b/www/src/Controller/RecordsController.php @@ -1,4 +1,5 @@ $date, ]); } - + /** - * @Route("/records/remove/{basename}", name="record_remove") + * @Route("/records/delete/{basename}", name="record_delete") */ - public function remove_record($basename) + public function delete_record(Connection $connection, $basename) { + $this->connection = $connection; $this->remove_record_by_basename($basename); return $this->redirectToRoute('records_index'); } - private function list_records() + private function list_records() { - $records_path = $this->getParameter('app.records_dir')."/out/*.wav"; + $records_path = $this->getParameter('app.records_dir') . "/out/*.wav"; $records = glob($records_path); - $records = array_map(function($record) { + $records = array_map(function ($record) { $record = basename($record); return $record; }, $records); return $records; } - private function get_record_date($record_path) + private function get_record_date($record_path) { $record_basename = basename($record_path); $record_date = explode("_", explode(".", $record_basename)[0])[1]; @@ -58,17 +60,37 @@ class RecordsController extends AbstractController return $date; } - private function only_on($date, $records) { - $filtered_records = array_filter($records, function($record) use ($date) { + private function only_on($date, $records) + { + $filtered_records = array_filter($records, function ($record) use ($date) { return $this->get_record_date($record) == $date; }); return $filtered_records; } - private function remove_record_by_basename($basename) { - $record_path = $this->getParameter('app.records_dir')."/out/$basename"; - unlink($record_path); - unlink($record_path.".d/model.out.csv"); - rmdir($this->getParameter('app.records_dir')."/out/$basename.d"); + private function remove_record_by_basename($basename) + { + if (strlen($basename) > 1) { + /** Remove files associated with this filename */ + $record_path = $this->getParameter('app.records_dir') . "/out/$basename"; + if (is_file($record_path)) + unlink($record_path); + $model_out_dir = $record_path.".d"; + $model_out_path = $model_out_dir."/model.out.csv"; + if (is_file($model_out_path)) + unlink($model_out_path); + if (is_dir($model_out_dir)) + rmdir($model_out_dir); + /** Remove database entry associated with this filename */ + $this->remove_observations_from_record($basename); + } } -} \ No newline at end of file + + private function remove_observations_from_record($basename) + { + $sql = "DELETE FROM observation WHERE audio_file = :filename"; + $stmt = $this->connection->prepare($sql); + $stmt->bindValue(':filename', $basename); + $stmt->executeStatement(); + } +} diff --git a/www/templates/base.html.twig b/www/templates/base.html.twig index c292e8b..ee10174 100644 --- a/www/templates/base.html.twig +++ b/www/templates/base.html.twig @@ -1,5 +1,5 @@ - +