Skip to content

Commit c6b8b17

Browse files
authored
fix: Add fallback version parsing for regional Play Store pages (fixes #521) (#522)
* fix: issue521 * fix: all test * test: add * chore: remove debugging log * Update lib/src/play_store_search_api.dart
1 parent 1445814 commit c6b8b17

File tree

3 files changed

+212
-2
lines changed

3 files changed

+212
-2
lines changed

lib/src/play_store_search_api.dart

Lines changed: 100 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -279,14 +279,112 @@ extension PlayStoreResults on PlayStoreSearchAPI {
279279
final storeVersion = versionElement.substring(
280280
storeVersionStartIndex, storeVersionEndIndex);
281281

282-
// storeVersion might be: 'Varies with device', which is not a valid version.
283-
version = Version.parse(storeVersion).toString();
282+
if (debugLogging) {
283+
print('upgrader: PlayStoreResults.redesignedVersion: extracted storeVersion="$storeVersion"');
284+
}
285+
286+
// storeVersion might be empty, null, or 'Varies with device', which is not a valid version.
287+
// Validate before parsing
288+
if (storeVersion.isEmpty) {
289+
return null;
290+
}
291+
292+
// Try to parse the version string
293+
try {
294+
version = Version.parse(storeVersion).toString();
295+
if (debugLogging) {
296+
print('upgrader: PlayStoreResults.redesignedVersion: successfully parsed version="$version"');
297+
}
298+
} on FormatException catch (e) {
299+
if (debugLogging) {
300+
print('upgrader: PlayStoreResults.redesignedVersion: invalid version format "$storeVersion": $e');
301+
}
302+
// If version parsing failed, try alternative pattern (for regional pages)
303+
version = _parseVersionAlternative(response, debugLogging);
304+
}
284305
} catch (e) {
285306
if (debugLogging) {
286307
print('upgrader: PlayStoreResults.redesignedVersion exception: $e');
287308
}
309+
// If the main parsing failed, try alternative pattern (for regional pages)
310+
version = _parseVersionAlternative(response, debugLogging);
288311
}
289312

290313
return version;
291314
}
315+
316+
/// Alternative version parsing for regional Play Store pages (e.g., Korean, Bengali)
317+
///
318+
/// When the main parsing method fails on regional pages, this method tries multiple
319+
/// fallback patterns to extract version information from the Play Store JSON data.
320+
///
321+
/// Patterns tried:
322+
/// 1. JSON key pattern: "XXX":[[["version" where XXX is a numeric key (common: 140-145)
323+
String? _parseVersionAlternative(Document response, bool debugLogging) {
324+
try {
325+
final scripts = response.getElementsByTagName("script");
326+
327+
// Pattern 1: Try common JSON data keys where version info appears (140-145)
328+
// These keys represent version data in Play Store's internal structure
329+
/*
330+
* The keys 140-145 were determined by inspecting the Play Store's page source and
331+
* network responses. In the Play Store's internal JSON data structure, the version
332+
* information for an app is often found under numeric keys in this range.
333+
* These keys are not documented by Google and may change if the Play Store's
334+
* internal structure changes. If version extraction fails in the future,
335+
* maintainers should re-examine the Play Store's page source or network traffic
336+
* to identify the new keys where version information is stored, and update this
337+
* list accordingly.
338+
*/
339+
for (var key in [140, 141, 142, 143, 144, 145]) {
340+
final pattern = '"$key":[[["';
341+
const patternEndOfString = '"';
342+
343+
final versionElements = scripts.where(
344+
(element) => element.text.contains(pattern));
345+
346+
if (versionElements.isNotEmpty) {
347+
final versionElement = versionElements.first.text;
348+
final versionStartIndex =
349+
versionElement.indexOf(pattern) + pattern.length;
350+
351+
if (versionStartIndex >= pattern.length) {
352+
final versionEndIndex = versionStartIndex +
353+
versionElement
354+
.substring(versionStartIndex)
355+
.indexOf(patternEndOfString);
356+
357+
if (versionEndIndex > versionStartIndex) {
358+
final storeVersion =
359+
versionElement.substring(versionStartIndex, versionEndIndex);
360+
361+
if (storeVersion.isNotEmpty) {
362+
// Try to parse the version string
363+
try {
364+
final parsed = Version.parse(storeVersion);
365+
if (debugLogging) {
366+
print('upgrader: PlayStoreResults._parseVersionAlternative: found version="$storeVersion" with key=$key');
367+
}
368+
return parsed.toString();
369+
} on FormatException {
370+
// This key didn't have a valid version, try next key
371+
continue;
372+
}
373+
}
374+
}
375+
}
376+
}
377+
}
378+
379+
if (debugLogging) {
380+
print('upgrader: PlayStoreResults._parseVersionAlternative: no valid version found in common patterns');
381+
}
382+
return null;
383+
} catch (e) {
384+
if (debugLogging) {
385+
print('upgrader: PlayStoreResults._parseVersionAlternative exception: $e');
386+
}
387+
return null;
388+
}
389+
}
292390
}

test/play_store_issue521_test.dart

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/*
2+
* Test for version parsing issue #521
3+
* https://github.com/larryaasen/upgrader/issues/521
4+
*/
5+
6+
import 'dart:io';
7+
import 'package:flutter_test/flutter_test.dart';
8+
import 'package:html/parser.dart';
9+
import 'package:upgrader/upgrader.dart';
10+
11+
void main() {
12+
test('testing Play Store version parsing issue #521', () async {
13+
// Load the actual Play Store HTML that causes the issue
14+
final testFile = File('test/test_play_store_page_issue521.txt');
15+
final exists = testFile.existsSync();
16+
17+
if (!exists) {
18+
print('Test file not found: test/test_play_store_page_issue521.txt');
19+
return;
20+
}
21+
22+
final contents = testFile.readAsStringSync();
23+
final document = parse(contents);
24+
25+
final playStore = PlayStoreSearchAPI();
26+
playStore.debugLogging = true;
27+
28+
// Try to parse version from regional Play Store page (Korean example)
29+
final version = playStore.version(document);
30+
31+
print('Parsed version: $version');
32+
33+
// FIXED: Issue #521 has been fixed! Now it successfully parses regional pages
34+
// Using alternative pattern "141":[[["1.5.1" for regional Play Store pages
35+
expect(version, equals('1.5.1'),
36+
reason: 'FIXED #521: Version parsing now works for regional Play Store pages using alternative pattern');
37+
}, skip: false);
38+
39+
test('testing redesignedVersion for issue #521', () async {
40+
final testFile = File('test/test_play_store_page_issue521.txt');
41+
final exists = testFile.existsSync();
42+
43+
if (!exists) {
44+
print('Test file not found: test/test_play_store_page_issue521.txt');
45+
return;
46+
}
47+
48+
final contents = testFile.readAsStringSync();
49+
final document = parse(contents);
50+
51+
final playStore = PlayStoreSearchAPI();
52+
playStore.debugLogging = true;
53+
54+
// Directly test the redesignedVersion method
55+
final version = playStore.redesignedVersion(document);
56+
57+
print('Redesigned version: $version');
58+
59+
// FIXED: Now successfully parses regional pages using alternative pattern
60+
// Falls back to "141":[[["1.5.1" pattern when main pattern fails
61+
expect(version, equals('1.5.1'),
62+
reason: 'FIXED #521: redesignedVersion now handles regional Play Store pages correctly');
63+
}, skip: false);
64+
65+
test('FormatException issue #521 is now fixed', () async {
66+
// This test verifies that issue #521 has been fixed:
67+
// The original error was "PlayStoreResults.redesignedVersion exception: FormatException: Not a properly formatted version string"
68+
// Now it should successfully parse the version using alternative pattern
69+
70+
final testFile = File('test/test_play_store_page_issue521.txt');
71+
if (!testFile.existsSync()) {
72+
print('Test file not found: test/test_play_store_page_issue521.txt');
73+
return;
74+
}
75+
76+
final contents = testFile.readAsStringSync();
77+
final document = parse(contents);
78+
79+
final playStore = PlayStoreSearchAPI();
80+
playStore.debugLogging = true;
81+
82+
// Now it should successfully parse the version without throwing an exception
83+
final version = playStore.version(document);
84+
85+
// FIXED: Now returns the actual version instead of null
86+
expect(version, equals('1.5.1'),
87+
reason: 'FIXED #521: Version parsing now works correctly for regional Play Store pages');
88+
}, skip: false);
89+
}

test/test_play_store_page_issue521.txt

Lines changed: 23 additions & 0 deletions
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)