123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134 |
- #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, 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, number, discNumber;
- 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;
- bool loading = false;
- // 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);
- bool 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
|