示例#1
0
def updateSet(beatmapSetID):
    apiResponse = osuapiHelper.osuApiRequest("get_beatmaps",
                                             "s={}".format(beatmapSetID),
                                             False)
    if len(apiResponse) == 0:
        return
    for i in apiResponse:
        beatmap.beatmap(i["file_md5"], int(i["beatmapset_id"]), refresh=True)
示例#2
0
    def recalc_score(score_data: Dict,
                     no_download=False) -> Optional[score.score]:
        """
        Recalculates pp for a score

        :param score_data: dict containing score and beatmap information about a score.
        :param no_download: if True, raise FileNotFoundError() if the map should be re-downloaded.
                            this ensures no requests are made to osu!
        :return: new `score` object, with `pp` attribute set to the new value
        """
        # Create score object and set its data
        s: score.score = score.score()
        s.setDataFromDict(score_data)
        s.passed = True

        # Create beatmap object and set its data
        b: beatmap.beatmap = beatmap.beatmap()
        b.setDataFromDict(score_data)

        # Abort if we are running in no_download mode and the map should be re-downloaded
        if no_download and mapsHelper.shouldDownloadMap(
                mapsHelper.cachedMapPath(b.beatmapID), b):
            raise FileNotFoundError("no_download mode and local map not found")

        # Calculate score pp
        s.calculatePP(b)
        del b
        return s
示例#3
0
    def asyncPost(self):
        statusCode = 400
        data = {"message": "unknown error"}
        try:
            # Check arguments
            if not requestsManager.checkArguments(self.request.arguments,
                                                  ["sid", "refresh"]):
                raise exceptions.invalidArgumentsException(MODULE_NAME)

            # Get beatmap set data from osu api
            beatmapSetID = self.get_argument("sid")
            refresh = int(self.get_argument("refresh"))
            if refresh == 1:
                log.debug("Forced refresh")
            apiResponse = osuapiHelper.osuApiRequest(
                "get_beatmaps", "s={}".format(beatmapSetID), False)
            if len(apiResponse) == 0:
                raise exceptions.invalidBeatmapException

            # Loop through all beatmaps in this set and save them in db
            data["maps"] = []
            for i in apiResponse:
                log.debug("Saving beatmap {} in db".format(i["file_md5"]))
                bmap = beatmap.beatmap(i["file_md5"],
                                       int(i["beatmapset_id"]),
                                       refresh=refresh)
                pp = glob.db.fetch(
                    "SELECT pp_100 FROM beatmaps WHERE beatmap_id = %s LIMIT 1",
                    [bmap.beatmapID])
                if pp is None:
                    pp = 0
                else:
                    pp = pp["pp_100"]
                data["maps"].append({
                    "id": bmap.beatmapID,
                    "name": bmap.songName,
                    "status": bmap.rankedStatus,
                    "frozen": bmap.rankedStatusFrozen,
                    "pp": pp,
                })

            # Set status code and message
            statusCode = 200
            data["message"] = "ok"
        except exceptions.invalidArgumentsException:
            # Set error and message
            statusCode = 400
            data["message"] = "missing required arguments"
        except exceptions.invalidBeatmapException:
            statusCode = 400
            data["message"] = "beatmap not found from osu!api."
        finally:
            # Add status code to data
            data["status"] = statusCode

            # Send response
            self.write(json.dumps(data))
            self.set_header("Content-Type", "application/json")
            #self.add_header("Access-Control-Allow-Origin", "*")
            self.set_status(statusCode)
示例#4
0
	def UpdateWrongDiff(self):
		bm = glob.db.fetch("SELECT beatmap_id,ranked, latest_update, beatmapset_id, beatmap_md5  FROM beatmaps WHERE mode = 0 AND difficulty_std = 0 AND hit_length>30 ORDER BY latest_update DESC LIMIT 1")
		if bm is not None:
			bmap = beatmap.beatmap(bm["beatmap_md5"], bm["beatmapset_id"], refresh=True)
			if(bmap.rankedStatus == rankedStatuses.NEED_UPDATE or bmap.rankedStatus < rankedStatuses.PENDING):
				self.stats["updated"] += 1
			self.stats["total"] += 1
		threading.Timer(15, self.UpdateWrongDiff).start()
		return
示例#5
0
	def UpdateQualif(self):
		bm = glob.db.fetch("SELECT beatmap_id,ranked, latest_update, beatmapset_id, beatmap_md5 FROM beatmaps WHERE ranked = 4 ORDER BY latest_update ASC LIMIT 1")
		if bm is not None:
			bmap = beatmap.beatmap(bm["beatmap_md5"], bm["beatmapset_id"], refresh=True)
			if(bmap.rankedStatus == rankedStatuses.NEED_UPDATE or bmap.rankedStatus < rankedStatuses.PENDING):
				self.stats["updated"] += 1
			self.stats["total"] += 1
		threading.Timer(self.time, self.UpdateQualif).start()
		return
示例#6
0
	def UpdateBeatmap(self):
		bm = glob.db.fetch("SELECT beatmap_id,ranked, latest_update, beatmapset_id, beatmap_md5 FROM beatmaps  WHERE ((ranked > 3 or ranked_status_freezed > 0) AND latest_update < %s) ORDER BY latest_update ASC LIMIT 1",[time.time() - 36400])
		if bm is not None:
			bmap = beatmap.beatmap(bm["beatmap_md5"], bm["beatmapset_id"], refresh=True)
			if(bmap.rankedStatus == rankedStatuses.NEED_UPDATE or bmap.rankedStatus < rankedStatuses.PENDING):
				self.stats["updated"] += 1
			self.stats["total"] += 1
		threading.Timer(self.time, self.UpdateBeatmap).start()
		return 
示例#7
0
    def setCompletedStatus(self, b=None):
        """
		Set this score completed status and rankedScoreIncrease
		"""
        self.completed = 0

        # Create beatmap object
        if b is None:
            b = beatmap.beatmap(self.fileMd5, 0)

        if self.passed == True and scoreUtils.isRankable(self.mods):
            # Get userID
            userID = userUtils.getID(self.playerName)

            # Make sure we don't have another score identical to this one
            duplicate = glob.db.fetch(
                "SELECT id FROM scores_relax WHERE userid = %s AND beatmap_md5 = %s AND play_mode = %s AND time = %s AND score = %s LIMIT 1",
                [userID, self.fileMd5, self.gameMode, self.date, self.score])
            if duplicate is not None:
                # Found same score in db. Don't save this score.
                self.completed = -1
                return

            # No duplicates found.
            # Get right "completed" value
            personalBest = glob.db.fetch(
                "SELECT id, pp, score FROM scores_relax WHERE userid = %s AND beatmap_md5 = %s AND play_mode = %s AND completed = 3 LIMIT 1",
                [userID, self.fileMd5, self.gameMode])
            if personalBest is None:
                # This is our first score on this map, so it's our best score
                self.completed = 3
                self.rankedScoreIncrease = self.score
                self.oldPersonalBest = 0
            else:
                # Compare personal best's score with current score
                if b.rankedStatus == rankedStatuses.RANKED or b.rankedStatus == rankedStatuses.APPROVED:
                    if self.pp > personalBest["pp"]:
                        # New best score
                        self.completed = 3
                        self.rankedScoreIncrease = self.score - personalBest[
                            "score"]
                        self.oldPersonalBest = personalBest["id"]
                    else:
                        self.completed = 2
                        self.rankedScoreIncrease = 0
                        self.oldPersonalBest = 0
                elif b.rankedStatus == rankedStatuses.LOVED:
                    if self.score > personalBest["score"]:
                        # New best score
                        self.completed = 3
                        self.rankedScoreIncrease = self.score - personalBest[
                            "score"]
                        self.oldPersonalBest = personalBest["id"]
                    else:
                        self.completed = 2
                        self.rankedScoreIncrease = 0
                        self.oldPersonalBest = 0
示例#8
0
def update(userID):
    achievement_ids = []

    entries = glob.db.fetchAll(
        "SELECT beatmap_md5, play_mode FROM scores WHERE completed = 3 AND userid = %s",
        [userID])
    for entry in entries:
        current_beatmap = beatmap.beatmap()
        current_beatmap.setDataFromDB(entry["beatmap_md5"])

        achievement_ids += check(entry["play_mode"], current_beatmap)

    return achievement_ids
示例#9
0
    def calculatePP(self, b=None):
        """
		Calculate this score's pp value if completed == 3
		"""
        # Create beatmap object
        if b is None:
            b = beatmap.beatmap(self.fileMd5, 0)

        # Calculate pp
        if b.rankedStatus >= rankedStatuses.RANKED and b.rankedStatus != rankedStatuses.LOVED and b.rankedStatus != rankedStatuses.UNKNOWN \
         and scoreUtils.isRankable(self.mods) and self.passed and self.gameMode in score.PP_CALCULATORS:
            calculator = score.PP_CALCULATORS[self.gameMode](b, self)
            self.pp = calculator.pp
        else:
            self.pp = 0
示例#10
0
    def recalcFromScoreData(scoreData):
        """
		Recalculate pp value for a score.
		Does every check, output and queries needed.

		score -- score+beatmap dictionary (returned from db with JOIN) of score to recalc
		return -- calculated pp value or None
		"""

        # Create score object and set its data
        s = score.score()
        s.setDataFromDict(scoreData)
        if s.scoreID == 0:
            # Make sure the score exists
            if glob.debug:
                consoleHelper.printColored(
                    "[!] No score with id {}".format(scoreData["id"]),
                    bcolors.RED)

        # Create beatmap object
        b = beatmap.beatmap()

        # Check if we have data for this song
        if scoreData["song_name"] is None or args.apirefresh == True:
            # If we don't have song data in scoreData, get with get_scores method (mysql, osuapi blabla)
            b.setData(scoreData["beatmap_md5"], 0)
        else:
            # If we have data, set data from dict
            b.setDataFromDict(scoreData)

        # Make sure the beatmap is ranked
        if b.rankedStatus < rankedStatuses.RANKED:
            if glob.debug:
                consoleHelper.printColored(
                    "[!] Beatmap {} is not ranked ().".format(s.fileMd5),
                    bcolors.RED)
            # Don't calculate pp if the beatmap is not ranked
            return False

        # Calculate score pp
        s.calculatePP(b)

        # Update score pp in dictionary
        scoreData["pp"] = s.pp
        return True
示例#11
0
    def recalc_score(self, score_data: Dict) -> scoreRelax2:
        """
        Recalculates pp for a score
        :param score_data: dict containing score and beatmap information about a score.
        :return: new `score` object, with `pp` attribute set to the new value
        """
        # Create score object and set its data
        s: scoreRelax2.score = scoreRelax2.score()
        s.setDataFromDict(score_data)
        s.passed = True

        # Create beatmap object and set its data
        b: beatmap.beatmap = beatmap.beatmap()
        b.setDataFromDict(score_data)

        # Calculate score pp
        s.calculatePP(b)
        del b
        return s
示例#12
0
    def recalc_score(self, score_data: Dict) -> score.baseScore:
        """
		Recalculates pp for a score

		:param score_data: dict containing score and beatmap information about a score.
		:return: new `score` object, with `pp` attribute set to the new value
		"""
        # Create score object and set its data
        s: score.baseScore = score.standardScore()
        s.setDataFromDict(score_data)
        s.passed = True

        # Create beatmap object and set its data
        b: beatmap.beatmap = beatmap.beatmap()
        b.setDataFromDict(score_data)

        # Calculate score pp
        s.calculatePP(b)
        if s.pp > 0:
            stat = b.stats.get(s.gameMode, s.mods)
            # print(s.scoreID, stat.length_drain, stat.actual_length_drain, stat.pp_scale)
        del b
        return s
示例#13
0
    def asyncGet(self, fileName=None):
        try:
            # Check arguments
            if fileName is None:
                raise exceptions.invalidArgumentsException(MODULE_NAME)
            if fileName == "":
                raise exceptions.invalidArgumentsException(MODULE_NAME)

            fileNameShort = fileName[:32] + \
                "..." if len(fileName) > 32 else fileName[:-4]
            log.info("Requested .osu file {}".format(fileNameShort))

            re = MAPFILE_REGEX.match(fileName)
            if not re:
                raise exceptions.invalidArgumentsException(MODULE_NAME)

            # Get .osu file from osu! server
            searchingBeatmap = beatmap.beatmap()
            searchingBeatmap.setData(None, None, unquote(fileName))

            if not searchingBeatmap.fileMD5:
                self.set_status(404)
                return self.write(b"")

            req = requests.get(
                f'{glob.conf.config["osuapi"]["apiurl"]}/osu/{searchingBeatmap.beatmapID}',
                timeout=20)
            req.encoding = "utf-8"
            response = req.content
            self.write(response)
            glob.dog.increment(glob.DATADOG_PREFIX +
                               ".osu_api.osu_file_requests")
        except exceptions.invalidArgumentsException:
            self.set_status(500)
        except exceptions.osuApiFailException:
            self.set_status(500)
示例#14
0
    def asyncPost(self):
        statusCode = 400
        data = {"message": "unknown error"}
        try:
            # Check arguments
            if not requestsManager.checkArguments(self.request.arguments,
                                                  ["refresh"]):
                raise exceptions.invalidArgumentsException(MODULE_NAME)

            userID = 0
            gameMode = 0
            outputType = 'dap_rank'
            if 'u' in self.request.arguments:
                userID = int(self.get_argument('u'))
            if 'm' in self.request.arguments:
                gameMode = int(self.get_argument('m'))
            if 'out' in self.request.arguments:
                if self.get_argument('out').lower() in ('page', ):
                    outputType = self.get_argument('out')
            # Get beatmap set data from osu api
            data["maps"] = []
            refresh = int(self.get_argument("refresh"))
            apiResponse = []
            if refresh == 1:
                log.debug("Forced refresh")
            if 'sid' in self.request.arguments:
                beatmapSetID = self.get_argument("sid")
                apiResponse = osuapiHelper.osuApiRequest(
                    "get_beatmaps", "s={}".format(beatmapSetID), False)
            elif 'bid' in self.request.arguments:
                beatmapID = self.get_argument("bid")
                apiResponse = osuapiHelper.osuApiRequest(
                    "get_beatmaps", "b={}".format(beatmapID), False)
            else:
                raise exceptions.invalidArgumentsException(MODULE_NAME)
            if len(apiResponse) == 0:
                raise exceptions.invalidBeatmapException
            forcefulStop = False
            for b in apiResponse:
                bm = glob.db.fetch(
                    'select beatmap_id id, beatmap_md5 md5 from beatmaps where beatmap_id = %s',
                    [b['beatmap_id']])
                if bm is None and not eligible.tryLoadBeatmap(userID):
                    forcefulStop = 'beatmapNotice2'
                if bm is not None and bm['md5'] != b[
                        'file_md5'] and not eligible.tryLoadBeatmap(userID):
                    forcefulStop = 'beatmapNotice3'
                if forcefulStop:
                    break
            if forcefulStop:
                params = {
                    "k": glob.conf.config["server"]["apikey"],
                    "to": userID,
                    "code": forcefulStop,
                    'force': 1
                }
                requests.get("{}/api/v1/specialNotice?{}".format(
                    glob.conf.config["server"]["banchourl"],
                    urlencode(params)))
                raise exceptions.invalidBeatmapException

            def processOutput(t, b, **x):
                if outputType == 'page':
                    if b.creatorSrc == 'datenshi_id':
                        ownerLink = "https://osu.troke.id/u/{}"
                    else:
                        ownerLink = "https://osu.ppy.sh/users/{}"
                    if b.mapperSrc == 'datenshi_id':
                        mapperLink = "https://osu.troke.id/u/{}"
                    else:
                        mapperLink = "https://osu.ppy.sh/users/{}"
                    mapStat = b.stats.get(b.gameMode or gameMode, 0)
                    mapDiff = {
                        'hp': mapStat.hp,
                        'od': mapStat.od,
                        'cs': mapStat.cs,
                        'ar': mapStat.ar,
                        'star': mapStat.rating,
                    }
                    if b.gameMode == 1:
                        del mapDiff['cs'], mapDiff['ar']
                    elif b.gameMode == 2:
                        del mapDiff['cs']
                    elif b.gameMode == 3:
                        del mapDiff['ar']
                    return {
                        'artist':
                        b.artist,
                        'title':
                        b.title,
                        'owner':
                        b.creatorName,
                        'owner_link':
                        ownerLink.format(b.creatorID),
                        'mapper':
                        b.mapperName or b.creatorName,
                        'mapper_link':
                        mapperLink.format(b.mapperID)
                        if b.mapperName else ownerLink.format(b.creatorID),
                        'source':
                        b.source,
                        'version':
                        b.difficultyName,
                        'difficulty':
                        mapDiff,
                        'length': {
                            'drain': mapStat.actual_length_drain,
                            'total': mapStat.actual_length_total,
                        },
                        'bpm':
                        b.bpm,
                    }
                else:
                    return {
                        "id": b.beatmapID,
                        "mode": b.mode,
                        "baseName": "{} - {}".format(b.artist, b.title),
                        "diffName": b.difficultyName,
                        "status": b.rankedStatus,
                        "frozen": b.rankedStatusFrozen,
                        "pp": x['pp'],
                        "request": x['request']
                    }

            # Loop through all beatmaps in this set and save them in db
            for i in apiResponse:
                log.debug("Saving beatmap {} in db".format(i["file_md5"]))
                bmap = beatmap.beatmap(i["file_md5"],
                                       int(i["beatmapset_id"]),
                                       refresh=refresh)
                if outputType == 'out' and bmap.takendown and not bmap.userHaveAccess(
                        userID):
                    continue
                pp = glob.db.fetch(
                    "SELECT pp_100 FROM beatmaps WHERE beatmap_id = %s LIMIT 1",
                    [bmap.beatmapID])
                if pp is None:
                    pp = 0
                else:
                    pp = pp["pp_100"]
                reqData = glob.db.fetch(
                    "SELECT userid as user_id, blacklisted as bad, hidden as done FROM rank_requests WHERE (`type` = 'b' and bid = %s) or (`type` = 's' and bid = %s)",
                    [bmap.beatmapID, bmap.beatmapSetID])
                data["maps"].append(
                    processOutput(outputType, bmap, pp=pp, request=reqData))

            # Set status code and message
            statusCode = 200
            data["message"] = "ok"
        except exceptions.invalidArgumentsException:
            # Set error and message
            statusCode = 400
            data["message"] = "missing required arguments"
        except exceptions.invalidBeatmapException:
            statusCode = 400
            data["message"] = "beatmap not found from osu!api."
        finally:
            # Add status code to data
            data["status"] = statusCode

            # Send response
            self.write(json.dumps(data))
            self.set_header("Content-Type", "application/json")
            #self.add_header("Access-Control-Allow-Origin", "*")
            self.set_status(statusCode)
示例#15
0
	def asyncGet(self):
		statusCode = 400
		data = {"message": "unknown error"}
		try:
			# Check arguments
			if not requestsManager.checkArguments(self.request.arguments, ["b"]):
				raise exceptions.invalidArgumentsException(MODULE_NAME)

			# Get beatmap ID and make sure it's a valid number
			beatmapID = self.get_argument("b")
			if not beatmapID.isdigit():
				raise exceptions.invalidArgumentsException(MODULE_NAME)

			# Get mods
			if "m" in self.request.arguments:
				modsEnum = self.get_argument("m")
				if not modsEnum.isdigit():
					raise exceptions.invalidArgumentsException(MODULE_NAME)
				modsEnum = int(modsEnum)
			else:
				modsEnum = 0

			# Get game mode
			if "g" in self.request.arguments:
				gameMode = self.get_argument("g")
				if not gameMode.isdigit():
					raise exceptions.invalidArgumentsException(MODULE_NAME)
				gameMode = int(gameMode)
			else:
				gameMode = 0

			# Get acc
			if "a" in self.request.arguments:
				accuracy = self.get_argument("a")
				try:
					accuracy = float(accuracy)
					if math.isnan(accuracy):
						accuracy = -1.0
				except ValueError:
					raise exceptions.invalidArgumentsException(MODULE_NAME)
			else:
				accuracy = -1.0

			if "x" in self.request.arguments:
				misses = self.get_argument("x")
				try:
					misses = int(misses)
				except ValueError:
					raise exceptions.invalidArgumentsException(MODULE_NAME)
			else:
				misses = 0

			if "c" in self.request.arguments:
				combo = self.get_argument("c")
				if not combo.isdigit():
					raise exceptions.invalidArgumentsException(MODULE_NAME)
				combo = int(combo)
			else:
				combo = 0


			# Print message
			log.info("Requested pp for beatmap {}".format(beatmapID))

			# Get beatmap md5 from osuapi
			# TODO: Move this to beatmap object
			"""osuapiData = osuapiHelper.osuApiRequest("get_beatmaps", "b={}".format(beatmapID))
			if osuapiData is None or "file_md5" not in osuapiData or "beatmapset_id" not in osuapiData:
				raise exceptions.invalidBeatmapException(MODULE_NAME)
			beatmapMd5 = osuapiData["file_md5"]
			beatmapSetID = osuapiData["beatmapset_id"]"""
			dbData = glob.db.fetch("SELECT beatmap_md5, beatmapset_id, mode FROM beatmaps WHERE beatmap_id = {}".format(beatmapID))
			if dbData is None:
				raise exceptions.invalidBeatmapException(MODULE_NAME)

			beatmapMd5 = dbData["beatmap_md5"]
			beatmapSetID = dbData["beatmapset_id"]

			# Create beatmap object
			bmap = beatmap.beatmap(beatmapMd5, beatmapSetID)
			stars = 0
			# Check beatmap length
			if bmap.hitLength > 900:
				raise exceptions.beatmapTooLongException(MODULE_NAME)

			returnPP = []
			if gameMode == gameModes.STD and bmap.starsStd == 0:
				gameMode = dbData["mode"]
				if(gameMode == 2):
					raise exceptions.unsupportedGameModeException
			if accuracy > 100 or combo > bmap.maxCombo or misses < 0 or math.isnan(accuracy):
				raise exceptions.invalidArgumentsException(MODULE_NAME)
			# Calculate pp
			if gameMode != 2:
				# Std pp
				if accuracy < 0 and combo == 0 and misses == 0 and gameMode == dbData["mode"]:
					# Generic acc
					# Get cached pp values
					cachedPP = bmap.getCachedTillerinoPP()
					if(modsEnum == 0 and cachedPP != [0,0,0,0]):
						log.debug("Got cached pp.")
						returnPP = cachedPP
						stars = bmap.starsStd
					else:
						log.debug("Cached pp not found. Calculating pp with oppai...")
						# Cached pp not found, calculate them
						if(gameMode == 1 or gameMode == 0):
							oppai = rippoppai.oppai(bmap, mods=modsEnum, tillerino=True, stars=True)
							returnPP = oppai.pp
							stars = oppai.stars
						else:
							xeno = omppcPy.piano(bmap, mods=modsEnum, tillerino=True, stars=True)
							returnPP = xeno.pp
							stars = xeno.stars

						# Cache values in DB
						log.debug("Saving cached pp...")
						if(modsEnum == 0 and type(returnPP) != int and len(returnPP) == 4):
							bmap.saveCachedTillerinoPP(returnPP)
				else:
					# Specific accuracy, calculate
					# Create oppai instance
					if(gameMode == 3):
							log.info("Specific request ({}%/{}). Calculating pp with omppc...".format(accuracy, modsEnum))
							xeno = omppcPy.piano(bmap, acc=accuracy, mods=modsEnum, tillerino=False, stars=True)
							returnPP.append(xeno.pp)
							stars = xeno.stars	
					else:
						if(gameMode != 0 and gameMode != 1):
							raise exceptions.unsupportedGameModeException
						log.debug("Specific request ({}%/{}). Calculating pp with oppai...".format(accuracy, modsEnum))
						oppai = rippoppai.oppai(bmap, mods=modsEnum, tillerino=False, stars=True)
						stars = oppai.stars
						if accuracy > 0:
							oppai.acc = accuracy
						if combo > 0:
							oppai.combo = combo
						if misses > 0:
							oppai.misses = misses	
						oppai.getPP()
						returnPP.append(oppai.pp)
			else:
				raise exceptions.unsupportedGameModeException

			# Data to return
			data = {
				"song_name": bmap.songName,
				"pp": returnPP,
				"length": round(bmap.hitLength*0.75) if modsEnum & mods.DOUBLETIME > 0 else bmap.hitLength if modsEnum & mods.HALFTIME < 1 else round(bmap.hitLength*1.50),
				"stars": stars,
				"ar": round(min(10, bmap.AR * 1.4),1) if modsEnum & mods.HARDROCK > 0 else bmap.AR if modsEnum & mods.EASY < 1 else round(max(0, bmap.AR / 2),1),
				"od": round(min(10, bmap.OD * 1.4),1) if modsEnum & mods.HARDROCK > 0 else bmap.OD if modsEnum & mods.EASY < 1 else round(max(0, bmap.OD / 2),1),
				"bpm": round(bmap.bpm*1.5) if modsEnum & mods.DOUBLETIME > 0 else bmap.bpm if modsEnum & mods.HALFTIME < 1 else round(bmap.bpm*0.75),
			}
			# Set status code and message
			statusCode = 200
			data["message"] = "ok"
		except exceptions.invalidArgumentsException:
			# Set error and message
			statusCode = 400
			data["message"] = "missing required arguments"
		except exceptions.invalidBeatmapException:
			statusCode = 400
			data["message"] = "beatmap not found"
		except exceptions.beatmapTooLongException:
			statusCode = 400
			data["message"] = "requested beatmap is too long"
		except exceptions.unsupportedGameModeException:
			statusCode = 400
			data["message"] = "Unsupported gamemode"
		finally:
			# Add status code to data
			data["status"] = statusCode

			# Debug output
			log.debug(str(data))

			# Send response
			#self.clear()
			self.write(json.dumps(data))
			self.set_header("Content-Type", "application/json")
			self.set_status(statusCode)
示例#16
0
    def autorankCheck(beatmap, recursive=True):
        # No autorank check for frozen maps
        if beatmap.rankedStatusFrozen not in (0, 3):
            return
        # No autorank check for already ranked beatmap
        # Only handle this if the map is ranked by autorank.
        if beatmap.rankedStatusFrozen != 3 and beatmap.rankedStatus >= 2:
            return
        # Only check if the map is autorankable.
        if not autorankFlagOK(beatmap.beatmapID):
            return
        # Check recursiveness
        if recursive:
            beatmap_id_query = glob.db.fetchAll(
                'select beatmap_md5, beatmap_id from beatmaps where beatmapset_id = %s',
                [beatmap.beatmapSetID])
            for beatmap_id_data in beatmap_id_query:
                # Skip self.
                if int(beatmap_id_data['beatmap_id']) == beatmap.beatmapID:
                    continue
                siblingmap = bm.beatmap(None, beatmap.beatmapSetID)
                siblingmap.setData(beatmap_id_data['beatmap_md5'],
                                   beatmap.beatmapSetID, True)
        # Not checking maps with non updated date.
        #if recursive:
        #log.info(f"Checking {beatmap.artist} - {beatmap.title} for autorank eligiblity.")
        # log.info(f"Checking {beatmap.songName} for autorank eligiblity.")
        obtainDateTime = lambda t: datetime.datetime.strptime(
            t, "%Y-%m-%d %H:%M:%S")
        obtainUnixClock = lambda t: int(time.mktime(t.timetuple()))
        if beatmap.updateDate == 0:
            log.info(f"Updating {beatmap.fileMD5} data")
            data = osuapiHelper.osuApiRequest('get_beatmaps',
                                              'h={}'.format(beatmap.fileMD5))
            if not data:
                return
            dateTouch = obtainDateTime(data['last_update'])
            beatmap.updateDate = obtainUnixClock(dateTouch)
        else:
            dateTouch = datetime.datetime.fromtimestamp(beatmap.updateDate)

        dateNow = datetime.datetime.today()
        dateQualify = dateTouch + datetime.timedelta(days=GRAVEYARD_DAYS -
                                                     QUALIFIED_DAYS)
        dateRanked = dateTouch + datetime.timedelta(days=GRAVEYARD_DAYS)
        forLove = autorankFlagForLove(beatmap.beatmapID)
        rankStatus = beatmap.rankedStatus
        needWipe = False
        if dateNow >= dateRanked:
            needWipe = rankStatus == rankedStatuses.QUALIFIED
            if forLove:
                log.debug(f"Considering {beatmap.fileMD5} to be loved")
                beatmap.rankedStatus = rankedStatuses.LOVED
            else:
                log.debug(f"Considering {beatmap.fileMD5} on ranking")
                beatmap.rankedStatus = rankedStatuses.RANKED
            beatmap.rankedStatusFrozen = 3
        elif dateNow >= dateQualify and not forLove:
            log.debug(f"Considering {beatmap.fileMD5} for qualified")
            beatmap.rankedStatus = rankedStatuses.QUALIFIED
            beatmap.rankedStatusFrozen = 3
        else:
            needWipe = rankStatus >= rankedStatuses.RANKED
            beatmap.rankedStatus = rankedStatuses.PENDING
            beatmap.rankedStatusFrozen = 0
        if rankStatus != beatmap.rankedStatus:
            dbBeatmap = glob.db.fetch(
                'select ranked_status_freezed as f from beatmaps where beatmap_md5 = %s',
                [beatmap.fileMD5])
            oldFreeze = 0
            if dbBeatmap is not None:
                oldFreeze = dbBeatmap['f']
            glob.db.execute(
                'update beatmaps set ranked_status_freezed = 0 where beatmap_md5 = %s',
                [beatmap.fileMD5])
            if oldFreeze != beatmap.rankedStatusFrozen and recursive:
                autorankAnnounce(beatmap)
            glob.db.execute(
                'update beatmaps set rankedby = %s where beatmap_md5 = %s',
                [autorankUserID(beatmap.beatmapID), beatmap.fileMD5])
        if needWipe:
            log.info(f"Wiping {beatmap.fileMD5} leaderboard")
            beatmap.clearLeaderboard()
            pass
示例#17
0
    def asyncGet(self):
        statusCode = 400
        data = {"message": "unknown error"}
        try:
            # Check arguments
            if not requestsManager.checkArguments(self.request.arguments,
                                                  ["b"]):
                raise exceptions.invalidArgumentsException(MODULE_NAME)

            # Get beatmap ID and make sure it's a valid number
            beatmapID = self.get_argument("b")
            if not beatmapID.isdigit():
                raise exceptions.invalidArgumentsException(MODULE_NAME)

            # Get mods
            if "m" in self.request.arguments:
                modsEnum = self.get_argument("m")
                if not modsEnum.isdigit():
                    raise exceptions.invalidArgumentsException(MODULE_NAME)
                modsEnum = int(modsEnum)
            else:
                modsEnum = 0

            # Get game mode
            if "g" in self.request.arguments:
                gameMode = self.get_argument("g")
                if not gameMode.isdigit():
                    raise exceptions.invalidArgumentsException(MODULE_NAME)
                gameMode = int(gameMode)
            else:
                gameMode = 0

            # Get acc
            if "a" in self.request.arguments:
                accuracy = self.get_argument("a")
                try:
                    accuracy = float(accuracy)
                except ValueError:
                    raise exceptions.invalidArgumentsException(MODULE_NAME)
            else:
                accuracy = None

            # Print message
            log.info("Requested pp for beatmap {}".format(beatmapID))

            # Get beatmap md5 from osuapi
            # TODO: Move this to beatmap object
            osuapiData = osuapiHelper.osuApiRequest("get_beatmaps",
                                                    "b={}".format(beatmapID))
            if int(beatmapID) > 100000000:
                raise exceptions.ppCustomBeatmap(MODULE_NAME)
            if osuapiData is None or "file_md5" not in osuapiData or "beatmapset_id" not in osuapiData:
                raise exceptions.invalidBeatmapException(MODULE_NAME)
            beatmapMd5 = osuapiData["file_md5"]
            beatmapSetID = osuapiData["beatmapset_id"]

            # Create beatmap object
            bmap = beatmap.beatmap(beatmapMd5, beatmapSetID)

            # Check beatmap length
            if bmap.hitLength > 900:
                raise exceptions.beatmapTooLongException(MODULE_NAME)

            returnPP = []
            if gameMode == gameModes.STD and bmap.starsStd == 0:
                # Mode Specific beatmap, auto detect game mode
                if bmap.starsTaiko > 0:
                    gameMode = gameModes.TAIKO
                if bmap.starsCtb > 0:
                    gameMode = gameModes.CTB
                if bmap.starsMania > 0:
                    gameMode = gameModes.MANIA

            # Calculate pp
            if gameMode in (gameModes.STD, gameModes.TAIKO):
                # Std pp
                if accuracy is None and modsEnum == 0:
                    # Generic acc/nomod
                    # Get cached pp values
                    cachedPP = bmap.getCachedTillerinoPP()
                    if (modsEnum & mods.RELAX):
                        cachedPP = [0, 0, 0, 0]
                    elif (modsEnum & mods.RELAX2):
                        cachedPP = [0, 0, 0, 0]

                    if cachedPP != [0, 0, 0, 0]:
                        log.debug("Got cached pp.")
                        returnPP = cachedPP
                    else:
                        log.debug(
                            "Cached pp not found. Calculating pp with oppai..."
                        )
                        # Cached pp not found, calculate them
                        if gameMode == gameModes.STD and (modsEnum
                                                          & mods.RELAX):
                            oppai = relaxoppai.oppai(bmap,
                                                     mods=modsEnum,
                                                     tillerino=True)
                        elif gameMode == gameModes.STD and (modsEnum
                                                            & mods.RELAX2):
                            oppai = relax2oppai.oppai(bmap,
                                                      mods=modsEnum,
                                                      tillerino=True)
                        else:
                            oppai = rippoppai.oppai(bmap,
                                                    mods=modsEnum,
                                                    tillerino=True)
                        returnPP = oppai.pp
                        bmap.starsStd = oppai.stars

                        if not (modsEnum & mods.RELAX) or (modsEnum
                                                           & mods.RELAX2):
                            # Cache values in DB
                            log.debug("Saving cached pp...")
                            if type(returnPP) == list and len(returnPP) == 4:
                                bmap.saveCachedTillerinoPP(returnPP)
                else:
                    # Specific accuracy/mods, calculate pp
                    # Create oppai instance
                    log.debug(
                        "Specific request ({}%/{}). Calculating pp with oppai..."
                        .format(accuracy, modsEnum))
                    if gameMode == gameModes.STD and (modsEnum & mods.RELAX):
                        oppai = relaxoppai.oppai(bmap,
                                                 mods=modsEnum,
                                                 tillerino=True)
                    elif gameMode == gameModes.STD and (modsEnum
                                                        & mods.RELAX2):
                        oppai = relax2oppai.oppai(bmap,
                                                  mods=modsEnum,
                                                  tillerino=True)
                    else:
                        oppai = rippoppai.oppai(bmap,
                                                mods=modsEnum,
                                                tillerino=True)
                    bmap.starsStd = oppai.stars
                    if accuracy is not None:
                        returnPP = calculatePPFromAcc(oppai, accuracy)
                    else:
                        returnPP = oppai.pp
            else:
                raise exceptions.unsupportedGameModeException()

            # Data to return
            data = {
                "song_name": bmap.songName,
                "pp":
                [x for x in returnPP] if type(returnPP) is list else returnPP,
                "length": bmap.hitLength,
                "stars": bmap.starsStd,
                "ar": bmap.AR,
                "bpm": bmap.bpm,
            }

            # Set status code and message
            statusCode = 200
            data["message"] = "ok"
        except exceptions.invalidArgumentsException:
            # Set error and message
            statusCode = 400
            data["message"] = "missing required arguments"
        except exceptions.ppCustomBeatmap:
            statusCode = 400
            data[
                "message"] = "Custom map does not supported pp calculating yet"
        except exceptions.invalidBeatmapException:
            statusCode = 400
            data["message"] = "beatmap not found"
        except exceptions.beatmapTooLongException:
            statusCode = 400
            data["message"] = "requested beatmap is too long"
        except exceptions.unsupportedGameModeException:
            statusCode = 400
            data["message"] = "Unsupported gamemode"
        finally:
            # Add status code to data
            data["status"] = statusCode

            # Debug output
            log.debug(str(data))

            # Send response
            #self.clear()
            self.write(json.dumps(data))
            self.set_header("Content-Type", "application/json")
            self.set_status(statusCode)
示例#18
0
    def asyncGet(self):
        statusCode = 400
        data = {"message": "unknown error"}
        try:
            # Check arguments
            if not requestsManager.checkArguments(self.request.arguments,
                                                  ["b"]):
                raise exceptions.invalidArgumentsException(self.MODULE_NAME)

            # Get beatmap ID and make sure it's a valid number
            beatmapId = self.get_argument("b")
            if not beatmapId.isdigit():
                raise exceptions.invalidArgumentsException(self.MODULE_NAME)

            # Get mods
            if "m" in self.request.arguments:
                modsEnum = self.get_argument("m")
                if not modsEnum.isdigit():
                    raise exceptions.invalidArgumentsException(
                        self.MODULE_NAME)
                modsEnum = int(modsEnum)
            else:
                modsEnum = 0

            # Get game mode
            if "g" in self.request.arguments:
                gameMode = self.get_argument("g")
                if not gameMode.isdigit():
                    raise exceptions.invalidArgumentsException(
                        self.MODULE_NAME)
                gameMode = int(gameMode)
            else:
                gameMode = 0

            # Get acc
            if "a" in self.request.arguments:
                accuracy = self.get_argument("a")
                try:
                    accuracy = float(accuracy)
                except ValueError:
                    raise exceptions.invalidArgumentsException(
                        self.MODULE_NAME)
            else:
                accuracy = None

            # Print message
            log.info("Requested pp for beatmap {}".format(beatmapId))

            # Get beatmap md5 from osuapi
            # TODO: Move this to beatmap object
            osuapiData = osuapiHelper.osuApiRequest("get_beatmaps",
                                                    "b={}".format(beatmapId))
            if osuapiData is None or "file_md5" not in osuapiData or "beatmapset_id" not in osuapiData:
                raise exceptions.invalidBeatmapException(self.MODULE_NAME)
            beatmapMd5 = osuapiData["file_md5"]
            beatmapSetID = osuapiData["beatmapset_id"]

            # Create beatmap object
            bmap = beatmap.beatmap(beatmapMd5, beatmapSetID)

            # Check beatmap length
            # TODO: Why do we do this?
            if bmap.hit_length > 900:
                raise exceptions.beatmapTooLongException(self.MODULE_NAME)

            if gameMode == gameModes.STD and bmap.starsStd == 0:
                # Mode Specific beatmap, auto detect game mode
                if bmap.starsTaiko > 0:
                    gameMode = gameModes.TAIKO
                if bmap.starsCtb > 0:
                    gameMode = gameModes.CTB
                if bmap.starsMania > 0:
                    gameMode = gameModes.MANIA

            # Calculate pp
            if gameMode in (gameModes.STD, gameModes.TAIKO):
                # osu!standard and osu!taiko
                oppai = ez.Ez(bmap,
                              mods_=modsEnum,
                              tillerino=accuracy is None,
                              acc=accuracy,
                              gameMode=gameMode)
                bmap.starsStd = oppai.stars
                returnPP = oppai.pp
            elif gameMode == gameModes.CTB:
                # osu!catch
                ciccio = cicciobello.Cicciobello(bmap,
                                                 mods_=modsEnum,
                                                 tillerino=accuracy is None,
                                                 accuracy=accuracy)
                bmap.starsStd = ciccio.stars
                returnPP = ciccio.pp
            else:
                raise exceptions.unsupportedGameModeException()

            # Data to return
            data = {
                "song_name": bmap.songName,
                "pp":
                [x for x in returnPP] if type(returnPP) is list else returnPP,
                "game_mode": gameMode,
                "length": bmap.hit_length,
                "stars": bmap.starsStd
                if bmap.starsStd is not None else bmap.difficultyrating,
                "ar": bmap.diff_approach,
                "bpm": bmap.bpm,
            }

            # Set status code and message
            statusCode = 200
            data["message"] = "ok"
        except exceptions.invalidArgumentsException:
            # Set error and message
            statusCode = 400
            data["message"] = "missing required arguments"
        except exceptions.invalidBeatmapException:
            statusCode = 400
            data["message"] = "beatmap not found"
        except exceptions.beatmapTooLongException:
            statusCode = 400
            data["message"] = "requested beatmap is too long"
        except exceptions.unsupportedGameModeException:
            statusCode = 400
            data["message"] = "Unsupported gamemode"
        finally:
            # Add status code to data
            data["status"] = statusCode

            # Debug output
            log.debug(str(data))

            # Send response
            self.write(json.dumps(data))
            self.set_header("Content-Type", "application/json")
            self.set_status(statusCode)
示例#19
0
def update_beatmaps(args):
    beatmaps = glob.db.fetchAll(
        'select beatmapset_id as sid, beatmap_id as id, beatmap_md5 as md5 from beatmaps'
    )
    for bm in beatmaps:
        b = beatmap.beatmap()
        b.beatmapID, b.fileMD5 = bm['id'], bm['md5']
        if bm['id'] > 100000000:
            continue
        fn = mapsHelper.cachedMapPath(bm['id'])
        if mapsHelper.os.path.isfile(fn) and mapsHelper.generalUtils.fileMd5(
                fn) == bm['md5']:
            continue
        try:
            if args.download_update:
                if mapsHelper.os.path.isfile(fn):
                    if mapsHelper.generalUtils.fileMd5(fn) != bm['md5']:
                        log.info("BID {}: Hash difference ({}) expected ({})".
                                 format(
                                     bm['id'],
                                     mapsHelper.generalUtils.fileMd5(fn)[:8],
                                     bm['md5'][:8]))
                else:
                    log.info("BID {}: Not exists, downloading...".format(
                        bm['id']))
                mapsHelper.cacheMap(fn, b)
            if mapsHelper.os.path.isfile(
                    fn) and mapsHelper.generalUtils.fileMd5(fn) != bm['md5']:
                bm['update.md5'] = mapsHelper.generalUtils.fileMd5(fn)
            else:
                bm['delete.file'] = True
        except Exception as e:
            log.warning('BID {}: ({}) {}'.format(bm['id'],
                                                 type(e).__name__, e))
            pass
        if args.download_update:
            time.sleep(2.5)
    stmts = []
    bmgroup = []

    def destructor(query):
        nonlocal bmgroup
        query += " end where beatmap_id in ({})".format(','.join(
            str(bm) for bm in bmgroup))
        del bmgroup[:]
        return query

    stmt = QueryBuilder("update beatmaps set beatmap_md5=case ", '', " ")
    stmt.finisher = destructor
    for bm in beatmaps:
        if 'delete.file' in bm:
            continue
        if 'update.md5' not in bm:
            continue
        bmgroup.append(bm['id'])
        stmtline = "when beatmap_id={} then '{}'".format(
            bm['id'], bm['update.md5'])
        newQuery = stmt.appendAndBuild(stmtline)
        if newQuery:
            stmts.append(newQuery)
    if stmt.query:
        stmts.append(stmt.build())
    for s in stmts:
        # print(s)
        glob.db.execute(s)