Files
plezy/lib/services/trakt/trakt_session.dart
T
2026-04-19 11:31:27 +02:00

93 lines
2.9 KiB
Dart

import 'dart:convert';
/// Immutable Trakt OAuth session.
///
/// Persisted as a JSON blob under `user_{uuid}_trakt_session` in
/// `SharedPreferences`. Tokens are stored in plaintext, matching the security
/// model of the existing Plex token.
class TraktSession {
final String accessToken;
final String refreshToken;
/// Epoch seconds at which the access token expires.
final int expiresAt;
/// Trakt username (`@handle`), populated after `getUserSettings`.
final String? username;
final String scope;
/// Epoch seconds at which the session was first created.
final int createdAt;
const TraktSession({
required this.accessToken,
required this.refreshToken,
required this.expiresAt,
required this.scope,
required this.createdAt,
this.username,
});
/// Whether the access token has already expired.
bool get isExpired => DateTime.now().millisecondsSinceEpoch ~/ 1000 >= expiresAt;
/// Whether the access token will expire in the next 5 minutes.
bool get needsRefresh => DateTime.now().millisecondsSinceEpoch ~/ 1000 >= expiresAt - 300;
TraktSession copyWith({
String? accessToken,
String? refreshToken,
int? expiresAt,
String? username,
String? scope,
int? createdAt,
}) {
return TraktSession(
accessToken: accessToken ?? this.accessToken,
refreshToken: refreshToken ?? this.refreshToken,
expiresAt: expiresAt ?? this.expiresAt,
username: username ?? this.username,
scope: scope ?? this.scope,
createdAt: createdAt ?? this.createdAt,
);
}
Map<String, dynamic> toJson() => {
'access_token': accessToken,
'refresh_token': refreshToken,
'expires_at': expiresAt,
'username': username,
'scope': scope,
'created_at': createdAt,
};
factory TraktSession.fromJson(Map<String, dynamic> json) {
return TraktSession(
accessToken: json['access_token'] as String,
refreshToken: json['refresh_token'] as String,
expiresAt: (json['expires_at'] as num).toInt(),
username: json['username'] as String?,
scope: json['scope'] as String? ?? 'public',
createdAt: (json['created_at'] as num).toInt(),
);
}
/// Build a session from Trakt's `/oauth/token` or `/oauth/device/token` response,
/// which uses `expires_in` (relative seconds) rather than `expires_at`.
factory TraktSession.fromTokenResponse(Map<String, dynamic> json) {
final createdAt = (json['created_at'] as num?)?.toInt() ?? DateTime.now().millisecondsSinceEpoch ~/ 1000;
final expiresIn = (json['expires_in'] as num).toInt();
return TraktSession(
accessToken: json['access_token'] as String,
refreshToken: json['refresh_token'] as String,
expiresAt: createdAt + expiresIn,
scope: json['scope'] as String? ?? 'public',
createdAt: createdAt,
);
}
String encode() => json.encode(toJson());
static TraktSession decode(String raw) => TraktSession.fromJson(json.decode(raw) as Map<String, dynamic>);
}