mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-16 04:43:35 +00:00
refactor: improve project loading
This commit is contained in:
@@ -31,6 +31,7 @@ import {
|
||||
setLastLoadedProject,
|
||||
} from '../app-state-service/AppStateService.js';
|
||||
import { runtimeService } from '../runtime-service/RuntimeService.js';
|
||||
import { getFirstRundown } from '../rundown-service/rundownUtils.js';
|
||||
|
||||
import {
|
||||
copyCorruptFile,
|
||||
@@ -40,7 +41,6 @@ import {
|
||||
moveCorruptFile,
|
||||
parseJsonFile,
|
||||
} from './projectServiceUtils.js';
|
||||
import { getFirstRundown } from '../rundown-service/rundownUtils.js';
|
||||
|
||||
type ProjectState =
|
||||
| {
|
||||
@@ -70,26 +70,50 @@ function init() {
|
||||
|
||||
export async function getCurrentProject(): Promise<{ filename: string; pathToFile: string }> {
|
||||
if (currentProjectState.status === 'PENDING') {
|
||||
const lastLoadedProject = await initialiseProject();
|
||||
currentProjectState = {
|
||||
status: 'INITIALIZED',
|
||||
currentProjectName: lastLoadedProject,
|
||||
};
|
||||
await initialiseProject();
|
||||
}
|
||||
const pathToFile = getPathToProject(currentProjectState.currentProjectName);
|
||||
// we know the project is loaded since we force initialisation above
|
||||
const pathToFile = getPathToProject(currentProjectState.currentProjectName as string);
|
||||
|
||||
return { filename: currentProjectState.currentProjectName, pathToFile };
|
||||
return { filename: currentProjectState.currentProjectName as string, pathToFile };
|
||||
}
|
||||
|
||||
/**
|
||||
* Private function loads a project file and handles necessary side effects
|
||||
*/
|
||||
async function loadProject(projectData: DatabaseModel, projectName: string) {
|
||||
// we need to make sure the file name is unique in the projects directory
|
||||
const pathToNewFile = generateUniqueFileName(publicDir.projectsDir, projectName);
|
||||
|
||||
// change LowDB to point to new file
|
||||
await initPersistence(getPathToProject(pathToNewFile), projectData);
|
||||
logger.info(LogOrigin.Server, `Loaded project ${projectName}`);
|
||||
|
||||
// stop the runtime service
|
||||
runtimeService.stop();
|
||||
|
||||
// load the first rundown in the project
|
||||
const firstRundown = getFirstRundown(projectData.rundowns);
|
||||
await initRundown(firstRundown, projectData.customFields);
|
||||
|
||||
// persist the project selection
|
||||
const newName = getFileNameFromPath(pathToNewFile);
|
||||
await setLastLoadedProject(newName);
|
||||
|
||||
// update the service state
|
||||
currentProjectState = {
|
||||
status: 'INITIALIZED',
|
||||
currentProjectName: newName,
|
||||
};
|
||||
|
||||
return newName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the demo project
|
||||
*/
|
||||
export async function loadDemoProject(): Promise<string> {
|
||||
const pathToNewFile = generateUniqueFileName(publicDir.projectsDir, config.demoProject);
|
||||
await initPersistence(getPathToProject(pathToNewFile), demoDb);
|
||||
const newName = getFileNameFromPath(pathToNewFile);
|
||||
await setLastLoadedProject(newName);
|
||||
return newName;
|
||||
return loadProject(demoDb, config.demoProject);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -97,11 +121,7 @@ export async function loadDemoProject(): Promise<string> {
|
||||
* to be composed in the loading functions
|
||||
*/
|
||||
async function loadNewProject(): Promise<string> {
|
||||
const pathToNewFile = generateUniqueFileName(publicDir.projectsDir, config.newProject);
|
||||
await initPersistence(getPathToProject(pathToNewFile), dbModel);
|
||||
const newName = getFileNameFromPath(pathToNewFile);
|
||||
await setLastLoadedProject(newName);
|
||||
return newName;
|
||||
return loadProject(dbModel, config.newProject);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -129,46 +149,32 @@ export async function initialiseProject(): Promise<string> {
|
||||
// check what was loaded before
|
||||
const previousProject = await getLastLoadedProject();
|
||||
|
||||
// in normal circumstances we dont have a previous project if it is the first app start
|
||||
// in which case we want to load a demo project
|
||||
if (!previousProject) {
|
||||
return loadDemoProject();
|
||||
}
|
||||
|
||||
// try and load the previous project
|
||||
const filePath = doesProjectExist(previousProject);
|
||||
if (filePath === null) {
|
||||
logger.warning(LogOrigin.Server, `Previous project file ${previousProject} not found`);
|
||||
return loadNewProject();
|
||||
}
|
||||
|
||||
try {
|
||||
const fileData = await parseJsonFile(filePath);
|
||||
const result = parseDatabaseModel(fileData);
|
||||
let parsedFileName = previousProject;
|
||||
let parsedFilePath = filePath;
|
||||
|
||||
if (result.errors.length > 0) {
|
||||
logger.warning(LogOrigin.Server, 'Project loaded with errors');
|
||||
parsedFileName = await handleCorruptedFile(filePath, previousProject);
|
||||
parsedFilePath = getPathToProject(parsedFileName);
|
||||
}
|
||||
|
||||
await initPersistence(parsedFilePath, result.data);
|
||||
await setLastLoadedProject(parsedFileName);
|
||||
return parsedFileName;
|
||||
const projectName = await loadProjectFile(previousProject);
|
||||
return projectName;
|
||||
} catch (error) {
|
||||
// if we are here, most likely the json parsing failed and the file is corrupt
|
||||
logger.warning(LogOrigin.Server, `Unable to load previous project ${previousProject}: ${getErrorMessage(error)}`);
|
||||
await moveCorruptFile(filePath, previousProject).catch((_) => {
|
||||
try {
|
||||
const pathToFile = getPathToProject(previousProject);
|
||||
await moveCorruptFile(pathToFile, previousProject);
|
||||
} catch (_) {
|
||||
/* while we have to catch the error, we dont need to handle it */
|
||||
});
|
||||
|
||||
return loadNewProject();
|
||||
}
|
||||
}
|
||||
|
||||
return loadNewProject();
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a data from a file into the runtime
|
||||
*/
|
||||
export async function loadProjectFile(name: string) {
|
||||
export async function loadProjectFile(name: string): Promise<string> {
|
||||
const filePath = doesProjectExist(name);
|
||||
if (filePath === null) {
|
||||
throw new Error('Project file not found');
|
||||
@@ -178,31 +184,14 @@ export async function loadProjectFile(name: string) {
|
||||
const fileData = await parseJsonFile(filePath);
|
||||
const result = parseDatabaseModel(fileData);
|
||||
let parsedFileName = name;
|
||||
let parsedFilePath = filePath;
|
||||
|
||||
if (result.errors.length > 0) {
|
||||
logger.warning(LogOrigin.Server, 'Project loaded with errors');
|
||||
parsedFileName = await handleCorruptedFile(filePath, name);
|
||||
parsedFilePath = getPathToProject(parsedFileName);
|
||||
}
|
||||
|
||||
// change LowDB to point to new file
|
||||
await initPersistence(parsedFilePath, result.data);
|
||||
logger.info(LogOrigin.Server, `Loaded project ${parsedFileName}`);
|
||||
|
||||
// persist the project selection
|
||||
await setLastLoadedProject(parsedFileName);
|
||||
|
||||
// since load happens at runtime, we need to update the services that depend on the data
|
||||
|
||||
// apply data model
|
||||
runtimeService.stop();
|
||||
|
||||
const { rundowns, customFields } = result.data;
|
||||
|
||||
// apply the rundown
|
||||
const firstRundown = getFirstRundown(rundowns);
|
||||
await initRundown(firstRundown, customFields);
|
||||
const projectName = await loadProject(result.data, parsedFileName);
|
||||
return projectName;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -239,7 +228,7 @@ export async function duplicateProjectFile(originalFile: string, newFilename: st
|
||||
/**
|
||||
* Renames an existing project file
|
||||
*/
|
||||
export async function renameProjectFile(originalFile: string, newFilename: string) {
|
||||
export async function renameProjectFile(originalFile: string, newFilename: string): Promise<string> {
|
||||
const projectFilePath = doesProjectExist(originalFile);
|
||||
if (projectFilePath === null) {
|
||||
throw new Error('Project file not found');
|
||||
@@ -257,50 +246,23 @@ export async function renameProjectFile(originalFile: string, newFilename: strin
|
||||
const isLoaded = await isLastLoadedProject(originalFile);
|
||||
if (isLoaded) {
|
||||
const fileData = await parseJsonFile(pathToRenamed);
|
||||
const result = parseDatabaseModel(fileData);
|
||||
const projectData = parseDatabaseModel(fileData);
|
||||
|
||||
// change LowDB to point to new file
|
||||
await initPersistence(pathToRenamed, result.data);
|
||||
logger.info(LogOrigin.Server, `Loaded project ${newFilename}`);
|
||||
|
||||
// persist the project selection
|
||||
await setLastLoadedProject(newFilename);
|
||||
|
||||
// apply data model
|
||||
runtimeService.stop();
|
||||
|
||||
const { rundowns, customFields } = result.data;
|
||||
|
||||
// apply the rundown
|
||||
const firstRundown = getFirstRundown(rundowns);
|
||||
await initRundown(firstRundown, customFields);
|
||||
const newFileName = await loadProject(projectData.data, newFilename);
|
||||
return newFileName;
|
||||
}
|
||||
return newFilename;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new project file and applies its result
|
||||
*/
|
||||
export async function createProject(filename: string, initialData: Partial<DatabaseModel>) {
|
||||
export async function createProject(filename: string, initialData: Partial<DatabaseModel>): Promise<string> {
|
||||
const data = safeMerge(dbModel, initialData);
|
||||
|
||||
const fileNameWithExtension = ensureJsonExtension(filename);
|
||||
const uniqueFileName = generateUniqueFileName(publicDir.projectsDir, fileNameWithExtension);
|
||||
const newFile = getPathToProject(uniqueFileName);
|
||||
|
||||
// change LowDB to point to new file
|
||||
await initPersistence(newFile, data);
|
||||
|
||||
// apply data to running services
|
||||
// we dont need to parse since we are creating a new file
|
||||
await patchCurrentProject(data);
|
||||
|
||||
// update app state to point to new value
|
||||
setLastLoadedProject(uniqueFileName);
|
||||
|
||||
// update the service state
|
||||
currentProjectState.currentProjectName = uniqueFileName;
|
||||
|
||||
return uniqueFileName;
|
||||
const newFilename = await loadProject(data, fileNameWithExtension);
|
||||
return newFilename;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -338,7 +300,7 @@ export async function patchCurrentProject(data: Partial<DatabaseModel>) {
|
||||
* We currently ignore all other rundowns
|
||||
*/
|
||||
const firstRundown = getFirstRundown(result.rundowns);
|
||||
initRundown(firstRundown, result.customFields);
|
||||
await initRundown(firstRundown, result.customFields);
|
||||
}
|
||||
|
||||
const updatedData = await getDataProvider().getData();
|
||||
|
||||
Reference in New Issue
Block a user