From b17e9dea28fde0b12d8ef0befab2dfe3f9c18e30 Mon Sep 17 00:00:00 2001 From: eson <474420502@qq.com> Date: Thu, 14 Aug 2025 00:28:29 +0800 Subject: [PATCH] update --- server/handler.go | 144 +++++++++++++-------------------- server/main.go | 5 +- server/movie.go | 4 +- src/Main.jsx | 59 +++++--------- src/components/VideoPlayer.jsx | 30 +++---- 5 files changed, 100 insertions(+), 142 deletions(-) diff --git a/server/handler.go b/server/handler.go index 230ecb0..6eea1c1 100644 --- a/server/handler.go +++ b/server/handler.go @@ -3,102 +3,20 @@ package main import ( "log" "net/http" - "sort" "strconv" "strings" + "sort" "github.com/gin-gonic/gin" ) -// 重命名电影的处理函数 -func PostRename(c *gin.Context) { - response := gin.H{ - "code": http.StatusInternalServerError, - "message": "An unexpected error occurred.", - } - - // 定义请求结构体 - type RenameRequest struct { - OldName string `json:"old_name" binding:"required"` - NewName string `json:"new_name" binding:"required"` - } - - var req RenameRequest - if err := c.ShouldBindJSON(&req); err != nil { - response["code"] = http.StatusBadRequest - response["message"] = "Invalid request body: " + err.Error() - c.JSON(http.StatusBadRequest, response) - return - } - - oldName := req.OldName - newName := req.NewName - - if oldName == "" || newName == "" { - response["code"] = http.StatusBadRequest - response["message"] = "Old name and new name are required." - c.JSON(http.StatusBadRequest, response) - return - } - - if oldName == newName { - response["code"] = http.StatusBadRequest - response["message"] = "Old name and new name cannot be the same." - c.JSON(http.StatusBadRequest, response) - return - } - - MovieDictLock.Lock() - defer MovieDictLock.Unlock() - - // 检查新名称是否已存在 - if _, exists := MovieDict[newName]; exists { - response["code"] = http.StatusConflict - response["message"] = "New name already exists." - c.JSON(http.StatusConflict, response) - return - } - - // 查找旧名称对应的电影 - movie, exists := MovieDict[oldName] - if exists { - // 更新电影信息 - err := movie.RenameVideo(newName) // 重命名视频文件 - if err != nil { - response["code"] = http.StatusInternalServerError - response["message"] = "Failed to rename video file." - c.JSON(http.StatusInternalServerError, response) - return - } - - delete(MovieDict, oldName) // 删除旧名称的条目 - MovieDict[newName] = movie // 添加新名称的条目 - - response["code"] = http.StatusOK - response["message"] = "Movie renamed successfully." - response["data"] = gin.H{"old_name": oldName, "new_name": newName} - c.JSON(http.StatusOK, response) - log.Printf("Movie renamed from %s to %s successfully", oldName, newName) - return - } - - // 如果旧名称不存在,返回404错误 - response["code"] = http.StatusNotFound - response["message"] = "Movie not found." - c.JSON(http.StatusNotFound, response) - log.Printf("Movie rename failed: %s not found", oldName) -} - +// GetCategoryList 获取分类列表 func GetCategoryList(c *gin.Context) { response := gin.H{ - "code": http.StatusInternalServerError, - "message": "An unexpected error occurred.", - "data": []string{}, + "code": http.StatusOK, + "message": "Success", + "data": Categories, } - - response["code"] = http.StatusOK - response["message"] = "Success" - response["data"] = Categories c.JSON(http.StatusOK, response) } @@ -227,3 +145,55 @@ func MovieList(c *gin.Context) { response["data"] = responseData c.JSON(http.StatusOK, response) } + +// PostRename 重命名文件 +func PostRename(c *gin.Context) { + var requestData struct { + OldName string `json:"old_name"` + NewName string `json:"new_name"` + } + + response := gin.H{ + "code": http.StatusOK, + "message": "Success", + "data": nil, + } + + if err := c.ShouldBindJSON(&requestData); err != nil { + response["code"] = http.StatusBadRequest + response["message"] = "Invalid request data: " + err.Error() + c.JSON(http.StatusBadRequest, response) + return + } + + if requestData.OldName == "" || requestData.NewName == "" { + response["code"] = http.StatusBadRequest + response["message"] = "Both old_name and new_name are required" + c.JSON(http.StatusBadRequest, response) + return + } + + // 查找要重命名的电影 + movie, exists := MovieDict[requestData.OldName] + if !exists { + response["code"] = http.StatusNotFound + response["message"] = "Movie not found" + c.JSON(http.StatusNotFound, response) + return + } + + // 执行重命名 + if err := movie.RenameVideo(requestData.NewName); err != nil { + response["code"] = http.StatusInternalServerError + response["message"] = "Failed to rename video: " + err.Error() + c.JSON(http.StatusInternalServerError, response) + return + } + + // 更新全局字典 + delete(MovieDict, requestData.OldName) + MovieDict[requestData.NewName] = movie + + response["message"] = "Rename successful" + c.JSON(http.StatusOK, response) +} \ No newline at end of file diff --git a/server/main.go b/server/main.go index 2142a47..606197f 100644 --- a/server/main.go +++ b/server/main.go @@ -10,6 +10,9 @@ import ( func main() { initMovie() + // 设置为发布模式 + gin.SetMode(gin.ReleaseMode) + eg := gin.Default() eg.Use(Cors()) eg.Static("/res", "movie/") @@ -37,4 +40,4 @@ func main() { movie.POST("/rename", PostRename) eg.Run("0.0.0.0:4444") -} +} \ No newline at end of file diff --git a/server/movie.go b/server/movie.go index 0094974..4783878 100644 --- a/server/movie.go +++ b/server/movie.go @@ -75,7 +75,7 @@ var Movies []*Movie // 存储所有电影信息的全局切 var MovieDict = make(map[string]*Movie) // 存储需要处理缩略图的视频字典 var MovieDictLock sync.Mutex // 保护MovieDict的并发访问 -var IsRemakePNG = true // 是否重新生成所有PNG缩略图 +var IsRemakePNG = false // 是否重新生成所有PNG缩略图 var Categories = []string{ // 分类 "15min", "30min", "60min", "大于60min", "最新添加"} @@ -331,4 +331,4 @@ func markRecentMovies() { for i := 0; i < len(Movies) && i < 20; i++ { Movies[i].TimeCategory = "最新添加" } -} +} \ No newline at end of file diff --git a/src/Main.jsx b/src/Main.jsx index a10b713..8e32fcb 100644 --- a/src/Main.jsx +++ b/src/Main.jsx @@ -1,9 +1,10 @@ +// ...existing code... import React, { useState, useEffect, useContext, useCallback, useRef } from 'react'; import axios from 'axios'; import Container from '@mui/material/Container'; import Grid from '@mui/material/Grid'; import Pagination from '@mui/material/Pagination'; -import { Link } from 'react-router-dom'; // useLocation removed as not directly used for this fix +import { Link } from 'react-router-dom'; import CircularProgress from '@mui/material/CircularProgress'; import TextField from '@mui/material/TextField'; import InputAdornment from '@mui/material/InputAdornment'; @@ -11,9 +12,10 @@ import IconButton from '@mui/material/IconButton'; import SearchIcon from '@mui/icons-material/Search'; import ClearIcon from '@mui/icons-material/Clear'; -import ConfigContext from './Config'; // Ensure this path is correct -import MovieCard from './components/MovieCard'; // Ensure this path is correct -import CategoryNav from './components/CategoryNav'; // Ensure this path is correct +import ConfigContext from './Config'; +import MovieCard from './components/MovieCard'; +import CategoryNav from './components/CategoryNav'; +// ...existing code... // 分类配置 const categories = [ @@ -71,9 +73,9 @@ const Main = () => { ]), }); - const isMounted = useRef(false); - const isPopStateNav = useRef(false); const scrollTimeoutRef = useRef(null); + const isPopStateNav = useRef(false); + const isMounted = useRef(false); const fetchMovies = useCallback(async (category, page, search = '', options = {}) => { setLoading(true); @@ -114,7 +116,7 @@ const Main = () => { isPopStateNav.current = false; } // IMPORTANT: Removed duplicated scroll logic block that was here - }, []); // No dependencies that change frequently, setPersistedParams is stable + }, [isPopStateNav]); const navigateAndFetch = useCallback((newCategory, newPage, newSearchQuery = '', options = {}) => { const { replace = false, preserveScroll = false, isPopStatePaging = false } = options; @@ -122,33 +124,25 @@ const Main = () => { setActiveCategory(newCategory); setCurrentPage(newPage); setActiveSearchQuery(newSearchQuery); + if (newCategory === SEARCH_CATEGORY) { setSearchInput(newSearchQuery); } else { setSearchInput(''); } - // Determine scroll position for history state - // If preserving scroll, use current window.scrollY or a remembered scroll for the target category if available - // Otherwise, new navigations (not popstate) typically scroll to 0 unless specified. - let scrollForHistory = 0; - if (preserveScroll) { - // For general preserveScroll (like back/fwd), use current scrollY. - // For specific category change restorations, this might be overridden by options.restoreScrollPos in fetchMovies. - scrollForHistory = window.history.state?.appState?.scrollPos || window.scrollY; - } - // If navigating to a category with a known scroll position, that should be prioritized for restoration. - // This is handled by passing restoreScrollPos to fetchMovies. For history state, use `scrollForHistory`. - + const scrollForHistory = preserveScroll + ? (window.history.state?.appState?.scrollPos || window.scrollY) + : 0; const historyState = { category: newCategory, page: newPage, searchQuery: newSearchQuery, - scrollPos: scrollForHistory, // This is the scroll position *at the moment of navigation* + scrollPos: scrollForHistory, }; - const url = window.location.pathname; // Keep URL simple, no query params in URL itself for now + const url = window.location.pathname; const browserHistoryState = window.history.state?.appState; const needsPush = !browserHistoryState || browserHistoryState.category !== newCategory || @@ -168,14 +162,14 @@ const Main = () => { if (oldCategoryState && oldCategoryState !== newCategory) { newCategoryHistory[oldCategoryState] = { ...newCategoryHistory[oldCategoryState], - scrollPos: window.scrollY, // Save scroll of category being left + scrollPos: window.scrollY, }; } newCategoryHistory[newCategory] = { ...newCategoryHistory[newCategory], lastPage: newPage, - scrollPos: scrollForHistory, // Store the scroll we intend to be at for the new state + scrollPos: scrollForHistory, ...(newCategory === SEARCH_CATEGORY && { searchQuery: newSearchQuery }), }; @@ -185,20 +179,11 @@ const Main = () => { }; }); - // Determine scroll position for fetching movies - // If preserving scroll, it means we want to restore to where we were or a specific point - let scrollPosForFetch = scrollForHistory; // Default to the scrollForHistory calculated - if (!preserveScroll) { // If not preserving, new main navigations usually go to top - scrollPosForFetch = 0; - } - // If navigating to a category and it has a specific remembered scroll, that should be preferred - // This part becomes complex if we want category changes to always restore *their* scroll. - // For now, `preserveScroll` will try to keep `window.scrollY`, otherwise `0`. - // PopState correctly restores its specific scroll. + const scrollPosForFetch = preserveScroll ? scrollForHistory : 0; fetchMovies(newCategory, newPage, newSearchQuery, { restoreScrollPos: scrollPosForFetch, - skipScrollRestore: false, // Allow fetchMovies to handle scroll unless explicitly told otherwise later + skipScrollRestore: false, isPopStatePaging: isPopStatePaging, }); }, [fetchMovies, setPersistedParams]); @@ -268,7 +253,7 @@ const Main = () => { clearTimeout(scrollTimeoutRef.current); }; // eslint-disable-next-line react-hooks/exhaustive-deps - }, [navigateAndFetch, setPersistedParams]); // Removed fetchMovies as navigateAndFetch calls it. + }, [navigateAndFetch, setPersistedParams]); useEffect(() => { const handleScroll = () => { @@ -306,7 +291,7 @@ const Main = () => { window.removeEventListener('scroll', handleScroll); clearTimeout(scrollTimeoutRef.current); }; - }, [loading, movies.length, setPersistedParams]); + }, [loading, movies.length, setPersistedParams, isPopStateNav, scrollTimeoutRef]); const handleCategoryChange = useCallback((newCategory) => { @@ -422,7 +407,7 @@ const Main = () => { setActiveSearchQuery(''); setPersistedParams(prev => ({ ...prev, lastKnownState: { ...prev.lastKnownState, searchQuery: '' } })); } - }, [activeCategory, navigateAndFetch, persistedParams.categoryHistory, setPersistedParams, fetchMovies]); + }, [activeCategory, persistedParams.categoryHistory, setPersistedParams, fetchMovies]); // MODIFIED handleRename diff --git a/src/components/VideoPlayer.jsx b/src/components/VideoPlayer.jsx index 5630dd5..a77776c 100644 --- a/src/components/VideoPlayer.jsx +++ b/src/components/VideoPlayer.jsx @@ -122,30 +122,30 @@ const VideoPlayer = () => { file: { attributes: { playsInline: true, // 重要:移动端内联播放 - preload: 'metadata' + preload: 'metadata', + disablePictureInPicture: true // 禁用画中画模式 } } }} style={{ maxWidth: '100%', - maxHeight: '100%' + maxHeight: '100%', + margin: 'auto' // 居中显示 }} /> - {/* 底部信息(仅桌面端显示) */} - {!isMobile && ( - - - 提示:双击视频可全屏播放 - - - )} + {/* 底部信息(移动端显示简化提示) */} + + + {isMobile ? '双击可全屏' : '提示:双击视频可全屏播放'} + + ); };