Initial commit

This commit is contained in:
boyska 2022-01-20 22:24:33 +01:00
commit 9c7b61256f
22 changed files with 480 additions and 0 deletions

14
Makefile Normal file
View file

@ -0,0 +1,14 @@
all: index.html spec.html radiomanifest.zip
testXMLs := $(shell find shows radio-manifest -type f -name '*.xml')
%.html: %.asciidoc
asciidoctor -b html5 $<
radiomanifest.zip: radio-manifest.xsd shows.xsd radio-manifest/Makefile shows/Makefile $(testXMLs)
zip -r $@ $^
test:
make -C shows/
make -C radio-manifest/
.PHONY: all

21
index.asciidoc Normal file
View file

@ -0,0 +1,21 @@
= Radiomanifest
CiurmaPirata
Scopo di questa specifica è quello di fare sì che il sito di una radio possa esporre in maniera strutturata alcune informazioni sul suo sito.
* link:spec.html[read the spec]
* link:radio-manifest.xsd[XSD radio-manifest]
* link:shows.xsd[XSD shows]
* link:radiomanifest.zip[examples dataset]
* link:examples/[example websites]
== Implementations
=== Clients
* link:https://git.lattuga.net/boyska/radiomanifest.js/[radiomanifest.js]
=== Server
none yet?

53
radio-manifest.xsd Normal file
View file

@ -0,0 +1,53 @@
<?xml version="1.0" encoding="UTF-8" ?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="radio-manifest">
<xs:complexType>
<xs:all>
<xs:element name="schedule" minOccurs="0">
<xs:complexType>
<xs:attribute name="src" type="srctype" use="required"/>
</xs:complexType>
</xs:element>
<xs:element name="shows" minOccurs="0">
<xs:complexType>
<xs:attribute name="src" type="srctype" use="required"/>
</xs:complexType>
</xs:element>
<xs:element name="streaming" minOccurs="0">
<xs:complexType>
<xs:sequence>
<xs:element name="source" maxOccurs="unbounded">
<xs:complexType>
<xs:attribute name="name" type="xs:string" use="required"/>
<xs:attribute name="src" type="srctype" use="required"/>
<xs:attribute name="priority" type="xs:byte" default="1" />
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="feed" minOccurs="0">
<xs:complexType>
<xs:attribute name="src" type="srctype" use="required" />
<xs:attribute name="type" type="xs:string" />
</xs:complexType>
</xs:element>
</xs:all>
</xs:complexType>
</xs:element>
<xs:simpleType name="srctype">
<xs:restriction base="xs:anyURI">
<xs:minLength value="1" />
</xs:restriction>
</xs:simpleType>
</xs:schema>

14
radio-manifest/Makefile Normal file
View file

@ -0,0 +1,14 @@
VALID = examples/*.xml
NONVALID = failing/*.xml
SCHEMA = ../radio-manifest.xsd
all: test
test: $(VALID) $(NONVALID)
examples/%.xml: $(SCHEMA)
xmllint --schema $(SCHEMA) --noout $@
failing/%.xml: $(SCHEMA)
if xmllint --schema $(SCHEMA) --noout $@ 2> /dev/null; then false; else true; fi
.PHONY: all test

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<radio-manifest>
<schedule src="https://www.radioexample.org/palinsesto.ics" />
<shows src="https://www.radioexample.org/shows.xml" />
<streaming>
<source name="hi-quality" src="https://www.radioexample.org/stream.m3u" />
<source name="lo-quality" src="https://www.radioexample.org/stream-low.m3u" />
</streaming>
<feed src="https://www.radioexample.org/all.xml" type="application/atom+xml" />
</radio-manifest>

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8" ?>
<radio-manifest>
</radio-manifest>

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<radio-manifest >
<schedule src="https://www.radioexample.org/palinsesto.ics" />
<shows src="https://www.radioexample.org/shows.xml" />
<streaming>
<source name="hi-quality" src="https://www.radioexample.org/stream.m3u" />
<source name="lo-quality" src="https://www.radioexample.org/stream-low.m3u" />
</streaming>
<feed src="https://www.radioexample.org/all.xml" />
</radio-manifest>

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<radio-manifest>
<streaming>
<source name="hi-quality" src="https://www.radioexample.org/stream.m3u" />
<source name="lo-quality" src="https://www.radioexample.org/stream-low.m3u" />
</streaming>
<shows src="https://www.radioexample.org/shows.xml" />
<schedule src="https://www.radioexample.org/palinsesto.ics" />
</radio-manifest>

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<radio-manifest>
<schedule src="https://www.radioexample.org/palinsesto.ics" />
<shows src="https://www.radioexample.org/shows.xml" />
<streaming>
<source name="dont-use-me" priority="-100" src="https://www.radioexample.org/stream-internal.m3u" />
<source name="hi-quality" priority="100" src="https://www.radioexample.org/stream.m3u" />
</streaming>
</radio-manifest>

View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8" ?>
<radio-manifest>
<schedule src="https://www.radioexample.org/palinsesto.ics" />
<shows src="https://www.radioexample.org/shows.xml" />
<streaming>
<source name="lo-quality" src="https://www.radioexample.org/stream-low.m3u" />
</streaming>
</radio-manifest>

View file

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8" ?>
<radio-manifest
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://boyska.degenerazione.xyz/radiomanifest/radiomanifest.xsd"
>
<schedule src="https://www.radioexample.org/palinsesto.ics" />
<shows src="https://www.radioexample.org/shows.xml" />
<shows src="https://www.radioexample.org/shows.xml" />
<streaming>
<source name="hi-quality" src="https://www.radioexample.org/stream.m3u" />
<source name="lo-quality" src="https://www.radioexample.org/stream-low.m3u" />
</streaming>
</radio-manifest>

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
<radio-manifest>
<streaming>
</streaming>
</radio-manifest>

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<radio-manifest>
<schedule src="https://www.radioexample.org/palinsesto.ics" />
<shows src="https://www.radioexample.org/shows.xml" />
<streaming>
<source name="hi-quality" priority="128" src="https://www.radioexample.org/stream.m3u" />
</streaming>
</radio-manifest>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8" ?>
<radio-manifest>
<schedule src="https://www.radioexample.org/palinsesto.ics" />
<shows src="https://www.radioexample.org/shows.xml" />
<streaming>
<source name="hi-quality" priority="-129" src="https://www.radioexample.org/stream.m3u" />
</streaming>
</radio-manifest>

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<radio-manifest>
<schedule src="https://www.radioexample.org/palinsesto.ics" />
<shows src="https://www.radioexample.org/shows.xml" />
<streaming>
<source name="lo-quality" src="" />
</streaming>
</radio-manifest>

43
shows-table.xsl Normal file
View file

@ -0,0 +1,43 @@
<?xml version="1.0"?>
<xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<html>
<head>
<title>Shows list</title>
<style type="text/css">
table, th, td { border: 1px solid black; border-collapse: collapse; }
th, td { padding: 0.3em; }
thead { background-color: #ccc; }
</style>
</head>
<body>
<table>
<thead>
<tr>
<th>Name</th>
<th>Website</th>
<th>Feed</th>
<th>Schedule</th>
</tr>
</thead>
<tbody>
<xsl:for-each select="shows/show">
<tr>
<xsl:variable name="name" select="name"/>
<xsl:variable name="website" select="website"/>
<xsl:variable name="feed" select="feed"/>
<xsl:variable name="schedule" select="schedule"/>
<td><xsl:value-of select="name" /></td>
<td><a href="{$website}"><xsl:value-of select="website" /></a></td>
<td><a href="{$feed}"><xsl:value-of select="feed" /></a></td>
<td><a href="{$schedule}"><xsl:value-of select="schedule" /></a></td>
</tr>
</xsl:for-each>
</tbody>
</table>
</body>
</html>
</xsl:template>
</xsl:transform>

26
shows.xsd Normal file
View file

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8" ?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="shows">
<xs:complexType>
<xs:sequence>
<xs:element name="show" maxOccurs="unbounded" minOccurs="0">
<xs:complexType>
<xs:all>
<xs:element name="name" type="xs:string" />
<xs:element name="website" type="srctype" minOccurs="0" />
<xs:element name="feed" type="srctype" minOccurs="0" />
<xs:element name="schedule" type="srctype" minOccurs="0" />
</xs:all>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:simpleType name="srctype">
<xs:restriction base="xs:anyURI">
<xs:minLength value="1" />
</xs:restriction>
</xs:simpleType>
</xs:schema>

14
shows/Makefile Normal file
View file

@ -0,0 +1,14 @@
VALID = examples/*.xml
NONVALID = failing/*.xml
SCHEMA = ../shows.xsd
all: test
test: $(VALID) $(NONVALID)
examples/%.xml: $(SCHEMA)
xmllint --schema $(SCHEMA) --noout $@
failing/%.xml: $(SCHEMA)
if xmllint --schema $(SCHEMA) --noout $@ 2> /dev/null; then false; else true; fi
.PHONY: all test

15
shows/examples/basic.xml Normal file
View file

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="https://boyska.degenerazione.xyz/radiomanifest/shows-table.xsl"?>
<shows>
<show>
<name>Learn to cook in C++</name>
<website>http://radioexample.com/shows/learn-cook</website>
<feed>http://radioexample.com/shows/learn-cook/feed</feed>
<schedule>http://radioexample.com/shows/learn-cook.ics</schedule>
</show>
<show>
<name>Uncensored information</name>
<website>http://radioexample.com/shows/info</website>
<feed>http://radioexample.com/shows/info/feed</feed>
</show>
</shows>

4
shows/examples/empty.xml Normal file
View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8" ?>
<shows>
</shows>

8
shows/failing/noname.xml Normal file
View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8" ?>
<shows>
<show>
<website>http://radioexample.com/shows/learn-cook</website>
<feed>http://radioexample.com/shows/learn-cook/feed</feed>
<schedule>http://radioexample.com/shows/learn-cook.ics</schedule>
</show>
</shows>

168
spec.asciidoc Normal file
View file

@ -0,0 +1,168 @@
= Radiomanifest specification
CiurmaPirata
Un formato per fare sì che il sito di una radio possa esporre in maniera strutturata alcune informazioni sul
suo sito.
Tali informazioni sono, brevemente:
* il palinsesto
* l'elenco delle trasmissioni che vanno in onda in modo regolare, e per ciascuna di esse i principali feed
* gli indirizzi di streaming
Per permettere un'implementazione più semplice, ciascuna di queste informazioni segue una sua specifica, possibilmente appoggiandosi a specifiche già esistenti.
== RadioManifest
Al fine di avere un singolo punto in cui "esporre" le funzionalità supportate dal sito web, un sito può creare un manifest. Esso *DOVREBBE* (*SHOULD*) chiamarsi sempre ``${BASEURL}/radiomanifest.xml``.
Ecco un esempio di xml:
<?xml version="1.0" encoding="UTF-8" ?>
<radio-manifest>
<schedule src="https://www.radioexample.org/palinsesto.ics" />
<streaming>
<source name="hi-quality" priority="100" src="https://www.radioexample.org/stream.m3u" />
<source name="lo-quality" priority="50" src="https://www.radioexample.org/stream-low.m3u" />
</streaming>
<shows src="https://www.radioexample.org/shows.xml" />
<feed src="https://www.radioexample.org/all.xml" />
</radio-manifest>
(link:radio-manifest.xsd[Schema XSD])
Ovvero:
* 0 o 1 ``schedule``. Gli schedule sono definiti in link:https://icalendar.org/RFC-Specifications/iCalendar-RFC-5545/[formato iCalendar]
* 0 o 1 oggetti ``streaming``; contengono un qualsiasi numero (almeno 1) di source, le quali
** *POSSONO* avere un campo ``name``. Il campo ``name`` è fortemente
raccomandato se viene fornito più di un oggetto ``streaming``
** *DEVONO* avere un campo ``src``, il quale deve puntare ad una risorsa di tipo M3U.
** *POSSONO* avere un campo ``priority``, contenente un valore intero; se omesso, si assume il valore "1". Un numero più grande indica una
maggiore importanza. Il campo ``priority`` serve a definire l'ordinamento con cui i client *DOVREBBERO*
mostrare le playlist agli utenti, o a definire quale playlist vada usata, se il client sceglie automaticamente
senza proporrere all'utente. Il valore della priority è relativo alle altre source, non si riferisce ad altre
radio. Valori di priority minori di zero indicano che la source non dovrebbe essere resa visibile all'utente
in condizioni normali.
* 0 o 1 oggetti ``shows``. Il campo ``src`` *DEVE* puntare ad una risorsa di formato _shows_ (vedi di
seguito).
* 0 o 1 ``feed``. Il campo ``src`` *DEVE* puntare ad una risorsa di tipo feed.
### Implementazione client
Un client dovrebbe chiedere all'utente di fornire l'indirizzo base di un sito. Il manifest dovrebbe essere
rintracciabile andando all'indirizzo ``radiomanifest.xml`` relativo all'indirizzo di un sito.
Ad esempio, se l'utente inserisce "http://hosting.com/myradio/" il manifest *DEVE* essere cercato all'indirizzo
"http://hosting.com/myradio/radiomanifest.xml"
Quando un client vuole suonare lo streaming associato ad un radiomanifest, esso potrebbe voler mostrare
all'utente la scelta tra le varie source, oppure decidere automaticamente.
Se il client sceglie automaticamente:
* *DEVE* rispettare l'ordinamento delle priority.
* *DEVE* escludere le source con priority minore di zero.
* Qualora la riproduzione della source dovesse avere problemi, *DOVREBBE* passare alla successiva
* Se alcune source hanno uguale priority, *DOVREBBE* scegliere casualmente tra di esse
Se il client fa scegliere l'utente:
* *DEVE* rispettare l'ordinamento delle priority
* *DOVREBBE* omettere le source con priority minore di zero.
== Schedule
The goal of the schedule is to express the typical weekly table that is commonly found in radio websites. It is an iCalendar file. Each show should be a distinct ``VEVENT``.
The schedule *SHOULD* include at least events up until 1 week
The schedule *SHOULD* use recurrency rule where appropriate, so that the user is informed of this.
== Shows
The goal of the shows file is to:
* list shows
* provide useful metadata for each show, such as:
** dedicated feed
** link to specialized page for the show
Here is an example:
<?xml version="1.0" encoding="UTF-8" ?>
<shows>
<show>
<name>Learn to cook in C++</name>
<website>http://radioexample.com/shows/learn-cook</website>
<feed>http://radioexample.com/shows/learn-cook/feed</feed>
<schedule>http://radioexample.com/shows/learn-cook.ics</schedule>
</show>
<show>
<name>Uncensored information</name>
<website>http://radioexample.com/shows/info</website>
<feed>http://radioexample.com/shows/info/feed</feed>
</show>
</shows>
(link:shows.xsd[Schema XSD])
Only ``name`` is required. A schedule can point to an iCal resource. All entries in the calendar are assumed to
be part of the show.
=== Relationship with schedule
It's pretty clear that in many cases shows.xml and schedule.ics will benefit from being linked. How to do
that?
1. If there is an ``X-SHOW-ID``, and it is the same as ``<name>``
2. If ``CATEGORIES`` include ``<name>``
3. If ``SUMMARY`` is the same as ``<name>``
== Implementation details
=== HTTP Implementation
Clients:
- *MUST* provide an ``Accept`` header in every HTTP request. This will enable maximum flexibility in the
future, allowing clients and servers to smoothly move to new file formats
Servers:
- *MUST* implement CORS adequately. Every file related to this specification must be retrievable by a browser
on a different domain via a `GET`.
== Casi d'uso
=== Player
Supponiamo di voler realizzare un player di radio con feature avanzate, che sia però portabile su molte radio.
Data una lista di URL di siti web, è possibile fare un player che:
* supporti vari indirizzi di streaming in modo trasparente per l'utente
* permetta all'utente di scegliere il tipo di streaming, ad esempio se la radio fornisce streaming di diversa
qualità
* possa dire all'utente informazioni utili sul palinsesto della radio che sta ascoltando
* permetta di puntare allo storico, o di andare rapidamente alla pagina della trasmissione che sta
ascoltando.
Come capita spesso, questo può essere applicato anche alla scrittura di app che supportino molte radio, senza che però esse debbano essere limitate al solo streaming come è il caso attualmente (vedi le varie Transistor, RadioDroid, ecc.)
=== Radio automation
Se un radio automation (della radio A) vuole importare contenuti da un'altra radio B, esso può facilmente
fornire all'utente della radio A utili informazioni. Ad esempio, permette di vedere il palinsesto in forma
grafica, selezionare uno show, vedere un'anteprima del feed corrispondente, quindi importarlo in una specifica
fascia oraria.
=== radio-browser++
https://www.radio-browser.info/ fornisce molte info utili su delle radio. Grazie a radio-manifest, sarebbe più semplice:
* mantenere le informazioni in modo più semplice; finché una radio mantiene lo stesso sito web, può aggiornare la lista di url di streaming in modo automatico
* più informazioni; radio-browser potrebbe includere le informazioni dettagliate disponibili tramite ``<schedule>`` e ``<shows>`` per fornire informazioni aggiuntive.
=== RadioDroid++
RadioDroid is a fine Android app to listen to stream. We'd like to have an improved version of RadioDroid that
also includes features that RadioManifest provide. See the _Player_ use case.