Skip to content

Instantly share code, notes, and snippets.

@alexose
Last active December 21, 2021 17:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save alexose/de57aaa129de7d9472cd676f0f20f00c to your computer and use it in GitHub Desktop.
Save alexose/de57aaa129de7d9472cd676f0f20f00c to your computer and use it in GitHub Desktop.
Plex livetv cli tuner
const PlexAPI = require("plex-api");
const spawn = require("child_process").spawn;
const http = require("http");
const identifier = "cli12345";
const session = "123456789";
const dvr = 7; // Find by querying '/livetv/dvrs' and looking for tv.plex.providers.epg.cloud:<number>
const channel = process.argv[2];
if (!channel) {
console.log("Usage: node index.js <channel>");
process.exit(0);
}
// You can create a separate config.js file, or simply overwrite the correct values below.
const config = require("./config.js");
const client = new PlexAPI({
username: config.username,
password: config.password,
hostname: config.hostname,
options: {identifier},
});
// Allow access to X-Plex-Token
let token;
client.authenticator.on("token", _token => (token = _token));
console.log("Tuning...");
client.postQuery(`/livetv/dvrs/${dvr}/channels/${channel}/tune`).then(
async function (result) {
const uuid = result?.MediaContainer?.MediaSubscription?.[0]?.MediaGrabOperation?.[0]?.Video?.Media?.[0]?.uuid;
if (!uuid) {
console.error(`Could not tune to channel ${channel}. Are you sure this channel exists?`);
process.exit(1);
}
const path = encodeURIComponent("/livetv/sessions/" + uuid);
const url = `
http://
${config.hostname}:32400
/video/:/transcode/universal/start.mpd
?hasMDE=1
&path=${path}
&mediaIndex=0
&partIndex=0
&protocol=hls
&location=wan
&maxVideoBitrate=2000
&mediaBufferSize=102400
&session=${session}
&X-Plex-Session-Identifier=${identifier}
&X-Plex-Platform=Chrome
&X-Plex-Token=${token}`;
// Open mpv as a child process
console.log("Opening MPV...");
const mpv = spawn("mpv", [url.replace(/\s/g, "")], {stdio: "inherit", detached: true});
mpv.on("exit", () => process.exit(0));
mpv.unref();
process.on("SIGINT", () => mpv.kill()); // Catch ctrl-c
process.on("SIGTERM", () => mpv.kill()); // Catch kill
// Begin keepalive pings
setInterval(() => keepAlive(path), 60 * 1000);
},
err => console.error("Could not connect to server", err)
);
function keepAlive(path) {
const url = `
http://
${config.hostname}:32400
/:/timeline
&key=${path}
&playbackTime=0
&state=playing
&X-Plex-Client-Identifier=${identifier}
&X-Plex-Session-Identifier=${identifier}
&X-Plex-Platform=Chrome
&X-Plex-Token=${token}`;
console.log("Sending keepalive");
http.get(url.replace(/\s/g, "")).on("error", err => console.log("Keepalive error: " + err));
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment