Skip to content

Commit

Permalink
Merge pull request #7 from MainKronos/dev
Browse files Browse the repository at this point in the history
1.5.0
  • Loading branch information
MainKronos authored Mar 27, 2023
2 parents 4580081 + 8c2ff74 commit 84f26bf
Show file tree
Hide file tree
Showing 12 changed files with 368 additions and 225 deletions.
84 changes: 84 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# Contributing

## Server

Questa sezione spiega come aggiungere alla libreia la possibilità di scaricare un episodio da un'altro server non ancora supportato.

|Name|Supported|
|:---|:-------:|
|AnimeWorld_Server|✔️|
|Streamtape|✔️|
|YouTube|✔️|
|StreamHide||
|FileMoon||
|Streamtape||
|Doodstream||
|StreamSB||
|Streamlare||

Per aggiungere un nuovo server basta seguire questi passi:
1. Creare un file .py con il nome del server e metterlo nella cartella [servers](https://github.com/MainKronos/AnimeWorld-API/tree/master/animeworld/servers). (es `NuovoServer.py`)
2. Usa questo template per la classe del nuovo server:
```py
from .Server import *

class NuovoServer(Server):
def fileLink(self): # Obbligatoria
"""
Recupera il link diretto per il download del file dell'episodio.
```
return str # Link del file
```
"""
pass #TODO: da completare

def fileInfo(self) -> Dict[str,str]: # Opzionale
"""
Recupera le informazioni del file dell'episodio.
```
return {
"content_type": str, # Tipo del file, es. video/mp4
"total_bytes": int, # Byte totali del file
"last_modified": datetime, # Data e ora dell'ultimo aggiornamento effettuato all'episodio sul server
"server_name": str, # Nome del server
"server_id": int, # ID del server
"url": str # url dell'episodio
}
```
"""
pass #TODO: da completare

def download(self, title: Optional[str]=None, folder: str='', *, hook: Callable[[Dict], None]=lambda *args:None, opt: List[str]=[]) -> Optional[str]: # Obbligatoria
"""
Scarica l'episodio.
- `title`: Nome con cui verrà nominato il file scaricato.
- `folder`: Posizione in cui verrà spostato il file scaricato.
- `hook`: Funzione che viene richiamata varie volte durante il download; la funzione riceve come argomento un dizionario con le seguenti chiavi:
- `total_bytes`: Byte totali da scaricare.
- `downloaded_bytes`: Byte attualmente scaricati.
- `percentage`: Percentuale del progresso di download.
- `speed`: Velocità di download (byte/s)
- `elapsed`: Tempo trascorso dall'inizio del download.
- `eta`: Tempo stimato rimanente per fine del download.
- `status`: 'downloading' | 'finished' | 'aborted'
- `filename`: Nome del file in download.
- `opt`: Lista per delle opzioni aggiuntive.
- `'abort'`: Ferma forzatamente il download.
```
return str # File scaricato
```
"""
if title is None: title = self._defTitle
else: title = self._sanitize(title)

pass #TODO: da completare
```
3. Completare le varie funzioni (quelle segnate con `Opzionale` possono anche non essere completate), prendendo anche spunto dai vari server caricati nella cartella.
4. Aggiungi la linea `from .NuovoServer import NuovoServer` al file [servers/__init__.py](https://github.com/MainKronos/AnimeWorld-API/tree/master/animeworld/servers/__init__.py).
5. 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).

Se tutto funziona correttamente apri una richiesta di [pull](https://github.com/MainKronos/AnimeWorld-API/pulls).
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,5 @@ Per un utilizzo avanzato consultare la [documentazione](https://github.com/MainK

## Contributing
Se volete contribuire aprendo Issue o Pull a questa libreria siete ben accetti, tutto il codice sorgente e la documentazione è reperible su [GitHub](https://github.com/MainKronos/AnimeWorld-API).

Se volete aggiungere un nuovo server da cui scaricare gli episodio, allora leggere la sezione [contributing](https://github.com/MainKronos/AnimeWorld-API/blob/master/CONTRIBUTING.md).
2 changes: 1 addition & 1 deletion animeworld/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@
from .utility import find
from .anime import Anime
from .episodio import Episodio
from .server import Server
from .servers import Server
from .exceptions import ServerNotSupported, AnimeNotAvailable, Error404, DeprecatedLibrary
48 changes: 33 additions & 15 deletions animeworld/episodio.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@
import requests
from bs4 import BeautifulSoup
from typing import *
import time

from .utility import SES
from .exceptions import ServerNotSupported
from .server import Server, AnimeWorld_Server, YouTube, Streamtape

from .servers import Server, AnimeWorld_Server, YouTube, Streamtape

class Episodio:
"""
Expand Down Expand Up @@ -120,17 +120,7 @@ def download(self, title: Optional[str]=None, folder: str='', *, hook: Callable[
```
"""

file = ""
err = None
for server in self.links:
try:
file = server.download(title,folder,hook=hook,opt=opt)
except (ServerNotSupported, requests.exceptions.RequestException) as e:
err = e
else:
return file

raise err
return self.__choiceBestServer().download(title,folder,hook=hook,opt=opt)

# Private
def __setServer(self, links: List[Dict], numero: str) -> List[Server]: # Per ogni link li posizioni nelle rispettive classi
Expand All @@ -147,7 +137,7 @@ def __setServer(self, links: List[Dict], numero: str) -> List[Server]: # Per ogn
]
```
"""
ret = [] # lista dei server
ret: List[Server] = [] # lista dei server
for prov in links:
if prov["id"] == 4:
ret.append(YouTube(prov["link"], prov["id"], prov["name"], numero))
Expand All @@ -168,4 +158,32 @@ def __sortServer(self, elem):
if isinstance(elem, YouTube): return 0
elif isinstance(elem, AnimeWorld_Server): return 1
elif isinstance(elem, Streamtape): return 2
else: return 4
else: return 4

def __choiceBestServer(self) -> Server:
"""
Sceglie il server più veloce per il download dell'episodio.
"""
servers = self.links

speed_test = [{
"server": x,
"bytes": -1
} for x in servers]

max_time = 0.5 # numero di secondi massimo

for test in speed_test:
try:
start = time.perf_counter()
with SES.get(test["server"].fileLink(), stream = True, timeout=0.9) as r:
for chunk in r.iter_content(chunk_size = 2048):
if time.perf_counter() - start > max_time: break
test["bytes"] += len(chunk)
except (ServerNotSupported, requests.exceptions.RequestException):
continue

speed_test = [x for x in speed_test if x["bytes"] != -1] # tolgo tutti i server che hanno generato un eccezione
if len(speed_test) == 0: return servers[0] # ritorno al caso standard

return max(speed_test, key=lambda x: x["bytes"])["server"] # restituisco il server che ha scaricato più byte in `max_time` secondi
7 changes: 4 additions & 3 deletions animeworld/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ def __init__(self, file, funName, line):
super().__init__(self.message)

class HardStoppedDownload(Exception):
"""Il file in download è stato forsatamente interrotto."""
def __init__(self):
self.message = "Il file in download è stato forsatamente interrotto."
"""Il file in download è stato forzatamente interrotto."""
def __init__(self, file:str):
self.file = file
self.message = f"Il file in download ({file}) è stato forzatamente interrotto."
super().__init__(self.message)
62 changes: 62 additions & 0 deletions animeworld/servers/AnimeWorld_Server.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
from bs4 import BeautifulSoup
import re

from .Server import *
from ..utility import HealthCheck

class AnimeWorld_Server(Server):
@HealthCheck
def fileLink(self) -> str:
"""
Recupera il link diretto per il download del file dell'episodio.
```
return str # Link del file
```
"""

return self.link.replace('download-file.php?id=', '')

def fileInfo(self) -> Dict[str,str]:
"""
Recupera le informazioni del file dell'episodio.
```
return {
"content_type": str, # Tipo del file, es. video/mp4
"total_bytes": int, # Byte totali del file
"last_modified": datetime, # Data e ora dell'ultimo aggiornamento effettuato all'episodio sul server
"server_name": str, # Nome del server
"server_id": int, # ID del server
"url": str # url dell'episodio
}
```
"""

return self._fileInfoIn()

def download(self, title: Optional[str]=None, folder: str='', *, hook: Callable[[Dict], None]=lambda *args:None, opt: List[str]=[]) -> Optional[str]:
"""
Scarica l'episodio.
- `title`: Nome con cui verrà nominato il file scaricato.
- `folder`: Posizione in cui verrà spostato il file scaricato.
- `hook`: Funzione che viene richiamata varie volte durante il download; la funzione riceve come argomento un dizionario con le seguenti chiavi:
- `total_bytes`: Byte totali da scaricare.
- `downloaded_bytes`: Byte attualmente scaricati.
- `percentage`: Percentuale del progresso di download.
- `speed`: Velocità di download (byte/s)
- `elapsed`: Tempo trascorso dall'inizio del download.
- `eta`: Tempo stimato rimanente per fine del download.
- `status`: 'downloading' | 'finished' | 'aborted'
- `filename`: Nome del file in download.
- `opt`: Lista per delle opzioni aggiuntive.
- `'abort'`: Ferma forzatamente il download.
```
return str # File scaricato
```
"""
if title is None: title = self._defTitle
else: title = self._sanitize(title)
return self._downloadIn(title,folder,hook=hook,opt=opt)
Loading

0 comments on commit 84f26bf

Please sign in to comment.