wd23 commited on
Commit
87a2b20
·
verified ·
1 Parent(s): ec8cc31

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +64 -43
app.py CHANGED
@@ -359,16 +359,14 @@ def discover():
359
  data = request.json
360
  media_type = data.get('type', 'movie')
361
  page = data.get('page', 1)
362
- preset = data.get('preset') # 预设(如 'popular', 'now_playing' 等)
363
  filters = data.get('filters', {})
364
  sort_by = filters.get('sort_by', 'latest')
365
 
366
- # 辅助函数:构建 discover URL 并附加通用筛选条件
367
  def build_discover_url(base_params):
368
  url = f"https://api.themoviedb.org/3/discover/{media_type}?api_key={TMDB_API_KEY}&language=zh-CN&page={page}"
369
  for k, v in base_params.items():
370
  url += f"&{k}={v}"
371
- # 附加 filters 中的筛选条件(地区、年份、类型)
372
  region = filters.get('region')
373
  if region:
374
  region_code = {
@@ -387,7 +385,7 @@ def discover():
387
  url += "&primary_release_date.lte=2009-12-31"
388
  else:
389
  url += f"&primary_release_year={year}"
390
- else: # tv
391
  if year == '2010年前':
392
  url += "&first_air_date.lte=2009-12-31"
393
  else:
@@ -398,26 +396,23 @@ def discover():
398
  url += f"&with_genres={genres_str}"
399
  return url
400
 
401
- # 情况1:有预设,则构建 discover 参数(预设决定排序或日期范围)
402
  if preset:
403
- # 预设映射表
404
  preset_map_movie = {
405
  'popular': {'sort_by': 'popularity.desc'},
406
- 'now_playing': {'sort_by': 'primary_release_date.desc'}, # 正在热映:按上映日期降序
407
  'top_rated': {'sort_by': 'vote_average.desc', 'vote_count.gte': 30},
408
- 'upcoming': {'sort_by': 'primary_release_date.asc'} # 即将上映:按上映日期升序
409
  }
410
  preset_map_tv = {
411
  'popular': {'sort_by': 'popularity.desc'},
412
  'top_rated': {'sort_by': 'vote_average.desc', 'vote_count.gte': 30},
413
  'on_the_air': {'sort_by': 'first_air_date.desc'},
414
- 'airing_today': {'sort_by': 'first_air_date.desc'} # 今日播出可进一步精确,但简单按日期降序
415
  }
416
  if media_type == 'movie':
417
  base_params = preset_map_movie.get(preset, {'sort_by': 'popularity.desc'})
418
  else:
419
  base_params = preset_map_tv.get(preset, {'sort_by': 'popularity.desc'})
420
- # 如果 filters 中也有 sort_by,则覆盖预设的排序
421
  if filters.get('sort_by'):
422
  user_sort = filters['sort_by']
423
  if user_sort == 'latest':
@@ -425,7 +420,6 @@ def discover():
425
  elif user_sort == 'imdb':
426
  base_params['sort_by'] = 'vote_average.desc'
427
  base_params['vote_count.gte'] = 30
428
- # douban 无法直接映射,忽略
429
  url = build_discover_url(base_params)
430
  resp = requests.get(url, timeout=15)
431
  result = resp.json()
@@ -436,14 +430,13 @@ def discover():
436
  result['results'] = results
437
  return jsonify(result)
438
 
439
- # 情况2:无预设,使用原有逻辑(兼容旧版,但该路径实际上当前端不传 preset 时触发)
440
  if media_type == 'movie':
441
  base_url = f"https://api.themoviedb.org/3/discover/movie?api_key={TMDB_API_KEY}&language=zh-CN&page={page}"
442
  if sort_by == 'latest':
443
  base_url += "&sort_by=primary_release_date.desc"
444
  else:
445
  base_url += "&sort_by=vote_average.desc&vote_count.gte=30"
446
-
447
  region = filters.get('region')
448
  if region:
449
  region_code = {
@@ -452,25 +445,22 @@ def discover():
452
  }.get(region, '')
453
  if region_code:
454
  base_url += f"&with_origin_country={region_code}"
455
-
456
  year = filters.get('year')
457
  if year:
458
  if year == '2010年前':
459
  base_url += "&primary_release_date.lte=2009-12-31"
460
  else:
461
  base_url += f"&primary_release_year={year}"
462
-
463
  genres = filters.get('genres', [])
464
  if genres:
465
  genres_str = '|'.join(str(g) for g in genres)
466
  base_url += f"&with_genres={genres_str}"
467
- else: # tv
468
  base_url = f"https://api.themoviedb.org/3/discover/tv?api_key={TMDB_API_KEY}&language=zh-CN&page={page}"
469
  if sort_by == 'latest':
470
  base_url += "&sort_by=first_air_date.desc"
471
  else:
472
  base_url += "&sort_by=vote_average.desc&vote_count.gte=30"
473
-
474
  region = filters.get('region')
475
  if region:
476
  region_code = {
@@ -479,19 +469,16 @@ def discover():
479
  }.get(region, '')
480
  if region_code:
481
  base_url += f"&with_origin_country={region_code}"
482
-
483
  year = filters.get('year')
484
  if year:
485
  if year == '2010年前':
486
  base_url += "&first_air_date.lte=2009-12-31"
487
  else:
488
  base_url += f"&first_air_date_year={year}"
489
-
490
  genres = filters.get('genres', [])
491
  if genres:
492
  genres_str = '|'.join(str(g) for g in genres)
493
  base_url += f"&with_genres={genres_str}"
494
-
495
  resp = requests.get(base_url, timeout=15)
496
  result = resp.json()
497
  results = result.get('results', [])
@@ -906,8 +893,15 @@ HTML_TEMPLATE = r"""<!DOCTYPE html>
906
  .preset-bar { display: flex; flex-wrap: wrap; gap: 12px; justify-content: center; margin-bottom: 20px; padding-bottom: 12px; border-bottom: 1px solid #2a2a2a; }
907
  .preset-btn { padding: 6px 18px; border-radius: 30px; background: #2a2a2a; border: none; color: #ccc; cursor: pointer; font-size: 13px; transition: 0.2s; }
908
  .preset-btn.active { background: #e50914; color: #fff; }
909
- .movie-cats { display: flex; flex-direction: column; gap: 15px; }
910
- .tv-cats { display: none; }
 
 
 
 
 
 
 
911
  .filter-row { margin-bottom: 15px; }
912
  .filter-label { font-size: 13px; color: #aaa; margin-bottom: 8px; text-align: center; font-weight: 600; }
913
  .filter-buttons { display: flex; flex-wrap: wrap; gap: 8px; justify-content: center; }
@@ -1030,17 +1024,26 @@ HTML_TEMPLATE = r"""<!DOCTYPE html>
1030
  <button class="preset-btn" data-preset="on_the_air">📺 正在播出</button>
1031
  <button class="preset-btn" data-preset="airing_today">📅 今日播出</button>
1032
  </div>
1033
- <div id="movie-cats" class="movie-cats">
1034
- <div class="filter-row"><div class="filter-label">🌍 地区</div><div class="filter-buttons" id="movie-region"></div></div>
1035
- <div class="filter-row"><div class="filter-label">📅 年份</div><div class="filter-buttons" id="movie-year"></div></div>
1036
- <div class="filter-row"><div class="filter-label">🎭 类型 (TMDB)</div><div class="filter-buttons" id="movie-genre"></div></div>
1037
- <div class="filter-row"><div class="filter-label">📊 排序</div><div class="filter-buttons" id="movie-sort"></div></div>
1038
- </div>
1039
- <div id="tv-cats" class="tv-cats">
1040
- <div class="filter-row"><div class="filter-label">🌍 地区</div><div class="filter-buttons" id="tv-region"></div></div>
1041
- <div class="filter-row"><div class="filter-label">📅 年份</div><div class="filter-buttons" id="tv-year"></div></div>
1042
- <div class="filter-row"><div class="filter-label">🎭 类型 (TMDB)</div><div class="filter-buttons" id="tv-genre"></div></div>
1043
- <div class="filter-row"><div class="filter-label">📊 排序</div><div class="filter-buttons" id="tv-sort"></div></div>
 
 
 
 
 
 
 
 
 
1044
  </div>
1045
  </div>
1046
  <div class="search-section">
@@ -1090,6 +1093,30 @@ HTML_TEMPLATE = r"""<!DOCTYPE html>
1090
  </div>
1091
  </div>
1092
  <script>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1093
  // 日志相关
1094
  let logRefreshInterval = null;
1095
  async function fetchLogs() {
@@ -1242,7 +1269,7 @@ HTML_TEMPLATE = r"""<!DOCTYPE html>
1242
  });
1243
  }
1244
  }
1245
- // 清除所有筛选(地区/年份/类型/排序)的高亮和值
1246
  function resetFilters() {
1247
  if (currentType === 'movie') {
1248
  movieFilters = { region:null, year:null, genres:[], sort_by:'latest' };
@@ -1258,20 +1285,19 @@ HTML_TEMPLATE = r"""<!DOCTYPE html>
1258
  renderTVSortButtons();
1259
  }
1260
  }
1261
- // 点击预设(与筛选共存)
1262
  function selectPreset(preset) {
1263
  if (currentPreset === preset) {
1264
- // 再次点击同一预设:取消预设(回到无预设模式,不清筛选)
1265
  currentPreset = null;
1266
  } else {
1267
  currentPreset = preset;
1268
  }
1269
- updatePresetButtons(); // 更新高亮
1270
  currentPage = 1;
1271
  selectedMovie = null;
1272
  loadMovies();
1273
  }
1274
- // 筛选交互 - 点击后不清除预设,直接修改筛选并加载
1275
  function selectMovieRegion(code) {
1276
  movieFilters.region = (movieFilters.region===code ? null : code);
1277
  renderMovieRegionButtons();
@@ -1317,7 +1343,6 @@ HTML_TEMPLATE = r"""<!DOCTYPE html>
1317
  // 切换类型
1318
  async function switchType(type){
1319
  currentType=type; currentPage=1; currentQuery=''; selectedMovie=null;
1320
- // 重置预设和筛选
1321
  currentPreset = 'popular';
1322
  resetFilters();
1323
  document.getElementById('search-input').value='';
@@ -1353,7 +1378,6 @@ HTML_TEMPLATE = r"""<!DOCTYPE html>
1353
  totalPages = data.total_pages || 1;
1354
  document.getElementById('panel-title').innerHTML = `🔍 搜索结果: ${currentQuery}`;
1355
  } else {
1356
- // 构建 discover 请求参数,同时携带 preset 和 filters
1357
  let filters = (currentType === 'movie') ? { ...movieFilters } : { ...tvFilters };
1358
  params = {type: currentType, page: currentPage, filters: filters};
1359
  if (currentPreset) {
@@ -1363,7 +1387,6 @@ HTML_TEMPLATE = r"""<!DOCTYPE html>
1363
  let data = await resp.json();
1364
  currentMovies = data.results || [];
1365
  totalPages = data.total_pages || 1;
1366
- // 设置面板标题
1367
  let title = '';
1368
  if (currentPreset) {
1369
  const presetNames = {
@@ -1398,7 +1421,6 @@ HTML_TEMPLATE = r"""<!DOCTYPE html>
1398
  let kw=document.getElementById('search-input').value.trim();
1399
  if(!kw) return;
1400
  currentQuery=kw; currentPage=1; selectedMovie=null;
1401
- // 搜索时清除预设和筛选
1402
  currentPreset = null;
1403
  resetFilters();
1404
  updatePresetButtons();
@@ -1557,7 +1579,6 @@ HTML_TEMPLATE = r"""<!DOCTYPE html>
1557
  // 初始化
1558
  (async function init() {
1559
  await Promise.all([loadMovieGenres(), loadTVGenres()]);
1560
- // 绑定预设按钮事件
1561
  document.getElementById('movie-presets').querySelectorAll('.preset-btn').forEach(btn => {
1562
  btn.addEventListener('click', () => selectPreset(btn.dataset.preset));
1563
  });
 
359
  data = request.json
360
  media_type = data.get('type', 'movie')
361
  page = data.get('page', 1)
362
+ preset = data.get('preset')
363
  filters = data.get('filters', {})
364
  sort_by = filters.get('sort_by', 'latest')
365
 
 
366
  def build_discover_url(base_params):
367
  url = f"https://api.themoviedb.org/3/discover/{media_type}?api_key={TMDB_API_KEY}&language=zh-CN&page={page}"
368
  for k, v in base_params.items():
369
  url += f"&{k}={v}"
 
370
  region = filters.get('region')
371
  if region:
372
  region_code = {
 
385
  url += "&primary_release_date.lte=2009-12-31"
386
  else:
387
  url += f"&primary_release_year={year}"
388
+ else:
389
  if year == '2010年前':
390
  url += "&first_air_date.lte=2009-12-31"
391
  else:
 
396
  url += f"&with_genres={genres_str}"
397
  return url
398
 
 
399
  if preset:
 
400
  preset_map_movie = {
401
  'popular': {'sort_by': 'popularity.desc'},
402
+ 'now_playing': {'sort_by': 'primary_release_date.desc'},
403
  'top_rated': {'sort_by': 'vote_average.desc', 'vote_count.gte': 30},
404
+ 'upcoming': {'sort_by': 'primary_release_date.asc'}
405
  }
406
  preset_map_tv = {
407
  'popular': {'sort_by': 'popularity.desc'},
408
  'top_rated': {'sort_by': 'vote_average.desc', 'vote_count.gte': 30},
409
  'on_the_air': {'sort_by': 'first_air_date.desc'},
410
+ 'airing_today': {'sort_by': 'first_air_date.desc'}
411
  }
412
  if media_type == 'movie':
413
  base_params = preset_map_movie.get(preset, {'sort_by': 'popularity.desc'})
414
  else:
415
  base_params = preset_map_tv.get(preset, {'sort_by': 'popularity.desc'})
 
416
  if filters.get('sort_by'):
417
  user_sort = filters['sort_by']
418
  if user_sort == 'latest':
 
420
  elif user_sort == 'imdb':
421
  base_params['sort_by'] = 'vote_average.desc'
422
  base_params['vote_count.gte'] = 30
 
423
  url = build_discover_url(base_params)
424
  resp = requests.get(url, timeout=15)
425
  result = resp.json()
 
430
  result['results'] = results
431
  return jsonify(result)
432
 
433
+ # 无预设(理论上前端会传 preset,此路径保留兼容)
434
  if media_type == 'movie':
435
  base_url = f"https://api.themoviedb.org/3/discover/movie?api_key={TMDB_API_KEY}&language=zh-CN&page={page}"
436
  if sort_by == 'latest':
437
  base_url += "&sort_by=primary_release_date.desc"
438
  else:
439
  base_url += "&sort_by=vote_average.desc&vote_count.gte=30"
 
440
  region = filters.get('region')
441
  if region:
442
  region_code = {
 
445
  }.get(region, '')
446
  if region_code:
447
  base_url += f"&with_origin_country={region_code}"
 
448
  year = filters.get('year')
449
  if year:
450
  if year == '2010年前':
451
  base_url += "&primary_release_date.lte=2009-12-31"
452
  else:
453
  base_url += f"&primary_release_year={year}"
 
454
  genres = filters.get('genres', [])
455
  if genres:
456
  genres_str = '|'.join(str(g) for g in genres)
457
  base_url += f"&with_genres={genres_str}"
458
+ else:
459
  base_url = f"https://api.themoviedb.org/3/discover/tv?api_key={TMDB_API_KEY}&language=zh-CN&page={page}"
460
  if sort_by == 'latest':
461
  base_url += "&sort_by=first_air_date.desc"
462
  else:
463
  base_url += "&sort_by=vote_average.desc&vote_count.gte=30"
 
464
  region = filters.get('region')
465
  if region:
466
  region_code = {
 
469
  }.get(region, '')
470
  if region_code:
471
  base_url += f"&with_origin_country={region_code}"
 
472
  year = filters.get('year')
473
  if year:
474
  if year == '2010年前':
475
  base_url += "&first_air_date.lte=2009-12-31"
476
  else:
477
  base_url += f"&first_air_date_year={year}"
 
478
  genres = filters.get('genres', [])
479
  if genres:
480
  genres_str = '|'.join(str(g) for g in genres)
481
  base_url += f"&with_genres={genres_str}"
 
482
  resp = requests.get(base_url, timeout=15)
483
  result = resp.json()
484
  results = result.get('results', [])
 
893
  .preset-bar { display: flex; flex-wrap: wrap; gap: 12px; justify-content: center; margin-bottom: 20px; padding-bottom: 12px; border-bottom: 1px solid #2a2a2a; }
894
  .preset-btn { padding: 6px 18px; border-radius: 30px; background: #2a2a2a; border: none; color: #ccc; cursor: pointer; font-size: 13px; transition: 0.2s; }
895
  .preset-btn.active { background: #e50914; color: #fff; }
896
+ /* 折叠面板样式 */
897
+ .filter-collapse { margin-top: 8px; border-top: 1px solid #2a2a2a; }
898
+ .filter-header { display: flex; justify-content: space-between; align-items: center; padding: 12px 0; cursor: pointer; user-select: none; color: #e50914; font-size: 14px; font-weight: 500; }
899
+ .filter-header:hover { opacity: 0.8; }
900
+ .filter-header span:first-child { display: flex; align-items: center; gap: 8px; }
901
+ .toggle-icon { font-size: 18px; transition: transform 0.2s; }
902
+ .filter-content { transition: all 0.2s ease; overflow: hidden; }
903
+ .filter-content.collapsed { display: none; }
904
+ .movie-cats, .tv-cats { display: flex; flex-direction: column; gap: 15px; }
905
  .filter-row { margin-bottom: 15px; }
906
  .filter-label { font-size: 13px; color: #aaa; margin-bottom: 8px; text-align: center; font-weight: 600; }
907
  .filter-buttons { display: flex; flex-wrap: wrap; gap: 8px; justify-content: center; }
 
1024
  <button class="preset-btn" data-preset="on_the_air">📺 正在播出</button>
1025
  <button class="preset-btn" data-preset="airing_today">📅 今日播出</button>
1026
  </div>
1027
+ <!-- 折叠面板 -->
1028
+ <div class="filter-collapse">
1029
+ <div class="filter-header" onclick="toggleFilterPanel()">
1030
+ <span>🔽 筛选条件 (地区/年份/类型/排序)</span>
1031
+ <span class="toggle-icon" id="filterToggleIcon">▼</span>
1032
+ </div>
1033
+ <div class="filter-content" id="filterContent">
1034
+ <div id="movie-cats" class="movie-cats">
1035
+ <div class="filter-row"><div class="filter-label">🌍 地区</div><div class="filter-buttons" id="movie-region"></div></div>
1036
+ <div class="filter-row"><div class="filter-label">📅 年份</div><div class="filter-buttons" id="movie-year"></div></div>
1037
+ <div class="filter-row"><div class="filter-label">🎭 类型 (TMDB)</div><div class="filter-buttons" id="movie-genre"></div></div>
1038
+ <div class="filter-row"><div class="filter-label">📊 排序</div><div class="filter-buttons" id="movie-sort"></div></div>
1039
+ </div>
1040
+ <div id="tv-cats" class="tv-cats" style="display: none;">
1041
+ <div class="filter-row"><div class="filter-label">🌍 地区</div><div class="filter-buttons" id="tv-region"></div></div>
1042
+ <div class="filter-row"><div class="filter-label">📅 年份</div><div class="filter-buttons" id="tv-year"></div></div>
1043
+ <div class="filter-row"><div class="filter-label">🎭 类型 (TMDB)</div><div class="filter-buttons" id="tv-genre"></div></div>
1044
+ <div class="filter-row"><div class="filter-label">📊 排序</div><div class="filter-buttons" id="tv-sort"></div></div>
1045
+ </div>
1046
+ </div>
1047
  </div>
1048
  </div>
1049
  <div class="search-section">
 
1093
  </div>
1094
  </div>
1095
  <script>
1096
+ // 折叠面板状态 (默认收起)
1097
+ let filterPanelCollapsed = true;
1098
+ function toggleFilterPanel() {
1099
+ const content = document.getElementById('filterContent');
1100
+ const icon = document.getElementById('filterToggleIcon');
1101
+ if (filterPanelCollapsed) {
1102
+ content.classList.remove('collapsed');
1103
+ content.style.display = 'block';
1104
+ icon.innerHTML = '▲';
1105
+ filterPanelCollapsed = false;
1106
+ } else {
1107
+ content.classList.add('collapsed');
1108
+ content.style.display = 'none';
1109
+ icon.innerHTML = '▼';
1110
+ filterPanelCollapsed = true;
1111
+ }
1112
+ }
1113
+ // 初始状态:折叠
1114
+ document.addEventListener('DOMContentLoaded', function() {
1115
+ const content = document.getElementById('filterContent');
1116
+ content.classList.add('collapsed');
1117
+ content.style.display = 'none';
1118
+ });
1119
+
1120
  // 日志相关
1121
  let logRefreshInterval = null;
1122
  async function fetchLogs() {
 
1269
  });
1270
  }
1271
  }
1272
+ // 清除所有筛选
1273
  function resetFilters() {
1274
  if (currentType === 'movie') {
1275
  movieFilters = { region:null, year:null, genres:[], sort_by:'latest' };
 
1285
  renderTVSortButtons();
1286
  }
1287
  }
1288
+ // 点击预设
1289
  function selectPreset(preset) {
1290
  if (currentPreset === preset) {
 
1291
  currentPreset = null;
1292
  } else {
1293
  currentPreset = preset;
1294
  }
1295
+ updatePresetButtons();
1296
  currentPage = 1;
1297
  selectedMovie = null;
1298
  loadMovies();
1299
  }
1300
+ // 筛选交互
1301
  function selectMovieRegion(code) {
1302
  movieFilters.region = (movieFilters.region===code ? null : code);
1303
  renderMovieRegionButtons();
 
1343
  // 切换类型
1344
  async function switchType(type){
1345
  currentType=type; currentPage=1; currentQuery=''; selectedMovie=null;
 
1346
  currentPreset = 'popular';
1347
  resetFilters();
1348
  document.getElementById('search-input').value='';
 
1378
  totalPages = data.total_pages || 1;
1379
  document.getElementById('panel-title').innerHTML = `🔍 搜索结果: ${currentQuery}`;
1380
  } else {
 
1381
  let filters = (currentType === 'movie') ? { ...movieFilters } : { ...tvFilters };
1382
  params = {type: currentType, page: currentPage, filters: filters};
1383
  if (currentPreset) {
 
1387
  let data = await resp.json();
1388
  currentMovies = data.results || [];
1389
  totalPages = data.total_pages || 1;
 
1390
  let title = '';
1391
  if (currentPreset) {
1392
  const presetNames = {
 
1421
  let kw=document.getElementById('search-input').value.trim();
1422
  if(!kw) return;
1423
  currentQuery=kw; currentPage=1; selectedMovie=null;
 
1424
  currentPreset = null;
1425
  resetFilters();
1426
  updatePresetButtons();
 
1579
  // 初始化
1580
  (async function init() {
1581
  await Promise.all([loadMovieGenres(), loadTVGenres()]);
 
1582
  document.getElementById('movie-presets').querySelectorAll('.preset-btn').forEach(btn => {
1583
  btn.addEventListener('click', () => selectPreset(btn.dataset.preset));
1584
  });