Fix SPA routing and frontend search duplicates
Some checks failed
Release APKs / Build TV APK (push) Has been cancelled
Release APKs / Build Mobile APK (push) Has been cancelled
Release APKs / Create Release (push) Has been cancelled

This commit is contained in:
vndangkhoa 2026-02-20 21:00:36 +07:00
parent 0230054f92
commit 1bd470731c
5 changed files with 88 additions and 29 deletions

View file

@ -118,6 +118,20 @@ func FileServer(r chi.Router, path string, root http.FileSystem) {
r.Get(path, func(w http.ResponseWriter, r *http.Request) {
rctx := chi.RouteContext(r.Context())
pathPrefix := strings.TrimSuffix(rctx.RoutePattern(), "/*")
reqPath := strings.TrimPrefix(r.URL.Path, pathPrefix)
if reqPath == "" {
reqPath = "/"
}
// Check if file exists in the static directory
f, err := root.Open(reqPath)
if err == nil {
f.Close()
} else {
// If not found, rewrite path to serve index.html for SPA routing
r.URL.Path = pathPrefix + "/index.html"
}
fs := http.StripPrefix(pathPrefix, http.FileServer(root))
fs.ServeHTTP(w, r)
})

View file

@ -236,35 +236,36 @@ func (s *PhimMoiChillScraper) GetMovieDetail(slug string) (*models.RophimMovie,
}
})
// Fallback: If no episodes found, finding "Xem phim" button for single movie
if len(episodes) == 0 {
// Common selectors for "Watch" button: .btn-watch, a:contains("Xem phim")
watchBtn := doc.Find("a.btn-watch, a.btn-see, ul.btn-block a")
if watchBtn.Length() > 0 {
href, _ := watchBtn.Attr("href")
if strings.Contains(href, "/xem/") {
episodes = append(episodes, models.Episode{
Number: 1,
Title: "Full",
URL: href,
ServerName: "PhimMoiChill",
})
}
} else {
// Try text content
doc.Find("a").Each(func(i int, s *goquery.Selection) {
if strings.Contains(strings.ToLower(s.Text()), "xem phim") {
href, _ := s.Attr("href")
if strings.Contains(href, "/xem/") {
episodes = append(episodes, models.Episode{
Number: 1,
Title: "Full",
URL: href,
ServerName: "PhimMoiChill",
})
return // Break
}
// Fallback/Main: Find "Xem phim" button which is often Episode 1 for series,
// or the only episode for movies. We always check this, not just when len(episodes)==0.
watchBtn := doc.Find("a.btn-watch, a.btn-see, ul.btn-block a")
var watchHref string
if watchBtn.Length() > 0 {
watchHref, _ = watchBtn.Attr("href")
} else {
doc.Find("a").Each(func(i int, s *goquery.Selection) {
if strings.Contains(strings.ToLower(s.Text()), "xem phim") {
href, _ := s.Attr("href")
if strings.Contains(href, "/xem/") {
watchHref = href
}
}
})
}
if watchHref != "" && strings.Contains(watchHref, "/xem/") {
// Only add if Episode 1 is not already present
if _, exists := epMap[1]; !exists {
epMap[1] = len(episodes)
title := "Tập 1"
if len(episodes) == 0 {
title = "Full"
}
episodes = append(episodes, models.Episode{
Number: 1,
Title: title,
URL: watchHref,
ServerName: "PhimMoiChill",
})
}
}

19
backend/test_movie.go Normal file
View file

@ -0,0 +1,19 @@
package main
import (
"encoding/json"
"fmt"
"log"
"streamflow-backend/internal/scraper"
)
func main() {
p := scraper.NewPhimMoiChillScraper()
movie, err := p.GetMovieDetail("vu-tru-cua-doi-ta-pm17193")
if err != nil {
log.Fatal(err)
}
b, _ := json.MarshalIndent(movie, "", " ")
fmt.Println(string(b))
}

19
backend/test_search.go Normal file
View file

@ -0,0 +1,19 @@
package main
import (
"encoding/json"
"fmt"
"log"
"streamflow-backend/internal/scraper"
)
func main() {
p := scraper.NewPhimMoiChillScraper()
movies, err := p.Search("vũ trụ của đôi ta", 1)
if err != nil {
log.Fatal(err)
}
b, _ := json.MarshalIndent(movies, "", " ")
fmt.Println(string(b))
}

View file

@ -68,7 +68,13 @@ export const HomeContent = ({ topPadding = "pt-24" }: HomeContentProps) => {
if (!data || data.length === 0) {
setHasMore(false);
} else {
setMovies(prev => page === 1 ? data : [...prev, ...data]);
setMovies(prev => {
if (page === 1) return data;
// Deduplicate arrays when appending to prevent React StrictMode or fast-scroll double fetches
const existingIds = new Set(prev.map(m => m.id));
const newUniqueMovies = data.filter((m: Movie) => !existingIds.has(m.id));
return [...prev, ...newUniqueMovies];
});
}
} catch {
console.error("Failed to fetch movies");