Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

minor changes + bugfix with process termination #96

Merged
merged 5 commits into from
Nov 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 0 additions & 22 deletions .eslintrc.cjs

This file was deleted.

2 changes: 2 additions & 0 deletions .flake8
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[flake8]
extend-ignore = E501
184 changes: 184 additions & 0 deletions decky.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
"""
This module exposes various constants and helpers useful for decky plugins.

* Plugin's settings and configurations should be stored under `DECKY_PLUGIN_SETTINGS_DIR`.
* Plugin's runtime data should be stored under `DECKY_PLUGIN_RUNTIME_DIR`.
* Plugin's persistent log files should be stored under `DECKY_PLUGIN_LOG_DIR`.

Avoid writing outside of `DECKY_HOME`, storing under the suggested paths is strongly recommended.

Some basic migration helpers are available: `migrate_any`, `migrate_settings`, `migrate_runtime`, `migrate_logs`.

A logging facility `logger` is available which writes to the recommended location.
"""

__version__ = '1.0.0'

import logging

from typing import Any

"""
Constants
"""

HOME: str
"""
The home directory of the effective user running the process.
Environment variable: `HOME`.
If `root` was specified in the plugin's flags it will be `/root` otherwise the user whose home decky resides in.
e.g.: `/home/deck`
"""

USER: str
"""
The effective username running the process.
Environment variable: `USER`.
It would be `root` if `root` was specified in the plugin's flags otherwise the user whose home decky resides in.
e.g.: `deck`
"""

DECKY_VERSION: str
"""
The version of the decky loader.
Environment variable: `DECKY_VERSION`.
e.g.: `v2.5.0-pre1`
"""

DECKY_USER: str
"""
The user whose home decky resides in.
Environment variable: `DECKY_USER`.
e.g.: `deck`
"""

DECKY_USER_HOME: str
"""
The home of the user where decky resides in.
Environment variable: `DECKY_USER_HOME`.
e.g.: `/home/deck`
"""

DECKY_HOME: str
"""
The root of the decky folder.
Environment variable: `DECKY_HOME`.
e.g.: `/home/deck/homebrew`
"""

DECKY_PLUGIN_SETTINGS_DIR: str
"""
The recommended path in which to store configuration files (created automatically).
Environment variable: `DECKY_PLUGIN_SETTINGS_DIR`.
e.g.: `/home/deck/homebrew/settings/decky-plugin-template`
"""

DECKY_PLUGIN_RUNTIME_DIR: str
"""
The recommended path in which to store runtime data (created automatically).
Environment variable: `DECKY_PLUGIN_RUNTIME_DIR`.
e.g.: `/home/deck/homebrew/data/decky-plugin-template`
"""

DECKY_PLUGIN_LOG_DIR: str
"""
The recommended path in which to store persistent logs (created automatically).
Environment variable: `DECKY_PLUGIN_LOG_DIR`.
e.g.: `/home/deck/homebrew/logs/decky-plugin-template`
"""

DECKY_PLUGIN_DIR: str
"""
The root of the plugin's directory.
Environment variable: `DECKY_PLUGIN_DIR`.
e.g.: `/home/deck/homebrew/plugins/decky-plugin-template`
"""

DECKY_PLUGIN_NAME: str
"""
The name of the plugin as specified in the 'plugin.json'.
Environment variable: `DECKY_PLUGIN_NAME`.
e.g.: `Example Plugin`
"""

DECKY_PLUGIN_VERSION: str
"""
The version of the plugin as specified in the 'package.json'.
Environment variable: `DECKY_PLUGIN_VERSION`.
e.g.: `0.0.1`
"""

DECKY_PLUGIN_AUTHOR: str
"""
The author of the plugin as specified in the 'plugin.json'.
Environment variable: `DECKY_PLUGIN_AUTHOR`.
e.g.: `John Doe`
"""

DECKY_PLUGIN_LOG: str
"""
The path to the plugin's main logfile.
Environment variable: `DECKY_PLUGIN_LOG`.
e.g.: `/home/deck/homebrew/logs/decky-plugin-template/plugin.log`
"""

"""
Migration helpers
"""


def migrate_any(target_dir: str, *files_or_directories: str) -> dict[str, str]:
"""
Migrate files and directories to a new location and remove old locations.
Specified files will be migrated to `target_dir`.
Specified directories will have their contents recursively migrated to `target_dir`.

Returns the mapping of old -> new location.
"""


def migrate_settings(*files_or_directories: str) -> dict[str, str]:
"""
Migrate files and directories relating to plugin settings to the recommended location and remove old locations.
Specified files will be migrated to `DECKY_PLUGIN_SETTINGS_DIR`.
Specified directories will have their contents recursively migrated to `DECKY_PLUGIN_SETTINGS_DIR`.

Returns the mapping of old -> new location.
"""


def migrate_runtime(*files_or_directories: str) -> dict[str, str]:
"""
Migrate files and directories relating to plugin runtime data to the recommended location and remove old locations
Specified files will be migrated to `DECKY_PLUGIN_RUNTIME_DIR`.
Specified directories will have their contents recursively migrated to `DECKY_PLUGIN_RUNTIME_DIR`.

Returns the mapping of old -> new location.
"""


def migrate_logs(*files_or_directories: str) -> dict[str, str]:
"""
Migrate files and directories relating to plugin logs to the recommended location and remove old locations.
Specified files will be migrated to `DECKY_PLUGIN_LOG_DIR`.
Specified directories will have their contents recursively migrated to `DECKY_PLUGIN_LOG_DIR`.

Returns the mapping of old -> new location.
"""


"""
Logging
"""

logger: logging.Logger
"""The main plugin logger writing to `DECKY_PLUGIN_LOG`."""

"""
Event handling
"""
# TODO better docstring im lazy
async def emit(event: str, *args: Any) -> None:
"""
Send an event to the frontend.
"""
29 changes: 29 additions & 0 deletions eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import pluginJs from '@eslint/js'
import tseslint from 'typescript-eslint'
import pluginReact from 'eslint-plugin-react'
import eslintConfigPrettier from 'eslint-config-prettier'
import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended'

export default [
{
ignores: ['dist/*', 'rollup.config.js', '.rollup.cache/*']
},
{
settings: {
react: {
version: '18.3'
}
}
},
pluginJs.configs.recommended,
...tseslint.configs.recommended,
pluginReact.configs.flat.recommended,
eslintConfigPrettier,
eslintPluginPrettierRecommended,
{
files: ['src/**/*.{js,mjs,cjs,ts,jsx,tsx}'],
rules: {
'react/react-in-jsx-scope': 'off'
}
}
]
18 changes: 10 additions & 8 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,18 @@
import json
import os
import ssl
from asyncio import Lock

import aiohttp
import certifi
import decky
from aiohttp import ClientSession
from settings import SettingsManager

import decky # type: ignore
from settings import SettingsManager # type: ignore


class Plugin:
yt_process: asyncio.subprocess.Process | None = None
# We need this lock to make sure the process output isn't read by two concurrent readers at once.
yt_process_lock = Lock()
yt_process_lock = asyncio.Lock()
music_path = f"{decky.DECKY_PLUGIN_RUNTIME_DIR}/music"
cache_path = f"{decky.DECKY_PLUGIN_RUNTIME_DIR}/cache"
ssl_context = ssl.create_default_context(cafile=certifi.where())
Expand All @@ -27,7 +27,8 @@ async def _main(self):
)

async def _unload(self):
if self.yt_process is not None:
# Add a check to make sure the process is still running before trying to terminate to avoid ProcessLookupError
if self.yt_process is not None and self.yt_process.returncode is None:
self.yt_process.terminate()
# Wait for process to terminate.
async with self.yt_process_lock:
Expand All @@ -45,7 +46,8 @@ async def get_setting(self, key, default):
return self.settings.getSetting(key, default)

async def search_yt(self, term: str):
if self.yt_process is not None:
# Add a check to make sure the process is still running before trying to terminate to avoid ProcessLookupError
if self.yt_process is not None and self.yt_process.returncode is None:
self.yt_process.terminate()
# Wait for process to terminate.
async with self.yt_process_lock:
Expand Down Expand Up @@ -137,7 +139,7 @@ async def download_yt_audio(self, id: str):
await process.communicate()

async def download_url(self, url: str, id: str):
async with ClientSession() as session:
async with aiohttp.ClientSession() as session:
res = await session.get(url, ssl=self.ssl_context)
res.raise_for_status()
with open(f"{self.music_path}/{id}.webm", "wb") as file:
Expand Down
30 changes: 16 additions & 14 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"description": "Play theme songs on your game pages",
"type": "module",
"scripts": {
"build": "shx rm -rf dist && rollup -c",
"build": "rollup -c",
"watch": "rollup -c -w",
"test": "echo \"Error: no test specified\" && exit 1"
},
Expand Down Expand Up @@ -34,24 +34,26 @@
],
"devDependencies": {
"@decky/rollup": "^1.0.1",
"@decky/ui": "^4.7.1",
"@types/react": "16.14.0",
"@types/webpack": "^5.28.1",
"@typescript-eslint/eslint-plugin": "^7.2.0",
"@typescript-eslint/parser": "^7.2.0",
"eslint": "^8.57.0",
"@decky/ui": "^4.8.2",
"@eslint/js": "^9.15.0",
"@types/react": "18.3.3",
"@types/react-dom": "18.3.0",
"@types/webpack": "^5.28.5",
"eslint": "^9.15.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-prettier": "^5.1.3",
"prettier": "^3.2.5",
"rollup": "^4.21.0",
"shx": "^0.3.4",
"tslib": "^2.6.2",
"typescript": "^5.4.2"
"eslint-plugin-prettier": "^5.2.1",
"eslint-plugin-react": "^7.37.2",
"prettier": "^3.4.1",
"rollup": "^4.27.4",
"rollup-plugin-external-globals": "^0.13.0",
"typescript": "^5.7.2",
"typescript-eslint": "^8.16.0"
},
"dependencies": {
"@decky/api": "^1.1.2",
"localforage": "^1.10.0",
"react-icons": "^5.0.1"
"react-icons": "^5.3.0",
"tslib": "^2.8.1"
},
"pnpm": {
"peerDependencyRules": {
Expand Down
Loading