-
No media imported
+
+ No media imported
+ No media found
diff --git a/packages/safelight/src/components/Menu/ProjectList/ProjectList.vue b/packages/safelight/src/components/Menu/ProjectList/ProjectList.vue
new file mode 100644
index 00000000..38a154ae
--- /dev/null
+++ b/packages/safelight/src/components/Menu/ProjectList/ProjectList.vue
@@ -0,0 +1,74 @@
+
+
+
+
+
+
+
+
+
+ {{ formatDateTime((slotProps.data as StoredProject).updated) }}
+
+
+
+
+ Open
+
+
+
+
+
+
diff --git a/packages/safelight/src/main.ts b/packages/safelight/src/main.ts
index 2a37149a..fb68d4c8 100644
--- a/packages/safelight/src/main.ts
+++ b/packages/safelight/src/main.ts
@@ -4,12 +4,10 @@ import { createRouter, createWebHistory } from 'vue-router/auto';
import App from './App.vue';
import PrimeVue, { type PrimeVueConfiguration } from 'primevue/config';
+import ConfirmationService from 'primevue/confirmationservice';
import 'primevue/resources/primevue.min.css';
import 'primevue/resources/themes/aura-dark-amber/theme.css';
import Tooltip from 'primevue/tooltip';
-// eslint-disable-next-line @typescript-eslint/ban-ts-comment
-//@ts-ignore
-// import SafelightTheme from '@/@core/Safelight';
import './style.scss';
@@ -24,10 +22,10 @@ export const pinia = createPinia();
app.use(pinia);
app.use(router);
app.use(PrimeVue, {
- // pt: SafelightTheme,
inputStyle: 'outlined',
ripple: false
} as PrimeVueConfiguration);
+app.use(ConfirmationService);
// Directives
app.directive('tooltip', Tooltip);
diff --git a/packages/safelight/src/stores/useProject.ts b/packages/safelight/src/stores/useProject.ts
index 8b4e2e68..9a1d9b20 100644
--- a/packages/safelight/src/stores/useProject.ts
+++ b/packages/safelight/src/stores/useProject.ts
@@ -1,5 +1,6 @@
+import { router } from '@/main';
import type BaseProject from '@safelight/shared/base/Project';
-import { Storage } from '@safelight/shared/base/Storage';
+import { Storage, type StoredProject } from '@safelight/shared/base/Storage';
import MediaManager from '@safelight/shared/Storage/MediaManager';
export const useProject = defineStore('Project', () => {
@@ -8,9 +9,13 @@ export const useProject = defineStore('Project', () => {
const timelineViewEnd = ref(0);
const project = shallowRef
();
+ const isLoaded = ref(false);
function setProject(newProject: BaseProject) {
project.value = newProject;
+ isLoaded.value = true;
+
+ useSessionStorage('project', '').value = newProject.id;
}
function loadFile(file: File) {
@@ -26,7 +31,6 @@ export const useProject = defineStore('Project', () => {
(m) => m.id == storingProcessing.value!.id
);
- console.log(existingMedia);
if (!existingMedia) {
const media = await Storage.getStorage().LoadMedia(
storingProcessing.value.id!
@@ -36,7 +40,6 @@ export const useProject = defineStore('Project', () => {
project.value!.media.push(media);
save();
}
- // project.activeTimeline.createTimelineItem(media);
}
resolve();
@@ -53,9 +56,49 @@ export const useProject = defineStore('Project', () => {
project,
setProject,
loadFile,
+ isLoaded,
cursor,
timelineViewStart,
timelineViewEnd,
- save
+ save,
+ openProject,
+ new: {
+ newSimpleProject
+ }
};
});
+
+async function newSimpleProject() {
+ const IndexedDbStorageController = (await import('@safelight/shared/Storage/IndexedDbStorage'))
+ .default;
+ Storage.setStorage(new IndexedDbStorageController());
+ const projectStore = useProject();
+ const SimpleProject = (await import('@safelight/shared/Project/SimpleProject')).default;
+ projectStore.setProject(new SimpleProject());
+}
+
+async function openProject(selectedProject: StoredProject) {
+ if (selectedProject.type == 'Simple') {
+ const IndexedDbStorageController = (
+ await import('@safelight/shared/Storage/IndexedDbStorage')
+ ).default;
+
+ const projectStore = useProject();
+ Storage.setStorage(new IndexedDbStorageController());
+
+ const project = await Storage.getStorage().LoadProject(selectedProject.id);
+
+ if (project) {
+ projectStore.setProject(project);
+ await toEditor();
+ } else {
+ console.error('Could not load project');
+ }
+ } else {
+ console.error('Project type not supported');
+ }
+}
+
+async function toEditor() {
+ await router.push('/editor');
+}
diff --git a/packages/safelight/src/style.scss b/packages/safelight/src/style.scss
index cf60635c..238997d0 100644
--- a/packages/safelight/src/style.scss
+++ b/packages/safelight/src/style.scss
@@ -65,8 +65,18 @@ body {
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
+
+ // Might wanna change this in the future
+ display: flex;
+ min-height: 100vh;
+
+ #app {
+ flex-grow: 1;
+ min-height: 100%;
+ }
}
html {
font-size: 14px;
+ min-height: 100%;
}
\ No newline at end of file
diff --git a/packages/safelight/src/views/Editor/Editor.vue b/packages/safelight/src/views/Editor/Editor.vue
index 9b959113..7a9e4c23 100644
--- a/packages/safelight/src/views/Editor/Editor.vue
+++ b/packages/safelight/src/views/Editor/Editor.vue
@@ -1,68 +1,97 @@
-
-
-
+
+
+
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
-
-
-
-
-
+
+
+
diff --git a/packages/safelight/src/views/projects.vue b/packages/safelight/src/views/projects.vue
index 3cbcd63b..e731ce6f 100644
--- a/packages/safelight/src/views/projects.vue
+++ b/packages/safelight/src/views/projects.vue
@@ -1,113 +1,9 @@
-
-
-
-
-
-
-
-
- {{ formatDateTime((slotProps.data as StoredProject).updated) }}
-
-
-
-
- Open
-
-
-
+
-
+
diff --git a/packages/shared/src/Storage/IndexedDbStorage.ts b/packages/shared/src/Storage/IndexedDbStorage.ts
index 2ad0653e..34c294b5 100644
--- a/packages/shared/src/Storage/IndexedDbStorage.ts
+++ b/packages/shared/src/Storage/IndexedDbStorage.ts
@@ -48,16 +48,29 @@ export default class IndexedDbStorageController extends BaseStorageController {
if (project.type == 'Simple') {
const proj = new SimpleProject();
proj.id = project.id;
- const mediaFetches = project.media.map((m) => this.LoadMedia(m));
- const timelineFetches = project.timelines.map((t) =>
- this.LoadTimeline(t)
- );
- proj.media.push(
- ...(await Promise.all(mediaFetches)).filter((m) => m !== undefined)
- );
- proj.timelines.push(
- ...(await Promise.all(timelineFetches)).filter((t) => t !== undefined)
- );
+ const mediaFetches = project.media.map(async (m) => {
+ const media = await this.LoadMedia(m).catch((reason) => {
+ console.error('Error loading media file for project', reason);
+ });
+ if (media) {
+ proj.media.push(media);
+ } else {
+ // TODO: Add a way for media to be marked as missing
+ }
+ });
+ const timelineFetches = project.timelines.map(async (t) => {
+ const timeline = await this.LoadTimeline(t).catch(
+ (reason) => {
+ console.error('Error loading timeline for project', reason);
+ }
+ );
+ if (timeline) {
+ proj.timelines.push(timeline);
+ }
+ });
+
+ // Load all timelines and media
+ await Promise.allSettled([...timelineFetches, ...mediaFetches]);
proj.name = project.name;
return proj;
} else {
@@ -107,9 +120,7 @@ export default class IndexedDbStorageController extends BaseStorageController {
this.db.media
.get({ id: mediaId })
.then((storedMedia) => {
- if (storedMedia) {
- resolve(this.storedMediaToMedia(storedMedia));
- }
+ resolve(storedMedia ? this.storedMediaToMedia(storedMedia) : undefined);
})
.catch(reject);
});
diff --git a/packages/shared/src/helpers/Video/GenerateMediaThumbnail.ts b/packages/shared/src/helpers/Video/GenerateMediaThumbnail.ts
index 079331bf..1d9ac180 100644
--- a/packages/shared/src/helpers/Video/GenerateMediaThumbnail.ts
+++ b/packages/shared/src/helpers/Video/GenerateMediaThumbnail.ts
@@ -1,6 +1,7 @@
import MimeMatcher from 'mime-matcher';
import MissingThumbnail from '../../../assets/missing_thumbnail.png?url';
+// TODO: Fix video not working for my HDR test video (HDR RGB lights.MOV)
export function generateMediaThumbnail(file: File, percent = 0.1, maxWidth = 427, maxHeight = 240) {
return new Promise((resolve) => {
const isVideo = new MimeMatcher('video/*').match(file.type);