- [x] Stream music
- [x] Play direct links from YouTube and PeerTube
- [x] Stream video from magnet links
+- [x] Download music
- [ ] Download video
-- [ ] Download music
- [ ] Configuration options
## Installation
| -m, --music | Play Music only, no video |
| -l, --lucky | Try your luck with the first search result |
| -f, --full-screen | Play video in full screen |
+| -d, --download | Download video or music |
## Development
# Only show these many results
let limit* = 10
+
+# Download videos to this directory
+let videoDownloadDirectory* = "~/Videos"
+
+# Download music to this directory
+let musicDownloadDirectory* = "~/Music"
import
htmlparser,
httpClient,
+ os,
osproc,
sequtils,
sugar,
strformat,
std/[terminal],
+ strformat,
strtabs,
strutils,
tables,
import config
type
- SearchResult* = tuple[title: string, url: string]
Options* = Table[string, bool]
+ SearchResult* = tuple[title: string, url: string]
CommandLineOptions* = tuple[searchQuery: string, options: Options]
-let processOptions = {poStdErrToStdOut, poUsePath}
+# poEchoCmd can be added to options for debugging
+let processOptions = {poStdErrToStdOut, poUsePath, poEchoCmd}
proc selectMediaPlayer*(): string =
let availablePlayers = filterIt(supportedPlayers, execProcess("which " & it).len != 0)
styledEcho $index, ". ", styleBright, fgMagenta, title, "\n", resetStyle, fgCyan, url, "\n"
proc play*(player: string, args: openArray[string], title: string) =
- # poEchoCmd can be added to options for debugging
styledEcho "\n", fgGreen, "Playing ", styleBright, fgMagenta, title
discard execProcess(player, args=args, options=processOptions)
+proc download*(args: openArray[string], title: string) =
+ styledEcho "\n", fgGreen, "Downloading ", styleBright, fgMagenta, title
+ discard execShellCmd(&"youtube-dl {args.join(\" \")}")
+
func urlLongen(url: string): string =
url.replace("youtu.be/", "www.youtube.com/watch?v=")
import
+ os,
parseopt,
std/[terminal],
strformat,
readLine(stdin)
# This is a pure function with no side effects
- func buildArgs(number: int): seq[string] =
+ func buildPlayerArgs(number: int): seq[string] =
var args = @[searchResults[number].url]
if options["musicOnly"]: args.add("--no-video")
if options["fullScreen"]: args.add("--fullscreen")
return args
+ func buildMusicDownloadArgs(number: int): seq[string] =
+ {.noSideEffect.}:
+ var args = @["--ignore-errors", "-f", "bestaudio", "--extract-audio", "--audio-format", "mp3", "--audio-quality", "0", "-o"]
+ let downloadLocation = &"'{expandTilde(musicDownloadDirectory)}/{searchResults[number].title}.mp3'"
+ args.add(downloadLocation)
+ args.add(searchResults[number].url)
+ return args
+
+ proc handleUserInput(number: int) =
+ if options["download"]:
+ if options["musicOnly"]:
+ download(buildMusicDownloadArgs(number), searchResults[number].title)
+ else:
+ play(player, buildPlayerArgs(number), searchResults[number].title)
+
+
while(true):
let userInput = getUserInput()
if userInput == "all":
for number in 0..(len(searchResults)):
- play(player, buildArgs(number), searchResults[number].title)
+ # TODO `spawn` this?
+ handleUserInput(number)
if userInput == "q":
break
# Play the video using the preferred/available media player
let videoNumber = parseInt(userInput)
- play(player, buildArgs(videoNumber), searchResults[videoNumber].title)
+ handleUserInput(videoNumber)
if options["feelingLucky"]:
break