Skip to content
Open
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
18 changes: 18 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
name: Build binaries
on:
push:
tags:
- "v*.*.*"
workflow_dispatch:
jobs:
pyinstaller-build:
runs-on: windows-latest
steps:
- name: Create Executable
uses: sayyid5416/pyinstaller@v1
with:
python_ver: '3.11'
spec: 'LPHK.py'
requirements: 'INSTALL/requirements.txt'
upload_exe_with_name: 'My executable'
options: --onefile, --name "LPHK", --windowed, ---icon resources\LPHK.ico, --add-data Version:., --add-data resources\:resources\
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ MANIFEST
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# *.spec
Output/
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
Expand Down
1 change: 1 addition & 0 deletions INSTALL/environment-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@ dependencies:
- https://github.com/pyinstaller/pyinstaller/archive/develop.zip
- py-getch
- pyautogui
- python-vlc
1 change: 1 addition & 0 deletions INSTALL/environment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ dependencies:
- tkcolorpicker
- py-getch
- pyautogui
- python-vlc
8 changes: 5 additions & 3 deletions INSTALL/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
MouseInfo==0.1.3
Pillow==9.3.0
Pillow
py-getch==1.0.1
PyAutoGUI==0.9.50
pygame==2.1.2
pygame
platformdirs
PyGetWindow==0.0.8
PyMsgBox==1.0.7
pynput==1.6.8
Expand All @@ -12,6 +13,7 @@ PyScreeze==0.1.26
python-xlib==0.27
python3-xlib==0.15
PyTweening==1.0.3
six==1.14.0
six
tkcolorpicker==2.1.3
python-vlc
-e git+https://github.com/FMMT666/launchpad.py.git@master#egg=launchpad-py
56 changes: 56 additions & 0 deletions LPHK.iss
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
; Script generated by the Inno Setup Script Wizard.
; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!

#define MyAppName "LPHK"
#define MyAppVersion "1.0.1"
#define MyAppPublisher "Clafter"
#define MyAppURL "https://github.com/clafter/LPHK"
#define MyAppExeName "LPHK.exe"

[Setup]
; NOTE: The value of AppId uniquely identifies this application. Do not use the same AppId value in installers for other applications.
; (To generate a new GUID, click Tools | Generate GUID inside the IDE.)
AppId={{49A18FB6-F3FE-4265-9BC5-B3F7AD939DF9}
AppName={#MyAppName}
AppVersion={#MyAppVersion}
;AppVerName={#MyAppName} {#MyAppVersion}
AppPublisher={#MyAppPublisher}
AppPublisherURL={#MyAppURL}
AppSupportURL={#MyAppURL}
AppUpdatesURL={#MyAppURL}
DefaultDirName={autopf}\{#MyAppName}
; "ArchitecturesAllowed=x64compatible" specifies that Setup cannot run
; on anything but x64 and Windows 11 on Arm.
ArchitecturesAllowed=x64compatible
; "ArchitecturesInstallIn64BitMode=x64compatible" requests that the
; install be done in "64-bit mode" on x64 or Windows 11 on Arm,
; meaning it should use the native 64-bit Program Files directory and
; the 64-bit view of the registry.
ArchitecturesInstallIn64BitMode=x64compatible
DisableProgramGroupPage=yes
; Uncomment the following line to run in non administrative install mode (install for current user only.)
;PrivilegesRequired=lowest
PrivilegesRequiredOverridesAllowed=dialog
OutputBaseFilename=lphk_setup
SetupIconFile=.\resources\LPHK.ico
Compression=lzma
SolidCompression=yes
WizardStyle=modern

[Languages]
Name: "english"; MessagesFile: "compiler:Default.isl"

[Tasks]
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked

[Files]
Source: ".\dist\{#MyAppExeName}"; DestDir: "{app}"; Flags: ignoreversion
; NOTE: Don't use "Flags: ignoreversion" on any shared system files

[Icons]
Name: "{autoprograms}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"
Name: "{autodesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon

[Run]
Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent

14 changes: 5 additions & 9 deletions LPHK.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,10 @@ def get_first_textfile_line(file_path):
first_line = file_lines[0]
return first_line.strip()

from platformdirs import *

USERPATH_FILE = os.path.join(PATH, "USERPATH")
if os.path.exists(USERPATH_FILE):
IS_PORTABLE = False
USER_PATH = get_first_textfile_line(USERPATH_FILE)
os.makedirs(USER_PATH, exist_ok=True)
else:
IS_PORTABLE = True
USER_PATH = PROG_PATH
IS_PORTABLE = False
USER_PATH = user_data_dir("LPHK", "io.github.clafter", ensure_exists=True)

# Get program version
VERSION = get_first_textfile_line(os.path.join(PATH, "VERSION"))
Expand Down Expand Up @@ -87,7 +82,7 @@ def datetime_str():
sys.exit("[LPHK] Error loading launchpad.py")
print("")

import lp_events, scripts, files, sound, window
import lp_events, scripts, files, sound, sound_vlc, window
from utils import launchpad_connector

lp = launchpad.Launchpad()
Expand All @@ -108,6 +103,7 @@ def init():

files.init(USER_PATH)
sound.init(USER_PATH)
sound_vlc.init(USER_PATH)


def shutdown():
Expand Down
41 changes: 41 additions & 0 deletions LPHK.spec
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# -*- mode: python ; coding: utf-8 -*-


a = Analysis(
['LPHK.py'],
pathex=[],
binaries=[],
datas=[
('VERSION','.'),
('resources', 'resources')
],
hiddenimports=[],
hookspath=[],
hooksconfig={},
runtime_hooks=[],
excludes=[],
noarchive=False,
optimize=0,
)
pyz = PYZ(a.pure)

exe = EXE(
pyz,
a.scripts,
a.binaries,
a.datas,
[],
name='LPHK',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
upx_exclude=[],
runtime_tmpdir=None,
console=True,
disable_windowed_traceback=False,
argv_emulation=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
)
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,19 @@ Commands follow the format: `COMMAND arg1 arg2 ...`. Scripts are just a text fil
* `SOUND_STOP`
* Stops all sounds currently playing.
* If (argument 1) supplied, fading for (argument 1) milliseconds and then stops the sounds.
* `SOUND_VLC`
* Play a sound named (argument 1) inside the `user_sounds/` folder.
* Supports all Files compatible with VLC-Mediaplayer.
* VLC must be installed
* Only one sound volume can be played at a time. Multiple sounds with different volumes will be played at the same volume.
* If (argument 2) supplied, set volume to (argument 2).
* Range is 0 to 100
* If (argument 3) supplied, set the start time to (argument 3) milliseconds.
* If (argument 4) supplied, set the end time to (argument 4) milliseconds.
* If (argument 5) supplied, set fadeout time to (argument 5) milliseconds
* `SOUND_VLC_STOP`
* Stops all sounds currently playing via VLC.
* If (argument 1) supplied, fading for (argument 1) milliseconds and then stops the sounds.
* `WAIT_UNPRESSED`
* Waits until the button the script is bound to is unpressed. (no arguments)
* `WEB`
Expand Down
29 changes: 27 additions & 2 deletions scripts.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import threading, webbrowser, os, subprocess
from time import sleep
from functools import partial
import lp_events, lp_colors, kb, sound, ms
import lp_events, lp_colors, kb, sound, sound_vlc, ms

COLOR_PRIMED = 5 #red
COLOR_FUNC_KEYS_PRIMED = 9 #amber
Expand All @@ -10,7 +10,7 @@

import files

VALID_COMMANDS = ["@ASYNC", "@SIMPLE", "@LOAD_LAYOUT", "STRING", "DELAY", "TAP", "PRESS", "RELEASE", "WEB", "WEB_NEW", "CODE", "SOUND", "SOUND_STOP", "WAIT_UNPRESSED", "M_MOVE", "M_SET", "M_SCROLL", "M_LINE", "M_LINE_MOVE", "M_LINE_SET", "LABEL", "IF_PRESSED_GOTO_LABEL", "IF_UNPRESSED_GOTO_LABEL", "GOTO_LABEL", "REPEAT_LABEL", "IF_PRESSED_REPEAT_LABEL", "IF_UNPRESSED_REPEAT_LABEL", "M_STORE", "M_RECALL", "M_RECALL_LINE", "OPEN", "RELEASE_ALL", "RESET_REPEATS"]
VALID_COMMANDS = ["@ASYNC", "@SIMPLE", "@LOAD_LAYOUT", "STRING", "DELAY", "TAP", "PRESS", "RELEASE", "WEB", "WEB_NEW", "CODE", "SOUND", "SOUND_STOP", "SOUND_VLC", "SOUND_VLC_STOP", "WAIT_UNPRESSED", "M_MOVE", "M_SET", "M_SCROLL", "M_LINE", "M_LINE_MOVE", "M_LINE_SET", "LABEL", "IF_PRESSED_GOTO_LABEL", "IF_UNPRESSED_GOTO_LABEL", "GOTO_LABEL", "REPEAT_LABEL", "IF_PRESSED_REPEAT_LABEL", "IF_UNPRESSED_REPEAT_LABEL", "M_STORE", "M_RECALL", "M_RECALL_LINE", "OPEN", "RELEASE_ALL", "RESET_REPEATS"]
ASYNC_HEADERS = ["@ASYNC", "@SIMPLE"]

threads = [[None for y in range(9)] for x in range(9)]
Expand Down Expand Up @@ -240,6 +240,31 @@ def main_logic(idx):
else:
print("[scripts] " + coords + " Stopping sounds")
sound.stop()
elif split_line[0] == "SOUND_VLC":
if len(split_line) == 6:
print("[scripts] " + coords + " Play sound file " + split_line[1] + " at volume " + str(split_line[2]) + " from " + str(split_line[3]) + " to " + str(split_line[4])+ " with " + str(split_line[5]) + " milliseconds fadeout time")
sound_vlc.play_vlc(split_line[1], int(split_line[2]), float(split_line[3]), float(split_line[4]),float(split_line[5]))
if len(split_line) == 5:
print("[scripts] " + coords + " Play sound file " + split_line[1] + " at volume " + str(split_line[2]) + " from " + str(split_line[3]) + " to " + str(split_line[4]))
sound_vlc.play_vlc(split_line[1], int(split_line[2]), float(split_line[3]), float(split_line[4]))
if len(split_line) == 4:
print("[scripts] " + coords + " Play sound file " + split_line[1] + " at volume " + str(split_line[2]) + " from " + str(split_line[3]))
sound_vlc.play_vlc(split_line[1], int(split_line[2]), float(split_line[3]))
if len(split_line) == 3:
print("[scripts] " + coords + " Play sound file " + split_line[1] + " at volume " + str(split_line[2]))
sound_vlc.play_vlc(split_line[1], int(split_line[2]))
if len(split_line) == 2:
print("[scripts] " + coords + " Play sound file " + split_line[1])
sound_vlc.play_vlc(split_line[1])
elif split_line[0] == "SOUND_VLC_STOP":
if len(split_line) > 1:
delay = split_line[1]
print("[scripts] " + coords +
" Stopping sounds with " + delay + " milliseconds fadeout time")
sound_vlc.fadeout_vlc(int(delay))
else:
print("[scripts] " + coords + " Stopping sounds")
sound_vlc.stop_vlc()
elif split_line[0] == "WAIT_UNPRESSED":
print("[scripts] " + coords + " Wait for script key to be unpressed")
while lp_events.pressed[x][y]:
Expand Down
110 changes: 110 additions & 0 deletions sound_vlc.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import os
import vlc
import time
import threading

SOUND_PATH = "/user_sounds/"
PATH = None

# Create a new instance of the VLC player
instance = vlc.Instance()
player_test = instance.media_player_new()
instances = []
players = []
fading = False


def init(path_in):
global PATH
PATH = path_in


def full_name(filename):
name = PATH + SOUND_PATH + filename
if PATH.find('\\') > 0:
name = name.replace('/', '\\')
return name


def is_valid(filename):
final_name = full_name(filename)
try:
media = instance.media_new(final_name)
player_test.set_media(media)
return player_test.will_play()
except:
return False


def play_vlc(filename, volume=100, start_time=0, end_time=0, fadeout=0):
global fading
fading = False
final_name = full_name(filename)

# Create a new player
player = instance.media_player_new()
players.append(player)

# Play the media
media = instance.media_new(final_name)
player.set_media(media)
# Set volume using
player.audio_set_volume(volume)
vlc.libvlc_audio_set_mute(player, False)
player.play()

if start_time > 0:
print("Setting start time to", start_time)
player.set_time(int(start_time * 1000))
if end_time > 0 and end_time > start_time:
# Non-blocking timer to stop the player after the end time
stop_player_thread_instance = threading.Thread(target=stop_player_thread,
args=(player, end_time - start_time, fadeout))
stop_player_thread_instance.start()


def stop_player_thread(player, delay, fadeout):
time.sleep(delay)
if fadeout > 0:
fadeout_vlc(fadeout, [player])
else:
player.stop()
players.remove(player)


def stop_vlc():
global fading
fading = False
for player in players:
player.stop()
players.clear()


def fadeout_vlc(delay, fadeout_players=None):
global fading

global players
if fadeout_players is None:
fadeout_players = []
for player in players:
fadeout_players.append(player)
fading = True
fadeout_vlc_thread_instance = threading.Thread(target=fadeout_vlc_thread, args=(delay, fadeout_players,))
fadeout_vlc_thread_instance.start()


def fadeout_vlc_thread(delay, fadeout_players):
global fading

delay = delay / 1000
fadeout_start_time = time.time()
current_volumes = [playerI.audio_get_volume() for playerI in fadeout_players]
while time.time() - fadeout_start_time < delay and fading:
for i, player in enumerate(fadeout_players):
volume = int((1 - (time.time() - fadeout_start_time) / delay) * current_volumes[i])
player.audio_set_volume(volume)
time.sleep(0.01)
fading = False
for player in fadeout_players:
player.stop()
fadeout_players.clear()
3 changes: 2 additions & 1 deletion window.py
Original file line number Diff line number Diff line change
Expand Up @@ -724,7 +724,8 @@ def make():
def close():
global root_destroyed, launchpad
app.modified_layout_save_prompt()
app.disconnect_lp()
if lpcon.get_launchpad() is not None:
app.disconnect_lp()

if not root_destroyed:
root.destroy()
Expand Down