Commit 027c8f78 authored by Konrad Mohrfeldt's avatar Konrad Mohrfeldt

update documentation and specifications for APIs

parent 444ac4ea
......@@ -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",