Git Product home page Git Product logo

live-radio's Introduction

Online Radio Player

Progressive web application for listening to online radio.

Motivation

The application started as a side project during the covid pandemic. I wanted to have a simple application to listen to online radio, that is not overrun with ads and works both on desktop and on mobile.

Then later, it became a test bed for experimenting with different ways to structure a Next.js application (more on that later).

The setup

The application is built with the Next.js framework. It is installable as a PWA, and it can be used anonymously (data is stored in IndexedDB) or as a registered user (data is stored in MongoDB Atlas).

You can seamlessly switch from anonymous to a registered user, and import the data from the anonymous account.

Application also supports multiple languages via Next.js internationalized routing and LinguiJS internationalization library.

Radio station data is consumed via free and open source Radio Browser API.

Application architecture

Here I'm going to document the main architecture of the application. This section is divided into the front-end and backend architecture.

Frontend

App Configuration

Next.js supports environment variables in the front-end at build time. All frontend configuration is set in one file and later used via dependency injection. Take a look at the configuration file or the injection root file.

PWA functionality

PWA functionality is made possible via Workbox library and it is used for:

  • controlling the service worker registration and activation
  • caching of static data and assets
  • notifying the user to install the application
  • notifying the user reload the application when it new version is updated.
  • providing an offline fallback when there is no internet connection

I've also created a small libary that streamlines setting up Workbox with Webpack and Next.js.

State management

State is managed via Mobx. I'm a big fan of reactive architecture and I think it's the future of state management as evidenced by Svelte and SolidJS design decisions.

Mobx stores are architected using the root store pattern (official docs). I have also blogged about using this pattern with Next.js.

I've also created a small abstraction on top of Mobx which implements the collection -> model pattern that handles persisting the data in the application.

Dependency injection - frontend

I've played around with dependency injection to connect all the Mobx stores, and for that, I've used my own small 2KB dependency injection libary that doesn't use decorators for registering and resolving the dependencies, so there is no additional webpack setup, and it can easily be integrated into other libraries and projects.

Take a look at the frontend injection root file.

Note: Some people would argue that the state management is overengineered but the purpose of the application is also to explore different state management patterns and architectures.

User interface

Material design version 4 is used for the user interface. The application has two layouts: desktop and mobile. I've also extracted the app shell layout into separate repository so it can easily be reused as a starting point for building Material UI progressive web apps.

You can test drive template demo here.

Error handling and tracking

Error handling is done via react-error-boundary and error tracking is done via Sentry

Backend

App Configuration

Backend configuration is separate from frontend configuration and it is done via environment variables as per Next.js docs all environment variables are used in only one file, where they are all validated, and default values are added if needed. This also helps with dependency injection when testing.

Take a look at the configuration file.

Dependency injection

Dependency injection is also used in the backend via my library. Using dependency injection significantly eases architecture complexity and testing (no more Jest module mocking).

You can take a look at the backend injection root file.

API routes

I tried something new regarding the Next.js API routes.

If you look into the API directory you will notice that there is only one route (disregarding the NextAuth route) This one and the only route is powered by Koa.js. But to use Koa.js inside the Next.js API routes I've had to wrap Kao.js in a bit of custom code which I've also open sourced.

That one route behaves as a Koa.js server and does all the internal routing, and you can also use all available Koa.js middleware.

Error tracking

Error tracking in the backend is done with Pino logger and [Sentry] error tracking, wrapped in Koa.js middleware.

Take a look at the middleware function file.

Authentication

For authentication NextAuth is used in combination with MongoDB. Currently, users can register with Google and Github accounts.

Testing

Testing is done via Jest and Cypress with Node test containers.

There are three types of tests:

Every time the tests are run, we start MongoDB containers, one DB container per Jest worker file.

I have created an accompanying repository with a distilled example: Test MongoDB and PostgreSQL databases with Jest and Docker containers (blog post is coming soon).

Data generation

In the scripts directory there are a couple of files that are generating special content for the application, generated files are saved in the generated directory and imported into the application like any other modules.

  • Generate countries generates a list of all existing countries and their flags in the form of emoji and also prepares them for translation.
  • Generate genres generates a list of music genres and prepares them for translation.
  • Generate languages generates a list of all existing languages and prepares them for translation.

Patching unmaintained packages

I'm using node-internet-radio to get the "now playing" information from the radio stream. However, there are issues with the package and the maintainer is unresponsive. So I've used the excellent patch-package module to patch (modify) the package myself.

Translation

Multi language support is done via Next.js internationalized routing and LinguiJS internationalization library.

The default language is English and there are translations for Serbian.

While in the development mode, there is also one additional language called "pseudo" which enables you to visually inspect the application and see if you missed translating any of the text.

I've written about internationalizing Next.js Apps for LogRocket blog.

You can also check out the translation demo

If you would like to help me translate the application to more languages please open an issue with the title: <Language>[translation]

Development experience

The repository is set to use VS Code containers or Github Codespaces.

When running via containers everything works (Docker inside Docker yo!) except launching the Cypress application although you can still run all Cypress tests but only in headless mode.

Future

I'm hoping to publish the application to the Google play store.

License

This project is licensed under the MIT License - see the LICENSE file for details


live-radio's People

Contributors

ivandotv avatar renovate-bot avatar renovate[bot] avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

live-radio's Issues

Create better visuals

Create better visuals for:

  • landing page
  • recent stations
  • favorite station
  • add/remove from favorites ( animation and design)

Add Analytics

Choose analytics software that supports open source.

Dependency Dashboard

This issue lists Renovate updates and detected dependencies. Read the Dependency Dashboard docs to learn more.

Warning

These dependencies are deprecated:

Datasource Name Replacement PR?
npm @babel/plugin-proposal-class-properties Unavailable
npm @material-ui/core Available
npm @material-ui/icons Available
npm @material-ui/lab Unavailable
npm eslint-plugin-node Available
npm npm-run-all Available

Rate-Limited

These updates are currently rate-limited. Click on a checkbox below to force their creation now.

  • chore(deps): replace dependency eslint-plugin-node with eslint-plugin-n ^14.0.0
  • fix(deps): replace dependency @material-ui/icons with @mui/icons-material ^5.0.0
  • chore(deps): update actions/cache action to v4
  • chore(deps): update actions/checkout action to v4
  • chore(deps): update actions/setup-node action to v4
  • chore(deps): update dependency @types/node to v20
  • chore(deps): update dependency @types/supertest to v6
  • chore(deps): update dependency countries-list to v3
  • chore(deps): update dependency cypress to v13
  • chore(deps): update dependency date-fns to v3
  • chore(deps): update dependency dotenv-cli to v7
  • chore(deps): update dependency eslint to v9
  • chore(deps): update dependency eslint-config-prettier to v9
  • chore(deps): update dependency eslint-plugin-cypress to v3
  • chore(deps): update dependency eslint-plugin-jest to v28
  • chore(deps): update dependency eslint-plugin-prettier to v5
  • chore(deps): update dependency husky to v9
  • chore(deps): update dependency iso-639-1 to v3
  • chore(deps): update dependency jest-watch-typeahead to v2
  • chore(deps): update dependency lint-staged to v15
  • chore(deps): update dependency msw to v2
  • chore(deps): update dependency patch-package to v8
  • chore(deps): update dependency pino-pretty to v11
  • chore(deps): update dependency prettier to v3
  • chore(deps): update dependency supertest to v7
  • chore(deps): update dependency testcontainers to v10
  • chore(deps): update dependency ts-essentials to v10
  • chore(deps): update dependency typescript to v5
  • chore(deps): update dependency zx to v8
  • chore(deps): update linguijs monorepo to v4 (major) (@lingui/cli, @lingui/core, @lingui/loader, @lingui/macro, @lingui/react)
  • chore(deps): update mcr.microsoft.com/vscode/devcontainers/base docker tag to v1
  • chore(deps): update mongo docker tag to v7
  • chore(deps): update nextjs monorepo to v14 (major) (@next/bundle-analyzer, eslint-config-next)
  • chore(deps): update pnpm/action-setup action to v4
  • chore(deps): update typescript-eslint monorepo to v7 (major) (@typescript-eslint/eslint-plugin, @typescript-eslint/parser)
  • fix(deps): update dependency @faker-js/faker to v8
  • fix(deps): update dependency @fuerte/core to v5
  • fix(deps): update dependency clsx to v2
  • fix(deps): update dependency idb to v8
  • fix(deps): update dependency koa-body to v6
  • fix(deps): update dependency mobx-react-lite to v4
  • fix(deps): update dependency notistack to v3
  • fix(deps): update dependency path-to-regexp to v7
  • fix(deps): update dependency pino to v9
  • fix(deps): update dependency pino-http to v10
  • fix(deps): update dependency pumpit to v7
  • fix(deps): update dependency react-error-boundary to v4
  • fix(deps): update dependency react-virtuoso to v4
  • fix(deps): update dependency request-ip to v3
  • fix(deps): update dependency tinga to v6
  • fix(deps): update dependency uuid to v10 (uuid, @types/uuid)
  • fix(deps): update workbox monorepo to v7 (major) (workbox-core, workbox-expiration, workbox-precaching, workbox-recipes, workbox-routing, workbox-strategies, workbox-webpack-plugin, workbox-window)
  • ๐Ÿ” Create all rate-limited PRs at once ๐Ÿ”

Other Branches

These updates are pending. To force PRs open, click the checkbox below.

  • fix(deps): update dependency mongodb to v4.17.0 [security]

Open

These updates have all been created already. Click a checkbox below to force a retry/rebase of any.

Detected dependencies

docker-compose
.devcontainer/docker-compose.override.yml
  • mongo 5.0.15
.devcontainer/docker-compose.yml
dockerfile
.devcontainer/Dockerfile
  • mcr.microsoft.com/vscode/devcontainers/base 0-bullseye
github-actions
.github/workflows/CI.yml
  • actions/checkout v2
  • actions/cache v2
  • actions/setup-node v2
  • pnpm/action-setup v2.2.4
  • cypress-io/github-action v4
npm
package.json
  • @faker-js/faker ^6.3.1
  • @fuerte/core ^4.0.0
  • @lingui/core ^3.13.3
  • @lingui/react ^3.13.3
  • @material-ui/core ^4.12.4
  • @material-ui/icons ^4.11.3
  • @material-ui/lab ^4.0.0-alpha.61
  • @next-auth/mongodb-adapter ^1.0.3
  • @sentry/nextjs ^6.19.7
  • @types/request-ip 0.0.37
  • accept-language-parser ^1.5.0
  • clsx ^1.1.1
  • country-code-emoji ^2.3.0
  • howler ^2.2.3
  • idb ^7.0.1
  • joi ^17.6.0
  • js-cookie ^3.0.1
  • js-search ^2.0.0
  • koa-body ^5.0.0
  • koa-pick-locale ^1.0.0
  • make-plural ^7.1.0
  • mobx ^6.5.0
  • mobx-react-lite ^3.4.0
  • mobx-utils ^6.0.5
  • mongodb ^4.5.0
  • next 12.2.4
  • next-auth ^4.3.4
  • nextjs-koa-api ^2.0.2
  • node-internet-radio ^0.2.1
  • notistack 1.0.10
  • path-to-regexp ^6.2.1
  • pino ^7.10.0
  • pino-http ^6.6.0
  • pumpit ^4.0.3
  • radio-browser-api ^6.0.1
  • react 17.0.2
  • react-dom 17.0.2
  • react-error-boundary ^3.1.4
  • react-transition-group ^4.4.2
  • react-use ^17.3.2
  • react-virtuoso ^2.11.0
  • request-ip ^2.1.3
  • styled-jsx ^5.0.2
  • tinga ^4.0.10
  • uuid ^8.3.2
  • workbox-core ^6.5.3
  • workbox-expiration ^6.5.3
  • workbox-precaching ^6.5.3
  • workbox-recipes ^6.5.3
  • workbox-routing ^6.5.3
  • workbox-strategies ^6.5.3
  • workbox-window ^6.5.3
  • @babel/core ^7.17.10
  • @babel/plugin-proposal-class-properties ^7.16.7
  • @lingui/cli ^3.13.3
  • @lingui/loader ^3.13.3
  • @lingui/macro ^3.13.3
  • @next/bundle-analyzer ^12.1.6
  • @types/accept-language-parser ^1.5.3
  • @types/csso ^5.0.0
  • @types/dateformat ^5.0.0
  • @types/howler ^2.2.7
  • @types/jest ^27.5.1
  • @types/js-cookie ^3.0.2
  • @types/js-search ^1.4.0
  • @types/lodash ^4.14.182
  • @types/node ^17.0.24
  • @types/react ^17.0.44
  • @types/supertest ^2.0.12
  • @types/underscore ^1.11.4
  • @types/uuid ^8.3.4
  • @types/wicg-mediasession ^1.1.3
  • @typescript-eslint/eslint-plugin ^5.23.0
  • @typescript-eslint/parser ^5.23.0
  • babel-plugin-macros ^3.1.0
  • babel-plugin-transform-define ^2.0.1
  • babel-plugin-transform-remove-console ^6.9.4
  • clean-webpack-plugin ^4.0.0
  • countries-list ^2.6.1
  • critters ^0.0.16
  • cypress ^10.2.0
  • date-fns ^2.28.0
  • dotenv ^16.0.1
  • dotenv-cli ^6.0.0
  • esbuild ^0.17.0
  • esbuild-runner ^2.2.1
  • eslint ^8.15.0
  • eslint-config-next ^12.1.6
  • eslint-config-prettier ^8.5.0
  • eslint-plugin-cypress ^2.12.1
  • eslint-plugin-import ^2.26.0
  • eslint-plugin-jest ^26.1.5
  • eslint-plugin-node ^11.1.0
  • eslint-plugin-prettier ^4.0.0
  • eslint-plugin-promise ^6.0.0
  • eslint-plugin-react ^7.29.4
  • eslint-plugin-react-hooks ^4.5.0
  • husky 8.0.3
  • iso-639-1 ^2.1.13
  • jest ^28.1.0
  • jest-watch-typeahead ^1.1.0
  • lint-staged ^12.4.1
  • lodash ^4.17.21
  • markdown-toc ^1.2.0
  • msw ^0.49.0
  • next-compose-plugins ^2.2.1
  • nextjs-workbox-config ^2.0.0
  • node-mocks-http ^1.11.0
  • npm-run-all ^4.1.5
  • patch-package ^6.4.7
  • pino-pretty ^7.6.1
  • postinstall-postinstall ^2.1.0
  • prettier ^2.6.2
  • prettier-standard ^16.4.1
  • supertest ^6.2.3
  • testcontainers ^8.10.0
  • time-span ^4.0.0
  • ts-essentials ^9.1.2
  • ts-node ^10.8.2
  • tsm ^2.2.1
  • typescript ^4.6.4
  • webpack ^5.72.1
  • workbox-webpack-plugin ^6.5.3
  • zx ^6.2.4

  • Check this box to trigger a request for Renovate to run again on this repository

Action Required: Fix Renovate Configuration

There is an error with this repository's Renovate configuration that needs to be fixed. As a precaution, Renovate will stop PRs until it is resolved.

Error type: undefined. Note: this is a nested preset so please contact the preset author if you are unable to fix it yourself.

Try switching to preact

Try switching the whole app to preact, and see if there are any performance improvements or size reductions.

Add analytics

Preferably find some new OSS solution to test out.

Default station for music player

If the user is the first time on the site, the music player should have default radio station ready to go ( BBC radio)
Or the last listened station.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.