From e21400debecad7975e98e5a761e1d75ff504b0b4 Mon Sep 17 00:00:00 2001
From: daijro <72637910+daijro@users.noreply.github.com>
Date: Mon, 27 Dec 2021 18:30:08 -0600
Subject: [PATCH 01/39] Fix preview gif
Use githubusercontent instead of discord cdn
---
README.md | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/README.md b/README.md
index cd03fa1..d666350 100644
--- a/README.md
+++ b/README.md
@@ -7,7 +7,8 @@
#### SearchifyX in action
-![in action](https://media.discordapp.net/attachments/714922631693860956/925164698058502175/ta1Y74Ehy8.gif)
+![in action](https://user-images.githubusercontent.com/72637910/147515480-236fe392-6282-44bc-b888-54f15adeb523.gif)
+
@@ -88,4 +89,4 @@ Here is a [demo website](https://www.codingwithjesse.com/demo/2007-05-16-detect-
#### Settings page
-![settings](https://i.imgur.com/iOciyxd.png)
\ No newline at end of file
+![settings](https://i.imgur.com/iOciyxd.png)
From a0abb5e48a4afdb5c89cf600c73af838e559abc8 Mon Sep 17 00:00:00 2001
From: daijro <72637910+daijro@users.noreply.github.com>
Date: Mon, 27 Dec 2021 18:52:37 -0600
Subject: [PATCH 02/39] Add disclaimer
Add disclaimer and CLI usage
---
README.md | 40 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 40 insertions(+)
diff --git a/README.md b/README.md
index d666350..734a3d9 100644
--- a/README.md
+++ b/README.md
@@ -30,6 +30,35 @@ pyperclip
You also need to download [tesseract-ocr](https://cdn.discordapp.com/attachments/714922631693860956/925180968808087572/tesseract-ocr.7z) and extract it to the root directory to use OCR.
+
+
+## CLI usage
+
+An alternative for Linux/Mac OS users is to use [`scraper.py`](https://github.com/daijro/SearchifyX/blob/main/scraper.py) as a CLI tool.
+
+Usage:
+```
+scraper.py [-h] [--query QUERY] [--output OUTPUT] [--sites SITES]
+```
+
+Required arguments:
+
+```
+--query QUERY, -q QUERY
+ query to search for
+```
+
+Optional arguments:
+
+```
+-h, --help show this help message and exit
+
+--output OUTPUT, -o OUTPUT
+ output file (optional)
+--sites SITES, -s SITES
+ question sources quizlet,quizizz,brainly (comma seperated list)
+```
+
---
@@ -41,6 +70,7 @@ You also need to download [tesseract-ocr](https://cdn.discordapp.com/attachments
- Various different options for input for stealthy use
+
### Stealth options
@@ -56,8 +86,11 @@ You also need to download [tesseract-ocr](https://cdn.discordapp.com/attachments
- Quickly change window opacity hotkey
+- Change window opacity (slider in title bar)
+
- Option to not show in taskbar
+
### Window switching safety lock
@@ -90,3 +123,10 @@ Here is a [demo website](https://www.codingwithjesse.com/demo/2007-05-16-detect-
#### Settings page
![settings](https://i.imgur.com/iOciyxd.png)
+
+
+---
+
+### Disclaimer
+
+The purpose of this program is to provide an example of asynchronous webscraping and data gathering in Python. I am not in any way attempting to promote cheating, and I am not responsible for any misuse of this tool. This tool was created strictly for educational purposes only.
From 578d8ac451538265696ea0106fe9e2477e423464 Mon Sep 17 00:00:00 2001
From: daijro <72637910+daijro@users.noreply.github.com>
Date: Mon, 27 Dec 2021 18:54:32 -0600
Subject: [PATCH 03/39] Change default transp value
Change default transparency value to 95 instead of 97 (looks nicer)
---
config.json | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/config.json b/config.json
index 7a89970..2aa5dac 100644
--- a/config.json
+++ b/config.json
@@ -10,11 +10,11 @@
"search_ocr": true,
"search_paste": true,
"save_focus": false,
- "save_transp": 97,
+ "save_transp": 95,
"save_pos": null,
"rclick_reset": true,
"on_top": true,
"hide_taskbar": false,
"theme": 0,
"font_size": 10
-}
\ No newline at end of file
+}
From c4dd7c577b854098a74093ea193be358381ba2bf Mon Sep 17 00:00:00 2001
From: daijro <72637910+daijro@users.noreply.github.com>
Date: Mon, 27 Dec 2021 19:58:59 -0600
Subject: [PATCH 04/39] Error handling
---
README.md | 12 +++---------
config.json | 2 +-
gui.pyw | 14 ++++++++++----
3 files changed, 14 insertions(+), 14 deletions(-)
diff --git a/README.md b/README.md
index 734a3d9..e100186 100644
--- a/README.md
+++ b/README.md
@@ -16,19 +16,13 @@
Windows binaries are not avaliable yet at this moment.
-Here are the [python 3.8](https://www.python.org/downloads/release/python-389/) dependencies:
+Install the [Python 3.8](https://www.python.org/downloads/release/python-389/) dependencies:
```
-PyQt5
-pywin32
-keyboard
-grequests
-beautifulsoup4
-pytesseract
-pyperclip
+python -m pip install requirements.txt
```
-You also need to download [tesseract-ocr](https://cdn.discordapp.com/attachments/714922631693860956/925180968808087572/tesseract-ocr.7z) and extract it to the root directory to use OCR.
+You also need to download [tesseract-ocr](https://cdn.discordapp.com/attachments/714922631693860956/925180968808087572/tesseract-ocr.7z) and extract it to the root directory to use the OCR tool.
diff --git a/config.json b/config.json
index 2aa5dac..7a7e025 100644
--- a/config.json
+++ b/config.json
@@ -17,4 +17,4 @@
"hide_taskbar": false,
"theme": 0,
"font_size": 10
-}
+}
\ No newline at end of file
diff --git a/gui.pyw b/gui.pyw
index 8b3b106..bc92d8b 100644
--- a/gui.pyw
+++ b/gui.pyw
@@ -314,13 +314,18 @@ class UI(QMainWindow):
# calling scraper and adding to ui
def run_searcher(self):
+ query = self.search_bar.text().strip()
+
+ if not query:
+ self.status_label.setText('Please enter a search query')
+ return
+
self.status_label.setText('Searching...')
self.search_frame.setEnabled(False)
QtWidgets.QApplication.processEvents()
sites = []
- query = self.search_bar.text().strip()
if self.quizizz_button.isChecked(): sites.append('quizizz')
if self.quizlet_button.isChecked(): sites.append('quizlet')
if self.brainly_button.isChecked(): sites.append('brainly')
@@ -365,12 +370,13 @@ class UI(QMainWindow):
# data entry tools
def run_ocr_tool(self, force_run_searcher=False):
+ if not os.path.exists(resource_path('tesseract-ocr')):
+ self.status_label.setText('Tesseract not found.')
+ return
+
self.status_label.setText('OCR in progress...')
QtWidgets.QApplication.processEvents()
- # # delay
- # time.sleep(0.2)
-
self.setWindowOpacity(0)
self.stackedWidget.setCurrentIndex(0)
QtWidgets.QApplication.processEvents()
From 68a655b736411df822391e166e229c7ba4fbd8d2 Mon Sep 17 00:00:00 2001
From: daijro <72637910+daijro@users.noreply.github.com>
Date: Mon, 27 Dec 2021 19:59:11 -0600
Subject: [PATCH 05/39] Create requirements.txt
---
requirements.txt | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)
create mode 100644 requirements.txt
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000..b49f6b6
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,22 @@
+beautifulsoup4==4.10.0
+certifi==2021.10.8
+cffi==1.15.0
+charset-normalizer==2.0.9
+gevent==21.12.0
+greenlet==1.1.2
+grequests==0.6.0
+idna==3.3
+keyboard==0.13.5
+Pillow==8.4.0
+pycparser==2.21
+pyperclip==1.8.2
+PyQt5==5.15.6
+PyQt5-Qt5==5.15.2
+PyQt5-sip==12.9.0
+pytesseract==0.3.8
+pywin32==303
+requests==2.26.0
+soupsieve==2.3.1
+urllib3==1.26.7
+zope.event==4.5.0
+zope.interface==5.4.0
From 4ac502963fab1eff90833ed69338545c42be0b7a Mon Sep 17 00:00:00 2001
From: daijro <72637910+daijro@users.noreply.github.com>
Date: Mon, 27 Dec 2021 21:30:53 -0600
Subject: [PATCH 06/39] Update download link
---
README.md | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/README.md b/README.md
index e100186..acfcaec 100644
--- a/README.md
+++ b/README.md
@@ -10,11 +10,15 @@
![in action](https://user-images.githubusercontent.com/72637910/147515480-236fe392-6282-44bc-b888-54f15adeb523.gif)
-
+---
## Download
-Windows binaries are not avaliable yet at this moment.
+#### Binaries
+
+Download Windows binaries [here](https://github.com/daijro/SearchifyX/releases)
+
+#### Running from source code
Install the [Python 3.8](https://www.python.org/downloads/release/python-389/) dependencies:
From 2593156c2bb0e47414f559cf47670ec74c6d0b22 Mon Sep 17 00:00:00 2001
From: daijro <72637910+daijro@users.noreply.github.com>
Date: Tue, 28 Dec 2021 14:28:44 -0600
Subject: [PATCH 07/39] Update README.md
---
README.md | 33 +++++++++++++++++++++++++++------
1 file changed, 27 insertions(+), 6 deletions(-)
diff --git a/README.md b/README.md
index acfcaec..57e387e 100644
--- a/README.md
+++ b/README.md
@@ -3,6 +3,9 @@
**SearchifyX**, predecessor to *[Searchify](https://www.reddit.com/user/daijro/comments/jg7wee/searchify_quizletbrainly_searcher/)*, is a ***fast*** Quizlet, Quizizz, and Brainly webscraper with various stealth features.
+SearchifyX lets you easily query a quiz question through different answer websites *(similar to Socratic on mobile)*, and sort them based on how similar they are to your query. It also includes a screen OCR scanner, hotkeys, and other features for stealthy use.
+
+
#### SearchifyX in action
@@ -14,11 +17,11 @@
## Download
-#### Binaries
+### Binaries
Download Windows binaries [here](https://github.com/daijro/SearchifyX/releases)
-#### Running from source code
+### Running from source code
Install the [Python 3.8](https://www.python.org/downloads/release/python-389/) dependencies:
@@ -60,14 +63,12 @@ Optional arguments:
---
-### What it does
+## What it does
- Searches Bing for Quizlet, Quizizz, and Brainly results
- Sort results by how similar the identified question is to the input question
-- Various different options for input for stealthy use
-
### Stealth options
@@ -90,9 +91,10 @@ Optional arguments:
+
### Window switching safety lock
-Many websites can detect when the window focus is lost. SearchifyX includes features such as hotkeys and a __window switching safety lock__ to prevent websites from knowing your are using a differenty window.
+Many websites can detect when the window focus is lost. SearchifyX includes a __window switching safety lock__ to prevent websites from knowing you are using a different window.
*Note: The text search bar does not work when the Window switch safety lock is enabled. It is designed to be used with the OCR tool and Paste & Search tool.*
@@ -100,6 +102,25 @@ Here is a [demo website](https://www.codingwithjesse.com/demo/2007-05-16-detect-
![window safety lock](https://i.imgur.com/mGBAV1K.gif)
+
+
+### Improvements from *Searchify*
+
+- Completely rewritten from scratch
+
+- Added stealth features ([listed above](https://github.com/daijro/SearchifyX#stealth-options))
+
+- Added screen OCR scanner
+
+- Added global hotkeys
+
+- MAJOR changes to UI
+
+- MAJOR changes web scraper code (averages 1-2 seconds now; uses significantly less CPU)
+
+- Completely fixed the "Too many requests" error
+
+- Significantly more stable & returns better results
---
From d88428a9f4293f86ee8c71422993a2a1eebd63ed Mon Sep 17 00:00:00 2001
From: daijro <72637910+daijro@users.noreply.github.com>
Date: Thu, 10 Feb 2022 21:50:58 -0600
Subject: [PATCH 08/39] Use dropbox
- Change discord cdn link for tesseract-ocr to dropbox
- Remove "quiz" from example (to not promote cheating)
---
README.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/README.md b/README.md
index 57e387e..3a00553 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,7 @@
**SearchifyX**, predecessor to *[Searchify](https://www.reddit.com/user/daijro/comments/jg7wee/searchify_quizletbrainly_searcher/)*, is a ***fast*** Quizlet, Quizizz, and Brainly webscraper with various stealth features.
-SearchifyX lets you easily query a quiz question through different answer websites *(similar to Socratic on mobile)*, and sort them based on how similar they are to your query. It also includes a screen OCR scanner, hotkeys, and other features for stealthy use.
+SearchifyX lets you easily query a question through different answer websites *(similar to Socratic on mobile)*, and sort them based on how similar they are to your query. It also includes a screen OCR scanner, hotkeys, and other features for stealthy use.
@@ -29,7 +29,7 @@ Install the [Python 3.8](https://www.python.org/downloads/release/python-389/) d
python -m pip install requirements.txt
```
-You also need to download [tesseract-ocr](https://cdn.discordapp.com/attachments/714922631693860956/925180968808087572/tesseract-ocr.7z) and extract it to the root directory to use the OCR tool.
+You also need to download [tesseract-ocr](https://www.dropbox.com/s/abuo044ayx4vlex/tesseract-ocr.7z?dl=1) and extract it to the root directory to use the OCR tool.
From 692e8fbd4bb0d3f877cd7bd5117d75537ff7eb93 Mon Sep 17 00:00:00 2001
From: daijro <72637910+daijro@users.noreply.github.com>
Date: Mon, 11 Apr 2022 02:54:24 -0500
Subject: [PATCH 09/39] Fix #2
- Fix Bing search
- Add Google and Startpage scrapers (change in ui settings)
- Fix headers + add header randomizer
- Completely remove Brainly from the project due to new WAF + slow processing time
- Fix delay in cursor restoring after OCR
- Manually calculate selection rectangle for OCR
- Change minimum value for window transparency slider to 5%
- Upgrade Pillow and pytesseract to fix security concerns
- Add requests_html, fake_headers, and their dependencies to requirements.txt
---
config.json | 2 +-
gui.pyw | 23 +++---
img/brainly.png | Bin 6933 -> 0 bytes
requirements.txt | 26 ++++++-
scraper.py | 186 ++++++++++++++++++++++++-----------------------
textshot.py | 9 ++-
window.ui | 113 ++++++++++++++++++++--------
7 files changed, 221 insertions(+), 138 deletions(-)
delete mode 100644 img/brainly.png
diff --git a/config.json b/config.json
index 7a7e025..4139f9f 100644
--- a/config.json
+++ b/config.json
@@ -1,7 +1,7 @@
{
"quizlet": true,
"quizizz": true,
- "brainly": false,
+ "search_engine": 0,
"hide_show_key": "Ctrl+D",
"ocr_key": "Ctrl+Shift+X",
"paste_key": "Ctrl+Shift+V",
diff --git a/gui.pyw b/gui.pyw
index bc92d8b..ae81d4a 100644
--- a/gui.pyw
+++ b/gui.pyw
@@ -19,7 +19,7 @@ import tkinter as tk
root = tk.Tk()
root.withdraw()
-from scraper import Searchify
+from scraper import Searchify, SearchEngine
from textshot import *
from windoweffect import WindowEffect
@@ -96,17 +96,13 @@ class UI(QMainWindow):
self.status_label = self.findChild(QtWidgets.QLabel, "status_label")
self.quizlet_button = self.findChild(QtWidgets.QPushButton, "quizlet_button")
self.quizizz_button = self.findChild(QtWidgets.QPushButton, "quizizz_button")
- self.brainly_button = self.findChild(QtWidgets.QPushButton, "brainly_button")
self.settings_button = self.findChild(QtWidgets.QPushButton, "settings_button")
self.quizlet_button.setChecked(self.conf['quizlet'])
self.quizizz_button.setChecked(self.conf['quizizz'])
- self.brainly_button.setChecked(self.conf['brainly'])
self.quizlet_button.toggled.connect(lambda: self.updatejson('quizlet'))
self.quizizz_button.toggled.connect(lambda: self.updatejson('quizizz'))
- self.brainly_button.toggled.connect(lambda: self.updatejson('brainly'))
-
self.settings_button.clicked.connect(lambda: self.stackedWidget.setCurrentIndex(1))
@@ -115,7 +111,6 @@ class UI(QMainWindow):
self.quizizz_button.setIcon(QtGui.QIcon(resource_path("img\\quizizz.png")))
self.quizlet_button.setIcon(QtGui.QIcon(resource_path("img\\quizlet.png")))
- self.brainly_button.setIcon(QtGui.QIcon(resource_path("img\\brainly.png")))
self.titleIcon.setPixmap(QtGui.QPixmap(resource_path("img\\search.png")))
@@ -203,6 +198,11 @@ class UI(QMainWindow):
self.setting_on_top.setChecked(self.conf['on_top'])
self.setting_on_top.toggled.connect(lambda: self.set_window_on_top())
+
+ self.search_engine_combo = self.findChild(QtWidgets.QComboBox, "search_engine_combo")
+ self.search_engine_combo.setCurrentIndex(self.conf['search_engine'])
+ self.search_engine = SearchEngine(self.search_engine_combo.currentText().lower())
+ self.search_engine_combo.currentIndexChanged.connect(lambda: self.run_search_engine())
# window theme
self.themeInput = self.findChild(QtWidgets.QComboBox, "themeInput")
@@ -291,7 +291,7 @@ class UI(QMainWindow):
font_size = self.font_size.value()
# icon sizes
- for obj in [self.quizizz_button, self.quizlet_button, self.brainly_button]:
+ for obj in [self.quizizz_button, self.quizlet_button]:
obj.setIconSize(QtCore.QSize(font_size*2, font_size*2))
@@ -313,6 +313,10 @@ class UI(QMainWindow):
# calling scraper and adding to ui
+ def run_search_engine(self):
+ self.search_engine = SearchEngine(self.search_engine_combo.currentText().lower())
+ self.updatejson('search_engine')
+
def run_searcher(self):
query = self.search_bar.text().strip()
@@ -328,14 +332,13 @@ class UI(QMainWindow):
if self.quizizz_button.isChecked(): sites.append('quizizz')
if self.quizlet_button.isChecked(): sites.append('quizlet')
- if self.brainly_button.isChecked(): sites.append('brainly')
if not sites:
self.status_label.setText('Please select at least one site.')
self.search_frame.setEnabled(True)
return
- searchify = Searchify(query, sites)
+ searchify = Searchify(query, sites, self.search_engine)
t = Thread(target=searchify.main)
t.daemon = True
@@ -530,7 +533,6 @@ class UI(QMainWindow):
# keybinds
"quizlet": lambda: self.quizlet_button.isChecked(),
"quizizz": lambda: self.quizizz_button.isChecked(),
- "brainly": lambda: self.brainly_button.isChecked(),
"hide_show_key": lambda: self.hide_show_key.keySequence().toString(),
"ocr_key": lambda: self.ocr_key.keySequence().toString(),
"paste_key": lambda: self.paste_key.keySequence().toString(),
@@ -550,6 +552,7 @@ class UI(QMainWindow):
"hide_taskbar": lambda: self.setting_hide_taskbar.isChecked(),
"theme": lambda: self.themeInput.currentIndex(),
"font_size": lambda: self.font_size.value(),
+ "search_engine": lambda: self.search_engine_combo.currentIndex(),
}
def updatejson(self, key):
diff --git a/img/brainly.png b/img/brainly.png
deleted file mode 100644
index 8c1df6900f3c61269080e89372a10cebe0b2aed5..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 6933
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
D#nc(I
diff --git a/requirements.txt b/requirements.txt
index b49f6b6..701b68a 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,22 +1,44 @@
+appdirs==1.4.4
beautifulsoup4==4.10.0
+bs4==0.0.1
certifi==2021.10.8
cffi==1.15.0
charset-normalizer==2.0.9
+colorama==0.4.4
+cssselect==1.1.0
+fake-headers==1.0.2
+fake-useragent==0.1.11
gevent==21.12.0
greenlet==1.1.2
grequests==0.6.0
+html5lib==1.1
idna==3.3
+importlib-metadata==4.11.3
keyboard==0.13.5
-Pillow==8.4.0
+lxml==4.8.0
+packaging==21.3
+parse==1.19.0
+Pillow==9.1.0
pycparser==2.21
+pyee==8.2.2
+pyparsing==3.0.8
pyperclip==1.8.2
+pyppeteer==1.0.2
PyQt5==5.15.6
PyQt5-Qt5==5.15.2
PyQt5-sip==12.9.0
-pytesseract==0.3.8
+pyquery==1.4.3
+pytesseract==0.3.9
pywin32==303
requests==2.26.0
+requests-html==0.10.0
+six==1.16.0
soupsieve==2.3.1
+tqdm==4.64.0
urllib3==1.26.7
+w3lib==1.22.0
+webencodings==0.5.1
+websockets==10.2
+zipp==3.8.0
zope.event==4.5.0
zope.interface==5.4.0
diff --git a/scraper.py b/scraper.py
index f923bfb..659bfed 100644
--- a/scraper.py
+++ b/scraper.py
@@ -1,71 +1,124 @@
+import grequests
import json
from bs4 import BeautifulSoup
from difflib import SequenceMatcher
import json
-import grequests
+from requests_html import HTMLSession
+from fake_headers import Headers
import re
import sys
import time
from urllib.parse import urlencode
from threading import Thread
-
headers = {
- "Connection": "keep-alive",
- 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36 Edg/95.0.1020.53',
- "Sec-Fetch-Site": "same-origin",
+ "Sec-Ch-Ua": "\"(Not(A:Brand\";v=\"8\", \"Chromium\";v=\"99\"",
+ "Sec-Ch-Ua-Mobile": "?0",
+ "Sec-Ch-Ua-Platform": "\"Windows\"",
+ "Upgrade-Insecure-Requests": "1",
+ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.74 Safari/537.36",
+ "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
+ "Sec-Fetch-Site": "none",
"Sec-Fetch-Mode": "navigate",
"Sec-Fetch-User": "?1",
"Sec-Fetch-Dest": "document",
- "Referer": "https://www.bing.com/",
- "Accept-Language": "en-US,en;q=0.9"
+ "Accept-Encoding": "gzip, deflate",
+ "Accept-Language": "en-US,en;q=0.9",
+ "Connection": "close"
}
-
get_text = lambda x: BeautifulSoup(x, features='lxml').get_text().strip()
sluggify = lambda a: ' '.join(re.sub(r'[^\\sa-z0-9\\.,\\(\\)]+', ' ', a.lower()).split())
similar = lambda a, b: SequenceMatcher(None, sluggify(a), sluggify(b)).ratio()
remove_duplicates = lambda a: list(set(a))
+def _make_headers():
+ return {**headers, **Headers(headers=True, browser='chrome', os='windows').generate()}
-class SearchBing:
+class SearchEngine:
+ headers = headers.copy()
+ def __init__(self, engine_name):
+ self.sess = HTMLSession()
+ self.engine_name = engine_name
+ self._web_engines = { # simple scrapers using get requests
+ 'google': ('https://www.google.com/search?', 'q', {'aqs': 'chrome..69i57.888j0j1', 'sourceid': 'chrome', 'ie': 'UTF-8'}),
+ 'bing': ('https://www.bing.com/search?', 'q', {'pq': ''}),
+ }
+ if engine_name in self._web_engines:
+ return
+ elif engine_name == 'startpage':
+ print('Starting startpage instance...')
+ self.t = Thread(target=self._init_startpage)
+ self.t.daemon = True
+ self.t.start()
+
+ def find_items(self, soup, args):
+ return {i: soup.find('input', {'type': 'hidden', 'name': i})['value'] for i in args}
+
+ def get_startpage_items(self, r):
+ soup = BeautifulSoup(r.text, 'lxml')
+ return {'query': None, 'cat': 'web', **self.find_items(soup, ['lui', 'language', 'sc', 'abp'])}
+
+ def _init_startpage(self):
+ self._startpage_data = self.get_startpage_items(self.sess.get('https://www.startpage.com/', headers=self.headers))
+ self.headers.update({"Sec-Fetch-Site": "same-origin", 'Referer': 'https://www.startpage.com/'})
+
+ def startpage_get_page(self, query, sites):
+ self.t.join()
+ resps = grequests.map([
+ grequests.post('https://www.startpage.com/sp/search',
+ headers=self.headers,
+ data={**self._startpage_data, **{'query': f'{query} site:{site}.com'}}
+ )
+ for site in sites
+ ])
+ self.t = Thread(target=self.get_startpage_items, args=(resps[-1],))
+ self.t.daemon = True
+ self.t.start()
+ return dict(zip(sites, resps))
+
+ def get_page(self, query, sites):
+ if self.engine_name == 'startpage':
+ return self.startpage_get_page(query, sites)
+ return dict(zip(
+ sites,
+ grequests.map([
+ grequests.get(
+ (web_engine := self._web_engines[self.engine_name])[0]
+ + urlencode({web_engine[1]: f'{query} site:{site}.com', **web_engine[2]}),
+ headers=self.headers, session=self.sess
+ )
+ for site in sites
+ ], size=len(sites))
+ ))
+
+
+class SearchWeb:
"""
- search bing for query
+ search web for query
"""
- def __init__(self, query, sites):
+ def __init__(self, query, sites, engine):
self.query = query
self.links = None
self.sites = sites
+ self.engine = engine
self._regex_objs = {
'quizlet': re.compile('https?://quizlet.com/\d+/[a-z0-9\\-]+/'),
'quizizz': re.compile('https?://quizizz.com/admin/quiz/[a-f0-9]+/[a-z\\-]+'),
- 'brainly': re.compile('https?://brainly.com/question/\d+'),
}
def search(self):
"""
- search bing for query
+ search web for query
"""
- resps = dict(zip(
- self.sites,
- grequests.map([
- grequests.get(
- 'https://www.bing.com/search?'
- + urlencode({'q': self.query + f' site:{site}.com'}),
- headers=headers,
- )
- for site in self.sites
- ], size=len(self.sites))
- ))
-
+ resps = self.engine.get_page(self.query, self.sites)
self.links = {
site: remove_duplicates(re.findall(self._regex_objs[site], resps[site].text))
for site in self.sites
}
-
class QuizizzScraper:
def __init__(self, links, query):
self.links = links
@@ -74,7 +127,7 @@ def __init__(self, links, query):
self.query = query
def async_requests(self, links):
- reqs = [grequests.get(u, headers=headers) for u in links]
+ reqs = [grequests.get(u, headers=_make_headers()) for u in links]
self.resps = grequests.map(reqs, size=len(reqs))
def parse_links(self):
@@ -136,7 +189,7 @@ def __init__(self, links, query):
self._regex_obj = re.compile('\\= \\{"alphabeticalIsDifferent.*\\}; QLoad\\(')
def async_requests(self, links):
- reqs = [grequests.get(u, headers=headers) for u in links]
+ reqs = [grequests.get(u, headers=_make_headers()) for u in links]
self.resps = grequests.map(reqs, size=len(reqs))
def parse_links(self):
@@ -170,61 +223,6 @@ def quizlet_parser(self, resp):
)
-
-class BrainlyScraper:
- def __init__(self, links, query):
- self.links = links
- self.resps = None
- self.brainlys = []
- self.query = query
-
- def async_requests(self, links):
- reqs = [grequests.get(u, headers=headers) for u in links]
- self.resps = grequests.map(reqs, size=len(reqs))
-
- def parse_links(self):
- self.async_requests(self.links)
- for resp in self.resps:
- try:
- self.brainlys.append(self.brainly_parser(resp))
- except Exception as e:
- print('exception', e, resp.url)
- # pass # skip over any errors
- return self.brainlys
-
-
- def brainly_parser(self, resp):
- data = json.loads(BeautifulSoup(resp.text, features='lxml').find('script', type="application/ld+json").string)[0]
- answers = []
- if 'acceptedAnswer' in data['mainEntity']:
- answers += data['mainEntity']['acceptedAnswer']
- if 'suggestedAnswer' in data['mainEntity']:
- answers += data['mainEntity']['suggestedAnswer']
-
- return max(
- (
- {
- 'question': data['name'].strip(),
- 'answer': get_text(i['text'])
- .replace('Answer:', 'Answer: ')
- .replace('Explanation:', '\nExplanation: ')
- + '\nUpvotes: '
- + str(i['upvoteCount']),
- 'similarity': (
- similar(data['name'], self.query),
- True,
- i['upvoteCount'],
- ),
- 'url': resp.url,
- }
- for i in answers
- ),
- key=lambda x: x['similarity'],
- )
-
-
-
-
class TimeLogger:
def __init__(self):
self.elapsed_total = time.time()
@@ -261,19 +259,17 @@ def print_timers(self):
-
-
class Searchify:
- def __init__(self, query, sites):
+ def __init__(self, query, sites, engine):
self.query = query
self.sites = sites
+ self.engine = engine
self.timer = TimeLogger()
self.flashcards = []
self.links = []
self.site_scrapers = {
'quizlet': QuizletScraper,
'quizizz': QuizizzScraper,
- 'brainly': BrainlyScraper,
}
def main(self):
@@ -306,8 +302,8 @@ def _flashcard_thread(self, site_scraper, links, site_name):
def get_links(self):
- self.timer.start('bing search')
- search_bing = SearchBing(self.query, self.sites)
+ self.timer.start('web search')
+ search_bing = SearchWeb(self.query, self.sites, self.engine)
search_bing.search()
self.timer.end()
self.links = search_bing.links
@@ -328,10 +324,11 @@ def sort_flashcards(self): # sourcery skip: for-index-replacement
if __name__ == '__main__' and len(sys.argv) > 1:
# argument parsing
import argparse
- parser = argparse.ArgumentParser(description='Search Bing for flashcards')
+ parser = argparse.ArgumentParser(description='Search the web for flashcards')
parser.add_argument('--query', '-q', help='query to search for', default=None)
parser.add_argument('--output', '-o', help='output file', default=None)
- parser.add_argument('--sites', '-s', help='question sources quizlet,quizizz,brainly (comma seperated list)', default='quizlet,quizizz,brainly')
+ parser.add_argument('--sites', '-s', help='question sources quizlet,quizizz (comma seperated list)', default='quizlet,quizizz')
+ parser.add_argument('--engine', '-e', help='search engine to use (google, bing)', default='bing')
args = parser.parse_args()
if args.output:
@@ -348,15 +345,20 @@ def sort_flashcards(self): # sourcery skip: for-index-replacement
flashcards = [] # create flashcard list
sites = args.sites.lower().split(',') # get list of sites
+ engine_name = args.engine.lower().strip() # get search engine
+ # start search engine
+ engine = SearchEngine(engine_name)
+
# run search
s = Searchify(
query=args.query,
sites=sites,
+ engine=engine,
)
s.main()
write(json.dumps(s.flashcards, indent=4))
- print(str(len(s.flashcards))+ ' flashcards found')
+ print(f'{len(s.flashcards)} flashcards found')
s.timer.print_timers()
\ No newline at end of file
diff --git a/textshot.py b/textshot.py
index 1c215e0..cae8cc3 100644
--- a/textshot.py
+++ b/textshot.py
@@ -134,8 +134,15 @@ def mouseReleaseEvent(self, event):
return super().mouseReleaseEvent(event)
self.hide()
+ QtWidgets.QApplication.restoreOverrideCursor()
QtWidgets.QApplication.processEvents()
- shot = self.screen.copy(QtCore.QRect(self.start, self.end))
+
+ shot = self.screen.copy(
+ min(self.start.x(), self.end.x()),
+ min(self.start.y(), self.end.y()),
+ abs(self.start.x() - self.end.x()),
+ abs(self.start.y() - self.end.y()),
+ )
self.processImage(shot)
self.quit_app()
print('done')
diff --git a/window.ui b/window.ui
index ae39845..9473920 100644
--- a/window.ui
+++ b/window.ui
@@ -204,28 +204,6 @@
- -
-
-
-
- 30
- 30
-
-
-
- Brainly
-
-
- true
-
-
- false
-
-
- true
-
-
-
-
@@ -404,9 +382,9 @@
0
- 0
- 379
- 631
+ -77
+ 656
+ 465
@@ -775,26 +753,97 @@
true
-
-
-
+
-
+
- Search after running OCR
+ Search after pasting
true
- -
-
+
-
+
- Search after pasting
+ Search after running OCR
true
+ -
+
+
-
+
+
+
+ 150
+ 0
+
+
+
-
+
+ Bing
+
+
+ -
+
+ Google
+
+
+ -
+
+ Startpage
+
+
+
+
+ -
+
+
+
+ 100
+ 0
+
+
+
+ Search engine:
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QSizePolicy::Fixed
+
+
+
+ 70
+ 20
+
+
+
+
+
+
@@ -972,7 +1021,7 @@
Window transparency
- 15
+ 5
100
From cee78c51529bc566714cd0439a810c2e2451cf63 Mon Sep 17 00:00:00 2001
From: kuro <72637910+daijro@users.noreply.github.com>
Date: Wed, 13 Apr 2022 22:22:45 -0500
Subject: [PATCH 10/39] Fix typo
Add -r parameter
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 3a00553..1ab8144 100644
--- a/README.md
+++ b/README.md
@@ -26,7 +26,7 @@ Download Windows binaries [here](https://github.com/daijro/SearchifyX/releases)
Install the [Python 3.8](https://www.python.org/downloads/release/python-389/) dependencies:
```
-python -m pip install requirements.txt
+python -m pip install -r requirements.txt
```
You also need to download [tesseract-ocr](https://www.dropbox.com/s/abuo044ayx4vlex/tesseract-ocr.7z?dl=1) and extract it to the root directory to use the OCR tool.
From 22fee1ec05a4053b3efb84e72d703dd679c46038 Mon Sep 17 00:00:00 2001
From: Jose <48543854+LavamasterYT@users.noreply.github.com>
Date: Sat, 3 Sep 2022 19:09:34 -0500
Subject: [PATCH 11/39] Add macOS support
---
.gitignore | 30 ++
macOS/README.md | 20 +
macOS/SearchifyX.xcodeproj/project.pbxproj | 351 ++++++++++++++++++
.../contents.xcworkspacedata | 7 +
.../xcshareddata/IDEWorkspaceChecks.plist | 8 +
.../UserInterfaceState.xcuserstate | Bin 0 -> 44306 bytes
.../AccentColor.colorset/Contents.json | 11 +
.../AppIcon.appiconset/1024.png | Bin 0 -> 51753 bytes
.../AppIcon.appiconset/Contents.json | 68 ++++
.../Icon-MacOS-128x128@1x.png | Bin 0 -> 6378 bytes
.../Icon-MacOS-128x128@2x.png | Bin 0 -> 12874 bytes
.../Icon-MacOS-16x16@1x.png | Bin 0 -> 578 bytes
.../Icon-MacOS-16x16@2x.png | Bin 0 -> 1261 bytes
.../Icon-MacOS-256x256@1x.png | Bin 0 -> 12888 bytes
.../Icon-MacOS-256x256@2x.png | Bin 0 -> 29707 bytes
.../Icon-MacOS-32x32@1x.png | Bin 0 -> 1279 bytes
.../Icon-MacOS-32x32@2x.png | Bin 0 -> 2801 bytes
.../Icon-MacOS-512x512@1x.png | Bin 0 -> 29351 bytes
.../SearchifyX/Assets.xcassets/Contents.json | 6 +
.../QuizizzIcon.imageset/Contents.json | 21 ++
.../QuizizzIcon.imageset/quizizz.png | Bin 0 -> 64690 bytes
.../AppLogo-Quizlet copy.png | Bin 0 -> 43220 bytes
.../QuizletIcon.imageset/Contents.json | 21 ++
macOS/SearchifyX/ContentView.swift | 119 ++++++
macOS/SearchifyX/Flashcard.swift | 16 +
macOS/SearchifyX/Scraper.swift | 40 ++
macOS/SearchifyX/SearchifyXApp.swift | 17 +
macOS/build.sh | 16 +
macOS/clean.sh | 7 +
macOS/requirements.txt | 6 +
30 files changed, 764 insertions(+)
create mode 100644 macOS/README.md
create mode 100644 macOS/SearchifyX.xcodeproj/project.pbxproj
create mode 100644 macOS/SearchifyX.xcodeproj/project.xcworkspace/contents.xcworkspacedata
create mode 100644 macOS/SearchifyX.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
create mode 100644 macOS/SearchifyX.xcodeproj/project.xcworkspace/xcuserdata/josem.xcuserdatad/UserInterfaceState.xcuserstate
create mode 100644 macOS/SearchifyX/Assets.xcassets/AccentColor.colorset/Contents.json
create mode 100644 macOS/SearchifyX/Assets.xcassets/AppIcon.appiconset/1024.png
create mode 100644 macOS/SearchifyX/Assets.xcassets/AppIcon.appiconset/Contents.json
create mode 100644 macOS/SearchifyX/Assets.xcassets/AppIcon.appiconset/Icon-MacOS-128x128@1x.png
create mode 100644 macOS/SearchifyX/Assets.xcassets/AppIcon.appiconset/Icon-MacOS-128x128@2x.png
create mode 100644 macOS/SearchifyX/Assets.xcassets/AppIcon.appiconset/Icon-MacOS-16x16@1x.png
create mode 100644 macOS/SearchifyX/Assets.xcassets/AppIcon.appiconset/Icon-MacOS-16x16@2x.png
create mode 100644 macOS/SearchifyX/Assets.xcassets/AppIcon.appiconset/Icon-MacOS-256x256@1x.png
create mode 100644 macOS/SearchifyX/Assets.xcassets/AppIcon.appiconset/Icon-MacOS-256x256@2x.png
create mode 100644 macOS/SearchifyX/Assets.xcassets/AppIcon.appiconset/Icon-MacOS-32x32@1x.png
create mode 100644 macOS/SearchifyX/Assets.xcassets/AppIcon.appiconset/Icon-MacOS-32x32@2x.png
create mode 100644 macOS/SearchifyX/Assets.xcassets/AppIcon.appiconset/Icon-MacOS-512x512@1x.png
create mode 100644 macOS/SearchifyX/Assets.xcassets/Contents.json
create mode 100644 macOS/SearchifyX/Assets.xcassets/QuizizzIcon.imageset/Contents.json
create mode 100644 macOS/SearchifyX/Assets.xcassets/QuizizzIcon.imageset/quizizz.png
create mode 100644 macOS/SearchifyX/Assets.xcassets/QuizletIcon.imageset/AppLogo-Quizlet copy.png
create mode 100644 macOS/SearchifyX/Assets.xcassets/QuizletIcon.imageset/Contents.json
create mode 100644 macOS/SearchifyX/ContentView.swift
create mode 100644 macOS/SearchifyX/Flashcard.swift
create mode 100644 macOS/SearchifyX/Scraper.swift
create mode 100644 macOS/SearchifyX/SearchifyXApp.swift
create mode 100755 macOS/build.sh
create mode 100755 macOS/clean.sh
create mode 100644 macOS/requirements.txt
diff --git a/.gitignore b/.gitignore
index 623bd3a..1afdaae 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,3 +2,33 @@ tesseract-ocr/
# tesserect-ocr needs to be redownloaded from link provided in README.md
__pycache__/
windoweffect/__pycache__/
+
+# scraper.app must be rebuilt because of the amount of files
+macOS/SearchifyX/scraper.app
+
+# General
+.DS_Store
+.AppleDouble
+.LSOverride
+
+# Icon must end with two \r
+Icon
+
+# Thumbnails
+._*
+
+# Files that might appear in the root of a volume
+.DocumentRevisions-V100
+.fseventsd
+.Spotlight-V100
+.TemporaryItems
+.Trashes
+.VolumeIcon.icns
+.com.apple.timemachine.donotpresent
+
+# Directories potentially created on remote AFP share
+.AppleDB
+.AppleDesktop
+Network Trash Folder
+Temporary Items
+.apdisk
diff --git a/macOS/README.md b/macOS/README.md
new file mode 100644
index 0000000..4f49865
--- /dev/null
+++ b/macOS/README.md
@@ -0,0 +1,20 @@
+# SearchifyX for macOS
+
+There now exists a native Swift app for macOS! It still uses Python for the scraping but the front-end/GUI is now native.
+The only downsides to this version is that so far the stealth features are not available in this version.
+
+## Building
+
+To build this version of SearchifyX, you will need to only install the dependencies required for this version of SearchifyX listed in the [requirements.txt]() file:
+```
+$ python3 -m pip install -r requirements.txt
+```
+
+Then just run the build script from the macOS directory to build the latest version of [scraper.py]():
+```
+$ ./build.sh
+```
+> You may need to give the build and clean scripts the proper permissions to execute
+
+That will build and compile the macOS app of scraper.py.
+Now you can launch the Xcode project and build from there!
\ No newline at end of file
diff --git a/macOS/SearchifyX.xcodeproj/project.pbxproj b/macOS/SearchifyX.xcodeproj/project.pbxproj
new file mode 100644
index 0000000..66847a2
--- /dev/null
+++ b/macOS/SearchifyX.xcodeproj/project.pbxproj
@@ -0,0 +1,351 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 55;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ 4214674528C1C5EF00E9D706 /* SearchifyXApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4214674428C1C5EF00E9D706 /* SearchifyXApp.swift */; };
+ 4214674728C1C5EF00E9D706 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4214674628C1C5EF00E9D706 /* ContentView.swift */; };
+ 4214674928C1C5F200E9D706 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4214674828C1C5F200E9D706 /* Assets.xcassets */; };
+ 4214677028C1CFB600E9D706 /* Flashcard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4214676F28C1CFB500E9D706 /* Flashcard.swift */; };
+ 4214677528C1D99300E9D706 /* Scraper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4214677428C1D99300E9D706 /* Scraper.swift */; };
+ 4214677928C2887F00E9D706 /* scraper.app in Resources */ = {isa = PBXBuildFile; fileRef = 4214677828C2887F00E9D706 /* scraper.app */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXFileReference section */
+ 4214674128C1C5EF00E9D706 /* SearchifyX.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SearchifyX.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ 4214674428C1C5EF00E9D706 /* SearchifyXApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchifyXApp.swift; sourceTree = ""; };
+ 4214674628C1C5EF00E9D706 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; };
+ 4214674828C1C5F200E9D706 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
+ 4214676F28C1CFB500E9D706 /* Flashcard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Flashcard.swift; sourceTree = ""; };
+ 4214677428C1D99300E9D706 /* Scraper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Scraper.swift; sourceTree = ""; };
+ 4214677828C2887F00E9D706 /* scraper.app */ = {isa = PBXFileReference; lastKnownFileType = wrapper.application; path = scraper.app; sourceTree = ""; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 4214673E28C1C5EF00E9D706 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 4214673828C1C5EF00E9D706 = {
+ isa = PBXGroup;
+ children = (
+ 4214674328C1C5EF00E9D706 /* SearchifyX */,
+ 4214674228C1C5EF00E9D706 /* Products */,
+ );
+ sourceTree = "";
+ };
+ 4214674228C1C5EF00E9D706 /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 4214674128C1C5EF00E9D706 /* SearchifyX.app */,
+ );
+ name = Products;
+ sourceTree = "";
+ };
+ 4214674328C1C5EF00E9D706 /* SearchifyX */ = {
+ isa = PBXGroup;
+ children = (
+ 4214677828C2887F00E9D706 /* scraper.app */,
+ 4214674428C1C5EF00E9D706 /* SearchifyXApp.swift */,
+ 4214674628C1C5EF00E9D706 /* ContentView.swift */,
+ 4214674828C1C5F200E9D706 /* Assets.xcassets */,
+ 4214676F28C1CFB500E9D706 /* Flashcard.swift */,
+ 4214677428C1D99300E9D706 /* Scraper.swift */,
+ );
+ path = SearchifyX;
+ sourceTree = "";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ 4214674028C1C5EF00E9D706 /* SearchifyX */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 4214676628C1C5F200E9D706 /* Build configuration list for PBXNativeTarget "SearchifyX" */;
+ buildPhases = (
+ 4214673D28C1C5EF00E9D706 /* Sources */,
+ 4214673E28C1C5EF00E9D706 /* Frameworks */,
+ 4214673F28C1C5EF00E9D706 /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = SearchifyX;
+ packageProductDependencies = (
+ );
+ productName = SearchifyX;
+ productReference = 4214674128C1C5EF00E9D706 /* SearchifyX.app */;
+ productType = "com.apple.product-type.application";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 4214673928C1C5EF00E9D706 /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ BuildIndependentTargetsInParallel = 1;
+ LastSwiftUpdateCheck = 1340;
+ LastUpgradeCheck = 1340;
+ TargetAttributes = {
+ 4214674028C1C5EF00E9D706 = {
+ CreatedOnToolsVersion = 13.4.1;
+ };
+ };
+ };
+ buildConfigurationList = 4214673C28C1C5EF00E9D706 /* Build configuration list for PBXProject "SearchifyX" */;
+ compatibilityVersion = "Xcode 13.0";
+ developmentRegion = en;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ Base,
+ );
+ mainGroup = 4214673828C1C5EF00E9D706;
+ packageReferences = (
+ );
+ productRefGroup = 4214674228C1C5EF00E9D706 /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 4214674028C1C5EF00E9D706 /* SearchifyX */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ 4214673F28C1C5EF00E9D706 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 4214677928C2887F00E9D706 /* scraper.app in Resources */,
+ 4214674928C1C5F200E9D706 /* Assets.xcassets in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 4214673D28C1C5EF00E9D706 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 4214677528C1D99300E9D706 /* Scraper.swift in Sources */,
+ 4214677028C1CFB600E9D706 /* Flashcard.swift in Sources */,
+ 4214674728C1C5EF00E9D706 /* ContentView.swift in Sources */,
+ 4214674528C1C5EF00E9D706 /* SearchifyXApp.swift in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin XCBuildConfiguration section */
+ 4214676428C1C5F200E9D706 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_ENABLE_OBJC_WEAK = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ CODE_SIGN_ALLOW_ENTITLEMENTS_MODIFICATION = YES;
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = dwarf;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_TESTABILITY = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ MACOSX_DEPLOYMENT_TARGET = 12.3;
+ MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
+ MTL_FAST_MATH = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ SDKROOT = macosx;
+ SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ };
+ name = Debug;
+ };
+ 4214676528C1C5F200E9D706 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_ENABLE_OBJC_WEAK = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ CODE_SIGN_ALLOW_ENTITLEMENTS_MODIFICATION = YES;
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ MACOSX_DEPLOYMENT_TARGET = 12.3;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ MTL_FAST_MATH = YES;
+ SDKROOT = macosx;
+ SWIFT_COMPILATION_MODE = wholemodule;
+ SWIFT_OPTIMIZATION_LEVEL = "-O";
+ };
+ name = Release;
+ };
+ 4214676728C1C5F200E9D706 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
+ ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES;
+ CODE_SIGN_ENTITLEMENTS = SearchifyX/SearchifyX.entitlements;
+ CODE_SIGN_STYLE = Automatic;
+ COMBINE_HIDPI_IMAGES = YES;
+ CURRENT_PROJECT_VERSION = 1;
+ DEVELOPMENT_ASSET_PATHS = SearchifyX/scraper.app;
+ DEVELOPMENT_TEAM = R5T6626VHW;
+ ENABLE_HARDENED_RUNTIME = YES;
+ ENABLE_PREVIEWS = YES;
+ GENERATE_INFOPLIST_FILE = YES;
+ INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.education";
+ INFOPLIST_KEY_NSHumanReadableCopyright = "";
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/../Frameworks",
+ );
+ MACOSX_DEPLOYMENT_TARGET = 12.0;
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = com.teamaurous.SearchifyX;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_EMIT_LOC_STRINGS = YES;
+ SWIFT_VERSION = 5.0;
+ };
+ name = Debug;
+ };
+ 4214676828C1C5F200E9D706 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
+ ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES;
+ CODE_SIGN_ENTITLEMENTS = SearchifyX/SearchifyX.entitlements;
+ CODE_SIGN_STYLE = Automatic;
+ COMBINE_HIDPI_IMAGES = YES;
+ CURRENT_PROJECT_VERSION = 1;
+ DEVELOPMENT_ASSET_PATHS = SearchifyX/scraper.app;
+ DEVELOPMENT_TEAM = R5T6626VHW;
+ ENABLE_HARDENED_RUNTIME = YES;
+ ENABLE_PREVIEWS = YES;
+ GENERATE_INFOPLIST_FILE = YES;
+ INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.education";
+ INFOPLIST_KEY_NSHumanReadableCopyright = "";
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/../Frameworks",
+ );
+ MACOSX_DEPLOYMENT_TARGET = 12.0;
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = com.teamaurous.SearchifyX;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_EMIT_LOC_STRINGS = YES;
+ SWIFT_VERSION = 5.0;
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 4214673C28C1C5EF00E9D706 /* Build configuration list for PBXProject "SearchifyX" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 4214676428C1C5F200E9D706 /* Debug */,
+ 4214676528C1C5F200E9D706 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 4214676628C1C5F200E9D706 /* Build configuration list for PBXNativeTarget "SearchifyX" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 4214676728C1C5F200E9D706 /* Debug */,
+ 4214676828C1C5F200E9D706 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = 4214673928C1C5EF00E9D706 /* Project object */;
+}
diff --git a/macOS/SearchifyX.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/macOS/SearchifyX.xcodeproj/project.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000..919434a
--- /dev/null
+++ b/macOS/SearchifyX.xcodeproj/project.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/macOS/SearchifyX.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/macOS/SearchifyX.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 0000000..18d9810
--- /dev/null
+++ b/macOS/SearchifyX.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+
+
+
+
+ IDEDidComputeMac32BitWarning
+
+
+
diff --git a/macOS/SearchifyX.xcodeproj/project.xcworkspace/xcuserdata/josem.xcuserdatad/UserInterfaceState.xcuserstate b/macOS/SearchifyX.xcodeproj/project.xcworkspace/xcuserdata/josem.xcuserdatad/UserInterfaceState.xcuserstate
new file mode 100644
index 0000000000000000000000000000000000000000..7a188d60987d7b3b01776033c541a56abdd42efb
GIT binary patch
literal 44306
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
z
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∾bGyU!^eilg~x~6!;`{O!qdX5!|TIO55Fe7FMLz@
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
Mt^fM}>%77L1HuDS-2eap
literal 0
HcmV?d00001
diff --git a/macOS/SearchifyX/Assets.xcassets/AccentColor.colorset/Contents.json b/macOS/SearchifyX/Assets.xcassets/AccentColor.colorset/Contents.json
new file mode 100644
index 0000000..eb87897
--- /dev/null
+++ b/macOS/SearchifyX/Assets.xcassets/AccentColor.colorset/Contents.json
@@ -0,0 +1,11 @@
+{
+ "colors" : [
+ {
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/macOS/SearchifyX/Assets.xcassets/AppIcon.appiconset/1024.png b/macOS/SearchifyX/Assets.xcassets/AppIcon.appiconset/1024.png
new file mode 100644
index 0000000000000000000000000000000000000000..ce352d1c77278edfb2f801fc03728831f3bdc997
GIT binary patch
literal 51753
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