]> njoseph.me Git - nimcoon.git/blobdiff - src/youtube.nim
youtube: Use a different Invidious instance
[nimcoon.git] / src / youtube.nim
index fd98d41e4f2f22b257789e34cf2fe437008b0ab3..a07a629f36cb04a39b67121d7c3ee31bd82f4115 100644 (file)
@@ -1,42 +1,52 @@
 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()))