cobalamin / obsidian-remarkable

Integrates the reMarkable tablet into an Obsidian workflow by letting users quickly capture and insert their drawings.
325 stars 22 forks source link

Windows #7

Open dermanu opened 2 years ago

dermanu commented 2 years ago

Hei,

I guess it doesn't work under Windows, right? reSnap seem to work fine.

The console gives following error message:

node:internal/errors:464 Uncaught (in promise) Error: spawn UNKNOWN
    at __node_internal_captureLargerStackTrace (node:internal/errors:464:5)
    at __node_internal_errnoException (node:internal/errors:594:12)
    at ChildProcess.spawn (node:internal/child_process:412:11)
    at spawn (node:child_process:707:9)
    at eval (plugin:obsidian-remarkable:5766:33)
    at new Promise (<anonymous>)
    at MyPlugin.eval (plugin:obsidian-remarkable:5765:20)
    at Generator.next (<anonymous>)
    at eval (plugin:obsidian-remarkable:31:71)
    at new Promise (<anonymous>)
    at __awaiter (plugin:obsidian-remarkable:27:12)
    at MyPlugin.runProcess (plugin:obsidian-remarkable:5760:16)
    at MyPlugin.eval (plugin:obsidian-remarkable:5807:24)
    at Generator.next (<anonymous>)
    at eval (plugin:obsidian-remarkable:31:71)
    at new Promise (<anonymous>)
    at __awaiter (plugin:obsidian-remarkable:27:12)
    at MyPlugin.callReSnap (plugin:obsidian-remarkable:5787:16)
    at MyPlugin.eval (plugin:obsidian-remarkable:5828:73)
    at Generator.next (<anonymous>)
    at eval (plugin:obsidian-remarkable:31:71)
    at new Promise (<anonymous>)
    at __awaiter (plugin:obsidian-remarkable:27:12)
    at MyPlugin.tryInsertingDrawing (plugin:obsidian-remarkable:5822:16)
    at Object.callback (plugin:obsidian-remarkable:5728:28)
    at yH (app.js:1:1672614)
    at e.executeCommand (app.js:1:1673930)
    at e.onTrigger (app.js:1:1177377)
    at e.handleKey (app.js:1:982170)
    at t.e.handleKey (app.js:1:982283)
    at t.handleKey (app.js:1:982492)
    at e.onKeyEvent (app.js:1:983408)

All the best, dermanu

cobalamin commented 2 years ago

Hi, it should work in principle from what I know, but I don't have a working Windows-based setup to test at the moment. The error message from node is not very helpful, sadly. The only guess I have at the moment is some kind of "antivirus/antimalware" program running on your machine, blocking the spawning of a child process with some nondescript error code (which shows up as UNKNOWN). Do you have something like that installed and active?

cobalamin commented 2 years ago

...and are the paths to reSnap and (optionally) the postprocessing script configured correctly in your plugin settings?

dermanu commented 2 years ago

Thanks for the fast reply. I was suggesting the same, and played around a bit with it. I have Windows 11 running, so Windows Defender is the antivirus on default. My vault is on a OneDrive (something like: C:\User\me\OneDrive - Work\Vault).

So my hypothesis are/were:

  1. Windows Defender blocks something. But its log doesn't show anything like that.
  2. OneDrive itself blocks it. But it doesn't work on the local hard drive either.
  3. It can handle that the path has a 'space' in it. But then as mentioned in 2. it doesn't work on the local drive.

I checked the path multiple times and tried different locations (OneDrive and locally). I am not using a post-processing atm.

But maybe I have to check the Windows Defender thingy again, next week. Or finally switch to Linux...

cobalamin commented 2 years ago

The path of the Vault should not be of any concern for this issue I think, since the error seems to happen before the child process (reSnap) is even launched. The only path that could be a problem is the one where reSnap lies.

BTW, how are you running reSnap? It's a bash script, so you'd need to run it inside WSL, no?

dermanu commented 2 years ago

Im running reSnap using the shell script in cmd (windows command prompt). And it runs fine on its own.

If I run it in the consol it works fine: "C:\Users\dermanu\OneDrive - Work\ProjectA\ObsidianA\.obsidian\reSnap\reSnap.sh" -o "C:\Users\dermanu\OneDrive - Work\ProjectA\ObsidianA\Remarkable\test.png"

However I have to put the paths in quotation marks, due to the space used in the file path. Don't know how this is handled in the plugin.

cobalamin commented 2 years ago

But you are running that command

C:\Users\dermanu\OneDrive - Work\ProjectA\ObsidianA\.obsidian\reSnap\reSnap.sh" -o "C:\Users\dermanu\OneDrive - Work\ProjectA\ObsidianA\Remarkable\test.png"

within WSL / "Bash on Ubuntu" or something like that – not within the 'normal' Windows console, right? reSnap is a bash script - as far as I'm aware, those have only become possible to run on Windows with Win10, and there also only run within WSL. I need to understand this properly to get any clue about how to make this work.

I don't think the spaces are the problem, as the paths are passed as single strings (including spaces) to the appropriate node.js function.

dermanu commented 2 years ago

No, i running that in the 'normal' Windows console (Windows 11). I haven't had WSL installed on that machine yet. It also runs in the git-bash using the command 'sh'.

cobalamin commented 2 years ago

The git-bash shell is not usable programmatically (to be called from node.js), so that doesn't help us. I still don't fully understand your setup, since I can't find any information on executing bash scripts under Win11 without having set up WSL at some point. Also keep in mind that Windows Terminal is much more powerful than, and not the same as, the oldschool Command Line.

That notwithstanding, my best guess is that you might be able to adapt the code of this plugin locally, changing this line: https://github.com/cobalamin/obsidian-remarkable/blob/master/main.ts#L96 from

const process = spawn(executable_path, args);

to

const process = spawn("bash", [executable_path].concat(args));

or

const process = spawn("sh", [executable_path].concat(args));

or maybe

const process = spawn(executable_path, args, {"shell": true});

I hope one of these works for you. Sorry about the hassle, but I'm not able to reproduce your setup on my machine.

dermanu commented 2 years ago

Thanks! I tried some combinations of the mentioned:

  1. Using const process = spawn("bash", [executable_path].concat(args)); AND quotation marks for reSnap directory:
Uncaught (in promise) Nonzero exitcode.
STDERR: /bin/bash: C:\Users\dermanu\OneDrive - Work\ProjectA\ObsidianA\.obsidian\reSnap\reSnap.sh: No such file or directory

STDOUT: 

Without quotation marks:

Uncaught (in promise) Nonzero exitcode.
STDERR: /bin/bash: C:\Users\dermanu\OneDrive: No such file or directory

STDOUT: 
  1. Using const process = spawn("sh", [executable_path].concat(args)), the same as in 1.
  2. Using const process = spawn("sh", [executable_path].concat(args), {"shell": true}); and adding C:\Program Files\Git\bin to PATH beforehand and using quotation marks for the reSnap path:
    Uncaught (in promise) Nonzero exitcode.
    STDERR: 
    STDOUT: Usage: C:\Users\dermanu\OneDrive - Work\ProjectA\ObsidianA\.obsidian\reSnap\reSnap.sh [-l] [-d] [-n] [-v] [--source <ssh-host>] [--output <output-file>] [-h]
    Examples:
    C:\Users\dermanu\OneDrive - Work\ProjectA\ObsidianA\.obsidian\reSnap\reSnap.sh                    # snapshot in portrait
    C:\Users\dermanu\OneDrive - Work\ProjectA\ObsidianA\.obsidian\reSnap\reSnap.sh -l                 # snapshot in landscape
    C:\Users\dermanu\OneDrive - Work\ProjectA\ObsidianA\.obsidian\reSnap\reSnap.sh -s 192.168.2.104   # snapshot over wifi
    C:\Users\dermanu\OneDrive - Work\ProjectA\ObsidianA\.obsidian\reSnap\reSnap.sh -o snapshot.png    # saves the snapshot in the current directory
    C:\Users\dermanu\OneDrive - Work\ProjectA\ObsidianA\.obsidian\reSnap\reSnap.sh -d                 # force display the file (requires feh)
    C:\Users\dermanu\OneDrive - Work\ProjectA\ObsidianA\.obsidian\reSnap\reSnap.sh -n                 # force don't display the file
    C:\Users\dermanu\OneDrive - Work\ProjectA\ObsidianA\.obsidian\reSnap\reSnap.sh -v                 # displays version
    C:\Users\dermanu\OneDrive - Work\ProjectA\ObsidianA\.obsidian\reSnap\reSnap.sh -h                 # displays help information (this)

    So 3. looks promising. Just args is not forwarded.

cobalamin commented 2 years ago

Hmm, that's curious. Maybe for debugging, to see what's actually passed to sh, try:

const process = spawn("echo", ["sh", executable_path].concat(args), {"shell": true});
dermanu commented 2 years ago

Hard to say now at least the obsidian notice says the drawing is inserted and generates a link, but there is no .png generated nor any other information.

Maybe the path for the png must be in quotation marks? I#ll try.

dermanu commented 2 years ago

I got it running... kinda. Currently a lot of hard coded paths. I had to install git and add the path to ffmpeg and lz4 to the environment paths in windows.

Then I changed following part of the code:

    runProcess(executable_path, args) {
        return __awaiter(this, void 0, void 0, function* () {
            let outputs = {
                'stderr': '',
                'stdout': ''
            };
            return new Promise(function (resolve, reject) {
                delete spawn.platform;
                spawn.platform = 'linux';
                const process = spawn(executable_path.concat(args), {
                    shell: 'C:/Program Files/Git/git-bash.exe',
                    platform: 'linux'});
                process.stdout.on('data', (data) => { outputs.stdout += data; });
                process.stderr.on('data', (data) => { outputs.stderr += data; });
                process.on('close', function (code) {
                    return __awaiter(this, void 0, void 0, function* () {
                        if (code === 0) {
                            resolve(outputs);
                        }
                        else {
                            reject("Nonzero exitcode.\nSTDERR: " + outputs.stderr
                                + "\nSTDOUT: " + outputs.stdout);
                        }
                    });
                });
                process.on('error', function (err) {
                    reject(err);
                });
                process.platform = 'win32';
            });
        });
    }

and here:

    callReSnap(landscape) {
        return __awaiter(this, void 0, void 0, function* () {
            const { reSnapPath, rmAddress } = this.settings;
            require('child_process');
            const adapter = this.app.vault.adapter;
            if (adapter instanceof obsidian.FileSystemAdapter) {
                adapter.getBasePath();
            }
            else {
                // Not on desktop, thus there is no basePath available. Cancel execution.
                new obsidian.Notice('Could not get vault path! Is this running on mobile...?');
                return;
            }
            const now = moment();
            const drawingFileName = `rM drawing ${now.format("YYYY-MM-DD-HH.mm.ss")}.png`;
            const absOutputFolderPath = adapter.getFullRealPath(this.settings.outputPath);
            const drawingFilePath = '"' + path.join(absOutputFolderPath, drawingFileName) + '"';
            var args = ' -o ' + drawingFilePath + ' -s ' + rmAddress + ' -n';
            if (landscape) {
                args = args + ' -l';
            }
            yield this.runProcess(reSnapPath, args);
            return { drawingFilePath, drawingFileName };
        });
    }

Haven't tested it with postprocessing.

dermanu commented 2 years ago

For me this works for post-processing:

    postprocessDrawing(drawingFilePath) {
        return __awaiter(this, void 0, void 0, function* () {
            var { postprocessor } = this.settings;
            postprocessor = 'py "' + postprocessor + '" ';
            if (postprocessor) {
                const args = drawingFilePath;
                yield this.runProcess(postprocessor, args);
            }
            return true;
        });
    }

There is something strange going on with my phython installation in the moment. I think it should normally also work withpostprocessor = '"' + postprocessor + '" '; instead of postprocessor = 'py "' + postprocessor + '" ';.

cobalamin commented 2 years ago

Hi, great to hear that it's working for you now. Unfortunately I'm not sure how to make this work in a general setting, as Windows setups for this type of thing are usually messy in different ways that I can't predict. I'd be more than happy to merge a pull request, if you got this to work in a way that doesn't depend on the particularities of your Windows machine. I'd then also test on my Linux machine again to see if that still works.

One common theme seems to be the need to add quotation marks, which seems odd to me and isn't necessary on my Linux machine for all I know. However I'll run some tests to see if doing so works on Linux as well.

dermanu commented 2 years ago

I can try. However I normally don't work with typescript, so this already was pushing my limits. I can try it on my other windows machine, but I think Git-bash, ffmpeg and lz4 must be installed always by hand to get it running.

For the code it think it'll be the easiest to have separate functions for windows OS and linux OS and using navigator.platform to detect the current OS.