| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133 | #pragma once#include <stddef.h>  // for size_t#include <atomic>#include <deque>#include <functional>#include <mutex>#include "BellTask.h"#include "PlaybackState.h"#include "TrackReference.h"#include "protobuf/metadata.pb.h"  // for Track, _Track, AudioFile, Episodenamespace bell {class WrappedSemaphore;};namespace cspot {struct Context;class AccessKeyFetcher;class CDNAudioFile;// Used in got track info eventstruct TrackInfo {  std::string name, album, artist, imageUrl, trackId;  uint32_t duration;  void loadPbTrack(Track* pbTrack, const std::vector<uint8_t>& gid);  void loadPbEpisode(Episode* pbEpisode, const std::vector<uint8_t>& gid);};class QueuedTrack { public:  QueuedTrack(TrackReference& ref, std::shared_ptr<cspot::Context> ctx,              uint32_t requestedPosition = 0);  ~QueuedTrack();  enum class State {    QUEUED,    PENDING_META,    KEY_REQUIRED,    PENDING_KEY,    CDN_REQUIRED,    READY,    FAILED  };  std::shared_ptr<bell::WrappedSemaphore> 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<cspot::CDNAudioFile> getAudioFile();  // --- Steps ---  void stepLoadMetadata(      Track* pbTrack, Episode* pbEpisode, std::mutex& trackListMutex,      std::shared_ptr<bell::WrappedSemaphore> updateSemaphore);  void stepParseMetadata(Track* pbTrack, Episode* pbEpisode);  void stepLoadAudioFile(      std::mutex& trackListMutex,      std::shared_ptr<bell::WrappedSemaphore> updateSemaphore);  void stepLoadCDNUrl(const std::string& accessKey);  void expire(); private:  std::shared_ptr<cspot::Context> ctx;  uint64_t pendingMercuryRequest = 0;  uint32_t pendingAudioKeyRequest = 0;  std::vector<uint8_t> trackId, fileId, audioKey;  std::string cdnUrl;};class TrackQueue : public bell::Task { public:  TrackQueue(std::shared_ptr<cspot::Context> ctx,             std::shared_ptr<cspot::PlaybackState> playbackState);  ~TrackQueue();  enum class SkipDirection { NEXT, PREV };  std::shared_ptr<bell::WrappedSemaphore> playableSemaphore;  std::atomic<bool> 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<QueuedTrack> consumeTrack(      std::shared_ptr<QueuedTrack> prevSong, int& offset); private:  static const int MAX_TRACKS_PRELOAD = 3;  std::shared_ptr<cspot::AccessKeyFetcher> accessKeyFetcher;  std::shared_ptr<PlaybackState> playbackState;  std::shared_ptr<cspot::Context> ctx;  std::shared_ptr<bell::WrappedSemaphore> processSemaphore;  std::deque<std::shared_ptr<QueuedTrack>> preloadedTracks;  std::vector<TrackReference> 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<QueuedTrack> track);  bool queueNextTrack(int offset = 0, uint32_t positionMs = 0);};}  // namespace cspot
 |