From aec3ba434ad03ab58bb7ee7a4fa900a4ca94e501 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Ribeiro=20Bezerra?= Date: Thu, 13 Jun 2024 16:15:04 +0100 Subject: [PATCH] Fixes --- layout/include/assetUtils.js | 11 +- layout/include/globals.js | 100 ++++++++---------- layout/stream_queue/index.js | 4 +- layout/stream_queue/settings.json | 2 +- layout/stream_queue_simple/index.js | 4 +- layout/versus_screen/index.js | 2 +- layout/versus_screen_pokken/index.js | 2 +- layout/versus_screen_tekken8/index.css | 1 - layout/versus_screen_tekken8/index.js | 2 +- src/TSHAssetDownloader.py | 10 +- src/TSHGameAssetManager.py | 13 ++- src/TSHSelectSetWindow.py | 40 +++++-- src/TSHThumbnailSettingsWidget.py | 45 +++++--- src/TSHTournamentDataProvider.py | 24 ++++- .../ChallongeDataProvider.py | 34 +++--- .../StartGGDataProvider.py | 81 +++++++++----- .../StartGGSetQuery.txt | 1 + .../TournamentDataProvider.py | 20 ++-- src/TournamentStreamHelper.py | 2 +- src/Workers.py | 25 +++-- 20 files changed, 263 insertions(+), 160 deletions(-) diff --git a/layout/include/assetUtils.js b/layout/include/assetUtils.js index cc25aa941..e60dc6e23 100644 --- a/layout/include/assetUtils.js +++ b/layout/include/assetUtils.js @@ -252,7 +252,10 @@ async function updateCharacterContainer(e, event) { } await Promise.allSettled(loads); - gsap.fromTo($(e).children(".tsh_character"), anim_out, anim_in); + + if($(e) && $(e).children(".tsh_character").length > 0){ + gsap.fromTo($(e).children(".tsh_character"), anim_out, anim_in); + } }; if (firstRun) { @@ -260,7 +263,11 @@ async function updateCharacterContainer(e, event) { await callback(); } else { // Fade out, then change data and fade in - gsap.to($(e).children(".tsh_character"), anim_out).then(callback); + if($(e) && $(e).children(".tsh_character").length > 0){ + gsap.to($(e).children(".tsh_character"), anim_out).then(callback); + } else { + await callback(); + } } } } diff --git a/layout/include/globals.js b/layout/include/globals.js index 6ba0f6406..4729d7bd7 100755 --- a/layout/include/globals.js +++ b/layout/include/globals.js @@ -27,11 +27,11 @@ async function UpdateWrapper(event) { // If initialization wasn't done yet, call Start() // We use gsap.globalTimeline.timeScale as 0 for animation to not play before this if (gsap.globalTimeline.timeScale() == 0) { + gsap.globalTimeline.timeScale(1); window.requestAnimationFrame(() => { $(document).waitForImages(() => { $("body").fadeTo(1, 1, () => { Start(); - gsap.globalTimeline.timeScale(1); }); }); }); @@ -199,10 +199,11 @@ async function InitAll() { } else { // Call program_state.json load just in case it takes // a bit to start the websocket - // await UpdateData(); await UpdateData_SocketIO(); } + // await UpdateData(); + console.log("== Init complete =="); document.dispatchEvent(new CustomEvent("tsh_init")); @@ -329,77 +330,70 @@ async function Transcript(text) { // Sequence: runs anim_out > changes content > runs anim_in // Uses FitText to scale div contents to fit async function SetInnerHtml(element, html, settings = {}) { - let force = undefined; - let fadeTime = 0.5; - let middleFunction = undefined; + // Extract settings + let { force, fadeTime = 0.5, middleFunction } = settings; if (element == null) return; - if (force == false) return; + if (force === false) return; // Fade out/in animations - let anim_in = { autoAlpha: 1, duration: fadeTime, stagger: 0.1 }; - - if (settings.anim_in) { - anim_in = settings.anim_in; - } - - let anim_out = { autoAlpha: 0, duration: fadeTime, stagger: 0.1 }; - - if (settings.anim_out) { - anim_out = settings.anim_out; - } + let anim_in = { autoAlpha: 1, duration: fadeTime, stagger: 0.1, ...settings.anim_in }; + let anim_out = { autoAlpha: 0, duration: fadeTime, stagger: 0.1, overwrite: true, ...settings.anim_out }; - anim_out.overwrite = true; - - if (html == null || html == undefined) html = ""; + if (html == null || html === undefined) html = ""; html = String(html); - let firstRun = false; + let firstRun = element.find(".text").length === 0; - // First run, no need of smooth fade out - if (element.find(".text").length == 0) { + // First run, no need for smooth fade out + if (firstRun) { // Put any text inside the div just so the font loading is triggered element.html("
 
"); - firstRun = true; } // Wait for font to load before calculating sizes - document.fonts.ready.then(() => { - if ( - force == true || - he.decode(String(element.find(".text").html()).replace(/'/g, '"')) != - he.decode(String(html).replace(/'/g, '"')) - ) { - const callback = () => { - element.find(".text").html(html); - if (html.trim().length == 0) { - element.find(".text").addClass("text_empty"); - element.addClass("text_empty"); - } else { - element.find(".text").removeClass("text_empty"); - element.removeClass("text_empty"); - } - FitText(element); - if (middleFunction != undefined) { - middleFunction(); - } - $(element).ready((e) => { - gsap.fromTo(element.find(".text"), anim_out, anim_in); - }); - }; + await document.fonts.ready; + + // Decode the HTML content to compare + const currentText = he.decode(String(element.find(".text").html()).replace(/'/g, '"')); + const newText = he.decode(String(html).replace(/'/g, '"')); + + if (force === true || currentText !== newText) { + const updateElement = () => { + element.find(".text").html(html); - if (!firstRun) { - gsap.to(element.find(".text"), anim_out).then(() => callback()); + if (html.trim().length === 0) { + element.find(".text").addClass("text_empty"); + element.addClass("text_empty"); } else { - let newAnimOut = Object.assign({}, anim_out); - newAnimOut.duration = 0; - gsap.to(element.find(".text"), anim_out).then(() => callback()); + element.find(".text").removeClass("text_empty"); + element.removeClass("text_empty"); } + + FitText(element); + + if (middleFunction) { + middleFunction(); + } + + if (firstRun) { + gsap.set(element.find(".text"), anim_in); + } else { + gsap.fromTo(element.find(".text"), anim_out, anim_in); + } + }; + + if (!firstRun) { + await gsap.to(element.find(".text"), anim_out).then(updateElement); + } else { + anim_out.duration = 1; + await gsap.to(element.find(".text"), anim_out).then(updateElement); } - }); + } } + const degrees_to_radians = (deg) => (deg * Math.PI) / 180.0; // Given a number of characters, returns an array os positions (0-1) for their eyesights diff --git a/layout/stream_queue/index.js b/layout/stream_queue/index.js index 7e6599f51..5d9b63e4c 100644 --- a/layout/stream_queue/index.js +++ b/layout/stream_queue/index.js @@ -125,8 +125,8 @@ LoadEverything().then(() => { ${ player.pronoun ? `
${ wrap_text((!isTeams && true) ? String(player.pronoun) : "") }
` : '' } - ${ team.seed ? - `
${wrap_text("Seed " + team.seed)}
` : '' + ${ player.seed ? + `
${wrap_text("Seed " + player.seed)}
` : '' } diff --git a/layout/stream_queue/settings.json b/layout/stream_queue/settings.json index 2e300bb9f..ec0c85b73 100644 --- a/layout/stream_queue/settings.json +++ b/layout/stream_queue/settings.json @@ -3,7 +3,7 @@ "default": {} }, "stream": "", - "default_stream" : "vgbootcamp", + "default_stream" : "", "force_multistream" : false, "display_stream_name" : "multistream", "sets_displayed" : -1, diff --git a/layout/stream_queue_simple/index.js b/layout/stream_queue_simple/index.js index 0dce0ea20..fe56147e8 100644 --- a/layout/stream_queue_simple/index.js +++ b/layout/stream_queue_simple/index.js @@ -125,8 +125,8 @@ LoadEverything().then(() => { ${ player.pronoun ? `
${ wrap_text((!isTeams && true) ? String(player.pronoun) : "") }
` : '' } - ${ team.seed ? - `
${wrap_text("Seed " + team.seed)}
` : '' + ${ player.seed ? + `
${wrap_text("Seed " + player.seed)}
` : '' } diff --git a/layout/versus_screen/index.js b/layout/versus_screen/index.js index 2d4f40327..9479fe37d 100755 --- a/layout/versus_screen/index.js +++ b/layout/versus_screen/index.js @@ -257,7 +257,7 @@ LoadEverything().then(() => { SetInnerHtml($(`.p${t + 1} .flagcountry`), ""); SetInnerHtml($(`.p${t + 1} .flagstate`), ""); SetInnerHtml($(`.p${t + 1} .pronoun`), ""); - SetInnerHtml($(`.p${t + 1} .seed`), team.seed ? `Seed ${team.seed}` : ""); + SetInnerHtml($(`.p${t + 1} .seed`), _.get(team, "player.1.seed") ? `Seed ${_.get(team, "player.1.seed")}` : ""); let characterNames = []; diff --git a/layout/versus_screen_pokken/index.js b/layout/versus_screen_pokken/index.js index d2161521c..cf288260f 100755 --- a/layout/versus_screen_pokken/index.js +++ b/layout/versus_screen_pokken/index.js @@ -265,7 +265,7 @@ LoadEverything().then(() => { SetInnerHtml($(`.p${t + 1} .pronoun`), ""); - SetInnerHtml($(`.p${t + 1} .seed`), team.seed ? `Seed ${team.seed}` : ""); + SetInnerHtml($(`.p${t + 1} .seed`), _.get(team, "player.1.seed") ? `Seed ${_.get(team, "player.1.seed")}` : ""); let characterNames = []; diff --git a/layout/versus_screen_tekken8/index.css b/layout/versus_screen_tekken8/index.css index 6f89790b8..4a527aa0c 100755 --- a/layout/versus_screen_tekken8/index.css +++ b/layout/versus_screen_tekken8/index.css @@ -34,7 +34,6 @@ body { background-size: cover; background-position: center; background-repeat: no-repeat; - background-image: url(./bg.png); filter: blur(5px) contrast(1.6) saturate(0.5) brightness(0.8); } diff --git a/layout/versus_screen_tekken8/index.js b/layout/versus_screen_tekken8/index.js index ccc445c37..c2c036f77 100755 --- a/layout/versus_screen_tekken8/index.js +++ b/layout/versus_screen_tekken8/index.js @@ -387,7 +387,7 @@ LoadEverything().then(() => { SetInnerHtml($(`.p${t + 1} .pronoun`), ""); - SetInnerHtml($(`.p${t + 1} .seed`), team.seed ? `
Seed ${team.seed}
` : ""); + SetInnerHtml($(`.p${t + 1} .seed`), _.get(team, "player.1.seed") ? `
Seed ${_.get(team, "player.1.seed")}
` : ""); let characterNames = []; diff --git a/src/TSHAssetDownloader.py b/src/TSHAssetDownloader.py index 4af23498a..8b650f2f4 100644 --- a/src/TSHAssetDownloader.py +++ b/src/TSHAssetDownloader.py @@ -92,7 +92,8 @@ def DownloadAssets(self): self.select.setEditable(True) self.select.completer().setFilterMode(Qt.MatchFlag.MatchContains) self.select.completer().setCompletionMode(QCompleter.PopupCompletion) - self.font_small = QFont("./assets/font/RobotoCondensed.ttf", pointSize=8) + self.font_small = QFont( + "./assets/font/RobotoCondensed.ttf", pointSize=8) self.select.setFont(self.font_small) self.select.setModel(QStandardItemModel()) self.preDownloadDialogue.layout().addWidget(self.select) @@ -331,7 +332,7 @@ def DownloadAssetsFetch(self): messagebox.exec() return assets - def DownloadGameIcon(self, game_code, index, progress_callback): + def DownloadGameIcon(self, game_code, index, progress_callback, cancel_event): try: response = urllib.request.urlopen( f"https://raw.githubusercontent.com/joaorb64/StreamHelperAssets/main/games/{game_code}/base_files/logo.png") @@ -355,7 +356,7 @@ def DownloadGameIconComplete(self, result): logger.error(traceback.format_exc()) return (None) - def DownloadAssetsWorker(self, files, progress_callback): + def DownloadAssetsWorker(self, files, progress_callback, cancel_event): totalSize = sum(sum(f["size"] for f in fileList) for fileList in files) downloaded = 0 @@ -450,7 +451,8 @@ def f(assets): for fileToDownload in filesToDownload: fileToDownload[ "path"] = f'https://github.com/joaorb64/StreamHelperAssets/releases/latest/download/{fileToDownload["name"]}' - fileToDownload["extractpath"] = f'./user_data/games/{game}' + fileToDownload["extractpath"] = f'./user_data/games/{ + game}' allFilesToDownload.append(filesToDownload) TSHAssetDownloader.instance.downloadDialogue = QProgressDialog( diff --git a/src/TSHGameAssetManager.py b/src/TSHGameAssetManager.py index ce405ac51..aceea6747 100644 --- a/src/TSHGameAssetManager.py +++ b/src/TSHGameAssetManager.py @@ -512,7 +512,7 @@ def UpdateStageModel(self): except: logger.error(traceback.format_exc()) - def LoadStageImage(self, stage, item, progress_callback): + def LoadStageImage(self, stage, item, progress_callback, cancel_event): try: if stage.get("path") and os.path.exists(stage.get("path")): img = Image.open(stage.get("path")) @@ -664,7 +664,7 @@ def UpdateSkinModel(self): for w in self.workers: self.threadpool.start(w) - def LoadSkinImages(self, allAssetData, allItem, skinModel, progress_callback): + def LoadSkinImages(self, allAssetData, allItem, skinModel, progress_callback, cancel_event): try: icons = [] @@ -939,8 +939,10 @@ def GetCharacterAssets(self, characterCodename: str, skin: int, assetpack: str = else: metadata_title_locale = asset.get("metadata", {})[ key].get("title", '') - charFiles[assetKey]['metadata'][f"{key}"]["title"] = metadata_title_locale - charFiles[assetKey]['metadata'][f"{key}"][f"value_en"] = metadata[key] + charFiles[assetKey]['metadata'][f"{ + key}"]["title"] = metadata_title_locale + charFiles[assetKey]['metadata'][f"{ + key}"][f"value_en"] = metadata[key] if TSHLocaleHelper.exportLocale in asset.get("metadata", {})[key]["values"].get(characterCodename, {}).get("locale", {}).keys() or TSHLocaleHelper.exportLocale.split('-')[0] in asset.get("metadata", {})[key]["values"].get(characterCodename, {}).get("locale", {}).keys(): try: metadata[key] = asset.get("metadata", {})[key]["values"].get( @@ -948,7 +950,8 @@ def GetCharacterAssets(self, characterCodename: str, skin: int, assetpack: str = except KeyError: metadata[key] = asset.get("metadata", {})[key]["values"].get( characterCodename, {}).get("locale", {})[TSHLocaleHelper.exportLocale.split('-')[0]] - charFiles[assetKey]['metadata'][f"{key}"][f"value"] = metadata[key] + charFiles[assetKey]['metadata'][f"{ + key}"][f"value"] = metadata[key] # if len(metadata.keys()) > 0: # if str(skin) in metadata: diff --git a/src/TSHSelectSetWindow.py b/src/TSHSelectSetWindow.py index 567d0d98d..5beaa8dfa 100644 --- a/src/TSHSelectSetWindow.py +++ b/src/TSHSelectSetWindow.py @@ -66,6 +66,9 @@ def filterList(text): lambda x: self.LoadSelectedSet() ) + self.labelStatus = QLabel() + layout.addWidget(self.labelStatus) + self.resize(1200, 500) qr = self.frameGeometry() @@ -73,8 +76,10 @@ def filterList(text): qr.moveCenter(cp) self.move(qr.topLeft()) - TSHTournamentDataProvider.instance.signals.get_sets_finished.connect( - self.SetSets) + TSHTournamentDataProvider.instance.signals.sets_data_updated.connect( + self.SetsUpdated) + + self.model = QStandardItemModel() def eventFilter(self, obj, event): if obj is self.startggSetSelectionItemList and event.type() == QEvent.KeyPress: @@ -86,10 +91,20 @@ def LoadSets(self): self.proxyModel.setSourceModel(QStandardItemModel()) TSHTournamentDataProvider.instance.LoadSets( showFinished=self.showFinished.isChecked()) + self.model.clear() + self.labelStatus.setText( + QApplication.translate("app", "Fetching sets...")) + + def SetsUpdated(self, data): + if data == None: + return + + if data["progress"] >= data["totalPages"]: + self.labelStatus.setText("") + else: + self.labelStatus.setText( + f"Fetching sets...({data['progress']}/{data['totalPages']})") - def SetSets(self, sets): - logger.info("Got sets" + str(len(sets))) - model = QStandardItemModel() horizontal_labels = ["Stream", "Station", "Wave", "Title", "Player 1", "Player 2"] horizontal_labels[0] = QApplication.translate("app", "Stream") @@ -100,7 +115,9 @@ def SetSets(self, sets): "app", "Player {0}").format(1) horizontal_labels[5] = QApplication.translate( "app", "Player {0}").format(2) - model.setHorizontalHeaderLabels(horizontal_labels) + self.model.setHorizontalHeaderLabels(horizontal_labels) + + sets = data["sets"] if sets is not None: for s in sets: @@ -124,7 +141,7 @@ def SetSets(self, sets): except Exception as e: logger.error(traceback.format_exc()) - model.appendRow([ + self.model.appendRow([ QStandardItem(s.get("stream", "")), QStandardItem(str(s.get("station", "")) if s.get( "station", "") != None else ""), @@ -135,7 +152,7 @@ def SetSets(self, sets): dataItem ]) - self.proxyModel.setSourceModel(model) + self.proxyModel.setSourceModel(self.model) self.startggSetSelectionItemList.setColumnHidden(6, True) self.startggSetSelectionItemList.resizeColumnsToContents() self.startggSetSelectionItemList.horizontalHeader( @@ -156,3 +173,10 @@ def LoadSelectedSet(self): if setId: setId["auto_update"] = "set" self.parent().signals.NewSetSelected.emit(setId) + + if TSHTournamentDataProvider.instance.setLoadingWorker: + # If there was a previous set loading worker, + # block its signals + TSHTournamentDataProvider.instance.setLoadingWorker.cancel() + TSHTournamentDataProvider.instance.setLoadingWorker.signals.blockSignals( + True) diff --git a/src/TSHThumbnailSettingsWidget.py b/src/TSHThumbnailSettingsWidget.py index 6703ac3e9..cf4c4a185 100644 --- a/src/TSHThumbnailSettingsWidget.py +++ b/src/TSHThumbnailSettingsWidget.py @@ -174,7 +174,7 @@ def __init__(self, *args): config["filename"] = t self.templates.append(config) except Exception as e: - logger.error(traceback.format_exc()) + logger.error(traceback.format_exc()) for t in self.templates: self.templateSelect.addItem( @@ -278,7 +278,8 @@ def __init__(self, *args): self.zoom.valueChanged.connect(lambda: TSHThumbnailSettingsWidget.SaveSettings( self, - key=f"game.{TSHGameAssetManager.instance.selectedGame.get('codename')}.zoom", + key=f"game.{TSHGameAssetManager.instance.selectedGame.get( + 'codename')}.zoom", val=self.zoom.value(), generatePreview=True ) @@ -287,7 +288,8 @@ def __init__(self, *args): self.horizontalAlign.valueChanged.connect(lambda val: [ TSHThumbnailSettingsWidget.SaveSettings( self, - key=f"game.{TSHGameAssetManager.instance.selectedGame.get('codename')}.align.horizontal", + key=f"game.{TSHGameAssetManager.instance.selectedGame.get( + 'codename')}.align.horizontal", val=val, generatePreview=True )] @@ -296,7 +298,8 @@ def __init__(self, *args): self.verticalAlign.valueChanged.connect(lambda val: [ TSHThumbnailSettingsWidget.SaveSettings( self, - key=f"game.{TSHGameAssetManager.instance.selectedGame.get('codename')}.align.vertical", + key=f"game.{TSHGameAssetManager.instance.selectedGame.get( + 'codename')}.align.vertical", val=val, generatePreview=True )] @@ -305,7 +308,8 @@ def __init__(self, *args): self.scaleToFillX.stateChanged.connect(lambda val: [ TSHThumbnailSettingsWidget.SaveSettings( self, - key=f"game.{TSHGameAssetManager.instance.selectedGame.get('codename')}.scaleFillX", + key=f"game.{TSHGameAssetManager.instance.selectedGame.get( + 'codename')}.scaleFillX", val=self.scaleToFillX.isChecked(), generatePreview=True )] @@ -314,7 +318,8 @@ def __init__(self, *args): self.scaleToFillY.stateChanged.connect(lambda val: [ TSHThumbnailSettingsWidget.SaveSettings( self, - key=f"game.{TSHGameAssetManager.instance.selectedGame.get('codename')}.scaleFillY", + key=f"game.{TSHGameAssetManager.instance.selectedGame.get( + 'codename')}.scaleFillY", val=self.scaleToFillY.isChecked(), generatePreview=True )] @@ -323,7 +328,8 @@ def __init__(self, *args): self.proportionalScaling.stateChanged.connect(lambda val: [ TSHThumbnailSettingsWidget.SaveSettings( self, - key=f"game.{TSHGameAssetManager.instance.selectedGame.get('codename')}.proportionalScaling", + key=f"game.{TSHGameAssetManager.instance.selectedGame.get( + 'codename')}.proportionalScaling", val=self.proportionalScaling.isChecked(), generatePreview=True )] @@ -332,7 +338,8 @@ def __init__(self, *args): self.hideSeparators.stateChanged.connect(lambda val: [ TSHThumbnailSettingsWidget.SaveSettings( self, - key=f"game.{TSHGameAssetManager.instance.selectedGame.get('codename')}.hideSeparators", + key=f"game.{TSHGameAssetManager.instance.selectedGame.get( + 'codename')}.hideSeparators", val=self.hideSeparators.isChecked(), generatePreview=True )] @@ -341,7 +348,8 @@ def __init__(self, *args): self.noSeparatorAngle.valueChanged.connect(lambda: TSHThumbnailSettingsWidget.SaveSettings( self, - key=f"game.{TSHGameAssetManager.instance.selectedGame.get('codename')}.noSeparatorAngle", + key=f"game.{TSHGameAssetManager.instance.selectedGame.get( + 'codename')}.noSeparatorAngle", val=self.noSeparatorAngle.value(), generatePreview=True ) @@ -350,7 +358,8 @@ def __init__(self, *args): self.noSeparatorDistance.valueChanged.connect(lambda: TSHThumbnailSettingsWidget.SaveSettings( self, - key=f"game.{TSHGameAssetManager.instance.selectedGame.get('codename')}.noSeparatorDistance", + key=f"game.{TSHGameAssetManager.instance.selectedGame.get( + 'codename')}.noSeparatorDistance", val=self.noSeparatorDistance.value(), generatePreview=True ) @@ -359,7 +368,8 @@ def __init__(self, *args): self.flipSeparators.stateChanged.connect(lambda val: [ TSHThumbnailSettingsWidget.SaveSettings( self, - key=f"game.{TSHGameAssetManager.instance.selectedGame.get('codename')}.flipSeparators", + key=f"game.{TSHGameAssetManager.instance.selectedGame.get( + 'codename')}.flipSeparators", val=self.flipSeparators.isChecked(), generatePreview=True )] @@ -507,7 +517,8 @@ def __init__(self, *args): self.selectRenderType.currentIndexChanged.connect(lambda: [ SettingsManager.Set( - f"thumbnail_config.game.{TSHGameAssetManager.instance.selectedGame.get('codename')}.asset_pack", + f"thumbnail_config.game.{ + TSHGameAssetManager.instance.selectedGame.get('codename')}.asset_pack", self.selectRenderType.currentData() ), self.updateFromSettings(), @@ -812,7 +823,7 @@ def SaveFont(self, key, font, type, fontPath): self.GeneratePreview() except Exception as e: logger.error("Error saving font") - logger.error(traceback.format_exc()) + logger.error(traceback.format_exc()) def ColorPicker(self, button, key): try: @@ -823,13 +834,13 @@ def ColorPicker(self, button, key): self.SaveSettings(f"{key}", val=color) self.updateFromSettings() except Exception as e: - logger.error(traceback.format_exc()) + logger.error(traceback.format_exc()) def SaveSettings(self, key, val, generatePreview=False): try: SettingsManager.Set(f"thumbnail_config.{key}", val) except Exception as e: - logger.error(traceback.format_exc()) + logger.error(traceback.format_exc()) if generatePreview: self.GeneratePreview() @@ -908,7 +919,7 @@ def GeneratePreview(self, manual=False): except Exception as e: self.DisplayErrorMessage(traceback.format_exc()) - def GeneratePreviewDo(self, progress_callback): + def GeneratePreviewDo(self, progress_callback, cancel_event): with self.lock: try: if self.thumbnailGenerationThread.activeThreadCount() > 1: @@ -922,7 +933,7 @@ def GeneratePreviewDo(self, progress_callback): pass def DisplayErrorMessage(self, e): - logger.error(traceback.format_exc()) + logger.error(traceback.format_exc()) msgBox = QMessageBox() msgBox.setWindowIcon(QIcon('assets/icons/icon.png')) msgBox.setWindowTitle(QApplication.translate( diff --git a/src/TSHTournamentDataProvider.py b/src/TSHTournamentDataProvider.py index 8f8f9ec58..612187911 100644 --- a/src/TSHTournamentDataProvider.py +++ b/src/TSHTournamentDataProvider.py @@ -17,6 +17,7 @@ from .Workers import Worker + class TSHTournamentDataProviderSignals(QObject): tournament_changed = Signal() entrants_updated = Signal() @@ -29,6 +30,7 @@ class TSHTournamentDataProviderSignals(QObject): tournament_phasegroup_updated = Signal(dict) game_changed = Signal(int) stream_queue_loaded = Signal(dict) + sets_data_updated = Signal(dict) class TSHTournamentDataProvider: @@ -45,6 +47,8 @@ def __init__(self) -> None: TSHGameAssetManager.instance.signals.onLoadAssets.connect( self.SetGameFromProvider) + self.setLoadingWorker = None + def GameChanged(self, videogame): StateManager.Set(f"provider_videogame", { "id": videogame @@ -88,7 +92,7 @@ def SetTournament(self, url, initialLoading=False): TSHTournamentDataProvider.instance.provider.GetEntrants() TSHTournamentDataProvider.instance.signals.tournament_changed.emit() - #TSHTournamentDataProvider.instance.SetGameFromProvider() + # TSHTournamentDataProvider.instance.SetGameFromProvider() else: TSHTournamentDataProvider.instance.signals.tournament_data_updated.emit({ }) @@ -213,12 +217,24 @@ def GetTournamentPhaseGroup(self, id): self.threadPool.start(worker) def LoadSets(self, showFinished): + if self.setLoadingWorker: + # If there was a previous set loading worker, + # block its signals + self.setLoadingWorker.cancel() + self.setLoadingWorker.signals.blockSignals(True) + worker = Worker(self.provider.GetMatches, ** {"getFinished": showFinished}) worker.signals.result.connect(lambda data: [ logger.info(data), self.signals.get_sets_finished.emit(data) ]) + worker.signals.progress.connect(lambda data: [ + logger.info(f"SetDataUpdated: {data}"), + self.signals.sets_data_updated.emit(data) + ]) + self.setLoadingWorker = worker + self.threadPool.start(worker) def LoadStations(self): @@ -250,14 +266,14 @@ def LoadStationSets(self, mainWindow): if queueCache and not queueCache.CheckQueue(stationSets): queueCache.UpdateQueue(stationSets) - TSHTournamentDataProvider.instance.GetStationMatches(stationSets, mainWindow) + TSHTournamentDataProvider.instance.GetStationMatches( + stationSets, mainWindow) if not stationSet: stationSet = {} stationSet["auto_update"] = mainWindow.lastStationSelected.get( "type") - mainWindow.signals.NewSetSelected.emit(stationSet) @@ -270,7 +286,7 @@ def LoadUserSet(self, mainWindow, user): _set["auto_update"] = "user" mainWindow.signals.NewSetSelected.emit(_set) - #omits the first one (loaded through NewSetSelected) + # omits the first one (loaded through NewSetSelected) def GetStationMatches(self, matchesId, mainWindow): matchesId = matchesId[1:] diff --git a/src/TournamentDataProvider/ChallongeDataProvider.py b/src/TournamentDataProvider/ChallongeDataProvider.py index e1c776831..c5d62cc59 100644 --- a/src/TournamentDataProvider/ChallongeDataProvider.py +++ b/src/TournamentDataProvider/ChallongeDataProvider.py @@ -96,14 +96,15 @@ def GetEnglishUrl(self): prefix = prefix+"." return f"https://{prefix}challonge.com/{self.GetSlug()}" - def GetTournamentData(self, progress_callback=None): + def GetTournamentData(self, progress_callback=None, cancel_event=None): finalData = {} try: slug = self.GetSlug() data = self.scraper.get( - f"https://challonge.com/en/search/tournaments.json?filters%5B&page=1&per=1&q={slug}", + f"https://challonge.com/en/search/tournaments.json?filters%5B&page=1&per=1&q={ + slug}", ) logger.debug(data.text) @@ -127,7 +128,8 @@ def GetTournamentData(self, progress_callback=None): finalData["startAt"] = dateutil.parser.parse( dateElement.get("text"), fuzzy=True).timestamp() except Exception as e: - logger.error(f"Could not get tournament date: {traceback.format_exc()}") + logger.error(f"Could not get tournament date: { + traceback.format_exc()}") participantsElement = next( (d for d in details if d.get("icon") == "fa fa-users"), None) @@ -149,7 +151,8 @@ def GetIconURL(self): slug = self.GetSlug() data = self.scraper.get( - f"https://challonge.com/en/search/tournaments.json?filters%5B&page=1&per=1&q={slug}", + f"https://challonge.com/en/search/tournaments.json?filters%5B&page=1&per=1&q={ + slug}", headers=HEADERS ) @@ -165,7 +168,7 @@ def GetIconURL(self): return url - def GetMatch(self, setId, progress_callback): + def GetMatch(self, setId, progress_callback, cancel_event): finalData = {} try: @@ -187,7 +190,7 @@ def GetMatch(self, setId, progress_callback): return finalData - def GetStations(self, progress_callback=None): + def GetStations(self, progress_callback=None, cancel_event=None): try: logger.info("Get stations") @@ -242,7 +245,7 @@ def GetStationMatchId(self, stationId): logger.error(traceback.format_exc()) return stationSet - def GetMatches(self, getFinished=False, progress_callback=None): + def GetMatches(self, getFinished=False, progress_callback=None, cancel_event=None): final_data = [] try: @@ -269,12 +272,19 @@ def GetMatches(self, getFinished=False, progress_callback=None): final_data.append(self.ParseMatchData(match)) final_data.reverse() + + if progress_callback: + progress_callback.emit({ + "progress": 1, + "totalPages": 1, + "sets": final_data + }) except Exception as e: logger.error(traceback.format_exc()) return final_data - def GetTournamentPhases(self, progress_callback=None): + def GetTournamentPhases(self, progress_callback=None, cancel_event=None): phases = [] try: @@ -316,7 +326,7 @@ def GetTournamentPhases(self, progress_callback=None): return phases - def GetTournamentPhaseGroup(self, id, progress_callback=None): + def GetTournamentPhaseGroup(self, id, progress_callback=None, cancel_event=None): finalData = {} try: data = self.scraper.get( @@ -733,7 +743,7 @@ def GetEntrants(self): worker = Worker(self.GetEntrantsWorker) self.threadpool.start(worker) - def GetEntrantsWorker(self, progress_callback): + def GetEntrantsWorker(self, progress_callback, cancel_event): try: data = self.scraper.get( self.GetEnglishUrl()+".json", @@ -797,7 +807,7 @@ def GetAllEntrantsFromData(self, data, phaseId=None): return (final_data) - def GetStandings(self, playerNumber, progress_callback): + def GetStandings(self, playerNumber, progress_callback, cancel_event): final_data = [] try: @@ -843,7 +853,7 @@ def GetStandings(self, playerNumber, progress_callback): except Exception as e: logger.error(traceback.format_exc()) - def GetLastSets(self, playerID, playerNumber, callback, progress_callback): + def GetLastSets(self, playerID, playerNumber, callback, progress_callback, cancel_event): try: data = self.scraper.get( self.GetEnglishUrl()+".json", diff --git a/src/TournamentDataProvider/StartGGDataProvider.py b/src/TournamentDataProvider/StartGGDataProvider.py index 030118f6d..14e49c5f6 100644 --- a/src/TournamentDataProvider/StartGGDataProvider.py +++ b/src/TournamentDataProvider/StartGGDataProvider.py @@ -69,7 +69,7 @@ def QueryRequests(self, url=None, type=None, headers={}, jsonParams=None, params requestCode = data.status_code return orjson.loads(data.text) - def GetTournamentData(self, progress_callback=None): + def GetTournamentData(self, progress_callback=None, cancel_event=None): finalData = {} try: @@ -143,7 +143,7 @@ def GetIconURL(self): return url - def GetTournamentPhases(self, progress_callback=None): + def GetTournamentPhases(self, progress_callback=None, cancel_event=None): phases = [] try: @@ -181,7 +181,7 @@ def GetTournamentPhases(self, progress_callback=None): return phases - def GetTournamentPhaseGroup(self, id, progress_callback=None): + def GetTournamentPhaseGroup(self, id, progress_callback=None, cancel_event=None): finalData = {} try: @@ -319,7 +319,7 @@ def GetTournamentPhaseGroup(self, id, progress_callback=None): return finalData - def GetMatch(self, setId, progress_callback): + def GetMatch(self, setId, progress_callback, cancel_event): finalResult = None try: @@ -329,6 +329,7 @@ def GetMatch(self, setId, progress_callback): fetchOld = Worker(self._GetMatchTasks, **{ "progress_callback": None, + "cancel_event": None, "setId": setId }) fetchOld.signals.result.connect( @@ -337,6 +338,7 @@ def GetMatch(self, setId, progress_callback): fetchNew = Worker(self._GetMatchNewApi, **{ "progress_callback": None, + "cancel_event": None, "setId": setId }) fetchNew.signals.result.connect( @@ -373,12 +375,13 @@ def GetMatch(self, setId, progress_callback): logger.error(traceback.format_exc()) return finalResult - def _GetMatchTasks(self, setId, progress_callback): + def _GetMatchTasks(self, setId, progress_callback, cancel_event): if "preview" in str(setId): return self.ParseMatchDataOldApi({}) data = self.QueryRequests( - f'https://www.start.gg/api/-/gg_api./set/{setId};bustCache=true;expand=["setTask"];fetchMostRecentCached=true', + f'https://www.start.gg/api/-/gg_api./set/{ + setId};bustCache=true;expand=["setTask"];fetchMostRecentCached=true', type=requests.get, params={ "extensions": {"cacheControl": {"version": 1, "noCache": True}}, @@ -389,7 +392,7 @@ def _GetMatchTasks(self, setId, progress_callback): ) return self.ParseMatchDataOldApi(data) - def _GetMatchNewApi(self, setId, progress_callback): + def _GetMatchNewApi(self, setId, progress_callback, cancel_event): data = self.QueryRequests( "https://www.start.gg/api/-/gql", type=requests.post, @@ -401,9 +404,10 @@ def _GetMatchNewApi(self, setId, progress_callback): "query": StartGGDataProvider.SetQuery } ) + logger.debug(data.get("data", {}).get("set", {})) return self.ParseMatchDataNewApi(data.get("data", {}).get("set", {})) - def GetMatches(self, getFinished=False, progress_callback=None): + def GetMatches(self, getFinished=False, progress_callback=None, cancel_event=None): try: logger.info("Get matches", getFinished) states = [1, 6, 2] @@ -418,7 +422,7 @@ def GetMatches(self, getFinished=False, progress_callback=None): logger.info("Fetching sets") - while page <= totalPages: + while page <= totalPages and not cancel_event.is_set(): data = self.QueryRequests( "https://www.start.gg/api/-/gql", type=requests.post, @@ -431,7 +435,7 @@ def GetMatches(self, getFinished=False, progress_callback=None): }, "eventSlug": self.url.split("start.gg/")[1], "page": page, - "perPage": 512 + "perPage": 64 }, "query": StartGGDataProvider.SetsQuery } @@ -441,21 +445,29 @@ def GetMatches(self, getFinished=False, progress_callback=None): data, "data.event.sets.pageInfo.totalPages", 0) sets = deep_get(data, "data.event.sets.nodes", []) + newSets = [] for _set in sets: - final_data.append(self.ParseMatchDataNewApi(_set)) + parsed = self.ParseMatchDataNewApi(_set) + final_data.append(parsed) + newSets.append(parsed) + + if progress_callback: + progress_callback.emit({ + "progress": page, + "totalPages": totalPages, + "sets": newSets + }) page += 1 - logger.info(f"Fetching sets... {page}/{totalPages}") - return (final_data) except Exception as e: logger.error(traceback.format_exc()) return (final_data) return ([]) - def GetStations(self, progress_callback=None): + def GetStations(self, progress_callback=None, cancel_event=None): try: logger.info("Get stations") @@ -563,6 +575,8 @@ def ParseMatchDataNewApi(self, _set): "bracket_type": bracket_type, "p1_name": p1.get("entrant", {}).get("name", "") if p1 and p1.get("entrant", {}) != None else "", "p2_name": p2.get("entrant", {}).get("name", "") if p2 and p2.get("entrant", {}) != None else "", + "p1_seed": p1.get("entrant", {}).get("initialSeedNum", None), + "p2_seed": p2.get("entrant", {}).get("initialSeedNum", None), "stream": _set.get("stream", {}).get("streamName", "") if _set.get("stream", {}) != None else "", "station": _set.get("station", {}).get("number", "") if _set.get("station", {}) != None else "", "isOnline": deep_get(_set, "event.isOnline"), @@ -677,10 +691,15 @@ def ParseMatchDataNewApi(self, _set): playerData["seed"] = self.player_seeds.get( playerData["id"][0]) + if setData.get(f"p{i+1}_seed"): + playerData["seed"] = setData.get(f"p{i+1}_seed") + players[i].append(playerData) setData["entrants"] = players + print("setData", setData) + return setData def TopNPlacement(self, placement): @@ -991,7 +1010,7 @@ def ProcessFutureSet(self, _set, eventSlug): teamData = { "teamName": entrant.get("name", ""), "losers": losers, - "seed": entrant.get("seeds", [])[0].get("seedNum", 889977666), + "seed": entrant.get("initialSeed", 889977666), "player": {} } @@ -1014,7 +1033,8 @@ def ProcessFutureSet(self, _set, eventSlug): if stateCode: stateData = states[stateCode] - path = f'./assets/state_flag/{countryCode}/{"_CON" if stateCode == "CON" else stateCode}.png' + path = f'./assets/state_flag/{countryCode}/{ + "_CON" if stateCode == "CON" else stateCode}.png' if not os.path.exists(path): path = None @@ -1040,7 +1060,7 @@ def ProcessFutureSet(self, _set, eventSlug): setData["team"][str(teamIndex + 1)] = teamData return setData - def GetStreamQueue(self, progress_callback=None): + def GetStreamQueue(self, progress_callback=None, cancel_event=None): try: data = self.QueryRequests( "https://www.start.gg/api/-/gql", @@ -1248,7 +1268,7 @@ def GetEntrants(self): }) self.threadpool.start(worker) - def GetLastSets(self, playerID, playerNumber, callback, progress_callback): + def GetLastSets(self, playerID, playerNumber, callback, progress_callback, cancel_event): try: data = self.QueryRequests( "https://www.start.gg/api/-/gql", @@ -1286,11 +1306,13 @@ def GetLastSets(self, playerID, playerNumber, callback, progress_callback): player1Info = set.get("slots", [{}])[0].get("entrant", {}).get( "participants", [{}])[0].get("player", {}) - player1Seed = set.get("slots", [{}])[0].get("seed", {}) + player1Seed = set.get( + "slots", [{}])[0].get("initialSeedNum", 0) player2Info = set.get("slots", [{}])[1].get("entrant", {}).get( "participants", [{}])[0].get("player", {}) - player2Seed = set.get("slots", [{}])[1].get("seed", {}) + player2Seed = set.get( + "slots", [{}])[1].get("initialSeedNum", 0) players = ["1", "2"] @@ -1319,7 +1341,7 @@ def GetLastSets(self, playerID, playerNumber, callback, progress_callback): logger.error(traceback.format_exc()) callback.emit({"playerNumber": playerNumber, "last_sets": []}) - def GetPlayerHistoryStandings(self, playerID, playerNumber, gameType, callback, progress_callback): + def GetPlayerHistoryStandings(self, playerID, playerNumber, gameType, callback, progress_callback, cancel_event): try: data = self.QueryRequests( "https://www.start.gg/api/-/gql", @@ -1370,7 +1392,7 @@ def GetPlayerHistoryStandings(self, playerID, playerNumber, gameType, callback, except Exception as e: callback.emit({"playerNumber": playerNumber, "history_sets": []}) - def GetRecentSets(self, id1, id2, videogame, callback, requestTime, progress_callback): + def GetRecentSets(self, id1, id2, videogame, callback, requestTime, progress_callback, cancel_event): try: id1 = [str(id1[0]), str(id1[1])] id2 = [str(id2[0]), str(id2[1])] @@ -1408,7 +1430,7 @@ def GetRecentSets(self, id1, id2, videogame, callback, requestTime, progress_cal logger.error(traceback.format_exc()) callback.emit({"sets": [], "request_time": requestTime}) - def GetRecentSetsWorker(self, id1, id2, page, videogame, inverted, progress_callback): + def GetRecentSetsWorker(self, id1, id2, page, videogame, inverted, progress_callback, cancel_event): try: recentSets = [] @@ -1516,7 +1538,7 @@ def GetRecentSetsWorker(self, id1, id2, page, videogame, inverted, progress_call logger.error(traceback.format_exc()) return [] - def GetEntrantsWorker(self, eventSlug, gameId, progress_callback): + def GetEntrantsWorker(self, eventSlug, gameId, progress_callback, cancel_event): try: page = 1 totalPages = 1 @@ -1550,7 +1572,8 @@ def GetEntrantsWorker(self, eventSlug, gameId, progress_callback): playerData = StartGGDataProvider.ProcessEntrantData( entrant) playerData["seed"] = team.get("initialSeedNum", 0) - self.player_seeds[playerData["id"][0]] = playerData["seed"] + self.player_seeds[playerData["id"] + [0]] = playerData["seed"] players.append(playerData) TSHPlayerDB.AddPlayers(players) @@ -1673,7 +1696,7 @@ def ProcessEntrantData(entrant, setData=[]): return (playerData) - def GetStandings(self, playerNumber, progress_callback): + def GetStandings(self, playerNumber, progress_callback, cancel_event): try: data = self.QueryRequests( "https://www.start.gg/api/-/gql", @@ -1712,7 +1735,7 @@ def GetStandings(self, playerNumber, progress_callback): except Exception as e: logger.error(traceback.format_exc()) - def GetFutureMatch(self, matchId, progress_callback): + def GetFutureMatch(self, matchId, progress_callback, cancel_event): data = self.QueryRequests( "https://www.start.gg/api/-/gql", type=requests.post, @@ -1734,13 +1757,13 @@ def GetFutureMatch(self, matchId, progress_callback): return data - def GetMatchAndInsertInListBecauseFuckPython(self, setId, list, i, progress_callback): + def GetMatchAndInsertInListBecauseFuckPython(self, setId, list, i, progress_callback, cancel_event): set = self.GetFutureMatch(setId, None) if set: list[i] = set - def GetFutureMatchesList(self, setsId, progress_callback): + def GetFutureMatchesList(self, setsId, progress_callback, cancel_event): sets = [] pool = self.getStationMatchesThreadPool i = 0 diff --git a/src/TournamentDataProvider/StartGGSetQuery.txt b/src/TournamentDataProvider/StartGGSetQuery.txt index dc1330ad4..ebf796937 100644 --- a/src/TournamentDataProvider/StartGGSetQuery.txt +++ b/src/TournamentDataProvider/StartGGSetQuery.txt @@ -15,6 +15,7 @@ query SetQuery($id: ID!) { entrant { id name + initialSeedNum participants { id user { diff --git a/src/TournamentDataProvider/TournamentDataProvider.py b/src/TournamentDataProvider/TournamentDataProvider.py index 4c751e84a..6e0c8fd8d 100644 --- a/src/TournamentDataProvider/TournamentDataProvider.py +++ b/src/TournamentDataProvider/TournamentDataProvider.py @@ -14,19 +14,19 @@ def GetIconURL(self): def GetEntrants(self): pass - def GetTournamentData(self, progress_callback=None): + def GetTournamentData(self, progress_callback=None, cancel_event=None): pass - def GetMatch(self, setId, progress_callback=None): + def GetMatch(self, setId, progress_callback=None, cancel_event=None): pass - def GetMatches(self, getFinished=False, progress_callback=None): + def GetMatches(self, getFinished=False, progress_callback=None, cancel_event=None): pass - def GetStations(self, progress_callback=None): + def GetStations(self, progress_callback=None, cancel_event=None): pass - def GetStreamQueue(self, streamName, progress_callback=None): + def GetStreamQueue(self, streamName, progress_callback=None, cancel_event=None): pass def GetStreamMatchId(self, streamName): @@ -50,10 +50,10 @@ def GetLastSets(self, playerId, playerNumber): def GetPlayerHistoryStandings(self, playerId, playerNumber, gameType): pass - def GetTournamentPhases(self, progress_callback=None): + def GetTournamentPhases(self, progress_callback=None, cancel_event=None): pass - def GetTournamentPhaseGroup(self, id, progress_callback=None): + def GetTournamentPhaseGroup(self, id, progress_callback=None, cancel_event=None): pass def GetStandings(self, playerNumber): @@ -62,6 +62,6 @@ def GetStandings(self, playerNumber): def GetFutureMatch(self, progrss_callback=None): pass - #give me a list of objects that contain a "id" property - def GetFutureMatchesList(self, sets: object, progress_callback=None): - pass \ No newline at end of file + # give me a list of objects that contain a "id" property + def GetFutureMatchesList(self, sets: object, progress_callback=None, cancel_event=None): + pass diff --git a/src/TournamentStreamHelper.py b/src/TournamentStreamHelper.py index 1cd849eda..72b624dc1 100755 --- a/src/TournamentStreamHelper.py +++ b/src/TournamentStreamHelper.py @@ -851,7 +851,7 @@ def Update(): Qt.WindowModality.WindowModal) self.downloadDialogue.show() - def worker(progress_callback): + def worker(progress_callback, cancel_event): with open("update.tar.gz", 'wb') as downloadFile: downloaded = 0 diff --git a/src/Workers.py b/src/Workers.py index 6ac640665..1b9cdc0ba 100755 --- a/src/Workers.py +++ b/src/Workers.py @@ -4,6 +4,8 @@ import traceback import sys from loguru import logger +import threading + class WorkerSignals(QObject): ''' @@ -13,10 +15,10 @@ class WorkerSignals(QObject): finished No data - + error `tuple` (exctype, value, traceback.format_exc() ) - + result `object` data returned from processing, anything @@ -54,14 +56,18 @@ def __init__(self, fn, *args, **kwargs): self.signals = WorkerSignals() # Add the callback to our kwargs - self.kwargs['progress_callback'] = self.signals.progress + self.kwargs['progress_callback'] = self.signals.progress + + # Cancellation event + self.cancel_event = threading.Event() + self.kwargs['cancel_event'] = self.cancel_event @Slot() def run(self): ''' Initialise the runner function with passed args, kwargs. ''' - + # Retrieve args/kwargs here; and fire processing using them try: result = self.fn(*self.args, **self.kwargs) @@ -70,7 +76,14 @@ def run(self): exctype, value = sys.exc_info()[:2] self.signals.error.emit((exctype, value, traceback.format_exc())) else: - self.signals.result.emit(result) # Return the result of the processing + # Return the result of the processing + self.signals.result.emit(result) finally: if self.signals.finished: - self.signals.finished.emit() # Done \ No newline at end of file + self.signals.finished.emit() # Done + + def cancel(self): + ''' + Set the cancel event to indicate that the task should be cancelled. + ''' + self.cancel_event.set()