diff --git a/api/index.go b/api/index.go index fa71980..81b0980 100644 --- a/api/index.go +++ b/api/index.go @@ -100,6 +100,11 @@ func Handler(w http.ResponseWriter, r *http.Request) { api.HandleFunc("/players/vidsrc/{media_type}/{imdb_id}", playersHandler.GetVidsrcPlayer).Methods("GET") api.HandleFunc("/players/vidlink/movie/{imdb_id}", playersHandler.GetVidlinkMoviePlayer).Methods("GET") api.HandleFunc("/players/vidlink/tv/{tmdb_id}", playersHandler.GetVidlinkTVPlayer).Methods("GET") + + // Client-side parsing players (custom player with HLS support) + api.HandleFunc("/players/vidsrc-parse/{media_type}/{imdb_id}", playersHandler.GetVidsrcParserPlayer).Methods("GET") + api.HandleFunc("/players/vidlink-parse/movie/{imdb_id}", playersHandler.GetVidlinkParserMoviePlayer).Methods("GET") + api.HandleFunc("/players/vidlink-parse/tv/{tmdb_id}", playersHandler.GetVidlinkParserTVPlayer).Methods("GET") api.HandleFunc("/players/rgshows/{tmdb_id}", playersHandler.GetRgShowsPlayer).Methods("GET") api.HandleFunc("/players/rgshows/{tmdb_id}/{season}/{episode}", playersHandler.GetRgShowsTVPlayer).Methods("GET") api.HandleFunc("/players/iframevideo/{kinopoisk_id}/{imdb_id}", playersHandler.GetIframeVideoPlayer).Methods("GET") diff --git a/main.go b/main.go index dec7ab7..c900e7c 100644 --- a/main.go +++ b/main.go @@ -81,6 +81,11 @@ func main() { api.HandleFunc("/players/vidsrc/{media_type}/{imdb_id}", playersHandler.GetVidsrcPlayer).Methods("GET") api.HandleFunc("/players/vidlink/movie/{imdb_id}", playersHandler.GetVidlinkMoviePlayer).Methods("GET") api.HandleFunc("/players/vidlink/tv/{tmdb_id}", playersHandler.GetVidlinkTVPlayer).Methods("GET") + + // Client-side parsing players (custom player with HLS support) + api.HandleFunc("/players/vidsrc-parse/{media_type}/{imdb_id}", playersHandler.GetVidsrcParserPlayer).Methods("GET") + api.HandleFunc("/players/vidlink-parse/movie/{imdb_id}", playersHandler.GetVidlinkParserMoviePlayer).Methods("GET") + api.HandleFunc("/players/vidlink-parse/tv/{tmdb_id}", playersHandler.GetVidlinkParserTVPlayer).Methods("GET") api.HandleFunc("/torrents/search/{imdbId}", torrentsHandler.SearchTorrents).Methods("GET") api.HandleFunc("/torrents/movies", torrentsHandler.SearchMovies).Methods("GET") diff --git a/pkg/handlers/client_parser.go b/pkg/handlers/client_parser.go new file mode 100644 index 0000000..da74f8b --- /dev/null +++ b/pkg/handlers/client_parser.go @@ -0,0 +1,419 @@ +package handlers + +import ( + "fmt" + "log" + "net/http" + + "github.com/gorilla/mux" +) + +// GetVidsrcParserPlayer handles Vidsrc.to player with client-side parsing +func (h *PlayersHandler) GetVidsrcParserPlayer(w http.ResponseWriter, r *http.Request) { + log.Printf("GetVidsrcParserPlayer called: %s %s", r.Method, r.URL.Path) + + vars := mux.Vars(r) + imdbId := vars["imdb_id"] + mediaType := vars["media_type"] // "movie" or "tv" + + if imdbId == "" || mediaType == "" { + http.Error(w, "imdb_id and media_type are required", http.StatusBadRequest) + return + } + + var embedURL string + if mediaType == "movie" { + embedURL = fmt.Sprintf("https://vidsrc.to/embed/movie/%s", imdbId) + } else if mediaType == "tv" { + season := r.URL.Query().Get("season") + episode := r.URL.Query().Get("episode") + if season == "" || episode == "" { + http.Error(w, "season and episode are required for TV shows", http.StatusBadRequest) + return + } + embedURL = fmt.Sprintf("https://vidsrc.to/embed/tv/%s/%s/%s", imdbId, season, episode) + } else { + http.Error(w, "Invalid media_type. Use 'movie' or 'tv'", http.StatusBadRequest) + return + } + + log.Printf("Generated Vidsrc embed URL: %s", embedURL) + + htmlDoc := generateClientParserHTML(embedURL, "Vidsrc Player") + + w.Header().Set("Content-Type", "text/html; charset=utf-8") + w.Write([]byte(htmlDoc)) + + log.Printf("Successfully served Vidsrc parser player for %s: %s", mediaType, imdbId) +} + +// GetVidlinkParserMoviePlayer handles Vidlink.pro parser for movies +func (h *PlayersHandler) GetVidlinkParserMoviePlayer(w http.ResponseWriter, r *http.Request) { + log.Printf("GetVidlinkParserMoviePlayer called: %s %s", r.Method, r.URL.Path) + + vars := mux.Vars(r) + imdbId := vars["imdb_id"] + + if imdbId == "" { + http.Error(w, "imdb_id is required", http.StatusBadRequest) + return + } + + embedURL := fmt.Sprintf("https://vidlink.pro/movie/%s", imdbId) + + log.Printf("Generated Vidlink movie embed URL: %s", embedURL) + + htmlDoc := generateClientParserHTML(embedURL, "Vidlink Player") + + w.Header().Set("Content-Type", "text/html; charset=utf-8") + w.Write([]byte(htmlDoc)) + + log.Printf("Successfully served Vidlink parser movie player: %s", imdbId) +} + +// GetVidlinkParserTVPlayer handles Vidlink.pro parser for TV shows +func (h *PlayersHandler) GetVidlinkParserTVPlayer(w http.ResponseWriter, r *http.Request) { + log.Printf("GetVidlinkParserTVPlayer called: %s %s", r.Method, r.URL.Path) + + vars := mux.Vars(r) + tmdbId := vars["tmdb_id"] + + if tmdbId == "" { + http.Error(w, "tmdb_id is required", http.StatusBadRequest) + return + } + + season := r.URL.Query().Get("season") + episode := r.URL.Query().Get("episode") + if season == "" || episode == "" { + http.Error(w, "season and episode are required for TV shows", http.StatusBadRequest) + return + } + + embedURL := fmt.Sprintf("https://vidlink.pro/tv/%s/%s/%s", tmdbId, season, episode) + + log.Printf("Generated Vidlink TV embed URL: %s", embedURL) + + htmlDoc := generateClientParserHTML(embedURL, "Vidlink Player") + + w.Header().Set("Content-Type", "text/html; charset=utf-8") + w.Write([]byte(htmlDoc)) + + log.Printf("Successfully served Vidlink parser TV player: %s S%sE%s", tmdbId, season, episode) +} + +func generateClientParserHTML(embedURL, title string) string { + return fmt.Sprintf(` + + + + + %s + + + + +
Инициализация плеера...
+ +
+
+
Загрузка видео...
+
+ + + +
+ +
+ + + + + + +`, title, embedURL, embedURL) +} diff --git a/pkg/handlers/docs.go b/pkg/handlers/docs.go index 0d84450..6c326af 100644 --- a/pkg/handlers/docs.go +++ b/pkg/handlers/docs.go @@ -484,6 +484,116 @@ func getOpenAPISpecWithURL(baseURL string) *OpenAPISpec { }, }, }, + "/api/v1/players/vidsrc-parse/{media_type}/{imdb_id}": map[string]interface{}{ + "get": map[string]interface{}{ + "summary": "Vidsrc плеер с парсингом (кастомный плеер)", + "description": "Возвращает HTML-страницу с кастомным Video.js плеером. Автоматически извлекает m3u8 ссылку из Vidsrc.to через клиентский парсинг в iframe. Использует IMDb ID для фильмов и сериалов.", + "tags": []string{"Players"}, + "parameters": []map[string]interface{}{ + { + "name": "media_type", + "in": "path", + "required": true, + "schema": map[string]interface{}{"type": "string", "enum": []string{"movie", "tv"}}, + "description": "Тип контента: movie (фильм) или tv (сериал)", + }, + { + "name": "imdb_id", + "in": "path", + "required": true, + "schema": map[string]string{"type": "string"}, + "description": "IMDb ID, например tt6385540 (с префиксом tt)", + }, + { + "name": "season", + "in": "query", + "required": false, + "schema": map[string]string{"type": "integer"}, + "description": "Номер сезона (обязательно для TV)", + }, + { + "name": "episode", + "in": "query", + "required": false, + "schema": map[string]string{"type": "integer"}, + "description": "Номер серии (обязательно для TV)", + }, + }, + "responses": map[string]interface{}{ + "200": map[string]interface{}{ + "description": "HTML с кастомным Video.js плеером и системой парсинга", + "content": map[string]interface{}{ + "text/html": map[string]interface{}{}, + }, + }, + "400": map[string]interface{}{"description": "Отсутствуют обязательные параметры"}, + }, + }, + }, + "/api/v1/players/vidlink-parse/movie/{imdb_id}": map[string]interface{}{ + "get": map[string]interface{}{ + "summary": "Vidlink плеер с парсингом для фильмов (кастомный)", + "description": "Возвращает HTML-страницу с кастомным Video.js плеером. Автоматически извлекает m3u8/mp4 ссылку из Vidlink.pro через клиентский парсинг. Использует IMDb ID для фильмов.", + "tags": []string{"Players"}, + "parameters": []map[string]interface{}{ + { + "name": "imdb_id", + "in": "path", + "required": true, + "schema": map[string]string{"type": "string"}, + "description": "IMDb ID фильма, например tt1234567 (с префиксом tt)", + }, + }, + "responses": map[string]interface{}{ + "200": map[string]interface{}{ + "description": "HTML с кастомным Video.js плеером и системой парсинга", + "content": map[string]interface{}{ + "text/html": map[string]interface{}{}, + }, + }, + "400": map[string]interface{}{"description": "IMDb ID не указан"}, + }, + }, + }, + "/api/v1/players/vidlink-parse/tv/{tmdb_id}": map[string]interface{}{ + "get": map[string]interface{}{ + "summary": "Vidlink плеер с парсингом для сериалов (кастомный)", + "description": "Возвращает HTML-страницу с кастомным Video.js плеером. Автоматически извлекает m3u8/mp4 ссылку из Vidlink.pro через клиентский парсинг. Использует TMDB ID для сериалов.", + "tags": []string{"Players"}, + "parameters": []map[string]interface{}{ + { + "name": "tmdb_id", + "in": "path", + "required": true, + "schema": map[string]string{"type": "integer"}, + "description": "TMDB ID сериала, например 94997 (числовой идентификатор без префикса)", + }, + { + "name": "season", + "in": "query", + "required": true, + "schema": map[string]string{"type": "integer"}, + "description": "Номер сезона (обязательно)", + }, + { + "name": "episode", + "in": "query", + "required": true, + "schema": map[string]string{"type": "integer"}, + "description": "Номер серии (обязательно)", + }, + }, + "responses": map[string]interface{}{ + "200": map[string]interface{}{ + "description": "HTML с кастомным Video.js плеером и системой парсинга", + "content": map[string]interface{}{ + "text/html": map[string]interface{}{}, + }, + }, + "400": map[string]interface{}{"description": "Отсутствуют обязательные параметры (tmdb_id, season, episode)"}, + }, + }, + }, "/api/v1/torrents/search/{imdbId}": map[string]interface{}{