anime-downloader/anime_downloader/sites/anime.py

177 lines
4.7 KiB
Python

import requests
from bs4 import BeautifulSoup
import time
import os
import logging
import sys
from anime_downloader.sites.exceptions import AnimeDLError, NotFoundError
from anime_downloader.sites import util
class BaseAnime:
sitename = ''
title = ''
meta = dict()
QUALITIES = None
_episodeClass = object
@classmethod
def search(cls, query):
return
def __init__(self, url, quality='720p'):
self.url = url
if quality in self.QUALITIES:
self.quality = quality
else:
raise AnimeDLError('Quality {0} not found in {1}'.format(quality, self.QUALITIES))
logging.info('Extracting episode info from page')
self.getEpisodes()
@classmethod
def verify_url(self, url):
if self.sitename in url:
return True
return False
def getEpisodes(self):
self._episodeIds = []
r = requests.get(self.url)
soup = BeautifulSoup(r.text, 'html.parser')
try:
self._getMetadata(soup)
except Exception as e:
logging.debug(e)
self._episodeIds = self._getEpisodeUrls(soup)
self._len = len(self._episodeIds)
logging.debug('EPISODE IDS: length: {}, ids: {}'.format(
self._len, self._episodeIds))
return self._episodeIds
def __len__(self):
return self._len
def __getitem__(self, index):
if isinstance(index, int):
ep_id = self._episodeIds[index]
return self._episodeClass(ep_id, self.quality, parent=self,
ep_no=index+1)
elif isinstance(index, slice):
self._episodeIds = self._episodeIds[index]
return self
def __repr__(self):
return '''
Site: {name}
Anime: {title}
Episode count: {length}
'''.format(name=self.sitename, title=self.title, length=len(self))
def __str__(self):
return self.title
def _getEpisodeUrls(self, soup):
return
def _getMetadata(self, soup):
return
class BaseEpisode:
QUALITIES = None
title = ''
stream_url = ''
def __init__(self, episode_id, quality='720p', parent=None,
ep_no=None):
if quality not in self.QUALITIES:
raise AnimeDLError('Incorrect quality: "{}"'.format(quality))
self.ep_no = ep_no
self.episode_id = episode_id
self.quality = quality
logging.debug("Extracting stream info of id: {}".format(self.episode_id))
try:
self.getData()
except NotFoundError:
for quality in parent.QUALITIES:
parent.QUALITIES.remove(self.quality)
logging.warning('Quality {} not found. Trying {}.'.format(self.quality, quality))
self.quality = quality
try:
self.getData()
parent.quality = self.quality
break
except NotFoundError:
pass
def getData(self):
raise NotImplementedError
def download(self, force=False, path=None):
logging.info('Downloading {}'.format(self.title))
file_name = util.slugify(self.title)
if path is None:
path = './' + file_name
if path.endswith('.mp4'):
path = path
else:
path = os.path.join(path, file_name)
logging.info(path)
r = requests.get(self.stream_url, stream=True)
total_size = int(r.headers['Content-length'])
downloaded, chunksize = 0, 2048
start_time = time.time()
if os.path.exists(path):
if os.stat(path).st_size == total_size and not force:
logging.warning('File already downloaded. Skipping download.')
return
else:
os.remove(path)
if r.status_code == 200:
with open(path, 'wb') as f:
for chunk in r.iter_content(chunk_size=chunksize):
if chunk:
f.write(chunk)
downloaded += chunksize
write_status((downloaded), (total_size),
start_time)
class SearchResult:
def __init__(self, title, url, poster):
self.title = title
self.url = url
self.poster = poster
self.meta = ''
def write_status(downloaded, total_size, start_time):
elapsed_time = time.time()-start_time
rate = (downloaded/1024)/elapsed_time
downloaded = float(downloaded)/1048576
total_size = float(total_size)/1048576
status = 'Downloaded: {0:.2f}MB/{1:.2f}MB, Rate: {2:.2f}KB/s'.format(
downloaded, total_size, rate)
sys.stdout.write("\r" + status + " "*5 + "\r")
sys.stdout.flush()