ssenaay commited on
Commit
2f1a64f
·
verified ·
1 Parent(s): 92b5f25

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +127 -737
app.py CHANGED
@@ -6,60 +6,26 @@ from sentence_transformers import SentenceTransformer, util
6
  import torch
7
  import re
8
 
9
- print("--- Film Öneri Sistemi Başlatılıyor (Orijinal Görsel Tasarım Entegrasyonu) ---")
10
-
11
- # --- ADIM 1: Veri Seti Yükleniyor ve Keşfediliyor ---
12
- print("ADIM 1: Veri Seti Yükleniyor ve Keşfediliyor...")
13
 
14
  csv_file_name = "imdb-top-rated-movies-user-rated.csv"
15
  file_path = os.path.join(".", csv_file_name)
16
 
17
  if not os.path.exists(file_path):
18
- print(f"HATA: '{csv_file_name}' dosyası '{file_path}' yolunda bulunamadı. Lütfen CSV dosyasını Space'e yüklediğinizden emin olun.")
19
- print("DEMO AMAÇLI SAHTE CSV OLUŞTURULUYOR...")
20
- # Test amaçlı minimal bir sahte DataFrame oluştur
21
- dummy_data = {
22
- 'Title': ["Django Unchained", "The Departed", "Pulp Fiction", "Inception", "The Dark Knight", "Forrest Gump", "The Matrix", "Interstellar", "Spirited Away", "Whiplash"],
23
- 'IMDb Rating': [8.5, 8.5, 8.9, 8.8, 9.0, 8.8, 8.7, 8.6, 8.6, 8.5],
24
- 'Tags': ["Drama, Western, Comedy", "Crime, Drama, Thriller", "Crime, Drama", "Action, Adventure, Sci-Fi", "Action, Crime, Drama", "Drama, Romance", "Action, Sci-Fi", "Adventure, Drama, Sci-Fi", "Animation, Adventure, Family", "Drama, Music"],
25
- 'Director': ["Quentin Tarantino", "Martin Scorsese", "Quentin Tarantino", "Christopher Nolan", "Christopher Nolan", "Robert Zemeckis", "Lana Wachowski, Lilly Wachowski", "Christopher Nolan", "Hayao Miyazaki", "Damien Chazelle"],
26
- 'Stars': ["Jamie Foxx, Christoph Waltz, Leonardo DiCaprio", "Leonardo DiCaprio, Matt Damon, Jack Nicholson", "John Travolta, Uma Thurman, Samuel L. Jackson", "Leonardo DiCaprio, Joseph Gordon-Levitt, Elliot Page", "Christian Bale, Heath Ledger, Aaron Eckhart", "Tom Hanks, Robin Wright, Gary Sinise", "Keanu Reeves, Laurence Fishburne, Carrie-Anne Moss", "Matthew McConaughey, Anne Hathaway, Jessica Chastain", "Daveigh Chase, Suzanne Pleshette, Jason Marsden", "Miles Teller, J.K. Simmons, Melissa Benoist"],
27
- 'Votes': ["1,800,000 Oy", "1,500,000 Oy", "2,100,000 Oy", "2,300,000 Oy", "2,800,000 Oy", "2,200,000 Oy", "2,000,000 Oy", "1,700,000 Oy", "1,100,000 Oy", "900,000 Oy"],
28
- 'Description': [
29
- "With the help of a German bounty hunter, a freed slave sets out to rescue his wife from a brutal Mississippi plantation owner.",
30
- "An undercover state cop and a mole in the police force try to identify each other.",
31
- "The lives of two mob hitmen, a boxer, a gangster's wife, and a pair of diner bandits intertwine in four tales of violence and redemption.",
32
- "A thief who steals corporate secrets through the use of dream-sharing technology is given the inverse task of planting an idea into the mind of a C.E.O.",
33
- "When the menace known as the Joker wreaks havoc and chaos on the people of Gotham, Batman must accept one of the greatest psychological and physical tests of his ability to fight injustice.",
34
- "The presidencies of Kennedy and Johnson, the Vietnam War, the Watergate scandal and other historical events unfold from the perspective of an Alabama man with an IQ of 75.",
35
- "A computer hacker learns from mysterious rebels about the true nature of his reality and his role in the war against its controllers.",
36
- "A team of explorers travel through a wormhole in space in an attempt to ensure humanity's survival.",
37
- "During her family's move to the suburbs, a sullen 10-year-old girl wanders into a world ruled by gods, witches, and spirits, and where humans are changed into beasts.",
38
- "A promising young drummer enrolls at a cut-throat music conservatory where his unorthodox instructor pushes him to the breaking point."
39
- ],
40
- 'Poster URL': ["", "", "", "", "", "", "", "", "", ""] # Demo için boş bırakıldı, gerçek poster URL'leri buraya gelebilir
41
- }
42
- df = pd.DataFrame(dummy_data)
43
- df.to_csv(file_path, index=False)
44
- print(f"Sahte CSV '{csv_file_name}' oluşturuldu ve yüklendi. Toplam {len(df)} film bulundu.")
45
- else:
46
- try:
47
- df = pd.read_csv(file_path)
48
- print(f"'{csv_file_name}' başarıyla yüklendi. Toplam {len(df)} film bulundu.")
49
- except Exception as e:
50
- print(f"HATA: CSV dosyası yüklenirken hata oluştu: {e}")
51
- exit(1)
52
- print("ADIM 1: Veri Seti Keşfi Tamamlandı.")
53
-
54
-
55
- # --- ADIM 2: Veri Temizliği ve Ön İşleme ---
56
- print("\nADIM 2: Veri Temizliği ve Ön İşleme Başlıyor...")
57
 
58
- df_filtered = df[['Title', 'IMDb Rating', 'Tags', 'Director', 'Stars', 'Votes', 'Description', 'Poster URL']].copy()
 
 
 
 
 
59
 
60
- df_filtered['Stars'].fillna('', inplace=True)
61
- df_filtered['Description'].fillna('', inplace=True)
62
- df_filtered['Poster URL'].fillna('', inplace=True)
 
63
 
64
  genre_mapping = {
65
  'action': ['action', 'action epic', 'gun fu', 'one-person army action', 'car action', 'kung fu', 'martial arts', 'martial-arts'],
@@ -91,7 +57,7 @@ def map_to_main_genres(tag_list):
91
  main_genres = set()
92
  for tag in tag_list:
93
  if tag in reverse_genre_map:
94
- main_genres.add(reverse_genre_map[tag])
95
  return list(main_genres)
96
 
97
  def clean_and_split(text_series):
@@ -99,35 +65,30 @@ def clean_and_split(text_series):
99
  return []
100
  item = str(text_series)
101
  item = item.replace('"', '').replace("'", '').strip()
102
- item = item.replace('sci, fi', 'sci-fi')
103
  split_items = [s.strip().lower() for s in item.split(',') if s.strip()]
104
  return split_items
105
 
106
-
107
  df_filtered['Tags_cleaned_raw'] = df_filtered['Tags'].apply(clean_and_split)
108
  df_filtered['Director_cleaned'] = df_filtered['Director'].apply(clean_and_split)
109
  df_filtered['Stars_cleaned'] = df_filtered['Stars'].apply(clean_and_split)
110
-
111
  df_filtered['Tags_cleaned'] = df_filtered['Tags_cleaned_raw'].apply(map_to_main_genres)
112
 
113
-
114
  def convert_votes_to_numeric(votes_str):
115
  if isinstance(votes_str, str):
116
- # " Oy" son ekini kaldır
117
- votes_str = votes_str.replace(" Oy", "").replace(",", "")
118
  if 'K' in votes_str:
119
  return float(votes_str.replace('K', '')) * 1000
120
  elif 'M' in votes_str:
121
  return float(votes_str.replace('M', '')) * 1_000_000
122
- try:
123
  return float(votes_str)
124
  except ValueError:
125
- return np.nan
126
 
127
  df_filtered['Votes_numeric'] = df_filtered['Votes'].apply(convert_votes_to_numeric)
128
- # Orijinal 'Votes' sütunu string olarak tutulsun, sayısal değer 'Votes_numeric'te.
129
- # df_filtered.drop('Votes', axis=1, inplace=True) # Bu satırı kaldırın
130
- df_filtered.dropna(subset=['Votes_numeric'], inplace=True)
131
 
132
  df_filtered['Combined_Text'] = df_filtered['Title'] + ". " + \
133
  df_filtered['Description'] + ". " + \
@@ -135,45 +96,26 @@ df_filtered['Combined_Text'] = df_filtered['Title'] + ". " + \
135
  df_filtered['Director_cleaned'].apply(lambda x: ", ".join(x)) + ". " + \
136
  df_filtered['Stars_cleaned'].apply(lambda x: ", ".join(x))
137
 
138
- print("\nADIM 2: Veri Temizliği ve Ön İşleme Tamamlandı.")
139
-
140
-
141
- # --- ADIM 3: NLP Modelini Yükleme ve ÖNCEDEN OLUŞTURULMUŞ Embedding'leri Yükleme ---
142
- print("\nADIM 3: NLP Modelini Yükleniyor ve Önceden Oluşturulmuş Embedding'ler Yükleniyor...")
143
-
144
  model_name = 'sentence-transformers/all-MiniLM-L6-v2'
145
- sentence_model = None # Modeli başlangıçta None olarak ayarla
146
  try:
147
  sentence_model = SentenceTransformer(model_name)
148
  print(f"'{model_name}' modeli başarıyla yüklendi.")
149
  except Exception as e:
150
  print(f"HATA: Sentence Transformer modeli yüklenirken hata oluştu: {e}")
151
- print("NLP modeli yüklenemedi, arama metni özelliği devre dışı bırakılacak.")
152
 
153
  embeddings_file_path = os.path.join(".", "film_embeddings.npy")
154
  if not os.path.exists(embeddings_file_path):
155
- print(f"HATA: '{embeddings_file_path}' dosyası bulunamadı. Lütfen Space'e yüklediğinizden emin olun.")
156
- print("DEMO AMAÇLI SAHTE EMBEDDING'LER OLUŞTURULUYOR...")
157
- # Sahte embedding'ler oluştur - her film için rastgele bir vektör
158
- dummy_embeddings = np.random.rand(len(df_filtered), 384) # all-MiniLM-L6-v2 boyutu 384
159
- film_embeddings = torch.from_numpy(dummy_embeddings).float()
160
- np.save(embeddings_file_path, dummy_embeddings) # Sonraki çalıştırmalar için kaydet
161
- print("Film embedding'leri başarıyla 'film_embeddings.npy' dosyasından oluşturuldu.")
162
- else:
163
- try:
164
- film_embeddings = torch.from_numpy(np.load(embeddings_file_path)).float() # Float tipini sağla
165
- print("Film embedding'leri başarıyla 'film_embeddings.npy' dosyasından yüklendi.")
166
- except Exception as e:
167
- print(f"HATA: film_embeddings.npy yüklenirken hata oluştu: {e}")
168
- exit(1)
169
-
170
- print("ADIM 3: NLP Modelini Yükleme ve Film Embedding'lerini Oluşturma Tamamlandı.")
171
-
172
 
173
- # --- ADIM 4: Film Öneri Sistemi Mantığını Oluşturma (Popüler Yönetmen/Oyuncu Sıralaması Eklendi) ---
174
- print("\nADIM 4: Film Öneri Sistemi Mantığı Oluşturuluyor...")
 
 
 
 
175
 
176
- # --- YENİ EKLENTİ: Popüler Yönetmen/Oyuncu Listelerini Oluşturma ---
177
  director_popularity = {}
178
  for index, row in df_filtered.iterrows():
179
  for director in row['Director_cleaned']:
@@ -184,30 +126,17 @@ for index, row in df_filtered.iterrows():
184
  for star in row['Stars_cleaned']:
185
  star_popularity[star] = star_popularity.get(star, 0) + row['Votes_numeric']
186
 
187
- # all_tags listesini, sadece eşlenmiş ana türlerden oluşturalım
188
  all_tags = sorted(list(set([tag for sublist in df_filtered['Tags_cleaned'] for tag in sublist if tag in genre_mapping])))
189
-
190
- # Popülerliğe göre sıralanmış yönetmen ve oyuncu listeleri
191
  all_directors = sorted(list(director_popularity.keys()), key=lambda d: director_popularity[d], reverse=True)
192
  all_stars = sorted(list(star_popularity.keys()), key=lambda s: star_popularity[s], reverse=True)
193
- # --- YENİ EKLENTİ SONU ---
194
 
195
  def get_movie_recommendations(selected_tags, selected_directors, selected_stars, min_imdb_rating_slider, num_recommendations_slider, search_text=""):
196
 
197
- print(f"\n--- Öneri İsteği ---")
198
- print(f"Seçilen Türler: {selected_tags}")
199
- print(f"Seçilen Yönetmenler: {selected_directors}")
200
- print(f"Seçilen Oyuncular: {selected_stars}")
201
- print(f"Minimum IMDb Puanı: {min_imdb_rating_slider}")
202
- print(f"Öneri Sayısı: {num_recommendations_slider}")
203
- print(f"Arama Metni: '{search_text}'")
204
-
205
  selected_tags_list = list(selected_tags) if selected_tags else []
206
  selected_directors_list = list(selected_directors) if selected_directors else []
207
  selected_stars_list = list(selected_stars) if selected_stars else []
208
 
209
  recommendations_df = df_filtered.copy()
210
-
211
  recommendations_df = recommendations_df[recommendations_df['IMDb Rating'] >= min_imdb_rating_slider]
212
 
213
  if selected_tags_list:
@@ -225,21 +154,10 @@ def get_movie_recommendations(selected_tags, selected_directors, selected_stars,
225
  recommendations_df['Stars_cleaned'].apply(lambda x: any(star in x for star in selected_stars_list))
226
  ]
227
 
228
- if search_text and len(recommendations_df) > 0 and sentence_model is not None:
229
  try:
230
  query_embedding = sentence_model.encode(search_text, convert_to_tensor=True)
231
- except Exception as e:
232
- print(f"HATA: Arama metni embedding'i oluşturulurken hata oluştu: {e}")
233
- return "Arama metni işlenirken bir hata oluştu. Lütfen tekrar deneyin."
234
-
235
- filtered_indices = recommendations_df.index.tolist()
236
- if not filtered_indices or len(film_embeddings) == 0:
237
- return "Filtreleme sonrası film bulunamadı."
238
-
239
- try:
240
- if filtered_indices and (max(filtered_indices) >= film_embeddings.shape[0] or min(filtered_indices) < 0):
241
- return "Benzerlik hesaplanırken bir hata oluştu (dizin hatası). Lütfen tekrar deneyin."
242
-
243
  current_film_embeddings = film_embeddings[filtered_indices]
244
  cosine_scores = util.cos_sim(query_embedding, current_film_embeddings)[0]
245
  recommendations_df['Similarity_Score'] = cosine_scores.cpu().numpy()
@@ -248,12 +166,9 @@ def get_movie_recommendations(selected_tags, selected_directors, selected_stars,
248
  ascending=[False, False, False]
249
  ).reset_index(drop=True)
250
  except Exception as e:
251
- print(f"HATA: NLP benzerlik hesaplanırken hata oluştu: {e}")
252
- return "Benzerlik hesaplanırken bir hata oluştu. Lütfen tekrar deneyin."
253
- elif search_text and sentence_model is None:
254
- print("NLP modeli yüklenemediği için arama metni filtrelemesi atlandı.")
255
 
256
- if not search_text or sentence_model is None:
257
  recommendations_df = recommendations_df.sort_values(
258
  by=['IMDb Rating', 'Votes_numeric'],
259
  ascending=[False, False]
@@ -263,625 +178,141 @@ def get_movie_recommendations(selected_tags, selected_directors, selected_stars,
263
 
264
  if top_recommendations.empty:
265
  return """
266
- <div class="no-results-card">
267
- <div class="no-results-icon">🎬</div>
268
- <h2 class="no-results-title">Sonuç Bulunamadı</h2>
269
- <p class="no-results-message">Seçtiğiniz kriterlere uygun film bulunamadı. Filtreleri değiştirerek tekrar deneyin.</p>
270
  </div>
271
  """
272
  else:
273
- html_output = "<div class='recommendations-grid'>"
274
  for idx, row in top_recommendations.iterrows():
275
  directors_str = ", ".join([d.title() for d in row['Director_cleaned']])
276
  stars_str = ", ".join([s.title() for s in row['Stars_cleaned']])
277
- tags_str_list = [tag.title() for tag in row['Tags_cleaned']]
278
 
279
  similarity_info = ""
280
- if 'Similarity_Score' in row and search_text and sentence_model is not None:
281
  sim_percentage = int(row['Similarity_Score'] * 100)
282
  similarity_info = f"""
283
- <span style="color: #bbb; font-size: 0.9em; margin-left: 10px;">
284
- (Benzerlik: {sim_percentage}%)
285
- </span>
286
- """
287
-
288
- # Poster veya yer tutucu HTML'i
289
- poster_content = ""
290
- if row['Poster URL'] and row['Poster URL'].strip() != "":
291
- poster_content = f"""
292
- <img src="{row['Poster URL']}" alt="{row['Title']}" loading="lazy" style="width: 100%; height: 100%; object-fit: cover; border-radius: 8px;">
293
- """
294
- else:
295
- # Orijinal görseldeki gibi film şeridi simgesi ve başlığın kısaltması
296
- short_title = row['Title'][:10] + "..." if len(row['Title']) > 13 else row['Title']
297
- poster_content = f"""
298
- <div class="poster-placeholder">
299
- <svg viewBox="0 0 24 24" width="60" height="60" stroke="#a0a0a0" stroke-width="1.5" fill="none" stroke-linecap="round" stroke-linejoin="round" class="clapboard-icon">
300
- <path d="M4 14.899A2.121 2.121 0 0 1 4 12.02c5.688 0 8.007-4.135 12-4.135 1.052-.007 1.954.184 2.766.527M12.63 7.885c2.735-2.22 4.673-4.22 7.37-4.22C21.49 3.665 22 4.173 22 4.802v14.4c0 .629-.51 1.137-1.137 1.137-.996 0-1.898-.24-2.71-.62-3.83-1.742-6.533-2.612-9.255-2.612-1.07 0-2.002.26-2.825.75-.487.27-.887.59-1.21.95M4 14.899v4.321c0 .629-.51 1.137-1.137 1.137-.996 0-1.898-.24-2.71-.62-3.83-1.742-6.533-2.612-9.255-2.612-1.07 0-2.002.26-2.825.75-.487.27-.887.59-1.21.95M4 14.899c-2.735-2.22-4.673-4.22-7.37-4.22C2.51 3.665 2 4.173 2 4.802v14.4c0 .629.51 1.137 1.137 1.137.996 0 1.898-.24 2.71-.62 3.83-1.742 6.533-2.612 9.255-2.612 1.07 0 2.002.26 2.825.75.487.27.887.59 1.21.95" />
301
- <rect x="3" y="1" width="18" height="2" rx="1" ry="1"></rect>
302
- <rect x="3" y="1" width="4" height="2" rx="1" ry="1"></rect>
303
- <rect x="7" y="1" width="4" height="2" rx="1" ry="1"></rect>
304
- <rect x="11" y="1" width="4" height="2" rx="1" ry="1"></rect>
305
- <rect x="15" y="1" width="4" height="2" rx="1" ry="1"></rect>
306
- <path d="M5 21H19a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v11a2 2 0 0 0 2 2z"></path>
307
- </svg>
308
- <span class="poster-title-placeholder">{short_title}</span>
309
- <span class="no-poster-text">Poster Yok</span>
310
  </div>
311
  """
 
 
 
 
 
 
 
 
 
 
312
 
313
  html_output += f"""
314
- <div class="movie-card">
315
- <div class="movie-card-left-panel">
316
- {poster_content}
 
317
  </div>
318
- <div class="movie-card-info-panel">
319
- <h3 class="movie-title-display">{row['Title']}</h3>
320
- <div class="rating-and-votes">
321
- <span class="rating-badge">⭐ {row['IMDb Rating']:.1f}</span>
322
- <div class="votes-info">
323
- <svg viewBox="0 0 24 24" width="16" height="16" stroke="#9e9e9e" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect><line x1="16" y1="17" x2="16" y2="10"></line><line x1="8" y1="17" x2="8" y2="10"></line><line x1="12" y1="17" x2="12" y2="10"></line><line x1="12" y1="3" x2="12" y2="21"></line></svg>
324
- <span>{row['Votes']}</span>
325
- {similarity_info}
326
  </div>
 
327
  </div>
328
- <div class="movie-details-row">
329
- <span class="detail-label">Yönetmen:</span>
330
- <span class="detail-value">{directors_str if directors_str else 'Bilinmiyor'}</span>
 
 
331
  </div>
332
- <div class="movie-details-row">
333
- <span class="detail-label">Oyuncular:</span>
334
- <span class="detail-value">{stars_str if stars_str else 'Bilinmiyor'}</span>
 
335
  </div>
336
- <div class="genre-tags-container">
337
- {''.join([f'<span class="genre-tag">{tag}</span>' for tag in tags_str_list])}
 
 
 
 
 
 
338
  </div>
339
  </div>
340
  </div>
341
  """
342
- html_output += "</div>"
343
  return html_output
344
 
345
- print("\nADIM 4: Film Öneri Sistemi Mantığı Oluşturuldu.")
346
-
347
-
348
- # --- ADIM 5: Gradio Web Arayüzü Oluşturma ---
349
- print("\nADIM 5: Gradio Web Arayüzü Oluşturuluyor (Orijinal Görsel Entegrasyonu ile)...")
350
-
351
- # Özel tema oluştur - tüm mor/mavi renkleri turuncu yap
352
- # Bu tema Gradio bileşenlerinin genel renklerini etkiler.
353
- custom_theme = gr.themes.Base(
354
- primary_hue=gr.themes.colors.orange, # Ana renk turuncu tonları
355
- secondary_hue=gr.themes.colors.orange, # İkincil renk turuncu tonları
356
- neutral_hue=gr.themes.colors.slate, # Nötr renkler (gri tonları)
357
- ).set(
358
- # Butonlar
359
- button_primary_background_fill="hsl(24, 88%, 50%)", # Turuncuya yakın renk
360
- button_primary_background_fill_hover="hsl(24, 88%, 40%)", # Daha koyu turuncu hover
361
- button_primary_text_color="white",
362
- # Checkbox'lar
363
- checkbox_label_background_fill="hsl(220, 10%, 20%)", # Koyu gri
364
- checkbox_label_background_fill_hover="hsl(220, 10%, 25%)",
365
- checkbox_label_background_fill_selected="hsl(24, 88%, 50%)", # Turuncu seçili
366
- checkbox_label_text_color="white",
367
- checkbox_label_text_color_selected="black", # Seçili metin siyah
368
- checkbox_label_border_color_selected="hsl(24, 88%, 50%)",
369
- # Dropdown, Slider, Textbox
370
- input_background_fill="hsl(220, 10%, 20%)", # Koyu gri
371
- input_border_color="hsl(220, 10%, 30%)",
372
- # input_text_color="white", # BU SATIR KALDIRILDI!
373
- slider_color="hsl(24, 88%, 50%)", # Slider dolgu rengi
374
- # Genel Gradio panelleri
375
- block_background_fill="hsl(220, 10%, 15%)", # Daha koyu panel arka planı
376
- border_color_accent="hsl(24, 88%, 50%)", # Turuncu vurgu kenarlığı
377
- )
378
-
379
-
380
- with gr.Blocks(theme=custom_theme, css="""
381
- @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');
382
-
383
- body {
384
- background-color: #0a0a0a !important; /* Genel sayfa arka planı daha koyu */
385
- color: #e0e0e0;
386
- font-family: 'Inter', sans-serif;
387
- }
388
-
389
- .gradio-container {
390
- max-width: 1300px !important;
391
- font-family: 'Inter', sans-serif !important;
392
- background-color: #0a0a0a !important;
393
- border-radius: 16px;
394
- overflow: hidden; /* Kenar yuvarlaklığı için */
395
- }
396
-
397
- /* Ana Başlık */
398
- h1 {
399
- color: #f39c12 !important; /* Turuncu */
400
- text-align: center;
401
- font-size: 40px !important;
402
- font-weight: 700 !important;
403
- margin-bottom: 5px !important;
404
- text-shadow: 0 0 10px rgba(243, 156, 18, 0.3);
405
- }
406
- .gr-markdown p {
407
- color: #d0d0d0;
408
- text-align: center;
409
- margin-top: 5px;
410
- font-size: 16px;
411
- }
412
-
413
- /* Bölüm Başlıkları (Önerilen Filmler, Film Tercihleri vb.) */
414
- h3 {
415
- color: #f39c12 !important;
416
- font-weight: 600 !important;
417
- font-size: 24px !important;
418
- margin-bottom: 20px !important;
419
- border-bottom: 2px solid #2a2a2a; /* Alt çizgi */
420
- padding-bottom: 10px;
421
- }
422
-
423
- /* Ana Düğme */
424
- .gr-button.gr-button-primary {
425
- background-color: #f39c12 !important; /* Turuncu */
426
- border: none !important;
427
- font-weight: 700 !important;
428
- font-size: 16px !important;
429
- padding: 14px 32px !important;
430
- border-radius: 8px !important;
431
- box-shadow: 0 4px 12px rgba(243, 156, 18, 0.4) !important;
432
- transition: all 0.3s ease !important;
433
- text-transform: uppercase !important;
434
- letter-spacing: 0.5px !important;
435
- color: white !important;
436
- }
437
- .gr-button.gr-button-primary:hover {
438
- background-color: #e67e22 !important; /* Daha koyu turuncu */
439
- transform: translateY(-2px) !important;
440
- box-shadow: 0 6px 16px rgba(243, 156, 18, 0.6) !important;
441
- }
442
-
443
- /* Tüm label başlıkları için turuncu arka plan */
444
- .gr-form label > span:first-child,
445
- .gr-box label > span:first-child,
446
- .gr-input-label > span,
447
- fieldset > legend > span {
448
- background-color: #f39c12 !important; /* Turuncu */
449
- color: white !important;
450
- padding: 6px 12px !important;
451
- border-radius: 6px !important;
452
- font-weight: 600 !important;
453
- display: inline-block !important;
454
- margin-bottom: 10px !important;
455
- box-shadow: 0 2px 6px rgba(243, 156, 18, 0.2);
456
- }
457
-
458
- /* Gradio Panelleri - Orijinal görseldeki gibi koyu, yuvarlak kenarlı */
459
- .gr-panel {
460
- background-color: #1c1c1c !important; /* Koyu gri */
461
- border: 1px solid #2a2a2a !important;
462
- border-radius: 12px !important;
463
- box-shadow: 0 4px 8px rgba(0,0,0,0.2);
464
- padding: 20px;
465
- }
466
-
467
- /* Giriş Alanları (Checkbox, Dropdown, Slider, Textbox) */
468
- .gr-checkbox-group, .gr-dropdown, .gr-slider, .gr-textbox {
469
- background-color: #242424 !important; /* Bir tık daha açık gri */
470
- border: 1px solid #3a3a3a !important;
471
- border-radius: 8px !important;
472
- padding: 15px !important;
473
- margin-bottom: 15px; /* Aralarında boşluk */
474
- }
475
-
476
- /* Checkbox Group Label'ları */
477
- .gr-checkbox-group label {
478
- background-color: #333333 !important; /* Normal checkbox arka planı */
479
- color: #ccc !important;
480
- border: 1px solid #444444 !important;
481
- border-radius: 5px !important;
482
- padding: 8px 12px !important;
483
- margin: 4px !important;
484
- transition: all 0.2s ease !important;
485
- font-weight: 500 !important;
486
- }
487
- .gr-checkbox-group input:checked + label {
488
- background-color: #f39c12 !important; /* Seçili turuncu */
489
- border-color: #f39c12 !important;
490
- color: white !important;
491
- font-weight: 600 !important;
492
- }
493
- .gr-checkbox-group label:hover {
494
- background-color: #444444 !important;
495
- border-color: #f39c12 !important;
496
- }
497
-
498
- /* Dropdown ve Textbox Giriş Alanları */
499
- .gr-input, .gr-textbox textarea, .gr-dropdown-container {
500
- background-color: #333333 !important;
501
- color: #e0e0e0 !important;
502
- border: 1px solid #444444 !important;
503
- border-radius: 6px !important;
504
- font-size: 15px !important;
505
- padding: 8px 12px !important;
506
- }
507
- .gr-input:focus, .gr-textbox textarea:focus, .gr-dropdown-container:focus-within {
508
  border-color: #f39c12 !important;
509
- box-shadow: 0 0 0 2px rgba(243, 156, 18, 0.3) !important;
510
  }
511
-
512
- /* Dropdown Menüleri */
513
- .gr-dropdown-menu {
514
- background-color: #2a2a2a !important;
515
- border: 1px solid #f39c12 !important;
516
- border-radius: 8px !important;
517
- box-shadow: 0 4px 12px rgba(0,0,0,0.4);
518
  }
519
- .gr-dropdown-item {
520
- color: #d0d0d0 !important;
521
- padding: 10px 15px !important;
522
- }
523
- .gr-dropdown-item:hover {
524
- background-color: #3a3a3a !important;
525
- color: #f39c12 !important;
526
  }
527
  .gr-dropdown-item.selected {
528
  background-color: #f39c12 !important;
529
- color: white !important;
530
- font-weight: 600 !important;
531
  }
532
-
533
- /* Slider */
534
- input[type="range"]::-webkit-slider-runnable-track {
535
- background: linear-gradient(to right, #f39c12 0%, #f39c12 var(--slider-value, 50%), #444444 var(--slider-value, 50%)) !important;
536
- height: 8px !important;
537
- border-radius: 4px !important;
538
  }
539
  input[type="range"]::-webkit-slider-thumb {
540
  background-color: #f39c12 !important;
541
- border: 2px solid white !important;
542
- width: 20px !important;
543
- height: 20px !important;
544
- margin-top: -6px !important;
545
- box-shadow: 0 2px 6px rgba(0,0,0,0.3);
546
- }
547
-
548
- /* No Results Card */
549
- .no-results-card {
550
- text-align: center;
551
- padding: 60px 20px;
552
- background-color: #242424;
553
- border-radius: 12px;
554
- border: 2px dashed #4a4a4a;
555
- margin-top: 20px;
556
- }
557
- .no-results-icon {
558
- font-size: 64px;
559
- margin-bottom: 20px;
560
- color: #6a6a6a;
561
- }
562
- .no-results-title {
563
- color: #f39c12;
564
- margin-bottom: 10px;
565
- font-size: 28px;
566
- font-weight: 700;
567
- }
568
- .no-results-message {
569
- color: #a0a0a0;
570
- font-size: 16px;
571
- }
572
-
573
- /* Film Kartları Grid Düzeni */
574
- .recommendations-grid {
575
- display: grid;
576
- grid-template-columns: repeat(auto-fit, minmax(350px, 1fr)); /* Daha geniş kartlar */
577
- gap: 25px; /* Kartlar arası boşluk */
578
- padding: 20px 0;
579
- }
580
-
581
- /* Tek Film Kartı - Orijinal Görsel Stil */
582
- .movie-card {
583
- display: flex;
584
- background-color: #242424; /* Koyu gri kart arka planı */
585
- border-radius: 12px;
586
- overflow: hidden;
587
- box-shadow: 0 6px 20px rgba(0,0,0,0.5); /* Daha belirgin gölge */
588
- border: 2px solid transparent; /* Varsayılan şeffaf kenarlık */
589
- transition: all 0.3s ease;
590
- position: relative; /* Orange border için */
591
  }
592
-
593
- .movie-card::before {
594
- content: '';
595
- position: absolute;
596
- top: 0;
597
- left: 0;
598
- right: 0;
599
- bottom: 0;
600
- border: 2px solid transparent;
601
- border-radius: 12px;
602
- pointer-events: none;
603
- transition: border-color 0.3s ease;
604
- }
605
-
606
- .movie-card:hover::before {
607
- border-color: #f39c12; /* Hover'da turuncu kenarlık */
608
- }
609
-
610
- .movie-card-left-panel {
611
- flex-shrink: 0;
612
- width: 120px; /* Poster genişliği */
613
- height: auto; /* İçeriğe göre yükseklik */
614
- background-color: #1a1a1a; /* Daha koyu panel */
615
- border-right: 1px solid #333; /* Hafif ayırıcı */
616
- border-top-left-radius: 10px;
617
- border-bottom-left-radius: 10px;
618
- overflow: hidden;
619
- padding: 5px; /* İç boşluk */
620
- display: flex;
621
- align-items: center;
622
- justify-content: center;
623
- }
624
-
625
- /* Poster Yer Tutucu */
626
- .poster-placeholder {
627
- width: 100%;
628
- height: 100%; /* Parent'ı kadar yer kapla */
629
- display: flex;
630
- flex-direction: column;
631
- align-items: center;
632
- justify-content: center;
633
- text-align: center;
634
- color: #a0a0a0;
635
- font-size: 0.8em;
636
- line-height: 1.2;
637
- padding: 5px;
638
- background-color: #1a1a1a; /* Orijinal görseldeki gibi koyu arka plan */
639
- border-radius: 8px; /* Hafif yuvarlak köşeler */
640
- }
641
- .poster-placeholder .clapboard-icon {
642
- color: #a0a0a0; /* Gri ikon */
643
- width: 50px; /* Daha büyük ikon */
644
- height: 50px;
645
- margin-bottom: 5px;
646
- }
647
- .poster-placeholder .poster-title-placeholder {
648
- font-weight: 600;
649
- color: #d0d0d0; /* Daha açık başlık */
650
- font-size: 0.9em;
651
- margin-bottom: 3px;
652
- }
653
- .poster-placeholder .no-poster-text {
654
- font-size: 0.7em;
655
- color: #707070; /* Daha koyu "Poster Yok" metni */
656
- }
657
-
658
- .movie-card-info-panel {
659
- flex-grow: 1;
660
- padding: 15px 20px;
661
- display: flex;
662
- flex-direction: column;
663
- justify-content: flex-start; /* Üste hizala */
664
- }
665
-
666
- .movie-title-display {
667
- margin-top: 0;
668
- margin-bottom: 8px;
669
- color: white; /* Beyaz başlık */
670
- font-size: 20px; /* Daha büyük başlık */
671
- font-weight: 600;
672
- line-height: 1.3;
673
- }
674
-
675
- .rating-and-votes {
676
- display: flex;
677
- align-items: center;
678
- margin-bottom: 12px;
679
- gap: 15px; /* Puan ve Oy arasında boşluk */
680
- }
681
-
682
- .rating-badge {
683
- background-color: #28a745; /* Yeşil */
684
- color: white;
685
- padding: 5px 10px;
686
- border-radius: 6px; /* Yuvarlatılmış köşeler */
687
- font-weight: 600;
688
- font-size: 15px;
689
- display: inline-flex;
690
- align-items: center;
691
- gap: 5px;
692
- box-shadow: 0 2px 8px rgba(40, 167, 69, 0.3);
693
- }
694
- .rating-badge span { /* Yıldız ikonu için */
695
- font-size: 1em;
696
- }
697
-
698
- .votes-info {
699
- display: flex;
700
- align-items: center;
701
- color: #bbb;
702
- font-size: 14px;
703
- gap: 5px;
704
- background-color: #333; /* Arka plan rengi */
705
- padding: 5px 10px;
706
- border-radius: 6px;
707
- border: 1px solid #444;
708
- }
709
- .votes-info svg {
710
- color: #9e9e9e; /* İkon rengi */
711
- fill: #9e9e9e;
712
- stroke: #9e9e9e;
713
- }
714
-
715
- .movie-details-row {
716
- margin-bottom: 6px;
717
- display: flex;
718
- flex-wrap: wrap; /* Uzun isimlerde alt satıra geçiş */
719
- }
720
- .detail-label {
721
- color: #f39c12; /* Turuncu etiket */
722
- font-weight: 500;
723
- font-size: 14px;
724
- margin-right: 8px;
725
- white-space: nowrap; /* Etiket tek satırda kalsın */
726
- }
727
- .detail-value {
728
- color: #d0d0d0;
729
- font-size: 14px;
730
- }
731
-
732
- .genre-tags-container {
733
- display: flex;
734
- flex-wrap: wrap;
735
- gap: 8px;
736
- margin-top: 15px;
737
- }
738
- .genre-tag {
739
- background-color: #f39c12; /* Turuncu */
740
- color: white;
741
- padding: 6px 12px;
742
- border-radius: 20px; /* Yuvarlak hap şekli */
743
- font-size: 13px;
744
- font-weight: 500;
745
- white-space: nowrap;
746
- box-shadow: 0 2px 6px rgba(243, 156, 18, 0.2);
747
- }
748
-
749
- /* Scroll bar */
750
- ::-webkit-scrollbar {
751
- width: 10px;
752
- background-color: #1a1a1a;
753
- }
754
-
755
- ::-webkit-scrollbar-thumb {
756
- background-color: #f39c12;
757
- border-radius: 5px;
758
- }
759
-
760
- ::-webkit-scrollbar-thumb:hover {
761
- background-color: #e67e22;
762
- }
763
-
764
- /* Örnekler bölümü */
765
- .gr-examples {
766
- background-color: #1c1c1c !important;
767
- border: 1px solid #2a2a2a !important;
768
- border-radius: 12px !important;
769
- padding: 15px !important;
770
- }
771
-
772
- .gr-examples .gr-button {
773
- background-color: #333333 !important;
774
- color: #d0d0d0 !important;
775
- border: 1px solid #444444 !important;
776
- border-radius: 8px !important;
777
- font-weight: 500;
778
- padding: 8px 12px;
779
- }
780
-
781
- .gr-examples .gr-button:hover {
782
- background-color: #444444 !important;
783
- border-color: #f39c12 !important;
784
- color: #f39c12 !important;
785
- }
786
-
787
- /* Nasıl Kullanılır bölümü */
788
- .how-to-use-section {
789
- background-color: #1c1c1c;
790
- padding: 30px;
791
- border-radius: 16px;
792
- border: 1px solid #2a2a2a;
793
- margin-top: 30px;
794
- }
795
- .how-to-use-grid {
796
- display: grid;
797
- grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
798
- gap: 20px;
799
- margin-top: 20px;
800
- }
801
- .how-to-use-step {
802
- background-color: #242424;
803
- padding: 20px;
804
- border-radius: 12px;
805
- border-left: 4px solid #f39c12; /* Turuncu sol kenarlık */
806
- }
807
- .how-to-use-step h4 {
808
- color: #f39c12;
809
- margin-top: 0;
810
- font-size: 18px;
811
- font-weight: 600;
812
- }
813
- .how-to-use-step p {
814
- color: #d0d0d0;
815
- font-size: 14px;
816
- line-height: 1.6;
817
- text-align: left;
818
- }
819
- .pro-tip-box {
820
- margin-top: 30px;
821
- padding: 20px;
822
- background-color: rgba(243, 156, 18, 0.1);
823
- border-radius: 12px;
824
- border: 1px solid rgba(243, 156, 18, 0.3);
825
- }
826
- .pro-tip-box h4 {
827
- color: #f39c12;
828
- margin-top: 0;
829
- display: flex;
830
- align-items: center;
831
- gap: 10px;
832
- }
833
- .pro-tip-box p {
834
- color: #d0d0d0;
835
- font-size: 14px;
836
- line-height: 1.6;
837
- margin: 0;
838
- text-align: left;
839
- }
840
- .footer-info {
841
- text-align: center;
842
- margin-top: 30px;
843
- padding: 20px;
844
- color: #707070;
845
- font-size: 13px;
846
  }
847
-
848
  """) as demo:
849
-
850
  gr.Markdown(
851
  """
852
- # 🎬 CineMate Film Öneri Sistemi
853
-
854
- <p>Yapay Zeka Destekli Kişiselleştirilmiş Film Keşfi</p>
855
  """
856
  )
857
 
858
  with gr.Row():
859
- with gr.Column(scale=3):
860
- gr.Markdown("### 🎯 Sizin İçin Seçtiklerimiz")
861
- output_html = gr.HTML(
862
- label="Film Önerileri",
863
- value="""
864
- <div class="no-results-card">
865
- <div class="no-results-icon">🎬</div>
866
- <h2 class="no-results-title">Filmler Yükleniyor...</h2>
867
- <p class="no-results-message">Tercihlerinizi seçin ve keşfetmeye başlayın!</p>
868
- </div>
869
- """
870
- )
871
 
872
  with gr.Row():
873
  with gr.Column(scale=1):
874
- gr.Markdown("### 🎨 Film Tercihleriniz")
875
 
876
  tags_input = gr.CheckboxGroup(
877
- label="🎭 Film Türleri",
878
  choices=all_tags,
879
  value=['action', 'drama'],
880
  interactive=True
881
  )
882
 
883
  directors_input = gr.Dropdown(
884
- label="🎬 Favori Yönetmenler",
885
  choices=all_directors,
886
  multiselect=True,
887
  allow_custom_value=False,
@@ -889,7 +320,7 @@ with gr.Blocks(theme=custom_theme, css="""
889
  )
890
 
891
  stars_input = gr.Dropdown(
892
- label="⭐ Favori Oyuncular",
893
  choices=all_stars,
894
  multiselect=True,
895
  allow_custom_value=False,
@@ -901,7 +332,7 @@ with gr.Blocks(theme=custom_theme, css="""
901
  maximum=df_filtered['IMDb Rating'].max(),
902
  step=0.1,
903
  value=7.6,
904
- label="📊 Minimum IMDb Puanı"
905
  )
906
 
907
  num_recommendations_slider = gr.Slider(
@@ -909,20 +340,15 @@ with gr.Blocks(theme=custom_theme, css="""
909
  maximum=20,
910
  step=1,
911
  value=10,
912
- label="🎯 Öneri Sayısı"
913
  )
914
 
915
  search_text_input = gr.Textbox(
916
- label="🔍 Akıllı Arama (Yapay Zeka Destekli)",
917
- placeholder="Örn: Uzayda geçen, aksiyon dolu bir macera...",
918
- lines=2
919
  )
920
 
921
- recommend_btn = gr.Button(
922
- "🚀 FİLMLERİ KEŞFET",
923
- variant="primary",
924
- size="lg"
925
- )
926
 
927
  recommend_btn.click(
928
  fn=get_movie_recommendations,
@@ -930,7 +356,6 @@ with gr.Blocks(theme=custom_theme, css="""
930
  outputs=output_html
931
  )
932
 
933
- gr.Markdown("### 💡 Hızlı Başlangıç Örnekleri")
934
  gr.Examples(
935
  examples=[
936
  [['action'], [], [], 7.6, 5, ""],
@@ -943,55 +368,20 @@ with gr.Blocks(theme=custom_theme, css="""
943
  inputs=[tags_input, directors_input, stars_input, min_imdb_rating_slider, num_recommendations_slider, search_text_input],
944
  outputs=output_html,
945
  fn=get_movie_recommendations,
946
- label="Popüler Aramalar"
947
  )
948
 
949
  gr.Markdown(
950
  """
951
  ---
952
-
953
- <div class='how-to-use-section'>
954
-
955
- <h3>📖 Nasıl Kullanılır?</h3>
956
-
957
- <div class='how-to-use-grid'>
958
-
959
- <div class='how-to-use-step'>
960
- <h4>🎭 1. Tür Seçin</h4>
961
- <p>İlginizi çeken film türlerini seçin. Birden fazla tür kombinleyebilirsiniz.</p>
962
- </div>
963
-
964
- <div class='how-to-use-step'>
965
- <h4>🎬 2. Filtre Uygulayın</h4>
966
- <p>Favori yönetmen ve oyuncularınızı, minimum IMDb puanını ayarlayın.</p>
967
- </div>
968
-
969
- <div class='how-to-use-step'>
970
- <h4>🔍 3. Akıllı Arama</h4>
971
- <p>Yapay zeka destekli arama ile konu, tema veya film adı arayın.</p>
972
- </div>
973
-
974
- <div class='how-to-use-step'>
975
- <h4>🚀 4. Keşfedin</h4>
976
- <p>Butona tıklayın ve size özel seçilmiş filmleri keşfedin!</p>
977
- </div>
978
-
979
- </div>
980
-
981
- <div class='pro-tip-box'>
982
- <h4><span style='font-size: 24px;'>💡</span> Pro İpucu</h4>
983
- <p>
984
- Daha spesifik sonuçlar için birden fazla filtreyi kombine edin. Örneğin: "Christopher Nolan yönetmenliğinde, bilim kurgu türünde, 8.0 ve üzeri puanlı filmler" gibi.
985
- </p>
986
- </div>
987
-
988
- </div>
989
-
990
- <div class='footer-info'>
991
- <p>🤖 Yapay Zeka ile Güçlendirilmiş | ⚡ Anlık Sonuçlar | 🎯 Kişiselleştirilmiş Öneriler</p>
992
- </div>
993
  """
994
  )
995
 
996
  demo.launch(share=True)
997
- print("\nADIM 5: Gradio Web Arayüzü Başlatıldı.")
 
6
  import torch
7
  import re
8
 
9
+ print("--- Film Öneri Sistemi Başlatılıyor ---")
 
 
 
10
 
11
  csv_file_name = "imdb-top-rated-movies-user-rated.csv"
12
  file_path = os.path.join(".", csv_file_name)
13
 
14
  if not os.path.exists(file_path):
15
+ print(f"HATA: '{csv_file_name}' dosyası bulunamadı.")
16
+ exit(1)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
 
18
+ try:
19
+ df = pd.read_csv(file_path)
20
+ print(f"'{csv_file_name}' başarıyla yüklendi. Toplam {len(df)} film bulundu.")
21
+ except Exception as e:
22
+ print(f"HATA: CSV dosyası yüklenirken hata oluştu: {e}")
23
+ exit(1)
24
 
25
+ df_filtered = df[['Title', 'IMDb Rating', 'Tags', 'Director', 'Stars', 'Votes', 'Description', 'Poster URL']].copy()
26
+ df_filtered['Stars'].fillna('', inplace=True)
27
+ df_filtered['Description'].fillna('', inplace=True)
28
+ df_filtered['Poster URL'].fillna('', inplace=True)
29
 
30
  genre_mapping = {
31
  'action': ['action', 'action epic', 'gun fu', 'one-person army action', 'car action', 'kung fu', 'martial arts', 'martial-arts'],
 
57
  main_genres = set()
58
  for tag in tag_list:
59
  if tag in reverse_genre_map:
60
+ main_genres.add(reverse_genre_map[tag])
61
  return list(main_genres)
62
 
63
  def clean_and_split(text_series):
 
65
  return []
66
  item = str(text_series)
67
  item = item.replace('"', '').replace("'", '').strip()
68
+ item = item.replace('sci, fi', 'sci-fi')
69
  split_items = [s.strip().lower() for s in item.split(',') if s.strip()]
70
  return split_items
71
 
 
72
  df_filtered['Tags_cleaned_raw'] = df_filtered['Tags'].apply(clean_and_split)
73
  df_filtered['Director_cleaned'] = df_filtered['Director'].apply(clean_and_split)
74
  df_filtered['Stars_cleaned'] = df_filtered['Stars'].apply(clean_and_split)
 
75
  df_filtered['Tags_cleaned'] = df_filtered['Tags_cleaned_raw'].apply(map_to_main_genres)
76
 
 
77
  def convert_votes_to_numeric(votes_str):
78
  if isinstance(votes_str, str):
79
+ votes_str = votes_str.replace(",", "")
 
80
  if 'K' in votes_str:
81
  return float(votes_str.replace('K', '')) * 1000
82
  elif 'M' in votes_str:
83
  return float(votes_str.replace('M', '')) * 1_000_000
84
+ try:
85
  return float(votes_str)
86
  except ValueError:
87
+ return np.nan
88
 
89
  df_filtered['Votes_numeric'] = df_filtered['Votes'].apply(convert_votes_to_numeric)
90
+ df_filtered.drop('Votes', axis=1, inplace=True)
91
+ df_filtered.dropna(subset=['Votes_numeric'], inplace=True)
 
92
 
93
  df_filtered['Combined_Text'] = df_filtered['Title'] + ". " + \
94
  df_filtered['Description'] + ". " + \
 
96
  df_filtered['Director_cleaned'].apply(lambda x: ", ".join(x)) + ". " + \
97
  df_filtered['Stars_cleaned'].apply(lambda x: ", ".join(x))
98
 
 
 
 
 
 
 
99
  model_name = 'sentence-transformers/all-MiniLM-L6-v2'
 
100
  try:
101
  sentence_model = SentenceTransformer(model_name)
102
  print(f"'{model_name}' modeli başarıyla yüklendi.")
103
  except Exception as e:
104
  print(f"HATA: Sentence Transformer modeli yüklenirken hata oluştu: {e}")
105
+ exit(1)
106
 
107
  embeddings_file_path = os.path.join(".", "film_embeddings.npy")
108
  if not os.path.exists(embeddings_file_path):
109
+ print(f"HATA: '{embeddings_file_path}' dosyası bulunamadı.")
110
+ exit(1)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
111
 
112
+ try:
113
+ film_embeddings = torch.from_numpy(np.load(embeddings_file_path))
114
+ print("Film embedding'leri başarıyla yüklendi.")
115
+ except Exception as e:
116
+ print(f"HATA: film_embeddings.npy yüklenirken hata oluştu: {e}")
117
+ exit(1)
118
 
 
119
  director_popularity = {}
120
  for index, row in df_filtered.iterrows():
121
  for director in row['Director_cleaned']:
 
126
  for star in row['Stars_cleaned']:
127
  star_popularity[star] = star_popularity.get(star, 0) + row['Votes_numeric']
128
 
 
129
  all_tags = sorted(list(set([tag for sublist in df_filtered['Tags_cleaned'] for tag in sublist if tag in genre_mapping])))
 
 
130
  all_directors = sorted(list(director_popularity.keys()), key=lambda d: director_popularity[d], reverse=True)
131
  all_stars = sorted(list(star_popularity.keys()), key=lambda s: star_popularity[s], reverse=True)
 
132
 
133
  def get_movie_recommendations(selected_tags, selected_directors, selected_stars, min_imdb_rating_slider, num_recommendations_slider, search_text=""):
134
 
 
 
 
 
 
 
 
 
135
  selected_tags_list = list(selected_tags) if selected_tags else []
136
  selected_directors_list = list(selected_directors) if selected_directors else []
137
  selected_stars_list = list(selected_stars) if selected_stars else []
138
 
139
  recommendations_df = df_filtered.copy()
 
140
  recommendations_df = recommendations_df[recommendations_df['IMDb Rating'] >= min_imdb_rating_slider]
141
 
142
  if selected_tags_list:
 
154
  recommendations_df['Stars_cleaned'].apply(lambda x: any(star in x for star in selected_stars_list))
155
  ]
156
 
157
+ if search_text and len(recommendations_df) > 0:
158
  try:
159
  query_embedding = sentence_model.encode(search_text, convert_to_tensor=True)
160
+ filtered_indices = recommendations_df.index.tolist()
 
 
 
 
 
 
 
 
 
 
 
161
  current_film_embeddings = film_embeddings[filtered_indices]
162
  cosine_scores = util.cos_sim(query_embedding, current_film_embeddings)[0]
163
  recommendations_df['Similarity_Score'] = cosine_scores.cpu().numpy()
 
166
  ascending=[False, False, False]
167
  ).reset_index(drop=True)
168
  except Exception as e:
169
+ return "Benzerlik hesaplanırken bir hata oluştu."
 
 
 
170
 
171
+ if not search_text:
172
  recommendations_df = recommendations_df.sort_values(
173
  by=['IMDb Rating', 'Votes_numeric'],
174
  ascending=[False, False]
 
178
 
179
  if top_recommendations.empty:
180
  return """
181
+ <div style="text-align: center; padding: 60px 20px; background: linear-gradient(135deg, #1a1a1a 0%, #2d1810 100%); border-radius: 16px; border: 2px solid #ff6b35;">
182
+ <div style="font-size: 64px; margin-bottom: 20px;">🎬</div>
183
+ <h2 style="color: #ff6b35; margin-bottom: 10px; font-size: 28px;">Sonuç Bulunamadı</h2>
184
+ <p style="color: #d4d4d4; font-size: 16px;">Seçtiğiniz kriterlere uygun film bulunamadı. Filtreleri değiştirerek tekrar deneyin.</p>
185
  </div>
186
  """
187
  else:
188
+ html_output = ""
189
  for idx, row in top_recommendations.iterrows():
190
  directors_str = ", ".join([d.title() for d in row['Director_cleaned']])
191
  stars_str = ", ".join([s.title() for s in row['Stars_cleaned']])
192
+ tags_str = ", ".join([t.title() for t in row['Tags_cleaned']])
193
 
194
  similarity_info = ""
195
+ if 'Similarity_Score' in row and search_text:
196
  sim_percentage = int(row['Similarity_Score'] * 100)
197
  similarity_info = f"""
198
+ <div style="display: inline-block; margin-left: 12px; padding: 6px 12px; background: linear-gradient(135deg, #ff6b35 0%, #ff8c42 100%); border-radius: 8px;">
199
+ <span style="color: white; font-weight: 700; font-size: 13px;">🎯 Eşleşme: %{sim_percentage}</span>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
200
  </div>
201
  """
202
+
203
+ rating_color = "#4ade80" if row['IMDb Rating'] >= 8.0 else "#fbbf24" if row['IMDb Rating'] >= 7.5 else "#fb923c"
204
+
205
+ poster_html = f"""
206
+ <div style="width: 160px; height: 240px; background: linear-gradient(135deg, #2a2a2a 0%, #1a1a1a 100%); border-radius: 12px; display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center; color: #888; font-size: 0.9em; line-height: 1.4; padding: 15px; box-shadow: 0 4px 12px rgba(0,0,0,0.4); border: 2px solid #3a3a3a;">
207
+ <div style="font-size: 48px; margin-bottom: 15px;">🎬</div>
208
+ <span style="font-weight: 600; color: #ddd; margin-bottom: 8px;">{row['Title'][:40]}...</span>
209
+ <span style="color: #999; font-size: 0.85em;">Poster Yok</span>
210
+ </div>
211
+ """
212
 
213
  html_output += f"""
214
+ <div style="display: flex; margin-bottom: 24px; border: 2px solid #3a3a3a; padding: 20px; border-radius: 16px; background: linear-gradient(135deg, #1a1a1a 0%, #252525 100%); box-shadow: 0 8px 24px rgba(0,0,0,0.3); transition: all 0.3s ease; position: relative; overflow: hidden;">
215
+ <div style="position: absolute; top: 0; left: 0; width: 6px; height: 100%; background: linear-gradient(180deg, #ff6b35 0%, #ff8c42 100%);"></div>
216
+ <div style="flex-shrink: 0; margin-right: 24px; margin-left: 6px;">
217
+ {poster_html}
218
  </div>
219
+ <div style="flex-grow: 1;">
220
+ <div style="margin-bottom: 12px;">
221
+ <h3 style="margin: 0; color: #ff8c42; font-size: 26px; font-weight: 700; display: inline-block;">{row['Title']}</h3>
222
+ <div style="display: inline-block; margin-left: 12px; background: {rating_color}; padding: 6px 14px; border-radius: 8px;">
223
+ <span style="font-size: 16px;">⭐</span>
224
+ <span style="color: #1a1a1a; font-weight: 700; font-size: 16px;">{row['IMDb Rating']:.1f}</span>
 
 
225
  </div>
226
+ {similarity_info}
227
  </div>
228
+
229
+ <div style="margin-bottom: 14px;">
230
+ <div style="display: inline-block; background: rgba(255, 107, 53, 0.15); padding: 8px 14px; border-radius: 8px; border: 1px solid rgba(255, 107, 53, 0.3);">
231
+ <span style="color: #ff8c42; font-weight: 600;">🗳️ {int(row['Votes_numeric']):,} Oy</span>
232
+ </div>
233
  </div>
234
+
235
+ <div style="margin-bottom: 12px;">
236
+ <span style="color: #ff8c42; font-weight: 600; font-size: 15px;">🎬 Yönetmen:</span>
237
+ <span style="color: #d4d4d4; font-size: 15px; margin-left: 8px;">{directors_str if directors_str else 'Bilinmiyor'}</span>
238
  </div>
239
+
240
+ <div style="margin-bottom: 14px;">
241
+ <span style="color: #ff8c42; font-weight: 600; font-size: 15px;">⭐ Oyuncular:</span>
242
+ <span style="color: #d4d4d4; font-size: 15px; margin-left: 8px;">{stars_str if stars_str else 'Bilinmiyor'}</span>
243
+ </div>
244
+
245
+ <div style="display: flex; gap: 8px; flex-wrap: wrap; margin-top: 12px;">
246
+ {''.join([f'<span style="background: linear-gradient(135deg, #ff6b35 0%, #ff8c42 100%); color: white; padding: 6px 14px; border-radius: 20px; font-size: 13px; font-weight: 600; box-shadow: 0 2px 8px rgba(255, 107, 53, 0.3);">{tag.title()}</span>' for tag in row['Tags_cleaned']])}
247
  </div>
248
  </div>
249
  </div>
250
  """
 
251
  return html_output
252
 
253
+ with gr.Blocks(theme=gr.themes.Soft(), css="""
254
+ .gradio-container { max-width: 1200px !important; font-family: 'Segoe UI', sans-serif; }
255
+ h1 { color: #f39c12; text-align: center; }
256
+ h3 { color: #eee; }
257
+ .gr-button.gr-button-primary { background-color: #f39c12 !important; border-color: #f39c12 !important; }
258
+ .gr-button.gr-button-primary:hover { background-color: #e67e22 !important; border-color: #e67e22 !important; }
259
+ .gr-checkbox-group label { color: #ccc; }
260
+ .gr-dropdown, .gr-slider, .gr-textbox { background-color: #2c2c2c; color: #eee; border-color: #555; }
261
+ .gr-dropdown-item { color: #eee; }
262
+ .gr-checkbox-group input[type='checkbox']:checked + label {
263
+ background-color: #f39c12 !important;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
264
  border-color: #f39c12 !important;
265
+ color: #1a1a1a !important;
266
  }
267
+ .gr-checkbox-group input[type='checkbox'] + label {
268
+ background-color: #333;
269
+ color: #eee;
270
+ border: 1px solid #555;
 
 
 
271
  }
272
+ .gr-checkbox-group input[type='checkbox'] + label:hover {
273
+ background-color: #444;
 
 
 
 
 
274
  }
275
  .gr-dropdown-item.selected {
276
  background-color: #f39c12 !important;
277
+ color: #1a1a1a !important;
 
278
  }
279
+ .gr-dropdown-item:hover {
280
+ background-color: #e67e22 !important;
281
+ color: #1a1a1a !important;
 
 
 
282
  }
283
  input[type="range"]::-webkit-slider-thumb {
284
  background-color: #f39c12 !important;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
285
  }
286
+ input[type="range"]::-moz-range-thumb {
287
+ background-color: #f39c12 !important;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
288
  }
 
289
  """) as demo:
 
290
  gr.Markdown(
291
  """
292
+ # 🎬 Film Öneri Sistemi
293
+ Favori film özelliklerinizi seçin, yüksek IMDb puanına sahip filmleri keşfedin!
294
+ İstediğiniz bir film veya konu hakkında yazın, benzerlerini de bulalım.
295
  """
296
  )
297
 
298
  with gr.Row():
299
+ with gr.Column(scale=2):
300
+ gr.Markdown("### Önerilen Filmler:")
301
+ output_html = gr.HTML(label="Önerileriniz burada listelenecektir.", value="<p style='text-align: center; color: #bbb;'>Henüz bir öneri yapılmadı. Özellikleri seçip butona tıklayın!</p>")
 
 
 
 
 
 
 
 
 
302
 
303
  with gr.Row():
304
  with gr.Column(scale=1):
305
+ gr.Markdown("### Film Özelliklerini Seçin:")
306
 
307
  tags_input = gr.CheckboxGroup(
308
+ label="Film Türleri",
309
  choices=all_tags,
310
  value=['action', 'drama'],
311
  interactive=True
312
  )
313
 
314
  directors_input = gr.Dropdown(
315
+ label="Yönetmenler",
316
  choices=all_directors,
317
  multiselect=True,
318
  allow_custom_value=False,
 
320
  )
321
 
322
  stars_input = gr.Dropdown(
323
+ label="Oyuncular",
324
  choices=all_stars,
325
  multiselect=True,
326
  allow_custom_value=False,
 
332
  maximum=df_filtered['IMDb Rating'].max(),
333
  step=0.1,
334
  value=7.6,
335
+ label="Minimum IMDb Puanı"
336
  )
337
 
338
  num_recommendations_slider = gr.Slider(
 
340
  maximum=20,
341
  step=1,
342
  value=10,
343
+ label="Öneri Sayısı"
344
  )
345
 
346
  search_text_input = gr.Textbox(
347
+ label="Film Adı veya Konu Hakkında Ara (NLP Tabanlı Benzerlik)",
348
+ placeholder="Örneğin: Batman, uzay filmi, zamanda yolculuk..."
 
349
  )
350
 
351
+ recommend_btn = gr.Button("🚀 Film Önerilerini Getir", variant="primary", size="lg")
 
 
 
 
352
 
353
  recommend_btn.click(
354
  fn=get_movie_recommendations,
 
356
  outputs=output_html
357
  )
358
 
 
359
  gr.Examples(
360
  examples=[
361
  [['action'], [], [], 7.6, 5, ""],
 
368
  inputs=[tags_input, directors_input, stars_input, min_imdb_rating_slider, num_recommendations_slider, search_text_input],
369
  outputs=output_html,
370
  fn=get_movie_recommendations,
371
+ label="Örnek Önerileri Deneyin"
372
  )
373
 
374
  gr.Markdown(
375
  """
376
  ---
377
+ ### ℹ️ Nasıl Kullanılır?
378
+ 1. **Film Türleri, Yönetmenler ve Oyuncular** bölümlerinden istediğiniz filtreleri seçin (birden fazla seçim yapabilirsiniz).
379
+ 2. **Minimum IMDb Puanı** ve **Öneri Sayısı** çubuklarını ayarlayın.
380
+ 3. İsterseniz **"Film Adı veya Konu Hakkında Ara"** kutucuğuna bir film adı, konu veya anahtar kelime yazın.
381
+ 4. **"🚀 Film Önerilerini Getir"** butonuna tıklayın.
382
+ 5. Öneriler üst panelde görünecektir!
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
383
  """
384
  )
385
 
386
  demo.launch(share=True)
387
+ print("Gradio Web Arayüzü Başlatıldı.")