Skip to content

Commit

Permalink
Added HTTP API
Browse files Browse the repository at this point in the history
  • Loading branch information
spietras committed Jul 24, 2024
1 parent ec8761f commit 28df639
Show file tree
Hide file tree
Showing 34 changed files with 443 additions and 19 deletions.
3 changes: 3 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
# Data
/data/

# OpenAPI
/openapi/schema.html

# Tracked, but not needed
/.devcontainer/
/.github/
Expand Down
2 changes: 2 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,7 @@
* linguist-vendored
# Treat docs as documentation
/docs/** -linguist-vendored linguist-documentation
# Treat openapi as documentation
/openapi/** -linguist-vendored linguist-documentation
# Unmark files in src, so that they are included in language stats
/src/** -linguist-vendored
2 changes: 2 additions & 0 deletions .github/workflows/image.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ name: Image
# Run only on changes to relevant files
paths:
- .github/workflows/image.yaml
- openapi/**
- scripts/**
- src/**
- .dockerignore
Expand All @@ -24,6 +25,7 @@ name: Image
# Run only on changes to relevant files
paths:
- .github/workflows/image.yaml
- openapi/**
- scripts/**
- src/**
- .dockerignore
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,6 @@
# Data
/data/*
!/data/.gitkeep

# OpenAPI
/openapi/schema.html
3 changes: 3 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ RUN useradd --create-home app && \
COPY scripts/shell.sh scripts/shell.sh
SHELL ["/app/scripts/shell.sh"]

# Copy OpenAPI schema
COPY openapi/ openapi/

# Copy source
COPY src/ src/

Expand Down
8 changes: 6 additions & 2 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,19 @@ services:
context: ./
network: host
environment:
- "FUSION__SERVER__HOST=${FUSION__SERVER__HOST:-0.0.0.0}"
- "FUSION__SERVER__PORT=${FUSION__SERVER__PORT:-9000}"
- "FUSION__SERVER__SRT__PORT=${FUSION__SERVER__SRT__PORT:-9000}"
- "FUSION__SERVER__HTTP__PORT=${FUSION__SERVER__HTTP__PORT:-9001}"
- "FUSION__STATE__STORE__PATH=${FUSION__STATE__STORE__PATH:-data/state.json}"
- "FUSION__STATE__CACHE__TTL=${FUSION__STATE__CACHE__TTL:-60}"
- "FUSION__STREAMCAST__ICY__HOST=${FUSION__STREAMCAST__ICY__HOST:-localhost}"
- "FUSION__STREAMCAST__ICY__PORT=${FUSION__STREAMCAST__ICY__PORT:-8000}"
- "FUSION__STREAMCAST__ICY__USER=${FUSION__STREAMCAST__ICY__USER:-source}"
- "FUSION__STREAMCAST__ICY__PASSWORD=${FUSION__STREAMCAST__ICY__PASSWORD:-password}"
- "FUSION__STREAMCAST__ICY__MOUNT=${FUSION__STREAMCAST__ICY__MOUNT:-radio.mp3}"
- "FUSION__EMITUNES__HTTP__SCHEME=${FUSION__EMITUNES__HTTP__SCHEME:-http}"
- "FUSION__EMITUNES__HTTP__HOST=${FUSION__EMITUNES__HTTP__HOST:-localhost}"
- "FUSION__EMITUNES__HTTP__PORT=${FUSION__EMITUNES__HTTP__PORT:-42000}"
- "FUSION__EMITUNES__HTTP__PATH=${FUSION__EMITUNES__HTTP__PATH:-}"
network_mode: host
volumes:
- data:/app/data/
Expand Down
20 changes: 20 additions & 0 deletions docs/docs/02-Usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,23 @@ ffmpeg \
-f ogg \
srt://127.0.0.1:9000
```

## Managing playlists

You can manage currently used playlist using the `/playlist` endpoint.
You can use the following HTTP methods:

- `GET` to retrieve the current playlist data
- `PUT` to update the current playlist data

For example, to change the playlist to use a different one,
you can use [`curl`](https://curl.se)
to send a `PUT` request to the `/playlist` endpoint:

```sh
curl \
--request PUT \
--header "Content-Type: application/json" \
--data '{"id": "123e4567-e89b-12d3-a456-426614174000"}' \
http://localhost:9001/playlist
```
22 changes: 17 additions & 5 deletions docs/docs/03-Configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ title: Configuration

You can configure the app at runtime using various environment variables:

- `FUSION__SERVER__HOST` -
host to listen for live audio
(default: `0.0.0.0`)
- `FUSION__SERVER__PORT` -
port to listen for live audio
- `FUSION__SERVER__SRT__PORT`
port to listen for SRT connections
(default: `9000`)
- `FUSION__SERVER__HTTP__PORT` -
port to listen for HTTP connections
(default: `9001`)
- `FUSION__STATE__STORE__PATH` -
path to the file to store the state
(default: `data/state.json`)
Expand All @@ -34,3 +34,15 @@ You can configure the app at runtime using various environment variables:
- `FUSION__STREAMCAST__ICY__MOUNT` -
mount point of the ICY API of the streamcast service to send the audio to
(default: `radio.mp3`)
- `FUSION__EMITUNES__HTTP__SCHEME` -
scheme of the HTTP API of the emitunes service
(default: `http`)
- `FUSION__EMITUNES__HTTP__HOST` -
host of the HTTP API of the emitunes service
(default: `localhost`)
- `FUSION__EMITUNES__HTTP__PORT` -
port of the HTTP API of the emitunes service
(default: `42000`)
- `FUSION__EMITUNES__HTTP__PATH` -
path of the HTTP API of the emitunes service
(default: ``)
3 changes: 3 additions & 0 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
trunk = pkgs.trunk-io;
copier = pkgs.copier;
liquidsoap = pkgs.liquidsoap;
redocly = pkgs.redocly;
ffmpeg = pkgs.ffmpeg;
tini = pkgs.tini;
su-exec = pkgs.su-exec;
Expand Down Expand Up @@ -75,6 +76,7 @@
trunk
copier
liquidsoap
redocly
ffmpeg
];

Expand All @@ -88,6 +90,7 @@

packages = [
liquidsoap
redocly
tini
su-exec
];
Expand Down
89 changes: 89 additions & 0 deletions openapi/schema.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
info:
title: fusion app
description: Audio streaming with Liquidsoap 🧼
openapi: 3.1.0
servers:
- url: /
paths:
/ping:
get:
summary: Ping
description: Do nothing.
responses:
"204":
description: Request fulfilled, nothing follows
headers:
Cache-Control:
schema:
type: string
example: no-cache
/playlist:
get:
summary: Get playlist data
description: Get the current playlist data.
responses:
"200":
description: Request fulfilled, document follows
content:
application/json:
schema:
$ref: "#/components/schemas/GetPlaylistResponse"
"404":
description: Request failed, resource not found
content:
text/plain:
schema:
type: string
example: Playlist data not found.
put:
summary: Update playlist data
description: Update the current playlist data.
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/PutPlaylistRequest"
responses:
"200":
description: Request fulfilled, document follows
content:
application/json:
schema:
$ref: "#/components/schemas/PutPlaylistResponse"
"400":
description: Bad request syntax
content:
text/plain:
schema:
type: string
example: Request body is not a valid JSON object.
components:
schemas:
GetPlaylistResponse:
type: object
properties:
id:
type: string
format: uuid
example: 123e4567-e89b-12d3-a456-426614174000
required:
- id
PutPlaylistRequest:
type: object
properties:
id:
type: string
format: uuid
example: 123e4567-e89b-12d3-a456-426614174000
required:
- id
PutPlaylistResponse:
type: object
properties:
id:
type: string
format: uuid
example: 123e4567-e89b-12d3-a456-426614174000
required:
- id
19 changes: 19 additions & 0 deletions src/api/app.liq
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
let api.app = {}

def api.app.register(~method, ~path, handler) =
def handle(request) =
try
handler(request)
catch _ do
api.responses.text(
status=api.codes.internalservererror,
body=
"Internal Server Error"
)
end
end

harbor.http.register.simple(
port=config.server.http.port, method=method, path, handle
)
end
8 changes: 8 additions & 0 deletions src/api/codes.liq
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
let api.codes = {}

let api.codes.ok = 200
let api.codes.created = 201
let api.codes.nocontent = 204
let api.codes.badrequest = 400
let api.codes.notfound = 404
let api.codes.internalservererror = 500
8 changes: 8 additions & 0 deletions src/api/index.liq
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
let api = {}

%include "codes.liq"
%include "responses.liq"
%include "app.liq"
%include "ping/index.liq"
%include "playlist/index.liq"
%include "schema/index.liq"
8 changes: 8 additions & 0 deletions src/api/ping/controller.liq
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
let api.ping.controller = {}

def api.ping.controller.ping(_) =
api.ping.service.ping()
api.responses.empty(headers=[("Cache-Control", "no-cache")])
end

api.app.register(method="GET", path="/ping", api.ping.controller.ping)
4 changes: 4 additions & 0 deletions src/api/ping/index.liq
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
let api.ping = {}

%include "service.liq"
%include "controller.liq"
5 changes: 5 additions & 0 deletions src/api/ping/service.liq
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
let api.ping.service = {}

def api.ping.service.ping() =
()
end
28 changes: 28 additions & 0 deletions src/api/playlist/controller.liq
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
let api.playlist.controller = {}

def api.playlist.controller.get(_) =
try
response = api.playlist.service.get()
api.responses.json(
status=api.codes.ok,
body=api.playlist.models.getresponse.serialize(response)
)
catch error : [api.playlist.errors.notfound] do
api.responses.text(status=api.codes.notfound, body=error.message)
end
end

def api.playlist.controller.put(request) =
try
req = api.playlist.models.putrequest.deserialize(request.body())
res = api.playlist.service.put(req)
api.responses.json(
status=api.codes.ok, body=api.playlist.models.putresponse.serialize(res)
)
catch error : [error.json] do
api.responses.text(status=api.codes.badrequest, body=error.message)
end
end

api.app.register(method="GET", path="/playlist", api.playlist.controller.get)
api.app.register(method="PUT", path="/playlist", api.playlist.controller.put)
4 changes: 4 additions & 0 deletions src/api/playlist/errors.liq
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
let api.playlist.errors = {}

let api.playlist.errors.notfound =
error.register("api.playlist.errors.notfound")
6 changes: 6 additions & 0 deletions src/api/playlist/index.liq
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
let api.playlist = {}

%include "errors.liq"
%include "models.liq"
%include "service.liq"
%include "controller.liq"
52 changes: 52 additions & 0 deletions src/api/playlist/models.liq
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
let api.playlist.models = {}

def api.playlist.models.getresponse =
def create(~id) =
{id=id}
end

def serialize(model) =
json.stringify(model)
end

def deserialize(data) =
let json.parse (model : {id: string}) = data
model
end

{create=create, serialize=serialize, deserialize=deserialize}
end

def api.playlist.models.putrequest =
def create(~id) =
{id=id}
end

def serialize(model) =
json.stringify(model)
end

def deserialize(data) =
let json.parse (model : {id: string}) = data
model
end

{create=create, serialize=serialize, deserialize=deserialize}
end

def api.playlist.models.putresponse =
def create(~id) =
{id=id}
end

def serialize(model) =
json.stringify(model)
end

def deserialize(data) =
let json.parse (model : {id: string}) = data
model
end

{create=create, serialize=serialize, deserialize=deserialize}
end
Loading

0 comments on commit 28df639

Please sign in to comment.