mirror of
https://github.com/SickGear/SickGear.git
synced 2024-12-22 10:43:38 +00:00
849 lines
27 KiB
Python
849 lines
27 KiB
Python
|
#!/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 <http://www.gnu.org/licenses/>.
|
||
|
import logging
|
||
|
import os
|
||
|
import sys
|
||
|
import threading
|
||
|
from datetime import timedelta
|
||
|
from time import sleep, time
|
||
|
|
||
|
from _23 import ConfigParser
|
||
|
|
||
|
from .aniDBlink import AniDBLink
|
||
|
from .aniDBcommands import *
|
||
|
from .aniDBerrors import *
|
||
|
from .aniDBAbstracter import Anime, Episode
|
||
|
|
||
|
version = 100
|
||
|
|
||
|
logger = logging.getLogger('adba')
|
||
|
logger.addHandler(logging.NullHandler())
|
||
|
|
||
|
|
||
|
class Connection(threading.Thread):
|
||
|
def __init__(self, clientname='adba', server='api.anidb.info', port=9000, myport=9876, user=None, password=None, session=None, keepAlive=False, commandDelay=4.1):
|
||
|
super(Connection, self).__init__()
|
||
|
|
||
|
self.link = AniDBLink(server, port, myport, delay=commandDelay)
|
||
|
self.link.session = session
|
||
|
self.clientname = clientname
|
||
|
self.clientver = version
|
||
|
|
||
|
# from original lib
|
||
|
self.mode = 1 # mode: 0=queue,1=unlock,2=callback
|
||
|
|
||
|
# Filename to maintain session info always in script directory
|
||
|
self.SessionFile = os.path.normpath(sys.path[0] + os.sep + "Session.cfg")
|
||
|
|
||
|
# to lock other threads out
|
||
|
self.lock = threading.RLock()
|
||
|
|
||
|
# last Command Time set to now even though no commands are sent yet
|
||
|
self.LastCommandTime = time()
|
||
|
|
||
|
# thread keep alive stuff
|
||
|
self.keepAlive = keepAlive
|
||
|
self.daemon = True
|
||
|
self.lastKeepAliveCheck = 0
|
||
|
self.lastAuth = 0
|
||
|
self._username = password
|
||
|
self._password = user
|
||
|
|
||
|
self._iamALIVE = False
|
||
|
|
||
|
self.counter = 0
|
||
|
self.counterAge = 0
|
||
|
|
||
|
def stop(self):
|
||
|
self.logout(cutConnection=True)
|
||
|
|
||
|
def cut(self):
|
||
|
self.link.stop()
|
||
|
|
||
|
def handle_response(self, response):
|
||
|
if response.rescode in ('501', '506') and response.req.command != 'AUTH':
|
||
|
logger.debug("seems like the last command got a not authed error back trying to reconnect now")
|
||
|
if self._reAuthenticate():
|
||
|
response.req.resp = None
|
||
|
self.handle(response.req, response.req.callback)
|
||
|
|
||
|
def handle(self, command, callback):
|
||
|
|
||
|
self.lock.acquire()
|
||
|
|
||
|
if self.counterAge < (time() - 120): # the last request was older then 2 min reset delay and counter
|
||
|
self.counter = 0
|
||
|
self.link.delay = 2
|
||
|
else: # something happened in the last 120 seconds
|
||
|
if self.counter < 5:
|
||
|
self.link.delay = 2 # short term "A Client MUST NOT send more than 0.5 packets per second (that's one packet every two seconds, not two packets a second!)"
|
||
|
elif self.counter >= 5:
|
||
|
self.link.delay = 6 # long term "A Client MUST NOT send more than one packet every four seconds over an extended amount of time."
|
||
|
|
||
|
if command.command not in ('AUTH', 'PING', 'ENCRYPT'):
|
||
|
self.counterAge = time()
|
||
|
self.counter += 1
|
||
|
if self.keepAlive:
|
||
|
self.authed()
|
||
|
|
||
|
def callback_wrapper(resp):
|
||
|
self.handle_response(resp)
|
||
|
if callback:
|
||
|
callback(resp)
|
||
|
|
||
|
logger.debug("handling({counter}-{delay}) command {command}".format(counter=self.counter, delay=self.link.delay, command=command.command))
|
||
|
|
||
|
# make live request
|
||
|
command.authorize(self.mode, self.link.new_tag(), self.link.session, callback_wrapper)
|
||
|
self.link.request(command)
|
||
|
|
||
|
# handle mode 1 (wait for response)
|
||
|
if self.mode == 1:
|
||
|
command.wait_response()
|
||
|
try:
|
||
|
command.resp
|
||
|
except:
|
||
|
self.lock.release()
|
||
|
if self.link.banned:
|
||
|
raise AniDBBannedError("User is banned")
|
||
|
else:
|
||
|
raise AniDBCommandTimeoutError("Command has timed out")
|
||
|
|
||
|
self.handle_response(command.resp)
|
||
|
self.lock.release()
|
||
|
return command.resp
|
||
|
else:
|
||
|
self.lock.release()
|
||
|
|
||
|
def authed(self, reAuthenticate=False):
|
||
|
self.lock.acquire()
|
||
|
authed = (self.link.session not in (None, ''))
|
||
|
if not authed and (reAuthenticate or self.keepAlive):
|
||
|
self._reAuthenticate()
|
||
|
authed = (self.link.session not in (None, ''))
|
||
|
self.lock.release()
|
||
|
return authed
|
||
|
|
||
|
def _reAuthenticate(self):
|
||
|
if self._username and self._password:
|
||
|
logger.info("auto re authenticating !")
|
||
|
resp = self.auth(self._username, self._password)
|
||
|
if resp and resp.rescode != '500':
|
||
|
return True
|
||
|
else:
|
||
|
return False
|
||
|
|
||
|
def _keep_alive(self):
|
||
|
self.lastKeepAliveCheck = time()
|
||
|
logger.info("auto check !")
|
||
|
# check every 30 minutes if the session is still valid
|
||
|
# if not reauthenticate
|
||
|
if self.lastAuth and time() - self.lastAuth > 1800:
|
||
|
logger.info("auto uptime !")
|
||
|
self.uptime() # this will update the self.link.session and will refresh the session if it is still alive
|
||
|
|
||
|
if self.authed(): # if we are authed we set the time
|
||
|
self.lastAuth = time()
|
||
|
else: # if we aren't authed and we have the user and pw then reauthenticate
|
||
|
self._reAuthenticate()
|
||
|
|
||
|
# issue a ping every 20 minutes after the last package
|
||
|
# this ensures the connection will be kept alive
|
||
|
if self.link.lastpacket and time() - self.link.lastpacket > 1200:
|
||
|
logger.info("auto ping !")
|
||
|
self.ping()
|
||
|
|
||
|
def run(self):
|
||
|
while self.keepAlive:
|
||
|
self._keep_alive()
|
||
|
sleep(120)
|
||
|
|
||
|
def auth(self, username, password, nat=None, mtu=None, callback=None):
|
||
|
"""
|
||
|
Login to AniDB UDP API
|
||
|
|
||
|
parameters:
|
||
|
username - your anidb username
|
||
|
password - your anidb password
|
||
|
nat - if this is 1, response will have "address" in attributes with your "ip:port" (default:0)
|
||
|
mtu - maximum transmission unit (max packet size) (default: 1400)
|
||
|
|
||
|
"""
|
||
|
|
||
|
if self.keepAlive:
|
||
|
self._username = username
|
||
|
self._password = password
|
||
|
if False is self.is_alive():
|
||
|
logger.debug("You wanted to keep this thing alive!")
|
||
|
if False is self._iamALIVE:
|
||
|
logger.info("Starting thread now...")
|
||
|
self.start()
|
||
|
self._iamALIVE = True
|
||
|
else:
|
||
|
logger.info("not starting thread seems like it is already running. this must be a _reAuthenticate")
|
||
|
|
||
|
config = ConfigParser()
|
||
|
config.read(self.SessionFile)
|
||
|
needauth = False
|
||
|
|
||
|
try:
|
||
|
if config.getboolean('DEFAULT', 'loggedin'):
|
||
|
self.lastCommandTime = config.getfloat('DEFAULT', 'lastcommandtime')
|
||
|
timeelapsed = time() - self.lastCommandTime
|
||
|
timeoutduration = timedelta(minutes=30).seconds
|
||
|
if timeelapsed < timeoutduration:
|
||
|
# we are logged in and within timeout so set up session key and assume valid
|
||
|
self.link.session = config.get('DEFAULT', 'sessionkey')
|
||
|
if not self.link.session:
|
||
|
needauth = True
|
||
|
else:
|
||
|
needauth = True
|
||
|
else:
|
||
|
needauth = True
|
||
|
except:
|
||
|
needauth = True
|
||
|
|
||
|
if needauth:
|
||
|
self.lastAuth = time()
|
||
|
logger.debug('No valid session, so authenticating')
|
||
|
try:
|
||
|
self.handle(AuthCommand(username, password, 3, self.clientname, self.clientver, nat, 1, 'utf8', mtu), callback)
|
||
|
except Exception as error:
|
||
|
logger.debug('Auth command with exception %r' % error)
|
||
|
# we force a config file with logged out to ensure a known state if an exception occurs, forcing us to log in again
|
||
|
config['DEFAULT'] = {'loggedin': 'yes', 'sessionkey': str(self.link.session or ''), 'exception': str(error),
|
||
|
'lastcommandtime': repr(time())}
|
||
|
with open(self.SessionFile, 'w') as configfile:
|
||
|
config.write(configfile)
|
||
|
return error
|
||
|
logger.debug('Successfully authenticated and recording session details')
|
||
|
config['DEFAULT'] = {'loggedin': 'yes', 'sessionkey': str(self.link.session or ''), 'lastcommandtime': repr(time())}
|
||
|
with open(self.SessionFile, 'w') as configfile:
|
||
|
config.write(configfile)
|
||
|
return
|
||
|
|
||
|
def logout(self, cutConnection=True, callback=None):
|
||
|
"""
|
||
|
Log out from AniDB UDP API
|
||
|
|
||
|
"""
|
||
|
config = ConfigParser()
|
||
|
config.read(self.SessionFile)
|
||
|
if config['DEFAULT']['loggedin'] == 'yes':
|
||
|
self.link.session = config.get('DEFAULT', 'sessionkey')
|
||
|
result = self.handle(LogoutCommand(), callback)
|
||
|
if cutConnection:
|
||
|
self.cut()
|
||
|
config['DEFAULT']['loggedin'] = 'no'
|
||
|
with open(self.SessionFile, 'w') as configfile:
|
||
|
config.write(configfile)
|
||
|
logger.debug('Logging out')
|
||
|
return result
|
||
|
logger.debug('Not logging out')
|
||
|
return
|
||
|
|
||
|
def stayloggedin(self):
|
||
|
"""
|
||
|
handles timeout constraints of the link before exiting
|
||
|
"""
|
||
|
config = ConfigParser()
|
||
|
config.read(self.SessionFile)
|
||
|
config['DEFAULT']['lastcommandtime'] = repr(time())
|
||
|
with open(self.SessionFile, 'w') as configfile:
|
||
|
config.write(configfile)
|
||
|
self.link._do_delay()
|
||
|
logger.debug('Staying logged in')
|
||
|
return
|
||
|
|
||
|
def push(self, notify, msg, buddy=None, callback=None):
|
||
|
"""
|
||
|
Subscribe/unsubscribe to/from notifications
|
||
|
|
||
|
parameters:
|
||
|
notify - Notifications about files added?
|
||
|
msg - Notifications about message added?
|
||
|
buddy - Notifications about buddy events?
|
||
|
|
||
|
structure of parameters:
|
||
|
notify msg [buddy]
|
||
|
|
||
|
"""
|
||
|
return self.handle(PushCommand(notify, msg, buddy), callback)
|
||
|
|
||
|
def pushack(self, nid, callback=None):
|
||
|
"""
|
||
|
Acknowledge notification (do this when you get 271-274)
|
||
|
|
||
|
parameters:
|
||
|
nid - Notification packet id
|
||
|
|
||
|
structure of parameters:
|
||
|
nid
|
||
|
|
||
|
"""
|
||
|
return self.handle(PushAckCommand(nid), callback)
|
||
|
|
||
|
def notifyadd(self, aid=None, gid=None, type=None, priority=None, callback=None):
|
||
|
"""
|
||
|
Add a notification
|
||
|
|
||
|
parameters:
|
||
|
aid - Anime id
|
||
|
gid - Group id
|
||
|
type - Type of notification: type=> 0=all, 1=new, 2=group, 3=complete
|
||
|
priority - low = 0, medium = 1, high = 2 (unconfirmed)
|
||
|
|
||
|
structure of parameters:
|
||
|
[aid={int}|gid={int}]&type={int}&priority={int}
|
||
|
|
||
|
"""
|
||
|
|
||
|
return self.handle(NotifyAddCommand(aid, gid, type, priority), callback)
|
||
|
|
||
|
def notify(self, buddy=None, callback=None):
|
||
|
"""
|
||
|
Get number of pending notifications and messages
|
||
|
|
||
|
parameters:
|
||
|
buddy - Also display number of online buddies
|
||
|
|
||
|
structure of parameters:
|
||
|
[buddy]
|
||
|
|
||
|
"""
|
||
|
return self.handle(NotifyCommand(buddy), callback)
|
||
|
|
||
|
def notifylist(self, callback=None):
|
||
|
"""
|
||
|
List all pending notifications/messages
|
||
|
|
||
|
"""
|
||
|
return self.handle(NotifyListCommand(), callback)
|
||
|
|
||
|
def notifyget(self, type, id, callback=None):
|
||
|
"""
|
||
|
Get notification/message
|
||
|
|
||
|
parameters:
|
||
|
type - (M=message, N=notification)
|
||
|
id - message/notification id
|
||
|
|
||
|
structure of parameters:
|
||
|
type id
|
||
|
|
||
|
"""
|
||
|
return self.handle(NotifyGetCommand(type, id), callback)
|
||
|
|
||
|
def notifyack(self, type, id, callback=None):
|
||
|
"""
|
||
|
Mark message read or clear a notification
|
||
|
|
||
|
parameters:
|
||
|
type - (M=message, N=notification)
|
||
|
id - message/notification id
|
||
|
|
||
|
structure of parameters:
|
||
|
type id
|
||
|
|
||
|
"""
|
||
|
return self.handle(NotifyAckCommand(type, id), callback)
|
||
|
|
||
|
def buddyadd(self, uid=None, uname=None, callback=None):
|
||
|
"""
|
||
|
Add a user to your buddy list
|
||
|
|
||
|
parameters:
|
||
|
uid - user id
|
||
|
uname - name of the user
|
||
|
|
||
|
structure of parameters:
|
||
|
(uid|uname)
|
||
|
|
||
|
"""
|
||
|
return self.handle(BuddyAddCommand(uid, uname), callback)
|
||
|
|
||
|
def buddydel(self, uid, callback=None):
|
||
|
"""
|
||
|
Remove a user from your buddy list
|
||
|
|
||
|
parameters:
|
||
|
uid - user id
|
||
|
|
||
|
structure of parameters:
|
||
|
uid
|
||
|
|
||
|
"""
|
||
|
return self.handle(BuddyDelCommand(uid), callback)
|
||
|
|
||
|
def buddyaccept(self, uid, callback=None):
|
||
|
"""
|
||
|
Accept user as buddy
|
||
|
|
||
|
parameters:
|
||
|
uid - user id
|
||
|
|
||
|
structure of parameters:
|
||
|
uid
|
||
|
|
||
|
"""
|
||
|
return self.handle(BuddyAcceptCommand(uid), callback)
|
||
|
|
||
|
def buddydeny(self, uid, callback=None):
|
||
|
"""
|
||
|
Deny user as buddy
|
||
|
|
||
|
parameters:
|
||
|
uid - user id
|
||
|
|
||
|
structure of parameters:
|
||
|
uid
|
||
|
|
||
|
"""
|
||
|
return self.handle(BuddyDenyCommand(uid), callback)
|
||
|
|
||
|
def buddylist(self, startat, callback=None):
|
||
|
"""
|
||
|
Retrieve your buddy list
|
||
|
|
||
|
parameters:
|
||
|
startat - number of buddy to start listing from
|
||
|
|
||
|
structure of parameters:
|
||
|
startat
|
||
|
|
||
|
"""
|
||
|
return self.handle(BuddyListCommand(startat), callback)
|
||
|
|
||
|
def buddystate(self, startat, callback=None):
|
||
|
"""
|
||
|
Retrieve buddy states
|
||
|
|
||
|
parameters:
|
||
|
startat - number of buddy to start listing from
|
||
|
|
||
|
structure of parameters:
|
||
|
startat
|
||
|
|
||
|
"""
|
||
|
return self.handle(BuddyStateCommand(startat), callback)
|
||
|
|
||
|
def anime(self, aid=None, aname=None, amask=-1, callback=None):
|
||
|
"""
|
||
|
Get information about an anime
|
||
|
|
||
|
parameters:
|
||
|
aid - anime id
|
||
|
aname - name of the anime
|
||
|
amask - a bitfield describing what information you want about the anime
|
||
|
|
||
|
structure of parameters:
|
||
|
(aid|aname) [amask]
|
||
|
|
||
|
structure of amask:
|
||
|
|
||
|
"""
|
||
|
return self.handle(AnimeCommand(aid, aname, amask), callback)
|
||
|
|
||
|
def episode(self, eid=None, aid=None, aname=None, epno=None, callback=None):
|
||
|
"""
|
||
|
Get information about an episode
|
||
|
|
||
|
parameters:
|
||
|
eid - episode id
|
||
|
aid - anime id
|
||
|
aname - name of the anime
|
||
|
epno - number of the episode
|
||
|
|
||
|
structure of parameters:
|
||
|
eid
|
||
|
(aid|aname) epno
|
||
|
|
||
|
"""
|
||
|
return self.handle(EpisodeCommand(eid, aid, aname, epno), callback)
|
||
|
|
||
|
def file(self, fid=None, size=None, ed2k=None, aid=None, aname=None, gid=None, gname=None, epno=None, fmask=-1, amask=0, callback=None):
|
||
|
"""
|
||
|
Get information about a file
|
||
|
|
||
|
parameters:
|
||
|
fid - file id
|
||
|
size - size of the file
|
||
|
ed2k - ed2k-hash of the file
|
||
|
aid - anime id
|
||
|
aname - name of the anime
|
||
|
gid - group id
|
||
|
gname - name of the group
|
||
|
epno - number of the episode
|
||
|
fmask - a bitfield describing what information you want about the file
|
||
|
amask - a bitfield describing what information you want about the anime
|
||
|
|
||
|
structure of parameters:
|
||
|
fid [fmask] [amask]
|
||
|
size ed2k [fmask] [amask]
|
||
|
(aid|aname) (gid|gname) epno [fmask] [amask]
|
||
|
|
||
|
structure of fmask:
|
||
|
bit key description
|
||
|
0 - -
|
||
|
1 aid aid
|
||
|
2 eid eid
|
||
|
3 gid gid
|
||
|
4 lid lid
|
||
|
5 - -
|
||
|
6 - -
|
||
|
7 - -
|
||
|
8 state state
|
||
|
9 size size
|
||
|
10 ed2k ed2k
|
||
|
11 md5 md5
|
||
|
12 sha1 sha1
|
||
|
13 crc32 crc32
|
||
|
14 - -
|
||
|
15 - -
|
||
|
16 dublang dub language
|
||
|
17 sublang sub language
|
||
|
18 quality quality
|
||
|
19 source source
|
||
|
20 audiocodec audio codec
|
||
|
21 audiobitrate audio bitrate
|
||
|
22 videocodec video codec
|
||
|
23 videobitrate video bitrate
|
||
|
24 resolution video resolution
|
||
|
25 filetype file type (extension)
|
||
|
26 length length in seconds
|
||
|
27 description description
|
||
|
28 - -
|
||
|
29 - -
|
||
|
30 filename anidb file name
|
||
|
31 - -
|
||
|
|
||
|
structure of amask:
|
||
|
bit key description
|
||
|
0 gname group name
|
||
|
1 gshortname group short name
|
||
|
2 - -
|
||
|
3 - -
|
||
|
4 - -
|
||
|
5 - -
|
||
|
6 - -
|
||
|
7 - -
|
||
|
8 epno epno
|
||
|
9 epname ep english name
|
||
|
10 epromaji ep romaji name
|
||
|
11 epkanji ep kanji name
|
||
|
12 - -
|
||
|
13 - -
|
||
|
14 - -
|
||
|
15 - -
|
||
|
16 totaleps anime total episodes
|
||
|
17 lastep last episode nr (highest, not special)
|
||
|
18 year year
|
||
|
19 type type
|
||
|
20 romaji romaji name
|
||
|
21 kanji kanji name
|
||
|
22 name english name
|
||
|
23 othername other name
|
||
|
24 shortnames short name list
|
||
|
25 synonyms synonym list
|
||
|
26 categories category list
|
||
|
27 relatedaids related aid list
|
||
|
28 producernames producer name list
|
||
|
29 producerids producer id list
|
||
|
30 - -
|
||
|
31 - -
|
||
|
|
||
|
"""
|
||
|
return self.handle(FileCommand(fid, size, ed2k, aid, aname, gid, gname, epno, fmask, amask), callback)
|
||
|
|
||
|
def group(self, gid=None, gname=None, callback=None):
|
||
|
"""
|
||
|
Get information about a group
|
||
|
|
||
|
parameters:
|
||
|
gid - group id
|
||
|
gname - name of the group
|
||
|
|
||
|
structure of parameters:
|
||
|
(gid|gname)
|
||
|
|
||
|
"""
|
||
|
return self.handle(GroupCommand(gid, gname), callback)
|
||
|
|
||
|
def groupstatus(self, aid=None, state=None, callback=None):
|
||
|
"""
|
||
|
Returns a list of group names and ranges of episodes released by the group for a given anime.
|
||
|
parameters:
|
||
|
aid - anime id
|
||
|
state - If state is not supplied, groups with a completion state of 'ongoing', 'finished', or 'complete' are returned
|
||
|
state values:
|
||
|
1 -> ongoing
|
||
|
2 -> stalled
|
||
|
3 -> complete
|
||
|
4 -> dropped
|
||
|
5 -> finished
|
||
|
6 -> specials only
|
||
|
"""
|
||
|
return self.handle(GroupstatusCommand(aid, state), callback)
|
||
|
|
||
|
def producer(self, pid=None, pname=None, callback=None):
|
||
|
"""
|
||
|
Get information about a producer
|
||
|
|
||
|
parameters:
|
||
|
pid - producer id
|
||
|
pname - name of the producer
|
||
|
|
||
|
structure of parameters:
|
||
|
(pid|pname)
|
||
|
|
||
|
"""
|
||
|
|
||
|
return self.handle(ProducerCommand(pid, pname), callback)
|
||
|
|
||
|
def mylist(self, lid=None, fid=None, size=None, ed2k=None, aid=None, aname=None, gid=None, gname=None, epno=None, callback=None):
|
||
|
"""
|
||
|
Get information about your mylist
|
||
|
|
||
|
parameters:
|
||
|
lid - mylist id
|
||
|
fid - file id
|
||
|
size - size of the file
|
||
|
ed2k - ed2k-hash of the file
|
||
|
aid - anime id
|
||
|
aname - name of the anime
|
||
|
gid - group id
|
||
|
gname - name of the group
|
||
|
epno - number of the episode
|
||
|
|
||
|
structure of parameters:
|
||
|
lid
|
||
|
fid
|
||
|
size ed2k
|
||
|
(aid|aname) (gid|gname) epno
|
||
|
|
||
|
"""
|
||
|
return self.handle(MyListCommand(lid, fid, size, ed2k, aid, aname, gid, gname, epno), callback)
|
||
|
|
||
|
def mylistadd(self, lid=None, fid=None, size=None, ed2k=None, aid=None, aname=None, gid=None, gname=None, epno=None, edit=None, state=None, viewed=None, source=None, storage=None, other=None, callback=None):
|
||
|
"""
|
||
|
Add/Edit information to/in your mylist
|
||
|
|
||
|
parameters:
|
||
|
lid - mylist id
|
||
|
fid - file id
|
||
|
size - size of the file
|
||
|
ed2k - ed2k-hash of the file
|
||
|
aid - anime id
|
||
|
aname - name of the anime
|
||
|
gid - group id
|
||
|
gname - name of the group
|
||
|
epno - number of the episode
|
||
|
edit - whether to add to mylist or edit an existing entry (0=add,1=edit)
|
||
|
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 parameters:
|
||
|
lid edit=1 [state viewed source storage other]
|
||
|
fid [state viewed source storage other] [edit]
|
||
|
size ed2k [state viewed source storage other] [edit]
|
||
|
(aid|aname) (gid|gname) epno [state viewed source storage other]
|
||
|
(aid|aname) edit=1 [(gid|gname) epno] [state viewed source storage other]
|
||
|
|
||
|
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)
|
||
|
|
||
|
structure of epno:
|
||
|
value meaning
|
||
|
x target episode x
|
||
|
0 target all episodes
|
||
|
-x target all episodes upto x
|
||
|
|
||
|
"""
|
||
|
return self.handle(MyListAddCommand(lid, fid, size, ed2k, aid, aname, gid, gname, epno, edit, state, viewed, source, storage, other), callback)
|
||
|
|
||
|
def mylistdel(self, lid=None, fid=None, aid=None, aname=None, gid=None, gname=None, epno=None, callback=None):
|
||
|
"""
|
||
|
Delete information from your mylist
|
||
|
|
||
|
parameters:
|
||
|
lid - mylist id
|
||
|
fid - file id
|
||
|
size - size of the file
|
||
|
ed2k - ed2k-hash of the file
|
||
|
aid - anime id
|
||
|
aname - name of the anime
|
||
|
gid - group id
|
||
|
gname - name of the group
|
||
|
epno - number of the episode
|
||
|
|
||
|
structure of parameters:
|
||
|
lid
|
||
|
fid
|
||
|
(aid|aname) (gid|gname) epno
|
||
|
|
||
|
"""
|
||
|
return self.handle(MyListCommand(lid, fid, aid, aname, gid, gname, epno), callback)
|
||
|
|
||
|
def myliststats(self, callback=None):
|
||
|
"""
|
||
|
Get summary information of your mylist
|
||
|
|
||
|
"""
|
||
|
return self.handle(MyListStatsCommand(), callback)
|
||
|
|
||
|
def vote(self, type, id=None, name=None, value=None, epno=None, callback=None):
|
||
|
"""
|
||
|
Rate an anime/episode/group
|
||
|
|
||
|
parameters:
|
||
|
type - type of the vote
|
||
|
id - anime/group id
|
||
|
name - name of the anime/group
|
||
|
value - the vote
|
||
|
epno - number of the episode
|
||
|
|
||
|
structure of parameters:
|
||
|
type (id|name) [value] [epno]
|
||
|
|
||
|
structure of type:
|
||
|
value meaning
|
||
|
1 rate an anime (episode if you also specify epno)
|
||
|
2 rate an anime temporarily (you haven't watched it all)
|
||
|
3 rate a group
|
||
|
|
||
|
structure of value:
|
||
|
value meaning
|
||
|
-x revoke vote
|
||
|
0 get old vote
|
||
|
100-1000 give vote
|
||
|
|
||
|
"""
|
||
|
return self.handle(VoteCommand(type, id, name, value, epno), callback)
|
||
|
|
||
|
def randomanime(self, type, callback=None):
|
||
|
"""
|
||
|
Get information of random anime
|
||
|
|
||
|
parameters:
|
||
|
type - where to take the random anime
|
||
|
|
||
|
structure of parameters:
|
||
|
type
|
||
|
|
||
|
structure of type:
|
||
|
value meaning
|
||
|
0 db
|
||
|
1 watched
|
||
|
2 unwatched
|
||
|
3 mylist
|
||
|
|
||
|
"""
|
||
|
return self.handle(RandomAnimeCommand(type), callback)
|
||
|
|
||
|
def ping(self, callback=None):
|
||
|
"""
|
||
|
Test connectivity to AniDB UDP API
|
||
|
|
||
|
"""
|
||
|
return self.handle(PingCommand(), callback)
|
||
|
|
||
|
def encrypt(self, user, apipassword, type=None, callback=None):
|
||
|
"""
|
||
|
Encrypt all future traffic
|
||
|
|
||
|
parameters:
|
||
|
user - your username
|
||
|
apipassword - your api password
|
||
|
type - type of encoding (1=128bit AES)
|
||
|
|
||
|
structure of parameters:
|
||
|
user [type]
|
||
|
|
||
|
"""
|
||
|
return self.handle(EncryptCommand(user, apipassword, type), callback)
|
||
|
|
||
|
def encoding(self, name, callback=None):
|
||
|
"""
|
||
|
Change encoding used in messages
|
||
|
|
||
|
parameters:
|
||
|
name - name of the encoding
|
||
|
|
||
|
structure of parameters:
|
||
|
name
|
||
|
|
||
|
comments:
|
||
|
DO NOT USE THIS!
|
||
|
utf8 is the only encoding which will support all the text in anidb responses
|
||
|
the responses have japanese, russian, french and probably other alphabets as well
|
||
|
even if you can't display utf-8 locally, don't change the server-client -connections encoding
|
||
|
rather, make python convert the encoding when you DISPLAY the text
|
||
|
it's better that way, let it go as utf8 to databases etc. because then you've the real data stored
|
||
|
|
||
|
"""
|
||
|
raise NotImplementedError("pylibanidb sets the encoding to utf8 as default and it's stupid to use any other encoding. you WILL lose some data if you use other encodings, and now you've been warned. you will need to modify the code yourself if you want to do something as stupid as changing the encoding")
|
||
|
|
||
|
def sendmsg(self, to, title, body, callback=None):
|
||
|
"""
|
||
|
Send message
|
||
|
|
||
|
parameters:
|
||
|
to - name of the user you want as the recipient
|
||
|
title - title of the message
|
||
|
body - the message
|
||
|
|
||
|
structure of parameters:
|
||
|
to title body
|
||
|
|
||
|
"""
|
||
|
return self.handle(SendMsgCommand(to, title, body), callback)
|
||
|
|
||
|
def user(self, user, callback=None):
|
||
|
"""
|
||
|
Retrieve user id
|
||
|
|
||
|
parameters:
|
||
|
user - username of the user
|
||
|
|
||
|
structure of parameters:
|
||
|
user
|
||
|
|
||
|
"""
|
||
|
return self.handle(UserCommand(user), callback)
|
||
|
|
||
|
def uptime(self, callback=None):
|
||
|
"""
|
||
|
Retrieve server uptime
|
||
|
|
||
|
"""
|
||
|
return self.handle(UptimeCommand(), callback)
|
||
|
|
||
|
def version(self, callback=None):
|
||
|
"""
|
||
|
Retrieve server version
|
||
|
|
||
|
"""
|
||
|
return self.handle(VersionCommand(), callback)
|