* 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
This commit is contained in:
parent
faf2eadcf8
commit
791ff29e20
|
@ -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
|
||||
|
|
511
src/illwill.nim
Normal file
511
src/illwill.nim
Normal file
|
@ -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
|
|
@ -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://"
|
||||
|
|
251
src/menu.nim
251
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
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in a new issue