bsharper / atv-desktop-remote

A simple app to allow you to control an Apple TV from your desktop
MIT License
193 stars 11 forks source link

ATV Remote.app fails to open properly. #6

Open EyyItsRon opened 2 years ago

EyyItsRon commented 2 years ago

I'm able to get the app running if I navigate to...

ATV Remote.app > Contents > MacOS 

... and open the 'ATV Remote' Unix Executable File there, but opening from the .app gets me the following:

Screen Shot 2022-02-02 at 6 53 59 PM

It's possible I messed something up by uninstalling the previous version using AppCleaner before grabbing v1.0.0, but nailing down anything specific is a bit beyond me. I'm working on an M1 if that's relevant.

Otherwise, the app works great once it's running. I did have to fiddle a bit to get it working. Running the...

python3 -m pip install -q --user websockets pyatv

... command in terminal did prompt me to run a different Python3 command to update pip. I can't find the output anymore, but after running the command it told me to, the above command runs fine.

I would recommend adding some documentation about this app's reliance on AirPlay. I was banging my head against the wall for a while trying to figure out why I could connect to one Apple TV in my house but not the other. Turns out setting AirPlay discoverability to 'Only People Sharing This Home' doesn't let the Apple TV talk with the app, but 'Anyone on the Same Network' works fine.

Overall, this is a great little app. The functionality is worth keeping a minimized terminal window open for the time being. Thanks for the work you've done to keep this running with the latest TvOS.

bsharper commented 2 years ago

Hi, @EyyItsRon, thanks for the information!

If you go to where the wsserver.py file gets written (~/Library/Application Support/ATV Remote on macOS or %APPDATA%/ATV Remote on Windows), you can create a file called skip_file_write by running something like

touch ~/Library/Application\ Support/ATV\ Remote/skip_file_write

If this file is present, it prevents this program from writing those files every time the program starts.

This means you can edit start_server.sh or start_server.bat to point to a different python3 executable. In fact, python3 -m pip install -q --user websockets pyatv doesn't need to be run every time.

I've worked a ton with python, but never tried to distribute a python script that requires dependencies across platforms. This has been a lot more work than I thought it would be. I tried compiling everything into wasm using pyodide (wouldn't import properly), compiling everything into a single executable (result was massive, 200MB+).

So the current solution isn't great. Ideally we would at least use venv to keep from installing into the user's python directory. Well really, ideally someone would create an updated nodejs library that would work so we could drop the python requirement completely.

You can actually view the output from the script by running the following in the Dev Tools of the program:

var server_runner = electron.remote.getGlobal("server_runner")
server_runner.setShowOutputs(true)

I will probably set that to true by default in the next release so at least people can see what errors are shown when the script runs.

As for the AirPlay requirement, thanks for letting me know that. When I have time to work on this some more I'll make sure document that requirement.

chschommer commented 2 years ago

I have the same issue, but when starting the app via the terminal (either running the binary inside the .app folder, or open the app via the 'open' command) it works fine.

Running start_server.sh works as well, however .. since I need to start it via the terminal, and the app works via terminal, it is not surprising).

I wonder if my terminal has a different enviroment than when just double clicking the app.

Keagel commented 2 years ago

I'm experiencing the same behavior. I suspect that it is because the PATH variable passed to the app is different when the app is started from the Terminal as it inherits your current PATH (which would have been edited by ~/.zshrc). To test my theory I wrapped the ATV Remote app in another app that redirects stdout and stderr:

mkdir -p "/Applications/ATV Remote-1.app/Contents/MacOS"
touch "/Applications/ATV Remote-1.app/Contents/MacOS/ATV Remote-1"
chmod +x "/Applications/ATV Remote-1.app/Contents/MacOS/ATV Remote-1"

In the file "ATV Remote-1" I put this:

#!/bin/bash
open "/Applications/ATV Remote.app" --stdout /tmp/atv-stdout.log --stderr /tmp/atv-stderr.log

Starting the app from Spotlight or Finder shows this:

Writing wsserver.py to /Users/mac-pro/Library/Application Support/ATV Remote/wsserver.py...
Writing start_server.bat to /Users/mac-pro/Library/Application Support/ATV Remote/start_server.bat...
Writing start_server.sh to /Users/mac-pro/Library/Application Support/ATV Remote/start_server.sh...
python exists: Python 3.8.9
server waiting for event
ready to show
isWSRunning

The server simply never starts. So I enabled what you said in Dev Tools:

var server_runner = electron.remote.getGlobal("server_runner")
server_runner.setShowOutputs(true)

And I saw errors like these:

SERVER.stderr:   ERROR: Command errored out with exit status 1:
SERVER.stderr:    command: /Applications/Xcode.app/Contents/Developer/usr/bin/python3 -u -c 'import sys, setuptools, tokenize; sys.argv[0] = '"'"'/private/var/folders/lt/vz_s_3qn0ds445f5_683sp240000gn/T/pip-install-77269w11/miniaudio/setup.py'"'"'; __file__='"'"'/private/var/folders/lt/vz_s_3qn0ds445f5_683sp240000gn/T/pip-install-77269w11/miniaudio/setup.py'"'"';f=getattr(tokenize, '"'"'open'"'"', open)(__file__);code=f.read().replace('"'"'\r\n'"'"', '"'"'\n'"'"');f.close();exec(compile(code, __file__, '"'"'exec'"'"'))' bdist_wheel -d /private/var/folders/lt/vz_s_3qn0ds445f5_683sp240000gn/T/pip-wheel-vhz4i043
SERVER.stderr:        cwd: /private/var/folders/lt/vz_s_3qn0ds445f5_683sp240000gn/T/pip-install-77269w11/miniaudio/
SERVER.stderr:   Complete output (27 lines):
SERVER.stderr:   Traceback (most recent call last):
SERVER.stderr:     File "<string>", line 1, in <module>
SERVER.stderr:     File "/private/var/folders/lt/vz_s_3qn0ds445f5_683sp240000gn/T/pip-install-77269w11/miniaudio/setup.py", line 20, in <module>
SERVER.stderr:       setup(
SERVER.stderr:     File "/Applications/Xcode.app/Contents/Developer/Library/Frameworks/Python3.framework/Versions/3.8/lib/python3.8/site-packages/setuptools/__init__.py", line 164, in setup
SERVER.stderr:       _install_setup_requires(attrs)
SERVER.stderr:     File "/Applications/Xcode.app/Contents/Developer/Library/Frameworks/Python3.framework/Versions/3.8/lib/python3.8/site-packages/setuptools/__init__.py", line 159, in _install_setup_requires
SERVER.stderr:       dist.fetch_build_eggs(dist.setup_requires)
SERVER.stderr:     File "/Applications/Xcode.app/Contents/Developer/Library/Frameworks/Python3.framework/Versions/3.8/lib/python3.8/site-packages/setuptools/dist.py", line 699, in fetch_build_eggs
SERVER.stderr:       resolved_dists = pkg_resources.working_set.resolve(
SERVER.stderr:     File "/Applications/Xcode.app/Contents/Developer/Library/Frameworks/Python3.framework/Versions/3.8/lib/python3.8/site-packages/pkg_resources/__init__.py", line 779, in resolve
SERVER.stderr:       dist = best[req.key] = env.best_match(
SERVER.stderr:     File "/Applications/Xcode.app/Contents/Developer/Library/Frameworks/Python3.framework/Versions/3.8/lib/python3.8/site-packages/pkg_resources/__init__.py", line 1064, in best_match
SERVER.stderr:       return self.obtain(req, installer)
SERVER.stderr:     File "/Applications/Xcode.app/Contents/Developer/Library/Frameworks/Python3.framework/Versions/3.8/lib/python3.8/site-packages/pkg_resources/__init__.py", line 1076, in obtain
SERVER.stderr:       return installer(requirement)
SERVER.stderr:     File "/Applications/Xcode.app/Contents/Developer/Library/Frameworks/Python3.framework/Versions/3.8/lib/python3.8/site-packages/setuptools/dist.py", line 758, in fetch_build_egg
SERVER.stderr:       return fetch_build_egg(self, req)
SERVER.stderr:     File "/Applications/Xcode.app/Contents/Developer/Library/Frameworks/Python3.framework/Versions/3.8/lib/python3.8/site-packages/setuptools/installer.py", line 133, in fetch_build_egg
SERVER.stderr:       wheel.install_as_egg(dist_location)
SERVER.stderr:     File "/Applications/Xcode.app/Contents/Developer/Library/Frameworks/Python3.framework/Versions/3.8/lib/python3.8/site-packages/setuptools/wheel.py", line 99, in install_as_egg
SERVER.stderr:       self._install_as_egg(destination_eggdir, zf)
SERVER.stderr:     File "/Applications/Xcode.app/Contents/Developer/Library/Frameworks/Python3.framework/Versions/3.8/lib/python3.8/site-packages/setuptools/wheel.py", line 107, in _install_as_egg
SERVER.stderr:       self._convert_metadata(zf, destination_eggdir, dist_info, egg_info)
SERVER.stderr:     File "/Applications/Xcode.app/Contents/Developer/Library/Frameworks/Python3.framework/Versions/3.8/lib/python3.8/site-packages/setuptools/wheel.py", line 128, in _convert_metadata
SERVER.stderr:       os.mkdir(destination_eggdir)
SERVER.stderr:   FileExistsError: [Errno 17] File exists: '/private/var/folders/lt/vz_s_3qn0ds445f5_683sp240000gn/T/pip-install-77269w11/miniaudio/.eggs/cffi-1.15.0-py3.8-macosx-10.14-arm64.egg'
SERVER.stderr:   ----------------------------------------
SERVER.stderr:   ERROR: Failed building wheel for miniaudio

Starting the app from the Terminal works fine even with the wrapper because instead of using Xcode's python3, it uses /opt/homebrew/Caskroom/miniforge/base/bin/python3. To get it working within Finder I had to override the PATH variable in my wrapper:

#!/bin/bash
PATH=/opt/homebrew/Caskroom/miniforge/base/bin:$PATH open "/Applications/ATV Remote.app" --stdout /tmp/atv-stdout.log --stderr /tmp/atv-stderr.log

launchctl could be used to override the PATH variable of Finder-launched applications but in the meantime wrapping the application works fine.

bsharper commented 2 years ago

Hi @Keagel thanks for posting this issue. You are on the right track, but the issue is that the server is spawned without inheriting the PATH that the program runs with. Here is a fix that should get it working for you:

Open Terminal and type cd ~/Library/Application\ Support/ATV\ Remote. Then type touch skip_file_write. This creates an empty file that this program checks for at start. When that file exists, it doesn't overwrite the scripts in this directory.

Now edit start_server.sh. All this script needs to do is launch wsserver.py. The problem, as a few people have found, is that when my remote app calls this script, it isn't executing using the same environmental variables.

So underneath #!/bin/bash just add your line PATH=/opt/homebrew/Caskroom/miniforge/base/bin:$PATH and see if that fixes it. Let me know either way, I'm trying to find time to create an updated version and your feedback would be helpful. Thanks!

EDIT: when editing the start_server.sh script, make sure he Python executable name is correct for your version under miniforge.

Keagel commented 2 years ago

Hi @bsharper! Doing the steps you gave me indeed works to run the app without my wrapper. I went one step further and simply added source ~/.zshrc at the top of the file. Until a better solution is found (if any) I suggest you simply add in that script multiple checks for common files (ie. .bashrc, .bash_profile, .zshrc, etc.) and source them if they exist. Should cover most users.

if [ -e "~/.bashrc" ]; then
    source ~/.bashrc
elif [ -e "~/.zshrc" ]; then
    source ~/.zshrc
fi

Or since the app is a graphical one, just source without checking if the files exist and allow the commands to fail.

source ~/.bashrc
source ~/.bash_profile
source ~/.zshrc

Only issue then would be the order of the files but if the PATH variable is appended (as it should be) instead of completely overwritten then it shouldn't cause problems.

tetanus commented 2 years ago

I have the same error as the screenshot that @EyyItsRon posted, except mine doesn't show "Connecting to ATV…"

When I run start_server.sh in Terminal, I get:

Callisto:~ myusername$ /Users/myusername/Library/Application\ Support/ATV\ Remote/start_server.sh 
/Users/myusername/Library/Application Support/ATV Remote/wsserver.py:21: DeprecationWarning: There is no current event loop
  loop = asyncio.get_event_loop()
================================================================================
                       wsserver.py WebSocket - ATV Server                       
================================================================================
server listening on 127.0.0.1:8765
server listening on [::1]:8765

What am I not doing right?

bsharper commented 2 years ago

@tetanus Which version are you using? If you haven't already, try the beta version here: https://github.com/bsharper/atv-desktop-remote/releases/tag/v1.0.0-beta

I'm not sure if that error is actually an error (DeprecationWarning), I think it just means I need to update something in the server code. But it's getting to "server listening" so that generally means it's working properly. This is backed up by this answered Stack Overflow question: https://stackoverflow.com/questions/70303895/python-3-10-asyncio-gather-shows-deprecationwarning-there-is-no-current-event

Try this: with the remote control window open, press Cmd+Option+I (macOS) or F12 (Windows). This should open the Dev Tools. Copy and paste the output of the Console tab and let me know what it says in there. It might be bad pairing data saved in the renderer, you can try typing localStorage.clear(); in the console area and pressing enter to clear that out. Then type window.location.reload() or press Cmd+R or F5 to reload the window.

tetanus commented 2 years ago

I am running the beta version. Here's the console output:

electron/js2c/renderer_init.js:13 (electron) The remote module is deprecated. Use https://github.com/electron/remote instead.
log @ electron/js2c/renderer_init.js:13
web_remote.js:106 [ main ] server waiting for event
web_remote.js:106 [ main ] ready to show
web_remote.js:106 [ main ] isWSRunning
web_remote.js:106 [ main ] Server exited with code 1

Typing localStorage.clear(); results in:

localStorage.clear();
undefined

The app is still blank after reloading the window.

Update:

I launched the UNIX executable like EyyItsRon mentioned, and now the app works.

By the way, I ran into a bug during the pairing process. After entering the pairing code, clicking the submit button looped back into the page with the searching animation and the text field to enter pairing code. A second attempt at the whole pairing process was successful.

bsharper commented 2 years ago

@tetanus

Interesting catch with that pairing bug, I'll see if I can replicate it.

What OS are you using?

tetanus commented 2 years ago

macOS Monterey 12.5 beta 1.

bsharper commented 2 years ago

@tetanus Thanks. You said the app is working now, does that mean working 100% or "I can get it working after messing with it a little bit each time" working?

tetanus commented 2 years ago

The app works only if I launch the Unix executable, which also launches a Terminal window. When launched normally, the app interface is blank with just the Quit button highlighted in a red stroke; there isn't even the "Connecting to ATV…" prompt at the bottom.