diff --git a/.github/workflows/deploy-mkdocs.yml b/.github/workflows/deploy-mkdocs.yml index b2c1a6b..0e91cd6 100644 --- a/.github/workflows/deploy-mkdocs.yml +++ b/.github/workflows/deploy-mkdocs.yml @@ -31,7 +31,7 @@ jobs: with: python-version: 3.x - name: Setup dependencies - run: pip install --upgrade pip && pip install mkdocs mkdocs-material mkdocstrings mkdocstrings[python] black mkdocs-git-revision-date-localized-plugin + run: pip install --upgrade pip && pip install mkdocs mkdocs-material mkdocstrings mkdocstrings[python] black mkdocs-git-revision-date-localized-plugin mkdocs-static-i18n[material] - name: Build with MkDocs run: | mkdocs build -c -d _site diff --git a/README.it.md b/README.it.md new file mode 100644 index 0000000..082e07d --- /dev/null +++ b/README.it.md @@ -0,0 +1,73 @@ +[![AnimeWorld](https://github.com/MainKronos/AnimeWorld-API/blob/master/docs/static/img/AnimeWorld-API.png)](https://mainkronos.github.io/AnimeWorld-API/) +# AnimeWorld-API + +[![Version](https://img.shields.io/pypi/v/animeworld)](https://github.com/MainKronos/AnimeWorld-API/releases/latest) +![Activity](https://img.shields.io/github/commit-activity/w/MainKronos/AnimeWorld-API) +[![Publish to PyPI](https://github.com/MainKronos/AnimeWorld-API/workflows/Publish%20to%20PyPI/badge.svg)](https://pypi.org/project/animeworld/) +[![Deploy MkDocs](https://github.com/MainKronos/AnimeWorld-API/actions/workflows/deploy-mkdocs.yml/badge.svg)](https://github.com/MainKronos/AnimeWorld-API/actions/workflows/deploy-mkdocs.yml) + +![PyPI - Downloads](https://img.shields.io/pypi/dm/animeworld) +![PyPI - Downloads](https://img.shields.io/pypi/dw/animeworld) +![PyPI - Downloads](https://img.shields.io/pypi/dd/animeworld) + +[![Static Badge](https://img.shields.io/badge/lang-english-%239FA8DA)](https://github.com/MainKronos/AnimeWorld-API/blob/master/README.md) +[![Static Badge](https://img.shields.io/badge/lang-italian-%239FA8DA)](https://github.com/MainKronos/AnimeWorld-API/blob/master/README.it.md) + + +AnimeWorld-API is an unofficial library for [AnimeWorld](https://www.animeworld.so/) (Italian anime site). + +## Installazione +Questa libreria richiede [Python 3.7](https://www.python.org/) o superiore. + +È Possibile installarare la libreria tramite pip: +```shell script +pip install animeworld +``` + +## Utilizzo +Per ricercare un anime per nome nel sito di animeWolrd è possibile usare la funzione find(). +```python +import animeworld as aw + +res = aw.find("No game no life") +print(res) +``` +La funzione estituirà un dizionario contentente per chiave il nome dell'anime e per valore il link della pagina di animeworld. +```python +{'name': 'No Game no Life', 'link': 'https://www.animeworld.so/play/no-game-no-life.IJUH1', ...} +``` +È Possibile anche scaricare gli episodi di un anime. +```python +import animeworld as aw + +anime = aw.Anime(link="https://www.animeworld.so/play/danmachi-3.Ydt8-") +for episodio in anime.getEpisodes(): + print("Episodio Numero: ", episodio.number) + + if(episodio.download()): + print("scaricato") + else: + print("errore") + + if episodio.number == '1': break +``` +``` +Episodio Numero: 1 +scaricato +``` + +## Documentazione + +La documentazione completa è disponibile qui: [Documentazione](https://mainkronos.github.io/AnimeWorld-API/) + +Per una panoramica di tutte le nozioni di base, vai alla sezione [QuickStart](https://mainkronos.github.io/AnimeWorld-API/usage/quickstart) + +Per argomenti più avanzati, vedere la sezione [Advanced Usage](https://mainkronos.github.io/AnimeWorld-API/usage/advanced) + +La sezione [API Reference](https://mainkronos.github.io/AnimeWorld-API/api-reference/developer-interface) fornisce un riferimento API completo. + +Se vuoi contribuire al progetto, vai alla sezione [Contributing](https://mainkronos.github.io/AnimeWorld-API/community/contributing) + +## Star History + +[![Star History Chart](https://api.star-history.com/svg?repos=MainKronos/AnimeWorld-API&type=Date)](https://star-history.com/#MainKronos/AnimeWorld-API&Date) diff --git a/README.md b/README.md index 28852da..af76394 100644 --- a/README.md +++ b/README.md @@ -10,59 +10,63 @@ ![PyPI - Downloads](https://img.shields.io/pypi/dw/animeworld) ![PyPI - Downloads](https://img.shields.io/pypi/dd/animeworld) +[![Static Badge](https://img.shields.io/badge/lang-english-%239FA8DA)](https://github.com/MainKronos/AnimeWorld-API/blob/master/README.md) +[![Static Badge](https://img.shields.io/badge/lang-italian-%239FA8DA)](https://github.com/MainKronos/AnimeWorld-API/blob/master/README.it.md) + + AnimeWorld-API is an unofficial library for [AnimeWorld](https://www.animeworld.so/) (Italian anime site). -## Installazione -Questa libreria richiede [Python 3.7](https://www.python.org/) o superiore. +## Installation +This library requires [Python 3.7](https://www.python.org/) or later. -È Possibile installarare la libreria tramite pip: +You can install the library using pip: ```shell script pip install animeworld ``` -## Utilizzo -Per ricercare un anime per nome nel sito di animeWolrd è possibile usare la funzione find(). +## Usage +To search for an anime by name on the AnimeWorld site, you can use the `find()` function. ```python import animeworld as aw res = aw.find("No game no life") print(res) ``` -La funzione estituirà un dizionario contentente per chiave il nome dell'anime e per valore il link della pagina di animeworld. +The function will return a dictionary with the anime name as the key and the link to the anime world page as the value. ```python {'name': 'No Game no Life', 'link': 'https://www.animeworld.so/play/no-game-no-life.IJUH1', ...} ``` -È Possibile anche scaricare gli episodi di un anime. +You can also download episodes of an anime. ```python import animeworld as aw anime = aw.Anime(link="https://www.animeworld.so/play/danmachi-3.Ydt8-") -for episodio in anime.getEpisodes(): - print("Episodio Numero: ", episodio.number) +for episode in anime.getEpisodes(): + print("Episode Number: ", episode.number) - if(episodio.download()): - print("scaricato") + if(episode.download()): + print("Downloaded") else: - print("errore") + print("Error") - if x.number == '1': break + if episode.number == '1': break ``` ``` -Episodio Numero: 1 -scaricato +Episode Number: 1 +Downloaded ``` -## Documentazione +## Documentation -La documentazione completa è disponibile qui: [Documentazione](https://mainkronos.github.io/AnimeWorld-API/) +The complete documentation is available here: [Documentation](https://mainkronos.github.io/AnimeWorld-API/) -Per una panoramica di tutte le nozioni di base, vai alla sezione [QuickStart](https://mainkronos.github.io/AnimeWorld-API/usage/quickstart) +For an overview of all the basics, go to the [QuickStart](https://mainkronos.github.io/AnimeWorld-API/usage/quickstart) section. -Per argomenti più avanzati, vedere la sezione [Advanced Usage](https://mainkronos.github.io/AnimeWorld-API/usage/advanced) +For more advanced topics, see the [Advanced Usage](https://mainkronos.github.io/AnimeWorld-API/usage/advanced) section. -La sezione [API Reference](https://mainkronos.github.io/AnimeWorld-API/api-reference/developer-interface) fornisce un riferimento API completo. +The [API Reference](https://mainkronos.github.io/AnimeWorld-API/api-reference/developer-interface) section provides a complete API reference. -Se vuoi contribuire al progetto, vai alla sezione [Contributing](https://mainkronos.github.io/AnimeWorld-API/community/contributing) +If you want to contribute to the project, visit the [Contributing](https://mainkronos.github.io/AnimeWorld-API/community/contributing) section. ## Star History diff --git a/REV.md b/REV.md deleted file mode 100644 index b451323..0000000 --- a/REV.md +++ /dev/null @@ -1,149 +0,0 @@ -# Rest Api - -base url: `https://www.animeworld.so/api/` - -## Anonimo - -> Richieste che non richiedono di essere autenticati. - -### POST - -
- POST search/v2?keyword={keyword} - -> Ricerca anime nel sito in base ad una chiave di ricerca. - -- parametri: - - `keyword`: chiave di ricerca -- header: - - `csrf-token`: token cross-site request forgery - -
- -
- POST download/{epID} - -> Restituisce informazioni per il download dell'episodio richiesto. - -- parametri: - - `epID`: id episodio -- header: - - `csrf-token`: token cross-site request forgery - -
- -
- POST comments/anime/get/{animeID} - -> Restituisce i commenti degli utenti dell'anime selezionato. - -> **Warning**\ -> Restituisce una risposta in formato HTML. - -- parametri: - - `animeID`: id anime - - `epID`: id episodio -- header: - - `csrf-token`: token cross-site request forgery - -
- -
- POST comments/anime/get/{animeID}/{epID} - -> Restituisce i commenti degli utenti di un episodio dell'anime selezionato. - -> **Warning**\ -> Restituisce una risposta in formato HTML. - -- parametri: - - `animeID`: id anime - - `epID`: id episodio -- header: - - `csrf-token`: token cross-site request forgery - -
- -
- POST user/register - -> Registra un account sul sito. - -> **Note**\ -> In caso di successo restituisce un codice 302. - -- payload: - - `_csrf:`: token cross-site request forgery - - `username`: nome utente - - `email`: email - - `password`: password - - `confirmPassword`: password di conferma - - `g-recaptcha-response:`: token captcha - -
- -### GET - -
- GET tooltip/{animeID} - -> Restituisce informazioni generali e **sintetiche** sull'anime. - -> **Warning**\ -> Restituisce una risposta in formato HTML. - -- parametri: - - `animeID`: id anime -- header: - - `csrf-token`: token cross-site request forgery - -
- -
- GET schedule?time={timestamp} - -> Restituisce il calendario delle uscite degli episodi per il giorno indicato. - -> **Warning**\ -> Restituisce una risposta in formato HTML. - -- parametri: - - `timestamp`: timestamp in millisecondi con timezone GMT+2 - -
- -## Utente - -> Richieste che richiedono di essere autenticati. - -### POST - -
- POST comments/profile/post/{userID} - -> Restituisce informazioni su una notifica ricevuta. - -- parametri: - - `userID`: id utente -- payload: - - `comment`: commento -- coockie: - - `sessionId`: token di sessione -- header: - - `csrf-token`: token cross-site request forgery - -
- -### GET - -
- GET notifications/open/{notificationID} - -> Restituisce informazioni su una notifica ricevuta. - -- parametri: - - `notificationID`: id notifica -- coockie: - - `sessionId`: token di sessione - -
\ No newline at end of file diff --git a/docs/community/contributing.en.md b/docs/community/contributing.en.md new file mode 100644 index 0000000..9cca213 --- /dev/null +++ b/docs/community/contributing.en.md @@ -0,0 +1,107 @@ +# Contributing + +## Server + +This section explains how to add the ability to download an episode from another unsupported server. + +--8<-- "static/server.txt" + +To add a new server, follow these steps: + +1. Create a .py file with the server's name and place it in the [servers](https://github.com/MainKronos/AnimeWorld-API/tree/master/animeworld/servers) folder (e.g., `NewServer.py`). + +1. Use this template for the class of the new server: +```py title="NewServer.py" linenums="1" +from .Server import * + +class NewServer(Server): + def fileLink(self): # Mandatory + """ + Retrieves the direct link for downloading the episode file. + + Returns: + Direct link. + + Example: + ```py + return str # File link + ``` + """ + pass # TODO: to be completed + + def fileInfo(self) -> Dict[str, str]: # Optional + """ + Retrieves information about the episode file. + + Returns: + Episode file information. + + Example: + ```py + return { + "content_type": str, # File type, e.g., video/mp4 + "total_bytes": int, # Total file bytes + "last_modified": datetime, # Date and time of the last update to the episode on the server + "server_name": str, # Server name + "server_id": int, # Server ID + "url": str # Episode URL + } + ``` + """ + pass # TODO: to be completed + + def download(self, title: Optional[str] = None, folder: str = '', *, hook: Callable[[Dict], None] = lambda *args: None, opt: List[str] = []) -> Optional[str]: # Mandatory + """ + Downloads the episode. + + Args: + title: Name to be given to the downloaded file. + folder: Location where the downloaded file will be moved. + + Other parameters: + hook: Function called multiple times during the download; the function receives a dictionary with the following keys:\n + - `total_bytes`: Total bytes to download. + - `downloaded_bytes`: Currently downloaded bytes. + - `percentage`: Download progress percentage. + - `speed`: Download speed (bytes/s). + - `elapsed`: Time elapsed since the start of the download. + - `eta`: Estimated remaining time for the download to finish. + - `status`: 'downloading' | 'finished' | 'aborted' + - `filename`: Downloaded file name. + + opt: List for additional options.\n + - `'abort'`: Forcefully stops the download. + + Returns: + Downloaded file name. + + Raises: + HardStoppedDownload: The downloading file has been forcibly interrupted. + + Example: + ```py + return str # Downloaded file + ``` + """ + if title is None: + title = self._defTitle + else: + title = self._sanitize(title) + + pass + + # TODO: to be completed, select one of the 2 methods: + # #NOTE: to be used if the file can be downloaded simply with httpx: + # return self._downloadIn(title,folder,hook=hook,opt=opt) + # + # #NOTE: to be used if the file must be downloaded using the youtube_dl library + # return self._dowloadEx(title,folder,hook=hook,opt=opt) +``` + +1. Complete the various functions (those marked as `Optional` can also be left incomplete), also taking inspiration from the various servers loaded in the folder. + +1. Add the line `from .NewServer import NewServer` to the file [servers/__init__.py](https://github.com/MainKronos/AnimeWorld-API/tree/master/animeworld/servers/__init__.py). + +1. Modify the file [episodio.py](https://github.com/MainKronos/AnimeWorld-API/tree/master/animeworld/episodio.py) by adding the server's name among the imports ([Line 12](https://github.com/MainKronos/AnimeWorld-API/blob/master/animeworld/episodio.py#L12)) and modifying the function [__setServer](https://github.com/MainKronos/AnimeWorld-API/blob/master/animeworld/episodio.py). + +If everything works correctly, open a [pull request](https://github.com/MainKronos/AnimeWorld-API/pulls). \ No newline at end of file diff --git a/docs/community/contributing.md b/docs/community/contributing.it.md similarity index 96% rename from docs/community/contributing.md rename to docs/community/contributing.it.md index 5fd21ce..9209dd0 100644 --- a/docs/community/contributing.md +++ b/docs/community/contributing.it.md @@ -100,6 +100,6 @@ class NuovoServer(Server): 1. Aggiungi la linea `from .NuovoServer import NuovoServer` al file [servers/__init__.py](https://github.com/MainKronos/AnimeWorld-API/tree/master/animeworld/servers/__init__.py). -1. Modificare il file [episodio.py](https://github.com/MainKronos/AnimeWorld-API/tree/master/animeworld/episodio.py) aggiungendo il nome del server tra gli import ([Linea 11](https://github.com/MainKronos/AnimeWorld-API/blob/master/animeworld/episodio.py#L11)) e modificare la funzione [__setServer](https://github.com/MainKronos/AnimeWorld-API/blob/master/animeworld/episodio.py). +1. Modificare il file [episodio.py](https://github.com/MainKronos/AnimeWorld-API/tree/master/animeworld/episodio.py) aggiungendo il nome del server tra gli import ([Linea 12](https://github.com/MainKronos/AnimeWorld-API/blob/master/animeworld/episodio.py#L12)) e modificare la funzione [__setServer](https://github.com/MainKronos/AnimeWorld-API/blob/master/animeworld/episodio.py). Se tutto funziona correttamente apri una richiesta di [pull](https://github.com/MainKronos/AnimeWorld-API/pulls). \ No newline at end of file diff --git a/docs/index.en.md b/docs/index.en.md new file mode 100644 index 0000000..30e9703 --- /dev/null +++ b/docs/index.en.md @@ -0,0 +1,53 @@ +

+ AnimeWorld-API +

+ +--- + +# Welcome to AnimeWorld-API wiki! + +AnimeWorld-API is an UNOFFICIAL library for AnimeWorld (Italian anime site). + +--- + +## Installation +Install the library using pip: +```bash +$ pip install animeworld +``` + +Now you can start searching for anime: +```python +>>> import animeworld as aw +>>> res = aw.find("No game no life") +>>> res +{'name': 'No Game no Life', 'link': 'https://www.animeworld.so/play/no-game-no-life.IJUH1', ...} +``` + +And download episodes: +```python +>>> import animeworld as aw +>>> anime = aw.Anime(link="https://www.animeworld.so/play/danmachi-3.Ydt8-") +>>> for episode in anime.getEpisodes(): +... print("Episode Number: ", episode.number) +... if(episode.download()): +... print("Download completed") +``` + +## Documentation + +For an overview of all the basics, go to the [QuickStart](usage/quickstart.md) section. + +For more advanced topics, see the [Advanced Usage](usage/advanced.md) section. + +The [API Reference](api-reference/developer-interface.md) section provides a complete API reference. + +If you want to contribute to the project, visit the [Contributing](community/contributing.md) section. + +## Dependencies + +- [`httpx`](https://github.com/encode/httpx) - A next-generation HTTP client for Python. + +- [`youtube_dl`](https://github.com/ytdl-org/youtube-dl) - Command-line program to download videos from YouTube.com and other video sites. + +- [`beautifulsoup4`](https://www.crummy.com/software/BeautifulSoup/bs4/doc/) - Beautiful Soup is a Python library designed for quick turnaround projects like screen-scraping. \ No newline at end of file diff --git a/docs/index.md b/docs/index.it.md similarity index 95% rename from docs/index.md rename to docs/index.it.md index f674845..77df895 100644 --- a/docs/index.md +++ b/docs/index.it.md @@ -1,5 +1,5 @@

- AnimeWorld-API + AnimeWorld-API

--- diff --git a/docs/usage/advanced.en.md b/docs/usage/advanced.en.md new file mode 100644 index 0000000..de6e166 --- /dev/null +++ b/docs/usage/advanced.en.md @@ -0,0 +1,213 @@ +# Advanced Usage + +## Exceptions + +The library raises several exceptions, the main ones being: [`AnimeNotAvailable`](../../api-reference/exceptions/#animeworld.exceptions.AnimeNotAvailable), [`Error404`](../../api-reference/exceptions/#animeworld.exceptions.Error404), and [`DeprecatedLibrary`](../../api-reference/exceptions/#animeworld.exceptions.DeprecatedLibrary).
For more information, consult the [documentation](../../api-reference/exceptions). + +### DeprecatedLibrary + +The [`DeprecatedLibrary`](../../api-reference/exceptions/#animeworld.exceptions.DeprecatedLibrary) exception is raised when a change on the [AnimeWorld](https://www.animeworld.so/) site is detected that is no longer supported by the library. + +This exception can be raised practically by any method of the library, so it is recommended to handle it globally. + +```py linenums="1" hl_lines="9" +try: + res = aw.find("...") + anime = aw.Anime("https://www.animeworld.so/play/...") + episodes = anime.getEpisodes() + + for x in episodes: + x.download() + +except aw.DeprecatedLibrary as e: + # Exception handling + print("Open an issue on GitHub :(") +``` + +### Error404 + +The [`Error404`](../../api-reference/exceptions/#animeworld.exceptions.Error404) exception is raised when the URL passed during the creation of the Anime object points to a [404](https://www.animeworld.so/404) page. + +Since this exception is only raised by the [`Anime`](../../api-reference/developer-interface/#animeworld.Anime) class, it is recommended to handle it only if you are indeed instantiating an object of that class. + +```py linenums="1" hl_lines="7" +try: + res = aw.find("...") + + try: + anime = aw.Anime("https://www.animeworld.so/play/...") + + except aw.Error404 as e: + # Exception handling + print("Anime not found :(") + + else: + episodes = anime.getEpisodes() + for x in episodes: + x.download() + +except aw.DeprecatedLibrary as e: + # Exception handling + print("Open an issue on GitHub :(") +``` + +### AnimeNotAvailable + +The [`AnimeNotAvailable`](../../api-reference/exceptions/#animeworld.exceptions.AnimeNotAvailable) exception is raised when the anime page exists, but the episodes are not yet available. This happens, for example, when a new season starts. + +The exception occurs only when calling the [`getEpisodes`](../../api-reference/developer-interface/#animeworld.anime.Anime.getEpisodes) method. + +```py linenums="1" hl_lines="15" +try: + res = aw.find("...") + + try: + anime = aw.Anime("https://www.animeworld.so/play/...") + + except aw.Error404 as e: + # Exception handling + print("Anime not found :(") + + else: + try: + episodes = anime.getEpisodes() + + except aw.AnimeNotAvailable as e: + # Exception handling + print("Anime not yet available :(") + + else: + for x in episodes: + x.download() + +except aw.DeprecatedLibrary as e: + # Exception handling + print("Open an issue on GitHub :(") +``` + +## Server + +To download an episode, you can manually select the server from which to download the video. To do this, first obtain the list of servers using the [`Episode.links`](../../api-reference/developer-interface/#animeworld.episode.Episode.links) attribute and then choose one of the supported ones. + +!!! Warning + I do not recommend using this method to download an episode; it is much simpler and safer to use the [`Episode.download`](../../api-reference/developer-interface/#animeworld.episode.Episode.download) method because: + + 1. The fastest server is always chosen at the beginning. + 2. If an unsupported server is chosen, the [ServerNotSupported](../../api-reference/exceptions/#animeworld.exceptions.ServerNotSupported) exception will be raised. + +```py linenums="1" +anime = aw.Anime("...") + +# Choose the first episode +episode = anime.getEpisodes()[0] + +# Get the list of servers +servers = episode.links + +# Choose the AnimeWorld_Server server, for example +server = [x for x in servers if isinstance(x.name, AnimeWorld_Server)][0] + +# Download the video +server.download() +``` + +### Supported Servers + +The supported servers are listed below; if you want to contribute to add others, you can take a look at the [Contributing](../../community/contributing/) section. + +--8<-- "static/server.txt" + +## Download + +!!! Warning inline end + If there are any disallowed characters in the file name (`#%&{}<>*?/$!'":@+\``|=`), they will be automatically removed. To obtain the actual file name written to disk, you can get it from the return of the [`Episode.download`](../../api-reference/developer-interface/#animeworld.episode.Episode.download) method. + +To obtain an episode, i recommend using the [`Episode.download`](../../api-reference/developer-interface/#animeworld.episodio.Episodio.download) method, which retrieves the video using the fastest available server at the time of the download. + +You can set the file name using the `title` parameter and the destination folder using the `folder` parameter. + +### hook + +The `hook` parameter is more interesting; this is a reference to a function that will be called every time a video chunk is downloaded (~524 KB). This is useful for displaying the download progress on the screen. The function must have a single parameter of type `Dict[str, Any]`. + +```py +def my_hook(data): + print(data) + +episodi = anime.getEpisodes() +for x in episodi: + x.download(hook=my_hook) +``` + +An example of a possible dictionary passed to the hook function is as follows: + +```py +{ + "total_bytes": 234127340, # Total size of the video in bytes + "downloaded_bytes": 524288, # Downloaded size in bytes + "percentage": 0.0022393283928310126, # Downloaded percentage [0, 1] + "speed": 3048288.673006227, # Download speed in bytes/s + "elapsed": 0.17199420928955078, # Elapsed time in seconds + "filename": "1 - AnimeWorld Server.mp4", # File name + "eta": 76.63416331551707, # Estimated remaining time in seconds + "status": "downloading", # Download status ('downloading' | 'finished' | 'aborted') +} +``` + +### opt + +It is also possible to forcefully stop the download using the `opt` parameter. This parameter is a list of strings, each string representing an option. Currently, the only possible option is `abort`, which stops the download. + +If the string `abort` appears in opt during the download, the download is stopped, and the partially downloaded file is deleted. + +An example of using the `opt` parameter is as follows: + +```py linenums="1" +import animeworld as aw +import time +from threading import Thread + +anime = aw.Anime("...") +episode = anime.getEpisodes()[0] + +# Define the function for the thread +def handle_download(options_list): + time.sleep(5) + options_list.append("abort") + + +opt = [] # Array for dynamic options +t = Thread(target=handle_download, args=(opt,)) # Create the thread + +t.start() # Start the thread + +episode.download(opt=opt) # Start the download +``` + +In this example, the download is stopped after 5 seconds. + +### I/O Buffer + +You can download an episode using a file descriptor directly instead of a string for the directory. Just pass an [IOBase](https://docs.python.org/3/library/io.html#i-o-base-classes) type to the `folder` parameter. + +```py linenums="1" +import animeworld as aw +import io + +anime = aw.Anime("...") +episode = anime.getEpisodes()[0] + +buffer = io.BytesIO() # Allocate an in-memory buffer + +episode.download(folder=buffer) # Start the download +``` + +In this example, the downloaded episode is written to memory without being saved as a file. + +--- + +## Complete Example + +```py title="example.py" linenums="1" +--8<-- "static/example.py" +``` \ No newline at end of file diff --git a/docs/usage/advanced.md b/docs/usage/advanced.it.md similarity index 89% rename from docs/usage/advanced.md rename to docs/usage/advanced.it.md index 3a41c8b..3406abe 100644 --- a/docs/usage/advanced.md +++ b/docs/usage/advanced.it.md @@ -1,6 +1,5 @@ # Advanced Usage - ## Eccezioni La libreria solleva diverse eccezioni, le principali sono: [`AnimeNotAvailable`](../../api-reference/exceptions/#animeworld.exceptions.AnimeNotAvailable), [`Error404`](../../api-reference/exceptions/#animeworld.exceptions.Error404) e [`DeprecatedLibrary`](../../api-reference/exceptions/#animeworld.exceptions.DeprecatedLibrary).
Per maggiori informazioni consultare la [documentazione](../../api-reference/exceptions). @@ -27,7 +26,7 @@ except aw.DeprecatedLibrary as e: ### Error404 -L'eccezione [`Error404`](../../api-reference/exceptions/#animeworld.exceptions.Error404) viene sollevata l'URL passato alla creazione dell'oggetto Anime punta ad una pagina [404](https://www.animeworld.so/404). +L'eccezione [`Error404`](../../api-reference/exceptions/#animeworld.exceptions.Error404) viene sollevata quando l'URL passato alla creazione dell'oggetto Anime punta ad una pagina [404](https://www.animeworld.so/404). Visto che questa eccezione viene sollevata solo dalla classe [`Anime`](../../api-reference/developer-interface/#animeworld.Anime), è consigliato gestirla solo se effettivamente si sta istanziando un oggetto di quella classe. @@ -54,7 +53,7 @@ except aw.DeprecatedLibrary as e: ### AnimeNotAvailable -L'eccezione [`AnimeNotAvailable`](../../api-reference/exceptions/#animeworld.exceptions.AnimeNotAvailable) viene sollevata quando la pagina dell'anime esiste ma gli episodi non sono ancora disponibili. Questo accade ad esempio quando inizia una nuova stagione e molti anime stagionali non sono ancora stati tradotti. +L'eccezione [`AnimeNotAvailable`](../../api-reference/exceptions/#animeworld.exceptions.AnimeNotAvailable) viene sollevata quando la pagina dell'anime esiste ma gli episodi non sono ancora disponibili. Questo accade ad esempio quando inizia una nuova stagione. L'eccezione si verifica soltanto alla chiamata del metodo [`getEpisodes`](../../api-reference/developer-interface/#animeworld.anime.Anime.getEpisodes). @@ -94,7 +93,7 @@ Per scarica un episodio è possibile selezione manualmente il server da cui scar Non consiglio di utilizzare questo metodo per scaricare un episodio, è molto piú semplice e sicuro utilizzare il metodo [`Episodio.download`](../../api-reference/developer-interface/#animeworld.episodio.Episodio.download), perchè: 1. Viene scelto sempre il server più veloce al momento del download. - 2. Se si usa un serer non supportato dalla libreria, verrà sollevata l'eccezione [ServerNotSupported](../../api-reference/exceptions/#animeworld.exceptions.ServerNotSupported). + 2. Se si sceglie un server non supportato, verrà sollevata l'eccezione [ServerNotSupported](../../api-reference/exceptions/#animeworld.exceptions.ServerNotSupported). ```py linenums="1" anime = aw.Anime("...") @@ -114,7 +113,7 @@ server.download() ### Server supportati -I server supportati sono querelli indicati di sotto, se vuoi contribuire ad aggiungerne altri puoi dare un occhiata alla sezione [Contributing](../../community/contributing/). +I server supportati sono indicati di seguito, se vuoi contribuire per aggiungerne altri puoi dare un occhiata alla sezione [Contributing](../../community/contributing/). --8<-- "static/server.txt" @@ -124,7 +123,7 @@ I server supportati sono querelli indicati di sotto, se vuoi contribuire ad aggi Se ci sono dei caratteri non ammessi nel nome del file (`#%&{}<>*?/$!'":@+\``|=`), questi verranno rimossi automaticamente. Per ottenere il nome del file effettivamente scritto su disco è possibile ottenerlo dal ritorno del metodo [`Episodio.download`](../../api-reference/developer-interface/#animeworld.episodio.Episodio.download). -Per scaricare un episodio è possibile utilizzare il metodo [`Episodio.download`](../../api-reference/developer-interface/#animeworld.episodio.Episodio.download), che scarica il video utilizzando il server piú veloce al momento del download. +Per ottenere un episodio, consiglio di utilizzare il metodo [`Episodio.download`](../../api-reference/developer-interface/#animeworld.episodio.Episodio.download), il quale recupera il video utilizzando il server più veloce disponibile al momento del download. È possibile impostare il nome del file utilizzando il parametro `title` e la cartella di destinazione utilizzando il parametro `folder`. @@ -132,6 +131,15 @@ Per scaricare un episodio è possibile utilizzare il metodo [`Episodio.download` Il parametro `hook` è più interessante, questo è un riferimento ad una funzione che poi verrà chiamata ogni volta che viene scaricato un chunk del video (~524 Kb). Questo è utile per mostrare a schermo il progresso del download. La funzione deve avere un singolo parametro di tipo `Dict[str, Any]`. +```py +def my_hook(data): + print(data) + +episodi = anime.getEpisodes() +for x in episodi: + x.download(hook=my_hook) +``` + Un esempio di un possibile dizionario passato alla funzione hook è il seguente: ```py diff --git a/docs/usage/quickstart.en.md b/docs/usage/quickstart.en.md new file mode 100644 index 0000000..9ea9fbe --- /dev/null +++ b/docs/usage/quickstart.en.md @@ -0,0 +1,147 @@ +# QuickStart + +First, let's import the library: + +```python linenums="1" +import animeworld as aw +``` + +## Find + +Now let's try searching for an anime: + +```python linenums="2" +res = aw.find("Sword Art Online") +print(res) +``` +??? Example "Output" + + ```python + [ + { + "id": 1717, + "name": "Sword Art Online", + "jtitle": "Sword Art Online", + "studio": "A-1 Pictures", + "release": "05 Luglio 2014", + "episodes": 25, + "state": "1", + "story": 'Kazuto "Kirito" Kirigaya, un genio della programmazione, entra in una realtà virtuale interattiva con pluralità di giocatori (una realtà "massively multi-player online" o "MMO") denominata "Sword Art Online". Il problema sta nel fatto che, una volta entrati, se ne può uscire solo vincitori, completando il gioco, perché il game over equivale a morte certa del giocatore.', + "categories": [...], + "image": "https://img.animeworld.so/locandine/36343l.jpg", + "durationEpisodes": "23", + "link": "https://www.animeworld.so/play/sword-art-online.N0onT", + "createdAt": "2020-08-02T15:42:44.000Z", + "language": "jp", + "year": "2012", + "dub": False, + "season": "summer", + "totViews": 461576, + "dayViews": 204, + "weekViews": 459, + "monthViews": 6416, + "malId": 11757, + "anilistId": 11757, + "mangaworldId": None, + "malVote": 7.35, + "trailer": "https://www.youtube.com/embed/6ohYYtxfDCg?enablejsapi=1&wmode=opaque", + }, + ... + ] + ``` + +The [find](../../api-reference/developer-interface/#animeworld.find) function returns a list of dictionaries, one for each found anime. Each dictionary contains many details, including the name, episode count, release date, link, etc. + +## Anime + +The [Anime](../../api-reference/developer-interface/#animeworld.Anime) class is the core object of this library. To instantiate it, you need to pass the anime link, obtained directly from the [AnimeWorld](https://www.animeworld.so/) site or from the [find](../../api-reference/developer-interface/#animeworld.find) function seen earlier. + +```python linenums="4" +anime = aw.Anime("https://www.animeworld.so/play/sword-art-online.N0onT") +``` + +!!! warning + If the passed link points to a [404](https://www.animeworld.so/404) page, the [Error404](../../api-reference/exceptions/#animeworld.exceptions.Error404) exception will be raised. + +With this class, you can obtain information about the anime: + +```python linenums="5" +# Title +print("Title:", anime.getName()) +print("----------------------------------\n") + +# Plot +print("Plot:", anime.getTrama()) +print("----------------------------------\n") + +# General information +info = anime.getInfo() +print("General Information:\n", "\n".join( + [f"{x}: {info[x]}" for x in info] +)) +print("----------------------------------\n") +``` + +??? Example "Output" + + ``` + Title: Sword Art Online + ---------------------------------- + + Plot: Kazuto "Kirito" Kirigaya, un genio della programmazione, entra in una realtà virtuale interattiva con pluralità di giocatori (una realtà "massively multi-player online" o "MMO") denominata "Sword Art Online". Il problema sta nel fatto che, una volta entrati, se ne può uscire solo vincitori, completando il gioco, perché il game over equivale a morte certa del giocatore. + ---------------------------------- + + General Information: + Category: Anime + Audio: Giapponese + Release Date: 08 Luglio 2012 + Season: Estate 2012 + Studio: A-1 Pictures + Genre: ['Avventura', 'Azione', 'Fantasy', 'Gioco', 'Romantico', 'Sentimentale'] + Rating: 8.36 / 10 + Duration: 23 min/ep + Episodes: 25 + Status: Finito + Views: 461.733 + ---------------------------------- + ``` + +But most importantly, download the episodes: + +!!! Quote inline end "" + + !!! Info + If no argument is passed to the `getEpisodes()` method, **ALL** episodes of the anime will be obtained. + + !!! Warning + `ep.number` is an attribute of type `str`, more information [here](../../api-reference/developer-interface/#animeworld.Episodio). + + + +```python linenums="19" +# Obtain a list of episodes +# that interest me +episodes = anime.getEpisodes([1, 2, 4]) + + +# And download them +for ep in episodes: + + print(f"Downloading episode {ep.number}.") + + # One at a time... + ep.download() + + print(f"Download completed.") +``` + +??? Example "Output" + + ``` + Downloading episode 1. + Download completed. + Downloading episode 2. + Download completed. + Downloading episode 4. + Download completed. + ``` \ No newline at end of file diff --git a/docs/usage/quickstart.md b/docs/usage/quickstart.it.md similarity index 100% rename from docs/usage/quickstart.md rename to docs/usage/quickstart.it.md diff --git a/mkdocs.yml b/mkdocs.yml index 77d4f7d..a29d2b9 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -3,29 +3,29 @@ site_description: UNOFFICIAL library for AnimeWorld site_url: https://github.com/MainKronos/AnimeWorld-API theme: - name: 'material' - language: it - palette: - - scheme: 'default' - media: '(prefers-color-scheme: light)' - toggle: - icon: 'material/lightbulb' - name: "Switch to dark mode" - - scheme: 'slate' - media: '(prefers-color-scheme: dark)' - primary: 'blue' - toggle: - icon: 'material/lightbulb-outline' - name: 'Switch to light mode' - icon: - repo: fontawesome/brands/github - logo: static/img/logo.svg - favicon: static/img/logo.svg - features: - - navigation.sections - - search.suggest - - content.code.annotate - - content.code.copy + name: 'material' + language: en + palette: + - scheme: 'default' + media: '(prefers-color-scheme: light)' + toggle: + icon: 'material/lightbulb' + name: "Switch to dark mode" + - scheme: 'slate' + media: '(prefers-color-scheme: dark)' + primary: 'blue' + toggle: + icon: 'material/lightbulb-outline' + name: 'Switch to light mode' + icon: + repo: fontawesome/brands/github + logo: static/img/logo.svg + favicon: static/img/logo.svg + features: + - navigation.sections + - search.suggest + - content.code.annotate + - content.code.copy repo_name: MainKronos/AnimeWorld-API repo_url: https://github.com/MainKronos/AnimeWorld-API @@ -57,12 +57,23 @@ markdown_extensions: - pymdownx.superfences plugins: +- i18n: + docs_structure: suffix + fallback_to_default: true + languages: + - locale: en + default: true + name: English + build: true + - locale: it + name: Italiano + build: true - autorefs - git-revision-date-localized: type: date - locale: it + locale: en - search: - lang: it + lang: en - mkdocstrings: default_handler: python handlers: