]> git.sesse.net Git - vlc/blob - share/lua/extensions/imdb.lua
use vlc.input.item() where appropriate
[vlc] / share / lua / extensions / imdb.lua
1 --[[
2  Get information about a movie from IMDb
3
4  Copyright © 2009-2010 VideoLAN and AUTHORS
5
6  Authors:  Jean-Philippe André (jpeg@videolan.org)
7
8  This program is free software; you can redistribute it and/or modify
9  it under the terms of the GNU General Public License as published by
10  the Free Software Foundation; either version 2 of the License, or
11  (at your option) any later version.
12
13  This program is distributed in the hope that it will be useful,
14  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  GNU General Public License for more details.
17
18  You should have received a copy of the GNU General Public License
19  along with this program; if not, write to the Free Software
20  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
21 --]]
22
23 dlg = nil
24 txt = nil
25 function descriptor()
26     return { title = "IMDb - The Internet Movie Database" ;
27              version = "0.1" ;
28              author = "Jean-Philippe André" ;
29              url = 'http://www.imdb.org/';
30              description = "<center><b>The Internet Movie Database</b></center>\n"
31                         .. "Get information about movies from the Internet "
32                         .. "Movie Database (IMDb).\nThis Extension will show "
33                         .. "you the cast, a short plot summary and a link to "
34                         .. "the web page on imdb.org." ;
35              capabilities = {} }
36 end
37
38 -- Update title text field. Removes file extensions.
39 function update_title()
40     local item = vlc.item()
41     local title = item and item:name()
42     if title ~= nil then
43         title = string.gsub(title, "(.*)(%.%w+)$", "%1")
44     end
45     if title ~= nil then
46         txt:set_text(title)
47     end
48 end
49
50 function create_dialog()
51     dlg = vlc.dialog("IMDb Search")
52     dlg:add_label("The Internet Movie Database", 1, 1, 4, 1)
53     dlg:add_label("<b>Movie Title</b>", 1, 2, 1, 1)
54     local item = vlc.item()
55     txt = dlg:add_text_input(item and item:name() or "", 2, 2, 1, 1)
56     dlg:add_button("Okay", "click_okay", 3, 2, 1, 1)
57     dlg:add_button("*", "update_title", 4, 2, 1, 1)
58     dlg:show() -- Show, if not already visible
59 end
60
61 function activate()
62     create_dialog()
63 end
64
65 function deactivate()
66 end
67
68 -- Dialog closed
69 function close()
70     -- Deactivate this extension
71     vlc.deactivate()
72 end
73
74 -- Some global variables: widgets
75 list = nil
76 button_open = nil
77 titles = nil
78 html = nil
79
80 function click_okay()
81     vlc.msg.dbg("Searching for " .. txt:get_text() .. " on IMDb")
82
83     if html then
84         dlg:del_widget(html)
85         html = nil
86     end
87
88     if not list then
89         list = dlg:add_list(1, 3, 4, 1)
90         button_open = dlg:add_button("Open", "click_open", 1, 4, 4, 1)
91     end
92
93     -- Clear previous results
94     list:clear()
95
96     -- Search IMDb
97     local url = "http://www.imdb.com/find?s=all&q="
98     local title = string.gsub(txt:get_text(), " ", "+")
99     local s = vlc.stream(url .. title)
100
101     -- Fetch HTML data
102     local data = s:read(65000)
103
104     -- Find titles
105     titles = {}
106     local count = 0
107
108     idxEnd = 1
109     while idxEnd ~= nil do
110         -- Find title types
111         _, idxEnd, titleType = string.find(data, "<b>([^<]*Titles[^<]*)</b>", idxEnd)
112         _, _, nextTitle = string.find(data, "<b>([^<]*Titles[^<]*)</b>", idxEnd)
113         if not titleType then
114             break
115         else
116             -- Find current scope
117             if not nextTitle then
118                 _, _, table = string.find(data, "<table>(.*)</table>", idxEnd)
119             else
120                 nextTitle = string.gsub(nextTitle, "%(", "%%(")
121                 nextTitle = string.gsub(nextTitle, "%)", "%%)")
122                 _, _, table = string.find(data, "<table>(.*)</table>.*"..nextTitle, idxEnd)
123             end
124             -- Find all titles in this scope
125             if not table then break end
126             pos = 0
127             while pos ~= nil do
128                 _, _, link = string.find(table, "<a href=\"([^\"]+title[^\"]+)\"", pos)
129                 if not link then break end -- this would not be normal behavior...
130                 _, pos, title = string.find(table, "<a href=\"" .. link .. "\"[^>]*>([^<]+)</a>", pos)
131                 if not title then break end -- this would not be normal behavior...
132                 _, _, year = string.find(table, "\((%d+)\)", pos)
133                 -- Add this title to the list
134                 count = count + 1
135                 _, _, imdbID = string.find(link, "/([^/]+)/$")
136                 title = replace_html_chars(title)
137                 titles[count] = { id = imdbID ; title = title ; year = year ; link = link }
138             end
139         end
140     end
141
142     for idx, title in ipairs(titles) do
143         list:add_value("[" .. title.id .. "] " .. title.title .. " (" .. title.year .. ")", idx)
144     end
145 end
146
147 function click_open()
148     selection = list:get_selection()
149     if not selection then return 1 end
150     if not html then
151         html = dlg:add_html("Loading IMDb page...", 1, 3, 4, 1)
152         -- userLink = dlg:add_label("", 1, 4, 5, 1)
153     end
154
155     dlg:del_widget(list)
156     dlg:del_widget(button_open)
157     list = nil
158     button_open = nil
159
160     local sel = nil
161     for idx, selectedItem in pairs(selection) do
162         sel = idx
163         break
164     end
165     imdbID = titles[sel].id
166     url = "http://www.imdb.org/title/" .. imdbID .. "/"
167
168     -- userLink:set_text("<a href=\"url\">" .. url .. "</a>")
169
170     local s = vlc.stream(url)
171     data = s:read(65000)
172
173     text = "<h1>" .. titles[sel].title .. " (" .. titles[sel].year .. ")</h1>"
174     text = text .. "<h2>Overview</h2><table>"
175
176     -- Director
177     local director = nil
178     _, nextIdx, _ = string.find(data, "<div id=\"director-info\"", 1, true)
179     if nextIdx then
180         _, _, director = string.find(data, "<a href[^>]+>([%w%s]+)</a>", nextIdx)
181     end
182     if not director then
183         director = "(Unknown)"
184     end
185     text = text .. "<tr><td><b>Director</b></td><td>" .. director .. "</td></tr>"
186
187     -- Main genres
188     local genres = "<tr><td><b>Genres</b></td>"
189     local first = true
190     for genre, _ in string.gmatch(data, "/Sections/Genres/(%w+)/\">") do
191         if first then
192             genres = genres .. "<td>" .. genre .. "</td></tr>"
193         else
194             genres = genres .. "<tr><td /><td>" .. genre .. "</td></tr>"
195         end
196         first = false
197     end
198     text = text .. genres
199
200     -- List main actors
201     local actors = "<tr><td><b>Cast</b></td>"
202     first = true
203     for nm, char in string.gmatch(data, "<td class=\"nm\"><a[^>]+>([%w%s]+)</a></td><td class=\"ddd\"> ... </td><td class=\"char\"><a[^>]+>([%w%s]+)</a>") do
204         if not first then
205             actors = actors .. "<tr><td />"
206         end
207         actors = actors .. "<td>" .. nm .. "</td><td><i>" .. char .. "</i></td></tr>"
208         first = false
209     end
210     text = text .. actors .. "</table>"
211
212     text = text .. "<h2>Plot Summary</h2>"
213     s = vlc.stream(url .. "plotsummary")
214     data = s:read(65000)
215
216     -- We read only the first summary
217     _, _, summary = string.find(data, "<p class=\"plotpar\">([^<]+)")
218     if not summary then
219         summary = "(Unknown)"
220     end
221     text = text .. "<p>" .. summary .. "</p>"
222     text = text .. "<p><h2>Source IMDb</h2><a href=\"" .. url .. "\">" .. url .. "</a></p>"
223
224     html:set_text(text)
225 end
226
227 -- Convert some HTML characters into UTF8
228 function replace_html_chars(txt)
229     if not txt then return nil end
230     -- return vlc.strings.resolve_xml_special_chars(txt)
231     for num in string.gmatch(txt, "&#x(%x+);") do
232         -- Convert to decimal (any better way?)
233         dec = 0
234         for c in string.gmatch(num, "%x") do
235             cc = string.byte(c) - string.byte("0")
236             if (cc >= 10 or cc < 0) then
237                 cc = string.byte(string.lower(c)) - string.byte("a") + 10
238             end
239             dec = dec * 16 + cc
240         end
241         txt = string.gsub(txt, "&#x" .. num .. ";", string.char(dec))
242     end
243     return txt
244 end
245