import
httpClient,
json,
+ std/[terminal],
strformat,
strutils,
- sequtils,
uri
-import types
-
-proc getYouTubePage(searchQuery: string): string =
- let queryParam = encodeUrl(searchQuery)
- let client = newHttpClient()
- let response = get(client, &"https://www.youtube.com/results?search_query={queryParam}")
- $response.body
-
+import
+ config,
+ types
-proc getSearchResults*(searchQuery: string): SearchResults =
- let html = getYouTubePage(searchQuery)
- let lines = html.split('\n').filterIt(it.contains("ytInitialData"))
- let line = lines[0]
- let jsonString = line.split('=', maxsplit=1)[1].strip().strip(chars={';'})
- let jsonData = parseJson(jsonString)
- let videos = jsonData["contents"]["twoColumnSearchResultsRenderer"]["primaryContents"]["sectionListRenderer"]["contents"][0]["itemSectionRenderer"]["contents"]
+discard """
- var searchResults: SearchResults = @[]
+Invidious API reference:
+https://github.com/iv-org/documentation/blob/master/API.md
+"""
- for video in videos:
- if video.hasKey("videoRenderer"):
- let title = ($video["videoRenderer"]["title"]["runs"][0]["text"]).strip(chars={'"'})
- let videoId = ($video["videoRenderer"]["videoId"]).strip(chars={'"'})
- let videoUrl = &"https://www.youtube.com/watch?v={videoId}"
- searchResults.add((title, videoUrl))
+func makeUrl(videoId: string): string =
+ "https://www.youtube.com/watch?v=" & videoId
- elif video.hasKey("playlistRenderer"):
- let title = ($video["playlistRenderer"]["title"]["simpleText"]).strip(chars={'"'})
- let playlistId = ($video["playlistRenderer"]["playlistId"]).strip(chars={'"'})
- let playlistUrl = &"https://www.youtube.com/playlist?list={playlistId}"
- searchResults.add((title, playlistUrl))
+proc getSearchResults*(searchQuery: string): SearchResults =
+ # Using Invidious API to retrieve the search results but playing the results directly from YouTube.
+ let queryParam = encodeUrl(searchQuery)
+ let client = newHttpClient()
+ let response = get(client, &"{invidiousInstance}/api/v1/search?q={queryParam}")
+ let jsonData = parseJson($response.body)
+ if jsonData.kind == JObject: # Could be a 403 error
+ stdout.styledWrite(fgRed, $response.body)
+ quit(1)
+ if len(jsonData) == 0:
+ stdout.styledWrite(fgRed, "Got empty response from Invidious instance")
+ quit(2)
+ var searchResults: SearchResults = @[]
+ for item in jsonData:
+ if item["type"].getStr() == "video":
+ searchResults.add((item["title"].getStr(), makeUrl(item["videoId"].getStr())))
+ elif item["type"].getStr() == "playlist":
+ searchResults.add((item["title"].getStr(), makeUrl(item["playlistId"].getStr())))
+ # Not handling type = channel for now
searchResults
+
+proc getAutoPlayVideo*(searchResult: SearchResult): SearchResult =
+ # Take a search result and fetch its first recommendation
+ let videoId = searchResult.url.split("=")[1]
+ let client = newHttpClient()
+ let response = get(client, &"{invidiousInstance}/api/v1/videos/{videoId}")
+ let jsonData = parseJson($response.body)
+ let firstRecommendation = jsonData["recommendedVideos"][0]
+ (firstRecommendation["title"].getStr(), makeUrl(firstRecommendation["videoId"].getStr()))