Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • aura/engine
  • hermannschwaerzler/engine
  • sumpfralle/aura-engine
3 results
Show changes
Showing
with 609 additions and 0 deletions
<svelte:options tag="aura-clock"/>
<script>
import { onMount } from 'svelte';
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 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 setCurrentTrack(info) {
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;
}
</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.5em;
padding-left: 53px;
}
#current-track * {
font-size: 1.75em;
/* margin: 20% 0; */
}
.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}
{setCurrentTrack(value)}
<!-- <h2>&#9654; Playing</h2> -->
{#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';
/node_modules/
/public/build/
.DS_Store
# Aura Player
A collection of JavaScript components to integrate Aura to your Website.
Provided components:
- Track Service
## Requirements
Node 13
## Install
npm install
## Run
npm start
## Resources
* Svelte Docs - https://svelte.dev/docs
\ No newline at end of file
This diff is collapsed.
{
"name": "aura-player",
"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-node-resolve": "^6.0.0",
"rollup": "^1.12.0",
"rollup-plugin-commonjs": "^10.0.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"
}
}
This diff is collapsed.
This diff is collapsed.
contrib/aura-player/public/favicon.png

18.8 KiB

This diff is collapsed.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset='utf-8'>
<meta name='viewport' content='width=device-width,initial-scale=1'>
<title>Svelte app</title>
<link rel='icon' type='image/png' href='/favicon.png'>
<link rel="stylesheet" href="/bootstrap.css">
<!--<link rel='stylesheet' href='/global.css'>-->
<link rel='stylesheet' href='/build/bundle.css'>
<script defer src='/build/bundle.js'></script>
</head>
<body>
</body>
</html>
This diff is collapsed.
<script>
import TrackService from './TrackService.svelte';
let queryTrackservice = "trackservice";
let result;
async function getData(query) {
// FIXME Configure DataURL and Port
let response = await fetch(`http://localhost:3333/api/v1/${query}`);
let data = await response.json();
if (response.ok) {
return data;
} else {
throw new Error(data)
}
}
result = getData(queryTrackservice);
</script>
<div class="container mt-5">
<div class="row">
<div class="col-md"></div>
<div class="col-md-8 text-center">
<h1 class="display-4">Track Service</h1>
{#await result}
<div class="spinner-border mt-5" role="status">
<span class="sr-only">Loading...</span>
</div>
{:then value}
<TrackService data={value} />
{:catch error}
<p style="color:red">{error.message}</p>
{/await}
</div>
<div class="col-md"></div>
</div>
</div>
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
{
}
\ No newline at end of file