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 }