#pragma once #include // for size_t #include #include #include #include #include "BellTask.h" #include "PlaybackState.h" #include "TrackReference.h" #include "protobuf/metadata.pb.h" // for Track, _Track, AudioFile, Episode namespace bell { class WrappedSemaphore; }; namespace cspot { struct Context; class AccessKeyFetcher; class CDNAudioFile; // Used in got track info event struct TrackInfo { std::string name, album, artist, imageUrl, trackId; uint32_t duration; void loadPbTrack(Track* pbTrack, const std::vector& gid); void loadPbEpisode(Episode* pbEpisode, const std::vector& gid); }; class QueuedTrack { public: QueuedTrack(TrackReference& ref, std::shared_ptr ctx, uint32_t requestedPosition = 0); ~QueuedTrack(); enum class State { QUEUED, PENDING_META, KEY_REQUIRED, PENDING_KEY, CDN_REQUIRED, READY, FAILED }; std::shared_ptr loadedSemaphore; State state = State::QUEUED; // Current state of the track TrackReference ref; // Holds GID, URI and Context TrackInfo trackInfo; // Full track information fetched from spotify, name etc uint32_t requestedPosition; std::string identifier; // Will return nullptr if the track is not ready std::shared_ptr getAudioFile(); // --- Steps --- void stepLoadMetadata( Track* pbTrack, Episode* pbEpisode, std::mutex& trackListMutex, std::shared_ptr updateSemaphore); void stepParseMetadata(Track* pbTrack, Episode* pbEpisode); void stepLoadAudioFile( std::mutex& trackListMutex, std::shared_ptr updateSemaphore); void stepLoadCDNUrl(const std::string& accessKey); void expire(); private: std::shared_ptr ctx; uint64_t pendingMercuryRequest = 0; uint32_t pendingAudioKeyRequest = 0; std::vector trackId, fileId, audioKey; std::string cdnUrl; }; class TrackQueue : public bell::Task { public: TrackQueue(std::shared_ptr ctx, std::shared_ptr playbackState); ~TrackQueue(); enum class SkipDirection { NEXT, PREV }; std::shared_ptr playableSemaphore; std::atomic notifyPending = false; void runTask() override; void stopTask(); bool hasTracks(); bool isFinished(); bool skipTrack(SkipDirection dir, bool expectNotify = true); void updateTracks(uint32_t requestedPosition = 0, bool initial = false); TrackInfo getTrackInfo(std::string_view identifier); std::shared_ptr consumeTrack( std::shared_ptr prevSong, int& offset); private: static const int MAX_TRACKS_PRELOAD = 3; std::shared_ptr accessKeyFetcher; std::shared_ptr playbackState; std::shared_ptr ctx; std::shared_ptr processSemaphore; std::deque> preloadedTracks; std::vector currentTracks; std::mutex tracksMutex, runningMutex; // PB data Track pbTrack; Episode pbEpisode; std::string accessKey; int16_t currentTracksIndex = -1; bool isRunning = false; void processTrack(std::shared_ptr track); bool queueNextTrack(int offset = 0, uint32_t positionMs = 0); }; } // namespace cspot