Skip to content

🎸 πŸ’¬ YouTube TV like service with super powers controlled via Discord

License

Notifications You must be signed in to change notification settings

Deseteral/lebkuchen-fm

Repository files navigation

LebkuchenFM

Actions Status

Monorepo for Lebkuchen FM project - opinionated YouTube TV-like service with super powers controlled via Discord.

Development

⚠️ Please don't use npm to install dependencies

Start by installing dependencies:

yarn install

To build application run:

yarn run build

You can run tests using:

yarn test

To clean up after previous builds before installing dependencies use:

yarn run clean:deps

To run the application you have to connect to MongoDB database.

If you have Docker installed you can use ./scripts/docker_db_local.sh script to run MongoDB in Docker locally. For more information you can refer to Local MongoDB in Docker documentation section.

Then create .env file in the root of this project and put desired configuration variables in it (refer to Service > Configuration section of this document for available options).

When that's done you can just start the application:

yarn start

If you want to create local frontend dev server with hot reloading you should use "dev" script from packages/client:

yarn workspace lebkuchen-fm-client dev

This project is separated into backend service (lebkuchen-fm-service located in packages/service) and web client application (lebkuchen-fm-client located in packages/client). For development information specific to modules refer to their Development sections in this document. For more information about managing modules check out yarn workspace docs.

Type checking

Modules using TypeScript will compile even when there are type checking errors in the code. This allows for fast development iteration. Type checking is done during test script. For development it's recommended that you use type checker in watch mode:

yarn run test:type-check:watch

Modules

This projects consists of these modules:

Backend service (/packages/service)

Core LebkuchenFM Node.js service with MongoDB storage that communicates with clients over WebSockets and REST endpoints.

Configuration

Configure your instance via environment variables.

Service
  • MONGODB_URI - MongoDB connection string
  • PORT - port on which the service will be running (automatically injected by cloud providers)
  • LOCALE - language of the service
  • COMMAND_PROMPT - command prompt (optional, defaults to /fm)
Youtube player
  • YOUTUBE_API_KEY - YouTube Data API token
Discord
  • DISCORD_CHANNEL_ID - ID of the Discord channel where the bot is allowed to run
  • DISCORD_CLIENT_ID - Discord application ID
  • DISCORD_GUILD_ID - ID of the Discord guild (server) where the bot will operate
  • DISCORD_TOKEN - token of the Discord bot
Dropbox - used for persisting files
  • DROPBOX_CLIENT_ID - Dropbox App Key
  • DROPBOX_SECRET - Dropbox App Secret
  • DROPBOX_REFRESH_TOKEN - Dropbox refresh token used for persisting files

Authorization

LebkuchenFM uses session cookie and/or basic auth with token methods to authorize it's users. Each request to /api/* endpoint has to be authorized.

Session cookie is set during successful POST request to /api/auth endpoint and is generally handled by the web client.

There is no way to register as a new user. Instead LebkuchenFM functions as an invite only system.
When there are no registered users, first login is always correct and creates that account. Every next user has to be created using admin dashboard (/admin). That way a new account will be created and user is going be able to set the password when they login for the first time.

External integrations

For external integrations users should use API tokens.

Each user can obtain this token after logging in the web client and requesting GET /api/auth as mentioned in REST endpoints section of this documentation.

Using this token external tools can integrate with LebkuchenFM by making requests with Authorization: Basic <api-token> header set. Socket clients using socket.io can authorize by providing API token during connection like this:

const socket = io({
  auth: {
    token: "api-token"
  }
});

Event stream

This service communicates with clients mostly using event stream implemented on WebSockets. For possible events check out event data models.

REST endpoints

GET /api/auth
Information about currently logged in user.

Response

{
  "username": "anton",
  "apiToken": "this_users_api_token"
}

POST /api/auth/logout
Logs out currently logged in user.


GET /api/history
History listing containing list of queued songs.

Response

{
  "entries": [
    {
      "date": "2022-05-31T12:46:17.968Z",
      "youtubeId": "c6pPAso-y8s"
    }
  ]
}

GET /api/songs
Returns list of all songs in the database sorted by play count (descending).

Response

{
  "songs": [
    {
      "_id": "storage_id",
      "name": "Rick Astley - Never Gonna Give You Up (Official Music Video)",
      "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
      "timesPlayed": 1337
    }
  ]
}

POST /api/commands/text
Plain text interface for slash commands.

Body

{
  "text": "/fm help"
}

Response

{
  "textResponse": "Command response in plain text format"
}

GET /api/users
List of all registered users.

Response

{
  "users": [
    {
      "name": "anton",
      "creationDate": "2022-05-31T19:59:05.879Z",
      "lastLoggedIn": "2022-05-31T20:01:17.072Z"
    }
  ]
}

GET /api/x-sounds
Returns list of all XSounds in the database.

Response

{
  "sounds": [
    {
      "_id": "storage_id",
      "name": "example sound",
      "url": "https://example.com/example_sound.wav",
      "tags": ["tag1", "tag2"],
      "timesPlayed": 6
    }
  ]
}

GET /api/x-sounds?tag=example-tag
Returns a filtered list of XSounds containing given tag in the database.

Response

{
  "sounds": [
    {
      "_id": "storage_id",
      "name": "example sound",
      "url": "https://example.com/example_sound.wav",
      "tags": ["example-tag"],
      "timesPlayed": 6
    }
  ]
}

POST /api/x-sounds
Adds new sound file to XSounds database.

Request
Requires content type to be multipart/form-data with fields:

  • soundName: name of the sound to be added (like "cool sound")
  • tags (optional): comma separated list of tags (like "tag1, tag2, tag3")
  • soundFile: sound File ideally in mp3 or wav format

Response

{
  "_id": "storage_id",
  "name": "my new sound",
  "url": "https://example.com/example_sound.wav",
  "timesPlayed": 0
}

GET /api/x-sounds/tags
Returns list of all unique XSounds tags in database.

Response

{
  "tags": [
    "example tag",
    "another tag"
  ]
}

Client (/packages/client)

Web client for the application. Communicates with the service via WebSocket event stream.

Development

Running yarn run dev runs the application in development mode with hot reload on file change. This version of application won't connect to the service.
Running yarn run build builds the application in production mode.

Devops scripts (/scripts)

Scripts related to maintenance of the service.

FM Dev Helper

Helper script scripts/fm.sh is available for local development purposes. By default it sends a command to a local development server.

Example commands:

> fm.sh "/fm resume"
> fm.sh "/fm q dQw4w9WgXcQ"
> fm.sh "/fm x alert"

Alternatively you can run it as an yarn command from the root of the project:

> yarn run fm "/fm resume"
> yarn run fm "/fm q dQw4w9WgXcQ"
> yarn run fm "/fm x alert"

Local MongoDB in Docker

Helper script scripts/docker_db_local.sh runs MongoDB and binds ports for local development.
To stop container run scripts/docker_db_local.sh stop.

Docker Compose

You can also run this app via Docker Compose. It has MongoDB already configured. Just pass other necessary config variables.

docker compose up --build

License

This project is licensed under the MIT license.