User: Давай писать функцию
func handleAiSearch(w http.ResponseWriter, r *http.Request) {
	log.Printf("Received request: %s %s\n", r.Method, r.URL.Path)
	deviceData := r.URL.Query().Get("deviceid")
	queryAI := r.URL.Query().Get("query")
	if deviceData == "" && queryAI == "" {
		http.Error(w, "Ошибка.", http.StatusInternalServerError)
		return
	}
	log.Printf("Requested deviceID: %s\n", deviceData)
	log.Printf("Requested queryAI: %s\n", queryAI)
	var exists bool
	err := db.QueryRow("SELECT EXISTS(SELECT 1 FROM devices WHERE device_id = $1)", deviceData).Scan(&exists)
	if err != nil {
		fmt.Printf("Ошибка при проверке существования записей: %v\n", err)
		http.Error(w, "Ошибка при проверке существования записей", http.StatusInternalServerError)
		return
	}
	if exists {
		gptContent := fmt.Sprintf(
			"Порекомендуй список из 40 элементов по Теме из \"Запрос пользователя\" и обязательно разбей по категориям (10 штук на категорию!): фильмы, сериалы, мультфильмы и аниме\n"+
				"Твой ответ должен быть такой (в {} динамичные данные, без {} статичные) (названия должны быть реальными!!)(за другой буду ругать!) {number}; ru_name - {RU_NAME}; en_name - {EN_NAME}; orig_name - {ORIG_NAME}; date - {realise_date}; media_type - {movie,tv}; category - {category}\n"+
				"Запрос пользователя: %s", queryAI)
		gptUrl := os.Getenv("AI_QUERY")
	} else {
		fmt.Printf("Устройство не найдено, доступ запрещен\n")
		http.Error(w, "Устройство не найдено, доступ запрещен", http.StatusInternalServerError)
		return
	}
}
в if exists нужно отправить POST запрос на gptUrl с телом
{
  "messages": [
    {
      "role": "user",
      "content": gptContent
    }
  ],
  "model": "gpt-4o",
  "provider": "NexraChatGPT",
  "web_search": false
}
Content-Type будет либо application/json либо text/event-stream; charset=utf-8
нужно принтануть ответ в лог
Assistant: Для того чтобы отправить POST запрос к GPT API и обработать ответ, необходимо использовать стандартные пакеты, такие как `net/http` для отправки HTTP-запросов и `encoding/json` для работы с JSON. Ниже приведён пример того, как можно изменить вашу функцию `handleAiSearch`, чтобы это реализовать:

```go
package main

import (
        "bytes"
        "encoding/json"
        "fmt"
        "log"
        "net/http"
        "os"

        _ "github.com/lib/pq"
)

func handleAiSearch(w http.ResponseWriter, r *http.Request) {
        log.Printf("Received request: %s %s\n", r.Method, r.URL.Path)
        deviceData := r.URL.Query().Get("deviceid")
        queryAI := r.URL.Query().Get("query")
        if deviceData == "" && queryAI == "" {
                http.Error(w, "Ошибка.", http.StatusInternalServerError)
                return
        }
        log.Printf("Requested deviceID: %s\n", deviceData)
        log.Printf("Requested queryAI: %s\n", queryAI)

        var exists bool
        err := db.QueryRow("SELECT EXISTS(SELECT 1 FROM devices WHERE device_id = $1)", deviceData).Scan(&exists)
        if err != nil {
                log.Printf("Ошибка при проверке существования записей: %v\n", err)
                http.Error(w, "Ошибка при проверке существования записей", http.StatusInternalServerError)
                return
        }

        if exists {
                gptContent := fmt.Sprintf(
                        "Порекомендуй список из 40 элементов по Теме из \"Запрос пользователя\" и обязательно разбей по категориям (10 штук на категорию!): фильмы, сериалы, мультфильмы и аниме\n"+
                                "Твой ответ должен быть такой (в {} динамичные данные, без {} статичные) (названия должны быть реальными!!)(за другой буду ругать!) {number}; ru_name - {RU_NAME}; en_name - {EN_NAME}; orig_name - {ORIG_NAME}; date - {realise_date}; media_type - {movie,tv}; category - {category}\n"+
                                "Запрос пользователя: %s", queryAI)

                gptUrl := os.Getenv("AI_QUERY")

                // Создание запроса
                payload := map[string]interface{}{
                        "messages": []map[string]string{
                                {
                                        "role":    "user",
                                        "content": gptContent,
                                },
                        },
                        "model":     "gpt-4o",
                        "provider":  "NexraChatGPT",
                        "web_search": false,
                }
                payloadBytes, err := json.Marshal(payload)
                if err != nil {
                        log.Printf("Ошибка при сериализации запроса: %v\n", err)
                        http.Error(w, "Ошибка при обработке запроса", http.StatusInternalServerError)
                        return
                }

                // Отправка POST запроса
                resp, err := http.Post(gptUrl, "application/json", bytes.NewBuffer(payloadBytes))
                if err != nil {
                        log.Printf("Ошибка при отправке запроса к API: %v\n", err)
                        http.Error(w, "Ошибка при отправке запроса", http.StatusInternalServerError)
                        return
                }
                defer resp.Body.Close()

                // Чтение ответа
                respBody, err := io.ReadAll(resp.Body)
                if err != nil {
                        log.Printf("Ошибка при чтении ответа: %v\n", err)
                        http.Error(w, "Ошибка при чтении ответа от API", http.StatusInternalServerError)
                        return
                }

                // Принт ответа в лог
                log.Printf("Ответ от GPT API: %s\n", respBody)

                // Отправка ответа пользователю
                w.Write(respBody)

        } else {
                log.Printf("Устройство не найдено, доступ запрещен\n")
                http.Error(w, "Устройство не найдено, доступ запрещен", http.StatusInternalServerError)
                return
        }
}
```

В этом коде кроме изменений в логике обработки `exists`, мы также добавили:

1. **Сериализацию** вашего запроса в JSON с помощью `json.Marshal`.
2. **Отправку POST запроса** к `gptUrl`.
3. **Чтение и логирование** ответа от API.
4. **Отправка ответа** от GPT обратно в `http.ResponseWriter`.

Не забудьте импортировать дополнительные пакеты `io` для чтения ответа от API и убедитесь, что у вас правильно настроены зависимости для работы с HTTP и JSON в вашем проекте на Go.
User: а у нас не будет проблем с ответом text/event-stream; charset=utf-8?
Assistant: , ответ с типом `text/event-stream` может вызвать некоторые проблемы при обработке, поскольку это формат, предназначенный для потоковой передачи данных (например, SSE - Server-Sent Events) и отличается от стандартного JSON-ответа. 

Если ваш API может вернуть данные в формате `text/event-stream`, вам следует учесть это и обрабатывать данные соответствующим образом. В этом случае, вместо того чтобы пытаться прочитать тело ответа как обычный JSON, вам нужно будет обрабатывать его как поток.

Вот как вы можете модифицировать вашу функцию, чтобы удерживать эту логику в производительности:

1. Определите, какой тип контента возвращается, и обработайте его соответственно.
2. Если это `text/event-stream`, читайте данные из `resp.Body` напрямую и логируйте их по мере поступления.
3. Если это стандартный JSON (например, `application/json`), обрабатывайте его как обычно.

Вот обновлённый фрагмент кода:

```go
package main

import (
        "bytes"
        "encoding/json"
        "fmt"
        "io"
        "log"
        "net/http"
        "os"
)

func handleAiSearch(w http.ResponseWriter, r *http.Request) {
        // Остальная часть функции остается без изменений...

        if exists {
                // Код для построения командного запроса остается без изменений...

                // Отправка POST запроса
                resp, err := http.Post(gptUrl, "application/json", bytes.NewBuffer(payloadBytes))
                if err != nil {
                        log.Printf("Ошибка при отправке запроса к API: %v\n", err)
                        http.Error(w, "Ошибка при отправке запроса", http.StatusInternalServerError)
                        return
                }
                defer resp.Body.Close()

                // Обработка ответа
                contentType := resp.Header.Get("Content-Type")
                if contentType == "application/json" {
                        // Чтение ответа JSON
                        respBody, err := io.ReadAll(resp.Body)
                        if err != nil {
                                log.Printf("Ошибка при чтении ответа: %v\n", err)
                                http.Error(w, "Ошибка при чтении ответа от API", http.StatusInternalServerError)
                                return
                        }
                        log.Printf("Ответ от GPT API: %s\n", respBody)
                        w.Header().Set("Content-Type", "application/json")
                        w.Write(respBody)

                } else if contentType == "text/event-stream" {
                        // Чтение и запись в ответ в режиме потока
                        log.Println("Получен поток ответов от GPT API:")
                        _, err := io.Copy(w, resp.Body)
                        if err != nil {
                                log.Printf("Ошибка при обработке потока ответа: %v\n", err)
                        }
                } else {
                        log.Printf("Неизвестный тип контента: %s\n", contentType)
                        http.Error(w, "Неизвестный тип ответа от API", http.StatusInternalServerError)
                }

        } else {
                // Код для обработки случая, когда устройство не найдено, остается без изменений...
        }
}
```

Таким образом, если API вернёт `text/event-stream`, ваш сервер будет считывать поток данных и отправлять их клиенту.

Учтите, что обработка потокового ответа может потребовать дополнительных мер предосторожности, так как такие ответы могут содержать уведомления, события или другие данные в реальном времени. Если вы планируете использовать этот функционал часто, убедитесь, что запросы и сигналы от вашего API хорошо структурированы и легко обрабатываются клиентом.
User: нам собрать ответ с text/event-stream тоже нужно
он будет примерно таким
{"type": "provider", "provider": {"name": "NexraChatGPT", "url": "https://nexra.aryahcr.cc/documentation/chatgpt/en", "model": "gpt-4o", "label": "Nexra ChatGPT"}}
{"type": "content", "content": "\u0412\u043e\u0442"}
{"type": "content", "content": " \u0441\u043f\u0438\u0441\u043e\u043a"}
{"type": "content", "content": " \u0438\u0437"}
{"type": "content", "content": " "}
{"type": "content", "content": "40"}
{"type": "content", "content": " \u043f\u0440\u043e\u0438\u0437\u0432\u0435\u0434"}
{"type": "content", "content": "\u0435\u043d\u0438\u0439"}
{"type": "content", "content": ","}
{"type": "content", "content": " \u0441\u0432\u044f\u0437\u0430\u043d\u043d\u044b\u0445"}
{"type": "content", "content": " \u0441"}
{"type": "content", "content": " \u0442\u0435\u043c"}
{"type": "content", "content": "\u043e\u0439"}
{"type": "content", "content": " \u043c\u0435\u0447"}
{"type": "content", "content": "\u0435\u0439"}
{"type": "content", "content": " \u0438"}
{"type": "content", "content": " \u0444"}
{"type": "content", "content": "\u0435\u0445"}
{"type": "content", "content": "\u0442"}
{"type": "content", "content": "\u043e\u0432\u0430\u043d\u0438\u044f"}
{"type": "content", "content": ","}
{"type": "content", "content": " \u0432\u043a\u043b\u044e\u0447\u0430\u044f"}
{"type": "content", "content": " \u0430"}
{"type": "content", "content": "\u043d\u0438\u043c"}
{"type": "content", "content": "\u0435"}
{"type": "content", "content": ","}
{"type": "content", "content": " \u0444\u0438\u043b\u044c\u043c\u044b"}
{"type": "content", "content": " \u0438"}
{"type": "content", "content": " \u0441\u0435\u0440\u0438"}
{"type": "content", "content": "\u0430\u043b\u044b"}
{"type": "content", "content": ":"}
Assistant:  собрать ответ из потока `text/event-stream` и собрать его в конечный объект, вы можете использовать подход, основанный на считывании данных из `resp.Body` и на аккумулировании контента в строку. Вам нужно будет распарсить каждую строку данных в зависимости от типа и добавлять текст в соответствующую переменную.

Вот как вы можете изменить вашу функцию для обработки потокового ответа:

```go
package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "io"
    "log"
    "net/http"
    "os"
    "strings"
)

func handleAiSearch(w http.ResponseWriter, r *http.Request) {
    log.Printf("Received request: %s %s\n", r.Method, r.URL.Path)
    deviceData := r.URL.Query().Get("deviceid")
    queryAI := r.URL.Query().Get("query")
    if deviceData == "" && queryAI == "" {
        http.Error(w, "Ошибка.", http.StatusInternalServerError)
        return
    }
    log.Printf("Requested deviceID: %s\n", deviceData)
    log.Printf("Requested queryAI: %s\n", queryAI)

    var exists bool
    err := db.QueryRow("SELECT EXISTS(SELECT 1 FROM devices WHERE device_id = $1)", deviceData).Scan(&exists)
    if err != nil {
        log.Printf("Ошибка при проверке существования записей: %v\n", err)
        http.Error(w, "Ошибка при проверке существования записей", http.StatusInternalServerError)
        return
    }

    if exists {
        gptContent := fmt.Sprintf(
            "Порекомендуй список из 40 элементов по Теме из \"Запрос пользователя\" и обязательно разбей по категориям (10 штук на категорию!): фильмы, сериалы, мультфильмы и аниме\n"+
                "Твой ответ должен быть такой (в {} динамичные данные, без {} статичные) (названия должны быть реальными!!)(за другой буду ругать!) {number}; ru_name - {RU_NAME}; en_name - {EN_NAME}; orig_name - {ORIG_NAME}; date - {realise_date}; media_type - {movie,tv}; category - {category}\n"+
                "Запрос пользователя: %s", queryAI)

        gptUrl := os.Getenv("AI_QUERY")

        // Создание запроса
        payload := map[string]interface{}{
            "messages": []map[string]string{
                {
                    "role":    "user",
                    "content": gptContent,
                },
            },
            "model":    "gpt-4o",
            "provider": "NexraChatGPT",
            "web_search": false,
        }
        payloadBytes, err := json.Marshal(payload)
        if err != nil {
            log.Printf("Ошибка при сериализации запроса: %v\n", err)
            http.Error(w, "Ошибка при обработке запроса", http.StatusInternalServerError)
            return
        }

        // Отправка POST запроса
        resp, err := http.Post(gptUrl, "application/json", bytes.NewBuffer(payloadBytes))
        if err != nil {
            log.Printf("Ошибка при отправке запроса к API: %v\n", err)
            http.Error(w, "Ошибка при отправке запроса", http.StatusInternalServerError)
            return
        }
        defer resp.Body.Close()

        // Проверка типа контента
        contentType := resp.Header.Get("Content-Type")
        if contentType == "application/json" {
            // Чтение ответа JSON
            respBody, err := io.ReadAll(resp.Body)
            if err != nil {
                log.Printf("Ошибка при чтении ответа: %v\n", err)
                http.Error(w, "Ошибка при чтении ответа от API", http.StatusInternalServerError)
                return
            }
            log.Printf("Ответ от GPT API: %s\n", respBody)
            w.Header().Set("Content-Type", "application/json")
            w.Write(respBody)

        } else if contentType == "text/event-stream" {
            var result strings.Builder
            log.Println("Получен поток ответов от GPT API:")
            decoder := json.NewDecoder(resp.Body)

            // Чтение потока данных
            for {
                var line map[string]interface{}
                if err := decoder.Decode(&line); err == io.EOF {
                    break
                } else if err != nil {
                    log.Printf("Ошибка при чтении потока ответа: %v\n", err)
                    break
                }

                // Поскольку ответ может содержать разные типы, мы собираем их
                if lineType, ok := line["type"].(string); ok {
                    if lineType == "content" {
                        if content, ok := line["content"].(string); ok {
                            result.WriteString(content)
                        }
                    } else if lineType == "provider" {
                        // Логируем информацию о провайдере
                        log.Printf("Provider Info: %v\n", line)
                    }
                }
            }

            // Возврат собранного результата
            finalResult := result.String()
            log.Printf("Собранный ответ: %s\n", finalResult)
            w.Header().Set("Content-Type", "application/json")
            w.Write([]byte(finalResult))

        } else {
            log.Printf("Неизвестный тип контента: %s\n", contentType)
            http.Error(w, "Неизвестный тип ответа от API", http.StatusInternalServerError)
        }

    } else {
        log.Printf("Устройство не найдено, доступ запрещен\n")
        http.Error(w, "Устройство не найдено, доступ запрещен", http.StatusInternalServerError)
        return
    }
}
```

### Объяснение изменений

1. **Система считывания событий**: Используется `json.NewDecoder` для декодирования входных данных по строкам.
2. **Сбор информации**: Создается `strings.Builder` для сбора всех текстовых сообщений типа `content`.
3. **Различные типы**: Логирование информации о типе провайдера с помощью простого логирования.
4. **Формат ответа**: Ваша конечная строка собирается и возвращается как JSON. Вы можете адаптировать это, если требуется возвращать более сложные структуры.

Эта логика обеспечит сбор информации из событийного потока, позволяя вам обрабатывать данные в реальном времени.
User: теперь для json и stream нужно сделать поиск данных по маске {number}\.?; ru_name - {RU_NAME}; en_name - {EN_NAME}; orig_name - {ORIG_NAME}; date - {realise_date}; media_type - {movie,tv}; category - {category} и заполнение их в переменную, после чего по каждому полученному orig_name делать запрос на https://api.themoviedb.org/3/search/multi?api_key=os.Getenv("TMDB_KEY")&language=ru&query=orig_name
ответ по каждому запросу будет примерно такой
{
    "page": 1,
    "results": [
        {
            "backdrop_path": "/ts2j3Fwk3f8WAY4AMPYrmOXyRz6.jpg",
            "id": 367206,
            "title": "Мастер меча",
            "original_title": "三少爷的剑",
            "overview": "Всем известному и признанному мастеру владения мечом Янь Шисаню недостаточно просто признания, он хочет победить единственного мастера, который лучше него управляется с мечом — Се Сяофэна. Вот только найти того не удалось. Отец Сяофэна говорит, что его сын умер, и показывает могилу. Разочаровавшись и потеряв цель в жизни, Шисань погружается в пьянство и решает покончить с собой — выбирает себе гроб и место покрасивей на первом попавшемся кладбище, но от самоубийства его останавливает местный крестьянин.",
            "poster_path": "/sCsFOWDTETklLlVj34msCn0S6JP.jpg",
            "media_type": "movie",
            "adult": false,
            "original_language": "zh",
            "genre_ids": [
                28
            ],
            "popularity": 11.486,
            "release_date": "2016-12-02",
            "video": false,
            "vote_average": 6.5,
            "vote_count": 118
        },...

нам нужно из всего собрать агрегированный json с полями как тут
{
    "results": [
        {
            "adult": false,
            "backdrop_path": "/tmU7GeKVybMWFButWEGl2M4GeiP.jpg",
            "genre_ids": [
                18,
                80
            ],
            "id": 238,
            "original_language": "en",
            "original_title": "The Godfather",
            "overview": "Криминальная сага, повествующая о нью-йоркской сицилийской мафиозной семье Корлеоне. Фильм охватывает период 1945-1955 годов. Глава семьи, Дон Вито Корлеоне, выдаёт замуж свою дочь. В это время со Второй мировой войны возвращается его любимый сын Майкл. Майкл, герой войны, гордость семьи, не выражает желания заняться жестоким семейным бизнесом. Дон Корлеоне ведёт дела по старым правилам, но наступают иные времена, и появляются люди, желающие изменить сложившиеся порядки. На Дона Корлеоне совершается покушение.",
            "popularity": 190.973,
            "poster_path": "/gZUc6DbAirZGWJL1685jsOd90Sf.jpg",
            "release_date": "1972-03-14",
            "title": "Крёстный отец",
            "video": false,
            "vote_average": 8.69,
            "vote_count": 20759,
            "media_type": "movie"
        },
и вернуть уже этот конечный json пользователю
Assistant:  собрать агрегированный JSON с данными, запрашивая дополнительные данные по каждому `orig_name`, вам нужно будет реализовать несколько шагов:

1. **Обработка ответа от API и извлечение нужных данных.**
2. **Отправка запросов к TMDB API для каждого `orig_name`.**
3. **Сбор ответов от TMDB API в финальный агрегированный JSON.**

Предположим, что вы уже имеете все необходимые данные (типы и содержимое, которые получили от GPT API или из потока `text/event-stream`). Вы можете использовать структуру для хранения результатов и затем возвращать их на выходе.

Вот пример кода, который выполняет необходимые задачи:

```go
package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "io"
    "log"
    "net/http"
    "os"
    "strings"
    "sync"
)

type TMDBResult struct {
    Adult           bool     `json:"adult"`
    BackdropPath    string   `json:"backdrop_path"`
    GenreIDs        []int    `json:"genre_ids"`
    ID              int      `json:"id"`
    OriginalLanguage string   `json:"original_language"`
    OriginalTitle   string   `json:"original_title"`
    Overview        string   `json:"overview"`
    Popularity      float64  `json:"popularity"`
    PosterPath      string   `json:"poster_path"`
    ReleaseDate     string   `json:"release_date"`
    Title           string   `json:"title"`
    Video           bool     `json:"video"`
    VoteAverage     float64  `json:"vote_average"`
    VoteCount       int      `json:"vote_count"`
}

type AggregatedResponse struct {
    Results []TMDBResult `json:"results"`
}

func handleAiSearch(w http.ResponseWriter, r *http.Request) {
    // Остальная часть функции остается без изменений...

    if exists {
        // Код для обработки GPT API и получения оригинальных названий (из 1 и 2 пунктов)...
        
        var origNames []string // Заполните этот массив оригинальными названиями из ответа от GPT
        
        var wg sync.WaitGroup
        var mu sync.Mutex
        aggregatedResults := AggregatedResponse{}

        for _, origName := range origNames {
            wg.Add(1)
            go func(origName string) {
                defer wg.Done()
                fetchTMDBData(origName, &aggregatedResults, &mu)
            }(origName)
        }

        wg.Wait() // Ждем, пока все горутины завершат свою работу

        // Возвращаем агрегированный результат
        w.Header().Set("Content-Type", "application/json")
        response, err := json.Marshal(aggregatedResults)
        if err != nil {
            log.Printf("Ошибка при сериализации ответа: %v\n", err)
            http.Error(w, "Ошибка при подготовке ответа", http.StatusInternalServerError)
            return
        }
        w.Write(response)

    } else {
        log.Printf("Устройство не найдено, доступ запрещен\n")
        http.Error(w, "Устройство не найдено, доступ запрещен", http.StatusInternalServerError)
        return
    }
}

func fetchTMDBData(origName string, results *AggregatedResponse, mu *sync.Mutex) {
    tmdbKey := os.Getenv("TMDB_KEY")
    tmdbUrl := fmt.Sprintf("https://api.themoviedb.org/3/search/multi?api_key=%s&language=ru&query=%s", tmdbKey, origName)

    resp, err := http.Get(tmdbUrl)
    if err != nil {
        log.Printf("Ошибка при запросе к TMDB API: %v\n", err)
        return
    }
    defer resp.Body.Close()

    if resp.StatusCode != http.StatusOK {
        log.Printf("Ошибка от TMDB API, код статуса: %d\n", resp.StatusCode)
        return
    }

    var tmdbResponse struct {
        Results []TMDBResult `json:"results"`
    }

    if err := json.NewDecoder(resp.Body).Decode(&tmdbResponse); err != nil {
        log.Printf("Ошибка при декодировании ответа TMDB: %v\n", err)
        return
    }

    // Сохраняем результаты в общий массив с блокировкой
    mu.Lock()
    results.Results = append(results.Results, tmdbResponse.Results...)
    mu.Unlock()
}
```

### Объяснение кода

1. **Определение структур**: Мы определили структуру `TMDBResult` для каждого результата TMDB и `AggregatedResponse` для итогового ответа.

2. **Горутины для параллельного выполнения запросов**: Используются горутины и `sync.WaitGroup` для отправки параллельных запросов к TMDB API по каждому `orig_name`.

3. **Функция `fetchTMDBData`**: Эта функция делает запрос к TMDB API по оригинальному названию. Результаты добавляются в общий массив с использованием мьютекса для предотвращения гонки данных.

4. **Сбор и возврат результата**: После завершения работы всех горутин собранный результат сериализуется в JSON и возвращается пользователю.

Убедитесь, что ваш API-ключ TMDB хранится в переменной окружения `TMDB_KEY`, и протестируйте эту функциональность на работе с реальными данными. Вы также можете добавить дополнительные проверки и улучшения, если это необходимо.
User: напиши полный код того что мы делаем
Assistant:  полный код вашей функции, которая обрабатывает запросы, взаимодействует с GPT API и TMDB API, а также собирает результаты в один агрегированный JSON-объект. Код включает в себя все необходимые части, включая проверки на наличие устройства, обработку JSON и потоковых событий, а также параллельные запросы к TMDB API.

```go
package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "io"
    "log"
    "net/http"
    "os"
    "strings"
    "sync"
)

type TMDBResult struct {
    Adult            bool     `json:"adult"`
    BackdropPath     string   `json:"backdrop_path"`
    GenreIDs         []int    `json:"genre_ids"`
    ID               int      `json:"id"`
    OriginalLanguage string   `json:"original_language"`
    OriginalTitle    string   `json:"original_title"`
    Overview         string   `json:"overview"`
    Popularity       float64  `json:"popularity"`
    PosterPath       string   `json:"poster_path"`
    ReleaseDate      string   `json:"release_date"`
    Title            string   `json:"title"`
    Video            bool     `json:"video"`
    VoteAverage      float64  `json:"vote_average"`
    VoteCount        int      `json:"vote_count"`
}

type AggregatedResponse struct {
    Results []TMDBResult `json:"results"`
}

func main() {
    http.HandleFunc("/search", handleAiSearch)
    log.Fatal(http.ListenAndServe(":8080", nil))
}

func handleAiSearch(w http.ResponseWriter, r *http.Request) {
    log.Printf("Received request: %s %s\n", r.Method, r.URL.Path)
    deviceData := r.URL.Query().Get("deviceid")
    queryAI := r.URL.Query().Get("query")
    
    if deviceData == "" && queryAI == "" {
        http.Error(w, "Ошибка.", http.StatusBadRequest)
        return
    }
    
    log.Printf("Requested deviceID: %s\n", deviceData)
    log.Printf("Requested queryAI: %s\n", queryAI)

    var exists bool
    err := db.QueryRow("SELECT EXISTS(SELECT 1 FROM devices WHERE device_id = $1)", deviceData).Scan(&exists)
    if err != nil {
        log.Printf("Ошибка при проверке существования записей: %v\n", err)
        http.Error(w, "Ошибка при проверке существования записей", http.StatusInternalServerError)
        return
    }

    if exists {
        gptContent := fmt.Sprintf(
            "Порекомендуй список из 40 элементов по Теме из \"Запрос пользователя\" и обязательно разбей по категориям (10 штук на категорию!): фильмы, сериалы, мультфильмы и аниме\n"+
                "Твой ответ должен быть такой (в {} динамичные данные, без {} статичные) (названия должны быть реальными!!)(за другой буду ругать!) {number}; ru_name - {RU_NAME}; en_name - {EN_NAME}; orig_name - {ORIG_NAME}; date - {realise_date}; media_type - {movie,tv}; category - {category}\n"+
                "Запрос пользователя: %s", queryAI)

        gptUrl := os.Getenv("AI_QUERY")

        // Создание запроса
        payload := map[string]interface{}{
            "messages": []map[string]string{
                {
                    "role":    "user",
                    "content": gptContent,
                },
            },
            "model":    "gpt-4o",
            "provider": "NexraChatGPT",
            "web_search": false,
        }
        payloadBytes, err := json.Marshal(payload)
        if err != nil {
            log.Printf("Ошибка при сериализации запроса: %v\n", err)
            http.Error(w, "Ошибка при обработке запроса", http.StatusInternalServerError)
            return
        }

        // Отправка POST запроса
        resp, err := http.Post(gptUrl, "application/json", bytes.NewBuffer(payloadBytes))
        if err != nil {
            log.Printf("Ошибка при отправке запроса к API: %v\n", err)
            http.Error(w, "Ошибка при отправке запроса", http.StatusInternalServerError)
            return
        }
        defer resp.Body.Close()

        // Проверка типа контента
        contentType := resp.Header.Get("Content-Type")
        if contentType == "text/event-stream" {
            var origNames []string
            var result strings.Builder

            // Чтение потока данных
            decoder := json.NewDecoder(resp.Body)
            for {
                var line map[string]interface{}
                if err := decoder.Decode(&line); err == io.EOF {
                    break
                } else if err != nil {
                    log.Printf("Ошибка при чтении потока ответа: %v\n", err)
                    break
                }

                // Обработка событий
                if lineType, ok := line["type"].(string); ok {
                    if lineType == "content" {
                        if content, ok := line["content"].(string); ok {
                            result.WriteString(content)
                        }
                    } else if lineType == "provider" {
                        log.Printf("Provider Info: %v\n", line)
                    }
                }
            }

            // Извлечение оригинальных названий
            origNames = extractOrigNames(result.String())

            // Запрос к TMDB API для получения данных
            aggregatedResults := AggregatedResponse{}
            var wg sync.WaitGroup
            var mu sync.Mutex

            for _, origName := range origNames {
                wg.Add(1)
                go func(origName string) {
                    defer wg.Done()
                    fetchTMDBData(origName, &aggregatedResults, &mu)
                }(origName)
            }

            wg.Wait() // Ждем, пока все горутины завершат свою работу

            // Возвращаем агрегированный результат
            w.Header().Set("Content-Type", "application/json")
            response, err := json.Marshal(aggregatedResults)
            if err != nil {
                log.Printf("Ошибка при сериализации ответа: %v\n", err)
                http.Error(w, "Ошибка при подготовке ответа", http.StatusInternalServerError)
                return
            }
            w.Write(response)

        } else {
            log.Printf("Неизвестный тип контента: %s\n", contentType)
            http.Error(w, "Неизвестный тип ответа от API", http.StatusInternalServerError)
        }

    } else {
        log.Printf("Устройство не найдено, доступ запрещен\n")
        http.Error(w, "Устройство не найдено, доступ запрещен", http.StatusInternalServerError)
        return
    }
}

func extractOrigNames(response string) []string {
    var origNames []string
    // Здесь вы можете парсить ответ и извлекать orig_name
    // Предположим, формат ответа известен
    lines := strings.Split(response, ";")
    for _, line := range lines {
        if strings.Contains(line, "orig_name") {
            parts := strings.Split(line, " ")
            for _, part := range parts {
                if strings.HasPrefix(part, "orig_name -") {
                    origNames = append(origNames, strings.TrimSpace(strings.Split(part, "-")[1]))
                }
            }
        }
    }
    return origNames
}

func fetchTMDBData(origName string, results *AggregatedResponse, mu *sync.Mutex) {
    tmdbKey := os.Getenv("TMDB_KEY")
    tmdbUrl := fmt.Sprintf("https://api.themoviedb.org/3/search/multi?api_key=%s&language=ru&query=%s", tmdbKey, origName)

    resp, err := http.Get(tmdbUrl)
    if err != nil {
        log.Printf("Ошибка при запросе к TMDB API: %v\n", err)
        return
    }
    defer resp.Body.Close()

    if resp.StatusCode != http.StatusOK {
        log.Printf("Ошибка от TMDB API, код статуса: %d\n", resp.StatusCode)
        return
    }

    var tmdbResponse struct {
        Results []TMDBResult `json:"results"`
    }

    if err := json.NewDecoder(resp.Body).Decode(&tmdbResponse); err != nil {
        log.Printf("Ошибка при декодировании ответа TMDB: %v\n", err)
        return
    }

    // Сохраняем результаты в общий массив с блокировкой
    mu.Lock()
    results.Results = append(results.Results, tmdbResponse.Results...)
    mu.Unlock()
}
```

### Объяснение кода

1. **Структуры**: Определены структуры `TMDBResult` и `AggregatedResponse` для хранения результатов TMDB API.
  
2. **Основная функция**: `handleAiSearch` обрабатывает входящие HTTP-запросы, получает данные от GPT API, читает ответ и формирует список оригинальных названий фильмов/сериалов.

3. **Извлечение оригинальных названий**: Функция `extractOrigNames` парсит строку с ответом от GPT API, чтобы извлечь `orig_name`.

4. **Асинхронные запросы**: `fetchTMDBData` делает параллельные запросы к TMDB API и собирает результаты.

5. **Возврат результата**: Полный агрегированный JSON возвращается как ответ на HTTP-запрос.

Не забудьте добавить необходимые обработки ошибок, а также протестировать код с реальными данными и API. Убедитесь, что ваши ключи API настроены через переменные окружения.
User: а каждому пользователю запросившему вернется свой ответ или нет?
Assistant: , в предложенном коде каждый пользователь, отправляющий запрос, будет получать свой собственный ответ. Это достигается благодаря следующему:

1. **Индивидуальные запросы**: Каждый запрос к вашему серверу обрабатывается в отдельной горутине. Таким образом, даже если несколько пользователей одновременно отправляют запросы, каждый из них получает и обрабатывает свои данные независимо от других.

2. **Локальные переменные**: Все переменные внутри функции `handleAiSearch`, включая входные данные (такие как `deviceData` и `queryAI`), а также результат запроса к API и результаты, которые собираются и возвращаются, являются локальными для этой функции. Это гарантирует, что данные одного пользователя не накладываются на данные другого пользователя.

3. **Ответы на запросы**: Когда пользователи делают запрос, каждый из них получает свой собственный ответ, основанный на их индивидуальных входных данных. Вы можете проверить, что для каждого запроса формируется отдельный `AggregatedResponse`, который затем возвращается пользователю.

Таким образом, ваш сервер будет корректно обрабатывать и возвращать ответы на запросы от разных пользователей, не смешивая их данные.

### Пример сценария использования

1. Пользователь A отправляет запрос с `deviceid=123` и `query=что-то`. Сервер обрабатывает этот запрос и возвращает результаты, полученные от GPT и TMDB API.

2. Пользователь B также отправляет запрос с `deviceid=456` и `query=другой запрос`. Сервер обрабатывает этот запрос независимо и возвращает результаты, которые могут отличаться от ответа пользователя A.

### Вывод

Эта организация выполнения кода и логики обработки данных позволяет каждому пользователю получать уникальные и соответствующие их запросам результаты, поэтому ваш сервер будет вести себя, как ожидается, отвечая на запросы каждого пользователя индивидуально.
User: как работает
func extractOrigNames(response string) []string {
	var origNames []string
	// Здесь вы можете парсить ответ и извлекать orig_name
	// Предположим, формат ответа известен
	lines := strings.Split(response, ";")
	for _, line := range lines {
		if strings.Contains(line, "orig_name") {
			parts := strings.Split(line, " ")
			for _, part := range parts {
				if strings.HasPrefix(part, "orig_name -") {
					origNames = append(origNames, strings.TrimSpace(strings.Split(part, "-")[1]))
				}
			}
		}
	}
	return origNames
}
Assistant: ункция `extractOrigNames` предназначена для извлечения значений `orig_name` из текстового ответа, который вы получаете от GPT API или из потокового ответа. Давайте разберём, как она работает, шаг за шагом.

### Шаги работы функции

1. **Инициализация массива**:
   ```go
   var origNames []string
   ```
   Здесь создаётся пустой срез строк `origNames`, в который будут добавляться извлечённые оригинальные названия.

2. **Разделение ответа на строки**:
   ```go
   lines := strings.Split(response, ";")
   ```
   Ответ `response` (строка) разбивается на отдельные строки на основе разделителя `;`. Это создаёт массив строк `lines`, каждая из которых будет содержать потенциальные данные, включая информацию об `orig_name`.

3. **Итерация по строкам**:
   ```go
   for _, line := range lines {
   ```
   Здесь происходит перебор каждой из строк в массиве `lines`.

4. **Проверка наличия `orig_name` в строке**:
   ```go
   if strings.Contains(line, "orig_name") {
   ```
   На данном шаге проверяется, содержит ли текущая строка термин `orig_name`. Если нет, строка пропускается.

5. **Разделение строки на части**:
   ```go
   parts := strings.Split(line, " ")
   ```
   Если строка содержит `orig_name`, она разделяется на части с использованием пробела в качестве разделителя. Это создаёт массив `parts`, где каждая часть может быть отдельным элементом данных.

6. **Извлечение оригинального имени**:
   ```go
   for _, part := range parts {
       if strings.HasPrefix(part, "orig_name -") {
           origNames = append(origNames, strings.TrimSpace(strings.Split(part, "-")[1]))
       }
   }
   ```
   Здесь происходит второй перебор всех частей строк:
   - Проверяется, начинается ли часть с строки `orig_name -`.
   - Если это так, выполняется разделение по `"-"` и извлечение второй части (которая, как ожидается, будет значением `orig_name`).
   - Значение добавляется в `origNames` после удаления лишних пробелов с помощью `strings.TrimSpace`.

7. **Возврат результата**:
   ```go
   return origNames
   ```
   В конце функция возвращает срез строк `origNames`, который содержит все найденные оригинальные названия.

### Пример

Рассмотрим пример работы функции:

Предположим, что `response` выглядит следующим образом:

```
"1; ru_name - Название1; en_name - Title1; orig_name - OriginalTitle1; date - 2021; media_type - movie; category - action; 2; ru_name - Название2; en_name - Title2; orig_name - OriginalTitle2; date - 2022; media_type - tv; category - drama;"
```

- После выполнения `strings.Split(response, ";")`, массив `lines` будет содержать:
  ```
  [
      "1", 
      " ru_name - Название1", 
      " en_name - Title1", 
      " orig_name - OriginalTitle1", 
      " date - 2021", 
      " media_type - movie", 
      " category - action", 
      " 2", 
      " ru_name - Название2", 
      " en_name - Title2", 
      " orig_name - OriginalTitle2", 
      " date - 2022", 
      " media_type - tv", 
      " category - drama", 
      ""
  ]
  ```

- В результате, функция найдет в первой строке `orig_name - OriginalTitle1` и во второй строке `orig_name - OriginalTitle2`, добавив их в `origNames`. В конце работы функции вернётся массив:
  ```go
  ["OriginalTitle1", "OriginalTitle2"]
  ```

Таким образом, функция эффективно извлекает значения `orig_name`, которые затем могут быть использованы для дальнейших запросов к API TMDB.
User: нужно больше дебага, у нас где-то в коде есть проблема
User: нет, я про этот код
package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "io"
    "log"
    "net/http"
    "os"
    "strings"
    "sync"
)

type TMDBResult struct {
    Adult            bool     `json:"adult"`
    BackdropPath     string   `json:"backdrop_path"`
    GenreIDs         []int    `json:"genre_ids"`
    ID               int      `json:"id"`
    OriginalLanguage string   `json:"original_language"`
    OriginalTitle    string   `json:"original_title"`
    Overview         string   `json:"overview"`
    Popularity       float64  `json:"popularity"`
    PosterPath       string   `json:"poster_path"`
    ReleaseDate      string   `json:"release_date"`
    Title            string   `json:"title"`
    Video            bool     `json:"video"`
    VoteAverage      float64  `json:"vote_average"`
    VoteCount        int      `json:"vote_count"`
}

type AggregatedResponse struct {
    Results []TMDBResult `json:"results"`
}

func main() {
    http.HandleFunc("/search", handleAiSearch)
    log.Fatal(http.ListenAndServe(":8080", nil))
}

func handleAiSearch(w http.ResponseWriter, r *http.Request) {
    log.Printf("Received request: %s %s\n", r.Method, r.URL.Path)
    deviceData := r.URL.Query().Get("deviceid")
    queryAI := r.URL.Query().Get("query")
    
    if deviceData == "" && queryAI == "" {
        http.Error(w, "Ошибка.", http.StatusBadRequest)
        return
    }
    
    log.Printf("Requested deviceID: %s\n", deviceData)
    log.Printf("Requested queryAI: %s\n", queryAI)

    var exists bool
    err := db.QueryRow("SELECT EXISTS(SELECT 1 FROM devices WHERE device_id = $1)", deviceData).Scan(&exists)
    if err != nil {
        log.Printf("Ошибка при проверке существования записей: %v\n", err)
        http.Error(w, "Ошибка при проверке существования записей", http.StatusInternalServerError)
        return
    }

    if exists {
        gptContent := fmt.Sprintf(
            "Порекомендуй список из 40 элементов по Теме из \"Запрос пользователя\" и обязательно разбей по категориям (10 штук на категорию!): фильмы, сериалы, мультфильмы и аниме\n"+
                "Твой ответ должен быть такой (в {} динамичные данные, без {} статичные) (названия должны быть реальными!!)(за другой буду ругать!) {number}; ru_name - {RU_NAME}; en_name - {EN_NAME}; orig_name - {ORIG_NAME}; date - {realise_date}; media_type - {movie,tv}; category - {category}\n"+
                "Запрос пользователя: %s", queryAI)

        gptUrl := os.Getenv("AI_QUERY")

        // Создание запроса
        payload := map[string]interface{}{
            "messages": []map[string]string{
                {
                    "role":    "user",
                    "content": gptContent,
                },
            },
            "model":    "gpt-4o",
            "provider": "NexraChatGPT",
            "web_search": false,
        }
        payloadBytes, err := json.Marshal(payload)
        if err != nil {
            log.Printf("Ошибка при сериализации запроса: %v\n", err)
            http.Error(w, "Ошибка при обработке запроса", http.StatusInternalServerError)
            return
        }

        // Отправка POST запроса
        resp, err := http.Post(gptUrl, "application/json", bytes.NewBuffer(payloadBytes))
        if err != nil {
            log.Printf("Ошибка при отправке запроса к API: %v\n", err)
            http.Error(w, "Ошибка при отправке запроса", http.StatusInternalServerError)
            return
        }
        defer resp.Body.Close()

        // Проверка типа контента
        contentType := resp.Header.Get("Content-Type")
        if contentType == "text/event-stream" {
            var origNames []string
            var result strings.Builder

            // Чтение потока данных
            decoder := json.NewDecoder(resp.Body)
            for {
                var line map[string]interface{}
                if err := decoder.Decode(&line); err == io.EOF {
                    break
                } else if err != nil {
                    log.Printf("Ошибка при чтении потока ответа: %v\n", err)
                    break
                }

                // Обработка событий
                if lineType, ok := line["type"].(string); ok {
                    if lineType == "content" {
                        if content, ok := line["content"].(string); ok {
                            result.WriteString(content)
                        }
                    } else if lineType == "provider" {
                        log.Printf("Provider Info: %v\n", line)
                    }
                }
            }

            // Извлечение оригинальных названий
            origNames = extractOrigNames(result.String())

            // Запрос к TMDB API для получения данных
            aggregatedResults := AggregatedResponse{}
            var wg sync.WaitGroup
            var mu sync.Mutex

            for _, origName := range origNames {
                wg.Add(1)
                go func(origName string) {
                    defer wg.Done()
                    fetchTMDBData(origName, &aggregatedResults, &mu)
                }(origName)
            }

            wg.Wait() // Ждем, пока все горутины завершат свою работу

            // Возвращаем агрегированный результат
            w.Header().Set("Content-Type", "application/json")
            response, err := json.Marshal(aggregatedResults)
            if err != nil {
                log.Printf("Ошибка при сериализации ответа: %v\n", err)
                http.Error(w, "Ошибка при подготовке ответа", http.StatusInternalServerError)
                return
            }
            w.Write(response)

        } else {
            log.Printf("Неизвестный тип контента: %s\n", contentType)
            http.Error(w, "Неизвестный тип ответа от API", http.StatusInternalServerError)
        }

    } else {
        log.Printf("Устройство не найдено, доступ запрещен\n")
        http.Error(w, "Устройство не найдено, доступ запрещен", http.StatusInternalServerError)
        return
    }
}

func extractOrigNames(response string) []string {
    var origNames []string
    // Здесь вы можете парсить ответ и извлекать orig_name
    // Предположим, формат ответа известен
    lines := strings.Split(response, ";")
    for _, line := range lines {
        if strings.Contains(line, "orig_name") {
            parts := strings.Split(line, " ")
            for _, part := range parts {
                if strings.HasPrefix(part, "orig_name -") {
                    origNames = append(origNames, strings.TrimSpace(strings.Split(part, "-")[1]))
                }
            }
        }
    }
    return origNames
}

func fetchTMDBData(origName string, results *AggregatedResponse, mu *sync.Mutex) {
    tmdbKey := os.Getenv("TMDB_KEY")
    tmdbUrl := fmt.Sprintf("https://api.themoviedb.org/3/search/multi?api_key=%s&language=ru&query=%s", tmdbKey, origName)

    resp, err := http.Get(tmdbUrl)
    if err != nil {
        log.Printf("Ошибка при запросе к TMDB API: %v\n", err)
        return
    }
    defer resp.Body.Close()

    if resp.StatusCode != http.StatusOK {
        log.Printf("Ошибка от TMDB API, код статуса: %d\n", resp.StatusCode)
        return
    }

    var tmdbResponse struct {
        Results []TMDBResult `json:"results"`
    }

    if err := json.NewDecoder(resp.Body).Decode(&tmdbResponse); err != nil {
        log.Printf("Ошибка при декодировании ответа TMDB: %v\n", err)
        return
    }

    // Сохраняем результаты в общий массив с блокировкой
    mu.Lock()
    results.Results = append(results.Results, tmdbResponse.Results...)
    mu.Unlock()
}

в нем нужно больше дебага
Assistant:  улучшить дебаггинг в вашем Go-коде, можно добавить более подробные сообщения в логах, которые помогут вам отслеживать прогресс выполнения приложения и выявлять, на каком этапе возникают проблемы. Давайте рассмотрим конкретные места, где можно добавить отладочные сообщения, а также посмотрим на оптимизацию самого кода:

### Обновленный код с добавленным дебаггингом

```go
package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "io"
    "log"
    "net/http"
    "os"
    "strings"
    "sync"
)

type TMDBResult struct {
    Adult            bool     `json:"adult"`
    BackdropPath     string   `json:"backdrop_path"`
    GenreIDs         []int    `json:"genre_ids"`
    ID               int      `json:"id"`
    OriginalLanguage string   `json:"original_language"`
    OriginalTitle    string   `json:"original_title"`
    Overview         string   `json:"overview"`
    Popularity       float64  `json:"popularity"`
    PosterPath       string   `json:"poster_path"`
    ReleaseDate      string   `json:"release_date"`
    Title            string   `json:"title"`
    Video            bool     `json:"video"`
    VoteAverage      float64  `json:"vote_average"`
    VoteCount        int      `json:"vote_count"`
}

type AggregatedResponse struct {
    Results []TMDBResult `json:"results"`
}

func main() {
    log.Println("Starting server on :8080...")
    http.HandleFunc("/search", handleAiSearch)
    log.Fatal(http.ListenAndServe(":8080", nil))
}

func handleAiSearch(w http.ResponseWriter, r *http.Request) {
    log.Printf("Received request: %s %s\n", r.Method, r.URL.Path)
    deviceData := r.URL.Query().Get("deviceid")
    queryAI := r.URL.Query().Get("query")
    
    if deviceData == "" || queryAI == "" {
        http.Error(w, "Ошибка: отсутствуют параметры", http.StatusBadRequest)
        return
    }
    
    log.Printf("Requested deviceID: %s\n", deviceData)
    log.Printf("Requested queryAI: %s\n", queryAI)

    var exists bool
    err := db.QueryRow("SELECT EXISTS(SELECT 1 FROM devices WHERE device_id = $1)", deviceData).Scan(&exists)
    if err != nil {
        log.Printf("Ошибка при проверке существования записей: %v\n", err)
        http.Error(w, "Ошибка при проверке существования записей", http.StatusInternalServerError)
        return
    }

    if exists {
        log.Println("Device found, preparing request to GPT API.")
        gptContent := fmt.Sprintf(
            "Порекомендуй список из 40 элементов по Теме из \"Запрос пользователя\" и обязательно разбей по категориям (10 штук на категорию!): фильмы, сериалы, мультфильмы и аниме\n"+
                "Твой ответ должен быть такой (в {} динамичные данные, без {} статичные) (названия должны быть реальными!!)(за другой буду ругать!) {number}; ru_name - {RU_NAME}; en_name - {EN_NAME}; orig_name - {ORIG_NAME}; date - {realise_date}; media_type - {movie,tv}; category - {category}\n"+
                "Запрос пользователя: %s", queryAI)

        gptUrl := os.Getenv("AI_QUERY")

        // Создание запроса
        payload := map[string]interface{}{
            "messages": []map[string]string{
                {
                    "role":    "user",
                    "content": gptContent,
                },
            },
            "model":    "gpt-4o",
            "provider": "NexraChatGPT",
            "web_search": false,
        }
        payloadBytes, err := json.Marshal(payload)
        if err != nil {
            log.Printf("Ошибка при сериализации запроса: %v\n", err)
            http.Error(w, "Ошибка при обработке запроса", http.StatusInternalServerError)
            return
        }

        log.Println("Sending request to GPT API...")
        // Отправка POST запроса
        resp, err := http.Post(gptUrl, "application/json", bytes.NewBuffer(payloadBytes))
        if err != nil {
            log.Printf("Ошибка при отправке запроса к API: %v\n", err)
            http.Error(w, "Ошибка при отправке запроса", http.StatusInternalServerError)
            return
        }
        defer resp.Body.Close()

        log.Printf("Received response from GPT API with status: %s\n", resp.Status)
        // Проверка типа контента
        contentType := resp.Header.Get("Content-Type")
        if contentType == "text/event-stream" {
            var origNames []string
            var result strings.Builder

            // Чтение потока данных
            decoder := json.NewDecoder(resp.Body)
            for {
                var line map[string]interface{}
                if err := decoder.Decode(&line); err == io.EOF {
                    break
                } else if err != nil {
                    log.Printf("Ошибка при чтении потока ответа: %v\n", err)
                    break
                }

                // Обработка событий
                if lineType, ok := line["type"].(string); ok {
                    log.Printf("Processing line type: %s\n", lineType)
                    if lineType == "content" {
                        if content, ok := line["content"].(string); ok {
                            result.WriteString(content)
                        }
                    } else if lineType == "provider" {
                        log.Printf("Provider Info: %v\n", line)
                    }
                }
            }

            // Извлечение оригинальных названий
            log.Println("Extracting original names...")
            origNames = extractOrigNames(result.String())
            log.Printf("Extracted original names: %v\n", origNames)

            // Запрос к TMDB API для получения данных
            aggregatedResults := AggregatedResponse{}
            log.Printf("Starting requests to TMDB API for %d original names.\n", len(origNames))
            var wg sync.WaitGroup
            var mu sync.Mutex

            for _, origName := range origNames {
                wg.Add(1)
                go func(origName string) {
                    defer wg.Done()
                    fetchTMDBData(origName, &aggregatedResults, &mu)
                }(origName)
            }

            wg.Wait() // Ждем, пока все горутины завершат свою работу
            log.Println("Finished TMDB API requests.")

            // Возвращаем агрегированный результат
            w.Header().Set("Content-Type", "application/json")
            response, err := json.Marshal(aggregatedResults)
            if err != nil {
                log.Printf("Ошибка при сериализации ответа: %v\n", err)
                http.Error(w, "Ошибка при подготовке ответа", http.StatusInternalServerError)
                return
            }
            log.Println("Sending aggregated response to the client.")
            w.Write(response)

        } else {
            log.Printf("Неизвестный тип контента: %s\n", contentType)
            http.Error(w, "Неизвестный тип ответа от API", http.StatusInternalServerError)
        }

    } else {
        log.Printf("Устройство не найдено, доступ запрещен\n")
        http.Error(w, "Устройство не найдено, доступ запрещен", http.StatusInternalServerError)
        return
    }
}

func extractOrigNames(response string) []string {
    var origNames []string
    log.Println("Extracting original names from response.")
    // Здесь вы можете парсить ответ и извлекать orig_name
    // Предположим, формат ответа известен
    lines := strings.Split(response, ";")
    for _, line := range lines {
        if strings.Contains(line, "orig_name") {
            parts := strings.Split(line, " ")
            for _, part := range parts {
                if strings.HasPrefix(part, "orig_name -") {
                    origName := strings.TrimSpace(strings.Split(part, "-")[1])
                    log.Printf("Found orig_name: %s\n", origName)
                    origNames = append(origNames, origName)
                }
            }
        }
    }
    log.Printf("Extracted orig_names: %v\n", origNames)
    return origNames
}

func fetchTMDBData(origName string, results *AggregatedResponse, mu *sync.Mutex) {
    tmdbKey := os.Getenv("TMDB_KEY")
    tmdbUrl := fmt.Sprintf("https://api.themoviedb.org/3/search/multi?api_key=%s&language=ru&query=%s", tmdbKey, origName)

    log.Printf("Fetching data from TMDB for origName: %s\n", origName)
    resp, err := http.Get(tmdbUrl)
    if err != nil {
        log.Printf("Ошибка при запросе к TMDB API: %v\n", err)
        return
    }
    defer resp.Body.Close()

    if resp.StatusCode != http.StatusOK {
        log.Printf("Ошибка от TMDB API, код статуса: %d\n", resp.StatusCode)
        return
    }

    var tmdbResponse struct {
        Results []TMDBResult `json:"results"`
    }

    if err := json.NewDecoder(resp.Body).Decode(&tmdbResponse); err != nil {
        log.Printf("Ошибка при декодировании ответа TMDB: %v\n", err)
        return
    }

    log.Printf("Retrieved %d results from TMDB for origName: %s\n", len(tmdbResponse.Results), origName)
    // Сохраняем результаты в общий массив с блокировкой
    mu.Lock()
    results.Results = append(results.Results, tmdbResponse.Results...)
    mu.Unlock()
}
```

### Внесенные изменения и добавленный дебаггинг:

1. **Логирование при запуске сервера**: Добавлено сообщение при старте сервера, что позволяет увидеть, когда он начал работать.

2. **Логирование запросов**: Теперь сообщения логируются для каждого полученного запроса, включая параметры.

3. **Логирование результатов запросов**: Сообщения о том, были ли найдены устройства, отправлены ли запросы к GPT API и TMDB API, а также статус ответов от этих API.

4. **Логирование ошибок**: Добавлено больше логирования ошибок, чтобы точно знать, где происходят сбои, и отладить их.

5. **Логирование извлечения имен**: После извлечения `orig_name` добавляется дополнительное логирование для каждого найдённого оригинального названия.

6. **Дебаг при побочном запросе**: Логируется количество результатов, получаемых от TMDB для каждого оригинального названия.

### Как это поможет

- Эти изменения позволят вам легко отслеживать поток выполнения кода и исторически фиксировать любые проблемы, возникающие во время выполнения. 
- Вы сможете видеть, где происходит задержка или ошибки, что значительно упростит последующий дебаггинг и тестирование.

Теперь при запуске приложения вы будете видеть подробные логи, которые помогут вам быстро выявлять и устранять возможные проблемы.
User: и в логе 2024/12/25 17:11:23 Received request: GET /aisearch 2024/12/25 17:11:23 Requested deviceID: sm1fxv-MTA5LjE5NS4yNDMuMjMy-YnJvd3Nlcg== 2024/12/25 17:11:23 Requested queryAI: слизь 2024/12/25 17:11:23 Device found, preparing request to GPT API. 2024/12/25 17:11:25 Received response from GPT API with status: 200 OK 2024/12/25 17:11:25 Provider Info: map[provider:map[label:Nexra ChatGPT model:gpt-4o name:NexraChatGPT url:https://nexra.aryahcr.cc/documentation/chatgpt/en] type:provider] 2024/12/25 17:11:53 Extracting original names... 2024/12/25 17:11:53 Extracting original names from response. 2024/12/25 17:11:53 Extracted orig_names: [] 2024/12/25 17:11:53 Extracted original names: [] 2024/12/25 17:11:53 Starting requests to TMDB API for 0 original names. 2024/12/25 17:11:53 Finished TMDB API requests. 2024/12/25 17:11:53 Sending aggregated response to the client.
при том что ответ от GPT такой 
{"type": "provider", "provider": {"name": "NexraChatGPT", "url": "https://nexra.aryahcr.cc/documentation/chatgpt/en", "model": "gpt-4o", "label": "Nexra ChatGPT"}}
{"type": "content", "content": "Вот"}
{"type": "content", "content": " список"}
{"type": "content", "content": " из"}
{"type": "content", "content": " "}
{"type": "content", "content": "40"}
{"type": "content", "content": " элементов"}
{"type": "content", "content": " на"}
{"type": "content", "content": " тему"}
{"type": "content", "content": " \""}
{"type": "content", "content": "С"}
{"type": "content", "content": "лиз"}
{"type": "content", "content": "ь"}
{"type": "content", "content": "\","}
{"type": "content", "content": " разб"}
{"type": "content", "content": "ит"}
{"type": "content", "content": "ый"}
{"type": "content", "content": " по"}
{"type": "content", "content": " категория"}
{"type": "content", "content": "м"}
{"type": "content", "content": ":"}
{"type": "content", "content": "\n\n###"}
{"type": "content", "content": " Ф"}
{"type": "content", "content": "иль"}
{"type": "content", "content": "мы"}
{"type": "content", "content": "\n1"}
{"type": "content", "content": ";"}
{"type": "content", "content": " ru"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "С"}
{"type": "content", "content": "лиз"}
{"type": "content", "content": "ь"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " en"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "Sl"}
{"type": "content", "content": "ime"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " orig"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "Sl"}
{"type": "content", "content": "ime"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " date"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "201"}
{"type": "content", "content": "8"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " media"}
{"type": "content", "content": "_type"}
{"type": "content", "content": " -"}
{"type": "content", "content": " movie"}
{"type": "content", "content": ";"}
{"type": "content", "content": " category"}
{"type": "content", "content": " -"}
{"type": "content", "content": " film"}
{"type": "content", "content": "  "}
{"type": "content", "content": "\n2"}
{"type": "content", "content": ";"}
{"type": "content", "content": " ru"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "В"}
{"type": "content", "content": " поис"}
{"type": "content", "content": "ках"}
{"type": "content", "content": " Д"}
{"type": "content", "content": "ори"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " en"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "Finding"}
{"type": "content", "content": " D"}
{"type": "content", "content": "ory"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " orig"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "Finding"}
{"type": "content", "content": " D"}
{"type": "content", "content": "ory"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " date"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "201"}
{"type": "content", "content": "6"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " media"}
{"type": "content", "content": "_type"}
{"type": "content", "content": " -"}
{"type": "content", "content": " movie"}
{"type": "content", "content": ";"}
{"type": "content", "content": " category"}
{"type": "content", "content": " -"}
{"type": "content", "content": " film"}
{"type": "content", "content": "  "}
{"type": "content", "content": "\n3"}
{"type": "content", "content": ";"}
{"type": "content", "content": " ru"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "Л"}
{"type": "content", "content": "ю"}
{"type": "content", "content": "ди"}
{"type": "content", "content": " в"}
{"type": "content", "content": " чер"}
{"type": "content", "content": "ном"}
{"type": "content", "content": ":"}
{"type": "content", "content": " Интер"}
{"type": "content", "content": "нэ"}
{"type": "content", "content": "ш"}
{"type": "content", "content": "н"}
{"type": "content", "content": "л"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " en"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "Men"}
{"type": "content", "content": " in"}
{"type": "content", "content": " Black"}
{"type": "content", "content": ":"}
{"type": "content", "content": " International"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " orig"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "Men"}
{"type": "content", "content": " in"}
{"type": "content", "content": " Black"}
{"type": "content", "content": ":"}
{"type": "content", "content": " International"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " date"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "201"}
{"type": "content", "content": "9"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " media"}
{"type": "content", "content": "_type"}
{"type": "content", "content": " -"}
{"type": "content", "content": " movie"}
{"type": "content", "content": ";"}
{"type": "content", "content": " category"}
{"type": "content", "content": " -"}
{"type": "content", "content": " film"}
{"type": "content", "content": "  "}
{"type": "content", "content": "\n4"}
{"type": "content", "content": ";"}
{"type": "content", "content": " ru"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "При"}
{"type": "content", "content": "ш"}
{"type": "content", "content": "ель"}
{"type": "content", "content": "цы"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " en"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "Ali"}
{"type": "content", "content": "ens"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " orig"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "Ali"}
{"type": "content", "content": "ens"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " date"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "198"}
{"type": "content", "content": "6"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " media"}
{"type": "content", "content": "_type"}
{"type": "content", "content": " -"}
{"type": "content", "content": " movie"}
{"type": "content", "content": ";"}
{"type": "content", "content": " category"}
{"type": "content", "content": " -"}
{"type": "content", "content": " film"}
{"type": "content", "content": "  "}
{"type": "content", "content": "\n5"}
{"type": "content", "content": ";"}
{"type": "content", "content": " ru"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "Р"}
{"type": "content", "content": "он"}
{"type": "content", "content": " Х"}
{"type": "content", "content": "ит"}
{"type": "content", "content": "кий"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " en"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "Ron"}
{"type": "content", "content": "’s"}
{"type": "content", "content": " Gone"}
{"type": "content", "content": " Wrong"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " orig"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "Ron"}
{"type": "content", "content": "’s"}
{"type": "content", "content": " Gone"}
{"type": "content", "content": " Wrong"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " date"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "202"}
{"type": "content", "content": "1"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " media"}
{"type": "content", "content": "_type"}
{"type": "content", "content": " -"}
{"type": "content", "content": " movie"}
{"type": "content", "content": ";"}
{"type": "content", "content": " category"}
{"type": "content", "content": " -"}
{"type": "content", "content": " film"}
{"type": "content", "content": "  "}
{"type": "content", "content": "\n6"}
{"type": "content", "content": ";"}
{"type": "content", "content": " ru"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "С"}
{"type": "content", "content": "уп"}
{"type": "content", "content": "ер"}
{"type": "content", "content": "сем"}
{"type": "content", "content": "ей"}
{"type": "content", "content": "ка"}
{"type": "content", "content": " "}
{"type": "content", "content": "2"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " en"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "In"}
{"type": "content", "content": "cred"}
{"type": "content", "content": "ibles"}
{"type": "content", "content": " "}
{"type": "content", "content": "2"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " orig"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "In"}
{"type": "content", "content": "cred"}
{"type": "content", "content": "ibles"}
{"type": "content", "content": " "}
{"type": "content", "content": "2"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " date"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "201"}
{"type": "content", "content": "8"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " media"}
{"type": "content", "content": "_type"}
{"type": "content", "content": " -"}
{"type": "content", "content": " movie"}
{"type": "content", "content": ";"}
{"type": "content", "content": " category"}
{"type": "content", "content": " -"}
{"type": "content", "content": " film"}
{"type": "content", "content": "  "}
{"type": "content", "content": "\n7"}
{"type": "content", "content": ";"}
{"type": "content", "content": " ru"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "Г"}
{"type": "content", "content": "ад"}
{"type": "content", "content": "кий"}
{"type": "content", "content": " Я"}
{"type": "content", "content": " "}
{"type": "content", "content": "3"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " en"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "Des"}
{"type": "content", "content": "pic"}
{"type": "content", "content": "able"}
{"type": "content", "content": " Me"}
{"type": "content", "content": " "}
{"type": "content", "content": "3"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " orig"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "Des"}
{"type": "content", "content": "pic"}
{"type": "content", "content": "able"}
{"type": "content", "content": " Me"}
{"type": "content", "content": " "}
{"type": "content", "content": "3"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " date"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "201"}
{"type": "content", "content": "7"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " media"}
{"type": "content", "content": "_type"}
{"type": "content", "content": " -"}
{"type": "content", "content": " movie"}
{"type": "content", "content": ";"}
{"type": "content", "content": " category"}
{"type": "content", "content": " -"}
{"type": "content", "content": " film"}
{"type": "content", "content": "  "}
{"type": "content", "content": "\n8"}
{"type": "content", "content": ";"}
{"type": "content", "content": " ru"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "М"}
{"type": "content", "content": "ада"}
{"type": "content", "content": "г"}
{"type": "content", "content": "ас"}
{"type": "content", "content": "кар"}
{"type": "content", "content": " "}
{"type": "content", "content": "3"}
{"type": "content", "content": ":"}
{"type": "content", "content": " Р"}
{"type": "content", "content": "ын"}
{"type": "content", "content": "ок"}
{"type": "content", "content": " развлеч"}
{"type": "content", "content": "ений"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " en"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "Mad"}
{"type": "content", "content": "agas"}
{"type": "content", "content": "car"}
{"type": "content", "content": " "}
{"type": "content", "content": "3"}
{"type": "content", "content": ":"}
{"type": "content", "content": " Europe's"}
{"type": "content", "content": " Most"}
{"type": "content", "content": " Wanted"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " orig"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "Mad"}
{"type": "content", "content": "agas"}
{"type": "content", "content": "car"}
{"type": "content", "content": " "}
{"type": "content", "content": "3"}
{"type": "content", "content": ":"}
{"type": "content", "content": " Europe's"}
{"type": "content", "content": " Most"}
{"type": "content", "content": " Wanted"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " date"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "201"}
{"type": "content", "content": "2"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " media"}
{"type": "content", "content": "_type"}
{"type": "content", "content": " -"}
{"type": "content", "content": " movie"}
{"type": "content", "content": ";"}
{"type": "content", "content": " category"}
{"type": "content", "content": " -"}
{"type": "content", "content": " film"}
{"type": "content", "content": "  "}
{"type": "content", "content": "\n9"}
{"type": "content", "content": ";"}
{"type": "content", "content": " ru"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "Т"}
{"type": "content", "content": "рол"}
{"type": "content", "content": "ли"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " en"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "T"}
{"type": "content", "content": "roll"}
{"type": "content", "content": "s"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " orig"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "T"}
{"type": "content", "content": "roll"}
{"type": "content", "content": "s"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " date"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "201"}
{"type": "content", "content": "6"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " media"}
{"type": "content", "content": "_type"}
{"type": "content", "content": " -"}
{"type": "content", "content": " movie"}
{"type": "content", "content": ";"}
{"type": "content", "content": " category"}
{"type": "content", "content": " -"}
{"type": "content", "content": " film"}
{"type": "content", "content": "  "}
{"type": "content", "content": "\n10"}
{"type": "content", "content": ";"}
{"type": "content", "content": " ru"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "З"}
{"type": "content", "content": "вер"}
{"type": "content", "content": "опол"}
{"type": "content", "content": "ис"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " en"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "Z"}
{"type": "content", "content": "oot"}
{"type": "content", "content": "opia"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " orig"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "Z"}
{"type": "content", "content": "oot"}
{"type": "content", "content": "opia"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " date"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "201"}
{"type": "content", "content": "6"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " media"}
{"type": "content", "content": "_type"}
{"type": "content", "content": " -"}
{"type": "content", "content": " movie"}
{"type": "content", "content": ";"}
{"type": "content", "content": " category"}
{"type": "content", "content": " -"}
{"type": "content", "content": " film"}
{"type": "content", "content": "  "}
{"type": "content", "content": "\n\n###"}
{"type": "content", "content": " С"}
{"type": "content", "content": "ери"}
{"type": "content", "content": "алы"}
{"type": "content", "content": "\n11"}
{"type": "content", "content": ";"}
{"type": "content", "content": " ru"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "С"}
{"type": "content", "content": "лиз"}
{"type": "content", "content": "ня"}
{"type": "content", "content": "к"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " en"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "Sl"}
{"type": "content", "content": "ime"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " orig"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "Sl"}
{"type": "content", "content": "ime"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " date"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "202"}
{"type": "content", "content": "0"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " media"}
{"type": "content", "content": "_type"}
{"type": "content", "content": " -"}
{"type": "content", "content": " tv"}
{"type": "content", "content": ";"}
{"type": "content", "content": " category"}
{"type": "content", "content": " -"}
{"type": "content", "content": " series"}
{"type": "content", "content": "  "}
{"type": "content", "content": "\n12"}
{"type": "content", "content": ";"}
{"type": "content", "content": " ru"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "Л"}
{"type": "content", "content": "аб"}
{"type": "content", "content": "орат"}
{"type": "content", "content": "ория"}
{"type": "content", "content": " Д"}
{"type": "content", "content": "екс"}
{"type": "content", "content": "тера"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " en"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "Dex"}
{"type": "content", "content": "ter"}
{"type": "content", "content": "'s"}
{"type": "content", "content": " Laboratory"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " orig"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "Dex"}
{"type": "content", "content": "ter"}
{"type": "content", "content": "'s"}
{"type": "content", "content": " Laboratory"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " date"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "199"}
{"type": "content", "content": "6"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " media"}
{"type": "content", "content": "_type"}
{"type": "content", "content": " -"}
{"type": "content", "content": " tv"}
{"type": "content", "content": ";"}
{"type": "content", "content": " category"}
{"type": "content", "content": " -"}
{"type": "content", "content": " series"}
{"type": "content", "content": "  "}
{"type": "content", "content": "\n13"}
{"type": "content", "content": ";"}
{"type": "content", "content": " ru"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "С"}
{"type": "content", "content": "меш"}
{"type": "content", "content": "ар"}
{"type": "content", "content": "ики"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " en"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "S"}
{"type": "content", "content": "mes"}
{"type": "content", "content": "har"}
{"type": "content", "content": "iki"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " orig"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "С"}
{"type": "content", "content": "меш"}
{"type": "content", "content": "ар"}
{"type": "content", "content": "ики"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " date"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "200"}
{"type": "content", "content": "4"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " media"}
{"type": "content", "content": "_type"}
{"type": "content", "content": " -"}
{"type": "content", "content": " tv"}
{"type": "content", "content": ";"}
{"type": "content", "content": " category"}
{"type": "content", "content": " -"}
{"type": "content", "content": " series"}
{"type": "content", "content": "  "}
{"type": "content", "content": "\n14"}
{"type": "content", "content": ";"}
{"type": "content", "content": " ru"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "П"}
{"type": "content", "content": "ок"}
{"type": "content", "content": "ем"}
{"type": "content", "content": "оны"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " en"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "Pok"}
{"type": "content", "content": "émon"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " orig"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "Pok"}
{"type": "content", "content": "émon"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " date"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "199"}
{"type": "content", "content": "7"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " media"}
{"type": "content", "content": "_type"}
{"type": "content", "content": " -"}
{"type": "content", "content": " tv"}
{"type": "content", "content": ";"}
{"type": "content", "content": " category"}
{"type": "content", "content": " -"}
{"type": "content", "content": " series"}
{"type": "content", "content": "  "}
{"type": "content", "content": "\n15"}
{"type": "content", "content": ";"}
{"type": "content", "content": " ru"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "Г"}
{"type": "content", "content": "рав"}
{"type": "content", "content": "ити"}
{"type": "content", "content": " Ф"}
{"type": "content", "content": "ол"}
{"type": "content", "content": "з"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " en"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "Gravity"}
{"type": "content", "content": " Falls"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " orig"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "Gravity"}
{"type": "content", "content": " Falls"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " date"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "201"}
{"type": "content", "content": "2"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " media"}
{"type": "content", "content": "_type"}
{"type": "content", "content": " -"}
{"type": "content", "content": " tv"}
{"type": "content", "content": ";"}
{"type": "content", "content": " category"}
{"type": "content", "content": " -"}
{"type": "content", "content": " series"}
{"type": "content", "content": "  "}
{"type": "content", "content": "\n16"}
{"type": "content", "content": ";"}
{"type": "content", "content": " ru"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "Ч"}
{"type": "content", "content": "уд"}
{"type": "content", "content": "еса"}
{"type": "content", "content": " на"}
{"type": "content", "content": " ви"}
{"type": "content", "content": "раж"}
{"type": "content", "content": "ах"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " en"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "Duck"}
{"type": "content", "content": "T"}
{"type": "content", "content": "ales"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " orig"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "Duck"}
{"type": "content", "content": "T"}
{"type": "content", "content": "ales"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " date"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "198"}
{"type": "content", "content": "7"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " media"}
{"type": "content", "content": "_type"}
{"type": "content", "content": " -"}
{"type": "content", "content": " tv"}
{"type": "content", "content": ";"}
{"type": "content", "content": " category"}
{"type": "content", "content": " -"}
{"type": "content", "content": " series"}
{"type": "content", "content": "  "}
{"type": "content", "content": "\n17"}
{"type": "content", "content": ";"}
{"type": "content", "content": " ru"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "Ф"}
{"type": "content", "content": "л"}
{"type": "content", "content": "эш"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " en"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "The"}
{"type": "content", "content": " Flash"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " orig"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "The"}
{"type": "content", "content": " Flash"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " date"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "201"}
{"type": "content", "content": "4"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " media"}
{"type": "content", "content": "_type"}
{"type": "content", "content": " -"}
{"type": "content", "content": " tv"}
{"type": "content", "content": ";"}
{"type": "content", "content": " category"}
{"type": "content", "content": " -"}
{"type": "content", "content": " series"}
{"type": "content", "content": "  "}
{"type": "content", "content": "\n18"}
{"type": "content", "content": ";"}
{"type": "content", "content": " ru"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "М"}
{"type": "content", "content": "ир"}
{"type": "content", "content": " ю"}
{"type": "content", "content": "р"}
{"type": "content", "content": "ского"}
{"type": "content", "content": " периода"}
{"type": "content", "content": ":"}
{"type": "content", "content": " Л"}
{"type": "content", "content": "аг"}
{"type": "content", "content": "ерь"}
{"type": "content", "content": " Х"}
{"type": "content", "content": "ра"}
{"type": "content", "content": "бр"}
{"type": "content", "content": "ости"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " en"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "Jur"}
{"type": "content", "content": "assic"}
{"type": "content", "content": " World"}
{"type": "content", "content": ":"}
{"type": "content", "content": " Camp"}
{"type": "content", "content": " C"}
{"type": "content", "content": "ret"}
{"type": "content", "content": "aceous"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " orig"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "Jur"}
{"type": "content", "content": "assic"}
{"type": "content", "content": " World"}
{"type": "content", "content": ":"}
{"type": "content", "content": " Camp"}
{"type": "content", "content": " C"}
{"type": "content", "content": "ret"}
{"type": "content", "content": "aceous"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " date"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "202"}
{"type": "content", "content": "0"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " media"}
{"type": "content", "content": "_type"}
{"type": "content", "content": " -"}
{"type": "content", "content": " tv"}
{"type": "content", "content": ";"}
{"type": "content", "content": " category"}
{"type": "content", "content": " -"}
{"type": "content", "content": " series"}
{"type": "content", "content": "  "}
{"type": "content", "content": "\n19"}
{"type": "content", "content": ";"}
{"type": "content", "content": " ru"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "Нав"}
{"type": "content", "content": "иг"}
{"type": "content", "content": "атор"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " en"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "Navigator"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " orig"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "Navigator"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " date"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "202"}
{"type": "content", "content": "2"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " media"}
{"type": "content", "content": "_type"}
{"type": "content", "content": " -"}
{"type": "content", "content": " tv"}
{"type": "content", "content": ";"}
{"type": "content", "content": " category"}
{"type": "content", "content": " -"}
{"type": "content", "content": " series"}
{"type": "content", "content": "  "}
{"type": "content", "content": "\n20"}
{"type": "content", "content": ";"}
{"type": "content", "content": " ru"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "Как"}
{"type": "content", "content": " избежать"}
{"type": "content", "content": " наказ"}
{"type": "content", "content": "ания"}
{"type": "content", "content": " за"}
{"type": "content", "content": " убий"}
{"type": "content", "content": "ство"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " en"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "How"}
{"type": "content", "content": " to"}
{"type": "content", "content": " Get"}
{"type": "content", "content": " Away"}
{"type": "content", "content": " with"}
{"type": "content", "content": " Murder"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " orig"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "How"}
{"type": "content", "content": " to"}
{"type": "content", "content": " Get"}
{"type": "content", "content": " Away"}
{"type": "content", "content": " with"}
{"type": "content", "content": " Murder"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " date"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "201"}
{"type": "content", "content": "4"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " media"}
{"type": "content", "content": "_type"}
{"type": "content", "content": " -"}
{"type": "content", "content": " tv"}
{"type": "content", "content": ";"}
{"type": "content", "content": " category"}
{"type": "content", "content": " -"}
{"type": "content", "content": " series"}
{"type": "content", "content": "  "}
{"type": "content", "content": "\n\n###"}
{"type": "content", "content": " М"}
{"type": "content", "content": "ульт"}
{"type": "content", "content": "ф"}
{"type": "content", "content": "иль"}
{"type": "content", "content": "мы"}
{"type": "content", "content": "\n21"}
{"type": "content", "content": ";"}
{"type": "content", "content": " ru"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "С"}
{"type": "content", "content": "л"}
{"type": "content", "content": "они"}
{"type": "content", "content": "к"}
{"type": "content", "content": "-п"}
{"type": "content", "content": "ут"}
{"type": "content", "content": "еше"}
{"type": "content", "content": "ствен"}
{"type": "content", "content": "ник"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " en"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "P"}
{"type": "content", "content": "uff"}
{"type": "content", "content": "in"}
{"type": "content", "content": " Rock"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " orig"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "P"}
{"type": "content", "content": "uff"}
{"type": "content", "content": "in"}
{"type": "content", "content": " Rock"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " date"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "201"}
{"type": "content", "content": "5"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " media"}
{"type": "content", "content": "_type"}
{"type": "content", "content": " -"}
{"type": "content", "content": " movie"}
{"type": "content", "content": ";"}
{"type": "content", "content": " category"}
{"type": "content", "content": " -"}
{"type": "content", "content": " animated"}
{"type": "content", "content": "  "}
{"type": "content", "content": "\n22"}
{"type": "content", "content": ";"}
{"type": "content", "content": " ru"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "Б"}
{"type": "content", "content": "ы"}
{"type": "content", "content": "ст"}
{"type": "content", "content": "рые"}
{"type": "content", "content": " и"}
{"type": "content", "content": " яр"}
{"type": "content", "content": "ост"}
{"type": "content", "content": "ные"}
{"type": "content", "content": ":"}
{"type": "content", "content": " Х"}
{"type": "content", "content": "об"}
{"type": "content", "content": "б"}
{"type": "content", "content": "с"}
{"type": "content", "content": " и"}
{"type": "content", "content": " Ш"}
{"type": "content", "content": "оу"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " en"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "H"}
{"type": "content", "content": "ob"}
{"type": "content", "content": "bs"}
{"type": "content", "content": " &"}
{"type": "content", "content": " Shaw"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " orig"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "H"}
{"type": "content", "content": "ob"}
{"type": "content", "content": "bs"}
{"type": "content", "content": " &"}
{"type": "content", "content": " Shaw"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " date"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "201"}
{"type": "content", "content": "9"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " media"}
{"type": "content", "content": "_type"}
{"type": "content", "content": " -"}
{"type": "content", "content": " movie"}
{"type": "content", "content": ";"}
{"type": "content", "content": " category"}
{"type": "content", "content": " -"}
{"type": "content", "content": " animated"}
{"type": "content", "content": "  "}
{"type": "content", "content": "\n23"}
{"type": "content", "content": ";"}
{"type": "content", "content": " ru"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "С"}
{"type": "content", "content": "каз"}
{"type": "content", "content": "ания"}
{"type": "content", "content": " об"}
{"type": "content", "content": " Э"}
{"type": "content", "content": "ль"}
{"type": "content", "content": "з"}
{"type": "content", "content": "ас"}
{"type": "content", "content": "е"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " en"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "The"}
{"type": "content", "content": " School"}
{"type": "content", "content": " of"}
{"type": "content", "content": " Good"}
{"type": "content", "content": " and"}
{"type": "content", "content": " Evil"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " orig"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "The"}
{"type": "content", "content": " School"}
{"type": "content", "content": " of"}
{"type": "content", "content": " Good"}
{"type": "content", "content": " and"}
{"type": "content", "content": " Evil"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " date"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "202"}
{"type": "content", "content": "2"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " media"}
{"type": "content", "content": "_type"}
{"type": "content", "content": " -"}
{"type": "content", "content": " movie"}
{"type": "content", "content": ";"}
{"type": "content", "content": " category"}
{"type": "content", "content": " -"}
{"type": "content", "content": " animated"}
{"type": "content", "content": "  "}
{"type": "content", "content": "\n24"}
{"type": "content", "content": ";"}
{"type": "content", "content": " ru"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "У"}
{"type": "content", "content": "ни"}
{"type": "content", "content": "верс"}
{"type": "content", "content": "итет"}
{"type": "content", "content": " Мон"}
{"type": "content", "content": "стров"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " en"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "Mon"}
{"type": "content", "content": "sters"}
{"type": "content", "content": " University"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " orig"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "Mon"}
{"type": "content", "content": "sters"}
{"type": "content", "content": " University"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " date"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "201"}
{"type": "content", "content": "3"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " media"}
{"type": "content", "content": "_type"}
{"type": "content", "content": " -"}
{"type": "content", "content": " movie"}
{"type": "content", "content": ";"}
{"type": "content", "content": " category"}
{"type": "content", "content": " -"}
{"type": "content", "content": " animated"}
{"type": "content", "content": "  "}
{"type": "content", "content": "\n25"}
{"type": "content", "content": ";"}
{"type": "content", "content": " ru"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "Т"}
{"type": "content", "content": "ай"}
{"type": "content", "content": "на"}
{"type": "content", "content": " К"}
{"type": "content", "content": "око"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " en"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "C"}
{"type": "content", "content": "oco"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " orig"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "C"}
{"type": "content", "content": "oco"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " date"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "201"}
{"type": "content", "content": "7"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " media"}
{"type": "content", "content": "_type"}
{"type": "content", "content": " -"}
{"type": "content", "content": " movie"}
{"type": "content", "content": ";"}
{"type": "content", "content": " category"}
{"type": "content", "content": " -"}
{"type": "content", "content": " animated"}
{"type": "content", "content": "  "}
{"type": "content", "content": "\n26"}
{"type": "content", "content": ";"}
{"type": "content", "content": " ru"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "Ф"}
{"type": "content", "content": "ик"}
{"type": "content", "content": "с"}
{"type": "content", "content": "ики"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " en"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "Fix"}
{"type": "content", "content": "ies"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " orig"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "Ф"}
{"type": "content", "content": "ик"}
{"type": "content", "content": "с"}
{"type": "content", "content": "ики"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " date"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "201"}
{"type": "content", "content": "0"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " media"}
{"type": "content", "content": "_type"}
{"type": "content", "content": " -"}
{"type": "content", "content": " movie"}
{"type": "content", "content": ";"}
{"type": "content", "content": " category"}
{"type": "content", "content": " -"}
{"type": "content", "content": " animated"}
{"type": "content", "content": "  "}
{"type": "content", "content": "\n27"}
{"type": "content", "content": ";"}
{"type": "content", "content": " ru"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "Л"}
{"type": "content", "content": "еген"}
{"type": "content", "content": "да"}
{"type": "content", "content": " о"}
{"type": "content", "content": " Т"}
{"type": "content", "content": "аре"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " en"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "T"}
{"type": "content", "content": "ales"}
{"type": "content", "content": " of"}
{"type": "content", "content": " Arc"}
{"type": "content", "content": "adia"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " orig"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "T"}
{"type": "content", "content": "ales"}
{"type": "content", "content": " of"}
{"type": "content", "content": " Arc"}
{"type": "content", "content": "adia"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " date"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "201"}
{"type": "content", "content": "8"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " media"}
{"type": "content", "content": "_type"}
{"type": "content", "content": " -"}
{"type": "content", "content": " movie"}
{"type": "content", "content": ";"}
{"type": "content", "content": " category"}
{"type": "content", "content": " -"}
{"type": "content", "content": " animated"}
{"type": "content", "content": "  "}
{"type": "content", "content": "\n28"}
{"type": "content", "content": ";"}
{"type": "content", "content": " ru"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "П"}
{"type": "content", "content": "анда"}
{"type": "content", "content": " кун"}
{"type": "content", "content": "г"}
{"type": "content", "content": "-ф"}
{"type": "content", "content": "у"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " en"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "Kung"}
{"type": "content", "content": " Fu"}
{"type": "content", "content": " Panda"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " orig"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "Kung"}
{"type": "content", "content": " Fu"}
{"type": "content", "content": " Panda"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " date"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "200"}
{"type": "content", "content": "8"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " media"}
{"type": "content", "content": "_type"}
{"type": "content", "content": " -"}
{"type": "content", "content": " movie"}
{"type": "content", "content": ";"}
{"type": "content", "content": " category"}
{"type": "content", "content": " -"}
{"type": "content", "content": " animated"}
{"type": "content", "content": "  "}
{"type": "content", "content": "\n29"}
{"type": "content", "content": ";"}
{"type": "content", "content": " ru"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "С"}
{"type": "content", "content": "уп"}
{"type": "content", "content": "ер"}
{"type": "content", "content": "сем"}
{"type": "content", "content": "ей"}
{"type": "content", "content": "ка"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " en"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "The"}
{"type": "content", "content": " In"}
{"type": "content", "content": "cred"}
{"type": "content", "content": "ibles"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " orig"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "The"}
{"type": "content", "content": " In"}
{"type": "content", "content": "cred"}
{"type": "content", "content": "ibles"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " date"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "200"}
{"type": "content", "content": "4"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " media"}
{"type": "content", "content": "_type"}
{"type": "content", "content": " -"}
{"type": "content", "content": " movie"}
{"type": "content", "content": ";"}
{"type": "content", "content": " category"}
{"type": "content", "content": " -"}
{"type": "content", "content": " animated"}
{"type": "content", "content": "  "}
{"type": "content", "content": "\n30"}
{"type": "content", "content": ";"}
{"type": "content", "content": " ru"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "С"}
{"type": "content", "content": "м"}
{"type": "content", "content": "ур"}
{"type": "content", "content": "ф"}
{"type": "content", "content": "ики"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " en"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "The"}
{"type": "content", "content": " Sm"}
{"type": "content", "content": "ur"}
{"type": "content", "content": "fs"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " orig"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "The"}
{"type": "content", "content": " Sm"}
{"type": "content", "content": "ur"}
{"type": "content", "content": "fs"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " date"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "201"}
{"type": "content", "content": "1"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " media"}
{"type": "content", "content": "_type"}
{"type": "content", "content": " -"}
{"type": "content", "content": " movie"}
{"type": "content", "content": ";"}
{"type": "content", "content": " category"}
{"type": "content", "content": " -"}
{"type": "content", "content": " animated"}
{"type": "content", "content": "  "}
{"type": "content", "content": "\n\n###"}
{"type": "content", "content": " А"}
{"type": "content", "content": "ним"}
{"type": "content", "content": "е"}
{"type": "content", "content": "\n31"}
{"type": "content", "content": ";"}
{"type": "content", "content": " ru"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "С"}
{"type": "content", "content": "лиз"}
{"type": "content", "content": "ь"}
{"type": "content", "content": " в"}
{"type": "content", "content": " другом"}
{"type": "content", "content": " мире"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " en"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "That"}
{"type": "content", "content": " Time"}
{"type": "content", "content": " I"}
{"type": "content", "content": " Got"}
{"type": "content", "content": " Re"}
{"type": "content", "content": "inc"}
{"type": "content", "content": "arn"}
{"type": "content", "content": "ated"}
{"type": "content", "content": " as"}
{"type": "content", "content": " a"}
{"type": "content", "content": " Sl"}
{"type": "content", "content": "ime"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " orig"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "T"}
{"type": "content", "content": "ense"}
{"type": "content", "content": "i"}
{"type": "content", "content": " shit"}
{"type": "content", "content": "ara"}
{"type": "content", "content": " Sl"}
{"type": "content", "content": "ime"}
{"type": "content", "content": " Dat"}
{"type": "content", "content": "ta"}
{"type": "content", "content": " Ken"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " date"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "201"}
{"type": "content", "content": "8"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " media"}
{"type": "content", "content": "_type"}
{"type": "content", "content": " -"}
{"type": "content", "content": " tv"}
{"type": "content", "content": ";"}
{"type": "content", "content": " category"}
{"type": "content", "content": " -"}
{"type": "content", "content": " anime"}
{"type": "content", "content": "  "}
{"type": "content", "content": "\n32"}
{"type": "content", "content": ";"}
{"type": "content", "content": " ru"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "Н"}
{"type": "content", "content": "евер"}
{"type": "content", "content": "оят"}
{"type": "content", "content": "ные"}
{"type": "content", "content": " приключ"}
{"type": "content", "content": "ения"}
{"type": "content", "content": " М"}
{"type": "content", "content": "ом"}
{"type": "content", "content": "от"}
{"type": "content", "content": "аро"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " en"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "Mom"}
{"type": "content", "content": "ot"}
{"type": "content", "content": "aro"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " orig"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "Mom"}
{"type": "content", "content": "otar"}
{"type": "content", "content": "ō"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " date"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "194"}
{"type": "content", "content": "5"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " media"}
{"type": "content", "content": "_type"}
{"type": "content", "content": " -"}
{"type": "content", "content": " tv"}
{"type": "content", "content": ";"}
{"type": "content", "content": " category"}
{"type": "content", "content": " -"}
{"type": "content", "content": " anime"}
{"type": "content", "content": "  "}
{"type": "content", "content": "\n33"}
{"type": "content", "content": ";"}
{"type": "content", "content": " ru"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "Б"}
{"type": "content", "content": "лич"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " en"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "Ble"}
{"type": "content", "content": "ach"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " orig"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "Ble"}
{"type": "content", "content": "ach"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " date"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "200"}
{"type": "content", "content": "4"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " media"}
{"type": "content", "content": "_type"}
{"type": "content", "content": " -"}
{"type": "content", "content": " tv"}
{"type": "content", "content": ";"}
{"type": "content", "content": " category"}
{"type": "content", "content": " -"}
{"type": "content", "content": " anime"}
{"type": "content", "content": "  "}
{"type": "content", "content": "\n34"}
{"type": "content", "content": ";"}
{"type": "content", "content": " ru"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "Д"}
{"type": "content", "content": "ра"}
{"type": "content", "content": "ко"}
{"type": "content", "content": "ний"}
{"type": "content", "content": " жем"}
{"type": "content", "content": "ч"}
{"type": "content", "content": "уг"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " en"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "Dragon"}
{"type": "content", "content": " Ball"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " orig"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "ドラ"}
{"type": "content", "content": "ゴ"}
{"type": "content", "content": "ン"}
{"type": "content", "content": "ボ"}
{"type": "content", "content": "ール"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " date"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "198"}
{"type": "content", "content": "6"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " media"}
{"type": "content", "content": "_type"}
{"type": "content", "content": " -"}
{"type": "content", "content": " tv"}
{"type": "content", "content": ";"}
{"type": "content", "content": " category"}
{"type": "content", "content": " -"}
{"type": "content", "content": " anime"}
{"type": "content", "content": "  "}
{"type": "content", "content": "\n35"}
{"type": "content", "content": ";"}
{"type": "content", "content": " ru"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "Н"}
{"type": "content", "content": "ару"}
{"type": "content", "content": "то"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " en"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "Nar"}
{"type": "content", "content": "uto"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " orig"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "ナ"}
{"type": "content", "content": "ルト"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " date"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "200"}
{"type": "content", "content": "2"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " media"}
{"type": "content", "content": "_type"}
{"type": "content", "content": " -"}
{"type": "content", "content": " tv"}
{"type": "content", "content": ";"}
{"type": "content", "content": " category"}
{"type": "content", "content": " -"}
{"type": "content", "content": " anime"}
{"type": "content", "content": "  "}
{"type": "content", "content": "\n36"}
{"type": "content", "content": ";"}
{"type": "content", "content": " ru"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "С"}
{"type": "content", "content": "каз"}
{"type": "content", "content": "ания"}
{"type": "content", "content": " о"}
{"type": "content", "content": " приз"}
{"type": "content", "content": "ра"}
{"type": "content", "content": "ках"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " en"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "N"}
{"type": "content", "content": "ats"}
{"type": "content", "content": "ume"}
{"type": "content", "content": "'s"}
{"type": "content", "content": " Book"}
{"type": "content", "content": " of"}
{"type": "content", "content": " Friends"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " orig"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "夏"}
{"type": "content", "content": "目"}
{"type": "content", "content": "友"}
{"type": "content", "content": "人"}
{"type": "content", "content": "帳"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " date"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "200"}
{"type": "content", "content": "8"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " media"}
{"type": "content", "content": "_type"}
{"type": "content", "content": " -"}
{"type": "content", "content": " tv"}
{"type": "content", "content": ";"}
{"type": "content", "content": " category"}
{"type": "content", "content": " -"}
{"type": "content", "content": " anime"}
{"type": "content", "content": "  "}
{"type": "content", "content": "\n37"}
{"type": "content", "content": ";"}
{"type": "content", "content": " ru"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "С"}
{"type": "content", "content": "ай"}
{"type": "content", "content": "лен"}
{"type": "content", "content": "д"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " en"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "Els"}
{"type": "content", "content": "word"}
{"type": "content", "content": ":"}
{"type": "content", "content": " El"}
{"type": "content", "content": "iana"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " orig"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "엠"}
{"type": "content", "content": "마"}
{"type": "content", "content": "의"}
{"type": "content", "content": " 전"}
{"type": "content", "content": "투"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " date"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "202"}
{"type": "content", "content": "1"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " media"}
{"type": "content", "content": "_type"}
{"type": "content", "content": " -"}
{"type": "content", "content": " tv"}
{"type": "content", "content": ";"}
{"type": "content", "content": " category"}
{"type": "content", "content": " -"}
{"type": "content", "content": " anime"}
{"type": "content", "content": "  "}
{"type": "content", "content": "\n38"}
{"type": "content", "content": ";"}
{"type": "content", "content": " ru"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "С"}
{"type": "content", "content": "ек"}
{"type": "content", "content": "рет"}
{"type": "content", "content": "ные"}
{"type": "content", "content": " материалы"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " en"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "The"}
{"type": "content", "content": " X"}
{"type": "content", "content": "-F"}
{"type": "content", "content": "iles"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " orig"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "The"}
{"type": "content", "content": " X"}
{"type": "content", "content": "-F"}
{"type": "content", "content": "iles"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " date"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "199"}
{"type": "content", "content": "3"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " media"}
{"type": "content", "content": "_type"}
{"type": "content", "content": " -"}
{"type": "content", "content": " tv"}
{"type": "content", "content": ";"}
{"type": "content", "content": " category"}
{"type": "content", "content": " -"}
{"type": "content", "content": " anime"}
{"type": "content", "content": "  "}
{"type": "content", "content": "\n39"}
{"type": "content", "content": ";"}
{"type": "content", "content": " ru"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "А"}
{"type": "content", "content": "та"}
{"type": "content", "content": "ка"}
{"type": "content", "content": " тит"}
{"type": "content", "content": "анов"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " en"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "Attack"}
{"type": "content", "content": " on"}
{"type": "content", "content": " Titan"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " orig"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "進"}
{"type": "content", "content": "撃"}
{"type": "content", "content": "の"}
{"type": "content", "content": "巨"}
{"type": "content", "content": "人"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " date"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "201"}
{"type": "content", "content": "3"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " media"}
{"type": "content", "content": "_type"}
{"type": "content", "content": " -"}
{"type": "content", "content": " tv"}
{"type": "content", "content": ";"}
{"type": "content", "content": " category"}
{"type": "content", "content": " -"}
{"type": "content", "content": " anime"}
{"type": "content", "content": "  "}
{"type": "content", "content": "\n40"}
{"type": "content", "content": ";"}
{"type": "content", "content": " ru"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "П"}
{"type": "content", "content": "ок"}
{"type": "content", "content": "ем"}
{"type": "content", "content": "оны"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " en"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "Pok"}
{"type": "content", "content": "émon"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " orig"}
{"type": "content", "content": "_name"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "ポ"}
{"type": "content", "content": "ケ"}
{"type": "content", "content": "モ"}
{"type": "content", "content": "ン"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " date"}
{"type": "content", "content": " -"}
{"type": "content", "content": " **"}
{"type": "content", "content": "199"}
{"type": "content", "content": "7"}
{"type": "content", "content": "**"}
{"type": "content", "content": ";"}
{"type": "content", "content": " media"}
{"type": "content", "content": "_type"}
{"type": "content", "content": " -"}
{"type": "content", "content": " tv"}
{"type": "content", "content": ";"}
{"type": "content", "content": " category"}
{"type": "content", "content": " -"}
{"type": "content", "content": " anime"}
{"type": "content", "content": "  "}
{"type": "content", "content": "\n\nЕсли"}
{"type": "content", "content": " нужно"}
{"type": "content", "content": " изменить"}
{"type": "content", "content": " какие"}
{"type": "content", "content": "-то"}
{"type": "content", "content": " асп"}
{"type": "content", "content": "екты"}
{"type": "content", "content": " списка"}
{"type": "content", "content": ","}
{"type": "content", "content": " д"}
{"type": "content", "content": "айте"}
{"type": "content", "content": " знать"}
{"type": "content", "content": "!"}
Assistant: роблема, с которой вы столкнулись, заключается в том, что функция `extractOrigNames` не может корректно извлечь названия из ответа, полученного от GPT API. Ваша функция должна искать строки с `orig_name` и извлекать соответствующие значения. Однако в вашем примере ответа от GPT API получено много строк с контентом, но они не соответствуют ожидаемому формату, который ваша функция пытается обработать.

### Анализ полученного ответа

Ваш ответ от GPT API имеет вид:

```json
{"type": "provider", "provider": {"name": "NexraChatGPT", ...}}
{"type": "content", "content": "Вот"}
{"type": "content", "content": " список"}
{"type": "content", ...}
```

Строки, содержащие информацию по `orig_name`, отсутствуют или неправильно структурированы.

### Исправление основного алгоритма `extractOrigNames`

Вам нужно будет изменить логику, чтобы учитывать данные, которые вы получили от GPT API. Давайте перепишем функцию `extractOrigNames`, чтобы она лучше обрабатывала ответ и учитывала разные ключи, а также использовала отдельный массив для сбора информации о названиях:

### Обновленная версия функции `extractOrigNames`

```go
func extractOrigNames(response string) []string {
    var origNames []string
    log.Println("Extracting original names from response.")

    // Уберите лишние символы и разделите ответ на строки
    lines := strings.Split(strings.TrimSpace(response), "\n")
    
    log.Printf("Lines extracted: %d\n", len(lines))

    for _, line := range lines {
        // Если строка не пустая, проверяем ее
        if strings.Contains(line, "orig_name -") {
            // Разбиваем строку на части
            parts := strings.Split(line, ";")
            for _, part := range parts {
                part = strings.TrimSpace(part) // чистим пробелы
                if strings.HasPrefix(part, "orig_name -") {
                    // Извлекаем и добавляем в список
                    origName := strings.TrimSpace(strings.Split(part, "-")[1])
                    if origName != "" {
                        log.Printf("Found orig_name: %s\n", origName)
                        origNames = append(origNames, origName)
                    }
                }
            }
        }
    }
    log.Printf("Extracted orig_names: %v\n", origNames)
    return origNames
}
```

### Изменения, внесенные в функцию

1. **Использование `strings.Split`**: Теперь мы разбиваем ответ на строки, используя явное разделение по символу новой строки. Это позволяет более точно извлекать нужные данные.

2. **Проверка на наличие `orig_name -`**: Функция теперь будет искать только в строках, содержащих `orig_name`.

3. **Логирование**: Добавлены логирующие сообщения для отслеживания выполнения и количества обработанных строк.

### Также, проверьте логику обработки ответа

На этапе обработки ответа GPT API, когда вы собираете результаты, убедитесь, что вы правильно обрабатываете событийный поток. Возможно, вам нужно будет сохранить контент событий в переменной и затем очищать его, чтобы избежать лишних пробелов.

### Обновленный фрагмент обработки ответа GPT API

Убедитесь, что вы обрабатываете данные последовательно и логически:

```go
// Чтение потока данных
decoder := json.NewDecoder(resp.Body)
for {
    var line map[string]interface{}
    if err := decoder.Decode(&line); err == io.EOF {
        break
    } else if err != nil {
        log.Printf("Ошибка при чтении потока ответа: %v\n", err)
        break
    }

    // Обработка событий
    if lineType, ok := line["type"].(string); ok {
        log.Printf("Processing line type: %s\n", lineType)
        if lineType == "content" {
            if content, ok := line["content"].(string); ok {
                result.WriteString(content) // аккумулируем контент
            }
        } else if lineType == "provider" {
            log.Printf("Provider Info: %v\n", line)
        }
    }
}
```

### Заключение

После внесения указанных изменений вы должны увидеть, что в логах количество `orig_names` увеличилось, и код сможет корректно извлекать оригинальные названия из ответа GPT API. Это позволит продолжить выполнение запроса к API TMDB без ошибок и вернуть корректный ответ клиенту.
User: расскажи подробнее как мы тут собираем result
for {
				var line map[string]interface{}
				if err := decoder.Decode(&line); err == io.EOF {
					break
				} else if err != nil {
					log.Printf("Ошибка при чтении потока ответа: %v\n", err)
					break
				}
				// Обработка событий
				if lineType, ok := line["type"].(string); ok {
					if lineType == "content" {
						if content, ok := line["content"].(string); ok {
							result.WriteString(content)
						}
					} else if lineType == "provider" {
						log.Printf("Provider Info: %v\n", line)
					}
				}
Assistant: авайте подробнее разберем, как в этом коде происходит сбор контента из потока `text/event-stream`. 

### Общая структура цикла

Цикл `for` предназначен для бесконечного считывания данных из потока ответа, поступающего от GPT API по протоколу `text/event-stream`. Мы используем `decoder.Decode(&line)` для преобразования каждой строки данных в мапу (`map[string]interface{}`), где ключами являются строки, а значениями — соответствующие значения ответов.

### Шаги выполнения

1. **Чтение строки в мапу**:
    ```go
    var line map[string]interface{}
    if err := decoder.Decode(&line); err == io.EOF {
        break
    } else if err != nil {
        log.Printf("Ошибка при чтении потока ответа: %v\n", err)
        break
    }
    ```
   - Мы создаем переменную `line` типа `map[string]interface{}` каждый раз, когда начинаем новую итерацию цикла.
   - Метод `decoder.Decode(&line)` пытается прочитать следующую строку из потока.
   - Если достигается конец потока (`io.EOF`), цикл прерывается.
   - Если происходит какая-либо ошибка при декодировании, она логируется, и цикл также прерывается.

2. **Обработка событий**:
    ```go
    if lineType, ok := line["type"].(string); ok {
    ```
   - Мы пытаемся получить значение по ключу `"type"` из мапы и проверить, является ли оно строкой.
   - Если это действительно строка, продолжаем обработку.

3. **Обработка контента**:
    ```go
    if lineType == "content" {
        if content, ok := line["content"].(string); ok {
            result.WriteString(content)
        }
    }
    ```
   - Если тип события равен `"content"`, мы пытаемся получить строку из ключа `"content"`.
   - Если всё получается, мы используем `result.WriteString(content)` для добавления полученного сообщения к `result`, который объявлен как `strings.Builder`.
   - `strings.Builder` — это структура, предназначенная для построчной записи строк Интерфейса. Она эффективнее работает с конкатенацией строк по сравнению с простым использованием обычной строки.

4. **Обработка информации о провайдере**:
    ```go
    else if lineType == "provider" {
        log.Printf("Provider Info: %v\n", line)
    }
    ```
   - В этой части мы обрабатываем тип события `"provider"`, выводя информацию о провайдере в логи.

### Что происходит с `result`

- `result` — это переменная типа `strings.Builder`, которая накапливает всю строку данных, получаемую из потока в порядке их поступления. Это позволяет избежать создания множества промежуточных строк, что было бы менее эффективно.
- После завершения цикла (после того, как все данные обработаны), итоговая строка, содержащая собранный контент, может быть использована для извлечения оригинальных названий или для других целей.

### Плюсы использования `strings.Builder`

- **Производительность**: Использование `strings.Builder` более эффективно для многократных конкатенаций строк, так как он управляет памятью и перераспределением более оптимально, чем простое использование обычных строк (объединение через `+`, что приводит к созданию новых строк).
- **Чистота кода**: Код остается чистым и понятным. Вы сосредоточены на том, чтобы просто писать строки в `result`, а не беспокоиться о том, как их объединить.

### Заключение

Таким образом, в этом цикле вы слушаете поток данных в реальном времени, собирая все сообщения типа `"content"`, и в конечном итоге получение полного собранного текста для дальнейшей обработки. Это подход хорошо подходит для работы с потоковыми данными, такими как события из GPT API. Вы можете дополнительно улучшить обработку, добавляя более сложную логику при необходимости, например, обработку других типов содержания или ошибки при его чтении.
User: нужно сделать так чтобы в aggregatedResults не попадали данные с tmdb у которых BackdropPath, PosterPath, MediaType или Title пусты
Assistant:  фильтровать результаты TMDB, исключая те, у которых есть пустые значения для полей `BackdropPath`, `PosterPath`, `MediaType` или `Title`, вам нужно будет добавить проверку во время обработки полученных данных внутри функции `fetchTMDBData`.

Давайте рассмотрим, как изменить функцию `fetchTMDBData` таким образом, чтобы она добавляла только те результаты, которые соответствуют заданным условиям:

### Измененный код функции `fetchTMDBData`

```go
func fetchTMDBData(origName string, results *AggregatedResponse, mu *sync.Mutex) {
    tmdbKey := os.Getenv("TMDB_KEY")
    tmdbUrl := fmt.Sprintf("https://api.themoviedb.org/3/search/multi?api_key=%s&language=ru&query=%s", tmdbKey, origName)

    log.Printf("Fetching data from TMDB for origName: %s\n", origName)
    resp, err := http.Get(tmdbUrl)
    if err != nil {
        log.Printf("Ошибка при запросе к TMDB API: %v\n", err)
        return
    }
    defer resp.Body.Close()

    if resp.StatusCode != http.StatusOK {
        log.Printf("Ошибка от TMDB API, код статуса: %d\n", resp.StatusCode)
        return
    }

    var tmdbResponse struct {
        Results []TMDBResult `json:"results"`
    }

    if err := json.NewDecoder(resp.Body).Decode(&tmdbResponse); err != nil {
        log.Printf("Ошибка при декодировании ответа TMDB: %v\n", err)
        return
    }

    log.Printf("Retrieved %d results from TMDB for origName: %s\n", len(tmdbResponse.Results), origName)
    
    // Сохраняем результаты в общий массив с блокировкой
    mu.Lock()
    for _, result := range tmdbResponse.Results {
        // Проверяем, что поля не пустые
        if result.BackdropPath != "" && result.PosterPath != "" && result.Title != "" {
            results.Results = append(results.Results, result)
        } else {
            log.Printf("Excluding result with empty fields: %+v\n", result)
        }
    }
    mu.Unlock()
}
```

### Изменения, внесенные в функцию

1. **Проверка полей**: Мы добавили условие внутри цикла, обрабатывающего `tmdbResponse.Results`. Теперь перед добавлением результата в `results.Results` проверяется, что поля `BackdropPath`, `PosterPath` и `Title` не пустые.

2. **Логирование исключенных результатов**: Если результат имеет одно или несколько пустых полей, он не добавляется в `results.Results`, и о этом событии записывается сообщение в логи, чтобы вы могли увидеть, какие результаты были исключены.

### Проверка поля `MediaType`

Если вы также хотите проверить поле `MediaType`, вы можете добавить дополнительное условие в проверку:

```go
if result.BackdropPath != "" && result.PosterPath != "" && result.Title != "" && result.MediaType != "" {
    results.Results = append(results.Results, result)
} else {
    log.Printf("Excluding result with empty fields: %+v\n", result)
}
```

### Заключение

Теперь ваша система будет добавлять только те результаты от TMDB, которые имеют заполненные необходимые поля. Это улучшит качество данных, которые вы отправляете пользователям, исключив ненужные или неполные записи.
User: нашел косяк в
type TMDBResult struct {
	Adult            bool    `json:"adult"`
	BackdropPath     string  `json:"backdrop_path"`
	GenreIDs         []int   `json:"genre_ids"`
	ID               int     `json:"id"`
	OriginalLanguage string  `json:"original_language"`
	OriginalTitle    string  `json:"original_title"`
	Overview         string  `json:"overview"`
	Popularity       float64 `json:"popularity"`
	PosterPath       string  `json:"poster_path"`
	ReleaseDate      string  `json:"release_date"`
	Title            string  `json:"title"`
	Video            bool    `json:"video"`
	VoteAverage      float64 `json:"vote_average"`
	VoteCount        int     `json:"vote_count"`
	MediaType        string  `json:"media_type"`
}
у нас может быть OriginalTitle или OriginalName и Title или Name как правильнее поступить?
Assistant:  TMDB API используются различные наименования полей, и при их отображении в результате можно столкнуться с некоторыми вариантами. Например, для названий фильмов и сериалов могут использоваться разные поля в зависимости от типа медиапроекта:

1. **Title**: используется для отображения названия на английском или другом международном языке.
2. **OriginalTitle**: используется для отображения названия исходного произведения на языке оригинала.
3. **Name**: аналогично `Title`, но чаще используется для сериалов.

### Как поступить

Чтобы выбрать между `OriginalTitle` и `Title`, а также `Name`, вы можете использовать следующую стратегию:

1. **Определение типов медиапроектов**:
   - Если это фильм, используйте `OriginalTitle` и `Title`.
   - Если это сериал, используйте `OriginalName` и `Name`.

2. **Измените структуру `TMDBResult`**, чтобы включить оба варианта. Например:

```go
type TMDBResult struct {
    Adult            bool     `json:"adult"`
    BackdropPath     string   `json:"backdrop_path"`
    GenreIDs         []int    `json:"genre_ids"`
    ID               int      `json:"id"`
    OriginalLanguage string    `json:"original_language"`
    OriginalTitle    string    `json:"original_title,omitempty"`
    OriginalName     string    `json:"original_name,omitempty"`
    Overview         string    `json:"overview"`
    Popularity       float64   `json:"popularity"`
    PosterPath       string    `json:"poster_path"`
    ReleaseDate      string    `json:"release_date"`
    Title            string    `json:"title,omitempty"`
    Name             string    `json:"name,omitempty"`
    Video            bool     `json:"video"`
    VoteAverage      float64  `json:"vote_average"`
    VoteCount        int      `json:"vote_count"`
    MediaType        string    `json:"media_type"`
}
```

### Как использовать

При агрегации результатов вы можете проверять поля:

- Если `MediaType` равно `"movie"`:
  - Используйте `OriginalTitle` и `Title`.

- Если `MediaType` равно `"tv"`:
  - Используйте `OriginalName` и `Name`.

### Пример агрегации

```go
for _, result := range tmdbResponse.Results {
    // Проверяем, что поля не пустые
    if result.BackdropPath != "" && result.PosterPath != "" && 
       ((result.MediaType == "movie" && (result.Title != "" || result.OriginalTitle != "")) ||
        (result.MediaType == "tv" && (result.Name != "" || result.OriginalName != ""))) {
        
        results.Results = append(results.Results, result)
    } else {
        log.Printf("Excluding result with empty fields: %+v\n", result)
    }
}
```

В этом подходе для фильмов и сериалов используются подходящие поля, что делает систему более универсальной. Также добавление `omitempty` в структуру полей позволяет JSON-сериализатору игнорировать пустые значения, что уменьшает объем ответа. 

### Заключение

Таким образом, выбор между `Title`, `OriginalTitle`, `Name` и `OriginalName` основывается на типе медиапроекта. Используя это решение, вы сможете обрабатывать данные TMDB более эффективно и точно.
User: а можно ли как-то еще проверить что title или name на русском языке?
Assistant: , можно провести проверку на русский языкей `Title` или `Name`, используя несколько подходов. Основные методы проверки правильности языка включают следующие:

### 1. Проверка по символам

Вы можете проверить, содержатся ли в строке какие-либо кириллические символы. Это можно сделать с использованием регулярных выражений или простых проверок на наличие диапазона символов Юникода, соответствующих кириллице.

#### Пример с регулярными выражениями

Для этого можно использовать пакет `regexp` в Go:

```go
import (
    "regexp"
)

// Функция для проверки соответствует ли строка русскому языку
func isCyrillic(s string) bool {
    re := regexp.MustCompile(`[А-Яа-яЁё]`)
    return re.MatchString(s)
}
```

### 2. Использование Unicode

Вместо регулярных выражений можно использовать диапазоны кодов символов, определяющих кириллицу:

```go
func isCyrillic(s string) bool {
    for _, r := range s {
        if (r >= 'А' && r <= 'я') || r == 'Ё' || r == 'ё' {
            return true
        }
    }
    return false
}
```

### Применение в вашей структуре

При обработке данных TMDB, вы можете использовать эту функцию для проверки, например, в фрагменте кода, где вы фильтруете результаты.

#### Пример кода

```go
for _, result := range tmdbResponse.Results {
    title := result.Title
    name := result.Name
    originalTitle := result.OriginalTitle
    originalName := result.OriginalName

    // Проверяем наличие русских символов
    isTitleCyrillic := isCyrillic(title)
    isNameCyrillic := isCyrillic(name)
    isOriginalTitleCyrillic := isCyrillic(originalTitle)
    isOriginalNameCyrillic := isCyrillic(originalName)

    // Логика для добавления результата, только если есть хотя бы одно русское название
    if result.BackdropPath != "" && result.PosterPath != "" && result.MediaType != "" {
        if (result.MediaType == "movie" && (isTitleCyrillic || isOriginalTitleCyrillic)) ||
           (result.MediaType == "tv" && (isNameCyrillic || isOriginalNameCyrillic)) {
            results.Results = append(results.Results, result)
        } else {
            log.Printf("Excluding result without Russian titles: %+v\n", result)
        }
    }
}
```

### Заключение

Используя методы выше, вы сможете проверять, находятся ли поля `Title` или `Name` на русском языке, добавляя только те записи, которые содержат русские символы. Это обеспечит большую точность в фильтрации данных.
User: А что-то более изящное можно и как регулярка отреагирует на пробел?
Assistant:  более изящной проверки строк на русском языке с использованием регулярных выражений можно создать регулярное выражение, которое будет учитывать как кириллические символы, так и пробелы, а также другие допустимые символы (например, знаки препинания). 

### Регулярное выражение для проверки русского текста

Регулярное выражение, которое позволяет проверить наличие русских символов и игнорировать пробелы и некоторые знаки препинания, может выглядеть следующим образом:

```go
import (
    "regexp"
)

// Функция для проверки, содержит ли строка кириллические символы
func isCyrillic(s string) bool {
    re := regexp.MustCompile(`[А-Яа-яЁё]`) // регулярка для проверки на наличие кириллических символов
    return re.MatchString(s) // возвращает true, если найдены русские буквы
}
```

### Добавление поддержки пробелов и знаков препинания

Если вы хотите также включать пробелы и игнорировать их при проверке, можно просто использовать существующую логику, поскольку регулярное выражение проверяет наличие символов независимо от пробелов или других знаков. В таком случае, вы просто проверяете текст на наличие хотя бы одной кириллической буквы.

### Пример корректной проверки

Вот как вы можете комбинировать функцию проверки с фильтрацией результатов в вашей логике:

```go
for _, result := range tmdbResponse.Results {
    var relevantName string

    // Выбор нужного поля в зависимости от типа медиа
    if result.MediaType == "movie" {
        relevantName = result.Title
    } else if result.MediaType == "tv" {
        relevantName = result.Name
    }

    // Если оригинальное название доступно, проверяем и его
    if relevantName == "" && result.OriginalTitle != "" {
        relevantName = result.OriginalTitle
    } else if relevantName == "" && result.OriginalName != "" {
        relevantName = result.OriginalName
    }

    // Проверка на наличие русских символов
    if result.BackdropPath != "" && result.PosterPath != "" && isCyrillic(relevantName) {
        results.Results = append(results.Results, result)
    } else {
        log.Printf("Excluding result due to empty fields or non-Russian title: %+v\n", result)
    }
}
```

### Объяснение кода

1. **Выбор названия**: В зависимости от `MediaType`, выбирается соответствующее название (либо `Title`, либо `Name`).
2. **Проверка оригинальных названий**: Если `relevantName` пустое, тогда проверяются оригинальные названия. Это подразумевает логику проверки всех потенциальных значений.
3. **Проверка на русские символы**: Функция `isCyrillic` проверяет, есть ли в строке кириллические буквы. Если есть, объект добавляется в результаты.
4. **Логирование исключенных результатов**: При отсутствии необходимых данных или русских символов результат исключается из конечного списка.

### Заключение

Такой подход позволяет элегантно разрешить вопрос о том, как проверять поля на наличие русского текста, без лишних усложнений. Вы просто ищете наличие русских символов, игнорируя пробелы и знаки препинания, что делает код более читаемым и поддерживаемым.
User: а если так?
for _, result := range tmdbResponse.Results {
		title := result.Title
		name := result.Name
		isTitleCyrillic := isCyrillic(title)
		isNameCyrillic := isCyrillic(name)
		if result.BackdropPath != "" && result.PosterPath != "" &&
			((result.MediaType == "movie" && (result.Title != "" || result.OriginalTitle != "") && isTitleCyrillic) ||
				(result.MediaType == "tv" && (result.Name != "" || result.OriginalName != "") && isNameCyrillic)) {
			results.Results = append(results.Results, result)
		} else {
			log.Printf("Excluding result with empty fields: %+v\n", result)
		}
	}
Assistant:  предложенный код выглядит вполне разумным и эффективным для фильтрации результатов на основе наличия русских символов и обязательных полей. Давайте более подробно разберем, как работает этот код и как вы можете его улучшить.

### Разбор кода

```go
for _, result := range tmdbResponse.Results {
    title := result.Title
    name := result.Name
    isTitleCyrillic := isCyrillic(title)
    isNameCyrillic := isCyrillic(name)
    
    if result.BackdropPath != "" && result.PosterPath != "" &&
        ((result.MediaType == "movie" && (result.Title != "" || result.OriginalTitle != "") && isTitleCyrillic) ||
         (result.MediaType == "tv" && (result.Name != "" || result.OriginalName != "") && isNameCyrillic)) {
        results.Results = append(results.Results, result)
    } else {
        log.Printf("Excluding result with empty fields: %+v\n", result)
    }
}
```

### Подробности работы

1. **Итерация по результатам**: Вы проходите по всем результатам `tmdbResponse.Results`, которые вы получили от TMDB API.

2. **Проверка названий**:
    - Сначала вы извлекаете `title` и `name` из `result`.
    - Затем вызывается функция `isCyrillic`, которая проверяет, содержатся ли в этих строках кириллические символы.

3. **Фильтрация результатов**:
    ```go
    if result.BackdropPath != "" && result.PosterPath != "" &&
        ((result.MediaType == "movie" && (result.Title != "" || result.OriginalTitle != "") && isTitleCyrillic) ||
         (result.MediaType == "tv" && (result.Name != "" || result.OriginalName != "") && isNameCyrillic)) {
    ```
   Эта часть кода проверяет:
   - Заполнены ли пути для изображений (`BackdropPath` и `PosterPath`).
   - Для фильмов (`MediaType == "movie"`):
     - Есть ли `Title` или `OriginalTitle`.
     - Проверяется, являются ли они русскими с помощью `isTitleCyrillic`.
   - Для сериалов (`MediaType == "tv"`):
     - Аналогично проверяются `Name` и `OriginalName`, а также русский язык через `isNameCyrillic`.

4. **Добавление в результаты**: Если все условия в блоке `if` выполнены, результат добавляется в `results.Results`.

5. **Логирование**: Если какое-либо из условий не выполняется, результат выводится в лог для анализа.

### Возможные улучшения

Ваш код уже хорошо написан, но его можно немного улучшить:

1. **Упрощение логики условий**: 
   В текущем коде вы повторяете некоторые условия (например, проверка на `OriginalTitle` и `OriginalName`). Это можно сделать более очевидным.

2. **Объединение проверок на заголовки**:
   Вместо дублирования имен `(result.Title != "" || result.OriginalTitle != "")` и `(result.Name != "" || result.OriginalName != "")`, вы можете объединить их в одну функцию. Это сделает код более читаемым и управляемым.

### Упрощенный код

```go
func hasValidName(mediaType string, title, originalTitle, name, originalName string) bool {
    // Проверяем, что минимум одно поле заполнено + проверяем на кириллицу
    switch mediaType {
    case "movie":
        return (title != "" || originalTitle != "") && (isCyrillic(title) || isCyrillic(originalTitle))
    case "tv":
        return (name != "" || originalName != "") && (isCyrillic(name) || isCyrillic(originalName))
    default:
        return false // Или вы можете что-то другое вернуть по вашему усмотрению
    }
}

// Используем новую функцию при фильтрации результатов:
for _, result := range tmdbResponse.Results {
    if result.BackdropPath != "" && result.PosterPath != "" && hasValidName(result.MediaType, result.Title, result.OriginalTitle, result.Name, result.OriginalName) {
        results.Results = append(results.Results, result)
    } else {
        log.Printf("Excluding result with empty fields: %+v\n", result)
    }
}
```

### Заключение

Этот упрощенный подход к проверкам позволяет избежать дублирования кода и делает логику более ясной. Теперь при проверке вы можете легко добавлять или изменять условия без необходимости многократно редактировать один и тот же код.
User: надо бы как-то еще перемешать агреггированный ответ
Assistant:  вам нужно перемешать (перемеш или случайное распределение) результаты в агрегированном ответе перед тем, как вернуть его пользователю, вы можете использовать алгоритм Фишера-Йетса (Fisher-Yates shuffle), который эффективно перемешивает элементы в срезе. 

Этот алгоритм работает с временной сложностью O(n) и позволяет перемещать элементы в срезе без дополнительного использования памяти. Вот как можно применить этот алгоритм в вашем коде.

### Реализация алгоритма Фишера-Йетса на Go

Вот простая функция для перемешивания среза:

```go
import (
    "math/rand"
    "time"
)

// shuffleResults перемешивает срез результатов
func shuffleResults(results []TMDBResult) {
    // Инициализация генератора случайных чисел
    rand.Seed(time.Now().UnixNano())
    n := len(results)
    for i := n - 1; i > 0; i-- {
        j := rand.Intn(i + 1) // генерируем случайный индекс от 0 до i
        results[i], results[j] = results[j], results[i] // меняем местами
    }
}
```

### Внедрение перемешивания в код

После того, как вы получили все результаты и передаете их в `results.Results`, вы можете вызвать `shuffleResults` перед сериализацией и возвратом результатов клиенту.

```go
// Возвращаем агрегированный результат
shuffleResults(results.Results) // Перемешиваем результаты

w.Header().Set("Content-Type", "application/json")
response, err := json.Marshal(results)
if err != nil {
    log.Printf("Ошибка при сериализации ответа: %v\n", err)
    http.Error(w, "Ошибка при подготовке ответа", http.StatusInternalServerError)
    return
}
log.Println("Sending aggregated response to the client.")
w.Write(response)
```

### Заключение

Теперь, когда вы вызываете `shuffleResults(results.Results)`, все результаты в итоговом ответе будут случайно перемешаны, что добавит элемент случайности в ваш ответ и сделает его более непредсказуемым для пользователей. Это может быть полезно, например, в приложениях, где вы хотите показать разнообразие рекомендуемых материалов.
User: ты забыл?
у нас агрегация так собирается
			aggregatedResults := AggregatedResponse{}
			log.Printf("Starting requests to TMDB API for %d original names.\n", len(origNames))
			var wg sync.WaitGroup
			var mu sync.Mutex
			for _, origName := range origNames {
				wg.Add(1)
				go func(origName string) {
					defer wg.Done()
					fetchTMDBData(origName, &aggregatedResults, &mu)
				}(origName)
			}
			wg.Wait() // Ждем, пока все горутины завершат свою работу
			log.Println("Finished TMDB API requests.")
			// Возвращаем агрегированный результат
			response, err := json.Marshal(aggregatedResults)
Assistant:  добавим перемешивание агрегированных результатов тогда, когда вы завершите запросы к TMDB API и получите все результаты. Учитывая вашу текущую реализацию, мы можем вызвать функцию `shuffleResults` непосредственно перед сериализацией `aggregatedResults` в JSON.

Вот как это может выглядеть в вашем коде:

### Полный сценарий с перемешиванием результатов

1. **Добавьте функцию перемешивания** в ваш код.

```go
import (
    "math/rand"
    "time"
)

// shuffleResults перемешивает срез результатов
func shuffleResults(results *[]TMDBResult) {
    rand.Seed(time.Now().UnixNano()) // Инициализация генератора случайных чисел
    n := len(*results)
    for i := n - 1; i > 0; i-- {
        j := rand.Intn(i + 1) // Генерируем случайный индекс от 0 до i
        (*results)[i], (*results)[j] = (*results)[j], (*results)[i] // Меняем местами
    }
}
```

2. **Обновите вызов после завершения запросов к TMDB API**.

Вот завершенный фрагмент вашего кода, включая перемешивание результатов:

```go
// Запрос к TMDB API для получения данных
aggregatedResults := AggregatedResponse{}
log.Printf("Starting requests to TMDB API for %d original names.\n", len(origNames))
var wg sync.WaitGroup
var mu sync.Mutex

for _, origName := range origNames {
    wg.Add(1)
    go func(origName string) {
        defer wg.Done()
        fetchTMDBData(origName, &aggregatedResults, &mu)
    }(origName)
}

wg.Wait() // Ждем, пока все горутины завершат свою работу
log.Println("Finished TMDB API requests.")

// Перемешиваем результаты перед отправкой клиенту
shuffleResults(&aggregatedResults.Results)

// Возвращаем агрегированный результат
response, err := json.Marshal(aggregatedResults)
if err != nil {
    log.Printf("Ошибка при сериализации ответа: %v\n", err)
    http.Error(w, "Ошибка при подготовке ответа", http.StatusInternalServerError)
    return
}
log.Println("Sending aggregated response to the client.")
w.Header().Set("Content-Type", "application/json")
w.Write(response)
```

### Объяснение изменений:

- **Перемешивание результатов**: После завершения всех запросов к TMDB API и перед сериализацией `aggregatedResults`, мы вызываем `shuffleResults(&aggregatedResults.Results)`. Это перемешивает срез `Results`, который находится внутри вашей структуры `AggregatedResponse`.

- **Использование указателя**: В функцию `shuffleResults` передается указатель на срез результатов (`*[]TMDBResult`). Это позволяет функции изменять оригинальный срез и перемешивать его без создания дополнительной копии.

Такие изменения обеспечивают перемешивание результатов перед их отправкой обратно клиенту, добавляя элемент неожиданности и разнообразия в ответ.
User: ошибка:
cannot use &aggregatedResults.Results (value of type *[]TMDBResult) as []TMDBResult value in argument to shuffleResultscompilerIncompatibleAssign
field Results []TMDBResult
Assistant: ибку, которую вы видите, можно устранить путем правильного управления указателями и значениями. Поскольку `Results` в структуре `AggregatedResponse` является срезом `[]TMDBResult`, вам нужно передавать его в функцию `shuffleResults` как срез, а не как указатель на него.

Давайте изменим вашу функцию `shuffleResults` так, чтобы она принимала срез, а не указатель, и вызывайте её таким образом:

### Обновленный код

1. **Измените функцию `shuffleResults`, чтобы она принимала срез, а не указатель**:

```go
// shuffleResults перемешивает срез результатов
func shuffleResults(results []TMDBResult) {
    rand.Seed(time.Now().UnixNano()) // Инициализация генератора случайных чисел
    n := len(results)
    for i := n - 1; i > 0; i-- {
        j := rand.Intn(i + 1) // Генерируем случайный индекс от 0 до i
        results[i], results[j] = results[j], results[i] // Меняем местами
    }
}
```

2. **Вызов перемешивания результатов в вашем коде**:

Теперь вы можете вызывать функцию `shuffleResults` без указателя:

```go
// Запрос к TMDB API для получения данных
aggregatedResults := AggregatedResponse{}
log.Printf("Starting requests to TMDB API for %d original names.\n", len(origNames))
var wg sync.WaitGroup
var mu sync.Mutex

for _, origName := range origNames {
    wg.Add(1)
    go func(origName string) {
        defer wg.Done()
        fetchTMDBData(origName, &aggregatedResults, &mu)
    }(origName)
}

wg.Wait() // Ждем, пока все горутины завершат свою работу
log.Println("Finished TMDB API requests.")

// Перемешиваем результаты перед отправкой клиенту
shuffleResults(aggregatedResults.Results) // Передаем срез, а не указатель

// Возвращаем агрегированный результат
response, err := json.Marshal(aggregatedResults)
if err != nil {
    log.Printf("Ошибка при сериализации ответа: %v\n", err)
    http.Error(w, "Ошибка при подготовке ответа", http.StatusInternalServerError)
    return
}
log.Println("Sending aggregated response to the client.")
w.Header().Set("Content-Type", "application/json")
w.Write(response)
```

### Объяснение изменений:

- Функция `shuffleResults` теперь принимает срез `results []TMDBResult` и изменяет элементы этого среза внутри самой функции.
  
- При вызове `shuffleResults(aggregatedResults.Results)` передается срез `Results` напрямую. Это избавляет от необходимости использования указателей и устраняет ошибку компиляции.

Теперь всё должно работать без проблем, и результаты будут перемешаны перед отправкой клиенту.
User: такс нам еще дубли из aggregatedResults по этим полям OriginalTitle OriginalName Name Title ID
Assistant: авайте немного расширим функциональность вашего кода, чтобы исключать дублирующиеся результаты по полям `OriginalTitle`, `OriginalName`, `Name`, `Title` и `ID` перед тем, как мы их отправим. Для этого мы можем использовать сет (map) для отслеживания уникальных значений, когда мы добавляем результаты в `aggregatedResults`.

### Вот как можно это сделать

1. **Обновите функцию `fetchTMDBData`**, чтобы исключить дублирующие результаты:

```go
func fetchTMDBData(origName string, results *AggregatedResponse, mu *sync.Mutex, seen map[string]bool) {
    tmdbKey := os.Getenv("TMDB_KEY")
    tmdbUrl := fmt.Sprintf("https://api.themoviedb.org/3/search/multi?api_key=%s&language=ru&query=%s", tmdbKey, origName)

    log.Printf("Fetching data from TMDB for origName: %s\n", origName)
    resp, err := http.Get(tmdbUrl)
    if err != nil {
        log.Printf("Ошибка при запросе к TMDB API: %v\n", err)
        return
    }
    defer resp.Body.Close()

    if resp.StatusCode != http.StatusOK {
        log.Printf("Ошибка от TMDB API, код статуса: %d\n", resp.StatusCode)
        return
    }

    var tmdbResponse struct {
        Results []TMDBResult `json:"results"`
    }

    if err := json.NewDecoder(resp.Body).Decode(&tmdbResponse); err != nil {
        log.Printf("Ошибка при декодировании ответа TMDB: %v\n", err)
        return
    }

    log.Printf("Retrieved %d results from TMDB for origName: %s\n", len(tmdbResponse.Results), origName)

    mu.Lock()
    defer mu.Unlock()

    for _, result := range tmdbResponse.Results {
        // Проверяем уникальность результата
        uniqueKey := fmt.Sprintf("%s_%s_%d", result.Title, result.OriginalTitle, result.ID)
        if seen[uniqueKey] {
            log.Printf("Excluding duplicate result: %+v\n", result)
            continue
        }

        // Проверяем, что поля не пустые
        if result.BackdropPath != "" && result.PosterPath != "" {
            results.Results = append(results.Results, result)
            seen[uniqueKey] = true // Устанавливаем ключ как "увиденный"
        } else {
            log.Printf("Excluding result with empty fields: %+v\n", result)
        }
    }
}
```

### Внесенные изменения

1. **Добавление карты `seen`**: В качестве дополнительного параметра мы передаем карту `seen`, которая будет отслеживать уникальные ключи для каждого результата. Ключ формируется на основе полей `Title`, `OriginalTitle` и `ID`.

2. **Проверка на дубликаты**: Перед добавлением результата в срез, мы проверяем, если уникальный ключ уже существует в карте `seen`. Если он существует, мы пропускаем добавление этого результата.

3. **Формат уникального ключа**: Ключ формируется как строка, состоящая из `Title`, `OriginalTitle` и `ID`, разделенных подчеркиванием, что позволяет обеспечить уникальность за счет комбинации значений.

### Вызов функции `fetchTMDBData`

Теперь при вызове `fetchTMDBData` в основном коде вам нужно также создать и передать карту `seen`.

Вот как это будет выглядеть:

```go
// Запрос к TMDB API для получения данных
aggregatedResults := AggregatedResponse{}
seen := make(map[string]bool) // Инициализация карты для уникальных записей
log.Printf("Starting requests to TMDB API for %d original names.\n", len(origNames))
var wg sync.WaitGroup
var mu sync.Mutex

for _, origName := range origNames {
    wg.Add(1)
    go func(origName string) {
        defer wg.Done()
        fetchTMDBData(origName, &aggregatedResults, &mu, seen) // Передаем карту seen
    }(origName)
}

wg.Wait() // Ждем, пока все горутины завершат свою работу
log.Println("Finished TMDB API requests.")

// Перемешиваем результаты перед отправкой клиенту
shuffleResults(aggregatedResults.Results) // Передаем срез, а не указатель

// Возвращаем агрегированный результат
response, err := json.Marshal(aggregatedResults)
if err != nil {
    log.Printf("Ошибка при сериализации ответа: %v\n", err)
    http.Error(w, "Ошибка при подготовке ответа", http.StatusInternalServerError)
    return
}
log.Println("Sending aggregated response to the client.")
w.Header().Set("Content-Type", "application/json")
w.Write(response)
```

### Заключение

Теперь ваш код будет исключать дублирующиеся результаты из `aggregatedResults`, что улучшит качество данных, которые вы возвращаете пользователю. Эта реализация проста и эффективно использует карту для отслеживания уже добавленных результатов, избегая лишнего расхода памяти.
User: не не не, я же говорю в aggregatedResults а не при сборке 1 запроса
Assistant: вините за недоразумение. Давайте рассмотрим, как избежать дублирования в итоговом агрегированном ответе `aggregatedResults`. Мы можем сделать это, фильтруя конечный результат после того, как все результаты были собраны от API TMDB.

### Шаги по устранению дубликатов из `aggregatedResults`

1. **Создайте карту** для отслеживания уникальных идентификаторов, используя комбинацию полей `OriginalTitle`, `OriginalName`, `Title`, `Name`, и `ID` в качестве ключа.
2. **Переберите результаты** в `aggregatedResults` и добавьте только уникальные результаты в новый срез.

### Пример реализации

Вот как вы можете реализовать этот процесс:

```go
// После сбора всех результатов:
finalResults := AggregatedResponse{}
seen := make(map[string]struct{}) // используем пустую структуру, так как она занимает 0 байт

for _, result := range aggregatedResults.Results {
    // Создаем уникальный ключ
    uniqueKey := fmt.Sprintf("%s|%s|%s|%s|%d", result.Title, result.OriginalTitle, result.Name, result.OriginalName, result.ID)
    
    // Проверяем, был ли этот ключ уже добавлен
    if _, exists := seen[uniqueKey]; !exists {
        seen[uniqueKey] = struct{}{} // добавляем ключ в seen
        finalResults.Results = append(finalResults.Results, result) // добавляем результат в финальный срез
    } else {
        log.Printf("Excluding duplicate result: %+v\n", result)
    }
}

// Теперь finalResults будет содержать только уникальные записи
// Возвращаем агрегированный результат
response, err := json.Marshal(finalResults)
if err != nil {
    log.Printf("Ошибка при сериализации ответа: %v\n", err)
    http.Error(w, "Ошибка при подготовке ответа", http.StatusInternalServerError)
    return
}
log.Println("Sending aggregated response to the client.")
w.Header().Set("Content-Type", "application/json")
w.Write(response)
```

### Объяснение кода

1. **Создание карты `seen`**: Используем `map[string]struct{}`, что позволяет избежать дублирования. Структура занимает 0 байт и служит только для хранения уникальных ключей.

2. **Формирование уникального ключа**: Уникальный ключ формируется непосредственно из значений полей `Title`, `OriginalTitle`, `Name`, `OriginalName` и `ID`, разделенных символом `|`. Это гарантирует, что даже если одно из полей будет пустым, ключ все равно будет уникальным.

3. **Проверка на дубликаты**: Перед добавлением результата в `finalResults`, мы сначала проверяем, существует ли этот ключ в `seen`. Если он не существует, мы добавляем его.

4. **Логирование**: Если результат уже присутствует, он будет исключен, и это будет зафиксировано в логах.

### Заключение

С помощью этой логики вы сможете эффективно фильтровать дублирующиеся результаты в `aggregatedResults`, при этом сохраняя только уникальные записи для возврата клиенту. Эта реализация является универсальной и может легко масштабироваться, если возникнет необходимость добавлять дополнительные условия для уникальности в будущем.
User: а как работает у нас shuffleResults(aggregatedResults.Results)
Assistant: ункция `shuffleResults(aggregatedResults.Results)` отвечает за случайное перемешивание элементов врезе `Results`, который находится в структуре `aggregatedResults`. Это полезно, если вы хотите обеспечить произвольный порядок представления данных, чтобы улучшить пользовательский опыт или предотвратить паттерны предсказуемости в рекомендуемых результатах.

### Как работает `shuffleResults`

Давайте рассмотрим, как может быть реализована функция `shuffleResults` и как она работает:

#### Реализация функции

Вот пример реализации `shuffleResults` с использованием алгоритма Фишера-Йетса:

```go
import (
    "math/rand"
    "time"
)

// shuffleResults перемешивает срез результатов
func shuffleResults(results []TMDBResult) {
    // Инициализация генератора случайных чисел
    rand.Seed(time.Now().UnixNano()) // Используем текущее время для случайности
    n := len(results)
    for i := n - 1; i > 0; i-- {
        j := rand.Intn(i + 1) // Генерируем случайный индекс от 0 до i
        // Меняем местами элементы
        results[i], results[j] = results[j], results[i]
    }
}
```

### Как работает `shuffleResults`

1. **Инициализация генератора случайных чисел**:
   - Вызов `rand.Seed(time.Now().UnixNano())` используется для инициализации генератора случайных чисел. Это гарантирует, что каждый раз при запуске программы случайные числа будут различными. Без этого шага генератор может выдавать одни и те же последовательности случайных чисел при каждом запуске.

2. **Перемешивание элементов**:
   - С помощью алгоритма Фишера-Йетса, который работает следующим образом:
     - Мы проходим по срезу с конца к началу.
     - На каждом шаге генерируется случайный индекс `j` от 0 до текущего индекса `i`.
     - Затем элементы `results[i]` и `results[j]` меняются местами.
   - Это обеспечивает равномерное перемешивание среза.

### Применение функции

Когда вы вызываете `shuffleResults(aggregatedResults.Results)`, вы перемешиваете все результаты, содержащиеся в `Results`, перед тем, как отправить их клиенту:

```go
shuffleResults(aggregatedResults.Results) // Перемешивание результатов
```

### Пример использования в основном коде

Вот как это может вписаться в ваше приложение:

```go
// Запрос к TMDB API для получения данных
aggregatedResults := AggregatedResponse{}
// ...код для сбора данных...

// Перемешиваем результаты перед отправкой клиенту
shuffleResults(aggregatedResults.Results) // Перемешиваем результаты

// Теперь отправляем перемешанные результаты
response, err := json.Marshal(aggregatedResults)
if err != nil {
    log.Printf("Ошибка при сериализации ответа: %v\n", err)
    http.Error(w, "Ошибка при подготовке ответа", http.StatusInternalServerError)
    return
}
log.Println("Sending aggregated response to the client.")
w.Header().Set("Content-Type", "application/json")
w.Write(response)
```

### Заключение

Таким образом, `shuffleResults` позволяет случайным образом перемешивать элементы в срезе `Results`, обеспечивая разнообразие в предоставляемых данных. Это может быть полезно в различных ситуациях, особенно когда порядок вывода данных может влиять на восприятие пользователем.
User: а приравнивать это shuffleResults(aggregatedResults.Results) не нужно к какой-то переменной?
Assistant: , вызове `shuffleResults(aggregatedResults.Results)` фактически изменяется порядок элементов в уже существующем срезе `Results`, поэтому нет необходимости присваивать его как новую переменную. Функция `shuffleResults` изменяет порядок элементов непосредственно в исходном срезе, поскольку передается по ссылке.

### Как это работает

Когда вы вызываете функцию среза в Go, передается указатель на существующий срез. Изменения, внесенные в срез внутри функции, отражаются на оригинальном срезе, так как оба среза ссылаются на одно и то же место в памяти.

#### Пример вызова `shuffleResults`

```go
// Перемешивание результатов перед отправкой клиенту
shuffleResults(aggregatedResults.Results) // Перемешиваем результаты
```

### Подходящий подход

- Нет необходимости присваивать перемешанный результат новой переменной. Существующий срез `aggregatedResults.Results` будет обновлен внутри функции.
- Таким образом, ваша структура `AggregatedResponse` останется с теми же данными, но в случайном порядке, что позволяет легко отправить измененный срез клиенту.

### Общий пример в контексте вашей агрегации

Ваш код может выглядеть так:

```go
// Запрос к TMDB API для получения данных
aggregatedResults := AggregatedResponse{}
// ... код для сбора данных ...

// Перемешиваем результаты перед отправкой клиенту
shuffleResults(aggregatedResults.Results) // Перемешиваем результаты

// Теперь отправляем перемешанные результаты
response, err := json.Marshal(aggregatedResults)
if err != nil {
    log.Printf("Ошибка при сериализации ответа: %v\n", err)
    http.Error(w, "Ошибка при подготовке ответа", http.StatusInternalServerError)
    return
}
log.Println("Sending aggregated response to the client.")
w.Header().Set("Content-Type", "application/json")
w.Write(response)
```

### Заключение

Таким образом, функция `shuffleResults` перемешивает элементы в `aggregatedResults.Results` на месте без необходимости создавать новую переменную. Это делает код более читаемым, простым и эффективным.
User: в нашем этом коде есть проблема кстати result.Name и result.OriginalName есть только у result.MediaType == "tv", а result.Title и result.OriginalTitle у result.MediaType == "movie"
finalResults := AggregatedResponse{}
			seen := make(map[string]struct{})
			for _, result := range aggregatedResults.Results {
				// Создаем уникальный ключ
				uniqueKey := fmt.Sprintf("%s|%s|%s|%s|%d", result.Title, result.OriginalTitle, result.Name, result.OriginalName, result.ID)
				// Проверяем, был ли этот ключ уже добавлен
				if _, exists := seen[uniqueKey]; !exists {
					seen[uniqueKey] = struct{}{}                                // добавляем ключ в seen
					finalResults.Results = append(finalResults.Results, result) // добавляем результат в финальный срез
				} else {
					log.Printf("Excluding duplicate result: %+v\n", result)
				}
			}
			response, err := json.Marshal(finalResults)
Assistant: