From 067ff3d1da4b7a3ed4179ccb87c9e019095be858 Mon Sep 17 00:00:00 2001 From: Simon Date: Fri, 3 Apr 2026 17:20:11 +0000 Subject: [PATCH] docs --- docs/hottubapp/đŸŽŦ Videos - Hot Tub Docs.html | 2040 +++++++++++++++ .../👤 Uploaders - Hot Tub Docs.html | 1761 +++++++++++++ docs/hottubapp/📡 Status - Hot Tub Docs.html | 2231 +++++++++++++++++ prompts/new-channel.md | 1 + 4 files changed, 6033 insertions(+) create mode 100644 docs/hottubapp/đŸŽŦ Videos - Hot Tub Docs.html create mode 100644 docs/hottubapp/👤 Uploaders - Hot Tub Docs.html create mode 100644 docs/hottubapp/📡 Status - Hot Tub Docs.html create mode 100644 prompts/new-channel.md diff --git a/docs/hottubapp/đŸŽŦ Videos - Hot Tub Docs.html b/docs/hottubapp/đŸŽŦ Videos - Hot Tub Docs.html new file mode 100644 index 0000000..d1e560f --- /dev/null +++ b/docs/hottubapp/đŸŽŦ Videos - Hot Tub Docs.html @@ -0,0 +1,2040 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + đŸŽŦ Videos - Hot Tub Docs + + + + + + + + + +
+ + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + +
+
+
+ + + +
+ +
+ + + +
+
+ + + + + +

đŸŽŦ Videos

+ +

Overview

+

The POST /api/videos endpoint is used to retrieve videos + from the given source. It contains information about the videos, +channels, and other relevant information.

+

Request

+

POST /api/videos

+
{
+  "query": "kittens",
+  "channel": "my-channel",
+  "channels": ["my-channel", "another-channel"], // Channels supporting multiple sites/channels
+  "sort": "new",
+  "page": 1,
+  "pageSize": 40,
+  "clientVersion": "2.2.7-38", // {version}—{build}, useful for feature availability 
+  "blockedKeywords": ["example"],
+  "blockedUploaders": ["uploader-id-123"],
+  // Your server's custom parameters will be added here
+  "quality": "hd",
+  "duration": "short",
+  "flavor": "mint_chocolate_chip"
+}
+
+

Request Parameters

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FieldTypeDefaultDescription
querystring""Search query
channelstring—Single channel identifier (as defined in your server's status response)
channelsstring[]—Multiple channel identifiers to search across (overrides channel)
sortstring"relevance"Sort option ID (as defined in your server's status response options)
pagenumber1Page number for pagination
pageSizenumber40Results per page
clientVersionstring—App version string (e.g., "2.2.7-38")
blockedKeywordsstring[][]Keywords to exclude from results
blockedUploadersstring[][]Uploader IDs to exclude from results
(server options)any—Any option IDs defined in your server's status response are forwarded as-is
+
+

Server-Defined Filter Options

+

Filter parameters beyond the core ones above (e.g., duration, +quality, content type, sexuality) are entirely defined by your server +via the options array in the Status endpoint response. Whatever id values you expose there will be sent back verbatim when the user selects them. Your /api/videos handler receives them as top-level keys in the request body.

+
+
+

Response

+
{
+  "pageInfo": {
+    "hasNextPage": true,
+    "recommendations": ["cats", "pets", "animals"],
+    "error": null, // e.g. "No results were found"
+    "message": null, // e.g. "Showing cached results" or "Some content filtered based on preferences"
+    "parameters": {
+      "totalResults": 1000
+    }
+  },
+  "items": [
+    {
+      "id": "c85017ca87477168d648727753c4ded8a35f173e22ef93743e707b296becb299",
+      "title": "20 Minutes of Adorable Kittens   BEST Compilation",
+      "url": "https://www.example.com/watch?v=y0sF5xhGreA",
+      "duration": 110,
+      "channel": "my-channel",
+      "thumb": "https://i.example.com/vi/y0sF5xhGreA/hqdefault.jpg",
+      "views": 14622653,
+      "rating": 95,
+      "uploader": "Awesome Vids",
+      "uploaderUrl": "https://www.example.com/@awesomeVids",
+      "uploaderId": "awesomeVids",
+      "verified": false,
+      "isVR": false,
+      "tags": ["cats", "kittens", "pets"],
+      "categories": ["Animals", "Entertainment"],
+      "uploadedAt": "2025-01-03T10:15:54.000Z",
+      "preview": "https://example.com/preview.mp4",
+      "uploaderId": "petcollective",
+      "aspectRatio": 1.78,
+      "uploaderProfile": {
+        "id": "abc123",
+        "name": "Awesome Vids",
+        "avatar": "https://example.com/avatar.jpg",
+        "videoCount": 342,
+        "totalViews": 14500000
+      },
+      "formats": [
+        {
+          "url": "https://www.example.com/watch?v=y0sF5xhGreA",
+          "quality": 1080,
+          "format": "mp4",
+          "formatId": "mp4-1080",
+          "protocol": "m3u8_native",
+          "ext": "mp4",
+          "httpHeaders": {
+            "Referer": "https://www.example.com"
+          }
+        }
+      ],
+      "embed": {
+        "source": "https://www.example.com/watch?v=y0sF5xhGreA",
+        "html": "<iframe src='...' width='560' height='315'></iframe>",
+        "width": 560,
+        "height": 315
+      }
+    }
+  ]
+}
+
+

Field Reference

+

Response Root Fields

+
+ + + + + + + + + + + + + + + + + + + +
FieldTypeDescription
pageInfoPageInfoPagination and status metadata
itemsVideo[]Array of video objects
+

PageInfo Object Fields

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FieldTypeRequiredDescription
hasNextPageboolean✅Whether more results are available
recommendationsstring[]âšĒSuggested search terms
errorstringâšĒError message (e.g., "No results were found")
messagestringâšĒInformational message (e.g., "Showing cached results")
parametersobjectâšĒAdditional metadata (e.g. "cursor": "abc123")
+
+

Error vs Message

+
    +
  • error: Use for actual errors that prevent normal operation (no results, API failure, invalid query)
  • +
  • message: Use for informational notices + that don't indicate failure (cached results, filtered content, rate +limiting info, feature announcements)
  • +
+
+

Video Object Fields

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FieldTypeRequiredDescription
titlestring✅Video title
urlstring✅Video source URL
durationnumber✅Video length in seconds
channelstring✅Source channel/platform identifier
thumbstring✅Thumbnail image URL
idstringâšĒUnique video identifier (auto-generated if not provided)
networkstring—Alias for channel — added by the server on every response item
isVRboolean—true when VR content is detected from tags — added by the server
uploaderProfileUploaderProfile—Partial uploader profile card — added by the server when a profile exists
viewsnumberâšĒView count
ratingnumberâšĒVideo percent rating score
uploaderstringâšĒContent creator name
uploaderUrlstringâšĒCreator's profile URL
uploaderIdstringâšĒUnique uploader identifier
tagsstring[]âšĒArray of content tags (ordering is up to your server)
categoriesstring[]âšĒArray of content categories
uploadedAtDate|StringâšĒUpload date (flexible format)
previewstringâšĒPreview GIF/video URL
formatsFormat[]âšĒVideo format array
aspectRationumberâšĒVideo aspect ratio (e.g., 1.78 for 16:9)
embedEmbedDataâšĒEmbed data for Reddit-style content
isLivebooleanâšĒtrue when the video is a live stream — replaces duration with a "LIVE" badge
liveStatusstringâšĒyt-dlp live status: "live", "not_live", "was_live", "post_live" — used alongside isLive for badge and availability UI
availabilitystringâšĒContent availability hint — "offline" shows an unavailable badge instead of duration
+
+

Live & Availability Display

+

The app uses isLive and liveStatus together to determine how a video card renders:

+
    +
  • Live badge shown when isLive == true or liveStatus == "live" — the duration label is hidden and replaced with a "LIVE" indicator.
  • +
  • Unavailable badge shown when availability == "offline", or liveStatus is "not_live" or "was_live" — again replacing the duration.
  • +
  • Normal duration shown otherwise.
  • +
+

During playback, the player also detects live streams from the HLS +stream itself (indefinite duration) and adjusts transport controls +accordingly — so isLive is not required for playback to behave correctly, but it ensures the video card UI is accurate before the stream is loaded.

+
+

UploaderProfile Object

+

Attached to each video item when a known uploader profile exists. +Used to render the uploader row on video cards without a separate API +call.

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FieldTypeDescription
idstringUploader identifier
namestringDisplay name
avatarstring \| nullAvatar image URL (canonical)
videoCountnumberTotal indexed videos
totalViewsnumberAggregated view count
+

See JSON key names for camelCase vs snake_case and the legacy profile_picture_url avatar key.

+

Format Object Fields

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FieldTypeRequiredDescription
urlstring✅Direct video stream URL
formatIdstringAuto-generatedFormat identifier (provided or auto-generated)
extstringâšĒFile extension ("mp4", "webm")
protocolstringâšĒStream protocol ("https", "m3u8_native") - important for playback
httpHeadersobjectâšĒ (🔑 CDN)Required headers for CDN authentication
heightnumberâšĒ (📱 UX)Video height in pixels (240, 480, 720, 1080) - recommended for better format IDs
widthnumberâšĒVideo width in pixels
resolutionstringâšĒResolution string ("720p" or "1280x720")
formatstringâšĒFormat description
fpsnumberâšĒFrames per second
qualitynumberâšĒyt-dlp quality score (fallback for formatId generation)
vcodecstringâšĒVideo codec ("avc1.640020", "h264")
acodecstringâšĒAudio codec ("mp4a.40.2", "aac")
tbrnumberâšĒTotal bitrate
abrnumberâšĒAudio bitrate
vbrnumberâšĒVideo bitrate
dynamicRangestringâšĒDynamic range ("SDR", "HDR")
aspectRationumberâšĒVideo aspect ratio
filesizenumberâšĒFile size in bytes
languagestringâšĒAudio language
containerstringâšĒContainer format
downloaderOptionsobjectâšĒDownload configuration (defaults to 1MB chunks)
+
+

CDN Authentication

+

Many premium/CDN streams require httpHeaders for authentication. Common headers include:

+
    +
  • User-Agent - Required by most CDNs
  • +
  • Referer - Required for hotlink protection
  • +
  • Accept, Accept-Language - For content negotiation
  • +
+
+
+

Format ID Generation

+

If formatId is not provided, it's auto-generated in this order:

+
    +
  1. {format}_{height}p (e.g., "mp4_720p")
  2. +
  3. {format}_{resolution} (e.g., "mp4_1280x720")
  4. +
  5. {format}_{quality} (e.g., "mp4_2.5")
  6. +
  7. {format} only
  8. +
+
+
+

Best Practices

+
    +
  • Only url is truly required - everything else is optional
  • +
  • Include height for meaningful format IDs and better user experience
  • +
  • Add httpHeaders for CDN streams that need authentication
  • +
  • Providing formats bypasses yt-dlp extraction (faster response)
  • +
  • Providing preview avoids on-device preview generation
  • +
+
+ + + + + + + + + + + + + +
+
+ + Was this page helpful? + +
+
+ + + + + +
+
+ + + + + +
+
+
+
+ + + +
+
+ + + +
+ + + +
+ + + + +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/hottubapp/👤 Uploaders - Hot Tub Docs.html b/docs/hottubapp/👤 Uploaders - Hot Tub Docs.html new file mode 100644 index 0000000..bb173af --- /dev/null +++ b/docs/hottubapp/👤 Uploaders - Hot Tub Docs.html @@ -0,0 +1,1761 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 👤 Uploaders - Hot Tub Docs + + + + + + + + + +
+ + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + +

👤 Uploaders

+ +

Overview

+

The POST /api/uploaders endpoint returns profile details + for a content creator or studio. The app calls this when a user taps an + uploader name to view their profile page.

+

Request

+

POST /api/uploaders

+

At least one of uploaderId or uploaderName must be provided.

+
{
+  "uploaderId": "abc123",
+  "profileContent": true,
+  "query": "kittens"
+}
+
+

Request Parameters

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FieldTypeRequiredDescription
uploaderIdstringâš ī¸ One ofInternal uploader ID
uploaderNamestringâš ī¸ One ofUploader display name
querystringâšĒCurrent search query used to personalize "For You" content ranking
+
+

Response

+
{
+  "id": "awesomeVids",
+  "name": "Awesome Vids",
+  "url": "https://www.example.com/@awesomeVids",
+  "channel": "my-channel",
+  "verified": true,
+  "videoCount": 342,
+  "totalViews": 14500000,
+  "channels": [
+    {
+      "channel": "my-channel",
+      "videoCount": 342,
+      "firstSeenAt": "2024-01-15T00:00:00",
+      "lastSeenAt": "2025-03-01T00:00:00"
+    }
+  ],
+  "avatar": "https://example.com/avatar.jpg",
+  "description": "The best videos on the internet.",
+  "bio": "Videos are awesome.",
+  "videos": [
+    {
+      "id": "c85017ca87477168d648727753c4ded8",
+      "url": "https://www.example.com/watch?v=y0sF5xhGreA",
+      "title": "20 Minutes of Adorable Kittens",
+      "duration": 1200,
+      "channel": "my-channel",
+      "uploadedAt": "2025-01-03T10:15:54.000Z",
+      "uploader": "The Pet Collective",
+      "uploaderId": "abc123",
+      "thumb": "https://i.example.com/vi/y0sF5xhGreA/hqdefault.jpg",
+      "preview": null,
+      "views": 14622653,
+      "rating": 95,
+      "aspectRatio": 1.78
+    }
+  ],
+  "tapes": [],
+  "playlists": [],
+  "layout": [
+    { "type": "horizontal", "title": "For You", "videoIds": ["c85017ca87477168d648727753c4ded8"] },
+    { "type": "videos", "title": null }
+  ]
+}
+
+
+

Field Reference

+

Uploader Object

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FieldTypeDescription
idstringUnique uploader identifier
namestringDisplay name
urlstring \| nullProfile URL
channelstring \| nullPrimary source channel
verifiedbooleanWhether the uploader is verified
videoCountnumberTotal indexed videos
totalViewsnumberAggregated view count
channelsChannelStat[] \| nullPer-channel video stats
avatarstring \| nullAvatar image URL (canonical)
descriptionstring \| nullShort description
biostring \| nullLong-form biography
videosVideoRef[] \| nullVideos — present when profileContent is true
tapesany[] \| nullShort-form content — present when profileContent is true
playlistsany[] \| nullPlaylists — present when profileContent is true
layoutLayoutRow[] \| nullOrdered profile page sections
+

See JSON key names for camelCase vs snake_case and the legacy profile_picture_url avatar key.

+
+

ChannelStat Object

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FieldTypeDescription
channelstringSource channel identifier
videoCountnumberVideos seen on this channel
firstSeenAtstring \| nullISO timestamp of first appearance on this channel
lastSeenAtstring \| nullISO timestamp of last appearance on this channel
+
+

VideoRef Object

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FieldTypeDescription
idstringVideo identifier
urlstringVideo source URL
titlestringVideo title
durationnumberDuration in seconds
channelstringSource channel identifier
uploadedAtstring \| nullISO upload timestamp
uploaderstringUploader display name
uploaderIdstringUploader identifier
thumbstringThumbnail URL
previewstring \| nullPreview clip URL
viewsnumberView count
ratingnumberPercent rating score
aspectRationumber \| nullAspect ratio (e.g., 1.78 for 16:9)
+
+

LayoutRow Object

+

Describes the ordered sections rendered on the profile page.

+
+ + + + + + + + + + + + + + + + + + + + + + + + +
FieldTypeDescription
typestring"videos" (vertical list) or horizontal (carousel). Legacy horizontal_videos is accepted.
titlestring \| nullSection heading
videoIdsstring[]Ordered video IDs — only for horizontal / legacy horizontal_videos. Snake_case video_ids is accepted when decoding.
+
+

Error Responses

+
+ + + + + + + + + + + + + + + + + + + + +
StatusCondition
400Neither uploaderId nor uploaderName given
404No uploader matched the provided identifier
500Internal server error
+ + + + + + + + + + + + + +
+
+ + Was this page helpful? + +
+
+ + + + + +
+
+ + + + + +
+
+
+
+ + + +
+
+ + + +
+ + + +
+ + + + +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/hottubapp/📡 Status - Hot Tub Docs.html b/docs/hottubapp/📡 Status - Hot Tub Docs.html new file mode 100644 index 0000000..2b5669d --- /dev/null +++ b/docs/hottubapp/📡 Status - Hot Tub Docs.html @@ -0,0 +1,2231 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 📡 Status - Hot Tub Docs + + + + + + + + + +
+ + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + +
+
+
+ + + + + + + +
+
+ + + + + +

📡 Status

+ +

Overview

+

The POST /api/status endpoint is used to retrieve the +status of the given source. It contains information about the source, +channels, and other relevant information.

+

Request

+

POST /api/status

+
{
+  "clientVersion": "2.1.2",
+  // Your server's global options can be sent in the status request
+  "sort": "new",
+  "flavor": "mint chocolate chip"
+}
+
+
+

Server Options

+

Your server's options can be used to customize the experience for the user. +For example, you may return different categories, channels, or notices based on their preferences.

+
+

Response

+
{
+  "id": "example-server",
+  "name": "Example Video Server",
+  "subtitle": "A demo video streaming service.",
+  "description": "This is an example server demonstrating the Hot Tub API structure and capabilities.",
+  "iconUrl": "https://example.com/icon.png",
+  "color": "#007AFF",
+  "status": "active",
+  "notices": [
+    {
+      "status": "warning",
+      "message": "Scheduled Maintenance",
+      "details": "Our servers will undergo maintenance on January 15th from 2:00-4:00 AM UTC. Some features may be temporarily unavailable.",
+      "priority": true,
+      "url": null
+    },
+    {
+      "status": "info",
+      "message": "New Features Available",
+      "details": "Check out our latest update with improved video quality and faster loading times!",
+      "priority": false,
+      "url": "https://example.com/changelog"
+    }
+  ],
+  "channels": [
+    {
+      "id": "example-tube",
+      "name": "Example Tube",
+      "description": "A sample video channel demonstrating the API structure.",
+      "premium": false,
+      "favicon": "https://example.com/favicon.ico",
+      "status": "active",
+      "categories": ["Entertainment", "Education", "Music"],
+      "options": [
+        {
+          "id": "quality",
+          "title": "Video Quality",
+          "systemImage": "video",
+          "colorName": "blue",
+          "multiSelect": false,
+          "options": [
+            {
+              "id": "hd",
+              "title": "HD",
+              "description": "High definition videos only"
+            },
+            {
+              "id": "any",
+              "title": "Any Quality",
+              "description": "Include all video qualities"
+            }
+          ]
+        },
+        {
+          "id": "duration",
+          "title": "Duration",
+          "systemImage": "clock",
+          "colorName": "orange",
+          "multiSelect": true,
+          "options": [
+            {
+              "id": "short",
+              "title": "Short (< 5 min)",
+              "description": "Videos under 5 minutes"
+            },
+            {
+              "id": "medium",
+              "title": "Medium (5-20 min)",
+              "description": "Videos between 5-20 minutes"
+            },
+            {
+              "id": "long",
+              "title": "Long (> 20 min)",
+              "description": "Videos over 20 minutes"
+            }
+          ]
+        }
+      ],
+      "nsfw": false,
+      "ytdlpCommand": "yt-dlp --format best",
+      "cacheDuration": 1800
+    }
+  ],
+  "nsfw": false,
+  "categories": [
+    "Funny",
+    "Cute",
+    "Action",
+    "Adventure",
+    "Animation",
+    "Comedy",
+    "Documentary",
+    "Drama"
+  ],
+  "options": [
+    {
+      "id": "sort",
+      "title": "Sort",
+      "systemImage": "arrow.up.arrow.down",
+      "colorName": "blue",
+      "multiSelect": false,
+      "options": [
+        {
+          "id": "new",
+          "title": "New",
+          "description": "Sort the videos by newest first."
+        },
+        {
+          "id": "likes",
+          "title": "Most Liked",
+          "description": "Sort the videos by likes."
+        }
+      ]
+    }
+  ],
+  "popup": null,
+  "filtersFooter": "Help us improve our algorithms by selecting the categories that best describe you. These may not change search results on your source, but they help personalize the overall app experience."
+}
+
+

Field Reference

+

ServerStatus Object

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FieldTypeRequiredDescription
idstring✅Unique server identifier
namestring✅Server display name
subtitlestringâšĒBrief server description
descriptionstringâšĒDetailed server description
iconUrlstringâšĒServer icon/logo URL
colorstringâšĒBrand color (hex code or Swift color names)
statusChannelStatusâšĒServer operational status
noticesNotice[]âšĒStatus notices/alerts
channelsChannel[]âšĒAvailable content channels
channelGroupsChannelGroup[]âšĒExplicit channel picker sections (takes priority over per-channel groupKey/sortOrder)
nsfwbooleanâšĒWhether server contains adult content
categoriesstring[]âšĒAvailable content categories
optionsChannelOption[]âšĒGlobal server options
filtersFooterstringâšĒFooter text for filter UI
+

ChannelGroup Object

+

An explicit, ordered section definition for the channel picker. When present, channelGroups takes full priority over per-channel groupKey and sortOrder.

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FieldTypeRequiredDescription
idstring✅Unique group identifier
titlestring✅Section heading displayed in the channel picker
channelIdsstring[]✅Ordered list of channel IDs belonging to this group
+
+

Channel Picker Section Logic

+

The channel picker builds sections using the following priority order:

+
    +
  1. channelGroups (top-level, on +ServerStatus) — explicit sections with named titles and an ordered list +of channel IDs. Use this for full control over grouping and order.
  2. +
  3. groupKey + sortOrder (per-channel fields) — lightweight fallback. Channels are grouped by their groupKey string; within each group they are sorted by sortOrder (ascending), then alphabetically. Use this when you don't want a separate top-level structure.
  4. +
  5. Default — if neither is provided, channels are +split into a "Premium" section and a general "Channels" section, each +sorted alphabetically.
  6. +
+
// Option A: channelGroups (explicit, recommended for complex layouts)
+{
+  "channelGroups": [
+    { "id": "featured", "title": "Featured", "channelIds": ["tube-a", "tube-b"] },
+    { "id": "niche",    "title": "Niche",    "channelIds": ["tube-c"] }
+  ]
+}
+
+// Option B: groupKey + sortOrder (per-channel, simpler)
+{ "id": "tube-a", "groupKey": "Featured", "sortOrder": 1 }
+{ "id": "tube-b", "groupKey": "Featured", "sortOrder": 2 }
+{ "id": "tube-c", "groupKey": "Niche",    "sortOrder": 1 }
+
+
+

Channel Object

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FieldTypeRequiredDescription
idstring✅Unique channel identifier
namestring✅Channel display name
faviconstringâšĒChannel favicon URL
statusChannelStatusâšĒChannel operational status
categoriesstring[]âšĒContent categories for this channel — drives the home screen category browser (tapping one runs a search for that term)
tagsChannelTag[]âšĒSpecialization chips shown in the channel picker (e.g. "4K", "Amateur")
optionsChannelOption[]âšĒChannel-specific filter options
maintainersChannelMaintainer[]âšĒAttribution entries shown in video cells
premiumbooleanâšĒWhether channel requires premium
descriptionstringâšĒChannel description
imagestringâšĒChannel banner/hero image URL
nsfwbooleanâšĒWhether channel contains adult content
defaultbooleanâšĒWhether this channel is selected by default
sortOrdernumberâšĒDisplay order when channelGroups is not used — lower values appear first
groupKeystringâšĒSection key for grouping channels (e.g. "favorites", "premium", or a category name)
ytdlpCommandstringâšĒCustom yt-dlp command
cacheDurationnumberâšĒCache duration in seconds — yt-dlp re-extracts video details when expired
+
+

Categories vs Tags

+
    +
  • categories is a flat list of content keywords (e.g. "Comedy", "Action") shared across videos from this channel. They populate the home screen category browser — tapping one runs a search using that term.
  • +
  • tags are specialization labels for the channel itself (e.g. "4K", "Amateur", "VR") displayed as chips in the channel picker UI. They describe what the site is known for, not individual video content.
  • +
+
+

ChannelTag Object

+

Tags can be supplied as a plain string (legacy) or a typed object — the app accepts both transparently.

+
+ + + + + + + + + + + + + + + + + + + + + + +
FieldTypeRequiredDescription
namestring✅Tag label displayed on the channel chip
systemImagestringâšĒSF Symbol name shown alongside the tag label
+
// Plain string (legacy — still accepted)
+"categories": ["4K", "Amateur"]
+
+// Object format (recommended — supports icons)
+"tags": [
+  { "name": "4K", "systemImage": "4k.tv" },
+  { "name": "VR", "systemImage": "visionpro" }
+]
+
+

ChannelMaintainer Object

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FieldTypeRequiredDescription
idstring✅Maintainer identifier
namestring✅Display name
avatarstringâšĒAvatar image URL (canonical)
rolestringâšĒ"maintainer" (built the integration) or "upstream" (federated proxy)
+

See JSON key names for naming conventions and the legacy profile_picture_url avatar key.

+

Notice Object

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FieldTypeRequiredDescription
statusstring✅Notice type ("success", "info", "error", "warning", etc.)
messagestringâšĒNotice title/message
detailsstringâšĒDetailed notice description
prioritybooleanâšĒWhether to display prominently on home page
urlstringâšĒAction URL for the notice
+

ChannelOption Object

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FieldTypeRequiredDescription
idstring✅Unique option identifier
titlestring✅Option display title
optionsChannelOptionChoice[]✅Available choices for this option
systemImagestringâšĒSF Symbol name for option icon
colorNamestringâšĒSwift color name for theming
multiSelectbooleanâšĒWhether multiple choices can be selected
valuestring\|number\|booleanâšĒCurrent option value
+

ChannelOptionChoice Object

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FieldTypeRequiredDescription
idstring✅Unique choice identifier
titlestring✅Choice display title
descriptionstringâšĒChoice description
optionsChannelOption[]âšĒNested options shown when this choice is selected
+

ChannelStatus Enum

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ValueDescription
activeChannel is online and fully functional (same as normal / ok)
normalAlias for active
okAlias for active
inactiveChannel deliberately disabled or suspended
degradedChannel working with reduced quality or performance
maintenanceChannel temporarily unavailable due to planned work
restrictedAccess is limited (e.g. region-locked, paywalled, or subject to a takedown)
errorChannel encountered a critical issue
unknownChannel status is not known
testingChannel is in testing mode for development
+
+

Implementation Notes

+
    +
  • Only id and name are truly required for both ServerStatus and Channel
  • +
  • Options create hierarchical filter UIs — choices can have nested options
  • +
  • priority notices appear on the home page; others only appear on the lock screen
  • +
  • Colors support both hex codes (#FF0000) and Swift color names (purple)
  • +
  • Channel tags accept either plain strings (legacy) or { name, systemImage } objects — mixing both in the same array is fine
  • +
  • sortOrder and groupKey are the per-channel fallback for sectioning; channelGroups on the server response takes priority when present
  • +
+
+
+

Popup System

+

The popup system allows servers to display onboarding flows, age +verification, preferences, and other modal content. Here's an example +multi-page onboarding popup:

+
{
+  "popup": {
+    "id": "onboarding",
+    "pages": [
+      {
+        "id": "ageVerification",
+        "title": "Welcome to",
+        "subtitle": "Example Server",
+        "warning": "âš ī¸ Adult Content Warning",
+        "body": "This source contains adult content. You must be 18 years or older to use it. By continuing, you confirm that you meet this requirement and agree to use the app responsibly.",
+        "items": [
+          {
+            "type": "section",
+            "title": "Age Verification",
+            "items": [
+              {
+                "type": "toggle",
+                "id": "termsToggle",
+                "title": "I am over 18 years of age",
+                "systemImage": "checkmark.seal.fill",
+                "color": "gray",
+                "state": false
+              }
+            ]
+          }
+        ],
+        "actions": [
+          {
+            "title": "Continue",
+            "systemImage": "arrow.right.circle.fill",
+            "color": "blue",
+            "actionType": "next",
+            "enabledBy": "termsToggle"
+          }
+        ]
+      },
+      {
+        "id": "preferences",
+        "title": "Choose your Preferences",
+        "subtitle": "Personalize Your Experience",
+        "body": "Select the type of content you'd like to explore. This can be changed later in the filters menu.",
+        "items": [
+          {
+            "type": "custom",
+            "view": "ServerFiltersView"
+          }
+        ],
+        "actions": [
+          {
+            "title": "Continue",
+            "systemImage": "arrow.right.circle.fill",
+            "color": "blue",
+            "actionType": "next"
+          }
+        ]
+      }
+    ]
+  }
+}
+
+

Popup Features:

+
    +
  • Multi-page flows - Create step-by-step onboarding experiences
  • +
  • Conditional actions - Use enabledBy to require toggle states before proceeding
  • +
  • Custom views - Embed specialized UI like ServerFiltersView for complex interactions
  • +
  • Rich content - Support for titles, subtitles, warnings, body text, and actions
  • +
  • Interactive controls - Toggles, buttons, and sections for user input
  • +
+
+ + + + + + + + + + + + + +
+
+ + Was this page helpful? + +
+
+ + + + + +
+
+ + + + + +
+
+
+
+ + + +
+
+ + + +
+ + + +
+ + + + +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/prompts/new-channel.md b/prompts/new-channel.md new file mode 100644 index 0000000..ce9f22d --- /dev/null +++ b/prompts/new-channel.md @@ -0,0 +1 @@ +write a provider for the site "vjav.com". use playwright-mcp to get the index site and get the layout of videos from this. also figure out how searches work and if it has a different layout then. also find out how more videos get loaded/more pages urls. also find if it has video sites like recommended,most views etc and include them. try to find as much information about a video on the site as possible and build the video items with all the information. put tags and uploader into lists so they can be utilized later. on query check if the search already exists in these FilterObject arrays and use them for the url instead (similar to omgxxx). after coding this, test that all provided urls work, yt-dlp can download the video.url, the thumbnails work, searches, tag-/uploader- searches work, more pages work. analzye the provider too and set the channel tags and put it in a group that matches this provider or create a new group where it fits in. do not include the embed field in the videoitems responses. when parsing uploaders, make sure that the uploaders. if unsure about the endpoints, check with the docs dir. \ No newline at end of file