# anicli-api
Программный интерфейс набора парсеров аниме с различных источников.
Присутствует поддержка sync и async методов с помощью `httpx` библиотеки
Парсеры работают на REST-API (если у источника есть доступ), parsel и обёртки scrape-schema
# install
`pip install anicli-api`
# Overview
Структура проекта
```
anicli_api
├── base.py - базовый класс модуля-парсера
├── _http.py - сконфигурированные классы httpx
├── _logger.py - логгер
├── player - модули получения ссылок на видео
│ ├── __template__.py - шаблон модуля PlayerExtractor
│ ├── ... ready-made модули
│ ...
└── source - модули парсеров с источников
├── parsers/... автоматически сгенерированные парсеры html страниц
├── __template__.py - шаблон для экстрактора
├─ ... ready-made парсеры
...
```
Схематичный принцип работы парсеров:
![](high-level-schema.png)
# source description
- name - имя модуля
- type - тип источника получения данных.
- **NO** - неофициальный (парсинг html страниц)
- **YES** - официальный (rest-api)
- note - примечания
- dubbers - тип озвучек. many - от различных авторов. subtitles - только субтитры. 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** (может периодически не работать), много источников |
| sovetromantica | https://sovetromantica.com | NO | subtitles, author | не на все тайтлы есть видео, у себя хостят |
| anilibria | https://anilibria.tv | YES | author | |
| animevost | https://animevost.org | YES | author | |
# players description
Требует дополнения и дополнительных тестов
- name - имя плеера
- max quality - максимальное разрешение выдаваемое источником. Это может быть 0 (аудио, без видео), 144, 240, 360, 480, 720, 1080
- note - примечания
| name | max quality | note |
|----------------|-------------------------------------------|----------------------------------------------------------------|
| kodik | 720 (на старых тайтлах - 480) | **работает только на IP СНГ** |
| aniboom | 1080 | **работает только на IP СНГ** |
| sibnet | 480 | |
| animejoy | 1080 | только актуальные ongoing, потом видео удаляются с их серверов |
| csst | 1080 | |
| dzen | 1080 | |
| mailru | | |
| okru | | |
| sovetromantica | 1080 | |
| vkcom | 1080 (какого качества автор зальет видео) | CDN сервера в РФ, в других странах загружается медленнее |
# Quickstart example
```python
from anicli_api.source.animego import Extractor # can usage any source
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(*[f"{i}) {r}" for i, r in enumerate(results)], sep="\n")
anime = results[int(input("anime > "))].get_anime()
episodes = anime.get_episodes()
print(*[f"{i}) {ep}" for i, ep in enumerate(episodes)], sep="\n")
episode = episodes[int(input("episode > "))]
sources = episode.get_sources()
print(*[f"{i}) {source}" for i, source in enumerate(sources)], sep="\n")
source = sources[int(input("source > "))]
videos = source.get_videos()
print(*[f"{i} {video}" for i, video in enumerate(videos)], sep="\n")
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())
```
## http path
### source
Если по какой-то либо причине вас не устраивают настройки по умолчанию - то вы можете задать
конфигурацию http клиентов экстракторов
```python
from anicli_api.base import HTTPSync
from httpx import HTTPTransport
client = HTTPSync()
print(client._transport) # anicli_api._http.HTTPRetryConnectSyncTransport
print(client.headers) # {'user-agent': 'Mozilla/5.0 (Linux; Android 6.0...', ...}
client_2 = HTTPSync(transport=HTTPTransport(), # default transport
headers={"user-agent": "My Cool useragent"}
)
print(client_2._transport) # httpx.HTTPTransport
print(client_2.headers) # {'user-agent': 'My Cool useragent', ...}
assert client_2 == client # Passed
```
### 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` для извлечения дополнительных данных.
### 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 - имя эпизода
- 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` - автоматически сгенерированные парсеры с помощью
[scrape-schema-codegen](https://github.com/vypivshiy/selector_schema_codegen) и файлов-конфигурации [libanime](https://github.com/libanime/libanime_schema).
- Так как это прототип и имеет свои ограничения, то при модификациях этих модулей
используйте наследование, чтобы не потерять изменения при обновлениях.
Пример из модуля [animego](anicli_api/source/animego.py):
```python
from anicli_api.source.parsers.animego_parser import AnimeView as AnimeViewOld
class AnimeView(AnimeViewOld):
@staticmethod
def _parse_description(part) -> str:
# remove whitespaces patch
val_0 = part.css(".description ::text").getall()
return " ".join(line.strip() for line in val_0)
```
- Проект разработан преимущественно на личное, некоммерческое использование с client-side
стороны.
Автор проекта не несет ответственности за поломки, убытки в высоко нагруженных проектах и решение
предоставляется "Как есть" в соответствии с [MIT](LIENSE) лицензией.
- Основная цель этого проекта — связать автоматизацию и эффективность извлечения того,
что предоставляется пользователю в Интернете.
Весь контент, доступный в рамках проекта, размещается на внешних неаффилированных источниках.
- **Этот проект не включает инструменты кеширования и сохранения всех полученных данных,
только готовые реализации парсеров и программные интерфейсы**
Raw data
{
"_id": null,
"home_page": "",
"name": "anicli-api",
"maintainer": "",
"docs_url": null,
"requires_python": ">=3.8,<4.0",
"maintainer_email": "",
"keywords": "anime,api,ru,russia,asyncio,parser,httpx,dev",
"author": "vypivshiy",
"author_email": "",
"download_url": "https://files.pythonhosted.org/packages/a1/6c/f2df2c53aa09069db63f07321deb792c811bd2b3859906c213e1b736560b/anicli_api-0.5.0.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\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), parsel \u0438 \u043e\u0431\u0451\u0440\u0442\u043a\u0438 scrape-schema\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```\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 - \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\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\u2514\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 \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 \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 \u251c\u2500 ... ready-made \u043f\u0430\u0440\u0441\u0435\u0440\u044b\n ...\n\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 \u043f\u0430\u0440\u0441\u0435\u0440\u043e\u0432:\n\n![](high-level-schema.png)\n\n# source description\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 \u0441\u0442\u0440\u0430\u043d\u0438\u0446) \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. many - \u043e\u0442 \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0445 \u0430\u0432\u0442\u043e\u0440\u043e\u0432. subtitles - \u0442\u043e\u043b\u044c\u043a\u043e \u0441\u0443\u0431\u0442\u0438\u0442\u0440\u044b. 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 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 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** (\u043c\u043e\u0436\u0435\u0442 \u043f\u0435\u0440\u0438\u043e\u0434\u0438\u0447\u0435\u0441\u043a\u0438 \u043d\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c), \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\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\u0445 \u0442\u0435\u0441\u0442\u043e\u0432\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 - 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** |\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 \u0438\u0445 \u0441\u0435\u0440\u0432\u0435\u0440\u043e\u0432 |\n| csst | 1080 | |\n| dzen | 1080 | |\n| mailru | | |\n| okru | | |\n| sovetromantica | 1080 | |\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\n\n# Quickstart example\n\n```python\nfrom anicli_api.source.animego import Extractor # can usage any source\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 \n print(*[f\"{i}) {r}\" for i, r in enumerate(results)], sep=\"\\n\")\n anime = results[int(input(\"anime > \"))].get_anime()\n episodes = anime.get_episodes()\n print(*[f\"{i}) {ep}\" for i, ep in enumerate(episodes)], sep=\"\\n\")\n episode = episodes[int(input(\"episode > \"))]\n sources = episode.get_sources()\n print(*[f\"{i}) {source}\" for i, source in enumerate(sources)], sep=\"\\n\")\n source = sources[int(input(\"source > \"))]\n videos = source.get_videos()\n print(*[f\"{i} {video}\" for i, video in enumerate(videos)], sep=\"\\n\")\n video = videos[int(input(\"video > \"))]\n print(video.type, video.quality, video.url, video.headers)\n\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## 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 \u044d\u043a\u0441\u0442\u0440\u0430\u043a\u0442\u043e\u0440\u043e\u0432\n\n```python\nfrom anicli_api.base import HTTPSync\n\nfrom httpx import HTTPTransport\n\nclient = HTTPSync()\nprint(client._transport) # anicli_api._http.HTTPRetryConnectSyncTransport\nprint(client.headers) # {'user-agent': 'Mozilla/5.0 (Linux; Android 6.0...', ...}\n\nclient_2 = HTTPSync(transport=HTTPTransport(), # default transport\n headers={\"user-agent\": \"My Cool useragent\"}\n ) \n\nprint(client_2._transport) # httpx.HTTPTransport\nprint(client_2.headers) # {'user-agent': 'My Cool useragent', ...}\nassert client_2 == client # Passed\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, \u0432 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a\u0430\u0445\n\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 \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\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 \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 \u0434\u0430\u043d\u043d\u044b\u0445.\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\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- `anicli_api/source/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 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \n[scrape-schema-codegen](https://github.com/vypivshiy/selector_schema_codegen) \u0438 \u0444\u0430\u0439\u043b\u043e\u0432-\u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438 [libanime](https://github.com/libanime/libanime_schema). \n\n- \u0422\u0430\u043a \u043a\u0430\u043a \u044d\u0442\u043e \u043f\u0440\u043e\u0442\u043e\u0442\u0438\u043f \u0438 \u0438\u043c\u0435\u0435\u0442 \u0441\u0432\u043e\u0438 \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u044f, \u0442\u043e \u043f\u0440\u0438 \u043c\u043e\u0434\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f\u0445 \u044d\u0442\u0438\u0445 \u043c\u043e\u0434\u0443\u043b\u0435\u0439 \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 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f\u0445.\n\n\u041f\u0440\u0438\u043c\u0435\u0440 \u0438\u0437 \u043c\u043e\u0434\u0443\u043b\u044f [animego](anicli_api/source/animego.py):\n\n```python\nfrom anicli_api.source.parsers.animego_parser import AnimeView as AnimeViewOld\n\nclass AnimeView(AnimeViewOld):\n @staticmethod\n def _parse_description(part) -> str:\n # remove whitespaces patch\n val_0 = part.css(\".description ::text\").getall()\n return \" \".join(line.strip() for line in val_0)\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.5.0",
"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": "3a1335c9c052063d646d8e9c1a7db0cbc56fef43591f9f88b2e20ce9e8f70c49",
"md5": "8e8aad7535df89a82cd30a0a02f73f07",
"sha256": "ab20454b89a408905c1d62395fa75f81fbc3e30a60102ff2aa9ab39677afaf2a"
},
"downloads": -1,
"filename": "anicli_api-0.5.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "8e8aad7535df89a82cd30a0a02f73f07",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.8,<4.0",
"size": 39527,
"upload_time": "2023-12-08T07:44:08",
"upload_time_iso_8601": "2023-12-08T07:44:08.246149Z",
"url": "https://files.pythonhosted.org/packages/3a/13/35c9c052063d646d8e9c1a7db0cbc56fef43591f9f88b2e20ce9e8f70c49/anicli_api-0.5.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "a16cf2df2c53aa09069db63f07321deb792c811bd2b3859906c213e1b736560b",
"md5": "fcba49033675449ab2ae4bc951663fd4",
"sha256": "66563288e2e66cf56d0fba779498efb0748550932fb1c177a376dc66f8e38325"
},
"downloads": -1,
"filename": "anicli_api-0.5.0.tar.gz",
"has_sig": false,
"md5_digest": "fcba49033675449ab2ae4bc951663fd4",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.8,<4.0",
"size": 29428,
"upload_time": "2023-12-08T07:44:10",
"upload_time_iso_8601": "2023-12-08T07:44:10.024255Z",
"url": "https://files.pythonhosted.org/packages/a1/6c/f2df2c53aa09069db63f07321deb792c811bd2b3859906c213e1b736560b/anicli_api-0.5.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2023-12-08 07:44:10",
"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"
}