Initial commit - OBS Source Switcher Plugin UI
Complete Next.js application for managing OBS Source Switcher - Stream management with multiple screen layouts - Team management CRUD operations - SQLite database integration - OBS WebSocket API integration - Updated to latest versions (Next.js 15.4.1, React 19.1.0, Tailwind CSS 4.0.0) - Enhanced .gitignore for privacy and development
This commit is contained in:
commit
1d4b1eefba
43 changed files with 9596 additions and 0 deletions
42
lib/constants.ts
Normal file
42
lib/constants.ts
Normal file
|
@ -0,0 +1,42 @@
|
|||
// Base table names
|
||||
export const BASE_TABLE_NAMES = {
|
||||
STREAMS: 'streams',
|
||||
TEAMS: 'teams',
|
||||
} as const;
|
||||
|
||||
// Table configuration interface
|
||||
export interface TableConfig {
|
||||
year: number;
|
||||
season: 'spring' | 'summer' | 'fall' | 'winter';
|
||||
suffix?: string;
|
||||
}
|
||||
|
||||
// Default configuration
|
||||
export const DEFAULT_TABLE_CONFIG: TableConfig = {
|
||||
year: 2025,
|
||||
season: 'spring',
|
||||
suffix: 'adr'
|
||||
};
|
||||
|
||||
/**
|
||||
* Generates a full table name using the provided configuration
|
||||
* @param baseTableName - The base table name (e.g., 'streams' or 'teams')
|
||||
* @param config - Optional configuration object. If not provided, uses DEFAULT_TABLE_CONFIG
|
||||
* @returns The full table name with year, season, and suffix
|
||||
*/
|
||||
export function getTableName(
|
||||
baseTableName: typeof BASE_TABLE_NAMES[keyof typeof BASE_TABLE_NAMES],
|
||||
config: Partial<TableConfig> = {}
|
||||
): string {
|
||||
const finalConfig = {...DEFAULT_TABLE_CONFIG, ...config};
|
||||
const suffix = finalConfig.suffix ? `_${finalConfig.suffix}` : '';
|
||||
|
||||
return `${baseTableName}_${finalConfig.year}_${finalConfig.season}${suffix}`;
|
||||
}
|
||||
|
||||
// Export commonly used full table names with default configuration
|
||||
export const TABLE_NAMES = {
|
||||
STREAMS: getTableName(BASE_TABLE_NAMES.STREAMS),
|
||||
TEAMS: getTableName(BASE_TABLE_NAMES.TEAMS),
|
||||
} as const;
|
||||
|
60
lib/database.ts
Normal file
60
lib/database.ts
Normal file
|
@ -0,0 +1,60 @@
|
|||
import sqlite3 from 'sqlite3';
|
||||
import { open, Database } from 'sqlite';
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
import { TABLE_NAMES } from './constants';
|
||||
|
||||
let db: Database<sqlite3.Database, sqlite3.Statement> | null = null;
|
||||
|
||||
const FILE_DIRECTORY = path.resolve(process.env.FILE_DIRECTORY || './files')
|
||||
|
||||
const ensureDirectoryExists = (dirPath: string) => {
|
||||
if (!fs.existsSync(dirPath)) {
|
||||
fs.mkdirSync(dirPath, { recursive: true });
|
||||
console.log(`Created directory: ${dirPath}`);
|
||||
}
|
||||
};
|
||||
|
||||
const initializeDatabase = async (database: Database<sqlite3.Database, sqlite3.Statement>) => {
|
||||
// Create streams table
|
||||
await database.exec(`
|
||||
CREATE TABLE IF NOT EXISTS ${TABLE_NAMES.STREAMS} (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL,
|
||||
obs_source_name TEXT NOT NULL,
|
||||
url TEXT NOT NULL,
|
||||
team_id INTEGER NOT NULL
|
||||
)
|
||||
`);
|
||||
|
||||
// Create teams table
|
||||
await database.exec(`
|
||||
CREATE TABLE IF NOT EXISTS ${TABLE_NAMES.TEAMS} (
|
||||
team_id INTEGER PRIMARY KEY,
|
||||
team_name TEXT NOT NULL
|
||||
)
|
||||
`);
|
||||
|
||||
console.log('Database tables initialized.');
|
||||
};
|
||||
|
||||
export const getDatabase = async () => {
|
||||
if (!db) {
|
||||
// Ensure the files directory exists
|
||||
ensureDirectoryExists(FILE_DIRECTORY);
|
||||
|
||||
const dbPath = path.join(FILE_DIRECTORY, 'sources.db');
|
||||
|
||||
db = await open({
|
||||
filename: dbPath,
|
||||
driver: sqlite3.Database,
|
||||
});
|
||||
console.log('Database connection established.');
|
||||
|
||||
// Initialize database tables
|
||||
await initializeDatabase(db);
|
||||
}
|
||||
return db;
|
||||
}
|
||||
|
||||
// export default getDatabase
|
125
lib/obsClient.js
Normal file
125
lib/obsClient.js
Normal file
|
@ -0,0 +1,125 @@
|
|||
// const config = require('../config');
|
||||
const { OBSWebSocket } = require('obs-websocket-js');
|
||||
|
||||
let obs = null;
|
||||
|
||||
async function connectToOBS() {
|
||||
if (!obs) {
|
||||
obs = new OBSWebSocket();
|
||||
}
|
||||
|
||||
try {
|
||||
const OBS_HOST = process.env.OBS_WEBSOCKET_HOST || '127.0.0.1';
|
||||
const OBS_PORT = process.env.OBS_WEBSOCKET_PORT || '4455';
|
||||
const OBS_PASSWORD = process.env.OBS_WEBSOCKET_PASSWORD || '';
|
||||
|
||||
console.log('Connecting to OBS WebSocket...');
|
||||
console.log('Host:', OBS_HOST);
|
||||
console.log('Port:', OBS_PORT);
|
||||
console.log('Password:', OBS_PASSWORD ? '***' : '(none)');
|
||||
|
||||
await obs.connect(`ws://${OBS_HOST}:${OBS_PORT}`, OBS_PASSWORD);
|
||||
console.log('Connected to OBS WebSocket.');
|
||||
} catch (err) {
|
||||
console.error('Failed to connect to OBS WebSocket:', err.message);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
function getOBSClient() {
|
||||
if (!obs) {
|
||||
throw new Error('OBS WebSocket client is not initialized. Call connectToOBS() first.');
|
||||
}
|
||||
// console.log('client', obs)
|
||||
return obs;
|
||||
}
|
||||
|
||||
async function disconnectFromOBS() {
|
||||
if (obs) {
|
||||
await obs.disconnect();
|
||||
console.log('Disconnected from OBS WebSocket.');
|
||||
obs = null;
|
||||
}
|
||||
}
|
||||
|
||||
async function addSourceToSwitcher(inputName, newSources) {
|
||||
if (!obs) {
|
||||
obs = new OBSWebSocket();
|
||||
}
|
||||
|
||||
try {
|
||||
const OBS_HOST = process.env.OBS_WEBSOCKET_HOST || '127.0.0.1';
|
||||
const OBS_PORT = process.env.OBS_WEBSOCKET_PORT || '4455';
|
||||
const OBS_PASSWORD = process.env.OBS_WEBSOCKET_PASSWORD || '';
|
||||
|
||||
await obs.connect(`ws://${OBS_HOST}:${OBS_PORT}`, OBS_PASSWORD);
|
||||
|
||||
// Step 1: Get current input settings
|
||||
const { inputSettings } = await obs.call('GetInputSettings', { inputName });
|
||||
// console.log('Current Settings:', inputSettings);
|
||||
|
||||
// Step 2: Add new sources to the sources array
|
||||
const updatedSources = [...inputSettings.sources, ...newSources];
|
||||
|
||||
// Step 3: Update the settings with the new sources array
|
||||
await obs.call('SetInputSettings', {
|
||||
inputName,
|
||||
inputSettings: {
|
||||
...inputSettings,
|
||||
sources: updatedSources,
|
||||
},
|
||||
});
|
||||
|
||||
console.log('Updated settings successfully for', inputName);
|
||||
obs.disconnect();
|
||||
} catch (error) {
|
||||
console.error('Error updating settings:', error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// async function addSourceToGroup(obs, teamName, obs_source_name, url) {
|
||||
// try {
|
||||
// // Step 1: Check if the group exists
|
||||
// const { scenes } = await obs.call('GetSceneList');
|
||||
// const groupExists = scenes.some((scene) => scene.sceneName === teamName);
|
||||
|
||||
// // Step 2: Create the group if it doesn't exist
|
||||
// if (!groupExists) {
|
||||
// console.log(`Group "${teamName}" does not exist. Creating it.`);
|
||||
// await obs.call('CreateScene', { sceneName: teamName });
|
||||
// } else {
|
||||
// console.log(`Group "${teamName}" already exists.`);
|
||||
// }
|
||||
|
||||
// // Step 3: Add the source to the group
|
||||
// console.log(`Adding source "${obs_source_name}" to group "${teamName}".`);
|
||||
// await obs.call('CreateInput', {
|
||||
// sceneName: teamName,
|
||||
// inputName: obs_source_name,
|
||||
// inputKind: 'browser_source',
|
||||
// inputSettings: {
|
||||
// width: 1600,
|
||||
// height: 900,
|
||||
// url,
|
||||
// control_audio: true,
|
||||
// },
|
||||
// });
|
||||
|
||||
// // Step 4: Enable "Control audio via OBS"
|
||||
// await obs.call('SetInputSettings', {
|
||||
// inputName: obs_source_name,
|
||||
// inputSettings: {
|
||||
// control_audio: true, // Enable audio control
|
||||
// },
|
||||
// overlay: true, // Keep existing settings and apply changes
|
||||
// });
|
||||
|
||||
// console.log(`Source "${obs_source_name}" successfully added to group "${teamName}".`);
|
||||
// } catch (error) {
|
||||
// console.error('Error adding source to group:', error.message);
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
// Export all functions
|
||||
module.exports = { connectToOBS, getOBSClient, disconnectFromOBS, addSourceToSwitcher};
|
Loading…
Add table
Add a link
Reference in a new issue