--[[
Program: Media Context
Purpose: Displays media context information
Author: Copyright 2011-2013 Xavion
License:
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see
"
.. "* Displays contextual information about the currently playing media
"
.. "* Adapted from similar scripts by rsyh93, Jean-Philippe Andre and ale5000
"
.. "
Feedback: Bad, Good, Idea";
capabilities = { "input-listener", "meta-listener" }
}
end
--[[
function menu()
return {}
end
]]
function activate()
-- Print API versions
vlc.msg.dbg("["..appname.."] ".._VERSION)
local vlcver = get_vlc_ver() or "2+"
vlc.msg.dbg("["..appname.."] VLC v"..vlcver)
-- Initialise
reset_variables()
test_environment()
show_dialog()
--[[
-- Retrieve
if not_stopped() and vlc_ok then
track_changed()
end
]]
return true
end
function deactivate()
busy = true
-- Close & reset
if dlg ~= nil then
remove_widgets()
--remove_widget(statusmsg)
if statusmsg then dlg:del_widget(statusmsg); statusmsg = nil end
dlg:delete()
dlg = nil
end
reset_variables()
vlc.deactivate()
return true
end
function close()
deactivate()
end
function input_changed()
if vlc.misc and vlc.misc.mdate then
vlc.msg.dbg("["..appname.."] Input change at " .. vlc.misc.mdate()/1000000 .. " seconds.")
end
-- Retrieve or reset
--if vlc.input.is_playing() == false then
if not_stopped() then
if vlc_ok then
--if source_is_file() == true then
--if vlc_ok and source_is_file() == true then
vlc.msg.dbg("["..appname.."] Media URI = " .. vlc.input.item():uri())
--track_changed()
end
else
remove_widgets()
reset_variables()
end
end
function meta_changed()
if vlc.misc and vlc.misc.mdate then
vlc.msg.dbg("["..appname.."] Meta change at " .. vlc.misc.mdate()/1000000 .. " seconds.")
end
-- Retrieve info
--if vlc.input.is_playing() == false then
if not_stopped() then
if vlc_ok then
--if source_is_file() == false then
--if vlc_ok and source_is_file() == false then
--if (now - last) > 15 then
--last = now
track_changed()
--end
end
end
end
function get_vlc_ver()
local vlcver
if vlc.misc and vlc.misc.version then
vlcver = vlc.misc.version()
end
return vlcver
end
function reset_variables()
statuslen = 60
res = 350
retrieve = nil
skip_next = 0
reset_last()
end
function reset_last()
busy = false
last = 0
topic = ""
artist = ""
imgs = nil
end
function reset_status()
dlg:set_title(appname)
-- Test requirements
if not vlc_ok then
set_status_text("Click here to download VLC v"..vlcreq.." first")
elseif not_stopped() then
track_changed()
--set_status_text("Always start 'MC' before playing media")
else
set_status_text("Waiting for media to be played ...")
end
end
function test_environment()
local vlcver = get_vlc_ver() or ""
vlcver = string.match(vlcver, "(%S+)")
vlc_ok = compare_versions(vlcver, vlcreq)
end
function show_dialog()
if dlg == nil then
dlg = vlc.dialog(appname)
end
statusmsg = dlg:add_label("", 21, 22, 35, 1)
reset_status()
end
--[[
function remove_widgets()
-- Image widgets
remove_image(true)
remove_widget(labimage)
remove_widget(getnewim)
-- Topic widgets
remove_widget(labcon)
remove_widget(toptype_dd)
remove_widget(field)
remove_widget(refine)
remove_html()
reset_status()
end
]]
function remove_widgets()
-- Image widgets
remove_image(true)
if labimage then dlg:del_widget(labimage); labimage = nil end
if imsize_dd then dlg:del_widget(imsize_dd); imsize_dd = nil end
if imtype_dd then dlg:del_widget(imtype_dd); imtype_dd = nil end
if labsearch then dlg:del_widget(labsearch); labsearch = nil end
if exact_dd then dlg:del_widget(exact_dd); exact_dd = nil end
if getnewim then dlg:del_widget(getnewim); getnewim = nil end
-- Topic widgets
if labcon then dlg:del_widget(labcon); labcon = nil end
if toptype_dd then dlg:del_widget(toptype_dd); toptype_dd = nil end
if field then dlg:del_widget(field); field = nil end
if refine then dlg:del_widget(refine); refine = nil end
remove_html()
reset_status()
end
--[[
function remove_widget(widget)
if widget ~= nil then
dlg:del_widget(widget)
--pcall(dlg:del_widget(widget))
widget = nil
end
end
]]
function remove_image(delfile)
--remove_widget(image)
if image then dlg:del_widget(image); image = nil end
-- Delete file
if imgfile and delfile then
os.remove(imgfile)
imgfile = nil
end
end
function remove_html()
--remove_widget(html)
if html then dlg:del_widget(html); html = nil end
end
function check_for_updates()
vlc.msg.dbg("["..appname.."] Checking for updates ...")
-- Get versions stream
local wstream = vlc.stream("http://dl.dropbox.com/u/106000/Programming/Lua/Media-Context/Current.txt")
if wstream == nil then
set_status_text("Check for a newer 'MC' release here")
return nil
end
--local doc = wstream:read(65536)
-- Check if current
local doc = ""
local utype = nil
local remver = nil
local current = true
local line = wstream:readline()
while line ~= nil do
if string.find(line, "Required", 0, true) then
utype = "stable"
remver = string.match(line, "Required = (.+)")
current = compare_versions(appver, remver)
if current then break end
elseif string.find(line, "Recommended", 0, true) then
utype = "beta"
remver = string.match(line, "Recommended = (.+)")
current = compare_versions(appver, remver)
if current then break end
end
line = wstream:readline()
end
-- Inform the user
if not current then
set_status_text("Download 'MC' v"..remver.." ("..utype..") from here")
end
end
function compare_versions(locver, remver)
local older = false
if locver and remver then
local lmajor, lminor, lpoint, rmajor, rminor, rpoint
lmajor, lminor, lpoint = string.match(locver, "(%d+)%p(%d+)%p(%d+)")
rmajor, rminor, rpoint = string.match(remver, "(%d+)%p(%d+)%p(%d+)")
lmajor = tonumber(lmajor)
lminor = tonumber(lminor)
lpoint = tonumber(lpoint)
rmajor = tonumber(rmajor)
rminor = tonumber(rminor)
rpoint = tonumber(rpoint)
vlc.msg.dbg("["..appname.."] Comparing versions ...")
vlc.msg.dbg("["..appname.."] Local version: major = "..lmajor..", minor = "..lminor..", point = "..lpoint)
vlc.msg.dbg("["..appname.."] Remote version: major = "..rmajor..", minor = "..rminor..", point = "..rpoint)
-- Major
if lmajor < rmajor then
older = true
elseif lmajor == rmajor then
-- Minor
if lminor < rminor then
older = true
elseif lminor == rminor then
-- Point
if lpoint < rpoint then
older = true
end
end
end
end
return not older
end
function source_is_file()
-- Get input source
local filesrc
local uri = vlc.input.item():uri()
if string.find(uri, "file://", 0, true) then
filesrc = true
else
filesrc = false
end
return filesrc
end
function not_stopped()
return vlc.playlist.status() == ("playing" or "paused")
end
function click_change()
if not busy then
busy = true
-- Update image list if necessary
if exact ~= get_exact() or imsize ~= get_image_size() or imtype ~= get_image_type() then
update_image_list()
end
-- Change the image
if change_image() then
-- Replace topic info
if html then
local text = html:get_text()
remove_html()
add_html(text)
end
else
--reset_last()
end
busy = false
end
end
function click_refine()
if not busy then
if choose_topic(false) or choose_topic(true) then
busy = true
update_image_list()
-- Change the topic
if not change_topic() then
--reset_last()
end
busy = false
end
end
end
function track_changed()
local proceed = false
if not busy then
proceed = choose_topic(true)
end
-- Retrieve only
if proceed == true then
busy = true
update_image_list()
local failed = false
-- Change image
--if vlc.input.is_playing() == true then
if not change_image() then
failed = true
end
-- Change topic
if not change_topic() then
failed = true
end
if failed == false then
set_status_text("Successfully retrieved new context details")
else
--reset_last()
set_status_text("Couldn't retrieve all new context details")
end
-- Check for updates
if math.random(10) == 5 then
check_for_updates()
end
busy = false
elseif proceed == nil then
set_status_text("Couldn't determine new media context (yet)")
end
end
function choose_topic(auto)
-- Retrieve or refine
local newtopic = nil
local proceed = false
if auto then
-- Metadata is stabler
if vlc.input.item():is_preparsed() then
newtopic = get_topic()
end
else
newtopic = field:get_text()
end
-- Topic change
if newtopic and (newtopic ~= topic) then
topic = newtopic
proceed = true
vlc.msg.dbg("["..appname.."] New topic = " .. topic)
elseif newtopic == nil then
proceed = nil
end
return proceed
end
function set_status_text(msg)
local newmsg = "" .. extend_string("Status: "..msg, statuslen) .. ""
statusmsg:set_text(newmsg)
dlg:update()
end
function add_html(text)
html = dlg:add_html(text, 21, 2, 35, 20)
end
function change_image()
local changed = false
local tmpimage = nil
set_status_text("Retrieving a new image of the topic ...")
imgfile = get_image_filename()
-- Add image label
if labimage == nil then
labimage = dlg:add_label("Image:", 1, 1, 0, 1)
end
-- Add size dropdown
if imsize_dd == nil then
imsize_dd = dlg:add_dropdown(2, 1, 0, 1)
imsize_dd:add_value("Small", 0)
imsize_dd:add_value("Medium", 1)
imsize_dd:add_value("Large", 2)
--imsize_dd:add_value("Disabled", 2)
end
-- Add type dropdown
if imtype_dd == nil then
imtype_dd = dlg:add_dropdown(3, 1, 0, 1)
imtype_dd:add_value("Photo", 0)
imtype_dd:add_value("Face", 1)
imtype_dd:add_value("Any", 2)
end
-- Add image label
if labsearch == nil then
labsearch = dlg:add_label("Search:", 1, 22, 0, 1)
end
-- Add exact check-box
if exact_dd == nil then
exact_dd = dlg:add_dropdown(2, 22, 0, 1)
exact_dd:add_value("Precise", 0)
exact_dd:add_value("Relaxed", 1)
end
-- Add change button
if getnewim == nil then
getnewim = dlg:add_button("Change", click_change, 3, 22, 0, 1)
end
-- Add temp image
if image then
tmpimage = dlg:add_image("", 1, 2, 20, 20, res, res)
end
-- Remove last image
remove_image(false)
-- Add new image
if imgfile and string.len(imgfile) > 0 then
--image = dlg:add_image(imgfile, 1, 2, 20, 20)
image = dlg:add_image(imgfile, 1, 2, 20, 20, res, res)
changed = true
end
-- Remove temp image
--remove_widget(tmpimage)
if tmpimage then dlg:del_widget(tmpimage); tmpimage = nil end
set_status_length()
if changed == true then
set_status_text("Successfully retrieved image of the topic")
else
set_status_text("Couldn't retrieve an image of the topic")
end
return changed
end
function change_topic()
local changed = false
-- Get new info
local data = ""
if topic ~= nil then
set_status_text("Retrieving the new topic information ...")
data = get_wikipedia_info(topic)
end
-- Remove last info
remove_html()
-- Add topic label
if labcon == nil then
labcon = dlg:add_label("Topic:", 21, 1, 0, 1)
end
-- Add topic list
if toptype_dd == nil then
toptype_dd = dlg:add_dropdown(22, 1, 0, 1)
toptype_dd:add_value("Artist", 0)
toptype_dd:add_value("Track", 1)
toptype_dd:add_value("Album", 2)
end
-- Add input field
if field == nil then
field = dlg:add_text_input(nil, 23, 1, 32, 1)
end
-- Add refine button
if refine == nil then
refine = dlg:add_button("Refine", click_refine, 55, 1, 0, 1)
end
-- Add new info
--if html == nil then
if data and string.len(data) > 0 then
-- Add webpage
add_html(data)
changed = true
--else
-- html:set_text(data)
--end
end
-- Set info titles
if topic ~= nil then
field:set_text(topic)
dlg:set_title(appname .. " - " .. topic)
end
if changed == true then
set_status_text("Successfully retrieved new topic information")
else
set_status_text("Couldn't retrieve the new topic information")
end
return changed
end
function get_image_size()
local imsize_id = 0
local imsize_text = nil
-- Get ID
if imsize_dd ~= nil then
imsize_id = imsize_dd:get_value()
end
-- ID to text
if imsize_id == 0 then
imsize_text = "isz:m"
res = 350
elseif imsize_id == 1 then
imsize_text = "isz:l"
res = 600
elseif imsize_id == 2 then
imsize_text = "isz:l"
res = 850
end
return imsize_text
end
function set_status_length()
local imsize_id = 0
-- Get ID
if imsize_dd ~= nil then
imsize_id = imsize_dd:get_value()
end
-- ID to text
if imsize_id == 0 then
statuslen = 60
elseif imsize_id == 1 then
statuslen = 80
elseif imsize_id == 2 then
statuslen = 100
end
end
function get_image_type()
local imtype_id = 0
local imtype_text = nil
-- Get ID
if imtype_dd ~= nil then
imtype_id = imtype_dd:get_value()
end
-- ID to text
if imtype_id == 0 then
imtype_text = ",itp:photo"
elseif imtype_id == 1 then
imtype_text = ",itp:face"
elseif imtype_id == 2 then
imtype_text = ""
end
return imtype_text
end
function get_exact()
local exact_id = 0
local exact_text = nil
-- Get ID
if exact_dd ~= nil then
exact_id = exact_dd:get_value()
end
-- ID to text
if exact_id == 0 then
exact_text = '"'
elseif exact_id == 1 then
exact_text = ''
end
return exact_text
end
function get_topic()
local topic_id = 0
local topic_text = nil
-- Get ID
if toptype_dd ~= nil then
topic_id = toptype_dd:get_value()
end
-- ID to text
if topic_id == 0 then
topic_text = get_artist()
elseif topic_id == 1 then
topic_text = get_track()
elseif topic_id == 2 then
topic_text = get_album()
end
return topic_text
end
function get_artist()
-- Check input
local item = vlc.item or vlc.input.item()
if not item then return nil end
-- Get metadata
local name = nil
local metas = item:metas()
if source_is_file() then
if metas["artist"] and string.len(metas["artist"]) > 0 then
name = metas["artist"]
elseif metas["title"] and string.len(metas["title"]) > 0 then
if string.find(metas["title"], " - ", 0, true) then
name = get_artist_from_combo(metas["title"])
end
else
local filename = string.gsub(item:name(), "^(.+)%.%w+$", "%1")
name = trim(filename or item:name())
if string.find(name, " - ", 0, true) then
name = get_artist_from_combo(name)
else
name = nil
end
end
else
if metas["now_playing"] and string.len(metas["now_playing"]) > 0 then
if string.find(metas["now_playing"], " - ", 0, true) then
name = get_artist_from_combo(metas["now_playing"])
else
name = metas["now_playing"]
end
end
end
return name
end
function get_track()
-- Check input
local item = vlc.item or vlc.input.item()
if not item then return nil end
-- Get metadata
local name = nil
local metas = item:metas()
if source_is_file() then
if metas["title"] and string.len(metas["title"]) > 0 then
if string.find(metas["title"], " - ", 0, true) and (not metas["artist"] or string.len(metas["artist"]) == 0) then
name = get_track_from_combo(metas["title"])
else
name = metas["title"]
end
else
local filename = string.gsub(item:name(), "^(.+)%.%w+$", "%1")
name = trim(filename or item:name())
if string.find(name, " - ", 0, true) then
name = get_track_from_combo(name)
end
end
else
if metas["now_playing"] and string.len(metas["now_playing"]) > 0 then
if string.find(metas["now_playing"], " - ", 0, true) then
name = get_track_from_combo(metas["now_playing"])
end
end
end
return name
end
function get_album()
-- Check input
local item = vlc.item or vlc.input.item()
if not item then return nil end
-- Get metadata
local name = nil
local metas = item:metas()
if source_is_file() then
if metas["album"] and string.len(metas["album"]) > 0 then
name = metas["album"]
--name = metas["album"] .. " ("..get_artist().." Album)"
end
end
return name
end
function get_artist_from_combo(combo)
return string.sub(combo, 0, string.find(combo, " - ", 0, true)-1)
end
function get_track_from_combo(combo)
return string.sub(combo, string.find(combo, " - ", 0, true)+3)
end
function get_image_list(loose)
-- Get image style
exact = ''
imsize = get_image_size()
imtype = get_image_type()
-- Get image range
if not loose then
exact = get_exact()
end
-- Get images stream
lststr = vlc.stream("http://images.google.com/images?q=" .. vlc.strings.encode_uri_component(exact..topic..exact) .. "&tbs=ift:jpg," .. imsize .. ",iar:s" .. imtype .. ",imgo:1")
if not lststr then return nil end
page = lststr:read(1024^2)
lststr = nil
-- Save list locally
--save_to_cache(page, "Images.html", "w")
-- List JPEG files
local imgs = {}
for imgurl, sz in string.gmatch(page, "imgurl=(http://.-%..-)&.-&sz=(%d-)&") do
if tonumber(sz) < 1024 then
table.insert(imgs, vlc.strings.decode_uri(imgurl))
--[[
vlc.msg.dbg("["..appname.."] Image: Added: " .. imgurl .. ", Size: " .. sz)
else
vlc.msg.dbg("["..appname.."] Image: Rejected: " .. imgurl .. ", Size: " .. sz)
]]
end
end
return imgs
end
function update_image_list()
set_status_text("Retrieving a list of available images ...")
-- Get image list
imgs = get_image_list(false)
vlc.msg.dbg("["..appname.."] Images: List Length (Precise) = " .. table.maxn(imgs))
if table.maxn(imgs) < 5 then
imgs = get_image_list(true)
vlc.msg.dbg("["..appname.."] Images: List Length (Relaxed) = " .. table.maxn(imgs))
end
end
function get_image_filename()
-- Get info
--artist = get_artist()
--track = get_track()
--if artist == nil then return nil end
if topic == nil then return nil end
-- Get random image
local arturl = nil
local pick = nil
local start = nil
local data = nil
local found = false
if table.maxn(imgs) > 0 then
while not found do
pick = math.random(table.maxn(imgs))
arturl = imgs[pick]
local imgstr = vlc.stream(arturl)
if imgstr then
start = imgstr:read(2)
-- Ensure JPEG format
if start and string.byte(start, 1) == 0xFF and string.byte(start, 2) == 0xD8 then
data = start .. imgstr:read(1024^2)
found = true
end
imgstr = nil
end
vlc.msg.dbg("["..appname.."] Image: Pick = "..pick..", URL = "..arturl)
end
end
if data == nil then return "" end
-- Save image locally
return save_to_cache(data, "Image.jpg", "wb")
end
function get_wikipedia_info(entry)
-- Get info URL
local list_url = get_wikipedia_url(entry)
if list_url == nil then return nil end
--vlc.msg.dbg("["..appname.."] List: URL = " .. list_url)
-- Get list stream
local list_str = vlc.stream(list_url)
if list_str == nil then return nil end
--local doc = list_str:read(65536)
local lang = string.match(list_url, "http://(%w+)%.")
-- Get page stream
local list = list_str:read(1024^2)
list_str = nil
local first = string.match(list, 'class="mw%-search%-result%-heading".-href="/wiki/(.-)"%s')
if first == nil then return nil end
--local page_url = "http://"..lang..".m.wikipedia.org/wiki/" .. first .. "&disableImages=1"
local page_url = "http://"..lang..".m.wikipedia.org/w/index.php?title=" .. first .. "&disableImages=1"
vlc.msg.dbg("["..appname.."] Info: URL = " .. page_url)
local page_str = vlc.stream(page_url)
if page_str == nil then return nil end
set_status_text("Extracting relevant topic information ...")
-- Format the doc
--local doc = page_str:read(1024^2)
--page_str = nil
--doc = format_topic_info(doc, lang)
local doc = format_topic_info(page_str:read(1024^2), lang)
page_str = nil
-- Save doc locally
--save_to_cache(doc, "Info.html", "w")
return doc
end
function get_wikipedia_url(entry)
if entry == nil or entry == "" then return nil end
-- Format query
local entry = trim(entry)
entry = string.gsub(entry, " ", "+")
--entry = string.gsub(entry, " ", "_")
return "http://en.m.wikipedia.org/w/index.php?title=Special:Search&search=~" .. entry
--return "http://en.m.wikipedia.org/wiki/" .. entry
end
function format_topic_info(doc, lang)
-- Alternatives
local alt = string.match(doc, '(
| %c-)', '', 1) -- Title (name) intro = string.gsub(intro, '( | |
|---|---|
| %c- | |
| %c- |