myConsciousness / atproto.dart

🦋 AT Protocol and Bluesky things for Dart and Flutter.
https://atprotodart.com
BSD 3-Clause "New" or "Revised" License
165 stars 15 forks source link

Bug Report: Upload video get ServiceAuth aud #1792

Closed hidea closed 2 days ago

hidea commented 2 days ago

Bug Behavior

In my case, Failed posted method ns.appBskyVideoUploadVideo with SocketException.

Expected Behavior

The video should upload correctly.

Error Log

flutter: postMessage Exception details:
 ClientException with SocketException: Broken pipe (OS Error: Broken pipe, errno = 32), address = video.bsky.app, port = 55896, uri=https://video.bsky.app/xrpc/app.bsky.video.uploadVideo?did=did%3Aplc%3Amlb2pidpmtfusfvd7x5zfcxq&name=pf13BJDuRjt9.mp4
flutter: Stack trace:
 #0      IOClient.send (package:http/src/io_client.dart:154:7)
<asynchronous suspension>
#1      BaseClient._sendUnstreamed (package:http/src/base_client.dart:93:32)
<asynchronous suspension>
#2      _withClient (package:http/http.dart:167:12)
<asynchronous suspension>
#3      Future.timeout.<anonymous closure> (dart:async/future_impl.dart:963:15)
<asynchronous suspension>
#4      procedure (package:xrpc/src/xrpc/xrpc.dart:288:9)
<asynchronous suspension>
#5      ServiceContext.post.<anonymous closure> (package:atproto_core/src/clients/service_context.dart:99:21)
<asynchronous suspension>
#6      Challenge.execute (package:atproto_core/src/clients/challenge.dart:27:14)
<asynchronous suspension>
#7      ServiceContext.post (package:atproto_core/src/clients/service_context.dart:98:7)
<asynchronous suspension>
#8      VideoService.uploadVideo (package:bluesky/src/services/video_service.dart:83:12)
<asynchronous suspension>
#9      Model.postMessage (package:sky_throw/model/mo<…>

Step to Reproduce

I copied and pasted the code in the example into my project and ran it.

    if (file != null) {
        final bsky = await getBluesky(currentActor!);

        //! Upload video
        final uploadedVideo = await bsky.video.uploadVideo(
          File(file!.path).readAsBytesSync(),
        );

        //! Let's post cool stuff!
        final createdRecord = await bsky.feed.post(
          text: 'Hello, Bluesky!',
          embed: Embed.video(
            data: EmbedVideo(
              video: uploadedVideo.data.blob!,
            ),
          ),
        );

        if (kDebugMode) {
          print(createdRecord);
        }
      }

Compared to the code I implemented myself, the following line of code has been changed so that no Socket errors occur.

video_service.dart::uploadVideo

    final auth = await _ctx.atproto.server.getServiceAuth(
-      aud: 'did:web:${_ctx.service}',
+      aud: 'did:web:${_ctx.atproto.session!.atprotoPdsEndpoint}',
      lxm: comAtprotoRepoUploadBlob,
      exp: DateTime.now().add(Duration(minutes: 30)).millisecondsSinceEpoch ~/
          1000,
    );
myConsciousness commented 2 days ago

Hi @hidea !

Are you using the service property when creating Bluesky object? The service property will be resolved dynamically based on Session object, but it will not if we use service property on Bluesky object.

So you can try:

    final bsky = Bluesky.fromSession(
        session,
        // service: 'SERVICE_NAME', ← Don't use it!
    );

In the future, the service property may need to be completely deprecated when creating Bluesky object from the session.

Also, video uploads may not be finished immediately, so you need to make sure the upload is complete:

    final session = await _session;

    final bsky = Bluesky.fromSession(session);

    final uploadLimits = await bsky.video.getUploadLimits();

    if (uploadLimits.data.canUpload) {
      final uploadedVideo = await bsky.video.uploadVideo(
        File('video.mov').readAsBytesSync(),
      );

      Blob? blob = uploadedVideo.data.blob;
      while (blob == null) {
        final jobStatus = await bsky.video.getJobStatus(
          jobId: uploadedVideo.data.jobId,
        );

        blob = jobStatus.data.jobStatus.blob;
      }

      await bsky.feed.post(
        text: 'Test video',
        embed: Embed.video(data: EmbedVideo(video: blob)),
      );
    }
hidea commented 2 days ago

Thank you for your advice! I changed the code so that the service argument is not passed in fromSession, as you suggested, and now the video upload is successful.

In the previous example, I excluded the exception that occurred before getJobStatus, but I have already dealt with getJobStatus!