Skip to content

Commit

Permalink
small adjustments
Browse files Browse the repository at this point in the history
  • Loading branch information
BigTeri committed Aug 28, 2016
1 parent 1fd08b2 commit a311baf
Show file tree
Hide file tree
Showing 9 changed files with 255 additions and 37 deletions.
210 changes: 210 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
# jscast
[![NPM Version](http://img.shields.io/npm/v/jscast.svg?style=flat)](https://www.npmjs.org/package/jscast)

A SHOUTcast Server/Library written in JavaScript

## Installation

As dependency:

```sh
$ npm install jscast
```

For developing forks:

```sh
$ git clone https://github.com/BigTeri/jscast
```

```sh
$ cd jscast
$ npm i
```

```sh
$ npm start
```

For minimalists:

```sh
$ npm i -g jscast
```

```sh
$ jscast-server
```

### Prerequisites

jscast uses [fluent-ffmpeg](https://github.com/fluent-ffmpeg/node-fluent-ffmpeg#prerequisites) so ffmpeg **needs** to be installed on your system.

## Quick Start

### Using cli

Install jscast globally:

```sh
$ npm i -g jscast
```

Use the new command to start a Server:

```sh
$ jscast-server
```
choose a different port with *-p 8888*

### Using script

```js
var Server = require("jscast").Server;

new Server().on("play", function (item, metadata) {
console.log("playing " + metadata.options.StreamTitle);
}).start(8000, function (server) {
console.log("jscast server is running on http://localhost:" + server.port);
console.log("go to http://localhost:" + server.port + "/manage to manage your playlists");
});
```

## Item Types

Built-in item types:

- **File** gets audio files from the filesystem using the *filename* option
- **YouTube** fetches the audio data and info from YouTube using an *url* option
- Use **Stream** to hand over a Readable Stream object with the *stream* option

[more](#/Custom Items) item types

## Storage Types

Built-in storage types:

- JSON creates a folder with a json file per playlist, filename is the playlist id
- Memory stores playlists in memory, so **changes will be lost** on shutdown

If thats not enough, you can create your [own one](#/Custom Storages)

## Examples

### Custom Items

jscast has playlists with typed items.
You can easily add your own item type:

```js
var fs = require("fs");
var jscast = require("jscast");
var Item = jscast.Item;
var Server = jscast.Server;

function MyItemType() {
this.streamNeedsPostProcessing = true; // indicates if stream should be post processed to mp3
}

MyItemType.prototype.getStream = function (item, done) {
// get stream code...
console.log(item.type); // MyItem
done && done(err, stream);
};

MyItemType.prototype.getMetadata = function (item, done) {
// get metadata code...
console.log(item.options.myProp); // myValue
done && done(err, {
StreamTitle: "my title"
});
};

Item.registerType("MyItem", new MyItemType());

new Server({
storageType: "Memory",
playlists: [{
type: "MyItem",
options: {
myProp: "myValue"
}
}, {
type: "YouTube",
options: {
url: "https://www.youtube.com/watch?v=hhHXAMpnUPM"
}
}, {
type: "Stream",
options: {
title: "A cool audio stream!",
stream: fs.creadReadStream("./sound.mp3")
}
}, {
type: "File",
options: {
title: "NICE TRACK!",
filename: "./myTrack.mp3"
}
}]
}).start();
```

### Custom Storages

You can use the built-in [storage types](#/API) or create your own one:

```js
var fs = require("fs");
var jscast = require("jscast");
var Storage = jscast.Storage;
var Server = jscast.Server;

function MyStorageType() {
this.isFillable = true; // indicates that this type can be pre filled on init
}

MyStorageType.prototype.activate = function (options, done) {
// initialize code...
done && done(err);
};

MyStorageType.prototype.fill = function (playlists, done) {
// fill storage from playlists option in Server and Station class
done && done(err);
};

MyStorageType.prototype.findAll = function (done) {
// findAll code...
done && done(err, playlists);
};

MyStorageType.prototype.insert = function (playlist, done) {
// insert code...
done && done(err);
};

MyStorageType.prototype.update = function (playlist, done) {
// update code...
done && done(err);
};

MyStorageType.prototype.remove = function (playlistId, done) {
// remove code...
done && done(err);
};

Storage.registerType("MyStorage", new MyStorageType());

new Server({
storageType: "MyStorage"
}).start();
```

## API

TODO...

## License

MIT
2 changes: 1 addition & 1 deletion build/server/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de

var storageTypeNames = _storage2.default.getTypeNames();

_commander2.default.version(_package2.default.version).option("-p, --port [port]", "sets server port", parseInt).option("-c, --country [country]", "allow specific country only e.g. US").option("--storageType [storageType]", "use storage type, builtin types: " + storageTypeNames.join(", ")).parse(process.argv);
_commander2.default.version(_package2.default.version).option("-p, --port [port]", "sets server port", parseInt).option("-c, --country [country]", "only allow specific country e.g. US").option("--storageType [storageType]", "use storage type, built-in types: " + storageTypeNames.join(", ")).parse(process.argv);

new _2.default({
storageType: _commander2.default.storageType,
Expand Down
2 changes: 1 addition & 1 deletion build/server/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ function _possibleConstructorReturn(self, call) { if (!self) { throw new Referen

function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }

var jscastDescription = "jscast - A SHOUTcast Server written in JavaScript";
var jscastDescription = "jscast - A SHOUTcast Server/Library written in JavaScript";
var jscastUrl = "https://github.com/BigTeri/jscast";

var Server = function (_EventEmitter) {
Expand Down
20 changes: 3 additions & 17 deletions demo/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,6 @@ const mapYouTube = function (url) {
};
};

const germanPlaylist = [
"https://www.youtube.com/watch?v=6S1Yupk77pA",
"https://www.youtube.com/watch?v=4rRYqtehPk8",
"https://www.youtube.com/watch?v=RZ6O4yvgQ3U",
"https://www.youtube.com/watch?v=ntoqc6hFkhE",
"https://www.youtube.com/watch?v=LUlZ5n0cyak",
"https://www.youtube.com/watch?v=mJbJDqEJ6Iw",
"https://www.youtube.com/watch?v=BoX5U8rAsNA",
"https://www.youtube.com/watch?v=8nSPcxPR-6I",
"https://www.youtube.com/watch?v=uDe-EdAEKvw",
"https://www.youtube.com/watch?v=YO1GBsuzTWU"
].map(mapYouTube);

const yogscastPlaylist = [
"https://www.youtube.com/watch?v=99dM8__wphY",
"https://www.youtube.com/watch?v=gqELqRCnW6g",
Expand All @@ -39,11 +26,10 @@ const suicidePlaylist = [
].map(mapYouTube);

new Server({
storageType: "JSON",
storageType: "Memory",
playlists: [
// yogscastPlaylist,
// germanPlaylist,
// suicidePlaylist
yogscastPlaylist,
suicidePlaylist
],
allow: (client) => {
if (ip.isEqual(client.ip, "127.0.0.1") || client.ip === "::1") return true;
Expand Down
22 changes: 18 additions & 4 deletions manage/index.css
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
.head {
padding-left: 20px;
background-color: #FF5722;
color: #FFF;
}
.toolbar {
background-color: #FF5722;
color: #FFF;
padding: 10px;
flex: 0 0 auto;
}
.toolbar input {
color: #FF5722;
border: 0;
padding: 5px 10px;
margin: 3px 8px;
}
.toolbar-icon {
padding: 5px;
Expand All @@ -15,17 +23,23 @@
.icon-button {
cursor: pointer;
}
.playlist-list {}
.playlist-list {
padding: 0 20px;
}
.playlist-item {
border-radius: 5px;
padding: 10px 20px;
padding: 10px 0;
margin: 10px 0;
width: 100%;
max-width: 600px;
}
.playlist-title {
color: #FF5722;
}
.item-item {
margin: 5px 0;
margin: 15px 0;
}
.item-item .item-type {
margin: 0;
}
.item-item.is-playing .item-type {
color: #FF5722;
Expand Down
21 changes: 12 additions & 9 deletions manage/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<meta name="msapplication-tap-highlight" content="no">
<meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width">
<meta name="theme-color" content="#FF5722">
<title>jscast webinterface</title>
<title>jscast - manage</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.0/jquery.min.js" charset="utf-8"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.0/knockout-min.js" charset="utf-8"></script>
<script src="./sockets/socket.io.js" charset="utf-8"></script>
Expand All @@ -22,32 +22,35 @@
</head>

<body class="layout-vertical">
<div class="head">
<h1 class="head-title">jscast</h1>
</div>
<div class="toolbar layout-horizontal">
<div class="toolbar-icon disable-selection" data-bind="click: addPlaylist">
<i class="material-icons">playlist_add</i>
</div>
<input type="url" class="flex" data-bind="value: itemUrl">
<div class="toolbar-icon disable-selection" data-bind="click: addItem">
<i class="material-icons">add</i>
</div>
<div class="toolbar-icon disable-selection" data-bind="click: next">
<i class="material-icons">skip_next</i>
</div>
<input type="url" class="flex" placeholder="YouTube Url" data-bind="value: itemUrl">
<div class="toolbar-icon disable-selection" data-bind="click: addItem">
<i class="material-icons">add</i>
</div>
</div>

<div class="flex" style="overflow-x: auto;">
<div class="playlist-list layout-vertical layout-center" data-bind="foreach: playlists">
<div class="playlist-item">
<div class="playlist-title layout-horizontal">
<div class="flex" data-bind="text: 'Playlist #' + _id"></div>
<div class="playlist-title layout-horizontal layout-center">
<h2 class="flex" data-bind="text: 'Playlist #' + _id"></h2>
<div class="icon-button disable-selection" data-bind="click: $root.playPlaylist.bind($root)">
<i class="material-icons">play_arrow</i>
</div>
</div>
<div class="item-list layout-vertical" data-bind="foreach: items">
<div class="item-item" data-bind="css: { 'is-playing': $root.isPlaying(_id) }">
<div class="layout-horizontal">
<div class="item-type flex" data-bind="text: type + ' #' + _id"></div>
<div class="layout-horizontal layout-center">
<h3 class="item-type flex" data-bind="text: type + ' #' + _id"></h3>
<i class="item-hearing material-icons">hearing</i>
</div>
<!-- ko with: options -->
Expand Down
9 changes: 7 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
{
"name": "jscast",
"version": "0.1.0",
"description": "A SHOUTcast Server written in JavaScript",
"version": "0.1.1",
"description": "A SHOUTcast Server/Library written in JavaScript",
"author": "BigTeri",
"license": "MIT",
"repository": "https://github.com/BigTeri/jscast",
"bugs": "https://github.com/BigTeri/jscast/issues",
"main": "build/index.js",
"dependencies": {
"async": "^2.0.1",
"commander": "^2.9.0",
Expand Down
4 changes: 2 additions & 2 deletions src/server/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ const storageTypeNames = Storage.getTypeNames();
program
.version(pkg.version)
.option("-p, --port [port]", "sets server port", parseInt)
.option("-c, --country [country]", "allow specific country only e.g. US")
.option("--storageType [storageType]", "use storage type, builtin types: " + storageTypeNames.join(", "))
.option("-c, --country [country]", "only allow specific country e.g. US")
.option("--storageType [storageType]", "use storage type, built-in types: " + storageTypeNames.join(", "))
.parse(process.argv);

new Server({
Expand Down
2 changes: 1 addition & 1 deletion src/server/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import Client from "./client";
import clientMiddleware from "./client-middleware";
import allowMiddleware from "./client-allow-middleware";

const jscastDescription = "jscast - A SHOUTcast Server written in JavaScript";
const jscastDescription = "jscast - A SHOUTcast Server/Library written in JavaScript";
const jscastUrl = "https://github.com/BigTeri/jscast";

export default class Server extends EventEmitter {
Expand Down

0 comments on commit a311baf

Please sign in to comment.