diff --git a/server/handler.go b/server/handler.go
index 5dd9985..b2a8bd9 100644
--- a/server/handler.go
+++ b/server/handler.go
@@ -19,28 +19,46 @@ func MovieList(c *gin.Context) {
}
defer c.JSON(0, response)
+ category := c.Query("category")
+
+ var movies []Movie
+ if category != "" {
+ cateidx, err := strconv.Atoi(category)
+ if err != nil {
+ log.Println(err)
+ return
+ }
+ movies = categories[cateidx].Movies
+ }
+
var page int = 0
spage := c.Query("page")
if spage != "" {
p, err := strconv.Atoi(spage)
if err != nil {
log.Println(err)
+ return
}
page = p - 1
}
- var limit int = 10
+ var limit int = 12
slimit := c.Query("limit")
if slimit != "" {
l, err := strconv.Atoi(slimit)
if err != nil {
log.Println(err)
+ return
}
limit = l
}
start := page * limit
var end int = start + limit
+
+ if start > len(movies) {
+ start = len(movies)
+ }
if end > len(movies) {
end = len(movies)
}
diff --git a/server/main.go b/server/main.go
index 00899b9..156130d 100644
--- a/server/main.go
+++ b/server/main.go
@@ -7,20 +7,31 @@ import (
"net/http"
"os/exec"
"path/filepath"
+ "sort"
+ "strconv"
"strings"
"github.com/gin-gonic/gin"
)
-// Movie 电影结构
-type Movie struct {
- Name string `json:"filename"`
- Image string `json:"image"`
+// Category 电影分类结构
+type Category struct {
+ Name string `json:"name"`
+ Movies []Movie `json:"movies"`
}
-var movies []Movie
+// Movie 电影结构
+type Movie struct {
+ Name string `json:"filename"`
+ Image string `json:"image"`
+ Duration int `json:"duration"`
+}
+
+// var movies []Movie
+var categories []Category
func initMovie() {
+ // 需要改进 如果存在这个文件的略缩图, 就不存进movieDict里
var movieDict map[string]string = make(map[string]string)
matches, err := filepath.Glob("movie/*")
@@ -31,7 +42,7 @@ func initMovie() {
for _, filename := range matches {
base := filepath.Base(filename)
- ext := filepath.Ext(base)
+ // ext := filepath.Ext(base)
base = base[:strings.IndexByte(base, '.')]
if _, ok := movieDict[base]; ok {
@@ -40,7 +51,6 @@ func initMovie() {
movieDict[base] = filename
}
- log.Println(base, ext)
}
for key, filename := range movieDict {
@@ -48,7 +58,6 @@ func initMovie() {
// height := 120
log.Println(filename)
- // cmd := exec.Command("ffmpeg", "-i", filename, "-vframes", "1", "-s", fmt.Sprintf("%dx%d", width, height), "movie/"+key+".png")
cmd := exec.Command("ffmpeg",
"-i", filename,
"-vf", "select='isnan(prev_selected_t)+gte(t-prev_selected_t\\,35)',scale=320:180,tile=3x3",
@@ -63,25 +72,60 @@ func initMovie() {
panic("could not generate frame")
}
- // err := generateThumbnail(filename, "movie/"+key+".png", width, height)
- // if err != nil {
- // panic("could not generate frame")
- // }
+ }
+
+ // 初始化分类
+ categories = []Category{
+ {Name: "15min", Movies: []Movie{}},
+ {Name: "30min", Movies: []Movie{}},
+ {Name: "60min", Movies: []Movie{}},
+ {Name: "大于60min", Movies: []Movie{}},
}
filepath.Walk("./movie", func(path string, info fs.FileInfo, err error) error {
if !info.IsDir() && filepath.Ext(info.Name()) != ".png" {
base := info.Name()
+ cmd := exec.Command("ffprobe", "-v", "error", "-select_streams", "v:0", "-show_entries", "stream=duration", "-of", "default=nw=1:nk=1", "./movie/"+info.Name())
+ durationOutput, err := cmd.Output()
+ if err != nil {
+ log.Printf("Error getting duration for %s: %v", info.Name(), err)
+ return err
+ }
+
+ duration, err := strconv.ParseFloat(strings.Trim(string(durationOutput), "\n "), 64)
+ if err != nil {
+ log.Printf("Error parsing duration for %s: %v", info.Name(), err)
+ return err
+ }
log.Println(path, info.Name())
- movies = append(movies, Movie{
- Name: info.Name(),
- Image: base[:strings.IndexByte(base, '.')] + ".png",
- })
+
+ movie := Movie{
+ Name: info.Name(),
+ Image: base[:strings.IndexByte(base, '.')] + ".png",
+ Duration: int(duration / 60.0),
+ }
+ if movie.Duration <= 15 {
+ categories[0].Movies = append(categories[0].Movies, movie)
+ } else if movie.Duration <= 30 {
+ categories[1].Movies = append(categories[1].Movies, movie)
+ } else if movie.Duration <= 60 {
+ categories[2].Movies = append(categories[2].Movies, movie)
+ } else {
+ categories[3].Movies = append(categories[3].Movies, movie)
+ }
+
}
return nil
})
- log.Printf("%##v", movies)
+ for _, category := range categories {
+ var movies = category.Movies
+ sort.Slice(movies, func(i, j int) bool {
+ return movies[i].Duration < movies[j].Duration
+ })
+ }
+
+ // log.Printf("%##v", categories)
}
func main() {
diff --git a/src/CategoryNav.js b/src/CategoryNav.js
new file mode 100644
index 0000000..437499c
--- /dev/null
+++ b/src/CategoryNav.js
@@ -0,0 +1,25 @@
+import React from 'react';
+import Tabs from '@mui/material/Tabs';
+import Tab from '@mui/material/Tab';
+
+const CategoryNav = ({ categories, currentCategory, onCategoryChange }) => {
+ const handleChange = (event, newValue) => {
+ onCategoryChange(newValue);
+ };
+
+ return (
+
+ {categories.map((category, idx) => (
+
+ ))}
+
+ );
+};
+
+export default CategoryNav;
\ No newline at end of file
diff --git a/src/Components.js b/src/Components.js
index 04d3c69..12705af 100644
--- a/src/Components.js
+++ b/src/Components.js
@@ -9,13 +9,24 @@ import Typography from '@mui/material/Typography';
import Pagination from '@mui/material/Pagination';
import { Link } from 'react-router-dom';
import CircularProgress from '@mui/material/CircularProgress';
-import ConfigContext from './Config';
+import ConfigContext from './Config';
+import MovieCard from './MovieCard';
+import CategoryNav from './CategoryNav';
const Main = () => {
const config = useContext(ConfigContext);
+ const [currentCategory, setCurrentCategory] = useState(0);
+
+ const categories = [
+ { label: '15min', idx: 0 },
+ { label: '30min', idx: 1 },
+ { label: '60min', idx: 2 },
+ { label: '大于60min', idx: 3 },
+ ];
+
const [pagination, setPagination] = useState({
movies: [],
page: 1,
@@ -25,24 +36,37 @@ const Main = () => {
const [loading, setLoading] = useState(false);
- const limit = 12;
+ const limit = 20;
- const fetchMovies = async (page) => {
+ const handleCategoryChange = (category) => {
+ setCurrentCategory(category);
+ fetchMovies(category, 1);
+ };
+
+
+ const fetchMovies = async (category, page) => {
setLoading(true);
try {
const response = await axios.get(
- `${config.Host}/movie/?page=${page}&limit=${limit}`
+ `${config.Host}/movie/?page=${page}&limit=${limit}&category=${category}`
);
+
if (response.status === 200) {
const data = response.data.data;
- setPagination({
- movies: data.items,
- page: page,
- total: data.total,
- length: Math.ceil(data.total / limit),
- });
- localStorage.setItem('lastPage', page);
+ console.log(`${config.Host}/movie/?page=${page}&limit=${limit}&category=${category}`);
+ if (data.items.length === 0 && page > 1) {
+ // 如果返回的数据为空且请求的页码大于1,则尝试获取上一页的数据
+ fetchMovies(page - 1);
+ } else {
+ setPagination({
+ movies: data.items,
+ page: page,
+ total: data.total,
+ length: Math.ceil(data.total / limit),
+ });
+ localStorage.setItem('lastPage', page);
+ }
}
} catch (error) {
console.error('Error fetching movies:', error);
@@ -53,11 +77,11 @@ const Main = () => {
useEffect(() => {
const lastPage = localStorage.getItem('lastPage') || 1;
- fetchMovies(lastPage);
+ fetchMovies(currentCategory, lastPage);
}, []);
const handlePageChange = (event, value) => {
- fetchMovies(value);
+ fetchMovies(currentCategory, value);
};
const truncateFilename = (filename, maxLength) => {
@@ -65,9 +89,15 @@ const Main = () => {
? filename.substring(0, maxLength - 3) + '...'
: filename;
};
+
return (
+
+
+
{loading ? (
{
) : (
+
+
+
- {pagination.movies.map((item) => (
- (
+
+
-
-
-
-
-
- {truncateFilename(item.filename, 15)}
-
-
-
-
-
- ))}
-
+
+
+
+ ))}
+
)}
+
+
{
+ const truncateFilename = (filename, maxLength) => {
+ return filename.length > maxLength
+ ? filename.substring(0, maxLength - 3) + '...'
+ : filename;
+ };
+
+ return (
+
+
+
+ {truncateFilename(movie.filename, 15)}
+
+ 时长: {movie.duration} min
+
+
+
+ );
+};
+
+export default MovieCard;
\ No newline at end of file
diff --git a/src/VideoPlayer.js b/src/VideoPlayer.js
index c03ab0f..2297427 100644
--- a/src/VideoPlayer.js
+++ b/src/VideoPlayer.js
@@ -16,6 +16,7 @@ const VideoPlayer = () => {
{filename}