Skip to content

Commit

Permalink
1.6.4
Browse files Browse the repository at this point in the history
  • Loading branch information
MainKronos committed Aug 22, 2023
1 parent 676a963 commit a13c571
Show file tree
Hide file tree
Showing 8 changed files with 157 additions and 80 deletions.
3 changes: 2 additions & 1 deletion animeworld/episodio.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from bs4 import BeautifulSoup
from typing import *
import time
import io

from .utility import SES
from .exceptions import ServerNotSupported, HardStoppedDownload
Expand Down Expand Up @@ -101,7 +102,7 @@ def fileInfo(self) -> Dict[str,str]:

raise err

def download(self, title: Optional[str]=None, folder: str='', *, hook: Callable[[Dict], None]=lambda *args:None, opt: List[str]=[]) -> Optional[str]: # Scarica l'episodio con il primo link nella lista
def download(self, title: Optional[str]=None, folder: Union[str, io.IOBase]='', *, hook: Callable[[Dict], None]=lambda *args:None, opt: List[str]=[]) -> Optional[str]: # Scarica l'episodio con il primo link nella lista
"""
Scarica l'episodio dal server più veloce.
Expand Down
2 changes: 1 addition & 1 deletion animeworld/servers/AnimeWorld_Server.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ def fileInfo(self) -> Dict[str,str]:

return self._fileInfoIn()

def download(self, title: Optional[str]=None, folder: str='', *, hook: Callable[[Dict], None]=lambda *args:None, opt: List[str]=[]) -> Optional[str]:
def download(self, title: Optional[str]=None, folder: Union[str, io.IOBase]='', *, hook: Callable[[Dict], None]=lambda *args:None, opt: List[str]=[]) -> Optional[str]:
"""
Scarica l'episodio.
Expand Down
92 changes: 54 additions & 38 deletions animeworld/servers/Server.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import os, time
from typing import Dict, List, Optional, Callable
import os, time, io
from typing import Dict, List, Optional, Callable, Union
from datetime import datetime
import youtube_dl

Expand Down Expand Up @@ -143,7 +143,7 @@ def error(self, msg):
}


def download(self, title: Optional[str]=None, folder: str='', *, hook: Callable[[Dict], None]=lambda *args:None, opt: List[str]=[]) -> Optional[str]:
def download(self, title: Optional[str]=None, folder: Union[str, io.IOBase]='', *, hook: Callable[[Dict], None]=lambda *args:None, opt: List[str]=[]) -> Optional[str]:
"""
Scarica l'episodio dal primo server funzionante della lista links.
Expand Down Expand Up @@ -180,7 +180,7 @@ def download(self, title: Optional[str]=None, folder: str='', *, hook: Callable[
raise ServerNotSupported(self.name)

# Protected
def _downloadIn(self, title: str, folder: str, *, hook: Callable[[Dict], None], opt: List[str]) -> Optional[str]: # Scarica l'episodio
def _downloadIn(self, title: str, folder: Union[str, io.IOBase], *, hook: Callable[[Dict], None], opt: List[str]) -> Optional[str]: # Scarica l'episodio

"""
Scarica il file utilizzando httpx.
Expand Down Expand Up @@ -225,48 +225,56 @@ def _downloadIn(self, title: str, folder: str, *, hook: Callable[[Dict], None],
start = time.time()
step = time.time()

fd:io.IOBase = None
if isinstance(folder, io.IOBase): fd = folder
else: fd = open(f"{os.path.join(folder,file)}", 'wb')

try:
with open(f"{os.path.join(folder,file)}", 'wb') as f:
for chunk in r.iter_bytes(chunk_size = 524288):
if chunk:
f.write(chunk)
f.flush()

current_lenght += len(chunk)

hook({
'total_bytes': total_length,
'downloaded_bytes': current_lenght,
'percentage': current_lenght/total_length,
'speed': len(chunk) / (time.time() - step) if (time.time() - step) != 0 else 0,
'elapsed': time.time() - start,
'filename': file,
'eta': ((total_length - current_lenght) / len(chunk)) * (time.time() - step),
'status': 'downloading' if "abort" not in opt else "aborted"
})

if "abort" in opt: raise HardStoppedDownload(file)

step = time.time()

else:
for chunk in r.iter_bytes(chunk_size = 524288):
if chunk:
fd.write(chunk)
fd.flush()

current_lenght += len(chunk)

hook({
'total_bytes': total_length,
'downloaded_bytes': total_length,
'percentage': 1,
'speed': 0,
'downloaded_bytes': current_lenght,
'percentage': current_lenght/total_length,
'speed': len(chunk) / (time.time() - step) if (time.time() - step) != 0 else 0,
'elapsed': time.time() - start,
'eta': 0,
'status': 'finished'
'filename': file,
'eta': ((total_length - current_lenght) / len(chunk)) * (time.time() - step),
'status': 'downloading' if "abort" not in opt else "aborted"
})

return file # Se il file è stato scaricato correttamente
if "abort" in opt: raise HardStoppedDownload(file)

step = time.time()

else:
hook({
'total_bytes': total_length,
'downloaded_bytes': total_length,
'percentage': 1,
'speed': 0,
'elapsed': time.time() - start,
'eta': 0,
'status': 'finished'
})

if isinstance(folder, str): fd.close()
else: fd.seek(0)
return file # Se il file è stato scaricato correttamente
except HardStoppedDownload:
os.remove(f"{os.path.join(folder,file)}")
if isinstance(folder, str):
fd.close()
os.remove(f"{os.path.join(folder,file)}")
else: fd.seek(0)
return None

# Protected
def _dowloadEx(self, title: str, folder: str, *, hook: Callable[[Dict], None], opt: List[str]) -> Optional[str]:
def _dowloadEx(self, title: str, folder: Union[str, io.IOBase], *, hook: Callable[[Dict], None], opt: List[str]) -> Optional[str]:
"""
Scarica il file utilizzando yutube_dl.
Expand Down Expand Up @@ -300,6 +308,9 @@ def _dowloadEx(self, title: str, folder: str, *, hook: Callable[[Dict], None], o
```
"""

tmp = ''
if isinstance(folder, str): tmp = folder

class MyLogger(object):
def debug(self, msg):
pass
Expand All @@ -325,7 +336,7 @@ def my_hook(d):
if "abort" in opt: raise HardStoppedDownload(d['filename'])

ydl_opts = {
'outtmpl': f"{os.path.join(folder,title)}.%(ext)s",
'outtmpl': f"{os.path.join(tmp,title)}.%(ext)s",
'logger': MyLogger(),
'progress_hooks': [my_hook],
}
Expand All @@ -336,6 +347,11 @@ def my_hook(d):
try:
ydl.download([url])
except HardStoppedDownload:
os.remove(f"{os.path.join(folder,filename)}")
os.remove(f"{os.path.join(tmp,filename)}")
return None
if isinstance(folder, io.IOBase):
with open(os.path.join(tmp,filename), 'rb') as f:
folder.write(f.read())
f.seek(0)
os.remove(f"{os.path.join(tmp,filename)}")
return filename
2 changes: 1 addition & 1 deletion animeworld/servers/Streamtape.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ def fileInfo(self) -> Dict[str,str]:

return self._fileInfoIn()

def download(self, title: Optional[str]=None, folder: str='', *, hook: Callable[[Dict], None]=lambda *args:None, opt: List[str]=[]) -> Optional[str]:
def download(self, title: Optional[str]=None, folder: Union[str, io.IOBase]='', *, hook: Callable[[Dict], None]=lambda *args:None, opt: List[str]=[]) -> Optional[str]:
"""
Scarica l'episodio.
Expand Down
2 changes: 1 addition & 1 deletion animeworld/servers/YouTube.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ def fileInfo(self) -> Dict[str,str]:

return self._fileInfoEx()

def download(self, title: Optional[str]=None, folder: str='', *, hook: Callable[[Dict], None]=lambda *args:None, opt: List[str]=[]) -> Optional[str]:
def download(self, title: Optional[str]=None, folder: Union[str, io.IOBase]='', *, hook: Callable[[Dict], None]=lambda *args:None, opt: List[str]=[]) -> Optional[str]:
"""
Scarica l'episodio.
Expand Down
18 changes: 18 additions & 0 deletions docs/usage/advanced.md
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,24 @@ episodio.download(opt=opt) # Avvio il download

In questo esempio il download viene fermato dopo 5 secondi.

### I/O Buffer

È possibile scaricare un episodio usando direttamente un descrittore di file invece che una stringa per la directory. Basta passare al parametro `folder` un tipo [IOBase](https://docs.python.org/3/library/io.html#i-o-base-classes).

```py linenums="1"
import animeworld as aw
import io

anime = aw.Anime("...")
episodio = anime.getEpisodes()[0]

buffer = io.BytesIO() # Alloco un buffer in memoria

episodio.download(folder=buffer) # Avvio il download
```

In questo esempio l'episodio scaricato viene scritto in memoria senza essere salvato come file.

---

## Esempio completo
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

setuptools.setup(
name="animeworld",
version="1.6.3",
version="1.6.4",
author="MainKronos",
description="AnimeWorld UNOFFICIAL API",
long_description=long_description,
Expand Down
116 changes: 79 additions & 37 deletions tests/test_animeworld.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import unittest

import unittest
import random
import random, io, time
from threading import Thread

import animeworld as aw
from animeworld.servers import AnimeWorld_Server, Streamtape
Expand Down Expand Up @@ -102,45 +103,86 @@ def test_anime(self):
self.assertIn('Durata', info)
self.assertIn('Episodi', info)
self.assertIn('Stato', info)
self.assertIn('Visualizzazioni', info)
self.assertIn('Visualizzazioni', info)

def test_servers(self):
"""
Controlli relativi ai server.
"""
def test_episodio(self):
ep = random.choice(self.anime.getEpisodes())

servers = ep.links

animeworld_server = [e for e in servers if isinstance(e, AnimeWorld_Server)][0]
streamtape_server = [e for e in servers if isinstance(e, Streamtape)][0]

self.assertEqual(animeworld_server.Nid, 9)
self.assertEqual(animeworld_server.name, "AnimeWorld Server")

self.assertEqual(streamtape_server.Nid, 8)
self.assertEqual(streamtape_server.name, "Streamtape")

self.assertIsInstance(animeworld_server.fileLink(), str)
self.assertIsInstance(streamtape_server.fileLink(), str)

animeworld_info = animeworld_server.fileInfo()
self.assertIsInstance(animeworld_info, dict)
self.assertIn("content_type", animeworld_info)
self.assertIn("total_bytes", animeworld_info)
self.assertIn("last_modified", animeworld_info)
self.assertIn("server_name", animeworld_info)
self.assertIn("server_id", animeworld_info)
self.assertIn("url", animeworld_info)

streamtape_info = streamtape_server.fileInfo()
self.assertIsInstance(animeworld_info, dict)
self.assertIn("content_type", streamtape_info)
self.assertIn("total_bytes", streamtape_info)
self.assertIn("last_modified", streamtape_info)
self.assertIn("server_name", streamtape_info)
self.assertIn("server_id", streamtape_info)
self.assertIn("url", streamtape_info)
self.assertIsInstance(ep, aw.Episodio)
self.assertIsInstance(ep.number, str)
self.assertIsInstance(ep.links, list)

class TestServer(unittest.TestCase):
@classmethod
def setUpClass(cls) -> None:
"""Sceglie un episodio per i test."""
cls.episodio = random.choice(aw.Anime("https://www.animeworld.so/play/fullmetal-alchemist-brotherhood.4vGGQ").getEpisodes())

@staticmethod
def stopDownload(opt:list):
time.sleep(1)
opt.append("abort")

def test_AnimeWorld_Server(self) -> None:

servers = [e for e in self.episodio.links if isinstance(e, AnimeWorld_Server)]

if len(servers) == 0:
self.skipTest('Il server AnimeWorld_Server non esiste in questo episodio.')
return

server = servers[0]

self.assertEqual(server.Nid, 9)
self.assertEqual(server.name, "AnimeWorld Server")
self.assertIsInstance(server.fileLink(), str)

info = server.fileInfo()
self.assertIsInstance(info, dict)
self.assertIn("content_type", info)
self.assertIn("total_bytes", info)
self.assertIn("last_modified", info)
self.assertIn("server_name", info)
self.assertIn("server_id", info)
self.assertIn("url", info)

with self.subTest('Animeworld_Server Download'):
buf = io.BytesIO()
opt = []
Thread(target=self.stopDownload, args=(opt,)).start()
self.assertIsNone(server.download(folder=buf, opt=opt))
buf.close()

def test_Streamtape(self) -> None:
servers = [e for e in self.episodio.links if isinstance(e, Streamtape)]

if len(servers) == 0:
self.skipTest('Il server Streamtape non esiste in questo episodio.')
return

server = servers[0]

self.assertEqual(server.Nid, 8)
self.assertEqual(server.name, "Streamtape")


self.assertIsInstance(server.fileLink(), str)

info = server.fileInfo()
self.assertIsInstance(info, dict)
self.assertIn("content_type", info)
self.assertIn("total_bytes", info)
self.assertIn("last_modified", info)
self.assertIn("server_name", info)
self.assertIn("server_id", info)
self.assertIn("url", info)

with self.subTest('Streamtape Download'):
buf = io.BytesIO()
opt = []
Thread(target=self.stopDownload, args=(opt,)).start()
self.assertIsNone(server.download(folder=buf, opt=opt))
buf.close()

if __name__ == '__main__':
unittest.main(verbosity=2)

0 comments on commit a13c571

Please sign in to comment.