Skip to content

Commit

Permalink
First Commit
Browse files Browse the repository at this point in the history
  • Loading branch information
ianbbqzy committed Aug 15, 2023
1 parent a0e880f commit 4c33494
Show file tree
Hide file tree
Showing 52 changed files with 2,242 additions and 6 deletions.
10 changes: 10 additions & 0 deletions .env_template
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
FIREBASE_API_KEY=<fill-me>
FIREBASE_AUTH_DOMAIN=<fill-me>
FIREBASE_PROJECT_ID=<fill-me>
FIREBASE_STORAGE_BUCKET=<fill-me>
FIREBASE_MESSAGING_SENDER_ID=<fill-me>
FIREBASE_APP_ID=<fill-me>
FIREBASE_MEASUREMENT_ID=<fill-me>
CLIENT_ID=<fill-me>
EXTENSION_KEY=<fill-me>
BACKEND_URL=<fill-me>
16 changes: 16 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
npm-debug.log
coverage/
node_modules/
package-lock.json
/vendor/
/*.zip
dist/
.env

server/.env
server/firebaseServiceAccountKey.json
server/.env
server/data
**/__pycache__/**
server/.venv
server/model/*
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@

# Change Log
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
The MIT License (MIT)

Copyright (c) 2023 Ian Lee

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
27 changes: 21 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@

# Manga Reader Browser Extension
# Veebee Browser Extension

**Install: [Chrome](https://chrome.google.com/webstore/detail/manga-reader/eabnmbpmoencafnpbobahdeamaljhoef)**
Uses Google Vision, DeepL, GPT, and [manga-ocr](https://github.com/kha-white/manga-ocr) to help you with translating and/or learning new languages while browing the internet in a Chrome Browser. One great use case is for reading manga but you can use it on most content on the web.

![](assets/example.png)

# Origins
**Install: [Chrome](https://chrome.google.com/webstore/detail/manga-reader/eabnmbpmoencafnpbobahdeamaljhoef)**

Note that the extension won't work on certain origins:
Note that the extension won't work on certain origins, such as:

- chrome origins like: `chrome://` and `chrome-extension://`
- the official chrome web store: `https://chrome.google.com/webstore/category/extensions`
Expand All @@ -21,17 +23,30 @@ Note that the extension won't work on certain origins:
## Build Backend Locally

1. Fill in the `server/.env_template` and rename it to `server/.env`
2. Set up GCP project with the correct (Optional if you comment out the GCP related stuff)
2. Set up GCP project (Optional if you comment out the GCP related stuff)
3. Create your `server/firebaseServiceAccountKey.json` (Optional if you comment out the authentication stuff)
4. Set up Firestore (Optional if you comment out the Firestore related code)
5. `pip install -r requirements.txt`
6. `python3 main.py`

# Contact

Please send any feedback or inquiries to [email protected]

This is an early Proof of Concept. If users like it, I am willing to invest more time to add various features, such as translating an entire page at a time, adding side panel to allow more space for translation details, and chat mode with GPT. Another obvious one is to add more languages. Any other feedback is very welcome!


# Acknowledgments

Learned a lot about Chrome extensions from
https://github.com/RasikaWarade/chrome-extension-mv3-firebase-boilerplate
https://github.com/simov/screenshot-capture

# License

The MIT License (MIT)

Copyright (c) 2023-present Ian Lee <[email protected]> (https://github.com/ianbbqzy/manga-reader)
Copyright (c) 2023-present Ian Lee <[email protected]> (https://github.com/ianbbqzy/veebee)

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
Binary file added assets/example.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
156 changes: 156 additions & 0 deletions background/background.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
// for stuff that doesn't involve the DOM of the web page the user is viewing

// Set default config values
chrome.storage.sync.get((config) => {
if (!config.api) {
chrome.storage.sync.set({api: 'deepl'})
}

if (!config.source_lang) {
chrome.storage.sync.set({source_lang: 'Japanese'})
}

if (!config.target_lang) {
chrome.storage.sync.set({target_lang: 'English'})
}

if (config.icon === undefined) {
config.icon = false
chrome.storage.sync.set({icon: false})
}

chrome.action.setIcon({
path: [16, 19, 38, 48, 128].reduce((all, size) => (
color = config.icon ? 'light' : 'dark',
all[size] = `/icons/${color}/${size}x${size}.png`,
all
), {})
})
})

// This is triggered when extension icon is clicked. This is the main entry point
// for screenshot capture.
// It injects the content script into the active tab.
chrome.action.onClicked.addListener((tab) => {
pingContentScript(tab, 'initCrop');
})

// take-screenshot is received when keyboard shortcut is triggered, as defined in manifest.json
// This is another entry point for screenshot capture.
chrome.commands.onCommand.addListener((command) => {
if (command === 'take-screenshot') {
chrome.tabs.query({active: true, currentWindow: true}, (tabs) => {
pingContentScript(tabs[0], 'initCrop');
})
}
})

// capture request is received when the user cropping by the user is done.
// active rquest is received when
chrome.runtime.onMessage.addListener((req, sender, res) => {
if (req.message === 'capture') {
chrome.tabs.query({active: true, currentWindow: true}, (tabs) => {
chrome.tabs.captureVisibleTab(tabs[0].windowId, (image) => {
res({message: 'image', image: image})
})
})
}
else if (req.message === 'active') {
// Change the extension icon and title based on whether the user is cropping
if (req.active) {
chrome.storage.sync.get(() => {
chrome.action.setTitle({tabId: sender.tab.id, title: 'Crop'})
chrome.action.setBadgeText({tabId: sender.tab.id, text: '◩'})
})
}
else {
chrome.action.setTitle({tabId: sender.tab.id, title: 'Crop Initialized'})
chrome.action.setBadgeText({tabId: sender.tab.id, text: ''})
}
}
return true
})

// Create context menu option
chrome.runtime.onInstalled.addListener(() => {
chrome.contextMenus.create({
id: 'translate-menu',
// chrome replaces %s with whatever is highlighted
title: 'Translate %s',
// selects what's hightlighted by the cursor?
contexts: ['selection']
});
});

// Handle when context menu is clicked
chrome.contextMenus.onClicked.addListener((info, tab) => {
console.log(info.menuItemId)
if (info.menuItemId === "translate-menu") {
// Let content script know that a text translation is initialized.
// This has to be done in background script in case the content
// script has not been initialized.
pingContentScript(tab, 'initTextTranslation');

// continue with the translation process.
chrome.storage.sync.get((config) => {
if (!config.idToken) {
pingContentScript(tab, "Please login first. Right click on the extension icon and click on options.")
} else {
callTranslateWithText(info.selectionText, config.source_lang, config.target_lang, config.api, config.idToken)
.then(response => {
pingContentScript(tab, response)
})
.catch(error => {
console.error(`error: ${error.message}`);
pingContentScript(tab, `error: ${error.message}`)
});
}
})
}
});

async function callTranslateWithText(text, source_lang, target_lang, api, idToken) {
const url = "__BACKEND_URL__";
const headers = new Headers();
headers.append('Authorization', `Bearer ${idToken}`);
headers.append('Content-Type', `application/json`);

try {
const resp = await fetch(url + '/translate-text?api=' + api + '&source_lang=' + source_lang + '&target_lang=' + target_lang, {
method: 'POST',
headers: headers,
body: JSON.stringify({
'text': text
})
}).then(res => res.json())
if (resp.error) {
return `Translation: ${resp.error}`;
}
return `Translation: ${resp.translation}`;
} catch (err) {
return `Translation: ${err.message}`;
}
}

// Sends a message to the content script
// If it doesn't receive a response within a specific timeout, it
// determines that content script is not initialized and it will initialize it.
// After another small timeout, it'll try to send the message again.
function pingContentScript(tab, message) {
chrome.tabs.sendMessage(tab.id, {message: message}, (res) => {
if (res) {
// if response is received before the timeout is triggered
// clears the timeout call
clearTimeout(timeout)
}
})

var timeout = setTimeout(() => {
chrome.scripting.insertCSS({files: ['css/content.css'], target: {tabId: tab.id}})
chrome.scripting.executeScript({files: ['content.js'], target: {tabId: tab.id}})

setTimeout(() => {
chrome.tabs.sendMessage(tab.id, {message: message})
}, 100)
}, 100)
}
Loading

0 comments on commit 4c33494

Please sign in to comment.