Skip to content

Cloud catalog & streaming fixes#16

Open
nyakaspeter wants to merge 6 commits into
ForWard-Technologies-LLC:masterfrom
nyakaspeter:fix/cloud-play-catalog-and-ps4-streaming
Open

Cloud catalog & streaming fixes#16
nyakaspeter wants to merge 6 commits into
ForWard-Technologies-LLC:masterfrom
nyakaspeter:fix/cloud-play-catalog-and-ps4-streaming

Conversation

@nyakaspeter

@nyakaspeter nyakaspeter commented Jun 3, 2026

Copy link
Copy Markdown

Hi, I downloaded Pylux for the cloud play capabilities, and I was delighted to see it works but while experimenting with it I faced numerous problems regarding the game catalog not loading, missing PS4 games in library etc.

I asked Claude to fix the issues and through a couple of hours of debugging it managed to fix most of them so I can say fairly confidently that with these changes the app shows both the PS Plus Game Catalog titles and my library items correctly and I can stream most of them without errors. My store region is Hungary so that's where I could confirm that the game catalog changes work, other regions may still need work/testing. I built the app for macOS, Android and Linux (Steam Deck) and tested the changes on all 3 platforms.

So I submit this PR as it probably contains some useful findings for further improving the app. Below is the summary of the changes generated by Claude:


Reworks PlayStation Plus Cloud Play end to end so it works in English-only regions (e.g. Hungary), streams owned PS4 and PS5 titles correctly, shows cross-gen editions, classifies ownership (full game vs trial vs add-on), and adds PS3 Classics cloud streaming. Applied across the Qt (C++/QML), iOS (Swift) and Android (Kotlin) clients.

Catalog / region

  • Store-locale fallback chain (lang-COUNTRY -> en-COUNTRY -> en-US) so the imagic catalog loads in every region; the validated locale persists.
  • Accept PS4 (not just PS5) cloud titles in the merge; capture streamingSupported=false subscription titles into the library-stream supplement from every subscription list (these stream via the legacy Kamaji/kratos path even though they are absent from public cloud browse).
  • Scope the views: Game Catalog = PS Plus subscription lists (plusCatalog tag); Library "all" = full streamable universe + owned; Library "owned" = owned.
  • Catalog falls back to the imagic catalog when the legacy PS Now /user/stores browse 404s (it does in many regions).
  • Dedupe per game per platform so cross-gen PS4/PS5 editions both appear.
  • Broaden the owned-games filter; match owned entitlements by conceptId in addition to product id / stable key.

Owned-title streaming (entitlement resolution)

  • PS5 streams the owned PRODUCT id, not the entitlement id: a cross-gen upgrade (PS4 purchase + free PS5 copy) carries a stale original-SKU entitlement id that Gaikai's cloud catalog has no game for (-> noGameForEntitlementId); product_id is the current streamable SKU. (Fixed Alan Wake Remastered, Death Stranding DC.)
  • When several SKUs collapse to one edition (base game + bonus/upgrade/avatars), keep the canonical full-game entitlement -- the one whose entitlement id EQUALS its product_id. Package/feature flags don't disambiguate (Death Stranding DC's "Bonus Content" is also PSGD + feature_type 3), so the id==product_id signal is what selects the real game over a DLC product Gaikai can't stream.
  • PS4 streams the catalog's streamable variant (e.g. God of War's "...N" SKU whose Kamaji container holds the PS-Now license_type=4 SKU), not the owned download SKU; derive the streaming platform from the owned product (cross-gen catalog entries list the other generation). PS4 (CUSA) -> Kamaji/psnow; PS5 (PPSA) -> direct Gaikai (cronos). Datacenter ping no longer hard-fails on a measurement error (Qt).

Ownership classification (feature_type)

  • feature_type 3/5 = full game owned, 1 = trial / free-to-play, 0 = add-on/DLC.
  • Drop feature_type==0 extras from the owned set (DLC/themes/avatars are never a base game). Keep trials and free-to-play; a trial is kept as its own card so the full version still shows separately as "Add Game" (a trial does not collapse into the full-game catalog entry).

Cross-gen owned-library split

  • Key owned-edition identity on conceptId + PLATFORM (matching the catalog tab) in both the owned cross-reference dedupe and the library merge, so a title owned on PS4 and PS5 (e.g. Days Gone + Days Gone Remastered) shows two separate, independently-streamable cards.

Platform labels

  • Derive PS4/PS5 from the title id (CUSA/PPSA) instead of the hard-coded platform="ps5" -- Android at display time (CloudGameAdapter), iOS in the parser/deserializer (self-correcting the cache); Qt already did.

Catalog ownership UX

  • Cross-reference the catalog against owned entitlements (mark-only): owned ->
    "Stream", non-owned modern cloud titles -> "Add Game"; OWNED / NOT OWNED badge.

PS3 Classics cloud streaming

  • PS Plus Premium streams ~250–330 PS3 Classics that never appear in the imagic/gameslist catalog used for everything else. They're sourced from the public pcnow ("Apollo") container API (no auth) and stream via the existing Gaikai konan (PS3) path. This is the only public source for these ids — the PS5's own catalog API is behind Sony's private DNAS PKI and unreadable; the catalog was recovered by capturing the PS Plus PC app's traffic.
  • Region-generic: pcnow exposes two Classics id families — Americas/SCEA (store MSF192018, UP9000/NPUA/BLUS ids, PS3 child container APOLLOPS3GAMES) and PAL/SCEE (store MSF192014, EP9000/NPEA/NPEB/BLES ids, PS3 child container APOLLOPS3). The account's region group selects the store; everything outside the Americas defaults to PAL. (JP/Asia have no Apollo store — same as Sony's official PC app, which simply doesn't offer cloud streaming there.)
  • PS3 titles appear in the Game Catalog and the Library "all" view (never "owned" — they're subscription-streamable, not owned) and always show "Stream Game".
  • Streaming: for legacy (non-CUSA/PPSA) ids, resolve product → entitlement in the region-group store, then skip the regional checkout/acquire on a 404 — PS Plus Premium auto-authorizes the entitlement at Gaikai, and the free 100%-off checkout is unavailable in regions without a pcnow storefront (e.g. Hungary, which returns "Storefront not found"). PS4 (CUSA) / PS5 (PPSA) paths are unchanged.

Android: portrait-mode stream-start crash

  • Starting a stream locks the activity to landscape via requestedOrientation. On tablets, a portrait↔landscape change also alters screenLayout/smallestScreenSize, which MainActivity didn't declare in configChanges — so Android recreated the activity, detached CloudPlayFragment, and the in-flight startCloudStreaming coroutine crashed on requireActivity() ("Fragment not attached to an activity"). Declared those config dimensions so the rotation is handled in place. (Phone-only orientation|screenSize was enough before, hence tablet/portrait-specific.)

Build

  • Remove the committed machine-specific org.gradle.java.home (an absolute Windows path that broke every non-Windows / CI Gradle build); document selecting the JDK 21 daemon per-machine via JAVA_HOME / ~/.gradle / the IDE Gradle JDK setting.

Verified

  • Qt/macOS (Hungary / PS Plus Premium): catalog loads region-wide; owned PS4 (God of War) and PS5 (Alan Wake Remastered, Death Stranding DC) stream from Library and Catalog; cross-gen Days Gone shows + streams both editions; a trial (Cyberpunk) shows its own Stream card plus an "Add Game" card for the full version; adding the PS5 Remaster lets Spider-Man stream; labels and OWNED/NOT OWNED badges correct.
  • PS3 Classics: streamed on a real PS Plus Premium (Hungary) account on Qt/macOS and on Android (on-device); appear in both the Catalog and Library "all" views. iOS PS3 path is compile-verified (swiftc -parse) but not yet device-tested.
  • Android portrait-mode stream-start crash: verified fixed on an Android tablet (Lenovo TB132FU).
  • iOS: swiftc -parse clean. Android: compiles (compileDebugKotlin). Mobile not device-re-tested for the latest PS4/PS5 streaming/ownership pass.

Upstream reconciliation (PR #15)

  • Sits on top of the merged "PS5 cloud ownership matching" PR (Fix PS5 cloud ownership matching across Qt, iOS, and Android. #15) and incorporates its useful additions: bundle-sibling expansion (a bundle entitlement, e.g. RE7 Gold, expands to its component games via componentIdsByProductId) and stable-key matching on the entitlement id. These are grafted onto our cross-reference as additive fallbacks (they only fire when our direct cascade finds no match), keeping our dedupe (conceptId+platform + canonical-entitlement rank), feature_type filtering and field convention where the two approaches differed.

Known limitations

  • Some PS Plus titles are download-only (no cloud-streaming SKU, e.g. Far Cry 5, original Spider-Man PS4): indistinguishable in the catalog from streamable PS4 titles, so they appear but fail at sessions/start with noGameForEntitlementId.
  • PS5 catalog-only titles must be added to the library externally (PS App) first.
  • PS3 Classics stream via the public pcnow (Apollo) catalog, but only for the regions Sony serves there — the Americas (SCEA) and PAL/Europe (SCEE) id families. JP/Asia have no Apollo store (Sony doesn't offer the PC app/cloud streaming there), so those accounts can't stream PS3 via this path. iOS PS3 is compile-verified but not yet device-tested.

nyakaspeter and others added 3 commits June 3, 2026 14:23
…ary + ownership (Qt/iOS/Android)

Reworks PlayStation Plus Cloud Play end to end so it works in English-only
regions (e.g. Hungary), streams owned PS4 and PS5 titles correctly, shows
cross-gen editions, and classifies ownership (full game vs trial vs add-on).
Applied across the Qt (C++/QML), iOS (Swift) and Android (Kotlin) clients.

Catalog / region
- Store-locale fallback chain (lang-COUNTRY -> en-COUNTRY -> en-US) so the
  imagic catalog loads in every region; the validated locale persists.
- Accept PS4 (not just PS5) cloud titles in the merge; capture
  streamingSupported=false subscription titles into the library-stream
  supplement from every subscription list (these stream via the legacy
  Kamaji/kratos path even though they are absent from public cloud browse).
- Scope the views: Game Catalog = PS Plus subscription lists (plusCatalog tag);
  Library "all" = full streamable universe + owned; Library "owned" = owned.
- Catalog falls back to the imagic catalog when the legacy PS Now /user/stores
  browse 404s (it does in many regions).
- Dedupe per game per platform so cross-gen PS4/PS5 editions both appear.
- Broaden the owned-games filter; match owned entitlements by conceptId in
  addition to product id / stable key.

Owned-title streaming (entitlement resolution)
- PS5 streams the owned PRODUCT id, not the entitlement id: a cross-gen upgrade
  (PS4 purchase + free PS5 copy) carries a stale original-SKU entitlement id that
  Gaikai's cloud catalog has no game for (-> noGameForEntitlementId); product_id
  is the current streamable SKU. (Fixed Alan Wake Remastered, Death Stranding DC.)
- When several SKUs collapse to one edition (base game + bonus/upgrade/avatars),
  keep the canonical full-game entitlement -- the one whose entitlement id EQUALS
  its product_id. Package/feature flags don't disambiguate (Death Stranding DC's
  "Bonus Content" is also PSGD + feature_type 3), so the id==product_id signal is
  what selects the real game over a DLC product Gaikai can't stream.
- PS4 streams the catalog's streamable variant (e.g. God of War's "...N" SKU whose
  Kamaji container holds the PS-Now license_type=4 SKU), not the owned download SKU;
  derive the streaming platform from the owned product (cross-gen catalog entries
  list the other generation). PS4 (CUSA) -> Kamaji/psnow; PS5 (PPSA) -> direct
  Gaikai (cronos). Datacenter ping no longer hard-fails on a measurement error (Qt).

Ownership classification (feature_type)
- feature_type 3/5 = full game owned, 1 = trial / free-to-play, 0 = add-on/DLC.
- Drop feature_type==0 extras from the owned set (DLC/themes/avatars are never a
  base game). Keep trials and free-to-play; a trial is kept as its own card so the
  full version still shows separately as "Add Game" (a trial does not collapse into
  the full-game catalog entry).

Cross-gen owned-library split
- Key owned-edition identity on conceptId + PLATFORM (matching the catalog tab) in
  both the owned cross-reference dedupe and the library merge, so a title owned on
  PS4 and PS5 (e.g. Days Gone + Days Gone Remastered) shows two separate,
  independently-streamable cards.

Platform labels
- Derive PS4/PS5 from the title id (CUSA/PPSA) instead of the hard-coded
  platform="ps5" -- Android at display time (CloudGameAdapter), iOS in the
  parser/deserializer (self-correcting the cache); Qt already did.

Catalog ownership UX
- Cross-reference the catalog against owned entitlements (mark-only): owned ->
  "Stream", non-owned modern cloud titles -> "Add Game"; OWNED / NOT OWNED badge.

Build
- Remove the committed machine-specific org.gradle.java.home (an absolute Windows
  path that broke every non-Windows / CI Gradle build); document selecting the
  JDK 21 daemon per-machine via JAVA_HOME / ~/.gradle / the IDE Gradle JDK setting.

Verified
- Qt/macOS (Hungary / PS Plus Premium): catalog loads region-wide; owned PS4 (God
  of War) and PS5 (Alan Wake Remastered, Death Stranding DC) stream from Library
  and Catalog; cross-gen Days Gone shows + streams both editions; a trial (Cyberpunk)
  shows its own Stream card plus an "Add Game" card for the full version; adding the
  PS5 Remaster lets Spider-Man stream; labels and OWNED/NOT OWNED badges correct.
- iOS: swiftc -parse clean. Android: compiles (compileDebugKotlin). Mobile not
  device-re-tested for the latest streaming/ownership pass.

Upstream reconciliation (PR ForWard-Technologies-LLC#15)
- Sits on top of the merged "PS5 cloud ownership matching" PR (ForWard-Technologies-LLC#15) and incorporates its
  useful additions: bundle-sibling expansion (a bundle entitlement, e.g. RE7 Gold, expands
  to its component games via componentIdsByProductId) and stable-key matching on the
  entitlement id. These are grafted onto our cross-reference as additive fallbacks (they only
  fire when our direct cascade finds no match), keeping our dedupe (conceptId+platform +
  canonical-entitlement rank), feature_type filtering and field convention where the two
  approaches differed.

Known limitations
- Some PS Plus titles are download-only (no cloud-streaming SKU, e.g. Far Cry 5,
  original Spider-Man PS4): indistinguishable in the catalog from streamable PS4
  titles, so they appear but fail at sessions/start with noGameForEntitlementId.
- PS5 catalog-only titles must be added to the library externally (PS App) first.
- PS3 titles absent from the modern imagic API; PS5 HEVC video-decode freeze is a
  separate pipeline issue.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
PS Plus Premium streams ~250-330 PS3 Classics that never appear in the
imagic/gameslist catalog the rest of cloud play uses. Source them from the
public pcnow ("Apollo") container API and stream them via the existing
Gaikai konan path.

- Catalog: new fetchPs3Catalog walks the public Apollo PS3 container (no auth),
  paginated; surfaced in the Game Catalog and Library "all" views (not "owned").
  PS3 cards always show "Stream Game".
- Region-generic: pcnow has two Classics id families -- Americas/SCEA
  (store MSF192018, UP/NPUA/BLUS ids, child APOLLOPS3GAMES) and PAL/SCEE
  (store MSF192014, EP/NPEA/NPEB/BLES ids, child APOLLOPS3). The account
  region group selects the store; everything outside the Americas -> PAL.
- Streaming: for legacy (non-CUSA/PPSA) ids, resolve product->entitlement in
  the region-group store, and skip the regional checkout/acquire on a 404
  (Premium auto-authorizes at Gaikai; the checkout is unavailable in regions
  without a pcnow storefront, e.g. Hungary).
- PS4 (CUSA) / PS5 (PPSA) paths unchanged.

Ported across macOS (Qt), iOS (Swift), Android (Kotlin). macOS + Android
verified streaming on a real PS Plus Premium account; iOS compile-verified.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Starting a cloud stream locks the activity to landscape via
requestedOrientation. On large tablets, portrait<->landscape also changes
screenLayout/smallestScreenSize, which MainActivity didn't declare in
configChanges -- so Android recreated the activity, detached
CloudPlayFragment, and the in-flight startCloudStreaming coroutine then
crashed on requireActivity() ("Fragment not attached to an activity").

Declare screenLayout|smallestScreenSize so MainActivity handles the rotation
itself instead of being recreated, keeping the fragment attached.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@nyakaspeter nyakaspeter closed this Jun 3, 2026
@nyakaspeter nyakaspeter deleted the fix/cloud-play-catalog-and-ps4-streaming branch June 3, 2026 20:26
@nyakaspeter nyakaspeter restored the fix/cloud-play-catalog-and-ps4-streaming branch June 3, 2026 20:30
@nyakaspeter nyakaspeter reopened this Jun 3, 2026
nyakaspeter and others added 3 commits June 4, 2026 14:26
PS Plus disc-upgrade entitlements (feature_type 5, e.g. Horizon Forbidden
West EP9000-PPSA01521) are the SKU the imagic browse catalog binds the
concept to, but Gaikai refuses to cloud-stream them
("disc-upgrade-unsupported"). The owned streamable edition (e.g. the
Complete Edition PPSA17903) is a different title id that is absent from the
catalog and -- like every commerce-API entitlement -- carries no conceptId,
so the owned cross-reference never matches it and only the unstreamable
disc-upgrade SKU survives the dedupe.

Add a disc-upgrade rescue to the owned cross-reference on all platforms
(Qt/iOS/Android): when a concept's surviving owned SKU is a disc upgrade,
adopt the product id of a same-name full-game (feature_type 3) owned SKU so
the card streams the edition Gaikai accepts. Since the only in-data bridge
is the title name, it is guarded to stay safe: same platform only (a PS5
disc upgrade can never resolve to a PS4 CUSA SKU), prefer the canonical base
game (product_id == entitlement id), and bail on genuine ambiguity rather
than guess.

Verified on macOS: Horizon Forbidden West now streams PPSA17903 instead of
the rejected PPSA01521.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…cards

The "all" library view merges owned entitlements into the browse catalog. For
PS5 (PPSA) the override of the catalog card's product id was guarded
(if (!existing.product_id)), so it applied only when the catalog card had no id.
When the browse row carries a product id -- e.g. Horizon Forbidden West's
concept is bound to the disc-upgrade SKU PPSA01521 -- the guard kept that
unstreamable id even though the cross-reference had rescued the owned full game
(PPSA17903), so Gaikai rejected it with "disc-upgrade-unsupported".

Override unconditionally for PS5, matching the iOS and Android merges (which
always copy the owned storeProductId). The owned PS5 product IS the streamable
entitlement, so it must win over the catalog's fixed per-concept SKU. PS4 (CUSA)
is unaffected (the whole block is PS5-only).

Fixes Horizon Forbidden West failing to stream on the Steam Deck / Linux build
while macOS and Android worked -- the guard only happened to pass on those when
the catalog cache had a null product id (data-dependent).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
ps5CloudPlatformToken() takes a GAME OBJECT (it reads game.productId / game.id),
but the "all"-view merge passed it the product-id STRING. A string has no
.productId/.id/.device, so it always returned "", the `=== "ps5"` test was never
true, and the block that copies the owned product id onto the matched catalog
card never executed -- for any game.

That left the catalog card's own (often unstreamable) SKU in place. For Horizon
Forbidden West the catalog binds the concept to the disc-upgrade SKU PPSA01521,
so the "all" filter streamed that and Gaikai rejected it
("disc-upgrade-unsupported"), while the "owned" filter worked (it uses the
cross-reference output directly, which already carries the rescued PPSA17903).

Pass the game object so the platform check resolves to "ps5" and the owned
product id wins. Pre-existing bug -- the earlier guard/un-guard edits were both
inside this dead block, which is why neither changed anything. iOS/Android were
unaffected (their merges copy storeProductId with no platform-token check).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@forward-technologies

Copy link
Copy Markdown
Collaborator

@nyakaspeter Nice! I appreciate it, I will do some testing on my end with this then see if I can get it merged in here. In your tests, were there any issues you noticed? Any specific games that still didnt load etc? I will focus my testing on that if so. Thanks for the PR :)

@nyakaspeter

Copy link
Copy Markdown
Author

Hey, I haven't had time to try all games of course but with the fixes, I haven't run into any games I couldn't stream. Well, apart from the ones that aren't streamable on console either, but I couldn't exclude from the game catalog without hiding some other (streamable) games (Far Cry 5 for example).

The biggest challenge as I understand is gathering all of the user's library from various sources and then choosing the correct streamable entitlement. The PlayStation APIs seem a bit all over the place (different APIs for listing PS3/4/5 games and different flows for getting the entitlement registered for the account) and they probably vary by region. Since I couldn't find any documentation for them, I relied on Claude to trial and error all the way through. To get the PS3 games list for my region for example, I had to sniff packets from the PS Plus windows app. It was especially tricky as for some reason the app doesn't work with a Hungarian account so I had to fall back to a UK one. Then I figured all European countries share the same IDs as well as the Americas. Though there are probably exceptions that's why I said regions other than mine may need other fixes.

I tried games with various scenarios and all of them seems to stream now:

  • PS5 games where I own multiple editions or acquired through PS4 to 5 disc upgrade (Horizon Forbidden West, had to filter out some editions in favor of the full game entitlement)
  • PS5 games where I only had a trial version (Cyberpunk 2077, had to filter out the trial version to be able to add full game to library, then stream)
  • PS5 games acquired through PS Plus Monthly (Alan Wake Remastered)
  • PS4 games that are streamable only after adding to library (I think AC Valhalla was like this)
  • PS4 games that are streamable without adding to library (most PS4 games are like this, but I couldn't separate them from the ones that aren't so I think it's best to require the user to add to library through PS App, then it will work either way)
  • PS3 games (God of War Ascension, Ratchet and Clank)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants