playlist/youtube.lua: Use HTTPS if input URL uses it
[libquvi-scripts.git] / share / playlist / youtube.lua
blobb2c43e65d6c16298753db0490b3b39200c2afc7d
1 -- libquvi-scripts
2 -- Copyright (C) 2012-2013 Toni Gundogdu <legatvs@gmail.com>
3 --
4 -- This file is part of libquvi-scripts <http://quvi.sourceforge.net/>.
5 --
6 -- This program is free software: you can redistribute it and/or
7 -- modify it under the terms of the GNU Affero General Public
8 -- License as published by the Free Software Foundation, either
9 -- version 3 of the License, or (at your option) any later version.
11 -- This program is distributed in the hope that it will be useful,
12 -- but WITHOUT ANY WARRANTY; without even the implied warranty of
13 -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 -- GNU Affero General Public License for more details.
16 -- You should have received a copy of the GNU Affero General
17 -- Public License along with this program. If not, see
18 -- <http://www.gnu.org/licenses/>.
21 local YouTube = {} -- Utility functions unique to this script
23 -- Identify the playlist script.
24 function ident(qargs)
25 return {
26 domains = table.concat({'youtube.com'}, ','),
27 can_parse_url = YouTube.can_parse_url(qargs)
29 end
31 -- Parse playlist properties.
32 function parse(qargs)
34 qargs.id = qargs.input_url:match('list=([%w_-]+)')
35 if #qargs.id <16 then
36 error('no match: playlist ID')
37 end
39 local Y = require 'quvi/youtube'
40 local U = require 'socket.url'
41 local L = require 'quvi/lxph'
42 local P = require 'lxp.lom'
44 local max_results = 25
45 local start_index = 1
47 qargs.media = {}
48 local r = {}
50 repeat -- Get the entire playlist.
51 local u = YouTube.config_url(qargs, U, start_index, max_results)
52 local c = quvi.http.fetch(u).data
54 local x = P.parse(c)
55 YouTube.chk_error_resp(x)
57 YouTube.parse_thumb_url(qargs, L, x)
58 YouTube.parse_title(qargs, L, x)
60 local n = YouTube.parse_media_urls(qargs, L, x)
61 start_index = start_index + n
62 until n == 0
64 return qargs
65 end
68 -- Utility functions
71 function YouTube.can_parse_url(qargs)
72 local U = require 'socket.url'
73 local t = U.parse(qargs.input_url)
74 if t and t.scheme and t.scheme:lower():match('^https?$')
75 and t.host and t.host:lower():match('youtube%.com$')
76 and t.path and t.path:lower():match('^/playlist$')
77 and t.query and t.query:lower():match('^list=[%w_-]+')
78 then
79 return true
80 else
81 return false
82 end
83 end
85 function YouTube.config_url(qargs, U, start_index, max_results)
86 local u = U.parse(qargs.input_url)
87 local t = { -- Refer to http://is.gd/0msY8X
88 u.scheme, '://gdata.youtube.com/feeds/api/playlists/',
89 qargs.id, '?v=2', '&start-index=', start_index,
90 '&max-results=', max_results, '&strict=true'
92 return table.concat(t,'')
93 end
95 function YouTube.entry_avail(x)
96 --[[
97 -- app:control may contain, for example:
98 -- yt:state name='restricted' reasonCode='private'
99 ]]--
100 for i=1, #x do
101 if x[i].tag == 'app:control' then return false end
103 return true
106 function YouTube.parse_entry(qargs, L, x, i, r)
107 for j=1, #x[i] do
108 if x[i][j].tag == 'link' then
109 if x[i][j].attr['rel'] == 'alternate' then
110 local t = {
111 title = L.find_first_tag(x[i], 'title')[1],
112 duration_ms = YouTube.parse_duration(L, x[i]),
113 url = x[i][j].attr['href']
115 table.insert(qargs.media, t)
121 function YouTube.parse_media_urls(qargs, L, x)
122 if not x then return 0 end
123 local n = 0
124 for i=1, #x do
125 if x[i].tag == 'entry' then
126 if YouTube.entry_avail(x[i]) then
127 YouTube.parse_entry(qargs, L, x, i, r)
129 n = n+1
132 return n
135 function YouTube.chk_error_resp(t)
136 if not t then return end
137 local r = {}
138 for i=1, #t do
139 if t[i].tag == 'error' then
140 for j=1, #t[i] do
141 if t[i][j].tag == 'domain' then
142 r.domain = t[i][j][1]
144 if t[i][j].tag == 'code' then
145 r.code = t[i][j][1]
147 if t[i][j].tag == 'internalReason' then
148 r.reason = t[i][j][1]
150 if t[i][j].tag == 'location' then -- Ignores 'type' attribute.
151 r.location = t[i][j][1]
156 if #r >0 then
157 local m
158 for k,v in pairs(r) do
159 m = m .. string.format("%s=%s ", k,v)
161 error(m)
165 function YouTube.parse_title(qargs, L, x)
166 if not qargs.title then
167 qargs.title = L.find_first_tag(x, 'title')[1]
171 function YouTube.parse_thumb_url(qargs, L, x)
172 if qargs.thumb_url then return end
173 local g = L.find_first_tag(x, 'media:group')
174 for i=1, #g do
175 if g[i].tag == 'media:thumbnail' then
176 if g[i].attr['yt:name'] == 'hqdefault' then
177 qargs.thumb_url = g[i].attr['url']
178 break
184 function YouTube.parse_duration(L, x)
185 local g = L.find_first_tag(x, 'media:group')
186 local d = L.find_first_tag(g, 'yt:duration')
187 return tonumber(d.attr['seconds']) * 1000
190 -- vim: set ts=2 sw=2 tw=72 expandtab: