AnonymusHochgenuss

Computercraft Streaming Music Program (Version 2.1)

Mar 30th, 2026
42
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. local api_base_url = "https://ipod-2to6magyna-uc.a.run.app/"
  2. local version = "2.1"
  3.  
  4. local width, height = term.getSize()
  5. local tab = 1
  6.  
  7. local waiting_for_input = false
  8. local last_search = nil
  9. local last_search_url = nil
  10. local search_results = nil
  11. local search_error = false
  12. local in_search_result = false
  13. local clicked_result = nil
  14.  
  15. local playing = false
  16. local queue = {}
  17. local now_playing = nil
  18. local looping = 0
  19. local volume = 1.5
  20.  
  21. local playing_id = nil
  22. local last_download_url = nil
  23. local playing_status = 0
  24. local is_loading = false
  25. local is_error = false;
  26.  
  27. local player_handle = nil
  28. local start = nil
  29. local pcm = nil
  30. local size = nil
  31. local decoder = require "cc.audio.dfpwm".make_decoder()
  32. local needs_next_chunk = 0
  33. local buffer
  34.  
  35. local speakers = { peripheral.find("speaker") }
  36. if #speakers == 0 then
  37.     error("No speakers attached. You need to connect a speaker to this computer. If this is an Advanced Noisy Pocket Computer, then this is a bug, and you should try restarting your Minecraft game.", 0)
  38. end
  39.  
  40. function redrawScreen()
  41.     if waiting_for_input then
  42.         return
  43.     end
  44.  
  45.     term.setCursorBlink(false)  -- Make sure cursor is off when redrawing
  46.     -- Clear the screen
  47.     term.setBackgroundColor(colors.black)
  48.     term.clear()
  49.  
  50.     --Draw the three top tabs
  51.     term.setCursorPos(1,1)
  52.     term.setBackgroundColor(colors.gray)
  53.     term.clearLine()
  54.    
  55.     tabs = {" Now Playing ", " Search "}
  56.    
  57.     for i=1,#tabs,1 do
  58.         if tab == i then
  59.             term.setTextColor(colors.black)
  60.             term.setBackgroundColor(colors.white)
  61.         else
  62.             term.setTextColor(colors.white)
  63.             term.setBackgroundColor(colors.gray)
  64.         end
  65.        
  66.         term.setCursorPos((math.floor((width/#tabs)*(i-0.5)))-math.ceil(#tabs[i]/2)+1, 1)
  67.         term.write(tabs[i])
  68.     end
  69.  
  70.     if tab == 1 then
  71.         drawNowPlaying()
  72.     elseif tab == 2 then
  73.         drawSearch()
  74.     end
  75. end
  76.  
  77. function drawNowPlaying()
  78.     if now_playing ~= nil then
  79.         term.setBackgroundColor(colors.black)
  80.         term.setTextColor(colors.white)
  81.         term.setCursorPos(2,3)
  82.         term.write(now_playing.name)
  83.         term.setTextColor(colors.lightGray)
  84.         term.setCursorPos(2,4)
  85.         term.write(now_playing.artist)
  86.     else
  87.         term.setBackgroundColor(colors.black)
  88.         term.setTextColor(colors.lightGray)
  89.         term.setCursorPos(2,3)
  90.         term.write("Not playing")
  91.     end
  92.  
  93.     if is_loading == true then
  94.         term.setTextColor(colors.gray)
  95.         term.setBackgroundColor(colors.black)
  96.         term.setCursorPos(2,5)
  97.         term.write("Loading...")
  98.     elseif is_error == true then
  99.         term.setTextColor(colors.red)
  100.         term.setBackgroundColor(colors.black)
  101.         term.setCursorPos(2,5)
  102.         term.write("Network error")
  103.     end
  104.  
  105.     term.setTextColor(colors.white)
  106.     term.setBackgroundColor(colors.gray)
  107.  
  108.     if playing then
  109.         term.setCursorPos(2, 6)
  110.         term.write(" Stop ")
  111.     else
  112.         if now_playing ~= nil or #queue > 0 then
  113.             term.setTextColor(colors.white)
  114.             term.setBackgroundColor(colors.gray)
  115.         else
  116.             term.setTextColor(colors.lightGray)
  117.             term.setBackgroundColor(colors.gray)
  118.         end
  119.         term.setCursorPos(2, 6)
  120.         term.write(" Play ")
  121.     end
  122.  
  123.     if now_playing ~= nil or #queue > 0 then
  124.         term.setTextColor(colors.white)
  125.         term.setBackgroundColor(colors.gray)
  126.     else
  127.         term.setTextColor(colors.lightGray)
  128.         term.setBackgroundColor(colors.gray)
  129.     end
  130.     term.setCursorPos(2 + 7, 6)
  131.     term.write(" Skip ")
  132.  
  133.     if looping ~= 0 then
  134.         term.setTextColor(colors.black)
  135.         term.setBackgroundColor(colors.white)
  136.     else
  137.         term.setTextColor(colors.white)
  138.         term.setBackgroundColor(colors.gray)
  139.     end
  140.     term.setCursorPos(2 + 7 + 7, 6)
  141.     if looping == 0 then
  142.         term.write(" Loop Off ")
  143.     elseif looping == 1 then
  144.         term.write(" Loop Queue ")
  145.     else
  146.         term.write(" Loop Song ")
  147.     end
  148.  
  149.     term.setCursorPos(2,8)
  150.     paintutils.drawBox(2,8,25,8,colors.gray)
  151.     local width = math.floor(24 * (volume / 3) + 0.5)-1
  152.     if not (width == -1) then
  153.         paintutils.drawBox(2,8,2+width,8,colors.white)
  154.     end
  155.     if volume < 0.6 then
  156.         term.setCursorPos(2+width+2,8)
  157.         term.setBackgroundColor(colors.gray)
  158.         term.setTextColor(colors.white)
  159.     else
  160.         term.setCursorPos(2+width-3-(volume == 3 and 1 or 0),8)
  161.         term.setBackgroundColor(colors.white)
  162.         term.setTextColor(colors.black)
  163.     end
  164.     term.write(math.floor(100 * (volume / 3) + 0.5) .. "%")
  165.  
  166.     if #queue > 0 then
  167.         term.setBackgroundColor(colors.black)
  168.         for i=1,#queue do
  169.             term.setTextColor(colors.white)
  170.             term.setCursorPos(2,10 + (i-1)*2)
  171.             term.write(queue[i].name)
  172.             term.setTextColor(colors.lightGray)
  173.             term.setCursorPos(2,11 + (i-1)*2)
  174.             term.write(queue[i].artist)
  175.         end
  176.     end
  177. end
  178.  
  179. function drawSearch()
  180.     -- Search bar
  181.     paintutils.drawFilledBox(2,3,width-1,5,colors.lightGray)
  182.     term.setBackgroundColor(colors.lightGray)
  183.     term.setCursorPos(3,4)
  184.     term.setTextColor(colors.black)
  185.     term.write(last_search or "Search...")
  186.  
  187.     --Search results
  188.     if search_results ~= nil then
  189.         term.setBackgroundColor(colors.black)
  190.         for i=1,#search_results do
  191.             term.setTextColor(colors.white)
  192.             term.setCursorPos(2,7 + (i-1)*2)
  193.             term.write(search_results[i].name)
  194.             term.setTextColor(colors.lightGray)
  195.             term.setCursorPos(2,8 + (i-1)*2)
  196.             term.write(search_results[i].artist)
  197.         end
  198.     else
  199.         term.setCursorPos(2,7)
  200.         term.setBackgroundColor(colors.black)
  201.         if search_error == true then
  202.             term.setTextColor(colors.red)
  203.             term.write("Network error")
  204.         elseif last_search_url ~= nil then
  205.             term.setTextColor(colors.lightGray)
  206.             term.write("Searching...")
  207.         else
  208.             term.setCursorPos(1,7)
  209.             term.setTextColor(colors.lightGray)
  210.             print("Tip: You can paste YouTube video or playlist links.")
  211.         end
  212.     end
  213.  
  214.     --fullscreen song options
  215.     if in_search_result == true then
  216.         term.setBackgroundColor(colors.black)
  217.         term.clear()
  218.         term.setCursorPos(2,2)
  219.         term.setTextColor(colors.white)
  220.         term.write(search_results[clicked_result].name)
  221.         term.setCursorPos(2,3)
  222.         term.setTextColor(colors.lightGray)
  223.         term.write(search_results[clicked_result].artist)
  224.  
  225.         term.setBackgroundColor(colors.gray)
  226.         term.setTextColor(colors.white)
  227.  
  228.         term.setCursorPos(2,6)
  229.         term.clearLine()
  230.         term.write("Play now")
  231.  
  232.         term.setCursorPos(2,8)
  233.         term.clearLine()
  234.         term.write("Play next")
  235.  
  236.         term.setCursorPos(2,10)
  237.         term.clearLine()
  238.         term.write("Add to queue")
  239.  
  240.         term.setCursorPos(2,13)
  241.         term.clearLine()
  242.         term.write("Cancel")
  243.     end
  244. end
  245.  
  246. function uiLoop()
  247.     redrawScreen()
  248.  
  249.     while true do
  250.         if waiting_for_input then
  251.             parallel.waitForAny(
  252.                 function()
  253.                     term.setCursorPos(3,4)
  254.                     term.setBackgroundColor(colors.white)
  255.                     term.setTextColor(colors.black)
  256.                     local input = read()
  257.  
  258.                     if string.len(input) > 0 then
  259.                         last_search = input
  260.                         last_search_url = api_base_url .. "?v=" .. version .. "&search=" .. textutils.urlEncode(input)
  261.                         http.request(last_search_url)
  262.                         search_results = nil
  263.                         search_error = false
  264.                     else
  265.                         last_search = nil
  266.                         last_search_url = nil
  267.                         search_results = nil
  268.                         search_error = false
  269.                     end
  270.  
  271.                     waiting_for_input = false
  272.                     os.queueEvent("redraw_screen")
  273.                 end,
  274.                 function()
  275.                     while waiting_for_input do
  276.                         local event, button, x, y = os.pullEvent("mouse_click")
  277.                         if y < 3 or y > 5 or x < 2 or x > width-1 then
  278.                             waiting_for_input = false
  279.                             os.queueEvent("redraw_screen")
  280.                             break
  281.                         end
  282.                     end
  283.                 end
  284.             )
  285.         else
  286.             parallel.waitForAny(
  287.                 function()
  288.                     local event, button, x, y = os.pullEvent("mouse_click")
  289.  
  290.                     if button == 1 then
  291.                         -- Tabs
  292.                         if in_search_result == false then
  293.                             if y == 1 then
  294.                                 if x < width/2 then
  295.                                     tab = 1
  296.                                 else
  297.                                     tab = 2
  298.                                 end
  299.                                 redrawScreen()
  300.                             end
  301.                         end
  302.                        
  303.                         if tab == 2 and in_search_result == false then
  304.                             -- Search box click
  305.                             if y >= 3 and y <= 5 and x >= 1 and x <= width-1 then
  306.                                 paintutils.drawFilledBox(2,3,width-1,5,colors.white)
  307.                                 term.setBackgroundColor(colors.white)
  308.                                 waiting_for_input = true
  309.                             end
  310.        
  311.                             -- Search result click
  312.                             if search_results then
  313.                                 for i=1,#search_results do
  314.                                     if y == 7 + (i-1)*2 or y == 8 + (i-1)*2 then
  315.                                         term.setBackgroundColor(colors.white)
  316.                                         term.setTextColor(colors.black)
  317.                                         term.setCursorPos(2,7 + (i-1)*2)
  318.                                         term.clearLine()
  319.                                         term.write(search_results[i].name)
  320.                                         term.setTextColor(colors.gray)
  321.                                         term.setCursorPos(2,8 + (i-1)*2)
  322.                                         term.clearLine()
  323.                                         term.write(search_results[i].artist)
  324.                                         sleep(0.2)
  325.                                         in_search_result = true
  326.                                         clicked_result = i
  327.                                         redrawScreen()
  328.                                     end
  329.                                 end
  330.                             end
  331.                         elseif tab == 2 and in_search_result == true then
  332.                             -- Search result menu clicks
  333.        
  334.                             term.setBackgroundColor(colors.white)
  335.                             term.setTextColor(colors.black)
  336.        
  337.                             if y == 6 then
  338.                                 term.setCursorPos(2,6)
  339.                                 term.clearLine()
  340.                                 term.write("Play now")
  341.                                 sleep(0.2)
  342.                                 in_search_result = false
  343.                                 for _, speaker in ipairs(speakers) do
  344.                                     speaker.stop()
  345.                                     os.queueEvent("playback_stopped")
  346.                                 end
  347.                                 playing = true
  348.                                 is_error = false
  349.                                 playing_id = nil
  350.                                 if search_results[clicked_result].type == "playlist" then
  351.                                     now_playing = search_results[clicked_result].playlist_items[1]
  352.                                     queue = {}
  353.                                     if #search_results[clicked_result].playlist_items > 1 then
  354.                                         for i=2, #search_results[clicked_result].playlist_items do
  355.                                             table.insert(queue, search_results[clicked_result].playlist_items[i])
  356.                                         end
  357.                                     end
  358.                                 else
  359.                                     now_playing = search_results[clicked_result]
  360.                                 end
  361.                                 os.queueEvent("audio_update")
  362.                             end
  363.        
  364.                             if y == 8 then
  365.                                 term.setCursorPos(2,8)
  366.                                 term.clearLine()
  367.                                 term.write("Play next")
  368.                                 sleep(0.2)
  369.                                 in_search_result = false
  370.                                 if search_results[clicked_result].type == "playlist" then
  371.                                     for i = #search_results[clicked_result].playlist_items, 1, -1 do
  372.                                         table.insert(queue, 1, search_results[clicked_result].playlist_items[i])
  373.                                     end
  374.                                 else
  375.                                     table.insert(queue, 1, search_results[clicked_result])
  376.                                 end
  377.                                 os.queueEvent("audio_update")
  378.                             end
  379.        
  380.                             if y == 10 then
  381.                                 term.setCursorPos(2,10)
  382.                                 term.clearLine()
  383.                                 term.write("Add to queue")
  384.                                 sleep(0.2)
  385.                                 in_search_result = false
  386.                                 if search_results[clicked_result].type == "playlist" then
  387.                                     for i = 1, #search_results[clicked_result].playlist_items do
  388.                                         table.insert(queue, search_results[clicked_result].playlist_items[i])
  389.                                     end
  390.                                 else
  391.                                     table.insert(queue, search_results[clicked_result])
  392.                                 end
  393.                                 os.queueEvent("audio_update")
  394.                             end
  395.        
  396.                             if y == 13 then
  397.                                 term.setCursorPos(2,13)
  398.                                 term.clearLine()
  399.                                 term.write("Cancel")
  400.                                 sleep(0.2)
  401.                                 in_search_result = false
  402.                             end
  403.        
  404.                             redrawScreen()
  405.                         elseif tab == 1 and in_search_result == false then
  406.                             -- Now playing tab clicks
  407.        
  408.                             if y == 6 then
  409.                                 -- Play/stop button
  410.                                 if x >= 2 and x < 2 + 6 then
  411.                                     if playing or now_playing ~= nil or #queue > 0 then
  412.                                         term.setBackgroundColor(colors.white)
  413.                                         term.setTextColor(colors.black)
  414.                                         term.setCursorPos(2, 6)
  415.                                         if playing then
  416.                                             term.write(" Stop ")
  417.                                         else
  418.                                             term.write(" Play ")
  419.                                         end
  420.                                         sleep(0.2)
  421.                                     end
  422.                                     if playing then
  423.                                         playing = false
  424.                                         for _, speaker in ipairs(speakers) do
  425.                                             speaker.stop()
  426.                                             os.queueEvent("playback_stopped")
  427.                                         end
  428.                                         playing_id = nil
  429.                                         is_loading = false
  430.                                         is_error = false
  431.                                         os.queueEvent("audio_update")
  432.                                     elseif now_playing ~= nil then
  433.                                         playing_id = nil
  434.                                         playing = true
  435.                                         is_error = false
  436.                                         os.queueEvent("audio_update")
  437.                                     elseif #queue > 0 then
  438.                                         now_playing = queue[1]
  439.                                         table.remove(queue, 1)
  440.                                         playing_id = nil
  441.                                         playing = true
  442.                                         is_error = false
  443.                                         os.queueEvent("audio_update")
  444.                                     end
  445.                                 end
  446.        
  447.                                 -- Skip button
  448.                                 if x >= 2 + 7 and x < 2 + 7 + 6 then
  449.                                     if now_playing ~= nil or #queue > 0 then
  450.                                         term.setBackgroundColor(colors.white)
  451.                                         term.setTextColor(colors.black)
  452.                                         term.setCursorPos(2 + 7, 6)
  453.                                         term.write(" Skip ")
  454.                                         sleep(0.2)
  455.        
  456.                                         is_error = false
  457.                                         if playing then
  458.                                             for _, speaker in ipairs(speakers) do
  459.                                                 speaker.stop()
  460.                                                 os.queueEvent("playback_stopped")
  461.                                             end
  462.                                         end
  463.                                         if #queue > 0 then
  464.                                             if looping == 1 then
  465.                                                 table.insert(queue, now_playing)
  466.                                             end
  467.                                             now_playing = queue[1]
  468.                                             table.remove(queue, 1)
  469.                                             playing_id = nil
  470.                                         else
  471.                                             now_playing = nil
  472.                                             playing = false
  473.                                             is_loading = false
  474.                                             is_error = false
  475.                                             playing_id = nil
  476.                                         end
  477.                                         os.queueEvent("audio_update")
  478.                                     end
  479.                                 end
  480.        
  481.                                 -- Loop button
  482.                                 if x >= 2 + 7 + 7 and x < 2 + 7 + 7 + 12 then
  483.                                     if looping == 0 then
  484.                                         looping = 1
  485.                                     elseif looping == 1 then
  486.                                         looping = 2
  487.                                     else
  488.                                         looping = 0
  489.                                     end
  490.                                 end
  491.                             end
  492.  
  493.                             if y == 8 then
  494.                                 -- Volume slider
  495.                                 if x >= 1 and x < 2 + 24 then
  496.                                     volume = (x - 1) / 24 * 3
  497.  
  498.                                     -- for _, speaker in ipairs(speakers) do
  499.                                     --  speaker.stop()
  500.                                     --  os.queueEvent("playback_stopped")
  501.                                     -- end
  502.                                     -- playing_id = nil
  503.                                     -- os.queueEvent("audio_update")
  504.                                 end
  505.                             end
  506.  
  507.                             redrawScreen()
  508.                         end
  509.                     end
  510.                 end,
  511.                 function()
  512.                     local event, button, x, y = os.pullEvent("mouse_drag")
  513.  
  514.                     if button == 1 then
  515.  
  516.                         if tab == 1 and in_search_result == false then
  517.  
  518.                             if y >= 7 and y <= 9 then
  519.                                 -- Volume slider
  520.                                 if x >= 1 and x < 2 + 24 then
  521.                                     volume = (x - 1) / 24 * 3
  522.  
  523.                                     -- for _, speaker in ipairs(speakers) do
  524.                                     --  speaker.stop()
  525.                                     --  os.queueEvent("playback_stopped")
  526.                                     -- end
  527.                                     -- playing_id = nil
  528.                                     -- os.queueEvent("audio_update")
  529.                                 end
  530.                             end
  531.  
  532.                             redrawScreen()
  533.                         end
  534.                     end
  535.                 end,
  536.                 function()
  537.                     local event = os.pullEvent("redraw_screen")
  538.  
  539.                     redrawScreen()
  540.                 end
  541.             )
  542.         end
  543.     end
  544. end
  545.  
  546. function audioLoop()
  547.     while true do
  548.  
  549.         -- AUDIO
  550.         if playing and now_playing then
  551.             local thisnowplayingid = now_playing.id
  552.             if playing_id ~= thisnowplayingid then
  553.                 playing_id = thisnowplayingid
  554.                 last_download_url = api_base_url .. "?v=" .. version .. "&id=" .. textutils.urlEncode(playing_id)
  555.                 playing_status = 0
  556.                 needs_next_chunk = 1
  557.  
  558.                 http.request({url = last_download_url, binary = true})
  559.                 is_loading = true
  560.  
  561.                 os.queueEvent("redraw_screen")
  562.                 os.queueEvent("audio_update")
  563.             elseif playing_status == 1 and needs_next_chunk == 1 then
  564.  
  565.                 while true do
  566.                     local chunk = player_handle.read(size)
  567.                     if not chunk then
  568.                         if looping == 2 or (looping == 1 and #queue == 0) then
  569.                             playing_id = nil
  570.                         elseif looping == 1 and #queue > 0 then
  571.                             table.insert(queue, now_playing)
  572.                             now_playing = queue[1]
  573.                             table.remove(queue, 1)
  574.                             playing_id = nil
  575.                         else
  576.                             if #queue > 0 then
  577.                                 now_playing = queue[1]
  578.                                 table.remove(queue, 1)
  579.                                 playing_id = nil
  580.                             else
  581.                                 now_playing = nil
  582.                                 playing = false
  583.                                 playing_id = nil
  584.                                 is_loading = false
  585.                                 is_error = false
  586.                             end
  587.                         end
  588.  
  589.                         os.queueEvent("redraw_screen")
  590.  
  591.                         player_handle.close()
  592.                         needs_next_chunk = 0
  593.                         break
  594.                     else
  595.                         if start then
  596.                             chunk, start = start .. chunk, nil
  597.                             size = size + 4
  598.                         end
  599.                
  600.                         buffer = decoder(chunk)
  601.                        
  602.                         local fn = {}
  603.                         for i, speaker in ipairs(speakers) do
  604.                             fn[i] = function()
  605.                                 local name = peripheral.getName(speaker)
  606.                                 if #speakers > 1 then
  607.                                     if speaker.playAudio(buffer, volume) then
  608.                                         parallel.waitForAny(
  609.                                             function()
  610.                                                 repeat until select(2, os.pullEvent("speaker_audio_empty")) == name
  611.                                             end,
  612.                                             function()
  613.                                                 local event = os.pullEvent("playback_stopped")
  614.                                                 return
  615.                                             end
  616.                                         )
  617.                                         if not playing or playing_id ~= thisnowplayingid then
  618.                                             return
  619.                                         end
  620.                                     end
  621.                                 else
  622.                                     while not speaker.playAudio(buffer, volume) do
  623.                                         parallel.waitForAny(
  624.                                             function()
  625.                                                 repeat until select(2, os.pullEvent("speaker_audio_empty")) == name
  626.                                             end,
  627.                                             function()
  628.                                                 local event = os.pullEvent("playback_stopped")
  629.                                                 return
  630.                                             end
  631.                                         )
  632.                                         if not playing or playing_id ~= thisnowplayingid then
  633.                                             return
  634.                                         end
  635.                                     end
  636.                                 end
  637.                                 if not playing or playing_id ~= thisnowplayingid then
  638.                                     return
  639.                                 end
  640.                             end
  641.                         end
  642.                        
  643.                         local ok, err = pcall(parallel.waitForAll, table.unpack(fn))
  644.                         if not ok then
  645.                             needs_next_chunk = 2
  646.                             is_error = true
  647.                             break
  648.                         end
  649.                        
  650.                         -- If we're not playing anymore, exit the chunk processing loop
  651.                         if not playing or playing_id ~= thisnowplayingid then
  652.                             break
  653.                         end
  654.                     end
  655.                 end
  656.                 os.queueEvent("audio_update")
  657.             end
  658.         end
  659.  
  660.         os.pullEvent("audio_update")
  661.     end
  662. end
  663.  
  664. function httpLoop()
  665.     while true do
  666.         parallel.waitForAny(
  667.             function()
  668.                 local event, url, handle = os.pullEvent("http_success")
  669.  
  670.                 if url == last_search_url then
  671.                     search_results = textutils.unserialiseJSON(handle.readAll())
  672.                     os.queueEvent("redraw_screen")
  673.                 end
  674.                 if url == last_download_url then
  675.                     is_loading = false
  676.                     player_handle = handle
  677.                     start = handle.read(4)
  678.                     size = 16 * 1024 - 4
  679.                     playing_status = 1
  680.                     os.queueEvent("redraw_screen")
  681.                     os.queueEvent("audio_update")
  682.                 end
  683.             end,
  684.             function()
  685.                 local event, url = os.pullEvent("http_failure")
  686.  
  687.                 if url == last_search_url then
  688.                     search_error = true
  689.                     os.queueEvent("redraw_screen")
  690.                 end
  691.                 if url == last_download_url then
  692.                     is_loading = false
  693.                     is_error = true
  694.                     playing = false
  695.                     playing_id = nil
  696.                     os.queueEvent("redraw_screen")
  697.                     os.queueEvent("audio_update")
  698.                 end
  699.             end
  700.         )
  701.     end
  702. end
  703.  
  704. parallel.waitForAny(uiLoop, audioLoop, httpLoop)
Add Comment
Please, Sign In to add comment