Murga-Projects Forums

Full Version: access to media files on a LAN server
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
I believe I've come up with a kludge for serving subtitles, although my current idea might work only for mplayer, and I'm dealing only with *.srt for now. I don't yet know anything about how another player might specify a subtitle file as part of the command. I might need to create a separate user-configurable variable for that.

Anyway, what I'm thinking (still thinking about it as I type, so nothing's been coded yet) is to create a subtitle filename string by replacing the filename extension with ".srt" and search body for that string. If it's found, then do another http.request for that subtitle file, write the string returned by that request to a tempfile, and use that tempfile as part of the mplayer command. This would be done at the start of the play function.

If it works for me I'll post the initial code, and then do some research on how other media players handle subtitles on the commandline.
This one works with mplayer, and probably smplayer.
For VLC, subflag=" --sub="..subtmp.." " should work.
In Media player Classic, apparently use subflag=" /sub "..subtmp.." " (might need extra quotes around the filename?)
I don't think Windows Media Player will work.

Code:
#!/usr/bin/env murgaLua

-- access media files on a file server & stream in a player
-- mikshaw 2010

--player="C:\\Progra~1\\SMPlayer\\smplayer.exe" -- Windows
player="mplayer" -- Linux
host="http://localhost/media"


ext= { "mp3", "ogg", "flac", "avi", "mp4", "mov", "divx", "flv",
"mkv", "asf", "mpg", "mpeg", "wmv", "wav" }

subtmp=os.tmpname()

function get_menu()
level=0
dir=host
get_files()
box:remove(1) -- get rid of the ../
while pos[1] do table.remove(pos) end
box:value(pos[level])
end

function get_files()
box:clear()
body,code,header=socket.http.request(dir)
for l in string.gmatch(body,"<a href=.-\n") do
filename=string.gsub(l,"<a href=\"(.-)\".*","%1")
filesize=string.gsub(l,".*>([%d%.]-[KMG])<.*","%1")
  if string.find(filename,"\/$") then box:add(filename) end
  for k,v in pairs(ext) do
    if string.find(string.lower(filename),"%."..v.."$") then
      box:add(filename.."\t"..filesize)
      break
    end
  end
end
fresh:activate()
if not pos[level] then pos[level]=1 end
box:value(pos[level])
end

function play()
realfile=string.gsub(box:text(box:value()),"\t.*","")
subfile=string.gsub(realfile,"%.%w-$","%.srt")
if string.find(body,"<a href=\""..subfile.."\"") then
subdata,y,z=socket.http.request(dir.."/"..subfile)
my_filename=io.open(subtmp,"w")
if my_filename then
  my_filename:write(subdata)
  my_filename:close()
end
subflag=" -sub "..subtmp.." "
else subflag=" "
end
win:iconize()
Fl:check()
os.execute(player..subflag..dir.."/"..realfile)
end

function go_back()
dir=string.gsub(dir,"/[^/]-/$","")
level=level-1
if host==dir then get_menu()
else
get_files()
end
end

function go_forward()
dir=dir.."/"..box:text(box:value())
level=level+1
get_files()
end

function dubbleclik()
if Fl:event() == fltk.FL_RELEASE and Fl:event_clicks() > 0 then
navigate()
end
end

function navigate()
if box:value() > 0 then
pos[level]=box:value()
  if box:text(box:value())=="../" then go_back()
  elseif string.find(box:text(box:value()),"\/$") then go_forward()
  else play() end
end
end

function quit()
os.remove(subtmp)
os.exit()
end

ww,wh,bh=550,400,25
bw=ww/3
win=fltk:Fl_Double_Window(ww,wh,"Movies")
win:callback(quit)
box=fltk:Fl_Hold_Browser(0,0,ww,wh); box:callback(dubbleclik)
box:column_widths({ww-100,100,0})
pack=fltk.Fl_Pack(0,wh,wh,bh)
pack:type(1)
go=fltk:Fl_Return_Button(0,0,bw,bh,"Open"); go:callback(navigate)
fresh=fltk:Fl_Button(0,0,bw,bh,"&Menu"); fresh:callback(get_menu)
fltk:Fl_End()

pos={}
pos[0]=1
get_menu()

win:resizable(box)
win:show()
Fl:run()

The subtitle feature seems to be a little flaky on Windows smplayer, don't know about Mac. I have two movies that include *.srt files; both work on Linux mplayer, but one works on Windows smplayer.

The current version allows for an optional configuration file, so you can change the player and host once and not have to do it again every time the script is updated. The file's format is lua variables (the file is parsed as a lua chunk).

The current available identifiers are player, subtitle_flag, and host.
I have not found a way yet in Windows to use the standard path name (including stupid spaces) for player, so I'm using "Progra~1\\...." (also note the escaped backslash).
The subtitle_flag variable only comes into play when srt files are found, and it might not work as currently scripted for a player that uses the "flag=value" format. The whole subtitle feature is a bit messy at this time. It will probably cause problems with some players.
The host variable is a URL pointing to the directory containing the server's media files, not including the trailing slash (though I don't yet know how a trailing slash might affect the script).

Note, as I probably said before, that even little changes in the way a server indexes directories might determine whether or not this script fails. I have tried to improve that part a bit, but i'm sure it's far from perfect.

This version also adds an update feature. The way this feature works will probably seem strange (updates are retrieved from your own server), but it was added only so that I could more easily update the same script on two different OSs for testing. It does not get updates from the internet.

Code:
#!/usr/bin/env murgaLua

-- access media files on a file server & stream in a player
-- mikshaw 2010

local VERSION=3

-- check for configuration file
local myos,cfg=murgaLua.getHostOsName()
if myos=="windows" then cfg=os.getenv("USERPROFILE").."\\media.lua.cfg"
else cfg=os.getenv("HOME").."/.media.lua.cfg" end
local get_cfg=loadfile(cfg)
if get_cfg then get_cfg() end

-- apply configuration variables
if myos=="windows" then
player=player or "C:\\Progra~1\\SMPlayer\\smplayer.exe"
else
player=player or "mplayer"
end
subtitle_flag=subtitle_flag or "-sub"
host=host or "http://192.168.1.101/media"

function update_me()
local remote_file=host.."/media.lua"
local body,code,header=socket.http.request(remote_file)
if code~=200 then fltk.fl_alert("I couldn't check for updates for some reason.\nNo biggie.")
return end
local current_version=tonumber(string.match(body,"VERSION=([%d]*)"))
if current_version > VERSION then
  local ask=fltk.fl_choice("There is a newer version available.\nDo you want to update?","no","yes",NULL)
  if ask==1 then
    local outpoot=io.open(arg[0],"w")
    if outpoot then
      outpoot:write(body)
      outpoot:close()
      fltk.fl_message("Update successful.\nPlease restart the program.")
    else fltk.fl_alert("Sorry, but I couldn't update your script.\n You can try to download it yourself at "..remote_file) end
  end
else fltk.fl_message("You have the latest version.") end
end

ext= { "mp3", "ogg", "flac", "avi", "mp4", "mov", "divx", "flv",
"mkv", "asf", "mpg", "mpeg", "wmv", "wav" }

subtmp=os.tmpname()

function get_menu()
level=0
dir=host
get_files()
box:remove(1) -- get rid of the ../
while pos[1] do table.remove(pos) end
box:value(pos[level])
end

function get_files()
box:clear()
body,code,header=socket.http.request(dir)
for l in string.gmatch(body,"<a href=.-\n") do
filename=string.gsub(l,"<a href=\"(.-)\".*","%1")
filesize=string.gsub(l,".*>([%d%.]-[KMG])<.*","%1")
  if string.find(filename,"\/$") then box:add(filename) end
  for k,v in pairs(ext) do
    if string.find(string.lower(filename),"%."..v.."$") then
      box:add(filename.."\t"..filesize)
      break
    end
  end
end
fresh:activate()
if not pos[level] then pos[level]=1 end
box:value(pos[level])
end

function play()
realfile=string.gsub(box:text(box:value()),"\t.*","")
subfile=string.gsub(realfile,"%.%w-$","%.srt")
if string.find(body,"<a href=\""..subfile.."\"") then
subdata,y,z=socket.http.request(dir.."/"..subfile)
my_filename=io.open(subtmp,"w")
if my_filename then
  my_filename:write(subdata)
  my_filename:close()
end
subflag=" "..subtitle_flag.." "..subtmp.." "
else subflag=" "
end
win:iconize()
Fl:check()
os.execute(player..subflag..dir.."/"..realfile)
win:show()
end

function go_back()
dir=string.gsub(dir,"/[^/]-/$","")
level=level-1
if host==dir then get_menu()
else
get_files()
end
end

function go_forward()
dir=dir.."/"..box:text(box:value())
level=level+1
get_files()
end

function dubbleclik()
if Fl:event() == fltk.FL_RELEASE and Fl:event_clicks() > 0 then
navigate()
end
end

function navigate()
if box:value() > 0 then
pos[level]=box:value()
  if box:text(box:value())=="../" then go_back()
  elseif string.find(box:text(box:value()),"\/$") then go_forward()
  else play() end
end
end

function quit()
os.remove(subtmp)
os.exit()
end

ww,wh,bh=550,400,25
win=fltk:Fl_Double_Window(ww,wh,"Movies")
win:callback(quit)
box=fltk:Fl_Hold_Browser(0,0,ww,wh); box:callback(dubbleclik)
box:column_widths({ww-100,100,0})
go=fltk:Fl_Return_Button(0,0,0,0,"Open"); go:callback(navigate)
fresh=fltk:Fl_Button(0,0,0,0,"&Menu"); fresh:callback(get_menu)
update=fltk:Fl_Button(0,0,0,0,"&Update"); update:callback(update_me)

pos={}
pos[0]=1
get_menu()

win:resizable(box)
win:show()
Fl:run()

I changed the host to myself and this to get it to work on Mac with VLC player.

Code:
-- apply configuration variables
if myos=="windows" then
player=player or "C:\\Progra~1\\SMPlayer\\smplayer.exe"
elseif myos=="macos" then player=player or  "/Applications/VLC.app/Contents/MacOS/VLC"
else
player=player or  "mplayer"
end


Works ok.
But gets stuck after playing each movie for a very long time.
Is ox.execute() blocking?

I'm not sure what you mean by blocking. In its current state it does prevent access to the murgaLua interface while the player is running....don't know if the player on mac is remaining open after the file is finished? If so, adding "&" at the end of the execute string might help (I don't think this works on windows).

I'm planning eventually to work out my trouble with sys.spawn to replace os.execute, so maybe that will help too.

The most recent version posted supports a config file ($HOME/.media.lua.cfg) where you can set the player variable, so you don't really need to change the script to do this, but I'll include the macos default in the next version. Is that path you're using a common one on mac? Also, does VLC on Mac use the --sub=filename option, or something else?

mikshaw Wrote:
I'm not sure what you mean by blocking.

Parent program ignores input and updates Ëœuntil child returns/exits.

mikshaw Wrote:
If so, adding "&" at the end of the execute string might help

The what line where now?

mikshaw Wrote:
Is that path you're using a common one on mac?


/Applications/
is the standard install directory.

Most applications are self contained, ie, VLC.app is technically a folder:
/Applications/VLC.app/

Binary executable have a standard location in that path:
/Contents/MacOS/VLC

Double clicking the 'folder' executes the binary and it knows where all its required elements are.

Quicktime.app would be the standard media player..or ..iTunes.app

mikshaw Wrote:
[quote]
Also, does VLC on Mac use the --sub=filename option, or something else?


I have no idea, really.
But here some links:
VLC command line index online
VLC on Mac OS X

Quote:
The what line where now?

In function play(), the os.execute line:
os.execute(player..subflag..dir.."/"..realfile.." &")
Adding an ampersand to the end of the command (in Linux/UNIX/OSX) runs the command in the background, detaching the player process from the script process and allowing interaction with the GUI even while the movie plays. I don't know if this is the issue, though.

Ah yes that made it work much better. Perfect.
Some changes/improvements...
* Moved the temp files into user directory...it seems Vista was having trouble writing to the files when using os.tmpname().
* Added checks to see if the server and media directory are available (separated host and mediadir variables to accomplish this).
* Added a "Play all" button to create and play a temporary playlist of all files in the current directory.
* Fixed the subtitle bug...filenames with dashes were being misinterpreted by string.find().
* Now using ltn12.sink.file() to write subtitle file to local directory.

Code:
#!/usr/bin/env murgaLua

-- access media files on a http server & stream in a player
-- mikshaw 2010

local VERSION=5

-- check for configuration file
myos=murgaLua.getHostOsName()
if myos=="windows" then cfg=os.getenv("USERPROFILE").."\\media.lua.cfg"
else cfg=os.getenv("HOME").."/.media.lua.cfg" end
local get_cfg=loadfile(cfg)
if get_cfg then get_cfg() end

-- apply configuration variables
if myos=="windows" then
player=player or "C:\\Progra~1\\SMPlayer\\smplayer.exe"
pls_flag=pls_flag or ""
else
player=player or "mplayer"
pls_flag=pls_flag or "-playlist"
end
subtitle_flag=subtitle_flag or "-sub"
host=host or "192.168.1.99"
mediadir=mediadir or "/media"

-- check is server is available
testy=socket.protect(function()
  local c = socket.connect(host, 80)
  if not c then fltk.fl_alert("server not found!"); os.exit()
  else c:close() end
end)
testy()

host="http://"..host..mediadir
-- check if the media dir is available
local b,c=socket.http.request(host)
if c~=200 then fltk.fl_alert("media not available"); os.exit() end

pls={}
ext= { "mp3", "ogg", "ogv", "flac", "avi", "mp4", "mov", "divx", "flv",
"mkv", "m4v", "asf", "mpg", "mpeg", "wmv", "wav" }

-- os.tmpname() seems to be unreliable, particularly in Vista
if myos=="windows" then
  plstmp=os.getenv("USERPROFILE").."\\media.lua.pls"
  subtmp=os.getenv("USERPROFILE").."\\media.lua.sub"
else
  plstmp=os.getenv("HOME").."/media.lua.pls"
  subtmp=os.getenv("HOME").."/media.lua.sub"
end

function update_me()
local remote_file=host.."/media.lua"
local body,code,header=socket.http.request(remote_file)
if code~=200 then fltk.fl_alert("I couldn't check for updates for some reason.\nNo biggie.")
return end
local current_version=tonumber(string.match(body,"VERSION=([%d]*)"))
if current_version > VERSION then
  local ask=fltk.fl_choice("There is a newer version available.\nDo you want to update?","no","yes",NULL)
  if ask==1 then
    local outpoot=io.open(arg[0],"w")
    if outpoot then
      outpoot:write(body)
      outpoot:close()
      fltk.fl_message("Update successful.\nPlease restart the program.")
    else fltk.fl_alert("Sorry, but I couldn't update your script.\n You can try to download it yourself at "..remote_file) end
  end
else fltk.fl_message("You have the latest version.") end
end

-- "menu" is a misnomer carried over from earlier version...really just resets to root directory
function get_menu()
level=0
dir=host
get_files()
box:remove(1) -- get rid of the ../
while pos[1] do table.remove(pos) end
box:value(pos[level])
end

function get_files()
box:clear()
pls={}
body,code,header=socket.http.request(dir)
for l in string.gmatch(body,"<a href=.-\n") do
filename=string.gsub(l,"<a href=\"(.-)\".*","%1")
filesize=string.gsub(l,".*>([%d%.]-[KMG])<.*","%1") -- server-specific
  if string.find(filename,"\/$") then box:add(filename) end
  for k,v in pairs(ext) do
    if string.find(string.lower(filename),"%."..v.."$") then
      box:add(filename.."\t"..filesize)
      table.insert(pls,dir.."/"..filename)
      break
    end
  end
end
fresh:activate()
if not pos[level] then pos[level]=1 end
box:value(pos[level])
end

function play()
realfile=string.gsub(box:text(box:value()),"\t.*","")
subfile=string.gsub(realfile,"%.%w-$","%.srt")
if string.find(body,subfile,1,1) then
socket.http.request{
url=dir.."/"..subfile,
sink=ltn12.sink.file(io.open(subtmp,"w"))
}
subflag=" "..subtitle_flag.." "..subtmp.." "
else subflag=" "
end
win:hide()
Fl:check()
os.execute(player..subflag..dir.."/"..realfile)
os.remove(subtmp)
win:show()
end

function play_list()
if not pls[1] then return end
local playlist=io.open(plstmp,"w")
if playlist then
  playlist:write("[playlist]\nNumberOfEntries="..table.maxn(pls).."\n")
  for i,v in pairs(pls) do playlist:write("File"..i.."="..v.."\n") end
  playlist:close()
  win:hide()
  Fl:check()
  os.execute(player.." "..pls_flag.." "..plstmp)
  win:show()
else fltk.fl_message("Sorry, but I couldn't write a playlist")
end
end

function go_back()
dir=string.gsub(dir,"/[^/]-/$","")
level=level-1
if host==dir then get_menu()
else
get_files()
end
end

function go_forward()
dir=dir.."/"..box:text(box:value())
level=level+1
get_files()
end

function dubbleclik()
if Fl:event() == fltk.FL_RELEASE and Fl:event_clicks() > 0 then
navigate()
end
end

function navigate()
if box:value() > 0 then
pos[level]=box:value()
  if box:text(box:value())=="../" then go_back()
  elseif string.find(box:text(box:value()),"\/$") then go_forward()
  else play() end
end
end

function quit()
os.remove(subtmp)
os.remove(plstmp)
os.exit()
end

ww,wh,bw,bh=640,400,160,25
win=fltk:Fl_Double_Window(ww,wh,"Movies")
win:callback(quit)
box=fltk:Fl_Hold_Browser(0,0,ww,wh-bh); box:callback(dubbleclik)
box:column_widths({ww-bw,bw,0})
go=fltk:Fl_Return_Button(0,0,0,0,"Open"); go:callback(navigate)
fresh=fltk:Fl_Button(0,wh-bh,bw,bh,"back to &Main"); fresh:callback(get_menu)
playall=fltk:Fl_Button(bw,wh-bh,bw,bh,"&Play all"); playall:callback(play_list)
update=fltk:Fl_Button(bw*2,wh-bh,bw,bh,"check for &Updates"); update:callback(update_me)
exit=fltk:Fl_Button(bw*3,wh-bh,bw,bh,"&Quit"); exit:callback(quit)

pos={}
pos[0]=1
get_menu()

win:resizable(box)
win:show()
Fl:run()

One bug fix. I just noticed that it chokes on filenames that contain special characters, parentheses for example, so I surrounded the filename with quotes in the play() function. hasn't been tested on Windows.

Code:
#!/usr/bin/env murgaLua

-- access media files on a http server & stream in a player
-- mikshaw 2010-11

local VERSION=6
Fl:get_system_colors()

-- check for configuration file
myos=murgaLua.getHostOsName()
if myos=="windows" then cfg=os.getenv("USERPROFILE").."\\media.lua.cfg"
else cfg=os.getenv("HOME").."/.media.lua.cfg" end
local get_cfg=loadfile(cfg)
if get_cfg then get_cfg() end

-- apply configuration variables
if myos=="windows" then
player=player or "C:\\Progra~1\\SMPlayer\\smplayer.exe"
pls_flag=pls_flag or ""
else
player=player or "mplayer"
pls_flag=pls_flag or "-playlist"
end
subtitle_flag=subtitle_flag or "-sub"
host=host or "192.168.1.99"
mediadir=mediadir or "/media"

-- check is server is available
testy=socket.protect(function()
  local c = socket.connect(host, 80)
  if not c then fltk.fl_alert("server not found!"); os.exit()
  else c:close() end
end)
testy()

host="http://"..host..mediadir
-- check if the media dir is available
local b,c=socket.http.request(host)
if c~=200 then fltk.fl_alert("media not available"); os.exit() end

pls={}
ext= { "mp3", "ogg", "ogv", "flac", "avi", "mp4", "mov", "divx", "flv",
"mkv", "m4v", "asf", "mpg", "mpeg", "wmv", "wav" }

-- os.tmpname() seems to be unreliable, particularly in Vista
if myos=="windows" then
  plstmp=os.getenv("USERPROFILE").."\\media.lua.pls"
  subtmp=os.getenv("USERPROFILE").."\\media.lua.sub"
else
  plstmp=os.getenv("HOME").."/media.lua.pls"
  subtmp=os.getenv("HOME").."/media.lua.sub"
end

function update_me()
local remote_file=host.."/media.lua"
local body,code,header=socket.http.request(remote_file)
if code~=200 then fltk.fl_alert("I couldn't check for updates for some reason.\nNo biggie.")
return end
local current_version=tonumber(string.match(body,"VERSION=([%d]*)"))
if current_version > VERSION then
  local ask=fltk.fl_choice("There is a newer version available.\nDo you want to update?","no","yes",NULL)
  if ask==1 then
    local outpoot=io.open(arg[0],"w")
    if outpoot then
      outpoot:write(body)
      outpoot:close()
      fltk.fl_message("Update successful.\nPlease restart the program.")
    else fltk.fl_alert("Sorry, but I couldn't update your script.\n You can try to download it yourself at "..remote_file) end
  end
else fltk.fl_message("You have the latest version.") end
end

-- "menu" is a misnomer carried over from earlier version...really just resets to root directory
function get_menu()
level=0
dir=host
get_files()
box:remove(1) -- get rid of the ../
while pos[1] do table.remove(pos) end
box:value(pos[level])
end

function get_files()
box:clear()
pls={}
body,code,header=socket.http.request(dir)
for l in string.gmatch(body,"<a href=.-\n") do
filename=string.gsub(l,"<a href=\"(.-)\".*","%1")
filesize=string.gsub(l,".*>([%d%.]-[KMG])<.*","%1") -- server-specific
  if string.find(filename,"\/$") then box:add(filename) end
  for k,v in pairs(ext) do
    if string.find(string.lower(filename),"%."..v.."$") then
      box:add(filename.."\t"..filesize)
      table.insert(pls,dir.."/"..filename)
      break
    end
  end
end
fresh:activate()
if not pos[level] then pos[level]=1 end
box:value(pos[level])
end

function play()
realfile=string.gsub(box:text(box:value()),"\t.*","")
subfile=string.gsub(realfile,"%.%w-$","%.srt")
if string.find(body,subfile,1,1) then
socket.http.request{
url=dir.."/"..subfile,
sink=ltn12.sink.file(io.open(subtmp,"w"))
}
subflag=" "..subtitle_flag.." "..subtmp.." "
else subflag=" "
end
win:hide()
Fl:check()
-- had to add extra quotes...special characters in filename were causing trouble
os.execute(player..subflag.."\""..dir.."/"..realfile.."\"")
os.remove(subtmp)
win:show()
end

function play_list()
if not pls[1] then return end
local playlist=io.open(plstmp,"w")
if playlist then
  playlist:write("[playlist]\nNumberOfEntries="..table.maxn(pls).."\n")
  for i,v in pairs(pls) do playlist:write("File"..i.."="..v.."\n") end
  playlist:close()
  win:hide()
  Fl:check()
  os.execute(player.." "..pls_flag.." "..plstmp)
  win:show()
else fltk.fl_message("Sorry, but I couldn't write a playlist")
end
end

function go_back()
dir=string.gsub(dir,"/[^/]-/$","")
level=level-1
if host==dir then get_menu()
else
get_files()
end
end

function go_forward()
dir=dir.."/"..box:text(box:value())
level=level+1
get_files()
end

function dubbleclik()
if Fl:event() == fltk.FL_RELEASE and Fl:event_clicks() > 0 then
navigate()
end
end

function navigate()
if box:value() > 0 then
pos[level]=box:value()
  if box:text(box:value())=="../" then go_back()
  elseif string.find(box:text(box:value()),"\/$") then go_forward()
  else play() end
end
end

function quit()
os.remove(subtmp)
os.remove(plstmp)
os.exit()
end

ww,wh,bw,bh=640,400,160,25
win=fltk:Fl_Double_Window(ww,wh,"Movies")
win:callback(quit)
box=fltk:Fl_Hold_Browser(0,0,ww,wh-bh); box:callback(dubbleclik)
box:column_widths({ww-bw,bw,0})
go=fltk:Fl_Return_Button(0,0,0,0,"Open"); go:callback(navigate)
fresh=fltk:Fl_Button(0,wh-bh,bw,bh,"back to &Main"); fresh:callback(get_menu)
playall=fltk:Fl_Button(bw,wh-bh,bw,bh,"&Play all"); playall:callback(play_list)
update=fltk:Fl_Button(bw*2,wh-bh,bw,bh,"check for &Updates"); update:callback(update_me)
exit=fltk:Fl_Button(bw*3,wh-bh,bw,bh,"&Quit"); exit:callback(quit)

pos={}
pos[0]=1
get_menu()

win:resizable(box)
win:show()
Fl:run()

Pages: 1 2
Reference URL's