diff --git a/nixosModules/ags-v2/config/services/gpu-screen-recorder.ts b/nixosModules/ags-v2/config/services/gpu-screen-recorder.ts new file mode 100644 index 00000000..8509b07a --- /dev/null +++ b/nixosModules/ags-v2/config/services/gpu-screen-recorder.ts @@ -0,0 +1,158 @@ +import { execAsync, subprocess } from 'astal'; +import GObject, { register } from 'astal/gobject'; + +/* Types */ +interface NotifyAction { + id: string + label: string + callback: () => void +} +interface NotifySendProps { + actions?: NotifyAction[] + appName?: string + body?: string + category?: string + hint?: string + iconName: string + replaceId?: number + title: string + urgency?: 'low' | 'normal' | 'critical' +} + + +const APP_NAME = 'gpu-screen-recorder'; +const ICON_NAME = 'nvidia'; + +const escapeShellArg = (arg: string): string => `'${arg.replace(/'/g, '\'\\\'\'')}'`; + +const notifySend = ({ + actions = [], + appName, + body, + category, + hint, + iconName, + replaceId, + title, + urgency = 'normal', +}: NotifySendProps) => new Promise((resolve) => { + let printedId = false; + + const cmd = [ + 'notify-send', + '--print-id', + `--icon=${escapeShellArg(iconName)}`, + escapeShellArg(title), + escapeShellArg(body ?? ''), + // Optional params + appName ? `--app-name=${escapeShellArg(appName)}` : '', + category ? `--category=${escapeShellArg(category)}` : '', + hint ? `--hint=${escapeShellArg(hint)}` : '', + replaceId ? `--replace-id=${replaceId.toString()}` : '', + `--urgency=${urgency}`, + ].concat( + actions.map(({ id, label }) => `--action=${escapeShellArg(id)}=${escapeShellArg(label)}`), + ).join(' '); + + subprocess( + cmd, + (out) => { + if (!printedId) { + resolve(parseInt(out)); + printedId = true; + } + else { + actions.find((action) => action.id === out)?.callback(); + } + }, + (err) => { + console.error(`[Notify] ${err}`); + }, + ); +}); + +@register() +class GSR extends GObject.Object { + private _lastNotifID: number | undefined; + + constructor() { + super(); + + subprocess( + ['gsr-start'], + (path) => { + if (!this._lastNotifID) { + console.error('[GSR] ID of warning notif not found'); + + setTimeout(() => { + this._onSaved(path); + }, 1000); + } + else { + this._onSaved(path); + } + }, + () => { /**/ }, + ); + } + + public saveReplay() { + execAsync(['gpu-save-replay']) + .then(async() => { + this._lastNotifID = await notifySend({ + appName: APP_NAME, + iconName: ICON_NAME, + title: 'Saving Replay', + body: 'Last 20 minutes', + }); + }) + .catch((err) => { + console.error(`[GSR save-replay] ${err}`); + }); + } + + private _onSaved(path: string) { + notifySend({ + appName: APP_NAME, + iconName: ICON_NAME, + replaceId: this._lastNotifID, + + title: 'Replay Saved', + body: `Saved to ${path}`, + + actions: [ + { + id: 'folder', + label: 'Open Folder', + + callback: () => execAsync([ + 'xdg-open', + path.substring(0, path.lastIndexOf('/')), + ]).catch(print), + }, + + { + id: 'video', + label: 'Open Video', + + callback: () => execAsync(['xdg-open', path]).catch(print), + }, + + { + id: 'kdenlive', + label: 'Edit in kdenlive', + + callback: () => execAsync([ + 'bash', + '-c', + `kdenlive -i ${path}`, + ]).catch(print), + }, + ], + }); + } +} + +const gsrService = new GSR(); + +export default gsrService;