Skip to content

Commit 008d2ef

Browse files
committed
Bump major version
2 parents 5cf1bcc + c0922af commit 008d2ef

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+2867
-929
lines changed

.travis.yml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
language: node_js
2+
node_js:
3+
- node
4+
cache:
5+
directories:
6+
- node_modules
7+
before_install:
8+
- sudo apt-get -qq update
9+
- sudo apt-get install -y build-essential libavahi-compat-libdnssd-dev
10+
before_script:
11+
- npm install -g gulp-cli
12+
script: gulp
13+
script: npm test

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
MultiCast v1.0
1+
MultiCast v2.0
22
=========
33

44
[![npm version](https://badge.fury.io/js/multicast.svg)](https://badge.fury.io/js/multicast)
5+
[![Build Status](https://travis-ci.org/superhawk610/multicast.svg?branch=wip)](https://travis-ci.org/superhawk610/multicast)
56

67
:green_heart: A persistent solution to presenting content across multiple Chromecast devices, inspired by [Greenscreen](http://greenscreen.io/).
78

app/config.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,8 @@ var startConfig = () => {
6464
console.log('')
6565
console.log('Configuration updated! To get started, just run\n' +
6666
'\n' +
67-
' multicast start')
67+
' multicast start'
68+
'\n')
6869
})
6970
rl.close()
7071
} else {

app/lib/channels.js

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
'use strict'
2+
3+
const Channel = require('../models/Channel')
4+
5+
const devices = require('./devices')
6+
7+
let channels = []
8+
9+
const func = {
10+
init: () => func.refresh(),
11+
refresh: () => {
12+
Channel.find()
13+
.sort('name')
14+
.exec((err, _channels) => {
15+
if (err) console.log(err)
16+
channels = _channels
17+
})
18+
},
19+
list: () => channels,
20+
withId: id => channels.find(c => c._id == id),
21+
create: (opts, callback) => {
22+
var c = new Channel(opts)
23+
c.save((err, channel) => {
24+
if (err) console.log(err)
25+
channels.push(c)
26+
callback(channel._id)
27+
})
28+
},
29+
update: (id, opts, callback) => {
30+
Channel.update({ _id: id }, opts, err => {
31+
Object.assign(func.withId(id), opts)
32+
opts._id = id
33+
devices.updateChannel(opts, () => callback(id))
34+
})
35+
},
36+
remove: (id, callback) => {
37+
Channel.remove({ _id: id }, () => {
38+
channels.splice(channels.findIndex(c => c._id == id), 1) // remove from local listing
39+
devices.removeChannel(id, () => callback())
40+
})
41+
}
42+
}
43+
44+
module.exports = func

app/lib/cleanObject.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
module.exports = obj => {
2+
let keys = Object.keys(obj)
3+
keys.forEach(k => {
4+
if (!obj[k]) delete obj[k]
5+
})
6+
return obj
7+
}

app/lib/config.js

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
'use strict'
2+
13
const fs = require('fs')
24
const path = require('path')
35

@@ -11,20 +13,25 @@ const configVariables = [
1113
'mongoPort'
1214
]
1315

14-
try {
16+
const port = 3944
1517

18+
try {
1619
// Attempts to pull entire config from environment variables. If any key is
1720
// not found, the configuration will instead be drawn from the .config file
1821
// which should be placed in the project root.
1922
var envUsed = true
2023
configVariables.forEach(envVar => {
21-
if (process.env[envVar] != undefined) module.exports[envVar] = process.env[envVar]
24+
if (process.env[envVar] != undefined)
25+
module.exports[envVar] = process.env[envVar]
2226
else envUsed = false // If any are empty, the .config file will be used
2327
})
2428

2529
// Skips if the full config is already in the environment variables
26-
if (!envUsed) module.exports = JSON.parse(fs.readFileSync(configPath))
27-
30+
if (!envUsed) {
31+
let opts = JSON.parse(fs.readFileSync(configPath))
32+
opts.port = port
33+
module.exports = opts
34+
}
2835
} catch (e) {
2936
console.log(`No config file found in ${configPath}
3037
Please run

app/lib/connection.js

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
'use strict'
2+
3+
const Client = require('castv2').Client
4+
const config = require('./config')
5+
6+
let func = {
7+
establish: (d, disconnectCallback) => {
8+
let host = d.address
9+
d.connectionFailCount = 0
10+
if (d.status == 'offline' || d.status == 'waiting') {
11+
const client = new Client()
12+
client.connect(host, () => {
13+
// reset number of failed connection attempts
14+
d.connectionFailCount = 0
15+
16+
// create various namespace handlers
17+
d.connection = client.createChannel(
18+
'sender-0',
19+
'receiver-0',
20+
'urn:x-cast:com.google.cast.tp.connection',
21+
'JSON'
22+
)
23+
d.heartbeat = client.createChannel(
24+
'sender-0',
25+
'receiver-0',
26+
'urn:x-cast:com.google.cast.tp.heartbeat',
27+
'JSON'
28+
)
29+
d.receiver = client.createChannel(
30+
'sender-0',
31+
'receiver-0',
32+
'urn:x-cast:com.google.cast.receiver',
33+
'JSON'
34+
)
35+
36+
// establish virtual connection to the receiver
37+
d.connection.send({ type: 'CONNECT' })
38+
39+
// start heartbeating
40+
d.missedHeartbeats = 0
41+
d.pulse = setInterval(() => {
42+
d.missedHeartbeats++
43+
if (d.missedHeartbeats > 6) {
44+
// receiver has been offline for more than 30 seconds
45+
d.status = 'offline' // mark receiver as offline
46+
clearInterval(d.pulse) // stop checking for pulse :(
47+
func.addError(
48+
d,
49+
'Receiver missed too many heartbeats, it is likely offline.'
50+
)
51+
} else d.heartbeat.send({ type: 'PING' })
52+
}, 5 * 1000)
53+
d.heartbeat.on('message', (data, broadcast) => {
54+
if (data.type == 'PONG') {
55+
d.missedHeartbeats = 0
56+
d.status = 'online'
57+
func.clearErrors(d)
58+
}
59+
})
60+
61+
// launch hub app
62+
d.receiver.send({ type: 'LAUNCH', appId: config.appId, requestId: 1 })
63+
64+
// monitor receiver status updates to insure hub is open
65+
d.receiver.on('message', (data, broadcast) => {
66+
if (data.type != 'RECEIVER_STATUS')
67+
func.addError(d, `Message from receiver: ${data.type}`)
68+
if ((data.type = 'RECEIVER_STATUS')) {
69+
// data.status contains relevant information about current app, volume, etc
70+
if (data.status && data.status.applications) {
71+
var apps = data.status.applications
72+
73+
/* Backdrop means that our hub applications has stopped running, so we need to restart it */
74+
if (apps.find(a => a.displayName == 'Backdrop')) {
75+
func.addError(d, 'Receiver closed hub, relaunching...')
76+
d.receiver.send({
77+
type: 'LAUNCH',
78+
appId: config.appId,
79+
requestId: 1
80+
})
81+
}
82+
}
83+
}
84+
})
85+
})
86+
client.on('error', () => {
87+
d.connectionFailCount++
88+
if (d.connectionFailCount > 6) {
89+
// receiver hasn't responded after 60 seconds
90+
clearTimeout(d.connectionFail)
91+
func.addError(
92+
d,
93+
'Receiver is unresponsive, attempting to reconnect...'
94+
)
95+
} else d.connectionFail = setTimeout(disconnectCallback, 10 * 1000)
96+
})
97+
}
98+
},
99+
hasErrors: () => Object.keys(errors).length > 0,
100+
getErrors: () => errors,
101+
addError: (device, message) => {
102+
// you can pass a tag directly instead of a device
103+
let tag = typeof device === 'object' ? device.deviceId : device
104+
if (!errors[tag])
105+
errors[tag] = {
106+
name: typeof device === 'object' ? device.location : device,
107+
messages: []
108+
}
109+
if (errors[tag].messages.indexOf(message) == -1)
110+
errors[tag].messages.push(message)
111+
},
112+
clearErrors: device =>
113+
delete errors[typeof device === 'object' ? device.deviceId : device],
114+
hasStatus: () => status.length > 0,
115+
getStatus: () => status.map(s => s.message),
116+
addStatus: (tag, message) =>
117+
status.push({
118+
tag: tag,
119+
message: message
120+
}),
121+
clearStatus: tag => (status = status.filter(s => s.tag != tag))
122+
},
123+
errors = {},
124+
status = []
125+
126+
module.exports = func

app/lib/dbConnect.js

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,19 @@
1+
'use strict'
2+
13
const mongoose = require('mongoose')
24

35
module.exports = config => {
4-
const userPassword = config.mongoUser ? `${config.mongoUser}:${config.mongoPass}@` : ''
5-
mongoose.connect(
6-
`mongodb://${userPassword}${config.mongoHost}:${config.mongoPort}/multicast?authSource=${config.mongoAuthSource}`, {
7-
useMongoClient: true
8-
}).on('error', () => console.log('Could not connect to Mongo.'))
9-
}
6+
const userPassword = config.mongoUser
7+
? `${config.mongoUser}:${config.mongoPass}@`
8+
: ''
9+
mongoose
10+
.connect(
11+
`mongodb://${userPassword}${config.mongoHost}:${
12+
config.mongoPort
13+
}/multicast?authSource=${config.mongoAuthSource}`,
14+
{
15+
useMongoClient: true
16+
}
17+
)
18+
.on('error', () => console.log('Could not connect to Mongo.'))
19+
}

0 commit comments

Comments
 (0)