crawlora-youtube 0.1.8 → 0.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2eb80e7fd5002744639c55013596f6a7d603aaaef708806ca66aa2c69e3e106b
4
- data.tar.gz: 35910147cf3c3710afd9f2e4751ac73da83c1631e49f53a90ca87529a5ba16ee
3
+ metadata.gz: d7b86318f6f5aa34512871e98863158c516f76fe3cc9c2c9665cb6351d9e526b
4
+ data.tar.gz: 5f90536e183860fa7d4d3e0123c1490749520c22262542c441247fd8624d8b62
5
5
  SHA512:
6
- metadata.gz: 2ed6f59846a96a1ca9aa361ea5dc807b0bf91293a8b08d3c5cac04f07c237c28ece1a0650981fd7ad0585866d94e9a43036290664e1b52306f6c9a4f70cd04b7
7
- data.tar.gz: 7c27f214d4bd94e67d01701f0f7b7c3c7881ffb2013b70030b45e8ceecfa583a79938e59049d81a7fb4078904857d895994e9ea333043ee46dd961eb3ee177d7
6
+ metadata.gz: 3be734757bff9701840f16054f7a377f20326532d8444a4c84249559c434ea214018028ad30730ffc31d2330c69b53391f349d251d04334acf3596d07fc2fb70
7
+ data.tar.gz: 2c9ed0b2af2d63ab9b1f7d2a473aac588ee526819d2791a16685fe9f813bd0d5b8f1bfb3a6b41441a288c5f756b9d9c7a032d1490f5a2a125ee1a88e520a4f6d
data/CHANGELOG.md CHANGED
@@ -1,5 +1,5 @@
1
1
  # Changelog
2
2
 
3
- ## 0.1.8
3
+ ## 0.1.9
4
4
 
5
5
  - Initial focused YouTube Ruby client for Crawlora's hosted API.
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
- # Crawlora YouTube Ruby client
1
+ # Crawlora YouTube API client for Ruby (`crawlora-youtube`)
2
2
 
3
- This gem calls the Crawlora hosted API at `https://api.crawlora.net/api/v1`. It does not call or scrape YouTube directly. Requests require your Crawlora API key and use your account's service plan.
3
+ This Ruby gem calls Crawlora's hosted YouTube API at `https://api.crawlora.net/api/v1`. It exposes 14 endpoints covering search, channels, videos, playlists, comments, captions, and transcripts; it does not call or scrape YouTube directly. Requests require your Crawlora API key and use your account's service plan.
4
4
 
5
5
  ## Install
6
6
 
@@ -15,19 +15,40 @@ require "json"
15
15
  require "crawlora/youtube"
16
16
 
17
17
  client = Crawlora::Youtube::Client.new
18
- result = client.request("youtube-channel-search", JSON.parse("{\"id\": \"sample-id\", \"q\": \"science explainers\"}"))
18
+ result = client.request("youtube-search", JSON.parse("{\"q\": \"science explainers\"}"))
19
19
  puts result
20
20
  client.close
21
21
  ```
22
22
 
23
- Use a operation-specific method for normal calls. `request(operation_id, params = {}, response_type: :auto)` is available for every operation. `response_type: :text` returns raw response text. This gem supports 14 operations.
23
+ Use an operation-specific method for normal calls. `request(operation_id, params = {}, response_type: :auto)` is available for every operation. `response_type: :text` returns raw response text. This gem includes 14 API operations.
24
24
 
25
25
  ```ruby
26
26
  client = Crawlora::Youtube::Client.new(api_key: ENV.fetch("CRAWLORA_API_KEY"), timeout: 30)
27
- # client.<operation_method>(...)
27
+ # client.<operation_method>(<endpoint parameters>)
28
28
  client.close
29
29
  ```
30
30
 
31
- Client options include `api_key`, `base_url`, and `timeout`. Ruby stdlib provides the HTTP and JSON transport.
32
-
33
- See [Crawlora](https://crawlora.net/?utm_source=rubygems&utm_medium=referral&utm_campaign=platform-clients&utm_content=youtube-ruby-homepage), the [API documentation](https://crawlora.net/docs?utm_source=rubygems&utm_medium=referral&utm_campaign=platform-clients&utm_content=youtube-ruby-api-docs), and [the package repository](https://github.com/Crawlora-org/crawlora-youtube) for account setup, the operation reference, and release history.
31
+ Client options include `api_key`, `base_url`, and `timeout`. Ruby stdlib provides the HTTP and JSON support.
32
+
33
+ ## Supported YouTube API endpoints
34
+
35
+ Browse every route included in this gem. The [endpoint reference](https://github.com/Crawlora-org/crawlora-youtube/blob/main/docs/usage.md) includes parameter and response details.
36
+
37
+ | Method | Endpoint | API operation | Description |
38
+ | --- | --- | --- | --- |
39
+ | `GET` | `/youtube/captions/{id}` | `youtube-captions` | Retrieve auto-generated or human captions |
40
+ | `GET` | `/youtube/channel/{id}/playlists` | `youtube-channel-playlists` | Retrieve the playlists tab for a YouTube channel |
41
+ | `GET` | `/youtube/channel/{id}/search` | `youtube-channel-search` | Search within a YouTube channel |
42
+ | `GET` | `/youtube/channel/{id}/shorts` | `youtube-channel-shorts` | Retrieve the shorts tab for a YouTube channel |
43
+ | `GET` | `/youtube/channel/{id}/videos` | `youtube-channel-videos` | Retrieve the videos tab for a YouTube channel |
44
+ | `GET` | `/youtube/comments/{id}` | `youtube-comments` | Retrieve video comments (top-level & replies) |
45
+ | `GET` | `/youtube/playlist/{id}` | `youtube-playlist` | Retrieve playlist metadata and items |
46
+ | `GET` | `/youtube/profile/{id}` | `youtube-profile` | Retrieve channel profile |
47
+ | `GET` | `/youtube/search` | `youtube-search` | Search YouTube |
48
+ | `GET` | `/youtube/suggest` | `youtube-suggest` | Suggest YouTube search queries |
49
+ | `GET` | `/youtube/tag/{tag}` | `youtube-tag` | Retrieve YouTube videos by tag |
50
+ | `GET` | `/youtube/transcript/{id}` | `youtube-transcript` | Retrieve transcript for a YouTube video |
51
+ | `GET` | `/youtube/transcript/{id}/languages` | `youtube-transcript-languages` | List transcript languages for a YouTube video |
52
+ | `GET` | `/youtube/video/{id}` | `youtube-video` | Retrieve video metadata & captions |
53
+
54
+ See [Crawlora](https://crawlora.net/?utm_source=rubygems&utm_medium=referral&utm_campaign=platform-clients&utm_content=youtube-ruby-homepage), the [API documentation](https://crawlora.net/docs?utm_source=rubygems&utm_medium=referral&utm_campaign=platform-clients&utm_content=youtube-ruby-api-docs), and [the package repository](https://github.com/Crawlora-org/crawlora-youtube) for account setup, the API operation reference, and release history.
@@ -19,7 +19,7 @@ module Crawlora
19
19
  end
20
20
 
21
21
  OPERATIONS = JSON.parse(<<~'JSON').freeze
22
- {"youtube-captions": {"id": "youtube-captions", "method": "GET", "params": [{"description": "YouTube video ID (11-character code)", "in": "path", "name": "id", "required": true, "type": "string", "x-example": "YbJOTdZBX1g"}, {"default": "en", "description": "Caption language code (ISO 639-1), defaults to **en**", "in": "query", "name": "lang", "type": "string"}], "path": "/youtube/captions/{id}", "pathParams": ["id"], "produces": ["application/json"], "queryParams": [{"in": "query", "name": "lang", "type": "string"}], "security": ["ApiKeyAuth"]}, "youtube-channel-playlists": {"id": "youtube-channel-playlists", "method": "GET", "params": [{"description": "Channel ID, @handle, /c path, /user path, or full YouTube channel URL", "in": "path", "name": "id", "required": true, "type": "string", "x-example": "UCXZCJLdBC09xxGZ6gcdrc6A"}, {"description": "Pagination token returned by a previous request", "in": "query", "name": "continuation_token", "type": "string"}], "path": "/youtube/channel/{id}/playlists", "pathParams": ["id"], "produces": ["application/json"], "queryParams": [{"in": "query", "name": "continuation_token", "type": "string"}], "security": ["ApiKeyAuth"]}, "youtube-channel-search": {"id": "youtube-channel-search", "method": "GET", "params": [{"description": "Channel ID, @handle, /c path, /user path, or full YouTube channel URL", "in": "path", "name": "id", "required": true, "type": "string", "x-example": "UCXZCJLdBC09xxGZ6gcdrc6A"}, {"description": "Search query", "in": "query", "name": "q", "required": true, "type": "string", "x-example": "gpt"}, {"description": "Pagination token returned by a previous request", "in": "query", "name": "continuation_token", "type": "string"}], "path": "/youtube/channel/{id}/search", "pathParams": ["id"], "produces": ["application/json"], "queryParams": [{"in": "query", "name": "q", "required": true, "type": "string"}, {"in": "query", "name": "continuation_token", "type": "string"}], "security": ["ApiKeyAuth"]}, "youtube-channel-shorts": {"id": "youtube-channel-shorts", "method": "GET", "params": [{"description": "Channel ID, @handle, /c path, /user path, or full YouTube channel URL", "in": "path", "name": "id", "required": true, "type": "string", "x-example": "UCXZCJLdBC09xxGZ6gcdrc6A"}], "path": "/youtube/channel/{id}/shorts", "pathParams": ["id"], "produces": ["application/json"], "queryParams": [], "security": ["ApiKeyAuth"]}, "youtube-channel-videos": {"id": "youtube-channel-videos", "method": "GET", "params": [{"description": "Channel ID, @handle, /c path, /user path, or full YouTube channel URL", "in": "path", "name": "id", "required": true, "type": "string", "x-example": "UCXZCJLdBC09xxGZ6gcdrc6A"}, {"description": "Pagination token returned by a previous request", "in": "query", "name": "continuation_token", "type": "string"}], "path": "/youtube/channel/{id}/videos", "pathParams": ["id"], "produces": ["application/json"], "queryParams": [{"in": "query", "name": "continuation_token", "type": "string"}], "security": ["ApiKeyAuth"]}, "youtube-comments": {"id": "youtube-comments", "method": "GET", "params": [{"description": "YouTube video ID (11-character code)", "in": "path", "name": "id", "required": true, "type": "string", "x-example": "YbJOTdZBX1g"}, {"description": "Pagination token returned by a previous request, first page if empty", "in": "query", "name": "continuation_token", "type": "string"}], "path": "/youtube/comments/{id}", "pathParams": ["id"], "produces": ["application/json"], "queryParams": [{"in": "query", "name": "continuation_token", "type": "string"}], "security": ["ApiKeyAuth"]}, "youtube-playlist": {"id": "youtube-playlist", "method": "GET", "params": [{"description": "YouTube playlist ID or full playlist URL", "in": "path", "name": "id", "required": true, "type": "string", "x-example": "PL-szjqIBRvM_aWOj-uXCVm28l-ZZFa4D2"}, {"description": "Pagination token returned by a previous request", "in": "query", "name": "continuation_token", "type": "string"}], "path": "/youtube/playlist/{id}", "pathParams": ["id"], "produces": ["application/json"], "queryParams": [{"in": "query", "name": "continuation_token", "type": "string"}], "security": ["ApiKeyAuth"]}, "youtube-profile": {"id": "youtube-profile", "method": "GET", "params": [{"description": "Channel ID, @handle, /c path, /user path, bare username, or full YouTube channel URL", "in": "path", "name": "id", "required": true, "type": "string", "x-example": "UCXZCJLdBC09xxGZ6gcdrc6A"}], "path": "/youtube/profile/{id}", "pathParams": ["id"], "produces": ["application/json"], "queryParams": [], "security": ["ApiKeyAuth"]}, "youtube-search": {"id": "youtube-search", "method": "GET", "params": [{"description": "Search query", "in": "query", "name": "q", "type": "string", "x-example": "gpt"}, {"description": "Alias for q", "in": "query", "name": "search_query", "type": "string", "x-example": "gpt"}, {"description": "Pagination token returned by a previous request", "in": "query", "name": "continuation_token", "type": "string"}, {"description": "Filter by type", "enum": ["video", "shorts", "channel", "playlist", "movie"], "in": "query", "name": "type", "type": "string"}, {"description": "Sort results", "enum": ["relevance", "upload_date", "view_count", "popularity", "rating"], "in": "query", "name": "sort_by", "type": "string"}, {"description": "Filter by upload date", "enum": ["last_hour", "today", "this_week", "this_month", "this_year"], "in": "query", "name": "upload_date", "type": "string"}, {"description": "Filter by duration; short, medium, and long preserve their previous upstream encodings", "enum": ["under_3_minutes", "three_to_20_minutes", "over_20_minutes", "under_3", "three_to_20", "over_20", "short", "medium", "long"], "in": "query", "name": "duration", "type": "string"}, {"description": "Comma-separated feature filters. Allowed values: live, 4k, hd, subtitles, cc, creative_commons, 360, vr180, 3d, hdr, location, purchased", "in": "query", "name": "features", "type": "string", "x-example": "hd,subtitles"}, {"description": "YouTube interface language", "in": "query", "name": "hl", "type": "string", "x-example": "en"}, {"description": "Two-letter YouTube region code", "in": "query", "name": "gl", "type": "string", "x-example": "US"}, {"description": "Raw protobuf-encoded search filter (base64)", "in": "query", "name": "params", "type": "string"}], "path": "/youtube/search", "pathParams": [], "produces": ["application/json"], "queryParams": [{"in": "query", "name": "q", "type": "string"}, {"in": "query", "name": "search_query", "type": "string"}, {"in": "query", "name": "continuation_token", "type": "string"}, {"enum": ["video", "shorts", "channel", "playlist", "movie"], "in": "query", "name": "type", "type": "string"}, {"enum": ["relevance", "upload_date", "view_count", "popularity", "rating"], "in": "query", "name": "sort_by", "type": "string"}, {"enum": ["last_hour", "today", "this_week", "this_month", "this_year"], "in": "query", "name": "upload_date", "type": "string"}, {"enum": ["under_3_minutes", "three_to_20_minutes", "over_20_minutes", "under_3", "three_to_20", "over_20", "short", "medium", "long"], "in": "query", "name": "duration", "type": "string"}, {"in": "query", "name": "features", "type": "string"}, {"in": "query", "name": "hl", "type": "string"}, {"in": "query", "name": "gl", "type": "string"}, {"in": "query", "name": "params", "type": "string"}], "security": ["ApiKeyAuth"]}, "youtube-suggest": {"id": "youtube-suggest", "method": "GET", "params": [{"description": "Search query prefix", "in": "query", "name": "q", "required": true, "type": "string", "x-example": "openai"}, {"description": "Suggestions to return; defaults to 10, clamped to 1..20", "in": "query", "maximum": 20, "minimum": 1, "name": "count", "type": "integer", "x-example": 10}, {"description": "YouTube interface language, such as en, de, or pt-BR; defaults to en", "in": "query", "name": "hl", "type": "string", "x-example": "en"}, {"description": "Two-letter YouTube region code; defaults to US", "in": "query", "name": "gl", "type": "string", "x-example": "US"}], "path": "/youtube/suggest", "pathParams": [], "produces": ["application/json"], "queryParams": [{"in": "query", "name": "q", "required": true, "type": "string"}, {"in": "query", "name": "count", "type": "integer"}, {"in": "query", "name": "hl", "type": "string"}, {"in": "query", "name": "gl", "type": "string"}], "security": ["ApiKeyAuth"]}, "youtube-tag": {"id": "youtube-tag", "method": "GET", "params": [{"description": "Tag to filter videos", "in": "path", "name": "tag", "required": true, "type": "string", "x-example": "openai"}, {"default": "all", "description": "Result tab to load", "enum": ["all", "shorts"], "in": "query", "name": "type", "type": "string"}, {"description": "Continuation token for pagination, first page if empty", "in": "query", "name": "continuation_token", "type": "string"}], "path": "/youtube/tag/{tag}", "pathParams": ["tag"], "produces": ["application/json"], "queryParams": [{"enum": ["all", "shorts"], "in": "query", "name": "type", "type": "string"}, {"in": "query", "name": "continuation_token", "type": "string"}], "security": ["ApiKeyAuth"]}, "youtube-transcript": {"id": "youtube-transcript", "method": "GET", "params": [{"description": "YouTube video ID (11-character code)", "in": "path", "name": "id", "required": true, "type": "string", "x-example": "YbJOTdZBX1g"}, {"default": "en", "description": "Preferred transcript language", "in": "query", "name": "lang", "type": "string"}, {"description": "Translate transcript to this language code", "in": "query", "name": "translate_to", "type": "string"}, {"default": "json", "description": "Response format", "enum": ["json", "text", "srt", "vtt"], "in": "query", "name": "format", "type": "string"}, {"default": true, "description": "Include timestamps in the JSON response", "in": "query", "name": "timestamps", "type": "boolean"}], "path": "/youtube/transcript/{id}", "pathParams": ["id"], "produces": ["application/json", "text/plain"], "queryParams": [{"in": "query", "name": "lang", "type": "string"}, {"in": "query", "name": "translate_to", "type": "string"}, {"enum": ["json", "text", "srt", "vtt"], "in": "query", "name": "format", "type": "string"}, {"in": "query", "name": "timestamps", "type": "boolean"}], "security": ["ApiKeyAuth"]}, "youtube-transcript-languages": {"id": "youtube-transcript-languages", "method": "GET", "params": [{"description": "YouTube video ID (11-character code)", "in": "path", "name": "id", "required": true, "type": "string", "x-example": "YbJOTdZBX1g"}], "path": "/youtube/transcript/{id}/languages", "pathParams": ["id"], "produces": ["application/json"], "queryParams": [], "security": ["ApiKeyAuth"]}, "youtube-video": {"id": "youtube-video", "method": "GET", "params": [{"description": "YouTube video ID (11-char code)", "in": "path", "name": "id", "required": true, "type": "string", "x-example": "YbJOTdZBX1g"}], "path": "/youtube/video/{id}", "pathParams": ["id"], "produces": ["application/json"], "queryParams": [], "security": ["ApiKeyAuth"]}}
22
+ {"youtube-captions": {"bodyParam": null, "bodyRequired": false, "id": "youtube-captions", "method": "GET", "params": [{"description": "YouTube video ID (11-character code)", "in": "path", "name": "id", "required": true, "type": "string", "x-example": "YbJOTdZBX1g"}, {"default": "en", "description": "Caption language code (ISO 639-1), defaults to **en**", "in": "query", "name": "lang", "type": "string"}], "path": "/youtube/captions/{id}", "pathParams": ["id"], "produces": ["application/json"], "queryParams": [{"in": "query", "name": "lang", "type": "string"}], "security": ["ApiKeyAuth"]}, "youtube-channel-playlists": {"bodyParam": null, "bodyRequired": false, "id": "youtube-channel-playlists", "method": "GET", "params": [{"description": "Channel ID, @handle, /c path, /user path, or full YouTube channel URL", "in": "path", "name": "id", "required": true, "type": "string", "x-example": "UCXZCJLdBC09xxGZ6gcdrc6A"}, {"description": "Pagination token returned by a previous request", "in": "query", "name": "continuation_token", "type": "string"}], "path": "/youtube/channel/{id}/playlists", "pathParams": ["id"], "produces": ["application/json"], "queryParams": [{"in": "query", "name": "continuation_token", "type": "string"}], "security": ["ApiKeyAuth"]}, "youtube-channel-search": {"bodyParam": null, "bodyRequired": false, "id": "youtube-channel-search", "method": "GET", "params": [{"description": "Channel ID, @handle, /c path, /user path, or full YouTube channel URL", "in": "path", "name": "id", "required": true, "type": "string", "x-example": "UCXZCJLdBC09xxGZ6gcdrc6A"}, {"description": "Search query", "in": "query", "name": "q", "required": true, "type": "string", "x-example": "gpt"}, {"description": "Pagination token returned by a previous request", "in": "query", "name": "continuation_token", "type": "string"}], "path": "/youtube/channel/{id}/search", "pathParams": ["id"], "produces": ["application/json"], "queryParams": [{"in": "query", "name": "q", "required": true, "type": "string"}, {"in": "query", "name": "continuation_token", "type": "string"}], "security": ["ApiKeyAuth"]}, "youtube-channel-shorts": {"bodyParam": null, "bodyRequired": false, "id": "youtube-channel-shorts", "method": "GET", "params": [{"description": "Channel ID, @handle, /c path, /user path, or full YouTube channel URL", "in": "path", "name": "id", "required": true, "type": "string", "x-example": "UCXZCJLdBC09xxGZ6gcdrc6A"}], "path": "/youtube/channel/{id}/shorts", "pathParams": ["id"], "produces": ["application/json"], "queryParams": [], "security": ["ApiKeyAuth"]}, "youtube-channel-videos": {"bodyParam": null, "bodyRequired": false, "id": "youtube-channel-videos", "method": "GET", "params": [{"description": "Channel ID, @handle, /c path, /user path, or full YouTube channel URL", "in": "path", "name": "id", "required": true, "type": "string", "x-example": "UCXZCJLdBC09xxGZ6gcdrc6A"}, {"description": "Pagination token returned by a previous request", "in": "query", "name": "continuation_token", "type": "string"}], "path": "/youtube/channel/{id}/videos", "pathParams": ["id"], "produces": ["application/json"], "queryParams": [{"in": "query", "name": "continuation_token", "type": "string"}], "security": ["ApiKeyAuth"]}, "youtube-comments": {"bodyParam": null, "bodyRequired": false, "id": "youtube-comments", "method": "GET", "params": [{"description": "YouTube video ID (11-character code)", "in": "path", "name": "id", "required": true, "type": "string", "x-example": "YbJOTdZBX1g"}, {"description": "Pagination token returned by a previous request, first page if empty", "in": "query", "name": "continuation_token", "type": "string"}], "path": "/youtube/comments/{id}", "pathParams": ["id"], "produces": ["application/json"], "queryParams": [{"in": "query", "name": "continuation_token", "type": "string"}], "security": ["ApiKeyAuth"]}, "youtube-playlist": {"bodyParam": null, "bodyRequired": false, "id": "youtube-playlist", "method": "GET", "params": [{"description": "YouTube playlist ID or full playlist URL", "in": "path", "name": "id", "required": true, "type": "string", "x-example": "PL-szjqIBRvM_aWOj-uXCVm28l-ZZFa4D2"}, {"description": "Pagination token returned by a previous request", "in": "query", "name": "continuation_token", "type": "string"}], "path": "/youtube/playlist/{id}", "pathParams": ["id"], "produces": ["application/json"], "queryParams": [{"in": "query", "name": "continuation_token", "type": "string"}], "security": ["ApiKeyAuth"]}, "youtube-profile": {"bodyParam": null, "bodyRequired": false, "id": "youtube-profile", "method": "GET", "params": [{"description": "Channel ID, @handle, /c path, /user path, bare username, or full YouTube channel URL", "in": "path", "name": "id", "required": true, "type": "string", "x-example": "UCXZCJLdBC09xxGZ6gcdrc6A"}], "path": "/youtube/profile/{id}", "pathParams": ["id"], "produces": ["application/json"], "queryParams": [], "security": ["ApiKeyAuth"]}, "youtube-search": {"bodyParam": null, "bodyRequired": false, "id": "youtube-search", "method": "GET", "params": [{"description": "Search query", "in": "query", "name": "q", "type": "string", "x-example": "gpt"}, {"description": "Alias for q", "in": "query", "name": "search_query", "type": "string", "x-example": "gpt"}, {"description": "Pagination token returned by a previous request", "in": "query", "name": "continuation_token", "type": "string"}, {"description": "Filter by type", "enum": ["video", "shorts", "channel", "playlist", "movie"], "in": "query", "name": "type", "type": "string"}, {"description": "Sort results", "enum": ["relevance", "upload_date", "view_count", "popularity", "rating"], "in": "query", "name": "sort_by", "type": "string"}, {"description": "Filter by upload date", "enum": ["last_hour", "today", "this_week", "this_month", "this_year"], "in": "query", "name": "upload_date", "type": "string"}, {"description": "Filter by duration; short, medium, and long preserve their previous upstream encodings", "enum": ["under_3_minutes", "three_to_20_minutes", "over_20_minutes", "under_3", "three_to_20", "over_20", "short", "medium", "long"], "in": "query", "name": "duration", "type": "string"}, {"description": "Comma-separated feature filters. Allowed values: live, 4k, hd, subtitles, cc, creative_commons, 360, vr180, 3d, hdr, location, purchased", "in": "query", "name": "features", "type": "string", "x-example": "hd,subtitles"}, {"description": "YouTube interface language", "in": "query", "name": "hl", "type": "string", "x-example": "en"}, {"description": "Two-letter YouTube region code", "in": "query", "name": "gl", "type": "string", "x-example": "US"}, {"description": "Raw protobuf-encoded search filter (base64)", "in": "query", "name": "params", "type": "string"}], "path": "/youtube/search", "pathParams": [], "produces": ["application/json"], "queryParams": [{"in": "query", "name": "q", "type": "string"}, {"in": "query", "name": "search_query", "type": "string"}, {"in": "query", "name": "continuation_token", "type": "string"}, {"enum": ["video", "shorts", "channel", "playlist", "movie"], "in": "query", "name": "type", "type": "string"}, {"enum": ["relevance", "upload_date", "view_count", "popularity", "rating"], "in": "query", "name": "sort_by", "type": "string"}, {"enum": ["last_hour", "today", "this_week", "this_month", "this_year"], "in": "query", "name": "upload_date", "type": "string"}, {"enum": ["under_3_minutes", "three_to_20_minutes", "over_20_minutes", "under_3", "three_to_20", "over_20", "short", "medium", "long"], "in": "query", "name": "duration", "type": "string"}, {"in": "query", "name": "features", "type": "string"}, {"in": "query", "name": "hl", "type": "string"}, {"in": "query", "name": "gl", "type": "string"}, {"in": "query", "name": "params", "type": "string"}], "security": ["ApiKeyAuth"]}, "youtube-suggest": {"bodyParam": null, "bodyRequired": false, "id": "youtube-suggest", "method": "GET", "params": [{"description": "Search query prefix", "in": "query", "name": "q", "required": true, "type": "string", "x-example": "openai"}, {"description": "Suggestions to return; defaults to 10, clamped to 1..20", "in": "query", "maximum": 20, "minimum": 1, "name": "count", "type": "integer", "x-example": 10}, {"description": "YouTube interface language, such as en, de, or pt-BR; defaults to en", "in": "query", "name": "hl", "type": "string", "x-example": "en"}, {"description": "Two-letter YouTube region code; defaults to US", "in": "query", "name": "gl", "type": "string", "x-example": "US"}], "path": "/youtube/suggest", "pathParams": [], "produces": ["application/json"], "queryParams": [{"in": "query", "name": "q", "required": true, "type": "string"}, {"in": "query", "name": "count", "type": "integer"}, {"in": "query", "name": "hl", "type": "string"}, {"in": "query", "name": "gl", "type": "string"}], "security": ["ApiKeyAuth"]}, "youtube-tag": {"bodyParam": null, "bodyRequired": false, "id": "youtube-tag", "method": "GET", "params": [{"description": "Tag to filter videos", "in": "path", "name": "tag", "required": true, "type": "string", "x-example": "openai"}, {"default": "all", "description": "Result tab to load", "enum": ["all", "shorts"], "in": "query", "name": "type", "type": "string"}, {"description": "Continuation token for pagination, first page if empty", "in": "query", "name": "continuation_token", "type": "string"}], "path": "/youtube/tag/{tag}", "pathParams": ["tag"], "produces": ["application/json"], "queryParams": [{"enum": ["all", "shorts"], "in": "query", "name": "type", "type": "string"}, {"in": "query", "name": "continuation_token", "type": "string"}], "security": ["ApiKeyAuth"]}, "youtube-transcript": {"bodyParam": null, "bodyRequired": false, "id": "youtube-transcript", "method": "GET", "params": [{"description": "YouTube video ID (11-character code)", "in": "path", "name": "id", "required": true, "type": "string", "x-example": "YbJOTdZBX1g"}, {"default": "en", "description": "Preferred transcript language", "in": "query", "name": "lang", "type": "string"}, {"description": "Translate transcript to this language code", "in": "query", "name": "translate_to", "type": "string"}, {"default": "json", "description": "Response format", "enum": ["json", "text", "srt", "vtt"], "in": "query", "name": "format", "type": "string"}, {"default": true, "description": "Include timestamps in the JSON response", "in": "query", "name": "timestamps", "type": "boolean"}], "path": "/youtube/transcript/{id}", "pathParams": ["id"], "produces": ["application/json", "text/plain"], "queryParams": [{"in": "query", "name": "lang", "type": "string"}, {"in": "query", "name": "translate_to", "type": "string"}, {"enum": ["json", "text", "srt", "vtt"], "in": "query", "name": "format", "type": "string"}, {"in": "query", "name": "timestamps", "type": "boolean"}], "security": ["ApiKeyAuth"]}, "youtube-transcript-languages": {"bodyParam": null, "bodyRequired": false, "id": "youtube-transcript-languages", "method": "GET", "params": [{"description": "YouTube video ID (11-character code)", "in": "path", "name": "id", "required": true, "type": "string", "x-example": "YbJOTdZBX1g"}], "path": "/youtube/transcript/{id}/languages", "pathParams": ["id"], "produces": ["application/json"], "queryParams": [], "security": ["ApiKeyAuth"]}, "youtube-video": {"bodyParam": null, "bodyRequired": false, "id": "youtube-video", "method": "GET", "params": [{"description": "YouTube video ID (11-char code)", "in": "path", "name": "id", "required": true, "type": "string", "x-example": "YbJOTdZBX1g"}], "path": "/youtube/video/{id}", "pathParams": ["id"], "produces": ["application/json"], "queryParams": [], "security": ["ApiKeyAuth"]}}
23
23
  JSON
24
24
  OPERATION_IDS = JSON.parse(<<~'JSON').freeze
25
25
  ["youtube-captions", "youtube-channel-playlists", "youtube-channel-search", "youtube-channel-shorts", "youtube-channel-videos", "youtube-comments", "youtube-playlist", "youtube-profile", "youtube-search", "youtube-suggest", "youtube-tag", "youtube-transcript", "youtube-transcript-languages", "youtube-video"]
@@ -29,7 +29,7 @@ module Crawlora
29
29
  class Client
30
30
  attr_reader :base_url
31
31
 
32
- def initialize(api_key: ENV["CRAWLORA_API_KEY"], base_url: "https://api.crawlora.net/api/v1", timeout: 30, user_agent: "crawlora-youtube-ruby/0.1.8", transport: nil)
32
+ def initialize(api_key: ENV["CRAWLORA_API_KEY"], base_url: "https://api.crawlora.net/api/v1", timeout: 30, user_agent: "crawlora-youtube-ruby/0.1.9", transport: nil)
33
33
  @api_key = api_key
34
34
  @base_url = base_url.to_s.sub(%r{/+$}, "")
35
35
  @timeout = Float(timeout)
@@ -45,15 +45,24 @@ module Crawlora
45
45
  raise Errors::ClientError.new("unknown operation: #{operation_id}", operation_id: operation_id) unless operation
46
46
  raise Errors::ClientError.new("Crawlora API key is required", operation_id: operation_id) if @api_key.nil? || @api_key.to_s.empty?
47
47
  normalized = params.each_with_object({}) { |(key, value), out| out[key.to_s] = value }
48
+ payload = request_body(operation, normalized)
48
49
  url = build_url(operation, normalized)
49
50
  uri = URI.parse(url)
50
- request = Net::HTTP::Get.new(uri)
51
+ request = operation["method"] == "POST" ? Net::HTTP::Post.new(uri) : Net::HTTP::Get.new(uri)
52
+ unless payload.nil?
53
+ request.body = payload
54
+ request["Content-Type"] = "application/json"
55
+ end
51
56
  request["x-api-key"] = @api_key
52
57
  request["User-Agent"] = @user_agent
53
58
  request["Accept"] = operation["produces"].include?("text/plain") ? "application/json, text/plain" : "application/json"
54
59
  begin
55
60
  if @transport
56
- response = @transport.call(url, request.to_hash, @timeout)
61
+ response = if operation["method"] == "POST"
62
+ @transport.call(url, request.to_hash, @timeout, "POST", payload)
63
+ else
64
+ @transport.call(url, request.to_hash, @timeout)
65
+ end
57
66
  status = Integer(response.fetch(:status) { response.fetch("status") })
58
67
  body = response.fetch(:body) { response.fetch("body", "") }
59
68
  headers = response.fetch(:headers) { response.fetch("headers", {}) }
@@ -164,8 +173,21 @@ module Crawlora
164
173
 
165
174
  private
166
175
 
176
+ # POST operations take their JSON body under the body parameter name or "body".
177
+ def request_body(operation, params)
178
+ name = operation["bodyParam"]
179
+ return nil if name.nil?
180
+ value = params.key?(name) ? params[name] : params["body"]
181
+ if value.nil?
182
+ raise Errors::ClientError.new("missing request body: #{name}", operation_id: operation["id"]) if operation["bodyRequired"]
183
+ return nil
184
+ end
185
+ value.is_a?(String) ? value : JSON.generate(value)
186
+ end
187
+
167
188
  def build_url(operation, params)
168
189
  known = operation["params"].map { |param| param["name"] }
190
+ known << "body" if operation["bodyParam"]
169
191
  unknown = params.keys - known
170
192
  raise Errors::ClientError.new("unknown parameters: #{unknown.join(', ')}", operation_id: operation["id"]) unless unknown.empty?
171
193
  path = operation["path"].dup
@@ -1,5 +1,5 @@
1
1
  module Crawlora
2
2
  module Youtube
3
- VERSION = "0.1.8"
3
+ VERSION = "0.1.9"
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,16 +1,17 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: crawlora-youtube
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.8
4
+ version: 0.1.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Crawlora
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-10-09 00:00:00.000000000 Z
11
+ date: 2026-10-12 00:00:00.000000000 Z
12
12
  dependencies: []
13
- description: Credential-free YouTube API access through Crawlora's hosted service.
13
+ description: Ruby client for Crawlora's hosted YouTube API. Covers search, channels,
14
+ videos, playlists, comments, captions, and transcripts across 14 endpoints.
14
15
  email:
15
16
  executables: []
16
17
  extensions: []
@@ -47,5 +48,5 @@ requirements: []
47
48
  rubygems_version: 3.4.19
48
49
  signing_key:
49
50
  specification_version: 4
50
- summary: YouTube client for the Crawlora hosted API
51
+ summary: YouTube API client for Crawlora (14 endpoints)
51
52
  test_files: []