mramazan commited on
Commit
be42acc
·
verified ·
1 Parent(s): 5724e66

Update main.py

Browse files
Files changed (1) hide show
  1. main.py +0 -845
main.py CHANGED
@@ -1,845 +0,0 @@
1
- from flask import Flask, render_template, request, jsonify, session, Response
2
- import sys
3
- import pickle
4
- import json
5
- import gc
6
- import weakref
7
- from pathlib import Path
8
- from utils import *
9
- from options import args
10
- from models import model_factory
11
- from flask_socketio import SocketIO, emit
12
- from datetime import datetime
13
- import random
14
- import re
15
- import xml.etree.ElementTree as ET
16
-
17
- app = Flask(__name__)
18
- app.secret_key = '1903bjk'
19
- socketio = SocketIO(app, cors_allowed_origins="*")
20
-
21
- # Memory-efficient chat system
22
- class ChatManager:
23
- def __init__(self, max_messages=100): # Reduced from 300
24
- self.messages = []
25
- self.active_users = {}
26
- self.max_messages = max_messages
27
-
28
- def add_message(self, message):
29
- self.messages.append(message)
30
- if len(self.messages) > self.max_messages:
31
- self.messages.pop(0)
32
-
33
- def get_messages(self):
34
- return self.messages
35
-
36
- def add_user(self, sid, username):
37
- self.active_users[sid] = {
38
- 'username': username,
39
- 'connected_at': datetime.now()
40
- }
41
-
42
- def remove_user(self, sid):
43
- return self.active_users.pop(sid, None)
44
-
45
- def get_user_count(self):
46
- return len(self.active_users)
47
-
48
- def get_username(self, sid):
49
- user = self.active_users.get(sid)
50
- return user['username'] if user else None
51
-
52
- def update_username(self, sid, new_username):
53
- if sid in self.active_users:
54
- self.active_users[sid]['username'] = new_username
55
-
56
- chat_manager = ChatManager()
57
-
58
- def generate_username():
59
- adjectives = ['Cool', 'Awesome', 'Swift', 'Bright', 'Happy', 'Smart', 'Kind', 'Brave', 'Calm', 'Epic', "Black"]
60
- nouns = ['Otaku', 'Ninja', 'Samurai', 'Dragon', 'Phoenix', 'Tiger', 'Wolf', 'Eagle', 'Fox', 'Bear']
61
- return f"{random.choice(adjectives)}{random.choice(nouns)}{random.randint(100, 999)}"
62
-
63
- def clean_message(message):
64
- # HTML tag'leri temizle
65
- message = re.sub(r'<[^>]*>', '', message)
66
- # Uzunluk kontrolü
67
- if len(message) > 500:
68
- message = message[:500]
69
- return message.strip()
70
-
71
- # Lazy loading için wrapper class
72
- class LazyDict:
73
- def __init__(self, file_path):
74
- self.file_path = file_path
75
- self._data = None
76
- self._loaded = False
77
-
78
- def _load_data(self):
79
- if not self._loaded:
80
- try:
81
- with open(self.file_path, "r", encoding="utf-8") as file:
82
- self._data = json.load(file)
83
- self._loaded = True
84
- except Exception as e:
85
- print(f"Warning: Could not load {self.file_path}: {str(e)}")
86
- self._data = {}
87
- self._loaded = True
88
-
89
- def get(self, key, default=None):
90
- self._load_data()
91
- return self._data.get(key, default)
92
-
93
- def __contains__(self, key):
94
- self._load_data()
95
- return key in self._data
96
-
97
- def items(self):
98
- self._load_data()
99
- return self._data.items()
100
-
101
- def keys(self):
102
- self._load_data()
103
- return self._data.keys()
104
-
105
- def __len__(self):
106
- self._load_data()
107
- return len(self._data)
108
-
109
- # Sitemap route'ları
110
- @app.route('/sitemap.xml')
111
- def sitemap():
112
- """Dinamik sitemap.xml oluşturur"""
113
- try:
114
- # XML root element
115
- urlset = ET.Element('urlset')
116
- urlset.set('xmlns', 'http://www.sitemaps.org/schemas/sitemap/0.9')
117
- urlset.set('xmlns:image', 'http://www.google.com/schemas/sitemap-image/1.1')
118
-
119
- # Base URL
120
- base_url = request.url_root.rstrip('/')
121
- current_date = datetime.now().strftime('%Y-%m-%d')
122
-
123
- # Ana sayfa
124
- url = ET.SubElement(urlset, 'url')
125
- ET.SubElement(url, 'loc').text = f'{base_url}/'
126
- ET.SubElement(url, 'lastmod').text = current_date
127
- ET.SubElement(url, 'changefreq').text = 'daily'
128
- ET.SubElement(url, 'priority').text = '1.0'
129
-
130
- # Chat sayfası
131
- url = ET.SubElement(urlset, 'url')
132
- ET.SubElement(url, 'loc').text = f'{base_url}/chat'
133
- ET.SubElement(url, 'lastmod').text = current_date
134
- ET.SubElement(url, 'changefreq').text = 'hourly'
135
- ET.SubElement(url, 'priority').text = '0.8'
136
-
137
- # Anime sayfaları (sadece ilk 50 anime - SEO için)
138
- if recommendation_system and recommendation_system.id_to_anime:
139
- anime_count = 0
140
- for anime_id, anime_data in recommendation_system.id_to_anime.items():
141
- if anime_count >= 50: # Reduced from 100
142
- break
143
-
144
- try:
145
- anime_name = anime_data[0] if isinstance(anime_data, list) and len(anime_data) > 0 else str(anime_data)
146
- safe_name = anime_name.replace(' ', '-').replace('/', '-').replace('?', '').replace('&', 'and')
147
- safe_name = re.sub(r'[^\w\-]', '', safe_name)
148
-
149
- url = ET.SubElement(urlset, 'url')
150
- ET.SubElement(url, 'loc').text = f'{base_url}/anime/{anime_id}/{safe_name}'
151
- ET.SubElement(url, 'lastmod').text = current_date
152
- ET.SubElement(url, 'changefreq').text = 'weekly'
153
- ET.SubElement(url, 'priority').text = '0.6'
154
-
155
- # Sadece gerekli durumlarda resim URL'si ekle
156
- if anime_count < 20: # Sadece ilk 20 anime için resim
157
- image_url = recommendation_system.get_anime_image_url(int(anime_id))
158
- if image_url:
159
- image_elem = ET.SubElement(url, 'image:image')
160
- ET.SubElement(image_elem, 'image:loc').text = image_url
161
- ET.SubElement(image_elem, 'image:title').text = anime_name
162
- ET.SubElement(image_elem, 'image:caption').text = f'Poster image for {anime_name}'
163
-
164
- anime_count += 1
165
- except Exception as e:
166
- print(f"Error processing anime {anime_id}: {e}")
167
- continue
168
-
169
- # XML'i string'e çevir
170
- xml_str = ET.tostring(urlset, encoding='unicode')
171
- xml_declaration = '<?xml version="1.0" encoding="UTF-8"?>\n'
172
- full_xml = xml_declaration + xml_str
173
-
174
- return Response(full_xml, mimetype='application/xml')
175
-
176
- except Exception as e:
177
- print(f"Sitemap generation error: {e}")
178
- return Response(
179
- '<?xml version="1.0" encoding="UTF-8"?><urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"></urlset>',
180
- mimetype='application/xml')
181
-
182
- @app.route('/robots.txt')
183
- def robots_txt():
184
- """Robots.txt dosyası"""
185
- robots_content = f"""User-agent: *
186
- Allow: /
187
- Allow: /chat
188
-
189
- Sitemap: {request.url_root.rstrip('/')}/sitemap.xml
190
- """
191
- return Response(robots_content, mimetype='text/plain')
192
-
193
- @app.route('/anime/<int:anime_id>/<path:anime_name>')
194
- def anime_detail(anime_id, anime_name):
195
- """Anime detay sayfası (SEO için)"""
196
- if not recommendation_system or str(anime_id) not in recommendation_system.id_to_anime:
197
- return render_template('error.html', error="Anime not found"), 404
198
-
199
- anime_data = recommendation_system.id_to_anime.get(str(anime_id))
200
- anime_name = anime_data[0] if isinstance(anime_data, list) and len(anime_data) > 0 else str(anime_data)
201
-
202
- # Anime bilgilerini lazy loading ile al
203
- image_url = recommendation_system.get_anime_image_url(anime_id)
204
- mal_url = recommendation_system.get_anime_mal_url(anime_id)
205
- genres = recommendation_system.get_anime_genres(anime_id)
206
- anime_type = recommendation_system._get_type(anime_id)
207
-
208
- # Benzer animeler öner (sadece 5 tane)
209
- similar_animes = []
210
- try:
211
- recommendations, _, _ = recommendation_system.get_recommendations([anime_id], num_recommendations=5)
212
- similar_animes = recommendations
213
- except:
214
- pass
215
-
216
- anime_info = {
217
- 'id': anime_id,
218
- 'name': anime_name,
219
- 'image_url': image_url,
220
- 'mal_url': mal_url,
221
- 'genres': genres,
222
- 'similar_animes': similar_animes,
223
- 'type': anime_type
224
- }
225
-
226
- # JSON-LD structured data oluştur
227
- structured_data = generate_anime_structured_data(anime_info)
228
-
229
- return render_template('anime_detail.html', anime=anime_info, structured_data=json.dumps(structured_data))
230
-
231
- def generate_anime_structured_data(anime_info):
232
- """Anime için JSON-LD structured data oluşturur"""
233
- structured_data = {
234
- "@context": "https://schema.org",
235
- "@type": anime_info["type"],
236
- "name": anime_info['name'],
237
- "url": f"{request.url_root.rstrip('/')}/anime/{anime_info['id']}/{anime_info['name'].replace(' ', '-')}"
238
- }
239
-
240
- if anime_info['genres']:
241
- structured_data["genre"] = anime_info['genres']
242
-
243
- if anime_info['image_url']:
244
- structured_data["image"] = anime_info['image_url']
245
-
246
- if anime_info['mal_url']:
247
- structured_data["sameAs"] = anime_info['mal_url']
248
-
249
- return structured_data
250
-
251
- @app.route('/sitemap-index.xml')
252
- def sitemap_index():
253
- """Sitemap index dosyası"""
254
- try:
255
- sitemapindex = ET.Element('sitemapindex')
256
- sitemapindex.set('xmlns', 'http://www.sitemaps.org/schemas/sitemap/0.9')
257
-
258
- base_url = request.url_root.rstrip('/')
259
- current_date = datetime.now().strftime('%Y-%m-%d')
260
-
261
- # Ana sitemap
262
- sitemap = ET.SubElement(sitemapindex, 'sitemap')
263
- ET.SubElement(sitemap, 'loc').text = f'{base_url}/sitemap.xml'
264
- ET.SubElement(sitemap, 'lastmod').text = current_date
265
-
266
- xml_str = ET.tostring(sitemapindex, encoding='unicode')
267
- xml_declaration = '<?xml version="1.0" encoding="UTF-8"?>\n'
268
- full_xml = xml_declaration + xml_str
269
-
270
- return Response(full_xml, mimetype='application/xml')
271
-
272
- except Exception as e:
273
- print(f"Sitemap index generation error: {e}")
274
- return Response(
275
- '<?xml version="1.0" encoding="UTF-8"?><sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"></sitemapindex>',
276
- mimetype='application/xml')
277
-
278
- @app.route('/chat')
279
- def chat():
280
- return render_template('chat.html')
281
-
282
- # SocketIO event'leri
283
- @socketio.on('connect')
284
- def on_connect():
285
- username = generate_username()
286
- chat_manager.add_user(request.sid, username)
287
-
288
- # Kullanıcıya mevcut mesajları gönder
289
- emit('chat_history', chat_manager.get_messages())
290
-
291
- # Kullanıcı katıldı mesajı
292
- join_message = {
293
- 'username': 'System',
294
- 'message': f'{username} joined the chat',
295
- 'timestamp': datetime.now().strftime('%H:%M'),
296
- 'type': 'system'
297
- }
298
-
299
- chat_manager.add_message(join_message)
300
- emit('new_message', join_message, broadcast=True)
301
- emit('user_count', chat_manager.get_user_count(), broadcast=True)
302
-
303
- @socketio.on('disconnect')
304
- def on_disconnect():
305
- user = chat_manager.remove_user(request.sid)
306
- if user:
307
- username = user['username']
308
- leave_message = {
309
- 'username': 'System',
310
- 'message': f'{username} left the chat',
311
- 'timestamp': datetime.now().strftime('%H:%M'),
312
- 'type': 'system'
313
- }
314
-
315
- chat_manager.add_message(leave_message)
316
- emit('new_message', leave_message, broadcast=True)
317
- emit('user_count', chat_manager.get_user_count(), broadcast=True)
318
-
319
- @socketio.on('send_message')
320
- def handle_message(data):
321
- username = chat_manager.get_username(request.sid)
322
- if not username:
323
- return
324
-
325
- message = clean_message(data.get('message', ''))
326
- if not message:
327
- return
328
-
329
- message_obj = {
330
- 'username': username,
331
- 'message': message,
332
- 'timestamp': datetime.now().strftime('%H:%M'),
333
- 'type': 'user'
334
- }
335
-
336
- chat_manager.add_message(message_obj)
337
- emit('new_message', message_obj, broadcast=True)
338
-
339
- @socketio.on('change_username')
340
- def handle_username_change(data):
341
- old_username = chat_manager.get_username(request.sid)
342
- if not old_username:
343
- return
344
-
345
- new_username = clean_message(data.get('username', ''))
346
- if not new_username or len(new_username) < 2:
347
- return
348
-
349
- chat_manager.update_username(request.sid, new_username)
350
-
351
- change_message = {
352
- 'username': 'System',
353
- 'message': f'{old_username} changed name to {new_username}',
354
- 'timestamp': datetime.now().strftime('%H:%M'),
355
- 'type': 'system'
356
- }
357
-
358
- chat_manager.add_message(change_message)
359
- emit('new_message', change_message, broadcast=True)
360
- emit('username_changed', {'username': new_username})
361
-
362
- class AnimeRecommendationSystem:
363
- def __init__(self, checkpoint_path, dataset_path, animes_path, images_path, mal_urls_path, type_seq_path, genres_path):
364
- self.model = None
365
- self.dataset = None
366
- self.checkpoint_path = checkpoint_path
367
- self.dataset_path = dataset_path
368
- self.animes_path = animes_path
369
-
370
- # Lazy loading ile memory optimization
371
- self.id_to_anime = LazyDict(animes_path)
372
- self.id_to_url = LazyDict(images_path)
373
- self.id_to_mal_url = LazyDict(mal_urls_path)
374
- self.id_to_type_seq = LazyDict(type_seq_path)
375
- self.id_to_genres = LazyDict(genres_path)
376
-
377
- # Cache için weak reference kullan
378
- self._cache = {}
379
-
380
- self.load_model_and_data()
381
-
382
- def load_model_and_data(self):
383
- try:
384
- print("Loading model and data...")
385
- args.bert_max_len = 128
386
-
387
- # Dataset'i yükle
388
- dataset_path = Path(self.dataset_path)
389
- with dataset_path.open('rb') as f:
390
- self.dataset = pickle.load(f)
391
-
392
- # Model'i yükle
393
- self.model = model_factory(args)
394
- self.load_checkpoint()
395
-
396
- # Garbage collection
397
- gc.collect()
398
- print("Model loaded successfully!")
399
-
400
- except Exception as e:
401
- print(f"Error loading model: {str(e)}")
402
- raise e
403
-
404
- def load_checkpoint(self):
405
- try:
406
- with open(self.checkpoint_path, 'rb') as f:
407
- checkpoint = torch.load(f, map_location='cpu', weights_only=False)
408
- self.model.load_state_dict(checkpoint['model_state_dict'])
409
- self.model.eval()
410
-
411
- # Checkpoint'i bellekten temizle
412
- del checkpoint
413
- gc.collect()
414
-
415
- except Exception as e:
416
- raise Exception(f"Failed to load checkpoint from {self.checkpoint_path}: {str(e)}")
417
-
418
- def get_anime_genres(self, anime_id):
419
- genres = self.id_to_genres.get(str(anime_id), [])
420
- return [genre.title() for genre in genres] if genres else []
421
-
422
- def get_all_animes(self):
423
- """Tüm anime listesini döndürür - cache kullanır"""
424
- cache_key = 'all_animes'
425
- if cache_key in self._cache:
426
- return self._cache[cache_key]
427
-
428
- animes = []
429
- # Sadece gerekli durumlarda yükle
430
- for k, v in list(self.id_to_anime.items())[:1000]: # İlk 1000 anime
431
- anime_name = v[0] if isinstance(v, list) and len(v) > 0 else str(v)
432
- animes.append((int(k), anime_name))
433
-
434
- animes.sort(key=lambda x: x[1])
435
- self._cache[cache_key] = animes
436
- return animes
437
-
438
- def get_anime_image_url(self, anime_id):
439
- return self.id_to_url.get(str(anime_id), None)
440
-
441
- def get_anime_mal_url(self, anime_id):
442
- return self.id_to_mal_url.get(str(anime_id), None)
443
-
444
- def get_filtered_anime_pool(self, filters):
445
- """Filtrelere göre anime havuzunu önceden filtreler"""
446
- if not filters:
447
- return None
448
-
449
- if filters.get('show_hentai') and len([k for k, v in filters.items() if v]) == 1:
450
- hentai_animes = []
451
- # Sadece gerekli verileri kontrol et
452
- for anime_id_str in list(self.id_to_anime.keys())[:500]: # Limit
453
- anime_id = int(anime_id_str)
454
- if self._is_hentai(anime_id):
455
- hentai_animes.append(anime_id)
456
- return hentai_animes
457
-
458
- return None
459
-
460
- def _is_hentai(self, anime_id):
461
- """Anime'nin hentai olup olmadığını kontrol eder"""
462
- type_seq_info = self.id_to_type_seq.get(str(anime_id))
463
- if not type_seq_info or len(type_seq_info) < 3:
464
- return False
465
- return type_seq_info[2]
466
-
467
- def _get_type(self, anime_id):
468
- """Anime tipini döndürür"""
469
- type_seq_info = self.id_to_type_seq.get(str(anime_id))
470
- if not type_seq_info or len(type_seq_info) < 2:
471
- return "Unknown"
472
- return type_seq_info[1]
473
-
474
- def get_recommendations(self, favorite_anime_ids, num_recommendations=20, filters=None): # Reduced from 40
475
- try:
476
- if not favorite_anime_ids:
477
- return [], [], "Please add some favorite animes first!"
478
-
479
- smap = self.dataset
480
- inverted_smap = {v: k for k, v in smap.items()}
481
-
482
- converted_ids = []
483
- for anime_id in favorite_anime_ids:
484
- if anime_id in smap:
485
- converted_ids.append(smap[anime_id])
486
-
487
- if not converted_ids:
488
- return [], [], "None of the selected animes are in the model vocabulary!"
489
-
490
- # Hentai filtresi özel durumu
491
- filtered_pool = self.get_filtered_anime_pool(filters)
492
- if filtered_pool is not None:
493
- return self._get_recommendations_from_pool(favorite_anime_ids, filtered_pool, num_recommendations, filters)
494
-
495
- # Normal öneriler
496
- target_len = 128
497
- padded = converted_ids + [0] * (target_len - len(converted_ids))
498
- input_tensor = torch.tensor(padded, dtype=torch.long).unsqueeze(0)
499
-
500
- max_predictions = min(75, len(inverted_smap)) # Reduced from 125
501
-
502
- with torch.no_grad():
503
- logits = self.model(input_tensor)
504
- last_logits = logits[:, -1, :]
505
- top_scores, top_indices = torch.topk(last_logits, k=max_predictions, dim=1)
506
-
507
- recommendations = []
508
- scores = []
509
-
510
- for idx, score in zip(top_indices.numpy()[0], top_scores.detach().numpy()[0]):
511
- if idx in inverted_smap:
512
- anime_id = inverted_smap[idx]
513
-
514
- if anime_id in favorite_anime_ids:
515
- continue
516
-
517
- if str(anime_id) in self.id_to_anime:
518
- # Filtreleme kontrolü
519
- if filters and not self._should_include_anime(anime_id, filters):
520
- continue
521
-
522
- anime_data = self.id_to_anime.get(str(anime_id))
523
- anime_name = anime_data[0] if isinstance(anime_data, list) and len(anime_data) > 0 else str(anime_data)
524
-
525
- # Lazy loading ile image ve mal url al
526
- image_url = self.get_anime_image_url(anime_id)
527
- mal_url = self.get_anime_mal_url(anime_id)
528
-
529
- recommendations.append({
530
- 'id': anime_id,
531
- 'name': anime_name,
532
- 'score': float(score),
533
- 'image_url': image_url,
534
- 'mal_url': mal_url,
535
- 'genres': self.get_anime_genres(anime_id)
536
- })
537
- scores.append(float(score))
538
-
539
- if len(recommendations) >= num_recommendations:
540
- break
541
-
542
- # Memory cleanup
543
- del logits, last_logits, top_scores, top_indices
544
- gc.collect()
545
-
546
- return recommendations, scores, f"Found {len(recommendations)} recommendations!"
547
-
548
- except Exception as e:
549
- return [], [], f"Error during prediction: {str(e)}"
550
-
551
- def _get_recommendations_from_pool(self, favorite_anime_ids, anime_pool, num_recommendations, filters):
552
- """Önceden filtrelenmiş anime havuzundan öneriler alır"""
553
- try:
554
- smap = self.dataset
555
- converted_ids = []
556
- for anime_id in favorite_anime_ids:
557
- if anime_id in smap:
558
- converted_ids.append(smap[anime_id])
559
-
560
- if not converted_ids:
561
- return [], [], "None of the selected animes are in the model vocabulary!"
562
-
563
- target_len = 128
564
- padded = converted_ids + [0] * (target_len - len(converted_ids))
565
- input_tensor = torch.tensor(padded, dtype=torch.long).unsqueeze(0)
566
-
567
- with torch.no_grad():
568
- logits = self.model(input_tensor)
569
- last_logits = logits[:, -1, :]
570
-
571
- # Anime havuzundaki her anime için skor hesapla
572
- anime_scores = []
573
- for anime_id in anime_pool:
574
- if anime_id in favorite_anime_ids:
575
- continue
576
-
577
- if anime_id in smap:
578
- model_id = smap[anime_id]
579
- if model_id < last_logits.shape[1]:
580
- score = last_logits[0, model_id].item()
581
- anime_scores.append((anime_id, score))
582
-
583
- # Skorlara göre sırala
584
- anime_scores.sort(key=lambda x: x[1], reverse=True)
585
-
586
- recommendations = []
587
- for anime_id, score in anime_scores[:num_recommendations]:
588
- if str(anime_id) in self.id_to_anime:
589
- anime_data = self.id_to_anime.get(str(anime_id))
590
- anime_name = anime_data[0] if isinstance(anime_data, list) and len(anime_data) > 0 else str(anime_data)
591
-
592
- recommendations.append({
593
- 'id': anime_id,
594
- 'name': anime_name,
595
- 'score': float(score),
596
- 'image_url': self.get_anime_image_url(anime_id),
597
- 'mal_url': self.get_anime_mal_url(anime_id),
598
- 'genres': self.get_anime_genres(anime_id)
599
- })
600
-
601
- # Memory cleanup
602
- del logits, last_logits
603
- gc.collect()
604
-
605
- return recommendations, [r['score'] for r in recommendations], f"Found {len(recommendations)} filtered recommendations!"
606
-
607
- except Exception as e:
608
- return [], [], f"Error during filtered prediction: {str(e)}"
609
-
610
- def _should_include_anime(self, anime_id, filters):
611
- """Filtrelere göre anime'nin dahil edilip edilmeyeceğini kontrol eder"""
612
- if 'blacklisted_animes' in filters:
613
- if anime_id in filters['blacklisted_animes']:
614
- return False
615
-
616
- type_seq_info = self.id_to_type_seq.get(str(anime_id))
617
- if not type_seq_info or len(type_seq_info) < 2:
618
- return True
619
-
620
- anime_type = type_seq_info[0]
621
- is_sequel = type_seq_info[1]
622
- is_hentai = type_seq_info[2]
623
-
624
- # Sequel filtresi
625
- if 'show_sequels' in filters:
626
- if not filters['show_sequels'] and is_sequel:
627
- return False
628
-
629
- # Hentai filtresi
630
- if 'show_hentai' in filters:
631
- if filters['show_hentai']:
632
- if not is_hentai:
633
- return False
634
- else:
635
- if is_hentai:
636
- return False
637
-
638
- # Tür filtreleri
639
- if 'show_movies' in filters:
640
- if not filters['show_movies'] and anime_type == 'MOVIE':
641
- return False
642
-
643
- if 'show_tv' in filters:
644
- if not filters['show_tv'] and anime_type == 'TV':
645
- return False
646
-
647
- if 'show_ova' in filters:
648
- if not filters['show_ova'] and anime_type in ['ONA', 'OVA', 'SPECIAL']:
649
- return False
650
-
651
- return True
652
-
653
- recommendation_system = None
654
-
655
- @app.route('/')
656
- def index():
657
- if recommendation_system is None:
658
- return render_template('error.html', error="Recommendation system not initialized. Please check server logs.")
659
-
660
- animes = recommendation_system.get_all_animes()
661
- return render_template('index.html', animes=animes)
662
-
663
- @app.route('/api/search_animes')
664
- def search_animes():
665
- query = request.args.get('q', '').lower()
666
- animes = []
667
-
668
- # Sadece ilk 200 anime'yi arama - performance için
669
- count = 0
670
- for k, v in recommendation_system.id_to_anime.items():
671
- if count >= 200:
672
- break
673
-
674
- anime_names = v if isinstance(v, list) else [v]
675
- match_found = False
676
-
677
- for name in anime_names:
678
- if query in name.lower():
679
- match_found = True
680
- break
681
-
682
- if not query or match_found:
683
- main_name = anime_names[0] if anime_names else "Unknown"
684
- animes.append((int(k), main_name))
685
- count += 1
686
-
687
- animes.sort(key=lambda x: x[1])
688
- return jsonify(animes)
689
-
690
- @app.route('/api/add_favorite', methods=['POST'])
691
- def add_favorite():
692
- if 'favorites' not in session:
693
- session['favorites'] = []
694
-
695
- data = request.get_json()
696
- anime_id = int(data['anime_id'])
697
-
698
- if anime_id not in session['favorites']:
699
- # Maksimum 20 favori anime (memory için)
700
- if len(session['favorites']) >= 20:
701
- return jsonify({'success': False, 'message': 'Maximum 20 favorite animes allowed'})
702
-
703
- session['favorites'].append(anime_id)
704
- session.modified = True
705
- return jsonify({'success': True})
706
- else:
707
- return jsonify({'success': False})
708
-
709
- @app.route('/api/remove_favorite', methods=['POST'])
710
- def remove_favorite():
711
- if 'favorites' not in session:
712
- session['favorites'] = []
713
-
714
- data = request.get_json()
715
- anime_id = int(data['anime_id'])
716
-
717
- if anime_id in session['favorites']:
718
- session['favorites'].remove(anime_id)
719
- session.modified = True
720
- return jsonify({'success': True})
721
- else:
722
- return jsonify({'success': False})
723
-
724
- @app.route('/api/clear_favorites', methods=['POST'])
725
- def clear_favorites():
726
- session['favorites'] = []
727
- session.modified = True
728
- return jsonify({'success': True})
729
-
730
- @app.route('/api/get_favorites')
731
- def get_favorites():
732
- if 'favorites' not in session:
733
- session['favorites'] = []
734
-
735
- favorite_animes = []
736
- for anime_id in session['favorites']:
737
- if str(anime_id) in recommendation_system.id_to_anime:
738
- anime_data = recommendation_system.id_to_anime.get(str(anime_id))
739
- anime_name = anime_data[0] if isinstance(anime_data, list) and len(anime_data) > 0 else str(anime_data)
740
- favorite_animes.append({'id': anime_id, 'name': anime_name})
741
-
742
- return jsonify(favorite_animes)
743
-
744
-
745
- @app.route('/api/get_recommendations', methods=['POST'])
746
- def get_recommendations():
747
- if 'favorites' not in session or not session['favorites']:
748
- return jsonify({'success': False, 'message': 'Please add some favorite animes first!'})
749
-
750
- data = request.get_json() or {}
751
- filters = data.get('filters', {})
752
-
753
- # Blacklist bilgisini ekle
754
- blacklisted_animes = data.get('blacklisted_animes', [])
755
- if blacklisted_animes:
756
- filters['blacklisted_animes'] = blacklisted_animes
757
-
758
- recommendations, scores, message = recommendation_system.get_recommendations(
759
- session['favorites'],
760
- filters=filters
761
- )
762
-
763
- if recommendations:
764
- return jsonify({
765
- 'success': True,
766
- 'recommendations': recommendations,
767
- 'message': message
768
- })
769
- else:
770
- return jsonify({'success': False, 'message': message})
771
-
772
-
773
- @app.route('/api/mal_logo')
774
- def get_mal_logo():
775
- # MyAnimeList logo URL'ini döndür
776
- return jsonify({
777
- 'success': True,
778
- 'logo_url': 'https://cdn.myanimelist.net/img/sp/icon/apple-touch-icon-256.png'
779
- })
780
-
781
-
782
- def main():
783
- global recommendation_system
784
-
785
- args.num_items = 15687
786
-
787
- import gdown
788
- import os
789
-
790
- file_ids = {
791
- "1C6mdjblhiWGhRgbIk5DP2XCc4ElS9x8p": "pretrained_bert.pth",
792
- "1J1RmuJE5OjZUO0z1irVb2M-xnvuVvvHR": "animes.json",
793
- "1xGxUCbCDUnbdnJa6Ab8wgM9cpInpeQnN": "dataset.pkl",
794
- "1PtB6o_91tNWAb4zN0xj-Kf8SKvVAJp1c": "id_to_url.json",
795
- "1xVfTB_CmeYEqq6-l_BkQXo-QAUEyBfbW": "anime_to_malurl.json",
796
- "1zMbL9TpCbODKfVT5ahiaYILlnwBZNJc1": "anime_to_typenseq.json",
797
- "1LLMRhYyw82GOz3d8SUDZF9YRJdybgAFA": "id_to_genres.json"
798
- }
799
-
800
- def download_from_gdrive(file_id, output_path):
801
- url = f"https://drive.google.com/uc?id={file_id}"
802
- try:
803
- print(f"Downloading: {file_id}")
804
- gdown.download(url, output_path, quiet=False)
805
- print(f"Downloaded: {output_path}")
806
- return True
807
- except Exception as e:
808
- print(f"Error: {e}")
809
- return False
810
-
811
- for key, value in file_ids.items():
812
- if os.path.isfile(value):
813
- continue
814
- download_from_gdrive(key, value)
815
-
816
- try:
817
- images_path = "id_to_url.json"
818
- mal_urls_path = "anime_to_malurl.json"
819
- type_seq_path = "anime_to_typenseq.json"
820
-
821
- if not os.path.exists(images_path):
822
- print(f"Warning: {images_path} not found. Images will not be displayed.")
823
-
824
- if not os.path.exists(mal_urls_path):
825
- print(f"Warning: {mal_urls_path} not found. MAL links will not be available.")
826
-
827
- recommendation_system = AnimeRecommendationSystem(
828
- "pretrained_bert.pth",
829
- "dataset.pkl",
830
- "animes.json",
831
- images_path,
832
- mal_urls_path,
833
- type_seq_path,
834
- "id_to_genres.json"
835
- )
836
- print("Recommendation system initialized successfully!")
837
- except Exception as e:
838
- print(f"Failed to initialize recommendation system: {e}")
839
- sys.exit(1)
840
-
841
- app.run(debug=False, host='0.0.0.0', port=5000)
842
-
843
-
844
- if __name__ == "__main__":
845
- main()