Search code examples
flutteraudio-service

Can we retrieve the same updated media item with added Duration in Dart [Flutter] which is derived from Cloud Firestore?


I have problem with [audio_service][1] of Dart's package inside Flutter. It has trouble in reading the duration of current MediaItem. As written in its official documentation [here][2], we are asked to create a new MediaItem to store the copied duration. If we don't do it, then we won't be able to maintain the progress bar when [audio_service][1] is playing the song. I meant, if it has no duration value of the song, it will break the progress bar if user tap on Pause button or Stop button.

Now, I thought another alternative in order to keep duration data. I used [flutter_ffmpeg][3], and one of its package to read duration in String. Then use these codes to convert String into Duration:

Future<MediaItem> addDuration(File myAudio) async {
    late final MediaItem songInfo;
    String duration = "";
    await readAudio(myAudio).then((value) => duration = value);
    durSong = parseDuration(duration);
    songInfo.copyWith(duration: durSong);
    log("Duration is $durSong");
    log("The current duration for ${songInfo.title} is ${songInfo.duration}");
    return songInfo;
  }

Future<String> readAudio(File audio) async {
    final FlutterFFprobe durReader = FlutterFFprobe();
    String duration = "";
    await durReader.getMediaInformation(audio.path).then((value) => duration = value.getMediaProperties()!["duration"]);
    return duration;
  }

Duration parseDuration(String s) {
    int hours = 0;
    int minutes = 0;
    int micros;
    List<String> parts = s.split(':');
    if (parts.length > 2) {
      hours = int.parse(parts[parts.length - 3]);
    }
    if (parts.length > 1) {
      minutes = int.parse(parts[parts.length - 2]);
    }
    micros = (double.parse(parts[parts.length - 1]) * 1000000).round();
    return Duration(hours: hours, minutes: minutes, microseconds: micros);
  }

My use case is I created server app and client app. Server app is used to process, retrieve the duration value using plugin flutter_ffmpeg and store it in MediaItem and then upload it to Cloud Firestore and Storage. Client app is used to retrieve data from Cloud Firestore and Storage.

I updated the media item according to [official documentation][2]. I intended to use the updated songs so I don't need to add duration again to current media item.

My question is:
Can the data included inside media item have the same duration value after being downloaded from Cloud Firestore? I meant, do we need to add Duration value again in the same song??? Or the Duration value is already stored within the song?

[1]: https://pub.dev/packages/audio_service
[2]: https://github.com/ryanheise/audio_service/wiki/FAQ#how-do-i-update-an-existing-mediaitem
[3]: https://pub.dev/packages/flutter_ffmpeg


Solution

  • I found out where I went wrong. Actually, we just need to add these code inside init() function:

        _player.durationStream.listen((duration) {
          var index = _player.currentIndex;
          if (index != null && duration != null) {
            final newQueue = queue.value;
            final oldMediaItem = newQueue[index];
            final newMediaItem = oldMediaItem.copyWith(duration: duration);
            newQueue[index] = newMediaItem;
            mediaItem.add(newMediaItem);
          }
        });
    I should also add these inside function setShuffleMode()

    @override
    Future<void> setShuffleMode(AudioServiceShuffleMode shuffleMode) async {
       final enabled = shuffleMode == AudioServiceShuffleMode.all;
       if (enabled) {
         await _player.shuffle();
         await _player.setShuffleModeEnabled(true);
         _player.durationStream.listen((duration) {
           var index = _player.currentIndex;
           if (index != null && duration != null) {
             final newQueue = queue.value;
             final oldMediaItem = newQueue[index];
             final newMediaItem = oldMediaItem.copyWith(duration: duration);
             newQueue[index] = newMediaItem;
             mediaItem.add(newMediaItem);
             playbackState.add(playbackState.value.copyWith(
               shuffleMode: shuffleMode,
               updatePosition: _player.position,
               bufferedPosition: _player.bufferedPosition,
             ));
           }
         });
       } else {
         shuffleMode = AudioServiceShuffleMode.none;
         await _player.setShuffleModeEnabled(false);
         _player.durationStream.listen((duration) {
           var index = _player.currentIndex;
           if (index != null && duration != null) {
             final newQueue = queue.value;
             final oldMediaItem = newQueue[index];
             final newMediaItem = oldMediaItem.copyWith(duration: duration);
             newQueue[index] = newMediaItem;
             mediaItem.add(newMediaItem);
             playbackState.add(playbackState.value.copyWith(
               shuffleMode: shuffleMode,
               updatePosition: _player.position,
               bufferedPosition: _player.bufferedPosition,
             ));
           }
         });
       }
     }

    I don't need to read the duration value using library flutter_ffmpeg, I just need to add that code inside init() function and replace the original function setShuffleMode. I think I have to erase my below comment for alternative way which is so much harder than this answer. Well, thank you anyway.