Browse Source

Initial commit

boyska 2 years ago
commit
9c7b61256f

+ 14 - 0
Makefile

@@ -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 - 0
index.asciidoc

@@ -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 - 0
radio-manifest.xsd

@@ -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 - 0
radio-manifest/Makefile

@@ -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

+ 10 - 0
radio-manifest/examples/1.xml

@@ -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>

+ 4 - 0
radio-manifest/examples/empty.xml

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

+ 10 - 0
radio-manifest/examples/feed-no-type.xml

@@ -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>

+ 10 - 0
radio-manifest/examples/reordered.xml

@@ -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>
+

+ 10 - 0
radio-manifest/examples/source-prio.xml

@@ -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>
+

+ 9 - 0
radio-manifest/examples/streaming-1.xml

@@ -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>
+

+ 14 - 0
radio-manifest/failing/repeated.xml

@@ -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>
+

+ 5 - 0
radio-manifest/failing/streaming-empty.xml

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

+ 10 - 0
radio-manifest/failing/streaming-prio-toohigh.xml

@@ -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>
+
+

+ 8 - 0
radio-manifest/failing/streaming-prio-toolow.xml

@@ -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>

+ 10 - 0
radio-manifest/failing/streaming-source-empty.xml

@@ -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 - 0
shows-table.xsl

@@ -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 - 0
shows.xsd

@@ -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 - 0
shows/Makefile

@@ -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 - 0
shows/examples/basic.xml

@@ -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 - 0
shows/examples/empty.xml

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

+ 8 - 0
shows/failing/noname.xml

@@ -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 - 0
spec.asciidoc

@@ -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.