def get_pp_infos(beatmap_id): BUFSIZE = 2000000 accuracys = [100, 99, 98, 97] mods = [pyoppai.nomod, pyoppai.hr, pyoppai.hd, pyoppai.dt, pyoppai.dt | pyoppai.hd, pyoppai.dt | pyoppai.hr, pyoppai.hd | pyoppai.hr, pyoppai.dt | pyoppai.hr | pyoppai.hd] mods_name = ["Nomod", "HR", "HD", "DT", "HDDT", "HRDT", "HDHR", "HRHDDT"] pp_results = {} ctx = pyoppai.new_ctx() buf = pyoppai.new_buffer(BUFSIZE) b = pyoppai.new_beatmap(ctx) err = pyoppai.err(ctx) if err: print(err) pass for accuracy in accuracys: i = 0 for mod in mods: pyoppai.parse(constants.Paths.beatmapsDownloadsPermanent + "/" + str(beatmap_id) + ".osu", b, buf, BUFSIZE, False, constants.Paths.beatmapsDownloadsPermanent) dctx = pyoppai.new_d_calc_ctx(ctx) pyoppai.apply_mods(b, mod) stars, aim, speed, _, _, _, _ = pyoppai.d_calc(dctx, b) _, pp, _, _, _ = pyoppai.pp_calc_acc(ctx, aim, speed, b, accuracy, mod) err = pyoppai.err(ctx) if err: print(err) pass pp_results["pp_" + str(accuracy) + "_" + mods_name[i]] = round(pp) i += 1 return pp_results
async def find_closest_pp(beatmap, args): """ Find the accuracy required to get the given amount of pp from this map. """ if pyoppai is None: return None ctx, beatmap_ctx = create_ctx(beatmap) # Create the difficulty context for calculating diff_ctx = pyoppai.new_d_calc_ctx(ctx) mods_bitmask = apply_settings(beatmap_ctx, args) stars, aim, speed, _, _, _, _ = pyoppai.d_calc(diff_ctx, beatmap_ctx) # Define a partial command for easily setting the pp value by 100s count def calc(accuracy: float): return pyoppai.pp_calc_acc(ctx, aim, speed, beatmap_ctx, accuracy, mods_bitmask, args.combo, args.misses, args.score_version)[1] # Find the smallest possible value oppai is willing to give min_pp = calc(accuracy=0.0) if args.pp <= min_pp: raise ValueError( "The given pp value is too low (oppai gives **{:.02f}pp** at **0% acc**)." .format(min_pp)) # Calculate the max pp value by using 100% acc previous_pp = calc(accuracy=100.0) if args.pp >= previous_pp: raise ValueError( "PP value should be below **{:.02f}pp** for this map.".format( previous_pp)) dec = .05 acc = 100.0 - dec while True: current_pp = calc(accuracy=acc) # Stop when we find a pp value between the current 100 count and the previous one if current_pp <= args.pp <= previous_pp: break else: previous_pp = current_pp acc -= dec # Find the closest pp of our two values, and return the amount of 100s closest_pp = min([previous_pp, current_pp], key=lambda v: abs(args.pp - v)) acc = acc if closest_pp == current_pp else acc + dec return ClosestPPStats(round(acc, 2), closest_pp, stars, pyoppai.artist(beatmap_ctx), pyoppai.title(beatmap_ctx), pyoppai.version(beatmap_ctx))
async def calculate_pp(beatmap_url_or_id, *options): """ Return a PPStats namedtuple from this beatmap, or a ClosestPPStats namedtuple when [pp_value]pp is given in the options. :param beatmap_url_or_id: beatmap_url as str or the id as int """ if pyoppai is None: return None beatmap = await download_beatmap(beatmap_url_or_id) args = parse_options(*options) # If the pp arg is given, return using the closest pp function if args.pp is not None: return await find_closest_pp(beatmap, args) ctx, beatmap_ctx = create_ctx(beatmap) # Create the difficulty context for calculating diff_ctx = pyoppai.new_d_calc_ctx(ctx) mods_bitmask = apply_settings(beatmap_ctx, args) stars, aim, speed, _, _, _, _ = pyoppai.d_calc(diff_ctx, beatmap_ctx) # Calculate using only acc when acc is specified if args.acc < 100: acc, pp, _, _, _ = pyoppai.pp_calc_acc(ctx, aim, speed, beatmap_ctx, args.acc, mods_bitmask, args.combo, args.misses, args.score_version) else: acc, pp, _, _, _ = pyoppai.pp_calc(ctx, aim, speed, beatmap_ctx, mods_bitmask, args.combo, args.misses, args.c300, args.c100, args.c50, args.score_version) return PPStats( pp, stars, pyoppai.artist(beatmap_ctx), pyoppai.title(beatmap_ctx), pyoppai.version(beatmap_ctx), )
async def py_oppai(map_id: str, accs=[100], mods=0, misses=0, combo=None, fc=None): url = 'https://osu.ppy.sh/osu/{}'.format(map_id) # try: ctx = pyoppai.new_ctx() b = pyoppai.new_beatmap(ctx) BUFSIZE = 2000000 buf = pyoppai.new_buffer(BUFSIZE) file_path = 'data/osu/temp/{}.osu'.format(map_id) # some unique filepath await download_file(url, file_path) # this is the file name that it downloaded pyoppai.parse(file_path, b, buf, BUFSIZE, True, 'data/osu/cache/') dctx = pyoppai.new_d_calc_ctx(ctx) pyoppai.apply_mods(b, mods) stars, aim, speed, _, _, _, _ = pyoppai.d_calc(dctx, b) cs, od, ar, hp = pyoppai.stats(b) if not combo: combo = pyoppai.max_combo(b) total_pp_list = [] aim_pp_list = [] speed_pp_list = [] acc_pp_list = [] for acc in accs: accurracy, pp, aim_pp, speed_pp, acc_pp = pyoppai.pp_calc_acc( ctx, aim, speed, b, acc, mods, combo, misses) total_pp_list.append(pp) aim_pp_list.append(aim_pp) speed_pp_list.append(speed_pp) acc_pp_list.append(acc_pp) if fc: _, fc_pp, _, _, _ = pyoppai.pp_calc_acc(ctx, aim, speed, b, fc, mods, pyoppai.max_combo(b), 0) total_pp_list.append(fc_pp) pyoppai_json = { 'version': pyoppai.version(b), 'title': pyoppai.title(b), 'artist': pyoppai.artist(b), 'creator': pyoppai.creator(b), 'combo': combo, 'misses': misses, 'max_combo': pyoppai.max_combo(b), 'mode': pyoppai.mode(b), 'num_objects': pyoppai.num_objects(b), 'num_circles': pyoppai.num_circles(b), 'num_sliders': pyoppai.num_sliders(b), 'num_spinners': pyoppai.num_spinners(b), 'stars': stars, 'aim_stars': aim, 'speed_stars': speed, 'pp': total_pp_list, # list 'aim_pp': aim_pp_list, 'speed_pp': speed_pp_list, 'acc_pp': acc_pp_list, 'acc': accs, # list 'cs': cs, 'od': od, 'ar': ar, 'hp': hp } os.remove(file_path) return pyoppai_json
data.append(beatmap_info['creator']) data.append(beatmap_info['artist']) data.append(str(beatmap_info['approved'] == BeatmapStatus.ranked)) if beatmap_info['mode'] == OsuMode.osu: ctx = pyoppai.new_ctx() buf = pyoppai.new_buffer(BUFSIZE) b = pyoppai.new_beatmap(ctx) for accuracy in accuracys: for mod in mods: pyoppai.parse(downloadDirr + "/" + beatmap, b, buf, BUFSIZE, False, downloadDirr) dctx = pyoppai.new_d_calc_ctx(ctx) pyoppai.apply_mods(b, mod) stars, aim, speed, _, _, _, _ = pyoppai.d_calc(dctx, b) acc, pp, aim_pp, speed_pp, acc_pp = pyoppai.pp_calc_acc( ctx, aim, speed, b, accuracy, mod) try: data.append(round(pp)) except OverflowError: #If pp = infinite data.append( 2147483647) #max sqlite int 4 bytes value if pyoppai.err(ctx) != "Could not find General info": try: cursor.execute( "INSERT INTO beatmaps (beatmapId ,songId ,diff_params ,stars ,combo ,bpm ,lenght ,drain ,version ,title ,creator ,artist ,ranked ,PP_100 ,PP_100_HR ,PP_100_HD ,PP_100_DT ,PP_100_DTHD ,PP_100_DTHR ,PP_100_HRHD ,PP_100_DTHRHD ,PP_99 ,PP_99_HR ,PP_99_HD ,PP_99_DT ,PP_99_DTHD ,PP_99_DTHR ,PP_99_HRHD ,PP_99_DTHRHD ,PP_98 ,PP_98_HR ,PP_98_HD ,PP_98_DT ,PP_98_DTHD ,PP_98_DTHR ,PP_98_HRHD ,PP_98_DTHRHD ,PP_97 ,PP_97_HR ,PP_97_HD ,PP_97_DT ,PP_97_DTHD ,PP_97_DTHR ,PP_97_HRHD ,PP_97_DTHRHD) VALUES(" + "?," * 44 + " ?)", data) conn.commit() except sqlite3.IntegrityError:
def main(mod,filepath): # if you need to multithread, create one ctx and buffer for each thread ctx = pyoppai.new_ctx() # parse beatmap ------------------------------------------------------------ b = pyoppai.new_beatmap(ctx) BUFSIZE = 2000000 # should be big enough to hold the .osu file buf = pyoppai.new_buffer(BUFSIZE) pyoppai.parse( filepath, b, buf, BUFSIZE, # don't disable caching and use python script's folder for caching False, os.path.dirname(os.path.realpath(__file__)) ); chk(ctx) if not mod: # diff calc ---------------------------------------------------------------- dctx = pyoppai.new_d_calc_ctx(ctx) stars, aim, speed, _, _, _, _ = pyoppai.d_calc(dctx, b) chk(ctx) # pp calc ------------------------------------------------------------------ acc, pp, aim_pp, speed_pp, acc_pp = \ pyoppai.pp_calc(ctx, aim, speed, b) chk(ctx) # pp calc (with acc %) ----------------------------------------------------- acc, pp95, aim_pp, speed_pp, acc_pp = \ pyoppai.pp_calc_acc(ctx, aim, speed, b, 95.0) chk(ctx) result=[stars,pp,pp95] return (result) else: dctx = pyoppai.new_d_calc_ctx(ctx) # mods are a bitmask, same as what the osu! api uses mods = pyoppai.dt pyoppai.apply_mods(b, mods) # mods are map-changing, recompute diff stars, aim, speed, _, _, _, _ = pyoppai.d_calc(dctx, b) chk(ctx) acc, pp, aim_pp, speed_pp, acc_pp = \ pyoppai.pp_calc(ctx, aim, speed, b, mods) chk(ctx) # pp calc (with acc %) ----------------------------------------------------- acc, pp95, aim_pp, speed_pp, acc_pp = \ pyoppai.pp_calc_acc(ctx, aim, speed, b, 95.0) chk(ctx) result=[stars,pp,pp95] return (result)
def calculatePP(self): """ Calculate total pp value with oppai and return it return -- total pp """ # Set variables self.pp = None try: # Build .osu map file path mapFile = "{path}/maps/{map}".format(path=self.OPPAI_FOLDER, map=self.map) log.debug("oppai ~> Map file: {}".format(mapFile)) try: # Check if we have to download the .osu file download = False if not os.path.isfile(mapFile): # .osu file doesn't exist. We must download it if glob.debug: consoleHelper.printColored("[!] {} doesn't exist".format(mapFile), bcolors.YELLOW) download = True else: # File exists, check md5 if generalUtils.fileMd5(mapFile) != self.beatmap.fileMD5: # MD5 don't match, redownload .osu file if glob.debug: consoleHelper.printColored("[!] Beatmaps md5 don't match", bcolors.YELLOW) download = True # Download .osu file if needed if download: log.debug("oppai ~> Downloading {} osu file".format(self.beatmap.beatmapID)) # Get .osu file from osu servers fileContent = osuapiHelper.getOsuFileFromID(self.beatmap.beatmapID) # Make sure osu servers returned something if fileContent is None: raise exceptions.osuApiFailException(MODULE_NAME) # Delete old .osu file if it exists if os.path.isfile(mapFile): os.remove(mapFile) # Save .osu file with open(mapFile, "wb+") as f: f.write(fileContent.encode("utf-8")) else: # Map file is already in folder log.debug("oppai ~> Beatmap found in cache!") except exceptions.osuApiFailException: log.error("oppai ~> osu!api error!") pass # Parse beatmap log.debug("oppai ~> About to parse beatmap") pyoppai.parse( mapFile, self._oppai_beatmap, self._oppai_buffer, self.BUFSIZE, False, self.OPPAI_FOLDER # /oppai_cache ) self.checkOppaiErrors() log.debug("oppai ~> Beatmap parsed with no errors") # Create diffcalc context and calculate difficulty log.debug("oppai ~> About to calculate difficulty") # Use only mods supported by oppai modsFixed = self.mods & 5979 if modsFixed > 0: pyoppai.apply_mods(self._oppai_beatmap, modsFixed) self._oppai_diffcalc_ctx = pyoppai.new_d_calc_ctx(self._oppai_ctx) diff_stars, diff_aim, diff_speed, _, _, _, _ = pyoppai.d_calc(self._oppai_diffcalc_ctx, self._oppai_beatmap) self.checkOppaiErrors() log.debug("oppai ~> Difficulty calculated with no errors. {}*, {} aim, {} speed".format(diff_stars, diff_aim, diff_speed)) # Calculate pp log.debug("oppai ~> About to calculate PP") if not self.tillerino: _, total_pp, aim_pp, speed_pp, acc_pp = pyoppai.pp_calc_acc(self._oppai_ctx, diff_aim, diff_speed, self._oppai_beatmap, self.acc if self.acc > 0 else 100, modsFixed, self.combo if self.combo > 0 else 0xFFFF, self.misses) self.checkOppaiErrors() log.debug("oppai ~> PP Calculated with no errors. {}pp, {} aim pp, {} speed pp, {} acc pp".format( total_pp, aim_pp, speed_pp, acc_pp )) self.pp = total_pp else: pp_list = [] for acc in [100, 99, 98, 95]: log.debug("oppai ~> Calculating PP with acc {}%".format(acc)) _, total_pp, aim_pp, speed_pp, acc_pp = pyoppai.pp_calc_acc(self._oppai_ctx, diff_aim, diff_speed, self._oppai_beatmap, acc, modsFixed) self.checkOppaiErrors() pp_list.append(total_pp) log.debug("oppai ~> PP Calculated with no errors. {}pp, {} aim pp, {} speed pp, {} acc pp".format( total_pp, aim_pp, speed_pp, acc_pp )) self.pp = pp_list self.stars = diff_stars log.debug("oppai ~> Calculated PP: {}".format(self.pp)) except OppaiError: log.error("oppai ~> pyoppai error!") self.pp = 0 except Exception as e: log.error("oppai ~> Unhandled exception: {}".format(str(e))) raise e finally: log.debug("oppai ~> Shutting down and returning {}pp".format(self.pp)) return self.pp
def main(): if len(sys.argv) != 2: print("Usage: " + sys.argv[0] + " file.osu") sys.exit(1) # if you need to multithread, create one ctx and buffer for each thread ctx = pyoppai.new_ctx() # parse beatmap ------------------------------------------------------------ b = pyoppai.new_beatmap(ctx) BUFSIZE = 2000000 # should be big enough to hold the .osu file buf = pyoppai.new_buffer(BUFSIZE) pyoppai.parse( sys.argv[1], b, buf, BUFSIZE, # don't disable caching and use python script's folder for caching False, os.path.dirname(os.path.realpath(__file__)) ); chk(ctx) print("Cache folder: " + os.path.dirname(os.path.realpath(__file__)) + "\n") cs, od, ar, hp = pyoppai.stats(b) print( "%s - %s [%s] (by %s)\n" "CS%g OD%g AR%g HP%g\n" "%d objects (%d circles, %d sliders, %d spinners)\n" "max combo: %d" % ( pyoppai.artist(b), pyoppai.title(b), pyoppai.version(b), pyoppai.creator(b), cs, od, ar, hp, pyoppai.num_objects(b), pyoppai.num_circles(b), pyoppai.num_sliders(b), pyoppai.num_spinners(b), pyoppai.max_combo(b) ) ) # diff calc ---------------------------------------------------------------- dctx = pyoppai.new_d_calc_ctx(ctx) stars, aim, speed, _, _, _, _ = pyoppai.d_calc(dctx, b) chk(ctx) print_diff(stars, aim, speed) # pp calc ------------------------------------------------------------------ acc, pp, aim_pp, speed_pp, acc_pp = \ pyoppai.pp_calc(ctx, aim, speed, b) chk(ctx) print_pp(acc, pp, aim_pp, speed_pp, acc_pp) # pp calc (with acc %) ----------------------------------------------------- acc, pp, aim_pp, speed_pp, acc_pp = \ pyoppai.pp_calc_acc(ctx, aim, speed, b, 90.0) chk(ctx) print_pp(acc, pp, aim_pp, speed_pp, acc_pp) # override OD example ------------------------------------------------------ print("\n----\nIf the map was od10:") pyoppai.set_od(b, 10) acc, pp, aim_pp, speed_pp, acc_pp = \ pyoppai.pp_calc(ctx, aim, speed, b) chk(ctx) print_pp(acc, pp, aim_pp, speed_pp, acc_pp) pyoppai.set_od(b, od) # override AR example ------------------------------------------------------ print("\n----\nIf the map was ar11:") pyoppai.set_ar(b, 11) acc, pp, aim_pp, speed_pp, acc_pp = \ pyoppai.pp_calc(ctx, aim, speed, b) chk(ctx) print_pp(acc, pp, aim_pp, speed_pp, acc_pp) pyoppai.set_ar(b, ar) # override CS example ------------------------------------------------------ print("\n----\nIf the map was cs6.5:") pyoppai.set_cs(b, 6.5) # remember that CS is map-changing so difficulty must be recomputed stars, aim, speed, _, _, _, _ = pyoppai.d_calc(dctx, b) chk(ctx) print_diff(stars, aim, speed) acc, pp, aim_pp, speed_pp, acc_pp = \ pyoppai.pp_calc(ctx, aim, speed, b) chk(ctx) print_pp(acc, pp, aim_pp, speed_pp, acc_pp) pyoppai.set_cs(b, cs) # mods example ------------------------------------------------------------- print("\n----\nWith HDHR:") # mods are a bitmask, same as what the osu! api uses mods = pyoppai.hd | pyoppai.hr pyoppai.apply_mods(b, mods) # mods are map-changing, recompute diff stars, aim, speed, _, _, _, _ = pyoppai.d_calc(dctx, b) chk(ctx) print_diff(stars, aim, speed) acc, pp, aim_pp, speed_pp, acc_pp = \ pyoppai.pp_calc(ctx, aim, speed, b, mods) chk(ctx) print_pp(acc, pp, aim_pp, speed_pp, acc_pp)