From 7e53011883d8a595c549be9bb6040fddb3819944 Mon Sep 17 00:00:00 2001 From: Aldrin Jenson Date: Wed, 17 Aug 2022 22:11:32 +0530 Subject: [PATCH] Create src directory and restrucure code --- README.md | 2 +- main.ts | 199 +-------------------------------------------- package-lock.json | 4 +- src/App.ts | 31 +++++++ src/Modal.ts | 184 +++++++++++++++++++++++++++++++++++++++++ src/SettingsTab.ts | 42 ++++++++++ 6 files changed, 262 insertions(+), 200 deletions(-) create mode 100644 src/App.ts create mode 100644 src/Modal.ts create mode 100644 src/SettingsTab.ts diff --git a/README.md b/README.md index ef070bc..dc9eee3 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ Or ## Usage: -- Open command palete and search for "camera" once the extension is enabled +- Open command palette and search for "camera" once the extension is enabled OR - Click the camera icon in the left ribbon diff --git a/main.ts b/main.ts index 66cb4b9..f1275c2 100644 --- a/main.ts +++ b/main.ts @@ -1,198 +1,3 @@ -import { App, MarkdownView, Modal, Notice, Plugin } from "obsidian"; - -export default class ObsidianCamera extends Plugin { - async onload() { - this.addRibbonIcon("camera", "Obsidian Camera", (evt: MouseEvent) => { - new CameraModal(this.app).open(); - }); - - this.addCommand({ - id: "Open camera modal", - name: "Open camera modal / File Picker", - callback: () => { - new CameraModal(this.app).open(); - }, - }); - } -} - -class CameraModal extends Modal { - constructor(app: App) { - super(app); - } - - async onOpen() { - const { contentEl } = this; - const webCamContainer = contentEl.createDiv(); - // const pText = webCamContainer.createEl('p', { text: 'getVideoStream not yet supported on this device.' }); - // pText.style.display = 'none' - const videoEl = webCamContainer.createEl("video"); - videoEl.autoplay = true; - videoEl.muted = true - const recordVideoButton = webCamContainer.createEl("button", { - text: "Start recording", - }); - const switchCameraButton = webCamContainer.createEl("button", { - text: "Switch Camera", - }); - const snapPhotoButton = webCamContainer.createEl("button", { - text: "Take a snap", - }); - - const filePicker = webCamContainer.createEl("input", { - placeholder: "Choose image file from system", - type: "file", - }); - filePicker.accept = "image/*,video/*"; - filePicker.capture = "camera"; // back camera by default for mobile screens - - const filePicker2 = webCamContainer.createEl("input", { - placeholder: "Choose image file from system", - type: "file", - }); - filePicker2.accept = "image/*"; - filePicker2.capture = "camera"; // back camera by default for mobile screens - // filePicker2.style.display = 'none' - - - const chosenFolderPath = "attachments/snaps"; - const chunks: BlobPart[] = []; - let recorder: MediaRecorder = null; - let videoStream: MediaStream = null; - - const cameras = ( - await navigator.mediaDevices.enumerateDevices() - ).filter((d) => d.kind === "videoinput"); - - if (cameras.length <= 1) switchCameraButton.style.display = "none"; - let cameraIndex = 0; - - const getVideoStream = async () => { - try { - return await navigator.mediaDevices.getUserMedia({ - video: { deviceId: cameras[cameraIndex].deviceId }, - audio: true, - }); - } catch (error) { - console.log(error); - return null; - } - }; - - videoStream = await getVideoStream(); - if (!videoStream) { - videoEl.style.display = 'none' - // pText.style.display = 'block' - snapPhotoButton.style.display = 'none' - recordVideoButton.style.display = 'none' - switchCameraButton.style.display = 'none' - filePicker2.style.display = 'block' - } - - const handleImageSelectChange = async (file: File) => { - const chosenFile = file; - const bufferFile = await chosenFile.arrayBuffer(); - saveFile(bufferFile, false, chosenFile.name.split(' ').join('-')); - }; - filePicker.onchange = () => handleImageSelectChange(filePicker.files[0]) - filePicker2.onchange = () => handleImageSelectChange(filePicker2.files[0]) - - - const view = this.app.workspace.getActiveViewOfType(MarkdownView); - - const saveFile = async (file: ArrayBuffer, isImage = false, fileName = '') => { - if (!fileName) { - const dateString = (new Date() + "") - .slice(4, 28) - .split(" ") - .join("_") - .split(":") - .join("-"); - fileName = isImage - ? `image_${dateString}.png` - : `video_${dateString}.webm`; - } - if (!isImage) new Notice("Adding video to vault...") - - const filePath = chosenFolderPath + "/" + fileName; - const folderExists = - app.vault.getAbstractFileByPath(chosenFolderPath); - if (!folderExists) await app.vault.createFolder(chosenFolderPath); - const fileExists = - app.vault.getAbstractFileByPath(filePath); - if (!fileExists) - await app.vault.createBinary(filePath, file); - - if (!view) return new Notice(`Saved to ${filePath}`); - - const cursor = view.editor.getCursor(); - view.editor.replaceRange( - isImage - ? `![${fileName}](${filePath})\n` - : `\n![[${filePath}]]\n`, - cursor - ); - videoStream && videoStream.getTracks().forEach((track) => { - track.stop(); - }); - this.close(); // closing the modal - }; - - - switchCameraButton.onclick = async () => { - cameraIndex = (cameraIndex + 1) % cameras.length; - videoStream = await getVideoStream(); - }; - - snapPhotoButton.onclick = () => { - const canvas = webCamContainer.createEl("canvas"); - canvas.style.display = "none"; - const { videoHeight, videoWidth } = videoEl - canvas.height = videoHeight; - canvas.width = videoWidth; - - canvas.getContext("2d").drawImage(videoEl, 0, 0, videoWidth, videoHeight); - canvas.toBlob(async (blob) => { - const bufferFile = await blob.arrayBuffer(); - saveFile(bufferFile, true); - }, "image/png"); - }; - - videoEl.srcObject = videoStream; - - recordVideoButton.onclick = async () => { - switchCameraButton.disabled = true; - let isRecording: boolean = - recorder && recorder.state === "recording"; - if (isRecording) recorder.stop(); - isRecording = !isRecording; - recordVideoButton.innerText = isRecording - ? "Stop Recording" - : "Start Recording"; - - if (!recorder) { - recorder = new MediaRecorder(videoStream, { - mimeType: "video/webm", - }); - } - - recorder.ondataavailable = (e) => chunks.push(e.data); - recorder.onstop = async (e) => { - const blob = new Blob(chunks, { - type: "audio/ogg; codecs=opus", - }); - const bufferFile = await blob.arrayBuffer(); - saveFile(bufferFile, false); - }; - recorder.start(); - }; - - - } - - onClose() { - const { contentEl } = this; - contentEl.empty(); - } -} +import ObsidianCamera from "src/App"; +export default ObsidianCamera \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 35e888e..245a0b0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,11 +1,11 @@ { - "name": "obsidian-sample-plugin", + "name": "obsidian-camera-plugin", "version": "1.2.1", "lockfileVersion": 2, "requires": true, "packages": { "": { - "name": "obsidian-sample-plugin", + "name": "obsidian-camera-plugin", "version": "1.2.1", "license": "MIT", "devDependencies": { diff --git a/src/App.ts b/src/App.ts new file mode 100644 index 0000000..28b5326 --- /dev/null +++ b/src/App.ts @@ -0,0 +1,31 @@ +import { Plugin } from "obsidian"; +import CameraModal from "./Modal"; +import SampleSettingTab, { DEFAULT_SETTINGS, MyPluginSettings } from "./SettingsTab"; + +export default class ObsidianCamera extends Plugin { + settings: MyPluginSettings; + async onload() { + await this.loadSettings(); + this.addRibbonIcon("camera", "Obsidian Camera", (evt: MouseEvent) => { + new CameraModal(this.app).open(); + }); + this.addSettingTab(new SampleSettingTab(this.app, this)); + + this.addCommand({ + id: "Open camera modal", + name: "Open camera modal / File Picker", + callback: () => { + new CameraModal(this.app).open(); + }, + }); + } + + + async loadSettings() { + this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData()); + } + + async saveSettings() { + await this.saveData(this.settings); + } +} diff --git a/src/Modal.ts b/src/Modal.ts new file mode 100644 index 0000000..06f4085 --- /dev/null +++ b/src/Modal.ts @@ -0,0 +1,184 @@ +import { App, MarkdownView, Modal, Notice } from 'obsidian' + +class CameraModal extends Modal { + constructor(app: App) { + super(app); + } + + async onOpen() { + const { contentEl } = this; + const webCamContainer = contentEl.createDiv(); + // const pText = webCamContainer.createEl + // ('p', { text: 'getVideoStream not yet supported on this device.' }); + // pText.style.display = 'none' + const videoEl = webCamContainer.createEl("video"); + videoEl.autoplay = true; + videoEl.muted = true + const recordVideoButton = webCamContainer.createEl("button", { + text: "Start recording", + }); + const switchCameraButton = webCamContainer.createEl("button", { + text: "Switch Camera", + }); + const snapPhotoButton = webCamContainer.createEl("button", { + text: "Take a snap", + }); + + const filePicker = webCamContainer.createEl("input", { + placeholder: "Choose image file from system", + type: "file", + }); + filePicker.accept = "image/*,video/*"; + filePicker.capture = "camera"; // back camera by default for mobile screens + + const filePicker2 = webCamContainer.createEl("input", { + placeholder: "Choose image file from system", + type: "file", + }); + filePicker2.accept = "image/*"; + filePicker2.capture = "camera"; // back camera by default for mobile screens + // filePicker2.style.display = 'none' + + + const chosenFolderPath = "attachments/snaps"; + const chunks: BlobPart[] = []; + let recorder: MediaRecorder = null; + let videoStream: MediaStream = null; + + const cameras = ( + await navigator.mediaDevices.enumerateDevices() + ).filter((d) => d.kind === "videoinput"); + + if (cameras.length <= 1) switchCameraButton.style.display = "none"; + let cameraIndex = 0; + + const getVideoStream = async () => { + try { + return await navigator.mediaDevices.getUserMedia({ + video: { deviceId: cameras[cameraIndex].deviceId }, + audio: true, + }); + } catch (error) { + console.log(error); + return null; + } + }; + + videoStream = await getVideoStream(); + if (!videoStream) { + videoEl.style.display = 'none' + // pText.style.display = 'block' + snapPhotoButton.style.display = 'none' + recordVideoButton.style.display = 'none' + switchCameraButton.style.display = 'none' + filePicker2.style.display = 'block' + } + + const handleImageSelectChange = async (file: File) => { + const chosenFile = file; + const bufferFile = await chosenFile.arrayBuffer(); + saveFile(bufferFile, false, chosenFile.name.split(' ').join('-')); + }; + filePicker.onchange = () => handleImageSelectChange(filePicker.files[0]) + filePicker2.onchange = () => handleImageSelectChange(filePicker2.files[0]) + + + const view = this.app.workspace.getActiveViewOfType(MarkdownView); + + const saveFile = async (file: ArrayBuffer, isImage = false, fileName = '') => { + if (!fileName) { + const dateString = (new Date() + "") + .slice(4, 28) + .split(" ") + .join("_") + .split(":") + .join("-"); + fileName = isImage + ? `image_${dateString}.png` + : `video_${dateString}.webm`; + } + if (!isImage) new Notice("Adding video to vault...") + + const filePath = chosenFolderPath + "/" + fileName; + const folderExists = + app.vault.getAbstractFileByPath(chosenFolderPath); + if (!folderExists) await app.vault.createFolder(chosenFolderPath); + const fileExists = + app.vault.getAbstractFileByPath(filePath); + if (!fileExists) + await app.vault.createBinary(filePath, file); + + if (!view) return new Notice(`Saved to ${filePath}`); + + const cursor = view.editor.getCursor(); + view.editor.replaceRange( + isImage + ? `![${fileName}](${filePath})\n` + : `\n![[${filePath}]]\n`, + cursor + ); + videoStream && videoStream.getTracks().forEach((track) => { + track.stop(); + }); + this.close(); // closing the modal + }; + + + switchCameraButton.onclick = async () => { + cameraIndex = (cameraIndex + 1) % cameras.length; + videoStream = await getVideoStream(); + }; + + snapPhotoButton.onclick = () => { + const canvas = webCamContainer.createEl("canvas"); + canvas.style.display = "none"; + const { videoHeight, videoWidth } = videoEl + canvas.height = videoHeight; + canvas.width = videoWidth; + + canvas.getContext("2d").drawImage(videoEl, 0, 0, videoWidth, videoHeight); + canvas.toBlob(async (blob) => { + const bufferFile = await blob.arrayBuffer(); + saveFile(bufferFile, true); + }, "image/png"); + }; + + videoEl.srcObject = videoStream; + + recordVideoButton.onclick = async () => { + switchCameraButton.disabled = true; + let isRecording: boolean = + recorder && recorder.state === "recording"; + if (isRecording) recorder.stop(); + isRecording = !isRecording; + recordVideoButton.innerText = isRecording + ? "Stop Recording" + : "Start Recording"; + + if (!recorder) { + recorder = new MediaRecorder(videoStream, { + mimeType: "video/webm", + }); + } + + recorder.ondataavailable = (e) => chunks.push(e.data); + recorder.onstop = async (e) => { + const blob = new Blob(chunks, { + type: "audio/ogg; codecs=opus", + }); + const bufferFile = await blob.arrayBuffer(); + saveFile(bufferFile, false); + }; + recorder.start(); + }; + + + } + + onClose() { + const { contentEl } = this; + contentEl.empty(); + } +} + +export default CameraModal \ No newline at end of file diff --git a/src/SettingsTab.ts b/src/SettingsTab.ts new file mode 100644 index 0000000..8def21c --- /dev/null +++ b/src/SettingsTab.ts @@ -0,0 +1,42 @@ + +import ObsidianCamera from "main"; +import { App, PluginSettingTab, Setting } from "obsidian"; + +export interface MyPluginSettings { + mySetting: string; +} + +export const DEFAULT_SETTINGS: MyPluginSettings = { + mySetting: 'default' +} + +class SampleSettingTab extends PluginSettingTab { + plugin: ObsidianCamera; + + constructor(app: App, plugin: ObsidianCamera) { + super(app, plugin); + this.plugin = plugin; + } + + display(): void { + const { containerEl } = this; + + containerEl.empty(); + + containerEl.createEl('h2', { text: 'Settings for my awesome plugin.' }); + + new Setting(containerEl) + .setName('Setting #1') + .setDesc('It\'s a secret') + .addText(text => text + .setPlaceholder('Enter your secret') + .setValue(this.plugin.settings.mySetting) + .onChange(async (value) => { + console.log('Secret: ' + value); + this.plugin.settings.mySetting = value; + await this.plugin.saveSettings(); + })); + } +} + +export default SampleSettingTab \ No newline at end of file