Skip to content

Commit

Permalink
Merge pull request #45 from PickleUpADoodle/feature/linux-support
Browse files Browse the repository at this point in the history
Add Linux support (Fixes #29)
  • Loading branch information
manucabral authored Nov 2, 2024
2 parents b047800 + 049ab5b commit 812edf7
Show file tree
Hide file tree
Showing 17 changed files with 348 additions and 143 deletions.
File renamed without changes.
42 changes: 21 additions & 21 deletions main.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
import json
import platform
import os
import sys
import httpx
from src import App
from src.operating_systems.operating_system import OperatingSystem
from src.operating_systems.windows_operating_system import WindowsOperatingSystem
from src.operating_systems.linux_operating_system import LinuxOperatingSystem
from src.notifiers.notifier import Notifier
from src.notifiers.windows_notifier import WindowsNotifier
from src import Logger
from src import __version__, __title__, __clientid___


def prepare_environment():
try:
raw_settings = json.load(open("settings.json"))
Expand Down Expand Up @@ -54,26 +57,23 @@ def prepare_environment():

if __name__ == "__main__":
try:
if platform.system() != "Windows":
Logger.write(message="Sorry! only supports Windows.", level="ERROR")
exit()
operating_system: str = platform.system()
system: OperatingSystem = None
notifier: Notifier = None
match operating_system:
case "Windows":
system = WindowsOperatingSystem()
notifier = WindowsNotifier()
case "Linux":
system = LinuxOperatingSystem()
case _:
Logger.write(message="Sorry! only supports Windows and Linux.", level="ERROR")
exit()
settings = prepare_environment()
# using conhost to allow the functionality of hidding and showing the console.
if len(sys.argv) == 1 and sys.argv[0] != "main.py":
found = []
for file in os.listdir(os.getcwd()):
if file.endswith(".exe"):
found.append(file)
file = found[0]
# print(f'cmd /k {os.environ["SYSTEMDRIVE"]}\\Windows\\System32\\conhost.exe ' + cmd)
os.system(
f'cmd /c {os.environ["SYSTEMDRIVE"]}\\Windows\\System32\\conhost.exe {os.path.join(os.getcwd(),file)} True'
)
exit()
os.system(
f"cmd /c taskkill /IM WindowsTerminal.exe /IM cmd.exe /F"
) # removed /IM cmd.exe in case that causes problems for windows 10. Windows 11 requires starting a new task and killing windows terminal.
system.hide_console_process()
app = App(
operating_system=system,
notifier=notifier,
client_id=settings["client_id"],
version=__version__,
title=__title__,
Expand All @@ -85,4 +85,4 @@ def prepare_environment():
app.run()
except KeyboardInterrupt:
Logger.write(message="User interrupted.")
app.stop()
app.stop()
10 changes: 6 additions & 4 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
anyio==4.3.0
certifi==2024.7.4
certifi==2024.2.2
exceptiongroup==1.2.1
filelock==3.15.3
h11==0.14.0
httpcore==1.0.5
httpx==0.27.0
idna==3.7
infi==0.0.1
infi.systray==0.1.12
pypiwin32==223
patchelf==0.17.2.1
pypiwin32==223; platform_system == "Windows"
pypresence==4.2.1
pywin32==306
pywin32==306; platform_system == "Windows"
sniffio==1.3.1
typing_extensions==4.11.0
websocket-client==1.6.0
websocket-client==1.6.0
2 changes: 1 addition & 1 deletion settings.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"first_run": true, "client_id": "828083725790609459", "profile_name": "Default", "refresh_rate": 1, "display_time_left": true}
{"first_run": false, "client_id": "828083725790609459", "profile_name": "Default", "refresh_rate": 1, "display_time_left": true}
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"main.py",
copyright="© 2020 - 2023 by " + __author__ + " - All rights reserved",
icon="assets/new_logo.ico",
target_name="Youtube Music Rich Presence.exe",
target_name="Youtube Music Rich Presence",
shortcut_name="Youtube Music RPC",
)
],
Expand Down
73 changes: 21 additions & 52 deletions src/app.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,17 @@
import time
import os
from infi.systray import SysTrayIcon
from .presence import Presence
from .logger import Logger
from .notifiers.notifier import Notifier
from .system_tray.system_tray import SystemTray
from .tab import Tab
from .notification import ToastNotifier
import win32con
import win32gui
import ctypes
from .operating_systems.operating_system import OperatingSystem
from .utils import (
remote_debugging,
run_browser,
get_default_browser,
get_browser_tabs,
find_windows_process,
)

DISCORD_STATUS_LIMIT = 15
toast = ToastNotifier()


class App:
Expand All @@ -35,11 +29,16 @@ class App:
"useTimeLeft",
"showen",
"systray",
"silent"
"silent",
"__operating_system",
"notifier",
)

def __init__(
self,
operating_system: OperatingSystem,
notifier: Notifier,
systray: SystemTray = None,
client_id: str = "",
version: str = None,
title: str = None,
Expand All @@ -61,6 +60,9 @@ def __init__(
self.useTimeLeft = useTimeLeft
self.silent = False
self.__profileName = profileName
self.__operating_system = operating_system
self.notifier = notifier
self.systray = systray

def __handle_exception(self, exc: Exception) -> None:
Logger.write(message=exc, level="ERROR", origin=self)
Expand All @@ -71,7 +73,7 @@ def sync(self) -> None:
status = self.__presence.connect()
if not status:
raise Exception("Can't connect to Discord.")
self.__browser = get_default_browser()
self.__browser = self.__operating_system.get_default_browser()
if not self.__browser:
raise Exception("Can't find default browser in your system.")
if not self.__browser["chromium"]:
Expand All @@ -85,7 +87,7 @@ def sync(self) -> None:
def stop(self) -> None:
if self.connected == True:
self.connected = False
self.systray.shutdown()
self.systray.stop()
self.__presence.close()
Logger.write(message="stopped.", origin=self)

Expand All @@ -108,29 +110,6 @@ def current_playing_tab(self, tabs: list) -> dict:
if tab.pause:
return tab
return None

def hideWindow(self, systray):
if self.showen is True:
self.showen = False
window = ctypes.windll.kernel32.GetConsoleWindow()
win32gui.ShowWindow(window, win32con.SW_HIDE)
elif self.showen is False:
self.showen = True
window = ctypes.windll.kernel32.GetConsoleWindow()
win32gui.ShowWindow(window, win32con.SW_SHOW)

def update(self, systray):
toast = ToastNotifier()
try:
toast.show_toast(
"Coming soon!",
"This feature isn't currently avaiable yet.",
duration = 5,
icon_path = f"{os.path.join(os.getcwd(), 'icon.ico')}",
threaded = True,
)
except TypeError:
pass

def on_quit_callback(self, systray):
if self.connected == True:
Expand All @@ -141,18 +120,12 @@ def on_quit_callback(self, systray):
def run(self) -> None:
last_updated_time: int = 1
try:
menu_options = (("Hide/Show Console", None, self.hideWindow), ("Force Update", None, self.update))
self.systray = SysTrayIcon("./icon.ico", "YT Music RPC", menu_options, on_quit=self.on_quit_callback)
self.systray.start()
if not self.connected:
raise RuntimeError("Not connected.")
browser_process = self.__browser["process"]["win32"]
browser_running = find_windows_process(
browser_process, self.__browser["name"]
)
browser_running = self.__operating_system.is_browser_running()
if not remote_debugging() and browser_running:
Logger.write(
message=f"Detected browser running ({browser_process}) without remote debugging enabled.",
message=f"Detected browser running ({self.__operating_system.get_browser_process_name()}) without remote debugging enabled.",
level="WARNING",
origin=self,
)
Expand All @@ -163,7 +136,7 @@ def run(self) -> None:
level="WARNING",
origin=self,
)
run_browser(self.__browser, self.__profileName)
self.__operating_system.run_browser_with_debugging_server(self.__profileName)
else:
Logger.write(
message="Remote debugging is enabled, connected successfully.",
Expand Down Expand Up @@ -238,15 +211,11 @@ def run(self) -> None:
silent=self.silent
)

if not self.silent:
if not self.silent and self.notifier is not None:
try:
toast.show_toast(
"Now Playing!",
f"{self.last_tab.title} by {self.last_tab.artist}",
duration = 3,
icon_path = f"{os.path.join(os.getcwd(), 'icon.ico')}",
threaded = True,
)
self.notifier.notify("Now Playing!",
f"{self.last_tab.title} by {self.last_tab.artist}"
)
except TypeError:
pass

Expand Down
Empty file added src/notifiers/__init__.py
Empty file.
5 changes: 5 additions & 0 deletions src/notifiers/notifier.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from abc import ABC, abstractmethod
class Notifier(ABC):
@abstractmethod
def notify(self) -> None:
pass
57 changes: 30 additions & 27 deletions src/notification.py → src/notifiers/toast_notifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,33 +17,36 @@
from pkg_resources import resource_filename

# 3rd party modules
from win32api import GetModuleHandle
from win32api import PostQuitMessage
from win32con import CW_USEDEFAULT
from win32con import IDI_APPLICATION
from win32con import IMAGE_ICON
from win32con import LR_DEFAULTSIZE
from win32con import LR_LOADFROMFILE
from win32con import WM_DESTROY
from win32con import WM_USER
from win32con import WS_OVERLAPPED
from win32con import WS_SYSMENU
from win32gui import CreateWindow
from win32gui import DestroyWindow
from win32gui import LoadIcon
from win32gui import LoadImage
from win32gui import NIF_ICON
from win32gui import NIF_INFO
from win32gui import NIF_MESSAGE
from win32gui import NIF_TIP
from win32gui import NIM_ADD
from win32gui import NIM_DELETE
from win32gui import NIM_MODIFY
from win32gui import RegisterClass
from win32gui import UnregisterClass
from win32gui import Shell_NotifyIcon
from win32gui import UpdateWindow
from win32gui import WNDCLASS
try:
from win32api import GetModuleHandle
from win32api import PostQuitMessage
from win32con import CW_USEDEFAULT
from win32con import IDI_APPLICATION
from win32con import IMAGE_ICON
from win32con import LR_DEFAULTSIZE
from win32con import LR_LOADFROMFILE
from win32con import WM_DESTROY
from win32con import WM_USER
from win32con import WS_OVERLAPPED
from win32con import WS_SYSMENU
from win32gui import CreateWindow
from win32gui import DestroyWindow
from win32gui import LoadIcon
from win32gui import LoadImage
from win32gui import NIF_ICON
from win32gui import NIF_INFO
from win32gui import NIF_MESSAGE
from win32gui import NIF_TIP
from win32gui import NIM_ADD
from win32gui import NIM_DELETE
from win32gui import NIM_MODIFY
from win32gui import RegisterClass
from win32gui import UnregisterClass
from win32gui import Shell_NotifyIcon
from win32gui import UpdateWindow
from win32gui import WNDCLASS
except ImportError:
pass

# ############################################################################
# ########### Classes ##############
Expand Down
19 changes: 19 additions & 0 deletions src/notifiers/windows_notifier.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import os
from .toast_notifier import ToastNotifier
from .notifier import Notifier

class WindowsNotifier(Notifier):
def __init__(self):
self.toast = ToastNotifier()

def notify(self, title: str, subtitle: str) -> None:
assets_path = os.path.join(os.getcwd(), 'assets')
notifier_path = os.path.join(assets_path, 'notifier')
icon_path = os.path.join(notifier_path, 'icon.ico')
self.toast.show_toast(
title,
subtitle,
duration = 3,
icon_path = icon_path,#os.path.join(os.getcwd(), 'icon.ico'),
threaded = True,
)
Empty file.
Loading

0 comments on commit 812edf7

Please sign in to comment.