DavidoTek / ProtonUp-Qt

Install and manage GE-Proton, Luxtorpeda & more for Steam and Wine-GE & more for Lutris with this graphical user interface.
https://davidotek.github.io/protonup-qt
GNU General Public License v3.0
1.22k stars 40 forks source link

Steam Apps: Don't fetch soundtracks as steamapps #183

Closed sonic2kk closed 1 year ago

sonic2kk commented 1 year ago

This PR fixes Steam game soundtracks being parsed by ProtonUp-Qt and showing up incorrectly in the Games List dialog. It does this by skipping apps that are not stored in /path/to/steamapps/common.

Before: image

After:

image

Background

Steam allows you to download soundtracks you own into a specified library folder. While games tend to go into /path/to/steamapps/common, music conveniently goes to /path/to/steamapps/music.

The properties dialog in Steam for the soundtrack is virtually identcial to any other Steam app, including the ability to select a compatibility tool! This does... nothing, apparently! If you want to use Wine with your game soundtracks you'll have to buy it from the store apparently :-)

Approach

Steam does not seem to have a good way locally of differentiating between the types of downloaded apps. Downloaded soundtracks are picked up as regular apps, and from browsing various VDF files from Steam I couldn't find a good way to tell my downloaded soundtracks apart straight from Steam. I also spent some time combing the steam Python library docs, and I found that they do have a Music (and MusicAlbum, both have the same value though) in their Enums - However I couldn't see this used anywhere by Steam locally. I think it might be used when checking the type of apps with a Web API call. It might be possible to check the App type locally, but I couldn't find a way.

I came up with a solution similar to something I implemented recently for SteamTinkerLaunch. Since soundtracks are stored by Steam as apps, they have their own AppIDs and so on. What I was able to do was the following in get_steam_app_list:

  1. Get the path to the App Manifest for the current AppID and library folder
  2. Use the VDF parser to parse the .acf file in the current library folder and get the installdir - This is the name of the folder where an app is "installed"
    • AppManifest files are files which store information about a game installed in that library folder, so if we have AppID 391570, the AppManifest will be called appmanifest_391570.acf
    • Despite the extension they are identical to plaintext VDF files like the libraryfolders.vdf file, and can be parsed by the VDF library
  3. Build out a steamapps/common path based on the current library folder and the install dir - e.g. if installdir is PowerWash Simulator, the built path would look like /home/gaben/Games/steamapps/common/PowerWash Simulator
  4. Check if this directory exists and if it does not, assume it's not

I chose skipping over parsing as unlike Anti-Cheat runtimes, which ProtonUp-Qt does atore and mark with an appropriate app_type, there shouldn't be a case where ProtonUp-Qt needs to manage soundtracks. You can technically set the compatibility tool for soundtracks, but it does nothing from what I can see. I personally think it's best to simply skip over apps not in common entirely, instead of perhaps checking if they're stored in steamapps/music and then setting an App Type. Let me know if there is a preferred approach her :-)


In my tests, the Games Used By Compatibility Tool count didn't change, so no apps should be skipped incorrectly, but further testing is welcome to ensure no regressions are introduced.

Thanks!

sonic2kk commented 1 year ago

Actually just noticed when re-reading after posting with the screenshots, that Proton Next appears in the game list too. I'd like to tackle that in this PR as well :-) It seems like Proton Next is not available in the dropdown of compatibility tools. Probably this is some other issue that should be handled separately, since it seems like the core of the issue here is that ProtonUp-Qt does not detect Proton Next as a separate compat tool? Not totally sure, but probably something that, on second thought, can be left out of this PR.

DavidoTek commented 1 year ago

Yeah, makes a lot of sense to not include the soundtracks in the game dialog ;)

Your approach seems good. I also cannot think of another way to get this information, appinfo.vdf probably doesn't have any infos on this? I'm wondering, does the approach have any impact on the start time?

ProtonUp-Qt does not detect Proton Next as a separate compat tool

Steam own compatibility tools are read in a different way using get_steam_ctool_list. It obtains the information whether a Steam app is a compatibility tool or not from the appinfo.vdf (see this). Maybe this contains incorrect information about Proton Next.

Thanks!

sonic2kk commented 1 year ago

appinfo.vdf probably doesn't have any infos on this?

From what I could see, it does not unfortunately. I couldn't find anywhere that stored information about app type :frowning:

I'm wondering, does the approach have any impact on the start time?

So when I first thought of this approach I also thought will this impact startup time, since so much work was put in recently in improving that I didn't want to just step all over it.

In my usage I didn't notice a bump, but you put me in mind to actually time it :-) So here we go!

I tested by putting a time.time() at the start and end of get_steam_app_list, where the additional logic is placed.

Main Branch

Full testing times:
First load of ProtonUp-Qt: - 1st test: `get_steam_app_list took 2.344550371170044 seconds` - 2nd test: `get_steam_app_list took 2.302269220352173 seconds` - 3rd test: `get_steam_app_list took 2.3538291454315186 seconds` Takes roughly 2.3 seconds. Opening Games List dialog (runs twice so showing both values): - 1st test: - `get_steam_app_list took 1.326843500137329 seconds` - `get_steam_app_list took 1.3295726776123047 seconds` - 2nd test - `get_steam_app_list took 1.3618791103363037 seconds` - `get_steam_app_list took 1.3580753803253174 seconds` - 3rd test - `get_steam_app_list took 1.3741896152496338 seconds` - `get_steam_app_list took 1.3622820377349854 seconds` Takes roughly 1.35 seconds.

This PR

Full testing times:
First load of ProtonUp-Qt: - 1st test: `get_steam_app_list took 2.3772754669189453 seconds` - 2nd test: `get_steam_app_list took 2.395808458328247 seconds` - 3rd test: `get_steam_app_list took 2.38689923286438 seconds` Takes roughly 2.4 seconds. Opening Games List dialog (runs twice so showing both values): - 1st test: - `get_steam_app_list took 1.4157302379608154 seconds` - `get_steam_app_list took 1.3954129219055176 seconds` - 2nd test - `get_steam_app_list took 1.3871181011199951 seconds` - `get_steam_app_list took 1.3708641529083252 seconds` - 3rd test - `get_steam_app_list took 1.402207374572754 seconds` - `get_steam_app_list took 1.3632738590240479 seconds` Takes roughly 1.4 seconds.

It seems like, looking at the timings here, that on my PC it takes very slightly longer. This probably is not a big deal, and to my eye I don't even notice the bump in load time. However please feel free to test this more thoroughly, or if there's a better way to parse the info from the file that could reduce the load times I would be happy to improve it :-)

Maybe this contains incorrect information about Proton Next

You may be onto something here. Proton Next is a weird case. Its folder structure is slightly different (it's missing the unpacked dist folder and just has a tar), and as well as this, currently its VERSION file is identical to that of the current stable Proton 7.0. It could indeed just be held in an unusual way.

DavidoTek commented 1 year ago

That's great. Thanks for taking the time and doing the testing :)

However please feel free to test this more thoroughly, or if there's a better way to parse the info from the file that could reduce the load times I would be happy to improve it :-)

I did a quick test and parsing a single appmanifest.acf file took less than 1ms on average (actually, parsing a single file 2000x took ~0.2ms per run. Not sure if there's any caching involved in parsing a single file). I think that's fine and maybe your approach will come in handy in the future if we need any other information from that file.

You may be onto something here. Proton Next is a weird case.

That's interesting. I'm wondering if its releases are also handled differently, like if it's just a link to the latest not yet released version...

EDIT: I made following change to prevent an error if the acf file doesn't contain AppState for some reason. appmanifest_install_path will be None in that case.

+ appmanifest_install_path = vdf.load(open(appmanifest_path)).get('AppState', {}).get('installdir')
- appmanifest_install_path = vdf.load(open(appmanifest_path)).get('AppState').get('installdir')