{"id":21010634,"url":"https://github.com/prohetamine/radio-station","last_synced_at":"2026-02-28T13:02:52.149Z","repository":{"id":65479725,"uuid":"448563104","full_name":"prohetamine/radio-station","owner":"prohetamine","description":"🎙 📡 A free radio station on node js with a simple setup and the ability to conduct live broadcasts on the site","archived":false,"fork":false,"pushed_at":"2022-02-09T17:12:36.000Z","size":330139,"stargazers_count":28,"open_issues_count":0,"forks_count":8,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-10-06T18:50:46.484Z","etag":null,"topics":["ice","icecast","icy","internet-radio","internet-radio-player","internet-radio-station","internet-radio-website","javascript","javascript-library","node-radio","nodejs","online-radio","radio","radio-js","radio-station","sdr","sdr-tool","web-radio","web-radio-station"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/prohetamine.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-01-16T13:31:45.000Z","updated_at":"2025-08-14T13:11:17.000Z","dependencies_parsed_at":"2023-01-25T18:31:30.716Z","dependency_job_id":null,"html_url":"https://github.com/prohetamine/radio-station","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/prohetamine/radio-station","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/prohetamine%2Fradio-station","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/prohetamine%2Fradio-station/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/prohetamine%2Fradio-station/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/prohetamine%2Fradio-station/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/prohetamine","download_url":"https://codeload.github.com/prohetamine/radio-station/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/prohetamine%2Fradio-station/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29934963,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-28T13:00:17.143Z","status":"ssl_error","status_checked_at":"2026-02-28T12:59:13.669Z","response_time":90,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ice","icecast","icy","internet-radio","internet-radio-player","internet-radio-station","internet-radio-website","javascript","javascript-library","node-radio","nodejs","online-radio","radio","radio-js","radio-station","sdr","sdr-tool","web-radio","web-radio-station"],"created_at":"2024-11-19T09:21:50.669Z","updated_at":"2026-02-28T13:02:52.116Z","avatar_url":"https://github.com/prohetamine.png","language":"JavaScript","funding_links":["https://www.patreon.com/prohetamine"],"categories":[],"sub_categories":[],"readme":"![logo](https://github.com/prohetamine/radio-station/blob/main/media/logo.png)\n\n##### README is available in the following languages: [Russian](https://github.com/prohetamine/radio-station/blob/main/README/russian.md) | [English](https://github.com/prohetamine/radio-station/blob/main/README.md)\n\n\n# Radio Station\n\n\u003e radio-station - an easy way to deploy your radio station and become a radio host online in javascript.\n\n### Why ?\nI decided that I wanted to be a radio host, but I had no particular desire to understand the existing software and I created my own software, maybe someday it will become a standard, I tried to achieve a better result.\n\n### Get started\n\nInstall the npm module ```radio-station```\n\n```sh\n$ npm install radio-station\n```\n\nor\n\n```sh\n$ yarn add radio-station\n```\n\nor\n\n```sh\n$ npm install https://github.com/prohetamine/radio-station\n```\n\n### Examples and description\n\nConnecting the module\n\n```javascript\nconst RadioStation = require('radio-station')\n```\n\nI recommend looking at the examples of work right away before trying to use the module yourself. Examples of how to launch your radio station without a launcher and with a launcher can be found on my github repositories [node-web-radio](https://github.com/prohetamine/node-web-radio) and [launcher-web-radio](https://github.com/prohetamine/launcher-web-radio), also note that I supply options much simpler, in the form of docker, which are very easy to run and use [docker-node-web-radio](https://github.com/prohetamine/docker-node-web-radio) and [launcher-web-radio](https://github.com/prohetamine/docker-launcher-web-radio). You should use ```mp3``` files because they contain the metadata necessary for correct display pay attention to this.\n\n#### \u003ca name=\"radiostation\"\u003eRadioStation\u003c/a\u003e\n\nThe Object [RadioStation](#radiostation) has only one method to create which is a craft and returns objects: [track](#track), [stream](#stream) and functions: [addListener](#classic), [picture](#classic), [info](#classic), [onUse](#classic).\n\n##### object\n\n| key | value | default value | mandatory | information |\n| ------ | ------ | ------ | ------ | ------ |\n| patchWorkDir | text | ./station | no | working folder with the tracks and other system records |\n| isLauncher | boolean | true | no | launcher activation |\n| port | number | 9933 | no | the internal system port is also used to connect to the launcher |\n| login | text | /* random */ | none | used for authorization in the launcher |\n| password | text | /* random */ | none | used for authorization in the launcher |\n| isAutoStart | boolean | false | none | responsible for automatic start |\n| puppeteerLauncher | object | { headless: true, args: ['--no-sandbox'] } | no | puppeteer launcher object |\n| debug | boolean | true | none | enables debugging mode |\n| mainPort | number | false | no | outputs the main port to the console |\n\n\n```javascript\nconst { create } = require('radio-station')\n\n;(async () =\u003e {\n  const radio = await create({\n    pathWorkDir: `${__dirname}/tracks-data-folder`,\n    isLauncher: true,\n    port: 9933,\n    login: 'localhost',\n    password: 'hackme',\n  })\n\n  console.log(radio)\n\n  /*\n    {\n      track: {\n        load: [AsyncFunction: load],\n        loads: [AsyncFunction: loads],\n        unload: [AsyncFunction: unload],\n        all: [Function: all],\n        find: [Function: find],\n        info: [AsyncFunction: info],\n        picture: [AsyncFunction: picture],\n        onLoad: [Function (anonymous)],\n        onUnload: [Function (anonymous)]\n      },\n      stream: {\n        start: [Function: start],\n        pop: [Function: pop],\n        push: [AsyncFunction: push],\n        current: [Function: current],\n        all: [Function: all],\n        onStart: [Function (anonymous)],\n        onPush: [Function (anonymous)],\n        onPop: [Function (anonymous)],\n        onUse: [Function (anonymous)]\n      },\n      addListener: [Function: addListener],\n      picture: [AsyncFunction: picture],\n      info: [AsyncFunction: info],\n      onUse: [AsyncFunction: onUse],\n    }\n  */\n})()\n```\n\n#### \u003ca name=\"classic\"\u003eclassic\u003c/a\u003e\n\nFull working example [node-web-radio](https://github.com/prohetamine/node-web-radio)\n\n```javascript\n;(async () =\u003e {\n  const radio = await RadioStation.create({ /* ... */ })\n\n  await radio.track.loads(\n    path.join(__dirname, '/tracks-for-load')\n  )\n\n  app.get('/radio', (req, res) =\u003e {\n    radio.addListener(req, res)\n  })\n\n  io.on('connection', async socket =\u003e {\n    radio.onUse(info =\u003e {\n      socket.emit('onUse', info)\n    })\n  })\n\n  app.get('/picture', async (req, res) =\u003e {\n    radio.picture(req, res)\n  })\n\n  app.get('/info', async (req, res) =\u003e {\n    radio.info(req, res)\n  })\n})()\n```\n\n#### \u003ca name=\"track\"\u003etrack\u003c/a\u003e\n\nThe object [track](#radiostation) works with the file system, has functions [load](#load), [loads](#loads), [onLoad](#onload), [unload](#unload), [onUnload](#onunload), [all](#tracksall), [find](#find), [info](#info), [picture](#picture).\n\n#### \u003ca name=\"load\"\u003eload\u003c/a\u003e\n\nThe function [track.load](#track) loads a track into the radio station's file system, accepts the file path with the extension ```.mp3```, is a promise and returns the ```id``` of the loaded track or ```null``` as an error, also has a handler for the [track.onLoad](#onload) loading event.\n\n```javascript\nconst { create } = require('radio-station')\n\n;(async () =\u003e {\n  const radio = await create({ /* ... */ })\n\n  const loadId = await radio.track.load('/path.mp3')\n\n  console.log(loadId) // de40e284351ed735b5c4a8cb73cc036a\n})()\n```\n\n#### \u003ca name=\"loads\"\u003eloads\u003c/a\u003e\n\nThe function [track.loads](#track) loads all tracks from a folder into the radio station's file system, accepts the path to a folder with files with the extensions ```.mp3```, is a promise and returns an array of elements of the ```id``` of the downloaded track or ```null``` as an error, also has a handler for the download event [track.onLoad](#onload).\n\n```javascript\nconst { create } = require('radio-station')\n\n;(async () =\u003e {\n  const radio = await create({ /* ... */ })\n\n  const loadsId = await radio.track.loads('/folder')\n\n  console.log(loadsId)\n\n  /*\n    [\n      \"de40e284351ed735b5c4a8cb73cc036a\",\n      null,\n      \"1de781bc71488694434557166e3f8c51\"\n    ]\n  */\n})()\n```\n\n#### \u003ca name=\"onload\"\u003eonLoad\u003c/a\u003e\n\nThe function [track.onLoad](#track) processes any download of a track to the file system, even taking into account the download via the launcher, takes a callback as the first parameter, passes the ```id``` of the downloaded track or ```null``` to the callback as an error.\n\n```javascript\nconst { create } = require('radio-station')\n\n;(async () =\u003e {\n  const radio = await create({ /* ... */ })\n\n  radio.track.onLoad(id =\u003e {\n    const name = radio.track.find(id).name\n    console.log(name, id) // track name and id\n  })\n\n  radio.track.load('/path.mp3')\n})()\n```\n\n#### \u003ca name=\"unload\"\u003eunload\u003c/a\u003e\n\nThe function [track.unload](#track) deletes a track from the file system, accepts the ```id``` parameter of the track being deleted, always returns the ```id``` of the deleted track or \"null\" as an error, also has a handler for the loading event [track.onUnload](#onunload).\n\n```javascript\nconst { create } = require('radio-station')\n\n;(async () =\u003e {\n  const radio = await create({ /* ... */ })\n\n  const loadId = await radio.track.load('/path.mp3')\n\n  console.log(loadId) // de40e284351ed735b5c4a8cb73cc036a\n\n  const unloadId = await radio.track.unload(loadId)\n\n  console.log(unloadId) // de40e284351ed735b5c4a8cb73cc036a\n})()\n```\n\n#### \u003ca name=\"onunload\"\u003eonUnload\u003c/a\u003e\n\nThe function [track.onUnload](#track) handles any deletion of a track from the file system, even taking into account the download via the launcher, takes a callback as the first parameter, passes the ```id``` of the deleted track or ```null``` to the callback as an error.\n\n```javascript\nconst { create } = require('radio-station')\n\n;(async () =\u003e {\n  const radio = await create({ /* ... */ })\n\n  radio.track.onUnload(id =\u003e {\n    console.log(id) // id track de40e284351ed735b5c4a8cb73cc036a\n  })\n\n  const loadId = await radio.track.load('/path.mp3')\n\n  console.log(loadId) // de40e284351ed735b5c4a8cb73cc036a\n\n  radio.track.unload(loadId)\n})()\n```\n\n#### \u003ca name=\"tracksall\"\u003eall\u003c/a\u003e\n\nThe function [track.all](#track) returns information about all tracks in the file system as an array with objects.\n\n```javascript\nconst { create } = require('radio-station')\n\n;(async () =\u003e {\n  const radio = await create({ /* ... */ })\n\n  const tracks = radio.track.all()\n  console.log(tracks)\n\n  /*\n    [\n      {\n        id: 'de40e284351ed735b5c4a8cb73cc036a',\n        path: '/track3.mp3',\n        parentPath: '/track3.mp3',\n        name: 'track3.mp3'\n      },\n      {\n        id: '1de781bc71488694434557166e3f8c51',\n        path: '/track1.mp3',\n        parentPath: '/track1.mp3',\n        name: 'track1.mp3'\n      },\n      {\n        id: '629cf251fad1890d8ffea3c6f5b19cf5',\n        path: '/track2.mp3',\n        parentPath: '/track2.mp3',\n        name: 'track2.mp3'\n      }\n    ]\n  */\n})()\n```\n\n#### \u003ca name=\"find\"\u003efind\u003c/a\u003e\n\nThe function [track.find](#track) returns the system information of the track in the file system, in the form of an object or ```null```.\n\n```javascript\nconst { create } = require('radio-station')\n\n;(async () =\u003e {\n  const radio = await create({ /* ... */ })\n\n  const loadId = await radio.track.load('/path.mp3')\n\n  const systemInfo = radio.track.find(loadId)\n\n  console.log(systemInfo)\n\n  /*\n    {\n      id: '629cf251fad1890d8ffea3c6f5b19cf5',\n      path: '/path.mp3',\n      parentPath: '/path.mp3',\n      name: 'path.mp3'\n    }\n  */\n})()\n```\n\n#### \u003ca name=\"info\"\u003einfo\u003c/a\u003e\n\nFunction [track.info](#track) returns complete information about the file, takes the parameter ```id```, returns an object or ```null```.\n\n```javascript\nconst { create } = require('radio-station')\n\n;(async () =\u003e {\n  const radio = await create({ /* ... */ })\n\n  const loadId = await radio.track.load('/path.mp3')\n\n  const fullInfo = await radio.track.info(loadId)\n\n  console.log(fullInfo)\n\n  /*\n    {\n      id: 'de40e284351ed735b5c4a8cb73cc036a',\n      path: 'track.mp3',\n      parentPath: 'track.mp3',\n      name: 'track.mp3',\n      format: {\n        tagTypes: ['ID3v2.4'],\n        trackInfo: [],\n        lossless: false,\n        container: 'MPEG',\n        codec: 'MPEG 1 Layer 3',\n        sampleRate: 48000,\n        numberOfChannels: 2,\n        bitrate: 128000,\n        codecProfile: 'CBR',\n        duration: 195.624\n      },\n      common: {\n        track: { no: 2, of: null },\n        disk: { no: null, of: null },\n        movementIndex: {},\n        title: '...',\n        artists: [ '...' ],\n        artist: '...',\n        album: '...',\n        encodersettings: 'Lavf58.76.100'\n      },\n      isAlbumImage: true\n    }\n  */\n})()\n```\n\n#### \u003ca name=\"picture\"\u003epicture\u003c/a\u003e\n\nThe function [track.picture](#track) takes the parameter ```id```, returns an object or ```null```.\n\n```javascript\nconst { create } = require('radio-station')\n\n;(async () =\u003e {\n  const radio = await create({ /* ... */ })\n\n  const loadId = await radio.track.load('/path.mp3')\n\n  const picture = await radio.track.picture(loadId)\n\n  console.log(picture)\n\n  /*\n    {\n      contentType: 'image/png',\n      contentLength: 121291,\n      buffer: \u003cBuffer 89 50 00 ... 121241 more bytes\u003e\n    }\n  */\n})()\n```\n\n#### \u003ca name=\"stream\"\u003estream\u003c/a\u003e\n\nObject [stream](#radiostation) works with ether, has functions [start](#start), [onStart](#onstart), [push](#push), [onPush](#onpush), [pop](#pop), [onPop](#onpop), [all](#streamall), [current](#current), [onUse](#onuse).\n\n#### \u003ca name=\"start\"\u003estart\u003c/a\u003e\n\nThe function [stream.start](#stream) starts the stream if in [RadioStation](#radiostation) ```isAutoStart``` has the value ```false```, returns ```true``` or ```false``` as an error, also has a load event handler [stream.onStart](#onstart) which is triggered regardless of the function call [stream.start](#stream).\n\n```javascript\nconst { create } = require('radio-station')\n\n;(async () =\u003e {\n  const radio = await create({ /* ... */ })\n\n  const isStart = radio.stream.start()\n  await new Promise(radio.stream.onStart)\n  console.log(isStart) // true\n})()\n```\n\n#### \u003ca name=\"onstart\"\u003eonStart\u003c/a\u003e\n\nThe function [stream.onStart](#stream) handles the start of the broadcast, accepts the callback parameter.\n\n```javascript\nconst { create } = require('radio-station')\n\n;(async () =\u003e {\n  const radio = await create({ /* ... */ })\n\n  radio.track.onStart(() =\u003e {\n    console.log('start stream!')\n  })\n})()\n```\n\n#### \u003ca name=\"push\"\u003epush\u003c/a\u003e\n\nThe function [stream.push](#push) forms a queue of tracks by adding a track to the end of the queue, accepts ```id```, returns ```streamId``` of the uploaded track or ```null``` as an error, also has an event handler [stream.onPush](#onpush).\n\n```javascript\nconst { create } = require('radio-station')\n\n;(async () =\u003e {\n  const radio = await create({ /* ... */ })\n\n  const loadId = await radio.track.load('path.mp3')\n  const streamId = await radio.stream.push(loadId)\n\n  console.log(streamId) // 36ed07fd2438271197e37420b582c6f575608bcb1580ff21675311e1913a035e\n})()\n```\n\n#### \u003ca name=\"onpush\"\u003eonPush\u003c/a\u003e\n\nThe function [stream.onPush](#stream) processes any addition of a track to the queue, even taking into account the addition via the launcher, takes a callback as the first parameter, passes the ```streamId``` of the uploaded track or ```null``` to the callback as an error.\n\n```javascript\nconst { create } = require('radio-station')\n\n;(async () =\u003e {\n  const radio = await create({ /* ... */ })\n\n  radio.track.onPush(streamId =\u003e {\n    console.log(streamId)\n  })\n\n  const loadId = await radio.track.load('path.mp3')\n  const streamId = await radio.stream.push(loadId)\n\n  radio.stream.push(streamId)\n})()\n```\n\n#### \u003ca name=\"pop\"\u003epop\u003c/a\u003e\n\nThe function [stream.pop](#pop) deletes a track from the queue, accepts ```streamId```, returns ```streamId``` of the deleted track or ```null``` as an error, also has an event handler [stream.onPop](#onpop).\n\n```javascript\nconst { create } = require('radio-station')\n\n;(async () =\u003e {\n  const radio = await create({ /* ... */ })\n\n  const loadId = await radio.track.load('path.mp3')\n\n  const streamId = await radio.stream.push(loadId)\n  const popStreamId = await radio.stream.pop(streamId)\n\n  console.log(popStreamId) // 36ed07fd2438271197e37420b582c6f575608bcb1580ff21675311e1913a035e\n})()\n```\n\n#### \u003ca name=\"onpop\"\u003eonPop\u003c/a\u003e\n\nThe function [stream.onPop](#stream) handles any deletion of a track from the queue, even taking into account deletion via the launcher, accepts callback as the first parameter, passes ```streamId``` of the uploaded track or ```null``` to callback as an error.\n\n```javascript\nconst { create } = require('radio-station')\n\n;(async () =\u003e {\n  const radio = await create({ /* ... */ })\n\n  radio.track.onPop(streamId =\u003e {\n    console.log(streamId) // 36ed07fd2438271197e37420b582c6f575608bcb1580ff21675311e1913a035e\n  })\n\n  const loadId = await radio.track.load('path.mp3')\n  const streamId = await radio.stream.push(loadId)\n  radio.stream.pop(streamId)\n})()\n```\n\n#### \u003ca name=\"streamall\"\u003eall\u003c/a\u003e\n\nThe function [stream.all](#stream) returns information about all tracks in the queue as an array with objects. It has three types of tracks. ```random```, ```queue```, ```error```.\n\n| type | type information |\n| ------ | ------ |\n| random | the track is selected randomly, the dynamic type of track appears when there is no queue |\n| queue | track in the queue |\n| error | track deleted or damaged |\n\n```javascript\nconst { create } = require('radio-station')\n\n;(async () =\u003e {\n  const radio = await create({ /* ... */ })\n\n  const tracks = radio.stream.all()\n  console.log(tracks)\n\n  /*\n    [\n      {\n        id: '1de781bc71488694434557166e3f8c51',\n        path: 'track1.mp3',\n        parentPath: 'track1.mp3',\n        name: 'track1.mp3',\n        type: 'random'\n      },\n      {\n        id: '1de781bc71488694434557166e3f8c51',\n        path: 'track2.mp3',\n        parentPath: 'track2.mp3',\n        name: 'track2.mp3',\n        streamId: 'f365c82f7bbe33cf52e60be959f4741adf08d63db0df8e080ef8585d56ce0b29',\n        type: 'queue'\n      },\n      {\n        id: '1de781bc71488694434557166e3f8c51',\n        path: 'track2.mp3',\n        parentPath: 'track2.mp3',\n        name: 'track2.mp3',\n        streamId: 'f365c82f7bbe33cf52e60be959f4741adf08d63db0df8e080ef8585d56ce0b29',\n        type: 'error'\n      }\n    ]\n  */\n})()\n```\n\n#### \u003ca name=\"current\"\u003ecurrent\u003c/a\u003e\n\nThe function [current](#current) returns information about the current track on the air in the form of an object or ```null``` as an error, can be considered an event handler [stream.onUse](#onuse).\n\n```javascript\nconst { create } = require('radio-station')\n\n;(async () =\u003e {\n  const radio = await create({ /* ... */ })\n\n  const track = radio.track.current()\n\n  console.log(track)\n\n  /*\n    {\n      id: '1de781bc71488694434557166e3f8c51',\n      path: '/track1.mp3',\n      parentPath: '/track1.mp3',\n      name: 'track1.mp3',\n      type: 'random'\n    }\n  */\n})()\n```\n\n#### \u003ca name=\"onuse\"\u003eonUse\u003c/a\u003e\n\nThe function [stream.onUse](#stream) processes the update of the current track, accepts the callback with the first parameter, passes the position in the queue to the callback with the first parameter, and the track information with the second.\n\n```javascript\nconst { create } = require('radio-station')\n\n;(async () =\u003e {\n  const radio = await create({ /* ... */ })\n\n  radio.stream.onUse((index, track) =\u003e {\n    console.log(index, track)\n    /*\n    0 {\n      id: '1de781bc71488694434557166e3f8c51',\n      path: '/track1.mp3',\n      parentPath: '/track1.mp3',\n      name: 'track1.mp3',\n      type: 'random'\n    }\n    */\n  })\n})()\n```\n\n### Contacts\n\nMy Telegram: [@prohetamine](https://t.me/prohetamine), [channel](https://t.me/prohetamines)\n\nEmail: prohetamine@gmail.com\n\nDonat money: [patreon](https://www.patreon.com/prohetamine)\n\nIf you have any questions and/or suggestions, please email me in telegram, if you find any bugs also let me know, I will be very grateful.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fprohetamine%2Fradio-station","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fprohetamine%2Fradio-station","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fprohetamine%2Fradio-station/lists"}