Drops dead code across services, models, utils and widgets, including the connection auth service, which had no implementer, and the Live TV DVR provisioning models, which had no caller. Tests that only covered deleted behaviour are removed or trimmed. No behaviour change.
121 lines
4.0 KiB
Dart
121 lines
4.0 KiB
Dart
import '../utils/app_logger.dart';
|
|
import '../utils/json_utils.dart';
|
|
import 'plex/plex_user_profile.dart';
|
|
|
|
class UserSwitchResponse {
|
|
final int id;
|
|
final String uuid;
|
|
final String username;
|
|
final String title;
|
|
final String email;
|
|
final String? friendlyName;
|
|
final String? locale;
|
|
final bool confirmed;
|
|
final int joinedAt;
|
|
final bool emailOnlyAuth;
|
|
final bool hasPassword;
|
|
final bool protected;
|
|
final String thumb;
|
|
final String authToken;
|
|
final bool? mailingListActive;
|
|
final String scrobbleTypes;
|
|
final String country;
|
|
final bool restricted;
|
|
final bool? anonymous;
|
|
final bool home;
|
|
final bool guest;
|
|
final int homeSize;
|
|
final bool homeAdmin;
|
|
final int maxHomeSize;
|
|
final PlexUserProfile profile;
|
|
final bool twoFactorEnabled;
|
|
final bool backupCodesCreated;
|
|
final String? attributionPartner;
|
|
|
|
UserSwitchResponse({
|
|
required this.id,
|
|
required this.uuid,
|
|
required this.username,
|
|
required this.title,
|
|
required this.email,
|
|
this.friendlyName,
|
|
this.locale,
|
|
required this.confirmed,
|
|
required this.joinedAt,
|
|
required this.emailOnlyAuth,
|
|
required this.hasPassword,
|
|
required this.protected,
|
|
required this.thumb,
|
|
required this.authToken,
|
|
this.mailingListActive,
|
|
required this.scrobbleTypes,
|
|
required this.country,
|
|
required this.restricted,
|
|
this.anonymous,
|
|
required this.home,
|
|
required this.guest,
|
|
required this.homeSize,
|
|
required this.homeAdmin,
|
|
required this.maxHomeSize,
|
|
required this.profile,
|
|
required this.twoFactorEnabled,
|
|
required this.backupCodesCreated,
|
|
this.attributionPartner,
|
|
});
|
|
|
|
/// INVARIANT (#1488): a successful token mint must never be lost to parsing
|
|
/// of decorative fields. `authToken` is the only field any caller consumes
|
|
/// (see plex_home_switch.dart) — it alone parses strictly; every other
|
|
/// field tolerates missing/wrong-typed values with sane defaults. Plex has
|
|
/// changed field shapes on this endpoint before (July 2026: profile
|
|
/// language lists became CSV strings), and each drift used to brick token
|
|
/// minting outright.
|
|
factory UserSwitchResponse.fromJson(Map<String, dynamic> json) {
|
|
final authToken = json['authToken'];
|
|
if (authToken is! String || authToken.isEmpty) {
|
|
throw const FormatException('Plex /switch response has no usable authToken');
|
|
}
|
|
|
|
PlexUserProfile profile;
|
|
try {
|
|
profile = PlexUserProfile.fromJson(json);
|
|
} catch (e, st) {
|
|
appLogger.w('UserSwitchResponse: profile blob failed to parse; using defaults', error: e, stackTrace: st);
|
|
profile = PlexUserProfile.defaults();
|
|
}
|
|
|
|
String? optString(String key) => json[key]?.toString();
|
|
|
|
return UserSwitchResponse(
|
|
id: flexibleInt(json['id']) ?? 0,
|
|
uuid: optString('uuid') ?? '',
|
|
username: optString('username') ?? '',
|
|
title: optString('title') ?? '',
|
|
email: optString('email') ?? '',
|
|
friendlyName: optString('friendlyName'),
|
|
locale: optString('locale'),
|
|
confirmed: flexibleBool(json['confirmed']),
|
|
joinedAt: flexibleInt(json['joinedAt']) ?? 0,
|
|
emailOnlyAuth: flexibleBool(json['emailOnlyAuth']),
|
|
hasPassword: flexibleBool(json['hasPassword']),
|
|
protected: flexibleBool(json['protected']),
|
|
thumb: optString('thumb') ?? '',
|
|
authToken: authToken,
|
|
mailingListActive: flexibleBoolNullable(json['mailingListActive']),
|
|
scrobbleTypes: optString('scrobbleTypes') ?? '',
|
|
country: optString('country') ?? '',
|
|
restricted: flexibleBool(json['restricted']),
|
|
anonymous: flexibleBoolNullable(json['anonymous']),
|
|
home: flexibleBool(json['home']),
|
|
guest: flexibleBool(json['guest']),
|
|
homeSize: flexibleInt(json['homeSize']) ?? 1,
|
|
homeAdmin: flexibleBool(json['homeAdmin']),
|
|
maxHomeSize: flexibleInt(json['maxHomeSize']) ?? 1,
|
|
profile: profile,
|
|
twoFactorEnabled: flexibleBool(json['twoFactorEnabled']),
|
|
backupCodesCreated: flexibleBool(json['backupCodesCreated']),
|
|
attributionPartner: optString('attributionPartner'),
|
|
);
|
|
}
|
|
}
|