simplify proxy

This commit is contained in:
2025-11-09 20:14:44 +02:00
parent 31ddaafe0b
commit c1e9be71b2
3 changed files with 88 additions and 107 deletions

View File

@@ -67,9 +67,9 @@ func Handler(w http.ResponseWriter, r *http.Request) {
tvHandler := handlersPkg.NewTVHandler(tvService)
favoritesHandler := handlersPkg.NewFavoritesHandler(favoritesService, globalCfg)
docsHandler := handlersPkg.NewDocsHandler()
searchHandler := handlersPkg.NewSearchHandler(tmdbService, kpService)
unifiedHandler := handlersPkg.NewUnifiedHandler(tmdbService, kpService)
categoriesHandler := handlersPkg.NewCategoriesHandler(tmdbService).WithKinopoisk(kpService)
searchHandler := handlersPkg.NewSearchHandler(tmdbService, kpService)
unifiedHandler := handlersPkg.NewUnifiedHandler(tmdbService, kpService)
categoriesHandler := handlersPkg.NewCategoriesHandler(tmdbService).WithKinopoisk(kpService)
playersHandler := handlersPkg.NewPlayersHandler(globalCfg)
torrentsHandler := handlersPkg.NewTorrentsHandler(torrentService, tmdbService)
reactionsHandler := handlersPkg.NewReactionsHandler(reactionsService)
@@ -96,8 +96,8 @@ func Handler(w http.ResponseWriter, r *http.Request) {
api.HandleFunc("/categories/{id}/movies", categoriesHandler.GetMoviesByCategory).Methods("GET")
api.HandleFunc("/categories/{id}/media", categoriesHandler.GetMediaByCategory).Methods("GET")
api.HandleFunc("/players/alloha/{id_type}/{id}", playersHandler.GetAllohaPlayer).Methods("GET")
api.HandleFunc("/players/alloha/meta/kp/{kp_id}", playersHandler.GetAllohaMetaByKP).Methods("GET")
api.HandleFunc("/players/alloha/{id_type}/{id}", playersHandler.GetAllohaPlayer).Methods("GET")
api.HandleFunc("/players/alloha/meta/kp/{kp_id}", playersHandler.GetAllohaMetaByKP).Methods("GET")
api.HandleFunc("/players/lumex/{id_type}/{id}", playersHandler.GetLumexPlayer).Methods("GET")
api.HandleFunc("/players/vibix/{id_type}/{id}", playersHandler.GetVibixPlayer).Methods("GET")
api.HandleFunc("/players/hdvb/{id_type}/{id}", playersHandler.GetHDVBPlayer).Methods("GET")
@@ -118,18 +118,18 @@ func Handler(w http.ResponseWriter, r *http.Request) {
api.HandleFunc("/reactions/{mediaType}/{mediaId}/counts", reactionsHandler.GetReactionCounts).Methods("GET")
api.HandleFunc("/images/{size}/{path:.*}", imagesHandler.GetImage).Methods("GET")
api.HandleFunc("/images/{type}/{id}", imagesHandler.GetImage).Methods("GET")
api.HandleFunc("/movies/search", movieHandler.Search).Methods("GET")
api.HandleFunc("/movies/popular", movieHandler.Popular).Methods("GET")
api.HandleFunc("/movies/top-rated", movieHandler.TopRated).Methods("GET")
api.HandleFunc("/movies/upcoming", movieHandler.Upcoming).Methods("GET")
api.HandleFunc("/movies/now-playing", movieHandler.NowPlaying).Methods("GET")
api.HandleFunc("/movies/{id}", movieHandler.GetByID).Methods("GET")
// Unified prefixed routes
api.HandleFunc("/movie/{id}", unifiedHandler.GetMovie).Methods("GET")
api.HandleFunc("/tv/{id}", unifiedHandler.GetTV).Methods("GET")
api.HandleFunc("/search", unifiedHandler.Search).Methods("GET")
api.HandleFunc("/movies/{id}", movieHandler.GetByID).Methods("GET")
// Unified prefixed routes
api.HandleFunc("/movie/{id}", unifiedHandler.GetMovie).Methods("GET")
api.HandleFunc("/tv/{id}", unifiedHandler.GetTV).Methods("GET")
api.HandleFunc("/search", unifiedHandler.Search).Methods("GET")
api.HandleFunc("/movies/{id}/recommendations", movieHandler.GetRecommendations).Methods("GET")
api.HandleFunc("/movies/{id}/similar", movieHandler.GetSimilar).Methods("GET")
api.HandleFunc("/movies/{id}/external-ids", movieHandler.GetExternalIDs).Methods("GET")

View File

@@ -94,7 +94,7 @@ func main() {
api.HandleFunc("/reactions/{mediaType}/{mediaId}/counts", reactionsHandler.GetReactionCounts).Methods("GET")
api.HandleFunc("/images/{size}/{path:.*}", imagesHandler.GetImage).Methods("GET")
api.HandleFunc("/images/{type}/{id}", imagesHandler.GetImage).Methods("GET")
api.HandleFunc("/movies/search", movieHandler.Search).Methods("GET")
api.HandleFunc("/movies/popular", movieHandler.Popular).Methods("GET")

View File

@@ -1,17 +1,16 @@
package handlers
import (
"fmt"
"io"
"net/http"
"net/url"
"os"
"path/filepath"
"strings"
"time"
"fmt"
"io"
"net/http"
"net/url"
"os"
"path/filepath"
"strings"
"time"
"github.com/gorilla/mux"
"neomovies-api/pkg/config"
"github.com/gorilla/mux"
)
type ImagesHandler struct{}
@@ -20,109 +19,91 @@ func NewImagesHandler() *ImagesHandler { return &ImagesHandler{} }
func (h *ImagesHandler) GetImage(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
size := vars["size"]
imagePath := vars["path"]
imageType := vars["type"]
imageID := vars["id"]
if size == "" || imagePath == "" {
http.Error(w, "Size and path are required", http.StatusBadRequest)
if imageType == "" || imageID == "" {
http.Error(w, "Type and ID are required", http.StatusBadRequest)
return
}
// Попробуем декодировать путь заранее (на фронте абсолютные URL передаются как encodeURIComponent)
decodedPath := imagePath
if dp, err := url.QueryUnescape(imagePath); err == nil {
decodedPath = dp
}
if imagePath == "placeholder.jpg" || decodedPath == "placeholder.jpg" {
if imageID == "placeholder.jpg" {
h.servePlaceholder(w, r)
return
}
validSizes := []string{"w92", "w154", "w185", "w342", "w500", "w780", "original"}
if !h.isValidSize(size, validSizes) {
size = "original"
var imageURL string
switch imageType {
case "kp":
imageURL = fmt.Sprintf("https://kinopoiskapiunofficial.tech/images/posters/kp/%s.jpg", imageID)
case "kp_small":
imageURL = fmt.Sprintf("https://kinopoiskapiunofficial.tech/images/posters/kp_small/%s.jpg", imageID)
case "kp_big":
imageURL = fmt.Sprintf("https://kinopoiskapiunofficial.tech/images/posters/kp_big/%s.jpg", imageID)
default:
http.Error(w, "Invalid image type. Use: kp, kp_small, kp_big", http.StatusBadRequest)
return
}
var imageURL string
// Нормализуем абсолютные ссылки вида "https:/..." → "https://...", а также "//..." → "https://..."
normalized := decodedPath
if strings.HasPrefix(normalized, "//") {
normalized = "https:" + normalized
}
if strings.HasPrefix(normalized, "http:/") && !strings.HasPrefix(normalized, "http://") {
normalized = strings.Replace(normalized, "http:/", "http://", 1)
}
if strings.HasPrefix(normalized, "https:/") && !strings.HasPrefix(normalized, "https://") {
normalized = strings.Replace(normalized, "https:/", "https://", 1)
}
client := &http.Client{Timeout: 12 * time.Second}
if strings.HasPrefix(normalized, "http://") || strings.HasPrefix(normalized, "https://") {
// Проксируем внешний абсолютный URL (например, Kinopoisk)
imageURL = normalized
} else {
// TMDB относительный путь
imageURL = fmt.Sprintf("%s/%s/%s", config.TMDBImageBaseURL, size, imagePath)
}
// Подготовим несколько вариантов заголовков для обхода ограничений источников
buildRequest := func(targetURL string, attempt int) (*http.Request, error) {
req, err := http.NewRequest("GET", targetURL, nil)
if err != nil {
return nil, err
}
// Универсальные заголовки как у браузера
req.Header.Set("Accept", "image/avif,image/webp,image/apng,image/*,*/*;q=0.8")
req.Header.Set("Accept-Language", "ru-RU,ru;q=0.9,en-US;q=0.8,en;q=0.7")
if ua := r.Header.Get("User-Agent"); ua != "" {
req.Header.Set("User-Agent", ua)
} else {
req.Header.Set("User-Agent", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0 Safari/537.36")
}
client := &http.Client{Timeout: 12 * time.Second}
// Настройка Referer: для Yandex/Kinopoisk ставим kinopoisk.ru, иначе — origin URL
parsed, _ := url.Parse(targetURL)
host := strings.ToLower(parsed.Host)
switch attempt {
case 0:
if strings.Contains(host, "kinopoisk") || strings.Contains(host, "yandex") {
req.Header.Set("Referer", "https://www.kinopoisk.ru/")
} else if parsed.Scheme != "" && parsed.Host != "" {
req.Header.Set("Referer", parsed.Scheme+"://"+parsed.Host+"/")
}
case 1:
// Без Referer
default:
// Оставляем как есть
}
// Подготовим несколько вариантов заголовков для обхода ограничений источников
buildRequest := func(targetURL string, attempt int) (*http.Request, error) {
req, err := http.NewRequest("GET", targetURL, nil)
if err != nil {
return nil, err
}
// Универсальные заголовки как у браузера
req.Header.Set("Accept", "image/avif,image/webp,image/apng,image/*,*/*;q=0.8")
req.Header.Set("Accept-Language", "ru-RU,ru;q=0.9,en-US;q=0.8,en;q=0.7")
if ua := r.Header.Get("User-Agent"); ua != "" {
req.Header.Set("User-Agent", ua)
} else {
req.Header.Set("User-Agent", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0 Safari/537.36")
}
return req, nil
}
// Настройка Referer: для Yandex/Kinopoisk ставим kinopoisk.ru, иначе — origin URL
parsed, _ := url.Parse(targetURL)
host := strings.ToLower(parsed.Host)
switch attempt {
case 0:
if strings.Contains(host, "kinopoisk") || strings.Contains(host, "yandex") {
req.Header.Set("Referer", "https://www.kinopoisk.ru/")
} else if parsed.Scheme != "" && parsed.Host != "" {
req.Header.Set("Referer", parsed.Scheme+"://"+parsed.Host+"/")
}
case 1:
// Без Referer
default:
// Оставляем как есть
}
return req, nil
}
// До 2-х попыток: с реферером источника и без реферера
var resp *http.Response
var err error
for attempt := 0; attempt < 2; attempt++ {
var req *http.Request
req, err = buildRequest(imageURL, attempt)
if err != nil {
continue
}
resp, err = client.Do(req)
if err == nil && resp != nil && resp.StatusCode == http.StatusOK {
break
}
if resp != nil {
resp.Body.Close()
}
}
// До 2-х попыток: с реферером источника и без реферера
var resp *http.Response
var err error
for attempt := 0; attempt < 2; attempt++ {
var req *http.Request
req, err = buildRequest(imageURL, attempt)
if err != nil {
continue
}
resp, err = client.Do(req)
if err == nil && resp != nil && resp.StatusCode == http.StatusOK {
break
}
if resp != nil {
resp.Body.Close()
}
}
if err != nil {
h.servePlaceholder(w, r)
return
}
defer resp.Body.Close()
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
h.servePlaceholder(w, r)