jofr / capacitor-media-session

Capacitor plugin for media notifications and platform media keys as well as background audio playback.
GNU General Public License v3.0
36 stars 25 forks source link

Update to Capacitor 6.0 #27

Closed Christoffyw closed 2 months ago

Christoffyw commented 5 months ago

Nope it was not, It was so I could get it on npm but I didn’t mean to commit it.On May 27, 2024, at 2:11 PM, mustafa0x @.***> wrote: @mustafa0x commented on this pull request.

In package.json:

@@ -1,6 +1,6 @@ {

  • "name": @.***/capacitor-media-session",
  • "version": "3.0.3",
  • "name": @.***/capacitor-media-session",

I don't think this change was intended. Same for the URL changes below.

—Reply to this email directly, view it on GitHub, or unsubscribe.You are receiving this because you authored the thread.Message ID: @.***>

Christoffyw commented 5 months ago

Sorry about that. I've reverted the changes.

jeremymouton commented 3 months ago

Can we get this merged, please?

foerster commented 3 months ago

Can we get this merged, please?

+1 I need this to update my app to capacitor 6.

webmasterab commented 3 months ago

Can we get this merged, please?

+1 I need this to update my app to capacitor 6.

I also need this to upgrade to 6

end3r-man commented 3 months ago

+1

webmasterab commented 3 months ago

@jofr Unfortunately there is no update yet. August 31st is fast approaching. Is it possible to release an update? This would save us a lot of work.

foerster commented 3 months ago

@jofr Unfortunately there is no update yet. August 31st is fast approaching. Is it possible to release an update? This would save us a lot of work.

I pushed a release containing this PR on a different package because I needed this also. Feel free to use until there's a real fix.

https://www.npmjs.com/package/@erafx-tech/capacitor-media-session-capacitor6

webmasterab commented 3 months ago

@jofr Unfortunately there is no update yet. August 31st is fast approaching. Is it possible to release an update? This would save us a lot of work.

I pushed a release containing this PR on a different package because I needed this also. Feel free to use until there's a real fix.

https://www.npmjs.com/package/@erafx-tech/capacitor-media-session-capacitor6

How can I install this to use it for the time being?

foerster commented 3 months ago

npm install @@.***/capacitor-media-session-capacitor6>

Then in your code, change the import from

@jofr/capacitor-media-session

To

@erafx-tech/capacitor-media-session-capacitor6

On Jul 28, 2024, at 3:42 PM, webmasterab @.***> wrote:



@jofrhttps://github.com/jofr Unfortunately there is no update yet. August 31st is fast approaching. Is it possible to release an update? This would save us a lot of work.

I pushed a release containing this PR on a different package because I needed this also. Feel free to use until there's a real fix.

@.***/capacitor-media-session-capacitor6

How can I install this to use it for the time being?

— Reply to this email directly, view it on GitHubhttps://github.com/jofr/capacitor-media-session/pull/27#issuecomment-2254623763, or unsubscribehttps://github.com/notifications/unsubscribe-auth/AACTXQHPTVB5RXD7KMGOVFDZOVCQPAVCNFSM6AAAAABGQST6CKVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDENJUGYZDGNZWGM. You are receiving this because you commented.Message ID: @.***>

CAUTION: This email originated from outside of the organization. Do not click links or open attachments unless you recognize the sender and know the content is safe.

webmasterab commented 3 months ago

@foerster I get this message when I want to do the installation.

npm error Could not resolve dependency: npm error peer @capacitor/core@"^6.0.0" from @erafx-tech/capacitor-media-session-capacitor6@4.0.0 @erafx-tech/capacitor-media-session-capacitor6@"*" from the root project

end3r-man commented 3 months ago

@jofr Unfortunately there is no update yet. August 31st is fast approaching. Is it possible to release an update? This would save us a lot of work.

I pushed a release containing this PR on a different package because I needed this also. Feel free to use until there's a real fix. https://www.npmjs.com/package/@erafx-tech/capacitor-media-session-capacitor6

How can I install this to use it for the time being?

working

webmasterab commented 3 months ago

working

I can't get it installed. My capacitor is 5.2

end3r-man commented 3 months ago

working

I can't get it installed. My capacitor is 5.2

im using capacitor 6.1 but ucan use the current version for capacitor 5

end3r-man commented 3 months ago

working

I can't get it installed. My capacitor is 5.2

https://github.com/jofr/capacitor-media-session?tab=readme-ov-file#install

webmasterab commented 3 months ago

@erafx-tech/capacitor-media-session-capacitor6@4.0.0 I can no longer install the original one from @jofr, which worked fine. That is strange if it is a forked version and there are only changes for update to cpacitor 6 I have now installed this version but the player now crashes every time. So unfortunately I can't use this. Pity

The app closes when I press play

foerster commented 3 months ago

@erafx-tech/capacitor-media-session-capacitor6@4.0.0 I can no longer install the original one from @jofr, which worked fine. That is strange if it is a forked version and there are only changes for update to cpacitor 6 I have now installed this version but the player now crashes every time. So unfortunately I can't use this. Pity

The app closes when I press play

No clue what you are trying to do, as the original plugins works for cap5. This fork was simply to apply this PR and make it work for cap6.

webmasterab commented 3 months ago

@foerster I'm trying to start with the play() function. But that is no longer the case. The app then crashes

foerster commented 3 months ago

@foerster I'm trying to start with the play() function. But that is no longer the case. The app then crashes

Sorry, I can't help you. I have nothing to do with this project other than I applied this PR and published a temporary build since the author/maintainer hasn't.

webmasterab commented 3 months ago

I solved my problem.

It was in a play and break listener somewhere that I didn't need.

I went through my code step by step and then I discovered this

webmasterab commented 3 months ago

Unfortunately, the app still freezes on this part

const playbackS = this.playbackStopped ? 'none' : (this.audio.paused ? 'paused' : 'playing');

MediaSession.setPlaybackState({
  playbackState: playbackS
});

If I disable this the app works fine.

Am I doing something wrong here?

It should prompt you to show the player when you pull down the top on your phone or on the lock screen.

That worked but not anymore now that it is Capacitor 6.

end3r-man commented 3 months ago

Unfortunately, the app still freezes on this part

const playbackS = this.playbackStopped ? 'none' : (this.audio.paused ? 'paused' : 'playing');

MediaSession.setPlaybackState({
  playbackState: playbackS
});

If I disable this the app works fine.

Am I doing something wrong here?

It should prompt you to show the player when you pull down the top on your phone or on the lock screen.

That worked but not anymore now that it is Capacitor 6.

trying to update the media session right?

webmasterab commented 3 months ago

This is at the start of an item.

It used to work, but not anymore since the update.

I looked and saw that it should be possible via this option this.audio.addEventListener('play', () =>{

  MediaSession.setPlaybackState({
    playbackState: 'playing'
  });

 });

But this doesn't work. I disable this part // MediaSession.setPlaybackState({ //playbackState: 'playing' // }); Then the listener will work

Very strange.

jofr commented 2 months ago

Sorry for the delay, didn't have much time for the project. Version for capacitor 6 is now published as plugin version 4.0.0, I'll try to look at the other issues in the next few days.

webmasterab commented 2 months ago

Sorry for the delay, didn't have much time for the project. Version for capacitor 6 is now published as plugin version 4.0.0, I'll try to look at the other issues in the next few days.

Glad you have time for it now.

The strange thing is since the plugin is now running in capacitor 6 and you call the part for the updatePlaybackState() {

const playbackState = this.playbackStopped ? 'none' : (this.audio.paused ? 'paused' : 'playing');
MediaSession.setPlaybackState({
    playbackState: playbackState
});

}

Then the app freezes and stops.

Christoffyw commented 2 months ago

@webmasterab This seems like an issue with your own implementation and not related to the plugin

webmasterab commented 2 months ago

@webmasterab This seems like an issue with your own implementation and not related to the plugin

That could very well be. Only I can't find where things are going wrong. Have been looking for more than two weeks to find out where things went wrong.

Christoffyw commented 2 months ago

@webmasterab This seems like an issue with your own implementation and not related to the plugin

That could very well be.

Only I can't find where things are going wrong.

Have been looking for more than two weeks to find out where things went wrong.

Does it freeze when using the built-in browser MediaSession API?

webmasterab commented 2 months ago

This is the code I have. This is loaded every time I start a stream or other item. It is not possible to stop the event listeners. It worked like this in capacitor 5 and now it doesn't.

I can't find it.

I think I have to rebuild the whole thing and then figure out step by step where it goes wrong.

The audio is linked to this.audio which is linked in the constructor of this service this.audio = new Audio();

const updatePositionState = () => {

MediaSession.setPositionState({ position: this.audio.currentTime, duration: this.audio.duration, playbackRate: this.audio.playbackRate }); }

this.audio.addEventListener('durationchange', updatePositionState()); this.audio.addEventListener('seeked', updatePositionState()); this.audio.addEventListener('ratechange', updatePositionState()); // this.audio.addEventListener('play', updatePositionState()); // this.audio.addEventListener('pause', updatePositionState());

this.audio.addEventListener('play', () => { this.playbackStopped = false; updatePositionState(); this.updatePlaybackState(); MediaSession.setMetadata({ title: 'title', artist: 'artist' }); });

this.audio.addEventListener('pause', () => { updatePositionState(); this.updatePlaybackState() });

MediaSession.setActionHandler({ action: 'play' }, () => { this.audio.play(); if (this.router.url === '/tabs/player') { this.streamPaused = true; } });

MediaSession.setActionHandler({ action: 'pause' }, () => { this.audio.pause(); this.pause = true; this.play = true; });

MediaSession.setActionHandler({ action: 'seekto' }, (details: any) => { this.audio.currentTime = details.seekTime; // const seekto = this.audio.duration * (details.seekTime / 100); // this.audio.currentTime = seekto; this.timePlaying = this.readableDuration(details.seekTime); });

MediaSession.setActionHandler({ action: 'seekforward' }, (details) => { // const seekOffset = details.seekTime ?? 30; this.audio.currentTime = this.audio.currentTime + details.seekTime ?? 30;; });

MediaSession.setActionHandler({ action: 'seekbackward' }, (details) => { // const seekOffset = details.seekTime ?? 30; this.audio.currentTime = this.audio.currentTime - 30; });

MediaSession.setActionHandler({ action: 'stop' }, () => { this.playbackStopped = true; this.audio.pause(); this.play = false; this.pause = false; if (this.router.url === '/tabs/player') { this.streamPaused = false; } if (this.noStreamSeconds > 0) { this.noStreamSeconds = 0; } if (this.route r.url === '/tabs/broadcast') { this.disabled = null; this.playerLink = null; } this.streamIsPlaying = false; this.broadcastIsPlaying = false;

webmasterab commented 2 months ago

Does it freeze when using the built-in browser MediaSession API? Yes it crashes when I use the MediaSession. If I turn that off it works fine

foerster commented 2 months ago

This is what I have -- it's ugly -- I ran into various weird issues overall trying to get things working originally. I got it working and never looked back.

import { DataService } from 'src/app/services/data.service';
import {
  Component,
  ElementRef,
  Input,
  OnDestroy,
  OnInit,
  ViewChild,
} from '@angular/core';
import { ModalController, Platform, RangeCustomEvent } from '@ionic/angular';

// import {Media} from '@awesome-cordova-plugins/media/ngx';
// import { MediaSession} from '@jofr/capacitor-media-session';
import { MediaSession } from '@erafx-tech/capacitor-media-session-capacitor6';
import { AudioTrackWithUserProgress } from 'src/app/lib/collection.types';

@Component({
  selector: 'app-track-modal',
  templateUrl: './track-modal.component.html',
  styleUrls: ['./track-modal.component.scss'],
})
export class TrackModalComponent implements OnInit, OnDestroy {
  constructor(
    private modalController: ModalController,
    private dataService: DataService,
    public platform: Platform,
  ) {}

  // we have an audio element in the view with #audioElement. Get a reference to it
  // @ViewChild('audioElement') audioElementRef!: ElementRef;

  @Input() selectedTrack!: AudioTrackWithUserProgress;
  @Input() audioUrl!: string;
  @Input() programName: string = '';
  audio: HTMLAudioElement | null = null; // Updated here
  isLoading: boolean = true;
  isPlaying: boolean = false;
  progress: number = 0;
  progressPercent: number = 0;
  duration: number = 0;
  is_playing = false;

  current_file: any;
  async ngOnInit() {}

  async ionViewDidEnter() {
    console.log('selected track', { ...this.selectedTrack.user_progress });
    if (!this.selectedTrack.user_progress) {
      this.selectedTrack.user_progress = {
        is_complete: false,
        progress_percentage: 0,
        progress_seconds: 0,
        created_at: null,
        id: 0,
        program_id: null,
        track_id: null,
        updated_at: null,
        user_id: null,
      };
    }

    this.audio = new Audio(this.audioUrl);
    // TODO: do we need load here?
    // this.audio.load();

    // this.audio = this.audioElementRef.nativeElement;
    if (!this.audio) {
      console.error('audio element not found');
      return;
    }
    this.initMediaSession();

    this.audio.onloadeddata = async () => {
      console.log('loaded data');
      // this.initMediaSession();
      this.isLoading = false;
      if (this.audio) {
        this.duration = this.audio.duration;

        // this hack seems required to make iOS show our media session on lock screen
        await this.audio.play();
        this.audio.pause();
        // console.log(
        //   'playback started, set current time to',
        //   this.selectedTrack.user_progress?.progress_seconds || 0,
        // );
        // this.audio.currentTime =
        //   this.selectedTrack.user_progress?.progress_seconds || 0;

        await this.audio.play();

        this.isPlaying = true;
      }
    };

    this.audio.ontimeupdate = () => {
      if (this.audio) {
        // console.log(this.audio.currentTime);
        this.progressPercent = this.audio.currentTime / this.duration;
        this.progress = this.audio.currentTime;
        // this.selectedTrack.user_progress!.progress_seconds = this.audio.currentTime;
        if (!this.selectedTrack.user_progress?.is_complete) {
          // console.log('not completed yet');
          // if track is 50% complete, mark it as complete
          if (this.audio.currentTime / this.duration >= 0.5) {
            this.selectedTrack.user_progress!.is_complete = true;
            this.saveCurrentProgress();
          }
        }
        if (this.selectedTrack.user_progress?.is_complete) {
          if (this.audio.currentTime / this.duration < 0.25) {
            this.selectedTrack.user_progress!.is_complete = false;
            this.saveCurrentProgress();
          }
        }
      }
    };

    this.audio.onerror = (error) => {
      console.error('An error occurred:', error);
    };

    this.audio.load();
  }

  async ngOnDestroy() {
    console.log('TrackModal destroyed');
    if (this.audio) {
      this.audio.pause();
      await this.saveCurrentProgress();

      this.audio.onloadeddata = null; // remove event listener
      this.audio.ontimeupdate = null; // remove event listener
      this.audio.onerror = null; // remove event listener
      this.audio.src = ''; // reset src attribute
      this.audio = null;
      await MediaSession.setPlaybackState({
        playbackState: 'none',
      });
    }
    // await Playlist.pause();
  }

  togglePlay() {
    if (this.audio) {
      this.isPlaying ? this.audio.pause() : this.audio.play();
      this.isPlaying = !this.isPlaying;
    }
  }

  onSliderChangeEnd(change: Event) {
    if (this.audio) {
      console.log((change as RangeCustomEvent).detail.value);
      this.audio.currentTime = (change as RangeCustomEvent).detail
        .value as number;
      console.log('slider change end');
      this.saveCurrentProgress();
    }
  }

  closeTrack() {
    this.saveCurrentProgress();
    this.modalController.dismiss();
  }

  resume = false;
  onIonKnobMoveStart(event: any) {
    if (this.audio && this.isPlaying) {
      this.resume = true;
      this.audio.pause();
    }
  }

  async saveCurrentProgress() {
    if (this.audio) {
      this.selectedTrack.user_progress!.progress_seconds =
        this.audio.currentTime;
      this.dataService.updateUserTrackProgress(
        this.selectedTrack.id,
        this.selectedTrack.program_id || 0,
        this.audio.currentTime / this.duration,
        this.audio.currentTime,
      );
    }
  }
  onIonKnobMoveEnd(event: any) {
    if (this.audio && this.resume) {
      this.audio.play();
      this.resume = false;

      // this.saveCurrentProgress();
    }
  }
  playbackStopped = true;
  async initMediaSession() {
    // const audioElement = document.querySelector('audio');

    const updatePositionState = async () => {
      console.log('updatePositionState');
      await MediaSession.setPositionState({
        position: this.audio!.currentTime,
        duration: this.audio!.duration,
        playbackRate: this.audio!.playbackRate,
      });
    };

    this.audio!.addEventListener('durationchange', updatePositionState);
    this.audio!.addEventListener('seeked', updatePositionState);
    this.audio!.addEventListener('ratechange', updatePositionState);
    this.audio!.addEventListener('play', updatePositionState);
    this.audio!.addEventListener('pause', updatePositionState);

    const updatePlaybackState = async () => {
      const playbackState = this.playbackStopped
        ? 'none'
        : this.audio!.paused
        ? 'paused'
        : 'playing';

      if (playbackState === 'paused') {
        console.log('paused, saving progress');
        this.saveCurrentProgress();
      }
      console.log('media session playback state', playbackState);
      await MediaSession.setPlaybackState({
        playbackState: playbackState,
      });
    };

    this.audio!.addEventListener('play', async () => {
      console.log('play handler invoked');

      console.log('media session metadata', {
        title: this.selectedTrack.title,
        artist: 'Champion By Design',
        album: this.programName,
        artwork: [
          {
            src: './assets/logo.png',
            type: 'image/png',
            sizes: '512x512',
          },
        ],
      });

      // some oddity here.  For Android, we need to set the metadata twice.
      // If we put the updatePlaybackState() first here, then set metadata, it crashes.
      // If we put the set metadata first, then updatePlaybackState, android doesn't show the metadata.
      // So, we have to set metadata twice.  Set Metadata, updatePlaybackState, setMetadata again.
      await MediaSession.setMetadata({
        title: this.selectedTrack.title || '',
        artist: 'Champion By Design',
        album: this.programName,
        artwork: [
          {
            src: './assets/logo.png',
            type: 'image/png',
            sizes: '512x512',
          },
        ],
      });

      this.playbackStopped = false;
      await updatePlaybackState();

      if (this.platform.is('android')) {
        await MediaSession.setMetadata({
          title: this.selectedTrack.title || '',
          artist: 'Champion By Design',
          album: this.programName,
          artwork: [
            {
              src: './assets/logo.png',
              type: 'image/png',
              sizes: '512x512',
            },
          ],
        });
      }
    });
    this.audio!.addEventListener('pause', updatePlaybackState);

    await MediaSession.setActionHandler({ action: 'play' }, () => {
      this.audio!.play();
      this.isPlaying = true;
    });

    await MediaSession.setActionHandler({ action: 'pause' }, () => {
      this.audio!.pause();
      this.isPlaying = false;
      console.log('pause action handler invoked');
      this.saveCurrentProgress();
    });

    await MediaSession.setActionHandler({ action: 'seekto' }, (details) => {
      this.audio!.currentTime = details.seekTime ?? 0;
    });

    await MediaSession.setActionHandler(
      { action: 'seekforward' },
      (details) => {
        const seekOffset = details.seekTime ?? 10;
        this.audio!.currentTime = this.audio!.currentTime + seekOffset;
      },
    );

    await MediaSession.setActionHandler(
      { action: 'seekbackward' },
      (details) => {
        const seekOffset = details.seekTime ?? 10;
        this.audio!.currentTime = this.audio!.currentTime - seekOffset;
      },
    );

    await MediaSession.setActionHandler({ action: 'stop' }, () => {
      this.playbackStopped = true;
      this.audio!.pause();
      this.isPlaying = false;
      console.log('stop action handler invoked');
      this.saveCurrentProgress();
    });
  }
}
webmasterab commented 2 months ago

@webmasterabDit lijkt een probleem te zijn met uw eigen implementatie en niet gerelateerd aan de plugin.

Dat zou best kunnen. Alleen kan ik niet vinden waar het fout gaat. Ik ben al meer dan twee weken op zoek naar de oorzaak van het probleem.

Loopt het vast als ik de ingebouwde MediaSession API van de browser gebruik?

I now have it as follows.

It's really stuck on this part now.

 updatePlaybackState() {
    console.log('loaded 2');

    const playbackState = this.playbackStopped ? 'none' : (this.audio.paused ? 'paused' : 'playing');
    console.log("🚀 ~ ServiceService ~ updatePlaybackState ~ playbackState:", playbackState)
    MediaSession.setPlaybackState({
      playbackState: 'playing'
    });
  }

See also the appendix of the check. The playback result was not found

mediasession playbackstate

end3r-man commented 5 days ago

@webmasterabDit lijkt een probleem te zijn met uw eigen implementatie en niet gerelateerd aan de plugin.

Dat zou best kunnen. Alleen kan ik niet vinden waar het fout gaat. Ik ben al meer dan twee weken op zoek naar de oorzaak van het probleem.

Loopt het vast als ik de ingebouwde MediaSession API van de browser gebruik?

I now have it as follows.

It's really stuck on this part now.

 updatePlaybackState() {
    console.log('loaded 2');

    const playbackState = this.playbackStopped ? 'none' : (this.audio.paused ? 'paused' : 'playing');
    console.log("🚀 ~ ServiceService ~ updatePlaybackState ~ playbackState:", playbackState)
    MediaSession.setPlaybackState({
      playbackState: 'playing'
    });
  }

See also the appendix of the check. The playback result was not found

mediasession playbackstate

usw await