#!/usr/bin/env python # coding=utf-8 # # This file is part of aDBa. # # aDBa is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # aDBa is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with aDBa. If not, see . import logging import os import re from six import string_types import string from . import aniDBfileInfo as fileInfo from .aniDBerrors import * from .aniDBmaper import AniDBMaper from .aniDBtvDBmaper import TvDBMap from .aniDBfileInfo import read_anidb_xml from _23 import decode_str logger = logging.getLogger('adba') logger.addHandler(logging.NullHandler()) class aniDBabstractObject(object): def __init__(self, aniDB, load=False): self.laoded = False self.set_connection(aniDB) if load: self.load_data() def set_connection(self, aniDB): self.aniDB = aniDB def _fill(self, dataline): for key in dataline: try: tmp_list = decode_str(dataline[key]).split("'") if len(tmp_list) > 1: new_list = [] for i in tmp_list: try: new_list.append(int(i)) except: new_list.append(i) self.__dict__[key] = new_list continue except: pass try: self.__dict__[key] = int(dataline[key]) except: # self.__dict__[key] = text_type(dataline[key], "utf-8") self.__dict__[key] = dataline[key] key = property(lambda x: dataline[key]) def __getattr__(self, name): try: return object.__getattribute__(self, name) except: return None def _build_names(self): names = [] names = self._easy_extend(names, self.english_name) names = self._easy_extend(names, self.short_name_list) names = self._easy_extend(names, self.synonym_list) names = self._easy_extend(names, self.other_name) self.allNames = names @staticmethod def _easy_extend(initialList, item): if item: if isinstance(item, list): initialList.extend(item) elif isinstance(item, string_types): initialList.append(item) return initialList def load_data(self): return False def add_notification(self): """ type - Type of notification: type=> 0=all, 1=new, 2=group, 3=complete priority - low = 0, medium = 1, high = 2 (unconfirmed) """ if self.aid: self.aniDB.notifyadd(aid=self.aid, type=1, priority=1) class Anime(aniDBabstractObject): def __init__(self, aniDB, name=None, aid=None, tvdbid=None, paramsA=None, autoCorrectName=False, load=False, cache_path=None): self.maper = AniDBMaper() if cache_path and not os.path.exists(cache_path): os.makedirs(cache_path) self.tvDBMap = TvDBMap(cache_path) self.allAnimeXML = None self.name = name self.aid = aid self.tvdb_id = tvdbid self.cache_path = cache_path if self.tvdb_id and not self.aid: self.aid = self.tvDBMap.get_anidb_for_tvdb(self.tvdb_id) if not (self.name or self.aid): raise AniDBIncorrectParameterError("No aid or name available") if not self.aid: self.aid = self._get_aid_from_xml(self.name) # if not self.name or autoCorrectName: # self.name = self._get_name_from_xml(self.aid) if not (self.name or self.aid): raise ValueError if not self.tvdb_id: self.tvdb_id = self.tvDBMap.get_tvdb_for_anidb(self.aid) if not paramsA: self.bitCode = "b2f0e0fc000000" self.params = self.maper.getAnimeCodesA(self.bitCode) else: self.paramsA = paramsA self.bitCode = self.maper.getAnimeBitsA(self.paramsA) super(Anime, self).__init__(aniDB, load) def load_data(self): """load the data from anidb""" if not (self.name or self.aid): raise ValueError self.rawData = self.aniDB.anime(aid=self.aid, aname=self.name, amask=self.bitCode) if self.rawData.datalines: self._fill(self.rawData.datalines[0]) self._builPreSequal() self.laoded = True def return_raw_data(self): """Returns all raw data from request""" return self.rawData def get_groups(self): if not self.aid: return [] self.rawData = self.aniDB.groupstatus(aid=self.aid) self.release_groups = [] for line in self.rawData.datalines: self.release_groups.append({"name": line["name"], "rating": line["rating"], "range": line["episode_range"] }) return self.release_groups # TODO: refactor and use the new functions in anidbFileinfo def _get_aid_from_xml(self, name): if not self.allAnimeXML: self.allAnimeXML = read_anidb_xml(self.cache_path) regex = re.compile(r'( \(\d{4}\))|[%s]' % re.escape(string.punctuation)) # remove any punctuation and e.g. ' (2011)' # regex = re.compile('[%s]' % re.escape(string.punctuation)) # remove any punctuation and e.g. ' (2011)' name = regex.sub('', name.lower()) last_aid = 0 for element in self.allAnimeXML.iter(): if element.get("aid", False): last_aid = int(element.get("aid")) if element.text: testname = regex.sub('', element.text.lower()) if testname == name: return last_aid return 0 # TODO: refactor and use the new functions in anidbFileinfo def _get_name_from_xml(self, aid, onlyMain=True): if not self.allAnimeXML: self.allAnimeXML = read_anidb_xml(self.cache_path) for anime in self.allAnimeXML.findall("anime"): if int(anime.get("aid", False)) == aid: for title in anime.getiterator(): current_lang = title.get("{http://www.w3.org/XML/1998/namespace}lang", False) current_type = title.get("type", False) if (current_lang == "en" and not onlyMain) or current_type == "main": return title.text return "" def _builPreSequal(self): if self.related_aid_list and self.related_aid_type: try: for i in range(len(self.related_aid_list)): if self.related_aid_type[i] == 2: self.__dict__["prequal"] = self.related_aid_list[i] elif self.related_aid_type[i] == 1: self.__dict__["sequal"] = self.related_aid_list[i] except: if self.related_aid_type == 2: self.__dict__["prequal"] = self.related_aid_list elif self.str_related_aid_type == 1: self.__dict__["sequal"] = self.related_aid_list class Episode(aniDBabstractObject): def __init__(self, aniDB, number=None, epid=None, filePath=None, fid=None, epno=None, paramsA=None, paramsF=None, load=False, calculate=False): if not aniDB and not number and not epid and not filePath and not fid: return self.maper = AniDBMaper() self.epid = epid self.filePath = filePath self.fid = fid self.epno = epno if calculate: (self.ed2k, self.size) = self._calculate_file_stuff(self.filePath) if not paramsA: self.bitCodeA = "C000F0C0" self.paramsA = self.maper.getFileCodesA(self.bitCodeA) else: self.paramsA = paramsA self.bitCodeA = self.maper.getFileBitsA(self.paramsA) if not paramsF: self.bitCodeF = "7FF8FEF8" self.paramsF = self.maper.getFileCodesF(self.bitCodeF) else: self.paramsF = paramsF self.bitCodeF = self.maper.getFileBitsF(self.paramsF) super(Episode, self).__init__(aniDB, load) def load_data(self): """load the data from anidb""" if self.filePath and not (self.ed2k or self.size): (self.ed2k, self.size) = self._calculate_file_stuff(self.filePath) self.rawData = self.aniDB.file(fid=self.fid, size=self.size, ed2k=self.ed2k, aid=self.aid, aname=None, gid=None, gname=None, epno=self.epno, fmask=self.bitCodeF, amask=self.bitCodeA) self._fill(self.rawData.datalines[0]) self._build_names() self.laoded = True def add_to_mylist(self, state=None, viewed=None, source=None, storage=None, other=None): """ state - the location of the file viewed - whether you have watched the file (0=unwatched,1=watched) source - where you got the file (bittorrent,dc++,ed2k,...) storage - for example the title of the cd you have this on other - other data regarding this file structure of state: value meaning 0 unknown - state is unknown or the user doesn't want to provide this information 1 on hdd - the file is stored on hdd 2 on cd - the file is stored on cd 3 deleted - the file has been deleted or is not available for other reasons (i.e. reencoded) """ if self.filePath and not (self.ed2k or self.size): (self.ed2k, self.size) = self._calculate_file_stuff(self.filePath) try: self.aniDB.mylistadd(size=self.size, ed2k=self.ed2k, state=state, viewed=viewed, source=source, storage=storage, other=other) except Exception as e: logger.exception("Exception: %s" % e) else: # TODO: add the name or something logger.info("Added the episode to anidb") def delete_from_mylist(self): if self.filePath and not (self.ed2k or self.size): (self.ed2k, self.size) = self._calculate_file_stuff(self.filePath) try: self.aniDB.mylistdel(size=self.size, ed2k=self.ed2k) except Exception as e: logger.exception("Exception: %s" % e) else: logger.info("Deleted the episode from anidb") @staticmethod def _calculate_file_stuff(filePath): if not filePath: return None, None logger.info("Calculating the ed2k. Please wait...") ed2k = fileInfo.get_ED2K(filePath) size = fileInfo.get_file_size(filePath) return ed2k, size