Skip to content

Commit

Permalink
remote: add multiplexing feature w/ remote Mirakurun(s).
Browse files Browse the repository at this point in the history
(has been merged Mirakuruns project.)
  • Loading branch information
kanreisa committed Feb 17, 2018
1 parent 47b4c02 commit d92d6b3
Show file tree
Hide file tree
Showing 10 changed files with 330 additions and 11 deletions.
11 changes: 9 additions & 2 deletions api.d.ts
Expand Up @@ -136,6 +136,7 @@ export interface TunerDevice {
pid: number;
users: TunerUser[];
isAvailable: boolean;
isRemote: boolean;
isFree: boolean;
isUsing: boolean;
isFault: boolean;
Expand Down Expand Up @@ -192,10 +193,16 @@ export interface ConfigTunersItem {
name: string;
/** channel type. */
types: ChannelType[];
/** command to get TS. (chardev / dvb) */
/** [chardev][dvb] command to get TS. */
command?: string;
/** dvr adapter device path (dvb) */
/** [dvb] dvr adapter device path */
dvbDevicePath?: string;
/** [remote] specify to use remote Mirakurun host like as `192.168.1.x`. */
remoteMirakurunHost?: string;
/** [remote] specify to use remote Mirakurun port number (default: 40772). */
remoteMirakurunPort?: number;
/** [remote] `true` to use remote decoder. `false` to use local decoder. (if decoder specified) */
remoteMirakurunDecoder?: boolean;
/** CAS processor command if needed. */
decoder?: string;
/** `true` to **disable** this tuner. */
Expand Down
8 changes: 8 additions & 0 deletions api.yml
Expand Up @@ -306,6 +306,8 @@ definitions:
$ref: '#/definitions/TunerUser'
isAvailable:
type: boolean
isRemote:
type: boolean
isFree:
type: boolean
isUsing:
Expand Down Expand Up @@ -426,6 +428,12 @@ definitions:
type: string
dvbDevicePath:
type: string
remoteMirakurunHost:
type: string
remoteMirakurunPort:
type: integer
remoteMirakurunDecoder:
type: boolean
decoder:
type: string
isDisabled:
Expand Down
8 changes: 7 additions & 1 deletion doc/Configuration.md
Expand Up @@ -43,9 +43,15 @@ sudo mirakurun config tuners
- BS
- CS
- SKY
# for chardev/dvb
command: cmd <channel> --arg1 --arg2 ... # string
# below are optional
# for dvb
dvbDevicePath: /dev/dvb/adapter/dvr/path # string
# for multiplexing w/ remote Mirakurun
remoteMirakurunHost: 192.168.x.x # string
remoteMirakurunPort: 40772 # integer
remoteMirakurunDecoder: false # boolean
# below are optional
decoder: cmd # string
isDisabled: false # boolean
```
Expand Down
115 changes: 115 additions & 0 deletions doc/Mirakuruns.md
@@ -0,0 +1,115 @@
# How To Multiplexing Mirakuruns

At first, This project has called "Mirakurun**s**".
The goal is to make able to unify multiple Mirakurun servers and simply.

## Limitations

* At this time, multiple regions isn't supported for one channel type.
You can use something like VPN but keep carefully don't mix different region in one channel type. (especially GR.)

## General Patterns

### a) Extends

* Mirakurun A
* Tuners
* Mirakurun B (Extended)
* Tuners

```
+-------------+
Tuners -> | |
+-------------+ | Mirakurun A | -> (client)
Tuners -> | Mirakurun B | -> | |
+-------------+ +-------------+
```

### b) Unify

* Mirakurun A
* CAS Processor
* No Tuners
* Mirakurun B (192.168.1.102)
* Tuners
* Mirakurun C (192.168.1.103)
* Tuners
* Mirakurun D (192.168.1.104)
* Tuners

```
+-------------+ +-------------+
Tuners -> | Mirakurun B | -> | |
+-------------+ | |
+-------------+ | |
Tuners -> | Mirakurun C | -> | Mirakurun A | -> (client)
+-------------+ | |
+-------------+ | |
Tuners -> | Mirakurun D | -> | |
+-------------+ +-------------+
|
[CAS Processor]
```


#### Example Configuration

##### tuners.yml (Mirakurun A)

```yaml
- name: Mirakurun-B-T1
types:
- GR
remoteMirakurunHost: 192.168.1.102
remoteMirakurunDecoder: false
decoder: CAS-PROCESSOR-CMD

- name: Mirakurun-B-T2
types:
- GR
remoteMirakurunHost: 192.168.1.102
remoteMirakurunDecoder: false
decoder: CAS-PROCESSOR-CMD

- name: Mirakurun-B-S1
types:
- BS
- CS
remoteMirakurunHost: 192.168.1.102
remoteMirakurunDecoder: false
decoder: CAS-PROCESSOR-CMD

- name: Mirakurun-B-S2
types:
- BS
- CS
remoteMirakurunHost: 192.168.1.102
remoteMirakurunDecoder: false
decoder: CAS-PROCESSOR-CMD

- name: Mirakurun-C-T
types:
- GR
remoteMirakurunHost: 192.168.1.103
remoteMirakurunDecoder: false
decoder: CAS-PROCESSOR-CMD

- name: Mirakurun-C-S
types:
- BS
- CS
remoteMirakurunHost: 192.168.1.103
remoteMirakurunDecoder: false
decoder: CAS-PROCESSOR-CMD

- name: Mirakurun-D-SKY
types:
- SKY
remoteMirakurunHost: 192.168.1.104
remoteMirakurunDecoder: false
decoder: CAS-PROCESSOR-CMD
```

##### other configs

Set up as usual.
21 changes: 21 additions & 0 deletions src/Mirakurun/Program.ts
Expand Up @@ -164,6 +164,27 @@ export default class Program {
return items;
}

async findByNetworkIdAndReplace(networkId: number, programs: db.Program[]): Promise<void> {

let count = 0;

for (let i = this._items.length - 1; i >= 0; i--) {
if (this._items[i].data.networkId === networkId) {
this._items.splice(i, 1);
--count;
}
}

for (const program of programs) {
this.add(new ProgramItem(program), true);
++count;
}

log.debug("programs replaced (networkId=%d, count=%d)", networkId, count);

this.save();
}

save(): void {
clearTimeout(this._saveTimerId);
this._saveTimerId = setTimeout(() => this._save(), 3000);
Expand Down
52 changes: 45 additions & 7 deletions src/Mirakurun/Tuner.ts
Expand Up @@ -176,11 +176,15 @@ export default class Tuner {
};

const stream = await this._getStream(setting, user);
return new Promise<void>((resolve) => {
setTimeout(() => stream.emit("close"), time);
stream.once("epgReady", () => stream.emit("close"));
stream.once("close", resolve);
});
if (stream === null) {
return Promise.resolve();
} else {
return new Promise<void>((resolve) => {
setTimeout(() => stream.emit("close"), time);
stream.once("epgReady", () => stream.emit("close"));
stream.once("close", resolve);
});
}
}

async getServices(channel: ChannelItem): Promise<db.Service[]> {
Expand Down Expand Up @@ -255,7 +259,7 @@ export default class Tuner {

tuners.forEach((tuner, i) => {

if (!tuner.name || !tuner.types || !tuner.command) {
if (!tuner.name || !tuner.types || (!tuner.remoteMirakurunHost && !tuner.command)) {
log.error("missing required property in tuner#%s configuration", i);
return;
}
Expand All @@ -271,7 +275,7 @@ export default class Tuner {
return;
}

if (typeof tuner.command !== "string") {
if (!tuner.remoteMirakurunHost && typeof tuner.command !== "string") {
log.error("invalid type of property `command` in tuner#%s configuration", i);
return;
}
Expand All @@ -281,6 +285,21 @@ export default class Tuner {
return;
}

if (tuner.remoteMirakurunHost && typeof tuner.remoteMirakurunHost !== "string") {
log.error("invalid type of property `remoteMirakurunHost` in tuner#%s configuration", i);
return;
}

if (tuner.remoteMirakurunPort && Number.isInteger(tuner.remoteMirakurunPort) === false) {
log.error("invalid type of property `remoteMirakurunPort` in tuner#%s configuration", i);
return;
}

if (tuner.remoteMirakurunDecoder !== undefined && typeof tuner.remoteMirakurunDecoder !== "boolean") {
log.error("invalid type of property `remoteMirakurunDecoder` in tuner#%s configuration", i);
return;
}

if (tuner.isDisabled) {
return;
}
Expand Down Expand Up @@ -316,6 +335,25 @@ export default class Tuner {
}
}

// x. use remote data
if (device === null && setting.noProvide === true) {
const remoteDevice = devices.find(device => device.isRemote);
if (remoteDevice) {
if (setting.networkId !== undefined && setting.parseEIT === true) {
remoteDevice.getRemotePrograms({ networkId: setting.networkId })
.then(async programs => {
await common.sleep(1000);
await _.program.findByNetworkIdAndReplace(setting.networkId, programs);
await common.sleep(1000);
})
.then(() => resolve(null))
.catch(err => reject(err));

return;
}
}
}

// 2. start as new
if (device === null) {
for (let i = 0; i < length; i++) {
Expand Down

0 comments on commit d92d6b3

Please sign in to comment.