netseecxld commited on
Commit
81b6ed0
Β·
verified Β·
1 Parent(s): 822bdd6

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +14 -363
app.py CHANGED
@@ -21,7 +21,7 @@ BATCH_SIZE = 500
21
  REQUEST_TIMEOUT = 10
22
  HF_TOKEN = os.environ.get('HF_TOKEN')
23
  HF_DATASET = os.environ.get('HF_DATASET')
24
- PUSH_EVERY = 10000 # ΠΏΡƒΡˆΠΈΡ‚ΡŒ ΠΊΠ°ΠΆΠ΄Ρ‹Π΅ 10k Π½Π°ΠΉΠ΄Π΅Π½Π½Ρ‹Ρ…
25
 
26
  HEADERS = {
27
  'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
@@ -45,308 +45,14 @@ def pull_from_hf():
45
  if not HF_TOKEN or not HF_DATASET:
46
  return
47
  try:
48
- hf_hub_download(
49
- repo_id=HF_DATASET,
50
- filename='linkm_posts.db',
51
- repo_type='dataset',
52
- local_dir='/app',
53
- token=HF_TOKEN,
54
- )
55
- print('βœ… Π‘Π°Π·Π° скачана с HF')
56
- except Exception as e:
57
- print(f'⚠️ НС ΡƒΠ΄Π°Π»ΠΎΡΡŒ ΡΠΊΠ°Ρ‡Π°Ρ‚ΡŒ Π±Π°Π·Ρƒ: {e}')
58
-
59
- try:
60
- hf_hub_download(
61
- repo_id=HF_DATASET,
62
- filename='linkm_progress.json',
63
- repo_type='dataset',
64
- local_dir='/app',
65
- token=HF_TOKEN,
66
- )
67
- print('βœ… ΠŸΡ€ΠΎΠ³Ρ€Π΅ΡΡ скачан с HF')
68
- except Exception as e:
69
- print(f'⚠️ ΠŸΡ€ΠΎΠ³Ρ€Π΅ΡΡ Π½Π΅ Π½Π°ΠΉΠ΄Π΅Π½ Π½Π° HF: {e}')
70
-
71
- # ─── БАЗА ДАННЫΠ₯ ──────────────────────────────────────────────
72
- def init_db():
73
- conn = sqlite3.connect(DB_PATH)
74
- conn.execute('PRAGMA journal_mode=WAL')
75
- c = conn.cursor()
76
- c.execute('''
77
- CREATE TABLE IF NOT EXISTS posts (
78
- post_id INTEGER PRIMARY KEY,
79
- text TEXT NOT NULL,
80
- parsed_at TEXT DEFAULT (datetime('now'))
81
- )
82
- ''')
83
- c.execute('''
84
- CREATE VIRTUAL TABLE IF NOT EXISTS posts_fts USING fts5(
85
- text,
86
- content='posts',
87
- content_rowid='post_id',
88
- tokenize='trigram'
89
- )
90
- ''')
91
- c.execute('''
92
- CREATE TRIGGER IF NOT EXISTS posts_ai AFTER INSERT ON posts BEGIN
93
- INSERT INTO posts_fts(rowid, text) VALUES (new.post_id, new.text);
94
- END
95
- ''')
96
- conn.commit()
97
- conn.close()
98
-
99
- # ─── ΠžΠ”ΠΠžΠ ΠΠ—ΠžΠ’ΠΠ― ΠœΠ˜Π“Π ΠΠ¦Π˜Π― Π’ FTS5 ───────────────────────────────
100
- def migrate_to_fts():
101
- conn = sqlite3.connect(DB_PATH, timeout=120)
102
- conn.execute('PRAGMA journal_mode=WAL')
103
- c = conn.cursor()
104
- c.execute('SELECT COUNT(*) FROM posts_fts')
105
- fts_count = c.fetchone()[0]
106
- c.execute('SELECT COUNT(*) FROM posts')
107
- posts_count = c.fetchone()[0]
108
-
109
- if fts_count < posts_count:
110
- print(f'πŸ”¨ Π‘Ρ‚Ρ€ΠΎΡŽ FTS индСкс: {fts_count:,} / {posts_count:,}, заполняю...')
111
- c.execute('''
112
- INSERT INTO posts_fts(rowid, text)
113
- SELECT post_id, text FROM posts
114
- WHERE post_id NOT IN (SELECT rowid FROM posts_fts)
115
- ''')
116
- conn.commit()
117
- print('βœ… FTS индСкс построСн')
118
- else:
119
- print('βœ… FTS индСкс ΡƒΠΆΠ΅ Π°ΠΊΡ‚ΡƒΠ°Π»Π΅Π½')
120
- conn.close()
121
-
122
- # ─── ΠŸΠ ΠžΠ“Π Π•Π‘Π‘ ─────────────────────────────────────────────────
123
- def load_progress():
124
- if os.path.exists(PROGRESS_FILE):
125
- try:
126
- with open(PROGRESS_FILE, 'r') as f:
127
- data = json.load(f)
128
- print(f'▢️ ΠŸΡ€ΠΎΠ΄ΠΎΠ»ΠΆΠ°Π΅ΠΌ с ID: {data["next_id"]} (Π½Π°ΠΉΠ΄Π΅Π½ΠΎ: {data["found"]:,})')
129
- return data
130
- except Exception:
131
- pass
132
- return {'next_id': START_ID, 'processed': 0, 'found': 0}
133
-
134
- def save_progress(data):
135
- with open(PROGRESS_FILE, 'w') as f:
136
- json.dump(data, f)
137
-
138
- # ─── ΠŸΠΠ Π‘Π˜ΠΠ“ ──────────────────────────────────────────────────
139
- def extract_text(html: str):
140
- soup = BeautifulSoup(html, 'html.parser')
141
-
142
- main = soup.find('main', class_='deeplink_main')
143
- if main:
144
- p = main.find('p')
145
- if p:
146
- parts = [node.strip() for node in p.descendants if isinstance(node, str) and node.strip()]
147
- result = ' '.join(parts).strip()
148
- if ' on Link. ' in result:
149
- result = result.split(' on Link. ', 1)[1].strip()
150
- elif result.endswith(' on Link.'):
151
- return None
152
- if result:
153
- return result
154
-
155
- og = soup.find('meta', property='og:description')
156
- if og:
157
- content = og.get('content', '').strip()
158
- if ' on Link. ' in content:
159
- content = content.split(' on Link. ', 1)[1].strip()
160
- elif content.endswith(' on Link.'):
161
- return None
162
- if content:
163
- return content
164
-
165
- return None
166
-
167
- async def fetch_post(session, post_id):
168
- try:
169
- async with session.get(
170
- BASE_URL.format(post_id),
171
- timeout=aiohttp.ClientTimeout(total=REQUEST_TIMEOUT)
172
- ) as resp:
173
- if resp.status != 200:
174
- return post_id, None
175
- html = await resp.text(encoding='utf-8', errors='replace')
176
- return post_id, extract_text(html)
177
- except Exception:
178
- return post_id, None
179
-
180
- def save_batch(rows):
181
- if not rows:
182
- return
183
- conn = sqlite3.connect(DB_PATH)
184
- conn.executemany('INSERT OR IGNORE INTO posts (post_id, text) VALUES (?, ?)', rows)
185
- conn.commit()
186
- conn.close()
187
-
188
- # ─── PUSH Π’ HUGGINGFACE ───────────────────────────────────────
189
- def push_to_hf(progress):
190
- if not HF_TOKEN or not HF_DATASET:
191
- return
192
- try:
193
- api = HfApi(token=HF_TOKEN)
194
- api.upload_file(
195
- path_or_fileobj=DB_PATH,
196
- path_in_repo='linkm_posts.db',
197
- repo_id=HF_DATASET,
198
- repo_type='dataset',
199
- )
200
- api.upload_file(
201
- path_or_fileobj=PROGRESS_FILE,
202
- path_in_repo='linkm_progress.json',
203
- repo_id=HF_DATASET,
204
- repo_type='dataset',
205
- )
206
- print(f'βœ… Π—Π°ΠΏΡƒΡˆΠ΅Π½ΠΎ Π² HF (Π½Π°ΠΉΠ΄Π΅Π½ΠΎ: {progress["found"]:,})')
207
- state['last_push'] = progress['found']
208
- except Exception as e:
209
- print(f'❌ Ошибка ΠΏΡƒΡˆΠ°: {e}')
210
-
211
- # ─── ГЛАВНЫЙ Π¦Π˜ΠšΠ› ─────────────────────────────────────────────
212
- async def run_parser():
213
- pull_from_hf()
214
- init_db()
215
- migrate_to_fts()
216
-
217
- progress = load_progress()
218
- current_id = progress['next_id']
219
- start_time = time.time()
220
-
221
- state['status'] = 'running'
222
- state['current_id'] = current_id
223
- state['found'] = progress['found']
224
- state['processed'] = progress['processed']
225
- state['last_push'] = progress['found']
226
-
227
- connector = aiohttp.TCPConnector(limit=WORKERS, ssl=False)
228
-
229
- async with aiohttp.ClientSession(connector=connector, headers=HEADERS) as session:
230
- while current_id > END_ID:
231
- batch_start = max(current_id - BATCH_SIZE, END_ID)
232
- batch_ids = list(range(current_id, batch_start, -1))
233
- results = await asyncio.gather(*[fetch_post(session, pid) for pid in batch_ids])
234
-
235
- found_rows = [(pid, txt) for pid, txt in results if txt]
236
- save_batch(found_rows)
237
-
238
- progress['processed'] += len(batch_ids)
239
- progress['found'] += len(found_rows)
240
- progress['next_id'] = batch_start
241
- save_progress(progress)
242
-
243
- elapsed = time.time() - start_time
244
- speed = progress['processed'] / max(elapsed, 1)
245
-
246
- state['current_id'] = current_id
247
- state['found'] = progress['found']
248
- state['processed'] = progress['processed']
249
- state['speed'] = round(speed)
250
-
251
- print(f'ID: {current_id:,} | НайдСно: {progress["found"]:,} | {speed:.0f} ID/сСк')
252
-
253
- if progress['found'] - state['last_push'] >= PUSH_EVERY:
254
- push_to_hf(progress)
255
-
256
- current_id = batch_start
257
-
258
- state['status'] = 'done'
259
- push_to_hf(progress)
260
-
261
- def start_parser():
262
- asyncio.run(run_parser())
263
-
264
- # ─── FASTAPI ──────────────────────────────────────────────────
265
- app = FastAPI()
266
-
267
- @app.get('/status')
268
- def get_status():
269
- elapsed = time.time() - state['started_at']
270
- remaining_ids = state['current_id'] - END_ID
271
- remaining_sec = remaining_ids / max(state['speed'], 1)
272
- return {
273
- 'status': state['status'],
274
- 'current_id': state['current_id'],
275
- 'end_id': END_ID,
276
- 'percent': round((START_ID - state['current_id']) / START_ID * 100, 2),
277
- 'found': state['found'] + 13000000,
278
- 'processed': state['processed'],
279
- 'speed_per_sec': state['speed'],
280
- 'elapsed_min': round(elapsed / 60, 1),
281
- 'remaining_min': round(remaining_sec / 60, 1),
282
- 'last_push_at': state['last_push'],
283
- }
284
- import sqlite3
285
- import asyncio
286
- import aiohttp
287
- import json
288
- import os
289
- import time
290
- import threading
291
- from bs4 import BeautifulSoup
292
- from fastapi import FastAPI
293
- from huggingface_hub import HfApi, hf_hub_download
294
- import uvicorn
295
-
296
- # ─── ΠΠΠ‘Π’Π ΠžΠ™ΠšΠ˜ ────────────────────────────────────────────────
297
- DB_PATH = '/app/linkm_posts.db'
298
- PROGRESS_FILE = '/app/linkm_progress.json'
299
- BASE_URL = 'https://linkm.me/posts/{}'
300
- START_ID = 213470273
301
- END_ID = START_ID
302
- WORKERS = 150
303
- BATCH_SIZE = 500
304
- REQUEST_TIMEOUT = 10
305
- HF_TOKEN = os.environ.get('HF_TOKEN')
306
- HF_DATASET = os.environ.get('HF_DATASET')
307
- PUSH_EVERY = 10000 # ΠΏΡƒΡˆΠΈΡ‚ΡŒ ΠΊΠ°ΠΆΠ΄Ρ‹Π΅ 10k Π½Π°ΠΉΠ΄Π΅Π½Π½Ρ‹Ρ…
308
-
309
- HEADERS = {
310
- 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
311
- 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
312
- 'Accept-Language': 'en-US,en;q=0.5',
313
- }
314
-
315
- # ─── Π‘ΠžΠ‘Π’ΠžΠ―ΠΠ˜Π• ────────────────────────────────────────────────
316
- state = {
317
- 'status': 'starting',
318
- 'current_id': START_ID,
319
- 'found': 0,
320
- 'processed': 0,
321
- 'speed': 0,
322
- 'last_push': 0,
323
- 'started_at': time.time(),
324
- }
325
-
326
- # ─── PULL Π‘ HF ПРИ БВАРВЕ ─────────────────────────────────────
327
- def pull_from_hf():
328
- if not HF_TOKEN or not HF_DATASET:
329
- return
330
- try:
331
- hf_hub_download(
332
- repo_id=HF_DATASET,
333
- filename='linkm_posts.db',
334
- repo_type='dataset',
335
- local_dir='/app',
336
- token=HF_TOKEN,
337
- )
338
  print('βœ… Π‘Π°Π·Π° скачана с HF')
339
  except Exception as e:
340
  print(f'⚠️ НС ΡƒΠ΄Π°Π»ΠΎΡΡŒ ΡΠΊΠ°Ρ‡Π°Ρ‚ΡŒ Π±Π°Π·Ρƒ: {e}')
341
-
342
  try:
343
- hf_hub_download(
344
- repo_id=HF_DATASET,
345
- filename='linkm_progress.json',
346
- repo_type='dataset',
347
- local_dir='/app',
348
- token=HF_TOKEN,
349
- )
350
  print('βœ… ΠŸΡ€ΠΎΠ³Ρ€Π΅ΡΡ скачан с HF')
351
  except Exception as e:
352
  print(f'⚠️ ΠŸΡ€ΠΎΠ³Ρ€Π΅ΡΡ Π½Π΅ Π½Π°ΠΉΠ΄Π΅Π½ Π½Π° HF: {e}')
@@ -363,45 +69,9 @@ def init_db():
363
  parsed_at TEXT DEFAULT (datetime('now'))
364
  )
365
  ''')
366
- c.execute('''
367
- CREATE VIRTUAL TABLE IF NOT EXISTS posts_fts2 USING fts5(
368
- text,
369
- content='posts',
370
- content_rowid='post_id',
371
- tokenize='trigram'
372
- )
373
- ''')
374
- c.execute('''
375
- CREATE TRIGGER IF NOT EXISTS posts_ai2 AFTER INSERT ON posts BEGIN
376
- INSERT INTO posts_fts2(rowid, text) VALUES (new.post_id, new.text);
377
- END
378
- ''')
379
  conn.commit()
380
  conn.close()
381
 
382
- # ─── ΠžΠ”ΠΠžΠ ΠΠ—ΠžΠ’ΠΠ― ΠœΠ˜Π“Π ΠΠ¦Π˜Π― Π’ FTS5 ───────────────────────────────
383
- def migrate_to_fts():
384
- conn = sqlite3.connect(DB_PATH, timeout=120)
385
- conn.execute('PRAGMA journal_mode=WAL')
386
- c = conn.cursor()
387
- c.execute('SELECT COUNT(*) FROM posts_fts2')
388
- fts_count = c.fetchone()[0]
389
- c.execute('SELECT COUNT(*) FROM posts')
390
- posts_count = c.fetchone()[0]
391
-
392
- if fts_count < posts_count:
393
- print(f'πŸ”¨ Π‘Ρ‚Ρ€ΠΎΡŽ FTS индСкс: {fts_count:,} / {posts_count:,}, заполняю...')
394
- c.execute('''
395
- INSERT INTO posts_fts2(rowid, text)
396
- SELECT post_id, text FROM posts
397
- WHERE post_id NOT IN (SELECT rowid FROM posts_fts2)
398
- ''')
399
- conn.commit()
400
- print('βœ… FTS индСкс построСн')
401
- else:
402
- print('βœ… FTS индСкс ΡƒΠΆΠ΅ Π°ΠΊΡ‚ΡƒΠ°Π»Π΅Π½')
403
- conn.close()
404
-
405
  # ─── ΠŸΠ ΠžΠ“Π Π•Π‘Π‘ ─────────────────────────────────────────────────
406
  def load_progress():
407
  if os.path.exists(PROGRESS_FILE):
@@ -474,18 +144,10 @@ def push_to_hf(progress):
474
  return
475
  try:
476
  api = HfApi(token=HF_TOKEN)
477
- api.upload_file(
478
- path_or_fileobj=DB_PATH,
479
- path_in_repo='linkm_posts.db',
480
- repo_id=HF_DATASET,
481
- repo_type='dataset',
482
- )
483
- api.upload_file(
484
- path_or_fileobj=PROGRESS_FILE,
485
- path_in_repo='linkm_progress.json',
486
- repo_id=HF_DATASET,
487
- repo_type='dataset',
488
- )
489
  print(f'βœ… Π—Π°ΠΏΡƒΡˆΠ΅Π½ΠΎ Π² HF (Π½Π°ΠΉΠ΄Π΅Π½ΠΎ: {progress["found"]:,})')
490
  state['last_push'] = progress['found']
491
  except Exception as e:
@@ -495,7 +157,6 @@ def push_to_hf(progress):
495
  async def run_parser():
496
  pull_from_hf()
497
  init_db()
498
- migrate_to_fts()
499
 
500
  progress = load_progress()
501
  current_id = progress['next_id']
@@ -570,8 +231,10 @@ def search(q: str, limit: int = 50):
570
  conn = sqlite3.connect(DB_PATH, timeout=30)
571
  conn.execute('PRAGMA journal_mode=WAL')
572
  c = conn.cursor()
573
- safe_q = q.replace('"', '""')
574
- c.execute('SELECT rowid AS post_id, text FROM posts_fts2 WHERE posts_fts2 MATCH ? LIMIT ?', (f'"{safe_q}"', limit))
 
 
575
  rows = c.fetchall()
576
  conn.close()
577
  return {
@@ -586,28 +249,16 @@ def debug(q: str = 'ΠΏΡ€ΠΈΠ²Π΅Ρ‚'):
586
  c = conn.cursor()
587
  c.execute('SELECT COUNT(*) FROM posts')
588
  posts_count = c.fetchone()[0]
589
- c.execute('SELECT COUNT(*) FROM posts_fts2')
590
- fts_count = c.fetchone()[0]
591
  c.execute('SELECT COUNT(*) FROM posts WHERE text LIKE ?', (f'%{q}%',))
592
  like_count = c.fetchone()[0]
593
- try:
594
- c.execute('SELECT COUNT(*) FROM posts_fts2 WHERE posts_fts2 MATCH ?', (f'"{q}"',))
595
- match_count = c.fetchone()[0]
596
- match_error = None
597
- except Exception as e:
598
- match_count = None
599
- match_error = str(e)
600
  conn.close()
601
  return {
602
  'posts_table_total': posts_count,
603
- 'posts_fts2_total': fts_count,
604
  'like_search_count': like_count,
605
- 'fts_match_count': match_count,
606
- 'fts_match_error': match_error,
607
  }
608
 
609
  # ─── БВАРВ ────────────────────────────────────────────────────
610
  if __name__ == '__main__':
611
  t = threading.Thread(target=start_parser, daemon=True)
612
  t.start()
613
- uvicorn.run(app, host='0.0.0.0', port=7860)
 
21
  REQUEST_TIMEOUT = 10
22
  HF_TOKEN = os.environ.get('HF_TOKEN')
23
  HF_DATASET = os.environ.get('HF_DATASET')
24
+ PUSH_EVERY = 10000
25
 
26
  HEADERS = {
27
  'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
 
45
  if not HF_TOKEN or not HF_DATASET:
46
  return
47
  try:
48
+ hf_hub_download(repo_id=HF_DATASET, filename='linkm_posts.db',
49
+ repo_type='dataset', local_dir='/app', token=HF_TOKEN)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
50
  print('βœ… Π‘Π°Π·Π° скачана с HF')
51
  except Exception as e:
52
  print(f'⚠️ НС ΡƒΠ΄Π°Π»ΠΎΡΡŒ ΡΠΊΠ°Ρ‡Π°Ρ‚ΡŒ Π±Π°Π·Ρƒ: {e}')
 
53
  try:
54
+ hf_hub_download(repo_id=HF_DATASET, filename='linkm_progress.json',
55
+ repo_type='dataset', local_dir='/app', token=HF_TOKEN)
 
 
 
 
 
56
  print('βœ… ΠŸΡ€ΠΎΠ³Ρ€Π΅ΡΡ скачан с HF')
57
  except Exception as e:
58
  print(f'⚠️ ΠŸΡ€ΠΎΠ³Ρ€Π΅ΡΡ Π½Π΅ Π½Π°ΠΉΠ΄Π΅Π½ Π½Π° HF: {e}')
 
69
  parsed_at TEXT DEFAULT (datetime('now'))
70
  )
71
  ''')
 
 
 
 
 
 
 
 
 
 
 
 
 
72
  conn.commit()
73
  conn.close()
74
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
75
  # ─── ΠŸΠ ΠžΠ“Π Π•Π‘Π‘ ─────────────────────────────────────────────────
76
  def load_progress():
77
  if os.path.exists(PROGRESS_FILE):
 
144
  return
145
  try:
146
  api = HfApi(token=HF_TOKEN)
147
+ api.upload_file(path_or_fileobj=DB_PATH, path_in_repo='linkm_posts.db',
148
+ repo_id=HF_DATASET, repo_type='dataset')
149
+ api.upload_file(path_or_fileobj=PROGRESS_FILE, path_in_repo='linkm_progress.json',
150
+ repo_id=HF_DATASET, repo_type='dataset')
 
 
 
 
 
 
 
 
151
  print(f'βœ… Π—Π°ΠΏΡƒΡˆΠ΅Π½ΠΎ Π² HF (Π½Π°ΠΉΠ΄Π΅Π½ΠΎ: {progress["found"]:,})')
152
  state['last_push'] = progress['found']
153
  except Exception as e:
 
157
  async def run_parser():
158
  pull_from_hf()
159
  init_db()
 
160
 
161
  progress = load_progress()
162
  current_id = progress['next_id']
 
231
  conn = sqlite3.connect(DB_PATH, timeout=30)
232
  conn.execute('PRAGMA journal_mode=WAL')
233
  c = conn.cursor()
234
+ c.execute(
235
+ 'SELECT post_id, text FROM posts WHERE text LIKE ? LIMIT ?',
236
+ (f'%{q}%', limit)
237
+ )
238
  rows = c.fetchall()
239
  conn.close()
240
  return {
 
249
  c = conn.cursor()
250
  c.execute('SELECT COUNT(*) FROM posts')
251
  posts_count = c.fetchone()[0]
 
 
252
  c.execute('SELECT COUNT(*) FROM posts WHERE text LIKE ?', (f'%{q}%',))
253
  like_count = c.fetchone()[0]
 
 
 
 
 
 
 
254
  conn.close()
255
  return {
256
  'posts_table_total': posts_count,
 
257
  'like_search_count': like_count,
 
 
258
  }
259
 
260
  # ─── БВАРВ ────────────────────────────────────────────────────
261
  if __name__ == '__main__':
262
  t = threading.Thread(target=start_parser, daemon=True)
263
  t.start()
264
+ uvicorn.run(app, host='0.0.0.0', port=7860)