fix: media start events fired too early when replaying a just-stopped sound (#9611)

#### Brief overview of PR changes/additions
- Since #9569 deferred the stop cleanup, replaying the file a player
just finished starts it synchronously, raising sysMediaStarted inside
the playSoundFile()/playMusicFile() call itself instead of
asynchronously as before
- Clear the reused player's stale source at claim time in
TMedia::play(), so every fresh play request loads asynchronously again;
the #9569 looping fix is untouched (TMediaLoopTest still passes 6/6, 0
skipped)

#### Motivation for adding to Mudlet
Unbreaks CI for all open PRs: the 6 Media_spec.lua failures on ubuntu
and windows64 (waitForEvent armed after the call misses the
now-synchronous event) come from this.

#### Other info
Root cause is #9569 (92f01b850) merged an hour before the media specs;
their CI runs never overlapped. Assisted-by: Claude:claude-fable-5

**Test case:** busted Media_spec.lua - 6 failures on current
development, 54/0/0 with this fix (full suite 1801/0/0).
This commit is contained in:
Vadim Peretokin 2026-08-03 06:24:40 +02:00 committed by GitHub
parent 102a08fc51
commit 3474cb58dc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 22 additions and 0 deletions

View file

@ -588,6 +588,25 @@ int TMedia::playersHoldingSource() const
+ countHeld(mAPIVideoList);
}
// The cleanup that releases a stopped player's source runs one event-loop turn
// after the stop (see handlePlayerPlaybackStateChanged), so a player reused for
// a new play request can still hold the file it just finished. Replaying that
// same file leaves QMediaPlayer with its media already loaded, so play() starts
// it synchronously, raising sysMediaStarted re-entrantly inside the script call
// that started it. Finish the deferred cleanup here instead, so every fresh
// play request loads its media asynchronously, as it did when the cleanup ran
// at stop time.
void TMedia::releaseStoppedSource(const std::shared_ptr<TMediaPlayer>& player)
{
if (!player || !player->mediaPlayer()) {
return;
}
if (player->getPlaybackState() == QMediaPlayer::StoppedState && !player->mediaPlayer()->source().isEmpty()) {
player->mediaPlayer()->setSource(QUrl());
}
}
void TMedia::setMediaPlayersMuted(const TMediaData::MediaProtocol mediaProtocol, const bool state)
{
TMediaData mediaData{};
@ -1658,6 +1677,7 @@ void TMedia::play(TMediaData& mediaData)
}
const QUrl mediaSource = mediaData.mediaInput() == TMediaData::MediaInputFile ? QUrl::fromLocalFile(absolutePathFileName) : QUrl(absolutePathFileName);
releaseStoppedSource(pPlayer);
pPlayer->noteClaimed();
pPlayer->mediaPlayer()->setSource(mediaSource);
} else {
@ -1725,6 +1745,7 @@ void TMedia::play(TMediaData& mediaData)
playlist->setCurrentIndex(0);
pPlayer->setPlaylist(playlist);
releaseStoppedSource(pPlayer);
pPlayer->noteClaimed();
pPlayer->mediaPlayer()->setSource(playlist->currentMedia());
}

View file

@ -183,6 +183,7 @@ private:
bool isMediaMatch(const std::shared_ptr<TMediaPlayer>& player, const TMediaData& mediaData);
bool resume(TMediaData mediaData);
void setMediaPlayersMuted(const TMediaData::MediaProtocol mediaProtocol, const bool state);
static void releaseStoppedSource(const std::shared_ptr<TMediaPlayer>& player);
void transitionNonRelativeFile(TMediaData& mediaData);
QString getStreamUrl(const TMediaData& mediaData);
QUrl parseUrl(TMediaData& mediaData);