chromium/google_apis/youtube_music/youtube_music_api_request_types.cc

// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "google_apis/youtube_music/youtube_music_api_request_types.h"

#include <string>

#include "base/check.h"
#include "base/i18n/time_formatting.h"
#include "base/json/json_writer.h"
#include "base/strings/string_number_conversions.h"
#include "base/time/time.h"
#include "base/values.h"

namespace google_apis::youtube_music {

namespace {

constexpr char kPlayableIdKey[] = "playableId";

constexpr char kExplicitFilterKey[] = "explicitFilter";
constexpr char kExplicitFilterNone[] = "none";
constexpr char kExplicitFilterBestEffort[] = "besteffort";

constexpr char kShuffleModeKey[] = "shuffleMode";
constexpr char kShuffleModeUnspecified[] = "SHUFFLE_MODE_UNSPECIFIED";
constexpr char kShuffleModeOff[] = "OFF";
constexpr char kShuffleModeOn[] = "ON";

constexpr char kPlaybackReportingTokenKey[] = "playbackReportingToken";
constexpr char kClientCurrentTimeKey[] = "clientCurrentTime";
constexpr char kPlaybackStartOffsetKey[] = "playbackStartOffset";
constexpr char kMediaTimeCurrentKey[] = "mediaTimeCurrent";
constexpr char kPlaybackStartDataKey[] = "playbackStartData";

constexpr char kConnectionTypeKey[] = "connectionType";
constexpr char kConnectionTypeUnspecified[] = "CONNECTION_TYPE_UNSPECIFIED";
constexpr char kConnectionTypeActiveUncategorized[] =
    "CONNECTION_TYPE_ACTIVE_UNCATEGORIZED";
constexpr char kConnectionTypeNone[] = "CONNECTION_TYPE_NONE";
constexpr char kConnectionTypeWifi[] = "CONNECTION_TYPE_WIFI";
constexpr char kConnectionTypeCellular2g[] = "CONNECTION_TYPE_CELLULAR_2G";
constexpr char kConnectionTypeCellular3g[] = "CONNECTION_TYPE_CELLULAR_3G";
constexpr char kConnectionTypeCellular4g[] = "CONNECTION_TYPE_CELLULAR_4G";
constexpr char kConnectionTypeCellularUnknown[] =
    "CONNECTION_TYPE_CELLULAR_UNKNOWN";
constexpr char kConnectionTypeDisco[] = "CONNECTION_TYPE_DISCO";
constexpr char kConnectionTypeWifiMetered[] = "CONNECTION_TYPE_WIFI_METERED";
constexpr char kConnectionTypeCellular5gSa[] = "CONNECTION_TYPE_CELLULAR_5G_SA";
constexpr char kConnectionTypeCellular5gNsa[] =
    "CONNECTION_TYPE_CELLULAR_5G_NSA";
constexpr char kConnectionTypeWired[] = "CONNECTION_TYPE_WIRED";
constexpr char kConnectionTypeInvalid[] = "CONNECTION_TYPE_INVALID";

constexpr char kWatchTimeSegmentsKey[] = "watchTimeSegments";
constexpr char kMediaTimeStartKey[] = "mediaTimeStart";
constexpr char kMediaTimeEndKey[] = "mediaTimeEnd";
constexpr char kClientStartTimeKey[] = "clientStartTime";

constexpr char kPlaybackStateKey[] = "playbackState";
constexpr char kPlaybackStateUnspecified[] = "PLAYBACK_STATE_UNSPECIFIED";
constexpr char kPlaybackStatePlaying[] = "PLAYBACK_STATE_PLAYING";
constexpr char kPlaybackStatePaused[] = "PLAYBACK_STATE_PAUSED";
constexpr char kPlaybackStateCompleted[] = "PLAYBACK_STATE_COMPLETED";

const char* GetExplicitFilterValue(
    const PlaybackQueuePrepareRequestPayload::ExplicitFilter& explicit_filter) {
  switch (explicit_filter) {
    case PlaybackQueuePrepareRequestPayload::ExplicitFilter::kNone:
      return kExplicitFilterNone;
    case PlaybackQueuePrepareRequestPayload::ExplicitFilter::kBestEffort:
      return kExplicitFilterBestEffort;
  }
}

const char* GetShuffleModeValue(
    const PlaybackQueuePrepareRequestPayload::ShuffleMode& shuffle_mode) {
  switch (shuffle_mode) {
    case PlaybackQueuePrepareRequestPayload::ShuffleMode::kUnspecified:
      return kShuffleModeUnspecified;
    case PlaybackQueuePrepareRequestPayload::ShuffleMode::kOff:
      return kShuffleModeOff;
    case PlaybackQueuePrepareRequestPayload::ShuffleMode::kOn:
      return kShuffleModeOn;
  }
}

std::string GetConnectionTypeValue(
    const ReportPlaybackRequestPayload::ConnectionType& connection_type) {
  switch (connection_type) {
    case ReportPlaybackRequestPayload::ConnectionType::kUnspecified:
      return kConnectionTypeUnspecified;
    case ReportPlaybackRequestPayload::ConnectionType::kActiveUncategorized:
      return kConnectionTypeActiveUncategorized;
    case ReportPlaybackRequestPayload::ConnectionType::kNone:
      return kConnectionTypeNone;
    case ReportPlaybackRequestPayload::ConnectionType::kWifi:
      return kConnectionTypeWifi;
    case ReportPlaybackRequestPayload::ConnectionType::kCellular2g:
      return kConnectionTypeCellular2g;
    case ReportPlaybackRequestPayload::ConnectionType::kCellular3g:
      return kConnectionTypeCellular3g;
    case ReportPlaybackRequestPayload::ConnectionType::kCellular4g:
      return kConnectionTypeCellular4g;
    case ReportPlaybackRequestPayload::ConnectionType::kCellularUnknown:
      return kConnectionTypeCellularUnknown;
    case ReportPlaybackRequestPayload::ConnectionType::kDisco:
      return kConnectionTypeDisco;
    case ReportPlaybackRequestPayload::ConnectionType::kWifiMetered:
      return kConnectionTypeWifiMetered;
    case ReportPlaybackRequestPayload::ConnectionType::kCellular5gSa:
      return kConnectionTypeCellular5gSa;
    case ReportPlaybackRequestPayload::ConnectionType::kCellular5gNsa:
      return kConnectionTypeCellular5gNsa;
    case ReportPlaybackRequestPayload::ConnectionType::kWired:
      return kConnectionTypeWired;
    case ReportPlaybackRequestPayload::ConnectionType::kInvalid:
      return kConnectionTypeInvalid;
  }
}

std::string GetPlaybackStateValue(
    const ReportPlaybackRequestPayload::PlaybackState& playback_state) {
  switch (playback_state) {
    case ReportPlaybackRequestPayload::PlaybackState::kUnspecified:
      return kPlaybackStateUnspecified;
    case ReportPlaybackRequestPayload::PlaybackState::kPlaying:
      return kPlaybackStatePlaying;
    case ReportPlaybackRequestPayload::PlaybackState::kPaused:
      return kPlaybackStatePaused;
    case ReportPlaybackRequestPayload::PlaybackState::kCompleted:
      return kPlaybackStateCompleted;
  }
}

std::string GetTimeDeltaString(const base::TimeDelta& time_delta) {
  return base::NumberToString(time_delta.InSeconds()) + "s";
}

}  // namespace

PlaybackQueuePrepareRequestPayload::PlaybackQueuePrepareRequestPayload(
    std::string playable_id,
    std::optional<ExplicitFilter> explicit_filter,
    std::optional<ShuffleMode> shuffle_mode)
    : playable_id(playable_id),
      explicit_filter(explicit_filter),
      shuffle_mode(shuffle_mode) {}
PlaybackQueuePrepareRequestPayload::PlaybackQueuePrepareRequestPayload(
    const PlaybackQueuePrepareRequestPayload&) = default;
PlaybackQueuePrepareRequestPayload&
PlaybackQueuePrepareRequestPayload::operator=(
    const PlaybackQueuePrepareRequestPayload&) = default;
PlaybackQueuePrepareRequestPayload::~PlaybackQueuePrepareRequestPayload() =
    default;

std::string PlaybackQueuePrepareRequestPayload::ToJson() const {
  base::Value::Dict root;

  CHECK(!playable_id.empty());
  root.Set(kPlayableIdKey, playable_id);
  if (explicit_filter.has_value()) {
    root.Set(kExplicitFilterKey,
             GetExplicitFilterValue(explicit_filter.value()));
  }
  if (shuffle_mode.has_value()) {
    root.Set(kShuffleModeKey, GetShuffleModeValue(shuffle_mode.value()));
  }

  const std::optional<std::string> json = base::WriteJson(root);
  CHECK(json);

  return json.value();
}

ReportPlaybackRequestPayload::Params::Params(
    const bool initial_report,
    const std::string& playback_reporting_token,
    const base::Time client_current_time,
    const base::TimeDelta playback_start_offset,
    const base::TimeDelta media_time_current,
    const ConnectionType connection_type,
    const PlaybackState playback_state,
    const std::vector<WatchTimeSegment>& watch_time_segments)
    : initial_report(initial_report),
      playback_reporting_token(playback_reporting_token),
      client_current_time(client_current_time),
      playback_start_offset(playback_start_offset),
      media_time_current(media_time_current),
      connection_type(connection_type),
      playback_state(playback_state),
      watch_time_segments(watch_time_segments) {}
ReportPlaybackRequestPayload::Params::Params(const Params&) = default;
ReportPlaybackRequestPayload::Params&
ReportPlaybackRequestPayload::Params::operator=(const Params&) = default;
ReportPlaybackRequestPayload::Params::~Params() = default;

ReportPlaybackRequestPayload::ReportPlaybackRequestPayload(const Params& params)
    : params(params) {
  for (const WatchTimeSegment& watch_time_segment :
       params.watch_time_segments) {
    CHECK_LT(watch_time_segment.media_time_start,
             watch_time_segment.media_time_end);
  }
}
ReportPlaybackRequestPayload::ReportPlaybackRequestPayload(
    const ReportPlaybackRequestPayload&) = default;
ReportPlaybackRequestPayload& ReportPlaybackRequestPayload::operator=(
    const ReportPlaybackRequestPayload&) = default;
ReportPlaybackRequestPayload::~ReportPlaybackRequestPayload() = default;

std::string ReportPlaybackRequestPayload::ToJson() const {
  CHECK(!params.playback_reporting_token.empty());

  base::Value::Dict root;
  root.Set(kPlaybackReportingTokenKey, params.playback_reporting_token);
  root.Set(kClientCurrentTimeKey,
           base::TimeFormatAsIso8601(params.client_current_time));
  root.Set(kPlaybackStartOffsetKey,
           GetTimeDeltaString(params.playback_start_offset));
  root.Set(kMediaTimeCurrentKey, GetTimeDeltaString(params.media_time_current));
  root.Set(kPlaybackStateKey, GetPlaybackStateValue(params.playback_state));

  if (params.initial_report) {
    root.Set(kPlaybackStartDataKey,
             base::Value::Dict().Set(
                 kConnectionTypeKey,
                 GetConnectionTypeValue(params.connection_type)));
  }

  if (!params.watch_time_segments.empty()) {
    base::Value::List segment_list = base::Value::List();
    for (const WatchTimeSegment& watch_time_segment :
         params.watch_time_segments) {
      segment_list.Append(
          base::Value::Dict()
              .Set(kMediaTimeStartKey,
                   GetTimeDeltaString(watch_time_segment.media_time_start))
              .Set(kMediaTimeEndKey,
                   GetTimeDeltaString(watch_time_segment.media_time_end))
              .Set(kClientStartTimeKey,
                   base::TimeFormatAsIso8601(
                       watch_time_segment.client_start_time))
              .Set(kConnectionTypeKey,
                   GetConnectionTypeValue(params.connection_type)));
    }
    root.Set(kWatchTimeSegmentsKey, std::move(segment_list));
  }

  const std::optional<std::string> json = base::WriteJson(root);
  CHECK(json);

  return json.value();
}

}  // namespace google_apis::youtube_music