From 791ff29e208c2d946b8c68a9f02631db43edfe0e Mon Sep 17 00:00:00 2001 From: bloomingchad Date: Sat, 6 Apr 2024 11:09:13 +0100 Subject: [PATCH] * use illwill to implement getch with timeout 0 * [regression] from crossPlat, fixed by using rsplit * reindent call, unneccesary indentations confuse the devs * added illwill in readme/cites * getCurrentSongV2 matured, getCurrentSong disasbled [1] illwill has some good implementation, we will clone it and remove dead code [2] when we implemented unixToNativePath it seems for subdirectories, it gets the absolute path when showing the name [3] if elif linkCheck intends the entire implmentation of call just to avoid return [4] comeon its just good practice to cite people --- readme.rst | 3 + src/illwill.nim | 511 ++++++++++++++++++++++++++++++++++++++++++++++++ src/link.nim | 6 +- src/menu.nim | 251 ++++++++++++------------ src/player.nim | 2 +- 5 files changed, 638 insertions(+), 135 deletions(-) create mode 100644 src/illwill.nim diff --git a/readme.rst b/readme.rst index 0042087..2d85598 100644 --- a/readme.rst +++ b/readme.rst @@ -64,6 +64,9 @@ Cites - c2nim -> https://github.com/nim-lang/c2nim helped wrapping objects. +- illwill -> https://github.com/johnnovak/illwill + provided us with async getch implementation. + Thanks ------ - hundreds of other people from which this code base was made diff --git a/src/illwill.nim b/src/illwill.nim new file mode 100644 index 0000000..8d84d1c --- /dev/null +++ b/src/illwill.nim @@ -0,0 +1,511 @@ +## :Authors: John Novak +## +## adapted to use in pnimrp. +## * Non-blocking keyboard input +import terminal +when not defined windows: import unicode, macros, os + +type + Key* {.pure.} = enum ## Supported single key presses and key combinations + None = (-1, "None"), + + # Special ASCII characters + CtrlA = (1, "CtrlA"), + CtrlB = (2, "CtrlB"), + CtrlC = (3, "CtrlC"), + CtrlD = (4, "CtrlD"), + CtrlE = (5, "CtrlE"), + CtrlF = (6, "CtrlF"), + CtrlG = (7, "CtrlG"), + CtrlH = (8, "CtrlH"), + Tab = (9, "Tab"), # Ctrl-I + CtrlJ = (10, "CtrlJ"), + CtrlK = (11, "CtrlK"), + CtrlL = (12, "CtrlL"), + Enter = (13, "Enter"), # Ctrl-M + CtrlN = (14, "CtrlN"), + CtrlO = (15, "CtrlO"), + CtrlP = (16, "CtrlP"), + CtrlQ = (17, "CtrlQ"), + CtrlR = (18, "CtrlR"), + CtrlS = (19, "CtrlS"), + CtrlT = (20, "CtrlT"), + CtrlU = (21, "CtrlU"), + CtrlV = (22, "CtrlV"), + CtrlW = (23, "CtrlW"), + CtrlX = (24, "CtrlX"), + CtrlY = (25, "CtrlY"), + CtrlZ = (26, "CtrlZ"), + Escape = (27, "Escape"), + + CtrlBackslash = (28, "CtrlBackslash"), + CtrlRightBracket = (29, "CtrlRightBracket"), + + # Printable ASCII characters + Space = (32, "Space"), + ExclamationMark = (33, "ExclamationMark"), + DoubleQuote = (34, "DoubleQuote"), + Hash = (35, "Hash"), + Dollar = (36, "Dollar"), + Percent = (37, "Percent"), + Ampersand = (38, "Ampersand"), + SingleQuote = (39, "SingleQuote"), + LeftParen = (40, "LeftParen"), + RightParen = (41, "RightParen"), + Asterisk = (42, "Asterisk"), + Plus = (43, "Plus"), + Comma = (44, "Comma"), + Minus = (45, "Minus"), + Dot = (46, "Dot"), + Slash = (47, "Slash"), + + Zero = (48, "Zero"), + One = (49, "One"), + Two = (50, "Two"), + Three = (51, "Three"), + Four = (52, "Four"), + Five = (53, "Five"), + Six = (54, "Six"), + Seven = (55, "Seven"), + Eight = (56, "Eight"), + Nine = (57, "Nine"), + + Colon = (58, "Colon"), + Semicolon = (59, "Semicolon"), + LessThan = (60, "LessThan"), + Equals = (61, "Equals"), + GreaterThan = (62, "GreaterThan"), + QuestionMark = (63, "QuestionMark"), + At = (64, "At"), + + ShiftA = (65, "ShiftA"), + ShiftB = (66, "ShiftB"), + ShiftC = (67, "ShiftC"), + ShiftD = (68, "ShiftD"), + ShiftE = (69, "ShiftE"), + ShiftF = (70, "ShiftF"), + ShiftG = (71, "ShiftG"), + ShiftH = (72, "ShiftH"), + ShiftI = (73, "ShiftI"), + ShiftJ = (74, "ShiftJ"), + ShiftK = (75, "ShiftK"), + ShiftL = (76, "ShiftL"), + ShiftM = (77, "ShiftM"), + ShiftN = (78, "ShiftN"), + ShiftO = (79, "ShiftO"), + ShiftP = (80, "ShiftP"), + ShiftQ = (81, "ShiftQ"), + ShiftR = (82, "ShiftR"), + ShiftS = (83, "ShiftS"), + ShiftT = (84, "ShiftT"), + ShiftU = (85, "ShiftU"), + ShiftV = (86, "ShiftV"), + ShiftW = (87, "ShiftW"), + ShiftX = (88, "ShiftX"), + ShiftY = (89, "ShiftY"), + ShiftZ = (90, "ShiftZ"), + + LeftBracket = (91, "LeftBracket"), + Backslash = (92, "Backslash"), + RightBracket = (93, "RightBracket"), + Caret = (94, "Caret"), + Underscore = (95, "Underscore"), + GraveAccent = (96, "GraveAccent"), + + A = (97, "A"), + B = (98, "B"), + C = (99, "C"), + D = (100, "D"), + E = (101, "E"), + F = (102, "F"), + G = (103, "G"), + H = (104, "H"), + I = (105, "I"), + J = (106, "J"), + K = (107, "K"), + L = (108, "L"), + M = (109, "M"), + N = (110, "N"), + O = (111, "O"), + P = (112, "P"), + Q = (113, "Q"), + R = (114, "R"), + S = (115, "S"), + T = (116, "T"), + U = (117, "U"), + V = (118, "V"), + W = (119, "W"), + X = (120, "X"), + Y = (121, "Y"), + Z = (122, "Z"), + + LeftBrace = (123, "LeftBrace"), + Pipe = (124, "Pipe"), + RightBrace = (125, "RightBrace"), + Tilde = (126, "Tilde"), + Backspace = (127, "Backspace"), + + # Special characters with virtual keycodes + Up = (1001, "Up"), + Down = (1002, "Down"), + Right = (1003, "Right"), + Left = (1004, "Left"), + Home = (1005, "Home"), + Insert = (1006, "Insert"), + Delete = (1007, "Delete"), + End = (1008, "End"), + PageUp = (1009, "PageUp"), + PageDown = (1010, "PageDown"), + + F1 = (1011, "F1"), + F2 = (1012, "F2"), + F3 = (1013, "F3"), + F4 = (1014, "F4"), + F5 = (1015, "F5"), + F6 = (1016, "F6"), + F7 = (1017, "F7"), + F8 = (1018, "F8"), + F9 = (1019, "F9"), + F10 = (1020, "F10"), + F11 = (1021, "F11"), + F12 = (1022, "F12"), + + Mouse = (5000, "Mouse") + + IllwillError* = object of CatchableError + +#[func toKey(c: int): Key = + try: + result = Key(c) + except RangeDefect: # ignore unknown keycodes + result = Key.None +]# + +proc toKey(c: int): Key = cast[Key](c) + +var gIllwillInitialised = false +var gFullScreen = false +when not defined windows: + var gFullRedrawNextFrame = false + +when defined(windows): + import winlean + + proc getConsoleMode(hConsoleHandle: Handle, dwMode: ptr DWORD): WINBOOL {. + stdcall, dynlib: "kernel32", importc: "GetConsoleMode".} + + proc setConsoleMode(hConsoleHandle: Handle, dwMode: DWORD): WINBOOL {. + stdcall, dynlib: "kernel32", importc: "SetConsoleMode".} + + type + WCHAR = WinChar + CHAR = char + BOOL = WINBOOL + WORD = uint16 + UINT = cint + SHORT = int16 + + # Windows console input structuress + type + KEY_EVENT_RECORD_UNION* {.bycopy, union.} = object + UnicodeChar*: WCHAR + AsciiChar*: CHAR + + INPUT_RECORD_UNION* {.bycopy, union.} = object + KeyEvent*: KEY_EVENT_RECORD + MenuEvent*: MENU_EVENT_RECORD + FocusEvent*: FOCUS_EVENT_RECORD + + COORD* {.bycopy.} = object + X*: SHORT + Y*: SHORT + + PCOORD* = ptr COORD + FOCUS_EVENT_RECORD* {.bycopy.} = object + bSetFocus*: BOOL + + MENU_EVENT_RECORD* {.bycopy.} = object + dwCommandId*: UINT + + PMENU_EVENT_RECORD* = ptr MENU_EVENT_RECORD + + INPUT_RECORD* {.bycopy.} = object + EventType*: WORD + Event*: INPUT_RECORD_UNION + + const + ENABLE_WRAP_AT_EOL_OUTPUT = 0x0002 + + var gOldConsoleModeInput: DWORD + var gOldConsoleMode: DWORD + + proc consoleInit() = + discard getConsoleMode(getStdHandle(STD_INPUT_HANDLE), gOldConsoleModeInput.addr) + if gFullScreen: + if getConsoleMode(getStdHandle(STD_OUTPUT_HANDLE), gOldConsoleMode.addr) != 0: + var mode = gOldConsoleMode and (not ENABLE_WRAP_AT_EOL_OUTPUT) + discard setConsoleMode(getStdHandle(STD_OUTPUT_HANDLE), mode) + else: + discard getConsoleMode(getStdHandle(STD_OUTPUT_HANDLE), gOldConsoleMode.addr) + + proc consoleDeinit() = + if gOldConsoleMode != 0: + discard setConsoleMode(getStdHandle(STD_OUTPUT_HANDLE), gOldConsoleMode) + + + proc getchTimeout(ms: int32): KEY_EVENT_RECORD = + let fd = getStdHandle(STD_INPUT_HANDLE) + var keyEvent = KEY_EVENT_RECORD() + var numRead: cint + while true: + case waitForSingleObject(fd, ms) + of WAIT_TIMEOUT: + keyEvent.eventType = -1 + return + of WAIT_OBJECT_0: + doAssert(readConsoleInput(fd, addr(keyEvent), 1, addr(numRead)) != 0) + if numRead == 0 or keyEvent.eventType != 1 or keyEvent.bKeyDown == 0: + continue + return keyEvent + else: + doAssert(false) + + proc getKeyAsync(ms: int): Key = + let event = getchTimeout(int32(ms)) + + if event.eventType == -1: + return Key.None + + if event.uChar != 0: + return toKey(event.uChar) + else: + case event.wVirtualScanCode + of 8: return Key.Backspace + of 9: return Key.Tab + of 13: return Key.Enter + of 32: return Key.Space + of 59: return Key.F1 + of 60: return Key.F2 + of 61: return Key.F3 + of 62: return Key.F4 + of 63: return Key.F5 + of 64: return Key.F6 + of 65: return Key.F7 + of 66: return Key.F8 + of 67: return Key.F9 + of 68: return Key.F10 + of 71: return Key.Home + of 72: return Key.Up + of 73: return Key.PageUp + of 75: return Key.Left + of 77: return Key.Right + of 79: return Key.End + of 80: return Key.Down + of 81: return Key.PageDown + of 82: return Key.Insert + of 83: return Key.Delete + of 87: return Key.F11 + of 88: return Key.F12 + else: return Key.None + +else: # OS X & Linux + import posix, tables, termios, strutils + + proc consoleInit() + proc consoleDeinit() + + # References: + # https://de.wikipedia.org/wiki/ANSI-Escapesequenz + # https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Extended-coordinates + const + KEYS_D = [Key.Up, Key.Down, Key.Right, Key.Left, Key.None, Key.End, Key.None, Key.Home] + KEYS_E = [Key.Delete, Key.End, Key.PageUp, Key.PageDown, Key.Home, Key.End] + KEYS_F = [Key.F1, Key.F2, Key.F3, Key.F4, Key.F5, Key.None, Key.F6, Key.F7, Key.F8] + KEYS_G = [Key.F9, Key.F10, Key.None, Key.F11, Key.F12] + + # Adapted from: + # https://ftp.gnu.org/old-gnu/Manuals/glibc-2.2.3/html_chapter/libc_24.html#SEC499 + proc SIGTSTP_handler(sig: cint) {.noconv.} = + signal(SIGTSTP, SIG_DFL) + # XXX why don't the below 3 lines seem to have any effect? + resetAttributes() + showCursor() + consoleDeinit() + discard posix.raise(SIGTSTP) + + proc SIGCONT_handler(sig: cint) {.noconv.} = + signal(SIGCONT, SIGCONT_handler) + signal(SIGTSTP, SIGTSTP_handler) + + gFullRedrawNextFrame = true + consoleInit() + hideCursor() + + proc installSignalHandlers() = + signal(SIGCONT, SIGCONT_handler) + signal(SIGTSTP, SIGTSTP_handler) + + proc nonblock(enabled: bool) = + var ttyState: Termios + + # get the terminal state + discard tcGetAttr(STDIN_FILENO, ttyState.addr) + + if enabled: + # turn off canonical mode & echo + ttyState.c_lflag = ttyState.c_lflag and not Cflag(ICANON or ECHO) + + # minimum of number input read + ttyState.c_cc[VMIN] = 0.char + + else: + # turn on canonical mode & echo + ttyState.c_lflag = ttyState.c_lflag or ICANON or ECHO + + # set the terminal attributes. + discard tcSetAttr(STDIN_FILENO, TCSANOW, ttyState.addr) + + proc kbhit(ms: int): cint = + var tv: Timeval + tv.tv_sec = Time(ms div 1000) + tv.tv_usec = 1000 * (int32(ms) mod 1000) # int32 because of macos + + var fds: TFdSet + FD_ZERO(fds) + FD_SET(STDIN_FILENO, fds) + discard select(STDIN_FILENO+1, fds.addr, nil, nil, tv.addr) + return FD_ISSET(STDIN_FILENO, fds) + + proc consoleInit() = + nonblock(true) + installSignalHandlers() + + proc consoleDeinit() = + nonblock(false) + + # surely a 100 char buffer is more than enough; the longest + # keycode sequence I've seen was 6 chars + proc parseStdin[T](input: T): Key = + var ch1, ch2, ch3, ch4, ch5: char + result = Key.None + if read(input, ch1.addr, 1) > 0: + case ch1 + of '\e': + if read(input, ch2.addr, 1) > 0: + if ch2 == 'O' and read(input, ch3.addr, 1) > 0: + if ch3 in "ABCDFH": + result = KEYS_D[int(ch3) - int('A')] + elif ch3 in "PQRS": + result = KEYS_F[int(ch3) - int('P')] + elif ch2 == '[' and read(input, ch3.addr, 1) > 0: + if ch3 in "ABCDFH": + result = KEYS_D[int(ch3) - int('A')] + elif ch3 in "PQRS": + result = KEYS_F[int(ch3) - int('P')] + elif ch3 == '1' and read(input, ch4.addr, 1) > 0: + if ch4 == '~': + result = Key.Home + elif ch4 in "12345789" and read(input, ch5.addr, 1) > 0 and ch5 == '~': + result = KEYS_F[int(ch4) - int('1')] + elif ch3 == '2' and read(input, ch4.addr, 1) > 0: + if ch4 == '~': + result = Key.Insert + elif ch4 in "0134" and read(input, ch5.addr, 1) > 0 and ch5 == '~': + result = KEYS_G[int(ch4) - int('0')] + elif ch3 in "345678" and read(input, ch4.addr, 1) > 0 and ch4 == '~': + result = KEYS_E[int(ch3) - int('3')] + else: + discard # if cannot parse full seq it is discarded + else: + discard # if cannot parse full seq it is discarded + else: + result = Key.Escape + of '\n': + result = Key.Enter + of '\b': + result = Key.Backspace + else: + result = toKey(int(ch1)) + + proc getKeyAsync(ms: int): Key = + result = Key.None + if kbhit(ms) > 0: + result = parseStdin(cint(STDIN_FILENO)) + +when defined(posix): + const + XtermColor = "xterm-color" + Xterm256Color = "xterm-256color" + +proc enterFullScreen() = + ## Enters full-screen mode (clears the terminal). + when defined(posix): + case getEnv("TERM"): + of XtermColor: + stdout.write "\e7\e[?47h" + of Xterm256Color: + stdout.write "\e[?1049h" + else: + eraseScreen() + else: + eraseScreen() + +proc exitFullScreen() = + ## Exits full-screen mode (restores the previous contents of the terminal). + when defined(posix): + case getEnv("TERM"): + of XtermColor: + stdout.write "\e[2J\e[?47l\e8" + of Xterm256Color: + stdout.write "\e[?1049l" + else: + eraseScreen() + else: + eraseScreen() + setCursorPos(0, 0) + +proc illwillInit*(fullScreen = true) = + if gIllwillInitialised: + raise newException(IllwillError, "Illwill already initialised") + gFullScreen = fullScreen + if gFullScreen: enterFullScreen() + + consoleInit() + gIllwillInitialised = true + resetAttributes() + +proc checkInit() = + if not gIllwillInitialised: + raise newException(IllwillError, "Illwill not initialised") + +proc illwillDeinit*() = + checkInit() + if gFullScreen: exitFullScreen() + consoleDeinit() + gIllwillInitialised = false + resetAttributes() + showCursor() + +proc getKey*(): Key = + ## Reads the next keystroke in a non-blocking manner. If there are no + ## keypress events in the buffer, `Key.None` is returned. + ## If the module is not intialised, `IllwillError` is raised. + checkInit() + result = getKeyAsync(0) + when defined(windows): + if result == Key.None: discard + +proc getKeyWithTimeout*(ms = 1000): Key = + ## Reads the next keystroke with a timeout. If there were no keypress events + ## in the specified `ms` period, `Key.None` is returned. + ## + ## If the module is not intialised, `IllwillError` is raised. + checkInit() + result = getKeyAsync(ms) + when defined(windows): + if result == Key.None: discard + +export + terminalWidth, terminalHeight, + terminalSize, hideCursor, showCursor, + Style diff --git a/src/link.nim b/src/link.nim index 2ecc143..13f0a02 100644 --- a/src/link.nim +++ b/src/link.nim @@ -1,4 +1,4 @@ -import strutils, json, httpclient, net, ui +import strutils, #[json,]# httpclient, net, ui proc cleanLink(str: string): string = var link = str @@ -20,7 +20,7 @@ proc cleanLink(str: string): string = except: return false return false]# -proc getCurrentSong*(linke: string): string = +#[proc getCurrentSong*(linke: string): string = #use mpv stream_lavf.c to get icy-title from audio buffer let client = newHttpClient() var link = linke @@ -43,7 +43,7 @@ proc getCurrentSong*(linke: string): string = JsonParsingError, #different technique than implemented ProtocolError, #connection refused? KeyError: "notimplemented" - +]# proc splitLink(str: string): seq[string] = split(str, ":", maxSplit = 1) template isHttps(link: string): bool = link.startsWith "https://" diff --git a/src/menu.nim b/src/menu.nim index 3e3bbd4..5e85702 100644 --- a/src/menu.nim +++ b/src/menu.nim @@ -1,130 +1,121 @@ import terminal, os, ui, strutils, - client, net, player, link + client, net, player, link, + illwill + +template nowStreaming = + eraseLine() + say "Now Streaming: " & currentSong, fgGreen + cursorUp() proc call(sub: string; sect = ""; stat, link: string) = - if link == "": warn "link empty" - elif link.contains " ": warn "link dont exist or is invalid" - else: - clear() - if sect == "": say (("PNimRP > " & sub) & (" > " & stat)) - else: say (("PNimRP > " & sub) & (" > " & sect) & ( + if link == "": + warn "link empty" + return + elif link.contains " ": + warn "link dont exist or is invalid" + return + + clear() + if sect == "": say (("PNimRP > " & sub) & (" > " & stat)) + else: say (("PNimRP > " & sub) & (" > " & sect) & ( " > " & stat)) - sayTermDraw12() + sayTermDraw12() - if not doesLinkWork link: - warn "no link work" - return - var ctx = create() - ctx.init link - var - #echoPlay = true - event = ctx.waitEvent - isPaused = false - isMuted = false - isSetToObserve = false - var currentSong = ""#getCurrentSong link - #ctx.getCurrentSongv2 - #nowPlayingExcept = false - #echo "link in call() before while true: " & link + if not doesLinkWork link: + warn "no link work" + return + var ctx = create() + ctx.init link + var + event = ctx.waitEvent + isPaused = false + isMuted = false + isSetToObserve = false + try: illwillinit false + except IllWillError: discard + var currentSong = "" + #echo "link in call() before while true: " & link - while true: - if not isPaused: event = ctx.waitEvent - if event.eventID in [IDPlaybackRestart] and not isSetToObserve: - ctx.seeIfSongTitleChanges - isSetToObserve = true + while true: + if not isPaused: event = ctx.waitEvent + if event.eventID in [IDPlaybackRestart] and not isSetToObserve: + ctx.seeIfSongTitleChanges + isSetToObserve = true - if event.eventID in [IDEventPropertyChange]: - currentSong = $ctx.getCurrentSongV2 + if event.eventID in [IDEventPropertyChange]: + currentSong = $ctx.getCurrentSongV2 + nowStreaming() - setCursorPos 0, 2 - eraseLine() - echo "event state: ", eventName event.eventID + setCursorPos 0, 2 + eraseLine() + #echo "event state: ", eventName event.eventID - if event.eventID in [IDEndFile, IDShutdown]: - warn "end of file? bad link?" - terminateDestroy ctx - break - eraseLine() - if not isPaused: - if not isMuted: say "Playing", fgGreen - else: warn "Muted" - else: - if not isMuted: warn "Paused" - else: warn "paused and muted" + if event.eventID in [IDEndFile, IDShutdown]: + warn "end of file? bad link?" + terminateDestroy ctx + break + eraseLine() + if not isPaused: + if isMuted: warn "Muted" + else: say "Playing", fgGreen + else: + if isMuted: warn "paused and muted" + else: warn "Paused" #if isMuted: warn "Muted", 4 #if echoPlay: #var event = ctx.waitEvent 1000 - if currentSong != "": + #[if currentSong != "": say "Now Streaming: " & #getCurrentSong link $getCurrentSongv2 ctx, fgGreen - cursorUp() + cursorUp()]# # echoPlay = false #remove cursorUp? - #add time check playing error link - #if not isPaused: - #var t0 = now().second - #event = ctx.waitEvent 1000 - #if now().second - t0 >= 5: - # error "timeout of 5s" - - case getch(): - of 'u', 'U': - #lyrics update func -> just to restart while true - - #[cursorDown() - sayPos 4, "Updated" + case getKey(): + of Key.P: + if isPaused: + #if nowPlayingExcept != true: + #cursorUp() eraseLine() + #cursorDown() + #eraseLine() + #if nowPlayingExcept != true: + #cursorUp() + + isPaused = false + ctx.pause false + else: + eraseLine() + cursorUp() + + ctx.pause true + isPaused = true + + of Key.M: + if isMuted: + #if nowPlayingExcept != true: + #[cursorUp() + eraseLine() + cursorDown() + eraseLine() + #if nowPlayingExcept != true: cursorUp()]# - discard - of 'p', 'P': - if isPaused: - #if nowPlayingExcept != true: - #cursorUp() - eraseLine() - #cursorDown() - #eraseLine() - #if nowPlayingExcept != true: - #cursorUp() - isPaused = false - ctx.pause false - #echoPlay = true - else: - eraseLine() - #warn "Paused", 4 - cursorUp() + ctx.mute false + isMuted = false - ctx.pause true - isPaused = true + else: + eraseLine() + cursorUp() + ctx.mute true + isMuted = true - of 'm', 'M': - if isMuted: - #if nowPlayingExcept != true: - #[cursorUp() - eraseLine() - cursorDown() - eraseLine() - #if nowPlayingExcept != true: - cursorUp()]# - - ctx.mute false - #echoPlay = true - isMuted = false - - else: - eraseLine() - #warn "Muted", 4 - cursorUp() - ctx.mute true - isMuted = true - - of '/', '+': - let volumeIncreased = ctx.volume true + of Key.Slash, Key.Plus: + let volumeIncreased = ctx.volume true #[var metadata: NodeList echo "getPropreturnVal:", ctx.getProperty("metadata", fmtNodeMap, addr metadata) @@ -132,40 +123,43 @@ proc call(sub: string; sect = ""; stat, link: string) = for i in 0 .. 100: try:echo "metadatavalues", metadata.values[i] except:discard]# - cursorDown() - warn "Volume+: " & $volumeIncreased - cursorUp() - eraseLine() - cursorUp() + cursorDown() + warn "Volume+: " & $volumeIncreased + cursorUp() + eraseLine() + cursorUp() - of '*', '-': - let volumeDecreased = ctx.volume false + of Key.Asterisk, Key.Minus: + let volumeDecreased = ctx.volume false - cursorDown() - warn "Volume-: " & $volumeDecreased - cursorUp() - eraseLine() - cursorUp() + cursorDown() + warn "Volume-: " & $volumeDecreased + cursorUp() + eraseLine() + cursorUp() - of 'r', 'R': - if not isPaused: terminateDestroy ctx - break - of 'q', 'Q': exit ctx, isPaused - else: inv() + of Key.R: + if not isPaused: terminateDestroy ctx + illwillDeInit() + break + of Key.Q: + illwillDeInit() + exit ctx, isPaused + of Key.None: continue + else: inv() proc initJsonLists(sub, file: string; sect = ""): seq[seq[string]] = var n, l: seq[string] = @[] let input = parseJArray file for f in input.low .. input.high: - case f mod 2: - of 0: n.add input[f] - of 1: + case bool f mod 2: + of false: n.add input[f] + of true: if input[f].startsWith("http://") or input[f].startsWith "https://": l.add input[f] else: l.add "http://" & input[f] - else: discard @[n, l] proc initIndx*(dir = getAppDir() / "assets"): seq[seq[string]] = @@ -179,6 +173,9 @@ proc initIndx*(dir = getAppDir() / "assets"): seq[seq[string]] = else: files.add file var procFile = file procFile.removePrefix(dir & "/".unixToNativePath) + if dir != appDir & "assets": + var procFile2 = procFile.rsplit("/".unixToNativePath) + procFile = procFile2[procFile2.high] procFile[0] = procFile[0].toUpperAscii procFile.removeSuffix ".json" if dir == appDir & "assets": @@ -202,7 +199,6 @@ proc menu(sub, file: string; sect = "") = if sub.endsWith "/".unixToNativePath: drawMainMenu(getAppDir() / "assets" / sub) return - #echo sub let list = initJsonLists(sub, file, sect) n = list[0] @@ -211,9 +207,7 @@ proc menu(sub, file: string; sect = "") = while true: var returnBack = false drawMenu sub, n, sect - #echo l - #add conditiinal check for every if len not thereown size - #else no use danger use release + hideCursor() while true: try: case getch(): @@ -246,14 +240,9 @@ proc drawMainMenu*(dir = getAppDir() / "assets") = indx = initIndx dir names = indx[0] files = indx[1] - #TODO menu dynamic selection; only 15 items possible! while true: var returnBack = false clear() - #echo names - #echo files - #echo dirs - #add drawMenu sayTermDraw8() say "Station Categories:", fgGreen sayIter names, ret = if dir != getAppDir() / "assets": true else: false diff --git a/src/player.nim b/src/player.nim index 07a7363..8da59bc 100644 --- a/src/player.nim +++ b/src/player.nim @@ -29,7 +29,7 @@ proc volume*(ctx; a: bool): cint = volumeChanged proc seeIfSongTitleChanges*(ctx) = - cE observeProperty(ctx, 0 , "media-title", fmtNone); + cE observeProperty(ctx, 0 , "media-title", fmtNone) proc getCurrentSongv2*(ctx): cstring = cE getProperty(ctx, "media-title", fmtString, addr result)