mirror of
https://github.com/khoaliber/khoj.git
synced 2026-03-02 21:19:12 +00:00
Fix Image Search and Improve Desktop App
### Fix Image Search
- Do not use XMP metadata by default for image search
- It seems to be buggy currently. The returned results do not make sense with XMP metadata enabled
### Fix Image Search using Desktop App
- Fix configuring Image Search via Desktop GUI
- Set `input-directories`, instead of unused `input-files` for `content-type.image` in `khoj.yml`
- Fix running Image Search via Desktop apps.
- Previously the transformers wasn't getting packaged into the app by pyinstaller
- This is required by image search to run. So the desktop apps would fail to start when image search was enabled
- Resolves #68
- Append selected files, directories via "Add" button in Desktop GUI
- This allows selecting multiple files, directories using Desktop GUI
- Previously selecting multiple image directories had to be entered manually
### Improve Desktop App
- Show Splash Screen to Desktop on App Initialization
- The app takes a while to load during first run
- A splash screen signals that app is loading and not being unresponsive
- Note: _Pyinstaller only supports splash screens on Windows, Linux. Not on Macs._
- Add Khoj icon to the Windows, Linux app. Windows expects a `.ico` icon type
- Only exclude `libtorch_{cuda, cpu, python}` on Linux machine
- Seems those libraries are being used on Mac (and maybe Windows).
- Linux is where the app size benefits from removing these is maximum anyway
- Fix PyInstaller Warnings on App Start
- The warning show up as annoying error popups on Windows
This commit is contained in:
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@@ -35,7 +35,7 @@ jobs:
|
||||
shell: bash
|
||||
run: |
|
||||
if [ "$RUNNER_OS" == "Linux" ]; then
|
||||
sudo apt install libegl1 libxcb-xinerama0 -y
|
||||
sudo apt install libegl1 libxcb-xinerama0 python3-tk -y
|
||||
fi
|
||||
python -m pip install --upgrade pip
|
||||
pip install pyinstaller
|
||||
|
||||
101
Khoj.spec
101
Khoj.spec
@@ -2,8 +2,12 @@
|
||||
from os.path import join
|
||||
from platform import system
|
||||
from PyInstaller.utils.hooks import copy_metadata
|
||||
import sysconfig
|
||||
|
||||
datas = [('src/interface/web', 'src/interface/web')]
|
||||
datas = [
|
||||
('src/interface/web', 'src/interface/web'),
|
||||
(f'{sysconfig.get_paths()["purelib"]}/transformers', 'transformers')
|
||||
]
|
||||
datas += copy_metadata('tqdm')
|
||||
datas += copy_metadata('regex')
|
||||
datas += copy_metadata('requests')
|
||||
@@ -12,10 +16,8 @@ datas += copy_metadata('filelock')
|
||||
datas += copy_metadata('numpy')
|
||||
datas += copy_metadata('tokenizers')
|
||||
|
||||
|
||||
block_cipher = None
|
||||
|
||||
|
||||
a = Analysis(
|
||||
['src/main.py'],
|
||||
pathex=[],
|
||||
@@ -32,41 +34,78 @@ a = Analysis(
|
||||
noarchive=False,
|
||||
)
|
||||
|
||||
# Filter out unused, duplicate shared libs
|
||||
extension = {'Windows': '.dll', 'Darwin': '.dylib', 'Linux': '.so'}[system()]
|
||||
# Filter out unused and/or duplicate shared libs
|
||||
torch_lib_paths = {
|
||||
join('torch', 'lib', 'libtorch_cuda' + extension),
|
||||
join('torch', 'lib', 'libtorch_cpu' + extension),
|
||||
join('torch', 'lib', 'libtorch_python' + extension)
|
||||
join('torch', 'lib', 'libtorch_cuda.so'),
|
||||
join('torch', 'lib', 'libtorch_cpu.so'),
|
||||
}
|
||||
a.datas = [entry for entry in a.datas if not entry[0] in torch_lib_paths]
|
||||
|
||||
a.datas = [entry for entry in a.datas if not 'torch/_C.cp' in entry[0]]
|
||||
a.datas = [entry for entry in a.datas if not 'torch/_dl.cp' in entry[0]]
|
||||
|
||||
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
|
||||
|
||||
exe = EXE(
|
||||
pyz,
|
||||
a.scripts,
|
||||
a.binaries,
|
||||
a.zipfiles,
|
||||
a.datas,
|
||||
[],
|
||||
name='Khoj',
|
||||
debug=False,
|
||||
bootloader_ignore_signals=False,
|
||||
strip=False,
|
||||
upx=True,
|
||||
upx_exclude=[],
|
||||
runtime_tmpdir=None,
|
||||
console=False,
|
||||
disable_windowed_traceback=False,
|
||||
argv_emulation=False,
|
||||
target_arch='x86_64',
|
||||
codesign_identity=None,
|
||||
entitlements_file=None,
|
||||
icon='src/interface/web/assets/icons/favicon.icns',
|
||||
)
|
||||
if system() != 'Darwin':
|
||||
# Add Splash screen to show on app launch
|
||||
splash = Splash(
|
||||
'src/interface/web/assets/icons/favicon-144x144.png',
|
||||
binaries=a.binaries,
|
||||
datas=a.datas,
|
||||
text_pos=(10, 50),
|
||||
text_size=12,
|
||||
text_color='blue',
|
||||
minify_script=True,
|
||||
always_on_top=True
|
||||
)
|
||||
|
||||
if system() == 'Darwin':
|
||||
exe = EXE(
|
||||
pyz,
|
||||
a.scripts,
|
||||
a.binaries,
|
||||
a.zipfiles,
|
||||
a.datas,
|
||||
splash,
|
||||
splash.binaries,
|
||||
[],
|
||||
name='Khoj',
|
||||
debug=False,
|
||||
bootloader_ignore_signals=False,
|
||||
strip=False,
|
||||
upx=True,
|
||||
upx_exclude=[],
|
||||
runtime_tmpdir=None,
|
||||
console=False,
|
||||
disable_windowed_traceback=False,
|
||||
argv_emulation=False,
|
||||
target_arch='x86_64',
|
||||
codesign_identity=None,
|
||||
entitlements_file=None,
|
||||
icon='src/interface/web/assets/icons/favicon-144x144.ico',
|
||||
)
|
||||
else:
|
||||
exe = EXE(
|
||||
pyz,
|
||||
a.scripts,
|
||||
a.binaries,
|
||||
a.zipfiles,
|
||||
a.datas,
|
||||
[],
|
||||
name='Khoj',
|
||||
debug=False,
|
||||
bootloader_ignore_signals=False,
|
||||
strip=False,
|
||||
upx=True,
|
||||
upx_exclude=[],
|
||||
runtime_tmpdir=None,
|
||||
console=False,
|
||||
disable_windowed_traceback=False,
|
||||
argv_emulation=False,
|
||||
target_arch='x86_64',
|
||||
codesign_identity=None,
|
||||
entitlements_file=None,
|
||||
icon='src/interface/web/assets/icons/favicon.icns',
|
||||
)
|
||||
app = BUNDLE(
|
||||
exe,
|
||||
name='Khoj.app',
|
||||
|
||||
@@ -22,7 +22,7 @@ content-type:
|
||||
input-filter: # /path/to/images/*.jpg REQUIRED IF input-directories IS NOT SET
|
||||
embeddings-file: "~/.khoj/content/image/image_embeddings.pt"
|
||||
batch-size: 50
|
||||
use-xmp-metadata: true
|
||||
use-xmp-metadata: false
|
||||
|
||||
music:
|
||||
input-files: # ["/path/to/music-file.org"] REQUIRED IF input-filter IS NOT SET OR
|
||||
|
||||
@@ -29,7 +29,7 @@ class FileBrowser(QtWidgets.QWidget):
|
||||
self.lineEdit.textChanged.connect(self.updateFieldHeight)
|
||||
layout.addWidget(self.lineEdit)
|
||||
|
||||
self.button = QtWidgets.QPushButton('Select')
|
||||
self.button = QtWidgets.QPushButton('Add')
|
||||
self.button.clicked.connect(self.storeFilesSelectedInFileDialog)
|
||||
layout.addWidget(self.button)
|
||||
layout.addStretch()
|
||||
@@ -47,7 +47,7 @@ class FileBrowser(QtWidgets.QWidget):
|
||||
return 'Images (*.jp[e]g)'
|
||||
|
||||
def storeFilesSelectedInFileDialog(self):
|
||||
filepaths = []
|
||||
filepaths = self.getPaths()
|
||||
if self.search_type == SearchType.Image:
|
||||
filepaths.append(QtWidgets.QFileDialog.getExistingDirectory(self, caption='Choose Folder',
|
||||
directory=self.dirpath))
|
||||
|
||||
@@ -185,7 +185,10 @@ class MainWindow(QtWidgets.QMainWindow):
|
||||
default_search_config = self.get_default_config(search_type = child.search_type)
|
||||
self.new_config['content-type'][child.search_type.value] = merge_dicts(current_search_config, default_search_config)
|
||||
elif isinstance(child, FileBrowser) and child.search_type in self.new_config['content-type']:
|
||||
self.new_config['content-type'][child.search_type.value]['input-files'] = child.getPaths() if child.getPaths() != [] else None
|
||||
if child.search_type.value == SearchType.Image:
|
||||
self.new_config['content-type'][child.search_type.value]['input-directories'] = child.getPaths() if child.getPaths() != [] else None
|
||||
else:
|
||||
self.new_config['content-type'][child.search_type.value]['input-files'] = child.getPaths() if child.getPaths() != [] else None
|
||||
|
||||
def update_processor_settings(self):
|
||||
"Update config with conversation settings from UI"
|
||||
|
||||
BIN
src/interface/web/assets/icons/favicon-144x144.ico
Normal file
BIN
src/interface/web/assets/icons/favicon-144x144.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 159 KiB |
18
src/main.py
18
src/main.py
@@ -1,7 +1,8 @@
|
||||
# Standard Packages
|
||||
from platform import system
|
||||
import os
|
||||
import signal
|
||||
import sys
|
||||
from platform import system
|
||||
|
||||
# External Packages
|
||||
import uvicorn
|
||||
@@ -26,6 +27,9 @@ app.include_router(router)
|
||||
|
||||
|
||||
def run():
|
||||
# Turn Tokenizers Parallelism Off. App does not support it.
|
||||
os.environ["TOKENIZERS_PARALLELISM"] = 'false'
|
||||
|
||||
# Load config from CLI
|
||||
state.cli_args = sys.argv[1:]
|
||||
args = cli(state.cli_args)
|
||||
@@ -67,6 +71,18 @@ def run():
|
||||
# Start Application
|
||||
server.start()
|
||||
gui.aboutToQuit.connect(server.terminate)
|
||||
|
||||
# Close Splash Screen if still open
|
||||
if system() != 'Darwin':
|
||||
try:
|
||||
import pyi_splash
|
||||
# Update the text on the splash screen
|
||||
pyi_splash.update_text("Khoj setup complete")
|
||||
# Close Splash Screen
|
||||
pyi_splash.close()
|
||||
except:
|
||||
pass
|
||||
|
||||
gui.exec()
|
||||
|
||||
|
||||
|
||||
@@ -211,9 +211,9 @@ def collate_results(hits, image_names, output_directory, image_files_url, count=
|
||||
# Add the image metadata to the results
|
||||
results += [{
|
||||
"entry": f'{image_files_url}/{target_image_name}',
|
||||
"score": f"{hit['score']:.3f}",
|
||||
"image_score": f"{hit['image_score']:.3f}",
|
||||
"metadata_score": f"{hit['metadata_score']:.3f}",
|
||||
"score": f"{hit['score']:.9f}",
|
||||
"image_score": f"{hit['image_score']:.9f}",
|
||||
"metadata_score": f"{hit['metadata_score']:.9f}",
|
||||
}]
|
||||
|
||||
return results
|
||||
|
||||
@@ -3,7 +3,7 @@ import argparse
|
||||
import pathlib
|
||||
|
||||
# Internal Packages
|
||||
from src.utils.helpers import get_absolute_path, resolve_absolute_path
|
||||
from src.utils.helpers import resolve_absolute_path
|
||||
from src.utils.yaml import parse_config_from_file
|
||||
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ default_config = {
|
||||
'input-filter': None,
|
||||
'embeddings-file': '~/.khoj/content/image/image_embeddings.pt',
|
||||
'batch-size': 50,
|
||||
'use-xmp-metadata': True
|
||||
'use-xmp-metadata': False
|
||||
},
|
||||
'music': {
|
||||
'input-files': None,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
# Standard Packages
|
||||
import pathlib
|
||||
import sys
|
||||
from os.path import join
|
||||
|
||||
|
||||
@@ -54,4 +55,9 @@ def load_model(model_name, model_dir, model_type):
|
||||
if model_path is not None:
|
||||
model.save(model_path)
|
||||
|
||||
return model
|
||||
return model
|
||||
|
||||
|
||||
def is_pyinstaller_app():
|
||||
"Returns true if the app is running from Native GUI created by PyInstaller"
|
||||
return getattr(sys, 'frozen', False) and hasattr(sys, '_MEIPASS')
|
||||
Reference in New Issue
Block a user