Skip to content
Snippets Groups Projects
Commit dd46633b authored by David Trattnig's avatar David Trattnig
Browse files

Merge branch 'master' into develop

parents f5b7e0e6 ac61ceb1
No related branches found
No related tags found
No related merge requests found
[submodule "contrib/engine-clock"]
path = contrib/engine-clock
url = git@gitlab.servus.at:aura/engine-clock.git
[submodule "contrib/engine-api"]
path = contrib/engine-api
url = git@gitlab.servus.at:aura/engine-api.git
/node_modules/
/public/build/
.DS_Store
*Looking for a shareable component template? Go here --> [sveltejs/component-template](https://github.com/sveltejs/component-template)*
---
# svelte app
This is a project template for [Svelte](https://svelte.dev) apps. It lives at https://github.com/sveltejs/template.
To create a new project based on this template using [degit](https://github.com/Rich-Harris/degit):
```bash
npx degit sveltejs/template svelte-app
cd svelte-app
```
*Note that you will need to have [Node.js](https://nodejs.org) installed.*
## Get started
Install the dependencies...
```bash
cd svelte-app
npm install
```
...then start [Rollup](https://rollupjs.org):
```bash
npm run dev
```
Navigate to [localhost:5000](http://localhost:5000). You should see your app running. Edit a component file in `src`, save it, and reload the page to see your changes.
By default, the server will only respond to requests from localhost. To allow connections from other computers, edit the `sirv` commands in package.json to include the option `--host 0.0.0.0`.
## Building and running in production mode
To create an optimised version of the app:
```bash
npm run build
```
You can run the newly built app with `npm run start`. This uses [sirv](https://github.com/lukeed/sirv), which is included in your package.json's `dependencies` so that the app will work when you deploy to platforms like [Heroku](https://heroku.com).
## Single-page app mode
By default, sirv will only respond to requests that match files in `public`. This is to maximise compatibility with static fileservers, allowing you to deploy your app anywhere.
If you're building a single-page app (SPA) with multiple routes, sirv needs to be able to respond to requests for *any* path. You can make it so by editing the `"start"` command in package.json:
```js
"start": "sirv public --single"
```
## Deploying to the web
### With [now](https://zeit.co/now)
Install `now` if you haven't already:
```bash
npm install -g now
```
Then, from within your project folder:
```bash
cd public
now deploy --name my-project
```
As an alternative, use the [Now desktop client](https://zeit.co/download) and simply drag the unzipped project folder to the taskbar icon.
### With [surge](https://surge.sh/)
Install `surge` if you haven't already:
```bash
npm install -g surge
```
Then, from within your project folder:
```bash
npm run build
surge public my-project.surge.sh
```
This diff is collapsed.
{
"name": "aura-engine-clock",
"version": "1.0.0",
"author": {
"name": "David Trattnig",
"email": "david.trattnig@subsquare.at",
"url": "https://subsquare.at"
},
"license": "AGPL-3.0-only",
"homepage": "https://gitlab.servus.at/aura/meta",
"repository": {
"type": "git",
"url": "git@gitlab.servus.at:aura/engine.git"
},
"scripts": {
"build": "rollup -c",
"dev": "rollup -c -w",
"start": "sirv public"
},
"devDependencies": {
"@rollup/plugin-commonjs": "^11.0.0",
"@rollup/plugin-node-resolve": "^7.0.0",
"rollup": "^1.20.0",
"rollup-plugin-livereload": "^1.0.0",
"rollup-plugin-svelte": "^5.0.3",
"rollup-plugin-terser": "^5.1.2",
"svelte": "^3.0.0"
},
"dependencies": {
"sirv-cli": "^0.4.4"
}
}
contrib/aura-clock/public/favicon.png

18.8 KiB

html, body {
position: relative;
width: 100%;
height: 100%;
}
body {
color: #333;
margin: 0;
padding: 8px;
box-sizing: border-box;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
}
a {
color: rgb(0,100,200);
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
a:visited {
color: rgb(0,80,160);
}
label {
display: block;
}
input, button, select, textarea {
font-family: inherit;
font-size: inherit;
padding: 0.4em;
margin: 0 0 0.5em 0;
box-sizing: border-box;
border: 1px solid #ccc;
border-radius: 2px;
}
input:disabled {
color: #ccc;
}
input[type="range"] {
height: 0;
}
button {
color: #333;
background-color: #f4f4f4;
outline: none;
}
button:disabled {
color: #999;
}
button:not(:disabled):active {
background-color: #ddd;
}
button:focus {
border-color: #666;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset='utf-8'>
<meta name='viewport' content='width=device-width,initial-scale=1'>
<title>Aura Engine - Studio Clock</title>
<link rel='icon' type='image/png' href='/favicon.png'>
<link rel='stylesheet' href='/global.css'>
<link rel='stylesheet' href='/build/aura-clock-bundle.css'>
<script defer src='/build/aura-clock-bundle.js'></script>
</head>
<body style="background-color: black;">
<aura-clock
name="Studio Clock"
logo="https://o94.at/themes/custom/radio_orange/logo1.png"
logosize="180px"
api="http://localhost:3333/api/v1/"
css="http://localhost:3333/css/aura.css">
</aura-clock>
</body>
</html>
import svelte from 'rollup-plugin-svelte';
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import livereload from 'rollup-plugin-livereload';
import { terser } from 'rollup-plugin-terser';
const production = !process.env.ROLLUP_WATCH;
export default {
input: 'src/main.js',
output: {
sourcemap: true,
format: 'iife',
name: 'app',
file: 'public/build/aura-clock-bundle.js'
},
plugins: [
svelte({
// enable run-time checks when not in production
dev: !production,
// we'll extract any component CSS out into
// a separate file - better for performance
css: css => {
css.write('public/build/aura-clock-bundle.css');
},
customElement: true
}),
// If you have external dependencies installed from
// npm, you'll most likely need these plugins. In
// some cases you'll need additional configuration -
// consult the documentation for details:
// https://github.com/rollup/plugins/tree/master/packages/commonjs
resolve({
browser: true,
dedupe: ['svelte']
}),
commonjs(),
// In dev mode, call `npm run start` once
// the bundle has been generated
!production && serve(),
// Watch the `public` directory and refresh the
// browser on changes when not in production
!production && livereload('public'),
// If we're building for production (npm run build
// instead of npm run dev), minify
production && terser()
],
watch: {
clearScreen: false
}
};
function serve() {
let started = false;
return {
writeBundle() {
if (!started) {
started = true;
require('child_process').spawn('npm', ['run', 'start', '--', '--dev'], {
stdio: ['ignore', 'inherit', 'inherit'],
shell: true
});
}
}
};
}
<svelte:options tag="aura-clock"/>
<main bind:this={rootElement}></main>
<script>
import { onMount } from 'svelte';
export let css = "";
export let api = "http://localhost:3333/api/v1";
export let name = "Studio Clock";
export let logo = "https://gitlab.servus.at/aura/meta/-/raw/master/images/aura-logo.png";
export let logosize = "100px";
export let noScheduleMessage = "Nothing scheduled!";
let time = new Date();
let queryCurrent = "/clock";
let rootElement;
let data;
let currentTrack = null;
let timeLeft;
// these automatically update when `time`
// changes, because of the `$:` prefix
$: hours = time.getHours();
$: minutes = time.getMinutes();
$: seconds = time.getSeconds();
data = fetchApi(queryCurrent);
onMount(() => {
const interval = setInterval(() => {
time = new Date();
timeLeft -= 1;
if (timeLeft <= 0 || data == null) {
currentTrack = null;
data = null;
data = fetchApi(queryCurrent);
}
}, 1000);
return () => {
clearInterval(interval);
};
});
async function fetchApi(query) {
let response;
let data;
try {
response = await fetch(api+query);
} catch {
throw new Error("Cannot connect to Engine!");
}
try {
data = await response.json();
} catch(e) {
console.log("Error while converting response to JSON!", e);
throw new Error(response.statusText);
}
if (response.ok) {
return data;
} else {
console.log("Error:", data);
throw new Error(data.message);
}
}
function initComponent(info) {
/* Load external CSS */
if (css != null)
loadExternalCss(rootElement, css);
/* Set currently loaded data */
if (currentTrack == null && info != null && info.track != null) {
currentTrack = info;
let t = time - Date.parse(info.track_start);
t = parseInt(t/1000);
timeLeft = info.track.duration - t - 3; /* FIXME improve timings in coordination with scheduler/trackservice/LQS */
console.log("Current Data", info);
}
return "";
}
function displayTitle(track) {
if (track != null) {
let artist = "";
if (track.artist != "")
artist = track.artist + " - ";
return artist + track.title;
}
return "";
}
function formatTime(seconds) {
if (seconds != null && Number.isInteger(seconds)) {
let d = new Date(null);
d.setSeconds(seconds);
let s;
if (seconds > 3600)
s = d.toISOString().substr(11, 8);
else
s = d.toISOString().substr(14, 5);
return s;
}
return "";
}
function displayShowName(show) {
let name = ""
if (show == null || show.name == null) {
name = '<span class="error">'+noScheduleMessage+'</span>';
} else {
name = show.name;
}
return name;
}
function displayShowSchedule(schedule) {
let str = "";
if (schedule != null && schedule.schedule_start != null) {
let scheduleStart = ""
let scheduleEnd = "";
if (schedule.schedule_start != null) {
let scheduleStart = new Date(Date.parse(schedule.schedule_start));
scheduleStart = scheduleStart.toLocaleTimeString(navigator.language, {
hour: '2-digit',
minute:'2-digit'
});
str = "(" + scheduleStart;
}
if (schedule.schedule_end != null) {
scheduleEnd = new Date(Date.parse(schedule.schedule_end));
scheduleEnd = scheduleEnd.toLocaleTimeString(navigator.language, {
hour: '2-digit',
minute:'2-digit'
});
str = str + " - " + scheduleEnd + ")";
} else {
str += ")";
}
}
return str;
}
function isActive(entry, currentTrack) {
if (currentTrack != null && entry.id == currentTrack.id) {
// Scroll to current playlist entry
location.hash = "#current-playlist-entry";
return true;
}
return false;
}
function loadExternalCss(root, file) {
let element = document.createElement("link");
element.setAttribute("rel", "stylesheet");
element.setAttribute("type", "text/css");
element.setAttribute("href", file);
root.appendChild(element);
}
</script>
<style>
#station-header {
width: 100%;
height: 50px;
/* margin: 20px 100px; */
padding: 40px 100px;
}
#station-name {
margin: 0;
font-size: 3em;
line-height: 80px;
}
#station-logo {
align-content: left;
text-align: right;
margin: 0 40px 0 10px;
opacity: 0.5;
filter: invert(100%);
}
#studio-clock {
width: calc(100% - 200px);
height: calc(100% - 500px);
margin: 100px;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
/* border: 2px solid #333; */
flex-direction: row;
}
#left-column {
width: 30%;
padding: 25px;
}
#right-column {
width: 70%;
padding: 25px 25px 25px 50px;
}
#current-schedule,
#next-schedule {
margin: 0 0 40px 20px;
}
#next-schedule {
background-color:rgb(24, 24, 24);
margin-right: 20px;
padding: 12px;
}
#current-schedule .schedule-title {
color: #ccc;
font-size: 3.5em;
}
#next-schedule .schedule-title {
color: gray !important;
font-size: 2em;
}
#playlist {
border: 2px solid #333;
margin: 20px 20px 40px 20px;
padding: 10px;
height: calc(80% - 100px);
overflow-y: auto;
scroll-behavior: smooth;
background-color: #111;
display: flex;
align-items: center;
}
#playlist::-webkit-scrollbar-track
{
border-radius: 10px;
-webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.3);
background-color: rgb(77, 73, 73);
}
#playlist::-webkit-scrollbar
{
width: 12px;
background-color: rgb(0, 0, 0);
}
#playlist::-webkit-scrollbar-thumb
{
border-radius: 10px;
-webkit-box-shadow: inset 0 0 6px rgba(0,0,0,.3);
background-color: rgb(34, 32, 32);
}
.playlist-entry {
font-size: 1.9em;
padding-left: 53px;
}
#current-track * {
font-size: 1.5em;
}
.play-icon,
.track-time-left {
margin: 25px 50px;
}
.is-active {
color: green;
padding-left: 0;
}
.is-active .track-title::before {
content: "\00a0\00a0▶\00a0\00a0\00a0";
font-size: larger;
color: green;
}
.is-active .track-time-left {
color: rgb(43, 241, 36);
background-color: #222;
padding: 5px 15px;
}
.error {
font-size: 1.3em;
color:red;
height:100%;
display : flex;
align-items : center;
justify-content: center;
}
svg {
width: 100%;
height: 100%;
}
.clock-face {
stroke: rgb(66, 66, 66);
fill: black;
}
.minor {
stroke: rgb(132, 132, 132);
stroke-width: 0.5;
}
.major {
stroke: rgb(162, 162, 162);
stroke-width: 1;
}
.hour {
stroke: rgba(255, 255, 255, 0.705);
}
.minute {
stroke: rgba(255, 255, 255, 0.705);
}
.second, .second-counterweight {
stroke: rgb(180,0,0);
}
.second-counterweight {
/* stroke-width: 3; */
}
footer {
width: 100%;
text-align: center;
font-size: 0.8em;
color: gray;
opacity: 0.5;
}
footer a {
color: gray;
text-decoration: underline;
}
footer #aura-logo {
/* opacity: 0.5; */
filter: invert(100%);
width: 75px;
margin: 0 0 20px 0;
}
</style>
<div id="station-header">
<img id="station-logo" src="{logo}" style="width:{logosize}" alt="Radio Station" align="left" />
<h1 id="station-name">{name}</h1>
</div>
<div id="studio-clock">
<div id="left-column" class="column">
<svg viewBox='-50 -50 100 100'>
<circle class='clock-face' r='48'/>
<!-- markers -->
{#each [0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55] as minute}
<line
class='major'
y1='35'
y2='45'
transform='rotate({30 * minute})'
/>
{#each [1, 2, 3, 4] as offset}
<line
class='minor'
y1='42'
y2='45'
transform='rotate({6 * (minute + offset)})'
/>
{/each}
{/each}
<!-- hour hand -->
<line
class='hour'
y1='2'
y2='-20'
transform='rotate({30 * hours + minutes / 2})'
/>
<!-- minute hand -->
<line
class='minute'
y1='4'
y2='-30'
transform='rotate({6 * minutes + seconds / 10})'
/>
<!-- second hand -->
<g transform='rotate({6 * seconds})'>
<line class='second' y1='10' y2='-38'/>
<line class='second-counterweight' y1='10' y2='2'/>
</g>
</svg>
</div>
<div id="right-column" class="column">
{#await data}
<div class="spinner-border mt-5" role="status">
<span class="sr-only">Loading...</span>
</div>
{:then value}
{initComponent(value)}
{#if value.current.show}
<div id="current-schedule">
<h1 class="schedule-title">{@html displayShowName(value.current.show)} {displayShowSchedule(value.current)}</h1>
<!-- <div class="schedule-details">
<b>Type:</b> {value.current.show.type}, <b>Host:</b> {value.current.show.host}</div>-->
</div>
<div id="playlist">
{#if value.current.playlist}
<ol>
{#each value.current.playlist.entries as entry, index}
{#if isActive(entry, value.track)}
<li id="current-playlist-entry" class="playlist-entry is-active">
<!-- <span class="play-icon">&#9654;</span> -->
<span class="track-title">{displayTitle(entry)}</span>
<span class="track-time-left">({formatTime(timeLeft)})</span>
</li>
{:else}
<li class="playlist-entry">
<span class="track-title">{displayTitle(entry)}</span>
<span class="track-duration">({formatTime(entry.duration)})</span>
</li>
{/if}
{/each}
</ol>
{:else}
<div id="current-track" class="is-active">
<h2>
<span class="track-title">{displayTitle(value.track)}</span>
<span class="track-time-left">{formatTime(timeLeft)}</span>
</h2>
</div>
{/if}
</div>
<div id="next-schedule">
<h3 class="schedule-title">Next: {@html displayShowName(value.next.show)} {displayShowSchedule(value)}</h3>
</div>
{/if}
{:catch error}
<div class="error"><p>{error}</p></div>
{/await}
</div>
</div>
<footer>
<a href="https://gitlab.servus.at/aura/meta"><img id="aura-logo" src="https://gitlab.servus.at/aura/meta/-/raw/master/images/aura-logo.png" alt="Aura Logo" /></a>
<br/>
Studio Clock is powered by <a href="https://gitlab.servus.at/autoradio">Aura Engine</a>
</footer>
\ No newline at end of file
import StudioClock from './StudioClock.svelte';
engine-api @ ae560241
Subproject commit ae560241e5947f595a793413dfc48657807233b0
engine-clock @ 6ae833ad
Subproject commit 6ae833ad05501cfed65d54becf1f749bded1fd0d
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment