Bläddra i källkod

Merge branch 'sumer-content-dir-module' into sumer-content-dir-impl-channels

Mokhtar Naamani 4 år sedan
förälder
incheckning
511736a884
65 ändrade filer med 6524 tillägg och 627 borttagningar
  1. 0 20
      .github/workflows/content-directory-schemas.yml
  2. 28 0
      .github/workflows/content-metadata.yml
  3. 4 0
      content-metadata-protobuf/.eslintignore
  4. 16 0
      content-metadata-protobuf/.eslintrc.js
  5. 2 0
      content-metadata-protobuf/.gitignore
  6. 4 0
      content-metadata-protobuf/.prettierignore
  7. 53 0
      content-metadata-protobuf/README.md
  8. 15 0
      content-metadata-protobuf/compile.sh
  9. 85 0
      content-metadata-protobuf/compiled/proto/Channel_pb.d.ts
  10. 645 0
      content-metadata-protobuf/compiled/proto/Channel_pb.js
  11. 57 0
      content-metadata-protobuf/compiled/proto/Person_pb.d.ts
  12. 427 0
      content-metadata-protobuf/compiled/proto/Person_pb.js
  13. 33 0
      content-metadata-protobuf/compiled/proto/Playlist_pb.d.ts
  14. 245 0
      content-metadata-protobuf/compiled/proto/Playlist_pb.js
  15. 85 0
      content-metadata-protobuf/compiled/proto/Series_pb.d.ts
  16. 665 0
      content-metadata-protobuf/compiled/proto/Series_pb.js
  17. 235 0
      content-metadata-protobuf/compiled/proto/Video_pb.d.ts
  18. 1846 0
      content-metadata-protobuf/compiled/proto/Video_pb.js
  19. 41 0
      content-metadata-protobuf/doc-appendix.md
  20. 374 0
      content-metadata-protobuf/doc/index.md
  21. 13 0
      content-metadata-protobuf/generate-md-doc.sh
  22. 41 0
      content-metadata-protobuf/package.json
  23. 28 0
      content-metadata-protobuf/proto/Channel.proto
  24. 13 0
      content-metadata-protobuf/proto/Person.proto
  25. 7 0
      content-metadata-protobuf/proto/Playlist.proto
  26. 19 0
      content-metadata-protobuf/proto/Series.proto
  27. 88 0
      content-metadata-protobuf/proto/Video.proto
  28. 74 0
      content-metadata-protobuf/src/KnownLicenses.json
  29. 10 0
      content-metadata-protobuf/src/index.ts
  30. 70 0
      content-metadata-protobuf/src/licenses.ts
  31. 33 0
      content-metadata-protobuf/test/channel.ts
  32. 42 0
      content-metadata-protobuf/test/license-codes.ts
  33. 115 0
      content-metadata-protobuf/test/video.ts
  34. 15 0
      content-metadata-protobuf/tsconfig.json
  35. 79 9
      node/src/chain_spec/content_config.rs
  36. 2 1
      package.json
  37. 4 0
      runtime-modules/common/src/lib.rs
  38. 3 9
      runtime-modules/common/src/storage.rs
  39. 185 211
      runtime-modules/content/src/lib.rs
  40. 4 4
      runtime-modules/content/src/permissions/curator_group.rs
  41. 7 2
      runtime-modules/content/src/permissions/mod.rs
  42. 2 1
      runtime-modules/service-discovery/Cargo.toml
  43. 2 3
      runtime-modules/service-discovery/src/lib.rs
  44. 288 27
      runtime-modules/storage/src/data_directory.rs
  45. 5 2
      runtime-modules/storage/src/data_object_storage_registry.rs
  46. 11 9
      runtime-modules/storage/src/data_object_type_registry.rs
  47. 20 35
      runtime-modules/storage/src/tests/data_directory.rs
  48. 3 1
      runtime-modules/storage/src/tests/data_object_storage_registry.rs
  49. 12 8
      runtime-modules/storage/src/tests/data_object_type_registry.rs
  50. 25 0
      runtime-modules/storage/src/tests/mock.rs
  51. 3 2
      runtime/src/lib.rs
  52. 3 3
      types/augment-codec/all.ts
  53. 21 1
      types/augment-codec/augment-api-query.ts
  54. 37 30
      types/augment-codec/augment-api-tx.ts
  55. 0 0
      types/augment-codec/augment-types.ts
  56. 34 45
      types/augment/all/defs.json
  57. 43 49
      types/augment/all/types.ts
  58. 21 1
      types/augment/augment-api-query.ts
  59. 37 30
      types/augment/augment-api-tx.ts
  60. 0 0
      types/augment/augment-types.ts
  61. 3 0
      types/src/common.ts
  62. 28 30
      types/src/content/index.ts
  63. 1 3
      types/src/discovery.ts
  64. 19 16
      types/src/storage.ts
  65. 194 75
      yarn.lock

+ 0 - 20
.github/workflows/content-directory-schemas.yml

@@ -1,20 +0,0 @@
-name: content-directory-schemas
-on: [pull_request, push]
-
-jobs:
-  schemas_checks:
-    name: Checks
-    runs-on: ubuntu-latest
-    strategy:
-      matrix:
-        node-version: [12.x]
-    steps:
-    - uses: actions/checkout@v1
-    - name: Use Node.js ${{ matrix.node-version }}
-      uses: actions/setup-node@v1
-      with:
-        node-version: ${{ matrix.node-version }}
-    - name: validate
-      run: |
-        yarn install --frozen-lockfile
-        yarn workspace @joystream/cd-schemas checks --quiet

+ 28 - 0
.github/workflows/content-metadata.yml

@@ -0,0 +1,28 @@
+name: content-metadata
+on: [pull_request, push]
+
+jobs:
+  schemas_checks:
+    name: Checks
+    runs-on: ubuntu-latest
+    strategy:
+      matrix:
+        node-version: [12.x]
+    steps:
+    - uses: actions/checkout@v1
+    - name: Use Node.js ${{ matrix.node-version }}
+      uses: actions/setup-node@v1
+      with:
+        node-version: ${{ matrix.node-version }}
+    - name: test protobuf
+      run: |
+        # # Install protoc compiler
+        # sudo apt-get install -y protobuf-compiler
+        # protoc --version
+        # # Install documentation plugin
+        # sudo apt-get install -y golang-go
+        # go get -u github.com/pseudomuto/protoc-gen-doc/cmd/protoc-gen-doc
+        yarn install --frozen-lockfile
+        # yarn workspace @joystream/content-metadata-protobuf build
+        yarn workspace @joystream/content-metadata-protobuf checks --quiet
+        yarn workspace @joystream/content-metadata-protobuf test

+ 4 - 0
content-metadata-protobuf/.eslintignore

@@ -0,0 +1,4 @@
+lib/
+proto/
+compiled/
+.eslintrc.js

+ 16 - 0
content-metadata-protobuf/.eslintrc.js

@@ -0,0 +1,16 @@
+module.exports = {
+  env: {
+    mocha: true,
+  },
+  parserOptions: {
+    project: './tsconfig.json'
+  },
+  extends: [
+    '@joystream/eslint-config'
+  ],
+  rules: {
+    'no-unused-vars': 'off', // Required by the typescript rule below
+    '@typescript-eslint/no-unused-vars': ['error'],
+    '@typescript-eslint/no-floating-promises': 'error',
+  },
+}

+ 2 - 0
content-metadata-protobuf/.gitignore

@@ -0,0 +1,2 @@
+node_modules/
+lib/

+ 4 - 0
content-metadata-protobuf/.prettierignore

@@ -0,0 +1,4 @@
+lib/
+doc/
+proto/
+compiled/

+ 53 - 0
content-metadata-protobuf/README.md

@@ -0,0 +1,53 @@
+## Joystream Content Directory Metadata Library
+
+This package contains protobuf message definitions compiled to Javascript/Typescript used for creating and updating various metadata blobs in the joystream content directory.
+
+### Message Specs
+
+Documented in [doc](./doc) folder
+
+### Choice of protobuf protocol v2
+
+For our usecase we wish to re-use same message to create and update  subset of fields.
+For this reason we need the explicit information about wether a field has been set or not and this is only possible with proto v2.
+
+Background: required/optional feilds are deprecated in [proto v3](https://www.ben-morris.com/handling-protocol-buffers-backwards-compatibility-between-versions-2-and-3-using-c/)
+
+
+### Helper methods
+The custom Joystream types such as License have helper methods to construct pre-defined well known values.
+
+### Example code:
+
+Best place to look at are the [tests specs](./test)
+
+### Opaque types
+We use simple [ISO_639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) code representation for Language.
+useful npm package https://www.npmjs.com/package/iso-639-1
+
+### Building the package
+
+Building will compile the protofiles and build the library from source.
+
+- pre-requisists for compiling protofiles:
+    - [protoc](https://github.com/protocolbuffers/protobuf/releases)
+
+- pre-requisists for generating documentation:
+    - [golang](https://golang.org/)
+    - [protoc-gen-doc](https://github.com/pseudomuto/protoc-gen-doc) to generate docs
+
+```
+yarn && yarn build
+```
+
+### Generating docs
+
+```
+yarn generate-docs
+```
+
+### Tests
+
+```
+yarn test
+```

+ 15 - 0
content-metadata-protobuf/compile.sh

@@ -0,0 +1,15 @@
+#!/usr/bin/env bash
+
+# Path to this plugin
+PROTOC_GEN_TS_PATH="./node_modules/.bin/protoc-gen-ts"
+
+# Directory to write generated code to (.js and .d.ts files)
+OUT_DIR="./compiled"
+mkdir -p ${OUT_DIR}
+
+# Compile proto files
+protoc \
+    --plugin="protoc-gen-ts=${PROTOC_GEN_TS_PATH}" \
+    --js_out="import_style=commonjs,binary:${OUT_DIR}" \
+    --ts_out="${OUT_DIR}" \
+    proto/*.proto

+ 85 - 0
content-metadata-protobuf/compiled/proto/Channel_pb.d.ts

@@ -0,0 +1,85 @@
+// package: 
+// file: proto/Channel.proto
+
+import * as jspb from "google-protobuf";
+
+export class ChannelMetadata extends jspb.Message {
+  hasTitle(): boolean;
+  clearTitle(): void;
+  getTitle(): string | undefined;
+  setTitle(value: string): void;
+
+  hasDescription(): boolean;
+  clearDescription(): void;
+  getDescription(): string | undefined;
+  setDescription(value: string): void;
+
+  hasIsPublic(): boolean;
+  clearIsPublic(): void;
+  getIsPublic(): boolean | undefined;
+  setIsPublic(value: boolean): void;
+
+  hasLanguage(): boolean;
+  clearLanguage(): void;
+  getLanguage(): string | undefined;
+  setLanguage(value: string): void;
+
+  hasCoverPhoto(): boolean;
+  clearCoverPhoto(): void;
+  getCoverPhoto(): number | undefined;
+  setCoverPhoto(value: number): void;
+
+  hasAvatarPhoto(): boolean;
+  clearAvatarPhoto(): void;
+  getAvatarPhoto(): number | undefined;
+  setAvatarPhoto(value: number): void;
+
+  hasCategory(): boolean;
+  clearCategory(): void;
+  getCategory(): number | undefined;
+  setCategory(value: number): void;
+
+  serializeBinary(): Uint8Array;
+  toObject(includeInstance?: boolean): ChannelMetadata.AsObject;
+  static toObject(includeInstance: boolean, msg: ChannelMetadata): ChannelMetadata.AsObject;
+  static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
+  static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
+  static serializeBinaryToWriter(message: ChannelMetadata, writer: jspb.BinaryWriter): void;
+  static deserializeBinary(bytes: Uint8Array): ChannelMetadata;
+  static deserializeBinaryFromReader(message: ChannelMetadata, reader: jspb.BinaryReader): ChannelMetadata;
+}
+
+export namespace ChannelMetadata {
+  export type AsObject = {
+    title?: string,
+    description?: string,
+    isPublic?: boolean,
+    language?: string,
+    coverPhoto?: number,
+    avatarPhoto?: number,
+    category?: number,
+  }
+}
+
+export class ChannelCategoryMetadata extends jspb.Message {
+  hasName(): boolean;
+  clearName(): void;
+  getName(): string | undefined;
+  setName(value: string): void;
+
+  serializeBinary(): Uint8Array;
+  toObject(includeInstance?: boolean): ChannelCategoryMetadata.AsObject;
+  static toObject(includeInstance: boolean, msg: ChannelCategoryMetadata): ChannelCategoryMetadata.AsObject;
+  static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
+  static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
+  static serializeBinaryToWriter(message: ChannelCategoryMetadata, writer: jspb.BinaryWriter): void;
+  static deserializeBinary(bytes: Uint8Array): ChannelCategoryMetadata;
+  static deserializeBinaryFromReader(message: ChannelCategoryMetadata, reader: jspb.BinaryReader): ChannelCategoryMetadata;
+}
+
+export namespace ChannelCategoryMetadata {
+  export type AsObject = {
+    name?: string,
+  }
+}
+

+ 645 - 0
content-metadata-protobuf/compiled/proto/Channel_pb.js

@@ -0,0 +1,645 @@
+// source: proto/Channel.proto
+/**
+ * @fileoverview
+ * @enhanceable
+ * @suppress {messageConventions} JS Compiler reports an error if a variable or
+ *     field starts with 'MSG_' and isn't a translatable message.
+ * @public
+ */
+// GENERATED CODE -- DO NOT EDIT!
+/* eslint-disable */
+// @ts-nocheck
+
+var jspb = require('google-protobuf');
+var goog = jspb;
+var global = Function('return this')();
+
+goog.exportSymbol('proto.ChannelCategoryMetadata', null, global);
+goog.exportSymbol('proto.ChannelMetadata', null, global);
+/**
+ * Generated by JsPbCodeGenerator.
+ * @param {Array=} opt_data Optional initial data array, typically from a
+ * server response, or constructed directly in Javascript. The array is used
+ * in place and becomes part of the constructed object. It is not cloned.
+ * If no data is provided, the constructed object will be empty, but still
+ * valid.
+ * @extends {jspb.Message}
+ * @constructor
+ */
+proto.ChannelMetadata = function(opt_data) {
+  jspb.Message.initialize(this, opt_data, 0, -1, null, null);
+};
+goog.inherits(proto.ChannelMetadata, jspb.Message);
+if (goog.DEBUG && !COMPILED) {
+  /**
+   * @public
+   * @override
+   */
+  proto.ChannelMetadata.displayName = 'proto.ChannelMetadata';
+}
+/**
+ * Generated by JsPbCodeGenerator.
+ * @param {Array=} opt_data Optional initial data array, typically from a
+ * server response, or constructed directly in Javascript. The array is used
+ * in place and becomes part of the constructed object. It is not cloned.
+ * If no data is provided, the constructed object will be empty, but still
+ * valid.
+ * @extends {jspb.Message}
+ * @constructor
+ */
+proto.ChannelCategoryMetadata = function(opt_data) {
+  jspb.Message.initialize(this, opt_data, 0, -1, null, null);
+};
+goog.inherits(proto.ChannelCategoryMetadata, jspb.Message);
+if (goog.DEBUG && !COMPILED) {
+  /**
+   * @public
+   * @override
+   */
+  proto.ChannelCategoryMetadata.displayName = 'proto.ChannelCategoryMetadata';
+}
+
+
+
+if (jspb.Message.GENERATE_TO_OBJECT) {
+/**
+ * Creates an object representation of this proto.
+ * Field names that are reserved in JavaScript and will be renamed to pb_name.
+ * Optional fields that are not set will be set to undefined.
+ * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default.
+ * For the list of reserved names please see:
+ *     net/proto2/compiler/js/internal/generator.cc#kKeyword.
+ * @param {boolean=} opt_includeInstance Deprecated. whether to include the
+ *     JSPB instance for transitional soy proto support:
+ *     http://goto/soy-param-migration
+ * @return {!Object}
+ */
+proto.ChannelMetadata.prototype.toObject = function(opt_includeInstance) {
+  return proto.ChannelMetadata.toObject(opt_includeInstance, this);
+};
+
+
+/**
+ * Static version of the {@see toObject} method.
+ * @param {boolean|undefined} includeInstance Deprecated. Whether to include
+ *     the JSPB instance for transitional soy proto support:
+ *     http://goto/soy-param-migration
+ * @param {!proto.ChannelMetadata} msg The msg instance to transform.
+ * @return {!Object}
+ * @suppress {unusedLocalVariables} f is only used for nested messages
+ */
+proto.ChannelMetadata.toObject = function(includeInstance, msg) {
+  var f, obj = {
+    title: (f = jspb.Message.getField(msg, 1)) == null ? undefined : f,
+    description: (f = jspb.Message.getField(msg, 2)) == null ? undefined : f,
+    isPublic: (f = jspb.Message.getBooleanField(msg, 3)) == null ? undefined : f,
+    language: (f = jspb.Message.getField(msg, 4)) == null ? undefined : f,
+    coverPhoto: (f = jspb.Message.getField(msg, 5)) == null ? undefined : f,
+    avatarPhoto: (f = jspb.Message.getField(msg, 6)) == null ? undefined : f,
+    category: (f = jspb.Message.getField(msg, 7)) == null ? undefined : f
+  };
+
+  if (includeInstance) {
+    obj.$jspbMessageInstance = msg;
+  }
+  return obj;
+};
+}
+
+
+/**
+ * Deserializes binary data (in protobuf wire format).
+ * @param {jspb.ByteSource} bytes The bytes to deserialize.
+ * @return {!proto.ChannelMetadata}
+ */
+proto.ChannelMetadata.deserializeBinary = function(bytes) {
+  var reader = new jspb.BinaryReader(bytes);
+  var msg = new proto.ChannelMetadata;
+  return proto.ChannelMetadata.deserializeBinaryFromReader(msg, reader);
+};
+
+
+/**
+ * Deserializes binary data (in protobuf wire format) from the
+ * given reader into the given message object.
+ * @param {!proto.ChannelMetadata} msg The message object to deserialize into.
+ * @param {!jspb.BinaryReader} reader The BinaryReader to use.
+ * @return {!proto.ChannelMetadata}
+ */
+proto.ChannelMetadata.deserializeBinaryFromReader = function(msg, reader) {
+  while (reader.nextField()) {
+    if (reader.isEndGroup()) {
+      break;
+    }
+    var field = reader.getFieldNumber();
+    switch (field) {
+    case 1:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setTitle(value);
+      break;
+    case 2:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setDescription(value);
+      break;
+    case 3:
+      var value = /** @type {boolean} */ (reader.readBool());
+      msg.setIsPublic(value);
+      break;
+    case 4:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setLanguage(value);
+      break;
+    case 5:
+      var value = /** @type {number} */ (reader.readUint32());
+      msg.setCoverPhoto(value);
+      break;
+    case 6:
+      var value = /** @type {number} */ (reader.readUint32());
+      msg.setAvatarPhoto(value);
+      break;
+    case 7:
+      var value = /** @type {number} */ (reader.readUint64());
+      msg.setCategory(value);
+      break;
+    default:
+      reader.skipField();
+      break;
+    }
+  }
+  return msg;
+};
+
+
+/**
+ * Serializes the message to binary data (in protobuf wire format).
+ * @return {!Uint8Array}
+ */
+proto.ChannelMetadata.prototype.serializeBinary = function() {
+  var writer = new jspb.BinaryWriter();
+  proto.ChannelMetadata.serializeBinaryToWriter(this, writer);
+  return writer.getResultBuffer();
+};
+
+
+/**
+ * Serializes the given message to binary data (in protobuf wire
+ * format), writing to the given BinaryWriter.
+ * @param {!proto.ChannelMetadata} message
+ * @param {!jspb.BinaryWriter} writer
+ * @suppress {unusedLocalVariables} f is only used for nested messages
+ */
+proto.ChannelMetadata.serializeBinaryToWriter = function(message, writer) {
+  var f = undefined;
+  f = /** @type {string} */ (jspb.Message.getField(message, 1));
+  if (f != null) {
+    writer.writeString(
+      1,
+      f
+    );
+  }
+  f = /** @type {string} */ (jspb.Message.getField(message, 2));
+  if (f != null) {
+    writer.writeString(
+      2,
+      f
+    );
+  }
+  f = /** @type {boolean} */ (jspb.Message.getField(message, 3));
+  if (f != null) {
+    writer.writeBool(
+      3,
+      f
+    );
+  }
+  f = /** @type {string} */ (jspb.Message.getField(message, 4));
+  if (f != null) {
+    writer.writeString(
+      4,
+      f
+    );
+  }
+  f = /** @type {number} */ (jspb.Message.getField(message, 5));
+  if (f != null) {
+    writer.writeUint32(
+      5,
+      f
+    );
+  }
+  f = /** @type {number} */ (jspb.Message.getField(message, 6));
+  if (f != null) {
+    writer.writeUint32(
+      6,
+      f
+    );
+  }
+  f = /** @type {number} */ (jspb.Message.getField(message, 7));
+  if (f != null) {
+    writer.writeUint64(
+      7,
+      f
+    );
+  }
+};
+
+
+/**
+ * optional string title = 1;
+ * @return {string}
+ */
+proto.ChannelMetadata.prototype.getTitle = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.ChannelMetadata} returns this
+ */
+proto.ChannelMetadata.prototype.setTitle = function(value) {
+  return jspb.Message.setField(this, 1, value);
+};
+
+
+/**
+ * Clears the field making it undefined.
+ * @return {!proto.ChannelMetadata} returns this
+ */
+proto.ChannelMetadata.prototype.clearTitle = function() {
+  return jspb.Message.setField(this, 1, undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.ChannelMetadata.prototype.hasTitle = function() {
+  return jspb.Message.getField(this, 1) != null;
+};
+
+
+/**
+ * optional string description = 2;
+ * @return {string}
+ */
+proto.ChannelMetadata.prototype.getDescription = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.ChannelMetadata} returns this
+ */
+proto.ChannelMetadata.prototype.setDescription = function(value) {
+  return jspb.Message.setField(this, 2, value);
+};
+
+
+/**
+ * Clears the field making it undefined.
+ * @return {!proto.ChannelMetadata} returns this
+ */
+proto.ChannelMetadata.prototype.clearDescription = function() {
+  return jspb.Message.setField(this, 2, undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.ChannelMetadata.prototype.hasDescription = function() {
+  return jspb.Message.getField(this, 2) != null;
+};
+
+
+/**
+ * optional bool is_public = 3;
+ * @return {boolean}
+ */
+proto.ChannelMetadata.prototype.getIsPublic = function() {
+  return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 3, false));
+};
+
+
+/**
+ * @param {boolean} value
+ * @return {!proto.ChannelMetadata} returns this
+ */
+proto.ChannelMetadata.prototype.setIsPublic = function(value) {
+  return jspb.Message.setField(this, 3, value);
+};
+
+
+/**
+ * Clears the field making it undefined.
+ * @return {!proto.ChannelMetadata} returns this
+ */
+proto.ChannelMetadata.prototype.clearIsPublic = function() {
+  return jspb.Message.setField(this, 3, undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.ChannelMetadata.prototype.hasIsPublic = function() {
+  return jspb.Message.getField(this, 3) != null;
+};
+
+
+/**
+ * optional string language = 4;
+ * @return {string}
+ */
+proto.ChannelMetadata.prototype.getLanguage = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 4, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.ChannelMetadata} returns this
+ */
+proto.ChannelMetadata.prototype.setLanguage = function(value) {
+  return jspb.Message.setField(this, 4, value);
+};
+
+
+/**
+ * Clears the field making it undefined.
+ * @return {!proto.ChannelMetadata} returns this
+ */
+proto.ChannelMetadata.prototype.clearLanguage = function() {
+  return jspb.Message.setField(this, 4, undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.ChannelMetadata.prototype.hasLanguage = function() {
+  return jspb.Message.getField(this, 4) != null;
+};
+
+
+/**
+ * optional uint32 cover_photo = 5;
+ * @return {number}
+ */
+proto.ChannelMetadata.prototype.getCoverPhoto = function() {
+  return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 5, 0));
+};
+
+
+/**
+ * @param {number} value
+ * @return {!proto.ChannelMetadata} returns this
+ */
+proto.ChannelMetadata.prototype.setCoverPhoto = function(value) {
+  return jspb.Message.setField(this, 5, value);
+};
+
+
+/**
+ * Clears the field making it undefined.
+ * @return {!proto.ChannelMetadata} returns this
+ */
+proto.ChannelMetadata.prototype.clearCoverPhoto = function() {
+  return jspb.Message.setField(this, 5, undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.ChannelMetadata.prototype.hasCoverPhoto = function() {
+  return jspb.Message.getField(this, 5) != null;
+};
+
+
+/**
+ * optional uint32 avatar_photo = 6;
+ * @return {number}
+ */
+proto.ChannelMetadata.prototype.getAvatarPhoto = function() {
+  return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 6, 0));
+};
+
+
+/**
+ * @param {number} value
+ * @return {!proto.ChannelMetadata} returns this
+ */
+proto.ChannelMetadata.prototype.setAvatarPhoto = function(value) {
+  return jspb.Message.setField(this, 6, value);
+};
+
+
+/**
+ * Clears the field making it undefined.
+ * @return {!proto.ChannelMetadata} returns this
+ */
+proto.ChannelMetadata.prototype.clearAvatarPhoto = function() {
+  return jspb.Message.setField(this, 6, undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.ChannelMetadata.prototype.hasAvatarPhoto = function() {
+  return jspb.Message.getField(this, 6) != null;
+};
+
+
+/**
+ * optional uint64 category = 7;
+ * @return {number}
+ */
+proto.ChannelMetadata.prototype.getCategory = function() {
+  return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 7, 0));
+};
+
+
+/**
+ * @param {number} value
+ * @return {!proto.ChannelMetadata} returns this
+ */
+proto.ChannelMetadata.prototype.setCategory = function(value) {
+  return jspb.Message.setField(this, 7, value);
+};
+
+
+/**
+ * Clears the field making it undefined.
+ * @return {!proto.ChannelMetadata} returns this
+ */
+proto.ChannelMetadata.prototype.clearCategory = function() {
+  return jspb.Message.setField(this, 7, undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.ChannelMetadata.prototype.hasCategory = function() {
+  return jspb.Message.getField(this, 7) != null;
+};
+
+
+
+
+
+if (jspb.Message.GENERATE_TO_OBJECT) {
+/**
+ * Creates an object representation of this proto.
+ * Field names that are reserved in JavaScript and will be renamed to pb_name.
+ * Optional fields that are not set will be set to undefined.
+ * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default.
+ * For the list of reserved names please see:
+ *     net/proto2/compiler/js/internal/generator.cc#kKeyword.
+ * @param {boolean=} opt_includeInstance Deprecated. whether to include the
+ *     JSPB instance for transitional soy proto support:
+ *     http://goto/soy-param-migration
+ * @return {!Object}
+ */
+proto.ChannelCategoryMetadata.prototype.toObject = function(opt_includeInstance) {
+  return proto.ChannelCategoryMetadata.toObject(opt_includeInstance, this);
+};
+
+
+/**
+ * Static version of the {@see toObject} method.
+ * @param {boolean|undefined} includeInstance Deprecated. Whether to include
+ *     the JSPB instance for transitional soy proto support:
+ *     http://goto/soy-param-migration
+ * @param {!proto.ChannelCategoryMetadata} msg The msg instance to transform.
+ * @return {!Object}
+ * @suppress {unusedLocalVariables} f is only used for nested messages
+ */
+proto.ChannelCategoryMetadata.toObject = function(includeInstance, msg) {
+  var f, obj = {
+    name: (f = jspb.Message.getField(msg, 1)) == null ? undefined : f
+  };
+
+  if (includeInstance) {
+    obj.$jspbMessageInstance = msg;
+  }
+  return obj;
+};
+}
+
+
+/**
+ * Deserializes binary data (in protobuf wire format).
+ * @param {jspb.ByteSource} bytes The bytes to deserialize.
+ * @return {!proto.ChannelCategoryMetadata}
+ */
+proto.ChannelCategoryMetadata.deserializeBinary = function(bytes) {
+  var reader = new jspb.BinaryReader(bytes);
+  var msg = new proto.ChannelCategoryMetadata;
+  return proto.ChannelCategoryMetadata.deserializeBinaryFromReader(msg, reader);
+};
+
+
+/**
+ * Deserializes binary data (in protobuf wire format) from the
+ * given reader into the given message object.
+ * @param {!proto.ChannelCategoryMetadata} msg The message object to deserialize into.
+ * @param {!jspb.BinaryReader} reader The BinaryReader to use.
+ * @return {!proto.ChannelCategoryMetadata}
+ */
+proto.ChannelCategoryMetadata.deserializeBinaryFromReader = function(msg, reader) {
+  while (reader.nextField()) {
+    if (reader.isEndGroup()) {
+      break;
+    }
+    var field = reader.getFieldNumber();
+    switch (field) {
+    case 1:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setName(value);
+      break;
+    default:
+      reader.skipField();
+      break;
+    }
+  }
+  return msg;
+};
+
+
+/**
+ * Serializes the message to binary data (in protobuf wire format).
+ * @return {!Uint8Array}
+ */
+proto.ChannelCategoryMetadata.prototype.serializeBinary = function() {
+  var writer = new jspb.BinaryWriter();
+  proto.ChannelCategoryMetadata.serializeBinaryToWriter(this, writer);
+  return writer.getResultBuffer();
+};
+
+
+/**
+ * Serializes the given message to binary data (in protobuf wire
+ * format), writing to the given BinaryWriter.
+ * @param {!proto.ChannelCategoryMetadata} message
+ * @param {!jspb.BinaryWriter} writer
+ * @suppress {unusedLocalVariables} f is only used for nested messages
+ */
+proto.ChannelCategoryMetadata.serializeBinaryToWriter = function(message, writer) {
+  var f = undefined;
+  f = /** @type {string} */ (jspb.Message.getField(message, 1));
+  if (f != null) {
+    writer.writeString(
+      1,
+      f
+    );
+  }
+};
+
+
+/**
+ * optional string name = 1;
+ * @return {string}
+ */
+proto.ChannelCategoryMetadata.prototype.getName = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.ChannelCategoryMetadata} returns this
+ */
+proto.ChannelCategoryMetadata.prototype.setName = function(value) {
+  return jspb.Message.setField(this, 1, value);
+};
+
+
+/**
+ * Clears the field making it undefined.
+ * @return {!proto.ChannelCategoryMetadata} returns this
+ */
+proto.ChannelCategoryMetadata.prototype.clearName = function() {
+  return jspb.Message.setField(this, 1, undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.ChannelCategoryMetadata.prototype.hasName = function() {
+  return jspb.Message.getField(this, 1) != null;
+};
+
+
+goog.object.extend(exports, proto);

+ 57 - 0
content-metadata-protobuf/compiled/proto/Person_pb.d.ts

@@ -0,0 +1,57 @@
+// package: 
+// file: proto/Person.proto
+
+import * as jspb from "google-protobuf";
+
+export class PersonMetadata extends jspb.Message {
+  hasFirstName(): boolean;
+  clearFirstName(): void;
+  getFirstName(): string | undefined;
+  setFirstName(value: string): void;
+
+  hasMiddleName(): boolean;
+  clearMiddleName(): void;
+  getMiddleName(): string | undefined;
+  setMiddleName(value: string): void;
+
+  hasLastName(): boolean;
+  clearLastName(): void;
+  getLastName(): string | undefined;
+  setLastName(value: string): void;
+
+  hasAbout(): boolean;
+  clearAbout(): void;
+  getAbout(): string | undefined;
+  setAbout(value: string): void;
+
+  hasCoverPhoto(): boolean;
+  clearCoverPhoto(): void;
+  getCoverPhoto(): number | undefined;
+  setCoverPhoto(value: number): void;
+
+  hasAvatarPhoto(): boolean;
+  clearAvatarPhoto(): void;
+  getAvatarPhoto(): number | undefined;
+  setAvatarPhoto(value: number): void;
+
+  serializeBinary(): Uint8Array;
+  toObject(includeInstance?: boolean): PersonMetadata.AsObject;
+  static toObject(includeInstance: boolean, msg: PersonMetadata): PersonMetadata.AsObject;
+  static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
+  static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
+  static serializeBinaryToWriter(message: PersonMetadata, writer: jspb.BinaryWriter): void;
+  static deserializeBinary(bytes: Uint8Array): PersonMetadata;
+  static deserializeBinaryFromReader(message: PersonMetadata, reader: jspb.BinaryReader): PersonMetadata;
+}
+
+export namespace PersonMetadata {
+  export type AsObject = {
+    firstName?: string,
+    middleName?: string,
+    lastName?: string,
+    about?: string,
+    coverPhoto?: number,
+    avatarPhoto?: number,
+  }
+}
+

+ 427 - 0
content-metadata-protobuf/compiled/proto/Person_pb.js

@@ -0,0 +1,427 @@
+// source: proto/Person.proto
+/**
+ * @fileoverview
+ * @enhanceable
+ * @suppress {messageConventions} JS Compiler reports an error if a variable or
+ *     field starts with 'MSG_' and isn't a translatable message.
+ * @public
+ */
+// GENERATED CODE -- DO NOT EDIT!
+/* eslint-disable */
+// @ts-nocheck
+
+var jspb = require('google-protobuf');
+var goog = jspb;
+var global = Function('return this')();
+
+goog.exportSymbol('proto.PersonMetadata', null, global);
+/**
+ * Generated by JsPbCodeGenerator.
+ * @param {Array=} opt_data Optional initial data array, typically from a
+ * server response, or constructed directly in Javascript. The array is used
+ * in place and becomes part of the constructed object. It is not cloned.
+ * If no data is provided, the constructed object will be empty, but still
+ * valid.
+ * @extends {jspb.Message}
+ * @constructor
+ */
+proto.PersonMetadata = function(opt_data) {
+  jspb.Message.initialize(this, opt_data, 0, -1, null, null);
+};
+goog.inherits(proto.PersonMetadata, jspb.Message);
+if (goog.DEBUG && !COMPILED) {
+  /**
+   * @public
+   * @override
+   */
+  proto.PersonMetadata.displayName = 'proto.PersonMetadata';
+}
+
+
+
+if (jspb.Message.GENERATE_TO_OBJECT) {
+/**
+ * Creates an object representation of this proto.
+ * Field names that are reserved in JavaScript and will be renamed to pb_name.
+ * Optional fields that are not set will be set to undefined.
+ * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default.
+ * For the list of reserved names please see:
+ *     net/proto2/compiler/js/internal/generator.cc#kKeyword.
+ * @param {boolean=} opt_includeInstance Deprecated. whether to include the
+ *     JSPB instance for transitional soy proto support:
+ *     http://goto/soy-param-migration
+ * @return {!Object}
+ */
+proto.PersonMetadata.prototype.toObject = function(opt_includeInstance) {
+  return proto.PersonMetadata.toObject(opt_includeInstance, this);
+};
+
+
+/**
+ * Static version of the {@see toObject} method.
+ * @param {boolean|undefined} includeInstance Deprecated. Whether to include
+ *     the JSPB instance for transitional soy proto support:
+ *     http://goto/soy-param-migration
+ * @param {!proto.PersonMetadata} msg The msg instance to transform.
+ * @return {!Object}
+ * @suppress {unusedLocalVariables} f is only used for nested messages
+ */
+proto.PersonMetadata.toObject = function(includeInstance, msg) {
+  var f, obj = {
+    firstName: (f = jspb.Message.getField(msg, 1)) == null ? undefined : f,
+    middleName: (f = jspb.Message.getField(msg, 2)) == null ? undefined : f,
+    lastName: (f = jspb.Message.getField(msg, 3)) == null ? undefined : f,
+    about: (f = jspb.Message.getField(msg, 4)) == null ? undefined : f,
+    coverPhoto: (f = jspb.Message.getField(msg, 5)) == null ? undefined : f,
+    avatarPhoto: (f = jspb.Message.getField(msg, 6)) == null ? undefined : f
+  };
+
+  if (includeInstance) {
+    obj.$jspbMessageInstance = msg;
+  }
+  return obj;
+};
+}
+
+
+/**
+ * Deserializes binary data (in protobuf wire format).
+ * @param {jspb.ByteSource} bytes The bytes to deserialize.
+ * @return {!proto.PersonMetadata}
+ */
+proto.PersonMetadata.deserializeBinary = function(bytes) {
+  var reader = new jspb.BinaryReader(bytes);
+  var msg = new proto.PersonMetadata;
+  return proto.PersonMetadata.deserializeBinaryFromReader(msg, reader);
+};
+
+
+/**
+ * Deserializes binary data (in protobuf wire format) from the
+ * given reader into the given message object.
+ * @param {!proto.PersonMetadata} msg The message object to deserialize into.
+ * @param {!jspb.BinaryReader} reader The BinaryReader to use.
+ * @return {!proto.PersonMetadata}
+ */
+proto.PersonMetadata.deserializeBinaryFromReader = function(msg, reader) {
+  while (reader.nextField()) {
+    if (reader.isEndGroup()) {
+      break;
+    }
+    var field = reader.getFieldNumber();
+    switch (field) {
+    case 1:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setFirstName(value);
+      break;
+    case 2:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setMiddleName(value);
+      break;
+    case 3:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setLastName(value);
+      break;
+    case 4:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setAbout(value);
+      break;
+    case 5:
+      var value = /** @type {number} */ (reader.readUint32());
+      msg.setCoverPhoto(value);
+      break;
+    case 6:
+      var value = /** @type {number} */ (reader.readUint32());
+      msg.setAvatarPhoto(value);
+      break;
+    default:
+      reader.skipField();
+      break;
+    }
+  }
+  return msg;
+};
+
+
+/**
+ * Serializes the message to binary data (in protobuf wire format).
+ * @return {!Uint8Array}
+ */
+proto.PersonMetadata.prototype.serializeBinary = function() {
+  var writer = new jspb.BinaryWriter();
+  proto.PersonMetadata.serializeBinaryToWriter(this, writer);
+  return writer.getResultBuffer();
+};
+
+
+/**
+ * Serializes the given message to binary data (in protobuf wire
+ * format), writing to the given BinaryWriter.
+ * @param {!proto.PersonMetadata} message
+ * @param {!jspb.BinaryWriter} writer
+ * @suppress {unusedLocalVariables} f is only used for nested messages
+ */
+proto.PersonMetadata.serializeBinaryToWriter = function(message, writer) {
+  var f = undefined;
+  f = /** @type {string} */ (jspb.Message.getField(message, 1));
+  if (f != null) {
+    writer.writeString(
+      1,
+      f
+    );
+  }
+  f = /** @type {string} */ (jspb.Message.getField(message, 2));
+  if (f != null) {
+    writer.writeString(
+      2,
+      f
+    );
+  }
+  f = /** @type {string} */ (jspb.Message.getField(message, 3));
+  if (f != null) {
+    writer.writeString(
+      3,
+      f
+    );
+  }
+  f = /** @type {string} */ (jspb.Message.getField(message, 4));
+  if (f != null) {
+    writer.writeString(
+      4,
+      f
+    );
+  }
+  f = /** @type {number} */ (jspb.Message.getField(message, 5));
+  if (f != null) {
+    writer.writeUint32(
+      5,
+      f
+    );
+  }
+  f = /** @type {number} */ (jspb.Message.getField(message, 6));
+  if (f != null) {
+    writer.writeUint32(
+      6,
+      f
+    );
+  }
+};
+
+
+/**
+ * optional string first_name = 1;
+ * @return {string}
+ */
+proto.PersonMetadata.prototype.getFirstName = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.PersonMetadata} returns this
+ */
+proto.PersonMetadata.prototype.setFirstName = function(value) {
+  return jspb.Message.setField(this, 1, value);
+};
+
+
+/**
+ * Clears the field making it undefined.
+ * @return {!proto.PersonMetadata} returns this
+ */
+proto.PersonMetadata.prototype.clearFirstName = function() {
+  return jspb.Message.setField(this, 1, undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.PersonMetadata.prototype.hasFirstName = function() {
+  return jspb.Message.getField(this, 1) != null;
+};
+
+
+/**
+ * optional string middle_name = 2;
+ * @return {string}
+ */
+proto.PersonMetadata.prototype.getMiddleName = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.PersonMetadata} returns this
+ */
+proto.PersonMetadata.prototype.setMiddleName = function(value) {
+  return jspb.Message.setField(this, 2, value);
+};
+
+
+/**
+ * Clears the field making it undefined.
+ * @return {!proto.PersonMetadata} returns this
+ */
+proto.PersonMetadata.prototype.clearMiddleName = function() {
+  return jspb.Message.setField(this, 2, undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.PersonMetadata.prototype.hasMiddleName = function() {
+  return jspb.Message.getField(this, 2) != null;
+};
+
+
+/**
+ * optional string last_name = 3;
+ * @return {string}
+ */
+proto.PersonMetadata.prototype.getLastName = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 3, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.PersonMetadata} returns this
+ */
+proto.PersonMetadata.prototype.setLastName = function(value) {
+  return jspb.Message.setField(this, 3, value);
+};
+
+
+/**
+ * Clears the field making it undefined.
+ * @return {!proto.PersonMetadata} returns this
+ */
+proto.PersonMetadata.prototype.clearLastName = function() {
+  return jspb.Message.setField(this, 3, undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.PersonMetadata.prototype.hasLastName = function() {
+  return jspb.Message.getField(this, 3) != null;
+};
+
+
+/**
+ * optional string about = 4;
+ * @return {string}
+ */
+proto.PersonMetadata.prototype.getAbout = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 4, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.PersonMetadata} returns this
+ */
+proto.PersonMetadata.prototype.setAbout = function(value) {
+  return jspb.Message.setField(this, 4, value);
+};
+
+
+/**
+ * Clears the field making it undefined.
+ * @return {!proto.PersonMetadata} returns this
+ */
+proto.PersonMetadata.prototype.clearAbout = function() {
+  return jspb.Message.setField(this, 4, undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.PersonMetadata.prototype.hasAbout = function() {
+  return jspb.Message.getField(this, 4) != null;
+};
+
+
+/**
+ * optional uint32 cover_photo = 5;
+ * @return {number}
+ */
+proto.PersonMetadata.prototype.getCoverPhoto = function() {
+  return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 5, 0));
+};
+
+
+/**
+ * @param {number} value
+ * @return {!proto.PersonMetadata} returns this
+ */
+proto.PersonMetadata.prototype.setCoverPhoto = function(value) {
+  return jspb.Message.setField(this, 5, value);
+};
+
+
+/**
+ * Clears the field making it undefined.
+ * @return {!proto.PersonMetadata} returns this
+ */
+proto.PersonMetadata.prototype.clearCoverPhoto = function() {
+  return jspb.Message.setField(this, 5, undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.PersonMetadata.prototype.hasCoverPhoto = function() {
+  return jspb.Message.getField(this, 5) != null;
+};
+
+
+/**
+ * optional uint32 avatar_photo = 6;
+ * @return {number}
+ */
+proto.PersonMetadata.prototype.getAvatarPhoto = function() {
+  return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 6, 0));
+};
+
+
+/**
+ * @param {number} value
+ * @return {!proto.PersonMetadata} returns this
+ */
+proto.PersonMetadata.prototype.setAvatarPhoto = function(value) {
+  return jspb.Message.setField(this, 6, value);
+};
+
+
+/**
+ * Clears the field making it undefined.
+ * @return {!proto.PersonMetadata} returns this
+ */
+proto.PersonMetadata.prototype.clearAvatarPhoto = function() {
+  return jspb.Message.setField(this, 6, undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.PersonMetadata.prototype.hasAvatarPhoto = function() {
+  return jspb.Message.getField(this, 6) != null;
+};
+
+
+goog.object.extend(exports, proto);

+ 33 - 0
content-metadata-protobuf/compiled/proto/Playlist_pb.d.ts

@@ -0,0 +1,33 @@
+// package: 
+// file: proto/Playlist.proto
+
+import * as jspb from "google-protobuf";
+
+export class PlaylistMetadata extends jspb.Message {
+  hasTitle(): boolean;
+  clearTitle(): void;
+  getTitle(): string | undefined;
+  setTitle(value: string): void;
+
+  clearVideosList(): void;
+  getVideosList(): Array<number>;
+  setVideosList(value: Array<number>): void;
+  addVideos(value: number, index?: number): number;
+
+  serializeBinary(): Uint8Array;
+  toObject(includeInstance?: boolean): PlaylistMetadata.AsObject;
+  static toObject(includeInstance: boolean, msg: PlaylistMetadata): PlaylistMetadata.AsObject;
+  static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
+  static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
+  static serializeBinaryToWriter(message: PlaylistMetadata, writer: jspb.BinaryWriter): void;
+  static deserializeBinary(bytes: Uint8Array): PlaylistMetadata;
+  static deserializeBinaryFromReader(message: PlaylistMetadata, reader: jspb.BinaryReader): PlaylistMetadata;
+}
+
+export namespace PlaylistMetadata {
+  export type AsObject = {
+    title?: string,
+    videosList: Array<number>,
+  }
+}
+

+ 245 - 0
content-metadata-protobuf/compiled/proto/Playlist_pb.js

@@ -0,0 +1,245 @@
+// source: proto/Playlist.proto
+/**
+ * @fileoverview
+ * @enhanceable
+ * @suppress {messageConventions} JS Compiler reports an error if a variable or
+ *     field starts with 'MSG_' and isn't a translatable message.
+ * @public
+ */
+// GENERATED CODE -- DO NOT EDIT!
+/* eslint-disable */
+// @ts-nocheck
+
+var jspb = require('google-protobuf');
+var goog = jspb;
+var global = Function('return this')();
+
+goog.exportSymbol('proto.PlaylistMetadata', null, global);
+/**
+ * Generated by JsPbCodeGenerator.
+ * @param {Array=} opt_data Optional initial data array, typically from a
+ * server response, or constructed directly in Javascript. The array is used
+ * in place and becomes part of the constructed object. It is not cloned.
+ * If no data is provided, the constructed object will be empty, but still
+ * valid.
+ * @extends {jspb.Message}
+ * @constructor
+ */
+proto.PlaylistMetadata = function(opt_data) {
+  jspb.Message.initialize(this, opt_data, 0, -1, proto.PlaylistMetadata.repeatedFields_, null);
+};
+goog.inherits(proto.PlaylistMetadata, jspb.Message);
+if (goog.DEBUG && !COMPILED) {
+  /**
+   * @public
+   * @override
+   */
+  proto.PlaylistMetadata.displayName = 'proto.PlaylistMetadata';
+}
+
+/**
+ * List of repeated fields within this message type.
+ * @private {!Array<number>}
+ * @const
+ */
+proto.PlaylistMetadata.repeatedFields_ = [2];
+
+
+
+if (jspb.Message.GENERATE_TO_OBJECT) {
+/**
+ * Creates an object representation of this proto.
+ * Field names that are reserved in JavaScript and will be renamed to pb_name.
+ * Optional fields that are not set will be set to undefined.
+ * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default.
+ * For the list of reserved names please see:
+ *     net/proto2/compiler/js/internal/generator.cc#kKeyword.
+ * @param {boolean=} opt_includeInstance Deprecated. whether to include the
+ *     JSPB instance for transitional soy proto support:
+ *     http://goto/soy-param-migration
+ * @return {!Object}
+ */
+proto.PlaylistMetadata.prototype.toObject = function(opt_includeInstance) {
+  return proto.PlaylistMetadata.toObject(opt_includeInstance, this);
+};
+
+
+/**
+ * Static version of the {@see toObject} method.
+ * @param {boolean|undefined} includeInstance Deprecated. Whether to include
+ *     the JSPB instance for transitional soy proto support:
+ *     http://goto/soy-param-migration
+ * @param {!proto.PlaylistMetadata} msg The msg instance to transform.
+ * @return {!Object}
+ * @suppress {unusedLocalVariables} f is only used for nested messages
+ */
+proto.PlaylistMetadata.toObject = function(includeInstance, msg) {
+  var f, obj = {
+    title: (f = jspb.Message.getField(msg, 1)) == null ? undefined : f,
+    videosList: (f = jspb.Message.getRepeatedField(msg, 2)) == null ? undefined : f
+  };
+
+  if (includeInstance) {
+    obj.$jspbMessageInstance = msg;
+  }
+  return obj;
+};
+}
+
+
+/**
+ * Deserializes binary data (in protobuf wire format).
+ * @param {jspb.ByteSource} bytes The bytes to deserialize.
+ * @return {!proto.PlaylistMetadata}
+ */
+proto.PlaylistMetadata.deserializeBinary = function(bytes) {
+  var reader = new jspb.BinaryReader(bytes);
+  var msg = new proto.PlaylistMetadata;
+  return proto.PlaylistMetadata.deserializeBinaryFromReader(msg, reader);
+};
+
+
+/**
+ * Deserializes binary data (in protobuf wire format) from the
+ * given reader into the given message object.
+ * @param {!proto.PlaylistMetadata} msg The message object to deserialize into.
+ * @param {!jspb.BinaryReader} reader The BinaryReader to use.
+ * @return {!proto.PlaylistMetadata}
+ */
+proto.PlaylistMetadata.deserializeBinaryFromReader = function(msg, reader) {
+  while (reader.nextField()) {
+    if (reader.isEndGroup()) {
+      break;
+    }
+    var field = reader.getFieldNumber();
+    switch (field) {
+    case 1:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setTitle(value);
+      break;
+    case 2:
+      var values = /** @type {!Array<number>} */ (reader.isDelimited() ? reader.readPackedUint64() : [reader.readUint64()]);
+      for (var i = 0; i < values.length; i++) {
+        msg.addVideos(values[i]);
+      }
+      break;
+    default:
+      reader.skipField();
+      break;
+    }
+  }
+  return msg;
+};
+
+
+/**
+ * Serializes the message to binary data (in protobuf wire format).
+ * @return {!Uint8Array}
+ */
+proto.PlaylistMetadata.prototype.serializeBinary = function() {
+  var writer = new jspb.BinaryWriter();
+  proto.PlaylistMetadata.serializeBinaryToWriter(this, writer);
+  return writer.getResultBuffer();
+};
+
+
+/**
+ * Serializes the given message to binary data (in protobuf wire
+ * format), writing to the given BinaryWriter.
+ * @param {!proto.PlaylistMetadata} message
+ * @param {!jspb.BinaryWriter} writer
+ * @suppress {unusedLocalVariables} f is only used for nested messages
+ */
+proto.PlaylistMetadata.serializeBinaryToWriter = function(message, writer) {
+  var f = undefined;
+  f = /** @type {string} */ (jspb.Message.getField(message, 1));
+  if (f != null) {
+    writer.writeString(
+      1,
+      f
+    );
+  }
+  f = message.getVideosList();
+  if (f.length > 0) {
+    writer.writeRepeatedUint64(
+      2,
+      f
+    );
+  }
+};
+
+
+/**
+ * optional string title = 1;
+ * @return {string}
+ */
+proto.PlaylistMetadata.prototype.getTitle = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.PlaylistMetadata} returns this
+ */
+proto.PlaylistMetadata.prototype.setTitle = function(value) {
+  return jspb.Message.setField(this, 1, value);
+};
+
+
+/**
+ * Clears the field making it undefined.
+ * @return {!proto.PlaylistMetadata} returns this
+ */
+proto.PlaylistMetadata.prototype.clearTitle = function() {
+  return jspb.Message.setField(this, 1, undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.PlaylistMetadata.prototype.hasTitle = function() {
+  return jspb.Message.getField(this, 1) != null;
+};
+
+
+/**
+ * repeated uint64 videos = 2;
+ * @return {!Array<number>}
+ */
+proto.PlaylistMetadata.prototype.getVideosList = function() {
+  return /** @type {!Array<number>} */ (jspb.Message.getRepeatedField(this, 2));
+};
+
+
+/**
+ * @param {!Array<number>} value
+ * @return {!proto.PlaylistMetadata} returns this
+ */
+proto.PlaylistMetadata.prototype.setVideosList = function(value) {
+  return jspb.Message.setField(this, 2, value || []);
+};
+
+
+/**
+ * @param {number} value
+ * @param {number=} opt_index
+ * @return {!proto.PlaylistMetadata} returns this
+ */
+proto.PlaylistMetadata.prototype.addVideos = function(value, opt_index) {
+  return jspb.Message.addToRepeatedField(this, 2, value, opt_index);
+};
+
+
+/**
+ * Clears the list making it empty but non-null.
+ * @return {!proto.PlaylistMetadata} returns this
+ */
+proto.PlaylistMetadata.prototype.clearVideosList = function() {
+  return this.setVideosList([]);
+};
+
+
+goog.object.extend(exports, proto);

+ 85 - 0
content-metadata-protobuf/compiled/proto/Series_pb.d.ts

@@ -0,0 +1,85 @@
+// package: 
+// file: proto/Series.proto
+
+import * as jspb from "google-protobuf";
+
+export class SeriesMetadata extends jspb.Message {
+  hasTitle(): boolean;
+  clearTitle(): void;
+  getTitle(): string | undefined;
+  setTitle(value: string): void;
+
+  hasDescription(): boolean;
+  clearDescription(): void;
+  getDescription(): string | undefined;
+  setDescription(value: string): void;
+
+  hasCoverPhoto(): boolean;
+  clearCoverPhoto(): void;
+  getCoverPhoto(): number | undefined;
+  setCoverPhoto(value: number): void;
+
+  clearPersonsList(): void;
+  getPersonsList(): Array<number>;
+  setPersonsList(value: Array<number>): void;
+  addPersons(value: number, index?: number): number;
+
+  serializeBinary(): Uint8Array;
+  toObject(includeInstance?: boolean): SeriesMetadata.AsObject;
+  static toObject(includeInstance: boolean, msg: SeriesMetadata): SeriesMetadata.AsObject;
+  static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
+  static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
+  static serializeBinaryToWriter(message: SeriesMetadata, writer: jspb.BinaryWriter): void;
+  static deserializeBinary(bytes: Uint8Array): SeriesMetadata;
+  static deserializeBinaryFromReader(message: SeriesMetadata, reader: jspb.BinaryReader): SeriesMetadata;
+}
+
+export namespace SeriesMetadata {
+  export type AsObject = {
+    title?: string,
+    description?: string,
+    coverPhoto?: number,
+    personsList: Array<number>,
+  }
+}
+
+export class SeasonMetadata extends jspb.Message {
+  hasTitle(): boolean;
+  clearTitle(): void;
+  getTitle(): string | undefined;
+  setTitle(value: string): void;
+
+  hasDescription(): boolean;
+  clearDescription(): void;
+  getDescription(): string | undefined;
+  setDescription(value: string): void;
+
+  hasCoverPhoto(): boolean;
+  clearCoverPhoto(): void;
+  getCoverPhoto(): number | undefined;
+  setCoverPhoto(value: number): void;
+
+  clearPersonsList(): void;
+  getPersonsList(): Array<number>;
+  setPersonsList(value: Array<number>): void;
+  addPersons(value: number, index?: number): number;
+
+  serializeBinary(): Uint8Array;
+  toObject(includeInstance?: boolean): SeasonMetadata.AsObject;
+  static toObject(includeInstance: boolean, msg: SeasonMetadata): SeasonMetadata.AsObject;
+  static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
+  static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
+  static serializeBinaryToWriter(message: SeasonMetadata, writer: jspb.BinaryWriter): void;
+  static deserializeBinary(bytes: Uint8Array): SeasonMetadata;
+  static deserializeBinaryFromReader(message: SeasonMetadata, reader: jspb.BinaryReader): SeasonMetadata;
+}
+
+export namespace SeasonMetadata {
+  export type AsObject = {
+    title?: string,
+    description?: string,
+    coverPhoto?: number,
+    personsList: Array<number>,
+  }
+}
+

+ 665 - 0
content-metadata-protobuf/compiled/proto/Series_pb.js

@@ -0,0 +1,665 @@
+// source: proto/Series.proto
+/**
+ * @fileoverview
+ * @enhanceable
+ * @suppress {messageConventions} JS Compiler reports an error if a variable or
+ *     field starts with 'MSG_' and isn't a translatable message.
+ * @public
+ */
+// GENERATED CODE -- DO NOT EDIT!
+/* eslint-disable */
+// @ts-nocheck
+
+var jspb = require('google-protobuf');
+var goog = jspb;
+var global = Function('return this')();
+
+goog.exportSymbol('proto.SeasonMetadata', null, global);
+goog.exportSymbol('proto.SeriesMetadata', null, global);
+/**
+ * Generated by JsPbCodeGenerator.
+ * @param {Array=} opt_data Optional initial data array, typically from a
+ * server response, or constructed directly in Javascript. The array is used
+ * in place and becomes part of the constructed object. It is not cloned.
+ * If no data is provided, the constructed object will be empty, but still
+ * valid.
+ * @extends {jspb.Message}
+ * @constructor
+ */
+proto.SeriesMetadata = function(opt_data) {
+  jspb.Message.initialize(this, opt_data, 0, -1, proto.SeriesMetadata.repeatedFields_, null);
+};
+goog.inherits(proto.SeriesMetadata, jspb.Message);
+if (goog.DEBUG && !COMPILED) {
+  /**
+   * @public
+   * @override
+   */
+  proto.SeriesMetadata.displayName = 'proto.SeriesMetadata';
+}
+/**
+ * Generated by JsPbCodeGenerator.
+ * @param {Array=} opt_data Optional initial data array, typically from a
+ * server response, or constructed directly in Javascript. The array is used
+ * in place and becomes part of the constructed object. It is not cloned.
+ * If no data is provided, the constructed object will be empty, but still
+ * valid.
+ * @extends {jspb.Message}
+ * @constructor
+ */
+proto.SeasonMetadata = function(opt_data) {
+  jspb.Message.initialize(this, opt_data, 0, -1, proto.SeasonMetadata.repeatedFields_, null);
+};
+goog.inherits(proto.SeasonMetadata, jspb.Message);
+if (goog.DEBUG && !COMPILED) {
+  /**
+   * @public
+   * @override
+   */
+  proto.SeasonMetadata.displayName = 'proto.SeasonMetadata';
+}
+
+/**
+ * List of repeated fields within this message type.
+ * @private {!Array<number>}
+ * @const
+ */
+proto.SeriesMetadata.repeatedFields_ = [4];
+
+
+
+if (jspb.Message.GENERATE_TO_OBJECT) {
+/**
+ * Creates an object representation of this proto.
+ * Field names that are reserved in JavaScript and will be renamed to pb_name.
+ * Optional fields that are not set will be set to undefined.
+ * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default.
+ * For the list of reserved names please see:
+ *     net/proto2/compiler/js/internal/generator.cc#kKeyword.
+ * @param {boolean=} opt_includeInstance Deprecated. whether to include the
+ *     JSPB instance for transitional soy proto support:
+ *     http://goto/soy-param-migration
+ * @return {!Object}
+ */
+proto.SeriesMetadata.prototype.toObject = function(opt_includeInstance) {
+  return proto.SeriesMetadata.toObject(opt_includeInstance, this);
+};
+
+
+/**
+ * Static version of the {@see toObject} method.
+ * @param {boolean|undefined} includeInstance Deprecated. Whether to include
+ *     the JSPB instance for transitional soy proto support:
+ *     http://goto/soy-param-migration
+ * @param {!proto.SeriesMetadata} msg The msg instance to transform.
+ * @return {!Object}
+ * @suppress {unusedLocalVariables} f is only used for nested messages
+ */
+proto.SeriesMetadata.toObject = function(includeInstance, msg) {
+  var f, obj = {
+    title: (f = jspb.Message.getField(msg, 1)) == null ? undefined : f,
+    description: (f = jspb.Message.getField(msg, 2)) == null ? undefined : f,
+    coverPhoto: (f = jspb.Message.getField(msg, 3)) == null ? undefined : f,
+    personsList: (f = jspb.Message.getRepeatedField(msg, 4)) == null ? undefined : f
+  };
+
+  if (includeInstance) {
+    obj.$jspbMessageInstance = msg;
+  }
+  return obj;
+};
+}
+
+
+/**
+ * Deserializes binary data (in protobuf wire format).
+ * @param {jspb.ByteSource} bytes The bytes to deserialize.
+ * @return {!proto.SeriesMetadata}
+ */
+proto.SeriesMetadata.deserializeBinary = function(bytes) {
+  var reader = new jspb.BinaryReader(bytes);
+  var msg = new proto.SeriesMetadata;
+  return proto.SeriesMetadata.deserializeBinaryFromReader(msg, reader);
+};
+
+
+/**
+ * Deserializes binary data (in protobuf wire format) from the
+ * given reader into the given message object.
+ * @param {!proto.SeriesMetadata} msg The message object to deserialize into.
+ * @param {!jspb.BinaryReader} reader The BinaryReader to use.
+ * @return {!proto.SeriesMetadata}
+ */
+proto.SeriesMetadata.deserializeBinaryFromReader = function(msg, reader) {
+  while (reader.nextField()) {
+    if (reader.isEndGroup()) {
+      break;
+    }
+    var field = reader.getFieldNumber();
+    switch (field) {
+    case 1:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setTitle(value);
+      break;
+    case 2:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setDescription(value);
+      break;
+    case 3:
+      var value = /** @type {number} */ (reader.readUint32());
+      msg.setCoverPhoto(value);
+      break;
+    case 4:
+      var values = /** @type {!Array<number>} */ (reader.isDelimited() ? reader.readPackedUint64() : [reader.readUint64()]);
+      for (var i = 0; i < values.length; i++) {
+        msg.addPersons(values[i]);
+      }
+      break;
+    default:
+      reader.skipField();
+      break;
+    }
+  }
+  return msg;
+};
+
+
+/**
+ * Serializes the message to binary data (in protobuf wire format).
+ * @return {!Uint8Array}
+ */
+proto.SeriesMetadata.prototype.serializeBinary = function() {
+  var writer = new jspb.BinaryWriter();
+  proto.SeriesMetadata.serializeBinaryToWriter(this, writer);
+  return writer.getResultBuffer();
+};
+
+
+/**
+ * Serializes the given message to binary data (in protobuf wire
+ * format), writing to the given BinaryWriter.
+ * @param {!proto.SeriesMetadata} message
+ * @param {!jspb.BinaryWriter} writer
+ * @suppress {unusedLocalVariables} f is only used for nested messages
+ */
+proto.SeriesMetadata.serializeBinaryToWriter = function(message, writer) {
+  var f = undefined;
+  f = /** @type {string} */ (jspb.Message.getField(message, 1));
+  if (f != null) {
+    writer.writeString(
+      1,
+      f
+    );
+  }
+  f = /** @type {string} */ (jspb.Message.getField(message, 2));
+  if (f != null) {
+    writer.writeString(
+      2,
+      f
+    );
+  }
+  f = /** @type {number} */ (jspb.Message.getField(message, 3));
+  if (f != null) {
+    writer.writeUint32(
+      3,
+      f
+    );
+  }
+  f = message.getPersonsList();
+  if (f.length > 0) {
+    writer.writePackedUint64(
+      4,
+      f
+    );
+  }
+};
+
+
+/**
+ * optional string title = 1;
+ * @return {string}
+ */
+proto.SeriesMetadata.prototype.getTitle = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.SeriesMetadata} returns this
+ */
+proto.SeriesMetadata.prototype.setTitle = function(value) {
+  return jspb.Message.setField(this, 1, value);
+};
+
+
+/**
+ * Clears the field making it undefined.
+ * @return {!proto.SeriesMetadata} returns this
+ */
+proto.SeriesMetadata.prototype.clearTitle = function() {
+  return jspb.Message.setField(this, 1, undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.SeriesMetadata.prototype.hasTitle = function() {
+  return jspb.Message.getField(this, 1) != null;
+};
+
+
+/**
+ * optional string description = 2;
+ * @return {string}
+ */
+proto.SeriesMetadata.prototype.getDescription = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.SeriesMetadata} returns this
+ */
+proto.SeriesMetadata.prototype.setDescription = function(value) {
+  return jspb.Message.setField(this, 2, value);
+};
+
+
+/**
+ * Clears the field making it undefined.
+ * @return {!proto.SeriesMetadata} returns this
+ */
+proto.SeriesMetadata.prototype.clearDescription = function() {
+  return jspb.Message.setField(this, 2, undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.SeriesMetadata.prototype.hasDescription = function() {
+  return jspb.Message.getField(this, 2) != null;
+};
+
+
+/**
+ * optional uint32 cover_photo = 3;
+ * @return {number}
+ */
+proto.SeriesMetadata.prototype.getCoverPhoto = function() {
+  return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 3, 0));
+};
+
+
+/**
+ * @param {number} value
+ * @return {!proto.SeriesMetadata} returns this
+ */
+proto.SeriesMetadata.prototype.setCoverPhoto = function(value) {
+  return jspb.Message.setField(this, 3, value);
+};
+
+
+/**
+ * Clears the field making it undefined.
+ * @return {!proto.SeriesMetadata} returns this
+ */
+proto.SeriesMetadata.prototype.clearCoverPhoto = function() {
+  return jspb.Message.setField(this, 3, undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.SeriesMetadata.prototype.hasCoverPhoto = function() {
+  return jspb.Message.getField(this, 3) != null;
+};
+
+
+/**
+ * repeated uint64 persons = 4;
+ * @return {!Array<number>}
+ */
+proto.SeriesMetadata.prototype.getPersonsList = function() {
+  return /** @type {!Array<number>} */ (jspb.Message.getRepeatedField(this, 4));
+};
+
+
+/**
+ * @param {!Array<number>} value
+ * @return {!proto.SeriesMetadata} returns this
+ */
+proto.SeriesMetadata.prototype.setPersonsList = function(value) {
+  return jspb.Message.setField(this, 4, value || []);
+};
+
+
+/**
+ * @param {number} value
+ * @param {number=} opt_index
+ * @return {!proto.SeriesMetadata} returns this
+ */
+proto.SeriesMetadata.prototype.addPersons = function(value, opt_index) {
+  return jspb.Message.addToRepeatedField(this, 4, value, opt_index);
+};
+
+
+/**
+ * Clears the list making it empty but non-null.
+ * @return {!proto.SeriesMetadata} returns this
+ */
+proto.SeriesMetadata.prototype.clearPersonsList = function() {
+  return this.setPersonsList([]);
+};
+
+
+
+/**
+ * List of repeated fields within this message type.
+ * @private {!Array<number>}
+ * @const
+ */
+proto.SeasonMetadata.repeatedFields_ = [4];
+
+
+
+if (jspb.Message.GENERATE_TO_OBJECT) {
+/**
+ * Creates an object representation of this proto.
+ * Field names that are reserved in JavaScript and will be renamed to pb_name.
+ * Optional fields that are not set will be set to undefined.
+ * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default.
+ * For the list of reserved names please see:
+ *     net/proto2/compiler/js/internal/generator.cc#kKeyword.
+ * @param {boolean=} opt_includeInstance Deprecated. whether to include the
+ *     JSPB instance for transitional soy proto support:
+ *     http://goto/soy-param-migration
+ * @return {!Object}
+ */
+proto.SeasonMetadata.prototype.toObject = function(opt_includeInstance) {
+  return proto.SeasonMetadata.toObject(opt_includeInstance, this);
+};
+
+
+/**
+ * Static version of the {@see toObject} method.
+ * @param {boolean|undefined} includeInstance Deprecated. Whether to include
+ *     the JSPB instance for transitional soy proto support:
+ *     http://goto/soy-param-migration
+ * @param {!proto.SeasonMetadata} msg The msg instance to transform.
+ * @return {!Object}
+ * @suppress {unusedLocalVariables} f is only used for nested messages
+ */
+proto.SeasonMetadata.toObject = function(includeInstance, msg) {
+  var f, obj = {
+    title: (f = jspb.Message.getField(msg, 1)) == null ? undefined : f,
+    description: (f = jspb.Message.getField(msg, 2)) == null ? undefined : f,
+    coverPhoto: (f = jspb.Message.getField(msg, 3)) == null ? undefined : f,
+    personsList: (f = jspb.Message.getRepeatedField(msg, 4)) == null ? undefined : f
+  };
+
+  if (includeInstance) {
+    obj.$jspbMessageInstance = msg;
+  }
+  return obj;
+};
+}
+
+
+/**
+ * Deserializes binary data (in protobuf wire format).
+ * @param {jspb.ByteSource} bytes The bytes to deserialize.
+ * @return {!proto.SeasonMetadata}
+ */
+proto.SeasonMetadata.deserializeBinary = function(bytes) {
+  var reader = new jspb.BinaryReader(bytes);
+  var msg = new proto.SeasonMetadata;
+  return proto.SeasonMetadata.deserializeBinaryFromReader(msg, reader);
+};
+
+
+/**
+ * Deserializes binary data (in protobuf wire format) from the
+ * given reader into the given message object.
+ * @param {!proto.SeasonMetadata} msg The message object to deserialize into.
+ * @param {!jspb.BinaryReader} reader The BinaryReader to use.
+ * @return {!proto.SeasonMetadata}
+ */
+proto.SeasonMetadata.deserializeBinaryFromReader = function(msg, reader) {
+  while (reader.nextField()) {
+    if (reader.isEndGroup()) {
+      break;
+    }
+    var field = reader.getFieldNumber();
+    switch (field) {
+    case 1:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setTitle(value);
+      break;
+    case 2:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setDescription(value);
+      break;
+    case 3:
+      var value = /** @type {number} */ (reader.readUint32());
+      msg.setCoverPhoto(value);
+      break;
+    case 4:
+      var values = /** @type {!Array<number>} */ (reader.isDelimited() ? reader.readPackedUint64() : [reader.readUint64()]);
+      for (var i = 0; i < values.length; i++) {
+        msg.addPersons(values[i]);
+      }
+      break;
+    default:
+      reader.skipField();
+      break;
+    }
+  }
+  return msg;
+};
+
+
+/**
+ * Serializes the message to binary data (in protobuf wire format).
+ * @return {!Uint8Array}
+ */
+proto.SeasonMetadata.prototype.serializeBinary = function() {
+  var writer = new jspb.BinaryWriter();
+  proto.SeasonMetadata.serializeBinaryToWriter(this, writer);
+  return writer.getResultBuffer();
+};
+
+
+/**
+ * Serializes the given message to binary data (in protobuf wire
+ * format), writing to the given BinaryWriter.
+ * @param {!proto.SeasonMetadata} message
+ * @param {!jspb.BinaryWriter} writer
+ * @suppress {unusedLocalVariables} f is only used for nested messages
+ */
+proto.SeasonMetadata.serializeBinaryToWriter = function(message, writer) {
+  var f = undefined;
+  f = /** @type {string} */ (jspb.Message.getField(message, 1));
+  if (f != null) {
+    writer.writeString(
+      1,
+      f
+    );
+  }
+  f = /** @type {string} */ (jspb.Message.getField(message, 2));
+  if (f != null) {
+    writer.writeString(
+      2,
+      f
+    );
+  }
+  f = /** @type {number} */ (jspb.Message.getField(message, 3));
+  if (f != null) {
+    writer.writeUint32(
+      3,
+      f
+    );
+  }
+  f = message.getPersonsList();
+  if (f.length > 0) {
+    writer.writePackedUint64(
+      4,
+      f
+    );
+  }
+};
+
+
+/**
+ * optional string title = 1;
+ * @return {string}
+ */
+proto.SeasonMetadata.prototype.getTitle = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.SeasonMetadata} returns this
+ */
+proto.SeasonMetadata.prototype.setTitle = function(value) {
+  return jspb.Message.setField(this, 1, value);
+};
+
+
+/**
+ * Clears the field making it undefined.
+ * @return {!proto.SeasonMetadata} returns this
+ */
+proto.SeasonMetadata.prototype.clearTitle = function() {
+  return jspb.Message.setField(this, 1, undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.SeasonMetadata.prototype.hasTitle = function() {
+  return jspb.Message.getField(this, 1) != null;
+};
+
+
+/**
+ * optional string description = 2;
+ * @return {string}
+ */
+proto.SeasonMetadata.prototype.getDescription = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.SeasonMetadata} returns this
+ */
+proto.SeasonMetadata.prototype.setDescription = function(value) {
+  return jspb.Message.setField(this, 2, value);
+};
+
+
+/**
+ * Clears the field making it undefined.
+ * @return {!proto.SeasonMetadata} returns this
+ */
+proto.SeasonMetadata.prototype.clearDescription = function() {
+  return jspb.Message.setField(this, 2, undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.SeasonMetadata.prototype.hasDescription = function() {
+  return jspb.Message.getField(this, 2) != null;
+};
+
+
+/**
+ * optional uint32 cover_photo = 3;
+ * @return {number}
+ */
+proto.SeasonMetadata.prototype.getCoverPhoto = function() {
+  return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 3, 0));
+};
+
+
+/**
+ * @param {number} value
+ * @return {!proto.SeasonMetadata} returns this
+ */
+proto.SeasonMetadata.prototype.setCoverPhoto = function(value) {
+  return jspb.Message.setField(this, 3, value);
+};
+
+
+/**
+ * Clears the field making it undefined.
+ * @return {!proto.SeasonMetadata} returns this
+ */
+proto.SeasonMetadata.prototype.clearCoverPhoto = function() {
+  return jspb.Message.setField(this, 3, undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.SeasonMetadata.prototype.hasCoverPhoto = function() {
+  return jspb.Message.getField(this, 3) != null;
+};
+
+
+/**
+ * repeated uint64 persons = 4;
+ * @return {!Array<number>}
+ */
+proto.SeasonMetadata.prototype.getPersonsList = function() {
+  return /** @type {!Array<number>} */ (jspb.Message.getRepeatedField(this, 4));
+};
+
+
+/**
+ * @param {!Array<number>} value
+ * @return {!proto.SeasonMetadata} returns this
+ */
+proto.SeasonMetadata.prototype.setPersonsList = function(value) {
+  return jspb.Message.setField(this, 4, value || []);
+};
+
+
+/**
+ * @param {number} value
+ * @param {number=} opt_index
+ * @return {!proto.SeasonMetadata} returns this
+ */
+proto.SeasonMetadata.prototype.addPersons = function(value, opt_index) {
+  return jspb.Message.addToRepeatedField(this, 4, value, opt_index);
+};
+
+
+/**
+ * Clears the list making it empty but non-null.
+ * @return {!proto.SeasonMetadata} returns this
+ */
+proto.SeasonMetadata.prototype.clearPersonsList = function() {
+  return this.setPersonsList([]);
+};
+
+
+goog.object.extend(exports, proto);

+ 235 - 0
content-metadata-protobuf/compiled/proto/Video_pb.d.ts

@@ -0,0 +1,235 @@
+// package: 
+// file: proto/Video.proto
+
+import * as jspb from "google-protobuf";
+
+export class PublishedBeforeJoystream extends jspb.Message {
+  hasIsPublished(): boolean;
+  clearIsPublished(): void;
+  getIsPublished(): boolean | undefined;
+  setIsPublished(value: boolean): void;
+
+  hasDate(): boolean;
+  clearDate(): void;
+  getDate(): string | undefined;
+  setDate(value: string): void;
+
+  serializeBinary(): Uint8Array;
+  toObject(includeInstance?: boolean): PublishedBeforeJoystream.AsObject;
+  static toObject(includeInstance: boolean, msg: PublishedBeforeJoystream): PublishedBeforeJoystream.AsObject;
+  static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
+  static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
+  static serializeBinaryToWriter(message: PublishedBeforeJoystream, writer: jspb.BinaryWriter): void;
+  static deserializeBinary(bytes: Uint8Array): PublishedBeforeJoystream;
+  static deserializeBinaryFromReader(message: PublishedBeforeJoystream, reader: jspb.BinaryReader): PublishedBeforeJoystream;
+}
+
+export namespace PublishedBeforeJoystream {
+  export type AsObject = {
+    isPublished?: boolean,
+    date?: string,
+  }
+}
+
+export class License extends jspb.Message {
+  hasCode(): boolean;
+  clearCode(): void;
+  getCode(): number | undefined;
+  setCode(value: number): void;
+
+  hasAttribution(): boolean;
+  clearAttribution(): void;
+  getAttribution(): string | undefined;
+  setAttribution(value: string): void;
+
+  hasCustomText(): boolean;
+  clearCustomText(): void;
+  getCustomText(): string | undefined;
+  setCustomText(value: string): void;
+
+  serializeBinary(): Uint8Array;
+  toObject(includeInstance?: boolean): License.AsObject;
+  static toObject(includeInstance: boolean, msg: License): License.AsObject;
+  static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
+  static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
+  static serializeBinaryToWriter(message: License, writer: jspb.BinaryWriter): void;
+  static deserializeBinary(bytes: Uint8Array): License;
+  static deserializeBinaryFromReader(message: License, reader: jspb.BinaryReader): License;
+}
+
+export namespace License {
+  export type AsObject = {
+    code?: number,
+    attribution?: string,
+    customText?: string,
+  }
+}
+
+export class MediaType extends jspb.Message {
+  hasCodecName(): boolean;
+  clearCodecName(): void;
+  getCodecName(): string | undefined;
+  setCodecName(value: string): void;
+
+  hasContainer(): boolean;
+  clearContainer(): void;
+  getContainer(): string | undefined;
+  setContainer(value: string): void;
+
+  hasMimeMediaType(): boolean;
+  clearMimeMediaType(): void;
+  getMimeMediaType(): string | undefined;
+  setMimeMediaType(value: string): void;
+
+  serializeBinary(): Uint8Array;
+  toObject(includeInstance?: boolean): MediaType.AsObject;
+  static toObject(includeInstance: boolean, msg: MediaType): MediaType.AsObject;
+  static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
+  static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
+  static serializeBinaryToWriter(message: MediaType, writer: jspb.BinaryWriter): void;
+  static deserializeBinary(bytes: Uint8Array): MediaType;
+  static deserializeBinaryFromReader(message: MediaType, reader: jspb.BinaryReader): MediaType;
+}
+
+export namespace MediaType {
+  export type AsObject = {
+    codecName?: string,
+    container?: string,
+    mimeMediaType?: string,
+  }
+}
+
+export class VideoMetadata extends jspb.Message {
+  hasTitle(): boolean;
+  clearTitle(): void;
+  getTitle(): string | undefined;
+  setTitle(value: string): void;
+
+  hasDescription(): boolean;
+  clearDescription(): void;
+  getDescription(): string | undefined;
+  setDescription(value: string): void;
+
+  hasVideo(): boolean;
+  clearVideo(): void;
+  getVideo(): number | undefined;
+  setVideo(value: number): void;
+
+  hasThumbnailPhoto(): boolean;
+  clearThumbnailPhoto(): void;
+  getThumbnailPhoto(): number | undefined;
+  setThumbnailPhoto(value: number): void;
+
+  hasDuration(): boolean;
+  clearDuration(): void;
+  getDuration(): number | undefined;
+  setDuration(value: number): void;
+
+  hasMediaPixelHeight(): boolean;
+  clearMediaPixelHeight(): void;
+  getMediaPixelHeight(): number | undefined;
+  setMediaPixelHeight(value: number): void;
+
+  hasMediaPixelWidth(): boolean;
+  clearMediaPixelWidth(): void;
+  getMediaPixelWidth(): number | undefined;
+  setMediaPixelWidth(value: number): void;
+
+  hasMediaType(): boolean;
+  clearMediaType(): void;
+  getMediaType(): MediaType | undefined;
+  setMediaType(value?: MediaType): void;
+
+  hasLanguage(): boolean;
+  clearLanguage(): void;
+  getLanguage(): string | undefined;
+  setLanguage(value: string): void;
+
+  hasLicense(): boolean;
+  clearLicense(): void;
+  getLicense(): License | undefined;
+  setLicense(value?: License): void;
+
+  hasPublishedBeforeJoystream(): boolean;
+  clearPublishedBeforeJoystream(): void;
+  getPublishedBeforeJoystream(): PublishedBeforeJoystream | undefined;
+  setPublishedBeforeJoystream(value?: PublishedBeforeJoystream): void;
+
+  hasHasMarketing(): boolean;
+  clearHasMarketing(): void;
+  getHasMarketing(): boolean | undefined;
+  setHasMarketing(value: boolean): void;
+
+  hasIsPublic(): boolean;
+  clearIsPublic(): void;
+  getIsPublic(): boolean | undefined;
+  setIsPublic(value: boolean): void;
+
+  hasIsExplicit(): boolean;
+  clearIsExplicit(): void;
+  getIsExplicit(): boolean | undefined;
+  setIsExplicit(value: boolean): void;
+
+  clearPersonsList(): void;
+  getPersonsList(): Array<number>;
+  setPersonsList(value: Array<number>): void;
+  addPersons(value: number, index?: number): number;
+
+  hasCategory(): boolean;
+  clearCategory(): void;
+  getCategory(): number | undefined;
+  setCategory(value: number): void;
+
+  serializeBinary(): Uint8Array;
+  toObject(includeInstance?: boolean): VideoMetadata.AsObject;
+  static toObject(includeInstance: boolean, msg: VideoMetadata): VideoMetadata.AsObject;
+  static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
+  static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
+  static serializeBinaryToWriter(message: VideoMetadata, writer: jspb.BinaryWriter): void;
+  static deserializeBinary(bytes: Uint8Array): VideoMetadata;
+  static deserializeBinaryFromReader(message: VideoMetadata, reader: jspb.BinaryReader): VideoMetadata;
+}
+
+export namespace VideoMetadata {
+  export type AsObject = {
+    title?: string,
+    description?: string,
+    video?: number,
+    thumbnailPhoto?: number,
+    duration?: number,
+    mediaPixelHeight?: number,
+    mediaPixelWidth?: number,
+    mediaType?: MediaType.AsObject,
+    language?: string,
+    license?: License.AsObject,
+    publishedBeforeJoystream?: PublishedBeforeJoystream.AsObject,
+    hasMarketing?: boolean,
+    isPublic?: boolean,
+    isExplicit?: boolean,
+    personsList: Array<number>,
+    category?: number,
+  }
+}
+
+export class VideoCategoryMetadata extends jspb.Message {
+  hasName(): boolean;
+  clearName(): void;
+  getName(): string | undefined;
+  setName(value: string): void;
+
+  serializeBinary(): Uint8Array;
+  toObject(includeInstance?: boolean): VideoCategoryMetadata.AsObject;
+  static toObject(includeInstance: boolean, msg: VideoCategoryMetadata): VideoCategoryMetadata.AsObject;
+  static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
+  static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
+  static serializeBinaryToWriter(message: VideoCategoryMetadata, writer: jspb.BinaryWriter): void;
+  static deserializeBinary(bytes: Uint8Array): VideoCategoryMetadata;
+  static deserializeBinaryFromReader(message: VideoCategoryMetadata, reader: jspb.BinaryReader): VideoCategoryMetadata;
+}
+
+export namespace VideoCategoryMetadata {
+  export type AsObject = {
+    name?: string,
+  }
+}
+

+ 1846 - 0
content-metadata-protobuf/compiled/proto/Video_pb.js

@@ -0,0 +1,1846 @@
+// source: proto/Video.proto
+/**
+ * @fileoverview
+ * @enhanceable
+ * @suppress {messageConventions} JS Compiler reports an error if a variable or
+ *     field starts with 'MSG_' and isn't a translatable message.
+ * @public
+ */
+// GENERATED CODE -- DO NOT EDIT!
+/* eslint-disable */
+// @ts-nocheck
+
+var jspb = require('google-protobuf');
+var goog = jspb;
+var global = Function('return this')();
+
+goog.exportSymbol('proto.License', null, global);
+goog.exportSymbol('proto.MediaType', null, global);
+goog.exportSymbol('proto.PublishedBeforeJoystream', null, global);
+goog.exportSymbol('proto.VideoCategoryMetadata', null, global);
+goog.exportSymbol('proto.VideoMetadata', null, global);
+/**
+ * Generated by JsPbCodeGenerator.
+ * @param {Array=} opt_data Optional initial data array, typically from a
+ * server response, or constructed directly in Javascript. The array is used
+ * in place and becomes part of the constructed object. It is not cloned.
+ * If no data is provided, the constructed object will be empty, but still
+ * valid.
+ * @extends {jspb.Message}
+ * @constructor
+ */
+proto.PublishedBeforeJoystream = function(opt_data) {
+  jspb.Message.initialize(this, opt_data, 0, -1, null, null);
+};
+goog.inherits(proto.PublishedBeforeJoystream, jspb.Message);
+if (goog.DEBUG && !COMPILED) {
+  /**
+   * @public
+   * @override
+   */
+  proto.PublishedBeforeJoystream.displayName = 'proto.PublishedBeforeJoystream';
+}
+/**
+ * Generated by JsPbCodeGenerator.
+ * @param {Array=} opt_data Optional initial data array, typically from a
+ * server response, or constructed directly in Javascript. The array is used
+ * in place and becomes part of the constructed object. It is not cloned.
+ * If no data is provided, the constructed object will be empty, but still
+ * valid.
+ * @extends {jspb.Message}
+ * @constructor
+ */
+proto.License = function(opt_data) {
+  jspb.Message.initialize(this, opt_data, 0, -1, null, null);
+};
+goog.inherits(proto.License, jspb.Message);
+if (goog.DEBUG && !COMPILED) {
+  /**
+   * @public
+   * @override
+   */
+  proto.License.displayName = 'proto.License';
+}
+/**
+ * Generated by JsPbCodeGenerator.
+ * @param {Array=} opt_data Optional initial data array, typically from a
+ * server response, or constructed directly in Javascript. The array is used
+ * in place and becomes part of the constructed object. It is not cloned.
+ * If no data is provided, the constructed object will be empty, but still
+ * valid.
+ * @extends {jspb.Message}
+ * @constructor
+ */
+proto.MediaType = function(opt_data) {
+  jspb.Message.initialize(this, opt_data, 0, -1, null, null);
+};
+goog.inherits(proto.MediaType, jspb.Message);
+if (goog.DEBUG && !COMPILED) {
+  /**
+   * @public
+   * @override
+   */
+  proto.MediaType.displayName = 'proto.MediaType';
+}
+/**
+ * Generated by JsPbCodeGenerator.
+ * @param {Array=} opt_data Optional initial data array, typically from a
+ * server response, or constructed directly in Javascript. The array is used
+ * in place and becomes part of the constructed object. It is not cloned.
+ * If no data is provided, the constructed object will be empty, but still
+ * valid.
+ * @extends {jspb.Message}
+ * @constructor
+ */
+proto.VideoMetadata = function(opt_data) {
+  jspb.Message.initialize(this, opt_data, 0, -1, proto.VideoMetadata.repeatedFields_, null);
+};
+goog.inherits(proto.VideoMetadata, jspb.Message);
+if (goog.DEBUG && !COMPILED) {
+  /**
+   * @public
+   * @override
+   */
+  proto.VideoMetadata.displayName = 'proto.VideoMetadata';
+}
+/**
+ * Generated by JsPbCodeGenerator.
+ * @param {Array=} opt_data Optional initial data array, typically from a
+ * server response, or constructed directly in Javascript. The array is used
+ * in place and becomes part of the constructed object. It is not cloned.
+ * If no data is provided, the constructed object will be empty, but still
+ * valid.
+ * @extends {jspb.Message}
+ * @constructor
+ */
+proto.VideoCategoryMetadata = function(opt_data) {
+  jspb.Message.initialize(this, opt_data, 0, -1, null, null);
+};
+goog.inherits(proto.VideoCategoryMetadata, jspb.Message);
+if (goog.DEBUG && !COMPILED) {
+  /**
+   * @public
+   * @override
+   */
+  proto.VideoCategoryMetadata.displayName = 'proto.VideoCategoryMetadata';
+}
+
+
+
+if (jspb.Message.GENERATE_TO_OBJECT) {
+/**
+ * Creates an object representation of this proto.
+ * Field names that are reserved in JavaScript and will be renamed to pb_name.
+ * Optional fields that are not set will be set to undefined.
+ * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default.
+ * For the list of reserved names please see:
+ *     net/proto2/compiler/js/internal/generator.cc#kKeyword.
+ * @param {boolean=} opt_includeInstance Deprecated. whether to include the
+ *     JSPB instance for transitional soy proto support:
+ *     http://goto/soy-param-migration
+ * @return {!Object}
+ */
+proto.PublishedBeforeJoystream.prototype.toObject = function(opt_includeInstance) {
+  return proto.PublishedBeforeJoystream.toObject(opt_includeInstance, this);
+};
+
+
+/**
+ * Static version of the {@see toObject} method.
+ * @param {boolean|undefined} includeInstance Deprecated. Whether to include
+ *     the JSPB instance for transitional soy proto support:
+ *     http://goto/soy-param-migration
+ * @param {!proto.PublishedBeforeJoystream} msg The msg instance to transform.
+ * @return {!Object}
+ * @suppress {unusedLocalVariables} f is only used for nested messages
+ */
+proto.PublishedBeforeJoystream.toObject = function(includeInstance, msg) {
+  var f, obj = {
+    isPublished: (f = jspb.Message.getBooleanField(msg, 1)) == null ? undefined : f,
+    date: (f = jspb.Message.getField(msg, 2)) == null ? undefined : f
+  };
+
+  if (includeInstance) {
+    obj.$jspbMessageInstance = msg;
+  }
+  return obj;
+};
+}
+
+
+/**
+ * Deserializes binary data (in protobuf wire format).
+ * @param {jspb.ByteSource} bytes The bytes to deserialize.
+ * @return {!proto.PublishedBeforeJoystream}
+ */
+proto.PublishedBeforeJoystream.deserializeBinary = function(bytes) {
+  var reader = new jspb.BinaryReader(bytes);
+  var msg = new proto.PublishedBeforeJoystream;
+  return proto.PublishedBeforeJoystream.deserializeBinaryFromReader(msg, reader);
+};
+
+
+/**
+ * Deserializes binary data (in protobuf wire format) from the
+ * given reader into the given message object.
+ * @param {!proto.PublishedBeforeJoystream} msg The message object to deserialize into.
+ * @param {!jspb.BinaryReader} reader The BinaryReader to use.
+ * @return {!proto.PublishedBeforeJoystream}
+ */
+proto.PublishedBeforeJoystream.deserializeBinaryFromReader = function(msg, reader) {
+  while (reader.nextField()) {
+    if (reader.isEndGroup()) {
+      break;
+    }
+    var field = reader.getFieldNumber();
+    switch (field) {
+    case 1:
+      var value = /** @type {boolean} */ (reader.readBool());
+      msg.setIsPublished(value);
+      break;
+    case 2:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setDate(value);
+      break;
+    default:
+      reader.skipField();
+      break;
+    }
+  }
+  return msg;
+};
+
+
+/**
+ * Serializes the message to binary data (in protobuf wire format).
+ * @return {!Uint8Array}
+ */
+proto.PublishedBeforeJoystream.prototype.serializeBinary = function() {
+  var writer = new jspb.BinaryWriter();
+  proto.PublishedBeforeJoystream.serializeBinaryToWriter(this, writer);
+  return writer.getResultBuffer();
+};
+
+
+/**
+ * Serializes the given message to binary data (in protobuf wire
+ * format), writing to the given BinaryWriter.
+ * @param {!proto.PublishedBeforeJoystream} message
+ * @param {!jspb.BinaryWriter} writer
+ * @suppress {unusedLocalVariables} f is only used for nested messages
+ */
+proto.PublishedBeforeJoystream.serializeBinaryToWriter = function(message, writer) {
+  var f = undefined;
+  f = /** @type {boolean} */ (jspb.Message.getField(message, 1));
+  if (f != null) {
+    writer.writeBool(
+      1,
+      f
+    );
+  }
+  f = /** @type {string} */ (jspb.Message.getField(message, 2));
+  if (f != null) {
+    writer.writeString(
+      2,
+      f
+    );
+  }
+};
+
+
+/**
+ * optional bool is_published = 1;
+ * @return {boolean}
+ */
+proto.PublishedBeforeJoystream.prototype.getIsPublished = function() {
+  return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 1, false));
+};
+
+
+/**
+ * @param {boolean} value
+ * @return {!proto.PublishedBeforeJoystream} returns this
+ */
+proto.PublishedBeforeJoystream.prototype.setIsPublished = function(value) {
+  return jspb.Message.setField(this, 1, value);
+};
+
+
+/**
+ * Clears the field making it undefined.
+ * @return {!proto.PublishedBeforeJoystream} returns this
+ */
+proto.PublishedBeforeJoystream.prototype.clearIsPublished = function() {
+  return jspb.Message.setField(this, 1, undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.PublishedBeforeJoystream.prototype.hasIsPublished = function() {
+  return jspb.Message.getField(this, 1) != null;
+};
+
+
+/**
+ * optional string date = 2;
+ * @return {string}
+ */
+proto.PublishedBeforeJoystream.prototype.getDate = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.PublishedBeforeJoystream} returns this
+ */
+proto.PublishedBeforeJoystream.prototype.setDate = function(value) {
+  return jspb.Message.setField(this, 2, value);
+};
+
+
+/**
+ * Clears the field making it undefined.
+ * @return {!proto.PublishedBeforeJoystream} returns this
+ */
+proto.PublishedBeforeJoystream.prototype.clearDate = function() {
+  return jspb.Message.setField(this, 2, undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.PublishedBeforeJoystream.prototype.hasDate = function() {
+  return jspb.Message.getField(this, 2) != null;
+};
+
+
+
+
+
+if (jspb.Message.GENERATE_TO_OBJECT) {
+/**
+ * Creates an object representation of this proto.
+ * Field names that are reserved in JavaScript and will be renamed to pb_name.
+ * Optional fields that are not set will be set to undefined.
+ * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default.
+ * For the list of reserved names please see:
+ *     net/proto2/compiler/js/internal/generator.cc#kKeyword.
+ * @param {boolean=} opt_includeInstance Deprecated. whether to include the
+ *     JSPB instance for transitional soy proto support:
+ *     http://goto/soy-param-migration
+ * @return {!Object}
+ */
+proto.License.prototype.toObject = function(opt_includeInstance) {
+  return proto.License.toObject(opt_includeInstance, this);
+};
+
+
+/**
+ * Static version of the {@see toObject} method.
+ * @param {boolean|undefined} includeInstance Deprecated. Whether to include
+ *     the JSPB instance for transitional soy proto support:
+ *     http://goto/soy-param-migration
+ * @param {!proto.License} msg The msg instance to transform.
+ * @return {!Object}
+ * @suppress {unusedLocalVariables} f is only used for nested messages
+ */
+proto.License.toObject = function(includeInstance, msg) {
+  var f, obj = {
+    code: (f = jspb.Message.getField(msg, 1)) == null ? undefined : f,
+    attribution: (f = jspb.Message.getField(msg, 2)) == null ? undefined : f,
+    customText: (f = jspb.Message.getField(msg, 3)) == null ? undefined : f
+  };
+
+  if (includeInstance) {
+    obj.$jspbMessageInstance = msg;
+  }
+  return obj;
+};
+}
+
+
+/**
+ * Deserializes binary data (in protobuf wire format).
+ * @param {jspb.ByteSource} bytes The bytes to deserialize.
+ * @return {!proto.License}
+ */
+proto.License.deserializeBinary = function(bytes) {
+  var reader = new jspb.BinaryReader(bytes);
+  var msg = new proto.License;
+  return proto.License.deserializeBinaryFromReader(msg, reader);
+};
+
+
+/**
+ * Deserializes binary data (in protobuf wire format) from the
+ * given reader into the given message object.
+ * @param {!proto.License} msg The message object to deserialize into.
+ * @param {!jspb.BinaryReader} reader The BinaryReader to use.
+ * @return {!proto.License}
+ */
+proto.License.deserializeBinaryFromReader = function(msg, reader) {
+  while (reader.nextField()) {
+    if (reader.isEndGroup()) {
+      break;
+    }
+    var field = reader.getFieldNumber();
+    switch (field) {
+    case 1:
+      var value = /** @type {number} */ (reader.readUint32());
+      msg.setCode(value);
+      break;
+    case 2:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setAttribution(value);
+      break;
+    case 3:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setCustomText(value);
+      break;
+    default:
+      reader.skipField();
+      break;
+    }
+  }
+  return msg;
+};
+
+
+/**
+ * Serializes the message to binary data (in protobuf wire format).
+ * @return {!Uint8Array}
+ */
+proto.License.prototype.serializeBinary = function() {
+  var writer = new jspb.BinaryWriter();
+  proto.License.serializeBinaryToWriter(this, writer);
+  return writer.getResultBuffer();
+};
+
+
+/**
+ * Serializes the given message to binary data (in protobuf wire
+ * format), writing to the given BinaryWriter.
+ * @param {!proto.License} message
+ * @param {!jspb.BinaryWriter} writer
+ * @suppress {unusedLocalVariables} f is only used for nested messages
+ */
+proto.License.serializeBinaryToWriter = function(message, writer) {
+  var f = undefined;
+  f = /** @type {number} */ (jspb.Message.getField(message, 1));
+  if (f != null) {
+    writer.writeUint32(
+      1,
+      f
+    );
+  }
+  f = /** @type {string} */ (jspb.Message.getField(message, 2));
+  if (f != null) {
+    writer.writeString(
+      2,
+      f
+    );
+  }
+  f = /** @type {string} */ (jspb.Message.getField(message, 3));
+  if (f != null) {
+    writer.writeString(
+      3,
+      f
+    );
+  }
+};
+
+
+/**
+ * optional uint32 code = 1;
+ * @return {number}
+ */
+proto.License.prototype.getCode = function() {
+  return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 1, 0));
+};
+
+
+/**
+ * @param {number} value
+ * @return {!proto.License} returns this
+ */
+proto.License.prototype.setCode = function(value) {
+  return jspb.Message.setField(this, 1, value);
+};
+
+
+/**
+ * Clears the field making it undefined.
+ * @return {!proto.License} returns this
+ */
+proto.License.prototype.clearCode = function() {
+  return jspb.Message.setField(this, 1, undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.License.prototype.hasCode = function() {
+  return jspb.Message.getField(this, 1) != null;
+};
+
+
+/**
+ * optional string attribution = 2;
+ * @return {string}
+ */
+proto.License.prototype.getAttribution = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.License} returns this
+ */
+proto.License.prototype.setAttribution = function(value) {
+  return jspb.Message.setField(this, 2, value);
+};
+
+
+/**
+ * Clears the field making it undefined.
+ * @return {!proto.License} returns this
+ */
+proto.License.prototype.clearAttribution = function() {
+  return jspb.Message.setField(this, 2, undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.License.prototype.hasAttribution = function() {
+  return jspb.Message.getField(this, 2) != null;
+};
+
+
+/**
+ * optional string custom_text = 3;
+ * @return {string}
+ */
+proto.License.prototype.getCustomText = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 3, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.License} returns this
+ */
+proto.License.prototype.setCustomText = function(value) {
+  return jspb.Message.setField(this, 3, value);
+};
+
+
+/**
+ * Clears the field making it undefined.
+ * @return {!proto.License} returns this
+ */
+proto.License.prototype.clearCustomText = function() {
+  return jspb.Message.setField(this, 3, undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.License.prototype.hasCustomText = function() {
+  return jspb.Message.getField(this, 3) != null;
+};
+
+
+
+
+
+if (jspb.Message.GENERATE_TO_OBJECT) {
+/**
+ * Creates an object representation of this proto.
+ * Field names that are reserved in JavaScript and will be renamed to pb_name.
+ * Optional fields that are not set will be set to undefined.
+ * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default.
+ * For the list of reserved names please see:
+ *     net/proto2/compiler/js/internal/generator.cc#kKeyword.
+ * @param {boolean=} opt_includeInstance Deprecated. whether to include the
+ *     JSPB instance for transitional soy proto support:
+ *     http://goto/soy-param-migration
+ * @return {!Object}
+ */
+proto.MediaType.prototype.toObject = function(opt_includeInstance) {
+  return proto.MediaType.toObject(opt_includeInstance, this);
+};
+
+
+/**
+ * Static version of the {@see toObject} method.
+ * @param {boolean|undefined} includeInstance Deprecated. Whether to include
+ *     the JSPB instance for transitional soy proto support:
+ *     http://goto/soy-param-migration
+ * @param {!proto.MediaType} msg The msg instance to transform.
+ * @return {!Object}
+ * @suppress {unusedLocalVariables} f is only used for nested messages
+ */
+proto.MediaType.toObject = function(includeInstance, msg) {
+  var f, obj = {
+    codecName: (f = jspb.Message.getField(msg, 1)) == null ? undefined : f,
+    container: (f = jspb.Message.getField(msg, 2)) == null ? undefined : f,
+    mimeMediaType: (f = jspb.Message.getField(msg, 3)) == null ? undefined : f
+  };
+
+  if (includeInstance) {
+    obj.$jspbMessageInstance = msg;
+  }
+  return obj;
+};
+}
+
+
+/**
+ * Deserializes binary data (in protobuf wire format).
+ * @param {jspb.ByteSource} bytes The bytes to deserialize.
+ * @return {!proto.MediaType}
+ */
+proto.MediaType.deserializeBinary = function(bytes) {
+  var reader = new jspb.BinaryReader(bytes);
+  var msg = new proto.MediaType;
+  return proto.MediaType.deserializeBinaryFromReader(msg, reader);
+};
+
+
+/**
+ * Deserializes binary data (in protobuf wire format) from the
+ * given reader into the given message object.
+ * @param {!proto.MediaType} msg The message object to deserialize into.
+ * @param {!jspb.BinaryReader} reader The BinaryReader to use.
+ * @return {!proto.MediaType}
+ */
+proto.MediaType.deserializeBinaryFromReader = function(msg, reader) {
+  while (reader.nextField()) {
+    if (reader.isEndGroup()) {
+      break;
+    }
+    var field = reader.getFieldNumber();
+    switch (field) {
+    case 1:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setCodecName(value);
+      break;
+    case 2:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setContainer(value);
+      break;
+    case 3:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setMimeMediaType(value);
+      break;
+    default:
+      reader.skipField();
+      break;
+    }
+  }
+  return msg;
+};
+
+
+/**
+ * Serializes the message to binary data (in protobuf wire format).
+ * @return {!Uint8Array}
+ */
+proto.MediaType.prototype.serializeBinary = function() {
+  var writer = new jspb.BinaryWriter();
+  proto.MediaType.serializeBinaryToWriter(this, writer);
+  return writer.getResultBuffer();
+};
+
+
+/**
+ * Serializes the given message to binary data (in protobuf wire
+ * format), writing to the given BinaryWriter.
+ * @param {!proto.MediaType} message
+ * @param {!jspb.BinaryWriter} writer
+ * @suppress {unusedLocalVariables} f is only used for nested messages
+ */
+proto.MediaType.serializeBinaryToWriter = function(message, writer) {
+  var f = undefined;
+  f = /** @type {string} */ (jspb.Message.getField(message, 1));
+  if (f != null) {
+    writer.writeString(
+      1,
+      f
+    );
+  }
+  f = /** @type {string} */ (jspb.Message.getField(message, 2));
+  if (f != null) {
+    writer.writeString(
+      2,
+      f
+    );
+  }
+  f = /** @type {string} */ (jspb.Message.getField(message, 3));
+  if (f != null) {
+    writer.writeString(
+      3,
+      f
+    );
+  }
+};
+
+
+/**
+ * optional string codec_name = 1;
+ * @return {string}
+ */
+proto.MediaType.prototype.getCodecName = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.MediaType} returns this
+ */
+proto.MediaType.prototype.setCodecName = function(value) {
+  return jspb.Message.setField(this, 1, value);
+};
+
+
+/**
+ * Clears the field making it undefined.
+ * @return {!proto.MediaType} returns this
+ */
+proto.MediaType.prototype.clearCodecName = function() {
+  return jspb.Message.setField(this, 1, undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.MediaType.prototype.hasCodecName = function() {
+  return jspb.Message.getField(this, 1) != null;
+};
+
+
+/**
+ * optional string container = 2;
+ * @return {string}
+ */
+proto.MediaType.prototype.getContainer = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.MediaType} returns this
+ */
+proto.MediaType.prototype.setContainer = function(value) {
+  return jspb.Message.setField(this, 2, value);
+};
+
+
+/**
+ * Clears the field making it undefined.
+ * @return {!proto.MediaType} returns this
+ */
+proto.MediaType.prototype.clearContainer = function() {
+  return jspb.Message.setField(this, 2, undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.MediaType.prototype.hasContainer = function() {
+  return jspb.Message.getField(this, 2) != null;
+};
+
+
+/**
+ * optional string mime_media_type = 3;
+ * @return {string}
+ */
+proto.MediaType.prototype.getMimeMediaType = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 3, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.MediaType} returns this
+ */
+proto.MediaType.prototype.setMimeMediaType = function(value) {
+  return jspb.Message.setField(this, 3, value);
+};
+
+
+/**
+ * Clears the field making it undefined.
+ * @return {!proto.MediaType} returns this
+ */
+proto.MediaType.prototype.clearMimeMediaType = function() {
+  return jspb.Message.setField(this, 3, undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.MediaType.prototype.hasMimeMediaType = function() {
+  return jspb.Message.getField(this, 3) != null;
+};
+
+
+
+/**
+ * List of repeated fields within this message type.
+ * @private {!Array<number>}
+ * @const
+ */
+proto.VideoMetadata.repeatedFields_ = [15];
+
+
+
+if (jspb.Message.GENERATE_TO_OBJECT) {
+/**
+ * Creates an object representation of this proto.
+ * Field names that are reserved in JavaScript and will be renamed to pb_name.
+ * Optional fields that are not set will be set to undefined.
+ * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default.
+ * For the list of reserved names please see:
+ *     net/proto2/compiler/js/internal/generator.cc#kKeyword.
+ * @param {boolean=} opt_includeInstance Deprecated. whether to include the
+ *     JSPB instance for transitional soy proto support:
+ *     http://goto/soy-param-migration
+ * @return {!Object}
+ */
+proto.VideoMetadata.prototype.toObject = function(opt_includeInstance) {
+  return proto.VideoMetadata.toObject(opt_includeInstance, this);
+};
+
+
+/**
+ * Static version of the {@see toObject} method.
+ * @param {boolean|undefined} includeInstance Deprecated. Whether to include
+ *     the JSPB instance for transitional soy proto support:
+ *     http://goto/soy-param-migration
+ * @param {!proto.VideoMetadata} msg The msg instance to transform.
+ * @return {!Object}
+ * @suppress {unusedLocalVariables} f is only used for nested messages
+ */
+proto.VideoMetadata.toObject = function(includeInstance, msg) {
+  var f, obj = {
+    title: (f = jspb.Message.getField(msg, 1)) == null ? undefined : f,
+    description: (f = jspb.Message.getField(msg, 2)) == null ? undefined : f,
+    video: (f = jspb.Message.getField(msg, 3)) == null ? undefined : f,
+    thumbnailPhoto: (f = jspb.Message.getField(msg, 4)) == null ? undefined : f,
+    duration: (f = jspb.Message.getField(msg, 5)) == null ? undefined : f,
+    mediaPixelHeight: (f = jspb.Message.getField(msg, 6)) == null ? undefined : f,
+    mediaPixelWidth: (f = jspb.Message.getField(msg, 7)) == null ? undefined : f,
+    mediaType: (f = msg.getMediaType()) && proto.MediaType.toObject(includeInstance, f),
+    language: (f = jspb.Message.getField(msg, 9)) == null ? undefined : f,
+    license: (f = msg.getLicense()) && proto.License.toObject(includeInstance, f),
+    publishedBeforeJoystream: (f = msg.getPublishedBeforeJoystream()) && proto.PublishedBeforeJoystream.toObject(includeInstance, f),
+    hasMarketing: (f = jspb.Message.getBooleanField(msg, 12)) == null ? undefined : f,
+    isPublic: (f = jspb.Message.getBooleanField(msg, 13)) == null ? undefined : f,
+    isExplicit: (f = jspb.Message.getBooleanField(msg, 14)) == null ? undefined : f,
+    personsList: (f = jspb.Message.getRepeatedField(msg, 15)) == null ? undefined : f,
+    category: (f = jspb.Message.getField(msg, 16)) == null ? undefined : f
+  };
+
+  if (includeInstance) {
+    obj.$jspbMessageInstance = msg;
+  }
+  return obj;
+};
+}
+
+
+/**
+ * Deserializes binary data (in protobuf wire format).
+ * @param {jspb.ByteSource} bytes The bytes to deserialize.
+ * @return {!proto.VideoMetadata}
+ */
+proto.VideoMetadata.deserializeBinary = function(bytes) {
+  var reader = new jspb.BinaryReader(bytes);
+  var msg = new proto.VideoMetadata;
+  return proto.VideoMetadata.deserializeBinaryFromReader(msg, reader);
+};
+
+
+/**
+ * Deserializes binary data (in protobuf wire format) from the
+ * given reader into the given message object.
+ * @param {!proto.VideoMetadata} msg The message object to deserialize into.
+ * @param {!jspb.BinaryReader} reader The BinaryReader to use.
+ * @return {!proto.VideoMetadata}
+ */
+proto.VideoMetadata.deserializeBinaryFromReader = function(msg, reader) {
+  while (reader.nextField()) {
+    if (reader.isEndGroup()) {
+      break;
+    }
+    var field = reader.getFieldNumber();
+    switch (field) {
+    case 1:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setTitle(value);
+      break;
+    case 2:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setDescription(value);
+      break;
+    case 3:
+      var value = /** @type {number} */ (reader.readUint32());
+      msg.setVideo(value);
+      break;
+    case 4:
+      var value = /** @type {number} */ (reader.readUint32());
+      msg.setThumbnailPhoto(value);
+      break;
+    case 5:
+      var value = /** @type {number} */ (reader.readUint32());
+      msg.setDuration(value);
+      break;
+    case 6:
+      var value = /** @type {number} */ (reader.readUint32());
+      msg.setMediaPixelHeight(value);
+      break;
+    case 7:
+      var value = /** @type {number} */ (reader.readUint32());
+      msg.setMediaPixelWidth(value);
+      break;
+    case 8:
+      var value = new proto.MediaType;
+      reader.readMessage(value,proto.MediaType.deserializeBinaryFromReader);
+      msg.setMediaType(value);
+      break;
+    case 9:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setLanguage(value);
+      break;
+    case 10:
+      var value = new proto.License;
+      reader.readMessage(value,proto.License.deserializeBinaryFromReader);
+      msg.setLicense(value);
+      break;
+    case 11:
+      var value = new proto.PublishedBeforeJoystream;
+      reader.readMessage(value,proto.PublishedBeforeJoystream.deserializeBinaryFromReader);
+      msg.setPublishedBeforeJoystream(value);
+      break;
+    case 12:
+      var value = /** @type {boolean} */ (reader.readBool());
+      msg.setHasMarketing(value);
+      break;
+    case 13:
+      var value = /** @type {boolean} */ (reader.readBool());
+      msg.setIsPublic(value);
+      break;
+    case 14:
+      var value = /** @type {boolean} */ (reader.readBool());
+      msg.setIsExplicit(value);
+      break;
+    case 15:
+      var values = /** @type {!Array<number>} */ (reader.isDelimited() ? reader.readPackedUint64() : [reader.readUint64()]);
+      for (var i = 0; i < values.length; i++) {
+        msg.addPersons(values[i]);
+      }
+      break;
+    case 16:
+      var value = /** @type {number} */ (reader.readUint64());
+      msg.setCategory(value);
+      break;
+    default:
+      reader.skipField();
+      break;
+    }
+  }
+  return msg;
+};
+
+
+/**
+ * Serializes the message to binary data (in protobuf wire format).
+ * @return {!Uint8Array}
+ */
+proto.VideoMetadata.prototype.serializeBinary = function() {
+  var writer = new jspb.BinaryWriter();
+  proto.VideoMetadata.serializeBinaryToWriter(this, writer);
+  return writer.getResultBuffer();
+};
+
+
+/**
+ * Serializes the given message to binary data (in protobuf wire
+ * format), writing to the given BinaryWriter.
+ * @param {!proto.VideoMetadata} message
+ * @param {!jspb.BinaryWriter} writer
+ * @suppress {unusedLocalVariables} f is only used for nested messages
+ */
+proto.VideoMetadata.serializeBinaryToWriter = function(message, writer) {
+  var f = undefined;
+  f = /** @type {string} */ (jspb.Message.getField(message, 1));
+  if (f != null) {
+    writer.writeString(
+      1,
+      f
+    );
+  }
+  f = /** @type {string} */ (jspb.Message.getField(message, 2));
+  if (f != null) {
+    writer.writeString(
+      2,
+      f
+    );
+  }
+  f = /** @type {number} */ (jspb.Message.getField(message, 3));
+  if (f != null) {
+    writer.writeUint32(
+      3,
+      f
+    );
+  }
+  f = /** @type {number} */ (jspb.Message.getField(message, 4));
+  if (f != null) {
+    writer.writeUint32(
+      4,
+      f
+    );
+  }
+  f = /** @type {number} */ (jspb.Message.getField(message, 5));
+  if (f != null) {
+    writer.writeUint32(
+      5,
+      f
+    );
+  }
+  f = /** @type {number} */ (jspb.Message.getField(message, 6));
+  if (f != null) {
+    writer.writeUint32(
+      6,
+      f
+    );
+  }
+  f = /** @type {number} */ (jspb.Message.getField(message, 7));
+  if (f != null) {
+    writer.writeUint32(
+      7,
+      f
+    );
+  }
+  f = message.getMediaType();
+  if (f != null) {
+    writer.writeMessage(
+      8,
+      f,
+      proto.MediaType.serializeBinaryToWriter
+    );
+  }
+  f = /** @type {string} */ (jspb.Message.getField(message, 9));
+  if (f != null) {
+    writer.writeString(
+      9,
+      f
+    );
+  }
+  f = message.getLicense();
+  if (f != null) {
+    writer.writeMessage(
+      10,
+      f,
+      proto.License.serializeBinaryToWriter
+    );
+  }
+  f = message.getPublishedBeforeJoystream();
+  if (f != null) {
+    writer.writeMessage(
+      11,
+      f,
+      proto.PublishedBeforeJoystream.serializeBinaryToWriter
+    );
+  }
+  f = /** @type {boolean} */ (jspb.Message.getField(message, 12));
+  if (f != null) {
+    writer.writeBool(
+      12,
+      f
+    );
+  }
+  f = /** @type {boolean} */ (jspb.Message.getField(message, 13));
+  if (f != null) {
+    writer.writeBool(
+      13,
+      f
+    );
+  }
+  f = /** @type {boolean} */ (jspb.Message.getField(message, 14));
+  if (f != null) {
+    writer.writeBool(
+      14,
+      f
+    );
+  }
+  f = message.getPersonsList();
+  if (f.length > 0) {
+    writer.writePackedUint64(
+      15,
+      f
+    );
+  }
+  f = /** @type {number} */ (jspb.Message.getField(message, 16));
+  if (f != null) {
+    writer.writeUint64(
+      16,
+      f
+    );
+  }
+};
+
+
+/**
+ * optional string title = 1;
+ * @return {string}
+ */
+proto.VideoMetadata.prototype.getTitle = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.VideoMetadata} returns this
+ */
+proto.VideoMetadata.prototype.setTitle = function(value) {
+  return jspb.Message.setField(this, 1, value);
+};
+
+
+/**
+ * Clears the field making it undefined.
+ * @return {!proto.VideoMetadata} returns this
+ */
+proto.VideoMetadata.prototype.clearTitle = function() {
+  return jspb.Message.setField(this, 1, undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.VideoMetadata.prototype.hasTitle = function() {
+  return jspb.Message.getField(this, 1) != null;
+};
+
+
+/**
+ * optional string description = 2;
+ * @return {string}
+ */
+proto.VideoMetadata.prototype.getDescription = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.VideoMetadata} returns this
+ */
+proto.VideoMetadata.prototype.setDescription = function(value) {
+  return jspb.Message.setField(this, 2, value);
+};
+
+
+/**
+ * Clears the field making it undefined.
+ * @return {!proto.VideoMetadata} returns this
+ */
+proto.VideoMetadata.prototype.clearDescription = function() {
+  return jspb.Message.setField(this, 2, undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.VideoMetadata.prototype.hasDescription = function() {
+  return jspb.Message.getField(this, 2) != null;
+};
+
+
+/**
+ * optional uint32 video = 3;
+ * @return {number}
+ */
+proto.VideoMetadata.prototype.getVideo = function() {
+  return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 3, 0));
+};
+
+
+/**
+ * @param {number} value
+ * @return {!proto.VideoMetadata} returns this
+ */
+proto.VideoMetadata.prototype.setVideo = function(value) {
+  return jspb.Message.setField(this, 3, value);
+};
+
+
+/**
+ * Clears the field making it undefined.
+ * @return {!proto.VideoMetadata} returns this
+ */
+proto.VideoMetadata.prototype.clearVideo = function() {
+  return jspb.Message.setField(this, 3, undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.VideoMetadata.prototype.hasVideo = function() {
+  return jspb.Message.getField(this, 3) != null;
+};
+
+
+/**
+ * optional uint32 thumbnail_photo = 4;
+ * @return {number}
+ */
+proto.VideoMetadata.prototype.getThumbnailPhoto = function() {
+  return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 4, 0));
+};
+
+
+/**
+ * @param {number} value
+ * @return {!proto.VideoMetadata} returns this
+ */
+proto.VideoMetadata.prototype.setThumbnailPhoto = function(value) {
+  return jspb.Message.setField(this, 4, value);
+};
+
+
+/**
+ * Clears the field making it undefined.
+ * @return {!proto.VideoMetadata} returns this
+ */
+proto.VideoMetadata.prototype.clearThumbnailPhoto = function() {
+  return jspb.Message.setField(this, 4, undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.VideoMetadata.prototype.hasThumbnailPhoto = function() {
+  return jspb.Message.getField(this, 4) != null;
+};
+
+
+/**
+ * optional uint32 duration = 5;
+ * @return {number}
+ */
+proto.VideoMetadata.prototype.getDuration = function() {
+  return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 5, 0));
+};
+
+
+/**
+ * @param {number} value
+ * @return {!proto.VideoMetadata} returns this
+ */
+proto.VideoMetadata.prototype.setDuration = function(value) {
+  return jspb.Message.setField(this, 5, value);
+};
+
+
+/**
+ * Clears the field making it undefined.
+ * @return {!proto.VideoMetadata} returns this
+ */
+proto.VideoMetadata.prototype.clearDuration = function() {
+  return jspb.Message.setField(this, 5, undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.VideoMetadata.prototype.hasDuration = function() {
+  return jspb.Message.getField(this, 5) != null;
+};
+
+
+/**
+ * optional uint32 media_pixel_height = 6;
+ * @return {number}
+ */
+proto.VideoMetadata.prototype.getMediaPixelHeight = function() {
+  return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 6, 0));
+};
+
+
+/**
+ * @param {number} value
+ * @return {!proto.VideoMetadata} returns this
+ */
+proto.VideoMetadata.prototype.setMediaPixelHeight = function(value) {
+  return jspb.Message.setField(this, 6, value);
+};
+
+
+/**
+ * Clears the field making it undefined.
+ * @return {!proto.VideoMetadata} returns this
+ */
+proto.VideoMetadata.prototype.clearMediaPixelHeight = function() {
+  return jspb.Message.setField(this, 6, undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.VideoMetadata.prototype.hasMediaPixelHeight = function() {
+  return jspb.Message.getField(this, 6) != null;
+};
+
+
+/**
+ * optional uint32 media_pixel_width = 7;
+ * @return {number}
+ */
+proto.VideoMetadata.prototype.getMediaPixelWidth = function() {
+  return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 7, 0));
+};
+
+
+/**
+ * @param {number} value
+ * @return {!proto.VideoMetadata} returns this
+ */
+proto.VideoMetadata.prototype.setMediaPixelWidth = function(value) {
+  return jspb.Message.setField(this, 7, value);
+};
+
+
+/**
+ * Clears the field making it undefined.
+ * @return {!proto.VideoMetadata} returns this
+ */
+proto.VideoMetadata.prototype.clearMediaPixelWidth = function() {
+  return jspb.Message.setField(this, 7, undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.VideoMetadata.prototype.hasMediaPixelWidth = function() {
+  return jspb.Message.getField(this, 7) != null;
+};
+
+
+/**
+ * optional MediaType media_type = 8;
+ * @return {?proto.MediaType}
+ */
+proto.VideoMetadata.prototype.getMediaType = function() {
+  return /** @type{?proto.MediaType} */ (
+    jspb.Message.getWrapperField(this, proto.MediaType, 8));
+};
+
+
+/**
+ * @param {?proto.MediaType|undefined} value
+ * @return {!proto.VideoMetadata} returns this
+*/
+proto.VideoMetadata.prototype.setMediaType = function(value) {
+  return jspb.Message.setWrapperField(this, 8, value);
+};
+
+
+/**
+ * Clears the message field making it undefined.
+ * @return {!proto.VideoMetadata} returns this
+ */
+proto.VideoMetadata.prototype.clearMediaType = function() {
+  return this.setMediaType(undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.VideoMetadata.prototype.hasMediaType = function() {
+  return jspb.Message.getField(this, 8) != null;
+};
+
+
+/**
+ * optional string language = 9;
+ * @return {string}
+ */
+proto.VideoMetadata.prototype.getLanguage = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 9, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.VideoMetadata} returns this
+ */
+proto.VideoMetadata.prototype.setLanguage = function(value) {
+  return jspb.Message.setField(this, 9, value);
+};
+
+
+/**
+ * Clears the field making it undefined.
+ * @return {!proto.VideoMetadata} returns this
+ */
+proto.VideoMetadata.prototype.clearLanguage = function() {
+  return jspb.Message.setField(this, 9, undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.VideoMetadata.prototype.hasLanguage = function() {
+  return jspb.Message.getField(this, 9) != null;
+};
+
+
+/**
+ * optional License license = 10;
+ * @return {?proto.License}
+ */
+proto.VideoMetadata.prototype.getLicense = function() {
+  return /** @type{?proto.License} */ (
+    jspb.Message.getWrapperField(this, proto.License, 10));
+};
+
+
+/**
+ * @param {?proto.License|undefined} value
+ * @return {!proto.VideoMetadata} returns this
+*/
+proto.VideoMetadata.prototype.setLicense = function(value) {
+  return jspb.Message.setWrapperField(this, 10, value);
+};
+
+
+/**
+ * Clears the message field making it undefined.
+ * @return {!proto.VideoMetadata} returns this
+ */
+proto.VideoMetadata.prototype.clearLicense = function() {
+  return this.setLicense(undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.VideoMetadata.prototype.hasLicense = function() {
+  return jspb.Message.getField(this, 10) != null;
+};
+
+
+/**
+ * optional PublishedBeforeJoystream published_before_joystream = 11;
+ * @return {?proto.PublishedBeforeJoystream}
+ */
+proto.VideoMetadata.prototype.getPublishedBeforeJoystream = function() {
+  return /** @type{?proto.PublishedBeforeJoystream} */ (
+    jspb.Message.getWrapperField(this, proto.PublishedBeforeJoystream, 11));
+};
+
+
+/**
+ * @param {?proto.PublishedBeforeJoystream|undefined} value
+ * @return {!proto.VideoMetadata} returns this
+*/
+proto.VideoMetadata.prototype.setPublishedBeforeJoystream = function(value) {
+  return jspb.Message.setWrapperField(this, 11, value);
+};
+
+
+/**
+ * Clears the message field making it undefined.
+ * @return {!proto.VideoMetadata} returns this
+ */
+proto.VideoMetadata.prototype.clearPublishedBeforeJoystream = function() {
+  return this.setPublishedBeforeJoystream(undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.VideoMetadata.prototype.hasPublishedBeforeJoystream = function() {
+  return jspb.Message.getField(this, 11) != null;
+};
+
+
+/**
+ * optional bool has_marketing = 12;
+ * @return {boolean}
+ */
+proto.VideoMetadata.prototype.getHasMarketing = function() {
+  return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 12, false));
+};
+
+
+/**
+ * @param {boolean} value
+ * @return {!proto.VideoMetadata} returns this
+ */
+proto.VideoMetadata.prototype.setHasMarketing = function(value) {
+  return jspb.Message.setField(this, 12, value);
+};
+
+
+/**
+ * Clears the field making it undefined.
+ * @return {!proto.VideoMetadata} returns this
+ */
+proto.VideoMetadata.prototype.clearHasMarketing = function() {
+  return jspb.Message.setField(this, 12, undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.VideoMetadata.prototype.hasHasMarketing = function() {
+  return jspb.Message.getField(this, 12) != null;
+};
+
+
+/**
+ * optional bool is_public = 13;
+ * @return {boolean}
+ */
+proto.VideoMetadata.prototype.getIsPublic = function() {
+  return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 13, false));
+};
+
+
+/**
+ * @param {boolean} value
+ * @return {!proto.VideoMetadata} returns this
+ */
+proto.VideoMetadata.prototype.setIsPublic = function(value) {
+  return jspb.Message.setField(this, 13, value);
+};
+
+
+/**
+ * Clears the field making it undefined.
+ * @return {!proto.VideoMetadata} returns this
+ */
+proto.VideoMetadata.prototype.clearIsPublic = function() {
+  return jspb.Message.setField(this, 13, undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.VideoMetadata.prototype.hasIsPublic = function() {
+  return jspb.Message.getField(this, 13) != null;
+};
+
+
+/**
+ * optional bool is_explicit = 14;
+ * @return {boolean}
+ */
+proto.VideoMetadata.prototype.getIsExplicit = function() {
+  return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 14, false));
+};
+
+
+/**
+ * @param {boolean} value
+ * @return {!proto.VideoMetadata} returns this
+ */
+proto.VideoMetadata.prototype.setIsExplicit = function(value) {
+  return jspb.Message.setField(this, 14, value);
+};
+
+
+/**
+ * Clears the field making it undefined.
+ * @return {!proto.VideoMetadata} returns this
+ */
+proto.VideoMetadata.prototype.clearIsExplicit = function() {
+  return jspb.Message.setField(this, 14, undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.VideoMetadata.prototype.hasIsExplicit = function() {
+  return jspb.Message.getField(this, 14) != null;
+};
+
+
+/**
+ * repeated uint64 persons = 15;
+ * @return {!Array<number>}
+ */
+proto.VideoMetadata.prototype.getPersonsList = function() {
+  return /** @type {!Array<number>} */ (jspb.Message.getRepeatedField(this, 15));
+};
+
+
+/**
+ * @param {!Array<number>} value
+ * @return {!proto.VideoMetadata} returns this
+ */
+proto.VideoMetadata.prototype.setPersonsList = function(value) {
+  return jspb.Message.setField(this, 15, value || []);
+};
+
+
+/**
+ * @param {number} value
+ * @param {number=} opt_index
+ * @return {!proto.VideoMetadata} returns this
+ */
+proto.VideoMetadata.prototype.addPersons = function(value, opt_index) {
+  return jspb.Message.addToRepeatedField(this, 15, value, opt_index);
+};
+
+
+/**
+ * Clears the list making it empty but non-null.
+ * @return {!proto.VideoMetadata} returns this
+ */
+proto.VideoMetadata.prototype.clearPersonsList = function() {
+  return this.setPersonsList([]);
+};
+
+
+/**
+ * optional uint64 category = 16;
+ * @return {number}
+ */
+proto.VideoMetadata.prototype.getCategory = function() {
+  return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 16, 0));
+};
+
+
+/**
+ * @param {number} value
+ * @return {!proto.VideoMetadata} returns this
+ */
+proto.VideoMetadata.prototype.setCategory = function(value) {
+  return jspb.Message.setField(this, 16, value);
+};
+
+
+/**
+ * Clears the field making it undefined.
+ * @return {!proto.VideoMetadata} returns this
+ */
+proto.VideoMetadata.prototype.clearCategory = function() {
+  return jspb.Message.setField(this, 16, undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.VideoMetadata.prototype.hasCategory = function() {
+  return jspb.Message.getField(this, 16) != null;
+};
+
+
+
+
+
+if (jspb.Message.GENERATE_TO_OBJECT) {
+/**
+ * Creates an object representation of this proto.
+ * Field names that are reserved in JavaScript and will be renamed to pb_name.
+ * Optional fields that are not set will be set to undefined.
+ * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default.
+ * For the list of reserved names please see:
+ *     net/proto2/compiler/js/internal/generator.cc#kKeyword.
+ * @param {boolean=} opt_includeInstance Deprecated. whether to include the
+ *     JSPB instance for transitional soy proto support:
+ *     http://goto/soy-param-migration
+ * @return {!Object}
+ */
+proto.VideoCategoryMetadata.prototype.toObject = function(opt_includeInstance) {
+  return proto.VideoCategoryMetadata.toObject(opt_includeInstance, this);
+};
+
+
+/**
+ * Static version of the {@see toObject} method.
+ * @param {boolean|undefined} includeInstance Deprecated. Whether to include
+ *     the JSPB instance for transitional soy proto support:
+ *     http://goto/soy-param-migration
+ * @param {!proto.VideoCategoryMetadata} msg The msg instance to transform.
+ * @return {!Object}
+ * @suppress {unusedLocalVariables} f is only used for nested messages
+ */
+proto.VideoCategoryMetadata.toObject = function(includeInstance, msg) {
+  var f, obj = {
+    name: (f = jspb.Message.getField(msg, 1)) == null ? undefined : f
+  };
+
+  if (includeInstance) {
+    obj.$jspbMessageInstance = msg;
+  }
+  return obj;
+};
+}
+
+
+/**
+ * Deserializes binary data (in protobuf wire format).
+ * @param {jspb.ByteSource} bytes The bytes to deserialize.
+ * @return {!proto.VideoCategoryMetadata}
+ */
+proto.VideoCategoryMetadata.deserializeBinary = function(bytes) {
+  var reader = new jspb.BinaryReader(bytes);
+  var msg = new proto.VideoCategoryMetadata;
+  return proto.VideoCategoryMetadata.deserializeBinaryFromReader(msg, reader);
+};
+
+
+/**
+ * Deserializes binary data (in protobuf wire format) from the
+ * given reader into the given message object.
+ * @param {!proto.VideoCategoryMetadata} msg The message object to deserialize into.
+ * @param {!jspb.BinaryReader} reader The BinaryReader to use.
+ * @return {!proto.VideoCategoryMetadata}
+ */
+proto.VideoCategoryMetadata.deserializeBinaryFromReader = function(msg, reader) {
+  while (reader.nextField()) {
+    if (reader.isEndGroup()) {
+      break;
+    }
+    var field = reader.getFieldNumber();
+    switch (field) {
+    case 1:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setName(value);
+      break;
+    default:
+      reader.skipField();
+      break;
+    }
+  }
+  return msg;
+};
+
+
+/**
+ * Serializes the message to binary data (in protobuf wire format).
+ * @return {!Uint8Array}
+ */
+proto.VideoCategoryMetadata.prototype.serializeBinary = function() {
+  var writer = new jspb.BinaryWriter();
+  proto.VideoCategoryMetadata.serializeBinaryToWriter(this, writer);
+  return writer.getResultBuffer();
+};
+
+
+/**
+ * Serializes the given message to binary data (in protobuf wire
+ * format), writing to the given BinaryWriter.
+ * @param {!proto.VideoCategoryMetadata} message
+ * @param {!jspb.BinaryWriter} writer
+ * @suppress {unusedLocalVariables} f is only used for nested messages
+ */
+proto.VideoCategoryMetadata.serializeBinaryToWriter = function(message, writer) {
+  var f = undefined;
+  f = /** @type {string} */ (jspb.Message.getField(message, 1));
+  if (f != null) {
+    writer.writeString(
+      1,
+      f
+    );
+  }
+};
+
+
+/**
+ * optional string name = 1;
+ * @return {string}
+ */
+proto.VideoCategoryMetadata.prototype.getName = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.VideoCategoryMetadata} returns this
+ */
+proto.VideoCategoryMetadata.prototype.setName = function(value) {
+  return jspb.Message.setField(this, 1, value);
+};
+
+
+/**
+ * Clears the field making it undefined.
+ * @return {!proto.VideoCategoryMetadata} returns this
+ */
+proto.VideoCategoryMetadata.prototype.clearName = function() {
+  return jspb.Message.setField(this, 1, undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.VideoCategoryMetadata.prototype.hasName = function() {
+  return jspb.Message.getField(this, 1) != null;
+};
+
+
+goog.object.extend(exports, proto);

+ 41 - 0
content-metadata-protobuf/doc-appendix.md

@@ -0,0 +1,41 @@
+<!-- 
+    This extra documentation will be appended to the generated docs.
+-->
+
+## Referencing Assets
+<a name=".Assets"></a>
+
+Applications that process messages that contain a `uint32` field that references an asset such as a cover photo or video, should interpret this value as a zero based index into an array/vector that is received external (out of band) to the protobuf message.
+
+Example in context of query-node processing the runtime event `VideoCreated`
+
+```rust
+// Runtime event associated with creating a Video
+VideoCreated(video_id: VideoId, video: Video, assets: Vec<NewAsset>, params: VideoCreationParameters)
+
+struct VideoCreationParameters {
+  in_category: VideoCategoryId,
+  // binary serialized VideoMetadata protobuf message
+  meta: Vec<u8>,
+}
+
+// suppose assets is a vector of two elements. This is the "out of band" array being referenced by the VideoMetadata message
+assets = [
+    NewAsset::Uri("https://mydomain.net/thumbnail.png"),
+    NewAsset::Upload({
+       content_id,
+       ipfs_hash,
+       size,
+       ...
+    }),
+];
+
+meta = VideoMetadata {
+    ...
+    // refers to second element: assets[1] which is being uploaded to the storage system
+    video: 1,
+    // refers to the first element assets[0] which is being referneced by a url string.
+    thumbnail_photo: 0,
+    ...
+};
+```

+ 374 - 0
content-metadata-protobuf/doc/index.md

@@ -0,0 +1,374 @@
+# Protocol Documentation
+<a name="top"></a>
+
+## Table of Contents
+
+- [proto/Channel.proto](#proto/Channel.proto)
+    - [ChannelCategoryMetadata](#.ChannelCategoryMetadata)
+    - [ChannelMetadata](#.ChannelMetadata)
+  
+- [proto/Person.proto](#proto/Person.proto)
+    - [PersonMetadata](#.PersonMetadata)
+  
+- [proto/Playlist.proto](#proto/Playlist.proto)
+    - [PlaylistMetadata](#.PlaylistMetadata)
+  
+- [proto/Series.proto](#proto/Series.proto)
+    - [SeasonMetadata](#.SeasonMetadata)
+    - [SeriesMetadata](#.SeriesMetadata)
+  
+- [proto/Video.proto](#proto/Video.proto)
+    - [License](#.License)
+    - [MediaType](#.MediaType)
+    - [PublishedBeforeJoystream](#.PublishedBeforeJoystream)
+    - [VideoCategoryMetadata](#.VideoCategoryMetadata)
+    - [VideoMetadata](#.VideoMetadata)
+  
+- [Scalar Value Types](#scalar-value-types)
+
+
+
+<a name="proto/Channel.proto"></a>
+<p align="right"><a href="#top">Top</a></p>
+
+## proto/Channel.proto
+
+
+
+<a name=".ChannelCategoryMetadata"></a>
+
+### ChannelCategoryMetadata
+
+
+
+| Field | Type | Label | Description |
+| ----- | ---- | ----- | ----------- |
+| name | [string](#string) | optional | Category Name |
+
+
+
+
+
+
+<a name=".ChannelMetadata"></a>
+
+### ChannelMetadata
+
+
+
+| Field | Type | Label | Description |
+| ----- | ---- | ----- | ----------- |
+| title | [string](#string) | optional | Channel Title |
+| description | [string](#string) | optional | Channel Description |
+| is_public | [bool](#bool) | optional | Wether to display channel to the public |
+| language | [string](#string) | optional | ISO_639-1 Language [Code](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) |
+| cover_photo | [uint32](#uint32) | optional | index into external [assets array](#.Assets) |
+| avatar_photo | [uint32](#uint32) | optional | index into external [assets array](#.Assets) |
+| category | [uint64](#uint64) | optional | Channel Category Id |
+
+
+
+
+
+ 
+
+ 
+
+ 
+
+ 
+
+
+
+<a name="proto/Person.proto"></a>
+<p align="right"><a href="#top">Top</a></p>
+
+## proto/Person.proto
+
+
+
+<a name=".PersonMetadata"></a>
+
+### PersonMetadata
+
+
+
+| Field | Type | Label | Description |
+| ----- | ---- | ----- | ----------- |
+| first_name | [string](#string) | optional |  |
+| middle_name | [string](#string) | optional |  |
+| last_name | [string](#string) | optional |  |
+| about | [string](#string) | optional |  |
+| cover_photo | [uint32](#uint32) | optional | index into external [assets array](#.Assets) |
+| avatar_photo | [uint32](#uint32) | optional | index into external [assets array](#.Assets) |
+
+
+
+
+
+ 
+
+ 
+
+ 
+
+ 
+
+
+
+<a name="proto/Playlist.proto"></a>
+<p align="right"><a href="#top">Top</a></p>
+
+## proto/Playlist.proto
+
+
+
+<a name=".PlaylistMetadata"></a>
+
+### PlaylistMetadata
+
+
+
+| Field | Type | Label | Description |
+| ----- | ---- | ----- | ----------- |
+| title | [string](#string) | optional |  |
+| videos | [uint64](#uint64) | repeated | Videos in the playlist |
+
+
+
+
+
+ 
+
+ 
+
+ 
+
+ 
+
+
+
+<a name="proto/Series.proto"></a>
+<p align="right"><a href="#top">Top</a></p>
+
+## proto/Series.proto
+
+
+
+<a name=".SeasonMetadata"></a>
+
+### SeasonMetadata
+
+
+
+| Field | Type | Label | Description |
+| ----- | ---- | ----- | ----------- |
+| title | [string](#string) | optional |  |
+| description | [string](#string) | optional |  |
+| cover_photo | [uint32](#uint32) | optional | index into external [assets array](#.Assets) |
+| persons | [uint64](#uint64) | repeated | Person(s) referenced by PersonId involved in this Season |
+
+
+
+
+
+
+<a name=".SeriesMetadata"></a>
+
+### SeriesMetadata
+
+
+
+| Field | Type | Label | Description |
+| ----- | ---- | ----- | ----------- |
+| title | [string](#string) | optional |  |
+| description | [string](#string) | optional |  |
+| cover_photo | [uint32](#uint32) | optional | index into external [assets array](#.Assets) |
+| persons | [uint64](#uint64) | repeated | Person(s) referenced by PersonId involved in this Series |
+
+
+
+
+
+ 
+
+ 
+
+ 
+
+ 
+
+
+
+<a name="proto/Video.proto"></a>
+<p align="right"><a href="#top">Top</a></p>
+
+## proto/Video.proto
+
+
+
+<a name=".License"></a>
+
+### License
+License types defined by Joystream
+
+
+| Field | Type | Label | Description |
+| ----- | ---- | ----- | ----------- |
+| code | [uint32](#uint32) | optional | License code defined by Joystream. [reference](../src/KnownLicenses.json) |
+| attribution | [string](#string) | optional | Text for licenses that require an attribution |
+| custom_text | [string](#string) | optional | Text for custom license type |
+
+
+
+
+
+
+<a name=".MediaType"></a>
+
+### MediaType
+Codec, Container, MIME media-type information
+
+
+| Field | Type | Label | Description |
+| ----- | ---- | ----- | ----------- |
+| codec_name | [string](#string) | optional | Codec corresponding to `name` field from [FFmpeg](https://github.com/FFmpeg/FFmpeg/blob/master/libavcodec/codec_desc.c) |
+| container | [string](#string) | optional | Video container format, eg. &#39;MP4&#39;, &#39;WebM&#39;, &#39;Ogg&#39; [ref](https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Video_codecs) |
+| mime_media_type | [string](#string) | optional | MIME Media Type, eg. &#39;video/mp4&#39; [ref](https://www.iana.org/assignments/media-types/media-types.xhtml#video) |
+
+
+
+
+
+
+<a name=".PublishedBeforeJoystream"></a>
+
+### PublishedBeforeJoystream
+Publication status before joystream
+
+
+| Field | Type | Label | Description |
+| ----- | ---- | ----- | ----------- |
+| is_published | [bool](#bool) | optional | Was video published before joystream platform |
+| date | [string](#string) | optional | Date of publication: &#39;YYYY-MM-DD&#39; [ISO-8601](https://www.iso.org/iso-8601-date-and-time-format.html) |
+
+
+
+
+
+
+<a name=".VideoCategoryMetadata"></a>
+
+### VideoCategoryMetadata
+
+
+
+| Field | Type | Label | Description |
+| ----- | ---- | ----- | ----------- |
+| name | [string](#string) | optional | Category name |
+
+
+
+
+
+
+<a name=".VideoMetadata"></a>
+
+### VideoMetadata
+
+
+
+| Field | Type | Label | Description |
+| ----- | ---- | ----- | ----------- |
+| title | [string](#string) | optional | Video Title |
+| description | [string](#string) | optional | Video Description |
+| video | [uint32](#uint32) | optional | index into external [assets array](#.Assets) |
+| thumbnail_photo | [uint32](#uint32) | optional | index into external [assets array](#.Assets) |
+| duration | [uint32](#uint32) | optional | Lengths of video in seconds |
+| media_pixel_height | [uint32](#uint32) | optional | Resolution of the video (Height) |
+| media_pixel_width | [uint32](#uint32) | optional | Resolution of the video (Width) |
+| media_type | [MediaType](#MediaType) | optional | Encoding and Container format used |
+| language | [string](#string) | optional | ISO_639-1 Language [Code](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) |
+| license | [License](#License) | optional | License type for the media |
+| published_before_joystream | [PublishedBeforeJoystream](#PublishedBeforeJoystream) | optional | Date of publication |
+| has_marketing | [bool](#bool) | optional | Does video have marketing or advertising in the stream |
+| is_public | [bool](#bool) | optional | Should video be publicy visible yet |
+| is_explicit | [bool](#bool) | optional | Does Video have explicit language or scenes |
+| persons | [uint64](#uint64) | repeated | Person(s) referenced by PersonId involved in this video |
+| category | [uint64](#uint64) | optional | Video Category Id |
+
+
+
+
+
+ 
+
+ 
+
+ 
+
+ 
+
+
+
+## Scalar Value Types
+
+| .proto Type | Notes | C++ | Java | Python | Go | C# | PHP | Ruby |
+| ----------- | ----- | --- | ---- | ------ | -- | -- | --- | ---- |
+| <a name="double" /> double |  | double | double | float | float64 | double | float | Float |
+| <a name="float" /> float |  | float | float | float | float32 | float | float | Float |
+| <a name="int32" /> int32 | Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint32 instead. | int32 | int | int | int32 | int | integer | Bignum or Fixnum (as required) |
+| <a name="int64" /> int64 | Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint64 instead. | int64 | long | int/long | int64 | long | integer/string | Bignum |
+| <a name="uint32" /> uint32 | Uses variable-length encoding. | uint32 | int | int/long | uint32 | uint | integer | Bignum or Fixnum (as required) |
+| <a name="uint64" /> uint64 | Uses variable-length encoding. | uint64 | long | int/long | uint64 | ulong | integer/string | Bignum or Fixnum (as required) |
+| <a name="sint32" /> sint32 | Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int32s. | int32 | int | int | int32 | int | integer | Bignum or Fixnum (as required) |
+| <a name="sint64" /> sint64 | Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int64s. | int64 | long | int/long | int64 | long | integer/string | Bignum |
+| <a name="fixed32" /> fixed32 | Always four bytes. More efficient than uint32 if values are often greater than 2^28. | uint32 | int | int | uint32 | uint | integer | Bignum or Fixnum (as required) |
+| <a name="fixed64" /> fixed64 | Always eight bytes. More efficient than uint64 if values are often greater than 2^56. | uint64 | long | int/long | uint64 | ulong | integer/string | Bignum |
+| <a name="sfixed32" /> sfixed32 | Always four bytes. | int32 | int | int | int32 | int | integer | Bignum or Fixnum (as required) |
+| <a name="sfixed64" /> sfixed64 | Always eight bytes. | int64 | long | int/long | int64 | long | integer/string | Bignum |
+| <a name="bool" /> bool |  | bool | boolean | boolean | bool | bool | boolean | TrueClass/FalseClass |
+| <a name="string" /> string | A string must always contain UTF-8 encoded or 7-bit ASCII text. | string | String | str/unicode | string | string | string | String (UTF-8) |
+| <a name="bytes" /> bytes | May contain any arbitrary sequence of bytes. | string | ByteString | str | []byte | ByteString | string | String (ASCII-8BIT) |
+
+<!-- 
+    This extra documentation will be appended to the generated docs.
+-->
+
+## Referencing Assets
+<a name=".Assets"></a>
+
+Applications that process messages that contain a `uint32` field that references an asset such as a cover photo or video, should interpret this value as a zero based index into an array/vector that is received external (out of band) to the protobuf message.
+
+Example in context of query-node processing the runtime event `VideoCreated`
+
+```rust
+// Runtime event associated with creating a Video
+VideoCreated(video_id: VideoId, video: Video, assets: Vec<NewAsset>, params: VideoCreationParameters)
+
+struct VideoCreationParameters {
+  in_category: VideoCategoryId,
+  // binary serialized VideoMetadata protobuf message
+  meta: Vec<u8>,
+}
+
+// suppose assets is a vector of two elements. This is the "out of band" array being referenced by the VideoMetadata message
+assets = [
+    NewAsset::Uri("https://mydomain.net/thumbnail.png"),
+    NewAsset::Upload({
+       content_id,
+       ipfs_hash,
+       size,
+       ...
+    }),
+];
+
+meta = VideoMetadata {
+    ...
+    // refers to second element: assets[1] which is being uploaded to the storage system
+    video: 1,
+    // refers to the first element assets[0] which is being referneced by a url string.
+    thumbnail_photo: 0,
+    ...
+};
+```

+ 13 - 0
content-metadata-protobuf/generate-md-doc.sh

@@ -0,0 +1,13 @@
+#!/usr/bin/env bash
+
+# Directory to write generated documentation to
+OUT_DIR_DOC="./doc"
+mkdir -p ${OUT_DIR_DOC}
+
+# Gernerate Markdown docs
+protoc \
+    --doc_out="${OUT_DIR_DOC}" --doc_opt=markdown,index.md \
+    proto/*.proto
+
+# Append some custom docs to generated protocol docs
+cat doc-appendix.md >> ${OUT_DIR_DOC}/index.md

+ 41 - 0
content-metadata-protobuf/package.json

@@ -0,0 +1,41 @@
+{
+  "name": "@joystream/content-metadata-protobuf",
+  "version": "1.0.0",
+  "description": "Joystream Content Metadata Protobuf Library ",
+  "main": "lib/index.js",
+  "types": "lib/index.d.ts",
+  "repository": "https://github.com/joystream/joystream",
+  "author": "Joystream Contributors",
+  "license": "MIT",
+  "private": false,
+  "scripts": {
+    "build": "./compile.sh && tsc",
+    "compile": "./compile.sh",
+    "generate-doc": "./generate-md-doc.sh",
+    "test": "env TS_NODE_COMPILER_OPTIONS='{\"module\": \"commonjs\" }' mocha -r ts-node/register 'test/**/*.ts'",
+    "lint": "eslint ./src --ext .ts",
+    "checks": "tsc --noEmit --pretty && prettier ./ --check && yarn lint",
+    "format": "prettier ./ --write"
+  },
+  "files": [
+    "lib/**/*",
+    "doc/**",
+    "proto/**",
+    "compiled/**/*",
+    "README.md"
+  ],
+  "dependencies": {
+    "google-protobuf": "^3.14.0"
+  },
+  "devDependencies": {
+    "@types/chai": "^4.2.11",
+    "@types/mocha": "^8.2.0",
+    "chai": "^4.2.0",
+    "eslint": "^7.6.0",
+    "mocha": "^8.2.1",
+    "prettier": "2.0.2",
+    "ts-node": "^8.8.1",
+    "ts-protoc-gen": "^0.14.0",
+    "typescript": "^4.1.3"
+  }
+}

+ 28 - 0
content-metadata-protobuf/proto/Channel.proto

@@ -0,0 +1,28 @@
+syntax = "proto2";
+
+message ChannelMetadata {
+    // Channel Title
+    optional string title = 1;
+
+    // Channel Description
+    optional string description = 2;
+
+    // Wether to display channel to the public
+    optional bool is_public = 3;
+
+    // ISO_639-1 Language [Code](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes)
+    optional string language = 4;
+
+    // index into external [assets array](#.Assets)
+    optional uint32 cover_photo = 5;
+    // index into external [assets array](#.Assets)
+    optional uint32 avatar_photo  = 6;
+
+    // Channel Category Id
+    optional uint64 category = 7;
+}
+
+message ChannelCategoryMetadata {
+    // Category Name
+    optional string name = 1;
+}

+ 13 - 0
content-metadata-protobuf/proto/Person.proto

@@ -0,0 +1,13 @@
+syntax = "proto2";
+
+message PersonMetadata {
+    optional string first_name = 1;
+    optional string middle_name = 2;
+    optional string last_name = 3;
+    optional string about = 4;
+    
+    // index into external [assets array](#.Assets)
+    optional uint32 cover_photo = 5;
+    // index into external [assets array](#.Assets)
+    optional uint32 avatar_photo = 6;
+}

+ 7 - 0
content-metadata-protobuf/proto/Playlist.proto

@@ -0,0 +1,7 @@
+syntax = "proto2";
+
+message PlaylistMetadata {
+    optional string title = 1;
+    // Videos in the playlist
+    repeated uint64 videos = 2;
+}

+ 19 - 0
content-metadata-protobuf/proto/Series.proto

@@ -0,0 +1,19 @@
+syntax = "proto2";
+
+message SeriesMetadata {
+    optional string title = 1;
+    optional string description = 2;
+    // index into external [assets array](#.Assets)
+    optional uint32 cover_photo = 3;
+    // Person(s) referenced by PersonId involved in this Series
+    repeated uint64 persons = 4 [packed=true];
+}
+
+message SeasonMetadata {
+    optional string title = 1;
+    optional string description = 2; 
+    // index into external [assets array](#.Assets)
+    optional uint32 cover_photo = 3;
+    // Person(s) referenced by PersonId involved in this Season
+    repeated uint64 persons = 4 [packed=true];
+}

+ 88 - 0
content-metadata-protobuf/proto/Video.proto

@@ -0,0 +1,88 @@
+syntax = "proto2";
+
+// Publication status before joystream
+message PublishedBeforeJoystream {
+    // Was video published before joystream platform
+    optional bool is_published = 1;
+    // Date of publication: 'YYYY-MM-DD' [ISO-8601](https://www.iso.org/iso-8601-date-and-time-format.html)
+    optional string date = 2;
+}
+
+// License types defined by Joystream
+message License {
+    // License code defined by Joystream. [reference](../src/KnownLicenses.json)
+    optional uint32 code = 1;
+    // Text for licenses that require an attribution
+    optional string attribution = 2;
+    // Text for custom license type
+    optional string custom_text = 3;
+}
+
+// Codec, Container, MIME media-type information
+message MediaType {
+    // Codec corresponding to `name` field from [FFmpeg](https://github.com/FFmpeg/FFmpeg/blob/master/libavcodec/codec_desc.c)
+    optional string codec_name = 1;
+
+    // Video container format, eg. 'MP4', 'WebM', 'Ogg' [ref](https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Video_codecs)
+    optional string container = 2;
+
+    // MIME Media Type, eg. 'video/mp4' [ref](https://www.iana.org/assignments/media-types/media-types.xhtml#video)
+    optional string mime_media_type = 3;
+}
+
+message VideoMetadata {
+    // Video Title
+    optional string title = 1;
+
+    // Video Description
+    optional string description = 2;
+
+    // Assets
+    
+    // index into external [assets array](#.Assets)
+    optional uint32 video = 3;
+
+    // index into external [assets array](#.Assets)
+    optional uint32 thumbnail_photo = 4;
+
+    // Lengths of video in seconds
+    optional uint32 duration = 5;
+
+    // Resolution of the video (Height)
+    optional uint32 media_pixel_height = 6;
+    
+    // Resolution of the video (Width)
+    optional uint32 media_pixel_width = 7;
+
+    // Encoding and Container format used
+    optional MediaType media_type = 8;
+
+    // ISO_639-1 Language [Code](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes)
+    optional string language = 9;
+
+    // License type for the media
+    optional License license = 10;
+
+    // Date of publication
+    optional PublishedBeforeJoystream published_before_joystream = 11;
+
+    // Does video have marketing or advertising in the stream
+    optional bool has_marketing = 12;
+
+    // Should video be publicy visible yet
+    optional bool is_public = 13;
+
+    // Does Video have explicit language or scenes
+    optional bool is_explicit = 14;
+
+    // Person(s) referenced by PersonId involved in this video
+    repeated uint64 persons = 15 [packed=true];
+
+    // Video Category Id
+    optional uint64 category = 16;
+}
+
+message VideoCategoryMetadata {
+    // Category name
+    optional string name = 1;
+}

+ 74 - 0
content-metadata-protobuf/src/KnownLicenses.json

@@ -0,0 +1,74 @@
+[
+  {
+    "code": 1000,
+    "name": "CUSTOM",
+    "longName": "Custom License",
+    "description": "A user defined License",
+    "url": "",
+    "attributionRequired": false
+  },
+  {
+    "code": 1001,
+    "name": "PDM",
+    "longName": "Public Domain",
+    "description": "For items which are not protected by copyright. This is not a license, but rather a copyright status. Some government-produced works, items with expired copyrights, and those which are ineligible for copyright protection may be included in this category.",
+    "url": "https://creativecommons.org/share-your-work/public-domain/pdm",
+    "attributionRequired": false
+  },
+  {
+    "code": 1002,
+    "name": "CC0",
+    "longName": "Public Domain Dedication",
+    "description": "The CC0 (Public Domain Dedication) License allows creators to waive all rights to their creations and release them into the Public Domain.",
+    "url": "https://creativecommons.org/share-your-work/public-domain/cc0",
+    "attributionRequired": true
+  },
+  {
+    "code": 1003,
+    "name": "CC_BY",
+    "longName": "Creative Commons Attribution License",
+    "description": "Sharing and adapting this content is permitted, but attribution must be provided. Read the License Deed for more information.",
+    "url": "https://creativecommons.org/licenses/by/4.0",
+    "attributionRequired": true
+  },
+  {
+    "code": 1004,
+    "name": "CC_BY_SA",
+    "longName": "Creative Commons Attribution-ShareAlike License",
+    "description": "Sharing and adapting this content is permitted, but attribution must be provided. Any derivative works must be distributed under the same license. Read the License Deed for more information.",
+    "url": "https://creativecommons.org/licenses/by-sa/4.0",
+    "attributionRequired": true
+  },
+  {
+    "code": 1005,
+    "name": "CC_BY_ND",
+    "longName": "Creative Commons Attribution-NoDerivs License",
+    "description": "Sharing this content is permitted, but attribution must be provided. You may not remix, transform, or build upon the material. Read the License Deed for more information.",
+    "url": "https://creativecommons.org/licenses/by-nd/4.0",
+    "attributionRequired": true
+  },
+  {
+    "code": 1006,
+    "name": "CC_BY_NC",
+    "longName": "Creative Commons Attribution-NonCommercial License",
+    "description": "Sharing and adapting this content is permitted, but attribution must be provided. Commercial use is not permitted. Read the License Deed for more information.",
+    "url": "https://creativecommons.org/licenses/by-nc/4.0",
+    "attributionRequired": true
+  },
+  {
+    "code": 1007,
+    "name": "CC_BY_NC_SA",
+    "longName": "Creative Commons Attribution-NonCommercial-ShareAlike License",
+    "description": "Sharing and adapting this content is permitted, but attribution must be provided. Any derivative works must be distributed under the same license. Commercial use is not permitted. Read the License Deed for more information.",
+    "url": "https://creativecommons.org/licenses/by-nc-sa/4.0",
+    "attributionRequired": true
+  },
+  {
+    "code": 1008,
+    "name": "CC_BY_NC_ND",
+    "longName": "Creative Commons Attribution-NonCommercial-NoDerivs License",
+    "description": "Sharing this content is permitted, but attribution must be provided. You may not remix, transform, or build upon the material. Commercial use is not permitted. Read the License Deed for more information.",
+    "url": "https://creativecommons.org/licenses/by-nc-nd/4.0",
+    "attributionRequired": true
+  }
+]

+ 10 - 0
content-metadata-protobuf/src/index.ts

@@ -0,0 +1,10 @@
+// Some helpers for constructing known licences
+import licences from './licenses'
+export { licences }
+
+// protobuf message constructors
+export * from '../compiled/proto/Video_pb'
+export * from '../compiled/proto/Channel_pb'
+export * from '../compiled/proto/Person_pb'
+export * from '../compiled/proto/Playlist_pb'
+export * from '../compiled/proto/Series_pb'

+ 70 - 0
content-metadata-protobuf/src/licenses.ts

@@ -0,0 +1,70 @@
+// Helper methods to handle joystream defined licence types
+// This should be factored out into a separate package
+
+import LICENSES from './KnownLicenses.json'
+import { License } from '../compiled/proto/Video_pb'
+
+export type LicenseCode = number
+export const CUSTOM_LICENSE_CODE: LicenseCode = 1000
+
+type KnownLicense = {
+  code: LicenseCode
+  name: string
+  longName: string
+  description: string
+  url: string
+  attributionRequired: boolean
+}
+
+export const KnownLicenses = new Map<LicenseCode, KnownLicense>()
+
+LICENSES.forEach((license: KnownLicense) => {
+  KnownLicenses.set(license.code, license)
+})
+
+export function getLicenseCodeByName(name: string): LicenseCode | undefined {
+  for (const [code, license] of KnownLicenses) {
+    if (license.name === name) return code
+  }
+}
+
+export function createKnownLicenseFromCode(code: LicenseCode, attribution?: string): License {
+  if (code === CUSTOM_LICENSE_CODE) {
+    throw new Error('Use createCustomLicense() instead')
+  }
+
+  const knownLicense = KnownLicenses.get(code)
+
+  if (!knownLicense) {
+    throw new Error('Unknown License Code')
+  }
+
+  const license = new License()
+
+  license.setCode(code)
+
+  if (knownLicense.attributionRequired) {
+    if (attribution === undefined) {
+      throw new Error('Attribution required for selected license')
+    }
+    license.setAttribution(attribution)
+  }
+
+  return license
+}
+
+export function createCustomKnownLicense(customText: string): License {
+  const license = new License()
+
+  license.setCode(CUSTOM_LICENSE_CODE)
+  license.setCustomText(customText)
+  return license
+}
+
+export default {
+  CUSTOM_LICENSE_CODE,
+  KnownLicenses,
+  createCustomKnownLicense,
+  createKnownLicenseFromCode,
+  getLicenseCodeByName,
+}

+ 33 - 0
content-metadata-protobuf/test/channel.ts

@@ -0,0 +1,33 @@
+import { ChannelMetadata } from '../src'
+import { assert } from 'chai'
+
+describe('Channel Metadata', () => {
+  it('Message', () => {
+    const channel = new ChannelMetadata()
+
+    const title = 'title'
+    const description = 'description'
+    const isPublic = false
+    const language = 'fr'
+
+    channel.setTitle(title)
+    channel.setDescription(description)
+    channel.setIsPublic(isPublic)
+    channel.setLanguage(language)
+    channel.setAvatarPhoto(0)
+    channel.setCoverPhoto(1)
+    channel.setCategory(100)
+
+    assert.deepEqual(channel.toObject(), {
+      title,
+      description,
+      isPublic,
+      language,
+      avatarPhoto: 0,
+      coverPhoto: 1,
+      category: 100,
+    })
+
+    assert.deepEqual(ChannelMetadata.deserializeBinary(channel.serializeBinary()), channel)
+  })
+})

+ 42 - 0
content-metadata-protobuf/test/license-codes.ts

@@ -0,0 +1,42 @@
+import {
+  KnownLicenses,
+  CUSTOM_LICENSE_CODE,
+  getLicenseCodeByName,
+  createKnownLicenseFromCode,
+  createCustomKnownLicense,
+} from '../src/licenses'
+import { VideoMetadata } from '../src/index'
+import { assert } from 'chai'
+
+describe('Known License Codes', () => {
+  it('Excludes default value 0', () => {
+    assert(!KnownLicenses.has(0))
+  })
+
+  it('Pre-defined Joystream license codes', () => {
+    // Make sure we have correct known custom licence
+    assert(KnownLicenses.has(CUSTOM_LICENSE_CODE))
+    assert.equal(KnownLicenses.get(CUSTOM_LICENSE_CODE)!.name, 'CUSTOM')
+
+    assert(KnownLicenses.has(1001))
+    assert(KnownLicenses.has(1002))
+    assert(KnownLicenses.has(1003))
+    assert(KnownLicenses.has(1004))
+    assert(KnownLicenses.has(1005))
+    assert(KnownLicenses.has(1006))
+    assert(KnownLicenses.has(1007))
+    assert(KnownLicenses.has(1008))
+  })
+
+  it('createCustomKnownLicense(): uses correct code', () => {
+    const license = createCustomKnownLicense('custom text')
+    assert.equal(license.getCode(), CUSTOM_LICENSE_CODE)
+  })
+
+  it('createKnownLicenseFromCode(): Licence can be created by name', () => {
+    const licenseCode = getLicenseCodeByName('CC_BY') as number
+    const license = createKnownLicenseFromCode(licenseCode as number, 'Attribution: Joystream')
+    const videoMeta = new VideoMetadata()
+    videoMeta.setLicense(license)
+  })
+})

+ 115 - 0
content-metadata-protobuf/test/video.ts

@@ -0,0 +1,115 @@
+import { VideoMetadata, PublishedBeforeJoystream, MediaType, License } from '../src'
+import { assert, expect } from 'chai'
+
+describe('Video Metadata', () => {
+  it('Message', () => {
+    const meta = new VideoMetadata()
+
+    const title = 'Video Title'
+    const description = 'Video Description'
+    const duration = 100
+
+    meta.setTitle(title)
+    meta.setDescription(description)
+    meta.setDuration(duration)
+    meta.setMediaPixelHeight(1)
+    meta.setMediaPixelWidth(2)
+    meta.setMediaType(new MediaType())
+    meta.setLanguage('en')
+    meta.setLicense(new License())
+    meta.setPublishedBeforeJoystream(new PublishedBeforeJoystream())
+    meta.setHasMarketing(true)
+    meta.setIsPublic(true)
+    meta.setIsExplicit(false)
+    meta.setVideo(0)
+    meta.setThumbnailPhoto(1)
+    meta.setCategory(101)
+
+    assert.deepEqual(meta.toObject(), {
+      title,
+      description,
+      duration,
+      mediaPixelHeight: 1,
+      mediaPixelWidth: 2,
+      mediaType: {
+        codecName: undefined,
+        container: undefined,
+        mimeMediaType: undefined,
+      },
+      language: 'en',
+      license: {
+        code: undefined,
+        attribution: undefined,
+        customText: undefined,
+      },
+      publishedBeforeJoystream: { isPublished: undefined, date: undefined },
+      hasMarketing: true,
+      isPublic: true,
+      isExplicit: false,
+      thumbnailPhoto: 1,
+      video: 0,
+      personsList: [],
+      category: 101,
+    })
+
+    // sanity check - encoding / decoding works
+    assert.deepEqual(VideoMetadata.deserializeBinary(meta.serializeBinary()), meta)
+  })
+
+  it('Message: PublishedBeforeJoystream', () => {
+    const meta = new VideoMetadata()
+
+    expect(meta.hasPublishedBeforeJoystream()).equals(false, 'PublishedBeforeJoystream field should NOT be set')
+
+    const published = new PublishedBeforeJoystream()
+    const isPublished = true
+    const date = '1950-12-24'
+    published.setIsPublished(isPublished)
+    published.setDate(date)
+
+    meta.setPublishedBeforeJoystream(published)
+
+    // Field should now be set
+    expect(meta.hasPublishedBeforeJoystream()).equals(true, 'PublishedBeforeJoystream field should be set')
+
+    assert.deepEqual(published.toObject(), {
+      isPublished,
+      date,
+    })
+  })
+
+  it('Message: Licence', () => {
+    const license = new License()
+
+    const code = 1000
+    const attribution = 'Attribution Text'
+    const customText = 'Custom License Details'
+    license.setCode(code)
+    license.setAttribution(attribution)
+    license.setCustomText(customText)
+
+    assert.deepEqual(license.toObject(), {
+      code,
+      attribution,
+      customText,
+    })
+  })
+
+  it('Message: MediaType', () => {
+    const mediaType = new MediaType()
+
+    const codecName = 'mpeg4'
+    const container = 'avi'
+    const mimeMediaType = 'videp/mp4'
+
+    mediaType.setCodecName(codecName)
+    mediaType.setContainer(container)
+    mediaType.setMimeMediaType(mimeMediaType)
+
+    assert.deepEqual(mediaType.toObject(), {
+      codecName,
+      container,
+      mimeMediaType,
+    })
+  })
+})

+ 15 - 0
content-metadata-protobuf/tsconfig.json

@@ -0,0 +1,15 @@
+{
+  "compilerOptions": {
+    "target": "esnext",
+    "module": "commonjs",
+    "outDir": "lib",
+    "strict": true,
+    "declaration": true,
+    "esModuleInterop": true,
+    "forceConsistentCasingInFileNames": true,
+    "skipLibCheck": true,
+    "resolveJsonModule": true,
+  },
+  "include": ["src"],
+  "exclude": ["node_modules", "test"]
+}

+ 79 - 9
node/src/chain_spec/content_config.rs

@@ -1,5 +1,9 @@
 use codec::Decode;
-use node_runtime::{data_directory::DataObject, ContentId, DataDirectoryConfig, Runtime};
+use node_runtime::{
+    common::storage::StorageObjectOwner,
+    data_directory::{DataObject, Quota},
+    ChannelId, ContentId, DAOId, DataDirectoryConfig, MemberId, Runtime,
+};
 use serde::Deserialize;
 use std::{fs, path::Path};
 
@@ -8,43 +12,68 @@ use std::{fs, path::Path};
 // them to json we get a string rather than an array of bytes, so deserializing them
 // is failing. So we are relying on parity codec encoding instead..
 #[derive(Decode)]
-struct DataObjectAndContentId {
+struct Content {
     content_id: ContentId,
     data_object: DataObject<Runtime>,
+    storage_object_owner: StorageObjectOwner<MemberId, ChannelId, DAOId>,
+    quota: Quota,
 }
 
 #[derive(Decode)]
 struct ContentData {
     /// DataObject(s) and ContentId
-    data_objects: Vec<DataObjectAndContentId>,
+    data_objects: Vec<Content>,
+    quota_size_limit_upper_bound: u64,
+    quota_objects_limit_upper_bound: u64,
+    global_quota: Quota,
+    uploading_blocked: bool,
 }
 
 #[derive(Deserialize)]
-struct EncodedDataObjectAndContentId {
+struct EncodedContent {
     /// hex encoded ContentId
     content_id: String,
     /// hex encoded DataObject<Runtime>
     data_object: String,
+    /// hex encoded StorageObjectOwner
+    storage_object_owner: String,
+    /// hex encoded Quota
+    quota: String,
 }
 
-impl EncodedDataObjectAndContentId {
-    fn decode(&self) -> DataObjectAndContentId {
+impl EncodedContent {
+    fn decode(&self) -> Content {
         // hex string must not include '0x' prefix!
         let encoded_content_id = hex::decode(&self.content_id[2..].as_bytes())
             .expect("failed to parse content_id hex string");
         let encoded_data_object = hex::decode(&self.data_object[2..].as_bytes())
             .expect("failed to parse data_object hex string");
-        DataObjectAndContentId {
+        let encoded_storage_object_owner = hex::decode(&self.storage_object_owner[2..].as_bytes())
+            .expect("failed to parse content_id hex string");
+        let encoded_quota = hex::decode(&self.quota[2..].as_bytes())
+            .expect("failed to parse data_object hex string");
+        Content {
             content_id: Decode::decode(&mut encoded_content_id.as_slice()).unwrap(),
             data_object: Decode::decode(&mut encoded_data_object.as_slice()).unwrap(),
+            storage_object_owner: Decode::decode(&mut encoded_storage_object_owner.as_slice())
+                .unwrap(),
+            quota: Decode::decode(&mut encoded_quota.as_slice()).unwrap(),
         }
     }
 }
 
 #[derive(Deserialize)]
 struct EncodedContentData {
-    /// DataObject(s) and ContentId
-    data_objects: Vec<EncodedDataObjectAndContentId>,
+    /// EncodedContent
+    data_objects: Vec<EncodedContent>,
+    /// hex encoded QuotaSizeLimitUpperBound
+    quota_size_limit_upper_bound: String,
+    /// hex encoded QuotaObjectsLimitUpperBound
+    quota_objects_limit_upper_bound: String,
+    /// hex encoded GlobalQuota
+    global_quota: String,
+    /// hex encoded UploadingBlocked flag
+    uploading_blocked: String,
 }
 
 fn parse_content_data(data_file: &Path) -> EncodedContentData {
@@ -60,6 +89,33 @@ impl EncodedContentData {
                 .iter()
                 .map(|data_objects| data_objects.decode())
                 .collect(),
+            quota_size_limit_upper_bound: {
+                let encoded_quota_size_limit_upper_bound =
+                    hex::decode(&self.quota_size_limit_upper_bound[2..].as_bytes())
+                        .expect("failed to parse data_object hex string");
+
+                Decode::decode(&mut encoded_quota_size_limit_upper_bound.as_slice()).unwrap()
+            },
+            quota_objects_limit_upper_bound: {
+                let encoded_quota_objects_limit_upper_bound =
+                    hex::decode(&self.quota_objects_limit_upper_bound[2..].as_bytes())
+                        .expect("failed to parse data_object hex string");
+
+                Decode::decode(&mut encoded_quota_objects_limit_upper_bound.as_slice()).unwrap()
+            },
+            global_quota: {
+                let encoded_global_quota = hex::decode(&self.global_quota[2..].as_bytes())
+                    .expect("failed to parse data_object hex string");
+
+                Decode::decode(&mut encoded_global_quota.as_slice()).unwrap()
+            },
+            uploading_blocked: {
+                let encoded_uploading_blocked =
+                    hex::decode(&self.uploading_blocked[2..].as_bytes())
+                        .expect("failed to parse data_object hex string");
+
+                Decode::decode(&mut encoded_uploading_blocked.as_slice()).unwrap()
+            },
         }
     }
 }
@@ -69,6 +125,11 @@ pub fn empty_data_directory_config() -> DataDirectoryConfig {
     DataDirectoryConfig {
         data_object_by_content_id: vec![],
         known_content_ids: vec![],
+        quotas: vec![],
+        quota_size_limit_upper_bound: 20000,
+        quota_objects_limit_upper_bound: 200,
+        global_quota: Quota::new(2000000, 2000),
+        uploading_blocked: false,
     }
 }
 
@@ -84,10 +145,19 @@ pub fn data_directory_config_from_json(data_file: &Path) -> DataDirectoryConfig
             .iter()
             .map(|object| (object.content_id, object.data_object.clone()))
             .collect(),
+        quotas: content
+            .data_objects
+            .iter()
+            .map(|object| (object.storage_object_owner.clone(), object.quota))
+            .collect(),
         known_content_ids: content
             .data_objects
             .into_iter()
             .map(|object| object.content_id)
             .collect(),
+        quota_size_limit_upper_bound: content.quota_size_limit_upper_bound,
+        quota_objects_limit_upper_bound: content.quota_objects_limit_upper_bound,
+        global_quota: content.global_quota,
+        uploading_blocked: content.uploading_blocked,
     }
 }

+ 2 - 1
package.json

@@ -23,7 +23,8 @@
     "utils/api-scripts",
     "content-directory-schemas",
     "query-node",
-    "query-node/generated/*"
+    "query-node/generated/*",
+    "content-metadata-protobuf"
   ],
   "resolutions": {
     "@polkadot/api": "1.26.1",

+ 4 - 0
runtime-modules/common/src/lib.rs

@@ -14,6 +14,7 @@ use serde::{Deserialize, Serialize};
 use frame_support::Parameter;
 use sp_arithmetic::traits::BaseArithmetic;
 use sp_runtime::traits::{MaybeSerialize, Member};
+use sp_std::vec::Vec;
 
 /// Member id type alias
 pub type MemberId<T> = <T as MembershipTypes>::MemberId;
@@ -21,6 +22,9 @@ pub type MemberId<T> = <T as MembershipTypes>::MemberId;
 /// Actor id type alias
 pub type ActorId<T> = <T as MembershipTypes>::ActorId;
 
+/// HTTP Url string
+pub type Url = Vec<u8>;
+
 /// Generic trait for membership dependent pallets.
 pub trait MembershipTypes: system::Trait {
     /// Describes the common type for the members.

+ 3 - 9
runtime-modules/common/src/storage.rs

@@ -13,23 +13,17 @@ pub struct ContentParameters<ContentId, DataObjectTypeId> {
     pub ipfs_content_id: Vec<u8>,
 }
 
+// New owner type for storage object struct
 #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
 #[derive(Clone, Encode, Decode, PartialEq, Eq, Debug)]
-pub enum AbstractStorageObjectOwner<ChannelId, DAOId> {
+pub enum StorageObjectOwner<MemberId, ChannelId, DAOId> {
+    Member(MemberId),
     Channel(ChannelId), // acts through content directory module, where again DAOs can own channels for example
     DAO(DAOId),         // acts through upcoming `content_finance` module
     Council,            // acts through proposal system
     WorkingGroup(WorkingGroup), // acts through new extrinsic in working group
 }
 
-// New owner type for storage object struct
-#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
-#[derive(Clone, Encode, Decode, PartialEq, Eq, Debug)]
-pub enum StorageObjectOwner<MemberId, ChannelId, DAOId> {
-    Member(MemberId),
-    AbstractStorageObjectOwner(AbstractStorageObjectOwner<ChannelId, DAOId>),
-}
-
 // To be implemented by current storage data_directory runtime module.
 // Defined in 'common' package
 pub trait StorageSystem<T: crate::StorageOwnership + crate::MembershipTypes> {

+ 185 - 211
runtime-modules/content/src/lib.rs

@@ -24,24 +24,25 @@ pub use serde::{Deserialize, Serialize};
 use sp_arithmetic::traits::{BaseArithmetic, One, Zero};
 use sp_runtime::traits::{MaybeSerializeDeserialize, Member};
 use sp_std::collections::btree_set::BTreeSet;
-// use sp_std::vec;
+use sp_std::vec;
 use sp_std::vec::Vec;
 use system::ensure_signed;
 
 pub use common::storage::{
-    AbstractStorageObjectOwner, ContentParameters, StorageObjectOwner, StorageSystem,
+    ContentParameters as ContentParametersRecord, StorageObjectOwner, StorageSystem,
 };
+
 pub use common::{
     currency::{BalanceOf, GovernanceCurrency},
     working_group::WorkingGroup,
-    MembershipTypes, StorageOwnership,
+    MembershipTypes, StorageOwnership, Url,
 };
 
 pub(crate) type ContentId<T> = <T as StorageOwnership>::ContentId;
 
 pub(crate) type DataObjectTypeId<T> = <T as StorageOwnership>::DataObjectTypeId;
 
-pub(crate) type UploadParameters<T> = ContentParameters<ContentId<T>, DataObjectTypeId<T>>;
+pub(crate) type ContentParameters<T> = ContentParametersRecord<ContentId<T>, DataObjectTypeId<T>>;
 
 /// Type, used in diffrent numeric constraints representations
 pub type MaxNumber = u32;
@@ -81,9 +82,6 @@ pub trait Trait:
     /// Channel Transfer Payments Escrow Account seed for ModuleId to compute deterministic AccountId
     type ChannelOwnershipPaymentEscrowId: Get<[u8; 8]>;
 
-    /// ChannelRevenueTreasury seed for ModuleId to compute deterministic AccountId
-    type ChannelRevenueTreasuryId: Get<[u8; 8]>;
-
     /// Type of identifier for Videos
     type VideoId: NumericIdentifier;
 
@@ -116,11 +114,11 @@ pub trait Trait:
 /// Channels, Videos, Series and Person
 #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
 #[derive(Encode, Decode, Clone, PartialEq, Eq, Debug)]
-pub enum NewAsset<UploadParameters> {
+pub enum NewAsset<ContentParameters> {
     /// Upload to the storage system
-    Upload(UploadParameters),
-    /// A url string pointing at an asset
-    Uri(Vec<u8>),
+    Upload(ContentParameters),
+    /// Multiple url strings pointing at an asset
+    Urls(Vec<Url>),
 }
 
 /// The owner of a channel, is the authorized "actor" that can update
@@ -128,8 +126,6 @@ pub enum NewAsset<UploadParameters> {
 #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
 #[derive(Encode, Decode, Clone, PartialEq, Eq, Debug)]
 pub enum ChannelOwner<MemberId, CuratorGroupId, DAOId> {
-    /// Do not use - Default value representing empty value
-    Nobody,
     /// A Member owns the channel
     Member(MemberId),
     /// A specific curation group owns the channel
@@ -140,18 +136,19 @@ pub enum ChannelOwner<MemberId, CuratorGroupId, DAOId> {
 
 // Default trait implemented only because its used in a Channel which needs to implement a Default trait
 // since it is a StorageValue.
-impl<MemberId, CuratorGroupId, DAOId> Default for ChannelOwner<MemberId, CuratorGroupId, DAOId> {
+impl<MemberId: Default, CuratorGroupId, DAOId> Default
+    for ChannelOwner<MemberId, CuratorGroupId, DAOId>
+{
     fn default() -> Self {
-        ChannelOwner::Nobody
+        ChannelOwner::Member(MemberId::default())
     }
 }
 
-/// A category which channels can belong to. The category will not be deleted if it contains any channels.
+/// A category which channels can belong to.
 #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
 #[derive(Encode, Decode, Default, Clone, PartialEq, Eq, Debug)]
 pub struct ChannelCategory {
-    /// The number of channels in this category.
-    number_of_channels_in: u32,
+    // No runtime information is currently stored for a Category.
 }
 
 /// Information on the category being created.
@@ -175,64 +172,66 @@ pub struct ChannelCategoryUpdateParameters {
 /// If a channel is deleted, all videos, playlists and series will also be deleted.
 #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
 #[derive(Encode, Decode, Default, Clone, PartialEq, Eq, Debug)]
-pub struct ChannelInternal<MemberId, CuratorGroupId, ChannelCategoryId, DAOId, Balance> {
+pub struct ChannelRecord<MemberId, CuratorGroupId, DAOId, AccountId, VideoId, PlaylistId, SeriesId>
+{
     /// The owner of a channel
     owner: ChannelOwner<MemberId, CuratorGroupId, DAOId>,
-    /// The category the channel belongs to
-    in_category: ChannelCategoryId,
-    /// The number of videos under this channel
-    number_of_videos: u32,
-    /// The number of playlists under this channel
-    number_of_playlists: u32,
-    /// The number of series under this channel
-    number_of_series: u32,
-    /// If curators have curated this channel or not
-    is_curated: bool,
-    /// Earned revenue yet to be withdrawn by channel owner
-    revenue: Balance,
-    // TODO: I think we need to add these instead of the counters!
-    // videos: Vec<VideoId>, playlists: Vec<PlaylistId>, series: Vec<SeriesId>
+    /// The videos under this channel
+    videos: Vec<VideoId>,
+    /// The playlists under this channel
+    playlists: Vec<PlaylistId>,
+    /// The series under this channel
+    series: Vec<SeriesId>,
+    /// If curators have censored this channel or not
+    is_censored: bool,
+    /// Reward account where revenue is sent if set.
+    reward_account: Option<AccountId>,
 }
 
 // Channel alias type for simplification.
-pub type Channel<T> = ChannelInternal<
+pub type Channel<T> = ChannelRecord<
     <T as MembershipTypes>::MemberId,
     <T as ContentActorAuthenticator>::CuratorGroupId,
-    <T as Trait>::ChannelCategoryId,
     <T as StorageOwnership>::DAOId,
-    BalanceOf<T>,
+    <T as system::Trait>::AccountId,
+    <T as Trait>::VideoId,
+    <T as Trait>::PlaylistId,
+    <T as Trait>::SeriesId,
 >;
 
 /// A request to buy a channel by a new ChannelOwner.
 #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
 #[derive(Encode, Decode, Default, Clone, PartialEq, Eq, Debug)]
-pub struct ChannelOwnershipTransferRequestInternal<
+pub struct ChannelOwnershipTransferRequestRecord<
     ChannelId,
     MemberId,
     CuratorGroupId,
     DAOId,
     Balance,
+    AccountId,
 > {
     channel_id: ChannelId,
     new_owner: ChannelOwner<MemberId, CuratorGroupId, DAOId>,
     payment: Balance,
+    new_reward_account: Option<AccountId>,
 }
 
 // ChannelOwnershipTransferRequest type alias for simplification.
-pub type ChannelOwnershipTransferRequest<T> = ChannelOwnershipTransferRequestInternal<
+pub type ChannelOwnershipTransferRequest<T> = ChannelOwnershipTransferRequestRecord<
     <T as StorageOwnership>::ChannelId,
     <T as MembershipTypes>::MemberId,
     <T as ContentActorAuthenticator>::CuratorGroupId,
     <T as StorageOwnership>::DAOId,
     BalanceOf<T>,
+    <T as system::Trait>::AccountId,
 >;
 
 /// Information about channel being created.
 #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
-#[derive(Encode, Decode, Default, Clone, PartialEq, Eq, Debug)]
-pub struct ChannelCreationParameters<ChannelCategoryId> {
-    /// ChannelCategory to enter the channel into.
-    in_category: ChannelCategoryId,
+#[derive(Encode, Decode, Clone, PartialEq, Eq, Debug)]
+pub struct ChannelCreationParameters<ContentParameters> {
+    /// Assets referenced by metadata
+    assets: Vec<NewAsset<ContentParameters>>,
     /// Metadata about the channel.
     meta: Vec<u8>,
 }
@@ -240,19 +239,18 @@ pub struct ChannelCreationParameters<ChannelCategoryId> {
 /// Information about channel being updated.
 #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
 #[derive(Encode, Decode, Default, Clone, PartialEq, Eq, Debug)]
-pub struct ChannelUpdateParameters<ChannelCategoryId> {
-    /// If set, the new channel category to move the channel into.
-    new_in_category: Option<ChannelCategoryId>,
+pub struct ChannelUpdateParameters<ContentParameters> {
+    /// Assets referenced by metadata
+    assets: Option<Vec<NewAsset<ContentParameters>>>,
     /// If set, metadata update for the channel.
     new_meta: Option<Vec<u8>>,
 }
 
-/// A category that videos can belong to. A category will not be deleted if it contains any videos.
+/// A category that videos can belong to.
 #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
 #[derive(Encode, Decode, Default, Clone, PartialEq, Eq, Debug)]
 pub struct VideoCategory {
-    /// The number of videos in this category.
-    number_of_videos_in_category: u32,
+    // No runtime information is currently stored for a Category.
 }
 
 /// Information about the video category being created.
@@ -274,19 +272,19 @@ pub struct VideoCategoryUpdateParameters {
 
 /// Information about the video being created.
 #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
-#[derive(Encode, Decode, Default, Clone, PartialEq, Eq, Debug)]
-pub struct VideoCreationParameters<VideoCategoryId> {
-    /// The video category the video belongs to.
-    in_category: VideoCategoryId,
+#[derive(Encode, Decode, Clone, PartialEq, Eq, Debug)]
+pub struct VideoCreationParameters<ContentParameters> {
+    /// Assets referenced by metadata
+    assets: Vec<NewAsset<ContentParameters>>,
     /// Metadata for the video.
     meta: Vec<u8>,
 }
 
 #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
 #[derive(Encode, Decode, Default, Clone, PartialEq, Eq, Debug)]
-pub struct VideoUpdateParameters<VideoCategoryId> {
-    /// If set, the new category the video should be moved into.
-    new_in_category: Option<VideoCategoryId>,
+pub struct VideoUpdateParameters<ContentParameters> {
+    /// Assets referenced by metadata
+    assets: Option<Vec<NewAsset<ContentParameters>>>,
     /// If set, metadata update for the video.
     new_meta: Option<Vec<u8>>,
 }
@@ -294,16 +292,13 @@ pub struct VideoUpdateParameters<VideoCategoryId> {
 /// A video which belongs to a channel. A video may be part of a series or playlist.
 #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
 #[derive(Encode, Decode, Default, Clone, PartialEq, Eq, Debug)]
-pub struct Video<ChannelId, SeriesId, PlaylistId> {
+pub struct Video<ChannelId, SeriesId> {
     in_channel: ChannelId,
-    // keep track of which seasons and playlists which reference the video
+    // keep track of which season the video is in if it is an 'episode'
     // - prevent removing a video if it is in a season (because order is important)
-    // - remove from playlist on deletion
-    in_series: Vec<SeriesId>,
-    in_playlists: Vec<PlaylistId>,
-
-    /// Whether the curators have curated the video or not.
-    is_curated: bool,
+    in_series: Option<SeriesId>,
+    /// Whether the curators have censored the video or not.
+    is_censored: bool,
     /// Whether the curators have chosen to feature the video or not.
     is_featured: bool,
 }
@@ -311,9 +306,7 @@ pub struct Video<ChannelId, SeriesId, PlaylistId> {
 /// Information about the plyalist being created.
 #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
 #[derive(Encode, Decode, Default, Clone, PartialEq, Eq, Debug)]
-pub struct PlaylistCreationParameters<VideoId> {
-    /// The full list of videos that make up the playlist.
-    videos: Vec<VideoId>,
+pub struct PlaylistCreationParameters {
     /// Metadata about the playlist.
     meta: Vec<u8>,
 }
@@ -321,29 +314,26 @@ pub struct PlaylistCreationParameters<VideoId> {
 /// Information about the playlist being updated.
 #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
 #[derive(Encode, Decode, Default, Clone, PartialEq, Eq, Debug)]
-pub struct PlaylistUpdateParameters<VideoId> {
-    /// If set, the new full list of videos that make up the playlist.
-    new_videos: Option<Vec<VideoId>>,
-    /// If set, metadata update for the playlist.
-    new_meta: Option<Vec<u8>>,
+pub struct PlaylistUpdateParameters {
+    // It is the only field so its not an Option
+    /// Metadata update for the playlist.
+    new_meta: Vec<u8>,
 }
 
 /// A playlist is an ordered collection of videos.
 #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
 #[derive(Encode, Decode, Default, Clone, PartialEq, Eq, Debug)]
-pub struct Playlist<ChannelId, VideoId> {
+pub struct Playlist<ChannelId> {
     /// The channel the playlist belongs to.
     in_channel: ChannelId,
-    /// The videos that make up the playlist.
-    videos: Vec<VideoId>,
 }
 
 /// Information about the episode being created or updated.
 #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
 #[derive(Encode, Decode, Clone, PartialEq, Eq, Debug)]
-pub enum EpisodeParameters<VideoCategoryId, VideoId> {
+pub enum EpisodeParameters<VideoId, ContentParameters> {
     /// A new video is being added as the episode.
-    NewVideo(VideoCreationParameters<VideoCategoryId>),
+    NewVideo(VideoCreationParameters<ContentParameters>),
     /// An existing video is being made into an episode.
     ExistingVideo(VideoId),
 }
@@ -351,13 +341,15 @@ pub enum EpisodeParameters<VideoCategoryId, VideoId> {
 /// Information about the season being created or updated.
 #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
 #[derive(Encode, Decode, Default, Clone, PartialEq, Eq, Debug)]
-pub struct SeasonParameters<VideoCategoryId, VideoId> {
+pub struct SeasonParameters<VideoId, ContentParameters> {
+    /// Season assets referenced by metadata
+    assets: Option<Vec<NewAsset<ContentParameters>>>,
     // ?? It might just be more straighforward to always provide full list of episodes at cost of larger tx.
     /// If set, updates the episodes of a season. Extends the number of episodes in a season
     /// when length of new_episodes is greater than previously set. Last elements must all be
     /// 'Some' in that case.
     /// Will truncate existing season when length of new_episodes is less than previously set.
-    episodes: Option<Vec<Option<EpisodeParameters<VideoCategoryId, VideoId>>>>,
+    episodes: Option<Vec<Option<EpisodeParameters<VideoId, ContentParameters>>>>,
     /// If set, Metadata update for season.
     meta: Option<Vec<u8>>,
 }
@@ -365,12 +357,14 @@ pub struct SeasonParameters<VideoCategoryId, VideoId> {
 /// Information about the series being created or updated.
 #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
 #[derive(Encode, Decode, Default, Clone, PartialEq, Eq, Debug)]
-pub struct SeriesParameters<VideoCategoryId, VideoId> {
+pub struct SeriesParameters<VideoId, ContentParameters> {
+    /// Series assets referenced by metadata
+    assets: Option<Vec<NewAsset<ContentParameters>>>,
     // ?? It might just be more straighforward to always provide full list of seasons at cost of larger tx.
     /// If set, updates the seasons of a series. Extend a series when length of seasons is
     /// greater than previoulsy set. Last elements must all be 'Some' in that case.
     /// Will truncate existing series when length of seasons is less than previously set.
-    seasons: Option<Vec<Option<SeasonParameters<VideoCategoryId, VideoId>>>>,
+    seasons: Option<Vec<Option<SeasonParameters<VideoId, ContentParameters>>>>,
     meta: Option<Vec<u8>>,
 }
 
@@ -401,8 +395,6 @@ pub enum PersonActor<MemberId, CuratorId> {
 #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
 #[derive(Encode, Decode, Clone, PartialEq, Eq, Debug)]
 pub enum PersonController<MemberId> {
-    /// Do not use - Default value representing empty value
-    Nobody,
     /// Member controls the person
     Member(MemberId),
     /// Any curator controls the person
@@ -411,16 +403,18 @@ pub enum PersonController<MemberId> {
 
 // Default trait implemented only because its used in Person which needs to implement a Default trait
 // since it is a StorageValue.
-impl<MemberId> Default for PersonController<MemberId> {
+impl<MemberId: Default> Default for PersonController<MemberId> {
     fn default() -> Self {
-        PersonController::Nobody
+        PersonController::Member(MemberId::default())
     }
 }
 
 /// Information for Person being created.
 #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
-#[derive(Encode, Decode, Default, Clone, PartialEq, Eq, Debug)]
-pub struct PersonCreationParameters {
+#[derive(Encode, Decode, Clone, PartialEq, Eq, Debug)]
+pub struct PersonCreationParameters<ContentParameters> {
+    /// Assets referenced by metadata
+    assets: Vec<NewAsset<ContentParameters>>,
     /// Metadata for person.
     meta: Vec<u8>,
 }
@@ -428,9 +422,11 @@ pub struct PersonCreationParameters {
 /// Information for Persion being updated.
 #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
 #[derive(Encode, Decode, Default, Clone, PartialEq, Eq, Debug)]
-pub struct PersonUpdateParameters {
+pub struct PersonUpdateParameters<ContentParameters> {
+    /// Assets referenced by metadata
+    assets: Option<Vec<NewAsset<ContentParameters>>>,
     /// Metadata to update person.
-    new_meta: Vec<u8>,
+    new_meta: Option<Vec<u8>>,
 }
 
 /// A Person represents a real person that may be associated with a video.
@@ -439,8 +435,6 @@ pub struct PersonUpdateParameters {
 pub struct Person<MemberId> {
     /// Who can update or delete this person.
     controlled_by: PersonController<MemberId>,
-    /// The number of videos this person appears in.
-    number_of_videos_person_involed_in: u32,
 }
 
 decl_storage! {
@@ -449,18 +443,16 @@ decl_storage! {
 
         pub ChannelCategoryById get(fn channel_category_by_id): map hasher(blake2_128_concat) T::ChannelCategoryId => ChannelCategory;
 
-        pub VideoById get(fn video_by_id): map hasher(blake2_128_concat) T::VideoId => Video<T::ChannelId, T::SeriesId, T::PlaylistId>;
+        pub VideoById get(fn video_by_id): map hasher(blake2_128_concat) T::VideoId => Video<T::ChannelId, T::SeriesId>;
 
         pub VideoCategoryById get(fn video_category_by_id): map hasher(blake2_128_concat) T::VideoCategoryId => VideoCategory;
 
-        pub PlaylistById get(fn playlist_by_id): map hasher(blake2_128_concat) T::PlaylistId => Playlist<T::ChannelId, T::VideoId>;
+        pub PlaylistById get(fn playlist_by_id): map hasher(blake2_128_concat) T::PlaylistId => Playlist<T::ChannelId>;
 
         pub SeriesById get(fn series_by_id): map hasher(blake2_128_concat) T::SeriesId => Series<T::ChannelId, T::VideoId>;
 
         pub PersonById get(fn person_by_id): map hasher(blake2_128_concat) T::PersonId => Person<T::MemberId>;
 
-        // pub PersonInVideo get(fn person_in_video): double_map hasher(blake2_128_concat) (T::VideoId, T::PersonId), hasher(blake2_128_concat) T::Hash => ();
-
         pub ChannelOwnershipTransferRequestById get(fn channel_ownership_transfer_request_by_id):
             map hasher(blake2_128_concat) T::ChannelOwnershipTransferRequestId => ChannelOwnershipTransferRequest<T>;
 
@@ -541,8 +533,8 @@ decl_module! {
             // Ensure CuratorGroup under given curator_group_id exists
             let curator_group = Self::ensure_curator_group_exists(&curator_group_id)?;
 
-            // We should previously ensure that curator_group  maintains no classes to be able to remove it
-            curator_group.ensure_curator_group_maintains_no_classes()?;
+            // We should previously ensure that curator_group  owns no channels to be able to remove it
+            curator_group.ensure_curator_group_owns_no_channels()?;
 
             //
             // == MUTATION SAFE ==
@@ -650,35 +642,32 @@ decl_module! {
             Ok(())
         }
 
+        // TODO: Add Option<reward_account> to ChannelCreationParameters ?
         #[weight = 10_000_000] // TODO: adjust weight
         pub fn create_channel(
             origin,
             actor: ContentActor<T::CuratorGroupId, T::CuratorId, T::MemberId>,
             owner: ChannelOwner<T::MemberId, T::CuratorGroupId, T::DAOId>,
-            assets: Vec<NewAsset<ContentParameters<T::ContentId, T::DataObjectTypeId>>>,
-            params: ChannelCreationParameters<T::ChannelCategoryId>,
+            params: ChannelCreationParameters<ContentParameters<T>>,
         ) -> DispatchResult {
-            let _sender = ensure_signed(origin.clone())?;
-
             ensure_actor_authorized_to_create_or_update_channel::<T>(
                 origin,
                 &actor,
                 &owner,
             )?;
 
-            // Ensure the channel category exists
-            Self::ensure_channel_category_exists(&params.in_category)?;
-
             // Pick out the assets to be uploaded to storage system
-            let upload_parameters: Vec<UploadParameters<T>> = Self::pick_upload_parameters_from_assets(&assets);
+            let content_parameters: Vec<ContentParameters<T>> = Self::pick_upload_parameters_from_assets(&params.assets);
+
+            let expected_channel_id = NextChannelId::<T>::get();
 
-            let object_owner = Self::channel_owner_to_object_owner(&owner)?;
+            let object_owner = Self::channel_owner_to_object_owner(&owner, &expected_channel_id)?;
 
             // check assets can be uploaded to storage.
             // update can_add_content() to only take &refrences
             T::StorageSystem::can_add_content(
                 object_owner.clone(),
-                upload_parameters.clone(),
+                content_parameters.clone(),
             )?;
 
             // TODO:
@@ -695,20 +684,20 @@ decl_module! {
             // This should not fail because of prior can_add_content() check!
             T::StorageSystem::atomically_add_content(
                 object_owner,
-                upload_parameters,
+                content_parameters,
             )?;
 
+            // incase we ever create channels following first get channel_id earlier, get again
             let channel_id = NextChannelId::<T>::get();
             NextChannelId::<T>::mutate(|id| *id += T::ChannelId::one());
 
-            let channel: Channel<T> = ChannelInternal {
+            let channel: Channel<T> = ChannelRecord {
                 owner: owner.clone(),
-                number_of_videos: 0,
-                number_of_playlists: 0,
-                number_of_series: 0,
-                in_category: params.in_category,
-                is_curated: false,
-                revenue: BalanceOf::<T>::from(0),
+                videos: vec![],
+                playlists: vec![],
+                series: vec![],
+                is_censored: false,
+                reward_account: None,
             };
             ChannelById::<T>::insert(channel_id, channel.clone());
 
@@ -716,20 +705,18 @@ decl_module! {
                 Self::increment_number_of_channels_owned_by_curator_group(curator_group_id);
             }
 
-            Self::deposit_event(RawEvent::ChannelCreated(channel_id, channel, assets, params));
+            Self::deposit_event(RawEvent::ChannelCreated(channel_id, channel, params));
             Ok(())
         }
 
+        // Include Option<AccountId> in ChannelUpdateParameters to update reward_account
         #[weight = 10_000_000] // TODO: adjust weight
         pub fn update_channel(
             origin,
             actor: ContentActor<T::CuratorGroupId, T::CuratorId, T::MemberId>,
             channel_id: T::ChannelId,
-            assets: Vec<NewAsset<ContentParameters<T::ContentId, T::DataObjectTypeId>>>,
-            params: ChannelUpdateParameters<T::ChannelCategoryId>,
+            params: ChannelUpdateParameters<ContentParameters<T>>,
         ) -> DispatchResult {
-            let _sender = ensure_signed(origin.clone())?;
-
             // check that channel exists
             let channel = Self::ensure_channel_exists(&channel_id)?;
 
@@ -739,45 +726,41 @@ decl_module! {
                 &channel.owner,
             )?;
 
-            // change of category?
-            if let Some(new_category_id) = params.new_in_category {
-                Self::ensure_channel_category_exists(&new_category_id)?;
-            }
-
             // Pick out the assets to be uploaded to storage system
-            let upload_parameters: Vec<UploadParameters<T>> = Self::pick_upload_parameters_from_assets(&assets);
+            let new_assets = if let Some(assets) = &params.assets {
+                let upload_parameters: Vec<ContentParameters<T>> = Self::pick_upload_parameters_from_assets(assets);
 
-            let object_owner = Self::channel_owner_to_object_owner(&channel.owner)?;
+                let object_owner = Self::channel_owner_to_object_owner(&channel.owner, &channel_id)?;
 
-            // check assets can be uploaded to storage.
-            // update can_add_content() to only take &refrences
-            T::StorageSystem::can_add_content(
-                object_owner.clone(),
-                upload_parameters.clone(),
-            )?;
+                // check assets can be uploaded to storage.
+                // update can_add_content() to only take &refrences
+                T::StorageSystem::can_add_content(
+                    object_owner.clone(),
+                    upload_parameters.clone(),
+                )?;
+
+                Some((upload_parameters, object_owner))
+            } else {
+                None
+            };
 
             //
             // == MUTATION SAFE ==
             //
 
-            let mut channel = channel;
-
-            // change of category, already validated
-            if let Some(new_category_id) = params.new_in_category {
-                channel.in_category = new_category_id;
-            }
-
             // update the channel
-            ChannelById::<T>::insert(channel_id, channel.clone());
+            // ChannelById::<T>::insert(channel_id, channel.clone());
 
             // add assets to storage
             // This should not fail because of prior can_add_content() check!
-            T::StorageSystem::atomically_add_content(
-                object_owner,
-                upload_parameters,
-            )?;
+            if let Some((upload_parameters, object_owner)) = new_assets {
+                T::StorageSystem::atomically_add_content(
+                    object_owner,
+                    upload_parameters,
+                )?;
+            }
 
-            Self::deposit_event(RawEvent::ChannelUpdated(channel_id, channel, assets, params));
+            Self::deposit_event(RawEvent::ChannelUpdated(channel_id, channel, params));
             Ok(())
         }
 
@@ -825,8 +808,7 @@ decl_module! {
             origin,
             owner: ChannelOwner<T::MemberId, T::CuratorGroupId, T::DAOId>,
             channel_id: T::ChannelId,
-            assets: Vec<NewAsset<ContentParameters<T::ContentId, T::DataObjectTypeId>>>,
-            params: VideoCreationParameters<T::VideoCategoryId>,
+            params: VideoCreationParameters<ContentParameters<T>>,
         ) -> DispatchResult {
             Ok(())
         }
@@ -836,8 +818,7 @@ decl_module! {
             origin,
             owner: ChannelOwner<T::MemberId, T::CuratorGroupId, T::DAOId>,
             video: T::VideoId,
-            assets: Vec<NewAsset<ContentParameters<T::ContentId, T::DataObjectTypeId>>>,
-            params: VideoUpdateParameters<T::VideoCategoryId>,
+            params: VideoUpdateParameters<ContentParameters<T>>,
         ) -> DispatchResult {
             Ok(())
         }
@@ -856,8 +837,7 @@ decl_module! {
             origin,
             owner: ChannelOwner<T::MemberId, T::CuratorGroupId, T::DAOId>,
             channel_id: T::ChannelId,
-            assets: Vec<NewAsset<ContentParameters<T::ContentId, T::DataObjectTypeId>>>,
-            params: PlaylistCreationParameters<T::VideoId>,
+            params: PlaylistCreationParameters,
         ) -> DispatchResult {
             Ok(())
         }
@@ -867,8 +847,7 @@ decl_module! {
             origin,
             owner: ChannelOwner<T::MemberId, T::CuratorGroupId, T::DAOId>,
             playlist: T::PlaylistId,
-            assets: Vec<NewAsset<ContentParameters<T::ContentId, T::DataObjectTypeId>>>,
-            params: PlaylistUpdateParameters<T::VideoId>,
+            params: PlaylistUpdateParameters,
         ) -> DispatchResult {
             Ok(())
         }
@@ -955,8 +934,7 @@ decl_module! {
         pub fn create_person(
             origin,
             actor: PersonActor<T::MemberId, T::CuratorId>,
-            assets: Vec<NewAsset<ContentParameters<T::ContentId, T::DataObjectTypeId>>>,
-            params: PersonCreationParameters,
+            params: PersonCreationParameters<ContentParameters<T>>,
         ) -> DispatchResult {
             Ok(())
         }
@@ -966,8 +944,7 @@ decl_module! {
             origin,
             actor: PersonActor<T::MemberId, T::CuratorId>,
             person: T::PersonId,
-            assets: Vec<NewAsset<ContentParameters<T::ContentId, T::DataObjectTypeId>>>,
-            params: PersonUpdateParameters,
+            params: PersonUpdateParameters<ContentParameters<T>>,
         ) -> DispatchResult {
             Ok(())
         }
@@ -1001,7 +978,7 @@ decl_module! {
         }
 
         #[weight = 10_000_000] // TODO: adjust weight
-        pub fn curate_video(
+        pub fn censor_video(
             origin,
             curator_id: T::CuratorId,
             video_id: T::VideoId,
@@ -1011,7 +988,7 @@ decl_module! {
         }
 
         #[weight = 10_000_000] // TODO: adjust weight
-        pub fn curate_channel(
+        pub fn censor_channel(
             origin,
             curator_id: T::CuratorId,
             channel_id: T::ChannelId,
@@ -1021,7 +998,7 @@ decl_module! {
         }
 
         #[weight = 10_000_000] // TODO: adjust weight
-        pub fn uncurate_video(
+        pub fn uncensor_video(
             origin,
             curator_id: T::CuratorId,
             video_id: T::VideoId,
@@ -1031,7 +1008,7 @@ decl_module! {
         }
 
         #[weight = 10_000_000] // TODO: adjust weight
-        pub fn uncurate_channel(
+        pub fn uncensor_channel(
             origin,
             curator_id: T::CuratorId,
             channel_id: T::ChannelId,
@@ -1045,8 +1022,7 @@ decl_module! {
             origin,
             owner: ChannelOwner<T::MemberId, T::CuratorGroupId, T::DAOId>,
             channel_id: T::ChannelId,
-            assets: Vec<NewAsset<ContentParameters<T::ContentId, T::DataObjectTypeId>>>,
-            params: SeriesParameters<T::VideoCategoryId, T::VideoId>,
+            params: SeriesParameters<T::VideoId, ContentParameters<T>>,
         ) -> DispatchResult {
             Ok(())
         }
@@ -1056,8 +1032,7 @@ decl_module! {
             origin,
             owner: ChannelOwner<T::MemberId, T::CuratorGroupId, T::DAOId>,
             channel_id: T::ChannelId,
-            assets: Vec<NewAsset<ContentParameters<T::ContentId, T::DataObjectTypeId>>>,
-            params: SeriesParameters<T::VideoCategoryId, T::VideoId>,
+            params: SeriesParameters<T::VideoId, ContentParameters<T>>,
         ) -> DispatchResult {
             Ok(())
         }
@@ -1075,7 +1050,7 @@ decl_module! {
 
 impl<T: Trait> Module<T> {
     // TODO: make this private again after used in module
-    /// Increment number of classes, maintained by each curator group
+    /// Increment number of channels, maintained by each curator group
     pub fn increment_number_of_channels_owned_by_curator_groups(
         curator_group_ids: BTreeSet<T::CuratorGroupId>,
     ) {
@@ -1085,7 +1060,7 @@ impl<T: Trait> Module<T> {
     }
 
     // TODO: make this private again after used in module
-    /// Decrement number of classes, maintained by each curator group
+    /// Decrement number of channels, maintained by each curator group
     pub fn decrement_number_of_channels_owned_by_curator_groups(
         curator_group_ids: BTreeSet<T::CuratorGroupId>,
     ) {
@@ -1094,7 +1069,7 @@ impl<T: Trait> Module<T> {
         });
     }
 
-    /// Increment number of classes, maintained by curator group
+    /// Increment number of channels, maintained by curator group
     fn increment_number_of_channels_owned_by_curator_group(curator_group_id: T::CuratorGroupId) {
         <CuratorGroupById<T>>::mutate(curator_group_id, |curator_group| {
             curator_group.increment_number_of_channels_owned_count();
@@ -1102,7 +1077,7 @@ impl<T: Trait> Module<T> {
     }
 
     // TODO: make this private again after used in module
-    /// Decrement number of classes, maintained by curator group
+    /// Decrement number of channels, maintained by curator group
     pub fn decrement_number_of_channels_owned_by_curator_group(
         curator_group_id: T::CuratorGroupId,
     ) {
@@ -1149,8 +1124,8 @@ impl<T: Trait> Module<T> {
         Ok(ChannelById::<T>::get(channel_id))
     }
 
-    fn ensure_channel_category_exists(
-        channel_category_id: &T::ChannelCategoryId
+    pub fn ensure_channel_category_exists(
+        channel_category_id: &T::ChannelCategoryId,
     ) -> Result<ChannelCategory, Error<T>> {
         ensure!(
             ChannelCategoryById::<T>::contains_key(channel_category_id),
@@ -1160,26 +1135,27 @@ impl<T: Trait> Module<T> {
     }
 
     fn pick_upload_parameters_from_assets(
-        assets: &Vec<NewAsset<ContentParameters<T::ContentId, T::DataObjectTypeId>>>
-    ) -> Vec<UploadParameters<T>> {
-        assets.clone().into_iter().filter_map(|asset| {
-            match asset {
+        assets: &Vec<NewAsset<ContentParameters<T>>>,
+    ) -> Vec<ContentParameters<T>> {
+        assets
+            .clone()
+            .into_iter()
+            .filter_map(|asset| match asset {
                 NewAsset::Upload(upload_parameters) => Some(upload_parameters),
                 _ => None,
-            }
-        }).collect()
+            })
+            .collect()
     }
 
     fn channel_owner_to_object_owner(
-        channel_owner: &ChannelOwner<T::MemberId, T::CuratorGroupId, T::DAOId>
-    ) -> Result<StorageObjectOwner<T::MemberId, T::ChannelId, T::DAOId>, Error::<T>> {
+        channel_owner: &ChannelOwner<T::MemberId, T::CuratorGroupId, T::DAOId>,
+        channel_id: &T::ChannelId,
+    ) -> Result<StorageObjectOwner<T::MemberId, T::ChannelId, T::DAOId>, Error<T>> {
         match channel_owner {
-            ChannelOwner::Member(member_id) => Ok(StorageObjectOwner::Member(*member_id)),
+            ChannelOwner::Member(_member_id) => Ok(StorageObjectOwner::Channel(*channel_id)),
             ChannelOwner::CuratorGroup(_id) => {
-                Ok(StorageObjectOwner::AbstractStorageObjectOwner(
-                    AbstractStorageObjectOwner::WorkingGroup(WorkingGroup::Content)
-                ))
-            },
+                Ok(StorageObjectOwner::WorkingGroup(WorkingGroup::Content))
+            }
             _ => Err(Error::<T>::CannotConverChannelOwnerToObjectOwner),
         }
     }
@@ -1207,17 +1183,16 @@ decl_event!(
         VideoId = <T as Trait>::VideoId,
         VideoCategoryId = <T as Trait>::VideoCategoryId,
         ChannelId = <T as StorageOwnership>::ChannelId,
-        // MemberId = <T as MembershipTypes>::MemberId,
-        NewAsset = NewAsset<ContentParameters<ContentId<T>, DataObjectTypeId<T>>>,
+        NewAsset = NewAsset<ContentParameters<T>>,
         ChannelCategoryId = <T as Trait>::ChannelCategoryId,
         ChannelOwnershipTransferRequestId = <T as Trait>::ChannelOwnershipTransferRequestId,
         PlaylistId = <T as Trait>::PlaylistId,
         SeriesId = <T as Trait>::SeriesId,
         PersonId = <T as Trait>::PersonId,
-        // DAOId = <T as StorageOwnership>::DAOId,
         ChannelOwnershipTransferRequest = ChannelOwnershipTransferRequest<T>,
         Series = Series<<T as StorageOwnership>::ChannelId, <T as Trait>::VideoId>,
         Channel = Channel<T>,
+        ContentParameters = ContentParameters<T>,
     {
         // Curators
         CuratorGroupCreated(CuratorGroupId),
@@ -1230,17 +1205,18 @@ decl_event!(
         ChannelCreated(
             ChannelId,
             Channel,
-            Vec<NewAsset>,
-            ChannelCreationParameters<ChannelCategoryId>,
+            ChannelCreationParameters<ContentParameters>,
         ),
         ChannelUpdated(
             ChannelId,
             Channel,
-            Vec<NewAsset>,
-            ChannelUpdateParameters<ChannelCategoryId>,
+            ChannelUpdateParameters<ContentParameters>,
         ),
         ChannelDeleted(ChannelId),
 
+        ChannelCensored(ChannelId, Vec<u8> /* rationale */),
+        ChannelUncensored(ChannelId, Vec<u8> /* rationale */),
+
         // Channel Ownership Transfers
         ChannelOwnershipTransferRequested(
             ChannelOwnershipTransferRequestId,
@@ -1259,49 +1235,47 @@ decl_event!(
         VideoCategoryUpdated(VideoCategoryId, VideoCategoryUpdateParameters),
         VideoCategoryDeleted(VideoCategoryId),
 
-        VideoCreated(
-            VideoId,
-            Vec<NewAsset>,
-            VideoCreationParameters<VideoCategoryId>,
-        ),
-        VideoUpdated(
-            VideoId,
-            Vec<NewAsset>,
-            VideoUpdateParameters<VideoCategoryId>,
-        ),
+        VideoCreated(VideoId, VideoCreationParameters<ContentParameters>),
+        VideoUpdated(VideoId, VideoUpdateParameters<ContentParameters>),
         VideoDeleted(VideoId),
 
-        VideoCurated(VideoId, Vec<u8> /* rationale */),
-        VideoUncurated(VideoId, Vec<u8> /* rationale */),
+        VideoCensored(VideoId, Vec<u8> /* rationale */),
+        VideoUncensored(VideoId, Vec<u8> /* rationale */),
 
         // Featured Videos
         FeaturedVideosSet(Vec<VideoId>),
 
         // Video Playlists
-        PlaylistCreated(PlaylistId, PlaylistCreationParameters<VideoId>),
-        PlaylistUpdated(PlaylistId, PlaylistUpdateParameters<VideoId>),
+        PlaylistCreated(PlaylistId, PlaylistCreationParameters),
+        PlaylistUpdated(PlaylistId, PlaylistUpdateParameters),
         PlaylistDeleted(PlaylistId),
 
         // Series
         SeriesCreated(
             SeriesId,
             Vec<NewAsset>,
-            SeriesParameters<VideoCategoryId, VideoId>,
+            SeriesParameters<VideoId, ContentParameters>,
             Series,
         ),
         SeriesUpdated(
             SeriesId,
             Vec<NewAsset>,
-            SeriesParameters<VideoCategoryId, VideoId>,
+            SeriesParameters<VideoId, ContentParameters>,
             Series,
         ),
         SeriesDeleted(SeriesId),
 
         // Persons
-        PersonCreated(PersonId, Vec<NewAsset>, PersonCreationParameters),
-        PersonUpdated(PersonId, Vec<NewAsset>, PersonUpdateParameters),
+        PersonCreated(
+            PersonId,
+            Vec<NewAsset>,
+            PersonCreationParameters<ContentParameters>,
+        ),
+        PersonUpdated(
+            PersonId,
+            Vec<NewAsset>,
+            PersonUpdateParameters<ContentParameters>,
+        ),
         PersonDeleted(PersonId),
-        PersonAddedToVideo(PersonId, VideoId),
-        PersonRemovedFromVideo(PersonId, VideoId),
     }
 );

+ 4 - 4
runtime-modules/content/src/permissions/curator_group.rs

@@ -55,18 +55,18 @@ impl<T: Trait> CuratorGroup<T> {
         &mut self.curators
     }
 
-    /// Increment number of classes `CuratorGroup` maintains
+    /// Increment number of channels `CuratorGroup` owns
     pub fn increment_number_of_channels_owned_count(&mut self) {
         self.number_of_channels_owned += 1;
     }
 
-    /// Decrement number of classes `CuratorGroup` maintains
+    /// Decrement number of channels `CuratorGroup` owns
     pub fn decrement_number_of_channels_owned_count(&mut self) {
         self.number_of_channels_owned -= 1;
     }
 
-    /// Ensure curator group does not maintain any `Class`
-    pub fn ensure_curator_group_maintains_no_classes(&self) -> Result<(), Error<T>> {
+    /// Ensure curator group does not maintain any `Channel`
+    pub fn ensure_curator_group_owns_no_channels(&self) -> Result<(), Error<T>> {
         ensure!(
             self.number_of_channels_owned == 0,
             Error::<T>::CuratorGroupRemovalForbidden

+ 7 - 2
runtime-modules/content/src/permissions/mod.rs

@@ -92,13 +92,13 @@ pub fn ensure_actor_authorized_to_create_or_update_channel<T: Trait>(
     actor: &ContentActor<T::CuratorGroupId, T::CuratorId, T::MemberId>,
     owner: &ChannelOwner<T::MemberId, T::CuratorGroupId, T::DAOId>,
 ) -> DispatchResult {
-    let sender = ensure_signed(origin)?;
-
     // Authenticate actor and proposed channel owner
     match actor {
         // Lead should use their member or curator role to create or update channels.
         ContentActor::Lead => Err(Error::<T>::OperationDeniedForActor),
         ContentActor::Curator(curator_group_id, curator_id) => {
+            let sender = ensure_signed(origin)?;
+
             // Authorize curator, performing all checks to ensure curator can act
             CuratorGroup::<T>::perform_curator_in_group_auth(
                 curator_id,
@@ -115,6 +115,8 @@ pub fn ensure_actor_authorized_to_create_or_update_channel<T: Trait>(
             Ok(())
         }
         ContentActor::Member(member_id) => {
+            let sender = ensure_signed(origin)?;
+
             ensure_member_auth_success::<T>(member_id, &sender)?;
 
             ensure!(
@@ -124,6 +126,8 @@ pub fn ensure_actor_authorized_to_create_or_update_channel<T: Trait>(
 
             Ok(())
         }
+        // TODO:
+        // ContentActor::Dao(_daoId) => Error::<T>::OperationDeniedForActor,
     }?;
     Ok(())
 }
@@ -139,6 +143,7 @@ pub enum ContentActor<
     Curator(CuratorGroupId, CuratorId),
     Member(MemberId),
     Lead,
+    // Dao,
 }
 
 impl<

+ 2 - 1
runtime-modules/service-discovery/Cargo.toml

@@ -12,6 +12,7 @@ frame-support = { package = 'frame-support', default-features = false, git = 'ht
 system = { package = 'frame-system', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = '00768a1f21a579c478fe5d4f51e1fa71f7db9fd4'}
 sp-runtime = { package = 'sp-runtime', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = '00768a1f21a579c478fe5d4f51e1fa71f7db9fd4'}
 working-group = { package = 'pallet-working-group', default-features = false, path = '../working-group'}
+common = { package = 'pallet-common', default-features = false, path = '../common'}
 
 [dev-dependencies]
 sp-io = { package = 'sp-io', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = '00768a1f21a579c478fe5d4f51e1fa71f7db9fd4'}
@@ -23,7 +24,6 @@ stake = { package = 'pallet-stake', default-features = false, path = '../stake'}
 hiring = { package = 'pallet-hiring', default-features = false, path = '../hiring'}
 minting = { package = 'pallet-token-mint', default-features = false, path = '../token-minting'}
 recurringrewards = { package = 'pallet-recurring-reward', default-features = false, path = '../recurring-reward'}
-common = { package = 'pallet-common', default-features = false, path = '../common'}
 
 [features]
 default = ['std']
@@ -35,4 +35,5 @@ std = [
 	'system/std',
 	'sp-runtime/std',
 	'working-group/std',
+	'common/std'
 ]

+ 2 - 3
runtime-modules/service-discovery/src/lib.rs

@@ -23,6 +23,8 @@ mod mock;
 mod tests;
 
 use codec::{Decode, Encode};
+use common::Url;
+
 #[cfg(feature = "std")]
 use serde::{Deserialize, Serialize};
 
@@ -45,9 +47,6 @@ use system::ensure_root;
 /// base58 encoded IPNS identity multihash codec
 pub type IPNSIdentity = Vec<u8>;
 
-/// HTTP Url string to a discovery service endpoint
-pub type Url = Vec<u8>;
-
 // The storage working group instance alias.
 pub(crate) type StorageWorkingGroupInstance = working_group::Instance2;
 

+ 288 - 27
runtime-modules/storage/src/data_directory.rs

@@ -33,7 +33,7 @@ use system::ensure_root;
 use serde::{Deserialize, Serialize};
 
 use common::origin::ActorOriginValidator;
-pub use common::storage::{AbstractStorageObjectOwner, ContentParameters, StorageObjectOwner};
+pub use common::storage::{ContentParameters, StorageObjectOwner};
 pub(crate) use common::BlockAndTime;
 
 use crate::data_object_type_registry;
@@ -56,13 +56,16 @@ pub trait Trait:
     /// Provides random storage provider id.
     type StorageProviderHelper: StorageProviderHelper<Self>;
 
-    ///Active data object type validator.
+    /// Active data object type validator.
     type IsActiveDataObjectType: data_object_type_registry::IsActiveDataObjectType<Self>;
 
     /// Validates member id and origin combination.
     type MemberOriginValidator: ActorOriginValidator<Self::Origin, MemberId<Self>, Self::AccountId>;
 
     type MaxObjectsPerInjection: Get<u32>;
+
+    /// Default content quota for all actors.
+    type DefaultQuota: Get<Quota>;
 }
 
 decl_error! {
@@ -84,7 +87,28 @@ decl_error! {
         RequireRootOrigin,
 
         /// DataObject Injection Failed. Too Many DataObjects.
-        DataObjectsInjectionExceededLimit
+        DataObjectsInjectionExceededLimit,
+
+        /// Contant uploading failed. Actor quota objects limit exceeded.
+        QuotaObjectsLimitExceeded,
+
+        /// Contant uploading failed. Actor quota size limit exceeded.
+        QuotaSizeLimitExceeded,
+
+        /// Quota size limit upper bound exceeded
+        QuotaSizeLimitUpperBoundExceeded,
+
+        /// Quota objects limit upper bound exceeded
+        QuotaObjectsLimitUpperBoundExceeded,
+
+        /// Contant uploading failed. Actor quota size limit exceeded.
+        GlobalQuotaSizeLimitExceeded,
+
+        /// Contant uploading failed. Actor quota objects limit exceeded.
+        GlobalQuotaObjectsLimitExceeded,
+
+        /// Content uploading blocked.
+        ContentUploadingBlocked,
     }
 }
 
@@ -153,6 +177,68 @@ pub struct DataObjectInternal<
     pub ipfs_content_id: Vec<u8>,
 }
 
+#[derive(Clone, Copy)]
+pub struct Voucher {
+    pub size: u64,
+    pub objects: u64,
+}
+
+/// Uploading quota for StorageObjectOwner
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+#[derive(Clone, Copy, Encode, Decode, PartialEq, Eq, Debug, Default)]
+pub struct Quota {
+    // Total objects size limit per StorageObjectOwner
+    pub size_limit: u64,
+    // Total objects number limit per StorageObjectOwner
+    pub objects_limit: u64,
+    pub size_used: u64,
+    pub objects_used: u64,
+}
+
+impl Quota {
+    /// Create new quota with provided size & objects limits
+    pub const fn new(size_limit: u64, objects_limit: u64) -> Self {
+        Self {
+            size_limit,
+            objects_limit,
+            size_used: 0,
+            objects_used: 0,
+        }
+    }
+
+    /// Calculate free quota
+    pub fn calculate_voucher(&self) -> Voucher {
+        Voucher {
+            size: self.size_limit - self.size_used,
+            objects: self.objects_limit - self.objects_used,
+        }
+    }
+
+    pub fn fill_quota(self, voucher: Voucher) -> Self {
+        Self {
+            size_used: self.size_used + voucher.size,
+            objects_used: self.objects_used + voucher.objects,
+            ..self
+        }
+    }
+
+    pub fn release_quota(self, voucher: Voucher) -> Self {
+        Self {
+            size_used: self.size_used - voucher.size,
+            objects_used: self.objects_used - voucher.objects,
+            ..self
+        }
+    }
+
+    pub fn set_new_size_limit(&mut self, new_size_limit: u64) {
+        self.size_limit = new_size_limit;
+    }
+
+    pub fn set_new_objects_limit(&mut self, new_objects_limit: u64) {
+        self.objects_limit = new_objects_limit;
+    }
+}
+
 /// A map collection of unique DataObjects keyed by the ContentId
 pub type DataObjectsMap<T> = BTreeMap<ContentId<T>, DataObject<T>>;
 
@@ -164,6 +250,22 @@ decl_storage! {
         /// Maps data objects by their content id.
         pub DataObjectByContentId get(fn data_object_by_content_id) config():
             map hasher(blake2_128_concat) T::ContentId => Option<DataObject<T>>;
+
+        /// Maps storage owner to it`s quota. Created when the first upload by the new actor occured.
+        pub Quotas get(fn quotas) config():
+            map hasher(blake2_128_concat) StorageObjectOwner<MemberId<T>, ChannelId<T>, DAOId<T>> => Quota;
+
+        /// Upper bound for the Quota size limit.
+        pub QuotaSizeLimitUpperBound get(fn quota_size_limit_upper_bound) config(): u64;
+
+        /// Upper bound for the Quota objects number limit.
+        pub QuotaObjectsLimitUpperBound get(fn quota_objects_limit_upper_bound) config(): u64;
+
+        /// Global quota.
+        pub GlobalQuota get(fn global_quota) config(): Quota;
+
+        /// If all new uploads blocked
+        pub UploadingBlocked get(fn uploading_blocked) config(): bool;
     }
 }
 
@@ -174,6 +276,8 @@ decl_event! {
         StorageProviderId = StorageProviderId<T>,
         Content = Vec<ContentParameters<ContentId<T>, DataObjectTypeId<T>>>,
         ContentId = ContentId<T>,
+        QuotaLimit = u64,
+        UploadingStatus = bool
     {
         /// Emits on adding of the content.
         /// Params:
@@ -192,6 +296,23 @@ decl_event! {
         /// - Id of the relationship.
         /// - Id of the storage provider.
         ContentRejected(ContentId, StorageProviderId),
+
+        /// Emits when the storage object owner quota size limit update performed.
+        /// Params:
+        /// - StorageObjectOwner enum.
+        /// - quota size limit.
+        StorageObjectOwnerQuotaSizeLimitUpdated(StorageObjectOwner, QuotaLimit),
+
+        /// Emits when the storage object owner quota objects limit update performed.
+        /// Params:
+        /// - StorageObjectOwner enum.
+        /// - quota objects limit.
+        StorageObjectOwnerQuotaObjectsLimitUpdated(StorageObjectOwner, QuotaLimit),
+
+        /// Emits when the content uploading status update performed.
+        /// Params:
+        /// - UploadingStatus bool flag.
+        ContentUploadingStatusUpdated(UploadingStatus),
     }
 }
 
@@ -207,23 +328,31 @@ decl_module! {
         /// Maximum objects allowed per inject_data_objects() transaction
         const MaxObjectsPerInjection: u32 = T::MaxObjectsPerInjection::get();
 
-        /// Adds the content to the system. Member id should match its origin. The created DataObject
+        /// Adds the content to the system. The created DataObject
         /// awaits liaison to accept or reject it.
         #[weight = 10_000_000] // TODO: adjust weight
-        pub fn add_content_as_member(
+        pub fn add_content(
             origin,
-            member_id: MemberId<T>,
-            content: Vec<ContentParameters<T::ContentId, DataObjectTypeId<T>>>
+            owner: StorageObjectOwner<MemberId<T>, ChannelId<T>, DAOId<T>>,
+            content: Vec<ContentParameters<ContentId<T>, DataObjectTypeId<T>>>
         ) {
-            T::MemberOriginValidator::ensure_actor_origin(
-                origin,
-                member_id,
-            )?;
 
-            let owner = StorageObjectOwner::Member(member_id);
+            // Ensure given origin can perform operation under specific storage object owner
+            Self::ensure_storage_object_owner_origin(origin, &owner)?;
+
+            Self::ensure_uploading_is_not_blocked()?;
 
             Self::ensure_content_is_valid(&content)?;
 
+            let owner_quota = Self::get_quota(&owner);
+
+            // Ensure owner quota constraints satisfied.
+            // Calculate upload voucher
+            let upload_voucher = Self::ensure_owner_quota_constraints_satisfied(owner_quota, &content)?;
+
+            // Ensure global quota constraints satisfied.
+            Self::ensure_global_quota_constraints_satisfied(upload_voucher)?;
+
             let liaison = T::StorageProviderHelper::get_random_storage_provider()?;
 
             //
@@ -231,35 +360,63 @@ decl_module! {
             //
 
             // Let's create the entry then
-            Self::upload_content(liaison, content.clone(), owner.clone());
+            Self::upload_content(owner_quota, upload_voucher, liaison, content.clone(), owner.clone());
 
             Self::deposit_event(RawEvent::ContentAdded(content, owner));
         }
 
-        /// Adds the content to the system. Requires root privileges. The created DataObject
-        /// awaits liaison to accept or reject it.
+        /// Updates storage object owner quota objects limit. Requires leader privileges.
         #[weight = 10_000_000] // TODO: adjust weight
-        pub fn add_content(
+        pub fn update_storage_object_owner_quota_objects_limit(
             origin,
-            abstract_owner: AbstractStorageObjectOwner<ChannelId<T>, DAOId<T>>,
-            content: Vec<ContentParameters<ContentId<T>, DataObjectTypeId<T>>>
+            abstract_owner: StorageObjectOwner<MemberId<T>, ChannelId<T>, DAOId<T>>,
+            new_quota_objects_limit: u64
         ) {
-            ensure_root(origin)?;
+            <StorageWorkingGroup<T>>::ensure_origin_is_active_leader(origin)?;
+            ensure!(new_quota_objects_limit <= Self::quota_objects_limit_upper_bound(), Error::<T>::QuotaSizeLimitUpperBoundExceeded);
 
-            Self::ensure_content_is_valid(&content)?;
+            //
+            // == MUTATION SAFE ==
+            //
 
-            let owner = StorageObjectOwner::AbstractStorageObjectOwner(abstract_owner);
+            if <Quotas<T>>::contains_key(&abstract_owner) {
+                <Quotas<T>>::mutate(&abstract_owner, |quota| {
+                    quota.set_new_objects_limit(new_quota_objects_limit);
+                });
+            } else {
+                let mut quota = T::DefaultQuota::get();
+                quota.set_new_objects_limit(new_quota_objects_limit);
+                <Quotas<T>>::insert(&abstract_owner, quota);
+            };
 
-            let liaison = T::StorageProviderHelper::get_random_storage_provider()?;
+            Self::deposit_event(RawEvent::StorageObjectOwnerQuotaObjectsLimitUpdated(abstract_owner, new_quota_objects_limit));
+        }
+
+        /// Updates storage object owner quota size limit. Requires leader privileges.
+        #[weight = 10_000_000] // TODO: adjust weight
+        pub fn update_storage_object_owner_quota_size_limit(
+            origin,
+            abstract_owner: StorageObjectOwner<MemberId<T>, ChannelId<T>, DAOId<T>>,
+            new_quota_size_limit: u64
+        ) {
+            <StorageWorkingGroup<T>>::ensure_origin_is_active_leader(origin)?;
+            ensure!(new_quota_size_limit <= Self::quota_size_limit_upper_bound(), Error::<T>::QuotaObjectsLimitUpperBoundExceeded);
 
             //
             // == MUTATION SAFE ==
             //
 
-            // Let's create the entry then
-            Self::upload_content(liaison, content.clone(), owner.clone());
+            if <Quotas<T>>::contains_key(&abstract_owner) {
+                <Quotas<T>>::mutate(&abstract_owner, |quota| {
+                    quota.set_new_size_limit(new_quota_size_limit);
+                });
+            } else {
+                let mut quota = T::DefaultQuota::get();
+                quota.set_new_size_limit(new_quota_size_limit);
+                <Quotas<T>>::insert(&abstract_owner, quota);
+            };
 
-            Self::deposit_event(RawEvent::ContentAdded(content, owner));
+            Self::deposit_event(RawEvent::StorageObjectOwnerQuotaSizeLimitUpdated(abstract_owner, new_quota_size_limit));
         }
 
         /// Storage provider accepts a content. Requires signed storage provider account and its id.
@@ -297,6 +454,17 @@ decl_module! {
             Self::deposit_event(RawEvent::ContentRejected(content_id, storage_provider_id));
         }
 
+        /// Locks / unlocks content uploading
+        #[weight = 10_000_000] // TODO: adjust weight
+        fn update_content_uploading_status(origin, is_blocked: bool) {
+            <StorageWorkingGroup<T>>::ensure_origin_is_active_leader(origin)?;
+
+            // == MUTATION SAFE ==
+
+            <UploadingBlocked>::put(is_blocked);
+            Self::deposit_event(RawEvent::ContentUploadingStatusUpdated(is_blocked));
+        }
+
         // Sudo methods
 
         /// Removes the content id from the list of known content ids. Requires root privileges.
@@ -342,7 +510,89 @@ decl_module! {
 }
 
 impl<T: Trait> Module<T> {
+    // Ensure given origin can perform operation under specific storage object owner
+    fn ensure_storage_object_owner_origin(
+        origin: T::Origin,
+        owner: &StorageObjectOwner<MemberId<T>, ChannelId<T>, DAOId<T>>,
+    ) -> DispatchResult {
+        if let StorageObjectOwner::Member(member_id) = owner {
+            T::MemberOriginValidator::ensure_actor_origin(origin, *member_id)?;
+        } else {
+            ensure_root(origin)?;
+        };
+        Ok(())
+    }
+
+    // Get owner quota if exists, otherwise return default one.
+    fn get_quota(owner: &StorageObjectOwner<MemberId<T>, ChannelId<T>, DAOId<T>>) -> Quota {
+        if <Quotas<T>>::contains_key(owner) {
+            Self::quotas(owner)
+        } else {
+            T::DefaultQuota::get()
+        }
+    }
+
+    // Ensure content uploading is not blocked
+    fn ensure_uploading_is_not_blocked() -> DispatchResult {
+        ensure!(
+            !Self::uploading_blocked(),
+            Error::<T>::ContentUploadingBlocked
+        );
+        Ok(())
+    }
+
+    // Ensure owner quota constraints satisfied, returns total object length and total size voucher for this upload.
+    fn ensure_owner_quota_constraints_satisfied(
+        owner_quota: Quota,
+        content: &[ContentParameters<T::ContentId, DataObjectTypeId<T>>],
+    ) -> Result<Voucher, Error<T>> {
+        let owner_quota_voucher = owner_quota.calculate_voucher();
+
+        // Ensure total content length is less or equal then available per given owner quota
+        let content_length = content.len() as u64;
+
+        ensure!(
+            owner_quota_voucher.objects >= content_length,
+            Error::<T>::QuotaObjectsLimitExceeded
+        );
+
+        // Ensure total content size is less or equal then available per given owner quota
+        let content_size = content
+            .iter()
+            .fold(0, |total_size, content| total_size + content.size);
+
+        ensure!(
+            owner_quota_voucher.size >= content_size,
+            Error::<T>::QuotaSizeLimitExceeded
+        );
+
+        Ok(Voucher {
+            size: content_size,
+            objects: content_length,
+        })
+    }
+
+    // Ensures global quota constraints satisfied.
+    fn ensure_global_quota_constraints_satisfied(upload_voucher: Voucher) -> DispatchResult {
+        let global_quota_voucher = Self::global_quota().calculate_voucher();
+
+        ensure!(
+            global_quota_voucher.objects >= upload_voucher.objects,
+            Error::<T>::GlobalQuotaObjectsLimitExceeded
+        );
+
+        ensure!(
+            global_quota_voucher.size >= upload_voucher.size,
+            Error::<T>::GlobalQuotaSizeLimitExceeded
+        );
+
+        Ok(())
+    }
+
+    // Complete content upload, update quotas
     fn upload_content(
+        owner_quota: Quota,
+        upload_voucher: Voucher,
         liaison: StorageProviderId<T>,
         multi_content: Vec<ContentParameters<T::ContentId, DataObjectTypeId<T>>>,
         owner: StorageObjectOwner<MemberId<T>, ChannelId<T>, DAOId<T>>,
@@ -360,6 +610,12 @@ impl<T: Trait> Module<T> {
 
             <DataObjectByContentId<T>>::insert(content.content_id, data);
         }
+
+        // Updade or create owner quota.
+        <Quotas<T>>::insert(owner, owner_quota.fill_quota(upload_voucher));
+
+        // Update global quota
+        <GlobalQuota>::mutate(|global_quota| global_quota.fill_quota(upload_voucher));
     }
 
     fn ensure_content_is_valid(
@@ -437,15 +693,20 @@ impl<T: Trait> common::storage::StorageSystem<T> for Module<T> {
 
         let liaison = T::StorageProviderHelper::get_random_storage_provider()?;
 
-        Self::upload_content(liaison, content, owner);
+        let owner_quota = Self::get_quota(&owner);
+        let upload_voucher = Self::ensure_owner_quota_constraints_satisfied(owner_quota, &content)?;
+
+        Self::upload_content(owner_quota, upload_voucher, liaison, content, owner);
         Ok(())
     }
 
     fn can_add_content(
-        _owner: StorageObjectOwner<MemberId<T>, ChannelId<T>, DAOId<T>>,
+        owner: StorageObjectOwner<MemberId<T>, ChannelId<T>, DAOId<T>>,
         content: Vec<ContentParameters<T::ContentId, DataObjectTypeId<T>>>,
     ) -> DispatchResult {
         T::StorageProviderHelper::get_random_storage_provider()?;
+        let owner_quota = Self::get_quota(&owner);
+        Self::ensure_owner_quota_constraints_satisfied(owner_quota, &content)?;
         Self::ensure_content_is_valid(&content)
     }
 }

+ 5 - 2
runtime-modules/storage/src/data_object_storage_registry.rs

@@ -124,9 +124,10 @@ decl_event! {
 
         /// Emits on adding of the data object storage relationship.
         /// Params:
+        /// - Id of the storage provider.
         /// - Id of the relationship.
         /// - Current state of the relationship (True=Active).
-        DataObjectStorageRelationshipReadyUpdated(DataObjectStorageRelationshipId, bool),
+        DataObjectStorageRelationshipReadyUpdated(StorageProviderId, DataObjectStorageRelationshipId, bool),
     }
 }
 
@@ -226,7 +227,9 @@ impl<T: Trait> Module<T> {
         // Update DOSR and fire event.
         <Relationships<T>>::insert(id, dosr);
         Self::deposit_event(RawEvent::DataObjectStorageRelationshipReadyUpdated(
-            id, ready,
+            storage_provider_id,
+            id,
+            ready,
         ));
 
         Ok(())

+ 11 - 9
runtime-modules/storage/src/data_object_type_registry.rs

@@ -56,7 +56,7 @@ decl_error! {
 }
 
 /// Contains description and constrains for the data object.
-#[derive(Clone, Encode, Decode, PartialEq, Debug)]
+#[derive(Clone, Encode, Decode, PartialEq, Eq, Debug)]
 pub struct DataObjectType {
     /// Data object description.
     pub description: Vec<u8>,
@@ -97,13 +97,15 @@ decl_event! {
     {
         /// Emits on the data object type registration.
         /// Params:
+        /// - DataObjectType
         /// - Id of the new data object type.
-        DataObjectTypeRegistered(DataObjectTypeId),
+        DataObjectTypeRegistered(DataObjectType, DataObjectTypeId),
 
         /// Emits on the data object type update.
         /// Params:
         /// - Id of the updated data object type.
-        DataObjectTypeUpdated(DataObjectTypeId),
+        /// - DataObjectType
+        DataObjectTypeUpdated(DataObjectTypeId, DataObjectType),
     }
 }
 
@@ -147,7 +149,7 @@ decl_module! {
             <DataObjectTypes<T>>::insert(new_do_type_id, do_type);
             <NextDataObjectTypeId<T>>::mutate(|n| { *n += T::DataObjectTypeId::from(1); });
 
-            Self::deposit_event(RawEvent::DataObjectTypeRegistered(new_do_type_id));
+            Self::deposit_event(RawEvent::DataObjectTypeRegistered(data_object_type, new_do_type_id));
         }
 
         /// Updates existing data object type. Requires leader privileges.
@@ -166,7 +168,7 @@ decl_module! {
 
             <DataObjectTypes<T>>::insert(id, do_type);
 
-            Self::deposit_event(RawEvent::DataObjectTypeUpdated(id));
+            Self::deposit_event(RawEvent::DataObjectTypeUpdated(id, data_object_type));
         }
 
         /// Activates existing data object type. Requires leader privileges.
@@ -182,9 +184,9 @@ decl_module! {
             // == MUTATION SAFE ==
             //
 
-            <DataObjectTypes<T>>::insert(id, do_type);
+            <DataObjectTypes<T>>::insert(id, do_type.clone());
 
-            Self::deposit_event(RawEvent::DataObjectTypeUpdated(id));
+            Self::deposit_event(RawEvent::DataObjectTypeUpdated(id, do_type));
         }
 
         /// Deactivates existing data object type. Requires leader privileges.
@@ -200,9 +202,9 @@ decl_module! {
             // == MUTATION SAFE ==
             //
 
-            <DataObjectTypes<T>>::insert(id, do_type);
+            <DataObjectTypes<T>>::insert(id, do_type.clone());
 
-            Self::deposit_event(RawEvent::DataObjectTypeUpdated(id));
+            Self::deposit_event(RawEvent::DataObjectTypeUpdated(id, do_type));
         }
     }
 }

+ 20 - 35
runtime-modules/storage/src/tests/data_directory.rs

@@ -1,5 +1,6 @@
 #![cfg(test)]
 
+use common::storage::StorageObjectOwner;
 use frame_support::dispatch::DispatchError;
 use sp_std::collections::btree_map::BTreeMap;
 use system::RawOrigin;
@@ -10,7 +11,7 @@ use super::mock::*;
 fn succeed_adding_content() {
     with_default_mock_builder(|| {
         let sender = 1u64;
-        let member_id = 1u64;
+        let owner = StorageObjectOwner::Member(1u64);
 
         let first_content_parameters = ContentParameters {
             content_id: 1,
@@ -29,11 +30,7 @@ fn succeed_adding_content() {
         let multi_content = vec![first_content_parameters, second_content_parameters];
 
         // Register a content with 1234 bytes of type 1, which should be recognized.
-        let res = TestDataDirectory::add_content_as_member(
-            Origin::signed(sender),
-            member_id,
-            multi_content,
-        );
+        let res = TestDataDirectory::add_content(Origin::signed(sender), owner, multi_content);
         assert!(res.is_ok());
     });
 }
@@ -41,7 +38,7 @@ fn succeed_adding_content() {
 #[test]
 fn add_content_fails_with_invalid_origin() {
     with_default_mock_builder(|| {
-        let member_id = 1u64;
+        let owner = StorageObjectOwner::Member(1u64);
 
         let content_parameters = ContentParameters {
             content_id: 1,
@@ -51,11 +48,8 @@ fn add_content_fails_with_invalid_origin() {
         };
 
         // Register a content with 1234 bytes of type 1, which should be recognized.
-        let res = TestDataDirectory::add_content_as_member(
-            RawOrigin::Root.into(),
-            member_id,
-            vec![content_parameters],
-        );
+        let res =
+            TestDataDirectory::add_content(RawOrigin::Root.into(), owner, vec![content_parameters]);
         assert_eq!(res, Err(DispatchError::Other("Bad origin")));
     });
 }
@@ -71,7 +65,7 @@ fn accept_and_reject_content_fail_with_invalid_storage_provider() {
         run_to_block(1);
 
         let sender = 1u64;
-        let member_id = 1u64;
+        let owner = StorageObjectOwner::Member(1u64);
 
         let content_parameters = ContentParameters {
             content_id: 1,
@@ -80,11 +74,8 @@ fn accept_and_reject_content_fail_with_invalid_storage_provider() {
             ipfs_content_id: vec![1, 2, 3, 4],
         };
 
-        let res = TestDataDirectory::add_content_as_member(
-            Origin::signed(sender),
-            member_id,
-            vec![content_parameters],
-        );
+        let res =
+            TestDataDirectory::add_content(Origin::signed(sender), owner, vec![content_parameters]);
         assert!(res.is_ok());
 
         let content_id = match &System::events().last().unwrap().event {
@@ -124,7 +115,7 @@ fn accept_content_as_liaison() {
         run_to_block(1);
 
         let sender = 1u64;
-        let member_id = 1u64;
+        let owner = StorageObjectOwner::Member(1u64);
 
         let content_parameters = ContentParameters {
             content_id: 1,
@@ -133,11 +124,8 @@ fn accept_content_as_liaison() {
             ipfs_content_id: vec![1, 2, 3, 4],
         };
 
-        let res = TestDataDirectory::add_content_as_member(
-            Origin::signed(sender),
-            member_id,
-            vec![content_parameters],
-        );
+        let res =
+            TestDataDirectory::add_content(Origin::signed(sender), owner, vec![content_parameters]);
         assert!(res.is_ok());
 
         // An appropriate event should have been fired.
@@ -178,7 +166,7 @@ fn reject_content_as_liaison() {
         run_to_block(1);
 
         let sender = 1u64;
-        let member_id = 1u64;
+        let owner = StorageObjectOwner::Member(1u64);
 
         let content_parameters = ContentParameters {
             content_id: 1,
@@ -187,11 +175,8 @@ fn reject_content_as_liaison() {
             ipfs_content_id: vec![1, 2, 3, 4],
         };
 
-        let res = TestDataDirectory::add_content_as_member(
-            Origin::signed(sender),
-            member_id,
-            vec![content_parameters],
-        );
+        let res =
+            TestDataDirectory::add_content(Origin::signed(sender), owner, vec![content_parameters]);
         assert!(res.is_ok());
 
         // An appropriate event should have been fired.
@@ -273,7 +258,7 @@ fn data_object_injection_works() {
 fn data_object_injection_overwrites_and_removes_duplicate_ids() {
     with_default_mock_builder(|| {
         let sender = 1u64;
-        let member_id = 1u64;
+        let owner = StorageObjectOwner::Member(1u64);
         let content_id_1 = 1;
         let content_id_2 = 2;
 
@@ -293,15 +278,15 @@ fn data_object_injection_overwrites_and_removes_duplicate_ids() {
 
         // Start with some existing objects in directory which will be
         // overwritten
-        let res = TestDataDirectory::add_content_as_member(
+        let res = TestDataDirectory::add_content(
             Origin::signed(sender),
-            member_id,
+            owner.clone(),
             vec![content_parameters_first],
         );
         assert!(res.is_ok());
-        let res = TestDataDirectory::add_content_as_member(
+        let res = TestDataDirectory::add_content(
             Origin::signed(sender),
-            member_id,
+            owner,
             vec![content_parameters_second],
         );
         assert!(res.is_ok());

+ 3 - 1
runtime-modules/storage/src/tests/data_object_storage_registry.rs

@@ -156,7 +156,9 @@ fn test_toggle_ready() {
             System::events().last().unwrap().event,
             MetaEvent::data_object_storage_registry(
                 data_object_storage_registry::RawEvent::DataObjectStorageRelationshipReadyUpdated(
-                    dosr_id, true,
+                    storage_provider_id,
+                    dosr_id,
+                    true,
                 )
             )
         );

+ 12 - 8
runtime-modules/storage/src/tests/data_object_type_registry.rs

@@ -35,7 +35,7 @@ impl SetLeadFixture {
 fn get_last_data_object_type_id() -> u64 {
     let dot_id = match System::events().last().unwrap().event {
         MetaEvent::data_object_type_registry(
-            data_object_type_registry::RawEvent::DataObjectTypeRegistered(dot_id),
+            data_object_type_registry::RawEvent::DataObjectTypeRegistered(_, dot_id),
         ) => dot_id,
         _ => 0xdeadbeefu64, // unlikely value
     };
@@ -242,7 +242,7 @@ fn update_existing() {
         let res = TestDataObjectTypeRegistry::update_data_object_type(
             RawOrigin::Signed(DEFAULT_LEADER_ACCOUNT_ID).into(),
             dot_id,
-            updated3,
+            updated3.clone(),
         );
         assert!(res.is_ok());
         assert_eq!(
@@ -250,7 +250,7 @@ fn update_existing() {
             EventRecord {
                 phase: Phase::Initialization,
                 event: MetaEvent::data_object_type_registry(
-                    data_object_type_registry::RawEvent::DataObjectTypeUpdated(dot_id)
+                    data_object_type_registry::RawEvent::DataObjectTypeUpdated(dot_id, updated3)
                 ),
                 topics: vec![],
             }
@@ -295,7 +295,7 @@ fn activate_existing() {
         };
         let id_res = TestDataObjectTypeRegistry::register_data_object_type(
             RawOrigin::Signed(DEFAULT_LEADER_ACCOUNT_ID).into(),
-            data,
+            data.clone(),
         );
         assert!(id_res.is_ok());
         assert_eq!(
@@ -304,6 +304,7 @@ fn activate_existing() {
                 phase: Phase::Initialization,
                 event: MetaEvent::data_object_type_registry(
                     data_object_type_registry::RawEvent::DataObjectTypeRegistered(
+                        data,
                         expected_data_object_type_id
                     )
                 ),
@@ -312,9 +313,9 @@ fn activate_existing() {
         );
 
         // Retrieve, and ensure it's not active.
-        let data = TestDataObjectTypeRegistry::data_object_types(expected_data_object_type_id);
-        assert!(data.is_some());
-        assert!(!data.unwrap().active);
+        let mut data =
+            TestDataObjectTypeRegistry::data_object_types(expected_data_object_type_id).unwrap();
+        assert!(!data.active);
 
         // Now activate the data object type
         let res = TestDataObjectTypeRegistry::activate_data_object_type(
@@ -322,13 +323,16 @@ fn activate_existing() {
             expected_data_object_type_id,
         );
         assert!(res.is_ok());
+
+        data.active = true;
         assert_eq!(
             *System::events().last().unwrap(),
             EventRecord {
                 phase: Phase::Initialization,
                 event: MetaEvent::data_object_type_registry(
                     data_object_type_registry::RawEvent::DataObjectTypeUpdated(
-                        expected_data_object_type_id
+                        expected_data_object_type_id,
+                        data
                     )
                 ),
                 topics: vec![],

+ 25 - 0
runtime-modules/storage/src/tests/mock.rs

@@ -11,6 +11,7 @@ use sp_runtime::{
 };
 
 use crate::data_directory::ContentIdExists;
+use crate::data_directory::Quota;
 pub use crate::data_directory::{ContentParameters, StorageObjectOwner};
 use crate::data_object_type_registry::IsActiveDataObjectType;
 use crate::ContentId;
@@ -96,6 +97,7 @@ parameter_types! {
     pub const AvailableBlockRatio: Perbill = Perbill::one();
     pub const MinimumPeriod: u64 = 5;
     pub const MaxObjectsPerInjection: u32 = 5;
+    pub const DefaultQuota: Quota = Quota::new(5000, 50);
 }
 
 impl system::Trait for Test {
@@ -179,6 +181,7 @@ impl data_directory::Trait for Test {
     type IsActiveDataObjectType = AnyDataObjectTypeIsActive;
     type MemberOriginValidator = ();
     type MaxObjectsPerInjection = MaxObjectsPerInjection;
+    type DefaultQuota = DefaultQuota;
 }
 
 impl crate::data_directory::StorageProviderHelper<Test> for () {
@@ -236,6 +239,9 @@ impl hiring::Trait for Test {
 }
 
 pub struct ExtBuilder {
+    quota_objects_limit_upper_bound: u64,
+    quota_size_limit_upper_bound: u64,
+    global_quota: Quota,
     first_data_object_type_id: u64,
     first_content_id: u64,
     first_relationship_id: u64,
@@ -245,6 +251,9 @@ pub struct ExtBuilder {
 impl Default for ExtBuilder {
     fn default() -> Self {
         Self {
+            quota_objects_limit_upper_bound: 200,
+            quota_size_limit_upper_bound: 20000,
+            global_quota: Quota::new(2000000, 2000),
             first_data_object_type_id: 1,
             first_content_id: 2,
             first_relationship_id: 3,
@@ -258,23 +267,39 @@ impl ExtBuilder {
         self.first_data_object_type_id = first_data_object_type_id;
         self
     }
+
     pub fn first_content_id(mut self, first_content_id: u64) -> Self {
         self.first_content_id = first_content_id;
         self
     }
+
     pub fn first_relationship_id(mut self, first_relationship_id: u64) -> Self {
         self.first_relationship_id = first_relationship_id;
         self
     }
+
     pub fn first_metadata_id(mut self, first_metadata_id: u64) -> Self {
         self.first_metadata_id = first_metadata_id;
         self
     }
+
     pub fn build(self) -> sp_io::TestExternalities {
         let mut t = system::GenesisConfig::default()
             .build_storage::<Test>()
             .unwrap();
 
+        data_directory::GenesisConfig::<Test> {
+            quota_size_limit_upper_bound: self.quota_size_limit_upper_bound,
+            quota_objects_limit_upper_bound: self.quota_objects_limit_upper_bound,
+            global_quota: self.global_quota,
+            data_object_by_content_id: vec![],
+            known_content_ids: vec![],
+            quotas: vec![],
+            uploading_blocked: false,
+        }
+        .assimilate_storage(&mut t)
+        .unwrap();
+
         data_object_type_registry::GenesisConfig::<Test> {
             first_data_object_type_id: self.first_data_object_type_id,
         }

+ 3 - 2
runtime/src/lib.rs

@@ -59,6 +59,7 @@ pub use membership;
 pub use pallet_balances::Call as BalancesCall;
 pub use pallet_staking::StakerStatus;
 pub use proposals_codex::ProposalsConfigParameters;
+use storage::data_directory::Quota;
 pub use storage::{data_directory, data_object_type_registry};
 pub use working_group;
 
@@ -369,13 +370,11 @@ impl pallet_finality_tracker::Trait for Runtime {
 parameter_types! {
     pub const MaxNumberOfCuratorsPerGroup: MaxNumber = 50;
     pub const ChannelOwnershipPaymentEscrowId: [u8; 8] = *b"chescrow";
-    pub const ChannelRevenueTreasuryId: [u8; 8] = *b"chtresry";
 }
 
 impl content::Trait for Runtime {
     type Event = Event;
     type ChannelOwnershipPaymentEscrowId = ChannelOwnershipPaymentEscrowId;
-    type ChannelRevenueTreasuryId = ChannelRevenueTreasuryId;
     type ChannelCategoryId = ChannelCategoryId;
     type VideoId = VideoId;
     type VideoCategoryId = VideoCategoryId;
@@ -455,6 +454,7 @@ impl memo::Trait for Runtime {
 
 parameter_types! {
     pub const MaxObjectsPerInjection: u32 = 100;
+    pub const DefaultQuota: Quota = Quota::new(5000, 50);
 }
 
 impl storage::data_object_type_registry::Trait for Runtime {
@@ -467,6 +467,7 @@ impl storage::data_directory::Trait for Runtime {
     type IsActiveDataObjectType = DataObjectTypeRegistry;
     type MemberOriginValidator = MembershipOriginValidator<Self>;
     type MaxObjectsPerInjection = MaxObjectsPerInjection;
+    type DefaultQuota = DefaultQuota;
 }
 
 impl storage::data_object_storage_registry::Trait for Runtime {

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 3 - 3
types/augment-codec/all.ts


+ 21 - 1
types/augment-codec/augment-api-query.ts

@@ -4,7 +4,7 @@
 import { AnyNumber, ITuple, Observable } from '@polkadot/types/types';
 import { Option, Vec } from '@polkadot/types/codec';
 import { Bytes, bool, u32, u64 } from '@polkadot/types/primitive';
-import { Application, ApplicationId, ApplicationOf, Category, CategoryId, Channel, ChannelCategory, ChannelCategoryId, ChannelId, ChannelOwnershipTransferRequest, ChannelOwnershipTransferRequestId, ContentId, CuratorGroup, CuratorGroupId, DataObject, DataObjectStorageRelationship, DataObjectStorageRelationshipId, DataObjectType, DataObjectTypeId, DiscussionPost, DiscussionThread, ElectionStage, ElectionStake, HiringApplicationId, InputValidationLengthConstraint, MemberId, Membership, MemoText, Mint, MintId, Opening, OpeningId, OpeningOf, PaidMembershipTerms, PaidTermId, Person, PersonId, Playlist, PlaylistId, Post, PostId, ProposalDetailsOf, ProposalId, ProposalOf, Recipient, RecipientId, RewardRelationship, RewardRelationshipId, SealedVote, Seats, Series, SeriesId, ServiceProviderRecord, Stake, StakeId, StorageProviderId, Thread, ThreadCounter, ThreadId, TransferableStake, Url, Video, VideoCategory, VideoCategoryId, VideoId, VoteKind, WorkerId, WorkerOf } from './all';
+import { Application, ApplicationId, ApplicationOf, Category, CategoryId, Channel, ChannelCategory, ChannelCategoryId, ChannelId, ChannelOwnershipTransferRequest, ChannelOwnershipTransferRequestId, ContentId, CuratorGroup, CuratorGroupId, DataObject, DataObjectStorageRelationship, DataObjectStorageRelationshipId, DataObjectType, DataObjectTypeId, DiscussionPost, DiscussionThread, ElectionStage, ElectionStake, HiringApplicationId, InputValidationLengthConstraint, MemberId, Membership, MemoText, Mint, MintId, Opening, OpeningId, OpeningOf, PaidMembershipTerms, PaidTermId, Person, PersonId, Playlist, PlaylistId, Post, PostId, ProposalDetailsOf, ProposalId, ProposalOf, Quota, Recipient, RecipientId, RewardRelationship, RewardRelationshipId, SealedVote, Seats, Series, SeriesId, ServiceProviderRecord, Stake, StakeId, StorageObjectOwner, StorageProviderId, Thread, ThreadCounter, ThreadId, TransferableStake, Url, Video, VideoCategory, VideoCategoryId, VideoId, VoteKind, WorkerId, WorkerOf } from './all';
 import { UncleEntryItem } from '@polkadot/types/interfaces/authorship';
 import { BabeAuthorityWeight, MaybeRandomness, NextConfigDescriptor, Randomness } from '@polkadot/types/interfaces/babe';
 import { AccountData, BalanceLock } from '@polkadot/types/interfaces/balances';
@@ -255,10 +255,30 @@ declare module '@polkadot/api/types/storage' {
        * Maps data objects by their content id.
        **/
       dataObjectByContentId: AugmentedQuery<ApiType, (arg: ContentId | string | Uint8Array) => Observable<Option<DataObject>>>;
+      /**
+       * Global quota.
+       **/
+      globalQuota: AugmentedQuery<ApiType, () => Observable<Quota>>;
       /**
        * List of ids known to the system.
        **/
       knownContentIds: AugmentedQuery<ApiType, () => Observable<Vec<ContentId>>>;
+      /**
+       * Upper bound for the Quota objects number limit.
+       **/
+      quotaObjectsLimitUpperBound: AugmentedQuery<ApiType, () => Observable<u64>>;
+      /**
+       * Maps storage owner to it`s quota. Created when the first upload by the new actor occured.
+       **/
+      quotas: AugmentedQuery<ApiType, (arg: StorageObjectOwner | { Member: any } | { Channel: any } | { DAO: any } | { Council: any } | { WorkingGroup: any } | string | Uint8Array) => Observable<Quota>>;
+      /**
+       * Upper bound for the Quota size limit.
+       **/
+      quotaSizeLimitUpperBound: AugmentedQuery<ApiType, () => Observable<u64>>;
+      /**
+       * If all new uploads blocked
+       **/
+      uploadingBlocked: AugmentedQuery<ApiType, () => Observable<bool>>;
     };
     dataObjectStorageRegistry: {
       /**

+ 37 - 30
types/augment-codec/augment-api-tx.ts

@@ -4,7 +4,7 @@
 import { AnyNumber } from '@polkadot/types/types';
 import { Compact, Option, Vec } from '@polkadot/types/codec';
 import { Bytes, bool, u16, u32, u64 } from '@polkadot/types/primitive';
-import { AbstractStorageObjectOwner, ActivateOpeningAt, AddOpeningParameters, ApplicationId, ApplicationIdSet, BalanceOfMint, CategoryId, ChannelCategoryCreationParameters, ChannelCategoryId, ChannelCategoryUpdateParameters, ChannelCreationParameters, ChannelId, ChannelOwner, ChannelOwnershipTransferRequestId, ChannelUpdateParameters, ContentId, ContentParameters, CuratorGroupId, CuratorId, DataObjectStorageRelationshipId, DataObjectType, DataObjectTypeId, DataObjectsMap, ElectionParameters, FillOpeningParameters, MemberId, MemoText, NewAsset, OpeningId, OpeningPolicyCommitment, OpeningType, PaidTermId, PersonActor, PersonCreationParameters, PersonId, PersonUpdateParameters, PlaylistCreationParameters, PlaylistId, PlaylistUpdateParameters, PostId, ProposalId, RewardPolicy, SeriesId, SeriesParameters, StorageProviderId, TerminateRoleParameters, ThreadId, Url, VideoCategoryCreationParameters, VideoCategoryId, VideoCategoryUpdateParameters, VideoCreationParameters, VideoId, VideoUpdateParameters, VoteKind, WorkerId, WorkingGroup } from './all';
+import { ActivateOpeningAt, AddOpeningParameters, ApplicationId, ApplicationIdSet, BalanceOfMint, CategoryId, ChannelCategoryCreationParameters, ChannelCategoryId, ChannelCategoryUpdateParameters, ChannelCreationParameters, ChannelId, ChannelOwner, ChannelOwnershipTransferRequestId, ChannelUpdateParameters, ContentId, ContentParameters, CuratorGroupId, CuratorId, DataObjectStorageRelationshipId, DataObjectType, DataObjectTypeId, DataObjectsMap, ElectionParameters, FillOpeningParameters, MemberId, MemoText, OpeningId, OpeningPolicyCommitment, OpeningType, PaidTermId, PersonActor, PersonCreationParameters, PersonId, PersonUpdateParameters, PlaylistCreationParameters, PlaylistId, PlaylistUpdateParameters, PostId, ProposalId, RewardPolicy, SeriesId, SeriesParameters, StorageObjectOwner, StorageProviderId, TerminateRoleParameters, ThreadId, Url, VideoCategoryCreationParameters, VideoCategoryId, VideoCategoryUpdateParameters, VideoCreationParameters, VideoId, VideoUpdateParameters, VoteKind, WorkerId, WorkingGroup } from './all';
 import { Extrinsic, Signature } from '@polkadot/types/interfaces/extrinsics';
 import { GrandpaEquivocationProof, KeyOwnerProof } from '@polkadot/types/interfaces/grandpa';
 import { Heartbeat } from '@polkadot/types/interfaces/imOnline';
@@ -99,56 +99,56 @@ declare module '@polkadot/api/types/submittable' {
       transferKeepAlive: AugmentedSubmittable<(dest: LookupSource | string | Uint8Array, value: Compact<Balance> | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>>;
     };
     content: {
-      acceptChannelTransfer: AugmentedSubmittable<(owner: ChannelOwner | { Nobody: any } | { Member: any } | { Curators: any } | { Dao: any } | string | Uint8Array, requestId: ChannelOwnershipTransferRequestId | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>>;
+      acceptChannelTransfer: AugmentedSubmittable<(owner: ChannelOwner | { Member: any } | { Curators: any } | { Dao: any } | string | Uint8Array, requestId: ChannelOwnershipTransferRequestId | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>>;
       /**
        * Add curator to curator group under given `curator_group_id`
        **/
       addCuratorToGroup: AugmentedSubmittable<(curatorGroupId: CuratorGroupId | AnyNumber | Uint8Array, curatorId: CuratorId | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>>;
-      addPersonToVideo: AugmentedSubmittable<(owner: ChannelOwner | { Nobody: any } | { Member: any } | { Curators: any } | { Dao: any } | string | Uint8Array, videoId: VideoId | AnyNumber | Uint8Array, person: PersonId | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>>;
+      addPersonToVideo: AugmentedSubmittable<(owner: ChannelOwner | { Member: any } | { Curators: any } | { Dao: any } | string | Uint8Array, videoId: VideoId | AnyNumber | Uint8Array, person: PersonId | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>>;
       cancelChannelTransferRequest: AugmentedSubmittable<(requestId: ChannelOwnershipTransferRequestId | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>>;
-      createChannel: AugmentedSubmittable<(owner: ChannelOwner | { Nobody: any } | { Member: any } | { Curators: any } | { Dao: any } | string | Uint8Array, assets: Vec<NewAsset> | (NewAsset | { Upload: any } | { Uri: any } | string | Uint8Array)[], params: ChannelCreationParameters | { in_category?: any; meta?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>>;
+      censorChannel: AugmentedSubmittable<(curatorId: CuratorId | AnyNumber | Uint8Array, channelId: ChannelId | AnyNumber | Uint8Array, rationale: Bytes | string | Uint8Array) => SubmittableExtrinsic<ApiType>>;
+      censorVideo: AugmentedSubmittable<(curatorId: CuratorId | AnyNumber | Uint8Array, videoId: VideoId | AnyNumber | Uint8Array, rationale: Bytes | string | Uint8Array) => SubmittableExtrinsic<ApiType>>;
+      createChannel: AugmentedSubmittable<(owner: ChannelOwner | { Member: any } | { Curators: any } | { Dao: any } | string | Uint8Array, params: ChannelCreationParameters | { assets?: any; meta?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>>;
       createChannelCategory: AugmentedSubmittable<(curator: CuratorId | AnyNumber | Uint8Array, params: ChannelCategoryCreationParameters | { meta?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>>;
       /**
        * Add new curator group to runtime storage
        **/
       createCuratorGroup: AugmentedSubmittable<() => SubmittableExtrinsic<ApiType>>;
-      createPerson: AugmentedSubmittable<(actor: PersonActor | { Member: any } | { Curator: any } | string | Uint8Array, assets: Vec<NewAsset> | (NewAsset | { Upload: any } | { Uri: any } | string | Uint8Array)[], params: PersonCreationParameters | { meta?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>>;
-      createPlaylist: AugmentedSubmittable<(owner: ChannelOwner | { Nobody: any } | { Member: any } | { Curators: any } | { Dao: any } | string | Uint8Array, channelId: ChannelId | AnyNumber | Uint8Array, assets: Vec<NewAsset> | (NewAsset | { Upload: any } | { Uri: any } | string | Uint8Array)[], params: PlaylistCreationParameters | { videos?: any; meta?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>>;
-      createSeries: AugmentedSubmittable<(owner: ChannelOwner | { Nobody: any } | { Member: any } | { Curators: any } | { Dao: any } | string | Uint8Array, channelId: ChannelId | AnyNumber | Uint8Array, assets: Vec<NewAsset> | (NewAsset | { Upload: any } | { Uri: any } | string | Uint8Array)[], params: SeriesParameters | { seasons?: any; meta?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>>;
-      createVideo: AugmentedSubmittable<(owner: ChannelOwner | { Nobody: any } | { Member: any } | { Curators: any } | { Dao: any } | string | Uint8Array, channelId: ChannelId | AnyNumber | Uint8Array, assets: Vec<NewAsset> | (NewAsset | { Upload: any } | { Uri: any } | string | Uint8Array)[], params: VideoCreationParameters | { in_category?: any; meta?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>>;
+      createPerson: AugmentedSubmittable<(actor: PersonActor | { Member: any } | { Curator: any } | string | Uint8Array, params: PersonCreationParameters | { assets?: any; meta?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>>;
+      createPlaylist: AugmentedSubmittable<(owner: ChannelOwner | { Member: any } | { Curators: any } | { Dao: any } | string | Uint8Array, channelId: ChannelId | AnyNumber | Uint8Array, params: PlaylistCreationParameters | { meta?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>>;
+      createSeries: AugmentedSubmittable<(owner: ChannelOwner | { Member: any } | { Curators: any } | { Dao: any } | string | Uint8Array, channelId: ChannelId | AnyNumber | Uint8Array, params: SeriesParameters | { assets?: any; seasons?: any; meta?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>>;
+      createVideo: AugmentedSubmittable<(owner: ChannelOwner | { Member: any } | { Curators: any } | { Dao: any } | string | Uint8Array, channelId: ChannelId | AnyNumber | Uint8Array, params: VideoCreationParameters | { assets?: any; meta?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>>;
       createVideoCategory: AugmentedSubmittable<(curator: CuratorId | AnyNumber | Uint8Array, params: VideoCategoryCreationParameters | { meta?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>>;
-      curateChannel: AugmentedSubmittable<(curatorId: CuratorId | AnyNumber | Uint8Array, channelId: ChannelId | AnyNumber | Uint8Array, rationale: Bytes | string | Uint8Array) => SubmittableExtrinsic<ApiType>>;
-      curateVideo: AugmentedSubmittable<(curatorId: CuratorId | AnyNumber | Uint8Array, videoId: VideoId | AnyNumber | Uint8Array, rationale: Bytes | string | Uint8Array) => SubmittableExtrinsic<ApiType>>;
-      deleteChannel: AugmentedSubmittable<(owner: ChannelOwner | { Nobody: any } | { Member: any } | { Curators: any } | { Dao: any } | string | Uint8Array, channelId: ChannelId | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>>;
+      deleteChannel: AugmentedSubmittable<(owner: ChannelOwner | { Member: any } | { Curators: any } | { Dao: any } | string | Uint8Array, channelId: ChannelId | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>>;
       deleteChannelCategory: AugmentedSubmittable<(curator: CuratorId | AnyNumber | Uint8Array, category: ChannelCategoryId | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>>;
       /**
        * Remove curator group under given `curator_group_id` from runtime storage
        **/
       deleteCuratorGroup: AugmentedSubmittable<(curatorGroupId: CuratorGroupId | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>>;
       deletePerson: AugmentedSubmittable<(actor: PersonActor | { Member: any } | { Curator: any } | string | Uint8Array, person: PersonId | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>>;
-      deletePlaylist: AugmentedSubmittable<(owner: ChannelOwner | { Nobody: any } | { Member: any } | { Curators: any } | { Dao: any } | string | Uint8Array, channelId: ChannelId | AnyNumber | Uint8Array, playlist: PlaylistId | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>>;
-      deleteSeries: AugmentedSubmittable<(owner: ChannelOwner | { Nobody: any } | { Member: any } | { Curators: any } | { Dao: any } | string | Uint8Array, series: SeriesId | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>>;
-      deleteVideo: AugmentedSubmittable<(owner: ChannelOwner | { Nobody: any } | { Member: any } | { Curators: any } | { Dao: any } | string | Uint8Array, video: VideoId | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>>;
+      deletePlaylist: AugmentedSubmittable<(owner: ChannelOwner | { Member: any } | { Curators: any } | { Dao: any } | string | Uint8Array, channelId: ChannelId | AnyNumber | Uint8Array, playlist: PlaylistId | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>>;
+      deleteSeries: AugmentedSubmittable<(owner: ChannelOwner | { Member: any } | { Curators: any } | { Dao: any } | string | Uint8Array, series: SeriesId | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>>;
+      deleteVideo: AugmentedSubmittable<(owner: ChannelOwner | { Member: any } | { Curators: any } | { Dao: any } | string | Uint8Array, video: VideoId | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>>;
       deleteVideoCategory: AugmentedSubmittable<(curator: CuratorId | AnyNumber | Uint8Array, category: VideoCategoryId | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>>;
       /**
        * Remove curator from a given curator group
        **/
       removeCuratorFromGroup: AugmentedSubmittable<(curatorGroupId: CuratorGroupId | AnyNumber | Uint8Array, curatorId: CuratorId | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>>;
-      removePersonFromVideo: AugmentedSubmittable<(owner: ChannelOwner | { Nobody: any } | { Member: any } | { Curators: any } | { Dao: any } | string | Uint8Array, videoId: VideoId | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>>;
-      requestChannelTransfer: AugmentedSubmittable<(newOwner: ChannelOwner | { Nobody: any } | { Member: any } | { Curators: any } | { Dao: any } | string | Uint8Array, channelId: ChannelId | AnyNumber | Uint8Array, payment: BalanceOf | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>>;
+      removePersonFromVideo: AugmentedSubmittable<(owner: ChannelOwner | { Member: any } | { Curators: any } | { Dao: any } | string | Uint8Array, videoId: VideoId | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>>;
+      requestChannelTransfer: AugmentedSubmittable<(newOwner: ChannelOwner | { Member: any } | { Curators: any } | { Dao: any } | string | Uint8Array, channelId: ChannelId | AnyNumber | Uint8Array, payment: BalanceOf | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>>;
       /**
        * Set `is_active` status for curator group under given `curator_group_id`
        **/
       setCuratorGroupStatus: AugmentedSubmittable<(curatorGroupId: CuratorGroupId | AnyNumber | Uint8Array, isActive: bool | boolean | Uint8Array) => SubmittableExtrinsic<ApiType>>;
       setFeaturedVideos: AugmentedSubmittable<(list: Vec<VideoId> | (VideoId | AnyNumber | Uint8Array)[]) => SubmittableExtrinsic<ApiType>>;
-      uncurateChannel: AugmentedSubmittable<(curatorId: CuratorId | AnyNumber | Uint8Array, channelId: ChannelId | AnyNumber | Uint8Array, rationale: Bytes | string | Uint8Array) => SubmittableExtrinsic<ApiType>>;
-      uncurateVideo: AugmentedSubmittable<(curatorId: CuratorId | AnyNumber | Uint8Array, videoId: VideoId | AnyNumber | Uint8Array, rationale: Bytes | string | Uint8Array) => SubmittableExtrinsic<ApiType>>;
-      updateChannel: AugmentedSubmittable<(owner: ChannelOwner | { Nobody: any } | { Member: any } | { Curators: any } | { Dao: any } | string | Uint8Array, channelId: ChannelId | AnyNumber | Uint8Array, newAssets: Vec<NewAsset> | (NewAsset | { Upload: any } | { Uri: any } | string | Uint8Array)[], params: ChannelUpdateParameters | { new_in_category?: any; new_meta?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>>;
+      uncensorChannel: AugmentedSubmittable<(curatorId: CuratorId | AnyNumber | Uint8Array, channelId: ChannelId | AnyNumber | Uint8Array, rationale: Bytes | string | Uint8Array) => SubmittableExtrinsic<ApiType>>;
+      uncensorVideo: AugmentedSubmittable<(curatorId: CuratorId | AnyNumber | Uint8Array, videoId: VideoId | AnyNumber | Uint8Array, rationale: Bytes | string | Uint8Array) => SubmittableExtrinsic<ApiType>>;
+      updateChannel: AugmentedSubmittable<(owner: ChannelOwner | { Member: any } | { Curators: any } | { Dao: any } | string | Uint8Array, channelId: ChannelId | AnyNumber | Uint8Array, params: ChannelUpdateParameters | { assets?: any; new_meta?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>>;
       updateChannelCategory: AugmentedSubmittable<(curator: CuratorId | AnyNumber | Uint8Array, category: ChannelCategoryId | AnyNumber | Uint8Array, params: ChannelCategoryUpdateParameters | { new_meta?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>>;
-      updatePerson: AugmentedSubmittable<(actor: PersonActor | { Member: any } | { Curator: any } | string | Uint8Array, person: PersonId | AnyNumber | Uint8Array, assets: Vec<NewAsset> | (NewAsset | { Upload: any } | { Uri: any } | string | Uint8Array)[], params: PersonUpdateParameters | { meta?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>>;
-      updatePlaylist: AugmentedSubmittable<(owner: ChannelOwner | { Nobody: any } | { Member: any } | { Curators: any } | { Dao: any } | string | Uint8Array, playlist: PlaylistId | AnyNumber | Uint8Array, assets: Vec<NewAsset> | (NewAsset | { Upload: any } | { Uri: any } | string | Uint8Array)[], params: PlaylistUpdateParameters | { new_videos?: any; new_meta?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>>;
-      updateSeries: AugmentedSubmittable<(owner: ChannelOwner | { Nobody: any } | { Member: any } | { Curators: any } | { Dao: any } | string | Uint8Array, channelId: ChannelId | AnyNumber | Uint8Array, assets: Vec<NewAsset> | (NewAsset | { Upload: any } | { Uri: any } | string | Uint8Array)[], params: SeriesParameters | { seasons?: any; meta?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>>;
-      updateVideo: AugmentedSubmittable<(owner: ChannelOwner | { Nobody: any } | { Member: any } | { Curators: any } | { Dao: any } | string | Uint8Array, video: VideoId | AnyNumber | Uint8Array, assets: Vec<NewAsset> | (NewAsset | { Upload: any } | { Uri: any } | string | Uint8Array)[], params: VideoUpdateParameters | { new_in_category?: any; new_meta?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>>;
+      updatePerson: AugmentedSubmittable<(actor: PersonActor | { Member: any } | { Curator: any } | string | Uint8Array, person: PersonId | AnyNumber | Uint8Array, params: PersonUpdateParameters | { assets?: any; meta?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>>;
+      updatePlaylist: AugmentedSubmittable<(owner: ChannelOwner | { Member: any } | { Curators: any } | { Dao: any } | string | Uint8Array, playlist: PlaylistId | AnyNumber | Uint8Array, params: PlaylistUpdateParameters | { new_meta?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>>;
+      updateSeries: AugmentedSubmittable<(owner: ChannelOwner | { Member: any } | { Curators: any } | { Dao: any } | string | Uint8Array, channelId: ChannelId | AnyNumber | Uint8Array, params: SeriesParameters | { assets?: any; seasons?: any; meta?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>>;
+      updateVideo: AugmentedSubmittable<(owner: ChannelOwner | { Member: any } | { Curators: any } | { Dao: any } | string | Uint8Array, video: VideoId | AnyNumber | Uint8Array, params: VideoUpdateParameters | { assets?: any; new_meta?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>>;
       updateVideoCategory: AugmentedSubmittable<(curator: CuratorId | AnyNumber | Uint8Array, category: VideoCategoryId | AnyNumber | Uint8Array, params: VideoCategoryUpdateParameters | { new_meta?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>>;
     };
     contentDirectoryWorkingGroup: {
@@ -288,15 +288,10 @@ declare module '@polkadot/api/types/submittable' {
        **/
       acceptContent: AugmentedSubmittable<(storageProviderId: StorageProviderId | AnyNumber | Uint8Array, contentId: ContentId | string | Uint8Array) => SubmittableExtrinsic<ApiType>>;
       /**
-       * Adds the content to the system. Requires root privileges. The created DataObject
+       * Adds the content to the system. The created DataObject
        * awaits liaison to accept or reject it.
        **/
-      addContent: AugmentedSubmittable<(abstractOwner: AbstractStorageObjectOwner | { Channel: any } | { DAO: any } | { Council: any } | { WorkingGroup: any } | string | Uint8Array, content: Vec<ContentParameters> | (ContentParameters | { content_id?: any; type_id?: any; ipfs_content_id?: any } | string | Uint8Array)[]) => SubmittableExtrinsic<ApiType>>;
-      /**
-       * Adds the content to the system. Member id should match its origin. The created DataObject
-       * awaits liaison to accept or reject it.
-       **/
-      addContentAsMember: AugmentedSubmittable<(memberId: MemberId | AnyNumber | Uint8Array, content: Vec<ContentParameters> | (ContentParameters | { content_id?: any; type_id?: any; ipfs_content_id?: any } | string | Uint8Array)[]) => SubmittableExtrinsic<ApiType>>;
+      addContent: AugmentedSubmittable<(owner: StorageObjectOwner | { Member: any } | { Channel: any } | { DAO: any } | { Council: any } | { WorkingGroup: any } | string | Uint8Array, content: Vec<ContentParameters> | (ContentParameters | { content_id?: any; type_id?: any; ipfs_content_id?: any } | string | Uint8Array)[]) => SubmittableExtrinsic<ApiType>>;
       /**
        * Injects a set of data objects and their corresponding content id into the directory.
        * The operation is "silent" - no events will be emitted as objects are added.
@@ -314,6 +309,18 @@ declare module '@polkadot/api/types/submittable' {
        * Removes the content id from the list of known content ids. Requires root privileges.
        **/
       removeKnownContentId: AugmentedSubmittable<(contentId: ContentId | string | Uint8Array) => SubmittableExtrinsic<ApiType>>;
+      /**
+       * Locks / unlocks content uploading
+       **/
+      updateContentUploadingStatus: AugmentedSubmittable<(isBlocked: bool | boolean | Uint8Array) => SubmittableExtrinsic<ApiType>>;
+      /**
+       * Updates storage object owner quota objects limit. Requires leader privileges.
+       **/
+      updateStorageObjectOwnerQuotaObjectsLimit: AugmentedSubmittable<(abstractOwner: StorageObjectOwner | { Member: any } | { Channel: any } | { DAO: any } | { Council: any } | { WorkingGroup: any } | string | Uint8Array, newQuotaObjectsLimit: u64 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>>;
+      /**
+       * Updates storage object owner quota size limit. Requires leader privileges.
+       **/
+      updateStorageObjectOwnerQuotaSizeLimit: AugmentedSubmittable<(abstractOwner: StorageObjectOwner | { Member: any } | { Channel: any } | { DAO: any } | { Council: any } | { WorkingGroup: any } | string | Uint8Array, newQuotaSizeLimit: u64 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>>;
     };
     dataObjectStorageRegistry: {
       /**

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
types/augment-codec/augment-types.ts


+ 34 - 45
types/augment/all/defs.json

@@ -99,6 +99,7 @@
     "LookupSource": "AccountId",
     "ChannelId": "u64",
     "DAOId": "u64",
+    "Url": "Text",
     "EntryMethod": {
         "_enum": {
             "Paid": "u64",
@@ -476,7 +477,6 @@
         "termination_unstaking_period": "Option<u32>",
         "exit_unstaking_period": "Option<u32>"
     },
-    "Url": "Text",
     "IPNSIdentity": "Text",
     "ServiceProviderRecord": {
         "identity": "IPNSIdentity",
@@ -520,25 +520,21 @@
     "StorageObjectOwner": {
         "_enum": {
             "Member": "MemberId",
-            "AbstractStorageObjectOwner": "AbstractStorageObjectOwner"
-        }
-    },
-    "AbstractStorageObjectOwner": {
-        "_enum": {
             "Channel": "ChannelId",
             "DAO": "DAOId",
             "Council": "Null",
-            "WorkingGroup": "WorkingGroupType"
+            "WorkingGroup": "WorkingGroup"
         }
     },
-    "WorkingGroupType": {
-        "_enum": [
-            "ContentDirectory",
-            "Builders",
-            "StorageProviders"
-        ]
-    },
     "Content": "Vec<ContentParameters>",
+    "Quota": {
+        "size_limit": "u64",
+        "objects_limit": "u64",
+        "size_used": "u64",
+        "objects_used": "u64"
+    },
+    "QuotaLimit": "u64",
+    "UploadingStatus": "bool",
     "ProposalId": "u32",
     "ProposalStatus": {
         "_enum": {
@@ -703,30 +699,26 @@
     "NewAsset": {
         "_enum": {
             "Upload": "ContentParameters",
-            "Uri": "Text"
+            "Urls": "Vec<Url>"
         }
     },
     "Channel": {
         "owner": "ChannelOwner",
-        "in_category": "ChannelCategoryId",
-        "number_of_videos": "u32",
-        "number_of_playlists": "u32",
-        "number_of_series": "u32",
-        "is_curated": "bool",
-        "revenue": "u128"
+        "videos": "Vec<VideoId>",
+        "playlists": "Vec<PlaylistId>",
+        "series": "Vec<SeriesId>",
+        "is_censored": "bool",
+        "reward_account": "Option<GenericAccountId>"
     },
     "ChannelOwner": {
         "_enum": {
-            "Nobody": "Null",
             "Member": "MemberId",
             "Curators": "CuratorGroupId",
             "Dao": "DAOId"
         }
     },
     "ChannelCategoryId": "u64",
-    "ChannelCategory": {
-        "number_of_channels_in": "u32"
-    },
+    "ChannelCategory": {},
     "ChannelCategoryCreationParameters": {
         "meta": "Bytes"
     },
@@ -734,31 +726,29 @@
         "new_meta": "Bytes"
     },
     "ChannelCreationParameters": {
-        "in_category": "ChannelCategoryId",
+        "assets": "Vec<NewAsset>",
         "meta": "Bytes"
     },
     "ChannelUpdateParameters": {
-        "new_in_category": "Option<ChannelCategoryId>",
+        "assets": "Option<Vec<NewAsset>>",
         "new_meta": "Bytes"
     },
     "ChannelOwnershipTransferRequestId": "u64",
     "ChannelOwnershipTransferRequest": {
         "channel_id": "ChannelId",
         "new_owner": "ChannelOwner",
-        "payment": "u128"
+        "payment": "u128",
+        "new_reward_account": "Option<GenericAccountId>"
     },
     "Video": {
         "in_channel": "ChannelId",
-        "in_series": "Vec<SeriesId>",
-        "in_playlists": "Vec<PlaylistId>",
-        "is_curated": "bool",
+        "in_series": "Option<SeriesId>",
+        "is_censored": "bool",
         "is_featured": "bool"
     },
     "VideoId": "u64",
     "VideoCategoryId": "u64",
-    "VideoCategory": {
-        "number_of_videos_in_category": "u32"
-    },
+    "VideoCategory": {},
     "VideoCategoryCreationParameters": {
         "meta": "Bytes"
     },
@@ -766,21 +756,19 @@
         "new_meta": "Bytes"
     },
     "VideoCreationParameters": {
-        "in_category": "VideoCategoryId",
+        "assets": "Vec<NewAsset>",
         "meta": "Bytes"
     },
     "VideoUpdateParameters": {
-        "new_in_category": "Option<VideoCategoryId>",
+        "assets": "Option<Vec<NewAsset>>",
         "new_meta": "Option<Bytes>"
     },
     "Person": {
-        "controlled_by": "PersonController",
-        "number_of_videos_person_involed_in": "u32"
+        "controlled_by": "PersonController"
     },
     "PersonId": "u64",
     "PersonController": {
         "_enum": {
-            "Nobody": "Null",
             "Member": "MemberId",
             "Curators": "Null"
         }
@@ -792,23 +780,22 @@
         }
     },
     "PersonCreationParameters": {
+        "assets": "Vec<NewAsset>",
         "meta": "Bytes"
     },
     "PersonUpdateParameters": {
-        "meta": "Bytes"
+        "assets": "Option<Vec<NewAsset>>",
+        "meta": "Option<Bytes>"
     },
     "Playlist": {
-        "in_channel": "ChannelId",
-        "videos": "Vec<VideoId>"
+        "in_channel": "ChannelId"
     },
     "PlaylistId": "u64",
     "PlaylistCreationParameters": {
-        "videos": "Vec<VideoId>",
         "meta": "Bytes"
     },
     "PlaylistUpdateParameters": {
-        "new_videos": "Option<Vec<VideoId>>",
-        "new_meta": "Option<Bytes>"
+        "new_meta": "Bytes"
     },
     "SeriesId": "u64",
     "Series": {
@@ -819,10 +806,12 @@
         "episodes": "Vec<VideoId>"
     },
     "SeriesParameters": {
+        "assets": "Option<Vec<NewAsset>>",
         "seasons": "Option<Vec<Option<SeasonParameters>>>",
         "meta": "Option<Bytes>"
     },
     "SeasonParameters": {
+        "assets": "Option<Vec<NewAsset>>",
         "episodes": "Option<Vec<Option<EpisodeParemters>>>",
         "meta": "Option<Bytes>"
     },

+ 43 - 49
types/augment/all/types.ts

@@ -7,17 +7,6 @@ import { GenericAccountId } from '@polkadot/types/generic';
 import { Bytes, Null, Text, bool, u128, u16, u32, u64 } from '@polkadot/types/primitive';
 import { AccountId, Balance, Hash } from '@polkadot/types/interfaces/runtime';
 
-/** @name AbstractStorageObjectOwner */
-export interface AbstractStorageObjectOwner extends Enum {
-  readonly isChannel: boolean;
-  readonly asChannel: ChannelId;
-  readonly isDao: boolean;
-  readonly asDao: DAOId;
-  readonly isCouncil: boolean;
-  readonly isWorkingGroup: boolean;
-  readonly asWorkingGroup: WorkingGroupType;
-}
-
 /** @name AcceptingApplications */
 export interface AcceptingApplications extends Struct {
   readonly started_accepting_applicants_at_block: u32;
@@ -191,18 +180,15 @@ export interface CategoryId extends u64 {}
 /** @name Channel */
 export interface Channel extends Struct {
   readonly owner: ChannelOwner;
-  readonly in_category: ChannelCategoryId;
-  readonly number_of_videos: u32;
-  readonly number_of_playlists: u32;
-  readonly number_of_series: u32;
-  readonly is_curated: bool;
-  readonly revenue: u128;
+  readonly videos: Vec<VideoId>;
+  readonly playlists: Vec<PlaylistId>;
+  readonly series: Vec<SeriesId>;
+  readonly is_censored: bool;
+  readonly reward_account: Option<GenericAccountId>;
 }
 
 /** @name ChannelCategory */
-export interface ChannelCategory extends Struct {
-  readonly number_of_channels_in: u32;
-}
+export interface ChannelCategory extends Struct {}
 
 /** @name ChannelCategoryCreationParameters */
 export interface ChannelCategoryCreationParameters extends Struct {
@@ -222,7 +208,7 @@ export interface ChannelContentType extends Null {}
 
 /** @name ChannelCreationParameters */
 export interface ChannelCreationParameters extends Struct {
-  readonly in_category: ChannelCategoryId;
+  readonly assets: Vec<NewAsset>;
   readonly meta: Bytes;
 }
 
@@ -234,7 +220,6 @@ export interface ChannelId extends u64 {}
 
 /** @name ChannelOwner */
 export interface ChannelOwner extends Enum {
-  readonly isNobody: boolean;
   readonly isMember: boolean;
   readonly asMember: MemberId;
   readonly isCurators: boolean;
@@ -248,6 +233,7 @@ export interface ChannelOwnershipTransferRequest extends Struct {
   readonly channel_id: ChannelId;
   readonly new_owner: ChannelOwner;
   readonly payment: u128;
+  readonly new_reward_account: Option<GenericAccountId>;
 }
 
 /** @name ChannelOwnershipTransferRequestId */
@@ -258,7 +244,7 @@ export interface ChannelPublicationStatus extends Null {}
 
 /** @name ChannelUpdateParameters */
 export interface ChannelUpdateParameters extends Struct {
-  readonly new_in_category: Option<ChannelCategoryId>;
+  readonly assets: Option<Vec<NewAsset>>;
   readonly new_meta: Bytes;
 }
 
@@ -602,8 +588,8 @@ export interface ModerationAction extends Struct {
 export interface NewAsset extends Enum {
   readonly isUpload: boolean;
   readonly asUpload: ContentParameters;
-  readonly isUri: boolean;
-  readonly asUri: Text;
+  readonly isUrls: boolean;
+  readonly asUrls: Vec<Url>;
 }
 
 /** @name NextAdjustment */
@@ -706,7 +692,6 @@ export interface ParametrizedPropertyValue extends Null {}
 /** @name Person */
 export interface Person extends Struct {
   readonly controlled_by: PersonController;
-  readonly number_of_videos_person_involed_in: u32;
 }
 
 /** @name PersonActor */
@@ -719,7 +704,6 @@ export interface PersonActor extends Enum {
 
 /** @name PersonController */
 export interface PersonController extends Enum {
-  readonly isNobody: boolean;
   readonly isMember: boolean;
   readonly asMember: MemberId;
   readonly isCurators: boolean;
@@ -727,6 +711,7 @@ export interface PersonController extends Enum {
 
 /** @name PersonCreationParameters */
 export interface PersonCreationParameters extends Struct {
+  readonly assets: Vec<NewAsset>;
   readonly meta: Bytes;
 }
 
@@ -735,18 +720,17 @@ export interface PersonId extends u64 {}
 
 /** @name PersonUpdateParameters */
 export interface PersonUpdateParameters extends Struct {
-  readonly meta: Bytes;
+  readonly assets: Option<Vec<NewAsset>>;
+  readonly meta: Option<Bytes>;
 }
 
 /** @name Playlist */
 export interface Playlist extends Struct {
   readonly in_channel: ChannelId;
-  readonly videos: Vec<VideoId>;
 }
 
 /** @name PlaylistCreationParameters */
 export interface PlaylistCreationParameters extends Struct {
-  readonly videos: Vec<VideoId>;
   readonly meta: Bytes;
 }
 
@@ -755,8 +739,7 @@ export interface PlaylistId extends u64 {}
 
 /** @name PlaylistUpdateParameters */
 export interface PlaylistUpdateParameters extends Struct {
-  readonly new_videos: Option<Vec<VideoId>>;
-  readonly new_meta: Option<Bytes>;
+  readonly new_meta: Bytes;
 }
 
 /** @name Post */
@@ -924,6 +907,17 @@ export interface ProposalStatus extends Enum {
   readonly asFinalized: Finalized;
 }
 
+/** @name Quota */
+export interface Quota extends Struct {
+  readonly size_limit: u64;
+  readonly objects_limit: u64;
+  readonly size_used: u64;
+  readonly objects_used: u64;
+}
+
+/** @name QuotaLimit */
+export interface QuotaLimit extends u64 {}
+
 /** @name RationaleText */
 export interface RationaleText extends Bytes {}
 
@@ -1026,6 +1020,7 @@ export interface Season extends Struct {
 
 /** @name SeasonParameters */
 export interface SeasonParameters extends Struct {
+  readonly assets: Option<Vec<NewAsset>>;
   readonly episodes: Option<Vec<Option<EpisodeParemters>>>;
   readonly meta: Option<Bytes>;
 }
@@ -1051,6 +1046,7 @@ export interface SeriesId extends u64 {}
 
 /** @name SeriesParameters */
 export interface SeriesParameters extends Struct {
+  readonly assets: Option<Vec<NewAsset>>;
   readonly seasons: Option<Vec<Option<SeasonParameters>>>;
   readonly meta: Option<Bytes>;
 }
@@ -1143,8 +1139,13 @@ export interface Status extends Null {}
 export interface StorageObjectOwner extends Enum {
   readonly isMember: boolean;
   readonly asMember: MemberId;
-  readonly isAbstractStorageObjectOwner: boolean;
-  readonly asAbstractStorageObjectOwner: AbstractStorageObjectOwner;
+  readonly isChannel: boolean;
+  readonly asChannel: ChannelId;
+  readonly isDao: boolean;
+  readonly asDao: DAOId;
+  readonly isCouncil: boolean;
+  readonly isWorkingGroup: boolean;
+  readonly asWorkingGroup: WorkingGroup;
 }
 
 /** @name StorageProviderId */
@@ -1214,6 +1215,9 @@ export interface UnstakingApplicationStage extends Struct {
 /** @name UpdatePropertyValuesOperation */
 export interface UpdatePropertyValuesOperation extends Null {}
 
+/** @name UploadingStatus */
+export interface UploadingStatus extends bool {}
+
 /** @name Url */
 export interface Url extends Text {}
 
@@ -1232,16 +1236,13 @@ export interface VecStoredValue extends Null {}
 /** @name Video */
 export interface Video extends Struct {
   readonly in_channel: ChannelId;
-  readonly in_series: Vec<SeriesId>;
-  readonly in_playlists: Vec<PlaylistId>;
-  readonly is_curated: bool;
+  readonly in_series: Option<SeriesId>;
+  readonly is_censored: bool;
   readonly is_featured: bool;
 }
 
 /** @name VideoCategory */
-export interface VideoCategory extends Struct {
-  readonly number_of_videos_in_category: u32;
-}
+export interface VideoCategory extends Struct {}
 
 /** @name VideoCategoryCreationParameters */
 export interface VideoCategoryCreationParameters extends Struct {
@@ -1258,7 +1259,7 @@ export interface VideoCategoryUpdateParameters extends Struct {
 
 /** @name VideoCreationParameters */
 export interface VideoCreationParameters extends Struct {
-  readonly in_category: VideoCategoryId;
+  readonly assets: Vec<NewAsset>;
   readonly meta: Bytes;
 }
 
@@ -1267,7 +1268,7 @@ export interface VideoId extends u64 {}
 
 /** @name VideoUpdateParameters */
 export interface VideoUpdateParameters extends Struct {
-  readonly new_in_category: Option<VideoCategoryId>;
+  readonly assets: Option<Vec<NewAsset>>;
   readonly new_meta: Option<Bytes>;
 }
 
@@ -1309,13 +1310,6 @@ export interface WorkingGroup extends Enum {
   readonly isContent: boolean;
 }
 
-/** @name WorkingGroupType */
-export interface WorkingGroupType extends Enum {
-  readonly isContentDirectory: boolean;
-  readonly isBuilders: boolean;
-  readonly isStorageProviders: boolean;
-}
-
 /** @name WorkingGroupUnstaker */
 export interface WorkingGroupUnstaker extends Null {}
 

+ 21 - 1
types/augment/augment-api-query.ts

@@ -4,7 +4,7 @@
 import { AnyNumber, ITuple, Observable } from '@polkadot/types/types';
 import { Option, Vec } from '@polkadot/types/codec';
 import { Bytes, bool, u32, u64 } from '@polkadot/types/primitive';
-import { Application, ApplicationId, ApplicationOf, Category, CategoryId, Channel, ChannelCategory, ChannelCategoryId, ChannelId, ChannelOwnershipTransferRequest, ChannelOwnershipTransferRequestId, ContentId, CuratorGroup, CuratorGroupId, DataObject, DataObjectStorageRelationship, DataObjectStorageRelationshipId, DataObjectType, DataObjectTypeId, DiscussionPost, DiscussionThread, ElectionStage, ElectionStake, HiringApplicationId, InputValidationLengthConstraint, MemberId, Membership, MemoText, Mint, MintId, Opening, OpeningId, OpeningOf, PaidMembershipTerms, PaidTermId, Person, PersonId, Playlist, PlaylistId, Post, PostId, ProposalDetailsOf, ProposalId, ProposalOf, Recipient, RecipientId, RewardRelationship, RewardRelationshipId, SealedVote, Seats, Series, SeriesId, ServiceProviderRecord, Stake, StakeId, StorageProviderId, Thread, ThreadCounter, ThreadId, TransferableStake, Url, Video, VideoCategory, VideoCategoryId, VideoId, VoteKind, WorkerId, WorkerOf } from './all';
+import { Application, ApplicationId, ApplicationOf, Category, CategoryId, Channel, ChannelCategory, ChannelCategoryId, ChannelId, ChannelOwnershipTransferRequest, ChannelOwnershipTransferRequestId, ContentId, CuratorGroup, CuratorGroupId, DataObject, DataObjectStorageRelationship, DataObjectStorageRelationshipId, DataObjectType, DataObjectTypeId, DiscussionPost, DiscussionThread, ElectionStage, ElectionStake, HiringApplicationId, InputValidationLengthConstraint, MemberId, Membership, MemoText, Mint, MintId, Opening, OpeningId, OpeningOf, PaidMembershipTerms, PaidTermId, Person, PersonId, Playlist, PlaylistId, Post, PostId, ProposalDetailsOf, ProposalId, ProposalOf, Quota, Recipient, RecipientId, RewardRelationship, RewardRelationshipId, SealedVote, Seats, Series, SeriesId, ServiceProviderRecord, Stake, StakeId, StorageObjectOwner, StorageProviderId, Thread, ThreadCounter, ThreadId, TransferableStake, Url, Video, VideoCategory, VideoCategoryId, VideoId, VoteKind, WorkerId, WorkerOf } from './all';
 import { UncleEntryItem } from '@polkadot/types/interfaces/authorship';
 import { BabeAuthorityWeight, MaybeRandomness, NextConfigDescriptor, Randomness } from '@polkadot/types/interfaces/babe';
 import { AccountData, BalanceLock } from '@polkadot/types/interfaces/balances';
@@ -255,10 +255,30 @@ declare module '@polkadot/api/types/storage' {
        * Maps data objects by their content id.
        **/
       dataObjectByContentId: AugmentedQuery<ApiType, (arg: ContentId | string | Uint8Array) => Observable<Option<DataObject>>>;
+      /**
+       * Global quota.
+       **/
+      globalQuota: AugmentedQuery<ApiType, () => Observable<Quota>>;
       /**
        * List of ids known to the system.
        **/
       knownContentIds: AugmentedQuery<ApiType, () => Observable<Vec<ContentId>>>;
+      /**
+       * Upper bound for the Quota objects number limit.
+       **/
+      quotaObjectsLimitUpperBound: AugmentedQuery<ApiType, () => Observable<u64>>;
+      /**
+       * Maps storage owner to it`s quota. Created when the first upload by the new actor occured.
+       **/
+      quotas: AugmentedQuery<ApiType, (arg: StorageObjectOwner | { Member: any } | { Channel: any } | { DAO: any } | { Council: any } | { WorkingGroup: any } | string | Uint8Array) => Observable<Quota>>;
+      /**
+       * Upper bound for the Quota size limit.
+       **/
+      quotaSizeLimitUpperBound: AugmentedQuery<ApiType, () => Observable<u64>>;
+      /**
+       * If all new uploads blocked
+       **/
+      uploadingBlocked: AugmentedQuery<ApiType, () => Observable<bool>>;
     };
     dataObjectStorageRegistry: {
       /**

+ 37 - 30
types/augment/augment-api-tx.ts

@@ -4,7 +4,7 @@
 import { AnyNumber } from '@polkadot/types/types';
 import { Compact, Option, Vec } from '@polkadot/types/codec';
 import { Bytes, bool, u16, u32, u64 } from '@polkadot/types/primitive';
-import { AbstractStorageObjectOwner, ActivateOpeningAt, AddOpeningParameters, ApplicationId, ApplicationIdSet, BalanceOfMint, CategoryId, ChannelCategoryCreationParameters, ChannelCategoryId, ChannelCategoryUpdateParameters, ChannelCreationParameters, ChannelId, ChannelOwner, ChannelOwnershipTransferRequestId, ChannelUpdateParameters, ContentId, ContentParameters, CuratorGroupId, CuratorId, DataObjectStorageRelationshipId, DataObjectType, DataObjectTypeId, DataObjectsMap, ElectionParameters, FillOpeningParameters, MemberId, MemoText, NewAsset, OpeningId, OpeningPolicyCommitment, OpeningType, PaidTermId, PersonActor, PersonCreationParameters, PersonId, PersonUpdateParameters, PlaylistCreationParameters, PlaylistId, PlaylistUpdateParameters, PostId, ProposalId, RewardPolicy, SeriesId, SeriesParameters, StorageProviderId, TerminateRoleParameters, ThreadId, Url, VideoCategoryCreationParameters, VideoCategoryId, VideoCategoryUpdateParameters, VideoCreationParameters, VideoId, VideoUpdateParameters, VoteKind, WorkerId, WorkingGroup } from './all';
+import { ActivateOpeningAt, AddOpeningParameters, ApplicationId, ApplicationIdSet, BalanceOfMint, CategoryId, ChannelCategoryCreationParameters, ChannelCategoryId, ChannelCategoryUpdateParameters, ChannelCreationParameters, ChannelId, ChannelOwner, ChannelOwnershipTransferRequestId, ChannelUpdateParameters, ContentId, ContentParameters, CuratorGroupId, CuratorId, DataObjectStorageRelationshipId, DataObjectType, DataObjectTypeId, DataObjectsMap, ElectionParameters, FillOpeningParameters, MemberId, MemoText, OpeningId, OpeningPolicyCommitment, OpeningType, PaidTermId, PersonActor, PersonCreationParameters, PersonId, PersonUpdateParameters, PlaylistCreationParameters, PlaylistId, PlaylistUpdateParameters, PostId, ProposalId, RewardPolicy, SeriesId, SeriesParameters, StorageObjectOwner, StorageProviderId, TerminateRoleParameters, ThreadId, Url, VideoCategoryCreationParameters, VideoCategoryId, VideoCategoryUpdateParameters, VideoCreationParameters, VideoId, VideoUpdateParameters, VoteKind, WorkerId, WorkingGroup } from './all';
 import { Extrinsic, Signature } from '@polkadot/types/interfaces/extrinsics';
 import { GrandpaEquivocationProof, KeyOwnerProof } from '@polkadot/types/interfaces/grandpa';
 import { Heartbeat } from '@polkadot/types/interfaces/imOnline';
@@ -99,56 +99,56 @@ declare module '@polkadot/api/types/submittable' {
       transferKeepAlive: AugmentedSubmittable<(dest: LookupSource | string | Uint8Array, value: Compact<Balance> | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>>;
     };
     content: {
-      acceptChannelTransfer: AugmentedSubmittable<(owner: ChannelOwner | { Nobody: any } | { Member: any } | { Curators: any } | { Dao: any } | string | Uint8Array, requestId: ChannelOwnershipTransferRequestId | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>>;
+      acceptChannelTransfer: AugmentedSubmittable<(owner: ChannelOwner | { Member: any } | { Curators: any } | { Dao: any } | string | Uint8Array, requestId: ChannelOwnershipTransferRequestId | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>>;
       /**
        * Add curator to curator group under given `curator_group_id`
        **/
       addCuratorToGroup: AugmentedSubmittable<(curatorGroupId: CuratorGroupId | AnyNumber | Uint8Array, curatorId: CuratorId | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>>;
-      addPersonToVideo: AugmentedSubmittable<(owner: ChannelOwner | { Nobody: any } | { Member: any } | { Curators: any } | { Dao: any } | string | Uint8Array, videoId: VideoId | AnyNumber | Uint8Array, person: PersonId | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>>;
+      addPersonToVideo: AugmentedSubmittable<(owner: ChannelOwner | { Member: any } | { Curators: any } | { Dao: any } | string | Uint8Array, videoId: VideoId | AnyNumber | Uint8Array, person: PersonId | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>>;
       cancelChannelTransferRequest: AugmentedSubmittable<(requestId: ChannelOwnershipTransferRequestId | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>>;
-      createChannel: AugmentedSubmittable<(owner: ChannelOwner | { Nobody: any } | { Member: any } | { Curators: any } | { Dao: any } | string | Uint8Array, assets: Vec<NewAsset> | (NewAsset | { Upload: any } | { Uri: any } | string | Uint8Array)[], params: ChannelCreationParameters | { in_category?: any; meta?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>>;
+      censorChannel: AugmentedSubmittable<(curatorId: CuratorId | AnyNumber | Uint8Array, channelId: ChannelId | AnyNumber | Uint8Array, rationale: Bytes | string | Uint8Array) => SubmittableExtrinsic<ApiType>>;
+      censorVideo: AugmentedSubmittable<(curatorId: CuratorId | AnyNumber | Uint8Array, videoId: VideoId | AnyNumber | Uint8Array, rationale: Bytes | string | Uint8Array) => SubmittableExtrinsic<ApiType>>;
+      createChannel: AugmentedSubmittable<(owner: ChannelOwner | { Member: any } | { Curators: any } | { Dao: any } | string | Uint8Array, params: ChannelCreationParameters | { assets?: any; meta?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>>;
       createChannelCategory: AugmentedSubmittable<(curator: CuratorId | AnyNumber | Uint8Array, params: ChannelCategoryCreationParameters | { meta?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>>;
       /**
        * Add new curator group to runtime storage
        **/
       createCuratorGroup: AugmentedSubmittable<() => SubmittableExtrinsic<ApiType>>;
-      createPerson: AugmentedSubmittable<(actor: PersonActor | { Member: any } | { Curator: any } | string | Uint8Array, assets: Vec<NewAsset> | (NewAsset | { Upload: any } | { Uri: any } | string | Uint8Array)[], params: PersonCreationParameters | { meta?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>>;
-      createPlaylist: AugmentedSubmittable<(owner: ChannelOwner | { Nobody: any } | { Member: any } | { Curators: any } | { Dao: any } | string | Uint8Array, channelId: ChannelId | AnyNumber | Uint8Array, assets: Vec<NewAsset> | (NewAsset | { Upload: any } | { Uri: any } | string | Uint8Array)[], params: PlaylistCreationParameters | { videos?: any; meta?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>>;
-      createSeries: AugmentedSubmittable<(owner: ChannelOwner | { Nobody: any } | { Member: any } | { Curators: any } | { Dao: any } | string | Uint8Array, channelId: ChannelId | AnyNumber | Uint8Array, assets: Vec<NewAsset> | (NewAsset | { Upload: any } | { Uri: any } | string | Uint8Array)[], params: SeriesParameters | { seasons?: any; meta?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>>;
-      createVideo: AugmentedSubmittable<(owner: ChannelOwner | { Nobody: any } | { Member: any } | { Curators: any } | { Dao: any } | string | Uint8Array, channelId: ChannelId | AnyNumber | Uint8Array, assets: Vec<NewAsset> | (NewAsset | { Upload: any } | { Uri: any } | string | Uint8Array)[], params: VideoCreationParameters | { in_category?: any; meta?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>>;
+      createPerson: AugmentedSubmittable<(actor: PersonActor | { Member: any } | { Curator: any } | string | Uint8Array, params: PersonCreationParameters | { assets?: any; meta?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>>;
+      createPlaylist: AugmentedSubmittable<(owner: ChannelOwner | { Member: any } | { Curators: any } | { Dao: any } | string | Uint8Array, channelId: ChannelId | AnyNumber | Uint8Array, params: PlaylistCreationParameters | { meta?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>>;
+      createSeries: AugmentedSubmittable<(owner: ChannelOwner | { Member: any } | { Curators: any } | { Dao: any } | string | Uint8Array, channelId: ChannelId | AnyNumber | Uint8Array, params: SeriesParameters | { assets?: any; seasons?: any; meta?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>>;
+      createVideo: AugmentedSubmittable<(owner: ChannelOwner | { Member: any } | { Curators: any } | { Dao: any } | string | Uint8Array, channelId: ChannelId | AnyNumber | Uint8Array, params: VideoCreationParameters | { assets?: any; meta?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>>;
       createVideoCategory: AugmentedSubmittable<(curator: CuratorId | AnyNumber | Uint8Array, params: VideoCategoryCreationParameters | { meta?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>>;
-      curateChannel: AugmentedSubmittable<(curatorId: CuratorId | AnyNumber | Uint8Array, channelId: ChannelId | AnyNumber | Uint8Array, rationale: Bytes | string | Uint8Array) => SubmittableExtrinsic<ApiType>>;
-      curateVideo: AugmentedSubmittable<(curatorId: CuratorId | AnyNumber | Uint8Array, videoId: VideoId | AnyNumber | Uint8Array, rationale: Bytes | string | Uint8Array) => SubmittableExtrinsic<ApiType>>;
-      deleteChannel: AugmentedSubmittable<(owner: ChannelOwner | { Nobody: any } | { Member: any } | { Curators: any } | { Dao: any } | string | Uint8Array, channelId: ChannelId | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>>;
+      deleteChannel: AugmentedSubmittable<(owner: ChannelOwner | { Member: any } | { Curators: any } | { Dao: any } | string | Uint8Array, channelId: ChannelId | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>>;
       deleteChannelCategory: AugmentedSubmittable<(curator: CuratorId | AnyNumber | Uint8Array, category: ChannelCategoryId | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>>;
       /**
        * Remove curator group under given `curator_group_id` from runtime storage
        **/
       deleteCuratorGroup: AugmentedSubmittable<(curatorGroupId: CuratorGroupId | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>>;
       deletePerson: AugmentedSubmittable<(actor: PersonActor | { Member: any } | { Curator: any } | string | Uint8Array, person: PersonId | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>>;
-      deletePlaylist: AugmentedSubmittable<(owner: ChannelOwner | { Nobody: any } | { Member: any } | { Curators: any } | { Dao: any } | string | Uint8Array, channelId: ChannelId | AnyNumber | Uint8Array, playlist: PlaylistId | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>>;
-      deleteSeries: AugmentedSubmittable<(owner: ChannelOwner | { Nobody: any } | { Member: any } | { Curators: any } | { Dao: any } | string | Uint8Array, series: SeriesId | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>>;
-      deleteVideo: AugmentedSubmittable<(owner: ChannelOwner | { Nobody: any } | { Member: any } | { Curators: any } | { Dao: any } | string | Uint8Array, video: VideoId | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>>;
+      deletePlaylist: AugmentedSubmittable<(owner: ChannelOwner | { Member: any } | { Curators: any } | { Dao: any } | string | Uint8Array, channelId: ChannelId | AnyNumber | Uint8Array, playlist: PlaylistId | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>>;
+      deleteSeries: AugmentedSubmittable<(owner: ChannelOwner | { Member: any } | { Curators: any } | { Dao: any } | string | Uint8Array, series: SeriesId | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>>;
+      deleteVideo: AugmentedSubmittable<(owner: ChannelOwner | { Member: any } | { Curators: any } | { Dao: any } | string | Uint8Array, video: VideoId | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>>;
       deleteVideoCategory: AugmentedSubmittable<(curator: CuratorId | AnyNumber | Uint8Array, category: VideoCategoryId | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>>;
       /**
        * Remove curator from a given curator group
        **/
       removeCuratorFromGroup: AugmentedSubmittable<(curatorGroupId: CuratorGroupId | AnyNumber | Uint8Array, curatorId: CuratorId | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>>;
-      removePersonFromVideo: AugmentedSubmittable<(owner: ChannelOwner | { Nobody: any } | { Member: any } | { Curators: any } | { Dao: any } | string | Uint8Array, videoId: VideoId | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>>;
-      requestChannelTransfer: AugmentedSubmittable<(newOwner: ChannelOwner | { Nobody: any } | { Member: any } | { Curators: any } | { Dao: any } | string | Uint8Array, channelId: ChannelId | AnyNumber | Uint8Array, payment: BalanceOf | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>>;
+      removePersonFromVideo: AugmentedSubmittable<(owner: ChannelOwner | { Member: any } | { Curators: any } | { Dao: any } | string | Uint8Array, videoId: VideoId | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>>;
+      requestChannelTransfer: AugmentedSubmittable<(newOwner: ChannelOwner | { Member: any } | { Curators: any } | { Dao: any } | string | Uint8Array, channelId: ChannelId | AnyNumber | Uint8Array, payment: BalanceOf | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>>;
       /**
        * Set `is_active` status for curator group under given `curator_group_id`
        **/
       setCuratorGroupStatus: AugmentedSubmittable<(curatorGroupId: CuratorGroupId | AnyNumber | Uint8Array, isActive: bool | boolean | Uint8Array) => SubmittableExtrinsic<ApiType>>;
       setFeaturedVideos: AugmentedSubmittable<(list: Vec<VideoId> | (VideoId | AnyNumber | Uint8Array)[]) => SubmittableExtrinsic<ApiType>>;
-      uncurateChannel: AugmentedSubmittable<(curatorId: CuratorId | AnyNumber | Uint8Array, channelId: ChannelId | AnyNumber | Uint8Array, rationale: Bytes | string | Uint8Array) => SubmittableExtrinsic<ApiType>>;
-      uncurateVideo: AugmentedSubmittable<(curatorId: CuratorId | AnyNumber | Uint8Array, videoId: VideoId | AnyNumber | Uint8Array, rationale: Bytes | string | Uint8Array) => SubmittableExtrinsic<ApiType>>;
-      updateChannel: AugmentedSubmittable<(owner: ChannelOwner | { Nobody: any } | { Member: any } | { Curators: any } | { Dao: any } | string | Uint8Array, channelId: ChannelId | AnyNumber | Uint8Array, newAssets: Vec<NewAsset> | (NewAsset | { Upload: any } | { Uri: any } | string | Uint8Array)[], params: ChannelUpdateParameters | { new_in_category?: any; new_meta?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>>;
+      uncensorChannel: AugmentedSubmittable<(curatorId: CuratorId | AnyNumber | Uint8Array, channelId: ChannelId | AnyNumber | Uint8Array, rationale: Bytes | string | Uint8Array) => SubmittableExtrinsic<ApiType>>;
+      uncensorVideo: AugmentedSubmittable<(curatorId: CuratorId | AnyNumber | Uint8Array, videoId: VideoId | AnyNumber | Uint8Array, rationale: Bytes | string | Uint8Array) => SubmittableExtrinsic<ApiType>>;
+      updateChannel: AugmentedSubmittable<(owner: ChannelOwner | { Member: any } | { Curators: any } | { Dao: any } | string | Uint8Array, channelId: ChannelId | AnyNumber | Uint8Array, params: ChannelUpdateParameters | { assets?: any; new_meta?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>>;
       updateChannelCategory: AugmentedSubmittable<(curator: CuratorId | AnyNumber | Uint8Array, category: ChannelCategoryId | AnyNumber | Uint8Array, params: ChannelCategoryUpdateParameters | { new_meta?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>>;
-      updatePerson: AugmentedSubmittable<(actor: PersonActor | { Member: any } | { Curator: any } | string | Uint8Array, person: PersonId | AnyNumber | Uint8Array, assets: Vec<NewAsset> | (NewAsset | { Upload: any } | { Uri: any } | string | Uint8Array)[], params: PersonUpdateParameters | { meta?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>>;
-      updatePlaylist: AugmentedSubmittable<(owner: ChannelOwner | { Nobody: any } | { Member: any } | { Curators: any } | { Dao: any } | string | Uint8Array, playlist: PlaylistId | AnyNumber | Uint8Array, assets: Vec<NewAsset> | (NewAsset | { Upload: any } | { Uri: any } | string | Uint8Array)[], params: PlaylistUpdateParameters | { new_videos?: any; new_meta?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>>;
-      updateSeries: AugmentedSubmittable<(owner: ChannelOwner | { Nobody: any } | { Member: any } | { Curators: any } | { Dao: any } | string | Uint8Array, channelId: ChannelId | AnyNumber | Uint8Array, assets: Vec<NewAsset> | (NewAsset | { Upload: any } | { Uri: any } | string | Uint8Array)[], params: SeriesParameters | { seasons?: any; meta?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>>;
-      updateVideo: AugmentedSubmittable<(owner: ChannelOwner | { Nobody: any } | { Member: any } | { Curators: any } | { Dao: any } | string | Uint8Array, video: VideoId | AnyNumber | Uint8Array, assets: Vec<NewAsset> | (NewAsset | { Upload: any } | { Uri: any } | string | Uint8Array)[], params: VideoUpdateParameters | { new_in_category?: any; new_meta?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>>;
+      updatePerson: AugmentedSubmittable<(actor: PersonActor | { Member: any } | { Curator: any } | string | Uint8Array, person: PersonId | AnyNumber | Uint8Array, params: PersonUpdateParameters | { assets?: any; meta?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>>;
+      updatePlaylist: AugmentedSubmittable<(owner: ChannelOwner | { Member: any } | { Curators: any } | { Dao: any } | string | Uint8Array, playlist: PlaylistId | AnyNumber | Uint8Array, params: PlaylistUpdateParameters | { new_meta?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>>;
+      updateSeries: AugmentedSubmittable<(owner: ChannelOwner | { Member: any } | { Curators: any } | { Dao: any } | string | Uint8Array, channelId: ChannelId | AnyNumber | Uint8Array, params: SeriesParameters | { assets?: any; seasons?: any; meta?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>>;
+      updateVideo: AugmentedSubmittable<(owner: ChannelOwner | { Member: any } | { Curators: any } | { Dao: any } | string | Uint8Array, video: VideoId | AnyNumber | Uint8Array, params: VideoUpdateParameters | { assets?: any; new_meta?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>>;
       updateVideoCategory: AugmentedSubmittable<(curator: CuratorId | AnyNumber | Uint8Array, category: VideoCategoryId | AnyNumber | Uint8Array, params: VideoCategoryUpdateParameters | { new_meta?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>>;
     };
     contentDirectoryWorkingGroup: {
@@ -288,15 +288,10 @@ declare module '@polkadot/api/types/submittable' {
        **/
       acceptContent: AugmentedSubmittable<(storageProviderId: StorageProviderId | AnyNumber | Uint8Array, contentId: ContentId | string | Uint8Array) => SubmittableExtrinsic<ApiType>>;
       /**
-       * Adds the content to the system. Requires root privileges. The created DataObject
+       * Adds the content to the system. The created DataObject
        * awaits liaison to accept or reject it.
        **/
-      addContent: AugmentedSubmittable<(abstractOwner: AbstractStorageObjectOwner | { Channel: any } | { DAO: any } | { Council: any } | { WorkingGroup: any } | string | Uint8Array, content: Vec<ContentParameters> | (ContentParameters | { content_id?: any; type_id?: any; ipfs_content_id?: any } | string | Uint8Array)[]) => SubmittableExtrinsic<ApiType>>;
-      /**
-       * Adds the content to the system. Member id should match its origin. The created DataObject
-       * awaits liaison to accept or reject it.
-       **/
-      addContentAsMember: AugmentedSubmittable<(memberId: MemberId | AnyNumber | Uint8Array, content: Vec<ContentParameters> | (ContentParameters | { content_id?: any; type_id?: any; ipfs_content_id?: any } | string | Uint8Array)[]) => SubmittableExtrinsic<ApiType>>;
+      addContent: AugmentedSubmittable<(owner: StorageObjectOwner | { Member: any } | { Channel: any } | { DAO: any } | { Council: any } | { WorkingGroup: any } | string | Uint8Array, content: Vec<ContentParameters> | (ContentParameters | { content_id?: any; type_id?: any; ipfs_content_id?: any } | string | Uint8Array)[]) => SubmittableExtrinsic<ApiType>>;
       /**
        * Injects a set of data objects and their corresponding content id into the directory.
        * The operation is "silent" - no events will be emitted as objects are added.
@@ -314,6 +309,18 @@ declare module '@polkadot/api/types/submittable' {
        * Removes the content id from the list of known content ids. Requires root privileges.
        **/
       removeKnownContentId: AugmentedSubmittable<(contentId: ContentId | string | Uint8Array) => SubmittableExtrinsic<ApiType>>;
+      /**
+       * Locks / unlocks content uploading
+       **/
+      updateContentUploadingStatus: AugmentedSubmittable<(isBlocked: bool | boolean | Uint8Array) => SubmittableExtrinsic<ApiType>>;
+      /**
+       * Updates storage object owner quota objects limit. Requires leader privileges.
+       **/
+      updateStorageObjectOwnerQuotaObjectsLimit: AugmentedSubmittable<(abstractOwner: StorageObjectOwner | { Member: any } | { Channel: any } | { DAO: any } | { Council: any } | { WorkingGroup: any } | string | Uint8Array, newQuotaObjectsLimit: u64 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>>;
+      /**
+       * Updates storage object owner quota size limit. Requires leader privileges.
+       **/
+      updateStorageObjectOwnerQuotaSizeLimit: AugmentedSubmittable<(abstractOwner: StorageObjectOwner | { Member: any } | { Channel: any } | { DAO: any } | { Council: any } | { WorkingGroup: any } | string | Uint8Array, newQuotaSizeLimit: u64 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>>;
     };
     dataObjectStorageRegistry: {
       /**

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
types/augment/augment-types.ts


+ 3 - 0
types/src/common.ts

@@ -44,6 +44,8 @@ export function JoyBTreeSet<V extends UInt>(valType: Constructor<V>): Constructo
   }
 }
 
+export class Url extends Text {}
+
 export class ChannelId extends u64 {}
 export class DAOId extends u64 {}
 
@@ -151,6 +153,7 @@ export const commonTypes: RegistryTypes = {
   LookupSource,
   ChannelId,
   DAOId,
+  Url,
 }
 
 export default commonTypes

+ 28 - 30
types/src/content/index.ts

@@ -1,8 +1,9 @@
-import { Text, Vec, Option, Tuple } from '@polkadot/types'
+import { Vec, Option, Tuple } from '@polkadot/types'
 import { bool, u64, u32, u128, Null, Bytes } from '@polkadot/types/primitive'
 import { MemberId } from '../members'
-import { JoyStructDecorated, JoyEnum, ChannelId, JoyBTreeSet, DAOId } from '../common'
+import { JoyStructDecorated, JoyEnum, ChannelId, JoyBTreeSet, DAOId, Url } from '../common'
 import { ContentParameters } from '../storage'
+import AccountId from '@polkadot/types/generic/AccountId'
 
 export class CuratorId extends u64 {}
 export class CuratorGroupId extends u64 {}
@@ -15,6 +16,11 @@ export class SeriesId extends u64 {}
 export class ChannelOwnershipTransferRequestId extends u64 {}
 export class MaxNumber extends u32 {}
 
+export class NewAsset extends JoyEnum({
+  Upload: ContentParameters,
+  Urls: Vec.with(Url),
+}) {}
+
 export class CuratorGroup extends JoyStructDecorated({
   curators: JoyBTreeSet(CuratorId),
   active: bool,
@@ -28,7 +34,6 @@ export class ContentActor extends JoyEnum({
 }) {}
 
 export class ChannelOwner extends JoyEnum({
-  Nobody: Null,
   Member: MemberId,
   Curators: CuratorGroupId,
   Dao: DAOId,
@@ -36,21 +41,20 @@ export class ChannelOwner extends JoyEnum({
 
 export class Channel extends JoyStructDecorated({
   owner: ChannelOwner,
-  in_category: ChannelCategoryId,
-  number_of_videos: u32,
-  number_of_playlists: u32,
-  number_of_series: u32,
-  is_curated: bool,
-  revenue: u128,
+  videos: Vec.with(VideoId),
+  playlists: Vec.with(PlaylistId),
+  series: Vec.with(SeriesId),
+  is_censored: bool,
+  reward_account: Option.with(AccountId),
 }) {}
 
 export class ChannelCreationParameters extends JoyStructDecorated({
-  in_category: ChannelCategoryId,
+  assets: Vec.with(NewAsset),
   meta: Bytes,
 }) {}
 
 export class ChannelUpdateParameters extends JoyStructDecorated({
-  new_in_category: Option.with(ChannelCategoryId),
+  assets: Option.with(Vec.with(NewAsset)),
   new_meta: Bytes,
 }) {}
 
@@ -58,10 +62,11 @@ export class ChannelOwnershipTransferRequest extends JoyStructDecorated({
   channel_id: ChannelId,
   new_owner: ChannelOwner,
   payment: u128,
+  new_reward_account: Option.with(AccountId),
 }) {}
 
 export class ChannelCategory extends JoyStructDecorated({
-  number_of_channels_in: u32,
+  // No runtime information is currently stored for a Category.
 }) {}
 
 export class ChannelCategoryCreationParameters extends JoyStructDecorated({
@@ -73,7 +78,7 @@ export class ChannelCategoryUpdateParameters extends JoyStructDecorated({
 }) {}
 
 export class VideoCategory extends JoyStructDecorated({
-  number_of_videos_in_category: u32,
+  // No runtime information is currently stored for a Category.
 }) {}
 
 export class VideoCategoryCreationParameters extends JoyStructDecorated({
@@ -86,35 +91,31 @@ export class VideoCategoryUpdateParameters extends JoyStructDecorated({
 
 export class Video extends JoyStructDecorated({
   in_channel: ChannelId,
-  in_series: Vec.with(SeriesId),
-  in_playlists: Vec.with(PlaylistId),
-  is_curated: bool,
+  in_series: Option.with(SeriesId),
+  is_censored: bool,
   is_featured: bool,
 }) {}
 
 export class VideoCreationParameters extends JoyStructDecorated({
-  in_category: VideoCategoryId,
+  assets: Vec.with(NewAsset),
   meta: Bytes,
 }) {}
 
 export class VideoUpdateParameters extends JoyStructDecorated({
-  new_in_category: Option.with(VideoCategoryId),
+  assets: Option.with(Vec.with(NewAsset)),
   new_meta: Option.with(Bytes),
 }) {}
 
 export class Playlist extends JoyStructDecorated({
   in_channel: ChannelId,
-  videos: Vec.with(VideoId),
 }) {}
 
 export class PlaylistCreationParameters extends JoyStructDecorated({
-  videos: Vec.with(VideoId),
   meta: Bytes,
 }) {}
 
 export class PlaylistUpdateParameters extends JoyStructDecorated({
-  new_videos: Option.with(Vec.with(VideoId)),
-  new_meta: Option.with(Bytes),
+  new_meta: Bytes,
 }) {}
 
 export class EpisodeParemters extends JoyEnum({
@@ -127,6 +128,7 @@ export class Season extends JoyStructDecorated({
 }) {}
 
 export class SeasonParameters extends JoyStructDecorated({
+  assets: Option.with(Vec.with(NewAsset)),
   episodes: Option.with(Vec.with(Option.with(EpisodeParemters))),
   meta: Option.with(Bytes),
 }) {}
@@ -137,27 +139,28 @@ export class Series extends JoyStructDecorated({
 }) {}
 
 export class SeriesParameters extends JoyStructDecorated({
+  assets: Option.with(Vec.with(NewAsset)),
   seasons: Option.with(Vec.with(Option.with(SeasonParameters))),
   meta: Option.with(Bytes),
 }) {}
 
 export class PersonController extends JoyEnum({
-  Nobody: Null,
   Member: MemberId,
   Curators: Null,
 }) {}
 
 export class Person extends JoyStructDecorated({
   controlled_by: PersonController,
-  number_of_videos_person_involed_in: u32,
 }) {}
 
 export class PersonCreationParameters extends JoyStructDecorated({
+  assets: Vec.with(NewAsset),
   meta: Bytes,
 }) {}
 
 export class PersonUpdateParameters extends JoyStructDecorated({
-  meta: Bytes,
+  assets: Option.with(Vec.with(NewAsset)),
+  meta: Option.with(Bytes),
 }) {}
 
 export class PersonActor extends JoyEnum({
@@ -165,11 +168,6 @@ export class PersonActor extends JoyEnum({
   Curator: CuratorId,
 }) {}
 
-export class NewAsset extends JoyEnum({
-  Upload: ContentParameters,
-  Uri: Text,
-}) {}
-
 export const contentDirectoryTypes = {
   CuratorId,
   CuratorGroupId,

+ 1 - 3
types/src/discovery.ts

@@ -1,9 +1,8 @@
-import { Text, u32 } from '@polkadot/types'
+import { u32, Text } from '@polkadot/types'
 import { RegistryTypes } from '@polkadot/types/types'
 import { JoyStructDecorated } from './common'
 
 export class IPNSIdentity extends Text {}
-export class Url extends Text {}
 
 export class ServiceProviderRecord extends JoyStructDecorated({
   identity: IPNSIdentity,
@@ -11,7 +10,6 @@ export class ServiceProviderRecord extends JoyStructDecorated({
 }) {}
 
 export const discoveryTypes: RegistryTypes = {
-  Url,
   IPNSIdentity,
   ServiceProviderRecord,
 }

+ 19 - 16
types/src/storage.ts

@@ -1,5 +1,5 @@
 import { Option, Vec, BTreeMap, u64, bool, Text, Null, Bytes } from '@polkadot/types'
-import { BlockAndTime, JoyEnum, JoyStructDecorated, Hash, ChannelId, DAOId } from './common'
+import { BlockAndTime, JoyEnum, JoyStructDecorated, Hash, ChannelId, DAOId, WorkingGroup } from './common'
 import { MemberId } from './members'
 import { StorageProviderId } from './working-group' // this should be in discovery really
 import { randomAsU8a } from '@polkadot/util-crypto'
@@ -40,22 +40,12 @@ export const LiaisonJudgementDef = {
 export type LiaisonJudgementKey = keyof typeof LiaisonJudgementDef
 export class LiaisonJudgement extends JoyEnum(LiaisonJudgementDef) {}
 
-export class WorkingGroupType extends JoyEnum({
-  ContentDirectory: Null,
-  Builders: Null,
-  StorageProviders: Null,
-}) {}
-
-export class AbstractStorageObjectOwner extends JoyEnum({
+export class StorageObjectOwner extends JoyEnum({
+  Member: MemberId,
   Channel: ChannelId,
   DAO: DAOId,
   Council: Null,
-  WorkingGroup: WorkingGroupType,
-}) {}
-
-export class StorageObjectOwner extends JoyEnum({
-  Member: MemberId,
-  AbstractStorageObjectOwner: AbstractStorageObjectOwner,
+  WorkingGroup: WorkingGroup,
 }) {}
 
 export class ContentParameters extends JoyStructDecorated({
@@ -100,6 +90,18 @@ export class DataObjectType extends JoyStructDecorated({
 
 export class DataObjectsMap extends BTreeMap.with(ContentId, DataObject) {}
 
+export class Quota extends JoyStructDecorated({
+  // Total objects size limit per StorageObjectOwner
+  size_limit: u64,
+  // Total objects number limit per StorageObjectOwner
+  objects_limit: u64,
+  size_used: u64,
+  objects_used: u64,
+}) {}
+
+export class QuotaLimit extends u64 {}
+export class UploadingStatus extends bool {}
+
 export const mediaTypes: RegistryTypes = {
   ContentId,
   LiaisonJudgement,
@@ -111,9 +113,10 @@ export const mediaTypes: RegistryTypes = {
   DataObjectsMap,
   ContentParameters,
   StorageObjectOwner,
-  AbstractStorageObjectOwner,
-  WorkingGroupType,
   Content,
+  Quota,
+  QuotaLimit,
+  UploadingStatus,
 }
 
 export default mediaTypes

+ 194 - 75
yarn.lock

@@ -5033,6 +5033,11 @@
   resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-5.2.7.tgz#315d570ccb56c53452ff8638738df60726d5b6ea"
   integrity sha512-NYrtPht0wGzhwe9+/idPaBB+TqkY9AhTvOLMkThm0IoEfLaiVQZwBwyJ5puCkO3AUCWrmcoePjp2mbFocKy4SQ==
 
+"@types/mocha@^8.2.0":
+  version "8.2.0"
+  resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-8.2.0.tgz#3eb56d13a1de1d347ecb1957c6860c911704bc44"
+  integrity sha512-/Sge3BymXo4lKc31C8OINJgXLaw+7vL1/L1pGiBNpGrBiT8FQiaFpSYV0uhTaG4y78vcMBTMFsWaHDvuD+xGzQ==
+
 "@types/mustache@^4.0.1":
   version "4.0.1"
   resolved "https://registry.yarnpkg.com/@types/mustache/-/mustache-4.0.1.tgz#e4d421ed2d06d463b120621774185a5cd1b92d77"
@@ -5621,6 +5626,11 @@
   dependencies:
     eslint-visitor-keys "^1.1.0"
 
+"@ungap/promise-all-settled@1.1.2":
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz#aa58042711d6e3275dd37dc597e5d31e8c290a44"
+  integrity sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==
+
 "@vue/babel-helper-vue-jsx-merge-props@^1.0.0":
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/@vue/babel-helper-vue-jsx-merge-props/-/babel-helper-vue-jsx-merge-props-1.0.0.tgz#048fe579958da408fb7a8b2a3ec050b50a661040"
@@ -6423,16 +6433,16 @@ ansi-align@^3.0.0:
   dependencies:
     string-width "^3.0.0"
 
+ansi-colors@4.1.1, ansi-colors@^4.1.1:
+  version "4.1.1"
+  resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348"
+  integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==
+
 ansi-colors@^3.0.0, ansi-colors@^3.2.1:
   version "3.2.4"
   resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.4.tgz#e3a3da4bfbae6c86a9c285625de124a234026fbf"
   integrity sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA==
 
-ansi-colors@^4.1.1:
-  version "4.1.1"
-  resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348"
-  integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==
-
 ansi-escapes@^3.0.0, ansi-escapes@^3.1.0, ansi-escapes@^3.2.0:
   version "3.2.0"
   resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b"
@@ -8813,6 +8823,21 @@ check-error@^1.0.2:
   resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82"
   integrity sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=
 
+chokidar@3.4.3, chokidar@^3.4.0:
+  version "3.4.3"
+  resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.4.3.tgz#c1df38231448e45ca4ac588e6c79573ba6a57d5b"
+  integrity sha512-DtM3g7juCXQxFVSNPNByEC2+NImtBuxQQvWlHunpJIS5Ocr0lG306cC7FCi7cEA0fzmybPUIl4txBIobk1gGOQ==
+  dependencies:
+    anymatch "~3.1.1"
+    braces "~3.0.2"
+    glob-parent "~5.1.0"
+    is-binary-path "~2.1.0"
+    is-glob "~4.0.1"
+    normalize-path "~3.0.0"
+    readdirp "~3.5.0"
+  optionalDependencies:
+    fsevents "~2.1.2"
+
 chokidar@^1.6.0:
   version "1.7.0"
   resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-1.7.0.tgz#798e689778151c8076b4b360e5edd28cda2bb468"
@@ -8848,21 +8873,6 @@ chokidar@^2.0.3, chokidar@^2.0.4, chokidar@^2.1.8:
   optionalDependencies:
     fsevents "^1.2.7"
 
-chokidar@^3.4.0:
-  version "3.4.3"
-  resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.4.3.tgz#c1df38231448e45ca4ac588e6c79573ba6a57d5b"
-  integrity sha512-DtM3g7juCXQxFVSNPNByEC2+NImtBuxQQvWlHunpJIS5Ocr0lG306cC7FCi7cEA0fzmybPUIl4txBIobk1gGOQ==
-  dependencies:
-    anymatch "~3.1.1"
-    braces "~3.0.2"
-    glob-parent "~5.1.0"
-    is-binary-path "~2.1.0"
-    is-glob "~4.0.1"
-    normalize-path "~3.0.0"
-    readdirp "~3.5.0"
-  optionalDependencies:
-    fsevents "~2.1.2"
-
 chokidar@^3.4.1:
   version "3.4.1"
   resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.4.1.tgz#e905bdecf10eaa0a0b1db0c664481cc4cbc22ba1"
@@ -10604,6 +10614,13 @@ debug@3.1.0, debug@=3.1.0, debug@~3.1.0:
   dependencies:
     ms "2.0.0"
 
+debug@4.2.0, debug@^4, debug@^4.2.0:
+  version "4.2.0"
+  resolved "https://registry.yarnpkg.com/debug/-/debug-4.2.0.tgz#7f150f93920e94c58f5574c2fd01a3110effe7f1"
+  integrity sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==
+  dependencies:
+    ms "2.1.2"
+
 debug@^3.0.0, debug@^3.1.0, debug@^3.1.1, debug@^3.2.5, debug@^3.2.6:
   version "3.2.6"
   resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b"
@@ -10611,13 +10628,6 @@ debug@^3.0.0, debug@^3.1.0, debug@^3.1.1, debug@^3.2.5, debug@^3.2.6:
   dependencies:
     ms "^2.1.1"
 
-debug@^4, debug@^4.2.0:
-  version "4.2.0"
-  resolved "https://registry.yarnpkg.com/debug/-/debug-4.2.0.tgz#7f150f93920e94c58f5574c2fd01a3110effe7f1"
-  integrity sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==
-  dependencies:
-    ms "2.1.2"
-
 debuglog@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492"
@@ -10636,6 +10646,11 @@ decamelize@^1.1.0, decamelize@^1.1.1, decamelize@^1.1.2, decamelize@^1.2.0:
   resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
   integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=
 
+decamelize@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837"
+  integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==
+
 decimal.js@^10.2.0:
   version "10.2.0"
   resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.2.0.tgz#39466113a9e036111d02f82489b5fd6b0b5ed231"
@@ -11085,7 +11100,7 @@ diff@3.5.0, diff@^3.1.0, diff@^3.5.0:
   resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12"
   integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==
 
-diff@^4.0.1:
+diff@4.0.2, diff@^4.0.1:
   version "4.0.2"
   resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d"
   integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==
@@ -13220,6 +13235,14 @@ find-up@3.0.0, find-up@^3.0.0:
   dependencies:
     locate-path "^3.0.0"
 
+find-up@5.0.0:
+  version "5.0.0"
+  resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc"
+  integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==
+  dependencies:
+    locate-path "^6.0.0"
+    path-exists "^4.0.0"
+
 find-up@^1.0.0:
   version "1.1.2"
   resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f"
@@ -13276,6 +13299,11 @@ flat-cache@^2.0.1:
     rimraf "2.6.3"
     write "1.0.3"
 
+flat@^5.0.2:
+  version "5.0.2"
+  resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241"
+  integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==
+
 flatmap@0.0.3:
   version "0.0.3"
   resolved "https://registry.yarnpkg.com/flatmap/-/flatmap-0.0.3.tgz#1f18a4d938152d495965f9c958d923ab2dd669b4"
@@ -13956,7 +13984,7 @@ glob2base@^0.0.12:
   dependencies:
     find-index "^0.1.1"
 
-glob@*, glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@~7.1.1:
+glob@*, glob@7.1.6, glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@~7.1.1:
   version "7.1.6"
   resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6"
   integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==
@@ -14267,6 +14295,11 @@ google-libphonenumber@^3.1.6, google-libphonenumber@^3.2.8:
   resolved "https://registry.yarnpkg.com/google-libphonenumber/-/google-libphonenumber-3.2.14.tgz#f206fa466511427c83aa6b893dd8d949eb8e30ae"
   integrity sha512-4r7mQRbk7EUYV1gyfP1SInYuQsjuDtRXCGLSotxeYDJaj/aF1xFO5PV/GSQeIxXWhIw050DujROICvWpZ1XYRw==
 
+google-protobuf@^3.14.0, google-protobuf@^3.6.1:
+  version "3.14.0"
+  resolved "https://registry.yarnpkg.com/google-protobuf/-/google-protobuf-3.14.0.tgz#20373d22046e63831a5110e11a84f713cc43651e"
+  integrity sha512-bwa8dBuMpOxg7COyqkW6muQuvNnWgVN8TX/epDRGW5m0jcrmq2QJyCyiV8ZE2/6LaIIqJtiv9bYokFhfpy/o6w==
+
 got@^6.3.0, got@^6.7.1:
   version "6.7.1"
   resolved "https://registry.yarnpkg.com/got/-/got-6.7.1.tgz#240cd05785a9a18e561dc1b44b41c763ef1e8db0"
@@ -14670,7 +14703,7 @@ he@1.1.1:
   resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd"
   integrity sha1-k0EP0hsAlzUVH4howvJx80J+I/0=
 
-he@1.2.x, he@^1.1.0, he@^1.2.0:
+he@1.2.0, he@1.2.x, he@^1.1.0, he@^1.2.0:
   version "1.2.0"
   resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
   integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
@@ -16514,6 +16547,11 @@ is-plain-obj@^2.0.0:
   resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.0.0.tgz#7fd1a7f1b69e160cde9181d2313f445c68aa2679"
   integrity sha512-EYisGhpgSCwspmIuRHGjROWTon2Xp8Z7U03Wubk/bTL5TTRC5R1rGVgyjzBrk9+ULdH6cRD06KRcw/xfqhVYKQ==
 
+is-plain-obj@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287"
+  integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==
+
 is-plain-object@3.0.0, is-plain-object@^3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-3.0.0.tgz#47bfc5da1b5d50d64110806c199359482e75a928"
@@ -18185,7 +18223,7 @@ js-tokens@^3.0.2:
   resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b"
   integrity sha1-mGbfOVECEw449/mWvOtlRDIJwls=
 
-js-yaml@^3.10.0, js-yaml@^3.14.0:
+js-yaml@3.14.0, js-yaml@^3.10.0, js-yaml@^3.14.0:
   version "3.14.0"
   resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.0.tgz#a7a34170f26a21bb162424d8adacb4113a69e482"
   integrity sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==
@@ -19089,6 +19127,13 @@ locate-path@^5.0.0:
   dependencies:
     p-locate "^4.1.0"
 
+locate-path@^6.0.0:
+  version "6.0.0"
+  resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286"
+  integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==
+  dependencies:
+    p-locate "^5.0.0"
+
 lodash-es@^4.17.14:
   version "4.17.15"
   resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.15.tgz#21bd96839354412f23d7a10340e5eac6ee455d78"
@@ -19294,6 +19339,13 @@ log-driver@^1.2.7:
   resolved "https://registry.yarnpkg.com/log-driver/-/log-driver-1.2.7.tgz#63b95021f0702fedfa2c9bb0a24e7797d71871d8"
   integrity sha512-U7KCmLdqsGHBLeWqYlFA0V0Sl6P08EE1ZrmA9cxjUE0WVqT9qnyVDPz1kzpFEP0jdJuFnasWIfSd7fsaNXkpbg==
 
+log-symbols@4.0.0, log-symbols@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.0.0.tgz#69b3cc46d20f448eccdb75ea1fa733d9e821c920"
+  integrity sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==
+  dependencies:
+    chalk "^4.0.0"
+
 log-symbols@^1.0.2:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-1.0.2.tgz#376ff7b58ea3086a0f09facc74617eca501e1a18"
@@ -19315,13 +19367,6 @@ log-symbols@^3.0.0:
   dependencies:
     chalk "^2.4.2"
 
-log-symbols@^4.0.0:
-  version "4.0.0"
-  resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.0.0.tgz#69b3cc46d20f448eccdb75ea1fa733d9e821c920"
-  integrity sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==
-  dependencies:
-    chalk "^4.0.0"
-
 log-update@^2.3.0:
   version "2.3.0"
   resolved "https://registry.yarnpkg.com/log-update/-/log-update-2.3.0.tgz#88328fd7d1ce7938b29283746f0b1bc126b24708"
@@ -20292,6 +20337,37 @@ mocha@^5.2.0:
     mkdirp "0.5.1"
     supports-color "5.4.0"
 
+mocha@^8.2.1:
+  version "8.2.1"
+  resolved "https://registry.yarnpkg.com/mocha/-/mocha-8.2.1.tgz#f2fa68817ed0e53343d989df65ccd358bc3a4b39"
+  integrity sha512-cuLBVfyFfFqbNR0uUKbDGXKGk+UDFe6aR4os78XIrMQpZl/nv7JYHcvP5MFIAb374b2zFXsdgEGwmzMtP0Xg8w==
+  dependencies:
+    "@ungap/promise-all-settled" "1.1.2"
+    ansi-colors "4.1.1"
+    browser-stdout "1.3.1"
+    chokidar "3.4.3"
+    debug "4.2.0"
+    diff "4.0.2"
+    escape-string-regexp "4.0.0"
+    find-up "5.0.0"
+    glob "7.1.6"
+    growl "1.10.5"
+    he "1.2.0"
+    js-yaml "3.14.0"
+    log-symbols "4.0.0"
+    minimatch "3.0.4"
+    ms "2.1.2"
+    nanoid "3.1.12"
+    serialize-javascript "5.0.1"
+    strip-json-comments "3.1.1"
+    supports-color "7.2.0"
+    which "2.0.2"
+    wide-align "1.1.3"
+    workerpool "6.0.2"
+    yargs "13.3.2"
+    yargs-parser "13.1.2"
+    yargs-unparser "2.0.0"
+
 mock-stdin@^0.3.1:
   version "0.3.1"
   resolved "https://registry.yarnpkg.com/mock-stdin/-/mock-stdin-0.3.1.tgz#c657d9642d90786435c64ca5e99bbd4d09bd7dd3"
@@ -20645,16 +20721,16 @@ nan@^2.13.2:
   resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.1.tgz#d7be34dfa3105b91494c3147089315eff8874b01"
   integrity sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw==
 
+nanoid@3.1.12, nanoid@^3.0.2, nanoid@^3.1.3:
+  version "3.1.12"
+  resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.12.tgz#6f7736c62e8d39421601e4a0c77623a97ea69654"
+  integrity sha512-1qstj9z5+x491jfiC4Nelk+f8XBad7LN20PmyWINJEMRSf3wcAjAWysw1qaA8z6NSKe2sjq1hRSDpBH5paCb6A==
+
 nanoid@^2.1.0:
   version "2.1.11"
   resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-2.1.11.tgz#ec24b8a758d591561531b4176a01e3ab4f0f0280"
   integrity sha512-s/snB+WGm6uwi0WjsZdaVcuf3KJXlfGl2LcxgwkEwJF0D/BWzVWAZW/XY4bFaiR7s0Jk3FPvlnepg1H1b1UwlA==
 
-nanoid@^3.0.2, nanoid@^3.1.3:
-  version "3.1.12"
-  resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.12.tgz#6f7736c62e8d39421601e4a0c77623a97ea69654"
-  integrity sha512-1qstj9z5+x491jfiC4Nelk+f8XBad7LN20PmyWINJEMRSf3wcAjAWysw1qaA8z6NSKe2sjq1hRSDpBH5paCb6A==
-
 nanomatch@^1.2.9:
   version "1.2.13"
   resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119"
@@ -21902,6 +21978,13 @@ p-locate@^4.1.0:
   dependencies:
     p-limit "^2.2.0"
 
+p-locate@^5.0.0:
+  version "5.0.0"
+  resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834"
+  integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==
+  dependencies:
+    p-limit "^3.0.2"
+
 p-map-series@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/p-map-series/-/p-map-series-1.0.0.tgz#bf98fe575705658a9e1351befb85ae4c1f07bdca"
@@ -25664,6 +25747,13 @@ serialize-error@^7.0.1:
   dependencies:
     type-fest "^0.13.1"
 
+serialize-javascript@5.0.1:
+  version "5.0.1"
+  resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-5.0.1.tgz#7886ec848049a462467a97d3d918ebb2aaf934f4"
+  integrity sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==
+  dependencies:
+    randombytes "^2.1.0"
+
 serialize-javascript@^2.1.0, serialize-javascript@^2.1.2:
   version "2.1.2"
   resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-2.1.2.tgz#ecec53b0e0317bdc95ef76ab7074b7384785fa61"
@@ -26799,6 +26889,11 @@ strip-indent@^3.0.0:
   dependencies:
     min-indent "^1.0.0"
 
+strip-json-comments@3.1.1, strip-json-comments@^3.1.0:
+  version "3.1.1"
+  resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"
+  integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==
+
 strip-json-comments@^2.0.0, strip-json-comments@~2.0.1:
   version "2.0.1"
   resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
@@ -26809,11 +26904,6 @@ strip-json-comments@^3.0.1:
   resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.0.1.tgz#85713975a91fb87bf1b305cca77395e40d2a64a7"
   integrity sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw==
 
-strip-json-comments@^3.1.0:
-  version "3.1.1"
-  resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"
-  integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==
-
 strip-markdown@^3.0.3:
   version "3.1.2"
   resolved "https://registry.yarnpkg.com/strip-markdown/-/strip-markdown-3.1.2.tgz#172f6f89f9a98896e65a65422e0507f2bbac1667"
@@ -27041,6 +27131,13 @@ supports-color@5.4.0:
   dependencies:
     has-flag "^3.0.0"
 
+supports-color@7.2.0:
+  version "7.2.0"
+  resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da"
+  integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==
+  dependencies:
+    has-flag "^4.0.0"
+
 supports-color@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7"
@@ -27920,6 +28017,13 @@ ts-pnp@^1.1.2:
   resolved "https://registry.yarnpkg.com/ts-pnp/-/ts-pnp-1.2.0.tgz#a500ad084b0798f1c3071af391e65912c86bca92"
   integrity sha512-csd+vJOb/gkzvcCHgTGSChYpy5f1/XKNsmvBGO4JXS+z1v2HobugDz4s1IeFXM3wZB44uczs+eazB5Q/ccdhQw==
 
+ts-protoc-gen@^0.14.0:
+  version "0.14.0"
+  resolved "https://registry.yarnpkg.com/ts-protoc-gen/-/ts-protoc-gen-0.14.0.tgz#a6f4c3fc37d1d449915551c18404fb7e9aa8fef6"
+  integrity sha512-2z6w2HioMCMVNcgNHBcEvudmQfzrn+3BjAlz+xgYZ9L0o8n8UG8WUiTJcbXHFiEg2SU8IltwH2pm1otLoMSKwg==
+  dependencies:
+    google-protobuf "^3.6.1"
+
 tsconfig-paths-webpack-plugin@^3.2.0:
   version "3.3.0"
   resolved "https://registry.yarnpkg.com/tsconfig-paths-webpack-plugin/-/tsconfig-paths-webpack-plugin-3.3.0.tgz#a7461723c20623ca9148621a5ce36532682ad2ff"
@@ -28202,7 +28306,7 @@ typescript-formatter@^7.2.2:
     commandpost "^1.0.0"
     editorconfig "^0.15.0"
 
-typescript@3.5.2, typescript@^3.0.3, typescript@^3.7.2, typescript@^3.7.5, typescript@^3.8.3, typescript@^3.9.5, typescript@^3.9.6, typescript@^3.9.7:
+typescript@3.5.2, typescript@^3.0.3, typescript@^3.7.2, typescript@^3.7.5, typescript@^3.8.3, typescript@^3.9.5, typescript@^3.9.6, typescript@^3.9.7, typescript@^4.1.3:
   version "3.9.7"
   resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.7.tgz#98d600a5ebdc38f40cb277522f12dc800e9e25fa"
   integrity sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw==
@@ -29600,14 +29704,14 @@ which@1, which@^1.1.1, which@^1.2.14, which@^1.2.9, which@^1.3.0, which@^1.3.1:
   dependencies:
     isexe "^2.0.0"
 
-which@^2.0.0, which@^2.0.1, which@^2.0.2:
+which@2.0.2, which@^2.0.0, which@^2.0.1, which@^2.0.2:
   version "2.0.2"
   resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1"
   integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==
   dependencies:
     isexe "^2.0.0"
 
-wide-align@^1.1.0:
+wide-align@1.1.3, wide-align@^1.1.0:
   version "1.1.3"
   resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457"
   integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==
@@ -29674,6 +29778,11 @@ worker-rpc@^0.1.0:
   dependencies:
     microevent.ts "~0.1.1"
 
+workerpool@6.0.2:
+  version "6.0.2"
+  resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.0.2.tgz#e241b43d8d033f1beb52c7851069456039d1d438"
+  integrity sha512-DSNyvOpFKrNusaaUwk+ej6cBj1bmhLcBfj80elGk+ZIo5JSkq+unB1dLKEOcNfJDZgjGICfhQ0Q5TbP0PvF4+Q==
+
 wrap-ansi@^2.0.0:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85"
@@ -29987,6 +30096,14 @@ yargs-parser@10.x, yargs-parser@^10.0.0:
   dependencies:
     camelcase "^4.1.0"
 
+yargs-parser@13.1.2, yargs-parser@^13.0.0, yargs-parser@^13.1.2:
+  version "13.1.2"
+  resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.2.tgz#130f09702ebaeef2650d54ce6e3e5706f7a4fb38"
+  integrity sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==
+  dependencies:
+    camelcase "^5.0.0"
+    decamelize "^1.2.0"
+
 yargs-parser@18.x, yargs-parser@^18.1.2, yargs-parser@^18.1.3:
   version "18.1.3"
   resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0"
@@ -30003,14 +30120,6 @@ yargs-parser@^11.1.1:
     camelcase "^5.0.0"
     decamelize "^1.2.0"
 
-yargs-parser@^13.0.0, yargs-parser@^13.1.2:
-  version "13.1.2"
-  resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.2.tgz#130f09702ebaeef2650d54ce6e3e5706f7a4fb38"
-  integrity sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==
-  dependencies:
-    camelcase "^5.0.0"
-    decamelize "^1.2.0"
-
 yargs-parser@^13.1.1:
   version "13.1.1"
   resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.1.tgz#d26058532aa06d365fe091f6a1fc06b2f7e5eca0"
@@ -30055,6 +30164,32 @@ yargs-parser@^8.1.0:
   dependencies:
     camelcase "^4.1.0"
 
+yargs-unparser@2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-2.0.0.tgz#f131f9226911ae5d9ad38c432fe809366c2325eb"
+  integrity sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==
+  dependencies:
+    camelcase "^6.0.0"
+    decamelize "^4.0.0"
+    flat "^5.0.2"
+    is-plain-obj "^2.1.0"
+
+yargs@13.3.2, yargs@^13.2.2, yargs@^13.3.2:
+  version "13.3.2"
+  resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.2.tgz#ad7ffefec1aa59565ac915f82dccb38a9c31a2dd"
+  integrity sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==
+  dependencies:
+    cliui "^5.0.0"
+    find-up "^3.0.0"
+    get-caller-file "^2.0.1"
+    require-directory "^2.1.1"
+    require-main-filename "^2.0.0"
+    set-blocking "^2.0.0"
+    string-width "^3.0.0"
+    which-module "^2.0.0"
+    y18n "^4.0.0"
+    yargs-parser "^13.1.2"
+
 yargs@^10.0.3:
   version "10.1.2"
   resolved "https://registry.yarnpkg.com/yargs/-/yargs-10.1.2.tgz#454d074c2b16a51a43e2fb7807e4f9de69ccb5c5"
@@ -30091,22 +30226,6 @@ yargs@^12.0.2:
     y18n "^3.2.1 || ^4.0.0"
     yargs-parser "^11.1.1"
 
-yargs@^13.2.2, yargs@^13.3.2:
-  version "13.3.2"
-  resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.2.tgz#ad7ffefec1aa59565ac915f82dccb38a9c31a2dd"
-  integrity sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==
-  dependencies:
-    cliui "^5.0.0"
-    find-up "^3.0.0"
-    get-caller-file "^2.0.1"
-    require-directory "^2.1.1"
-    require-main-filename "^2.0.0"
-    set-blocking "^2.0.0"
-    string-width "^3.0.0"
-    which-module "^2.0.0"
-    y18n "^4.0.0"
-    yargs-parser "^13.1.2"
-
 yargs@^13.3.0:
   version "13.3.0"
   resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.0.tgz#4c657a55e07e5f2cf947f8a366567c04a0dedc83"

Vissa filer visades inte eftersom för många filer har ändrats