blob: b8ee0c1cbf8a37630b63ecbf42f9076435a9fd1b [file] [log] [blame]
Raverf79fdb62025-06-03 06:02:49 +00001package api;
2
3import java.io.File;
4
Raveraae06122025-06-05 08:13:35 +00005import javax.annotation.PostConstruct;
6
Raverf79fdb62025-06-03 06:02:49 +00007import org.springframework.core.io.FileSystemResource;
8import org.springframework.core.io.Resource;
9import org.springframework.http.HttpHeaders;
Raveraae06122025-06-05 08:13:35 +000010import org.springframework.http.HttpStatus;
Raverf79fdb62025-06-03 06:02:49 +000011import org.springframework.http.MediaType;
12import org.springframework.http.ResponseEntity;
13import org.springframework.web.bind.annotation.RequestParam;
14import org.springframework.web.bind.annotation.RestController;
15import org.springframework.web.multipart.MultipartFile;
Raveraae06122025-06-05 08:13:35 +000016import org.springframework.web.bind.annotation.CrossOrigin;
rhjc6a4ee02025-06-06 00:45:18 +080017import org.springframework.web.bind.annotation.RequestBody;
18import org.springframework.web.bind.annotation.PostMapping;
Raveraae06122025-06-05 08:13:35 +000019
20import com.fasterxml.jackson.core.JsonProcessingException;
21import com.fasterxml.jackson.databind.ObjectMapper;
Raverf79fdb62025-06-03 06:02:49 +000022
23import database.Database1;
Raverf79fdb62025-06-03 06:02:49 +000024import tracker.Tracker;
25
rhjc6a4ee02025-06-06 00:45:18 +080026import entity.Seed;
27import entity.User;
rhj46f62c42025-06-06 23:24:10 +080028import entity.Post;
rhjc6a4ee02025-06-06 00:45:18 +080029
30import java.util.UUID;
Raveraae06122025-06-05 08:13:35 +000031
Raverf79fdb62025-06-03 06:02:49 +000032@RestController
33public class ApiController implements ApiInterface {
34
Raveraae06122025-06-05 08:13:35 +000035 private static Database1 db1;
Raverf79fdb62025-06-03 06:02:49 +000036 private static Tracker tracker;
Raveraae06122025-06-05 08:13:35 +000037 private static ObjectMapper mapper;
rhjc6a4ee02025-06-06 00:45:18 +080038 private static HttpHeaders headers;
39 private static HttpHeaders errorHeaders;
Raveraae06122025-06-05 08:13:35 +000040
41 @PostConstruct
42 public void init() {
43 db1 = new Database1();
44 tracker = new Tracker();
45 mapper = new ObjectMapper();
46 mapper.configure(com.fasterxml.jackson.databind.SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
rhjc6a4ee02025-06-06 00:45:18 +080047 headers = new HttpHeaders();
48 headers.add("Access-Control-Allow-Origin", "*");
49 headers.add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
50 headers.add("Access-Control-Allow-Headers", "Content-Type, Authorization");
51 errorHeaders = new HttpHeaders();
52 errorHeaders.add("Access-Control-Allow-Origin", "*");
53 errorHeaders.add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
54 errorHeaders.add("Access-Control-Allow-Headers", "Content-Type, Authorization");
Raveraae06122025-06-05 08:13:35 +000055 }
Raverf79fdb62025-06-03 06:02:49 +000056
57 @Override
58 public ResponseEntity<Integer> saveTorrent(
59 @RequestParam("userid") String userid,
60 @RequestParam("title") String title,
61 @RequestParam("tag") String tag,
62 @RequestParam("file") MultipartFile file
63 ) {
64 try {
65 Seed seed = new Seed();
rhjc6a4ee02025-06-06 00:45:18 +080066 seed.seedid = UUID.randomUUID().toString(); // 生成唯一的种子ID
67 seed.seeduserid = userid;
68 seed.title = title;
69 seed.seedsize = "1GB";
70 seed.seedtag = tag;
Raverf79fdb62025-06-03 06:02:49 +000071 seed.url = "http://example.com/torrent"; // 示例URL
rhjc6a4ee02025-06-06 00:45:18 +080072 System.out.println("seed is null? " + (seed == null));
Raveraae06122025-06-05 08:13:35 +000073 int ret = db1.RegisterSeed(seed);
rhjc6a4ee02025-06-06 00:45:18 +080074 if (ret != 0) {
75 // 如果注册种子失败,返回错误状态
76 return new ResponseEntity<>(ret, headers, HttpStatus.INTERNAL_SERVER_ERROR);
77 }
Raverf79fdb62025-06-03 06:02:49 +000078 File tempFile = File.createTempFile(seed.seedid, file.getOriginalFilename());
79 file.transferTo(tempFile);
80 tracker.SaveTorrent(seed.seedid, tempFile);
Raveraae06122025-06-05 08:13:35 +000081 return new ResponseEntity<>(0, headers, HttpStatus.OK); // 返回 0 表示成功
Raverf79fdb62025-06-03 06:02:49 +000082 } catch (Exception e) {
83 e.printStackTrace();
Raveraae06122025-06-05 08:13:35 +000084 return new ResponseEntity<>(1, errorHeaders, HttpStatus.INTERNAL_SERVER_ERROR); // 返回 1 表示失败
Raverf79fdb62025-06-03 06:02:49 +000085 }
86 }
87
88 @Override
Raveraae06122025-06-05 08:13:35 +000089 @CrossOrigin(origins = "*", allowedHeaders = "*") // 允许所有来源和头部
Raverf79fdb62025-06-03 06:02:49 +000090 public ResponseEntity<Resource> getTorrent(
rhjc6a4ee02025-06-06 00:45:18 +080091 @RequestParam("torrentId") String seedid,
92 @RequestParam("userId") String userid
Raverf79fdb62025-06-03 06:02:49 +000093 ) {
Raveraae06122025-06-05 08:13:35 +000094 File file = tracker.GetTTorent(seedid, userid);
Raverf79fdb62025-06-03 06:02:49 +000095 if (file != null) {
96 FileSystemResource resource = new FileSystemResource(file);
97 return ResponseEntity.ok()
98 .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + file.getName() + "\"")
Raveraae06122025-06-05 08:13:35 +000099 .header(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, HttpHeaders.CONTENT_DISPOSITION) // 关键:允许前端访问Content-Disposition
Raverf79fdb62025-06-03 06:02:49 +0000100 .contentType(MediaType.APPLICATION_OCTET_STREAM)
101 .body(resource);
102 } else {
Raveraae06122025-06-05 08:13:35 +0000103 return ResponseEntity.notFound().build();
104 }
105 }
106
Raveraae06122025-06-05 08:13:35 +0000107 @Override
108 public ResponseEntity<String> getSeedListByTag(
109 @RequestParam("tag") String tag
110 ) {
111 try {
112 Seed[] seeds = db1.GetSeedListByTag(tag);
rhjc6a4ee02025-06-06 00:45:18 +0800113 if (seeds == null || seeds.length == 0) {
114 return new ResponseEntity<>("[]", errorHeaders, HttpStatus.INTERNAL_SERVER_ERROR); // 返回 404 表示未找到种子
115 }
Raveraae06122025-06-05 08:13:35 +0000116 String json = mapper.writeValueAsString(seeds);
Raveraae06122025-06-05 08:13:35 +0000117 return new ResponseEntity<>(json, headers, HttpStatus.OK);
118 } catch (JsonProcessingException e) {
119 e.printStackTrace();
Raveraae06122025-06-05 08:13:35 +0000120 return new ResponseEntity<>("[]", errorHeaders, HttpStatus.INTERNAL_SERVER_ERROR);
121 }
122 }
123
124 @Override
rhjc6a4ee02025-06-06 00:45:18 +0800125 public ResponseEntity<String> getUserProfile(
126 @RequestParam("userid") String userid
127 ) {
128 try {
129 User user = db1.GetInformation(userid);
130 if (user == null) {
131 return new ResponseEntity<>("", errorHeaders, HttpStatus.INTERNAL_SERVER_ERROR);
132 }
133 String json = mapper.writeValueAsString(user);
134 return new ResponseEntity<>(json, headers, HttpStatus.OK);
135 } catch (JsonProcessingException e) {
136 e.printStackTrace();
137 return new ResponseEntity<>("", errorHeaders, HttpStatus.INTERNAL_SERVER_ERROR);
138 }
139 }
140
141 @Override
142 @CrossOrigin(origins = "*", allowedHeaders = "*")
143 public ResponseEntity<Integer> changeProfile(
144 @RequestBody String requestBody
145 ) {
146 try {
147 // 解析 JSON 数据
148 com.fasterxml.jackson.databind.JsonNode jsonNode = mapper.readTree(requestBody);
149
150 // 安全地获取 userid 字段
151 com.fasterxml.jackson.databind.JsonNode useridNode = jsonNode.get("userid");
152 if (useridNode == null) {
153 return new ResponseEntity<>(1, HttpStatus.BAD_REQUEST);
154 }
155 String userid = useridNode.asText();
156
157 // 添加参数验证
158 if (userid == null || userid.trim().isEmpty()) {
159 return new ResponseEntity<>(1, HttpStatus.BAD_REQUEST);
160 }
161
162 // 手动映射前端字段到 User 对象,处理类型转换
163 User user = new User();
164 user.userid = userid;
165
166 // 安全地获取其他字段并进行类型转换
167 if (jsonNode.has("username") && !jsonNode.get("username").isNull()) {
168 user.username = jsonNode.get("username").asText();
169 }
170 if (jsonNode.has("school") && !jsonNode.get("school").isNull()) {
171 user.school = jsonNode.get("school").asText();
172 }
173 if (jsonNode.has("gender") && !jsonNode.get("gender").isNull()) {
174 user.sex = jsonNode.get("gender").asText();
175 }
176 if (jsonNode.has("avatar_url") && !jsonNode.get("avatar_url").isNull()) {
177 user.pictureurl = jsonNode.get("avatar_url").asText();
178 }
179
180 // 处理 account_status 的类型转换(字符串/数字 -> 布尔值)
181 if (jsonNode.has("account_status") && !jsonNode.get("account_status").isNull()) {
182 com.fasterxml.jackson.databind.JsonNode statusNode = jsonNode.get("account_status");
183 if (statusNode.isTextual()) {
184 String statusStr = statusNode.asText();
185 user.accountstate = "1".equals(statusStr) || "封禁".equals(statusStr);
186 } else if (statusNode.isNumber()) {
187 user.accountstate = statusNode.asInt() == 1;
188 } else if (statusNode.isBoolean()) {
189 user.accountstate = statusNode.asBoolean();
190 }
191 }
192
193 // 处理 invite_left 的类型转换(字符串 -> 整数)
194 if (jsonNode.has("invite_left") && !jsonNode.get("invite_left").isNull()) {
195 com.fasterxml.jackson.databind.JsonNode inviteNode = jsonNode.get("invite_left");
196 if (inviteNode.isTextual()) {
197 try {
198 user.invitetimes = Integer.parseInt(inviteNode.asText());
199 } catch (NumberFormatException e) {
200 user.invitetimes = 0; // 默认值
201 }
202 } else if (inviteNode.isNumber()) {
203 user.invitetimes = inviteNode.asInt();
204 }
205 }
206
207 int ret = db1.UpdateInformation(user);
208 if (ret == 0) {
209 return new ResponseEntity<>(0, HttpStatus.OK); // 返回 0 表示成功
210 } else {
211 return new ResponseEntity<>(ret, HttpStatus.INTERNAL_SERVER_ERROR); // 返回其他状态表示失败
212 }
213 } catch (JsonProcessingException e) {
214 e.printStackTrace();
215 return new ResponseEntity<>(1, HttpStatus.INTERNAL_SERVER_ERROR); // 返回 1 表示处理失败
216 } catch (Exception e) {
217 e.printStackTrace();
218 return new ResponseEntity<>(1, HttpStatus.INTERNAL_SERVER_ERROR);
219 }
220 }
221
222 @Override
223 public ResponseEntity<String> getUserSeeds(
224 @RequestParam("userid") String userid
225 ) {
226 try {
227 Seed[] seeds = db1.GetSeedListByUser(userid);
228 if (seeds == null || seeds.length == 0) {
229 return new ResponseEntity<>("[]", errorHeaders, HttpStatus.INTERNAL_SERVER_ERROR); // 返回 404 表示未找到种子
230 }
231 String json = mapper.writeValueAsString(seeds);
232 return new ResponseEntity<>(json, headers, HttpStatus.OK);
233 } catch (JsonProcessingException e) {
234 e.printStackTrace();
235 return new ResponseEntity<>("[]", errorHeaders, HttpStatus.INTERNAL_SERVER_ERROR);
236 }
237 }
238
239 @Override
240 @CrossOrigin(origins = "*", allowedHeaders = "*")
241 public ResponseEntity<Integer> deleteSeed(
242 @RequestBody String requestBody
243 ) {
244 try {
245 // 解析 JSON 数据
246 com.fasterxml.jackson.databind.JsonNode jsonNode = mapper.readTree(requestBody);
247 com.fasterxml.jackson.databind.JsonNode seedidNode = jsonNode.get("seedid");
248 if (seedidNode == null) {
249 return new ResponseEntity<>(1, HttpStatus.BAD_REQUEST);
250 }
251 String seedid = seedidNode.asText();
252
253 // 添加参数验证
254 if (seedid == null || seedid.trim().isEmpty()) {
255 return new ResponseEntity<>(1, HttpStatus.BAD_REQUEST);
256 }
257
258 int ret = db1.DeleteSeed(seedid);
259 if (ret == 0) {
260 return new ResponseEntity<>(0, HttpStatus.OK);
261 } else {
262 return new ResponseEntity<>(ret, HttpStatus.INTERNAL_SERVER_ERROR);
263 }
264 } catch (Exception e) {
265 e.printStackTrace();
266 return new ResponseEntity<>(1, HttpStatus.INTERNAL_SERVER_ERROR);
267 }
268 }
269
270 @Override
271 public ResponseEntity<String> getUserStat(
272 @RequestParam("userid") String userid
273 ) {
274 try {
275 User user = db1.GetInformation(userid);
276 if (user == null) {
277 return new ResponseEntity<>("", errorHeaders, HttpStatus.INTERNAL_SERVER_ERROR);
278 }
279 String json = mapper.writeValueAsString(user);
280 return new ResponseEntity<>(json, headers, HttpStatus.OK);
281 } catch (JsonProcessingException e) {
282 e.printStackTrace();
283 return new ResponseEntity<>("", errorHeaders, HttpStatus.INTERNAL_SERVER_ERROR);
284 }
285 }
286
287 @Override
Raveraae06122025-06-05 08:13:35 +0000288 public ResponseEntity<String> getTorrentDetail(
289 @RequestParam("id") String seedid
290 ) {
291 try {
292 Seed seed = db1.GetSeedInformation(seedid);
293 if (seed != null) {
294 String json = mapper.writeValueAsString(seed);
295 HttpHeaders headers = new HttpHeaders();
296 headers.add("Access-Control-Allow-Origin", "*");
297 headers.add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
298 headers.add("Access-Control-Allow-Headers", "Content-Type, Authorization");
299 return new ResponseEntity<>(json, headers, HttpStatus.OK);
300 } else {
301 return ResponseEntity.notFound().build(); // 返回 404 表示种子未找到
302 }
303 } catch (JsonProcessingException e) {
304 e.printStackTrace();
305 HttpHeaders errorHeaders = new HttpHeaders();
306 errorHeaders.add("Access-Control-Allow-Origin", "*");
307 errorHeaders.add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
308 errorHeaders.add("Access-Control-Allow-Headers", "Content-Type, Authorization");
309 return new ResponseEntity<>("{}", errorHeaders, HttpStatus.INTERNAL_SERVER_ERROR);
Raverf79fdb62025-06-03 06:02:49 +0000310 }
311 }
rhj46f62c42025-06-06 23:24:10 +0800312
313 @Override
314 @CrossOrigin(origins = "*", allowedHeaders = "*")
315 public ResponseEntity<String> loginUser(
316 @RequestBody String requestBody
317 ) {
318 try {
319 // 解析前端发送的JSON数据 {email: xxx, password: xxx}
320 com.fasterxml.jackson.databind.JsonNode jsonNode = mapper.readTree(requestBody);
321
322 // 获取email和password字段
323 com.fasterxml.jackson.databind.JsonNode emailNode = jsonNode.get("email");
324 com.fasterxml.jackson.databind.JsonNode passwordNode = jsonNode.get("password");
325
326 if (emailNode == null || passwordNode == null) {
327 com.fasterxml.jackson.databind.node.ObjectNode errorJson = mapper.createObjectNode();
328 errorJson.put("message", "缺少必要参数");
329 String jsonError = mapper.writeValueAsString(errorJson);
330 return new ResponseEntity<>(jsonError, HttpStatus.BAD_REQUEST);
331 }
332
333 String email = emailNode.asText();
334 String password = passwordNode.asText();
335
336 // 参数验证
337 if (email == null || email.trim().isEmpty() ||
338 password == null || password.trim().isEmpty()) {
339 com.fasterxml.jackson.databind.node.ObjectNode errorJson = mapper.createObjectNode();
340 errorJson.put("message", "邮箱和密码不能为空");
341 String jsonError = mapper.writeValueAsString(errorJson);
342 return new ResponseEntity<>(jsonError, HttpStatus.BAD_REQUEST);
343 }
344
345 // 创建User对象进行登录验证
346 User user = new User();
347 user.email = email.trim();
348 user.password = password;
349
350 String userid = db1.LoginUser(user);
351 if (userid != null) {
352 com.fasterxml.jackson.databind.node.ObjectNode responseJson = mapper.createObjectNode();
353 responseJson.put("userId", userid);
354 responseJson.put("userid", userid);
355 responseJson.put("message", "登录成功");
356 String jsonResponse = mapper.writeValueAsString(responseJson);
357 return new ResponseEntity<>(jsonResponse, HttpStatus.OK);
358 } else {
359 // 返回JSON格式的错误信息
360 com.fasterxml.jackson.databind.node.ObjectNode errorJson = mapper.createObjectNode();
361 errorJson.put("message", "登录失败,请检查账号密码");
362 String jsonError = mapper.writeValueAsString(errorJson);
363 return new ResponseEntity<>(jsonError, HttpStatus.UNAUTHORIZED);
364 }
365 } catch (JsonProcessingException e) {
366 e.printStackTrace();
367 com.fasterxml.jackson.databind.node.ObjectNode errorJson = mapper.createObjectNode();
368 try {
369 errorJson.put("message", "服务器内部错误");
370 String jsonError = mapper.writeValueAsString(errorJson);
371 return new ResponseEntity<>(jsonError, HttpStatus.INTERNAL_SERVER_ERROR);
372 } catch (JsonProcessingException ex) {
373 return new ResponseEntity<>("{\"message\":\"服务器内部错误\"}", HttpStatus.INTERNAL_SERVER_ERROR);
374 }
375 } catch (Exception e) {
376 e.printStackTrace();
377 com.fasterxml.jackson.databind.node.ObjectNode errorJson = mapper.createObjectNode();
378 try {
379 errorJson.put("message", "服务器内部错误");
380 String jsonError = mapper.writeValueAsString(errorJson);
381 return new ResponseEntity<>(jsonError, HttpStatus.INTERNAL_SERVER_ERROR);
382 } catch (JsonProcessingException ex) {
383 return new ResponseEntity<>("{\"message\":\"服务器内部错误\"}", HttpStatus.INTERNAL_SERVER_ERROR);
384 }
385 }
386 }
387
388 @Override
389 @CrossOrigin(origins = "*", allowedHeaders = "*")
390 public ResponseEntity<Integer> registerUser(
391 @RequestBody String requestBody
392 ) {
393 try {
394 System.out.println("Register request body: " + requestBody);
395 // 解析 JSON 数据
396 com.fasterxml.jackson.databind.JsonNode jsonNode = mapper.readTree(requestBody);
397
398 // 安全地获取字段
399 com.fasterxml.jackson.databind.JsonNode usernameNode = jsonNode.get("username");
400 com.fasterxml.jackson.databind.JsonNode passwordNode = jsonNode.get("password");
401 com.fasterxml.jackson.databind.JsonNode inviteEmailNode = jsonNode.get("invite_email");
402
403 if (usernameNode == null || passwordNode == null || inviteEmailNode == null) {
404 return new ResponseEntity<>(2, HttpStatus.BAD_REQUEST); // 参数不完整
405 }
406
407 String username = usernameNode.asText();
408 String password = passwordNode.asText();
409 String inviteEmail = inviteEmailNode.asText();
410
411 // 参数验证
412 if (username == null || username.trim().isEmpty() ||
413 password == null || password.trim().isEmpty() ||
414 inviteEmail == null || inviteEmail.trim().isEmpty()) {
415 return new ResponseEntity<>(2, HttpStatus.BAD_REQUEST);
416 }
417
418 // 创建 User 对象
419 User user = new User();
420 user.userid = java.util.UUID.randomUUID().toString(); // 生成唯一用户ID
421 user.username = username.trim();
422 user.password = password;
423 user.email = inviteEmail.trim(); // 使用邀请邮箱作为用户邮箱
424
425 // 设置默认值
426 user.sex = "m"; // 默认性别
427 user.school = ""; // 默认学校
428 user.pictureurl = ""; // 默认头像URL
429 user.profile = ""; // 默认个人简介
430 user.accountstate = false; // 默认账号状态为正常
431 user.invitetimes = 5; // 默认邀请次数
432
433 // 设置时间字段
434 user.lastDetectedTime = new java.util.Date();
435 user.fakeLastDetectedTime = new java.util.Date();
436
437 // 调用数据库注册方法
438 int ret = db1.RegisterUser(user);
439 System.out.println("Register result: " + ret);
440
441 // // 如果注册成功,还需要创建对应的 UserPT 记录
442 // if (ret == 0) {
443 // try {
444 // entity.UserPT userPT = new entity.UserPT();
445 // userPT.userid = user.userid;
446 // userPT.magic = 100; // 初始魔力值
447 // userPT.upload = 0L; // 初始上传量
448 // userPT.download = 0L; // 初始下载量
449 // userPT.share = 0.0; // 初始分享率
450 // userPT.farmurl = ""; // 默认做种路径
451 // userPT.viptime = 0; // 初始VIP次数
452 // userPT.user = user; // 设置关联
453
454 // int ptRet = db1.RegisterUserPT(userPT);
455 // if (ptRet != 0) {
456 // // 如果 UserPT 创建失败,记录日志但不影响主要注册流程
457 // System.err.println("Warning: Failed to create UserPT for user " + user.userid);
458 // }
459 // } catch (Exception e) {
460 // System.err.println("Warning: Exception creating UserPT: " + e.getMessage());
461 // }
462 // }
463
464 if (ret == 0) {
465 return new ResponseEntity<>(0, HttpStatus.OK); // 返回 0 表示注册成功
466 } else if (ret == 1) {
467 return new ResponseEntity<>(1, HttpStatus.CONFLICT); // 返回 1 表示邮箱重复
468 } else {
469 return new ResponseEntity<>(ret, HttpStatus.BAD_REQUEST); // 返回 2 表示未被邀请或其他错误
470 }
471 } catch (JsonProcessingException e) {
472 e.printStackTrace();
473 return new ResponseEntity<>(2, HttpStatus.INTERNAL_SERVER_ERROR); // 返回 2 表示处理失败
474 } catch (Exception e) {
475 e.printStackTrace();
476 return new ResponseEntity<>(2, HttpStatus.INTERNAL_SERVER_ERROR);
477 }
478 }
479
480 @Override
481 public ResponseEntity<String> getForum() {
482 try {
483 Post[] posts = db1.GetPostList();
484 if (posts == null || posts.length == 0) {
485 return new ResponseEntity<>("[]", HttpStatus.OK); // 返回空数组表示没有帖子
486 }
487 String json = mapper.writeValueAsString(posts);
488 return new ResponseEntity<>(json, HttpStatus.OK);
489 } catch (JsonProcessingException e) {
490 e.printStackTrace();
491 return new ResponseEntity<>("[]", HttpStatus.INTERNAL_SERVER_ERROR);
492 } catch (Exception e) {
493 e.printStackTrace();
494 return new ResponseEntity<>("[]", HttpStatus.INTERNAL_SERVER_ERROR);
495 }
496 }
Raverf79fdb62025-06-03 06:02:49 +0000497}