Files
neomovies-api/src/routes/movies.js
Foxix fe7399ef9f Update 2 files
- /src/routes/movies.js
- /src/config/tmdb.js
2025-01-16 16:21:32 +00:00

737 lines
22 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const express = require('express');
const router = express.Router();
const { formatDate } = require('../utils/date');
// Middleware для логирования запросов
router.use((req, res, next) => {
console.log('Movies API Request:', {
method: req.method,
path: req.path,
query: req.query,
params: req.params
});
next();
});
/**
* @swagger
* /movies/search:
* get:
* summary: Поиск фильмов
* description: Поиск фильмов по запросу с поддержкой русского языка
* tags: [movies]
* parameters:
* - in: query
* name: query
* required: true
* description: Поисковый запрос
* schema:
* type: string
* example: Матрица
* - in: query
* name: page
* description: Номер страницы (по умолчанию 1)
* schema:
* type: integer
* minimum: 1
* default: 1
* example: 1
* responses:
* 200:
* description: Успешный поиск
* content:
* application/json:
* schema:
* type: object
* properties:
* page:
* type: integer
* description: Текущая страница
* total_pages:
* type: integer
* description: Всего страниц
* total_results:
* type: integer
* description: Всего результатов
* results:
* type: array
* items:
* $ref: '#/components/schemas/Movie'
* 400:
* description: Неверный запрос
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
* 500:
* description: Ошибка сервера
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
*/
router.get('/search', async (req, res) => {
try {
const { query, page = 1 } = req.query;
if (!query) {
return res.status(400).json({ error: 'Query parameter is required' });
}
console.log('Search request:', { query, page });
const data = await req.tmdb.searchMovies(query, page);
console.log('Search response:', {
page: data.page,
total_results: data.total_results,
total_pages: data.total_pages,
results_count: data.results?.length
});
// Форматируем даты в результатах
const formattedResults = data.results.map(movie => ({
...movie,
release_date: formatDate(movie.release_date)
}));
res.json({
...data,
results: formattedResults
});
} catch (error) {
console.error('Error searching movies:', error);
res.status(500).json({ error: error.message });
}
});
/**
* @swagger
* /search/multi:
* get:
* summary: Мультипоиск
* description: Поиск фильмов и сериалов по запросу
* tags: [search]
* parameters:
* - in: query
* name: query
* required: true
* description: Поисковый запрос
* schema:
* type: string
* - in: query
* name: page
* description: Номер страницы
* schema:
* type: integer
* minimum: 1
* default: 1
* responses:
* 200:
* description: Успешный поиск
* content:
* application/json:
* schema:
* type: object
* properties:
* page:
* type: integer
* results:
* type: array
* items:
* type: object
* properties:
* id:
* type: integer
* title:
* type: string
* name:
* type: string
* media_type:
* type: string
* enum: [movie, tv]
*/
router.get('/search/multi', async (req, res) => {
try {
const { query, page = 1 } = req.query;
if (!query) {
return res.status(400).json({ error: 'Query parameter is required' });
}
console.log('Multi search request:', { query, page });
// Параллельный поиск фильмов и сериалов
const [moviesData, tvData] = await Promise.all([
req.tmdb.searchMovies(query, page),
req.tmdb.searchTVShows(query, page)
]);
// Объединяем и сортируем результаты по популярности
const combinedResults = [
...moviesData.results.map(movie => ({
...movie,
media_type: 'movie',
release_date: formatDate(movie.release_date)
})),
...tvData.results.map(show => ({
...show,
media_type: 'tv',
first_air_date: formatDate(show.first_air_date)
}))
].sort((a, b) => b.popularity - a.popularity);
// Пагинация результатов
const itemsPerPage = 20;
const startIndex = (parseInt(page) - 1) * itemsPerPage;
const paginatedResults = combinedResults.slice(startIndex, startIndex + itemsPerPage);
res.json({
page: parseInt(page),
results: paginatedResults,
total_pages: Math.ceil(combinedResults.length / itemsPerPage),
total_results: combinedResults.length
});
} catch (error) {
console.error('Error in multi search:', error);
res.status(500).json({ error: error.message });
}
});
/**
* @swagger
* /movies/popular:
* get:
* summary: Популярные фильмы
* description: Получает список популярных фильмов с русскими названиями и описаниями
* tags: [movies]
* parameters:
* - in: query
* name: page
* description: Номер страницы
* schema:
* type: integer
* minimum: 1
* default: 1
* example: 1
* responses:
* 200:
* description: Список популярных фильмов
* content:
* application/json:
* schema:
* type: object
* properties:
* page:
* type: integer
* results:
* type: array
* items:
* $ref: '#/components/schemas/Movie'
* 500:
* description: Ошибка сервера
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
*/
router.get('/popular', async (req, res) => {
try {
const { page } = req.query;
const pageNum = parseInt(page, 10) || 1;
console.log('Popular movies request:', {
requestedPage: page,
parsedPage: pageNum,
rawQuery: req.query
});
if (pageNum < 1) {
return res.status(400).json({ error: 'Page must be greater than 0' });
}
const movies = await req.tmdb.getPopularMovies(pageNum);
console.log('Popular movies response:', {
requestedPage: pageNum,
returnedPage: movies.page,
totalPages: movies.total_pages,
resultsCount: movies.results?.length
});
if (!movies || !movies.results) {
throw new Error('Invalid response from TMDB');
}
const formattedResults = movies.results.map(movie => ({
...movie,
release_date: formatDate(movie.release_date)
}));
res.json({
...movies,
results: formattedResults
});
} catch (error) {
console.error('Popular movies error:', error);
res.status(500).json({
error: 'Failed to fetch popular movies',
details: error.message
});
}
});
/**
* @swagger
* /movies/top-rated:
* get:
* summary: Лучшие фильмы
* description: Получает список лучших фильмов с русскими названиями и описаниями
* tags: [movies]
* parameters:
* - in: query
* name: page
* description: Номер страницы
* schema:
* type: integer
* minimum: 1
* default: 1
* example: 1
* responses:
* 200:
* description: Список лучших фильмов
* content:
* application/json:
* schema:
* type: object
* properties:
* page:
* type: integer
* results:
* type: array
* items:
* $ref: '#/components/schemas/Movie'
* 500:
* description: Ошибка сервера
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
*/
router.get('/top-rated', async (req, res) => {
try {
const { page } = req.query;
const pageNum = parseInt(page, 10) || 1;
if (pageNum < 1) {
return res.status(400).json({ error: 'Page must be greater than 0' });
}
const movies = await req.tmdb.getTopRatedMovies(pageNum);
if (!movies || !movies.results) {
throw new Error('Invalid response from TMDB');
}
const formattedResults = movies.results.map(movie => ({
...movie,
release_date: formatDate(movie.release_date)
}));
res.json({
...movies,
results: formattedResults
});
} catch (error) {
console.error('Top rated movies error:', error);
res.status(500).json({
error: 'Failed to fetch top rated movies',
details: error.message
});
}
});
/**
* @swagger
* /movies/{id}:
* get:
* summary: Детали фильма
* description: Получает подробную информацию о фильме по его ID
* tags: [movies]
* parameters:
* - in: path
* name: id
* required: true
* description: ID фильма
* schema:
* type: integer
* example: 550
* responses:
* 200:
* description: Детали фильма
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Movie'
* 404:
* description: Фильм не найден
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
* 500:
* description: Ошибка сервера
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
*/
router.get('/:id', async (req, res) => {
try {
const { id } = req.params;
const movie = await req.tmdb.getMovie(id);
if (!movie) {
return res.status(404).json({ error: 'Movie not found' });
}
res.json({
...movie,
release_date: formatDate(movie.release_date)
});
} catch (error) {
console.error('Get movie error:', error);
res.status(500).json({
error: 'Failed to fetch movie details',
details: error.message
});
}
});
/**
* @swagger
* /movies/{id}/external-ids:
* get:
* summary: Внешние ID фильма
* description: Получает внешние идентификаторы фильма (IMDb, и т.д.)
* tags: [movies]
* parameters:
* - in: path
* name: id
* required: true
* description: ID фильма
* schema:
* type: integer
* example: 550
* responses:
* 200:
* description: Внешние ID фильма
* content:
* application/json:
* schema:
* type: object
* properties:
* imdb_id:
* type: string
* facebook_id:
* type: string
* instagram_id:
* type: string
* twitter_id:
* type: string
* 404:
* description: Фильм не найден
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
* 500:
* description: Ошибка сервера
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
*/
router.get('/:id/external-ids', async (req, res) => {
try {
const { id } = req.params;
const externalIds = await req.tmdb.getMovieExternalIDs(id);
if (!externalIds) {
return res.status(404).json({ error: 'External IDs not found' });
}
res.json(externalIds);
} catch (error) {
console.error('Get external IDs error:', error);
res.status(500).json({
error: 'Failed to fetch external IDs',
details: error.message
});
}
});
/**
* @swagger
* /movies/upcoming:
* get:
* summary: Предстоящие фильмы
* description: Получает список предстоящих фильмов с русскими названиями и описаниями
* tags: [movies]
* parameters:
* - in: query
* name: page
* description: Номер страницы
* schema:
* type: integer
* minimum: 1
* default: 1
* example: 1
* responses:
* 200:
* description: Список предстоящих фильмов
* content:
* application/json:
* schema:
* type: object
* properties:
* page:
* type: integer
* results:
* type: array
* items:
* $ref: '#/components/schemas/Movie'
* 500:
* description: Ошибка сервера
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
*/
router.get('/upcoming', async (req, res) => {
try {
const { page } = req.query;
const pageNum = parseInt(page, 10) || 1;
if (pageNum < 1) {
return res.status(400).json({ error: 'Page must be greater than 0' });
}
const movies = await req.tmdb.getUpcomingMovies(pageNum);
if (!movies || !movies.results) {
throw new Error('Invalid response from TMDB');
}
const formattedResults = movies.results.map(movie => ({
...movie,
release_date: formatDate(movie.release_date)
}));
res.json({
...movies,
results: formattedResults
});
} catch (error) {
console.error('Upcoming movies error:', error);
res.status(500).json({
error: 'Failed to fetch upcoming movies',
details: error.message
});
}
});
/**
* @swagger
* /movies/{id}/videos:
* get:
* summary: Видео фильма
* description: Получает список видео для фильма (трейлеры, тизеры и т.д.)
* tags: [movies]
* parameters:
* - in: path
* name: id
* required: true
* description: ID фильма
* schema:
* type: integer
* example: 550
* responses:
* 200:
* description: Список видео
* content:
* application/json:
* schema:
* type: object
* properties:
* results:
* type: array
* items:
* type: object
* properties:
* id:
* type: string
* key:
* type: string
* name:
* type: string
* site:
* type: string
* type:
* type: string
* 404:
* description: Видео не найдены
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
* 500:
* description: Ошибка сервера
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
*/
router.get('/:id/videos', async (req, res) => {
try {
const { id } = req.params;
const videos = await req.tmdb.getMovieVideos(id);
if (!videos || !videos.results) {
return res.status(404).json({ error: 'Videos not found' });
}
res.json(videos);
} catch (error) {
console.error('Get videos error:', error);
res.status(500).json({
error: 'Failed to fetch videos',
details: error.message
});
}
});
/**
* @swagger
* /movies/genre/{id}:
* get:
* summary: Фильмы по жанру
* description: Получает список фильмов определенного жанра
* tags: [movies]
* parameters:
* - in: path
* name: id
* required: true
* description: ID жанра
* schema:
* type: integer
* example: 28
* - in: query
* name: page
* description: Номер страницы
* schema:
* type: integer
* minimum: 1
* default: 1
* example: 1
* responses:
* 200:
* description: Список фильмов
* content:
* application/json:
* schema:
* type: object
* properties:
* page:
* type: integer
* results:
* type: array
* items:
* $ref: '#/components/schemas/Movie'
* 404:
* description: Жанр не найден
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
* 500:
* description: Ошибка сервера
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
*/
router.get('/genre/:id', async (req, res) => {
try {
const { id } = req.params;
const { page = 1 } = req.query;
console.log('Fetching movies by genre:', { id, page });
const response = await req.tmdb.makeRequest('get', '/discover/movie', {
params: {
with_genres: id,
page,
language: 'ru-RU',
'vote_count.gte': 100,
'vote_average.gte': 1,
sort_by: 'popularity.desc',
include_adult: false
}
});
console.log('Movies by genre response:', {
page: response.data.page,
total_results: response.data.total_results,
results_count: response.data.results?.length
});
// Форматируем даты в результатах
const formattedResults = response.data.results.map(movie => ({
...movie,
release_date: movie.release_date ? formatDate(movie.release_date) : undefined
}));
res.json({
...response.data,
results: formattedResults
});
} catch (error) {
console.error('Error fetching movies by genre:', error.response?.data || error.message);
res.status(500).json({
error: 'Failed to fetch movies by genre',
details: error.response?.data?.status_message || error.message
});
}
});
/**
* @swagger
* /movies/genres:
* get:
* summary: Получение списка жанров
* description: Возвращает список всех доступных жанров фильмов
* tags: [movies]
* responses:
* 200:
* description: Список жанров
*/
router.get('/genres', async (req, res) => {
try {
console.log('Fetching genres...');
const response = await req.tmdb.makeRequest('get', '/genre/movie/list', {
language: 'ru-RU'
});
console.log('Genres response:', response.data);
res.json(response.data);
} catch (error) {
console.error('Error fetching genres:', error.response?.data || error.message);
res.status(500).json({
error: 'Failed to fetch genres',
details: error.response?.data?.status_message || error.message
});
}
});
module.exports = router;