This commit is contained in:
Nathan Rashleigh committed 2024-04-07 14:04:44 +10:00
1 parent db6918fa6c
commit 43966f91f4
6 files changed
+164 -18

No files matched your search

+56
View File
@@ -0,0 +1,56 @@
---
services:
pg:
image: postgres:latest
environment:
POSTGRES_USER: '${POSTGRES_USER}'
POSTGRES_DB: '${POSTGRES_DB}'
POSTGRES_PASSWORD: '${POSTGRES_PASSWORD}'
ports:
- '5432:5432'
volumes:
- "./data/pg:/var/lib/postgresql/data"
redis:
image: redis:latest
ports:
- '6379:6379'
command: ['redis-server', '/usr/local/etc/redis/redis.conf']
volumes:
- "./data/redis:/data"
- "./redis.conf:/usr/local/etc/redis/redis.conf"
meili:
image: getmeili/meilisearch:v1.7
ports:
- '7700:7700'
volumes:
- './data/meili:/meili_data'
environment:
MEILI_MASTER_KEY: '${MEILI_MASTER_KEY}'
minio:
image: quay.io/minio/minio
ports:
- 9000:9000
- 9001:9001
command: ['minio', 'server', '/data', '--console-address', ':9001']
volumes:
- './data/minio:/data'
# meilisync:
# platform: linux/x86_64
# image: long2ice/meilisync
# volumes:
# - ./config.yml:/meilisync/config.yml
# nginx:
# image: nginx:latest
# depends_on:
# - 'api'
# volumes:
# - './nginx/localhost.nginx.conf:/etc/nginx/conf.d/default.conf'
# ports:
# - '80:80'
+45 -17
View File
@@ -4,18 +4,24 @@ import aiohttp
import json
import meilisearch
from io import BytesIO
from dataclasses import dataclass
from typing import Dict, Any
from typing import Dict, Any, List
from dotenv import dotenv_values, load_dotenv
from google.cloud import vision as google_vision
from google.protobuf.json_format import MessageToDict, MessageToJson
from google.cloud.vision_v1 import types
from PIL import Image
# need to change the env for the google api calls
load_dotenv()
config = dotenv_values(".env")
DATA_DIR = './data/memesearch'
MEILI_INDEX = 'memes'
MEILI_URL = 'http://localhost:7700'
CHANNEL_IDS = [
367936426962190336, # TPM shitposts
1095060717117374687, # aella memes
@@ -31,6 +37,13 @@ headers = {
# [ ] save img as json + imagefile
# [ ] check extensions (ignore mp4 maybe)
@dataclass
class MemeDocument:
id: int
url: str
text: str | None
tags: List[str]
@dataclass
class MemeMessage:
id: int
@@ -38,8 +51,8 @@ class MemeMessage:
guild_name: str
channel_name: str
text: str | None
vision_json: str | None
text: str | None
# maybe don't need these
# kind of extra
@@ -47,22 +60,34 @@ class MemeMessage:
guild_json: Dict[str, Any]
channel_json: Dict[str, Any]
def output_dir(self):
return f'{DATA_DIR}/guilds/{self.guild_name}/{self.channel_name}'
def img_dir(self):
return f"{DATA_DIR}/imgs/"
async def save_img(self, session: aiohttp.ClientSession):
output_dir = self.output_dir()
webp_output = f'{DATA_DIR}/imgs/'
# there's a big param of some kind after the image url that we remove
url_filename = os.path.basename(self.url).split('?')[0]
(filename, ext) = os.path.splitext(url_filename)
print(f"filename={filename} ext={ext}")
os.makedirs(output_dir, exist_ok=True);
os.makedirs(self.output_dir(), exist_ok=True);
os.makedirs(self.img_dir(), exist_ok=True);
async with session.get(self.url) as response:
if response.status == 200:
with open(f'{output_dir}/{self.id}{ext}', 'wb') as f:
f.write(await response.read())
res_bytes = await response.read()
def output_dir(self):
return f'./data/{self.guild_name}/{self.channel_name}'
with open(f'{self.output_dir()}/{self.id}{ext}', 'wb') as f:
f.write(res_bytes)
convert_extensions = ['jpg', 'png', 'jpeg', 'webp']
if ext.lower() in convert_extensions:
image = Image.open(BytesIO(res_bytes))
image.save(f"{self.img_dir()}/{self.id}.webp", format='WebP')
def annotate_image_request(self, vision):
request = {
@@ -82,12 +107,13 @@ class MemeMessage:
with open(f'{self.output_dir()}/{self.id}.json', 'w') as f:
json.dump(vars(self), f, indent=4)
def to_meili_document(self):
return {
'id': self.id,
'text': self.text,
'url': self.url,
}
def to_document(self):
return MemeDocument(
id=self.id,
url=f'{DATA_DIR}/{self.id}.webp',
text=self.text,
tags=[self.guild_name, self.channel_name],
)
def filter_message_img_urls(messages):
url_lists = map(
@@ -142,9 +168,11 @@ async def download_channel_imgs(session, vision, channel_id):
[meme.annotate_image_request(vision) for meme in memes]
[meme.persist() for meme in memes]
client = meilisearch.Client('http://localhost:7700', 'aSampleMasterKey')
documents = [meme.to_meili_document() for meme in memes]
client.index('memes').add_documents(documents)
client = meilisearch.Client(MEILI_URL, config['MEILI_MASTER_KEY'])
# have to vars the dataclass out for meili
documents = [vars(meme.to_document()) for meme in memes]
client.index(MEILI_INDEX).add_documents(documents)
async def main():
vision = google_vision.ImageAnnotatorClient()
+20
View File
@@ -0,0 +1,20 @@
FROM python:3.9-slim as compiler
ENV PYTHONUNBUFFERED 1
WORKDIR /app/
RUN python -m venv /opt/venv
# Enable venv
ENV PATH="/opt/venv/bin:$PATH"
COPY ./requirements.txt /app/requirements.txt
RUN pip install -Ur requirements.txt
FROM python:3.9-slim as runner
WORKDIR /app/
COPY --from=compiler /opt/venv /opt/venv
# Enable venv
ENV PATH="/opt/venv/bin:$PATH"
COPY . /app/
CMD ["python", "main.py", ]
+1
View File
@@ -0,0 +1 @@
appendfsync everysec
+2 -1
View File
@@ -1,4 +1,5 @@
python-dotenv
aiohttp
google-cloud-vision
meilisearch
meilisearch
pillow
+40
View File
@@ -0,0 +1,40 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@meilisearch/instant-meilisearch/templates/basic_search.css" />
</head>
<body>
<div id="app" class="wrapper"></div>
</body>
<script src="https://unpkg.com/react@18/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js" crossorigin></script>
<script src="https://cdn.jsdelivr.net/npm/react-instantsearch-dom@6.7.0/dist/umd/ReactInstantSearchDOM.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@meilisearch/instant-meilisearch/dist/instant-meilisearch.umd.min.js"></script>
<script>
const { InstantSearch, SearchBox, Hits, Highlight, Configure } = ReactInstantSearchDOM;
const searchClient = instantMeiliSearch(
"http://localhost:7700",
'73xjT1ueTlcXPEKJtzvJxtrlLNOR5q85fJ_TOzoRgcA'
);
const App = () => (
React.createElement(InstantSearch, {
indexName: "memes",
searchClient: searchClient
}, [
React.createElement(SearchBox, { key: 1 }),
React.createElement(Hits, { hitComponent: Hit, key: 2 }),
React.createElement(Configure, { hitsPerPage: 10 })]
)
);
const Hit = (props) => (
React.createElement(Highlight, {
attribute: "id",
hit: props.hit
})
);
ReactDOM.createRoot(document.querySelector('#app')).render(App());
console.log(searchClient)
</script>
</html>