
syntax = "proto3";

package twitch.video.usher;
option go_package = "code.justin.tv/video/usherapi/rpc/usher";

import "google/protobuf/wrappers.proto";

// The stream service provides calls that return lists of stream data, and
// is usually used to query for stream state. Most of its code can be found here:
//
// https://git-aws.internal.justin.tv/video/usher/blob/7ea7e560fc708b27a5f9e10bc9e6352662ebc61e/api/query_stream.py
//
// See the end of this file for example output from usher
//
// Some type information was gathered from here
// https://git-aws.internal.justin.tv/video/usher/blob/8625aaf2dfad2462cf7b570a1d6bc9a8fd2e98b8/py_tests/modules/testdata/usher/schema.sql#L754-L824

// Stream is a common type representing usher video streams.
message Stream {
  // Identity
  // id is the standard video system stream_id, referenced throughout various
  // systems for identifying a single playable video stream
  int64 id = 1 [json_name = "id"];

  // name is set based on the stream format (which is nearly always hls) and
  // either the twitch channel name, or the customer + content id
  //
  // For Twitch streams this looks like:
  //   "hls_user_ninja"
  //
  // For LVS streams this looks like:
  //   "hls_user_lvs.TwitchDev.teststream1"
  string name = 2 [json_name = "name"];

  // channel is the twitch channel name. this field should not be used for any
  // purpose other than display, channel_id should be used instead to identify
  // channels
  string channel = 3 [json_name = "channel"];

  // channel_id is the identifier for a channel in the Twitch users database
  int64 channel_id = 4 [json_name = "channel_id"];

  // Timestamps
  // start_time_ansic represents the time at which this stream entry was created
  // in ASIC format. Usher has historically run on Pacific time. This is not
  // necessarily the time at which it became playable. Notably this timestamp is
  // missing timezone information
  //
  // Strings will look like this:
  //   "Thu Jun 14 22:54:32 2018"
  string start_time_ansic = 11 [json_name = "up_time"];

  // update_time_ansic represents the last time at which this stream entry was
  // updated in ANSIC format. Stream entries must be updated on a regular
  // interval or usher will garbage collect them, so this is used as a livenes
  // mechanism.
  string update_time_ansic = 12 [json_name = "updated_on"];

  // Metadata
  // format is nearly always set to "hls" and may not be used any more, but it
  // is used to form a stream's name. In previous iterations of the video system
  // format could have been "live" (which referred to rtmp) or a few other values
  string format = 21 [json_name = "format"];

  // broadcaster is read from the broadcast_tag typically, and is used to indicate
  // when special broadcast integrations are in use (rebroadcasts, delay, etc)
  string broadcaster = 22 [json_name = "broadcaster"];

  // broadcast_tag is the RTMP query string from the rtmp broadcast for this stream
  string broadcast_tag = 23 [json_name = "broadcast_tag"];

  // video_codec is the codec of the video source of this broadcast
  string video_codec = 24 [json_name = "video_codec"];

  // audio_codec is the codec of the audio source of this broadcast
  string audio_codec = 25 [json_name = "audio_codec"];

  // type appears to mostly be "stream"
  // TODO: improve this documentation
  string type = 26 [json_name = "type"];

  // stream_type appears to mostly be "hls"
  // TODO: improve this documentation
  string stream_type = 27 [json_name = "stream_type"];

  // origin_name appears to mostly be "hls"
  // TODO: improve this documentation
  string origin_name = 28 [json_name = "origin_name"];

  // stream state
  // ready notes whether this stream is playable (and thus findable)
  bool ready = 40 [json_name = "ready"];

  // starved was originally used to indicate that a stream was starving, it is
  // unclear if this is still the case for HLS streams (but may be)
  bool starved = 41 [json_name = "starved"];

  // connect was originally used to store the RTMP connect string for a stream
  // it may no longer be used but as it's unclear we are including it here
  string connect = 42 [json_name = "connect"];


  // lvs fields
  // customer_id is the LVS customer_id, set to "twitch" for Twitch streams. LVS
  // IDs have a schema, refer to git-aws.internal.justin.tv/video/lvsapi for
  // more detail
  string customer_id = 50 [json_name = "customer_id"];

  // content_id is the LVS content_id, an arbitrary string passed in by users.
  // it will always be set to the stringified channel_id
  string content_id = 51 [json_name = "content_id"];

  // twitch_content should not be used
  bool twitch_content = 52 [json_name = "twitch_content", deprecated = true];

  // viewcount fields
  int32 site_count = 60 [json_name = "site_count"];
  int32 stream_count = 61 [json_name = "stream_count"];
  int32 channel_count = 62 [json_name = "channel_count"];
  int32 embed_count = 63 [json_name = "embed_count"];
  int32 unique_ip_count = 64 [json_name = "unique_ip_count"];
  double embed_ratio = 65 [json_name = "embed_ratio"];

  // these fields are maybe used or maybe not. their meaning is unclear. use
  // only with great caution
  string sdk_version = 70 [json_name = "sdk_version"];
  bool unfindable = 71 [json_name = "unfindable"];
  bool killed = 72 [json_name = "killed"];
  bool directory_hidden = 73 [json_name = "directory_hidden"];
  string client_id = 74 [json_name = "client_id"];
  string flows = 75 [json_name = "flows"];
  bool embed_enabled = 76 [json_name = "embed_enabled"];
  int64 recorder = 77 [json_name = "recorder"];
  bool origin_only = 78 [json_name = "origin_only"];


  // the following fields were left out of this schema because they appear to be
  // unused, or incorrect
  // "os" -- seems unset
  // "cdn_preferred" -- seems unreferenced
  // "video_encoder" -- seems unset
  // "partner" -- was set to false for nina, seems no longer correctly populated
  // "category" -- is always gaming, and is never read
  // "iphone_restricted" -- seems unreferenced
  // "instant_replicate" -- seems unreferenced

  // the following fields were left out because they are no longer used
  // "broadcast_part" -- vestige of the FLV recording system, totally meaningless
}

message StreamMinuteBroadcastsRequest {}

message ListOf_StreamMinuteBroadcastsResponse {
  repeated StreamMinuteBroadcast stream_minute_broadcasts = 1;
}

// StreamMinuteBroadcast has some legacy fields omitted which are in practice
// always null and omitted anyways by usher itself
message StreamMinuteBroadcast {
  string channel = 1 [json_name = "channel"];
  int64 channel_id = 2 [json_name = "channel_id"];
  double minutes_broadcast = 3 [json_name = "minutes_broadcast"];
  bool partner = 5 [json_name = "partner"];
  bool directory_hidden = 6 [json_name = "directory_hidden"];
  bool embed_enabled = 7 [json_name = "embed_enabled"];
  string platform = 8 [json_name = "platform"];
  string broadcaster_software = 9 [json_name = "broadcaster_software"];
  string sdk_version = 10 [json_name = "sdk_version"];
  string client_id = 11 [json_name = "client_id"];
  string video_encoder = 12 [json_name = "video_encoder"];
  string os = 13 [json_name = "os"];
  bool starved = 14 [json_name = "starved"];
  double video_bitrate = 15 [json_name = "video_bitrate"];
  string video_codec = 16 [json_name = "video_codec"];
  string audio_codec = 17 [json_name = "audio_codec"];
  string resolution = 18 [json_name = "resolution"];
  int32 pixels = 19 [json_name = "pixels"];
  string detailed_video_codec = 20 [json_name = "detailed_video_codec"];
  string detailed_audio_codec = 21 [json_name = "detailed_audio_codec"];
  double sample_rate = 22 [json_name = "sample_rate"];
  double max_idr_delta = 23 [json_name = "max_idr_delta"];
  string transcoder_type = 24 [json_name = "transcoder_type"];
  string session_id = 25 [json_name = "session_id"];
  int64 broadcast_id = 26 [json_name = "broadcast_id"];
  string customer_id = 27 [json_name = "customer_id"];
  string content_id = 28 [json_name = "content_id"];
  string latency_mode = 29 [json_name = "latency_mode"];
  string encode_type = 30 [json_name = "encode_type"];
  string job_type = 31 [json_name = "job_type"];
}

message StreamListRequest {
  // limit sets a limit to the number of streams returned in the stream list
  int32 limit = 1;

  // channel queries for all streams entries for this given channel
  string channel = 2;
}

message ListOf_Streams {
  repeated Stream streams = 1;
}

message StreamShowRequest {
  int64 stream_id = 1;
}

message StreamBulkUpdateRequest {
  repeated StreamUpdate updates = 1 [json_name = "updates"];
}

message StreamUpdate {
  // the id of the stream to update
  string id = 1 [json_name = "id"];

  // the number of unique viewers by ip address 
  google.protobuf.Int32Value unique_ip_count = 2 [json_name = "unique_ip_count"];
}

message StreamChannelMappingsRequest {
}

message ListOf_ChannelMappings {
    repeated ChannelMapping mappings = 1;
}

message ChannelMapping {
    // the id of the stream
    int64 id = 1 [json_name = "id"];

    // the id of the channel the stream is being broadcast on.
    // for non-twitch streams, this value is not used.
    int64 channel_id = 2 [json_name = "channel_id"];

    // the id of the content of the stream. for twitch streams,
    // this value is the same as channel_id. for non-twitch streams,
    // this is a user-specified string (example: "twitchmedia_lvs_staging")
    string content_id = 3 [json_name = "content"];

    // the id of the customer whose platform the stream belongs to.
    // for twitch streams, this is set to "twitch". LVS customers have their own
    // identifiers (example: "twitch-lowlatency-darklaunch").
    string customer_id = 4 [json_name = "customer_id"];

    // channel indicates where the stream is being broadcast. for twitch streams,
    // this is the name of the user's channel (example: "monstercat"). for LVS customers,
    // this value is a combination of the content and customer_id (example: "lvs.twitch-lowlatency-darklaunch.145090292").
    string channel = 5 [json_name = "channel"];
}

//
// Example stream list output from usher
//
// [
//   {
//     "broadcast_tag": "<redacted>",
//     "cdn_preferred": false,
//     "video_encoder": "",
//     "unique_ip_count": 93807,
//     "broadcast_part": 1,
//     "origin_only": false,
//     "audio_codec": "aac",
//     "ready": true,
//     "partner": false,
//     "id": "29094074736",
//     "category": "gaming",
//     "origin_name": "hls",
//     "twitch_content": true,
//     "sdk_version": "",
//     "instant_replicate": false,
//     "recorder": 3847712,
//     "customer_id": "twitch",
//     "embed_enabled": true,
//     "channel": "ninja",
//     "up_time": "Thu Jun 14 17:57:22 2018",
//     "format": "hls",
//     "channel_count": 93403,
//     "embed_count": 0,
//     "flows": "/stream/flows/29094074736.json",
//     "client_id": "",
//     "video_codec": "AVC",
//     "directory_hidden": false,
//     "stream_type": "hls",
//     "killed": false,
//     "unfindable": false,
//     "site_count": 93403,
//     "embed_ratio": 0,
//     "name": "hls_user_ninja",
//     "stream_count": 93403,
//     "type": "stream",
//     "channel_id": 19571641,
//     "broadcaster": "unknown_rtmp",
//     "starved": false,
//     "iphone_restricted": false,
//     "updated_on": "Thu Jun 14 18:39:45 2018",
//     "content_id": "19571641",
//     "os": ""
//   }
// ]
//

message StreamForceReplicationByChannelIdRequest {
  string channel_id = 1 [json_name = "id"];
  string host_id = 2 [json_name = "host_id"];
}

message StreamForceReplicationByChannelIdResponse {
  string warning = 1 [json_name = "warning"];
  int64  replicated_flow_count = 2 [json_name = "replicated_flow_count"];
}
