Added movie search

This commit is contained in:
2025-03-01 21:10:26 +11:00
parent 540782e2d5
commit 141b541e98
6 changed files with 174 additions and 18 deletions

41
handler/movie_search.go Normal file
View File

@@ -0,0 +1,41 @@
package handler
import (
"net/http"
"projectreshoot/config"
"projectreshoot/tmdb"
"projectreshoot/view/component/search"
"projectreshoot/view/page"
"github.com/rs/zerolog"
)
func SearchMovies(
logger *zerolog.Logger,
config *config.Config,
) http.Handler {
return http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
query := r.FormValue("search")
if query == "" {
w.WriteHeader(http.StatusOK)
return
}
movies, err := tmdb.SearchMovies(config.TMDBToken, query, false, 1)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
return
}
search.MovieResults(movies, &config.TMDBConfig.Image).Render(r.Context(), w)
},
)
}
func MoviesPage() http.Handler {
return http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
page.Movies().Render(r.Context(), w)
},
)
}

View File

@@ -61,6 +61,10 @@ func addRoutes(
route("POST /change-bio", loggedIn(handler.ChangeBio(logger, conn)))
route("POST /change-password", loggedIn(fresh(handler.ChangePassword(logger, conn))))
// Movies Search
route("GET /movies", handler.MoviesPage())
route("POST /search-movies", handler.SearchMovies(logger, config))
// Movie page
route("GET /movie/{movie_id}", handler.Movie(logger, config))
}

View File

@@ -23,7 +23,11 @@ func (movie *Movie) GetPoster(image *Image, size string) string {
}
func (movie *Movie) ReleaseYear() string {
return movie.ReleaseDate[:4]
if movie.ReleaseDate == "" {
return ""
} else {
return "(" + movie.ReleaseDate[:4] + ")"
}
}
func (movie *Movie) FGenres() string {
@@ -31,5 +35,8 @@ func (movie *Movie) FGenres() string {
for _, genre := range movie.Genres {
genres += genre.Name + ", "
}
return genres[:len(genres)-2]
if len(genres) > 2 {
return genres[:len(genres)-2]
}
return genres
}

View File

@@ -4,6 +4,7 @@ import (
"encoding/json"
"fmt"
"net/url"
"path"
"github.com/pkg/errors"
)
@@ -16,23 +17,51 @@ type Result struct {
type ResultMovies struct {
Result
Results []struct {
Adult bool `json:"adult"`
BackdropPath string `json:"backdrop_path"`
GenreIDs []int `json:"genre_ids"`
ID int32 `json:"id"`
OriginalLanguage string `json:"original_language"`
OriginalTitle string `json:"original_title"`
Overview string `json:"overview"`
Popularity int `json:"popularity"`
PosterPath string `json:"poster_path"`
ReleaseDate string `json:"release_date"`
Title string `json:"title"`
Video bool `json:"video"`
VoteAverage int `json:"vote_average"`
VoteCount int `json:"vote_count"`
} `json:"results"`
Results []ResultMovie `json:"results"`
}
type ResultMovie struct {
Adult bool `json:"adult"`
BackdropPath string `json:"backdrop_path"`
GenreIDs []int `json:"genre_ids"`
ID int32 `json:"id"`
OriginalLanguage string `json:"original_language"`
OriginalTitle string `json:"original_title"`
Overview string `json:"overview"`
Popularity int `json:"popularity"`
PosterPath string `json:"poster_path"`
ReleaseDate string `json:"release_date"`
Title string `json:"title"`
Video bool `json:"video"`
VoteAverage int `json:"vote_average"`
VoteCount int `json:"vote_count"`
}
func (movie *ResultMovie) GetPoster(image *Image, size string) string {
base, err := url.Parse(image.SecureBaseURL)
if err != nil {
return ""
}
fullPath := path.Join(base.Path, size, movie.PosterPath)
base.Path = fullPath
return base.String()
}
func (movie *ResultMovie) ReleaseYear() string {
if movie.ReleaseDate == "" {
return ""
} else {
return "(" + movie.ReleaseDate[:4] + ")"
}
}
// TODO: genres list https://developer.themoviedb.org/reference/genre-movie-list
// func (movie *ResultMovie) FGenres() string {
// genres := ""
// for _, genre := range movie.Genres {
// genres += genre.Name + ", "
// }
// return genres[:len(genres)-2]
// }
func SearchMovies(token string, query string, adult bool, page int) (*ResultMovies, error) {
url := "https://api.themoviedb.org/3/search/movie" +

View File

@@ -0,0 +1,44 @@
package search
import "projectreshoot/tmdb"
import "fmt"
templ MovieResults(movies *tmdb.ResultMovies, image *tmdb.Image) {
for _, movie := range movies.Results {
<div
class="bg-surface0 p-4 rounded-lg shadow-lg flex
items-start space-x-4"
>
<img
src={ movie.GetPoster(image, "w92") }
alt="Movie Poster"
class="rounded-lg object-cover"
width="96"
height="144"
onerror="this.onerror=null; setFallbackColor(this);"
/>
<script>
function setFallbackColor(img) {
const baseColor = getComputedStyle(document.documentElement).
getPropertyValue('--base').trim();
img.src = `data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='96' height='144'%3E%3Crect width='100%' height='100%' fill='${baseColor}'/%3E%3C/svg%3E`;
}
</script>
<div>
<a
href={ templ.SafeURL(fmt.Sprintf("/movie/%v", movie.ID)) }
class="text-xl font-semibold transition hover:text-green"
>{ movie.Title } { movie.ReleaseYear() }</a>
<p class="text-subtext0">
Released:
<span class="font-medium">{ movie.ReleaseDate }</span>
</p>
<p class="text-subtext0">
Original Title:
<span class="font-medium">{ movie.OriginalTitle }</span>
</p>
<p class="text-subtext0">{ movie.Overview }</p>
</div>
</div>
}
}

View File

@@ -0,0 +1,31 @@
package page
import "projectreshoot/view/layout"
templ Movies() {
@layout.Global() {
<div class="max-w-4xl mx-auto md:mt-0 mt-2 px-2 md:px-0">
<form hx-post="/search-movies" hx-target="#search-movies-results">
<div
class="max-w-100 flex items-center space-x-2 mb-2"
>
<input
id="search"
name="search"
type="text"
placeholder="Search movies..."
class="flex-grow p-2 border rounded-lg
bg-mantle border-surface2 shadow-sm
focus:outline-none focus:ring-2 focus:ring-blue"
/>
<button
type="submit"
class="py-2 px-4 bg-green text-mantle rounded-lg transition
hover:cursor-pointer hover:bg-green/75"
>Search</button>
</div>
<div id="search-movies-results" class="space-y-4"></div>
</form>
</div>
}
}