anicli_api


Nameanicli_api JSON
Version 0.6.4 PyPI version JSON
download
home_pageNone
SummaryAnime extractors api implementation
upload_time2024-03-27 12:34:51
maintainerNone
docs_urlNone
authorvypivshiy
requires_python<4.0,>=3.8
licenseMIT
keywords anime api ru russia asyncio parser httpx dev
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # anicli-api

Программный интерфейс набора парсеров аниме с различных источников.

Присутствует поддержка sync и async методов с помощью `httpx` библиотеки.

Парсеры работают на REST-API (если у источника есть доступ) или если такой интерфейс
отсутствует, то с помощью parsel, chompjs, jmespath, regex библиотек. 


# install
`pip install anicli-api`

# Overview
Структура проекта

- source - наборы модулей для извлечения информации об аниме тайтлов из источников
- player - наборы модулей для извлечения прямой ссылки на видео

Подробнее про `source` и `player` смотрите ниже.

```
anicli_api
├── base.py - базовый класс модуля-парсера
├── _http.py - предварительно сконфигурированные классы httpx.Client и httpx.AsyncClient
├── _logger.py - логгер
├── player - модули получения ссылок на видео
│     ├── __template__.py - шаблон модуля PlayerExtractor
│     ├── ...  ready-made модули
│     ...
├── source - модули парсеров с источников
│     ├── parsers/... автоматически сгенерированные парсеры html страниц
│     ├── __template__.py - шаблон для экстрактора
│     ├─ ... ready-made парсеры
│     ...
└── tools - прочие модули

```

Схематичный принцип работы модуля из директории `source`

префикс `a_` обозначает асинхронный метод, возвращаемые объекты идентичны 

```mermaid
flowchart TD
    E[Extractor] -->|"search('QUERY') | a_search('QUERY')"| S("List[Search]")
    E -->|"ongoing() | a_ongoing()"| O("List[Ongoing]")
    
    O -->|"get_anime() | a_get_anime()"| A[Anime]
    S -->|"get_anime() | a_get_anime()"| A
    
    A -->|"get_episodes() | a_get_episodes()"|Ep["List[Episode]"]
    Ep -->|"get_sources() | a_get_sources()"|So["List[Source]"]
    So -->|"get_videos() | a_get_videos()"|V["List[Video]"]

```
# quickstart

```python
from anicli_api.source.animego import Extractor
from anicli_api.tools import cli

if __name__ == '__main__':
    cli(Extractor())
```

> Этот модуль реализован для простого ручного тестирования модулей и "имитирует" потенциальное настоящее приложение


Пример своей реализации

```python
from anicli_api.source.animego import Extractor  # can usage any source


def _print_to_rows(items):
    print(*[f"{i}) {r}" for i, r in enumerate(items)], sep="\n")


if __name__ == "__main__":
    ex = Extractor()
    print("PRESS CTRL + C for exit app")
    while True:
        results = ex.search(input("search query > "))
        if not results:
            print("Not founded, try again")
        continue
    _print_to_rows(results)

    anime = results[int(input("anime > "))].get_anime()
    print(anime)

    episodes = anime.get_episodes()
    _print_to_rows(episodes)
    episode = episodes[int(input("episode > "))]

    sources = episode.get_sources()
    _print_to_rows(sources)
    source = sources[int(input("source > "))]

    videos = source.get_videos()
    _print_to_rows(videos)
    video = videos[int(input("video > "))]
    print(video.type, video.quality, video.url, video.headers)
```

С asyncio аналогично, но **все** методы получения объектов имеют префикс `a_`:

```python
import asyncio
from anicli_api.source.animego import Extractor # или любой другой источник

async def main():
    ex = Extractor()
    prompt = input("search query > ")
    # a_ - async prefix.
    # simular in Ongoing, Anime, Episode, Source, Video objects
    results = await ex.a_search(prompt) 
    print(*[f"{i}) {r}" for i, r in enumerate(results)], sep="\n")
    
if __name__ == '__main__':
    asyncio.run(main())
```

# Player

Также, можно использовать отдельно экстракторы видео

> Эти модули минимально реализуют получение ссылок на видео с минимальными метаданными и заголовками для скачивания и 
> не стремятся стать заменой yt-dlp

```python
import asyncio

from anicli_api.player.sibnet import SibNet

async def main():
    videos = await SibNet().a_parse(URL)
    print(*videos)
    
    
if __name__ == '__main__':
    URL = 'https://video.sibnet.ru/shell.php?videoid=432356'
    print(*SibNet().parse(URL))
    # asyncio support!
    asyncio.run(main())
```

# source description

- name - имя модуля
- type - тип источника получения данных.
  - **NO** - неофициальный (парсинг html документов и запросы недокументированным API методам) 
  - **YES** - официальный (rest-api)
- note - примечания
- dubbers - тип озвучек. 
  - many - от различных авторов. 
  - subtitles - только субтитры. 
  - once - один вид (случайный)
  - author - своя

| name           | url                        | official api | dubbers           | note                                                                                                                                             |
|----------------|----------------------------|--------------|-------------------|--------------------------------------------------------------------------------------------------------------------------------------------------|
| animego        | https://animego.org        | NO           | many              | источники kodik, animego, не работает на IP отличных от СНГ                                                                                      |
| animania       | https://animania.online    | NO           | many              | источник kodik, не работает на IP отличных от СНГ                                                                                                |
| animejoy       | https://animejoy.ru        | NO           | subtitles         | **имеет cloudflare**, требуется реализация обхода или предварительное получения cookies и headers, много источников                              |
| sovetromantica | https://sovetromantica.com | NO           | subtitles, author | не на все тайтлы есть видео, у себя хостят                                                                                                       |
| anilibria      | https://anilibria.tv       | YES          | author            |                                                                                                                                                  |
| animevost      | https://animevost.org      | YES          | author            |                                                                                                                                                  |
| jutsu          | https://jut.su             | NO           | once              | Запуск видео в сторонних плеерах зависим от используемого user-agent заголовка в API интерфейсе. Некоторые тайтлы заблокированы на территории РФ |
| sameband       | https://jut.su             | NO           | author            |                                                                                                                                                  |


# players description

> Требует дополнения и дополнительные тесты

- name - имя плеера
- max quality - максимальное разрешение выдаваемое источником. Это может быть 0 (аудио, без видео), 144, 240, 360, 480, 720, 1080
- note - примечания

| name           | max quality                                                  | note                                                                    |
|----------------|--------------------------------------------------------------|-------------------------------------------------------------------------|
| kodik          | 720 (на старых тайтлах (ранние One Peace, Evangelion) - 480) | **работает только на IP СНГ**                                           |
| aniboom        | 1080                                                         | **работает только на IP СНГ**. Иногда не возвращает mpd ссылку на видео |
| sibnet         | 480                                                          |                                                                         |
| animejoy       | 1080                                                         | только актуальные ongoing, потом видео удаляются с серверов             |
| csst           | 1080                                                         |                                                                         |
| dzen           | 1080                                                         |                                                                         |
| mailru         |                                                              |                                                                         |
| okru           |                                                              |                                                                         |
| sovetromantica | 1080                                                         | не на все тайтлы присутствуют видео                                     |
| vkcom          | 1080 (какого качества автор зальет видео)                    | CDN сервера в РФ, в других странах загружается медленнее                |
| nuum           | 1080                                                         | проект от wasd.tv                                                       |
| anilibria      | 1080                                                         |                                                                         |
| jutsu          | 1080                                                         |                                                                         |
| sameband       | 1080                                                         |                                                                         |
  
## logging

Настройка логгера идет через ключ `anicli-api`

```python
import logging
logger = logging.getLogger('anicli-api')
```

## http path

### source

Если по какой-то либо причине вас не устраивают настройки по умолчанию - то вы можете задать
конфигурацию http клиентов для экстракторов. Или если необходимо подключить proxy

```python
from anicli_api.source.animego import Extractor
import httpx
# не обязательно настраивать все клиенты, зависит от режима использования
# например, если вы будете использовать только asyncio - настраивайте только http_async_client 
my_client = httpx.Client(headers={"user-agent": "007"}, proxies="http://127.0.0.1:8080")
my_async_client = httpx.AsyncClient(headers={"user-agent": "007"}, proxies="http://127.0.0.1:8080")

# настройки клиентов будут передаваться всем объектам кроме методов Source.get_videos() 
# и Source.a_get_videos()

ex = Extractor(http_client=my_client, http_async_client=my_async_client)

# изменение http клиента для объекта
results = ex.search("lain")
result = results[0]
result.http = my_client
result.http_async = my_async_client
...

```

### player

В player для модификации httpx клиентов (Client, AsyncioClient) необходимо передать kwargs аргументы:

```python
from anicli_api.source.animego import Extractor


sources = (
    Extractor()
    .search("lain")[0]
    .get_anime()
    .get_episodes()[0]
    .get_sources()
)

videos = sources[0].get_videos(transport=None,  # reset to default httpx.HTTPTransport
                               headers={"User-Agent": "i'm crushing :("})
```

## Структуры объектов

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

- Например, в `anilibria` и `animevost` поля почти идентичны ответам API. 
В `animego.Anime` есть сырой несериализованный `raw_json` для извлечения дополнительных метаданных.

- В некоторых источниках на полях могут присутствовать "заглушки" для поддержания консистентности API интерфейса. Например, 
- в модуле `anicli_api.source.jutsu.Episode` уже можно получить прямые ссылки на видео (Video объект),
но для поддержания полиморфизма, необходимо возвращать объект `Source` и только потом `Video`
- Если по какой-либо причине объекты не получены (ddos защита, региональные ограничения) - то возвращает пустой список.
  (#TODO возможно, необходимо выбрасывать исключение?)

### Search
- url: str - URL на тайтл
- title: str - имя найденного тайтла
- thumbnail: str - изображение

### Ongoing
- url: str - URL на тайтл
- title: str - имя найденного тайтла
- thumbnail: str - изображение

### Anime
- title: str - имя тайтла (на русском)
- thumbnail: str - изображение
- description: Optional[str] - описание тайтла

### Episode
- title: str - имя эпизода (Если источник его не хранит, то будет Серия или Serie)
- num: str - номер эпизода

### Source
- url: str - ссылка на источник
- title: str - даббер или имя источника

### Video

Объект `Video`, полученный из `Source.get_video` (или `Source.a_get_video`) 
имеет следующую структуру:

* type - тип видео (m3u8, mp4, mpd, audio)
* quality - разрешение видео (0, 144, 240, 360, 480, 720, 1080)
* url - прямая ссылка на видео
* headers - заголовки требуемые для получения видео. 
Если возвращает пустой словарь - заголовки не нужны

# Примечания
- Парсеры из директории 
[anicli_api/source/parsers](anicli_api/source/parsers) автоматически генерируются с помощью 
[ssc_gen](https://github.com/vypivshiy/selector_schema_codegen), 
настройки хранятся в [libanime_schema](https://github.com/libanime/libanime_schema)

- Для модификаций парсеров из директории `anicli_api/source/parsers`
используйте наследование, чтобы не потерять изменения при перегенерации библиотекой `ssc_gen`.

Пример из модуля [animejoy](anicli_api/source/animejoy.py):

```python
from anicli_api.source.parsers.animejoy_parser import PlayerUrlsView as PlayerUrlsViewOld
from parsel import Selector


class PlayerUrlsView(PlayerUrlsViewOld):
    def _parse_url(self, part: Selector) -> str:
        val_0 = part.attrib["data-file"]
        # maybe exclude https: prefix
        return f"https:{val_0}" if val_0.startswith("//") else val_0

```

- Проект разработан преимущественно на личное, некоммерческое использование с client-side 
стороны. 
Автор проекта не несет ответственности за поломки, убытки в высоко нагруженных проектах и решение
предоставляется "Как есть" в соответствии с [MIT](LIENSE) лицензией.

- Основная цель этого проекта — связать автоматизацию и эффективность извлечения того, 
что предоставляется пользователю в Интернете. 
Весь контент, доступный в рамках проекта, размещается на внешних неаффилированных источниках.

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

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "anicli_api",
    "maintainer": null,
    "docs_url": null,
    "requires_python": "<4.0,>=3.8",
    "maintainer_email": null,
    "keywords": "anime, api, ru, russia, asyncio, parser, httpx, dev",
    "author": "vypivshiy",
    "author_email": null,
    "download_url": "https://files.pythonhosted.org/packages/a2/9f/48be2345bfb5eb9e5e0a019805ef4210dfb90fd987b4553b8655ceffeeec/anicli_api-0.6.4.tar.gz",
    "platform": null,
    "description": "# anicli-api\n\n\u041f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u043d\u044b\u0439 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u043d\u0430\u0431\u043e\u0440\u0430 \u043f\u0430\u0440\u0441\u0435\u0440\u043e\u0432 \u0430\u043d\u0438\u043c\u0435 \u0441 \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0445 \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a\u043e\u0432.\n\n\u041f\u0440\u0438\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u0435\u0442 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 sync \u0438 async \u043c\u0435\u0442\u043e\u0434\u043e\u0432 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e `httpx` \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438.\n\n\u041f\u0430\u0440\u0441\u0435\u0440\u044b \u0440\u0430\u0431\u043e\u0442\u0430\u044e\u0442 \u043d\u0430 REST-API (\u0435\u0441\u043b\u0438 \u0443 \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a\u0430 \u0435\u0441\u0442\u044c \u0434\u043e\u0441\u0442\u0443\u043f) \u0438\u043b\u0438 \u0435\u0441\u043b\u0438 \u0442\u0430\u043a\u043e\u0439 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\n\u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u0435\u0442, \u0442\u043e \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e parsel, chompjs, jmespath, regex \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a. \n\n\n# install\n`pip install anicli-api`\n\n# Overview\n\u0421\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0430\n\n- source - \u043d\u0430\u0431\u043e\u0440\u044b \u043c\u043e\u0434\u0443\u043b\u0435\u0439 \u0434\u043b\u044f \u0438\u0437\u0432\u043b\u0435\u0447\u0435\u043d\u0438\u044f \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438 \u043e\u0431 \u0430\u043d\u0438\u043c\u0435 \u0442\u0430\u0439\u0442\u043b\u043e\u0432 \u0438\u0437 \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a\u043e\u0432\n- player - \u043d\u0430\u0431\u043e\u0440\u044b \u043c\u043e\u0434\u0443\u043b\u0435\u0439 \u0434\u043b\u044f \u0438\u0437\u0432\u043b\u0435\u0447\u0435\u043d\u0438\u044f \u043f\u0440\u044f\u043c\u043e\u0439 \u0441\u0441\u044b\u043b\u043a\u0438 \u043d\u0430 \u0432\u0438\u0434\u0435\u043e\n\n\u041f\u043e\u0434\u0440\u043e\u0431\u043d\u0435\u0435 \u043f\u0440\u043e `source` \u0438 `player` \u0441\u043c\u043e\u0442\u0440\u0438\u0442\u0435 \u043d\u0438\u0436\u0435.\n\n```\nanicli_api\n\u251c\u2500\u2500 base.py - \u0431\u0430\u0437\u043e\u0432\u044b\u0439 \u043a\u043b\u0430\u0441\u0441 \u043c\u043e\u0434\u0443\u043b\u044f-\u043f\u0430\u0440\u0441\u0435\u0440\u0430\n\u251c\u2500\u2500 _http.py - \u043f\u0440\u0435\u0434\u0432\u0430\u0440\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0441\u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u043a\u043b\u0430\u0441\u0441\u044b httpx.Client \u0438 httpx.AsyncClient\n\u251c\u2500\u2500 _logger.py - \u043b\u043e\u0433\u0433\u0435\u0440\n\u251c\u2500\u2500 player - \u043c\u043e\u0434\u0443\u043b\u0438 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0441\u0441\u044b\u043b\u043e\u043a \u043d\u0430 \u0432\u0438\u0434\u0435\u043e\n\u2502     \u251c\u2500\u2500 __template__.py - \u0448\u0430\u0431\u043b\u043e\u043d \u043c\u043e\u0434\u0443\u043b\u044f PlayerExtractor\n\u2502     \u251c\u2500\u2500 ...  ready-made \u043c\u043e\u0434\u0443\u043b\u0438\n\u2502     ...\n\u251c\u2500\u2500 source - \u043c\u043e\u0434\u0443\u043b\u0438 \u043f\u0430\u0440\u0441\u0435\u0440\u043e\u0432 \u0441 \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a\u043e\u0432\n\u2502     \u251c\u2500\u2500 parsers/... \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u043f\u0430\u0440\u0441\u0435\u0440\u044b html \u0441\u0442\u0440\u0430\u043d\u0438\u0446\n\u2502     \u251c\u2500\u2500 __template__.py - \u0448\u0430\u0431\u043b\u043e\u043d \u0434\u043b\u044f \u044d\u043a\u0441\u0442\u0440\u0430\u043a\u0442\u043e\u0440\u0430\n\u2502     \u251c\u2500 ... ready-made \u043f\u0430\u0440\u0441\u0435\u0440\u044b\n\u2502     ...\n\u2514\u2500\u2500 tools - \u043f\u0440\u043e\u0447\u0438\u0435 \u043c\u043e\u0434\u0443\u043b\u0438\n\n```\n\n\u0421\u0445\u0435\u043c\u0430\u0442\u0438\u0447\u043d\u044b\u0439 \u043f\u0440\u0438\u043d\u0446\u0438\u043f \u0440\u0430\u0431\u043e\u0442\u044b \u043c\u043e\u0434\u0443\u043b\u044f \u0438\u0437 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438 `source`\n\n\u043f\u0440\u0435\u0444\u0438\u043a\u0441 `a_` \u043e\u0431\u043e\u0437\u043d\u0430\u0447\u0430\u0435\u0442 \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0439 \u043c\u0435\u0442\u043e\u0434, \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c\u044b\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u044b \u0438\u0434\u0435\u043d\u0442\u0438\u0447\u043d\u044b \n\n```mermaid\nflowchart TD\n    E[Extractor] -->|\"search('QUERY') | a_search('QUERY')\"| S(\"List[Search]\")\n    E -->|\"ongoing() | a_ongoing()\"| O(\"List[Ongoing]\")\n    \n    O -->|\"get_anime() | a_get_anime()\"| A[Anime]\n    S -->|\"get_anime() | a_get_anime()\"| A\n    \n    A -->|\"get_episodes() | a_get_episodes()\"|Ep[\"List[Episode]\"]\n    Ep -->|\"get_sources() | a_get_sources()\"|So[\"List[Source]\"]\n    So -->|\"get_videos() | a_get_videos()\"|V[\"List[Video]\"]\n\n```\n# quickstart\n\n```python\nfrom anicli_api.source.animego import Extractor\nfrom anicli_api.tools import cli\n\nif __name__ == '__main__':\n    cli(Extractor())\n```\n\n> \u042d\u0442\u043e\u0442 \u043c\u043e\u0434\u0443\u043b\u044c \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d \u0434\u043b\u044f \u043f\u0440\u043e\u0441\u0442\u043e\u0433\u043e \u0440\u0443\u0447\u043d\u043e\u0433\u043e \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043c\u043e\u0434\u0443\u043b\u0435\u0439 \u0438 \"\u0438\u043c\u0438\u0442\u0438\u0440\u0443\u0435\u0442\" \u043f\u043e\u0442\u0435\u043d\u0446\u0438\u0430\u043b\u044c\u043d\u043e\u0435 \u043d\u0430\u0441\u0442\u043e\u044f\u0449\u0435\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435\n\n\n\u041f\u0440\u0438\u043c\u0435\u0440 \u0441\u0432\u043e\u0435\u0439 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438\n\n```python\nfrom anicli_api.source.animego import Extractor  # can usage any source\n\n\ndef _print_to_rows(items):\n    print(*[f\"{i}) {r}\" for i, r in enumerate(items)], sep=\"\\n\")\n\n\nif __name__ == \"__main__\":\n    ex = Extractor()\n    print(\"PRESS CTRL + C for exit app\")\n    while True:\n        results = ex.search(input(\"search query > \"))\n        if not results:\n            print(\"Not founded, try again\")\n        continue\n    _print_to_rows(results)\n\n    anime = results[int(input(\"anime > \"))].get_anime()\n    print(anime)\n\n    episodes = anime.get_episodes()\n    _print_to_rows(episodes)\n    episode = episodes[int(input(\"episode > \"))]\n\n    sources = episode.get_sources()\n    _print_to_rows(sources)\n    source = sources[int(input(\"source > \"))]\n\n    videos = source.get_videos()\n    _print_to_rows(videos)\n    video = videos[int(input(\"video > \"))]\n    print(video.type, video.quality, video.url, video.headers)\n```\n\n\u0421 asyncio \u0430\u043d\u0430\u043b\u043e\u0433\u0438\u0447\u043d\u043e, \u043d\u043e **\u0432\u0441\u0435** \u043c\u0435\u0442\u043e\u0434\u044b \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432 \u0438\u043c\u0435\u044e\u0442 \u043f\u0440\u0435\u0444\u0438\u043a\u0441 `a_`:\n\n```python\nimport asyncio\nfrom anicli_api.source.animego import Extractor # \u0438\u043b\u0438 \u043b\u044e\u0431\u043e\u0439 \u0434\u0440\u0443\u0433\u043e\u0439 \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a\n\nasync def main():\n    ex = Extractor()\n    prompt = input(\"search query > \")\n    # a_ - async prefix.\n    # simular in Ongoing, Anime, Episode, Source, Video objects\n    results = await ex.a_search(prompt) \n    print(*[f\"{i}) {r}\" for i, r in enumerate(results)], sep=\"\\n\")\n    \nif __name__ == '__main__':\n    asyncio.run(main())\n```\n\n# Player\n\n\u0422\u0430\u043a\u0436\u0435, \u043c\u043e\u0436\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e \u044d\u043a\u0441\u0442\u0440\u0430\u043a\u0442\u043e\u0440\u044b \u0432\u0438\u0434\u0435\u043e\n\n> \u042d\u0442\u0438 \u043c\u043e\u0434\u0443\u043b\u0438 \u043c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u044e\u0442 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0441\u0441\u044b\u043b\u043e\u043a \u043d\u0430 \u0432\u0438\u0434\u0435\u043e \u0441 \u043c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u044b\u043c\u0438 \u043c\u0435\u0442\u0430\u0434\u0430\u043d\u043d\u044b\u043c\u0438 \u0438 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0430\u043c\u0438 \u0434\u043b\u044f \u0441\u043a\u0430\u0447\u0438\u0432\u0430\u043d\u0438\u044f \u0438 \n> \u043d\u0435 \u0441\u0442\u0440\u0435\u043c\u044f\u0442\u0441\u044f \u0441\u0442\u0430\u0442\u044c \u0437\u0430\u043c\u0435\u043d\u043e\u0439 yt-dlp\n\n```python\nimport asyncio\n\nfrom anicli_api.player.sibnet import SibNet\n\nasync def main():\n    videos = await SibNet().a_parse(URL)\n    print(*videos)\n    \n    \nif __name__ == '__main__':\n    URL = 'https://video.sibnet.ru/shell.php?videoid=432356'\n    print(*SibNet().parse(URL))\n    # asyncio support!\n    asyncio.run(main())\n```\n\n# source description\n\n- name - \u0438\u043c\u044f \u043c\u043e\u0434\u0443\u043b\u044f\n- type - \u0442\u0438\u043f \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a\u0430 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0434\u0430\u043d\u043d\u044b\u0445.\n  - **NO** - \u043d\u0435\u043e\u0444\u0438\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u0439 (\u043f\u0430\u0440\u0441\u0438\u043d\u0433 html \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u043e\u0432 \u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u043d\u0435\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u043c API \u043c\u0435\u0442\u043e\u0434\u0430\u043c) \n  - **YES** - \u043e\u0444\u0438\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u0439 (rest-api)\n- note - \u043f\u0440\u0438\u043c\u0435\u0447\u0430\u043d\u0438\u044f\n- dubbers - \u0442\u0438\u043f \u043e\u0437\u0432\u0443\u0447\u0435\u043a. \n  - many - \u043e\u0442 \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0445 \u0430\u0432\u0442\u043e\u0440\u043e\u0432. \n  - subtitles - \u0442\u043e\u043b\u044c\u043a\u043e \u0441\u0443\u0431\u0442\u0438\u0442\u0440\u044b. \n  - once - \u043e\u0434\u0438\u043d \u0432\u0438\u0434 (\u0441\u043b\u0443\u0447\u0430\u0439\u043d\u044b\u0439)\n  - author - \u0441\u0432\u043e\u044f\n\n| name           | url                        | official api | dubbers           | note                                                                                                                                             |\n|----------------|----------------------------|--------------|-------------------|--------------------------------------------------------------------------------------------------------------------------------------------------|\n| animego        | https://animego.org        | NO           | many              | \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a\u0438 kodik, animego, \u043d\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u043d\u0430 IP \u043e\u0442\u043b\u0438\u0447\u043d\u044b\u0445 \u043e\u0442 \u0421\u041d\u0413                                                                                      |\n| animania       | https://animania.online    | NO           | many              | \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a kodik, \u043d\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u043d\u0430 IP \u043e\u0442\u043b\u0438\u0447\u043d\u044b\u0445 \u043e\u0442 \u0421\u041d\u0413                                                                                                |\n| animejoy       | https://animejoy.ru        | NO           | subtitles         | **\u0438\u043c\u0435\u0435\u0442 cloudflare**, \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u043e\u0431\u0445\u043e\u0434\u0430 \u0438\u043b\u0438 \u043f\u0440\u0435\u0434\u0432\u0430\u0440\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0435 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f cookies \u0438 headers, \u043c\u043d\u043e\u0433\u043e \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a\u043e\u0432                              |\n| sovetromantica | https://sovetromantica.com | NO           | subtitles, author | \u043d\u0435 \u043d\u0430 \u0432\u0441\u0435 \u0442\u0430\u0439\u0442\u043b\u044b \u0435\u0441\u0442\u044c \u0432\u0438\u0434\u0435\u043e, \u0443 \u0441\u0435\u0431\u044f \u0445\u043e\u0441\u0442\u044f\u0442                                                                                                       |\n| anilibria      | https://anilibria.tv       | YES          | author            |                                                                                                                                                  |\n| animevost      | https://animevost.org      | YES          | author            |                                                                                                                                                  |\n| jutsu          | https://jut.su             | NO           | once              | \u0417\u0430\u043f\u0443\u0441\u043a \u0432\u0438\u0434\u0435\u043e \u0432 \u0441\u0442\u043e\u0440\u043e\u043d\u043d\u0438\u0445 \u043f\u043b\u0435\u0435\u0440\u0430\u0445 \u0437\u0430\u0432\u0438\u0441\u0438\u043c \u043e\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u043e\u0433\u043e user-agent \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0430 \u0432 API \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0435. \u041d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0442\u0430\u0439\u0442\u043b\u044b \u0437\u0430\u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u0430\u043d\u044b \u043d\u0430 \u0442\u0435\u0440\u0440\u0438\u0442\u043e\u0440\u0438\u0438 \u0420\u0424 |\n| sameband       | https://jut.su             | NO           | author            |                                                                                                                                                  |\n\n\n# players description\n\n> \u0422\u0440\u0435\u0431\u0443\u0435\u0442 \u0434\u043e\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u0438 \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u0442\u0435\u0441\u0442\u044b\n\n- name - \u0438\u043c\u044f \u043f\u043b\u0435\u0435\u0440\u0430\n- max quality - \u043c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u043e\u0435 \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u0432\u044b\u0434\u0430\u0432\u0430\u0435\u043c\u043e\u0435 \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a\u043e\u043c. \u042d\u0442\u043e \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c 0 (\u0430\u0443\u0434\u0438\u043e, \u0431\u0435\u0437 \u0432\u0438\u0434\u0435\u043e), 144, 240, 360, 480, 720, 1080\n- note - \u043f\u0440\u0438\u043c\u0435\u0447\u0430\u043d\u0438\u044f\n\n| name           | max quality                                                  | note                                                                    |\n|----------------|--------------------------------------------------------------|-------------------------------------------------------------------------|\n| kodik          | 720 (\u043d\u0430 \u0441\u0442\u0430\u0440\u044b\u0445 \u0442\u0430\u0439\u0442\u043b\u0430\u0445 (\u0440\u0430\u043d\u043d\u0438\u0435 One Peace, Evangelion) - 480) | **\u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0442\u043e\u043b\u044c\u043a\u043e \u043d\u0430 IP \u0421\u041d\u0413**                                           |\n| aniboom        | 1080                                                         | **\u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0442\u043e\u043b\u044c\u043a\u043e \u043d\u0430 IP \u0421\u041d\u0413**. \u0418\u043d\u043e\u0433\u0434\u0430 \u043d\u0435 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 mpd \u0441\u0441\u044b\u043b\u043a\u0443 \u043d\u0430 \u0432\u0438\u0434\u0435\u043e |\n| sibnet         | 480                                                          |                                                                         |\n| animejoy       | 1080                                                         | \u0442\u043e\u043b\u044c\u043a\u043e \u0430\u043a\u0442\u0443\u0430\u043b\u044c\u043d\u044b\u0435 ongoing, \u043f\u043e\u0442\u043e\u043c \u0432\u0438\u0434\u0435\u043e \u0443\u0434\u0430\u043b\u044f\u044e\u0442\u0441\u044f \u0441 \u0441\u0435\u0440\u0432\u0435\u0440\u043e\u0432             |\n| csst           | 1080                                                         |                                                                         |\n| dzen           | 1080                                                         |                                                                         |\n| mailru         |                                                              |                                                                         |\n| okru           |                                                              |                                                                         |\n| sovetromantica | 1080                                                         | \u043d\u0435 \u043d\u0430 \u0432\u0441\u0435 \u0442\u0430\u0439\u0442\u043b\u044b \u043f\u0440\u0438\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u044e\u0442 \u0432\u0438\u0434\u0435\u043e                                     |\n| vkcom          | 1080 (\u043a\u0430\u043a\u043e\u0433\u043e \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0430 \u0430\u0432\u0442\u043e\u0440 \u0437\u0430\u043b\u044c\u0435\u0442 \u0432\u0438\u0434\u0435\u043e)                    | CDN \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u0432 \u0420\u0424, \u0432 \u0434\u0440\u0443\u0433\u0438\u0445 \u0441\u0442\u0440\u0430\u043d\u0430\u0445 \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0435\u0442\u0441\u044f \u043c\u0435\u0434\u043b\u0435\u043d\u043d\u0435\u0435                |\n| nuum           | 1080                                                         | \u043f\u0440\u043e\u0435\u043a\u0442 \u043e\u0442 wasd.tv                                                       |\n| anilibria      | 1080                                                         |                                                                         |\n| jutsu          | 1080                                                         |                                                                         |\n| sameband       | 1080                                                         |                                                                         |\n  \n## logging\n\n\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u043b\u043e\u0433\u0433\u0435\u0440\u0430 \u0438\u0434\u0435\u0442 \u0447\u0435\u0440\u0435\u0437 \u043a\u043b\u044e\u0447 `anicli-api`\n\n```python\nimport logging\nlogger = logging.getLogger('anicli-api')\n```\n\n## http path\n\n### source\n\n\u0415\u0441\u043b\u0438 \u043f\u043e \u043a\u0430\u043a\u043e\u0439-\u0442\u043e \u043b\u0438\u0431\u043e \u043f\u0440\u0438\u0447\u0438\u043d\u0435 \u0432\u0430\u0441 \u043d\u0435 \u0443\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u044e\u0442 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e - \u0442\u043e \u0432\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0437\u0430\u0434\u0430\u0442\u044c\n\u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e http \u043a\u043b\u0438\u0435\u043d\u0442\u043e\u0432 \u0434\u043b\u044f \u044d\u043a\u0441\u0442\u0440\u0430\u043a\u0442\u043e\u0440\u043e\u0432. \u0418\u043b\u0438 \u0435\u0441\u043b\u0438 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c proxy\n\n```python\nfrom anicli_api.source.animego import Extractor\nimport httpx\n# \u043d\u0435 \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u043d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0442\u044c \u0432\u0441\u0435 \u043a\u043b\u0438\u0435\u043d\u0442\u044b, \u0437\u0430\u0432\u0438\u0441\u0438\u0442 \u043e\u0442 \u0440\u0435\u0436\u0438\u043c\u0430 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f\n# \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0435\u0441\u043b\u0438 \u0432\u044b \u0431\u0443\u0434\u0435\u0442\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e asyncio - \u043d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0439\u0442\u0435 \u0442\u043e\u043b\u044c\u043a\u043e http_async_client \nmy_client = httpx.Client(headers={\"user-agent\": \"007\"}, proxies=\"http://127.0.0.1:8080\")\nmy_async_client = httpx.AsyncClient(headers={\"user-agent\": \"007\"}, proxies=\"http://127.0.0.1:8080\")\n\n# \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u043a\u043b\u0438\u0435\u043d\u0442\u043e\u0432 \u0431\u0443\u0434\u0443\u0442 \u043f\u0435\u0440\u0435\u0434\u0430\u0432\u0430\u0442\u044c\u0441\u044f \u0432\u0441\u0435\u043c \u043e\u0431\u044a\u0435\u043a\u0442\u0430\u043c \u043a\u0440\u043e\u043c\u0435 \u043c\u0435\u0442\u043e\u0434\u043e\u0432 Source.get_videos() \n# \u0438 Source.a_get_videos()\n\nex = Extractor(http_client=my_client, http_async_client=my_async_client)\n\n# \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0435 http \u043a\u043b\u0438\u0435\u043d\u0442\u0430 \u0434\u043b\u044f \u043e\u0431\u044a\u0435\u043a\u0442\u0430\nresults = ex.search(\"lain\")\nresult = results[0]\nresult.http = my_client\nresult.http_async = my_async_client\n...\n\n```\n\n### player\n\n\u0412 player \u0434\u043b\u044f \u043c\u043e\u0434\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 httpx \u043a\u043b\u0438\u0435\u043d\u0442\u043e\u0432 (Client, AsyncioClient) \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u043f\u0435\u0440\u0435\u0434\u0430\u0442\u044c kwargs \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u044b:\n\n```python\nfrom anicli_api.source.animego import Extractor\n\n\nsources = (\n    Extractor()\n    .search(\"lain\")[0]\n    .get_anime()\n    .get_episodes()[0]\n    .get_sources()\n)\n\nvideos = sources[0].get_videos(transport=None,  # reset to default httpx.HTTPTransport\n                               headers={\"User-Agent\": \"i'm crushing :(\"})\n```\n\n## \u0421\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u044b \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432\n\n\u041f\u0440\u0438\u0432\u0435\u0434\u0435\u043d\u044b \u043f\u043e\u043b\u044f, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 **\u0433\u0430\u0440\u0430\u043d\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e** \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u044e\u0442\u0441\u044f \u0432 API \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0435 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438. \n\u0412 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a\u0430\u0445 \u043c\u043e\u0433\u0443\u0442 \u043f\u0440\u0438\u0441\u0443\u0442\u0441\u0442\u0432\u043e\u0432\u0430\u0442\u044c \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u043f\u043e\u043b\u044f \u0438\u043b\u0438 \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u044b \u0434\u043b\u044f \n\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u0432\u043e \u0432\u043d\u0443\u0442\u0440\u0435\u043d\u043d\u0438\u0445 \u043c\u0435\u0442\u043e\u0434\u0430\u0445.\n\n- \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0432 `anilibria` \u0438 `animevost` \u043f\u043e\u043b\u044f \u043f\u043e\u0447\u0442\u0438 \u0438\u0434\u0435\u043d\u0442\u0438\u0447\u043d\u044b \u043e\u0442\u0432\u0435\u0442\u0430\u043c API. \n\u0412 `animego.Anime` \u0435\u0441\u0442\u044c \u0441\u044b\u0440\u043e\u0439 \u043d\u0435\u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u043d\u044b\u0439 `raw_json` \u0434\u043b\u044f \u0438\u0437\u0432\u043b\u0435\u0447\u0435\u043d\u0438\u044f \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0445 \u043c\u0435\u0442\u0430\u0434\u0430\u043d\u043d\u044b\u0445.\n\n- \u0412 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a\u0430\u0445 \u043d\u0430 \u043f\u043e\u043b\u044f\u0445 \u043c\u043e\u0433\u0443\u0442 \u043f\u0440\u0438\u0441\u0443\u0442\u0441\u0442\u0432\u043e\u0432\u0430\u0442\u044c \"\u0437\u0430\u0433\u043b\u0443\u0448\u043a\u0438\" \u0434\u043b\u044f \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0430\u043d\u0438\u044f \u043a\u043e\u043d\u0441\u0438\u0441\u0442\u0435\u043d\u0442\u043d\u043e\u0441\u0442\u0438 API \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \n- \u0432 \u043c\u043e\u0434\u0443\u043b\u0435 `anicli_api.source.jutsu.Episode` \u0443\u0436\u0435 \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u043f\u0440\u044f\u043c\u044b\u0435 \u0441\u0441\u044b\u043b\u043a\u0438 \u043d\u0430 \u0432\u0438\u0434\u0435\u043e (Video \u043e\u0431\u044a\u0435\u043a\u0442),\n\u043d\u043e \u0434\u043b\u044f \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0430\u043d\u0438\u044f \u043f\u043e\u043b\u0438\u043c\u043e\u0440\u0444\u0438\u0437\u043c\u0430, \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0442\u044c \u043e\u0431\u044a\u0435\u043a\u0442 `Source` \u0438 \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u043e\u0442\u043e\u043c `Video`\n- \u0415\u0441\u043b\u0438 \u043f\u043e \u043a\u0430\u043a\u043e\u0439-\u043b\u0438\u0431\u043e \u043f\u0440\u0438\u0447\u0438\u043d\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u044b \u043d\u0435 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u044b (ddos \u0437\u0430\u0449\u0438\u0442\u0430, \u0440\u0435\u0433\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u044b\u0435 \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u044f) - \u0442\u043e \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u043f\u0443\u0441\u0442\u043e\u0439 \u0441\u043f\u0438\u0441\u043e\u043a.\n  (#TODO \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e, \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0432\u044b\u0431\u0440\u0430\u0441\u044b\u0432\u0430\u0442\u044c \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435?)\n\n### Search\n- url: str - URL \u043d\u0430 \u0442\u0430\u0439\u0442\u043b\n- title: str - \u0438\u043c\u044f \u043d\u0430\u0439\u0434\u0435\u043d\u043d\u043e\u0433\u043e \u0442\u0430\u0439\u0442\u043b\u0430\n- thumbnail: str - \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435\n\n### Ongoing\n- url: str - URL \u043d\u0430 \u0442\u0430\u0439\u0442\u043b\n- title: str - \u0438\u043c\u044f \u043d\u0430\u0439\u0434\u0435\u043d\u043d\u043e\u0433\u043e \u0442\u0430\u0439\u0442\u043b\u0430\n- thumbnail: str - \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435\n\n### Anime\n- title: str - \u0438\u043c\u044f \u0442\u0430\u0439\u0442\u043b\u0430 (\u043d\u0430 \u0440\u0443\u0441\u0441\u043a\u043e\u043c)\n- thumbnail: str - \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435\n- description: Optional[str] - \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u0442\u0430\u0439\u0442\u043b\u0430\n\n### Episode\n- title: str - \u0438\u043c\u044f \u044d\u043f\u0438\u0437\u043e\u0434\u0430 (\u0415\u0441\u043b\u0438 \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a \u0435\u0433\u043e \u043d\u0435 \u0445\u0440\u0430\u043d\u0438\u0442, \u0442\u043e \u0431\u0443\u0434\u0435\u0442 \u0421\u0435\u0440\u0438\u044f \u0438\u043b\u0438 Serie)\n- num: str - \u043d\u043e\u043c\u0435\u0440 \u044d\u043f\u0438\u0437\u043e\u0434\u0430\n\n### Source\n- url: str - \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a\n- title: str - \u0434\u0430\u0431\u0431\u0435\u0440 \u0438\u043b\u0438 \u0438\u043c\u044f \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a\u0430\n\n### Video\n\n\u041e\u0431\u044a\u0435\u043a\u0442 `Video`, \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u044b\u0439 \u0438\u0437 `Source.get_video` (\u0438\u043b\u0438 `Source.a_get_video`) \n\u0438\u043c\u0435\u0435\u0442 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0443\u044e \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443:\n\n* type - \u0442\u0438\u043f \u0432\u0438\u0434\u0435\u043e (m3u8, mp4, mpd, audio)\n* quality - \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u0432\u0438\u0434\u0435\u043e (0, 144, 240, 360, 480, 720, 1080)\n* url - \u043f\u0440\u044f\u043c\u0430\u044f \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u0432\u0438\u0434\u0435\u043e\n* headers - \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0438 \u0442\u0440\u0435\u0431\u0443\u0435\u043c\u044b\u0435 \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0432\u0438\u0434\u0435\u043e. \n\u0415\u0441\u043b\u0438 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u043f\u0443\u0441\u0442\u043e\u0439 \u0441\u043b\u043e\u0432\u0430\u0440\u044c - \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0438 \u043d\u0435 \u043d\u0443\u0436\u043d\u044b\n\n# \u041f\u0440\u0438\u043c\u0435\u0447\u0430\u043d\u0438\u044f\n- \u041f\u0430\u0440\u0441\u0435\u0440\u044b \u0438\u0437 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438 \n[anicli_api/source/parsers](anicli_api/source/parsers) \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u044e\u0442\u0441\u044f \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \n[ssc_gen](https://github.com/vypivshiy/selector_schema_codegen), \n\u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0445\u0440\u0430\u043d\u044f\u0442\u0441\u044f \u0432 [libanime_schema](https://github.com/libanime/libanime_schema)\n\n- \u0414\u043b\u044f \u043c\u043e\u0434\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0439 \u043f\u0430\u0440\u0441\u0435\u0440\u043e\u0432 \u0438\u0437 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438 `anicli_api/source/parsers`\n\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 \u043d\u0430\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u043d\u0438\u0435, \u0447\u0442\u043e\u0431\u044b \u043d\u0435 \u043f\u043e\u0442\u0435\u0440\u044f\u0442\u044c \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u043f\u0440\u0438 \u043f\u0435\u0440\u0435\u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0438 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u043e\u0439 `ssc_gen`.\n\n\u041f\u0440\u0438\u043c\u0435\u0440 \u0438\u0437 \u043c\u043e\u0434\u0443\u043b\u044f [animejoy](anicli_api/source/animejoy.py):\n\n```python\nfrom anicli_api.source.parsers.animejoy_parser import PlayerUrlsView as PlayerUrlsViewOld\nfrom parsel import Selector\n\n\nclass PlayerUrlsView(PlayerUrlsViewOld):\n    def _parse_url(self, part: Selector) -> str:\n        val_0 = part.attrib[\"data-file\"]\n        # maybe exclude https: prefix\n        return f\"https:{val_0}\" if val_0.startswith(\"//\") else val_0\n\n```\n\n- \u041f\u0440\u043e\u0435\u043a\u0442 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0430\u043d \u043f\u0440\u0435\u0438\u043c\u0443\u0449\u0435\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u043d\u0430 \u043b\u0438\u0447\u043d\u043e\u0435, \u043d\u0435\u043a\u043e\u043c\u043c\u0435\u0440\u0447\u0435\u0441\u043a\u043e\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 \u0441 client-side \n\u0441\u0442\u043e\u0440\u043e\u043d\u044b. \n\u0410\u0432\u0442\u043e\u0440 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u043d\u0435 \u043d\u0435\u0441\u0435\u0442 \u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0441\u0442\u0438 \u0437\u0430 \u043f\u043e\u043b\u043e\u043c\u043a\u0438, \u0443\u0431\u044b\u0442\u043a\u0438 \u0432 \u0432\u044b\u0441\u043e\u043a\u043e \u043d\u0430\u0433\u0440\u0443\u0436\u0435\u043d\u043d\u044b\u0445 \u043f\u0440\u043e\u0435\u043a\u0442\u0430\u0445 \u0438 \u0440\u0435\u0448\u0435\u043d\u0438\u0435\n\u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f \"\u041a\u0430\u043a \u0435\u0441\u0442\u044c\" \u0432 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0438\u0438 \u0441 [MIT](LIENSE) \u043b\u0438\u0446\u0435\u043d\u0437\u0438\u0435\u0439.\n\n- \u041e\u0441\u043d\u043e\u0432\u043d\u0430\u044f \u0446\u0435\u043b\u044c \u044d\u0442\u043e\u0433\u043e \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u2014 \u0441\u0432\u044f\u0437\u0430\u0442\u044c \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0430\u0446\u0438\u044e \u0438 \u044d\u0444\u0444\u0435\u043a\u0442\u0438\u0432\u043d\u043e\u0441\u0442\u044c \u0438\u0437\u0432\u043b\u0435\u0447\u0435\u043d\u0438\u044f \u0442\u043e\u0433\u043e, \n\u0447\u0442\u043e \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044e \u0432 \u0418\u043d\u0442\u0435\u0440\u043d\u0435\u0442\u0435. \n\u0412\u0435\u0441\u044c \u043a\u043e\u043d\u0442\u0435\u043d\u0442, \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b\u0439 \u0432 \u0440\u0430\u043c\u043a\u0430\u0445 \u043f\u0440\u043e\u0435\u043a\u0442\u0430, \u0440\u0430\u0437\u043c\u0435\u0449\u0430\u0435\u0442\u0441\u044f \u043d\u0430 \u0432\u043d\u0435\u0448\u043d\u0438\u0445 \u043d\u0435\u0430\u0444\u0444\u0438\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0445 \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a\u0430\u0445.\n\n- **\u042d\u0442\u043e\u0442 \u043f\u0440\u043e\u0435\u043a\u0442 \u043d\u0435 \u0432\u043a\u043b\u044e\u0447\u0430\u0435\u0442 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u044b \u043a\u0435\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0438 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0432\u0441\u0435\u0445 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445, \n\u0442\u043e\u043b\u044c\u043a\u043e \u0433\u043e\u0442\u043e\u0432\u044b\u0435 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u043f\u0430\u0440\u0441\u0435\u0440\u043e\u0432 \u0438 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u043d\u044b\u0435 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u044b**\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "Anime extractors api implementation",
    "version": "0.6.4",
    "project_urls": {
        "Bug Tracker": "https://github.com/vypivshiy/anicli-api/issues",
        "Cli app": "https://github.com/vypivshiy/ani-cli-ru"
    },
    "split_keywords": [
        "anime",
        " api",
        " ru",
        " russia",
        " asyncio",
        " parser",
        " httpx",
        " dev"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "3262064da04656b847d1dc1c80a06e9b47bce40c75a52949fe45197c84dbc227",
                "md5": "cfa70e556782c68f4d5fe5e8df804d90",
                "sha256": "5a0e18ad2fa702575a7367167a82043e6a1fe0c27c65bd6d0762137f09ccb781"
            },
            "downloads": -1,
            "filename": "anicli_api-0.6.4-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "cfa70e556782c68f4d5fe5e8df804d90",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": "<4.0,>=3.8",
            "size": 55148,
            "upload_time": "2024-03-27T12:34:49",
            "upload_time_iso_8601": "2024-03-27T12:34:49.691845Z",
            "url": "https://files.pythonhosted.org/packages/32/62/064da04656b847d1dc1c80a06e9b47bce40c75a52949fe45197c84dbc227/anicli_api-0.6.4-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "a29f48be2345bfb5eb9e5e0a019805ef4210dfb90fd987b4553b8655ceffeeec",
                "md5": "72a19a2c4756d200699f86af48c650c7",
                "sha256": "633ebf4751711e590847dd4527f2d9d22a91b3d4e6b9c290abb4bd39162dfa80"
            },
            "downloads": -1,
            "filename": "anicli_api-0.6.4.tar.gz",
            "has_sig": false,
            "md5_digest": "72a19a2c4756d200699f86af48c650c7",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": "<4.0,>=3.8",
            "size": 41519,
            "upload_time": "2024-03-27T12:34:51",
            "upload_time_iso_8601": "2024-03-27T12:34:51.554792Z",
            "url": "https://files.pythonhosted.org/packages/a2/9f/48be2345bfb5eb9e5e0a019805ef4210dfb90fd987b4553b8655ceffeeec/anicli_api-0.6.4.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-03-27 12:34:51",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "vypivshiy",
    "github_project": "anicli-api",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": false,
    "lcname": "anicli_api"
}
        
Elapsed time: 0.21022s