forked from tilderadio/site
Compare commits
87 Commits
Author | SHA1 | Date |
---|---|---|
deepend | 036aa94ed3 | |
deepend | 5147f82859 | |
Mark Cornick | 0205a9b223 | |
Ben Harris | 4b0b058d76 | |
Santiago Forero | eedee8afd6 | |
Santiago Forero | c02cf1e929 | |
Ben Harris | b926b0d380 | |
Santiago Forero | 43d36bfeec | |
Ben Harris | 324ae061a5 | |
Lohan G | b7389d8907 | |
Ben Harris | 5025739ef6 | |
Ben Harris | 8efcdfab8c | |
Ben Harris | e5b4379406 | |
Ben Harris | af6bb5e0df | |
Ben Harris | 11f25e9707 | |
Santiago Forero | 1222388339 | |
Santiago Forero | b57596bd47 | |
Ben Harris | 54e91ab6e7 | |
Santiago Forero | 9cf094f4ef | |
Ben Harris | c006a40edf | |
Charles E. Lehner | 05e101f9e8 | |
Charles E. Lehner | 1e68d971c8 | |
Charles E. Lehner | a731a47f1c | |
deepend | 8916d836c5 | |
deepend | aec6cc49ba | |
Robert Miles | 03848b1924 | |
Robert Miles | d32fa86540 | |
Ben Harris | 8e9a7fed30 | |
deepend | 163d9991f8 | |
cat K. | 0f842a9c5c | |
deepend | 55df3c66fb | |
James Tomasino | a447477ed4 | |
deepend | eda12ccb4a | |
Robert Miles | 6b6bc48ae7 | |
Robert Miles | 8b72e32771 | |
deepend | b0d14c0ebb | |
deepend | f17df8ae52 | |
Robert Miles | 3150e62a31 | |
deepend | 7d40425445 | |
deepend | 5aa784495e | |
James Tomasino | 6c272afa11 | |
James Tomasino | edc9543120 | |
James Tomasino | 3987c26428 | |
James Tomasino | d726d6522a | |
James Tomasino | b8980af520 | |
Robert Miles | b5992444e8 | |
James Tomasino | 8729aecc37 | |
Robert Miles | 7e19179fcc | |
James Tomasino | 76c878d65a | |
Ben Harris | af00cded1e | |
Ben Harris | abc0b5f531 | |
cmccabe | 76abc458e7 | |
Robert Miles | 86166f3d31 | |
deepend | dc59e404b7 | |
Robert Miles | 4a1b41d7de | |
Robert Miles | 3ca33228f5 | |
Ben Harris | 24fe2f80c0 | |
Robert Miles | e22a43f8a4 | |
Robert Miles | 99961cfa55 | |
Ben Harris | 3bd8e4ab04 | |
Robert Miles | 1c1744092b | |
Robert Miles | f409367cfe | |
Ben Harris | c78aea671c | |
Robert Miles | 9a928f4a39 | |
deepend | ce7eef8f4a | |
Robert Miles | f535af0d2c | |
deepend | 6a65a50a38 | |
Robert Miles | fcbbfeebbd | |
pendragon | 3826d2d700 | |
Ben Harris | 3d695c8b60 | |
Ben Harris | 853e92965e | |
Ben Harris | 2675c01f37 | |
Ben Harris | d7d98f43c3 | |
Ben Harris | 6addb64f5a | |
Ben Harris | fd224caedc | |
deepend | 6867b90ea8 | |
deepend | 2ab5c890d8 | |
Ben Harris | 16c24696a2 | |
Ben Harris | cf9cc6adb8 | |
Ben Harris | 28cb91b102 | |
Ben Harris | b71e866604 | |
Ben Harris | e83dcb9f7e | |
deepend | 54d8680435 | |
deepend | 4d12281933 | |
deepend | b83368174a | |
deepend | 568ae66483 | |
Lars Lehtonen | 6048051c0e |
|
@ -0,0 +1,25 @@
|
|||
---
|
||||
kind: pipeline
|
||||
type: ssh
|
||||
name: deploy
|
||||
|
||||
server:
|
||||
host:
|
||||
from_secret: host
|
||||
user:
|
||||
from_secret: username
|
||||
ssh_key:
|
||||
from_secret: ssh_key
|
||||
|
||||
clone:
|
||||
disable: true
|
||||
|
||||
trigger:
|
||||
branch:
|
||||
- master
|
||||
|
||||
steps:
|
||||
- name: deploy
|
||||
commands:
|
||||
- git -C /usr/share/nginx/tilderadio.org pull --rebase origin master
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
# redirect proper start
|
||||
<IfModule mod_rewrite.c>
|
||||
RewriteEngine On
|
||||
Redirect 302 /listen https://radio.tildeverse.org/radio/8000/radio.ogg
|
||||
Redirect 302 /listen https://azuracast.tilderadio.org/radio/8000/radio.ogg
|
||||
</IfModule>
|
||||
# redirect proper end
|
|
@ -2,3 +2,4 @@
|
|||
|
||||
tilderadio site. Very much inspired / borrowed from tildeverse.org.
|
||||
|
||||
Radio logo based on the radio emoji from Twemoji 13.0.2.
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
#This is a configuration file for butt (broadcast using this tool)
|
||||
|
||||
[main]
|
||||
bg_color = 151540480
|
||||
txt_color = -256
|
||||
server = Tilde
|
||||
srv_ent = Tilde
|
||||
icy = RFPC
|
||||
icy_ent = RFPC
|
||||
num_of_srv = 1
|
||||
num_of_icy = 1
|
||||
song_path =
|
||||
song_update = 0
|
||||
app_update_service = 3
|
||||
app_update = 1
|
||||
gain = 0.707946
|
||||
connect_at_startup = 0
|
||||
log_file =
|
||||
|
||||
[audio]
|
||||
# edit here
|
||||
device = 3
|
||||
samplerate = 44100
|
||||
bitrate = 128
|
||||
channel = 2
|
||||
codec = mp3
|
||||
aac_overwrite_aot = 0
|
||||
aac_aot = 5
|
||||
resample_mode = 0
|
||||
buffer_ms = 50
|
||||
|
||||
[record]
|
||||
bitrate = 128
|
||||
codec = mp3
|
||||
start_rec = 0
|
||||
rec_after_launch = 0
|
||||
sync_to_hour = 0
|
||||
split_time = 0
|
||||
filename = rec_%Y%m%d-%H%M%S.mp3
|
||||
# edit here
|
||||
folder = /home/myuser/
|
||||
|
||||
[dsp]
|
||||
equalizer = 0
|
||||
gain1 = 0.000000
|
||||
gain2 = 0.000000
|
||||
gain3 = 0.000000
|
||||
gain4 = 0.000000
|
||||
gain5 = 0.000000
|
||||
|
||||
[gui]
|
||||
attach = 1
|
||||
ontop = 1
|
||||
lcd_auto = 0
|
||||
|
||||
[Tilde]
|
||||
address = azuracast.tilderadio.org
|
||||
# live stream
|
||||
port = 8005
|
||||
# test stream
|
||||
#port = 8015
|
||||
# edit here
|
||||
password = your stream password here
|
||||
type = 1
|
||||
mount = /
|
||||
# edit here
|
||||
usr = myuser
|
||||
|
||||
[RFPC]
|
||||
pub = 0
|
||||
description = radio free tilde
|
||||
genre = Various
|
||||
url = https://tilderadio.org
|
||||
irc = irc.tilde.chat
|
||||
icq =
|
||||
aim =
|
||||
|
|
@ -0,0 +1,152 @@
|
|||
.calendar {
|
||||
width: 100%;
|
||||
border-spacing: 0;
|
||||
border-collapse: separate;
|
||||
table-layout: fixed;
|
||||
margin-bottom: 50px;
|
||||
}
|
||||
.calendar thead tr th {
|
||||
background: #1c201b;
|
||||
padding: 0.5em;
|
||||
overflow: hidden;
|
||||
text-align: center;
|
||||
border-bottom: 1px solid #26d700;
|
||||
}
|
||||
.calendar thead tr th:first-child {
|
||||
border-radius: 3px 0 0 0;
|
||||
}
|
||||
.calendar thead tr th:last-child {
|
||||
border-radius: 0 3px 0 0;
|
||||
}
|
||||
.calendar thead tr th .day {
|
||||
display: block;
|
||||
font-size: 1.2em;
|
||||
border-radius: 50%;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
margin: 0 auto 5px;
|
||||
padding: 5px;
|
||||
line-height: 1.8;
|
||||
}
|
||||
.calendar thead tr th .day.active {
|
||||
background: #d1d5db;
|
||||
color: #626E7E;
|
||||
}
|
||||
.calendar thead tr th .short {
|
||||
display: none;
|
||||
}
|
||||
.calendar thead tr th i {
|
||||
vertical-align: middle;
|
||||
font-size: 2em;
|
||||
}
|
||||
.calendar tbody tr {
|
||||
background: #1c201b;
|
||||
}
|
||||
.calendar tbody tr:nth-child(odd) {
|
||||
background: #313e2f;
|
||||
}
|
||||
.calendar tbody tr:nth-child(2n+0) td {
|
||||
border-bottom: 1px solid #626E7E;
|
||||
}
|
||||
.calendar tbody tr td {
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
border-left: 1px solid #626E7E;
|
||||
position: relative;
|
||||
/* Height here should be synced with $cell_height in ../schedule/calendar.php */
|
||||
height: 32px;
|
||||
cursor: pointer;
|
||||
overflow: hidden;
|
||||
}
|
||||
.calendar tbody tr td:last-child {
|
||||
border-right: 1px solid #626E7E;
|
||||
}
|
||||
.calendar tbody tr td.hour {
|
||||
font-size: 2em;
|
||||
padding: 0;
|
||||
background: #072000;
|
||||
border-bottom: 1px solid #626E7E;
|
||||
border-collapse: separate;
|
||||
min-width: 100px;
|
||||
cursor: default;
|
||||
}
|
||||
.calendar tbody tr td.hour span {
|
||||
display: block;
|
||||
}
|
||||
.calendar tbody tr td.active {
|
||||
border: 1px solid yellow;
|
||||
}
|
||||
#pointer {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
border-top: 1px solid red;
|
||||
z-index: 1;
|
||||
}
|
||||
.pointer-label {
|
||||
color: red;
|
||||
}
|
||||
.pointer-label-wrapper {
|
||||
color: yellow;
|
||||
}
|
||||
.show-title {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
@media (max-width: 60em) {
|
||||
.calendar-wrapper {
|
||||
display: block;
|
||||
overflow-x: scroll;
|
||||
}
|
||||
.calendar {
|
||||
min-width: 800px;
|
||||
}
|
||||
.calendar tbody tr td.hour {
|
||||
font-size: 16px;
|
||||
}
|
||||
.calendar thead tr th .long {
|
||||
display: none;
|
||||
}
|
||||
.calendar thead tr th .short {
|
||||
display: block;
|
||||
}
|
||||
.calendar tbody tr td.hour span {
|
||||
transform: rotate(270deg);
|
||||
-webkit-transform: rotate(270deg);
|
||||
-moz-transform: rotate(270deg);
|
||||
}
|
||||
}
|
||||
@media (max-width: 27em) {
|
||||
.calendar-wrapper {
|
||||
display: block;
|
||||
overflow-x: scroll;
|
||||
}
|
||||
.calendar {
|
||||
min-width: 800px;
|
||||
}
|
||||
.calendar tbody tr td.hour {
|
||||
font-size: 16px;
|
||||
text-align: right;
|
||||
}
|
||||
.calendar thead tr th {
|
||||
font-size: 65%;
|
||||
}
|
||||
.calendar thead tr th .day {
|
||||
display: block;
|
||||
font-size: 1.2em;
|
||||
border-radius: 50%;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
margin: 0 auto 5px;
|
||||
padding: 5px;
|
||||
}
|
||||
.calendar thead tr th .day.active {
|
||||
background: #d1d5db;
|
||||
color: #626E7E;
|
||||
}
|
||||
.calendar tbody tr td.hour span {
|
||||
transform: translateY(16px) rotate(270deg);
|
||||
-webkit-transform: translateY(16px) rotate(270deg);
|
||||
-moz-transform: translateY(16px) rotate(270deg);
|
||||
}
|
||||
}
|
|
@ -304,7 +304,8 @@ th {
|
|||
|
||||
.img-thumbnail,
|
||||
body {
|
||||
background-color: #040304
|
||||
background-color: #040304;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
|
@ -1585,7 +1586,7 @@ h6 {
|
|||
|
||||
.h1,
|
||||
h1 {
|
||||
font-size: 48px
|
||||
font-size: 38px
|
||||
}
|
||||
|
||||
.h2,
|
||||
|
@ -1834,6 +1835,11 @@ dd {
|
|||
}
|
||||
|
||||
@media (min-width:768px) {
|
||||
.h1,
|
||||
h1 {
|
||||
font-size: 48px
|
||||
}
|
||||
|
||||
.dl-horizontal dt {
|
||||
float: left;
|
||||
width: 160px;
|
||||
|
@ -8014,4 +8020,4 @@ tr.active {
|
|||
::-moz-selection {
|
||||
background: #3EE77B;
|
||||
color: #040304
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
<?php
|
||||
include __DIR__.'/../header.php';
|
||||
?>
|
||||
<h1>how to stream</h1>
|
||||
|
||||
<dl>
|
||||
<dt>Server:</dt>
|
||||
<dd>azuracast.tilderadio.org</dd>
|
||||
<dt>Port:</dt>
|
||||
<dd>8005</dd>
|
||||
<dt>Mount Name:</dt>
|
||||
<dd>/</dd>
|
||||
</dl>
|
||||
|
||||
<p>Setup info for most streaming clients is available on the
|
||||
<a href="https://www.azuracast.com/station-management/djs/streaming_software.html">
|
||||
azuracast wiki</a></p>
|
||||
<p>if you're having trouble, try setting your software to broadcast 128kbps mp3</p>
|
||||
<p><a href="/butt.cfg.txt">example butt config</a></p>
|
||||
|
||||
<p>pulseaudio wizardry</p>
|
||||
<pre>
|
||||
# Setup virtual null sink & two loopbacks
|
||||
pulseaudio -k
|
||||
pactl load-module module-null-sink sink_name=v1 sink_properties=device.description="v1"
|
||||
pactl load-module module-loopback sink=v1
|
||||
pactl load-module module-loopback sink=v1
|
||||
|
||||
# Run pavucontrol
|
||||
pavucontrol &
|
||||
|
||||
# Configure
|
||||
|
||||
Playback:
|
||||
Loopbacks => v1
|
||||
Applications => Headset stereo device
|
||||
|
||||
Recording:
|
||||
Butt => Monitor of v1
|
||||
Loopback 1 => Headset stereo device
|
||||
Loopback 2 => Monitor of input device
|
||||
|
||||
All volume sliders to 100%
|
||||
</pre>
|
||||
|
||||
<p>the test stream is available on port 8015 and you can listen to it
|
||||
<a href="https://azuracast.tilderadio.org/public/test">here</a>
|
||||
or <a href="https://azuracast.tilderadio.org/radio/8010/radio.ogg">here as ogg</a></p>
|
||||
|
||||
<?php
|
||||
include __DIR__.'/../footer.php';
|
3
dmca.php
3
dmca.php
|
@ -1,7 +1,4 @@
|
|||
<?php include 'header.php'; ?>
|
||||
<h1><a href="https://tilderadio.org"><img style="width:75px;" src="./logos/tilderadio-green.png">tilderadio.org</a></h1>
|
||||
<br>
|
||||
<br>
|
||||
<p><b>Digital Millennium Copyright Act ("DMCA")</b></p>
|
||||
|
||||
<p>tilderadio respects the intellectual property rights of others. Per the DMCA, tilderadio will respond expeditiously to claims of copyright infringement on the Site if submitted to tilderadio's Copyright Agent as described below. Upon receipt of a notice alleging copyright infringement, tilderadio will take whatever action it deems appropriate within its sole discretion, including removal of the allegedly infringing materials and termination of access for repeat infringers of copyright protected content.</p>
|
||||
|
|
17
footer.php
17
footer.php
|
@ -1,10 +1,11 @@
|
|||
</div>
|
||||
<footer class="text-center">
|
||||
<a href="https://tildegit.org/tilderadio/site">page source</a> | <a href="dmca.php">DMCA</a>
|
||||
</footer>
|
||||
<footer class="text-center">
|
||||
<a href="https://tildegit.org/tilderadio/site">page source</a> | <a href="/dmca.php">DMCA</a>
|
||||
</footer>
|
||||
|
||||
<script src="/js/jquery.min.js"></script>
|
||||
<script src="/js/bootstrap.min.js"></script>
|
||||
</body>
|
||||
</div>
|
||||
|
||||
</html>
|
||||
<script src="/js/jquery.min.js"></script>
|
||||
<script src="/js/bootstrap.min.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
21
header.php
21
header.php
|
@ -8,7 +8,8 @@
|
|||
</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="/css/hacker.css">
|
||||
<link rel="icon" type="image/png" href="https://tilderadio.org/logos/tilderadio-green.png">
|
||||
<link rel="icon" type="image/png" href="/logos/tilderadio.png">
|
||||
<?=isset($additional_head) ? PHP_EOL . " " . $additional_head . PHP_EOL : ""?>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
@ -18,17 +19,27 @@
|
|||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"> </span>
|
||||
<span class="icon-bar"> </span>
|
||||
<span class="icon-bar"> </span>
|
||||
</button>
|
||||
</div>
|
||||
<div id="navbar" class="navbar-collapse collapse">
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li><a href="/">home</a></li>
|
||||
<li><a href="/schedule/">schedule</a></li>
|
||||
<li><a href="/listen">listen now</a></li>
|
||||
</ul>
|
||||
|
||||
<!--/.nav-collapse -->
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<br><br>
|
||||
</div>
|
||||
<br>
|
||||
<br>
|
||||
<div class="container">
|
||||
<h1>
|
||||
<a href="/"><img style="width:72px;margin-top:-30px;margin-right:5px;" src="/logos/tilderadio.png" alt="">tilderadio.org</a>
|
||||
</h1>
|
||||
<hr>
|
51
index.php
51
index.php
|
@ -1,21 +1,50 @@
|
|||
<?php include 'header.php'; ?>
|
||||
|
||||
<h1><a href="https://tilderadio.org"><img style="width:75px;" src="./logos/tilderadio-green.png">tilderadio.org</a></h1>
|
||||
<p><em><?php include 'schedule/nextdj.php'; ?></em></p>
|
||||
<blockquote><?=json_decode(file_get_contents("https://bot.tildegit.org/api/slogan"))?></blockquote>
|
||||
|
||||
<br>
|
||||
<br>
|
||||
<p>
|
||||
TildeRadio is Internet radio streamed by / for users of the tildeverse.
|
||||
<a href="https://tildeverse.org/">tildeverse.org</a>
|
||||
</p>
|
||||
<br>
|
||||
<br>
|
||||
<p>tilderadio is internet radio streamed by / for users of the <a href="https://tildeverse.org/">tildeverse</a>.</p>
|
||||
|
||||
<hr>
|
||||
<p>
|
||||
<iframe src="https://radio.tildeverse.org/public/tilderadio/embed" frameborder="0" allowtransparency="true" style="width: 100%; min-height: 160px; border: 0;"></iframe>
|
||||
<a href="https://tilde.chat/kiwi/#tilderadio" target="_blank">Join us in #tilderadio</a>,
|
||||
our dedicated IRC channel on <a href="https://tilde.chat">tilde.chat</a>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
follow us on mastodon where we announce when DJ's go live!
|
||||
<a href="https://tilde.zone/@tilderadio" target="_blank">@tilderadio</a>
|
||||
</p>
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
|
||||
<h2>how to listen</h2>
|
||||
|
||||
<p>use the player at the bottom of the page, or use one of the following links in your media player of choice or visit them in your browser:</p>
|
||||
|
||||
<ul>
|
||||
<li><a href="https://tilderadio.org/listen">https://tilderadio.org/listen</a></li>
|
||||
<li>
|
||||
<a href="https://azuracast.tilderadio.org/radio/8000/radio.ogg">
|
||||
https://azuracast.tilderadio.org/radio/8000/radio.ogg</a> (default, 192 kbps ogg vorbis)
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://azuracast.tilderadio.org/radio/8000/320k.ogg">
|
||||
https://azuracast.tilderadio.org/radio/8000/320k.ogg</a> (320 kbps ogg vorbis)
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://azuracast.tilderadio.org/radio/8000/radio.mp3">
|
||||
https://azuracast.tilderadio.org/radio/8000/radio.mp3</a> (192 kbps mp3)
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://azuracast.tilderadio.org/radio/8000/320k.mp3">
|
||||
https://azuracast.tilderadio.org/radio/8000/320k.mp3</a> (320 kbps mp3)
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<hr>
|
||||
<iframe src="https://azuracast.tilderadio.org/public/tilderadio/embed" style="width: 100%; min-height: 160px; border: 0;"></iframe>
|
||||
|
||||
<?php include 'footer.php'; ?>
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 36 KiB |
Binary file not shown.
After Width: | Height: | Size: 26 KiB |
|
@ -0,0 +1 @@
|
|||
apikey.php
|
|
@ -0,0 +1,2 @@
|
|||
<?php
|
||||
$apikey = "my azuracast api key";
|
|
@ -0,0 +1,92 @@
|
|||
<section class="calendar-wrapper">
|
||||
<h4>upcoming broadcasts</h4>
|
||||
<?php
|
||||
function check_in_range($start_date, $end_date, $checkdate) {
|
||||
$start_ts = strtotime($start_date);
|
||||
$end_ts = strtotime($end_date);
|
||||
$ch_ts = strtotime($checkdate);
|
||||
return (($ch_ts >= $start_ts) && ($ch_ts < $end_ts));
|
||||
}
|
||||
|
||||
// Create a date range between the schedule start and end dates
|
||||
$begin = new DateTime($schedule[0]['start']);
|
||||
$end = new DateTime(end($schedule)['start']);
|
||||
$daterange = new DatePeriod($begin, new DateInterval('P1D'), $end);
|
||||
?>
|
||||
|
||||
<table class="calendar">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<?php
|
||||
// Loop over our date range to draw the headers
|
||||
foreach($daterange as $date){ ?>
|
||||
<th>
|
||||
<span class="day"><?php echo $date->format("d") ?></span>
|
||||
<span class="long"><?php echo $date->format("l") ?></span>
|
||||
<span class="short"><?php echo $date->format("D") ?></span>
|
||||
</th>
|
||||
<?php } ?>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php
|
||||
// time will count us by 30-min increments through the day
|
||||
$time = mktime(0, 0, 0, 1, 1);
|
||||
// Loop over the day in 30 min increments
|
||||
for ($i = 0; $i < 86400; $i += 1800) { ?>
|
||||
<tr>
|
||||
<?php
|
||||
// Only show row if we're on a full hour block. It's a rowspan 2
|
||||
if ((($i / 1800) % 2) === 0 ) { ?>
|
||||
<td class="hour" rowspan="2"><span><?php echo date('H', $time + $i) ?>:00</span></td>
|
||||
<?php } ?>
|
||||
|
||||
<?php
|
||||
$now = new DateTime();
|
||||
$halfhour = new DateInterval('PT30M');
|
||||
$wrotepointer = false;
|
||||
// Loop over each day of the week for this 30 min span
|
||||
foreach ($daterange as $date) {
|
||||
// merge date (changing days) and time (incrementing by 30 min) to draw calendar by row.
|
||||
$merge = new DateTime($date->format('Y-m-d') . ' ' . date('H:i:s', $time + $i));
|
||||
// Set id for this time span, for referencing in JS.
|
||||
$props = 'id="show-' . $merge->getTimestamp() . '"';
|
||||
// We'll now use $merge to see if any shows are airing at this time
|
||||
$matchedshow = null;
|
||||
foreach ($schedule as $show) {
|
||||
if (check_in_range($show['start'], $show['end'], $merge->format('Y-m-d H:i:s'))) {
|
||||
$matchedshow = $show;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$active = false;
|
||||
if (!$wrotepointer) {
|
||||
// If current time is in this range, draw pointer.
|
||||
$end = DateTimeImmutable::createFromMutable($merge)->add($halfhour);
|
||||
if ($now >= $merge && $now < $end) {
|
||||
$active = true;
|
||||
$props .= ' class="active"';
|
||||
}
|
||||
}
|
||||
echo "<td $props>";
|
||||
if ($matchedshow) {
|
||||
echo '<div class="show-title">' . $matchedshow['title'] . '</div>';
|
||||
// if no match was found, leave an empty node
|
||||
}
|
||||
if ($active) {
|
||||
// Cell height here should be synced with height of '.calendar tbody tr td' in ../css/calendar.css
|
||||
$height = 32;
|
||||
$top = round(date_diff($merge, $now)->format('%i') / 30 * ($height-1));
|
||||
echo '<div id="pointer" style="top:'.$top.'px"></div>';
|
||||
$wrotepointer = true;
|
||||
}
|
||||
echo "</td>\n";
|
||||
}
|
||||
?>
|
||||
</tr>
|
||||
<?php } ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
|
@ -0,0 +1,75 @@
|
|||
<?php
|
||||
$from = gmdate("Y-m-d\T00:00:00\Z", strtotime("today"));
|
||||
$to = gmdate("Y-m-d\T00:00:00\Z", strtotime("today + 8 days"));
|
||||
include 'schedule.php';
|
||||
|
||||
function ics_formatdate($date) {
|
||||
return gmdate("Ymd\THis\Z", strtotime($date));
|
||||
}
|
||||
|
||||
// ICS generation. Here be dragons.
|
||||
// I created the file using a Python script and reverse-engineered it to figure this out.
|
||||
|
||||
// text/calendar MIME type for ICS files
|
||||
header("Content-Type: text/calendar");
|
||||
// It should download as a .ics file
|
||||
header('Content-Disposition: attachment; filename="tilderadio.ics"');
|
||||
|
||||
// the iCalendar Validator throws a hissy fit if lines aren't CRLF terminated
|
||||
define("ICS_EOL","\r\n");
|
||||
|
||||
// Header.
|
||||
echo "BEGIN:VCALENDAR" . ICS_EOL;
|
||||
echo "VERSION:2.0" . ICS_EOL;
|
||||
echo "PRODID:tilderadio schedule" . ICS_EOL;
|
||||
echo "DTSTAMP:" . ics_formatdate("now") . ICS_EOL;
|
||||
|
||||
// A list of event IDs that we have on the calendar, to avoid duplication
|
||||
$event_ids = [];
|
||||
|
||||
foreach ($schedule as $event) {
|
||||
$id = strval($event["id"]) . gmdate("DHis", strtotime($event["start"])) . gmdate("DHis", strtotime($event["end"]));
|
||||
|
||||
if (!in_array($id,$event_ids)) {
|
||||
array_push($event_ids, $id);
|
||||
// The VEVENT structure's pretty easy to generate, especially since we're already in UTC.
|
||||
echo "BEGIN:VEVENT" . ICS_EOL;
|
||||
// First, we need a creation date.
|
||||
// Just go with now.
|
||||
echo "DTSTAMP:" . ics_formatdate("now") . ICS_EOL;
|
||||
// Next, the event start and end.
|
||||
echo "DTEND:" . ics_formatdate($event["end"]) . ICS_EOL;
|
||||
echo "DTSTART:" . ics_formatdate($event["start"]) . ICS_EOL;
|
||||
// Next, the recurrence rule.
|
||||
if ($event["title"] != "tomasino") {
|
||||
// We assume the format is weekly. (tomasino is the only DJ to have requested any other frequency.)
|
||||
echo "RRULE:FREQ=WEEKLY" . ICS_EOL;
|
||||
} else {
|
||||
// TTT only comes on the last Sunday of the month, 23:30:00 UTC to 01:00:00 UTC
|
||||
if (gmdate("His", strtotime($event["start"])) == "233000" && gmdate("His", strtotime($event["end"])) == "010000") {
|
||||
echo "RRULE:FREQ=MONTHLY;WKST=SU;BYDAY=SU;BYSETPOS=-1" . ICS_EOL;
|
||||
} else {
|
||||
// his other shows are weekly though
|
||||
echo "RRULE:FREQ=WEEKLY" . ICS_EOL;
|
||||
}
|
||||
}
|
||||
// Next, the event title, or "summary" as the spec calls it.
|
||||
echo "SUMMARY:DJ " . $event["title"] . ICS_EOL;
|
||||
// Finally, a unique ID for this event.
|
||||
// To make absolutely certain we don't repeat the same event ID, I decided to use a SHA256 hash of the event structure.
|
||||
echo "UID:";
|
||||
echo hash("sha256", json_encode($event));
|
||||
// to avoid the validator complaining about lines longer than 75 characters, split after the hash
|
||||
echo ICS_EOL . " ";
|
||||
// Now finish the address UID
|
||||
echo "@tilderadio.org" . ICS_EOL;
|
||||
// Finally, close the VEVENT structure.
|
||||
echo "END:VEVENT" . ICS_EOL;
|
||||
// Next event?
|
||||
}
|
||||
}
|
||||
|
||||
// Finally, close out the VCALENDAR structure.
|
||||
echo "END:VCALENDAR" . ICS_EOL;
|
||||
|
||||
?>
|
|
@ -1,32 +1,121 @@
|
|||
<?php
|
||||
include '../header.php';
|
||||
$schedule = json_decode(file_get_contents("https://radio.tildeverse.org/api/station/1/schedule?rows=1000&now=last%20monday"), true);
|
||||
|
||||
date_default_timezone_set("UTC");
|
||||
function formatdate($date) {
|
||||
return date("D H:i", strtotime($date));
|
||||
}
|
||||
$additional_head='<link rel="stylesheet" href="/css/calendar.css">
|
||||
<link rel="alternate" type="text/calendar" href="https://tilderadio.org/schedule/ics.php">';
|
||||
include __DIR__ . '/../header.php';
|
||||
include 'schedule.php';
|
||||
?>
|
||||
|
||||
<h1><a href="https://tilderadio.org"><img style="width:75px;" src="../logos/tilderadio-green.png">tilderadio.org</a></h1>
|
||||
<h4>upcoming broadcasts</h4>
|
||||
<p>all times in UTC</p>
|
||||
<p>
|
||||
all times in UTC. current time is <span id="utcdate"><?=formatdate("now")?></span>
|
||||
<span class="pointer-label-wrapper">(</span><span class="pointer-label">—</span><span class="pointer-label-wrapper">)</span>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
this schedule is also available in <a href="https://icalendar.org/validator.html?url=https://tilderadio.org/schedule/ics.php">iCalendar format</a>.
|
||||
point your calendar client at <code>https://tilderadio.org/schedule/ics.php</code>.
|
||||
</p>
|
||||
|
||||
<?php
|
||||
include 'calendar.php';
|
||||
?>
|
||||
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>dj</th>
|
||||
<th>time</th>
|
||||
<th>start</th>
|
||||
<th>end</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($schedule as $item): ?>
|
||||
<tr>
|
||||
<td><?=$item["name"]?></td>
|
||||
<td><?=formatdate($item["start"])?> - <?=formatdate($item["end"])?></td>
|
||||
<td><?=$item["title"]?></td>
|
||||
<td><?=formatdate($item["start"])?></td>
|
||||
<td><?=formatdate($item["end"])?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<?php include '../footer.php'; ?>
|
||||
<script>
|
||||
let halfHour = 1800000;
|
||||
// Gather schedule table cells by timestamp
|
||||
const cells = document.querySelectorAll('.calendar tbody td');
|
||||
let ranges = [];
|
||||
for (const cell of Array.from(cells)) {
|
||||
const m = /^show-(\d*)$/.exec(cell.id);
|
||||
if (!m) continue;
|
||||
ranges.push({
|
||||
startTime: m[1]*1000,
|
||||
cell,
|
||||
});
|
||||
}
|
||||
ranges.sort((a, b) => {
|
||||
return a.startTime - b.startTime;
|
||||
});
|
||||
let currentRangeI = 0;
|
||||
|
||||
function getCurrentRange(now) {
|
||||
for (i = currentRangeI; i < ranges.length; i++) {
|
||||
const range = ranges[i];
|
||||
if (!range) continue;
|
||||
const rangeStart = range.startTime;
|
||||
if (rangeStart <= now && (rangeStart + halfHour) > now) {
|
||||
return range;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update the timer and pointer as time passes.
|
||||
// Because PHP gives the time on page load, people who disable Javascript won't be missing out on much.
|
||||
let months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
|
||||
let daysOfWeek = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
|
||||
|
||||
function update_date() {
|
||||
var d = new Date();
|
||||
document.getElementById("utcdate").innerText =
|
||||
daysOfWeek[d.getUTCDay()] + " " + months[d.getUTCMonth()] + " " +
|
||||
d.getUTCDate().toString() + " " + d.getUTCHours().toString().padStart(2,'0') +
|
||||
":" + d.getUTCMinutes().toString().padStart(2,'0');
|
||||
setTimeout(update_date, 15000);
|
||||
updatePointer(d);
|
||||
}
|
||||
setTimeout(update_date, 15000);
|
||||
|
||||
let pointer = document.getElementById("pointer");
|
||||
// Create pointer in case it wasn't added in the page generation.
|
||||
if (!pointer) {
|
||||
pointer = document.createElement('div');
|
||||
pointer.id = 'pointer';
|
||||
}
|
||||
|
||||
let prevRange = getCurrentRange(new Date().getTime());
|
||||
function updatePointer(d) {
|
||||
// Find current cell
|
||||
const range = getCurrentRange(d.getTime());
|
||||
if (!range) {
|
||||
// Done with schedule. Remove pointer. Need to refresh.
|
||||
if (prevRange) {
|
||||
prevRange.cell.removeChild(pointer);
|
||||
prevRange.cell.classList.remove('active');
|
||||
prevRange = null;
|
||||
}
|
||||
return;
|
||||
}
|
||||
// Move pointer to different cell if changed cell.
|
||||
if (range !== prevRange) {
|
||||
if (prevRange) prevRange.cell.classList.remove('active');
|
||||
range.cell.classList.add('active');
|
||||
range.cell.appendChild(pointer);
|
||||
prevRange = range;
|
||||
}
|
||||
// Move pointer based on time in current cell.
|
||||
const progress = (d.getTime() - range.startTime) / halfHour;
|
||||
pointer.style.top = (progress * (range.cell.offsetHeight - 1)).toFixed(0) + 'px';
|
||||
}
|
||||
// Update pointer immediately
|
||||
updatePointer(new Date());
|
||||
</script>
|
||||
|
||||
<?php include __DIR__.'/../footer.php'; ?>
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
<?php
|
||||
include 'apikey.php';
|
||||
|
||||
if (empty($apikey)) {
|
||||
/* If we don't have the API key, assume we're developing. Pull data from live version of this file */
|
||||
$context = stream_context_create([
|
||||
"http" => [
|
||||
"method" => "GET",
|
||||
]
|
||||
]);
|
||||
$schedule = json_decode(file_get_contents("https://tilderadio.org/schedule/nextdj.php?json=true", false, $context), true);
|
||||
} else {
|
||||
$context = stream_context_create([
|
||||
"http" => [
|
||||
"method" => "GET",
|
||||
"header" => "X-API-Key: $apikey\r\n"
|
||||
]
|
||||
]);
|
||||
|
||||
$schedule = json_decode(
|
||||
file_get_contents(
|
||||
"https://azuracast.tilderadio.org/api/station/1/schedule?rows=2",
|
||||
false,
|
||||
$context
|
||||
),
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
if (isset($_GET["json"]) && $_GET["json"] === "yes") {
|
||||
echo json_encode($schedule);
|
||||
} else {
|
||||
$data = $schedule[0];
|
||||
if ((strtotime($data["start"]) - strtotime("now")) < 0) {
|
||||
echo $data["name"] . " should be streaming now, and ";
|
||||
$data = $schedule[1];
|
||||
}
|
||||
echo $data["name"] . " will stream at " . gmdate("D M d H:i", strtotime($data["start"])) . " UTC (in ";
|
||||
|
||||
$diff = strtotime($data["start"]) - strtotime("now");
|
||||
if ($diff < 60) {
|
||||
echo "" . $diff . " seconds)";
|
||||
} else {
|
||||
$minutes = intdiv($diff, 60);
|
||||
$seconds = $diff % 60;
|
||||
if ($minutes<60) {
|
||||
echo "" . $minutes . " minutes and " . $seconds . " seconds)";
|
||||
} else {
|
||||
$hours = intdiv($minutes, 60);
|
||||
$minutes = $minutes % 60;
|
||||
echo "" . $hours . " hours, " . $minutes . " minutes and " . $seconds . " seconds)";
|
||||
}
|
||||
}
|
||||
echo ".";
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
<?php
|
||||
include 'apikey.php';
|
||||
|
||||
if (empty($apikey)) {
|
||||
/* If we don't have the API key, assume we're developing. Pull data from live version of this file */
|
||||
$context = stream_context_create([
|
||||
"http" => [
|
||||
"method" => "GET",
|
||||
]
|
||||
]);
|
||||
$schedule = json_decode(file_get_contents("https://tilderadio.org/schedule/schedule.php?json=yes", false, $context), true);
|
||||
} else {
|
||||
$context = stream_context_create([
|
||||
"http" => [
|
||||
"method" => "GET",
|
||||
"header" => "X-API-Key: $apikey\r\n"
|
||||
]
|
||||
]);
|
||||
|
||||
// allow ics.php to overwrite $from and $to
|
||||
if (!isset($from, $to)) {
|
||||
$from = gmdate("Y-m-d\TH:i:s\Z", strtotime("today"));
|
||||
$to = gmdate("Y-m-d\TH:i:s\Z", strtotime("today + 8 days"));
|
||||
}
|
||||
|
||||
$schedule = json_decode(
|
||||
file_get_contents(
|
||||
"https://azuracast.tilderadio.org/api/station/1/streamers/schedule?start=$from&end=$to",
|
||||
false,
|
||||
$context
|
||||
),
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
usort($schedule, function ($a, $b) {
|
||||
return $a["start"] <=> $b["start"];
|
||||
});
|
||||
|
||||
if (isset($_GET["json"]) && $_GET["json"] === "yes")
|
||||
echo json_encode($schedule);
|
||||
|
||||
function formatdate($date) {
|
||||
return gmdate("D M d H:i", strtotime($date));
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
The soundtrack in your cat's head
|
||||
Your cat says she likes us better
|
||||
radio for the people
|
||||
radio by bots, for bots
|
||||
On the Internet, no one knows you're a weakly superhuman AGI with a really good speech synth module.
|
||||
We found your cat
|
||||
cats.
|
||||
a hacker's excuse for radio
|
||||
0::1 is where the heart is
|
||||
now with the longest slogan in human history
|
||||
reticulating splines
|
||||
Hold the shift
|
||||
a very serious place for very serious happenings
|
||||
sorry, this extension is invalid
|
||||
This is the radio you were looking for
|
||||
Not The Illuminati, We Promise
|
||||
the radio is coming from INSIDE the house
|
||||
Dont Sue Us!
|
||||
it's radio minus radio
|
||||
where your passwords are stored in plain text
|
||||
Like radio. Only better.
|
||||
tomasino 0wnz y0u!
|
||||
that escalated quickly
|
||||
music for your brain meats
|
||||
003 Days Since Last Workplace Accident
|
||||
*heavy breathing*
|
||||
Dizzy? We stop the world from spinning
|
||||
Where Disney Princesses Go Slumming
|
||||
any slogan but "eat the poop or die"
|
||||
that's not what she said!
|
||||
not product placement, we promise! (shop.tildeverse.org)
|
||||
SYNTH TUBA JAZZ VOMIT
|
Loading…
Reference in New Issue