Compare commits

...

11 Commits

10 changed files with 784 additions and 216 deletions

1
.gitignore vendored
View File

@@ -4,3 +4,4 @@ out
.DS_Store
*.log*
electron.vite.config.1716830836044.mjs
.eslintrc.cjs

View File

@@ -2,3 +2,4 @@ singleQuote: true
semi: false
printWidth: 100
trailingComma: none
endOfLine: "auto"

603
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -20,7 +20,14 @@
"dependencies": {
"@electron-toolkit/preload": "^3.0.0",
"@electron-toolkit/utils": "^3.0.0",
"serialport": "^12.0.0"
"@node-steam/vdf": "^2.2.0",
"find-steam-app": "^1.0.2",
"get-installed-apps": "^1.1.0",
"get-startapps": "^1.0.4",
"glob": "^10.4.1",
"node-fetch": "^2.6.1",
"serialport": "^12.0.0",
"vuedraggable": "^4.1.0"
},
"devDependencies": {
"@electron-toolkit/eslint-config": "^1.0.1",

42
src/main/Steam.js Normal file
View File

@@ -0,0 +1,42 @@
import * as VDF from '@node-steam/vdf'
import fs from 'fs'
import { glob, globSync } from 'glob'
import { findSteam } from 'find-steam-app'
// const VDF = require('@node-steam/vdf')
// const fs = require('fs')
// const glob = require('glob')
export async function getLibraryFolders(steamPath) {
if (!steamPath) steamPath = await findSteam()
if (!fs.existsSync(steamPath)) throw new Error(`Steam path not found: ${steamPath}`)
if (!fs.existsSync(`${steamPath}/steamapps/libraryfolders.vdf`))
throw new Error(`Steam config.vdf not found: ${steamPath}/steanapps/libraryfolders.vdf`)
const libraryFolders = VDF.parse(
fs.readFileSync(`${steamPath}/steamapps/libraryfolders.vdf`, 'utf8')
)
console.log(typeof libraryFolders)
return Object.values(libraryFolders.libraryfolders)
}
export function getGames(libraryFolder) {
if (!libraryFolder) return null
if (!fs.existsSync(libraryFolder.path)) throw new Error(`Steam path not found: ${libraryFolder}`)
const appManifests = glob.globSync(`${libraryFolder.path}/steamapps/*.acf`)
const gamesCollection = []
appManifests.forEach((appManifest) => {
const appManifestData = VDF.parse(fs.readFileSync(appManifest, 'utf8'))
gamesCollection.push({
baseDirectory: libraryFolder.path.replaceAll('\\\\', '\\'),
appid: appManifestData.AppState.appid,
name: appManifestData.AppState.name,
installdir: appManifestData.AppState.installdir.replaceAll('\\\\', '\\')
})
})
return gamesCollection
}

View File

@@ -3,8 +3,13 @@ import { join } from 'path'
import { electronApp, optimizer, is } from '@electron-toolkit/utils'
import icon from '../../resources/icon.png?asset'
import fs from 'node:fs/promises'
// import {getInstalledApps} from 'get-installed-apps'
import getapps from 'get-startapps'
import * as steam from './Steam.js'
import { SerialPort } from 'serialport'
import * as logTimestamp from 'log-timestamp'
import { globSync } from 'glob'
async function loadConfigurationYAML() {
try {
@@ -21,6 +26,33 @@ async function getSerialPorts() {
return await SerialPort.list()
}
async function getApps() {
// let appCollection = await getInstalledApps()
let appCollection = await getapps()
let filters = [(a) => a.appid.includes('.exe'), (a) => a.appid.includes('steam')]
console.log(` Total apps ${appCollection.length}`)
return appCollection.filter((app) => filters.some((fn) => fn(app)))
}
async function getSteamApps() {
let steamGames = []
let steamLibraries = await steam.getLibraryFolders()
steamLibraries.forEach((libraryFolder) => {
let games = steam.getGames(libraryFolder)
games.forEach((game) => {
const dir = `${game.baseDirectory}\\steamapps\\common\\${game.installdir}\\`
const exeFiles = globSync(`${dir}/**/*.exe`)
exeFiles.forEach((file) => {
steamGames.push({
name: game.name,
app: file.split('\\').pop()
})
})
})
})
return steamGames
}
function createWindow() {
// Create the browser window.
const mainWindow = new BrowserWindow({
@@ -53,10 +85,6 @@ function createWindow() {
}
}
function developmentConsole() {
// SerialPort.list().then((data) => console.log(data))
}
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
@@ -75,10 +103,10 @@ app.whenReady().then(() => {
ipcMain.on('ping', () => console.log('pong'))
ipcMain.handle('loadConfigurationYML', loadConfigurationYAML)
ipcMain.handle('getSerialPorts', getSerialPorts)
ipcMain.handle('getInstalledApps', getApps)
ipcMain.handle('getSteamApps', getSteamApps)
developmentConsole()
createWindow()
app.on('activate', function () {
// On macOS it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.

View File

@@ -1,52 +1,145 @@
<script setup lang="ts">
import Versions from './components/Versions.vue'
import MovieCard from './components/MovieCard.vue'
import COMselect from './components/COMselect.vue'
import { onMounted } from 'vue'
import ProgramSelector from './components/ProgramSelector.vue'
import { getTransitionRawChildren, onMounted } from 'vue'
import { ref } from 'vue'
import * as logTimestamp from 'log-timestamp'
import draggable from 'vuedraggable'
import ProgramGroup from './components/ProgramGroup.vue'
// import MovieCard from './components/MovieCard.vue'
const movies = ref([])
const apiKey = 'a0eb411ca9c81896004dce1d27a7245b'
// const movies = ref([])
// const apiKey = 'a0eb411ca9c81896004dce1d27a7245b'
const configuration = ref('Loading....')
const serialPorts = ref('Loading Serial ports.... ')
const installedApps = ref([])
const steamApps = ref([])
const steamPath = ref('')
//model variables for user provided input
const programCounter = ref([
'python.exe',
'nvm.exe',
'narrator.exe',
['TimeCamp.exe', 'charmap.exe'],
'nvm.exe'
])
const serialPortSelection = ref('select the COM port... ')
const ipcHandle = () => window.electron.ipcRenderer.send('ping')
const getConfigFileContents = async () => {
const returnValue = await window.electron.ipcRenderer.invoke('loadConfigurationYML')
configuration.value = returnValue
}
// const getConfigFileContents = async () => {
// const returnValue = await window.electron.ipcRenderer.invoke('loadConfigurationYML')
// configuration.value = returnValue
// }
const getSerialPorts = async () => {
serialPorts.value = await window.electron.ipcRenderer.invoke('getSerialPorts')
console.log(`the returned serial ports are ${serialPorts.value}`)
}
const getTrendingMovies = (category) => {
return fetch(`https://api.themoviedb.org/3/trending/movie/${category}?api_key=${apiKey}`)
.then((response) => response.json())
.then((data) => {
movies.value = data.results
// const getTrendingMovies = (category) => {
// return fetch(`https://api.themoviedb.org/3/trending/movie/${category}?api_key=${apiKey}`)
// .then((response) => response.json())
// .then((data) => {
// movies.value = data.results
// })
// }
async function getInstalledApps() {
installedApps.value = await window.electron.ipcRenderer.invoke('getInstalledApps')
// console.log(installedApps.value)
installedApps.value.forEach((app) => {
app.appid = app.appid.split('\\').pop()
})
getSteamApps()
}
async function getSteamApps() {
steamApps.value = await window.electron.ipcRenderer.invoke('getSteamApps')
steamApps.value.forEach((game) => {
console.log(`the game is ${Object.entries(game)}`)
})
}
function deleteProgramRow(index) {
programCounter.value.splice(index, 1)
// Vue.delete(programCounter.value, index)
console.log(`Got event to delete row for index : ${index}`)
}
function deleteAppFromCollection(index, childIndex) {
console.log(`The Indexi is ${index} and the child index is ${childIndex}`)
programCounter.value[index].splice(childIndex, 1)
}
function addProgram() {
programCounter.value.push('')
}
function addAppToCollection(index, childIndex) {
programCounter.value[index].push('')
}
function updateCollectionApp(index, childIndex, appName) {
programCounter.value[index][childIndex] = appName
}
onMounted(() => {
getTrendingMovies('day')
// getTrendingMovies('day')
getSerialPorts()
getInstalledApps()
})
</script>
<template>
<!-- <img alt="logo" class="logo" src="./assets/electron.svg" />
<div class="creator">Powered by electron-vite</div>
<img alt="logo" class="logo" src="./assets/electron.svg" />
<COMselect
v-model="serialPortSelection"
:serialports="serialPorts"
:installed-apps="installedApps"
/>
<!-- <div class="row mt-2">
<button type="button" class="col-auto" @click="ipcHandle">Send Ping</button>
<button type="button" class="col-auto" @click="getSerialPorts">Load config</button>
</div>
<div>{{ configuration }}</div> -->
<Versions /> -->
<draggable v-model="programCounter">
<template #item="{ element, index }">
<div v-if="typeof element === 'string'">
<ProgramSelector
:installed-apps="installedApps"
:steam-apps="steamApps"
:selected-app="element"
@update-app="programCounter[index] = $event"
@delete-row="deleteProgramRow(index)"
/>
</div>
<div v-else class="border-top border-bottom py-2 mt-2">
<ProgramGroup
:installed-apps="installedApps"
:program-counter="element"
:steam-apps="steamApps"
@delete-row="deleteProgramRow(index)"
@update-app="(appName, childIndex) => updateCollectionApp(index, childIndex, appName)"
@add-app="(childIndex) => addAppToCollection(index, childIndex)"
@delete-app="(childIndex) => deleteAppFromCollection(index, childIndex)"
/>
</div>
</template>
</draggable>
<COMselect :serialports="serialPorts" />
<button type="button" @click="ipcHandle">Send Ping</button>
<button type="button" @click="getSerialPorts">Load config</button>
<div>{{ configuration }}</div>
<div class="row mt-2">
<button class="col-auto" @click="addProgram">Add</button>
<!-- <button class="col-auto" click="removeProgram">Remove</button>/ -->
</div>
<div class="row">
<div class="col-auto">COM Port</div>
<div class="col-auto">{{ serialPortSelection }}</div>
</div>
<div>{{ programCounter }}</div>
<div>{{ steamPath }}</div>
<!--
<div class="container">
<div class="text-center">
@@ -61,8 +154,10 @@ onMounted(() => {
</div>
<div v-if="movies.length > 0" class="row">
<div v-for="(movie, i) in movies.slice(0, 2)" :key="i" class="col-md-4">
<MovieCard :movie="movie" />
</div>
<draggable v-model="movies">
<template #item="{ element }">
<MovieCard :movie="element" />
</template>
</draggable>
</div> -->
</template>

View File

@@ -1,49 +1,29 @@
<script setup>
import { ref } from 'vue'
const props = defineProps(['serialports'])
const com_port = ref('')
const iterator = ref(1)
function optioniterator() { return iterator.value++};
function addNewProgram() {
const programsContent = document.getElementById('programs')
const programNode = document.createElement('div')
programNode.innerHTML = `<div class='row'><select class='form-select'> <option> ${optioniterator()} </option> </select></div>`
programsContent.appendChild(programNode.firstChild)
}
</script>
<template>
<p>Select COM port for device: {{ com_port }}</p>
<div class="row">
<div class="col-auto">
<label class="form-label">Select COM Port for your deej device</label>
</div>
<div class="col-auto">
<select v-model="com_port" class="form-select">
<option v-for="(serialport, i) in props.serialports" :key="i" :value="serialport.path">
<option
v-for="(serialport, i) in props.serialports"
:key="i"
:value="serialport.path"
class="form-option"
>
{{ serialport.friendlyName }}
</option>
</select>
</div>
</div>
<div id="programs"></div>
<div>
<button class="form-button" @click="addNewProgram">Add program</button>
</div>
<!-- <table>
<tr>
<td>Select COM Port for device</td>
<td>
<select v-model="com_port">
<option v-for="(serialport, i) in props.serialports" :key="i" :value="serialport.path">
{{ serialport.friendlyName }}
</option>
</select>
</td>
</tr>
</table> --->
</template>
<script setup>
import { ref } from 'vue'
const props = defineProps(['serialports', 'installedApps'])
console.log (`Inside COMSelect the length of installedApps is ${props.installedApps.length}`)
const com_port = defineModel()
</script>

View File

@@ -0,0 +1,56 @@
<script setup>
import ProgramSelector from './ProgramSelector.vue'
import draggable from 'vuedraggable'
import { ref } from 'vue'
const props = defineProps({
installedApps: {
type: Array,
required: true
},
programCounter: {
type: Array,
required: false
},
steamApps: {
type: Array,
required: false
}
})
const emit = defineEmits(['update-app', 'delete-row', 'add-app', 'delete-app'])
// function deleteProgramRow(index) {
// programCounter.value.splice(index, 1)
// // Vue.delete(programCounter.value, index)
// console.log(`Got event to delete row for index : ${index}`)
// }
// function addProgramRow() {
// programCounter.value.push('')
// }
</script>
<template>
<div>{{ programCounter }}</div>
<div class="row">
<div class="col">Select programs</div>
<div class="col text-end">
<i class="fas fa-plus p-2" @click="$emit('add-app', index)"></i>
<i class="fas fa-trash-alt" @click="$emit('delete-row')"></i>
</div>
</div>
<draggable :list="programCounter">
<template #item="{ element, index }">
<div>
<ProgramSelector
:installed-apps="installedApps"
:selected-app="element"
:steam-apps="steamApps"
@delete-row="$emit('delete-app', index)"
@update-app="$emit('update-app', $event, index)"
/>
</div>
</template>
</draggable>
</template>

View File

@@ -0,0 +1,47 @@
<script setup>
defineEmits(['delete-row', 'update-app'])
const props = defineProps({ installedApps: Object, selectedApp: String, steamApps: Object })
console.log(props.installedApps)
</script>
<template>
<div class="row mt-2 d-flex align-items-center">
<div class="col-auto"><i class="fas fa-bars"></i></div>
<div class="col-auto">Program</div>
<div class="col-auto">
<select class="form-select" @change="$emit('update-app', $event.target.value)">
<optgroup label="Deej defaults">
<option value="master">Master</option>
<option value="deej.unmapped">deej.unmapped</option>
<option value="mic">Mic</option>
<option value="system">System</option>
</optgroup>
<optgroup label="Steam Games">
<option
v-for="(app, i) in props.steamApps"
:key="i"
class="col-auto mt-3"
:value="app.app"
:selected="app.app == selectedApp"
>
<b>{{ app.name }}:</b> {{ app.app }}
</option>
</optgroup>
<optgroup label="Installed apps">
<option
v-for="(app, i) in props.installedApps"
:key="i"
class="col-auto mt-3"
:value="app.appid"
:selected="app.appid == selectedApp"
>
{{ app.name }}
</option>
</optgroup>
</select>
</div>
<div class="col-auto"><i class="fas fa-trash-alt" @click="$emit('delete-row')"></i></div>
</div>
<!-- <div>{{ selectedApp }}</div> -->
</template>