-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathradiorater.py
265 lines (232 loc) · 8.65 KB
/
radiorater.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
##
## animenfo rating bot
##
## Last Updated: 09-09-2011
## Version 2
##
## future improvements:
## request songs that haven't been voted for yet
## lot of error-handling missing
##
from BeautifulSoup import BeautifulSoup
import datetime
import ConfigParser
import mechanize
import urllib
import random
import sys
import time
# read configuration file
# TODO: need to implement creating configuration file if it doesn't exist
defaults = {'cookie-file':'cookies.dat',
'song-url':'https://www.animenfo.com/radio/nowplaying.php?ajax=true&mod=playing',
'user-agent':'Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.8) Gecko/20100723 Ubuntu/10.04 (lucid) Firefox/3.6.8'
}
config = ConfigParser.SafeConfigParser(defaults)
config.read('settings.cfg')
# set up constants from config file
USER_AGENT = config.get('general','user-agent')
COOKIE_FILE = config.get('general','cookie-file')
SONG_URL = config.get('site-specific','song-url')
USERNAME = config.get('site-specific','username')
PASSWORD = config.get('site-specific','password')
if (USERNAME == '' or PASSWORD == ''):
print 'Error: No username or password specified. Check settings.cfg'
sys.exit(0)
# constants not yet added to config file
START_HOUR = 9
END_HOUR = 17
# initialize browser
br = mechanize.Browser(factory=mechanize.RobustFactory())
# initialize cookie jar
cj = mechanize.LWPCookieJar(COOKIE_FILE)
try:
# reload cookies from saved file
# existing cookies in cj are overwritten to be safe
cj.revert()
except:
# if file doesn't exist, create a new one
cj.save()
br.set_cookiejar(cj)
# change useragent to useragent constant
br.addheaders = [('User-agent',USER_AGENT)]
#debug messages
#br.set_debug_http(True)
#br.set_debug_redirects(True)
#br.set_debug_responses(True)
def safeOpen(url, data=None):
''' Retries opening URL until it opens successfully '''
success = False
while not success:
try:
if data == None:
br.open(url)
else:
br.open(url,data)
success = True
except:
# if url opening fails, sleep for 2 seconds
success = False
time.sleep(2)
def login():
''' Logs into the site and saves a cookie to the cookiejar '''
values = {'frompage':'index.php',
'username':USERNAME,
'password':PASSWORD
}
data = urllib.urlencode(values)
safeOpen("https://www.animenfo.com/radio/login.php", data)
# TODO: add some kind of handler if login fails
cj.save()
def getStdDev(rates):
''' Returns a standard deviation used for rating based on the number of people
who have already rated a song '''
# These are fairly arbitrary numbers. I wouldn't mind finding
# a smoother way to do this, maybe some sort of exponential curve
if rates == 0:
return 1.5
elif rates < 100:
return 1
elif rates < 1000:
return 0.75
else:
return 0.5
def generateRating(currentRating, rates):
''' Generates an integer rating between 1 and 10 based on the current
song rating and the number of times it has been rated '''
try:
rating = float(currentRating)
except:
rating = 5.0
stdDev = getStdDev(float(rates))
myRating = int(round(random.gauss(rating, stdDev)))
if myRating > 10:
return 10
if myRating < 1:
return 1
else:
return myRating
def favoriteSong():
''' Add a song to the favorites list '''
if (findMechanizeLinkById('np_removefav') == None):
favoriteLink = findMechanizeLinkById('np_addfav')
if (favoriteLink != None):
br.follow_link(favoriteLink)
br.back()
print "Added this song to favorites."
else:
print "Unable to favorite song - no favorites links detected."
else:
print "Already favorited this song."
def getContent():
soup = BeautifulSoup(br.response().read())
items = soup.findAll('td', limit=3)
return items
def getSongInfo():
return getContent()[1]
def getSongStats():
return getContent()[2]
def outputSongInfo():
text = getSongInfo()
data = ''.join(text.findAll(text=True)).partition('favourites')
data = (data[0]+data[1]).strip()
data = data.replace('\t\t\t\t','')
return data
def findMechanizeLinkById(desiredId):
''' Given the ID of a link on the page, returns that link as a mechanize link '''
for link in br.links():
attrDict = dict(link.attrs)
try:
if (attrDict['id'] == desiredId):
return link
# if KeyError is thrown while accessing id, link has no id, so skip it
except KeyError:
pass
return None
def findMechanizeLinksWithClass(desiredClass):
''' Given a class, returns a list of mechanize links that have that class '''
linkList = []
for link in br.links():
attrDict = dict(link.attrs)
try:
if (desiredClass in attrDict['class']):
linkList.append(link)
# if KeyError is thrown while accessing class, link has no class, so skip it
except KeyError:
pass
return linkList
def findSoupItemById(tagName, desiredId):
soup = BeautifulSoup(br.response().read())
return soup.find(tagName, attrs={'id' : desiredId})
def getSecondsFromPage():
''' Gets the number of seconds a song has been playing from the page '''
timeSpan = findSoupItemById('span','np_timer')
return int(timeSpan['rel'])
def songHasBeenRated():
rateText = findSoupItemById('form', 'rateform')
rateText = rateText.contents[4].strip()
return (rateText.find('Change your rating for this song:') != -1)
def canRate(currentTime):
''' Returns true if the current time is between START_HOUR and END_HOUR '''
date = currentTime.date()
startDatetime = datetime.datetime.combine(date, datetime.time(START_HOUR,0,0))
endDatetime = datetime.datetime.combine(date, datetime.time(END_HOUR,0,0))
return (startDatetime < currentTime and endDatetime > currentTime)
def getSecondsUntilTomorrow(currentTime):
''' Returns the number of seconds until START_HOUR '''
date = currentTime.date()
date = date + datetime.timedelta(days=1)
tomorrowDatetime = datetime.datetime.combine(date, datetime.time(START_HOUR,0,0))
delta = tomorrowDatetime - currentTime
return delta.seconds
# main (infinite) loop
def main():
while(True):
# get current time at the start to try to avoid race conditions
currentTime = datetime.datetime.now()
while(canRate(currentTime)):
safeOpen(SONG_URL)
try:
br.select_form(nr=0)
except:
login() # if a form can't be selected, the browser isn't logged in
safeOpen(SONG_URL)
br.select_form(nr=0)
seconds = getSecondsFromPage()
if seconds <= 45:
# if <= 45, print the song info, then check if the song has been rated
print outputSongInfo()
if not (songHasBeenRated()):
# if it hasn't, rate the song
text = getSongStats()
rating = text.contents[5].strip().strip('Rating: ')
currentRating = rating.split('/', 1)[0]
rates = rating.split('rate', 1)[0]
rates = rates.split('(')[1]
myRating = generateRating(currentRating, rates)
print 'My Rating: %d' % myRating
br['rating'] = [str(myRating)]
br.submit()
# if song is above rating threshold, add it to favorites
if (myRating >= 9):
favoriteSong()
else:
rating = br['rating'][0]
print 'Already rated this song: %s' % rating
# if song is above rating threshold, add it to favorites
if (float(rating) >= 9):
favoriteSong()
seconds = seconds + random.randint(5,15)
print 'Sleeping for %d seconds...' % (seconds)
time.sleep(seconds)
else:
# if > 30, set timer to timer - ~20 seconds
seconds = seconds - random.randint(15,45)
print 'Sleeping for %d seconds...' % (seconds)
time.sleep(seconds)
currentTime = datetime.datetime.now()
sleepTime = getSecondsUntilTomorrow(currentTime)
print 'Sleeping for %d hours, %d minutes...' % ((sleepTime / 3600), ((sleepTime % 3600)/ 60))
time.sleep(sleepTime)
if __name__ == "__main__":
main()