foxygit / Torlinkc Log in
commits tags

/include/torlinkc/engine/queue.hpp · 4.67 KB

raw
#pragma once

#include <cstdint>
#include <functional>
#include <optional>
#include <string>
#include <unordered_map>
#include <vector>

#include "torlinkc/engine/history.hpp"
#include "torlinkc/engine/persist.hpp"
#include "torlinkc/engine/torrent_engine.hpp"
#include "torlinkc/engine/types.hpp"

namespace torlinkc {

struct AddInput {
  std::string id;
  std::string name;
  std::string magnet;
  std::string source;  // empty = none
  std::optional<std::int64_t> sizeBytes;
};

struct RestoreOptions {
  // Safe mode: the previous boot died while restoring (see bootguard.hpp), so
  // bring every item back paused and start no engines. The list stays intact
  // and visible; the user resumes each item on their own terms.
  bool safe = false;
};

// A real seed never pulls data off the network: verifying on-disk files reads
// the disk (network speed stays 0), only fetching *missing* data raises it.
// So sustained network download on a "seed" means its files are gone or
// partial. Ported from queue.ts's strayDownload().
bool strayDownload(std::int64_t total, double progress, int speed);

// Ported from download/queue.ts. Where the original relied on webtorrent's
// async events firing into the queue via Node's event loop, this Phase 1
// port is driven by an explicit tick(): it drains libtorrent alerts and
// refreshes every item/seed's live stats. Call tick() periodically (the
// console harness does so once a second) -- Phase 2 replaces this with a
// background alert thread posting closures onto the UI thread instead.
class DownloadQueue {
 public:
  explicit DownloadQueue(std::optional<int> maxDownloads = std::nullopt);

  // Extra announce URLs appended to every torrent added from now on. Existing
  // running torrents aren't retro-updated -- the change takes effect for the
  // next add / resume / re-seed.
  void setTrackers(std::vector<std::string> trackers);

  std::vector<QueueItem> getItems() const;
  int activeCount() const;
  bool has(const std::string& id) const;

  void add(const AddInput& input, const std::string& dir);

  // Advances the engine: drains alerts (metadata/done/error) and refreshes
  // progress for every downloading item and seeding seed. Call periodically.
  void tick();

  void pause(const std::string& id);
  void resume(const std::string& id);
  void togglePause(const std::string& id);

  std::optional<std::string> exportTorrentFile(const std::string& id);

  // Fetches the .torrent metadata for a magnet-only result and exports it to
  // exportDir. Blocking (bounded by a timeout), driven by repeatedly polling
  // the engine -- Phase 1 has no background thread to await this on.
  std::optional<std::string> fetchAndExportTorrent(const AddInput& input, const std::string& exportDir);

  void cancel(const std::string& id);
  bool remove(const std::string& id, bool deleteFiles = false);
  void retry(const std::string& id);
  void retryFailed();

  std::optional<SeedItem> getSeed(const std::string& id) const;
  std::vector<SeedItem> getSeeds() const;
  int seedingCount() const;

  void startSeeding(const HistoryItem& h);
  void stopSeeding(const std::string& id);
  void toggleSeeding(const HistoryItem& h);
  void restoreSeeds(const std::vector<SeedRecord>& records, RestoreOptions opts = {});

  void restore(std::vector<QueueItem> items, RestoreOptions opts = {});
  void restoreHistory(std::vector<HistoryItem> items);
  std::vector<HistoryItem> getHistory() const;
  void removeHistory(const std::string& id);
  void clearHistory();

  // Synchronously flushes every state file from current memory. Touches no
  // engine state, so it can never block shutdown.
  void persistSync();
  void suspend();

  // Fired synchronously wherever the TS original called `this.emit(...)`.
  // Unset by default (Phase 1's console harness polls getItems() directly);
  // Phase 2 wires these into the FTXUI redraw pipeline.
  std::function<void()> onUpdate;
  std::function<void(const std::string&)> onCompleted;

 private:
  std::unordered_map<std::string, QueueItem> items_;
  TorrentEngine engine_;
  std::vector<HistoryItem> history_;
  std::unordered_map<std::string, SeedItem> seeds_;
  std::unordered_map<std::string, int> strayHits_;
  std::unordered_map<std::string, std::int64_t> seedStartedAt_;
  std::vector<std::string> trackers_;
  int maxDownloads_ = 0;  // 0 = unlimited

  void startEngine(QueueItem& item);
  void promote();
  AddHandlers engineHandlers(std::string id);
  void completeItem(QueueItem it);
  void beginSeed(const QueueItem& it);
  void restorePaused(const HistoryItem& h);
  std::vector<SeedRecord> seedRecords() const;
  void persistSeeds() const;
  void persistQueue() const;
  void recordHistory(const QueueItem& it);
  void changed();
};

}  // namespace torlinkc