{"version":3,"file":"index.mjs","names":[],"sources":["../src/api/date-decoder.ts","../src/api/errors.ts","../src/api/api-client.ts","../src/http-session.ts","../src/web/errors.ts","../src/web/helpers.ts","../src/web/types/anime-list-status.ts","../src/web/web-client.ts"],"sourcesContent":["/**\n * Парсит даты в формате API Anime365: \"yyyy-MM-dd HH:mm:ss\" в московской таймзоне.\n */\nexport function parseApiDate(dateString: string): Date {\n  // API возвращает даты в формате \"2024-01-15 14:30:00\" в московской таймзоне (UTC+3)\n  const [datePart, timePart] = dateString.split(\" \");\n  if (!datePart || !timePart) {\n    throw new Error(`Invalid date format: ${dateString}`);\n  }\n\n  // Добавляем смещение московской таймзоны (+03:00)\n  const isoString = `${datePart}T${timePart}+03:00`;\n  const date = new Date(isoString);\n\n  if (Number.isNaN(date.getTime())) {\n    throw new Error(`Invalid date: ${dateString}`);\n  }\n\n  return date;\n}\n\n/**\n * Проверяет, является ли дата \"пустой\" датой-заглушкой API (2000-01-01 00:00:00).\n */\nexport function isEmptyDate(date: Date): boolean {\n  // API возвращает \"2000-01-01 00:00:00\" MSK как пустую дату\n  // В UTC это будет 1999-12-31 21:00:00\n  return date.getTime() === new Date(\"1999-12-31T21:00:00.000Z\").getTime();\n}\n\n/**\n * Рекурсивно преобразует строки дат в объекты Date в JSON-ответе.\n * Ищет поля, заканчивающиеся на \"DateTime\".\n */\nexport function transformDatesInResponse<T>(obj: unknown): T {\n  if (obj === null || obj === undefined) {\n    return obj as T;\n  }\n\n  if (Array.isArray(obj)) {\n    return obj.map((item) => transformDatesInResponse(item)) as T;\n  }\n\n  if (typeof obj === \"object\") {\n    const result: Record<string, unknown> = {};\n    for (const [key, value] of Object.entries(obj)) {\n      if (key.endsWith(\"DateTime\") && typeof value === \"string\") {\n        result[key] = parseApiDate(value);\n      } else {\n        result[key] = transformDatesInResponse(value);\n      }\n    }\n    return result as T;\n  }\n\n  return obj as T;\n}\n","export class ApiError extends Error {\n  constructor(\n    public readonly code: number,\n    message: string\n  ) {\n    super(message);\n    this.name = \"ApiError\";\n  }\n\n  static authenticationRequired(): ApiError {\n    return new ApiError(403, \"Authentication required\");\n  }\n\n  static notFound(): ApiError {\n    return new ApiError(404, \"Not found\");\n  }\n\n  static other(code: number, message: string): ApiError {\n    return new ApiError(code, message);\n  }\n}\n\nexport class ApiClientError extends Error {\n  constructor(\n    message: string,\n    public readonly cause?: ApiError | Error\n  ) {\n    super(message);\n    this.name = \"ApiClientError\";\n  }\n\n  static canNotDecodeResponseJson(error?: Error): ApiClientError {\n    return new ApiClientError(\"Cannot decode response JSON\", error);\n  }\n\n  static apiError(error: ApiError): ApiClientError {\n    return new ApiClientError(`API error: ${error.message}`, error);\n  }\n\n  static requestFailed(error?: Error): ApiClientError {\n    return new ApiClientError(\"Request failed\", error);\n  }\n}\n","import type { HttpSession } from \"../http-session.js\";\nimport { transformDatesInResponse } from \"./date-decoder.js\";\nimport { ApiClientError, ApiError } from \"./errors.js\";\nimport type {\n  Episode,\n  EpisodeFull,\n  Series,\n  SeriesFull,\n  TranslationEmbed,\n  TranslationFull,\n} from \"./types/index.js\";\n\ninterface ApiSuccessfulResponse<T> {\n  data: T;\n}\n\ninterface ApiErrorResponse {\n  error: {\n    code: number;\n    message: string;\n  };\n}\n\nexport interface ListSeriesOptions {\n  query?: string;\n  limit?: number;\n  offset?: number;\n  chips?: Record<string, string>;\n  myAnimeListId?: number;\n}\n\nexport interface ListEpisodesOptions {\n  seriesId?: number;\n  limit?: number;\n  offset?: number;\n}\n\nexport interface ApiClientConfig {\n  debug?: boolean;\n}\n\nexport class ApiClient {\n  private readonly debug: boolean;\n\n  constructor(\n    private readonly session: HttpSession,\n    config: ApiClientConfig = {}\n  ) {\n    this.debug = config.debug ?? false;\n  }\n\n  async sendRequest<T>(endpoint: string, queryItems: Record<string, string> = {}): Promise<T> {\n    const url = new URL(`/api${endpoint}`, this.session.baseUrl);\n\n    // Сортируем параметры по имени (как в Swift)\n    const sortedParams = Object.entries(queryItems).sort(([a], [b]) => a.localeCompare(b));\n    for (const [key, value] of sortedParams) {\n      url.searchParams.set(key, value);\n    }\n\n    let response: Response;\n    try {\n      response = await this.session.request(url.pathname + url.search, {\n        method: \"GET\",\n        headers: {\n          Accept: \"application/json\",\n        },\n      });\n    } catch (error) {\n      if (this.debug) {\n        console.debug(`[ApiClient] Request failed: GET ${url.toString()}`, error);\n      }\n      throw ApiClientError.requestFailed(error instanceof Error ? error : undefined);\n    }\n\n    if (this.debug) {\n      console.debug(`[ApiClient] API request: GET ${url.toString()} [${response.status}]`);\n    }\n\n    let data: unknown;\n    let responseText: string | undefined;\n    try {\n      responseText = await response.text();\n      data = JSON.parse(responseText);\n    } catch (error) {\n      if (this.debug) {\n        console.error(\n          `[ApiClient] Decoding JSON error:\\n\\n${error}\\n\\nAPI response:\\n\\n${responseText ?? \"(no response text)\"}`\n        );\n      }\n      throw ApiClientError.canNotDecodeResponseJson(error instanceof Error ? error : undefined);\n    }\n\n    // Проверяем на ошибку API\n    if (this.isApiErrorResponse(data)) {\n      const apiError = data.error;\n\n      if (this.debug) {\n        console.warn(`[ApiClient] API error:\\n\\n${responseText}`);\n      }\n\n      if (apiError.code === 403) {\n        throw ApiClientError.apiError(ApiError.authenticationRequired());\n      }\n\n      if (apiError.code === 404) {\n        throw ApiClientError.apiError(ApiError.notFound());\n      }\n\n      throw ApiClientError.apiError(ApiError.other(apiError.code, apiError.message));\n    }\n\n    // Успешный ответ\n    if (this.isApiSuccessfulResponse<T>(data)) {\n      return transformDatesInResponse<T>(data.data);\n    }\n\n    if (this.debug) {\n      console.error(\n        `[ApiClient] Could not decode response as ApiSuccessfulResponse:\\n\\n${responseText}`\n      );\n    }\n\n    throw ApiClientError.canNotDecodeResponseJson();\n  }\n\n  private isApiErrorResponse(data: unknown): data is ApiErrorResponse {\n    return (\n      typeof data === \"object\" &&\n      data !== null &&\n      \"error\" in data &&\n      typeof (data as ApiErrorResponse).error === \"object\" &&\n      (data as ApiErrorResponse).error !== null &&\n      \"code\" in (data as ApiErrorResponse).error &&\n      \"message\" in (data as ApiErrorResponse).error\n    );\n  }\n\n  private isApiSuccessfulResponse<T>(data: unknown): data is ApiSuccessfulResponse<T> {\n    return typeof data === \"object\" && data !== null && \"data\" in data;\n  }\n\n  // === API Requests ===\n\n  async getSeries(seriesId: number): Promise<SeriesFull> {\n    return this.sendRequest<SeriesFull>(`/series/${seriesId}`);\n  }\n\n  async getEpisode(episodeId: number): Promise<EpisodeFull> {\n    return this.sendRequest<EpisodeFull>(`/episodes/${episodeId}`);\n  }\n\n  async listSeries(options: ListSeriesOptions = {}): Promise<Series[]> {\n    const queryItems: Record<string, string> = {};\n\n    if (options.chips) {\n      const chipsValue = Object.entries(options.chips)\n        .sort(([a], [b]) => a.localeCompare(b))\n        .map(([key, value]) => `${key}=${value}`)\n        .join(\";\");\n      queryItems.chips = chipsValue;\n    }\n\n    if (options.query !== undefined) {\n      queryItems.query = options.query;\n    }\n\n    if (options.limit !== undefined) {\n      queryItems.limit = String(options.limit);\n    }\n\n    if (options.offset !== undefined) {\n      queryItems.offset = String(options.offset);\n    }\n\n    if (options.myAnimeListId !== undefined) {\n      queryItems.myAnimeListId = String(options.myAnimeListId);\n    }\n\n    return this.sendRequest<Series[]>(\"/series\", queryItems);\n  }\n\n  async listEpisodes(options: ListEpisodesOptions = {}): Promise<Episode[]> {\n    const queryItems: Record<string, string> = {};\n\n    if (options.seriesId !== undefined) {\n      queryItems.seriesId = String(options.seriesId);\n    }\n\n    if (options.limit !== undefined) {\n      queryItems.limit = String(options.limit);\n    }\n\n    if (options.offset !== undefined) {\n      queryItems.offset = String(options.offset);\n    }\n\n    return this.sendRequest<Episode[]>(\"/episodes\", queryItems);\n  }\n\n  async getTranslation(translationId: number): Promise<TranslationFull> {\n    return this.sendRequest<TranslationFull>(`/translations/${translationId}`);\n  }\n\n  async getTranslationEmbed(translationId: number): Promise<TranslationEmbed> {\n    return this.sendRequest<TranslationEmbed>(`/translations/embed/${translationId}`);\n  }\n}\n","import { CookieJar } from \"tough-cookie\";\n\nexport interface RequestOptions extends Omit<RequestInit, \"dispatcher\"> {\n  timeout?: number;\n}\n\nexport class HttpSession {\n  readonly jar: CookieJar;\n  readonly baseUrl: string;\n\n  constructor(baseUrl: string) {\n    this.baseUrl = baseUrl;\n    this.jar = new CookieJar();\n  }\n\n  async request(path: string, options: RequestOptions = {}): Promise<Response> {\n    const { timeout = 10000, ...init } = options;\n    let url = new URL(path, this.baseUrl);\n\n    const controller = new AbortController();\n    const timeoutId = setTimeout(() => controller.abort(), timeout);\n\n    try {\n      // Manually follow redirects to preserve cookies from intermediate responses\n      let redirectCount = 0;\n      const maxRedirects = 10;\n\n      while (redirectCount < maxRedirects) {\n        // Get cookies for this URL\n        const cookieString = await this.jar.getCookieString(url.toString());\n\n        const headers = {\n          Accept: \"*/*\",\n          \"User-Agent\":\n            \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36\",\n          \"Cache-Control\": \"no-cache\",\n          ...(cookieString ? { Cookie: cookieString } : {}),\n          ...init.headers,\n        };\n\n        const response = await fetch(url, {\n          ...init,\n          headers,\n          signal: controller.signal,\n          redirect: \"manual\", // Don't follow redirects automatically\n        });\n\n        // Store cookies from response BEFORE following redirect\n        const setCookieHeaders = response.headers.getSetCookie();\n        for (const setCookie of setCookieHeaders) {\n          await this.jar.setCookie(setCookie, url.toString());\n        }\n\n        // Check if it's a redirect\n        if (response.status >= 300 && response.status < 400) {\n          const location = response.headers.get(\"location\");\n          if (!location) {\n            return response;\n          }\n\n          // Resolve relative URL against current URL\n          url = new URL(location, url);\n          redirectCount++;\n\n          // For 302/303, switch to GET method\n          if (response.status === 302 || response.status === 303) {\n            init.method = \"GET\";\n            init.body = undefined;\n          }\n\n          continue;\n        }\n\n        return response;\n      }\n\n      throw new Error(\"Too many redirects\");\n    } finally {\n      clearTimeout(timeoutId);\n    }\n  }\n\n  async getCookie(name: string): Promise<string | undefined> {\n    const cookies = await this.jar.getCookies(this.baseUrl);\n    return cookies.find((c) => c.key === name)?.value;\n  }\n\n  async getCookieString(): Promise<string> {\n    return await this.jar.getCookieString(this.baseUrl);\n  }\n\n  async setCookie(name: string, value: string): Promise<void> {\n    const url = new URL(this.baseUrl);\n    await this.jar.setCookie(`${name}=${value}; Domain=${url.hostname}; Path=/`, this.baseUrl);\n  }\n}\n","export class WebClientError extends Error {\n  constructor(message: string) {\n    super(message);\n    this.name = \"WebClientError\";\n  }\n\n  static couldNotConvertResponseToHttpResponse(): WebClientError {\n    return new WebClientError(\"Could not convert response to HTTP response\");\n  }\n\n  static couldNotConvertResponseDataToString(): WebClientError {\n    return new WebClientError(\"Could not convert response data to string\");\n  }\n\n  static badStatusCode(statusCode: number): WebClientError {\n    return new WebClientError(`Bad status code: ${statusCode}`);\n  }\n\n  static authenticationRequired(): WebClientError {\n    return new WebClientError(\"Authentication required\");\n  }\n\n  static couldNotParseHtml(): WebClientError {\n    return new WebClientError(\"Could not parse HTML\");\n  }\n\n  static unknownError(error: Error): WebClientError {\n    const err = new WebClientError(`Unknown error: ${error.message}`);\n    err.cause = error;\n    return err;\n  }\n}\n\nexport class WebClientTypeNormalizationError extends Error {\n  constructor(message: string) {\n    super(message);\n    this.name = \"WebClientTypeNormalizationError\";\n  }\n\n  static failedCreatingDTOFromHTMLElement(message: string): WebClientTypeNormalizationError {\n    return new WebClientTypeNormalizationError(message);\n  }\n}\n","/**\n * Извлекает ID сериала и эпизода из URL вида /catalog/{seriesId}-{slug}/{episodeId}-{slug}\n */\nexport function extractIdentifiersFromUrl(url: URL): {\n  seriesId: number | null;\n  episodeId: number | null;\n} {\n  const pathParts = url.pathname.split(\"/\").filter(Boolean);\n\n  if (pathParts.length < 2 || pathParts[0] !== \"catalog\") {\n    return { seriesId: null, episodeId: null };\n  }\n\n  let seriesId: number | null = null;\n  let episodeId: number | null = null;\n\n  // Извлекаем seriesId из второго сегмента (например, \"12345-anime-name\")\n  const seriesMatch = pathParts[1]?.match(/(\\d+)$/);\n  if (seriesMatch) {\n    seriesId = Number.parseInt(seriesMatch[1], 10);\n  }\n\n  // Извлекаем episodeId из третьего сегмента (например, \"67890-episode-1\")\n  if (pathParts.length >= 3) {\n    const episodeMatch = pathParts[2]?.match(/(\\d+)$/);\n    if (episodeMatch) {\n      episodeId = Number.parseInt(episodeMatch[1], 10);\n    }\n  }\n\n  return { seriesId, episodeId };\n}\n\n/**\n * Парсит строку длительности формата \"mm:ss\" или \"hh:mm:ss\" в секунды.\n */\nexport function parseDurationString(durationString: string): number | null {\n  const parts = durationString.split(\":\").map((p) => Number.parseInt(p, 10));\n\n  if (parts.some((p) => Number.isNaN(p))) {\n    return null;\n  }\n\n  switch (parts.length) {\n    case 2: // mm:ss\n      return parts[0] * 60 + parts[1];\n    case 3: // hh:mm:ss\n      return parts[0] * 3600 + parts[1] * 60 + parts[2];\n    default:\n      return null;\n  }\n}\n\n/**\n * Парсит дату в формате \"dd.MM.yyyy HH:mm\" в московской таймзоне.\n */\nexport function parseWebDate(dateString: string): Date | null {\n  // Формат: \"15.01.2024 14:30\"\n  const match = dateString.match(/^(\\d{2})\\.(\\d{2})\\.(\\d{4}) (\\d{2}):(\\d{2})$/);\n  if (!match) {\n    return null;\n  }\n\n  const [, day, month, year, hours, minutes] = match;\n  // Создаем ISO строку с московским смещением (+03:00)\n  const isoString = `${year}-${month}-${day}T${hours}:${minutes}:00+03:00`;\n  const date = new Date(isoString);\n\n  return Number.isNaN(date.getTime()) ? null : date;\n}\n","export type AnimeListEntryStatus =\n  | \"watching\"\n  | \"completed\"\n  | \"onHold\"\n  | \"dropped\"\n  | \"planned\"\n  | \"notInList\";\n\nexport const AnimeListEntryStatusNumericId: Record<AnimeListEntryStatus, number> = {\n  planned: 0,\n  watching: 1,\n  completed: 2,\n  onHold: 3,\n  dropped: 4,\n  notInList: 99,\n};\n\nexport function animeListEntryStatusFromNumericId(id: number): AnimeListEntryStatus | null {\n  switch (id) {\n    case 0:\n      return \"planned\";\n    case 1:\n      return \"watching\";\n    case 2:\n      return \"completed\";\n    case 3:\n      return \"onHold\";\n    case 4:\n      return \"dropped\";\n    case 99:\n      return \"notInList\";\n    default:\n      return null;\n  }\n}\n\nexport type AnimeListCategory = \"watching\" | \"completed\" | \"onHold\" | \"dropped\" | \"planned\";\n\nexport const AnimeListCategoryWebPath: Record<AnimeListCategory, string> = {\n  watching: \"watching\",\n  completed: \"completed\",\n  onHold: \"onhold\",\n  dropped: \"dropped\",\n  planned: \"planned\",\n};\n\nexport const AnimeListCategoryNumericId: Record<AnimeListCategory, number> = {\n  planned: 0,\n  watching: 1,\n  completed: 2,\n  onHold: 3,\n  dropped: 4,\n};\n\nexport function animeListCategoryFromNumericId(id: number): AnimeListCategory | null {\n  switch (id) {\n    case 0:\n      return \"planned\";\n    case 1:\n      return \"watching\";\n    case 2:\n      return \"completed\";\n    case 3:\n      return \"onHold\";\n    case 4:\n      return \"dropped\";\n    default:\n      return null;\n  }\n}\n","import * as cheerio from \"cheerio\";\nimport type { Element } from \"domhandler\";\nimport type { HttpSession } from \"../http-session.js\";\nimport { WebClientError } from \"./errors.js\";\nimport { extractIdentifiersFromUrl, parseDurationString, parseWebDate } from \"./helpers.js\";\nimport type {\n  AnimeListCategory,\n  AnimeListEditableEntry,\n  AnimeListEntry,\n  MomentDetails,\n  MomentEmbed,\n  MomentPreview,\n  MomentSorting,\n  NewPersonalEpisode,\n  NewRecentEpisode,\n  Profile,\n  VideoSource,\n} from \"./types/index.js\";\nimport { AnimeListCategoryWebPath, animeListEntryStatusFromNumericId } from \"./types/index.js\";\n\nconst COOKIE_NAME_CSRF = \"csrf\";\nconst FORM_DATA_FIELD_CSRF = \"csrf\";\n\nexport interface WebClientConfig {\n  debug?: boolean;\n}\n\nexport class WebClient {\n  private readonly debug: boolean;\n\n  constructor(\n    private readonly session: HttpSession,\n    config: WebClientConfig = {}\n  ) {\n    this.debug = config.debug ?? false;\n  }\n\n  get baseUrl(): string {\n    return this.session.baseUrl;\n  }\n\n  async sendRequest(path: string, queryItems: Record<string, string> = {}): Promise<string> {\n    const url = new URL(path, this.session.baseUrl);\n\n    // Сортируем параметры по имени (как в Swift)\n    const sortedParams = Object.entries(queryItems).sort(([a], [b]) => a.localeCompare(b));\n    for (const [key, value] of sortedParams) {\n      url.searchParams.set(key, value);\n    }\n\n    let response: Response;\n    try {\n      response = await this.session.request(url.pathname + url.search, {\n        method: \"GET\",\n      });\n    } catch (error) {\n      if (this.debug) {\n        console.debug(`[WebClient] Request failed: GET ${url.toString()}`, error);\n      }\n      throw WebClientError.unknownError(error instanceof Error ? error : new Error(String(error)));\n    }\n\n    if (this.debug) {\n      console.debug(`[WebClient] Web request: GET ${url.toString()} [${response.status}]`);\n    }\n\n    if (response.status >= 400) {\n      if (this.debug) {\n        console.debug(`[WebClient] Bad status code: ${response.status}`);\n      }\n      throw WebClientError.badStatusCode(response.status);\n    }\n\n    let html: string;\n    try {\n      html = await response.text();\n    } catch (error) {\n      if (this.debug) {\n        console.debug(`[WebClient] Could not convert response data to string`, error);\n      }\n      throw WebClientError.couldNotConvertResponseDataToString();\n    }\n\n    return html;\n  }\n\n  async sendPostRequest(\n    path: string,\n    queryItems: Record<string, string> = {},\n    formData: Record<string, string> = {}\n  ): Promise<string> {\n    const url = new URL(path, this.session.baseUrl);\n\n    // Сортируем параметры по имени\n    const sortedParams = Object.entries(queryItems).sort(([a], [b]) => a.localeCompare(b));\n    for (const [key, value] of sortedParams) {\n      url.searchParams.set(key, value);\n    }\n\n    // Получаем или создаем CSRF токен\n    let csrfToken = await this.session.getCookie(COOKIE_NAME_CSRF);\n    if (!csrfToken) {\n      csrfToken = crypto.randomUUID();\n      await this.session.setCookie(COOKIE_NAME_CSRF, csrfToken);\n    }\n\n    // Добавляем CSRF токен в form data\n    const formDataWithCsrf = {\n      ...formData,\n      [FORM_DATA_FIELD_CSRF]: csrfToken,\n    };\n\n    // Формируем body как URL-encoded\n    const body = new URLSearchParams(formDataWithCsrf).toString();\n\n    let response: Response;\n    try {\n      response = await this.session.request(url.pathname + url.search, {\n        method: \"POST\",\n        headers: {\n          \"Content-Type\": \"application/x-www-form-urlencoded\",\n        },\n        body,\n      });\n    } catch (error) {\n      if (this.debug) {\n        console.debug(`[WebClient] Request failed: POST ${url.toString()}`, error);\n      }\n      throw WebClientError.unknownError(error instanceof Error ? error : new Error(String(error)));\n    }\n\n    if (this.debug) {\n      console.debug(`[WebClient] Web request: POST ${url.toString()} [${response.status}]`);\n    }\n\n    if (response.status >= 400) {\n      if (this.debug) {\n        console.debug(`[WebClient] Bad status code: ${response.status}`);\n        console.debug(`[WebClient] Form data: ${JSON.stringify(formDataWithCsrf)}`);\n      }\n      throw WebClientError.badStatusCode(response.status);\n    }\n\n    let html: string;\n    try {\n      html = await response.text();\n    } catch (error) {\n      if (this.debug) {\n        console.debug(`[WebClient] Could not convert response data to string`, error);\n      }\n      throw WebClientError.couldNotConvertResponseDataToString();\n    }\n\n    return html;\n  }\n\n  parseHtml(html: string): cheerio.CheerioAPI {\n    return cheerio.load(html);\n  }\n\n  // === Login ===\n\n  async login(username: string, password: string): Promise<void> {\n    const html = await this.sendPostRequest(\n      \"/users/login\",\n      {},\n      {\n        \"LoginForm[username]\": username,\n        \"LoginForm[password]\": password,\n        dynpage: \"1\",\n        yt0: \"\",\n      }\n    );\n\n    if (html.includes(\"Неверный E-mail или пароль.\") || html.includes(\"Вход по паролю\")) {\n      throw new WebClientError(\"Invalid credentials\");\n    }\n  }\n\n  // === Profile ===\n\n  async getProfile(): Promise<Profile> {\n    const html = await this.sendRequest(\"/users/profile\", { dynpage: \"1\" });\n\n    if (html.includes(\"Вход по паролю\")) {\n      throw WebClientError.authenticationRequired();\n    }\n\n    const $ = this.parseHtml(html);\n\n    // Извлекаем ID аккаунта\n    const accountIdMatch = html.match(/ID аккаунта: (\\d+)/);\n    if (!accountIdMatch) {\n      throw WebClientError.couldNotParseHtml();\n    }\n    const id = Number.parseInt(accountIdMatch[1], 10);\n\n    // Извлекаем имя\n    const name = $(\"content .m-small-title\").first().text().trim();\n    if (!name) {\n      throw WebClientError.couldNotParseHtml();\n    }\n\n    // Извлекаем аватар\n    const avatarSrc = $(\"content .card-image.hide-on-small-and-down img\").first().attr(\"src\");\n    if (!avatarSrc) {\n      throw WebClientError.couldNotParseHtml();\n    }\n    const avatarUrl = new URL(avatarSrc, this.baseUrl).toString();\n\n    return { id, name, avatarUrl };\n  }\n\n  // === Personal Episodes ===\n\n  async getPersonalEpisodes(page: number): Promise<NewPersonalEpisode[]> {\n    const queryItems: Record<string, string> = {\n      ajax: \"m-index-personal-episodes\",\n    };\n\n    if (page > 1) {\n      queryItems.pageP = String(page);\n    }\n\n    const html = await this.sendRequest(\"/\", queryItems);\n\n    if (html.includes(\"Вход или регистрация\") || html.includes(\"Вход - Anime 365\")) {\n      throw WebClientError.authenticationRequired();\n    }\n\n    const $ = this.parseHtml(html);\n    const episodes: NewPersonalEpisode[] = [];\n\n    $(\"#m-index-personal-episodes div.m-new-episode\").each((_, element) => {\n      try {\n        const episode = this.parsePersonalEpisode($, element);\n        if (episode) {\n          episodes.push(episode);\n        }\n      } catch {\n        // Skip invalid episodes\n      }\n    });\n\n    return episodes;\n  }\n\n  private parsePersonalEpisode($: cheerio.CheerioAPI, element: Element): NewPersonalEpisode | null {\n    const $el = $(element);\n\n    // Извлекаем URL эпизода\n    const episodeHref = $el.find(\"a[href]\").first().attr(\"href\");\n    if (!episodeHref) {\n      return null;\n    }\n\n    const episodeUrl = new URL(episodeHref, this.baseUrl);\n    const { seriesId, episodeId } = extractIdentifiersFromUrl(episodeUrl);\n\n    if (seriesId === null || episodeId === null) {\n      return null;\n    }\n\n    // Извлекаем постер\n    const styleAttr = $el.find(\"div.circle[style]\").first().attr(\"style\") || \"\";\n    const posterMatch = styleAttr.match(/background-image: ?url\\('(.+?)'\\)/);\n    if (!posterMatch) {\n      return null;\n    }\n    const seriesPosterUrl = new URL(\n      posterMatch[1].replace(\".140x140.1\", \"\"),\n      this.baseUrl\n    ).toString();\n\n    // Извлекаем названия\n    const seriesTitleRu = $el.find(\"h5.line-1 a\").first().text().trim();\n    const seriesTitleRomaji = $el.find(\"h6.line-2 a\").first().text().trim();\n\n    if (!seriesTitleRu || !seriesTitleRomaji) {\n      return null;\n    }\n\n    // Извлекаем номер эпизода\n    const episodeNumberLabel = $el.find(\"span.online-h\").first().text().trim();\n    if (!episodeNumberLabel) {\n      return null;\n    }\n\n    // Извлекаем тип обновления\n    const titleText = $el.find(\"span.title\").first().text();\n    const updateTypeMatch = titleText.match(/\\((.+?)\\)/);\n    if (!updateTypeMatch) {\n      return null;\n    }\n    const episodeUpdateType = updateTypeMatch[1].trim();\n\n    return {\n      seriesId,\n      seriesPosterUrl,\n      seriesTitleRu,\n      seriesTitleRomaji,\n      episodeId,\n      episodeNumberLabel,\n      episodeUpdateType,\n    };\n  }\n\n  // === Recent Episodes ===\n\n  async getRecentEpisodes(page: number): Promise<NewRecentEpisode[]> {\n    const html = await this.sendRequest(`/page/${page}`, {\n      ajax: \"m-index-recent-episodes\",\n    });\n\n    if (html.includes(\"Вход или регистрация\") || html.includes(\"Вход - Anime 365\")) {\n      throw WebClientError.authenticationRequired();\n    }\n\n    const $ = this.parseHtml(html);\n    const episodes: NewRecentEpisode[] = [];\n\n    $(\"#m-index-recent-episodes .m-new-episodes.collection.with-header\").each(\n      (_, sectionElement) => {\n        const $section = $(sectionElement);\n        const sectionHeader = $section.find(\"h3\").text().trim();\n\n        if (!sectionHeader) {\n          return;\n        }\n\n        $section.find(\"div.m-new-episode\").each((_, episodeElement) => {\n          try {\n            const episode = this.parseRecentEpisode($, episodeElement, sectionHeader);\n            if (episode) {\n              episodes.push(episode);\n            }\n          } catch {\n            // Skip invalid episodes\n          }\n        });\n      }\n    );\n\n    return episodes;\n  }\n\n  private parseRecentEpisode(\n    $: cheerio.CheerioAPI,\n    element: Element,\n    sectionTitle: string\n  ): NewRecentEpisode | null {\n    const $el = $(element);\n\n    // Извлекаем URL эпизода\n    const episodeHref = $el.find(\"a[href]\").first().attr(\"href\");\n    if (!episodeHref) {\n      return null;\n    }\n\n    const episodeUrl = new URL(episodeHref, this.baseUrl);\n    const { seriesId, episodeId } = extractIdentifiersFromUrl(episodeUrl);\n\n    if (seriesId === null || episodeId === null) {\n      return null;\n    }\n\n    // Извлекаем постер\n    const styleAttr = $el.find(\"div.circle[style]\").first().attr(\"style\") || \"\";\n    const posterMatch = styleAttr.match(/background-image: ?url\\('(.+?)'\\)/);\n    if (!posterMatch) {\n      return null;\n    }\n    const seriesPosterUrl = new URL(\n      posterMatch[1].replace(\".140x140.1\", \"\"),\n      this.baseUrl\n    ).toString();\n\n    // Извлекаем названия\n    const seriesTitleRu = $el.find(\"h5.line-1 a\").first().text().trim();\n    const seriesTitleRomaji = $el.find(\"h6.line-2 a\").first().text().trim();\n\n    if (!seriesTitleRu || !seriesTitleRomaji) {\n      return null;\n    }\n\n    // Извлекаем номер эпизода\n    const episodeNumberLabel = $el.find(\"span.online-h\").first().text().trim();\n    if (!episodeNumberLabel) {\n      return null;\n    }\n\n    // Извлекаем время загрузки\n    const titleText = $el.find(\"span.title\").first().text();\n    const timeMatch = titleText.match(/в (\\d{2}:\\d{2})/);\n    if (!timeMatch) {\n      return null;\n    }\n\n    // Парсим дату из заголовка секции\n    const dateString = sectionTitle.replace(\"Новые серии \", \"\");\n    const episodeUploadedAt = parseWebDate(`${dateString} ${timeMatch[1]}`);\n\n    if (!episodeUploadedAt) {\n      return null;\n    }\n\n    return {\n      seriesId,\n      seriesPosterUrl,\n      seriesTitleRu,\n      seriesTitleRomaji,\n      episodeId,\n      episodeNumberLabel,\n      episodeUploadedAt,\n    };\n  }\n\n  // === Anime List ===\n\n  async getAnimeList(userId: number, category: AnimeListCategory): Promise<AnimeListEntry[]> {\n    const html = await this.sendRequest(\n      `/users/${userId}/list/${AnimeListCategoryWebPath[category]}`,\n      {\n        dynpage: \"1\",\n      }\n    );\n\n    if (html.includes(\"Вход или регистрация\") || html.includes(\"Вход - Anime 365\")) {\n      throw WebClientError.authenticationRequired();\n    }\n\n    const $ = this.parseHtml(html);\n    const entries: AnimeListEntry[] = [];\n\n    $(\"div.card.m-animelist-card tr.m-animelist-item\").each((_, element) => {\n      try {\n        const entry = this.parseAnimeListEntry($, element);\n        if (entry) {\n          entries.push(entry);\n        }\n      } catch {\n        // Skip invalid entries\n      }\n    });\n\n    return entries;\n  }\n\n  private parseAnimeListEntry($: cheerio.CheerioAPI, element: Element): AnimeListEntry | null {\n    const $el = $(element);\n\n    const seriesIdStr = $el.attr(\"data-id\");\n    if (!seriesIdStr) {\n      return null;\n    }\n    const seriesId = Number.parseInt(seriesIdStr, 10);\n\n    const seriesTitleFull = $el.find(\"a[href]\").first().text().trim();\n    if (!seriesTitleFull) {\n      return null;\n    }\n\n    const episodesString = $el.find('td[data-name=\"episodes\"]').first().text().trim();\n    const episodesParts = episodesString.split(\" / \");\n    if (episodesParts.length !== 2) {\n      return null;\n    }\n\n    const episodesWatched = Number.parseInt(episodesParts[0], 10);\n    if (Number.isNaN(episodesWatched)) {\n      return null;\n    }\n\n    const episodesTotal = Number.parseInt(episodesParts[1], 10);\n\n    const scoreString = $el.find('td[data-name=\"score\"]').first().text().trim();\n    const score = scoreString ? Number.parseInt(scoreString, 10) : null;\n\n    return {\n      seriesId,\n      seriesTitleFull,\n      episodesWatched,\n      episodesTotal: Number.isNaN(episodesTotal) ? null : episodesTotal,\n      score: score && !Number.isNaN(score) ? score : null,\n    };\n  }\n\n  async getAnimeListEditableEntry(seriesId: number): Promise<AnimeListEditableEntry> {\n    const html = await this.sendRequest(`/animelist/edit/${seriesId}`, {\n      mode: \"mini\",\n    });\n\n    if (html.includes(\"Вход или регистрация\") || html.includes(\"Вход - Anime 365\")) {\n      throw WebClientError.authenticationRequired();\n    }\n\n    if (html.includes(\"Добавить в список\")) {\n      return {\n        episodesWatched: 0,\n        status: \"notInList\",\n        score: null,\n        commentary: null,\n      };\n    }\n\n    const $ = this.parseHtml(html);\n\n    const episodesWatchedStr = $(\"input#UsersRates_episodes\").first().attr(\"value\");\n    if (!episodesWatchedStr) {\n      throw WebClientError.couldNotParseHtml();\n    }\n    const episodesWatched = Number.parseInt(episodesWatchedStr, 10);\n\n    const statusStr = $(\"select#UsersRates_status option[selected]\").first().attr(\"value\");\n    if (!statusStr) {\n      throw WebClientError.couldNotParseHtml();\n    }\n    const statusInt = Number.parseInt(statusStr, 10);\n    const status = animeListEntryStatusFromNumericId(statusInt);\n    if (!status) {\n      throw WebClientError.couldNotParseHtml();\n    }\n\n    const scoreStr = $(\"#UsersRates_score option[selected]\").first().attr(\"value\");\n    const scoreInt = scoreStr ? Number.parseInt(scoreStr, 10) : 0;\n    const score = scoreInt > 0 ? scoreInt : null;\n\n    const commentary = $(\"#UsersRates_comment\").first().text() || null;\n\n    return {\n      episodesWatched,\n      status,\n      score,\n      commentary,\n    };\n  }\n\n  async editAnimeListEntry(\n    seriesId: number,\n    score: number,\n    episodes: number,\n    status: number,\n    comment: string\n  ): Promise<void> {\n    await this.sendPostRequest(\n      `/animelist/edit/${seriesId}`,\n      { mode: \"mini\" },\n      {\n        \"UsersRates[score]\": String(score),\n        \"UsersRates[episodes]\": String(episodes),\n        \"UsersRates[status]\": String(status),\n        \"UsersRates[comment]\": comment,\n      }\n    );\n  }\n\n  // === Mark Episode ===\n\n  async markEpisodeAsWatched(translationId: number): Promise<void> {\n    await this.sendPostRequest(`/translations/watched/${translationId}`, {}, {});\n  }\n\n  // === Moments ===\n\n  async getMoments(page: number, sort?: MomentSorting): Promise<MomentPreview[]> {\n    const queryItems: Record<string, string> = {};\n\n    if (page === 1) {\n      queryItems.dynpage = \"1\";\n    } else {\n      queryItems.ajaxPage = \"yw_moments_all\";\n      queryItems.ajaxPageMode = \"more\";\n      queryItems[\"moments-page\"] = String(page);\n    }\n\n    if (sort) {\n      queryItems[\"MomentsFilter[sort]\"] = sort;\n    }\n\n    const html = await this.sendRequest(\"/moments/index\", queryItems);\n\n    if (html.includes(\"Вход или регистрация\") || html.includes(\"Вход - Anime 365\")) {\n      throw WebClientError.authenticationRequired();\n    }\n\n    const $ = this.parseHtml(html);\n    const moments: MomentPreview[] = [];\n\n    $(\"#yw_moments_all div.m-moment\").each((_, element) => {\n      try {\n        const moment = this.parseMomentPreview($, element);\n        if (moment) {\n          moments.push(moment);\n        }\n      } catch {\n        // Skip invalid moments\n      }\n    });\n\n    return moments;\n  }\n\n  async getMomentsBySeries(seriesId: number, page: number): Promise<MomentPreview[]> {\n    const queryItems: Record<string, string> = {};\n\n    if (page === 1) {\n      queryItems.dynpage = \"1\";\n    } else {\n      queryItems.ajaxPage = \"yw_moments_by_series\";\n      queryItems.ajaxPageMode = \"more\";\n      queryItems[\"moments-page\"] = String(page);\n    }\n\n    const html = await this.sendRequest(`/moments/listBySeries/${seriesId}`, queryItems);\n\n    if (html.includes(\"Вход или регистрация\") || html.includes(\"Вход - Anime 365\")) {\n      throw WebClientError.authenticationRequired();\n    }\n\n    const $ = this.parseHtml(html);\n    const moments: MomentPreview[] = [];\n\n    $(\"#yw_moments_by_series div.m-moment\").each((_, element) => {\n      try {\n        const moment = this.parseMomentPreview($, element);\n        if (moment) {\n          moments.push(moment);\n        }\n      } catch {\n        // Skip invalid moments\n      }\n    });\n\n    return moments;\n  }\n\n  private parseMomentPreview($: cheerio.CheerioAPI, element: Element): MomentPreview | null {\n    const $el = $(element);\n\n    const momentTitle = $el.find(\".m-moment__title a\").first().text().trim();\n    if (!momentTitle) {\n      return null;\n    }\n\n    const sourceDescription = $el.find(\".m-moment__episode\").first().text().trim();\n    if (!sourceDescription) {\n      return null;\n    }\n\n    const previewSrc = $el.find(\".m-moment__thumb.a img[src]\").first().attr(\"src\");\n    if (!previewSrc) {\n      return null;\n    }\n    const coverUrl = new URL(\n      previewSrc.trim().replace(\".320x180\", \".1280x720\").replace(/\\?.+$/, \"\"),\n      this.baseUrl\n    ).toString();\n\n    const momentHref = $el.find(\".m-moment__title a[href]\").first().attr(\"href\");\n    if (!momentHref) {\n      return null;\n    }\n    const momentIdMatch = momentHref.match(/\\/moments\\/(\\d+)/);\n    if (!momentIdMatch) {\n      return null;\n    }\n    const momentId = Number.parseInt(momentIdMatch[1], 10);\n\n    const durationString = $el.find(\".m-moment__duration\").first().text().trim();\n    const durationSeconds = parseDurationString(durationString);\n    if (durationSeconds === null) {\n      return null;\n    }\n\n    return {\n      momentId,\n      coverUrl,\n      momentTitle,\n      sourceDescription,\n      durationSeconds,\n    };\n  }\n\n  async getMomentDetails(momentId: number): Promise<MomentDetails> {\n    const html = await this.sendRequest(`/moments/${momentId}`, {});\n\n    if (html.includes(\"Вход или регистрация\") || html.includes(\"Вход - Anime 365\")) {\n      throw WebClientError.authenticationRequired();\n    }\n\n    const $ = this.parseHtml(html);\n\n    const linkElements = $(\".m-moment-player h3 a[href]\");\n    if (linkElements.length !== 2) {\n      throw WebClientError.couldNotParseHtml();\n    }\n\n    const episodeHref = linkElements.first().attr(\"href\");\n    if (!episodeHref) {\n      throw WebClientError.couldNotParseHtml();\n    }\n\n    const episodeUrl = new URL(episodeHref, this.baseUrl);\n    const { seriesId, episodeId } = extractIdentifiersFromUrl(episodeUrl);\n\n    if (seriesId === null || episodeId === null) {\n      throw WebClientError.couldNotParseHtml();\n    }\n\n    const seriesTitle = linkElements.eq(1).text().trim();\n    if (!seriesTitle) {\n      throw WebClientError.couldNotParseHtml();\n    }\n\n    return { seriesId, seriesTitle, episodeId };\n  }\n\n  async getMomentEmbed(momentId: number): Promise<MomentEmbed> {\n    const html = await this.sendRequest(`/moments/embed/${momentId}`, {});\n\n    if (html.includes(\"Вход или регистрация\") || html.includes(\"Вход - Anime 365\")) {\n      throw WebClientError.authenticationRequired();\n    }\n\n    const $ = this.parseHtml(html);\n\n    const dataSourcesJson = $(\"#main-video\").first().attr(\"data-sources\");\n    if (!dataSourcesJson) {\n      throw WebClientError.couldNotParseHtml();\n    }\n\n    let videoSources: VideoSource[];\n    try {\n      videoSources = JSON.parse(dataSourcesJson);\n    } catch {\n      throw WebClientError.couldNotParseHtml();\n    }\n\n    const validSource = videoSources\n      .filter((s) => s.urls && s.urls.length > 0)\n      .sort((a, b) => b.height - a.height)[0];\n\n    if (!validSource || !validSource.urls[0]) {\n      throw WebClientError.couldNotParseHtml();\n    }\n\n    return { videoUrl: validSource.urls[0] };\n  }\n}\n"],"mappings":"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"}