package handlers
import (
"encoding/json"
"html/template"
"net/http"
"net/url"
"strconv"
"neomovies-api/pkg/models"
"neomovies-api/pkg/services"
)
type WebTorrentHandler struct {
tmdbService *services.TMDBService
}
func NewWebTorrentHandler(tmdbService *services.TMDBService) *WebTorrentHandler {
return &WebTorrentHandler{
tmdbService: tmdbService,
}
}
// Структура для ответа с метаданными
type MediaMetadata struct {
ID int `json:"id"`
Title string `json:"title"`
Type string `json:"type"` // "movie" or "tv"
Year int `json:"year,omitempty"`
PosterPath string `json:"posterPath,omitempty"`
BackdropPath string `json:"backdropPath,omitempty"`
Overview string `json:"overview,omitempty"`
Seasons []SeasonMetadata `json:"seasons,omitempty"`
Episodes []EpisodeMetadata `json:"episodes,omitempty"`
Runtime int `json:"runtime,omitempty"`
Genres []models.Genre `json:"genres,omitempty"`
}
type SeasonMetadata struct {
SeasonNumber int `json:"seasonNumber"`
Name string `json:"name"`
Episodes []EpisodeMetadata `json:"episodes"`
}
type EpisodeMetadata struct {
EpisodeNumber int `json:"episodeNumber"`
SeasonNumber int `json:"seasonNumber"`
Name string `json:"name"`
Overview string `json:"overview,omitempty"`
Runtime int `json:"runtime,omitempty"`
StillPath string `json:"stillPath,omitempty"`
}
// Открытие плеера с магнет ссылкой
func (h *WebTorrentHandler) OpenPlayer(w http.ResponseWriter, r *http.Request) {
magnetLink := r.Header.Get("X-Magnet-Link")
if magnetLink == "" {
magnetLink = r.URL.Query().Get("magnet")
}
if magnetLink == "" {
http.Error(w, "Magnet link is required", http.StatusBadRequest)
return
}
// Декодируем magnet ссылку если она закодирована
decodedMagnet, err := url.QueryUnescape(magnetLink)
if err != nil {
decodedMagnet = magnetLink
}
// Отдаем HTML страницу с плеером
tmpl := `
NeoMovies WebTorrent Player
`
// Создаем template и выполняем его
t, err := template.New("player").Parse(tmpl)
if err != nil {
http.Error(w, "Template error", http.StatusInternalServerError)
return
}
data := struct {
MagnetLink string
}{
MagnetLink: strconv.Quote(decodedMagnet),
}
w.Header().Set("Content-Type", "text/html; charset=utf-8")
err = t.Execute(w, data)
if err != nil {
http.Error(w, "Template execution error", http.StatusInternalServerError)
return
}
}
// API для получения метаданных фильма/сериала по названию
func (h *WebTorrentHandler) GetMetadata(w http.ResponseWriter, r *http.Request) {
query := r.URL.Query().Get("query")
if query == "" {
http.Error(w, "Query parameter is required", http.StatusBadRequest)
return
}
// Пытаемся определить тип контента и найти его
metadata, err := h.searchAndBuildMetadata(query)
if err != nil {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(models.APIResponse{
Success: false,
Message: "Media not found: " + err.Error(),
})
return
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(models.APIResponse{
Success: true,
Data: metadata,
})
}
func (h *WebTorrentHandler) searchAndBuildMetadata(query string) (*MediaMetadata, error) {
// Сначала пробуем поиск по фильмам
movieResults, err := h.tmdbService.SearchMovies(query, 1, "ru-RU", "", 0)
if err == nil && len(movieResults.Results) > 0 {
movie := movieResults.Results[0]
// Получаем детальную информацию о фильме
fullMovie, err := h.tmdbService.GetMovie(movie.ID, "ru-RU")
if err == nil {
return &MediaMetadata{
ID: fullMovie.ID,
Title: fullMovie.Title,
Type: "movie",
Year: extractYear(fullMovie.ReleaseDate),
PosterPath: fullMovie.PosterPath,
BackdropPath: fullMovie.BackdropPath,
Overview: fullMovie.Overview,
Runtime: fullMovie.Runtime,
Genres: fullMovie.Genres,
}, nil
}
}
// Затем пробуем поиск по сериалам
tvResults, err := h.tmdbService.SearchTV(query, 1, "ru-RU", 0)
if err == nil && len(tvResults.Results) > 0 {
tv := tvResults.Results[0]
// Получаем детальную информацию о сериале
fullTV, err := h.tmdbService.GetTVShow(tv.ID, "ru-RU")
if err == nil {
metadata := &MediaMetadata{
ID: fullTV.ID,
Title: fullTV.Name,
Type: "tv",
Year: extractYear(fullTV.FirstAirDate),
PosterPath: fullTV.PosterPath,
BackdropPath: fullTV.BackdropPath,
Overview: fullTV.Overview,
Genres: fullTV.Genres,
}
// Получаем информацию о сезонах и сериях
var allEpisodes []EpisodeMetadata
for _, season := range fullTV.Seasons {
if season.SeasonNumber == 0 {
continue // Пропускаем спецвыпуски
}
seasonDetails, err := h.tmdbService.GetTVSeason(fullTV.ID, season.SeasonNumber, "ru-RU")
if err == nil {
var episodes []EpisodeMetadata
for _, episode := range seasonDetails.Episodes {
episodeData := EpisodeMetadata{
EpisodeNumber: episode.EpisodeNumber,
SeasonNumber: season.SeasonNumber,
Name: episode.Name,
Overview: episode.Overview,
Runtime: episode.Runtime,
StillPath: episode.StillPath,
}
episodes = append(episodes, episodeData)
allEpisodes = append(allEpisodes, episodeData)
}
metadata.Seasons = append(metadata.Seasons, SeasonMetadata{
SeasonNumber: season.SeasonNumber,
Name: season.Name,
Episodes: episodes,
})
}
}
metadata.Episodes = allEpisodes
return metadata, nil
}
}
return nil, err
}
func extractYear(dateString string) int {
if len(dateString) >= 4 {
yearStr := dateString[:4]
if year, err := strconv.Atoi(yearStr); err == nil {
return year
}
}
return 0
}
// Проверяем есть ли нужные методы в TMDB сервисе
func (h *WebTorrentHandler) checkMethods() {
// Эти методы должны существовать в TMDBService:
// - SearchMovies
// - SearchTV
// - GetMovie
// - GetTVShow
// - GetTVSeason
}