Advertisement
RavenMac

Bloodcat beatmap auto-downloader (Py 3.4)

Oct 13th, 2014
1,647
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 8.49 KB | None | 0 0
  1. import ctypes, json, os, sys, urllib.request, tkinter, tkinter.filedialog, tkinter.messagebox
  2. from sys import exc_info
  3. from time import sleep, strftime
  4.  
  5. #return time with/without dash or space that it takes up
  6. def getTime(num=1): return {1: strftime('%H:%M:%S')+' - ', 2: strftime('%H:%M:%S'), -1: ' '*len(strftime('%H:%M:%S')+' - '), -2: ' '*len(strftime('%H:%M:%S'))}[num]
  7.  
  8. #makes a system beep
  9. def beep(): sys.stdout.write('\a')
  10.  
  11. #checks the JSON data and compares it to Recent.json or Recent.num
  12. #if any new songs/updates are found, download it
  13. def checkJSON(down_to, save_as_unicode):
  14.     try:
  15.         print (getTime()+'Checking JSON data.')
  16.         items = getIs(1) #number of pages checked. 61? songs per page
  17.         if os.path.isfile('Recent.json'): #check file and download new/updated songs
  18.             with open('Recent.json') as file:
  19.                 data = json.load(file)
  20.                 if 'beatmaps' in data[0]:
  21.                     to_down = [i for i in items if i not in data] #makes list if checked items are updated/new from Recent.json
  22.                     for i in enumerate(to_down):
  23.                         print (getTime()+'Downloading '+str(i[0]+1)+' of '+str(len(to_down))+'.\n'+
  24.                             itemInfo(i[1]))
  25.                         download(down_to, i[1], save_as_unicode)
  26.                 else:
  27.                     os.remove('Recent.json') #delete old file if improper json is used
  28.                     for i in enumerate(items):
  29.                         print (getTime()+'Downloading '+str(i[0]+1)+' of '+str(len(items))+'.\n'+
  30.                                 itemInfo(i[1]))
  31.                         download(down_to, i[1], save_as_unicode)
  32. #       elif os.path.isfile('Recent.num'): #check old file type and download new/updated songs
  33. #           print (getTime(-1)+'Old file type found.\n'+
  34. #               getTime(-1)+'Will replace old file type with the new\n'+
  35. #               getTime(-1)+'after check and downloads are complete.')
  36. #           with open('Recent.num') as file:
  37. #               data = file.read()
  38. #               to_down = [i for i in items if i['id'] not in data]
  39. #               for i in enumerate(to_down):
  40. #                   print (getTime()+'Downloading '+str(i[0]+1)+' of '+str(len(to_down))+'.\n'+
  41. #                       itemInfo(i[1]))
  42. #                   download(down_to, i[1], save_as_unicode)
  43. #           os.remove('Recent.num') #delete old file type
  44.         else: #if recent.num isn't found, download everything
  45.             print (getTime(-1)+'Recent.json or Recent.num files not found.\n'+
  46.                 getTime(-1)+'Downloading all available beatmaps.')
  47.             for i in enumerate(items):
  48.                 print (getTime()+'Downloading '+str(i[0]+1)+' of '+str(len(items))+'.\n'+
  49.                         itemInfo(i[1]))
  50.                 download(down_to, i[1], save_as_unicode)
  51.         with open('Recent.json', 'w') as file: #save recently checked items to file
  52.             json.dump(items, file)
  53.         print (getTime()+'Finished check / download.')
  54.     except:
  55.         beep()
  56.         print (getTime()+'Error in checkJSON.')
  57.         print (exc_info())
  58.         sleep(5)
  59.         checkJSON(down_to, save_as_unicode)
  60.  
  61. #this returns a list of all JSON data online.
  62. def getIs(max, page=1, error=0):
  63.     if error >= 5: #if there are connection errors, check every minute.
  64.         sleep(300)
  65.     try:
  66.         print (getTime()+'Checking page '+str(page)+' of '+str(max)+'.')
  67.         conn = urllib.request.urlopen('http://bloodcat.com/osu/?mod=json&p='+str(page), timeout=60) #make the connection
  68.         #data = json.loads(conn.readall().decode('utf-8')) #load the data old
  69.         items = json.loads(conn.readall().decode('utf-8')) #load the data
  70.         conn.close() #close connection
  71.         #items = [i for i in data['results']] #separate unneeded info old
  72.         sleep(.5) #can't check too fast. Only 15 checks per 10 seconds allowed as i found out via emails with the owner Sung Hwan.
  73.         if page != max: #small bit of recursion.
  74.             for i in getIs(max, page+1): #get another page if not the last/max page
  75.                 items.append(i) #add to this one
  76.         return items
  77.     except:
  78.         beep()
  79.         print (getTime()+'Error in getIs.')
  80.         print (exc_info())
  81.         sleep(5)
  82.         return getIs(max, page, error+1)
  83.  
  84. #this downloads the songs
  85. def download(down_to, item, save_as_unicode, error=0):
  86.     if error >= 5: #if there are connection errors, check every 5 minutes
  87.         sleep(300)
  88.     try:
  89.         conn = urllib.request.urlopen('http://bloodcat.com/osu/m/'+item['id'], timeout=600) #make connection
  90.         data = conn.read() #download to program
  91.         conn.close() #close connection
  92.         if save_as_unicode and songFileName(item, True) != songFileName(item): #save file with unicode if chosen and if available
  93.             with open(down_to+''.join([(i if i not in '\\/:*?"<>|' else '_') for i in songFileName(item, True)])+'.osz', 'wb') as file: #create new binary file. Replace any illegal Windows file name characters
  94.                 file.write(data) #actual save to file happens here
  95.         else:
  96.             with open(down_to+''.join([(i if i not in '\\/:*?"<>|' else '_') for i in songFileName(item)])+'.osz', 'wb') as file:
  97.                 file.write(data) #or here if you don't want unicode
  98.         if os.path.isfile('Recent.json'):
  99.             with open('Recent.json') as file: #save json data on completion if program is closed during multiple downloads
  100.                 data = json.load(file)
  101.                 data.append(item)
  102.                 with open('Recent.json', 'w') as f:
  103.                     json.dump(data, f)
  104.         else:
  105.             with open('Recent.json', 'w') as file:
  106.                 json.dump([item], file)
  107.     except:
  108.         beep()
  109.         print (getTime()+'Error in download.\n'+
  110.             getTime(-1)+'Redownloading: '+songFileName(item)+'\n')
  111.         print (exc_info())
  112.         sleep(5)
  113.         download(down_to, item, save_as_unicode, error+1)
  114.  
  115. #returns a string of all the info for each song being downloaded
  116. #def itemInfo(item):
  117. #   return (getTime(-1)+'Song:'.ljust(13)+songFileName(item)+'\n'+
  118. #       getTime(-1)+'Creator:'.ljust(13)+item['creatorId']+' '+item['creator']+'\n'+
  119. #       getTime(-1)+'Genre:'.ljust(13)+'('+item['genre']+') '+genreCheck(item['genre'])+'\n'+
  120. #       getTime(-1)+'Mode:'.ljust(13)+'('+item['mode'].upper()+') '+modeCheck(item['mode'])+'\n'+
  121. #       getTime(-1)+'Song Status:'.ljust(13)+'('+item['status']+') '+statusCheck(item['status']))
  122.  
  123. #returns a string of all the info for each song being downloaded
  124. def itemInfo(item):
  125.     return (getTime(-1)+'Song:'.ljust(13)+songFileName(item)+'\n'+
  126.         getTime(-1)+'Creator:'.ljust(13)+item['creatorId']+' '+item['creator']+'\n'+
  127.         getTime(-1)+'Song Status:'.ljust(13)+'('+item['status']+') '+statusCheck(item['status'])+'\n'+
  128.         getTime(-1)+'Mode:'.ljust(13)+modeCheck(item))
  129.        
  130. #returns a string with songID artist and title with / without unicode
  131. def songFileName(item, save_as_unicode=False):
  132.     if save_as_unicode: #only if user chooses to save as unicode will a unicode title be created
  133.         if item['artistU']:
  134.             if item['titleU']:
  135.                 return item['id']+' '+item['artistU']+' - '+item['titleU']
  136.             else:
  137.                 return item['id']+' '+item['artistU']+' - '+item['title']
  138.         else:
  139.             if item['titleU']:
  140.                 return item['id']+' '+item['artist']+' - '+item['titleU']
  141.     return (item['id']+' '+item['artist']+' - '+item['title']).encode('ascii', 'replace').decode('utf-8').replace('?', '_') #ascii title + replace any unicode characters if any for some reason
  142.     (test+'test').encode('ascii', 'replace').decode('utf-8').replace('?', '_')
  143.  
  144. #returns a string for the genre depending on the number
  145. def genreCheck(num='0'): return {'0': 'Undefined', '1': 'Video Games', '2': 'Animation', '3': 'Easy', '4': 'Pop', '5': 'Other', '6': 'Novelty', '8': 'Hip-Hop', '9': 'Electronic'}[num]
  146.  
  147. #returns a string of playable modes
  148. def modeCheck(item):
  149.     modes={'0': 'Standard', '1': 'Taiko', '2': 'Catch the Beat', '3': 'Mania'} #library for modes
  150.     count={'0': 0, '1': 0, '2': 0, '3': 0}
  151.     for i in item['beatmaps']: #for each character
  152.         count[i['mode']] += 1
  153.     temp = ''
  154.     for i in count:
  155.         if count[i] != 0:
  156.             temp += modes[i]+', '
  157.     return temp[:temp.rfind(', ')] #and don't forget to remove the last comma and space before return
  158.  
  159. #returns a string for the status of the song
  160. def statusCheck(status='0'): return {'0': 'Unranked (Others)', '1': 'Ranked', '2': 'Approved', '3': 'Qualified'}[status]
  161.  
  162. #start of running code
  163. ctypes.windll.kernel32.SetConsoleTitleW('Bloodcat beatmap auto-downloader') #window title
  164. delay = 300 #wait time before checks (5 minutes)
  165. root = tkinter.Tk() #used for the GUI
  166. root.withdraw() #hides the unneeded GUI window
  167. download_to_folder = tkinter.filedialog.askdirectory(parent=root, initialdir='\\',
  168.     title='Select download destination.') #asks user location of downloads
  169.  
  170. #if any directory was selected
  171. if download_to_folder != '':
  172.     try:
  173.         save_as_unicode = tkinter.messagebox.askyesno('Save as Unicode?', #asks user if tey want files saved with unicode characters
  174.             'Would you like to save the beatmaps\nwith unicode characters? (Ex.\u3084\u3042.osz)')
  175.         while(True): #main loop
  176.             checkJSON(download_to_folder+'\\', save_as_unicode)
  177.             sleep(delay)
  178.     except:
  179.         beep()
  180.         print (getTime()+'Error in MAIN')
  181.         print (exc_info())
  182.         sleep(1000)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement