...
 
Commits (2)
......@@ -11,6 +11,18 @@ make_test:
- apk add grep make
- make test
make_docs:
stage: test
image: node:lts-alpine
tags:
- fast-io
script:
- apk add grep make
- make docs
artifacts:
paths:
- build/docs
make_deb_package:
stage: deploy
image: git-registry.hack-hro.de:443/lohro/lohrothek/thekno/build:buster
......
......@@ -12,5 +12,6 @@ clean::
include make.d/app.mk
include make.d/ci.mk
include make.d/deploy.mk
include make.d/docs.mk
include make.d/packaging.mk
include make.d/release.mk
# thekno
# Thekno
A Vue.js-based media library and stream web app for your community radio.
This project requires *NodeJS* and *npm*. If any of the commands below are
not available on your computer, see the [System Requirements](#system-requirements)
section at the end of this document.
![mockup.jpg](./docs/mockup_640.jpg)
## About
*Thekno* is a [Progressive Web App](https://en.wikipedia.org/wiki/Progressive_web_applications) and a reference implementation for a set of APIs formalized as part of this project.
By implementing these APIs any community radio will be able to offer a Web App for their listeners who will be able to install it similar to a native app. *Thekno* offers a variety of configuration options for providers including live streams, social platforms, content suggestions and more.
## Design Goals
* **Familiarity**
*Thekno* should feel and behave like other apps that people use on their phones and desktops.
* **Explorable**
*Thekno* should encourage users to explore the diversity of your community radio. Both new content and content that matches the user’s interest should be easy to find.
* **Privacy**
*Thekno* aims for client-storage only and embraces the data sovereignty of its users. Personal information should only be stored on the users device.
### Development and Testing Workflow
This project requires *NodeJS* and *npm*. If any of the commands below are
not available on your computer, see the [System Requirements](#system-requirements)
section at the end of this document.
The project uses numerous external dependencies that you can install with the
`npm install` command. After that you can just run `npm run serve` to start up
a development server.
*thekno* ships with fixtures that do not require a backend server, but in case
*Thekno* ships with fixtures that do not require a backend server, but in case
you do want to work with the [Lohrothek REST API](https://git.hack-hro.de/lohro/lohrothek/lohrothek-api)
you can do that. The development server started by `npm run serve` will proxy all
requests to the `/api/` endpoint to `http://localhost:8000/` by default. This
......@@ -35,7 +58,7 @@ VUE_APP_APIORIGIN=https://thek.lohro.de npm run serve
```
You may also use the included docker script `scripts/docker.sh`. It will build
and start *thekno* in an environment similar to those in production. Providing
and start *Thekno* in an environment similar to those in production. Providing
a [custom environment](#environments-and-configuration) is possible with the
`--build-arg` option like this:
......@@ -56,18 +79,18 @@ report any errors (that did not exist before 😅).
VueJS provides a build target out of the box. You can create a build with
`npm run build` and deploy the contents of the `build/dist` directory
afterwards. There is also a `Dockerfile` that can be used to create docker
images (in fact it’s used to automatically deploy *thekno* to our
images (in fact it’s used to automatically deploy *Thekno* to our
[staging server](https://thekno.farbdev.org)).
There will also be a deb package for Debian at a later point.
See [Custom Environments](#custom-environments) for a way to alter the
default behaviour of *thekno*.
default behaviour of *Thekno*.
## Environments and Configuration
*thekno* supports extensive environment runtime configuration. See the
*Thekno* supports extensive environment runtime configuration. See the
[Environment Configuration](./docs/environment-configuration.md) document for
more information on this topic.
......
It would be nice to re-arrange this into a RAML library but support for this
feature seems to be very buggy in raml2html so we have to fallback to includes :(.
collection:
get:
displayName: Retrieve <<itemType | !pluralize>>
description: Retrieves a list of <<itemType | !pluralize>>.
is:
- paged:
itemType: <<itemType>>
item:
get:
displayName: Retrieve <<itemType>>
description: Retrieves a single <<itemType>>.
responses:
200:
body:
application/json:
type: <<itemType>>
paged:
queryParameters:
page_size:
type: integer
description: >
The number of items in a collection to return per page.
The backend may define the default size of a page if none is
given in the query.
page:
type: integer
default: 1
minimum: 1
description: >
The page to display. Pages are numbered starting at 1.
responses:
200:
body:
application/json:
type: object
properties:
count:
type: integer
minimum: 0
description: >
Total number of items in this collection after all filters have been applied
that are not tied to pagination. If no filter has been defined for this collection
returns the overall number of items in the collection.
next:
type: string | nil
description: >
Absolute URL to the next page of this collection. If any filters have been applied to the
current query they MUST be contained in this URL. MUST be nil if there is no next page for
the currently applied filter set.
previous:
type: string | nil
description: >
Absolute URL to the previous page of this collection. If any filters have been applied to
the current query they MUST be contained in this URL. MUST be nil if the `page` query parameter
is `1`.
results:
type: array
items: <<itemType>>
type: datetime
format: rfc3339
description: >
A datetime string formatted according to
[RFC 3339](https://xml2rfc.tools.ietf.org/public/rfc/html/rfc3339.html#anchor14) (based on the popular
ISO 8601 standard) in the format `yyyy-mm-ddThh:mm:ss[.ff...]` followed by a timezone offset in the format
`+hh:mm`/`-hh:mm` or `Z` if the datetime is in UTC.
examples:
datetime_utc:
displayName: UTC DateTime
value: 2019-10-27T21:18:01.888059117Z
datetime_with_offset:
displayName: DateTime with offset
value: 2019-10-27T22:18:01.888059117+01:00
datetime_imprecise:
displayName: UTC DateTime without fractional precision
value: 2019-10-27T21:20:44Z
type: date-only
description: >
A date string formatted according to
[RFC 3339](https://xml2rfc.tools.ietf.org/public/rfc/html/rfc3339.html#anchor14) (based on the popular
ISO 8601 standard) in the format `yyyy-mm-dd`.
examples:
standard_date:
displayName: Standard Date
value: 2019-10-27
type: object
properties:
src:
type: string
description: >
Absolute URL to the image. The endpoint SHOULD support the `width`, `height` and `format` query
parameters.
The `width` and `height` parameters are optional and positive integers.
If both parameters have been provided the image should be cropped so it fits the specified dimensions.
The algorithm that defines the cutout for the image is left to the backend implementation.
If the only one parameter is given the image should be scaled to fit the specified width or height
while maintaining its aspect ratio. If neither `width` nor `height` have been given that backend
SHOULD return the image in its original size preferably not smaller than 1000px.
The `format` query parameter may be `PNG`, `JPEG` or `WEBP`. The backend SHOULD treat the format
as preferred by the client but MAY return any other content type. In case `format` has not been
provided the image MUST be returned in format the backend finds suitable.
The server SHOULD serve images with an `Access-Control-Allow-Origin` header that allows the client
to interact with the resource once it’s loaded.
#%RAML 1.0
title: Thekno Content API
baseUri: https://{content_api_host}/api/{version}
version: v1
baseUriParameters:
content_api_host:
type: string
description: >
Hostname or IP with optional port where the implementation of the
Thekno API is hosted. Note that the path component defined in the
baseUri is optional. The Thekno environment configuration allows you
to define any arbitrary origin + path as a starting point as long
as it properly implements the Thekno Content API.
protocols: [ HTTPS ]
mediaType: [ application/json ]
types: !include content/types.raml
resourceTypes: !include common/resource-types.raml
traits: !include common/traits.raml
documentation:
- title: Introduction
content: !include content/introduction.md
- title: Concepts
content: !include content/concepts.md
/recordings:
type:
collection: { itemType: Recording }
displayName: Recordings
description: >
Recordings are an important if not the most important part of the Thekno Content API. Each audio
file that should be available to the user is associated with a recording that encapsulates
information like a title and description, tags and other useful data.
get:
queryParameters:
broadcast:
type: integer
description: >
Only return recordings that have been aired as part of the specified broadcast.
series:
type: integer
description: >
Only return recordings that have been produced as part of the specified series.
is_available:
type: integer
enum: [0, 1]
description: >
Only return recordings that are (un-)available by any contraint the backend defines.
See the `can_be_streamed` property of the Recording data type.
tags:
type: array
items: string
description: >
Only return recordings that have been tagged with one of the specified tags. Tags can
be formatted as comma-separated values and refer to the tag slug **not** its label.
id:
type: array
items: integer
description: >
Only return recordings whose IDs are included in the filter. IDs can be formatted
as comma-separated values.
omit:
type: array
items: integer
description: >
Only return recordings whose IDs are **not** included in the filter. IDs can be formatted
as comma-separated values.
keywords:
type: string
description: >
Only return recordings that match the provided filter with a full-text search in the title
or description.
responses:
200:
body:
application/json:
examples:
list_available_recordings:
displayName: "Example: First Page of available Recordings"
value: !include content/examples/recordings.json
/{recordingId}:
type:
item: { itemType: Recording }
uriParameters:
recordingId:
type: integer
description: The id of the recording that should be retrieved.
get:
responses:
200:
body:
application/json:
examples:
common:
displayName: "Example: Common Recording"
value: !include content/examples/recording.json
/broadcasts:
type:
collection: { itemType: Broadcast }
displayName: Broadcasts
description: >
Broadcasts are basically episodes of a radio program series. They are used
to remind users of upcoming shows of their favorite program and to contextualize
multiple recordings.
get:
queryParameters:
series:
type: integer
description: >
Only return broadcasts that are associated with the specified series.
start_time__gt:
type: DateTime
description: >
Return only broadcasts that will start or have started after the specified time.
start_time__lt:
type: DateTime
description: >
Return only broadcasts that will start or have started before the specified time.
end_time__gt:
type: DateTime
description: >
Return only broadcasts that will end or have ended after the specified time.
end_time__lt:
type: DateTime
description: >
Return only broadcasts that will end or have ended after the specified time.
/{broadcastId}:
type:
item: { itemType: Broadcast }
uriParameters:
broadcastId:
type: integer
description: >
The id of the broadcast that should be retrieved.
/series:
type:
collection: { itemType: Series }
displayName: Series
description: >
A series represents a radio program or show that is usually scheduled to be aired
on a regular interval.
/{seriesId}:
type:
item: { itemType: Series }
uriParameters:
seriesId:
type: integer
description: >
The id of the series that should be retrieved.
/tags:
type:
collection: { itemType: Tag }
displayName: Tags
description: >
A tag is an ad-hoc categorization for content and are used to find similar content.
The tag API returns either BaseTag or AnnotatedTag instances so some properties might
be missing on some tags.
/{tagSlug}:
type:
item: { itemType: AnyTag }
uriParameters:
tagSlug:
type: string
description: >
The slug of the tag that should be retrieved.
The API is based on three pillars namely series’, broadcasts, and recordings.
A short nomenclature:
Series
: A radio program or show that is usually scheduled to be aired on a regular interval.
Broadcast
: An instance of a series or in less technical terms the time and date when a series has been aired.
Recording:
: A segment, report, or story that encapsulates the actual audio data.
Every series has multiple broadcasts and every broadcast usually has multiple recordings. It may also happen that a recording was aired more than once so that it appears in multiple broadcasts. Vice versa you may find recordings that are not associated to any broadcast. In that case the Recording was never aired and was only submitted for archival purposes.
See the following reference for details.
{
"bitrate": 96.263,
"codec": "audio/aac",
"container": "audio/mp4",
"src": "https://thek.lohro.de/api/v1/audiofiles/154/stream"
}
{
"bitrate": 192.0,
"codec": "audio/mpeg",
"container": null,
"src": "https://thek.lohro.de/api/v1/audiofiles/45/stream"
}
{
"bitrate": 64.114,
"codec": "audio/opus",
"container": "audio/ogg",
"src": "https://thek.lohro.de/api/v1/audiofiles/155/stream"
}
{
"id": 45,
"description": null,
"duration": 219,
"sources": [
{
"bitrate": 192.0,
"codec": "audio/mpeg",
"container": null,
"src": "https://thek.lohro.de/api/v1/audiofiles/45/stream"
},
{
"bitrate": 96.263,
"codec": "audio/aac",
"container": "audio/mp4",
"src": "https://thek.lohro.de/api/v1/audiofiles/154/stream"
},
{
"bitrate": 64.114,
"codec": "audio/opus",
"container": "audio/ogg",
"src": "https://thek.lohro.de/api/v1/audiofiles/155/stream"
},
{
"bitrate": 68.243,
"codec": "audio/mpeg",
"container": null,
"src": "https://thek.lohro.de/api/v1/audiofiles/156/stream"
}
]
}
{
"audio": [
{
"id": 45,
"description": "",
"duration": 219,
"sources": [
{
"bitrate": 192.0,
"codec": "audio/mpeg",
"container": null,
"src": "https://thek.lohro.de/api/v1/audiofiles/45/stream?format=json"
},
{
"bitrate": 96.263,
"codec": "audio/aac",
"container": "audio/mp4",
"src": "https://thek.lohro.de/api/v1/audiofiles/154/stream?format=json"
},
{
"bitrate": 64.114,
"codec": "audio/opus",
"container": "audio/ogg",
"src": "https://thek.lohro.de/api/v1/audiofiles/155/stream?format=json"
},
{
"bitrate": 68.243,
"codec": "audio/mpeg",
"container": null,
"src": "https://thek.lohro.de/api/v1/audiofiles/156/stream?format=json"
}
]
}
],
"broadcast_ids": [],
"can_be_streamed": false,
"cover_image": null,
"description": "Nach über 6 Jahren fand an der Universität Rostock eine Vollversammlung statt. Über 650 Studierende versammelten sich zum Debattieren und Abstimmen im Audimax zusammen. Bei einer anschließenden Bildungsdemonstration sprachen neben Studierenden unter anderem Prof. Dr. Wolfgang Schareck (Rektor) und Kai Hörig (Geschäftsführer Studierendenwerk) und stellten selbst Forderungen an das Land.",
"id": 58,
"license": {
"name": "CC-BY-SA",
"spdx_identifier": "CC-BY-SA-4.0",
"url": "https://creativecommons.org/licenses/by-sa/4.0/deed.de"
},
"production_date": "2019-05-16",
"tags": [
{
"name": "regelstudienzeit",
"slug": "regelstudienzeit"
},
{
"name": "audimax",
"slug": "audimax"
},
{
"name": "vollversammlung",
"slug": "vollversammlung"
},
{
"name": "jacqueline dejosez",
"slug": "jacqueline-dejosez"
},
{
"name": "rektor",
"slug": "rektor"
},
{
"name": "wohnheim",
"slug": "wohnheim"
},
{
"name": "uni",
"slug": "uni"
},
{
"name": "studierendenwerk",
"slug": "studierendenwerk"
},
{
"name": "kai hörig",
"slug": "kai-horig"
},
{
"name": "stuwe",
"slug": "stuwe"
},
{
"name": "flexibilität",
"slug": "flexibilitat"
},
{
"name": "mensa",
"slug": "mensa"
},
{
"name": "hochschule",
"slug": "hochschule"
},
{
"name": "landeshochschulgesetz",
"slug": "landeshochschulgesetz"
},
{
"name": "bildung",
"slug": "bildung"
},
{
"name": "geld",
"slug": "geld"
},
{
"name": "bafög",
"slug": "bafog"
},
{
"name": "sozial",
"slug": "sozial"
},
{
"name": "familienfreundlich",
"slug": "familienfreundlich"
},
{
"name": "asta",
"slug": "asta"
},
{
"name": "studium",
"slug": "studium"
},
{
"name": "wolfgang schareck",
"slug": "wolfgang-schareck"
},
{
"name": "stura",
"slug": "stura"
},
{
"name": "studierendenticket",
"slug": "studierendenticket"
},
{
"name": "studierende",
"slug": "studierende"
},
{
"name": "freiversuch",
"slug": "freiversuch"
},
{
"name": "universität",
"slug": "universitat"
},
{
"name": "unterfinanzierung",
"slug": "unterfinanzierung"
},
{
"name": "demo",
"slug": "demo"
}
],
"title": "Studentische Vollversammlung und Bildungsdemonstration an der Universität Rostock 2019"
}
{
"count": 55,
"next": "https://thek.lohro.de/api/v1/recordings?is_available=1&page=2&page_size=3",
"previous": null,
"results": [
{
"audio": [
{
"id": 288,
"description": null,
"duration": 464,
"sources": [
{
"bitrate": 320.0,
"codec": "audio/mpeg",
"container": null,
"src": "https://thek.lohro.de/api/v1/audiofiles/288/stream"
},
{
"bitrate": 96.249,
"codec": "audio/aac",
"container": "audio/mp4",
"src": "https://thek.lohro.de/api/v1/audiofiles/289/stream"
},
{
"bitrate": 65.294,
"codec": "audio/opus",
"container": "audio/ogg",
"src": "https://thek.lohro.de/api/v1/audiofiles/290/stream"
},
{
"bitrate": 109.577,
"codec": "audio/mpeg",
"container": null,
"src": "https://thek.lohro.de/api/v1/audiofiles/291/stream"
}
]
}
],
"broadcast_ids": [],
"can_be_streamed": true,
"cover_image": {
"src": "https://thek.lohro.de/api/v1/images/55/render"
},
"description": "Temples - \"Hot Motion\" Rezension",
"id": 93,
"license": {
"name": "CC-BY-SA",
"spdx_identifier": "CC-BY-SA-4.0",
"url": "https://creativecommons.org/licenses/by-sa/4.0/deed.de"
},
"production_date": "2019-10-17",
"tags": [
{
"name": "Temples",
"slug": "temples"
},
{
"name": "Hot Motion",
"slug": "hot-motion"
}
],
"title": "Temples - \"Hot Motion\" Rezension"
},
{
"audio": [
{
"id": 284,
"description": null,
"duration": 494,
"sources": [
{
"bitrate": 320.0,
"codec": "audio/mpeg",
"container": null,
"src": "https://thek.lohro.de/api/v1/audiofiles/284/stream"
},
{
"bitrate": 96.337,
"codec": "audio/aac",
"container": "audio/mp4",
"src": "https://thek.lohro.de/api/v1/audiofiles/285/stream"
},
{
"bitrate": 64.614,
"codec": "audio/opus",
"container": "audio/ogg",
"src": "https://thek.lohro.de/api/v1/audiofiles/286/stream"
},
{
"bitrate": 106.157,
"codec": "audio/mpeg",
"container": null,
"src": "https://thek.lohro.de/api/v1/audiofiles/287/stream"
}
]
}
],
"broadcast_ids": [],
"can_be_streamed": true,
"cover_image": null,
"description": "LOHROtation KW 42",
"id": 92,
"license": {
"name": "CC-BY-SA",
"spdx_identifier": "CC-BY-SA-4.0",
"url": "https://creativecommons.org/licenses/by-sa/4.0/deed.de"
},
"production_date": "2019-10-14",
"tags": [
{
"name": "LOHROtation",
"slug": "lohrotation"
}
],
"title": "LOHROtation KW 42"
},
{
"audio": [
{
"id": 280,
"description": null,
"duration": 214,
"sources": [
{
"bitrate": 320.0,
"codec": "audio/mpeg",
"container": null,
"src": "https://thek.lohro.de/api/v1/audiofiles/280/stream"
},
{
"bitrate": 96.477,
"codec": "audio/aac",
"container": "audio/mp4",
"src": "https://thek.lohro.de/api/v1/audiofiles/281/stream"
},
{
"bitrate": 69.09,
"codec": "audio/opus",
"container": "audio/ogg",
"src": "https://thek.lohro.de/api/v1/audiofiles/282/stream"
},
{
"bitrate": 72.991,
"codec": "audio/mpeg",
"container": null,
"src": "https://thek.lohro.de/api/v1/audiofiles/283/stream"
}
]
}
],
"broadcast_ids": [],
"can_be_streamed": true,
"cover_image": null,
"description": "Die Diskriminierung von Menschen mit Behinderung ist nach wie vor fest verankert in unseren gesellschaftlichen Systemen und Prozessen. Aufklärung und Anti-Stigma-Kampagnen sind wichtig; das zeigen auch immer wieder subtile und weniger unterschwellige Vorfälle in der Praxis, bei denen Menschen mit Behinderung angegriffen, beleidigt, verspottet oder (unbewusst) benachteiligt werden. Im Fachjargon wird hier auch vom \"Ableismus\" (Normalisierung des gesunden Körpers und Geistes und der Abwertung von Menschen mit Beeinträchtigung) gesprochen. Unsere Kollegin Anne Horn war Augenzeugin eines solchen Diskriminierungsvorfalls in Rostock. Davon erzählt sie in einem Kommentar.",
"id": 91,
"license": {
"name": "CC-BY-SA",
"spdx_identifier": "CC-BY-SA-4.0",
"url": "https://creativecommons.org/licenses/by-sa/4.0/deed.de"
},
"production_date": "2019-09-25",
"tags": [
{
"name": "Kommentar",
"slug": "kommentar"
},
{
"name": "Diskriminierung",
"slug": "diskriminierung"
},
{
"name": "Weckruf",
"slug": "weckruf"
},
{
"name": "Rostock",
"slug": "rostock"
},
{
"name": "LOHRO",
"slug": "lohro"
},
{
"name": "Behinderung",
"slug": "behinderung"
}
],
"title": "Diskriminierung von Menschen mit Behinderung (Kommentar)"
}
]
}
The Thekno Content API is a REST-like web API that allows a client to implement a
media library. It is focused on the needs of community radios. Every application
that implements the Thekno Content API will be able to use the Thekno Progressive Web App.
If you plan on implementing the API for your community radio you may use this
document as a reference. For a reference implementation you can look at the
[lohrothek-api project](https://git.hack-hro.de/lohro/lohrothek/lohrothek-api).
In case you already use the lohrothek-api project or any other project that
provides this API there is probably no good reason to look at this document 🙂.
Most of the low-level concepts like query parameters, request methods, response codes,
etc. are defined in the [HTTP specification](https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol) and are not part of this document. If you’re
unfamiliar with these words or struggle with some of the more basic concepts
of this documentation you may want to read an introduction to the HTTP
protocol in general and REST APIs in particular first. A good starting point are
the respective Wikipedia articles and [restfulapi.net](https://restfulapi.net/).
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD",
"SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to
be interpreted as described in [RFC 2119](https://tools.ietf.org/html/rfc2119).
Date: !include ../common/types/date.raml
DateTime: !include ../common/types/date-time.raml
Image: !include ../common/types/image.raml
AudioTrack:
type: object
properties:
id:
type: integer
description: >
Unique identifier of this audio track.
description:
type: string | nil
description: >
A short description of the content of the audio track.
duration?:
type: number | nil
description: >
The duration of this audio track in seconds.
sources:
type: array
items: AudioTrackSource
description: >
An array of audio track sources. The Thekno app will choose which source it will
use based on a number of factors including the users internet connection speed.
Your backend MAY provide any number of sources with different codecs, containers
and bitrates.
examples:
standard_track:
displayName: A 3:38 minute long track
value: !include examples/audio-track.json
AudioTrackSource:
type: object
properties:
bitrate:
type: number
description: >
The bitrate per second for this audio track.
codec:
type: string
enum: [ audio/mpeg, audio/aac, audio/opus ]
description: >
The codec that was used for encoding this audio track.
container:
type: string | nil
enum: [ audio/mp4, audio/ogg, null ]
description: >
The container format the audio stream is embedded in.
src:
type: string
description: >
The absolute URL to the audio data of this audio track.
The server MUST set the following CORS headers for any audio file:
* `Access-Control-Allow-Methods: GET`
* `Access-Control-Expose-Headers: Accept-Ranges, Content-Length, Content-Range`
* `Access-Control-Request-Headers: Range`
It must also set the `Access-Control-Allow-Origin` header to the hostname the
client is served under or `*` to allow any client. The server SHOULD support
the `Range` header to allow clients to request only a part of the content.
examples:
aac:
displayName: AAC Audio Track Source
value: !include examples/audio-track-source-aac.json
mp3:
displayName: MP3 Audio Track Source
value: !include examples/audio-track-source-mp3.json
opus:
displayName: OPUS Audio Track Source
value: !include examples/audio-track-source-opus.json
License:
type: object
properties:
name:
type: string
description: >
A human-readable name of this license.
spdx_identifier:
type: string | nil
description: >
An [SPDX license](https://spdx.org/licenses/) identifier if available for this license.
url:
type: string | nil
description: >
URL to a page that provides information on the license and
informs the user about their rights and obligations under
its terms.
description: >
A license that determines if and how users can use the provided content.
Slug:
type: string
description: >
A string stripped of spaces and any other characters that may not
be used safely in a URL. MAY contain unicode characters.
Recording:
type: object
properties:
id:
type: integer
description: >
Unique identifier of this recording.
title:
type: string
description: >
A human-readable title of this recording.
description:
type: string | nil
description: >
Description of this recording, its content and the audio-files it contains.
production_date:
type: Date
description: >
The day the recording was produced by its creator.
license:
type: License
description: >
License that this recording and the associated audio-files
are published under.
tags:
type: array
items: BaseTag
description: >
A list of tags that categorize the content of this recording. These tags will
also be used to find similar content for this recording so that users can explore
your library through content they already like or listened to.
cover_image:
type: Image | nil
description: >
An image that can be used to represent the content of this recording. This image
is shown as part of the recording preview.
can_be_streamed:
type: boolean
description: >
Whether or not this recording can be accessed by the user under any constraints
that the backend may define. If this field is `true` the user MUST be able to
play this recording and MAY be able to download it. Common constraints that cause this
field to be `false` MAY include legal restrictions for the publication of a
recording as a whole (i.e. depublication rules enforced in Germany), the current
time for low-barrier age restrictions (i.e. content that is only available between
10pm and 6am), or pending transcoding tasks (i.e. the creation of low-quality audio files
for mobile devices with metered internet connections).
See also the `is_available` query parameter of the Recording collection.
broadcast_ids:
type: array
items: integer
description: >
An array of IDs referring to broadcasts in the Broadcast collection. Any ID listed
here means that this recording has been aired as part of the referenced Broadcast.
audio:
type: array
items: AudioTrack
description: >
Audio tracks that are associated with this recording.
Broadcast:
type: object
properties:
id:
type: number
description: >
Unique identifier of this broadcast.
series_id:
type: number
description: >
Unique identifier of the series this broadcast is associated with.
title:
type: string | nil
description: >
A human-readable title of this broadcast.
description:
type: string | nil
description: >
Description of this broadcast, its content and the recordings it contains.
start_time:
type: DateTime
description: >
The time this broadcast is scheduled to start.
end_time:
type: DateTime
description: >
The time this broadcast is scheduled to end.
recordings_ids:
type: array
items: integer
description: >
Ordered list of Recordings IDs. Every Recording referenced here was aired
during the broadcast.
cover_image:
type: Image | nil
description: >
An image that can be used to represent the content of this broadcast. This image
is shown as part of the broadcast preview.
Series:
type: object
properties:
id:
type: integer
description: >
Unique identifier of this series.
title:
type: string | nil
description: >
A human-readable title of this series.
description:
type: string | nil
description: >
Description of the series and the type of content users can expect to be published.
teaser:
type: string | nil
description: >
Very short teaser that summarizes the essence of the series in a view words.
cover_image:
type: Image | nil
description: >
An image that can be used to represent this series. The image is shown as part of the series
preview and as a background on the series detail page.
BaseTag:
type: object
properties:
icon?:
type: Image | nil
description: >
A small image that is displayed next to the tag name.
name:
type: string
description: >
A human-readable name for this tag.
slug:
type: Slug
description: >
A usually short keyword that describes or categorizes the content it is associated with.
AnnotatedTag:
type: BaseTag
properties:
cover_image:
type: Image | nil
description: >
An image that is used as a background image for the tag page.
description:
type: string
description: >
A string that describes what this tag is about and what kind of content
users will find when they are searching for it.
AnyTag:
type: BaseTag | AnnotatedTag
Tag:
type: AnyTag
properties:
weight?:
type: number
minimum: 0
maximum: 1
description: >
A scale that represents how often this tag was used compared to other tags in the
same filter set and specified time frame. If near-zero this tag was almost never
used, if near-one this tag was almost exclusively used.
#%RAML 1.0
title: Thekno TrackService API
baseUri: https://{track_service_api_host}/api
baseUriParameters:
track_service_api_host:
type: string
description: >
Hostname or IP with optional port where the implementation of the
Thekno TrackService API is hosted. Note that the path component defined in the
baseUri is optional. The Thekno environment configuration allows you to define
any arbitrary origin + path as the API root as long as it properly implements
the Thekno TrackService API.
protocols: [ HTTPS ]
mediaType: [ application/json ]
types: !include track-service/types.raml
resourceTypes: !include common/resource-types.raml
traits: !include common/traits.raml
documentation:
- title: Introduction
content: !include track-service/introduction.md
/tracks:
type:
collection: { itemType: Track }
displayName: Tracks
get:
description: >
Retrieves a list of Tracks. The list is sorted by the `started_at` property in descending order.
{
"event": "update",
"payload": {
"ended_at": null,
"started_at": "2019-10-27T21:34:34",
"title": "I'll Never Get Out Of This World Alive",
"cover_image": null,
"description": "Entombed A.D.",
"artist": "Entombed A.D.",
"id": 1402286
},
"source": "current_track"
}
{
"ended_at": null,
"started_at": "2019-10-27T21:34:34+01:00",
"title": "I'll Never Get Out Of This World Alive",
"cover_image": null,
"description": "Entombed A.D.",
"artist": "Entombed A.D.",
"id": 1402286
}
The Thekno TrackService API allows the Thekno Progressive Web App to display
track metadata when the user started the live-stream.
The environment configuration allows providers to configure either a polling or
websocket endpoint. For the polling endpoint see the [`/tracks`](#tracks_get) path
below. If you want to implement a WebSocket, which is the preferred way as it
keeps traffic to a minimum, see the example for the [Event type](#type__Event) below.
For a reference implementation see the [lohro-track-api project](https://git.hack-hro.de/lohro/lohrothek/lohro-track-api).
Date: !include ../common/types/date.raml
DateTime: !include ../common/types/date-time.raml
Image: !include ../common/types/image.raml
Track:
type: object
properties:
id:
type: integer | string
description: >
Unique identifier for this track.
title:
type: string
description: >
Title of this track.
artist?:
type: string
description: >
Artist of this track.
album?:
type: string | nil
description: >
Album this track was published on if available.
started_at:
type: DateTime
description: >
Time this track started to play.
ended_at?:
type: DateTime | nil
description: >
Time this track finished playing.
description:
type: string | nil
description: >
Any additional information for this track.
cover_image:
type: Image | nil
description: >
An image that can be used to represent this track. This image is shown as part
of the track info in the player.
examples:
standard_track:
displayName: "Example: Common Track"
value: !include examples/track.json
Event:
type: object
properties:
source:
type: string
enum: [ current_track ]
description: >
The source this event originated from.
event:
type: string
enum: [ update ]
description: >
The type of event that was triggered for the source.
payload:
type: object | nil
description: >
The payload of the this event for this specific source and event type.
examples:
current_track_event:
displayName: "Example: CurrentTrackEvent"
description: >
Emitted when the current track changes.
value: !include examples/current-track-event.json
# Thekno Track-Service API
TODO
BIN_RAML2HTML ?= $(DIR_NODE)/.bin/raml2html
DIR_DOCS = docs
DIR_BUILD_DOCS = $(DIR_BUILD)/docs
DOC_THEME ?= raml2html-werk-theme
DOC_DEPS_API = $(shell find "$(DIR_DOCS)/apis/common" -type f)
DOC_DEPS_API_CONTENT = $(shell find "$(DIR_DOCS)/apis/content" -type f)
DOC_DEPS_API_TRACK_SERVICE = $(shell find "$(DIR_DOCS)/apis/track-service" -type f)
DOC_SRC_API_CONTENT = $(DIR_DOCS)/apis/content-api.raml
DOC_SRC_API_TRACK_SERVICE = $(DIR_DOCS)/apis/track-service-api.raml
DOC_BUILD_API_ASSETS = $(DIR_BUILD)/docs/assets
DOC_BUILD_API_CONTENT = $(DIR_BUILD)/docs/content-api.html
DOC_BUILD_API_TRACK_SERVICE = $(DIR_BUILD)/docs/track-service-api.html
DOC_BUILD_INDEX = $(DIR_BUILD)/docs/index.html
build: docs
$(DOC_BUILD_API_ASSETS):
mkdir -p "$(dir $@)"
cp -r "$(DIR_NODE)/raml2html-werk-theme/dist/assets" "$@"
$(DOC_BUILD_API_CONTENT): $(DOC_BUILD_API_ASSETS) $(DOC_DEPS_APIS) $(DOC_DEPS_API_CONTENT) $(DOC_SRC_API_CONTENT)
mkdir -p "$(dir $@)"
$(BIN_RAML2HTML) --validate --theme "$(DOC_THEME)" -o "$@" --no-bundle-assets "$(DOC_SRC_API_CONTENT)"
$(DOC_BUILD_API_TRACK_SERVICE): $(DOC_BUILD_API_ASSETS) $(DOC_DEPS_APIS) $(DOC_DEPS_API_TRACK_SERVICE) $(DOC_SRC_API_TRACK_SERVICE)
mkdir -p "$(dir $@)"
$(BIN_RAML2HTML) --validate --theme "$(DOC_THEME)" -o "$@" --no-bundle-assets "$(DOC_SRC_API_TRACK_SERVICE)"
.PHONY: docs
docs: $(DIR_NODE) $(DOC_BUILD_API_CONTENT) $(DOC_BUILD_API_TRACK_SERVICE)
clean::
rm -rf "$(DIR_BUILD_DOCS)"
This source diff could not be displayed because it is too large. You can view the blob instead.