--[[ 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 . ]] -- Defines appname = "Media Context" appver = "0.6.6" author = "Xavion" username = "Media-Context" domain = author .. ".name" --website = "http://" .. username .. "." .. domain website = "http://addons.videolan.org/content/show.php?content=143241" fb_url = "mailto:"..username.."@"..domain.."?subject="..appname.."%20v"..appver.."%20-%20Feedback%20-%20" vlcreq = "2.0.1" vlcsite = 'http://www.videolan.org/vlc/' -- Functions function descriptor() return { title = appname; version = appver; author = author; url = website; shortdesc = "Displays media context information"; description = "Purpose:
" .. "* 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, '(