import path from 'path'; import yaml from 'yaml'; import fs from 'fs-extra'; import { lookpath } from 'lookpath'; import { console } from './log'; import { GuiState } from '../@types/messageHandler'; // new-cfg const workingDir = (process as NodeJS.Process & { pkg?: unknown }).pkg ? path.dirname(process.execPath) : process.env.contentDirectory ? process.env.contentDirectory : path.join(__dirname, '/..'); export { workingDir }; const binCfgFile = path.join(workingDir, 'config', 'bin-path'); const dirCfgFile = path.join(workingDir, 'config', 'dir-path'); const guiCfgFile = path.join(workingDir, 'config', 'gui'); const cliCfgFile = path.join(workingDir, 'config', 'cli-defaults'); const hdPflCfgFile = path.join(workingDir, 'config', 'hd_profile'); const sessCfgFile = { cr: path.join(workingDir, 'config', 'cr_sess'), hd: path.join(workingDir, 'config', 'hd_sess'), ao: path.join(workingDir, 'config', 'ao_sess'), adn: path.join(workingDir, 'config', 'adn_sess') }; const stateFile = path.join(workingDir, 'config', 'guistate'); const tokenFile = { cr: path.join(workingDir, 'config', 'cr_token'), hd: path.join(workingDir, 'config', 'hd_token'), hdNew:path.join(workingDir, 'config', 'hd_new_token'), ao: path.join(workingDir, 'config', 'ao_token'), adn: path.join(workingDir, 'config', 'adn_token') }; export const ensureConfig = () => { if (!fs.existsSync(path.join(workingDir, 'config'))) fs.mkdirSync(path.join(workingDir, 'config')); if (process.env.contentDirectory) [binCfgFile, dirCfgFile, cliCfgFile, guiCfgFile].forEach(a => { if (!fs.existsSync(`${a}.yml`)) fs.copyFileSync(path.join(__dirname, '..', 'config', `${path.basename(a)}.yml`), `${a}.yml`); }); }; const loadYamlCfgFile = >(file: string, isSess?: boolean): T => { if(fs.existsSync(`${file}.user.yml`) && !isSess){ file += '.user'; } file += '.yml'; if(fs.existsSync(file)){ try{ return yaml.parse(fs.readFileSync(file, 'utf8')); } catch(e){ console.error('[ERROR]', e); return {} as T; } } return {} as T; }; export type WriteObjects = { gui: GUIConfig } const writeYamlCfgFile = (file: T, data: WriteObjects[T]) => { const fn = path.join(workingDir, 'config', `${file}.yml`); if (fs.existsSync(fn)) fs.removeSync(fn); fs.writeFileSync(fn, yaml.stringify(data)); }; export type GUIConfig = { port: number, password?: string }; export type ConfigObject = { dir: { content: string, trash: string, fonts: string; config: string; }, bin: { ffmpeg?: string, mkvmerge?: string, ffprobe?: string, mp4decrypt?: string }, cli: { [key: string]: any }, gui: GUIConfig } const loadCfg = () : ConfigObject => { // load cfgs const defaultCfg: ConfigObject = { bin: {}, dir: loadYamlCfgFile<{ content: string, trash: string, fonts: string config: string }>(dirCfgFile), cli: loadYamlCfgFile<{ [key: string]: any }>(cliCfgFile), gui: loadYamlCfgFile(guiCfgFile) }; const defaultDirs = { fonts: '${wdir}/fonts/', content: '${wdir}/videos/', trash: '${wdir}/videos/_trash/', config: '${wdir}/config' }; if (typeof defaultCfg.dir !== 'object' || defaultCfg.dir === null || Array.isArray(defaultCfg.dir)) { defaultCfg.dir = defaultDirs; } const keys = Object.keys(defaultDirs) as (keyof typeof defaultDirs)[]; for (const key of keys) { if (!Object.prototype.hasOwnProperty.call(defaultCfg.dir, key) || typeof defaultCfg.dir[key] !== 'string') { defaultCfg.dir[key] = defaultDirs[key]; } if (!path.isAbsolute(defaultCfg.dir[key])) { defaultCfg.dir[key] = path.join(workingDir, defaultCfg.dir[key].replace(/^\${wdir}/, '')); } } if(!fs.existsSync(defaultCfg.dir.content)){ try{ fs.ensureDirSync(defaultCfg.dir.content); } catch(e){ console.error('Content directory not accessible!'); return defaultCfg; } } if(!fs.existsSync(defaultCfg.dir.trash)){ defaultCfg.dir.trash = defaultCfg.dir.content; } // output return defaultCfg; }; const loadBinCfg = async () => { const binCfg = loadYamlCfgFile(binCfgFile); // binaries const defaultBin = { ffmpeg: 'ffmpeg', mkvmerge: 'mkvmerge', ffprobe: 'ffprobe', mp4decrypt: 'mp4decrypt' }; const keys = Object.keys(defaultBin) as (keyof typeof defaultBin)[]; for(const dir of keys){ if(!Object.prototype.hasOwnProperty.call(binCfg, dir) || typeof binCfg[dir] != 'string'){ binCfg[dir] = defaultBin[dir]; } if ((binCfg[dir] as string).match(/^\${wdir}/)) { binCfg[dir] = (binCfg[dir] as string).replace(/^\${wdir}/, ''); binCfg[dir] = path.join(workingDir, binCfg[dir] as string); } if (!path.isAbsolute(binCfg[dir] as string)){ binCfg[dir] = path.join(workingDir, binCfg[dir] as string); } binCfg[dir] = await lookpath(binCfg[dir] as string); binCfg[dir] = binCfg[dir] ? binCfg[dir] : undefined; if(!binCfg[dir]){ const binFile = await lookpath(path.basename(defaultBin[dir])); binCfg[dir] = binFile ? binFile : binCfg[dir]; } } return binCfg; }; const loadCRSession = () => { let session = loadYamlCfgFile(sessCfgFile.cr, true); if(typeof session !== 'object' || session === null || Array.isArray(session)){ session = {}; } for(const cv of Object.keys(session)){ if(typeof session[cv] !== 'object' || session[cv] === null || Array.isArray(session[cv])){ session[cv] = {}; } } return session; }; const saveCRSession = (data: Record) => { const cfgFolder = path.dirname(sessCfgFile.cr); try{ fs.ensureDirSync(cfgFolder); fs.writeFileSync(`${sessCfgFile.cr}.yml`, yaml.stringify(data)); } catch(e){ console.error('Can\'t save session file to disk!'); } }; const loadCRToken = () => { let token = loadYamlCfgFile(tokenFile.cr, true); if(typeof token !== 'object' || token === null || Array.isArray(token)){ token = {}; } return token; }; const saveCRToken = (data: Record) => { const cfgFolder = path.dirname(tokenFile.cr); try{ fs.ensureDirSync(cfgFolder); fs.writeFileSync(`${tokenFile.cr}.yml`, yaml.stringify(data)); } catch(e){ console.error('Can\'t save token file to disk!'); } }; const loadADNToken = () => { let token = loadYamlCfgFile(tokenFile.adn, true); if(typeof token !== 'object' || token === null || Array.isArray(token)){ token = {}; } return token; }; const saveADNToken = (data: Record) => { const cfgFolder = path.dirname(tokenFile.adn); try{ fs.ensureDirSync(cfgFolder); fs.writeFileSync(`${tokenFile.adn}.yml`, yaml.stringify(data)); } catch(e){ console.error('Can\'t save token file to disk!'); } }; const loadAOToken = () => { let token = loadYamlCfgFile(tokenFile.ao, true); if(typeof token !== 'object' || token === null || Array.isArray(token)){ token = {}; } return token; }; const saveAOToken = (data: Record) => { const cfgFolder = path.dirname(tokenFile.ao); try{ fs.ensureDirSync(cfgFolder); fs.writeFileSync(`${tokenFile.ao}.yml`, yaml.stringify(data)); } catch(e){ console.error('Can\'t save token file to disk!'); } }; const loadHDSession = () => { let session = loadYamlCfgFile(sessCfgFile.hd, true); if(typeof session !== 'object' || session === null || Array.isArray(session)){ session = {}; } for(const cv of Object.keys(session)){ if(typeof session[cv] !== 'object' || session[cv] === null || Array.isArray(session[cv])){ session[cv] = {}; } } return session; }; const saveHDSession = (data: Record) => { const cfgFolder = path.dirname(sessCfgFile.hd); try{ fs.ensureDirSync(cfgFolder); fs.writeFileSync(`${sessCfgFile.hd}.yml`, yaml.stringify(data)); } catch(e){ console.error('Can\'t save session file to disk!'); } }; const loadHDToken = () => { let token = loadYamlCfgFile(tokenFile.hd, true); if(typeof token !== 'object' || token === null || Array.isArray(token)){ token = {}; } return token; }; const saveHDToken = (data: Record) => { const cfgFolder = path.dirname(tokenFile.hd); try{ fs.ensureDirSync(cfgFolder); fs.writeFileSync(`${tokenFile.hd}.yml`, yaml.stringify(data)); } catch(e){ console.error('Can\'t save token file to disk!'); } }; const saveHDProfile = (data: Record) => { const cfgFolder = path.dirname(hdPflCfgFile); try{ fs.ensureDirSync(cfgFolder); fs.writeFileSync(`${hdPflCfgFile}.yml`, yaml.stringify(data)); } catch(e){ console.error('Can\'t save profile file to disk!'); } }; const loadHDProfile = () => { let profile = loadYamlCfgFile(hdPflCfgFile, true); if(typeof profile !== 'object' || profile === null || Array.isArray(profile) || Object.keys(profile).length === 0){ profile = { // base ipAddress : '', xNonce : '', xSignature: '', // personal visitId : '', // profile data profile: { userId : 0, profileId: 0, deviceId : '', }, }; } return profile; }; const loadNewHDToken = () => { let token = loadYamlCfgFile(tokenFile.hdNew, true); if(typeof token !== 'object' || token === null || Array.isArray(token)){ token = {}; } return token; }; const saveNewHDToken = (data: Record) => { const cfgFolder = path.dirname(tokenFile.hdNew); try{ fs.ensureDirSync(cfgFolder); fs.writeFileSync(`${tokenFile.hdNew}.yml`, yaml.stringify(data)); } catch(e){ console.error('Can\'t save token file to disk!'); } }; const cfgDir = path.join(workingDir, 'config'); const getState = (): GuiState => { const fn = `${stateFile}.json`; if (!fs.existsSync(fn)) { return { 'setup': false, 'services': {} }; } try { return JSON.parse(fs.readFileSync(fn).toString()); } catch(e) { console.error('Invalid state file, regenerating'); return { 'setup': false, 'services': {} }; } }; const setState = (state: GuiState) => { const fn = `${stateFile}.json`; try { fs.writeFileSync(fn, JSON.stringify(state, null, 2)); } catch(e) { console.error('Failed to write state file.'); } }; export { loadBinCfg, loadCfg, saveCRSession, loadCRSession, saveCRToken, loadCRToken, saveADNToken, loadADNToken, saveHDSession, loadHDSession, saveHDToken, loadHDToken, saveNewHDToken, loadNewHDToken, saveHDProfile, loadHDProfile, saveAOToken, loadAOToken, getState, setState, writeYamlCfgFile, sessCfgFile, hdPflCfgFile, cfgDir };