X-Git-Url: http://njoseph.me/gitweb/nimcoon.git/blobdiff_plain/86e6cb72964db0443c6b5527892f49721079f72a..9ed70b9e0ee8ebf8e0b1fc1bbe1b77178d1f008c:/src/lib.nim diff --git a/src/lib.nim b/src/lib.nim index d36e2c2..17761f3 100644 --- a/src/lib.nim +++ b/src/lib.nim @@ -1,5 +1,4 @@ import - htmlparser, httpClient, json, os, @@ -8,27 +7,16 @@ import sequtils, std/[terminal], strformat, - strformat, - strtabs, strutils, - sugar, - tables, - uri, - xmltree - -import config + tables - -type - Options* = Table[string, bool] - SearchResult* = tuple[title: string, url: string] - SearchResults* = seq[tuple[title: string, url: string]] - CommandLineOptions* = tuple[searchQuery: string, options: Options] - SelectionRange* = tuple[begin: int, until: int] +import + config, + types let - processOptions = {poStdErrToStdOut, poUsePath} # poEchoCmd can be added to options for debugging + processOptions = {poStdErrToStdOut, poUsePath, poEchoCmd} PEERTUBE_REGEX = re"videos\/watch\/[0-9a-f]{8}\b-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-\b[0-9a-f]{12}" @@ -45,13 +33,6 @@ proc selectMediaPlayer*(): string = return availablePlayers[0] -proc getYoutubePage*(searchQuery: string): string = - let queryParam = encodeUrl(searchQuery) - let client = newHttpClient() - let response = get(client, &"https://www.youtube.com/results?hl=en&search_query={queryParam}") - $response.body - - proc getPeerTubeMagnetLink(url: string): string = ## Gets the magnet link of the best possible resolution from PeerTube let uuid = url.substr(find(url, PEERTUBE_REGEX) + "videos/watch/".len) @@ -63,31 +44,13 @@ proc getPeerTubeMagnetLink(url: string): string = jsonNode["files"][0]["magnetUri"].getStr() -func extractTitlesAndUrls*(html: string): SearchResults = - {.noSideEffect.}: - parseHtml(html).findAll("a"). - filter(a => "watch" in a.attrs["href"] and a.attrs.hasKey "title"). - map(a => (a.attrs["title"], "https://www.youtube.com" & a.attrs["href"])) - - proc presentVideoOptions*(searchResults: SearchResults) = eraseScreen() for index, (title, url) in searchResults: styledEcho $index, ". ", styleBright, fgMagenta, title, "\n", resetStyle, fgCyan, " ", url, "\n" -func isPlaylist(url: string): bool = - ##[ Identifies if video is part of a playlist. - Only YouTube playlists are supported for now. ]## - "www.youtube.com" in url and "&list=" in url - - func buildPlayerArgs(url: string, options: Table[string, bool], player: string): seq[string] = - let url = - # Playlists are only supported by MPV player. VLC needs a plugin. - if isPlaylist(url) and player == "mpv": - "https://www.youtube.com/playlist?" & url.split('&')[1] - else: url let musicOnly = if options["musicOnly"]: "--no-video" else: "" let fullScreen = if options["fullScreen"]: "--fullscreen" else: "" filterIt([url, musicOnly, fullScreen], it != "") @@ -124,9 +87,11 @@ proc download*(args: openArray[string], title: string) = func urlLongen(url: string): string = url.replace("youtu.be/", "www.youtube.com/watch?v=") -func rewriteInvidiousToYouTube(url: string): string = +func rewriteInvidiousToYouTube*(url: string): string = {.noSideEffect.}: - if rewriteInvidiousURLs: url.replace("invidio.us", "www.youtube.com") else: url + if rewriteInvidiousURLs and url.replace(".", "").contains("invidious"): + &"https://www.youtube.com/watch?v={url.split(\"=\")[1]}" + else: url func stripZshEscaping(url: string): string = url.replace("\\", "") @@ -156,7 +121,10 @@ proc directDownload*(url: string, musicOnly: bool) = let args = if musicOnly: buildMusicDownloadArgs(url) else: buildVideoDownloadArgs(url) - discard execShellCmd(&"youtube-dl {args.join(\" \")}") + if isInstalled("aria2c"): + discard execShellCmd(&"youtube-dl {args.join(\" \")} --external-downloader aria2c --external-downloader-args '-x 16 -s 16 -k 2M'") + else: + discard execShellCmd(&"youtube-dl {args.join(\" \")}") proc offerSelection(searchResults: SearchResults, options: Table[string, bool], selectionRange: SelectionRange): string = @@ -177,6 +145,32 @@ proc handleUserInput(searchResult: SearchResult, options: Table[string, bool], p play(player, options, searchResult.url, searchResult.title) +proc isValidOptions*(options: Options): bool = + # Check for invalid combinations of options + var invalidCombinations = [("musicOnly", "fullScreen"), ("download", "fullScreen")] + result = true + for combination in invalidCombinations: + if options[combination[0]] and options[combination[1]]: + stderr.writeLine fmt"Incompatible options provided: {combination[0]} and {combination[1]}" + result = false + + +proc updateOptions(options: Options, newOptions: string): Options = + result = options + + for option in newOptions: + case option + of 'm': result["musicOnly"] = true + of 'f': result["fullScreen"] = true + of 'd': result["download"] = true + else: + echo "Invalid option provided!" + quit(2) + + if(not isValidOptions(result)): + quit(2) + + proc present*(searchResults: SearchResults, options: Table[string, bool], selectionRange: SelectionRange, player: string) = ##[ Continuously present options till the user quits the application @@ -205,8 +199,14 @@ proc present*(searchResults: SearchResults, options: Table[string, bool], select of "q": quit(0) else: - let searchResult = searchResults[selectionRange.begin .. selectionRange.until][parseInt(userInput)] - handleUserInput(searchResult, options, player) + if " " in userInput: + let selection = parseInt(userInput.split(" ")[0]) + let updatedOptions = updateOptions(options, userInput.split(" ")[1]) + let searchResult = searchResults[selectionRange.begin .. selectionRange.until][selection] + handleUserInput(searchResult, updatedOptions, player) + else: + let searchResult = searchResults[selectionRange.begin .. selectionRange.until][parseInt(userInput)] + handleUserInput(searchResult, options, player) if options["feelingLucky"]: quit(0) else: