Skip to content

Commit

Permalink
Added documentation and little refactoring (#329)
Browse files Browse the repository at this point in the history
  • Loading branch information
ch1ller0 authored Jan 14, 2023
1 parent ec1d491 commit bf1b57a
Show file tree
Hide file tree
Showing 17 changed files with 232 additions and 474 deletions.
13 changes: 11 additions & 2 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,16 @@ jobs:
at: ~/repo
- run:
name: Running linter
command: yarn lint:ci
command: yarn test:lint

typecheck:
<<: *defaults
steps:
- attach_workspace:
at: ~/repo
- run:
name: Running typechecks
command: yarn test:types

test:
<<: *defaults
Expand All @@ -64,7 +73,7 @@ jobs:
at: ~/repo
- run:
name: Running unit tests and coverage
command: yarn test:cov
command: yarn test:unit:cov
- persist_to_workspace:
root: ~/repo
<<: *whitelist
Expand Down
11 changes: 11 additions & 0 deletions .github/ISSUE_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
* **I'm submitting a ...**
[ ] bug report
[ ] feature request
[ ] question about the decisions made in the repository
[ ] question about how to use this project

* **Summary**



* **Other information** (e.g. detailed explanation, stacktraces, related issues, suggestions how to fix, links for us to have context, eg. StackOverflow, personal fork, etc.)
7 changes: 7 additions & 0 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
* **What kind of change does this PR introduce?** (Bug fix, feature, docs update, ...)

* **What is the current behavior?** (You can also link to an open issue here)

* **What is the new behavior (if this is a feature change)?**

* **Other information**:
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ station.start();
Creating a station is as simple as
```javascript
const myAwesomeStation = new Station({
verbose: false, // if true - enables verbose logging (great for debugging),
verbose: false, // if true - enables verbose logging (for debugging purposes),
responseHeaders: { // in case you want custom response headers for your endpoint
'icy-genre': 'jazz'
}
Expand Down Expand Up @@ -130,11 +130,11 @@ station.on(PUBLIC_EVENTS.ERROR, (e) => { handleError(e) });
> or just go to [examples](./examples/server.js)
## Development
```
yarn dev
yarn start
```
or
```
yarn dev [path/to/your_mp3tracks]
yarn start [path/to/your_mp3tracks]
# in this case it would take a little more time, just wait
```

Expand Down
30 changes: 7 additions & 23 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
{
"name": "@fridgefm/radio-core",
"author": "Grigory Gorshkov",
"version": "3.1.3",
"version": "3.1.4",
"description": "internet radio engine made on NodeJS platform",
"license": "MIT",
"main": "./lib/index.js",
"types": "./lib/index.d.ts",
"engines": {
"node": ">=12.22.2"
},
"keywords": [
"radio",
"music",
Expand All @@ -17,24 +14,18 @@
"stream",
"nodejs"
],
"husky": {
"hooks": {
"pre-commit": "lint-staged",
"pre-push": "yarn test"
}
},
"repository": {
"type": "git",
"url": "git+https://github.com/ch1ller0/fridgefm-radio-core.git"
},
"scripts": {
"dev": " yarn watch & tsnd ./examples/server.ts",
"start": " yarn watch & tsnd ./examples/server.ts",
"build": "tsc",
"watch": "tsc --watch",
"lint": "eslint --fix ./src/**/*",
"lint:ci": "eslint ./src/**/*",
"test": "jest --config jest.config.json",
"test:cov": "yarn test --collectCoverage=true"
"test:types": "tsc --noEmit",
"test:lint": "eslint ./src/**/*",
"test:unit": "jest --config jest.config.json",
"test:unit:cov": "yarn test:unit --collectCoverage=true"
},
"dependencies": {
"chalk": "^4.1.2",
Expand Down Expand Up @@ -63,24 +54,17 @@
"eslint-plugin-import": "^2.25.2",
"eslint-plugin-jest": "^25.2.2",
"eslint-plugin-prettier": "^4.0.0",
"husky": "^7.0.4",
"jest": "^27.3.1",
"lint-staged": ">=11.2.3",
"prettier": "^2.4.1",
"ts-jest": "^27.0.7",
"ts-node-dev": "^1.1.8",
"typescript": "^4.4.4"
"typescript": "^4.9.4"
},
"bugs": {
"url": "https://github.com/ch1ller0/fridgefm-radio-core/issues"
},
"homepage": "http://fridgefm.com",
"directories": {
"example": "examples"
},
"lint-staged": {
"./src/**/*.{js,ts}": [
"yarn lint"
]
}
}
158 changes: 75 additions & 83 deletions src/base/Playlist/Playlist.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,96 +3,88 @@ import { captureTime } from '../../utils/time';
import { PUBLIC_EVENTS } from '../../features/EventBus/events';
import { createTrackMap } from './methods';

import type { TPlaylist, TrackMap, TrackList, ReorderCb, PathList, PlaylistElement } from './Playlist.types';
import type { TPlaylist, TrackMap, ReorderCb, PathList } from './Playlist.types';
import type { EventBus } from '../../features/EventBus/EventBus';
import type { InfoEvent } from '../../features/EventBus/events';
import type { TTrack } from '../Track/Track.types';

type Deps = { eventBus: EventBus };

export class Playlist implements TPlaylist {
private _currentIndex = -1;

private _list: PathList = [];

private _tracksMap: TrackMap = new Map();

private _folders: Set<string> = new Set();

private _deps: Deps;

private revalidate() {
const ct = captureTime();
this._list = createList(Array.from(this._folders));
this._tracksMap = createTrackMap(this._list);

const result = this.getList();
this._emitInfo({ event: 'revalidate', message: 'Playlist revalidated', timings: ct() });
return result;
}

private _emitInfo(a: InfoEvent) {
this._deps.eventBus.emit(PUBLIC_EVENTS.INFO, { name: 'playlist', ...a });
}

constructor(deps: Deps) {
this._deps = deps;
}

public addFolder(folder: string) {
this._folders.add(folder);
return this.revalidate();
}

public getNext(): PlaylistElement {
if (this._list.length - 1 === this._currentIndex) {
// the playlist drained
export const createPlaylist = (deps: Deps) => {
const folders = new Set<string>();
const emitInfo = (a: InfoEvent) => deps.eventBus.emit(PUBLIC_EVENTS.INFO, { name: 'playlist', ...a });

let currentIndex = -1;
let list: PathList = [];
let tracksMap: TrackMap = new Map();

const instance: TPlaylist = {
addFolder: (folder: string) => {
folders.add(folder);
// eslint-disable-next-line @typescript-eslint/no-use-before-define
return revalidate();
},
getList: () => {
return list.map((v, i) => {
const tra = tracksMap.get(v) as TTrack;

return {
...tra,
isPlaying: currentIndex === i,
};
});
},
reorder: (cb: ReorderCb) => {
const ct = captureTime();
this.revalidate();
this._currentIndex = 0;
this._deps.eventBus.emit(PUBLIC_EVENTS.RESTART, this.getList(), ct());
} else {
this._currentIndex += 1;
}
const nextPath = this._list[this._currentIndex] as string;
const nextTrack = this._tracksMap.get(nextPath);

if (!nextTrack) {
this._emitInfo({ level: 'warn', event: 'no-next-track', message: `No next track found for ${nextPath}` });
// try next tracks
return this.getNext();
}
nextTrack.playCount += 1;

return { ...nextTrack, isPlaying: true };
}

public reorder(cb: ReorderCb) {
const prevList = instance.getList();
const currentlyPlaying = prevList.find((v) => !!v.isPlaying);

list = cb(prevList).map((b) => b.fsStats.fullPath);
currentIndex = list.findIndex((v) => v === currentlyPlaying?.fsStats.fullPath);

emitInfo({
level: 'info',
event: 'reorder',
message: 'Playlist reordered',
timings: ct(),
});

return instance.getList();
},
getNext: () => {
if (list.length - 1 === currentIndex) {
// the playlist drained
const ct = captureTime();
// eslint-disable-next-line @typescript-eslint/no-use-before-define
revalidate();
currentIndex = 0;
deps.eventBus.emit(PUBLIC_EVENTS.RESTART, instance.getList(), ct());
} else {
currentIndex += 1;
}
const nextPath = list[currentIndex] as string;
const nextTrack = tracksMap.get(nextPath);

if (!nextTrack) {
emitInfo({ level: 'warn', event: 'no-next-track', message: `No next track found for ${nextPath}` });
// try next tracks
return instance.getNext();
}
nextTrack.playCount += 1;

return { ...nextTrack, isPlaying: true };
},
};

const revalidate = () => {
const ct = captureTime();
const prevList = this.getList();
const currentlyPlaying = prevList.find((v) => !!v.isPlaying);
list = createList(Array.from(folders));
tracksMap = createTrackMap(list);

this._list = cb(prevList).map((b) => b.fsStats.fullPath);
this._currentIndex = this._list.findIndex((v) => v === currentlyPlaying?.fsStats.fullPath);

this._emitInfo({
level: 'info',
event: 'reorder',
message: 'Playlist reordered',
timings: ct(),
});

return this.getList();
}

public getList(): TrackList {
return this._list.map((v, i) => {
const tra = this._tracksMap.get(v) as TTrack;
const result = instance.getList();
emitInfo({ event: 'revalidate', message: 'Playlist revalidated', timings: ct() });
return result;
};

return {
...tra,
isPlaying: this._currentIndex === i,
};
});
}
}
return instance;
};
4 changes: 2 additions & 2 deletions src/base/Playlist/methods.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Mp3 from '../../utils/mp3';
import { extractLast, shuffleArray } from '../../utils/funcs';
import { Track } from '../Track/Track';
import { createTrack } from '../Track/Track';

import type { TrackList, TrackMap } from './Playlist.types';

Expand All @@ -17,7 +17,7 @@ export const createTrackMap = (paths: readonly string[]): TrackMap =>
return acc;
}

return acc.set(path, new Track(path));
return acc.set(path, createTrack(path));
}, new Map() as TrackMap);

export const SHUFFLE_METHODS = {
Expand Down
4 changes: 2 additions & 2 deletions src/base/Queuestream.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Readable, Transform, Writable } from 'stream';
import devnull from 'dev-null';
import { captureTime } from '../utils/time';
import { Prebuffer } from '../features/Prebuffer';
import { createPrebuffer } from '../features/Prebuffer';
import { PUBLIC_EVENTS } from '../features/EventBus/events';

import type { EventBus } from '../features/EventBus/EventBus';
Expand All @@ -27,7 +27,7 @@ export class QueueStream {
private _trackStream: Readable;

// prebuffering for faster client response (side-effect)
private _prebuffer = new Prebuffer();
private _prebuffer = createPrebuffer();

constructor(deps: Deps) {
this._deps = deps;
Expand Down
10 changes: 6 additions & 4 deletions src/base/Station.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { EventBus } from '../features/EventBus/EventBus';
import { createEventBus, EventBus } from '../features/EventBus/EventBus';
import { PUBLIC_EVENTS } from '../features/EventBus/events';
import { captureTime } from '../utils/time';
import { mergeConfig, Config } from '../config/index';
import { Playlist } from './Playlist/Playlist';
import { createPlaylist } from './Playlist/Playlist';
import { QueueStream } from './Queuestream';

import type { ClientRequest, ServerResponse } from 'http';
Expand All @@ -22,8 +22,8 @@ export class Station implements TStation {

constructor(extConfig?: Partial<Config>) {
const config = mergeConfig(extConfig || {});
const eventBus = new EventBus({ config });
const playlist = new Playlist({ eventBus });
const eventBus = createEventBus({ config });
const playlist = createPlaylist({ eventBus });
const queuestream = new QueueStream({ playlist, eventBus });

this._deps = {
Expand All @@ -45,6 +45,8 @@ export class Station implements TStation {
eventBus.emit(PUBLIC_EVENTS.START, this.getPlaylist(), ct());
}

public togglePause(): void {}

public addFolder(folder: string) {
return this._deps.playlist.addFolder(folder);
}
Expand Down
Loading

0 comments on commit bf1b57a

Please sign in to comment.