diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1062418 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.idea/ +*.iml diff --git a/player-ctl/index.js b/player-ctl/index.js new file mode 100644 index 0000000..b55b67c --- /dev/null +++ b/player-ctl/index.js @@ -0,0 +1,8 @@ +module.exports = { + commands: { + 'player-ctl-play': require('./player-ctl-play'), + 'player-ctl-pause': require('./player-ctl-pause'), + 'player-ctl-next': require('./player-ctl-next'), + 'player-ctl-previous': require('./player-ctl-previous'), + }, +}; diff --git a/player-ctl/manifest.yml b/player-ctl/manifest.yml new file mode 100644 index 0000000..484e487 --- /dev/null +++ b/player-ctl/manifest.yml @@ -0,0 +1,53 @@ +name: player-ctl +label: Player Controls +version: 1.0.0 +author: Backslash + +commands: + - name: player-ctl-play + label: Play media + isImmediate: true + bgColor: "#2563EB" + color: "#FFFFFF" + icon: play + keywords: + - player + - media + - play + - resume + + - name: player-ctl-pause + label: Pause media + isImmediate: true + bgColor: "#2563EB" + color: "#FFFFFF" + icon: pause + keywords: + - player + - media + - pause + - stop + + - name: player-ctl-next + label: Next media item + isImmediate: true + bgColor: "#2563EB" + color: "#FFFFFF" + icon: fast-forward + keywords: + - player + - media + - next + - skip + + - name: player-ctl-previous + label: Previous media item + isImmediate: true + bgColor: "#2563EB" + color: "#FFFFFF" + icon: rewind + keywords: + - player + - media + - previous + - back diff --git a/player-ctl/player-ctl-next.js b/player-ctl/player-ctl-next.js new file mode 100644 index 0000000..037c3f3 --- /dev/null +++ b/player-ctl/player-ctl-next.js @@ -0,0 +1,16 @@ +const { runPlayerctlCommand } = require("./utils"); + +const run = async (_, context) => { + const { search } = context; + + const didRun = await runPlayerctlCommand(context, { + command: "playerctl next", + errorPrefix: "Failed to skip to the next item", + }); + + if (didRun) { + search?.clear?.(); + } +}; + +module.exports = { run, actions: [] }; diff --git a/player-ctl/player-ctl-pause.js b/player-ctl/player-ctl-pause.js new file mode 100644 index 0000000..1cfe046 --- /dev/null +++ b/player-ctl/player-ctl-pause.js @@ -0,0 +1,16 @@ +const { runPlayerctlCommand } = require("./utils"); + +const run = async (_, context) => { + const { search } = context; + + const didRun = await runPlayerctlCommand(context, { + command: "playerctl pause", + errorPrefix: "Failed to pause media playback", + }); + + if (didRun) { + search?.clear?.(); + } +}; + +module.exports = { run, actions: [] }; diff --git a/player-ctl/player-ctl-play.js b/player-ctl/player-ctl-play.js new file mode 100644 index 0000000..a384496 --- /dev/null +++ b/player-ctl/player-ctl-play.js @@ -0,0 +1,16 @@ +const { runPlayerctlCommand } = require("./utils"); + +const run = async (_, context) => { + const { search } = context; + + const didRun = await runPlayerctlCommand(context, { + command: "playerctl play", + errorPrefix: "Failed to start media playback", + }); + + if (didRun) { + search?.clear?.(); + } +}; + +module.exports = { run, actions: [] }; diff --git a/player-ctl/player-ctl-previous.js b/player-ctl/player-ctl-previous.js new file mode 100644 index 0000000..5d05a31 --- /dev/null +++ b/player-ctl/player-ctl-previous.js @@ -0,0 +1,16 @@ +const { runPlayerctlCommand } = require("./utils"); + +const run = async (_, context) => { + const { search } = context; + + const didRun = await runPlayerctlCommand(context, { + command: "playerctl previous", + errorPrefix: "Failed to go to the previous item", + }); + + if (didRun) { + search?.clear?.(); + } +}; + +module.exports = { run, actions: [] }; diff --git a/player-ctl/utils.js b/player-ctl/utils.js new file mode 100644 index 0000000..a82d4c1 --- /dev/null +++ b/player-ctl/utils.js @@ -0,0 +1,62 @@ +const execCommand = (exec, command) => + new Promise((resolve, reject) => { + exec(command, (error, stdout, stderr) => { + if (error) { + const message = stderr?.trim() || error.message || "Unknown error"; + return reject(new Error(message)); + } + resolve(stdout); + }); + }); + +const ensurePlayerctl = async ({ exec, toast }) => { + try { + await execCommand(exec, "command -v playerctl"); + } catch { + const message = + "playerctl is required to control media. Install it with your package manager."; + + if (toast?.error) { + toast.error("Missing dependency", { description: message }); + } + + throw new Error(message); + } +}; + +const handleNoPlayersFound = (toast) => { + if (toast?.error) { + toast.error("No media players running", { + description: "Start playback in your media player and try again.", + }); + } +}; + +const runPlayerctlCommand = async ( + context, + { command, errorPrefix } +) => { + const { exec, toast } = context; + + await ensurePlayerctl(context); + + try { + await execCommand(exec, command); + return true; + } catch (error) { + const message = error?.message || "Unknown error"; + + if (message.toLowerCase().includes("no players found")) { + handleNoPlayersFound(toast); + return false; + } + + throw new Error(`${errorPrefix}: ${message}`); + } +}; + +module.exports = { + execCommand, + ensurePlayerctl, + runPlayerctlCommand, +};