[READ ONLY MIRROR] Open Source TikTok alternative built on AT Protocol github.com/sprksocial/client
flutter atproto video dart
10
fork

Configure Feed

Select the types of activity you want to include in your feed.

fix: refresh video service token while polling

+77 -13
+77 -13
lib/src/core/network/atproto/data/repositories/feed_repository_impl.dart
··· 6 6 7 7 import 'package:atproto/com_atproto_label_defs.dart'; 8 8 import 'package:atproto/com_atproto_repo_strongref.dart'; 9 + import 'package:atproto/atproto.dart'; 9 10 import 'package:atproto/core.dart'; 10 11 import 'package:bluesky/app_bsky_feed_getauthorfeed.dart'; 11 12 import 'package:bluesky/app_bsky_richtext_facet.dart'; ··· 1264 1265 ); 1265 1266 } 1266 1267 1267 - final pdsService = authAtProto.service; 1268 - final serviceTokenRes = await authAtProto.server.getServiceAuth( 1269 - aud: 'did:web:$pdsService', 1270 - lxm: 'com.atproto.repo.uploadBlob', 1271 - exp: 1272 - DateTime.now() 1273 - .toUtc() 1274 - .add(const Duration(minutes: 5)) 1275 - .millisecondsSinceEpoch ~/ 1276 - 1000, 1277 - ); 1278 - 1279 - final serviceToken = serviceTokenRes.data.token; 1268 + var serviceToken = await _createVideoServiceAuthToken(); 1280 1269 final uploadRequest = 1281 1270 http.StreamedRequest( 1282 1271 'POST', ··· 1357 1346 'Content-Type': _getContentType(cleanVideoPath), 1358 1347 }, 1359 1348 ); 1349 + if (_isExpiredVideoServiceTokenResponse(response)) { 1350 + _logger.i( 1351 + 'Video service token expired while polling; minting a new token', 1352 + ); 1353 + serviceToken = await _createVideoServiceAuthToken( 1354 + refreshPdsSessionOnFailure: true, 1355 + ); 1356 + response = await http.get( 1357 + Uri.parse( 1358 + '${AppConfig.videoServiceUrl}/xrpc/so.sprk.video.getJobStatus', 1359 + ).replace( 1360 + queryParameters: {'jobId': responseData['jobStatus']?['jobId']}, 1361 + ), 1362 + headers: { 1363 + 'Authorization': 'Bearer $serviceToken', 1364 + 'Content-Type': _getContentType(cleanVideoPath), 1365 + }, 1366 + ); 1367 + } 1360 1368 if (response.statusCode != 200) { 1361 1369 throw Exception( 1362 1370 'Failed to check video upload status: ${response.statusCode} ' ··· 1468 1476 } 1469 1477 1470 1478 onUploadProgress?.call(1); 1479 + } 1480 + 1481 + Future<String> _createVideoServiceAuthToken({ 1482 + bool refreshPdsSessionOnFailure = false, 1483 + }) async { 1484 + final atproto = _client.authRepository.atproto; 1485 + if (atproto == null) { 1486 + throw Exception('AtProto not initialized'); 1487 + } 1488 + 1489 + try { 1490 + return await _requestVideoServiceAuthToken(atproto); 1491 + } catch (e) { 1492 + if (!refreshPdsSessionOnFailure) { 1493 + rethrow; 1494 + } 1495 + 1496 + _logger.i( 1497 + 'Refreshing PDS session before minting video service token', 1498 + error: e, 1499 + ); 1500 + final refreshed = await _client.authRepository.refreshToken(); 1501 + if (!refreshed) { 1502 + throw Exception('Session expired. Please log in again.'); 1503 + } 1504 + 1505 + final refreshedAtproto = _client.authRepository.atproto; 1506 + if (refreshedAtproto == null) { 1507 + throw Exception('AtProto not initialized after refresh'); 1508 + } 1509 + return _requestVideoServiceAuthToken(refreshedAtproto); 1510 + } 1511 + } 1512 + 1513 + Future<String> _requestVideoServiceAuthToken(ATProto atproto) async { 1514 + final serviceTokenRes = await atproto.server.getServiceAuth( 1515 + aud: 'did:web:${atproto.service}', 1516 + lxm: 'com.atproto.repo.uploadBlob', 1517 + exp: 1518 + DateTime.now() 1519 + .toUtc() 1520 + .add(const Duration(minutes: 5)) 1521 + .millisecondsSinceEpoch ~/ 1522 + 1000, 1523 + ); 1524 + 1525 + return serviceTokenRes.data.token; 1526 + } 1527 + 1528 + bool _isExpiredVideoServiceTokenResponse(http.Response response) { 1529 + if (response.statusCode != 401) { 1530 + return false; 1531 + } 1532 + 1533 + final body = response.body.toLowerCase(); 1534 + return body.contains('jwt has expired') || body.contains('invalidtoken'); 1471 1535 } 1472 1536 1473 1537 /// Crosspost images to Bluesky using adapter to handle Bluesky-specific model