diff --git a/.gitignore b/.gitignore index effd1305..00477319 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,4 @@ sign.sh /Transcription-Server/build AutoSubs-App/package-lock.json /Mac-Package/Payload +*.pem \ No newline at end of file diff --git a/AutoSubs-App/src-tauri/resources/AutoSubs V2.lua b/AutoSubs-App/src-tauri/resources/AutoSubs V2.lua index e1b78ee4..2f2bc62f 100644 --- a/AutoSubs-App/src-tauri/resources/AutoSubs V2.lua +++ b/AutoSubs-App/src-tauri/resources/AutoSubs V2.lua @@ -290,10 +290,16 @@ function ExportAudio(outputDir) markIn = renderSettings["MarkIn"], markOut = renderSettings["MarkOut"] } - + local renderingLoops = 0 while project:IsRenderingInProgress() do print("Rendering...") sleep(0.5) -- Check every 500 milliseconds + renderingLoops = renderingLoops + 1 + if renderingLoops > 3 then + print("Render Status") + print(project:GetRenderStatus(pid)) + renderingLoops = 0 + end end end) @@ -457,6 +463,13 @@ function AddSubtitles(filePath, trackIndex, templateName, textFormat, removePunc end end + +-- Add subtitles to the timeline using the specified template +function AddMediaToBin(filePath) + local mediaStorage = resolve:GetMediaStorage() + mediaStorage:AddItemListToMediaPool(filePath) +end + local function set_cors_headers(client) client:send("HTTP/1.1 200 OK\r\n") client:send("Access-Control-Allow-Origin: *\r\n") @@ -584,6 +597,23 @@ while not quitServer do body = json.encode({ message = "Job completed" }) + elseif data.func == "GetTimelineStoragePath" then -- this is how davinchi knows what to do. Localhost:55010 + print("[AutoSubs Server] Cooked With OIL") + + print("Mounted Volume List: ", mountedVolumeList[1]) + print("Current Project: ", currentProject) + -- above is where files collected using OIL are stored mountedVolumeList[1]+currentProject + -- Prepare the body for the API request + + body = json.encode({ + message = "Job completed", + filePath = mountedVolumeList[1] .. '/' .. currentProject + }) + elseif data.func == "AddMediaToBin" then -- this is h + AddMediaToBin(data.filePath) + body = json.encode({ + message = "Job completed" + }) elseif data.func == "Exit" then body = json.encode({ message = "Server shutting down" diff --git a/BrowserExtentions/ChromeExtention.crx b/BrowserExtentions/ChromeExtention.crx new file mode 100644 index 00000000..1adbe07f Binary files /dev/null and b/BrowserExtentions/ChromeExtention.crx differ diff --git a/BrowserExtentions/ChromeExtention/background.js b/BrowserExtentions/ChromeExtention/background.js new file mode 100644 index 00000000..e8bb662e --- /dev/null +++ b/BrowserExtentions/ChromeExtention/background.js @@ -0,0 +1,197 @@ +// Create context menu items for images and videos +chrome.runtime.onInstalled.addListener(() => { + chrome.contextMenus.create({ + id: "sendMediaToApi", + title: "Send URL to Local API", + contexts: ["image", "video"], // Show menu on right-clicking images or videos + }); + + chrome.contextMenus.create({ + id: 'exit', + title: 'Exit', + contexts: ["image", "video"] + }); +}); + +function makenotifications(title, message) { + chrome.notifications.create( + { + type: "basic", + iconUrl: "images/icon-128x128.png", + title: title, + message: message, + priority: 2, + }, + (notificationId) => { + // Clear the notification after 3 seconds + setTimeout(() => { + chrome.notifications.clear(notificationId); + }, 3000); + } + ); +} + + + +chrome.tabs.onUpdated.addListener((tabId, changeInfo) => { + if (changeInfo.status === "complete") { + chrome.scripting + .executeScript({ + target: { tabId }, + files: ["./content.js"], + }) + .then(() => { + console.log("content script injected"); + }) + .catch((err) => console.log(err, "error injecting script")); + } + }); + +// Handle the context menu click +chrome.contextMenus.onClicked.addListener((info) => { + console.log(info); + if (info.menuItemId === "sendMediaToApi" && info.srcUrl) { + const mediaUrl = info.srcUrl; // URL of the image or video + sendStringToApi('save_image',mediaUrl).then((result) => { + if (result != "true") { + console.log(result); + const title = "Failed To send, check if localhost is running"; + const message = + "Failed to send media URL. Please check if the local API is running."; + makenotifications(title, message); + } + }); + } + else if (info.menuItemId === 'exit') { + exit().then((result) => { + if (result != "true") { + console.log(result); + const title = "Failed To exit, check if localhost is running"; + const message = + "Failed to exit. Please check if the local API is running."; + makenotifications(title, message); + } + }); + } +}); + +chrome.commands.onCommand.addListener((command) => { + console.log(`Command: ${command}`); + if (command === "open-screen-captue") { + // Get the current active tab + chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => { + if (tabs.length > 0) { + const currentTab = tabs[0]; + console.log("Current Tab:", currentTab); + + // Perform any action with the current tab + // For example, you can send a message to the content script + chrome.tabs.sendMessage( + currentTab.id, + { action: "recording_request" }, + (response) => { + if (chrome.runtime.lastError) { + console.error( + "Error sending message:", + chrome.runtime.lastError.message + ); + const title = "Screen Recorder Error"; + const message = + "Failed to start screen recording. Please refresh the page, or go to a different tab to start capture."; + makenotifications(title, message); + } else { + console.log("Response from content script:", response); + if (response === undefined) { + const title = "cannot start capture on base chrome pages"; + const message = + "Failed to start screen recording. Please or go to a different tab or page to start capture."; + makenotifications(title, message); + } + } + } + ); + + chrome.action.openPopup(); + console.log("Popup opened"); + } else { + console.error("No active tab found"); + } + }); + } +}); + +chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { + if (message.action === "get_tab_title") { + chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => { + if (tabs.length > 0) { + sendResponse({ tabTitle: tabs[0].title }); + } else { + sendResponse({ tabTitle: null }); + } + }); + return true; // Keep the message channel open for sendResponse + //the file save name to send to the backend. + } else if (message.action === "save_name") { + sendStringToApi('save_video',message.saveName).then((result) => { + if (result != "true") { + console.log(result); + const title = "Failed To send filename, check if localhost is running"; + const message = + "Failed to send filename. Please check if the local API is running."; + makenotifications(title, message); + } + }); + } +}); +function exit() { + const apiUrl = `http://localhost:55000`; // Replace with your local API endpoint + console.log("Sending exit to API:", apiUrl); + return fetch(apiUrl, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + func: 'Exit', + }), + }) + .then((response) => { + if (response.ok) { + console.log("stringSent sent successfully!"); + return "true"; + } else { + console.error("Failed to send stringSent:", response.status); + return response.status; + } + }) + .catch((error) => { + console.error("Error:", error); + return error; + }); +} +function sendStringToApi(apiPath,stringSent) { + const apiUrl = `http://localhost:55000`; // Replace with your local API endpoint + console.log("Sending stringSent to API:", apiUrl); + return fetch(apiUrl, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + func: 'save_image', + releventString: stringSent }), + }) + .then((response) => { + if (response.ok) { + console.log("stringSent sent successfully!"); + return "true"; + } else { + console.error("Failed to send stringSent:", response.status); + return response.status; + } + }) + .catch((error) => { + console.error("Error:", error); + return error; + }); +} diff --git a/BrowserExtentions/ChromeExtention/content.js b/BrowserExtentions/ChromeExtention/content.js new file mode 100644 index 00000000..ccac7a91 --- /dev/null +++ b/BrowserExtentions/ChromeExtention/content.js @@ -0,0 +1,111 @@ +console.log("content script running"); + +var recorder = null; + +function onAccessApproved(stream) { + recorder = new MediaRecorder(stream, { + videoBitsPerSecond: 5000000, + ignoreMutedMedia: true, + mimeType: "video/webm;codecs=h264,vp9,opus", + }); + + recorder.start(); + + recorder.onstop = function () { + stream.getTracks().forEach(function (track) { + if (track.readyState === "live") { + track.stop(); + } + }); + }; + + recorder.ondataavailable = function (event) { + let recordedData = event.data; + let url = URL.createObjectURL(recordedData); + // Get the current tab's title + chrome.runtime.sendMessage({ action: "get_tab_title" }, (response) => { + if (response && response.tabTitle) { + let tabTitle = response.tabTitle; + // Clean the tab title to make it a valid file name + let cleanedTitle = tabTitle.replace(/[^a-z0-9]/gi, "_").toLowerCase(); + + let a = document.createElement("a"); + a.style.display = "none"; + a.href = url; + a.download = `${cleanedTitle}.mp4`; + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); + URL.revokeObjectURL(url); + chrome.runtime.sendMessage({ action: "save_name", saveName: a.download }); + } else { + console.error("Failed to get tab title"); + let a = document.createElement("a"); + a.style.display = "none"; + a.href = url; + a.download = "screen-recording.mp4"; + document.body.append(a); + a.click(); + document.body.removeChild(a); + URL.revokeObjectURL(url); + chrome.runtime.sendMessage({ action: "save_name", saveName: a.download }); + } + }); + }; +} + +chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { + if (message.action === "recording_request") { + console.log("recording requested"); + sendResponse(`seen: ${message.action}`); + + navigator.mediaDevices + .getDisplayMedia({ + audio: true, + video: true, + }) + .then((stream) => { + onAccessApproved(stream); + }); + } + + if (message.action === "stop_recording") { + console.log("Stop recording"); + sendResponse(`seen: ${message.action}`); + + if (!recorder) { + return console.log("no recording"); + } else { + recorder.stop(); + } + } +}); + +chrome.commands.onCommand.addListener((command) => { + console.log(`Command: ${command}`); + console.log(bool(command === "open-screen-captue")); + if (command === "open-screen-captue") { + console.log("recording requested"); + sendResponse(`seen: ${command}`); + + navigator.mediaDevices + .getDisplayMedia({ + audio: true, + video: true, + }) + .then((stream) => { + onAccessApproved(stream); + }); + } + + if (command === "end-screen-captue") { + console.log("Stop recording"); + sendResponse(`seen: ${command}`); + + if (!recorder) { + return console.log("no recording"); + } else { + recorder.stop(); + } + } +}); diff --git a/BrowserExtentions/ChromeExtention/css/styles.css b/BrowserExtentions/ChromeExtention/css/styles.css new file mode 100644 index 00000000..48b52786 --- /dev/null +++ b/BrowserExtentions/ChromeExtention/css/styles.css @@ -0,0 +1,17 @@ + +.content{ + min-width: 250px; + padding: 5px; + +} + +.button button{ + background: black; + padding: 5px; + border-radius: 5px; + color: yellow; +} + +.button button:hover{ + box-shadow: 1px 0px 5p gray; +} \ No newline at end of file diff --git a/BrowserExtentions/ChromeExtention/images/icon-128x128.png b/BrowserExtentions/ChromeExtention/images/icon-128x128.png new file mode 100644 index 00000000..6a38792a Binary files /dev/null and b/BrowserExtentions/ChromeExtention/images/icon-128x128.png differ diff --git a/BrowserExtentions/ChromeExtention/images/icon-16x16.png b/BrowserExtentions/ChromeExtention/images/icon-16x16.png new file mode 100644 index 00000000..6a38792a Binary files /dev/null and b/BrowserExtentions/ChromeExtention/images/icon-16x16.png differ diff --git a/BrowserExtentions/ChromeExtention/images/icon-32x32.png b/BrowserExtentions/ChromeExtention/images/icon-32x32.png new file mode 100644 index 00000000..6a38792a Binary files /dev/null and b/BrowserExtentions/ChromeExtention/images/icon-32x32.png differ diff --git a/BrowserExtentions/ChromeExtention/js/popup.js b/BrowserExtentions/ChromeExtention/js/popup.js new file mode 100644 index 00000000..a349d255 --- /dev/null +++ b/BrowserExtentions/ChromeExtention/js/popup.js @@ -0,0 +1,43 @@ +startVideoButtondocument.addEventListener("DOMContentLoaded", ()=>{ + const startRecordingButton = document.querySelector("button#start_recording") + const stopRecordingButton = document.querySelector("button#stop_recording") + + chrome.notifications.getAll(function(notifications) { + window.close(); + }); + + chrome.tabs.query({active: true, currentWindow: true}, function(tabs){ + console.log(tabs, 'line 7') + chrome.tabs.sendMessage(tabs[0].id, {action: "recording_request"}, function(response){ + if(!chrome.runtime.lastError){ + console.log(response) + }else{ + console.log(chrome.runtime.lastError, 'line 15 error') + } + }) + }) + + startRecordingButton.addEventListener("click", ()=>{ + chrome.tabs.query({active: true, currentWindow: true}, function(tabs){ + chrome.tabs.sendMessage(tabs[0].id, {action: "recording_request"}, function(response){ + if(!chrome.runtime.lastError){ + console.log(response) + }else{ + console.log(chrome.runtime.lastError, 'line 26 error') + } + }) + }) + }); + + stopRecordingButton.addEventListener("click", ()=>{ + chrome.tabs.query({active: true, currentWindow: true}, function(tabs){ + chrome.tabs.sendMessage(tabs[0].id, {action: "stop_recording"}, function(response){ + if(!chrome.runtime.lastError){ + console.log(response) + }else{ + console.log(chrome.runtime.lastError, 'line 38 error') + } + }) + }) + }) +}) \ No newline at end of file diff --git a/BrowserExtentions/ChromeExtention/manifest.json b/BrowserExtentions/ChromeExtention/manifest.json new file mode 100644 index 00000000..b1333c1a --- /dev/null +++ b/BrowserExtentions/ChromeExtention/manifest.json @@ -0,0 +1,48 @@ +{ + "name": "Davinchi OIL", + "version": "0.1", + "manifest_version": 3, + "description": "Send content to Davinchi Resolve BIN", + "permissions": ["storage","notifications", "tabs", "activeTab","scripting","contextMenus","downloads"], + "icons":{ + "128": "./images/icon-128x128.png" + }, + "background":{ + "service_worker": "./background.js" + }, + "action":{ + "default_popup": "popup.html", + "default_icon": { + "16": "./images/icon-16x16.png", + "32": "./images/icon-32x32.png", + "128": "./images/icon-128x128.png" + } + }, + "host_permissions": [ + "http://localhost:55000/*", + "" + ],"commands": { + "open-screen-captue": { + "suggested_key": { + "default": "Ctrl+Shift+E", + "mac": "Command+Shift+E" + }, + "description": "Run \"screen-captue\" on the current page." + }, + "end-screen-captue": { + "suggested_key": { + "default": "Ctrl+Shift+Q", + "mac": "Command+Shift+Q" + }, + "description": "INCOMEPLETE:Run \"screen-captue\" on the current page." + }, + "_execute_action": { + "suggested_key": { + "windows": "Ctrl+Shift+E", + "mac": "Command+Shift+E", + "chromeos": "Ctrl+Shift+E", + "linux": "Ctrl+Shift+E" + } + } + } +} \ No newline at end of file diff --git a/BrowserExtentions/ChromeExtention/popup.html b/BrowserExtentions/ChromeExtention/popup.html new file mode 100644 index 00000000..2b6d1ef4 --- /dev/null +++ b/BrowserExtentions/ChromeExtention/popup.html @@ -0,0 +1,21 @@ + + + + + + OIL Video Recorder + + +
+

START RECORDING

+
+ +
+
+
+ +
+
+ + + \ No newline at end of file diff --git a/Transcription-Server/requirements-win.txt b/Transcription-Server/requirements-win.txt index e320ba84..015a46cc 100644 --- a/Transcription-Server/requirements-win.txt +++ b/Transcription-Server/requirements-win.txt @@ -7,4 +7,7 @@ git+https://github.com/jianfch/stable-ts.git faster-whisper pyannote.audio appdirs -pyinstaller \ No newline at end of file +pyinstaller +urllib +mimetypes +requests \ No newline at end of file diff --git a/Transcription-Server/server.py b/Transcription-Server/server.py index 20985630..6a558b93 100644 --- a/Transcription-Server/server.py +++ b/Transcription-Server/server.py @@ -35,12 +35,20 @@ def __getattr__(self, attr): import random import uvicorn from pydantic import BaseModel -from fastapi import FastAPI, HTTPException, status +from fastapi import FastAPI, HTTPException, status, Request import asyncio import appdirs import time import platform import stable_whisper +from fastapi.responses import JSONResponse +import sys +import os +import requests +import mimetypes +from urllib.parse import urlparse, urlunparse +import re + # Define a base cache directory using appdirs if platform.system() == 'Windows': @@ -167,8 +175,7 @@ def is_model_accessible(model_id, token=None, revision=None): return False except HfHubHTTPError as e: if e.response.status_code == 403: - print(f"Access denied to model '{ - model_id}'. You may need to accept the model's terms or provide a valid token.") + print(f"Access denied to model '{model_id}'. You may need to accept the model's terms or provide a valid token.") elif e.response.status_code == 401: print(f"Unauthorized access. Please check your Hugging Face access token.") else: @@ -601,5 +608,96 @@ async def validate_model(request: ValidateRequest): return {"isAvailable": True, "message": "All required models are available"} +@app.route('/save_image/', methods=['POST']) +async def receive_text(request:Request): + data = await request.json() + print("data",data) + + relevent_string = data.get('releventString', '') + response = { + 'releventString': relevent_string, + 'message': 'url received successfully' + } + print(relevent_string) + print(response) + handle_url(response['releventString']) + return JSONResponse(content=response, status_code=200) + +def handle_url(url): + print("url",url) + parsed_url = urlparse(url) + clean_url = urlunparse(parsed_url._replace(query='')) + mime_type, _ = mimetypes.guess_type(clean_url) + if mime_type : + if mime_type.startswith('image'): + downloadfile(url, 'image') + elif mime_type.startswith('video') or mime_type.startswith('audio'): + downloadfile(url, 'video') + else: + print("Unsupported file type") + else: + print("Could not determine the file type,defualt to img") + downloadfile(url, 'image') + return "Downloaded" + +def sanitize_filename(filename): + return re.sub(r'[^\w\-_\. ]', '_', filename) + + +def OIL_LUA_call(func, otherDataName = 'Null', otherData = 'Null'): + resolveAPI = "http://localhost:55010/" + # Define the headers + headers = { + 'Content-Type': 'application/json', + } + # Define the JSON payload + data = { + "func": func, + otherDataName: otherDataName + } + # Send the POST request with JSON data + response = requests.post(resolveAPI, headers=headers, json=data) + # Print the response + +def downloadfile( url,type): + + response = OIL_LUA_call("GetTimelineStoragePath") + jsondata = response.json() + getfilepath = jsondata['filePath'] + + + # Create the directory if it doesn't exist + if not os.path.exists(getfilepath): + os.makedirs(getfilepath) + + if type == "video": # TODO: handle WEBM and other nom MP4 files (wav? if not supported already) + response = requests.get(url, stream=True) + file_extension = mimetypes.guess_extension(response.headers['Content-Type']) + if not file_extension: + file_extension = '.mp4' # Default to .mp4 if Content-Type is not available + if len(sanitized_url) > 50: + sanitized_url = sanitized_url[:50] + file_path = f"{getfilepath}/{sanitized_url.replace(".","")}{file_extension}" + with open(file_path, 'wb') as f: + for chunk in response.iter_content(chunk_size=1024): + if chunk: + f.write(chunk) + elif type == "image": + response = requests.get(url) + fileformat = mimetypes.guess_extension(response.headers['Content-Type']) + file_path = f"{getfilepath}/{sanitized_url.replace(".","")}" + with open(file_path+fileformat, 'wb') as f: + f.write(response.content) + file_path = file_path+fileformat + + + response=OIL_LUA_call("AddMediaToBin", "filePath", file_path) + + # Print the response + print("Status Code:", response.status_code) + print("Response JSON:", response.json()) + + + if __name__ == "__main__": uvicorn.run(app, host="localhost", port=55000, log_level="info") diff --git a/other-scripts/uninstaller b/other-scripts/uninstaller index 9e0a07e0..60785c08 100755 --- a/other-scripts/uninstaller +++ b/other-scripts/uninstaller @@ -1,21 +1,16 @@ #!/bin/bash -# Define the installation directory -INSTALL_DIR="/Applications/AutoSubs" - -echo "Uninstalling AutoSubs..." - # Paths to remove -AUTOSUBS_DIR="${INSTALL_DIR}" -LUA_SCRIPT="${INSTALL_DIR}/Scripts/Utility/AutoSubs V2.lua" +echo "Uninstalling AutoSubs..." +files="${APPDATA}/Blackmagic Design/DaVinci Resolve/Support/Fusion/Scripts/Utility/" -# Remove the AutoSubs directory -if [ -d "$AUTOSUBS_DIR" ]; then - rm -rf "$AUTOSUBS_DIR" - echo "Removed directory: $AUTOSUBS_DIR" -else - echo "Directory not found: $AUTOSUBS_DIR" -fi +# Loop through each file in the directory +for item in "${files}"*; do + if [[ "$item" == *AutoSub* ]]; then + rm -rf "$item" + echo "Removed: $item" + fi +done echo "Uninstallation complete." exit 0 \ No newline at end of file diff --git a/server-testing/server.lua b/server-testing/server.lua index 1070dc47..1fb55840 100644 --- a/server-testing/server.lua +++ b/server-testing/server.lua @@ -271,7 +271,10 @@ function AddSubtitles(filePath, trackIndex, templateName) end end end - +function AddMediaToBin(filePath) + local mediaStorage = resolve:GetMediaStorage() + mediaStorage:AddItemListToMediaPool(filePath) +end print("AutoSubs server is listening on port: ", port) itm.Message.Text = "Waiting for Task" while not itm.ExitButton.Checked do @@ -312,6 +315,23 @@ while not itm.ExitButton.Checked do itm.Message.Text = "Adding subtitles to timeline..." AddSubtitles(data.filePath, data.trackIndex, data.templateName) body = json.encode({ message = "Job completed" }) + elseif data.func == "GetTimelineStoragePath" then -- this is how davinchi knows what to do. Localhost:55010 + print("[AutoSubs Server] Cooked With OIL") + + print("Mounted Volume List: ", mountedVolumeList[1]) + print("Current Project: ", currentProject) + -- above is where files collected using OIL are stored mountedVolumeList[1]+currentProject + -- Prepare the body for the API request + + body = json.encode({ + message = "Job completed", + filePath = mountedVolumeList[1] .. '/' .. currentProject + }) + elseif data.func == "AddMediaToBin" then -- this is h + AddMediaToBin(data.filePath) + body = json.encode({ + message = "Job completed" + }) elseif data.func == "Exit" then break else