Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 55 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "antennas",
"version": "4.1.2",
"version": "4.2.0",
"description": "HDHomeRun emulator for Plex DVR to connect to Tvheadend.",
"main": "index.js",
"repository": "https://github.com/jfarseneau/antennas",
Expand All @@ -11,6 +11,7 @@
},
"dependencies": {
"axios": "^0.24.0",
"axios-digest": "^0.3.0",
"js-yaml": "^3.13.1",
"koa": "^2.5.0",
"koa-logger": "^3.2.0",
Expand Down
4 changes: 4 additions & 0 deletions public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@ <h2>Config Settings</h2>
<td>Tuner Count</td>
<td id="tunerCount"></td>
</tr>
<tr>
<td>Channel Count</td>
<td id="channelCount"></td>
</tr>
</tbody>
</table>
</div>
Expand Down
1 change: 1 addition & 0 deletions public/js/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ fetch('/antennas_config.json').then((result) => {
urlReplace('#tvheadendStreamUrl')(config.tvheadend_parsed_stream_uri);
urlReplace('#antennasUrl')(config.antennas_url);
replace('#tunerCount')(config.tuner_count);
replace('#channelCount')(config.channel_count);
replace('#status')(config.status);
});

25 changes: 16 additions & 9 deletions src/lineup.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,25 @@ const tvheadendApi = require('./tvheadendApi');

module.exports = async (config) => {
const response = await tvheadendApi.get('/api/channel/grid?start=0&limit=999999', config);
const { data } = response;
// TODO: Check if there's a Plex permission problem
const lineup = [];
for (const channel of data.entries) {
if (channel.enabled) {
lineup.push({
GuideNumber: String(channel.number),
GuideName: channel.name,
URL: `${config.tvheadend_stream_url}/stream/channel/${channel.uuid}`,
});

if (response) {
const { data } = response;
// TODO: Check if there's a Plex permission problem

if (data && data.entries) {
for (const channel of data.entries) {
if (channel.enabled) {
lineup.push({
GuideNumber: String(channel.number),
GuideName: channel.name,
URL: `${config.tvheadend_stream_url}/stream/channel/${channel.uuid}`,
});
}
}
}
}


return lineup;
};
20 changes: 19 additions & 1 deletion src/lineup.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ test.serial('calls the API with the right options', async (t) => {
remote_timeshift: false,
services: ['test-service'],
tags: [],
bouquet: ''
bouquet: '',
}],
total: 1,
},
Expand All @@ -61,5 +61,23 @@ test.serial('returns empty when Tvheadend has no channel', async (t) => {

const actual = await lineup({ tvheadend_stream_url: 'https://stream.test' });

t.deepEqual(actual, []);
});

test.serial('returns empty when Tvheadend returns undefined', async (t) => {
const expectedResponse = undefined;
tvheadendApiStub.resolves(expectedResponse);

const actual = await lineup({ tvheadend_stream_url: 'https://stream.test' });

t.deepEqual(actual, []);
});

test.serial('returns empty when Tvheadend returns no data', async (t) => {
const expectedResponse = { data: undefined };
tvheadendApiStub.resolves(expectedResponse);

const actual = await lineup({ tvheadend_stream_url: 'https://stream.test' });

t.deepEqual(actual, []);
});
30 changes: 22 additions & 8 deletions src/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,14 @@ const tvheadendApi = require('./tvheadendApi');
async function getConnectionStatus(config) {
try {
const channels = await tvheadendApi.get('/api/channel/grid?start=0&limit=999999', config);
if (channels.data.total === 0) {
return 'Connected but no channels found from Tvheadend';
}
return 'All systems go';
let status = 'All systems go';
if (channels?.response?.status === 403) { throw new Error('Username and password not accepted by Tvheadend'); }
if (channels?.code === 'ECONNREFUSED') { throw new Error('Unable to connect to Tvheadend'); }
if (channels?.data?.total === 0) { status = 'Connected but no channels found from Tvheadend'; }
return {
status,
channelCount: channels?.data?.total,
};
} catch (err) {
console.log(`
Antennas failed to connect to Tvheadend!
Expand All @@ -21,9 +25,17 @@ async function getConnectionStatus(config) {
Here's a dump of the error:
${err}`);

if (err.response.status === 401) { return 'Failed to authenticate with Tvheadend'; }
if (err.code === 'ECONNABORTED') { return 'Unable to find Tvheadend server, make sure the server is up and the configuration is pointing to the right spot'; }
return 'Unknown error, check the logs for more details';
let status = 'Unknown error, check the logs for more details';

if (err && err.response && err.response.status === 401) { status = 'Failed to authenticate with Tvheadend'; }
if (err && err.code === 'ECONNABORTED') { status = 'Unable to find Tvheadend server, make sure the server is up and the configuration is pointing to the right spot'; }
if (err && err.message === 'Auth params error.' || err?.message === 'Username and password not accepted by Tvheadend' ) { status = 'Access denied to Tvheadend; check the username, password, and access rights'; }
if (err && err.message === 'Unable to connect to Tvheadend') { status = 'Unable to connect to Tvheadend; is it running?'; }

return {
status,
channelCount: 0,
};
}
}

Expand All @@ -32,7 +44,9 @@ module.exports = (config, device) => {

router.get('/antennas_config.json', async (ctx) => {
ctx.type = 'application/json';
config.status = await getConnectionStatus(config);
const { status, channelCount } = await getConnectionStatus(config);
config.status = status;
config.channel_count = channelCount;
ctx.body = config;
});

Expand Down
12 changes: 11 additions & 1 deletion src/tvheadendApi.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const axios = require('axios');
const AxiosDigest = require('axios-digest').default;

async function get(apiPath, config) {
const options = {
Expand All @@ -13,7 +14,16 @@ async function get(apiPath, config) {
};
}

return axios.get(`${config.tvheadend_parsed_uri}${apiPath}`, options);
try {
return await axios.get(`${config.tvheadend_parsed_uri}${apiPath}`, options);
} catch (err) {
if (err && err.response && err.response.status === 401) {
const axiosDigest = new AxiosDigest(config.tvheadend_username, config.tvheadend_password);
return axiosDigest.get(`${config.tvheadend_parsed_uri}${apiPath}`);
}

return err;
}
}

module.exports = { get };