SickGear/lib/adba/aniDBAbstracter.py
echel0n 4a061d4dd2 AniDB now updates its xml lists daily.
Added writeback cache for both rss feeds and name parser.
2014-07-14 23:55:52 -07:00

282 lines
9.9 KiB
Python

#!/usr/bin/env python
#
# 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 <http://www.gnu.org/licenses/>.
from __future__ import with_statement
from time import time, sleep
import aniDBfileInfo as fileInfo
import xml.etree.cElementTree as etree
import os, re, string
from aniDBmaper import AniDBMaper
from aniDBtvDBmaper import TvDBMap
from aniDBerrors import *
from aniDBfileInfo import read_anidb_xml, read_tvdb_map_xml
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
if self.aniDB:
self.log = self.aniDB.log
else:
self.log = self._fake_log()
def _fake_log(self, x=None):
pass
def _fill(self, dataline):
for key in dataline:
try:
tmpList = dataline[key].split("'")
if len(tmpList) > 1:
newList = []
for i in tmpList:
try:
newList.append(int(i))
except:
newList.append(unicode(i, "utf-8"))
self.__dict__[key] = newList
continue
except:
pass
try:
self.__dict__[key] = int(dataline[key])
except:
self.__dict__[key] = unicode(dataline[key], "utf-8")
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
def _easy_extend(self, initialList, item):
if item:
if isinstance(item, list):
initialList.extend(item)
elif isinstance(item, basestring):
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):
self.maper = AniDBMaper()
self.tvDBMap = TvDBMap()
self.allAnimeXML = None
self.name = name
self.aid = aid
self.tvdb_id = tvdbid
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 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": unicode(line["name"], "utf-8"),
"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()
regex = re.compile(
'( \(\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())
lastAid = 0
for element in self.allAnimeXML.getiterator():
if element.get("aid", False):
lastAid = int(element.get("aid"))
if element.text:
testname = regex.sub('', element.text.lower())
if testname == name:
return lastAid
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()
for anime in self.allAnimeXML.findall("anime"):
if int(anime.get("aid", False)) == aid:
for title in anime.getiterator():
currentLang = title.get("{http://www.w3.org/XML/1998/namespace}lang", False)
currentType = title.get("type", False)
if (currentLang == "en" and not onlyMain) or currentType == "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):
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, status=None):
"""
status:
0 unknown - state is unknown or the user doesn't want to provide this information (default)
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=status)
except Exception, e:
self.log(u"exception msg: " + str(e))
else:
# TODO: add the name or something
self.log(u"Added the episode to anidb")
def _calculate_file_stuff(self, filePath):
if not filePath:
return (None, None)
self.log("Calculating the ed2k. Please wait...")
ed2k = fileInfo.get_file_hash(filePath)
size = fileInfo.get_file_size(filePath)
return (ed2k, size)