作业提交2

main
ruanshuoyang 5 months ago
parent 1767d27f05
commit c7e6d47f71

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 185 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

@ -0,0 +1,8 @@
# 默认忽略的文件
/shelf/
/workspace.xml
# 基于编辑器的 HTTP 客户端请求
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="com.codeverse.userSettings.MarscodeWorkspaceAppSettingsState">
<option name="chatAppRouterInfo" value="builder/691b1079571b48717d170973" />
<option name="progress" value="1.0" />
</component>
</project>

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DataSourceManagerImpl" format="xml" multifile-model="true">
<data-source source="LOCAL" name="movie_recommend" uuid="e02c85ad-c202-42bf-a430-49c0aa140fe2">
<driver-ref>sqlite.xerial</driver-ref>
<synchronize>true</synchronize>
<jdbc-driver>org.sqlite.JDBC</jdbc-driver>
<jdbc-url>jdbc:sqlite:D:\软工课设\instance\movie_recommend.db</jdbc-url>
<working-dir>$ProjectFileDir$</working-dir>
<libraries>
<library>
<url>file://$APPLICATION_CONFIG_DIR$/jdbc-drivers/Xerial SQLiteJDBC/3.45.1/org/xerial/sqlite-jdbc/3.45.1.0/sqlite-jdbc-3.45.1.0.jar</url>
</library>
<library>
<url>file://$APPLICATION_CONFIG_DIR$/jdbc-drivers/Xerial SQLiteJDBC/3.45.1/org/slf4j/slf4j-api/1.7.36/slf4j-api-1.7.36.jar</url>
</library>
</libraries>
</data-source>
<data-source source="LOCAL" name="movie_recommend [2]" uuid="a47c6868-8910-4195-972d-735f2c797cb3">
<driver-ref>sqlite.xerial</driver-ref>
<synchronize>true</synchronize>
<jdbc-driver>org.sqlite.JDBC</jdbc-driver>
<jdbc-url>jdbc:sqlite:D:\软工课设 - 修改版1\instance\movie_recommend.db</jdbc-url>
<working-dir>$ProjectFileDir$</working-dir>
<libraries>
<library>
<url>file://$APPLICATION_CONFIG_DIR$/jdbc-drivers/Xerial SQLiteJDBC/3.45.1/org/xerial/sqlite-jdbc/3.45.1.0/sqlite-jdbc-3.45.1.0.jar</url>
</library>
<library>
<url>file://$APPLICATION_CONFIG_DIR$/jdbc-drivers/Xerial SQLiteJDBC/3.45.1/org/slf4j/slf4j-api/1.7.36/slf4j-api-1.7.36.jar</url>
</library>
</libraries>
</data-source>
</component>
</project>

@ -0,0 +1,6 @@
<component name="InspectionProjectProfileManager">
<settings>
<option name="USE_PROJECT_PROFILE" value="false" />
<version value="1.0" />
</settings>
</component>

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Black">
<option name="sdkName" value="Python 3.13 virtualenv at D:\软工课设 - 修改版1\.venv" />
</component>
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.10" project-jdk-type="Python SDK" />
</project>

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/软工课设.iml" filepath="$PROJECT_DIR$/.idea/软工课设.iml" />
</modules>
</component>
</project>

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/.venv" />
</content>
<orderEntry type="jdk" jdkName="Python 3.10" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
<component name="TemplatesService">
<option name="TEMPLATE_CONFIGURATION" value="Jinja2" />
<option name="TEMPLATE_FOLDERS">
<list>
<option value="$MODULE_DIR$/templates" />
</list>
</option>
</component>
</module>

@ -0,0 +1,105 @@
import requests
import json
import time
import random
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
def get_tmdb_50_movies(api_key):
base_url = "https://api.themoviedb.org/3"
movies = []
output_file = "tmdb_50_movies.json" # 输出文件路径
# 尝试加载已保存的部分数据(如果存在)
try:
with open(output_file, "r", encoding="utf-8") as f:
existing_data = json.load(f)
movies = existing_data
print(f"检测到已保存{len(movies)}部电影,将继续爬取...")
except (FileNotFoundError, json.JSONDecodeError):
print("未检测到已有数据,将从头开始爬取")
# 配置会话和重试策略
retry_strategy = Retry(
total=3,
backoff_factor=1,
status_forcelist=[429, 500, 502, 503, 504],
allowed_methods=["GET"]
)
adapter = HTTPAdapter(max_retries=retry_strategy)
session = requests.Session()
session.mount("https://", adapter)
session.mount("http://", adapter)
# 爬取逻辑(从当前进度继续)
page = 1
while len(movies) < 50:
url = f"{base_url}/movie/popular?api_key={api_key}&language=zh-CN&page={page}"
try:
# 随机延时,降低请求频率
time.sleep(random.uniform(1, 2))
response = session.get(url, timeout=10)
response.raise_for_status()
page_data = response.json()
for movie in page_data["results"]:
# 跳过已爬取的电影(避免重复)
if any(m["title"] == movie["title"] for m in movies):
continue
# 控制总量不超过50
if len(movies) >= 50:
break
# 获取电影详情
detail_url = f"{base_url}/movie/{movie['id']}?api_key={api_key}&language=zh-CN"
time.sleep(random.uniform(0.5, 1.5))
detail_res = session.get(detail_url, timeout=10)
detail_res.raise_for_status()
detail_data = detail_res.json()
# 提取信息
movie_info = {
"title": detail_data["title"],
"genre": ", ".join([g["name"] for g in detail_data["genres"]]),
"description": detail_data["overview"],
"year": detail_data["release_date"].split("-")[0] if detail_data.get("release_date") else "未知",
"rating": detail_data["vote_average"],
"cover": f"https://image.tmdb.org/t/p/w500{detail_data['poster_path']}" if detail_data.get(
"poster_path") else "",
"tags": ", ".join([g["name"] for g in detail_data["genres"]])
}
movies.append(movie_info)
print(f"已爬取 {len(movies)}/50 部:{detail_data['title']}")
# 每爬取10部保存一次避免意外丢失
if len(movies) % 10 == 0:
with open(output_file, "w", encoding="utf-8") as f:
json.dump(movies, f, ensure_ascii=False, indent=2)
print(f"已临时保存{len(movies)}部电影数据")
page += 1
except Exception as e:
print(f"\n爬取出错:{str(e)}")
# 出错时立即保存已爬取的数据
with open(output_file, "w", encoding="utf-8") as f:
json.dump(movies, f, ensure_ascii=False, indent=2)
print(f"已保存当前进度:{len(movies)}部电影")
# 询问是否继续
choice = input("是否继续爬取?(y/n)")
if choice.lower() != "y":
print("爬取终止,已保存当前数据")
return
time.sleep(5) # 延迟5秒再重试
# 最终保存完整数据
with open(output_file, "w", encoding="utf-8") as f:
json.dump(movies, f, ensure_ascii=False, indent=2)
print(f"爬取完成!共获取{len(movies)}部电影,数据已保存至{output_file}")
if __name__ == "__main__":
api_key = "6df978cd734acfa766eba02c1772ec0e"
get_tmdb_50_movies(api_key)

@ -0,0 +1,306 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
批量添加电影脚本
支持从多种数据源批量导入电影数据
1. 内置电影列表
2. CSV文件导入
3. JSON文件导入
4. 手动输入列表
用法
python auto-add-script.py --source builtin # 使用内置列表
python auto-add-script.py --source csv --file movies.csv # 从CSV导入
python auto-add-script.py --source json --file movies.json # 从JSON导入
python auto-add-script.py --source manual --count 20 # 手动生成20部电影
"""
import argparse
import csv
import json
import random
import sys
from datetime import datetime
from flask import Flask
from models import db, Movie
def create_app():
"""创建Flask应用"""
app = Flask(__name__)
app.config['SECRET_KEY'] = 'your_custom_secret_key_123456'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///movie_recommend.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db.init_app(app)
return app
# 内置电影数据(扩展版)
BUILTIN_MOVIES = [
# 科幻类
{"title": "星际穿越", "genre": "科幻", "tags": "科幻,冒险,太空", "description": "探索虫洞寻找新家园。", "year": 2014, "cover": "static/covers/interstellar.jpg"},
{"title": "阿凡达", "genre": "科幻", "tags": "科幻,冒险,3D", "description": "潘多拉星球的史诗冒险。", "year": 2009, "cover": "static/covers/avatar.jpg"},
{"title": "盗梦空间", "genre": "悬疑,科幻", "tags": "科幻,悬疑,梦境", "description": "梦境中的潜入与对抗。", "year": 2010, "cover": "static/covers/inception.jpg"},
{"title": "黑客帝国", "genre": "科幻,动作", "tags": "科幻,动作,虚拟现实", "description": "矩阵中的真实与反抗。", "year": 1999, "cover": "static/covers/matrix.jpg"},
{"title": "沙丘", "genre": "科幻,冒险", "tags": "科幻,史诗,沙漠", "description": "厄拉科斯的庞大史诗。", "year": 2021, "cover": "static/covers/dune.jpg"},
{"title": "银翼杀手2049", "genre": "科幻,剧情", "tags": "科幻,赛博朋克,未来", "description": "复制人与身份之谜。", "year": 2017, "cover": "static/covers/blade_runner_2049.jpg"},
{"title": "火星救援", "genre": "科幻,冒险", "tags": "科幻,太空,生存", "description": "独自在火星求生。", "year": 2015, "cover": "static/covers/the_martian.jpg"},
{"title": "终结者2审判日", "genre": "动作,科幻", "tags": "科幻,动作,机器人", "description": "T-800守护约翰·康纳。", "year": 1991, "cover": "static/covers/terminator2.jpg"},
# 动作类
{"title": "蝙蝠侠:黑暗骑士", "genre": "动作,犯罪", "tags": "动作,犯罪,超级英雄", "description": "哥谭的正义与混沌。", "year": 2008, "cover": "static/covers/dark_knight.jpg"},
{"title": "复仇者联盟", "genre": "动作,科幻", "tags": "动作,科幻,超级英雄", "description": "超级英雄集结对抗外星威胁。", "year": 2012, "cover": "static/covers/avengers.jpg"},
{"title": "钢铁侠", "genre": "动作,科幻", "tags": "动作,科幻,超级英雄", "description": "托尼·斯塔克的装甲英雄起源。", "year": 2008, "cover": "static/covers/iron_man.jpg"},
{"title": "疯狂的麦克斯4狂暴之路", "genre": "动作,冒险", "tags": "动作,冒险,末日", "description": "末日荒原的疯狂逃亡。", "year": 2015, "cover": "static/covers/mad_max_fury_road.jpg"},
# 剧情类
{"title": "肖申克的救赎", "genre": "剧情", "tags": "剧情,励志,监狱", "description": "希望与自由的赞歌。", "year": 1994, "cover": "static/covers/shawshank.jpg"},
{"title": "阿甘正传", "genre": "剧情,爱情", "tags": "剧情,励志,人生", "description": "阿甘的一生与奇迹。", "year": 1994, "cover": "static/covers/forrest_gump.jpg"},
{"title": "美丽人生", "genre": "剧情,爱情", "tags": "剧情,战争,父爱", "description": "笑中带泪的生命赞歌。", "year": 1997, "cover": "static/covers/life_is_beautiful.jpg"},
{"title": "当幸福来敲门", "genre": "剧情,传记", "tags": "剧情,励志,奋斗", "description": "逆境中的父爱与坚持。", "year": 2006, "cover": "static/covers/pursuit_of_happyness.jpg"},
{"title": "绿皮书", "genre": "剧情,传记", "tags": "剧情,种族,友谊", "description": "跨越种族的巡演之旅。", "year": 2018, "cover": "static/covers/green_book.jpg"},
# 爱情类
{"title": "泰坦尼克号", "genre": "爱情,剧情", "tags": "爱情,灾难,史诗", "description": "跨越阶级的爱情故事。", "year": 1997, "cover": "static/covers/titanic.jpg"},
{"title": "爱乐之城", "genre": "爱情,歌舞", "tags": "爱情,音乐,梦想", "description": "追梦人的爱与抉择。", "year": 2016, "cover": "static/covers/la_la_land.jpg"},
{"title": "大话西游之大圣娶亲", "genre": "爱情,奇幻", "tags": "爱情,喜剧,神话", "description": "至尊宝与紫霞的宿命。", "year": 1995, "cover": "static/covers/a_chinese_odyssey.jpg"},
# 动画类
{"title": "狮子王", "genre": "动画,冒险", "tags": "动画,冒险,成长", "description": "辛巴的成长与荣耀之旅。", "year": 1994, "cover": "static/covers/lion_king.jpg"},
{"title": "千与千寻", "genre": "动画,奇幻", "tags": "动画,奇幻,成长", "description": "少女千寻的神隐世界历险。", "year": 2001, "cover": "static/covers/spirited_away.jpg"},
{"title": "疯狂动物城", "genre": "动画,冒险", "tags": "动画,冒险,喜剧", "description": "万物共生的大城市。", "year": 2016, "cover": "static/covers/zootopia.jpg"},
{"title": "寻梦环游记", "genre": "动画,冒险", "tags": "动画,音乐,亲情", "description": "音乐与亲情的追寻。", "year": 2017, "cover": "static/covers/coco.jpg"},
# 悬疑犯罪类
{"title": "七宗罪", "genre": "悬疑,犯罪", "tags": "悬疑,犯罪,心理", "description": "连环罪案与人性拷问。", "year": 1995, "cover": "static/covers/se7en.jpg"},
{"title": "搏击俱乐部", "genre": "剧情,惊悚", "tags": "悬疑,心理,社会", "description": "规则一:不要谈论搏击俱乐部。", "year": 1999, "cover": "static/covers/fight_club.jpg"},
{"title": "致命魔术", "genre": "剧情,悬疑", "tags": "悬疑,魔术,竞争", "description": "魔术师的宿命对决。", "year": 2006, "cover": "static/covers/the_prestige.jpg"},
{"title": "无间道", "genre": "犯罪,惊悚", "tags": "犯罪,卧底,警匪", "description": "警匪之间的双面人生。", "year": 2002, "cover": "static/covers/infernal_affairs.jpg"},
# 奇幻冒险类
{"title": "指环王:王者归来", "genre": "奇幻,冒险", "tags": "奇幻,史诗,冒险", "description": "中土世界的最终之战。", "year": 2003, "cover": "static/covers/lotr_rotk.jpg"},
{"title": "哈利·波特与魔法石", "genre": "奇幻,冒险", "tags": "奇幻,魔法,成长", "description": "魔法世界的入门之旅。", "year": 2001, "cover": "static/covers/harry_potter1.jpg"},
{"title": "驯龙高手", "genre": "动画,冒险", "tags": "动画,冒险,龙", "description": "人与龙的羁绊。", "year": 2010, "cover": "static/covers/how_to_train_your_dragon.jpg"},
]
# 手动生成电影的数据模板
MOVIE_TEMPLATES = {
"科幻": [
{"title": "星际探索", "tags": "科幻,太空,冒险", "description": "人类探索宇宙的壮丽旅程。"},
{"title": "未来都市", "tags": "科幻,未来,科技", "description": "高科技城市中的生存挑战。"},
{"title": "时间旅行者", "tags": "科幻,时间,冒险", "description": "穿越时空的奇妙冒险。"},
],
"动作": [
{"title": "极限特工", "tags": "动作,特工,冒险", "description": "特工执行危险任务的惊险故事。"},
{"title": "城市猎人", "tags": "动作,犯罪,都市", "description": "都市中的正义守护者。"},
],
"剧情": [
{"title": "人生旅程", "tags": "剧情,人生,成长", "description": "普通人不平凡的人生故事。"},
{"title": "家庭纽带", "tags": "剧情,家庭,情感", "description": "家庭成员之间的情感纠葛。"},
],
"爱情": [
{"title": "遇见爱情", "tags": "爱情,浪漫,相遇", "description": "命中注定的浪漫邂逅。"},
{"title": "时光恋人", "tags": "爱情,时光,命运", "description": "跨越时空的爱情故事。"},
]
}
def add_movie_batch(movies_data, skip_duplicates=True):
"""批量添加电影到数据库"""
added_count = 0
skipped_count = 0
error_count = 0
for movie_data in movies_data:
try:
# 检查必填字段
if not movie_data.get('title'):
print(f"⚠️ 跳过:电影标题为空")
skipped_count += 1
continue
# 检查重复
if skip_duplicates:
existing_movie = Movie.query.filter_by(title=movie_data['title']).first()
if existing_movie:
print(f"⚠️ 跳过重复:{movie_data['title']}")
skipped_count += 1
continue
# 字段类型转换和过滤
def safe_int(val):
try:
return int(val)
except:
return None
def safe_float(val):
try:
return float(val)
except:
return None
movie = Movie(
title=movie_data.get('title', ''),
genre=movie_data.get('genre', ''),
tags=movie_data.get('tags', ''),
description=movie_data.get('description', ''),
year=safe_int(movie_data.get('year')),
cover=movie_data.get('cover', 'static/covers/default.jpg'),
rating=safe_float(movie_data.get('rating')),
)
db.session.add(movie)
added_count += 1
print(f"✅ 添加:{movie_data.get('title', '')}")
# 每添加10条提交一次避免事务过大
if added_count % 10 == 0:
db.session.commit()
print(f"📊 已提交 {added_count} 部电影...")
except Exception as e:
error_count += 1
print(f"❌ 添加失败:{movie_data.get('title', 'Unknown')} - {e}")
continue
# 提交剩余记录
try:
db.session.commit()
print(f"🎉 批量添加完成!")
print(f" - 成功添加:{added_count}")
print(f" - 跳过重复:{skipped_count}")
print(f" - 添加失败:{error_count}")
return added_count
except Exception as e:
db.session.rollback()
print(f"❌ 提交失败:{e}")
return 0
def import_from_csv(file_path):
"""从CSV文件导入电影数据"""
print(f"📁 从CSV文件导入{file_path}")
try:
with open(file_path, 'r', encoding='utf-8') as file:
reader = csv.DictReader(file)
movies_data = []
for row in reader:
movie_data = {
'title': row.get('标题') or row.get('title', ''),
'genre': row.get('类型') or row.get('genre', ''),
'tags': row.get('标签') or row.get('tags', ''),
'description': row.get('描述') or row.get('description', ''),
'year': int(row['年份']) if row.get('年份') and row['年份'].isdigit() else None,
'cover': row.get('封面') or row.get('cover', ''),
'director': row.get('导演') or row.get('director', ''),
'actors': row.get('主演') or row.get('actors', ''),
'country': row.get('国家') or row.get('country', ''),
'language': row.get('语言') or row.get('language', ''),
'duration': int(row['时长']) if row.get('时长') and row['时长'].isdigit() else None,
'rating': float(row['评分']) if row.get('评分') else None,
'rating_count': int(row['评分人数']) if row.get('评分人数') and row['评分人数'].isdigit() else None
}
movies_data.append(movie_data)
return add_movie_batch(movies_data)
except Exception as e:
print(f"❌ CSV导入失败{e}")
return 0
def import_from_json(file_path):
"""从JSON文件导入电影数据"""
print(f"📁 从JSON文件导入{file_path}")
try:
with open(file_path, 'r', encoding='utf-8') as file:
movies_data = json.load(file)
if not isinstance(movies_data, list):
print("❌ JSON文件格式错误应为电影列表")
return 0
# 只保留Movie表支持的字段
allowed_fields = {'title', 'genre', 'tags', 'description', 'year', 'cover', 'rating'}
filtered_movies = []
for m in movies_data:
filtered = {k: v for k, v in m.items() if k in allowed_fields}
filtered_movies.append(filtered)
return add_movie_batch(filtered_movies)
except Exception as e:
print(f"❌ JSON导入失败{e}")
return 0
def generate_manual_movies(count):
"""手动生成电影数据"""
print(f"🎬 手动生成 {count} 部电影数据")
movies_data = []
genres = list(MOVIE_TEMPLATES.keys())
for i in range(count):
genre = random.choice(genres)
template = random.choice(MOVIE_TEMPLATES[genre])
movie_data = {
'title': f"{template['title']} {i+1}",
'genre': genre,
'tags': template['tags'],
'description': template['description'],
'year': random.randint(1990, 2024),
'cover': 'static/covers/default.jpg'
}
movies_data.append(movie_data)
return add_movie_batch(movies_data)
def main():
"""主函数"""
parser = argparse.ArgumentParser(description='批量添加电影脚本')
parser.add_argument('--source', choices=['builtin', 'csv', 'json', 'manual'],
default='builtin', help='数据源类型')
parser.add_argument('--file', help='数据文件路径CSV或JSON')
parser.add_argument('--count', type=int, default=50, help='手动生成电影数量')
parser.add_argument('--skip-duplicates', action='store_true', default=True,
help='跳过重复电影(默认开启)')
args = parser.parse_args()
# 创建应用上下文
app = create_app()
with app.app_context():
# 创建数据库表
db.create_all()
added_count = 0
if args.source == 'builtin':
print("🎬 使用内置电影列表")
added_count = add_movie_batch(BUILTIN_MOVIES, args.skip_duplicates)
elif args.source == 'csv':
if not args.file:
print("❌ 请使用 --file 参数指定CSV文件路径")
return
added_count = import_from_csv(args.file)
elif args.source == 'json':
if not args.file:
print("❌ 请使用 --file 参数指定JSON文件路径")
return
added_count = import_from_json(args.file)
elif args.source == 'manual':
added_count = generate_manual_movies(args.count)
# 显示统计信息
total_movies = Movie.query.count()
print(f"\n📊 数据库统计:")
print(f" - 当前电影总数:{total_movies}")
print(f" - 本次添加:{added_count}")
if added_count > 0:
print(f"\n✅ 批量添加完成!")
else:
print(f"\n 没有添加新电影(可能都是重复数据)")
if __name__ == '__main__':
main()

@ -0,0 +1,28 @@
import os
import glob
# 要删除的数据库文件列表
db_files = [
'users.db',
'movies.db',
'movie_recommend.db',
'users.db-journal',
'movies.db-journal'
]
print("正在清理旧的数据库文件...")
deleted_count = 0
for db_file in db_files:
if os.path.exists(db_file):
try:
os.remove(db_file)
print(f"✓ 已删除: {db_file}")
deleted_count += 1
except Exception as e:
print(f"✗ 删除失败 {db_file}: {e}")
else:
print(f"- 文件不存在: {db_file}")
print(f"\n清理完成!共删除了 {deleted_count} 个文件")
print("现在可以运行 app.py 启动新版本了")

@ -0,0 +1,46 @@
[
{
"title": "钢铁侠3",
"genre": "动作,科幻,冒险",
"description": "在《钢铁侠3》中托尼·斯塔克面临着前所未有的挑战。当他的世界被神秘恐怖分子满大人摧毁后斯塔克开始了寻找真凶的旅程。这次他必须依靠自己的智慧和创造力而不是依赖高科技装备。影片深入探讨了托尼·斯塔克作为超级英雄和普通人的双重身份展现了他面对心理创伤和身体威胁时的成长与蜕变。满大人的真实身份和背后的阴谋让整个故事充满了悬疑和反转而小辣椒波茨在关键时刻的英勇表现也为影片增添了更多情感深度。",
"year": 2013,
"rating": 8.0,
"cover": "https://ts1.tc.mm.bing.net/th/id/R-C.6fac09995416e84bf1a7327caf252d07?rik=kTPbKbUiaM9iAA&riu=http%3a%2f%2fimg.sccnn.com%2fbimg%2f337%2f48129.jpg&ehk=1Fqs8xYGMwv1Mtg%2b50Atm3BUUxaROFbCc1S75CRqycc%3d&risl=&pid=ImgRaw&r=0",
"tags": "热门,漫威,超级英雄,动作,科幻,复仇者联盟"
},
{
"title": "阿凡达",
"genre": "动作,科幻,冒险",
"description": "在《阿凡达》中,前海军陆战队员杰克·萨利被派往潘多拉星球执行任务。他通过阿凡达化身融入纳美族社会,并最终领导他们对抗人类采矿公司的压迫。影片以惊人的视觉效果和深刻的环保主题著称,探讨了殖民主义、身份认同和自然和谐。杰克在潘多拉的冒险中找到了新的归属,并与纳美族公主妮特丽建立了深厚的感情。",
"year": 2009,
"rating": 7.8,
"cover": "https://image.tmdb.org/t/p/w500/jRXYjXNq0Cs2TcJjLkki24MLp7u.jpg",
"tags": "热门,科幻,动作,冒险,3D"
},
{
"title": "泰坦尼克号",
"genre": "剧情,爱情,灾难",
"description": "《泰坦尼克号》讲述了穷画家杰克和贵族少女露丝在泰坦尼克号邮轮上相遇并相爱的故事。影片以1912年泰坦尼克号沉没的真实事件为背景展现了爱情、阶级差异和人类在灾难面前的勇气。杰克和露丝的浪漫故事贯穿始终而邮轮的沉没场景则充满了紧张和悲壮。影片不仅获得了巨大的商业成功还赢得了多项奥斯卡奖成为电影史上的经典之作。",
"year": 1997,
"rating": 7.8,
"cover": "https://image.tmdb.org/t/p/w500/kHXEpyfl6zqn8a6YuozZUujufXf.jpg",
"tags": "经典,爱情,灾难,剧情,奥斯卡"
},
{
"title": "星球大战:原力觉醒",
"genre": "动作,科幻,冒险",
"description": "在《星球大战原力觉醒》中银河系面临着新的威胁——第一秩序。蕾伊一个生活在沙漠星球贾库的拾荒者偶然遇到了机器人BB-8并卷入了一场寻找卢克·天行者的冒险。她与逃亡士兵芬恩和传奇英雄汉·索罗联手共同对抗第一秩序和凯洛·伦。影片继承了星球大战系列的经典元素同时引入了新角色和故事线充满了动作、幽默和情感深度。",
"year": 2015,
"rating": 7.8,
"cover": "https://image.tmdb.org/t/p/w500/wqnLdwVXoBjKibFRR5U3y0aDUhs.jpg",
"tags": "热门,科幻,动作,冒险,星球大战"
},
{
"title": "复仇者联盟4终局之战",
"genre": "动作,科幻,冒险",
"description": "在《复仇者联盟4终局之战》中复仇者联盟在灭霸的响指事件后必须联手逆转灾难拯救消失的一半宇宙生命。影片通过时间旅行元素让英雄们回到过去收集无限宝石同时探讨了牺牲、友谊和英雄主义。钢铁侠托尼·斯塔克的最终牺牲和美国队长的归宿为漫威电影宇宙的一个时代画上了句号。这部影片以其宏大的场面和情感冲击力成为影史票房最高的电影之一。",
"year": 2019,
"rating": 8.4,
"cover": "https://image.tmdb.org/t/p/w500/or06FN3Dka5tukK1e9sl16pB3iy.jpg",
"tags": "热门,漫威,超级英雄,动作,科幻,终结"
},]

@ -0,0 +1,46 @@
from flask_sqlalchemy import SQLAlchemy
from werkzeug.security import generate_password_hash, check_password_hash
from datetime import datetime
db = SQLAlchemy()
class User(db.Model):
__tablename__ = 'user'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True, nullable=False)
password_hash = db.Column(db.String(200), nullable=False)
has_selected_tags = db.Column(db.Boolean, default=False)
selected_tags = db.Column(db.Text)
def set_password(self, password):
self.password_hash = generate_password_hash(password)
def check_password(self, password):
return check_password_hash(self.password_hash, password)
class UserBehavior(db.Model):
__tablename__ = 'user_behavior'
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
movie_id = db.Column(db.Integer, nullable=False)
rating = db.Column(db.Float)
is_collected = db.Column(db.Boolean, default=False)
collected = db.Column(db.Boolean, default=False)
timestamp = db.Column(db.DateTime, default=datetime.utcnow) # 添加时间戳字段
class Movie(db.Model):
__tablename__ = 'movie'
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(200), nullable=False)
genre = db.Column(db.String(100))
description = db.Column(db.Text)
year = db.Column(db.Integer)
rating = db.Column(db.Float, default=0.0)
cover = db.Column(db.String(500))
tags = db.Column(db.Text)
class UserFavorite(db.Model):
__tablename__ = 'user_favorite'
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
movie_id = db.Column(db.Integer, nullable=False)

@ -0,0 +1,49 @@
[
{
"title": "新增电影1",
"genre": "动作,科幻,冒险",
"description": "在《钢铁侠3》中托尼·斯塔克面临着前所未有的挑战。当他的世界被神秘恐怖分子满大人摧毁后斯塔克开始了寻找真凶的旅程。这次他必须依靠自己的智慧和创造力而不是依赖高科技装备。影片深入探讨了托尼·斯塔克作为超级英雄和普通人的双重身份展现了他面对心理创伤和身体威胁时的成长与蜕变。满大人的真实身份和背后的阴谋让整个故事充满了悬疑和反转而小辣椒波茨在关键时刻的英勇表现也为影片增添了更多情感深度。",
"year": 2013,
"rating": 8.0,
"cover": "static/covers/1.jpg",
"tags": "热门,漫威,超级英雄,动作,科幻,复仇者联盟"
},
{
"title": "新增电影2",
"genre": "动作,科幻,冒险",
"description": "在《阿凡达》中,前海军陆战队员杰克·萨利被派往潘多拉星球执行任务。他通过阿凡达化身融入纳美族社会,并最终领导他们对抗人类采矿公司的压迫。影片以惊人的视觉效果和深刻的环保主题著称,探讨了殖民主义、身份认同和自然和谐。杰克在潘多拉的冒险中找到了新的归属,并与纳美族公主妮特丽建立了深厚的感情。",
"year": 2009,
"rating": 7.8,
"cover": "static/covers/1.jpg",
"tags": "热门,科幻,动作,冒险,3D"
},
{
"title": "新增电影3",
"genre": "动作,科幻,冒险",
"description": "在《复仇者联盟4终局之战》中复仇者联盟在灭霸的响指事件后必须联手逆转灾难拯救消失的一半宇宙生命。影片通过时间旅行元素让英雄们回到过去收集无限宝石同时探讨了牺牲、友谊和英雄主义。钢铁侠托尼·斯塔克的最终牺牲和美国队长的归宿为漫威电影宇宙的一个时代画上了句号。这部影片以其宏大的场面和情感冲击力成为影史票房最高的电影之一。",
"year": 2019,
"rating": 8.4,
"cover": "static/covers/1.jpg",
"tags": "热门,漫威,超级英雄,动作,科幻,终结"
},
{
"title": "新增电影4",
"genre": "科幻,动作,惊悚",
"description": "《盗梦空间》讲述了一群盗贼通过潜入他人梦境窃取机密或植入思想的故事。主角多姆·柯布被迫执行一项看似不可能的任务——在目标潜意识中植入一个想法。影片以复杂的梦境层次和时间概念为特色,探讨了现实与梦境的界限、记忆和赎罪。诺兰的导演手法让观众沉浸在视觉奇观和哲学思考中,结局的开放式悬念更引发了广泛讨论。",
"year": 2010,
"rating": 8.8,
"cover": "static/covers/1.jpg",
"tags": "科幻,惊悚,动作,烧脑,诺兰"
},
{
"title": "新增电影5",
"genre": "动作,犯罪,剧情",
"description": "在《黑暗骑士》中,蝙蝠侠面对他最强大的对手——小丑,一个混乱无秩序的恐怖分子。小丑试图摧毁高谭市的道德秩序,迫使蝙蝠侠在正义与暴力之间做出选择。影片深入探讨了英雄主义、疯狂和社会正义,希斯·莱杰饰演的小丑成为影史经典反派。克里斯蒂安·贝尔的蝙蝠侠和加里·奥德曼的戈登警长也为故事增添了深度,使这部电影不仅是超级英雄片,更是一部哲学惊悚片。",
"year": 2008,
"rating": 9.0,
"cover": "static/covers/1.jpg",
"tags": "超级英雄,犯罪,剧情,黑暗,奥斯卡"
}
]

@ -0,0 +1,537 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>电影批量添加工具</title>
<style>
* {
box-sizing: border-box;
margin: 0;
padding: 0;
font-family: 'Segoe UI', 'Microsoft YaHei', sans-serif;
}
body {
background: linear-gradient(135deg, #1a2a6c, #b21f1f, #fdbb2d);
color: #fff;
min-height: 100vh;
padding: 20px;
}
.container {
max-width: 1200px;
margin: 0 auto;
}
header {
text-align: center;
margin-bottom: 30px;
padding: 20px;
background: rgba(0, 0, 0, 0.3);
border-radius: 10px;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
}
h1 {
font-size: 2.5rem;
margin-bottom: 10px;
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5);
}
.subtitle {
font-size: 1.2rem;
opacity: 0.9;
}
.content {
display: flex;
flex-wrap: wrap;
gap: 30px;
}
.form-section {
flex: 1;
min-width: 300px;
background: rgba(0, 0, 0, 0.5);
padding: 25px;
border-radius: 10px;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
}
.preview-section {
flex: 1;
min-width: 300px;
background: rgba(0, 0, 0, 0.5);
padding: 25px;
border-radius: 10px;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
}
h2 {
margin-bottom: 20px;
padding-bottom: 10px;
border-bottom: 2px solid rgba(255, 255, 255, 0.2);
}
.form-group {
margin-bottom: 20px;
}
label {
display: block;
margin-bottom: 8px;
font-weight: 600;
}
input, textarea, select {
width: 100%;
padding: 12px;
border: none;
border-radius: 5px;
background: rgba(255, 255, 255, 0.9);
font-size: 1rem;
}
textarea {
min-height: 100px;
resize: vertical;
}
.btn {
display: inline-block;
padding: 12px 25px;
background: #e50914;
color: white;
border: none;
border-radius: 5px;
font-size: 1rem;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
margin-right: 10px;
margin-top: 10px;
}
.btn:hover {
background: #ff0a16;
transform: translateY(-2px);
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.3);
}
.btn-secondary {
background: #6c757d;
}
.btn-secondary:hover {
background: #5a6268;
}
.movie-list {
max-height: 500px;
overflow-y: auto;
margin-top: 20px;
}
.movie-item {
background: rgba(255, 255, 255, 0.1);
border-radius: 8px;
padding: 15px;
margin-bottom: 15px;
display: flex;
gap: 15px;
transition: all 0.3s ease;
}
.movie-item:hover {
background: rgba(255, 255, 255, 0.15);
transform: translateY(-3px);
}
.movie-poster {
width: 80px;
height: 120px;
object-fit: cover;
border-radius: 5px;
}
.movie-info {
flex: 1;
}
.movie-title {
font-size: 1.2rem;
font-weight: 600;
margin-bottom: 5px;
}
.movie-meta {
font-size: 0.9rem;
opacity: 0.8;
margin-bottom: 8px;
}
.movie-description {
font-size: 0.9rem;
opacity: 0.9;
line-height: 1.4;
}
.counter {
text-align: center;
margin-top: 15px;
font-size: 1.1rem;
}
.notification {
position: fixed;
top: 20px;
right: 20px;
padding: 15px 25px;
background: #28a745;
color: white;
border-radius: 5px;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
transform: translateX(150%);
transition: transform 0.3s ease;
}
.notification.show {
transform: translateX(0);
}
.batch-section {
margin-top: 30px;
background: rgba(0, 0, 0, 0.5);
padding: 25px;
border-radius: 10px;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
}
@media (max-width: 768px) {
.content {
flex-direction: column;
}
}
</style>
</head>
<body>
<div class="container">
<header>
<h1>电影批量添加工具</h1>
<p class="subtitle">一次性添加多部电影到您的数据库</p>
</header>
<div class="content">
<div class="form-section">
<h2>添加电影信息</h2>
<div class="form-group">
<label for="movieTitle">电影标题 *</label>
<input type="text" id="movieTitle" placeholder="例如:盗梦空间">
</div>
<div class="form-group">
<label for="movieGenres">电影类型(可选,逗号分隔)</label>
<input type="text" id="movieGenres" placeholder="例如:科幻,悬疑,动作">
</div>
<div class="form-group">
<label for="movieDescription">电影描述(可选)</label>
<textarea id="movieDescription" placeholder="请输入电影的剧情简介..."></textarea>
</div>
<div class="form-group">
<label for="movieYear">上映年份(可选)</label>
<input type="number" id="movieYear" placeholder="例如2024" min="1900" max="2030">
</div>
<div class="form-group">
<label for="imageSource">图片生成方式</label>
<select id="imageSource">
<option value="placeholder">使用占位图片</option>
<option value="picsum">使用Picsum随机图片</option>
<option value="custom">自定义图片URL</option>
</select>
</div>
<div class="form-group" id="customImageGroup" style="display: none;">
<label for="customImageUrl">自定义图片URL</label>
<input type="text" id="customImageUrl" placeholder="例如https://example.com/inception.jpg">
</div>
<button class="btn" id="addMovieBtn">添加到列表</button>
<button class="btn btn-secondary" id="clearFormBtn">清空表单</button>
<div class="counter">
当前列表:<span id="movieCount">0</span> 部电影
</div>
</div>
<div class="preview-section">
<h2>电影预览</h2>
<div class="movie-list" id="movieList">
<!-- 电影列表将在这里动态生成 -->
<p style="text-align: center; padding: 20px;">暂无电影,请先添加</p>
</div>
<button class="btn" id="batchAddBtn">批量添加电影</button>
<button class="btn btn-secondary" id="clearListBtn">清空列表</button>
</div>
</div>
<div class="batch-section">
<h2>批量导入功能</h2>
<p>您可以使用以下JSON格式批量导入电影数据</p>
<textarea id="batchJson" placeholder='[
{
"title": "电影标题",
"genres": "科幻,动作",
"description": "电影描述",
"year": "2024",
"imageUrl": "https://example.com/image.jpg"
}
]' style="height: 150px; margin: 15px 0;"></textarea>
<button class="btn" id="importJsonBtn">导入JSON数据</button>
<button class="btn btn-secondary" id="exportJsonBtn">导出为JSON</button>
</div>
</div>
<div class="notification" id="notification">
电影已成功添加到列表!
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
const movieList = document.getElementById('movieList');
const movieCount = document.getElementById('movieCount');
const addMovieBtn = document.getElementById('addMovieBtn');
const clearFormBtn = document.getElementById('clearFormBtn');
const batchAddBtn = document.getElementById('batchAddBtn');
const clearListBtn = document.getElementById('clearListBtn');
const notification = document.getElementById('notification');
const imageSource = document.getElementById('imageSource');
const customImageGroup = document.getElementById('customImageGroup');
const importJsonBtn = document.getElementById('importJsonBtn');
const exportJsonBtn = document.getElementById('exportJsonBtn');
const batchJson = document.getElementById('batchJson');
let movies = [];
// 切换图片源选项
imageSource.addEventListener('change', function() {
if (this.value === 'custom') {
customImageGroup.style.display = 'block';
} else {
customImageGroup.style.display = 'none';
}
});
// 添加电影到列表
addMovieBtn.addEventListener('click', function() {
const title = document.getElementById('movieTitle').value.trim();
const genres = document.getElementById('movieGenres').value.trim();
const description = document.getElementById('movieDescription').value.trim();
const year = document.getElementById('movieYear').value;
const imageSourceValue = imageSource.value;
const customImageUrl = document.getElementById('customImageUrl').value.trim();
if (!title) {
alert('请输入电影标题!');
return;
}
let imageUrl = '';
// 根据选择的图片源生成图片URL
switch (imageSourceValue) {
case 'placeholder':
imageUrl = `https://via.placeholder.com/300x450/1a2a6c/ffffff?text=${encodeURIComponent(title)}`;
break;
case 'picsum':
imageUrl = `https://picsum.photos/300/450?random=${Math.floor(Math.random() * 1000)}`;
break;
case 'custom':
imageUrl = customImageUrl || `https://via.placeholder.com/300x450/1a2a6c/ffffff?text=${encodeURIComponent(title)}`;
break;
}
const movie = {
id: Date.now(),
title,
genres: genres ? genres.split(',').map(g => g.trim()) : [],
description,
year: year || '未知',
imageUrl
};
movies.push(movie);
updateMovieList();
showNotification();
clearFormBtn.click();
});
// 清空表单
clearFormBtn.addEventListener('click', function() {
document.getElementById('movieTitle').value = '';
document.getElementById('movieGenres').value = '';
document.getElementById('movieDescription').value = '';
document.getElementById('movieYear').value = '';
document.getElementById('customImageUrl').value = '';
});
// 批量添加电影
batchAddBtn.addEventListener('click', function() {
if (movies.length === 0) {
alert('请先添加电影到列表!');
return;
}
// 在实际应用中,这里应该发送数据到服务器
alert(`成功添加 ${movies.length} 部电影到数据库!`);
// 模拟API调用
setTimeout(() => {
movies = [];
updateMovieList();
showNotification('电影已成功添加到数据库!');
}, 1000);
});
// 清空列表
clearListBtn.addEventListener('click', function() {
if (movies.length === 0) return;
if (confirm('确定要清空电影列表吗?')) {
movies = [];
updateMovieList();
}
});
// 导入JSON数据
importJsonBtn.addEventListener('click', function() {
try {
const jsonData = JSON.parse(batchJson.value);
if (Array.isArray(jsonData)) {
jsonData.forEach(movie => {
movie.id = Date.now() + Math.random();
if (!movie.imageUrl) {
movie.imageUrl = `https://via.placeholder.com/300x450/1a2a6c/ffffff?text=${encodeURIComponent(movie.title)}`;
}
if (typeof movie.genres === 'string') {
movie.genres = movie.genres.split(',').map(g => g.trim());
}
movies.push(movie);
});
updateMovieList();
showNotification(`成功导入 ${jsonData.length} 部电影!`);
} else {
alert('JSON数据格式不正确应该是一个电影数组');
}
} catch (e) {
alert('JSON格式错误请检查数据格式');
}
});
// 导出为JSON
exportJsonBtn.addEventListener('click', function() {
if (movies.length === 0) {
alert('没有电影数据可导出');
return;
}
const exportData = movies.map(movie => ({
title: movie.title,
genres: movie.genres.join(', '),
description: movie.description,
year: movie.year,
imageUrl: movie.imageUrl
}));
batchJson.value = JSON.stringify(exportData, null, 2);
showNotification('电影数据已导出到JSON区域');
});
// 更新电影列表显示
function updateMovieList() {
movieCount.textContent = movies.length;
if (movies.length === 0) {
movieList.innerHTML = '<p style="text-align: center; padding: 20px;">暂无电影,请先添加</p>';
return;
}
movieList.innerHTML = '';
movies.forEach(movie => {
const movieItem = document.createElement('div');
movieItem.className = 'movie-item';
movieItem.innerHTML = `
<img src="${movie.imageUrl}" alt="${movie.title}" class="movie-poster">
<div class="movie-info">
<div class="movie-title">${movie.title}</div>
<div class="movie-meta">
${movie.year}年 | ${movie.genres.join(', ')}
</div>
<div class="movie-description">${movie.description || '暂无描述'}</div>
</div>
`;
movieList.appendChild(movieItem);
});
}
// 显示通知
function showNotification(message = '电影已成功添加到列表!') {
notification.textContent = message;
notification.classList.add('show');
setTimeout(() => {
notification.classList.remove('show');
}, 3000);
}
// 预填充一些示例电影
function addSampleMovies() {
const sampleMovies = [
{
title: "盗梦空间",
genres: ["科幻", "悬疑", "动作"],
description: "一名窃贼通过潜入他人梦境窃取机密信息,被赋予一个看似不可能的任务:在他人脑海中植入一个想法。",
year: "2010",
imageUrl: "https://picsum.photos/300/450?random=101"
},
{
title: "阿凡达",
genres: ["科幻", "冒险", "动作"],
description: "一名瘫痪的海军陆战队员被派往潘多拉星球执行任务,通过意识控制一个基因工程创造的纳美人体。",
year: "2009",
imageUrl: "https://picsum.photos/300/450?random=102"
},
{
title: "泰坦尼克号",
genres: ["爱情", "剧情", "灾难"],
description: "一位贵族少女与一个贫穷画家在泰坦尼克号邮轮上相遇并坠入爱河,但他们的爱情面临着灾难的考验。",
year: "1997",
imageUrl: "https://picsum.photos/300/450?random=103"
}
];
sampleMovies.forEach(movie => {
movies.push({
id: Date.now() + Math.random(),
...movie
});
});
updateMovieList();
}
// 页面加载时添加示例电影
addSampleMovies();
});
</script>
</body>
</html>

@ -0,0 +1,6 @@
Flask==2.3.3
Flask-SQLAlchemy==3.0.5
Werkzeug==2.3.7
numpy==1.24.3
requests==2.31.0
openai==1.3.0

@ -0,0 +1,12 @@
# reset_db.py
from app import app, db
from models import Movie
with app.app_context():
# 删除movie表中的所有记录
Movie.query.delete()
db.session.commit()
print("电影库内容已清空完成!")
db.drop_all()
db.create_all()
print("数据库重置完成!")

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save