Open brendenhoffman opened 1 year ago
Wrote a small Python script that gets the latest GitHub release tag for a project, it's possible that this could be used to get the latest available version on GitHub and compare it with what we have, and then ProtonUp-Qt could prompt and ask for an update
import requests
import re
import os
def get_latest_github_tag(project_url):
github_url = 'https://github.com'
releases_url = f'{project_url}/releases'.replace(github_url, '')
tags_url = f'{project_url}/tags'
tagspage_resp = requests.get(tags_url).text
release_tags = re.search(f'{releases_url}[^\"]+', tagspage_resp)
return os.path.basename(release_tags.group(0))
ge_tag = get_latest_github_tag('https://github.com/GloriousEggroll/proton-ge-custom')
stl_tag = get_latest_github_tag('https://github.com/sonic2kk/steamtinkerlaunch')
pupqt_tag = get_latest_github_tag('https://github.com/DavidoTek/ProtonUp-Qt')
boxtron_tag = get_latest_github_tag('https://github.com/dreamer/boxtron')
luxtorpeda_tag = get_latest_github_tag('https://github.com/luxtorpeda-dev/luxtorpeda')
kron4ek_tag = get_latest_github_tag('https://github.com/Kron4ek/Wine-Builds')
# Seems like TKG doesn't return anything useful...
#protontkg_tag = get_latest_github_tag('https://github.com/Frogging-Family/wine-tkg-git')
print(f'Latest GE-Proton release is: {ge_tag}')
print(f'Latest SteamTinkerLaunch release is: {stl_tag}')
print(f'Latest ProtonUp-Qt release is: {pupqt_tag}')
print(f'Latest Boxtron release is: {boxtron_tag}')
print(f'Latest Luxtorpeda release is: {luxtorpeda_tag}')
print(f'Latest Kron4ek Wine release is: {kron4ek_tag}')
Just in case anyone was thinking about this :-)
We could include it in the ctmod and run it somehow on every startup for each detected compatiblity tool. Then show a message box or indicate it with a :arrow_up: (arrow up) beside the compatibility tool.
Ctmods could have an update_tool
method and maybe an update_available
property? Then we can show the up arrow on the compat tool list and a user can double click / press an "Update" button (which would be greyed out / invisible if no update is available - not sure which is good UX) it can show a dialog with the update information.
The dialog would have some information like current version and the version being updated, the path, number of games using the tool, and a button to update, cancel and view the release notes (if applicable -- i.e. take a user to the latest GitHub release page for the latest GE-Proton). When they click "Update", it would call an an available update method on a ctmod, and then batch update tools that are using that compat tool to the latest version.
Ctmods would be responsible for implementing a check_update_available
method too, and we shouldn't show the update icon on the ctmod list unless a ctmod has this method. This could be responsible for setting the update_available
property.
Since the upgrade path would be different for each tool (DXVK vs GE-Proton vs SteamTinkerLaunch etc etc), having this as a method would be a good idea. And we shouldn't show an "update available" unless a tool has this method also.
Basically, if a tool hasn't implemented the proper methods first, we shouldn't even try checking if it has updates.
The consideration here is that, one of the benefits of a tool like ProtonUp-Qt is being able to manage several versions of things like GE-Proton. Showing an update available on all compat tools might be a bit... visually noisy? Imagine having 5 different releases of GE-Proton, but there is only one that you really want as your "main" version, and the others are for specific games. Seeing an :arrow_up: beside all of them could be visually noisy. That's just a minor "UX" consideration though :-)
Another consideration here is that, a user might have GE-Proton7-39, GE-Proton7-40 and GE-Proton7-41 at the moment. In this case, 7-39 and 7-40 would show updates being available. However this would overwrite the existing GE-Proton7-41. I am not sure what the best behaviour would be here, it's just something I realised today while thinking about how a feature like this would work. What is the best way to handle updating tools to a version that is already downloaded? There could be the route of showing updates for all compat tools, but then also the route of only showing an update for the latest available version of a tool.
None of this is meant to put this feature request down either, I actually think this would be a really neat feature. There are just various things to consider, and documenting / discussing them here could help turn this issue into a better reference for implementation.
Then we can show the up arrow on the compat tool list and a user can double click / press an "Update" button
Yes
Ctmods would be responsible for implementing a check_update_available method too
Each ctmod could have a method get_newest_version
responsible for fetching the newest version number for the tool.
Then we need some way to compare the newest version against the newest installed version.
First idea: Ideally local compatibility tools would also not be loaded by a global ls <dir>
-like function (currently get_installed_ctools
in util.py
), but rather each ctmod would have a method get_installed_versions
that would return a CompatTool
object. That would also simplify removing the tools. Each ctmod would then have a child class of CompatTool that implements functions like get_install_dir
, get_tool_launcher
and remove_tool
Showing an update available on all compat tools might be a bit... visually noisy?
Grouping the tools would be great. Not sure how good/bad that would look. Maybe like this (quite complex to realitze I guess and would assume we implemented a get_installed_version
for each compatibility tool):
> Boxtron multiple version of Boxtron, collapsed
ᐯ GE-Proton [🡹] multiple version of GE-Proton, expanded, update available
v16.9
v17.0-2
Luxtorpedia v40 [🡹] v41 single version of Luxtorpeda, update available
I meant to reply to this and forgot...
I'm not sure of the best way to "create" it with Qt, but I think this is a good idea - Basically a parent tool "category" with a dropdown for each version of the tool. Maybe by default, all the tools should be expanded?
I am sure Qt has an element for this, but getting the UI right on it so that it isn't too much of a "striking" change while keeping it friendly is something to consider.
Sounds like a use case for a tree view. It's a bit more complex than a list view as it uses the QAbstractItemModel, but that makes it also more flexible.
We would need to find a way to group the compatibility tools which is the hardest part I think. We could use a regular expression (or just 'string'.contains()) to check if an installed to belongs to a ctmod.
I would like to add a function like this to each ctmod (I sketched a concept to make ProtonUp-Qt more modular a while ago but hadn't time yet to implement it):
class CtInstallerDxvk(CtModSuperClass):
...
def get_installed_versions(self, launcher: Launcher) -> List[CompatTool]:
tools = []
install_dir = os.path.join( launcher.get_base_dir(), 'runtime/dxvk' )
for f in os.listdir(install_dir):
if not _some_regex_magic(f):
continue
version = _some_regex_magic(f)
tools.append( CompatTool(name='DXVK', launcher=launcher, version=version,
absolute_install_dir=install_dir, ctinstaller=CtInstallerDxvk) )
return tools
def get_newest_release(self) -> CompatTool:
newest_version = _fetch_newest_version_from_github()
return CompatTool(name='DXVK', version=newest_version, installed=False, ctinstaller=CtInstallerDxvk)
pupgui2.api.register_ctinstaller(CtInstallerDxvk, launchers=['lutris'], packages=['native', 'flatpak'])
Grouping is definitely going to be the tricky part, which I ran into when creating this (extremely rough) mockup:
This is really just a UI proof-of-concept, the grouping is massively hardcoded and also since it uses a TreeView, the signals aren't working (they would need to be set up differently, since a double-click on the top level item has different behaviour). So right now it isn't very useful other than "visualizing"
SteamTinkerLaunch and Luxtorpeda are also in subcategories -- Probably there would need to be something maybe in the ctmod where it's set whether this can be "grouped" or not because right now it may have to be hardcoded / set in a dictionary or something.
The code to populate the list view is like this (I have a branch too just for fun):
def update_ui(self):
# ...
self.populate_treeview_with_ctools(['GE-Proton', 'Proton-Tkg', 'SteamTinkerLaunch', 'Luxtorpeda'])
# ...
def populate_treeview_with_ctools(self, ctool_names):
ctool_toplevels = {}
for ct in self.compat_tool_index_map:
ctool_name = ct.get_displayname(unused_tr=self.tr('unused'))
ctool_widget = QTreeWidgetItem([ctool_name])
for cn in ctool_names:
if cn.lower() in ctool_name.lower():
if cn not in ctool_toplevels.keys():
ctool_toplevel_widget = QTreeWidgetItem([cn])
self.ui.listInstalledVersions.addTopLevelItem(ctool_toplevel_widget)
ctool_toplevels[cn] = ctool_toplevel_widget
ctool_toplevels[cn].addChild(ctool_widget)
Of course none of this is even close to any sort of even functional implementation, and at least in its current iteration it's not something I'd dream of putting a PR up against. This is just to show off what I came up with on a visual level -- Under the hood there could be many improvements made across the board to better facilitate this (even just in this example, having the compat tools for Steam/Lutris/etc as lists in constants.py
would be an improvement off the top of my head).
Maybe the ideas here could serve as a basis though. Making ProtonUp-Qt more modular would be pretty sweet and help a lot here (then various properties could just be taken from the ctool in a loop to generate the tree view). Implementing something like this should probably wait until there are other changes to better allow for integrating this sort of feature.
That looks great! Yes, we would need to change that quite a bit and I think the compat_tool_index_map` wouldn't be sufficient anymore.
I also started working on adding a get_installed_versions
method to the ctmods, but there is still a lot to do.
Is your feature request related to a problem? Please describe. No, aside from updating is manual
Describe the solution you'd like This might be asking for a lot, but I have given it some thought to maybe give you a simple solution for auto updates. So, the premise is, the program would work as-is for explicitly installed apps, but auto updated apps will come from a subscribed list. ProtonUp-Qt can simply run in the background and poll updates, or the user can open the app to check for updates on the subscribed list. When a new version for an app is available ProtonUp-Qt will install it basically as if the user did so, and runs the batch update immediately after. The previous version can optionally be kept if the new version breaks anything, the rest will be auto deleted during the update process. Old unused versions can simply be uninstalled, if it is in use the user would have had to change that themselves, so it should not be uninstalled. The use state and version number should be a pretty reliable way to check what should be uninstalled. The only thing that I could see throwing a wrench in things is if an app changes the versioning scheme, but depending on how you have ProtonUp-Qt check if something is a new version or not, that may be an issue even without an auto updater, or it could be a non issue if you have already planned for that. I think this is a pretty user friendly solution and might just amount to a pretty small script. If I were a more experienced programmer I would give it a shot.
Describe alternatives you've considered Apps could be updated in place of the old one, but I think that would be even harder to implement and it would not have the flexibility of the batch update system you already have in place, plus there is less worries if an update breaks something. I imagine you have already given thought to updates and all of the reasons you wouldn't want that.