streamripper-on3.lua 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. #!/usr/bin/env lua
  2. --[[
  3. -- Copyright (c) 2013-2016 Marcus Rohrmoser, http://purl.mro.name/recorder
  4. --
  5. -- Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
  6. -- associated documentation files (the "Software"), to deal in the Software without restriction,
  7. -- including without limitation the rights to use, copy, modify, merge, publish, distribute,
  8. -- sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
  9. -- furnished to do so, subject to the following conditions:
  10. --
  11. -- The above copyright notice and this permission notice shall be included in all copies or
  12. -- substantial portions of the Software.
  13. --
  14. -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
  15. -- NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  16. -- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
  17. -- OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  18. -- CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  19. --
  20. -- MIT License http://opensource.org/licenses/MIT
  21. ]]
  22. if os.getenv('HTTP_HOST') then
  23. io.write('HTTP/1.1 400 Bad Request', '\n')
  24. io.write('Content-Type: text/plain', '\n')
  25. io.write('Server: RadioPi 2013/lua', '\n')
  26. io.write('\n', 'I\'m not supposed to be run as cgi')
  27. io.write('\n')
  28. io.flush()
  29. os.exit(1)
  30. end
  31. require 'luarocks.loader' -- http://www.luarocks.org/en/Using_LuaRocks
  32. --
  33. -- grab artist + title from http://www.br.de/on3/welle116~liveHeader.jsp
  34. --
  35. http = require 'socket.http'
  36. local function scrape_playlist_tracks(now)
  37. local t0 = now or os.time()
  38. local d0 = os.date('*t', t0)
  39. local b,c,h = http.request('http://www.br.de/on3/welle116~liveHeader.jsp')
  40. local rows = {}
  41. for clz,txt in b:gmatch('class="playlist_([^"]+)"[^>]*>([^<]*)</') do
  42. -- io.stderr:write(clz,txt,"\n")
  43. table.insert(rows, { [clz] = txt })
  44. end
  45. -- group 3 entries into one, ascending acc. time
  46. assert( #rows % 3 == 0)
  47. local play_list = {}
  48. for i = #rows,3,-3 do
  49. local item = {}
  50. for j = 0,2,1 do for k,v in pairs(rows[i-j]) do item[k] = v end end
  51. if item.time then
  52. local h,m = item.time:match('(%d%d):(%d%d) Uhr')
  53. local time = {
  54. year = d0.year,
  55. month = d0.month,
  56. day = d0.day,
  57. hour = assert(tonumber(h)),
  58. min = assert(tonumber(m)),
  59. }
  60. -- assume tracks to be less than 11h...
  61. if d0.hour > time.hour + 11 then time.day = time.day + 1 end
  62. item.time = os.time(time)
  63. item.artist = assert(item.interpret)
  64. item.interpret = nil
  65. table.insert(play_list, 1, item)
  66. end
  67. end
  68. return play_list
  69. end
  70. local psx = require'posix'
  71. local function msleep(msecs)
  72. psx.nanosleep(msecs / 1000, (msecs % 1000) * 1000)
  73. end
  74. local stream_delay_sec = 3
  75. local last_scraped = 0
  76. local track = {}
  77. -- endless loop
  78. while true do
  79. local now = os.time() - stream_delay_sec
  80. if track.future and now >= track.future.time then
  81. io.stderr:write('switch: ', track.current.title, "\n")
  82. track.current,track.future = track.future,nil
  83. elseif not track.future and os.difftime(now,last_scraped) >= 30 then -- no more often than 30sec
  84. io.stderr:write('scrape..', "\n")
  85. track.future = nil
  86. for _,tr in ipairs( scrape_playlist_tracks(now) ) do
  87. io.stderr:write('> ', os.date('%H:%M', tr.time), ' ', tr.title, ' - ', tr.artist, "\n")
  88. if tr.time <= now then track.current = tr
  89. elseif not track.future then track.future = tr
  90. end
  91. end
  92. last_scraped = now
  93. io.stderr:write('current ', os.date('%H:%M', track.current.time), ' ', track.current.title, ' - ', track.current.artist, "\n")
  94. if track.future then
  95. io.stderr:write('future ', os.date('%H:%M', track.future.time), ' ', track.future.title, ' - ', track.future.artist, "\n")
  96. end
  97. end
  98. io.write('ARTIST', '=', track.current.artist, "\n")
  99. io.write('ALBUM', '=', 'streamripper', "\n")
  100. io.write('TITLE', '=', track.current.title, "\n")
  101. io.write(".\n")
  102. io.flush()
  103. local dt = 1
  104. msleep( 1000 * math.max(0.01, math.min(0.1, dt) ) ) -- write every .1 second, but no more than every 1/100th sec
  105. end