diff --git a/.gitignore b/.gitignore index 4a113e3..da81425 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ /lib /build *.dwarf +/Gemfile.lock diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..dbae211 --- /dev/null +++ b/Gemfile @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +source 'https://rubygems.org' + +gem 'openssl' diff --git a/Makefile b/Makefile index 130b332..4a3373c 100644 --- a/Makefile +++ b/Makefile @@ -2,18 +2,20 @@ all: teletube teletube: main.cr src/**/*.cr shards - ruby scripts/options.rb + bundle + bundle exec ruby scripts/options.rb crystal build --error-trace -o teletube main.cr @strip teletube @du -sh teletube release: main.cr src/**/*.cr shards - ruby scripts/options.rb + bundle + bundle exec ruby scripts/options.rb crystal build main.cr --release -o teletube @strip teletube @du -sh teletube - ruby scripts/build.rb + bundle exec ruby scripts/build.rb clean: rm -rf .crystal teletube .deps .shards libs lib *.dwarf build @@ -22,4 +24,4 @@ PREFIX ?= /usr/local install: teletube install -d $(PREFIX)/bin - install teletube $(PREFIX)/bin \ No newline at end of file + install teletube $(PREFIX)/bin diff --git a/scripts/options.rb b/scripts/options.rb index 73866e5..9db1371 100755 --- a/scripts/options.rb +++ b/scripts/options.rb @@ -7,7 +7,12 @@ BASE_URI = 'https://tube.switch.ch' RESOURCES = { 'channel' => '/schema/v1/channel.json', - 'video' => '/schema/v1/video.json' + 'video' => '/schema/v1/video.json', + 'collection' => '/schema/v1/collection.json', + 'create_collection_item' => '/schema/v1/create_collection_item.json', + 'update_collection_item' => '/schema/v1/update_collection_item.json', + 'create_collaborator' => '/schema/v1/create_collaborator.json', + 'create_collaborator_invitation' => '/schema/v1/create_collaborator_invitation.json', }.freeze class Property diff --git a/src/teletube/cli.cr b/src/teletube/cli.cr index d850dab..066c1a0 100644 --- a/src/teletube/cli.cr +++ b/src/teletube/cli.cr @@ -80,6 +80,48 @@ module Teletube else @client.get_channels end + when "collaborators" + case context.command + when "create" + @client.create_collaborator + when "destroy" + @client.destroy_collaborator + else + @client.get_collaborators + end + when "collaborator-invitations" + case context.command + when "create" + @client.create_collaborator_invitation + when "destroy" + @client.destroy_collaborator_invitation + else + @client.get_collaborator_invitations + end + when "collection-items" + case context.command + when "create" + @client.create_collection_item + when "update" + @client.update_collection_item + when "destroy" + @client.destroy_collection_item + else + @client.get_collection_items + end + when "collections" + case context.command + when "create" + @client.create_collection + when "show" + @client.get_collection + when "update" + @client.update_collection + when "destroy" + @client.destroy_collection + else + @client.get_collections + end when "documents" case context.command when "create" @@ -109,6 +151,8 @@ module Teletube else @client.get_profiles_me end + when "positions" + @client.update_positions when "progress" @client.get_video_progress when "restorations" diff --git a/src/teletube/client.cr b/src/teletube/client.cr index c3a6fc5..7c49b8f 100644 --- a/src/teletube/client.cr +++ b/src/teletube/client.cr @@ -44,6 +44,109 @@ module Teletube ) end + def get_collaborators + handle_response( + @http.get( + path: + "/api/v1/#{@context.params["type"]}/#{@context.params["id"]}" \ + "/collaborators" + ) + ) + end + + def create_collaborator + handle_response( + @http.post( + path: + "/api/v1/#{@context.params["type"]}/#{@context.params["id"]}" \ + "/collaborators", + params: @context.params + ) + ) + end + + def destroy_collaborator + handle_response(@http.delete(path: "/api/v1/collaborators/#{@context.params["id"]}")) + end + + def get_collaborator_invitations + handle_response( + @http.get( + path: + "/api/v1/#{@context.params["type"]}/#{@context.params["id"]}" \ + "/collaborator_invitations" + ) + ) + end + + def create_collaborator_invitation + handle_response( + @http.post( + path: + "/api/v1/#{@context.params["type"]}/#{@context.params["id"]}" \ + "/collaborator_invitations", + params: @context.params + ) + ) + end + + def destroy_collaborator_invitation + handle_response( + @http.delete(path: "/api/v1/collaborator_invitations/#{@context.params["id"]}") + ) + end + + def get_collection_items + handle_response( + @http.get(path: "/api/v1/collections/#{@context.params["collection_id"]}/collection_items") + ) + end + + def create_collection_item + handle_response( + @http.post( + path: "/api/v1/collections/#{@context.params["collection_id"]}/collection_items", + params: @context.params + ) + ) + end + + def update_collection_item + handle_response( + @http.patch( + path: "/api/v1/collection_items/#{@context.params["id"]}", params: @context.params + ) + ) + end + + def destroy_collection_item + handle_response(@http.delete(path: "/api/v1/collection_items/#{@context.params["id"]}")) + end + + def get_collections + handle_response(@http.get(path: "/api/v1/collections")) + end + + def create_collection + handle_response(@http.post(path: "/api/v1/collections", params: @context.params)) + end + + def get_collection + handle_response(@http.get(path: "/api/v1/collections/#{@context.params["id"]}")) + end + + def update_collection + handle_response( + @http.patch(path: "/api/v1/collections/#{@context.params["id"]}", params: @context.params) + ) + end + + def destroy_collection + handle_response( + @http.delete(path: "/api/v1/collections/#{@context.params["id"]}") + ) + end + class Upload # 50 megabytes SEGMENT_SIZE = 52428800 @@ -205,6 +308,8 @@ module Teletube handle_response(@http.get(path: "/api/v1/videos/#{@context.params["video_id"]}/documents")) elsif @context.params.has_key?("channel_id") handle_response(@http.get(path: "/api/v1/channels/#{@context.params["channel_id"]}/documents")) + else + handle_response(@http.get(path: "/api/v1/documents")) end end @@ -238,6 +343,18 @@ module Teletube handle_response(@http.get(path: "/api/v1/profiles/me")) end + def update_positions + positions = @context.params["positions"].as_s.split(",").map do |position| + position.to_i + end + handle_response( + @http.patch( + path: "/api/v1/#{@context.params["type"]}/#{@context.params["id"]}/positions", + params: positions + ), + ) + end + def get_video_progress handle_response(@http.get(path: "/api/v1/videos/#{@context.params["video_id"]}/progress")) end diff --git a/src/teletube/http.cr b/src/teletube/http.cr index 5bedf13..3aa5d35 100644 --- a/src/teletube/http.cr +++ b/src/teletube/http.cr @@ -50,7 +50,7 @@ module Teletube @http.post(path: path, headers: all, body: body) end - def patch(path : String, params : Hash(String, JSON::Any)) + def patch(path : String, params : Array | Hash(String, JSON::Any)) @http.patch(path: path, headers: @headers, body: params.to_json) end diff --git a/src/teletube/option_parser.cr b/src/teletube/option_parser.cr index 8bdc732..6a67e1a 100644 --- a/src/teletube/option_parser.cr +++ b/src/teletube/option_parser.cr @@ -96,6 +96,146 @@ module Teletube end end + parser.on( + "collaborator-invitations", + "Invite people to collaborator on channels or collections" + ) do + context.resource = "collaborator-invitations" + context.command = "list" + parser.separator "" + parser.separator "Actions:" + parser.on("list", "List all invitations for a channel or collection.") do + context.command = "list" + + parser.on("--type TYPE", "Either ‘channels’ or ‘collections’.") do |type| + context.params["type"] = JSON::Any.new(type) + end + + parser.on("--id ID", "The identifier of the channel or collection.") do |id| + context.params["id"] = JSON::Any.new(id) + end + end + parser.on("create", "Create a new invitation to a channel or collection.") do + context.command = "create" + + parser.on("--type TYPE", "Either ‘channels’ or ‘collections’.") do |type| + context.params["type"] = JSON::Any.new(type) + end + + parser.on("--id ID", "The identifier of the channel or collection.") do |id| + context.params["id"] = JSON::Any.new(id) + end + + Teletube::Resources::CreateCollaboratorInvitation.parse_properties(parser, context) + end + parser.on("destroy", "Delete a collaborator invitation.") do + context.command = "destroy" + parser.on("--id ID", "The identifier of the collaborator invitation to remove.") do |id| + context.params["id"] = JSON::Any.new(id) + end + end + end + + parser.on("collaborators", "Add collaborators to a channel or collection") do + context.resource = "collaborators" + context.command = "list" + parser.separator "" + parser.separator "Actions:" + parser.on("list", "List all collaborators for a channel or collection.") do + context.command = "list" + + parser.on("--type TYPE", "Either ‘channels’ or ‘collections’.") do |type| + context.params["type"] = JSON::Any.new(type) + end + + parser.on("--id ID", "The identifier of the channel or collection.") do |id| + context.params["id"] = JSON::Any.new(id) + end + end + parser.on("create", "Create a new collaborator for a channel or collection.") do + context.command = "create" + + parser.on("--type TYPE", "Either ‘channels’ or ‘collections’.") do |type| + context.params["type"] = JSON::Any.new(type) + end + + parser.on("--id ID", "The identifier of the channel or collection.") do |id| + context.params["id"] = JSON::Any.new(id) + end + + Teletube::Resources::CreateCollaborator.parse_properties(parser, context) + end + parser.on("destroy", "Delete a collaborator.") do + context.command = "destroy" + parser.on("--id ID", "The identifier of the collaborator invitation to remove.") do |id| + context.params["id"] = JSON::Any.new(id) + end + end + end + + parser.on("collection-items", "Manage items in a collection.") do + context.resource = "collection-items" + context.command = "list" + parser.separator "" + parser.separator "Actions:" + parser.on("list", "List all items in a collection.") do + context.command = "list" + parser.on("--collection-id ID", "The identifier of the collection.") do |id| + context.params["collection_id"] = JSON::Any.new(id) + end + end + parser.on("create", "Create a new collection item.") do + context.command = "create" + Teletube::Resources::CreateCollectionItem.parse_properties(parser, context) + end + parser.on("update", "Create note for a collectin item.") do + context.command = "update" + parser.on("--id ID", "The identifier of the collection item to update.") do |id| + context.params["id"] = JSON::Any.new(id) + end + Teletube::Resources::UpdateCollectionItem.parse_properties(parser, context) + end + parser.on("destroy", "Remove a collection item from a collection.") do + context.command = "destroy" + parser.on("--id ID", "The identifier of the collection item to remove.") do |id| + context.params["id"] = JSON::Any.new(id) + end + end + end + + parser.on("collections", "Interact with collections.") do + context.resource = "collections" + context.command = "list" + parser.separator "" + parser.separator "Actions:" + parser.on("list", "List all editable collections.") do + context.command = "list" + end + parser.on("create", "Create a new collection.") do + context.command = "create" + Teletube::Resources::Collection.parse_properties(parser, context) + end + parser.on("show", "Show details about a collection.") do + context.command = "show" + parser.on("--id ID", "The identifier of the collection to show.") do |id| + context.params["id"] = JSON::Any.new(id) + end + end + parser.on("update", "Update details for a collection.") do + context.command = "update" + parser.on("--id ID", "The identifier of the collection to update.") do |id| + context.params["id"] = JSON::Any.new(id) + end + Teletube::Resources::Collection.parse_properties(parser, context) + end + parser.on("destroy", "Destroy a collection.") do + context.command = "destroy" + parser.on("--id ID", "The identifier of the collection to destroy.") do |id| + context.params["id"] = JSON::Any.new(id) + end + end + end + parser.on("documents", "Interact with documents.") do context.resource = "documents" parser.on("list", "List all documents uploaded for a channel or video.") do @@ -199,14 +339,34 @@ module Teletube context.command = "list" end + parser.on("positions", "Manage order for resources.") do + context.resource = "positions" + parser.on("update", "Re-order collection items.") do + context.command = "update" + + parser.on("--id ID", "The identifier of the profile to show.") do |id| + context.params["id"] = JSON::Any.new(id) + end + + parser.on("--type TYPE", "Currenly only ‘collections’ is suported.") do |type| + context.params["type"] = JSON::Any.new(type) + end + + parser.on("--id ID", "The identifier of the video or channel.") do |id| + context.params["id"] = JSON::Any.new(id) + end + + parser.on("--positions POSITIONS", "Comma-separated list of ids, eg: 23,32,1,172") do |ids| + context.params["positions"] = JSON::Any.new(ids) + end + end + end + parser.on("profiles", "Interact with user profiles.") do context.resource = "profiles" context.command = "me" parser.on("show", "Show details about a profile.") do context.command = "show" - parser.on("--id ID", "The identifier of the profile to show.") do |id| - context.params["id"] = JSON::Any.new(id) - end end end @@ -222,7 +382,7 @@ module Teletube context.resource = "restorations" context.command = "create" - parser.on("--type TYPE", "Either ‘channels’ or ‘videos’.") do |type| + parser.on("--type TYPE", "Either ‘channels’, ‘videos’, or ‘collections’.") do |type| context.params["type"] = JSON::Any.new(type) end diff --git a/src/teletube/resources/channel.cr b/src/teletube/resources/channel.cr index 99519c4..78e03b6 100644 --- a/src/teletube/resources/channel.cr +++ b/src/teletube/resources/channel.cr @@ -2,7 +2,10 @@ module Teletube module Resources class Channel macro parse_properties(parser, context) - parser.on("--name NAME", "Display name of the channel.") do |value| + parser.on("--title TITLE", "The title of the channel") do |value| + context.params["title"] = JSON::Any.new(value) + end + parser.on("--name NAME", "Deprecated, please use title instead.") do |value| context.params["name"] = JSON::Any.new(value) end parser.on("--description DESCRIPTION", "Description of a channel, the text may contain a limited set of HTML tags: p, h3, ul, ol, li, pre, blockquote, a, em, strong.") do |value| diff --git a/src/teletube/resources/collection.cr b/src/teletube/resources/collection.cr new file mode 100644 index 0000000..56af905 --- /dev/null +++ b/src/teletube/resources/collection.cr @@ -0,0 +1,19 @@ +module Teletube + module Resources + class Collection + macro parse_properties(parser, context) + parser.on("--title TITLE", "The title of the collection.") do |value| + context.params["title"] = JSON::Any.new(value) + end + parser.on("--description DESCRIPTION", "Description of a collection, the text may contain a limited set of HTML tags: p, h3, ul, ol, li, pre, blockquote, a, em, strong. + ") do |value| + context.params["description"] = JSON::Any.new(value) + end + parser.on("--viewable-by VIEWABLE_BY", "Gives permission to see the collection, note that the collection items will always have their own permissions dictated by their channel. See the form to edit collection permissions on the website to understand how permissions work. After choosing the collaborator setting you can use the createCollectionCollaborators operation to manage collaborator permissions for profiles. + ") do |value| + context.params["viewable_by"] = JSON::Any.new(value) + end + end + end + end +end diff --git a/src/teletube/resources/collection_item.cr b/src/teletube/resources/collection_item.cr new file mode 100644 index 0000000..083e3b2 --- /dev/null +++ b/src/teletube/resources/collection_item.cr @@ -0,0 +1,20 @@ +module Teletube + module Resources + class CollectionItem + macro parse_properties(parser, context) + parser.on("--collectable-type COLLECTABLE_TYPE", "The resource type of the collectable. + ") do |value| + context.params["collectable_type"] = JSON::Any.new(value) + end + parser.on("--collectable-id COLLECTABLE_ID", "The resource id of the collectable. + ") do |value| + context.params["collectable_id"] = JSON::Any.new(value) + end + parser.on("--note NOTE", "Optional note which is presented with the collection item when viewing the collection. The text may contain a limited set of HTML tags: p, h3, ul, ol, li, pre, blockquote, a, em, strong. + ") do |value| + context.params["note"] = JSON::Any.new(value) + end + end + end + end +end diff --git a/src/teletube/resources/create_collaborator.cr b/src/teletube/resources/create_collaborator.cr new file mode 100644 index 0000000..609b301 --- /dev/null +++ b/src/teletube/resources/create_collaborator.cr @@ -0,0 +1,15 @@ +module Teletube + module Resources + class CreateCollaborator + macro parse_properties(parser, context) + parser.on("--profile-id PROFILE_ID", "Numeric identifier for the profile.") do |value| + context.params["profile_id"] = JSON::Any.new(value) + end + parser.on("--permission PERMISSION", "String that indicates the permissions the profile will have. The permission names should speak for themselves and are incremental, meaning that manage-channel also gives upload and viewing permission. + ") do |value| + context.params["permission"] = JSON::Any.new(value) + end + end + end + end +end diff --git a/src/teletube/resources/create_collaborator_invitation.cr b/src/teletube/resources/create_collaborator_invitation.cr new file mode 100644 index 0000000..43c8107 --- /dev/null +++ b/src/teletube/resources/create_collaborator_invitation.cr @@ -0,0 +1,14 @@ +module Teletube + module Resources + class CreateCollaboratorInvitation + macro parse_properties(parser, context) + parser.on("--email EMAIL", "The e-mail address used to send the invitation. The recipient will be able to click a link to accept the invitation. If the e-mail address matches their Switch Tube profile they will also be able to accept the invitation on their profile page.") do |value| + context.params["email"] = JSON::Any.new(value) + end + parser.on("--permission PERMISSION", "Holds a single string that describes the access permission of the collaborator to the channel or collection. See the permissions form for a channel or collection on the website for a longer description of the permissions.") do |value| + context.params["permission"] = JSON::Any.new(value) + end + end + end + end +end diff --git a/src/teletube/resources/create_collection_item.cr b/src/teletube/resources/create_collection_item.cr new file mode 100644 index 0000000..808ad07 --- /dev/null +++ b/src/teletube/resources/create_collection_item.cr @@ -0,0 +1,20 @@ +module Teletube + module Resources + class CreateCollectionItem + macro parse_properties(parser, context) + parser.on("--collectable-type COLLECTABLE_TYPE", "The resource type of the collectable. + ") do |value| + context.params["collectable_type"] = JSON::Any.new(value) + end + parser.on("--collectable-id COLLECTABLE_ID", "The resource id of the collectable. + ") do |value| + context.params["collectable_id"] = JSON::Any.new(value) + end + parser.on("--note NOTE", "Optional note which is presented with the collection item when viewing the collection. The text may contain a limited set of HTML tags: p, h3, ul, ol, li, pre, blockquote, a, em, strong. + ") do |value| + context.params["note"] = JSON::Any.new(value) + end + end + end + end +end diff --git a/src/teletube/resources/update_collection_item.cr b/src/teletube/resources/update_collection_item.cr new file mode 100644 index 0000000..ead3b22 --- /dev/null +++ b/src/teletube/resources/update_collection_item.cr @@ -0,0 +1,12 @@ +module Teletube + module Resources + class UpdateCollectionItem + macro parse_properties(parser, context) + parser.on("--note NOTE", "Optional note which is presented with the collection item when viewing the collection. The text may contain a limited set of HTML tags: p, h3, ul, ol, li, pre, blockquote, a, em, strong. + ") do |value| + context.params["note"] = JSON::Any.new(value) + end + end + end + end +end